@nanisoft/prism-ui 0.16.1 → 1.1.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/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 +35 -3
- package/package.json +7 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The props a NothingChosen01 takes.
|
|
4
|
+
*
|
|
5
|
+
* Every string is a prop and the Block ships none. There is no sentence, no
|
|
6
|
+
* fallback, no reason value and no label for a control the Block does not
|
|
7
|
+
* render, and the absence is the argument rather than an omission: this is the
|
|
8
|
+
* one state in the system whose words are entirely the caller's, because the
|
|
9
|
+
* next step in it is a click in the pane beside this one and the sentence that
|
|
10
|
+
* describes it is a claim about that reader's product, not about this design
|
|
11
|
+
* system.
|
|
12
|
+
*/
|
|
13
|
+
export type NothingChosen01Props = {
|
|
14
|
+
/**
|
|
15
|
+
* The words the reader reads: what is in this pane, and what they do next, in
|
|
16
|
+
* the product's own voice. Required, because a pane with a frame and no words
|
|
17
|
+
* is a box a reader has to interpret, and because the Block's whole job is to
|
|
18
|
+
* carry the caller's sentence and nothing else.
|
|
19
|
+
*
|
|
20
|
+
* "Select an invoice" says what is here and what to do. "Nothing here" says
|
|
21
|
+
* something false, and the failure this Block is shaped to prevent starts with
|
|
22
|
+
* a pane that tells a reader there is nothing to read while a list of records
|
|
23
|
+
* sits beside it.
|
|
24
|
+
*/
|
|
25
|
+
title: string;
|
|
26
|
+
/**
|
|
27
|
+
* The sentence under the words, for the part they cannot fit: what the pane
|
|
28
|
+
* will show, or what the reader should know before they choose. Omit it when
|
|
29
|
+
* the words above are the whole message, which is the ordinary case for a
|
|
30
|
+
* detail pane a reader lands in.
|
|
31
|
+
*/
|
|
32
|
+
body?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The mark above the words, as a slot.
|
|
35
|
+
*
|
|
36
|
+
* A slot and not a mark this Block picks, for the reason `EmptyState01` takes
|
|
37
|
+
* one: the shape that means "choose something" is a claim about the product's
|
|
38
|
+
* records, and four products calling their records four different things would
|
|
39
|
+
* each be handed an icon that is wrong for the other three. It is drawn in the
|
|
40
|
+
* muted ink and sized by the caller, and it is `aria-hidden` wherever a caller
|
|
41
|
+
* puts it, because the state is carried by the words and a mark a screen
|
|
42
|
+
* reader reads is the same sentence twice.
|
|
43
|
+
*/
|
|
44
|
+
icon?: ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* Layout only, exactly as on every Component and Block. Changing a Prism-owned
|
|
47
|
+
* visual property from here is prohibited.
|
|
48
|
+
*/
|
|
49
|
+
className?: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The resting detail pane: a reader has opened a region that can hold a record
|
|
53
|
+
* and has chosen nothing yet.
|
|
54
|
+
*
|
|
55
|
+
* **This is a statement about a reader's pointer, and that is the whole of why it
|
|
56
|
+
* is a Block beside `EmptyState01` rather than a reason on it.** The reasons that
|
|
57
|
+
* Block takes are answers to one question: what happened to the collection this
|
|
58
|
+
* region would have drawn? Nothing has ever existed in it, the reader's own
|
|
59
|
+
* narrowing emptied it, the reader's own earlier action moved every record out of
|
|
60
|
+
* it, or this reader may not see it. Each is a statement about a set, and the
|
|
61
|
+
* frame those four share is the assertion that follows from them together: there
|
|
62
|
+
* is nothing here to read. Nothing chosen is not that. No collection has been
|
|
63
|
+
* emptied, narrowed or hidden, the records are in the index a few centimetres to
|
|
64
|
+
* the left, and what is missing is the reader's pointer at one of them. A reason
|
|
65
|
+
* here would have to keep that frame honest by claiming there is nothing to read
|
|
66
|
+
* here while a readable region sits next to it, which is the failure this Block
|
|
67
|
+
* exists rather than to commit.
|
|
68
|
+
*
|
|
69
|
+
* **The newest of those four is the one that shows the line most sharply, because
|
|
70
|
+
* a region the reader emptied has the same frame and the opposite neighbourhood.**
|
|
71
|
+
* A bin a reader has just cleared has no readable neighbour, so the shared frame
|
|
72
|
+
* is honest there and the reason belongs on `EmptyState01` beside the other three.
|
|
73
|
+
* This pane has a full index beside it, so the same frame would be a claim the
|
|
74
|
+
* reader can see is false. The difference between the two answers is not which is
|
|
75
|
+
* more correct; it is what each one is a statement about.
|
|
76
|
+
*
|
|
77
|
+
* **What it does not draw is half of what it is.** No action, and
|
|
78
|
+
* `scripts/check-block-controls.mjs` is the reason rather than a coincidence: the
|
|
79
|
+
* next step in this state is a click in the other pane, so a control here would be
|
|
80
|
+
* either a button Prism wires to nothing or a second copy of an affordance the
|
|
81
|
+
* index already draws for the same reader. So there is no control in this source
|
|
82
|
+
* at all, the gate has nothing to catch here, and a reader auditing for the
|
|
83
|
+
* absence finds none. No height floor either, and that one is the composition's
|
|
84
|
+
* business rather than the Block's: a pane in a grid is already as tall as its
|
|
85
|
+
* neighbour, and a `min-h` authored for a page region would be a second answer to
|
|
86
|
+
* a question the split's tracks have already answered. So this Block fills the
|
|
87
|
+
* height it is given and centres itself in it, and a caller who wants a floor
|
|
88
|
+
* writes one in the composition where the pane is placed.
|
|
89
|
+
*
|
|
90
|
+
* **No sentence of its own, no fallback and no frame.** The Block ships no text,
|
|
91
|
+
* and it ships no border, no fill and no minimum size either, for the same reason
|
|
92
|
+
* the words are the caller's: a dashed frame is a claim that the region has
|
|
93
|
+
* nothing in it, and this region has everything in it that the index beside it
|
|
94
|
+
* has. What the caller gives this Block is a region, and the words; the frame
|
|
95
|
+
* around them, if there is one, belongs to whatever drew the pane.
|
|
96
|
+
*
|
|
97
|
+
* **The words are a `<p>` and not a heading, for the reason `EmptyState01` draws
|
|
98
|
+
* its headline as one.** The pane persists and its contents do not: what is in it
|
|
99
|
+
* is replaced by a record the moment the reader chooses. A heading here would put
|
|
100
|
+
* an entry in the document outline that disappears on the first click, and a
|
|
101
|
+
* reader navigating by heading would select "Select an invoice" and land where
|
|
102
|
+
* the entry no longer exists. The pane's own claim belongs to the heading the
|
|
103
|
+
* caller's shell already draws above the split.
|
|
104
|
+
*
|
|
105
|
+
* **Emphasis belongs in the pane's content and not in the index's width.** The
|
|
106
|
+
* instinct when a detail pane is empty is to widen the index and let the pane go,
|
|
107
|
+
* and it is refused: a split whose tracks change ratio when a row is clicked moves
|
|
108
|
+
* the list out from under the hand that clicked it. The tracks hold their ratio at
|
|
109
|
+
* every selection state, so what changes is content, and forty rows beside one
|
|
110
|
+
* sentence is not a competition. A caller who wants the index louder makes the
|
|
111
|
+
* index denser, which is the index's own decision to take.
|
|
112
|
+
*
|
|
113
|
+
* **It is a server Component.** It holds no state, imports no client code and
|
|
114
|
+
* renders no control, so a consumer that passes a client node inside it pays for
|
|
115
|
+
* the node and not for this frame.
|
|
116
|
+
*/
|
|
117
|
+
export declare function NothingChosen01({ title, body, icon, className }: NothingChosen01Props): import("react").JSX.Element;
|
|
118
|
+
export default NothingChosen01;
|
|
119
|
+
//# sourceMappingURL=nothing-chosen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nothing-chosen.d.ts","sourceRoot":"","sources":["../../../src/blocks/nothing-chosen-01/nothing-chosen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;;;;;;OAUG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,oBAAoB,+BA+CrF;AAED,eAAe,eAAe,CAAA"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../lib/utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* The resting detail pane: a reader has opened a region that can hold a record
|
|
5
|
+
* and has chosen nothing yet.
|
|
6
|
+
*
|
|
7
|
+
* **This is a statement about a reader's pointer, and that is the whole of why it
|
|
8
|
+
* is a Block beside `EmptyState01` rather than a reason on it.** The reasons that
|
|
9
|
+
* Block takes are answers to one question: what happened to the collection this
|
|
10
|
+
* region would have drawn? Nothing has ever existed in it, the reader's own
|
|
11
|
+
* narrowing emptied it, the reader's own earlier action moved every record out of
|
|
12
|
+
* it, or this reader may not see it. Each is a statement about a set, and the
|
|
13
|
+
* frame those four share is the assertion that follows from them together: there
|
|
14
|
+
* is nothing here to read. Nothing chosen is not that. No collection has been
|
|
15
|
+
* emptied, narrowed or hidden, the records are in the index a few centimetres to
|
|
16
|
+
* the left, and what is missing is the reader's pointer at one of them. A reason
|
|
17
|
+
* here would have to keep that frame honest by claiming there is nothing to read
|
|
18
|
+
* here while a readable region sits next to it, which is the failure this Block
|
|
19
|
+
* exists rather than to commit.
|
|
20
|
+
*
|
|
21
|
+
* **The newest of those four is the one that shows the line most sharply, because
|
|
22
|
+
* a region the reader emptied has the same frame and the opposite neighbourhood.**
|
|
23
|
+
* A bin a reader has just cleared has no readable neighbour, so the shared frame
|
|
24
|
+
* is honest there and the reason belongs on `EmptyState01` beside the other three.
|
|
25
|
+
* This pane has a full index beside it, so the same frame would be a claim the
|
|
26
|
+
* reader can see is false. The difference between the two answers is not which is
|
|
27
|
+
* more correct; it is what each one is a statement about.
|
|
28
|
+
*
|
|
29
|
+
* **What it does not draw is half of what it is.** No action, and
|
|
30
|
+
* `scripts/check-block-controls.mjs` is the reason rather than a coincidence: the
|
|
31
|
+
* next step in this state is a click in the other pane, so a control here would be
|
|
32
|
+
* either a button Prism wires to nothing or a second copy of an affordance the
|
|
33
|
+
* index already draws for the same reader. So there is no control in this source
|
|
34
|
+
* at all, the gate has nothing to catch here, and a reader auditing for the
|
|
35
|
+
* absence finds none. No height floor either, and that one is the composition's
|
|
36
|
+
* business rather than the Block's: a pane in a grid is already as tall as its
|
|
37
|
+
* neighbour, and a `min-h` authored for a page region would be a second answer to
|
|
38
|
+
* a question the split's tracks have already answered. So this Block fills the
|
|
39
|
+
* height it is given and centres itself in it, and a caller who wants a floor
|
|
40
|
+
* writes one in the composition where the pane is placed.
|
|
41
|
+
*
|
|
42
|
+
* **No sentence of its own, no fallback and no frame.** The Block ships no text,
|
|
43
|
+
* and it ships no border, no fill and no minimum size either, for the same reason
|
|
44
|
+
* the words are the caller's: a dashed frame is a claim that the region has
|
|
45
|
+
* nothing in it, and this region has everything in it that the index beside it
|
|
46
|
+
* has. What the caller gives this Block is a region, and the words; the frame
|
|
47
|
+
* around them, if there is one, belongs to whatever drew the pane.
|
|
48
|
+
*
|
|
49
|
+
* **The words are a `<p>` and not a heading, for the reason `EmptyState01` draws
|
|
50
|
+
* its headline as one.** The pane persists and its contents do not: what is in it
|
|
51
|
+
* is replaced by a record the moment the reader chooses. A heading here would put
|
|
52
|
+
* an entry in the document outline that disappears on the first click, and a
|
|
53
|
+
* reader navigating by heading would select "Select an invoice" and land where
|
|
54
|
+
* the entry no longer exists. The pane's own claim belongs to the heading the
|
|
55
|
+
* caller's shell already draws above the split.
|
|
56
|
+
*
|
|
57
|
+
* **Emphasis belongs in the pane's content and not in the index's width.** The
|
|
58
|
+
* instinct when a detail pane is empty is to widen the index and let the pane go,
|
|
59
|
+
* and it is refused: a split whose tracks change ratio when a row is clicked moves
|
|
60
|
+
* the list out from under the hand that clicked it. The tracks hold their ratio at
|
|
61
|
+
* every selection state, so what changes is content, and forty rows beside one
|
|
62
|
+
* sentence is not a competition. A caller who wants the index louder makes the
|
|
63
|
+
* index denser, which is the index's own decision to take.
|
|
64
|
+
*
|
|
65
|
+
* **It is a server Component.** It holds no state, imports no client code and
|
|
66
|
+
* renders no control, so a consumer that passes a client node inside it pays for
|
|
67
|
+
* the node and not for this frame.
|
|
68
|
+
*/
|
|
69
|
+
export function NothingChosen01({ title, body, icon, className }) {
|
|
70
|
+
return (_jsxs("div", { "data-slot": "nothing-chosen", className: cn(
|
|
71
|
+
// Centred in the pane it is given rather than floored to a size of its
|
|
72
|
+
// own. `h-full` takes the height the composition already decided and
|
|
73
|
+
// `justify-center` puts the words in the middle of it, so this Block
|
|
74
|
+
// draws no minimum height and no frame: a pane in a grid is as tall as
|
|
75
|
+
// its neighbour, and a floor authored here would be a second answer to
|
|
76
|
+
// a question the split's tracks have already answered.
|
|
77
|
+
'flex h-full flex-col items-center justify-center gap-3 p-8 text-center', className), children: [icon === undefined ? null : (_jsx("div", { "data-slot": "nothing-chosen-icon", "aria-hidden": true, className: "text-muted-foreground flex-none", children: icon })), _jsx("p", { "data-slot": "nothing-chosen-title", className: "text-base font-semibold text-balance", children: title }), body === undefined ? null : (_jsx("p", { "data-slot": "nothing-chosen-body", className: "text-muted-foreground max-w-measure-narrow text-pretty text-sm", children: body }))] }));
|
|
78
|
+
}
|
|
79
|
+
export default NothingChosen01;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { ProjectDashboard01, default } from './project-dashboard.js';
|
|
2
|
-
export type {
|
|
2
|
+
export type { ProjectDashboard01Span, ProjectDashboard01Panel, ProjectDashboard01Props, } from './project-dashboard.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/project-dashboard-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/project-dashboard-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGjE,YAAY,EACV,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,qBAAqB,CAAA"}
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
-
|
|
4
|
-
* One figure in the row across the top.
|
|
5
|
-
*
|
|
6
|
-
* The same three fields `Project01` takes for its figures, declared separately
|
|
7
|
-
* rather than imported for the reason `ProcessFlow01` gives on `ProcessStage`: a
|
|
8
|
-
* dashboard and a detail header are different claims, and a caller composing one has
|
|
9
|
-
* no reason to take the other's type, and sharing the declaration would invite a
|
|
10
|
-
* caller to widen a dashboard figure by reaching for a detail field. The field names
|
|
11
|
-
* are still the same on purpose, so a consumer with a project model has one model.
|
|
12
|
-
*/
|
|
13
|
-
export type ProjectDashboard01Figure = {
|
|
14
|
-
/** What the figure measures, read under it. */
|
|
15
|
-
label: string;
|
|
16
|
-
/** The figure itself, already formatted by the caller. */
|
|
17
|
-
value: ReactNode;
|
|
18
|
-
/**
|
|
19
|
-
* The change against the previous reading, as a number whose sign is the
|
|
20
|
-
* direction. See `MetricDelta` on the Component.
|
|
21
|
-
*/
|
|
22
|
-
delta?: number;
|
|
23
|
-
};
|
|
3
|
+
import type { MetricSpec } from '../../lib/spec.js';
|
|
24
4
|
/**
|
|
25
5
|
* How much of the grid one panel takes.
|
|
26
6
|
*
|
|
@@ -116,8 +96,18 @@ export type ProjectDashboard01Props = {
|
|
|
116
96
|
state?: string;
|
|
117
97
|
/** The words for that state. Required whenever `state` is given. */
|
|
118
98
|
stateLabel?: string;
|
|
119
|
-
/**
|
|
120
|
-
|
|
99
|
+
/**
|
|
100
|
+
* The figures, in the order a reader should meet them.
|
|
101
|
+
*
|
|
102
|
+
* **The shared `MetricSpec` and not a shape of this Block's own.** `key`,
|
|
103
|
+
* `label` and `value` are required on each figure, `delta` is a number whose sign
|
|
104
|
+
* is the direction, `deltaFormat` is the caller's own words for that change,
|
|
105
|
+
* `hint` carries the period or the caveat, and a `series` and an `href` each travel
|
|
106
|
+
* with the label or the words they cannot be read without. The type is published as
|
|
107
|
+
* `@nanisoft/prism-ui/spec`, so the words a caller learns here are the words on
|
|
108
|
+
* every other figure surface.
|
|
109
|
+
*/
|
|
110
|
+
figures?: MetricSpec[];
|
|
121
111
|
/**
|
|
122
112
|
* The panels, in the order a reader should meet them.
|
|
123
113
|
*
|
|
@@ -172,10 +162,14 @@ export type ProjectDashboard01Props = {
|
|
|
172
162
|
* composed one is not. `Status` owns the dot and the arrangement that matters most on
|
|
173
163
|
* a dashboard: the dot is `aria-hidden` and the words carry the state, so a reader
|
|
174
164
|
* who cannot separate the tones still reads which is which, and a row of states
|
|
175
|
-
* never becomes a row of colours. The
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
165
|
+
* never becomes a row of colours. The figures are the shared `MetricSpec` and not
|
|
166
|
+
* this Block's own shape, so a figure declared for this row is the same figure a
|
|
167
|
+
* metric summary takes, and the formatter travels with it: a figure with a `delta`
|
|
168
|
+
* and no formatter prints the number the caller passed, which is honest and usually
|
|
169
|
+
* not what was wanted, and a figure with a formatter prints the caller's own words.
|
|
170
|
+
* That is the disagreement this migration closes, because the same consumer reading
|
|
171
|
+
* two dashboards built out of this package now sees one shape rather than a formatted
|
|
172
|
+
* change on one screen and a bare `0.12` on another.
|
|
179
173
|
*
|
|
180
174
|
* **A panel's title is a heading one step below the section, derived rather than
|
|
181
175
|
* written, and a panel with no title draws no heading at all.** A dashboard is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-dashboard.d.ts","sourceRoot":"","sources":["../../../src/blocks/project-dashboard-01/project-dashboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"project-dashboard.d.ts","sourceRoot":"","sources":["../../../src/blocks/project-dashboard-01/project-dashboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC,OAAO,EAAuC,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAGpG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAoChD;;;;;;;;;GASG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAgD9D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAA;IACV;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,2DAA2D;IAC3D,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAA;IAClC,iEAAiE;IACjE,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,IAAI,EACJ,OAAO,EACP,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,YAAmB,EACnB,SAAS,GACV,EAAE,uBAAuB,+BA2HzB;AAED,eAAe,kBAAkB,CAAA"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card.js';
|
|
3
|
+
import { CtaLink } from '../../components/ui/cta-link.js';
|
|
3
4
|
import { Metric } from '../../components/ui/metric.js';
|
|
4
5
|
import { Section, SectionHeading, childLevel } from '../../components/ui/section.js';
|
|
6
|
+
import { Sparkline } from '../../components/ui/sparkline.js';
|
|
5
7
|
import { Status } from '../../components/ui/status.js';
|
|
6
8
|
import { cn } from '../../lib/utils.js';
|
|
7
9
|
/**
|
|
@@ -49,6 +51,32 @@ const SPAN = {
|
|
|
49
51
|
half: 'lg:col-span-3',
|
|
50
52
|
third: 'lg:col-span-2',
|
|
51
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* The refusals, as checks, so a tile that would render a link nobody can name is a
|
|
56
|
+
* diagnostic in a console rather than a rendered link.
|
|
57
|
+
*
|
|
58
|
+
* **The type already holds both pairs, and these checks exist for the caller the
|
|
59
|
+
* type never saw.** `MetricSpec` requires a destination's words wherever it has a
|
|
60
|
+
* destination and a series name wherever it has a series, so a typed caller cannot
|
|
61
|
+
* reach either diagnostic. A JavaScript caller can, and the cost of reaching one is
|
|
62
|
+
* a figure that draws a link with no words or a shape no reader can announce, so the
|
|
63
|
+
* same rule is stated once more where a runtime value can be read: the pair rule is
|
|
64
|
+
* the one `Dashboard01` makes.
|
|
65
|
+
*/
|
|
66
|
+
function assertMetrics(metrics) {
|
|
67
|
+
for (const metric of metrics) {
|
|
68
|
+
if ((metric.href === undefined) !== (metric.hrefLabel === undefined)) {
|
|
69
|
+
throw new Error(`ProjectDashboard01: the figure "${metric.key}" declares one of href and hrefLabel without the other, so the ` +
|
|
70
|
+
'figure would carry a link with no words on it, or a name with no link beside it. Pass the words that say ' +
|
|
71
|
+
'what following it does, or omit the href.');
|
|
72
|
+
}
|
|
73
|
+
if ((metric.series === undefined) !== (metric.seriesLabel === undefined)) {
|
|
74
|
+
throw new Error(`ProjectDashboard01: the figure "${metric.key}" declares one of series and seriesLabel without the other, so the ` +
|
|
75
|
+
'figure would be a picture with no name, which a screen reader cannot announce and two shapes in one row ' +
|
|
76
|
+
'cannot be told apart. Pass the series name in the words the product uses, or omit the series.');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
52
80
|
/**
|
|
53
81
|
* One project's overview: the figures across the top, and a grid of panels whose
|
|
54
82
|
* spans the caller declares.
|
|
@@ -89,10 +117,14 @@ const SPAN = {
|
|
|
89
117
|
* composed one is not. `Status` owns the dot and the arrangement that matters most on
|
|
90
118
|
* a dashboard: the dot is `aria-hidden` and the words carry the state, so a reader
|
|
91
119
|
* who cannot separate the tones still reads which is which, and a row of states
|
|
92
|
-
* never becomes a row of colours. The
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
120
|
+
* never becomes a row of colours. The figures are the shared `MetricSpec` and not
|
|
121
|
+
* this Block's own shape, so a figure declared for this row is the same figure a
|
|
122
|
+
* metric summary takes, and the formatter travels with it: a figure with a `delta`
|
|
123
|
+
* and no formatter prints the number the caller passed, which is honest and usually
|
|
124
|
+
* not what was wanted, and a figure with a formatter prints the caller's own words.
|
|
125
|
+
* That is the disagreement this migration closes, because the same consumer reading
|
|
126
|
+
* two dashboards built out of this package now sees one shape rather than a formatted
|
|
127
|
+
* change on one screen and a bare `0.12` on another.
|
|
96
128
|
*
|
|
97
129
|
* **A panel's title is a heading one step below the section, derived rather than
|
|
98
130
|
* written, and a panel with no title draws no heading at all.** A dashboard is
|
|
@@ -119,6 +151,14 @@ export function ProjectDashboard01({ eyebrow, name, summary, state, stateLabel,
|
|
|
119
151
|
'not there. A state a reader can only see is a state nobody can act on, so pass both or neither.');
|
|
120
152
|
}
|
|
121
153
|
const Title = childLevel(headingLevel);
|
|
122
|
-
|
|
154
|
+
if (figures !== undefined && figures.length > 0)
|
|
155
|
+
assertMetrics(figures);
|
|
156
|
+
return (_jsxs(Section, { "data-slot": "project-dashboard-01", className: cn(className), children: [_jsxs("div", { "data-slot": "project-dashboard-head", className: "flex flex-col gap-6", children: [_jsx(SectionHeading, { as: headingLevel, align: "left", eyebrow: eyebrow, title: name, description: summary }), state === undefined ? null : (_jsx("div", { "data-slot": "project-dashboard-state", children: _jsx(Status, { tone: toneOf(state), label: stateLabel }) }))] }), figures === undefined || figures.length === 0 ? null : (_jsx("div", { "data-slot": "project-dashboard-figures", className: "mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-4", children: figures.map((metric) => {
|
|
157
|
+
// The delta's words are the caller's own node, and `Metric` takes a
|
|
158
|
+
// formatter, so the node is placed through one. A delta with no words is
|
|
159
|
+
// left to `Metric`, which prints the number the caller passed.
|
|
160
|
+
const deltaWords = metric.deltaFormat;
|
|
161
|
+
return (_jsxs("div", { "data-slot": "project-dashboard-figure", "data-metric": metric.key, className: "flex flex-col gap-3", children: [_jsx(Metric, { value: metric.value, label: metric.label, delta: metric.delta, deltaFormat: deltaWords === undefined ? undefined : () => deltaWords, hint: metric.hint }), metric.series === undefined ? null : (_jsx("div", { "data-slot": "project-dashboard-sparkline", className: "flex", children: _jsx(Sparkline, { values: metric.series, label: metric.seriesLabel, className: "ms-auto" }) })), metric.href === undefined ? null : (_jsx(CtaLink, { href: metric.href, variant: "ghost", size: "sm", children: metric.hrefLabel }))] }, metric.key));
|
|
162
|
+
}) })), panels === undefined || panels.length === 0 ? null : (_jsx("div", { "data-slot": "project-dashboard-panels", className: "mt-10 grid items-start gap-6 sm:grid-cols-2 lg:grid-cols-6", children: panels.map((panel) => (_jsxs(Card, { "data-slot": "project-dashboard-panel", "data-panel": panel.id, "data-span": panel.span ?? 'half', className: SPAN[panel.span ?? 'half'], children: [panel.title === undefined && panel.toolbar === undefined ? null : (_jsxs(CardHeader, { className: "flex items-center justify-between gap-4", children: [panel.title === undefined ? null : (_jsx(CardTitle, { as: Title, className: "min-w-0", children: panel.title })), panel.toolbar === undefined ? null : (_jsx("div", { "data-slot": "project-dashboard-panel-toolbar", className: "flex shrink-0 items-center gap-2", children: panel.toolbar }))] })), _jsx(CardContent, { className: "min-w-0", children: panel.children })] }, panel.id))) }))] }));
|
|
123
163
|
}
|
|
124
164
|
export default ProjectDashboard01;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Provisioning01, default } from './provisioning.js';
|
|
2
|
-
export type { Provisioning01Props, Provisioning01Status,
|
|
2
|
+
export type { Provisioning01Props, Provisioning01Status, ProvisioningStep, ProvisioningValue, } from './provisioning.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/provisioning-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/provisioning-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAMxD,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA"}
|
|
@@ -1,97 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
-
|
|
4
|
-
* The five controls a provisioning run can ask a reader to fill in.
|
|
5
|
-
*
|
|
6
|
-
* A closed set, and the closure is the point, for the reason `ContactField`
|
|
7
|
-
* gives at full length: the controls are this Block's, so the field declaration is
|
|
8
|
-
* data and Prism supplies none of it. A run that is committed in three steps and
|
|
9
|
-
* four that is committed in one want the same Component, and a provisioning
|
|
10
|
-
* question that needs a date picker or a file, or a set of radio buttons whose
|
|
11
|
-
* options each carry their own description, is a caller's own surface composed
|
|
12
|
-
* beside this one rather than a sixth arm here. The cost is named rather than
|
|
13
|
-
* hidden: a consumer whose step needs a control Prism does not draw cannot reach
|
|
14
|
-
* it through this type, and the answer is a step with no fields plus the control
|
|
15
|
-
* the caller composes into `actions`.
|
|
16
|
-
*/
|
|
17
|
-
export type ProvisioningFieldType = 'text' | 'email' | 'number' | 'select' | 'textarea';
|
|
18
|
-
/**
|
|
19
|
-
* One choice in a `select` field, and nothing else.
|
|
20
|
-
*
|
|
21
|
-
* A native `<option>` holds text and nothing more, which is the whole reason a
|
|
22
|
-
* provisioning step's select is a `NativeSelect` rather than the Base UI one. The
|
|
23
|
-
* two values are a string and its label, so a caller who needs a description
|
|
24
|
-
* beside an option, a count, or anything nested is reaching for a control the
|
|
25
|
-
* platform cannot render, and this Block does not pretend otherwise.
|
|
26
|
-
*/
|
|
27
|
-
export type ProvisioningOption = {
|
|
28
|
-
/** What the run records for this choice, which is a key and not a sentence. */
|
|
29
|
-
value: string;
|
|
30
|
-
/** The words the reader sees, in the product's own language. */
|
|
31
|
-
label: string;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* What every provisioning field carries, whichever control it draws.
|
|
35
|
-
*
|
|
36
|
-
* `required` is a boolean rather than an optional prop because there is no third
|
|
37
|
-
* state worth having: a field is one the reader must fill or one they may leave,
|
|
38
|
-
* and `required?: boolean` leaves a caller who passed nothing unable to say which
|
|
39
|
-
* of the two they meant.
|
|
40
|
-
*
|
|
41
|
-
* The field's value is not here. A run collects what the reader typed and hands
|
|
42
|
-
* the whole record over at the commit, so there is nothing for a caller to hold
|
|
43
|
-
* between two steps: a step is mounted, filled and unmounted, and the values
|
|
44
|
-
* travel in the Block's own state, which is why this is a client Component.
|
|
45
|
-
*/
|
|
46
|
-
type ProvisioningFieldBase = {
|
|
47
|
-
/** The caller's key for this field, and the key the value arrives under. */
|
|
48
|
-
id: string;
|
|
49
|
-
/** The field's visible name, and the accessible name its control announces. */
|
|
50
|
-
label: string;
|
|
51
|
-
/** Whether the reader may commit the run without filling it. */
|
|
52
|
-
required: boolean;
|
|
53
|
-
/**
|
|
54
|
-
* A line under the control, announced with it.
|
|
55
|
-
*
|
|
56
|
-
* A node, because the honest line is sometimes not one sentence: a caller's
|
|
57
|
-
* hint, a link to the thing being asked about, a piece of the page's own copy.
|
|
58
|
-
* It is wired to the control through `aria-describedby` rather than drawn as a
|
|
59
|
-
* footnote, so a screen reader hears what this field is for as they reach it.
|
|
60
|
-
*/
|
|
61
|
-
description?: ReactNode;
|
|
62
|
-
/** What the control shows while it is empty. A hint, not a name. */
|
|
63
|
-
placeholder?: string;
|
|
64
|
-
/** The platform's own autofill hint for this field. */
|
|
65
|
-
autoComplete?: string;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* One field of the caller's run, and the control it draws.
|
|
69
|
-
*
|
|
70
|
-
* **A union and not one type with an optional `options`, and that is the shape
|
|
71
|
-
* of the whole argument in this Block.** A run's fields are the caller's
|
|
72
|
-
* declaration and not this package's opinion about what a reader may be asked,
|
|
73
|
-
* and the one place that shows is the select: a select with no options is a
|
|
74
|
-
* control with nothing in it, which is a question no reader can answer and looks
|
|
75
|
-
* answered only because the control is there and focusable. A `options?: string[]`
|
|
76
|
-
* on one type would typecheck the defect and leave only a diagnostic at run time
|
|
77
|
-
* to catch it, which is one gate too late. The union is the same mechanism
|
|
78
|
-
* `HeroAction` uses for the same reason: the shape that requires something
|
|
79
|
-
* declares it, and the shape that forbids it says so in the type rather than in
|
|
80
|
-
* prose.
|
|
81
|
-
*
|
|
82
|
-
* The cost of the union is that a caller building a field list generically, from
|
|
83
|
-
* data, has to narrow before it can type the result, and the answer to that is a
|
|
84
|
-
* narrowing function in the caller's own code rather than a looser type here.
|
|
85
|
-
*/
|
|
86
|
-
export type ProvisioningField = (ProvisioningFieldBase & {
|
|
87
|
-
type: 'select';
|
|
88
|
-
/** The choices, in the order the reader should meet them. */
|
|
89
|
-
options: readonly ProvisioningOption[];
|
|
90
|
-
}) | (ProvisioningFieldBase & {
|
|
91
|
-
type: 'text' | 'email' | 'number' | 'textarea';
|
|
92
|
-
/** A select with no options is a question nobody can answer, so it is not one here. */
|
|
93
|
-
options?: never;
|
|
94
|
-
});
|
|
3
|
+
import type { FieldSpec } from '../../lib/spec.js';
|
|
95
4
|
/**
|
|
96
5
|
* One step of a run: what it is called, what happens there, and what the reader is
|
|
97
6
|
* asked for while they are on it.
|
|
@@ -119,12 +28,15 @@ export type ProvisioningStep = {
|
|
|
119
28
|
/**
|
|
120
29
|
* What the reader is asked for on this step, in the order they should meet it.
|
|
121
30
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
31
|
+
* The shared `FieldSpec`, so a step's fields use the vocabulary a record write
|
|
32
|
+
* form uses: a stable `key`, a required `label` and a required `kind` drawn from
|
|
33
|
+
* Prism's own controls, with the options a choice carries under that kind and a
|
|
34
|
+
* `slot` arm for a control this package does not ship. A list and not a fixed
|
|
35
|
+
* set, for the reason `Contact01` states in full: a provisioning run with a
|
|
36
|
+
* fixed set of fields is a run that decides what a reader may be asked, and the
|
|
37
|
+
* decision is invisible in a review because the rendered form looks complete.
|
|
126
38
|
*/
|
|
127
|
-
fields: readonly
|
|
39
|
+
fields: readonly FieldSpec[];
|
|
128
40
|
};
|
|
129
41
|
/**
|
|
130
42
|
* Everything the reader typed, keyed by the caller's own field ids.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provisioning.d.ts","sourceRoot":"","sources":["../../../src/blocks/provisioning-01/provisioning.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"provisioning.d.ts","sourceRoot":"","sources":["../../../src/blocks/provisioning-01/provisioning.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtC,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAA;AAUpC,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1D;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAA;IACV,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;;;OAUG;IACH,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;CAC7B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEtD;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,gFAAgF;IAChF,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IAC5C,4DAA4D;IAC5D,OAAO,EAAE,SAAS,CAAA;CACnB,CAAA;AAED,mFAAmF;AACnF,MAAM,MAAM,mBAAmB,GAAG;IAChC,mEAAmE;IACnE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,gFAAgF;IAChF,KAAK,EAAE,SAAS,CAAA;IAChB,kFAAkF;IAClF,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;OAOG;IACH,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAClC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC7C;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,iEAAiE;IACjE,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA2GD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,OAAW,EACX,MAAM,EACN,MAAM,EACN,SAAS,EACT,SAAS,EACT,YAAY,EACZ,SAAS,EACT,KAAK,EACL,MAAM,EACN,YAAmB,EACnB,SAAS,GACV,EAAE,mBAAmB,+BAuNrB;AAED,eAAe,cAAc,CAAA"}
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId, useState } from 'react';
|
|
4
4
|
import { Button } from '../../components/ui/button.js';
|
|
5
|
-
import { Field, FieldDescription
|
|
6
|
-
import {
|
|
5
|
+
import { Field, FieldDescription } from '../../components/ui/field.js';
|
|
6
|
+
import { Label } from '../../components/ui/label.js';
|
|
7
7
|
import { LiveRegion } from '../../components/ui/live-region.js';
|
|
8
|
-
import { NativeSelect } from '../../components/ui/native-select.js';
|
|
9
8
|
import { Section, SectionHeading, childLevel, } from '../../components/ui/section.js';
|
|
10
9
|
import { Steps } from '../../components/ui/steps.js';
|
|
11
|
-
import {
|
|
10
|
+
import { CHOICE_KINDS, fieldText, renderControl, serialize, SELF_LABELLED, } from '../../lib/field-render.js';
|
|
12
11
|
import { cn } from '../../lib/utils.js';
|
|
13
12
|
/**
|
|
14
13
|
* Whether a caller-supplied string never arrived, arrived empty, or arrived as
|
|
@@ -56,13 +55,14 @@ function assertRun(steps, backLabel, nextLabel, confirmLabel) {
|
|
|
56
55
|
}
|
|
57
56
|
for (const step of steps) {
|
|
58
57
|
for (const field of step.fields) {
|
|
59
|
-
if (blank(field.label)) {
|
|
60
|
-
throw new Error(`Provisioning01: the field "${field.
|
|
58
|
+
if (blank(typeof field.label === 'string' ? field.label : undefined)) {
|
|
59
|
+
throw new Error(`Provisioning01: the field "${field.key}" on the step "${step.id}" declares no label, so the ` +
|
|
61
60
|
'control would be announced with no name and two fields on this run would be announced ' +
|
|
62
61
|
'identically. Every control this Block draws is named by its label, so there is no fallback.');
|
|
63
62
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
63
|
+
if (CHOICE_KINDS.has(field.kind) &&
|
|
64
|
+
blankList(field.options)) {
|
|
65
|
+
throw new Error(`Provisioning01: the field "${field.key}" on the step "${step.id}" is a choice and passes no ` +
|
|
66
66
|
'options, so it would render a control with nothing in it, which is a question no reader can ' +
|
|
67
67
|
'answer. Pass the choices, or declare the field as a text field and check the answer in your ' +
|
|
68
68
|
'own handler.');
|
|
@@ -186,6 +186,7 @@ export function Provisioning01({ eyebrow, title, description, steps, initial = 0
|
|
|
186
186
|
const generated = useId();
|
|
187
187
|
const [at, setAt] = useState(() => stepAt(steps, initial));
|
|
188
188
|
const [values, setValues] = useState({});
|
|
189
|
+
const setValue = (key, value) => setValues((held) => ({ ...held, [key]: value }));
|
|
189
190
|
assertRun(steps, backLabel, nextLabel, confirmLabel);
|
|
190
191
|
// Read once, on mount, so a caller whose steps change mid-run is not moved under
|
|
191
192
|
// a reader who is typing. See `initial` for the argument.
|
|
@@ -210,11 +211,23 @@ export function Provisioning01({ eyebrow, title, description, steps, initial = 0
|
|
|
210
211
|
const StepHeading = childLevel(headingLevel);
|
|
211
212
|
return (_jsx(Section, { "data-slot": "provisioning-01", className: cn(className), children: _jsxs("div", { "data-slot": "provisioning-01-body", className: "flex flex-col gap-8", children: [_jsx(SectionHeading, { as: headingLevel, align: "left", eyebrow: eyebrow, title: title, description: description }), _jsx("div", { "data-slot": "provisioning-01-rail", className: "border-border border-b pb-8", children: _jsx(Steps, { steps: rail, current: index }) }), _jsxs("form", { "data-slot": "provisioning-01-form", className: "flex w-full max-w-measure flex-col gap-6", onSubmit: (event) => {
|
|
212
213
|
event.preventDefault();
|
|
213
|
-
onConfirm(values);
|
|
214
|
-
}, children: [_jsx(StepHeading, { "data-slot": "provisioning-01-step", className: "text-lg font-semibold tracking-tight", children: step.name }), step.description === undefined ? null : (_jsx("div", { "data-slot": "provisioning-01-step-description", className: "text-muted-foreground text-pretty text-sm", children: step.description })), step.fields.length === 0 ? null : (_jsx("div", { "data-slot": "provisioning-01-fields", className: "flex flex-col gap-4", children: step.fields.map((field) => {
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
|
|
214
|
+
onConfirm(Object.fromEntries(Object.entries(values).map(([key, value]) => [key, serialize(value)])));
|
|
215
|
+
}, children: [_jsx(StepHeading, { "data-slot": "provisioning-01-step", className: "text-lg font-semibold tracking-tight", children: step.name }), step.description === undefined ? null : (_jsx("div", { "data-slot": "provisioning-01-step-description", className: "text-muted-foreground text-pretty text-sm", children: step.description })), step.fields.length === 0 ? null : (_jsx("div", { "data-slot": "provisioning-01-fields", className: "flex flex-col gap-4", children: step.fields.map((field, fieldIndex) => {
|
|
216
|
+
const id = `${generated}-${index}-${fieldIndex}`;
|
|
217
|
+
const labelId = `${id}-label`;
|
|
218
|
+
const helpId = field.help === undefined ? undefined : `${id}-help`;
|
|
219
|
+
const kind = field.kind.toLowerCase();
|
|
220
|
+
const ctx = {
|
|
221
|
+
id,
|
|
222
|
+
labelId,
|
|
223
|
+
describedBy: helpId,
|
|
224
|
+
invalid: false,
|
|
225
|
+
text: fieldText(field),
|
|
226
|
+
values,
|
|
227
|
+
setValue,
|
|
228
|
+
controlled: true,
|
|
229
|
+
};
|
|
230
|
+
return (_jsxs(Field, { "data-slot": "provisioning-01-field", "data-field": field.kind, children: [SELF_LABELLED.has(kind) ? null : (_jsx(Label, { id: labelId, htmlFor: id, required: field.required, disabled: field.disabled, children: field.label })), renderControl(field, ctx), field.help === undefined ? null : (_jsx(FieldDescription, { id: helpId, children: field.help }))] }, field.key));
|
|
218
231
|
}) })), terms === undefined || !last ? null : (_jsx("p", { "data-slot": "provisioning-01-terms", className: "text-muted-foreground text-pretty text-sm", children: terms })), _jsxs("div", { "data-slot": "provisioning-01-controls", className: "flex flex-col gap-3 sm:flex-row-reverse sm:items-center sm:justify-start", children: [last ? (_jsx(Button, { "data-slot": "provisioning-01-confirm", type: "submit", disabled: working || done, className: "sm:order-2", children: confirmLabel })) : (_jsx(Button, { "data-slot": "provisioning-01-next", type: "button", disabled: working, className: "sm:order-2", onClick: () => {
|
|
219
232
|
const to = steps[index + 1];
|
|
220
233
|
if (to === undefined)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/record-detail-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACzD,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RecordDetail01, default } from './record-detail.js';
|