@godxjp/ui 9.2.0 → 10.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.
Files changed (137) hide show
  1. package/README.md +58 -29
  2. package/dist/app/index.d.ts +1 -1
  3. package/dist/app/index.js +4 -4
  4. package/dist/{app.prop-UTc4j4nj.d.ts → app.prop-Cy6dJnU8.d.ts} +18 -40
  5. package/dist/{checkbox-ChRsR7Nk.d.ts → checkbox-em-oFM5D.d.ts} +1 -1
  6. package/dist/{chunk-LJLGABFV.js → chunk-2HXZT2WJ.js} +17 -9
  7. package/dist/{chunk-QLMXEJSY.js → chunk-3Q4A4U2P.js} +24 -1
  8. package/dist/{chunk-26CPAKUP.js → chunk-44YRPSZ7.js} +1 -2
  9. package/dist/{chunk-HB2OHB5X.js → chunk-5NCFLCM7.js} +27 -16
  10. package/dist/{chunk-FXFJF4YA.js → chunk-6CSBMMZS.js} +262 -31
  11. package/dist/{chunk-E7HBHUJY.js → chunk-6HHSU6RG.js} +8 -6
  12. package/dist/{chunk-457KVJTX.js → chunk-7Q45MBFW.js} +7 -5
  13. package/dist/{chunk-O24Z3ULJ.js → chunk-BE6GJGKJ.js} +1 -1
  14. package/dist/{chunk-FVPCVZL3.js → chunk-BG5RNXTH.js} +1 -1
  15. package/dist/{chunk-AINW5WYN.js → chunk-COD66MFF.js} +1 -2
  16. package/dist/{chunk-IOGU3ZWF.js → chunk-DNGJHWJZ.js} +3 -3
  17. package/dist/{chunk-3TS3G4U3.js → chunk-EE5DKOHX.js} +3 -1
  18. package/dist/{chunk-G6Q32VHO.js → chunk-EQZP53KI.js} +33 -8
  19. package/dist/{chunk-BHV2FUOA.js → chunk-EZHHJQWQ.js} +1 -1
  20. package/dist/{chunk-N3JPLJ3B.js → chunk-GDDCSKCB.js} +12 -5
  21. package/dist/{chunk-RLGHEV4A.js → chunk-HTG5VHU7.js} +10 -1
  22. package/dist/{chunk-R2W2FX5Q.js → chunk-I7NQ2LIL.js} +1 -9
  23. package/dist/{chunk-XQMPK4GM.js → chunk-IHRMOJXD.js} +86 -39
  24. package/dist/{chunk-TILFZBTE.js → chunk-INIIF7F7.js} +1 -4
  25. package/dist/{chunk-UIYEAUWA.js → chunk-IY347EQA.js} +2 -2
  26. package/dist/{chunk-HCM4JAC2.js → chunk-JWGLJXQU.js} +39 -11
  27. package/dist/{chunk-TO33OY4L.js → chunk-LMKUKCTN.js} +1 -1
  28. package/dist/chunk-NXVCI6YB.js +453 -0
  29. package/dist/{chunk-S6TBIL7J.js → chunk-O6DQZYNI.js} +63 -44
  30. package/dist/{chunk-O2OUNXV4.js → chunk-P5KPCT6R.js} +3 -3
  31. package/dist/{chunk-6HQMUUQW.js → chunk-PDXFQS7M.js} +14 -30
  32. package/dist/{chunk-F7PG4OEV.js → chunk-QSGW3ZWK.js} +12 -4
  33. package/dist/{chunk-25RYBC5T.js → chunk-S2IJKT3D.js} +1 -1
  34. package/dist/{chunk-OJZ6C2HM.js → chunk-SARQRCKO.js} +54 -48
  35. package/dist/chunk-T2QO2S65.js +126 -0
  36. package/dist/{chunk-26WDEDWL.js → chunk-TGNBXS7H.js} +32 -23
  37. package/dist/{chunk-6J7GRCDA.js → chunk-UNVRNJCB.js} +71 -11
  38. package/dist/{chunk-6YBYAEXD.js → chunk-VSM44AYE.js} +94 -24
  39. package/dist/{chunk-QWLXN6CT.js → chunk-VSUYVT2Q.js} +3 -2
  40. package/dist/{chunk-4R7QL3MW.js → chunk-X2VY4MOW.js} +14 -29
  41. package/dist/{chunk-ETLAI3QU.js → chunk-Z46J47FY.js} +73 -77
  42. package/dist/components/admin/index.d.ts +22 -12
  43. package/dist/components/admin/index.js +23 -24
  44. package/dist/components/data-display/badge.js +3 -3
  45. package/dist/components/data-display/card.d.ts +3 -3
  46. package/dist/components/data-display/card.js +1 -1
  47. package/dist/components/data-display/carousel.js +3 -1
  48. package/dist/components/data-display/index.js +54 -32
  49. package/dist/components/data-entry/calendar.d.ts +1 -1
  50. package/dist/components/data-entry/calendar.js +1 -1
  51. package/dist/components/data-entry/cascader.d.ts +1 -1
  52. package/dist/components/data-entry/cascader.js +5 -5
  53. package/dist/components/data-entry/checkbox.d.ts +2 -2
  54. package/dist/components/data-entry/checkbox.js +2 -2
  55. package/dist/components/data-entry/color-picker.d.ts +1 -1
  56. package/dist/components/data-entry/color-picker.js +3 -3
  57. package/dist/components/data-entry/date-picker.d.ts +2 -2
  58. package/dist/components/data-entry/date-picker.js +4 -4
  59. package/dist/components/data-entry/date-range-picker.d.ts +2 -2
  60. package/dist/components/data-entry/date-range-picker.js +4 -4
  61. package/dist/components/data-entry/index.d.ts +9 -25
  62. package/dist/components/data-entry/index.js +22 -26
  63. package/dist/components/data-entry/radio.d.ts +1 -1
  64. package/dist/components/data-entry/radio.js +2 -2
  65. package/dist/components/data-entry/select.d.ts +2 -2
  66. package/dist/components/data-entry/select.js +3 -4
  67. package/dist/components/data-entry/slider.d.ts +1 -1
  68. package/dist/components/data-entry/switch.d.ts +2 -2
  69. package/dist/components/data-entry/switch.js +1 -1
  70. package/dist/components/data-entry/time-input.js +2 -2
  71. package/dist/components/data-entry/time-picker.d.ts +3 -1
  72. package/dist/components/data-entry/time-picker.js +3 -3
  73. package/dist/components/data-entry/transfer.d.ts +2 -2
  74. package/dist/components/data-entry/transfer.js +5 -5
  75. package/dist/components/data-entry/tree-select.d.ts +1 -1
  76. package/dist/components/data-entry/tree-select.js +5 -5
  77. package/dist/components/data-entry/upload.d.ts +2 -2
  78. package/dist/components/data-entry/upload.js +5 -5
  79. package/dist/components/feedback/alert.js +5 -5
  80. package/dist/components/feedback/dialog.js +3 -3
  81. package/dist/components/feedback/index.d.ts +1 -1
  82. package/dist/components/feedback/index.js +8 -8
  83. package/dist/components/feedback/sheet.js +1 -1
  84. package/dist/components/layout/index.d.ts +6 -9
  85. package/dist/components/layout/index.js +5 -3
  86. package/dist/components/navigation/dropdown-menu.js +1 -1
  87. package/dist/components/navigation/index.d.ts +14 -16
  88. package/dist/components/navigation/index.js +7 -8
  89. package/dist/components/navigation/pagination.d.ts +11 -8
  90. package/dist/components/navigation/pagination.js +4 -5
  91. package/dist/components/navigation/steps.d.ts +3 -3
  92. package/dist/components/navigation/steps.js +3 -1
  93. package/dist/components/query/index.d.ts +1 -5
  94. package/dist/components/query/index.js +6 -8
  95. package/dist/components/ui/index.d.ts +5 -7
  96. package/dist/components/ui/index.js +26 -29
  97. package/dist/{data-entry.prop-CDkOajPj.d.ts → data-entry.prop-BR4vNA1j.d.ts} +7 -35
  98. package/dist/filter-bar-BxjSJJnQ.d.ts +7 -0
  99. package/dist/{inline-CDSVAN54.d.ts → flex-D_EXRFSW.d.ts} +2 -8
  100. package/dist/form/index.js +1 -1
  101. package/dist/i18n/index.d.ts +82 -10
  102. package/dist/i18n/index.js +2 -2
  103. package/dist/index.d.ts +6 -6
  104. package/dist/index.js +34 -37
  105. package/dist/lib/datetime/index.js +1 -1
  106. package/dist/{navigation.prop-8DgElO0c.d.ts → navigation.prop-DMcXkR-J.d.ts} +9 -11
  107. package/dist/{password-strength-kQkloEeo.d.ts → password-strength-DVRvXEOK.d.ts} +2 -2
  108. package/dist/props/components/index.d.ts +3 -3
  109. package/dist/props/index.d.ts +3 -3
  110. package/dist/props/index.js +1 -1
  111. package/dist/props/registry.d.ts +84 -39
  112. package/dist/props/registry.js +1 -1
  113. package/dist/{search-input-cezAxpgb.d.ts → search-input-DpqDMXcn.d.ts} +2 -4
  114. package/dist/{skeleton-uWAjSacg.d.ts → skeleton-cj9kh5wo.d.ts} +1 -3
  115. package/dist/styles/control.css +176 -41
  116. package/dist/styles/data-display-layout.css +41 -15
  117. package/dist/styles/feedback-layout.css +44 -12
  118. package/dist/styles/index.css +45 -1
  119. package/dist/styles/layout.css +18 -17
  120. package/dist/styles/navigation-layout.css +3 -1
  121. package/dist/styles/shell-layout.css +3 -3
  122. package/dist/styles/table-layout.css +13 -0
  123. package/dist/tokens/foundation.css +12 -1
  124. package/dist/tokens/semantic/layout.css +2 -2
  125. package/package.json +6 -7
  126. package/scripts/ui-audit.mjs +31 -2
  127. package/dist/chunk-6MCI7W5G.js +0 -201
  128. package/dist/chunk-CAEL2ZD2.js +0 -222
  129. package/dist/chunk-GKXPALFT.js +0 -32
  130. package/dist/chunk-JKHWLPM5.js +0 -101
  131. package/dist/chunk-KDBGFJJI.js +0 -220
  132. package/dist/components/data-entry/autocomplete.d.ts +0 -24
  133. package/dist/components/data-entry/autocomplete.js +0 -10
  134. package/dist/components/data-entry/combobox.d.ts +0 -22
  135. package/dist/components/data-entry/combobox.js +0 -6
  136. package/dist/filter-bar-B5TPUqEO.d.ts +0 -14
  137. /package/dist/{chunk-LDSLS6HE.js → chunk-7CFO5FFE.js} +0 -0
