@nanisoft/prism-ui 0.16.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts +120 -190
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts.map +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.js +107 -97
- package/dist/blocks/activity-feed-01/index.d.ts +1 -1
- package/dist/blocks/activity-feed-01/index.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.d.ts +86 -53
- package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.js +95 -31
- package/dist/blocks/auth-form-01/index.d.ts +1 -1
- package/dist/blocks/auth-form-01/index.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.d.ts +28 -0
- package/dist/blocks/calendar-01/calendar.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.js +15 -1
- package/dist/blocks/card-index-01/card-index.d.ts +225 -0
- package/dist/blocks/card-index-01/card-index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/card-index.js +154 -0
- package/dist/blocks/card-index-01/index.d.ts +3 -0
- package/dist/blocks/card-index-01/index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/index.js +1 -0
- package/dist/blocks/chart-card-01/chart-card-01.d.ts +23 -21
- package/dist/blocks/chart-card-01/chart-card-01.d.ts.map +1 -1
- package/dist/blocks/chart-card-01/chart-card-01.js +46 -1
- package/dist/blocks/chart-card-01/index.d.ts +1 -1
- package/dist/blocks/chart-card-01/index.d.ts.map +1 -1
- package/dist/blocks/chart-group-01/chart-group-01.d.ts +3 -3
- package/dist/blocks/chart-group-01/chart-group-01.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.d.ts +30 -82
- package/dist/blocks/contact-01/contact.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.js +59 -30
- package/dist/blocks/contact-01/index.d.ts +1 -1
- package/dist/blocks/contact-01/index.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.d.ts +26 -71
- package/dist/blocks/dashboard-01/dashboard-01.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.js +34 -23
- package/dist/blocks/dashboard-01/index.d.ts +1 -1
- package/dist/blocks/dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.d.ts +276 -59
- package/dist/blocks/data-table-01/data-table.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.js +217 -35
- package/dist/blocks/data-table-01/index.d.ts +1 -1
- package/dist/blocks/data-table-01/index.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.d.ts +23 -2
- package/dist/blocks/directory-01/directory-01.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.js +3 -3
- package/dist/blocks/empty-state-01/empty-state.d.ts +60 -21
- package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -1
- package/dist/blocks/empty-state-01/empty-state.js +52 -17
- package/dist/blocks/index-detail-01/index-detail.d.ts +85 -0
- package/dist/blocks/index-detail-01/index-detail.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index-detail.js +58 -0
- package/dist/blocks/index-detail-01/index.d.ts +3 -0
- package/dist/blocks/index-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index.js +1 -0
- package/dist/blocks/index.d.ts +20 -8
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/index.js +7 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts +18 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts.map +1 -1
- package/dist/blocks/issue-detail-01/issue-detail.js +18 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts +3 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/index.js +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts +119 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.js +79 -0
- package/dist/blocks/project-dashboard-01/index.d.ts +1 -1
- package/dist/blocks/project-dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts +21 -27
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.js +45 -5
- package/dist/blocks/provisioning-01/index.d.ts +1 -1
- package/dist/blocks/provisioning-01/index.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.d.ts +9 -97
- package/dist/blocks/provisioning-01/provisioning.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.js +26 -13
- package/dist/blocks/record-detail-01/index.d.ts +3 -0
- package/dist/blocks/record-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/index.js +1 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts +239 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/record-detail.js +150 -0
- package/dist/blocks/record-form-01/index.d.ts +3 -0
- package/dist/blocks/record-form-01/index.d.ts.map +1 -0
- package/dist/blocks/record-form-01/index.js +1 -0
- package/dist/blocks/record-form-01/record-form.d.ts +148 -0
- package/dist/blocks/record-form-01/record-form.d.ts.map +1 -0
- package/dist/blocks/record-form-01/record-form.js +107 -0
- package/dist/blocks/record-grid-01/index.d.ts +3 -0
- package/dist/blocks/record-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/index.js +1 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts +145 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/record-grid.js +114 -0
- package/dist/blocks/record-wizard-01/index.d.ts +3 -0
- package/dist/blocks/record-wizard-01/index.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/index.js +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts +152 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.js +144 -0
- package/dist/blocks/settings-members-01/settings-members.d.ts +2 -1
- package/dist/blocks/settings-members-01/settings-members.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.d.ts +165 -67
- package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.js +92 -33
- package/dist/blocks/signup-01/index.d.ts +1 -1
- package/dist/blocks/signup-01/index.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.d.ts +10 -84
- package/dist/blocks/signup-01/signup-01.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.js +41 -23
- package/dist/blocks/stats-01/index.d.ts +5 -5
- package/dist/blocks/stats-01/index.d.ts.map +1 -1
- package/dist/blocks/stats-01/index.js +4 -4
- package/dist/blocks/stats-01/stats.d.ts +49 -14
- package/dist/blocks/stats-01/stats.d.ts.map +1 -1
- package/dist/blocks/stats-01/stats.js +65 -15
- package/dist/blocks/trend-01/index.d.ts +1 -1
- package/dist/blocks/trend-01/index.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.d.ts +26 -73
- package/dist/blocks/trend-01/trend.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.js +47 -30
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +93 -3
- package/dist/components/ui/metric.d.ts +4 -2
- package/dist/components/ui/metric.d.ts.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/lib/field-render.d.ts +107 -0
- package/dist/lib/field-render.d.ts.map +1 -0
- package/dist/lib/field-render.js +251 -0
- package/dist/lib/spec.d.ts +609 -0
- package/dist/lib/spec.d.ts.map +1 -0
- package/dist/lib/spec.js +1 -0
- package/dist/live/index.d.ts +2 -0
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js +6 -0
- package/dist/live/message-thread-01/index.d.ts +3 -0
- package/dist/live/message-thread-01/index.d.ts.map +1 -0
- package/dist/live/message-thread-01/index.js +1 -0
- package/dist/live/message-thread-01/message-thread.d.ts +282 -0
- package/dist/live/message-thread-01/message-thread.d.ts.map +1 -0
- package/dist/live/message-thread-01/message-thread.js +221 -0
- package/dist/live/run-stream-01/run-stream.d.ts +2 -1
- package/dist/live/run-stream-01/run-stream.d.ts.map +1 -1
- package/dist/live/run-stream-01/run-stream.js +1 -7
- package/dist/live/status.d.ts +27 -0
- package/dist/live/status.d.ts.map +1 -0
- package/dist/live/status.js +15 -0
- package/dist/live/tool-call-row.d.ts +26 -0
- package/dist/live/tool-call-row.d.ts.map +1 -0
- package/dist/live/tool-call-row.js +60 -0
- package/dist/live/tool-ledger-01/tool-ledger.d.ts.map +1 -1
- package/dist/live/tool-ledger-01/tool-ledger.js +2 -42
- package/dist/pages/contact-page/index.d.ts +7 -7
- package/dist/pages/contact-page/index.d.ts.map +1 -1
- package/dist/pages/contact-page/index.js +17 -5
- package/dist/pages/docs-shell/docs-shell.d.ts +42 -30
- package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
- package/dist/pages/docs-shell/docs-shell.js +70 -48
- package/dist/pages/document-page/document-page.d.ts +146 -0
- package/dist/pages/document-page/document-page.d.ts.map +1 -0
- package/dist/pages/document-page/document-page.js +80 -0
- package/dist/pages/document-page/index.d.ts +3 -0
- package/dist/pages/document-page/index.d.ts.map +1 -0
- package/dist/pages/document-page/index.js +1 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +6 -0
- package/dist/styles.css +59 -3
- package/package.json +7 -2
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { XIcon } from 'lucide-react';
|
|
4
|
+
import { useEffect, useId, useRef, useState } from 'react';
|
|
5
|
+
import { Button } from '../../components/ui/button.js';
|
|
6
|
+
import { Checkbox } from '../../components/ui/checkbox.js';
|
|
7
|
+
import { LiveRegion } from '../../components/ui/live-region.js';
|
|
8
|
+
import { Pagination, PaginationContent, PaginationItem } from '../../components/ui/pagination.js';
|
|
9
|
+
import { headingSizeClass } from '../../components/ui/section.js';
|
|
10
|
+
import { cn } from '../../lib/utils.js';
|
|
11
|
+
/** The page numbers to show, with `ellipsis` where a run is held back. */
|
|
12
|
+
function pageWindow(page, pageCount) {
|
|
13
|
+
if (pageCount <= 7) {
|
|
14
|
+
return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
15
|
+
}
|
|
16
|
+
const pages = [1];
|
|
17
|
+
const start = Math.max(2, page - 1);
|
|
18
|
+
const end = Math.min(pageCount - 1, page + 1);
|
|
19
|
+
if (start > 2)
|
|
20
|
+
pages.push('ellipsis');
|
|
21
|
+
for (let value = start; value <= end; value += 1)
|
|
22
|
+
pages.push(value);
|
|
23
|
+
if (end < pageCount - 1)
|
|
24
|
+
pages.push('ellipsis');
|
|
25
|
+
pages.push(pageCount);
|
|
26
|
+
return pages;
|
|
27
|
+
}
|
|
28
|
+
/** The grid track count at the widest width, as the one static class per arm. */
|
|
29
|
+
function gridClass(columnsPerRow) {
|
|
30
|
+
if (columnsPerRow === 2)
|
|
31
|
+
return 'grid grid-cols-1 gap-4 sm:grid-cols-2';
|
|
32
|
+
if (columnsPerRow === 4)
|
|
33
|
+
return 'grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4';
|
|
34
|
+
return 'grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The record index as a grid of picture cards a reader compares and acts on.
|
|
38
|
+
*
|
|
39
|
+
* **It is the record index's second arrangement, and the job-identity test is
|
|
40
|
+
* what settles it.** A reader here is doing the index's job, deciding which
|
|
41
|
+
* record to act on, and the difference from `DataTable01` is the arrangement
|
|
42
|
+
* rather than the content: records are compared as pictures rather than
|
|
43
|
+
* across columns, so a screen that wants this has not found the index with one
|
|
44
|
+
* more prop, it has found the picture arrangement. `DESIGN.md` names it as an
|
|
45
|
+
* arrangement that forces a second Item, and this is that Item. A card carries
|
|
46
|
+
* the caller's own picture, name and price through `renderCard`, and per card
|
|
47
|
+
* actions through `renderCardActions`.
|
|
48
|
+
*
|
|
49
|
+
* **It shares the record index's selection and announcement contract.** The
|
|
50
|
+
* same `selectable` union, the same held set, the same three-state header
|
|
51
|
+
* control, the same polite `LiveRegion` carrying a count, the same batch bar
|
|
52
|
+
* that mounts only while the set is non-empty, and the same `selectionScope`
|
|
53
|
+
* that distinguishes a page-wide selection from a filter-wide one, all because
|
|
54
|
+
* ticket 166 made one contract the law for the index family. The count is
|
|
55
|
+
* announced once, politely, and never assertively; when the set empties the
|
|
56
|
+
* region speaks its own sentence rather than a count of zero; and the visible
|
|
57
|
+
* summary and the announced count are the same string.
|
|
58
|
+
*
|
|
59
|
+
* **It draws the page of cards it was handed, and it fetches nothing.** It sorts
|
|
60
|
+
* nothing, filters nothing and slices nothing, and every control in the footer
|
|
61
|
+
* is a report. It owns no action, batch or per card, so it owns no confirmation
|
|
62
|
+
* dialog, no toast and no undo; the batch bar is the Block's region and the
|
|
63
|
+
* caller's controls, and the dismiss is the Block's one control because its
|
|
64
|
+
* whole body is a call on the set the Block already holds.
|
|
65
|
+
*
|
|
66
|
+
* **The card is the caller's composition, and the arrangement is the Block's.**
|
|
67
|
+
* A picture, a name and a price are three different value shapes, and the read
|
|
68
|
+
* vocabulary this package publishes (`ColumnSpec` and `ColumnKind`) describes a
|
|
69
|
+
* value a reader reads in a table rather than the anatomy of a card. So the card
|
|
70
|
+
* body is one node per record and the picture, the name and the price are the
|
|
71
|
+
* caller's own composition, exactly as a per card action is. The Block owns the
|
|
72
|
+
* grid, the selection set, the count, the batch bar and the page boundary, which
|
|
73
|
+
* is the whole of what makes this an index rather than a list of pictures.
|
|
74
|
+
*/
|
|
75
|
+
export function CardIndex01(props) {
|
|
76
|
+
const { title, description, headingLevel = 'h2', rows, getRowId, renderCard, renderCardActions, columnsPerRow = 3, page: pageProp, defaultPage = 1, pageCount, onPageChange, emptyMessage, labels, className, } = props;
|
|
77
|
+
const Heading = headingLevel;
|
|
78
|
+
const headingId = useId();
|
|
79
|
+
const isSelectable = props.selectable === true;
|
|
80
|
+
const [internalSelected, setInternalSelected] = useState(() => [
|
|
81
|
+
...(props.defaultSelectedIds ?? []),
|
|
82
|
+
]);
|
|
83
|
+
const [internalPage, setInternalPage] = useState(defaultPage);
|
|
84
|
+
const selected = isSelectable
|
|
85
|
+
? props.selectedIds === undefined
|
|
86
|
+
? internalSelected
|
|
87
|
+
: [...props.selectedIds]
|
|
88
|
+
: [];
|
|
89
|
+
const page = pageProp ?? internalPage;
|
|
90
|
+
const clampPage = (value) => Math.min(Math.max(1, value), Math.max(1, pageCount));
|
|
91
|
+
const currentPage = clampPage(page);
|
|
92
|
+
const hasCardActions = renderCardActions !== undefined;
|
|
93
|
+
const scope = isSelectable ? props.selectionScope : undefined;
|
|
94
|
+
const filterScope = scope?.scope === 'filter' ? scope : undefined;
|
|
95
|
+
// A filter-wide selection is a selection even though its keys are not on
|
|
96
|
+
// screen, so the bar mounts and the region speaks from the scope rather than
|
|
97
|
+
// from the page's keys. The number is the caller's and is never the rows on
|
|
98
|
+
// screen.
|
|
99
|
+
const selectionActive = isSelectable && (filterScope !== undefined || selected.length > 0);
|
|
100
|
+
const selectionSummary = filterScope !== undefined
|
|
101
|
+
? labels.selectedAllMatching(filterScope.count)
|
|
102
|
+
: labels.selectedCount(selected.length);
|
|
103
|
+
const rowIds = rows.map(getRowId);
|
|
104
|
+
const allSelected = filterScope !== undefined || (rowIds.length > 0 && rowIds.every((id) => selected.includes(id)));
|
|
105
|
+
const someSelected = filterScope !== undefined || rowIds.some((id) => selected.includes(id));
|
|
106
|
+
// The emptying sentence. A live region speaks when its text changes, and the
|
|
107
|
+
// sequence where the selection falls to nothing is the one the reader has to
|
|
108
|
+
// be told about because the batch bar they were about to use disappears with
|
|
109
|
+
// it. The previous presence is ref-held, so the render that reaches nothing
|
|
110
|
+
// still knows there was a selection and can speak the sentence rather than the
|
|
111
|
+
// absence of one.
|
|
112
|
+
const previousActive = useRef(selectionActive);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
previousActive.current = selectionActive;
|
|
115
|
+
}, [selectionActive]);
|
|
116
|
+
const announcement = selectionActive
|
|
117
|
+
? selectionSummary
|
|
118
|
+
: previousActive.current
|
|
119
|
+
? labels.clearedSelection
|
|
120
|
+
: null;
|
|
121
|
+
function changeSelection(ids) {
|
|
122
|
+
if (isSelectable === false)
|
|
123
|
+
return;
|
|
124
|
+
if (props.selectedIds === undefined)
|
|
125
|
+
setInternalSelected(ids);
|
|
126
|
+
props.onSelectedIdsChange?.(ids);
|
|
127
|
+
}
|
|
128
|
+
function clearSelection() {
|
|
129
|
+
changeSelection([]);
|
|
130
|
+
}
|
|
131
|
+
function toggleAll(checked) {
|
|
132
|
+
if (checked) {
|
|
133
|
+
changeSelection([...new Set([...selected, ...rowIds])]);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
changeSelection(selected.filter((id) => !rowIds.includes(id)));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function toggleRow(id, checked) {
|
|
140
|
+
changeSelection(checked ? [...selected, id] : selected.filter((item) => item !== id));
|
|
141
|
+
}
|
|
142
|
+
function changePage(next) {
|
|
143
|
+
const clamped = clampPage(next);
|
|
144
|
+
if (pageProp === undefined)
|
|
145
|
+
setInternalPage(clamped);
|
|
146
|
+
onPageChange?.(clamped);
|
|
147
|
+
}
|
|
148
|
+
return (_jsxs("div", { "data-slot": "card-index-01", className: cn('flex flex-col gap-4', className), children: [title || description ? (_jsxs("div", { "data-slot": "card-index-01-header", className: "flex flex-col gap-1", children: [title ? (_jsx(Heading, { id: headingId, className: cn('font-semibold tracking-tight text-balance', headingSizeClass(Heading)), children: title })) : null, description ? (_jsx("p", { className: "text-muted-foreground text-sm", children: description })) : null] })) : null, selectionActive ? (_jsxs("div", { "data-slot": "card-index-01-batch-bar", role: "toolbar", "aria-label": selectionSummary, className: "bg-card flex w-full items-center gap-1 rounded-md border p-1", children: [_jsx("span", { className: "text-muted-foreground px-2 text-sm font-medium", children: selectionSummary }), _jsx("div", { className: "flex flex-1 flex-wrap items-center gap-2", children: props.selectable === true ? props.batchActions : null }), _jsx(Button, { "data-slot": "card-index-01-dismiss", type: "button", variant: "ghost", size: "sm", "aria-label": labels.dismissSelection, onClick: clearSelection, children: _jsx(XIcon, { "aria-hidden": "true" }) })] })) : null, _jsx(LiveRegion, { "data-slot": "card-index-01-announcement", politeness: "polite", className: "sr-only", children: announcement }), isSelectable ? (_jsx("div", { "data-slot": "card-index-01-select-all", className: "flex items-center gap-2", children: _jsx(Checkbox, { checked: allSelected, indeterminate: !allSelected && someSelected, onCheckedChange: toggleAll, "aria-label": labels.selectAll }) })) : null, rows.length ? (_jsx("ul", { "data-slot": "card-index-01-grid", className: gridClass(columnsPerRow), children: rows.map((row) => {
|
|
149
|
+
const id = getRowId(row);
|
|
150
|
+
const isSelected = selected.includes(id);
|
|
151
|
+
return (_jsxs("li", { "data-slot": "card-index-01-card", "data-state": isSelected ? 'selected' : undefined, className: "bg-card flex flex-col gap-3 rounded-xl border p-3", children: [isSelectable || hasCardActions ? (_jsxs("div", { className: "flex items-start justify-between gap-2", children: [isSelectable ? (_jsx(Checkbox, { checked: isSelected, onCheckedChange: (checked) => toggleRow(id, checked), "aria-label": labels.selectCard(row) })) : (_jsx("span", {})), hasCardActions ? (_jsx("div", { role: "group", "aria-label": labels.cardActions, className: "flex items-center gap-1", children: renderCardActions?.(row) })) : null] })) : null, _jsx("div", { className: "min-w-0 flex-1", children: renderCard(row) })] }, id));
|
|
152
|
+
}) })) : (_jsx("div", { "data-slot": "card-index-01-empty", className: "text-muted-foreground border-border flex h-24 items-center justify-center rounded-xl border text-center text-sm", children: emptyMessage ?? labels.empty })), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [selectionActive ? (_jsx("span", { className: "text-muted-foreground text-sm", children: selectionSummary })) : (_jsx("span", {})), _jsx(Pagination, { className: "mx-0 w-auto justify-end", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: currentPage <= 1, "aria-label": labels.previous, onClick: () => changePage(currentPage - 1), children: labels.previous }) }), pageWindow(currentPage, Math.max(1, pageCount)).map((item, index) => item === 'ellipsis' ? (_jsx(PaginationItem, { children: _jsx("span", { "aria-hidden": "true", className: "text-muted-foreground px-2", children: "..." }) }, `ellipsis-${index}`)) : (_jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: item === currentPage ? 'outline' : 'ghost', size: "icon", "aria-label": labels.page(item), "aria-current": item === currentPage ? 'page' : undefined, onClick: () => changePage(item), children: item }) }, item))), _jsx(PaginationItem, { children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: currentPage >= Math.max(1, pageCount), "aria-label": labels.next, onClick: () => changePage(currentPage + 1), children: labels.next }) })] }) })] })] }));
|
|
153
|
+
}
|
|
154
|
+
export default CardIndex01;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/card-index-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACnD,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,yBAAyB,GAC1B,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CardIndex01, default } from './card-index.js';
|
|
@@ -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 a six-track grid one card takes.
|
|
5
6
|
*
|
|
@@ -18,25 +19,6 @@ import { type HeadingLevel } from '../../components/ui/section.js';
|
|
|
18
19
|
* grid the caller composed them into.
|
|
19
20
|
*/
|
|
20
21
|
export type ChartCard01Span = 'full' | 'half' | 'third';
|
|
21
|
-
/**
|
|
22
|
-
* One figure the card states about what the figure shows.
|
|
23
|
-
*
|
|
24
|
-
* A node and not a number for the reason `Metric` gives: a caller's figure is often a
|
|
25
|
-
* value it formatted itself, and a number prop would make it either ship that as a
|
|
26
|
-
* string outside the Component or lose the unit it belongs to. `deltaFormat` is here
|
|
27
|
-
* for the same reason: Prism formats the delta and nothing else, and a delta of `0.12`
|
|
28
|
-
* is twelve percent, twelve cents or twelve milliseconds.
|
|
29
|
-
*/
|
|
30
|
-
export type ChartCard01Reading = {
|
|
31
|
-
/** What the figure measures, read under the figure rather than above it. */
|
|
32
|
-
label: string;
|
|
33
|
-
/** The figure itself, in the caller's own units and the caller's own formatting. */
|
|
34
|
-
value: ReactNode;
|
|
35
|
-
/** The change against the previous reading, where the sign is the direction. */
|
|
36
|
-
delta?: number;
|
|
37
|
-
/** The words for the delta, given the number. See the note on this type. */
|
|
38
|
-
deltaFormat?: (value: number) => string;
|
|
39
|
-
};
|
|
40
22
|
/**
|
|
41
23
|
* The props a ChartCard01 takes. Every string is a prop and the Block ships none.
|
|
42
24
|
*/
|
|
@@ -69,8 +51,19 @@ export type ChartCard01Props = {
|
|
|
69
51
|
* here, which is the only reason the swatch and the mark cannot disagree.
|
|
70
52
|
*/
|
|
71
53
|
legend?: ReactNode;
|
|
72
|
-
/**
|
|
73
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The figures this card states about what the figure shows, as the shared
|
|
56
|
+
* `MetricSpec` typed data.
|
|
57
|
+
*
|
|
58
|
+
* **The shared specification and not a shape of this Block's own.** `key`,
|
|
59
|
+
* `label` and `value` are required on each reading, `delta` is a number whose sign
|
|
60
|
+
* is the direction, `deltaFormat` is the caller's own words for that change,
|
|
61
|
+
* `hint` carries the period or the caveat, and a `series` and an `href` each travel
|
|
62
|
+
* with the label or the words they cannot be read without. The type is published as
|
|
63
|
+
* `@nanisoft/prism-ui/spec`, so the words a caller learns on a summary are the
|
|
64
|
+
* words on this card.
|
|
65
|
+
*/
|
|
66
|
+
reading?: MetricSpec[];
|
|
74
67
|
/** The controls that act on the figure: a range, a segment, a download. */
|
|
75
68
|
actions?: ReactNode;
|
|
76
69
|
/** The band under the figure, for the line that qualifies it. */
|
|
@@ -123,6 +116,15 @@ export type ChartCard01Props = {
|
|
|
123
116
|
* so a card composed one level deeper carries its outline with it instead of
|
|
124
117
|
* announcing three siblings of the section that introduces them.
|
|
125
118
|
*
|
|
119
|
+
* **The readings are the shared `MetricSpec` and not a shape of this Block's own, so
|
|
120
|
+
* a figure declared for a summary is the same figure here.** Each reading carries a
|
|
121
|
+
* stable `key` that is never the words of the label, a required `label` and `value`,
|
|
122
|
+
* a `delta` whose sign is the direction, an optional `deltaFormat` carrying the
|
|
123
|
+
* caller's own words for the change, an optional `hint` for the period or the
|
|
124
|
+
* caveat, and an optional `series` and `href` with their names. A delta with no
|
|
125
|
+
* formatter prints the number the caller passed and one with a formatter prints the
|
|
126
|
+
* caller's words, which is the disagreement this migration closes.
|
|
127
|
+
*
|
|
126
128
|
* **The readings sit in a flex row that wraps, and they share the width rather than
|
|
127
129
|
* holding a fixed one.** The measurement that decides it: a `third` card at the
|
|
128
130
|
* 72rem container is about 22rem wide, so three readings across it are about 7rem
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-card-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/chart-card-01/chart-card-01.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"chart-card-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/chart-card-01/chart-card-01.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAYtC,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAE3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEvD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB,qEAAqE;IACrE,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;OAQG;IACH,MAAM,EAAE,SAAS,CAAA;IACjB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,iEAAiE;IACjE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,WAAW,EACX,MAAM,EACN,MAAM,EACN,OAAY,EACZ,OAAO,EACP,MAAM,EACN,IAAa,EACb,YAAmB,EACnB,SAAS,GACV,EAAE,gBAAgB,+BA+FlB;AAED,eAAe,WAAW,CAAA"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Card, CardContent, CardDescription, CardFooter, 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 { childLevel } from '../../components/ui/section.js';
|
|
6
|
+
import { Sparkline } from '../../components/ui/sparkline.js';
|
|
5
7
|
import { cn } from '../../lib/utils.js';
|
|
6
8
|
/**
|
|
7
9
|
* What each named span takes of a six-track grid, at the breakpoint the grid appears.
|
|
@@ -16,6 +18,32 @@ const SPAN = {
|
|
|
16
18
|
half: 'lg:col-span-3',
|
|
17
19
|
third: 'lg:col-span-2',
|
|
18
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* The refusals, as checks, so a reading that would render a link nobody can name is
|
|
23
|
+
* a diagnostic in a console rather than a rendered link.
|
|
24
|
+
*
|
|
25
|
+
* **The type already holds both pairs, and these checks exist for the caller the
|
|
26
|
+
* type never saw.** `MetricSpec` requires a destination's words wherever it has a
|
|
27
|
+
* destination and a series name wherever it has a series, so a typed caller cannot
|
|
28
|
+
* reach either diagnostic. A JavaScript caller can, and the cost of reaching one is
|
|
29
|
+
* a reading that draws a link with no words or a shape no reader can announce, so
|
|
30
|
+
* the same rule is stated once more where a runtime value can be read: the pair rule
|
|
31
|
+
* is the one `Dashboard01` makes.
|
|
32
|
+
*/
|
|
33
|
+
function assertMetrics(metrics) {
|
|
34
|
+
for (const metric of metrics) {
|
|
35
|
+
if ((metric.href === undefined) !== (metric.hrefLabel === undefined)) {
|
|
36
|
+
throw new Error(`ChartCard01: the reading "${metric.key}" declares one of href and hrefLabel without the other, so the ` +
|
|
37
|
+
'reading would carry a link with no words on it, or a name with no link beside it. Pass the words that say ' +
|
|
38
|
+
'what following it does, or omit the href.');
|
|
39
|
+
}
|
|
40
|
+
if ((metric.series === undefined) !== (metric.seriesLabel === undefined)) {
|
|
41
|
+
throw new Error(`ChartCard01: the reading "${metric.key}" declares one of series and seriesLabel without the other, so the ` +
|
|
42
|
+
'figure would be a picture with no name, which a screen reader cannot announce and two shapes in one row ' +
|
|
43
|
+
'cannot be told apart. Pass the series name in the words the product uses, or omit the series.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
19
47
|
/**
|
|
20
48
|
* A titled panel holding one figure, the key for it, the controls that act on it, and
|
|
21
49
|
* the figures the card states about it.
|
|
@@ -51,6 +79,15 @@ const SPAN = {
|
|
|
51
79
|
* so a card composed one level deeper carries its outline with it instead of
|
|
52
80
|
* announcing three siblings of the section that introduces them.
|
|
53
81
|
*
|
|
82
|
+
* **The readings are the shared `MetricSpec` and not a shape of this Block's own, so
|
|
83
|
+
* a figure declared for a summary is the same figure here.** Each reading carries a
|
|
84
|
+
* stable `key` that is never the words of the label, a required `label` and `value`,
|
|
85
|
+
* a `delta` whose sign is the direction, an optional `deltaFormat` carrying the
|
|
86
|
+
* caller's own words for the change, an optional `hint` for the period or the
|
|
87
|
+
* caveat, and an optional `series` and `href` with their names. A delta with no
|
|
88
|
+
* formatter prints the number the caller passed and one with a formatter prints the
|
|
89
|
+
* caller's words, which is the disagreement this migration closes.
|
|
90
|
+
*
|
|
54
91
|
* **The readings sit in a flex row that wraps, and they share the width rather than
|
|
55
92
|
* holding a fixed one.** The measurement that decides it: a `third` card at the
|
|
56
93
|
* 72rem container is about 22rem wide, so three readings across it are about 7rem
|
|
@@ -76,6 +113,14 @@ export function ChartCard01({ title, description, figure, legend, reading = [],
|
|
|
76
113
|
// One step below the heading that introduces this card, so the card's own title
|
|
77
114
|
// nests under the section rather than beside it.
|
|
78
115
|
const Title = childLevel(headingLevel);
|
|
79
|
-
|
|
116
|
+
if (reading.length > 0)
|
|
117
|
+
assertMetrics(reading);
|
|
118
|
+
return (_jsx("div", { "data-slot": "chart-card-01", "data-span": span, className: cn(SPAN[span], className), children: _jsxs(Card, { className: "h-full gap-4 py-5", children: [_jsxs(CardHeader, { className: "grid-cols-[minmax(0,1fr)_auto] gap-x-3", children: [_jsxs("div", { "data-slot": "chart-card-01-head", className: "flex min-w-0 flex-col gap-1.5", children: [_jsx(CardTitle, { as: Title, className: "text-base", children: title }), description === undefined ? null : (_jsx(CardDescription, { className: "text-pretty", children: description }))] }), actions === undefined ? null : (_jsx("div", { "data-slot": "chart-card-01-actions", className: "flex shrink-0 items-center gap-2", children: actions }))] }), _jsxs(CardContent, { className: "flex flex-col gap-3", children: [_jsx("div", { "data-slot": "chart-card-01-figure", className: "min-w-0", children: figure }), legend === undefined ? null : (_jsx("div", { "data-slot": "chart-card-01-legend", className: "min-w-0", children: legend })), reading.length === 0 ? null : (_jsx("div", { "data-slot": "chart-card-01-reading", className: "border-border flex flex-wrap items-end gap-x-6 gap-y-3 border-t pt-4", children: reading.map((metric) => {
|
|
119
|
+
// The delta's words are the caller's own node, and `Metric` takes a
|
|
120
|
+
// formatter, so the node is placed through one. A delta with no words
|
|
121
|
+
// is left to `Metric`, which prints the number the caller passed.
|
|
122
|
+
const deltaWords = metric.deltaFormat;
|
|
123
|
+
return (_jsxs("div", { "data-slot": "chart-card-01-reading-item", "data-metric": metric.key, className: "flex min-w-0 flex-1 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": "chart-card-01-reading-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));
|
|
124
|
+
}) }))] }), footer === undefined ? null : (_jsx(CardFooter, { "data-slot": "chart-card-01-footer", children: footer }))] }) }));
|
|
80
125
|
}
|
|
81
126
|
export default ChartCard01;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { ChartCard01, default } from './chart-card-01.js';
|
|
2
|
-
export type { ChartCard01Props,
|
|
2
|
+
export type { ChartCard01Props, ChartCard01Span } from './chart-card-01.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/chart-card-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/chart-card-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAItD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { MetricSpec } from '../../lib/spec.js';
|
|
4
4
|
/**
|
|
5
5
|
* One card in the grid: a title, a figure, and everything the card carries around it.
|
|
6
6
|
*
|
|
@@ -32,8 +32,8 @@ export type ChartGroup01Card = {
|
|
|
32
32
|
figure: ReactNode;
|
|
33
33
|
/** The key for the figure, wherever the caller wants it. */
|
|
34
34
|
legend?: ReactNode;
|
|
35
|
-
/** The figures this card states about what the figure shows. */
|
|
36
|
-
reading?:
|
|
35
|
+
/** The figures this card states about what the figure shows. See `MetricSpec`. */
|
|
36
|
+
reading?: MetricSpec[];
|
|
37
37
|
/** The controls that act on the figure: a range, a segment, a download. */
|
|
38
38
|
actions?: ReactNode;
|
|
39
39
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-group-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/chart-group-01/chart-group-01.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"chart-group-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/chart-group-01/chart-group-01.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AACxF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAIhD;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;;;OAMG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,0EAA0E;IAC1E,KAAK,EAAE,SAAS,CAAA;IAChB,qEAAqE;IACrE,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;OAMG;IACH,MAAM,EAAE,SAAS,CAAA;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,kFAAkF;IAClF,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACf;;;;;;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;AAeD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,OAAW,EACX,KAAK,EACL,YAAmB,EACnB,SAAS,GACV,EAAE,iBAAiB,+BA2CnB;AAED,eAAe,YAAY,CAAA"}
|
|
@@ -1,81 +1,21 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type HeadingLevel } from '../../components/ui/section.js';
|
|
3
|
+
import type { FieldSpecGroup } from '../../lib/spec.js';
|
|
3
4
|
/**
|
|
4
|
-
* One
|
|
5
|
+
* One thing wrong with the form, in the caller's own words.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `NativeSelect`'s own argument in its JSDoc and stops there.
|
|
7
|
+
* `field` is the `key` of the field the message belongs to, and the key rather
|
|
8
|
+
* than the label because it is what the caller's values and the form's own
|
|
9
|
+
* `FormData` are keyed by: a message matched by a label's words breaks the moment
|
|
10
|
+
* the label is translated. The shape is the one the dialog, the wizard and the
|
|
11
|
+
* write form already publish, so this Block mints no fifth spelling of an issue.
|
|
12
12
|
*/
|
|
13
|
-
export type
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
/** The
|
|
17
|
-
|
|
13
|
+
export type Contact01Issue = {
|
|
14
|
+
/** The `key` of the field this message belongs to. */
|
|
15
|
+
field: string;
|
|
16
|
+
/** The message, in the product's own words. */
|
|
17
|
+
message: string;
|
|
18
18
|
};
|
|
19
|
-
/**
|
|
20
|
-
* What every contact field carries, whichever control it draws.
|
|
21
|
-
*
|
|
22
|
-
* `required` is a boolean rather than an optional prop because there is no third
|
|
23
|
-
* state worth having: a field is one the reader must fill or one they may leave,
|
|
24
|
-
* and a `required?: boolean` leaves a caller who passed nothing unable to say which
|
|
25
|
-
* of the two they meant. A field is a data declaration and nothing else, and the
|
|
26
|
-
* two arms below differ only in which control is drawn and in the one property
|
|
27
|
-
* that control needs and the other has no use for.
|
|
28
|
-
*/
|
|
29
|
-
type ContactFieldBase = {
|
|
30
|
-
/** The caller's key for this field, and the key the value arrives under. */
|
|
31
|
-
id: string;
|
|
32
|
-
/** The field's visible name, and the accessible name its control announces. */
|
|
33
|
-
label: string;
|
|
34
|
-
/** Whether the reader may submit the form without filling it. */
|
|
35
|
-
required: boolean;
|
|
36
|
-
/**
|
|
37
|
-
* A line under the control, announced with it.
|
|
38
|
-
*
|
|
39
|
-
* A node, because the honest line is sometimes not one sentence: a caller's
|
|
40
|
-
* hint, a link to the thing being asked about, a piece of the page's own copy.
|
|
41
|
-
* It is wired to the control through `aria-describedby` rather than drawn as a
|
|
42
|
-
* footnote, so a screen reader hears what this field is for as they reach it.
|
|
43
|
-
*/
|
|
44
|
-
description?: ReactNode;
|
|
45
|
-
/** What the control shows while it is empty. A hint, not a name. */
|
|
46
|
-
placeholder?: string;
|
|
47
|
-
/** The platform's own autofill hint for this field. */
|
|
48
|
-
autoComplete?: string;
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* One field of the caller's form, and the control it draws.
|
|
52
|
-
*
|
|
53
|
-
* **A union and not one type with an optional `options`, and that is the shape of
|
|
54
|
-
* the whole argument in this Block.** A contact form's fields are the caller's
|
|
55
|
-
* declaration, not this package's opinion about what a reader may be asked, and
|
|
56
|
-
* the one place that shows is the select: a select with no options is a control
|
|
57
|
-
* with nothing in it, which is a question no reader can answer, so `options` is
|
|
58
|
-
* required inside the select arm and forbidden beside the other four. A
|
|
59
|
-
* `options?: string[]` on one type would typecheck the defect and only the
|
|
60
|
-
* diagnostic at run time would catch it, which is one gate too late. The union is
|
|
61
|
-
* the same mechanism `HeroAction` uses for the same reason: the shape that
|
|
62
|
-
* requires something declares it, and the shape that forbids it says so in the
|
|
63
|
-
* type rather than in prose.
|
|
64
|
-
*
|
|
65
|
-
* The cost of the union is that a caller who wants to build a field list
|
|
66
|
-
* generically, from data, has to narrow before it can type the result, and the
|
|
67
|
-
* answer to that is a narrowing function in the caller's own code rather than a
|
|
68
|
-
* looser type here.
|
|
69
|
-
*/
|
|
70
|
-
export type ContactField = (ContactFieldBase & {
|
|
71
|
-
type: 'select';
|
|
72
|
-
/** The choices, in the order the reader should meet them. */
|
|
73
|
-
options: Contact01Option[];
|
|
74
|
-
}) | (ContactFieldBase & {
|
|
75
|
-
type: 'text' | 'email' | 'tel' | 'textarea';
|
|
76
|
-
/** A select with no options is a question nobody can answer, so it is not one here. */
|
|
77
|
-
options?: never;
|
|
78
|
-
});
|
|
79
19
|
/**
|
|
80
20
|
* Where a reader can reach a person, and the one address this Block links.
|
|
81
21
|
*
|
|
@@ -155,7 +95,7 @@ export type Contact01Props = {
|
|
|
155
95
|
*/
|
|
156
96
|
onSubmit: (value: ContactValue) => void;
|
|
157
97
|
/**
|
|
158
|
-
* The fields,
|
|
98
|
+
* The fields, grouped and ordered, as the shared field specification.
|
|
159
99
|
*
|
|
160
100
|
* **Required, and this list is the whole design rather than a convenience.** A
|
|
161
101
|
* contact form Component with a fixed set of fields is a Component that decides
|
|
@@ -165,15 +105,23 @@ export type Contact01Props = {
|
|
|
165
105
|
* be abandoned and retried through a support channel, a form that cannot ask for
|
|
166
106
|
* a company size sends a one-line hello to a sales team that then spends a week
|
|
167
107
|
* on discovery calls, and a form that cannot ask for a budget guarantees the
|
|
168
|
-
* reply nobody wanted.
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
108
|
+
* reply nobody wanted. The fields arrive as `FieldSpecGroup`, which is the same
|
|
109
|
+
* type a record write form takes, so the vocabulary a consumer learns on one
|
|
110
|
+
* screen is the vocabulary here, and a control this package does not ship is the
|
|
111
|
+
* `slot` arm rather than a second Block. The cost is stated rather than hidden: a
|
|
112
|
+
* consumer that wants a second column for a subset of its fields composes two
|
|
113
|
+
* `Contact01`s or reaches for a `Page`, because this Block draws one column in
|
|
114
|
+
* the order the list gives.
|
|
115
|
+
*/
|
|
116
|
+
groups: readonly FieldSpecGroup[];
|
|
117
|
+
/**
|
|
118
|
+
* The issues to draw, keyed by field. Drawn under the control that owns them.
|
|
119
|
+
*
|
|
120
|
+
* An entry names a field's `key` and a message in the product's own words, and
|
|
121
|
+
* a control's issue is drawn under that control. A message about the submission
|
|
122
|
+
* rather than about a control is the caller's `status`, which names no field.
|
|
175
123
|
*/
|
|
176
|
-
|
|
124
|
+
issues?: readonly Contact01Issue[];
|
|
177
125
|
/** The label on the one control that submits. */
|
|
178
126
|
submitLabel: string;
|
|
179
127
|
/**
|
|
@@ -314,6 +262,6 @@ export type Contact01Props = {
|
|
|
314
262
|
* nothing. The rest of the rendering is a section and five controls, which is the
|
|
315
263
|
* price of the argument above and not a claim about what else this Block might do.
|
|
316
264
|
*/
|
|
317
|
-
export declare function Contact01({ eyebrow, title, description, onSubmit,
|
|
265
|
+
export declare function Contact01({ eyebrow, title, description, onSubmit, groups, issues, submitLabel, consent, address, variant, status, headingLevel, className, }: Contact01Props): import("react").JSX.Element;
|
|
318
266
|
export default Contact01;
|
|
319
267
|
//# sourceMappingURL=contact.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../../src/blocks/contact-01/contact.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../../src/blocks/contact-01/contact.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,OAAO,CAAA;AAQjD,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAUxF,OAAO,KAAK,EAAwB,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG1E;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,yEAAyE;IACzE,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,GAAG,CACA;IACE,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAA;CACnB,GACD;IACE,6CAA6C;IAC7C,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,UAAU,CAAC,EAAE,KAAK,CAAA;CACnB,CACJ,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEjD;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IAC5C,4DAA4D;IAC5D,OAAO,EAAE,SAAS,CAAA;CACnB,CAAA;AAED,8EAA8E;AAC9E,MAAM,MAAM,cAAc,GAAG;IAC3B,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,iFAAiF;IACjF,KAAK,EAAE,SAAS,CAAA;IAChB,kFAAkF;IAClF,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;;OASG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IACvC;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,EAAE,SAAS,cAAc,EAAE,CAAA;IACjC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAA;IAClC,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,oFAAoF;IACpF,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,iEAAiE;IACjE,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAkED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,KAAK,EACL,WAAW,EACX,QAAQ,EACR,MAAM,EACN,MAAM,EACN,WAAW,EACX,OAAO,EACP,OAAO,EACP,OAAiB,EACjB,MAAM,EACN,YAAmB,EACnB,SAAS,GACV,EAAE,cAAc,+BAiOhB;AAED,eAAe,SAAS,CAAA"}
|