@godxjp/ui 23.2.1 → 23.4.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 (101) hide show
  1. package/dist/app/timezones.js +2 -1
  2. package/dist/components/charts/chart-cartesian.js +2 -4
  3. package/dist/components/charts/chart-frame.js +2 -1
  4. package/dist/components/data-display/card.d.ts +87 -1
  5. package/dist/components/data-display/card.js +88 -15
  6. package/dist/components/data-display/index.d.ts +5 -1
  7. package/dist/components/data-display/index.js +5 -0
  8. package/dist/components/data-display/thought-chain.d.ts +42 -0
  9. package/dist/components/data-display/thought-chain.js +177 -0
  10. package/dist/components/data-display/welcome.d.ts +26 -0
  11. package/dist/components/data-display/welcome.js +37 -0
  12. package/dist/components/data-entry/attachments.d.ts +19 -0
  13. package/dist/components/data-entry/attachments.js +322 -0
  14. package/dist/components/data-entry/date-picker.js +12 -14
  15. package/dist/components/data-entry/index.d.ts +2 -0
  16. package/dist/components/data-entry/index.js +2 -0
  17. package/dist/components/data-entry/number-input.js +2 -1
  18. package/dist/components/feedback/dialog.d.ts +10 -7
  19. package/dist/components/feedback/dialog.js +40 -17
  20. package/dist/components/feedback/index.d.ts +3 -3
  21. package/dist/components/feedback/index.js +2 -0
  22. package/dist/components/feedback/skeleton.d.ts +11 -2
  23. package/dist/components/feedback/skeleton.js +8 -0
  24. package/dist/components/general/actions.d.ts +59 -0
  25. package/dist/components/general/actions.js +253 -0
  26. package/dist/components/general/activity.js +1 -0
  27. package/dist/components/general/button.js +2 -1
  28. package/dist/components/general/float-button.d.ts +41 -0
  29. package/dist/components/general/float-button.js +336 -0
  30. package/dist/components/general/index.d.ts +6 -2
  31. package/dist/components/general/index.js +12 -1
  32. package/dist/components/general/typography.d.ts +79 -4
  33. package/dist/components/general/typography.js +598 -54
  34. package/dist/components/layout/draggable-panel.d.ts +18 -0
  35. package/dist/components/layout/draggable-panel.js +189 -0
  36. package/dist/components/layout/error-surface.js +3 -2
  37. package/dist/components/layout/index.d.ts +2 -0
  38. package/dist/components/layout/index.js +2 -0
  39. package/dist/components/navigation/conversations.d.ts +39 -0
  40. package/dist/components/navigation/conversations.js +371 -0
  41. package/dist/components/navigation/index.d.ts +2 -0
  42. package/dist/components/navigation/index.js +2 -0
  43. package/dist/components/ui/card.js +1 -0
  44. package/dist/components/ui/toggle.js +2 -1
  45. package/dist/i18n/messages/en.json +53 -0
  46. package/dist/i18n/messages/ja.json +53 -0
  47. package/dist/i18n/messages/vi.json +53 -0
  48. package/dist/i18n/translate.js +3 -2
  49. package/dist/lib/datetime/picker-format.js +2 -1
  50. package/dist/lib/format.js +3 -2
  51. package/dist/lib/intl-cache.d.ts +32 -0
  52. package/dist/lib/intl-cache.js +29 -0
  53. package/dist/props/components/data-display.prop.d.ts +143 -1
  54. package/dist/props/components/data-entry.prop.d.ts +87 -0
  55. package/dist/props/components/feedback.prop.d.ts +53 -1
  56. package/dist/props/components/general.prop.d.ts +440 -3
  57. package/dist/props/components/index.d.ts +2 -2
  58. package/dist/props/components/layout.prop.d.ts +63 -1
  59. package/dist/props/components/navigation.prop.d.ts +150 -0
  60. package/dist/props/registry.d.ts +431 -0
  61. package/dist/props/registry.js +551 -0
  62. package/dist/props/vocabulary/index.d.ts +1 -1
  63. package/dist/props/vocabulary/interaction.prop.d.ts +113 -0
  64. package/dist/styles/alert-layout.css +22 -0
  65. package/dist/styles/card-layout.css +34 -0
  66. package/dist/styles/core.css +1 -0
  67. package/dist/styles/data-display-layout.css +236 -0
  68. package/dist/styles/float-button-layout.css +168 -0
  69. package/dist/styles/form-layout.css +11 -1
  70. package/dist/styles/index.css +1 -0
  71. package/dist/styles/layout.css +196 -0
  72. package/dist/styles/navigation-layout.css +139 -0
  73. package/dist/styles/text-layout.css +156 -0
  74. package/dist/tokens/axes.css +5 -0
  75. package/dist/tokens/base.css +7 -0
  76. package/dist/tokens/components/actions.css +14 -0
  77. package/dist/tokens/components/attachments.css +15 -0
  78. package/dist/tokens/components/conversations.css +28 -0
  79. package/dist/tokens/components/draggable-panel.css +25 -0
  80. package/dist/tokens/components/float-button.css +39 -0
  81. package/dist/tokens/components/text.css +13 -0
  82. package/dist/tokens/components/thought-chain.css +31 -0
  83. package/dist/tokens/components/welcome.css +19 -0
  84. package/dist/tokens/foundation.css +6 -4
  85. package/docs/DESIGN-AUTHORITY.md +133 -20
  86. package/docs/FRAME-COVERAGE-REPORT.md +5 -2
  87. package/docs/data-display/card/examples/tab-list.tsx +97 -0
  88. package/docs/data-display/thought-chain.tsx +176 -0
  89. package/docs/data-display/welcome.tsx +115 -0
  90. package/docs/data-entry/attachments.tsx +160 -0
  91. package/docs/feedback/dialog.tsx +50 -0
  92. package/docs/general/actions.tsx +175 -0
  93. package/docs/general/float-button.tsx +133 -0
  94. package/docs/general/typography.tsx +63 -1
  95. package/docs/layout/draggable-panel.tsx +127 -0
  96. package/docs/navigation/conversations.tsx +176 -0
  97. package/docs/roadmap/parity-audit-layout-navigation-general.md +17 -16
  98. package/package.json +20 -4
  99. package/scripts/_agent-setup.mjs +36 -5
  100. package/scripts/guinea-pig-skill.md +14 -0
  101. package/scripts/ui-audit.mjs +69 -9
