@godxjp/ui 28.5.0 → 28.6.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/components/data-entry/field.d.ts +1 -1
- package/dist/components/data-entry/field.js +35 -4
- package/dist/components/data-entry/input.d.ts +1 -1
- package/dist/components/layout/index.d.ts +1 -1
- package/dist/components/layout/nav-list.d.ts +9 -1
- package/dist/components/layout/nav-list.js +22 -11
- package/dist/components/layout/sidebar.d.ts +16 -2
- package/dist/components/layout/sidebar.js +1 -0
- package/dist/components/ui/password-input.d.ts +1 -1
- package/dist/contracts/measurement.json +1 -1
- package/dist/lib/control-styles.d.ts +18 -3
- package/dist/lib/control-styles.js +1 -1
- package/dist/props/components/data-entry.prop.d.ts +36 -2
- package/dist/props/components/layout.prop.d.ts +16 -5
- package/dist/props/registry.d.ts +1 -1
- package/dist/props/registry.js +8 -1
- package/dist/styles/control.css +12 -0
- package/dist/styles/data-display-layout.css +25 -2
- package/dist/styles/dialog-layout.css +12 -0
- package/dist/styles/layout.css +1 -1
- package/dist/tokens/components/data-display.css +2 -1
- package/dist/tokens/components/feedback.css +2 -0
- package/docs/FRAME-COVERAGE-REPORT.md +1 -1
- package/docs/data-display/collapsible.tsx +5 -5
- package/docs/data-display/descriptions.tsx +188 -109
- package/docs/data-display/legend.tsx +17 -4
- package/docs/data-display/scroll-area.tsx +75 -5
- package/docs/data-display/stat-card.tsx +319 -64
- package/docs/data-entry/form.tsx +700 -6
- package/docs/data-entry/input.tsx +663 -53
- package/docs/data-entry/radio-group.tsx +216 -2
- package/docs/data-entry/switch.tsx +210 -4
- package/docs/data-entry/upload-crop-dialog.tsx +24 -25
- package/docs/layout/nav-list.tsx +556 -35
- package/package.json +7 -6
|
@@ -2,4 +2,4 @@ import * as React from "react";
|
|
|
2
2
|
import type { FieldProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { FieldProp, FieldProp as FieldProps } from "../../props/components/data-entry.prop.js";
|
|
4
4
|
/** Label + optional description beside a checkbox/radio/switch control. */
|
|
5
|
-
export declare function Field({ id, label, description, className, children }: FieldProp): React.JSX.Element;
|
|
5
|
+
export declare function Field({ id, label, labelAddon, description, error, className, children, }: FieldProp): React.JSX.Element;
|
|
@@ -1,12 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
2
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
+
import { mergeAriaIds } from "../../lib/field-a11y.js";
|
|
3
6
|
import { Label } from "./label.js";
|
|
4
|
-
function Field({
|
|
7
|
+
function Field({
|
|
8
|
+
id,
|
|
9
|
+
label,
|
|
10
|
+
labelAddon,
|
|
11
|
+
description,
|
|
12
|
+
error,
|
|
13
|
+
className,
|
|
14
|
+
children
|
|
15
|
+
}) {
|
|
16
|
+
const descriptionId = description ? `${id}-description` : void 0;
|
|
17
|
+
const errorId = error ? `${id}-error` : void 0;
|
|
18
|
+
const childProps = React.isValidElement(children) ? children.props : void 0;
|
|
19
|
+
const control = React.isValidElement(children) ? React.cloneElement(children, {
|
|
20
|
+
"aria-describedby": mergeAriaIds(
|
|
21
|
+
childProps?.["aria-describedby"],
|
|
22
|
+
descriptionId,
|
|
23
|
+
errorId
|
|
24
|
+
),
|
|
25
|
+
"aria-errormessage": mergeAriaIds(
|
|
26
|
+
childProps?.["aria-errormessage"],
|
|
27
|
+
errorId
|
|
28
|
+
),
|
|
29
|
+
"aria-invalid": error ? true : childProps?.["aria-invalid"]
|
|
30
|
+
}) : children;
|
|
31
|
+
const labelNode = /* @__PURE__ */ jsx(Label, { htmlFor: id, className: "ui-choice-label", children: label });
|
|
5
32
|
return /* @__PURE__ */ jsxs("div", { className: cn("ui-choice-field", className), children: [
|
|
6
|
-
/* @__PURE__ */ jsx("div", { className: "ui-choice-control", children }),
|
|
33
|
+
/* @__PURE__ */ jsx("div", { className: "ui-choice-control", children: control }),
|
|
7
34
|
/* @__PURE__ */ jsxs("div", { className: "ui-choice-content", children: [
|
|
8
|
-
/* @__PURE__ */
|
|
9
|
-
|
|
35
|
+
labelAddon != null ? /* @__PURE__ */ jsxs("div", { className: "ui-choice-label-row", children: [
|
|
36
|
+
labelNode,
|
|
37
|
+
labelAddon
|
|
38
|
+
] }) : labelNode,
|
|
39
|
+
description ? /* @__PURE__ */ jsx("p", { id: descriptionId, className: "ui-choice-description", children: description }) : null,
|
|
40
|
+
error ? /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "ui-choice-error", children: error }) : null
|
|
10
41
|
] })
|
|
11
42
|
] });
|
|
12
43
|
}
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
export type { InputProp, InputProp as InputProps } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export declare const Input: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "prefix" | "size"> & {
|
|
4
4
|
onValueChange?: (value: string) => void;
|
|
5
|
-
size?: "sm" | "md" | "lg";
|
|
5
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
6
6
|
status?: import("../../props/index.js").ControlStatusProp;
|
|
7
7
|
variant?: import("../../props/index.js").ControlVariantProp;
|
|
8
8
|
allowClear?: import("../../props/index.js").AllowClearProp;
|
|
@@ -38,7 +38,7 @@ export type { ErrorSurfaceModeProp, ErrorSurfaceStatusProp } from "../../props/v
|
|
|
38
38
|
export { Breadcrumb } from "./breadcrumb.js";
|
|
39
39
|
export type { BreadcrumbProps } from "./breadcrumb.js";
|
|
40
40
|
export { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
|
|
41
|
-
export type { SidebarItemData, SidebarLinkComponentProp, SidebarLinkProp, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarRenderItemProp, SidebarSectionProp, } from "../../props/components/layout.prop.js";
|
|
41
|
+
export type { SidebarItemData, SidebarItemProp, SidebarLinkComponentProp, SidebarLinkProp, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarRenderItemProp, SidebarSectionProp, } from "../../props/components/layout.prop.js";
|
|
42
42
|
export { Topbar } from "./topbar.js";
|
|
43
43
|
export type { TopbarProp, TopbarProps } from "./topbar.js";
|
|
44
44
|
export { TopbarItem } from "./topbar-item.js";
|
|
@@ -16,9 +16,17 @@ export type NavListProps = NavListProp;
|
|
|
16
16
|
* consumer that already learnt `SidebarItemProp` / `linkComponent` for the rail knows this too.
|
|
17
17
|
* Only the container differs — a `<nav>` landmark instead of the shell's grid area, and no
|
|
18
18
|
* collapsed rail (a page-level nav has no rail to collapse into).
|
|
19
|
+
*
|
|
20
|
+
* `item.children` is part of that shared row shape, and it is rendered through the rail's OWN
|
|
21
|
+
* `NavGroup` (gh#815). It used to be dropped: `items` mapped straight to `SidebarItem`, so a
|
|
22
|
+
* nested group type-checked, rendered as a single flat row, and its children vanished with no
|
|
23
|
+
* error and no warning. A grouped settings nav — アカウント / セキュリティ / 通知 / 請求 — is the
|
|
24
|
+
* canonical use of this component, and it took one `NavList` per group wrapped in
|
|
25
|
+
* `SidebarSection`, i.e. N `<nav>` landmarks where the page has one navigation. Now it is one
|
|
26
|
+
* `NavList`, one landmark, and the group opens itself whenever the route lands on a descendant.
|
|
19
27
|
*/
|
|
20
28
|
export declare const NavList: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLElement>, "onSelect"> & {
|
|
21
|
-
items: import("
|
|
29
|
+
items: import("./sidebar.js").SidebarItemProp[];
|
|
22
30
|
activeId?: string;
|
|
23
31
|
label: string;
|
|
24
32
|
linkComponent?: import("./sidebar.js").SidebarLinkComponentProp;
|
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
-
import { SidebarItem } from "./sidebar.js";
|
|
5
|
+
import { NavGroup, SidebarItem } from "./sidebar.js";
|
|
6
6
|
const NavList = React.forwardRef(
|
|
7
|
-
({ items, activeId, label, linkComponent, onSelect, className, ...props }, ref) => /* @__PURE__ */ jsx("nav", { ref, "aria-label": label, className: cn("ui-nav-list", className), ...props, children: items.map(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
({ items, activeId, label, linkComponent, onSelect, className, ...props }, ref) => /* @__PURE__ */ jsx("nav", { ref, "aria-label": label, className: cn("ui-nav-list", className), ...props, children: items.map(
|
|
8
|
+
(item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(
|
|
9
|
+
NavGroup,
|
|
10
|
+
{
|
|
11
|
+
item,
|
|
12
|
+
activeId: activeId ?? "",
|
|
13
|
+
linkComponent,
|
|
14
|
+
onSelect
|
|
15
|
+
},
|
|
16
|
+
item.id
|
|
17
|
+
) : /* @__PURE__ */ jsx(
|
|
18
|
+
SidebarItem,
|
|
19
|
+
{
|
|
20
|
+
item,
|
|
21
|
+
active: item.id === activeId,
|
|
22
|
+
linkComponent,
|
|
23
|
+
onActivate: onSelect
|
|
24
|
+
},
|
|
25
|
+
item.id
|
|
26
|
+
)
|
|
27
|
+
) })
|
|
17
28
|
);
|
|
18
29
|
NavList.displayName = "NavList";
|
|
19
30
|
export {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type { SidebarItemData, SidebarLinkComponentProp, SidebarProp, SidebarRenderItemProp } from "../../props/components/layout.prop.js";
|
|
3
|
-
export type { SidebarItemData, SidebarLinkComponentProp, SidebarLinkProp, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarRenderItemProp, } from "../../props/components/layout.prop.js";
|
|
2
|
+
import type { SidebarItemData, SidebarItemProp, SidebarLinkComponentProp, SidebarProp, SidebarRenderItemProp } from "../../props/components/layout.prop.js";
|
|
3
|
+
export type { SidebarItemData, SidebarItemProp, SidebarLinkComponentProp, SidebarLinkProp, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarRenderItemProp, } from "../../props/components/layout.prop.js";
|
|
4
4
|
type RenderItem = (item: SidebarItemData, rowProps: SidebarRenderItemProp) => React.ReactNode;
|
|
5
5
|
type SidebarHeaderProps = React.HTMLAttributes<HTMLDivElement>;
|
|
6
6
|
type SidebarSectionProps = {
|
|
@@ -28,6 +28,20 @@ type SidebarItemProps = {
|
|
|
28
28
|
export declare function SidebarHeader({ children, className, ...props }: SidebarHeaderProps): React.JSX.Element;
|
|
29
29
|
export declare function SidebarSection({ label, collapsed, children, className, ...props }: SidebarSectionProps & React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
|
|
30
30
|
export declare function SidebarItem({ item, active, sub, onActivate, linkComponent: LinkComponent, asChild, renderItem, children, ...props }: SidebarItemProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick">): React.JSX.Element;
|
|
31
|
+
type RowProps = {
|
|
32
|
+
item: SidebarItemProp;
|
|
33
|
+
activeId: string;
|
|
34
|
+
onSelect?: (id: string) => void;
|
|
35
|
+
sub?: boolean;
|
|
36
|
+
linkComponent?: SidebarLinkComponentProp;
|
|
37
|
+
renderItem?: RenderItem;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The collapsible submenu group of a nested `SidebarItemProp`. Exported (not from the package —
|
|
41
|
+
* only within `src/components/layout`) so `NavList` renders nested items through the SAME group as
|
|
42
|
+
* the rail instead of dropping `item.children` on the floor (gh#815).
|
|
43
|
+
*/
|
|
44
|
+
export declare function NavGroup({ item, activeId, onSelect, linkComponent, renderItem }: RowProps): React.JSX.Element;
|
|
31
45
|
export { createSidebarLink } from "./sidebar-link.js";
|
|
32
46
|
/**
|
|
33
47
|
* Sidebar — data-driven vertical nav rail. Use {@link createSidebarLink} to adapt a router `Link`,
|
|
@@ -18,7 +18,7 @@ export type PasswordInputProps = Omit<React.ComponentPropsWithoutRef<typeof Inpu
|
|
|
18
18
|
};
|
|
19
19
|
export declare const PasswordInput: React.ForwardRefExoticComponent<Omit<Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "prefix" | "size"> & {
|
|
20
20
|
onValueChange?: (value: string) => void;
|
|
21
|
-
size?: "sm" | "md" | "lg";
|
|
21
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
22
22
|
status?: import("../../props/index.js").ControlStatusProp;
|
|
23
23
|
variant?: import("../../props/index.js").ControlVariantProp;
|
|
24
24
|
allowClear?: import("../../props/index.js").AllowClearProp;
|
|
@@ -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.6.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,
|
|
@@ -30,11 +30,26 @@ export declare const controlOpenRingClass = "ui-control-trigger";
|
|
|
30
30
|
* `--control-bounded-width` in gh#375). A control that wants the antd `variant` × `status` matrix
|
|
31
31
|
* therefore composes THIS class plus `ui-control-surface`, which supplies the identical resting
|
|
32
32
|
* border and fill from `--control-surface-*`.
|
|
33
|
+
*
|
|
34
|
+
* TRUNCATION HAS TO READ AS TRUNCATION (gh#813). `line-clamp-1` clips the value and — inside the
|
|
35
|
+
* trigger's `whitespace-nowrap` — never reaches a second line, so its own ellipsis never engages:
|
|
36
|
+
* `YYYY-MM-DD(年-月-日)` in a 101px value box showed `YYYY-MM-DD(全`, a format string cut through
|
|
37
|
+
* the middle of a glyph, with nothing to say it had been cut. Two declarations because the two
|
|
38
|
+
* engines ellipsize by different mechanisms, both measured on
|
|
39
|
+
* `/isolate/navigation-app-setting-picker` (dateFormat, ja, 1280×1000, value box 101px / text
|
|
40
|
+
* 160px):
|
|
41
|
+
*
|
|
42
|
+
* text-ellipsis Blink honours `text-overflow` on the clamped box → `YYYY-MM-DD…`.
|
|
43
|
+
* Gecko ignores it there (still `YYYY-MM-DD(全`).
|
|
44
|
+
* whitespace-normal lets `-webkit-line-clamp` clamp a real line, which is the ellipsis Gecko
|
|
45
|
+
* DOES paint → `YYYY-MM-D…` in Firefox, `YYYY-MM-DD…` in Chromium.
|
|
46
|
+
*
|
|
47
|
+
* The clamp keeps it to one line, so a wrappable value cannot grow the control.
|
|
33
48
|
*/
|
|
34
|
-
export declare const controlTriggerBaseClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1";
|
|
35
|
-
export declare const controlTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1 border-input bg-background";
|
|
49
|
+
export declare const controlTriggerBaseClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1 [&>[data-slot=select-value]]:whitespace-normal [&>[data-slot=select-value]]:text-ellipsis";
|
|
50
|
+
export declare const controlTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1 [&>[data-slot=select-value]]:whitespace-normal [&>[data-slot=select-value]]:text-ellipsis border-input bg-background";
|
|
36
51
|
/** `controlTriggerBaseClass` + the token-driven surface — the select-family trigger. */
|
|
37
|
-
export declare const controlSurfaceTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1 ui-control-surface";
|
|
52
|
+
export declare const controlSurfaceTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1 [&>[data-slot=select-value]]:whitespace-normal [&>[data-slot=select-value]]:text-ellipsis ui-control-surface";
|
|
38
53
|
export declare const controlIconClass = "size-[length:var(--control-height)] shrink-0";
|
|
39
54
|
export declare const controlIconSmClass = "size-[calc(var(--control-height)-0.5rem)] shrink-0";
|
|
40
55
|
/** Leading/affix icon inside an input row (search, command) — sized to `--control-icon-size`. */
|
|
@@ -2,7 +2,7 @@ const controlMultilineClass = "ui-control-multiline aria-invalid:border-destruct
|
|
|
2
2
|
const controlMultilineGhostClass = "ui-control-multiline data-[status=error]:border-destructive data-[status=warning]:border-warning w-full min-h-0 border-0 bg-transparent shadow-none placeholder:text-muted-foreground focus-visible:ring-0";
|
|
3
3
|
const controlMultilineFilledClass = "ui-control-multiline ui-control--filled aria-invalid:border-destructive data-[status=error]:border-destructive data-[status=warning]:border-warning w-full rounded-[var(--control-radius)] ring-offset-background placeholder:text-muted-foreground";
|
|
4
4
|
const controlOpenRingClass = "ui-control-trigger";
|
|
5
|
-
const controlTriggerBaseClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1";
|
|
5
|
+
const controlTriggerBaseClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] transition-[color,box-shadow] [&>[data-slot=select-value]]:line-clamp-1 [&>[data-slot=select-value]]:whitespace-normal [&>[data-slot=select-value]]:text-ellipsis";
|
|
6
6
|
const controlTriggerClass = `${controlTriggerBaseClass} border-input bg-background`;
|
|
7
7
|
const controlSurfaceTriggerClass = `${controlTriggerBaseClass} ui-control-surface`;
|
|
8
8
|
const controlIconClass = "size-[length:var(--control-height)] shrink-0";
|
|
@@ -126,8 +126,18 @@ export type CommandProp = {
|
|
|
126
126
|
};
|
|
127
127
|
export type InputProp = Omit<React.InputHTMLAttributes<HTMLInputElement>, "size" | "prefix"> & {
|
|
128
128
|
onValueChange?: (value: string) => void;
|
|
129
|
-
/**
|
|
130
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Control height tier: `md` (default), plus `xs`, `sm` and `lg` — the same tiers as
|
|
131
|
+
* SelectTrigger and NumberInput.
|
|
132
|
+
*
|
|
133
|
+
* `xs` was missing from this union alone. Everything underneath it already worked: `Input`
|
|
134
|
+
* renders `ui-control` and emits `data-size={size}`, and `.ui-control[data-size="xs"]`
|
|
135
|
+
* (src/styles/control.css:1674) binds `--control-height-xs`. That rule was added for the
|
|
136
|
+
* select family — its comment says the token "existed with nothing reading it" — and this
|
|
137
|
+
* union was never widened to match, so the most-used control in the package was the one
|
|
138
|
+
* control missing the bottom rung, for a reason no user could have guessed from behaviour.
|
|
139
|
+
*/
|
|
140
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
131
141
|
/** Validation state the field paints — antd `status`. `error` also reports `aria-invalid`. */
|
|
132
142
|
status?: ControlStatusProp;
|
|
133
143
|
/** Chrome level — antd `variant`. Default `outlined`. */
|
|
@@ -547,7 +557,31 @@ export type SwitchProp = Omit<React.ComponentPropsWithoutRef<"button">, "checked
|
|
|
547
557
|
export type FieldProp = {
|
|
548
558
|
id: IdProp;
|
|
549
559
|
label: LabelProp;
|
|
560
|
+
/**
|
|
561
|
+
* Optional content rendered BESIDE the label and OUTSIDE the `<label>` element — a help
|
|
562
|
+
* button, a Tooltip trigger, a status chip, a short text action. `FormField.labelAddon` with
|
|
563
|
+
* the same semantics, and it has to live outside the label for a reason this row has that a
|
|
564
|
+
* stacked field does not: `Field`'s label is a real `<label htmlFor>` pointing at a
|
|
565
|
+
* checkbox / radio / switch, so a browser forwards a click anywhere inside it to that
|
|
566
|
+
* control. An interactive addon passed through `label` therefore TOGGLES the control when
|
|
567
|
+
* pressed (gh#812). A non-interactive mark (a `Badge as="span"`) is still fine inside
|
|
568
|
+
* `label`; anything pressable belongs here.
|
|
569
|
+
*/
|
|
570
|
+
labelAddon?: React.ReactNode;
|
|
571
|
+
/**
|
|
572
|
+
* Muted hint under the label. Announced with the control (`aria-describedby`) rather than
|
|
573
|
+
* left as decoration, and it COMPOSES with `error` — `.ui-choice-content` is already a
|
|
574
|
+
* vertical stack, so the two lines sit under each other exactly as they do on `FormField`.
|
|
575
|
+
*/
|
|
550
576
|
description?: React.ReactNode;
|
|
577
|
+
/**
|
|
578
|
+
* Validation message under the description (`role="alert"`). Presence of a message IS the
|
|
579
|
+
* invalid state: the control is cloned with `aria-invalid`, and the message id reaches it
|
|
580
|
+
* through `aria-errormessage` AND `aria-describedby` (see field.tsx — react-aria's hidden
|
|
581
|
+
* input drops the former). This is the slot that lets a validated boolean stay in `Field`:
|
|
582
|
+
* `FormField` must not wrap a bare `Switch`, so before this there was nowhere to put it.
|
|
583
|
+
*/
|
|
584
|
+
error?: ErrorProp;
|
|
551
585
|
className?: ClassNameProp;
|
|
552
586
|
children: React.ReactNode;
|
|
553
587
|
};
|
|
@@ -932,11 +932,14 @@ export type SidebarItemProp = {
|
|
|
932
932
|
id: string;
|
|
933
933
|
label: string;
|
|
934
934
|
/**
|
|
935
|
-
* Leading 16px glyph
|
|
936
|
-
*
|
|
935
|
+
* Leading 16px glyph. STRONGLY RECOMMENDED on the rail — the collapsed rail is icon-only, so a
|
|
936
|
+
* row without one reads as a hole there — but OPTIONAL at the type level (gh#815): the runtime
|
|
937
|
+
* has always rendered an empty `.sb-icon` box for a row whose data carries no glyph, precisely
|
|
938
|
+
* so the label column still aligns, and `NavList` (which has no collapsed rail) routinely mixes
|
|
939
|
+
* rows with and without one. Its colour is themeable separately from the label via
|
|
937
940
|
* `--sidebar-nav-icon-foreground` (see {@link SidebarProp}).
|
|
938
941
|
*/
|
|
939
|
-
icon
|
|
942
|
+
icon?: ComponentType<SVGProps<SVGSVGElement>>;
|
|
940
943
|
/**
|
|
941
944
|
* Count/status affix rendered in the row's `.sb-badge` pill. Pass the CONTENT ONLY — a number, a
|
|
942
945
|
* string, `"9+"`.
|
|
@@ -968,7 +971,11 @@ export type SidebarItemProp = {
|
|
|
968
971
|
* right-click / open-in-new-tab / middle-click all work.
|
|
969
972
|
*/
|
|
970
973
|
href?: string;
|
|
971
|
-
/**
|
|
974
|
+
/**
|
|
975
|
+
* Nested rows — renders a collapsible submenu group (the parent reads active when any child is).
|
|
976
|
+
* Honoured by BOTH `Sidebar` and `NavList` (gh#815): `NavList` used to map `items` straight to a
|
|
977
|
+
* row, so a nested group type-checked, rendered as one flat row and lost its children silently.
|
|
978
|
+
*/
|
|
972
979
|
children?: SidebarItemProp[];
|
|
973
980
|
};
|
|
974
981
|
/**
|
|
@@ -979,7 +986,11 @@ export type SidebarItemProp = {
|
|
|
979
986
|
* learn a second one, and a fix to the row reaches both.
|
|
980
987
|
*/
|
|
981
988
|
export type NavListProp = Omit<React.HTMLAttributes<HTMLElement>, "onSelect"> & {
|
|
982
|
-
/**
|
|
989
|
+
/**
|
|
990
|
+
* Rows, in reading order. An item carrying `children` renders as a collapsible GROUP — the same
|
|
991
|
+
* `.sb-nav-group` the rail draws, opened by the route whenever a descendant is active — so a
|
|
992
|
+
* grouped settings nav is one `NavList` and one `<nav>` landmark, not one per group (gh#815).
|
|
993
|
+
*/
|
|
983
994
|
items: SidebarItemProp[];
|
|
984
995
|
/** `SidebarItemProp.id` of the current route; that row gets `aria-current="page"`. */
|
|
985
996
|
activeId?: string;
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -1470,7 +1470,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1470
1470
|
readonly FieldProp: {
|
|
1471
1471
|
readonly group: "data-entry";
|
|
1472
1472
|
readonly file: "components/data-entry.prop.ts";
|
|
1473
|
-
readonly vocabulary: readonly ["IdProp", "LabelProp", "DescriptionProp", "ClassNameProp", "ChildrenProp"];
|
|
1473
|
+
readonly vocabulary: readonly ["IdProp", "LabelProp", "DescriptionProp", "ErrorProp", "ClassNameProp", "ChildrenProp"];
|
|
1474
1474
|
};
|
|
1475
1475
|
readonly SliderProp: {
|
|
1476
1476
|
readonly group: "data-entry";
|
package/dist/props/registry.js
CHANGED
|
@@ -1689,7 +1689,14 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1689
1689
|
FieldProp: {
|
|
1690
1690
|
group: "data-entry",
|
|
1691
1691
|
file: "components/data-entry.prop.ts",
|
|
1692
|
-
vocabulary: [
|
|
1692
|
+
vocabulary: [
|
|
1693
|
+
"IdProp",
|
|
1694
|
+
"LabelProp",
|
|
1695
|
+
"DescriptionProp",
|
|
1696
|
+
"ErrorProp",
|
|
1697
|
+
"ClassNameProp",
|
|
1698
|
+
"ChildrenProp"
|
|
1699
|
+
]
|
|
1693
1700
|
},
|
|
1694
1701
|
SliderProp: {
|
|
1695
1702
|
group: "data-entry",
|
package/dist/styles/control.css
CHANGED
|
@@ -435,11 +435,23 @@
|
|
|
435
435
|
font-weight: var(--font-weight-normal);
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
.ui-choice-label-row {
|
|
439
|
+
display: flex;
|
|
440
|
+
flex-wrap: wrap;
|
|
441
|
+
align-items: center;
|
|
442
|
+
gap: var(--control-label-space-gap);
|
|
443
|
+
}
|
|
444
|
+
|
|
438
445
|
.ui-choice-description {
|
|
439
446
|
color: hsl(var(--muted-foreground));
|
|
440
447
|
font-size: var(--choice-description-font-size);
|
|
441
448
|
}
|
|
442
449
|
|
|
450
|
+
.ui-choice-error {
|
|
451
|
+
color: hsl(var(--text-error));
|
|
452
|
+
font-size: var(--choice-description-font-size);
|
|
453
|
+
}
|
|
454
|
+
|
|
443
455
|
.ui-label {
|
|
444
456
|
display: flex;
|
|
445
457
|
align-items: center;
|
|
@@ -289,9 +289,32 @@
|
|
|
289
289
|
.ui-progress-ring-label {
|
|
290
290
|
position: relative;
|
|
291
291
|
color: hsl(var(--foreground));
|
|
292
|
-
font-size: var(--progress-ring-label-font-size);
|
|
292
|
+
font-size: var(--progress-ring-label-font-size, calc(var(--progress-ring-size) * 0.28));
|
|
293
293
|
font-variant-numeric: tabular-nums;
|
|
294
|
-
|
|
294
|
+
|
|
295
|
+
line-height: var(--line-height-normal);
|
|
296
|
+
|
|
297
|
+
max-inline-size: calc(
|
|
298
|
+
var(--progress-ring-size) - 2 * var(--progress-ring-border-width) - var(--space-1)
|
|
299
|
+
);
|
|
300
|
+
overflow: hidden;
|
|
301
|
+
text-overflow: ellipsis;
|
|
302
|
+
white-space: nowrap;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.ui-disclosure-chevron {
|
|
306
|
+
transition: rotate var(--duration-fast) var(--ease-standard);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
:is([data-state="open"], [aria-expanded="true"]) > .ui-disclosure-chevron,
|
|
310
|
+
:is([data-state="open"], [aria-expanded="true"]) .ui-disclosure-chevron {
|
|
311
|
+
rotate: 180deg;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@media (prefers-reduced-motion: reduce) {
|
|
315
|
+
.ui-disclosure-chevron {
|
|
316
|
+
transition: none;
|
|
317
|
+
}
|
|
295
318
|
}
|
|
296
319
|
|
|
297
320
|
.ui-legend {
|
|
@@ -146,6 +146,18 @@
|
|
|
146
146
|
opacity: var(--dialog-close-rest-alpha);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
:is([data-slot="dialog-close"], [data-slot="sheet-close"])::after {
|
|
150
|
+
content: "";
|
|
151
|
+
position: absolute;
|
|
152
|
+
inset-block-start: 50%;
|
|
153
|
+
inset-inline-start: 50%;
|
|
154
|
+
translate: -50% -50%;
|
|
155
|
+
min-inline-size: var(--dialog-close-size);
|
|
156
|
+
min-block-size: var(--dialog-close-size);
|
|
157
|
+
inline-size: 100%;
|
|
158
|
+
block-size: 100%;
|
|
159
|
+
}
|
|
160
|
+
|
|
149
161
|
[data-slot="dialog-close"]:hover {
|
|
150
162
|
opacity: 1;
|
|
151
163
|
}
|
package/dist/styles/layout.css
CHANGED
|
@@ -113,7 +113,8 @@
|
|
|
113
113
|
--progress-ring-size: 2.75rem;
|
|
114
114
|
--progress-ring-size-sm: 2rem;
|
|
115
115
|
--progress-ring-border-width: var(--stroke-lg);
|
|
116
|
-
|
|
116
|
+
|
|
117
|
+
--progress-ring-label-font-size: initial;
|
|
117
118
|
|
|
118
119
|
--legend-gap: 0.875rem;
|
|
119
120
|
--legend-item-gap: 0.3125rem;
|
|
@@ -173,7 +173,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
173
173
|
| Carousel | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
174
174
|
| AppSettingPicker | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
175
175
|
| AppSettingToggle | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
176
|
-
| Field | ✓ | N/A |
|
|
176
|
+
| Field | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
177
177
|
| LineChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
178
178
|
| BarChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
179
179
|
| CompactBarTrend | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "@godxjp/ui/data-display";
|
|
11
11
|
import { Button, Text } from "@godxjp/ui/general";
|
|
12
12
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
13
|
-
import {
|
|
13
|
+
import { ChevronDown } from "lucide-react";
|
|
14
14
|
import { useState } from "react";
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -43,7 +43,7 @@ export default function Demo() {
|
|
|
43
43
|
aria-label="開閉"
|
|
44
44
|
className="transition-transform data-[state=open]:rotate-180"
|
|
45
45
|
>
|
|
46
|
-
<
|
|
46
|
+
<ChevronDown className="ui-disclosure-chevron" />
|
|
47
47
|
</Button>
|
|
48
48
|
</CollapsibleTrigger>
|
|
49
49
|
</Flex>
|
|
@@ -74,7 +74,7 @@ export default function Demo() {
|
|
|
74
74
|
aria-label="開閉"
|
|
75
75
|
className="transition-transform data-[state=open]:rotate-180"
|
|
76
76
|
>
|
|
77
|
-
<
|
|
77
|
+
<ChevronDown className="ui-disclosure-chevron" />
|
|
78
78
|
</Button>
|
|
79
79
|
</CollapsibleTrigger>
|
|
80
80
|
</Flex>
|
|
@@ -112,7 +112,7 @@ export default function Demo() {
|
|
|
112
112
|
aria-label="開閉"
|
|
113
113
|
className="transition-transform data-[state=open]:rotate-180"
|
|
114
114
|
>
|
|
115
|
-
<
|
|
115
|
+
<ChevronDown className="ui-disclosure-chevron" />
|
|
116
116
|
</Button>
|
|
117
117
|
</CollapsibleTrigger>
|
|
118
118
|
</Flex>
|
|
@@ -144,7 +144,7 @@ export default function Demo() {
|
|
|
144
144
|
aria-label="開閉"
|
|
145
145
|
className="transition-transform data-[state=open]:rotate-180"
|
|
146
146
|
>
|
|
147
|
-
<
|
|
147
|
+
<ChevronDown className="ui-disclosure-chevron" />
|
|
148
148
|
</Button>
|
|
149
149
|
</CollapsibleTrigger>
|
|
150
150
|
</Flex>
|