@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.
- package/dist/app/timezones.js +2 -1
- package/dist/components/charts/chart-cartesian.js +2 -4
- package/dist/components/charts/chart-frame.js +2 -1
- package/dist/components/data-display/card.d.ts +87 -1
- package/dist/components/data-display/card.js +88 -15
- package/dist/components/data-display/index.d.ts +5 -1
- package/dist/components/data-display/index.js +5 -0
- package/dist/components/data-display/thought-chain.d.ts +42 -0
- package/dist/components/data-display/thought-chain.js +177 -0
- package/dist/components/data-display/welcome.d.ts +26 -0
- package/dist/components/data-display/welcome.js +37 -0
- package/dist/components/data-entry/attachments.d.ts +19 -0
- package/dist/components/data-entry/attachments.js +322 -0
- package/dist/components/data-entry/date-picker.js +12 -14
- package/dist/components/data-entry/index.d.ts +2 -0
- package/dist/components/data-entry/index.js +2 -0
- package/dist/components/data-entry/number-input.js +2 -1
- package/dist/components/feedback/dialog.d.ts +10 -7
- package/dist/components/feedback/dialog.js +40 -17
- package/dist/components/feedback/index.d.ts +3 -3
- package/dist/components/feedback/index.js +2 -0
- package/dist/components/feedback/skeleton.d.ts +11 -2
- package/dist/components/feedback/skeleton.js +8 -0
- package/dist/components/general/actions.d.ts +59 -0
- package/dist/components/general/actions.js +253 -0
- package/dist/components/general/activity.js +1 -0
- package/dist/components/general/button.js +2 -1
- package/dist/components/general/float-button.d.ts +41 -0
- package/dist/components/general/float-button.js +336 -0
- package/dist/components/general/index.d.ts +6 -2
- package/dist/components/general/index.js +12 -1
- package/dist/components/general/typography.d.ts +79 -4
- package/dist/components/general/typography.js +598 -54
- package/dist/components/layout/draggable-panel.d.ts +18 -0
- package/dist/components/layout/draggable-panel.js +189 -0
- package/dist/components/layout/error-surface.js +3 -2
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.js +2 -0
- package/dist/components/navigation/conversations.d.ts +39 -0
- package/dist/components/navigation/conversations.js +371 -0
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/ui/card.js +1 -0
- package/dist/components/ui/toggle.js +2 -1
- package/dist/i18n/messages/en.json +53 -0
- package/dist/i18n/messages/ja.json +53 -0
- package/dist/i18n/messages/vi.json +53 -0
- package/dist/i18n/translate.js +3 -2
- package/dist/lib/datetime/picker-format.js +2 -1
- package/dist/lib/format.js +3 -2
- package/dist/lib/intl-cache.d.ts +32 -0
- package/dist/lib/intl-cache.js +29 -0
- package/dist/props/components/data-display.prop.d.ts +143 -1
- package/dist/props/components/data-entry.prop.d.ts +87 -0
- package/dist/props/components/feedback.prop.d.ts +53 -1
- package/dist/props/components/general.prop.d.ts +440 -3
- package/dist/props/components/index.d.ts +2 -2
- package/dist/props/components/layout.prop.d.ts +63 -1
- package/dist/props/components/navigation.prop.d.ts +150 -0
- package/dist/props/registry.d.ts +431 -0
- package/dist/props/registry.js +551 -0
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +113 -0
- package/dist/styles/alert-layout.css +22 -0
- package/dist/styles/card-layout.css +34 -0
- package/dist/styles/core.css +1 -0
- package/dist/styles/data-display-layout.css +236 -0
- package/dist/styles/float-button-layout.css +168 -0
- package/dist/styles/form-layout.css +11 -1
- package/dist/styles/index.css +1 -0
- package/dist/styles/layout.css +196 -0
- package/dist/styles/navigation-layout.css +139 -0
- package/dist/styles/text-layout.css +156 -0
- package/dist/tokens/axes.css +5 -0
- package/dist/tokens/base.css +7 -0
- package/dist/tokens/components/actions.css +14 -0
- package/dist/tokens/components/attachments.css +15 -0
- package/dist/tokens/components/conversations.css +28 -0
- package/dist/tokens/components/draggable-panel.css +25 -0
- package/dist/tokens/components/float-button.css +39 -0
- package/dist/tokens/components/text.css +13 -0
- package/dist/tokens/components/thought-chain.css +31 -0
- package/dist/tokens/components/welcome.css +19 -0
- package/dist/tokens/foundation.css +6 -4
- package/docs/DESIGN-AUTHORITY.md +133 -20
- package/docs/FRAME-COVERAGE-REPORT.md +5 -2
- package/docs/data-display/card/examples/tab-list.tsx +97 -0
- package/docs/data-display/thought-chain.tsx +176 -0
- package/docs/data-display/welcome.tsx +115 -0
- package/docs/data-entry/attachments.tsx +160 -0
- package/docs/feedback/dialog.tsx +50 -0
- package/docs/general/actions.tsx +175 -0
- package/docs/general/float-button.tsx +133 -0
- package/docs/general/typography.tsx +63 -1
- package/docs/layout/draggable-panel.tsx +127 -0
- package/docs/navigation/conversations.tsx +176 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +17 -16
- package/package.json +20 -4
- package/scripts/_agent-setup.mjs +36 -5
- package/scripts/guinea-pig-skill.md +14 -0
- 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
|
-
|
|
301
|
-
|
|
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
|
|
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:
|
|
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
|
|
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 ?
|
|
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
|
-
() =>
|
|
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
|
-
/**
|
|
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({
|
|
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
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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. */
|