@cosmicdrift/kumiko-types 0.384.0 → 0.386.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.
- package/dist/db-connection.d.ts +2 -2
- package/dist/handlers.d.ts +3 -1
- package/dist/handlers.js +13 -1
- package/dist/screen-action-form.d.ts +302 -0
- package/dist/screen-action-form.js +1 -0
- package/dist/screen-custom-config.d.ts +122 -0
- package/dist/screen-custom-config.js +1 -0
- package/dist/screen-dashboard.d.ts +348 -0
- package/dist/screen-dashboard.js +10 -0
- package/dist/screen-entity-edit.d.ts +493 -0
- package/dist/screen-entity-edit.js +8 -0
- package/dist/screen-entity-list.d.ts +427 -0
- package/dist/screen-entity-list.js +1 -0
- package/dist/screen-projection.d.ts +217 -0
- package/dist/screen-projection.js +1 -0
- package/dist/screen.d.ts +15 -1877
- package/dist/screen.js +2 -18
- package/dist/where-clause-types.d.ts +1 -0
- package/package.json +1 -1
- package/src/changes.json +12 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import type { FieldCondition } from "./fields.ts";
|
|
2
|
+
import type { AccessRule, AgentScreenHints } from "./handlers.ts";
|
|
3
|
+
import type { IconKey } from "./nav-icon.ts";
|
|
4
|
+
import type { FieldRenderer, PlatformComponent, ScreenNavSugar, ScreenSlots } from "./screen.ts";
|
|
5
|
+
import type { ActionFormRedirect } from "./screen-action-form.ts";
|
|
6
|
+
import type { DashboardPanelVisibility } from "./screen-dashboard.ts";
|
|
7
|
+
import type { EditRelatedListSection, ListEmptyStateSpec } from "./screen-entity-edit.ts";
|
|
8
|
+
export type ListColumnSpec = string | {
|
|
9
|
+
readonly field: string;
|
|
10
|
+
readonly renderer?: FieldRenderer;
|
|
11
|
+
/** Optional header, overriding the default
|
|
12
|
+
* `<feature>:entity:<entity>:field:<field>` i18n convention. Resolved
|
|
13
|
+
* through `translate` like any header — an i18n key, or a plain literal
|
|
14
|
+
* shown verbatim if it isn't a key. Also the way to declare a *virtual*
|
|
15
|
+
* column whose `field` is NOT an entity field — a presentational column
|
|
16
|
+
* drawn entirely by a `columnRenderer` component that reads the `row`
|
|
17
|
+
* (e.g. a tag-chips cell), not a stored value. Such a column needs a
|
|
18
|
+
* label; without one it is rejected at boot as an unknown field. `field`
|
|
19
|
+
* is then just a stable column key (pick any unique slug). */
|
|
20
|
+
readonly label?: string;
|
|
21
|
+
/** Marks a column on a query-backed list (projectionList, relatedList,
|
|
22
|
+
* dashboard list, expandableRow) as drawn by a renderer rather than read
|
|
23
|
+
* from a row field. Without it, a query that declares an `outputSchema`
|
|
24
|
+
* must return `field`; a `label` alone does not exempt the column, since
|
|
25
|
+
* nearly every column carries one. */
|
|
26
|
+
readonly virtual?: boolean;
|
|
27
|
+
/** Marks this column as a reference lookup instead of a plain value —
|
|
28
|
+
* for projectionList/relatedList columns, which have no
|
|
29
|
+
* `EntityDefinition` to carry a real `reference` field type. Same
|
|
30
|
+
* target convention as `parseRefTarget`: an entity name (same
|
|
31
|
+
* feature) or `feature:entity` (cross-feature). Ignored on
|
|
32
|
+
* `entityList` columns, where the field's own declared type always
|
|
33
|
+
* wins. */
|
|
34
|
+
readonly refEntity?: string;
|
|
35
|
+
/** Row field on the referenced entity shown as the label (default
|
|
36
|
+
* "id"). Only meaningful together with `refEntity`. */
|
|
37
|
+
readonly refLabelField?: string;
|
|
38
|
+
/** Marks this column as sortable via a clickable header — for
|
|
39
|
+
* relatedList columns, which have no `EntityDefinition` field to carry
|
|
40
|
+
* `sortable` on. Ignored on `entityList` columns, where the entity
|
|
41
|
+
* field's own `sortable` flag wins. */
|
|
42
|
+
readonly sortable?: boolean;
|
|
43
|
+
/** Value type of a relatedList column, which has no `EntityDefinition`
|
|
44
|
+
* field to derive it from. Numeric types right-align the column and its
|
|
45
|
+
* header with tabular figures, like an entityList column of that field
|
|
46
|
+
* type. Ignored on entityList columns, where the field's own type wins. */
|
|
47
|
+
readonly valueType?: "number" | "decimal" | "bigInt" | "money";
|
|
48
|
+
/** Leaves the column out of the compact card layout used below the
|
|
49
|
+
* `md` breakpoint (tables keep it). For columns that only make sense
|
|
50
|
+
* next to the others, like a sort key. */
|
|
51
|
+
readonly hideOnNarrow?: boolean;
|
|
52
|
+
/** Dashboard list panels only. "bar": field holds a 0..1 fraction, drawn
|
|
53
|
+
* as bar + percent. "badge": field holds a label, drawn as a pill whose
|
|
54
|
+
* tone comes from `badgeToneField`. "datetime": field holds an ISO string or
|
|
55
|
+
* epoch ms, shown in the user's locale and time zone. Other list renderers
|
|
56
|
+
* ignore it. */
|
|
57
|
+
readonly display?: ListColumnDisplay;
|
|
58
|
+
/** Row field naming the badge tone; only with `display: "badge"`. */
|
|
59
|
+
readonly badgeToneField?: string;
|
|
60
|
+
};
|
|
61
|
+
export type ListColumnDisplay = "bar" | "badge" | "datetime";
|
|
62
|
+
export type ListBadgeTone = "positive" | "warn" | "negative" | "neutral";
|
|
63
|
+
export type ListPaginationMode = "pages" | "infinite" | false;
|
|
64
|
+
export type ListSortDir = "asc" | "desc";
|
|
65
|
+
export type ListSortSpec = {
|
|
66
|
+
readonly field: string;
|
|
67
|
+
readonly dir: ListSortDir;
|
|
68
|
+
};
|
|
69
|
+
export type ScreenFilterOp = "eq" | "ne" | "lt" | "gt" | "lte" | "gte" | "in";
|
|
70
|
+
export type ScreenFilter = {
|
|
71
|
+
readonly field: string;
|
|
72
|
+
readonly op: ScreenFilterOp;
|
|
73
|
+
readonly value: unknown;
|
|
74
|
+
};
|
|
75
|
+
/** Declarative row-field extractor — JSON-safe (no function prop, survives
|
|
76
|
+
* GET /api/schema / buildAppSchema).
|
|
77
|
+
*
|
|
78
|
+
* `pick`: extracts fields 1:1. `{ pick: ["id", "version"] }` → `{ id: row.id, version: row.version }`
|
|
79
|
+
* `map`: renames fields. `{ map: { incidentId: "id" } }` → `{ incidentId: row.id }`
|
|
80
|
+
*
|
|
81
|
+
* Limitation: computed/templated values can't be expressed this way —
|
|
82
|
+
* that logic belongs server-side in the write handler. */
|
|
83
|
+
export type RowFieldExtractor = {
|
|
84
|
+
readonly pick: readonly string[];
|
|
85
|
+
} | {
|
|
86
|
+
readonly map: Readonly<Record<string, string>>;
|
|
87
|
+
};
|
|
88
|
+
/** How a row action renders inline in the row-action column. `"button"` is a
|
|
89
|
+
* bordered button (its colour follows the action's `style`), `"link"` a text
|
|
90
|
+
* link, `"icon"` an icon-only button (needs a resolved icon, falls back to
|
|
91
|
+
* `"link"` without one). `"responsive"` is a `"button"` with icon and label
|
|
92
|
+
* from tablet width up and an icon-only button below it, the label staying the
|
|
93
|
+
* accessible name (needs a resolved icon, falls back to `"button"` without
|
|
94
|
+
* one). Unset: the renderer decides (primary action as link, the rest in the
|
|
95
|
+
* kebab menu). */
|
|
96
|
+
export type RowActionDisplay = "button" | "link" | "icon" | "responsive";
|
|
97
|
+
/** Row-action column layout. `"adaptive"` (default): primary action as link,
|
|
98
|
+
* the rest in a kebab menu — except actions with an explicit `display`, which
|
|
99
|
+
* stay inline. `"inline"`: every action inline, no kebab. */
|
|
100
|
+
export type RowActionMode = "adaptive" | "inline";
|
|
101
|
+
export type RowAction = RowActionWriteHandler | RowActionNavigate | RowActionDrawer | RowActionClientAction;
|
|
102
|
+
export type RowActionWriteHandler = {
|
|
103
|
+
/** Default for RowActions without an explicitly set `kind` —
|
|
104
|
+
* backwards-compatible form. Can also be set explicitly. */
|
|
105
|
+
readonly kind?: "writeHandler";
|
|
106
|
+
/** Stable id per screen — kebab-case, unique within the action set. */
|
|
107
|
+
readonly id: string;
|
|
108
|
+
/** Anzeige-Text (i18n-Key). */
|
|
109
|
+
readonly label: string;
|
|
110
|
+
/** Qualified name of the server handler, e.g.
|
|
111
|
+
* "publicstatus:write:maintenance:start". Dispatched via
|
|
112
|
+
* useDispatcher. */
|
|
113
|
+
readonly handler: string;
|
|
114
|
+
/** Declarative payload per row. Default = `{ id: row.id }`.
|
|
115
|
+
* `pick` extracts fields of the same name; `map` renames. */
|
|
116
|
+
readonly payload?: RowFieldExtractor;
|
|
117
|
+
/** i18n key for the confirm dialog description. When set, a modal opens
|
|
118
|
+
* before execution — the user must confirm explicitly. Together with
|
|
119
|
+
* `style: "danger"` this is the standard safety guard for destructive
|
|
120
|
+
* actions. */
|
|
121
|
+
readonly confirm?: string;
|
|
122
|
+
/** i18n key for the confirm button text in the dialog. Default = `label`
|
|
123
|
+
* (so "Delete" → "Delete" button in the confirm). Set it when the action
|
|
124
|
+
* has a long name ("Mark Subscription as Cancelled") and the button
|
|
125
|
+
* should be shorter ("Cancel Subscription"). */
|
|
126
|
+
readonly confirmLabel?: string;
|
|
127
|
+
/** Conditional visibility per row. */
|
|
128
|
+
readonly visible?: FieldCondition;
|
|
129
|
+
/** Visual style. "danger" renders red + forces a confirm dialog (even
|
|
130
|
+
* without an explicit `confirm` key). */
|
|
131
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
132
|
+
/** Overrides the id-based default icon (see ACTION_ICON_BY_ID in
|
|
133
|
+
* kumiko-renderer) — closed IconKey vocabulary into the ICONS registry
|
|
134
|
+
* (renderer/web), analogous to EditFieldSpec.icon. */
|
|
135
|
+
readonly icon?: IconKey;
|
|
136
|
+
/** Inline render form in the row-action column — see `RowActionDisplay`. */
|
|
137
|
+
readonly display?: RowActionDisplay;
|
|
138
|
+
/** Where to go after the write succeeded instead of staying on the shown
|
|
139
|
+
* record. Same forms as entityEdit `redirect`. Honored on record actions
|
|
140
|
+
* (projectionDetail header/section actions, entityEdit header/section
|
|
141
|
+
* actions); a valid `returnTo` wins under the same rule as
|
|
142
|
+
* entityEdit.redirect. Not used by list row actions. Without it, an action
|
|
143
|
+
* deleting the shown record (`<feature>:write:<entity>:delete` with that
|
|
144
|
+
* record's id) leaves to `returnTo`/the list; any other action refetches. */
|
|
145
|
+
readonly redirect?: string | ActionFormRedirect;
|
|
146
|
+
};
|
|
147
|
+
export type RowActionNavigateBase = {
|
|
148
|
+
readonly kind: "navigate";
|
|
149
|
+
readonly id: string;
|
|
150
|
+
readonly label: string;
|
|
151
|
+
/** Field name whose value is embedded as entityId in the URL path
|
|
152
|
+
* (`/<workspace>/<screen>/<entityId>`). entityEdit reads the id
|
|
153
|
+
* EXCLUSIVELY from the path. Default: "id" if the target screen is an
|
|
154
|
+
* entityEdit OR if `entity` (instead of `screen`) is set. */
|
|
155
|
+
readonly entityId?: string;
|
|
156
|
+
/** Declarative URL search params extracted from the clicked row.
|
|
157
|
+
* Both actionForm and entityEdit-create targets read `params` as
|
|
158
|
+
* initial values (pre-filling the form). entityEdit-update does
|
|
159
|
+
* not — the existing record's values take precedence.
|
|
160
|
+
* `pick` extracts fields of the same name; `map` renames them. */
|
|
161
|
+
readonly params?: RowFieldExtractor;
|
|
162
|
+
/** Section id of the tab to activate on the target projectionDetail
|
|
163
|
+
* (layout.mode "tabs"); merged into the navigate search params next to
|
|
164
|
+
* returnTo. Boot validator checks the id exists on the target. */
|
|
165
|
+
readonly tab?: string;
|
|
166
|
+
/** Conditional visibility per row. */
|
|
167
|
+
readonly visible?: FieldCondition;
|
|
168
|
+
/** Visual style only — unlike the writeHandler variants, "danger" does not
|
|
169
|
+
* force a confirm dialog here: the target form is the confirmation. */
|
|
170
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
171
|
+
/** When true, a click on the whole row (not just the action menu) triggers
|
|
172
|
+
* this navigate action. At most one per list (the boot validator checks).
|
|
173
|
+
* Only on navigate — a row click must not trigger a (possibly destructive,
|
|
174
|
+
* unconfirmed) write, hence not on writeHandler actions. */
|
|
175
|
+
readonly rowClick?: boolean;
|
|
176
|
+
/** Overrides the id-based default icon (see ACTION_ICON_BY_ID in
|
|
177
|
+
* kumiko-renderer) — closed IconKey vocabulary into the ICONS registry
|
|
178
|
+
* (renderer/web), analogous to EditFieldSpec.icon. */
|
|
179
|
+
readonly icon?: IconKey;
|
|
180
|
+
/** Inline render form in the row-action column — see `RowActionDisplay`. */
|
|
181
|
+
readonly display?: RowActionDisplay;
|
|
182
|
+
};
|
|
183
|
+
/** Exactly one of `screen` / `entity` — mutual exclusivity is type-enforced
|
|
184
|
+
* (boot validator still rejects both/neither for untyped schemas). */
|
|
185
|
+
export type RowActionNavigate = RowActionNavigateBase & ({
|
|
186
|
+
/** Screen id (short, unqualified). The boot validator checks that it
|
|
187
|
+
* exists in the same feature. */
|
|
188
|
+
readonly screen: string;
|
|
189
|
+
readonly entity?: never;
|
|
190
|
+
} | {
|
|
191
|
+
/** Entity name instead of screen id (fw#2228) — resolves at boot time to
|
|
192
|
+
* the screen that declares `detailFor: "<entity>"` (see ObjectTarget/
|
|
193
|
+
* resolveTarget in @cosmicdrift/kumiko-renderer's nav.tsx). */
|
|
194
|
+
readonly entity: string;
|
|
195
|
+
readonly screen?: never;
|
|
196
|
+
});
|
|
197
|
+
export type RowActionDrawer = {
|
|
198
|
+
readonly kind: "drawer";
|
|
199
|
+
readonly id: string;
|
|
200
|
+
readonly label: string;
|
|
201
|
+
/** Short, unqualified id of an `actionForm` screen in the same feature.
|
|
202
|
+
* Boot validator rejects a missing screen and a screen whose `type`
|
|
203
|
+
* isn't `actionForm` — same check as ToolbarAction's drawer variant
|
|
204
|
+
* (see docs/plans/bundled-features-screen-standardisierung.md §2.6c). */
|
|
205
|
+
readonly screen: string;
|
|
206
|
+
/** Declarative prefill extracted from the clicked row. `pick` extracts
|
|
207
|
+
* fields of the same name; `map` renames them. */
|
|
208
|
+
readonly params?: RowFieldExtractor;
|
|
209
|
+
/** Per-row conditional visibility. */
|
|
210
|
+
readonly visible?: FieldCondition;
|
|
211
|
+
/** Visual style only — unlike the writeHandler variants, "danger" does not
|
|
212
|
+
* force a confirm dialog here: the target form is the confirmation. */
|
|
213
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
214
|
+
/** Overrides the id-based default icon (see ACTION_ICON_BY_ID in
|
|
215
|
+
* kumiko-renderer) — closed IconKey vocabulary into the ICONS registry
|
|
216
|
+
* (renderer/web), analogous to EditFieldSpec.icon. */
|
|
217
|
+
readonly icon?: IconKey;
|
|
218
|
+
/** Inline render form in the row-action column — see `RowActionDisplay`. */
|
|
219
|
+
readonly display?: RowActionDisplay;
|
|
220
|
+
/** Drawer heading (i18n key) instead of the action `label`. `{name}`
|
|
221
|
+
* placeholders resolve from the drawer prefill (`params`), formatted for
|
|
222
|
+
* display like `ActionFormScreenDefinition.summary`. */
|
|
223
|
+
readonly title?: string;
|
|
224
|
+
/** Line under the drawer heading (i18n key or a bare `{name}` template),
|
|
225
|
+
* resolved like `title`. */
|
|
226
|
+
readonly subtitle?: string;
|
|
227
|
+
/** Clicking the row opens this drawer with that row's `params`. At most one
|
|
228
|
+
* `rowClick` action (navigate or drawer) per list; rejected on
|
|
229
|
+
* projectionDetail header actions, like navigate's. */
|
|
230
|
+
readonly rowClick?: boolean;
|
|
231
|
+
};
|
|
232
|
+
export type RowActionClientAction = {
|
|
233
|
+
readonly kind: "clientAction";
|
|
234
|
+
readonly id: string;
|
|
235
|
+
readonly label: string;
|
|
236
|
+
/** Registered client handler name, `<feature>:<kebab>`, e.g.
|
|
237
|
+
* "auth-email-password:request-password-reset". Resolved when the action is
|
|
238
|
+
* triggered; a name no client feature registered shows an error toast. */
|
|
239
|
+
readonly action: string;
|
|
240
|
+
/** Payload handed to the handler, picked from the row (or the shown record).
|
|
241
|
+
* Default `{ id: row.id }`, like a writeHandler. */
|
|
242
|
+
readonly payload?: RowFieldExtractor;
|
|
243
|
+
/** i18n key for the confirm dialog description. */
|
|
244
|
+
readonly confirm?: string;
|
|
245
|
+
/** i18n key for the confirm button, default `label`. */
|
|
246
|
+
readonly confirmLabel?: string;
|
|
247
|
+
/** Conditional visibility per row. */
|
|
248
|
+
readonly visible?: FieldCondition;
|
|
249
|
+
/** Visual style. "danger" forces a confirm dialog, like a writeHandler. */
|
|
250
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
251
|
+
/** Overrides the id-based default icon, see `RowActionWriteHandler.icon`. */
|
|
252
|
+
readonly icon?: IconKey;
|
|
253
|
+
/** Inline render form in the row-action column — see `RowActionDisplay`. */
|
|
254
|
+
readonly display?: RowActionDisplay;
|
|
255
|
+
/** i18n key of the toast shown after the handler reported `{ ok: true }`
|
|
256
|
+
* (default `kumiko.clientAction.success`). */
|
|
257
|
+
readonly successMessage?: string;
|
|
258
|
+
};
|
|
259
|
+
export type ToolbarAction = {
|
|
260
|
+
readonly kind: "navigate";
|
|
261
|
+
readonly id: string;
|
|
262
|
+
readonly label: string;
|
|
263
|
+
/** Screen id (short, unqualified) to navigate to. */
|
|
264
|
+
readonly screen: string;
|
|
265
|
+
/** Section id of the tab to activate on the target projectionDetail
|
|
266
|
+
* (layout.mode "tabs"); merged into the navigate search params next to
|
|
267
|
+
* returnTo. Boot validator checks the id exists on the target. */
|
|
268
|
+
readonly tab?: string;
|
|
269
|
+
/** Visual style only — unlike the writeHandler variant, "danger" does
|
|
270
|
+
* not force a confirm dialog here: the target form is the
|
|
271
|
+
* confirmation. */
|
|
272
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
273
|
+
} | {
|
|
274
|
+
readonly kind: "writeHandler";
|
|
275
|
+
readonly id: string;
|
|
276
|
+
readonly label: string;
|
|
277
|
+
readonly handler: string;
|
|
278
|
+
/** Static payload without row context. Default = `{}`. */
|
|
279
|
+
readonly payload?: Record<string, unknown>;
|
|
280
|
+
/** i18n key for the confirm dialog description. When set AND/OR
|
|
281
|
+
* style="danger": modal before execution. */
|
|
282
|
+
readonly confirm?: string;
|
|
283
|
+
/** i18n key for the confirm button text in the dialog. Default = `label`. */
|
|
284
|
+
readonly confirmLabel?: string;
|
|
285
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
286
|
+
} | {
|
|
287
|
+
readonly kind: "drawer";
|
|
288
|
+
readonly id: string;
|
|
289
|
+
readonly label: string;
|
|
290
|
+
/** Short, unqualified id of an `actionForm` screen in the same
|
|
291
|
+
* feature. Boot validator rejects a missing screen and a screen
|
|
292
|
+
* whose `type` isn't `actionForm` — the caller decides full-page
|
|
293
|
+
* vs. drawer, not the form (see docs/plans/bundled-features-screen-
|
|
294
|
+
* standardisierung.md §2.6c). */
|
|
295
|
+
readonly screen: string;
|
|
296
|
+
/** Visual style only — unlike the writeHandler variant, "danger" does
|
|
297
|
+
* not force a confirm dialog here: the target form is the
|
|
298
|
+
* confirmation. */
|
|
299
|
+
readonly style?: "primary" | "secondary" | "danger";
|
|
300
|
+
/** Drawer heading (i18n key) instead of `label`. */
|
|
301
|
+
readonly title?: string;
|
|
302
|
+
/** Line under the drawer heading (i18n key). */
|
|
303
|
+
readonly subtitle?: string;
|
|
304
|
+
};
|
|
305
|
+
export type RelatedListToolbarAction = (Extract<ToolbarAction, {
|
|
306
|
+
readonly kind: "navigate";
|
|
307
|
+
}> & {
|
|
308
|
+
/** Conditional visibility, evaluated against the relatedList's parent
|
|
309
|
+
* record (the "Akte"). */
|
|
310
|
+
readonly visible?: FieldCondition;
|
|
311
|
+
/** Declarative URL search params extracted from the parent record,
|
|
312
|
+
* prefilling the target screen. Replaces the implicit
|
|
313
|
+
* `{ [parentParam]: parentId }` default when set. */
|
|
314
|
+
readonly params?: RowFieldExtractor;
|
|
315
|
+
}) | (Extract<ToolbarAction, {
|
|
316
|
+
readonly kind: "writeHandler";
|
|
317
|
+
}> & {
|
|
318
|
+
readonly visible?: FieldCondition;
|
|
319
|
+
}) | (Extract<ToolbarAction, {
|
|
320
|
+
readonly kind: "drawer";
|
|
321
|
+
}> & {
|
|
322
|
+
readonly visible?: FieldCondition;
|
|
323
|
+
});
|
|
324
|
+
/** Expandable area of an `entityList` row: a relatedList of the row's record.
|
|
325
|
+
* `id` (tab id) and `countField` (tab badge) have no meaning outside a tabs
|
|
326
|
+
* layout and are therefore omitted. `kind` stays the discriminator so further
|
|
327
|
+
* expansion kinds can be added without a breaking change. */
|
|
328
|
+
export type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField">;
|
|
329
|
+
/** An extra choice on a facet next to the field's own options: a named
|
|
330
|
+
* bundle of values ("Running" = several statuses) or "no filter" (`values:
|
|
331
|
+
* []`, e.g. "All"). Chips only. */
|
|
332
|
+
export type EntityListFacetExtraOption = {
|
|
333
|
+
readonly id: string;
|
|
334
|
+
/** i18n key. */
|
|
335
|
+
readonly label: string;
|
|
336
|
+
/** Option values this choice selects; `[]` selects nothing, i.e. no filter. */
|
|
337
|
+
readonly values: readonly string[];
|
|
338
|
+
/** Where the chip sits relative to the field's own options. Default "start". */
|
|
339
|
+
readonly position?: "start" | "end";
|
|
340
|
+
};
|
|
341
|
+
export type EntityListFacetConfig = {
|
|
342
|
+
/** `"select"` (default): dropdown with checkboxes. `"chips"`: one toggle
|
|
343
|
+
* chip per option, single choice. */
|
|
344
|
+
readonly display?: "select" | "chips";
|
|
345
|
+
/** Chips show how many rows each option matches (other facets and the
|
|
346
|
+
* search apply). Counts come from the list query itself, one `limit: 1`
|
|
347
|
+
* count request per chip. */
|
|
348
|
+
readonly showCounts?: boolean;
|
|
349
|
+
/** Chips with zero matching rows are not shown. Needs counts; implies
|
|
350
|
+
* `showCounts`'s requests even when the number itself is not shown. */
|
|
351
|
+
readonly hideEmpty?: boolean;
|
|
352
|
+
readonly extraOptions?: readonly EntityListFacetExtraOption[];
|
|
353
|
+
};
|
|
354
|
+
export type EntityListScreenDefinition = {
|
|
355
|
+
readonly id: string;
|
|
356
|
+
readonly type: "entityList";
|
|
357
|
+
/** Default true: the screen fills the shell content height, its body scrolls
|
|
358
|
+
* inside and the pager / action bar stays pinned. `false` restores page
|
|
359
|
+
* scroll (table grows with its rows, pager below the last row, form footer
|
|
360
|
+
* in the flow). */
|
|
361
|
+
readonly fillHeight?: boolean;
|
|
362
|
+
readonly nav?: ScreenNavSugar;
|
|
363
|
+
readonly detailFor?: string;
|
|
364
|
+
readonly description?: string;
|
|
365
|
+
readonly agent?: AgentScreenHints;
|
|
366
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
367
|
+
* app); exempts it from the nav-area boot check. */
|
|
368
|
+
readonly dormant?: boolean;
|
|
369
|
+
/** UI-only gate, also on direct URL access; handlers still enforce access. Unmet or failed renders `fallback`. */
|
|
370
|
+
readonly visibleWhen?: DashboardPanelVisibility;
|
|
371
|
+
/** Same-feature short id or cross-feature QN `<feature>:screen:<id>` shown when `visibleWhen` is unmet (default: standard notice). */
|
|
372
|
+
readonly fallback?: string;
|
|
373
|
+
readonly entity: string;
|
|
374
|
+
/** Short screen id (same feature) the create button opens instead of the entity's default edit screen. */
|
|
375
|
+
readonly createScreen?: string;
|
|
376
|
+
/** Set by the role projection when `createScreen` is not reachable for the role: the renderer then shows no create button instead of falling back to the entity's edit screen. Not for app authors. */
|
|
377
|
+
readonly createUnavailable?: boolean;
|
|
378
|
+
/** `false` hides this list's "+ New" button (and the empty-state create CTA) while the entity's edit screen keeps its create branch for other entry points, e.g. a prefilled action. Default `true`. */
|
|
379
|
+
readonly allowCreate?: boolean;
|
|
380
|
+
readonly columns: readonly ListColumnSpec[];
|
|
381
|
+
readonly rowRenderer?: PlatformComponent;
|
|
382
|
+
readonly cardRenderer?: PlatformComponent;
|
|
383
|
+
/** Per-row actions, rendered as an actions column on the right.
|
|
384
|
+
* Adaptive (default, see `rowActionMode`): the primary action as a link,
|
|
385
|
+
* the rest in the kebab menu; actions with an explicit `display` stay
|
|
386
|
+
* inline. Array order is UI order. */
|
|
387
|
+
readonly rowActions?: readonly RowAction[];
|
|
388
|
+
/** Layout of the row-action column — see `RowActionMode`. */
|
|
389
|
+
readonly rowActionMode?: RowActionMode;
|
|
390
|
+
/** Toolbar actions (list header). "Open Incident", "Schedule Maintenance"
|
|
391
|
+
* etc. — next to "+ New" if present. Array order = UI order,
|
|
392
|
+
* primary-style links. */
|
|
393
|
+
readonly toolbarActions?: readonly ToolbarAction[];
|
|
394
|
+
/** Expandable area under each row: a relatedList of the row's record
|
|
395
|
+
* (parentId = the row's `id`). Rows get a toggle button; several rows can
|
|
396
|
+
* be open at once. Its own row actions run against the sub-list rows, and
|
|
397
|
+
* a successful write also reloads this list. */
|
|
398
|
+
readonly expandableRow?: EntityListExpandableRow;
|
|
399
|
+
/** Server-side filter, fixed on the screen — three buckets of the same
|
|
400
|
+
* entity without custom pages (e.g. "Upcoming" / "Active" / "Past"
|
|
401
|
+
* Maintenance). The user-side q search runs ON the filtered set on top.
|
|
402
|
+
* The boot validator pins that the field exists in the entity. */
|
|
403
|
+
readonly filter?: ScreenFilter;
|
|
404
|
+
readonly pagination?: ListPaginationMode;
|
|
405
|
+
readonly pageSize?: number;
|
|
406
|
+
readonly defaultSort?: ListSortSpec;
|
|
407
|
+
/** Initial facet selection, per filterable field: option values (string
|
|
408
|
+
* facets) or a boolean. Applies only while the URL carries no value for
|
|
409
|
+
* that field, so a link or the user's own choice wins; picking "no filter"
|
|
410
|
+
* (an `extraOptions` entry with `values: []`, or clearing the facet) is
|
|
411
|
+
* remembered in the URL and is not overridden again. */
|
|
412
|
+
readonly defaultFilters?: Readonly<Record<string, readonly string[] | boolean>>;
|
|
413
|
+
/** Per-facet display options for the entity's `filterable` fields, keyed by
|
|
414
|
+
* field name. `false` hides the facet's control (its filter stays usable
|
|
415
|
+
* through `defaultFilters` and the URL). Fields without an entry keep the
|
|
416
|
+
* default dropdown. */
|
|
417
|
+
readonly facets?: Readonly<Record<string, EntityListFacetConfig | false>>;
|
|
418
|
+
/** Replaces the default empty-list title/hint; the create button stays when the list can create. */
|
|
419
|
+
readonly emptyState?: ListEmptyStateSpec;
|
|
420
|
+
/** i18n key for the primary create button label (default `kumiko.actions.create`). */
|
|
421
|
+
readonly createLabel?: string;
|
|
422
|
+
/** i18n key for the search field placeholder (default `kumiko.list.search-placeholder`). */
|
|
423
|
+
readonly searchPlaceholder?: string;
|
|
424
|
+
readonly searchable?: boolean;
|
|
425
|
+
readonly slots?: ScreenSlots;
|
|
426
|
+
readonly access?: AccessRule;
|
|
427
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import type { SelectOptionTone } from "./fields.ts";
|
|
2
|
+
import type { AccessRule, AgentScreenHints } from "./handlers.ts";
|
|
3
|
+
import type { PlatformComponent, ScreenNavSugar, ScreenSlots } from "./screen.ts";
|
|
4
|
+
import type { DashboardPanelVisibility } from "./screen-dashboard.ts";
|
|
5
|
+
import type { EditLayout, ListEmptyStateSpec } from "./screen-entity-edit.ts";
|
|
6
|
+
import type { ListColumnSpec, ListPaginationMode, ListSortSpec, RowAction, RowActionMode, RowFieldExtractor, ScreenFilter, ToolbarAction } from "./screen-entity-list.ts";
|
|
7
|
+
export type ListFacetSpec = {
|
|
8
|
+
readonly field: string;
|
|
9
|
+
readonly type: "select";
|
|
10
|
+
readonly label: string;
|
|
11
|
+
readonly options: readonly {
|
|
12
|
+
readonly value: string;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
}[];
|
|
15
|
+
} | {
|
|
16
|
+
readonly field: string;
|
|
17
|
+
readonly type: "boolean";
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly trueLabel: string;
|
|
20
|
+
readonly falseLabel: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly field: string;
|
|
23
|
+
readonly type: "reference";
|
|
24
|
+
readonly label: string;
|
|
25
|
+
/** Entity name (same feature) or `feature:entity` (cross-feature), same
|
|
26
|
+
* convention as `ListColumnSpec.refEntity`. Options load at render time. */
|
|
27
|
+
readonly entity: string;
|
|
28
|
+
/** Row field on the referenced entity shown as the option label
|
|
29
|
+
* (default "id"). */
|
|
30
|
+
readonly labelField?: string;
|
|
31
|
+
} | {
|
|
32
|
+
/** Declarative only — the date field the range narrows. It is NOT sent
|
|
33
|
+
* to the server (the bounds go under `params`); it namespaces the
|
|
34
|
+
* facet's URL keys and tells a reading agent which column this list
|
|
35
|
+
* is time-rangeable on. */
|
|
36
|
+
readonly field: string;
|
|
37
|
+
readonly type: "dateRange";
|
|
38
|
+
readonly label: string;
|
|
39
|
+
/** Query-payload keys the chosen bounds are sent under as ISO instants.
|
|
40
|
+
* Explicit rather than a `from`/`to` convention — a query is free to
|
|
41
|
+
* name its time bounds anything, and the boot-validator checks these
|
|
42
|
+
* keys against the handler's Zod schema. */
|
|
43
|
+
readonly params: {
|
|
44
|
+
readonly from: string;
|
|
45
|
+
readonly to: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export type ProjectionListScreenDefinition = {
|
|
49
|
+
readonly id: string;
|
|
50
|
+
readonly type: "projectionList";
|
|
51
|
+
/** Default true: the screen fills the shell content height, its body scrolls
|
|
52
|
+
* inside and the pager / action bar stays pinned. `false` restores page
|
|
53
|
+
* scroll (table grows with its rows, pager below the last row, form footer
|
|
54
|
+
* in the flow). */
|
|
55
|
+
readonly fillHeight?: boolean;
|
|
56
|
+
readonly nav?: ScreenNavSugar;
|
|
57
|
+
readonly detailFor?: string;
|
|
58
|
+
readonly description?: string;
|
|
59
|
+
readonly agent?: AgentScreenHints;
|
|
60
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
61
|
+
* app); exempts it from the nav-area boot check. */
|
|
62
|
+
readonly dormant?: boolean;
|
|
63
|
+
/** UI-only gate, also on direct URL access; handlers still enforce access. Unmet or failed renders `fallback`. */
|
|
64
|
+
readonly visibleWhen?: DashboardPanelVisibility;
|
|
65
|
+
/** Same-feature short id or cross-feature QN `<feature>:screen:<id>` shown when `visibleWhen` is unmet (default: standard notice). */
|
|
66
|
+
readonly fallback?: string;
|
|
67
|
+
readonly query: string;
|
|
68
|
+
readonly columns: readonly ListColumnSpec[];
|
|
69
|
+
readonly rowRenderer?: PlatformComponent;
|
|
70
|
+
readonly cardRenderer?: PlatformComponent;
|
|
71
|
+
readonly rowActions?: readonly RowAction[];
|
|
72
|
+
/** Layout of the row-action column — see `RowActionMode`. */
|
|
73
|
+
readonly rowActionMode?: RowActionMode;
|
|
74
|
+
readonly toolbarActions?: readonly ToolbarAction[];
|
|
75
|
+
readonly pagination?: ListPaginationMode;
|
|
76
|
+
readonly pageSize?: number;
|
|
77
|
+
readonly defaultSort?: ListSortSpec;
|
|
78
|
+
/** Replaces the default empty-list title/hint; the create button stays when the list can create. */
|
|
79
|
+
readonly emptyState?: ListEmptyStateSpec;
|
|
80
|
+
/** i18n key for the primary create button label (default `kumiko.actions.create`). */
|
|
81
|
+
readonly createLabel?: string;
|
|
82
|
+
/** i18n key for the search field placeholder (default `kumiko.list.search-placeholder`). */
|
|
83
|
+
readonly searchPlaceholder?: string;
|
|
84
|
+
readonly searchable?: boolean;
|
|
85
|
+
/** Derived by buildAppSchema from the query handler's Zod schema (`sort`
|
|
86
|
+
* param present). Same type serves author and wire schema (no separate
|
|
87
|
+
* client-facing screen type exists), so this stays structurally
|
|
88
|
+
* writable — validateProjectionListScreens rejects any hand-authored
|
|
89
|
+
* value at boot (fw#2165). */
|
|
90
|
+
readonly sortable?: boolean;
|
|
91
|
+
/** Derived by buildAppSchema from the query handler's Zod schema (`cursor`
|
|
92
|
+
* or `offset` param present). See `sortable` doc for why this is
|
|
93
|
+
* boot-enforced rather than type-enforced. */
|
|
94
|
+
readonly paginated?: boolean;
|
|
95
|
+
/** Server-side filter, fixed on the screen — see `ScreenFilter` doc above
|
|
96
|
+
* (entityList). Field existence can't be checked without an entity; the
|
|
97
|
+
* boot-validator only checks structure (`op:"in"` ⇒ array value). */
|
|
98
|
+
readonly filter?: ScreenFilter;
|
|
99
|
+
/** User-toggleable facet dropdowns — see `ListFacetSpec` doc. `field`
|
|
100
|
+
* must be a declared column, except for `type: "reference"`: it filters
|
|
101
|
+
* by id through the bound query's `filters` and needs a registered
|
|
102
|
+
* `entity` instead. The bound query handler must accept `filters` in its
|
|
103
|
+
* Zod schema — the boot-validator checks both. A
|
|
104
|
+
* "dateRange" facet instead needs its two `params` keys in that schema;
|
|
105
|
+
* it sends no `filters` entry. */
|
|
106
|
+
readonly facets?: readonly ListFacetSpec[];
|
|
107
|
+
readonly slots?: ScreenSlots;
|
|
108
|
+
readonly access?: AccessRule;
|
|
109
|
+
};
|
|
110
|
+
export type RecordHeaderSubtitlePart = string | {
|
|
111
|
+
readonly field: string;
|
|
112
|
+
readonly navigate?: MetricNavigate;
|
|
113
|
+
};
|
|
114
|
+
export type RecordHeaderSpec = {
|
|
115
|
+
readonly title: string;
|
|
116
|
+
/** Field name, or several parts (field name or `{ field, navigate }`)
|
|
117
|
+
* joined by a separator; parts with an empty value are dropped. */
|
|
118
|
+
readonly subtitle?: string | readonly RecordHeaderSubtitlePart[];
|
|
119
|
+
readonly status?: string;
|
|
120
|
+
/** Record field holding an absolute http(s) URL. When present and the
|
|
121
|
+
* field's value is such a URL, the subtitle renders as an external link
|
|
122
|
+
* (`target="_blank"`) instead of plain text. Only valid with the string
|
|
123
|
+
* form of `subtitle`. */
|
|
124
|
+
readonly subtitleHref?: string;
|
|
125
|
+
/** Record field whose value selects the `statusTones` entry, for a badge text that
|
|
126
|
+
* carries more than the state (e.g. "Terminated on 31.03.2027"). Defaults to `status`. */
|
|
127
|
+
readonly statusToneField?: string;
|
|
128
|
+
/** Badge tone per `statusToneField` value (default: the `status` value); unlisted values fall back to the value heuristic. */
|
|
129
|
+
readonly statusTones?: {
|
|
130
|
+
readonly [statusValue: string]: SelectOptionTone;
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
export type MetricNavigate = {
|
|
134
|
+
readonly screen?: string;
|
|
135
|
+
readonly entity?: string;
|
|
136
|
+
readonly entityId?: string;
|
|
137
|
+
readonly params?: RowFieldExtractor;
|
|
138
|
+
/** Section id of the tab to activate at the destination. Alone (no
|
|
139
|
+
* `screen`/`entity`), it activates that tab on the current record
|
|
140
|
+
* instead of navigating away. */
|
|
141
|
+
readonly tab?: string;
|
|
142
|
+
};
|
|
143
|
+
export type MetricSpec = {
|
|
144
|
+
readonly field: string;
|
|
145
|
+
readonly label: string;
|
|
146
|
+
readonly navigate?: MetricNavigate;
|
|
147
|
+
};
|
|
148
|
+
export type ProjectionDetailScreenDefinition = {
|
|
149
|
+
readonly id: string;
|
|
150
|
+
readonly type: "projectionDetail";
|
|
151
|
+
/** Default true: the screen fills the shell content height, its body scrolls
|
|
152
|
+
* inside and the pager / action bar stays pinned. `false` restores page
|
|
153
|
+
* scroll (table grows with its rows, pager below the last row, form footer
|
|
154
|
+
* in the flow). */
|
|
155
|
+
readonly fillHeight?: boolean;
|
|
156
|
+
readonly nav?: ScreenNavSugar;
|
|
157
|
+
readonly detailFor?: string;
|
|
158
|
+
readonly description?: string;
|
|
159
|
+
readonly agent?: AgentScreenHints;
|
|
160
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
161
|
+
* app); exempts it from the nav-area boot check. */
|
|
162
|
+
readonly dormant?: boolean;
|
|
163
|
+
/** UI-only gate, also on direct URL access; handlers still enforce access. Unmet or failed renders `fallback`. */
|
|
164
|
+
readonly visibleWhen?: DashboardPanelVisibility;
|
|
165
|
+
/** Same-feature short id or cross-feature QN `<feature>:screen:<id>` shown when `visibleWhen` is unmet (default: standard notice). */
|
|
166
|
+
readonly fallback?: string;
|
|
167
|
+
readonly query: string;
|
|
168
|
+
/** Query output field whose value titles the page header (breadcrumb) when no `header` is set. */
|
|
169
|
+
readonly recordTitleField?: string;
|
|
170
|
+
/** Query-payload key for the row-id. Default "id". */
|
|
171
|
+
readonly idParam?: string;
|
|
172
|
+
/** The server determines the shown row from the caller's session/context
|
|
173
|
+
* instead of a row id in the path — a self-service screen ("my profile",
|
|
174
|
+
* "my data") that has no row to link to (unlike `EntityEditScreenDefinition.
|
|
175
|
+
* singleton`, which still resolves via `<entity>:list` limit 1; here the
|
|
176
|
+
* query itself owns the row selection, e.g. `user:query:user:me`). The
|
|
177
|
+
* query is called WITHOUT the `idParam` key — there is no id to send, so
|
|
178
|
+
* the boot-validator rejects declaring `idParam` alongside this flag
|
|
179
|
+
* instead of letting one silently win. Stricter than an id-addressed
|
|
180
|
+
* screen, not looser: the server picks the row, so no client-supplied id
|
|
181
|
+
* can reach the query — a stray id on the path is ignored, never
|
|
182
|
+
* forwarded. */
|
|
183
|
+
readonly singleton?: boolean;
|
|
184
|
+
readonly layout: EditLayout;
|
|
185
|
+
/** Optional per-field label i18n key (field name → key), analogous to
|
|
186
|
+
* entityEdit.fieldLabels. The pseudo entity `__projection-detail__` has
|
|
187
|
+
* no natural field namespace — a missing entry falls back to the
|
|
188
|
+
* convention `<feature>:entity:__projection-detail__:field:<name>`. */
|
|
189
|
+
readonly fieldLabels?: Readonly<Record<string, string>>;
|
|
190
|
+
/** Record header above the layout — title/subtitle/status name columns
|
|
191
|
+
* in the query row (not literals). Rendered only when set. */
|
|
192
|
+
readonly header?: RecordHeaderSpec;
|
|
193
|
+
/** Metric band above the layout — column names in the query row, each with its own i18n `label`.
|
|
194
|
+
* Rendered only when set. */
|
|
195
|
+
readonly metrics?: readonly MetricSpec[];
|
|
196
|
+
/** Parent list screen (short id) for a "back" navigation. */
|
|
197
|
+
readonly listScreenId?: string;
|
|
198
|
+
readonly slots?: ScreenSlots;
|
|
199
|
+
readonly access?: AccessRule;
|
|
200
|
+
/** Header action buttons. Reuses `RowAction` — the screen's single
|
|
201
|
+
* displayed record stands in for the "row", so `pick`/`map`, `visible`,
|
|
202
|
+
* `confirm`/`confirmLabel` and `style` keep their meaning unchanged.
|
|
203
|
+
* `rowClick` has no target here (there is no row to click) and is
|
|
204
|
+
* rejected by the boot-validator. */
|
|
205
|
+
readonly actions?: readonly RowAction[];
|
|
206
|
+
/** Id of the action that stays the visible header button when the actions
|
|
207
|
+
* collapse into a menu (and the one that keeps the phone icon slot when it
|
|
208
|
+
* has an icon and no confirm). An `actions` id or `"edit"` (the default edit
|
|
209
|
+
* action); default precedence is `edit`, then `style: "primary"`, then the
|
|
210
|
+
* first action. */
|
|
211
|
+
readonly primaryAction?: string;
|
|
212
|
+
/** How head fields render. Default "text" — plain text instead of a
|
|
213
|
+
* disabled Input, since every field on this screen type is forced
|
|
214
|
+
* readOnly anyway (there is no write path, see the shim doc above).
|
|
215
|
+
* Set "form" to opt back into the disabled-Input look (fw#2245). */
|
|
216
|
+
readonly valueDisplay?: "form" | "text";
|
|
217
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|