@yarcl/react 0.1.0 → 0.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 +14 -2
- package/{src/apply.ts → dist/apply.d.ts} +4 -23
- package/dist/apply.js +37 -0
- package/dist/classes.d.ts +13 -0
- package/dist/classes.js +22 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +58 -0
- package/dist/color.js +49 -32
- package/dist/components/Accordion.d.ts +96 -0
- package/dist/components/Accordion.js +148 -0
- package/dist/components/Alert.d.ts +60 -0
- package/dist/components/Alert.js +61 -0
- package/dist/components/Avatar.d.ts +100 -0
- package/dist/components/Avatar.js +89 -0
- package/dist/components/Badge.d.ts +44 -0
- package/dist/components/Badge.js +43 -0
- package/dist/components/Breadcrumb.d.ts +77 -0
- package/dist/components/Breadcrumb.js +126 -0
- package/dist/components/Button.d.ts +18 -0
- package/dist/components/Button.js +33 -0
- package/dist/components/ButtonGroup.d.ts +29 -0
- package/dist/components/ButtonGroup.js +37 -0
- package/dist/components/Card.d.ts +36 -0
- package/dist/components/Card.js +26 -0
- package/dist/components/Checkbox.d.ts +28 -0
- package/dist/components/Checkbox.js +38 -0
- package/dist/components/Combobox.d.ts +75 -0
- package/dist/components/Combobox.js +362 -0
- package/dist/components/CommandPalette.d.ts +92 -0
- package/dist/components/CommandPalette.js +293 -0
- package/dist/components/DatePicker.d.ts +157 -0
- package/dist/components/DatePicker.js +230 -0
- package/dist/components/Dialog.d.ts +25 -0
- package/dist/components/Dialog.js +29 -0
- package/dist/components/Divider.d.ts +23 -0
- package/dist/components/Divider.js +25 -0
- package/dist/components/Drawer.d.ts +21 -0
- package/dist/components/Drawer.js +25 -0
- package/dist/components/EmptyState.d.ts +51 -0
- package/dist/components/EmptyState.js +47 -0
- package/dist/components/Field.d.ts +27 -0
- package/dist/components/Field.js +59 -0
- package/dist/components/Heading.d.ts +26 -0
- package/dist/components/Heading.js +25 -0
- package/dist/components/HoverCard.d.ts +47 -0
- package/dist/components/HoverCard.js +53 -0
- package/dist/components/IconButton.d.ts +19 -0
- package/dist/components/IconButton.js +32 -0
- package/dist/components/Inline.d.ts +21 -0
- package/dist/components/Inline.js +24 -0
- package/dist/components/Input.d.ts +19 -0
- package/dist/components/Input.js +25 -0
- package/dist/components/Label.d.ts +46 -0
- package/dist/components/Label.js +33 -0
- package/dist/components/Link.d.ts +27 -0
- package/dist/components/Link.js +26 -0
- package/dist/components/Menu.d.ts +73 -0
- package/dist/components/Menu.js +143 -0
- package/dist/components/Modal.d.ts +45 -0
- package/dist/components/Modal.js +110 -0
- package/dist/components/NumberInput.d.ts +49 -0
- package/dist/components/NumberInput.js +130 -0
- package/dist/components/Pagination.d.ts +86 -0
- package/dist/components/Pagination.js +119 -0
- package/dist/components/Popover.d.ts +72 -0
- package/dist/components/Popover.js +87 -0
- package/dist/components/Progress.d.ts +60 -0
- package/dist/components/Progress.js +59 -0
- package/dist/components/Radio.d.ts +32 -0
- package/dist/components/Radio.js +47 -0
- package/dist/components/RadioGroup.d.ts +57 -0
- package/dist/components/RadioGroup.js +68 -0
- package/dist/components/Select.d.ts +47 -0
- package/dist/components/Select.js +138 -0
- package/dist/components/Skeleton.d.ts +46 -0
- package/dist/components/Skeleton.js +40 -0
- package/dist/components/Slider.d.ts +126 -0
- package/dist/components/Slider.js +166 -0
- package/dist/components/Spinner.d.ts +23 -0
- package/dist/components/Spinner.js +23 -0
- package/dist/components/Stack.d.ts +35 -0
- package/dist/components/Stack.js +24 -0
- package/dist/components/Switch.d.ts +27 -0
- package/dist/components/Switch.js +33 -0
- package/dist/components/Table.d.ts +71 -0
- package/dist/components/Table.js +61 -0
- package/dist/components/Tabs.d.ts +66 -0
- package/dist/components/Tabs.js +109 -0
- package/dist/components/Text.d.ts +32 -0
- package/dist/components/Text.js +28 -0
- package/dist/components/Textarea.d.ts +15 -0
- package/dist/components/Textarea.js +24 -0
- package/dist/components/Toast.d.ts +90 -0
- package/dist/components/Toast.js +156 -0
- package/dist/components/ToggleGroup.d.ts +79 -0
- package/dist/components/ToggleGroup.js +101 -0
- package/dist/components/Tooltip.d.ts +46 -0
- package/dist/components/Tooltip.js +62 -0
- package/dist/components/VisuallyHidden.d.ts +18 -0
- package/dist/components/VisuallyHidden.js +22 -0
- package/dist/contrast.d.ts +32 -0
- package/dist/css.d.ts +2 -2
- package/dist/css.js +193 -157
- package/dist/define.d.ts +40 -15
- package/dist/define.js +30 -27
- package/dist/demo.d.ts +2 -0
- package/dist/demo.js +2 -0
- package/dist/esbuild.cjs +5 -0
- package/dist/esbuild.d.ts +4 -0
- package/dist/esbuild.js +6 -0
- package/dist/field-context.d.ts +16 -0
- package/dist/field-context.js +17 -0
- package/dist/floating.d.ts +12 -0
- package/dist/floating.js +52 -0
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.js +12 -0
- package/{src/index.ts → dist/index.d.ts} +11 -33
- package/dist/index.js +50 -0
- package/dist/init.d.ts +18 -0
- package/dist/init.js +224 -0
- package/dist/plugin.d.ts +10 -26
- package/dist/plugin.js +152 -78
- package/dist/reference/DemoYarcl.d.ts +12 -0
- package/dist/reference/DemoYarcl.js +312 -0
- package/dist/reference/DesignReference.d.ts +24 -0
- package/dist/reference/DesignReference.js +403 -0
- package/dist/reference/index.js +2 -0
- package/dist/rollup.cjs +5 -0
- package/dist/rollup.d.ts +4 -0
- package/dist/rollup.js +6 -0
- package/dist/rspack.cjs +5 -0
- package/dist/rspack.d.ts +4 -0
- package/dist/rspack.js +6 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.js +30 -0
- package/dist/setup.d.ts +6 -0
- package/dist/setup.js +33 -0
- package/{src → dist}/styles.css +224 -106
- package/dist/themes/bloom.d.ts +295 -0
- package/dist/themes/bloom.js +202 -0
- package/dist/themes/brutalist.d.ts +277 -0
- package/dist/themes/brutalist.js +199 -0
- package/dist/themes/compact.d.ts +274 -0
- package/dist/themes/compact.js +200 -0
- package/dist/themes/contract.d.ts +23 -0
- package/dist/themes/editorial.d.ts +277 -0
- package/dist/themes/editorial.js +183 -0
- package/dist/themes/index.d.ts +1425 -0
- package/dist/themes/index.js +45 -0
- package/{src/types.ts → dist/types.d.ts} +40 -37
- package/dist/vite.cjs +5 -0
- package/dist/vite.d.ts +4 -0
- package/dist/vite.js +6 -0
- package/dist/webpack.cjs +5 -0
- package/dist/webpack.d.ts +4 -0
- package/dist/webpack.js +6 -0
- package/dist/yarcl.config.d.ts +276 -0
- package/dist/yarcl.config.js +273 -0
- package/package.json +69 -17
- package/src/classes.ts +0 -23
- package/src/color.ts +0 -48
- package/src/components/Accordion.tsx +0 -274
- package/src/components/Alert.tsx +0 -85
- package/src/components/Badge.tsx +0 -79
- package/src/components/Breadcrumb.tsx +0 -239
- package/src/components/Button.tsx +0 -58
- package/src/components/ButtonGroup.tsx +0 -61
- package/src/components/Card.tsx +0 -50
- package/src/components/Checkbox.tsx +0 -49
- package/src/components/Combobox.tsx +0 -522
- package/src/components/CommandPalette.tsx +0 -452
- package/src/components/DatePicker.tsx +0 -432
- package/src/components/Dialog.tsx +0 -39
- package/src/components/Divider.tsx +0 -34
- package/src/components/Drawer.tsx +0 -31
- package/src/components/Field.tsx +0 -61
- package/src/components/Heading.tsx +0 -40
- package/src/components/HoverCard.tsx +0 -108
- package/src/components/IconButton.tsx +0 -59
- package/src/components/Inline.tsx +0 -42
- package/src/components/Input.tsx +0 -28
- package/src/components/Label.tsx +0 -58
- package/src/components/Link.tsx +0 -41
- package/src/components/Menu.tsx +0 -207
- package/src/components/Modal.tsx +0 -164
- package/src/components/NumberInput.tsx +0 -208
- package/src/components/Pagination.tsx +0 -227
- package/src/components/Popover.tsx +0 -140
- package/src/components/Progress.tsx +0 -122
- package/src/components/Radio.tsx +0 -59
- package/src/components/RadioGroup.tsx +0 -114
- package/src/components/Select.tsx +0 -197
- package/src/components/Skeleton.tsx +0 -95
- package/src/components/Slider.tsx +0 -315
- package/src/components/Spinner.tsx +0 -38
- package/src/components/Stack.tsx +0 -57
- package/src/components/Switch.tsx +0 -42
- package/src/components/Table.tsx +0 -99
- package/src/components/Tabs.tsx +0 -159
- package/src/components/Text.tsx +0 -67
- package/src/components/Textarea.tsx +0 -26
- package/src/components/Toast.tsx +0 -190
- package/src/components/ToggleGroup.tsx +0 -227
- package/src/components/Tooltip.tsx +0 -78
- package/src/contrast.ts +0 -82
- package/src/css.ts +0 -228
- package/src/define.check.ts +0 -259
- package/src/define.ts +0 -379
- package/src/field-context.ts +0 -31
- package/src/floating.tsx +0 -61
- package/src/hooks.ts +0 -19
- package/src/plugin.ts +0 -94
- package/src/reference/DesignReference.tsx +0 -374
- package/src/runtime.ts +0 -38
- package/src/themes/bloom.ts +0 -72
- package/src/themes/brutalist.ts +0 -63
- package/src/themes/compact.ts +0 -63
- package/src/themes/contract.ts +0 -24
- package/src/themes/editorial.ts +0 -55
- package/src/themes/index.ts +0 -47
- package/src/yarcl.config.ts +0 -104
- /package/{src/reference/index.ts → dist/reference/index.d.ts} +0 -0
- /package/{src → dist}/reference/reference.css +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { useConfig, useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx, radiusClass, sizeClass } from "../classes.js";
|
|
3
|
+
import { useControllable } from "../hooks.js";
|
|
4
|
+
import { useModalDialog } from "./Modal.js";
|
|
5
|
+
import { cloneElement, useEffect, useId, useState, useSyncExternalStore } from "react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/CommandPalette.tsx
|
|
8
|
+
function detectApple() {
|
|
9
|
+
return typeof navigator !== "undefined" && /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent);
|
|
10
|
+
}
|
|
11
|
+
var noop = () => () => {};
|
|
12
|
+
function useIsApple() {
|
|
13
|
+
return useSyncExternalStore(noop, detectApple, () => false);
|
|
14
|
+
}
|
|
15
|
+
function parseShortcut(shortcut, apple) {
|
|
16
|
+
const parts = shortcut.split("+").map((part) => part.trim().toLowerCase());
|
|
17
|
+
const key = parts[parts.length - 1] ?? "";
|
|
18
|
+
const mods = new Set(parts.slice(0, -1));
|
|
19
|
+
const mod = mods.has("mod");
|
|
20
|
+
return {
|
|
21
|
+
key,
|
|
22
|
+
ctrl: mods.has("ctrl") || mods.has("control") || mod && !apple,
|
|
23
|
+
meta: mods.has("meta") || mods.has("cmd") || mod && apple,
|
|
24
|
+
alt: mods.has("alt") || mods.has("option"),
|
|
25
|
+
shift: mods.has("shift")
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function matchesShortcut(event, shortcut, apple) {
|
|
29
|
+
const s = parseShortcut(shortcut, apple);
|
|
30
|
+
if (event.ctrlKey !== s.ctrl || event.metaKey !== s.meta || event.altKey !== s.alt || event.shiftKey !== s.shift) return false;
|
|
31
|
+
const code = s.key.length === 1 ? /[a-z]/.test(s.key) ? `Key${s.key.toUpperCase()}` : /\d/.test(s.key) ? `Digit${s.key}` : "" : "";
|
|
32
|
+
return event.key?.toLowerCase() === s.key || code !== "" && event.code === code;
|
|
33
|
+
}
|
|
34
|
+
function ariaChord(chord, apple) {
|
|
35
|
+
const s = parseShortcut(chord, apple);
|
|
36
|
+
const key = s.key.length === 1 ? s.key.toUpperCase() : s.key.charAt(0).toUpperCase() + s.key.slice(1);
|
|
37
|
+
return [
|
|
38
|
+
s.ctrl && "Control",
|
|
39
|
+
s.alt && "Alt",
|
|
40
|
+
s.shift && "Shift",
|
|
41
|
+
s.meta && "Meta",
|
|
42
|
+
key
|
|
43
|
+
].filter(Boolean).join("+");
|
|
44
|
+
}
|
|
45
|
+
function ariaShortcut(shortcut, apple) {
|
|
46
|
+
return shortcut.trim().split(/\s+/).map((chord) => ariaChord(chord, apple)).join(" ");
|
|
47
|
+
}
|
|
48
|
+
function shortcutKeys(shortcut, apple) {
|
|
49
|
+
return shortcut.trim().split(/[\s+]+/).filter(Boolean).map((raw) => {
|
|
50
|
+
const part = raw.trim();
|
|
51
|
+
const lower = part.toLowerCase();
|
|
52
|
+
if (lower === "mod") return apple ? "⌘" : "Ctrl";
|
|
53
|
+
if (lower === "ctrl" || lower === "control") return apple ? "⌃" : "Ctrl";
|
|
54
|
+
if (lower === "alt" || lower === "option") return apple ? "⌥" : "Alt";
|
|
55
|
+
if (lower === "shift") return apple ? "⇧" : "Shift";
|
|
56
|
+
if (lower === "meta" || lower === "cmd") return apple ? "⌘" : "Meta";
|
|
57
|
+
return part.length === 1 ? part.toUpperCase() : part;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function ShortcutHint({ shortcut, apple }) {
|
|
61
|
+
return /* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: "yarcl-command-palette-shortcut",
|
|
63
|
+
"aria-hidden": "true",
|
|
64
|
+
children: shortcutKeys(shortcut, apple).map((key, i) => /* @__PURE__ */ jsx("kbd", { children: key }, i))
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function SearchIcon() {
|
|
68
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
69
|
+
viewBox: "0 0 24 24",
|
|
70
|
+
fill: "none",
|
|
71
|
+
stroke: "currentColor",
|
|
72
|
+
strokeWidth: "2",
|
|
73
|
+
strokeLinecap: "round",
|
|
74
|
+
"aria-hidden": "true",
|
|
75
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
76
|
+
cx: "11",
|
|
77
|
+
cy: "11",
|
|
78
|
+
r: "7"
|
|
79
|
+
}), /* @__PURE__ */ jsx("path", { d: "m20 20-3.5-3.5" })]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function contains(command, text) {
|
|
83
|
+
const haystack = [
|
|
84
|
+
command.label,
|
|
85
|
+
command.group ?? "",
|
|
86
|
+
...command.keywords ?? []
|
|
87
|
+
].join(" ").toLowerCase();
|
|
88
|
+
return text.toLowerCase().split(/\s+/).filter(Boolean).every((word) => haystack.includes(word));
|
|
89
|
+
}
|
|
90
|
+
function firstEnabled(list, from = 0, step = 1) {
|
|
91
|
+
for (let i = 0; i < list.length; i++) {
|
|
92
|
+
const index = ((from + i * step) % list.length + list.length) % list.length;
|
|
93
|
+
if (!list[index]?.disabled) return index;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function CommandPaletteBody({ commands, inputValue, onInputValueChange, filter = true, placeholder = "Search commands…", label = "Command palette", emptyMessage = "No results", apple, choose }) {
|
|
98
|
+
const [text, setText] = useControllable(inputValue, "", onInputValueChange);
|
|
99
|
+
const [activeIndex, setActiveIndex] = useState(null);
|
|
100
|
+
const idPrefix = useId();
|
|
101
|
+
const listId = `${idPrefix}-list`;
|
|
102
|
+
const optionId = (i) => `${idPrefix}-option-${i}`;
|
|
103
|
+
const match = filter === true ? contains : filter === false ? () => true : filter;
|
|
104
|
+
const sections = [];
|
|
105
|
+
for (const command of commands) {
|
|
106
|
+
if (!match(command, text)) continue;
|
|
107
|
+
let section = sections.find((s) => s.heading === command.group);
|
|
108
|
+
if (!section) sections.push(section = {
|
|
109
|
+
heading: command.group,
|
|
110
|
+
commands: []
|
|
111
|
+
});
|
|
112
|
+
section.commands.push(command);
|
|
113
|
+
}
|
|
114
|
+
const visible = sections.flatMap((s) => s.commands);
|
|
115
|
+
const shown = activeIndex === null || activeIndex >= visible.length ? firstEnabled(visible) : activeIndex;
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (shown != null) document.getElementById(`${idPrefix}-option-${shown}`)?.scrollIntoView({ block: "nearest" });
|
|
118
|
+
}, [shown, idPrefix]);
|
|
119
|
+
function onKeyDown(event) {
|
|
120
|
+
if (visible.length === 0) return;
|
|
121
|
+
let next;
|
|
122
|
+
if (event.key === "ArrowDown") next = firstEnabled(visible, shown == null ? 0 : shown + 1);
|
|
123
|
+
else if (event.key === "ArrowUp") next = firstEnabled(visible, shown == null ? visible.length - 1 : shown - 1, -1);
|
|
124
|
+
else if (event.key === "Home") next = firstEnabled(visible);
|
|
125
|
+
else if (event.key === "End") next = firstEnabled(visible, visible.length - 1, -1);
|
|
126
|
+
else if (event.key === "Enter") {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
const command = shown != null ? visible[shown] : void 0;
|
|
129
|
+
if (command) choose(command);
|
|
130
|
+
return;
|
|
131
|
+
} else return;
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
setActiveIndex(next);
|
|
134
|
+
}
|
|
135
|
+
let index = 0;
|
|
136
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
137
|
+
className: "yarcl-command-palette-content",
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ jsxs("div", {
|
|
140
|
+
className: "yarcl-command-palette-search",
|
|
141
|
+
children: [/* @__PURE__ */ jsx(SearchIcon, {}), /* @__PURE__ */ jsx("input", {
|
|
142
|
+
className: "yarcl-command-palette-input",
|
|
143
|
+
type: "text",
|
|
144
|
+
role: "combobox",
|
|
145
|
+
"aria-label": label,
|
|
146
|
+
"aria-autocomplete": "list",
|
|
147
|
+
"aria-expanded": visible.length > 0,
|
|
148
|
+
"aria-controls": visible.length > 0 ? listId : void 0,
|
|
149
|
+
"aria-activedescendant": shown != null ? optionId(shown) : void 0,
|
|
150
|
+
autoComplete: "off",
|
|
151
|
+
autoCorrect: "off",
|
|
152
|
+
spellCheck: false,
|
|
153
|
+
placeholder,
|
|
154
|
+
value: text,
|
|
155
|
+
onChange: (event) => {
|
|
156
|
+
setText(event.target.value);
|
|
157
|
+
setActiveIndex(null);
|
|
158
|
+
},
|
|
159
|
+
onKeyDown
|
|
160
|
+
})]
|
|
161
|
+
}),
|
|
162
|
+
visible.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
163
|
+
id: listId,
|
|
164
|
+
role: "listbox",
|
|
165
|
+
"aria-label": label,
|
|
166
|
+
className: "yarcl-listbox yarcl-command-palette-list",
|
|
167
|
+
children: sections.map((section, s) => {
|
|
168
|
+
const headingId = `${idPrefix}-group-${s}`;
|
|
169
|
+
const options = section.commands.map((command) => {
|
|
170
|
+
const i = index++;
|
|
171
|
+
const isActive = i === shown;
|
|
172
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
173
|
+
id: optionId(i),
|
|
174
|
+
role: "option",
|
|
175
|
+
"aria-selected": isActive,
|
|
176
|
+
"aria-disabled": command.disabled || void 0,
|
|
177
|
+
"aria-keyshortcuts": command.shortcut ? ariaShortcut(command.shortcut, apple) : void 0,
|
|
178
|
+
"data-active": isActive || void 0,
|
|
179
|
+
className: "yarcl-option",
|
|
180
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
181
|
+
onMouseMove: () => {
|
|
182
|
+
if (!command.disabled && i !== shown) setActiveIndex(i);
|
|
183
|
+
},
|
|
184
|
+
onClick: () => choose(command),
|
|
185
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
186
|
+
className: "yarcl-command-palette-item",
|
|
187
|
+
children: [command.icon, /* @__PURE__ */ jsx("span", {
|
|
188
|
+
className: "yarcl-option-label",
|
|
189
|
+
children: command.label
|
|
190
|
+
})]
|
|
191
|
+
}), command.shortcut && /* @__PURE__ */ jsx(ShortcutHint, {
|
|
192
|
+
shortcut: command.shortcut,
|
|
193
|
+
apple
|
|
194
|
+
})]
|
|
195
|
+
}, command.id);
|
|
196
|
+
});
|
|
197
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
198
|
+
role: "group",
|
|
199
|
+
"aria-labelledby": section.heading ? headingId : void 0,
|
|
200
|
+
className: "yarcl-command-palette-group",
|
|
201
|
+
children: [section.heading && /* @__PURE__ */ jsx("div", {
|
|
202
|
+
id: headingId,
|
|
203
|
+
className: "yarcl-command-palette-heading",
|
|
204
|
+
"aria-hidden": "true",
|
|
205
|
+
children: section.heading
|
|
206
|
+
}), options]
|
|
207
|
+
}, section.heading ?? "");
|
|
208
|
+
})
|
|
209
|
+
}),
|
|
210
|
+
/* @__PURE__ */ jsx("div", {
|
|
211
|
+
role: "status",
|
|
212
|
+
className: "yarcl-listbox-message yarcl-command-palette-empty",
|
|
213
|
+
children: visible.length === 0 ? emptyMessage : null
|
|
214
|
+
})
|
|
215
|
+
]
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* A searchable command menu in a modal dialog, opened with a keyboard shortcut (⌘K or Ctrl+K by default).
|
|
220
|
+
* The search input is a combobox: arrow keys, Home and End move through the matching commands,
|
|
221
|
+
* Enter runs the highlighted one, Esc closes the palette and returns focus.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```tsx
|
|
225
|
+
* <CommandPalette
|
|
226
|
+
* commands={[
|
|
227
|
+
* { id: 'new', label: 'New file', group: 'File', shortcut: 'Mod+N', onSelect: createFile },
|
|
228
|
+
* { id: 'theme', label: 'Toggle dark mode', group: 'View', keywords: ['theme'], onSelect: toggleTheme },
|
|
229
|
+
* ]}
|
|
230
|
+
* />
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
function CommandPalette({ commands, open: openProp, defaultOpen = false, onOpenChange, shortcut = "Mod+K", trigger, onSelect, inputValue, onInputValueChange, filter, placeholder, label = "Command palette", emptyMessage, size, radius, color, className, onClick, onClose, ...props }) {
|
|
234
|
+
const own = useDefaults("CommandPalette");
|
|
235
|
+
const config = useConfig();
|
|
236
|
+
const apple = useIsApple();
|
|
237
|
+
const [open, setOpen] = useControllable(openProp, defaultOpen, onOpenChange);
|
|
238
|
+
const dialog = useModalDialog(open, setOpen);
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
if (!shortcut) return;
|
|
241
|
+
function onKeyDown(event) {
|
|
242
|
+
if (event.defaultPrevented || !shortcut || !matchesShortcut(event, shortcut, detectApple())) return;
|
|
243
|
+
event.preventDefault();
|
|
244
|
+
setOpen(!open);
|
|
245
|
+
}
|
|
246
|
+
document.addEventListener("keydown", onKeyDown);
|
|
247
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
248
|
+
}, [
|
|
249
|
+
shortcut,
|
|
250
|
+
open,
|
|
251
|
+
setOpen
|
|
252
|
+
]);
|
|
253
|
+
function choose(command) {
|
|
254
|
+
if (command.disabled) return;
|
|
255
|
+
setOpen(false);
|
|
256
|
+
command.onSelect?.();
|
|
257
|
+
onSelect?.(command);
|
|
258
|
+
}
|
|
259
|
+
const resolvedSize = size ?? own.size;
|
|
260
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [trigger && cloneElement(trigger, {
|
|
261
|
+
onClick(event) {
|
|
262
|
+
trigger.props.onClick?.(event);
|
|
263
|
+
setOpen(true);
|
|
264
|
+
},
|
|
265
|
+
...shortcut ? { "aria-keyshortcuts": ariaShortcut(shortcut, apple) } : {}
|
|
266
|
+
}), /* @__PURE__ */ jsx("dialog", {
|
|
267
|
+
"aria-label": label,
|
|
268
|
+
...props,
|
|
269
|
+
ref: dialog.ref,
|
|
270
|
+
className: cx("yarcl-modal yarcl-dialog yarcl-command-palette", `yarcl-modal-size-${config.defaults.modalSize}`, sizeClass(resolvedSize, "CommandPalette"), radiusClass(radius ?? own.radius, resolvedSize), colorClass(color ?? own.color), className),
|
|
271
|
+
onClose: (event) => {
|
|
272
|
+
dialog.onClose(event);
|
|
273
|
+
onClose?.(event);
|
|
274
|
+
},
|
|
275
|
+
onClick: (event) => {
|
|
276
|
+
dialog.onClick(event);
|
|
277
|
+
onClick?.(event);
|
|
278
|
+
},
|
|
279
|
+
children: open && /* @__PURE__ */ jsx(CommandPaletteBody, {
|
|
280
|
+
commands,
|
|
281
|
+
inputValue,
|
|
282
|
+
onInputValueChange,
|
|
283
|
+
filter,
|
|
284
|
+
placeholder,
|
|
285
|
+
label,
|
|
286
|
+
emptyMessage,
|
|
287
|
+
apple,
|
|
288
|
+
choose
|
|
289
|
+
})
|
|
290
|
+
})] });
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
export { CommandPalette };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { type Day, type Locale } from 'date-fns';
|
|
2
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
3
|
+
import type { TokenProps, VariantProps } from '../types';
|
|
4
|
+
/** A date range picked with a {@link DatePicker} in `mode="range"`. `to` is `null` while only the start is picked. */
|
|
5
|
+
export interface DateRange {
|
|
6
|
+
/** First day of the range. */
|
|
7
|
+
from: Date;
|
|
8
|
+
/** Last day of the range, or `null` until the second day is picked. */
|
|
9
|
+
to: Date | null;
|
|
10
|
+
}
|
|
11
|
+
/** Text for the parts of a {@link DatePicker} that have no visible label. */
|
|
12
|
+
export interface DatePickerLabels {
|
|
13
|
+
/** Accessible name of the calendar popover. */
|
|
14
|
+
dialog: string;
|
|
15
|
+
/** Accessible name of the previous month button. */
|
|
16
|
+
previousMonth: string;
|
|
17
|
+
/** Accessible name of the next month button. */
|
|
18
|
+
nextMonth: string;
|
|
19
|
+
}
|
|
20
|
+
/** Props shared by both modes of {@link DatePicker}. */
|
|
21
|
+
export interface DatePickerBaseProps extends TokenProps<'DatePicker'>, VariantProps, Omit<ComponentProps<'button'>, 'color' | 'value' | 'defaultValue' | 'onChange' | 'children' | 'type' | 'name'> {
|
|
22
|
+
/**
|
|
23
|
+
* Earliest day that can be picked. Earlier days are shown disabled and the calendar can't move before its month.
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <DatePicker min={new Date()} />
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
min?: Date;
|
|
30
|
+
/**
|
|
31
|
+
* Latest day that can be picked.
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <DatePicker max={addYears(new Date(), 1)} />
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
max?: Date;
|
|
38
|
+
/**
|
|
39
|
+
* Returns `true` for days that can't be picked, e.g. weekends or booked dates. They stay focusable but can't be chosen.
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <DatePicker isDateDisabled={isWeekend} />
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
isDateDisabled?: (date: Date) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* A date-fns locale. Sets month and weekday names, the display format and the first day of the week.
|
|
48
|
+
* @default enUS
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* import { de } from 'date-fns/locale';
|
|
52
|
+
* <DatePicker locale={de} />
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
locale?: Locale;
|
|
56
|
+
/**
|
|
57
|
+
* First day of the week, from 0 (Sunday) to 6 (Saturday).
|
|
58
|
+
* @default the locale's first day of the week
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* <DatePicker weekStartsOn={1} />
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
weekStartsOn?: Day;
|
|
65
|
+
/**
|
|
66
|
+
* A date-fns `format` pattern for the value shown in the trigger.
|
|
67
|
+
* @default 'PP'
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <DatePicker displayFormat="yyyy-MM-dd" />
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
displayFormat?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Text shown when nothing is picked.
|
|
76
|
+
* @example
|
|
77
|
+
* ```tsx
|
|
78
|
+
* <DatePicker placeholder="Pick a date" />
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
placeholder?: ReactNode;
|
|
82
|
+
/**
|
|
83
|
+
* Form field name. Renders a hidden input with the value as `yyyy-MM-dd`, or `yyyy-MM-dd/yyyy-MM-dd` for a range.
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* <DatePicker name="birthday" />
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
name?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Accessible names for the popover and the month buttons, for translation.
|
|
92
|
+
* @default { dialog: 'Choose date', previousMonth: 'Previous month', nextMonth: 'Next month' }
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* <DatePicker labels={{ dialog: 'Datum wählen', previousMonth: 'Vorheriger Monat', nextMonth: 'Nächster Monat' }} />
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
labels?: Partial<DatePickerLabels>;
|
|
99
|
+
}
|
|
100
|
+
/** Props for {@link DatePicker} picking one day. */
|
|
101
|
+
export interface DatePickerSingleProps extends DatePickerBaseProps {
|
|
102
|
+
/**
|
|
103
|
+
* Picks one day.
|
|
104
|
+
* @default 'single'
|
|
105
|
+
*/
|
|
106
|
+
mode?: 'single';
|
|
107
|
+
/** Controlled value. `null` means nothing is picked. */
|
|
108
|
+
value?: Date | null;
|
|
109
|
+
/**
|
|
110
|
+
* Initial value when uncontrolled.
|
|
111
|
+
* @default null
|
|
112
|
+
*/
|
|
113
|
+
defaultValue?: Date | null;
|
|
114
|
+
/**
|
|
115
|
+
* Called with the picked day.
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* <DatePicker value={date} onValueChange={setDate} />
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
onValueChange?: (value: Date | null) => void;
|
|
122
|
+
}
|
|
123
|
+
/** Props for {@link DatePicker} picking a range of days. */
|
|
124
|
+
export interface DatePickerRangeProps extends DatePickerBaseProps {
|
|
125
|
+
/** Picks a start and an end day. */
|
|
126
|
+
mode: 'range';
|
|
127
|
+
/** Controlled value. `null` means nothing is picked. */
|
|
128
|
+
value?: DateRange | null;
|
|
129
|
+
/**
|
|
130
|
+
* Initial value when uncontrolled.
|
|
131
|
+
* @default null
|
|
132
|
+
*/
|
|
133
|
+
defaultValue?: DateRange | null;
|
|
134
|
+
/**
|
|
135
|
+
* Called when the start is picked (with `to: null`) and again when the range is complete.
|
|
136
|
+
* @example
|
|
137
|
+
* ```tsx
|
|
138
|
+
* <DatePicker mode="range" value={range} onValueChange={setRange} />
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
onValueChange?: (value: DateRange | null) => void;
|
|
142
|
+
}
|
|
143
|
+
/** Props for {@link DatePicker}. `mode` decides whether one day or a range is picked. */
|
|
144
|
+
export type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
|
|
145
|
+
/**
|
|
146
|
+
* Picks a day or a range of days from a calendar popover. Built on {@link Popover}, with dates handled by date-fns.
|
|
147
|
+
* The trigger shares the size scale with {@link Input}. Picked days use `variant`, days inside a range use
|
|
148
|
+
* `defaults.softVariant`. The calendar follows the WAI-ARIA date picker dialog pattern. Works inside a {@link Field}.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```tsx
|
|
152
|
+
* <Field label="Check-in">
|
|
153
|
+
* <DatePicker value={date} onValueChange={setDate} min={new Date()} placeholder="Pick a date" />
|
|
154
|
+
* </Field>
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export declare function DatePicker(props: DatePickerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx, radiusClass, sizeClass, softVariantClass, variantClass } from "../classes.js";
|
|
3
|
+
import { useControllable } from "../hooks.js";
|
|
4
|
+
import { FieldContext, useFieldProps } from "../field-context.js";
|
|
5
|
+
import { Popover } from "./Popover.js";
|
|
6
|
+
import { useContext, useEffect, useId, useRef, useState } from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { addDays, addMonths, addYears, endOfMonth, endOfWeek, format, isAfter, isBefore, isSameDay, isSameMonth, isToday, startOfDay, startOfMonth, startOfWeek } from "date-fns";
|
|
9
|
+
import { enUS } from "date-fns/locale/en-US";
|
|
10
|
+
//#region src/components/DatePicker.tsx
|
|
11
|
+
var defaultLabels = {
|
|
12
|
+
dialog: "Choose date",
|
|
13
|
+
previousMonth: "Previous month",
|
|
14
|
+
nextMonth: "Next month"
|
|
15
|
+
};
|
|
16
|
+
function Icon({ d }) {
|
|
17
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
18
|
+
viewBox: "0 0 24 24",
|
|
19
|
+
fill: "none",
|
|
20
|
+
stroke: "currentColor",
|
|
21
|
+
strokeWidth: "2",
|
|
22
|
+
strokeLinecap: "round",
|
|
23
|
+
strokeLinejoin: "round",
|
|
24
|
+
"aria-hidden": "true",
|
|
25
|
+
children: /* @__PURE__ */ jsx("path", { d })
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
var calendarPath = "M8 3v4M16 3v4M4 10h16M5 5h14a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z";
|
|
29
|
+
var iso = (date) => format(date, "yyyy-MM-dd");
|
|
30
|
+
/**
|
|
31
|
+
* Picks a day or a range of days from a calendar popover. Built on {@link Popover}, with dates handled by date-fns.
|
|
32
|
+
* The trigger shares the size scale with {@link Input}. Picked days use `variant`, days inside a range use
|
|
33
|
+
* `defaults.softVariant`. The calendar follows the WAI-ARIA date picker dialog pattern. Works inside a {@link Field}.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <Field label="Check-in">
|
|
38
|
+
* <DatePicker value={date} onValueChange={setDate} min={new Date()} placeholder="Pick a date" />
|
|
39
|
+
* </Field>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
function DatePicker(props) {
|
|
43
|
+
const own = useDefaults("DatePicker");
|
|
44
|
+
const field = useContext(FieldContext);
|
|
45
|
+
const { mode = "single", value: valueProp, defaultValue = null, onValueChange, min, max, isDateDisabled, locale = enUS, weekStartsOn, displayFormat = "PP", placeholder, name, labels: labelsProp, size, radius, color, variant, className, required: _required, ...rest } = useFieldProps(props);
|
|
46
|
+
const range = mode === "range";
|
|
47
|
+
const labels = {
|
|
48
|
+
...defaultLabels,
|
|
49
|
+
...labelsProp
|
|
50
|
+
};
|
|
51
|
+
const [value, setValue] = useControllable(valueProp, defaultValue, onValueChange);
|
|
52
|
+
const from = value == null ? null : value instanceof Date ? value : value.from;
|
|
53
|
+
const to = value == null ? null : value instanceof Date ? value : value.to;
|
|
54
|
+
const [open, setOpen] = useState(false);
|
|
55
|
+
const [focused, setFocused] = useState(() => startOfDay(/* @__PURE__ */ new Date()));
|
|
56
|
+
const [hovered, setHovered] = useState(null);
|
|
57
|
+
const cellRef = useRef(null);
|
|
58
|
+
const moveFocus = useRef(false);
|
|
59
|
+
const id = useId();
|
|
60
|
+
const titleId = `${id}-title`;
|
|
61
|
+
const valueId = `${id}-value`;
|
|
62
|
+
const minDay = min && startOfDay(min);
|
|
63
|
+
const maxDay = max && startOfDay(max);
|
|
64
|
+
const outOfRange = (day) => minDay != null && isBefore(day, minDay) || maxDay != null && isAfter(day, maxDay);
|
|
65
|
+
const unavailable = (day) => outOfRange(day) || !!isDateDisabled?.(day);
|
|
66
|
+
const clamp = (day) => minDay && isBefore(day, minDay) ? minDay : maxDay && isAfter(day, maxDay) ? maxDay : day;
|
|
67
|
+
const week = {
|
|
68
|
+
locale,
|
|
69
|
+
weekStartsOn
|
|
70
|
+
};
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!open || !moveFocus.current) return;
|
|
73
|
+
moveFocus.current = false;
|
|
74
|
+
cellRef.current?.focus();
|
|
75
|
+
}, [open, focused]);
|
|
76
|
+
function onOpenChange(next) {
|
|
77
|
+
if (next) {
|
|
78
|
+
setFocused(clamp(startOfDay(from ?? /* @__PURE__ */ new Date())));
|
|
79
|
+
setHovered(null);
|
|
80
|
+
}
|
|
81
|
+
setOpen(next);
|
|
82
|
+
}
|
|
83
|
+
function pick(day) {
|
|
84
|
+
if (unavailable(day)) return;
|
|
85
|
+
if (!range) {
|
|
86
|
+
setValue(day);
|
|
87
|
+
setOpen(false);
|
|
88
|
+
} else if (!from || to) setValue({
|
|
89
|
+
from: day,
|
|
90
|
+
to: null
|
|
91
|
+
});
|
|
92
|
+
else {
|
|
93
|
+
setValue(isBefore(day, from) ? {
|
|
94
|
+
from: day,
|
|
95
|
+
to: from
|
|
96
|
+
} : {
|
|
97
|
+
from,
|
|
98
|
+
to: day
|
|
99
|
+
});
|
|
100
|
+
setOpen(false);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function navigate(day) {
|
|
104
|
+
moveFocus.current = true;
|
|
105
|
+
setFocused(clamp(day));
|
|
106
|
+
}
|
|
107
|
+
function onGridKeyDown(event) {
|
|
108
|
+
const move = {
|
|
109
|
+
ArrowLeft: () => addDays(focused, -1),
|
|
110
|
+
ArrowRight: () => addDays(focused, 1),
|
|
111
|
+
ArrowUp: () => addDays(focused, -7),
|
|
112
|
+
ArrowDown: () => addDays(focused, 7),
|
|
113
|
+
Home: () => startOfWeek(focused, week),
|
|
114
|
+
End: () => startOfDay(endOfWeek(focused, week)),
|
|
115
|
+
PageUp: () => event.shiftKey ? addYears(focused, -1) : addMonths(focused, -1),
|
|
116
|
+
PageDown: () => event.shiftKey ? addYears(focused, 1) : addMonths(focused, 1)
|
|
117
|
+
}[event.key];
|
|
118
|
+
if (move) {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
navigate(move());
|
|
121
|
+
} else if (event.key === "Enter" || event.key === " ") {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
pick(focused);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const month = startOfMonth(focused);
|
|
127
|
+
const first = startOfWeek(month, week);
|
|
128
|
+
const days = Array.from({ length: 42 }, (_, i) => addDays(first, i));
|
|
129
|
+
const weeks = Array.from({ length: 6 }, (_, i) => days.slice(i * 7, i * 7 + 7));
|
|
130
|
+
const prevDisabled = minDay != null && isBefore(endOfMonth(addMonths(month, -1)), minDay);
|
|
131
|
+
const nextDisabled = maxDay != null && isAfter(addMonths(month, 1), maxDay);
|
|
132
|
+
const previewEnd = range && from && !to ? hovered ?? focused : null;
|
|
133
|
+
const [spanStart, spanEnd] = previewEnd && from ? isBefore(previewEnd, from) ? [previewEnd, from] : [from, previewEnd] : [from, to];
|
|
134
|
+
const inSpan = (day) => spanStart != null && spanEnd != null && isAfter(day, spanStart) && isBefore(day, spanEnd);
|
|
135
|
+
const isEndpoint = (day) => from != null && isSameDay(day, from) || to != null && isSameDay(day, to);
|
|
136
|
+
const show = (date) => format(date, displayFormat, { locale });
|
|
137
|
+
const display = from ? range && to ? `${show(from)} – ${show(to)}` : show(from) : null;
|
|
138
|
+
const labelled = field != null || rest["aria-label"] != null || rest["aria-labelledby"] != null;
|
|
139
|
+
const s = size ?? own.size;
|
|
140
|
+
const hidden = value == null ? "" : range ? `${from ? iso(from) : ""}/${to ? iso(to) : ""}` : from ? iso(from) : "";
|
|
141
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Popover, {
|
|
142
|
+
open,
|
|
143
|
+
onOpenChange,
|
|
144
|
+
placement: "bottom-start",
|
|
145
|
+
modal: true,
|
|
146
|
+
children: [/* @__PURE__ */ jsx(Popover.Trigger, { children: /* @__PURE__ */ jsxs("button", {
|
|
147
|
+
type: "button",
|
|
148
|
+
className: cx("yarcl-input yarcl-select yarcl-date-picker", sizeClass(s, "DatePicker"), radiusClass(radius ?? own.radius, s), colorClass(color ?? own.color), className),
|
|
149
|
+
...rest,
|
|
150
|
+
"aria-describedby": cx(labelled && display && valueId, rest["aria-describedby"]) || void 0,
|
|
151
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
152
|
+
id: valueId,
|
|
153
|
+
className: cx("yarcl-select-value", !display && "yarcl-select-placeholder"),
|
|
154
|
+
children: display ?? placeholder
|
|
155
|
+
}), /* @__PURE__ */ jsx(Icon, { d: calendarPath })]
|
|
156
|
+
}) }), /* @__PURE__ */ jsxs(Popover.Content, {
|
|
157
|
+
"aria-label": labels.dialog,
|
|
158
|
+
initialFocus: cellRef,
|
|
159
|
+
className: cx("yarcl-date-picker-panel", sizeClass(s, "DatePicker"), colorClass(color ?? own.color)),
|
|
160
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
161
|
+
className: "yarcl-date-picker-header",
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsx("button", {
|
|
164
|
+
type: "button",
|
|
165
|
+
className: "yarcl-date-picker-nav",
|
|
166
|
+
"aria-label": labels.previousMonth,
|
|
167
|
+
"aria-disabled": prevDisabled || void 0,
|
|
168
|
+
onClick: () => !prevDisabled && setFocused(clamp(addMonths(focused, -1))),
|
|
169
|
+
children: /* @__PURE__ */ jsx(Icon, { d: "m15 6-6 6 6 6" })
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ jsx("div", {
|
|
172
|
+
id: titleId,
|
|
173
|
+
className: "yarcl-date-picker-title",
|
|
174
|
+
"aria-live": "polite",
|
|
175
|
+
children: format(month, "LLLL y", { locale })
|
|
176
|
+
}),
|
|
177
|
+
/* @__PURE__ */ jsx("button", {
|
|
178
|
+
type: "button",
|
|
179
|
+
className: "yarcl-date-picker-nav",
|
|
180
|
+
"aria-label": labels.nextMonth,
|
|
181
|
+
"aria-disabled": nextDisabled || void 0,
|
|
182
|
+
onClick: () => !nextDisabled && setFocused(clamp(addMonths(focused, 1))),
|
|
183
|
+
children: /* @__PURE__ */ jsx(Icon, { d: "m9 6 6 6-6 6" })
|
|
184
|
+
})
|
|
185
|
+
]
|
|
186
|
+
}), /* @__PURE__ */ jsxs("table", {
|
|
187
|
+
role: "grid",
|
|
188
|
+
"aria-labelledby": titleId,
|
|
189
|
+
"aria-multiselectable": range || void 0,
|
|
190
|
+
className: cx("yarcl-date-picker-grid", radiusClass(radius ?? own.radius, s)),
|
|
191
|
+
onKeyDown: onGridKeyDown,
|
|
192
|
+
onMouseLeave: () => setHovered(null),
|
|
193
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: weeks[0].map((day) => /* @__PURE__ */ jsx("th", {
|
|
194
|
+
scope: "col",
|
|
195
|
+
abbr: format(day, "EEEE", { locale }),
|
|
196
|
+
children: format(day, "EEEEEE", { locale })
|
|
197
|
+
}, day.getDay())) }) }), /* @__PURE__ */ jsx("tbody", { children: weeks.map((row) => /* @__PURE__ */ jsx("tr", { children: row.map((day) => {
|
|
198
|
+
if (!isSameMonth(day, month)) return /* @__PURE__ */ jsx("td", { role: "gridcell" }, iso(day));
|
|
199
|
+
const isFocused = isSameDay(day, focused);
|
|
200
|
+
const endpoint = isEndpoint(day);
|
|
201
|
+
const between = inSpan(day);
|
|
202
|
+
return /* @__PURE__ */ jsx("td", {
|
|
203
|
+
ref: isFocused ? cellRef : void 0,
|
|
204
|
+
role: "gridcell",
|
|
205
|
+
tabIndex: isFocused ? 0 : -1,
|
|
206
|
+
"aria-selected": endpoint || between && !previewEnd,
|
|
207
|
+
"aria-disabled": unavailable(day) || void 0,
|
|
208
|
+
"aria-current": isToday(day) ? "date" : void 0,
|
|
209
|
+
"aria-label": format(day, "PPPP", { locale }),
|
|
210
|
+
"data-selected": endpoint || void 0,
|
|
211
|
+
"data-in-range": between || void 0,
|
|
212
|
+
className: cx("yarcl-date-picker-day", endpoint ? variantClass(variant ?? own.variant) : between && softVariantClass()),
|
|
213
|
+
onClick: () => {
|
|
214
|
+
setFocused(day);
|
|
215
|
+
pick(day);
|
|
216
|
+
},
|
|
217
|
+
onMouseEnter: () => range && setHovered(day),
|
|
218
|
+
children: format(day, "d", { locale })
|
|
219
|
+
}, iso(day));
|
|
220
|
+
}) }, iso(row[0]))) })]
|
|
221
|
+
})]
|
|
222
|
+
})]
|
|
223
|
+
}), name && /* @__PURE__ */ jsx("input", {
|
|
224
|
+
type: "hidden",
|
|
225
|
+
name,
|
|
226
|
+
value: hidden
|
|
227
|
+
})] });
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
export { DatePicker };
|