@exsto/ds 0.0.1-rc.16 → 0.0.1-rc.18

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.
Files changed (53) hide show
  1. package/README.md +38 -0
  2. package/dist/ds/src/file-input.d.ts +9 -0
  3. package/dist/ds/src/time-field.d.ts +9 -0
  4. package/dist/file-input.js +2 -0
  5. package/dist/index.js +4 -3
  6. package/dist/source-hash.txt +1 -1
  7. package/dist/styles.css +176 -38
  8. package/dist/time-field.js +2 -0
  9. package/dist/ui/src/components/edyn/file-input/file-input.d.ts +90 -0
  10. package/dist/ui/src/components/edyn/file-input/file-input.js +261 -0
  11. package/dist/ui/src/components/edyn/file-input/file-input.js.map +1 -0
  12. package/dist/ui/src/components/edyn/file-input/file-input.test.d.ts +8 -0
  13. package/dist/ui/src/components/edyn/file-input/index.d.ts +1 -0
  14. package/dist/ui/src/components/edyn/multi-select/multi-select.d.ts +12 -1
  15. package/dist/ui/src/components/edyn/multi-select/multi-select.js +11 -7
  16. package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -1
  17. package/dist/ui/src/components/edyn/presence/presence.d.ts +1 -1
  18. package/dist/ui/src/components/edyn/presence/presence.js +5 -1
  19. package/dist/ui/src/components/edyn/presence/presence.js.map +1 -1
  20. package/dist/ui/src/components/edyn/time-field/index.d.ts +1 -0
  21. package/dist/ui/src/components/edyn/time-field/time-field.d.ts +85 -0
  22. package/dist/ui/src/components/edyn/time-field/time-field.js +330 -0
  23. package/dist/ui/src/components/edyn/time-field/time-field.js.map +1 -0
  24. package/dist/ui/src/components/edyn/time-field/time-field.test.d.ts +8 -0
  25. package/dist/ui/src/components/ui/calendar/calendar.behavior.test.d.ts +9 -0
  26. package/dist/ui/src/components/ui/calendar/calendar.d.ts +1 -1
  27. package/dist/ui/src/components/ui/calendar/calendar.js +33 -2
  28. package/dist/ui/src/components/ui/calendar/calendar.js.map +1 -1
  29. package/dist/ui/src/components/ui/combobox/combobox.d.ts +10 -0
  30. package/dist/ui/src/components/ui/combobox/combobox.js +37 -5
  31. package/dist/ui/src/components/ui/combobox/combobox.js.map +1 -1
  32. package/dist/ui/src/components/ui/context-menu/context-menu.d.ts +1 -1
  33. package/dist/ui/src/components/ui/context-menu/context-menu.js +23 -10
  34. package/dist/ui/src/components/ui/context-menu/context-menu.js.map +1 -1
  35. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js +5 -4
  36. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
  37. package/dist/ui/src/components/ui/menubar/menubar.js +1 -1
  38. package/dist/ui/src/components/ui/menubar/menubar.js.map +1 -1
  39. package/dist/ui/src/components/ui/scroll-area/scroll-area.js +1 -1
  40. package/dist/ui/src/components/ui/scroll-area/scroll-area.js.map +1 -1
  41. package/dist/ui/src/components/ui/select/select.js +7 -6
  42. package/dist/ui/src/components/ui/select/select.js.map +1 -1
  43. package/dist/ui/src/components/ui/sheet/sheet.d.ts +8 -0
  44. package/dist/ui/src/components/ui/sheet/sheet.js +11 -3
  45. package/dist/ui/src/components/ui/sheet/sheet.js.map +1 -1
  46. package/dist/ui/src/components/ui/table/table.d.ts +9 -2
  47. package/dist/ui/src/components/ui/table/table.js +19 -6
  48. package/dist/ui/src/components/ui/table/table.js.map +1 -1
  49. package/dist/ui/src/index.d.ts +1 -0
  50. package/dist/ui/src/lib/popup-list.d.ts +29 -4
  51. package/dist/ui/src/lib/popup-list.js +30 -5
  52. package/dist/ui/src/lib/popup-list.js.map +1 -1
  53. package/package.json +9 -1
