@godxjp/ui 23.4.4 → 23.4.6
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-display/data-table.js +2 -1
- package/dist/components/data-entry/form-field.js +2 -1
- package/dist/components/general/float-button.js +3 -2
- package/dist/components/general/typography.js +2 -1
- package/dist/components/layout/app-shell.js +2 -1
- package/dist/components/layout/error-surface.js +2 -1
- package/dist/components/navigation/tabs.js +22 -6
- package/dist/components/ui/password-input.js +2 -1
- package/dist/components/ui/separator.js +2 -1
- package/dist/email/tokens.generated.d.ts +2 -2
- package/dist/email/tokens.generated.js +2 -2
- package/dist/lib/dev.d.ts +45 -0
- package/dist/lib/dev.js +7 -0
- package/dist/styles/alert-layout.css +4 -4
- package/dist/styles/chart-layout.css +4 -0
- package/dist/styles/control.css +2 -1
- package/dist/styles/data-entry-layout.css +5 -5
- package/dist/styles/form-layout.css +1 -1
- package/dist/styles/layout.css +1 -1
- package/dist/styles/motion.css +4 -4
- package/dist/styles/navigation-layout.css +48 -6
- package/dist/styles/table-layout.css +13 -0
- package/dist/tokens/foundation.css +4 -2
- package/docs/data-entry/password-strength.tsx +3 -1
- package/docs/navigation/dropdown-menu.tsx +32 -0
- package/docs/navigation/filter-bar.tsx +20 -0
- package/docs/navigation/steps.tsx +14 -1
- package/docs/navigation/tabs.tsx +22 -2
- package/package.json +2 -2
|
@@ -73,6 +73,7 @@ import {
|
|
|
73
73
|
tableCellPaddingClass,
|
|
74
74
|
tableRowHeightClass
|
|
75
75
|
} from "../../lib/control-styles.js";
|
|
76
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
76
77
|
function resolveColumnHideBelow(col) {
|
|
77
78
|
if (col.hideBelow) return col.hideBelow;
|
|
78
79
|
if (col.hiddenOnMobile) return "md";
|
|
@@ -796,7 +797,7 @@ DataTable.Content = function DataTableContent() {
|
|
|
796
797
|
const fixedTableLayout = scroll?.x !== void 0 || visibleColumns.some((col) => col.ellipsis || columnWidth(col.width).style !== void 0);
|
|
797
798
|
const missingHeaderNames = visibleColumns.filter((col) => isEmptyHeader(col.header) && !col.ariaLabel).map((col) => col.key).join("|");
|
|
798
799
|
React.useEffect(() => {
|
|
799
|
-
if (
|
|
800
|
+
if (!isDevelopment() || !missingHeaderNames) return;
|
|
800
801
|
for (const key of missingHeaderNames.split("|")) {
|
|
801
802
|
console.warn(
|
|
802
803
|
`[DataTable] Column "${key}" renders a <th> with no visible or accessible text. Give it a visible \`header\`, or set \`ariaLabel\` (e.g. "Actions"/"Select") so screen readers can announce the column and axe reports no empty-table-header violation.`
|
|
@@ -10,6 +10,7 @@ import { FieldIdentityContext, FieldNameContext, mergeAriaIds } from "../../lib/
|
|
|
10
10
|
import { useOptionalAppContext } from "../../app/app-provider.js";
|
|
11
11
|
import { useFormLayout } from "./form.js";
|
|
12
12
|
import { firstBagMessage, useClaimErrorKey, useFormErrorsRegistry } from "./form-errors.js";
|
|
13
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
13
14
|
const toCssLength = (v) => typeof v === "number" ? `${v}px` : v;
|
|
14
15
|
const FOCUSABLE_SELECTOR = 'input:not([type="hidden"]), select, textarea, button, [tabindex]:not([tabindex="-1"])';
|
|
15
16
|
function FormField({
|
|
@@ -55,7 +56,7 @@ function FormField({
|
|
|
55
56
|
const fieldKey = field ?? name ?? id;
|
|
56
57
|
const emitFieldNames = useOptionalAppContext()?.emitFieldNames ?? false;
|
|
57
58
|
const isStatic = staticText !== void 0;
|
|
58
|
-
if (!isStatic &&
|
|
59
|
+
if (!isStatic && isDevelopment() && !React.isValidElement(children)) {
|
|
59
60
|
console.warn(
|
|
60
61
|
"FormField expects a single React element child to receive aria-describedby/aria-errormessage; the helper text and error message will not be associated with the control. Pass plain text via `staticText` instead of `children` for a read-only value row."
|
|
61
62
|
);
|
|
@@ -8,6 +8,7 @@ import { numberFormat } from "../../lib/intl-cache.js";
|
|
|
8
8
|
import { cn } from "../../lib/utils.js";
|
|
9
9
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../feedback/tooltip.js";
|
|
10
10
|
import { Button } from "./button.js";
|
|
11
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
11
12
|
const GroupContext = React.createContext(null);
|
|
12
13
|
const DEFAULT_ICON = /* @__PURE__ */ jsx(FileText, { "aria-hidden": "true" });
|
|
13
14
|
const BUTTON_VARIANT = {
|
|
@@ -67,7 +68,7 @@ const FloatButtonRoot = React.forwardRef(function FloatButton({
|
|
|
67
68
|
const mergedContent = isRenderable(content) ? content : description;
|
|
68
69
|
const hasContent = isRenderable(mergedContent);
|
|
69
70
|
const mergedIcon = !hasContent && icon == null ? DEFAULT_ICON : icon;
|
|
70
|
-
if (
|
|
71
|
+
if (isDevelopment() && mergedShape === "circle" && hasContent) {
|
|
71
72
|
console.warn(
|
|
72
73
|
"[@godxjp/ui] FloatButton: `content` is supported only when `shape` is `square` \u2014 a circle has no room for a line of text."
|
|
73
74
|
);
|
|
@@ -153,7 +154,7 @@ function FloatButtonGroup({
|
|
|
153
154
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
154
155
|
const isControlled = open != null;
|
|
155
156
|
const isOpen = isControlled ? open : uncontrolledOpen;
|
|
156
|
-
if (
|
|
157
|
+
if (isDevelopment() && isControlled && !trigger) {
|
|
157
158
|
console.warn(
|
|
158
159
|
"[@godxjp/ui] FloatButton.Group: `open` needs to be used together with `trigger`."
|
|
159
160
|
);
|
|
@@ -6,6 +6,7 @@ import { cn } from "../../lib/utils.js";
|
|
|
6
6
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
7
7
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../feedback/tooltip.js";
|
|
8
8
|
import { Textarea } from "../data-entry/textarea.js";
|
|
9
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
9
10
|
const COPIED_RESET_MS = 3e3;
|
|
10
11
|
const DECORATION_ELEMENTS = [
|
|
11
12
|
["strong", "strong"],
|
|
@@ -409,7 +410,7 @@ const TextBase = React.forwardRef((props, ref) => {
|
|
|
409
410
|
const ellipsisRowCount = ellipsis ? ellipsisRows(ellipsis) : 0;
|
|
410
411
|
const effectiveClamp = ellipsisRowCount > 1 ? ellipsisRowCount : ellipsis ? void 0 : clampLines;
|
|
411
412
|
const truncating = (truncate === true || ellipsisRowCount === 1) && effectiveClamp === void 0;
|
|
412
|
-
if (
|
|
413
|
+
if (isDevelopment()) {
|
|
413
414
|
if (clamp !== void 0 && clampLines === void 0) {
|
|
414
415
|
console.warn(`Text: \`clamp\` must be a finite number \u2265 1 (got ${String(clamp)}); ignored.`);
|
|
415
416
|
}
|
|
@@ -7,6 +7,7 @@ import { useTranslation } from "../../i18n/use-translation.js";
|
|
|
7
7
|
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../feedback/sheet.js";
|
|
8
8
|
import { NavSurfaceProvider } from "./nav-surface.js";
|
|
9
9
|
import { TopbarItem } from "./topbar-item.js";
|
|
10
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
10
11
|
function useAppShellNavigationMode() {
|
|
11
12
|
return useMediaQuery("(width <= 56.25rem)") ? "drawer" : "docked";
|
|
12
13
|
}
|
|
@@ -59,7 +60,7 @@ function AppShell({
|
|
|
59
60
|
}
|
|
60
61
|
};
|
|
61
62
|
const hasTopbarContent = topbar !== void 0 || topbarLeft !== void 0 || topbarRight !== void 0 || logo !== void 0;
|
|
62
|
-
if (
|
|
63
|
+
if (isDevelopment() && topbar !== void 0) {
|
|
63
64
|
const ignored = [
|
|
64
65
|
topbarLeft !== void 0 && "topbarLeft",
|
|
65
66
|
topbarRight !== void 0 && "topbarRight"
|
|
@@ -9,6 +9,7 @@ import { Progress } from "../data-display/progress.js";
|
|
|
9
9
|
import { Text } from "../general/typography.js";
|
|
10
10
|
import { CenteredShell } from "./centered-shell.js";
|
|
11
11
|
import { dateTimeFormat, numberFormat } from "../../lib/intl-cache.js";
|
|
12
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
12
13
|
const STATUS_META = {
|
|
13
14
|
400: { icon: TriangleAlert, tone: "warning" },
|
|
14
15
|
403: { icon: ShieldAlert, tone: "warning" },
|
|
@@ -23,7 +24,7 @@ function singleAction(action) {
|
|
|
23
24
|
const flattened = React.Children.toArray(action).flatMap(
|
|
24
25
|
(child) => React.isValidElement(child) && child.type === React.Fragment ? React.Children.toArray(child.props.children) : [child]
|
|
25
26
|
);
|
|
26
|
-
if (flattened.length > 1 &&
|
|
27
|
+
if (flattened.length > 1 && isDevelopment()) {
|
|
27
28
|
console.error(
|
|
28
29
|
"[@godxjp/ui] ErrorSurface: `action` takes EXACTLY ONE recovery action. The extra elements were dropped. Put support contact or secondary guidance in `description`."
|
|
29
30
|
);
|
|
@@ -166,6 +166,12 @@ function Tabs({
|
|
|
166
166
|
const needsBar = Boolean(extraStart || extraEnd || showAdd || collapsible);
|
|
167
167
|
const card = variant === "card" || variant === "editable-card";
|
|
168
168
|
const sizeTriggerClassName = triggerSizeClassName(size);
|
|
169
|
+
const CARD_FACE = {
|
|
170
|
+
top: "rounded-[var(--tabs-card-radius)_var(--tabs-card-radius)_0_0] data-[state=active]:border-b-background",
|
|
171
|
+
bottom: "rounded-[0_0_var(--tabs-card-radius)_var(--tabs-card-radius)] data-[state=active]:border-t-background",
|
|
172
|
+
start: "rounded-s-[var(--tabs-card-radius)] rounded-e-none data-[state=active]:border-e-background",
|
|
173
|
+
end: "rounded-e-[var(--tabs-card-radius)] rounded-s-none data-[state=active]:border-s-background"
|
|
174
|
+
};
|
|
169
175
|
const list = items ? /* @__PURE__ */ jsx(
|
|
170
176
|
TabsList,
|
|
171
177
|
{
|
|
@@ -239,11 +245,21 @@ function Tabs({
|
|
|
239
245
|
// pixel and `justify-content` has nothing left to centre — measured, the leading and
|
|
240
246
|
// trailing gaps were both 0px with `centered` on and off.
|
|
241
247
|
centered && "flex-none",
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
// rail
|
|
245
|
-
//
|
|
246
|
-
|
|
248
|
+
// The merged edge is the SURFACE colour, never `transparent`: the rail is an inset
|
|
249
|
+
// shadow at the strip's padding-box edge, so a see-through border would let that 1px
|
|
250
|
+
// of rail run straight across the tab it is joined to.
|
|
251
|
+
// CARD face: a real boundary on every side, opened toward the panel by
|
|
252
|
+
// `CARD_FACE`. `flex-none` is antd's geometry, not a preference — its
|
|
253
|
+
// `nav-list` is a plain `display: flex` with no `flex-grow` on the tab, so a
|
|
254
|
+
// card tab is CONTENT-WIDTH. The base trigger is `flex-1`, which made every
|
|
255
|
+
// card tab stretch to an equal share of the strip: measured, three tabs in a
|
|
256
|
+
// 942px strip came out 311px each for labels needing ~70px, and with
|
|
257
|
+
// `justify-center` the label and its × floated in the middle of that box —
|
|
258
|
+
// the × ended up 90px from the tab's own trailing edge (179px on a two-tab
|
|
259
|
+
// strip). antd's is 8px away. This is also why the face read as a free-standing
|
|
260
|
+
// bordered box rather than a tab.
|
|
261
|
+
card && "flex-none border-[color:hsl(var(--border))] bg-[hsl(var(--tabs-card-background,var(--muted)))]",
|
|
262
|
+
card && CARD_FACE[placement]
|
|
247
263
|
),
|
|
248
264
|
children: [
|
|
249
265
|
item.icon ? /* @__PURE__ */ jsx(
|
|
@@ -463,7 +479,7 @@ const TabsTrigger = React.forwardRef(
|
|
|
463
479
|
// The line indicator lives in src/styles/navigation-layout.css so it reads --tabs-indicator-*.
|
|
464
480
|
// Selected and focused stay visually distinct (WCAG 2.4.7): selected is a 1px hairline in the
|
|
465
481
|
// border, focused is the 2px ring plus its halo outside it.
|
|
466
|
-
"text-muted-foreground ring-offset-background hover:text-foreground ui-focus-ring data-[state=active]:bg-background data-[state=active]:text-foreground group-data-[variant=default]/tabs-list:data-[state=active]:border-primary/25 relative inline-flex flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-3 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:flex-none group-data-[orientation=vertical]/tabs:justify-start group-data-[variant=line]/tabs-list:border-e-0 group-data-[variant=line]/tabs-list:border-b-0 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none",
|
|
482
|
+
"text-muted-foreground ring-offset-background hover:text-foreground ui-focus-ring data-[state=active]:bg-background data-[state=active]:text-foreground group-data-[variant=default]/tabs:group-data-[variant=default]/tabs-list:data-[state=active]:border-primary/25 relative inline-flex flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-3 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:flex-none group-data-[orientation=vertical]/tabs:justify-start group-data-[variant=line]/tabs-list:border-e-0 group-data-[variant=line]/tabs-list:border-b-0 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs:group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none",
|
|
467
483
|
// The tier the root was given. Without this the compound form ignored `size` outright.
|
|
468
484
|
triggerSizeClassName(size),
|
|
469
485
|
className
|
|
@@ -5,6 +5,7 @@ import { Eye, EyeOff } from "lucide-react";
|
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
7
|
import { Input } from "../data-entry/input.js";
|
|
8
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
8
9
|
const PasswordInput = React.forwardRef(
|
|
9
10
|
({ className, visibilityToggle = true, iconRender, suffix, trailingIcon, allowClear, ...props }, ref) => {
|
|
10
11
|
const { t } = useTranslation();
|
|
@@ -19,7 +20,7 @@ const PasswordInput = React.forwardRef(
|
|
|
19
20
|
onVisibleChange?.(next);
|
|
20
21
|
};
|
|
21
22
|
const toggleOwnsTrailing = showToggle;
|
|
22
|
-
if (toggleOwnsTrailing &&
|
|
23
|
+
if (toggleOwnsTrailing && isDevelopment() && (suffix != null || trailingIcon != null || allowClear)) {
|
|
23
24
|
console.warn(
|
|
24
25
|
"[godxjp-ui] PasswordInput: the reveal toggle owns the trailing slot, so `suffix` / `trailingIcon` / `allowClear` are ignored. Use `leadingIcon` (or `prefix`), or pass `visibilityToggle={false}` to hand the slot back."
|
|
25
26
|
);
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { Separator as AriaSeparator } from "react-aria-components";
|
|
4
4
|
import { padStepToken } from "../../lib/variants.js";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
+
import { isDevelopment } from "../../lib/dev.js";
|
|
6
7
|
const Separator = React.forwardRef(
|
|
7
8
|
({
|
|
8
9
|
className,
|
|
@@ -20,7 +21,7 @@ const Separator = React.forwardRef(
|
|
|
20
21
|
}, ref) => {
|
|
21
22
|
const hasLabel = typeof label === "string" && label.trim() !== "";
|
|
22
23
|
const labelled = hasLabel && orientation === "horizontal";
|
|
23
|
-
if (
|
|
24
|
+
if (isDevelopment()) {
|
|
24
25
|
if (hasLabel && orientation === "vertical") {
|
|
25
26
|
console.warn(
|
|
26
27
|
'Separator: `label` is rendered on orientation="horizontal" only; it was ignored. Use a horizontal rule for a day divider / unread watermark.'
|
|
@@ -33,7 +33,7 @@ export declare const EMAIL_COLOR_SOURCE: {
|
|
|
33
33
|
};
|
|
34
34
|
readonly border: {
|
|
35
35
|
readonly cssVar: "--border";
|
|
36
|
-
readonly hsl: "30 7%
|
|
36
|
+
readonly hsl: "30 7% 93%";
|
|
37
37
|
};
|
|
38
38
|
readonly primary: {
|
|
39
39
|
readonly cssVar: "--primary";
|
|
@@ -92,7 +92,7 @@ export declare const EMAIL_COLOR_SOURCE_DARK: {
|
|
|
92
92
|
};
|
|
93
93
|
readonly border: {
|
|
94
94
|
readonly cssVar: "--border";
|
|
95
|
-
readonly hsl: "45 6%
|
|
95
|
+
readonly hsl: "45 6% 19%";
|
|
96
96
|
};
|
|
97
97
|
readonly primary: {
|
|
98
98
|
readonly cssVar: "--primary";
|
|
@@ -25,7 +25,7 @@ const EMAIL_COLOR_SOURCE = {
|
|
|
25
25
|
},
|
|
26
26
|
"border": {
|
|
27
27
|
"cssVar": "--border",
|
|
28
|
-
"hsl": "30 7%
|
|
28
|
+
"hsl": "30 7% 93%"
|
|
29
29
|
},
|
|
30
30
|
"primary": {
|
|
31
31
|
"cssVar": "--primary",
|
|
@@ -83,7 +83,7 @@ const EMAIL_COLOR_SOURCE_DARK = {
|
|
|
83
83
|
},
|
|
84
84
|
"border": {
|
|
85
85
|
"cssVar": "--border",
|
|
86
|
-
"hsl": "45 6%
|
|
86
|
+
"hsl": "45 6% 19%"
|
|
87
87
|
},
|
|
88
88
|
"primary": {
|
|
89
89
|
"cssVar": "--primary",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `isDevelopment()` — the ONE place this package is allowed to ask whether it is in a dev build.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS, and it is a crash rather than a tidiness question. Eight shipped modules read
|
|
5
|
+
* `process.env.NODE_ENV` to decide whether to emit a development warning. Three of them guarded
|
|
6
|
+
* the read with `typeof process !== "undefined"`; five did not, and `process` is not a browser
|
|
7
|
+
* global. Nothing in the build replaces it either — `tsup.config.ts` declares no `define`, and
|
|
8
|
+
* `grep -o 'process\.env' dist` on the published tarball of 23.4.4 returns 9 hits, raw.
|
|
9
|
+
*
|
|
10
|
+
* Measured, not theorised: this repo's own preview app (Vite 8, dev mode) renders
|
|
11
|
+
* `/isolate/data-entry-password-strength` as **"Preview render failed — process is not defined"**,
|
|
12
|
+
* six DOM nodes and nothing else. `PasswordInput` is one of the five unguarded modules and that
|
|
13
|
+
* page renders it. A bundler that happens to define `process.env.NODE_ENV` papers over it, which
|
|
14
|
+
* is exactly why it survived: most consumers' toolchains do, and the ones that do not get a blank
|
|
15
|
+
* screen rather than a warning.
|
|
16
|
+
*
|
|
17
|
+
* The helper reads the guard ONCE so the two halves cannot drift apart again, and it fails SAFE:
|
|
18
|
+
* where `process` is absent we cannot tell dev from production, so the answer is `false` and the
|
|
19
|
+
* warnings go quiet. A missing warning is a cost; a component that throws while rendering is not
|
|
20
|
+
* a cost, it is a broken page.
|
|
21
|
+
*
|
|
22
|
+
* A new bare `process.env` read in `src/components/**` is a CI failure — see
|
|
23
|
+
* `src/lib/__tests__/dev-guard.test.ts`.
|
|
24
|
+
*
|
|
25
|
+
* TWO THINGS ABOUT THE SHAPE BELOW, and the first one is why the three "guarded" modules were
|
|
26
|
+
* only half guarded.
|
|
27
|
+
*
|
|
28
|
+
* NO OPTIONAL CHAINING ON `process.env`. It does not survive the build. Written as
|
|
29
|
+
* `process.env?.NODE_ENV`, the transformed output measured in this repo's own vitest pipeline is
|
|
30
|
+
*
|
|
31
|
+
* return typeof process !== "undefined" && process.env.NODE_ENV !== "production";
|
|
32
|
+
*
|
|
33
|
+
* — the `?.` is GONE, because a bundler `define` matches the text `process.env` and substitutes
|
|
34
|
+
* through it, discarding the chain. So `?.` is not a guard here; it reads like one, which is
|
|
35
|
+
* worse than nothing. `typeof process` does survive (measured in the same dump), and a truthiness
|
|
36
|
+
* test on `process.env` survives as a truthiness test whatever it is replaced with.
|
|
37
|
+
*
|
|
38
|
+
* THE LITERAL IS KEPT ON THE LAST LINE ON PURPOSE. `process.env.NODE_ENV` is the exact text every
|
|
39
|
+
* bundler's `define` looks for, and that substitution is what lets a consumer's production build
|
|
40
|
+
* fold these branches away and drop the warning strings entirely. Reading the global indirectly
|
|
41
|
+
* (`globalThis.process`) would be equally crash-proof and would cost every consumer that
|
|
42
|
+
* dead-code elimination, so the order of the checks does the work instead: the guard runs first,
|
|
43
|
+
* and the replaceable literal is only reached once `env` is known to exist.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isDevelopment(): boolean;
|
package/dist/lib/dev.js
ADDED
|
@@ -76,19 +76,19 @@
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
[data-slot="alert-icon"][data-tone="destructive"] {
|
|
79
|
-
color: hsl(var(--
|
|
79
|
+
color: hsl(var(--text-error));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
[data-slot="alert-icon"][data-tone="warning"] {
|
|
83
|
-
color: hsl(var(--warning));
|
|
83
|
+
color: hsl(var(--text-warning));
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
[data-slot="alert-icon"][data-tone="success"] {
|
|
87
|
-
color: hsl(var(--success));
|
|
87
|
+
color: hsl(var(--text-success));
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
[data-slot="alert-icon"][data-tone="info"] {
|
|
91
|
-
color: hsl(var(--info));
|
|
91
|
+
color: hsl(var(--text-info));
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
[data-slot="alert-body"] {
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
color: hsl(var(--muted-foreground));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
.ui-chart .recharts-legend-item-text {
|
|
31
|
+
color: hsl(var(--muted-foreground)) !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
.ui-chart .recharts-default-tooltip {
|
|
31
35
|
border-radius: var(--radius);
|
|
32
36
|
border: 1px solid hsl(var(--border)) !important;
|
package/dist/styles/control.css
CHANGED
|
@@ -1053,6 +1053,7 @@
|
|
|
1053
1053
|
width: 1.25rem;
|
|
1054
1054
|
height: 1.25rem;
|
|
1055
1055
|
}
|
|
1056
|
+
|
|
1056
1057
|
.ui-rating-star-filled {
|
|
1057
1058
|
color: hsl(var(--warning));
|
|
1058
1059
|
}
|
|
@@ -1572,7 +1573,7 @@
|
|
|
1572
1573
|
}
|
|
1573
1574
|
|
|
1574
1575
|
.ui-search-select-placeholder[data-tone="destructive"] {
|
|
1575
|
-
color: hsl(var(--
|
|
1576
|
+
color: hsl(var(--text-error));
|
|
1576
1577
|
}
|
|
1577
1578
|
|
|
1578
1579
|
.ui-search-select-footer {
|
|
@@ -87,15 +87,15 @@
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
.ui-password-strength-segment[data-tone="destructive"] {
|
|
90
|
-
background: hsl(var(--destructive));
|
|
90
|
+
background: hsl(var(--mark-destructive));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
.ui-password-strength-segment[data-tone="warning"] {
|
|
94
|
-
background: hsl(var(--warning));
|
|
94
|
+
background: hsl(var(--mark-warning));
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
.ui-password-strength-segment[data-tone="success"] {
|
|
98
|
-
background: hsl(var(--success));
|
|
98
|
+
background: hsl(var(--mark-success));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
.ui-password-strength-meta {
|
|
@@ -133,11 +133,11 @@
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
.ui-password-strength-checklist-item[data-state="passed"] svg {
|
|
136
|
-
color: hsl(var(--success));
|
|
136
|
+
color: hsl(var(--text-success));
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
.ui-password-strength-checklist-item[data-state="failed"] svg {
|
|
140
|
-
color: hsl(var(--
|
|
140
|
+
color: hsl(var(--text-error));
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
package/dist/styles/layout.css
CHANGED
package/dist/styles/motion.css
CHANGED
|
@@ -76,16 +76,16 @@
|
|
|
76
76
|
color: hsl(var(--primary));
|
|
77
77
|
}
|
|
78
78
|
.ui-activity[data-tone="success"] .ui-activity-mark {
|
|
79
|
-
color: hsl(var(--success));
|
|
79
|
+
color: hsl(var(--text-success));
|
|
80
80
|
}
|
|
81
81
|
.ui-activity[data-tone="warning"] .ui-activity-mark {
|
|
82
|
-
color: hsl(var(--warning));
|
|
82
|
+
color: hsl(var(--text-warning));
|
|
83
83
|
}
|
|
84
84
|
.ui-activity[data-tone="destructive"] .ui-activity-mark {
|
|
85
|
-
color: hsl(var(--
|
|
85
|
+
color: hsl(var(--text-error));
|
|
86
86
|
}
|
|
87
87
|
.ui-activity[data-tone="info"] .ui-activity-mark {
|
|
88
|
-
color: hsl(var(--info));
|
|
88
|
+
color: hsl(var(--text-info));
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
.ui-activity-dot {
|
|
@@ -60,7 +60,16 @@
|
|
|
60
60
|
|
|
61
61
|
[data-slot="tabs-list"][data-variant="line"][data-orientation="horizontal"]
|
|
62
62
|
[data-slot="tabs-trigger"]::after {
|
|
63
|
-
inset-block-end: calc(
|
|
63
|
+
inset-block-end: calc(
|
|
64
|
+
-1 *
|
|
65
|
+
(
|
|
66
|
+
var(--tabs-indicator-offset) + var(--tabs-list-line-space-inset) +
|
|
67
|
+
var(
|
|
68
|
+
--tabs-list-focus-ring-space-inset,
|
|
69
|
+
calc(var(--focus-ring-width) + var(--focus-ring-glow-width))
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
);
|
|
64
73
|
block-size: var(--tabs-indicator-size);
|
|
65
74
|
inline-size: calc(100% - 2 * var(--tabs-indicator-inset));
|
|
66
75
|
inset-inline-start: var(--tabs-indicator-inset);
|
|
@@ -204,6 +213,38 @@
|
|
|
204
213
|
box-shadow: inset 0 calc(-1 * var(--tabs-card-rail-border-width)) 0 hsl(var(--border));
|
|
205
214
|
}
|
|
206
215
|
|
|
216
|
+
[data-slot="tabs"][data-variant="card"][data-placement="bottom"] [data-slot="tabs-list"],
|
|
217
|
+
[data-slot="tabs"][data-variant="editable-card"][data-placement="bottom"]
|
|
218
|
+
[data-slot="tabs-list"] {
|
|
219
|
+
box-shadow: inset 0 var(--tabs-card-rail-border-width) 0 hsl(var(--border));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
[data-slot="tabs"][data-variant="card"][data-placement="start"] [data-slot="tabs-list"],
|
|
223
|
+
[data-slot="tabs"][data-variant="editable-card"][data-placement="start"]
|
|
224
|
+
[data-slot="tabs-list"] {
|
|
225
|
+
box-shadow: inset calc(-1 * var(--tabs-card-rail-border-width)) 0 0 hsl(var(--border));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
[data-slot="tabs"][data-variant="card"][data-placement="end"] [data-slot="tabs-list"],
|
|
229
|
+
[data-slot="tabs"][data-variant="editable-card"][data-placement="end"]
|
|
230
|
+
[data-slot="tabs-list"] {
|
|
231
|
+
box-shadow: inset var(--tabs-card-rail-border-width) 0 0 hsl(var(--border));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
[dir="rtl"] [data-slot="tabs"][data-variant="card"][data-placement="start"]
|
|
235
|
+
[data-slot="tabs-list"],
|
|
236
|
+
[dir="rtl"] [data-slot="tabs"][data-variant="editable-card"][data-placement="start"]
|
|
237
|
+
[data-slot="tabs-list"] {
|
|
238
|
+
box-shadow: inset var(--tabs-card-rail-border-width) 0 0 hsl(var(--border));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
[dir="rtl"] [data-slot="tabs"][data-variant="card"][data-placement="end"]
|
|
242
|
+
[data-slot="tabs-list"],
|
|
243
|
+
[dir="rtl"] [data-slot="tabs"][data-variant="editable-card"][data-placement="end"]
|
|
244
|
+
[data-slot="tabs-list"] {
|
|
245
|
+
box-shadow: inset calc(-1 * var(--tabs-card-rail-border-width)) 0 0 hsl(var(--border));
|
|
246
|
+
}
|
|
247
|
+
|
|
207
248
|
.ui-tabs-tab-remove {
|
|
208
249
|
display: inline-flex;
|
|
209
250
|
flex: 0 0 auto;
|
|
@@ -223,7 +264,8 @@
|
|
|
223
264
|
color: hsl(var(--accent-foreground));
|
|
224
265
|
}
|
|
225
266
|
|
|
226
|
-
.ui-tabs-tab-remove-icon
|
|
267
|
+
.ui-tabs-tab-remove-icon,
|
|
268
|
+
.ui-tabs-tab-remove > :is(svg, img) {
|
|
227
269
|
inline-size: var(--tabs-tab-remove-icon-size);
|
|
228
270
|
block-size: var(--tabs-tab-remove-icon-size);
|
|
229
271
|
}
|
|
@@ -374,7 +416,7 @@
|
|
|
374
416
|
}
|
|
375
417
|
|
|
376
418
|
.ui-dropdown-menu-item[data-variant="destructive"] {
|
|
377
|
-
color: hsl(var(--
|
|
419
|
+
color: hsl(var(--text-error));
|
|
378
420
|
}
|
|
379
421
|
|
|
380
422
|
.ui-dropdown-menu-separator,
|
|
@@ -461,7 +503,7 @@
|
|
|
461
503
|
}
|
|
462
504
|
|
|
463
505
|
.ui-steps-inline-item[data-status="error"] .ui-steps-inline-control {
|
|
464
|
-
color: hsl(var(--
|
|
506
|
+
color: hsl(var(--text-error));
|
|
465
507
|
}
|
|
466
508
|
|
|
467
509
|
.ui-steps-inline-item[data-status="wait"] .ui-steps-inline-control {
|
|
@@ -590,7 +632,7 @@
|
|
|
590
632
|
|
|
591
633
|
.ui-dropdown-menu-item[data-variant="destructive"][data-highlighted] {
|
|
592
634
|
background: hsl(var(--destructive) / 0.1);
|
|
593
|
-
color: hsl(var(--
|
|
635
|
+
color: hsl(var(--text-error));
|
|
594
636
|
}
|
|
595
637
|
|
|
596
638
|
.ui-dropdown-menu-item[data-disabled],
|
|
@@ -709,7 +751,7 @@
|
|
|
709
751
|
}
|
|
710
752
|
|
|
711
753
|
.ui-steps-title[data-status="error"] {
|
|
712
|
-
color: hsl(var(--
|
|
754
|
+
color: hsl(var(--text-error));
|
|
713
755
|
}
|
|
714
756
|
|
|
715
757
|
.ui-steps-subtitle {
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
.ui-data-table-sort-button {
|
|
124
|
+
position: relative;
|
|
124
125
|
display: inline-flex;
|
|
125
126
|
align-items: center;
|
|
126
127
|
gap: var(--space-inline-xs);
|
|
@@ -132,6 +133,18 @@
|
|
|
132
133
|
outline: none;
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
.ui-data-table-sort-button::after {
|
|
137
|
+
content: "";
|
|
138
|
+
position: absolute;
|
|
139
|
+
inset-block-start: 50%;
|
|
140
|
+
inset-inline-start: 50%;
|
|
141
|
+
translate: -50% -50%;
|
|
142
|
+
min-inline-size: var(--touch-target-min);
|
|
143
|
+
min-block-size: var(--touch-target-min);
|
|
144
|
+
inline-size: 100%;
|
|
145
|
+
block-size: 100%;
|
|
146
|
+
}
|
|
147
|
+
|
|
135
148
|
.ui-data-table-bulk {
|
|
136
149
|
display: flex;
|
|
137
150
|
align-items: center;
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
--accent-foreground: 48 8% 13%;
|
|
21
21
|
--destructive: 357 64% 44%;
|
|
22
22
|
--destructive-foreground: 60 33% 99%;
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
--border: 30 7% 93%;
|
|
24
25
|
|
|
25
26
|
--input: 30 7% 53%;
|
|
26
27
|
|
|
@@ -331,7 +332,8 @@
|
|
|
331
332
|
|
|
332
333
|
--destructive: 357 55% 48%;
|
|
333
334
|
--destructive-foreground: 0 0% 100%;
|
|
334
|
-
|
|
335
|
+
|
|
336
|
+
--border: 45 6% 19%;
|
|
335
337
|
|
|
336
338
|
--input: 45 6% 47%;
|
|
337
339
|
|
|
@@ -11,7 +11,9 @@ import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
|
11
11
|
*/
|
|
12
12
|
export default function Demo() {
|
|
13
13
|
const [signupPassword, setSignupPassword] = useState("");
|
|
14
|
-
|
|
14
|
+
/* Seeded so the PASSED state is on screen without typing: the checklist's success icon is
|
|
15
|
+
* otherwise only reachable by interaction, which means no sweep can measure it (gh#612). */
|
|
16
|
+
const [resetPassword, setResetPassword] = useState("Passw0rdSeed");
|
|
15
17
|
|
|
16
18
|
return (
|
|
17
19
|
<PageContainer
|
|
@@ -300,6 +300,38 @@ export default function Demo() {
|
|
|
300
300
|
</DropdownMenu>
|
|
301
301
|
</CardContent>
|
|
302
302
|
</Card>
|
|
303
|
+
{/* defaultOpen — OPEN ON MOUNT, on purpose, and this is the only example on the page that
|
|
304
|
+
is. A menu paints nothing until it is opened, so for as long as this page has existed
|
|
305
|
+
no sweep has ever measured a single menu pixel: that is how
|
|
306
|
+
`.ui-dropdown-menu-item[data-variant="destructive"]` — the label on a Delete row, i.e.
|
|
307
|
+
prose — kept the destructive FILL tier at 2.95:1 on dark (gh#612). Same reasoning as
|
|
308
|
+
`?toast=` in check-contrast's route list: a surface that needs an interaction is a
|
|
309
|
+
surface no gate sees. */}
|
|
310
|
+
<Card>
|
|
311
|
+
<CardHeader>
|
|
312
|
+
<CardTitle level={2}>defaultOpen · 計測できる状態で開いておく</CardTitle>
|
|
313
|
+
<CardDescription>
|
|
314
|
+
antd の open/defaultOpen。ここだけはマウント時から開いたままにしてある。
|
|
315
|
+
閉じたメニューは 1 ピクセルも描かないので、どのスイープにも測れない。
|
|
316
|
+
destructive の行が読める色で描かれているかは、開いていて初めて測れる。
|
|
317
|
+
</CardDescription>
|
|
318
|
+
</CardHeader>
|
|
319
|
+
<CardContent>
|
|
320
|
+
<DropdownMenu defaultOpen>
|
|
321
|
+
<DropdownMenuTrigger asChild>
|
|
322
|
+
<Button variant="outline" size="sm">
|
|
323
|
+
仕訳アクション
|
|
324
|
+
</Button>
|
|
325
|
+
</DropdownMenuTrigger>
|
|
326
|
+
<DropdownMenuContent align="start">
|
|
327
|
+
<DropdownMenuItem>編集</DropdownMenuItem>
|
|
328
|
+
<DropdownMenuItem>複製</DropdownMenuItem>
|
|
329
|
+
<DropdownMenuSeparator />
|
|
330
|
+
<DropdownMenuItem variant="destructive">削除</DropdownMenuItem>
|
|
331
|
+
</DropdownMenuContent>
|
|
332
|
+
</DropdownMenu>
|
|
333
|
+
</CardContent>
|
|
334
|
+
</Card>
|
|
303
335
|
</Flex>
|
|
304
336
|
</PageContainer>
|
|
305
337
|
);
|
|
@@ -128,6 +128,26 @@ export default function Demo() {
|
|
|
128
128
|
/>
|
|
129
129
|
</Flex>
|
|
130
130
|
|
|
131
|
+
{/* error — the ONE sentence that says why a filter set was refused, and the reason this
|
|
132
|
+
page has an example at all: `.ui-filter-bar-error` was painted with the destructive
|
|
133
|
+
FILL tier (2.95:1 on dark, gh#612) and NO route rendered it, so no sweep could ever
|
|
134
|
+
have caught that. Static, not behind a submit: a surface that needs an interaction is
|
|
135
|
+
a surface `check:contrast` does not measure. */}
|
|
136
|
+
<Flex direction="col" gap="xs">
|
|
137
|
+
<Text size="xs" tone="muted">
|
|
138
|
+
error · role="alert" line under the bar, painted from the TEXT tier
|
|
139
|
+
</Text>
|
|
140
|
+
<FilterBar
|
|
141
|
+
search={{
|
|
142
|
+
value: modelQuery,
|
|
143
|
+
onValueChange: setModelQuery,
|
|
144
|
+
placeholder: "氏名・メールで検索",
|
|
145
|
+
ariaLabel: "メンバーを検索",
|
|
146
|
+
}}
|
|
147
|
+
error="期間の開始日が終了日より後です。条件を修正してください。"
|
|
148
|
+
/>
|
|
149
|
+
</Flex>
|
|
150
|
+
|
|
131
151
|
{/* overflow="wrap" (default) — stacked below 640px, wrapping rows above. */}
|
|
132
152
|
<Flex direction="col" gap="xs">
|
|
133
153
|
<Text size="xs" tone="muted">
|
|
@@ -27,7 +27,11 @@ const onboardingSteps = [
|
|
|
27
27
|
export default function Demo() {
|
|
28
28
|
const [approvalCurrent, setApprovalCurrent] = useState(1);
|
|
29
29
|
const [wizardCurrent, setWizardCurrent] = useState(0);
|
|
30
|
-
|
|
30
|
+
/* Seeded TRUE so `status="error"` paints on mount. The toggle below is still the point of the
|
|
31
|
+
* demo, but starting from `false` meant the error state was reachable only by clicking —
|
|
32
|
+
* and a surface that needs an interaction is a surface no sweep measures (gh#612), which is
|
|
33
|
+
* how `.ui-steps-title[data-status="error"]` kept the destructive FILL tier unnoticed. */
|
|
34
|
+
const [hasError, setHasError] = useState(true);
|
|
31
35
|
|
|
32
36
|
return (
|
|
33
37
|
<PageContainer
|
|
@@ -256,6 +260,15 @@ export default function Demo() {
|
|
|
256
260
|
value={1}
|
|
257
261
|
items={[{ title: "コード入力" }, { title: "確認" }, { title: "完了" }]}
|
|
258
262
|
/>
|
|
263
|
+
{/* status="error" on the INLINE form too. The vertical/horizontal error title had a
|
|
264
|
+
route; `.ui-steps-inline-item[data-status="error"] .ui-steps-inline-control` did
|
|
265
|
+
not, and it reads the same tone token (gh#612). */}
|
|
266
|
+
<Steps
|
|
267
|
+
type="inline"
|
|
268
|
+
status="error"
|
|
269
|
+
value={1}
|
|
270
|
+
items={[{ title: "コード入力" }, { title: "確認" }, { title: "完了" }]}
|
|
271
|
+
/>
|
|
259
272
|
</Flex>
|
|
260
273
|
</CardContent>
|
|
261
274
|
</Card>
|
package/docs/navigation/tabs.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { CircleX } from "lucide-react";
|
|
4
4
|
|
|
5
5
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
6
6
|
import { Button, Text } from "@godxjp/ui/general";
|
|
@@ -547,7 +547,7 @@ export default function Demo() {
|
|
|
547
547
|
id="antd-remove-icon"
|
|
548
548
|
variant="editable-card"
|
|
549
549
|
defaultValue="je-0042"
|
|
550
|
-
closeIcon={<
|
|
550
|
+
closeIcon={<CircleX aria-hidden="true" />}
|
|
551
551
|
onEdit={() => undefined}
|
|
552
552
|
items={[
|
|
553
553
|
{
|
|
@@ -600,6 +600,26 @@ export default function Demo() {
|
|
|
600
600
|
size="lg"
|
|
601
601
|
items={journalItems}
|
|
602
602
|
/>
|
|
603
|
+
{/* CARD on the vertical axis. `card` + `bottom` above had shipped upside down, and
|
|
604
|
+
`start`/`end` had no card example at all — so the rail, which is drawn with a
|
|
605
|
+
box-shadow and therefore cannot be logical, had nothing to be measured against
|
|
606
|
+
in either direction. */}
|
|
607
|
+
<Tabs
|
|
608
|
+
id="antd-card-start"
|
|
609
|
+
defaultValue="pending"
|
|
610
|
+
variant="card"
|
|
611
|
+
tabPlacement="start"
|
|
612
|
+
size="sm"
|
|
613
|
+
items={journalItems}
|
|
614
|
+
/>
|
|
615
|
+
<Tabs
|
|
616
|
+
id="antd-card-end"
|
|
617
|
+
defaultValue="pending"
|
|
618
|
+
variant="card"
|
|
619
|
+
tabPlacement="end"
|
|
620
|
+
size="sm"
|
|
621
|
+
items={journalItems}
|
|
622
|
+
/>
|
|
603
623
|
</Flex>
|
|
604
624
|
</CardContent>
|
|
605
625
|
</Card>
|