@godxjp/ui 33.20.0 → 33.22.1

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 (116) hide show
  1. package/README.md +4 -4
  2. package/agent/START-HERE.md +7 -7
  3. package/agent/components/AppSettingPicker.json +0 -1
  4. package/agent/components/AppShell.json +11 -1
  5. package/agent/components/BlockEditor.json +6 -1
  6. package/agent/components/Button.json +1 -1
  7. package/agent/components/CardShowcase.json +44 -0
  8. package/agent/components/CatalogCard.json +83 -0
  9. package/agent/components/CatalogDetail.json +76 -0
  10. package/agent/components/CatalogDetailSection.json +52 -0
  11. package/agent/components/CatalogDetailTag.json +27 -0
  12. package/agent/components/EditorialArticle.json +53 -0
  13. package/agent/components/EditorialAuth.json +53 -0
  14. package/agent/components/EditorialAuthCard.json +52 -0
  15. package/agent/components/EditorialBanner.json +53 -0
  16. package/agent/components/EditorialBreadcrumb.json +38 -0
  17. package/agent/components/EditorialCard.json +70 -0
  18. package/agent/components/EditorialEmptyPanel.json +57 -0
  19. package/agent/components/EditorialFilterBar.json +51 -0
  20. package/agent/components/EditorialFilterField.json +39 -0
  21. package/agent/components/EditorialFilterPanel.json +46 -0
  22. package/agent/components/EditorialFooter.json +54 -0
  23. package/agent/components/EditorialFootnote.json +36 -0
  24. package/agent/components/EditorialHero.json +52 -0
  25. package/agent/components/EditorialInlineSearch.json +54 -0
  26. package/agent/components/EditorialMasthead.json +52 -0
  27. package/agent/components/EditorialMetaBar.json +37 -0
  28. package/agent/components/EditorialPageHeader.json +73 -0
  29. package/agent/components/EditorialPanel.json +48 -0
  30. package/agent/components/EditorialPillNav.json +41 -0
  31. package/agent/components/EditorialPrompt.json +54 -0
  32. package/agent/components/EditorialSearch.json +112 -0
  33. package/agent/components/EditorialSection.json +68 -0
  34. package/agent/components/EditorialSectionHead.json +62 -0
  35. package/agent/components/EditorialStats.json +32 -0
  36. package/agent/components/EditorialTextButton.json +47 -0
  37. package/agent/components/EditorialToggleGroup.json +52 -0
  38. package/agent/components/EditorialToolbar.json +45 -0
  39. package/agent/components/Eyebrow.json +39 -0
  40. package/agent/components/HoldingCard.json +69 -0
  41. package/agent/components/HoldingCardAction.json +51 -0
  42. package/agent/components/HoldingGrid.json +37 -0
  43. package/agent/components/Markdown.json +6 -0
  44. package/agent/components/NumberInput.json +5 -0
  45. package/agent/components/PriceSummary.json +32 -0
  46. package/agent/components/SettingsLayout.json +31 -0
  47. package/agent/components/SettingsPanel.json +53 -0
  48. package/agent/components/ThemePicker.json +54 -0
  49. package/agent/components/TopbarItem.json +5 -0
  50. package/agent/components-index.json +292 -98
  51. package/agent/components.json +2059 -4
  52. package/agent/index.json +7 -7
  53. package/agent/llms.txt +8 -8
  54. package/agent/tokens.json +5640 -132
  55. package/dist/components/data-display/badge.js +4 -2
  56. package/dist/components/data-display/image.js +6 -0
  57. package/dist/components/data-entry/form-field.js +2 -2
  58. package/dist/components/data-entry/number-input.d.ts +1 -0
  59. package/dist/components/data-entry/number-input.js +8 -0
  60. package/dist/components/data-entry/select.js +24 -2
  61. package/dist/components/general/button.js +14 -1
  62. package/dist/components/general/typography.d.ts +1 -1
  63. package/dist/components/layout/app-shell.d.ts +1 -1
  64. package/dist/components/layout/app-shell.js +28 -2
  65. package/dist/components/layout/centered-shell.js +4 -0
  66. package/dist/components/layout/editorial-account.d.ts +51 -0
  67. package/dist/components/layout/editorial-account.js +466 -0
  68. package/dist/components/layout/editorial-catalog.d.ts +18 -0
  69. package/dist/components/layout/editorial-catalog.js +164 -0
  70. package/dist/components/layout/editorial.d.ts +56 -0
  71. package/dist/components/layout/editorial.js +867 -0
  72. package/dist/components/layout/index.d.ts +6 -0
  73. package/dist/components/layout/index.js +98 -0
  74. package/dist/components/layout/mobile-shell.d.ts +2 -0
  75. package/dist/components/layout/mobile-shell.js +18 -11
  76. package/dist/components/layout/responsive-grid.js +9 -1
  77. package/dist/components/layout/topbar-item.d.ts +1 -0
  78. package/dist/components/layout/topbar-item.js +2 -0
  79. package/dist/contracts/measurement.json +1 -1
  80. package/dist/lab/legal-document-shell.js +61 -51
  81. package/dist/lib/toast-clearance.js +1 -1
  82. package/dist/props/components/data-display.prop.d.ts +2 -0
  83. package/dist/props/components/data-entry.prop.d.ts +9 -0
  84. package/dist/props/components/editorial-account.prop.d.ts +249 -0
  85. package/dist/props/components/editorial-account.prop.js +0 -0
  86. package/dist/props/components/general.prop.d.ts +6 -2
  87. package/dist/props/components/layout.prop.d.ts +602 -2
  88. package/dist/props/registry.d.ts +1124 -0
  89. package/dist/props/registry.js +792 -0
  90. package/dist/styles/badge-layout.css +2 -0
  91. package/dist/styles/control.css +60 -13
  92. package/dist/styles/core.css +1 -0
  93. package/dist/styles/data-display-layout.css +9 -3
  94. package/dist/styles/editorial-account-layout.css +1130 -0
  95. package/dist/styles/focus-ring.css +33 -10
  96. package/dist/styles/form-layout.css +8 -1
  97. package/dist/styles/index.css +1 -0
  98. package/dist/styles/layers.json +248 -1
  99. package/dist/styles/layout.css +2031 -0
  100. package/dist/styles/logo-layout.css +27 -0
  101. package/dist/styles/shell-layout.css +101 -3
  102. package/dist/styles/table-layout.css +25 -13
  103. package/dist/styles/text-layout.css +6 -0
  104. package/dist/styles.css +1 -1
  105. package/dist/tokens/base.css +2 -0
  106. package/dist/tokens/components/badge.css +3 -0
  107. package/dist/tokens/components/control.css +9 -0
  108. package/dist/tokens/components/editorial-account.css +393 -0
  109. package/dist/tokens/components/editorial.css +721 -0
  110. package/dist/tokens/components/form.css +4 -0
  111. package/dist/tokens/components/logo.css +2 -0
  112. package/dist/tokens/components/shell.css +23 -0
  113. package/dist/tokens/components/table.css +1 -1
  114. package/docs/TOKENS.md +26 -0
  115. package/package.json +2 -2
  116. 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: "text-foreground",
