@godxjp/ui 28.4.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/README.md +58 -0
- package/dist/app/theme-axes.js +6 -0
- package/dist/components/data-entry/field.d.ts +1 -1
- package/dist/components/data-entry/field.js +35 -4
- package/dist/components/data-entry/index.d.ts +1 -0
- package/dist/components/data-entry/index.js +2 -0
- 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/base.css +5 -3
- package/dist/styles/card-layout.css +11 -6
- package/dist/styles/control.css +20 -8
- package/dist/styles/data-display-layout.css +51 -20
- package/dist/styles/data-entry-layout.css +3 -3
- package/dist/styles/dialog-layout.css +12 -0
- package/dist/styles/layout.css +1 -1
- package/dist/styles/table-layout.css +9 -6
- package/dist/styles/text-layout.css +1 -1
- package/dist/theme/famgia.service.css +2 -4
- package/dist/tokens/components/control.css +4 -3
- package/dist/tokens/components/data-display.css +2 -1
- package/dist/tokens/components/feedback.css +2 -0
- package/dist/tokens/derived.css +20 -0
- package/dist/tokens/foundation.css +6 -13
- package/docs/CUSTOMER-THEMING.md +82 -3
- package/docs/FRAME-COVERAGE-REPORT.md +2 -2
- 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 +109 -0
- package/docs/layout/nav-list.tsx +556 -35
- package/package.json +99 -91
package/README.md
CHANGED
|
@@ -12,6 +12,64 @@ npm i @godxjp/ui
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## Using this from an AI agent
|
|
16
|
+
|
|
17
|
+
The catalog — 165 components, 1616 tokens, 47 cardinal rules — is published in two forms from one
|
|
18
|
+
source. **Entry point for either: [`AGENTS.md`](AGENTS.md).**
|
|
19
|
+
|
|
20
|
+
### If your agent can run a process
|
|
21
|
+
|
|
22
|
+
Claude Code · Codex CLI · Cursor · any MCP client:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @godxjp/ui sync-rules
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Writes `.mcp.json`, `CLAUDE.md` and `.ai/rules/` into the consumer repo and wires
|
|
29
|
+
`@godxjp/ui-mcp`. Prefer this: it is searchable, it drills down instead of dumping, and it is
|
|
30
|
+
**locked to the version on disk** — the failure it prevents is an agent being told a prop does not
|
|
31
|
+
exist because the catalog was two minors behind (gh#789).
|
|
32
|
+
|
|
33
|
+
### If your agent can only fetch URLs
|
|
34
|
+
|
|
35
|
+
ChatGPT on the web, Claude.ai, or any client without a local process. The same data is served as
|
|
36
|
+
static files straight from this public repo — no hosting, no deploy step:
|
|
37
|
+
|
|
38
|
+
| file | size | what it is |
|
|
39
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
|
|
40
|
+
| [`agent/START-HERE.md`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md) | 6 KB | **read first** — self-contained: the four rules, the token override model, a page that passes review |
|
|
41
|
+
| [`agent/llms.txt`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/llms.txt) | 2 KB | the [llms.txt](https://llmstxt.org/) entry point |
|
|
42
|
+
| [`agent/index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json) | 2 KB | manifest: version, counts, every file's URL |
|
|
43
|
+
| [`agent/components-index.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json) | 42 KB | all 165 as name + group + tagline — **fetch this first** |
|
|
44
|
+
| [`agent/components/<Name>.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json) | 1–32 KB | one file per component, each with its `importPath` — **this is the route to take** |
|
|
45
|
+
| [`agent/components.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json) | 1.1 MB | all 165 entries in one file — most URL fetchers truncate this silently; prefer the per-component files |
|
|
46
|
+
| [`agent/tokens.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json) | 0.6 MB | every design token, its value and why it exists |
|
|
47
|
+
| [`agent/vocabulary.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json) | 7 KB | the controlled prop vocabulary |
|
|
48
|
+
| [`agent/rules.json`](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json) | 17 KB | the 47 cardinal rules |
|
|
49
|
+
|
|
50
|
+
Pasteable bootstrap:
|
|
51
|
+
|
|
52
|
+
> Read https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md and follow it.
|
|
53
|
+
> Then fetch .../agent/components-index.json to choose components, and
|
|
54
|
+
> .../agent/components/<Name>.json for the props of each one you chose. Tell me which catalog
|
|
55
|
+
> version you read.
|
|
56
|
+
|
|
57
|
+
**Pin to the version you installed.** Every URL above tracks `main`. Swap `main` for the matching
|
|
58
|
+
tag — `https://raw.githubusercontent.com/godx-jp/godxjp-ui/v28.4.0/agent/...` — because a catalog
|
|
59
|
+
newer than your package describes props you do not have, an older one hides props you do, and
|
|
60
|
+
neither failure announces itself. Pinned URLs only resolve for releases whose tag contains
|
|
61
|
+
`agent/`; on a 404 the release predates this catalog, so read `main` and compare
|
|
62
|
+
`index.json` → `version` against what you installed.
|
|
63
|
+
|
|
64
|
+
### Why static files and not a hosted MCP endpoint
|
|
65
|
+
|
|
66
|
+
GitHub serves files; it does not run servers. MCP over the network needs a process handling POST
|
|
67
|
+
requests, which no amount of Pages or raw hosting provides. A hosted HTTP MCP would work and would
|
|
68
|
+
need real hosting, ops and an auth decision — so the static lane ships first, carrying the same
|
|
69
|
+
data at no operational cost. Regenerate both from the one source with `pnpm gen:agent-catalog`.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
15
73
|
## Role & boundary — read this first
|
|
16
74
|
|
|
17
75
|
This package is **the single source of UI truth**. It is shared, versioned infrastructure, which means two things are non-negotiable:
|
package/dist/app/theme-axes.js
CHANGED
|
@@ -68,6 +68,12 @@ function applyPrimaryColor(root, color, foreground) {
|
|
|
68
68
|
"--primary-active": "initial",
|
|
69
69
|
"--primary-border": "initial",
|
|
70
70
|
"--control-outline": "initial",
|
|
71
|
+
/* The brand TEXT roles derive from the seed too (gh#664), so they are reset for the same
|
|
72
|
+
* reason: an ancestor theme that pinned a link colour for the previous brand would otherwise
|
|
73
|
+
* outrank the seed just set, and a re-tinted product would keep the old brand's links. */
|
|
74
|
+
"--text-link": "initial",
|
|
75
|
+
"--text-brand": "initial",
|
|
76
|
+
"--text-primary": "initial",
|
|
71
77
|
"--primary-hover-channels": `var(--primary-hover-${polarity}-channels)`,
|
|
72
78
|
"--primary-active-channels": `var(--primary-active-${polarity}-channels)`,
|
|
73
79
|
"--ring": hsl(rgb)
|
|
@@ -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
|
}
|
|
@@ -42,6 +42,7 @@ export type { SelectProp, SelectProp as SelectProps } from "./select.js";
|
|
|
42
42
|
export type { SearchSelectOptionProp as SelectOption, SearchSelectLoadParamsProp as SelectLoadParams, SearchSelectLoadResultProp as SelectLoadResult, } from "./search-select.js";
|
|
43
43
|
export { UPLOAD_LIST_IGNORE, Upload, collectUploadCommitActions, createUploadItem, useUploadDraft, } from "./upload.js";
|
|
44
44
|
export type { UploadProps, UploadFileItem, UploadVariant, UploadCommitAction } from "./upload.js";
|
|
45
|
+
export { UploadCropDialog } from "./upload-crop-dialog.js";
|
|
45
46
|
export { Cascader } from "./cascader.js";
|
|
46
47
|
export type { CascaderProps, TreeOption, TreeFieldNames } from "./cascader.js";
|
|
47
48
|
export { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select.js";
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
createUploadItem,
|
|
40
40
|
useUploadDraft
|
|
41
41
|
} from "./upload.js";
|
|
42
|
+
import { UploadCropDialog } from "./upload-crop-dialog.js";
|
|
42
43
|
import { Cascader } from "./cascader.js";
|
|
43
44
|
import { TreeSelect, SHOW_CHILD, SHOW_PARENT, SHOW_ALL } from "./tree-select.js";
|
|
44
45
|
import { Transfer } from "./transfer.js";
|
|
@@ -127,6 +128,7 @@ export {
|
|
|
127
128
|
TreeSelect,
|
|
128
129
|
UPLOAD_LIST_IGNORE,
|
|
129
130
|
Upload,
|
|
131
|
+
UploadCropDialog,
|
|
130
132
|
collectUploadCommitActions,
|
|
131
133
|
createUploadItem,
|
|
132
134
|
dateMatchModifiers,
|
|
@@ -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/base.css
CHANGED
|
@@ -74,7 +74,9 @@
|
|
|
74
74
|
--color-attention: hsl(var(--attention));
|
|
75
75
|
--color-attention-foreground: hsl(var(--attention-foreground));
|
|
76
76
|
|
|
77
|
-
--color-primary-strong: hsl(
|
|
77
|
+
--color-primary-strong: hsl(
|
|
78
|
+
var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels))
|
|
79
|
+
);
|
|
78
80
|
--color-success-strong: hsl(var(--text-success));
|
|
79
81
|
--color-warning-strong: hsl(var(--text-warning));
|
|
80
82
|
--color-info-strong: hsl(var(--text-info));
|
|
@@ -97,8 +99,8 @@
|
|
|
97
99
|
|
|
98
100
|
--color-foreground-tertiary: hsl(var(--text-tertiary));
|
|
99
101
|
--color-foreground-disabled: hsl(var(--text-disabled));
|
|
100
|
-
--color-link: hsl(var(--text-link));
|
|
101
|
-
--color-brand: hsl(var(--text-brand));
|
|
102
|
+
--color-link: hsl(var(--text-link, from hsl(var(--primary)) var(--text-link-channels)));
|
|
103
|
+
--color-brand: hsl(var(--text-brand, from hsl(var(--primary)) var(--text-brand-channels)));
|
|
102
104
|
|
|
103
105
|
--color-primary-hover: hsl(
|
|
104
106
|
var(--primary-hover, from hsl(var(--primary)) var(--primary-hover-channels))
|
|
@@ -55,27 +55,32 @@
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
[data-slot="card"][data-accent="primary"] {
|
|
58
|
-
--card-accent-color: hsl(
|
|
58
|
+
--card-accent-color: hsl(
|
|
59
|
+
var(
|
|
60
|
+
--mark-primary,
|
|
61
|
+
var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels))
|
|
62
|
+
)
|
|
63
|
+
);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
[data-slot="card"][data-accent="success"] {
|
|
62
|
-
--card-accent-color: hsl(var(--mark-success));
|
|
67
|
+
--card-accent-color: hsl(var(--mark-success, var(--text-success)));
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
[data-slot="card"][data-accent="warning"] {
|
|
66
|
-
--card-accent-color: hsl(var(--mark-warning));
|
|
71
|
+
--card-accent-color: hsl(var(--mark-warning, var(--text-warning)));
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
[data-slot="card"][data-accent="info"] {
|
|
70
|
-
--card-accent-color: hsl(var(--mark-info));
|
|
75
|
+
--card-accent-color: hsl(var(--mark-info, var(--text-info)));
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
[data-slot="card"][data-accent="attention"] {
|
|
74
|
-
--card-accent-color: hsl(var(--mark-attention));
|
|
79
|
+
--card-accent-color: hsl(var(--mark-attention, var(--attention)));
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
[data-slot="card"][data-accent="destructive"] {
|
|
78
|
-
--card-accent-color: hsl(var(--mark-destructive));
|
|
83
|
+
--card-accent-color: hsl(var(--mark-destructive, var(--text-error)));
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
[data-slot="card"][data-accent] {
|
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;
|
|
@@ -999,7 +1011,7 @@
|
|
|
999
1011
|
.ui-otp-container:has(.ui-otp-input[data-status="warning"]) .ui-otp-slot,
|
|
1000
1012
|
.ui-otp-container:has(.ui-otp-input[data-status="warning"])
|
|
1001
1013
|
.ui-otp-group[data-appearance="grouped"] {
|
|
1002
|
-
border-color: var(--control-status-warning-border-color);
|
|
1014
|
+
border-color: var(--control-status-warning-border-color, hsl(var(--text-warning)));
|
|
1003
1015
|
}
|
|
1004
1016
|
|
|
1005
1017
|
.ui-otp-container:has(.ui-otp-input[readonly]) .ui-otp-slot {
|
|
@@ -1371,10 +1383,10 @@
|
|
|
1371
1383
|
}
|
|
1372
1384
|
|
|
1373
1385
|
.ui-control-surface[data-status="warning"] {
|
|
1374
|
-
border-color: var(--control-status-warning-border-color);
|
|
1386
|
+
border-color: var(--control-status-warning-border-color, hsl(var(--text-warning)));
|
|
1375
1387
|
--focus-ring-color: var(--warning);
|
|
1376
1388
|
--focus-outline-color: var(--warning);
|
|
1377
|
-
--focus-ring-glow-color: var(--control-status-warning-outline-color);
|
|
1389
|
+
--focus-ring-glow-color: var(--control-status-warning-outline-color, var(--text-warning));
|
|
1378
1390
|
}
|
|
1379
1391
|
|
|
1380
1392
|
.ui-number-input[data-size="sm"] {
|
|
@@ -2442,7 +2454,7 @@
|
|
|
2442
2454
|
|
|
2443
2455
|
.ui-calendar .ui-calendar-day.day-today:not(.day-selected) .ui-calendar-day-button {
|
|
2444
2456
|
box-shadow: inset 0 0 0 1px hsl(var(--primary));
|
|
2445
|
-
color: hsl(var(--text-primary));
|
|
2457
|
+
color: hsl(var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels)));
|
|
2446
2458
|
font-weight: var(--font-weight-medium);
|
|
2447
2459
|
}
|
|
2448
2460
|
|
|
@@ -2471,7 +2483,7 @@
|
|
|
2471
2483
|
|
|
2472
2484
|
.ui-calendar .ui-calendar-day.day-today:not(:has(button)) {
|
|
2473
2485
|
box-shadow: inset 0 0 0 1px hsl(var(--primary));
|
|
2474
|
-
color: hsl(var(--text-primary));
|
|
2486
|
+
color: hsl(var(--text-primary, from hsl(var(--primary)) var(--text-primary-channels)));
|
|
2475
2487
|
font-weight: var(--font-weight-medium);
|
|
2476
2488
|
border-radius: var(--calendar-day-radius);
|
|
2477
2489
|
}
|
|
@@ -2806,8 +2818,8 @@
|
|
|
2806
2818
|
|
|
2807
2819
|
.ui-control[data-status="warning"],
|
|
2808
2820
|
.ui-control-multiline[data-status="warning"] {
|
|
2809
|
-
--focus-ring-color: var(--control-status-warning-outline-color);
|
|
2810
|
-
--focus-outline-color: var(--control-status-warning-outline-color);
|
|
2821
|
+
--focus-ring-color: var(--control-status-warning-outline-color, var(--text-warning));
|
|
2822
|
+
--focus-outline-color: var(--control-status-warning-outline-color, var(--text-warning));
|
|
2811
2823
|
--focus-ring-glow-color: var(--control-status-warning-glow-color);
|
|
2812
2824
|
--focus-ring-glow-alpha: var(--control-status-warning-glow-alpha);
|
|
2813
2825
|
}
|
|
@@ -2833,7 +2845,7 @@
|
|
|
2833
2845
|
}
|
|
2834
2846
|
|
|
2835
2847
|
.ui-control-count[data-exceeded="true"] {
|
|
2836
|
-
color: hsl(var(--control-count-exceeded-color));
|
|
2848
|
+
color: hsl(var(--control-count-exceeded-color, var(--text-error)));
|
|
2837
2849
|
}
|
|
2838
2850
|
|
|
2839
2851
|
.ui-textarea-count {
|