@@ -1 +1 @@
1
- {"version":3,"file":"presence.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/presence/presence.tsx"],"sourcesContent":["import { cn } from '@edyn/ui/lib/utils';\nimport type { ComponentPropsWithoutRef } from 'react';\n\n/** Where a person is, as a chat or a directory shows it. */\nexport const PRESENCE_STATES = ['available', 'busy', 'away', 'offline', 'unknown'] as const;\n\nexport type TPresenceState = (typeof PRESENCE_STATES)[number];\n\n/** The words for each state, for assistive technology and for the visible label. A product that is not in English passes its own. */\nexport const DEFAULT_PRESENCE_LABELS: Readonly<Record<TPresenceState, string>> = {\n available: 'Available',\n busy: 'Busy',\n away: 'Away',\n offline: 'Offline',\n unknown: 'Unknown',\n};\n\n/**\n * Each state has its own colour and its own shape, because colour alone is not a signal (WCAG 1.4.1): available is a solid dot,\n * busy a solid dot with a bar across it, away a solid dot with a bite out of it, offline a hollow ring and unknown a dashed ring.\n * The colours are the theme's tokens, so they follow light and dark, and each clears 3:1 against the page, a card and a muted\n * surface in both (e2e/presence.spec.ts measures it).\n */\nconst DOT: Readonly<Record<TPresenceState, string>> = {\n available: 'bg-success',\n busy: 'bg-destructive after:absolute after:inset-x-0.5 after:top-1/2 after:h-0.5 after:-translate-y-1/2 after:bg-background',\n away: 'bg-warning after:absolute after:-top-px after:-right-px after:size-1.5 after:rounded-full after:bg-background',\n offline: 'border-2 border-muted-foreground',\n unknown: 'border-2 border-dashed border-muted-foreground',\n};\n\nexport interface IPresenceProps extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {\n state: TPresenceState;\n /** Show the state's word beside the dot. Off, the dot stands alone and the word is there for assistive technology only. */\n showLabel?: boolean;\n /** Other words for the state (a translation, or \"In a meeting\"); the default words are in `DEFAULT_PRESENCE_LABELS`. */\n label?: string;\n}\n\n/** A person's presence: a small dot, and optionally its word. Always has the word as text, so it is never a picture alone. */\nexport const Presence = ({ state, showLabel = false, label, className, ...props }: IPresenceProps) => {\n return (\n <span data-slot=\"presence\" data-state={state} className={cn('inline-flex items-center gap-1.5 text-sm', className)} {...props}>\n <span aria-hidden=\"true\" data-slot=\"presence-dot\" className={cn('relative size-2.5 shrink-0 rounded-full', DOT[state])} />\n <span data-slot=\"presence-label\" className={cn(!showLabel && 'sr-only')}>\n {label ?? DEFAULT_PRESENCE_LABELS[state]}\n </span>\n </span>\n );\n};\n"],"mappings":";;;;AAIA,IAAa,kBAAkB;CAAC;CAAa;CAAQ;CAAQ;CAAW;AAAS;;AAKjF,IAAa,0BAAoE;CAC/E,WAAW;CACX,MAAM;CACN,MAAM;CACN,SAAS;CACT,SAAS;AACX;;;;;;;AAQA,IAAM,MAAgD;CACpD,WAAW;CACX,MAAM;CACN,MAAM;CACN,SAAS;CACT,SAAS;AACX;;AAWA,IAAa,YAAY,EAAE,OAAO,YAAY,OAAO,OAAO,WAAW,GAAG,YAA4B;CACpG,OACE,qBAAC,QAAD;EAAM,aAAU;EAAW,cAAY;EAAO,WAAW,GAAG,4CAA4C,SAAS;EAAG,GAAI;EAAxH,UAAA,CACE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAe,WAAW,GAAG,2CAA2C,IAAI,MAAM;EAAI,CAAA,GACzH,oBAAC,QAAD;GAAM,aAAU;GAAiB,WAAW,GAAG,CAAC,aAAa,SAAS;GACnE,UAAA,SAAS,wBAAwB;EAC9B,CAAA,CACF;;AAEV"}
1
+ {"version":3,"file":"presence.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/presence/presence.tsx"],"sourcesContent":["import { cn } from '@edyn/ui/lib/utils';\nimport type { ComponentPropsWithoutRef } from 'react';\n\n/** Where a person is, as a chat or a directory shows it. */\nexport const PRESENCE_STATES = ['available', 'busy', 'away', 'out-of-office', 'offline', 'unknown'] as const;\n\nexport type TPresenceState = (typeof PRESENCE_STATES)[number];\n\n/** The words for each state, for assistive technology and for the visible label. A product that is not in English passes its own. */\nexport const DEFAULT_PRESENCE_LABELS: Readonly<Record<TPresenceState, string>> = {\n available: 'Available',\n busy: 'Busy',\n away: 'Away',\n 'out-of-office': 'Out of office',\n offline: 'Offline',\n unknown: 'Unknown',\n};\n\n/**\n * Each state has its own colour and its own shape, because colour alone is not a signal (WCAG 1.4.1): available is a solid dot,\n * busy a solid dot with a bar across it, away a solid dot with a bite out of it, out of office a solid dot with an arrow pointing out\n * (the info colour, the one hue the other states leave free), offline a hollow ring and unknown a dashed ring.\n * The colours are the theme's tokens, so they follow light and dark, and each clears 3:1 against the page, a card and a muted\n * surface in both (e2e/presence.spec.ts measures it).\n */\nconst DOT: Readonly<Record<TPresenceState, string>> = {\n available: 'bg-success',\n busy: 'bg-destructive after:absolute after:inset-x-0.5 after:top-1/2 after:h-0.5 after:-translate-y-1/2 after:bg-background',\n away: 'bg-warning after:absolute after:-top-px after:-right-px after:size-1.5 after:rounded-full after:bg-background',\n 'out-of-office':\n 'bg-info after:absolute after:top-1/2 after:left-1/2 after:-translate-x-1/3 after:-translate-y-1/2 after:border-y-3 after:border-l-5 after:border-y-transparent after:border-l-background',\n offline: 'border-2 border-muted-foreground',\n unknown: 'border-2 border-dashed border-muted-foreground',\n};\n\nexport interface IPresenceProps extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {\n state: TPresenceState;\n /** Show the state's word beside the dot. Off, the dot stands alone and the word is there for assistive technology only. */\n showLabel?: boolean;\n /** Other words for the state (a translation, or \"In a meeting\"); the default words are in `DEFAULT_PRESENCE_LABELS`. */\n label?: string;\n}\n\n/** A person's presence: a small dot, and optionally its word. Always has the word as text, so it is never a picture alone. */\nexport const Presence = ({ state, showLabel = false, label, className, ...props }: IPresenceProps) => {\n return (\n <span data-slot=\"presence\" data-state={state} className={cn('inline-flex items-center gap-1.5 text-sm', className)} {...props}>\n <span aria-hidden=\"true\" data-slot=\"presence-dot\" className={cn('relative size-2.5 shrink-0 rounded-full', DOT[state])} />\n <span data-slot=\"presence-label\" className={cn(!showLabel && 'sr-only')}>\n {label ?? DEFAULT_PRESENCE_LABELS[state]}\n </span>\n </span>\n );\n};\n"],"mappings":";;;;AAIA,IAAa,kBAAkB;CAAC;CAAa;CAAQ;CAAQ;CAAiB;CAAW;AAAS;;AAKlG,IAAa,0BAAoE;CAC/E,WAAW;CACX,MAAM;CACN,MAAM;CACN,iBAAiB;CACjB,SAAS;CACT,SAAS;AACX;;;;;;;;AASA,IAAM,MAAgD;CACpD,WAAW;CACX,MAAM;CACN,MAAM;CACN,iBACE;CACF,SAAS;CACT,SAAS;AACX;;AAWA,IAAa,YAAY,EAAE,OAAO,YAAY,OAAO,OAAO,WAAW,GAAG,YAA4B;CACpG,OACE,qBAAC,QAAD;EAAM,aAAU;EAAW,cAAY;EAAO,WAAW,GAAG,4CAA4C,SAAS;EAAG,GAAI;EAAxH,UAAA,CACE,oBAAC,QAAD;GAAM,eAAY;GAAO,aAAU;GAAe,WAAW,GAAG,2CAA2C,IAAI,MAAM;EAAI,CAAA,GACzH,oBAAC,QAAD;GAAM,aAAU;GAAiB,WAAW,GAAG,CAAC,aAAa,SAAS;GACnE,UAAA,SAAS,wBAAwB;EAC9B,CAAA,CACF;;AAEV"}
@@ -0,0 +1 @@
1
+ export * from './time-field';
@@ -0,0 +1,85 @@
1
+ import { ComponentPropsWithoutRef } from 'react';
2
+ export type TTimeFieldGranularity = 'minute' | 'second';
3
+ export type TTimeFieldHourCycle = 12 | 24;
4
+ export type TTimeFieldSegment = 'hour' | 'minute' | 'second' | 'period';
5
+ export type TTimeFieldPeriod = 'AM' | 'PM';
6
+ /** The segments as they are shown: the hour in the field's own cycle (0 to 23, or 1 to 12 beside a period), each empty until entered. */
7
+ export interface ITimeParts {
8
+ hour: number | null;
9
+ minute: number | null;
10
+ second: number | null;
11
+ period: TTimeFieldPeriod | null;
12
+ }
13
+ export declare const EMPTY_TIME: ITimeParts;
14
+ export interface ITimeFieldLabels {
15
+ hour: string;
16
+ minute: string;
17
+ second: string;
18
+ /** The AM/PM segment. */
19
+ period: string;
20
+ /** Read for a segment with nothing in it. */
21
+ empty: string;
22
+ }
23
+ export declare const DEFAULT_TIME_FIELD_LABELS: ITimeFieldLabels;
24
+ /** 12 where the reader's locale writes AM and PM, 24 where it does not. */
25
+ export declare const localeHourCycle: () => TTimeFieldHourCycle;
26
+ /** `HH:mm` or `HH:mm:ss` (24-hour, as `<input type="time">` writes it) to the segments; anything else is no time. */
27
+ export declare const parseTime: (value: string, hourCycle: TTimeFieldHourCycle) => ITimeParts;
28
+ /** The segments to `HH:mm` or `HH:mm:ss`, or '' until every segment the field shows is in. */
29
+ export declare const formatTime: (parts: ITimeParts, hourCycle: TTimeFieldHourCycle, granularity: TTimeFieldGranularity) => string;
30
+ export interface IRange {
31
+ min: number;
32
+ max: number;
33
+ }
34
+ export declare const rangeOf: (segment: Exclude<TTimeFieldSegment, "period">, hourCycle: TTimeFieldHourCycle) => IRange;
35
+ /** One step up or down inside a range, wrapping at the ends. A segment with nothing in it starts at the end the step is heading away from. */
36
+ export declare const stepValue: (current: number | null, delta: number, { min, max }: IRange) => number;
37
+ /**
38
+ * Types a digit into a segment. The second digit joins the first when it makes a number the segment can hold; otherwise it starts
39
+ * over. The segment is done, and focus moves on, at two digits or as soon as no second digit could fit (a 3 in the hours, a 6 in the
40
+ * minutes).
41
+ */
42
+ export declare const typeDigit: (buffer: string, digit: string, { min, max }: IRange) => {
43
+ value: number;
44
+ buffer: string;
45
+ done: boolean;
46
+ };
47
+ export interface IEdit {
48
+ parts: ITimeParts;
49
+ /** The digits typed so far into this segment and not yet complete. */
50
+ buffer: string;
51
+ /** Move to the next segment: this one is finished. */
52
+ advance: boolean;
53
+ }
54
+ /** What a key does to a segment, or null when the key is not one the segment handles (Tab, a letter in the hours...). */
55
+ export declare const editSegment: (parts: ITimeParts, segment: TTimeFieldSegment, key: string, hourCycle: TTimeFieldHourCycle, buffer: string) => IEdit | null;
56
+ export interface ITimeFieldProps extends Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onChange' | 'children' | 'role'> {
57
+ /** The time as `HH:mm` or `HH:mm:ss`, 24-hour whatever the field shows, as `<input type="time">` writes it. '' is no time. */
58
+ value?: string;
59
+ defaultValue?: string;
60
+ /** Called with the time as soon as every segment is in, and with '' when one of a full time is cleared. */
61
+ onValueChange?: (value: string) => void;
62
+ /** `second` adds a seconds segment and a `HH:mm:ss` value. */
63
+ granularity?: TTimeFieldGranularity;
64
+ /** 12 shows an AM/PM segment, 24 does not. The default is what the reader's locale writes. */
65
+ hourCycle?: TTimeFieldHourCycle;
66
+ /** The earliest and the latest time, `HH:mm`. A time outside them is marked invalid; it is not refused. */
67
+ min?: string;
68
+ max?: string;
69
+ disabled?: boolean;
70
+ /** Shown, focusable and readable, not changeable. */
71
+ readOnly?: boolean;
72
+ invalid?: boolean;
73
+ /** Submits the time with a form, under this name, as `HH:mm` or `HH:mm:ss`. */
74
+ name?: string;
75
+ labels?: Partial<ITimeFieldLabels>;
76
+ }
77
+ /**
78
+ * A time of day typed in segments, in the kit's field: hours, minutes, optional seconds, and AM/PM where the reader's locale uses it.
79
+ * It exists so an app does not fall back to `<input type="time">`, whose look differs between browsers and from the rest of the kit.
80
+ *
81
+ * Each segment is a spinbutton: type digits (the focus moves on by itself), ArrowUp and ArrowDown step with wrap-around, PageUp and
82
+ * PageDown step by more, Home and End jump to the ends, Backspace empties, ArrowLeft and ArrowRight move between segments, and A or P
83
+ * set the period. The group is named by `aria-label` or `aria-labelledby`; a screen reader reads each segment by its own name and value.
84
+ */
85
+ export declare const TimeField: ({ value, defaultValue, onValueChange, granularity, hourCycle: hourCycleProp, min, max, disabled, readOnly, invalid, name, labels: labelOverrides, className, ...rest }: ITimeFieldProps) => import('react').JSX.Element;
@@ -0,0 +1,330 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { InputGroup, InputGroupAddon } from "../../ui/input-group/input-group.js";
3
+ import { useMemo, useRef, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { IconClock } from "@tabler/icons-react";
6
+ //#region ../ui/src/components/edyn/time-field/time-field.tsx
7
+ var EMPTY_TIME = {
8
+ hour: null,
9
+ minute: null,
10
+ second: null,
11
+ period: null
12
+ };
13
+ var DEFAULT_TIME_FIELD_LABELS = {
14
+ hour: "Hours",
15
+ minute: "Minutes",
16
+ second: "Seconds",
17
+ period: "AM or PM",
18
+ empty: "Empty"
19
+ };
20
+ var TIME = /^(\d{2}):(\d{2})(?::(\d{2}))?$/;
21
+ var two = (value) => String(value).padStart(2, "0");
22
+ /** 12 where the reader's locale writes AM and PM, 24 where it does not. */
23
+ var localeHourCycle = () => new Intl.DateTimeFormat(void 0, { hour: "numeric" }).resolvedOptions().hour12 ? 12 : 24;
24
+ /** `HH:mm` or `HH:mm:ss` (24-hour, as `<input type="time">` writes it) to the segments; anything else is no time. */
25
+ var parseTime = (value, hourCycle) => {
26
+ const match = TIME.exec(value);
27
+ if (match === null) return EMPTY_TIME;
28
+ const hour24 = Number(match[1]);
29
+ const minute = Number(match[2]);
30
+ const second = match[3] === void 0 ? 0 : Number(match[3]);
31
+ if (hour24 > 23 || minute > 59 || second > 59) return EMPTY_TIME;
32
+ if (hourCycle === 24) return {
33
+ hour: hour24,
34
+ minute,
35
+ second,
36
+ period: null
37
+ };
38
+ return {
39
+ hour: hour24 % 12 === 0 ? 12 : hour24 % 12,
40
+ minute,
41
+ second,
42
+ period: hour24 >= 12 ? "PM" : "AM"
43
+ };
44
+ };
45
+ /** The segments to `HH:mm` or `HH:mm:ss`, or '' until every segment the field shows is in. */
46
+ var formatTime = (parts, hourCycle, granularity) => {
47
+ const { hour, minute, second, period } = parts;
48
+ if (hour === null || minute === null || granularity === "second" && second === null) return "";
49
+ let hour24 = hour;
50
+ if (hourCycle === 12) {
51
+ if (period === null || hour === 0) return "";
52
+ hour24 = hour % 12 + (period === "PM" ? 12 : 0);
53
+ }
54
+ return granularity === "second" ? `${two(hour24)}:${two(minute)}:${two(second ?? 0)}` : `${two(hour24)}:${two(minute)}`;
55
+ };
56
+ var rangeOf = (segment, hourCycle) => {
57
+ if (segment === "hour") return hourCycle === 12 ? {
58
+ min: 1,
59
+ max: 12
60
+ } : {
61
+ min: 0,
62
+ max: 23
63
+ };
64
+ return {
65
+ min: 0,
66
+ max: 59
67
+ };
68
+ };
69
+ /** One step up or down inside a range, wrapping at the ends. A segment with nothing in it starts at the end the step is heading away from. */
70
+ var stepValue = (current, delta, { min, max }) => {
71
+ if (current === null) return delta > 0 ? min : max;
72
+ const size = max - min + 1;
73
+ return ((current - min + delta) % size + size) % size + min;
74
+ };
75
+ /**
76
+ * Types a digit into a segment. The second digit joins the first when it makes a number the segment can hold; otherwise it starts
77
+ * over. The segment is done, and focus moves on, at two digits or as soon as no second digit could fit (a 3 in the hours, a 6 in the
78
+ * minutes).
79
+ */
80
+ var typeDigit = (buffer, digit, { min, max }) => {
81
+ let text = buffer + digit;
82
+ if (text.length > 2 || Number(text) > max) text = digit;
83
+ const value = Number(text);
84
+ const done = text.length === 2 || value * 10 > max;
85
+ return {
86
+ value: done && value < min ? min : value,
87
+ buffer: done ? "" : text,
88
+ done
89
+ };
90
+ };
91
+ var PAGE_STEP = {
92
+ hour: 3,
93
+ minute: 10,
94
+ second: 10
95
+ };
96
+ var withSegment = (parts, segment, value) => ({
97
+ ...parts,
98
+ [segment]: value
99
+ });
100
+ /** What a key does to a segment, or null when the key is not one the segment handles (Tab, a letter in the hours...). */
101
+ var editSegment = (parts, segment, key, hourCycle, buffer) => {
102
+ if (segment === "period") {
103
+ const current = parts.period;
104
+ const set = (period) => ({
105
+ parts: {
106
+ ...parts,
107
+ period
108
+ },
109
+ buffer: "",
110
+ advance: false
111
+ });
112
+ switch (key) {
113
+ case "a":
114
+ case "A": return {
115
+ ...set("AM"),
116
+ advance: true
117
+ };
118
+ case "p":
119
+ case "P": return {
120
+ ...set("PM"),
121
+ advance: true
122
+ };
123
+ case "ArrowUp":
124
+ case "ArrowDown": return set(current === null ? key === "ArrowUp" ? "AM" : "PM" : current === "AM" ? "PM" : "AM");
125
+ case "Home": return set("AM");
126
+ case "End": return set("PM");
127
+ case "Backspace":
128
+ case "Delete": return set(null);
129
+ default: return null;
130
+ }
131
+ }
132
+ const range = rangeOf(segment, hourCycle);
133
+ const current = parts[segment];
134
+ const set = (value, advance = false) => ({
135
+ parts: withSegment(parts, segment, value),
136
+ buffer: "",
137
+ advance
138
+ });
139
+ if (/^[0-9]$/.test(key)) {
140
+ const typed = typeDigit(buffer, key, range);
141
+ return {
142
+ parts: withSegment(parts, segment, typed.value),
143
+ buffer: typed.buffer,
144
+ advance: typed.done
145
+ };
146
+ }
147
+ switch (key) {
148
+ case "ArrowUp": return set(stepValue(current, 1, range));
149
+ case "ArrowDown": return set(stepValue(current, -1, range));
150
+ case "PageUp": return set(stepValue(current, PAGE_STEP[segment], range));
151
+ case "PageDown": return set(stepValue(current, -PAGE_STEP[segment], range));
152
+ case "Home": return set(range.min);
153
+ case "End": return set(range.max);
154
+ case "Backspace":
155
+ case "Delete": return set(null);
156
+ default: return null;
157
+ }
158
+ };
159
+ var SEGMENT_CLASS = "h-6 rounded-sm bg-transparent text-center text-sm tabular-nums caret-transparent outline-none selection:bg-transparent placeholder:text-muted-foreground focus:bg-foreground/10 disabled:cursor-not-allowed";
160
+ /**
161
+ * A time of day typed in segments, in the kit's field: hours, minutes, optional seconds, and AM/PM where the reader's locale uses it.
162
+ * It exists so an app does not fall back to `<input type="time">`, whose look differs between browsers and from the rest of the kit.
163
+ *
164
+ * Each segment is a spinbutton: type digits (the focus moves on by itself), ArrowUp and ArrowDown step with wrap-around, PageUp and
165
+ * PageDown step by more, Home and End jump to the ends, Backspace empties, ArrowLeft and ArrowRight move between segments, and A or P
166
+ * set the period. The group is named by `aria-label` or `aria-labelledby`; a screen reader reads each segment by its own name and value.
167
+ */
168
+ var TimeField = ({ value, defaultValue = "", onValueChange, granularity = "minute", hourCycle: hourCycleProp, min, max, disabled = false, readOnly = false, invalid = false, name, labels: labelOverrides, className, ...rest }) => {
169
+ const labels = useMemo(() => ({
170
+ ...DEFAULT_TIME_FIELD_LABELS,
171
+ ...labelOverrides
172
+ }), [labelOverrides]);
173
+ const hourCycle = useMemo(() => hourCycleProp ?? localeHourCycle(), [hourCycleProp]);
174
+ const [parts, setParts] = useState(() => parseTime(value ?? defaultValue, hourCycle));
175
+ const [seen, setSeen] = useState(value);
176
+ const refSegments = useRef([]);
177
+ const refBuffer = useRef({
178
+ segment: "hour",
179
+ text: ""
180
+ });
181
+ const formatted = formatTime(parts, hourCycle, granularity);
182
+ const refReported = useRef(formatted);
183
+ const refParts = useRef(parts);
184
+ refParts.current = parts;
185
+ if (value !== seen) {
186
+ setSeen(value);
187
+ if (value !== void 0 && value !== formatted) {
188
+ setParts(parseTime(value, hourCycle));
189
+ refReported.current = value;
190
+ }
191
+ }
192
+ const segments = [
193
+ "hour",
194
+ "minute",
195
+ ...granularity === "second" ? ["second"] : [],
196
+ ...hourCycle === 12 ? ["period"] : []
197
+ ];
198
+ const focusSegment = (index) => {
199
+ const target = refSegments.current[index];
200
+ if (target !== null && target !== void 0) {
201
+ target.focus();
202
+ target.select();
203
+ }
204
+ };
205
+ const report = (next) => {
206
+ const text = formatTime(next, hourCycle, granularity);
207
+ if (text !== refReported.current) {
208
+ refReported.current = text;
209
+ onValueChange?.(text);
210
+ }
211
+ };
212
+ const edit = (segment, key) => {
213
+ const buffer = refBuffer.current.segment === segment ? refBuffer.current.text : "";
214
+ const result = editSegment(refParts.current, segment, key, hourCycle, buffer);
215
+ if (result === null) return false;
216
+ refBuffer.current = {
217
+ segment,
218
+ text: result.buffer
219
+ };
220
+ refParts.current = result.parts;
221
+ setParts(result.parts);
222
+ if (result.buffer === "") report(result.parts);
223
+ if (result.advance) focusSegment(segments.indexOf(segment) + 1);
224
+ return true;
225
+ };
226
+ const onKeyDown = (segment, event) => {
227
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
228
+ const index = segments.indexOf(segment);
229
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
230
+ event.preventDefault();
231
+ focusSegment(index + (event.key === "ArrowRight" ? 1 : -1));
232
+ return;
233
+ }
234
+ if (disabled || readOnly) return;
235
+ if (edit(segment, event.key)) event.preventDefault();
236
+ };
237
+ const onBlur = (segment) => {
238
+ const unfinished = refBuffer.current.segment === segment && refBuffer.current.text !== "";
239
+ refBuffer.current = {
240
+ segment,
241
+ text: ""
242
+ };
243
+ let settled = refParts.current;
244
+ if (segment === "hour" && hourCycle === 12 && settled.hour === 0) {
245
+ settled = {
246
+ ...settled,
247
+ hour: null
248
+ };
249
+ refParts.current = settled;
250
+ setParts(settled);
251
+ }
252
+ if (unfinished) report(settled);
253
+ };
254
+ const onChange = (segment, event) => {
255
+ if (disabled || readOnly) return;
256
+ const typed = event.target.value.slice(-1);
257
+ edit(segment, typed);
258
+ };
259
+ const outOfRange = formatted !== "" && (min !== void 0 && formatted.slice(0, min.length) < min || max !== void 0 && formatted.slice(0, max.length) > max);
260
+ const flagged = invalid || outOfRange;
261
+ const display = (segment) => {
262
+ if (segment === "period") return parts.period ?? "";
263
+ const current = parts[segment];
264
+ return current === null ? "" : two(current);
265
+ };
266
+ return /* @__PURE__ */ jsxs(InputGroup, {
267
+ role: "group",
268
+ className: cn("w-fit", className),
269
+ ...rest,
270
+ children: [
271
+ /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(IconClock, { "aria-hidden": "true" }) }),
272
+ /* @__PURE__ */ jsx("div", {
273
+ className: "flex items-center gap-0.5 pr-2",
274
+ children: segments.map((segment, index) => {
275
+ const numeric = segment !== "period";
276
+ const range = numeric ? rangeOf(segment, hourCycle) : {
277
+ min: 0,
278
+ max: 1
279
+ };
280
+ const current = numeric ? parts[segment] : parts.period === null ? null : parts.period === "AM" ? 0 : 1;
281
+ return /* @__PURE__ */ jsxs("span", {
282
+ className: "flex items-center gap-0.5",
283
+ children: [index > 0 && segment !== "period" && /* @__PURE__ */ jsx("span", {
284
+ "aria-hidden": "true",
285
+ className: "text-muted-foreground",
286
+ children: ":"
287
+ }), /* @__PURE__ */ jsx("input", {
288
+ ref: (node) => {
289
+ refSegments.current[index] = node;
290
+ },
291
+ "data-slot": "input-group-control",
292
+ "data-segment": segment,
293
+ role: "spinbutton",
294
+ inputMode: numeric ? "numeric" : "text",
295
+ autoComplete: "off",
296
+ autoCorrect: "off",
297
+ spellCheck: false,
298
+ value: display(segment),
299
+ placeholder: "––",
300
+ disabled,
301
+ readOnly,
302
+ "aria-label": labels[segment],
303
+ "aria-readonly": readOnly || void 0,
304
+ "aria-invalid": flagged || void 0,
305
+ "aria-valuemin": range.min,
306
+ "aria-valuemax": range.max,
307
+ "aria-valuenow": current ?? void 0,
308
+ "aria-valuetext": current === null ? labels.empty : segment === "period" ? parts.period ?? labels.empty : two(current),
309
+ className: cn(SEGMENT_CLASS, segment === "period" ? "w-7" : "w-5"),
310
+ onKeyDown: (event) => onKeyDown(segment, event),
311
+ onChange: (event) => onChange(segment, event),
312
+ onFocus: (event) => event.currentTarget.select(),
313
+ onBlur: () => onBlur(segment)
314
+ })]
315
+ }, segment);
316
+ })
317
+ }),
318
+ name !== void 0 && /* @__PURE__ */ jsx("input", {
319
+ type: "hidden",
320
+ name,
321
+ value: formatted,
322
+ disabled
323
+ })
324
+ ]
325
+ });
326
+ };
327
+ //#endregion
328
+ export { DEFAULT_TIME_FIELD_LABELS, EMPTY_TIME, TimeField, editSegment, formatTime, localeHourCycle, parseTime, rangeOf, stepValue, typeDigit };
329
+
330
+ //# sourceMappingURL=time-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-field.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/time-field/time-field.tsx"],"sourcesContent":["import { InputGroup, InputGroupAddon } from '@edyn/ui/components/ui/input-group';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconClock } from '@tabler/icons-react';\nimport { type ChangeEvent, type ComponentPropsWithoutRef, type KeyboardEvent, useMemo, useRef, useState } from 'react';\n\nexport type TTimeFieldGranularity = 'minute' | 'second';\n\nexport type TTimeFieldHourCycle = 12 | 24;\n\nexport type TTimeFieldSegment = 'hour' | 'minute' | 'second' | 'period';\n\nexport type TTimeFieldPeriod = 'AM' | 'PM';\n\n/** The segments as they are shown: the hour in the field's own cycle (0 to 23, or 1 to 12 beside a period), each empty until entered. */\nexport interface ITimeParts {\n hour: number | null;\n minute: number | null;\n second: number | null;\n period: TTimeFieldPeriod | null;\n}\n\nexport const EMPTY_TIME: ITimeParts = { hour: null, minute: null, second: null, period: null };\n\nexport interface ITimeFieldLabels {\n hour: string;\n minute: string;\n second: string;\n /** The AM/PM segment. */\n period: string;\n /** Read for a segment with nothing in it. */\n empty: string;\n}\n\nexport const DEFAULT_TIME_FIELD_LABELS: ITimeFieldLabels = {\n hour: 'Hours',\n minute: 'Minutes',\n second: 'Seconds',\n period: 'AM or PM',\n empty: 'Empty',\n};\n\nconst TIME = /^(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\n\nconst two = (value: number): string => String(value).padStart(2, '0');\n\n/** 12 where the reader's locale writes AM and PM, 24 where it does not. */\nexport const localeHourCycle = (): TTimeFieldHourCycle =>\n new Intl.DateTimeFormat(undefined, { hour: 'numeric' }).resolvedOptions().hour12 ? 12 : 24;\n\n/** `HH:mm` or `HH:mm:ss` (24-hour, as `<input type=\"time\">` writes it) to the segments; anything else is no time. */\nexport const parseTime = (value: string, hourCycle: TTimeFieldHourCycle): ITimeParts => {\n const match = TIME.exec(value);\n if (match === null) {\n return EMPTY_TIME;\n }\n const hour24 = Number(match[1]);\n const minute = Number(match[2]);\n const second = match[3] === undefined ? 0 : Number(match[3]);\n if (hour24 > 23 || minute > 59 || second > 59) {\n return EMPTY_TIME;\n }\n if (hourCycle === 24) {\n return { hour: hour24, minute, second, period: null };\n }\n return { hour: hour24 % 12 === 0 ? 12 : hour24 % 12, minute, second, period: hour24 >= 12 ? 'PM' : 'AM' };\n};\n\n/** The segments to `HH:mm` or `HH:mm:ss`, or '' until every segment the field shows is in. */\nexport const formatTime = (parts: ITimeParts, hourCycle: TTimeFieldHourCycle, granularity: TTimeFieldGranularity): string => {\n const { hour, minute, second, period } = parts;\n if (hour === null || minute === null || (granularity === 'second' && second === null)) {\n return '';\n }\n let hour24 = hour;\n if (hourCycle === 12) {\n // 0 is only ever a first digit still waiting for its second.\n if (period === null || hour === 0) {\n return '';\n }\n hour24 = (hour % 12) + (period === 'PM' ? 12 : 0);\n }\n return granularity === 'second' ? `${two(hour24)}:${two(minute)}:${two(second ?? 0)}` : `${two(hour24)}:${two(minute)}`;\n};\n\nexport interface IRange {\n min: number;\n max: number;\n}\n\nexport const rangeOf = (segment: Exclude<TTimeFieldSegment, 'period'>, hourCycle: TTimeFieldHourCycle): IRange => {\n if (segment === 'hour') {\n return hourCycle === 12 ? { min: 1, max: 12 } : { min: 0, max: 23 };\n }\n return { min: 0, max: 59 };\n};\n\n/** One step up or down inside a range, wrapping at the ends. A segment with nothing in it starts at the end the step is heading away from. */\nexport const stepValue = (current: number | null, delta: number, { min, max }: IRange): number => {\n if (current === null) {\n return delta > 0 ? min : max;\n }\n const size = max - min + 1;\n return ((((current - min + delta) % size) + size) % size) + min;\n};\n\n/**\n * Types a digit into a segment. The second digit joins the first when it makes a number the segment can hold; otherwise it starts\n * over. The segment is done, and focus moves on, at two digits or as soon as no second digit could fit (a 3 in the hours, a 6 in the\n * minutes).\n */\nexport const typeDigit = (buffer: string, digit: string, { min, max }: IRange): { value: number; buffer: string; done: boolean } => {\n let text = buffer + digit;\n if (text.length > 2 || Number(text) > max) {\n text = digit;\n }\n const value = Number(text);\n const done = text.length === 2 || value * 10 > max;\n return { value: done && value < min ? min : value, buffer: done ? '' : text, done };\n};\n\nconst PAGE_STEP: Record<Exclude<TTimeFieldSegment, 'period'>, number> = { hour: 3, minute: 10, second: 10 };\n\nexport interface IEdit {\n parts: ITimeParts;\n /** The digits typed so far into this segment and not yet complete. */\n buffer: string;\n /** Move to the next segment: this one is finished. */\n advance: boolean;\n}\n\nconst withSegment = (parts: ITimeParts, segment: Exclude<TTimeFieldSegment, 'period'>, value: number | null): ITimeParts => ({\n ...parts,\n [segment]: value,\n});\n\n/** What a key does to a segment, or null when the key is not one the segment handles (Tab, a letter in the hours...). */\nexport const editSegment = (\n parts: ITimeParts,\n segment: TTimeFieldSegment,\n key: string,\n hourCycle: TTimeFieldHourCycle,\n buffer: string,\n): IEdit | null => {\n if (segment === 'period') {\n const current = parts.period;\n const set = (period: TTimeFieldPeriod | null): IEdit => ({ parts: { ...parts, period }, buffer: '', advance: false });\n switch (key) {\n case 'a':\n case 'A':\n return { ...set('AM'), advance: true };\n case 'p':\n case 'P':\n return { ...set('PM'), advance: true };\n case 'ArrowUp':\n case 'ArrowDown':\n return set(current === null ? (key === 'ArrowUp' ? 'AM' : 'PM') : current === 'AM' ? 'PM' : 'AM');\n case 'Home':\n return set('AM');\n case 'End':\n return set('PM');\n case 'Backspace':\n case 'Delete':\n return set(null);\n default:\n return null;\n }\n }\n\n const range = rangeOf(segment, hourCycle);\n const current = parts[segment];\n const set = (value: number | null, advance = false): IEdit => ({ parts: withSegment(parts, segment, value), buffer: '', advance });\n\n if (/^[0-9]$/.test(key)) {\n const typed = typeDigit(buffer, key, range);\n return { parts: withSegment(parts, segment, typed.value), buffer: typed.buffer, advance: typed.done };\n }\n switch (key) {\n case 'ArrowUp':\n return set(stepValue(current, 1, range));\n case 'ArrowDown':\n return set(stepValue(current, -1, range));\n case 'PageUp':\n return set(stepValue(current, PAGE_STEP[segment], range));\n case 'PageDown':\n return set(stepValue(current, -PAGE_STEP[segment], range));\n case 'Home':\n return set(range.min);\n case 'End':\n return set(range.max);\n case 'Backspace':\n case 'Delete':\n return set(null);\n default:\n return null;\n }\n};\n\nexport interface ITimeFieldProps extends Omit<ComponentPropsWithoutRef<'div'>, 'defaultValue' | 'onChange' | 'children' | 'role'> {\n /** The time as `HH:mm` or `HH:mm:ss`, 24-hour whatever the field shows, as `<input type=\"time\">` writes it. '' is no time. */\n value?: string;\n defaultValue?: string;\n /** Called with the time as soon as every segment is in, and with '' when one of a full time is cleared. */\n onValueChange?: (value: string) => void;\n /** `second` adds a seconds segment and a `HH:mm:ss` value. */\n granularity?: TTimeFieldGranularity;\n /** 12 shows an AM/PM segment, 24 does not. The default is what the reader's locale writes. */\n hourCycle?: TTimeFieldHourCycle;\n /** The earliest and the latest time, `HH:mm`. A time outside them is marked invalid; it is not refused. */\n min?: string;\n max?: string;\n disabled?: boolean;\n /** Shown, focusable and readable, not changeable. */\n readOnly?: boolean;\n invalid?: boolean;\n /** Submits the time with a form, under this name, as `HH:mm` or `HH:mm:ss`. */\n name?: string;\n labels?: Partial<ITimeFieldLabels>;\n}\n\nconst SEGMENT_CLASS =\n 'h-6 rounded-sm bg-transparent text-center text-sm tabular-nums caret-transparent outline-none selection:bg-transparent placeholder:text-muted-foreground focus:bg-foreground/10 disabled:cursor-not-allowed';\n\n/**\n * A time of day typed in segments, in the kit's field: hours, minutes, optional seconds, and AM/PM where the reader's locale uses it.\n * It exists so an app does not fall back to `<input type=\"time\">`, whose look differs between browsers and from the rest of the kit.\n *\n * Each segment is a spinbutton: type digits (the focus moves on by itself), ArrowUp and ArrowDown step with wrap-around, PageUp and\n * PageDown step by more, Home and End jump to the ends, Backspace empties, ArrowLeft and ArrowRight move between segments, and A or P\n * set the period. The group is named by `aria-label` or `aria-labelledby`; a screen reader reads each segment by its own name and value.\n */\nexport const TimeField = ({\n value,\n defaultValue = '',\n onValueChange,\n granularity = 'minute',\n hourCycle: hourCycleProp,\n min,\n max,\n disabled = false,\n readOnly = false,\n invalid = false,\n name,\n labels: labelOverrides,\n className,\n ...rest\n}: ITimeFieldProps) => {\n const labels = useMemo(() => ({ ...DEFAULT_TIME_FIELD_LABELS, ...labelOverrides }), [labelOverrides]);\n const hourCycle = useMemo(() => hourCycleProp ?? localeHourCycle(), [hourCycleProp]);\n const [parts, setParts] = useState<ITimeParts>(() => parseTime(value ?? defaultValue, hourCycle));\n const [seen, setSeen] = useState(value);\n const refSegments = useRef<Array<HTMLInputElement | null>>([]);\n const refBuffer = useRef<{ segment: TTimeFieldSegment; text: string }>({ segment: 'hour', text: '' });\n const formatted = formatTime(parts, hourCycle, granularity);\n // What the parent was last told, and the segments as the last key left them (state is a render behind inside one event).\n const refReported = useRef(formatted);\n const refParts = useRef(parts);\n refParts.current = parts;\n // A time handed in from outside replaces what is shown, unless it is the very time that is shown: clearing one segment of a full\n // time reports '', and taking that back as a new value would wipe the segments still being edited.\n if (value !== seen) {\n setSeen(value);\n if (value !== undefined && value !== formatted) {\n setParts(parseTime(value, hourCycle));\n refReported.current = value;\n }\n }\n\n const segments: TTimeFieldSegment[] = [\n 'hour',\n 'minute',\n ...(granularity === 'second' ? (['second'] as const) : []),\n ...(hourCycle === 12 ? (['period'] as const) : []),\n ];\n\n const focusSegment = (index: number): void => {\n const target = refSegments.current[index];\n if (target !== null && target !== undefined) {\n target.focus();\n target.select();\n }\n };\n\n const report = (next: ITimeParts): void => {\n const text = formatTime(next, hourCycle, granularity);\n if (text !== refReported.current) {\n refReported.current = text;\n onValueChange?.(text);\n }\n };\n\n const edit = (segment: TTimeFieldSegment, key: string): boolean => {\n const buffer = refBuffer.current.segment === segment ? refBuffer.current.text : '';\n const result = editSegment(refParts.current, segment, key, hourCycle, buffer);\n if (result === null) {\n return false;\n }\n refBuffer.current = { segment, text: result.buffer };\n refParts.current = result.parts;\n setParts(result.parts);\n // A segment still waiting for its second digit is not a time yet worth reporting (a 3 may become 30); it is reported when it is\n // done, or when the focus leaves it.\n if (result.buffer === '') {\n report(result.parts);\n }\n if (result.advance) {\n focusSegment(segments.indexOf(segment) + 1);\n }\n return true;\n };\n\n const onKeyDown = (segment: TTimeFieldSegment, event: KeyboardEvent<HTMLInputElement>): void => {\n if (event.altKey || event.ctrlKey || event.metaKey) {\n return;\n }\n const index = segments.indexOf(segment);\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault();\n focusSegment(index + (event.key === 'ArrowRight' ? 1 : -1));\n return;\n }\n if (disabled || readOnly) {\n return;\n }\n if (edit(segment, event.key)) {\n event.preventDefault();\n }\n };\n\n // Leaving a segment ends what was being typed into it: coming back and typing a 2 must not make 12 of an old 1. A 0 left alone in a\n // 12-hour hour is the start of a number that never came, so it is emptied.\n const onBlur = (segment: TTimeFieldSegment): void => {\n const unfinished = refBuffer.current.segment === segment && refBuffer.current.text !== '';\n refBuffer.current = { segment, text: '' };\n let settled = refParts.current;\n if (segment === 'hour' && hourCycle === 12 && settled.hour === 0) {\n settled = { ...settled, hour: null };\n refParts.current = settled;\n setParts(settled);\n }\n if (unfinished) {\n report(settled);\n }\n };\n\n // A phone's keyboard does not send key events a page can use; what it types arrives as a change to the input.\n const onChange = (segment: TTimeFieldSegment, event: ChangeEvent<HTMLInputElement>): void => {\n if (disabled || readOnly) {\n return;\n }\n const typed = event.target.value.slice(-1);\n edit(segment, typed);\n };\n\n const outOfRange =\n formatted !== '' && ((min !== undefined && formatted.slice(0, min.length) < min) || (max !== undefined && formatted.slice(0, max.length) > max));\n const flagged = invalid || outOfRange;\n\n const display = (segment: TTimeFieldSegment): string => {\n if (segment === 'period') {\n return parts.period ?? '';\n }\n const current = parts[segment];\n return current === null ? '' : two(current);\n };\n\n return (\n <InputGroup role=\"group\" className={cn('w-fit', className)} {...rest}>\n <InputGroupAddon>\n <IconClock aria-hidden=\"true\" />\n </InputGroupAddon>\n <div className=\"flex items-center gap-0.5 pr-2\">\n {segments.map((segment, index) => {\n const numeric = segment !== 'period';\n const range = numeric ? rangeOf(segment, hourCycle) : { min: 0, max: 1 };\n const current = numeric ? parts[segment] : parts.period === null ? null : parts.period === 'AM' ? 0 : 1;\n return (\n <span key={segment} className=\"flex items-center gap-0.5\">\n {index > 0 && segment !== 'period' && (\n <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n :\n </span>\n )}\n <input\n ref={(node) => {\n refSegments.current[index] = node;\n }}\n data-slot=\"input-group-control\"\n data-segment={segment}\n role=\"spinbutton\"\n inputMode={numeric ? 'numeric' : 'text'}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n value={display(segment)}\n placeholder=\"––\"\n disabled={disabled}\n readOnly={readOnly}\n aria-label={labels[segment]}\n aria-readonly={readOnly || undefined}\n aria-invalid={flagged || undefined}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n aria-valuenow={current ?? undefined}\n aria-valuetext={current === null ? labels.empty : segment === 'period' ? (parts.period ?? labels.empty) : two(current)}\n className={cn(SEGMENT_CLASS, segment === 'period' ? 'w-7' : 'w-5')}\n onKeyDown={(event) => onKeyDown(segment, event)}\n onChange={(event) => onChange(segment, event)}\n onFocus={(event) => event.currentTarget.select()}\n onBlur={() => onBlur(segment)}\n />\n </span>\n );\n })}\n </div>\n {name !== undefined && <input type=\"hidden\" name={name} value={formatted} disabled={disabled} />}\n </InputGroup>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * TimeField: the arithmetic of a time typed in segments, and the keyboard on a mounted field.
3
+ *
4
+ * PATH TAKEN: parsing, formatting, stepping and the typing rules are plain functions and run in happy-dom, and so does a mounted field
5
+ * driven by key events, which is where the focus moving on, the value reported and the roles are checked. How it looks in the kit's
6
+ * field, and the browser's own handling of focus, is for e2e/a11y.spec.ts and the visual cells.
7
+ */
8
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A day button shows its number and is named with the date: the number has to be a word of that name (WCAG 2.5.3, Label in Name).
3
+ *
4
+ * PATH TAKEN: the names are text react-day-picker builds from a date and a locale, so happy-dom is enough. axe 4.14 and later reads
5
+ * a name word by word ("30" is not found in "30th"), and the version in this repository reads it as a string, so the rule is
6
+ * written out here (the same words, split the same way) and holds whichever axe runs. e2e/label-in-name.spec.ts does the same in a
7
+ * browser, over the pickers.
8
+ */
9
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { DayButton, DayPicker, Locale } from 'react-day-picker';
2
2
  import { Button } from '../button';
