@godxjp/ui 15.0.0 → 16.0.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.
@@ -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,
@@ -5,7 +5,7 @@ import { usePickerLocales, useTranslation } from "../../i18n/use-translation";
5
5
  import { isValidHhmm, normalizeHhmm } from "../../lib/datetime";
6
6
  import { cn } from "../../lib/utils";
7
7
  import { Button } from "../general/button";
8
- import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover";
8
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover";
9
9
  import { Input } from "./input";
10
10
  function pad2(n) {
11
11
  return String(n).padStart(2, "0");
@@ -233,59 +233,58 @@ function TimePicker({
233
233
  setValue("");
234
234
  setText("");
235
235
  };
236
- return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
237
- /* @__PURE__ */ jsx(
238
- Input,
239
- {
240
- id,
241
- name,
242
- value: text,
243
- disabled,
244
- placeholder: resolvedPlaceholder,
245
- inputMode: "numeric",
246
- autoComplete: "off",
247
- role: "combobox",
248
- "aria-expanded": open,
249
- "aria-haspopup": "dialog",
250
- className: cn("tabular-nums", showClear ? "pe-16" : "pe-10"),
251
- onClick: () => {
252
- if (!disabled) setOpen(true);
253
- },
254
- onKeyDown: (event) => {
255
- if (event.key === "ArrowDown") {
256
- event.preventDefault();
257
- setOpen(true);
258
- } else if (event.key === "Escape" && open) {
259
- setOpen(false);
236
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
237
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
238
+ /* @__PURE__ */ jsx(
239
+ Input,
240
+ {
241
+ id,
242
+ name,
243
+ value: text,
244
+ disabled,
245
+ placeholder: resolvedPlaceholder,
246
+ inputMode: "numeric",
247
+ autoComplete: "off",
248
+ role: "combobox",
249
+ "aria-expanded": open,
250
+ "aria-haspopup": "dialog",
251
+ className: cn("tabular-nums", "pe-10"),
252
+ onClick: () => {
253
+ if (!disabled) setOpen(true);
254
+ },
255
+ onKeyDown: (event) => {
256
+ if (event.key === "ArrowDown") {
257
+ event.preventDefault();
258
+ setOpen(true);
259
+ } else if (event.key === "Escape" && open) {
260
+ setOpen(false);
261
+ }
262
+ },
263
+ onChange: (event) => {
264
+ setText(event.target.value);
265
+ const normalized = normalizeHhmm(event.target.value);
266
+ if (normalized) setValue(normalized);
267
+ },
268
+ onBlur: (event) => {
269
+ const normalized = normalizeHhmm(event.target.value);
270
+ setText(normalized ?? (isValidHhmm(value) ? value : ""));
260
271
  }
261
- },
262
- onChange: (event) => {
263
- setText(event.target.value);
264
- const normalized = normalizeHhmm(event.target.value);
265
- if (normalized) setValue(normalized);
266
- },
267
- onBlur: (event) => {
268
- const normalized = normalizeHhmm(event.target.value);
269
- setText(normalized ?? (isValidHhmm(value) ? value : ""));
270
272
  }
271
- }
272
- ),
273
- showClear ? /* @__PURE__ */ jsx(
274
- Button,
275
- {
276
- type: "button",
277
- variant: "ghost",
278
- size: "icon",
279
- disabled,
280
- tabIndex: -1,
281
- "aria-label": t("common.clear") ?? "Clear",
282
- className: "text-muted-foreground absolute inset-y-0 end-8 h-full px-2 hover:bg-transparent",
283
- onClick: clear,
284
- children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
285
- }
286
- ) : null,
287
- /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
288
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
273
+ ),
274
+ showClear ? /* @__PURE__ */ jsx(
275
+ Button,
276
+ {
277
+ type: "button",
278
+ variant: "ghost",
279
+ size: "icon",
280
+ disabled,
281
+ tabIndex: -1,
282
+ "aria-label": t("common.clear") ?? "Clear",
283
+ className: "text-muted-foreground absolute inset-y-0 end-0 h-full px-2 hover:bg-transparent",
284
+ onClick: clear,
285
+ children: /* @__PURE__ */ jsx(X, { className: "size-4 shrink-0", "aria-hidden": "true" })
286
+ }
287
+ ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
289
288
  Button,
290
289
  {
291
290
  type: "button",
@@ -297,31 +296,31 @@ function TimePicker({
297
296
  className: "text-muted-foreground absolute inset-y-0 end-0 h-full px-2 hover:bg-transparent",
298
297
  children: /* @__PURE__ */ jsx(Clock, { className: "size-4 shrink-0", "aria-hidden": "true" })
299
298
  }
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
- }
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);
320
319
  }
321
- )
322
- }
323
- )
324
- ] })
320
+ }
321
+ )
322
+ }
323
+ )
325
324
  ] });
