@godxjp/ui 33.19.0 → 33.21.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 +4 -4
- package/agent/START-HERE.md +7 -7
- package/agent/components/AppSettingPicker.json +0 -1
- package/agent/components/AppShell.json +11 -1
- package/agent/components/CardShowcase.json +44 -0
- package/agent/components/CatalogCard.json +83 -0
- package/agent/components/CatalogDetail.json +76 -0
- package/agent/components/CatalogDetailSection.json +52 -0
- package/agent/components/CatalogDetailTag.json +27 -0
- package/agent/components/Descriptions.json +6 -0
- package/agent/components/EditorialArticle.json +53 -0
- package/agent/components/EditorialAuth.json +53 -0
- package/agent/components/EditorialAuthCard.json +52 -0
- package/agent/components/EditorialBanner.json +53 -0
- package/agent/components/EditorialBreadcrumb.json +38 -0
- package/agent/components/EditorialCard.json +70 -0
- package/agent/components/EditorialEmptyPanel.json +57 -0
- package/agent/components/EditorialFilterBar.json +51 -0
- package/agent/components/EditorialFilterField.json +39 -0
- package/agent/components/EditorialFilterPanel.json +46 -0
- package/agent/components/EditorialFooter.json +54 -0
- package/agent/components/EditorialFootnote.json +36 -0
- package/agent/components/EditorialHero.json +52 -0
- package/agent/components/EditorialInlineSearch.json +54 -0
- package/agent/components/EditorialMasthead.json +52 -0
- package/agent/components/EditorialMetaBar.json +37 -0
- package/agent/components/EditorialPageHeader.json +73 -0
- package/agent/components/EditorialPanel.json +48 -0
- package/agent/components/EditorialPillNav.json +41 -0
- package/agent/components/EditorialPrompt.json +54 -0
- package/agent/components/EditorialSearch.json +112 -0
- package/agent/components/EditorialSection.json +68 -0
- package/agent/components/EditorialSectionHead.json +62 -0
- package/agent/components/EditorialStats.json +32 -0
- package/agent/components/EditorialTextButton.json +47 -0
- package/agent/components/EditorialToggleGroup.json +52 -0
- package/agent/components/EditorialToolbar.json +45 -0
- package/agent/components/Eyebrow.json +39 -0
- package/agent/components/HoldingCard.json +69 -0
- package/agent/components/HoldingCardAction.json +51 -0
- package/agent/components/HoldingGrid.json +37 -0
- package/agent/components/NumberInput.json +5 -0
- package/agent/components/PriceSummary.json +32 -0
- package/agent/components/SettingsLayout.json +31 -0
- package/agent/components/SettingsPanel.json +53 -0
- package/agent/components/Tabs.json +2 -2
- package/agent/components/ThemePicker.json +54 -0
- package/agent/components/TopbarItem.json +5 -0
- package/agent/components-index.json +292 -98
- package/agent/components.json +2054 -4
- package/agent/index.json +7 -7
- package/agent/llms.txt +8 -8
- package/agent/tokens.json +5646 -126
- package/dist/components/data-display/badge.js +4 -2
- package/dist/components/data-display/descriptions.d.ts +5 -1
- package/dist/components/data-display/descriptions.js +21 -9
- package/dist/components/data-entry/form-field.js +2 -2
- package/dist/components/data-entry/number-input.d.ts +1 -0
- package/dist/components/data-entry/number-input.js +8 -0
- package/dist/components/data-entry/select.js +24 -2
- package/dist/components/data-entry/toggle.d.ts +2 -1
- package/dist/components/data-entry/toggle.js +12 -2
- package/dist/components/general/button.js +7 -0
- package/dist/components/general/typography.d.ts +1 -1
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +28 -2
- package/dist/components/layout/centered-shell.js +4 -0
- package/dist/components/layout/editorial-account.d.ts +51 -0
- package/dist/components/layout/editorial-account.js +466 -0
- package/dist/components/layout/editorial-catalog.d.ts +18 -0
- package/dist/components/layout/editorial-catalog.js +164 -0
- package/dist/components/layout/editorial.d.ts +56 -0
- package/dist/components/layout/editorial.js +867 -0
- package/dist/components/layout/index.d.ts +6 -0
- package/dist/components/layout/index.js +98 -0
- package/dist/components/layout/mobile-shell.d.ts +2 -0
- package/dist/components/layout/mobile-shell.js +18 -11
- package/dist/components/layout/responsive-grid.js +9 -1
- package/dist/components/layout/topbar-item.d.ts +1 -0
- package/dist/components/layout/topbar-item.js +2 -0
- package/dist/components/navigation/tabs.d.ts +1 -1
- package/dist/components/navigation/tabs.js +17 -5
- package/dist/contracts/measurement.json +1 -1
- package/dist/lib/toast-clearance.js +1 -1
- package/dist/props/components/data-display.prop.d.ts +3 -1
- package/dist/props/components/data-entry.prop.d.ts +9 -0
- package/dist/props/components/editorial-account.prop.d.ts +249 -0
- package/dist/props/components/editorial-account.prop.js +0 -0
- package/dist/props/components/general.prop.d.ts +6 -2
- package/dist/props/components/layout.prop.d.ts +602 -2
- package/dist/props/components/navigation.prop.d.ts +2 -0
- package/dist/props/registry.d.ts +1140 -8
- package/dist/props/registry.js +805 -1
- package/dist/styles/badge-layout.css +2 -0
- package/dist/styles/control.css +58 -12
- package/dist/styles/core.css +1 -0
- package/dist/styles/data-display-layout.css +39 -3
- package/dist/styles/editorial-account-layout.css +1130 -0
- package/dist/styles/focus-ring.css +33 -10
- package/dist/styles/form-layout.css +8 -1
- package/dist/styles/index.css +1 -0
- package/dist/styles/layers.json +248 -1
- package/dist/styles/layout.css +2022 -0
- package/dist/styles/navigation-layout.css +5 -0
- package/dist/styles/shell-layout.css +100 -2
- package/dist/styles/table-layout.css +25 -13
- package/dist/styles/text-layout.css +6 -0
- package/dist/styles.css +1 -1
- package/dist/tokens/base.css +2 -0
- package/dist/tokens/components/badge.css +3 -0
- package/dist/tokens/components/control.css +9 -0
- package/dist/tokens/components/descriptions.css +2 -0
- package/dist/tokens/components/editorial-account.css +393 -0
- package/dist/tokens/components/editorial.css +721 -0
- package/dist/tokens/components/form.css +4 -0
- package/dist/tokens/components/navigation.css +2 -0
- package/dist/tokens/components/shell.css +23 -0
- package/dist/tokens/components/table.css +1 -1
- package/docs/TOKENS.md +26 -0
- package/package.json +2 -2
- package/scripts/ui-audit.mjs +3 -2
|
@@ -62,8 +62,10 @@ const badgeVariants = cva(
|
|
|
62
62
|
variant: {
|
|
63
63
|
default: "border-transparent bg-primary text-primary-foreground",
|
|
64
64
|
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
|
65
|
-
outline
|
|
66
|
-
|
|
65
|
+
// gh#1439 — the outline / dashed ink is a role-mirror knob (default --foreground), so a
|
|
66
|
+
// brand whose plain chip reads muted sets it once instead of wrapping every label.
|
|
67
|
+
outline: "text-[color:var(--badge-outline-foreground,hsl(var(--foreground)))]",
|
|
68
|
+
dashed: "border-dashed text-[color:var(--badge-outline-foreground,hsl(var(--foreground)))]",
|
|
67
69
|
/* `color` mode. Deliberately empty and deliberately NOT in the public
|
|
68
70
|
* `BadgeVariant` union: every fill, border and text utility has to stay
|
|
69
71
|
* off the element so badge-layout.css's `[data-tinted]` rule is
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { WidthProp } from "../../props/vocabulary/shared.prop.js";
|
|
2
3
|
import type { DescriptionsLayoutProp } from "../../props/vocabulary/interaction.prop.js";
|
|
3
4
|
import type { DescriptionsColumnProp, DescriptionsItemsProp, DescriptionsSpanProp } from "../../props/vocabulary/data.prop.js";
|
|
4
5
|
export type { DescriptionsLayoutProp };
|
|
@@ -16,6 +17,9 @@ export interface DescriptionsProps {
|
|
|
16
17
|
* it there would read as a mistake, exactly like `Form`'s own contract.
|
|
17
18
|
*/
|
|
18
19
|
labelAlign?: "start" | "end";
|
|
20
|
+
/** Horizontal label width. Omitted/auto keeps the theme width; content shares the longest
|
|
21
|
+
* label track per visual column. CSS lengths (numbers are px) override the theme per list. */
|
|
22
|
+
labelWidth?: WidthProp | "content";
|
|
19
23
|
/**
|
|
20
24
|
* Draw the grid as a bordered table (antd `bordered`): an outer frame, a rule between every
|
|
21
25
|
* cell, and a shaded label cell. Colour and rhythm come from the `--descriptions-*` tokens.
|
|
@@ -29,7 +33,7 @@ export interface DescriptionsProps {
|
|
|
29
33
|
className?: string;
|
|
30
34
|
children?: React.ReactNode;
|
|
31
35
|
}
|
|
32
|
-
export declare function Descriptions({ columns, layout, labelAlign, bordered, items, className, children, }: DescriptionsProps): React.JSX.Element;
|
|
36
|
+
export declare function Descriptions({ columns, layout, labelAlign, labelWidth, bordered, items, className, children, }: DescriptionsProps): React.JSX.Element;
|
|
33
37
|
export declare namespace Descriptions {
|
|
34
38
|
export { DescriptionsItem as Item };
|
|
35
39
|
}
|
|
@@ -5,7 +5,8 @@ import { cn } from "../../lib/utils.js";
|
|
|
5
5
|
const DescriptionsLayoutContext = React.createContext({
|
|
6
6
|
layout: "vertical",
|
|
7
7
|
labelAlign: "start",
|
|
8
|
-
bordered: false
|
|
8
|
+
bordered: false,
|
|
9
|
+
contentLabels: false
|
|
9
10
|
});
|
|
10
11
|
const BREAKPOINT_LADDER = ["sm", "md", "lg", "xl"];
|
|
11
12
|
function responsiveVars(name, base, steps) {
|
|
@@ -25,6 +26,7 @@ function Descriptions({
|
|
|
25
26
|
columns = 2,
|
|
26
27
|
layout = "vertical",
|
|
27
28
|
labelAlign = "start",
|
|
29
|
+
labelWidth,
|
|
28
30
|
bordered = false,
|
|
29
31
|
items,
|
|
30
32
|
className,
|
|
@@ -32,21 +34,31 @@ function Descriptions({
|
|
|
32
34
|
}) {
|
|
33
35
|
const legacyColumns = typeof columns === "number" ? LEGACY_COLUMN_CLASS[columns] : void 0;
|
|
34
36
|
const responsiveColumns = legacyColumns === void 0 ? typeof columns === "number" ? { sm: Math.min(columns, 2), lg: columns } : columns : void 0;
|
|
37
|
+
const intrinsicWidth = labelWidth === "content" || labelWidth === "max-content" || labelWidth === "min-content" || labelWidth === "fit-content";
|
|
38
|
+
const contentLabels = layout === "horizontal" && intrinsicWidth;
|
|
39
|
+
const labelStyle = layout === "horizontal" && labelWidth !== void 0 && labelWidth !== "auto" && !intrinsicWidth ? {
|
|
40
|
+
"--descriptions-label-width": typeof labelWidth === "number" ? `${labelWidth}px` : labelWidth
|
|
41
|
+
} : {};
|
|
42
|
+
const contentColumns = contentLabels && !responsiveColumns ? typeof columns === "number" ? columns === 3 ? { sm: 2, lg: 3 } : columns === 2 ? { sm: 2 } : {} : columns : responsiveColumns;
|
|
35
43
|
const context = React.useMemo(
|
|
36
|
-
() => ({ layout, labelAlign, bordered }),
|
|
37
|
-
[layout, labelAlign, bordered]
|
|
44
|
+
() => ({ layout, labelAlign, bordered, contentLabels }),
|
|
45
|
+
[layout, labelAlign, bordered, contentLabels]
|
|
38
46
|
);
|
|
39
47
|
return /* @__PURE__ */ jsx(DescriptionsLayoutContext.Provider, { value: context, children: /* @__PURE__ */ jsxs(
|
|
40
48
|
"dl",
|
|
41
49
|
{
|
|
42
50
|
"data-slot": "descriptions",
|
|
43
51
|
"data-columns": typeof columns === "number" ? columns : void 0,
|
|
44
|
-
"data-columns-responsive": responsiveColumns ? "" : void 0,
|
|
52
|
+
"data-columns-responsive": responsiveColumns || contentLabels ? "" : void 0,
|
|
53
|
+
"data-label-width": contentLabels ? "content" : void 0,
|
|
45
54
|
"data-bordered": bordered ? "" : void 0,
|
|
46
|
-
style:
|
|
55
|
+
style: {
|
|
56
|
+
...contentColumns ? responsiveVars("--descriptions-column-count", 1, contentColumns) : {},
|
|
57
|
+
...labelStyle
|
|
58
|
+
},
|
|
47
59
|
className: cn(
|
|
48
60
|
"grid gap-x-[var(--descriptions-column-gap)] gap-y-[var(--descriptions-row-gap)]",
|
|
49
|
-
legacyColumns,
|
|
61
|
+
!contentLabels && legacyColumns,
|
|
50
62
|
className
|
|
51
63
|
),
|
|
52
64
|
children: [
|
|
@@ -71,9 +83,9 @@ const LEGACY_SPAN_CLASS = {
|
|
|
71
83
|
3: "sm:col-span-2 lg:col-span-3"
|
|
72
84
|
};
|
|
73
85
|
function DescriptionsItem({ label, mono, span, className, children }) {
|
|
74
|
-
const { layout, labelAlign } = React.useContext(DescriptionsLayoutContext);
|
|
75
|
-
const legacySpan = typeof span === "number" ? LEGACY_SPAN_CLASS[span] : void 0;
|
|
76
|
-
const responsiveSpan = span !== void 0 && span !== "filled" && legacySpan === void 0 ? typeof span === "number" ? { sm: span } : span : void 0;
|
|
86
|
+
const { layout, labelAlign, contentLabels } = React.useContext(DescriptionsLayoutContext);
|
|
87
|
+
const legacySpan = !contentLabels && typeof span === "number" ? LEGACY_SPAN_CLASS[span] : void 0;
|
|
88
|
+
const responsiveSpan = span !== void 0 && span !== "filled" && legacySpan === void 0 ? typeof span === "number" ? span === 3 ? { sm: 2, lg: 3 } : { sm: span } : span : void 0;
|
|
77
89
|
const endAlignedLabel = layout === "horizontal" && labelAlign === "end";
|
|
78
90
|
return /* @__PURE__ */ jsxs(
|
|
79
91
|
"div",
|
|
@@ -54,7 +54,7 @@ function FormField({
|
|
|
54
54
|
const labelId = `${resolvedId}-label`;
|
|
55
55
|
const hasLabel = !(label === void 0 || label === null || label === false || label === "");
|
|
56
56
|
const helperId = helper ? `${resolvedId}-helper` : void 0;
|
|
57
|
-
const helperNode = helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "ui-form-field-note text-muted-foreground
|
|
57
|
+
const helperNode = helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "ui-form-field-note text-muted-foreground", children: helper }) : null;
|
|
58
58
|
const validationStatus = error ? "error" : validateStatus;
|
|
59
59
|
const feedbackId = hasFeedback && validationStatus ? `${resolvedId}-feedback` : void 0;
|
|
60
60
|
const FeedbackIcon = validationStatus === "validating" ? LoaderCircle : validationStatus === "error" ? CircleAlert : validationStatus === "warning" ? TriangleAlert : CheckCircle;
|
|
@@ -200,7 +200,7 @@ function FormField({
|
|
|
200
200
|
}
|
|
201
201
|
) : null,
|
|
202
202
|
helperPlacement === "after" ? helperNode : null,
|
|
203
|
-
error ? /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "ui-form-field-note text-error-strong
|
|
203
|
+
error ? /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "ui-form-field-note text-error-strong", children: error }) : null
|
|
204
204
|
] })
|
|
205
205
|
]
|
|
206
206
|
}
|
|
@@ -25,6 +25,7 @@ export declare const NumberInput: React.ForwardRefExoticComponent<import(".").Fi
|
|
|
25
25
|
placeholder?: import("../../props/index.js").PlaceholderProp;
|
|
26
26
|
prefix?: React.ReactNode;
|
|
27
27
|
suffix?: React.ReactNode;
|
|
28
|
+
width?: import("../../props/index.js").ControlWidthProp;
|
|
28
29
|
name?: import("../../props/index.js").NameProp;
|
|
29
30
|
id?: import("../../props/index.js").IdProp;
|
|
30
31
|
className?: import("../../props/index.js").ClassNameProp;
|
|
@@ -15,6 +15,7 @@ function decimalsOf(n) {
|
|
|
15
15
|
const dot = s.indexOf(".");
|
|
16
16
|
return dot === -1 ? 0 : s.length - dot - 1;
|
|
17
17
|
}
|
|
18
|
+
const AUTO_CHARS_UNBOUNDED = 6;
|
|
18
19
|
function clamp(n, min, max) {
|
|
19
20
|
let out = n;
|
|
20
21
|
if (min != null && out < min) out = min;
|
|
@@ -51,6 +52,7 @@ const NumberInput = React.forwardRef(
|
|
|
51
52
|
placeholder,
|
|
52
53
|
prefix,
|
|
53
54
|
suffix,
|
|
55
|
+
width = "full",
|
|
54
56
|
name,
|
|
55
57
|
id,
|
|
56
58
|
className,
|
|
@@ -182,6 +184,10 @@ const NumberInput = React.forwardRef(
|
|
|
182
184
|
if (!changeOnWheel || !interactive || !focused || event.deltaY === 0) return;
|
|
183
185
|
stepBy(event.deltaY < 0 ? 1 : -1);
|
|
184
186
|
};
|
|
187
|
+
const autoChars = width !== "auto" ? null : min == null && max == null ? AUTO_CHARS_UNBOUNDED : Math.max(
|
|
188
|
+
...[min, max].filter((bound) => bound != null && Number.isFinite(bound)).map((bound) => formatAtRest(bound).length),
|
|
189
|
+
1
|
|
190
|
+
);
|
|
185
191
|
return /* @__PURE__ */ jsxs(
|
|
186
192
|
"div",
|
|
187
193
|
{
|
|
@@ -190,7 +196,9 @@ const NumberInput = React.forwardRef(
|
|
|
190
196
|
"data-status": status,
|
|
191
197
|
"data-variant": variant,
|
|
192
198
|
"data-controls": controls ? void 0 : "off",
|
|
199
|
+
"data-width": width === "full" ? void 0 : width,
|
|
193
200
|
className: cn("ui-number-input", className),
|
|
201
|
+
style: autoChars == null ? void 0 : { "--number-input-chars": autoChars },
|
|
194
202
|
children: [
|
|
195
203
|
prefix != null ? /* @__PURE__ */ jsx(
|
|
196
204
|
"span",
|
|
@@ -319,6 +319,7 @@ const SelectTrigger = React.forwardRef(
|
|
|
319
319
|
},
|
|
320
320
|
[ref]
|
|
321
321
|
);
|
|
322
|
+
const openedWithPointer = React.useRef(false);
|
|
322
323
|
const wasOpen = React.useRef(false);
|
|
323
324
|
React.useEffect(() => {
|
|
324
325
|
if (!wasOpen.current || isOpen) {
|
|
@@ -354,6 +355,7 @@ const SelectTrigger = React.forwardRef(
|
|
|
354
355
|
ref: composedRef,
|
|
355
356
|
id,
|
|
356
357
|
onPointerDown: (event) => {
|
|
358
|
+
openedWithPointer.current = true;
|
|
357
359
|
event.currentTarget.focus();
|
|
358
360
|
},
|
|
359
361
|
...events,
|
|
@@ -384,6 +386,10 @@ const SelectTrigger = React.forwardRef(
|
|
|
384
386
|
"data-status": status,
|
|
385
387
|
"data-width": width,
|
|
386
388
|
"data-state": isOpen ? "open" : "closed",
|
|
389
|
+
"data-open-modality": isOpen ? openedWithPointer.current ? "pointer" : "keyboard" : void 0,
|
|
390
|
+
onKeyDownCapture: () => {
|
|
391
|
+
openedWithPointer.current = false;
|
|
392
|
+
},
|
|
387
393
|
"data-placeholder": hasValue ? void 0 : "",
|
|
388
394
|
"data-value": dataValue
|
|
389
395
|
}
|
|
@@ -600,6 +606,13 @@ function groupDataOptions(options) {
|
|
|
600
606
|
}
|
|
601
607
|
return order.map((key) => ({ heading: key || void 0, items: buckets.get(key) ?? [] }));
|
|
602
608
|
}
|
|
609
|
+
function withOptionIcon(option, label) {
|
|
610
|
+
if (!option.icon) return label;
|
|
611
|
+
return /* @__PURE__ */ jsxs("span", { className: "ui-select-option-body", children: [
|
|
612
|
+
/* @__PURE__ */ jsx("span", { className: "ui-select-option-icon", "aria-hidden": "true", children: option.icon }),
|
|
613
|
+
/* @__PURE__ */ jsx("span", { className: "ui-select-option-label", children: label })
|
|
614
|
+
] });
|
|
615
|
+
}
|
|
603
616
|
function DataSelectLabel({
|
|
604
617
|
labelRender,
|
|
605
618
|
options
|
|
@@ -716,7 +729,7 @@ function DataSelect(props) {
|
|
|
716
729
|
disabled: option.disabled,
|
|
717
730
|
"data-testid": optionTestId(option.value),
|
|
718
731
|
selectedIcon: menuItemSelectedIcon,
|
|
719
|
-
children: optionRender ? optionRender(option, { index }) : renderOption ? renderOption(option) : option.label
|
|
732
|
+
children: optionRender ? optionRender(option, { index }) : renderOption ? renderOption(option) : withOptionIcon(option, option.label)
|
|
720
733
|
},
|
|
721
734
|
option.value
|
|
722
735
|
);
|
|
@@ -771,7 +784,16 @@ function DataSelect(props) {
|
|
|
771
784
|
...ariaProps,
|
|
772
785
|
children: [
|
|
773
786
|
prefix === void 0 || prefix === null ? null : /* @__PURE__ */ jsx("span", { "data-slot": "select-prefix", className: "ui-select-prefix", children: prefix }),
|
|
774
|
-
/* @__PURE__ */ jsx(SelectValue, { placeholder: placeholder ?? t("dataEntry.searchSelect.placeholder"), children: labelRender ? /* @__PURE__ */ jsx(DataSelectLabel, { labelRender, options }) :
|
|
787
|
+
/* @__PURE__ */ jsx(SelectValue, { placeholder: placeholder ?? t("dataEntry.searchSelect.placeholder"), children: labelRender ? /* @__PURE__ */ jsx(DataSelectLabel, { labelRender, options }) : options.some((option) => option.icon) ? (
|
|
788
|
+
// gh#1444 — the selected option's icon leads the trigger too, as in the search panel.
|
|
789
|
+
/* @__PURE__ */ jsx(
|
|
790
|
+
DataSelectLabel,
|
|
791
|
+
{
|
|
792
|
+
labelRender: ({ label, option }) => option ? withOptionIcon(option, label) : label,
|
|
793
|
+
options
|
|
794
|
+
}
|
|
795
|
+
)
|
|
796
|
+
) : void 0 }),
|
|
775
797
|
suffixIcon === void 0 || suffixIcon === null || showClear || loading ? null : /* @__PURE__ */ jsx("span", { "data-slot": "select-suffix", className: "ui-select-suffix", "aria-hidden": "true", children: suffixIcon })
|
|
776
798
|
]
|
|
777
799
|
}
|
|
@@ -55,9 +55,10 @@ type TogglePressedFields = {
|
|
|
55
55
|
* accessible name (gh#734) — without a second counting API growing beside this one (gh#762).
|
|
56
56
|
* It defaults to `toggle`, so every existing call emits the identical markup it always has.
|
|
57
57
|
*/
|
|
58
|
-
declare function useCounterPill({ count, overflowCount, showZero, countLabel, ariaLabel, slot, }: ToggleCountFields & {
|
|
58
|
+
declare function useCounterPill({ count, overflowCount, showZero, countLabel, ariaLabel, slot, countTone, }: ToggleCountFields & {
|
|
59
59
|
ariaLabel?: string;
|
|
60
60
|
slot?: string;
|
|
61
|
+
countTone?: "neutral" | "accent";
|
|
61
62
|
}): {
|
|
62
63
|
pill: null;
|
|
63
64
|
resolvedAriaLabel: string | undefined;
|
|
@@ -58,7 +58,8 @@ function useCounterPill({
|
|
|
58
58
|
showZero = true,
|
|
59
59
|
countLabel,
|
|
60
60
|
ariaLabel,
|
|
61
|
-
slot = "toggle"
|
|
61
|
+
slot = "toggle",
|
|
62
|
+
countTone
|
|
62
63
|
}) {
|
|
63
64
|
const { locale } = useTranslation();
|
|
64
65
|
const visible = count != null && (count !== 0 || showZero);
|
|
@@ -73,7 +74,16 @@ function useCounterPill({
|
|
|
73
74
|
const spoken = countLabel ? `${formatted} ${countLabel}` : formatted;
|
|
74
75
|
return {
|
|
75
76
|
pill: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
76
|
-
/* @__PURE__ */ jsx(
|
|
77
|
+
/* @__PURE__ */ jsx(
|
|
78
|
+
"span",
|
|
79
|
+
{
|
|
80
|
+
"data-slot": `${slot}-count`,
|
|
81
|
+
"data-count-tone": countTone,
|
|
82
|
+
className: `ui-${slot}-count`,
|
|
83
|
+
"aria-hidden": "true",
|
|
84
|
+
children: formatted
|
|
85
|
+
}
|
|
86
|
+
),
|
|
77
87
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: `, ${spoken}` })
|
|
78
88
|
] }),
|
|
79
89
|
resolvedAriaLabel: ariaLabel == null ? ariaLabel : `${ariaLabel}, ${spoken}`
|
|
@@ -74,6 +74,11 @@ const buttonVariants = cva("ui-button", {
|
|
|
74
74
|
},
|
|
75
75
|
defaultVariants: { variant: "default", size: "default", shape: "default" }
|
|
76
76
|
});
|
|
77
|
+
function hasDirectText(children) {
|
|
78
|
+
return React.Children.toArray(children).some(
|
|
79
|
+
(child) => typeof child === "string" && child.trim() !== "" || typeof child === "number"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
77
82
|
const Button = React.forwardRef(
|
|
78
83
|
({
|
|
79
84
|
className,
|
|
@@ -108,6 +113,7 @@ const Button = React.forwardRef(
|
|
|
108
113
|
const spokenCount = countLabel ? `${countText} ${countLabel}` : countText;
|
|
109
114
|
const ariaLabel = props["aria-label"];
|
|
110
115
|
const foldedAriaLabel = showCount && ariaLabel != null ? { "aria-label": `${ariaLabel}, ${spokenCount}` } : null;
|
|
116
|
+
const labelled = asChild ? React.isValidElement(children) && hasDirectText(children.props.children) : hasDirectText(loadingText ?? children);
|
|
111
117
|
const countNode = showCount ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
112
118
|
/* @__PURE__ */ jsx("span", { "data-slot": "button-count", className: "ui-button-count", "aria-hidden": "true", children: countText }),
|
|
113
119
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: `, ${spokenCount}` })
|
|
@@ -124,6 +130,7 @@ const Button = React.forwardRef(
|
|
|
124
130
|
"data-wrap": wrap ? "" : void 0,
|
|
125
131
|
"data-align": align === "center" ? void 0 : align,
|
|
126
132
|
"data-loading": isLoading ? "" : void 0,
|
|
133
|
+
"data-label": labelled ? "" : void 0,
|
|
127
134
|
"aria-busy": isLoading || void 0,
|
|
128
135
|
disabled: isLoading || disabled,
|
|
129
136
|
type: asChild ? void 0 : type ?? "button",
|
|
@@ -44,7 +44,7 @@ export declare const Text: React.ForwardRefExoticComponent<Omit<React.HTMLAttrib
|
|
|
44
44
|
as?: "span" | "p" | "div" | "a" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr";
|
|
45
45
|
asChild?: import("../../props/index.js").AsChildProp;
|
|
46
46
|
link?: boolean;
|
|
47
|
-
size?: import("../../props/index.js").TextSizeProp;
|
|
47
|
+
size?: import("../../props/index.js").TextSizeProp | "inherit";
|
|
48
48
|
tone?: import("../../props/index.js").TextToneProp;
|
|
49
49
|
weight?: import("../../props/index.js").FontWeightProp;
|
|
50
50
|
align?: import("../../props/index.js").TextAlignProp;
|
|
@@ -3,4 +3,4 @@ import type { AppShellProp } from "../../props/components/layout.prop.js";
|
|
|
3
3
|
export type { AppShellProp, AppShellProp as AppShellProps, } from "../../props/components/layout.prop.js";
|
|
4
4
|
/** Same rail boundary as shell-layout.css; SSR starts in docked mode. */
|
|
5
5
|
export declare function useAppShellNavigationMode(): "drawer" | "docked";
|
|
6
|
-
export declare function AppShell({ sidebar, topbar, topbarLeft, topbarRight, logo, logoCompact, logoCompactBelow, breadcrumb, footer, children, sidebarCollapsed, responsiveNavigation, topbarSpan, navRail, navRailPosition, navRailEnd, navRailLabel, mobileNav, mobileNavLabel, mobileNavTriggerLabel, mobileNavOnCloseAutoFocus, mobileNavOpen, onMobileNavOpenChange, }: AppShellProp): React.JSX.Element;
|
|
6
|
+
export declare function AppShell({ sidebar, topbar, topbarLeft, topbarRight, logo, logoCompact, logoCompactBelow, breadcrumb, footer, children, sidebarCollapsed, responsiveNavigation, topbarSpan, navRail, navRailPosition, navRailEnd, navRailLabel, mobileTabBar, mobileTabBarLabel, mobileNav, mobileNavLabel, mobileNavTriggerLabel, mobileNavOnCloseAutoFocus, mobileNavOpen, onMobileNavOpenChange, }: AppShellProp): React.JSX.Element;
|
|
@@ -6,6 +6,8 @@ import { Menu } from "lucide-react";
|
|
|
6
6
|
import { useMediaQuery } from "../../lib/hooks.js";
|
|
7
7
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
8
8
|
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../feedback/sheet.js";
|
|
9
|
+
import { ShellTabBar } from "./mobile-shell.js";
|
|
10
|
+
import { useToastStickyBarClearance } from "../../lib/toast-clearance.js";
|
|
9
11
|
import { NavSurfaceProvider } from "./nav-surface.js";
|
|
10
12
|
import { TopbarItem } from "./topbar-item.js";
|
|
11
13
|
import { isDevelopment } from "../../lib/dev.js";
|
|
@@ -30,6 +32,8 @@ function AppShell({
|
|
|
30
32
|
navRailPosition = "start",
|
|
31
33
|
navRailEnd,
|
|
32
34
|
navRailLabel,
|
|
35
|
+
mobileTabBar,
|
|
36
|
+
mobileTabBarLabel,
|
|
33
37
|
mobileNav,
|
|
34
38
|
mobileNavLabel,
|
|
35
39
|
mobileNavTriggerLabel,
|
|
@@ -39,6 +43,23 @@ function AppShell({
|
|
|
39
43
|
}) {
|
|
40
44
|
const { t } = useTranslation();
|
|
41
45
|
const navigationMode = useAppShellNavigationMode();
|
|
46
|
+
const rootRef = React.useRef(null);
|
|
47
|
+
const tabBarRef = React.useRef(null);
|
|
48
|
+
const showTabBar = responsiveNavigation === "drawer" && navigationMode === "drawer" && mobileTabBar != null && mobileTabBar !== false;
|
|
49
|
+
useToastStickyBarClearance(showTabBar ? [tabBarRef] : [], showTabBar);
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
const root = rootRef.current;
|
|
52
|
+
const bar2 = tabBarRef.current;
|
|
53
|
+
if (!root || !bar2 || !showTabBar || typeof ResizeObserver === "undefined") return;
|
|
54
|
+
const publish = () => root.style.setProperty("--app-shell-mobile-tab-bar-block-size", `${bar2.offsetHeight}px`);
|
|
55
|
+
publish();
|
|
56
|
+
const observer = new ResizeObserver(publish);
|
|
57
|
+
observer.observe(bar2, { box: "border-box" });
|
|
58
|
+
return () => {
|
|
59
|
+
observer.disconnect();
|
|
60
|
+
root.style.removeProperty("--app-shell-mobile-tab-bar-block-size");
|
|
61
|
+
};
|
|
62
|
+
}, [showTabBar]);
|
|
42
63
|
const hasSidebar = sidebar !== void 0 && sidebar !== null && sidebar !== false;
|
|
43
64
|
const railContent = navRail === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
44
65
|
navRail,
|
|
@@ -146,10 +167,13 @@ function AppShell({
|
|
|
146
167
|
] }),
|
|
147
168
|
hasTopbarContent && resolvedTopbar
|
|
148
169
|
] });
|
|
170
|
+
const footerRegion = footer !== void 0 ? /* @__PURE__ */ jsx("footer", { className: "app-footer", "aria-label": t("layout.appShell.footerLabel"), children: footer }) : null;
|
|
149
171
|
return /* @__PURE__ */ jsxs(
|
|
150
172
|
"div",
|
|
151
173
|
{
|
|
174
|
+
ref: rootRef,
|
|
152
175
|
className: "app-root",
|
|
176
|
+
"data-mobile-tab-bar": showTabBar ? "" : void 0,
|
|
153
177
|
"data-collapsed": hasSidebar && sidebarCollapsed ? "true" : void 0,
|
|
154
178
|
"data-sidebar": hasSidebar ? void 0 : "none",
|
|
155
179
|
"data-responsive-navigation": responsiveNavigation,
|
|
@@ -177,11 +201,13 @@ function AppShell({
|
|
|
177
201
|
tabIndex: 0,
|
|
178
202
|
children: [
|
|
179
203
|
breadcrumb !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-breadcrumb", children: breadcrumb }),
|
|
180
|
-
children
|
|
204
|
+
children,
|
|
205
|
+
showTabBar && footerRegion
|
|
181
206
|
]
|
|
182
207
|
}
|
|
183
208
|
),
|
|
184
|
-
|
|
209
|
+
!showTabBar && footerRegion,
|
|
210
|
+
showTabBar && /* @__PURE__ */ jsx(ShellTabBar, { ref: tabBarRef, className: "app-mobile-tab-bar", "aria-label": mobileTabBarLabel, children: mobileTabBar })
|
|
185
211
|
]
|
|
186
212
|
}
|
|
187
213
|
);
|
|
@@ -15,6 +15,8 @@ function PageLayout({
|
|
|
15
15
|
width = "md",
|
|
16
16
|
align = "start",
|
|
17
17
|
preset = "default",
|
|
18
|
+
footerTone = "default",
|
|
19
|
+
flow = "column",
|
|
18
20
|
className
|
|
19
21
|
}) {
|
|
20
22
|
const { t } = useTranslation();
|
|
@@ -23,6 +25,7 @@ function PageLayout({
|
|
|
23
25
|
{
|
|
24
26
|
"data-slot": "centered-shell",
|
|
25
27
|
"data-preset": preset === "default" ? void 0 : preset,
|
|
28
|
+
"data-flow": flow === "sections" ? "sections" : void 0,
|
|
26
29
|
className: cn("ui-centered-shell", className),
|
|
27
30
|
children: [
|
|
28
31
|
topbar !== void 0 && /* @__PURE__ */ jsx(
|
|
@@ -39,6 +42,7 @@ function PageLayout({
|
|
|
39
42
|
{
|
|
40
43
|
className: "ui-centered-shell-footer",
|
|
41
44
|
"aria-label": t("layout.centeredShell.footerLabel"),
|
|
45
|
+
"data-tone": footerTone === "inverse" ? "inverse" : void 0,
|
|
42
46
|
children: footer
|
|
43
47
|
}
|
|
44
48
|
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { EditorialAuthCardProp, EditorialAuthProp, EditorialEmptyPanelProp, EditorialFootnoteProp, EditorialMetaBarProp, EditorialStatsProp, EditorialTextButtonProp, EditorialToggleGroupProp, EditorialToolbarProp, HoldingCardActionProp, HoldingCardProp, HoldingGridProp, SettingsLayoutProp, SettingsPanelProp, ThemePickerProp } from "../../props/components/editorial-account.prop.js";
|
|
3
|
+
export type { EditorialAuthCardProp, EditorialAuthCardProp as EditorialAuthCardProps, EditorialAuthProp, EditorialAuthProp as EditorialAuthProps, EditorialEmptyPanelProp, EditorialEmptyPanelProp as EditorialEmptyPanelProps, EditorialFootnoteProp, EditorialFootnoteProp as EditorialFootnoteProps, EditorialMetaBarProp, EditorialMetaBarProp as EditorialMetaBarProps, EditorialStatsItemProp, EditorialStatsProp, EditorialStatsProp as EditorialStatsProps, EditorialTextButtonProp, EditorialTextButtonProp as EditorialTextButtonProps, EditorialToggleGroupItemProp, EditorialToggleGroupProp, EditorialToggleGroupProp as EditorialToggleGroupProps, EditorialToolbarProp, EditorialToolbarProp as EditorialToolbarProps, HoldingCardActionProp, HoldingCardProp, HoldingCardProp as HoldingCardProps, HoldingGridProp, HoldingGridProp as HoldingGridProps, HoldingViewProp, SettingsLayoutProp, SettingsLayoutProp as SettingsLayoutProps, SettingsPanelProp, SettingsPanelProp as SettingsPanelProps, ThemePickerOptionProp, ThemePickerPreviewProp, ThemePickerProp, ThemePickerProp as ThemePickerProps, } from "../../props/components/editorial-account.prop.js";
|
|
4
|
+
/** A named group of `aria-pressed` buttons, one pressed at a time: text filter or icon switch. */
|
|
5
|
+
export declare function EditorialToggleGroup({ items, value, onValueChange, variant, className, ...rest }: EditorialToggleGroupProp): React.JSX.Element;
|
|
6
|
+
/** A hairline-ruled strip: a glyph and a line at the start, a badge at the end. */
|
|
7
|
+
export declare function EditorialMetaBar({ icon, start, end, className }: EditorialMetaBarProp): React.JSX.Element;
|
|
8
|
+
/** A centred signed-out (`guest`, with a card-stack illustration) or empty (`empty`) panel. */
|
|
9
|
+
export declare function EditorialEmptyPanel({ variant, eyebrow, title, level, description, actions, icon, className, }: EditorialEmptyPanelProp): React.JSX.Element;
|
|
10
|
+
/** A strip of figure tiles on unequal tracks; the first tile is the lead. */
|
|
11
|
+
export declare function EditorialStats({ items, className, ...rest }: EditorialStatsProp): React.JSX.Element;
|
|
12
|
+
/**
|
|
13
|
+
* The row above a collection: a summary (count + note) and / or a start cluster, an end cluster
|
|
14
|
+
* (order, view switch, primary action), optionally over a hairline. With a `summary` it is a
|
|
15
|
+
* listing row that keeps one line; without one it is a bar whose clusters may stack.
|
|
16
|
+
*/
|
|
17
|
+
export declare function EditorialToolbar({ summary, start, end, divider, className, }: EditorialToolbarProp): React.JSX.Element;
|
|
18
|
+
/** The track of HoldingCards; `view="list"` turns every card into a wide row. */
|
|
19
|
+
export declare function HoldingGrid({ view, children, className, ...rest }: HoldingGridProp): React.JSX.Element;
|
|
20
|
+
/** One owned item: picture ground, overline, title, meta, tags, purchase line, date, actions. */
|
|
21
|
+
export declare function HoldingCard({ media, overline, title, level, meta, tags, purchase, date, actions, className, }: HoldingCardProp): React.JSX.Element;
|
|
22
|
+
/** A quiet glyph + label action of a HoldingCard; `asChild` turns a router link into it. */
|
|
23
|
+
export declare function HoldingCardAction({ icon, children, asChild, onClick, disabled, className, ...rest }: HoldingCardActionProp): React.JSX.Element;
|
|
24
|
+
/** A settings page: a sticky rail beside the panels. */
|
|
25
|
+
export declare function SettingsLayout({ aside, children, className }: SettingsLayoutProp): React.JSX.Element;
|
|
26
|
+
/** One bordered settings section: number, title, description, then its fields. */
|
|
27
|
+
export declare function SettingsPanel({ number, title, level, description, children, titleId, className, }: SettingsPanelProp): React.JSX.Element;
|
|
28
|
+
/** An illustrated radio group for the colour theme (painted light / dark / system previews). */
|
|
29
|
+
export declare function ThemePicker({ legend, options, value, defaultValue, onValueChange, name, className, }: ThemePickerProp): React.JSX.Element;
|
|
30
|
+
/** A sign-in / sign-up page: the story column beside the form card. */
|
|
31
|
+
export declare function EditorialAuth({ eyebrow, title, description, art, benefits, children, className, }: EditorialAuthProp): React.JSX.Element;
|
|
32
|
+
/** The form card of EditorialAuth: badge, title, copy, the form and a ruled footer line. */
|
|
33
|
+
export declare function EditorialAuthCard({ badge, title, level, description, children, footer, className, }: EditorialAuthCardProp): React.JSX.Element;
|
|
34
|
+
/** An underlined text action in the primary tone; `asChild` turns a router link into it. */
|
|
35
|
+
export declare function EditorialTextButton({ children, icon, asChild, onClick, disabled, className, }: EditorialTextButtonProp): React.JSX.Element;
|
|
36
|
+
/**
|
|
37
|
+
* `EditorialSearch variant="inline"` (gh#1439) — internal: a collection's own search row, visible
|
|
38
|
+
* label, framed field with a glyph and a live count. Rendered by EditorialSearch; not exported.
|
|
39
|
+
*/
|
|
40
|
+
export declare function EditorialSearchInline({ label, id, placeholder, defaultValue, onSearch, debounce, count, className, }: {
|
|
41
|
+
label: string;
|
|
42
|
+
id?: string;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
defaultValue?: string;
|
|
45
|
+
onSearch?: (query: string) => void;
|
|
46
|
+
debounce?: number;
|
|
47
|
+
count?: React.ReactNode;
|
|
48
|
+
className?: string;
|
|
49
|
+
}): React.JSX.Element;
|
|
50
|
+
/** A quiet closing line: a note at the start, an optional action at the end. */
|
|
51
|
+
export declare function EditorialFootnote({ children, action, align, className, }: EditorialFootnoteProp): React.JSX.Element;
|