@godxjp/ui 19.6.0 → 20.0.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-display/card.d.ts +2 -2
- package/dist/components/data-display/collapsible.d.ts +23 -4
- package/dist/components/data-display/collapsible.js +119 -4
- package/dist/components/data-display/index.d.ts +3 -1
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/legend.d.ts +15 -0
- package/dist/components/data-display/legend.js +11 -0
- package/dist/components/data-display/list-row.js +1 -1
- package/dist/components/data-display/popover.d.ts +58 -5
- package/dist/components/data-display/popover.js +206 -27
- package/dist/components/data-display/progress.d.ts +50 -2
- package/dist/components/data-display/progress.js +57 -9
- package/dist/components/data-entry/calendar.d.ts +1 -1
- package/dist/components/data-entry/calendar.js +67 -32
- package/dist/components/data-entry/checkbox.d.ts +19 -6
- package/dist/components/data-entry/checkbox.js +55 -16
- package/dist/components/data-entry/choice-option.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/field.js +0 -1
- package/dist/components/data-entry/form.js +1 -1
- package/dist/components/data-entry/label.d.ts +3 -2
- package/dist/components/data-entry/label.js +23 -10
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +16 -6
- package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +29 -12
- package/dist/components/data-entry/tree-utils.d.ts +1 -1
- package/dist/components/data-entry/upload.js +7 -2
- package/dist/components/feedback/dialog.d.ts +105 -38
- package/dist/components/feedback/dialog.js +272 -194
- package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
- package/dist/components/feedback/overlay-close-focus.js +31 -0
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +53 -11
- package/dist/components/feedback/sheet.js +150 -81
- package/dist/components/feedback/tooltip.d.ts +51 -7
- package/dist/components/feedback/tooltip.js +107 -25
- package/dist/components/general/button.js +2 -2
- package/dist/components/general/logo.d.ts +17 -0
- package/dist/components/general/logo.js +22 -16
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +52 -8
- package/dist/components/layout/aspect-ratio.js +0 -1
- package/dist/components/layout/auth-divider.js +0 -1
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +19 -2
- package/dist/components/layout/nav-surface.d.ts +26 -0
- package/dist/components/layout/nav-surface.js +17 -0
- package/dist/components/layout/org-switcher.d.ts +5 -1
- package/dist/components/layout/org-switcher.js +5 -3
- package/dist/components/layout/separator.js +0 -1
- package/dist/components/layout/sidebar.js +4 -1
- package/dist/components/layout/split-pane.d.ts +14 -1
- package/dist/components/layout/split-pane.js +26 -13
- package/dist/components/layout/topbar-item.js +1 -2
- package/dist/components/navigation/app-setting-picker.js +21 -2
- package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
- package/dist/components/navigation/app-setting-toggle.js +96 -0
- package/dist/components/navigation/dropdown-menu.d.ts +199 -18
- package/dist/components/navigation/dropdown-menu.js +344 -117
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/pagination-utils.d.ts +2 -1
- package/dist/components/navigation/tabs.d.ts +47 -6
- package/dist/components/navigation/tabs.js +152 -90
- package/dist/components/ui/accordion.d.ts +50 -5
- package/dist/components/ui/accordion.js +239 -33
- package/dist/components/ui/aspect-ratio.d.ts +23 -2
- package/dist/components/ui/aspect-ratio.js +15 -13
- package/dist/components/ui/avatar.d.ts +29 -4
- package/dist/components/ui/avatar.js +111 -25
- package/dist/components/ui/hover-card.d.ts +42 -4
- package/dist/components/ui/hover-card.js +183 -27
- package/dist/components/ui/label.js +0 -1
- package/dist/components/ui/segmented.d.ts +3 -3
- package/dist/components/ui/separator.d.ts +8 -2
- package/dist/components/ui/separator.js +17 -9
- package/dist/components/ui/toggle-group.d.ts +50 -5
- package/dist/components/ui/toggle-group.js +79 -20
- package/dist/components/ui/toggle.d.ts +31 -5
- package/dist/components/ui/toggle.js +42 -3
- package/dist/i18n/messages/en.json +8 -0
- package/dist/i18n/messages/ja.json +8 -0
- package/dist/i18n/messages/vi.json +8 -0
- package/dist/lib/control-styles.d.ts +1 -1
- package/dist/lib/control-styles.js +1 -1
- package/dist/lib/slot.d.ts +32 -0
- package/dist/lib/slot.js +22 -0
- package/dist/lib/variants.d.ts +22 -3
- package/dist/lib/variants.js +56 -1
- package/dist/props/components/app.prop.d.ts +25 -1
- package/dist/props/components/data-display.prop.d.ts +17 -0
- package/dist/props/components/data-entry.prop.d.ts +32 -4
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +118 -5
- package/dist/props/components/navigation.prop.d.ts +1 -1
- package/dist/props/registry.d.ts +22 -2
- package/dist/props/registry.js +33 -2
- package/dist/props/vocabulary/content.prop.d.ts +1 -1
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
- package/dist/props/vocabulary/layout.prop.d.ts +57 -1
- package/dist/styles/control.css +142 -1
- package/dist/styles/core.css +5 -2
- package/dist/styles/data-display-layout.css +72 -0
- package/dist/styles/focus-ring.css +4 -0
- package/dist/styles/index.css +5 -2
- package/dist/styles/layout.css +106 -0
- package/dist/styles/shell-layout.css +140 -8
- package/dist/styles/text-layout.css +5 -4
- package/dist/tokens/base.css +1 -1
- package/dist/tokens/components/data-display.css +11 -0
- package/dist/tokens/components/shell.css +3 -1
- package/dist/tokens/foundation.css +2 -0
- package/docs/COMPONENTS.md +9 -3
- package/docs/DESIGN-AUTHORITY.md +96 -71
- package/docs/FRAME-COVERAGE-REPORT.md +5 -3
- package/docs/README.md +14 -14
- package/docs/WHAT-BELONGS-HERE.md +179 -0
- package/docs/data-display/legend.tsx +145 -0
- package/docs/data-display/progress.tsx +32 -0
- package/docs/data-entry/segmented.tsx +1 -1
- package/docs/data-entry/select.tsx +2 -2
- package/docs/feedback/sheet.tsx +1 -1
- package/docs/layout/app-shell-arrangements.tsx +225 -0
- package/docs/layout/aspect-ratio.tsx +1 -1
- package/docs/navigation/app-setting-picker.tsx +26 -1
- package/docs/navigation/app-setting-toggle.tsx +111 -0
- package/package.json +11 -8
- package/scripts/_agent-setup.mjs +165 -3
- package/scripts/consumer-rule.md +98 -0
- package/scripts/guinea-pig-skill.md +322 -0
- package/scripts/init-guinea-pig.mjs +62 -0
- package/scripts/postinstall.mjs +13 -2
- package/scripts/ui-audit.mjs +115 -14
- /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { Slot } from "../../lib/slot.js";
|
|
3
4
|
import { cn } from "../../lib/utils.js";
|
|
4
5
|
const X_HEIGHT_TOP = /^[acegmnopqrsuvwxyz]+$/;
|
|
5
6
|
const LATIN_DESCENDER = /[gjpqy]/;
|
|
@@ -83,42 +84,47 @@ const Logo = React.forwardRef(
|
|
|
83
84
|
tone = "primary",
|
|
84
85
|
wordmark,
|
|
85
86
|
label,
|
|
87
|
+
asChild,
|
|
88
|
+
children,
|
|
86
89
|
className,
|
|
87
90
|
...props
|
|
88
91
|
}, ref) => {
|
|
89
92
|
const hasWordmark = wordmark !== void 0 && wordmark !== null && wordmark !== false;
|
|
90
93
|
const identity = { "data-mark": mark, "data-size": size, "data-tone": tone };
|
|
94
|
+
const renderRoot = (rootProps, content) => {
|
|
95
|
+
if (!asChild) {
|
|
96
|
+
return /* @__PURE__ */ jsx("span", { ref, ...rootProps, children: content });
|
|
97
|
+
}
|
|
98
|
+
const borrowed = React.Children.only(children);
|
|
99
|
+
return /* @__PURE__ */ jsx(Slot, { ...rootProps, ref, children: React.cloneElement(borrowed, void 0, content) });
|
|
100
|
+
};
|
|
91
101
|
if (hasWordmark) {
|
|
92
|
-
return
|
|
93
|
-
"span",
|
|
102
|
+
return renderRoot(
|
|
94
103
|
{
|
|
95
|
-
ref,
|
|
96
104
|
"data-slot": "logo-lockup",
|
|
97
105
|
...identity,
|
|
98
106
|
className: cn("ui-logo-lockup", className),
|
|
99
107
|
role: label ? "img" : void 0,
|
|
100
108
|
"aria-label": label,
|
|
101
|
-
...props
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
109
|
+
...props
|
|
110
|
+
},
|
|
111
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
112
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "logo", ...identity, className: "ui-logo", "aria-hidden": "true", children: /* @__PURE__ */ jsx(MarkArtwork, { mark, glyph }) }),
|
|
113
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "logo-wordmark", "data-logotype": true, className: "ui-logo-wordmark", children: wordmark })
|
|
114
|
+
] })
|
|
107
115
|
);
|
|
108
116
|
}
|
|
109
|
-
return
|
|
110
|
-
"span",
|
|
117
|
+
return renderRoot(
|
|
111
118
|
{
|
|
112
|
-
ref,
|
|
113
119
|
"data-slot": "logo",
|
|
114
120
|
...identity,
|
|
115
121
|
className: cn("ui-logo", className),
|
|
116
122
|
role: label ? "img" : void 0,
|
|
117
123
|
"aria-label": label,
|
|
118
124
|
"aria-hidden": label ? void 0 : true,
|
|
119
|
-
...props
|
|
120
|
-
|
|
121
|
-
}
|
|
125
|
+
...props
|
|
126
|
+
},
|
|
127
|
+
/* @__PURE__ */ jsx(MarkArtwork, { mark, glyph })
|
|
122
128
|
);
|
|
123
129
|
}
|
|
124
130
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { AppShellProp } from "../../props/components/layout.prop.js";
|
|
3
3
|
export type { AppShellProp, AppShellProp as AppShellProps, } from "../../props/components/layout.prop.js";
|
|
4
|
-
export declare function AppShell({ sidebar, topbar, topbarLeft, topbarRight, logo, breadcrumb, footer, children, sidebarCollapsed, responsiveNavigation, topbarSpan, mobileNav, mobileNavLabel, mobileNavOpen, onMobileNavOpenChange, }: AppShellProp): React.JSX.Element;
|
|
4
|
+
export declare function AppShell({ sidebar, topbar, topbarLeft, topbarRight, logo, breadcrumb, footer, children, sidebarCollapsed, responsiveNavigation, topbarSpan, navRail, navRailLabel, mobileNav, mobileNavLabel, mobileNavOpen, onMobileNavOpenChange, }: AppShellProp): React.JSX.Element;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Menu } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { Button } from "../general/button.js";
|
|
7
7
|
import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../feedback/sheet.js";
|
|
8
|
+
import { NavSurfaceProvider } from "./nav-surface.js";
|
|
8
9
|
function AppShell({
|
|
9
10
|
sidebar,
|
|
10
11
|
topbar,
|
|
@@ -17,14 +18,20 @@ function AppShell({
|
|
|
17
18
|
sidebarCollapsed = false,
|
|
18
19
|
responsiveNavigation = "drawer",
|
|
19
20
|
topbarSpan = "content",
|
|
21
|
+
navRail,
|
|
22
|
+
navRailLabel,
|
|
20
23
|
mobileNav,
|
|
21
24
|
mobileNavLabel,
|
|
22
25
|
mobileNavOpen,
|
|
23
26
|
onMobileNavOpenChange
|
|
24
27
|
}) {
|
|
25
28
|
const { t } = useTranslation();
|
|
26
|
-
const drawerNav = mobileNav !== void 0 ? mobileNav :
|
|
29
|
+
const drawerNav = mobileNav !== void 0 ? mobileNav : navRail !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
30
|
+
navRail,
|
|
31
|
+
sidebar
|
|
32
|
+
] }) : sidebar;
|
|
27
33
|
const hasDrawer = responsiveNavigation === "drawer" && drawerNav != null;
|
|
34
|
+
const railInDrawer = mobileNav === void 0 && navRail !== void 0;
|
|
28
35
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
29
36
|
const drawerOpen = mobileNavOpen ?? uncontrolledOpen;
|
|
30
37
|
const setDrawerOpen = onMobileNavOpenChange ?? setUncontrolledOpen;
|
|
@@ -36,13 +43,39 @@ function AppShell({
|
|
|
36
43
|
}
|
|
37
44
|
};
|
|
38
45
|
const hasTopbarContent = topbar !== void 0 || topbarLeft !== void 0 || topbarRight !== void 0 || logo !== void 0;
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
if (process.env.NODE_ENV !== "production" && topbar !== void 0) {
|
|
47
|
+
const ignored = [
|
|
48
|
+
topbarLeft !== void 0 && "topbarLeft",
|
|
49
|
+
topbarRight !== void 0 && "topbarRight"
|
|
50
|
+
].filter(Boolean);
|
|
51
|
+
if (ignored.length > 0) {
|
|
52
|
+
console.warn(
|
|
53
|
+
`AppShell: \`topbar\` \u0111\u01B0\u1EE3c truy\u1EC1n, n\xEAn ${ignored.join(", ")} b\u1ECB b\u1ECF qua v\xE0 KH\xD4NG \u0111\u01B0\u1EE3c v\u1EBD. Ho\u1EB7c b\u1ECF \`topbar\` \u0111\u1EC3 AppShell t\u1EF1 d\u1EF1ng d\u1EA3i t\u1EEB c\xE1c khe kia, ho\u1EB7c \u0111\u1EB7t n\u1ED9i dung \u0111\xF3 v\xE0o trong ch\xEDnh \`topbar\`. (\`logo\` KH\xD4NG n\u1EB1m trong s\u1ED1 n\xE0y \u2014 n\xF3 lu\xF4n \u0111\u01B0\u1EE3c v\u1EBD.)`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const logoInRail = logo !== void 0 && topbarSpan !== "full";
|
|
58
|
+
const resolvedTopbar = topbar !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "app-topbar-rail", children: [
|
|
59
|
+
!logoInRail && logo !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-logo", children: logo }),
|
|
60
|
+
/* @__PURE__ */ jsx("div", { className: "app-topbar-custom", children: topbar })
|
|
61
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "app-topbar-rail", children: [
|
|
62
|
+
!logoInRail && logo !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-logo", children: logo }),
|
|
41
63
|
topbarLeft !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-left", children: topbarLeft }),
|
|
42
64
|
/* @__PURE__ */ jsx("div", { className: "app-topbar-spacer" }),
|
|
43
65
|
topbarRight !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-right", children: topbarRight })
|
|
44
66
|
] });
|
|
45
|
-
const rail = /* @__PURE__ */
|
|
67
|
+
const rail = /* @__PURE__ */ jsxs("aside", { className: "app-sidebar", "aria-label": t("layout.appShell.sidebarLabel"), children: [
|
|
68
|
+
logoInRail && /* @__PURE__ */ jsx("div", { className: "app-sidebar-logo", children: logo }),
|
|
69
|
+
sidebar
|
|
70
|
+
] });
|
|
71
|
+
const navRailRegion = navRail === void 0 ? null : /* @__PURE__ */ jsx(
|
|
72
|
+
"aside",
|
|
73
|
+
{
|
|
74
|
+
className: "app-nav-rail",
|
|
75
|
+
"aria-label": navRailLabel ?? t("layout.appShell.navRailLabel"),
|
|
76
|
+
children: navRail
|
|
77
|
+
}
|
|
78
|
+
);
|
|
46
79
|
const bar = !hasTopbarContent && !hasDrawer ? null : /* @__PURE__ */ jsxs("header", { className: "app-topbar ui-scale-fixed", "aria-label": t("layout.appShell.headerLabel"), children: [
|
|
47
80
|
hasDrawer && /* @__PURE__ */ jsxs(Sheet, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
|
|
48
81
|
/* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
@@ -71,7 +104,10 @@ function AppShell({
|
|
|
71
104
|
{
|
|
72
105
|
className: "app-mobile-nav-body px-[var(--app-shell-mobile-nav-inset)]",
|
|
73
106
|
onClick: handleDrawerClick,
|
|
74
|
-
children:
|
|
107
|
+
children: railInDrawer ? /* @__PURE__ */ jsxs("div", { className: "app-mobile-nav-columns", children: [
|
|
108
|
+
/* @__PURE__ */ jsx("div", { className: "app-mobile-nav-rail", children: navRail }),
|
|
109
|
+
/* @__PURE__ */ jsx(NavSurfaceProvider, { surface: "drawer", children: /* @__PURE__ */ jsx("div", { className: "app-mobile-nav-sections", children: sidebar }) })
|
|
110
|
+
] }) : /* @__PURE__ */ jsx(NavSurfaceProvider, { surface: "drawer", children: drawerNav })
|
|
75
111
|
}
|
|
76
112
|
)
|
|
77
113
|
]
|
|
@@ -88,9 +124,17 @@ function AppShell({
|
|
|
88
124
|
"data-responsive-navigation": responsiveNavigation,
|
|
89
125
|
"data-topbar": hasTopbarContent ? void 0 : "none",
|
|
90
126
|
"data-topbar-span": topbarSpan === "full" ? "full" : void 0,
|
|
127
|
+
"data-nav-rail": navRail !== void 0 ? "" : void 0,
|
|
91
128
|
children: [
|
|
92
|
-
topbarSpan === "full" ?
|
|
93
|
-
|
|
129
|
+
topbarSpan === "full" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
130
|
+
bar,
|
|
131
|
+
navRailRegion,
|
|
132
|
+
rail
|
|
133
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
134
|
+
navRailRegion,
|
|
135
|
+
rail,
|
|
136
|
+
bar
|
|
137
|
+
] }),
|
|
94
138
|
/* @__PURE__ */ jsxs("main", { className: "app-main", "aria-label": t("layout.appShell.mainLabel"), tabIndex: 0, children: [
|
|
95
139
|
breadcrumb !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-breadcrumb", children: breadcrumb }),
|
|
96
140
|
children
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FlexProp } from "../../props/components/layout.prop.js";
|
|
2
2
|
export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProp as FlexProps, } from "../../props/components/layout.prop.js";
|
|
3
|
-
export declare function Flex({ as: Element, direction, gap, align, justify, wrap, hideBelow, hideFrom, className, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
3
|
+
export declare function Flex({ as: Element, direction, gap, gapRaw, pad, padRaw, align, justify, wrap, hideBelow, hideFrom, fill, width, className, style, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
@@ -2,17 +2,24 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { mergeAriaIds } from "../../lib/field-a11y.js";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
-
import { flexGapClass } from "../../lib/variants.js";
|
|
5
|
+
import { flexGapClass, padStyle } from "../../lib/variants.js";
|
|
6
|
+
const toCssLength = (value) => typeof value === "number" ? `${value}px` : value;
|
|
6
7
|
function Flex({
|
|
7
8
|
as: Element = "div",
|
|
8
9
|
direction = "row",
|
|
9
10
|
gap = "md",
|
|
11
|
+
gapRaw,
|
|
12
|
+
pad,
|
|
13
|
+
padRaw,
|
|
10
14
|
align,
|
|
11
15
|
justify,
|
|
12
16
|
wrap = false,
|
|
13
17
|
hideBelow,
|
|
14
18
|
hideFrom,
|
|
19
|
+
fill = false,
|
|
20
|
+
width,
|
|
15
21
|
className,
|
|
22
|
+
style,
|
|
16
23
|
children,
|
|
17
24
|
...props
|
|
18
25
|
}) {
|
|
@@ -39,7 +46,17 @@ function Flex({
|
|
|
39
46
|
"data-wrap": wrap ? "true" : void 0,
|
|
40
47
|
"data-hide-below": hideBelow,
|
|
41
48
|
"data-hide-from": hideFrom,
|
|
42
|
-
|
|
49
|
+
"data-fill": fill ? "" : void 0,
|
|
50
|
+
"data-width-raw": width === void 0 ? void 0 : "",
|
|
51
|
+
"data-gap-raw": gapRaw,
|
|
52
|
+
className: cn("ui-flex", gapRaw === void 0 ? flexGapClass[gap] : void 0, className),
|
|
53
|
+
"data-pad-raw": padRaw === void 0 ? void 0 : "",
|
|
54
|
+
style: {
|
|
55
|
+
...style,
|
|
56
|
+
...gapRaw === void 0 ? void 0 : { gap: `${gapRaw}px` },
|
|
57
|
+
...width === void 0 ? void 0 : { inlineSize: toCssLength(width) },
|
|
58
|
+
...padStyle(pad, padRaw)
|
|
59
|
+
},
|
|
43
60
|
...domProps,
|
|
44
61
|
children
|
|
45
62
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* WHICH SURFACE a navigation component is rendering into.
|
|
4
|
+
*
|
|
5
|
+
* `AppShell` reuses the very same `sidebar` / `navRail` nodes for the mobile drawer that it docks
|
|
6
|
+
* on desktop — that is the point of the default, since a consumer should not have to build its
|
|
7
|
+
* navigation twice. But those nodes carry `collapsed`, and collapse is a DESKTOP answer: it trades
|
|
8
|
+
* labels for horizontal room in a docked column. A drawer has no such pressure, and below the
|
|
9
|
+
* breakpoint it is the ONLY navigation there is.
|
|
10
|
+
*
|
|
11
|
+
* Shipping both together produced a drawer of anonymous icons: a consumer collapsed its sidebar at
|
|
12
|
+
* 1280px, resized to 393px, opened the drawer, and got the organization mark plus five unlabelled
|
|
13
|
+
* glyphs — captured in that repo's own browser test, which is named "desktop sidebar collapse
|
|
14
|
+
* stays independent from the mobile navigation drawer" and had been asserting exactly this.
|
|
15
|
+
*
|
|
16
|
+
* The node cannot be un-collapsed from the outside once it is built, so the surface announces
|
|
17
|
+
* itself instead and the navigation components read it. Not exported from the public barrel: this
|
|
18
|
+
* is a contract between `AppShell` and the navigation it hosts, not a consumer knob.
|
|
19
|
+
*/
|
|
20
|
+
export type NavSurface = "docked" | "drawer";
|
|
21
|
+
export declare function NavSurfaceProvider({ surface, children, }: {
|
|
22
|
+
surface: NavSurface;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}): React.JSX.Element;
|
|
25
|
+
/** `"docked"` outside an AppShell drawer — so a standalone `Sidebar` keeps its own `collapsed`. */
|
|
26
|
+
export declare function useNavSurface(): NavSurface;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
const NavSurfaceContext = React.createContext("docked");
|
|
5
|
+
function NavSurfaceProvider({
|
|
6
|
+
surface,
|
|
7
|
+
children
|
|
8
|
+
}) {
|
|
9
|
+
return /* @__PURE__ */ jsx(NavSurfaceContext.Provider, { value: surface, children });
|
|
10
|
+
}
|
|
11
|
+
function useNavSurface() {
|
|
12
|
+
return React.useContext(NavSurfaceContext);
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
NavSurfaceProvider,
|
|
16
|
+
useNavSurface
|
|
17
|
+
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { OrgSwitcherProp } from "../../props/components/layout.prop.js";
|
|
3
3
|
export type { OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, OrgSwitcherProp as OrgSwitcherProps, } from "../../props/components/layout.prop.js";
|
|
4
|
+
/** Arbitrary `data-*` hooks — the one escape hatch this closed component keeps open. */
|
|
5
|
+
type DataAttributes = {
|
|
6
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
7
|
+
};
|
|
4
8
|
/**
|
|
5
9
|
* Organization switcher with one stable shell contract: canonical trigger geometry, an optional
|
|
6
10
|
* status badge, a searchable desktop popover, a focus-trapped bottom Sheet at/below the shared
|
|
7
11
|
* `--sheet-responsive-breakpoint-width`, and explicit loading/empty/error states.
|
|
8
12
|
*/
|
|
9
|
-
export declare function OrgSwitcher({ organizations, value, onValueChange, collapsed, disabled, loading, error, onRetry, labels, responsive, open, onOpenChange, className, }: OrgSwitcherProp): React.JSX.Element;
|
|
13
|
+
export declare function OrgSwitcher({ organizations, value, onValueChange, collapsed, disabled, loading, error, onRetry, labels, responsive, open, onOpenChange, className, ...rest }: OrgSwitcherProp & Pick<React.ComponentPropsWithoutRef<"button">, "id"> & DataAttributes): React.JSX.Element;
|
|
@@ -46,7 +46,7 @@ const OrgSwitcherTrigger = React.forwardRef(
|
|
|
46
46
|
ref,
|
|
47
47
|
type: "button",
|
|
48
48
|
variant: "ghost",
|
|
49
|
-
className: "ui-org-switcher-trigger",
|
|
49
|
+
className: cn("ui-org-switcher-trigger", "h-[length:var(--org-switcher-trigger-height)]"),
|
|
50
50
|
"data-collapsed": collapsed ? "true" : void 0,
|
|
51
51
|
disabled,
|
|
52
52
|
"aria-label": label,
|
|
@@ -147,7 +147,8 @@ function OrgSwitcher({
|
|
|
147
147
|
responsive = "auto",
|
|
148
148
|
open,
|
|
149
149
|
onOpenChange,
|
|
150
|
-
className
|
|
150
|
+
className,
|
|
151
|
+
...rest
|
|
151
152
|
}) {
|
|
152
153
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
|
|
153
154
|
const controlled = open !== void 0;
|
|
@@ -173,7 +174,8 @@ function OrgSwitcher({
|
|
|
173
174
|
organization: current,
|
|
174
175
|
collapsed,
|
|
175
176
|
disabled: triggerDisabled,
|
|
176
|
-
label: triggerLabel
|
|
177
|
+
label: triggerLabel,
|
|
178
|
+
...rest
|
|
177
179
|
}
|
|
178
180
|
);
|
|
179
181
|
const panel = /* @__PURE__ */ jsx(
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { Slot } from "
|
|
4
|
+
import { Slot } from "../../lib/slot.js";
|
|
5
5
|
import { ChevronDown } from "lucide-react";
|
|
6
6
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../data-display/collapsible.js";
|
|
7
7
|
import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
|
|
8
8
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../feedback/tooltip.js";
|
|
9
|
+
import { useNavSurface } from "./nav-surface.js";
|
|
9
10
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
10
11
|
import { cn } from "../../lib/utils.js";
|
|
11
12
|
function SidebarHeader({ children, className, ...props }) {
|
|
@@ -301,6 +302,8 @@ function Sidebar({
|
|
|
301
302
|
}) {
|
|
302
303
|
const { t } = useTranslation();
|
|
303
304
|
const resolvedSections = sections ?? [];
|
|
305
|
+
const surface = useNavSurface();
|
|
306
|
+
collapsed = surface === "drawer" ? false : collapsed;
|
|
304
307
|
return /* @__PURE__ */ jsxs("div", { className: "sb-root", "data-collapsed": collapsed ? "true" : void 0, children: [
|
|
305
308
|
brand !== void 0 ? /* @__PURE__ */ jsx(SidebarHeader, { children: brand }) : product ? (() => {
|
|
306
309
|
const interactive = onProductClick != null;
|
|
@@ -15,5 +15,18 @@ export type SplitPaneProps = {
|
|
|
15
15
|
asideWidth?: "sm" | "md" | "lg";
|
|
16
16
|
/** Accessible complementary landmark name; required when multiple panes share a document. */
|
|
17
17
|
asideLabel?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Fill the remaining height of the parent instead of growing with the content.
|
|
20
|
+
*
|
|
21
|
+
* Default `false` keeps the pane content-height — the right answer for a pane that sits in a
|
|
22
|
+
* scrolling page. Set it for an app-shell surface whose columns own their own scrolling (a chat
|
|
23
|
+
* transcript with a pinned composer, a full-height table beside a detail rail): the pane and
|
|
24
|
+
* both columns get a DEFINITE height, so `overflow` inside a column scrolls that column rather
|
|
25
|
+
* than pushing the page taller.
|
|
26
|
+
*
|
|
27
|
+
* The parent still decides how much height there is to fill — `fill` claims 100% of it, it does
|
|
28
|
+
* not invent it. Inside a flex column give the wrapper `flex: 1; min-height: 0`.
|
|
29
|
+
*/
|
|
30
|
+
fill?: boolean;
|
|
18
31
|
};
|
|
19
|
-
export declare function SplitPane({ children, aside, asideWidth, asideLabel }: SplitPaneProps): import("react").JSX.Element;
|
|
32
|
+
export declare function SplitPane({ children, aside, asideWidth, asideLabel, fill, }: SplitPaneProps): import("react").JSX.Element;
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
function SplitPane({
|
|
2
|
+
function SplitPane({
|
|
3
|
+
children,
|
|
4
|
+
aside,
|
|
5
|
+
asideWidth = "md",
|
|
6
|
+
asideLabel,
|
|
7
|
+
fill = false
|
|
8
|
+
}) {
|
|
3
9
|
const closed = aside === null || aside === void 0;
|
|
4
|
-
return
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
return (
|
|
11
|
+
// `data-fill` is published on BOTH wrappers, not just the grid: the scope element is the one
|
|
12
|
+
// the consumer's own box actually contains, so without it the height stops at the scope and
|
|
13
|
+
// the grid has nothing definite to fill. Two attributes here are what keeps the consumer from
|
|
14
|
+
// reaching past its own child with `[&>*>*]` to say the same thing.
|
|
15
|
+
/* @__PURE__ */ jsx("div", { className: "ui-split-pane-scope", "data-fill": fill ? "true" : void 0, children: /* @__PURE__ */ jsxs(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
className: "ui-split-pane",
|
|
19
|
+
"data-aside-width": asideWidth,
|
|
20
|
+
"data-aside": closed ? "closed" : void 0,
|
|
21
|
+
"data-fill": fill ? "true" : void 0,
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsx("div", { className: "ui-split-pane-main", children }),
|
|
24
|
+
closed ? null : /* @__PURE__ */ jsx("aside", { className: "ui-split-pane-aside", "aria-label": asideLabel, children: aside })
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
) })
|
|
28
|
+
);
|
|
16
29
|
}
|
|
17
30
|
export {
|
|
18
31
|
SplitPane
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
import { jsx } from "react/jsx-runtime";
|
|
3
2
|
import * as React from "react";
|
|
4
|
-
import { Slot } from "
|
|
3
|
+
import { Slot } from "../../lib/slot.js";
|
|
5
4
|
import { cn } from "../../lib/utils.js";
|
|
6
5
|
const TopbarItem = React.forwardRef(function TopbarItem2({ asChild = false, className, type, ...props }, ref) {
|
|
7
6
|
const Comp = asChild ? Slot : "button";
|
|
@@ -110,7 +110,8 @@ const AppSettingPicker = React.forwardRef(
|
|
|
110
110
|
}, [kind, ctx?.timezoneOptions, current, t, locale, fallbackLocale]);
|
|
111
111
|
const unbound = current === void 0 || !handleChange;
|
|
112
112
|
const Icon = ICON[kind];
|
|
113
|
-
const
|
|
113
|
+
const bar = resolvedAppearance === "bar";
|
|
114
|
+
const iconOnly = bar || resolvedAppearance === "icon";
|
|
114
115
|
const inline = resolvedAppearance === "inline";
|
|
115
116
|
const isCompact = compact && !inline;
|
|
116
117
|
return /* @__PURE__ */ jsxs(
|
|
@@ -164,7 +165,24 @@ const AppSettingPicker = React.forwardRef(
|
|
|
164
165
|
// thua lớp utilities mà SelectTrigger phát ra.
|
|
165
166
|
cn(
|
|
166
167
|
"ui-app-setting-picker-icon hover:bg-accent hover:text-accent-foreground",
|
|
167
|
-
"w-[length:var(--control-height)] shrink-0 justify-center"
|
|
168
|
+
"w-[length:var(--control-height)] shrink-0 justify-center",
|
|
169
|
+
// `bar` — a CELL of the bar, not a control in it. Both overrides are UTILITIES
|
|
170
|
+
// for the reason the block above already records twice: SelectTrigger emits
|
|
171
|
+
// `rounded-[var(--control-radius)]` from controlTriggerClass and takes its
|
|
172
|
+
// height from `.ui-control`, and a rule in @layer components loses to both.
|
|
173
|
+
//
|
|
174
|
+
// HEIGHT IS `self-stretch` + `h-auto`, NOT A LENGTH. The bar's height comes
|
|
175
|
+
// from a grid row in AppShell, from --topbar-height standalone, or from the
|
|
176
|
+
// coarse-pointer override on touch — a cell that named any one of those would
|
|
177
|
+
// be wrong in the other two. `h-auto` is what releases `.ui-control`'s height
|
|
178
|
+
// so the stretch can take effect; without it the box stays 32px in a 48px bar
|
|
179
|
+
// and the hover paints a pill, which is the whole defect this value fixes.
|
|
180
|
+
// `min-h` keeps the touch target when the bar is SHORTER than --control-height.
|
|
181
|
+
// The radius reads --topbar-item-radius, the SAME knob `TopbarItem` uses, so a
|
|
182
|
+
// service that wants softer bar cells retunes one token and both follow. A
|
|
183
|
+
// `rounded-none` literal here would put the shape out of a theme's reach
|
|
184
|
+
// (rules #44/#45) and let the two bar cells drift apart.
|
|
185
|
+
bar && "h-auto min-h-[length:var(--control-height)] self-stretch rounded-[var(--topbar-item-radius)]"
|
|
168
186
|
)
|
|
169
187
|
) : (
|
|
170
188
|
// Labeled: sized to a per-kind width from `sm` up; below `sm` it hugs its content and
|
|
@@ -180,6 +198,7 @@ const AppSettingPicker = React.forwardRef(
|
|
|
180
198
|
className
|
|
181
199
|
),
|
|
182
200
|
"data-kind": kind,
|
|
201
|
+
"data-appearance": resolvedAppearance,
|
|
183
202
|
"aria-label": t(ARIA_KEY[kind]),
|
|
184
203
|
children: [
|
|
185
204
|
inline ? null : /* @__PURE__ */ jsx(
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { AppSettingToggleProp } from "../../props/components/app.prop.js";
|
|
3
|
+
export type { AppSettingToggleKind, AppSettingToggleProp, AppSettingToggleProp as AppSettingToggleProps, } from "../../props/components/app.prop.js";
|
|
4
|
+
/**
|
|
5
|
+
* AppSettingToggle — ONE button that steps a single AppProvider setting to its next value.
|
|
6
|
+
*
|
|
7
|
+
* The no-menu counterpart to {@link AppSettingPicker}: same binding contract, same option order,
|
|
8
|
+
* one tap instead of open-then-choose. Reach for it when the value set is closed and short (theme,
|
|
9
|
+
* density, font size, clock format) and the control lives in a top bar, where a dropdown for three
|
|
10
|
+
* values is a menu nobody wanted to open.
|
|
11
|
+
*
|
|
12
|
+
* The accessible name carries BOTH the setting and its current value ("Theme: Dark"), because the
|
|
13
|
+
* glyph is the only visible state and a screen-reader user would otherwise be told only which
|
|
14
|
+
* setting they are standing on, never where it currently is.
|
|
15
|
+
*/
|
|
16
|
+
export declare const AppSettingToggle: React.ForwardRefExoticComponent<AppSettingToggleProp & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
AArrowDown,
|
|
6
|
+
AArrowUp,
|
|
7
|
+
ALargeSmall,
|
|
8
|
+
Clock,
|
|
9
|
+
Monitor,
|
|
10
|
+
Moon,
|
|
11
|
+
Rows2,
|
|
12
|
+
Rows3,
|
|
13
|
+
Rows4,
|
|
14
|
+
Sun
|
|
15
|
+
} from "lucide-react";
|
|
16
|
+
import { APP_TIME_FORMAT_OPTIONS, getTimeFormatLabel } from "../../app/time-format-labels.js";
|
|
17
|
+
import { APP_DENSITIES, APP_FONT_SIZES, APP_THEMES } from "../../app/theme-axes.js";
|
|
18
|
+
import { useOptionalAppContext } from "../../app/app-provider.js";
|
|
19
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
20
|
+
import { Button } from "../general/button.js";
|
|
21
|
+
import { TopbarItem } from "../layout/topbar-item.js";
|
|
22
|
+
const VALUES = {
|
|
23
|
+
theme: APP_THEMES,
|
|
24
|
+
density: APP_DENSITIES,
|
|
25
|
+
fontSize: APP_FONT_SIZES,
|
|
26
|
+
timeFormat: APP_TIME_FORMAT_OPTIONS.map((option) => option.value)
|
|
27
|
+
};
|
|
28
|
+
const VALUE_ICON = {
|
|
29
|
+
theme: { light: Sun, dark: Moon, system: Monitor },
|
|
30
|
+
density: { compact: Rows4, default: Rows3, comfortable: Rows2 },
|
|
31
|
+
fontSize: { sm: AArrowDown, default: ALargeSmall, lg: AArrowUp },
|
|
32
|
+
// 12h vs 24h is a NOTATION, not a picture: `Clock3`/`Clock12` differ only by hand position, which
|
|
33
|
+
// reads as a time of day. So this kind shows the kind glyph plus the value as text instead.
|
|
34
|
+
timeFormat: {}
|
|
35
|
+
};
|
|
36
|
+
const KIND_ICON = {
|
|
37
|
+
theme: Sun,
|
|
38
|
+
density: Rows3,
|
|
39
|
+
fontSize: ALargeSmall,
|
|
40
|
+
timeFormat: Clock
|
|
41
|
+
};
|
|
42
|
+
const ARIA_KEY = {
|
|
43
|
+
theme: "navigation.themePicker.ariaLabel",
|
|
44
|
+
density: "navigation.densityPicker.ariaLabel",
|
|
45
|
+
fontSize: "navigation.fontSizePicker.ariaLabel",
|
|
46
|
+
timeFormat: "navigation.timeFormatPicker.ariaLabel"
|
|
47
|
+
};
|
|
48
|
+
const AppSettingToggle = React.forwardRef(
|
|
49
|
+
function AppSettingToggle2({ kind, appearance = "bar", className, disabled, id, value, onValueChange }, ref) {
|
|
50
|
+
const ctx = useOptionalAppContext();
|
|
51
|
+
const { t, locale, fallbackLocale } = useTranslation();
|
|
52
|
+
const raw = value ?? ctx?.[kind];
|
|
53
|
+
const current = raw == null ? void 0 : String(raw);
|
|
54
|
+
const setter = ctx ? {
|
|
55
|
+
theme: ctx.setTheme,
|
|
56
|
+
density: ctx.setDensity,
|
|
57
|
+
fontSize: ctx.setFontSize,
|
|
58
|
+
timeFormat: ctx.setTimeFormat
|
|
59
|
+
}[kind] : void 0;
|
|
60
|
+
const handleChange = onValueChange ?? setter;
|
|
61
|
+
const unbound = current === void 0 || !handleChange;
|
|
62
|
+
const values = VALUES[kind];
|
|
63
|
+
const handleClick = () => {
|
|
64
|
+
if (current === void 0 || !handleChange) return;
|
|
65
|
+
handleChange(values[(values.indexOf(current) + 1) % values.length]);
|
|
66
|
+
};
|
|
67
|
+
const ValueIcon = current === void 0 ? void 0 : VALUE_ICON[kind][current];
|
|
68
|
+
const Icon = ValueIcon ?? KIND_ICON[kind];
|
|
69
|
+
const valueLabel = current === void 0 ? "" : kind === "timeFormat" ? getTimeFormatLabel(current, locale, fallbackLocale) : t(`navigation.${kind}Picker.${current}`);
|
|
70
|
+
const shared = {
|
|
71
|
+
ref,
|
|
72
|
+
id,
|
|
73
|
+
type: "button",
|
|
74
|
+
disabled: disabled || unbound,
|
|
75
|
+
onClick: handleClick,
|
|
76
|
+
"data-kind": kind,
|
|
77
|
+
// The button has no visible label, so this aria-label is its ONLY accessible name — and it
|
|
78
|
+
// must name the VALUE too, or a cycler announces the same string at every step.
|
|
79
|
+
"aria-label": t("navigation.settingToggle.label", {
|
|
80
|
+
setting: t(ARIA_KEY[kind]),
|
|
81
|
+
value: valueLabel
|
|
82
|
+
}),
|
|
83
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
84
|
+
/* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }),
|
|
85
|
+
ValueIcon ? null : /* @__PURE__ */ jsx("span", { children: current })
|
|
86
|
+
] })
|
|
87
|
+
};
|
|
88
|
+
if (appearance === "bar") {
|
|
89
|
+
return /* @__PURE__ */ jsx(TopbarItem, { className, ...shared });
|
|
90
|
+
}
|
|
91
|
+
return /* @__PURE__ */ jsx(Button, { variant: "ghost", size: ValueIcon ? "icon" : "sm", className, ...shared });
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
export {
|
|
95
|
+
AppSettingToggle
|
|
96
|
+
};
|