@craftainer/crud-views 0.0.0-stage → 0.2.0

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.
@@ -0,0 +1,1180 @@
1
+ import { ComponentType, ReactElement, ReactNode } from "react";
2
+ import { Theme } from "@mui/material/styles";
3
+ import { InfiniteData, QueryClient, UseInfiniteQueryResult, UseMutationResult, UseQueryResult } from "@tanstack/react-query";
4
+ import { BulkResult, Capabilities, CrudClient, Filters, JsonObject, Resource, ResourceClient } from "@craftainer/crud-sdk";
5
+ import { CrudQuery, InfiniteListInit, TanstackApi, UpdateVariables } from "@craftainer/crud-sdk/tanstack";
6
+ import { DataGridProps } from "@mui/x-data-grid";
7
+ //#region src/core/types.d.ts
8
+ /** What a manifest field renders as. `array` and `json` are fallbacks for schema members the manifest `fields` list does not cover. */
9
+ type FieldKind = "string" | "number" | "integer" | "boolean" | "date" | "datetime" | "enum" | "array" | "json";
10
+ /** The manifest's `filter_ops` vocabulary (`eq` is the bare `field=` form). */
11
+ type FilterOp = "eq" | "min" | "max" | "in" | "contains" | "icontains" | "regex";
12
+ /** A JSON value as it travels on the wire. */
13
+ type FieldValue = string | number | boolean | null | undefined | readonly FieldValue[] | {
14
+ readonly [key: string]: FieldValue;
15
+ };
16
+ /** Validation hints lifted from the read/create/update JSON Schemas. */
17
+ interface FieldConstraints {
18
+ readonly minLength: number | undefined;
19
+ readonly maxLength: number | undefined;
20
+ readonly minimum: number | undefined;
21
+ readonly maximum: number | undefined;
22
+ readonly pattern: string | undefined;
23
+ }
24
+ /** One field of a resource, merged from the manifest `fields` and `schemas`. */
25
+ interface FieldDescriptor {
26
+ readonly name: string;
27
+ /** Human label derived from `name` (`power_level` -> `Power level`). */
28
+ readonly label: string;
29
+ readonly kind: FieldKind;
30
+ /** The JSON Schema `format` (`email`, `uri`, `uuid`, ...), or `null`. */
31
+ readonly format: string | null;
32
+ /** The value may be `null` (the schema allows it). */
33
+ readonly nullable: boolean;
34
+ /** Neither `create` nor `update` accepts it: show it, never edit it. */
35
+ readonly readOnly: boolean;
36
+ /** `create` requires it. */
37
+ readonly required: boolean;
38
+ /** `enum` choices (from the manifest field, else the schema). */
39
+ readonly choices: readonly string[] | null;
40
+ /** For `array`, the kind of the items. */
41
+ readonly itemKind: FieldKind | null;
42
+ readonly constraints: FieldConstraints;
43
+ /** The manifest lists it as a field: it can be filtered/sorted per `filterOps`/`sortable`. */
44
+ readonly inManifest: boolean;
45
+ readonly sortable: boolean;
46
+ readonly filterOps: readonly FilterOp[];
47
+ readonly numeric: boolean;
48
+ readonly categorical: boolean;
49
+ }
50
+ /** One operand of a filter. `datetime` filters carry `Date`s, `date` filters `YYYY-MM-DD` strings (the SDK's query builder wants exactly that). */
51
+ type FilterScalar = string | number | boolean | Date;
52
+ /** The filter state of one field: operator -> operand (`in` takes an array). Absent or empty means no filter. */
53
+ type FieldFilter = Readonly<Partial<Record<FilterOp, FilterScalar | readonly FilterScalar[]>>>;
54
+ /** Filter state of a whole resource: field name -> its filter. */
55
+ type FieldFilters = Readonly<Record<string, FieldFilter | undefined>>;
56
+ /** Props every renderer gets. */
57
+ interface RendererBase {
58
+ readonly field: FieldDescriptor;
59
+ /** The resource name, for renderers that special-case a field. */
60
+ readonly resource: string;
61
+ readonly size?: "small" | "medium" | undefined;
62
+ }
63
+ /** Shows a value. `value` is the wire value (`undefined` when the record has no such member). */
64
+ interface DisplayRendererProps extends RendererBase {
65
+ readonly value: FieldValue;
66
+ readonly record?: Readonly<Record<string, FieldValue>> | undefined;
67
+ }
68
+ /**
69
+ * Edits a value. Wire values in and out: strings for `string`/`enum`/`date`
70
+ * (`YYYY-MM-DD`)/`datetime` (ISO-8601 UTC), numbers for `number`/`integer`,
71
+ * booleans, arrays and JSON for `array`/`json`. A cleared input emits `null`
72
+ * when the field is nullable, else `undefined` (leave the member out).
73
+ */
74
+ interface InputRendererProps extends RendererBase {
75
+ readonly value: FieldValue;
76
+ readonly onChange: (value: FieldValue) => void;
77
+ readonly onBlur?: (() => void) | undefined;
78
+ /** Validation message to show (the form owns validation). */
79
+ readonly error?: string | undefined;
80
+ readonly helperText?: string | undefined;
81
+ readonly disabled?: boolean | undefined;
82
+ readonly autoFocus?: boolean | undefined;
83
+ readonly id?: string | undefined;
84
+ /**
85
+ * Reports whether the input's own draft is invalid, with the reason: it is
86
+ * called with a message when the draft cannot be turned into a valid value
87
+ * (non-numeric, malformed JSON, out of range) and with `null` when it is
88
+ * valid again (or the input unmounts). A form blocks submit while any field
89
+ * reports a reason. Additive to the frozen I4 (ADR 0002 addendum).
90
+ */
91
+ readonly onInvalid?: ((reason: string | null) => void) | undefined;
92
+ }
93
+ /** Edits the filter of one field. Emits `undefined` when cleared. */
94
+ interface FilterRendererProps extends RendererBase {
95
+ readonly value: FieldFilter | undefined;
96
+ readonly onChange: (value: FieldFilter | undefined) => void;
97
+ }
98
+ type DisplayRenderer = ComponentType<DisplayRendererProps>;
99
+ type InputRenderer = ComponentType<InputRendererProps>;
100
+ type FilterRenderer = ComponentType<FilterRendererProps>;
101
+ type RendererRole = "display" | "input" | "filter";
102
+ /** The three renderers for one selector; each is optional. */
103
+ interface FieldRenderers {
104
+ readonly display?: DisplayRenderer;
105
+ readonly input?: InputRenderer;
106
+ readonly filter?: FilterRenderer;
107
+ }
108
+ /**
109
+ * Selector -> renderers. A selector is `"<resource>.<field>"` (one field),
110
+ * `"<kind>:<format>"` (`string:email`) or `"<kind>"` (`date`).
111
+ */
112
+ type RendererOverrides = Readonly<Record<string, FieldRenderers>>;
113
+ //#endregion
114
+ //#region src/core/provider.d.ts
115
+ interface CrudProviderProps {
116
+ readonly client: CrudClient;
117
+ readonly queryClient: QueryClient;
118
+ /** Wraps the tree in this MUI theme. Omit to inherit the ambient theme. */
119
+ readonly theme?: Theme | undefined;
120
+ /** Renderer overrides for this tree (selector -> renderers). */
121
+ readonly renderers?: RendererOverrides | undefined;
122
+ readonly scope?: string | undefined;
123
+ readonly locale?: string | undefined;
124
+ readonly timeZone?: string | undefined;
125
+ readonly children?: ReactNode;
126
+ }
127
+ /** Provides the SDK client, the TanStack `QueryClient`, an optional MUI theme and renderer overrides. */
128
+ export declare function CrudProvider({ client, queryClient, theme, renderers, scope, locale, timeZone, children }: CrudProviderProps): ReactElement;
129
+ //#endregion
130
+ //#region src/core/context.d.ts
131
+ /** What `useCrud` returns. */
132
+ interface CrudContextValue {
133
+ readonly client: CrudClient;
134
+ readonly queryClient: QueryClient;
135
+ /** Separates cached manifests of different callers (e.g. the user id). */
136
+ readonly scope: string;
137
+ /** BCP 47 tag for number and date formatting; `undefined` = the browser's. */
138
+ readonly locale: string | undefined;
139
+ /** IANA zone for `datetime` display; `undefined` = the browser's. */
140
+ readonly timeZone: string | undefined;
141
+ readonly renderers: RendererOverrides | undefined;
142
+ }
143
+ /** The provider's value; throws outside a `CrudProvider`. */
144
+ export declare function useCrud(): CrudContextValue;
145
+ //#endregion
146
+ //#region src/core/useResource.d.ts
147
+ /** TanStack hooks bound to one resource. Stable between renders while the resource is. */
148
+ interface ResourceHooks<Read extends object = JsonObject, Create extends object = JsonObject, Update extends object = JsonObject> {
149
+ useList(query?: CrudQuery): UseQueryResult<Read[]>;
150
+ useInfiniteList(query?: CrudQuery, init?: InfiniteListInit): UseInfiniteQueryResult<InfiniteData<Read[], number>>;
151
+ useRecord(id: number): UseQueryResult<Read>;
152
+ useStats(query?: CrudQuery): UseQueryResult<JsonObject>;
153
+ usePredict(query?: CrudQuery): UseQueryResult<JsonObject>;
154
+ useRevisions(id: number): UseQueryResult<JsonObject[]>;
155
+ /** Invalidates lists, stats and predictions on success. */
156
+ useCreate(): UseMutationResult<Read, Error, Create>;
157
+ /** Variables carry `ifMatch` for `If-Match`; a stale one fails with `PreconditionFailed`. */
158
+ useUpdate(): UseMutationResult<Read, Error, UpdateVariables<Update>>;
159
+ useDelete(): UseMutationResult<unknown, Error, number>;
160
+ }
161
+ /** The loaded state of `useResource`. */
162
+ interface ReadyResource<Read extends object = JsonObject, Create extends object = JsonObject, Update extends object = JsonObject> {
163
+ readonly status: "ready";
164
+ readonly error: null;
165
+ /** The manifest entry (schemas, fields, pagination, paths, ...). */
166
+ readonly resource: Resource;
167
+ /** Shortcut for `resource.capabilities`: gate every optional view on it. */
168
+ readonly capabilities: Capabilities;
169
+ readonly fields: readonly FieldDescriptor[];
170
+ field(name: string): FieldDescriptor | undefined;
171
+ /** The SDK handle, for calls the hooks do not cover (clone, publish, restore, bulk). */
172
+ readonly handle: ResourceClient<Read, Create, Update>;
173
+ readonly api: TanstackApi<Read, Create, Update>;
174
+ readonly hooks: ResourceHooks<Read, Create, Update>;
175
+ }
176
+ interface PendingResource {
177
+ readonly status: "pending";
178
+ readonly error: null;
179
+ }
180
+ interface FailedResource {
181
+ readonly status: "error";
182
+ readonly error: Error;
183
+ }
184
+ type UseResourceResult<Read extends object = JsonObject, Create extends object = JsonObject, Update extends object = JsonObject> = PendingResource | FailedResource | ReadyResource<Read, Create, Update>;
185
+ /**
186
+ * Loads the manifest (once per caller scope) and returns the resource `name`
187
+ * (the highest `api_version` unless given) with its capabilities, described
188
+ * fields, SDK handle and TanStack hooks. Query keys use `name`, or
189
+ * `name@apiVersion` when `apiVersion` is given, so two versions never share a cache entry.
190
+ */
191
+ export declare function useResource<Read extends object = JsonObject, Create extends object = JsonObject, Update extends object = JsonObject>(name: string, apiVersion?: string): UseResourceResult<Read, Create, Update>;
192
+ //#endregion
193
+ //#region src/core/ResourceGate.d.ts
194
+ interface ResourceGateProps {
195
+ readonly name: string;
196
+ readonly apiVersion?: string | undefined;
197
+ /** Shown while the manifest loads. Default: a spinner. */
198
+ readonly pending?: ReactNode;
199
+ /** Shown on failure (unknown resource, network). Default: an error alert. */
200
+ readonly fallback?: ((error: Error) => ReactNode) | undefined;
201
+ readonly children: (ready: ReadyResource) => ReactNode;
202
+ }
203
+ /** Renders `children` once the resource is loaded; the common loading and error states otherwise. */
204
+ export declare function ResourceGate({ name, apiVersion, pending, fallback, children }: ResourceGateProps): ReactElement;
205
+ //#endregion
206
+ //#region src/core/fields.d.ts
207
+ /** `power_level` -> `Power level`. */
208
+ export declare function humanize(name: string): string;
209
+ /**
210
+ * Every field of `resource`, in read-schema order, then manifest fields the
211
+ * read schema does not list. The `id` field is included (read only).
212
+ */
213
+ export declare function describeFields(resource: Resource): readonly FieldDescriptor[];
214
+ //#endregion
215
+ //#region src/core/registry.d.ts
216
+ /** The renderer type of each role. */
217
+ interface RendererByRole {
218
+ readonly display: DisplayRenderer;
219
+ readonly input: InputRenderer;
220
+ readonly filter: FilterRenderer;
221
+ }
222
+ /** The selectors that match `field` of `resource`, most specific first. */
223
+ export declare function selectorsFor(field: FieldDescriptor, resource: string): readonly string[];
224
+ /** One renderer role resolved from `layers` (nearest first); `undefined` when no layer has one. */
225
+ export declare function resolveRenderer<R extends RendererRole>(role: R, field: FieldDescriptor, resource: string, layers: readonly (RendererOverrides | undefined)[]): RendererByRole[R] | undefined;
226
+ /**
227
+ * Registers renderers process-wide (the layer between `CrudProvider`'s
228
+ * `renderers` and the built-ins). A later registration wins per role. Returns
229
+ * the function that removes exactly this registration, in any order.
230
+ * Prefer the provider's `renderers` prop in apps: this is shared state.
231
+ */
232
+ export declare function registerFieldRenderer(selector: string, renderers: FieldRenderers): () => void;
233
+ /** The process-wide registrations merged (a new object after every change). */
234
+ export declare function getRegisteredRenderers(): RendererOverrides;
235
+ //#endregion
236
+ //#region src/core/renderers/builtins.d.ts
237
+ /** The built-in layer: one entry per `FieldKind`. */
238
+ export declare const builtinRenderers: RendererOverrides;
239
+ //#endregion
240
+ //#region src/core/fieldComponents.d.ts
241
+ /** Props of the three field components: the renderer props plus a per-component override layer. */
242
+ type WithOverrides<P> = P & {
243
+ /** Highest-precedence layer, for this one component. */
244
+ readonly renderers?: RendererOverrides | undefined;
245
+ };
246
+ /** Shows `value` with the resolved display renderer. */
247
+ export declare function FieldDisplay({ renderers, ...props }: WithOverrides<DisplayRendererProps>): ReactElement;
248
+ /** Edits a value with the resolved input renderer. */
249
+ export declare function FieldInput({ renderers, ...props }: WithOverrides<InputRendererProps>): ReactElement;
250
+ /** Edits the filter of a field with the resolved filter renderer. */
251
+ export declare function FieldFilterInput({ renderers, ...props }: WithOverrides<FilterRendererProps>): ReactElement;
252
+ //#endregion
253
+ //#region src/core/values.d.ts
254
+ /** What a cleared input emits: `null` for a nullable field, else `undefined`. */
255
+ export declare const emptyValue: (field: FieldDescriptor) => FieldValue;
256
+ //#endregion
257
+ //#region src/core/filterState.d.ts
258
+ /** Sets `op` on `current`; an empty operand removes it, and no operators left is `undefined`. */
259
+ export declare function withOp(current: FieldFilter | undefined, op: FilterOp, operand: FilterScalar | readonly FilterScalar[] | undefined): FieldFilter | undefined;
260
+ /** Whether the manifest lets a client filter on this field at all. */
261
+ export declare const isFilterable: (field: FieldDescriptor) => boolean;
262
+ //#endregion
263
+ //#region src/core/filters.d.ts
264
+ /**
265
+ * Turns per-field filter state into the SDK's flat `Filters`: `eq` is the bare
266
+ * field name, every other operator is `field__op`.
267
+ */
268
+ export declare function toQueryFilters(filters: FieldFilters): Filters;
269
+ //#endregion
270
+ //#region src/core/shells.d.ts
271
+ /** Props every shell shares. */
272
+ interface ShellProps {
273
+ readonly title: ReactNode;
274
+ /** Buttons for the footer (dialog, drawer) or the header (page). */
275
+ readonly actions?: ReactNode;
276
+ readonly children?: ReactNode;
277
+ }
278
+ interface PageShellProps extends ShellProps {
279
+ readonly subtitle?: ReactNode;
280
+ /** Links shown above the title, e.g. `[<Link>Heroes</Link>]`. */
281
+ readonly breadcrumbs?: readonly ReactNode[] | undefined;
282
+ readonly maxWidth?: "sm" | "md" | "lg" | "xl" | false;
283
+ }
284
+ /** A full page: heading row (title, subtitle, actions) above the content. */
285
+ export declare function PageShell({ title, subtitle, actions, breadcrumbs, maxWidth, children }: PageShellProps): ReactElement;
286
+ interface OverlayShellProps extends ShellProps {
287
+ readonly open: boolean;
288
+ readonly onClose: () => void;
289
+ /** Label of the close button that is always present. Default "Close". */
290
+ readonly closeLabel?: string;
291
+ }
292
+ interface DialogShellProps extends OverlayShellProps {
293
+ readonly maxWidth?: "xs" | "sm" | "md" | "lg";
294
+ }
295
+ /** A modal dialog with a title, scrolling content and a footer of actions. */
296
+ export declare function DialogShell({ open, onClose, title, actions, closeLabel, maxWidth, children }: DialogShellProps): ReactElement;
297
+ interface DrawerShellProps extends OverlayShellProps {
298
+ readonly anchor?: "left" | "right";
299
+ /** Width in theme spacing units on screens from `sm` up (full width below). Default 60. */
300
+ readonly width?: number;
301
+ }
302
+ /** A side drawer with a title, scrolling content and a footer of actions. */
303
+ export declare function DrawerShell({ open, onClose, title, actions, closeLabel, anchor, width, children }: DrawerShellProps): ReactElement;
304
+ //#endregion
305
+ //#region src/bulk/selection.d.ts
306
+ /** Which records are selected: explicit ids, or every record matching the current filters. */
307
+ interface BulkSelection {
308
+ /** Ids ticked by hand (empty while `allMatching`). */
309
+ readonly selected: readonly number[];
310
+ /** All records matching the list's filters are selected, on every page. */
311
+ readonly allMatching: boolean;
312
+ isSelected(id: number): boolean;
313
+ /**
314
+ * Ticks or unticks one record. Passing the visible page's ids lets an
315
+ * untick while `allMatching` keep the other visible records selected.
316
+ */
317
+ toggle(id: number, visible?: readonly number[]): void;
318
+ /** Ticks or unticks every id of the visible page. */
319
+ setPage(ids: readonly number[], on: boolean): void;
320
+ selectAllMatching(): void;
321
+ clear(): void;
322
+ }
323
+ /** Selection state for a list. Ticking a row after "select all matching" returns to explicit ids. */
324
+ export declare function useBulkSelection(): BulkSelection;
325
+ //#endregion
326
+ //#region src/bulk/BulkToolbar.d.ts
327
+ type BulkAction = "delete" | "export" | "publish";
328
+ interface BulkDone {
329
+ readonly action: BulkAction;
330
+ /** Records the call touched. */
331
+ readonly count: number;
332
+ }
333
+ interface BulkToolbarProps {
334
+ readonly ready: ReadyResource;
335
+ readonly selection: BulkSelection;
336
+ /** The list's current filters. "Select all matching" needs at least one. */
337
+ readonly filters?: FieldFilters | undefined;
338
+ /** Actions to offer; each also needs the matching capability. Default: all. */
339
+ readonly actions?: readonly BulkAction[] | undefined;
340
+ /** Receives the CSV of "Export"; default: a file download. */
341
+ readonly onExport?: ((csv: string, filename: string) => void) | undefined;
342
+ readonly onDone?: ((done: BulkDone) => void) | undefined;
343
+ }
344
+ /** The toolbar over a list's selection: count, bulk delete, export and bulk publish. Hidden while nothing is selected. */
345
+ export declare function BulkToolbar({ ready, selection, filters, actions, onExport, onDone }: BulkToolbarProps): ReactElement | null;
346
+ //#endregion
347
+ //#region src/bulk/BulkRecords.d.ts
348
+ interface BulkPanelProps extends Pick<BulkToolbarProps, "actions" | "onExport" | "onDone"> {
349
+ readonly ready: ReadyResource;
350
+ readonly filters?: FieldFilters | undefined;
351
+ /** Columns to show; default: the first four non-system fields. */
352
+ readonly columns?: readonly string[] | undefined;
353
+ /** Page size; default 25. */
354
+ readonly limit?: number | undefined;
355
+ readonly renderers?: RendererOverrides | undefined;
356
+ }
357
+ /** A selectable table over one page of an already loaded resource, with the bulk toolbar above it. */
358
+ export declare function BulkPanel({ ready, filters, columns, limit, renderers, ...toolbar }: BulkPanelProps): ReactElement;
359
+ interface BulkRecordsProps extends Omit<BulkPanelProps, "ready"> {
360
+ readonly resource: string;
361
+ readonly apiVersion?: string | undefined;
362
+ }
363
+ /**
364
+ * Records with checkboxes and the bulk toolbar: selection count, bulk delete,
365
+ * export, bulk publish and filter-based select-all (no count preview). Bulk
366
+ * calls respect `bulk.max_matched`.
367
+ */
368
+ export declare function BulkRecords({ resource, apiVersion, ...rest }: BulkRecordsProps): ReactElement;
369
+ //#endregion
370
+ //#region src/bulk/errors.d.ts
371
+ /** More records than the server will touch in one call (`bulk.max_matched`). */
372
+ export declare class BulkLimitError extends Error {
373
+ constructor(max: number);
374
+ }
375
+ /**
376
+ * A message for a failed bulk call. The server answers `400` when a filter
377
+ * matches more than `bulk.max_matched` records (or the request is bad for
378
+ * another reason, which its own message then says).
379
+ */
380
+ export declare function describeBulkError(error: Error, max: number): string;
381
+ //#endregion
382
+ //#region src/bulk/export.d.ts
383
+ /** RFC 4180 CSV: a header of field names, then one line per record. Objects and arrays become JSON. */
384
+ export declare function toCsv(fields: readonly FieldDescriptor[], rows: readonly JsonObject[]): string;
385
+ /** Offers `text` to the browser as a file download. */
386
+ export declare function downloadText(filename: string, text: string, type?: string): void;
387
+ //#endregion
388
+ //#region src/delete/DeleteAction.d.ts
389
+ /**
390
+ * - `immediate`: delete on click.
391
+ * - `confirm`: ask first, showing the record.
392
+ * - `undo`: hold the deletion back for `undoMs`; Undo cancels it.
393
+ * - `soft`: delete (the server archives the record) and offer Undo, which
394
+ * restores it; renders lifecycle's `ArchiveButton`, needs the `archive_restore`
395
+ * capability, else renders nothing.
396
+ */
397
+ type DeleteVariant = "immediate" | "confirm" | "undo" | "soft";
398
+ interface DeleteActionProps {
399
+ readonly name: string;
400
+ readonly apiVersion?: string | undefined;
401
+ readonly id: number;
402
+ /** Default `"confirm"`. */
403
+ readonly variant?: DeleteVariant;
404
+ /** Button text. Default "Delete". */
405
+ readonly label?: string | undefined;
406
+ /** What one record is called in messages. Default "record". */
407
+ readonly recordLabel?: string | undefined;
408
+ /** Called once the record is deleted on the server. */
409
+ readonly onDeleted?: ((id: number) => void) | undefined;
410
+ /** Called when Undo cancelled a pending deletion or restored the record. */
411
+ readonly onUndone?: ((id: number) => void) | undefined;
412
+ /** `undo`: how long the deletion waits, in milliseconds. Default 6000. */
413
+ readonly undoMs?: number | undefined;
414
+ /** `confirm`: the fields shown. Default the first three besides `id`. */
415
+ readonly summaryFields?: readonly string[] | undefined;
416
+ readonly renderers?: RendererOverrides | undefined;
417
+ }
418
+ /** A delete button for one record; see `DeleteActionProps`. */
419
+ export declare function DeleteAction(props: DeleteActionProps): ReactElement;
420
+ //#endregion
421
+ //#region src/detail/RecordDetail.d.ts
422
+ type DetailVariant = "page" | "form" | "metadata" | "tabs" | "dialog" | "drawer";
423
+ /** What `renderForm` receives: enough to mount VW2's edit form for this record. */
424
+ interface DetailFormContext {
425
+ readonly ready: ReadyResource;
426
+ readonly id: number;
427
+ readonly record: JsonObject;
428
+ /** Call after a save so the detail refetches. */
429
+ readonly onDone: () => void;
430
+ }
431
+ interface DetailPanelProps {
432
+ readonly ready: ReadyResource;
433
+ readonly id: number;
434
+ readonly variant?: DetailVariant | undefined;
435
+ readonly renderers?: RendererOverrides | undefined;
436
+ /** Replaces the page title; default `<Resource title> #<id>`. */
437
+ readonly title?: ReactNode;
438
+ /**
439
+ * The `form` variant renders this in place of the field list, so the edit
440
+ * form (VW2's `RecordForm`, or your own) lives in the detail page without
441
+ * this package importing it.
442
+ */
443
+ readonly renderForm?: ((context: DetailFormContext) => ReactNode) | undefined;
444
+ /** `dialog` and `drawer`: whether it is shown (default true). */
445
+ readonly open?: boolean | undefined;
446
+ /** `dialog` and `drawer`: called when the overlay asks to close. */
447
+ readonly onClose?: (() => void) | undefined;
448
+ /** Called after a publish, discard, archive or restore. */
449
+ readonly onChanged?: (() => void) | undefined;
450
+ }
451
+ interface BodyProps {
452
+ readonly ready: ReadyResource;
453
+ readonly record: JsonObject;
454
+ readonly renderers: RendererOverrides | undefined;
455
+ }
456
+ /** The server-maintained fields: ids, owner, lifecycle and audit timestamps. */
457
+ export declare function DetailMetadata({ ready, record, renderers }: BodyProps): ReactElement;
458
+ /** One record of an already loaded resource, in the chosen `variant` (default `page`). */
459
+ export declare function DetailPanel({ variant, open, ...rest }: DetailPanelProps): ReactElement;
460
+ interface RecordDetailProps extends Omit<DetailPanelProps, "ready"> {
461
+ readonly resource: string;
462
+ readonly apiVersion?: string | undefined;
463
+ }
464
+ /**
465
+ * The detail of one record: `page` (read-only fields), `form` (an edit form
466
+ * from `renderForm`), `metadata` (fields beside a metadata panel), `tabs`,
467
+ * `dialog` or `drawer`. Status, publish, discard, archive and restore actions
468
+ * show only when the manifest's `capabilities` allow them.
469
+ */
470
+ export declare function RecordDetail({ resource, apiVersion, ...rest }: RecordDetailProps): ReactElement;
471
+ //#endregion
472
+ //#region src/detail/FieldList.d.ts
473
+ interface FieldListProps {
474
+ readonly resource: string;
475
+ readonly fields: readonly FieldDescriptor[];
476
+ readonly record: JsonObject;
477
+ readonly renderers?: RendererOverrides | undefined;
478
+ }
479
+ /** A label and value for each field, as a description list. */
480
+ export declare function FieldList({ resource, fields, record, renderers }: FieldListProps): ReactElement;
481
+ //#endregion
482
+ //#region src/filter/FacetSidebar.d.ts
483
+ interface FacetSidebarProps {
484
+ readonly ready: ReadyResource;
485
+ readonly filters: FieldFilters;
486
+ readonly onChange: (filters: FieldFilters) => void;
487
+ /** Field names to show. Default: every categorical field with known values. */
488
+ readonly fields?: readonly string[] | undefined;
489
+ }
490
+ /** Checkboxes for the categorical fields: pick values, the list narrows to them. */
491
+ export declare function FacetSidebar({ ready, filters, onChange, fields }: FacetSidebarProps): ReactElement | null;
492
+ //#endregion
493
+ //#region src/filter/FilterBar.d.ts
494
+ interface FilterBarProps {
495
+ readonly ready: ReadyResource;
496
+ readonly filters: FieldFilters;
497
+ readonly onChange: (filters: FieldFilters) => void;
498
+ /** Field names to show, in order. Default: every filterable field except the id. */
499
+ readonly fields?: readonly string[] | undefined;
500
+ readonly renderers?: RendererOverrides | undefined;
501
+ }
502
+ /** One filter control per field, side by side: the filters are all visible at once. */
503
+ export declare function FilterBar({ ready, filters, onChange, fields, renderers }: FilterBarProps): ReactElement;
504
+ //#endregion
505
+ //#region src/filter/FilterBuilder.d.ts
506
+ interface FilterBuilderProps {
507
+ readonly ready: ReadyResource;
508
+ readonly filters: FieldFilters;
509
+ readonly onChange: (filters: FieldFilters) => void;
510
+ readonly renderers?: RendererOverrides | undefined;
511
+ }
512
+ /**
513
+ * A list of conditions, each "field, operator, operand". Operands use the
514
+ * field's filter renderer where it edits that operator, a text box otherwise.
515
+ * Several conditions on one field combine as the API does: all must hold.
516
+ */
517
+ export declare function FilterBuilder({ ready, filters, onChange, renderers }: FilterBuilderProps): ReactElement;
518
+ //#endregion
519
+ //#region src/filter/FilterControl.d.ts
520
+ interface FilterControlProps {
521
+ readonly field: FieldDescriptor;
522
+ readonly resource: string;
523
+ readonly value: FieldFilter | undefined;
524
+ readonly onChange: (value: FieldFilter | undefined) => void;
525
+ readonly renderers?: RendererOverrides | undefined;
526
+ }
527
+ /** The filter renderer of one field. A datetime filter says its operands are UTC. */
528
+ export declare function FilterControl({ field, resource, value, onChange, renderers }: FilterControlProps): ReactElement;
529
+ //#endregion
530
+ //#region src/filter/QuickSearch.d.ts
531
+ interface QuickSearchProps {
532
+ /** The string fields to search (`icontains`); see `QueryStateApi.searchFields`. */
533
+ readonly fields: readonly FieldDescriptor[];
534
+ readonly value: string;
535
+ /** The searched field; with several, a selector appears (the API cannot OR fields). */
536
+ readonly field: string | undefined;
537
+ readonly onChange: (value: string) => void;
538
+ readonly onFieldChange: (field: string) => void;
539
+ /** Wait this long after the last keystroke before calling `onChange`. Default 300. */
540
+ readonly debounceMs?: number | undefined;
541
+ }
542
+ /** A search box: case-insensitive "contains" on one string field. Renders nothing without one. */
543
+ export declare function QuickSearch({ fields, value, field, onChange, onFieldChange, debounceMs }: QuickSearchProps): ReactElement | null;
544
+ //#endregion
545
+ //#region src/filter/queryState.d.ts
546
+ /** One sort key: a sortable field and its direction. */
547
+ interface SortKey {
548
+ readonly field: string;
549
+ readonly desc: boolean;
550
+ }
551
+ /** Everything a list view reads: filters, quick search, sort, paging and the visibility toggles. */
552
+ interface QueryState {
553
+ readonly filters: FieldFilters;
554
+ /** Quick-search text (`icontains` on `qField`); empty means off. */
555
+ readonly q: string;
556
+ readonly qField: string | undefined;
557
+ readonly sort: readonly SortKey[];
558
+ /** 1-based. */
559
+ readonly page: number;
560
+ readonly pageSize: number;
561
+ readonly includeArchived: boolean;
562
+ readonly includeUnpublished: boolean;
563
+ }
564
+ /** Values the state falls back to while the URL says nothing. */
565
+ interface QueryDefaults {
566
+ /** Default filter values; the user clears them and the URL remembers that. */
567
+ readonly filters?: FieldFilters | undefined;
568
+ readonly sort?: readonly SortKey[] | undefined;
569
+ readonly pageSize?: number | undefined;
570
+ }
571
+ /** What the codecs need to know about the resource. */
572
+ interface QueryContext {
573
+ readonly fields: readonly FieldDescriptor[];
574
+ readonly defaults: QueryDefaults;
575
+ /** Prefix of every URL parameter, so several lists can share one page. */
576
+ readonly prefix: string;
577
+ /** Default quick-search field, from the `searchField` option or the first searchable one. */
578
+ readonly searchField: string | undefined;
579
+ /** The largest page size the server accepts, less one (the view asks for one extra row to learn whether a next page exists). */
580
+ readonly maxPageSize: number;
581
+ }
582
+ export declare const DEFAULT_PAGE_SIZE = 25;
583
+ /** Fields the quick search can target: strings with `icontains`. */
584
+ export declare const searchableFields: (fields: readonly FieldDescriptor[]) => FieldDescriptor[];
585
+ /** The sort a header click or dropdown can use. */
586
+ export declare const sortableFields: (fields: readonly FieldDescriptor[]) => FieldDescriptor[];
587
+ /** The state with nothing in the URL. */
588
+ export declare function initialState(ctx: QueryContext): QueryState;
589
+ /** Reads typed text as the operand of `op` on `field` (`in` takes a comma-separated list); `undefined` when it does not parse. */
590
+ export declare function parseOperand(field: FieldDescriptor, op: FilterOp, text: string): FilterScalar | readonly FilterScalar[] | undefined;
591
+ /** The text form of an operand: the inverse of `parseOperand`. */
592
+ export declare function formatOperand(operand: FilterScalar | readonly FilterScalar[]): string;
593
+ /**
594
+ * The query string for `state`, merged into `base` (other parameters of the
595
+ * page survive). Values equal to the defaults are left out; a filter set that
596
+ * differs from the defaults is written with an `f` marker, so clearing a
597
+ * default filter sticks.
598
+ */
599
+ export declare function encodeState(state: QueryState, ctx: QueryContext, base?: string): string;
600
+ /** The state a query string describes; anything invalid for the resource is ignored. */
601
+ export declare function decodeState(search: string, ctx: QueryContext): QueryState;
602
+ /** The filters including the quick search, as per-field state. */
603
+ export declare function effectiveFilters(state: QueryState): FieldFilters;
604
+ /**
605
+ * The SDK query for `state`. `paged` adds `skip` and `limit` (one extra row,
606
+ * to learn whether a next page exists); an infinite list pages itself.
607
+ */
608
+ export declare function toCrudQuery(state: QueryState, paged: boolean): CrudQuery;
609
+ /** Whether any filter or quick search narrows the list. */
610
+ export declare const isFiltered: (state: QueryState) => boolean;
611
+ //#endregion
612
+ //#region src/filter/urlAdapter.d.ts
613
+ /** Reads and writes the query string (no leading `?`) the list state is kept in. */
614
+ interface UrlAdapter {
615
+ /** The current query string. */
616
+ read(): string;
617
+ /** Replaces the query string; `push` adds a history entry, `replace` does not. */
618
+ write(search: string, mode: "push" | "replace"): void;
619
+ /** Calls `listener` whenever the query string changes, from anywhere; returns the unsubscribe. */
620
+ subscribe(listener: () => void): () => void;
621
+ }
622
+ /** An in-memory adapter: tests, stories, and embedded lists that must not touch the address bar. */
623
+ export declare function memoryUrlAdapter(initial?: string): UrlAdapter;
624
+ /** The default adapter: `window.location` and `history` (back and forward work). */
625
+ export declare function windowUrlAdapter(): UrlAdapter;
626
+ /** The process-wide window adapter, so every list on a page shares one listener set. */
627
+ export declare function defaultUrlAdapter(): UrlAdapter;
628
+ //#endregion
629
+ //#region src/filter/useQueryState.d.ts
630
+ interface QueryStateOptions {
631
+ /** Where the state lives. Default: the window's query string. */
632
+ readonly adapter?: UrlAdapter | undefined;
633
+ /** Prefix of this list's URL parameters; give each list on a page its own. */
634
+ readonly prefix?: string | undefined;
635
+ readonly defaults?: QueryDefaults | undefined;
636
+ /** The field the quick search targets first. Default: the first string field with `icontains`. */
637
+ readonly searchField?: string | undefined;
638
+ }
639
+ /** The URL-synced state of one list plus the setters and the SDK queries derived from it. */
640
+ interface QueryStateApi {
641
+ readonly state: QueryState;
642
+ readonly fields: readonly FieldDescriptor[];
643
+ /** Fields the quick search can target. */
644
+ readonly searchFields: readonly FieldDescriptor[];
645
+ /** The query of a paged view: `skip` and `limit` (page size + 1, to detect a next page). */
646
+ readonly pagedQuery: CrudQuery;
647
+ /** The query of an infinite view: filters and sort only. */
648
+ readonly feedQuery: CrudQuery;
649
+ /** Any change but the page itself returns to page 1. */
650
+ setFilters(filters: FieldFilters): void;
651
+ setQuickSearch(q: string): void;
652
+ setQuickSearchField(field: string): void;
653
+ setSort(sort: readonly SortKey[]): void;
654
+ setPage(page: number): void;
655
+ setPageSize(pageSize: number): void;
656
+ setIncludeArchived(on: boolean): void;
657
+ setIncludeUnpublished(on: boolean): void;
658
+ /** Back to the defaults. */
659
+ reset(): void;
660
+ }
661
+ /**
662
+ * Filter, sort and paging state for `ready`, kept in the URL through the
663
+ * adapter. Operands are validated against the manifest, so a stale or
664
+ * hand-edited link degrades to the defaults instead of failing the request.
665
+ */
666
+ export declare function useQueryState(ready: ReadyResource, options?: QueryStateOptions): QueryStateApi;
667
+ //#endregion
668
+ //#region src/form/sections.d.ts
669
+ /** A titled group of fields, by field name. */
670
+ interface FormSection {
671
+ readonly title: string;
672
+ readonly fields: readonly string[];
673
+ }
674
+ type FormLayoutKind = "flat" | "sections" | "tabs" | "stepper";
675
+ /**
676
+ * The sections of a form. Given `sections`, fields they do not name go to a
677
+ * trailing "Other" section; without them, required fields come first under
678
+ * "Required", the rest under "Optional". Empty sections are dropped.
679
+ */
680
+ export declare function groupFields(fields: readonly FieldDescriptor[], sections: readonly FormSection[] | undefined): readonly FormSection[];
681
+ //#endregion
682
+ //#region src/form/ResourceForm.d.ts
683
+ type Row$2 = Readonly<Record<string, FieldValue>>;
684
+ interface ResourceFormProps {
685
+ /** The manifest resource name. */
686
+ readonly name: string;
687
+ readonly apiVersion?: string | undefined;
688
+ /** Edit this record; without it the form creates one. */
689
+ readonly id?: number | undefined;
690
+ /** Where the form renders. Default `"page"`. */
691
+ readonly presentation?: "page" | "dialog" | "drawer";
692
+ /** Dialog and drawer: whether it shows (a closed one renders nothing). */
693
+ readonly open?: boolean | undefined;
694
+ readonly onClose?: (() => void) | undefined;
695
+ /** How fields are arranged. Default `"flat"`. */
696
+ readonly layout?: FormLayoutKind;
697
+ /** Titled groups of field names for `sections`, `tabs` and `stepper`; default Required and Optional. */
698
+ readonly sections?: readonly FormSection[] | undefined;
699
+ /** Only these fields (in the resource's order). Default every writable field. */
700
+ readonly fields?: readonly string[] | undefined;
701
+ /** Create: starting values. */
702
+ readonly initial?: Row$2 | undefined;
703
+ /** Edit: save by itself after a pause in typing, while the form is valid. */
704
+ readonly autoSave?: boolean | undefined;
705
+ /** Pause before an auto-save, in milliseconds. Default 1000. */
706
+ readonly autoSaveDelayMs?: number | undefined;
707
+ /**
708
+ * The record's current ETag, sent as `If-Match` on every save (edit only,
709
+ * when the resource has `conditional_requests`). Called before each save,
710
+ * because a save changes the ETag. Without it no header is sent by the form
711
+ * (the SDK cache's `autoIfMatch` can still add one).
712
+ */
713
+ readonly etag?: ((id: number) => string | undefined | Promise<string | undefined>) | undefined;
714
+ readonly onSaved?: ((record: JsonObject) => void) | undefined;
715
+ /** Heading override. Default "New <recordLabel>" or "Edit <recordLabel> <id>". */
716
+ readonly title?: ReactNode;
717
+ /** What one record is called in titles. Default "record". */
718
+ readonly recordLabel?: string | undefined;
719
+ readonly renderers?: RendererOverrides | undefined;
720
+ }
721
+ /** A create or edit form for a resource; see `ResourceFormProps`. */
722
+ export declare function ResourceForm(props: ResourceFormProps): ReactElement | null;
723
+ //#endregion
724
+ //#region src/form/problem.d.ts
725
+ /** What a failed save shows. */
726
+ interface SaveFailure {
727
+ /** 412: the record changed since it was loaded. */
728
+ readonly conflict: boolean;
729
+ /** Messages by field name, from 422 `errors`. */
730
+ readonly fieldErrors: Readonly<Record<string, string>>;
731
+ /** Text for an alert: everything not tied to a visible field. */
732
+ readonly message: string | null;
733
+ }
734
+ /** Splits a mutation error into field messages, a conflict flag and an alert message. */
735
+ export declare function describeFailure(error: unknown, fieldNames: readonly string[]): SaveFailure;
736
+ //#endregion
737
+ //#region src/form/validate.d.ts
738
+ /**
739
+ * The message for the first schema constraint `value` breaks, or `null`.
740
+ * A missing value breaks only `required`; booleans are never missing.
741
+ */
742
+ export declare function validateValue(field: FieldDescriptor, value: FieldValue): string | null;
743
+ /** Errors by field name for every field whose value breaks a constraint. */
744
+ export declare function validateAll(fields: readonly FieldDescriptor[], values: Readonly<Record<string, FieldValue>>): Record<string, string>;
745
+ //#endregion
746
+ //#region src/lifecycle/DraftControls.d.ts
747
+ interface StatusBadgeProps {
748
+ readonly ready: ReadyResource;
749
+ readonly record: JsonObject;
750
+ }
751
+ /** A chip with the record's state. Hidden unless the resource has drafts or an archive. */
752
+ export declare function StatusBadge({ ready, record }: StatusBadgeProps): ReactElement | null;
753
+ interface RecordActionProps {
754
+ readonly ready: ReadyResource;
755
+ readonly id: number;
756
+ /** Called after the server accepted the change. */
757
+ readonly onDone?: (() => void) | undefined;
758
+ }
759
+ /** Publishes a draft. Hidden unless `capabilities.draft_publish`. */
760
+ export declare function PublishButton({ ready, id, onDone }: RecordActionProps): ReactElement | null;
761
+ /** Discards a draft after a confirmation (a draft has no published state to fall back to, so it is deleted). */
762
+ export declare function DiscardDraftButton({ ready, id, onDone }: RecordActionProps): ReactElement | null;
763
+ interface DraftControlsProps {
764
+ readonly ready: ReadyResource;
765
+ readonly record: JsonObject;
766
+ readonly id: number;
767
+ /** Called after a publish or a discard. */
768
+ readonly onChanged?: (() => void) | undefined;
769
+ }
770
+ /** Status badge plus, for a draft, Publish and Discard draft. Hidden without `draft_publish`. */
771
+ export declare function DraftControls({ ready, record, id, onChanged }: DraftControlsProps): ReactElement | null;
772
+ //#endregion
773
+ //#region src/lifecycle/ArchiveControls.d.ts
774
+ interface ArchivedToggleProps {
775
+ readonly ready: ReadyResource;
776
+ /** Maps to the list query's `includeArchived`. */
777
+ readonly checked: boolean;
778
+ readonly onChange: (checked: boolean) => void;
779
+ }
780
+ /** "Show archived" switch. Hidden unless `capabilities.archive_restore`. */
781
+ export declare function ArchivedToggle({ ready, checked, onChange }: ArchivedToggleProps): ReactElement | null;
782
+ /**
783
+ * Archives a record (the server's delete is a soft delete when it has an
784
+ * archive) and offers Undo, which restores it. Hidden without `archive_restore`.
785
+ */
786
+ export declare function ArchiveButton({ ready, id, onDone, onArchived, onUndone, label, ariaLabel, archived }: RecordActionProps & {
787
+ /** Button text. Default "Archive". */
788
+ readonly label?: string | undefined;
789
+ /** Accessible name when the text alone is ambiguous, e.g. "Delete hero 1". */
790
+ readonly ariaLabel?: string | undefined;
791
+ /** Called once the record is archived (`onDone` also runs, as after an undo). */
792
+ readonly onArchived?: (() => void) | undefined;
793
+ /** Called once Undo restored the record. */
794
+ readonly onUndone?: (() => void) | undefined;
795
+ /** The record is archived already: the button hides, an Undo toast can still show. */
796
+ readonly archived?: boolean;
797
+ }): ReactElement | null;
798
+ /** Restores an archived record. Hidden without `archive_restore`. */
799
+ export declare function RestoreButton({ ready, id, onDone }: RecordActionProps): ReactElement | null;
800
+ //#endregion
801
+ //#region src/lifecycle/ConfirmDialog.d.ts
802
+ interface ConfirmDialogProps {
803
+ readonly open: boolean;
804
+ readonly title: string;
805
+ readonly children: ReactNode;
806
+ readonly confirmLabel: string;
807
+ readonly busy?: boolean;
808
+ readonly onConfirm: () => void;
809
+ readonly onClose: () => void;
810
+ }
811
+ /** A small confirmation built on `DialogShell`: Cancel and one destructive action. */
812
+ export declare function ConfirmDialog({ open, title, children, confirmLabel, busy, onConfirm, onClose }: ConfirmDialogProps): ReactElement;
813
+ //#endregion
814
+ //#region src/lifecycle/mutations.d.ts
815
+ /**
816
+ * Refreshes every cached query of one resource (all API versions), for the
817
+ * calls the core's hooks do not cover: publish, restore and bulk.
818
+ */
819
+ export declare function useInvalidateResource(name: string): () => Promise<void>;
820
+ /** `POST publish?id=`: turns a draft into a published record. */
821
+ export declare function usePublish(ready: ReadyResource): UseMutationResult<JsonObject, Error, number>;
822
+ /** `POST restore?id=`: brings an archived record back. */
823
+ export declare function useRestore(ready: ReadyResource): UseMutationResult<BulkResult, Error, number>;
824
+ //#endregion
825
+ //#region src/lifecycle/status.d.ts
826
+ type RecordStatus = "published" | "draft" | "archived";
827
+ /** Fields the server maintains (lifecycle and audit); the detail view shows them in its metadata panel. */
828
+ export declare const SYSTEM_FIELDS: readonly string[];
829
+ /** `archived` wins over `draft`; a record with neither marker is `published`. */
830
+ export declare function recordStatus(record: JsonObject): RecordStatus;
831
+ type StatusFilterValue = "published" | "draft" | "all";
832
+ /** What a list query needs for a status choice: spread into `useList`'s query. */
833
+ interface StatusQuery {
834
+ readonly includeUnpublished: boolean;
835
+ readonly filters: Readonly<Record<string, unknown>>;
836
+ }
837
+ /**
838
+ * The list query for a status choice. The server hides drafts unless
839
+ * `include_unpublished` is set; `draft` also filters on `is_draft`.
840
+ */
841
+ export declare function statusQuery(value: StatusFilterValue): StatusQuery;
842
+ //#endregion
843
+ //#region src/lifecycle/StatusFilter.d.ts
844
+ interface StatusFilterProps {
845
+ readonly ready: ReadyResource;
846
+ readonly value: StatusFilterValue;
847
+ readonly onChange: (value: StatusFilterValue) => void;
848
+ }
849
+ /** Published / Draft / All. Hidden unless `capabilities.draft_publish`. */
850
+ export declare function StatusFilter({ ready, value, onChange }: StatusFilterProps): ReactElement | null;
851
+ //#endregion
852
+ //#region src/lifecycle/TrashList.d.ts
853
+ interface TrashPanelProps {
854
+ readonly ready: ReadyResource;
855
+ readonly renderers?: RendererOverrides | undefined;
856
+ /** Columns to show; default: the first three non-system fields. */
857
+ readonly columns?: readonly string[] | undefined;
858
+ /** Called after a record was restored. */
859
+ readonly onRestored?: ((id: number) => void) | undefined;
860
+ }
861
+ /** The archived records of an already loaded resource, each with Restore. Hidden without `archive_restore`. */
862
+ export declare function TrashPanel(props: TrashPanelProps): ReactElement | null;
863
+ interface TrashListProps extends Omit<TrashPanelProps, "ready"> {
864
+ readonly resource: string;
865
+ readonly apiVersion?: string | undefined;
866
+ }
867
+ /** The trash: archived records with Restore. Renders nothing for a resource without an archive. */
868
+ export declare function TrashList({ resource, apiVersion, ...rest }: TrashListProps): ReactElement;
869
+ //#endregion
870
+ //#region src/list/columns.d.ts
871
+ /** A column: a field name, or a name with a header and a size. */
872
+ type ColumnSpec = string | {
873
+ readonly field: string;
874
+ /** Header text. Default: the field's label. */
875
+ readonly header?: string | undefined;
876
+ /** Width in pixels (a number) for the table. */
877
+ readonly width?: number | undefined;
878
+ };
879
+ /** A resolved column. */
880
+ interface Column {
881
+ readonly field: FieldDescriptor;
882
+ readonly header: string;
883
+ readonly width: number | undefined;
884
+ }
885
+ /**
886
+ * The columns to show. Default: every described field except JSON ones.
887
+ * Names the resource does not have are skipped.
888
+ */
889
+ export declare function resolveColumns(ready: ReadyResource, columns?: readonly ColumnSpec[]): Column[];
890
+ //#endregion
891
+ //#region src/list/RecordCard.d.ts
892
+ type Row = Readonly<Record<string, FieldValue>>;
893
+ interface RecordCardProps {
894
+ readonly ready: ReadyResource;
895
+ readonly row: Row;
896
+ readonly columns?: readonly ColumnSpec[] | undefined;
897
+ /** The field shown as the card's heading. Default: the first column. */
898
+ readonly titleField?: string | undefined;
899
+ readonly renderers?: RendererOverrides | undefined;
900
+ readonly onClick?: ((row: Row) => void) | undefined;
901
+ }
902
+ /** One record as a card: a heading and a label/value pair per other column. */
903
+ export declare function RecordCard({ ready, row, columns, titleField, renderers, onClick }: RecordCardProps): ReactElement;
904
+ //#endregion
905
+ //#region src/list/CardGridList.d.ts
906
+ interface CardGridListProps {
907
+ readonly ready: ReadyResource;
908
+ readonly state: QueryStateApi;
909
+ readonly columns?: readonly ColumnSpec[] | undefined;
910
+ readonly titleField?: string | undefined;
911
+ readonly onRowClick?: ((row: Row) => void) | undefined;
912
+ readonly renderers?: RendererOverrides | undefined;
913
+ }
914
+ /** A page of cards in a responsive grid, with Previous and Next (the server reports no total). */
915
+ export declare function CardGridList({ ready, state, columns, titleField, onRowClick, renderers }: CardGridListProps): ReactElement;
916
+ //#endregion
917
+ //#region src/list/CardList.d.ts
918
+ interface CardListProps {
919
+ readonly ready: ReadyResource;
920
+ readonly rows: readonly Row[];
921
+ readonly columns?: readonly ColumnSpec[] | undefined;
922
+ readonly titleField?: string | undefined;
923
+ /** `list`: one card per row; `grid`: as many as fit. */
924
+ readonly layout: "list" | "grid";
925
+ readonly onRowClick?: ((row: Row) => void) | undefined;
926
+ readonly renderers?: RendererOverrides | undefined;
927
+ }
928
+ /** The cards of the card views (and their empty state). */
929
+ export declare function CardList({ ready, rows, columns, titleField, layout, onRowClick, renderers }: CardListProps): ReactElement;
930
+ //#endregion
931
+ //#region src/list/ColumnsMenu.d.ts
932
+ interface ColumnsMenuProps {
933
+ readonly columns: readonly Column[];
934
+ /** Field names currently hidden. */
935
+ readonly hidden: ReadonlySet<string>;
936
+ readonly onChange: (hidden: ReadonlySet<string>) => void;
937
+ }
938
+ /** A button that opens a checklist of the columns, to show or hide each. */
939
+ export declare function ColumnsMenu({ columns, hidden, onChange }: ColumnsMenuProps): ReactElement;
940
+ //#endregion
941
+ //#region src/list/DataTableList.d.ts
942
+ type Row$1 = Readonly<Record<string, FieldValue>>;
943
+ /** DataGrid props a caller may pass through (not the ones this view controls). */
944
+ type GridOptions = Partial<Omit<DataGridProps, "onRowClick" | "rows" | "columns">>;
945
+ interface DataTableListProps {
946
+ readonly ready: ReadyResource;
947
+ /** Filter, sort and paging state; see `useQueryState`. */
948
+ readonly state: QueryStateApi;
949
+ /** Columns in order. Default: every field but JSON ones. */
950
+ readonly columns?: readonly ColumnSpec[] | undefined;
951
+ /** Show only columns the server can sort by. */
952
+ readonly sortableOnly?: boolean | undefined;
953
+ /** Let the user hide columns (a "Columns" button). Default true. */
954
+ readonly configurableColumns?: boolean | undefined;
955
+ /** Edit cells in place (double-click or Enter; Enter saves, Escape cancels). Read-only fields stay read-only. */
956
+ readonly editable?: boolean | undefined;
957
+ /**
958
+ * The ETag of a row, sent as `If-Match` on an inline edit (a stale one fails
959
+ * with 412 and the view reloads the row). The list endpoint returns no ETags,
960
+ * so the app supplies them (for example from its SDK cache); without this
961
+ * prop the edit is unconditional. May return a promise (same shape as
962
+ * `ResourceFormProps.etag`): the edit waits for it, stays in a saving state
963
+ * meanwhile, and is not sent if it rejects (the error is shown like any save
964
+ * error). `undefined` sends the PATCH without `If-Match`.
965
+ */
966
+ readonly ifMatch?: ((row: Row$1) => string | undefined | Promise<string | undefined>) | undefined;
967
+ readonly pageSizeOptions?: readonly number[] | undefined;
968
+ readonly onRowClick?: ((row: Row$1) => void) | undefined;
969
+ readonly renderers?: RendererOverrides | undefined;
970
+ /** Passed to the DataGrid last (a way to reach options this view does not expose). */
971
+ readonly gridProps?: GridOptions | undefined;
972
+ }
973
+ /** A paged data table (MUI X DataGrid): server-side sort and paging, optional inline editing. */
974
+ export declare function DataTableList({ ready, state, columns, sortableOnly, configurableColumns, editable, ifMatch, pageSizeOptions, onRowClick, renderers, gridProps }: DataTableListProps): ReactElement;
975
+ //#endregion
976
+ //#region src/list/LoadMoreList.d.ts
977
+ interface LoadMoreListProps {
978
+ readonly ready: ReadyResource;
979
+ readonly state: QueryStateApi;
980
+ readonly columns?: readonly ColumnSpec[] | undefined;
981
+ readonly titleField?: string | undefined;
982
+ /** `list`: one card per row; `grid`: cards side by side. Default `list`. */
983
+ readonly layout?: "list" | "grid" | undefined;
984
+ readonly onRowClick?: ((row: Row) => void) | undefined;
985
+ readonly renderers?: RendererOverrides | undefined;
986
+ }
987
+ /** A growing list: the first page, then "Load more" appends the next (`useInfiniteList`). The page number is not used. */
988
+ export declare function LoadMoreList({ ready, state, columns, titleField, layout, onRowClick, renderers }: LoadMoreListProps): ReactElement;
989
+ //#endregion
990
+ //#region src/list/ResourceList.d.ts
991
+ /** `table`: paged data table; `loadMore`: growing list; `cards`: paged card grid. */
992
+ type ListVariant = "table" | "loadMore" | "cards";
993
+ /** `quick`: search box; `bar`: a control per field; `builder`: field/operator/operand rows; `facets`: checkboxes for categorical fields. */
994
+ type FilterVariant = "quick" | "bar" | "builder" | "facets";
995
+ interface ResourceListProps {
996
+ readonly resource: string;
997
+ readonly apiVersion?: string | undefined;
998
+ readonly variant?: ListVariant | undefined;
999
+ /** Which filter UIs to show, in addition to default filter values. Default `["quick"]`. */
1000
+ readonly filters?: readonly FilterVariant[] | undefined;
1001
+ /** Fields of the filter bar, in order. Default: all filterable fields. */
1002
+ readonly barFields?: readonly string[] | undefined;
1003
+ /** Fields of the facet sidebar. Default: all categorical fields. */
1004
+ readonly facetFields?: readonly string[] | undefined;
1005
+ readonly columns?: readonly ColumnSpec[] | undefined;
1006
+ readonly titleField?: string | undefined;
1007
+ /** Table only: edit cells in place. */
1008
+ readonly editable?: boolean | undefined;
1009
+ /** Table only: a row's ETag for inline edits; may be async. See `DataTableListProps.ifMatch`. */
1010
+ readonly ifMatch?: ((row: Row) => string | undefined | Promise<string | undefined>) | undefined;
1011
+ readonly sortableOnly?: boolean | undefined;
1012
+ /** Show the sort dropdown. Default: for the card variants, which have no header to click. */
1013
+ readonly sortDropdown?: boolean | undefined;
1014
+ /** Where the state lives. Default: the window's query string. */
1015
+ readonly urlAdapter?: UrlAdapter | undefined;
1016
+ /** Prefix of this list's URL parameters. */
1017
+ readonly prefix?: string | undefined;
1018
+ readonly defaults?: QueryDefaults | undefined;
1019
+ readonly searchField?: string | undefined;
1020
+ readonly renderers?: RendererOverrides | undefined;
1021
+ readonly onRowClick?: ((row: Row) => void) | undefined;
1022
+ readonly gridProps?: GridOptions | undefined;
1023
+ }
1024
+ /** A resource's list, ready to use: filters, sort and paging in the URL, one of three layouts. */
1025
+ export declare function ResourceList({ resource, apiVersion, ...props }: ResourceListProps): ReactElement;
1026
+ //#endregion
1027
+ //#region src/list/SortDropdown.d.ts
1028
+ interface SortDropdownProps {
1029
+ readonly ready: ReadyResource;
1030
+ readonly sort: readonly SortKey[];
1031
+ readonly onChange: (sort: readonly SortKey[]) => void;
1032
+ }
1033
+ /** Sort by one field, for layouts with no header row to click. Lists only fields the manifest marks sortable. */
1034
+ export declare function SortDropdown({ ready, sort, onChange }: SortDropdownProps): ReactElement | null;
1035
+ //#endregion
1036
+ //#region src/revisions/revision.d.ts
1037
+ /** One entry of a record's history (`GET revisions?id=`, newest first). */
1038
+ interface Revision {
1039
+ readonly id: number;
1040
+ readonly action: string;
1041
+ readonly actor: string;
1042
+ /** ISO-8601 UTC. */
1043
+ readonly createdAt: string;
1044
+ /** The record as it was after the action. */
1045
+ readonly snapshot: JsonObject;
1046
+ }
1047
+ /** Reads the server's revision rows. A row without a snapshot gets an empty one. */
1048
+ export declare function toRevisions(rows: readonly JsonObject[]): Revision[];
1049
+ /** Names of the `fields` whose value differs between two snapshots. */
1050
+ export declare function changedFields(fields: readonly FieldDescriptor[], before: JsonObject, after: JsonObject): string[];
1051
+ /**
1052
+ * The `PATCH` body that puts a record back to `snapshot`: the writable fields
1053
+ * the snapshot has. A `null` for a field that does not take `null` is left out,
1054
+ * as a cleared optional field is everywhere else.
1055
+ */
1056
+ export declare function restorePatch(fields: readonly FieldDescriptor[], snapshot: JsonObject): JsonObject;
1057
+ //#endregion
1058
+ //#region src/revisions/Revisions.d.ts
1059
+ type RevisionsVariant = "list" | "audit" | "diff" | "preview";
1060
+ interface RevisionsPanelProps {
1061
+ readonly ready: ReadyResource;
1062
+ readonly id: number;
1063
+ readonly variant?: RevisionsVariant | undefined;
1064
+ readonly renderers?: RendererOverrides | undefined;
1065
+ /** Called after a revision was restored (list variant). */
1066
+ readonly onRestored?: ((revision: Revision) => void) | undefined;
1067
+ }
1068
+ /** The history of one record of an already loaded resource. Hidden without `capabilities.revisions`. */
1069
+ export declare function RevisionsPanel(props: RevisionsPanelProps): ReactElement | null;
1070
+ interface RecordRevisionsProps extends Omit<RevisionsPanelProps, "ready"> {
1071
+ readonly resource: string;
1072
+ readonly apiVersion?: string | undefined;
1073
+ }
1074
+ /**
1075
+ * A record's revisions in one of four shapes: `list` (with Restore), `audit`
1076
+ * (table), `diff` (two revisions compared) or `preview` (one snapshot).
1077
+ */
1078
+ export declare function RecordRevisions({ resource, apiVersion, ...rest }: RecordRevisionsProps): ReactElement;
1079
+ //#endregion
1080
+ //#region src/stats/model.d.ts
1081
+ /** The time buckets the server aggregates by. */
1082
+ type Bucket = "day" | "week" | "month";
1083
+ //#endregion
1084
+ //#region src/stats/shared.d.ts
1085
+ /** Props every stats and prediction variant takes. */
1086
+ interface ViewProps {
1087
+ /** The manifest resource name (`"heroes"`). */
1088
+ readonly resource: string;
1089
+ readonly apiVersion?: string | undefined;
1090
+ /** Field-renderer overrides, passed to every `FieldDisplay` the view renders. */
1091
+ readonly renderers?: RendererOverrides | undefined;
1092
+ }
1093
+ //#endregion
1094
+ //#region src/stats/StatsKpiCards.d.ts
1095
+ /** Variant 1: one card for the record count and one per numeric field (average, with its range). Hidden without the `stats` capability. */
1096
+ export declare function StatsKpiCards({ resource, apiVersion, renderers }: ViewProps): ReactElement;
1097
+ //#endregion
1098
+ //#region src/stats/StatsTimeSeries.d.ts
1099
+ interface StatsTimeSeriesProps extends ViewProps {
1100
+ /** The initial bucket size. Default `"day"`. */
1101
+ readonly bucket?: Bucket | undefined;
1102
+ }
1103
+ /**
1104
+ * Variant 2: records created per UTC bucket as a line chart, with a
1105
+ * day/week/month toggle. Buckets with no records are absent from the server's
1106
+ * series and stay absent on the time axis. Hidden without the `stats`
1107
+ * capability.
1108
+ */
1109
+ export declare function StatsTimeSeries({ resource, apiVersion, bucket: initial }: StatsTimeSeriesProps): ReactElement;
1110
+ //#endregion
1111
+ //#region src/stats/StatsCategoryChart.d.ts
1112
+ type CategoryChartKind = "bar" | "pie";
1113
+ interface StatsCategoryChartProps extends ViewProps {
1114
+ /** The initial chart. Default `"bar"`. */
1115
+ readonly kind?: CategoryChartKind | undefined;
1116
+ /** The categorical field shown first. Default: the first the server reports. */
1117
+ readonly field?: string | undefined;
1118
+ }
1119
+ /**
1120
+ * Variant 3: the value distribution of a boolean or enum field as a bar or pie
1121
+ * chart, with a field picker and a chart-type toggle. Hidden without the
1122
+ * `stats` capability.
1123
+ */
1124
+ export declare function StatsCategoryChart({ resource, apiVersion, kind, field }: StatsCategoryChartProps): ReactElement;
1125
+ //#endregion
1126
+ //#region src/stats/StatsTablePanel.d.ts
1127
+ /**
1128
+ * Variant 4: two tables, the numeric aggregates of every numeric field and
1129
+ * the value counts of every categorical field. Values go through the field
1130
+ * renderers. Hidden without the `stats` capability.
1131
+ */
1132
+ export declare function StatsTablePanel({ resource, apiVersion, renderers }: ViewProps): ReactElement;
1133
+ //#endregion
1134
+ //#region src/stats/StatsDashboard.d.ts
1135
+ /** All four statistics variants on one page. Hidden without the `stats` capability. */
1136
+ export declare function StatsDashboard(props: ViewProps): ReactElement;
1137
+ //#endregion
1138
+ //#region src/stats/predictShared.d.ts
1139
+ /** What `/predict` is asked: a numeric field (default the record count), how many buckets ahead, and the bucket size. */
1140
+ interface PredictionQuery {
1141
+ /** A numeric field to forecast the per-bucket sum of; omit to forecast the record count. */
1142
+ readonly field?: string | undefined;
1143
+ /** Buckets to project, 1 to 52. Default 4. */
1144
+ readonly periods?: number | undefined;
1145
+ /** Default `"day"`. */
1146
+ readonly bucket?: Bucket | undefined;
1147
+ }
1148
+ type PredictionProps = ViewProps & PredictionQuery;
1149
+ //#endregion
1150
+ //#region src/stats/PredictionChart.d.ts
1151
+ /**
1152
+ * Predictions variant 1: a line chart of the known series and the forecast
1153
+ * after it. The history is the server's count series, so it is drawn only
1154
+ * when the forecast is of the record count (no `field`) and `stats` is
1155
+ * available; a field forecast shows its last known value and the projection.
1156
+ * Hidden without the `predict` capability.
1157
+ */
1158
+ export declare function PredictionChart(props: PredictionProps): ReactElement;
1159
+ //#endregion
1160
+ //#region src/stats/PredictionTable.d.ts
1161
+ /**
1162
+ * Predictions variant 2: the last known value and every forecast bucket as a
1163
+ * table, values shown by the field's renderer. Hidden without the `predict`
1164
+ * capability.
1165
+ */
1166
+ export declare function PredictionTable(props: PredictionProps): ReactElement;
1167
+ //#endregion
1168
+ //#region src/stats/PredictionKpi.d.ts
1169
+ /**
1170
+ * Predictions variant 3: a card with the first forecast bucket's value and how
1171
+ * it moves from the last known one. Hidden without the `predict` capability.
1172
+ */
1173
+ export declare function PredictionKpi(props: PredictionProps): ReactElement;
1174
+ //#endregion
1175
+ //#region src/stats/PredictionsDashboard.d.ts
1176
+ /** All three prediction variants with the controls that drive them (field, buckets ahead, bucket size). Hidden without the `predict` capability. */
1177
+ export declare function PredictionsDashboard(props: ViewProps): ReactElement;
1178
+ //#endregion
1179
+ export type { ArchivedToggleProps, Bucket, BulkAction, BulkDone, BulkPanelProps, BulkRecordsProps, BulkSelection, BulkToolbarProps, CardGridListProps, CardListProps, CategoryChartKind, Column, ColumnSpec, ColumnsMenuProps, ConfirmDialogProps, CrudContextValue, CrudProviderProps, DataTableListProps, DeleteActionProps, DeleteVariant, DetailFormContext, DetailPanelProps, DetailVariant, DialogShellProps, DisplayRenderer, DisplayRendererProps, DraftControlsProps, DrawerShellProps, FacetSidebarProps, FailedResource, FieldConstraints, FieldDescriptor, FieldFilter, FieldFilters, FieldKind, FieldListProps, FieldRenderers, FieldValue, FilterBarProps, FilterBuilderProps, FilterControlProps, FilterOp, FilterRenderer, FilterRendererProps, FilterScalar, FilterVariant, FormLayoutKind, FormSection, GridOptions, InputRenderer, InputRendererProps, ListVariant, LoadMoreListProps, OverlayShellProps, PageShellProps, PendingResource, PredictionProps, PredictionQuery, QueryContext, QueryDefaults, QueryState, QueryStateApi, QueryStateOptions, QuickSearchProps, ReadyResource, RecordActionProps, RecordCardProps, RecordDetailProps, RecordRevisionsProps, RecordStatus, RendererByRole, RendererOverrides, RendererRole, ResourceFormProps, ResourceGateProps, ResourceHooks, ResourceListProps, Revision, RevisionsPanelProps, RevisionsVariant, Row, SaveFailure, ShellProps, SortDropdownProps, SortKey, StatsCategoryChartProps, StatsTimeSeriesProps, ViewProps as StatsViewProps, StatusBadgeProps, StatusFilterProps, StatusFilterValue, StatusQuery, TrashListProps, TrashPanelProps, UrlAdapter, UseResourceResult };
1180
+ //# sourceMappingURL=index.d.mts.map