@godxjp/ui 28.10.0 → 28.13.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/agent/START-HERE.md +2 -2
- package/agent/components/AppLauncher.json +10 -0
- package/agent/components/Attachments.json +27 -1
- package/agent/components/BranchScopePicker.json +10 -0
- package/agent/components/Cascader.json +5 -0
- package/agent/components/Checkbox.json +6 -0
- package/agent/components/CredentialReveal.json +21 -0
- package/agent/components/InputOTP.json +35 -0
- package/agent/components/PermissionMatrix.json +5 -0
- package/agent/components/SearchInput.json +5 -0
- package/agent/components/ServiceLauncherCard.json +10 -3
- package/agent/components/ServiceRolePanel.json +5 -0
- package/agent/components/Switch.json +6 -0
- package/agent/components/Tabs.json +10 -0
- package/agent/components/TimeRangePicker.json +5 -0
- package/agent/components/Transfer.json +5 -0
- package/agent/components/Upload.json +5 -0
- package/agent/components-index.json +1 -1
- package/agent/components.json +170 -4
- package/agent/index.json +3 -3
- package/agent/llms.txt +4 -4
- package/agent/tokens.json +30 -25
- package/dist/components/data-display/service-launcher-card.d.ts +19 -0
- package/dist/components/data-display/service-launcher-card.js +14 -1
- package/dist/components/data-entry/attachments.js +77 -33
- package/dist/components/ui/avatar.d.ts +1 -18
- package/dist/components/ui/avatar.js +1 -36
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +0 -530
- package/dist/i18n/messages/ja.json +0 -524
- package/dist/i18n/messages/vi.json +0 -524
- package/dist/lib/image-loading-status.d.ts +25 -0
- package/dist/lib/image-loading-status.js +41 -0
- package/dist/props/registry.d.ts +5 -1
- package/dist/props/registry.js +12 -1
- package/dist/styles/card-layout.css +6 -0
- package/dist/styles/control.css +2 -2
- package/dist/styles/data-entry-layout.css +245 -2
- package/dist/tokens/components/attachments.css +18 -9
- package/dist/tokens/components/segmented.css +2 -2
- package/docs/DESIGN-AUTHORITY.md +51 -0
- package/docs/assets/service-mark-rose.svg +6 -0
- package/docs/assets/service-mark-teal.svg +5 -0
- package/docs/data-display/service-launcher-card.tsx +232 -92
- package/docs/i18n/messages/en.json +699 -0
- package/docs/i18n/messages/ja.json +693 -0
- package/docs/i18n/messages/vi.json +693 -0
- package/docs/showcase/marketing-page.tsx +3 -2
- package/docs/showcase/table-pagination.tsx +18 -7
- package/docs/showcase/theme-customization.tsx +2 -1
- package/package.json +4 -3
|
@@ -6,7 +6,6 @@ import { useTranslation } from "../../i18n/use-translation.js";
|
|
|
6
6
|
import { formatBytes } from "../../lib/format.js";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
8
8
|
import { Button } from "../general/button.js";
|
|
9
|
-
import { Text } from "../general/typography.js";
|
|
10
9
|
import { Progress } from "../data-display/progress.js";
|
|
11
10
|
import { createUploadItem } from "./upload-types.js";
|
|
12
11
|
import { readDroppedFiles } from "./upload-files.js";
|
|
@@ -30,6 +29,10 @@ function resolvePlaceholder(placeholder, type) {
|
|
|
30
29
|
if (!placeholder) return {};
|
|
31
30
|
return typeof placeholder === "function" ? placeholder(type) : placeholder;
|
|
32
31
|
}
|
|
32
|
+
function splitFileName(name) {
|
|
33
|
+
const match = /^(.*)\.[^.]+$/.exec(name ?? "");
|
|
34
|
+
return match ? [match[1], (name ?? "").slice(match[1].length)] : [name ?? "", ""];
|
|
35
|
+
}
|
|
33
36
|
function fileMatchesAccept(file, accept) {
|
|
34
37
|
if (!accept) return true;
|
|
35
38
|
return accept.split(",").some((rule) => {
|
|
@@ -60,6 +63,7 @@ function PlaceholderUploader({
|
|
|
60
63
|
className: cn("ui-attachments-placeholder", className),
|
|
61
64
|
style,
|
|
62
65
|
"data-drag-active": dragActive ? "true" : void 0,
|
|
66
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
63
67
|
"aria-hidden": disabled ? true : void 0,
|
|
64
68
|
onDragEnter: (event) => {
|
|
65
69
|
event.preventDefault();
|
|
@@ -71,6 +75,7 @@ function PlaceholderUploader({
|
|
|
71
75
|
},
|
|
72
76
|
onDrop: async (event) => {
|
|
73
77
|
event.preventDefault();
|
|
78
|
+
event.stopPropagation();
|
|
74
79
|
setDragActive(false);
|
|
75
80
|
if (disabled) return;
|
|
76
81
|
const files = await readDroppedFiles(event.dataTransfer, false);
|
|
@@ -107,27 +112,55 @@ function AttachmentCard({
|
|
|
107
112
|
const { t } = useTranslation();
|
|
108
113
|
const { disabled } = React.useContext(AttachmentContext);
|
|
109
114
|
const preview = item.thumbUrl ?? item.url;
|
|
115
|
+
const [namePrefix, nameSuffix] = splitFileName(item.name);
|
|
110
116
|
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(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
117
|
+
return /* @__PURE__ */ jsxs(
|
|
118
|
+
"li",
|
|
119
|
+
{
|
|
120
|
+
className: cn("ui-attachments-card", classNames?.card, classNames?.file),
|
|
121
|
+
"data-status": item.status,
|
|
122
|
+
children: [
|
|
123
|
+
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, {}) }),
|
|
124
|
+
/* @__PURE__ */ jsxs("span", { className: "ui-attachments-card-content", children: [
|
|
125
|
+
/* @__PURE__ */ jsxs("span", { className: "ui-attachments-card-name", title: item.name, children: [
|
|
126
|
+
/* @__PURE__ */ jsx("span", { className: "ui-attachments-card-name-prefix", children: namePrefix }),
|
|
127
|
+
/* @__PURE__ */ jsx("span", { className: "ui-attachments-card-name-suffix", children: nameSuffix })
|
|
128
|
+
] }),
|
|
129
|
+
desc ? /* @__PURE__ */ jsx(
|
|
130
|
+
"span",
|
|
131
|
+
{
|
|
132
|
+
className: "ui-attachments-card-meta",
|
|
133
|
+
title: typeof desc === "string" ? desc : void 0,
|
|
134
|
+
children: desc
|
|
135
|
+
}
|
|
136
|
+
) : null
|
|
137
|
+
] }),
|
|
138
|
+
item.status === "uploading" && item.percent != null ? /* @__PURE__ */ jsx("span", { className: "ui-attachments-card-progress", children: /* @__PURE__ */ jsx(
|
|
139
|
+
Progress,
|
|
140
|
+
{
|
|
141
|
+
size: "sm",
|
|
142
|
+
value: item.percent,
|
|
143
|
+
"aria-label": t("dataEntry.attachments.uploading")
|
|
144
|
+
}
|
|
145
|
+
) }) : null,
|
|
146
|
+
!disabled && onRemove ? /* @__PURE__ */ jsx(
|
|
147
|
+
Button,
|
|
148
|
+
{
|
|
149
|
+
type: "button",
|
|
150
|
+
variant: "ghost",
|
|
151
|
+
size: "icon-xs",
|
|
152
|
+
className: "ui-attachments-card-remove",
|
|
153
|
+
"aria-label": t("dataEntry.attachments.remove", { name: item.name }),
|
|
154
|
+
onClick: (event) => {
|
|
155
|
+
event.stopPropagation();
|
|
156
|
+
onRemove();
|
|
157
|
+
},
|
|
158
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
159
|
+
}
|
|
160
|
+
) : null
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
);
|
|
131
164
|
}
|
|
132
165
|
const Attachments = React.forwardRef(
|
|
133
166
|
({
|
|
@@ -151,6 +184,7 @@ const Attachments = React.forwardRef(
|
|
|
151
184
|
const containerRef = React.useRef(null);
|
|
152
185
|
const inputRef = React.useRef(null);
|
|
153
186
|
const [internalItems, setInternalItems] = React.useState([]);
|
|
187
|
+
const [dropActive, setDropActive] = React.useState(false);
|
|
154
188
|
const items = controlledItems ?? internalItems;
|
|
155
189
|
const setItems = React.useCallback(
|
|
156
190
|
(next, file) => {
|
|
@@ -207,7 +241,7 @@ const Attachments = React.forwardRef(
|
|
|
207
241
|
{
|
|
208
242
|
ref: inputRef,
|
|
209
243
|
type: "file",
|
|
210
|
-
className: "ui-attachments-input",
|
|
244
|
+
className: cn("ui-attachments-input", "sr-only"),
|
|
211
245
|
accept,
|
|
212
246
|
multiple: multiple ?? (maxCount == null || maxCount > 1),
|
|
213
247
|
disabled,
|
|
@@ -244,7 +278,7 @@ const Attachments = React.forwardRef(
|
|
|
244
278
|
)
|
|
245
279
|
] });
|
|
246
280
|
}
|
|
247
|
-
const
|
|
281
|
+
const dropScope = getDropContainer?.() ? "viewport" : "control";
|
|
248
282
|
return /* @__PURE__ */ jsx(AttachmentContext.Provider, { value: { disabled }, children: /* @__PURE__ */ jsxs(
|
|
249
283
|
"div",
|
|
250
284
|
{
|
|
@@ -252,6 +286,21 @@ const Attachments = React.forwardRef(
|
|
|
252
286
|
className: cn("ui-attachments", className, rootClassName, classNames.root),
|
|
253
287
|
style: styles.root,
|
|
254
288
|
dir: "auto",
|
|
289
|
+
onDragEnter: (event) => {
|
|
290
|
+
event.preventDefault();
|
|
291
|
+
if (!disabled) setDropActive(true);
|
|
292
|
+
},
|
|
293
|
+
onDragOver: (event) => event.preventDefault(),
|
|
294
|
+
onDragLeave: (event) => {
|
|
295
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setDropActive(false);
|
|
296
|
+
},
|
|
297
|
+
onDrop: async (event) => {
|
|
298
|
+
event.preventDefault();
|
|
299
|
+
setDropActive(false);
|
|
300
|
+
if (disabled) return;
|
|
301
|
+
const files = await readDroppedFiles(event.dataTransfer, false);
|
|
302
|
+
if (files.length) dispatchFiles(files);
|
|
303
|
+
},
|
|
255
304
|
children: [
|
|
256
305
|
fileInput,
|
|
257
306
|
hasFiles ? /* @__PURE__ */ jsxs(
|
|
@@ -297,20 +346,15 @@ const Attachments = React.forwardRef(
|
|
|
297
346
|
hidden: hasFiles
|
|
298
347
|
}
|
|
299
348
|
),
|
|
300
|
-
|
|
349
|
+
/* @__PURE__ */ jsx(
|
|
301
350
|
"div",
|
|
302
351
|
{
|
|
303
352
|
className: "ui-attachments-drop-layer",
|
|
304
|
-
"data-
|
|
305
|
-
"
|
|
306
|
-
|
|
307
|
-
onDrop: async (event) => {
|
|
308
|
-
event.preventDefault();
|
|
309
|
-
const files = await readDroppedFiles(event.dataTransfer, false);
|
|
310
|
-
dispatchFiles(files);
|
|
311
|
-
}
|
|
353
|
+
"data-scope": dropScope,
|
|
354
|
+
"data-active": dropActive ? "true" : void 0,
|
|
355
|
+
"aria-hidden": "true"
|
|
312
356
|
}
|
|
313
|
-
)
|
|
357
|
+
)
|
|
314
358
|
]
|
|
315
359
|
}
|
|
316
360
|
) });
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { type ImageLoadingStatus } from "../../lib/image-loading-status.js";
|
|
2
3
|
import type { AvatarProp } from "../../props/components/data-display.prop.js";
|
|
3
|
-
/**
|
|
4
|
-
* Trạng thái tải của ảnh trong avatar — chia sẻ giữa `AvatarImage` và
|
|
5
|
-
* `AvatarFallback` qua context, đúng như Radix làm.
|
|
6
|
-
*
|
|
7
|
-
* React Aria KHÔNG có avatar, nên đây là việc TỰ DỰNG. Nhưng phần cần dựng lại
|
|
8
|
-
* không phải cái vỏ `<span>` — mà là logic thật mà `@radix-ui/react-avatar` cầm:
|
|
9
|
-
* ảnh chỉ được vẽ khi đã tải XONG, fallback chỉ được vẽ khi ảnh CHƯA xong hoặc
|
|
10
|
-
* HỎNG, và `delayMs` giữ fallback im lặng đủ lâu để mạng nhanh không kịp nháy
|
|
11
|
-
* một khung initials trước khi ảnh về.
|
|
12
|
-
*
|
|
13
|
-
* Phép dò là một `new Image()` RỜI, không phải `onLoad`/`onError` trên chính
|
|
14
|
-
* thẻ được vẽ: thẻ chỉ tồn tại khi đã `loaded`, nên handler trên nó không bao
|
|
15
|
-
* giờ có cơ hội báo "đang tải" hay "hỏng". Đây cũng là lý do một `<img>` hỏng
|
|
16
|
-
* không bao giờ nằm trong DOM để trình duyệt vẽ biểu tượng ảnh vỡ lên trên
|
|
17
|
-
* fallback.
|
|
18
|
-
*/
|
|
19
|
-
type ImageLoadingStatus = "idle" | "loading" | "loaded" | "error";
|
|
20
4
|
export declare const Avatar: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & {
|
|
21
5
|
asChild?: boolean;
|
|
22
6
|
} & Pick<AvatarProp, "appearance" | "presence" | "presenceLabel" | "shape" | "size"> & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -28,4 +12,3 @@ export declare const AvatarFallback: React.ForwardRefExoticComponent<Omit<React.
|
|
|
28
12
|
asChild?: boolean;
|
|
29
13
|
delayMs?: number;
|
|
30
14
|
} & React.RefAttributes<HTMLSpanElement>>;
|
|
31
|
-
export {};
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffectEvent, useLayoutEffect } from "@react-aria/utils";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
+
import { useImageLoadingStatus } from "../../lib/image-loading-status.js";
|
|
6
7
|
import { Slot } from "../../lib/slot.js";
|
|
7
8
|
import { cn } from "../../lib/utils.js";
|
|
8
9
|
const AvatarContext = React.createContext(null);
|
|
@@ -13,42 +14,6 @@ function useAvatarContext(part) {
|
|
|
13
14
|
}
|
|
14
15
|
return context;
|
|
15
16
|
}
|
|
16
|
-
function getImageLoadingStatus(image) {
|
|
17
|
-
if (!image.complete) {
|
|
18
|
-
return "loading";
|
|
19
|
-
}
|
|
20
|
-
return image.naturalWidth > 0 ? "loaded" : "error";
|
|
21
|
-
}
|
|
22
|
-
function useImageLoadingStatus(src, {
|
|
23
|
-
loadingStatus,
|
|
24
|
-
setLoadingStatus,
|
|
25
|
-
referrerPolicy,
|
|
26
|
-
crossOrigin
|
|
27
|
-
}) {
|
|
28
|
-
useLayoutEffect(() => {
|
|
29
|
-
if (!src) {
|
|
30
|
-
setLoadingStatus("error");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const image = new window.Image();
|
|
34
|
-
const handleLoad = (event) => setLoadingStatus(getImageLoadingStatus(event.currentTarget));
|
|
35
|
-
const handleError = () => setLoadingStatus("error");
|
|
36
|
-
image.addEventListener("load", handleLoad);
|
|
37
|
-
image.addEventListener("error", handleError);
|
|
38
|
-
if (referrerPolicy) {
|
|
39
|
-
image.referrerPolicy = referrerPolicy;
|
|
40
|
-
}
|
|
41
|
-
image.crossOrigin = crossOrigin ?? null;
|
|
42
|
-
image.src = src;
|
|
43
|
-
setLoadingStatus(getImageLoadingStatus(image));
|
|
44
|
-
return () => {
|
|
45
|
-
image.removeEventListener("load", handleLoad);
|
|
46
|
-
image.removeEventListener("error", handleError);
|
|
47
|
-
setLoadingStatus("idle");
|
|
48
|
-
};
|
|
49
|
-
}, [src, crossOrigin, referrerPolicy, setLoadingStatus]);
|
|
50
|
-
return loadingStatus;
|
|
51
|
-
}
|
|
52
17
|
const Avatar = React.forwardRef(
|
|
53
18
|
({
|
|
54
19
|
className,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "28.
|
|
3
|
+
"version": "28.13.0",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|