@nanisoft/prism-ui 0.16.0 → 1.0.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/.tsbuildinfo +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts +120 -190
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts.map +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.js +107 -97
- package/dist/blocks/activity-feed-01/index.d.ts +1 -1
- package/dist/blocks/activity-feed-01/index.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.d.ts +86 -53
- package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.js +95 -31
- package/dist/blocks/auth-form-01/index.d.ts +1 -1
- package/dist/blocks/auth-form-01/index.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.d.ts +28 -0
- package/dist/blocks/calendar-01/calendar.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.js +15 -1
- package/dist/blocks/card-index-01/card-index.d.ts +225 -0
- package/dist/blocks/card-index-01/card-index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/card-index.js +154 -0
- package/dist/blocks/card-index-01/index.d.ts +3 -0
- package/dist/blocks/card-index-01/index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/index.js +1 -0
- package/dist/blocks/chart-card-01/chart-card-01.d.ts +23 -21
- package/dist/blocks/chart-card-01/chart-card-01.d.ts.map +1 -1
- package/dist/blocks/chart-card-01/chart-card-01.js +46 -1
- package/dist/blocks/chart-card-01/index.d.ts +1 -1
- package/dist/blocks/chart-card-01/index.d.ts.map +1 -1
- package/dist/blocks/chart-group-01/chart-group-01.d.ts +3 -3
- package/dist/blocks/chart-group-01/chart-group-01.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.d.ts +30 -82
- package/dist/blocks/contact-01/contact.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.js +59 -30
- package/dist/blocks/contact-01/index.d.ts +1 -1
- package/dist/blocks/contact-01/index.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.d.ts +26 -71
- package/dist/blocks/dashboard-01/dashboard-01.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.js +34 -23
- package/dist/blocks/dashboard-01/index.d.ts +1 -1
- package/dist/blocks/dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.d.ts +276 -59
- package/dist/blocks/data-table-01/data-table.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.js +217 -35
- package/dist/blocks/data-table-01/index.d.ts +1 -1
- package/dist/blocks/data-table-01/index.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.d.ts +23 -2
- package/dist/blocks/directory-01/directory-01.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.js +3 -3
- package/dist/blocks/empty-state-01/empty-state.d.ts +60 -21
- package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -1
- package/dist/blocks/empty-state-01/empty-state.js +52 -17
- package/dist/blocks/index-detail-01/index-detail.d.ts +85 -0
- package/dist/blocks/index-detail-01/index-detail.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index-detail.js +58 -0
- package/dist/blocks/index-detail-01/index.d.ts +3 -0
- package/dist/blocks/index-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index.js +1 -0
- package/dist/blocks/index.d.ts +20 -8
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/index.js +7 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts +18 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts.map +1 -1
- package/dist/blocks/issue-detail-01/issue-detail.js +18 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts +3 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/index.js +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts +119 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.js +79 -0
- package/dist/blocks/project-dashboard-01/index.d.ts +1 -1
- package/dist/blocks/project-dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts +21 -27
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.js +45 -5
- package/dist/blocks/provisioning-01/index.d.ts +1 -1
- package/dist/blocks/provisioning-01/index.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.d.ts +9 -97
- package/dist/blocks/provisioning-01/provisioning.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.js +26 -13
- package/dist/blocks/record-detail-01/index.d.ts +3 -0
- package/dist/blocks/record-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/index.js +1 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts +239 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/record-detail.js +150 -0
- package/dist/blocks/record-form-01/index.d.ts +3 -0
- package/dist/blocks/record-form-01/index.d.ts.map +1 -0
- package/dist/blocks/record-form-01/index.js +1 -0
- package/dist/blocks/record-form-01/record-form.d.ts +148 -0
- package/dist/blocks/record-form-01/record-form.d.ts.map +1 -0
- package/dist/blocks/record-form-01/record-form.js +107 -0
- package/dist/blocks/record-grid-01/index.d.ts +3 -0
- package/dist/blocks/record-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/index.js +1 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts +145 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/record-grid.js +114 -0
- package/dist/blocks/record-wizard-01/index.d.ts +3 -0
- package/dist/blocks/record-wizard-01/index.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/index.js +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts +152 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.js +144 -0
- package/dist/blocks/settings-members-01/settings-members.d.ts +2 -1
- package/dist/blocks/settings-members-01/settings-members.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.d.ts +165 -67
- package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.js +92 -33
- package/dist/blocks/signup-01/index.d.ts +1 -1
- package/dist/blocks/signup-01/index.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.d.ts +10 -84
- package/dist/blocks/signup-01/signup-01.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.js +41 -23
- package/dist/blocks/stats-01/index.d.ts +5 -5
- package/dist/blocks/stats-01/index.d.ts.map +1 -1
- package/dist/blocks/stats-01/index.js +4 -4
- package/dist/blocks/stats-01/stats.d.ts +49 -14
- package/dist/blocks/stats-01/stats.d.ts.map +1 -1
- package/dist/blocks/stats-01/stats.js +65 -15
- package/dist/blocks/trend-01/index.d.ts +1 -1
- package/dist/blocks/trend-01/index.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.d.ts +26 -73
- package/dist/blocks/trend-01/trend.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.js +47 -30
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +93 -3
- package/dist/components/ui/metric.d.ts +4 -2
- package/dist/components/ui/metric.d.ts.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/lib/field-render.d.ts +107 -0
- package/dist/lib/field-render.d.ts.map +1 -0
- package/dist/lib/field-render.js +251 -0
- package/dist/lib/spec.d.ts +609 -0
- package/dist/lib/spec.d.ts.map +1 -0
- package/dist/lib/spec.js +1 -0
- package/dist/live/index.d.ts +2 -0
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js +6 -0
- package/dist/live/message-thread-01/index.d.ts +3 -0
- package/dist/live/message-thread-01/index.d.ts.map +1 -0
- package/dist/live/message-thread-01/index.js +1 -0
- package/dist/live/message-thread-01/message-thread.d.ts +282 -0
- package/dist/live/message-thread-01/message-thread.d.ts.map +1 -0
- package/dist/live/message-thread-01/message-thread.js +221 -0
- package/dist/live/run-stream-01/run-stream.d.ts +2 -1
- package/dist/live/run-stream-01/run-stream.d.ts.map +1 -1
- package/dist/live/run-stream-01/run-stream.js +1 -7
- package/dist/live/status.d.ts +27 -0
- package/dist/live/status.d.ts.map +1 -0
- package/dist/live/status.js +15 -0
- package/dist/live/tool-call-row.d.ts +26 -0
- package/dist/live/tool-call-row.d.ts.map +1 -0
- package/dist/live/tool-call-row.js +60 -0
- package/dist/live/tool-ledger-01/tool-ledger.d.ts.map +1 -1
- package/dist/live/tool-ledger-01/tool-ledger.js +2 -42
- package/dist/pages/contact-page/index.d.ts +7 -7
- package/dist/pages/contact-page/index.d.ts.map +1 -1
- package/dist/pages/contact-page/index.js +17 -5
- package/dist/pages/docs-shell/docs-shell.d.ts +42 -30
- package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
- package/dist/pages/docs-shell/docs-shell.js +70 -48
- package/dist/pages/document-page/document-page.d.ts +146 -0
- package/dist/pages/document-page/document-page.d.ts.map +1 -0
- package/dist/pages/document-page/document-page.js +80 -0
- package/dist/pages/document-page/index.d.ts +3 -0
- package/dist/pages/document-page/index.d.ts.map +1 -0
- package/dist/pages/document-page/index.js +1 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +6 -0
- package/dist/styles.css +59 -3
- package/package.json +7 -2
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
+
import type { RelationSpec } from '../../lib/spec.js';
|
|
4
|
+
import { type EmptyReason } from '../empty-state-01/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* One fact a reader is told about the record, in the caller's own order.
|
|
7
|
+
*
|
|
8
|
+
* `id` is a stable key rather than the words of the label, for the reason every
|
|
9
|
+
* other key in this package gives: it is what a caller keys their own rendering
|
|
10
|
+
* by, and a key built out of a label breaks the moment the label is translated.
|
|
11
|
+
* The value is a node rather than a string because the facts a record carries are
|
|
12
|
+
* the caller's own and they are not all text: a status word, a moment, a link and
|
|
13
|
+
* a composed figure are all things a caller puts in one slot.
|
|
14
|
+
*/
|
|
15
|
+
export type RecordDetail01Field = {
|
|
16
|
+
/** A stable key for the field, so a caller and a test can name one. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** What the fact is, in the product's own words. */
|
|
19
|
+
label: ReactNode;
|
|
20
|
+
/** What it reads. */
|
|
21
|
+
value: ReactNode;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The words a relationship draws in its own frame when it has no members.
|
|
25
|
+
*
|
|
26
|
+
* **A relation declares its own empty words, and the reason is that an empty
|
|
27
|
+
* collection and a reader who has chosen nothing are two different sentences.** A
|
|
28
|
+
* customer with no invoices, an order with no payments and a project with no
|
|
29
|
+
* people are all collections that are empty, narrowed, emptied or hidden, and
|
|
30
|
+
* every one of those is a reason `EmptyState01` was drawn for. The words are the
|
|
31
|
+
* caller's because only the product knows whether the answer is "nothing has ever
|
|
32
|
+
* been here" or "this reader may not see it", and a Block that published one
|
|
33
|
+
* sentence for all four would tell a reader who emptied a collection that their
|
|
34
|
+
* filter matched nothing.
|
|
35
|
+
*
|
|
36
|
+
* `reason` is `EmptyState01`'s own closed union, reused rather than re-spelled,
|
|
37
|
+
* so a relation cannot reach an empty state the Block that draws it does not
|
|
38
|
+
* have.
|
|
39
|
+
*/
|
|
40
|
+
export type RecordDetail01RelationEmpty = {
|
|
41
|
+
/** Which of the four situations this is. */
|
|
42
|
+
reason: EmptyReason;
|
|
43
|
+
/** The headline, in the reader's own words. */
|
|
44
|
+
title: string;
|
|
45
|
+
/** The sentence under the headline, for the part the headline cannot fit. */
|
|
46
|
+
body?: string;
|
|
47
|
+
/** The label of the one action, when the reader has something to do here. */
|
|
48
|
+
actionLabel?: string;
|
|
49
|
+
/** The handler for `actionLabel`, required whenever `actionLabel` is set. */
|
|
50
|
+
onAction?: () => void;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* One relationship of a record, as the detail draws it.
|
|
54
|
+
*
|
|
55
|
+
* **It is the shared `RelationSpec` plus the words its empty frame needs, and the
|
|
56
|
+
* split is deliberate.** The specification carries the data: a stable `key`, the
|
|
57
|
+
* caller's `label`, the `kind`, an optional `href` on the heading with its
|
|
58
|
+
* required `hrefLabel`, an optional `count` that is the caller's own string, and
|
|
59
|
+
* the `members`. The empty words are a drawing rather than data, and the
|
|
60
|
+
* specification module stays plain serialisable data with no reference to a
|
|
61
|
+
* Block, which is why they sit here beside the Block that draws them rather than
|
|
62
|
+
* on the shared type.
|
|
63
|
+
*
|
|
64
|
+
* **Depth is one, and the bound is in `RelationSpec` rather than in this type.**
|
|
65
|
+
* `members` is `readonly unknown[]` and never names `RelationSpec`, so a relation
|
|
66
|
+
* cannot hold a relation. A member is a row, a person, an occurrence or a node in
|
|
67
|
+
* the caller's data, and a related record with its own related records is a graph
|
|
68
|
+
* the consumer walks: a member carries the caller's own `href` when it should
|
|
69
|
+
* open, and the destination is the caller's route.
|
|
70
|
+
*/
|
|
71
|
+
export type RecordDetail01Relation = RelationSpec & {
|
|
72
|
+
/** The words this relation draws when it has no members. */
|
|
73
|
+
empty: RecordDetail01RelationEmpty;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The props a `RecordDetail01` accepts.
|
|
77
|
+
*
|
|
78
|
+
* Every word is the caller's and the Block ships none: no title, no fact, no
|
|
79
|
+
* action label, no relation name, no empty sentence and not one figure. The
|
|
80
|
+
* record's own content arrives as `title`, `eyebrow`, `description`, `fields` and
|
|
81
|
+
* `children`, its own controls arrive as `actions`, and its relationships arrive
|
|
82
|
+
* as one ordered `relations` list. The Block fetches nothing, reads nothing by
|
|
83
|
+
* key and defaults to no record.
|
|
84
|
+
*/
|
|
85
|
+
export type RecordDetail01Props = {
|
|
86
|
+
/**
|
|
87
|
+
* The record's own heading, in the caller's words.
|
|
88
|
+
*
|
|
89
|
+
* Required, because a detail that does not name the record it is showing is a
|
|
90
|
+
* surface a reader has to identify from its fields, and the heading is the one
|
|
91
|
+
* thing the caller already holds when they put the record in the address.
|
|
92
|
+
*/
|
|
93
|
+
title: ReactNode;
|
|
94
|
+
/**
|
|
95
|
+
* A line above the title, such as the record's own key or type.
|
|
96
|
+
*
|
|
97
|
+
* The caller's own words, and it is drawn in the mono face because an
|
|
98
|
+
* identifier is machine notation: it is what a reader pastes into a search box,
|
|
99
|
+
* quotes in a message and cites in a review, and this repository annotates
|
|
100
|
+
* machine-readable values with the mono stack rather than with anything else.
|
|
101
|
+
*/
|
|
102
|
+
eyebrow?: ReactNode;
|
|
103
|
+
/** A sentence under the title, for the part the title cannot fit. */
|
|
104
|
+
description?: ReactNode;
|
|
105
|
+
/**
|
|
106
|
+
* The facts a reader is entitled to be told about the record, in the order they
|
|
107
|
+
* should be read.
|
|
108
|
+
*
|
|
109
|
+
* The caller's list, because a fixed set of fields would decide what a record
|
|
110
|
+
* is made of, which is a claim only the product can make. A record that carries
|
|
111
|
+
* a story point, a sprint, a customer and a severity has four facts this
|
|
112
|
+
* package knows nothing about, and a Block that named four of them would have
|
|
113
|
+
* to be forked to carry the fifth.
|
|
114
|
+
*/
|
|
115
|
+
fields?: readonly RecordDetail01Field[];
|
|
116
|
+
/**
|
|
117
|
+
* The record's own content, composed by the caller.
|
|
118
|
+
*
|
|
119
|
+
* A slot, because what a record shows besides its facts is the caller's
|
|
120
|
+
* composition: a screen that wants a figure composes `Summary01` or `Stats01`
|
|
121
|
+
* here rather than asking this Block to keep a running total, and a screen with
|
|
122
|
+
* prose composes `Prose`. The Block places it and styles nothing inside it.
|
|
123
|
+
*/
|
|
124
|
+
children?: ReactNode;
|
|
125
|
+
/**
|
|
126
|
+
* The controls that act on the record, placed in the record's own band.
|
|
127
|
+
*
|
|
128
|
+
* One optional node the caller wrote, and the Block renders no control of its
|
|
129
|
+
* own. What the screen can do to itself belongs to `PageHeader01`; what can be
|
|
130
|
+
* done to this record belongs beside its identity, and a detail in a split pane
|
|
131
|
+
* has no page header above it. There is no declared action union and no
|
|
132
|
+
* destination arm, so the strongest action beside a record is the caller's, and
|
|
133
|
+
* the confirmation for a destructive one is the caller's own `AlertDialog`
|
|
134
|
+
* composed around their control in this slot.
|
|
135
|
+
*/
|
|
136
|
+
actions?: ReactNode;
|
|
137
|
+
/**
|
|
138
|
+
* The record's relationships, one ordered list, drawn open in the declared
|
|
139
|
+
* order.
|
|
140
|
+
*
|
|
141
|
+
* One list on one frame rather than a card per relation, so a record with four
|
|
142
|
+
* relationships and a record with one read the same way. Every relation is
|
|
143
|
+
* drawn open, because a relation a reader has to ask for is a disclosure, a
|
|
144
|
+
* disclosure is a control, and a control in a Block is the finding
|
|
145
|
+
* `scripts/check-block-controls.mjs` exists to catch.
|
|
146
|
+
*/
|
|
147
|
+
relations?: readonly RecordDetail01Relation[];
|
|
148
|
+
/**
|
|
149
|
+
* Heading level for the record's own heading.
|
|
150
|
+
*
|
|
151
|
+
* Each relation's label sits one level under it, derived rather than asked for,
|
|
152
|
+
* so a Block moved one level deeper carries its relation headings with it.
|
|
153
|
+
*
|
|
154
|
+
* @defaultValue 'h2'
|
|
155
|
+
*/
|
|
156
|
+
headingLevel?: HeadingLevel;
|
|
157
|
+
/** Layout only. Changing a Prism-owned visual property from here is prohibited. */
|
|
158
|
+
className?: string;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* The record detail: one record in full, its relationships drawn open, and its
|
|
162
|
+
* own actions beside its identity.
|
|
163
|
+
*
|
|
164
|
+
* **It is handed the record and it owns no selection.** The consumer put the
|
|
165
|
+
* record in the address; a detail that knew which record it shows would own the
|
|
166
|
+
* selection state the split and the record index already hold, and a Page cannot
|
|
167
|
+
* own it either because a Page imports no router. So `title`, `eyebrow`,
|
|
168
|
+
* `description`, `fields` and `children` are the caller's, the `relations` are
|
|
169
|
+
* the caller's ordered list, and this Block fetches nothing, reads nothing by key
|
|
170
|
+
* and defaults to no record. A record that is gone, one this reader may not see
|
|
171
|
+
* and one whose fetch failed are three causes sharing one address and three
|
|
172
|
+
* different sentences, so whether this Block is drawn at all is the caller's
|
|
173
|
+
* answer.
|
|
174
|
+
*
|
|
175
|
+
* **Relationships are one declared list on one frame, not a card per relation.**
|
|
176
|
+
* Each entry is a `RelationSpec` from `@nanisoft/prism-ui/spec` plus the words its
|
|
177
|
+
* own empty frame needs. They are drawn in the declared order, under one heading
|
|
178
|
+
* level, inside one bounded frame with one rule between them, so a record with
|
|
179
|
+
* four relationships and a record with one read the same way rather than becoming
|
|
180
|
+
* a grid of equal panels. The `kind` names the collection arrangement this
|
|
181
|
+
* package already draws, and a relation the union does not name is the `slot`
|
|
182
|
+
* member, drawn open and placed by the caller.
|
|
183
|
+
*
|
|
184
|
+
* **Depth is one, and the bound is in the type.** `RelationSpec.members` is
|
|
185
|
+
* `readonly unknown[]` and never names `RelationSpec`, so a relation cannot hold
|
|
186
|
+
* a relation and this Block draws one ring of a graph the consumer walks. A
|
|
187
|
+
* descendant is reached by a link rather than by depth: a member carries the
|
|
188
|
+
* caller's own `href` when it should open, and the destination is the caller's
|
|
189
|
+
* route. The Block composes no disclosure anywhere, because a disclosure is a
|
|
190
|
+
* control and a control in a Block is the finding the block controls gate exists
|
|
191
|
+
* to catch, and because a relation behind a disclosure is a record the reader has
|
|
192
|
+
* to choose to find.
|
|
193
|
+
*
|
|
194
|
+
* **The record's actions are the caller's node, placed in the record's own band.**
|
|
195
|
+
* What the screen can do to itself belongs to `PageHeader01`; what can be done to
|
|
196
|
+
* this record belongs beside its identity, and a detail in a split pane has no
|
|
197
|
+
* page header above it. The prop is one optional `ReactNode` and the Block
|
|
198
|
+
* renders no control of its own, so there is no edit, archive or delete button
|
|
199
|
+
* here, and the confirmation for a destructive action is the caller's own
|
|
200
|
+
* `AlertDialog` composed around their control in the same slot. There is no cap
|
|
201
|
+
* on how many actions are placed, because a slot cannot be counted.
|
|
202
|
+
*
|
|
203
|
+
* **A relation with no members draws `EmptyState01` at a reason the caller names,
|
|
204
|
+
* in the relation's own frame.** Every one of that Block's reasons is a statement
|
|
205
|
+
* about a collection, and a relation with no members is exactly that: a customer
|
|
206
|
+
* with no invoices, an order with no payments and a project with no people are
|
|
207
|
+
* all collections that are empty, narrowed, emptied or hidden. `NothingChosen01`
|
|
208
|
+
* is a statement about a reader's pointer at a record in the index beside the
|
|
209
|
+
* pane, which this Block cannot be in, because a Block handed a record was handed
|
|
210
|
+
* the selection with it.
|
|
211
|
+
*
|
|
212
|
+
* **It takes no total, no count and no sum.** A relation's `count` is the
|
|
213
|
+
* caller's own string and is never derived from the members, because a count
|
|
214
|
+
* computed over the members a Block was handed is false the moment the collection
|
|
215
|
+
* is larger than the page. A screen that wants a figure composes `Summary01` or
|
|
216
|
+
* `Stats01` into `children` rather than asking this Block to keep a running
|
|
217
|
+
* total, and a record write form is a Block composed beside or below this one and
|
|
218
|
+
* never a mode of it.
|
|
219
|
+
*
|
|
220
|
+
* It owns no selection, fetch, ordering, paging or filtering of its relations,
|
|
221
|
+
* no navigation and no address, no edit state, no confirmation, toast, retry or
|
|
222
|
+
* undo, and no second pane. It is a server Component: no hook beyond `useId` and
|
|
223
|
+
* no state of its own, so a consumer that passes a client action inside a slot
|
|
224
|
+
* pays for the action and not for the frame. The trail relation is the one client
|
|
225
|
+
* island, because a dated occurrence is drawn by the client `ActivityFeed01`.
|
|
226
|
+
*
|
|
227
|
+
* **It is measured against `IssueDetail01`, which is deprecated in its favour.** An
|
|
228
|
+
* issue detail and a record detail are the same job: a key in the mono face, a state
|
|
229
|
+
* as a tone beside the caller's own words, declared facts, a body and a thread in the
|
|
230
|
+
* caller's order. The issue detail's name is wrong the moment the record is an invoice
|
|
231
|
+
* or a subscription, and a name that has to be wrong about nine tenths of its uses is
|
|
232
|
+
* a name that fails the test that its name survives being wrong, so that Item is
|
|
233
|
+
* deprecated rather than merged or replaced. A reader arriving at either Item arrives
|
|
234
|
+
* at the same answer: a record drawn in full is this Block, whatever the product calls
|
|
235
|
+
* its records.
|
|
236
|
+
*/
|
|
237
|
+
export declare function RecordDetail01({ title, eyebrow, description, fields, children, actions, relations, headingLevel, className, }: RecordDetail01Props): import("react").JSX.Element;
|
|
238
|
+
export default RecordDetail01;
|
|
239
|
+
//# sourceMappingURL=record-detail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-detail.d.ts","sourceRoot":"","sources":["../../../src/blocks/record-detail-01/record-detail.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAK7C,OAAO,EAAyD,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAGtH,OAAO,KAAK,EAA2B,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAG3E,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAElE;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAA;IACV,oDAAoD;IACpD,KAAK,EAAE,SAAS,CAAA;IAChB,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,4CAA4C;IAC5C,MAAM,EAAE,WAAW,CAAA;IACnB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG;IAClD,4DAA4D;IAC5D,KAAK,EAAE,2BAA2B,CAAA;CACnC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;;;OAMG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qEAAqE;IACrE,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;IACvC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAA;IAC7C;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA4HD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,SAAS,EACT,YAAmB,EACnB,SAAS,GACV,EAAE,mBAAmB,+BAgFrB;AAED,eAAe,cAAc,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from 'react';
|
|
3
|
+
import { AvatarGroup } from '../../components/ui/avatar-group.js';
|
|
4
|
+
import { CtaLink } from '../../components/ui/cta-link.js';
|
|
5
|
+
import { ListPanel } from '../../components/ui/list-panel.js';
|
|
6
|
+
import { Section, SectionHeading, childLevel, headingSizeClass } from '../../components/ui/section.js';
|
|
7
|
+
import { Table, TableBody } from '../../components/ui/table.js';
|
|
8
|
+
import { Timeline } from '../../components/ui/timeline.js';
|
|
9
|
+
import { cn } from '../../lib/utils.js';
|
|
10
|
+
import { ActivityFeed01 } from '../activity-feed-01/index.js';
|
|
11
|
+
import { EmptyState01 } from '../empty-state-01/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* The collection arrangements a relation is drawn in, keyed by the kind's
|
|
14
|
+
* lowercase name.
|
|
15
|
+
*
|
|
16
|
+
* The map covers every member of `RelationKind`, so a kind added to the union
|
|
17
|
+
* fails the compiler here until it is drawn. Each arrangement is one of the
|
|
18
|
+
* Items this package already ships, and each takes the relation's `members`
|
|
19
|
+
* as the shape that Item already declares: a `Table` relation's members are
|
|
20
|
+
* the caller's own row nodes, drawn in a `TableBody`; a `ListPanel` relation's
|
|
21
|
+
* members are the caller's own list rows; a `Timeline` relation's members are
|
|
22
|
+
* `TimelineEntry` values, which carry a duration and a state; an
|
|
23
|
+
* `ActivityFeed01` relation's members are `EventSpec` values, which carry a
|
|
24
|
+
* moment and an actor, so a record's event history is drawn as a dated trail
|
|
25
|
+
* and never as an instrument; an `AvatarGroup` relation's members are
|
|
26
|
+
* `{ name, src }` values; and a `slot` relation's members are the caller's own
|
|
27
|
+
* nodes, placed in the order given. The cast is the same one `DataTable01`
|
|
28
|
+
* makes for a cell, and for the same reason: the specification is the weakest
|
|
29
|
+
* type in the module on purpose, and the arrangement is the thing that knows
|
|
30
|
+
* what a member is.
|
|
31
|
+
*
|
|
32
|
+
* The trail is drawn by the client Block `ActivityFeed01`, so the frame stays a
|
|
33
|
+
* server Component and only the trail is a client island. Its own section padding
|
|
34
|
+
* is dropped here because the relation already draws the heading, the count and
|
|
35
|
+
* the frame around it, and its required `empty` is the relation's own empty words,
|
|
36
|
+
* which a relation with no members never reaches because it draws `EmptyState01`
|
|
37
|
+
* instead.
|
|
38
|
+
*/
|
|
39
|
+
const ARRANGEMENT = {
|
|
40
|
+
table: (members, { labelledBy }) => (_jsx(Table, { "aria-labelledby": labelledBy, children: _jsx(TableBody, { children: members }) })),
|
|
41
|
+
listpanel: (members) => (_jsx(ListPanel, { scroll: false, children: _jsx("ul", { className: "divide-border flex flex-col divide-y", children: members }) })),
|
|
42
|
+
timeline: (members, { label }) => (_jsx(Timeline, { entries: members, label: label })),
|
|
43
|
+
activityfeed01: (members, { empty }) => (_jsx(ActivityFeed01, { events: members, empty: empty, className: "py-0" })),
|
|
44
|
+
avatargroup: (members) => (_jsx(AvatarGroup, { avatars: members })),
|
|
45
|
+
slot: (members) => (_jsx("div", { "data-slot": "record-detail-01-slot", className: "flex flex-col gap-4", children: members })),
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* One relationship, drawn open: its heading and count, its optional destination,
|
|
49
|
+
* and either its members in the arrangement the kind names or the caller's empty
|
|
50
|
+
* state in the relation's own frame.
|
|
51
|
+
*
|
|
52
|
+
* The heading level is passed in rather than derived here, so every relation on a
|
|
53
|
+
* record sits at one level under the record's own heading.
|
|
54
|
+
*/
|
|
55
|
+
function Relation({ relation, level }) {
|
|
56
|
+
const headingId = useId();
|
|
57
|
+
const Heading = level;
|
|
58
|
+
// A `Timeline` names its list with a string, so a node label falls back to the
|
|
59
|
+
// relation's own key rather than to a flattened sentence the Block invented.
|
|
60
|
+
const labelText = typeof relation.label === 'string' ? relation.label : relation.key;
|
|
61
|
+
const arrangement = ARRANGEMENT[relation.kind.toLowerCase()];
|
|
62
|
+
return (_jsxs("section", { "data-slot": "record-detail-01-relation", "data-relation": relation.key, "aria-labelledby": headingId, className: "flex flex-col gap-4 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1", children: [_jsx(Heading, { id: headingId, className: cn('font-semibold tracking-tight text-balance', headingSizeClass(level)), children: relation.label }), relation.href === undefined ? null : (_jsx(CtaLink, { "data-slot": "record-detail-01-relation-link", href: relation.href, variant: "ghost", size: "sm", children: relation.hrefLabel }))] }), relation.count === undefined ? null : (_jsx("p", { "data-slot": "record-detail-01-relation-count", className: "text-muted-foreground text-sm tabular-nums", children: relation.count })), relation.members.length === 0 ? (_jsx(EmptyState01, { reason: relation.empty.reason, title: relation.empty.title, body: relation.empty.body, actionLabel: relation.empty.actionLabel, onAction: relation.empty.onAction })) : (arrangement(relation.members, {
|
|
63
|
+
label: labelText,
|
|
64
|
+
labelledBy: headingId,
|
|
65
|
+
empty: relation.empty.title,
|
|
66
|
+
}))] }));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The record detail: one record in full, its relationships drawn open, and its
|
|
70
|
+
* own actions beside its identity.
|
|
71
|
+
*
|
|
72
|
+
* **It is handed the record and it owns no selection.** The consumer put the
|
|
73
|
+
* record in the address; a detail that knew which record it shows would own the
|
|
74
|
+
* selection state the split and the record index already hold, and a Page cannot
|
|
75
|
+
* own it either because a Page imports no router. So `title`, `eyebrow`,
|
|
76
|
+
* `description`, `fields` and `children` are the caller's, the `relations` are
|
|
77
|
+
* the caller's ordered list, and this Block fetches nothing, reads nothing by key
|
|
78
|
+
* and defaults to no record. A record that is gone, one this reader may not see
|
|
79
|
+
* and one whose fetch failed are three causes sharing one address and three
|
|
80
|
+
* different sentences, so whether this Block is drawn at all is the caller's
|
|
81
|
+
* answer.
|
|
82
|
+
*
|
|
83
|
+
* **Relationships are one declared list on one frame, not a card per relation.**
|
|
84
|
+
* Each entry is a `RelationSpec` from `@nanisoft/prism-ui/spec` plus the words its
|
|
85
|
+
* own empty frame needs. They are drawn in the declared order, under one heading
|
|
86
|
+
* level, inside one bounded frame with one rule between them, so a record with
|
|
87
|
+
* four relationships and a record with one read the same way rather than becoming
|
|
88
|
+
* a grid of equal panels. The `kind` names the collection arrangement this
|
|
89
|
+
* package already draws, and a relation the union does not name is the `slot`
|
|
90
|
+
* member, drawn open and placed by the caller.
|
|
91
|
+
*
|
|
92
|
+
* **Depth is one, and the bound is in the type.** `RelationSpec.members` is
|
|
93
|
+
* `readonly unknown[]` and never names `RelationSpec`, so a relation cannot hold
|
|
94
|
+
* a relation and this Block draws one ring of a graph the consumer walks. A
|
|
95
|
+
* descendant is reached by a link rather than by depth: a member carries the
|
|
96
|
+
* caller's own `href` when it should open, and the destination is the caller's
|
|
97
|
+
* route. The Block composes no disclosure anywhere, because a disclosure is a
|
|
98
|
+
* control and a control in a Block is the finding the block controls gate exists
|
|
99
|
+
* to catch, and because a relation behind a disclosure is a record the reader has
|
|
100
|
+
* to choose to find.
|
|
101
|
+
*
|
|
102
|
+
* **The record's actions are the caller's node, placed in the record's own band.**
|
|
103
|
+
* What the screen can do to itself belongs to `PageHeader01`; what can be done to
|
|
104
|
+
* this record belongs beside its identity, and a detail in a split pane has no
|
|
105
|
+
* page header above it. The prop is one optional `ReactNode` and the Block
|
|
106
|
+
* renders no control of its own, so there is no edit, archive or delete button
|
|
107
|
+
* here, and the confirmation for a destructive action is the caller's own
|
|
108
|
+
* `AlertDialog` composed around their control in the same slot. There is no cap
|
|
109
|
+
* on how many actions are placed, because a slot cannot be counted.
|
|
110
|
+
*
|
|
111
|
+
* **A relation with no members draws `EmptyState01` at a reason the caller names,
|
|
112
|
+
* in the relation's own frame.** Every one of that Block's reasons is a statement
|
|
113
|
+
* about a collection, and a relation with no members is exactly that: a customer
|
|
114
|
+
* with no invoices, an order with no payments and a project with no people are
|
|
115
|
+
* all collections that are empty, narrowed, emptied or hidden. `NothingChosen01`
|
|
116
|
+
* is a statement about a reader's pointer at a record in the index beside the
|
|
117
|
+
* pane, which this Block cannot be in, because a Block handed a record was handed
|
|
118
|
+
* the selection with it.
|
|
119
|
+
*
|
|
120
|
+
* **It takes no total, no count and no sum.** A relation's `count` is the
|
|
121
|
+
* caller's own string and is never derived from the members, because a count
|
|
122
|
+
* computed over the members a Block was handed is false the moment the collection
|
|
123
|
+
* is larger than the page. A screen that wants a figure composes `Summary01` or
|
|
124
|
+
* `Stats01` into `children` rather than asking this Block to keep a running
|
|
125
|
+
* total, and a record write form is a Block composed beside or below this one and
|
|
126
|
+
* never a mode of it.
|
|
127
|
+
*
|
|
128
|
+
* It owns no selection, fetch, ordering, paging or filtering of its relations,
|
|
129
|
+
* no navigation and no address, no edit state, no confirmation, toast, retry or
|
|
130
|
+
* undo, and no second pane. It is a server Component: no hook beyond `useId` and
|
|
131
|
+
* no state of its own, so a consumer that passes a client action inside a slot
|
|
132
|
+
* pays for the action and not for the frame. The trail relation is the one client
|
|
133
|
+
* island, because a dated occurrence is drawn by the client `ActivityFeed01`.
|
|
134
|
+
*
|
|
135
|
+
* **It is measured against `IssueDetail01`, which is deprecated in its favour.** An
|
|
136
|
+
* issue detail and a record detail are the same job: a key in the mono face, a state
|
|
137
|
+
* as a tone beside the caller's own words, declared facts, a body and a thread in the
|
|
138
|
+
* caller's order. The issue detail's name is wrong the moment the record is an invoice
|
|
139
|
+
* or a subscription, and a name that has to be wrong about nine tenths of its uses is
|
|
140
|
+
* a name that fails the test that its name survives being wrong, so that Item is
|
|
141
|
+
* deprecated rather than merged or replaced. A reader arriving at either Item arrives
|
|
142
|
+
* at the same answer: a record drawn in full is this Block, whatever the product calls
|
|
143
|
+
* its records.
|
|
144
|
+
*/
|
|
145
|
+
export function RecordDetail01({ title, eyebrow, description, fields, children, actions, relations, headingLevel = 'h2', className, }) {
|
|
146
|
+
const relationLevel = childLevel(headingLevel);
|
|
147
|
+
const list = relations ?? [];
|
|
148
|
+
return (_jsx(Section, { "data-slot": "record-detail-01", className: cn(className), children: _jsxs("div", { className: "flex flex-col gap-10", children: [_jsxs("div", { "data-slot": "record-detail-01-band", className: "flex flex-wrap items-start justify-between gap-x-6 gap-y-4", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [eyebrow === undefined ? null : (_jsx("span", { "data-slot": "record-detail-01-eyebrow", className: "text-muted-foreground font-mono text-sm", children: eyebrow })), _jsx(SectionHeading, { as: headingLevel, align: "left", title: title, description: description, className: "mb-0" })] }), actions === undefined ? null : (_jsx("div", { "data-slot": "record-detail-01-actions", className: "flex shrink-0 flex-wrap items-center gap-2", children: actions }))] }), fields === undefined || fields.length === 0 ? null : (_jsx("dl", { "data-slot": "record-detail-01-fields", className: "grid gap-x-8 gap-y-3 sm:grid-cols-[auto_minmax(0,1fr)]", children: fields.map((field) => (_jsxs("div", { "data-slot": "record-detail-01-field", className: "contents", children: [_jsx("dt", { className: "text-muted-foreground text-sm font-medium", children: field.label }), _jsx("dd", { className: "min-w-0 text-sm", children: field.value })] }, field.id))) })), children === undefined ? null : (_jsx("div", { "data-slot": "record-detail-01-content", className: "flex flex-col gap-10", children: children })), list.length === 0 ? null : (_jsx("div", { "data-slot": "record-detail-01-relations", className: "divide-border divide-y overflow-hidden rounded-xl border", children: list.map((relation) => (_jsx(Relation, { relation: relation, level: relationLevel }, relation.key))) }))] }) }));
|
|
149
|
+
}
|
|
150
|
+
export default RecordDetail01;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/record-form-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACrD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RecordForm01, default } from './record-form.js';
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
+
import type { FieldSpecGroup } from '../../lib/spec.js';
|
|
4
|
+
/**
|
|
5
|
+
* One thing wrong with the form, in the caller's own words.
|
|
6
|
+
*
|
|
7
|
+
* `field` is the `key` of the field the message belongs to, and the key rather
|
|
8
|
+
* than the label because it is what the caller's values and the form's own
|
|
9
|
+
* `FormData` are keyed by: a message matched by a label's words breaks the moment
|
|
10
|
+
* the label is translated. The shape is the one `FormDialog` and `FormWizard`
|
|
11
|
+
* already publish, so this Block mints no third spelling of an issue.
|
|
12
|
+
*/
|
|
13
|
+
export type RecordForm01Issue = {
|
|
14
|
+
/** The `key` of the field this message belongs to. */
|
|
15
|
+
field: string;
|
|
16
|
+
/** The message, in the product's own words. */
|
|
17
|
+
message: string;
|
|
18
|
+
};
|
|
19
|
+
/** The members every arm of the save union shares. */
|
|
20
|
+
type RecordForm01Base = {
|
|
21
|
+
/** The form's own heading, absent for a form that needs none. */
|
|
22
|
+
title?: ReactNode;
|
|
23
|
+
/** One line under the heading saying what submitting this will do. */
|
|
24
|
+
description?: ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* The fields, grouped and ordered.
|
|
27
|
+
*
|
|
28
|
+
* Every field belongs to a group, and a form with no headings is one group with
|
|
29
|
+
* no label. The array's order is the order they are drawn in.
|
|
30
|
+
*/
|
|
31
|
+
groups: readonly FieldSpecGroup[];
|
|
32
|
+
/**
|
|
33
|
+
* How many columns the fields sit in.
|
|
34
|
+
*
|
|
35
|
+
* One at every width by default, and one column again at the narrow breakpoint
|
|
36
|
+
* when two are asked for. There is no per-field layout prop and no slot per
|
|
37
|
+
* region.
|
|
38
|
+
*
|
|
39
|
+
* @defaultValue 1
|
|
40
|
+
*/
|
|
41
|
+
columns?: 1 | 2;
|
|
42
|
+
/** The issues to draw, keyed by field. Drawn under the control that owns them. */
|
|
43
|
+
issues?: readonly RecordForm01Issue[];
|
|
44
|
+
/**
|
|
45
|
+
* A message about the submission rather than about a control.
|
|
46
|
+
*
|
|
47
|
+
* It names no field and marks at most one, which is the arrangement `AuthForm01`
|
|
48
|
+
* argues for: telling a reader that two correctly filled fields are wrong is how
|
|
49
|
+
* they learn to stop trusting the rest of the form.
|
|
50
|
+
*/
|
|
51
|
+
submitError?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Heading level for the form's own heading. See `HeadingLevel`.
|
|
54
|
+
*
|
|
55
|
+
* @defaultValue 'h2'
|
|
56
|
+
*/
|
|
57
|
+
headingLevel?: HeadingLevel;
|
|
58
|
+
/** A secondary control beside the save, such as a cancel link. */
|
|
59
|
+
footerStart?: ReactNode;
|
|
60
|
+
/** Layout only. Changing a Prism-owned visual property from here is prohibited. */
|
|
61
|
+
className?: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The props a `RecordForm01` accepts.
|
|
65
|
+
*
|
|
66
|
+
* **A discriminated union with three arms, at least one required, and each
|
|
67
|
+
* forbidding the other two.** The compiler holds that rule rather than a JSDoc
|
|
68
|
+
* block, so a caller cannot reach a save that activates to nothing and cannot
|
|
69
|
+
* pass a handler beside a form that posts to a URL. `action` posts the form to a
|
|
70
|
+
* URL the caller names; `onSubmit` hands the form's own element to a function and
|
|
71
|
+
* makes the Block a client Component; `submit` is a slot holding the caller's own
|
|
72
|
+
* control, placed unstyled in the footer.
|
|
73
|
+
*/
|
|
74
|
+
export type RecordForm01Props = (RecordForm01Base & {
|
|
75
|
+
/** The URL the form posts to, the browser's own activation. */
|
|
76
|
+
action: string;
|
|
77
|
+
/** The label of the save control. */
|
|
78
|
+
submitLabel: ReactNode;
|
|
79
|
+
onSubmit?: never;
|
|
80
|
+
submit?: never;
|
|
81
|
+
}) | (RecordForm01Base & {
|
|
82
|
+
/** Called with the form element when the reader submits. */
|
|
83
|
+
onSubmit: (form: HTMLFormElement) => void;
|
|
84
|
+
/** The label of the save control. */
|
|
85
|
+
submitLabel: ReactNode;
|
|
86
|
+
action?: never;
|
|
87
|
+
submit?: never;
|
|
88
|
+
}) | (RecordForm01Base & {
|
|
89
|
+
/** The caller's own control, placed unstyled in the footer. */
|
|
90
|
+
submit: ReactNode;
|
|
91
|
+
action?: never;
|
|
92
|
+
onSubmit?: never;
|
|
93
|
+
submitLabel?: never;
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* The record write form: a typed field specification, rendered whole.
|
|
97
|
+
*
|
|
98
|
+
* **It takes a field specification and draws the whole form.** `groups` is an
|
|
99
|
+
* ordered list of `FieldSpecGroup`, each an optional heading, an optional
|
|
100
|
+
* description and an ordered list of `FieldSpec`; every field carries a stable
|
|
101
|
+
* `key`, a required `label`, a required `kind` drawn from Prism's own input
|
|
102
|
+
* vocabulary, and optional help, hint, placeholder, disabled flag and starting
|
|
103
|
+
* value. A field whose control this package does not ship carries the `slot` arm,
|
|
104
|
+
* and the Block still draws the label, the help and the error around it, so a
|
|
105
|
+
* field has the same shell either way. Order is the array's order, and the Block
|
|
106
|
+
* never reorders, hides or drops a field.
|
|
107
|
+
*
|
|
108
|
+
* **Prism owns the shape and the vocabulary; the consumer owns the values, the
|
|
109
|
+
* errors and the fetch.** The specification carries no validator, no constraint
|
|
110
|
+
* object, no schema and no pattern, because evaluating a rule is behaviour and a
|
|
111
|
+
* Block ships none. Requiredness is not validation: it is a rendering fact and an
|
|
112
|
+
* HTML fact, so the Block draws the mark and sets the attribute and never checks a
|
|
113
|
+
* value against it.
|
|
114
|
+
*
|
|
115
|
+
* **The error state arrives as an issue list keyed by field.** An entry is a
|
|
116
|
+
* `field` naming a key and a `message` in the product's own words, the shape
|
|
117
|
+
* `FormDialog` and `FormWizard` already take, and a control's issue is drawn
|
|
118
|
+
* under that control. A message about the submission rather than about a control
|
|
119
|
+
* is `submitError`: it names no field and marks at most one.
|
|
120
|
+
*
|
|
121
|
+
* **Where the submission goes is a union with three arms, at least one
|
|
122
|
+
* required.** `action` posts the form to a URL the caller names. `onSubmit` hands
|
|
123
|
+
* the form's own element to a function and makes the Block a client Component.
|
|
124
|
+
* `submit` is a slot holding the caller's own control, placed unstyled in the
|
|
125
|
+
* footer. Each arm forbids the other two, so the compiler holds the rule rather
|
|
126
|
+
* than this paragraph. The save control is `type="submit"` inside a `<form>` the
|
|
127
|
+
* Block renders, and the Block never wires a save to a handler of its own.
|
|
128
|
+
*
|
|
129
|
+
* **Layout is derived, and the only knob is how many columns the fields sit
|
|
130
|
+
* in.** `columns` is `1 | 2` at the form level; two is the only multi-column form
|
|
131
|
+
* layout in which a label stays above its control, and a two-column form fills
|
|
132
|
+
* across rows rather than down columns, because column-wise filling breaks the
|
|
133
|
+
* reading order a screen reader and a keyboard follow. At the narrow breakpoint a
|
|
134
|
+
* two-column form is one column again. There is no per-field layout prop and no
|
|
135
|
+
* slot per region.
|
|
136
|
+
*
|
|
137
|
+
* **A field whose value is absent renders empty rather than absent**, because a
|
|
138
|
+
* form that changes shape while the reader is halfway through it is a form the
|
|
139
|
+
* reader has to read twice. The plain form is uncontrolled where a Prism control
|
|
140
|
+
* can be, so the caller reads the values out of the form element at submit: the
|
|
141
|
+
* `onSubmit` arm passes that element, and `action` lets the browser post it. The
|
|
142
|
+
* handful of Prism controls the platform will not submit on their own hold their
|
|
143
|
+
* value and carry a hidden input under the field's key, so the payload is
|
|
144
|
+
* complete either way.
|
|
145
|
+
*/
|
|
146
|
+
export declare function RecordForm01(props: RecordForm01Props): import("react").JSX.Element;
|
|
147
|
+
export default RecordForm01;
|
|
148
|
+
//# sourceMappingURL=record-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-form.d.ts","sourceRoot":"","sources":["../../../src/blocks/record-form-01/record-form.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAmC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAKvE,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAS7F,OAAO,KAAK,EAAa,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG/D;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,sDAAsD;AACtD,KAAK,gBAAgB,GAAG;IACtB,iEAAiE;IACjE,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,sEAAsE;IACtE,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;OAKG;IACH,MAAM,EAAE,SAAS,cAAc,EAAE,CAAA;IACjC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACf,kFAAkF;IAClF,MAAM,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACrC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,kEAAkE;IAClE,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GACzB,CAAC,gBAAgB,GAAG;IAClB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAA;IACd,qCAAqC;IACrC,WAAW,EAAE,SAAS,CAAA;IACtB,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB,MAAM,CAAC,EAAE,KAAK,CAAA;CACf,CAAC,GACF,CAAC,gBAAgB,GAAG;IAClB,4DAA4D;IAC5D,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;IACzC,qCAAqC;IACrC,WAAW,EAAE,SAAS,CAAA;IACtB,MAAM,CAAC,EAAE,KAAK,CAAA;IACd,MAAM,CAAC,EAAE,KAAK,CAAA;CACf,CAAC,GACF,CAAC,gBAAgB,GAAG;IAClB,+DAA+D;IAC/D,MAAM,EAAE,SAAS,CAAA;IACjB,MAAM,CAAC,EAAE,KAAK,CAAA;IACd,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB,WAAW,CAAC,EAAE,KAAK,CAAA;CACpB,CAAC,CAAA;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAmKpD;AAED,eAAe,YAAY,CAAA"}
|