@godxjp/ui 23.4.8 → 23.4.10
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/components/data-entry/upload-crop-dialog.js +3 -2
- package/dist/components/data-entry/upload.js +3 -3
- package/dist/components/feedback/dialog.d.ts +2 -1
- package/dist/components/feedback/dialog.js +26 -21
- package/dist/components/layout/app-launcher.js +2 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/styles/control.css +4 -0
- package/dist/styles/dialog-layout.css +22 -1
- package/dist/styles/layout.css +5 -5
- package/dist/tokens/components/feedback.css +3 -0
- package/docs/FRAME-COVERAGE-REPORT.md +22 -7
- package/docs/data-display/credential-reveal.tsx +9 -6
- package/docs/data-entry/form-field/index.tsx +11 -8
- package/docs/feedback/danger-confirm.tsx +6 -3
- package/docs/feedback/dialog.tsx +21 -12
- package/package.json +4 -3
- package/scripts/ui-audit.mjs +47 -0
|
@@ -6,6 +6,7 @@ import { cn } from "../../lib/utils.js";
|
|
|
6
6
|
import { Button } from "../general/button.js";
|
|
7
7
|
import {
|
|
8
8
|
Dialog,
|
|
9
|
+
DialogBody,
|
|
9
10
|
DialogContent,
|
|
10
11
|
DialogDescription,
|
|
11
12
|
DialogFooter,
|
|
@@ -57,7 +58,7 @@ function UploadCropDialog({ open, onOpenChange, file, onConfirm }) {
|
|
|
57
58
|
/* @__PURE__ */ jsx(DialogTitle, { children: t("dataEntry.upload.cropTitle") }),
|
|
58
59
|
/* @__PURE__ */ jsx(DialogDescription, { children: t("dataEntry.upload.cropDescription") })
|
|
59
60
|
] }),
|
|
60
|
-
/* @__PURE__ */ jsxs("div", { className: "ui-stack-sm", children: [
|
|
61
|
+
/* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("div", { className: "ui-stack-sm", children: [
|
|
61
62
|
/* @__PURE__ */ jsx(
|
|
62
63
|
"div",
|
|
63
64
|
{
|
|
@@ -91,7 +92,7 @@ function UploadCropDialog({ open, onOpenChange, file, onConfirm }) {
|
|
|
91
92
|
}
|
|
92
93
|
)
|
|
93
94
|
] })
|
|
94
|
-
] }),
|
|
95
|
+
] }) }),
|
|
95
96
|
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
96
97
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: t("common.cancel") }),
|
|
97
98
|
/* @__PURE__ */ jsx(Button, { onClick: () => void handleConfirm(), children: t("dataEntry.upload.cropConfirm") })
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "./upload-types.js";
|
|
26
26
|
import { readDroppedFiles } from "./upload-files.js";
|
|
27
27
|
import { uploadRequest } from "./upload-request.js";
|
|
28
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../feedback/dialog.js";
|
|
28
|
+
import { Dialog, DialogBody, DialogContent, DialogHeader, DialogTitle } from "../feedback/dialog.js";
|
|
29
29
|
import { Progress } from "../data-display/progress.js";
|
|
30
30
|
import { useUploadDraft } from "./use-upload-draft.js";
|
|
31
31
|
import { collectUploadCommitActions, createUploadItem as createUploadItem2, UPLOAD_LIST_IGNORE as UPLOAD_LIST_IGNORE2 } from "./upload-types.js";
|
|
@@ -414,14 +414,14 @@ function Upload({
|
|
|
414
414
|
},
|
|
415
415
|
children: /* @__PURE__ */ jsxs(DialogContent, { "aria-describedby": void 0, children: [
|
|
416
416
|
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: previewItem?.name ?? t("dataEntry.upload.preview") }) }),
|
|
417
|
-
previewItem?.previewUrl && /* @__PURE__ */ jsx(
|
|
417
|
+
/* @__PURE__ */ jsx(DialogBody, { children: previewItem?.previewUrl && /* @__PURE__ */ jsx(
|
|
418
418
|
"img",
|
|
419
419
|
{
|
|
420
420
|
src: previewItem.previewUrl,
|
|
421
421
|
alt: previewItem.name,
|
|
422
422
|
className: "ui-upload-picture-img"
|
|
423
423
|
}
|
|
424
|
-
)
|
|
424
|
+
) })
|
|
425
425
|
] })
|
|
426
426
|
}
|
|
427
427
|
),
|
|
@@ -142,7 +142,8 @@ declare const AlertDialogAction: typeof DialogAction;
|
|
|
142
142
|
declare const AlertDialogCancel: typeof DialogCancel;
|
|
143
143
|
/**
|
|
144
144
|
* Preset: confirm / destructive / typed-challenge / step-up without compound markup. Both flows
|
|
145
|
-
* force the destructive
|
|
145
|
+
* force the destructive shape: the confirm button plus antd's leading status glyph. The header
|
|
146
|
+
* surface stays untinted — see the note on `showDangerIcon` below.
|
|
146
147
|
*/
|
|
147
148
|
declare function AlertDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, variant, confirmPhrase, challenge, onConfirm, stepUp, keepOpenOnConfirm, pending, }: AlertDialogProp): React.JSX.Element;
|
|
148
149
|
export declare const Dialog: typeof DialogRoot & {
|
|
@@ -4,7 +4,7 @@ import { useOverlayPortalContainer } from "../../lib/overlay-portal.js";
|
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { chain, mergeRefs } from "@react-aria/utils";
|
|
6
6
|
import { Dialog as RacDialog, Modal, ModalOverlay } from "react-aria-components";
|
|
7
|
-
import { X } from "lucide-react";
|
|
7
|
+
import { AlertCircle, X } from "lucide-react";
|
|
8
8
|
import { cn } from "../../lib/utils.js";
|
|
9
9
|
import { Slot } from "../../lib/slot.js";
|
|
10
10
|
import { overlayHeaderToneClass } from "./overlay-header-tone.js";
|
|
@@ -392,7 +392,7 @@ function AlertDialog({
|
|
|
392
392
|
const needsPhrase = phrase != null && phrase.length > 0;
|
|
393
393
|
const phraseMatches = !needsPhrase || typed === phrase;
|
|
394
394
|
const effectiveVariant = needsPhrase ? "destructive" : variant;
|
|
395
|
-
const
|
|
395
|
+
const showDangerIcon = effectiveVariant === "destructive";
|
|
396
396
|
const resolvedConfirm = confirmLabel ?? (needsPhrase ? t("common.delete") : t("common.continue"));
|
|
397
397
|
const resolvedCancel = cancelLabel ?? t("common.cancel");
|
|
398
398
|
const busy = pending || verifying;
|
|
@@ -429,26 +429,31 @@ function AlertDialog({
|
|
|
429
429
|
})();
|
|
430
430
|
};
|
|
431
431
|
return /* @__PURE__ */ jsx(AlertDialogRoot, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
432
|
-
/* @__PURE__ */
|
|
433
|
-
/* @__PURE__ */ jsx(
|
|
434
|
-
|
|
432
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsxs("div", { className: "ui-dialog-confirm-body", children: [
|
|
433
|
+
showDangerIcon && /* @__PURE__ */ jsx(AlertCircle, { className: "ui-dialog-confirm-icon", "aria-hidden": true }),
|
|
434
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-dialog-confirm-text", children: [
|
|
435
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: title }),
|
|
436
|
+
description ? /* @__PURE__ */ jsx(AlertDialogDescription, { children: description }) : null
|
|
437
|
+
] })
|
|
438
|
+
] }) }),
|
|
439
|
+
/* @__PURE__ */ jsxs(DialogBody, { children: [
|
|
440
|
+
needsPhrase && /* @__PURE__ */ jsx(FormField, { id: inputId, label: t("common.typeToConfirm", { phrase }), children: /* @__PURE__ */ jsx(
|
|
441
|
+
Input,
|
|
442
|
+
{
|
|
443
|
+
id: inputId,
|
|
444
|
+
value: typed,
|
|
445
|
+
onChange: (e) => {
|
|
446
|
+
setTyped(e.target.value);
|
|
447
|
+
},
|
|
448
|
+
autoComplete: "off",
|
|
449
|
+
spellCheck: false,
|
|
450
|
+
placeholder: phrase,
|
|
451
|
+
"aria-required": "true",
|
|
452
|
+
disabled: busy
|
|
453
|
+
}
|
|
454
|
+
) }),
|
|
455
|
+
stepUpFailed && /* @__PURE__ */ jsx("p", { id: stepErrorId, role: "alert", className: "ui-dialog-step-up-error", children: t("feedback.alert.stepUpFailed") })
|
|
435
456
|
] }),
|
|
436
|
-
needsPhrase && /* @__PURE__ */ jsx(FormField, { id: inputId, label: t("common.typeToConfirm", { phrase }), children: /* @__PURE__ */ jsx(
|
|
437
|
-
Input,
|
|
438
|
-
{
|
|
439
|
-
id: inputId,
|
|
440
|
-
value: typed,
|
|
441
|
-
onChange: (e) => {
|
|
442
|
-
setTyped(e.target.value);
|
|
443
|
-
},
|
|
444
|
-
autoComplete: "off",
|
|
445
|
-
spellCheck: false,
|
|
446
|
-
placeholder: phrase,
|
|
447
|
-
"aria-required": "true",
|
|
448
|
-
disabled: busy
|
|
449
|
-
}
|
|
450
|
-
) }),
|
|
451
|
-
stepUpFailed && /* @__PURE__ */ jsx("p", { id: stepErrorId, role: "alert", className: "ui-dialog-step-up-error", children: t("feedback.alert.stepUpFailed") }),
|
|
452
457
|
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
453
458
|
/* @__PURE__ */ jsx(DialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", disabled: busy, children: resolvedCancel }) }),
|
|
454
459
|
/* @__PURE__ */ jsx(
|
|
@@ -7,6 +7,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover
|
|
|
7
7
|
import {
|
|
8
8
|
Dialog,
|
|
9
9
|
DialogContent,
|
|
10
|
+
DialogBody,
|
|
10
11
|
DialogHeader,
|
|
11
12
|
DialogTitle,
|
|
12
13
|
DialogTrigger
|
|
@@ -217,7 +218,7 @@ function AppLauncher({
|
|
|
217
218
|
overlayClassName: "ui-app-launcher-launchpad-overlay",
|
|
218
219
|
children: [
|
|
219
220
|
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: labels.title }) }),
|
|
220
|
-
panel
|
|
221
|
+
/* @__PURE__ */ jsx(DialogBody, { children: panel })
|
|
221
222
|
]
|
|
222
223
|
}
|
|
223
224
|
)
|
|
@@ -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": "23.4.
|
|
3
|
+
"version": "23.4.10",
|
|
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,
|
package/dist/styles/control.css
CHANGED
|
@@ -470,6 +470,10 @@
|
|
|
470
470
|
fill: currentColor;
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
.ui-radio[data-state="unchecked"] .ui-radio-icon {
|
|
474
|
+
visibility: hidden;
|
|
475
|
+
}
|
|
476
|
+
|
|
473
477
|
.ui-switch:has(input[aria-invalid="true"]) {
|
|
474
478
|
--tw-ring-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--destructive));
|
|
475
479
|
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--destructive));
|
|
@@ -123,7 +123,8 @@
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
[data-slot="dialog-content"]:has([data-slot="dialog-body"]) [data-slot="dialog-header"]
|
|
126
|
+
[data-slot="dialog-content"]:has([data-slot="dialog-body"]) [data-slot="dialog-header"],
|
|
127
|
+
[data-slot="dialog-header"]:not([data-tone="default"]) {
|
|
127
128
|
margin-inline: calc(-1 * var(--dialog-space-x));
|
|
128
129
|
margin-block-start: calc(-1 * var(--dialog-space-y));
|
|
129
130
|
padding: var(--dialog-space-y) var(--dialog-space-x);
|
|
@@ -222,6 +223,26 @@
|
|
|
222
223
|
[data-slot="dialog-header"]:not([data-tone="default"]) [data-slot="dialog-description"] {
|
|
223
224
|
color: hsl(var(--foreground));
|
|
224
225
|
}
|
|
226
|
+
|
|
227
|
+
.ui-dialog-confirm-body {
|
|
228
|
+
display: flex;
|
|
229
|
+
align-items: flex-start;
|
|
230
|
+
gap: var(--dialog-confirm-space-gap);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.ui-dialog-confirm-icon {
|
|
234
|
+
flex: none;
|
|
235
|
+
inline-size: var(--dialog-confirm-icon-size);
|
|
236
|
+
block-size: var(--dialog-confirm-icon-size);
|
|
237
|
+
color: hsl(var(--text-error));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.ui-dialog-confirm-text {
|
|
241
|
+
display: flex;
|
|
242
|
+
min-inline-size: 0;
|
|
243
|
+
flex-direction: column;
|
|
244
|
+
gap: var(--dialog-title-block-space-gap);
|
|
245
|
+
}
|
|
225
246
|
}
|
|
226
247
|
|
|
227
248
|
@media (prefers-reduced-motion: reduce) {
|
package/dist/styles/layout.css
CHANGED
|
@@ -716,6 +716,11 @@
|
|
|
716
716
|
width: 100%;
|
|
717
717
|
}
|
|
718
718
|
|
|
719
|
+
.ui-page-header-extra > .ui-flex[data-direction="row"] {
|
|
720
|
+
flex-wrap: wrap;
|
|
721
|
+
max-inline-size: 100%;
|
|
722
|
+
}
|
|
723
|
+
|
|
719
724
|
.ui-page-header-title-row {
|
|
720
725
|
display: flex;
|
|
721
726
|
flex-wrap: wrap;
|
|
@@ -762,11 +767,6 @@
|
|
|
762
767
|
.ui-page-header .ui-page-subtitle {
|
|
763
768
|
font-size: var(--page-subtitle-font-size-compact);
|
|
764
769
|
}
|
|
765
|
-
|
|
766
|
-
.ui-page-header-extra > .ui-flex[data-direction="row"] {
|
|
767
|
-
flex-wrap: wrap;
|
|
768
|
-
max-width: 100%;
|
|
769
|
-
}
|
|
770
770
|
}
|
|
771
771
|
|
|
772
772
|
@media (max-width: 639.98px) {
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
--dialog-title-block-space-gap: var(--space-1);
|
|
37
37
|
--dialog-extra-space-gap: var(--space-2);
|
|
38
38
|
|
|
39
|
+
--dialog-confirm-icon-size: var(--icon-size-xl);
|
|
40
|
+
--dialog-confirm-space-gap: var(--space-3);
|
|
41
|
+
|
|
39
42
|
--dialog-header-close-space-inline-end: var(--space-8);
|
|
40
43
|
|
|
41
44
|
--dialog-close-rest-alpha: 0.7;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
> Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
|
|
4
4
|
> Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
|
|
5
5
|
|
|
6
|
-
- Public components: **
|
|
7
|
-
- With ≥1 frame: **
|
|
6
|
+
- Public components: **163**
|
|
7
|
+
- With ≥1 frame: **142** · zero-frame: **21**
|
|
8
8
|
- Fully covered (every contract axis declared): **0**
|
|
9
9
|
|
|
10
10
|
## UNTESTED — no `/frame/**` route
|
|
@@ -16,7 +16,11 @@
|
|
|
16
16
|
- [ ] `CardBar`
|
|
17
17
|
- [ ] `VisuallyHidden`
|
|
18
18
|
- [ ] `RangeTimeline`
|
|
19
|
+
- [ ] `Title`
|
|
20
|
+
- [ ] `Paragraph`
|
|
21
|
+
- [ ] `Link`
|
|
19
22
|
- [ ] `ServiceCatalogCta`
|
|
23
|
+
- [ ] `SkeletonRows`
|
|
20
24
|
- [ ] `OverlayPortalProvider`
|
|
21
25
|
- [ ] `SkeletonArticle`
|
|
22
26
|
- [ ] `SkeletonAvatar`
|
|
@@ -54,14 +58,19 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
54
58
|
| TopbarItem | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
55
59
|
| NavList | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
56
60
|
| MasterDetail | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
61
|
+
| DraggablePanel | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
57
62
|
| SplitPane | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
58
63
|
| ErrorSurface | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
59
64
|
| LegalDocumentShell | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
60
65
|
| Breadcrumb | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
61
|
-
| FloatButton | ✓ |
|
|
62
|
-
| Button | ✓ | ✓ | · |
|
|
63
|
-
| Text | ✓ | ✓ |
|
|
66
|
+
| FloatButton | ✓ | ✓ | · | N/A | · | · | · | · | · | · |
|
|
67
|
+
| Button | ✓ | ✓ | · | N/A | · | · | · | · | · | · |
|
|
68
|
+
| Text | ✓ | ✓ | · | N/A | · | · | · | · | · | · |
|
|
64
69
|
| Heading | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
70
|
+
| Typography | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
71
|
+
| Title | **✗** | · | · | N/A | · | · | · | · | · | · |
|
|
72
|
+
| Paragraph | **✗** | · | · | N/A | · | · | · | · | · | · |
|
|
73
|
+
| Link | **✗** | · | · | N/A | · | · | · | · | · | · |
|
|
65
74
|
| Logo | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
66
75
|
| Reveal | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
67
76
|
| Activity | ✓ | · | N/A | N/A | · | · | · | · | · | · |
|
|
@@ -102,13 +111,14 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
102
111
|
| Checkbox | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
103
112
|
| RadioGroup | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
104
113
|
| DatePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
105
|
-
| Dialog | ✓ |
|
|
114
|
+
| Dialog | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
|
|
106
115
|
| AlertDialog | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
107
116
|
| AlertDialogRoot | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
108
117
|
| Sheet | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
109
118
|
| Alert | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
110
119
|
| Banner | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
111
120
|
| SkeletonTable | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
121
|
+
| SkeletonRows | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
112
122
|
| Toaster | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
113
123
|
| Tabs | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
|
|
114
124
|
| Pagination | ✓ | · | · | · | · | · | · | · | · | · |
|
|
@@ -153,7 +163,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
153
163
|
| HoverCard | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
154
164
|
| PasswordInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
155
165
|
| PasswordStrength | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
156
|
-
| InputOTP | ✓ | · |
|
|
166
|
+
| InputOTP | ✓ | · | N/A | · | · | · | · | · | · | · |
|
|
157
167
|
| Rating | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
158
168
|
| Segmented | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
159
169
|
| TagInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
@@ -185,4 +195,9 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
185
195
|
| ChatBubbleList | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
186
196
|
| ChatComposer | ✓ | · | · | · | · | · | · | · | · | · |
|
|
187
197
|
| ChatSuggestion | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
198
|
+
| Conversations | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
199
|
+
| Welcome | ✓ | · | N/A | N/A | · | · | · | · | · | · |
|
|
200
|
+
| Actions | ✓ | · | N/A | N/A | · | · | · | · | · | · |
|
|
201
|
+
| ThoughtChain | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
202
|
+
| Attachments | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
188
203
|
|
|
@@ -11,6 +11,7 @@ import { Button } from "@godxjp/ui/general";
|
|
|
11
11
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
12
12
|
import {
|
|
13
13
|
Dialog,
|
|
14
|
+
DialogBody,
|
|
14
15
|
DialogContent,
|
|
15
16
|
DialogDescription,
|
|
16
17
|
DialogHeader,
|
|
@@ -51,12 +52,14 @@ export default function Demo() {
|
|
|
51
52
|
このキーはこの画面でのみ表示されます。安全な場所に保管してください。
|
|
52
53
|
</DialogDescription>
|
|
53
54
|
</DialogHeader>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
<DialogBody>
|
|
56
|
+
<CredentialReveal
|
|
57
|
+
label="APIキー"
|
|
58
|
+
secret={API_KEY}
|
|
59
|
+
downloadable
|
|
60
|
+
onAcknowledge={() => setOpen(false)}
|
|
61
|
+
/>
|
|
62
|
+
</DialogBody>
|
|
60
63
|
</DialogContent>
|
|
61
64
|
</Dialog>
|
|
62
65
|
</CardContent>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
DialogHeader,
|
|
9
9
|
DialogTitle,
|
|
10
10
|
Sheet,
|
|
11
|
+
SheetBody,
|
|
11
12
|
SheetContent,
|
|
12
13
|
SheetHeader,
|
|
13
14
|
SheetTitle,
|
|
@@ -312,14 +313,16 @@ export default function Demo() {
|
|
|
312
313
|
<SheetHeader>
|
|
313
314
|
<SheetTitle>請求先設定</SheetTitle>
|
|
314
315
|
</SheetHeader>
|
|
315
|
-
<
|
|
316
|
-
<
|
|
317
|
-
<
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
<
|
|
321
|
-
|
|
322
|
-
|
|
316
|
+
<SheetBody>
|
|
317
|
+
<Form>
|
|
318
|
+
<FormField id="sheet-name" label="宛名" required>
|
|
319
|
+
<Input id="sheet-name" />
|
|
320
|
+
</FormField>
|
|
321
|
+
<FormField id="sheet-note" label="備考">
|
|
322
|
+
<Input id="sheet-note" />
|
|
323
|
+
</FormField>
|
|
324
|
+
</Form>
|
|
325
|
+
</SheetBody>
|
|
323
326
|
</SheetContent>
|
|
324
327
|
</Sheet>
|
|
325
328
|
</CardContent>
|
|
@@ -7,7 +7,8 @@ import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
|
7
7
|
/**
|
|
8
8
|
* DangerConfirm recipe (godxui#193) — high-stakes deletion built on the AlertDialog preset.
|
|
9
9
|
* No new component: `challenge` gates the confirm button behind an exact typed slug, and `stepUp`
|
|
10
|
-
* runs an async passkey / 2FA re-auth before onConfirm fires. Both force the destructive
|
|
10
|
+
* runs an async passkey / 2FA re-auth before onConfirm fires. Both force the destructive shape
|
|
11
|
+
* (button + leading status glyph, untinted surface — antd `Modal.confirm` parity).
|
|
11
12
|
* Mirrors DXS SCR-203 (org delete by slug) and SCR-209 (refund with step-up).
|
|
12
13
|
*/
|
|
13
14
|
export default function Demo() {
|
|
@@ -25,8 +26,10 @@ export default function Demo() {
|
|
|
25
26
|
<CardTitle level={2}>Organization delete · typed slug challenge</CardTitle>
|
|
26
27
|
<CardDescription>
|
|
27
28
|
The confirm button stays disabled until the operator types the exact organization slug
|
|
28
|
-
(<code>acme-inc</code>). A typed challenge always forces the destructive
|
|
29
|
-
|
|
29
|
+
(<code>acme-inc</code>). A typed challenge always forces the destructive shape: the
|
|
30
|
+
destructive confirm button and antd’s leading status glyph. The header surface
|
|
31
|
+
stays untinted — the glyph is the signal, exactly as <code>Modal.confirm</code> does
|
|
32
|
+
it.
|
|
30
33
|
</CardDescription>
|
|
31
34
|
</CardHeader>
|
|
32
35
|
<CardContent>
|
package/docs/feedback/dialog.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
Dialog,
|
|
4
4
|
DialogAction,
|
|
5
5
|
DialogCancel,
|
|
6
|
+
DialogBody,
|
|
6
7
|
DialogContent,
|
|
7
8
|
DialogDescription,
|
|
8
9
|
DialogFooter,
|
|
@@ -72,6 +73,7 @@ export default function Demo() {
|
|
|
72
73
|
借方・貸方科目と金額を入力して保存してください。
|
|
73
74
|
</DialogDescription>
|
|
74
75
|
</DialogHeader>
|
|
76
|
+
<DialogBody>
|
|
75
77
|
<Flex direction="col" gap="md">
|
|
76
78
|
<Flex direction="row" gap="sm" align="center">
|
|
77
79
|
<Text tone="muted" className="w-20 shrink-0">
|
|
@@ -92,6 +94,7 @@ export default function Demo() {
|
|
|
92
94
|
<Text>¥ 120,000</Text>
|
|
93
95
|
</Flex>
|
|
94
96
|
</Flex>
|
|
97
|
+
</DialogBody>
|
|
95
98
|
<DialogFooter>
|
|
96
99
|
<Button variant="outline" onClick={() => setCreateOpen(false)}>
|
|
97
100
|
キャンセル
|
|
@@ -123,6 +126,7 @@ export default function Demo() {
|
|
|
123
126
|
<DialogTitle>取引 #JE-2024-0042</DialogTitle>
|
|
124
127
|
<DialogDescription>2024年3月31日 · 月次決算仕訳</DialogDescription>
|
|
125
128
|
</DialogHeader>
|
|
129
|
+
<DialogBody>
|
|
126
130
|
<Flex direction="col" gap="sm">
|
|
127
131
|
<Flex direction="row" justify="between">
|
|
128
132
|
<Text tone="muted">借方</Text>
|
|
@@ -137,6 +141,7 @@ export default function Demo() {
|
|
|
137
141
|
<Text>3月売上計上</Text>
|
|
138
142
|
</Flex>
|
|
139
143
|
</Flex>
|
|
144
|
+
</DialogBody>
|
|
140
145
|
</DialogContent>
|
|
141
146
|
</Dialog>
|
|
142
147
|
</CardContent>
|
|
@@ -178,10 +183,12 @@ export default function Demo() {
|
|
|
178
183
|
subtitle={`見出し帯は tone="${headerTone}" で描画しています。`}
|
|
179
184
|
extra={<Badge tone={headerTone}>{headerTone}</Badge>}
|
|
180
185
|
/>
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
<DialogBody>
|
|
187
|
+
<Text size="sm">
|
|
188
|
+
帯の色だけが tone に追従し、見出しと本文の文字色は変わりません。extra
|
|
189
|
+
スロットは帯の右端に固定され、閉じるボタンとは重なりません。
|
|
190
|
+
</Text>
|
|
191
|
+
</DialogBody>
|
|
185
192
|
<DialogFooter>
|
|
186
193
|
<Button variant="outline" onClick={() => setToneOpen(false)}>
|
|
187
194
|
閉じる
|
|
@@ -219,14 +226,16 @@ export default function Demo() {
|
|
|
219
226
|
title="提携を終了しますか?"
|
|
220
227
|
subtitle="この操作は元に戻せません。終了理由は監査ログに残ります。"
|
|
221
228
|
/>
|
|
222
|
-
<
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
<DialogBody>
|
|
230
|
+
<FormField id="termination-reason" label="終了理由" required>
|
|
231
|
+
<Textarea
|
|
232
|
+
id="termination-reason"
|
|
233
|
+
value={reason}
|
|
234
|
+
onChange={(event) => setReason(event.target.value)}
|
|
235
|
+
placeholder="契約違反の内容を記載してください"
|
|
236
|
+
/>
|
|
237
|
+
</FormField>
|
|
238
|
+
</DialogBody>
|
|
230
239
|
<DialogFooter>
|
|
231
240
|
<DialogCancel asChild>
|
|
232
241
|
<Button variant="ghost">キャンセル</Button>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui",
|
|
3
|
-
"version": "23.4.
|
|
4
|
-
"godxUiMcp": "23.4.
|
|
3
|
+
"version": "23.4.10",
|
|
4
|
+
"godxUiMcp": "23.4.10",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -321,6 +321,7 @@
|
|
|
321
321
|
"test:coverage": "vitest run --coverage",
|
|
322
322
|
"check:example-imports": "node scripts/check-example-imports.mjs",
|
|
323
323
|
"check:frame-coverage": "node scripts/frame-coverage.mjs",
|
|
324
|
+
"check:frame-coverage-report": "node scripts/frame-coverage.mjs --check",
|
|
324
325
|
"gen:frame-coverage-ledger": "node scripts/gen-frame-coverage-ledger.mjs",
|
|
325
326
|
"check:frame-coverage-ledger": "node scripts/gen-frame-coverage-ledger.mjs --check && node scripts/check-frame-coverage-ledger.mjs",
|
|
326
327
|
"check:screen-reader-evidence": "node scripts/check-screen-reader-evidence.mjs",
|
|
@@ -336,7 +337,7 @@
|
|
|
336
337
|
"verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage && pnpm test",
|
|
337
338
|
"verify:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:token-scale-bypass && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm test",
|
|
338
339
|
"verify:ci": "pnpm verify:ci:static && pnpm check:frame-contracts && pnpm test",
|
|
339
|
-
"verify:ci:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:registry && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:no-antd-runtime && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:measurement-contract && pnpm check:mcp-token-sync && pnpm check:email-token-sync && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-catalog-completeness && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:gate-coverage && pnpm check:mcp-prop-sync && pnpm check:catalog-contradictions && pnpm check:catalog-snippets",
|
|
340
|
+
"verify:ci:static": "pnpm build && pnpm check:packed-public-contract && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:registry && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:no-external-assets && pnpm check:token-scale-bypass && pnpm check:no-antd-runtime && pnpm check:radix-surface && pnpm check:dist-tokens-resolve && pnpm check:no-hardcoded-geometry && pnpm check:no-hardcoded-css-values && pnpm check:disclosure-duplication && pnpm check:no-inline-magic-numbers && pnpm check:no-tailwind-class-assertions && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-lockstep && pnpm check:measurement-contract && pnpm check:mcp-token-sync && pnpm check:email-token-sync && pnpm check:mcp-sync && pnpm check:doc-prop-existence && pnpm check:mcp-catalog-coverage && pnpm check:mcp-orphans && pnpm check:mcp-catalog-completeness && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage-report && pnpm check:gate-coverage && pnpm check:mcp-prop-sync && pnpm check:catalog-contradictions && pnpm check:catalog-snippets",
|
|
340
341
|
"verify:browser": "pnpm check:contrast && pnpm check:text-ink-clip && pnpm check:font-fallback-metrics && pnpm check:visual-audit",
|
|
341
342
|
"verify:release": "pnpm verify:static && pnpm check:frame-contracts && pnpm check:frame-coverage",
|
|
342
343
|
"verify:publish-tree": "pnpm build && pnpm check:packed-public-contract && pnpm check:use-client && pnpm check:dist-tokens-resolve",
|
package/scripts/ui-audit.mjs
CHANGED
|
@@ -923,6 +923,30 @@ const CARD_TABLE_FLUSH = new RegExp(
|
|
|
923
923
|
"g",
|
|
924
924
|
);
|
|
925
925
|
|
|
926
|
+
/*
|
|
927
|
+
* A `DialogContent` / `SheetContent` that has chrome (a header or a footer) but no BODY.
|
|
928
|
+
*
|
|
929
|
+
* The scroll lives on the body, and only on the body:
|
|
930
|
+
*
|
|
931
|
+
* [data-slot="dialog-body"] { min-height: 0; max-height: min(60vh, 32rem); overflow-y: auto }
|
|
932
|
+
* [data-slot="dialog-content"] { overflow: hidden } // no max-height, deliberately
|
|
933
|
+
*
|
|
934
|
+
* So an overlay without one cannot scroll at all: content longer than the viewport is clipped at
|
|
935
|
+
* BOTH ends, taking the footer's buttons with it, and the only way out is Escape. Reported from a
|
|
936
|
+
* consumer whose five dialogs all had the defect and none of whose reviewers could see it — it only
|
|
937
|
+
* appears on real customer data, never on demo data (gh#617).
|
|
938
|
+
*
|
|
939
|
+
* THE SAME SHAPE AS gh#611, and the reporter named it as such: a layout contract that EXISTS in the
|
|
940
|
+
* stylesheet with no gate to enforce it. `Card`→`CardContent` had `card-needs-content`;
|
|
941
|
+
* `Dialog`→`DialogBody` had nothing. `Sheet` is included for the reason gh#611 taught: when a rule
|
|
942
|
+
* names a component, ask whether its required sibling slots are checked too — Sheet's CSS carries
|
|
943
|
+
* the identical `:has([data-slot="sheet-body"])` pair, so it carries the identical defect.
|
|
944
|
+
*
|
|
945
|
+
* Chrome is the trigger, not length: a bare `<DialogContent>` holding one line needs nothing, but
|
|
946
|
+
* once a header or footer is present the middle is a body and the middle is what scrolls.
|
|
947
|
+
*/
|
|
948
|
+
const OVERLAY_NEEDS_BODY = /<(Dialog|AlertDialog|Sheet)Content\b[^>]*>([\s\S]*?)<\/\1Content>/g;
|
|
949
|
+
|
|
926
950
|
const CARD_FLUSH = new RegExp(
|
|
927
951
|
`<Card(?!${ATTRS}\\bp-0\\b)(?:\\s${ATTRS})?>\\s*<(?!CardContent|CardHeader|CardCover|CardFooter|CardBar|\\/Card)`,
|
|
928
952
|
"g",
|
|
@@ -1179,6 +1203,29 @@ for (const dir of SCAN_DIRS) {
|
|
|
1179
1203
|
snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
|
|
1180
1204
|
});
|
|
1181
1205
|
}
|
|
1206
|
+
for (const match of scanContent.matchAll(OVERLAY_NEEDS_BODY)) {
|
|
1207
|
+
const [, family, inner] = match;
|
|
1208
|
+
const slot = family === "Sheet" ? "Sheet" : "Dialog";
|
|
1209
|
+
const hasChrome = new RegExp(`<${slot}(?:Header|Footer)\\b`).test(inner);
|
|
1210
|
+
const hasBody = new RegExp(`<${slot}Body\\b`).test(inner);
|
|
1211
|
+
if (!hasChrome || hasBody) continue;
|
|
1212
|
+
const lineNo = scanContent.slice(0, match.index).split("\n").length;
|
|
1213
|
+
if (suppressed("dialog-needs-body", lineNo - 1)) continue;
|
|
1214
|
+
findings.push({
|
|
1215
|
+
file: rel,
|
|
1216
|
+
line: lineNo,
|
|
1217
|
+
rule: "dialog-needs-body",
|
|
1218
|
+
severity: "error",
|
|
1219
|
+
message:
|
|
1220
|
+
`<${family}Content> has a ${slot}Header/${slot}Footer but no <${slot}Body>. The scroll lives on ` +
|
|
1221
|
+
`the body — the content box is \`overflow: hidden\` with no max-height — so content taller ` +
|
|
1222
|
+
`than the viewport is clipped at BOTH ends and the footer's buttons go with it, leaving ` +
|
|
1223
|
+
`Escape as the only way out. It only shows on real data, never on demo data.`,
|
|
1224
|
+
replacement: `${slot}Body`,
|
|
1225
|
+
snippet: match[0].replace(/\s+/g, " ").slice(0, 120),
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1182
1229
|
if (
|
|
1183
1230
|
!(SELF && !args.includes("--consumer")) &&
|
|
1184
1231
|
!/<(?:Flex|ResponsiveGrid|PageContainer)\b/.test(scanContent)
|