326
325
  }
327
326
  export {
@@ -164,7 +164,7 @@ function Pagination({
164
164
  /* @__PURE__ */ jsx(
165
165
  SelectTrigger,
166
166
  {
167
- className: "ui-pagination-size-trigger",
167
+ className: "ui-pagination-size-trigger w-[var(--pagination-size-width)]",
168
168
  "aria-label": t("navigation.pagination.pageSize"),
169
169
  children: /* @__PURE__ */ jsx(SelectValue, {})
170
170
  }
@@ -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;
@@ -84,8 +84,14 @@
84
84
  justify-content: space-evenly;
85
85
  }
86
86
 
87
- .ui-flex-gap-xs,
87
+ .ui-flex-gap-xs {
88
+ gap: var(--space-stack-xs);
89
+ }
90
+ /* Match the other `.ui-stack-*` sizes: a stack is a vertical column, not a row.
91
+ * Without this, `gap="xs"` stacks (e.g. ToolbarGroup label + control) laid out
92
+ * as a row and squeezed CJK labels into a vertical wrap. */
88
93
  .ui-stack-xs {
94
+ flex-direction: column;
89
95
  gap: var(--space-stack-xs);
90
96
  }
91
97
  .ui-flex-gap-sm {
@@ -591,8 +597,7 @@
591
597
  font-variant-numeric: tabular-nums;
592
598
  }
593
599
 
594
- .ui-toolbar,
595
- .ui-filter-bar {
600
+ .ui-toolbar {
596
601
  display: flex;
597
602
  flex-direction: column;
598
603
  gap: var(--filter-bar-gap);
@@ -600,41 +605,35 @@
600
605
  }
601
606
 
602
607
  @media (min-width: 640px) {
603
- .ui-toolbar,
604
- .ui-filter-bar {
608
+ .ui-toolbar {
605
609
  flex-flow: row wrap;
606
610
  align-items: flex-end;
607
611
  }
608
612
  }
609
613
 
610
- .ui-toolbar-clear,
611
- .ui-filter-clear {
614
+ .ui-toolbar-clear {
612
615
  width: 100%;
613
616
  }
614
617
 
615
618
  @media (min-width: 640px) {
616
- .ui-toolbar-clear,
617
- .ui-filter-clear {
619
+ .ui-toolbar-clear {
618
620
  width: auto;
619
621
  margin-inline-start: auto;
620
622
  }
621
623
  }
622
624
 
623
- .ui-toolbar-group,
624
- .ui-filter-group {
625
+ .ui-toolbar-group {
625
626
  min-width: 0;
626
627
  width: 100%;
627
628
  }
628
629
 
629
630
  @media (min-width: 640px) {
630
- .ui-toolbar-group,
631
- .ui-filter-group {
631
+ .ui-toolbar-group {
632
632
  width: auto;
633
633
  }
634
634
  }
635
635
 
636
- .ui-toolbar-label,
637
- .ui-filter-label {
636
+ .ui-toolbar-label {
638
637
  display: flex;
639
638
  align-items: center;
640
639
  color: hsl(var(--muted-foreground));
@@ -17,7 +17,6 @@
17
17
  .ui-context-menu-checkbox-item,
18
18
  .ui-context-menu-radio-item,
19
19
  .ui-context-menu-shortcut,
20
- .ui-context-menu-content > div,
21
20
  .ui-menubar-item,
22
21
  .ui-menubar-label,
23
22
  .ui-menubar-trigger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "15.0.0",
3
+ "version": "16.0.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@10.29.1",
6
6
  "sideEffects": false,
@@ -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";