@@ -1,10 +1,10 @@
1
1
  import { normalizeTreeOptions, collectAllExpandableKeys, filterVisibleTree, flattenVisibleTree, findNodeByValue, reactNodeText, getDescendantValues } from './chunk-SMLKNECP.js';
2
- import { Checkbox } from './chunk-O24Z3ULJ.js';
3
2
  import { Command, CommandInput } from './chunk-HTEL5DQI.js';
3
+ import { Checkbox } from './chunk-BE6GJGKJ.js';
4
4
  import { Button } from './chunk-M4PZNAMV.js';
5
5
  import { Popover, PopoverTrigger, PopoverContent } from './chunk-DY5C44UP.js';
6
6
  import { ScrollArea } from './chunk-3KPEZ5CF.js';
7
- import { useTranslation } from './chunk-RLGHEV4A.js';
7
+ import { useTranslation } from './chunk-HTG5VHU7.js';
8
8
  import { cn } from './chunk-U7N2A7A3.js';
9
9
  import * as React from 'react';
10
10
  import { X, ChevronsUpDown, ChevronDown, ChevronRight } from 'lucide-react';
@@ -65,6 +65,8 @@ function TreeSelectRoot({
65
65
  );
66
66
  const isControlled = value !== void 0;
67
67
  const [internal, setInternal] = React.useState(() => toArray(defaultValue));
68
+ const [activeKey, setActiveKey] = React.useState(null);
69
+ const treeItemRefs = React.useRef(/* @__PURE__ */ new Map());
68
70
  const selected = isControlled ? toArray(value) : internal;
69
71
  const resolvedPlaceholder = placeholder ?? t("dataEntry.treeSelect.placeholder");
70
72
  const visible = React.useMemo(() => {
@@ -110,6 +112,53 @@ function TreeSelectRoot({
110
112
  e.stopPropagation();
111
113
  commit([]);
112
114
  };
115
+ const rovingKey = (activeKey && visible.some(({ node }) => node.value === activeKey) ? activeKey : null) ?? visible[0]?.node.value ?? null;
116
+ React.useEffect(() => {
117
+ if (!open || !activeKey) return;
118
+ treeItemRefs.current.get(activeKey)?.focus();
119
+ }, [activeKey, open, visible]);
120
+ const focusByOffset = (currentValue, delta) => {
121
+ const index = visible.findIndex(({ node }) => node.value === currentValue);
122
+ if (index === -1) return;
123
+ const next = visible[index + delta];
124
+ if (next) setActiveKey(next.node.value);
125
+ };
126
+ const onTreeItemKeyDown = (event, node, hasChildren, expanded) => {
127
+ switch (event.key) {
128
+ case "ArrowDown": {
129
+ event.preventDefault();
130
+ focusByOffset(node.value, 1);
131
+ break;
132
+ }
133
+ case "ArrowUp": {
134
+ event.preventDefault();
135
+ focusByOffset(node.value, -1);
136
+ break;
137
+ }
138
+ case "ArrowRight": {
139
+ event.preventDefault();
140
+ if (hasChildren && !expanded) {
141
+ toggleExpand(node.value);
142
+ } else if (hasChildren && expanded) {
143
+ focusByOffset(node.value, 1);
144
+ }
145
+ break;
146
+ }
147
+ case "ArrowLeft": {
148
+ event.preventDefault();
149
+ if (hasChildren && expanded) {
150
+ toggleExpand(node.value);
151
+ }
152
+ break;
153
+ }
154
+ case "Enter":
155
+ case " ": {
156
+ event.preventDefault();
157
+ toggleSelect(node);
158
+ break;
159
+ }
160
+ }
161
+ };
113
162
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
114
163
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
115
164
  Button,
@@ -127,13 +176,15 @@ function TreeSelectRoot({
127
176
  ),
128
177
  children: [
129
178
  /* @__PURE__ */ jsx("span", { className: "truncate", children: displayKeys.length ? displayLabel : resolvedPlaceholder }),
130
- /* @__PURE__ */ jsxs("span", { className: "ml-2 flex shrink-0 items-center gap-1", children: [
179
+ /* @__PURE__ */ jsxs("span", { className: "ms-2 flex shrink-0 items-center gap-1", children: [
131
180
  allowClear && displayKeys.length > 0 && !disabled && /* @__PURE__ */ jsx(
132
- X,
181
+ "button",
133
182
  {
134
- className: "size-4 opacity-50 hover:opacity-100",
135
- "aria-hidden": "true",
136
- onClick: clearValue
183
+ type: "button",
184
+ "aria-label": t("dataEntry.treeSelect.clear"),
185
+ className: "flex size-4 items-center justify-center rounded-sm opacity-50 hover:opacity-100 focus-visible:opacity-100",
186
+ onClick: clearValue,
187
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
137
188
  }
138
189
  ),
139
190
  /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-4 opacity-50", "aria-hidden": "true" })
@@ -162,16 +213,23 @@ function TreeSelectRoot({
162
213
  return /* @__PURE__ */ jsxs(
163
214
  "div",
164
215
  {
216
+ ref: (el) => {
217
+ treeItemRefs.current.set(node.value, el);
218
+ },
165
219
  role: "treeitem",
220
+ tabIndex: node.disabled ? -1 : rovingKey === node.value ? 0 : -1,
166
221
  "aria-expanded": hasChildren ? expanded : void 0,
167
222
  "aria-selected": isSelected,
223
+ onFocus: () => setActiveKey(node.value),
224
+ onKeyDown: (event) => onTreeItemKeyDown(event, node, hasChildren, expanded),
168
225
  className: cn(
169
- "flex items-center rounded-sm py-1.5 pr-2 text-sm outline-none",
226
+ "flex items-center rounded-sm py-1.5 pe-2 text-sm outline-none",
170
227
  "hover:bg-accent hover:text-accent-foreground",
228
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-1",
171
229
  isSelected && "bg-accent/60",
172
230
  node.disabled && "pointer-events-none opacity-50"
173
231
  ),
174
- style: { paddingLeft: `${depth * 1.25 + 0.5}rem` },
232
+ style: { paddingInlineStart: `${depth * 1.25 + 0.5}rem` },
175
233
  children: [
176
234
  /* @__PURE__ */ jsx(
177
235
  "button",
@@ -180,7 +238,7 @@ function TreeSelectRoot({
180
238
  tabIndex: -1,
181
239
  "aria-label": expanded ? t("dataEntry.treeSelect.collapse") : t("dataEntry.treeSelect.expand"),
182
240
  className: cn(
183
- "mr-1 flex size-5 shrink-0 items-center justify-center rounded-sm",
241
+ "me-1 flex size-5 shrink-0 items-center justify-center rounded-sm",
184
242
  !hasChildren && "invisible"
185
243
  ),
186
244
  onClick: () => toggleExpand(node.value),
@@ -192,6 +250,7 @@ function TreeSelectRoot({
192
250
  Checkbox,
193
251
  {
194
252
  checked: isSelected,
253
+ tabIndex: -1,
195
254
  disabled: Boolean(node.disabled) || Boolean(node.disableCheckbox),
196
255
  onCheckedChange: () => toggleSelect(node)
197
256
  }
@@ -201,7 +260,8 @@ function TreeSelectRoot({
201
260
  "button",
202
261
  {
203
262
  type: "button",
204
- className: "flex-1 truncate text-left",
263
+ tabIndex: -1,
264
+ className: "flex-1 truncate text-start",
205
265
  disabled: node.disabled,
206
266
  onClick: () => toggleSelect(node),
207
267
  children: node.label
@@ -1,8 +1,8 @@
1
1
  import { Input } from './chunk-VOHTRR5X.js';
2
2
  import { Button } from './chunk-M4PZNAMV.js';
3
3
  import { Popover, PopoverTrigger, PopoverContent } from './chunk-DY5C44UP.js';
4
- import { useTranslation } from './chunk-RLGHEV4A.js';
5
- import { normalizeHhmm, isValidHhmm } from './chunk-FXFJF4YA.js';
4
+ import { useTranslation, usePickerLocales } from './chunk-HTG5VHU7.js';
5
+ import { normalizeHhmm, isValidHhmm } from './chunk-6CSBMMZS.js';
6
6
  import { cn } from './chunk-U7N2A7A3.js';
7
7
  import * as React from 'react';
8
8
  import { Clock } from 'lucide-react';
@@ -23,49 +23,97 @@ function parseHhmm(value) {
23
23
  const [h, m] = normalized.split(":").map(Number);
24
24
  return { hour: h, minute: m };
25
25
  }
26
+ function to12h(hour24) {
27
+ const h = hour24 % 12;
28
+ return h === 0 ? 12 : h;
29
+ }
30
+ function from12h(hour12, meridiem) {
31
+ const base = hour12 % 12;
32
+ return meridiem === "pm" ? base + 12 : base;
33
+ }
26
34
  function TimeColumn({
27
35
  label,
28
36
  items,
29
37
  selected,
38
+ formatItem,
30
39
  onSelect
31
40
  }) {
32
41
  const listRef = React.useRef(null);
33
42
  React.useEffect(() => {
34
43
  listRef.current?.querySelector('[data-selected="true"]')?.scrollIntoView({ block: "center" });
35
44
  }, [selected]);
45
+ const moveFocus = (index) => {
46
+ const options = listRef.current?.querySelectorAll('[role="option"]');
47
+ options?.[index]?.focus();
48
+ };
49
+ const onKeyDown = (e, index) => {
50
+ switch (e.key) {
51
+ case "ArrowDown":
52
+ e.preventDefault();
53
+ moveFocus(Math.min(items.length - 1, index + 1));
54
+ break;
55
+ case "ArrowUp":
56
+ e.preventDefault();
57
+ moveFocus(Math.max(0, index - 1));
58
+ break;
59
+ case "Home":
60
+ e.preventDefault();
61
+ moveFocus(0);
62
+ break;
63
+ case "End":
64
+ e.preventDefault();
65
+ moveFocus(items.length - 1);
66
+ break;
67
+ case "Enter":
68
+ case " ":
69
+ e.preventDefault();
70
+ onSelect(items[index]);
71
+ break;
72
+ }
73
+ };
36
74
  return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
37
75
  /* @__PURE__ */ jsx("div", { className: "text-muted-foreground border-b px-1 py-1.5 text-center text-xs font-medium", children: label }),
38
76
  /* @__PURE__ */ jsx(
39
77
  "div",
40
78
  {
41
79
  ref: listRef,
80
+ role: "listbox",
81
+ "aria-label": label,
42
82
  className: "h-52 [scrollbar-width:thin] [scrollbar-gutter:stable] overflow-y-scroll overscroll-contain p-1",
43
- children: items.map((item) => /* @__PURE__ */ jsx(
44
- "button",
45
- {
46
- type: "button",
47
- "data-selected": item === selected,
48
- className: cn(
49
- "hover:bg-accent flex w-full items-center justify-center rounded-md py-1.5 text-sm tabular-nums transition-colors",
50
- item === selected && "bg-primary text-primary-foreground hover:bg-primary/90"
51
- ),
52
- onClick: () => {
53
- onSelect(item);
83
+ children: items.map((item, index) => {
84
+ const isSelected = item === selected;
85
+ return /* @__PURE__ */ jsx(
86
+ "button",
87
+ {
88
+ type: "button",
89
+ role: "option",
90
+ "aria-selected": isSelected,
91
+ "data-selected": isSelected,
92
+ tabIndex: isSelected ? 0 : -1,
93
+ className: cn(
94
+ "hover:bg-accent flex w-full items-center justify-center rounded-md py-1.5 text-sm tabular-nums transition-colors",
95
+ isSelected && "bg-primary text-primary-foreground hover:bg-primary/90"
96
+ ),
97
+ onClick: () => {
98
+ onSelect(item);
99
+ },
100
+ onKeyDown: (e) => onKeyDown(e, index),
101
+ children: formatItem(item)
54
102
  },
55
- children: pad2(item)
56
- },
57
- item
58
- ))
103
+ item
104
+ );
105
+ })
59
106
  }
60
107
  )
61
108
  ] });
62
109
  }
63
- function TimePickerPanel({ value, minuteStep, onChange, onDone }) {
110
+ function TimePickerPanel({ value, minuteStep, use12h, onChange, onDone }) {
64
111
  const { t } = useTranslation();
65
112
  const { hour, minute } = parseHhmm(value);
66
113
  const minutes = buildMinutes(minuteStep);
67
114
  const snappedMinute = minutes.includes(minute) ? minute : minutes[0];
68
115
  const [draft, setDraft] = React.useState(value);
116
+ const meridiem = hour >= 12 ? "pm" : "am";
69
117
  React.useEffect(() => {
70
118
  setDraft(value);
71
119
  }, [value]);
@@ -79,16 +127,20 @@ function TimePickerPanel({ value, minuteStep, onChange, onDone }) {
79
127
  onChange(next);
80
128
  onDone?.();
81
129
  };
82
- return /* @__PURE__ */ jsxs("div", { className: "w-36", children: [
130
+ const hourItems = use12h ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 24 }, (_, i) => i);
131
+ const selectedHourItem = use12h ? to12h(hour) : hour;
132
+ return /* @__PURE__ */ jsxs("div", { className: use12h ? "w-52" : "w-36", children: [
83
133
  /* @__PURE__ */ jsxs("div", { className: "divide-border flex divide-x", children: [
84
134
  /* @__PURE__ */ jsx(
85
135
  TimeColumn,
86
136
  {
87
137
  label: t("dataEntry.timePicker.hour"),
88
- items: Array.from({ length: 24 }, (_, i) => i),
89
- selected: hour,
138
+ items: hourItems,
139
+ selected: selectedHourItem,
140
+ formatItem: (h) => use12h ? String(h) : pad2(h),
90
141
  onSelect: (h) => {
91
- onChange(`${pad2(h)}:${pad2(snappedMinute)}`);
142
+ const hour24 = use12h ? from12h(h, meridiem) : h;
143
+ onChange(`${pad2(hour24)}:${pad2(snappedMinute)}`);
92
144
  }
93
145
  }
94
146
  ),
@@ -98,10 +150,25 @@ function TimePickerPanel({ value, minuteStep, onChange, onDone }) {
98
150
  label: t("dataEntry.timePicker.minute"),
99
151
  items: minutes,
100
152
  selected: snappedMinute,
153
+ formatItem: (m) => pad2(m),
101
154
  onSelect: (m) => {
102
155
  commit(`${pad2(hour)}:${pad2(m)}`);
103
156
  }
104
157
  }
158
+ ),
159
+ use12h && /* @__PURE__ */ jsx(
160
+ TimeColumn,
161
+ {
162
+ label: t("dataEntry.timePicker.meridiem"),
163
+ items: [0, 1],
164
+ selected: meridiem === "pm" ? 1 : 0,
165
+ formatItem: (m) => m === 1 ? t("dataEntry.timePicker.pm") : t("dataEntry.timePicker.am"),
166
+ onSelect: (m) => {
167
+ const nextMeridiem = m === 1 ? "pm" : "am";
168
+ const hour24 = from12h(to12h(hour), nextMeridiem);
169
+ onChange(`${pad2(hour24)}:${pad2(snappedMinute)}`);
170
+ }
171
+ }
105
172
  )
106
173
  ] }),
107
174
  /* @__PURE__ */ jsx("div", { className: "border-t p-2", children: /* @__PURE__ */ jsx(
@@ -142,6 +209,8 @@ function TimePicker({
142
209
  minuteStep = 5
143
210
  }) {
144
211
  const { t } = useTranslation();
212
+ const { timeFormat } = usePickerLocales();
213
+ const use12h = timeFormat === "12h";
145
214
  const [open, setOpen] = React.useState(false);
146
215
  const [internal, setInternal] = React.useState(defaultValue ?? "");
147
216
  const isControlled = controlledValue !== void 0;
@@ -169,7 +238,7 @@ function TimePicker({
169
238
  role: "combobox",
170
239
  "aria-expanded": open,
171
240
  "aria-haspopup": "dialog",
172
- className: "pr-10 tabular-nums",
241
+ className: "pe-10 tabular-nums",
173
242
  onChange: (event) => {
174
243
  setText(event.target.value);
175
244
  const normalized = normalizeHhmm(event.target.value);
@@ -191,7 +260,7 @@ function TimePicker({
191
260
  disabled,
192
261
  tabIndex: -1,
193
262
  "aria-label": t("dataEntry.timePicker.openPicker") ?? "Open time picker",
194
- className: "text-muted-foreground absolute inset-y-0 right-0 h-full px-2 hover:bg-transparent",
263
+ className: "text-muted-foreground absolute inset-y-0 end-0 h-full px-2 hover:bg-transparent",
195
264
  children: /* @__PURE__ */ jsx(Clock, { className: "size-4 shrink-0", "aria-hidden": "true" })
196
265
  }
197
266
  ) }),
@@ -200,6 +269,7 @@ function TimePicker({
200
269
  {
201
270
  value: value || "09:00",
202
271
  minuteStep,
272
+ use12h,
203
273
  onChange: (next) => {
204
274
  setValue(next);
205
275
  setText(next);
@@ -14,13 +14,13 @@ var toggleVariants = cva("ui-toggle", {
14
14
  },
15
15
  size: {
16
16
  sm: "ui-toggle-sm",
17
- default: "ui-toggle-default-size",
17
+ md: "ui-toggle-default-size",
18
18
  lg: "ui-toggle-lg"
19
19
  }
20
20
  },
21
21
  defaultVariants: {
22
22
  variant: "default",
23
- size: "default"
23
+ size: "md"
24
24
  }
25
25
  });
26
26
  var Toggle = React2.forwardRef(({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
@@ -131,6 +131,7 @@ function PasswordStrength({
131
131
  "data-state": checks[rule] ? "passed" : "failed",
132
132
  children: [
133
133
  checks[rule] ? /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(X, { "aria-hidden": "true" }),
134
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: checks[rule] ? "Passed: " : "Failed: " }),
134
135
  /* @__PURE__ */ jsx("span", { children: labelForRule(rule) })
135
136
  ]
136
137
  },
@@ -1,4 +1,4 @@
1
- import { formatDate, translateCurrent } from './chunk-FXFJF4YA.js';
1
+ import { formatDate, getSyncedLocale, translateCurrent } from './chunk-6CSBMMZS.js';
2
2
 
3
3
  // src/lib/format.ts
4
4
  function formatDateTime(value, options) {
@@ -10,38 +10,23 @@ function formatDateLong(value, options) {
10
10
  function formatRelative(value, options) {
11
11
  return formatDate(value, { ...options, kind: "relative" });
12
12
  }
13
- function formatBytes(n) {
13
+ function formatBytes(n, locale = getSyncedLocale()) {
14
14
  if (n == null) return "\u2014";
15
- if (n < 1024) return `${n} B`;
16
- if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
17
- if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
18
- return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
15
+ const num = (digits, scaled) => new Intl.NumberFormat(locale, {
16
+ minimumFractionDigits: digits,
17
+ maximumFractionDigits: digits
18
+ }).format(scaled);
19
+ if (n < 1024) return `${num(0, n)} B`;
20
+ if (n < 1024 * 1024) return `${num(1, n / 1024)} KB`;
21
+ if (n < 1024 * 1024 * 1024) return `${num(1, n / 1024 / 1024)} MB`;
22
+ return `${num(2, n / 1024 / 1024 / 1024)} GB`;
19
23
  }
20
- function formatCurrency(amountMinor, currency) {
24
+ function formatCurrency(amountMinor, currency, locale = getSyncedLocale()) {
21
25
  if (amountMinor == null || !currency) return "\u2014";
22
- const zeroDecimal = [
23
- "JPY",
24
- "VND",
25
- "KRW",
26
- "CLP",
27
- "ISK",
28
- "BIF",
29
- "DJF",
30
- "GNF",
31
- "KMF",
32
- "RWF",
33
- "XAF",
34
- "XOF",
35
- "XPF"
36
- ];
37
- const minorUnitDigits = zeroDecimal.includes(currency.toUpperCase()) ? 0 : 2;
26
+ const formatter = new Intl.NumberFormat(locale, { style: "currency", currency });
27
+ const minorUnitDigits = formatter.resolvedOptions().maximumFractionDigits ?? 2;
38
28
  const major = amountMinor / Math.pow(10, minorUnitDigits);
39
- return new Intl.NumberFormat("en-US", {
40
- style: "currency",
41
- currency,
42
- minimumFractionDigits: minorUnitDigits,
43
- maximumFractionDigits: minorUnitDigits
44
- }).format(major);
29
+ return formatter.format(major);
45
30
  }
46
31
  function shortId(id) {
47
32
  if (!id) return "\u2014";
@@ -1,68 +1,14 @@
1
- import { Select, SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectItem } from './chunk-CAEL2ZD2.js';
2
1
  import { Input } from './chunk-VOHTRR5X.js';
3
- import { Inline } from './chunk-TILFZBTE.js';
2
+ import { useTranslation } from './chunk-HTG5VHU7.js';
4
3
  import { cn } from './chunk-U7N2A7A3.js';
5
- import { jsxs, jsx } from 'react/jsx-runtime';
6
4
  import * as React2 from 'react';
7
5
  import { EyeOff, Eye, Minus, Star, X } from 'lucide-react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
7
  import { OTPInput, OTPInputContext } from 'input-otp';
9
8
 
10
- function CountryOptionLabel({
11
- country,
12
- showCode = false,
13
- className
14
- }) {
15
- const code = country.value ?? country.code ?? "";
16
- const text = country.nativeName != null && country.nativeName !== "" ? `${country.name} (${country.nativeName})` : country.name;
17
- return /* @__PURE__ */ jsxs(Inline, { gap: "xs", className: cn("items-center", className), children: [
18
- country.flagSvgPath != null && country.flagSvgPath !== "" && /* @__PURE__ */ jsx(
19
- "img",
20
- {
21
- src: country.flagSvgPath,
22
- alt: "",
23
- className: "h-3 w-5 shrink-0 rounded-sm object-cover",
24
- "aria-hidden": "true"
25
- }
26
- ),
27
- /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
28
- text,
29
- showCode && code !== "" && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
30
- " \xB7 ",
31
- code
32
- ] })
33
- ] })
34
- ] });
35
- }
36
- function CountrySelect({
37
- id,
38
- name,
39
- options,
40
- defaultValue,
41
- required = false,
42
- allowEmpty = false,
43
- emptyLabel = "\u2014",
44
- placeholder,
45
- invalid = false
46
- }) {
47
- const emptyValue = "0";
48
- const resolvedDefault = defaultValue && defaultValue !== "" ? defaultValue : emptyValue;
49
- return /* @__PURE__ */ jsxs(
50
- Select,
51
- {
52
- name,
53
- defaultValue: allowEmpty ? resolvedDefault : defaultValue ?? options[0]?.value,
54
- children: [
55
- /* @__PURE__ */ jsx(SelectTrigger, { id, className: "w-full", "aria-invalid": invalid, "aria-required": required, children: /* @__PURE__ */ jsx(SelectValue, { placeholder }) }),
56
- /* @__PURE__ */ jsx(SelectContent, { children: /* @__PURE__ */ jsxs(SelectGroup, { children: [
57
- allowEmpty && /* @__PURE__ */ jsx(SelectItem, { value: emptyValue, children: emptyLabel }),
58
- options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value ?? "", children: /* @__PURE__ */ jsx(CountryOptionLabel, { country: option }) }, option.value))
59
- ] }) })
60
- ]
61
- }
62
- );
63
- }
64
9
  var PasswordInput = React2.forwardRef(
65
10
  ({ className, ...props }, ref) => {
11
+ const { t } = useTranslation();
66
12
  const [visible, setVisible] = React2.useState(false);
67
13
  return /* @__PURE__ */ jsxs("div", { className: "ui-password-input", "data-slot": "password-input", children: [
68
14
  /* @__PURE__ */ jsx(
@@ -80,8 +26,8 @@ var PasswordInput = React2.forwardRef(
80
26
  type: "button",
81
27
  className: "ui-password-input-toggle",
82
28
  onClick: () => setVisible((v) => !v),
83
- "aria-label": visible ? "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u96A0\u3059" : "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u8868\u793A",
84
- tabIndex: -1,
29
+ "aria-label": visible ? t("ui.passwordInput.hide") : t("ui.passwordInput.show"),
30
+ "aria-pressed": visible,
85
31
  children: visible ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": "true" })
86
32
  }
87
33
  )
@@ -145,6 +91,7 @@ var Rating = React2.forwardRef(
145
91
  className,
146
92
  ...rest
147
93
  }, ref) => {
94
+ const { t } = useTranslation();
148
95
  const [internal, setInternal] = React2.useState(defaultValue);
149
96
  const current = value ?? internal;
150
97
  const [hover, setHover] = React2.useState(null);
@@ -155,6 +102,31 @@ var Rating = React2.forwardRef(
155
102
  if (value === void 0) setInternal(next);
156
103
  onValueChange?.(next);
157
104
  };
105
+ const focusableStar = current > 0 ? current : 1;
106
+ const onKeyDown = (e, star) => {
107
+ if (!interactive) return;
108
+ let next = null;
109
+ switch (e.key) {
110
+ case "ArrowRight":
111
+ case "ArrowUp":
112
+ next = Math.min(max, star + 1);
113
+ break;
114
+ case "ArrowLeft":
115
+ case "ArrowDown":
116
+ next = Math.max(1, star - 1);
117
+ break;
118
+ case "Home":
119
+ next = 1;
120
+ break;
121
+ case "End":
122
+ next = max;
123
+ break;
124
+ default:
125
+ return;
126
+ }
127
+ e.preventDefault();
128
+ select(next);
129
+ };
158
130
  return /* @__PURE__ */ jsxs(
159
131
  "div",
160
132
  {
@@ -162,7 +134,7 @@ var Rating = React2.forwardRef(
162
134
  role: "radiogroup",
163
135
  "data-slot": "rating",
164
136
  className: cn("ui-rating", className),
165
- "aria-label": rest["aria-label"] ?? "\u8A55\u4FA1",
137
+ "aria-label": rest["aria-label"] ?? t("ui.rating.ariaLabel"),
166
138
  children: [
167
139
  Array.from({ length: max }, (_, i) => i + 1).map((star) => /* @__PURE__ */ jsx(
168
140
  "button",
@@ -170,13 +142,14 @@ var Rating = React2.forwardRef(
170
142
  type: "button",
171
143
  role: "radio",
172
144
  "aria-checked": current === star,
173
- "aria-label": String(star),
145
+ "aria-label": t("ui.rating.starLabel", { star, max }),
174
146
  disabled: disabled || readOnly,
175
- tabIndex: interactive ? 0 : -1,
147
+ tabIndex: interactive && star === focusableStar ? 0 : -1,
176
148
  className: cn("ui-rating-star", star <= display && "ui-rating-star-filled"),
177
149
  onMouseEnter: () => interactive && setHover(star),
178
150
  onMouseLeave: () => setHover(null),
179
151
  onClick: () => select(star),
152
+ onKeyDown: (e) => onKeyDown(e, star),
180
153
  children: /* @__PURE__ */ jsx(Star, { "aria-hidden": "true" })
181
154
  },
182
155
  star
@@ -189,7 +162,18 @@ var Rating = React2.forwardRef(
189
162
  );
190
163
  Rating.displayName = "Rating";
191
164
  var TagInput = React2.forwardRef(
192
- ({ value, defaultValue = [], onValueChange, placeholder, disabled, name, className }, ref) => {
165
+ ({
166
+ value,
167
+ defaultValue = [],
168
+ onValueChange,
169
+ placeholder,
170
+ disabled,
171
+ name,
172
+ className,
173
+ id,
174
+ "aria-label": ariaLabel
175
+ }, ref) => {
176
+ const { t } = useTranslation();
193
177
  const [internal, setInternal] = React2.useState(defaultValue);
194
178
  const tags = value ?? internal;
195
179
  const [draft, setDraft] = React2.useState("");
@@ -218,28 +202,39 @@ var TagInput = React2.forwardRef(
218
202
  "data-slot": "tag-input",
219
203
  className: cn("ui-tag-input", disabled && "ui-tag-input-disabled", className),
220
204
  children: [
221
- tags.map((tag, i) => /* @__PURE__ */ jsxs("span", { className: "ui-tag-input-chip", "data-slot": "tag-input-chip", children: [
222
- tag,
223
- !disabled ? /* @__PURE__ */ jsx(
224
- "button",
225
- {
226
- type: "button",
227
- className: "ui-tag-input-remove",
228
- "aria-label": `${tag} \u3092\u524A\u9664`,
229
- onClick: () => removeAt(i),
230
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
231
- }
232
- ) : null
233
- ] }, tag)),
205
+ tags.length > 0 ? /* @__PURE__ */ jsx("ul", { role: "list", className: "ui-tag-input-list", "data-slot": "tag-input-list", children: tags.map((tag, i) => /* @__PURE__ */ jsxs(
206
+ "li",
207
+ {
208
+ role: "listitem",
209
+ className: "ui-tag-input-chip",
210
+ "data-slot": "tag-input-chip",
211
+ children: [
212
+ tag,
213
+ !disabled ? /* @__PURE__ */ jsx(
214
+ "button",
215
+ {
216
+ type: "button",
217
+ className: "ui-tag-input-remove",
218
+ "aria-label": t("ui.tagInput.removeTag", { tag }),
219
+ onClick: () => removeAt(i),
220
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
221
+ }
222
+ ) : null
223
+ ]
224
+ },
225
+ tag
226
+ )) }) : null,
234
227
  /* @__PURE__ */ jsx(
235
228
  "input",
236
229
  {
237
230
  ref,
231
+ id,
238
232
  type: "text",
239
233
  className: "ui-tag-input-field",
240
234
  value: draft,
241
235
  placeholder: tags.length === 0 ? placeholder : void 0,
242
236
  disabled,
237
+ "aria-label": ariaLabel ?? t("ui.tagInput.inputLabel"),
243
238
  onChange: (e) => setDraft(e.target.value),
244
239
  onKeyDown,
245
240
  onBlur: () => {
@@ -250,6 +245,7 @@ var TagInput = React2.forwardRef(
250
245
  }
251
246
  }
252
247
  ),
248
+ /* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "sr-only", "data-slot": "tag-input-status", children: t("ui.tagInput.tagCount", { count: tags.length }) }),
253
249
  name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: tags.join(",") }) : null
254
250
  ]
255
251
  }
@@ -258,4 +254,4 @@ var TagInput = React2.forwardRef(
258
254
  );
259
255
  TagInput.displayName = "TagInput";
260
256
 
261
- export { CountryOptionLabel, CountrySelect, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, PasswordInput, Rating, TagInput };
257
+ export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, PasswordInput, Rating, TagInput };