gridsmith-ui 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -433,6 +433,15 @@ interface FormWrapperProps {
433
433
  */
434
434
  actions?: ReactNode;
435
435
  actionsAlign?: "start" | "end";
436
+ /**
437
+ * Renders a secondary Cancel before the actions. Inside a Drawer, Sheet or Dialog it closes the overlay through its
438
+ * unsaved-changes check — the question Escape and the close control ask while the form is dirty — and the overlay's
439
+ * onClose runs; anywhere else this function runs. Never hand-wire a Cancel to the overlay's close: it skips the
440
+ * question (round 21, run 58).
441
+ */
442
+ onCancel?: () => void;
443
+ /** The Cancel button's label (default "Cancel"). */
444
+ cancelLabel?: string;
436
445
  /** After a failed submit, scroll to and focus the first invalid field (default on). */
437
446
  focusFirstError?: boolean;
438
447
  /**
@@ -443,7 +452,7 @@ interface FormWrapperProps {
443
452
  className?: string;
444
453
  ref?: React.Ref<HTMLFormElement>;
445
454
  }
446
- declare function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions, actionsAlign: actionsAlignProp, focusFirstError, errorSummary, className, ref }: FormWrapperProps): react_jsx_runtime.JSX.Element;
455
+ declare function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions: actionsProp, actionsAlign: actionsAlignProp, onCancel, cancelLabel, focusFirstError, errorSummary, className, ref }: FormWrapperProps): react_jsx_runtime.JSX.Element;
447
456
 
448
457
  interface TransferItem {
449
458
  id: string;
@@ -716,6 +725,7 @@ declare const Tag: react.ForwardRefExoticComponent<TagProps & react.RefAttribute
716
725
  type AvatarStatus = "online" | "away" | "busy" | "offline";
717
726
  type AvatarColor = "auto" | "accent" | "neutral" | 1 | 2 | 3 | 4 | 5;
718
727
  interface AvatarProps {
728
+ /** One to three letters ("JB"). A name passed here shows its initials ("Amara Haddad" → "AH"), never the whole name. */
719
729
  initials?: string;
720
730
  src?: string;
721
731
  alt?: string;
@@ -791,7 +801,7 @@ interface EntityCellProps {
791
801
  avatar?: ReactNode;
792
802
  /** An image URL for the avatar. */
793
803
  src?: string;
794
- /** Initials for the avatar. Absent, and with no src or element, they are derived from `primary` ("Sarah Chen" → "SC"). */
804
+ /** Leave it out: with no src or element the initials come from `primary` ("Sarah Chen" → "SC"). A name passed here shows its initials too (round 21). */
795
805
  initials?: string;
796
806
  primary: string;
797
807
  secondary?: string;
@@ -850,6 +860,17 @@ interface TableToolbarProps {
850
860
  /** How many rows match; shown with `resultNoun`. */
851
861
  resultCount?: number;
852
862
  resultNoun?: [singular: string, plural: string];
863
+ /**
864
+ * The list is paged: pass the `usePagination` result and the count says which rows are showing — "Showing 51–100 of
865
+ * 200 events" — instead of a bare total the table does not show (round 21, the owner's call); one page reads
866
+ * "12 events". Under tabs that carry the counts, pass the pager alone (no resultCount): the line then shows only the
867
+ * range, and only while the list spans pages — the tabs already say how many (gs:count-duplicated-by-tabs).
868
+ */
869
+ pagination?: {
870
+ page: number;
871
+ pageSize: number;
872
+ totalItems?: number;
873
+ };
853
874
  /** Utility actions on the right — an overflow DropdownMenu, an export button. Primary actions belong in the PageHeader. */
854
875
  actions?: ReactNode;
855
876
  size?: "sm" | "md";
@@ -865,7 +886,7 @@ interface TableToolbarProps {
865
886
  * component, so a page never hand-rolls the row. Primary actions belong in
866
887
  * the PageHeader, not here.
867
888
  */
868
- declare function TableToolbar({ search, primaryFilter, filters, filtersOpen, onFiltersOpenChange, activeFilterCount, activeFilters, onClearAll, resultCount, resultNoun, actions, size, compactBreakpoint, className, }: TableToolbarProps): react_jsx_runtime.JSX.Element;
889
+ declare function TableToolbar({ search, primaryFilter, filters, filtersOpen, onFiltersOpenChange, activeFilterCount, activeFilters, onClearAll, resultCount, resultNoun, pagination, actions, size, compactBreakpoint, className, }: TableToolbarProps): react_jsx_runtime.JSX.Element | null;
869
890
 
870
891
  interface TimelineItem {
871
892
  title: string;
@@ -2170,8 +2191,29 @@ interface AppShellPreferences {
2170
2191
  undoWindowSeconds?: number;
2171
2192
  }
2172
2193
  declare const PreferencesContext: react.Context<AppShellPreferences | null>;
2173
- /** The shell's preferences, or an empty object outside any shell — so `usePreferences().itemsPerPage ?? 25` reads naturally. */
2174
- declare function usePreferences(): AppShellPreferences;
2194
+ /** Every preference in force, the shell's value or the house default — what app code reads. */
2195
+ interface ResolvedPreferences {
2196
+ validationTiming: PreferenceValidationTiming;
2197
+ formActions: {
2198
+ align: "start" | "end";
2199
+ placement: "auto" | "inline";
2200
+ };
2201
+ itemsPerPage: number;
2202
+ tableToCardsBreakpoint: PreferenceBreakpoint;
2203
+ emptyStyle: PreferenceEmptyStyle;
2204
+ loadingStyle: PreferenceLoadingStyle;
2205
+ destructiveMode: PreferenceDestructiveMode;
2206
+ highSeverityActions: string[];
2207
+ typeToConfirmActions: string[];
2208
+ undoWindowSeconds: number;
2209
+ }
2210
+ /**
2211
+ * For app code: every preference in force — the shell's value, else the house default — so a page never writes a
2212
+ * fallback of its own. Round 21 (run 58): with every field optional, TypeScript made the builder write
2213
+ * `const { itemsPerPage = 20 } = usePreferences()`, a second copy of the preference it then edited by hand when the
2214
+ * rule changed. Paged lists use `usePagination`, which reads the page size itself.
2215
+ */
2216
+ declare function usePreferences(): ResolvedPreferences;
2175
2217
  /**
2176
2218
  * "Remove range" → "remove_range": the key the high-severity and type-to-confirm lists use. The engine's `actionKey`
2177
2219
  * (platform/src/lib/destructive-mode.ts) is the same rule for compliance; a platform test keeps the two identical.
@@ -2277,6 +2319,49 @@ interface AppShellProps {
2277
2319
  }
2278
2320
  declare const AppShell: react.ForwardRefExoticComponent<AppShellProps & react.RefAttributes<HTMLDivElement>>;
2279
2321
 
2322
+ /**
2323
+ * A paged list in one hook (round 21). The page size is the project's `dataDisplay.itemsPerPage`, read from the
2324
+ * shell, so a mid-build `update_rule` reaches every paged list with no page edited; the page gets its slice, the
2325
+ * props for `<Pagination>` and the numbers the count line needs ("Showing 51–100 of 200 events").
2326
+ *
2327
+ * Run 58 showed why: both pages hand-wrote the same slicing, a `totalPages` sum and a fallback page size of their
2328
+ * own, and the count line above the table said "200 events" while the table showed fifty.
2329
+ *
2330
+ * const pager = usePagination(sortedEvents, { resetOn: [sortKey, sortDir] })
2331
+ * <TableToolbar resultCount={sortedEvents.length} resultNoun={["event", "events"]} pagination={pager} />
2332
+ * <Table columns={columns} data={pager.items.map(toRow)} />
2333
+ * <Pagination {...pager.props} />
2334
+ */
2335
+ interface Pager<T> {
2336
+ /** This page's slice of the list. */
2337
+ items: T[];
2338
+ /** The current page, from 1. */
2339
+ page: number;
2340
+ pageSize: number;
2341
+ totalItems: number;
2342
+ totalPages: number;
2343
+ /** The first and last item numbers on this page, from 1 ("Showing 51–100"); [0, 0] for an empty list. */
2344
+ range: [number, number];
2345
+ setPage: (page: number) => void;
2346
+ /** A page-size selector's choice; it wins over the project's page size and starts again at page 1. */
2347
+ setPageSize: (size: number) => void;
2348
+ /** Spread onto <Pagination>. */
2349
+ props: {
2350
+ currentPage: number;
2351
+ totalPages: number;
2352
+ onPageChange: (page: number) => void;
2353
+ pageSize: number;
2354
+ onPageSizeChange: (size: number) => void;
2355
+ };
2356
+ }
2357
+ interface PaginationOptions {
2358
+ /** A fixed page size for this list, over the project's. Leave it out: the project's page size is the rule. */
2359
+ pageSize?: number;
2360
+ /** Values that start the list again at page 1 when they change — the sort column and direction, a search query. */
2361
+ resetOn?: unknown;
2362
+ }
2363
+ declare function usePagination<T>(list: readonly T[], options?: PaginationOptions): Pager<T>;
2364
+
2280
2365
  interface DestructiveActionOptions {
2281
2366
  /** The action's key as the preference lists name it ("remove_range"); derived from `title` when omitted. */
2282
2367
  action?: string;
@@ -2452,4 +2537,4 @@ interface UseTextOverflowResult {
2452
2537
  */
2453
2538
  declare function useTextOverflow(options?: UseTextOverflowOptions): UseTextOverflowResult;
2454
2539
 
2455
- export { Accordion, ActionPanel, ActionPanelGroup, Alert, type AlertPlacement, AppShell, type AppShellContextValue, type AppShellPreferences, type AppShellTheme, AsidePanel, type AsidePanelProps, type AsidePanelSide, Avatar, AvatarGroup, type AvatarGroupItem, type AvatarGroupProps, type AvatarStatus, Badge, type BadgePaletteColor, type BadgeSeverity, type BadgeSize, type BadgeVariant, BottomNav, type BottomNavItem, type BreadcrumbItem$1 as BreadcrumbItem, Breadcrumbs, type BreadcrumbsSeparator, type BreadcrumbsSize, type BreadcrumbsVariant, BreakpointKey, BulkActionBar, Button, ButtonSizeContext, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardSelectType, CardTitle, type CardVariant, Carousel, type ChatMessage, type ChatMessageAction, type ChatMessageLayout, ChatMessageList, type ChatMessageSender, type ChatMessageSource, type ChatMessageStatus, ChatPanel, type ChatPanelProps, type ChatPanelStatusTone, Checkbox, CheckboxGroup, CheckboxGroupItem, CodeBlock, Collapsible, ColorPicker, type Column, Combobox, CommandPalette, ContextMenu, type ControlSize, ControlSizeContext, CornerPanel, type CornerPanelEdge, type CornerPanelProps, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, type DateRange, DateRangePicker, type DateRangePickerProps, type DateRangePreset, type DescriptionItem, DescriptionList, type DescriptionListOrientation, type DescriptionListSize, type DestructiveActionHandle, type DestructiveActionOptions, Dialog, Divider, Dock, Drawer, Dropdown, DropdownMenu, type DropdownMenuDivider, type DropdownMenuEntry, type DropdownMenuItem, EmptyState, EntityCell, Field, type FieldControlProps, type FieldProps, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, type LauncherEdge, type LauncherProps, type LauncherVariant, type LauncherWhenOpen, Link, type LinkProps, MentionInput, MobileHeaderMenu, type MobileHeaderMenuItemData, type MobileHeaderMenuType, NavItem, type NavItemProps, type NavItemType, NavMenu, type NavMenuItem, Navbar, type NavbarSize, type NavbarType, type NavbarVariant, NotificationCenter, NumberInput, PageContainer, type PageContainerWidth, PageHeader, type PageHeaderBehavior, type PageHeaderIntensity, type PageHeaderProps, type PageHeaderVariant, Pagination, PinInput, Popover, PreferencesContext, Progress, type PromiseToastMessages, RadioGroup, Rating, ScrollArea, SectionHeading, type SectionHeadingProps, SegmentedControl, SettingsLayout, type SettingsLayoutProps, type SettingsSection, Sheet, Sidebar, type SidebarContextValue, type SidebarItem, SidebarNav, type SidebarNavSize, type SidebarPosition, type SidebarSection, type SidebarType, Skeleton, SkipLink, SliderRange, Spinner, StackedList, type StackedListVariant, StatsCard, StatusDot, type StatusDotVariant, Stepper, type Tab, Table, type TableDensity, type TableProps, TableToolbar, type TableToolbarActiveFilter, type TableToolbarFilterOption, Tabs, Tag, TagInput, type TagPaletteColor, type TagVariant, TextOverflow, type TextOverflowProps, Textarea, TimePicker, Timeline, Toast, type ToastAction, type ToastApi, type ToastOptions, type ToastPosition, ToastProvider, type ToastVariant, Toggle, Tooltip, Tour, TransferList, TreeView, type Trend, type UseTextOverflowOptions, type UseTextOverflowResult, preferenceActionKey, useAppShell, useContentWidth, useDestructiveAction, useFormContext, usePreferences, useSidebar, useTextOverflow, useToast, useToastOptional };
2540
+ export { Accordion, ActionPanel, ActionPanelGroup, Alert, type AlertPlacement, AppShell, type AppShellContextValue, type AppShellPreferences, type AppShellTheme, AsidePanel, type AsidePanelProps, type AsidePanelSide, Avatar, AvatarGroup, type AvatarGroupItem, type AvatarGroupProps, type AvatarStatus, Badge, type BadgePaletteColor, type BadgeSeverity, type BadgeSize, type BadgeVariant, BottomNav, type BottomNavItem, type BreadcrumbItem$1 as BreadcrumbItem, Breadcrumbs, type BreadcrumbsSeparator, type BreadcrumbsSize, type BreadcrumbsVariant, BreakpointKey, BulkActionBar, Button, ButtonSizeContext, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardSelectType, CardTitle, type CardVariant, Carousel, type ChatMessage, type ChatMessageAction, type ChatMessageLayout, ChatMessageList, type ChatMessageSender, type ChatMessageSource, type ChatMessageStatus, ChatPanel, type ChatPanelProps, type ChatPanelStatusTone, Checkbox, CheckboxGroup, CheckboxGroupItem, CodeBlock, Collapsible, ColorPicker, type Column, Combobox, CommandPalette, ContextMenu, type ControlSize, ControlSizeContext, CornerPanel, type CornerPanelEdge, type CornerPanelProps, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, type DateRange, DateRangePicker, type DateRangePickerProps, type DateRangePreset, type DescriptionItem, DescriptionList, type DescriptionListOrientation, type DescriptionListSize, type DestructiveActionHandle, type DestructiveActionOptions, Dialog, Divider, Dock, Drawer, Dropdown, DropdownMenu, type DropdownMenuDivider, type DropdownMenuEntry, type DropdownMenuItem, EmptyState, EntityCell, Field, type FieldControlProps, type FieldProps, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, type LauncherEdge, type LauncherProps, type LauncherVariant, type LauncherWhenOpen, Link, type LinkProps, MentionInput, MobileHeaderMenu, type MobileHeaderMenuItemData, type MobileHeaderMenuType, NavItem, type NavItemProps, type NavItemType, NavMenu, type NavMenuItem, Navbar, type NavbarSize, type NavbarType, type NavbarVariant, NotificationCenter, NumberInput, PageContainer, type PageContainerWidth, PageHeader, type PageHeaderBehavior, type PageHeaderIntensity, type PageHeaderProps, type PageHeaderVariant, type Pager, Pagination, type PaginationOptions, PinInput, Popover, PreferencesContext, Progress, type PromiseToastMessages, RadioGroup, Rating, type ResolvedPreferences, ScrollArea, SectionHeading, type SectionHeadingProps, SegmentedControl, SettingsLayout, type SettingsLayoutProps, type SettingsSection, Sheet, Sidebar, type SidebarContextValue, type SidebarItem, SidebarNav, type SidebarNavSize, type SidebarPosition, type SidebarSection, type SidebarType, Skeleton, SkipLink, SliderRange, Spinner, StackedList, type StackedListVariant, StatsCard, StatusDot, type StatusDotVariant, Stepper, type Tab, Table, type TableDensity, type TableProps, TableToolbar, type TableToolbarActiveFilter, type TableToolbarFilterOption, Tabs, Tag, TagInput, type TagPaletteColor, type TagVariant, TextOverflow, type TextOverflowProps, Textarea, TimePicker, Timeline, Toast, type ToastAction, type ToastApi, type ToastOptions, type ToastPosition, ToastProvider, type ToastVariant, Toggle, Tooltip, Tour, TransferList, TreeView, type Trend, type UseTextOverflowOptions, type UseTextOverflowResult, preferenceActionKey, useAppShell, useContentWidth, useDestructiveAction, useFormContext, usePagination, usePreferences, useSidebar, useTextOverflow, useToast, useToastOptional };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { useAppShell, ControlSizeContext, Button, Spinner, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, useDirtyGuard, DiscardConfirm, defaultMaxWidth } from './chunk-YMUCTZXS.js';
2
- export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell } from './chunk-YMUCTZXS.js';
1
+ import { useAppShell, ControlSizeContext, Button, Spinner, OverlayCloseCtx, DrawerFooterCtx, useCompact, useElementWidth, Sheet, useCloseSettle, Drawer, useRememberedWidth, pxOf, ResizeEdge, useDirtyGuard, DiscardConfirm, defaultMaxWidth } from './chunk-HL3KYWOK.js';
2
+ export { AppShell, Button, ButtonSizeContext, ControlSizeContext, Drawer, Sheet, SkipLink, Spinner, useAppShell } from './chunk-HL3KYWOK.js';
3
3
  import { Skeleton } from './chunk-M3AARGLI.js';
4
4
  export { Skeleton } from './chunk-M3AARGLI.js';
5
5
  import { useOverlayLayer, useCloseOnFocusOut, focusInitial, rovingTabStop, OVERLAY_CLOSE_ATTR, useNonModalDialog, useFocusTrap } from './chunk-NHQCMOAN.js';
@@ -8,8 +8,8 @@ export { CodeBlock, Divider, Link } from './chunk-QJBCZ7BR.js';
8
8
  import { Badge } from './chunk-U2JT37GJ.js';
9
9
  export { Badge } from './chunk-U2JT37GJ.js';
10
10
  import { inferColumnKinds, CardValue } from './chunk-CR2KF73R.js';
11
- import { SplitPaneCtx, usePreferences, TableCardCtx, preferenceActionKey } from './chunk-QNVSMY4R.js';
12
- export { PreferencesContext, preferenceActionKey, usePreferences } from './chunk-QNVSMY4R.js';
11
+ import { SplitPaneCtx, useShellPreferences, TableCardCtx, PREFERENCE_HOUSE_DEFAULTS, preferenceActionKey } from './chunk-YO7JVPXK.js';
12
+ export { PreferencesContext, preferenceActionKey, usePreferences } from './chunk-YO7JVPXK.js';
13
13
  import { useBreakpoint } from './chunk-CRQ6KCRD.js';
14
14
  export { BreakpointWidthProvider, useBreakpoint } from './chunk-CRQ6KCRD.js';
15
15
  import { useTextOverflow, Tooltip, usePosition } from './chunk-IYD6ZDM2.js';
@@ -3859,10 +3859,19 @@ function collectInvalidFields(form) {
3859
3859
  }
3860
3860
  return out;
3861
3861
  }
3862
- function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions, actionsAlign: actionsAlignProp, focusFirstError = true, errorSummary = true, className, ref }) {
3863
- const preferences = usePreferences();
3862
+ function FormWrapper({ onSubmit, validationMode: validationModeProp, children, actions: actionsProp, actionsAlign: actionsAlignProp, onCancel, cancelLabel = "Cancel", focusFirstError = true, errorSummary = true, className, ref }) {
3863
+ const preferences = useShellPreferences();
3864
3864
  const validationMode = validationModeProp ?? preferences.validationTiming ?? "onSubmit";
3865
3865
  const actionsAlign = actionsAlignProp ?? preferences.formActions?.align ?? "end";
3866
+ const overlayClose = useContext(OverlayCloseCtx);
3867
+ const actions = useMemo(() => {
3868
+ if (!onCancel) return actionsProp;
3869
+ const cancel = /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", onClick: () => (overlayClose ?? onCancel)(), children: cancelLabel }, "gs-form-cancel");
3870
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3871
+ cancel,
3872
+ actionsProp
3873
+ ] });
3874
+ }, [actionsProp, onCancel, overlayClose, cancelLabel]);
3866
3875
  const [errors, setErrors] = useState({});
3867
3876
  const [submitted, setSubmitted] = useState(false);
3868
3877
  const [submitCount, setSubmitCount] = useState(0);
@@ -4201,6 +4210,17 @@ function PanelCheckbox({ checked, indeterminate, onClick, label }) {
4201
4210
  }
4202
4211
  );
4203
4212
  }
