@godxjp/ui 17.0.0 → 18.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.
Files changed (78) hide show
  1. package/dist/components/data-display/card.d.ts +37 -1
  2. package/dist/components/data-display/card.js +7 -1
  3. package/dist/components/data-display/data-table.js +18 -3
  4. package/dist/components/data-display/empty-state.d.ts +1 -1
  5. package/dist/components/data-display/empty-state.js +5 -1
  6. package/dist/components/data-display/scroll-area.js +1 -1
  7. package/dist/components/data-display/table.js +14 -9
  8. package/dist/components/data-entry/calendar.js +4 -0
  9. package/dist/components/data-entry/cascader.d.ts +1 -1
  10. package/dist/components/data-entry/cascader.js +10 -1
  11. package/dist/components/data-entry/checkbox-group.d.ts +1 -1
  12. package/dist/components/data-entry/checkbox-group.js +13 -4
  13. package/dist/components/data-entry/color-picker.d.ts +1 -1
  14. package/dist/components/data-entry/color-picker.js +5 -2
  15. package/dist/components/data-entry/command.d.ts +2 -2
  16. package/dist/components/data-entry/date-picker.d.ts +1 -1
  17. package/dist/components/data-entry/date-picker.js +11 -1
  18. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  19. package/dist/components/data-entry/date-range-picker.js +11 -3
  20. package/dist/components/data-entry/form-field.js +7 -3
  21. package/dist/components/data-entry/month-picker.d.ts +1 -1
  22. package/dist/components/data-entry/month-picker.js +13 -1
  23. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  24. package/dist/components/data-entry/month-range-picker.js +11 -3
  25. package/dist/components/data-entry/number-input.d.ts +19 -2
  26. package/dist/components/data-entry/number-input.js +5 -7
  27. package/dist/components/data-entry/radio.d.ts +1 -1
  28. package/dist/components/data-entry/radio.js +11 -3
  29. package/dist/components/data-entry/search-input.d.ts +3 -2
  30. package/dist/components/data-entry/search-input.js +5 -2
  31. package/dist/components/data-entry/search-select.d.ts +1 -1
  32. package/dist/components/data-entry/search-select.js +51 -10
  33. package/dist/components/data-entry/select.d.ts +7 -0
  34. package/dist/components/data-entry/select.js +21 -3
  35. package/dist/components/data-entry/time-picker.d.ts +1 -1
  36. package/dist/components/data-entry/time-picker.js +11 -1
  37. package/dist/components/data-entry/transfer.d.ts +1 -1
  38. package/dist/components/data-entry/transfer.js +73 -59
  39. package/dist/components/data-entry/tree-select.d.ts +1 -1
  40. package/dist/components/data-entry/tree-select.js +10 -1
  41. package/dist/components/data-entry/upload.d.ts +1 -1
  42. package/dist/components/data-entry/upload.js +7 -2
  43. package/dist/components/layout/app-shell.d.ts +1 -1
  44. package/dist/components/layout/app-shell.js +42 -2
  45. package/dist/components/layout/breadcrumb.d.ts +7 -1
  46. package/dist/components/layout/breadcrumb.js +6 -2
  47. package/dist/components/layout/page-container.d.ts +1 -1
  48. package/dist/components/layout/page-container.js +32 -22
  49. package/dist/components/layout/responsive-grid.js +1 -1
  50. package/dist/components/layout/sidebar.d.ts +1 -1
  51. package/dist/components/layout/sidebar.js +44 -11
  52. package/dist/components/layout/split-pane.js +2 -2
  53. package/dist/components/navigation/app-setting-picker.js +8 -1
  54. package/dist/components/navigation/pagination.d.ts +1 -1
  55. package/dist/components/navigation/pagination.js +84 -95
  56. package/dist/components/navigation/tabs.js +21 -4
  57. package/dist/i18n/messages/en.json +3 -1
  58. package/dist/i18n/messages/ja.json +3 -1
  59. package/dist/i18n/messages/vi.json +3 -1
  60. package/dist/lib/field-a11y.d.ts +66 -0
  61. package/dist/lib/field-a11y.js +44 -0
  62. package/dist/props/components/data-display.prop.d.ts +16 -1
  63. package/dist/props/components/data-entry.prop.d.ts +69 -19
  64. package/dist/props/components/layout.prop.d.ts +42 -0
  65. package/dist/props/components/navigation.prop.d.ts +16 -0
  66. package/dist/props/registry.d.ts +8 -3
  67. package/dist/props/registry.js +21 -3
  68. package/dist/props/vocabulary/data.prop.d.ts +9 -0
  69. package/dist/props/vocabulary/interaction.prop.d.ts +1 -1
  70. package/dist/styles/base.css +1 -1
  71. package/dist/styles/layout.css +46 -9
  72. package/dist/styles/shell-layout.css +19 -0
  73. package/dist/styles/text-layout.css +1 -1
  74. package/dist/tokens/components/card.css +1 -1
  75. package/dist/tokens/foundation.css +3 -3
  76. package/package.json +8 -4
  77. package/scripts/ui-audit.mjs +2 -2
  78. package/scripts/visual-audit-rules.mjs +2 -2