@@ -0,0 +1,322 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { FileIcon, ImagePlus, Plus, X } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { formatBytes } from "../../lib/format.js";
7
+ import { cn } from "../../lib/utils.js";
8
+ import { Button } from "../general/button.js";
9
+ import { Text } from "../general/typography.js";
10
+ import { Progress } from "../data-display/progress.js";
11
+ import { createUploadItem } from "./upload-types.js";
12
+ import { readDroppedFiles } from "./upload-files.js";
13
+ const AttachmentContext = React.createContext({ disabled: false });
14
+ function toAttachmentItem(item) {
15
+ const status = item.status === "uploading" ? "uploading" : item.status === "done" ? "done" : item.status === "error" ? "error" : item.status === "removed" ? "removed" : void 0;
16
+ return {
17
+ uid: item.uid,
18
+ name: item.name,
19
+ size: item.size,
20
+ status,
21
+ percent: item.percent,
22
+ url: item.url,
23
+ thumbUrl: item.previewUrl,
24
+ originFileObj: item.file,
25
+ response: item.response,
26
+ error: item.error
27
+ };
28
+ }
29
+ function resolvePlaceholder(placeholder, type) {
30
+ if (!placeholder) return {};
31
+ return typeof placeholder === "function" ? placeholder(type) : placeholder;
32
+ }
33
+ function fileMatchesAccept(file, accept) {
34
+ if (!accept) return true;
35
+ return accept.split(",").some((rule) => {
36
+ const trimmed = rule.trim().toLowerCase();
37
+ if (trimmed.endsWith("/*")) return file.type.toLowerCase().startsWith(trimmed.slice(0, -1));
38
+ return file.type.toLowerCase() === trimmed || trimmed.startsWith(".") && file.name.toLowerCase().endsWith(trimmed);
39
+ });
40
+ }
41
+ function PlaceholderUploader({
42
+ type,
43
+ placeholder,
44
+ onOpen,
45
+ onFiles,
46
+ className,
47
+ style,
48
+ hidden
49
+ }) {
50
+ const { t } = useTranslation();
51
+ const { disabled } = React.useContext(AttachmentContext);
52
+ const [dragActive, setDragActive] = React.useState(false);
53
+ if (hidden) return null;
54
+ const title = placeholder.title ?? t("dataEntry.attachments.placeholderTitle");
55
+ const description = placeholder.description ?? t("dataEntry.attachments.placeholderDescription");
56
+ const icon = placeholder.icon ?? /* @__PURE__ */ jsx(ImagePlus, { "aria-hidden": "true" });
57
+ return /* @__PURE__ */ jsxs(
58
+ "div",
59
+ {
60
+ className: cn("ui-attachments-placeholder", className),
61
+ style,
62
+ "data-drag-active": dragActive ? "true" : void 0,
63
+ "aria-hidden": disabled ? true : void 0,
64
+ onDragEnter: (event) => {
65
+ event.preventDefault();
66
+ if (!disabled) setDragActive(true);
67
+ },
68
+ onDragOver: (event) => event.preventDefault(),
69
+ onDragLeave: (event) => {
70
+ if (!event.currentTarget.contains(event.relatedTarget)) setDragActive(false);
71
+ },
72
+ onDrop: async (event) => {
73
+ event.preventDefault();
74
+ setDragActive(false);
75
+ if (disabled) return;
76
+ const files = await readDroppedFiles(event.dataTransfer, false);
77
+ if (files.length) onFiles(files);
78
+ },
79
+ onClick: () => {
80
+ if (!disabled) onOpen();
81
+ },
82
+ onKeyDown: (event) => {
83
+ if (disabled) return;
84
+ if (event.key === "Enter" || event.key === " ") {
85
+ event.preventDefault();
86
+ onOpen();
87
+ }
88
+ },
89
+ role: "button",
90
+ tabIndex: disabled ? -1 : 0,
91
+ "aria-label": String(title),
92
+ children: [
93
+ /* @__PURE__ */ jsx("span", { className: "ui-attachments-placeholder-icon", children: icon }),
94
+ title ? /* @__PURE__ */ jsx("span", { className: "ui-attachments-placeholder-title", children: title }) : null,
95
+ description ? /* @__PURE__ */ jsx("span", { className: "ui-attachments-placeholder-description", children: description }) : null,
96
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("dataEntry.attachments.dropLabel", { type }) })
97
+ ]
98
+ }
99
+ );
100
+ }
101
+ function AttachmentCard({
102
+ item,
103
+ description,
104
+ onRemove,
105
+ classNames
106
+ }) {
107
+ const { t } = useTranslation();
108
+ const { disabled } = React.useContext(AttachmentContext);
109
+ const preview = item.thumbUrl ?? item.url;
110
+ const desc = description ?? item.description ?? (item.status === "uploading" ? `${item.percent ?? 0}%` : item.status === "error" ? typeof item.response === "string" ? item.response : t("dataEntry.attachments.error") : item.size ? formatBytes(item.size) : "");
111
+ return /* @__PURE__ */ jsxs("li", { className: cn("ui-attachments-card", classNames?.card, classNames?.file), children: [
112
+ preview ? /* @__PURE__ */ jsx("img", { src: preview, alt: "", className: "ui-attachments-card-preview", "aria-hidden": "true" }) : /* @__PURE__ */ jsx("span", { className: "ui-attachments-card-placeholder", "aria-hidden": "true", children: /* @__PURE__ */ jsx(FileIcon, {}) }),
113
+ (item.status === "uploading" || item.status === "error") && /* @__PURE__ */ jsx("div", { className: "ui-attachments-card-overlay", children: item.status === "uploading" && item.percent != null ? /* @__PURE__ */ jsx(Progress, { value: item.percent, "aria-label": t("dataEntry.attachments.uploading") }) : /* @__PURE__ */ jsx(Text, { size: "xs", children: String(desc) }) }),
114
+ desc && item.status !== "uploading" && item.status !== "error" ? /* @__PURE__ */ jsx("div", { className: "ui-attachments-card-meta", children: desc }) : null,
115
+ !disabled && onRemove ? /* @__PURE__ */ jsx(
116
+ Button,
117
+ {
118
+ type: "button",
119
+ variant: "ghost",
120
+ size: "icon-xs",
121
+ className: "ui-attachments-card-remove",
122
+ "aria-label": t("dataEntry.attachments.remove", { name: item.name }),
123
+ onClick: (event) => {
124
+ event.stopPropagation();
125
+ onRemove();
126
+ },
127
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
128
+ }
129
+ ) : null
130
+ ] });
131
+ }
132
+ const Attachments = React.forwardRef(
133
+ ({
134
+ className,
135
+ rootClassName,
136
+ classNames = {},
137
+ styles = {},
138
+ items: controlledItems,
139
+ onChange,
140
+ onRemove,
141
+ overflow,
142
+ placeholder,
143
+ getDropContainer,
144
+ disabled = false,
145
+ maxCount,
146
+ accept,
147
+ multiple,
148
+ children
149
+ }, ref) => {
150
+ const { t } = useTranslation();
151
+ const containerRef = React.useRef(null);
152
+ const inputRef = React.useRef(null);
153
+ const [internalItems, setInternalItems] = React.useState([]);
154
+ const items = controlledItems ?? internalItems;
155
+ const setItems = React.useCallback(
156
+ (next, file) => {
157
+ if (controlledItems === void 0) setInternalItems(next);
158
+ onChange?.({ file, fileList: next });
159
+ },
160
+ [controlledItems, onChange]
161
+ );
162
+ const openPicker = React.useCallback(() => {
163
+ inputRef.current?.click();
164
+ }, []);
165
+ const dispatchFiles = React.useCallback(
166
+ (files) => {
167
+ const accepted = files.filter((file) => fileMatchesAccept(file, accept));
168
+ if (!accepted.length) return;
169
+ const room = maxCount == null ? accepted.length : Math.max(0, maxCount - items.length);
170
+ const picked = accepted.slice(0, room);
171
+ const next = [
172
+ ...items,
173
+ ...picked.map((file) => {
174
+ const uploadItem = createUploadItem(file);
175
+ return toAttachmentItem(uploadItem);
176
+ })
177
+ ];
178
+ const last = next[next.length - 1];
179
+ if (last) setItems(next, last);
180
+ },
181
+ [accept, items, maxCount, setItems]
182
+ );
183
+ React.useImperativeHandle(ref, () => ({
184
+ nativeElement: containerRef.current,
185
+ fileNativeElement: inputRef.current,
186
+ upload: (file) => dispatchFiles([file]),
187
+ select: (options) => {
188
+ const input = inputRef.current;
189
+ if (!input) return;
190
+ input.multiple = options?.multiple ?? false;
191
+ input.accept = options?.accept ?? accept ?? "";
192
+ input.click();
193
+ }
194
+ }));
195
+ const handleRemove = async (item) => {
196
+ const ret = await Promise.resolve(typeof onRemove === "function" ? onRemove(item) : onRemove);
197
+ if (ret === false) return;
198
+ const next = items.filter((entry) => entry.uid !== item.uid);
199
+ setItems(next, { ...item, status: "removed" });
200
+ };
201
+ const showExtension = !disabled && (maxCount == null || maxCount > items.length) && items.length > 0;
202
+ const inlinePlaceholder = resolvePlaceholder(placeholder, "inline");
203
+ const dropPlaceholder = resolvePlaceholder(placeholder, "drop");
204
+ const hasFiles = items.length > 0;
205
+ const fileInput = /* @__PURE__ */ jsx(
206
+ "input",
207
+ {
208
+ ref: inputRef,
209
+ type: "file",
210
+ className: "ui-attachments-input",
211
+ accept,
212
+ multiple: multiple ?? (maxCount == null || maxCount > 1),
213
+ disabled,
214
+ "aria-label": t("dataEntry.attachments.inputLabel"),
215
+ onChange: (event) => {
216
+ const files = event.target.files ? Array.from(event.target.files) : [];
217
+ event.target.value = "";
218
+ dispatchFiles(files);
219
+ }
220
+ }
221
+ );
222
+ if (children) {
223
+ return /* @__PURE__ */ jsxs(AttachmentContext.Provider, { value: { disabled }, children: [
224
+ fileInput,
225
+ /* @__PURE__ */ jsx(
226
+ "div",
227
+ {
228
+ ref: containerRef,
229
+ className: cn("ui-attachments", rootClassName, classNames.root),
230
+ style: styles.root,
231
+ children
232
+ }
233
+ ),
234
+ /* @__PURE__ */ jsx(
235
+ PlaceholderUploader,
236
+ {
237
+ type: "drop",
238
+ placeholder: dropPlaceholder,
239
+ onOpen: openPicker,
240
+ onFiles: dispatchFiles,
241
+ className: classNames.placeholder,
242
+ style: styles.placeholder
243
+ }
244
+ )
245
+ ] });
246
+ }
247
+ const dropHost = getDropContainer?.() ?? containerRef.current;
248
+ return /* @__PURE__ */ jsx(AttachmentContext.Provider, { value: { disabled }, children: /* @__PURE__ */ jsxs(
249
+ "div",
250
+ {
251
+ ref: containerRef,
252
+ className: cn("ui-attachments", className, rootClassName, classNames.root),
253
+ style: styles.root,
254
+ dir: "auto",
255
+ children: [
256
+ fileInput,
257
+ hasFiles ? /* @__PURE__ */ jsxs(
258
+ "ul",
259
+ {
260
+ className: cn("ui-attachments-list", classNames.list),
261
+ style: styles.list,
262
+ "data-overflow": overflow ?? "wrap",
263
+ children: [
264
+ items.map((item) => /* @__PURE__ */ jsx(
265
+ AttachmentCard,
266
+ {
267
+ item,
268
+ onRemove: () => handleRemove(item),
269
+ classNames
270
+ },
271
+ item.uid
272
+ )),
273
+ showExtension ? /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
274
+ "button",
275
+ {
276
+ type: "button",
277
+ className: cn("ui-attachments-upload-btn", classNames.upload),
278
+ style: styles.upload,
279
+ disabled,
280
+ "aria-label": t("dataEntry.attachments.add"),
281
+ onClick: openPicker,
282
+ children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" })
283
+ }
284
+ ) }) : null
285
+ ]
286
+ }
287
+ ) : null,
288
+ /* @__PURE__ */ jsx(
289
+ PlaceholderUploader,
290
+ {
291
+ type: "inline",
292
+ placeholder: inlinePlaceholder,
293
+ onOpen: openPicker,
294
+ onFiles: dispatchFiles,
295
+ className: classNames.placeholder,
296
+ style: hasFiles ? { display: "none" } : styles.placeholder,
297
+ hidden: hasFiles
298
+ }
299
+ ),
300
+ dropHost ? /* @__PURE__ */ jsx(
301
+ "div",
302
+ {
303
+ className: "ui-attachments-drop-layer",
304
+ "data-active": void 0,
305
+ "aria-hidden": "true",
306
+ onDragOver: (event) => event.preventDefault(),
307
+ onDrop: async (event) => {
308
+ event.preventDefault();
309
+ const files = await readDroppedFiles(event.dataTransfer, false);
310
+ dispatchFiles(files);
311
+ }
312
+ }
313
+ ) : null
314
+ ]
315
+ }
316
+ ) });
317
+ }
318
+ );
319
+ Attachments.displayName = "Attachments";
320
+ export {
321
+ Attachments
322
+ };
@@ -22,6 +22,7 @@ import { CONTROL_STATUS_CHROME_CLASS, CONTROL_VARIANT_CHROME_CLASS } from "./con
22
22
  import { Input } from "./input.js";
