@nanisoft/prism-ui 0.16.1 → 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/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
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { ChevronDown, ListFilter,
|
|
4
|
-
import { useId, useState } from 'react';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown, ListFilter, Search, XIcon } from 'lucide-react';
|
|
4
|
+
import { Fragment, useEffect, useId, useRef, useState } from 'react';
|
|
5
|
+
import { Avatar } from '../../components/ui/avatar.js';
|
|
6
|
+
import { Badge } from '../../components/ui/badge.js';
|
|
5
7
|
import { Button } from '../../components/ui/button.js';
|
|
6
8
|
import { Checkbox } from '../../components/ui/checkbox.js';
|
|
7
|
-
import {
|
|
9
|
+
import { Checklist } from '../../components/ui/checklist.js';
|
|
10
|
+
import { CodeBlock } from '../../components/ui/code-block.js';
|
|
11
|
+
import { Diff } from '../../components/ui/diff.js';
|
|
12
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '../../components/ui/dropdown-menu.js';
|
|
8
13
|
import { Input } from '../../components/ui/input.js';
|
|
14
|
+
import { Kbd } from '../../components/ui/kbd.js';
|
|
15
|
+
import { LiveRegion } from '../../components/ui/live-region.js';
|
|
16
|
+
import { Mark } from '../../components/ui/mark.js';
|
|
9
17
|
import { Pagination, PaginationContent, PaginationItem } from '../../components/ui/pagination.js';
|
|
10
18
|
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover.js';
|
|
19
|
+
import { Price } from '../../components/ui/price.js';
|
|
20
|
+
import { RelativeTime } from '../../components/ui/relative-time.js';
|
|
11
21
|
import { headingSizeClass } from '../../components/ui/section.js';
|
|
22
|
+
import { Status } from '../../components/ui/status.js';
|
|
12
23
|
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, } from '../../components/ui/table.js';
|
|
24
|
+
import { TableSort } from '../../components/ui/table-sort.js';
|
|
25
|
+
import { TagGroup } from '../../components/ui/tag-group.js';
|
|
26
|
+
import { Text } from '../../components/ui/typography.js';
|
|
27
|
+
import { cn } from '../../lib/utils.js';
|
|
13
28
|
/** The page numbers to show, with `ellipsis` where a run is held back. */
|
|
14
29
|
function pageWindow(page, pageCount) {
|
|
15
30
|
if (pageCount <= 7) {
|
|
@@ -27,47 +42,193 @@ function pageWindow(page, pageCount) {
|
|
|
27
42
|
pages.push(pageCount);
|
|
28
43
|
return pages;
|
|
29
44
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
/** Where a value belongs in a cell, defaulting to the start. */
|
|
46
|
+
function alignClass(column) {
|
|
47
|
+
if (column.align === 'center')
|
|
48
|
+
return 'text-center';
|
|
49
|
+
if (column.align === 'end')
|
|
50
|
+
return 'text-end';
|
|
51
|
+
return 'text-start';
|
|
36
52
|
}
|
|
37
53
|
/**
|
|
38
|
-
*
|
|
54
|
+
* One cell, drawn from the column's kind and the row's value at its key.
|
|
39
55
|
*
|
|
40
|
-
* The
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
56
|
+
* The value at `column.key` is the cell's own content. A kind that names a
|
|
57
|
+
* single-node cell (`Typography`, `Badge`, `Avatar`, `Kbd`) draws the value as that
|
|
58
|
+
* Component's child; a kind that names a structured cell (`Price`, `RelativeTime`,
|
|
59
|
+
* `Status`, `TagGroup`, `Checklist`, `Diff`, `Mark`, `CodeBlock`) takes the value as
|
|
60
|
+
* that Component's props; and `slot` draws the value as the caller's own node.
|
|
61
|
+
*
|
|
62
|
+
* The map is keyed by the kind's lowercase name and every member of `ColumnKind`
|
|
63
|
+
* has one entry, so a kind added to the union fails the compiler here until it is
|
|
64
|
+
* drawn. The lowercase keys are machine values rather than copy, which is the same
|
|
65
|
+
* reason a variant name is written in lowercase everywhere else in this package.
|
|
66
|
+
*/
|
|
67
|
+
const CELL = {
|
|
68
|
+
typography: (value) => _jsx(Text, { as: "span", children: value }),
|
|
69
|
+
price: (value) => _jsx(Price, { ...value }),
|
|
70
|
+
relativetime: (value) => _jsx(RelativeTime, { ...value }),
|
|
71
|
+
status: (value) => _jsx(Status, { ...value }),
|
|
72
|
+
badge: (value) => _jsx(Badge, { children: value }),
|
|
73
|
+
avatar: (value) => _jsx(Avatar, { children: value }),
|
|
74
|
+
taggroup: (value) => _jsx(TagGroup, { ...value }),
|
|
75
|
+
checklist: (value) => _jsx(Checklist, { ...value }),
|
|
76
|
+
diff: (value) => _jsx(Diff, { ...value }),
|
|
77
|
+
mark: (value) => _jsx(Mark, { ...value }),
|
|
78
|
+
kbd: (value) => _jsx(Kbd, { children: value }),
|
|
79
|
+
codeblock: (value) => _jsx(CodeBlock, { ...value }),
|
|
80
|
+
slot: (value) => value,
|
|
81
|
+
};
|
|
82
|
+
/** The cell for one column and one row. */
|
|
83
|
+
function renderCell(column, row) {
|
|
84
|
+
return CELL[column.kind.toLowerCase()](row[column.key]);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The record index: a table of rows a reader scans and acts on.
|
|
88
|
+
*
|
|
89
|
+
* It draws the page of rows it was handed for the page it was told about, and it
|
|
90
|
+
* fetches nothing, sorts nothing, filters nothing and slices nothing. `columns` is
|
|
91
|
+
* the shared `ColumnSpec` typed data rather than a render function per column, so
|
|
92
|
+
* alignment, width, the header and the sort affordance are drawn once for every
|
|
93
|
+
* consumer. `getRowId` is required, because a row's position moves under sorting,
|
|
94
|
+
* filtering and paging and an index-keyed selection would act on the wrong records.
|
|
95
|
+
*
|
|
96
|
+
* **It is also the log and the event stream, drawing event rows from the shared event
|
|
97
|
+
* specification.** A consumer composing a delivery log or a console events screen
|
|
98
|
+
* passes the same `EventSpec` values an activity trail takes, and draws columns from
|
|
99
|
+
* them: the sending process in `actor`, the event type in `action`, the endpoint in
|
|
100
|
+
* `target`, the payload in a `CodeBlock` cell the caller composes, and the response
|
|
101
|
+
* status in a `Status` cell the caller composes rather than declared as a member,
|
|
102
|
+
* because the event specification has no kind and no severity. A log is a different
|
|
103
|
+
* job from the trail on the arrangement signal: it gains a filter region the trail
|
|
104
|
+
* does not draw, a second axis on which entries are compared, and a page boundary in
|
|
105
|
+
* place of a silent limit, and every one of those belongs to this Block rather than
|
|
106
|
+
* to a rail. So a screen that wants the events of one record composes
|
|
107
|
+
* `ActivityFeed01`, a screen that wants to find one entry among many composes this
|
|
108
|
+
* Block, and there is no second log Item beside it.
|
|
109
|
+
*
|
|
110
|
+
* **The Block composes no disclosure.** Seeing one entry in full is a destination the
|
|
111
|
+
* caller names with its required words, expanding a payload in place is the caller's
|
|
112
|
+
* own `Collapsible` placed in a `slot` cell, and taking an identifier away is the
|
|
113
|
+
* caller's own control composed beside a code block. A control whose handler arrives
|
|
114
|
+
* through a spread of unknown shape reads as carrying no handler, so the escape for a
|
|
115
|
+
* per-entry action is the `renderRowActions` node rather than a spread.
|
|
116
|
+
*
|
|
117
|
+
* **It draws no retention window, no archive, no purge, no legal hold and no export**,
|
|
118
|
+
* and no immutability mark, no time axis, no live tail, no aggregate over the
|
|
119
|
+
* population and no command of its own. A filter is a narrowing the caller passed and
|
|
120
|
+
* the Block states nothing about what is behind it. Row selection exists so the caller
|
|
121
|
+
* can drive a command against its own store.
|
|
122
|
+
*
|
|
123
|
+
* **It holds the selection set it was given, and reports every change.** This is the
|
|
124
|
+
* one piece of state a block in this repository keeps, and it is scoped by what the
|
|
125
|
+
* state is: a set of keys the caller passed for the page about to be drawn. Nothing
|
|
126
|
+
* in the block fetches, derives from application state or decides. A consumer that
|
|
127
|
+
* wants the set mirrored passes `selectedIds` and writes one line in
|
|
128
|
+
* `onSelectedIdsChange`; a consumer that wants nothing passes neither and the block
|
|
129
|
+
* still draws the tick, the header's three-state control, the count, the batch bar
|
|
130
|
+
* and the announcement.
|
|
131
|
+
*
|
|
132
|
+
* **The count is announced once, politely.** One `LiveRegion` at `polite` carries the
|
|
133
|
+
* count and is never assertive, and it is mounted while there is an announcement
|
|
134
|
+
* rather than always, so its appearance is the first thing spoken rather than a
|
|
135
|
+
* silent mutation of a node that was always there. It announces the count and never
|
|
136
|
+
* a list of labels, because each row's own checkbox already names its record. When
|
|
137
|
+
* the set empties, the region speaks its own sentence rather than a count of zero.
|
|
138
|
+
* The visible summary in the footer and the announced count are the same string.
|
|
139
|
+
*
|
|
140
|
+
* **The batch bar is the block's region and the caller's controls.** It mounts only
|
|
141
|
+
* while the set is non-empty, displaces the filter controls rather than sitting
|
|
142
|
+
* beside them, and holds one required `batchActions` node the caller wrote plus the
|
|
143
|
+
* block's own dismiss. The dismiss is the only control the block draws, and its whole
|
|
144
|
+
* body is the same report every tick makes with an empty set.
|
|
145
|
+
*
|
|
146
|
+
* **Per-row actions are a node per row.** The block draws the trailing cell and
|
|
147
|
+
* places `renderRowActions(row)` inside it, so a row that navigates and a row that is
|
|
148
|
+
* ticked are two separately reachable targets and the block owns no command.
|
|
149
|
+
*
|
|
150
|
+
* **Grouping is a key on a row, not a second arrangement.** `groupBy` returns the key
|
|
151
|
+
* a row belongs to and the block draws `groupLabel(key)` in a full-width row above
|
|
152
|
+
* each run of rows that shares one, in the caller's own order. A heading is a row in
|
|
153
|
+
* the same body as any other and it is not a record: it is never in the selection
|
|
154
|
+
* set, never in the count and never what the header's page scope counts.
|
|
155
|
+
*
|
|
156
|
+
* **The two selection scopes are announced differently.** A page-wide selection is
|
|
157
|
+
* one the block can count, so it speaks `labels.selectedCount`. A filter-wide
|
|
158
|
+
* selection is a population the block never fetched, so the caller reports it
|
|
159
|
+
* through `selectionScope` and the block speaks `labels.selectedAllMatching` with the
|
|
160
|
+
* caller's own number rather than printing the page as if it were the total.
|
|
45
161
|
*/
|
|
46
|
-
export function DataTable01(
|
|
162
|
+
export function DataTable01(props) {
|
|
163
|
+
const { title, description, columns, rows, getRowId, caption, headingLevel = 'h2', sort: sortProp, onSortChange, searchValue, onSearchChange, filters, filterValues, onFilterChange, page: pageProp, defaultPage = 1, pageCount, onPageChange, renderRowActions, toolbarActions, emptyMessage, labels, groupBy, groupLabel, } = props;
|
|
47
164
|
const Heading = headingLevel;
|
|
48
165
|
// The handle the table names itself from; see the note on the table.
|
|
49
166
|
const headingId = useId();
|
|
50
|
-
|
|
167
|
+
// The prefix the per-cell help ids are built from.
|
|
168
|
+
const helpPrefix = useId();
|
|
169
|
+
const isSelectable = props.selectable === true;
|
|
170
|
+
const [internalSelected, setInternalSelected] = useState(() => [
|
|
171
|
+
...(props.defaultSelectedIds ?? []),
|
|
172
|
+
]);
|
|
51
173
|
const [internalPage, setInternalPage] = useState(defaultPage);
|
|
52
174
|
const [internalSearch, setInternalSearch] = useState('');
|
|
53
175
|
const [internalFilters, setInternalFilters] = useState({});
|
|
176
|
+
const [internalSort, setInternalSort] = useState(null);
|
|
54
177
|
const [hiddenColumns, setHiddenColumns] = useState([]);
|
|
55
|
-
const selected =
|
|
178
|
+
const selected = isSelectable
|
|
179
|
+
? props.selectedIds === undefined
|
|
180
|
+
? internalSelected
|
|
181
|
+
: [...props.selectedIds]
|
|
182
|
+
: [];
|
|
56
183
|
const page = pageProp ?? internalPage;
|
|
57
184
|
const search = searchValue ?? internalSearch;
|
|
58
185
|
const activeFilters = filterValues ?? internalFilters;
|
|
186
|
+
const sortState = sortProp !== undefined ? sortProp : internalSort;
|
|
59
187
|
const clampPage = (value) => Math.min(Math.max(1, value), Math.max(1, pageCount));
|
|
60
188
|
const currentPage = clampPage(page);
|
|
61
|
-
const visibleColumns = columns.filter((column) => !hiddenColumns.includes(column.
|
|
62
|
-
const hasRowActions =
|
|
63
|
-
const columnCount = (
|
|
189
|
+
const visibleColumns = columns.filter((column) => !hiddenColumns.includes(column.key));
|
|
190
|
+
const hasRowActions = renderRowActions !== undefined;
|
|
191
|
+
const columnCount = (isSelectable ? 1 : 0) + visibleColumns.length + (hasRowActions ? 1 : 0);
|
|
192
|
+
// The scope the caller reports. Absent means the page, which is the one
|
|
193
|
+
// population the block can count for itself.
|
|
194
|
+
const scope = isSelectable ? props.selectionScope : undefined;
|
|
195
|
+
const filterScope = scope?.scope === 'filter' ? scope : undefined;
|
|
196
|
+
// A filter-wide selection is a selection even though its keys are not on screen,
|
|
197
|
+
// so the bar mounts and the region speaks from the scope rather than from the
|
|
198
|
+
// page's keys. The number is the caller's and is never the rows on screen.
|
|
199
|
+
const selectionActive = isSelectable && (filterScope !== undefined || selected.length > 0);
|
|
200
|
+
const selectionSummary = filterScope !== undefined
|
|
201
|
+
? labels.selectedAllMatching(filterScope.count)
|
|
202
|
+
: labels.selectedCount(selected.length);
|
|
64
203
|
const rowIds = rows.map(getRowId);
|
|
65
|
-
|
|
66
|
-
|
|
204
|
+
// A filter-wide selection covers the page whether or not the page's keys were
|
|
205
|
+
// passed, so the header reads as fully selected on that arm. A heading is not in
|
|
206
|
+
// `rowIds`, so grouping never changes what these two say.
|
|
207
|
+
const allSelected = filterScope !== undefined || (rowIds.length > 0 && rowIds.every((id) => selected.includes(id)));
|
|
208
|
+
const someSelected = filterScope !== undefined || rowIds.some((id) => selected.includes(id));
|
|
209
|
+
// The emptying sentence. A live region speaks when its text changes, and the
|
|
210
|
+
// sequence where the selection falls to nothing is the one the reader has to be told
|
|
211
|
+
// about because the batch bar they were about to use disappears with it. The
|
|
212
|
+
// previous presence is ref-held, so the render that reaches nothing still knows
|
|
213
|
+
// there was a selection and can speak the sentence rather than the absence of one.
|
|
214
|
+
const previousActive = useRef(selectionActive);
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
previousActive.current = selectionActive;
|
|
217
|
+
}, [selectionActive]);
|
|
218
|
+
const announcement = selectionActive
|
|
219
|
+
? selectionSummary
|
|
220
|
+
: previousActive.current
|
|
221
|
+
? labels.clearedSelection
|
|
222
|
+
: null;
|
|
67
223
|
function changeSelection(ids) {
|
|
68
|
-
if (
|
|
224
|
+
if (isSelectable === false)
|
|
225
|
+
return;
|
|
226
|
+
if (props.selectedIds === undefined)
|
|
69
227
|
setInternalSelected(ids);
|
|
70
|
-
onSelectedIdsChange?.(ids);
|
|
228
|
+
props.onSelectedIdsChange?.(ids);
|
|
229
|
+
}
|
|
230
|
+
function clearSelection() {
|
|
231
|
+
changeSelection([]);
|
|
71
232
|
}
|
|
72
233
|
function toggleAll(checked) {
|
|
73
234
|
if (checked) {
|
|
@@ -103,18 +264,39 @@ export function DataTable01({ title, description, columns, rows, getRowId, capti
|
|
|
103
264
|
setInternalPage(clamped);
|
|
104
265
|
onPageChange?.(clamped);
|
|
105
266
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
267
|
+
function changeSort(column, direction) {
|
|
268
|
+
const next = direction === 'none' ? null : { column, direction };
|
|
269
|
+
if (sortProp === undefined)
|
|
270
|
+
setInternalSort(next);
|
|
271
|
+
onSortChange?.(next);
|
|
272
|
+
}
|
|
273
|
+
function toggleColumn(key, visible) {
|
|
274
|
+
setHiddenColumns((previous) => visible ? previous.filter((item) => item !== key) : [...previous, key]);
|
|
108
275
|
}
|
|
109
276
|
const activeFilterCount = Object.values(activeFilters).filter(Boolean).length;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
277
|
+
// The key of the row before this one, so a heading belongs to a run of rows and
|
|
278
|
+
// not to a row. It resets every render and is read by the body below.
|
|
279
|
+
let previousGroupKey;
|
|
280
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [title || description ? (_jsxs("div", { className: "flex flex-col gap-1", children: [title ? (_jsx(Heading, { id: headingId, className: `font-semibold tracking-tight text-balance ${headingSizeClass(headingLevel)}`, children: title })) : null, description ? (_jsx("p", { className: "text-muted-foreground text-sm", children: description })) : null] })) : null, _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: selectionActive ? (_jsxs("div", { "data-slot": "data-table-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": "data-table-01-dismiss", type: "button", variant: "ghost", size: "sm", "aria-label": labels.dismissSelection, onClick: clearSelection, children: _jsx(XIcon, { "aria-hidden": "true" }) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative", children: [_jsx(Search, { className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2" }), _jsx(Input, { value: search, onChange: (event) => changeSearch(event.target.value), placeholder: labels.search, "aria-label": labels.search, className: "h-9 w-56 pl-8" })] }), filters?.length ? (_jsxs(Popover, { children: [_jsxs(PopoverTrigger, { children: [_jsx(ListFilter, { className: "size-4" }), labels.filters, activeFilterCount > 0 ? (_jsx("span", { className: "bg-primary text-primary-foreground rounded-full px-1.5 text-xs", children: activeFilterCount })) : null] }), _jsxs(PopoverContent, { align: "start", className: "flex w-64 flex-col gap-4", children: [filters.map((filter) => (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "text-sm font-medium", children: filter.label }), _jsx("div", { className: "flex flex-wrap gap-1.5", children: filter.options.map((option) => {
|
|
281
|
+
const active = activeFilters[filter.id] === option.value;
|
|
282
|
+
return (_jsx(Button, { type: "button", size: "sm", variant: active ? 'default' : 'outline', onClick: () => changeFilter(filter.id, active ? undefined : option.value), children: option.label }, option.value));
|
|
283
|
+
}) })] }, filter.id))), _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: activeFilterCount === 0, onClick: resetFilters, children: labels.reset })] })] })) : null] })) }), _jsxs("div", { className: "flex items-center gap-2", children: [toolbarActions, _jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { children: [labels.viewColumns, _jsx(ChevronDown, { className: "size-4" })] }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: labels.columns }), _jsx(DropdownMenuSeparator, {}), columns.map((column) => (_jsx(DropdownMenuCheckboxItem, { checked: !hiddenColumns.includes(column.key), closeOnClick: false, onCheckedChange: (checked) => toggleColumn(column.key, checked), children: typeof column.header === 'string' ? column.header : column.key }, column.key)))] })] })] })] }), _jsx(LiveRegion, { "data-slot": "data-table-01-announcement", politeness: "polite", className: "sr-only", children: announcement }), _jsx("div", { className: "border-border overflow-hidden rounded-xl border", children: _jsxs(Table, { "aria-labelledby": caption === undefined && title ? headingId : undefined, children: [caption ? _jsx(TableCaption, { children: caption }) : null, _jsx(TableHeader, { children: _jsxs(TableRow, { children: [isSelectable ? (_jsx(TableHead, { className: "w-10", children: _jsx(Checkbox, { checked: allSelected, indeterminate: !allSelected && someSelected, onCheckedChange: toggleAll, "aria-label": labels.selectAll }) })) : null, visibleColumns.map((column) => column.sortable ? (_jsx(TableSort, { column: column.key, direction: sortState?.column === column.key ? sortState.direction : 'none', onDirectionChange: (direction) => changeSort(column.key, direction), announce: (direction) => labels.sort(column.key, direction), className: cn(column.width), children: column.header }, column.key)) : (_jsx(TableHead, { className: cn(column.width, alignClass(column)), children: column.header }, column.key))), hasRowActions ? (_jsx(TableHead, { className: "w-10", children: _jsx("span", { className: "sr-only", children: labels.rowActions }) })) : null] }) }), _jsx(TableBody, { children: rows.length ? (rows.map((row, rowIndex) => {
|
|
114
284
|
const id = getRowId(row);
|
|
115
285
|
const isSelected = selected.includes(id);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
286
|
+
// A heading is drawn once per run of rows with one key, in the
|
|
287
|
+
// caller's own order. A row with no key starts no heading and is not
|
|
288
|
+
// a record that a group owns; it is simply ungrouped.
|
|
289
|
+
const rowGroupKey = groupBy?.(row);
|
|
290
|
+
const startsGroup = groupBy !== undefined &&
|
|
291
|
+
rowGroupKey !== undefined &&
|
|
292
|
+
rowGroupKey !== previousGroupKey;
|
|
293
|
+
previousGroupKey = rowGroupKey;
|
|
294
|
+
return (_jsxs(Fragment, { children: [startsGroup ? (_jsx(TableRow, { "data-slot": "data-table-01-group", children: _jsx(TableCell, { colSpan: columnCount, className: "text-muted-foreground bg-muted text-sm font-medium", children: groupLabel?.(rowGroupKey) }) })) : null, _jsxs(TableRow, { "data-state": isSelected ? 'selected' : undefined, children: [isSelectable ? (_jsx(TableCell, { className: "w-10", children: _jsx(Checkbox, { checked: isSelected, onCheckedChange: (checked) => toggleRow(id, checked), "aria-label": labels.selectRow(row) }) })) : null, visibleColumns.map((column, columnIndex) => {
|
|
295
|
+
const helpId = column.help === undefined
|
|
296
|
+
? undefined
|
|
297
|
+
: `${helpPrefix}-help-${columnIndex}-${rowIndex}`;
|
|
298
|
+
return (_jsxs(TableCell, { className: cn(column.width, alignClass(column)), "aria-describedby": helpId, children: [renderCell(column, row), helpId === undefined ? null : (_jsx("span", { id: helpId, className: "sr-only", children: column.help }))] }, column.key));
|
|
299
|
+
}), hasRowActions ? (_jsx(TableCell, { className: "text-right", children: renderRowActions?.(row) })) : null] })] }, id));
|
|
300
|
+
})) : (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: columnCount, className: "text-muted-foreground h-24 text-center", 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 }) })] }) })] })] }));
|
|
119
301
|
}
|
|
120
302
|
export default DataTable01;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { DataTable01, default } from './data-table.js';
|
|
2
|
-
export type { DataTable01Props, DataTable01Labels,
|
|
2
|
+
export type { DataTable01Props, DataTable01Labels, DataTable01SelectionScope, DataTableFilter, DataTableFilterOption, DataTableRow, DataTableSort, } from './data-table.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/data-table-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACnD,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/data-table-01/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACnD,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EACzB,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,aAAa,GACd,MAAM,cAAc,CAAA"}
|
|
@@ -182,7 +182,7 @@ export type Directory01Props = {
|
|
|
182
182
|
*/
|
|
183
183
|
empty: ReactNode;
|
|
184
184
|
/**
|
|
185
|
-
* The
|
|
185
|
+
* The result line, written the way four products write it.
|
|
186
186
|
*
|
|
187
187
|
* A function and not a node, because the count a reader reads is a sentence whose
|
|
188
188
|
* word order and noun inflection belong to their language, and a Block that composed
|
|
@@ -192,6 +192,27 @@ export type Directory01Props = {
|
|
|
192
192
|
* paint.
|
|
193
193
|
*/
|
|
194
194
|
summary?: (matches: number) => ReactNode;
|
|
195
|
+
/**
|
|
196
|
+
* The caller's own control for one member, placed without styling at the member's
|
|
197
|
+
* trailing edge.
|
|
198
|
+
*
|
|
199
|
+
* A node per member rather than one node for the directory, because what a caller
|
|
200
|
+
* puts here acts on the member it sits beside: a plugin's enable is a `Switch`
|
|
201
|
+
* whose `onCheckedChange` names that plugin, and one node reused across fifty
|
|
202
|
+
* members could not. It is a function in the shape `DataTable01`'s
|
|
203
|
+
* `renderRowActions` already takes, so the two per-row action shapes in this
|
|
204
|
+
* package are one shape and not two.
|
|
205
|
+
*
|
|
206
|
+
* **The Block draws no control of its own here, and that is the whole reason the
|
|
207
|
+
* prop is a node.** `scripts/check-block-controls.mjs` classifies `Switch` and
|
|
208
|
+
* fails a Block that renders one without its handler, and a Block ships no
|
|
209
|
+
* behaviour and so cannot supply one. So a member's action arrives as the caller's
|
|
210
|
+
* own node, holding the caller's own control and its own handler, and the Block
|
|
211
|
+
* places it in the member's trailing edge in both arrangements and draws nothing
|
|
212
|
+
* where it is not passed. Until a directory wants one of these, this is absent and
|
|
213
|
+
* a member is a name, a role, the tags and a destination.
|
|
214
|
+
*/
|
|
215
|
+
renderMemberAction?: (member: Directory01Member) => ReactNode;
|
|
195
216
|
/**
|
|
196
217
|
* How the set is drawn.
|
|
197
218
|
*
|
|
@@ -311,6 +332,6 @@ export type Directory01Props = {
|
|
|
311
332
|
* itself is a substring test per member and holds no index of its own, so the client
|
|
312
333
|
* cost is the derivation and not a running loop.
|
|
313
334
|
*/
|
|
314
|
-
export declare function Directory01({ eyebrow, title, description, value, onValueChange, label, clearLabel, categories, empty, summary, variant, headingLevel, className, }: Directory01Props): import("react").JSX.Element;
|
|
335
|
+
export declare function Directory01({ eyebrow, title, description, value, onValueChange, label, clearLabel, categories, empty, summary, renderMemberAction, variant, headingLevel, className, }: Directory01Props): import("react").JSX.Element;
|
|
315
336
|
export default Directory01;
|
|
316
337
|
//# sourceMappingURL=directory-01.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/directory-01/directory-01.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQtC,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAA;AA4EpC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE;QACP,2EAA2E;QAC3E,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,wDAAwD;QACxD,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAChD,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,0FAA0F;IAC1F,EAAE,EAAE,MAAM,CAAA;IACV,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,sEAAsE;IACtE,OAAO,EAAE,iBAAiB,EAAE,CAAA;CAC7B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAA;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,mFAAmF;IACnF,KAAK,EAAE,SAAS,CAAA;IAChB,kFAAkF;IAClF,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,UAAU,EAAE,mBAAmB,EAAE,CAAA;IACjC;;;;;;;;;;OAUG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAA;IACxC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,iEAAiE;IACjE,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,aAAa,EACb,KAAK,EACL,UAAU,EACV,UAAU,EACV,KAAK,EACL,OAAO,EACP,OAAiB,EACjB,YAAmB,EACnB,SAAS,GACV,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"directory-01.d.ts","sourceRoot":"","sources":["../../../src/blocks/directory-01/directory-01.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQtC,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAA;AA4EpC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAA;IACV,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;;;;OAWG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE;QACP,2EAA2E;QAC3E,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,wDAAwD;QACxD,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAChD,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,0FAA0F;IAC1F,EAAE,EAAE,MAAM,CAAA;IACV,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,sEAAsE;IACtE,OAAO,EAAE,iBAAiB,EAAE,CAAA;CAC7B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAA;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,mFAAmF;IACnF,KAAK,EAAE,SAAS,CAAA;IAChB,kFAAkF;IAClF,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,UAAU,EAAE,mBAAmB,EAAE,CAAA;IACjC;;;;;;;;;;OAUG;IACH,KAAK,EAAE,SAAS,CAAA;IAChB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAA;IACxC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,SAAS,CAAA;IAC7D;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,iEAAiE;IACjE,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,WAAW,EACX,KAAK,EACL,aAAa,EACb,KAAK,EACL,UAAU,EACV,UAAU,EACV,KAAK,EACL,OAAO,EACP,kBAAkB,EAClB,OAAiB,EACjB,YAAmB,EACnB,SAAS,GACV,EAAE,gBAAgB,+BAyTlB;AAED,eAAe,WAAW,CAAA"}
|
|
@@ -196,7 +196,7 @@ function assertMembers(members) {
|
|
|
196
196
|
* itself is a substring test per member and holds no index of its own, so the client
|
|
197
197
|
* cost is the derivation and not a running loop.
|
|
198
198
|
*/
|
|
199
|
-
export function Directory01({ eyebrow, title, description, value, onValueChange, label, clearLabel, categories, empty, summary, variant = 'cards', headingLevel = 'h2', className, }) {
|
|
199
|
+
export function Directory01({ eyebrow, title, description, value, onValueChange, label, clearLabel, categories, empty, summary, renderMemberAction, variant = 'cards', headingLevel = 'h2', className, }) {
|
|
200
200
|
// A category's title is a heading one step below the section that introduces the
|
|
201
201
|
// set, and a member's name is a step below that, so a reader who searches a
|
|
202
202
|
// directory and then navigates by heading lands on the team and then on the person
|
|
@@ -239,7 +239,7 @@ export function Directory01({ eyebrow, title, description, value, onValueChange,
|
|
|
239
239
|
// is one set of elements whichever arrangement is drawn and a consumer
|
|
240
240
|
// who switches `variant` at render time gets one list in the
|
|
241
241
|
// accessibility tree rather than two.
|
|
242
|
-
asCards ? 'grid items-start gap-6 md:grid-cols-2 lg:grid-cols-3' : null), children: visible.map((category) => (_jsxs("section", { "data-slot": "directory-01-category", "data-category": category.id, "aria-labelledby": `${category.id}-heading`, className: "flex min-w-0 flex-col gap-4", children: [_jsxs("div", { "data-slot": "directory-01-category-head", className: "flex flex-col gap-1.5", children: [_jsx(Title, { id: `${category.id}-heading`, "data-slot": "directory-01-category-title", className: "text-lg font-semibold tracking-tight", children: category.title }), category.description === undefined ? null : (_jsx("p", { "data-slot": "directory-01-category-description", className: "text-muted-foreground text-pretty text-sm", children: category.description }))] }), asCards ? (_jsx("ul", { "data-slot": "directory-01-members", className: "grid gap-4 sm:grid-cols-2", children: category.members.map((member) => (_jsx("li", { "data-slot": "directory-01-card", "data-member": member.id, className: "flex", children: _jsxs(Card, { "data-slot": "directory-01-card-surface", className: "w-full gap-4 py-5", children: [_jsxs(CardHeader, { className: "gap-3", children: [_jsxs("div", { "data-slot": "directory-01-identity", className: "flex min-w-0 gap-3", children: [member.avatar === undefined ? null : (_jsxs(Avatar, { className: "size-10 shrink-0", children: [member.avatar.src === undefined ? null : (_jsx(AvatarImage, { src: member.avatar.src, alt: "" })), _jsx(AvatarFallback, { className: "text-mono", children: initialsOf(member.avatar.name) })] })), _jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [_jsx(CardTitle, { as: Member, "data-slot": "directory-01-name", className: "truncate text-sm leading-snug", children: member.name }), member.role === undefined ? null : (_jsx("span", { "data-slot": "directory-01-role", className: "text-muted-foreground text-xs", children: member.role }))] })] }), member.tags === undefined || member.tags.length === 0 ? null : (_jsx("div", { "data-slot": "directory-01-tags", className: "flex flex-wrap items-center gap-1.5", children: member.tags.map((tag) => (_jsx(Badge, { variant: "secondary", children: tag.label }, tag.id))) }))] }),
|
|
242
|
+
asCards ? 'grid items-start gap-6 md:grid-cols-2 lg:grid-cols-3' : null), children: visible.map((category) => (_jsxs("section", { "data-slot": "directory-01-category", "data-category": category.id, "aria-labelledby": `${category.id}-heading`, className: "flex min-w-0 flex-col gap-4", children: [_jsxs("div", { "data-slot": "directory-01-category-head", className: "flex flex-col gap-1.5", children: [_jsx(Title, { id: `${category.id}-heading`, "data-slot": "directory-01-category-title", className: "text-lg font-semibold tracking-tight", children: category.title }), category.description === undefined ? null : (_jsx("p", { "data-slot": "directory-01-category-description", className: "text-muted-foreground text-pretty text-sm", children: category.description }))] }), asCards ? (_jsx("ul", { "data-slot": "directory-01-members", className: "grid gap-4 sm:grid-cols-2", children: category.members.map((member) => (_jsx("li", { "data-slot": "directory-01-card", "data-member": member.id, className: "flex", children: _jsxs(Card, { "data-slot": "directory-01-card-surface", className: "w-full gap-4 py-5", children: [_jsxs(CardHeader, { className: "gap-3", children: [_jsxs("div", { "data-slot": "directory-01-identity", className: "flex min-w-0 gap-3", children: [member.avatar === undefined ? null : (_jsxs(Avatar, { className: "size-10 shrink-0", children: [member.avatar.src === undefined ? null : (_jsx(AvatarImage, { src: member.avatar.src, alt: "" })), _jsx(AvatarFallback, { className: "text-mono", children: initialsOf(member.avatar.name) })] })), _jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [_jsx(CardTitle, { as: Member, "data-slot": "directory-01-name", className: "truncate text-sm leading-snug", children: member.name }), member.role === undefined ? null : (_jsx("span", { "data-slot": "directory-01-role", className: "text-muted-foreground text-xs", children: member.role }))] })] }), member.tags === undefined || member.tags.length === 0 ? null : (_jsx("div", { "data-slot": "directory-01-tags", className: "flex flex-wrap items-center gap-1.5", children: member.tags.map((tag) => (_jsx(Badge, { variant: "secondary", children: tag.label }, tag.id))) }))] }), _jsxs(CardFooter, { className: "gap-2", children: [_jsx(CtaLink, { href: member.href, variant: "outline", size: "sm", children: member.hrefLabel }), renderMemberAction === undefined ? null : (_jsx("span", { "data-slot": "directory-01-member-action", className: "flex items-center", children: renderMemberAction(member) }))] })] }) }, member.id))) })) : (
|
|
243
243
|
/*
|
|
244
244
|
* A list rather than a grid of cards, because in this arrangement
|
|
245
245
|
* the portrait belongs at the leading edge of a line and the badges
|
|
@@ -247,6 +247,6 @@ export function Directory01({ eyebrow, title, description, value, onValueChange,
|
|
|
247
247
|
* border rather than a gap alone, so the boundary survives at any
|
|
248
248
|
* zoom level where the gap stops being visible.
|
|
249
249
|
*/
|
|
250
|
-
_jsx("ul", { "data-slot": "directory-01-members", className: "border-border flex flex-col border-t", children: category.members.map((member) => (_jsxs("li", { "data-slot": "directory-01-row", "data-member": member.id, className: "border-border flex flex-wrap items-center gap-x-4 gap-y-2 border-b py-4 last:border-b-0", children: [member.avatar === undefined ? null : (_jsxs(Avatar, { className: "size-8 shrink-0", children: [member.avatar.src === undefined ? null : (_jsx(AvatarImage, { src: member.avatar.src, alt: "" })), _jsx(AvatarFallback, { className: "text-mono", children: initialsOf(member.avatar.name) })] })), _jsx(Member, { "data-slot": "directory-01-name", className: "min-w-0 truncate text-sm font-medium", children: member.name }), member.role === undefined ? null : (_jsx("span", { "data-slot": "directory-01-role", className: "text-muted-foreground min-w-0 flex-1 truncate text-sm", children: member.role })), member.tags === undefined || member.tags.length === 0 ? null : (_jsx("span", { "data-slot": "directory-01-tags", className: "flex flex-wrap items-center gap-1.5", children: member.tags.map((tag) => (_jsx(Badge, { variant: "secondary", children: tag.label }, tag.id))) })), _jsx(CtaLink, { href: member.href, variant: "outline", size: "sm", className: "ms-auto shrink-0", children: member.hrefLabel })] }, member.id))) }))] }, category.id))) }))] }) }));
|
|
250
|
+
_jsx("ul", { "data-slot": "directory-01-members", className: "border-border flex flex-col border-t", children: category.members.map((member) => (_jsxs("li", { "data-slot": "directory-01-row", "data-member": member.id, className: "border-border flex flex-wrap items-center gap-x-4 gap-y-2 border-b py-4 last:border-b-0", children: [member.avatar === undefined ? null : (_jsxs(Avatar, { className: "size-8 shrink-0", children: [member.avatar.src === undefined ? null : (_jsx(AvatarImage, { src: member.avatar.src, alt: "" })), _jsx(AvatarFallback, { className: "text-mono", children: initialsOf(member.avatar.name) })] })), _jsx(Member, { "data-slot": "directory-01-name", className: "min-w-0 truncate text-sm font-medium", children: member.name }), member.role === undefined ? null : (_jsx("span", { "data-slot": "directory-01-role", className: "text-muted-foreground min-w-0 flex-1 truncate text-sm", children: member.role })), member.tags === undefined || member.tags.length === 0 ? null : (_jsx("span", { "data-slot": "directory-01-tags", className: "flex flex-wrap items-center gap-1.5", children: member.tags.map((tag) => (_jsx(Badge, { variant: "secondary", children: tag.label }, tag.id))) })), _jsx(CtaLink, { href: member.href, variant: "outline", size: "sm", className: "ms-auto shrink-0", children: member.hrefLabel }), renderMemberAction === undefined ? null : (_jsx("span", { "data-slot": "directory-01-member-action", className: "flex shrink-0 items-center", children: renderMemberAction(member) }))] }, member.id))) }))] }, category.id))) }))] }) }));
|
|
251
251
|
}
|
|
252
252
|
export default Directory01;
|
|
@@ -1,34 +1,55 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
3
|
+
* The four reasons a region has nothing in it.
|
|
4
4
|
*
|
|
5
|
-
* **
|
|
5
|
+
* **Four, and the set is closed at four because the four want different words and
|
|
6
6
|
* different actions, so a consumer who cannot say which one they have writes
|
|
7
7
|
* "No data".** That is the whole failure this Block exists to prevent, and it is
|
|
8
|
-
* a failure rather than a style preference because
|
|
9
|
-
*
|
|
8
|
+
* a failure rather than a style preference because all four answer one question
|
|
9
|
+
* and the reader acts on each answer differently. The question is what happened
|
|
10
|
+
* to the collection this region would have drawn:
|
|
10
11
|
*
|
|
11
12
|
* - `first-run` - nothing has ever existed here. The reader is not blocked, they
|
|
12
13
|
* are at the beginning, and the action is to *make the first one*. A sentence
|
|
13
14
|
* that says nothing is available and an action that creates is the whole state.
|
|
14
|
-
* - `no-match` - things exist and the reader's own
|
|
15
|
+
* - `no-match` - things exist and the reader's own narrowing removed all of them.
|
|
15
16
|
* The reader is not at the beginning and nothing is missing; the action is to
|
|
16
|
-
* *widen or clear the
|
|
17
|
+
* *widen or clear the narrowing*, and offering to create something here invents
|
|
17
18
|
* work the reader does not need.
|
|
19
|
+
* - `emptied-by-reader` - things existed here and this reader's own earlier action
|
|
20
|
+
* moved every one of them out of it. The reader is not at the beginning either,
|
|
21
|
+
* and nothing is missing or hidden: this is a bin the reader has just cleared.
|
|
22
|
+
* There is no action *in this region*, because a reader who has deleted nothing
|
|
23
|
+
* has nothing to restore, and restoring is a per row command and a batch action
|
|
24
|
+
* in the surrounding surface, on the caller's own nodes.
|
|
18
25
|
* - `not-permitted` - things exist and this reader may not see them. The action
|
|
19
26
|
* is to *ask for access or change context*, and a create action here is a lie
|
|
20
27
|
* about what the reader can do, which is worse than no action at all.
|
|
21
28
|
*
|
|
22
|
-
*
|
|
29
|
+
* **Two of the four are the reader's own doing, and a narrowing and a deletion are
|
|
30
|
+
* not the same doing, so they are two members rather than one.** `no-match` hides
|
|
31
|
+
* rows out of a set that is still there, so clearing it brings them back;
|
|
32
|
+
* `emptied-by-reader` is a record that is gone from the live set rather than hidden
|
|
33
|
+
* in it, and the reader put it there. Every saved view a caller builds out of the
|
|
34
|
+
* filter values it holds is already `no-match` and wants no member of its own,
|
|
35
|
+
* because a view that matches nothing is a narrowing that came back empty. A trash
|
|
36
|
+
* view is the one that differs in kind rather than in content: it is a different
|
|
37
|
+
* collection the caller chose to fetch rather than a narrowing of this one. So the
|
|
38
|
+
* two differ in exactly the fact a reader must be told honestly, which is whether
|
|
39
|
+
* the records are still there behind the narrowing or gone and only the caller can
|
|
40
|
+
* bring them back. One sentence for both would tell a reader who has deleted
|
|
41
|
+
* everything that their own search found nothing.
|
|
42
|
+
*
|
|
43
|
+
* **A fifth such as `error` or `loading` is deliberately absent:** those are states
|
|
23
44
|
* the surrounding surface already owns, and a region that is erroring has an
|
|
24
45
|
* `Alert` and a region that is loading has a `Spinner`. A Block that drew them
|
|
25
46
|
* would give a consumer two places to put the same fact.
|
|
26
47
|
*
|
|
27
48
|
* @see EMPTY_REASONS
|
|
28
49
|
*/
|
|
29
|
-
export declare const EMPTY_REASONS: readonly ["first-run", "no-match", "not-permitted"];
|
|
50
|
+
export declare const EMPTY_REASONS: readonly ["first-run", "no-match", "emptied-by-reader", "not-permitted"];
|
|
30
51
|
/**
|
|
31
|
-
* One of the
|
|
52
|
+
* One of the four reasons a region has nothing in it.
|
|
32
53
|
*
|
|
33
54
|
* The reason is a machine value and never reader-facing text. The words are the
|
|
34
55
|
* consumer's: two products call the same state "Nothing here yet" and "Your
|
|
@@ -45,12 +66,15 @@ export type EmptyReason = (typeof EMPTY_REASONS)[number];
|
|
|
45
66
|
*/
|
|
46
67
|
export type EmptyState01Props = {
|
|
47
68
|
/**
|
|
48
|
-
* Which of the
|
|
69
|
+
* Which of the four situations this is. Required rather than defaulted,
|
|
49
70
|
* because the default would be the one that hides the bug: a Block that
|
|
50
71
|
* defaulted to `first-run` would render a create action beside a permission
|
|
51
72
|
* boundary, and a Block that defaulted to `no-match` would tell every reader
|
|
52
73
|
* their own filter removed something on their first visit. Making the caller
|
|
53
|
-
* name the reason is what stops either from happening quietly
|
|
74
|
+
* name the reason is what stops either from happening quietly, and it is what
|
|
75
|
+
* separates the two the reader emptied themselves: passing `no-match` beside a
|
|
76
|
+
* bin the reader just cleared says their search found nothing in it, which is
|
|
77
|
+
* false, because nothing there was there to be filtered.
|
|
54
78
|
*/
|
|
55
79
|
reason: EmptyReason;
|
|
56
80
|
/**
|
|
@@ -69,11 +93,15 @@ export type EmptyState01Props = {
|
|
|
69
93
|
* The label of the one action, in the product's own words.
|
|
70
94
|
*
|
|
71
95
|
* Optional, and its absence is a legitimate state rather than a gap: a region
|
|
72
|
-
* a reader cannot act on - a permission boundary,
|
|
73
|
-
* feature that is switched off for their plan
|
|
74
|
-
*
|
|
75
|
-
* help them. `reason` does not
|
|
76
|
-
*
|
|
96
|
+
* a reader cannot act on - a permission boundary, a bin the reader has just
|
|
97
|
+
* emptied, an archived-only view, a feature that is switched off for their plan
|
|
98
|
+
* - has no next step *in this region*, and inventing one is how an empty state
|
|
99
|
+
* ends up offering a reader a button that cannot help them. `reason` does not
|
|
100
|
+
* require it either, because the same rule applies across the four: the action
|
|
101
|
+
* exists when the reader has something to do here. For `emptied-by-reader` the
|
|
102
|
+
* next step is elsewhere by construction, since the records to restore are the
|
|
103
|
+
* caller's own nodes and their restore command belongs to the index around this
|
|
104
|
+
* region.
|
|
77
105
|
*/
|
|
78
106
|
actionLabel?: string;
|
|
79
107
|
/**
|
|
@@ -88,7 +116,7 @@ export type EmptyState01Props = {
|
|
|
88
116
|
* A slot and not an icon the Block picks, because the mark that means "nothing
|
|
89
117
|
* here yet" and the mark that means "you cannot see this" are different marks,
|
|
90
118
|
* and a Block that chose one would give four products an icon that is wrong for
|
|
91
|
-
* one of the
|
|
119
|
+
* one of the four reasons. It is `aria-hidden` wherever a consumer puts it: the
|
|
92
120
|
* reason is carried by the words, and an icon a screen reader reads is a second
|
|
93
121
|
* announcement of the same sentence.
|
|
94
122
|
*/
|
|
@@ -105,12 +133,13 @@ export type EmptyState01Props = {
|
|
|
105
133
|
*
|
|
106
134
|
* **The design decision is the `reason` prop, and it is the reason this is a Block
|
|
107
135
|
* rather than a Component.** An empty state looks like one thing, which is why it
|
|
108
|
-
* has been written by hand in every product that has one, and it is actually
|
|
136
|
+
* has been written by hand in every product that has one, and it is actually four
|
|
109
137
|
* things that need different words and different actions. A component with a
|
|
110
138
|
* `title` and a `body` and an `action` cannot express the difference, so every
|
|
111
139
|
* consumer re-decides it in their own words and at least one of them gets it
|
|
112
|
-
* wrong: a permission boundary that offers "Create your first project",
|
|
113
|
-
* first-run state that says "No results found"
|
|
140
|
+
* wrong: a permission boundary that offers "Create your first project", a
|
|
141
|
+
* first-run state that says "No results found", or a bin the reader has just
|
|
142
|
+
* emptied that says their filter matched nothing. Naming the reason forces the
|
|
114
143
|
* decision into the open and gives the Block something to enforce.
|
|
115
144
|
*
|
|
116
145
|
* **A Block and not a Component because it is a composition of five decisions
|
|
@@ -132,7 +161,17 @@ export type EmptyState01Props = {
|
|
|
132
161
|
* actions is a form, and a form in a region that has nothing in it is a form with
|
|
133
162
|
* nothing to submit. One action is the next step; the rest belong in the
|
|
134
163
|
* surrounding surface, where the reader can see them without this region
|
|
135
|
-
* competing.
|
|
164
|
+
* competing. `emptied-by-reader` is the case where there is provably no next step
|
|
165
|
+
* in the region at all, because the records a reader could restore are not in it.
|
|
166
|
+
*
|
|
167
|
+
* **All four are statements about a collection, and that is what keeps the fourth
|
|
168
|
+
* one here rather than beside `NothingChosen01`.** Each answers what happened to
|
|
169
|
+
* the set this region would have drawn, so the frame the four share stays true of
|
|
170
|
+
* all four: there is nothing here to read. A bin the reader emptied has no
|
|
171
|
+
* readable neighbour to make that frame a lie beside it, because everything it
|
|
172
|
+
* would have drawn is gone. `NothingChosen01` is the opposite claim in a region
|
|
173
|
+
* whose records are all still there, which is why it is a Block of its own and
|
|
174
|
+
* not a fifth reason here; `DESIGN.md` holds that argument in full.
|
|
136
175
|
*
|
|
137
176
|
* It is a server Component. It holds no state and imports no client code, so a
|
|
138
177
|
* consumer that passes a client action inside it pays for the action and not for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empty-state.d.ts","sourceRoot":"","sources":["../../../src/blocks/empty-state-01/empty-state.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC
|
|
1
|
+
{"version":3,"file":"empty-state.d.ts","sourceRoot":"","sources":["../../../src/blocks/empty-state-01/empty-state.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,eAAO,MAAM,aAAa,0EAA2E,CAAA;AAErG;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AA0BxD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;;;;;;;OAUG;IACH,MAAM,EAAE,WAAW,CAAA;IACnB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;;OAaG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,KAAK,EACL,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,SAAS,GACV,EAAE,iBAAiB,+BAoEnB;AAED,eAAe,YAAY,CAAA"}
|