@cratis/pi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +43 -0
  2. package/package.json +24 -0
  3. package/skills/cratis-application-react-specifications/LICENSE +2 -0
  4. package/skills/cratis-application-react-specifications/SKILL.md +192 -0
  5. package/skills/cratis-application-slice-specifications/LICENSE +2 -0
  6. package/skills/cratis-application-slice-specifications/SKILL.md +222 -0
  7. package/skills/cratis-arc-authentication-authorization-and-identity/LICENSE +2 -0
  8. package/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +233 -0
  9. package/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +140 -0
  10. package/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +112 -0
  11. package/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +102 -0
  12. package/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +85 -0
  13. package/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +87 -0
  14. package/skills/cratis-arc-command/LICENSE +2 -0
  15. package/skills/cratis-arc-command/SKILL.md +286 -0
  16. package/skills/cratis-arc-command/references/command-result.md +111 -0
  17. package/skills/cratis-arc-command/references/handler-shapes.md +135 -0
  18. package/skills/cratis-arc-command/references/proxy-generation.md +87 -0
  19. package/skills/cratis-arc-command/references/read-model-injection.md +97 -0
  20. package/skills/cratis-arc-command-execution/LICENSE +2 -0
  21. package/skills/cratis-arc-command-execution/SKILL.md +148 -0
  22. package/skills/cratis-arc-command-validation/LICENSE +2 -0
  23. package/skills/cratis-arc-command-validation/SKILL.md +230 -0
  24. package/skills/cratis-arc-ef-core-migration/LICENSE +2 -0
  25. package/skills/cratis-arc-ef-core-migration/SKILL.md +231 -0
  26. package/skills/cratis-arc-observable-query-http/LICENSE +2 -0
  27. package/skills/cratis-arc-observable-query-http/SKILL.md +157 -0
  28. package/skills/cratis-arc-query-paging/LICENSE +2 -0
  29. package/skills/cratis-arc-query-paging/SKILL.md +173 -0
  30. package/skills/cratis-arc-react-page/LICENSE +2 -0
  31. package/skills/cratis-arc-react-page/SKILL.md +291 -0
  32. package/skills/cratis-arc-react-page/references/data-page.md +125 -0
  33. package/skills/cratis-arc-react-page/references/data-tables.md +136 -0
  34. package/skills/cratis-arc-react-page/references/dialogs.md +238 -0
  35. package/skills/cratis-arc-react-page/references/mvvm.md +150 -0
  36. package/skills/cratis-arc-react-page/references/queries-and-commands.md +200 -0
  37. package/skills/cratis-chronicle-cli-operations/LICENSE +2 -0
  38. package/skills/cratis-chronicle-cli-operations/SKILL.md +199 -0
  39. package/skills/cratis-chronicle-client-dotnet/LICENSE +2 -0
  40. package/skills/cratis-chronicle-client-dotnet/SKILL.md +433 -0
  41. package/skills/cratis-chronicle-client-elixir/LICENSE +2 -0
  42. package/skills/cratis-chronicle-client-elixir/SKILL.md +429 -0
  43. package/skills/cratis-chronicle-client-kotlin/LICENSE +2 -0
  44. package/skills/cratis-chronicle-client-kotlin/SKILL.md +498 -0
  45. package/skills/cratis-chronicle-client-typescript/LICENSE +2 -0
  46. package/skills/cratis-chronicle-client-typescript/SKILL.md +396 -0
  47. package/skills/cratis-chronicle-compliance/LICENSE +2 -0
  48. package/skills/cratis-chronicle-compliance/SKILL.md +246 -0
  49. package/skills/cratis-chronicle-event-constraints/LICENSE +2 -0
  50. package/skills/cratis-chronicle-event-constraints/SKILL.md +227 -0
  51. package/skills/cratis-chronicle-event-modeling/LICENSE +2 -0
  52. package/skills/cratis-chronicle-event-modeling/SKILL.md +157 -0
  53. package/skills/cratis-chronicle-event-specifications/LICENSE +2 -0
  54. package/skills/cratis-chronicle-event-specifications/SKILL.md +200 -0
  55. package/skills/cratis-chronicle-event-type-migration/LICENSE +2 -0
  56. package/skills/cratis-chronicle-event-type-migration/SKILL.md +235 -0
  57. package/skills/cratis-chronicle-mcp-inspection/LICENSE +2 -0
  58. package/skills/cratis-chronicle-mcp-inspection/SKILL.md +79 -0
  59. package/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +12 -0
  60. package/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +8 -0
  61. package/skills/cratis-chronicle-multi-tenancy/LICENSE +2 -0
  62. package/skills/cratis-chronicle-multi-tenancy/SKILL.md +198 -0
  63. package/skills/cratis-chronicle-projection/LICENSE +2 -0
  64. package/skills/cratis-chronicle-projection/SKILL.md +162 -0
  65. package/skills/cratis-chronicle-projection/references/fluent-builder.md +126 -0
  66. package/skills/cratis-chronicle-projection/references/model-bound-attributes.md +118 -0
  67. package/skills/cratis-chronicle-reactor/LICENSE +2 -0
  68. package/skills/cratis-chronicle-reactor/SKILL.md +344 -0
  69. package/skills/cratis-chronicle-read-model/LICENSE +2 -0
  70. package/skills/cratis-chronicle-read-model/SKILL.md +193 -0
  71. package/skills/cratis-chronicle-read-model/references/queries.md +142 -0
  72. package/skills/cratis-chronicle-read-model-specifications/LICENSE +2 -0
  73. package/skills/cratis-chronicle-read-model-specifications/SKILL.md +176 -0
  74. package/skills/cratis-chronicle-reducer/LICENSE +2 -0
  75. package/skills/cratis-chronicle-reducer/SKILL.md +173 -0
  76. package/skills/cratis-chronicle-web-workbench/LICENSE +2 -0
  77. package/skills/cratis-chronicle-web-workbench/SKILL.md +177 -0
  78. package/skills/cratis-cli-terminal-workbench/LICENSE +2 -0
  79. package/skills/cratis-cli-terminal-workbench/SKILL.md +156 -0
  80. package/skills/cratis-code-review/LICENSE +2 -0
  81. package/skills/cratis-code-review/SKILL.md +177 -0
  82. package/skills/cratis-components-accessibility/LICENSE +2 -0
  83. package/skills/cratis-components-accessibility/SKILL.md +176 -0
  84. package/skills/cratis-components-schema-editor/LICENSE +2 -0
  85. package/skills/cratis-components-schema-editor/SKILL.md +238 -0
  86. package/skills/cratis-components-stepper-command-dialog/LICENSE +2 -0
  87. package/skills/cratis-components-stepper-command-dialog/SKILL.md +209 -0
  88. package/skills/cratis-components-styling/LICENSE +2 -0
  89. package/skills/cratis-components-styling/SKILL.md +221 -0
  90. package/skills/cratis-components-toolbar/LICENSE +2 -0
  91. package/skills/cratis-components-toolbar/SKILL.md +259 -0
  92. package/skills/cratis-documentation-writing/LICENSE +2 -0
  93. package/skills/cratis-documentation-writing/SKILL.md +121 -0
  94. package/skills/cratis-event-model-diagram/LICENSE +2 -0
  95. package/skills/cratis-event-model-diagram/SKILL.md +142 -0
  96. package/skills/cratis-fundamentals-concept/LICENSE +2 -0
  97. package/skills/cratis-fundamentals-concept/SKILL.md +198 -0
  98. package/skills/cratis-fundamentals-type-discovery/LICENSE +2 -0
  99. package/skills/cratis-fundamentals-type-discovery/SKILL.md +152 -0
  100. package/skills/cratis-governed-release-methodology/LICENSE +2 -0
  101. package/skills/cratis-governed-release-methodology/SKILL.md +297 -0
  102. package/skills/cratis-lens-browser-extension/LICENSE +2 -0
  103. package/skills/cratis-lens-browser-extension/SKILL.md +239 -0
  104. package/skills/cratis-performance-review/LICENSE +2 -0
  105. package/skills/cratis-performance-review/SKILL.md +139 -0
  106. package/skills/cratis-screenplay-model-authoring/LICENSE +2 -0
  107. package/skills/cratis-screenplay-model-authoring/SKILL.md +231 -0
  108. package/skills/cratis-security-review/LICENSE +2 -0
  109. package/skills/cratis-security-review/SKILL.md +141 -0
  110. package/skills/cratis-specification-by-example/LICENSE +2 -0
  111. package/skills/cratis-specification-by-example/SKILL.md +188 -0
  112. package/skills/cratis-specifications-csharp/LICENSE +2 -0
  113. package/skills/cratis-specifications-csharp/SKILL.md +230 -0
  114. package/skills/cratis-specifications-csharp/references/application-scenarios.md +191 -0
  115. package/skills/cratis-specifications-csharp/references/csharp-patterns.md +142 -0
  116. package/skills/cratis-specifications-csharp/references/integration-specs.md +126 -0
  117. package/skills/cratis-specifications-typescript/LICENSE +2 -0
  118. package/skills/cratis-specifications-typescript/SKILL.md +202 -0
  119. package/skills/cratis-specifications-typescript/references/typescript-patterns.md +168 -0
  120. package/skills/cratis-stage-rendering-and-sandbox/LICENSE +2 -0
  121. package/skills/cratis-stage-rendering-and-sandbox/SKILL.md +230 -0
  122. package/skills/cratis-studio-mcp-safety-guidance/LICENSE +2 -0
  123. package/skills/cratis-studio-mcp-safety-guidance/SKILL.md +69 -0
  124. package/skills/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +12 -0
  125. package/skills/cratis-studio-mcp-safety-guidance/references/observational-tools.md +8 -0