66
- dashed: "border-dashed text-foreground",
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
@@ -429,6 +429,9 @@ const ImageRoot = React.forwardRef(function Image({
429
429
  className,
430
430
  onLoad,
431
431
  onError,
432
+ fetchPriority,
433
+ loading = fetchPriority === "high" ? "eager" : "lazy",
434
+ decoding = "async",
432
435
  ...imgProps
433
436
  }, ref) {
434
437
  const { t } = useTranslation();
@@ -486,6 +489,9 @@ const ImageRoot = React.forwardRef(function Image({
486
489
  "img",
487
490
  {
488
491
  ...imgProps,
492
+ loading,
493
+ decoding,
494
+ fetchPriority,
489
495
  width,
490
496
  height,
491
497
  ref: setImgRef,
@@ -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 text-xs", children: helper }) : null;
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 text-xs", children: error }) : null
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 }) : void 0 }),
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
  }
@@ -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,
@@ -94,11 +99,15 @@ const Button = React.forwardRef(
94
99
  disabled,
95
100
  type,
96
101
  children,
102
+ onClick,
97
103
  ...props
98
104
  }, ref) => {
99
105
  const { locale } = useTranslation();
100
106
  const Comp = asChild ? Slot : "button";
101
107
  const isLoading = !asChild && loading;
108
+ const handleClick = isLoading ? (event) => {
109
+ event.preventDefault();
110
+ } : onClick;
102
111
  const content = isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
103
112
  /* @__PURE__ */ jsx(Loader2, { className: "animate-spin", "aria-hidden": "true" }),
104
113
  loadingText ?? children
@@ -108,6 +117,7 @@ const Button = React.forwardRef(
108
117
  const spokenCount = countLabel ? `${countText} ${countLabel}` : countText;
109
118
  const ariaLabel = props["aria-label"];
110
119
  const foldedAriaLabel = showCount && ariaLabel != null ? { "aria-label": `${ariaLabel}, ${spokenCount}` } : null;
120
+ const labelled = asChild ? React.isValidElement(children) && hasDirectText(children.props.children) : hasDirectText(loadingText ?? children);
111
121
  const countNode = showCount ? /* @__PURE__ */ jsxs(Fragment, { children: [
112
122
  /* @__PURE__ */ jsx("span", { "data-slot": "button-count", className: "ui-button-count", "aria-hidden": "true", children: countText }),
113
123
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: `, ${spokenCount}` })
@@ -124,8 +134,11 @@ const Button = React.forwardRef(
124
134
  "data-wrap": wrap ? "" : void 0,
125
135
  "data-align": align === "center" ? void 0 : align,
126
136
  "data-loading": isLoading ? "" : void 0,
137
+ "data-label": labelled ? "" : void 0,
127
138
  "aria-busy": isLoading || void 0,
128
- disabled: isLoading || disabled,
139
+ "aria-disabled": isLoading && !disabled || void 0,
140
+ disabled,
141
+ onClick: handleClick,
129
142
  type: asChild ? void 0 : type ?? "button",
130
143
  className: cn(
131
144
  fullWidth && "w-full",
@@ -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
- footer !== void 0 && /* @__PURE__ */ jsx("footer", { className: "app-footer", "aria-label": t("layout.appShell.footerLabel"), children: footer })
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;