@apliteni/apliteni-ui 0.67.0 → 0.69.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.67.0",
3
+ "version": "0.69.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
package/react/README.md CHANGED
@@ -29,7 +29,7 @@ import '@apliteni/apliteni-ui/react/css'; // React components' shell styles (mo
29
29
  import { DataTable, Modal, Button } from '@apliteni/apliteni-ui/react';
30
30
  ```
31
31
 
32
- Components: `Success`, `SuccessPanel`, `SuccessCheck`, `Checkbox`, `Switch`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
32
+ Components: `Success`, `SuccessPanel`, `SuccessCheck`, `Checkbox`, `Switch`, `SearchField`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
33
33
 
34
34
  `Pill` renders metadata with the existing pill spacing. Omit `variant` for neutral
35
35
  metadata or use `live` or `soon`. Its children accept React content.
@@ -477,6 +477,27 @@ class and invalid styling as appropriate:
477
477
  The frame supplies a stable ID, required state, and linked hint/error messages.
478
478
  An optional `id` lets the caller choose the control ID; it must be unique.
479
479
 
480
+ ### Search
481
+
482
+ `SearchField` is the toolbar's search box: a search glyph, a native
483
+ `type="search"` input, and no visible label, so the row keeps the height of the
484
+ unlabelled controls beside it. `ariaLabel` is required and is the control's only
485
+ name. It forwards its ref and the native input props, and `className` joins
486
+ `.ui-input`. There is no clear button — see the specification for why.
487
+
488
+ ```tsx
489
+ <div className="ui-toolbar">
490
+ <SearchField ariaLabel="Search invoices" placeholder="Vendor or number"
491
+ value={query} onChange={e => setQuery(e.currentTarget.value)} />
492
+ {/* then the filters, then the view switch */}
493
+ </div>
494
+ ```
495
+
496
+ Put it first in a `.ui-toolbar`. Above 560px it is the control there that grows
497
+ into the slack the others leave; at 560px and below it takes a line of its own
498
+ and the rest of the row wraps under it. Use `TextField` instead wherever the
499
+ field stands in a form and a visible label belongs over it.
500
+
480
501
  ## Toast
481
502
 
482
503
  Wrap your app in `Toast`, then call `useToast()` inside it to add a notice.
@@ -483,8 +483,8 @@ declare function Segmented({ options, value, onChange, label, appearance, disabl
483
483
 
484
484
  type Filter = {
485
485
  id: string;
486
- label: string;
487
- value: string;
486
+ label: string; /** empty or absent while nothing is chosen; the chip then shows `label` */
487
+ value?: string;
488
488
  items: DropdownEntry[];
489
489
  disabled?: boolean;
490
490
  open?: boolean;
@@ -602,6 +602,29 @@ type FileFieldProps = FieldMessage & Omit<InputHTMLAttributes<HTMLInputElement>,
602
602
  };
603
603
  declare function FileField({ label, hint, error, required, disabled, onFileChange, className, ...props }: FileFieldProps): react.JSX.Element;
604
604
 
605
+ type SearchFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'aria-label'> & {
606
+ /** The control's name. A toolbar search box shows no label, so this is the
607
+ only name it has. */
608
+ ariaLabel: string;
609
+ };
610
+ /**
611
+ * The toolbar's search box: one `.ui-input-group` holding the `search` glyph and
612
+ * a native `type="search"` control on `.ui-input`. It has no visible label, so
613
+ * the row keeps the height of the unlabelled controls beside it, and no CSS of
614
+ * its own, so the toolbar's row rule and the field's focus ring reach it from
615
+ * the kit's stylesheet.
616
+ *
617
+ * No clear button. The browser paints its own near-black on the light field and
618
+ * white on the dark one, beside the kit's `--muted` magnifier — two glyphs, and
619
+ * only one answers to a token — so `input.css` suppresses it.
620
+ * why: docs/specification.md#react-search-field
621
+ */
622
+ declare const SearchField: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "aria-label"> & {
623
+ /** The control's name. A toolbar search box shows no label, so this is the
624
+ only name it has. */
625
+ ariaLabel: string;
626
+ } & react.RefAttributes<HTMLInputElement>>;
627
+
605
628
  type ToastNotice = {
606
629
  tone?: 'success' | 'danger' | 'warn' | 'info' | 'neutral';
607
630
  title: string;
@@ -799,4 +822,4 @@ declare const SidebarNav: react.ForwardRefExoticComponent<Omit<Omit<react.Detail
799
822
  declare const PAGE_SIZES: readonly number[];
800
823
  declare const DEFAULT_PAGE_SIZE: number;
801
824
 
802
- export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
825
+ export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, SearchField, type SearchFieldProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Success, SuccessCheck, type SuccessCheckProps, type SuccessCountdown, SuccessPanel, type SuccessPanelProps, type SuccessProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
@@ -79,7 +79,7 @@ var cx = (...a) => a.filter(Boolean).join(" ");
79
79
  var Button = forwardRef3(function Button2({
80
80
  variant = "secondary",
81
81
  size = "md",
82
- icon: icon6,
82
+ icon: icon7,
83
83
  leading,
84
84
  iconRight,
85
85
  iconOnly,
@@ -143,7 +143,7 @@ var Button = forwardRef3(function Button2({
143
143
  className
144
144
  );
145
145
  const labelled = rest["aria-label"] != null || rest["aria-labelledby"] != null;
146
- const fallback = typeof children === "string" && children.trim() ? children.trim() : icon6;
146
+ const fallback = typeof children === "string" && children.trim() ? children.trim() : icon7;
147
147
  const named = iconOnly && !labelled && fallback ? { "aria-label": fallback, title: rest.title ?? fallback } : {};
148
148
  const Root = href === void 0 ? "button" : "a";
149
149
  return /* @__PURE__ */ jsxs3(
@@ -188,7 +188,7 @@ var Button = forwardRef3(function Button2({
188
188
  if (!blockKey(event)) onKeyUp?.(event);
189
189
  },
190
190
  children: [
191
- leading != null ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: leading }) : icon6 && /* @__PURE__ */ jsx4(Icon, { name: icon6 }),
191
+ leading != null ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: leading }) : icon7 && /* @__PURE__ */ jsx4(Icon, { name: icon7 }),
192
192
  !iconOnly && renderedChildren != null && /* @__PURE__ */ jsx4("span", { className: "ui-btn__label-slot", children: /* @__PURE__ */ jsx4("span", { className: "ui-btn__label", ref: labelRef, children: renderedChildren }) }),
193
193
  iconRight && /* @__PURE__ */ jsx4(Icon, { name: iconRight }),
194
194
  busy && /* @__PURE__ */ jsxs3("span", { className: "ui-btn__dots", "aria-hidden": "true", children: [
@@ -258,9 +258,9 @@ function Card({ title, sub, level = 2, children }) {
258
258
  // src/primitives/Callout.tsx
259
259
  import { calloutIcons, icon as kitIcon } from "@apliteni/apliteni-ui";
260
260
  import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
261
- function Callout({ variant, icon: icon6 = calloutIcons[variant ?? "neutral"], children, actions }) {
261
+ function Callout({ variant, icon: icon7 = calloutIcons[variant ?? "neutral"], children, actions }) {
262
262
  return /* @__PURE__ */ jsxs5("div", { className: `ui-callout${variant && variant !== "neutral" ? ` ui-callout--${variant}` : ""}`, role: variant === "danger" ? "alert" : void 0, children: [
263
- /* @__PURE__ */ jsx9("span", { className: "ui-callout__icon", dangerouslySetInnerHTML: { __html: kitIcon(icon6) } }),
263
+ /* @__PURE__ */ jsx9("span", { className: "ui-callout__icon", dangerouslySetInnerHTML: { __html: kitIcon(icon7) } }),
264
264
  /* @__PURE__ */ jsxs5("div", { className: "ui-callout__body", children: [
265
265
  children,
266
266
  actions != null && /* @__PURE__ */ jsx9("div", { className: "ui-toolbar ui-callout__actions", children: actions })
@@ -1429,12 +1429,12 @@ function Denied({
1429
1429
  title = "You don\u2019t have access",
1430
1430
  sub,
1431
1431
  need,
1432
- icon: icon6 = "lock",
1432
+ icon: icon7 = "lock",
1433
1433
  className,
1434
1434
  children
1435
1435
  }) {
1436
1436
  return /* @__PURE__ */ jsxs14("div", { className: cx4("ui-denied", className), children: [
1437
- /* @__PURE__ */ jsx18("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx18(Icon, { name: icon6 }) }),
1437
+ /* @__PURE__ */ jsx18("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx18(Icon, { name: icon7 }) }),
1438
1438
  /* @__PURE__ */ jsx18("div", { className: "ui-denied__title", children: title }),
1439
1439
  sub && /* @__PURE__ */ jsx18("div", { className: "ui-denied__sub", children: sub }),
1440
1440
  need && /* @__PURE__ */ jsxs14("div", { className: "ui-denied__need", children: [
@@ -1938,6 +1938,7 @@ function Segmented({ options, value, onChange, label, appearance = "pill", disab
1938
1938
 
1939
1939
  // src/FilterBar.tsx
1940
1940
  import { useRef as useRef11, useState as useState7 } from "react";
1941
+ import { filterChipText, filterChipName, filterChipUnset } from "@apliteni/apliteni-ui";
1941
1942
  import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
1942
1943
  function FilterBar({ filters, label = "Filters", clearLabel = "Clear all filters", disabled = false, busy = false, onRemove, onClear, onChange }) {
1943
1944
  const bar = useRef11(null);
@@ -1983,11 +1984,10 @@ function FilterBar({ filters, label = "Filters", clearLabel = "Clear all filters
1983
1984
  /* @__PURE__ */ jsx23(
1984
1985
  Dropdown,
1985
1986
  {
1986
- label: filter.label,
1987
- value: filter.value,
1988
1987
  items: filter.items,
1989
1988
  variant: "select",
1990
- ariaLabel: `${filter.label}: ${filter.value}`,
1989
+ ariaLabel: filterChipName(filter),
1990
+ triggerContent: /* @__PURE__ */ jsx23("span", { className: filterChipUnset(filter) ? "ui-dropdown__value is-placeholder" : "ui-dropdown__value", children: filterChipText(filter) }),
1991
1991
  open: !blocked && !filter.disabled && (opened[filter.id] && opened[filter.id].against === filter.open ? opened[filter.id].open : !!filter.open),
1992
1992
  onOpenChange: (open) => setOpened((previous) => ({ ...previous, [filter.id]: { open, against: filter.open } })),
1993
1993
  onSelect: (value) => {
@@ -2362,10 +2362,30 @@ function FileField({ label, hint, error, required, disabled, onFileChange, class
2362
2362
  ) });
2363
2363
  }
2364
2364
 
2365
+ // src/SearchField.tsx
2366
+ import { forwardRef as forwardRef7 } from "react";
2367
+ import { icon as icon5 } from "@apliteni/apliteni-ui";
2368
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2369
+ var SearchField = forwardRef7(function SearchField2({ ariaLabel, className, ...props }, ref) {
2370
+ return /* @__PURE__ */ jsxs24("div", { className: "ui-input-group", children: [
2371
+ /* @__PURE__ */ jsx29("span", { className: "ui-input-group__icon", dangerouslySetInnerHTML: { __html: icon5("search") } }),
2372
+ /* @__PURE__ */ jsx29(
2373
+ "input",
2374
+ {
2375
+ ref,
2376
+ ...props,
2377
+ type: "search",
2378
+ "aria-label": ariaLabel,
2379
+ className: ["ui-input", className].filter(Boolean).join(" ")
2380
+ }
2381
+ )
2382
+ ] });
2383
+ });
2384
+
2365
2385
  // src/Toast.tsx
2366
2386
  import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect4, useRef as useRef16, useState as useState10 } from "react";
2367
2387
  import { createPortal as createPortal4 } from "react-dom";
2368
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2388
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2369
2389
  var Context = createContext2(null);
2370
2390
  var glyphs = { success: "circleCheck", danger: "circleX", warn: "circleAlert", info: "info", neutral: "bolt" };
2371
2391
  function useToast() {
@@ -2411,7 +2431,7 @@ function Notice({ notice, remove }) {
2411
2431
  remaining.current = Math.max(0, remaining.current - (Date.now() - started));
2412
2432
  };
2413
2433
  }, [dismiss, leaving2, notice.action, paused]);
2414
- return /* @__PURE__ */ jsxs24(
2434
+ return /* @__PURE__ */ jsxs25(
2415
2435
  "div",
2416
2436
  {
2417
2437
  ref: root,
@@ -2425,12 +2445,12 @@ function Notice({ notice, remove }) {
2425
2445
  if (!event.currentTarget.contains(event.relatedTarget)) setFocused(false);
2426
2446
  },
2427
2447
  children: [
2428
- /* @__PURE__ */ jsx29("span", { className: "ui-toast__icon", children: /* @__PURE__ */ jsx29(Icon, { name: glyphs[tone] }) }),
2429
- /* @__PURE__ */ jsxs24("div", { className: "ui-toast__body", children: [
2430
- /* @__PURE__ */ jsx29("div", { className: "ui-toast__title", children: notice.title }),
2431
- notice.text && /* @__PURE__ */ jsx29("div", { className: "ui-toast__text", children: notice.text })
2448
+ /* @__PURE__ */ jsx30("span", { className: "ui-toast__icon", children: /* @__PURE__ */ jsx30(Icon, { name: glyphs[tone] }) }),
2449
+ /* @__PURE__ */ jsxs25("div", { className: "ui-toast__body", children: [
2450
+ /* @__PURE__ */ jsx30("div", { className: "ui-toast__title", children: notice.title }),
2451
+ notice.text && /* @__PURE__ */ jsx30("div", { className: "ui-toast__text", children: notice.text })
2432
2452
  ] }),
2433
- notice.action && /* @__PURE__ */ jsx29(
2453
+ notice.action && /* @__PURE__ */ jsx30(
2434
2454
  "button",
2435
2455
  {
2436
2456
  type: "button",
@@ -2445,8 +2465,8 @@ function Notice({ notice, remove }) {
2445
2465
  children: notice.action.label
2446
2466
  }
2447
2467
  ),
2448
- /* @__PURE__ */ jsx29("button", { type: "button", className: "ui-toast__close", "aria-label": "Dismiss", disabled: leaving2, onClick: dismiss, children: /* @__PURE__ */ jsx29(Icon, { name: "x" }) }),
2449
- !notice.action && /* @__PURE__ */ jsx29(
2468
+ /* @__PURE__ */ jsx30("button", { type: "button", className: "ui-toast__close", "aria-label": "Dismiss", disabled: leaving2, onClick: dismiss, children: /* @__PURE__ */ jsx30(Icon, { name: "x" }) }),
2469
+ !notice.action && /* @__PURE__ */ jsx30(
2450
2470
  "span",
2451
2471
  {
2452
2472
  className: "ui-toast__timer is-running",
@@ -2488,10 +2508,10 @@ function Toast({ children }) {
2488
2508
  setEntries((current) => [...current, { id, notice, remove }]);
2489
2509
  }, []);
2490
2510
  usePublishedReach(stack2, entries.length);
2491
- return /* @__PURE__ */ jsxs24(Context.Provider, { value: push, children: [
2511
+ return /* @__PURE__ */ jsxs25(Context.Provider, { value: push, children: [
2492
2512
  children,
2493
2513
  typeof document !== "undefined" && createPortal4(
2494
- /* @__PURE__ */ jsx29("div", { className: "ui-toast-stack rx-toast-stack", ref: stack2, children: entries.map((entry) => /* @__PURE__ */ jsx29(Notice, { notice: entry.notice, remove: entry.remove }, entry.id)) }),
2514
+ /* @__PURE__ */ jsx30("div", { className: "ui-toast-stack rx-toast-stack", ref: stack2, children: entries.map((entry) => /* @__PURE__ */ jsx30(Notice, { notice: entry.notice, remove: entry.remove }, entry.id)) }),
2495
2515
  document.body
2496
2516
  )
2497
2517
  ] });
@@ -2502,9 +2522,9 @@ import { useEffect as useEffect13, useId as useId12, useState as useState12 } fr
2502
2522
  import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
2503
2523
 
2504
2524
  // src/SidebarNav.tsx
2505
- import { forwardRef as forwardRef7, useEffect as useEffect12, useId as useId11, useState as useState11 } from "react";
2525
+ import { forwardRef as forwardRef8, useEffect as useEffect12, useId as useId11, useState as useState11 } from "react";
2506
2526
  import { ENTRANCE_FALLBACK_MS } from "@apliteni/apliteni-ui/motion";
2507
- import { Fragment as Fragment7, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2527
+ import { Fragment as Fragment7, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2508
2528
  var badgeText = (badge) => typeof badge === "object" ? badge.text : badge;
2509
2529
  function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
2510
2530
  const uid = useId11();
@@ -2530,15 +2550,15 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
2530
2550
  item.disabled && "is-disabled",
2531
2551
  item.danger && "is-danger"
2532
2552
  ].filter(Boolean).join(" ");
2533
- const children = /* @__PURE__ */ jsxs25(Fragment7, { children: [
2534
- (item.leading ?? item.icon) && /* @__PURE__ */ jsx30("span", { className: "ui-nav__ic", children: item.leading ?? /* @__PURE__ */ jsx30(Icon, { name: item.icon }) }),
2535
- /* @__PURE__ */ jsx30("span", { className: "ui-nav__label", children: item.label }),
2536
- count != null && count !== "" && /* @__PURE__ */ jsx30("span", { className: `ui-nav__badge is-${typeof item.badge === "object" ? item.badge.tone ?? "neutral" : "neutral"}`, children: count }),
2537
- group && /* @__PURE__ */ jsx30("span", { className: "ui-nav__caret", "aria-hidden": "true" })
2553
+ const children = /* @__PURE__ */ jsxs26(Fragment7, { children: [
2554
+ (item.leading ?? item.icon) && /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: item.leading ?? /* @__PURE__ */ jsx31(Icon, { name: item.icon }) }),
2555
+ /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: item.label }),
2556
+ count != null && count !== "" && /* @__PURE__ */ jsx31("span", { className: `ui-nav__badge is-${typeof item.badge === "object" ? item.badge.tone ?? "neutral" : "neutral"}`, children: count }),
2557
+ group && /* @__PURE__ */ jsx31("span", { className: "ui-nav__caret", "aria-hidden": "true" })
2538
2558
  ] });
2539
2559
  const common = { className, "aria-label": name, title: collapsed ? name : void 0, children };
2540
- if (group) return /* @__PURE__ */ jsxs25("li", { className: `ui-nav__group${open ? " is-open" : ""}`, children: [
2541
- /* @__PURE__ */ jsx30(
2560
+ if (group) return /* @__PURE__ */ jsxs26("li", { className: `ui-nav__group${open ? " is-open" : ""}`, children: [
2561
+ /* @__PURE__ */ jsx31(
2542
2562
  "button",
2543
2563
  {
2544
2564
  ...common,
@@ -2552,25 +2572,25 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
2552
2572
  }
2553
2573
  }
2554
2574
  ),
2555
- /* @__PURE__ */ jsx30("ul", { id: uid, className: `ui-nav__sub${entering ? " is-entering" : ""}`, hidden: !open, onAnimationEnd: () => setEntering(false), children: item.items.map((child) => /* @__PURE__ */ jsx30(Row, { item: child, active, activeIs, collapsed, renderLink, sub: true }, child.id)) })
2575
+ /* @__PURE__ */ jsx31("ul", { id: uid, className: `ui-nav__sub${entering ? " is-entering" : ""}`, hidden: !open, onAnimationEnd: () => setEntering(false), children: item.items.map((child) => /* @__PURE__ */ jsx31(Row, { item: child, active, activeIs, collapsed, renderLink, sub: true }, child.id)) })
2556
2576
  ] });
2557
- if (item.disabled) return /* @__PURE__ */ jsx30("li", { children: /* @__PURE__ */ jsx30("span", { ...common, role: "link", "aria-disabled": "true" }) });
2577
+ if (item.disabled) return /* @__PURE__ */ jsx31("li", { children: /* @__PURE__ */ jsx31("span", { ...common, role: "link", "aria-disabled": "true" }) });
2558
2578
  const props = {
2559
2579
  ...common,
2560
2580
  href: item.href ?? `#${item.id}`,