@@ -0,0 +1,291 @@
1
+ ---
2
+ name: cratis-arc-react-page
3
+ description: Build a React page in a Cratis Arc application with Cratis Components — DataPage lists, columns, toolbar menu items, command dialogs, confirmation and busy-indicator dialogs, row selection, details panes, snapshot and observable queries, paging, and MVVM view models. Use when building or changing a page that lists or displays data, wiring Add/Edit/Delete actions, connecting a component to a generated Arc query or command proxy, or asking the user to confirm something. Do not use for a multi-step wizard dialog alone, for a canvas tool palette, or for backend command and read-model work.
4
+ license: MIT
5
+ ---
6
+
7
+ # Cratis Arc React pages
8
+
9
+ Build the page from the generated Arc proxies and the Cratis Components
10
+ wrappers. Do not hand-roll a table, a dialog, or a fetch.
11
+
12
+ ## Verified product sources
13
+
14
+ This skill is verified against these exact package contracts:
15
+
16
+ | Package | Version | Verified from |
17
+ | --- | --- | --- |
18
+ | `@cratis/components` | `3.0.0` | its package manifest and component sources |
19
+ | `@cratis/arc` | `>=20.3.1 <23` | peer range declared by `@cratis/components@3.0.0` |
20
+ | `@cratis/arc.react` | `>=20.3.1 <23` | peer range declared by `@cratis/components@3.0.0` |
21
+ | `primereact` | `^11.0.0` | peer of `@cratis/components@3.0.0` |
22
+ | `primeicons` | `^8.0.0` | peer of `@cratis/components@3.0.0` |
23
+ | `react` | `^19.0.0` | peer of `@cratis/components@3.0.0` |
24
+
25
+ `@cratis/arc.react.mvvm` ships with `@cratis/arc.react` and is an explicit
26
+ application dependency. Reverify before claiming another version.
27
+
28
+ ## Import from subpaths, never the root barrel
29
+
30
+ The root `@cratis/components` entry exports **namespaces**, not components —
31
+ `import { DataPage } from '@cratis/components'` yields a namespace object whose
32
+ component is `DataPage.DataPage`. Always import from the subpath:
33
+
34
+ ```tsx
35
+ import { DataPage, MenuItem, Column } from '@cratis/components/DataPage';
36
+ import { CommandDialog } from '@cratis/components/CommandDialog';
37
+ import { InputTextField } from '@cratis/components/CommandForm';
38
+ import { Dialog } from '@cratis/components/Dialogs';
39
+ import { DialogProps, DialogResult, useDialog } from '@cratis/arc.react/dialogs';
40
+ ```
41
+
42
+ ## Step 1 — Prerequisites
43
+
44
+ - The backend query and command must already exist, and a Debug build must have
45
+ regenerated the TypeScript proxies. Generated proxies carry a
46
+ `**DO NOT EDIT** - This file is an automatically generated file.` header —
47
+ fix the C# source and rebuild instead of editing one.
48
+ - The app must mount `CratisComponentsProvider` (from
49
+ `@cratis/components/Common`) above every Cratis component. PrimeReact 11
50
+ resolves its configuration from a provider, so components fail without one.
51
+ - The app must import the stylesheets explicitly; components no longer import
52
+ their own CSS:
53
+
54
+ ```ts
55
+ import '@cratis/components/tokens'; // the --cratis-* token layer
56
+ import '@cratis/components/styles'; // every component stylesheet
57
+ import '@cratis/components/theme'; // optional license-free baseline look
58
+ ```
59
+
60
+ See the **cratis-components-styling** skill for the full theming contract.
61
+
62
+ ## Step 2 — The DataPage shell
63
+
64
+ `DataPage` owns the query subscription, paging, selection, action menubar, and
65
+ the optional details split. Do not pre-fetch rows and pass an array.
66
+
67
+ Required props: `title`, `query`, `emptyMessage`, `children`. Columns and
68
+ toolbar actions are compositional children.
69
+
70
+ ```tsx
71
+ import { Column, DataPage } from '@cratis/components/DataPage';
72
+ import { AllAccounts } from './AllAccounts';
73
+
74
+ export const AccountsPage = () => (
75
+ <DataPage title='Accounts' query={AllAccounts} emptyMessage='No accounts yet.' dataKey='id'>
76
+ <DataPage.Columns>
77
+ <Column field='name' header='Name' sortable />
78
+ <Column field='balance' header='Balance' />
79
+ </DataPage.Columns>
80
+ </DataPage>
81
+ );
82
+ ```
83
+
84
+ Only `DataPage.Columns` and `DataPage.MenuItems` exist as compound members.
85
+ `MenuItem` and `Column` are **named exports** — there is no `DataPage.MenuItem`
86
+ and no `DataPage.Column`. Pass `dataKey` whenever the read model has an
87
+ identity.
88
+
89
+ See [data-page.md](references/data-page.md) for every prop, and
90
+ [data-tables.md](references/data-tables.md) when you need a table without the
91
+ page chrome.
92
+
93
+ ## Step 3 — Toolbar actions
94
+
95
+ Menu items go in `<DataPage.MenuItems>`. `MenuItem` takes `command`, not
96
+ `onClick`, and its `icon` is a **React component type**, not an icon class
97
+ string. `disableOnUnselected` greys the item out until a row is selected.
98
+
99
+ ```tsx
100
+ import { DataPage, MenuItem } from '@cratis/components/DataPage';
101
+ import { useDialog } from '@cratis/arc.react/dialogs';
102
+ import { CreateAccountDialog } from './CreateAccountDialog';
103
+
104
+ const [CreateAccountWrapper, showCreateAccount] = useDialog(CreateAccountDialog);
105
+
106
+ <DataPage title='Accounts' query={AllAccounts} emptyMessage='No accounts yet.'>
107
+ <DataPage.MenuItems>
108
+ <MenuItem label='Add account' icon={() => <i className='pi pi-plus' />} command={() => showCreateAccount()} />
109
+ <MenuItem label='Edit account' icon={() => <i className='pi pi-pencil' />} command={() => showEditAccount()} disableOnUnselected />
110
+ </DataPage.MenuItems>
111
+ <DataPage.Columns>
112
+ <Column field='name' header='Name' />
113
+ </DataPage.Columns>
114
+ </DataPage>
115
+ <CreateAccountWrapper />
116
+ ```
117
+
118
+ Render the wrapper returned by `useDialog` once in the tree. See
119
+ [dialogs.md](references/dialogs.md) for the full dialog contract.
120
+
121
+ ## Step 4 — Command dialogs
122
+
123
+ A dialog that runs a command is its own component built on `CommandDialog`.
124
+ Bind each input with a `CommandForm` field whose `value` accessor selects the
125
+ command property; the label prop is `title`.
126
+
127
+ ```tsx
128
+ import { CommandDialog } from '@cratis/components/CommandDialog';
129
+ import { InputTextField } from '@cratis/components/CommandForm';
130
+ import { DialogResult, useDialogContext } from '@cratis/arc.react/dialogs';
131
+ import { CreateAccount } from './CreateAccount';
132
+
133
+ export const CreateAccountDialog = () => {
134
+ const { closeDialog } = useDialogContext();
135
+
136
+ return (
137
+ <CommandDialog<CreateAccount>
138
+ command={CreateAccount}
139
+ title='Create account'
140
+ okLabel='Create'
141
+ onSuccess={() => closeDialog(DialogResult.Ok)}
142
+ onCancel={() => closeDialog(DialogResult.Cancelled)}>
143
+ <InputTextField<CreateAccount> value={c => c.name} title='Account name' />
144
+ </CommandDialog>
145
+ );
146
+ };
147
+ ```
148
+
149
+ Never put a raw PrimeReact control inside a command dialog for a command value —
150
+ it bypasses the field wrapper, so validation never re-runs and the submit button
151
+ stays disabled. Seed values that must be present for validity with
152
+ `initialValues`, not `onBeforeExecute`.
153
+
154
+ ## Step 5 — Confirming, and showing that work is in progress
155
+
156
+ Do not build a confirmation or busy dialog into a page. `ConfirmationDialog` and
157
+ `BusyIndicatorDialog` are registered once at the app root through
158
+ `DialogComponents` and raised by hook, which is what keeps every one of them
159
+ identical:
160
+
161
+ ```tsx
162
+ import { DialogComponents } from '@cratis/arc.react/dialogs';
163
+ import { BusyIndicatorDialog, ConfirmationDialog } from '@cratis/components/Dialogs';
164
+
165
+ <DialogComponents confirmation={ConfirmationDialog} busyIndicator={BusyIndicatorDialog}>
166
+ <YourApp />
167
+ </DialogComponents>
168
+ ```
169
+
170
+ ```tsx
171
+ import { DialogButtons, DialogResult, useBusyIndicator, useConfirmationDialog } from '@cratis/arc.react/dialogs';
172
+
173
+ const [confirm] = useConfirmationDialog();
174
+ if (await confirm('Delete this account?', `"${account.name}" disappears permanently.`, DialogButtons.YesNo) !== DialogResult.Yes) return;
175
+ ```
176
+
177
+ A busy indicator is modal and deliberately non-dismissible, so reserve it for
178
+ work the user genuinely cannot proceed past — a multi-step import, a migration.
179
+ For an ordinary command behind a button, the in-flight disabled button is the
180
+ right control; a modal that flashes for 200 ms tells the user nothing. When you
181
+ do use one, pair it with the close call in a `finally`:
182
+
183
+ ```tsx
184
+ const [showBusy, closeBusy] = useBusyIndicator('Importing', 'This takes a moment.');
185
+ showBusy();
186
+ try { await importEverything(); } finally { closeBusy(); }
187
+ ```
188
+
189
+ ## Step 6 — Row selection and a details pane
190
+
191
+ `selection` and `onSelectionChange` are controlled. The change event carries
192
+ `value`, which is `null` when the selection is cleared.
193
+
194
+ ```tsx
195
+ import type { DataTableSelectionChangeEvent } from '@cratis/components/DataTables';
196
+
197
+ const [selected, setSelected] = useState<AccountSummary | null>(null);
198
+
199
+ <DataPage
200
+ title='Accounts'
201
+ query={AllAccounts}
202
+ emptyMessage='No accounts yet.'
203
+ dataKey='id'
204
+ selection={selected}
205
+ onSelectionChange={(event: DataTableSelectionChangeEvent<AccountSummary>) => setSelected(event.value)}
206
+ detailsComponent={AccountDetails}>
207
+ <DataPage.Columns>
208
+ <Column field='name' header='Name' />
209
+ </DataPage.Columns>
210
+ </DataPage>
211
+ ```
212
+
213
+ `detailsComponent` receives `IDetailsComponentProps<T>` — `{ item, onRefresh? }`
214
+ — and renders beside the table for the selected row.
215
+
216
+ ## Step 7 — Snapshot or observable query
217
+
218
+ The **same `query` prop** takes a snapshot query or an observable query. There
219
+ is no separate `observableQuery` prop: `DataPage` inspects the query prototype
220
+ and picks the snapshot or observable table itself. Pass an observable query and
221
+ the page stays live; pass a snapshot query and call `onRefresh` after a command
222
+ succeeds.
223
+
224
+ Read [queries-and-commands.md](references/queries-and-commands.md) for the
225
+ generated proxy hooks, their exact return tuples, paging, and how to read a
226
+ command result.
227
+
228
+ ## Step 8 — MVVM for pages with real logic
229
+
230
+ Extract a view model as soon as the component has three or more `useState`
231
+ calls, a state-synchronizing `useEffect`, or derived values. Keep the component
232
+ declarative and read `viewModel.x` **inside** JSX so MobX tracks it.
233
+
234
+ ```tsx
235
+ import { withViewModel } from '@cratis/arc.react.mvvm';
236
+ import { injectable } from 'tsyringe';
237
+
238
+ @injectable()
239
+ export class AccountsViewModel {
240
+ selected: AccountSummary | null = null;
241
+ select(account: AccountSummary | null) { this.selected = account; }
242
+ }
243
+
244
+ export const AccountsPage = withViewModel(AccountsViewModel, ({ viewModel }) => (
245
+ <DataPage title='Accounts' query={AllAccounts} emptyMessage='No accounts yet.' dataKey='id'
246
+ selection={viewModel.selected}
247
+ onSelectionChange={event => viewModel.select(event.value)}>
248
+ <DataPage.Columns>
249
+ <Column field='name' header='Name' />
250
+ </DataPage.Columns>
251
+ </DataPage>
252
+ ));
253
+ ```
254
+
255
+ `withViewModel` applies `makeAutoObservable` itself — do not call it in the
256
+ constructor. See [mvvm.md](references/mvvm.md) for injection, route parameters,
257
+ props handling, and teardown.
258
+
259
+ ## Quick decision guide
260
+
261
+ | Need | Use |
262
+ | --- | --- |
263
+ | Read-only list page | `DataPage` with a snapshot query |
264
+ | Live-updating list page | `DataPage` with an observable query on the same `query` prop |
265
+ | Add or create action | `<DataPage.MenuItems>` + `MenuItem` + `CommandDialog` + `useDialog` |
266
+ | Edit the selected row | `selection` / `onSelectionChange` + `CommandDialog` + `initialValues` / `currentValues` |
267
+ | Detail for the selected row | `detailsComponent` |
268
+ | Table without page chrome | `DataTableForQuery` / `DataTableForObservableQuery` |
269
+ | Ask the user to confirm | `useConfirmationDialog` — never `window.confirm`, never a hand-rolled Yes/No dialog |
270
+ | Multi-step wizard dialog | the **cratis-components-stepper-command-dialog** skill |
271
+ | Canvas tool palette | the **cratis-components-toolbar** skill |
272
+ | Complex page state | `withViewModel` |
273
+
274
+ ## Verify
275
+
276
+ - Every Cratis Components import uses a subpath, not the root barrel.
277
+ - `CratisComponentsProvider` wraps the tree and the three stylesheets are
278
+ imported once at the app entry point.
279
+ - `DataPage` receives `title`, `query`, `emptyMessage`, and children, and
280
+ `dataKey` when the read model has an identity.
281
+ - Columns sit in `<DataPage.Columns>`; menu items sit in
282
+ `<DataPage.MenuItems>` and use `command`, not `onClick`.
283
+ - No component pre-fetches rows and passes them to `DataPage` as an array.
284
+ - Every command value in a dialog is bound through a `CommandForm` field.
285
+ - Values required for validity come from `initialValues`, not
286
+ `onBeforeExecute`.
287
+ - Confirmations and busy indicators are raised by hook, registered once at the
288
+ app root.
289
+ - Every `showBusy()` has a matching close in a `finally`.
290
+ - No generated proxy file was edited.
291
+ - Lint, the frontend test gate, and the TypeScript build all pass.
@@ -0,0 +1,125 @@
1
+ # DataPage reference
2
+
3
+ `DataPage` is the standard list page: an action menubar, a data table, and an
4
+ optional details pane in one component. Import from
5
+ `@cratis/components/DataPage`.
6
+
7
+ ```tsx
8
+ import { Column, DataPage, MenuItem } from '@cratis/components/DataPage';
9
+ import type { IDetailsComponentProps } from '@cratis/components/DataPage';
10
+ ```
11
+
12
+ ## Compound members
13
+
14
+ Exactly two static members exist:
15
+
16
+ - `DataPage.Columns` — wraps the `Column` elements.
17
+ - `DataPage.MenuItems` — wraps the `MenuItem` elements.
18
+
19
+ `Column` and `MenuItem` are named exports of the same subpath. There is no
20
+ `DataPage.Column` and no `DataPage.MenuItem`.
21
+
22
+ ## `DataPageProps`
23
+
24
+ | Prop | Type | Notes |
25
+ | --- | --- | --- |
26
+ | `title` | `string` | required — menubar title |
27
+ | `query` | `Constructor<TQuery>` | required — a snapshot **or** observable query proxy |
28
+ | `emptyMessage` | `string` | required — shown when the query returns no rows |
29
+ | `children` | `ReactNode` | required — `DataPage.Columns` and optionally `DataPage.MenuItems` |
30
+ | `queryArguments` | `TArguments` | forwarded to the query |
31
+ | `dataKey` | `string \| undefined` | row identity; pass whenever the read model has one |
32
+ | `selection` | `any \| undefined \| null` | controlled selection |
33
+ | `onSelectionChange` | `(event: DataTableSelectionChangeEvent<any>) => void` | `event.value` is the row or `null` |
34
+ | `detailsComponent` | `React.FC<IDetailsComponentProps<any>>` | rendered beside the table for the selected row |
35
+ | `globalFilterFields` | `string[] \| undefined` | enables the global search box over these fields |
36
+ | `defaultFilters` | `DataTableFilterMeta` | initial per-column filter state |
37
+ | `onRefresh` | `() => void` | invoked to re-fetch a snapshot query |
38
+ | `actionsAriaLabel` | `string` | accessible name of the actions menubar, default `'Actions'` |
39
+ | `tableClassName` | `string` | class on the inner table |
40
+ | `tablePt` / `tablePtOptions` / `tableUnstyled` | PrimeReact pass-through | target the inner data table |
41
+ | `menubarClassName` | `string` | class on the action menubar |
42
+ | `menubarPt` / `menubarPtOptions` / `menubarUnstyled` | PrimeReact pass-through | target the action buttons |
43
+ | `clientFiltering` | `boolean` | **deprecated and a no-op** — do not use in new code |
44
+
45
+ ## Snapshot versus observable
46
+
47
+ There is no `observableQuery` prop. `DataPage` inspects the query prototype at
48
+ render time: a snapshot query renders the snapshot table, anything else renders
49
+ the observable table. Pass an observable query proxy and the page pushes
50
+ updates automatically; pass a snapshot query and re-fetch through `onRefresh`
51
+ after a command succeeds.
52
+
53
+ ## `MenuItemProps`
54
+
55
+ | Prop | Type | Notes |
56
+ | --- | --- | --- |
57
+ | `icon` | `React.ComponentType<{ className?: string }>` | a **component**, not an icon class string |
58
+ | `label` | `string` | menu label |
59
+ | `command` | `() => void` | the action — **not** `onClick` |
60
+ | `disabled` | `boolean` | unconditionally disabled |
61
+ | `disableOnUnselected` | `boolean` | disabled until a row is selected |
62
+
63
+ `MenuItem` renders nothing on its own; it is a marker that `DataPage.MenuItems`
64
+ reads. Icons are usually a small inline component:
65
+
66
+ ```tsx
67
+ <MenuItem label='Add account' icon={() => <i className='pi pi-plus' />} command={() => showCreate()} />
68
+ ```
69
+
70
+ ## `IDetailsComponentProps<TDataType>`
71
+
72
+ ```ts
73
+ interface IDetailsComponentProps<TDataType> {
74
+ item: TDataType;
75
+ onRefresh?: () => void;
76
+ }
77
+ ```
78
+
79
+ ```tsx
80
+ const AccountDetails = ({ item }: IDetailsComponentProps<AccountSummary>) => <div>{item.name}</div>;
81
+ ```
82
+
83
+ ## `Column`
84
+
85
+ `Column` is re-exported from `@cratis/components/DataPage` and defined in
86
+ `@cratis/components/DataTables`. It renders nothing itself — it is a
87
+ declaration the table reads.
88
+
89
+ | Prop | Type |
90
+ | --- | --- |
91
+ | `field` | `string` |
92
+ | `header` | `React.ReactNode` |
93
+ | `body` | `(rowData: TData) => React.ReactNode` |
94
+ | `sortable` | `boolean` |
95
+ | `filter` | `boolean` |
96
+ | `filterField` | `string` |
97
+ | `filterPlaceholder` | `string` |
98
+ | `dataType` | `'text' \| 'numeric' \| 'date' \| 'boolean'` |
99
+ | `showFilterMatchModes` | `boolean` |
100
+ | `filterElement` | `(options: ColumnFilterElementOptions) => ReactNode` |
101
+ | `filterLabels` | `Partial<ColumnFilterMenuLabels>` |
102
+ | `selectionMode` | `'single' \| 'multiple'` |
103
+ | `style` / `className` | `React.CSSProperties` / `string` |
104
+ | `headerStyle` / `headerClassName` | `React.CSSProperties` / `string` |
105
+ | `bodyStyle` / `bodyClassName` | `React.CSSProperties` / `string` |
106
+
107
+ Use `body` for a computed or formatted cell:
108
+
109
+ ```tsx
110
+ <Column field='balance' header='Balance' body={row => `$${row.balance.toFixed(2)}`} />
111
+ ```
112
+
113
+ ## Type name caution
114
+
115
+ `@cratis/components/DataPage` exports two different things called
116
+ `ColumnProps` — the props of the `DataPage.Columns` wrapper and the re-exported
117
+ column declaration props. Import `ColumnProps` from
118
+ `@cratis/components/DataTables` when you need the column type, so the reference
119
+ is unambiguous.
120
+
121
+ ## Layout
122
+
123
+ The details pane renders in a resizable split. Give the page a bounded height —
124
+ the stories wrap `DataPage` in a container with an explicit height — or the
125
+ split has nothing to size against.
@@ -0,0 +1,136 @@
1
+ # Data tables reference
2
+
3
+ Use a standalone table when you need query-backed rows **without** the
4
+ `DataPage` chrome — embedded in a panel, a card, or a dialog. Import from
5
+ `@cratis/components/DataTables`.
6
+
7
+ ```tsx
8
+ import {
9
+ Column,
10
+ DataTableForObservableQuery,
11
+ DataTableForQuery,
12
+ } from '@cratis/components/DataTables';
13
+ import type { DataTableSelectionChangeEvent } from '@cratis/components/DataTables';
14
+ ```
15
+
16
+ ## Pick the component by query kind
17
+
18
+ | Situation | Component |
19
+ | --- | --- |
20
+ | Full list page with menubar and details | `DataPage` |
21
+ | Embedded table, snapshot query | `DataTableForQuery` |
22
+ | Embedded table, observable query | `DataTableForObservableQuery` |
23
+
24
+ Unlike `DataPage`, these do **not** auto-detect — pass a snapshot query to
25
+ `DataTableForQuery` and an observable query to `DataTableForObservableQuery`.
26
+
27
+ ## Shared props
28
+
29
+ Both components take the same prop names:
30
+
31
+ | Prop | Type | Notes |
32
+ | --- | --- | --- |
33
+ | `query` | `Constructor<TQuery>` | required |
34
+ | `emptyMessage` | `string` | required |
35
+ | `children` | `ReactNode` | the `Column` declarations |
36
+ | `queryArguments` | `TArguments` | forwarded to the query |
37
+ | `dataKey` | `string \| undefined` | row identity |
38
+ | `selection` | `TDataType \| undefined \| null` | controlled selection |
39
+ | `onSelectionChange` | `(event: DataTableSelectionChangeEvent<TDataType>) => void` | `event.value` is the row or `null` |
40
+ | `globalFilterFields` | `string[] \| undefined` | enables the search box |
41
+ | `defaultFilters` | `DataTableFilterMeta` | initial filter state |
42
+ | `className` | `string` | |
43
+ | `pt` / `ptOptions` / `unstyled` | PrimeReact pass-through | |
44
+ | `paginatorClassName` | `string` | |
45
+ | `paginatorAriaLabels` | paginator label overrides | localize the paginator |
46
+ | `clientFiltering` | `boolean` | **deprecated and a no-op** |
47
+
48
+ ```tsx
49
+ <DataTableForQuery<AllAccounts, AccountSummary, object>
50
+ query={AllAccounts}
51
+ emptyMessage='No accounts found'
52
+ dataKey='id'
53
+ globalFilterFields={['name', 'ownerName']}
54
+ selection={selected}
55
+ onSelectionChange={event => setSelected(event.value)}>
56
+ <Column field='name' header='Name' sortable filter filterPlaceholder='Filter by name' />
57
+ <Column field='ownerName' header='Owner' />
58
+ </DataTableForQuery>
59
+ ```
60
+
61
+ ## Paging
62
+
63
+ Both tables page server-side through the Arc paging hooks with a fixed page
64
+ size of 20 rows starting at page 0. The paginator renders only when the result
65
+ reports more than one page. There is no page-size prop on these components —
66
+ when you need caller-controlled paging, call the query's paging hook yourself
67
+ (see [queries-and-commands.md](queries-and-commands.md)) and render the rows
68
+ with your own layout.
69
+
70
+ ## Selection event
71
+
72
+ ```ts
73
+ interface DataTableSelectionChangeEvent<TData> {
74
+ value: TData | null;
75
+ originalEvent?: SyntheticEvent;
76
+ }
77
+ ```
78
+
79
+ Add a selection column with `<Column selectionMode='single' headerStyle={{ width: '3rem' }} />`
80
+ when you want an explicit selector rather than row click selection.
81
+
82
+ ## Column filtering
83
+
84
+ Set `filter` on a column to get a per-column filter menu. `dataType` picks the
85
+ match modes (`'text'`, `'numeric'`, `'date'`, `'boolean'`; default `'text'`),
86
+ `showFilterMatchModes={false}` hides the mode picker, and `filterElement`
87
+ replaces the editor entirely:
88
+
89
+ ```tsx
90
+ <Column
91
+ field='rating'
92
+ header='Rating'
93
+ filter
94
+ filterElement={options => (
95
+ <RatingEditor value={options.value} onChange={value => options.onChange(value)} onApply={options.onApply} />
96
+ )} />
97
+ ```
98
+
99
+ `ColumnFilterElementOptions` gives `field`, `value`, `matchMode`, `onChange`,
100
+ `onApply`, and `onClear`.
101
+
102
+ Filter menu strings are localizable per column with `filterLabels`
103
+ (`filterTriggerAriaLabel`, `clear`, `apply`, `true`, `false`) — never ship a
104
+ hard-coded English accessible name.
105
+
106
+ ## Filter state shape
107
+
108
+ ```ts
109
+ type DataTableFilterConstraint = { value: unknown; matchMode?: DataTableFilterMatchMode };
110
+ type DataTableFilterMeta = Record<string, DataTableFilterConstraint>;
111
+ ```
112
+
113
+ `DataTableFilterMatchMode` is a const object, not an enum:
114
+ `StartsWith`, `Contains`, `NotContains`, `EndsWith`, `Equals`, `NotEquals`,
115
+ `In`, `Between`, `LessThan`, `LessThanOrEqual`, `GreaterThan`,
116
+ `GreaterThanOrEqual`, `DateIs`, `DateIsNot`, `DateBefore`, `DateAfter`.
117
+
118
+ ## Custom match modes
119
+
120
+ Register a matcher to add a domain-specific mode, and unregister it when the
121
+ owner unmounts:
122
+
123
+ ```ts
124
+ import { registerDataTableFilterMatcher } from '@cratis/components/DataTables';
125
+
126
+ const registration = registerDataTableFilterMatcher(
127
+ 'startsWithAccountPrefix',
128
+ (value, filter) => String(value).startsWith(String(filter)));
129
+
130
+ // later
131
+ registration.unregister();
132
+ ```
133
+
134
+ Registration rejects names that collide with a built-in mode, names that are
135
+ not identifier-like, and prototype-polluting names. Re-registering the same
136
+ name with a *different* function throws — register once per matcher.