# @rulecms/source-components-react

React source components for RuleCMS widgets.

## Installation

```bash
npm install @rulecms/source-components-react
```

## Usage

```typescript
import { getSourceComponentsPlugin } from '@rulecms/source-components-react';

// Use the plugin in your RuleCMS widget configuration
const plugin = getSourceComponentsPlugin();
```

## Components

| Type | Description |
|---|---|
| `r-text` | Text in a configurable enclosing tag, with inline links |
| `cloudinary-advanced-image` | Responsive Cloudinary image |
| `r-video` | Cloudinary video with poster |
| `r-icon` | A single Lucide `<svg>` |
| `r-button` | A `<button>`, or an `<a>` when it links somewhere, styled by preset, with hover / focus / active from the library stylesheet |
| `r-divider` | A single `<hr>` separating two sections |
| `r-embed` | A third-party `<iframe>` from a pasted URL |
| `r-list` | A `<ul>` or `<ol>`, one item per authored line |
| `r-accordion` | A `<details>`/`<summary>` disclosure — a summary that expands to its detail, with a right-side chevron and CSS open/close motion, and no JavaScript |

## Icons

`r-icon` renders geometry carried on its own attribute rather than looking a
name up at render time:

```jsonc
{
  "type": "r-icon",
  "componentAttributes": {
    "icon-name-3-resolutions": {
      "name": "rocket",
      "iconNode": [["path", { "d": "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" }]]
    }
  }
}
```

The composer stamps that value when the author picks an icon. Because the
geometry travels with the widget, customer pages ship no icon package at all
(~250 bytes per icon instead of ~962 KB of registry), server rendering needs no
async work, and published pages are unaffected by upstream renames. `name` is
kept alongside so geometry can be re-resolved later.

### Authoring-side registry

Name resolution, the RuleCMS alias table, and tag search live behind a separate
entry point that **only the composer** should import — never a customer page:

```typescript
import {
  buildIconAttributeValue,
  searchIconNames,
} from '@rulecms/source-components-react/icon-registry';

searchIconNames('launch');          // ['dock', 'rocket'] — matches Lucide tags,
                                    // not just names
buildIconAttributeValue('rocket');  // { name, iconNode } — persist this
```

That entry pulls in `lucide-static`. `__bundle_tests__/bundle-payload-guard.test.ts`
fails the build if it ever becomes reachable from the main entry; run it with
`npm run test:bundle`.

## Development

### Install dependencies
```bash
npm install
```

### Build
```bash
npm run build
```

### Run tests
```bash
npm run test
```

### Run Storybook
```bash
npm run storybook
```

### Type checking
```bash
npm run typecheck
```

### Linting
```bash
npm run lint
```

## Publishing

Bump, then push the branch and the version tag. GitHub Actions publishes to
npm via trusted publishing (`.github/workflows/publish.yml`). Skipping
`git push --tags` leaves the tag local-only and npm stays on the old version.
Do not run `npm publish` locally.

```bash
npm run version:patch   # or version:minor / version:major
git push && git push --tags
# wait until this matches the tag you just pushed
npm view @rulecms/source-components-react version
```

Publishing this package is not finished when this repo is green: every consumer
has to be upgraded afterwards. See
`rulecms/__docs__/__RUNBOOKS__/RUNBOOK_rulecms-packages-upgrade-across-repos.md`.

## License

UNLICENSED - This is proprietary software for RuleCMS, LLC.