4213
+
4214
+ // ../../showcase/src/lib/initials.ts
4215
+ function initialsOf(name) {
4216
+ return name.trim().split(/\s+/).filter(Boolean).slice(0, 2).map((word) => Array.from(word)[0] ?? "").join("").toUpperCase();
4217
+ }
4218
+ function toInitials(value) {
4219
+ const s = value.trim();
4220
+ if (!s) return "";
4221
+ if (!/\s/.test(s) && Array.from(s).length <= 3) return s;
4222
+ return initialsOf(s);
4223
+ }
4204
4224
  var variantColors = {
4205
4225
  success: "bg-[var(--ds-status-success)]",
4206
4226
  warning: "bg-[var(--ds-status-warning)]",
@@ -4309,7 +4329,7 @@ function Avatar({ initials, src, alt, size = "md", status, color = "auto", class
4309
4329
  colorStyles(color, alt ?? initials ?? ""),
4310
4330
  "border-[length:var(--ds-border-width)] border-transparent"
4311
4331
  ),
4312
- children: initials
4332
+ children: initials != null ? toInitials(initials) : null
4313
4333
  }
4314
4334
  );
4315
4335
  if (!status) return avatarElement;
@@ -5243,7 +5263,7 @@ function Table({
5243
5263
  (col) => col.kind ?? inferredKinds.get(col.key),
5244
5264
  [inferredKinds]
5245
5265
  );
5246
- const preferences = usePreferences();
5266
+ const preferences = useShellPreferences();
5247
5267
  const mobileBreakpoint = mobileBreakpointProp ?? preferences.tableToCardsBreakpoint ?? "md";
5248
5268
  const isMobile = useBreakpoint(mobileBreakpoint);
5249
5269
  if (isMobile) {
@@ -7208,7 +7228,7 @@ function renderIcon(icon) {
7208
7228
  return icon;
7209
7229
  }
7210
7230
  function EmptyState({ icon, iconFrame = "circle", title, description, action, variant = "empty", bordered = false, style: styleProp, className }) {
7211
- const preferences = usePreferences();
7231
+ const preferences = useShellPreferences();
7212
7232
  const style = styleProp ?? (preferences.emptyStyle === "text-cta" ? "text" : "illustration");
7213
7233
  const iconNode = style === "text" ? void 0 : icon === void 0 || icon === null ? void 0 : renderIcon(icon);
7214
7234
  return /* @__PURE__ */ jsxs(
@@ -7231,9 +7251,6 @@ function EmptyState({ icon, iconFrame = "circle", title, description, action, va
7231
7251
  }
7232
7252
  );
7233
7253
  }
7234
- function initialsOf(name) {
7235
- return name.trim().split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
7236
- }
7237
7254
  function looksLikeUrl(s) {
7238
7255
  return /^(?:https?:|data:|blob:|\/|\.{1,2}\/)/.test(s) || /\.[a-z]{2,5}(?:$|[?#])/i.test(s);
7239
7256
  }
@@ -7289,6 +7306,7 @@ function TableToolbar({
7289
7306
  onClearAll,
7290
7307
  resultCount,
7291
7308
  resultNoun = ["result", "results"],
7309
+ pagination,
7292
7310
  actions,
7293
7311
  size = "sm",
7294
7312
  compactBreakpoint = 680,
@@ -7308,8 +7326,14 @@ function TableToolbar({
7308
7326
  const hasChips = !!activeFilters?.length;
7309
7327
  const inPane = useContext(SplitPaneCtx);
7310
7328
  const searchFills = inPane || !primaryFilter && !filters && !actions;
7311
- return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-3 min-w-0", className), "data-table-toolbar": "", children: [
7312
- /* @__PURE__ */ jsxs("div", { ref: rowRef, className: "flex items-center gap-ds-2 flex-wrap", children: [
7329
+ const hasControls = !!(search || primaryFilter || filters || actions);
7330
+ const noun = (n) => n === 1 ? resultNoun[0] : resultNoun[1];
7331
+ const total = resultCount ?? pagination?.totalItems;
7332
+ const paged = total !== void 0 && !!pagination && pagination.pageSize > 0 && total > pagination.pageSize;
7333
+ const countText = total === void 0 ? null : paged ? `Showing ${Math.min((pagination.page - 1) * pagination.pageSize + 1, total)}\u2013${Math.min(pagination.page * pagination.pageSize, total)} of ${total} ${noun(total)}` : resultCount !== void 0 ? `${resultCount} ${noun(resultCount)}` : null;
7334
+ if (!hasControls && countText === null && !hasChips) return null;
7335
+ return /* @__PURE__ */ jsx(ControlSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-ds-3 min-w-0 mb-ds-3 last:mb-0", className), "data-table-toolbar": "", children: [
7336
+ hasControls && /* @__PURE__ */ jsxs("div", { ref: rowRef, className: "flex items-center gap-ds-2 flex-wrap", children: [
7313
7337
  search && /* @__PURE__ */ jsx(
7314
7338
  Input,
7315
7339
  {
@@ -7362,12 +7386,8 @@ function TableToolbar({
7362
7386
  ] })
7363
7387
  ] }),
7364
7388
  filters && open && /* @__PURE__ */ jsx("div", { id: filterRowId, className: "flex items-center gap-ds-3 flex-wrap", children: filters }),
7365
- (resultCount !== void 0 || hasChips) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 flex-wrap", children: [
7366
- resultCount !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--ds-text-muted)]", "aria-live": "polite", children: [
7367
- resultCount,
7368
- " ",
7369
- resultCount === 1 ? resultNoun[0] : resultNoun[1]
7370
- ] }),
7389
+ (countText !== null || hasChips) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2 flex-wrap", children: [
7390
+ countText !== null && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--ds-text-muted)]", "aria-live": "polite", "data-result-count": "", children: countText }),
7371
7391
  activeFilters?.map((f) => /* @__PURE__ */ jsx(Tag, { size: "sm", variant: "subtle", onRemove: f.onRemove, children: f.label }, f.id)),
7372
7392
  hasChips && onClearAll && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: "Clear all" })
7373
7393
  ] })
@@ -8397,16 +8417,18 @@ var TOAST_INSET_VARS = {
8397
8417
  "bottom-right": "--ds-toast-inset-bottom-right",
8398
8418
  "bottom-left": "--ds-toast-inset-bottom-left",
8399
8419
  right: "--ds-toast-inset-right",
8400
- left: "--ds-toast-inset-left"
8420
+ left: "--ds-toast-inset-left",
8421
+ top: "--ds-toast-inset-top"
8401
8422
  };
8402
8423
  var LAUNCHER_INSET_PRIORITY = 1;
8403
8424
  var PANEL_INSET_PRIORITY = 2;
8425
+ var NAVBAR_INSET_PRIORITY = 1;
8404
8426
  var registry = /* @__PURE__ */ new WeakMap();
8405
8427
  var seq = 0;
8406
8428
  function claimsFor(target) {
8407
8429
  let entry = registry.get(target);
8408
8430
  if (!entry) {
8409
- entry = { "bottom-right": [], "bottom-left": [], right: [], left: [] };
8431
+ entry = { "bottom-right": [], "bottom-left": [], right: [], left: [], top: [] };
8410
8432
  registry.set(target, entry);
8411
8433
  }
8412
8434
  return entry;
@@ -9827,7 +9849,7 @@ function Popover({ open, onOpenChange, trigger, children, side = "bottom", align
9827
9849
  }
9828
9850
  function Pagination({ currentPage, totalPages, onPageChange, pageSize: pageSizeProp, pageSizeOptions, onPageSizeChange, className }) {
9829
9851
  const pages = buildPageRange(currentPage, totalPages);
9830
- const preferences = usePreferences();
9852
+ const preferences = useShellPreferences();
9831
9853
  const pageSize = pageSizeProp ?? preferences.itemsPerPage;
9832
9854
  return /* @__PURE__ */ jsxs("nav", { "aria-label": "Pagination", className: cn("flex flex-wrap items-center gap-ds-3", className), children: [
9833
9855
  pageSizeOptions && pageSize != null && onPageSizeChange && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
@@ -12612,6 +12634,31 @@ var Navbar = forwardRef(function Navbar2({
12612
12634
  ro.observe(header);
12613
12635
  return () => ro.disconnect();
12614
12636
  }, [mobileHidesLinks, links]);
12637
+ const [topInset, setTopInset] = useState(null);
12638
+ const shellEl = appShell?.container ?? null;
12639
+ useLayoutEffect(() => {
12640
+ const header = headerRef.current;
12641
+ if (!shellEl || !header) {
12642
+ setTopInset(null);
12643
+ return;
12644
+ }
12645
+ const measure = () => {
12646
+ const shell = shellEl.getBoundingClientRect();
12647
+ const fillsWindow = shell.top <= 1 && shell.height >= window.innerHeight - 1;
12648
+ const bottom = Math.round(header.getBoundingClientRect().bottom);
12649
+ setTopInset(fillsWindow && bottom > 0 ? `calc(${bottom}px + var(--ds-space-3))` : null);
12650
+ };
12651
+ measure();
12652
+ window.addEventListener("resize", measure);
12653
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
12654
+ ro?.observe(header);
12655
+ ro?.observe(shellEl);
12656
+ return () => {
12657
+ window.removeEventListener("resize", measure);
12658
+ ro?.disconnect();
12659
+ };
12660
+ }, [shellEl]);
12661
+ useCornerInset(topInset ? "top" : null, topInset, NAVBAR_INSET_PRIORITY);
12615
12662
  const hidden = useMemo(() => {
12616
12663
  if (visibleCount === null || links.length === 0) return /* @__PURE__ */ new Set();
12617
12664
  return new Set(links.map((_, i) => i).filter((i) => i >= visibleCount));
@@ -12961,7 +13008,7 @@ function Dialog({ open, onClose: onCloseProp, title, description, children, foot
12961
13008
  }
12962
13009
  ),
12963
13010
  description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "mt-ds-2 text-sm text-[var(--ds-text-muted)]", children: description }),
12964
- children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children })
13011
+ children && /* @__PURE__ */ jsx("div", { className: "mt-ds-4", children: /* @__PURE__ */ jsx(OverlayCloseCtx, { value: onClose, children }) })
12965
13012
  ] })
12966
13013
  ] }) }),
