@exsto/ds 0.0.1-rc.17 → 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.
- package/README.md +34 -0
- package/dist/ds/src/file-input.d.ts +9 -0
- package/dist/ds/src/time-field.d.ts +9 -0
- package/dist/file-input.js +2 -0
- package/dist/index.js +4 -3
- package/dist/source-hash.txt +1 -1
- package/dist/styles.css +143 -38
- package/dist/time-field.js +2 -0
- package/dist/ui/src/components/edyn/file-input/file-input.d.ts +90 -0
- package/dist/ui/src/components/edyn/file-input/file-input.js +261 -0
- package/dist/ui/src/components/edyn/file-input/file-input.js.map +1 -0
- package/dist/ui/src/components/edyn/file-input/file-input.test.d.ts +8 -0
- package/dist/ui/src/components/edyn/file-input/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.d.ts +12 -1
- package/dist/ui/src/components/edyn/multi-select/multi-select.js +11 -7
- package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -1
- package/dist/ui/src/components/edyn/time-field/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/time-field/time-field.d.ts +85 -0
- package/dist/ui/src/components/edyn/time-field/time-field.js +330 -0
- package/dist/ui/src/components/edyn/time-field/time-field.js.map +1 -0
- package/dist/ui/src/components/edyn/time-field/time-field.test.d.ts +8 -0
- package/dist/ui/src/components/ui/combobox/combobox.d.ts +10 -0
- package/dist/ui/src/components/ui/combobox/combobox.js +37 -5
- package/dist/ui/src/components/ui/combobox/combobox.js.map +1 -1
- package/dist/ui/src/components/ui/context-menu/context-menu.d.ts +1 -1
- package/dist/ui/src/components/ui/context-menu/context-menu.js +23 -10
- package/dist/ui/src/components/ui/context-menu/context-menu.js.map +1 -1
- package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js +5 -4
- package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/ui/src/components/ui/menubar/menubar.js +1 -1
- package/dist/ui/src/components/ui/menubar/menubar.js.map +1 -1
- package/dist/ui/src/components/ui/scroll-area/scroll-area.js +1 -1
- package/dist/ui/src/components/ui/scroll-area/scroll-area.js.map +1 -1
- package/dist/ui/src/components/ui/select/select.js +7 -6
- package/dist/ui/src/components/ui/select/select.js.map +1 -1
- package/dist/ui/src/components/ui/sheet/sheet.d.ts +8 -0
- package/dist/ui/src/components/ui/sheet/sheet.js +11 -3
- package/dist/ui/src/components/ui/sheet/sheet.js.map +1 -1
- package/dist/ui/src/components/ui/table/table.d.ts +9 -2
- package/dist/ui/src/components/ui/table/table.js +19 -6
- package/dist/ui/src/components/ui/table/table.js.map +1 -1
- package/dist/ui/src/index.d.ts +1 -0
- package/dist/ui/src/lib/popup-list.d.ts +29 -4
- package/dist/ui/src/lib/popup-list.js +30 -5
- package/dist/ui/src/lib/popup-list.js.map +1 -1
- package/package.json +9 -1
|
@@ -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 {};
|
|
@@ -13,3 +13,13 @@ export interface IComboboxContentProps extends Omit<ComboboxPrimitive.Popup.Prop
|
|
|
13
13
|
export declare const ComboboxContent: ({ className, children, empty, portalContainer, ...props }: IComboboxContentProps) => import('react').JSX.Element;
|
|
14
14
|
export declare const ComboboxItem: ({ className, children, ...props }: ComboboxPrimitive.Item.Props) => import('react').JSX.Element;
|
|
15
15
|
export declare const ComboboxEmpty: ({ className, children, ...props }: ComboboxPrimitive.Empty.Props) => import('react').JSX.Element;
|
|
16
|
+
/**
|
|
17
|
+
* A titled run of items. Put a `ComboboxLabel` first in it; with grouped `items` on the root, render the group's own items
|
|
18
|
+
* through `ComboboxCollection` (Base UI's grouped-items pattern), so a filter hides a group whose items all drop out.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ComboboxGroup: ({ ...props }: ComboboxPrimitive.Group.Props) => import('react').JSX.Element;
|
|
21
|
+
/** A group's heading, the same one Select and the menus draw. */
|
|
22
|
+
export declare const ComboboxLabel: ({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) => import('react').JSX.Element;
|
|
23
|
+
/** The items of one group, for grouped `items` on the root. */
|
|
24
|
+
export declare const ComboboxCollection: ({ ...props }: ComboboxPrimitive.Collection.Props) => import('react').JSX.Element;
|
|
25
|
+
export declare const ComboboxSeparator: ({ className, ...props }: ComboboxPrimitive.Separator.Props) => import('react').JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
2
|
import { ScrollArea } from "../scroll-area/scroll-area.js";
|
|
3
|
-
import { POPUP_ITEM_INDICATOR_CLASS, POPUP_LIST_WIDTH_CLASS, popupItemClass } from "../../../lib/popup-list.js";
|
|
3
|
+
import { POPUP_FRAME_CLASS, POPUP_GROUP_LABEL_CLASS, POPUP_ITEM_INDICATOR_CLASS, POPUP_LIST_HEIGHT_CLASS, POPUP_LIST_WIDTH_CLASS, popupItemClass } from "../../../lib/popup-list.js";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { IconCheck, IconChevronDown } from "@tabler/icons-react";
|
|
6
6
|
import { Combobox } from "@base-ui/react/combobox";
|
|
@@ -39,13 +39,13 @@ var ComboboxContent = ({ className, children, empty, portalContainer, ...props }
|
|
|
39
39
|
className: "z-50",
|
|
40
40
|
children: /* @__PURE__ */ jsxs(Combobox.Popup, {
|
|
41
41
|
"data-slot": "combobox-content",
|
|
42
|
-
className: cn(
|
|
42
|
+
className: cn("flex max-h-(--available-height) flex-col overflow-hidden", POPUP_FRAME_CLASS, POPUP_LIST_WIDTH_CLASS, "motion-reduce:animate-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
43
43
|
...props,
|
|
44
44
|
children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: empty }), /* @__PURE__ */ jsx(ScrollArea, {
|
|
45
45
|
type: "always",
|
|
46
46
|
fade: true,
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
className: POPUP_LIST_HEIGHT_CLASS,
|
|
48
|
+
viewportClassName: "scroll-py-1",
|
|
49
49
|
children: /* @__PURE__ */ jsx(Combobox.List, {
|
|
50
50
|
"data-slot": "combobox-list",
|
|
51
51
|
className: "p-1",
|
|
@@ -79,7 +79,39 @@ var ComboboxEmpty = ({ className, children, ...props }) => {
|
|
|
79
79
|
children: children ?? "No results found."
|
|
80
80
|
});
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* A titled run of items. Put a `ComboboxLabel` first in it; with grouped `items` on the root, render the group's own items
|
|
84
|
+
* through `ComboboxCollection` (Base UI's grouped-items pattern), so a filter hides a group whose items all drop out.
|
|
85
|
+
*/
|
|
86
|
+
var ComboboxGroup = ({ ...props }) => {
|
|
87
|
+
return /* @__PURE__ */ jsx(Combobox.Group, {
|
|
88
|
+
"data-slot": "combobox-group",
|
|
89
|
+
...props
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
/** A group's heading, the same one Select and the menus draw. */
|
|
93
|
+
var ComboboxLabel = ({ className, ...props }) => {
|
|
94
|
+
return /* @__PURE__ */ jsx(Combobox.GroupLabel, {
|
|
95
|
+
"data-slot": "combobox-label",
|
|
96
|
+
className: cn(POPUP_GROUP_LABEL_CLASS, className),
|
|
97
|
+
...props
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
/** The items of one group, for grouped `items` on the root. */
|
|
101
|
+
var ComboboxCollection = ({ ...props }) => {
|
|
102
|
+
return /* @__PURE__ */ jsx(Combobox.Collection, {
|
|
103
|
+
"data-slot": "combobox-collection",
|
|
104
|
+
...props
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
var ComboboxSeparator = ({ className, ...props }) => {
|
|
108
|
+
return /* @__PURE__ */ jsx(Combobox.Separator, {
|
|
109
|
+
"data-slot": "combobox-separator",
|
|
110
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
111
|
+
...props
|
|
112
|
+
});
|
|
113
|
+
};
|
|
82
114
|
//#endregion
|
|
83
|
-
export { Combobox$1 as Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem };
|
|
115
|
+
export { Combobox$1 as Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxSeparator };
|
|
84
116
|
|
|
85
117
|
//# sourceMappingURL=combobox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combobox.js","names":[],"sources":["../../../../../../../ui/src/components/ui/combobox/combobox.tsx"],"sourcesContent":["import { Combobox as ComboboxPrimitive } from '@base-ui/react/combobox';\nimport {
|
|
1
|
+
{"version":3,"file":"combobox.js","names":[],"sources":["../../../../../../../ui/src/components/ui/combobox/combobox.tsx"],"sourcesContent":["import { Combobox as ComboboxPrimitive } from '@base-ui/react/combobox';\nimport {\n POPUP_FRAME_CLASS,\n POPUP_GROUP_LABEL_CLASS,\n POPUP_ITEM_INDICATOR_CLASS,\n POPUP_LIST_HEIGHT_CLASS,\n POPUP_LIST_WIDTH_CLASS,\n popupItemClass,\n} from '@edyn/ui/lib/popup-list';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCheck, IconChevronDown } from '@tabler/icons-react';\nimport type { ReactNode } from 'react';\nimport { ScrollArea } from '../scroll-area';\n\nexport const Combobox = <TValue,>({ ...props }: ComboboxPrimitive.Root.Props<TValue>) => {\n return <ComboboxPrimitive.Root data-slot=\"combobox\" {...props} />;\n};\n\nexport const ComboboxInput = ({ className, ...props }: ComboboxPrimitive.Input.Props) => {\n return (\n <ComboboxPrimitive.InputGroup data-slot=\"combobox-input-group\" className=\"relative flex w-full items-center\">\n <ComboboxPrimitive.Input\n data-slot=\"combobox-input\"\n className={cn(\n 'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 pr-8 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',\n className,\n )}\n {...props}\n />\n <ComboboxPrimitive.Trigger\n data-slot=\"combobox-trigger\"\n className=\"absolute right-2 flex items-center text-muted-foreground disabled:pointer-events-none disabled:opacity-50\"\n tabIndex={-1}\n aria-hidden\n >\n <IconChevronDown className=\"size-4\" />\n </ComboboxPrimitive.Trigger>\n </ComboboxPrimitive.InputGroup>\n );\n};\n\nexport interface IComboboxContentProps extends Omit<ComboboxPrimitive.Popup.Props, 'children'> {\n /** Item nodes, or the Base UI function form `(item, index) => node` (requires `items` on the root). */\n children?: ComboboxPrimitive.List.Props['children'];\n /** Empty-state content; rendered (with the default text) only when the filtered list is empty. */\n empty?: ReactNode;\n /** Optional portal destination, used by shadow-DOM element wrappers. */\n portalContainer?: ComboboxPrimitive.Portal.Props['container'];\n}\n\nexport const ComboboxContent = ({ className, children, empty, portalContainer, ...props }: IComboboxContentProps) => {\n return (\n <ComboboxPrimitive.Portal data-slot=\"combobox-portal\" container={portalContainer}>\n <ComboboxPrimitive.Positioner data-slot=\"combobox-positioner\" sideOffset={4} align=\"start\" className=\"z-50\">\n <ComboboxPrimitive.Popup\n data-slot=\"combobox-content\"\n className={cn(\n 'flex max-h-(--available-height) flex-col overflow-hidden',\n POPUP_FRAME_CLASS,\n POPUP_LIST_WIDTH_CLASS,\n 'motion-reduce:animate-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\n className,\n )}\n {...props}\n >\n <ComboboxEmpty>{empty}</ComboboxEmpty>\n {/* The ScrollArea, not the List, is the scroll container: a filtered list\n that runs past the fold needs a bar and a fade to say so. Base UI\n keeps the highlighted item in view through the nearest scrollable\n ancestor, which is now the viewport. POPUP_LIST_HEIGHT_CLASS ends\n the list on a whole row. */}\n <ScrollArea type=\"always\" fade className={POPUP_LIST_HEIGHT_CLASS} viewportClassName=\"scroll-py-1\">\n <ComboboxPrimitive.List data-slot=\"combobox-list\" className=\"p-1\">\n {children}\n </ComboboxPrimitive.List>\n </ScrollArea>\n </ComboboxPrimitive.Popup>\n </ComboboxPrimitive.Positioner>\n </ComboboxPrimitive.Portal>\n );\n};\n\nexport const ComboboxItem = ({ className, children, ...props }: ComboboxPrimitive.Item.Props) => {\n return (\n <ComboboxPrimitive.Item data-slot=\"combobox-item\" className={cn(popupItemClass('base-ui', true), className)} {...props}>\n <span className=\"flex-1\">{children}</span>\n <ComboboxPrimitive.ItemIndicator data-slot=\"combobox-item-indicator\" className={POPUP_ITEM_INDICATOR_CLASS}>\n <IconCheck className=\"size-3.5\" />\n </ComboboxPrimitive.ItemIndicator>\n </ComboboxPrimitive.Item>\n );\n};\n\nexport const ComboboxEmpty = ({ className, children, ...props }: ComboboxPrimitive.Empty.Props) => {\n return (\n <ComboboxPrimitive.Empty\n data-slot=\"combobox-empty\"\n // empty:py-0 collapses the padding while the list has results: Base UI\n // keeps this status element mounted and only fills it when the list is\n // empty (and only when the root has `items` to derive emptiness from).\n className={cn('py-4 text-center text-sm text-muted-foreground empty:py-0', className)}\n {...props}\n >\n {children ?? 'No results found.'}\n </ComboboxPrimitive.Empty>\n );\n};\n\n/**\n * A titled run of items. Put a `ComboboxLabel` first in it; with grouped `items` on the root, render the group's own items\n * through `ComboboxCollection` (Base UI's grouped-items pattern), so a filter hides a group whose items all drop out.\n */\nexport const ComboboxGroup = ({ ...props }: ComboboxPrimitive.Group.Props) => {\n return <ComboboxPrimitive.Group data-slot=\"combobox-group\" {...props} />;\n};\n\n/** A group's heading, the same one Select and the menus draw. */\nexport const ComboboxLabel = ({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) => {\n return <ComboboxPrimitive.GroupLabel data-slot=\"combobox-label\" className={cn(POPUP_GROUP_LABEL_CLASS, className)} {...props} />;\n};\n\n/** The items of one group, for grouped `items` on the root. */\nexport const ComboboxCollection = ({ ...props }: ComboboxPrimitive.Collection.Props) => {\n return <ComboboxPrimitive.Collection data-slot=\"combobox-collection\" {...props} />;\n};\n\nexport const ComboboxSeparator = ({ className, ...props }: ComboboxPrimitive.Separator.Props) => {\n return <ComboboxPrimitive.Separator data-slot=\"combobox-separator\" className={cn('-mx-1 my-1 h-px bg-border', className)} {...props} />;\n};\n"],"mappings":";;;;;;;AAcA,IAAa,cAAqB,EAAE,GAAG,YAAkD;CACvF,OAAO,oBAAC,SAAkB,MAAnB;EAAwB,aAAU;EAAW,GAAI;CAAQ,CAAA;AAClE;AAEA,IAAa,iBAAiB,EAAE,WAAW,GAAG,YAA2C;CACvF,OACE,qBAAC,SAAkB,YAAnB;EAA8B,aAAU;EAAuB,WAAU;EAAzE,UAAA,CACE,oBAAC,SAAkB,OAAnB;GACE,aAAU;GACV,WAAW,GACT,qiBACA,SACF;GACA,GAAI;EACL,CAAA,GACD,oBAAC,SAAkB,SAAnB;GACE,aAAU;GACV,WAAU;GACV,UAAU;GACV,eAAA;GAEA,UAAA,oBAAC,iBAAD,EAAiB,WAAU,SAAU,CAAA;EACZ,CAAA,CACC;;AAElC;AAWA,IAAa,mBAAmB,EAAE,WAAW,UAAU,OAAO,iBAAiB,GAAG,YAAmC;CACnH,OACE,oBAAC,SAAkB,QAAnB;EAA0B,aAAU;EAAkB,WAAW;EAC/D,UAAA,oBAAC,SAAkB,YAAnB;GAA8B,aAAU;GAAsB,YAAY;GAAG,OAAM;GAAQ,WAAU;GACnG,UAAA,qBAAC,SAAkB,OAAnB;IACE,aAAU;IACV,WAAW,GACT,4DACA,mBACA,wBACA,mKACA,SACF;IACA,GAAI;IATN,UAAA,CAWE,oBAAC,eAAD,EAAA,UAAgB,MAAqB,CAAA,GAMrC,oBAAC,YAAD;KAAY,MAAK;KAAS,MAAA;KAAK,WAAW;KAAyB,mBAAkB;KACnF,UAAA,oBAAC,SAAkB,MAAnB;MAAwB,aAAU;MAAgB,WAAU;MACzD;KACqB,CAAA;IACd,CAAA,CACW;;EACG,CAAA;CACN,CAAA;AAE9B;AAEA,IAAa,gBAAgB,EAAE,WAAW,UAAU,GAAG,YAA0C;CAC/F,OACE,qBAAC,SAAkB,MAAnB;EAAwB,aAAU;EAAgB,WAAW,GAAG,eAAe,WAAW,IAAI,GAAG,SAAS;EAAG,GAAI;EAAjH,UAAA,CACE,oBAAC,QAAD;GAAM,WAAU;GAAU;EAAe,CAAA,GACzC,oBAAC,SAAkB,eAAnB;GAAiC,aAAU;GAA0B,WAAW;GAC9E,UAAA,oBAAC,WAAD,EAAW,WAAU,WAAY,CAAA;EACF,CAAA,CACX;;AAE5B;AAEA,IAAa,iBAAiB,EAAE,WAAW,UAAU,GAAG,YAA2C;CACjG,OACE,oBAAC,SAAkB,OAAnB;EACE,aAAU;EAIV,WAAW,GAAG,6DAA6D,SAAS;EACpF,GAAI;EAEH,UAAA,YAAY;CACU,CAAA;AAE7B;;;;;AAMA,IAAa,iBAAiB,EAAE,GAAG,YAA2C;CAC5E,OAAO,oBAAC,SAAkB,OAAnB;EAAyB,aAAU;EAAiB,GAAI;CAAQ,CAAA;AACzE;;AAGA,IAAa,iBAAiB,EAAE,WAAW,GAAG,YAAgD;CAC5F,OAAO,oBAAC,SAAkB,YAAnB;EAA8B,aAAU;EAAiB,WAAW,GAAG,yBAAyB,SAAS;EAAG,GAAI;CAAQ,CAAA;AACjI;;AAGA,IAAa,sBAAsB,EAAE,GAAG,YAAgD;CACtF,OAAO,oBAAC,SAAkB,YAAnB;EAA8B,aAAU;EAAsB,GAAI;CAAQ,CAAA;AACnF;AAEA,IAAa,qBAAqB,EAAE,WAAW,GAAG,YAA+C;CAC/F,OAAO,oBAAC,SAAkB,WAAnB;EAA6B,aAAU;EAAqB,WAAW,GAAG,6BAA6B,SAAS;EAAG,GAAI;CAAQ,CAAA;AACxI"}
|
|
@@ -3,7 +3,7 @@ import type * as React from 'react';
|
|
|
3
3
|
export declare const ContextMenu: ({ ...props }: ContextMenuPrimitive.Root.Props) => React.JSX.Element;
|
|
4
4
|
export declare const ContextMenuPortal: ({ ...props }: ContextMenuPrimitive.Portal.Props) => React.JSX.Element;
|
|
5
5
|
export declare const ContextMenuTrigger: ({ className, ...props }: ContextMenuPrimitive.Trigger.Props) => React.JSX.Element;
|
|
6
|
-
export declare const ContextMenuContent: ({ className, align, alignOffset, side, sideOffset, ...props }: ContextMenuPrimitive.Popup.Props & Pick<ContextMenuPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) => React.JSX.Element;
|
|
6
|
+
export declare const ContextMenuContent: ({ className, children, align, alignOffset, side, sideOffset, ...props }: ContextMenuPrimitive.Popup.Props & Pick<ContextMenuPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) => React.JSX.Element;
|
|
7
7
|
export declare const ContextMenuGroup: ({ ...props }: ContextMenuPrimitive.Group.Props) => React.JSX.Element;
|
|
8
8
|
export declare const ContextMenuLabel: ({ className, inset, ...props }: ContextMenuPrimitive.GroupLabel.Props & {
|
|
9
9
|
inset?: boolean;
|