3
3
  import * as React from 'react';
4
- export declare const Calendar: ({ className, classNames, showOutsideDays, captionLayout, buttonVariant, locale, formatters, components, ...props }: React.ComponentProps<typeof DayPicker> & {
4
+ export declare const Calendar: ({ className, classNames, showOutsideDays, captionLayout, buttonVariant, locale, formatters, labels, components, ...props }: React.ComponentProps<typeof DayPicker> & {
5
5
  buttonVariant?: React.ComponentProps<typeof Button>["variant"];
6
6
  }) => React.JSX.Element;
7
7
  export declare const CalendarDayButton: ({ className, day, modifiers, locale, ...props }: React.ComponentProps<typeof DayButton> & {
@@ -3,15 +3,46 @@ import { Button, buttonVariants } from "../button/button.js";
3
3
  import * as React from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { IconChevronDown, IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
6
- import { DayPicker, getDefaultClassNames } from "react-day-picker";
6
+ import { DateLib, DayPicker, getDefaultClassNames } from "react-day-picker";
7
7
  //#region ../ui/src/components/ui/calendar/calendar.tsx
8
- var Calendar = ({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", locale, formatters, components, ...props }) => {
8
+ /** Whether `day` stands in `label` as a word of its own, not as the start of "30th" or "30일" (axe 4.14 and later reads the name word by word). */
9
+ var hasWord = (label, day) => new RegExp(`(^|[^\\p{L}\\p{N}])${day}($|[^\\p{L}\\p{N}])`, "u").test(label);
10
+ /**
11
+ * A day button's accessible name. A button that shows "30" has to be named with that 30 as a word of its own (WCAG 2.5.3, Label in
12
+ * Name): axe 4.14 and later (label-content-name-mismatch) reads the name word by word, and react-day-picker's "Monday, November 30th,
13
+ * 2026" has no word "30", only "30th".
14
+ *
15
+ * The name a locale gives (react-day-picker/locale, English included) is kept when the day is a word in it ("Montag, 30. November
16
+ * 2026"), loses an English ordinal ("Monday, November 30, 2026") and has the day put in front when it is glued to a unit
17
+ * ("30, 2026년 11월 30일 월요일"). A locale with no names of its own is written "Monday, 30 November 2026" in its words, with
18
+ * react-day-picker's English "Today" and "selected"; a product that translates those passes `labels`, which wins.
19
+ */
20
+ var labelDayButton = (date, modifiers, options, dateLib) => {
21
+ const lib = dateLib ?? new DateLib(options);
22
+ const day = lib.format(date, "d");
23
+ const own = lib.options.locale?.labels?.labelDayButton;
24
+ if (own !== void 0) {
25
+ const label = typeof own === "string" ? own : own(date, modifiers, options, lib);
26
+ if (hasWord(label, day)) return label;
27
+ const plain = label.replace(new RegExp(`(?<![\\p{L}\\p{N}])${day}(st|nd|rd|th)(?![\\p{L}\\p{N}])`, "u"), day);
28
+ return hasWord(plain, day) ? plain : `${day}, ${label}`;
29
+ }
30
+ let label = lib.format(date, "EEEE, d MMMM yyyy");
31
+ if (modifiers.today) label = `Today, ${label}`;
32
+ if (modifiers.selected) label = `${label}, selected`;
33
+ return label;
34
+ };
35
+ var Calendar = ({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", locale, formatters, labels, components, ...props }) => {
9
36
  const defaultClassNames = getDefaultClassNames();
10
37
  return /* @__PURE__ */ jsx(DayPicker, {
11
38
  showOutsideDays,
12
39
  className: cn("group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className),
13
40
  captionLayout,
14
41
  locale,
42
+ labels: {
43
+ labelDayButton,
44
+ ...labels
45
+ },
15
46
  formatters: {
16
47
  formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: "short" }),
17
48
  ...formatters
@@ -1 +1 @@
1
- {"version":3,"file":"calendar.js","names":[],"sources":["../../../../../../../ui/src/components/ui/calendar/calendar.tsx"],"sourcesContent":["import { cn } from '@edyn/ui/lib/utils';\nimport { IconChevronDown, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';\nimport * as React from 'react';\nimport { type DayButton, DayPicker, getDefaultClassNames, type Locale } from 'react-day-picker';\nimport { Button, buttonVariants } from '../button';\n\nexport const Calendar = ({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = 'label',\n buttonVariant = 'ghost',\n locale,\n formatters,\n components,\n ...props\n}: React.ComponentProps<typeof DayPicker> & {\n buttonVariant?: React.ComponentProps<typeof Button>['variant'];\n}) => {\n const defaultClassNames = getDefaultClassNames();\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn(\n 'group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className,\n )}\n captionLayout={captionLayout}\n locale={locale}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: 'short' }),\n ...formatters,\n }}\n classNames={{\n root: cn('w-fit', defaultClassNames.root),\n months: cn('relative flex flex-col gap-4 md:flex-row', defaultClassNames.months),\n month: cn('flex w-full flex-col gap-4', defaultClassNames.month),\n nav: cn('absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\n defaultClassNames.button_previous,\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\n defaultClassNames.button_next,\n ),\n month_caption: cn('flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)', defaultClassNames.month_caption),\n dropdowns: cn('flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium', defaultClassNames.dropdowns),\n dropdown_root: cn('relative rounded-(--cell-radius)', defaultClassNames.dropdown_root),\n dropdown: cn('absolute inset-0 bg-popover opacity-0', defaultClassNames.dropdown),\n caption_label: cn(\n 'font-medium select-none',\n captionLayout === 'label'\n ? 'text-sm'\n : 'flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground',\n defaultClassNames.caption_label,\n ),\n month_grid: cn('w-full border-collapse', defaultClassNames.month_grid),\n weekdays: cn('flex', defaultClassNames.weekdays),\n weekday: cn('flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none', defaultClassNames.weekday),\n week: cn('mt-2 flex w-full', defaultClassNames.week),\n week_number_header: cn('w-(--cell-size) select-none', defaultClassNames.week_number_header),\n week_number: cn('text-[0.8rem] text-muted-foreground select-none', defaultClassNames.week_number),\n day: cn(\n 'group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)',\n props.showWeekNumber\n ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)'\n : '[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)',\n defaultClassNames.day,\n ),\n range_start: cn(\n 'relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted',\n defaultClassNames.range_start,\n ),\n range_middle: cn('rounded-none', defaultClassNames.range_middle),\n range_end: cn(\n 'relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted',\n defaultClassNames.range_end,\n ),\n today: cn('rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none', defaultClassNames.today),\n outside: cn('text-muted-foreground aria-selected:text-muted-foreground', defaultClassNames.outside),\n disabled: cn('text-muted-foreground opacity-50', defaultClassNames.disabled),\n hidden: cn('invisible', defaultClassNames.hidden),\n ...classNames,\n }}\n components={{\n Root: ({ className, rootRef, ...props }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(className)} {...props} />;\n },\n Chevron: ({ className, orientation, ...props }) => {\n if (orientation === 'left') {\n return <IconChevronLeft className={cn('size-4', className)} {...props} />;\n }\n\n if (orientation === 'right') {\n return <IconChevronRight className={cn('size-4', className)} {...props} />;\n }\n\n return <IconChevronDown className={cn('size-4', className)} {...props} />;\n },\n DayButton: ({ ...props }) => <CalendarDayButton locale={locale} {...props} />,\n WeekNumber: ({ children, ...props }) => {\n return (\n <td {...props}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components,\n }}\n {...props}\n />\n );\n};\n\nexport const CalendarDayButton = ({\n className,\n day,\n modifiers,\n locale,\n ...props\n}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) => {\n const defaultClassNames = getDefaultClassNames();\n\n const refButton = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) {\n refButton.current?.focus();\n }\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={refButton}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString(locale?.code)}\n data-selected-single={modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle}\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n 'relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70',\n defaultClassNames.day,\n className,\n )}\n {...props}\n />\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"calendar.js","names":[],"sources":["../../../../../../../ui/src/components/ui/calendar/calendar.tsx"],"sourcesContent":["import { cn } from '@edyn/ui/lib/utils';\nimport { IconChevronDown, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';\nimport * as React from 'react';\nimport { DateLib, type DayButton, DayPicker, getDefaultClassNames, type Locale, type labelDayButton as TLabelDayButton } from 'react-day-picker';\nimport { Button, buttonVariants } from '../button';\n\n/** Whether `day` stands in `label` as a word of its own, not as the start of \"30th\" or \"30일\" (axe 4.14 and later reads the name word by word). */\nconst hasWord = (label: string, day: string): boolean => new RegExp(`(^|[^\\\\p{L}\\\\p{N}])${day}($|[^\\\\p{L}\\\\p{N}])`, 'u').test(label);\n\n/**\n * A day button's accessible name. A button that shows \"30\" has to be named with that 30 as a word of its own (WCAG 2.5.3, Label in\n * Name): axe 4.14 and later (label-content-name-mismatch) reads the name word by word, and react-day-picker's \"Monday, November 30th,\n * 2026\" has no word \"30\", only \"30th\".\n *\n * The name a locale gives (react-day-picker/locale, English included) is kept when the day is a word in it (\"Montag, 30. November\n * 2026\"), loses an English ordinal (\"Monday, November 30, 2026\") and has the day put in front when it is glued to a unit\n * (\"30, 2026년 11월 30일 월요일\"). A locale with no names of its own is written \"Monday, 30 November 2026\" in its words, with\n * react-day-picker's English \"Today\" and \"selected\"; a product that translates those passes `labels`, which wins.\n */\nconst labelDayButton: typeof TLabelDayButton = (date, modifiers, options, dateLib) => {\n const lib = dateLib ?? new DateLib(options);\n const day = lib.format(date, 'd');\n const own = lib.options.locale?.labels?.labelDayButton;\n if (own !== undefined) {\n const label = typeof own === 'string' ? own : own(date, modifiers, options, lib);\n if (hasWord(label, day)) {\n return label;\n }\n // English writes 30th, so \"30th\" becomes \"30\"; a unit glued to the day (30일) stays, and the day goes in front.\n const plain = label.replace(new RegExp(`(?<![\\\\p{L}\\\\p{N}])${day}(st|nd|rd|th)(?![\\\\p{L}\\\\p{N}])`, 'u'), day);\n return hasWord(plain, day) ? plain : `${day}, ${label}`;\n }\n let label = lib.format(date, 'EEEE, d MMMM yyyy');\n if (modifiers.today) {\n label = `Today, ${label}`;\n }\n if (modifiers.selected) {\n label = `${label}, selected`;\n }\n return label;\n};\n\nexport const Calendar = ({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = 'label',\n buttonVariant = 'ghost',\n locale,\n formatters,\n labels,\n components,\n ...props\n}: React.ComponentProps<typeof DayPicker> & {\n buttonVariant?: React.ComponentProps<typeof Button>['variant'];\n}) => {\n const defaultClassNames = getDefaultClassNames();\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn(\n 'group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className,\n )}\n captionLayout={captionLayout}\n locale={locale}\n labels={{ labelDayButton, ...labels }}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: 'short' }),\n ...formatters,\n }}\n classNames={{\n root: cn('w-fit', defaultClassNames.root),\n months: cn('relative flex flex-col gap-4 md:flex-row', defaultClassNames.months),\n month: cn('flex w-full flex-col gap-4', defaultClassNames.month),\n nav: cn('absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\n defaultClassNames.button_previous,\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\n defaultClassNames.button_next,\n ),\n month_caption: cn('flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)', defaultClassNames.month_caption),\n dropdowns: cn('flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium', defaultClassNames.dropdowns),\n dropdown_root: cn('relative rounded-(--cell-radius)', defaultClassNames.dropdown_root),\n dropdown: cn('absolute inset-0 bg-popover opacity-0', defaultClassNames.dropdown),\n caption_label: cn(\n 'font-medium select-none',\n captionLayout === 'label'\n ? 'text-sm'\n : 'flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground',\n defaultClassNames.caption_label,\n ),\n month_grid: cn('w-full border-collapse', defaultClassNames.month_grid),\n weekdays: cn('flex', defaultClassNames.weekdays),\n weekday: cn('flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none', defaultClassNames.weekday),\n week: cn('mt-2 flex w-full', defaultClassNames.week),\n week_number_header: cn('w-(--cell-size) select-none', defaultClassNames.week_number_header),\n week_number: cn('text-[0.8rem] text-muted-foreground select-none', defaultClassNames.week_number),\n day: cn(\n 'group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)',\n props.showWeekNumber\n ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)'\n : '[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)',\n defaultClassNames.day,\n ),\n range_start: cn(\n 'relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted',\n defaultClassNames.range_start,\n ),\n range_middle: cn('rounded-none', defaultClassNames.range_middle),\n range_end: cn(\n 'relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted',\n defaultClassNames.range_end,\n ),\n today: cn('rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none', defaultClassNames.today),\n outside: cn('text-muted-foreground aria-selected:text-muted-foreground', defaultClassNames.outside),\n disabled: cn('text-muted-foreground opacity-50', defaultClassNames.disabled),\n hidden: cn('invisible', defaultClassNames.hidden),\n ...classNames,\n }}\n components={{\n Root: ({ className, rootRef, ...props }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(className)} {...props} />;\n },\n Chevron: ({ className, orientation, ...props }) => {\n if (orientation === 'left') {\n return <IconChevronLeft className={cn('size-4', className)} {...props} />;\n }\n\n if (orientation === 'right') {\n return <IconChevronRight className={cn('size-4', className)} {...props} />;\n }\n\n return <IconChevronDown className={cn('size-4', className)} {...props} />;\n },\n DayButton: ({ ...props }) => <CalendarDayButton locale={locale} {...props} />,\n WeekNumber: ({ children, ...props }) => {\n return (\n <td {...props}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components,\n }}\n {...props}\n />\n );\n};\n\nexport const CalendarDayButton = ({\n className,\n day,\n modifiers,\n locale,\n ...props\n}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) => {\n const defaultClassNames = getDefaultClassNames();\n\n const refButton = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) {\n refButton.current?.focus();\n }\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={refButton}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString(locale?.code)}\n data-selected-single={modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle}\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n 'relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70',\n defaultClassNames.day,\n className,\n )}\n {...props}\n />\n );\n};\n"],"mappings":";;;;;;;;AAOA,IAAM,WAAW,OAAe,QAAyB,IAAI,OAAO,sBAAsB,IAAI,sBAAsB,GAAG,CAAC,CAAC,KAAK,KAAK;;;;;;;;;;;AAYnI,IAAM,kBAA0C,MAAM,WAAW,SAAS,YAAY;CACpF,MAAM,MAAM,WAAW,IAAI,QAAQ,OAAO;CAC1C,MAAM,MAAM,IAAI,OAAO,MAAM,GAAG;CAChC,MAAM,MAAM,IAAI,QAAQ,QAAQ,QAAQ;CACxC,IAAI,QAAQ,KAAA,GAAW;EACrB,MAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,IAAI,MAAM,WAAW,SAAS,GAAG;EAC/E,IAAI,QAAQ,OAAO,GAAG,GACpB,OAAO;EAGT,MAAM,QAAQ,MAAM,QAAQ,IAAI,OAAO,sBAAsB,IAAI,kCAAkC,GAAG,GAAG,GAAG;EAC5G,OAAO,QAAQ,OAAO,GAAG,IAAI,QAAQ,GAAG,IAAI,IAAI;CAClD;CACA,IAAI,QAAQ,IAAI,OAAO,MAAM,mBAAmB;CAChD,IAAI,UAAU,OACZ,QAAQ,UAAU;CAEpB,IAAI,UAAU,UACZ,QAAQ,GAAG,MAAM;CAEnB,OAAO;AACT;AAEA,IAAa,YAAY,EACvB,WACA,YACA,kBAAkB,MAClB,gBAAgB,SAChB,gBAAgB,SAChB,QACA,YACA,QACA,YACA,GAAG,YAGC;CACJ,MAAM,oBAAoB,qBAAqB;CAE/C,OACE,oBAAC,WAAD;EACmB;EACjB,WAAW,GACT,yLACA,OAAO,GAAG,6CACV,OAAO,GAAG,iDACV,SACF;EACe;EACP;EACR,QAAQ;GAAE;GAAgB,GAAG;EAAO;EACpC,YAAY;GACV,sBAAsB,SAAS,KAAK,eAAe,QAAQ,MAAM,EAAE,OAAO,QAAQ,CAAC;GACnF,GAAG;EACL;EACA,YAAY;GACV,MAAM,GAAG,SAAS,kBAAkB,IAAI;GACxC,QAAQ,GAAG,4CAA4C,kBAAkB,MAAM;GAC/E,OAAO,GAAG,8BAA8B,kBAAkB,KAAK;GAC/D,KAAK,GAAG,2EAA2E,kBAAkB,GAAG;GACxG,iBAAiB,GACf,eAAe,EAAE,SAAS,cAAc,CAAC,GACzC,+DACA,kBAAkB,eACpB;GACA,aAAa,GACX,eAAe,EAAE,SAAS,cAAc,CAAC,GACzC,+DACA,kBAAkB,WACpB;GACA,eAAe,GAAG,4EAA4E,kBAAkB,aAAa;GAC7H,WAAW,GAAG,uFAAuF,kBAAkB,SAAS;GAChI,eAAe,GAAG,oCAAoC,kBAAkB,aAAa;GACrF,UAAU,GAAG,yCAAyC,kBAAkB,QAAQ;GAChF,eAAe,GACb,2BACA,kBAAkB,UACd,YACA,0GACJ,kBAAkB,aACpB;GACA,YAAY,GAAG,0BAA0B,kBAAkB,UAAU;GACrE,UAAU,GAAG,QAAQ,kBAAkB,QAAQ;GAC/C,SAAS,GAAG,8FAA8F,kBAAkB,OAAO;GACnI,MAAM,GAAG,oBAAoB,kBAAkB,IAAI;GACnD,oBAAoB,GAAG,+BAA+B,kBAAkB,kBAAkB;GAC1F,aAAa,GAAG,mDAAmD,kBAAkB,WAAW;GAChG,KAAK,GACH,0KACA,MAAM,iBACF,0EACA,wEACJ,kBAAkB,GACpB;GACA,aAAa,GACX,iIACA,kBAAkB,WACpB;GACA,cAAc,GAAG,gBAAgB,kBAAkB,YAAY;GAC/D,WAAW,GACT,gIACA,kBAAkB,SACpB;GACA,OAAO,GAAG,sFAAsF,kBAAkB,KAAK;GACvH,SAAS,GAAG,6DAA6D,kBAAkB,OAAO;GAClG,UAAU,GAAG,oCAAoC,kBAAkB,QAAQ;GAC3E,QAAQ,GAAG,aAAa,kBAAkB,MAAM;GAChD,GAAG;EACL;EACA,YAAY;GACV,OAAO,EAAE,WAAW,SAAS,GAAG,YAAY;IAC1C,OAAO,oBAAC,OAAD;KAAK,aAAU;KAAW,KAAK;KAAS,WAAW,GAAG,SAAS;KAAG,GAAI;IAAQ,CAAA;GACvF;GACA,UAAU,EAAE,WAAW,aAAa,GAAG,YAAY;IACjD,IAAI,gBAAgB,QAClB,OAAO,oBAAC,iBAAD;KAAiB,WAAW,GAAG,UAAU,SAAS;KAAG,GAAI;IAAQ,CAAA;IAG1E,IAAI,gBAAgB,SAClB,OAAO,oBAAC,kBAAD;KAAkB,WAAW,GAAG,UAAU,SAAS;KAAG,GAAI;IAAQ,CAAA;IAG3E,OAAO,oBAAC,iBAAD;KAAiB,WAAW,GAAG,UAAU,SAAS;KAAG,GAAI;IAAQ,CAAA;GAC1E;GACA,YAAY,EAAE,GAAG,YAAY,oBAAC,mBAAD;IAA2B;IAAQ,GAAI;GAAQ,CAAA;GAC5E,aAAa,EAAE,UAAU,GAAG,YAAY;IACtC,OACE,oBAAC,MAAD;KAAI,GAAI;KACN,UAAA,oBAAC,OAAD;MAAK,WAAU;MAAmE;KAAc,CAAA;IAC9F,CAAA;GAER;GACA,GAAG;EACL;EACA,GAAI;CACL,CAAA;AAEL;AAEA,IAAa,qBAAqB,EAChC,WACA,KACA,WACA,QACA,GAAG,YACwE;CAC3E,MAAM,oBAAoB,qBAAqB;CAE/C,MAAM,YAAY,MAAM,OAA0B,IAAI;CACtD,MAAM,gBAAgB;EACpB,IAAI,UAAU,SACZ,UAAU,SAAS,MAAM;CAE7B,GAAG,CAAC,UAAU,OAAO,CAAC;CAEtB,OACE,oBAAC,QAAD;EACE,KAAK;EACL,SAAQ;EACR,MAAK;EACL,YAAU,IAAI,KAAK,mBAAmB,QAAQ,IAAI;EAClD,wBAAsB,UAAU,YAAY,CAAC,UAAU,eAAe,CAAC,UAAU,aAAa,CAAC,UAAU;EACzG,oBAAkB,UAAU;EAC5B,kBAAgB,UAAU;EAC1B,qBAAmB,UAAU;EAC7B,WAAW,GACT,w7BACA,kBAAkB,KAClB,SACF;EACA,GAAI;CACL,CAAA;AAEL"}