@godxjp/ui 27.9.0 → 27.10.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/upload.d.ts +1 -1
- package/dist/components/data-entry/upload.js +2 -1
- package/dist/components/general/button.d.ts +1 -0
- package/dist/components/general/button.js +10 -2
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +12 -2
- package/dist/components/ui/toggle-group.d.ts +4 -0
- package/dist/components/ui/toggle-group.js +10 -2
- package/dist/components/ui/toggle.d.ts +4 -2
- package/dist/components/ui/toggle.js +22 -3
- package/dist/contracts/measurement.json +1 -1
- package/dist/props/components/data-entry.prop.d.ts +16 -0
- package/dist/props/components/general.prop.d.ts +12 -0
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +20 -1
- package/dist/props/registry.d.ts +7 -2
- package/dist/props/registry.js +10 -0
- package/dist/styles/control.css +11 -0
- package/dist/styles/toggle.css +5 -0
- package/dist/tokens/components/toggle.css +7 -0
- package/docs/data-entry/toggle-count.tsx +51 -0
- package/docs/data-entry/toggle-group.tsx +40 -2
- package/docs/data-entry/toggle.tsx +54 -2
- package/docs/data-entry/upload.tsx +44 -0
- package/docs/general/button/index.tsx +18 -6
- package/docs/layout/page-container.tsx +67 -1
- package/package.json +2 -2
|
@@ -4,4 +4,4 @@ export type { UploadProp, UploadProp as UploadProps, UploadFileItemProp, UploadV
|
|
|
4
4
|
export type { UploadFileItem, UploadVariant, UploadCommitAction } from "./upload-types.js";
|
|
5
5
|
export { collectUploadCommitActions, createUploadItem, UPLOAD_LIST_IGNORE } from "./upload-types.js";
|
|
6
6
|
export { useUploadDraft } from "./use-upload-draft.js";
|
|
7
|
-
export declare function Upload({ variant, listType, triggerSize, triggerVariant, value, defaultValue, onValueChange, accept: acceptProp, multiple: multipleProp, maxCount: maxCountProp, maxSizeBytes, disabled: disabledProp, readOnly, directory, pastable, openFileDialogOnClick, name, action, method, headers, data, withCredentials, beforeUpload, onReject, onRemove, onPreview, onDownload, previewFile, onDrop, showUploadList, itemRender, removable, onUpload, id, className, children, ...ariaProps }: UploadProp): React.JSX.Element;
|
|
7
|
+
export declare function Upload({ variant, listType, triggerSize, triggerVariant, triggerIcon: TriggerIcon, value, defaultValue, onValueChange, accept: acceptProp, multiple: multipleProp, maxCount: maxCountProp, maxSizeBytes, disabled: disabledProp, readOnly, directory, pastable, openFileDialogOnClick, name, action, method, headers, data, withCredentials, beforeUpload, onReject, onRemove, onPreview, onDownload, previewFile, onDrop, showUploadList, itemRender, removable, onUpload, id, className, children, ...ariaProps }: UploadProp): React.JSX.Element;
|
|
@@ -70,6 +70,7 @@ function Upload({
|
|
|
70
70
|
listType,
|
|
71
71
|
triggerSize,
|
|
72
72
|
triggerVariant = "outline",
|
|
73
|
+
triggerIcon: TriggerIcon = UploadIcon,
|
|
73
74
|
value,
|
|
74
75
|
defaultValue,
|
|
75
76
|
onValueChange,
|
|
@@ -498,7 +499,7 @@ function Upload({
|
|
|
498
499
|
"aria-label": triggerAriaLabel,
|
|
499
500
|
children: [
|
|
500
501
|
/* @__PURE__ */ jsx(
|
|
501
|
-
|
|
502
|
+
TriggerIcon,
|
|
502
503
|
{
|
|
503
504
|
className: "ui-upload-trigger-icon",
|
|
504
505
|
"data-with-label": iconOnly ? void 0 : "",
|
|
@@ -71,6 +71,7 @@ const Button = React.forwardRef(
|
|
|
71
71
|
count,
|
|
72
72
|
overflowCount = 99,
|
|
73
73
|
showZero = true,
|
|
74
|
+
countLabel,
|
|
74
75
|
disabled,
|
|
75
76
|
type,
|
|
76
77
|
children,
|
|
@@ -84,8 +85,14 @@ const Button = React.forwardRef(
|
|
|
84
85
|
loadingText ?? children
|
|
85
86
|
] }) : children;
|
|
86
87
|
const showCount = !asChild && count != null && (count !== 0 || showZero);
|
|
87
|
-
const
|
|
88
|
-
const
|
|
88
|
+
const countText = showCount && count != null && count > overflowCount ? `${numberFormat(locale).format(overflowCount)}+` : count != null ? numberFormat(locale).format(count) : "";
|
|
89
|
+
const spokenCount = countLabel ? `${countText} ${countLabel}` : countText;
|
|
90
|
+
const ariaLabel = props["aria-label"];
|
|
91
|
+
const foldedAriaLabel = showCount && ariaLabel != null ? { "aria-label": `${ariaLabel}, ${spokenCount}` } : null;
|
|
92
|
+
const countNode = showCount ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
93
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "button-count", className: "ui-button-count", "aria-hidden": "true", children: countText }),
|
|
94
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: `, ${spokenCount}` })
|
|
95
|
+
] }) : null;
|
|
89
96
|
return /* @__PURE__ */ jsx(
|
|
90
97
|
Comp,
|
|
91
98
|
{
|
|
@@ -109,6 +116,7 @@ const Button = React.forwardRef(
|
|
|
109
116
|
),
|
|
110
117
|
ref,
|
|
111
118
|
...props,
|
|
119
|
+
...foldedAriaLabel,
|
|
112
120
|
children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
113
121
|
content,
|
|
114
122
|
countNode
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PageContainerProp, PageInsetProp } from "../../props/components/layout.prop.js";
|
|
2
|
-
export type { PageContainerProp, PageContainerProp as PageContainerProps, } from "../../props/components/layout.prop.js";
|
|
2
|
+
export type { PageContainerProp, PageContainerProp as PageContainerProps, PageContainerExtraProp, } from "../../props/components/layout.prop.js";
|
|
3
3
|
export type { BreadcrumbItemProp, BreadcrumbItemProp as BreadcrumbItem, } from "../../props/vocabulary/navigation.prop.js";
|
|
4
4
|
export declare function PageContainerInset({ className, children, ...props }: PageInsetProp): import("react").JSX.Element;
|
|
5
5
|
declare function PageContainerRoot({ title, subtitle, status, extra, toolbar, footer, toolbarPad, footerPad, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, headerLoading, density, variant, preset, headerLayout, headerScale, measure, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { isValidElement, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { ChevronRight } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
@@ -33,6 +33,12 @@ function useFooterReveal(enabled) {
|
|
|
33
33
|
function PageContainerInset({ className, children, ...props }) {
|
|
34
34
|
return /* @__PURE__ */ jsx("div", { className: cn("ui-page-container-inset", className), ...props, children });
|
|
35
35
|
}
|
|
36
|
+
function resolvePageExtra(extra) {
|
|
37
|
+
if (extra === void 0 || extra === null || extra === false) return {};
|
|
38
|
+
const isSlotMap = typeof extra === "object" && !isValidElement(extra) && !Array.isArray(extra);
|
|
39
|
+
if (isSlotMap) return extra;
|
|
40
|
+
return { start: extra };
|
|
41
|
+
}
|
|
36
42
|
function footerPadStyle(footerPad) {
|
|
37
43
|
const style = padStyle(footerPad, void 0);
|
|
38
44
|
const end = style?.paddingInlineEnd ?? style?.padding;
|
|
@@ -68,6 +74,7 @@ function PageContainerRoot({
|
|
|
68
74
|
const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
|
|
69
75
|
const { headerRef, revealed } = useFooterReveal(reveal);
|
|
70
76
|
const { t } = useTranslation();
|
|
77
|
+
const { start: extraStart, end: extraEnd } = resolvePageExtra(extra);
|
|
71
78
|
return /* @__PURE__ */ jsxs(
|
|
72
79
|
"div",
|
|
73
80
|
{
|
|
@@ -149,7 +156,10 @@ function PageContainerRoot({
|
|
|
149
156
|
)
|
|
150
157
|
) : subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
|
|
151
158
|
] }),
|
|
152
|
-
|
|
159
|
+
(extraStart != null || extraEnd != null) && /* @__PURE__ */ jsxs("div", { className: "ui-page-header-extra", children: [
|
|
160
|
+
extraStart,
|
|
161
|
+
extraEnd
|
|
162
|
+
] })
|
|
153
163
|
] })
|
|
154
164
|
]
|
|
155
165
|
}
|
|
@@ -2,9 +2,11 @@ import * as React from "react";
|
|
|
2
2
|
import { type ToggleCountFields, type ToggleProp } from "./toggle.js";
|
|
3
3
|
type ToggleGroupVariant = ToggleProp["variant"];
|
|
4
4
|
type ToggleGroupSize = ToggleProp["size"];
|
|
5
|
+
type ToggleGroupShape = ToggleProp["shape"];
|
|
5
6
|
type ToggleGroupBaseProp = Omit<React.ComponentPropsWithoutRef<"div">, "defaultValue" | "dir"> & {
|
|
6
7
|
variant?: ToggleGroupVariant;
|
|
7
8
|
size?: ToggleGroupSize;
|
|
9
|
+
shape?: ToggleGroupShape;
|
|
8
10
|
/** Disable every item at once — RAC `isDisabled`. */
|
|
9
11
|
disabled?: boolean;
|
|
10
12
|
/** Which way the arrow keys walk the group. */
|
|
@@ -51,6 +53,7 @@ export type ToggleGroupItemProp = Omit<React.ComponentPropsWithoutRef<"button">,
|
|
|
51
53
|
value: string;
|
|
52
54
|
variant?: ToggleGroupVariant;
|
|
53
55
|
size?: ToggleGroupSize;
|
|
56
|
+
shape?: ToggleGroupShape;
|
|
54
57
|
};
|
|
55
58
|
export type ToggleGroupItemProps = ToggleGroupItemProp;
|
|
56
59
|
export declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & ToggleCountFields & {
|
|
@@ -61,5 +64,6 @@ export declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<Omit<
|
|
|
61
64
|
value: string;
|
|
62
65
|
variant?: ToggleGroupVariant;
|
|
63
66
|
size?: ToggleGroupSize;
|
|
67
|
+
shape?: ToggleGroupShape;
|
|
64
68
|
} & React.RefAttributes<HTMLButtonElement>>;
|
|
65
69
|
export {};
|
|
@@ -31,6 +31,7 @@ const ToggleGroup = React.forwardRef(
|
|
|
31
31
|
className,
|
|
32
32
|
variant,
|
|
33
33
|
size,
|
|
34
|
+
shape,
|
|
34
35
|
children,
|
|
35
36
|
type,
|
|
36
37
|
value,
|
|
@@ -40,7 +41,7 @@ const ToggleGroup = React.forwardRef(
|
|
|
40
41
|
loop: _loop,
|
|
41
42
|
...props
|
|
42
43
|
}, ref) => {
|
|
43
|
-
const context = React.useMemo(() => ({ variant, size }), [variant, size]);
|
|
44
|
+
const context = React.useMemo(() => ({ variant, size, shape }), [variant, size, shape]);
|
|
44
45
|
const selectedKeys = React.useMemo(() => toSelectedKeys(value), [value]);
|
|
45
46
|
const defaultSelectedKeys = React.useMemo(() => toSelectedKeys(defaultValue), [defaultValue]);
|
|
46
47
|
const handleSelectionChange = (keys) => {
|
|
@@ -58,6 +59,7 @@ const ToggleGroup = React.forwardRef(
|
|
|
58
59
|
"data-slot": "toggle-group",
|
|
59
60
|
"data-variant": variant,
|
|
60
61
|
"data-size": size,
|
|
62
|
+
"data-shape": shape,
|
|
61
63
|
className: cn("ui-toggle-group", className),
|
|
62
64
|
...props,
|
|
63
65
|
selectionMode: type,
|
|
@@ -77,6 +79,7 @@ const ToggleGroupItem = React.forwardRef(
|
|
|
77
79
|
className,
|
|
78
80
|
variant,
|
|
79
81
|
size,
|
|
82
|
+
shape,
|
|
80
83
|
count,
|
|
81
84
|
overflowCount,
|
|
82
85
|
showZero,
|
|
@@ -90,6 +93,7 @@ const ToggleGroupItem = React.forwardRef(
|
|
|
90
93
|
const context = React.useContext(ToggleGroupContext);
|
|
91
94
|
const resolvedVariant = variant ?? context.variant;
|
|
92
95
|
const resolvedSize = size ?? context.size;
|
|
96
|
+
const resolvedShape = shape ?? context.shape;
|
|
93
97
|
const { pill, resolvedAriaLabel } = useCounterPill({
|
|
94
98
|
count,
|
|
95
99
|
overflowCount,
|
|
@@ -107,8 +111,12 @@ const ToggleGroupItem = React.forwardRef(
|
|
|
107
111
|
"data-state": isPressed ? "on" : "off",
|
|
108
112
|
"data-variant": resolvedVariant,
|
|
109
113
|
"data-size": resolvedSize,
|
|
114
|
+
"data-shape": resolvedShape,
|
|
110
115
|
"aria-label": resolvedAriaLabel,
|
|
111
|
-
className: cn(
|
|
116
|
+
className: cn(
|
|
117
|
+
toggleVariants({ variant: resolvedVariant, size: resolvedSize, shape: resolvedShape }),
|
|
118
|
+
className
|
|
119
|
+
),
|
|
112
120
|
...props,
|
|
113
121
|
id: value,
|
|
114
122
|
isDisabled: disabled,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const toggleVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "outline" | null | undefined;
|
|
4
|
+
variant?: "default" | "outline" | "soft" | null | undefined;
|
|
5
5
|
size?: "lg" | "md" | "sm" | "xs" | null | undefined;
|
|
6
|
+
shape?: "default" | "pill" | "sharp" | null | undefined;
|
|
6
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
8
|
/**
|
|
8
9
|
* Not exported on its own — it is folded into `ToggleProp` and `ToggleGroupItemProp` so there is
|
|
@@ -68,8 +69,9 @@ declare function restoreDomProps<P extends {
|
|
|
68
69
|
export type ToggleProp = React.ComponentPropsWithoutRef<"button"> & TogglePressedFields & VariantProps<typeof toggleVariants> & ToggleCountFields;
|
|
69
70
|
export type ToggleProps = ToggleProp;
|
|
70
71
|
export declare const Toggle: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & TogglePressedFields & VariantProps<(props?: ({
|
|
71
|
-
variant?: "default" | "outline" | null | undefined;
|
|
72
|
+
variant?: "default" | "outline" | "soft" | null | undefined;
|
|
72
73
|
size?: "lg" | "md" | "sm" | "xs" | null | undefined;
|
|
74
|
+
shape?: "default" | "pill" | "sharp" | null | undefined;
|
|
73
75
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & ToggleCountFields & React.RefAttributes<HTMLButtonElement>>;
|
|
74
76
|
export { toggleVariants, useCounterPill, restoreDomProps };
|
|
75
77
|
export type { ToggleCountFields, TogglePressedFields };
|
|
@@ -10,7 +10,15 @@ const toggleVariants = cva("ui-toggle", {
|
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
12
|
default: "ui-toggle-default",
|
|
13
|
-
outline: "ui-toggle-outline"
|
|
13
|
+
outline: "ui-toggle-outline",
|
|
14
|
+
// The REST FILL (gh#734). `default` is a 1px TRANSPARENT border and `outline` a hairline on
|
|
15
|
+
// `--background`, so neither draws a chip that reads as filled before it is pressed — which
|
|
16
|
+
// is why a consumer building a tag-filter panel reported its tags "look transparent" and
|
|
17
|
+
// fell back to `Button variant="secondary" shape="pill" aria-pressed`, a control that paints
|
|
18
|
+
// no pressed state at all. `soft` is antd `Tag`'s resting surface, on the SAME
|
|
19
|
+
// `hsl(var(--secondary))` fill `Badge variant="secondary"` and `Button variant="secondary"`
|
|
20
|
+
// already paint, so a chip, a badge and a button on one row are one family.
|
|
21
|
+
soft: "ui-toggle-soft"
|
|
14
22
|
},
|
|
15
23
|
size: {
|
|
16
24
|
// The fourth step of the shared control ladder (gh#716): 24px, `--control-height-xs`, the
|
|
@@ -20,11 +28,21 @@ const toggleVariants = cva("ui-toggle", {
|
|
|
20
28
|
sm: "ui-toggle-sm",
|
|
21
29
|
md: "ui-toggle-default-size",
|
|
22
30
|
lg: "ui-toggle-lg"
|
|
31
|
+
},
|
|
32
|
+
// Corner shape at parity with `Button` and `Badge` — the same three values mapped to the same
|
|
33
|
+
// two radius tokens, so `shape="pill"` means one thing across the library. `default` is empty
|
|
34
|
+
// on purpose: it leaves `.ui-toggle`'s own `--radius-md` in place instead of restating it,
|
|
35
|
+
// exactly as `badgeVariants` does.
|
|
36
|
+
shape: {
|
|
37
|
+
default: "",
|
|
38
|
+
pill: "rounded-[var(--radius-pill)]",
|
|
39
|
+
sharp: "rounded-[var(--radius-sharp)]"
|
|
23
40
|
}
|
|
24
41
|
},
|
|
25
42
|
defaultVariants: {
|
|
26
43
|
variant: "default",
|
|
27
|
-
size: "md"
|
|
44
|
+
size: "md",
|
|
45
|
+
shape: "default"
|
|
28
46
|
}
|
|
29
47
|
});
|
|
30
48
|
function useCounterPill({
|
|
@@ -82,6 +100,7 @@ const Toggle = React.forwardRef(
|
|
|
82
100
|
className,
|
|
83
101
|
variant,
|
|
84
102
|
size,
|
|
103
|
+
shape,
|
|
85
104
|
count,
|
|
86
105
|
overflowCount,
|
|
87
106
|
showZero,
|
|
@@ -113,7 +132,7 @@ const Toggle = React.forwardRef(
|
|
|
113
132
|
"data-slot": "toggle",
|
|
114
133
|
"data-state": isPressed ? "on" : "off",
|
|
115
134
|
"aria-label": resolvedAriaLabel,
|
|
116
|
-
className: cn(toggleVariants({ variant, size }), className),
|
|
135
|
+
className: cn(toggleVariants({ variant, size, shape }), className),
|
|
117
136
|
...props,
|
|
118
137
|
isSelected: isPressed,
|
|
119
138
|
onChange: onSelectionChange,
|
|
@@ -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": "27.
|
|
3
|
+
"version": "27.10.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,
|
|
@@ -1546,6 +1546,22 @@ export type UploadProp = FieldA11yProps & {
|
|
|
1546
1546
|
* @see Button — same variant scale
|
|
1547
1547
|
*/
|
|
1548
1548
|
triggerVariant?: "default" | "destructive" | "outline" | "dashed" | "secondary" | "ghost" | "link";
|
|
1549
|
+
/**
|
|
1550
|
+
* `variant="button"` only — the glyph COMPONENT the trigger draws, `triggerIcon={Plus}` rather
|
|
1551
|
+
* than `<Plus />`. Defaults to the upload arrow.
|
|
1552
|
+
*
|
|
1553
|
+
* It exists because the glyph was hard-coded and `triggerVariant` only moves emphasis, so a
|
|
1554
|
+
* "create new" action that HAPPENS to be an upload could not carry a plus and had to announce
|
|
1555
|
+
* itself as an upload (gh#734). The component is rendered onto the `<svg>` itself, at the same
|
|
1556
|
+
* `--upload-row-icon-size` the built-in glyph uses, so a swapped icon cannot change the
|
|
1557
|
+
* trigger's metrics.
|
|
1558
|
+
*
|
|
1559
|
+
* Typed as the widened glyph component (SVG props + ref) rather than the narrower `IconProp`,
|
|
1560
|
+
* for the reason `IconGlyphProp.as` already records: the library hands the glyph its class, its
|
|
1561
|
+
* `aria-hidden` and its label-spacing attribute, none of which `IconProp` can accept. Every
|
|
1562
|
+
* `lucide-react` icon and every `Icon`-compatible component qualifies.
|
|
1563
|
+
*/
|
|
1564
|
+
triggerIcon?: React.ComponentType<React.SVGProps<SVGSVGElement> & React.RefAttributes<SVGSVGElement>>;
|
|
1549
1565
|
className?: ClassNameProp;
|
|
1550
1566
|
children?: React.ReactNode;
|
|
1551
1567
|
};
|
|
@@ -269,6 +269,18 @@ export type ButtonProp = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
|
269
269
|
* `true` (a `0` pill shows); pass `false` to hide the pill at zero.
|
|
270
270
|
*/
|
|
271
271
|
showZero?: boolean;
|
|
272
|
+
/**
|
|
273
|
+
* Localized description of what the count MEANS, folded into the accessible name so the control
|
|
274
|
+
* never announces as a bare digit run. The SAME prop and the same contract `Toggle` has carried
|
|
275
|
+
* since gh#312 — without it a counted button measured `"Git3"` in the accessibility tree, not
|
|
276
|
+
* "Git, 3 pages" (gh#734).
|
|
277
|
+
*
|
|
278
|
+
* The pill itself is `aria-hidden` and the spoken form is an `sr-only` sibling, so the name
|
|
279
|
+
* reads `"<label>, <count> <countLabel>"` from the contents; with an explicit `aria-label`
|
|
280
|
+
* (an icon-only counted button) the same clause is folded into that instead. Pass a
|
|
281
|
+
* `t()`-resolved string — the library does not own this wording.
|
|
282
|
+
*/
|
|
283
|
+
countLabel?: string;
|
|
272
284
|
};
|
|
273
285
|
/**
|
|
274
286
|
* @see Reveal — entrance-motion primitive (staggered fade-up). Wraps content in a real element
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { AppLauncherApp, AppLauncherGroup, AppLauncherLabels, AppLauncherProp, AppShellProp, AuthFooterProp, AuthIdentityProp, OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, DragAxisProp, DragBoundsProp, DraggablePanelLabels, DraggablePanelPlacementProp, DraggablePanelPositionProp, DraggablePanelProp, PageContainerProp, PageInsetProp, SidebarItemProp, SidebarProductProp, SidebarProp, SidebarSectionProp, TopbarProp, } from "./layout.prop.js";
|
|
1
|
+
export type { AppLauncherApp, AppLauncherGroup, AppLauncherLabels, AppLauncherProp, AppShellProp, AuthFooterProp, AuthIdentityProp, OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, DragAxisProp, DragBoundsProp, DraggablePanelLabels, DraggablePanelPlacementProp, DraggablePanelPositionProp, DraggablePanelProp, PageContainerExtraProp, PageContainerProp, PageInsetProp, SidebarItemProp, SidebarProductProp, SidebarProp, SidebarSectionProp, TopbarProp, } from "./layout.prop.js";
|
|
2
2
|
export type { ButtonProp } from "./general.prop.js";
|
|
3
3
|
export type { CommandProp, InputProp, TextareaProp, FormFieldProp, SearchInputProp, CheckboxProp, CheckboxGroupProp, ChoiceOptionProp, RadioProp, RadioGroupProp, SwitchProp, SliderProp, CalendarProp, DatePickerProp, TimePickerProp, ColorPickerProp, UploadProp, UploadFileItemProp, UploadVariantProp, TreeOptionProp, TreeFieldNamesProp, CascaderProp, TreeSelectProp, ShowCheckedStrategyProp, TransferProp, TransferItemProp, } from "./data-entry.prop.js";
|
|
4
4
|
export type { AvatarProp, EmptyStateProp, DescriptionsProp, DescriptionsItemProp, BadgeProp, DataTableProp, QrCodeProp, } from "./data-display.prop.js";
|
|
@@ -22,6 +22,24 @@ export type PageContainerMeasureProp = "default" | "narrow" | "medium";
|
|
|
22
22
|
* `document` (default) — the row is the page's TITLE: a record, a form, a collection, a report.
|
|
23
23
|
*/
|
|
24
24
|
export type PageContainerHeaderScaleProp = "document" | "chrome";
|
|
25
|
+
/**
|
|
26
|
+
* @see PageContainer — the header's trailing slot, one node or two named sub-slots.
|
|
27
|
+
*
|
|
28
|
+
* The bare node is the shape every page already passes and stays the whole API for a single
|
|
29
|
+
* action cluster. `{ start, end }` splits that cluster in two so a page can put something AFTER
|
|
30
|
+
* the actions: the convention a consumer's four record screens share is identity → actions →
|
|
31
|
+
* pager LAST, and with one slot the pager had to live in a second header band inside the body,
|
|
32
|
+
* so the app carried two header shapes (gh#734).
|
|
33
|
+
*
|
|
34
|
+
* The shape is `TabsExtraProp`'s, deliberately — `Tabs.extra` already accepts exactly this union
|
|
35
|
+
* for exactly this reason, and a second spelling of one axis is what `check:prop-vocabulary`
|
|
36
|
+
* exists to prevent. Logical inline names (`start`/`end`, never `left`/`right`), so the slots
|
|
37
|
+
* swap sides under `dir="rtl"` with no second code path.
|
|
38
|
+
*/
|
|
39
|
+
export type PageContainerExtraProp = ExtraProp | {
|
|
40
|
+
start?: ReactNode;
|
|
41
|
+
end?: ReactNode;
|
|
42
|
+
};
|
|
25
43
|
/** @see PageContainer */
|
|
26
44
|
export type PageContainerProp = {
|
|
27
45
|
toolbarPad?: PadProp;
|
|
@@ -40,7 +58,8 @@ export type PageContainerProp = {
|
|
|
40
58
|
* violation) so the page's heading outline never disappears mid-load.
|
|
41
59
|
*/
|
|
42
60
|
headerLoading?: boolean;
|
|
43
|
-
|
|
61
|
+
/** @see PageContainerExtraProp */
|
|
62
|
+
extra?: PageContainerExtraProp;
|
|
44
63
|
/**
|
|
45
64
|
* FIXED chrome band between the page header and the scrolling body — a filter strip, a status
|
|
46
65
|
* bar, a "channel workflow" rail. It is a first-class page-chrome slot precisely because the
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -711,6 +711,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
711
711
|
readonly file: "components/layout.prop.ts";
|
|
712
712
|
readonly vocabulary: readonly [];
|
|
713
713
|
};
|
|
714
|
+
readonly PageContainerExtraProp: {
|
|
715
|
+
readonly group: "layout";
|
|
716
|
+
readonly file: "components/layout.prop.ts";
|
|
717
|
+
readonly vocabulary: readonly ["ExtraProp"];
|
|
718
|
+
};
|
|
714
719
|
readonly PageContainerMeasureProp: {
|
|
715
720
|
readonly group: "layout";
|
|
716
721
|
readonly file: "components/layout.prop.ts";
|
|
@@ -2449,7 +2454,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2449
2454
|
readonly ToggleProp: {
|
|
2450
2455
|
readonly group: "data-entry";
|
|
2451
2456
|
readonly file: "components/ui/toggle.tsx";
|
|
2452
|
-
readonly vocabulary: readonly ["SizeProp", "ClassNameProp", {
|
|
2457
|
+
readonly vocabulary: readonly ["SizeProp", "ShapeProp", "ClassNameProp", {
|
|
2453
2458
|
readonly field: "count";
|
|
2454
2459
|
readonly local: true;
|
|
2455
2460
|
readonly reason: "Counter-pill vocabulary shared VERBATIM with ButtonProp (count/overflowCount/showZero) so a counted filter tab and a counted pressed chip are one vocabulary — gh#312.";
|
|
@@ -2462,7 +2467,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2462
2467
|
readonly ToggleGroupItemProp: {
|
|
2463
2468
|
readonly group: "data-entry";
|
|
2464
2469
|
readonly file: "components/ui/toggle-group.tsx";
|
|
2465
|
-
readonly vocabulary: readonly ["SizeProp", "ClassNameProp", {
|
|
2470
|
+
readonly vocabulary: readonly ["SizeProp", "ShapeProp", "ClassNameProp", {
|
|
2466
2471
|
readonly field: "count";
|
|
2467
2472
|
readonly local: true;
|
|
2468
2473
|
readonly reason: "Per-item counter pill — variant/size come from group context, but the number is per-item data (gh#312).";
|
package/dist/props/registry.js
CHANGED
|
@@ -722,6 +722,11 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
722
722
|
file: "components/layout.prop.ts",
|
|
723
723
|
vocabulary: []
|
|
724
724
|
},
|
|
725
|
+
PageContainerExtraProp: {
|
|
726
|
+
group: "layout",
|
|
727
|
+
file: "components/layout.prop.ts",
|
|
728
|
+
vocabulary: ["ExtraProp"]
|
|
729
|
+
},
|
|
725
730
|
PageContainerMeasureProp: {
|
|
726
731
|
group: "layout",
|
|
727
732
|
file: "components/layout.prop.ts",
|
|
@@ -2842,6 +2847,9 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
2842
2847
|
file: "components/ui/toggle.tsx",
|
|
2843
2848
|
vocabulary: [
|
|
2844
2849
|
"SizeProp",
|
|
2850
|
+
// The control `ShapeProp` verbatim — same three values, same two radius tokens as Button and
|
|
2851
|
+
// Badge, so `shape="pill"` means one thing library-wide (gh#734).
|
|
2852
|
+
"ShapeProp",
|
|
2845
2853
|
"ClassNameProp",
|
|
2846
2854
|
{
|
|
2847
2855
|
field: "count",
|
|
@@ -2860,6 +2868,8 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
2860
2868
|
file: "components/ui/toggle-group.tsx",
|
|
2861
2869
|
vocabulary: [
|
|
2862
2870
|
"SizeProp",
|
|
2871
|
+
// Reaches the item through the group's context, exactly as `variant`/`size` do (gh#734).
|
|
2872
|
+
"ShapeProp",
|
|
2863
2873
|
"ClassNameProp",
|
|
2864
2874
|
{
|
|
2865
2875
|
field: "count",
|
package/dist/styles/control.css
CHANGED
|
@@ -26,6 +26,12 @@
|
|
|
26
26
|
background: hsl(var(--background));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
.ui-toggle-soft {
|
|
30
|
+
border: 1px solid transparent;
|
|
31
|
+
background: var(--toggle-soft-background, hsl(var(--secondary)));
|
|
32
|
+
color: var(--toggle-soft-color, hsl(var(--secondary-foreground)));
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
.ui-toggle-default-size {
|
|
30
36
|
min-height: var(--control-height);
|
|
31
37
|
padding-inline: var(--space-3);
|
|
@@ -53,6 +59,11 @@
|
|
|
53
59
|
color: hsl(var(--accent-foreground));
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
.ui-toggle-soft:hover {
|
|
63
|
+
background: var(--toggle-soft-hover-background, hsl(var(--secondary-hover)));
|
|
64
|
+
color: var(--toggle-soft-color, hsl(var(--secondary-foreground)));
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
.ui-toggle[data-state="on"] {
|
|
57
68
|
background: var(--toggle-on-background, hsl(var(--primary)));
|
|
58
69
|
color: hsl(var(--primary-foreground));
|
package/dist/styles/toggle.css
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
font-variant-numeric: tabular-nums;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
.ui-toggle-soft .ui-toggle-count {
|
|
20
|
+
background: var(--toggle-soft-count-background, hsl(var(--background)));
|
|
21
|
+
color: var(--toggle-soft-count-color, hsl(var(--foreground)));
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
.ui-toggle[data-state="on"] {
|
|
20
25
|
border-color: var(--toggle-pressed-border-color, hsl(var(--primary)));
|
|
21
26
|
}
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
--toggle-count-background: initial;
|
|
12
12
|
--toggle-count-color: initial;
|
|
13
13
|
|
|
14
|
+
--toggle-soft-background: initial;
|
|
15
|
+
--toggle-soft-color: initial;
|
|
16
|
+
--toggle-soft-hover-background: initial;
|
|
17
|
+
|
|
18
|
+
--toggle-soft-count-background: initial;
|
|
19
|
+
--toggle-soft-count-color: initial;
|
|
20
|
+
|
|
14
21
|
--toggle-pressed-count-background: initial;
|
|
15
22
|
--toggle-pressed-count-color: initial;
|
|
16
23
|
|
|
@@ -72,6 +72,14 @@ const TICKETS: Ticket[] = [
|
|
|
72
72
|
},
|
|
73
73
|
];
|
|
74
74
|
|
|
75
|
+
const TAGS = [
|
|
76
|
+
{ value: "design", label: "設計", count: 12 },
|
|
77
|
+
{ value: "runbook", label: "運用手順", count: 148 },
|
|
78
|
+
{ value: "onboarding", label: "入社手続き", count: 7 },
|
|
79
|
+
{ value: "security", label: "セキュリティ", count: 31 },
|
|
80
|
+
{ value: "archive", label: "アーカイブ", count: 1240 },
|
|
81
|
+
];
|
|
82
|
+
|
|
75
83
|
const STATUS_LABEL: Record<Ticket["status"], string> = {
|
|
76
84
|
open: "未対応",
|
|
77
85
|
waiting: "保留中",
|
|
@@ -93,6 +101,8 @@ const COLUMNS = [
|
|
|
93
101
|
];
|
|
94
102
|
|
|
95
103
|
export default function Demo() {
|
|
104
|
+
// The tag panel of a Wiki/document screen — the chip this vocabulary exists for.
|
|
105
|
+
const [tags, setTags] = useState<string[]>(["design"]);
|
|
96
106
|
// Faceted filter chips — pressed = the facet is applied. Counts are the facet sizes.
|
|
97
107
|
const [facets, setFacets] = useState<string[]>(["open"]);
|
|
98
108
|
// A reaction row: the count is how many people reacted, the pressed state is whether YOU did.
|
|
@@ -112,6 +122,47 @@ export default function Demo() {
|
|
|
112
122
|
subtitle="カウント付きの押下チップ · faceted filter · リアクション"
|
|
113
123
|
>
|
|
114
124
|
<Flex direction="col" gap="lg">
|
|
125
|
+
<Card>
|
|
126
|
+
<CardHeader>
|
|
127
|
+
<CardTitle level={2}>タグで絞り込む · soft + pill のチップ</CardTitle>
|
|
128
|
+
<CardDescription>
|
|
129
|
+
Wiki / ドキュメントのタグパネル。variant="soft" が休止時の面(--secondary、
|
|
130
|
+
Badge variant="secondary" と同じ塗り)、shape="pill" が角、count
|
|
131
|
+
が件数、押下が選択。variant / size / shape はグループに一度だけ書けば context
|
|
132
|
+
で全アイテムに届く。default や outline
|
|
133
|
+
だとチップは押されるまで透明に見えるので、チップ列にはこの組み合わせを使う。
|
|
134
|
+
</CardDescription>
|
|
135
|
+
</CardHeader>
|
|
136
|
+
<CardContent>
|
|
137
|
+
<Flex direction="col" gap="md">
|
|
138
|
+
<ToggleGroup
|
|
139
|
+
type="multiple"
|
|
140
|
+
variant="soft"
|
|
141
|
+
shape="pill"
|
|
142
|
+
size="xs"
|
|
143
|
+
value={tags}
|
|
144
|
+
onValueChange={setTags}
|
|
145
|
+
aria-label="タグで絞り込み"
|
|
146
|
+
>
|
|
147
|
+
{TAGS.map((tag) => (
|
|
148
|
+
<ToggleGroupItem
|
|
149
|
+
key={tag.value}
|
|
150
|
+
value={tag.value}
|
|
151
|
+
count={tag.count}
|
|
152
|
+
countLabel="ページ"
|
|
153
|
+
>
|
|
154
|
+
{tag.label}
|
|
155
|
+
</ToggleGroupItem>
|
|
156
|
+
))}
|
|
157
|
+
</ToggleGroup>
|
|
158
|
+
<Text tone="muted" size="sm">
|
|
159
|
+
読み上げは「設計, 12 ページ」。数字はピル側が aria-hidden で、単位つきの一文が
|
|
160
|
+
sr-only で並ぶので、ラベルと数字がくっついて「設計12」になることはない。
|
|
161
|
+
</Text>
|
|
162
|
+
</Flex>
|
|
163
|
+
</CardContent>
|
|
164
|
+
</Card>
|
|
165
|
+
|
|
115
166
|
<Card>
|
|
116
167
|
<CardHeader>
|
|
117
168
|
<CardTitle level={2}>問い合わせ受信箱</CardTitle>
|
|
@@ -95,10 +95,11 @@ export default function Demo() {
|
|
|
95
95
|
|
|
96
96
|
<Card>
|
|
97
97
|
<CardHeader>
|
|
98
|
-
<CardTitle level={2}>variant · default / outline</CardTitle>
|
|
98
|
+
<CardTitle level={2}>variant · default / outline / soft</CardTitle>
|
|
99
99
|
<CardDescription>
|
|
100
100
|
variant は ToggleGroup にだけ指定すれば全アイテムへ伝播する(各 ToggleGroupItem
|
|
101
|
-
への繰り返し指定は不要)。default は選択時に背景が塗られ、outline は枠線で示す。
|
|
101
|
+
への繰り返し指定は不要)。default は選択時に背景が塗られ、outline は枠線で示す。soft
|
|
102
|
+
は休止時から --secondary で塗られるので、チップの列に使う。
|
|
102
103
|
</CardDescription>
|
|
103
104
|
</CardHeader>
|
|
104
105
|
<CardContent>
|
|
@@ -123,6 +124,43 @@ export default function Demo() {
|
|
|
123
124
|
<ToggleGroupItem value="xlsx">Excel</ToggleGroupItem>
|
|
124
125
|
</ToggleGroup>
|
|
125
126
|
</Flex>
|
|
127
|
+
<Flex direction="col" gap="sm">
|
|
128
|
+
<Text as="p" size="sm" tone="muted">
|
|
129
|
+
variant="soft"
|
|
130
|
+
</Text>
|
|
131
|
+
<ToggleGroup type="single" variant="soft" defaultValue="pdf">
|
|
132
|
+
<ToggleGroupItem value="pdf">PDF</ToggleGroupItem>
|
|
133
|
+
<ToggleGroupItem value="csv">CSV</ToggleGroupItem>
|
|
134
|
+
<ToggleGroupItem value="xlsx">Excel</ToggleGroupItem>
|
|
135
|
+
</ToggleGroup>
|
|
136
|
+
</Flex>
|
|
137
|
+
</Flex>
|
|
138
|
+
</CardContent>
|
|
139
|
+
</Card>
|
|
140
|
+
|
|
141
|
+
<Card>
|
|
142
|
+
<CardHeader>
|
|
143
|
+
<CardTitle level={2}>shape · default / pill / sharp</CardTitle>
|
|
144
|
+
<CardDescription>
|
|
145
|
+
shape も variant / size と同じく行の決定なので、ToggleGroup
|
|
146
|
+
に一度書けば全アイテムへ伝播する。値は Button / Badge と同じ三つで、読む radius
|
|
147
|
+
トークンも同じ。チップの列は soft + pill。
|
|
148
|
+
</CardDescription>
|
|
149
|
+
</CardHeader>
|
|
150
|
+
<CardContent>
|
|
151
|
+
<Flex direction="col" gap="md">
|
|
152
|
+
{(["default", "pill", "sharp"] as const).map((shape) => (
|
|
153
|
+
<Flex key={shape} direction="col" gap="sm">
|
|
154
|
+
<Text as="p" size="sm" tone="muted">
|
|
155
|
+
shape="{shape}"
|
|
156
|
+
</Text>
|
|
157
|
+
<ToggleGroup type="single" variant="soft" shape={shape} defaultValue="pdf">
|
|
158
|
+
<ToggleGroupItem value="pdf">PDF</ToggleGroupItem>
|
|
159
|
+
<ToggleGroupItem value="csv">CSV</ToggleGroupItem>
|
|
160
|
+
<ToggleGroupItem value="xlsx">Excel</ToggleGroupItem>
|
|
161
|
+
</ToggleGroup>
|
|
162
|
+
</Flex>
|
|
163
|
+
))}
|
|
126
164
|
</Flex>
|
|
127
165
|
</CardContent>
|
|
128
166
|
</Card>
|
|
@@ -46,11 +46,14 @@ export default function Demo() {
|
|
|
46
46
|
<CardHeader>
|
|
47
47
|
<CardTitle level={2}>Variants</CardTitle>
|
|
48
48
|
<CardDescription>
|
|
49
|
-
default
|
|
49
|
+
default(押下時だけ面が出る・休止時は透明な 1px 枠)· outline(--background
|
|
50
|
+
の上の細い枠)· soft(休止時から --secondary で塗られる)。
|
|
51
|
+
最初の二つはツールバー向けで、休止しているチップは「何もない」ように見える。
|
|
52
|
+
チップとして並べるときは soft。
|
|
50
53
|
</CardDescription>
|
|
51
54
|
</CardHeader>
|
|
52
55
|
<CardContent>
|
|
53
|
-
<Flex direction="row" gap="md">
|
|
56
|
+
<Flex direction="row" gap="md" wrap>
|
|
54
57
|
<Toggle
|
|
55
58
|
variant="default"
|
|
56
59
|
pressed={pinned}
|
|
@@ -67,6 +70,55 @@ export default function Demo() {
|
|
|
67
70
|
>
|
|
68
71
|
消費税対象のみ
|
|
69
72
|
</Toggle>
|
|
73
|
+
<Toggle variant="soft" aria-label="草案のみ">
|
|
74
|
+
草案のみ
|
|
75
|
+
</Toggle>
|
|
76
|
+
</Flex>
|
|
77
|
+
</CardContent>
|
|
78
|
+
</Card>
|
|
79
|
+
|
|
80
|
+
<Card>
|
|
81
|
+
<CardHeader>
|
|
82
|
+
<CardTitle level={2}>
|
|
83
|
+
soft + shape="pill" · チェック可能なタグチップ
|
|
84
|
+
</CardTitle>
|
|
85
|
+
<CardDescription>
|
|
86
|
+
antd の Tag.CheckableTag にあたる形。soft が休止時の面を、shape が角を担う。shape は
|
|
87
|
+
Button / Badge と同じ default / pill / sharp の三値で、同じ radius
|
|
88
|
+
トークンを読む。押下すると面が --primary に反転するので、状態は色だけに依存しない。
|
|
89
|
+
取り消せる(×つきの)タグは選択ではなく削除なので Badge の onRemove
|
|
90
|
+
側、つまり別のコンポーネントになる。
|
|
91
|
+
</CardDescription>
|
|
92
|
+
</CardHeader>
|
|
93
|
+
<CardContent>
|
|
94
|
+
<Flex direction="col" gap="md">
|
|
95
|
+
<Flex direction="row" gap="sm" wrap align="center">
|
|
96
|
+
<Toggle variant="soft" shape="pill" size="xs" defaultPressed>
|
|
97
|
+
設計
|
|
98
|
+
</Toggle>
|
|
99
|
+
<Toggle variant="soft" shape="pill" size="xs">
|
|
100
|
+
実装
|
|
101
|
+
</Toggle>
|
|
102
|
+
<Toggle variant="soft" shape="pill" size="xs">
|
|
103
|
+
検証
|
|
104
|
+
</Toggle>
|
|
105
|
+
</Flex>
|
|
106
|
+
<Flex direction="row" gap="sm" wrap align="center">
|
|
107
|
+
<Toggle variant="soft" shape="default">
|
|
108
|
+
default(--radius-md)
|
|
109
|
+
</Toggle>
|
|
110
|
+
<Toggle variant="soft" shape="pill">
|
|
111
|
+
pill
|
|
112
|
+
</Toggle>
|
|
113
|
+
<Toggle variant="soft" shape="sharp">
|
|
114
|
+
sharp
|
|
115
|
+
</Toggle>
|
|
116
|
+
</Flex>
|
|
117
|
+
<Text as="p" size="xs" tone="muted">
|
|
118
|
+
soft の面は Badge variant="secondary" / Button
|
|
119
|
+
variant="secondary" と同じ --secondary。ホバーは不透明な --secondary-hover
|
|
120
|
+
なので、ページの上でもカードの上でも同じ色になる。
|
|
121
|
+
</Text>
|
|
70
122
|
</Flex>
|
|
71
123
|
</CardContent>
|
|
72
124
|
</Card>
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@godxjp/ui/data-entry";
|
|
10
10
|
import { Button, Text } from "@godxjp/ui/general";
|
|
11
11
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
12
|
+
import { FilePlus2, Plus } from "lucide-react";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Upload — drag-and-drop / button / avatar / picture file uploader (6 variants).
|
|
@@ -32,6 +33,7 @@ export default function Demo() {
|
|
|
32
33
|
const [submittedFiles, setSubmittedFiles] = useState<string[]>([]);
|
|
33
34
|
const [dropzoneItems, setDropzoneItems] = useState<UploadFileItem[]>([]);
|
|
34
35
|
const [buttonItems, setButtonItems] = useState<UploadFileItem[]>([]);
|
|
36
|
+
const [triggerIconItems, setTriggerIconItems] = useState<UploadFileItem[]>([]);
|
|
35
37
|
const [pictureCardItems, setPictureCardItems] = useState<UploadFileItem[]>([]);
|
|
36
38
|
const [pictureItem, setPictureItem] = useState<UploadFileItem[]>([]);
|
|
37
39
|
const [avatarItem, setAvatarItem] = useState<UploadFileItem[]>([]);
|
|
@@ -187,6 +189,48 @@ export default function Demo() {
|
|
|
187
189
|
</CardContent>
|
|
188
190
|
</Card>
|
|
189
191
|
|
|
192
|
+
<Card>
|
|
193
|
+
<CardHeader>
|
|
194
|
+
<CardTitle level={2}>triggerIcon · 「新規作成」がアップロードのとき</CardTitle>
|
|
195
|
+
<CardDescription>
|
|
196
|
+
variant="button"
|
|
197
|
+
のトリガーが描くグリフを差し替える。コンポーネントを渡す(triggerIcon={Plus}、
|
|
198
|
+
要素ではない)。triggerVariant
|
|
199
|
+
は強弱しか変えられないので、実体はアップロードでも「新規作成」として並ぶボタンは
|
|
200
|
+
これまで矢印を背負うしかなかった。クラスと余白と aria-hidden
|
|
201
|
+
はライブラリ側が持ったままなので、差し替えても大きさは --upload-row-icon-size
|
|
202
|
+
のまま、読み上げにも出ない。
|
|
203
|
+
</CardDescription>
|
|
204
|
+
</CardHeader>
|
|
205
|
+
<CardContent>
|
|
206
|
+
<Flex direction="row" gap="md" wrap align="center">
|
|
207
|
+
<Upload
|
|
208
|
+
variant="button"
|
|
209
|
+
triggerIcon={Plus}
|
|
210
|
+
triggerVariant="default"
|
|
211
|
+
value={triggerIconItems}
|
|
212
|
+
onValueChange={setTriggerIconItems}
|
|
213
|
+
accept=".pdf"
|
|
214
|
+
onUpload={demoUpload}
|
|
215
|
+
>
|
|
216
|
+
資料を追加
|
|
217
|
+
</Upload>
|
|
218
|
+
<Upload
|
|
219
|
+
variant="button"
|
|
220
|
+
triggerIcon={FilePlus2}
|
|
221
|
+
triggerSize="icon-sm"
|
|
222
|
+
triggerVariant="ghost"
|
|
223
|
+
value={[]}
|
|
224
|
+
onValueChange={() => {}}
|
|
225
|
+
aria-label="請求書を添付"
|
|
226
|
+
/>
|
|
227
|
+
<Upload variant="button" value={[]} onValueChange={() => {}}>
|
|
228
|
+
既定のグリフ
|
|
229
|
+
</Upload>
|
|
230
|
+
</Flex>
|
|
231
|
+
</CardContent>
|
|
232
|
+
</Card>
|
|
233
|
+
|
|
190
234
|
<Card>
|
|
191
235
|
<CardHeader>
|
|
192
236
|
<CardTitle level={2}>Picture Card</CardTitle>
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "@godxjp/ui/data-display";
|
|
9
9
|
import { Button } from "@godxjp/ui/general";
|
|
10
10
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
11
|
-
import { Pencil, Plus, Trash2 } from "lucide-react";
|
|
11
|
+
import { Bell, Pencil, Plus, Trash2 } from "lucide-react";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Button — the core action primitive. Composed only from real @godxjp/ui
|
|
@@ -26,23 +26,35 @@ export default function Demo() {
|
|
|
26
26
|
<CardHeader>
|
|
27
27
|
<CardTitle level={2}>Count</CardTitle>
|
|
28
28
|
<CardDescription>
|
|
29
|
-
Trailing totals, overflow cap, and explicit zero visibility.
|
|
29
|
+
Trailing totals, overflow cap, and explicit zero visibility. countLabel
|
|
30
|
+
は数字が何を数えているかを読み上げに渡す(Toggle と同じ語彙)。ピルは aria-hidden
|
|
31
|
+
で、単位つきの一文が sr-only で並ぶので、名前は「保留中, 18 件」。countLabel
|
|
32
|
+
を渡さなくても「保留中, 18」までは分かれる。
|
|
30
33
|
</CardDescription>
|
|
31
34
|
</CardHeader>
|
|
32
35
|
<CardContent>
|
|
33
36
|
<Flex direction="row" wrap align="center" gap="md">
|
|
34
|
-
<Button variant="outline" count={18}>
|
|
37
|
+
<Button variant="outline" count={18} countLabel="件">
|
|
35
38
|
保留中
|
|
36
39
|
</Button>
|
|
37
|
-
<Button variant="outline" count={128} overflowCount={99}>
|
|
40
|
+
<Button variant="outline" count={128} overflowCount={99} countLabel="件">
|
|
38
41
|
受信トレイ
|
|
39
42
|
</Button>
|
|
40
|
-
<Button variant="ghost" count={0} showZero>
|
|
43
|
+
<Button variant="ghost" count={0} showZero countLabel="件">
|
|
41
44
|
完了
|
|
42
45
|
</Button>
|
|
43
|
-
<Button variant="ghost" count={0} showZero={false}>
|
|
46
|
+
<Button variant="ghost" count={0} showZero={false} countLabel="件">
|
|
44
47
|
非表示のゼロ
|
|
45
48
|
</Button>
|
|
49
|
+
<Button
|
|
50
|
+
variant="outline"
|
|
51
|
+
size="icon-sm"
|
|
52
|
+
aria-label="通知"
|
|
53
|
+
count={3}
|
|
54
|
+
countLabel="件の未読"
|
|
55
|
+
>
|
|
56
|
+
<Bell aria-hidden="true" />
|
|
57
|
+
</Button>
|
|
46
58
|
</Flex>
|
|
47
59
|
</CardContent>
|
|
48
60
|
</Card>
|
|
@@ -17,7 +17,7 @@ import type { ColumnDef } from "@godxjp/ui/data-display";
|
|
|
17
17
|
import { Button, Text } from "@godxjp/ui/general";
|
|
18
18
|
import { SearchInput } from "@godxjp/ui/data-entry";
|
|
19
19
|
import { ResponsiveGrid } from "@godxjp/ui/layout";
|
|
20
|
-
import { Toolbar, ToolbarGroup } from "@godxjp/ui/navigation";
|
|
20
|
+
import { Pagination, Toolbar, ToolbarGroup } from "@godxjp/ui/navigation";
|
|
21
21
|
import { Plus, Download, Filter, Search } from "lucide-react";
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -129,6 +129,9 @@ export default function Demo() {
|
|
|
129
129
|
{ key: "ghost", label: "ghost" },
|
|
130
130
|
] as const;
|
|
131
131
|
|
|
132
|
+
/** The record pager that must sit AFTER the header's action cluster (`extra.end`). */
|
|
133
|
+
const [recordPage, setRecordPage] = useState(12);
|
|
134
|
+
|
|
132
135
|
const densities = [
|
|
133
136
|
{ key: "compact", label: "compact" },
|
|
134
137
|
{ key: "default", label: "default" },
|
|
@@ -165,6 +168,69 @@ export default function Demo() {
|
|
|
165
168
|
</ResponsiveGrid>
|
|
166
169
|
</PageContainer>
|
|
167
170
|
|
|
171
|
+
{/* ── 1a. `extra` as { start, end } · 本人確認 → 操作 → ページャが最後 ── */}
|
|
172
|
+
{/* `extra` はノード 1 つでも、start / end の 2 スロットでも受ける(Tabs.extra と同じ形)。
|
|
173
|
+
レコード画面の並びは「誰の何か → 操作の束 → ページャ」で、ページャは操作の“あと”に
|
|
174
|
+
来る。1 スロットしかなかったころは、この最後の一つを本文の中に第二のヘッダ帯として
|
|
175
|
+
置くしかなく、アプリがヘッダを 2 種類抱えることになっていた。両方とも同じ
|
|
176
|
+
.ui-page-header-extra の直下に並ぶので、DOM 順がそのまま読み上げ順になる。 */}
|
|
177
|
+
<PageContainer
|
|
178
|
+
title="不具合 #1042 · 請求書の消費税区分が誤っている"
|
|
179
|
+
subtitle="Issue detail · 担当 佐藤 · 更新 2026-09-14"
|
|
180
|
+
breadcrumb={[
|
|
181
|
+
{ label: "ホーム", to: "/" },
|
|
182
|
+
{ label: "不具合", to: "/issues" },
|
|
183
|
+
{ label: "#1042" },
|
|
184
|
+
]}
|
|
185
|
+
breadcrumbLabel="不具合詳細のパンくず"
|
|
186
|
+
extra={{
|
|
187
|
+
start: (
|
|
188
|
+
<Flex gap="sm">
|
|
189
|
+
<Button variant="outline" size="sm">
|
|
190
|
+
<Download />
|
|
191
|
+
エクスポート
|
|
192
|
+
</Button>
|
|
193
|
+
<Button size="sm">
|
|
194
|
+
<Plus />
|
|
195
|
+
コメント
|
|
196
|
+
</Button>
|
|
197
|
+
</Flex>
|
|
198
|
+
),
|
|
199
|
+
end: (
|
|
200
|
+
<Pagination
|
|
201
|
+
size="sm"
|
|
202
|
+
value={recordPage}
|
|
203
|
+
onValueChange={setRecordPage}
|
|
204
|
+
total={48}
|
|
205
|
+
pageSize={1}
|
|
206
|
+
simple
|
|
207
|
+
ariaLabel="不具合の前後移動"
|
|
208
|
+
/>
|
|
209
|
+
),
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
<Card>
|
|
213
|
+
<CardHeader>
|
|
214
|
+
<CardTitle level={2}>ページャはヘッダの最後に置く</CardTitle>
|
|
215
|
+
<CardDescription>
|
|
216
|
+
extra にノードを 1 つ渡したときは start に入るので、既存のページは 1
|
|
217
|
+
文字も変わらない。end を足したときだけ、その後ろにもう 1
|
|
218
|
+
スロットが増える。名前は論理方向(start / end)なので dir="rtl"
|
|
219
|
+
でそのまま入れ替わる。本文の中にもう一段ヘッダ帯を作る必要はない。
|
|
220
|
+
</CardDescription>
|
|
221
|
+
</CardHeader>
|
|
222
|
+
<CardContent>
|
|
223
|
+
<Descriptions
|
|
224
|
+
items={[
|
|
225
|
+
{ label: "状態", value: "未対応" },
|
|
226
|
+
{ label: "優先度", value: "高" },
|
|
227
|
+
{ label: "報告者", value: "鈴木" },
|
|
228
|
+
]}
|
|
229
|
+
/>
|
|
230
|
+
</CardContent>
|
|
231
|
+
</Card>
|
|
232
|
+
</PageContainer>
|
|
233
|
+
|
|
168
234
|
{/* ── 1b. Canonical page-header contract · status/meta band ── */}
|
|
169
235
|
{/* PageContainer's embedded header IS the DXS PageHeader: breadcrumbs + title +
|
|
170
236
|
subtitle + status/meta + actions + responsive overflow on ONE renderer. The
|