23
23
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
24
24
  import { Calendar } from "./calendar.js";
25
+ import { dateTimeFormat, numberFormat } from "../../lib/intl-cache.js";
25
26
  const ISO_HINT = "yyyy-mm-dd";
26
27
  const PERIOD_PICKERS = /* @__PURE__ */ new Set(["month", "quarter", "year"]);
27
28
  const PERIOD_COLUMNS = 3;
@@ -49,6 +50,7 @@ function DatePicker(props) {
49
50
  disabledDate,
50
51
  cellRender,
51
52
  allowClear,
53
+ triggerLabel,
52
54
  format: formatProp,
53
55
  parseFormat,
54
56
  minDate,
@@ -296,13 +298,9 @@ function DatePicker(props) {
296
298
  (index) => index < periodCells
297
299
  );
298
300
  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);
301
+ const prevDisabled = () => !pickerDateAllowed(periodPage(viewYear - pageStep)[periodCells - 1], minimum, void 0);
302
+ const nextDisabled = () => !pickerDateAllowed(periodPage(viewYear + pageStep)[0], void 0, maximum);
303
+ const periodLabel = (date, index) => picker === "year" ? dateTimeFormat(locale, { year: "numeric" }).format(date) : picker === "quarter" ? t("dataEntry.datePicker.quarter", { quarter: index + 1 }) ?? `Q${index + 1}` : dateTimeFormat(locale, { month: "short" }).format(date);
306
304
  const periodPick = (date) => {
307
305
  if (!range) {
308
306
  choose(date);
@@ -318,7 +316,7 @@ function DatePicker(props) {
318
316
  choose({ from: pendingFrom, to: date });
319
317
  if (!needConfirm) setOpen(false);
320
318
  };
321
- const periodPanel = /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-panel", children: [
319
+ const renderPeriodPanel = () => /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-panel", children: [
322
320
  /* @__PURE__ */ jsxs("div", { className: "ui-month-picker-nav", children: [
323
321
  /* @__PURE__ */ jsx(
324
322
  Button,
@@ -326,21 +324,21 @@ function DatePicker(props) {
326
324
  type: "button",
327
325
  variant: "outline",
328
326
  size: "icon-sm",
329
- disabled: prevDisabled,
327
+ disabled: prevDisabled(),
330
328
  "aria-label": t("dataEntry.monthPicker.previousYear") ?? "Previous year",
331
329
  className: "ui-month-picker-nav-button",
332
330
  onClick: () => setViewYear(viewYear - pageStep),
333
331
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ui-month-picker-icon", "aria-hidden": "true" })
334
332
  }
335
333
  ),
336
- /* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: new Intl.NumberFormat(locale, { useGrouping: false }).format(viewYear) }),
334
+ /* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: numberFormat(locale, { useGrouping: false }).format(viewYear) }),
337
335
  /* @__PURE__ */ jsx(
338
336
  Button,
339
337
  {
340
338
  type: "button",
341
339
  variant: "outline",
342
340
  size: "icon-sm",
343
- disabled: nextDisabled,
341
+ disabled: nextDisabled(),
344
342
  "aria-label": t("dataEntry.monthPicker.nextYear") ?? "Next year",
345
343
  className: "ui-month-picker-nav-button",
346
344
  onClick: () => setViewYear(viewYear + pageStep),
@@ -390,7 +388,7 @@ function DatePicker(props) {
390
388
  showClose,
391
389
  onClose: () => setOpen(false)
392
390
  };
393
- const dayPanel = range ? /* @__PURE__ */ jsx(
391
+ const renderDayPanel = () => range ? /* @__PURE__ */ jsx(
394
392
  Calendar,
395
393
  {
396
394
  mode: "range",
@@ -458,7 +456,7 @@ function DatePicker(props) {
458
456
  type: "button",
459
457
  disabled: allDisabled,
460
458
  tabIndex: -1,
461
- "aria-label": (isPeriod ? t("dataEntry.monthPicker.openGrid") : range ? t("dataEntry.dateRangePicker.openCalendar") : t("dataEntry.datePicker.openCalendar")) ?? "Open calendar",
459
+ "aria-label": triggerLabel ?? (isPeriod ? t("dataEntry.monthPicker.openGrid") : range ? t("dataEntry.dateRangePicker.openCalendar") : t("dataEntry.datePicker.openCalendar")) ?? "Open calendar",
462
460
  className: range ? "text-muted-foreground hover:text-foreground shrink-0" : "ui-control-inline-affix-action",
463
461
  children: /* @__PURE__ */ jsx(
464
462
  CalendarIcon,
@@ -502,7 +500,7 @@ function DatePicker(props) {
502
500
  },
503
501
  index
504
502
  )) }) : null,
505
- isPeriod ? periodPanel : dayPanel,
503
+ isPeriod ? renderPeriodPanel() : renderDayPanel(),
506
504
  showTime ? /* @__PURE__ */ jsx(Flex, { pad: "sm", children: /* @__PURE__ */ jsx(
507
505
  TimePicker,
508
506
  {
@@ -69,3 +69,5 @@ export { TimeRangePicker } from "./time-range-picker.js";
69
69
  export type { TimeRangePickerProps } from "./time-range-picker.js";
70
70
  export type { UploadRequestContext, UploadResult, UploadRejection } from "./upload-types.js";
71
71
  export type { UploadItemActions } from "./upload-types.js";
72
+ export { Attachments } from "./attachments.js";
73
+ export type { AttachmentsProp, AttachmentsProps, AttachmentsItemProp, AttachmentsPlaceholderProp, AttachmentsOverflowProp, AttachmentsRefProp, } from "./attachments.js";
@@ -59,7 +59,9 @@ import { TagInput } from "./tag-input.js";
59
59
  import { Segmented } from "./segmented.js";
60
60
  import { BranchScopePicker } from "./branch-scope-picker.js";
61
61
  import { TimeRangePicker } from "./time-range-picker.js";
62
+ import { Attachments } from "./attachments.js";
62
63
  export {
64
+ Attachments,
63
65
  BranchScopePicker,
64
66
  Calendar,
65
67
  Cascader,
@@ -7,6 +7,7 @@ import { cn } from "../../lib/utils.js";
7
7
  import { pickFieldA11y } from "../../lib/field-a11y.js";
8
8
  import { Button } from "../general/button.js";
9
9
  import { Input } from "./input.js";
10
+ import { numberFormat } from "../../lib/intl-cache.js";
10
11
  function decimalsOf(n) {
11
12
  if (!Number.isFinite(n)) return 0;
12
13
  const s = String(n);
@@ -77,7 +78,7 @@ const NumberInput = React.forwardRef(
77
78
  const numericValue = isControlled ? controlledValue ?? null : internal;
78
79
  const effectivePrecision = precision ?? decimalsOf(step);
79
80
  const intlFormatter = React.useMemo(
80
- () => new Intl.NumberFormat(locale, {
81
+ () => numberFormat(locale, {
81
82
  minimumFractionDigits: 0,
82
83
  maximumFractionDigits: Math.max(effectivePrecision, 0),
83
84
  useGrouping: false
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import type { ToneProp } from "../../props/vocabulary/index.js";
3
- import type { AlertDialogProp } from "../../props/components/feedback.prop.js";
4
- export type { AlertDialogProp, AlertDialogProp as AlertDialogProps, } from "../../props/components/feedback.prop.js";
3
+ import type { AlertDialogProp, DialogContentProp, DialogProp } from "../../props/components/feedback.prop.js";
4
+ export type { AlertDialogProp, AlertDialogProp as AlertDialogProps, DialogProp, DialogProp as DialogProps, DialogContentProp, DialogContentProp as DialogContentProps, } from "../../props/components/feedback.prop.js";
5
5
  interface OverlayOpenProps {
6
6
  /** Trạng thái mở, có kiểm soát. */
7
7
  open?: boolean;
@@ -10,7 +10,7 @@ interface OverlayOpenProps {
10
10
  /** Gọi khi trạng thái mở đổi. */
11
11
  onOpenChange?: (open: boolean) => void;
12
12
  }
13
- interface DialogRootProps extends OverlayOpenProps {
13
+ interface DialogRootProps extends OverlayOpenProps, Pick<DialogProp, "variant"> {
14
14
  /**
15
15
  * Giữ tên prop của Radix. RAC không có kiểu overlay không-modal: `Modal` LUÔN khoá cuộn và ẩn
16
16
  * nền khỏi trình đọc màn hình, nên `modal={false}` không còn tắt được điều đó.
@@ -19,7 +19,7 @@ interface DialogRootProps extends OverlayOpenProps {
19
19
  children?: React.ReactNode;
20
20
  }
21
21
  /** `data-slot="dialog"` cũ nằm trên Root của Radix, thứ không dựng thẻ nào — nó chưa từng ra DOM. */
22
- declare function DialogRoot({ children, modal: _modal, ...openProps }: DialogRootProps): React.JSX.Element;
22
+ declare function DialogRoot({ children, modal: _modal, variant, ...openProps }: DialogRootProps): React.JSX.Element;
23
23
  interface DialogTriggerProps extends React.ComponentPropsWithRef<"button"> {
24
24
  /** Mượn thẻ của con thay vì dựng `<button>` riêng. */
25
25
  asChild?: boolean;
@@ -48,7 +48,7 @@ declare function DialogOverlay(_props: DialogOverlayProps): null;
48
48
  declare namespace DialogOverlay {
49
49
  var displayName: string;
50
50
  }
51
- interface DialogContentProps extends Omit<React.ComponentPropsWithRef<"section">, "role"> {
51
+ interface DialogContentProps extends Omit<React.ComponentPropsWithRef<"section">, "role">, Pick<DialogContentProp, "variant"> {
52
52
  showClose?: boolean;
53
53
  showCloseButton?: boolean;
54
54
  overlayClassName?: string;
@@ -57,7 +57,7 @@ interface DialogContentProps extends Omit<React.ComponentPropsWithRef<"section">
57
57
  /** Giữ tên prop của Radix — xem `useCloseAutoFocus`. */
58
58
  onCloseAutoFocus?: (event: Event) => void;
59
59
  }
60
- declare function DialogContent({ className, children, showClose, showCloseButton: showCloseButtonProp, overlayClassName, forceMount: _forceMount, ...props }: DialogContentProps): React.JSX.Element;
60
+ declare function DialogContent({ className, children, showClose, showCloseButton: showCloseButtonProp, overlayClassName, variant: variantProp, forceMount: _forceMount, ...props }: DialogContentProps): React.JSX.Element;
61
61
  declare namespace DialogContent {
62
62
  var displayName: string;
63
63
  }
@@ -125,7 +125,10 @@ interface DialogActionProps extends React.ComponentPropsWithRef<"button"> {
125
125
  /** Mượn thẻ của con thay vì dựng `<button>` riêng. */
126
126
  asChild?: boolean;
127
127
  }
128
- /** Confirm mode — primary action (maps to Radix AlertDialogAction). */
128
+ /**
129
+ * Confirm mode — primary action (maps to Radix AlertDialogAction). Nhấn mạnh đọc từ `variant` của
130
+ * hộp thoại: đây là phần thứ ba của một-prop-ba-việc ở đầu tệp, và là chỗ antd đặt `okType`.
131
+ */
129
132
  declare function DialogAction({ asChild, className, onClick, ...props }: DialogActionProps): React.JSX.Element;
130
133
  declare namespace DialogAction {
131
134
  var displayName: string;
@@ -40,6 +40,7 @@ function useDialogOpenState(component) {
40
40
  return state;
41
41
  }
42
42
  const DialogLabelContext = React.createContext(null);
43
+ const DialogVariantContext = React.createContext("default");
43
44
  const OVERLAY_CLASS = "ui-dialog-overlay data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0";
44
45
  const CONTENT_CLASS = "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 duration-200 outline-none";
45
46
  function DialogShell({
@@ -89,9 +90,14 @@ function DialogShell({
89
90
  }
90
91
  );
91
92
  }
92
- function DialogRoot({ children, modal: _modal, ...openProps }) {
93
+ function DialogRoot({
94
+ children,
95
+ modal: _modal,
96
+ variant = "default",
97
+ ...openProps
98
+ }) {
93
99
  const state = useOverlayOpenState(openProps);
94
- return /* @__PURE__ */ jsx(DialogOpenContext.Provider, { value: state, children });
100
+ return /* @__PURE__ */ jsx(DialogOpenContext.Provider, { value: state, children: /* @__PURE__ */ jsx(DialogVariantContext.Provider, { value: variant, children }) });
95
101
  }
96
102
  function DialogTrigger({ asChild, onClick, ...props }) {
97
103
  const state = useDialogOpenState("DialogTrigger");
@@ -139,24 +145,39 @@ function DialogContent({
139
145
  showClose,
140
146
  showCloseButton: showCloseButtonProp,
141
147
  overlayClassName,
148
+ variant: variantProp,
142
149
  forceMount: _forceMount,
143
150
  ...props
144
151
  }) {
145
152
  const { t } = useTranslation();
146
- const showCloseButton = showCloseButtonProp ?? showClose ?? true;
147
- return /* @__PURE__ */ jsxs(DialogShell, { isDismissable: true, className, overlayClassName, ...props, children: [
148
- children,
149
- showCloseButton ? /* @__PURE__ */ jsxs(
150
- DialogClose,
151
- {
152
- className: "ui-focus-ring transition-opacity disabled:pointer-events-none",
153
- children: [
154
- /* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" }),
155
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("feedback.alert.dismiss") })
156
- ]
157
- }
158
- ) : null
159
- ] });
153
+ const inheritedVariant = React.useContext(DialogVariantContext);
154
+ const variant = variantProp ?? inheritedVariant;
155
+ const isDestructive = variant === "destructive";
156
+ const showCloseButton = showCloseButtonProp ?? showClose ?? !isDestructive;
157
+ return /* @__PURE__ */ jsx(DialogVariantContext.Provider, { value: variant, children: /* @__PURE__ */ jsxs(
158
+ DialogShell,
159
+ {
160
+ role: isDestructive ? "alertdialog" : "dialog",
161
+ isDismissable: !isDestructive,
162
+ "data-variant": variant,
163
+ className,
164
+ overlayClassName,
165
+ ...props,
166
+ children: [
167
+ children,
168
+ showCloseButton ? /* @__PURE__ */ jsxs(
169
+ DialogClose,
170
+ {
171
+ className: "ui-focus-ring transition-opacity disabled:pointer-events-none",
172
+ children: [
173
+ /* @__PURE__ */ jsx(X, { className: "ui-dialog-close-icon", "aria-hidden": "true" }),
174
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("feedback.alert.dismiss") })
175
+ ]
176
+ }
177
+ ) : null
178
+ ]
179
+ }
180
+ ) });
160
181
  }
161
182
  DialogContent.displayName = "DialogContent";
162
183
  const DialogHeader = ({
@@ -312,12 +333,14 @@ const AlertDialogTitle = DialogTitle;
312
333
  const AlertDialogDescription = DialogDescription;
313
334
  function DialogAction({ asChild, className, onClick, ...props }) {
314
335
  const state = useDialogOpenState("DialogAction");
336
+ const variant = React.useContext(DialogVariantContext);
315
337
  const Comp = asChild ? Slot : "button";
316
338
  return /* @__PURE__ */ jsx(
317
339
  Comp,
318
340
  {
319
341
  type: "button",
320
- className: cn(buttonVariants(), className),
342
+ "data-variant": asChild ? void 0 : variant,
343
+ className: cn(buttonVariants({ variant }), className),
321
344
  ...props,
322
345
  onClick: chain(onClick, () => {
323
346
  state.setOpen(false);
@@ -1,13 +1,13 @@
1
1
  export { Dialog, DialogClose, DialogContent, DialogDescription, DialogBody, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DialogAction, DialogCancel, AlertDialogRoot, AlertDialogTrigger, AlertDialogPortal, AlertDialogOverlay, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, AlertDialog, } from "./dialog.js";
2
- export type { AlertDialogProp, AlertDialogProps } from "./dialog.js";
2
+ export type { AlertDialogProp, AlertDialogProps, DialogProp, DialogProps, DialogContentProp, DialogContentProps, } from "./dialog.js";
3
3
  export { Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, useSheetResponsiveMode, } from "./sheet.js";
4
4
  export type { SheetContentProps, SheetPresentation, SheetResponsiveProp } from "./sheet.js";
5
5
  export { TwoFactorSetup } from "./two-factor-setup.js";
6
6
  export type { TwoFactorSetupLabels, TwoFactorSetupProps } from "./two-factor-setup.js";
7
7
  export { Toaster } from "./sonner.js";
8
8
  export { toast } from "./use-toast.js";
9
- export { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat, SkeletonArticle, SkeletonAvatar, SkeletonButton, SkeletonInput, SkeletonNode, SkeletonImage, } from "./skeleton.js";
10
- export type { SkeletonProp, SkeletonProps, SkeletonWidth, SkeletonArticleProp, SkeletonArticleProps, SkeletonAvatarProp, SkeletonAvatarProps, SkeletonButtonProp, SkeletonButtonProps, SkeletonInputProp, SkeletonInputProps, SkeletonNodeProp, SkeletonNodeProps, SkeletonImageProp, SkeletonImageProps, } from "./skeleton.js";
9
+ export { Skeleton, SkeletonForm, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat, SkeletonArticle, SkeletonAvatar, SkeletonButton, SkeletonInput, SkeletonNode, SkeletonImage, } from "./skeleton.js";
10
+ export type { SkeletonProp, SkeletonProps, SkeletonWidth, SkeletonArticleProp, SkeletonArticleProps, SkeletonAvatarProp, SkeletonAvatarProps, SkeletonButtonProp, SkeletonButtonProps, SkeletonFormProp, SkeletonFormProps, SkeletonInputProp, SkeletonInputProps, SkeletonNodeProp, SkeletonNodeProps, SkeletonImageProp, SkeletonImageProps, } from "./skeleton.js";
11
11
  export { Banner } from "./banner.js";
12
12
  export type { BannerProp, BannerProps } from "./banner.js";
13
13
  export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, } from "./alert.js";
@@ -44,6 +44,7 @@ import { Toaster } from "./sonner.js";
44
44
  import { toast } from "./use-toast.js";
45
45
  import {
46
46
  Skeleton,
47
+ SkeletonForm,
47
48
  SkeletonRows,
48
49
  SkeletonTable,
49
50
  SkeletonDetail,
@@ -114,6 +115,7 @@ export {
114
115
  SkeletonAvatar,
115
116
  SkeletonButton,
116
117
  SkeletonDetail,
118
+ SkeletonForm,
117
119
  SkeletonImage,
118
120
  SkeletonInput,
119
121
  SkeletonNode,
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
- import type { SkeletonArticleProp, SkeletonAvatarProp, SkeletonButtonProp, SkeletonImageProp, SkeletonInputProp, SkeletonNodeProp, SkeletonProp } from "../../props/components/feedback.prop.js";
3
- export type { SkeletonProp, SkeletonProp as SkeletonProps, SkeletonWidth, SkeletonArticleProp, SkeletonArticleProp as SkeletonArticleProps, SkeletonAvatarProp, SkeletonAvatarProp as SkeletonAvatarProps, SkeletonButtonProp, SkeletonButtonProp as SkeletonButtonProps, SkeletonInputProp, SkeletonInputProp as SkeletonInputProps, SkeletonNodeProp, SkeletonNodeProp as SkeletonNodeProps, SkeletonImageProp, SkeletonImageProp as SkeletonImageProps, } from "../../props/components/feedback.prop.js";
2
+ import type { SkeletonArticleProp, SkeletonAvatarProp, SkeletonButtonProp, SkeletonFormProp, SkeletonImageProp, SkeletonInputProp, SkeletonNodeProp, SkeletonProp } from "../../props/components/feedback.prop.js";
3
+ export type { SkeletonProp, SkeletonProp as SkeletonProps, SkeletonFormProp, SkeletonFormProp as SkeletonFormProps, SkeletonWidth, SkeletonArticleProp, SkeletonArticleProp as SkeletonArticleProps, SkeletonAvatarProp, SkeletonAvatarProp as SkeletonAvatarProps, SkeletonButtonProp, SkeletonButtonProp as SkeletonButtonProps, SkeletonInputProp, SkeletonInputProp as SkeletonInputProps, SkeletonNodeProp, SkeletonNodeProp as SkeletonNodeProps, SkeletonImageProp, SkeletonImageProp as SkeletonImageProps, } from "../../props/components/feedback.prop.js";
4
4
  declare function SkeletonBlock({ active, loading, className, children, ...props }: SkeletonProp): React.JSX.Element;
5
5
  /** Stands in for an `Avatar` — circle for a person, square for an entity mark. */
6
6
  export declare function SkeletonAvatar({ size, shape, active, className }: SkeletonAvatarProp): React.JSX.Element;
@@ -39,6 +39,15 @@ interface SkeletonRowsProps {
39
39
  }
40
40
  /** Skeleton for a flat list of rows (use inside a Card or section). */
41
41
  export declare function SkeletonRows({ rows, columns, className }: SkeletonRowsProps): React.JSX.Element;
42
+ /**
43
+ * Skeleton of a `Form columns={N}` — label + control PAIRS on the form's own grid (gh#552).
44
+ *
45
+ * It renders through `ResponsiveGrid` with the SAME `columns` value the form takes, so the two
46
+ * share one breakpoint ladder rather than two that agree today. `SkeletonRows` could get the column
47
+ * count right and never the inside of a cell: a form field is a short label stacked on a full-width
48
+ * control, and a flat line list is neither.
49
+ */
50
+ export declare function SkeletonForm({ columns, fields, active, className }: SkeletonFormProp): React.JSX.Element;
42
51
  /** Skeleton matching the DataTable layout — header row + N body rows. */
43
52
  export declare function SkeletonTable({ rows, columns }: SkeletonRowsProps): React.JSX.Element;
44
53
  /** Skeleton matching a Card detail layout — title + 6 metadata rows. */