@godxjp/ui 20.2.1 → 22.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 (141) hide show
  1. package/dist/app/app-provider.js +2 -1
  2. package/dist/components/data-display/badge.d.ts +28 -1
  3. package/dist/components/data-display/badge.js +2 -0
  4. package/dist/components/data-display/chat-bubble.d.ts +30 -0
  5. package/dist/components/data-display/chat-bubble.js +229 -0
  6. package/dist/components/data-display/data-table.d.ts +18 -2
  7. package/dist/components/data-display/data-table.js +4 -0
  8. package/dist/components/data-display/descriptions.js +4 -1
  9. package/dist/components/data-display/index.d.ts +5 -3
  10. package/dist/components/data-display/index.js +5 -2
  11. package/dist/components/data-display/popover.d.ts +19 -1
  12. package/dist/components/data-display/popover.js +12 -3
  13. package/dist/components/data-display/progress.d.ts +37 -0
  14. package/dist/components/data-display/progress.js +57 -5
  15. package/dist/components/data-display/scroll-area.js +26 -1
  16. package/dist/components/data-display/tree.d.ts +8 -0
  17. package/dist/components/data-display/tree.js +426 -0
  18. package/dist/components/data-entry/chat-composer.d.ts +50 -0
  19. package/dist/components/data-entry/chat-composer.js +163 -0
  20. package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
  21. package/dist/components/data-entry/chat-suggestion.js +285 -0
  22. package/dist/components/data-entry/date-picker.d.ts +10 -3
  23. package/dist/components/data-entry/date-picker.js +503 -238
  24. package/dist/components/data-entry/form-field.d.ts +1 -1
  25. package/dist/components/data-entry/form-field.js +4 -1
  26. package/dist/components/data-entry/form.js +10 -1
  27. package/dist/components/data-entry/index.d.ts +4 -6
  28. package/dist/components/data-entry/index.js +4 -6
  29. package/dist/components/data-entry/label.d.ts +14 -1
  30. package/dist/components/data-entry/label.js +6 -1
  31. package/dist/components/data-entry/radio.d.ts +27 -6
  32. package/dist/components/data-entry/radio.js +88 -43
  33. package/dist/components/data-entry/search-select.js +8 -1
  34. package/dist/components/data-entry/select.js +2 -0
  35. package/dist/components/data-entry/switch.d.ts +6 -3
  36. package/dist/components/data-entry/switch.js +34 -11
  37. package/dist/components/data-entry/textarea.js +3 -1
  38. package/dist/components/data-entry/tree-utils.d.ts +10 -48
  39. package/dist/components/data-entry/tree-utils.js +1 -154
  40. package/dist/components/layout/mobile-shell.d.ts +1 -1
  41. package/dist/components/layout/mobile-shell.js +2 -0
  42. package/dist/components/navigation/menubar.d.ts +10 -3
  43. package/dist/components/navigation/steps.d.ts +1 -1
  44. package/dist/components/navigation/steps.js +3 -1
  45. package/dist/components/navigation/tabs-scroll.d.ts +27 -0
  46. package/dist/components/navigation/tabs-scroll.js +52 -1
  47. package/dist/components/navigation/tabs.d.ts +2 -2
  48. package/dist/components/navigation/tabs.js +88 -23
  49. package/dist/components/ui/index.d.ts +0 -1
  50. package/dist/components/ui/index.js +0 -1
  51. package/dist/components/ui/segmented.d.ts +15 -1
  52. package/dist/components/ui/segmented.js +13 -6
  53. package/dist/i18n/messages/en.json +48 -2
  54. package/dist/i18n/messages/ja.json +46 -2
  55. package/dist/i18n/messages/vi.json +47 -2
  56. package/dist/lib/datetime/picker-format.d.ts +18 -0
  57. package/dist/lib/datetime/picker-format.js +35 -1
  58. package/dist/lib/field-a11y.d.ts +12 -0
  59. package/dist/lib/field-a11y.js +14 -1
  60. package/dist/lib/tree.d.ts +53 -0
  61. package/dist/lib/tree.js +155 -0
  62. package/dist/props/components/data-display.prop.d.ts +194 -1
  63. package/dist/props/components/data-entry.prop.d.ts +307 -99
  64. package/dist/props/components/index.d.ts +1 -1
  65. package/dist/props/components/layout.prop.d.ts +12 -1
  66. package/dist/props/components/navigation.prop.d.ts +76 -1
  67. package/dist/props/registry.d.ts +156 -19
  68. package/dist/props/registry.js +218 -18
  69. package/dist/props/vocabulary/data.prop.d.ts +12 -0
  70. package/dist/props/vocabulary/index.d.ts +2 -2
  71. package/dist/props/vocabulary/layout.prop.d.ts +11 -0
  72. package/dist/styles/badge-layout.css +4 -0
  73. package/dist/styles/card-layout.css +6 -6
  74. package/dist/styles/control.css +22 -6
  75. package/dist/styles/data-display-layout.css +385 -68
  76. package/dist/styles/data-entry-layout.css +64 -0
  77. package/dist/styles/focus-ring.css +4 -2
  78. package/dist/styles/navigation-layout.css +25 -0
  79. package/dist/styles/shell-layout.css +7 -1
  80. package/dist/styles/table-layout.css +34 -0
  81. package/dist/tokens/base.css +3 -0
  82. package/dist/tokens/components/chat-bubble.css +36 -0
  83. package/dist/tokens/components/chat-composer.css +19 -0
  84. package/dist/tokens/components/data-display.css +9 -6
  85. package/dist/tokens/components/descriptions.css +4 -0
  86. package/dist/tokens/components/navigation.css +6 -0
  87. package/dist/tokens/components/segmented.css +0 -2
  88. package/dist/tokens/components/shell.css +3 -0
  89. package/dist/tokens/components/table.css +3 -0
  90. package/dist/tokens/components/tree.css +27 -0
  91. package/dist/tokens/foundation.css +7 -0
  92. package/docs/COMPONENTS.md +14 -14
  93. package/docs/CONSUMER-RULES.md +24 -1
  94. package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
  95. package/docs/FRAME-COVERAGE-REPORT.md +9 -7
  96. package/docs/TOKENS.md +65 -0
  97. package/docs/data-display/badge.tsx +37 -0
  98. package/docs/data-display/chat-bubble.tsx +397 -0
  99. package/docs/data-display/data-table/index.tsx +18 -0
  100. package/docs/data-display/popover.tsx +41 -1
  101. package/docs/data-display/progress.tsx +28 -0
  102. package/docs/data-display/timeline.tsx +46 -0
  103. package/docs/data-display/tree.tsx +394 -0
  104. package/docs/data-entry/calendar.tsx +2 -2
  105. package/docs/data-entry/chat-composer.tsx +464 -0
  106. package/docs/data-entry/chat-suggestion.tsx +301 -0
  107. package/docs/data-entry/date-picker.tsx +143 -3
  108. package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
  109. package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
  110. package/docs/data-entry/form.tsx +4 -4
  111. package/docs/data-entry/number-input.tsx +31 -0
  112. package/docs/data-entry/segmented.tsx +103 -1
  113. package/docs/layout/mobile-shell.tsx +5 -0
  114. package/docs/navigation/tabs.tsx +149 -0
  115. package/docs/roadmap/ai-chat-components.md +207 -0
  116. package/docs/roadmap/antd-parity.md +155 -0
  117. package/docs/roadmap/badge-tag-chip-count.md +172 -0
  118. package/docs/roadmap/list-masonry.md +159 -0
  119. package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
  120. package/docs/roadmap/parity-audit-data-entry.md +357 -0
  121. package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
  122. package/docs/roadmap/parity-backlog.md +78 -0
  123. package/docs/roadmap/tree-components.md +151 -0
  124. package/docs/showcase/acme-portal.tsx +7 -0
  125. package/docs/showcase/table-tree-rows.tsx +4 -4
  126. package/package.json +13 -23
  127. package/scripts/ui-audit.mjs +17 -4
  128. package/dist/components/data-display/tree-list.d.ts +0 -13
  129. package/dist/components/data-display/tree-list.js +0 -26
  130. package/dist/components/data-entry/date-range-picker.d.ts +0 -11
  131. package/dist/components/data-entry/date-range-picker.js +0 -349
  132. package/dist/components/data-entry/month-picker.d.ts +0 -10
  133. package/dist/components/data-entry/month-picker.js +0 -241
  134. package/dist/components/data-entry/month-range-picker.d.ts +0 -10
  135. package/dist/components/data-entry/month-range-picker.js +0 -301
  136. package/dist/components/ui/date-range-picker.d.ts +0 -1
  137. package/dist/components/ui/date-range-picker.js +0 -2
  138. package/docs/data-display/tree-list.tsx +0 -107
  139. package/docs/data-entry/date-range-picker.tsx +0 -175
  140. package/docs/data-entry/month-picker.tsx +0 -101
  141. package/docs/data-entry/month-range-picker.tsx +0 -115
