@exsto/ds 0.0.1-rc.1 → 0.0.1-rc.13
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 +108 -27
- package/dist/chunks/date-range-picker-CWVcHgy2.js +204 -0
- package/dist/chunks/date-range-picker-CWVcHgy2.js.map +1 -0
- package/dist/chunks/input-syWxjY2U.js +172 -0
- package/dist/chunks/input-syWxjY2U.js.map +1 -0
- package/dist/chunks/toggle-Bxg8P-61.js +490 -0
- package/dist/chunks/toggle-Bxg8P-61.js.map +1 -0
- package/dist/date-picker.js +345 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/ds/src/date-picker.d.ts +9 -0
- package/dist/ds/src/index.d.ts +2 -3
- package/dist/ds/src/rich-text-editor.d.ts +8 -0
- package/dist/index.js +4514 -2
- package/dist/index.js.map +1 -0
- package/dist/rich-text-editor.js +905 -0
- package/dist/rich-text-editor.js.map +1 -0
- package/dist/source-hash.txt +1 -0
- package/dist/styles.css +938 -235
- package/dist/theme.css +308 -0
- package/dist/theme.css.d.ts +2 -0
- package/dist/ui/src/components/edyn/animated-number/animated-number.d.ts +30 -0
- package/dist/ui/src/components/edyn/animated-number/animated-number.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/animated-number/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/audio-player/audio-player.d.ts +3 -2
- package/dist/ui/src/components/edyn/date-picker/date-picker.d.ts +76 -0
- package/dist/ui/src/components/edyn/date-picker/date-picker.state.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/date-picker/date-text.d.ts +40 -0
- package/dist/ui/src/components/edyn/date-picker/index.d.ts +2 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.d.ts +28 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/date-range-picker/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/initials-avatar/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.d.ts +54 -0
- package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.test.d.ts +10 -0
- package/dist/ui/src/components/edyn/multi-select/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.d.ts +106 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/progress-line/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/progress-line/progress-line.d.ts +23 -0
- package/dist/ui/src/components/edyn/progress-line/progress-line.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/rich-text-content/allow-list.d.ts +11 -0
- package/dist/ui/src/components/edyn/rich-text-content/index.d.ts +2 -0
- package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.d.ts +22 -0
- package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.test.d.ts +8 -0
- package/dist/ui/src/components/edyn/rich-text-editor/extensions.d.ts +18 -0
- package/dist/ui/src/components/edyn/rich-text-editor/index.d.ts +3 -0
- package/dist/ui/src/components/edyn/rich-text-editor/mention-list.d.ts +17 -0
- package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.d.ts +53 -0
- package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.test.d.ts +6 -0
- package/dist/ui/src/components/edyn/rich-text-editor/serialize.d.ts +13 -0
- package/dist/ui/src/components/edyn/rich-text-editor/sync.d.ts +19 -0
- package/dist/ui/src/components/edyn/rich-text-editor/sync.test.d.ts +11 -0
- package/dist/ui/src/components/edyn/rich-text-editor/toolbar.d.ts +20 -0
- package/dist/ui/src/components/edyn/rich-text-editor/types.d.ts +68 -0
- package/dist/ui/src/components/edyn/video-player/video-player.d.ts +2 -2
- package/dist/ui/src/components/forwarded-props.test.d.ts +8 -0
- package/dist/ui/src/components/ui/accordion/accordion.d.ts +9 -8
- package/dist/ui/src/components/ui/attachment/attachment.d.ts +1 -1
- package/dist/ui/src/components/ui/badge/badge.d.ts +1 -1
- package/dist/ui/src/components/ui/button/button.behavior.test.d.ts +8 -0
- package/dist/ui/src/components/ui/button/button.d.ts +1 -1
- package/dist/ui/src/components/ui/command/command.d.ts +17 -1
- package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.d.ts +12 -1
- package/dist/ui/src/components/ui/input-group/input-group.d.ts +1 -1
- package/dist/ui/src/components/ui/item/item.d.ts +1 -1
- package/dist/ui/src/components/ui/pagination/pagination.d.ts +3 -2
- package/dist/ui/src/components/ui/popover/popover.d.ts +1 -1
- package/dist/ui/src/components/ui/progress/progress.behavior.test.d.ts +8 -0
- package/dist/ui/src/components/ui/progress/progress.d.ts +43 -1
- package/dist/ui/src/components/ui/scroll-area/scroll-area.behavior.test.d.ts +12 -0
- package/dist/ui/src/components/ui/scroll-area/scroll-area.d.ts +59 -6
- package/dist/ui/src/components/ui/separator/separator.behavior.test.d.ts +14 -0
- package/dist/ui/src/components/ui/separator/separator.d.ts +14 -0
- package/dist/ui/src/components/ui/table/table.behavior.test.d.ts +4 -5
- package/dist/ui/src/components/ui/table/table.d.ts +8 -1
- package/dist/ui/src/components/ui/tabs/tabs.d.ts +9 -8
- package/dist/ui/src/index.d.ts +6 -0
- package/dist/ui/src/lib/first-weekday.d.ts +6 -0
- package/dist/ui/src/lib/focus-ring.test.d.ts +8 -0
- package/dist/ui/src/lib/popup-list.d.ts +53 -0
- package/dist/ui/src/lib/popup-list.test.d.ts +10 -0
- package/package.json +32 -8
- package/dist/chunks/toggle-group-ByNq8g7H.js +0 -4263
- package/dist/chunks/toggle-group-ByNq8g7H.js.map +0 -1
- package/dist/dom/src/context.d.ts +0 -34
- package/dist/dom/src/define-element.d.ts +0 -11
- package/dist/dom/src/field-control.d.ts +0 -5
- package/dist/dom/src/index.d.ts +0 -6
- package/dist/dom/src/mode.d.ts +0 -8
- package/dist/dom/src/styles.d.ts +0 -6
- package/dist/dom/src/theme-state.d.ts +0 -1
- package/dist/dom/src/types.d.ts +0 -65
- package/dist/ds/src/elements.d.ts +0 -12
- package/dist/elements/src/edx-accordion.d.ts +0 -14
- package/dist/elements/src/edx-accordion.test.d.ts +0 -1
- package/dist/elements/src/edx-alert.d.ts +0 -14
- package/dist/elements/src/edx-audio-player.d.ts +0 -11
- package/dist/elements/src/edx-avatar.d.ts +0 -10
- package/dist/elements/src/edx-badge.d.ts +0 -12
- package/dist/elements/src/edx-breadcrumb.d.ts +0 -11
- package/dist/elements/src/edx-button-group.d.ts +0 -7
- package/dist/elements/src/edx-button.d.ts +0 -20
- package/dist/elements/src/edx-calendar.d.ts +0 -21
- package/dist/elements/src/edx-card.d.ts +0 -8
- package/dist/elements/src/edx-checkbox.d.ts +0 -18
- package/dist/elements/src/edx-combobox.d.ts +0 -28
- package/dist/elements/src/edx-content-components.d.ts +0 -2
- package/dist/elements/src/edx-dialog.d.ts +0 -20
- package/dist/elements/src/edx-drawer.d.ts +0 -21
- package/dist/elements/src/edx-dropdown-menu.d.ts +0 -29
- package/dist/elements/src/edx-field.d.ts +0 -9
- package/dist/elements/src/edx-field.test.d.ts +0 -1
- package/dist/elements/src/edx-indicator.d.ts +0 -14
- package/dist/elements/src/edx-input-group.d.ts +0 -32
- package/dist/elements/src/edx-input-otp.d.ts +0 -19
- package/dist/elements/src/edx-input.d.ts +0 -31
- package/dist/elements/src/edx-interactive-components.d.ts +0 -2
- package/dist/elements/src/edx-label.d.ts +0 -6
- package/dist/elements/src/edx-pagination.d.ts +0 -8
- package/dist/elements/src/edx-popover.d.ts +0 -24
- package/dist/elements/src/edx-progress.d.ts +0 -10
- package/dist/elements/src/edx-radio-group.d.ts +0 -22
- package/dist/elements/src/edx-radio-group.test.d.ts +0 -1
- package/dist/elements/src/edx-scroll-area.d.ts +0 -15
- package/dist/elements/src/edx-scroll-area.test.d.ts +0 -1
- package/dist/elements/src/edx-select.d.ts +0 -34
- package/dist/elements/src/edx-separator.d.ts +0 -10
- package/dist/elements/src/edx-skeleton.d.ts +0 -8
- package/dist/elements/src/edx-slider.d.ts +0 -17
- package/dist/elements/src/edx-slider.test.d.ts +0 -1
- package/dist/elements/src/edx-switch.d.ts +0 -18
- package/dist/elements/src/edx-table.d.ts +0 -13
- package/dist/elements/src/edx-table.test.d.ts +0 -1
- package/dist/elements/src/edx-tabs.d.ts +0 -15
- package/dist/elements/src/edx-tabs.test.d.ts +0 -1
- package/dist/elements/src/edx-textarea.d.ts +0 -25
- package/dist/elements/src/edx-toggle-group.d.ts +0 -27
- package/dist/elements/src/edx-toggle-group.test.d.ts +0 -1
- package/dist/elements/src/edx-toggle.d.ts +0 -18
- package/dist/elements/src/edx-tooltip.d.ts +0 -28
- package/dist/elements/src/edx-video-player.d.ts +0 -16
- package/dist/elements/src/field-control-bridge.d.ts +0 -7
- package/dist/elements/src/index.d.ts +0 -14
- package/dist/elements/src/projected-content.test.d.ts +0 -1
- package/dist/elements/src/registry.d.ts +0 -39
- package/dist/elements/src/registry.test.d.ts +0 -1
- package/dist/elements.js +0 -4020
- package/dist/elements.js.map +0 -1
- /package/dist/{dom/src/define-element.test.d.ts → ui/src/components/edyn/date-picker/date-text.test.d.ts} +0 -0
- /package/dist/{dom/src/native-slot.test.d.ts → ui/src/lib/first-weekday.test.d.ts} +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { c as Button, n as Popover, r as PopoverContent, s as PopoverTrigger, t as Input, u as cn } from "./chunks/input-syWxjY2U.js";
|
|
2
|
+
import { i as firstWeekdayOf, n as dateToDay, o as Calendar, r as dayToDate } from "./chunks/date-range-picker-CWVcHgy2.js";
|
|
3
|
+
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { IconCalendar } from "@tabler/icons-react";
|
|
6
|
+
//#region ../ui/src/components/edyn/date-picker/date-text.ts
|
|
7
|
+
/**
|
|
8
|
+
* Reading and writing a calendar day as the text a person types.
|
|
9
|
+
*
|
|
10
|
+
* A day is the string `yyyy-mm-dd` everywhere here, never a Date: a Date is an instant, and the instant for "5 March" is a
|
|
11
|
+
* different calendar day in another time zone (local midnight serialised as UTC lands on the 4th east of Greenwich). The
|
|
12
|
+
* string is also what `<input type="date">`, JSON and a .NET `DateOnly` use. Strings of that shape sort and compare as
|
|
13
|
+
* days, so min, max and disabled days are compared as plain strings with no Date involved.
|
|
14
|
+
*/
|
|
15
|
+
var SHAPE = {
|
|
16
|
+
day: "2-digit",
|
|
17
|
+
month: "2-digit",
|
|
18
|
+
year: "numeric"
|
|
19
|
+
};
|
|
20
|
+
/** The order a locale writes day, month and year in: en-GB is day, month, year; en-US month, day, year; sv year first. */
|
|
21
|
+
var datePartsOf = (locale) => new Intl.DateTimeFormat(locale, SHAPE).formatToParts(new Date(2e3, 0, 2, 12)).flatMap((part) => part.type === "day" || part.type === "month" || part.type === "year" ? [part.type] : []);
|
|
22
|
+
/** What to type, written the locale's way: `DD/MM/YYYY` for en-GB. */
|
|
23
|
+
var datePatternOf = (locale) => new Intl.DateTimeFormat(locale, SHAPE).formatToParts(new Date(2e3, 0, 2, 12)).map((part) => {
|
|
24
|
+
if (part.type === "day") return "DD";
|
|
25
|
+
if (part.type === "month") return "MM";
|
|
26
|
+
return part.type === "year" ? "YYYY" : part.value;
|
|
27
|
+
}).join("").trim();
|
|
28
|
+
/** A day written the locale's way: `05/03/2026` for en-GB. */
|
|
29
|
+
var formatDay = (day, locale) => {
|
|
30
|
+
const date = dayToDate(day);
|
|
31
|
+
return date === void 0 ? "" : new Intl.DateTimeFormat(locale, SHAPE).format(date);
|
|
32
|
+
};
|
|
33
|
+
var isRealDay = (year, month, dayOfMonth) => {
|
|
34
|
+
if (year < 1e3 || month < 1 || month > 12 || dayOfMonth < 1) return false;
|
|
35
|
+
const date = new Date(year, month - 1, dayOfMonth, 12);
|
|
36
|
+
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === dayOfMonth;
|
|
37
|
+
};
|
|
38
|
+
var toDay = (year, month, dayOfMonth) => dateToDay(new Date(year, month - 1, dayOfMonth, 12)) ?? "";
|
|
39
|
+
/**
|
|
40
|
+
* Reads typed text as a day. The locale's order decides which number is the day; a first number of four digits is always
|
|
41
|
+
* a year (so `2026-03-05` is read the same everywhere); the separator can be anything that is not a digit, and eight
|
|
42
|
+
* digits with none (`05032026`) are split by the order. A year must have four digits: `5/3/26` is refused, not guessed.
|
|
43
|
+
*/
|
|
44
|
+
var parseDateText = (text, locale) => {
|
|
45
|
+
const trimmed = text.trim();
|
|
46
|
+
if (trimmed === "") return { kind: "empty" };
|
|
47
|
+
if (!/^[\d\s./\-,]+$/.test(trimmed)) return {
|
|
48
|
+
kind: "problem",
|
|
49
|
+
problem: "format"
|
|
50
|
+
};
|
|
51
|
+
const order = datePartsOf(locale);
|
|
52
|
+
let numbers = trimmed.match(/\d+/g) ?? [];
|
|
53
|
+
let parts = order;
|
|
54
|
+
if (numbers.length === 1 && numbers[0]?.length === 8) {
|
|
55
|
+
const digits = numbers[0];
|
|
56
|
+
numbers = order[0] === "year" ? [
|
|
57
|
+
digits.slice(0, 4),
|
|
58
|
+
digits.slice(4, 6),
|
|
59
|
+
digits.slice(6)
|
|
60
|
+
] : [
|
|
61
|
+
digits.slice(0, 2),
|
|
62
|
+
digits.slice(2, 4),
|
|
63
|
+
digits.slice(4)
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
if (numbers.length !== 3) return {
|
|
67
|
+
kind: "problem",
|
|
68
|
+
problem: "format"
|
|
69
|
+
};
|
|
70
|
+
if (numbers[0]?.length === 4) parts = [
|
|
71
|
+
"year",
|
|
72
|
+
"month",
|
|
73
|
+
"day"
|
|
74
|
+
];
|
|
75
|
+
const read = {
|
|
76
|
+
day: "",
|
|
77
|
+
month: "",
|
|
78
|
+
year: ""
|
|
79
|
+
};
|
|
80
|
+
parts.forEach((part, index) => {
|
|
81
|
+
read[part] = numbers[index] ?? "";
|
|
82
|
+
});
|
|
83
|
+
if (read.year.length !== 4 || read.day.length > 2 || read.month.length > 2) return {
|
|
84
|
+
kind: "problem",
|
|
85
|
+
problem: "format"
|
|
86
|
+
};
|
|
87
|
+
const [year, month, dayOfMonth] = [
|
|
88
|
+
Number(read.year),
|
|
89
|
+
Number(read.month),
|
|
90
|
+
Number(read.day)
|
|
91
|
+
];
|
|
92
|
+
return isRealDay(year, month, dayOfMonth) ? {
|
|
93
|
+
kind: "day",
|
|
94
|
+
day: toDay(year, month, dayOfMonth)
|
|
95
|
+
} : {
|
|
96
|
+
kind: "problem",
|
|
97
|
+
problem: "notReal"
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
/** Whether a day is allowed, and if not, which limit it breaks. */
|
|
101
|
+
var dayProblem = (day, { min, max, disabledDates }) => {
|
|
102
|
+
if (min !== void 0 && day < min) return "beforeMin";
|
|
103
|
+
if (max !== void 0 && day > max) return "afterMax";
|
|
104
|
+
return (typeof disabledDates === "function" ? disabledDates(day) : disabledDates?.includes(day) ?? false) ? "unavailable" : void 0;
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region ../ui/src/components/edyn/date-picker/date-picker.tsx
|
|
108
|
+
var DEFAULT_DATE_PICKER_LABELS = {
|
|
109
|
+
choose: "Choose date",
|
|
110
|
+
format: (pattern) => `Enter a date as ${pattern}.`,
|
|
111
|
+
notReal: "That date does not exist.",
|
|
112
|
+
beforeMin: (min) => `Choose ${min} or later.`,
|
|
113
|
+
afterMax: (max) => `Choose ${max} or earlier.`,
|
|
114
|
+
unavailable: "That date is not available."
|
|
115
|
+
};
|
|
116
|
+
var problemText = (problem, labels, locale, limits, pattern) => {
|
|
117
|
+
switch (problem) {
|
|
118
|
+
case "format": return labels.format(pattern);
|
|
119
|
+
case "notReal": return labels.notReal;
|
|
120
|
+
case "beforeMin": return labels.beforeMin(limits.min === void 0 ? "" : formatDay(limits.min, locale));
|
|
121
|
+
case "afterMax": return labels.afterMax(limits.max === void 0 ? "" : formatDay(limits.max, locale));
|
|
122
|
+
default: return labels.unavailable;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it
|
|
127
|
+
* when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.
|
|
128
|
+
*/
|
|
129
|
+
var DatePicker = ({ id, value, onValueChange, min, max, disabledDates, disabled = false, locale = "en-GB", calendarLocale, placeholder, "aria-label": ariaLabel, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, labels: labelOverrides, className }) => {
|
|
130
|
+
const labels = useMemo(() => ({
|
|
131
|
+
...DEFAULT_DATE_PICKER_LABELS,
|
|
132
|
+
...labelOverrides
|
|
133
|
+
}), [labelOverrides]);
|
|
134
|
+
const pattern = useMemo(() => datePatternOf(locale), [locale]);
|
|
135
|
+
const current = value === "" ? void 0 : value;
|
|
136
|
+
const errorId = useId();
|
|
137
|
+
const refField = useRef(null);
|
|
138
|
+
const refInput = useRef(null);
|
|
139
|
+
const refPointer = useRef(false);
|
|
140
|
+
const refKnown = useRef(current);
|
|
141
|
+
const refRefused = useRef(null);
|
|
142
|
+
const [text, setText] = useState(current === void 0 ? "" : formatDay(current, locale));
|
|
143
|
+
const [problem, setProblem] = useState(null);
|
|
144
|
+
const [open, setOpen] = useState(false);
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (current !== refKnown.current) {
|
|
147
|
+
refKnown.current = current;
|
|
148
|
+
refRefused.current = null;
|
|
149
|
+
setText(current === void 0 ? "" : formatDay(current, locale));
|
|
150
|
+
setProblem(null);
|
|
151
|
+
}
|
|
152
|
+
}, [current, locale]);
|
|
153
|
+
const report = (day, state) => {
|
|
154
|
+
refKnown.current = day;
|
|
155
|
+
refRefused.current = state.kind === "refused" ? `${state.text}\u0000${state.problem}` : null;
|
|
156
|
+
onValueChange(day, state);
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Reads the text: a day is reported and written back in full, anything else is said inline and reported as no day,
|
|
160
|
+
* with the state saying whether the field is empty or holds text it refused.
|
|
161
|
+
*/
|
|
162
|
+
const commit = (typed) => {
|
|
163
|
+
const parsed = parseDateText(typed, locale);
|
|
164
|
+
if (parsed.kind === "empty") {
|
|
165
|
+
setProblem(null);
|
|
166
|
+
if (current !== void 0 || refRefused.current !== null) report(void 0, { kind: "empty" });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const found = parsed.kind === "problem" ? parsed.problem : dayProblem(parsed.day, {
|
|
170
|
+
min,
|
|
171
|
+
max,
|
|
172
|
+
disabledDates
|
|
173
|
+
});
|
|
174
|
+
if (found !== void 0) {
|
|
175
|
+
setProblem(found);
|
|
176
|
+
const message = problemText(found, labels, locale, {
|
|
177
|
+
min,
|
|
178
|
+
max
|
|
179
|
+
}, pattern);
|
|
180
|
+
if (current !== void 0 || refRefused.current !== `${typed}\u0000${message}`) report(void 0, {
|
|
181
|
+
kind: "refused",
|
|
182
|
+
text: typed,
|
|
183
|
+
problem: message
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (parsed.kind === "day") {
|
|
188
|
+
setProblem(null);
|
|
189
|
+
setText(formatDay(parsed.day, locale));
|
|
190
|
+
if (parsed.day !== current || refRefused.current !== null) report(parsed.day, { kind: "day" });
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const pick = (date) => {
|
|
194
|
+
const day = dateToDay(date);
|
|
195
|
+
if (day === void 0) return;
|
|
196
|
+
setProblem(null);
|
|
197
|
+
setText(formatDay(day, locale));
|
|
198
|
+
if (day !== current || refRefused.current !== null) report(day, { kind: "day" });
|
|
199
|
+
setOpen(false);
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a
|
|
203
|
+
* day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus
|
|
204
|
+
* going into the calendar that Alt+ArrowDown opened, with the text half typed. Leaving the picker for anywhere else
|
|
205
|
+
* reads whatever is typed.
|
|
206
|
+
*/
|
|
207
|
+
const handleBlur = (event) => {
|
|
208
|
+
const next = event.relatedTarget;
|
|
209
|
+
const intoCalendar = next?.closest("[data-slot=\"popover-content\"]") != null;
|
|
210
|
+
const intoButton = next?.closest("[data-slot=\"date-picker-trigger\"]") != null;
|
|
211
|
+
const typed = refInput.current?.value ?? "";
|
|
212
|
+
if (!(intoCalendar || intoButton || next !== null && refField.current?.contains(next) === true)) {
|
|
213
|
+
commit(typed);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (event.target !== refInput.current) return;
|
|
217
|
+
const onTheWay = intoCalendar || intoButton && refPointer.current;
|
|
218
|
+
const parsed = parseDateText(typed, locale);
|
|
219
|
+
if (parsed.kind === "empty" || parsed.kind === "day" && dayProblem(parsed.day, {
|
|
220
|
+
min,
|
|
221
|
+
max,
|
|
222
|
+
disabledDates
|
|
223
|
+
}) === void 0 || !onTheWay) commit(typed);
|
|
224
|
+
};
|
|
225
|
+
const handleKeyDown = (event) => {
|
|
226
|
+
if (event.key === "ArrowDown" && event.altKey && !disabled) {
|
|
227
|
+
event.preventDefault();
|
|
228
|
+
setOpen(true);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (event.key === "Escape" && open) {
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
event.stopPropagation();
|
|
234
|
+
setOpen(false);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (event.key === "Enter") {
|
|
238
|
+
const parsed = parseDateText(event.currentTarget.value, locale);
|
|
239
|
+
if (parsed.kind !== "empty" && (parsed.kind === "problem" || dayProblem(parsed.day, {
|
|
240
|
+
min,
|
|
241
|
+
max,
|
|
242
|
+
disabledDates
|
|
243
|
+
}) !== void 0)) event.preventDefault();
|
|
244
|
+
commit(event.currentTarget.value);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
const selected = dayToDate(current);
|
|
248
|
+
const limits = {
|
|
249
|
+
min,
|
|
250
|
+
max
|
|
251
|
+
};
|
|
252
|
+
const invalid = problem !== null || ariaInvalid === true || ariaInvalid === "true";
|
|
253
|
+
const describedBy = [ariaDescribedBy, problem === null ? void 0 : errorId].filter(Boolean).join(" ") || void 0;
|
|
254
|
+
const isOff = (date) => {
|
|
255
|
+
const day = dateToDay(date);
|
|
256
|
+
return day !== void 0 && dayProblem(day, {
|
|
257
|
+
min,
|
|
258
|
+
max,
|
|
259
|
+
disabledDates
|
|
260
|
+
}) !== void 0;
|
|
261
|
+
};
|
|
262
|
+
const today = /* @__PURE__ */ new Date();
|
|
263
|
+
const start = dayToDate(min);
|
|
264
|
+
const end = dayToDate(max);
|
|
265
|
+
const month = selected ?? (start !== void 0 && today < start ? start : end !== void 0 && today > end ? end : today);
|
|
266
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
267
|
+
"data-slot": "date-picker",
|
|
268
|
+
className: cn("flex flex-col gap-1.5", className),
|
|
269
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
270
|
+
ref: refField,
|
|
271
|
+
className: "relative",
|
|
272
|
+
onPointerDownCapture: () => {
|
|
273
|
+
refPointer.current = true;
|
|
274
|
+
},
|
|
275
|
+
onPointerUpCapture: () => {
|
|
276
|
+
refPointer.current = false;
|
|
277
|
+
},
|
|
278
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
279
|
+
ref: refInput,
|
|
280
|
+
id,
|
|
281
|
+
type: "text",
|
|
282
|
+
inputMode: "numeric",
|
|
283
|
+
autoComplete: "off",
|
|
284
|
+
"data-slot": "date-picker-input",
|
|
285
|
+
value: text,
|
|
286
|
+
placeholder: placeholder ?? pattern,
|
|
287
|
+
disabled,
|
|
288
|
+
"aria-label": ariaLabel,
|
|
289
|
+
"aria-invalid": invalid ? true : void 0,
|
|
290
|
+
"aria-describedby": describedBy,
|
|
291
|
+
className: "pr-9",
|
|
292
|
+
onChange: (event) => {
|
|
293
|
+
setText(event.target.value);
|
|
294
|
+
setProblem(null);
|
|
295
|
+
},
|
|
296
|
+
onBlur: handleBlur,
|
|
297
|
+
onKeyDown: handleKeyDown
|
|
298
|
+
}), /* @__PURE__ */ jsxs(Popover, {
|
|
299
|
+
open,
|
|
300
|
+
onOpenChange: setOpen,
|
|
301
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
302
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
303
|
+
variant: "ghost",
|
|
304
|
+
size: "icon-sm",
|
|
305
|
+
"data-slot": "date-picker-trigger",
|
|
306
|
+
"aria-label": labels.choose,
|
|
307
|
+
disabled,
|
|
308
|
+
className: "absolute top-0.5 right-0.5",
|
|
309
|
+
onBlur: handleBlur
|
|
310
|
+
}),
|
|
311
|
+
children: /* @__PURE__ */ jsx(IconCalendar, { "aria-hidden": "true" })
|
|
312
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
313
|
+
anchor: refField,
|
|
314
|
+
align: "start",
|
|
315
|
+
className: "w-auto p-0",
|
|
316
|
+
finalFocus: refInput,
|
|
317
|
+
onBlur: handleBlur,
|
|
318
|
+
children: /* @__PURE__ */ jsx(Calendar, {
|
|
319
|
+
mode: "single",
|
|
320
|
+
required: true,
|
|
321
|
+
autoFocus: true,
|
|
322
|
+
locale: calendarLocale,
|
|
323
|
+
weekStartsOn: firstWeekdayOf(locale),
|
|
324
|
+
defaultMonth: month,
|
|
325
|
+
startMonth: start,
|
|
326
|
+
endMonth: end,
|
|
327
|
+
selected,
|
|
328
|
+
disabled: isOff,
|
|
329
|
+
onSelect: pick
|
|
330
|
+
})
|
|
331
|
+
})]
|
|
332
|
+
})]
|
|
333
|
+
}), problem !== null && /* @__PURE__ */ jsx("p", {
|
|
334
|
+
id: errorId,
|
|
335
|
+
"data-slot": "date-picker-error",
|
|
336
|
+
role: "alert",
|
|
337
|
+
className: "text-sm text-destructive",
|
|
338
|
+
children: problemText(problem, labels, locale, limits, pattern)
|
|
339
|
+
})]
|
|
340
|
+
});
|
|
341
|
+
};
|
|
342
|
+
//#endregion
|
|
343
|
+
export { DEFAULT_DATE_PICKER_LABELS, DatePicker };
|
|
344
|
+
|
|
345
|
+
//# sourceMappingURL=date-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-picker.js","names":[],"sources":["../../ui/src/components/edyn/date-picker/date-text.ts","../../ui/src/components/edyn/date-picker/date-picker.tsx"],"sourcesContent":["/**\n * Reading and writing a calendar day as the text a person types.\n *\n * A day is the string `yyyy-mm-dd` everywhere here, never a Date: a Date is an instant, and the instant for \"5 March\" is a\n * different calendar day in another time zone (local midnight serialised as UTC lands on the 4th east of Greenwich). The\n * string is also what `<input type=\"date\">`, JSON and a .NET `DateOnly` use. Strings of that shape sort and compare as\n * days, so min, max and disabled days are compared as plain strings with no Date involved.\n */\n\nimport { dateToDay, dayToDate } from '@edyn/ui/components/edyn/date-range-picker';\n\nexport type TDatePart = 'day' | 'month' | 'year';\n\n/** Why a typed text is not a day the picker can use. */\nexport type TDateProblem = 'format' | 'notReal' | 'beforeMin' | 'afterMax' | 'unavailable';\n\nexport type TParsedDay = { kind: 'empty' } | { kind: 'day'; day: string } | { kind: 'problem'; problem: 'format' | 'notReal' };\n\nconst SHAPE = { day: '2-digit', month: '2-digit', year: 'numeric' } as const;\n\n/** The order a locale writes day, month and year in: en-GB is day, month, year; en-US month, day, year; sv year first. */\nexport const datePartsOf = (locale: string): TDatePart[] =>\n new Intl.DateTimeFormat(locale, SHAPE)\n .formatToParts(new Date(2000, 0, 2, 12))\n .flatMap((part) => (part.type === 'day' || part.type === 'month' || part.type === 'year' ? [part.type] : []));\n\n/** What to type, written the locale's way: `DD/MM/YYYY` for en-GB. */\nexport const datePatternOf = (locale: string): string =>\n new Intl.DateTimeFormat(locale, SHAPE)\n .formatToParts(new Date(2000, 0, 2, 12))\n .map((part) => {\n if (part.type === 'day') {\n return 'DD';\n }\n if (part.type === 'month') {\n return 'MM';\n }\n return part.type === 'year' ? 'YYYY' : part.value;\n })\n .join('')\n .trim();\n\n/** A day written the locale's way: `05/03/2026` for en-GB. */\nexport const formatDay = (day: string, locale: string): string => {\n const date = dayToDate(day);\n return date === undefined ? '' : new Intl.DateTimeFormat(locale, SHAPE).format(date);\n};\n\nconst isRealDay = (year: number, month: number, dayOfMonth: number): boolean => {\n if (year < 1000 || month < 1 || month > 12 || dayOfMonth < 1) {\n return false;\n }\n const date = new Date(year, month - 1, dayOfMonth, 12);\n return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === dayOfMonth;\n};\n\nconst toDay = (year: number, month: number, dayOfMonth: number): string => dateToDay(new Date(year, month - 1, dayOfMonth, 12)) ?? '';\n\n/**\n * Reads typed text as a day. The locale's order decides which number is the day; a first number of four digits is always\n * a year (so `2026-03-05` is read the same everywhere); the separator can be anything that is not a digit, and eight\n * digits with none (`05032026`) are split by the order. A year must have four digits: `5/3/26` is refused, not guessed.\n */\nexport const parseDateText = (text: string, locale: string): TParsedDay => {\n const trimmed = text.trim();\n if (trimmed === '') {\n return { kind: 'empty' };\n }\n if (!/^[\\d\\s./\\-,]+$/.test(trimmed)) {\n return { kind: 'problem', problem: 'format' };\n }\n const order = datePartsOf(locale);\n let numbers = trimmed.match(/\\d+/g) ?? [];\n let parts: readonly TDatePart[] = order;\n if (numbers.length === 1 && numbers[0]?.length === 8) {\n const digits = numbers[0];\n const yearFirst = order[0] === 'year';\n numbers = yearFirst ? [digits.slice(0, 4), digits.slice(4, 6), digits.slice(6)] : [digits.slice(0, 2), digits.slice(2, 4), digits.slice(4)];\n }\n if (numbers.length !== 3) {\n return { kind: 'problem', problem: 'format' };\n }\n if (numbers[0]?.length === 4) {\n parts = ['year', 'month', 'day'];\n }\n const read: Record<TDatePart, string> = { day: '', month: '', year: '' };\n parts.forEach((part, index) => {\n read[part] = numbers[index] ?? '';\n });\n if (read.year.length !== 4 || read.day.length > 2 || read.month.length > 2) {\n return { kind: 'problem', problem: 'format' };\n }\n const [year, month, dayOfMonth] = [Number(read.year), Number(read.month), Number(read.day)];\n return isRealDay(year, month, dayOfMonth) ? { kind: 'day', day: toDay(year, month, dayOfMonth) } : { kind: 'problem', problem: 'notReal' };\n};\n\nexport type TDisabledDays = readonly string[] | ((day: string) => boolean);\n\nexport interface IDayLimits {\n min?: string;\n max?: string;\n disabledDates?: TDisabledDays;\n}\n\n/** Whether a day is allowed, and if not, which limit it breaks. */\nexport const dayProblem = (day: string, { min, max, disabledDates }: IDayLimits): TDateProblem | undefined => {\n if (min !== undefined && day < min) {\n return 'beforeMin';\n }\n if (max !== undefined && day > max) {\n return 'afterMax';\n }\n const off = typeof disabledDates === 'function' ? disabledDates(day) : (disabledDates?.includes(day) ?? false);\n return off ? 'unavailable' : undefined;\n};\n","import { dateToDay, dayToDate } from '@edyn/ui/components/edyn/date-range-picker';\nimport { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Input } from '@edyn/ui/components/ui/input';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport { type ChangeEvent, type FocusEvent, type KeyboardEvent, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport type { Locale } from 'react-day-picker';\nimport { datePatternOf, dayProblem, formatDay, parseDateText, type TDateProblem, type TDisabledDays } from './date-text';\n\n/** Every word the component prints, so a product can translate it. */\nexport interface IDatePickerLabels {\n /** The calendar button's accessible name. */\n choose: string;\n /** Said when the text is not a date; `pattern` is what to type, written the locale's way (`DD/MM/YYYY`). */\n format: (pattern: string) => string;\n notReal: string;\n /** `min` and `max` are written the locale's way. */\n beforeMin: (min: string) => string;\n afterMax: (max: string) => string;\n unavailable: string;\n}\n\nexport const DEFAULT_DATE_PICKER_LABELS: IDatePickerLabels = {\n choose: 'Choose date',\n format: (pattern) => `Enter a date as ${pattern}.`,\n notReal: 'That date does not exist.',\n beforeMin: (min) => `Choose ${min} or later.`,\n afterMax: (max) => `Choose ${max} or earlier.`,\n unavailable: 'That date is not available.',\n};\n\n/**\n * What the field holds when it reports, so a parent can tell three things apart that all arrive as `undefined`:\n * - `day`: a usable day, typed or picked.\n * - `empty`: the field was emptied, or nothing has been put in it. A required field is right to ask for a date.\n * - `refused`: the field holds text it will not take (not a day, not a real one, or outside `min`, `max` or\n * `disabledDates`). `text` is what was typed and `problem` the message the picker shows under the field, already\n * in the picker's `labels`. The picker is saying so itself, so the page should not add a second message.\n */\nexport type TDatePickerState = { kind: 'day' } | { kind: 'empty' } | { kind: 'refused'; text: string; problem: string };\n\nexport interface IDatePickerProps {\n /** For a Label's htmlFor; inside a Field the Field's label is already wired. */\n id?: string;\n /**\n * The chosen day as `yyyy-mm-dd`, or `undefined` for none (an empty string counts as none, so a value kept for an\n * `<input type=\"date\">` drops in). A string and not a Date, on purpose: a Date is an instant, and the instant for\n * \"5 March\" is another calendar day in another time zone, so a Date serialised for an API drifts a day. The text is\n * what `<input type=\"date\">`, JSON and a .NET `DateOnly` use, and it compares as a day.\n */\n value: string | undefined;\n /**\n * Called with a valid day, or with `undefined` when the field is emptied or its text is not a usable day. The second\n * argument says which of the two it was (see `TDatePickerState`); a caller that has no use for it can ignore it.\n * Text that is refused is reported each time it changes, also when the field held no day before, so a form that\n * tracks the state hears about it. Emptying the field after that reports `empty`.\n */\n onValueChange: (value: string | undefined, state: TDatePickerState) => void;\n /** The earliest and latest day that can be chosen, as `yyyy-mm-dd`, both included. */\n min?: string;\n max?: string;\n /** Days that cannot be chosen: a list of `yyyy-mm-dd`, or a function that says so for one (weekends, say). */\n disabledDates?: TDisabledDays;\n disabled?: boolean;\n /** How a day is written and in which order it is typed (BCP 47); en-GB reads 05/03/2026. */\n locale?: string;\n /** The calendar's own words (month and weekday names), as a date-fns locale; English by default. The first weekday follows `locale`. */\n calendarLocale?: Locale;\n /** What the empty field shows; the pattern for the locale (`DD/MM/YYYY`) by default. */\n placeholder?: string;\n /** An accessible name, when there is no visible label to point at. */\n 'aria-label'?: string;\n /** Marks the field invalid (the kit's destructive border), as a Field's invalid state does. */\n 'aria-invalid'?: boolean | 'true' | 'false';\n /** The id of the text that explains it. Added to what a Field names already, and to the picker's own error. */\n 'aria-describedby'?: string;\n labels?: Partial<IDatePickerLabels>;\n /** Layout only (a width); the look is the kit's. */\n className?: string;\n}\n\nconst problemText = (\n problem: TDateProblem,\n labels: IDatePickerLabels,\n locale: string,\n limits: { min?: string; max?: string },\n pattern: string,\n): string => {\n switch (problem) {\n case 'format':\n return labels.format(pattern);\n case 'notReal':\n return labels.notReal;\n case 'beforeMin':\n return labels.beforeMin(limits.min === undefined ? '' : formatDay(limits.min, locale));\n case 'afterMax':\n return labels.afterMax(limits.max === undefined ? '' : formatDay(limits.max, locale));\n default:\n return labels.unavailable;\n }\n};\n\n/**\n * One calendar day, typed or picked. The field takes the day in the locale's order (05/03/2026 in en-GB) and reads it\n * when it loses focus, saying inline what is wrong with it; the button, or Alt+ArrowDown in the field, opens a calendar.\n */\nexport const DatePicker = ({\n id,\n value,\n onValueChange,\n min,\n max,\n disabledDates,\n disabled = false,\n locale = 'en-GB',\n calendarLocale,\n placeholder,\n 'aria-label': ariaLabel,\n 'aria-invalid': ariaInvalid,\n 'aria-describedby': ariaDescribedBy,\n labels: labelOverrides,\n className,\n}: IDatePickerProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_DATE_PICKER_LABELS, ...labelOverrides }), [labelOverrides]);\n const pattern = useMemo(() => datePatternOf(locale), [locale]);\n const current = value === '' ? undefined : value;\n const errorId = useId();\n const refField = useRef<HTMLDivElement | null>(null);\n const refInput = useRef<HTMLInputElement | null>(null);\n // The mouse is down inside the picker, so a blur of the field is a click on something in it, not a Tab.\n const refPointer = useRef(false);\n // The day the field last agreed on with the page, so a change from outside is told from our own report of it.\n const refKnown = useRef<string | undefined>(current);\n // The refused text last reported (with its message), or null: told apart from a fresh refusal so it is reported once,\n // and so that emptying a field that held a refused text is reported although it held no day.\n const refRefused = useRef<string | null>(null);\n const [text, setText] = useState(current === undefined ? '' : formatDay(current, locale));\n const [problem, setProblem] = useState<TDateProblem | null>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n if (current !== refKnown.current) {\n refKnown.current = current;\n refRefused.current = null;\n setText(current === undefined ? '' : formatDay(current, locale));\n setProblem(null);\n }\n }, [current, locale]);\n\n const report = (day: string | undefined, state: TDatePickerState) => {\n refKnown.current = day;\n refRefused.current = state.kind === 'refused' ? `${state.text}\\u0000${state.problem}` : null;\n onValueChange(day, state);\n };\n\n /**\n * Reads the text: a day is reported and written back in full, anything else is said inline and reported as no day,\n * with the state saying whether the field is empty or holds text it refused.\n */\n const commit = (typed: string) => {\n const parsed = parseDateText(typed, locale);\n if (parsed.kind === 'empty') {\n setProblem(null);\n if (current !== undefined || refRefused.current !== null) {\n report(undefined, { kind: 'empty' });\n }\n return;\n }\n const found = parsed.kind === 'problem' ? parsed.problem : dayProblem(parsed.day, { min, max, disabledDates });\n if (found !== undefined) {\n setProblem(found);\n const message = problemText(found, labels, locale, { min, max }, pattern);\n if (current !== undefined || refRefused.current !== `${typed}\\u0000${message}`) {\n report(undefined, { kind: 'refused', text: typed, problem: message });\n }\n return;\n }\n if (parsed.kind === 'day') {\n setProblem(null);\n setText(formatDay(parsed.day, locale));\n if (parsed.day !== current || refRefused.current !== null) {\n report(parsed.day, { kind: 'day' });\n }\n }\n };\n\n const pick = (date: Date | undefined) => {\n const day = dateToDay(date);\n if (day === undefined) {\n return;\n }\n setProblem(null);\n setText(formatDay(day, locale));\n if (day !== current || refRefused.current !== null) {\n report(day, { kind: 'day' });\n }\n setOpen(false);\n };\n\n /**\n * Text is read as the person leaves the field. A day (or nothing) is taken at once; a message about text that is not a\n * day is shown too, unless they are only on the way to the calendar: a press of the mouse on the button, or focus\n * going into the calendar that Alt+ArrowDown opened, with the text half typed. Leaving the picker for anywhere else\n * reads whatever is typed.\n */\n const handleBlur = (event: FocusEvent<HTMLElement>) => {\n const next = event.relatedTarget as Element | null;\n const intoCalendar = next?.closest('[data-slot=\"popover-content\"]') != null;\n const intoButton = next?.closest('[data-slot=\"date-picker-trigger\"]') != null;\n const typed = refInput.current?.value ?? '';\n if (!(intoCalendar || intoButton || (next !== null && refField.current?.contains(next) === true))) {\n commit(typed);\n return;\n }\n if (event.target !== refInput.current) {\n return;\n }\n const onTheWay = intoCalendar || (intoButton && refPointer.current);\n const parsed = parseDateText(typed, locale);\n const usable = parsed.kind === 'empty' || (parsed.kind === 'day' && dayProblem(parsed.day, { min, max, disabledDates }) === undefined);\n if (usable || !onTheWay) {\n commit(typed);\n }\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown' && event.altKey && !disabled) {\n event.preventDefault();\n setOpen(true);\n return;\n }\n // The field has no key handler of Base UI's to close the calendar, so the key would reach the Sheet or Dialog around\n // it and close that too. While the calendar is open it is this field's key; when it is closed it is the page's.\n if (event.key === 'Escape' && open) {\n event.preventDefault();\n event.stopPropagation();\n setOpen(false);\n return;\n }\n if (event.key === 'Enter') {\n const parsed = parseDateText(event.currentTarget.value, locale);\n if (parsed.kind !== 'empty' && (parsed.kind === 'problem' || dayProblem(parsed.day, { min, max, disabledDates }) !== undefined)) {\n event.preventDefault();\n }\n commit(event.currentTarget.value);\n }\n };\n\n const selected = dayToDate(current);\n const limits = { min, max };\n const invalid = problem !== null || ariaInvalid === true || ariaInvalid === 'true';\n const describedBy = [ariaDescribedBy, problem === null ? undefined : errorId].filter(Boolean).join(' ') || undefined;\n const isOff = (date: Date): boolean => {\n const day = dateToDay(date);\n return day !== undefined && dayProblem(day, { min, max, disabledDates }) !== undefined;\n };\n const today = new Date();\n const start = dayToDate(min);\n const end = dayToDate(max);\n const month = selected ?? (start !== undefined && today < start ? start : end !== undefined && today > end ? end : today);\n\n return (\n <div data-slot=\"date-picker\" className={cn('flex flex-col gap-1.5', className)}>\n <div\n ref={refField}\n className=\"relative\"\n onPointerDownCapture={() => {\n refPointer.current = true;\n }}\n onPointerUpCapture={() => {\n refPointer.current = false;\n }}\n >\n <Input\n ref={refInput}\n id={id}\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"off\"\n data-slot=\"date-picker-input\"\n value={text}\n placeholder={placeholder ?? pattern}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-invalid={invalid ? true : undefined}\n aria-describedby={describedBy}\n className=\"pr-9\"\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n setText(event.target.value);\n setProblem(null);\n }}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n />\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n data-slot=\"date-picker-trigger\"\n aria-label={labels.choose}\n disabled={disabled}\n className=\"absolute top-0.5 right-0.5\"\n onBlur={handleBlur}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n </PopoverTrigger>\n <PopoverContent anchor={refField} align=\"start\" className=\"w-auto p-0\" finalFocus={refInput} onBlur={handleBlur}>\n <Calendar\n mode=\"single\"\n required\n autoFocus\n locale={calendarLocale}\n weekStartsOn={firstWeekdayOf(locale)}\n defaultMonth={month}\n startMonth={start}\n endMonth={end}\n selected={selected}\n disabled={isOff}\n onSelect={pick}\n />\n </PopoverContent>\n </Popover>\n </div>\n {problem !== null && (\n <p id={errorId} data-slot=\"date-picker-error\" role=\"alert\" className=\"text-sm text-destructive\">\n {problemText(problem, labels, locale, limits, pattern)}\n </p>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@exsto/ds/date-picker` - the DatePicker, on its own entry on purpose.
|
|
3
|
+
*
|
|
4
|
+
* It is a field a person types a date into as well as picks one, with the parsing, the locale's order of day, month and
|
|
5
|
+
* year and the messages that come with that (about 13 KB unminified). Re-exported from `@exsto/ds` it would be one more
|
|
6
|
+
* thing in the entry every consumer loads and a bundler cannot always tell it unused. Here an app pays for it only on the
|
|
7
|
+
* pages that import this path.
|
|
8
|
+
*/
|
|
9
|
+
export * from '../../ui/src/components/edyn/date-picker';
|
package/dist/ds/src/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@exsto/ds` - the React entry, and the default way to use the design system.
|
|
3
3
|
*
|
|
4
|
-
* Re-exports the component tier verbatim.
|
|
5
|
-
* `@exsto/ds/
|
|
6
|
-
* use, and styles ship precompiled at `@exsto/ds/styles.css`.
|
|
4
|
+
* Re-exports the component tier verbatim. Styles ship precompiled at
|
|
5
|
+
* `@exsto/ds/styles.css`, and the token layer uncompiled at `@exsto/ds/theme.css`.
|
|
7
6
|
*/
|
|
8
7
|
export * from '../../ui/src';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@exsto/ds/rich-text-editor` - the rich-text editor, on its own entry on purpose.
|
|
3
|
+
*
|
|
4
|
+
* The editor carries its own engine (TipTap on ProseMirror, roughly 140 KB gzipped). Re-exported from
|
|
5
|
+
* `@exsto/ds` it would be one more import in the entry every consumer loads, and a bundler cannot
|
|
6
|
+
* always tell it unused. Here an app pays for it only on the pages that import this path.
|
|
7
|
+
*/
|
|
8
|
+
export * from '../../ui/src/components/edyn/rich-text-editor';
|