@charcuterie/ui 3.0.0 → 3.2.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/README.md +10 -0
- package/dist/Badge/Badge.d.ts +33 -3
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +20 -2
- package/dist/Badge/Badge.js.map +1 -1
- package/dist/Board/Board.d.ts +119 -0
- package/dist/Board/Board.d.ts.map +1 -0
- package/dist/Board/Board.js +165 -0
- package/dist/Board/Board.js.map +1 -0
- package/dist/Board/BoardCard.d.ts +120 -0
- package/dist/Board/BoardCard.d.ts.map +1 -0
- package/dist/Board/BoardCard.js +77 -0
- package/dist/Board/BoardCard.js.map +1 -0
- package/dist/Board/BoardLaneList.d.ts +103 -0
- package/dist/Board/BoardLaneList.d.ts.map +1 -0
- package/dist/Board/BoardLaneList.js +69 -0
- package/dist/Board/BoardLaneList.js.map +1 -0
- package/dist/Board/boardMove.d.ts +116 -0
- package/dist/Board/boardMove.d.ts.map +1 -0
- package/dist/Board/boardMove.js +116 -0
- package/dist/Board/boardMove.js.map +1 -0
- package/dist/Board/useBoardDrag.d.ts +44 -0
- package/dist/Board/useBoardDrag.d.ts.map +1 -0
- package/dist/Board/useBoardDrag.js +237 -0
- package/dist/Board/useBoardDrag.js.map +1 -0
- package/dist/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/Checkbox/Checkbox.js +15 -4
- package/dist/Checkbox/Checkbox.js.map +1 -1
- package/dist/DataTable/DataTable.d.ts +159 -0
- package/dist/DataTable/DataTable.d.ts.map +1 -0
- package/dist/DataTable/DataTable.js +197 -0
- package/dist/DataTable/DataTable.js.map +1 -0
- package/dist/DatePicker/DateGrid.d.ts +79 -0
- package/dist/DatePicker/DateGrid.d.ts.map +1 -0
- package/dist/DatePicker/DateGrid.js +220 -0
- package/dist/DatePicker/DateGrid.js.map +1 -0
- package/dist/DatePicker/DatePicker.d.ts +138 -0
- package/dist/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/DatePicker/DatePicker.js +384 -0
- package/dist/DatePicker/DatePicker.js.map +1 -0
- package/dist/DatePicker/datePresets.d.ts +29 -0
- package/dist/DatePicker/datePresets.d.ts.map +1 -0
- package/dist/DatePicker/datePresets.js +28 -0
- package/dist/DatePicker/datePresets.js.map +1 -0
- package/dist/DatePicker/parseDateInput.d.ts +106 -0
- package/dist/DatePicker/parseDateInput.d.ts.map +1 -0
- package/dist/DatePicker/parseDateInput.js +419 -0
- package/dist/DatePicker/parseDateInput.js.map +1 -0
- package/dist/DatePicker/plainDate.d.ts +167 -0
- package/dist/DatePicker/plainDate.d.ts.map +1 -0
- package/dist/DatePicker/plainDate.js +272 -0
- package/dist/DatePicker/plainDate.js.map +1 -0
- package/dist/Dialog/Dialog.d.ts +12 -2
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -2
- package/dist/Dialog/Dialog.js.map +1 -1
- package/dist/MarkdownEditor/MarkdownEditor.d.ts +147 -0
- package/dist/MarkdownEditor/MarkdownEditor.d.ts.map +1 -0
- package/dist/MarkdownEditor/MarkdownEditor.js +492 -0
- package/dist/MarkdownEditor/MarkdownEditor.js.map +1 -0
- package/dist/MarkdownEditor/markdownCommands.d.ts +202 -0
- package/dist/MarkdownEditor/markdownCommands.d.ts.map +1 -0
- package/dist/MarkdownEditor/markdownCommands.js +379 -0
- package/dist/MarkdownEditor/markdownCommands.js.map +1 -0
- package/dist/MarkdownEditor/markdownSpans.d.ts +92 -0
- package/dist/MarkdownEditor/markdownSpans.d.ts.map +1 -0
- package/dist/MarkdownEditor/markdownSpans.js +303 -0
- package/dist/MarkdownEditor/markdownSpans.js.map +1 -0
- package/dist/Modal/Modal.d.ts +6 -2
- package/dist/Modal/Modal.d.ts.map +1 -1
- package/dist/Modal/Modal.js +1 -1
- package/dist/Modal/Modal.js.map +1 -1
- package/dist/Overlay/OverlayPanel.d.ts +13 -2
- package/dist/Overlay/OverlayPanel.d.ts.map +1 -1
- package/dist/Overlay/OverlayPanel.js +5 -1
- package/dist/Overlay/OverlayPanel.js.map +1 -1
- package/dist/categoricalStyles.d.ts +53 -0
- package/dist/categoricalStyles.d.ts.map +1 -0
- package/dist/categoricalStyles.js +195 -0
- package/dist/categoricalStyles.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/Badge/Badge.mdx +46 -0
- package/src/Badge/Badge.stories.tsx +98 -4
- package/src/Badge/Badge.test.tsx +157 -2
- package/src/Badge/Badge.tsx +49 -3
- package/src/Board/Board.mdx +199 -0
- package/src/Board/Board.stories.tsx +707 -0
- package/src/Board/Board.test.tsx +310 -0
- package/src/Board/Board.tsx +333 -0
- package/src/Board/BoardCard.tsx +290 -0
- package/src/Board/BoardLaneList.tsx +275 -0
- package/src/Board/boardMove.test.ts +213 -0
- package/src/Board/boardMove.ts +225 -0
- package/src/Board/useBoardDrag.ts +413 -0
- package/src/Checkbox/Checkbox.tsx +34 -3
- package/src/DataTable/DataTable.mdx +123 -0
- package/src/DataTable/DataTable.stories.tsx +411 -0
- package/src/DataTable/DataTable.test.tsx +386 -0
- package/src/DataTable/DataTable.tsx +497 -0
- package/src/DatePicker/DateGrid.tsx +467 -0
- package/src/DatePicker/DatePicker.mdx +170 -0
- package/src/DatePicker/DatePicker.stories.tsx +268 -0
- package/src/DatePicker/DatePicker.test.tsx +455 -0
- package/src/DatePicker/DatePicker.tsx +852 -0
- package/src/DatePicker/datePresets.ts +34 -0
- package/src/DatePicker/parseDateInput.test.ts +187 -0
- package/src/DatePicker/parseDateInput.ts +629 -0
- package/src/DatePicker/plainDate.test.ts +375 -0
- package/src/DatePicker/plainDate.ts +465 -0
- package/src/Dialog/Dialog.stories.tsx +48 -0
- package/src/Dialog/Dialog.test.tsx +30 -0
- package/src/Dialog/Dialog.tsx +13 -1
- package/src/MarkdownEditor/MarkdownEditor.mdx +189 -0
- package/src/MarkdownEditor/MarkdownEditor.stories.tsx +267 -0
- package/src/MarkdownEditor/MarkdownEditor.test.tsx +627 -0
- package/src/MarkdownEditor/MarkdownEditor.tsx +862 -0
- package/src/MarkdownEditor/markdownCommands.test.ts +384 -0
- package/src/MarkdownEditor/markdownCommands.ts +566 -0
- package/src/MarkdownEditor/markdownSpans.test.ts +217 -0
- package/src/MarkdownEditor/markdownSpans.ts +458 -0
- package/src/Modal/Modal.tsx +7 -1
- package/src/Overlay/OverlayPanel.tsx +17 -1
- package/src/argTypes.storyHelpers.ts +39 -0
- package/src/categoricalStyles.ts +244 -0
- package/src/index.ts +89 -0
- package/src/sourceRules.test.ts +52 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
4
|
+
import { SortableTableHeader } from "../SortableTableHeader/SortableTableHeader.js";
|
|
5
|
+
import { toClassName } from "../toClassName.js";
|
|
6
|
+
import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden.js";
|
|
7
|
+
/**
|
|
8
|
+
* The cell padding, in one place because four elements have to agree
|
|
9
|
+
* on it: the sortable header's button (which brings its own, inside
|
|
10
|
+
* `SortableTableHeader`), the plain header, the body cell, and the
|
|
11
|
+
* empty-state cell.
|
|
12
|
+
*/
|
|
13
|
+
const CELL_PADDING_CLASS = "px-3 py-2";
|
|
14
|
+
/**
|
|
15
|
+
* `role` on every part, and it is not belt-and-braces.
|
|
16
|
+
*
|
|
17
|
+
* The narrow layout takes `display` away from the table elements —
|
|
18
|
+
* `block` on the table, `grid` on the row, `flex` on the cell — and
|
|
19
|
+
* a table's semantics have historically travelled with its
|
|
20
|
+
* `display`. Measured in this sandbox's chromium via CDP
|
|
21
|
+
* (`Accessibility.getFullAXTree`), that engine now keeps
|
|
22
|
+
* `table` / `row` / `columnheader` / `cell` intact through
|
|
23
|
+
* `display: block`; the roles below are therefore a **no-op here**
|
|
24
|
+
* and insurance for the engines this sandbox cannot run. The one
|
|
25
|
+
* measured difference is `<tbody>`: chromium ignores it entirely
|
|
26
|
+
* until it is given `role="rowgroup"`, at which point it joins the
|
|
27
|
+
* tree as a rowgroup beside `<thead>`'s — which is what a table
|
|
28
|
+
* with two rowgroups is supposed to look like.
|
|
29
|
+
*
|
|
30
|
+
* The cost of being wrong in each direction is what settles it. A
|
|
31
|
+
* redundant role changes nothing anywhere. A dropped one turns the
|
|
32
|
+
* whole table into a stack of unrelated text in exactly the layout
|
|
33
|
+
* where the column headers are no longer on screen to read.
|
|
34
|
+
*/
|
|
35
|
+
const ROW_GROUP_ROLE = "rowgroup";
|
|
36
|
+
/**
|
|
37
|
+
* **A table that reflows instead of scrolling, sized by its
|
|
38
|
+
* container.**
|
|
39
|
+
*
|
|
40
|
+
* ### It composes `SortableTableHeader`; it does not replace it
|
|
41
|
+
*
|
|
42
|
+
* The header cell shipped first, on its own, and stays on its own:
|
|
43
|
+
* it is already in a published 1.0.0 with a consumer of its own
|
|
44
|
+
* (mux-magic's `FileExplorerModal`), and subsuming it would be a
|
|
45
|
+
* breaking change bought for nothing. Every sortable column here
|
|
46
|
+
* renders that component, so there is exactly one place in the fleet
|
|
47
|
+
* that knows `aria-sort` belongs on the `<th>` while the click
|
|
48
|
+
* target is a `<button>` inside it, and one place to fix if that is
|
|
49
|
+
* ever wrong.
|
|
50
|
+
*
|
|
51
|
+
* That composition is also why this is a **real `<table>`**. A grid
|
|
52
|
+
* of `<div role="row">`s is the other reasonable choice, and it
|
|
53
|
+
* cannot host a `<th>` at all — so it would have meant either
|
|
54
|
+
* duplicating the header or breaking it.
|
|
55
|
+
*
|
|
56
|
+
* ### The narrow layout reflows; it never scrolls sideways
|
|
57
|
+
*
|
|
58
|
+
* Below `--cq-md` (32rem) each row stops being a row and becomes a
|
|
59
|
+
* labelled block — the column name beside its value, one pair per
|
|
60
|
+
* line — while the header row wraps into a strip of sort controls
|
|
61
|
+
* that stays reachable. Above it, a normal table.
|
|
62
|
+
*
|
|
63
|
+
* The two rejected alternatives are worth naming because both are
|
|
64
|
+
* common:
|
|
65
|
+
*
|
|
66
|
+
* - **Horizontal scroll.** WCAG 2.2 SC 1.4.10 (Reflow) exists
|
|
67
|
+
* because of it, and the consumer this was built for bans it
|
|
68
|
+
* outright ("No horizontal scroll, ever"). A pan surface also
|
|
69
|
+
* hides the *existence* of the columns off-screen.
|
|
70
|
+
* - **Dropping low-priority columns.** Cheapest to build and the
|
|
71
|
+
* only one that loses data: the assignee column does not come
|
|
72
|
+
* back, and there is no gesture that asks for it.
|
|
73
|
+
*
|
|
74
|
+
* ### Container queries, never a media query
|
|
75
|
+
*
|
|
76
|
+
* The width that matters is the element's, not the window's. A table
|
|
77
|
+
* in a three-up board lane is narrow on a 4K monitor, and the owner
|
|
78
|
+
* of this fleet browses zoomed in — a 1500px window at 175% zoom is
|
|
79
|
+
* ~860 effective pixels, so a `@media (max-width: …)` is measuring
|
|
80
|
+
* something that is not the question. The `@container` is declared
|
|
81
|
+
* on the wrapper and queried by its descendants, because a container
|
|
82
|
+
* query never matches the element that declares it.
|
|
83
|
+
*
|
|
84
|
+
* ### What it does not own
|
|
85
|
+
*
|
|
86
|
+
* **Sorting the data.** `sort` says which column is sorted;
|
|
87
|
+
* `onSortChange` asks for a different one. Comparing two values is
|
|
88
|
+
* per-column app logic — a date, a title, a priority enum with an
|
|
89
|
+
* order that is not alphabetical — and a table that sorted its own
|
|
90
|
+
* rows would have to be handed a comparator per column to do it,
|
|
91
|
+
* which is the same code in a worse place. The rows arrive ordered.
|
|
92
|
+
*
|
|
93
|
+
* **The selected set.** `selection.selectedRowKeys` is authoritative
|
|
94
|
+
* and lives with the consumer, because every use of a selection —
|
|
95
|
+
* the count in a toolbar, the bulk action, the undo — is outside
|
|
96
|
+
* this component. That is the opposite of `Checkbox`, and for a
|
|
97
|
+
* stated reason: the platform owns one checkbox's checkedness, and
|
|
98
|
+
* nothing at all owns "these five rows".
|
|
99
|
+
*
|
|
100
|
+
* **Column widths.** No drag handle, no measured layout. The browser's
|
|
101
|
+
* own auto table layout sizes columns to their content, which is a
|
|
102
|
+
* better algorithm than anything worth reimplementing, and
|
|
103
|
+
* `column.className` overrides it. Drag-to-resize is a *persistence*
|
|
104
|
+
* question (whose widths, stored where, keyed on what) that a
|
|
105
|
+
* component cannot answer for its consumer.
|
|
106
|
+
*
|
|
107
|
+
* **Virtualisation.** Deliberately absent; see the decision record.
|
|
108
|
+
* Every row is in the DOM, which is what keeps the accessibility
|
|
109
|
+
* tree honest and the auto column layout possible.
|
|
110
|
+
*/
|
|
111
|
+
export const DataTable = ({ className, columns, emptyState, getRowKey, isLabelVisible = false, label, onSortChange, rows, selection, sort, }) => {
|
|
112
|
+
const tableRef = useRef(null);
|
|
113
|
+
const selectedRowKeys = selection?.selectedRowKeys;
|
|
114
|
+
/**
|
|
115
|
+
* The checkboxes are **written to**, not re-mounted.
|
|
116
|
+
*
|
|
117
|
+
* `Checkbox` is uncontrolled by design — `isChecked` seeds the
|
|
118
|
+
* first paint and the `<input>` is the store from then on — so a
|
|
119
|
+
* consumer changing `selectedRowKeys` out from under it (a "clear
|
|
120
|
+
* selection" button, a filter that drops a selected row) would
|
|
121
|
+
* otherwise leave the tick behind. Re-mounting with a
|
|
122
|
+
* state-bearing `key` fixes the tick and throws the focus away
|
|
123
|
+
* mid-keyboard, one row into a `Space`-through-the-list.
|
|
124
|
+
*
|
|
125
|
+
* So the store gets written instead, which is also the only way to
|
|
126
|
+
* express the header box's third state: `indeterminate` is a DOM
|
|
127
|
+
* property with no HTML attribute at all, so there is no markup
|
|
128
|
+
* that could have set it.
|
|
129
|
+
*
|
|
130
|
+
* No dependency array: this has to run after every render that
|
|
131
|
+
* could have changed a row, and it is a `querySelectorAll` over
|
|
132
|
+
* the rows already on screen.
|
|
133
|
+
*/
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
const table = tableRef.current;
|
|
136
|
+
if (!table || !selectedRowKeys) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const selectedKeys = new Set(selectedRowKeys);
|
|
140
|
+
for (const input of table.querySelectorAll('tbody input[type="checkbox"]')) {
|
|
141
|
+
input.checked = selectedKeys.has(input.value);
|
|
142
|
+
}
|
|
143
|
+
const selectAllInput = table.querySelector('thead input[type="checkbox"]');
|
|
144
|
+
if (selectAllInput) {
|
|
145
|
+
const selectedRowCount = rows.filter((row) => selectedKeys.has(getRowKey(row))).length;
|
|
146
|
+
selectAllInput.checked =
|
|
147
|
+
rows.length > 0 && selectedRowCount === rows.length;
|
|
148
|
+
selectAllInput.indeterminate =
|
|
149
|
+
selectedRowCount > 0 &&
|
|
150
|
+
selectedRowCount < rows.length;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const selectedKeys = new Set(selectedRowKeys ?? []);
|
|
154
|
+
const columnCount = columns.length + (selection ? 1 : 0);
|
|
155
|
+
return (_jsx("div", { className: toClassName("@container", className), children: _jsxs("table", { className: "block w-full border-collapse text-start text-content-primary text-md cq-md:table", ref: tableRef, role: "table", children: [_jsx("caption", { className: toClassName("text-start", isLabelVisible
|
|
156
|
+
? "pb-2 font-medium text-content-secondary text-sm"
|
|
157
|
+
: "sr-only"), children: label }), _jsx("thead", { className: "block cq-md:table-header-group", role: ROW_GROUP_ROLE, children: _jsxs("tr", { className: "flex flex-wrap items-center gap-x-1 border-border-subtle border-b cq-md:table-row cq-md:border-b-0", role: "row", children: [selection ? (_jsx("th", { className: toClassName("border-border-subtle p-2 text-start cq-md:table-cell cq-md:border-b"), role: "columnheader", scope: "col", children: _jsx(Checkbox, { label: _jsx(VisuallyHidden, { children: selection.selectAllLabel ??
|
|
158
|
+
"Select all rows" }), onChange: (isChecked) => {
|
|
159
|
+
selection.onSelectionChange(isChecked ? rows.map(getRowKey) : []);
|
|
160
|
+
} }) })) : null, columns.map((column) => column.isSortable ? (_jsx(SortableTableHeader
|
|
161
|
+
// The header keeps its own `border-b` for the
|
|
162
|
+
// wide layout and loses it in the strip, where
|
|
163
|
+
// one underline per chip reads as five separate
|
|
164
|
+
// rules rather than one header edge.
|
|
165
|
+
, {
|
|
166
|
+
// The header keeps its own `border-b` for the
|
|
167
|
+
// wide layout and loses it in the strip, where
|
|
168
|
+
// one underline per chip reads as five separate
|
|
169
|
+
// rules rather than one header edge.
|
|
170
|
+
className: toClassName("border-b-0 cq-md:border-b", column.className), direction: sort?.columnKey === column.key
|
|
171
|
+
? sort.direction
|
|
172
|
+
: undefined, onSort: (direction) => {
|
|
173
|
+
onSortChange?.({
|
|
174
|
+
columnKey: column.key,
|
|
175
|
+
direction,
|
|
176
|
+
});
|
|
177
|
+
}, children: column.header }, column.key)) : (_jsx("th", { className: toClassName("border-border-subtle text-start font-medium text-content-secondary text-sm uppercase tracking-wide cq-md:table-cell cq-md:border-b", CELL_PADDING_CLASS, column.className), role: "columnheader", scope: "col", children: column.header }, column.key)))] }) }), _jsx("tbody", { className: "flex flex-col gap-2 pt-2 cq-md:table-row-group cq-md:gap-0 cq-md:pt-0", role: ROW_GROUP_ROLE, children: rows.length === 0 ? (_jsx("tr", { className: "block cq-md:table-row", role: "row", children: _jsx("td", { className: "block p-0 cq-md:table-cell", colSpan: columnCount, role: "cell", children: emptyState }) })) : (rows.map((row) => {
|
|
178
|
+
const rowKey = getRowKey(row);
|
|
179
|
+
const isSelected = selectedKeys.has(rowKey);
|
|
180
|
+
return (_jsxs("tr", { className: toClassName(
|
|
181
|
+
// Narrow: a card. Wide: a row with a hairline
|
|
182
|
+
// under it. Both states are written here rather
|
|
183
|
+
// than in a stylesheet so a reader can see the
|
|
184
|
+
// whole of a row's shape in one place.
|
|
185
|
+
"flex flex-col gap-1 rounded-lg border border-border-subtle p-3 transition-colors duration-(--duration-fast) ease-standard", "cq-md:table-row cq-md:gap-0 cq-md:rounded-none cq-md:border-0 cq-md:border-border-subtle cq-md:border-b cq-md:p-0", isSelected
|
|
186
|
+
? "bg-intent-accent-surface"
|
|
187
|
+
: "bg-surface-raised hover:bg-intent-neutral-surface cq-md:bg-transparent"), "data-is-selected": isSelected, role: "row", children: [selection ? (_jsx("td", { className: "block p-2 cq-md:table-cell cq-md:align-middle", role: "cell", children: _jsx(Checkbox, { isChecked: isSelected, label: _jsx(VisuallyHidden, { children: selection.getRowLabel(row) }), onChange: (isChecked) => {
|
|
188
|
+
selection.onSelectionChange(isChecked
|
|
189
|
+
? [
|
|
190
|
+
...selection.selectedRowKeys,
|
|
191
|
+
rowKey,
|
|
192
|
+
]
|
|
193
|
+
: selection.selectedRowKeys.filter((one) => one !== rowKey));
|
|
194
|
+
}, value: rowKey }) })) : null, columns.map((column) => (_jsxs("td", { className: toClassName("flex items-baseline gap-3 py-0.5 cq-md:table-cell cq-md:align-middle", CELL_PADDING_CLASS, "cq-md:py-2", column.className), role: "cell", children: [_jsx("span", { "aria-hidden": "true", className: "w-20 shrink-0 truncate text-content-secondary text-xs uppercase tracking-wide cq-sm:w-24 cq-md:hidden", children: column.header }), _jsx("span", { className: "min-w-0 flex-1 wrap-anywhere cq-md:wrap-normal", children: column.renderCell(row) })] }, column.key)))] }, rowKey));
|
|
195
|
+
})) })] }) }));
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=DataTable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTable.js","sourceRoot":"","sources":["../../src/DataTable/DataTable.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { PlainDate } from "./plainDate.ts";
|
|
3
|
+
export type DateGridProps = {
|
|
4
|
+
firstDayOfWeek: number;
|
|
5
|
+
focusedDate: PlainDate;
|
|
6
|
+
/**
|
|
7
|
+
* The month this grid draws — any date inside it. `DatePicker`
|
|
8
|
+
* renders two of these in range mode, so the month is a prop
|
|
9
|
+
* rather than derived from the focus.
|
|
10
|
+
*/
|
|
11
|
+
gridMonth: PlainDate;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the keyboard is *in* the grid. Only the grid that owns
|
|
14
|
+
* focus pulls it onto its focused cell; the other one would rip
|
|
15
|
+
* focus back and forth between the two months on every arrow key.
|
|
16
|
+
*/
|
|
17
|
+
isGridFocused: boolean;
|
|
18
|
+
isRange: boolean;
|
|
19
|
+
label: string;
|
|
20
|
+
locale?: string;
|
|
21
|
+
maximum?: PlainDate;
|
|
22
|
+
minimum?: PlainDate;
|
|
23
|
+
onFocusDate: (date: PlainDate) => void;
|
|
24
|
+
onHoverDate: (date: null | PlainDate) => void;
|
|
25
|
+
onSelectDate: (date: PlainDate) => void;
|
|
26
|
+
/**
|
|
27
|
+
* The end of the band being drawn *right now* — the committed end,
|
|
28
|
+
* or the hovered/keyboard-focused day while the second click is
|
|
29
|
+
* still outstanding. Painting the pending half is the difference
|
|
30
|
+
* between a range picker you can aim and one you have to guess at.
|
|
31
|
+
*/
|
|
32
|
+
previewEnd: null | PlainDate;
|
|
33
|
+
rangeStart: null | PlainDate;
|
|
34
|
+
selectedDates: readonly PlainDate[];
|
|
35
|
+
today: PlainDate;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* One month, as a real `<table role="grid">`.
|
|
39
|
+
*
|
|
40
|
+
* ### Why the roving focus is arithmetic and not `useRovingFocus`
|
|
41
|
+
*
|
|
42
|
+
* `@charcuterie/logic`'s `RovingFocus` is a **registration** kind:
|
|
43
|
+
* members register from an effect and `next`/`previous`/`first`/
|
|
44
|
+
* `last` walk that list. `SegmentedControl` and `Tabs` are exactly
|
|
45
|
+
* that shape, and a calendar is not, for two reasons that are worth
|
|
46
|
+
* stating rather than discovering:
|
|
47
|
+
*
|
|
48
|
+
* - The movement is two-dimensional and it is *date* arithmetic, not
|
|
49
|
+
* list traversal. ArrowDown is "+7 days", which is a different
|
|
50
|
+
* thing from "the seventh next registered member" the moment a
|
|
51
|
+
* month does not start on the first column.
|
|
52
|
+
* - The move can leave the rendered set entirely. ArrowRight on 31
|
|
53
|
+
* August lands on 1 September, which is a day this grid has not
|
|
54
|
+
* rendered and no member has registered. A registration list
|
|
55
|
+
* cannot express a move to something that does not exist yet; a
|
|
56
|
+
* day number can, and re-rendering the grid around it is the
|
|
57
|
+
* correct response.
|
|
58
|
+
*
|
|
59
|
+
* So the focused *date* is the state and the DOM follows it. This is
|
|
60
|
+
* the same conclusion `Field` reached about `createLinkedIds` — the
|
|
61
|
+
* state kind is real and this is not its shape.
|
|
62
|
+
*
|
|
63
|
+
* ### The cell is the gridcell
|
|
64
|
+
*
|
|
65
|
+
* `<td tabindex>` rather than `<td><button>`. It is the APG date
|
|
66
|
+
* picker's own structure, it keeps exactly one tab stop in the grid
|
|
67
|
+
* (which `expectAgentDrivable` checks), and it avoids a focusable
|
|
68
|
+
* button inside a focusable cell — two tab stops per day, which is
|
|
69
|
+
* what the naive build produces.
|
|
70
|
+
*
|
|
71
|
+
* Each cell's accessible name is the **whole date**, formatted in
|
|
72
|
+
* the active locale: "Wednesday, August 19, 2026", not "19". A
|
|
73
|
+
* screen reader reading a bare number in a grid with no row or
|
|
74
|
+
* column headers announced is a user counting cells, and
|
|
75
|
+
* `getByRole("gridcell", { name: … })` becomes unusable the moment
|
|
76
|
+
* two months are on screen.
|
|
77
|
+
*/
|
|
78
|
+
export declare const DateGrid: ({ firstDayOfWeek, focusedDate, gridMonth, isGridFocused, isRange, label, locale, maximum, minimum, onFocusDate, onHoverDate, onSelectDate, previewEnd, rangeStart, selectedDates, today, }: DateGridProps) => ReactNode;
|
|
79
|
+
//# sourceMappingURL=DateGrid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateGrid.d.ts","sourceRoot":"","sources":["../../src/DatePicker/DateGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAe/C,MAAM,MAAM,aAAa,GAAG;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,SAAS,CAAA;IACtB;;;;OAIG;IACH,SAAS,EAAE,SAAS,CAAA;IACpB;;;;OAIG;IACH,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACtC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAC7C,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACvC;;;;;OAKG;IACH,UAAU,EAAE,IAAI,GAAG,SAAS,CAAA;IAC5B,UAAU,EAAE,IAAI,GAAG,SAAS,CAAA;IAC5B,aAAa,EAAE,SAAS,SAAS,EAAE,CAAA;IACnC,KAAK,EAAE,SAAS,CAAA;CACjB,CAAA;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,QAAQ,GAAI,4LAiBtB,aAAa,KAAG,SA0VlB,CAAA"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { FOCUS_RING_CLASS } from "../intentStyles.js";
|
|
4
|
+
import { toClassName } from "../toClassName.js";
|
|
5
|
+
import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden.js";
|
|
6
|
+
import { addDays, addMonths, clampPlainDate, comparePlainDates, formatPlainDate, getDaysInMonth, getIsSameDay, getIsWithinRange, getWeekday, getWeekdayNames, toIsoDate, } from "./plainDate.js";
|
|
7
|
+
const CELL_BASE_CLASS = "cursor-pointer rounded-md p-0 text-center";
|
|
8
|
+
const DAY_BASE_CLASS = "mx-auto flex size-8 items-center justify-center rounded-md border border-transparent text-xs transition-colors duration-(--duration-fast) ease-standard cq-xs:size-9 cq-xs:text-sm cq-sm:size-10";
|
|
9
|
+
/**
|
|
10
|
+
* One month, as a real `<table role="grid">`.
|
|
11
|
+
*
|
|
12
|
+
* ### Why the roving focus is arithmetic and not `useRovingFocus`
|
|
13
|
+
*
|
|
14
|
+
* `@charcuterie/logic`'s `RovingFocus` is a **registration** kind:
|
|
15
|
+
* members register from an effect and `next`/`previous`/`first`/
|
|
16
|
+
* `last` walk that list. `SegmentedControl` and `Tabs` are exactly
|
|
17
|
+
* that shape, and a calendar is not, for two reasons that are worth
|
|
18
|
+
* stating rather than discovering:
|
|
19
|
+
*
|
|
20
|
+
* - The movement is two-dimensional and it is *date* arithmetic, not
|
|
21
|
+
* list traversal. ArrowDown is "+7 days", which is a different
|
|
22
|
+
* thing from "the seventh next registered member" the moment a
|
|
23
|
+
* month does not start on the first column.
|
|
24
|
+
* - The move can leave the rendered set entirely. ArrowRight on 31
|
|
25
|
+
* August lands on 1 September, which is a day this grid has not
|
|
26
|
+
* rendered and no member has registered. A registration list
|
|
27
|
+
* cannot express a move to something that does not exist yet; a
|
|
28
|
+
* day number can, and re-rendering the grid around it is the
|
|
29
|
+
* correct response.
|
|
30
|
+
*
|
|
31
|
+
* So the focused *date* is the state and the DOM follows it. This is
|
|
32
|
+
* the same conclusion `Field` reached about `createLinkedIds` — the
|
|
33
|
+
* state kind is real and this is not its shape.
|
|
34
|
+
*
|
|
35
|
+
* ### The cell is the gridcell
|
|
36
|
+
*
|
|
37
|
+
* `<td tabindex>` rather than `<td><button>`. It is the APG date
|
|
38
|
+
* picker's own structure, it keeps exactly one tab stop in the grid
|
|
39
|
+
* (which `expectAgentDrivable` checks), and it avoids a focusable
|
|
40
|
+
* button inside a focusable cell — two tab stops per day, which is
|
|
41
|
+
* what the naive build produces.
|
|
42
|
+
*
|
|
43
|
+
* Each cell's accessible name is the **whole date**, formatted in
|
|
44
|
+
* the active locale: "Wednesday, August 19, 2026", not "19". A
|
|
45
|
+
* screen reader reading a bare number in a grid with no row or
|
|
46
|
+
* column headers announced is a user counting cells, and
|
|
47
|
+
* `getByRole("gridcell", { name: … })` becomes unusable the moment
|
|
48
|
+
* two months are on screen.
|
|
49
|
+
*/
|
|
50
|
+
export const DateGrid = ({ firstDayOfWeek, focusedDate, gridMonth, isGridFocused, isRange, label, locale, maximum, minimum, onFocusDate, onHoverDate, onSelectDate, previewEnd, rangeStart, selectedDates, today, }) => {
|
|
51
|
+
const cellElements = useRef(new Map());
|
|
52
|
+
const daysInMonth = getDaysInMonth(gridMonth.year, gridMonth.month);
|
|
53
|
+
const firstOfMonth = {
|
|
54
|
+
day: 1,
|
|
55
|
+
month: gridMonth.month,
|
|
56
|
+
year: gridMonth.year,
|
|
57
|
+
};
|
|
58
|
+
const leadingBlanks = (getWeekday(firstOfMonth) - firstDayOfWeek + 7) % 7;
|
|
59
|
+
const days = Array.from({ length: daysInMonth }, (_unused, index) => ({
|
|
60
|
+
day: index + 1,
|
|
61
|
+
month: gridMonth.month,
|
|
62
|
+
year: gridMonth.year,
|
|
63
|
+
}));
|
|
64
|
+
const getIsEnabled = (date) => getIsWithinRange(date, minimum, maximum);
|
|
65
|
+
const isFocusInThisMonth = focusedDate.month === gridMonth.month &&
|
|
66
|
+
focusedDate.year === gridMonth.year;
|
|
67
|
+
/**
|
|
68
|
+
* Exactly one cell in this grid is in the tab order. When the
|
|
69
|
+
* focused date is in another month — the second grid of a range
|
|
70
|
+
* picker — the tab stop falls to the first day this grid *can*
|
|
71
|
+
* offer, because a grid with zero tab stops strands the widget and
|
|
72
|
+
* a grid where every cell is tabbable is the pattern not being
|
|
73
|
+
* implemented at all.
|
|
74
|
+
*/
|
|
75
|
+
const tabStopDate = isFocusInThisMonth
|
|
76
|
+
? focusedDate
|
|
77
|
+
: (days.find(getIsEnabled) ?? firstOfMonth);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!isGridFocused || !isFocusInThisMonth) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
cellElements.current
|
|
83
|
+
.get(toIsoDate(focusedDate))
|
|
84
|
+
?.focus();
|
|
85
|
+
}, [focusedDate, isFocusInThisMonth, isGridFocused]);
|
|
86
|
+
const moveFocus = (date) => {
|
|
87
|
+
onFocusDate(clampPlainDate(date, minimum, maximum));
|
|
88
|
+
};
|
|
89
|
+
const handleKeyDown = (keyEvent) => {
|
|
90
|
+
const moves = {
|
|
91
|
+
ArrowDown: () => addDays(focusedDate, 7),
|
|
92
|
+
ArrowLeft: () => addDays(focusedDate, -1),
|
|
93
|
+
ArrowRight: () => addDays(focusedDate, 1),
|
|
94
|
+
ArrowUp: () => addDays(focusedDate, -7),
|
|
95
|
+
End: () => addDays(focusedDate, 6 -
|
|
96
|
+
((getWeekday(focusedDate) -
|
|
97
|
+
firstDayOfWeek +
|
|
98
|
+
7) %
|
|
99
|
+
7)),
|
|
100
|
+
Home: () => addDays(focusedDate, -((getWeekday(focusedDate) - firstDayOfWeek + 7) %
|
|
101
|
+
7)),
|
|
102
|
+
PageDown: () => addMonths(focusedDate, keyEvent.shiftKey ? 12 : 1),
|
|
103
|
+
PageUp: () => addMonths(focusedDate, keyEvent.shiftKey ? -12 : -1),
|
|
104
|
+
};
|
|
105
|
+
const move = moves[keyEvent.key];
|
|
106
|
+
if (move) {
|
|
107
|
+
// Arrows scroll the page by default, and PageUp/PageDown scroll
|
|
108
|
+
// it a long way. A calendar that pages the document while it
|
|
109
|
+
// pages the month is the hand-rolled version of this component.
|
|
110
|
+
keyEvent.preventDefault();
|
|
111
|
+
moveFocus(move());
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (keyEvent.key === "Enter" || keyEvent.key === " ") {
|
|
115
|
+
keyEvent.preventDefault();
|
|
116
|
+
if (getIsEnabled(focusedDate)) {
|
|
117
|
+
onSelectDate(focusedDate);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const weekdayNames = {
|
|
122
|
+
long: getWeekdayNames({
|
|
123
|
+
firstDayOfWeek,
|
|
124
|
+
locale,
|
|
125
|
+
weekday: "long",
|
|
126
|
+
}),
|
|
127
|
+
narrow: getWeekdayNames({
|
|
128
|
+
firstDayOfWeek,
|
|
129
|
+
locale,
|
|
130
|
+
weekday: "narrow",
|
|
131
|
+
}),
|
|
132
|
+
short: getWeekdayNames({
|
|
133
|
+
firstDayOfWeek,
|
|
134
|
+
locale,
|
|
135
|
+
weekday: "short",
|
|
136
|
+
}),
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* The band being painted right now: the committed pair, or the
|
|
140
|
+
* committed start plus whatever the pointer or the keyboard is
|
|
141
|
+
* currently over. Ordered, so a backwards drag paints forwards.
|
|
142
|
+
*/
|
|
143
|
+
const bandFrom = previewEnd && rangeStart
|
|
144
|
+
? comparePlainDates(previewEnd, rangeStart) < 0
|
|
145
|
+
? previewEnd
|
|
146
|
+
: rangeStart
|
|
147
|
+
: null;
|
|
148
|
+
const bandTo = previewEnd && rangeStart
|
|
149
|
+
? comparePlainDates(previewEnd, rangeStart) < 0
|
|
150
|
+
? rangeStart
|
|
151
|
+
: previewEnd
|
|
152
|
+
: null;
|
|
153
|
+
const isBandWiderThanADay = bandFrom !== null &&
|
|
154
|
+
bandTo !== null &&
|
|
155
|
+
!getIsSameDay(bandFrom, bandTo);
|
|
156
|
+
/**
|
|
157
|
+
* Every cell of every week, including the neighbouring months'
|
|
158
|
+
* days, each keyed by its own real date.
|
|
159
|
+
*
|
|
160
|
+
* Keying a blank cell by its position in the array is the obvious
|
|
161
|
+
* thing and it is wrong twice over: React reuses the wrong node
|
|
162
|
+
* when the month changes length, and the lint rule that says so is
|
|
163
|
+
* right. A leading blank in August *is* a day in July; it has an
|
|
164
|
+
* ISO date whether or not it is drawn.
|
|
165
|
+
*/
|
|
166
|
+
const weeks = Array.from({
|
|
167
|
+
length: Math.ceil((leadingBlanks + daysInMonth) / 7),
|
|
168
|
+
}, (_unused, weekIndex) => Array.from({ length: 7 }, (_blank, dayIndex) => addDays(firstOfMonth, weekIndex * 7 + dayIndex - leadingBlanks)));
|
|
169
|
+
return (_jsxs("table", { "aria-label": label, className: "w-full border-collapse", onMouseLeave: () => {
|
|
170
|
+
onHoverDate(null);
|
|
171
|
+
},
|
|
172
|
+
// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: a calendar IS the ARIA spec's own worked example of `role="grid"` on a `<table>` — an interactive grid whose cells take roving focus. Biome's advice ("replace table with a div") trades correct table semantics for five more suppressions on the div rebuild. It has to sit on the line immediately above the attribute: a biome suppression covers the NEXT LINE, not the next node, so one placed above a multi-line `<table` covers only the tag name — and `yarn lint` runs `--write --unsafe`, so a misplaced one deletes the role silently and the widget degrades to a static `role="table"`.
|
|
173
|
+
role: "grid", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayNames.long.map((longName, index) => (_jsxs("th", { className: "pb-1 font-medium text-content-secondary text-xs", scope: "col", children: [_jsx("span", { "aria-hidden": "true", className: "cq-xs:hidden", children: weekdayNames.narrow[index] }), _jsx("span", { "aria-hidden": "true", className: "hidden cq-xs:inline", children: weekdayNames.short[index] }), _jsx(VisuallyHidden, { children: longName })] }, longName))) }) }), _jsx("tbody", { children: weeks.map((week) => (_jsx("tr", { children: week.map((date) => {
|
|
174
|
+
const isoDate = toIsoDate(date);
|
|
175
|
+
if (date.month !== gridMonth.month) {
|
|
176
|
+
return (_jsx("td", { "aria-hidden": "true", className: "p-0", children: _jsx("span", { className: "block size-8 cq-xs:size-9 cq-sm:size-10" }) }, isoDate));
|
|
177
|
+
}
|
|
178
|
+
const isEnabled = getIsEnabled(date);
|
|
179
|
+
const isSelected = selectedDates.some((one) => getIsSameDay(one, date));
|
|
180
|
+
const isInBand = isRange &&
|
|
181
|
+
isBandWiderThanADay &&
|
|
182
|
+
bandFrom !== null &&
|
|
183
|
+
bandTo !== null &&
|
|
184
|
+
comparePlainDates(date, bandFrom) >= 0 &&
|
|
185
|
+
comparePlainDates(date, bandTo) <= 0;
|
|
186
|
+
return (_jsx("td", { "aria-current": getIsSameDay(today, date)
|
|
187
|
+
? "date"
|
|
188
|
+
: undefined, "aria-disabled": isEnabled ? undefined : true, "aria-label": formatPlainDate(date, {
|
|
189
|
+
dateStyle: "full",
|
|
190
|
+
locale,
|
|
191
|
+
}), "aria-selected": isSelected, className: toClassName(CELL_BASE_CLASS, FOCUS_RING_CLASS, !isEnabled && "cursor-not-allowed", isInBand && "bg-intent-accent-surface", isInBand &&
|
|
192
|
+
bandFrom !== null &&
|
|
193
|
+
getIsSameDay(bandFrom, date) &&
|
|
194
|
+
"rounded-s-md", isInBand &&
|
|
195
|
+
bandTo !== null &&
|
|
196
|
+
getIsSameDay(bandTo, date) &&
|
|
197
|
+
"rounded-e-md"), onClick: () => {
|
|
198
|
+
if (isEnabled) {
|
|
199
|
+
onSelectDate(date);
|
|
200
|
+
}
|
|
201
|
+
}, onKeyDown: handleKeyDown, onMouseEnter: () => {
|
|
202
|
+
onHoverDate(date);
|
|
203
|
+
}, ref: (element) => {
|
|
204
|
+
if (element) {
|
|
205
|
+
cellElements.current.set(isoDate, element);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
cellElements.current.delete(isoDate);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
// biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: the APG date-picker's own structure — the `<td>` IS the gridcell and carries the roving tabindex, rather than wrapping a `<button>` and putting 31 tab stops in one month. A `<td>`'s implicit role is `cell`, so `gridcell` is the one role the platform does not already imply, and `aria-selected` is not supported without it.
|
|
212
|
+
role: "gridcell", tabIndex: getIsSameDay(tabStopDate, date) ? 0 : -1, children: _jsx("span", { "aria-hidden": "true", className: toClassName(DAY_BASE_CLASS, isSelected &&
|
|
213
|
+
"bg-intent-accent-solid text-intent-accent-on-solid", !isSelected &&
|
|
214
|
+
getIsSameDay(today, date) &&
|
|
215
|
+
"border-intent-accent-border font-semibold", !isSelected &&
|
|
216
|
+
isEnabled &&
|
|
217
|
+
"hover:bg-intent-neutral-surface-hover", !isEnabled && "text-content-disabled"), children: date.day }) }, isoDate));
|
|
218
|
+
}) }, toIsoDate(week[0] ?? firstOfMonth)))) })] }));
|
|
219
|
+
};
|
|
220
|
+
//# sourceMappingURL=DateGrid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateGrid.js","sourceRoot":"","sources":["../../src/DatePicker/DateGrid.tsx"],"names":[],"mappings":"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"}
|