@@ -1,26 +1,37 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react";
4
+ import { ArrowRight, CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react";
5
5
  import { usePickerLocales, useTranslation } from "../../i18n/use-translation.js";
6
- import { toIsoDate } from "../../lib/datetime/parse.js";
7
6
  import {
8
7
  formatPickerDate,
8
+ parseIsoPeriod,
9
9
  parsePickerDate,
10
10
  pickerDateAllowed,
11
- pickerPeriodStart
11
+ pickerPeriodStart,
12
+ toIsoPeriod
12
13
  } from "../../lib/datetime/picker-format.js";
13
14
  import { Button } from "../general/button.js";
14
15
  import { Flex } from "../layout/flex.js";
15
16
  import { TimePicker } from "./time-picker.js";
16
17
  import { useControlledLatch } from "../../lib/hooks.js";
17
- import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
18
+ import { pickFieldA11y, pickGroupFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
18
19
  import { cn } from "../../lib/utils.js";
19
20
  import { resolveAllowClear } from "./control-surface.js";
21
+ import { CONTROL_STATUS_CHROME_CLASS, CONTROL_VARIANT_CHROME_CLASS } from "./control-appearance.js";
20
22
  import { Input } from "./input.js";
21
23
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
22
24
  import { Calendar } from "./calendar.js";
23
25
  const ISO_HINT = "yyyy-mm-dd";
26
+ const PERIOD_PICKERS = /* @__PURE__ */ new Set(["month", "quarter", "year"]);
27
+ const PERIOD_COLUMNS = 3;
28
+ const PERIOD_HINT = {
29
+ date: ISO_HINT,
30
+ week: "yyyy-Www",
31
+ month: "yyyy-mm",
32
+ quarter: "yyyy-mm",
33
+ year: "yyyy"
34
+ };
24
35
  function DatePicker(props) {
25
36
  const {
26
37
  value: valueProp,
@@ -44,8 +55,11 @@ function DatePicker(props) {
44
55
  maxDate,
45
56
  showWeek,
46
57
  showTime,
58
+ defaultPickerValue,
59
+ pickerValue,
47
60
  presets,
48
61
  multiple = false,
62
+ range = false,
49
63
  picker = "date",
50
64
  order = true,
51
65
  needConfirm = Boolean(showTime),
@@ -60,17 +74,23 @@ function DatePicker(props) {
60
74
  status,
61
75
  variant,
62
76
  ref,
63
- ...ariaProps
77
+ ...rest
64
78
  } = props;
79
+ const { allowEmpty = [true, true], ...ariaProps } = rest;
65
80
  const format = formatProp ?? (typeof showTime === "object" && showTime.showSeconds ? "yyyy-MM-dd HH:mm:ss" : void 0);
66
81
  const { t } = useTranslation();
67
82
  const { dayPickerLocale, locale } = usePickerLocales(localeProp);
83
+ const isPeriod = PERIOD_PICKERS.has(picker);
84
+ const disabledEdges = Array.isArray(disabled) ? disabled : [Boolean(disabled), Boolean(disabled)];
85
+ const allDisabled = disabledEdges[0] && disabledEdges[1];
86
+ const anyDisabled = disabledEdges[0] || disabledEdges[1];
87
+ const edgeDisabled = (edge) => disabledEdges[edge === "from" ? 0 : 1];
68
88
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
69
- const open = !disabled && (openProp ?? internalOpen);
89
+ const open = !allDisabled && (openProp ?? internalOpen);
70
90
  const [pending, setPending] = React.useState();
71
91
  const [hasPending, setHasPending] = React.useState(false);
72
92
  const setOpen = (next) => {
73
- if (disabled && next) return;
93
+ if (allDisabled && next) return;
74
94
  if (openProp === void 0) setInternalOpen(next);
75
95
  onOpenChange?.(next);
76
96
  if (!next) {
@@ -85,41 +105,91 @@ function DatePicker(props) {
85
105
  const minimum = minDate ?? fromDate;
86
106
  const maximum = maxDate ?? toDate;
87
107
  const allowed = (date) => pickerDateAllowed(date, minimum, maximum, disabledDate);
88
- const display = (date) => Array.isArray(date) ? date.map((day) => formatPickerDate(day, format, locale)).join(", ") : formatPickerDate(date, format, locale, Boolean(showTime));
89
- const parse = (text2) => parsePickerDate(text2, format, parseFormat, Boolean(showTime));
90
- const fieldA11y = pickFieldA11y(ariaProps);
108
+ const display = (date) => format || showTime ? formatPickerDate(date, format, locale, Boolean(showTime)) : toIsoPeriod(date, picker);
109
+ const displayAll = (date) => Array.isArray(date) ? date.map((day) => display(day)).join(", ") : display(date);
110
+ const parse = (text2) => parsePickerDate(text2, format, parseFormat, Boolean(showTime)) ?? parseIsoPeriod(text2, picker);
111
+ const periodDate = (date) => pickerPeriodStart(date, picker, dayPickerLocale);
112
+ const fieldA11y = range ? pickGroupFieldA11y(ariaProps) : pickFieldA11y(ariaProps);
91
113
  const identity = useFieldIdentity({ id, name, "data-field": fieldA11y["data-field"] });
92
114
  const resolvedName = name ?? identity.name;
93
115
  const resolvedField = fieldA11y["data-field"] ?? identity["data-field"];
94
116
  const reactId = React.useId();
95
- const dialogId = `${id ?? reactId}-dialog`;
117
+ const rootId = id ?? reactId;
118
+ const dialogId = `${rootId}-dialog`;
119
+ const fromId = `${rootId}-from`;
120
+ const toId = `${rootId}-to`;
96
121
  const isControlled = useControlledLatch(valueProp !== void 0);
97
- const [internalValue, setInternalValue] = React.useState(defaultValue);
122
+ const [internalValue, setInternalValue] = React.useState(
123
+ defaultValue
124
+ );
98
125
  const value = isControlled ? valueProp : internalValue;
99
126
  const working = hasPending ? pending : value;
100
- const selectedDate = Array.isArray(working) ? working[0] : working;
101
- const [viewYear, setViewYear] = React.useState((selectedDate ?? /* @__PURE__ */ new Date()).getFullYear());
102
- const periodDate = (date) => pickerPeriodStart(date, picker, dayPickerLocale);
127
+ const rangeValue = range ? working : void 0;
128
+ const selectedDate = range ? rangeValue?.from : Array.isArray(working) ? working[0] : working;
103
129
  const emit = (next) => {
104
130
  if (!isControlled) setInternalValue(next);
105
- if (props.multiple)
106
- props.onValueChange?.(Array.isArray(next) ? next : next ? [next] : void 0);
107
- else props.onValueChange?.(Array.isArray(next) ? next[0] : next);
131
+ const onValueChange = props.onValueChange;
132
+ if (range) onValueChange?.(next);
133
+ else if (multiple) onValueChange?.(Array.isArray(next) ? next : next ? [next] : void 0);
134
+ else onValueChange?.(Array.isArray(next) ? next[0] : next);
108
135
  };
109
- const [text, setText] = React.useState(() => display(value));
136
+ const committedSingle = range ? void 0 : value;
137
+ const committedRange = range ? value : void 0;
138
+ const [text, setText] = React.useState(() => range ? "" : displayAll(committedSingle));
139
+ const [fromText, setFromText] = React.useState(() => display(committedRange?.from));
140
+ const [toText, setToText] = React.useState(() => display(committedRange?.to));
141
+ const committedFrom = committedRange?.from;
142
+ const committedTo = committedRange?.to;
110
143
  React.useEffect(() => {
111
- setText(
112
- Array.isArray(value) ? value.map((date) => formatPickerDate(date, format, locale)).join(", ") : formatPickerDate(value, format, locale, Boolean(showTime))
113
- );
114
- }, [value, format, locale, showTime]);
115
- const resolvedPlaceholder = placeholder ?? t("dataEntry.datePicker.placeholder") ?? ISO_HINT;
144
+ if (range) {
145
+ setFromText(display(committedFrom));
146
+ setToText(display(committedTo));
147
+ } else {
148
+ setText(displayAll(committedSingle));
149
+ }
150
+ }, [range, value, committedFrom, committedTo, format, locale, showTime]);
151
+ const valueAnchor = range ? committedRange?.from : Array.isArray(committedSingle) ? committedSingle[0] : committedSingle;
152
+ const [internalView, setInternalView] = React.useState(
153
+ () => defaultPickerValue ?? valueAnchor
154
+ );
155
+ const viewAnchor = pickerValue ?? internalView ?? /* @__PURE__ */ new Date();
156
+ const viewYear = viewAnchor.getFullYear();
157
+ const setViewAnchor = (next) => {
158
+ if (pickerValue === void 0) setInternalView(next);
159
+ };
160
+ const setViewYear = (year) => setViewAnchor(new Date(year, viewAnchor.getMonth(), 1));
161
+ React.useEffect(() => {
162
+ if (open) setInternalView(defaultPickerValue ?? valueAnchor);
163
+ }, [open]);
164
+ const resolvedPlaceholder = placeholder ?? (isPeriod ? t("dataEntry.monthPicker.placeholder") : range ? t("dataEntry.dateRangePicker.placeholder") : t("dataEntry.datePicker.placeholder")) ?? PERIOD_HINT[picker];
165
+ const clearControl = resolveAllowClear(allowClear, true, t("common.clear") ?? "Clear");
166
+ const showClear = clearControl.enabled && !anyDisabled && (range ? allowEmpty.every(Boolean) && Boolean(committedRange?.from || committedRange?.to) : text !== "");
116
167
  const clear = () => {
117
168
  emit(void 0);
118
169
  setText("");
170
+ setFromText("");
171
+ setToText("");
119
172
  };
120
- const clearControl = resolveAllowClear(allowClear, true, t("common.clear") ?? "Clear");
121
- const showClear = clearControl.enabled && text !== "" && !disabled;
173
+ const validRange = (next) => Boolean(
174
+ next && (next.from ? allowed(next.from) : allowEmpty[0]) && (next.to ? allowed(next.to) : allowEmpty[1])
175
+ );
122
176
  const choose = (input) => {
177
+ if (range) {
178
+ let next = input;
179
+ if (anyDisabled && next)
180
+ next = {
181
+ from: disabledEdges[0] ? committedRange?.from : next.from,
182
+ to: disabledEdges[1] ? committedRange?.to : next.to
183
+ };
184
+ if (next?.from && !allowed(next.from) || next?.to && !allowed(next.to)) return;
185
+ if (order && next?.from && next.to && next.from > next.to)
186
+ next = { from: next.to, to: next.from };
187
+ if (needConfirm) {
188
+ setPending(next);
189
+ setHasPending(true);
190
+ } else emit(next);
191
+ return;
192
+ }
123
193
  let date = input;
124
194
  if (Array.isArray(date)) {
125
195
  if (date.some((day) => !allowed(day))) return;
@@ -129,7 +199,8 @@ function DatePicker(props) {
129
199
  if (!allowed(date) || !needConfirm && !timeAllowed(date)) return;
130
200
  if (multiple) {
131
201
  const current = Array.isArray(working) ? working : [];
132
- date = current.some((day) => +day === +date) ? current.filter((day) => +day !== +date) : [...current, date];
202
+ const picked = date;
203
+ date = current.some((day) => +day === +picked) ? current.filter((day) => +day !== +picked) : [...current, picked];
133
204
  if (order) date.sort((a, b) => +a - +b);
134
205
  }
135
206
  }
@@ -138,7 +209,7 @@ function DatePicker(props) {
138
209
  setHasPending(true);
139
210
  } else {
140
211
  emit(date);
141
- setText(display(date));
212
+ setText(displayAll(date));
142
213
  }
143
214
  };
144
215
  const commit = (raw) => {
@@ -149,22 +220,410 @@ function DatePicker(props) {
149
220
  const parsed = parse(raw);
150
221
  if (parsed && allowed(parsed)) choose(parsed);
151
222
  };
152
- return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
153
- (format || showTime || multiple || needConfirm || preserveInvalidOnBlur) && resolvedName ? /* @__PURE__ */ jsx(
223
+ const commitEdge = (edge, raw) => {
224
+ if (edgeDisabled(edge)) return;
225
+ const trimmed = raw.trim();
226
+ const parsed = parse(trimmed);
227
+ if (trimmed && (!parsed || !allowed(parsed))) return;
228
+ if (!trimmed && !allowEmpty[edge === "from" ? 0 : 1]) return;
229
+ const base = working;
230
+ const next = { from: base?.from, to: base?.to, [edge]: parsed && periodDate(parsed) };
231
+ choose(next.from || next.to ? next : void 0);
232
+ };
233
+ const keyHandlers = (onEnter) => ({
234
+ onKeyDown: (event) => {
235
+ if (event.key === "ArrowDown") {
236
+ event.preventDefault();
237
+ setOpen(true);
238
+ } else if (event.key === "Enter") {
239
+ event.preventDefault();
240
+ onEnter();
241
+ } else if (event.key === "Escape" && open) {
242
+ setOpen(false);
243
+ }
244
+ }
245
+ });
246
+ const enterSingle = () => {
247
+ const parsed = parse(text);
248
+ if (parsed && allowed(parsed) && timeAllowed(parsed)) {
249
+ const next = periodDate(parsed);
250
+ emit(next);
251
+ setText(display(parsed));
252
+ }
253
+ setOpen(false);
254
+ };
255
+ const enterEdge = (edge) => () => {
256
+ commitEdge(edge, edge === "from" ? fromText : toText);
257
+ setOpen(false);
258
+ };
259
+ const displayIsCanonical = !format && !showTime && !multiple && !needConfirm && !preserveInvalidOnBlur && picker === "date";
260
+ const isoValue = (date) => showTime ? date?.toISOString() ?? "" : toIsoPeriod(date, picker);
261
+ const hiddenFields = displayIsCanonical ? null : range ? /* @__PURE__ */ jsxs(Fragment, { children: [
262
+ /* @__PURE__ */ jsx(
263
+ "input",
264
+ {
265
+ type: "hidden",
266
+ disabled: allDisabled,
267
+ name: `${resolvedName}_from`,
268
+ value: isoValue(committedRange?.from)
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsx(
154
272
  "input",
155
273
  {
156
274
  type: "hidden",
157
- disabled,
158
- name: resolvedName,
159
- value: showTime ? (Array.isArray(value) ? value[0] : value)?.toISOString() ?? "" : Array.isArray(value) ? value.map(toIsoDate).join(",") : toIsoDate(value)
275
+ disabled: allDisabled,
276
+ name: `${resolvedName}_to`,
277
+ value: isoValue(committedRange?.to)
278
+ }
279
+ )
280
+ ] }) : /* @__PURE__ */ jsx(
281
+ "input",
282
+ {
283
+ type: "hidden",
284
+ disabled: allDisabled,
285
+ name: resolvedName,
286
+ value: Array.isArray(committedSingle) ? committedSingle.map((day) => isoValue(day)).join(",") : isoValue(committedSingle)
287
+ }
288
+ );
289
+ const periodCells = picker === "quarter" ? 4 : 12;
290
+ const periodPage = (year) => Array.from(
291
+ { length: periodCells },
292
+ (_, index) => picker === "year" ? new Date(year + index, 0, 1) : new Date(year, index * (picker === "quarter" ? 3 : 1), 1)
293
+ );
294
+ const periodDateAt = (index) => periodPage(viewYear)[index];
295
+ const periodRow = (row) => Array.from({ length: PERIOD_COLUMNS }, (_, column) => row * PERIOD_COLUMNS + column).filter(
296
+ (index) => index < periodCells
297
+ );
298
+ const pageStep = picker === "year" ? periodCells : 1;
299
+ const prevDisabled = !pickerDateAllowed(
300
+ periodPage(viewYear - pageStep)[periodCells - 1],
301
+ minimum,
302
+ void 0
303
+ );
304
+ const nextDisabled = !pickerDateAllowed(periodPage(viewYear + pageStep)[0], void 0, maximum);
305
+ const periodLabel = (date, index) => picker === "year" ? new Intl.DateTimeFormat(locale, { year: "numeric" }).format(date) : picker === "quarter" ? t("dataEntry.datePicker.quarter", { quarter: index + 1 }) ?? `Q${index + 1}` : new Intl.DateTimeFormat(locale, { month: "short" }).format(date);
306
+ const periodPick = (date) => {
307
+ if (!range) {
308
+ choose(date);
309
+ if (!needConfirm && !multiple) setOpen(false);
310
+ return;
311
+ }
312
+ const base = working;
313
+ const pendingFrom = base?.from && !base.to ? base.from : void 0;
314
+ if (!pendingFrom) {
315
+ choose({ from: date, to: void 0 });
316
+ return;
317
+ }
318
+ choose({ from: pendingFrom, to: date });
319
+ if (!needConfirm) setOpen(false);
320
+ };
321
+ const periodPanel = /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-panel", children: [
322
+ /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-nav", children: [
323
+ /* @__PURE__ */ jsx(
324
+ Button,
325
+ {
326
+ type: "button",
327
+ variant: "outline",
328
+ size: "icon-sm",
329
+ disabled: prevDisabled,
330
+ "aria-label": t("dataEntry.monthPicker.previousYear") ?? "Previous year",
331
+ className: "ui-month-picker-nav-button",
332
+ onClick: () => setViewYear(viewYear - pageStep),
333
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ui-month-picker-icon", "aria-hidden": "true" })
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: new Intl.NumberFormat(locale, { useGrouping: false }).format(viewYear) }),
337
+ /* @__PURE__ */ jsx(
338
+ Button,
339
+ {
340
+ type: "button",
341
+ variant: "outline",
342
+ size: "icon-sm",
343
+ disabled: nextDisabled,
344
+ "aria-label": t("dataEntry.monthPicker.nextYear") ?? "Next year",
345
+ className: "ui-month-picker-nav-button",
346
+ onClick: () => setViewYear(viewYear + pageStep),
347
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "ui-month-picker-icon", "aria-hidden": "true" })
348
+ }
349
+ )
350
+ ] }),
351
+ /* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "ui-month-picker-grid", children: Array.from({ length: Math.ceil(periodCells / PERIOD_COLUMNS) }, (_, row) => /* @__PURE__ */ jsx("div", { role: "row", className: "ui-month-picker-row", children: periodRow(row).map((index) => {
352
+ const date = periodDateAt(index);
353
+ const selected = range ? [rangeValue?.from, rangeValue?.to].some(
354
+ (edge) => edge && +periodDate(edge) === +date
355
+ ) : (Array.isArray(working) ? working : working ? [working] : []).some(
356
+ (day) => +periodDate(day) === +date
357
+ );
358
+ const inRange = range && rangeValue?.from && rangeValue.to ? +date > +periodDate(rangeValue.from) && +date < +periodDate(rangeValue.to) : false;
359
+ return /* @__PURE__ */ jsx("div", { role: "gridcell", className: "ui-month-picker-gridcell", children: /* @__PURE__ */ jsx(
360
+ Button,
361
+ {
362
+ type: "button",
363
+ variant: selected ? "default" : inRange ? "secondary" : "ghost",
364
+ size: "sm",
365
+ "aria-pressed": selected,
366
+ disabled: !allowed(date),
367
+ className: "ui-month-picker-cell",
368
+ onClick: () => periodPick(date),
369
+ children: periodLabel(date, index)
370
+ }
371
+ ) }, index);
372
+ }) }, row)) })
373
+ ] });
374
+ const calendarBounds = [
375
+ ...minimum ? [{ before: minimum }] : [],
376
+ ...maximum ? [{ after: maximum }] : [],
377
+ ...disabledDate ? [disabledDate] : []
378
+ ];
379
+ const calendarShared = {
380
+ // The day grid takes its month from the SAME anchor the period grid uses, so `pickerValue` and
381
+ // `defaultPickerValue` mean one thing at every granularity.
382
+ month: viewAnchor,
383
+ onMonthChange: setViewAnchor,
384
+ locale: dayPickerLocale,
385
+ cellRender,
386
+ showWeekNumber: showWeek,
387
+ startMonth: minimum,
388
+ endMonth: maximum,
389
+ showToday,
390
+ showClose,
391
+ onClose: () => setOpen(false)
392
+ };
393
+ const dayPanel = range ? /* @__PURE__ */ jsx(
394
+ Calendar,
395
+ {
396
+ mode: "range",
397
+ selected: rangeValue,
398
+ numberOfMonths: 2,
399
+ onSelect: (next) => {
400
+ choose(next);
401
+ setFromText(display(next?.from));
402
+ setToText(display(next?.to));
403
+ },
404
+ disabled: calendarBounds,
405
+ ...calendarShared
406
+ }
407
+ ) : multiple ? /* @__PURE__ */ jsx(
408
+ Calendar,
409
+ {
410
+ mode: "multiple",
411
+ selected: Array.isArray(working) ? working : [],
412
+ onSelect: choose,
413
+ disabled: (day) => !allowed(day),
414
+ ...calendarShared
415
+ }
416
+ ) : /* @__PURE__ */ jsx(
417
+ Calendar,
418
+ {
419
+ mode: "single",
420
+ selected: selectedDate,
421
+ onSelect: (date) => {
422
+ if (date && showTime && selectedDate)
423
+ date.setHours(
424
+ selectedDate.getHours(),
425
+ selectedDate.getMinutes(),
426
+ selectedDate.getSeconds()
427
+ );
428
+ choose(date);
429
+ if (!needConfirm && !multiple) setOpen(false);
430
+ },
431
+ disabled: calendarBounds,
432
+ ...calendarShared
433
+ }
434
+ );
435
+ const trailing = showClear ? /* @__PURE__ */ jsx(
436
+ "button",
437
+ {
438
+ type: "button",
439
+ tabIndex: -1,
440
+ "aria-label": clearControl.label,
441
+ onClick: (event) => {
442
+ event.stopPropagation();
443
+ event.currentTarget.closest("div")?.querySelector("input:not([type=hidden])")?.focus();
444
+ clear();
445
+ },
446
+ className: range ? "text-muted-foreground hover:text-foreground shrink-0" : "ui-control-inline-affix-action",
447
+ children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(
448
+ X,
449
+ {
450
+ className: range ? "ui-month-picker-icon" : "ui-control-inline-affix-icon",
451
+ "aria-hidden": "true"
452
+ }
453
+ )
454
+ }
455
+ ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
456
+ "button",
457
+ {
458
+ type: "button",
459
+ disabled: allDisabled,
460
+ tabIndex: -1,
461
+ "aria-label": (isPeriod ? t("dataEntry.monthPicker.openGrid") : range ? t("dataEntry.dateRangePicker.openCalendar") : t("dataEntry.datePicker.openCalendar")) ?? "Open calendar",
462
+ className: range ? "text-muted-foreground hover:text-foreground shrink-0" : "ui-control-inline-affix-action",
463
+ children: /* @__PURE__ */ jsx(
464
+ CalendarIcon,
465
+ {
466
+ className: range ? "ui-month-picker-icon" : "ui-control-inline-affix-icon",
467
+ "aria-hidden": "true"
468
+ }
469
+ )
470
+ }
471
+ ) });
472
+ const panel = /* @__PURE__ */ jsxs(
473
+ PopoverContent,
474
+ {
475
+ id: dialogId,
476
+ role: "dialog",
477
+ "aria-label": (isPeriod ? t("dataEntry.monthPicker.openGrid") : t("dataEntry.datePicker.openCalendar")) ?? "Calendar",
478
+ className: isPeriod ? "ui-month-picker-panel" : "ui-control-panel-flush",
479
+ side: placement.startsWith("top") ? "top" : "bottom",
480
+ align: placement.endsWith("end") ? "end" : "start",
481
+ onOpenAutoFocus: (event) => event.preventDefault(),
482
+ onClick: (event) => event.stopPropagation(),
483
+ children: [
484
+ presets?.length ? /* @__PURE__ */ jsx(Flex, { wrap: true, gap: "xs", pad: "sm", children: presets.map((preset, index) => /* @__PURE__ */ jsx(
485
+ Button,
486
+ {
487
+ variant: "ghost",
488
+ size: "sm",
489
+ type: "button",
490
+ onClick: () => {
491
+ const picked = typeof preset.value === "function" ? preset.value() : preset.value;
492
+ if (range) {
493
+ if (!validRange(picked)) return;
494
+ choose(picked);
495
+ return;
496
+ }
497
+ if (!allowed(picked)) return;
498
+ choose(picked);
499
+ if (!needConfirm && !multiple) setOpen(false);
500
+ },
501
+ children: preset.label
502
+ },
503
+ index
504
+ )) }) : null,
505
+ isPeriod ? periodPanel : dayPanel,
506
+ showTime ? /* @__PURE__ */ jsx(Flex, { pad: "sm", children: /* @__PURE__ */ jsx(
507
+ TimePicker,
508
+ {
509
+ ...typeof showTime === "object" ? showTime : {},
510
+ "aria-label": t("dataEntry.timePicker.openPicker"),
511
+ value: selectedDate ? [
512
+ selectedDate.getHours(),
513
+ selectedDate.getMinutes(),
514
+ ...typeof showTime === "object" && showTime.showSeconds ? [selectedDate.getSeconds()] : []
515
+ ].map((n) => String(n).padStart(2, "0")).join(":") : "",
516
+ onValueChange: (time) => {
517
+ if (!time) return;
518
+ const date = new Date(selectedDate ?? /* @__PURE__ */ new Date());
519
+ const [h, m, sec = 0] = time.split(":").map(Number);
520
+ date.setHours(h, m, sec, 0);
521
+ choose(date);
522
+ }
523
+ }
524
+ ) }) : null,
525
+ needConfirm ? /* @__PURE__ */ jsx(Flex, { pad: "sm", justify: "end", children: /* @__PURE__ */ jsx(
526
+ Button,
527
+ {
528
+ type: "button",
529
+ disabled: range ? !validRange(rangeValue) : !selectedDate || !allowed(selectedDate) || !timeAllowed(selectedDate),
530
+ onClick: () => {
531
+ if (range ? validRange(rangeValue) : selectedDate && allowed(selectedDate)) {
532
+ emit(working);
533
+ if (range) {
534
+ setFromText(display(rangeValue?.from));
535
+ setToText(display(rangeValue?.to));
536
+ } else {
537
+ setText(displayAll(working));
538
+ }
539
+ setOpen(false);
540
+ }
541
+ },
542
+ children: t("dataEntry.timePicker.confirm")
543
+ }
544
+ ) }) : null,
545
+ renderExtraFooter?.()
546
+ ]
547
+ }
548
+ );
549
+ if (range) {
550
+ const edgeInput = (edge) => /* @__PURE__ */ jsx(
551
+ "input",
552
+ {
553
+ id: edge === "from" ? fromId : toId,
554
+ "data-field": resolvedField ? `${resolvedField}_${edge}` : void 0,
555
+ name: displayIsCanonical && resolvedName ? `${resolvedName}_${edge}` : void 0,
556
+ ref: edge === "from" ? ref : void 0,
557
+ readOnly: inputReadOnly,
558
+ "aria-readonly": inputReadOnly || void 0,
559
+ value: edge === "from" ? fromText : toText,
560
+ disabled: edgeDisabled(edge),
561
+ placeholder: resolvedPlaceholder,
562
+ inputMode: "numeric",
563
+ autoComplete: "off",
564
+ "aria-label": (edge === "from" ? t("dataEntry.dateRangePicker.from") : t("dataEntry.dateRangePicker.to")) ?? edge,
565
+ className: "ui-month-picker-input",
566
+ ...keyHandlers(enterEdge(edge)),
567
+ onChange: (event) => {
568
+ if (edge === "from") setFromText(event.target.value);
569
+ else setToText(event.target.value);
570
+ commitEdge(edge, event.target.value);
571
+ },
572
+ onBlur: (event) => {
573
+ if (preserveInvalidOnBlur) return;
574
+ const parsed = parse(event.target.value);
575
+ const accepted = parsed && allowed(parsed) ? parsed : void 0;
576
+ const fallback = edge === "from" ? committedRange?.from : committedRange?.to;
577
+ const next = accepted ? display(accepted) : display(fallback);
578
+ if (edge === "from") setFromText(next);
579
+ else setToText(next);
580
+ }
160
581
  }
161
- ) : null,
582
+ );
583
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
584
+ hiddenFields,
585
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
586
+ "div",
587
+ {
588
+ role: "group",
589
+ id: rootId,
590
+ ...fieldA11y,
591
+ "data-field": resolvedField,
592
+ "data-size": size,
593
+ "data-status": status,
594
+ "data-variant": variant,
595
+ "aria-disabled": allDisabled ? true : void 0,
596
+ "data-disabled": allDisabled ? "" : void 0,
597
+ "data-state": open ? "open" : "closed",
598
+ className: cn(
599
+ "ui-control ui-control-composite-field",
600
+ "aria-invalid:border-destructive",
601
+ CONTROL_VARIANT_CHROME_CLASS[variant ?? "outlined"],
602
+ CONTROL_STATUS_CHROME_CLASS,
603
+ className
604
+ ),
605
+ onClick: () => {
606
+ if (!allDisabled) setOpen(true);
607
+ },
608
+ children: [
609
+ edgeInput("from"),
610
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ui-month-picker-separator-icon", "aria-hidden": "true" }),
611
+ edgeInput("to"),
612
+ trailing,
613
+ panel
614
+ ]
615
+ }
616
+ ) })
617
+ ] });
618
+ }
619
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
620
+ hiddenFields,
162
621
  /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