@@ -3,4 +3,4 @@ export type { UploadProp, UploadProp as UploadProps, UploadFileItemProp, UploadV
3
3
  export type { UploadFileItem, UploadVariant, UploadCommitAction } from "./upload-types.js";
4
4
  export { collectUploadCommitActions, createUploadItem } from "./upload-types.js";
5
5
  export { useUploadDraft } from "./use-upload-draft.js";
6
- export declare function Upload({ variant, value, defaultValue, onValueChange, accept: acceptProp, multiple: multipleProp, maxCount: maxCountProp, maxSizeBytes, disabled, removable, onUpload, className, children, }: UploadProp): import("react/jsx-runtime").JSX.Element;
6
+ export declare function Upload({ variant, value, defaultValue, onValueChange, accept: acceptProp, multiple: multipleProp, maxCount: maxCountProp, maxSizeBytes, disabled, removable, onUpload, id, className, children, ...ariaProps }: UploadProp): import("react/jsx-runtime").JSX.Element;
@@ -5,6 +5,7 @@ import { Camera, ImagePlus, RotateCcw, Trash2, Upload as UploadIcon, X } from "l
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { formatBytes } from "../../lib/format.js";
7
7
  import { cn } from "../../lib/utils.js";
8
+ import { resolveFieldA11y } from "../../lib/field-a11y.js";
8
9
  import { controlIconClass } from "../../lib/control-styles.js";
9
10
  import { Button } from "../general/button.js";
10
11
  import { UploadCropDialog } from "./upload-crop-dialog.js";
@@ -84,10 +85,13 @@ function Upload({
84
85
  disabled,
85
86
  removable = true,
86
87
  onUpload,
88
+ id,
87
89
  className,
88
- children
90
+ children,
91
+ ...ariaProps
89
92
  }) {
90
93
  const { t } = useTranslation();
94
+ const inputA11y = resolveFieldA11y(ariaProps, t("dataEntry.upload.inputLabel"));
91
95
  const accept = acceptProp ?? defaultAcceptForVariant(variant);
92
96
  const maxCount = maxCountProp ?? defaultMaxCount(variant);
93
97
  const multiple = multipleProp ?? (maxCount === 1 ? false : true);
@@ -144,12 +148,13 @@ function Upload({
144
148
  "input",
145
149
  {
146
150
  ref: inputRef,
151
+ id,
147
152
  type: "file",
148
153
  className: "sr-only",
149
154
  accept,
150
155
  multiple: multiple && !isSingleAvatar,
151
156
  disabled,
152
- "aria-label": t("dataEntry.upload.inputLabel"),
157
+ ...inputA11y,
153
158
  onChange: (e) => {
154
159
  pickFiles(e.target.files);
155
160
  e.target.value = "";
@@ -1,3 +1,3 @@
1
1
  import type { AppShellProp } from "../../props/components/layout.prop.js";
2
2
  export type { AppShellProp, AppShellProp as AppShellProps, } from "../../props/components/layout.prop.js";
3
- export declare function AppShell({ sidebar, topbar, topbarLeft, topbarRight, logo, breadcrumb, footer, children, sidebarCollapsed, }: AppShellProp): import("react/jsx-runtime").JSX.Element;
3
+ export declare function AppShell({ sidebar, topbar, topbarLeft, topbarRight, logo, breadcrumb, footer, children, sidebarCollapsed, mobileNav, mobileNavLabel, mobileNavOpen, onMobileNavOpenChange, }: AppShellProp): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Menu } from "lucide-react";
3
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { Button } from "../general/button.js";
7
+ import { Sheet, SheetBody, SheetContent, SheetHeader, SheetTrigger } from "../feedback/sheet.js";
4
8
  function AppShell({
5
9
  sidebar,
6
10
  topbar,
@@ -10,9 +14,25 @@ function AppShell({
10
14
  breadcrumb,
11
15
  footer,
12
16
  children,
13
- sidebarCollapsed = false
17
+ sidebarCollapsed = false,
18
+ mobileNav,
19
+ mobileNavLabel,
20
+ mobileNavOpen,
21
+ onMobileNavOpenChange
14
22
  }) {
15
23
  const { t } = useTranslation();
24
+ const drawerNav = mobileNav !== void 0 ? mobileNav : sidebar;
25
+ const hasDrawer = drawerNav != null;
26
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
27
+ const drawerOpen = mobileNavOpen ?? uncontrolledOpen;
28
+ const setDrawerOpen = onMobileNavOpenChange ?? setUncontrolledOpen;
29
+ const handleDrawerClick = (event) => {
30
+ const target = event.target;
31
+ const hit = target.closest("a[href], button, [role='menuitem']");
32
+ if (hit && !hit.classList.contains("sb-nav-group-trigger")) {
33
+ setDrawerOpen(false);
34
+ }
35
+ };
16
36
  const resolvedTopbar = topbar !== void 0 ? topbar : /* @__PURE__ */ jsxs("div", { className: "app-topbar-rail", children: [
17
37
  logo !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-logo", children: logo }),
18
38
  topbarLeft !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-left", children: topbarLeft }),
@@ -21,7 +41,27 @@ function AppShell({
21
41
  ] });
22
42
  return /* @__PURE__ */ jsxs("div", { className: "app-root", "data-collapsed": sidebarCollapsed ? "true" : void 0, children: [
23
43
  /* @__PURE__ */ jsx("aside", { className: "app-sidebar", "aria-label": t("layout.appShell.sidebarLabel"), children: sidebar }),
24
- /* @__PURE__ */ jsx("header", { className: "app-topbar ui-scale-fixed", "aria-label": t("layout.appShell.headerLabel"), children: resolvedTopbar }),
44
+ /* @__PURE__ */ jsxs("header", { className: "app-topbar ui-scale-fixed", "aria-label": t("layout.appShell.headerLabel"), children: [
45
+ hasDrawer && /* @__PURE__ */ jsxs(Sheet, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
46
+ /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
47
+ Button,
48
+ {
49
+ type: "button",
50
+ variant: "ghost",
51
+ size: "sm",
52
+ className: "app-mobile-nav-trigger",
53
+ "aria-label": t("layout.appShell.openNav"),
54
+ "aria-haspopup": "dialog",
55
+ children: /* @__PURE__ */ jsx(Menu, { className: "size-5", "aria-hidden": "true" })
56
+ }
57
+ ) }),
58
+ /* @__PURE__ */ jsxs(SheetContent, { side: "left", className: "app-mobile-nav-drawer", children: [
59
+ /* @__PURE__ */ jsx(SheetHeader, { title: mobileNavLabel ?? t("layout.appShell.navLabel") }),
60
+ /* @__PURE__ */ jsx(SheetBody, { className: "app-mobile-nav-body", onClick: handleDrawerClick, children: drawerNav })
61
+ ] })
62
+ ] }),
63
+ resolvedTopbar
64
+ ] }),
25
65
  /* @__PURE__ */ jsxs("main", { className: "app-main", "aria-label": t("layout.appShell.mainLabel"), children: [
26
66
  breadcrumb !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-breadcrumb", children: breadcrumb }),
27
67
  children
@@ -3,5 +3,11 @@ import type { BreadcrumbProp } from "../../props/vocabulary/navigation.prop.js";
3
3
  export type BreadcrumbProps = {
4
4
  items: BreadcrumbProp;
5
5
  linkComponent?: ElementType;
6
+ /**
7
+ * Override the `<nav>` landmark's accessible name. Defaults to a localized "Breadcrumb".
8
+ * Multiple Breadcrumb instances on one page/view need a DISTINCT name each — two `<nav>`
9
+ * landmarks sharing one name/role fail axe's `landmark-unique` (WCAG 2.4.1 / 1.3.1).
10
+ */
11
+ "aria-label"?: string;
6
12
  };
7
- export declare function Breadcrumb({ items, linkComponent: LinkComponent }: BreadcrumbProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function Breadcrumb({ items, linkComponent: LinkComponent, "aria-label": ariaLabel, }: BreadcrumbProps): import("react/jsx-runtime").JSX.Element;
@@ -2,9 +2,13 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { ChevronRight } from "lucide-react";
4
4
  import { useTranslation } from "../../i18n/use-translation.js";
5
- function Breadcrumb({ items, linkComponent: LinkComponent = "a" }) {
5
+ function Breadcrumb({
6
+ items,
7
+ linkComponent: LinkComponent = "a",
8
+ "aria-label": ariaLabel
9
+ }) {
6
10
  const { t } = useTranslation();
7
- return /* @__PURE__ */ jsx("nav", { "aria-label": t("navigation.breadcrumb.ariaLabel"), className: "ui-breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: items.map((item, index) => {
11
+ return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel ?? t("navigation.breadcrumb.ariaLabel"), className: "ui-breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: items.map((item, index) => {
8
12
  const isLast = index === items.length - 1;
9
13
  return /* @__PURE__ */ jsxs("li", { className: "ui-breadcrumb-item", children: [
10
14
  item.to && !isLast ? /* @__PURE__ */ jsx(LinkComponent, { href: item.to, to: item.to, className: "ui-breadcrumb-link", children: item.label }) : /* @__PURE__ */ jsx("span", { className: "ui-breadcrumb-current", "aria-current": isLast ? "page" : void 0, children: item.label }),
@@ -2,7 +2,7 @@ import type { PageContainerProp, PageInsetProp } from "../../props/components/la
2
2
  export type { PageContainerProp, PageContainerProp as PageContainerProps, } 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-runtime").JSX.Element;
5
- declare function PageContainerRoot({ title, subtitle, extra, footer, breadcrumb, linkComponent: LinkComponent, density, variant, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react/jsx-runtime").JSX.Element;
5
+ declare function PageContainerRoot({ title, subtitle, extra, footer, breadcrumb, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react/jsx-runtime").JSX.Element;
6
6
  export declare const PageContainer: typeof PageContainerRoot & {
7
7
  Inset: typeof PageContainerInset;
8
8
  };
@@ -2,6 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { ChevronRight } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
5
6
  import { cn } from "../../lib/utils.js";
6
7
  import { densityClass, pageContainerVariantClass } from "../../lib/variants.js";
7
8
  function scrollParent(el) {
@@ -38,6 +39,7 @@ function PageContainerRoot({
38
39
  extra,
39
40
  footer,
40
41
  breadcrumb,
42
+ breadcrumbAriaLabel,
41
43
  linkComponent: LinkComponent = "a",
42
44
  density,
43
45
  variant = "default",
@@ -49,6 +51,7 @@ function PageContainerRoot({
49
51
  }) {
50
52
  const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
51
53
  const { headerRef, revealed } = useFooterReveal(reveal);
54
+ const { t } = useTranslation();
52
55
  return /* @__PURE__ */ jsxs(
53
56
  "div",
54
57
  {
@@ -66,28 +69,35 @@ function PageContainerRoot({
66
69
  ),
67
70
  children: [
68
71
  /* @__PURE__ */ jsxs("header", { ref: headerRef, className: "ui-page-header", children: [
69
- breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "ui-breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: breadcrumb.map((item, i) => {
70
- const isLast = i === breadcrumb.length - 1;
71
- return /* @__PURE__ */ jsxs("li", { className: "ui-inline-xs", children: [
72
- item.to && !isLast ? /* @__PURE__ */ jsx(
73
- LinkComponent,
74
- {
75
- href: item.to,
76
- to: item.to,
77
- className: "hover:text-foreground hover:underline",
78
- children: item.label
79
- }
80
- ) : /* @__PURE__ */ jsx(
81
- "span",
82
- {
83
- className: isLast ? "text-foreground" : "",
84
- "aria-current": isLast ? "page" : void 0,
85
- children: item.label
86
- }
87
- ),
88
- !isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
89
- ] }, i);
90
- }) }) }),
72
+ breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx(
73
+ "nav",
74
+ {
75
+ "aria-label": breadcrumbAriaLabel ?? t("navigation.breadcrumb.ariaLabel"),
76
+ className: "ui-breadcrumb",
77
+ children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: breadcrumb.map((item, i) => {
78
+ const isLast = i === breadcrumb.length - 1;
79
+ return /* @__PURE__ */ jsxs("li", { className: "ui-inline-xs", children: [
80
+ item.to && !isLast ? /* @__PURE__ */ jsx(
81
+ LinkComponent,
82
+ {
83
+ href: item.to,
84
+ to: item.to,
85
+ className: "hover:text-foreground hover:underline",
86
+ children: item.label
87
+ }
88
+ ) : /* @__PURE__ */ jsx(
89
+ "span",
90
+ {
91
+ className: isLast ? "text-foreground" : "",
92
+ "aria-current": isLast ? "page" : void 0,
93
+ children: item.label
94
+ }
95
+ ),
96
+ !isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
97
+ ] }, i);
98
+ }) })
99
+ }
100
+ ),
91
101
  /* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
92
102
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
93
103
  /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
@@ -14,7 +14,7 @@ function resolveColumns(columns) {
14
14
  };
15
15
  }
16
16
  function ResponsiveGrid({ columns = 4, children }) {
17
- return /* @__PURE__ */ jsx("div", { className: "ui-responsive-grid", style: resolveColumns(columns), children });
17
+ return /* @__PURE__ */ jsx("div", { className: "ui-responsive-grid-scope", children: /* @__PURE__ */ jsx("div", { className: "ui-responsive-grid", style: resolveColumns(columns), children }) });
18
18
  }
19
19
  export {
20
20
  ResponsiveGrid
@@ -18,4 +18,4 @@ type SidebarItemProps = {
18
18
  export declare function SidebarHeader({ children, className, ...props }: SidebarHeaderProps): import("react/jsx-runtime").JSX.Element;
19
19
  export declare function SidebarSection({ label, collapsed, children, className, ...props }: SidebarSectionProps & React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
20
20
  export declare function SidebarItem({ item, active, sub, onActivate, renderItem, children, ...props }: SidebarItemProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick">): import("react/jsx-runtime").JSX.Element;
21
- export declare function Sidebar({ activeId, onSelect, sections, product, onProductClick, brand, collapsed, children, renderItem, footer, }: SidebarProp): import("react/jsx-runtime").JSX.Element;
21
+ export declare function Sidebar({ activeId, onSelect, sections, product, onProductClick, brand, collapsed, children, renderItem, footer, "aria-label": ariaLabel, }: SidebarProp): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  "use client";
2
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import { Slot } from "@radix-ui/react-slot";
4
5
  import { ChevronDown } from "lucide-react";
5
6
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../data-display/collapsible.js";
6
7
  import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
@@ -34,25 +35,52 @@ function SidebarItem({
34
35
  const Icon = item.icon;
35
36
  const showBadge = item.badge !== void 0 && item.badge !== "";
36
37
  const disabled = item.disabled || props.disabled;
37
- const content = children ?? (renderItem ? renderItem(item) : void 0);
38
- return /* @__PURE__ */ jsx(
38
+ const custom = children ?? (renderItem ? renderItem(item) : void 0);
39
+ const rowClass = cn("sb-nav-item", sub && "sb-nav-item--sub");
40
+ const stateProps = {
41
+ className: rowClass,
42
+ "data-active": active ? "true" : void 0,
43
+ "aria-current": active ? "page" : void 0
44
+ };
45
+ if (custom !== void 0) {
46
+ return /* @__PURE__ */ jsx(Slot, { ...stateProps, "aria-disabled": disabled || void 0, children: custom });
47
+ }
48
+ if (item.href) {
49
+ return /* @__PURE__ */ jsxs(
50
+ "a",
51
+ {
52
+ ...props,
53
+ href: disabled ? void 0 : item.href,
54
+ ...stateProps,
55
+ "aria-disabled": disabled || void 0,
56
+ onClick: (event) => {
57
+ if (disabled) event.preventDefault();
58
+ else onActivate?.(item.id);
59
+ },
60
+ children: [
61
+ !sub ? /* @__PURE__ */ jsx("span", { className: "sb-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) }) : null,
62
+ /* @__PURE__ */ jsx("span", { className: "sb-label", children: item.label }),
63
+ showBadge ? /* @__PURE__ */ jsx("span", { className: "sb-badge", children: item.badge }) : null
64
+ ]
65
+ }
66
+ );
67
+ }
68
+ return /* @__PURE__ */ jsxs(
39
69
  "button",
40
70
  {
41
71
  type: "button",
42
- className: cn("sb-nav-item", sub && "sb-nav-item--sub"),
43
- "data-active": active ? "true" : void 0,
44
- "aria-current": active ? "page" : void 0,
72
+ ...stateProps,
45
73
  "aria-disabled": disabled,
46
74
  ...props,
47
75
  onClick: () => {
48
76
  if (disabled) return;
49
77
  onActivate?.(item.id);
50
78
  },
51
- children: content ? content : /* @__PURE__ */ jsxs(Fragment, { children: [
79
+ children: [
52
80
  !sub ? /* @__PURE__ */ jsx("span", { className: "sb-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) }) : null,
53
81
  /* @__PURE__ */ jsx("span", { className: "sb-label", children: item.label }),
54
82
  showBadge ? /* @__PURE__ */ jsx("span", { className: "sb-badge", children: item.badge }) : null
55
- ] })
83
+ ]
56
84
  }
57
85
  );
58
86
  }
@@ -95,7 +123,11 @@ function NavGroup({ item, activeId, onSelect, renderItem }) {
95
123
  const Icon = item.icon;
96
124
  const active = isItemActive(item, activeId);
97
125
  const children = item.children ?? [];
98
- return /* @__PURE__ */ jsxs(Collapsible, { defaultOpen: active, className: "sb-nav-group", children: [
126
+ const [open, setOpen] = React.useState(active);
127
+ React.useEffect(() => {
128
+ if (active) setOpen(true);
129
+ }, [active]);
130
+ return /* @__PURE__ */ jsxs(Collapsible, { open, onOpenChange: setOpen, className: "sb-nav-group", children: [
99
131
  /* @__PURE__ */ jsxs(
100
132
  CollapsibleTrigger,
101
133
  {
@@ -186,7 +218,8 @@ function Sidebar({
186
218
  collapsed = false,
187
219
  children,
188
220
  renderItem,
189
- footer
221
+ footer,
222
+ "aria-label": ariaLabel
190
223
  }) {
191
224
  const { t } = useTranslation();
192
225
  const resolvedSections = sections ?? [];
@@ -223,7 +256,7 @@ function Sidebar({
223
256
  meta
224
257
  ] });
225
258
  })() : null,
226
- /* @__PURE__ */ jsx("nav", { className: "sb-nav-scroll", "aria-label": t("layout.sidebar.ariaLabel"), children: children ?? resolvedSections.map((section, sectionIndex) => /* @__PURE__ */ jsx(
259
+ /* @__PURE__ */ jsx("nav", { className: "sb-nav-scroll", "aria-label": ariaLabel ?? t("layout.sidebar.ariaLabel"), children: children ?? resolvedSections.map((section, sectionIndex) => /* @__PURE__ */ jsx(
227
260
  SidebarSection,
228
261
  {
229
262
  label: section.label,
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  function SplitPane({ children, aside, asideWidth = "md" }) {
3
- return /* @__PURE__ */ jsxs("div", { className: "ui-split-pane", "data-aside-width": asideWidth, children: [
3
+ return /* @__PURE__ */ jsx("div", { className: "ui-split-pane-scope", children: /* @__PURE__ */ jsxs("div", { className: "ui-split-pane", "data-aside-width": asideWidth, children: [
4
4
  /* @__PURE__ */ jsx("div", { className: "ui-split-pane-main", children }),
5
5
  /* @__PURE__ */ jsx("aside", { className: "ui-split-pane-aside", children: aside })
6
- ] });
6
+ ] }) });
7
7
  }
8
8
  export {
9
9
  SplitPane
@@ -140,7 +140,14 @@ const AppSettingPicker = React.forwardRef(
140
140
  // the density-aware --control-height tap target, centre the icon and hide the
141
141
  // trailing chevron — no consumer descendant-selector overrides required.
142
142
  "w-[length:var(--control-height)] justify-center ps-0 pe-0 [&_[data-slot=select-chevron]]:hidden"
143
- ) : cn("w-full", TRIGGER_WIDTH[kind]),
143
+ ) : (
144
+ // Labeled: sized to a per-kind width from `sm` up; below `sm` it hugs its content and
145
+ // caps at the container (`w-auto max-w-full`) instead of the old UNCONDITIONAL
146
+ // `w-full` — so a labeled picker dropped into a narrow topbar no longer stretches to
147
+ // fill the bar (gh#165). A form field that wants a full-width control passes
148
+ // `className="w-full"`, which wins over `w-auto`.
149
+ cn("w-auto max-w-full", TRIGGER_WIDTH[kind])
150
+ ),
144
151
  className
145
152
  ),
146
153
  "aria-label": t(ARIA_KEY[kind]),
@@ -22,5 +22,5 @@ declare const PaginationNext: React.ForwardRefExoticComponent<Omit<Omit<React.De
22
22
  disabled?: boolean;
23
23
  href?: string;
24
24
  } & React.RefAttributes<HTMLButtonElement>>;
25
- export declare function Pagination({ value, total, pageSize, pageSizeOptions, showSizeChanger, showTotal, simple, disabled, className, onValueChange, }: PaginationProp): import("react/jsx-runtime").JSX.Element | null;
25
+ export declare function Pagination({ value, total, pageSize, pageSizeOptions, showSizeChanger, showTotal, hideOnSinglePage, simple, disabled, className, onValueChange, "aria-label": ariaLabel, }: PaginationProp): import("react/jsx-runtime").JSX.Element | null;
26
26
  export { PaginationContent, PaginationItem, PaginationLink, PaginationEllipsis, PaginationPrevious, PaginationNext, };
@@ -88,12 +88,15 @@ function Pagination({
88
88
  pageSizeOptions = [10, 20, 50, 100],
89
89
  showSizeChanger,
90
90
  showTotal,
91
+ hideOnSinglePage = true,
91
92
  simple,
92
93
  disabled,
93
94
  className,
94
- onValueChange
95
+ onValueChange,
96
+ "aria-label": ariaLabel
95
97
  }) {
96
98
  const { t } = useTranslation();
99
+ const navLabel = ariaLabel ?? t("navigation.pagination.ariaLabel");
97
100
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
98
101
  const safeCurrent = Math.min(Math.max(1, value), totalPages);
99
102
  const pages = buildPageRange(safeCurrent, totalPages);
@@ -106,112 +109,98 @@ function Pagination({
106
109
  total === 0 ? 0 : (safeCurrent - 1) * pageSize + 1,
107
110
  Math.min(safeCurrent * pageSize, total)
108
111
  ]) : showTotal ? t("navigation.pagination.total", { total }) : null;
109
- if (total <= pageSize) return null;
112
+ if (total <= 0) return null;
113
+ if (hideOnSinglePage && totalPages <= 1) return null;
110
114
  if (simple) {
111
- return /* @__PURE__ */ jsxs(
112
- "nav",
115
+ return /* @__PURE__ */ jsxs("nav", { "aria-label": navLabel, "data-simple": "true", className: cn("ui-pagination", className), children: [
116
+ totalLabel && /* @__PURE__ */ jsx("span", { className: "ui-pagination-total", children: totalLabel }),
117
+ /* @__PURE__ */ jsx(
118
+ Button,
119
+ {
120
+ type: "button",
121
+ variant: "outline",
122
+ size: "sm",
123
+ disabled: Boolean(disabled) || safeCurrent <= 1,
124
+ "aria-label": t("navigation.pagination.prev"),
125
+ onClick: () => go(safeCurrent - 1),
126
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4", "aria-hidden": "true" })
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsxs("span", { className: "ui-pagination-count", children: [
130
+ safeCurrent,
131
+ " / ",
132
+ totalPages
133
+ ] }),
134
+ /* @__PURE__ */ jsx(
135
+ Button,
136
+ {
137
+ type: "button",
138
+ variant: "outline",
139
+ size: "sm",
140
+ disabled: Boolean(disabled) || safeCurrent >= totalPages,
141
+ "aria-label": t("navigation.pagination.next"),
142
+ onClick: () => go(safeCurrent + 1),
143
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": "true" })
144
+ }
145
+ )
146
+ ] });
147
+ }
148
+ return /* @__PURE__ */ jsxs("nav", { "aria-label": navLabel, className: cn("ui-pagination", className), children: [
149
+ totalLabel && /* @__PURE__ */ jsx("span", { className: "ui-pagination-total", children: totalLabel }),
150
+ showSizeChanger && /* @__PURE__ */ jsxs(
151
+ Select,
113
152
  {
114
- "aria-label": t("navigation.pagination.ariaLabel"),
115
- "data-simple": "true",
116
- className: cn("ui-pagination", className),
153
+ value: String(pageSize),
154
+ onValueChange: (v) => go(1, Number(v)),
155
+ disabled,
117
156
  children: [
118
- totalLabel && /* @__PURE__ */ jsx("span", { className: "ui-pagination-total", children: totalLabel }),
119
157
  /* @__PURE__ */ jsx(
120
- Button,
158
+ SelectTrigger,
121
159
  {
122
- type: "button",
123
- variant: "outline",
124
- size: "sm",
125
- disabled: Boolean(disabled) || safeCurrent <= 1,
126
- "aria-label": t("navigation.pagination.prev"),
127
- onClick: () => go(safeCurrent - 1),
128
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4", "aria-hidden": "true" })
160
+ className: "ui-pagination-size-trigger w-[var(--pagination-size-width)]",
161
+ "aria-label": t("navigation.pagination.pageSize"),
162
+ children: /* @__PURE__ */ jsx(SelectValue, {})
129
163
  }
130
164
  ),
131
- /* @__PURE__ */ jsxs("span", { className: "ui-pagination-count", children: [
132
- safeCurrent,
133
- " / ",
134
- totalPages
135
- ] }),
136
- /* @__PURE__ */ jsx(
137
- Button,
138
- {
139
- type: "button",
140
- variant: "outline",
141
- size: "sm",
142
- disabled: Boolean(disabled) || safeCurrent >= totalPages,
143
- "aria-label": t("navigation.pagination.next"),
144
- onClick: () => go(safeCurrent + 1),
145
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": "true" })
146
- }
147
- )
165
+ /* @__PURE__ */ jsx(SelectContent, { children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx(SelectItem, { value: String(size), children: t("navigation.pagination.pageSizeOption", { size }) }, size)) })
148
166
  ]
149
167
  }
150
- );
151
- }
152
- return /* @__PURE__ */ jsxs(
153
- "nav",
154
- {
155
- "aria-label": t("navigation.pagination.ariaLabel"),
156
- className: cn("ui-pagination", className),
157
- children: [
158
- totalLabel && /* @__PURE__ */ jsx("span", { className: "ui-pagination-total", children: totalLabel }),
159
- showSizeChanger && /* @__PURE__ */ jsxs(
160
- Select,
168
+ ),
169
+ /* @__PURE__ */ jsxs(PaginationContent, { children: [
170
+ /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(
171
+ PaginationPrevious,
172
+ {
173
+ "aria-label": t("navigation.pagination.prev"),
174
+ onClick: () => go(safeCurrent - 1),
175
+ disabled: Boolean(disabled) || safeCurrent <= 1,
176
+ children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" })
177
+ }
178
+ ) }),
179
+ pages.map(
180
+ (page, index) => page === "ellipsis" ? /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(PaginationEllipsis, {}) }, `e-${index}`) : /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(
181
+ PaginationLink,
161
182
  {
162
- value: String(pageSize),
163
- onValueChange: (v) => go(1, Number(v)),
183
+ isActive: page === safeCurrent,
184
+ "aria-label": t("navigation.pagination.page", { page }),
185
+ onClick: () => {
186
+ if (!disabled) go(page);
187
+ },
164
188
  disabled,
165
- children: [
166
- /* @__PURE__ */ jsx(
167
- SelectTrigger,
168
- {
169
- className: "ui-pagination-size-trigger w-[var(--pagination-size-width)]",
170
- "aria-label": t("navigation.pagination.pageSize"),
171
- children: /* @__PURE__ */ jsx(SelectValue, {})
172
- }
173
- ),
174
- /* @__PURE__ */ jsx(SelectContent, { children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx(SelectItem, { value: String(size), children: t("navigation.pagination.pageSizeOption", { size }) }, size)) })
175
- ]
189
+ children: page
176
190
  }
177
- ),
178
- /* @__PURE__ */ jsxs(PaginationContent, { children: [
179
- /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(
180
- PaginationPrevious,
181
- {
182
- "aria-label": t("navigation.pagination.prev"),
183
- onClick: () => go(safeCurrent - 1),
184
- disabled: Boolean(disabled) || safeCurrent <= 1,
185
- children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" })
186
- }
187
- ) }),
188
- pages.map(
189
- (page, index) => page === "ellipsis" ? /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(PaginationEllipsis, {}) }, `e-${index}`) : /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(
190
- PaginationLink,
191
- {
192
- isActive: page === safeCurrent,
193
- "aria-label": t("navigation.pagination.page", { page }),
194
- onClick: () => {
195
- if (!disabled) go(page);
196
- },
197
- disabled,
198
- children: page
199
- }
200
- ) }, page)
201
- ),
202
- /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(
203
- PaginationNext,
204
- {
205
- "aria-label": t("navigation.pagination.next"),
206
- onClick: () => go(safeCurrent + 1),
207
- disabled: Boolean(disabled) || safeCurrent >= totalPages,
208
- children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })
209
- }
210
- ) })
211
- ] })
212
- ]
213
- }
214
- );
191
+ ) }, page)
192
+ ),
193
+ /* @__PURE__ */ jsx(PaginationItem, { children: /* @__PURE__ */ jsx(
194
+ PaginationNext,
195
+ {
196
+ "aria-label": t("navigation.pagination.next"),
197
+ onClick: () => go(safeCurrent + 1),
198
+ disabled: Boolean(disabled) || safeCurrent >= totalPages,
199
+ children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })
200
+ }
201
+ ) })
202
+ ] })
203
+ ] });
215
204
  }
216
205
  export {
217
206
  Pagination,