Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
400cc53
WIP: PrimeReact 11 migration checkpoint (Phases 0-4)
woksin Jul 16, 2026
1375c84
Rebuild Stepper stack on PrimeReact 11 compositional Stepper
woksin Jul 16, 2026
521acda
Reimplement Tooltip on PrimeReact 11 and migrate NameCell
woksin Jul 16, 2026
b389a85
Rebuild DataTables on PrimeReact 11 headless DataTable
woksin Jul 16, 2026
ec23803
Migrate DataPage and SchemaEditor to the v11 table and action menubar
woksin Jul 16, 2026
a6cf322
Migrate remaining wrappers to PrimeReact 11
woksin Jul 16, 2026
28d7a31
Fix build config and specs for green PrimeReact 11 gates
woksin Jul 16, 2026
daefa82
Update docs and version for PrimeReact 11
woksin Jul 16, 2026
4db9595
Remap the Storybook pt preset to PrimeReact 11 slots
woksin Jul 16, 2026
1db96ca
Render a selection radio for selectionMode data-table columns
woksin Jul 16, 2026
088261e
Restore Stepper orientation, headerPosition, start, and end
woksin Jul 16, 2026
36c1715
Restore DataTable per-column filter menus and add global search
woksin Jul 16, 2026
675faf8
Add a range report to the table paginator
woksin Jul 16, 2026
e52bf65
Add status & display components (Tag, Badge, Chip, Skeleton, Avatar, …
woksin Jul 16, 2026
7322bb1
Add Toast notifications with Arc command-result integration
woksin Jul 16, 2026
b5d5ecf
Add PasswordField, ToggleSwitchField, and RatingField
woksin Jul 16, 2026
ba0cf5d
Add consumer migration guide and BDD specs for the new logic
woksin Jul 16, 2026
708b40b
Add a license-free Cratis baseline theme
woksin Jul 16, 2026
a9094af
Document the new component surface in the .ai rules
woksin Jul 16, 2026
605579f
Accessibility fixes and provider Toaster auto-mount
woksin Jul 16, 2026
b4d7ff6
Fix baseline theme light variant and preview it in Storybook
woksin Jul 16, 2026
c010ca4
Fix accessible names on ToggleSwitchField and RatingField
woksin Jul 16, 2026
66a695f
Use Tag for the status column in the DataTableForQuery story
woksin Jul 16, 2026
8f5d9f3
Exclude Storybook build and test scaffolding from the published package
woksin Jul 16, 2026
1811c3b
Fix invalid tab ARIA on the command stepper
woksin Jul 16, 2026
ab01e80
Theme Arc's component chrome tokens in the baseline theme
woksin Jul 16, 2026
d2ab61a
Fix button contrast in the Storybook Tailwind pt preset
woksin Jul 16, 2026
ecbaae5
Document the toast options object in the components rule
woksin Jul 16, 2026
830d30f
Fix README drift from the PrimeReact 11 migration
woksin Jul 16, 2026
4645772
Give the boolean/choice CommandForm fields accessible names
woksin Jul 16, 2026
9e474d1
Make component aria-labels localizable, not hard-coded English
woksin Jul 16, 2026
57da6ee
Extend the baseline theme: Arc spacing tokens, missing scopes, contrast
woksin Jul 16, 2026
661e8b0
Fix Storybook stories: removed Button API and contrast
woksin Jul 16, 2026
6d7a169
Fix provider-prop API and stale references in the docs
woksin Jul 16, 2026
c52b679
Exclude a stale dist/cjs tree from the published package
woksin Jul 16, 2026
e040c5d
Make all remaining component labels configurable, not hard-coded
woksin Jul 16, 2026
0a701c8
Fix PrimeReact 11 drift in the documentation site
woksin Jul 16, 2026
1264ebd
Document the new components in the docs site
woksin Jul 16, 2026
9d3555a
Fix PrimeReact 11 widget names and Button API in two more doc pages
woksin Jul 16, 2026
c5d4746
Refine the styling docs for the v11 token model
woksin Jul 16, 2026
e590e7d
Restore public API surface narrowed beyond what PrimeReact 11 forced
woksin Jul 16, 2026
84eb32c
Correct docs that overstated the API and add a reduced-capabilities note
woksin Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions .ai/agents/frontend-developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,24 +189,21 @@ import { Page } from '@cratis/components/Common';
import { AddProject } from './Registration/AddProject';
import { Listing } from './Listing/Listing';
import { DialogResult, useDialog } from '@cratis/arc.react/dialogs';
import { Menubar } from 'primereact/menubar';
import { MenuItem } from 'primereact/menuitem';
import { Button } from 'primereact/button';
import * as mdIcons from 'react-icons/md';

export const Projects = () => {
const [AddProjectDialog, showAddProjectDialog] = useDialog(AddProject);

const menuItems: MenuItem[] = [
{
label: 'Add Project',
icon: mdIcons.MdAdd,
command: async () => { await showAddProjectDialog(); }
}
];

// For a query-backed list page, prefer `DataPage` with `<DataPage.MenuItems>`
// (it owns the action bar). PrimeReact 11 removed the standalone `Menubar`;
// for a custom toolbar, compose `Button`s (content is children in v11).
return (
<Page title="Projects">
<Menubar model={menuItems} />
<Button variant="text" onClick={() => showAddProjectDialog()}>
<mdIcons.MdAdd />
<span>Add Project</span>
</Button>
<Listing />
<AddProjectDialog />
</Page>
Expand Down
31 changes: 28 additions & 3 deletions .ai/rules/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,45 @@ Reach PrimeReact almost exclusively through Cratis Components wrappers. Import f
| Dropdown | `Dropdown` | `@cratis/components/Dropdown` |
| Command dialog | `CommandDialog` / `StepperCommandDialog` | `@cratis/components/CommandDialog` |
| Data/confirmation dialog | `Dialog` / `ConfirmationDialog` / `BusyIndicatorDialog` | `@cratis/components/Dialogs` |
| Command form fields | `InputTextField`, … | `@cratis/components/CommandForm` |
| Command form fields | `InputTextField`, `PasswordField`, `ToggleSwitchField`, `RatingField`, … | `@cratis/components/CommandForm` |
| Notifications (toasts) | `Toaster` / `toast` / `toastCommandResult` | `@cratis/components/Notifications` |
| Status & display | `Tag` / `Badge` / `Chip` / `Skeleton` / `Avatar` / `ProgressBar` | `@cratis/components/Display` |
| Canvas tool palette | `Toolbar` | `@cratis/components/Toolbar` |

Use `Dropdown` from `@cratis/components/Dropdown` (not raw `primereact/dropdown`) — it appends to the document body and stacks correctly above overlays, avoiding the z-index issues raw PrimeReact dropdowns have inside dialogs.

### Notifications — feedback for commands run outside a dialog

`CommandDialog` handles success/error feedback itself. For a command executed
**programmatically** (`command.execute()` outside a dialog), mount one
`<Toaster />` near the app root and surface the result with `toastCommandResult`
(both from `@cratis/components/Notifications`) — it maps the granular
`ICommandResult` flags to the right toast (success, not-authorized, validation
with per-field messages, exceptions — never stack traces):

```tsx
const result = await command.execute();
if (toastCommandResult(result, { successTitle: 'Author registered' })) refresh();
```

For ad-hoc notifications, call the imperative `toast.success/info/warn/error(...)`
— each takes an **options object**, not a bare string: `toast.info({ title: 'Saved', description: '…' })`.

### Column filtering & display components

`<Column>` supports `filter` (a per-column filter menu with match modes) and
`DataPage` / the data tables show a global search box when `globalFilterFields`
is set. Use the `Display` components (`Tag`, `Badge`, `Skeleton`, …) for status
indicators and loading states in tables and detail views.

### `DataPage` — query list pages

`DataPage` (from `@cratis/components/DataPage`) owns the data table's subscription, paging, selection, action menubar, and details split — **do not pre-fetch rows and pass an `items` array**. Required props: `title`, `query` (`Constructor<TQuery>`; snapshot and observable queries are auto-detected), `emptyMessage`, and `children`. Other props: `queryArguments`, `dataKey` (pass whenever the read model has an identity), `selection` / `onSelectionChange`, `globalFilterFields` / `defaultFilters` / `clientFiltering`, `detailsComponent` (`React.FC<IDetailsComponentProps<T>>` = `{ item, onRefresh? }`), `onRefresh`, and PrimeReact pass-through `tablePt`/`tableClassName`/`menubarPt`/`menubarClassName`.

Columns and toolbar actions are compositional children:

```tsx
import { DataPage, MenuItem } from '@cratis/components/DataPage';
import { Column } from 'primereact/column';
import { DataPage, MenuItem, Column } from '@cratis/components/DataPage';

<DataPage title="Accounts" query={AllAccounts} emptyMessage="No accounts yet.">
<DataPage.Columns>
Expand Down
4 changes: 2 additions & 2 deletions .ai/rules/dialogs.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ It receives the current command values and **must return them** (mutated or not)

### CommandForm fields

Use built-in `CommandForm` fields (from `@cratis/components/CommandForm`) for every user-input value — a raw PrimeReact control inside a command dialog bypasses `CommandFormFieldWrapper`, so validation never re-runs and the submit button stays **permanently disabled**. Catalog: `InputTextField`, `NumberField`, `DropdownField`, `CheckboxField`, `TextAreaField`, `CalendarField`, `RadioButtonField`, `RadioGroupField`, `ChipsField`, `MultiSelectField`, `ColorPickerField`, `SliderField`.
Use built-in `CommandForm` fields (from `@cratis/components/CommandForm`) for every user-input value — a raw PrimeReact control inside a command dialog bypasses `CommandFormFieldWrapper`, so validation never re-runs and the submit button stays **permanently disabled**. Catalog: `InputTextField`, `PasswordField`, `NumberField`, `DropdownField`, `CheckboxField`, `ToggleSwitchField`, `TextAreaField`, `CalendarField`, `RadioButtonField`, `RadioGroupField`, `ChipsField`, `MultiSelectField`, `ColorPickerField`, `SliderField`, `RatingField`.

- The `value={c => c.name}` **accessor lambda doubles as the binding and type-checked field selection** — renaming a command property surfaces a compile error at every binding.
- **`RadioGroupField<T>`** renders a whole group from data (`options`/`optionLabel`/`optionValue`, `layout='horizontal'|'vertical'`); **`RadioButtonField<T>`** is one component per option (each takes a `buttonValue`). Both infer the value type from the accessor — no `as string` casts.
Expand Down Expand Up @@ -223,4 +223,4 @@ Use `buttons={null}` for dialogs that contain their own internal actions (e.g. a
| `width` | `string` | Dialog width (e.g. `'50vw'`) — replaces PrimeReact `style={{ width }}` |
| `resizable` | `boolean` | Default `false` |

PrimeReact-specific props (`style`, `contentStyle`, `modal`, `dismissableMask`, `draggable`, `footer`, `onHide`) are **not** available — do not use them.
`style`, `contentStyle`, and `dismissable` **are** supported. The other v10 PrimeReact Dialog props (`modal`, `dismissableMask`, `draggable`, `footer`, `onHide`) are **not** available — do not use them. (`resizable` is accepted for compatibility but is a no-op in PrimeReact 11.)
2 changes: 2 additions & 0 deletions .ai/rules/react.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ if (result.hasExceptions) { toast.error('Something went wrong'); console.error(r
// happy path — refresh queries, close, etc.
```

`toastCommandResult(result, opts)` from `@cratis/components/Notifications` collapses this whole branch into one call (with a `<Toaster />` mounted) — success/not-authorized/validation/exception → the right toast, no stack traces shown.

### Command helpers

- **`useCommandInstance(Command)`** — read the live reactive command instance for dependent fields (read it; never mutate — mutations go through field bindings).
Expand Down
6 changes: 3 additions & 3 deletions .ai/skills/cratis-react-page/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Import `DataPage` (and its `Column`/`MenuItem` helpers) from the **subpath**, no

```tsx
import { DataPage, MenuItem } from '@cratis/components/DataPage';
import { Column } from 'primereact/column';
import { Column } from '@cratis/components/DataPage';
import { CommandDialog } from '@cratis/components/CommandDialog';
import { useDialog, DialogProps } from '@cratis/arc.react/dialogs';
```
Expand All @@ -27,7 +27,7 @@ import { useDialog, DialogProps } from '@cratis/arc.react/dialogs';

```tsx
import { DataPage } from '@cratis/components/DataPage';
import { Column } from 'primereact/column';
import { Column } from '@cratis/components/DataPage';
import { AllAccounts } from './AllAccounts';

export const AccountsPage = () => (
Expand Down Expand Up @@ -68,7 +68,7 @@ export const CreateAccountDialog = ({ closeDialog }: DialogProps) => (

```tsx
import { DataPage, MenuItem } from '@cratis/components/DataPage';
import { Column } from 'primereact/column';
import { Column } from '@cratis/components/DataPage';
import { useDialog } from '@cratis/arc.react/dialogs';
import { CreateAccountDialog } from './CreateAccountDialog';

Expand Down
21 changes: 9 additions & 12 deletions .ai/skills/new-vertical-slice/references/PATTERNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public record StockDecreased(ISBN Isbn, BookStock StockBeforeDecrease);
// Copyright (c) Cratis. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { Column } from 'primereact/column';
import { Column } from '@cratis/components/DataPage';
import { DataTable } from 'primereact/datatable';
import { AllProjects } from './AllProjects';

Expand Down Expand Up @@ -318,8 +318,7 @@ export const AddProject = ({ closeDialog }: DialogProps) => {
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

import { DialogResult, useDialog } from '@cratis/arc.react/dialogs';
import { Menubar } from 'primereact/menubar';
import { MenuItem } from 'primereact/menuitem';
import { Button } from 'primereact/button';
import * as mdIcons from 'react-icons/md';
import { Page } from '@cratis/components/Common';
import { AddProject } from './Registration/AddProject';
Expand All @@ -328,17 +327,15 @@ import { Listing } from './Listing/Listing';
export const Projects = () => {
const [AddProjectDialog, showAddProjectDialog] = useDialog(AddProject);

const menuItems: MenuItem[] = [
{
label: 'Add Project',
icon: mdIcons.MdAdd,
command: async () => { await showAddProjectDialog(); }
}
];

// PrimeReact 11 removed the standalone Menubar; for a query-backed list
// page prefer `DataPage` + `<DataPage.MenuItems>`, or compose `Button`s
// (content is children in v11) for a custom toolbar.
return (
<Page title="Projects">
<Menubar model={menuItems} />
<Button variant="text" onClick={() => showAddProjectDialog()}>
<mdIcons.MdAdd />
<span>Add Project</span>
</Button>
<Listing />
<AddProjectDialog />
</Page>
Expand Down
2 changes: 1 addition & 1 deletion .ai/skills/stepper-command-dialog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Run a Release `dotnet build` to generate the `CreateProject` TypeScript proxy be

```tsx
import { StepperCommandDialog } from '@cratis/components/CommandDialog';
import { StepperPanel } from 'primereact/stepperpanel';
import { StepperPanel } from '@cratis/components/CommandDialog';
import { InputTextField, TextAreaField, NumberField } from '@cratis/components/CommandForm/fields';
import { DialogResult, useDialogContext } from '@cratis/arc.react/dialogs';
import { CreateProject } from '../api/Projects/CreateProject';
Expand Down
31 changes: 31 additions & 0 deletions Documentation/CommandForm/password-field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# PasswordField

`PasswordField` provides a masked text entry backed by the PrimeReact `InputPassword` component, bound to a `string` property on a command.

## Usage

```tsx
import { CommandDialog } from '@cratis/components/CommandDialog';
import { PasswordField } from '@cratis/components/CommandForm';

<CommandDialog command={SetPassword} visible={visible} onCancel={() => setVisible(false)}>
<PasswordField<SetPassword> value={c => c.password} placeholder="At least 8 characters" />
</CommandDialog>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `(instance: TCommand) => unknown` | — | **Required.** Accessor that returns the bound property from the command instance. Pass the command type as the generic parameter for full type safety. |
| `placeholder` | `string` | — | Placeholder text shown when the field is empty. |
| `className` | `string` | — | Extra CSS class combined with the default `w-full`. |
| `pt` | `InputPasswordProps['pt']` | — | PrimeReact pass-through configuration for the underlying `InputPassword`. |
| `ptOptions` | `InputPasswordProps['ptOptions']` | — | PrimeReact pass-through options. |
| `unstyled` | `boolean` | `false` | Disables every base PrimeReact style on the underlying `InputPassword`. |

## Behavior

- Default value is an empty string.
- The input masks its content; the underlying `InputPassword` provides the show/hide affordance.
- Validation state is reflected via the PrimeReact `invalid` flag.
32 changes: 32 additions & 0 deletions Documentation/CommandForm/rating-field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RatingField

`RatingField` provides a star-rating input backed by the PrimeReact `Rating` component, bound to a `number` property on a command.

## Usage

```tsx
import { CommandDialog } from '@cratis/components/CommandDialog';
import { RatingField } from '@cratis/components/CommandForm';

<CommandDialog command={SubmitReview} visible={visible} onCancel={() => setVisible(false)}>
<RatingField<SubmitReview> value={c => c.rating} stars={5} />
</CommandDialog>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `(instance: TCommand) => unknown` | — | **Required.** Accessor that returns the bound property from the command instance. Pass the command type as the generic parameter for full type safety. |
| `stars` | `number` | `5` | Number of stars to display. |
| `starAriaLabel` | `(starValue: number) => string` | `` n => `${n} star(s)` `` | Builds the accessible name for each star. Override to localize. |
| `className` | `string` | — | Extra CSS class name. |
| `pt` | `RatingRootProps['pt']` | — | PrimeReact pass-through configuration. |
| `ptOptions` | `RatingRootProps['ptOptions']` | — | PrimeReact pass-through options. |
| `unstyled` | `boolean` | `false` | Disables every base PrimeReact style on the underlying `Rating`. |

## Behavior

- Default value is `0` (no rating selected).
- The bound value is the selected star count (`1`–`stars`).
- Validation state is reflected via the PrimeReact `invalid` flag.
6 changes: 6 additions & 0 deletions Documentation/CommandForm/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@
href: multi-select-field.md
- name: NumberField
href: number-field.md
- name: PasswordField
href: password-field.md
- name: RadioButtonField
href: radio-button-field.md
- name: RadioGroupField
href: radio-group-field.md
- name: RatingField
href: rating-field.md
- name: SliderField
href: slider-field.md
- name: TextAreaField
href: text-area-field.md
- name: ToggleSwitchField
href: toggle-switch-field.md
31 changes: 31 additions & 0 deletions Documentation/CommandForm/toggle-switch-field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ToggleSwitchField

`ToggleSwitchField` provides an on/off switch backed by the PrimeReact `ToggleSwitch` component, bound to a `boolean` property on a command. It is the switch-styled counterpart of [CheckboxField](./checkbox-field.md).

## Usage

```tsx
import { CommandDialog } from '@cratis/components/CommandDialog';
import { ToggleSwitchField } from '@cratis/components/CommandForm';

<CommandDialog command={UpdateProfile} visible={visible} onCancel={() => setVisible(false)}>
<ToggleSwitchField<UpdateProfile> value={c => c.notificationsEnabled} label="Enable notifications" />
</CommandDialog>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `(instance: TCommand) => unknown` | — | **Required.** Accessor that returns the bound property from the command instance. Pass the command type as the generic parameter for full type safety. |
| `label` | `string` | — | Text displayed next to the switch. It is the switch's accessible name — override it to localize. |
| `className` | `string` | — | Extra CSS class forwarded to the underlying `ToggleSwitch`. |
| `pt` | `ToggleSwitchRootProps['pt']` | — | PrimeReact pass-through configuration. |
| `ptOptions` | `ToggleSwitchRootProps['ptOptions']` | — | PrimeReact pass-through options. |
| `unstyled` | `boolean` | `false` | Disables every base PrimeReact style on the underlying `ToggleSwitch`. |

## Behavior

- Default value is `false`.
- The switch is wrapped in a `<label>`, so the visible text is its accessible name and clicking the text toggles it.
- Validation state is reflected via the PrimeReact `invalid` flag.
2 changes: 1 addition & 1 deletion Documentation/CommandStepper/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Use it when you want to:

```tsx
import { CommandStepper } from '@cratis/components/CommandDialog';
import { StepperPanel } from 'primereact/stepperpanel';
import { StepperPanel } from '@cratis/components/CommandDialog';
import { InputTextField } from '@cratis/components/CommandForm/fields';
import { CreateProject } from '../api/projects/CreateProject';

Expand Down
Loading
Loading