@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
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useId } from 'react';
|
|
3
|
+
import { useId, useState } from 'react';
|
|
4
4
|
import { Button } from '../../components/ui/button.js';
|
|
5
5
|
import { CtaLink } from '../../components/ui/cta-link.js';
|
|
6
|
-
import { Field, FieldDescription,
|
|
7
|
-
import {
|
|
6
|
+
import { Field, FieldDescription, FieldError, FieldGroup } from '../../components/ui/field.js';
|
|
7
|
+
import { Label } from '../../components/ui/label.js';
|
|
8
8
|
import { LiveRegion } from '../../components/ui/live-region.js';
|
|
9
|
-
import { NativeSelect } from '../../components/ui/native-select.js';
|
|
10
9
|
import { Section, SectionHeading } from '../../components/ui/section.js';
|
|
11
|
-
import {
|
|
10
|
+
import { CHOICE_KINDS, fieldText, NEEDS_HIDDEN, 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
|
|
@@ -41,19 +40,27 @@ function blankList(value) {
|
|
|
41
40
|
*
|
|
42
41
|
* Both reach a developer in a console rather than a reader, which is what makes
|
|
43
42
|
* them refusals rather than copy: the message says what was passed and what to
|
|
44
|
-
* pass instead, and neither of those sentences is ever shown to a reader.
|
|
43
|
+
* pass instead, and neither of those sentences is ever shown to a reader. The
|
|
44
|
+
* type catches both for a TypeScript caller, and the check is here for the
|
|
45
|
+
* JavaScript caller and for the value that came out of a database with the
|
|
46
|
+
* type's guarantee already gone.
|
|
45
47
|
*/
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
function assertGroups(groups) {
|
|
49
|
+
for (const group of groups) {
|
|
50
|
+
for (const field of group.fields) {
|
|
51
|
+
if (blank(typeof field.label === 'string' ? field.label : undefined)) {
|
|
52
|
+
throw new Error(`Contact01: the field "${field.key}" declares no label, so the control would be announced with no name and ` +
|
|
53
|
+
'two fields on this page would be announced identically. Every control this Block draws is named by its ' +
|
|
54
|
+
'label, so there is no fallback to fall back to.');
|
|
55
|
+
}
|
|
56
|
+
if (CHOICE_KINDS.has(field.kind) &&
|
|
57
|
+
blankList(field.options)) {
|
|
58
|
+
throw new Error(`Contact01: the field "${field.key}" is a choice and passes no options, so it would render a control with ` +
|
|
59
|
+
'nothing in it, which is a question no reader can answer and looks answered only because the control is ' +
|
|
60
|
+
'there and focusable. Pass the choices, or declare the field as a text field and check the answer in your ' +
|
|
61
|
+
'own handler.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
66
|
/**
|
|
@@ -156,34 +163,56 @@ function assertField(field) {
|
|
|
156
163
|
* nothing. The rest of the rendering is a section and five controls, which is the
|
|
157
164
|
* price of the argument above and not a claim about what else this Block might do.
|
|
158
165
|
*/
|
|
159
|
-
export function Contact01({ eyebrow, title, description, onSubmit,
|
|
166
|
+
export function Contact01({ eyebrow, title, description, onSubmit, groups, issues, submitLabel, consent, address, variant = 'split', status, headingLevel = 'h2', className, }) {
|
|
160
167
|
const generated = useId();
|
|
161
168
|
const sending = status?.state === 'sending';
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
const [values, setValues] = useState({});
|
|
170
|
+
const setValue = (key, value) => setValues((previous) => ({ ...previous, [key]: value }));
|
|
171
|
+
assertGroups(groups);
|
|
172
|
+
const fields = groups.flatMap((group) => group.fields);
|
|
164
173
|
/*
|
|
165
174
|
* The values are read out of the form rather than out of any prop, because a
|
|
166
175
|
* contact form is uncontrolled by nature: there are as many fields as the caller
|
|
167
|
-
* declared and no state to hold them in. The keys are the caller's own
|
|
168
|
-
* the record that arrives is shaped like the declaration that produced
|
|
169
|
-
* caller that renames a field in one place renames it in one place.
|
|
176
|
+
* declared and no state to hold them in. The keys are the caller's own field
|
|
177
|
+
* `key`s, so the record that arrives is shaped like the declaration that produced
|
|
178
|
+
* it, and a caller that renames a field in one place renames it in one place. The
|
|
179
|
+
* handful of Prism controls the platform will not submit on their own hold their
|
|
180
|
+
* value here and carry a hidden input under the field's key.
|
|
170
181
|
*/
|
|
171
182
|
const handleSubmit = (event) => {
|
|
172
183
|
event.preventDefault();
|
|
173
184
|
const read = new FormData(event.currentTarget);
|
|
174
185
|
const next = {};
|
|
175
186
|
for (const field of fields) {
|
|
176
|
-
const one = read.get(field.
|
|
187
|
+
const one = read.get(field.key);
|
|
177
188
|
if (typeof one === 'string')
|
|
178
|
-
next[field.
|
|
189
|
+
next[field.key] = one;
|
|
179
190
|
}
|
|
180
191
|
onSubmit(next);
|
|
181
192
|
};
|
|
182
|
-
return (_jsx(Section, { "data-slot": "contact-01", className: cn(className), children: _jsxs("div", { "data-slot": "contact-01-body", className: "flex flex-col gap-8", children: [_jsx(SectionHeading, { eyebrow: eyebrow, title: title, description: description, align: "left", as: headingLevel }), _jsxs("div", { "data-slot": "contact-01-columns", "data-variant": variant, className: cn('grid items-start gap-10', variant === 'split' ? 'lg:grid-cols-[minmax(0,3fr)_minmax(0,2fr)] lg:gap-14' : null), children: [_jsxs("form", { "data-slot": "contact-01-form", onSubmit: handleSubmit, className: "flex w-full max-w-measure flex-col gap-4", children: [fields.map((field) => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
return (_jsx(Section, { "data-slot": "contact-01", className: cn(className), children: _jsxs("div", { "data-slot": "contact-01-body", className: "flex flex-col gap-8", children: [_jsx(SectionHeading, { eyebrow: eyebrow, title: title, description: description, align: "left", as: headingLevel }), _jsxs("div", { "data-slot": "contact-01-columns", "data-variant": variant, className: cn('grid items-start gap-10', variant === 'split' ? 'lg:grid-cols-[minmax(0,3fr)_minmax(0,2fr)] lg:gap-14' : null), children: [_jsxs("form", { "data-slot": "contact-01-form", onSubmit: handleSubmit, className: "flex w-full max-w-measure flex-col gap-4", children: [groups.map((group, groupIndex) => (_jsxs(FieldGroup, { "data-slot": "contact-01-group", className: "flex flex-col gap-4", children: [group.label === undefined ? null : (_jsx("p", { "data-slot": "contact-01-group-label", className: "text-foreground text-sm font-medium", children: group.label })), group.description === undefined ? null : (_jsx("p", { "data-slot": "contact-01-group-description", className: "text-muted-foreground text-sm", children: group.description })), group.fields.map((field, fieldIndex) => {
|
|
194
|
+
const id = `${generated}-${groupIndex}-${fieldIndex}`;
|
|
195
|
+
const labelId = `${id}-label`;
|
|
196
|
+
const helpId = field.help === undefined ? undefined : `${id}-help`;
|
|
197
|
+
const fieldIssues = (issues ?? []).filter((issue) => issue.field === field.key);
|
|
198
|
+
const errorId = fieldIssues.length > 0 ? `${id}-error` : undefined;
|
|
199
|
+
const describedBy = helpId === undefined
|
|
200
|
+
? errorId
|
|
201
|
+
: errorId === undefined
|
|
202
|
+
? helpId
|
|
203
|
+
: `${helpId} ${errorId}`;
|
|
204
|
+
const kind = field.kind.toLowerCase();
|
|
205
|
+
const ctx = {
|
|
206
|
+
id,
|
|
207
|
+
labelId,
|
|
208
|
+
describedBy,
|
|
209
|
+
invalid: fieldIssues.length > 0,
|
|
210
|
+
text: fieldText(field),
|
|
211
|
+
values,
|
|
212
|
+
setValue,
|
|
213
|
+
};
|
|
214
|
+
return (_jsxs(Field, { "data-slot": "contact-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), NEEDS_HIDDEN.has(kind) ? (_jsx("input", { type: "hidden", name: field.key, value: serialize(values[field.key] ?? field.defaultValue) })) : null, field.help === undefined ? null : (_jsx(FieldDescription, { id: helpId, children: field.help })), fieldIssues.length === 0 ? null : (_jsx(FieldError, { id: errorId, children: fieldIssues.map((issue, issueIndex) => (_jsx("span", { "data-slot": "contact-01-issue", children: issue.message }, issueIndex))) }))] }, field.key));
|
|
215
|
+
})] }, group.id ?? groupIndex))), consent === undefined ? null : (_jsx("p", { "data-slot": "contact-01-consent", className: "text-muted-foreground text-sm", children: consent })), _jsxs("div", { "data-slot": "contact-01-actions", className: "flex flex-col gap-3", children: [_jsx(Button, { "data-slot": "contact-01-submit", type: "submit", disabled: sending, className: "self-start", children: submitLabel }), _jsx(LiveRegion, { politeness: status?.state === 'error' ? 'assertive' : 'polite', busy: sending, className: "text-sm", children: status?.message })] })] }), address === undefined ? null : (
|
|
187
216
|
/*
|
|
188
217
|
* An `address` element, because that is what this is: contact
|
|
189
218
|
* information for the organisation, which is what the element names and
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Contact01, default } from './contact.js';
|
|
2
|
-
export type { Contact01Address,
|
|
2
|
+
export type { Contact01Address, Contact01Issue, Contact01Props, Contact01Status, ContactValue, } from './contact.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/contact-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/contact-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAO9C,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,EACf,YAAY,GACb,MAAM,WAAW,CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
+
import type { MetricSpec } from '../../lib/spec.js';
|
|
3
4
|
/**
|
|
4
5
|
* How much of the six-track panel grid one panel takes.
|
|
5
6
|
*
|
|
@@ -13,64 +14,6 @@ import { type HeadingLevel } from '../../components/ui/section.js';
|
|
|
13
14
|
* full width under a row of thirds, two thirds above a row of halves.
|
|
14
15
|
*/
|
|
15
16
|
export type Dashboard01Span = 'full' | 'half' | 'third';
|
|
16
|
-
/**
|
|
17
|
-
* One figure at the top of an overview, and the four optional things a figure
|
|
18
|
-
* comes with.
|
|
19
|
-
*
|
|
20
|
-
* `label` and `value` are the two that make a figure a figure. The rest are
|
|
21
|
-
* annotation, and a metric with no annotation renders the two it has rather than a
|
|
22
|
-
* card with a line of empty space where the annotation would have been, which is
|
|
23
|
-
* the rule every optional field on this type follows.
|
|
24
|
-
*
|
|
25
|
-
* `deltaFormat` is here for the reason `Metric` refuses to guess: a delta of `0.12`
|
|
26
|
-
* is twelve percent, twelve cents or twelve milliseconds, and a Component that
|
|
27
|
-
* attached a percent sign to it would be claiming a caller's units. The cost of the
|
|
28
|
-
* refusal is that a caller who passes a fraction and no formatter gets `0.12`
|
|
29
|
-
* printed, which is honest and almost never what was wanted, so the formatter is a
|
|
30
|
-
* prop here and the alternative was a Block guessing on every consumer's behalf.
|
|
31
|
-
*/
|
|
32
|
-
export type Dashboard01Metric = {
|
|
33
|
-
/** What the figure measures, read under the figure rather than above it. */
|
|
34
|
-
label: string;
|
|
35
|
-
/** The figure itself, in the caller's own units and the caller's own formatting. */
|
|
36
|
-
value: ReactNode;
|
|
37
|
-
/**
|
|
38
|
-
* The change against the previous reading, where the sign is the direction.
|
|
39
|
-
*
|
|
40
|
-
* A `number` and not an object with a `direction`, for the reason `Metric` states
|
|
41
|
-
* in full: a number has exactly one sign, and a shape with both lets a caller put
|
|
42
|
-
* a rising mark on a falling reading.
|
|
43
|
-
*/
|
|
44
|
-
delta?: number;
|
|
45
|
-
/** The words for the delta, given the number. See the note on this type. */
|
|
46
|
-
deltaFormat?: (value: number) => string;
|
|
47
|
-
/** The line under the label: the period, the source, the caveat that matters. */
|
|
48
|
-
hint?: ReactNode;
|
|
49
|
-
/** The readings behind the figure, drawn small beside it. See `sparklineLabel`. */
|
|
50
|
-
sparkline?: number[];
|
|
51
|
-
/**
|
|
52
|
-
* The name of the series the sparkline draws, and the only thing a screen reader
|
|
53
|
-
* reads before the numbers.
|
|
54
|
-
*
|
|
55
|
-
* Required whenever `sparkline` is set, and the pair is checked together rather
|
|
56
|
-
* than left to the Component. `Sparkline` needs a name for the same reason `Chart`
|
|
57
|
-
* does: two sparklines in a row of four are two pictures a reader cannot tell
|
|
58
|
-
* apart, and a picture with no name is not announced and not linkable. Requiring
|
|
59
|
-
* it at the Block's own boundary rather than letting the Component throw means the
|
|
60
|
-
* diagnostic names the field the caller has to add.
|
|
61
|
-
*/
|
|
62
|
-
sparklineLabel?: string;
|
|
63
|
-
/** Where the figure is kept. Its presence makes the tile carry a link. */
|
|
64
|
-
href?: string;
|
|
65
|
-
/**
|
|
66
|
-
* The words on the link, and required whenever `href` is.
|
|
67
|
-
*
|
|
68
|
-
* A link whose only words are the figure itself tells a reader nothing about what
|
|
69
|
-
* following it does, and the same rule holds in every Block in this package that
|
|
70
|
-
* draws a link.
|
|
71
|
-
*/
|
|
72
|
-
hrefLabel?: string;
|
|
73
|
-
};
|
|
74
17
|
/**
|
|
75
18
|
* One panel in the grid: an identifier, how much width it takes, and the caller's
|
|
76
19
|
* own content.
|
|
@@ -130,13 +73,22 @@ export type Dashboard01Props = {
|
|
|
130
73
|
*/
|
|
131
74
|
header?: ReactNode;
|
|
132
75
|
/**
|
|
133
|
-
* The figures along the top,
|
|
76
|
+
* The figures along the top, as the shared `MetricSpec` typed data, in the order
|
|
77
|
+
* a reader should meet them.
|
|
78
|
+
*
|
|
79
|
+
* **It is the shared specification and not a shape of this Block's own.** `key`,
|
|
80
|
+
* `label` and `value` are required on each figure, `delta` is a number whose sign
|
|
81
|
+
* is the direction, `deltaFormat` is the caller's own words for that change,
|
|
82
|
+
* `hint` carries the period or the caveat, and a `series` and an `href` each
|
|
83
|
+
* travel with the label or the words they cannot be read without. The type is
|
|
84
|
+
* published as `@nanisoft/prism-ui/spec` so the words a caller learns on this row
|
|
85
|
+
* are the words on every other figure surface.
|
|
134
86
|
*
|
|
135
87
|
* Order is the caller's because it is a claim about what matters first. A row
|
|
136
88
|
* that sorted itself by size or by name would be making that claim on the
|
|
137
89
|
* caller's behalf, and on a dashboard the claim is the whole point.
|
|
138
90
|
*/
|
|
139
|
-
metrics?:
|
|
91
|
+
metrics?: MetricSpec[];
|
|
140
92
|
/** The panels, in the order a reader should meet them. See `Dashboard01Panel`. */
|
|
141
93
|
panels?: Dashboard01Panel[];
|
|
142
94
|
/**
|
|
@@ -179,17 +131,20 @@ export type Dashboard01Props = {
|
|
|
179
131
|
* Prism happened to put first. So `panels` is a list with a width on each entry and
|
|
180
132
|
* this Block's only remaining opinion is the six-track grid they are placed on.
|
|
181
133
|
*
|
|
182
|
-
* **The figures are
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
134
|
+
* **The figures are typed by the shared `MetricSpec` and drawn by `Metric` and
|
|
135
|
+
* `Sparkline`, and a dashboard that hand-drew its own metric row would be a second
|
|
136
|
+
* implementation of a Component whose whole argument is that the direction is derived
|
|
137
|
+
* rather than declared.** The row is a list of the same specification every other
|
|
138
|
+
* figure surface takes, published as `@nanisoft/prism-ui/spec`, so a consumer that
|
|
139
|
+
* learned the shape here exports it unchanged to a project dashboard and a summary
|
|
140
|
+
* panel. `Metric`'s delta is a `number` and the sign is the direction, so a rising
|
|
141
|
+
* mark cannot be drawn on a falling reading; a Block that took `{ value, direction }`
|
|
142
|
+
* could, and the mistake would be invisible in review and visible to every reader of
|
|
143
|
+
* that dashboard. It also puts the label under the value, which is the arrangement a
|
|
144
|
+
* dashboard row wants, because a row is scanned by its figures and settled by its
|
|
145
|
+
* names. And `Sparkline` draws the numbers behind its shape as a table in the
|
|
146
|
+
* document, so a metric row of six costs six tables in the accessibility tree rather
|
|
147
|
+
* than six pictures nobody can query. The cost is stated rather than implied: a series
|
|
193
148
|
* needs a name and a series that is not flat, and a caller who passes an empty array
|
|
194
149
|
* or a series of one repeated value gets `Sparkline`'s own refusal, which is the
|
|
195
150
|
* right place for it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/dashboard-01/dashboard-01.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAWtC,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"dashboard-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/dashboard-01/dashboard-01.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAWtC,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEvD;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAA;IACV,wEAAwE;IACxE,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,mEAAmE;IACnE,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,4EAA4E;IAC5E,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,kFAAkF;IAClF,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiFG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,WAAW,EACX,MAAM,EACN,OAAY,EACZ,MAAW,EACX,IAAI,EACJ,KAAK,EACL,YAAmB,EACnB,SAAS,GACV,EAAE,gBAAgB,+BAoLlB;AAED,eAAe,WAAW,CAAA"}
|
|
@@ -23,24 +23,26 @@ const SPAN = {
|
|
|
23
23
|
* The refusals, as checks, so a tile that would render a control nobody can name is
|
|
24
24
|
* a diagnostic in a console rather than a rendered link.
|
|
25
25
|
*
|
|
26
|
-
* The
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
26
|
+
* **The type already holds both pairs, and these checks exist for the caller the
|
|
27
|
+
* type never saw.** `MetricSpec` requires a destination's words wherever it has a
|
|
28
|
+
* destination and a series name wherever it has a series, so a typed caller cannot
|
|
29
|
+
* reach either diagnostic. A JavaScript caller can, and the cost of reaching one is
|
|
30
|
+
* a tile that draws a link with no words or a shape no reader can announce, so the
|
|
31
|
+
* same rule is stated once more where a runtime value can be read: the pair rule is
|
|
32
|
+
* the one `Changelog01` and `ContentGrid01` make, and the series name is the one
|
|
33
|
+
* `Sparkline` gives in full.
|
|
32
34
|
*/
|
|
33
35
|
function assertMetrics(metrics) {
|
|
34
36
|
for (const metric of metrics) {
|
|
35
37
|
if ((metric.href === undefined) !== (metric.hrefLabel === undefined)) {
|
|
36
|
-
throw new Error(`Dashboard01: the metric "${metric.
|
|
38
|
+
throw new Error(`Dashboard01: the metric "${metric.key}" declares one of href and hrefLabel without the other, so the ` +
|
|
37
39
|
'tile would carry a link with no words on it, or a name with no link beside it. Pass the words that say ' +
|
|
38
40
|
'what following it does, or omit the href.');
|
|
39
41
|
}
|
|
40
|
-
if (metric.
|
|
41
|
-
throw new Error(`Dashboard01: the metric "${metric.
|
|
42
|
-
'be a picture with no name, which a screen reader cannot announce and two
|
|
43
|
-
'told apart. Pass the series name in the words the product uses.');
|
|
42
|
+
if ((metric.series === undefined) !== (metric.seriesLabel === undefined)) {
|
|
43
|
+
throw new Error(`Dashboard01: the metric "${metric.key}" declares one of series and seriesLabel without the other, so the ` +
|
|
44
|
+
'figure would be a picture with no name, which a screen reader cannot announce and two shapes in one row ' +
|
|
45
|
+
'cannot be told apart. Pass the series name in the words the product uses, or omit the series.');
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
}
|
|
@@ -60,17 +62,20 @@ function assertMetrics(metrics) {
|
|
|
60
62
|
* Prism happened to put first. So `panels` is a list with a width on each entry and
|
|
61
63
|
* this Block's only remaining opinion is the six-track grid they are placed on.
|
|
62
64
|
*
|
|
63
|
-
* **The figures are
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
65
|
+
* **The figures are typed by the shared `MetricSpec` and drawn by `Metric` and
|
|
66
|
+
* `Sparkline`, and a dashboard that hand-drew its own metric row would be a second
|
|
67
|
+
* implementation of a Component whose whole argument is that the direction is derived
|
|
68
|
+
* rather than declared.** The row is a list of the same specification every other
|
|
69
|
+
* figure surface takes, published as `@nanisoft/prism-ui/spec`, so a consumer that
|
|
70
|
+
* learned the shape here exports it unchanged to a project dashboard and a summary
|
|
71
|
+
* panel. `Metric`'s delta is a `number` and the sign is the direction, so a rising
|
|
72
|
+
* mark cannot be drawn on a falling reading; a Block that took `{ value, direction }`
|
|
73
|
+
* could, and the mistake would be invisible in review and visible to every reader of
|
|
74
|
+
* that dashboard. It also puts the label under the value, which is the arrangement a
|
|
75
|
+
* dashboard row wants, because a row is scanned by its figures and settled by its
|
|
76
|
+
* names. And `Sparkline` draws the numbers behind its shape as a table in the
|
|
77
|
+
* document, so a metric row of six costs six tables in the accessibility tree rather
|
|
78
|
+
* than six pictures nobody can query. The cost is stated rather than implied: a series
|
|
74
79
|
* needs a name and a series that is not flat, and a caller who passes an empty array
|
|
75
80
|
* or a series of one repeated value gets `Sparkline`'s own refusal, which is the
|
|
76
81
|
* right place for it.
|
|
@@ -136,6 +141,12 @@ export function Dashboard01({ eyebrow, title, description, header, metrics = [],
|
|
|
136
141
|
// The gap between the header and the body is a fact about whether the lead
|
|
137
142
|
// drew anything, so it arrives with that fact rather than as a margin on
|
|
138
143
|
// two of the three possible children.
|
|
139
|
-
hasLead ? 'mt-8' : null, hasRail ? 'lg:grid lg:grid-cols-4 lg:items-start lg:gap-6' : null), children: [_jsxs("div", { "data-slot": "dashboard-01-main", className: cn('flex min-w-0 flex-col gap-6', hasRail ? 'lg:col-span-3' : null), children: [metrics.length > 0 ? (_jsx("div", { "data-slot": "dashboard-01-metrics", className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: metrics.map((metric) =>
|
|
144
|
+
hasLead ? 'mt-8' : null, hasRail ? 'lg:grid lg:grid-cols-4 lg:items-start lg:gap-6' : null), children: [_jsxs("div", { "data-slot": "dashboard-01-main", className: cn('flex min-w-0 flex-col gap-6', hasRail ? 'lg:col-span-3' : null), children: [metrics.length > 0 ? (_jsx("div", { "data-slot": "dashboard-01-metrics", className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: metrics.map((metric) => {
|
|
145
|
+
// The delta's words are the caller's own node, and `Metric` takes a
|
|
146
|
+
// formatter, so the node is placed through one. A delta with no words
|
|
147
|
+
// is left to `Metric`, which prints the number the caller passed.
|
|
148
|
+
const deltaWords = metric.deltaFormat;
|
|
149
|
+
return (_jsxs(Card, { "data-slot": "dashboard-01-metric", "data-metric": metric.key, className: "gap-4 py-5", children: [_jsxs(CardContent, { 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": "dashboard-01-sparkline", className: "flex", children: _jsx(Sparkline, { values: metric.series, label: metric.seriesLabel, className: "ms-auto" }) }))] }), metric.href === undefined ? null : (_jsx(CardFooter, { "data-slot": "dashboard-01-metric-link", className: "justify-end", children: _jsx(CtaLink, { href: metric.href, variant: "ghost", size: "sm", children: metric.hrefLabel }) }))] }, metric.key));
|
|
150
|
+
}) })) : null, panels.length > 0 ? (_jsx("div", { "data-slot": "dashboard-01-panels", className: "grid gap-4 lg:grid-cols-6", children: panels.map((panel) => (_jsx("div", { "data-slot": "dashboard-01-panel", "data-panel": panel.id, className: SPAN[panel.span ?? 'full'], children: _jsxs(Card, { className: "h-full gap-4 py-5", children: [panel.title === undefined && panel.toolbar === undefined ? null : (_jsxs(CardHeader, { className: "grid-cols-[minmax(0,1fr)_auto] gap-x-3", children: [panel.title === undefined ? null : (_jsx(CardTitle, { as: PanelTitle, className: "text-base", children: panel.title })), panel.toolbar === undefined ? null : (_jsx("div", { "data-slot": "dashboard-01-panel-toolbar", className: "flex min-w-0 items-center gap-2", children: panel.toolbar }))] })), _jsx(CardContent, { children: panel.children }), panel.footer === undefined ? null : (_jsx(CardFooter, { "data-slot": "dashboard-01-panel-footer", children: panel.footer }))] }) }, panel.id))) })) : null, hasBody ? null : _jsx("div", { "data-slot": "dashboard-01-empty", children: empty })] }), hasRail ? (_jsx("div", { "data-slot": "dashboard-01-rail", className: "flex min-w-0 flex-col gap-4 lg:col-span-1", children: rail })) : null] })] }));
|
|
140
151
|
}
|
|
141
152
|
export default Dashboard01;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Dashboard01, default } from './dashboard-01.js';
|
|
2
|
-
export type {
|
|
2
|
+
export type { Dashboard01Panel, Dashboard01Props, Dashboard01Span } from './dashboard-01.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/dashboard-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAIrD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/dashboard-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAIrD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA"}
|