12967
13014
  /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard }),
@@ -13838,11 +13885,11 @@ var variantIcons = {
13838
13885
  error: { name: "error", className: "text-[var(--ds-status-error)]" }
13839
13886
  };
13840
13887
  var TOAST_POSITION_CLASSES = {
13841
- "top-right": "top-ds-4 right-[var(--ds-toast-inset-right)]",
13842
- "top-left": "top-ds-4 left-[var(--ds-toast-inset-left)]",
13888
+ "top-right": "top-[var(--ds-toast-inset-top)] right-[var(--ds-toast-inset-right)]",
13889
+ "top-left": "top-[var(--ds-toast-inset-top)] left-[var(--ds-toast-inset-left)]",
13843
13890
  "bottom-right": "bottom-[var(--ds-toast-inset-bottom-right)] right-[var(--ds-toast-inset-right)]",
13844
13891
  "bottom-left": "bottom-[var(--ds-toast-inset-bottom-left)] left-[var(--ds-toast-inset-left)]",
13845
- "top-center": "top-ds-4 left-1/2 -translate-x-1/2",
13892
+ "top-center": "top-[var(--ds-toast-inset-top)] left-1/2 -translate-x-1/2",
13846
13893
  "bottom-center": "bottom-ds-4 left-1/2 -translate-x-1/2"
13847
13894
  };
