@hjmds/design-contracts 0.8.2
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 +208 -0
- package/dist/alert-dialog.d.ts +102 -0
- package/dist/alert-dialog.d.ts.map +1 -0
- package/dist/alert-dialog.js +136 -0
- package/dist/alert-dialog.js.map +1 -0
- package/dist/base-recipes.d.ts +184 -0
- package/dist/base-recipes.d.ts.map +1 -0
- package/dist/base-recipes.js +129 -0
- package/dist/base-recipes.js.map +1 -0
- package/dist/behaviors.d.ts +1254 -0
- package/dist/behaviors.d.ts.map +1 -0
- package/dist/behaviors.js +972 -0
- package/dist/behaviors.js.map +1 -0
- package/dist/bottom-navigation-defaults.d.ts +13 -0
- package/dist/bottom-navigation-defaults.d.ts.map +1 -0
- package/dist/bottom-navigation-defaults.js +10 -0
- package/dist/bottom-navigation-defaults.js.map +1 -0
- package/dist/bottom-navigation.d.ts +114 -0
- package/dist/bottom-navigation.d.ts.map +1 -0
- package/dist/bottom-navigation.js +223 -0
- package/dist/bottom-navigation.js.map +1 -0
- package/dist/breadcrumb.d.ts +97 -0
- package/dist/breadcrumb.d.ts.map +1 -0
- package/dist/breadcrumb.js +99 -0
- package/dist/breadcrumb.js.map +1 -0
- package/dist/calendar.d.ts +285 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +297 -0
- package/dist/calendar.js.map +1 -0
- package/dist/card.d.ts +39 -0
- package/dist/card.d.ts.map +1 -0
- package/dist/card.js +50 -0
- package/dist/card.js.map +1 -0
- package/dist/carousel.d.ts +180 -0
- package/dist/carousel.d.ts.map +1 -0
- package/dist/carousel.js +172 -0
- package/dist/carousel.js.map +1 -0
- package/dist/catalog.d.ts +7158 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +220 -0
- package/dist/catalog.js.map +1 -0
- package/dist/collection.d.ts +123 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +211 -0
- package/dist/collection.js.map +1 -0
- package/dist/color-references.d.ts +46 -0
- package/dist/color-references.d.ts.map +1 -0
- package/dist/color-references.js +39 -0
- package/dist/color-references.js.map +1 -0
- package/dist/colors.d.ts +56 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +90 -0
- package/dist/colors.js.map +1 -0
- package/dist/command-palette.d.ts +240 -0
- package/dist/command-palette.d.ts.map +1 -0
- package/dist/command-palette.js +97 -0
- package/dist/command-palette.js.map +1 -0
- package/dist/component-contracts.d.ts +137 -0
- package/dist/component-contracts.d.ts.map +1 -0
- package/dist/component-contracts.js +53 -0
- package/dist/component-contracts.js.map +1 -0
- package/dist/component-definitions.d.ts +135 -0
- package/dist/component-definitions.d.ts.map +1 -0
- package/dist/component-definitions.js +142 -0
- package/dist/component-definitions.js.map +1 -0
- package/dist/component-recipes.d.ts +3583 -0
- package/dist/component-recipes.d.ts.map +1 -0
- package/dist/component-recipes.js +1503 -0
- package/dist/component-recipes.js.map +1 -0
- package/dist/component-references.d.ts +416 -0
- package/dist/component-references.d.ts.map +1 -0
- package/dist/component-references.js +140 -0
- package/dist/component-references.js.map +1 -0
- package/dist/content-state.d.ts +104 -0
- package/dist/content-state.d.ts.map +1 -0
- package/dist/content-state.js +116 -0
- package/dist/content-state.js.map +1 -0
- package/dist/counter-badge-recipe.d.ts +80 -0
- package/dist/counter-badge-recipe.d.ts.map +1 -0
- package/dist/counter-badge-recipe.js +44 -0
- package/dist/counter-badge-recipe.js.map +1 -0
- package/dist/counter-badge.d.ts +12 -0
- package/dist/counter-badge.d.ts.map +1 -0
- package/dist/counter-badge.js +21 -0
- package/dist/counter-badge.js.map +1 -0
- package/dist/data-table.d.ts +203 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/data-table.js +182 -0
- package/dist/data-table.js.map +1 -0
- package/dist/date-picker.d.ts +268 -0
- package/dist/date-picker.d.ts.map +1 -0
- package/dist/date-picker.js +168 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/description-list.d.ts +68 -0
- package/dist/description-list.d.ts.map +1 -0
- package/dist/description-list.js +85 -0
- package/dist/description-list.js.map +1 -0
- package/dist/design-system-provider.d.ts +80 -0
- package/dist/design-system-provider.d.ts.map +1 -0
- package/dist/design-system-provider.js +147 -0
- package/dist/design-system-provider.js.map +1 -0
- package/dist/evidence.d.ts +49 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +133 -0
- package/dist/evidence.js.map +1 -0
- package/dist/file-picker.d.ts +183 -0
- package/dist/file-picker.d.ts.map +1 -0
- package/dist/file-picker.js +224 -0
- package/dist/file-picker.js.map +1 -0
- package/dist/floating-action-button.d.ts +143 -0
- package/dist/floating-action-button.d.ts.map +1 -0
- package/dist/floating-action-button.js +149 -0
- package/dist/floating-action-button.js.map +1 -0
- package/dist/form.d.ts +143 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +206 -0
- package/dist/form.js.map +1 -0
- package/dist/foundations.d.ts +300 -0
- package/dist/foundations.d.ts.map +1 -0
- package/dist/foundations.js +238 -0
- package/dist/foundations.js.map +1 -0
- package/dist/grid.d.ts +75 -0
- package/dist/grid.d.ts.map +1 -0
- package/dist/grid.js +133 -0
- package/dist/grid.js.map +1 -0
- package/dist/icon-button-recipe.d.ts +99 -0
- package/dist/icon-button-recipe.d.ts.map +1 -0
- package/dist/icon-button-recipe.js +53 -0
- package/dist/icon-button-recipe.js.map +1 -0
- package/dist/icon.d.ts +41 -0
- package/dist/icon.d.ts.map +1 -0
- package/dist/icon.js +147 -0
- package/dist/icon.js.map +1 -0
- package/dist/image.d.ts +82 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +100 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +118 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +118 -0
- package/dist/layout.js.map +1 -0
- package/dist/link.d.ts +57 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +142 -0
- package/dist/link.js.map +1 -0
- package/dist/load-more.d.ts +64 -0
- package/dist/load-more.d.ts.map +1 -0
- package/dist/load-more.js +124 -0
- package/dist/load-more.js.map +1 -0
- package/dist/mentions.d.ts +67 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +107 -0
- package/dist/mentions.js.map +1 -0
- package/dist/number-field.d.ts +208 -0
- package/dist/number-field.d.ts.map +1 -0
- package/dist/number-field.js +247 -0
- package/dist/number-field.js.map +1 -0
- package/dist/otp-field.d.ts +152 -0
- package/dist/otp-field.d.ts.map +1 -0
- package/dist/otp-field.js +117 -0
- package/dist/otp-field.js.map +1 -0
- package/dist/pagination.d.ts +181 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +226 -0
- package/dist/pagination.js.map +1 -0
- package/dist/password-field.d.ts +187 -0
- package/dist/password-field.d.ts.map +1 -0
- package/dist/password-field.js +120 -0
- package/dist/password-field.js.map +1 -0
- package/dist/popover.d.ts +152 -0
- package/dist/popover.d.ts.map +1 -0
- package/dist/popover.js +137 -0
- package/dist/popover.js.map +1 -0
- package/dist/progress-recipe.d.ts +43 -0
- package/dist/progress-recipe.d.ts.map +1 -0
- package/dist/progress-recipe.js +16 -0
- package/dist/progress-recipe.js.map +1 -0
- package/dist/recipes.d.ts +31 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/recipes.js +46 -0
- package/dist/recipes.js.map +1 -0
- package/dist/responsive.d.ts +27 -0
- package/dist/responsive.d.ts.map +1 -0
- package/dist/responsive.js +66 -0
- package/dist/responsive.js.map +1 -0
- package/dist/result.d.ts +111 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +97 -0
- package/dist/result.js.map +1 -0
- package/dist/selection-helpers.d.ts +16 -0
- package/dist/selection-helpers.d.ts.map +1 -0
- package/dist/selection-helpers.js +52 -0
- package/dist/selection-helpers.js.map +1 -0
- package/dist/semantic-colors.d.ts +275 -0
- package/dist/semantic-colors.d.ts.map +1 -0
- package/dist/semantic-colors.js +84 -0
- package/dist/semantic-colors.js.map +1 -0
- package/dist/sheet.d.ts +51 -0
- package/dist/sheet.d.ts.map +1 -0
- package/dist/sheet.js +69 -0
- package/dist/sheet.js.map +1 -0
- package/dist/showcase.d.ts +153 -0
- package/dist/showcase.d.ts.map +1 -0
- package/dist/showcase.js +210 -0
- package/dist/showcase.js.map +1 -0
- package/dist/side-panel.d.ts +199 -0
- package/dist/side-panel.d.ts.map +1 -0
- package/dist/side-panel.js +111 -0
- package/dist/side-panel.js.map +1 -0
- package/dist/slider.d.ts +138 -0
- package/dist/slider.d.ts.map +1 -0
- package/dist/slider.js +150 -0
- package/dist/slider.js.map +1 -0
- package/dist/splitter.d.ts +113 -0
- package/dist/splitter.d.ts.map +1 -0
- package/dist/splitter.js +99 -0
- package/dist/splitter.js.map +1 -0
- package/dist/statistic.d.ts +41 -0
- package/dist/statistic.d.ts.map +1 -0
- package/dist/statistic.js +76 -0
- package/dist/statistic.js.map +1 -0
- package/dist/steps.d.ts +196 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/steps.js +160 -0
- package/dist/steps.js.map +1 -0
- package/dist/tag.d.ts +128 -0
- package/dist/tag.d.ts.map +1 -0
- package/dist/tag.js +93 -0
- package/dist/tag.js.map +1 -0
- package/dist/timeline.d.ts +147 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +127 -0
- package/dist/timeline.js.map +1 -0
- package/dist/toast.d.ts +164 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +529 -0
- package/dist/toast.js.map +1 -0
- package/dist/tokens.d.ts +9 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +9 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tooltip.d.ts +44 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +88 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/tour.d.ts +218 -0
- package/dist/tour.d.ts.map +1 -0
- package/dist/tour.js +211 -0
- package/dist/tour.js.map +1 -0
- package/dist/transfer-list.d.ts +207 -0
- package/dist/transfer-list.d.ts.map +1 -0
- package/dist/transfer-list.js +193 -0
- package/dist/transfer-list.js.map +1 -0
- package/dist/tree-select.d.ts +78 -0
- package/dist/tree-select.d.ts.map +1 -0
- package/dist/tree-select.js +132 -0
- package/dist/tree-select.js.map +1 -0
- package/dist/tree.d.ts +206 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +223 -0
- package/dist/tree.js.map +1 -0
- package/dist/upload-item.d.ts +173 -0
- package/dist/upload-item.d.ts.map +1 -0
- package/dist/upload-item.js +156 -0
- package/dist/upload-item.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/docs/affix.md +63 -0
- package/docs/anchor.md +59 -0
- package/docs/ant-design-coverage.md +119 -0
- package/docs/app-provider.md +50 -0
- package/docs/app-rn-adoption.md +227 -0
- package/docs/architecture.md +320 -0
- package/docs/authoring-brief.md +87 -0
- package/docs/border-beam.md +66 -0
- package/docs/bottom-navigation.md +127 -0
- package/docs/breadcrumb.md +82 -0
- package/docs/calendar.md +154 -0
- package/docs/carousel.md +130 -0
- package/docs/cascader.md +93 -0
- package/docs/catalog-decision-status.md +306 -0
- package/docs/color-picker.md +72 -0
- package/docs/command-palette.md +116 -0
- package/docs/confirm-popover.md +93 -0
- package/docs/consistency-audit.md +383 -0
- package/docs/consumer-release-gate.md +90 -0
- package/docs/content-state.md +156 -0
- package/docs/context-panel.md +88 -0
- package/docs/cross-platform-core-normalization.md +118 -0
- package/docs/data-table.md +79 -0
- package/docs/date-picker.md +84 -0
- package/docs/description-list.md +75 -0
- package/docs/design-system-provider.md +139 -0
- package/docs/dropdown.md +78 -0
- package/docs/expansion-roadmap.md +285 -0
- package/docs/file-picker.md +59 -0
- package/docs/floating-action-button.md +111 -0
- package/docs/form.md +134 -0
- package/docs/generated/component-maturity.md +103 -0
- package/docs/generated/renderer-evidence.json +5597 -0
- package/docs/generated/renderer-evidence.md +132 -0
- package/docs/generated/showcase-manifest.json +3732 -0
- package/docs/icon.md +22 -0
- package/docs/identity.md +121 -0
- package/docs/image.md +84 -0
- package/docs/implementation-0.5.md +85 -0
- package/docs/layout-primitives.md +107 -0
- package/docs/layout.md +83 -0
- package/docs/library-reference-decisions.md +123 -0
- package/docs/link.md +67 -0
- package/docs/load-more.md +31 -0
- package/docs/mentions.md +82 -0
- package/docs/migration-0.2.md +108 -0
- package/docs/migration-0.3.md +72 -0
- package/docs/migration-0.5.md +82 -0
- package/docs/migration-0.6.md +197 -0
- package/docs/notification.md +54 -0
- package/docs/number-field.md +82 -0
- package/docs/otp-field.md +103 -0
- package/docs/pagination.md +139 -0
- package/docs/password-field.md +98 -0
- package/docs/popover.md +124 -0
- package/docs/promotion-candidates.md +169 -0
- package/docs/qr-code.md +69 -0
- package/docs/rating.md +58 -0
- package/docs/responsive-grid.md +90 -0
- package/docs/result.md +96 -0
- package/docs/showcase.md +97 -0
- package/docs/side-panel.md +71 -0
- package/docs/slider.md +79 -0
- package/docs/splitter.md +55 -0
- package/docs/statistic.md +16 -0
- package/docs/steps.md +115 -0
- package/docs/tag.md +57 -0
- package/docs/time-picker.md +88 -0
- package/docs/timeline.md +146 -0
- package/docs/toast.md +127 -0
- package/docs/tooltip.md +58 -0
- package/docs/tour.md +136 -0
- package/docs/transfer-list.md +94 -0
- package/docs/tree-select.md +101 -0
- package/docs/tree.md +123 -0
- package/docs/upload-item.md +68 -0
- package/docs/utility.md +53 -0
- package/docs/virtual-list.md +70 -0
- package/docs/watermark.md +58 -0
- package/package.json +402 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { collectionItemContract, focusIndicatorContract } from "./component-contracts.js";
|
|
2
|
+
import { control, fontWeight, spacing } from "./foundations.js";
|
|
3
|
+
import { semanticColors } from "./semantic-colors.js";
|
|
4
|
+
export const dataTableColumnDefaults = {
|
|
5
|
+
align: "start",
|
|
6
|
+
sortable: false,
|
|
7
|
+
};
|
|
8
|
+
function assertNonEmpty(value, field) {
|
|
9
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
10
|
+
throw new TypeError(`DataTable ${field} must not be empty`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function validateDataTableColumns(columns) {
|
|
14
|
+
if (columns.length === 0) {
|
|
15
|
+
throw new TypeError("DataTable must declare at least one column");
|
|
16
|
+
}
|
|
17
|
+
const ids = new Set();
|
|
18
|
+
for (const column of columns) {
|
|
19
|
+
assertNonEmpty(column.id, "column id");
|
|
20
|
+
if (ids.has(column.id)) {
|
|
21
|
+
throw new TypeError(`Duplicate DataTable column id: ${column.id}`);
|
|
22
|
+
}
|
|
23
|
+
ids.add(column.id);
|
|
24
|
+
assertNonEmpty(column.header, `column "${column.id}" header`);
|
|
25
|
+
if (column.width !== undefined && (!Number.isFinite(column.width) || column.width <= 0)) {
|
|
26
|
+
throw new RangeError(`DataTable column "${column.id}" width must be a positive finite number`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function validateDataTableRows(rows) {
|
|
31
|
+
const ids = new Set();
|
|
32
|
+
for (const row of rows) {
|
|
33
|
+
assertNonEmpty(row.id, "row id");
|
|
34
|
+
if (ids.has(row.id)) {
|
|
35
|
+
throw new TypeError(`Duplicate DataTable row id: ${row.id}`);
|
|
36
|
+
}
|
|
37
|
+
ids.add(row.id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Header "select all" state as the same tri-state `CheckboxState` the
|
|
42
|
+
* selection column's checkbox already renders (check/dash), instead of a new
|
|
43
|
+
* `"none" | "some" | "all"` enum the recipe would have to translate again.
|
|
44
|
+
* Disabled rows are excluded from both the denominator and the count.
|
|
45
|
+
*/
|
|
46
|
+
export function resolveDataTableSelectAllState(rows, selectedKeys) {
|
|
47
|
+
const selectable = rows.filter((row) => !row.disabled);
|
|
48
|
+
if (selectable.length === 0)
|
|
49
|
+
return false;
|
|
50
|
+
const selectedCount = selectable.filter((row) => selectedKeys.has(row.id)).length;
|
|
51
|
+
if (selectedCount === 0)
|
|
52
|
+
return false;
|
|
53
|
+
if (selectedCount === selectable.length)
|
|
54
|
+
return true;
|
|
55
|
+
return "mixed";
|
|
56
|
+
}
|
|
57
|
+
export const dataTableDefaults = {
|
|
58
|
+
sortCycle: "three-state",
|
|
59
|
+
};
|
|
60
|
+
export function validateDataTableSortState(state, columns) {
|
|
61
|
+
if (state === null)
|
|
62
|
+
return;
|
|
63
|
+
if (state.direction !== "ascending" && state.direction !== "descending") {
|
|
64
|
+
throw new TypeError(`Unsupported DataTable sort direction: ${String(state.direction)}`);
|
|
65
|
+
}
|
|
66
|
+
const column = columns.find((candidate) => candidate.id === state.columnId);
|
|
67
|
+
if (!column) {
|
|
68
|
+
throw new TypeError(`DataTable sort references unknown column id: ${String(state.columnId)}`);
|
|
69
|
+
}
|
|
70
|
+
if (!column.sortable) {
|
|
71
|
+
throw new TypeError(`DataTable column "${String(state.columnId)}" is not sortable`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Pure toggle: does not read or write product data. The product still owns
|
|
76
|
+
* actually re-sorting rows (Combobox's local/external filtering split is the
|
|
77
|
+
* same boundary — HJM judges intent, never executes the data operation).
|
|
78
|
+
*/
|
|
79
|
+
export function getNextDataTableSortState(current, columnId, cycle = dataTableDefaults.sortCycle) {
|
|
80
|
+
if (current === null || current.columnId !== columnId) {
|
|
81
|
+
return { columnId, direction: "ascending" };
|
|
82
|
+
}
|
|
83
|
+
if (current.direction === "ascending") {
|
|
84
|
+
return { columnId, direction: "descending" };
|
|
85
|
+
}
|
|
86
|
+
return cycle === "three-state" ? null : { columnId, direction: "ascending" };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reuses `collectionItemContract`'s hover/selected/focus tokens instead of a
|
|
90
|
+
* second set of row-interaction colors — Menu, Select, and DataTable rows
|
|
91
|
+
* stay one visual vocabulary.
|
|
92
|
+
*/
|
|
93
|
+
export const dataTableRecipe = {
|
|
94
|
+
slots: [
|
|
95
|
+
"root",
|
|
96
|
+
"header",
|
|
97
|
+
"headerCell",
|
|
98
|
+
"sortButton",
|
|
99
|
+
"sortIcon",
|
|
100
|
+
"row",
|
|
101
|
+
"cell",
|
|
102
|
+
"selectionCell",
|
|
103
|
+
"emptyState",
|
|
104
|
+
"errorState",
|
|
105
|
+
],
|
|
106
|
+
defaults: { density: "regular" },
|
|
107
|
+
density: {
|
|
108
|
+
compact: { paddingVertical: spacing.xs, paddingHorizontal: spacing.sm },
|
|
109
|
+
regular: { paddingVertical: spacing.sm, paddingHorizontal: spacing.md },
|
|
110
|
+
},
|
|
111
|
+
header: {
|
|
112
|
+
background: semanticColors.surface.sunken,
|
|
113
|
+
color: semanticColors.content.secondary,
|
|
114
|
+
textVariant: "label",
|
|
115
|
+
fontWeight: fontWeight.bold,
|
|
116
|
+
borderBottom: semanticColors.border.default,
|
|
117
|
+
},
|
|
118
|
+
row: {
|
|
119
|
+
borderBottom: semanticColors.border.subtle,
|
|
120
|
+
hoverBackground: collectionItemContract.highlightedBackground,
|
|
121
|
+
selectedBackground: collectionItemContract.selectedBackground,
|
|
122
|
+
},
|
|
123
|
+
cell: {
|
|
124
|
+
color: semanticColors.content.body,
|
|
125
|
+
textVariant: "body",
|
|
126
|
+
},
|
|
127
|
+
sortButton: {
|
|
128
|
+
minTarget: control.minTouchTarget,
|
|
129
|
+
color: semanticColors.content.secondary,
|
|
130
|
+
activeColor: semanticColors.content.brand,
|
|
131
|
+
},
|
|
132
|
+
selectionCell: {
|
|
133
|
+
width: control.minTouchTarget,
|
|
134
|
+
},
|
|
135
|
+
emptyState: {
|
|
136
|
+
color: semanticColors.content.secondary,
|
|
137
|
+
textVariant: "body",
|
|
138
|
+
},
|
|
139
|
+
errorState: {
|
|
140
|
+
color: semanticColors.content.danger,
|
|
141
|
+
textVariant: "body",
|
|
142
|
+
},
|
|
143
|
+
states: {
|
|
144
|
+
focus: focusIndicatorContract,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* No roving-tabindex grid keyboard navigation (arrow keys between cells):
|
|
149
|
+
* no product using this contract has measured demand for full ARIA-grid
|
|
150
|
+
* traversal, and it is a materially bigger accessibility surface than a
|
|
151
|
+
* sortable header button plus native checkbox/radio tab stops.
|
|
152
|
+
*/
|
|
153
|
+
export const dataTableBehavior = {
|
|
154
|
+
controlled: ["selection"],
|
|
155
|
+
inputs: ["columns", "rows", "sortState", "asyncState"],
|
|
156
|
+
events: ["onSortChange"],
|
|
157
|
+
configuration: { sortCycle: ["two-state", "three-state"] },
|
|
158
|
+
defaults: dataTableDefaults,
|
|
159
|
+
stateAxes: {
|
|
160
|
+
value: ["selected"],
|
|
161
|
+
content: ["idle", "loading", "loadingMore", "empty", "error"],
|
|
162
|
+
},
|
|
163
|
+
web: {
|
|
164
|
+
roles: ["table", "row", "columnheader", "cell", "button", "checkbox", "radio"],
|
|
165
|
+
keyboard: ["Tab", "Enter", "Space"],
|
|
166
|
+
focus: "native",
|
|
167
|
+
},
|
|
168
|
+
native: { roles: [], states: [], actions: [] },
|
|
169
|
+
scenarios: [
|
|
170
|
+
"row-selection-reuses-the-shared-collection-selection-model-unchanged",
|
|
171
|
+
"select-all-reuses-the-shared-tri-state-checkbox-value",
|
|
172
|
+
"async-state-reuses-the-shared-collection-async-state-unchanged",
|
|
173
|
+
"sortable-header-is-a-button-inside-the-columnheader-not-the-header-itself",
|
|
174
|
+
"sort-direction-values-pass-through-to-aria-sort-without-translation",
|
|
175
|
+
"disabled-rows-are-excluded-from-select-all-accounting",
|
|
176
|
+
"pagination-or-load-more-is-composed-beneath-the-table-not-owned-by-it",
|
|
177
|
+
"row-expansion-is-not-owned-here-compose-the-disclosure-group-contract-per-row",
|
|
178
|
+
"a-data-cell-exposes-at-most-one-focusable-control",
|
|
179
|
+
"no-roving-tabindex-grid-navigation-is-implied",
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
//# sourceMappingURL=data-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-table.js","sourceRoot":"","sources":["../src/data-table.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type {\n AsyncCollectionState,\n BehaviorContract,\n CheckboxState,\n CollectionKey,\n CollectionSelectionModel,\n} from \"./behaviors.js\";\nimport { collectionItemContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, fontWeight, spacing, type FontWeightValue, type TextVariant } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type DataTableAlign = \"start\" | \"center\" | \"end\";\n\nexport type DataTableColumnDescriptor<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: Key;\n /** Visible column header text; also the columnheader's accessible name. */\n header: string;\n align?: DataTableAlign;\n sortable?: boolean;\n /** Layout hint only (px or flex-basis); renderers may ignore or clamp it. */\n width?: number;\n}>;\n\nexport const dataTableColumnDefaults = {\n align: \"start\",\n sortable: false,\n} as const satisfies Readonly<{ align: DataTableAlign; sortable: boolean }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`DataTable ${field} must not be empty`);\n }\n}\n\nexport function validateDataTableColumns<Key extends CollectionKey>(\n columns: readonly DataTableColumnDescriptor<Key>[],\n): void {\n if (columns.length === 0) {\n throw new TypeError(\"DataTable must declare at least one column\");\n }\n const ids = new Set<Key>();\n for (const column of columns) {\n assertNonEmpty(column.id, \"column id\");\n if (ids.has(column.id)) {\n throw new TypeError(`Duplicate DataTable column id: ${column.id}`);\n }\n ids.add(column.id);\n assertNonEmpty(column.header, `column \"${column.id}\" header`);\n if (column.width !== undefined && (!Number.isFinite(column.width) || column.width <= 0)) {\n throw new RangeError(`DataTable column \"${column.id}\" width must be a positive finite number`);\n }\n }\n}\n\n/**\n * Row identity and selection deliberately do NOT reuse `CollectionItemDescriptor`\n * verbatim: a table row has no single visible `label`/`textValue` the way a\n * Menu or Select item does — its \"content\" is however many columns the\n * product renders per row. Only the parts of the Collection base contract\n * that are genuinely row-shaped (`CollectionKey` identity, `disabled`) apply.\n */\nexport type DataTableRowDescriptor<Key extends CollectionKey = CollectionKey> = Readonly<{\n id: Key;\n disabled?: boolean;\n}>;\n\nexport function validateDataTableRows<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n): void {\n const ids = new Set<Key>();\n for (const row of rows) {\n assertNonEmpty(row.id, \"row id\");\n if (ids.has(row.id)) {\n throw new TypeError(`Duplicate DataTable row id: ${row.id}`);\n }\n ids.add(row.id);\n }\n}\n\n/** Row selection is the unmodified Collection base contract — no DataTable-only selection shape. */\nexport type DataTableSelection<Key extends CollectionKey = CollectionKey> =\n CollectionSelectionModel<Key>;\n\n/**\n * Header \"select all\" state as the same tri-state `CheckboxState` the\n * selection column's checkbox already renders (check/dash), instead of a new\n * `\"none\" | \"some\" | \"all\"` enum the recipe would have to translate again.\n * Disabled rows are excluded from both the denominator and the count.\n */\nexport function resolveDataTableSelectAllState<Key extends CollectionKey>(\n rows: readonly DataTableRowDescriptor<Key>[],\n selectedKeys: ReadonlySet<Key>,\n): CheckboxState {\n const selectable = rows.filter((row) => !row.disabled);\n if (selectable.length === 0) return false;\n const selectedCount = selectable.filter((row) => selectedKeys.has(row.id)).length;\n if (selectedCount === 0) return false;\n if (selectedCount === selectable.length) return true;\n return \"mixed\";\n}\n\n/**\n * Values are the literal `aria-sort` vocabulary (minus `\"none\"`, which is\n * `null`) so the Web renderer passes them through without a translation\n * table — the same \"pass the platform value through\" choice Slider makes\n * for `valueText`.\n */\nexport type DataTableSortDirection = \"ascending\" | \"descending\";\n\nexport type DataTableSortState<Key extends CollectionKey = CollectionKey> = Readonly<{\n columnId: Key;\n direction: DataTableSortDirection;\n}> | null;\n\nexport type DataTableSortCycle = \"two-state\" | \"three-state\";\n\nexport const dataTableDefaults = {\n sortCycle: \"three-state\",\n} as const satisfies Readonly<{ sortCycle: DataTableSortCycle }>;\n\nexport function validateDataTableSortState<Key extends CollectionKey>(\n state: DataTableSortState<Key>,\n columns: readonly DataTableColumnDescriptor<Key>[],\n): void {\n if (state === null) return;\n if (state.direction !== \"ascending\" && state.direction !== \"descending\") {\n throw new TypeError(`Unsupported DataTable sort direction: ${String(state.direction)}`);\n }\n const column = columns.find((candidate) => candidate.id === state.columnId);\n if (!column) {\n throw new TypeError(`DataTable sort references unknown column id: ${String(state.columnId)}`);\n }\n if (!column.sortable) {\n throw new TypeError(`DataTable column \"${String(state.columnId)}\" is not sortable`);\n }\n}\n\n/**\n * Pure toggle: does not read or write product data. The product still owns\n * actually re-sorting rows (Combobox's local/external filtering split is the\n * same boundary — HJM judges intent, never executes the data operation).\n */\nexport function getNextDataTableSortState<Key extends CollectionKey>(\n current: DataTableSortState<Key>,\n columnId: Key,\n cycle: DataTableSortCycle = dataTableDefaults.sortCycle,\n): DataTableSortState<Key> {\n if (current === null || current.columnId !== columnId) {\n return { columnId, direction: \"ascending\" };\n }\n if (current.direction === \"ascending\") {\n return { columnId, direction: \"descending\" };\n }\n return cycle === \"three-state\" ? null : { columnId, direction: \"ascending\" };\n}\n\n/** The whole table's row-loading status — the unmodified Collection base async contract. */\nexport type DataTableAsyncState = AsyncCollectionState;\n\nexport type DataTableDensity = \"compact\" | \"regular\";\n\n/**\n * Reuses `collectionItemContract`'s hover/selected/focus tokens instead of a\n * second set of row-interaction colors — Menu, Select, and DataTable rows\n * stay one visual vocabulary.\n */\nexport const dataTableRecipe = {\n slots: [\n \"root\",\n \"header\",\n \"headerCell\",\n \"sortButton\",\n \"sortIcon\",\n \"row\",\n \"cell\",\n \"selectionCell\",\n \"emptyState\",\n \"errorState\",\n ] as const,\n defaults: { density: \"regular\" },\n density: {\n compact: { paddingVertical: spacing.xs, paddingHorizontal: spacing.sm },\n regular: { paddingVertical: spacing.sm, paddingHorizontal: spacing.md },\n },\n header: {\n background: semanticColors.surface.sunken,\n color: semanticColors.content.secondary,\n textVariant: \"label\",\n fontWeight: fontWeight.bold,\n borderBottom: semanticColors.border.default,\n },\n row: {\n borderBottom: semanticColors.border.subtle,\n hoverBackground: collectionItemContract.highlightedBackground,\n selectedBackground: collectionItemContract.selectedBackground,\n },\n cell: {\n color: semanticColors.content.body,\n textVariant: \"body\",\n },\n sortButton: {\n minTarget: control.minTouchTarget,\n color: semanticColors.content.secondary,\n activeColor: semanticColors.content.brand,\n },\n selectionCell: {\n width: control.minTouchTarget,\n },\n emptyState: {\n color: semanticColors.content.secondary,\n textVariant: \"body\",\n },\n errorState: {\n color: semanticColors.content.danger,\n textVariant: \"body\",\n },\n states: {\n focus: focusIndicatorContract,\n },\n} as const satisfies {\n slots: readonly [\n \"root\",\n \"header\",\n \"headerCell\",\n \"sortButton\",\n \"sortIcon\",\n \"row\",\n \"cell\",\n \"selectionCell\",\n \"emptyState\",\n \"errorState\",\n ];\n defaults: { density: DataTableDensity };\n density: Record<DataTableDensity, { paddingVertical: number; paddingHorizontal: number }>;\n header: {\n background: ColorReference;\n color: ColorReference;\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n borderBottom: ColorReference;\n };\n row: {\n borderBottom: ColorReference;\n hoverBackground: ColorReference;\n selectedBackground: ColorReference;\n };\n cell: { color: ColorReference; textVariant: TextVariant };\n sortButton: { minTarget: number; color: ColorReference; activeColor: ColorReference };\n selectionCell: { width: number };\n emptyState: { color: ColorReference; textVariant: TextVariant };\n errorState: { color: ColorReference; textVariant: TextVariant };\n states: { focus: typeof focusIndicatorContract };\n};\n\n/**\n * No roving-tabindex grid keyboard navigation (arrow keys between cells):\n * no product using this contract has measured demand for full ARIA-grid\n * traversal, and it is a materially bigger accessibility surface than a\n * sortable header button plus native checkbox/radio tab stops.\n */\nexport const dataTableBehavior = {\n controlled: [\"selection\"],\n inputs: [\"columns\", \"rows\", \"sortState\", \"asyncState\"],\n events: [\"onSortChange\"],\n configuration: { sortCycle: [\"two-state\", \"three-state\"] },\n defaults: dataTableDefaults,\n stateAxes: {\n value: [\"selected\"],\n content: [\"idle\", \"loading\", \"loadingMore\", \"empty\", \"error\"],\n },\n web: {\n roles: [\"table\", \"row\", \"columnheader\", \"cell\", \"button\", \"checkbox\", \"radio\"],\n keyboard: [\"Tab\", \"Enter\", \"Space\"],\n focus: \"native\",\n },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"row-selection-reuses-the-shared-collection-selection-model-unchanged\",\n \"select-all-reuses-the-shared-tri-state-checkbox-value\",\n \"async-state-reuses-the-shared-collection-async-state-unchanged\",\n \"sortable-header-is-a-button-inside-the-columnheader-not-the-header-itself\",\n \"sort-direction-values-pass-through-to-aria-sort-without-translation\",\n \"disabled-rows-are-excluded-from-select-all-accounting\",\n \"pagination-or-load-more-is-composed-beneath-the-table-not-owned-by-it\",\n \"row-expansion-is-not-owned-here-compose-the-disclosure-group-contract-per-row\",\n \"a-data-cell-exposes-at-most-one-focusable-control\",\n \"no-roving-tabindex-grid-navigation-is-implied\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { type CalendarGridDescriptor, type CalendarMonthNavigation, type ComposeCalendarAccessibleName, type ResolvedCalendarGridCell } from "./calendar.js";
|
|
2
|
+
/**
|
|
3
|
+
* Mirrors `SelectLabel` by shape rather than importing it: Select's type
|
|
4
|
+
* lives in `collection.ts` for Select/Combobox/Menu's shared collection
|
|
5
|
+
* story, and DatePicker's date grid deliberately does not adopt that
|
|
6
|
+
* collection contract (see docs/calendar.md). Repeating the small, obvious
|
|
7
|
+
* "visible label or accessibility label" shape here keeps this module
|
|
8
|
+
* self-sufficient while staying consistent with the rest of the system.
|
|
9
|
+
*/
|
|
10
|
+
export type DatePickerLabel = Readonly<{
|
|
11
|
+
label: string;
|
|
12
|
+
accessibilityLabel?: string;
|
|
13
|
+
}> | Readonly<{
|
|
14
|
+
label?: never;
|
|
15
|
+
accessibilityLabel: string;
|
|
16
|
+
}>;
|
|
17
|
+
export type DatePickerOpenChangeReason = "trigger" | "keyboard" | "selection" | "clear" | "escape" | "outside" | "blur" | "programmatic";
|
|
18
|
+
export type DatePickerOpenState = Readonly<{
|
|
19
|
+
open: boolean;
|
|
20
|
+
defaultOpen?: never;
|
|
21
|
+
onOpenChange(open: boolean, reason: DatePickerOpenChangeReason): void;
|
|
22
|
+
}> | Readonly<{
|
|
23
|
+
open?: never;
|
|
24
|
+
defaultOpen?: boolean;
|
|
25
|
+
onOpenChange?: (open: boolean, reason: DatePickerOpenChangeReason) => void;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* A committed date closes the popover/sheet the same way Select's
|
|
29
|
+
* `selection-requests-close-and-restores-trigger-focus` scenario works.
|
|
30
|
+
* `clear` commits `null` without reopening — this is the field's only
|
|
31
|
+
* "empty" affordance; there is no separate disallow-empty configuration
|
|
32
|
+
* because an optional date field is the common case (unlike Select, which
|
|
33
|
+
* more often names a required option).
|
|
34
|
+
*/
|
|
35
|
+
export type DatePickerSelection = Readonly<{
|
|
36
|
+
selectedDate: string | null;
|
|
37
|
+
defaultSelectedDate?: never;
|
|
38
|
+
onSelectionChange(date: string | null, reason: "activate" | "clear"): void;
|
|
39
|
+
}> | Readonly<{
|
|
40
|
+
selectedDate?: never;
|
|
41
|
+
defaultSelectedDate?: string | null;
|
|
42
|
+
onSelectionChange?: (date: string | null, reason: "activate" | "clear") => void;
|
|
43
|
+
}>;
|
|
44
|
+
export declare function validateDatePickerSelection(selection: DatePickerSelection): void;
|
|
45
|
+
/**
|
|
46
|
+
* Field + grid + adaptive overlay. The grid itself is the exact
|
|
47
|
+
* `CalendarGridDescriptor` from `calendar.ts` — Web renders it inside an
|
|
48
|
+
* anchored popover, Native inside a Sheet (the `Select` adaptive split:
|
|
49
|
+
* `adaptive: { web: "popover", native: "sheet" }`), but neither renderer
|
|
50
|
+
* reinterprets the grid's cells, navigation, or accessible-name contract.
|
|
51
|
+
*/
|
|
52
|
+
export type DatePickerDescriptor<Content = unknown> = Readonly<{
|
|
53
|
+
grid: CalendarGridDescriptor<Content>;
|
|
54
|
+
/** Product-formatted trigger copy for the committed date ("2026년 8월 19일"); DatePicker never formats a date itself. */
|
|
55
|
+
displayValue: string | null;
|
|
56
|
+
placeholder: string;
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
readOnly?: boolean;
|
|
59
|
+
invalid?: boolean;
|
|
60
|
+
}> & DatePickerLabel & DatePickerOpenState & DatePickerSelection & CalendarMonthNavigation;
|
|
61
|
+
export declare function validateDatePickerDescriptor<Content>(descriptor: DatePickerDescriptor<Content>): void;
|
|
62
|
+
/** Trigger copy is the committed display value, or the placeholder when nothing is selected yet. */
|
|
63
|
+
export declare function resolveDatePickerTriggerText(descriptor: Pick<DatePickerDescriptor, "displayValue" | "placeholder">): string;
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the popover/sheet content: the exact same cell shape, today/
|
|
66
|
+
* selected marks, and accessible names Calendar renders inline. DatePicker
|
|
67
|
+
* adds no per-cell semantics of its own — only the surrounding field/overlay
|
|
68
|
+
* lifecycle is new.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveDatePickerGrid<Content>(descriptor: DatePickerDescriptor<Content>, options: Readonly<{
|
|
71
|
+
composeAccessibleName: ComposeCalendarAccessibleName<Content>;
|
|
72
|
+
}>): readonly ResolvedCalendarGridCell<Content>[];
|
|
73
|
+
export type DatePickerSize = "medium" | "large";
|
|
74
|
+
/**
|
|
75
|
+
* Reuses `fieldFrameContract`/`formSupportContract` verbatim, exactly like
|
|
76
|
+
* NumberField and Select each do independently — a second field frame would
|
|
77
|
+
* drift the first time either one's border or height changes. `calendar`
|
|
78
|
+
* anatomy is intentionally not re-declared here: the popover/sheet mounts
|
|
79
|
+
* `calendarRecipe` unchanged, so this recipe only owns the field/trigger and
|
|
80
|
+
* the overlay frame around it.
|
|
81
|
+
*/
|
|
82
|
+
export declare const datePickerRecipe: {
|
|
83
|
+
readonly slots: readonly ["root", "label", "trigger", "leading", "value", "placeholder", "clear", "indicator", "description", "error", "popover"];
|
|
84
|
+
readonly defaults: {
|
|
85
|
+
readonly size: "medium";
|
|
86
|
+
};
|
|
87
|
+
readonly adaptive: {
|
|
88
|
+
readonly web: "popover";
|
|
89
|
+
readonly native: "sheet";
|
|
90
|
+
};
|
|
91
|
+
readonly frame: {
|
|
92
|
+
readonly background: Readonly<{
|
|
93
|
+
source: "theme";
|
|
94
|
+
key: "surface";
|
|
95
|
+
alpha?: number;
|
|
96
|
+
}>;
|
|
97
|
+
readonly border: Readonly<{
|
|
98
|
+
source: "theme";
|
|
99
|
+
key: "textMuted";
|
|
100
|
+
alpha?: number;
|
|
101
|
+
}>;
|
|
102
|
+
readonly focusBorder: Readonly<{
|
|
103
|
+
source: "theme";
|
|
104
|
+
key: "contentBrand";
|
|
105
|
+
alpha?: number;
|
|
106
|
+
}>;
|
|
107
|
+
readonly invalidBorder: Readonly<{
|
|
108
|
+
source: "theme";
|
|
109
|
+
key: "danger";
|
|
110
|
+
alpha?: number;
|
|
111
|
+
}>;
|
|
112
|
+
readonly radius: "md";
|
|
113
|
+
readonly borderWidth: 1;
|
|
114
|
+
readonly minHeight: 44;
|
|
115
|
+
readonly paddingHorizontal: 16;
|
|
116
|
+
};
|
|
117
|
+
readonly sizes: {
|
|
118
|
+
readonly medium: {
|
|
119
|
+
readonly minHeight: 44;
|
|
120
|
+
readonly paddingHorizontal: 16;
|
|
121
|
+
readonly textVariant: "body";
|
|
122
|
+
readonly glyph: "sm";
|
|
123
|
+
};
|
|
124
|
+
readonly large: {
|
|
125
|
+
readonly minHeight: 52;
|
|
126
|
+
readonly paddingHorizontal: 20;
|
|
127
|
+
readonly textVariant: "bodyLarge";
|
|
128
|
+
readonly glyph: "md";
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
readonly support: {
|
|
132
|
+
readonly label: {
|
|
133
|
+
readonly color: Readonly<{
|
|
134
|
+
source: "theme";
|
|
135
|
+
key: "textBody";
|
|
136
|
+
alpha?: number;
|
|
137
|
+
}>;
|
|
138
|
+
readonly textVariant: "body";
|
|
139
|
+
readonly fontWeight: "600";
|
|
140
|
+
};
|
|
141
|
+
readonly hint: {
|
|
142
|
+
readonly color: Readonly<{
|
|
143
|
+
source: "theme";
|
|
144
|
+
key: "textMuted";
|
|
145
|
+
alpha?: number;
|
|
146
|
+
}>;
|
|
147
|
+
readonly textVariant: "label";
|
|
148
|
+
};
|
|
149
|
+
readonly error: {
|
|
150
|
+
readonly color: Readonly<{
|
|
151
|
+
source: "theme";
|
|
152
|
+
key: "danger";
|
|
153
|
+
alpha?: number;
|
|
154
|
+
}>;
|
|
155
|
+
readonly textVariant: "label";
|
|
156
|
+
};
|
|
157
|
+
readonly gap: 8;
|
|
158
|
+
};
|
|
159
|
+
readonly value: {
|
|
160
|
+
readonly color: Readonly<{
|
|
161
|
+
source: "theme";
|
|
162
|
+
key: "textBody";
|
|
163
|
+
alpha?: number;
|
|
164
|
+
}>;
|
|
165
|
+
readonly placeholderColor: Readonly<{
|
|
166
|
+
source: "theme";
|
|
167
|
+
key: "textMuted";
|
|
168
|
+
alpha?: number;
|
|
169
|
+
}>;
|
|
170
|
+
};
|
|
171
|
+
readonly leading: {
|
|
172
|
+
readonly color: Readonly<{
|
|
173
|
+
source: "theme";
|
|
174
|
+
key: "textMuted";
|
|
175
|
+
alpha?: number;
|
|
176
|
+
}>;
|
|
177
|
+
readonly icon: "calendar";
|
|
178
|
+
};
|
|
179
|
+
readonly indicator: {
|
|
180
|
+
readonly color: Readonly<{
|
|
181
|
+
source: "theme";
|
|
182
|
+
key: "textMuted";
|
|
183
|
+
alpha?: number;
|
|
184
|
+
}>;
|
|
185
|
+
};
|
|
186
|
+
readonly clear: {
|
|
187
|
+
readonly diameter: 36;
|
|
188
|
+
readonly hitSlop: 4;
|
|
189
|
+
readonly glyph: "xs";
|
|
190
|
+
readonly color: Readonly<{
|
|
191
|
+
source: "theme";
|
|
192
|
+
key: "textMuted";
|
|
193
|
+
alpha?: number;
|
|
194
|
+
}>;
|
|
195
|
+
};
|
|
196
|
+
readonly popover: {
|
|
197
|
+
readonly minWidth: 300;
|
|
198
|
+
readonly maxWidth: 360;
|
|
199
|
+
readonly sideOffset: 8;
|
|
200
|
+
readonly collisionPadding: 8;
|
|
201
|
+
readonly background: Readonly<{
|
|
202
|
+
source: "theme";
|
|
203
|
+
key: "bg";
|
|
204
|
+
alpha?: number;
|
|
205
|
+
}>;
|
|
206
|
+
readonly border: Readonly<{
|
|
207
|
+
source: "theme";
|
|
208
|
+
key: "border";
|
|
209
|
+
alpha?: number;
|
|
210
|
+
}>;
|
|
211
|
+
readonly borderWidth: 1;
|
|
212
|
+
readonly radius: "md";
|
|
213
|
+
readonly shadow: {
|
|
214
|
+
readonly color: "#000000";
|
|
215
|
+
readonly opacity: 0.12;
|
|
216
|
+
readonly radius: 12;
|
|
217
|
+
readonly offsetY: 4;
|
|
218
|
+
};
|
|
219
|
+
readonly padding: 8;
|
|
220
|
+
};
|
|
221
|
+
readonly states: {
|
|
222
|
+
readonly focus: {
|
|
223
|
+
readonly color: Readonly<{
|
|
224
|
+
source: "theme";
|
|
225
|
+
key: "contentBrand";
|
|
226
|
+
alpha?: number;
|
|
227
|
+
}>;
|
|
228
|
+
readonly width: 2;
|
|
229
|
+
readonly offset: 2;
|
|
230
|
+
};
|
|
231
|
+
readonly invalidBorder: Readonly<{
|
|
232
|
+
source: "theme";
|
|
233
|
+
key: "danger";
|
|
234
|
+
alpha?: number;
|
|
235
|
+
}>;
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Web mounts the grid as a real focus-bearing `dialog` (APG's Date Picker
|
|
240
|
+
* Dialog pattern moves actual DOM focus through gridcells via roving
|
|
241
|
+
* tabindex, unlike Select's `activeDescendant` listbox where focus stays on
|
|
242
|
+
* the trigger). Native mounts the same grid inside a Sheet. Both close on a
|
|
243
|
+
* committed selection or `clear` and restore trigger focus, matching
|
|
244
|
+
* Select's own dismiss-and-restore scenario.
|
|
245
|
+
*/
|
|
246
|
+
export declare const datePickerBehavior: {
|
|
247
|
+
readonly controlled: readonly ["selectedDate", "defaultSelectedDate", "onSelectionChange", "open", "defaultOpen", "onOpenChange"];
|
|
248
|
+
readonly inputs: readonly ["grid", "displayValue", "focusedMonth", "defaultFocusedMonth"];
|
|
249
|
+
readonly stateAxes: {
|
|
250
|
+
readonly availability: readonly ["enabled", "disabled", "readOnly", "busy"];
|
|
251
|
+
readonly value: readonly ["empty", "selected", "open"];
|
|
252
|
+
readonly validation: readonly ["valid", "invalid"];
|
|
253
|
+
};
|
|
254
|
+
readonly web: {
|
|
255
|
+
readonly roles: readonly ["button", "dialog", "grid", "row", "gridcell"];
|
|
256
|
+
readonly keyboard: readonly ["Tab", "Enter", "Space", "Escape", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"];
|
|
257
|
+
readonly focus: "roving";
|
|
258
|
+
readonly dismiss: readonly ["selection", "escape", "outside", "blur"];
|
|
259
|
+
};
|
|
260
|
+
readonly native: {
|
|
261
|
+
readonly roles: readonly ["button", "dialog"];
|
|
262
|
+
readonly states: readonly ["disabled", "expanded"];
|
|
263
|
+
readonly actions: readonly ["activate", "dismiss"];
|
|
264
|
+
readonly dismiss: readonly ["selection", "back", "outside"];
|
|
265
|
+
};
|
|
266
|
+
readonly scenarios: readonly ["visible-label-or-accessibility-label-names-trigger", "trigger-shows-the-product-formatted-display-value-or-placeholder", "popover-and-sheet-content-reuse-the-calendar-grid-contract-unchanged", "activating-an-enabled-date-commits-selection-and-closes", "disabled-dates-remain-focusable-but-never-commit-or-close", "clear-commits-null-and-closes-without-reopening", "escape-closes-without-commit-and-restores-trigger-focus", "outside-press-closes-without-commit", "controlled-owner-may-defer-a-selection-close-request", "changing-the-focused-month-never-changes-or-clears-the-selection", "range-selection-is-not-part-of-the-contract"];
|
|
267
|
+
};
|
|
268
|
+
//# sourceMappingURL=date-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../src/date-picker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { assertIsoCalendarDate, validateCalendarGridDescriptor, resolveCalendarGridDescriptor, validateCalendarMonthNavigation, } from "./calendar.js";
|
|
2
|
+
import { fieldFrameContract, floatingSurfaceContract, focusIndicatorContract, formSupportContract } from "./component-contracts.js";
|
|
3
|
+
import { control, spacing } from "./foundations.js";
|
|
4
|
+
import { semanticColors } from "./semantic-colors.js";
|
|
5
|
+
function assertNonEmpty(value, field) {
|
|
6
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
7
|
+
throw new TypeError(`DatePicker ${field} must not be empty`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function validateDatePickerSelection(selection) {
|
|
11
|
+
const selected = selection.selectedDate ?? selection.defaultSelectedDate;
|
|
12
|
+
if (selected != null)
|
|
13
|
+
assertIsoCalendarDate(selected, "selectedDate");
|
|
14
|
+
}
|
|
15
|
+
export function validateDatePickerDescriptor(descriptor) {
|
|
16
|
+
validateCalendarGridDescriptor(descriptor.grid);
|
|
17
|
+
assertNonEmpty(descriptor.placeholder, "placeholder");
|
|
18
|
+
if (descriptor.displayValue !== null) {
|
|
19
|
+
assertNonEmpty(descriptor.displayValue, "displayValue");
|
|
20
|
+
}
|
|
21
|
+
const label = descriptor.label ?? descriptor.accessibilityLabel;
|
|
22
|
+
assertNonEmpty(label, "label or accessibilityLabel");
|
|
23
|
+
validateDatePickerSelection(descriptor);
|
|
24
|
+
validateCalendarMonthNavigation(descriptor);
|
|
25
|
+
}
|
|
26
|
+
/** Trigger copy is the committed display value, or the placeholder when nothing is selected yet. */
|
|
27
|
+
export function resolveDatePickerTriggerText(descriptor) {
|
|
28
|
+
if (descriptor.displayValue !== null)
|
|
29
|
+
assertNonEmpty(descriptor.displayValue, "displayValue");
|
|
30
|
+
assertNonEmpty(descriptor.placeholder, "placeholder");
|
|
31
|
+
return descriptor.displayValue ?? descriptor.placeholder;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the popover/sheet content: the exact same cell shape, today/
|
|
35
|
+
* selected marks, and accessible names Calendar renders inline. DatePicker
|
|
36
|
+
* adds no per-cell semantics of its own — only the surrounding field/overlay
|
|
37
|
+
* lifecycle is new.
|
|
38
|
+
*/
|
|
39
|
+
export function resolveDatePickerGrid(descriptor, options) {
|
|
40
|
+
validateDatePickerDescriptor(descriptor);
|
|
41
|
+
const selectedDate = descriptor.selectedDate ?? descriptor.defaultSelectedDate ?? null;
|
|
42
|
+
return resolveCalendarGridDescriptor(descriptor.grid, selectedDate, options);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reuses `fieldFrameContract`/`formSupportContract` verbatim, exactly like
|
|
46
|
+
* NumberField and Select each do independently — a second field frame would
|
|
47
|
+
* drift the first time either one's border or height changes. `calendar`
|
|
48
|
+
* anatomy is intentionally not re-declared here: the popover/sheet mounts
|
|
49
|
+
* `calendarRecipe` unchanged, so this recipe only owns the field/trigger and
|
|
50
|
+
* the overlay frame around it.
|
|
51
|
+
*/
|
|
52
|
+
export const datePickerRecipe = {
|
|
53
|
+
slots: [
|
|
54
|
+
"root",
|
|
55
|
+
"label",
|
|
56
|
+
"trigger",
|
|
57
|
+
"leading",
|
|
58
|
+
"value",
|
|
59
|
+
"placeholder",
|
|
60
|
+
"clear",
|
|
61
|
+
"indicator",
|
|
62
|
+
"description",
|
|
63
|
+
"error",
|
|
64
|
+
"popover",
|
|
65
|
+
],
|
|
66
|
+
defaults: { size: "medium" },
|
|
67
|
+
adaptive: { web: "popover", native: "sheet" },
|
|
68
|
+
frame: fieldFrameContract,
|
|
69
|
+
sizes: {
|
|
70
|
+
medium: {
|
|
71
|
+
minHeight: fieldFrameContract.minHeight,
|
|
72
|
+
paddingHorizontal: fieldFrameContract.paddingHorizontal,
|
|
73
|
+
textVariant: "body",
|
|
74
|
+
glyph: "sm",
|
|
75
|
+
},
|
|
76
|
+
large: {
|
|
77
|
+
minHeight: control.buttonHeight.large,
|
|
78
|
+
paddingHorizontal: spacing.lg,
|
|
79
|
+
textVariant: "bodyLarge",
|
|
80
|
+
glyph: "md",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
support: formSupportContract,
|
|
84
|
+
value: {
|
|
85
|
+
color: semanticColors.content.body,
|
|
86
|
+
placeholderColor: semanticColors.content.secondary,
|
|
87
|
+
},
|
|
88
|
+
leading: { color: semanticColors.content.secondary, icon: "calendar" },
|
|
89
|
+
indicator: { color: semanticColors.content.secondary },
|
|
90
|
+
clear: {
|
|
91
|
+
diameter: control.buttonHeight.small,
|
|
92
|
+
hitSlop: control.buttonHitSlop.small,
|
|
93
|
+
glyph: "xs",
|
|
94
|
+
color: semanticColors.content.secondary,
|
|
95
|
+
},
|
|
96
|
+
popover: {
|
|
97
|
+
...floatingSurfaceContract,
|
|
98
|
+
minWidth: 300,
|
|
99
|
+
maxWidth: 360,
|
|
100
|
+
sideOffset: spacing.xs,
|
|
101
|
+
collisionPadding: spacing.xs,
|
|
102
|
+
},
|
|
103
|
+
states: {
|
|
104
|
+
focus: focusIndicatorContract,
|
|
105
|
+
invalidBorder: semanticColors.border.danger,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Web mounts the grid as a real focus-bearing `dialog` (APG's Date Picker
|
|
110
|
+
* Dialog pattern moves actual DOM focus through gridcells via roving
|
|
111
|
+
* tabindex, unlike Select's `activeDescendant` listbox where focus stays on
|
|
112
|
+
* the trigger). Native mounts the same grid inside a Sheet. Both close on a
|
|
113
|
+
* committed selection or `clear` and restore trigger focus, matching
|
|
114
|
+
* Select's own dismiss-and-restore scenario.
|
|
115
|
+
*/
|
|
116
|
+
export const datePickerBehavior = {
|
|
117
|
+
controlled: [
|
|
118
|
+
"selectedDate",
|
|
119
|
+
"defaultSelectedDate",
|
|
120
|
+
"onSelectionChange",
|
|
121
|
+
"open",
|
|
122
|
+
"defaultOpen",
|
|
123
|
+
"onOpenChange",
|
|
124
|
+
],
|
|
125
|
+
inputs: ["grid", "displayValue", "focusedMonth", "defaultFocusedMonth"],
|
|
126
|
+
stateAxes: {
|
|
127
|
+
availability: ["enabled", "disabled", "readOnly", "busy"],
|
|
128
|
+
value: ["empty", "selected", "open"],
|
|
129
|
+
validation: ["valid", "invalid"],
|
|
130
|
+
},
|
|
131
|
+
web: {
|
|
132
|
+
roles: ["button", "dialog", "grid", "row", "gridcell"],
|
|
133
|
+
keyboard: [
|
|
134
|
+
"Tab",
|
|
135
|
+
"Enter",
|
|
136
|
+
"Space",
|
|
137
|
+
"Escape",
|
|
138
|
+
"ArrowUp",
|
|
139
|
+
"ArrowDown",
|
|
140
|
+
"ArrowLeft",
|
|
141
|
+
"ArrowRight",
|
|
142
|
+
"Home",
|
|
143
|
+
"End",
|
|
144
|
+
],
|
|
145
|
+
focus: "roving",
|
|
146
|
+
dismiss: ["selection", "escape", "outside", "blur"],
|
|
147
|
+
},
|
|
148
|
+
native: {
|
|
149
|
+
roles: ["button", "dialog"],
|
|
150
|
+
states: ["disabled", "expanded"],
|
|
151
|
+
actions: ["activate", "dismiss"],
|
|
152
|
+
dismiss: ["selection", "back", "outside"],
|
|
153
|
+
},
|
|
154
|
+
scenarios: [
|
|
155
|
+
"visible-label-or-accessibility-label-names-trigger",
|
|
156
|
+
"trigger-shows-the-product-formatted-display-value-or-placeholder",
|
|
157
|
+
"popover-and-sheet-content-reuse-the-calendar-grid-contract-unchanged",
|
|
158
|
+
"activating-an-enabled-date-commits-selection-and-closes",
|
|
159
|
+
"disabled-dates-remain-focusable-but-never-commit-or-close",
|
|
160
|
+
"clear-commits-null-and-closes-without-reopening",
|
|
161
|
+
"escape-closes-without-commit-and-restores-trigger-focus",
|
|
162
|
+
"outside-press-closes-without-commit",
|
|
163
|
+
"controlled-owner-may-defer-a-selection-close-request",
|
|
164
|
+
"changing-the-focused-month-never-changes-or-clears-the-selection",
|
|
165
|
+
"range-selection-is-not-part-of-the-contract",
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=date-picker.js.map
|