163
622
  /* @__PURE__ */ jsx(
164
623
  Input,
165
624
  {
166
- id,
167
- name: format || showTime || multiple || needConfirm || preserveInvalidOnBlur ? "" : resolvedName,
625
+ id: rootId,
626
+ name: displayIsCanonical ? resolvedName : "",
168
627
  ref,
169
628
  size,
170
629
  status,
@@ -172,7 +631,7 @@ function DatePicker(props) {
172
631
  readOnly: inputReadOnly || multiple,
173
632
  "data-field": resolvedField,
174
633
  value: text,
175
- disabled,
634
+ disabled: allDisabled,
176
635
  placeholder: resolvedPlaceholder,
177
636
  inputMode: "numeric",
178
637
  autoComplete: "off",
@@ -181,50 +640,11 @@ function DatePicker(props) {
181
640
  "aria-haspopup": "dialog",
182
641
  "aria-controls": open ? dialogId : void 0,
183
642
  ...fieldA11y,
184
- trailingIcon: /* @__PURE__ */ jsx("span", { className: "ui-time-picker-affix", children: showClear ? /* @__PURE__ */ jsx(
185
- "button",
186
- {
187
- type: "button",
188
- tabIndex: -1,
189
- "aria-label": clearControl.label,
190
- onClick: (event) => {
191
- event.stopPropagation();
192
- event.currentTarget.closest("div")?.querySelector("input:not([type=hidden])")?.focus();
193
- clear();
194
- },
195
- className: "ui-control-inline-affix-action",
196
- children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
197
- }
198
- ) : /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
199
- "button",
200
- {
201
- type: "button",
202
- disabled,
203
- tabIndex: -1,
204
- "aria-label": t("dataEntry.datePicker.openCalendar") ?? "Open calendar",
205
- className: "ui-control-inline-affix-action",
206
- children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-control-inline-affix-icon", "aria-hidden": "true" })
207
- }
208
- ) }) }),
643
+ trailingIcon: /* @__PURE__ */ jsx("span", { className: "ui-time-picker-affix", children: trailing }),
209
644
  onClick: () => {
210
- if (!disabled) setOpen(true);
211
- },
212
- onKeyDown: (event) => {
213
- if (event.key === "ArrowDown") {
214
- event.preventDefault();
215
- setOpen(true);
216
- } else if (event.key === "Enter") {
217
- const parsed = parse(text);
218
- if (parsed && allowed(parsed) && timeAllowed(parsed)) {
219
- const next = periodDate(parsed);
220
- emit(next);
221
- setText(display(parsed));
222
- setOpen(false);
223
- }
224
- } else if (event.key === "Escape" && open) {
225
- setOpen(false);
226
- }
645
+ if (!allDisabled) setOpen(true);
227
646
  },
647
+ ...keyHandlers(enterSingle),
228
648
  onChange: (event) => {
229
649
  setText(event.target.value);
230
650
  commit(event.target.value);
@@ -232,167 +652,12 @@ function DatePicker(props) {
232
652
  onBlur: (event) => {
233
653
  const parsed = parse(event.target.value);
234
654
  const accepted = parsed && allowed(parsed) && timeAllowed(parsed) ? parsed : void 0;
235
- if (!preserveInvalidOnBlur) setText(accepted ? display(accepted) : display(value));
655
+ if (!preserveInvalidOnBlur)
656
+ setText(accepted ? display(accepted) : displayAll(committedSingle));
236
657
  }
237
658
  }
238
659
  ),
239
- /* @__PURE__ */ jsxs(
240
- PopoverContent,
241
- {
242
- id: dialogId,
243
- role: "dialog",
244
- "aria-label": t("dataEntry.datePicker.openCalendar") ?? "Calendar",
245
- className: "ui-control-panel-flush",
246
- side: placement.startsWith("top") ? "top" : "bottom",
247
- align: placement.endsWith("end") ? "end" : "start",
248
- onOpenAutoFocus: (event) => event.preventDefault(),
249
- children: [
250
- presets?.length ? /* @__PURE__ */ jsx(Flex, { wrap: true, gap: "xs", pad: "sm", children: presets.map((preset, index) => /* @__PURE__ */ jsx(
251
- Button,
252
- {
253
- variant: "ghost",
254
- size: "sm",
255
- type: "button",
256
- onClick: () => {
257
- const date = typeof preset.value === "function" ? preset.value() : preset.value;
258
- if (!allowed(date)) return;
259
- choose(date);
260
- if (!needConfirm && !multiple) setOpen(false);
261
- },
262
- children: preset.label
263
- },
264
- index
265
- )) }) : null,
266
- picker === "date" || picker === "week" ? multiple ? /* @__PURE__ */ jsx(
267
- Calendar,
268
- {
269
- mode: "multiple",
270
- selected: Array.isArray(working) ? working : [],
271
- defaultMonth: selectedDate,
272
- locale: dayPickerLocale,
273
- onSelect: choose,
274
- disabled: (day) => !allowed(day),
275
- cellRender,
276
- showWeekNumber: showWeek,
277
- startMonth: minimum,
278
- endMonth: maximum,
279
- showToday,
280
- showClose,
281
- onClose: () => setOpen(false)
282
- }
283
- ) : /* @__PURE__ */ jsx(
284
- Calendar,
285
- {
286
- mode: "single",
287
- selected: selectedDate,
288
- defaultMonth: selectedDate,
289
- showWeekNumber: showWeek,
290
- onSelect: (date) => {
291
- if (date && showTime && selectedDate)
292
- date.setHours(
293
- selectedDate.getHours(),
294
- selectedDate.getMinutes(),
295
- selectedDate.getSeconds()
296
- );
297
- choose(date);
298
- if (!needConfirm && !multiple) setOpen(false);
299
- },
300
- locale: dayPickerLocale,
301
- disabled: [
302
- ...minimum ? [{ before: minimum }] : [],
303
- ...maximum ? [{ after: maximum }] : [],
304
- ...disabledDate ? [disabledDate] : []
305
- ],
306
- cellRender,
307
- startMonth: minimum,
308
- endMonth: maximum,
309
- showToday,
310
- showClose,
311
- onClose: () => setOpen(false)
312
- }
313
- ) : /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-panel", children: [
314
- /* @__PURE__ */ jsxs(Flex, { justify: "between", align: "center", children: [
315
- /* @__PURE__ */ jsx(
316
- Button,
317
- {
318
- type: "button",
319
- variant: "ghost",
320
- "aria-label": t("dataEntry.monthPicker.previousYear"),
321
- onClick: () => setViewYear(viewYear - (picker === "year" ? 12 : 1)),
322
- children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" })
323
- }
324
- ),
325
- /* @__PURE__ */ jsx("span", { "aria-live": "polite", children: new Intl.NumberFormat(locale, { useGrouping: false }).format(viewYear) }),
326
- /* @__PURE__ */ jsx(
327
- Button,
328
- {
329
- type: "button",
330
- variant: "ghost",
331
- "aria-label": t("dataEntry.monthPicker.nextYear"),
332
- onClick: () => setViewYear(viewYear + (picker === "year" ? 12 : 1)),
333
- children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })
334
- }
335
- )
336
- ] }),
337
- /* @__PURE__ */ jsx("div", { className: "ui-month-picker-grid", children: Array.from({ length: picker === "quarter" ? 4 : 12 }, (_, index) => {
338
- const date = picker === "year" ? new Date(viewYear + index, 0, 1) : new Date(viewYear, index * (picker === "quarter" ? 3 : 1), 1);
339
- const label = picker === "year" ? new Intl.DateTimeFormat(locale, { year: "numeric" }).format(date) : picker === "quarter" ? t("dataEntry.datePicker.quarter", { quarter: index + 1 }) : new Intl.DateTimeFormat(locale, { month: "short" }).format(date);
340
- const selected = (Array.isArray(working) ? working : working ? [working] : []).some((day) => +periodDate(day) === +date);
341
- return /* @__PURE__ */ jsx(
342
- Button,
343
- {
344
- type: "button",
345
- variant: selected ? "default" : "ghost",
346
- "aria-pressed": selected,
347
- disabled: !allowed(date),
348
- onClick: () => {
349
- choose(date);
350
- if (!needConfirm && !multiple) setOpen(false);
351
- },
352
- children: label
353
- },
354
- index
355
- );
356
- }) })
357
- ] }),
358
- showTime ? /* @__PURE__ */ jsx(Flex, { pad: "sm", children: /* @__PURE__ */ jsx(
359
- TimePicker,
360
- {
361
- ...typeof showTime === "object" ? showTime : {},
362
- "aria-label": t("dataEntry.timePicker.openPicker"),
363
- value: selectedDate ? [
364
- selectedDate.getHours(),
365
- selectedDate.getMinutes(),
366
- ...typeof showTime === "object" && showTime.showSeconds ? [selectedDate.getSeconds()] : []
367
- ].map((n) => String(n).padStart(2, "0")).join(":") : "",
368
- onValueChange: (time) => {
369
- if (!time) return;
370
- const date = new Date(selectedDate ?? /* @__PURE__ */ new Date());
371
- const [h, m, sec = 0] = time.split(":").map(Number);
372
- date.setHours(h, m, sec, 0);
373
- choose(date);
374
- }
375
- }
376
- ) }) : null,
377
- needConfirm ? /* @__PURE__ */ jsx(Flex, { pad: "sm", justify: "end", children: /* @__PURE__ */ jsx(
378
- Button,
379
- {
380
- type: "button",
381
- disabled: !selectedDate || !allowed(selectedDate) || !timeAllowed(selectedDate),
382
- onClick: () => {
383
- if (selectedDate && allowed(selectedDate) && timeAllowed(selectedDate)) {
384
- emit(working);
385
- setText(display(working));
386
- setOpen(false);
387
- }
388
- },
389
- children: t("dataEntry.timePicker.confirm")
390
- }
391
- ) }) : null,
392
- renderExtraFooter?.()
393
- ]
394
- }
395
- )
660
+ panel
396
661
  ] }) })
397
662
  ] });
398
663
  }