13848
13895
  function Toast({ variant = "default", message, visible, onDismiss, duration = 5e3, pauseOnHover = false, action, position = "bottom-right", mode, container, inline = false }) {
@@ -14040,9 +14087,62 @@ function ToastProvider({ children, maxVisible = 1, position = "bottom-right", du
14040
14087
  )
14041
14088
  ] });
14042
14089
  }
14090
+ function firstKey(item) {
14091
+ if (item !== null && typeof item === "object") return "id" in item ? item.id : void 0;
14092
+ return item;
14093
+ }
14094
+ function signature(list, resetOn) {
14095
+ let reset = "";
14096
+ try {
14097
+ reset = JSON.stringify(resetOn ?? null);
14098
+ } catch {
14099
+ reset = String(resetOn);
14100
+ }
14101
+ let first = "";
14102
+ try {
14103
+ first = JSON.stringify(firstKey(list[0]) ?? null);
14104
+ } catch {
14105
+ first = "";
14106
+ }
14107
+ return `${list.length}|${first}|${reset}`;
14108
+ }
14109
+ function usePagination(list, options = {}) {
14110
+ const shell = useShellPreferences();
14111
+ const [chosenSize, setChosenSize] = useState(null);
14112
+ const pageSize = Math.max(1, Math.floor(chosenSize ?? options.pageSize ?? shell.itemsPerPage ?? PREFERENCE_HOUSE_DEFAULTS.itemsPerPage));
14113
+ const [page, setPageState] = useState(1);
14114
+ const sig = signature(list, options.resetOn);
14115
+ const [seen, setSeen] = useState(sig);
14116
+ if (seen !== sig) {
14117
+ setSeen(sig);
14118
+ if (page !== 1) setPageState(1);
14119
+ }
14120
+ const totalItems = list.length;
14121
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
14122
+ const current = Math.min(Math.max(1, seen !== sig ? 1 : page), totalPages);
14123
+ const start = (current - 1) * pageSize;
14124
+ const items = list.slice(start, start + pageSize);
14125
+ const range = totalItems === 0 ? [0, 0] : [start + 1, start + items.length];
14126
+ const setPage = (next) => setPageState(Math.min(Math.max(1, Math.floor(next)), totalPages));
14127
+ const setPageSize = (size) => {
14128
+ setChosenSize(Math.max(1, Math.floor(size)));
14129
+ setPageState(1);
14130
+ };
14131
+ return {
14132
+ items,
14133
+ page: current,
14134
+ pageSize,
14135
+ totalItems,
14136
+ totalPages,
14137
+ range,
14138
+ setPage,
14139
+ setPageSize,
14140
+ props: { currentPage: current, totalPages, onPageChange: setPage, pageSize, onPageSizeChange: setPageSize }
14141
+ };
14142
+ }
14043
14143
  function useDestructiveAction(options) {
14044
14144
  const { action, title, description, confirmLabel, typeToConfirm, onConfirm, onUndo, undoMessage, severity } = options;
14045
- const preferences = usePreferences();
14145
+ const preferences = useShellPreferences();
14046
14146
  const toast = useToastOptional();
14047
14147
  const [open, setOpen] = useState(false);
14048
14148
  const [typed, setTyped] = useState("");
@@ -14248,6 +14348,6 @@ var TextOverflow = forwardRef(function TextOverflow2({
14248
14348
  return /* @__PURE__ */ jsx(Tooltip, { content: resolvedTooltipContent, children: content });
14249
14349
  });
14250
14350
 
14251
- export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useContentWidth, useDestructiveAction, useFormContext, useSidebar, useToast, useToastOptional };
14351
+ export { Accordion, ActionPanel, ActionPanelGroup, Alert, AsidePanel, Avatar, AvatarGroup, BottomNav, Breadcrumbs, BulkActionBar, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, ChatMessageList, ChatPanel, Checkbox, CheckboxGroup, CheckboxGroupItem, Collapsible, ColorPicker, Combobox, CommandPalette, ContextMenu, CornerPanel, DEFAULT_DATE_RANGE_PRESETS, DataList, DatePicker, DateRangePicker, DescriptionList, Dialog, Dock, Dropdown, DropdownMenu, EmptyState, EntityCell, Field, FileUpload, FormWrapper, HoverCard, InlineFieldError, Input, Kbd, Label, Launcher, MentionInput, MobileHeaderMenu, NavItem, NavMenu, Navbar, NotificationCenter, NumberInput, PageContainer, PageHeader, Pagination, PinInput, Popover, Progress, RadioGroup, Rating, ScrollArea, SectionHeading, SegmentedControl, SettingsLayout, Sidebar, SidebarNav, SliderRange, StackedList, StatsCard, StatusDot, Stepper, Table, TableToolbar, Tabs, Tag, TagInput, TextOverflow, Textarea, TimePicker, Timeline, Toast, ToastProvider, Toggle, Tour, TransferList, TreeView, useContentWidth, useDestructiveAction, useFormContext, usePagination, useSidebar, useToast, useToastOptional };
14252
14352
  //# sourceMappingURL=index.js.map
14253
14353
  //# sourceMappingURL=index.js.map