2561
2581
  target: item.target,
2562
2582
  "aria-current": isActive ? activeIs === "section" ? "true" : "page" : void 0
2563
2583
  };
2564
- return /* @__PURE__ */ jsx30("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx30("a", { ...props }) });
2584
+ return /* @__PURE__ */ jsx31("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx31("a", { ...props }) });
2565
2585
  }
2566
2586
  function Section({ section, ...props }) {
2567
2587
  const uid = useId11();
2568
- return /* @__PURE__ */ jsxs25("div", { className: "ui-nav__section", children: [
2569
- section.label && /* @__PURE__ */ jsx30("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
2570
- /* @__PURE__ */ jsx30("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx30(Row, { item, ...props }, item.id)) })
2588
+ return /* @__PURE__ */ jsxs26("div", { className: "ui-nav__section", children: [
2589
+ section.label && /* @__PURE__ */ jsx31("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
2590
+ /* @__PURE__ */ jsx31("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx31(Row, { item, ...props }, item.id)) })
2571
2591
  ] });
2572
2592
  }
2573
- var SidebarNav = forwardRef7(function SidebarNav2({
2593
+ var SidebarNav = forwardRef8(function SidebarNav2({
2574
2594
  items = [],
2575
2595
  sections,
2576
2596
  active,
@@ -2582,8 +2602,8 @@ var SidebarNav = forwardRef7(function SidebarNav2({
2582
2602
  "aria-label": label = "Sidebar",
2583
2603
  ...rest
2584
2604
  }, ref) {
2585
- return /* @__PURE__ */ jsxs25("nav", { ...rest, ref, "aria-label": label, className: ["ui-nav", "ui-nav--side", collapsed && "is-collapsed", className].filter(Boolean).join(" "), children: [
2586
- (sections?.length ? sections : [{ items }]).map((section, index) => /* @__PURE__ */ jsx30(
2605
+ return /* @__PURE__ */ jsxs26("nav", { ...rest, ref, "aria-label": label, className: ["ui-nav", "ui-nav--side", collapsed && "is-collapsed", className].filter(Boolean).join(" "), children: [
2606
+ (sections?.length ? sections : [{ items }]).map((section, index) => /* @__PURE__ */ jsx31(
2587
2607
  Section,
2588
2608
  {
2589
2609
  section,
@@ -2594,12 +2614,12 @@ var SidebarNav = forwardRef7(function SidebarNav2({
2594
2614
  },
2595
2615
  index
2596
2616
  )),
2597
- footer && /* @__PURE__ */ jsx30("div", { className: "ui-nav__foot", children: footer })
2617
+ footer && /* @__PURE__ */ jsx31("div", { className: "ui-nav__foot", children: footer })
2598
2618
  ] });
2599
2619
  });
2600
2620
 
2601
2621
  // src/AppShell.tsx
2602
- import { Fragment as Fragment8, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2622
+ import { Fragment as Fragment8, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2603
2623
  var path = (value) => value.split(/[?#]/)[0].replace(/\/+$/, "") || "/";
2604
2624
  function AppShell({
2605
2625
  sections,
@@ -2672,15 +2692,15 @@ function AppShell({
2672
2692
  onClick: (event) => {
2673
2693
  if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
2674
2694
  },
2675
- children: /* @__PURE__ */ jsxs26(Fragment8, { children: [
2676
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx31(Icon, { name: section.icon }) }),
2677
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: section.label }),
2678
- section.count != null && /* @__PURE__ */ jsx31("span", { className: "ui-nav__badge is-neutral", children: section.count })
2695
+ children: /* @__PURE__ */ jsxs27(Fragment8, { children: [
2696
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx32(Icon, { name: section.icon }) }),
2697
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: section.label }),
2698
+ section.count != null && /* @__PURE__ */ jsx32("span", { className: "ui-nav__badge is-neutral", children: section.count })
2679
2699
  ] })
2680
2700
  };
2681
- return /* @__PURE__ */ jsx31("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx31("a", { ...linkProps }) }, section.href);
2701
+ return /* @__PURE__ */ jsx32("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx32("a", { ...linkProps }) }, section.href);
2682
2702
  });
2683
- const sidebar = (items, label) => /* @__PURE__ */ jsx31(
2703
+ const sidebar = (items, label) => /* @__PURE__ */ jsx32(
2684
2704
  SidebarNav,
2685
2705
  {
2686
2706
  "aria-label": label,
@@ -2692,7 +2712,7 @@ function AppShell({
2692
2712
  const linkProps = { ...props, onClick: (event) => {
2693
2713
  if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
2694
2714
  } };
2695
- return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx31("a", { ...linkProps });
2715
+ return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx32("a", { ...linkProps });
2696
2716
  }
2697
2717
  }
2698
2718
  );
@@ -2700,33 +2720,33 @@ function AppShell({
2700
2720
  const visible = sections.length > 4 ? sections.slice(0, 3) : sections;
2701
2721
  const remaining = sections.slice(visible.length);
2702
2722
  const initials = account.name.trim().split(/\s+/).map((part) => part[0]).slice(0, 2).join("");
2703
- return /* @__PURE__ */ jsxs26("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
2704
- /* @__PURE__ */ jsxs26("div", { className: "ui-app__rail", children: [
2705
- /* @__PURE__ */ jsx31("div", { className: "ui-app__head", children: /* @__PURE__ */ jsxs26("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
2706
- brand ?? /* @__PURE__ */ jsx31("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
2707
- /* @__PURE__ */ jsx31("span", { children: word })
2723
+ return /* @__PURE__ */ jsxs27("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
2724
+ /* @__PURE__ */ jsxs27("div", { className: "ui-app__rail", children: [
2725
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__head", children: /* @__PURE__ */ jsxs27("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
2726
+ brand ?? /* @__PURE__ */ jsx32("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
2727
+ /* @__PURE__ */ jsx32("span", { children: word })
2708
2728
  ] }) }),
2709
2729
  sidebar(sections, "Sections"),
2710
- /* @__PURE__ */ jsx31("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx31("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs26("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
2711
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs26("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2712
- /* @__PURE__ */ jsx31("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2713
- /* @__PURE__ */ jsx31("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
2714
- /* @__PURE__ */ jsx31("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
2730
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx32("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs27("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
2731
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs27("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2732
+ /* @__PURE__ */ jsx32("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2733
+ /* @__PURE__ */ jsx32("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
2734
+ /* @__PURE__ */ jsx32("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
2715
2735
  ] }) }),
2716
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: foldLabel })
2736
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: foldLabel })
2717
2737
  ] }) }) })
2718
2738
  ] }),
2719
- /* @__PURE__ */ jsxs26("div", { className: "ui-app__well", children: [
2720
- /* @__PURE__ */ jsxs26("header", { className: "ui-app__bar", children: [
2721
- /* @__PURE__ */ jsxs26("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
2722
- /* @__PURE__ */ jsx31("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx31(Icon, { name: "search" }) }),
2723
- /* @__PURE__ */ jsx31("span", { className: "ui-app__search-txt", children: "Search or run a command\u2026" }),
2724
- /* @__PURE__ */ jsx31("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
2739
+ /* @__PURE__ */ jsxs27("div", { className: "ui-app__well", children: [
2740
+ /* @__PURE__ */ jsxs27("header", { className: "ui-app__bar", children: [
2741
+ /* @__PURE__ */ jsxs27("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
2742
+ /* @__PURE__ */ jsx32("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx32(Icon, { name: "search" }) }),
2743
+ /* @__PURE__ */ jsx32("span", { className: "ui-app__search-txt", children: "Search or run a command\u2026" }),
2744
+ /* @__PURE__ */ jsx32("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
2725
2745
  ] }),
2726
- /* @__PURE__ */ jsx31("span", { className: "ui-app__bar-gap" }),
2746
+ /* @__PURE__ */ jsx32("span", { className: "ui-app__bar-gap" }),
2727
2747
  bandControl,
2728
- /* @__PURE__ */ jsx31("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx31("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
2729
- /* @__PURE__ */ jsx31("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx31(
2748
+ /* @__PURE__ */ jsx32("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx32("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
2749
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx32(
2730
2750
  Dropdown,
2731
2751
  {
2732
2752
  variant: "menu",
@@ -2735,27 +2755,27 @@ function AppShell({
2735
2755
  ariaLabel: `Signed in as ${account.name}, ${account.email}`,
2736
2756
  triggerClass: "ui-app__user-trigger",
2737
2757
  panelClass: "ui-app__user-panel",
2738
- triggerContent: /* @__PURE__ */ jsx31("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
2739
- header: /* @__PURE__ */ jsxs26("div", { className: "ui-dropdown__head", children: [
2740
- /* @__PURE__ */ jsx31("b", { children: account.name }),
2741
- /* @__PURE__ */ jsx31("span", { children: account.email })
2758
+ triggerContent: /* @__PURE__ */ jsx32("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
2759
+ header: /* @__PURE__ */ jsxs27("div", { className: "ui-dropdown__head", children: [
2760
+ /* @__PURE__ */ jsx32("b", { children: account.name }),
2761
+ /* @__PURE__ */ jsx32("span", { children: account.email })
2742
2762
  ] }),
2743
2763
  items: [{ label: "Sign out", icon: "logout", danger: true }],
2744
2764
  onSelect: onSignOut
2745
2765
  }
2746
2766
  ) })
2747
2767
  ] }),
2748
- /* @__PURE__ */ jsxs26("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
2749
- back && /* @__PURE__ */ jsx31(BackLink, { ...back }),
2750
- /* @__PURE__ */ jsx31("h1", { children: title }),
2751
- lede && /* @__PURE__ */ jsx31("p", { className: "ui-app__sub", children: lede }),
2752
- actions && /* @__PURE__ */ jsx31("div", { className: "ui-react-app__actions", children: actions }),
2753
- /* @__PURE__ */ jsx31("div", { className: "ui-app__body", children })
2768
+ /* @__PURE__ */ jsxs27("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
2769
+ back && /* @__PURE__ */ jsx32(BackLink, { ...back }),
2770
+ /* @__PURE__ */ jsx32("h1", { children: title }),
2771
+ lede && /* @__PURE__ */ jsx32("p", { className: "ui-app__sub", children: lede }),
2772
+ actions && /* @__PURE__ */ jsx32("div", { className: "ui-react-app__actions", children: actions }),
2773
+ /* @__PURE__ */ jsx32("div", { className: "ui-app__body", children })
2754
2774
  ] })
2755
2775
  ] }),
2756
- /* @__PURE__ */ jsx31("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs26("ul", { className: "ui-nav__list", children: [
2776
+ /* @__PURE__ */ jsx32("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs27("ul", { className: "ui-nav__list", children: [
2757
2777
  links(visible),
2758
- remaining.length > 0 && /* @__PURE__ */ jsx31("li", { children: /* @__PURE__ */ jsxs26(
2778
+ remaining.length > 0 && /* @__PURE__ */ jsx32("li", { children: /* @__PURE__ */ jsxs27(
2759
2779
  "button",
2760
2780
  {
2761
2781
  type: "button",
@@ -2764,43 +2784,43 @@ function AppShell({
2764
2784
  "aria-expanded": more,
2765
2785
  onClick: () => setMore(true),
2766
2786
  children: [
2767
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx31(Icon, { name: "moreHorizontal" }) }),
2768
- /* @__PURE__ */ jsx31("span", { className: "ui-nav__label", children: "More" })
2787
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx32(Icon, { name: "moreHorizontal" }) }),
2788
+ /* @__PURE__ */ jsx32("span", { className: "ui-nav__label", children: "More" })
2769
2789
  ]
2770
2790
  }
2771
2791
  ) })
2772
2792
  ] }) }),
2773
- /* @__PURE__ */ jsx31(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
2774
- /* @__PURE__ */ jsx31(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
2793
+ /* @__PURE__ */ jsx32(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
2794
+ /* @__PURE__ */ jsx32(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
2775
2795
  ] });
2776
2796
  }
2777
2797
 
2778
2798
  // src/EmptyState.tsx
2779
2799
  import { icon as kitIcon2, illo } from "@apliteni/apliteni-ui";
2780
- import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2800
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2781
2801
  var cases = {
2782
2802
  "first-run": { title: "Nothing needs you yet", sub: "New items will appear here when they need your attention.", icon: "check" },
2783
2803
  "no-matches": { title: "No results in this view", sub: "Try another search or clear your filters.", icon: "search" },
2784
2804
  "not-found": { title: "We can't find that page", sub: "Check the address or return to the home page.", icon: "folder" },
2785
2805
  "not-yet-built": { title: "This ships later", sub: "This screen is not available yet. Check back later.", icon: "clock" }
2786
2806
  };
2787
- function EmptyState({ variant = "first-run", title, sub, icon: icon6, art, actions }) {
2807
+ function EmptyState({ variant = "first-run", title, sub, icon: icon7, art, actions }) {
2788
2808
  const preset = cases[variant];
2789
2809
  const heading = title ?? preset.title;
2790
2810
  const guidance = sub ?? preset.sub;
2791
- const glyph = icon6 ?? preset.icon;
2792
- return /* @__PURE__ */ jsxs27("div", { className: "ui-empty", children: [
2793
- art ? typeof art === "string" ? /* @__PURE__ */ jsx32("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx32("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx32("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
2794
- heading && /* @__PURE__ */ jsx32("div", { className: "ui-empty__title", children: heading }),
2795
- guidance && /* @__PURE__ */ jsx32("div", { className: "ui-empty__sub", children: guidance }),
2796
- actions && /* @__PURE__ */ jsx32("div", { className: "ui-empty__actions", children: actions })
2811
+ const glyph = icon7 ?? preset.icon;
2812
+ return /* @__PURE__ */ jsxs28("div", { className: "ui-empty", children: [
2813
+ art ? typeof art === "string" ? /* @__PURE__ */ jsx33("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx33("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx33("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
2814
+ heading && /* @__PURE__ */ jsx33("div", { className: "ui-empty__title", children: heading }),
2815
+ guidance && /* @__PURE__ */ jsx33("div", { className: "ui-empty__sub", children: guidance }),
2816
+ actions && /* @__PURE__ */ jsx33("div", { className: "ui-empty__actions", children: actions })
2797
2817
  ] });
2798
2818
  }
2799
2819
 
2800
2820
  // src/ThemeToggle.tsx
2801
2821
  import { useSyncExternalStore } from "react";
2802
- import { icon as icon5 } from "@apliteni/apliteni-ui";
2803
- import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2822
+ import { icon as icon6 } from "@apliteni/apliteni-ui";
2823
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
2804
2824
  var STORAGE_KEY = "apliteni-strategy-theme";
2805
2825
  var CHOICE_EVENT = "apliteni-theme-choice";
2806
2826
  var THEME_INIT_SCRIPT = `(()=>{let t;try{t=localStorage.getItem('apliteni-strategy-theme')}catch{}document.documentElement.setAttribute('data-theme',t==='dark'||t==='light'?t:window.matchMedia?.('(prefers-color-scheme: light)').matches?'light':'dark')})()`;
@@ -2858,10 +2878,10 @@ function ThemeToggle({ labelled = false }) {
2858
2878
  }
2859
2879
  window.dispatchEvent(new Event(CHOICE_EVENT));
2860
2880
  };
2861
- const button = /* @__PURE__ */ jsx33("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx33("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon5(theme === "auto" ? "monitor" : theme === "light" ? "sun" : "moon") } }) });
2862
- return labelled ? /* @__PURE__ */ jsxs28("span", { className: "ui-theme-toggle", children: [
2881
+ const button = /* @__PURE__ */ jsx34("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx34("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon6(theme === "auto" ? "monitor" : theme === "light" ? "sun" : "moon") } }) });
2882
+ return labelled ? /* @__PURE__ */ jsxs29("span", { className: "ui-theme-toggle", children: [
2863
2883
  button,
2864
- /* @__PURE__ */ jsxs28("span", { children: [
2884
+ /* @__PURE__ */ jsxs29("span", { children: [
2865
2885
  label,
2866
2886
  " theme"
2867
2887
  ] })
@@ -2869,11 +2889,11 @@ function ThemeToggle({ labelled = false }) {
2869
2889
  }
2870
2890
 
2871
2891
  // src/Success.tsx
2872
- import { forwardRef as forwardRef8, useEffect as useEffect14, useRef as useRef17, useState as useState13 } from "react";
2873
- import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
2874
- var SuccessCheck = forwardRef8(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
2892
+ import { forwardRef as forwardRef9, useEffect as useEffect14, useRef as useRef17, useState as useState13 } from "react";
2893
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
2894
+ var SuccessCheck = forwardRef9(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
2875
2895
  const circled = variant === "circled";
2876
- return /* @__PURE__ */ jsxs29(
2896
+ return /* @__PURE__ */ jsxs30(
2877
2897
  "svg",
2878
2898
  {
2879
2899
  ...rest,
@@ -2885,18 +2905,18 @@ var SuccessCheck = forwardRef8(function SuccessCheck2({ variant = "line", classN
2885
2905
  "aria-hidden": "true",
2886
2906
  focusable: "false",
2887
2907
  children: [
2888
- circled && /* @__PURE__ */ jsx34("path", { className: "ui-sx__circle", d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
2889
- /* @__PURE__ */ jsx34("path", { className: "ui-sx__tick", d: circled ? "M22 4L12 14.01l-3-3" : "M20 6L9 17l-5-5" })
2908
+ circled && /* @__PURE__ */ jsx35("path", { className: "ui-sx__circle", d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
2909
+ /* @__PURE__ */ jsx35("path", { className: "ui-sx__tick", d: circled ? "M22 4L12 14.01l-3-3" : "M20 6L9 17l-5-5" })
2890
2910
  ]
2891
2911
  }
2892
2912
  );
2893
2913
  });
2894
- var SuccessPanel = forwardRef8(function SuccessPanel2({ title = "Done", sub, check = "line", className, ...rest }, ref) {
2914
+ var SuccessPanel = forwardRef9(function SuccessPanel2({ title = "Done", sub, check = "line", className, ...rest }, ref) {
2895
2915
  const circled = check === "circled";
2896
- return /* @__PURE__ */ jsxs29("div", { ...rest, ref, className: ["ui-success", className].filter(Boolean).join(" "), children: [
2897
- /* @__PURE__ */ jsx34("div", { className: ["ui-success__check", circled && "ui-success__check--circled"].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx34(SuccessCheck, { variant: circled ? "circled" : "line" }) }),
2898
- /* @__PURE__ */ jsx34("div", { className: "ui-success__title", children: title }),
2899
- sub && /* @__PURE__ */ jsx34("div", { className: "ui-success__sub", children: sub })
2916
+ return /* @__PURE__ */ jsxs30("div", { ...rest, ref, className: ["ui-success", className].filter(Boolean).join(" "), children: [
2917
+ /* @__PURE__ */ jsx35("div", { className: ["ui-success__check", circled && "ui-success__check--circled"].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx35(SuccessCheck, { variant: circled ? "circled" : "line" }) }),
2918
+ /* @__PURE__ */ jsx35("div", { className: "ui-success__title", children: title }),
2919
+ sub && /* @__PURE__ */ jsx35("div", { className: "ui-success__sub", children: sub })
2900
2920
  ] });
2901
2921
  });
2902
2922
  var pieces = [
@@ -2933,17 +2953,17 @@ function Countdown({ seconds, label = "Redirecting", onDone }) {
2933
2953
  }, 1e3);
2934
2954
  return () => clearInterval(timer);
2935
2955
  }, [seconds]);
2936
- return /* @__PURE__ */ jsxs29("div", { className: "ui-sx__count", "data-sx-count": "", style: { "--sx-secs": `${seconds}s` }, children: [
2937
- /* @__PURE__ */ jsx34("span", { className: "ui-sx__count-ring", "aria-hidden": "true" }),
2938
- /* @__PURE__ */ jsxs29("span", { className: "ui-sx__count-text", children: [
2956
+ return /* @__PURE__ */ jsxs30("div", { className: "ui-sx__count", "data-sx-count": "", style: { "--sx-secs": `${seconds}s` }, children: [
2957
+ /* @__PURE__ */ jsx35("span", { className: "ui-sx__count-ring", "aria-hidden": "true" }),
2958
+ /* @__PURE__ */ jsxs30("span", { className: "ui-sx__count-text", children: [
2939
2959
  label,
2940
2960
  " in ",
2941
- /* @__PURE__ */ jsx34("b", { "data-sx-num": "", children: remaining }),
2961
+ /* @__PURE__ */ jsx35("b", { "data-sx-num": "", children: remaining }),
2942
2962
  "s"
2943
2963
  ] })
2944
2964
  ] });
2945
2965
  }
2946
- var Success = forwardRef8(function Success2({
2966
+ var Success = forwardRef9(function Success2({
2947
2967
  layout = "hero",
2948
2968
  level,
2949
2969
  check = "line",
@@ -2965,7 +2985,7 @@ var Success = forwardRef8(function Success2({
2965
2985
  const seconds = countdown?.seconds;
2966
2986
  const duration = seconds != null && Number.isFinite(seconds) && seconds >= 1 ? Math.floor(seconds) : 5;
2967
2987
  const mark = check === "circled" ? "circled" : "line";
2968
- return /* @__PURE__ */ jsxs29(
2988
+ return /* @__PURE__ */ jsxs30(
2969
2989
  "div",
2970
2990
  {
2971
2991
  role: "status",
@@ -2974,16 +2994,16 @@ var Success = forwardRef8(function Success2({
2974
2994
  ref,
2975
2995
  className: ["ui-sx", `ui-sx--${layout}`, `ui-sx--check-${mark}`, confetti && "ui-sx--confetti", className].filter(Boolean).join(" "),
2976
2996
  children: [
2977
- confetti && /* @__PURE__ */ jsx34("div", { className: "ui-sx__confetti", "aria-hidden": "true", children: pieces.map(
2978
- ([x, d, r, t, s], index) => /* @__PURE__ */ jsx34("i", { className: `ui-sx__piece ui-sx__piece--${t}`, style: { left: `${x}%`, "--sx-d": `${d}s`, "--sx-r": `${r}deg`, "--sx-s": s } }, index)
2997
+ confetti && /* @__PURE__ */ jsx35("div", { className: "ui-sx__confetti", "aria-hidden": "true", children: pieces.map(
2998
+ ([x, d, r, t, s], index) => /* @__PURE__ */ jsx35("i", { className: `ui-sx__piece ui-sx__piece--${t}`, style: { left: `${x}%`, "--sx-d": `${d}s`, "--sx-r": `${r}deg`, "--sx-s": s } }, index)
2979
2999
  ) }),
2980
- /* @__PURE__ */ jsxs29("div", { className: "ui-sx__inner", children: [
2981
- /* @__PURE__ */ jsx34("div", { className: "ui-sx__visual", children: /* @__PURE__ */ jsx34(SuccessCheck, { variant: mark }) }),
2982
- /* @__PURE__ */ jsxs29("div", { className: "ui-sx__content", children: [
2983
- /* @__PURE__ */ jsx34(Heading, { className: "ui-sx__title", children: title }),
2984
- body && /* @__PURE__ */ jsx34("p", { className: "ui-sx__body", children: body }),
2985
- hasActions && /* @__PURE__ */ jsx34("div", { className: "ui-sx__actions", children: actions }),
2986
- countdown && /* @__PURE__ */ jsx34(Countdown, { seconds: duration, label: countdown.label, onDone: onCountdownEnd }, duration)
3000
+ /* @__PURE__ */ jsxs30("div", { className: "ui-sx__inner", children: [
3001
+ /* @__PURE__ */ jsx35("div", { className: "ui-sx__visual", children: /* @__PURE__ */ jsx35(SuccessCheck, { variant: mark }) }),
3002
+ /* @__PURE__ */ jsxs30("div", { className: "ui-sx__content", children: [
3003
+ /* @__PURE__ */ jsx35(Heading, { className: "ui-sx__title", children: title }),
3004
+ body && /* @__PURE__ */ jsx35("p", { className: "ui-sx__body", children: body }),
3005
+ hasActions && /* @__PURE__ */ jsx35("div", { className: "ui-sx__actions", children: actions }),
3006
+ countdown && /* @__PURE__ */ jsx35(Countdown, { seconds: duration, label: countdown.label, onDone: onCountdownEnd }, duration)
2987
3007
  ] })
2988
3008
  ] })
2989
3009
  ]
@@ -3024,6 +3044,7 @@ export {
3024
3044
  Pagination,
3025
3045
  Pill,
3026
3046
  RowIdentity,
3047
+ SearchField,
3027
3048
  Segmented,
3028
3049
  SelectField,
3029
3050
  SidebarNav,
@@ -1,12 +1,17 @@
1
1
  import { button, esc } from './index.js';
2
2
  import { dropdown, wireDropdown } from './dropdown.js';
3
+ import { filterChipText, filterChipName, filterChipUnset } from '../logic/filter-bar.js';
3
4
 
4
5
  export function filterBar({ filters = [], label = 'Filters', clearLabel = 'Clear all filters', disabled = false, busy = false } = {}) {
5
6
  return `<fieldset class="ui-filter-bar" data-filter-bar${disabled || busy ? ' disabled' : ''}${busy ? ' aria-busy="true"' : ''}>`
6
7
  + `<legend class="ui-filter-bar__legend">${esc(label)}</legend>`
7
8
  + filters.map(filter => `<fieldset class="ui-filter-bar__chip" data-filter-id="${esc(filter.id)}"${filter.disabled ? ' disabled' : ''}>`
8
9
  + `<legend class="ui-filter-bar__legend">${esc(filter.label)}</legend>`
9
- + dropdown({ label: filter.label, value: filter.value, items: filter.items || [], variant: 'select', ariaLabel: `${filter.label}: ${filter.value}`, open: !!filter.open && !disabled && !busy && !filter.disabled })
10
+ + dropdown({ items: filter.items || [], variant: 'select',
11
+ // The chip prints one line; the field's name reaches a reader through the
12
+ // trigger's name and the chip's own legend. why: docs/specification.md#dense-financial-tables
13
+ triggerContent: `<span class="${filterChipUnset(filter) ? 'ui-dropdown__value is-placeholder' : 'ui-dropdown__value'}">${esc(filterChipText(filter))}</span>`,
14
+ ariaLabel: filterChipName(filter), open: !!filter.open && !disabled && !busy && !filter.disabled })
10
15
  + `<button type="button" class="ui-filter-bar__remove" data-filter-remove aria-label="${esc(`Remove ${filter.label} filter`)}">×</button></fieldset>`).join('')
11
16
  + `<span data-filter-clear>${button({ label: clearLabel, size: 'sm', variant: 'ghost', disabled: !filters.length })}</span></fieldset>`;
12
17
  }
@@ -46,7 +51,18 @@ export function initFilterBar(host, options = {}) {
46
51
  if (item.getAttribute('aria-disabled') !== 'true') {
47
52
  const id = chip?.dataset.filterId, value = item.dataset.value;
48
53
  // Dropdown completes its own close/focus before the consumer replaces the markup.
49
- queueMicrotask(() => emit('ui-filter-change', { id, value }));
54
+ queueMicrotask(() => {
55
+ // Dropdown writes the picked row's label into the trigger; the name it is
56
+ // read by follows it, so the two cannot disagree while the consumer answers.
57
+ const filter = (current.filters || []).find(f => f.id === id);
58
+ const trigger = chip?.querySelector('[data-dropdown-trigger]');
59
+ const span = trigger?.querySelector('.ui-dropdown__value');
60
+ const shown = span?.textContent;
61
+ if (filter && trigger) trigger.setAttribute('aria-label', filterChipName({ ...filter, value: shown }));
62
+ // A chip that was empty stops being a placeholder the moment it shows a pick.
63
+ if (span) span.classList.toggle('is-placeholder', filterChipUnset({ value: shown }));
64
+ emit('ui-filter-change', { id, value });
65
+ });
50
66
  }
51
67
  }
52
68
  };
package/src/index.d.ts CHANGED
@@ -7,6 +7,9 @@ export declare function numericValue(opts?: Record<string, unknown>): string;
7
7
  export declare function deltaValue(opts?: Record<string, unknown>): string;
8
8
  export declare function filterBar(opts?: Record<string, unknown>): string;
9
9
  export declare function initFilterBar(host: Element, opts?: Record<string, unknown>): { update: (opts: Record<string, unknown>) => void; destroy: () => void };
10
+ export declare function filterChipText(filter?: { label?: string; value?: string }): string;
11
+ export declare function filterChipName(filter?: { label?: string; value?: string }): string;
12
+ export declare function filterChipUnset(filter?: { value?: string }): boolean;
10
13
  export declare function segmentedNextIndex(key: string, index: number, length: number): number | null;
11
14
  export declare function icon(name: string, className?: string): string;
12
15
  export declare function snippet(opts?: Record<string, unknown>): string;
package/src/index.js CHANGED
@@ -30,6 +30,7 @@ export * from './components/filter-bar.js';
30
30
  export { dropdownMatch, dropdownFiltering } from './logic/dropdown.js';
31
31
  export { SCORE, rankGroups, rankCommands, scoreCommand, paletteHotkey } from './logic/command-palette.js';
32
32
  export { segmentedNextIndex } from './logic/segmented.js';
33
+ export { filterChipText, filterChipName, filterChipUnset } from './logic/filter-bar.js';
33
34
  export { PAGE_SIZES, DEFAULT_PAGE_SIZE } from './logic/pagination.js';
34
35
  export { calloutIcons } from './logic/callout.js';
35
36
  export { formatNumericValue, formatDeltaValue } from './logic/table-values.js';
@@ -0,0 +1,29 @@
1
+ /* What a filter chip shows, what it is called, and when it counts as unset.
2
+ * The chip prints the chosen value by itself — never the field's name beside it
3
+ * — and the field's name stands in while nothing is chosen. Both faces ask
4
+ * these, so a chip reads the same in vanilla and in React.
5
+ *
6
+ * `value` is display text: it is the chip's only visible line, so a consumer
7
+ * answers a change with text a reader can read, not a raw code.
8
+ * why: docs/specification.md#dense-financial-tables
9
+ */
10
+
11
+ const text = (v) => (v == null ? '' : String(v));
12
+
13
+ /** No value chosen: absent, null or the empty string. */
14
+ export function filterChipUnset({ value } = {}) {
15
+ return text(value) === '';
16
+ }
17
+
18
+ /** The one line the chip prints: the value, or the field's name while unset. */
19
+ export function filterChipText(filter = {}) {
20
+ return filterChipUnset(filter) ? text(filter.label) : text(filter.value);
21
+ }
22
+
23
+ /** What a reader hears: the field, and its value or the empty state. A filter
24
+ * with no label is named by its value alone — "undefined" is not a name. */
25
+ export function filterChipName(filter = {}) {
26
+ const field = text(filter.label);
27
+ if (!field) return text(filter.value);
28
+ return filterChipUnset(filter) ? `${field}: any` : `${field}: ${text(filter.value)}`;
29
+ }
@@ -7,7 +7,10 @@
7
7
  .ui-filter-bar:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
8
8
  .ui-filter-bar button:disabled { /* muted-ink: state — disabled filters */ color: var(--disabled-ink); cursor: not-allowed; }
9
9
 
10
- .ui-filter-bar .ui-dropdown__pre, .ui-filter-bar .ui-dropdown__value { color: inherit; }
10
+ .ui-filter-bar .ui-dropdown__value { color: inherit; }
11
+ /* A chip with nothing chosen prints the field's name in the slot the value will
12
+ take. `:enabled` leaves a disabled chip its own ink. why: guidelines/labels-and-titles.md */
13
+ .ui-filter-bar button:enabled .ui-dropdown__value.is-placeholder { /* muted-ink: placeholder — a filter chip with nothing chosen */ color: var(--muted); }
11
14
 
12
15
  /* A chip's panel is as wide as the chip's trigger, never the 240px floor
13
16
  .ui-dropdown__panel gives a standalone dropdown. A panel is positioned at its
@@ -441,6 +441,13 @@
441
441
  .ui-toolbar { display: flex; flex-wrap: wrap; gap: 10px; }
442
442
  .ui-toolbar > .ui-input,
443
443
  .ui-toolbar > .ui-input-group { flex: 1 1 6rem; min-width: 0; }
444
+ /* At one column the 6rem basis stops being a floor and becomes the field's size:
445
+ a row with two narrow chips beside it keeps all three on the line and cuts the
446
+ field's own placeholder off. why: docs/specification.md#a-toolbar-at-one-column */
447
+ @media (max-width: 560px) {
448
+ .ui-toolbar > .ui-input,
449
+ .ui-toolbar > .ui-input-group { flex-basis: 100%; }
450
+ }
444
451
 
445
452
  /* -- The narrow rail ------------------------------------------------------ */
446
453
  /* Below this width the rail folds to the icon strip instead of disappearing, and