@godxjp/ui 15.0.1 → 16.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-entry/cascader.js +12 -15
- package/dist/components/data-entry/date-picker.js +14 -31
- package/dist/components/data-entry/date-range-picker.js +1 -2
- package/dist/components/data-entry/index.d.ts +0 -2
- package/dist/components/data-entry/index.js +0 -2
- package/dist/components/data-entry/input.d.ts +12 -0
- package/dist/components/data-entry/input.js +26 -15
- package/dist/components/data-entry/month-picker.js +1 -2
- package/dist/components/data-entry/month-range-picker.js +1 -2
- package/dist/components/data-entry/search-select.js +13 -16
- package/dist/components/data-entry/time-picker.js +40 -58
- package/dist/components/data-entry/tree-select.js +10 -13
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.js +0 -1
- package/dist/styles/alert-layout.css +8 -0
- package/dist/styles/navigation-layout.css +0 -1
- package/package.json +1 -1
- package/dist/components/data-entry/time-input.d.ts +0 -9
- package/dist/components/data-entry/time-input.js +0 -137
- package/dist/components/ui/time-input.d.ts +0 -1
- package/dist/components/ui/time-input.js +0 -1
|
@@ -242,8 +242,8 @@ function Cascader({
|
|
|
242
242
|
className: cn(
|
|
243
243
|
"w-full justify-start font-normal",
|
|
244
244
|
controlOpenRingClass,
|
|
245
|
-
// Reserve trailing room for the clear
|
|
246
|
-
|
|
245
|
+
// Reserve trailing room for the single clear-or-chevron overlay rendered below.
|
|
246
|
+
"pe-9",
|
|
247
247
|
!displayLabel && "text-muted-foreground"
|
|
248
248
|
),
|
|
249
249
|
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: displayLabel ?? resolvedPlaceholder })
|
|
@@ -307,19 +307,16 @@ function Cascader({
|
|
|
307
307
|
}
|
|
308
308
|
)
|
|
309
309
|
] }),
|
|
310
|
-
/* @__PURE__ */
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
),
|
|
321
|
-
/* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" })
|
|
322
|
-
] })
|
|
310
|
+
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 end-3 flex items-center", children: showClear ? /* @__PURE__ */ jsx(
|
|
311
|
+
"button",
|
|
312
|
+
{
|
|
313
|
+
type: "button",
|
|
314
|
+
"aria-label": t("dataEntry.cascader.clear"),
|
|
315
|
+
className: "pointer-events-auto flex size-4 items-center justify-center rounded-sm opacity-50 hover:opacity-100 focus-visible:opacity-100",
|
|
316
|
+
onClick: clearValue,
|
|
317
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
|
|
318
|
+
}
|
|
319
|
+
) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" }) })
|
|
323
320
|
] });
|
|
324
321
|
}
|
|
325
322
|
export {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { CalendarIcon
|
|
3
|
+
import { CalendarIcon } from "lucide-react";
|
|
4
4
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
|
|
5
5
|
import { parseDateInput, toIsoDate } from "../../lib/datetime/parse";
|
|
6
6
|
import { useControlledLatch } from "../../lib/hooks";
|
|
7
7
|
import { cn } from "../../lib/utils";
|
|
8
|
-
import { Button } from "../general/button";
|
|
9
8
|
import { Input } from "./input";
|
|
10
9
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover";
|
|
11
10
|
import { Calendar } from "./calendar";
|
|
@@ -39,7 +38,6 @@ function DatePicker({
|
|
|
39
38
|
setText(toIsoDate(value));
|
|
40
39
|
}, [value]);
|
|
41
40
|
const resolvedPlaceholder = placeholder ?? t("dataEntry.datePicker.placeholder") ?? ISO_HINT;
|
|
42
|
-
const showClear = allowClear && Boolean(value) && !disabled;
|
|
43
41
|
const clear = () => {
|
|
44
42
|
emit(void 0);
|
|
45
43
|
setText("");
|
|
@@ -70,7 +68,19 @@ function DatePicker({
|
|
|
70
68
|
role: "combobox",
|
|
71
69
|
"aria-expanded": open,
|
|
72
70
|
"aria-haspopup": "dialog",
|
|
73
|
-
|
|
71
|
+
allowClear,
|
|
72
|
+
onClear: clear,
|
|
73
|
+
trailingIcon: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
74
|
+
"button",
|
|
75
|
+
{
|
|
76
|
+
type: "button",
|
|
77
|
+
disabled,
|
|
78
|
+
tabIndex: -1,
|
|
79
|
+
"aria-label": t("dataEntry.datePicker.openCalendar") ?? "Open calendar",
|
|
80
|
+
className: "text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100",
|
|
81
|
+
children: /* @__PURE__ */ jsx(CalendarIcon, { className: "size-4", "aria-hidden": "true" })
|
|
82
|
+
}
|
|
83
|
+
) }),
|
|
74
84
|
onClick: () => {
|
|
75
85
|
if (!disabled) setOpen(true);
|
|
76
86
|
},
|
|
@@ -92,33 +102,6 @@ function DatePicker({
|
|
|
92
102
|
}
|
|
93
103
|
}
|
|
94
104
|
),
|
|
95
|
-
showClear ? /* @__PURE__ */ jsx(
|
|
96
|
-
Button,
|
|
97
|
-
{
|
|
98
|
-
type: "button",
|
|
99
|
-
variant: "ghost",
|
|
100
|
-
size: "icon",
|
|
101
|
-
disabled,
|
|
102
|
-
tabIndex: -1,
|
|
103
|
-
"aria-label": t("common.clear") ?? "Clear",
|
|
104
|
-
className: "text-muted-foreground absolute inset-y-0 end-8 h-full px-2 hover:bg-transparent",
|
|
105
|
-
onClick: clear,
|
|
106
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
107
|
-
}
|
|
108
|
-
) : null,
|
|
109
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
110
|
-
Button,
|
|
111
|
-
{
|
|
112
|
-
type: "button",
|
|
113
|
-
variant: "ghost",
|
|
114
|
-
size: "icon",
|
|
115
|
-
disabled,
|
|
116
|
-
tabIndex: -1,
|
|
117
|
-
"aria-label": t("dataEntry.datePicker.openCalendar") ?? "Open calendar",
|
|
118
|
-
className: "text-muted-foreground absolute inset-y-0 end-0 h-full px-2 hover:bg-transparent",
|
|
119
|
-
children: /* @__PURE__ */ jsx(CalendarIcon, { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
120
|
-
}
|
|
121
|
-
) }),
|
|
122
105
|
/* @__PURE__ */ jsx(
|
|
123
106
|
PopoverContent,
|
|
124
107
|
{
|
|
@@ -142,8 +142,7 @@ function DateRangePicker({
|
|
|
142
142
|
},
|
|
143
143
|
children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
144
144
|
}
|
|
145
|
-
) :
|
|
146
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
145
|
+
) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
147
146
|
"button",
|
|
148
147
|
{
|
|
149
148
|
type: "button",
|
|
@@ -45,8 +45,6 @@ export { Cascader } from "./cascader";
|
|
|
45
45
|
export type { CascaderProps, TreeOption, TreeFieldNames } from "./cascader";
|
|
46
46
|
export { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select";
|
|
47
47
|
export type { TreeSelectProps } from "./tree-select";
|
|
48
|
-
export { TimeInput } from "./time-input";
|
|
49
|
-
export type { TimeInputProps } from "./time-input";
|
|
50
48
|
export { Transfer } from "./transfer";
|
|
51
49
|
export type { TransferProps, TransferItemProp } from "./transfer";
|
|
52
50
|
export { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "./command";
|
|
@@ -35,7 +35,6 @@ import { ColorPicker } from "./color-picker";
|
|
|
35
35
|
import { Upload, collectUploadCommitActions, createUploadItem, useUploadDraft } from "./upload";
|
|
36
36
|
import { Cascader } from "./cascader";
|
|
37
37
|
import { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select";
|
|
38
|
-
import { TimeInput } from "./time-input";
|
|
39
38
|
import { Transfer } from "./transfer";
|
|
40
39
|
import {
|
|
41
40
|
Command,
|
|
@@ -101,7 +100,6 @@ export {
|
|
|
101
100
|
Switch,
|
|
102
101
|
TagInput,
|
|
103
102
|
Textarea,
|
|
104
|
-
TimeInput,
|
|
105
103
|
TimePicker,
|
|
106
104
|
Toggle,
|
|
107
105
|
ToggleGroup,
|
|
@@ -4,10 +4,22 @@ export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
|
4
4
|
allowClear?: boolean;
|
|
5
5
|
/** Called after the field is cleared via the inline ✕. */
|
|
6
6
|
onClear?: () => void;
|
|
7
|
+
/**
|
|
8
|
+
* A trailing affordance pinned inside the field — e.g. a calendar / clock popover
|
|
9
|
+
* trigger button. ONE trailing icon shows at a time: when `allowClear` and the field
|
|
10
|
+
* holds a value the clear ✕ REPLACES this icon; otherwise this icon shows. Never both.
|
|
11
|
+
*/
|
|
12
|
+
trailingIcon?: React.ReactNode;
|
|
7
13
|
};
|
|
8
14
|
export declare const Input: React.ForwardRefExoticComponent<React.InputHTMLAttributes<HTMLInputElement> & {
|
|
9
15
|
/** Show an inline ✕ that clears the field while it holds text (default false). */
|
|
10
16
|
allowClear?: boolean;
|
|
11
17
|
/** Called after the field is cleared via the inline ✕. */
|
|
12
18
|
onClear?: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* A trailing affordance pinned inside the field — e.g. a calendar / clock popover
|
|
21
|
+
* trigger button. ONE trailing icon shows at a time: when `allowClear` and the field
|
|
22
|
+
* holds a value the clear ✕ REPLACES this icon; otherwise this icon shows. Never both.
|
|
23
|
+
*/
|
|
24
|
+
trailingIcon?: React.ReactNode;
|
|
13
25
|
} & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -13,7 +13,17 @@ const inputBaseClass = [
|
|
|
13
13
|
"aria-invalid:border-destructive aria-invalid:ring-destructive/20"
|
|
14
14
|
];
|
|
15
15
|
const Input = React.forwardRef(
|
|
16
|
-
({
|
|
16
|
+
({
|
|
17
|
+
className,
|
|
18
|
+
type,
|
|
19
|
+
allowClear = false,
|
|
20
|
+
onClear,
|
|
21
|
+
trailingIcon,
|
|
22
|
+
value,
|
|
23
|
+
defaultValue,
|
|
24
|
+
onChange,
|
|
25
|
+
...props
|
|
26
|
+
}, ref) => {
|
|
17
27
|
const { t } = useTranslation();
|
|
18
28
|
const innerRef = React.useRef(null);
|
|
19
29
|
const setRefs = React.useCallback(
|
|
@@ -48,7 +58,7 @@ const Input = React.forwardRef(
|
|
|
48
58
|
setHasText(false);
|
|
49
59
|
onClear?.();
|
|
50
60
|
};
|
|
51
|
-
if (!allowClear) {
|
|
61
|
+
if (!allowClear && trailingIcon == null) {
|
|
52
62
|
return /* @__PURE__ */ jsx(
|
|
53
63
|
"input",
|
|
54
64
|
{
|
|
@@ -63,7 +73,18 @@ const Input = React.forwardRef(
|
|
|
63
73
|
}
|
|
64
74
|
);
|
|
65
75
|
}
|
|
66
|
-
const showClear = hasText && !props.disabled && !props.readOnly;
|
|
76
|
+
const showClear = allowClear && hasText && !props.disabled && !props.readOnly;
|
|
77
|
+
const trailing = showClear ? /* @__PURE__ */ jsx(
|
|
78
|
+
"button",
|
|
79
|
+
{
|
|
80
|
+
type: "button",
|
|
81
|
+
tabIndex: -1,
|
|
82
|
+
"aria-label": t("common.clear") ?? "Clear",
|
|
83
|
+
onClick: clear,
|
|
84
|
+
className: "text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100",
|
|
85
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
|
|
86
|
+
}
|
|
87
|
+
) : trailingIcon;
|
|
67
88
|
return /* @__PURE__ */ jsxs("span", { "data-slot": "input-affix-wrapper", className: "relative inline-flex w-full items-center", children: [
|
|
68
89
|
/* @__PURE__ */ jsx(
|
|
69
90
|
"input",
|
|
@@ -74,21 +95,11 @@ const Input = React.forwardRef(
|
|
|
74
95
|
value,
|
|
75
96
|
defaultValue,
|
|
76
97
|
onChange: handleChange,
|
|
77
|
-
className: cn(inputBaseClass, showClear && "pe-9", className),
|
|
98
|
+
className: cn(inputBaseClass, (showClear || trailingIcon != null) && "pe-9", className),
|
|
78
99
|
...props
|
|
79
100
|
}
|
|
80
101
|
),
|
|
81
|
-
|
|
82
|
-
"button",
|
|
83
|
-
{
|
|
84
|
-
type: "button",
|
|
85
|
-
tabIndex: -1,
|
|
86
|
-
"aria-label": t("common.clear") ?? "Clear",
|
|
87
|
-
onClick: clear,
|
|
88
|
-
className: "text-muted-foreground hover:text-foreground absolute end-2 inline-flex size-5 items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:opacity-100",
|
|
89
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
|
|
90
|
-
}
|
|
91
|
-
) : null
|
|
102
|
+
trailing != null ? /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 end-2 inline-flex items-center", children: trailing }) : null
|
|
92
103
|
] });
|
|
93
104
|
}
|
|
94
105
|
);
|
|
@@ -109,8 +109,7 @@ function MonthPicker({
|
|
|
109
109
|
},
|
|
110
110
|
children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
111
111
|
}
|
|
112
|
-
) :
|
|
113
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
112
|
+
) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
114
113
|
"button",
|
|
115
114
|
{
|
|
116
115
|
type: "button",
|
|
@@ -164,8 +164,7 @@ function MonthRangePicker({
|
|
|
164
164
|
},
|
|
165
165
|
children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
166
166
|
}
|
|
167
|
-
) :
|
|
168
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
167
|
+
) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
169
168
|
"button",
|
|
170
169
|
{
|
|
171
170
|
type: "button",
|
|
@@ -172,8 +172,8 @@ function SearchSelect({
|
|
|
172
172
|
className: cn(
|
|
173
173
|
"w-full justify-start font-normal",
|
|
174
174
|
controlOpenRingClass,
|
|
175
|
-
// Reserve trailing room for the clear
|
|
176
|
-
|
|
175
|
+
// Reserve trailing room for the single clear-or-chevron overlay rendered below.
|
|
176
|
+
"pe-9"
|
|
177
177
|
),
|
|
178
178
|
children: /* @__PURE__ */ jsx(
|
|
179
179
|
"span",
|
|
@@ -265,20 +265,17 @@ function SearchSelect({
|
|
|
265
265
|
]
|
|
266
266
|
}
|
|
267
267
|
),
|
|
268
|
-
/* @__PURE__ */
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
) : null,
|
|
280
|
-
/* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" })
|
|
281
|
-
] })
|
|
268
|
+
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 end-3 flex items-center", children: showClear ? /* @__PURE__ */ jsx(
|
|
269
|
+
"button",
|
|
270
|
+
{
|
|
271
|
+
type: "button",
|
|
272
|
+
"aria-label": clearLabel ?? t("dataEntry.searchSelect.clear"),
|
|
273
|
+
"data-testid": optionTestId("clear"),
|
|
274
|
+
className: "pointer-events-auto flex size-4 items-center justify-center rounded-sm opacity-50 hover:opacity-100 focus-visible:opacity-100",
|
|
275
|
+
onClick: clear,
|
|
276
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
|
|
277
|
+
}
|
|
278
|
+
) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 shrink-0 opacity-50", "aria-hidden": "true" }) })
|
|
282
279
|
] });
|
|
283
280
|
}
|
|
284
281
|
export {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { Clock
|
|
3
|
+
import { Clock } from "lucide-react";
|
|
4
4
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
|
|
5
5
|
import { isValidHhmm, normalizeHhmm } from "../../lib/datetime";
|
|
6
6
|
import { cn } from "../../lib/utils";
|
|
7
|
-
import {
|
|
8
|
-
import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover";
|
|
7
|
+
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover";
|
|
9
8
|
import { Input } from "./input";
|
|
10
9
|
function pad2(n) {
|
|
11
10
|
return String(n).padStart(2, "0");
|
|
@@ -228,13 +227,12 @@ function TimePicker({
|
|
|
228
227
|
if (!isControlled) setInternal(next);
|
|
229
228
|
onValueChange?.(next);
|
|
230
229
|
};
|
|
231
|
-
const showClear = allowClear && Boolean(value) && !disabled;
|
|
232
230
|
const clear = () => {
|
|
233
231
|
setValue("");
|
|
234
232
|
setText("");
|
|
235
233
|
};
|
|
236
|
-
return /* @__PURE__ */ jsxs(
|
|
237
|
-
/* @__PURE__ */ jsx(
|
|
234
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
235
|
+
/* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: cn("relative", className), children: /* @__PURE__ */ jsx(
|
|
238
236
|
Input,
|
|
239
237
|
{
|
|
240
238
|
id,
|
|
@@ -247,7 +245,20 @@ function TimePicker({
|
|
|
247
245
|
role: "combobox",
|
|
248
246
|
"aria-expanded": open,
|
|
249
247
|
"aria-haspopup": "dialog",
|
|
250
|
-
className:
|
|
248
|
+
className: "tabular-nums",
|
|
249
|
+
allowClear,
|
|
250
|
+
onClear: clear,
|
|
251
|
+
trailingIcon: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
252
|
+
"button",
|
|
253
|
+
{
|
|
254
|
+
type: "button",
|
|
255
|
+
disabled,
|
|
256
|
+
tabIndex: -1,
|
|
257
|
+
"aria-label": t("dataEntry.timePicker.openPicker") ?? "Open time picker",
|
|
258
|
+
className: "text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded-sm opacity-70 transition-opacity hover:opacity-100",
|
|
259
|
+
children: /* @__PURE__ */ jsx(Clock, { className: "size-4", "aria-hidden": "true" })
|
|
260
|
+
}
|
|
261
|
+
) }),
|
|
251
262
|
onClick: () => {
|
|
252
263
|
if (!disabled) setOpen(true);
|
|
253
264
|
},
|
|
@@ -269,59 +280,30 @@ function TimePicker({
|
|
|
269
280
|
setText(normalized ?? (isValidHhmm(value) ? value : ""));
|
|
270
281
|
}
|
|
271
282
|
}
|
|
272
|
-
),
|
|
273
|
-
|
|
274
|
-
|
|
283
|
+
) }) }),
|
|
284
|
+
/* @__PURE__ */ jsx(
|
|
285
|
+
PopoverContent,
|
|
275
286
|
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
type: "button",
|
|
292
|
-
variant: "ghost",
|
|
293
|
-
size: "icon",
|
|
294
|
-
disabled,
|
|
295
|
-
tabIndex: -1,
|
|
296
|
-
"aria-label": t("dataEntry.timePicker.openPicker") ?? "Open time picker",
|
|
297
|
-
className: "text-muted-foreground absolute inset-y-0 end-0 h-full px-2 hover:bg-transparent",
|
|
298
|
-
children: /* @__PURE__ */ jsx(Clock, { className: "size-4 shrink-0", "aria-hidden": "true" })
|
|
299
|
-
}
|
|
300
|
-
) }),
|
|
301
|
-
/* @__PURE__ */ jsx(
|
|
302
|
-
PopoverContent,
|
|
303
|
-
{
|
|
304
|
-
className: "w-auto p-0",
|
|
305
|
-
align: "end",
|
|
306
|
-
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
307
|
-
children: /* @__PURE__ */ jsx(
|
|
308
|
-
TimePickerPanel,
|
|
309
|
-
{
|
|
310
|
-
value: value || "09:00",
|
|
311
|
-
minuteStep,
|
|
312
|
-
use12h,
|
|
313
|
-
onChange: (next) => {
|
|
314
|
-
setValue(next);
|
|
315
|
-
setText(next);
|
|
316
|
-
},
|
|
317
|
-
onDone: () => {
|
|
318
|
-
setOpen(false);
|
|
319
|
-
}
|
|
287
|
+
className: "w-auto p-0",
|
|
288
|
+
align: "end",
|
|
289
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
290
|
+
children: /* @__PURE__ */ jsx(
|
|
291
|
+
TimePickerPanel,
|
|
292
|
+
{
|
|
293
|
+
value: value || "09:00",
|
|
294
|
+
minuteStep,
|
|
295
|
+
use12h,
|
|
296
|
+
onChange: (next) => {
|
|
297
|
+
setValue(next);
|
|
298
|
+
setText(next);
|
|
299
|
+
},
|
|
300
|
+
onDone: () => {
|
|
301
|
+
setOpen(false);
|
|
320
302
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
)
|
|
325
307
|
] });
|
|
326
308
|
}
|
|
327
309
|
export {
|
|
@@ -185,19 +185,16 @@ function TreeSelectRoot({
|
|
|
185
185
|
),
|
|
186
186
|
children: [
|
|
187
187
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: displayKeys.length ? displayLabel : resolvedPlaceholder }),
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
),
|
|
199
|
-
/* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 opacity-50", "aria-hidden": "true" })
|
|
200
|
-
] })
|
|
188
|
+
/* @__PURE__ */ jsx("span", { className: "ms-2 flex shrink-0 items-center", children: allowClear && displayKeys.length > 0 && !disabled ? /* @__PURE__ */ jsx(
|
|
189
|
+
"button",
|
|
190
|
+
{
|
|
191
|
+
type: "button",
|
|
192
|
+
"aria-label": t("dataEntry.treeSelect.clear"),
|
|
193
|
+
className: "flex size-4 items-center justify-center rounded-sm opacity-50 hover:opacity-100 focus-visible:opacity-100",
|
|
194
|
+
onClick: clearValue,
|
|
195
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
|
|
196
|
+
}
|
|
197
|
+
) : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 opacity-50", "aria-hidden": "true" }) })
|
|
201
198
|
]
|
|
202
199
|
}
|
|
203
200
|
) }),
|
|
@@ -21,7 +21,6 @@ export { Separator } from "./separator";
|
|
|
21
21
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../feedback/tooltip";
|
|
22
22
|
export * from "./dropdown-menu";
|
|
23
23
|
export * from "./input";
|
|
24
|
-
export * from "./time-input";
|
|
25
24
|
export * from "./label";
|
|
26
25
|
export * from "./pagination";
|
|
27
26
|
export * from "./password-strength";
|
|
@@ -21,7 +21,6 @@ import { Separator } from "./separator";
|
|
|
21
21
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../feedback/tooltip";
|
|
22
22
|
export * from "./dropdown-menu";
|
|
23
23
|
export * from "./input";
|
|
24
|
-
export * from "./time-input";
|
|
25
24
|
export * from "./label";
|
|
26
25
|
export * from "./pagination";
|
|
27
26
|
export * from "./password-strength";
|
|
@@ -193,6 +193,14 @@
|
|
|
193
193
|
border-radius: var(--skeleton-radius);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
/* Drop the self-frame inside a flush CardContent so the Card supplies the single
|
|
197
|
+
* border — mirrors `.ui-data-table-surface` so the skeleton placeholder and the
|
|
198
|
+
* real DataTable it swaps for sit identically (no nested double border). */
|
|
199
|
+
[data-slot="card-content"][data-flush] .ui-skeleton-table {
|
|
200
|
+
border-width: 0;
|
|
201
|
+
border-radius: 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
196
204
|
.ui-skeleton-table-head {
|
|
197
205
|
display: flex;
|
|
198
206
|
align-items: center;
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
type TimeInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue"> & {
|
|
3
|
-
value?: string;
|
|
4
|
-
defaultValue?: string;
|
|
5
|
-
onValueChange?: (value: string) => void;
|
|
6
|
-
step?: number;
|
|
7
|
-
};
|
|
8
|
-
export declare function TimeInput({ value: controlledValue, defaultValue, onValueChange, placeholder, step, className, name, id, ...props }: TimeInputProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
export type { TimeInputProps };
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
|
-
import { isValidHhmm, normalizeHhmm } from "../../lib/datetime";
|
|
5
|
-
function clampStep(step) {
|
|
6
|
-
if (!step || Number.isNaN(step)) return 1;
|
|
7
|
-
const parsed = Math.max(1, Math.floor(step));
|
|
8
|
-
if (!Number.isFinite(parsed)) return 1;
|
|
9
|
-
return Math.min(59, parsed);
|
|
10
|
-
}
|
|
11
|
-
function applyStep(value, step) {
|
|
12
|
-
const normalized = normalizeHhmm(value);
|
|
13
|
-
if (!normalized) return value;
|
|
14
|
-
const [hourText, minuteText] = normalized.split(":");
|
|
15
|
-
const hour = Number(hourText);
|
|
16
|
-
const minute = Number(minuteText);
|
|
17
|
-
const floored = Math.floor(minute / step) * step;
|
|
18
|
-
const safeMinute = Math.min(59, Math.max(0, floored));
|
|
19
|
-
return `${String(hour).padStart(2, "0")}:${String(safeMinute).padStart(2, "0")}`;
|
|
20
|
-
}
|
|
21
|
-
function maskTime(raw) {
|
|
22
|
-
const digits = raw.replace(/\D/g, "").slice(0, 4);
|
|
23
|
-
if (digits.length <= 2) return digits;
|
|
24
|
-
return `${digits.slice(0, 2)}:${digits.slice(2)}`;
|
|
25
|
-
}
|
|
26
|
-
function stepTime(value, deltaMinutes) {
|
|
27
|
-
const normalized = normalizeHhmm(value) ?? "00:00";
|
|
28
|
-
const [hourText, minuteText] = normalized.split(":");
|
|
29
|
-
const total = (Number(hourText) * 60 + Number(minuteText) + deltaMinutes + 24 * 60) % (24 * 60);
|
|
30
|
-
const hour = Math.floor(total / 60);
|
|
31
|
-
const minute = total % 60;
|
|
32
|
-
return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
|
|
33
|
-
}
|
|
34
|
-
function TimeInput({
|
|
35
|
-
value: controlledValue,
|
|
36
|
-
defaultValue = "",
|
|
37
|
-
onValueChange,
|
|
38
|
-
placeholder,
|
|
39
|
-
step = 1,
|
|
40
|
-
className,
|
|
41
|
-
name,
|
|
42
|
-
id,
|
|
43
|
-
...props
|
|
44
|
-
}) {
|
|
45
|
-
const safeStep = clampStep(step);
|
|
46
|
-
const [internal, setInternal] = React.useState(defaultValue);
|
|
47
|
-
const [displayValue, setDisplayValue] = React.useState(controlledValue ?? defaultValue);
|
|
48
|
-
const isControlled = controlledValue !== void 0;
|
|
49
|
-
const value = isControlled ? controlledValue ?? "" : internal;
|
|
50
|
-
React.useEffect(() => {
|
|
51
|
-
if (!isControlled) return;
|
|
52
|
-
setDisplayValue(controlledValue ?? "");
|
|
53
|
-
}, [controlledValue, isControlled]);
|
|
54
|
-
React.useEffect(() => {
|
|
55
|
-
if (isControlled) return;
|
|
56
|
-
setDisplayValue(internal);
|
|
57
|
-
}, [internal, isControlled]);
|
|
58
|
-
const commit = React.useCallback(
|
|
59
|
-
(next, fromBlur = false) => {
|
|
60
|
-
const normalized = normalizeHhmm(next);
|
|
61
|
-
if (!normalized) return;
|
|
62
|
-
const snapped = applyStep(normalized, safeStep);
|
|
63
|
-
if (!isControlled) {
|
|
64
|
-
setInternal(snapped);
|
|
65
|
-
}
|
|
66
|
-
if (isValidHhmm(snapped)) {
|
|
67
|
-
onValueChange?.(snapped);
|
|
68
|
-
setDisplayValue(snapped);
|
|
69
|
-
}
|
|
70
|
-
if (fromBlur && value !== snapped) {
|
|
71
|
-
onValueChange?.(snapped);
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
[isControlled, onValueChange, safeStep, value]
|
|
75
|
-
);
|
|
76
|
-
const valid = isValidHhmm(value);
|
|
77
|
-
return /* @__PURE__ */ jsx(
|
|
78
|
-
"input",
|
|
79
|
-
{
|
|
80
|
-
id,
|
|
81
|
-
name,
|
|
82
|
-
value: displayValue,
|
|
83
|
-
"data-slot": "time-input",
|
|
84
|
-
className: cn("ui-time-input", className),
|
|
85
|
-
role: "spinbutton",
|
|
86
|
-
"aria-valuemin": 0,
|
|
87
|
-
"aria-valuemax": 24 * 60 - 1,
|
|
88
|
-
"aria-valuenow": valid ? Number(value.slice(0, 2)) * 60 + Number(value.slice(3, 5)) : void 0,
|
|
89
|
-
"aria-valuetext": valid ? value : void 0,
|
|
90
|
-
"aria-invalid": displayValue !== "" && !isValidHhmm(displayValue) ? "true" : void 0,
|
|
91
|
-
placeholder: placeholder ?? "HH:mm",
|
|
92
|
-
inputMode: "numeric",
|
|
93
|
-
autoComplete: "off",
|
|
94
|
-
...props,
|
|
95
|
-
onChange: (event) => {
|
|
96
|
-
const masked = maskTime(event.target.value);
|
|
97
|
-
setDisplayValue(masked);
|
|
98
|
-
const normalized = normalizeHhmm(masked);
|
|
99
|
-
if (normalized) {
|
|
100
|
-
const snapped = applyStep(normalized, safeStep);
|
|
101
|
-
if (!isControlled) {
|
|
102
|
-
setInternal(snapped);
|
|
103
|
-
}
|
|
104
|
-
onValueChange?.(snapped);
|
|
105
|
-
setDisplayValue(snapped);
|
|
106
|
-
}
|
|
107
|
-
props.onChange?.(event);
|
|
108
|
-
},
|
|
109
|
-
onBlur: (event) => {
|
|
110
|
-
commit(event.target.value, true);
|
|
111
|
-
const normalized = normalizeHhmm(event.target.value);
|
|
112
|
-
if (!normalized) {
|
|
113
|
-
setDisplayValue(value);
|
|
114
|
-
}
|
|
115
|
-
props.onBlur?.(event);
|
|
116
|
-
},
|
|
117
|
-
onKeyDown: (event) => {
|
|
118
|
-
if (event.key === "Enter") {
|
|
119
|
-
commit(event.currentTarget.value, true);
|
|
120
|
-
} else if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
121
|
-
event.preventDefault();
|
|
122
|
-
const base = isValidHhmm(displayValue) ? displayValue : value || "00:00";
|
|
123
|
-
const next = stepTime(base, event.key === "ArrowUp" ? safeStep : -safeStep);
|
|
124
|
-
setDisplayValue(next);
|
|
125
|
-
if (!isControlled) {
|
|
126
|
-
setInternal(next);
|
|
127
|
-
}
|
|
128
|
-
onValueChange?.(next);
|
|
129
|
-
}
|
|
130
|
-
props.onKeyDown?.(event);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
export {
|
|
136
|
-
TimeInput
|
|
137
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "../data-entry/time-input";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "../data-entry/time-input";
|