@sovereignfs/ui 0.41.0 → 0.54.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/Accordion.module.css +3 -0
- package/dist/Alert.module.css +62 -0
- package/dist/AspectRatio.module.css +15 -0
- package/dist/Badge.module.css +75 -9
- package/dist/Breadcrumb.module.css +49 -0
- package/dist/Button.module.css +12 -3
- package/dist/ButtonGroup.module.css +29 -0
- package/dist/Card.module.css +6 -0
- package/dist/Checkbox.module.css +3 -1
- package/dist/Collapsible.module.css +70 -0
- package/dist/Combobox.module.css +122 -0
- package/dist/Command.module.css +80 -0
- package/dist/ConfirmDialog.module.css +5 -1
- package/dist/DataTable.module.css +28 -0
- package/dist/DatePicker.module.css +6 -2
- package/dist/Dialog.module.css +3 -0
- package/dist/HoverCard.module.css +3 -0
- package/dist/Input.module.css +6 -1
- package/dist/Item.module.css +70 -0
- package/dist/Kbd.module.css +14 -0
- package/dist/Label.module.css +11 -0
- package/dist/LegalLinks.module.css +24 -0
- package/dist/Markdown.module.css +49 -0
- package/dist/Marker.module.css +33 -0
- package/dist/Menu.module.css +8 -1
- package/dist/Message.module.css +99 -0
- package/dist/MessageScroller.module.css +54 -0
- package/dist/MobileAppsDrawer.module.css +81 -0
- package/dist/MobileFooter.module.css +73 -0
- package/dist/MobileHeader.module.css +63 -0
- package/dist/NavigationMenu.module.css +53 -0
- package/dist/PageContainer.module.css +20 -0
- package/dist/Pagination.module.css +98 -0
- package/dist/Progress.module.css +20 -0
- package/dist/RadioGroup.module.css +88 -0
- package/dist/Resizable.module.css +70 -0
- package/dist/ScrollArea.module.css +20 -0
- package/dist/Select.module.css +7 -3
- package/dist/Slider.module.css +95 -0
- package/dist/SwipableMobileCarousel.module.css +55 -0
- package/dist/SwipableMobileCarouselDots.module.css +49 -0
- package/dist/SwipableMobileCarouselSlideParts.module.css +29 -0
- package/dist/Table.module.css +45 -0
- package/dist/Tabs.module.css +6 -1
- package/dist/Toggle.module.css +6 -1
- package/dist/Typography.module.css +47 -0
- package/dist/index.d.ts +1268 -8
- package/dist/index.js +3046 -975
- package/dist/tokens/primitives.css +47 -8
- package/package.json +13 -9
- package/src/components/Accordion/Accordion.module.css +3 -0
- package/src/components/Accordion/Accordion.stories.tsx +110 -0
- package/src/components/Accordion/Accordion.tsx +53 -0
- package/src/components/Accordion/__tests__/Accordion.test.tsx +61 -0
- package/src/components/Alert/Alert.module.css +62 -0
- package/src/components/Alert/Alert.stories.tsx +96 -0
- package/src/components/Alert/Alert.tsx +58 -0
- package/src/components/Alert/__tests__/Alert.test.tsx +75 -0
- package/src/components/AspectRatio/AspectRatio.module.css +15 -0
- package/src/components/AspectRatio/AspectRatio.stories.tsx +78 -0
- package/src/components/AspectRatio/AspectRatio.tsx +21 -0
- package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +27 -0
- package/src/components/Badge/Badge.module.css +75 -9
- package/src/components/Badge/Badge.stories.tsx +109 -1
- package/src/components/Badge/Badge.tsx +31 -9
- package/src/components/Badge/__tests__/Badge.test.tsx +26 -2
- package/src/components/Breadcrumb/Breadcrumb.module.css +49 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +47 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +57 -0
- package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +52 -0
- package/src/components/Button/Button.module.css +12 -3
- package/src/components/Button/Button.stories.tsx +3 -2
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Button/__tests__/Button.test.tsx +10 -0
- package/src/components/ButtonGroup/ButtonGroup.module.css +29 -0
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +57 -0
- package/src/components/ButtonGroup/ButtonGroup.tsx +27 -0
- package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +29 -0
- package/src/components/Card/Card.module.css +6 -0
- package/src/components/Checkbox/Checkbox.module.css +3 -1
- package/src/components/Collapsible/Collapsible.module.css +70 -0
- package/src/components/Collapsible/Collapsible.stories.tsx +62 -0
- package/src/components/Collapsible/Collapsible.tsx +66 -0
- package/src/components/Collapsible/__tests__/Collapsible.test.tsx +71 -0
- package/src/components/Combobox/Combobox.module.css +122 -0
- package/src/components/Combobox/Combobox.stories.tsx +79 -0
- package/src/components/Combobox/Combobox.tsx +186 -0
- package/src/components/Combobox/__tests__/Combobox.test.tsx +158 -0
- package/src/components/Command/Command.module.css +80 -0
- package/src/components/Command/Command.stories.tsx +104 -0
- package/src/components/Command/Command.tsx +168 -0
- package/src/components/Command/__tests__/Command.test.tsx +162 -0
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +5 -1
- package/src/components/ContextMenu/ContextMenu.stories.tsx +81 -0
- package/src/components/ContextMenu/ContextMenu.tsx +100 -0
- package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +87 -0
- package/src/components/DataTable/DataTable.module.css +28 -0
- package/src/components/DataTable/DataTable.stories.tsx +74 -0
- package/src/components/DataTable/DataTable.tsx +154 -0
- package/src/components/DataTable/__tests__/DataTable.test.tsx +123 -0
- package/src/components/DatePicker/DatePicker.module.css +6 -2
- package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +58 -0
- package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +45 -0
- package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +100 -0
- package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +119 -0
- package/src/components/Dialog/Dialog.module.css +3 -0
- package/src/components/Dialog/Dialog.tsx +8 -59
- package/src/components/Drawer/Drawer.stories.tsx +44 -0
- package/src/components/Drawer/Drawer.tsx +18 -56
- package/src/components/Drawer/__tests__/Drawer.test.tsx +22 -0
- package/src/components/HoverCard/HoverCard.module.css +3 -0
- package/src/components/HoverCard/HoverCard.stories.tsx +66 -0
- package/src/components/HoverCard/HoverCard.tsx +111 -0
- package/src/components/HoverCard/__tests__/HoverCard.test.tsx +99 -0
- package/src/components/Icon/icons/circle-check.tsx +21 -0
- package/src/components/Icon/icons/circle-x.tsx +22 -0
- package/src/components/Icon/icons/index.ts +16 -0
- package/src/components/Icon/icons/menu.tsx +22 -0
- package/src/components/Icon/icons/smartphone.tsx +21 -0
- package/src/components/Input/Input.module.css +6 -1
- package/src/components/Item/Item.module.css +70 -0
- package/src/components/Item/Item.stories.tsx +62 -0
- package/src/components/Item/Item.tsx +54 -0
- package/src/components/Item/__tests__/Item.test.tsx +39 -0
- package/src/components/Kbd/Kbd.module.css +14 -0
- package/src/components/Kbd/Kbd.stories.tsx +32 -0
- package/src/components/Kbd/Kbd.tsx +12 -0
- package/src/components/Kbd/__tests__/Kbd.test.tsx +14 -0
- package/src/components/Label/Label.module.css +11 -0
- package/src/components/Label/Label.stories.tsx +40 -0
- package/src/components/Label/Label.tsx +24 -0
- package/src/components/Label/__tests__/Label.test.tsx +32 -0
- package/src/components/LegalLinks/LegalLinks.module.css +24 -0
- package/src/components/LegalLinks/LegalLinks.stories.tsx +22 -0
- package/src/components/LegalLinks/LegalLinks.tsx +40 -0
- package/src/components/Markdown/Markdown.module.css +49 -0
- package/src/components/Markdown/Markdown.stories.tsx +30 -0
- package/src/components/Markdown/Markdown.tsx +168 -0
- package/src/components/Marker/Marker.module.css +33 -0
- package/src/components/Marker/Marker.stories.tsx +39 -0
- package/src/components/Marker/Marker.tsx +34 -0
- package/src/components/Marker/__tests__/Marker.test.tsx +23 -0
- package/src/components/Menu/Menu.module.css +8 -1
- package/src/components/Menu/Menu.tsx +30 -8
- package/src/components/Menubar/Menubar.stories.tsx +52 -0
- package/src/components/Menubar/Menubar.tsx +42 -0
- package/src/components/Menubar/__tests__/Menubar.test.tsx +45 -0
- package/src/components/Message/Message.module.css +99 -0
- package/src/components/Message/Message.stories.tsx +79 -0
- package/src/components/Message/Message.tsx +41 -0
- package/src/components/Message/__tests__/Message.test.tsx +46 -0
- package/src/components/MessageScroller/MessageScroller.module.css +54 -0
- package/src/components/MessageScroller/MessageScroller.stories.tsx +84 -0
- package/src/components/MessageScroller/MessageScroller.tsx +72 -0
- package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +105 -0
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +81 -0
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +78 -0
- package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +80 -0
- package/src/components/MobileFooter/MobileFooter.module.css +73 -0
- package/src/components/MobileFooter/MobileFooter.stories.tsx +47 -0
- package/src/components/MobileFooter/MobileFooter.tsx +109 -0
- package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +71 -0
- package/src/components/MobileHeader/MobileHeader.module.css +63 -0
- package/src/components/MobileHeader/MobileHeader.stories.tsx +57 -0
- package/src/components/MobileHeader/MobileHeader.tsx +77 -0
- package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +58 -0
- package/src/components/NavTabs/NavTabs.tsx +39 -11
- package/src/components/NavigationMenu/NavigationMenu.module.css +53 -0
- package/src/components/NavigationMenu/NavigationMenu.stories.tsx +58 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +125 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +93 -0
- package/src/components/OfflineGate/OfflineGate.tsx +44 -0
- package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +66 -0
- package/src/components/PageContainer/PageContainer.module.css +20 -0
- package/src/components/PageContainer/PageContainer.tsx +31 -0
- package/src/components/PageContainer/__tests__/PageContainer.test.tsx +37 -0
- package/src/components/PageHeader/PageHeader.tsx +16 -2
- package/src/components/Pagination/Pagination.module.css +98 -0
- package/src/components/Pagination/Pagination.stories.tsx +49 -0
- package/src/components/Pagination/Pagination.tsx +87 -0
- package/src/components/Pagination/__tests__/Pagination.test.tsx +56 -0
- package/src/components/Progress/Progress.module.css +20 -0
- package/src/components/Progress/Progress.stories.tsx +40 -0
- package/src/components/Progress/Progress.tsx +35 -0
- package/src/components/Progress/__tests__/Progress.test.tsx +31 -0
- package/src/components/RadioGroup/RadioGroup.module.css +88 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +69 -0
- package/src/components/RadioGroup/RadioGroup.tsx +99 -0
- package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +59 -0
- package/src/components/Resizable/Resizable.module.css +70 -0
- package/src/components/Resizable/Resizable.stories.tsx +82 -0
- package/src/components/Resizable/Resizable.tsx +313 -0
- package/src/components/Resizable/__tests__/Resizable.test.tsx +128 -0
- package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +24 -0
- package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +73 -0
- package/src/components/ScrollArea/ScrollArea.module.css +20 -0
- package/src/components/ScrollArea/ScrollArea.stories.tsx +57 -0
- package/src/components/ScrollArea/ScrollArea.tsx +27 -0
- package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +39 -0
- package/src/components/Select/Select.module.css +7 -3
- package/src/components/Sheet/Sheet.tsx +9 -51
- package/src/components/Slider/Slider.module.css +95 -0
- package/src/components/Slider/Slider.stories.tsx +64 -0
- package/src/components/Slider/Slider.tsx +77 -0
- package/src/components/Slider/__tests__/Slider.test.tsx +45 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +55 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +227 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +35 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +42 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +18 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +21 -0
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +29 -0
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +133 -0
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +61 -0
- package/src/components/SwipableMobileCarousel/context.ts +13 -0
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +49 -0
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +62 -0
- package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +63 -0
- package/src/components/Table/Table.module.css +45 -0
- package/src/components/Table/Table.stories.tsx +125 -0
- package/src/components/Table/Table.tsx +51 -0
- package/src/components/Table/__tests__/Table.test.tsx +50 -0
- package/src/components/Tabs/Tabs.module.css +6 -1
- package/src/components/Toggle/Toggle.module.css +6 -1
- package/src/components/Typography/Typography.module.css +47 -0
- package/src/components/Typography/Typography.stories.tsx +62 -0
- package/src/components/Typography/Typography.tsx +30 -0
- package/src/components/Typography/__tests__/Typography.test.tsx +34 -0
- package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +126 -0
- package/src/hooks/__tests__/useIsOffline.test.tsx +53 -0
- package/src/hooks/__tests__/useOfflineTileState.test.tsx +69 -0
- package/src/hooks/__tests__/useResponsiveLayout.test.tsx +87 -0
- package/src/hooks/__tests__/useSnapCarousel.test.tsx +93 -0
- package/src/hooks/__tests__/useSwipeReveal.test.tsx +110 -0
- package/src/hooks/index.ts +18 -0
- package/src/hooks/useCarouselRouteSync.ts +92 -0
- package/src/hooks/useIsOffline.ts +41 -0
- package/src/hooks/useOfflineTileState.ts +45 -0
- package/src/hooks/useResponsiveLayout.ts +36 -0
- package/src/hooks/useSnapCarousel.ts +79 -0
- package/src/hooks/useSwipeReveal.ts +107 -0
- package/src/index.ts +141 -3
- package/src/overlay-shell.ts +94 -0
- package/src/stories/DesignSystemOverview.stories.tsx +936 -3
- package/src/stories/DeviceOnlyGate.stories.tsx +35 -0
- package/src/stories/DeviceStorageKeyGate.stories.tsx +57 -0
- package/src/stories/InteractionHooks.stories.tsx +483 -0
- package/src/stories/MobilePatterns.stories.tsx +255 -4
- package/src/stories/NavTabs.stories.tsx +18 -0
- package/src/stories/OfflineGate.stories.tsx +42 -0
- package/src/stories/PageContainer.stories.tsx +37 -0
- package/src/stories/PageHeader.stories.tsx +11 -0
- package/src/stories/ResponsiveSurface.stories.tsx +47 -0
- package/src/stories/SwipableMobileCarousel.stories.tsx +304 -0
- package/src/stories/TokenGallery.stories.tsx +177 -16
- package/src/tokens/primitives.css +47 -8
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ButtonHTMLAttributes, SelectHTMLAttributes, ReactElement, CSSProperties, InputHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, PointerEvent, MouseEvent,
|
|
2
|
+
import { ReactNode, ButtonHTMLAttributes, SelectHTMLAttributes, ReactElement, CSSProperties, InputHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes, LabelHTMLAttributes, ElementType, PointerEvent, KeyboardEvent, MouseEvent, RefObject } from 'react';
|
|
3
|
+
|
|
4
|
+
interface LegalLinksProps {
|
|
5
|
+
privacyHref?: string;
|
|
6
|
+
tosHref?: string;
|
|
7
|
+
/** Renders a linked item. Defaults to a plain `<a href>`, which causes a
|
|
8
|
+
* full page reload — pass Next's `<Link>` (or equivalent) to keep
|
|
9
|
+
* navigation client-side. Not needed for a full-reload navigation away
|
|
10
|
+
* from a standalone auth page, but matters if this is ever mounted inside
|
|
11
|
+
* an SPA-like shell. */
|
|
12
|
+
renderLink?: (href: string, label: string) => ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
declare function LegalLinks({ privacyHref, tosHref, renderLink, className, }: LegalLinksProps): react.JSX.Element;
|
|
16
|
+
|
|
17
|
+
interface MarkdownProps {
|
|
18
|
+
/** Raw markdown source. Supports the subset used by Sovereign's own
|
|
19
|
+
* long-form content pages: headings (#/##/###), paragraphs, **bold**,
|
|
20
|
+
* *italic*, `code`, [links](url), unordered lists (-), and blockquotes
|
|
21
|
+
* (>), including soft-wrapped continuation lines within a paragraph or
|
|
22
|
+
* list item. Not a general-purpose CommonMark parser — no tables,
|
|
23
|
+
* images, ordered lists, or nested blocks. */
|
|
24
|
+
content: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Renders a constrained markdown subset (see MarkdownProps) as styled React
|
|
28
|
+
* elements built from Typography — no HTML string injection. Meant for
|
|
29
|
+
* long-form, first-party content pages (privacy policy, terms of service)
|
|
30
|
+
* sourced from a single markdown file, not for rendering arbitrary or
|
|
31
|
+
* user-supplied markdown. */
|
|
32
|
+
declare function Markdown({ content, className }: MarkdownProps): react.JSX.Element;
|
|
3
33
|
|
|
4
34
|
type BadgeVariant = 'role' | 'status' | 'mono';
|
|
35
|
+
type BadgeSize = 'sm' | 'md' | 'lg';
|
|
5
36
|
type BadgeStatus = 'active' | 'enabled' | 'deactivated' | 'failed' | 'invited' | 'pending' | 'neutral';
|
|
6
37
|
interface BadgeProps {
|
|
7
38
|
variant?: BadgeVariant;
|
|
39
|
+
/** 'sm' | 'md' (default) | 'lg'. 'md' is the badge's original — and until
|
|
40
|
+
* now, only — size, so the default renders unchanged. */
|
|
41
|
+
size?: BadgeSize;
|
|
8
42
|
/** Only relevant when variant="status" — determines the dot colour. */
|
|
9
43
|
status?: BadgeStatus;
|
|
44
|
+
/** Forces ALL CAPS display regardless of the case `children` is passed
|
|
45
|
+
* in. Defaults to `true` — every existing badge in the app relies on
|
|
46
|
+
* this. Set `false` for a title-case badge that renders exactly the text
|
|
47
|
+
* passed in (e.g. "Owner" instead of "OWNER"). */
|
|
48
|
+
uppercase?: boolean;
|
|
10
49
|
children: ReactNode;
|
|
11
50
|
}
|
|
12
51
|
/**
|
|
@@ -15,10 +54,13 @@ interface BadgeProps {
|
|
|
15
54
|
* - `role` neutral surface + border, semibold — for Owner / Admin / User
|
|
16
55
|
* - `status` tinted chip with leading colour dot — for Active / Deactivated / etc.
|
|
17
56
|
* - `mono` monospace font, neutral surface — for platform / community / v0.1.0
|
|
57
|
+
*
|
|
58
|
+
* Three sizes (`sm`/`md`/`lg`, default `md`). ALL CAPS by default
|
|
59
|
+
* (`uppercase`) — set `uppercase={false}` for a title-case badge instead.
|
|
18
60
|
*/
|
|
19
|
-
declare function Badge({ variant, status, children }: BadgeProps): react.JSX.Element;
|
|
61
|
+
declare function Badge({ variant, size, status, uppercase, children, }: BadgeProps): react.JSX.Element;
|
|
20
62
|
|
|
21
|
-
type IconName = 'house' | 'settings' | 'log-out' | 'chevron-right' | 'chevron-left' | 'chevron-down' | 'chevron-up' | 'x' | 'check' | 'plus' | 'trash-2' | 'pencil' | 'rotate-ccw' | 'search' | 'user' | 'shield' | 'lock' | 'eye' | 'eye-off' | 'mail' | 'bell' | 'activity' | 'package' | 'grid-2x2' | 'info' | 'alert-triangle' | 'calendar' | 'sliders-horizontal' | 'ellipsis-vertical' | 'file' | 'upload' | 'external-link' | 'banana' | 'apple' | 'carrot' | 'egg' | 'beef' | 'fish' | 'coffee' | 'wine' | 'beer' | 'cookie' | 'pizza' | 'candy' | 'salad' | 'milk' | 'drumstick' | 'croissant' | 'cup-soda' | 'spray-can' | 'snowflake' | 'shopping-basket';
|
|
63
|
+
type IconName = 'house' | 'settings' | 'log-out' | 'smartphone' | 'chevron-right' | 'chevron-left' | 'chevron-down' | 'chevron-up' | 'menu' | 'x' | 'check' | 'plus' | 'trash-2' | 'pencil' | 'rotate-ccw' | 'search' | 'user' | 'shield' | 'lock' | 'eye' | 'eye-off' | 'mail' | 'bell' | 'activity' | 'package' | 'grid-2x2' | 'info' | 'alert-triangle' | 'circle-check' | 'circle-x' | 'calendar' | 'sliders-horizontal' | 'ellipsis-vertical' | 'file' | 'upload' | 'external-link' | 'banana' | 'apple' | 'carrot' | 'egg' | 'beef' | 'fish' | 'coffee' | 'wine' | 'beer' | 'cookie' | 'pizza' | 'candy' | 'salad' | 'milk' | 'drumstick' | 'croissant' | 'cup-soda' | 'spray-can' | 'snowflake' | 'shopping-basket';
|
|
22
64
|
|
|
23
65
|
/** Decorative icon — visually meaningful but described by surrounding text. */
|
|
24
66
|
type DecorativeProps = {
|
|
@@ -194,7 +236,7 @@ interface PopoverProps {
|
|
|
194
236
|
declare function Popover({ trigger, open, onClose, align, width, panelStyle, 'aria-label': ariaLabel, children, }: PopoverProps): react.JSX.Element;
|
|
195
237
|
|
|
196
238
|
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'destructive';
|
|
197
|
-
type ButtonSize = 'sm' | 'md';
|
|
239
|
+
type ButtonSize = 'sm' | 'md' | 'lg';
|
|
198
240
|
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
199
241
|
variant?: ButtonVariant;
|
|
200
242
|
size?: ButtonSize;
|
|
@@ -362,6 +404,12 @@ interface DrawerProps {
|
|
|
362
404
|
* "half screen or less" convention for Drawer, this is the only other
|
|
363
405
|
* supported size; taller content should use `Sheet` instead. */
|
|
364
406
|
snapHeight?: 'content' | 'half';
|
|
407
|
+
/** Renders a built-in `OverlayHeader` (title + close) below the grab
|
|
408
|
+
* handle. Omit when the content supplies its own header, or none is
|
|
409
|
+
* needed at all — e.g. the mobile shell's Apps drawer, whose grab handle
|
|
410
|
+
* and swipe-down/scrim-tap dismissal are enough on their own. Mirrors
|
|
411
|
+
* `Sheet`'s identical `title` prop. */
|
|
412
|
+
title?: string;
|
|
365
413
|
children: ReactNode;
|
|
366
414
|
}
|
|
367
415
|
/**
|
|
@@ -386,7 +434,7 @@ interface DrawerProps {
|
|
|
386
434
|
* scroll, the same reasoning behind the tasks plugin's edge-zone-only
|
|
387
435
|
* swipe-to-reveal technique.
|
|
388
436
|
*/
|
|
389
|
-
declare function Drawer({ open, onClose, 'aria-label': ariaLabel, snapHeight, children, }: DrawerProps): react.JSX.Element | null;
|
|
437
|
+
declare function Drawer({ open, onClose, 'aria-label': ariaLabel, snapHeight, title, children, }: DrawerProps): react.JSX.Element | null;
|
|
390
438
|
|
|
391
439
|
interface OverlayHeaderProps {
|
|
392
440
|
/** Overlay title, shown between the optional back button and the trailing
|
|
@@ -569,6 +617,17 @@ interface MenuProps {
|
|
|
569
617
|
* Selecting an item both closes the menu and calls its `onSelect` — a
|
|
570
618
|
* consumer's `onSelect` never needs to call `onClose` itself.
|
|
571
619
|
*/
|
|
620
|
+
/**
|
|
621
|
+
* MenuEntries — the `<ul role="menu">` item list itself, factored out of
|
|
622
|
+
* `Menu` so other adaptive-surface components (e.g. `ContextMenu`) can
|
|
623
|
+
* render the exact same items/destructive/checked/icon markup inside their
|
|
624
|
+
* own positioning shell, instead of re-deriving it and risking drift.
|
|
625
|
+
*/
|
|
626
|
+
declare function MenuEntries({ items, onSelect, }: {
|
|
627
|
+
items: MenuEntry[];
|
|
628
|
+
/** Called after an item's own onSelect — typically the surface's onClose. */
|
|
629
|
+
onSelect: (entry: MenuItem) => void;
|
|
630
|
+
}): react.JSX.Element;
|
|
572
631
|
declare function Menu({ trigger, open, onClose, items, 'aria-label': ariaLabel, align }: MenuProps): react.JSX.Element;
|
|
573
632
|
|
|
574
633
|
interface ToastItem {
|
|
@@ -633,8 +692,132 @@ interface PageHeaderProps {
|
|
|
633
692
|
description?: string;
|
|
634
693
|
action?: ReactNode;
|
|
635
694
|
className?: string;
|
|
695
|
+
/** Heading level for the title. Defaults to `1` for standalone use; pass
|
|
696
|
+
* `2` or `3` when the page already sits under a shell/plugin `<h1>`, to
|
|
697
|
+
* avoid a duplicate top-level heading. Visual style is unchanged at every
|
|
698
|
+
* level — only the rendered tag changes. */
|
|
699
|
+
headingLevel?: 1 | 2 | 3;
|
|
636
700
|
}
|
|
637
|
-
declare function PageHeader({ title, description, action, className }: PageHeaderProps): react.JSX.Element;
|
|
701
|
+
declare function PageHeader({ title, description, action, className, headingLevel, }: PageHeaderProps): react.JSX.Element;
|
|
702
|
+
|
|
703
|
+
interface PageContainerProps extends HTMLAttributes<HTMLDivElement> {
|
|
704
|
+
/** Constrains and centers content width. Adds no padding of its own — the
|
|
705
|
+
* runtime shell already pads plugin content. Defaults to 'md'. */
|
|
706
|
+
maxWidth?: 'sm' | 'md' | 'lg' | 'full';
|
|
707
|
+
children: ReactNode;
|
|
708
|
+
}
|
|
709
|
+
declare function PageContainer({ maxWidth, className, children, ...rest }: PageContainerProps): react.JSX.Element;
|
|
710
|
+
|
|
711
|
+
interface MobileHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
712
|
+
/** The brand/logo element — already a complete, clickable link home (e.g. an
|
|
713
|
+
* instance logo wrapped in the consumer's own router `Link`). Always
|
|
714
|
+
* rendered. `MobileHeader` stays router-agnostic, so this is provided
|
|
715
|
+
* fully built rather than as a bare icon + href. Optional — omit it to get
|
|
716
|
+
* a default "S" badge (matching the runtime shell's own fallback brand
|
|
717
|
+
* icon when no instance logo is configured), sized and styled to the same
|
|
718
|
+
* 36px/radius-md convention a custom logo is expected to follow. */
|
|
719
|
+
logo?: ReactNode;
|
|
720
|
+
/** Optional contextual title rendered beside the logo (e.g. the active
|
|
721
|
+
* plugin's name). Absent by default — this is the one overridable part of
|
|
722
|
+
* the header. See RFC 0088. */
|
|
723
|
+
title?: string;
|
|
724
|
+
/** Notification bell — always rendered; not overridable.
|
|
725
|
+
* TODO: this is just an icon slot today (the consumer wires up its own
|
|
726
|
+
* click handler/content) — it doesn't yet accommodate a full notification
|
|
727
|
+
* feature (unread badge, dropdown panel, etc. — see the runtime shell's
|
|
728
|
+
* own NotificationBell). Deferred; revisit once a plugin actually needs
|
|
729
|
+
* more than a bare bell icon here. */
|
|
730
|
+
bell: ReactNode;
|
|
731
|
+
/** Avatar / account menu trigger — always rendered; not overridable. */
|
|
732
|
+
avatarMenu: ReactNode;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* The mobile header bar: brand/logo (always shown), an optional contextual
|
|
736
|
+
* title, and a fixed bell + avatar-menu cluster. Owns its own chrome —
|
|
737
|
+
* background, border-bottom, sticky positioning, safe-area-aware padding,
|
|
738
|
+
* and a 768px max-width (so it never stretches full-bleed if mounted outside
|
|
739
|
+
* an actual mobile viewport) — matching the runtime shell's own mobile
|
|
740
|
+
* header exactly, since this component is meant to replace that hand-rolled
|
|
741
|
+
* markup. Presentational only — no data fetching; the consumer supplies
|
|
742
|
+
* `bell`/`avatarMenu` (and, optionally, `logo`) as already-wired components
|
|
743
|
+
* (their own sizing — 36px logo/avatar, 44px bell tap target — is the
|
|
744
|
+
* consumer's responsibility, matching the reference implementation in the
|
|
745
|
+
* runtime shell). See RFC 0088 for the immutable/overridable boundary this
|
|
746
|
+
* enforces.
|
|
747
|
+
*/
|
|
748
|
+
declare function MobileHeader({ logo, title, bell, avatarMenu, className, ...rest }: MobileHeaderProps): react.JSX.Element;
|
|
749
|
+
|
|
750
|
+
interface FooterIcon {
|
|
751
|
+
icon: ReactNode;
|
|
752
|
+
label: string;
|
|
753
|
+
href?: string;
|
|
754
|
+
onClick?: () => void;
|
|
755
|
+
active?: boolean;
|
|
756
|
+
}
|
|
757
|
+
interface MobileFooterProps extends HTMLAttributes<HTMLElement> {
|
|
758
|
+
/** Opens the app drawer/launcher. Always rendered as the centered slot;
|
|
759
|
+
* not overridable. */
|
|
760
|
+
onOpenApps: () => void;
|
|
761
|
+
/** Custom launcher icon (e.g. a plugin-supplied image). Defaults to a
|
|
762
|
+
* generic grid icon. */
|
|
763
|
+
launcherIcon?: ReactNode;
|
|
764
|
+
/** Whether the launcher's own drawer is currently open (applies the
|
|
765
|
+
* pressed/active visual state). */
|
|
766
|
+
launcherOpen?: boolean;
|
|
767
|
+
/** 1 or 2 icons rendered to the left of the centered launcher. */
|
|
768
|
+
leftIcons: FooterIcon[];
|
|
769
|
+
/** 1 or 2 icons rendered to the right of the centered launcher. Must match
|
|
770
|
+
* `leftIcons.length` — the launcher stays visually centered, so a mismatch
|
|
771
|
+
* is flagged (dev-mode only, never thrown) rather than silently allowed. */
|
|
772
|
+
rightIcons: FooterIcon[];
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* The mobile footer bar: a centered, fixed "Apps" launcher flanked by 1 or 2
|
|
776
|
+
* overridable icons on each side (3 or 5 total, matching standard
|
|
777
|
+
* iOS/Android bottom-nav conventions). Owns its own chrome — background,
|
|
778
|
+
* border-top, safe-area-aware bottom padding, and a 768px max-width (so it
|
|
779
|
+
* never stretches full-bleed if mounted outside an actual mobile viewport)
|
|
780
|
+
* — matching the runtime shell's own mobile footer (MobileNav) exactly,
|
|
781
|
+
* since this component is meant to replace that hand-rolled markup.
|
|
782
|
+
* Presentational only — no data fetching; the consumer owns the
|
|
783
|
+
* drawer/overlay `onOpenApps` opens. See RFC 0088 for the
|
|
784
|
+
* immutable/overridable boundary this enforces.
|
|
785
|
+
*/
|
|
786
|
+
declare function MobileFooter({ onOpenApps, launcherIcon, launcherOpen, leftIcons, rightIcons, className, ...rest }: MobileFooterProps): react.JSX.Element;
|
|
787
|
+
|
|
788
|
+
interface MobileAppsDrawerItem {
|
|
789
|
+
/** Stable identity for this tile — used as the React key. */
|
|
790
|
+
key: string;
|
|
791
|
+
icon: ReactNode;
|
|
792
|
+
label: string;
|
|
793
|
+
/** Renders an `<a>` when set; otherwise a `<button>` calling `onClick`. */
|
|
794
|
+
href?: string;
|
|
795
|
+
onClick?: () => void;
|
|
796
|
+
}
|
|
797
|
+
interface MobileAppsDrawerProps {
|
|
798
|
+
open: boolean;
|
|
799
|
+
onClose: () => void;
|
|
800
|
+
/** Accessible name for the drawer panel — required since there's no
|
|
801
|
+
* visible title to fall back to (Drawer's grab handle plus swipe-down /
|
|
802
|
+
* scrim-tap already cover dismissal, so no header row is rendered). */
|
|
803
|
+
'aria-label': string;
|
|
804
|
+
/** The grid tiles — supplied by the consumer (e.g. installed plugins,
|
|
805
|
+
* a plugin's own sections). MobileAppsDrawer owns only the drawer
|
|
806
|
+
* chrome/grid layout, never the item list itself. */
|
|
807
|
+
items: MobileAppsDrawerItem[];
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* The mobile "Apps" drawer: a bottom sheet with a 3-column grid of icon
|
|
811
|
+
* tiles — same tile size (56px, radius 14px) and grid layout as the runtime
|
|
812
|
+
* shell's own Apps drawer (MobileNav's Drawer content), generalized so the
|
|
813
|
+
* tile list comes from the consumer (the shell passes installed plugins; a
|
|
814
|
+
* plugin composing its own MobileFooter launcher passes whatever its own
|
|
815
|
+
* "apps" are, e.g. a section index) rather than being hardcoded to plugins
|
|
816
|
+
* specifically. No header row (title/close button) — Drawer's own grab
|
|
817
|
+
* handle plus swipe-down/scrim-tap dismissal are enough, and a header ate
|
|
818
|
+
* into the limited vertical space a launcher grid needs most.
|
|
819
|
+
*/
|
|
820
|
+
declare function MobileAppsDrawer({ open, onClose, items, ...rest }: MobileAppsDrawerProps): react.JSX.Element;
|
|
638
821
|
|
|
639
822
|
interface EmptyStateProps {
|
|
640
823
|
icon?: IconName;
|
|
@@ -645,6 +828,121 @@ interface EmptyStateProps {
|
|
|
645
828
|
}
|
|
646
829
|
declare function EmptyState({ icon, heading, description, action, className }: EmptyStateProps): react.JSX.Element;
|
|
647
830
|
|
|
831
|
+
interface OfflineGateProps {
|
|
832
|
+
children: ReactNode;
|
|
833
|
+
/** Defaults to a generic "this section" phrasing — pass the surface's own name. */
|
|
834
|
+
surfaceName?: string;
|
|
835
|
+
className?: string;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* OfflineGate — blocks administrative/settings surfaces from rendering
|
|
839
|
+
* (possibly stale) cached content while the device has no network, showing an
|
|
840
|
+
* explanatory empty state instead (research 0012, epic task 2.32).
|
|
841
|
+
*
|
|
842
|
+
* Distinct from `OfflineBanner`: the banner is an informational overlay that
|
|
843
|
+
* coexists with normal content everywhere in the shell. This is a hard block
|
|
844
|
+
* for surfaces where operating against stale data is actively wrong — e.g. a
|
|
845
|
+
* Console user list or an Account billing page reflects a point-in-time
|
|
846
|
+
* snapshot the moment it's server-rendered, and any cached copy replayed
|
|
847
|
+
* later has no way to signal that it may already be stale. `OfflineGate`
|
|
848
|
+
* doesn't try to tell staleness apart from freshness; it removes the
|
|
849
|
+
* question by not rendering the content at all while offline.
|
|
850
|
+
*
|
|
851
|
+
* `children` is rendered unconditionally while online, so this adds no cost
|
|
852
|
+
* to the common case — it only decides what to show, never how the wrapped
|
|
853
|
+
* content itself is fetched or cached.
|
|
854
|
+
*/
|
|
855
|
+
declare function OfflineGate({ children, surfaceName, className }: OfflineGateProps): react.JSX.Element;
|
|
856
|
+
|
|
857
|
+
interface DeviceOnlyGateProps {
|
|
858
|
+
children: ReactNode;
|
|
859
|
+
/** Defaults to a generic "this app" phrasing — pass the plugin's own name. */
|
|
860
|
+
surfaceName?: string;
|
|
861
|
+
/**
|
|
862
|
+
* The caller's own `isDeviceOnlyTierAvailable()` result (from
|
|
863
|
+
* `@sovereignfs/sdk/device-client`) — taken as a plain boolean rather than
|
|
864
|
+
* computed inside this component, so `@sovereignfs/ui` never depends on
|
|
865
|
+
* `@sovereignfs/sdk` (this package must stay usable standalone, outside the
|
|
866
|
+
* plugin runtime — RFC 0073).
|
|
867
|
+
*/
|
|
868
|
+
available: boolean;
|
|
869
|
+
className?: string;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* DeviceOnlyGate — blocks a `device-only`-tier plugin's content from
|
|
873
|
+
* rendering on a surface that cannot provide the durable, encrypted,
|
|
874
|
+
* device-auth-gated store that tier requires, showing an explanatory empty
|
|
875
|
+
* state instead of a broken screen (research 0012, epic tasks 2.33 + 3.36).
|
|
876
|
+
*
|
|
877
|
+
* A `device-only` plugin wraps its own root content in this the same way
|
|
878
|
+
* Console and Account wrap theirs in `OfflineGate` — an opt-in pattern each
|
|
879
|
+
* surface applies to itself, not a platform-level route gate. The launcher
|
|
880
|
+
* tile's own "Phone only" badge (`useOfflineTileState`) is advisory UI on top
|
|
881
|
+
* of this; this component is the actual gate, since a user can always reach a
|
|
882
|
+
* route directly (a bookmark, a deep link) without passing through the
|
|
883
|
+
* launcher tile at all.
|
|
884
|
+
*
|
|
885
|
+
* **Not a security boundary.** Availability here is a capability signal, not
|
|
886
|
+
* an authorization check — the real protection for `device-only` data is
|
|
887
|
+
* that it is encrypted and the key requires device auth to release (epic task
|
|
888
|
+
* 1.22), so it stays inaccessible regardless of whether this gate is present,
|
|
889
|
+
* bypassed, or simply never reached (e.g. a hand-crafted request against the
|
|
890
|
+
* plugin's own API routes).
|
|
891
|
+
*/
|
|
892
|
+
declare function DeviceOnlyGate({ children, surfaceName, available, className, }: DeviceOnlyGateProps): react.JSX.Element;
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* `'checking'` covers the moment before the caller's own async status check
|
|
896
|
+
* resolves — same transitional-state pattern `EncryptionSection`'s local
|
|
897
|
+
* `LocalState` uses, so this gate never flashes an empty state before the
|
|
898
|
+
* real answer is known. `'no-device-auth'` is the hard-block case (RFC 0093
|
|
899
|
+
* §5, epic task 1.22): the environment supports the tier, but this device
|
|
900
|
+
* has no passcode, fingerprint, or face unlock configured, so a key cannot
|
|
901
|
+
* be created here at all — distinct from `'not-set-up'`, where it can.
|
|
902
|
+
*/
|
|
903
|
+
type DeviceStorageKeyGateStatus = 'checking' | 'unsupported' | 'no-device-auth' | 'not-set-up' | 'set-up';
|
|
904
|
+
interface DeviceStorageKeyGateProps {
|
|
905
|
+
children: ReactNode;
|
|
906
|
+
/** Defaults to a generic "This app" phrasing — pass the plugin's own name. */
|
|
907
|
+
surfaceName?: string;
|
|
908
|
+
/**
|
|
909
|
+
* The caller's own `getDeviceStorageKeyStatus()` result (from
|
|
910
|
+
* `@sovereignfs/sdk/device-only-storage`) — taken as a plain status rather
|
|
911
|
+
* than computed inside this component, so `@sovereignfs/ui` never depends
|
|
912
|
+
* on `@sovereignfs/sdk` (same boundary `DeviceOnlyGate`'s `available` prop
|
|
913
|
+
* draws — this package must stay usable standalone, outside the plugin
|
|
914
|
+
* runtime — RFC 0073).
|
|
915
|
+
*/
|
|
916
|
+
status: DeviceStorageKeyGateStatus;
|
|
917
|
+
/**
|
|
918
|
+
* Rendered under the "not set up" message — typically a link to Account →
|
|
919
|
+
* Security. Not built in here: this package depends on no router, so the
|
|
920
|
+
* caller supplies its own navigable element (a plain `<a>`, a framework
|
|
921
|
+
* `<Link>`, whatever the host app uses).
|
|
922
|
+
*/
|
|
923
|
+
setupAction?: ReactNode;
|
|
924
|
+
className?: string;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* DeviceStorageKeyGate — blocks a `device-only`-tier plugin's content until
|
|
928
|
+
* the user has set up their Device Storage Key (RFC 0093 §2, epic task
|
|
929
|
+
* 1.22). Distinct from `DeviceOnlyGate`: that one checks whether the *tier
|
|
930
|
+
* itself* is available on this surface (native shell, secure-storage bridge
|
|
931
|
+
* present); this one checks whether the user has actually completed the
|
|
932
|
+
* one-time, per-device enrollment that tier depends on. A `device-only`
|
|
933
|
+
* plugin typically wants both — `DeviceOnlyGate` outermost (no point
|
|
934
|
+
* checking enrollment on a surface that cannot offer the tier at all), this
|
|
935
|
+
* gate inside it.
|
|
936
|
+
*
|
|
937
|
+
* Enrollment is centralized and decoupled from any single plugin (RFC 0093
|
|
938
|
+
* §2): a plugin that finds no key set up does not run its own enrollment
|
|
939
|
+
* ceremony inline. It stops here and points the user at Account → Security —
|
|
940
|
+
* the one place enrollment happens for every `device-only` plugin on this
|
|
941
|
+
* device — via `setupAction`, the same opt-in, self-applied pattern
|
|
942
|
+
* `OfflineGate`/`DeviceOnlyGate` already use for their own surfaces.
|
|
943
|
+
*/
|
|
944
|
+
declare function DeviceStorageKeyGate({ children, surfaceName, status, setupAction, className, }: DeviceStorageKeyGateProps): react.JSX.Element | null;
|
|
945
|
+
|
|
648
946
|
interface SpinnerProps {
|
|
649
947
|
size?: 'sm' | 'md' | 'lg';
|
|
650
948
|
label?: string;
|
|
@@ -665,12 +963,25 @@ interface NavTabItem {
|
|
|
665
963
|
href: string;
|
|
666
964
|
active?: boolean;
|
|
667
965
|
}
|
|
966
|
+
/** Props to spread onto a consumer-supplied link element — keeps NavTabs' own
|
|
967
|
+
* class names and a11y attributes in sync with whatever the consumer renders. */
|
|
968
|
+
interface NavTabLinkProps {
|
|
969
|
+
href: string;
|
|
970
|
+
className: string;
|
|
971
|
+
'aria-current': 'page' | undefined;
|
|
972
|
+
children: ReactNode;
|
|
973
|
+
}
|
|
668
974
|
interface NavTabsProps {
|
|
669
975
|
items: NavTabItem[];
|
|
670
976
|
className?: string;
|
|
671
977
|
'aria-label'?: string;
|
|
978
|
+
/** Render a tab as something other than a plain `<a href>` — e.g. Next's
|
|
979
|
+
* `<Link replace>` inside an overlay-shell plugin, where a full page
|
|
980
|
+
* navigation would break the dialog's history contract. Receives the item
|
|
981
|
+
* plus the class name/aria attributes NavTabs would otherwise apply. */
|
|
982
|
+
renderLink?: (item: NavTabItem, linkProps: NavTabLinkProps) => ReactNode;
|
|
672
983
|
}
|
|
673
|
-
declare function NavTabs({ items, className, 'aria-label': ariaLabel }: NavTabsProps): react.JSX.Element;
|
|
984
|
+
declare function NavTabs({ items, className, 'aria-label': ariaLabel, renderLink }: NavTabsProps): react.JSX.Element;
|
|
674
985
|
|
|
675
986
|
interface TooltipProps {
|
|
676
987
|
content: string;
|
|
@@ -689,6 +1000,595 @@ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'typ
|
|
|
689
1000
|
}
|
|
690
1001
|
declare function Checkbox({ checked, onChange, label, strikeThrough, disabled, className, id, ...rest }: CheckboxProps): react.JSX.Element;
|
|
691
1002
|
|
|
1003
|
+
interface RadioGroupItem {
|
|
1004
|
+
label: string;
|
|
1005
|
+
value: string;
|
|
1006
|
+
disabled?: boolean;
|
|
1007
|
+
}
|
|
1008
|
+
interface RadioGroupProps {
|
|
1009
|
+
items: RadioGroupItem[];
|
|
1010
|
+
value: string;
|
|
1011
|
+
onChange: (value: string) => void;
|
|
1012
|
+
/** Groups the underlying native radios. Auto-generated when omitted —
|
|
1013
|
+
* only pass this to participate in a native, non-React form submission. */
|
|
1014
|
+
name?: string;
|
|
1015
|
+
/** Disables every item. Use `items[].disabled` to disable one. */
|
|
1016
|
+
disabled?: boolean;
|
|
1017
|
+
id?: string;
|
|
1018
|
+
className?: string;
|
|
1019
|
+
'aria-label'?: string;
|
|
1020
|
+
'aria-labelledby'?: string;
|
|
1021
|
+
'aria-describedby'?: string;
|
|
1022
|
+
'aria-invalid'?: boolean;
|
|
1023
|
+
required?: boolean;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* RadioGroup — single-select list of options.
|
|
1027
|
+
*
|
|
1028
|
+
* Renders real `<input type="radio">` elements sharing one `name`, visually
|
|
1029
|
+
* hidden behind a custom circle (same technique as `Checkbox`). Keyboard
|
|
1030
|
+
* arrow-key navigation between options is native browser behavior for
|
|
1031
|
+
* grouped radio inputs — nothing to hand-roll here.
|
|
1032
|
+
*/
|
|
1033
|
+
declare function RadioGroup({ items, value, onChange, name, disabled, id, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, required, }: RadioGroupProps): react.JSX.Element;
|
|
1034
|
+
|
|
1035
|
+
interface SliderProps {
|
|
1036
|
+
value: number;
|
|
1037
|
+
onChange: (value: number) => void;
|
|
1038
|
+
min: number;
|
|
1039
|
+
max: number;
|
|
1040
|
+
step?: number;
|
|
1041
|
+
/** Renders a visible label above the track. Omit and pass `aria-label`
|
|
1042
|
+
* instead for a standalone, unlabeled slider. */
|
|
1043
|
+
label?: string;
|
|
1044
|
+
disabled?: boolean;
|
|
1045
|
+
id?: string;
|
|
1046
|
+
className?: string;
|
|
1047
|
+
'aria-label'?: string;
|
|
1048
|
+
'aria-describedby'?: string;
|
|
1049
|
+
'aria-invalid'?: boolean;
|
|
1050
|
+
required?: boolean;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Slider — single-thumb range input.
|
|
1054
|
+
*
|
|
1055
|
+
* A native `<input type="range">` under custom track/thumb styling — arrow
|
|
1056
|
+
* key (adjust by `step`), Home/End (jump to min/max), and touch-drag
|
|
1057
|
+
* support all come from the browser, nothing hand-rolled. Dual-thumb/range
|
|
1058
|
+
* selection is out of scope until a consumer needs it.
|
|
1059
|
+
*/
|
|
1060
|
+
declare function Slider({ value, onChange, min, max, step, label, disabled, id, className, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, required, }: SliderProps): react.JSX.Element;
|
|
1061
|
+
|
|
1062
|
+
interface ProgressProps {
|
|
1063
|
+
/** 0–100. Values outside that range are clamped. */
|
|
1064
|
+
value: number;
|
|
1065
|
+
/** Accessible name — Progress has no visible text of its own. */
|
|
1066
|
+
label?: string;
|
|
1067
|
+
id?: string;
|
|
1068
|
+
className?: string;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Progress — determinate bar.
|
|
1072
|
+
*
|
|
1073
|
+
* `role="progressbar"` with `aria-valuenow`/`aria-valuemin`/`aria-valuemax`
|
|
1074
|
+
* so screen readers announce the current value. Indeterminate state is out
|
|
1075
|
+
* of scope until a consumer needs it.
|
|
1076
|
+
*/
|
|
1077
|
+
declare function Progress({ value, label, id, className }: ProgressProps): react.JSX.Element;
|
|
1078
|
+
|
|
1079
|
+
interface TableProps {
|
|
1080
|
+
children: ReactNode;
|
|
1081
|
+
className?: string;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Table — thin, styled wrappers around the native table elements.
|
|
1085
|
+
*
|
|
1086
|
+
* Not a data grid: no sort, filter, or virtualization. `Table` wraps its
|
|
1087
|
+
* content in a horizontally-scrolling container (masked-edge fade, hidden
|
|
1088
|
+
* scrollbar — same technique as `NavTabs`) so a table wider than its
|
|
1089
|
+
* container scrolls instead of overflowing the page, at any viewport size.
|
|
1090
|
+
*/
|
|
1091
|
+
declare function Table({ children, className }: TableProps): react.JSX.Element;
|
|
1092
|
+
declare function TableHead({ children }: {
|
|
1093
|
+
children: ReactNode;
|
|
1094
|
+
}): react.JSX.Element;
|
|
1095
|
+
declare function TableBody({ children }: {
|
|
1096
|
+
children: ReactNode;
|
|
1097
|
+
}): react.JSX.Element;
|
|
1098
|
+
declare function TableRow({ children }: {
|
|
1099
|
+
children: ReactNode;
|
|
1100
|
+
}): react.JSX.Element;
|
|
1101
|
+
declare function TableHeaderCell({ children, ...rest }: ThHTMLAttributes<HTMLTableCellElement>): react.JSX.Element;
|
|
1102
|
+
declare function TableCell({ children, ...rest }: TdHTMLAttributes<HTMLTableCellElement>): react.JSX.Element;
|
|
1103
|
+
|
|
1104
|
+
type AlertVariant = 'info' | 'success' | 'warning' | 'error' | 'neutral';
|
|
1105
|
+
interface AlertProps {
|
|
1106
|
+
variant: AlertVariant;
|
|
1107
|
+
heading?: string;
|
|
1108
|
+
/** Leading icon next to the heading. Defaults to a per-variant icon (see
|
|
1109
|
+
* `DEFAULT_ICON`) — pass an `IconName` to override it, or `false` to
|
|
1110
|
+
* suppress the icon entirely. Purely decorative: the variant's colour and
|
|
1111
|
+
* `heading`/`children` text already carry the meaning. */
|
|
1112
|
+
icon?: IconName | false;
|
|
1113
|
+
children: ReactNode;
|
|
1114
|
+
id?: string;
|
|
1115
|
+
className?: string;
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Alert — inline, non-dismissible banner.
|
|
1119
|
+
*
|
|
1120
|
+
* Distinct from `Toast` (transient, auto-dismissing) and `SystemBanner`
|
|
1121
|
+
* (instance-wide): Alert is for in-context messaging — form-level errors,
|
|
1122
|
+
* or explaining an empty/blocked state.
|
|
1123
|
+
*
|
|
1124
|
+
* `role="alert"` for `error` (interrupts screen readers immediately, since
|
|
1125
|
+
* an error demands attention); `role="status"` for the other variants
|
|
1126
|
+
* (announced politely, without interrupting).
|
|
1127
|
+
*/
|
|
1128
|
+
declare function Alert({ variant, heading, icon, children, id, className }: AlertProps): react.JSX.Element;
|
|
1129
|
+
|
|
1130
|
+
interface BreadcrumbItem {
|
|
1131
|
+
label: string;
|
|
1132
|
+
/** Omit for the current page — rendered as plain text with aria-current. */
|
|
1133
|
+
href?: string;
|
|
1134
|
+
}
|
|
1135
|
+
interface BreadcrumbProps {
|
|
1136
|
+
items: BreadcrumbItem[];
|
|
1137
|
+
/** Renders a linked item. Defaults to a plain `<a href>`, which causes a
|
|
1138
|
+
* full page reload — pass Next's `<Link>` (or equivalent) to keep
|
|
1139
|
+
* navigation client-side, which matters inside overlay-shell plugins
|
|
1140
|
+
* where a full reload breaks the router.back()-driven dismiss flow. */
|
|
1141
|
+
renderLink?: (item: BreadcrumbItem, children: ReactNode) => ReactNode;
|
|
1142
|
+
className?: string;
|
|
1143
|
+
'aria-label'?: string;
|
|
1144
|
+
}
|
|
1145
|
+
declare function Breadcrumb({ items, renderLink, className, 'aria-label': ariaLabel, }: BreadcrumbProps): react.JSX.Element;
|
|
1146
|
+
|
|
1147
|
+
interface PaginationProps {
|
|
1148
|
+
/** 1-indexed current page. */
|
|
1149
|
+
page: number;
|
|
1150
|
+
totalPages: number;
|
|
1151
|
+
onChange: (page: number) => void;
|
|
1152
|
+
className?: string;
|
|
1153
|
+
'aria-label'?: string;
|
|
1154
|
+
}
|
|
1155
|
+
declare function Pagination({ page, totalPages, onChange, className, 'aria-label': ariaLabel, }: PaginationProps): react.JSX.Element;
|
|
1156
|
+
|
|
1157
|
+
interface KbdProps {
|
|
1158
|
+
children: ReactNode;
|
|
1159
|
+
className?: string;
|
|
1160
|
+
}
|
|
1161
|
+
/** Kbd — inline keyboard-key styling. Renders a real `<kbd>` element. */
|
|
1162
|
+
declare function Kbd({ children, className }: KbdProps): react.JSX.Element;
|
|
1163
|
+
|
|
1164
|
+
interface CollapsibleProps {
|
|
1165
|
+
open: boolean;
|
|
1166
|
+
onOpenChange: (open: boolean) => void;
|
|
1167
|
+
trigger: ReactNode;
|
|
1168
|
+
children: ReactNode;
|
|
1169
|
+
id?: string;
|
|
1170
|
+
className?: string;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Collapsible — single expand/collapse primitive. Independently useful
|
|
1174
|
+
* (e.g. a "show more" toggle), and composed internally by `Accordion`.
|
|
1175
|
+
*
|
|
1176
|
+
* The expand/collapse animation uses `grid-template-rows: 0fr` → `1fr`
|
|
1177
|
+
* rather than animating `height`, which can't transition to/from `auto`
|
|
1178
|
+
* in CSS without JS-measuring pixel heights. Collapsed content stays in
|
|
1179
|
+
* the DOM (so no layout jump on re-open) but is marked `inert` — removed
|
|
1180
|
+
* from the tab order and the accessibility tree — so it isn't reachable
|
|
1181
|
+
* while hidden.
|
|
1182
|
+
*/
|
|
1183
|
+
declare function Collapsible({ open, onOpenChange, trigger, children, id, className, }: CollapsibleProps): react.JSX.Element;
|
|
1184
|
+
|
|
1185
|
+
interface AccordionItem {
|
|
1186
|
+
id: string;
|
|
1187
|
+
trigger: ReactNode;
|
|
1188
|
+
content: ReactNode;
|
|
1189
|
+
}
|
|
1190
|
+
interface AccordionProps {
|
|
1191
|
+
items: AccordionItem[];
|
|
1192
|
+
/** `single` closes other sections when one opens. `multiple` allows any
|
|
1193
|
+
* number of sections open at once. */
|
|
1194
|
+
type: 'single' | 'multiple';
|
|
1195
|
+
openIds: string[];
|
|
1196
|
+
onOpenIdsChange: (openIds: string[]) => void;
|
|
1197
|
+
className?: string;
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Accordion — one or more `Collapsible` sections.
|
|
1201
|
+
*
|
|
1202
|
+
* Keyboard support (Enter/Space toggles the focused trigger) comes from
|
|
1203
|
+
* `Collapsible`'s real `<button>` — no arrow-key roving-tabindex handling
|
|
1204
|
+
* here, since accordion sections aren't a roving-tabindex widget per the
|
|
1205
|
+
* WAI-ARIA accordion pattern (each trigger is independently Tab-focusable).
|
|
1206
|
+
*/
|
|
1207
|
+
declare function Accordion({ items, type, openIds, onOpenIdsChange, className }: AccordionProps): react.JSX.Element;
|
|
1208
|
+
|
|
1209
|
+
interface AspectRatioProps {
|
|
1210
|
+
/** Width / height, e.g. 16 / 9 or 1. */
|
|
1211
|
+
ratio: number;
|
|
1212
|
+
children: ReactNode;
|
|
1213
|
+
className?: string;
|
|
1214
|
+
}
|
|
1215
|
+
/** AspectRatio — constrains content (image, video, embed) to a fixed ratio. */
|
|
1216
|
+
declare function AspectRatio({ ratio, children, className }: AspectRatioProps): react.JSX.Element;
|
|
1217
|
+
|
|
1218
|
+
interface ButtonGroupProps {
|
|
1219
|
+
/** Adjacent Button elements. */
|
|
1220
|
+
children: ReactNode;
|
|
1221
|
+
className?: string;
|
|
1222
|
+
'aria-label'?: string;
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* ButtonGroup — visually joins adjacent `Button`s into one control (shared
|
|
1226
|
+
* border, connected corners). Targets its children's rendered `<button>`/
|
|
1227
|
+
* `<a>` elements directly rather than requiring a special child component,
|
|
1228
|
+
* so it works with plain `Button`.
|
|
1229
|
+
*/
|
|
1230
|
+
declare function ButtonGroup({ children, className, 'aria-label': ariaLabel }: ButtonGroupProps): react.JSX.Element;
|
|
1231
|
+
|
|
1232
|
+
interface ItemProps {
|
|
1233
|
+
leading?: ReactNode;
|
|
1234
|
+
title: ReactNode;
|
|
1235
|
+
description?: ReactNode;
|
|
1236
|
+
trailing?: ReactNode;
|
|
1237
|
+
/** Renders as a button when given; a plain row otherwise. */
|
|
1238
|
+
onClick?: () => void;
|
|
1239
|
+
disabled?: boolean;
|
|
1240
|
+
className?: string;
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Item — generic row primitive: leading slot, title + optional description,
|
|
1244
|
+
* trailing slot. The building block for settings rows, list rows, and
|
|
1245
|
+
* menu-adjacent rows that don't fit `Menu`'s own item shape.
|
|
1246
|
+
*/
|
|
1247
|
+
declare function Item({ leading, title, description, trailing, onClick, disabled, className, }: ItemProps): react.JSX.Element;
|
|
1248
|
+
|
|
1249
|
+
interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
|
|
1250
|
+
disabled?: boolean;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Label — standalone accessible form label, independent of `FormField`.
|
|
1254
|
+
* Use `FormField` when a control needs hint/error text too; use `Label`
|
|
1255
|
+
* directly for a bare label (e.g. next to a `Checkbox` group heading).
|
|
1256
|
+
*/
|
|
1257
|
+
declare function Label({ disabled, className, children, ...rest }: LabelProps): react.JSX.Element;
|
|
1258
|
+
|
|
1259
|
+
interface ScrollAreaProps {
|
|
1260
|
+
children: ReactNode;
|
|
1261
|
+
/** Constrains the scrollable direction; content taller/wider than this scrolls. */
|
|
1262
|
+
maxHeight?: string | number;
|
|
1263
|
+
maxWidth?: string | number;
|
|
1264
|
+
className?: string;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* ScrollArea — a styled scrollable container (thin, token-colored scrollbar
|
|
1268
|
+
* instead of the OS default). Native overflow scrolling underneath — no
|
|
1269
|
+
* virtualization, no custom scroll physics.
|
|
1270
|
+
*/
|
|
1271
|
+
declare function ScrollArea({ children, maxHeight, maxWidth, className }: ScrollAreaProps): react.JSX.Element;
|
|
1272
|
+
|
|
1273
|
+
type TypographyVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'body' | 'caption' | 'label';
|
|
1274
|
+
interface TypographyProps {
|
|
1275
|
+
variant: TypographyVariant;
|
|
1276
|
+
children: ReactNode;
|
|
1277
|
+
/** Override the rendered tag while keeping the variant's visual style —
|
|
1278
|
+
* e.g. `variant="h1" as="div"` to avoid a second `<h1>` on a page that
|
|
1279
|
+
* already has one. */
|
|
1280
|
+
as?: ElementType;
|
|
1281
|
+
className?: string;
|
|
1282
|
+
}
|
|
1283
|
+
/** Typography — text bound to the design system's font-size/weight scale. */
|
|
1284
|
+
declare function Typography({ variant, children, as, className }: TypographyProps): react.JSX.Element;
|
|
1285
|
+
|
|
1286
|
+
interface MarkerProps {
|
|
1287
|
+
/** The number shown in the marker, e.g. 1, 2, 3. */
|
|
1288
|
+
index: number;
|
|
1289
|
+
/** Accessible label describing the citation, e.g. "Source: Notes". */
|
|
1290
|
+
label: string;
|
|
1291
|
+
/** Reveals source details (e.g. opens a source trace panel) when given. */
|
|
1292
|
+
onClick?: () => void;
|
|
1293
|
+
className?: string;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Marker — inline citation/reference marker, for attributing part of an
|
|
1297
|
+
* assistant's answer to a source (matches RFC 0040's "source trace" —
|
|
1298
|
+
* Harness shows which plugin/data source informed an answer).
|
|
1299
|
+
*/
|
|
1300
|
+
declare function Marker({ index, label, onClick, className }: MarkerProps): react.JSX.Element;
|
|
1301
|
+
|
|
1302
|
+
type MessageSender = 'user' | 'assistant' | 'tool';
|
|
1303
|
+
interface MessageProps {
|
|
1304
|
+
/** Matches the three roles Sovereign Harness stores per RFC 0040:
|
|
1305
|
+
* user, assistant, and tool messages. Named `sender`, not `role`, to
|
|
1306
|
+
* avoid colliding with the ARIA `role` attribute in JSX tooling. */
|
|
1307
|
+
sender: MessageSender;
|
|
1308
|
+
children?: ReactNode;
|
|
1309
|
+
/** Shows a "thinking" indicator instead of children — before the first
|
|
1310
|
+
* token of a streaming response arrives. Once content starts streaming
|
|
1311
|
+
* in, pass it as children and stop passing `pending`. */
|
|
1312
|
+
pending?: boolean;
|
|
1313
|
+
/** Rendered in a small row below the bubble — e.g. "Forget this" (RFC
|
|
1314
|
+
* 0040's per-message memory action), "Copy", "Regenerate". */
|
|
1315
|
+
actions?: ReactNode;
|
|
1316
|
+
className?: string;
|
|
1317
|
+
}
|
|
1318
|
+
/** Message — a single chat turn. Content is caller-controlled `ReactNode`
|
|
1319
|
+
* (markdown rendering, if any, is the consumer's choice, not baked in here). */
|
|
1320
|
+
declare function Message({ sender, children, pending, actions, className }: MessageProps): react.JSX.Element;
|
|
1321
|
+
|
|
1322
|
+
interface MessageScrollerProps {
|
|
1323
|
+
children: ReactNode;
|
|
1324
|
+
className?: string;
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* MessageScroller — auto-scrolling chat container.
|
|
1328
|
+
*
|
|
1329
|
+
* Scrolls to the newest message automatically, but only while the user is
|
|
1330
|
+
* already near the bottom. If they've scrolled up to read history, new
|
|
1331
|
+
* content does not yank them back down — a "New messages" button appears
|
|
1332
|
+
* instead, matching the pattern every chat product (Slack, Discord,
|
|
1333
|
+
* ChatGPT) already uses.
|
|
1334
|
+
*/
|
|
1335
|
+
declare function MessageScroller({ children, className }: MessageScrollerProps): react.JSX.Element;
|
|
1336
|
+
|
|
1337
|
+
interface HoverCardProps {
|
|
1338
|
+
/** The element that opens the card — cloned with hover/focus handlers
|
|
1339
|
+
* (desktop) or a tap handler (touch). */
|
|
1340
|
+
trigger: ReactElement;
|
|
1341
|
+
children: ReactNode;
|
|
1342
|
+
align?: 'left' | 'right';
|
|
1343
|
+
width?: number | 'trigger';
|
|
1344
|
+
'aria-label': string;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* HoverCard — hover-triggered popover on desktop, tap-to-toggle on touch.
|
|
1348
|
+
*
|
|
1349
|
+
* Built on `Popover` rather than reimplementing positioning/collision
|
|
1350
|
+
* detection: this only adds the hover-intent open/close timing (avoids
|
|
1351
|
+
* flicker on an accidental mouse pass-over, and gives the pointer time to
|
|
1352
|
+
* travel from the trigger to the card itself across the gap between them)
|
|
1353
|
+
* and the touch fallback. Also opens on keyboard focus and closes on blur —
|
|
1354
|
+
* a hover-only trigger is a WCAG failure, since keyboard users can't hover.
|
|
1355
|
+
*/
|
|
1356
|
+
declare function HoverCard({ trigger, children, align, width, 'aria-label': ariaLabel, }: HoverCardProps): react.JSX.Element;
|
|
1357
|
+
|
|
1358
|
+
interface ContextMenuProps {
|
|
1359
|
+
/** The element that gets right-click (desktop) / long-press (touch)
|
|
1360
|
+
* behavior — cloned with the trigger handlers, rendered as-is otherwise.
|
|
1361
|
+
* Typed with `<any>` props (not a bare `ReactElement`, whose implicit
|
|
1362
|
+
* `unknown` props type makes `cloneElement` reject *both* branches of
|
|
1363
|
+
* the long-press/onContextMenu union below — an interface value like
|
|
1364
|
+
* `LongPressHandlers` isn't assignable to an indexed props type without
|
|
1365
|
+
* its own index signature) — the standard, narrowly-scoped escape hatch
|
|
1366
|
+
* for "clone an arbitrary child with props not known ahead of time". */
|
|
1367
|
+
children: ReactElement<any>;
|
|
1368
|
+
items: MenuEntry[];
|
|
1369
|
+
'aria-label': string;
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* ContextMenu — right-click menu on desktop, long-press on touch.
|
|
1373
|
+
*
|
|
1374
|
+
* Desktop positioning reuses `Popover`'s existing collision-detection logic
|
|
1375
|
+
* rather than reimplementing it: the panel is wrapped in a zero-size,
|
|
1376
|
+
* `position: fixed` anchor placed at the click coordinates, so `Popover`
|
|
1377
|
+
* measures and flips against *that* point exactly as it would a normal
|
|
1378
|
+
* trigger element.
|
|
1379
|
+
*
|
|
1380
|
+
* Touch uses `useLongPress` (which already handles the OS's own long-press
|
|
1381
|
+
* callout/context-menu suppression) and opens the same items in a `Drawer`
|
|
1382
|
+
* bottom sheet — the same adaptive-surface split `Menu` already uses,
|
|
1383
|
+
* because a floating panel positioned at a touch point isn't a pattern
|
|
1384
|
+
* touch interfaces have in the first place.
|
|
1385
|
+
*/
|
|
1386
|
+
declare function ContextMenu({ children, items, 'aria-label': ariaLabel }: ContextMenuProps): react.JSX.Element;
|
|
1387
|
+
|
|
1388
|
+
interface NavigationMenuItem {
|
|
1389
|
+
label: string;
|
|
1390
|
+
/** A plain link with no flyout — mutually exclusive with `content`. */
|
|
1391
|
+
href?: string;
|
|
1392
|
+
/** Flyout panel content shown when this item is triggered. Pass a
|
|
1393
|
+
* function instead of a plain node when the content needs to close its
|
|
1394
|
+
* own flyout after an action (e.g. `Menubar`'s menu items) — it receives
|
|
1395
|
+
* a `close` callback. */
|
|
1396
|
+
content?: ReactNode | ((close: () => void) => ReactNode);
|
|
1397
|
+
}
|
|
1398
|
+
interface NavigationMenuProps {
|
|
1399
|
+
items: NavigationMenuItem[];
|
|
1400
|
+
/** Renders a plain-link item's anchor. Defaults to `<a href>`, which
|
|
1401
|
+
* causes a full page reload — pass Next's `<Link>` (or equivalent) to
|
|
1402
|
+
* keep navigation client-side. */
|
|
1403
|
+
renderLink?: (href: string, children: ReactNode) => ReactNode;
|
|
1404
|
+
'aria-label'?: string;
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* NavigationMenu — top-level nav bar where some items open a flyout panel.
|
|
1408
|
+
*
|
|
1409
|
+
* Desktop-oriented by design — a hover-triggered flyout bar has no mobile
|
|
1410
|
+
* equivalent (Research 0004); it renders as-is on touch rather than
|
|
1411
|
+
* attempting a redesign no consumer has asked for yet.
|
|
1412
|
+
*
|
|
1413
|
+
* Each flyout is its own `Popover` instance rather than one shared
|
|
1414
|
+
* positioned panel, reusing its collision detection per item. Once one
|
|
1415
|
+
* flyout is open, moving the pointer to a sibling trigger switches directly
|
|
1416
|
+
* to that one — standard menu-bar behavior — and ArrowLeft/ArrowRight move
|
|
1417
|
+
* focus between top-level triggers (a roving-tabindex bar, per the
|
|
1418
|
+
* WAI-ARIA menu pattern), Escape closes.
|
|
1419
|
+
*/
|
|
1420
|
+
declare function NavigationMenu({ items, renderLink, 'aria-label': ariaLabel, }: NavigationMenuProps): react.JSX.Element;
|
|
1421
|
+
|
|
1422
|
+
interface MenubarMenu {
|
|
1423
|
+
label: string;
|
|
1424
|
+
items: MenuEntry[];
|
|
1425
|
+
}
|
|
1426
|
+
interface MenubarProps {
|
|
1427
|
+
menus: MenubarMenu[];
|
|
1428
|
+
'aria-label'?: string;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Menubar — desktop app-style menu bar (File / Edit / View...). A thin
|
|
1432
|
+
* composition of `NavigationMenu` (top-level triggers, per-item flyout
|
|
1433
|
+
* positioning, hover-switches-when-one-is-open, arrow-key navigation) with
|
|
1434
|
+
* `MenuEntries` (the same item list markup `Menu`'s dropdowns use) as each
|
|
1435
|
+
* item's flyout content — no positioning or list-rendering logic
|
|
1436
|
+
* duplicated from either.
|
|
1437
|
+
*/
|
|
1438
|
+
declare function Menubar({ menus, 'aria-label': ariaLabel }: MenubarProps): react.JSX.Element;
|
|
1439
|
+
|
|
1440
|
+
interface CommandItem {
|
|
1441
|
+
id: string;
|
|
1442
|
+
label: string;
|
|
1443
|
+
onSelect: () => void;
|
|
1444
|
+
icon?: IconName;
|
|
1445
|
+
/** Section header this item is grouped under. */
|
|
1446
|
+
group?: string;
|
|
1447
|
+
/** Extra searchable text beyond the label (keywords, description). */
|
|
1448
|
+
keywords?: string;
|
|
1449
|
+
}
|
|
1450
|
+
interface CommandProps {
|
|
1451
|
+
open: boolean;
|
|
1452
|
+
onClose: () => void;
|
|
1453
|
+
items: CommandItem[];
|
|
1454
|
+
placeholder?: string;
|
|
1455
|
+
'aria-label'?: string;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Command — a ⌘K-style palette. Opened via `Dialog`, which supplies the
|
|
1459
|
+
* modal shell (scrim, panel, mobile full-screen sheet, focus handling) —
|
|
1460
|
+
* this only adds the search input, filtering, and arrow-key/Enter
|
|
1461
|
+
* selection on top of it. Controlled (`open`/`onClose`): the consumer wires
|
|
1462
|
+
* up their own global shortcut listener to flip `open` — a DS primitive
|
|
1463
|
+
* shouldn't own a document-level keydown listener itself.
|
|
1464
|
+
*/
|
|
1465
|
+
declare function Command({ open, onClose, items, placeholder, 'aria-label': ariaLabel, }: CommandProps): react.JSX.Element;
|
|
1466
|
+
|
|
1467
|
+
type ResizableDirection = 'horizontal' | 'vertical';
|
|
1468
|
+
interface ResizablePanelProps {
|
|
1469
|
+
/** Initial size as a percentage of the group's main axis. Defaults to an
|
|
1470
|
+
* equal split across all panels in the group. */
|
|
1471
|
+
defaultSize?: number;
|
|
1472
|
+
/** Minimum size as a percentage. Defaults to 10. */
|
|
1473
|
+
minSize?: number;
|
|
1474
|
+
/** Maximum size as a percentage. Defaults to 90. */
|
|
1475
|
+
maxSize?: number;
|
|
1476
|
+
children?: ReactNode;
|
|
1477
|
+
className?: string;
|
|
1478
|
+
/** Injected by ResizablePanelGroup — not part of the public API. */
|
|
1479
|
+
style?: CSSProperties;
|
|
1480
|
+
}
|
|
1481
|
+
/** One pane of a ResizablePanelGroup. Sizing is computed and injected by the
|
|
1482
|
+
* group (via defaultSize/minSize/maxSize read off this element's props) —
|
|
1483
|
+
* this component only renders the scrollable content box. */
|
|
1484
|
+
declare function ResizablePanel({ children, className, style }: ResizablePanelProps): react.JSX.Element;
|
|
1485
|
+
interface ResizableHandleProps {
|
|
1486
|
+
'aria-label'?: string;
|
|
1487
|
+
/** Injected by ResizablePanelGroup — not part of the public API. */
|
|
1488
|
+
direction?: ResizableDirection;
|
|
1489
|
+
onPointerDown?: (e: PointerEvent<HTMLDivElement>) => void;
|
|
1490
|
+
onKeyDown?: (e: KeyboardEvent<HTMLDivElement>) => void;
|
|
1491
|
+
'aria-valuenow'?: number;
|
|
1492
|
+
'aria-valuemin'?: number;
|
|
1493
|
+
'aria-valuemax'?: number;
|
|
1494
|
+
}
|
|
1495
|
+
/** The draggable divider between two ResizablePanels. Placed directly
|
|
1496
|
+
* between the two ResizablePanel elements it resizes — the group pairs each
|
|
1497
|
+
* handle with its preceding and following panel by position.
|
|
1498
|
+
*
|
|
1499
|
+
* A `div` with `role="separator"` and `tabIndex=0` (the WAI-ARIA "window
|
|
1500
|
+
* splitter" pattern), not a `<button>` — `separator` is a non-interactive
|
|
1501
|
+
* role, and a native button's implicit role can't be overridden to it. */
|
|
1502
|
+
declare function ResizableHandle({ 'aria-label': ariaLabel, direction, onPointerDown, onKeyDown, 'aria-valuenow': valueNow, 'aria-valuemin': valueMin, 'aria-valuemax': valueMax, }: ResizableHandleProps): react.JSX.Element;
|
|
1503
|
+
interface ResizablePanelGroupProps {
|
|
1504
|
+
direction: ResizableDirection;
|
|
1505
|
+
/** Alternating ResizablePanel / ResizableHandle elements — N panels
|
|
1506
|
+
* separated by N-1 handles. */
|
|
1507
|
+
children: ReactNode;
|
|
1508
|
+
className?: string;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* ResizablePanelGroup — a row or column of resizable panes (editor layouts,
|
|
1512
|
+
* multi-pane inspectors). Each handle resizes only its two immediate
|
|
1513
|
+
* neighbor panels (drag redistributes size between them, clamped to both
|
|
1514
|
+
* panels' own min/max, so their combined size is a fixed invariant for the
|
|
1515
|
+
* duration of one drag) — the same handle-local model most resizable-panel
|
|
1516
|
+
* implementations use, rather than redistributing across the whole group.
|
|
1517
|
+
*
|
|
1518
|
+
* Desktop-oriented, like NavigationMenu — a drag handle has no direct touch
|
|
1519
|
+
* equivalent; panels render at their default sizes on touch rather than
|
|
1520
|
+
* attempting a redesign no consumer has asked for yet. For a two-pane
|
|
1521
|
+
* layout that also needs a mobile single-column fallback, use SplitPane
|
|
1522
|
+
* instead.
|
|
1523
|
+
*
|
|
1524
|
+
* `direction="vertical"` needs a definite-height ancestor — percentage
|
|
1525
|
+
* flex-basis on a column's children only resolves against one.
|
|
1526
|
+
*/
|
|
1527
|
+
declare function ResizablePanelGroup({ direction, children, className }: ResizablePanelGroupProps): react.JSX.Element;
|
|
1528
|
+
|
|
1529
|
+
type SortDirection = 'asc' | 'desc';
|
|
1530
|
+
interface DataTableColumn<T> {
|
|
1531
|
+
key: string;
|
|
1532
|
+
header: string;
|
|
1533
|
+
sortable?: boolean;
|
|
1534
|
+
/** Cell content. Defaults to String(row[key]) when omitted. */
|
|
1535
|
+
render?: (row: T) => ReactNode;
|
|
1536
|
+
/** Value compared when sorting by this column. Defaults to row[key].
|
|
1537
|
+
* Required if `key` isn't a property on T (a computed/joined column). */
|
|
1538
|
+
sortValue?: (row: T) => string | number;
|
|
1539
|
+
align?: 'start' | 'end';
|
|
1540
|
+
}
|
|
1541
|
+
interface DataTableProps<T> {
|
|
1542
|
+
columns: DataTableColumn<T>[];
|
|
1543
|
+
data: T[];
|
|
1544
|
+
getRowKey: (row: T) => string;
|
|
1545
|
+
/** Initial sort — uncontrolled after mount, like Table itself has no
|
|
1546
|
+
* externally-controlled state. */
|
|
1547
|
+
defaultSortKey?: string;
|
|
1548
|
+
defaultSortDirection?: SortDirection;
|
|
1549
|
+
emptyMessage?: string;
|
|
1550
|
+
className?: string;
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* DataTable — sortable table built on Table's own primitives (no
|
|
1554
|
+
* virtualization or filtering — those stay out of scope, matching Table's
|
|
1555
|
+
* own "not a data grid" boundary). Column-driven: pass `columns` + `data`
|
|
1556
|
+
* rather than composing `<TableRow>`/`<TableCell>` by hand.
|
|
1557
|
+
*
|
|
1558
|
+
* Sort state is internal (uncontrolled) and click-cycles
|
|
1559
|
+
* ascending → descending → unsorted for a sortable column, mirroring the
|
|
1560
|
+
* "toggle → toggle → clear" pattern most sortable tables use rather than
|
|
1561
|
+
* being permanently stuck in whichever direction was clicked last.
|
|
1562
|
+
*/
|
|
1563
|
+
declare function DataTable<T>({ columns, data, getRowKey, defaultSortKey, defaultSortDirection, emptyMessage, className, }: DataTableProps<T>): react.JSX.Element;
|
|
1564
|
+
|
|
1565
|
+
interface ComboboxOption {
|
|
1566
|
+
value: string;
|
|
1567
|
+
label: string;
|
|
1568
|
+
}
|
|
1569
|
+
interface ComboboxProps {
|
|
1570
|
+
options: ComboboxOption[];
|
|
1571
|
+
value: string | null;
|
|
1572
|
+
onChange: (value: string) => void;
|
|
1573
|
+
placeholder?: string;
|
|
1574
|
+
searchPlaceholder?: string;
|
|
1575
|
+
emptyMessage?: string;
|
|
1576
|
+
'aria-label': string;
|
|
1577
|
+
disabled?: boolean;
|
|
1578
|
+
className?: string;
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Combobox — a searchable single-select: `Popover` on desktop, a bottom-sheet
|
|
1582
|
+
* `Drawer` on mobile (the platform's standard adaptive-surface pattern,
|
|
1583
|
+
* matching `Menu` and `DatePicker`). For a fixed, short option list where
|
|
1584
|
+
* search adds no value, use `Select`'s native `<select>` instead — this is
|
|
1585
|
+
* for option lists long enough that typing to filter beats scanning.
|
|
1586
|
+
*
|
|
1587
|
+
* The trigger is built in, like `DatePicker` — a combobox is a form field,
|
|
1588
|
+
* not an arbitrary action-menu trigger.
|
|
1589
|
+
*/
|
|
1590
|
+
declare function Combobox({ options, value, onChange, placeholder, searchPlaceholder, emptyMessage, 'aria-label': ariaLabel, disabled, className, }: ComboboxProps): react.JSX.Element;
|
|
1591
|
+
|
|
692
1592
|
interface DragHandleRowProps extends HTMLAttributes<HTMLDivElement> {
|
|
693
1593
|
children: ReactNode;
|
|
694
1594
|
/** Pass drag-listener and drag-control props from dnd-kit here. */
|
|
@@ -1099,6 +1999,57 @@ declare const MOBILE_BREAKPOINT_PX = 768;
|
|
|
1099
1999
|
*/
|
|
1100
2000
|
declare function useIsMobile(breakpointPx?: number): boolean;
|
|
1101
2001
|
|
|
2002
|
+
/**
|
|
2003
|
+
* useIsOffline — whether the browser currently has no network connection.
|
|
2004
|
+
*
|
|
2005
|
+
* SSR-safe: always initialises to `false` (online) so the server-rendered HTML
|
|
2006
|
+
* matches the first client render, then reads the real `navigator.onLine`
|
|
2007
|
+
* value in `useEffect`. This picks up the case where the page loaded already
|
|
2008
|
+
* offline (e.g. served from the service-worker cache) at the cost of an
|
|
2009
|
+
* imperceptible one-frame delay before the offline state appears — the same
|
|
2010
|
+
* trade-off `OfflineBanner` makes, which this hook was extracted from so every
|
|
2011
|
+
* consumer shares one connectivity source instead of re-deriving it slightly
|
|
2012
|
+
* differently (research 0012, epic task 2.32).
|
|
2013
|
+
*
|
|
2014
|
+
* Reflects only `navigator.onLine` — true whenever the OS reports a network
|
|
2015
|
+
* interface is up, even without real upstream connectivity (e.g. Wi-Fi with no
|
|
2016
|
+
* internet). That is a known limitation of the browser API itself, not of this
|
|
2017
|
+
* hook; there is currently no better cross-browser signal available.
|
|
2018
|
+
*/
|
|
2019
|
+
declare function useIsOffline(): boolean;
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* The two reasons an app tile can be non-interactive, kept structurally
|
|
2023
|
+
* distinct because they have different causes and must never read as the same
|
|
2024
|
+
* thing to a user (research 0012, epic task 2.33):
|
|
2025
|
+
*
|
|
2026
|
+
* - `'connectivity-dimmed'` — a no-offline-tier app, dimmed only while the
|
|
2027
|
+
* device is **actually offline right now**. Reactive and temporary; clears
|
|
2028
|
+
* the instant connectivity returns.
|
|
2029
|
+
* - `'capability-restricted'` — a `device-only` app on a surface without the
|
|
2030
|
+
* durable, encrypted, device-auth-gated store it needs. Static and
|
|
2031
|
+
* unrelated to connectivity — it stays restricted even when fully online,
|
|
2032
|
+
* and must never say "offline" to a user who plainly isn't.
|
|
2033
|
+
*/
|
|
2034
|
+
type OfflineTileState = 'connectivity-dimmed' | 'capability-restricted' | null;
|
|
2035
|
+
/**
|
|
2036
|
+
* Which of the two states, if any, applies to an app tile given its declared
|
|
2037
|
+
* offline tier.
|
|
2038
|
+
*
|
|
2039
|
+
* `deviceOnlyAvailable` is the caller's own
|
|
2040
|
+
* `isDeviceOnlyTierAvailable()` result (from
|
|
2041
|
+
* `@sovereignfs/sdk/device-client`) — deliberately taken as a plain boolean
|
|
2042
|
+
* parameter rather than computed inside this hook, so `@sovereignfs/ui` never
|
|
2043
|
+
* depends on `@sovereignfs/sdk` (this package must stay usable standalone,
|
|
2044
|
+
* outside the plugin runtime — RFC 0073).
|
|
2045
|
+
*
|
|
2046
|
+
* An `offline-first` app is never restricted by either state: that tier's
|
|
2047
|
+
* whole point is working with no network, so it stays fully interactive both
|
|
2048
|
+
* online and offline. A `device-only` app that *is* available behaves the
|
|
2049
|
+
* same way — the restriction is about availability, not connectivity.
|
|
2050
|
+
*/
|
|
2051
|
+
declare function useOfflineTileState(offline: 'offline-first' | 'device-only' | undefined, deviceOnlyAvailable: boolean): OfflineTileState;
|
|
2052
|
+
|
|
1102
2053
|
interface CommitOnEnterOrBlurHandlers {
|
|
1103
2054
|
onKeyDown: (e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
|
1104
2055
|
onBlur: () => void;
|
|
@@ -1127,4 +2078,313 @@ interface CommitOnEnterOrBlurHandlers {
|
|
|
1127
2078
|
*/
|
|
1128
2079
|
declare function useCommitOnEnterOrBlur(onCommit: () => void): CommitOnEnterOrBlurHandlers;
|
|
1129
2080
|
|
|
1130
|
-
|
|
2081
|
+
interface UseSwipeRevealOptions {
|
|
2082
|
+
/** Max reveal distance in px — the row translates by `-revealWidth` when open. */
|
|
2083
|
+
revealWidth: number;
|
|
2084
|
+
/** Whether the reveal is currently open. Controlled by the caller so a
|
|
2085
|
+
* list of rows can coordinate "only one open at a time" — this hook has
|
|
2086
|
+
* no open state of its own. */
|
|
2087
|
+
open: boolean;
|
|
2088
|
+
/** Called when a drag releases past the halfway point — the caller should
|
|
2089
|
+
* set `open` to true. */
|
|
2090
|
+
onOpen: () => void;
|
|
2091
|
+
/** Called when a drag releases before the halfway point — the caller
|
|
2092
|
+
* should set `open` to false. */
|
|
2093
|
+
onClose: () => void;
|
|
2094
|
+
/** Skip entirely (hooks can't be called conditionally). */
|
|
2095
|
+
disabled?: boolean;
|
|
2096
|
+
}
|
|
2097
|
+
interface UseSwipeRevealHandlers {
|
|
2098
|
+
onPointerDown: (e: PointerEvent) => void;
|
|
2099
|
+
onPointerMove: (e: PointerEvent) => void;
|
|
2100
|
+
onPointerUp: (e: PointerEvent) => void;
|
|
2101
|
+
onPointerCancel: (e: PointerEvent) => void;
|
|
2102
|
+
}
|
|
2103
|
+
interface UseSwipeRevealResult {
|
|
2104
|
+
/** Attach to the swipeable row element — the hook writes a live transform
|
|
2105
|
+
* to it directly during the drag (not via React state) so the row tracks
|
|
2106
|
+
* the finger at 60fps with no re-render per pointermove. */
|
|
2107
|
+
rowRef: RefObject<HTMLDivElement | null>;
|
|
2108
|
+
/** Spread onto the row's pointer event handlers. */
|
|
2109
|
+
handlers: UseSwipeRevealHandlers;
|
|
2110
|
+
}
|
|
2111
|
+
/**
|
|
2112
|
+
* useSwipeReveal — horizontal swipe-to-reveal (e.g. Done/Delete actions
|
|
2113
|
+
* behind a list row), extracted from `sovereign-tasks`'s `TaskItem`/
|
|
2114
|
+
* `ListSidebar`, which independently hand-rolled the identical gesture twice
|
|
2115
|
+
* with only `revealWidth` differing (RFC 0079, epic task 9.20).
|
|
2116
|
+
*
|
|
2117
|
+
* Axis-locks on the first 8px of movement (so a vertical scroll or the
|
|
2118
|
+
* carousel's own horizontal swipe-between-slides gesture isn't hijacked by a
|
|
2119
|
+
* diagonal drag) and only resolves to open/closed on release, past the
|
|
2120
|
+
* halfway point of `revealWidth`.
|
|
2121
|
+
*/
|
|
2122
|
+
declare function useSwipeReveal({ revealWidth, open, onOpen, onClose, disabled, }: UseSwipeRevealOptions): UseSwipeRevealResult;
|
|
2123
|
+
|
|
2124
|
+
interface UseSnapCarouselOptions {
|
|
2125
|
+
/** Total number of slides — settle detection clamps to this range. */
|
|
2126
|
+
itemCount: number;
|
|
2127
|
+
/** Called when the user's scroll gesture settles on a different slide
|
|
2128
|
+
* index than the last one reported. */
|
|
2129
|
+
onSettle?: (index: number) => void;
|
|
2130
|
+
/** Debounce delay (ms) after the last `scroll` event before checking where
|
|
2131
|
+
* the container settled. Default 120 — matches the value this hook was
|
|
2132
|
+
* extracted from. Debounced rather than keyed to the `scrollend` event,
|
|
2133
|
+
* which pre-17.4 iOS Safari/WKWebView doesn't support. */
|
|
2134
|
+
debounceMs?: number;
|
|
2135
|
+
}
|
|
2136
|
+
interface UseSnapCarouselResult {
|
|
2137
|
+
/** Attach to the `scroll-snap-type: x` scroll container. */
|
|
2138
|
+
scrollRef: RefObject<HTMLDivElement | null>;
|
|
2139
|
+
/** Imperatively scrolls the container to a slide index. */
|
|
2140
|
+
scrollToIndex: (index: number, behavior?: ScrollBehavior) => void;
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* useSnapCarousel — debounced "settled slide" detection over a native
|
|
2144
|
+
* `scroll-snap-type: x` container, extracted from `sovereign-tasks`'s
|
|
2145
|
+
* `MobileTasksCarousel` and independently reimplemented in
|
|
2146
|
+
* `sovereign-shopper`'s `MobileShopperCarousel` (RFC 0079, epic task 9.20).
|
|
2147
|
+
* Gives swipe-between-slides physics for free via native scroll-snap, with
|
|
2148
|
+
* this hook only handling "which slide did we land on."
|
|
2149
|
+
*/
|
|
2150
|
+
declare function useSnapCarousel({ itemCount, onSettle, debounceMs, }: UseSnapCarouselOptions): UseSnapCarouselResult;
|
|
2151
|
+
|
|
2152
|
+
interface UseResponsiveLayoutOptions<TWeb, TMobile> {
|
|
2153
|
+
web: TWeb;
|
|
2154
|
+
mobile: TMobile;
|
|
2155
|
+
breakpointPx?: number;
|
|
2156
|
+
}
|
|
2157
|
+
interface UseResponsiveLayoutResult<TWeb, TMobile> {
|
|
2158
|
+
isMobile: boolean;
|
|
2159
|
+
value: TWeb | TMobile;
|
|
2160
|
+
}
|
|
2161
|
+
/**
|
|
2162
|
+
* useResponsiveLayout — picks between a `web` and `mobile` value for the
|
|
2163
|
+
* current viewport, built on `useIsMobile` (same breakpoint/SSR-safe-default
|
|
2164
|
+
* behavior, not reimplemented here). Generic over the value type so it works
|
|
2165
|
+
* for JSX (the common case — forking an entire component tree, not just a
|
|
2166
|
+
* class name) or any other per-breakpoint value a plugin wants to pick
|
|
2167
|
+
* without repeating the isMobile-ternary boilerplate every call site.
|
|
2168
|
+
*
|
|
2169
|
+
* This formalizes the `if (isMobile) return <mobile/>; return <web/>;` fork
|
|
2170
|
+
* plugins have hand-rolled per-plugin (e.g. a `MobileAwareShell` component
|
|
2171
|
+
* that swaps an entire routed page tree for a client-only mobile one) —
|
|
2172
|
+
* see `ResponsiveSurface` for the equivalent as a plain JSX component.
|
|
2173
|
+
*/
|
|
2174
|
+
declare function useResponsiveLayout<TWeb, TMobile>({ web, mobile, breakpointPx, }: UseResponsiveLayoutOptions<TWeb, TMobile>): UseResponsiveLayoutResult<TWeb, TMobile>;
|
|
2175
|
+
|
|
2176
|
+
interface UseCarouselRouteSyncOptions {
|
|
2177
|
+
/** Maps the current pathname to a slide index — the caller's own routing
|
|
2178
|
+
* logic (e.g. special-casing a virtual "starred" slide), unchanged from
|
|
2179
|
+
* whatever a plugin already has today. This hook has no idea what a
|
|
2180
|
+
* plugin's routes look like, by design — it never imports a router. */
|
|
2181
|
+
indexForPathname: (pathname: string) => number;
|
|
2182
|
+
/** Inverse mapping — given a settled slide index, the path to navigate to. */
|
|
2183
|
+
pathForIndex: (index: number) => string;
|
|
2184
|
+
/** The current pathname. The caller reads this via their own router (e.g.
|
|
2185
|
+
* `usePathname()` from `next/navigation`) and passes it in every render —
|
|
2186
|
+
* this package stays framework-generic and has zero runtime dependencies. */
|
|
2187
|
+
pathname: string;
|
|
2188
|
+
/** Called when this hook wants to navigate — typically
|
|
2189
|
+
* `(path) => router.replace(path, { scroll: false })`. */
|
|
2190
|
+
onNavigate: (path: string) => void;
|
|
2191
|
+
}
|
|
2192
|
+
interface UseCarouselRouteSyncResult {
|
|
2193
|
+
/** Feed directly into SwipableMobileCarousel's `activeIndex` prop. */
|
|
2194
|
+
activeIndex: number;
|
|
2195
|
+
/** Feed directly into SwipableMobileCarousel's `onSettle` prop. */
|
|
2196
|
+
onSettle: (index: number) => void;
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* useCarouselRouteSync — centralizes the pathname↔slide-index mapping and the
|
|
2200
|
+
* "was this pathname change our own settle, or an external navigation"
|
|
2201
|
+
* distinction that carousel-driving plugins otherwise hand-roll per plugin
|
|
2202
|
+
* (a tapped `<Link>`, browser back/forward, vs. this hook's own
|
|
2203
|
+
* `router.replace` after a swipe settles). Router-agnostic on purpose: the
|
|
2204
|
+
* caller owns the actual `usePathname()`/`router.replace()` calls and only
|
|
2205
|
+
* hands this hook plain values/callbacks.
|
|
2206
|
+
*
|
|
2207
|
+
* Deliberately does not own neighbor-prefetch (SwipableMobileCarousel's
|
|
2208
|
+
* `prefetchDistance`/mount-window covers that) or scroll mechanics (the
|
|
2209
|
+
* carousel component reacts to `activeIndex` changes itself) — this hook is
|
|
2210
|
+
* pathname↔index mapping only, keeping routing glue decoupled from
|
|
2211
|
+
* data-fetching and DOM scrolling.
|
|
2212
|
+
*/
|
|
2213
|
+
declare function useCarouselRouteSync({ indexForPathname, pathForIndex, pathname, onNavigate, }: UseCarouselRouteSyncOptions): UseCarouselRouteSyncResult;
|
|
2214
|
+
|
|
2215
|
+
interface ResponsiveSurfaceProps {
|
|
2216
|
+
web: ReactNode;
|
|
2217
|
+
mobile: ReactNode;
|
|
2218
|
+
breakpointPx?: number;
|
|
2219
|
+
}
|
|
2220
|
+
/**
|
|
2221
|
+
* ResponsiveSurface — thin JSX convenience wrapper around
|
|
2222
|
+
* `useResponsiveLayout` for the common two-tree case: render an entirely
|
|
2223
|
+
* different component tree below the breakpoint, not a CSS squeeze of the
|
|
2224
|
+
* same one. Renders only whichever side is active — the other tree is never
|
|
2225
|
+
* mounted.
|
|
2226
|
+
*
|
|
2227
|
+
* This does not replace a plugin's own shell component (e.g. a
|
|
2228
|
+
* `MobileAwareShell` that also threads plugin-specific props like a refresh
|
|
2229
|
+
* signal) — those stay a thin per-plugin wrapper around this.
|
|
2230
|
+
*/
|
|
2231
|
+
declare function ResponsiveSurface({ web, mobile, breakpointPx }: ResponsiveSurfaceProps): react.JSX.Element;
|
|
2232
|
+
|
|
2233
|
+
interface SwipableMobileCarouselDotsProps {
|
|
2234
|
+
count: number;
|
|
2235
|
+
activeIndex: number;
|
|
2236
|
+
onJump: (index: number) => void;
|
|
2237
|
+
/** Per-dot accessible names, e.g. ["Lists", "Starred", "Groceries"]. Falls
|
|
2238
|
+
* back to "Slide N of count" for any index without one. */
|
|
2239
|
+
labels?: (string | undefined)[];
|
|
2240
|
+
/** Labels the whole group — required, no generic fallback (mirrors
|
|
2241
|
+
* SwipableMobileCarousel's own required aria-label). */
|
|
2242
|
+
'aria-label': string;
|
|
2243
|
+
className?: string;
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* SwipableMobileCarouselDots — a real, tappable, labeled slide indicator.
|
|
2247
|
+
* Standalone (not carousel-only): reusable anywhere a set of positions needs
|
|
2248
|
+
* a dot indicator (e.g. an image gallery, an onboarding stepper), and is also
|
|
2249
|
+
* SwipableMobileCarousel's default `renderIndicator`.
|
|
2250
|
+
*
|
|
2251
|
+
* Replaces the `aria-hidden`, non-interactive dots pattern both
|
|
2252
|
+
* sovereign-tasks and sovereign-shopper currently hand-roll — every dot here
|
|
2253
|
+
* is a real `role="tab"` button with its own accessible name, reachable by
|
|
2254
|
+
* keyboard. No roving tabIndex/arrow-key handling: `Tabs` (this library's
|
|
2255
|
+
* other `role="tablist"` component) has none either, so this stays
|
|
2256
|
+
* consistent with it rather than gold-plating past its sibling.
|
|
2257
|
+
*/
|
|
2258
|
+
declare function SwipableMobileCarouselDots({ count, activeIndex, onJump, labels, 'aria-label': ariaLabel, className, }: SwipableMobileCarouselDotsProps): react.JSX.Element;
|
|
2259
|
+
|
|
2260
|
+
interface SwipableMobileCarouselIndicatorProps {
|
|
2261
|
+
count: number;
|
|
2262
|
+
activeIndex: number;
|
|
2263
|
+
labels: (string | undefined)[];
|
|
2264
|
+
onJump: (index: number) => void;
|
|
2265
|
+
}
|
|
2266
|
+
interface SwipableMobileCarouselProps {
|
|
2267
|
+
/** Controlled active slide index — the caller owns this (typically via
|
|
2268
|
+
* useCarouselRouteSync, or plain useState). */
|
|
2269
|
+
activeIndex: number;
|
|
2270
|
+
/** Called when a swipe gesture settles on a different index, or when the
|
|
2271
|
+
* reorder-jump fix re-snaps after the active slide's position changes.
|
|
2272
|
+
* The caller is responsible for updating `activeIndex` in response. */
|
|
2273
|
+
onSettle: (index: number) => void;
|
|
2274
|
+
/** How many slides on each side of the active one stay mounted. Default 1
|
|
2275
|
+
* matches both sovereign-tasks' and sovereign-shopper's existing "swipe
|
|
2276
|
+
* never shows a spinner" behavior. Slides outside this window keep their
|
|
2277
|
+
* DOM slot (so scroll-snap's position-based indexing stays correct) but
|
|
2278
|
+
* their children are not mounted at all. */
|
|
2279
|
+
prefetchDistance?: number;
|
|
2280
|
+
/** Passed straight through to the internal useSnapCarousel. */
|
|
2281
|
+
settleDebounceMs?: number;
|
|
2282
|
+
/** Required — labels the scroller region for assistive tech. No generic
|
|
2283
|
+
* fallback, since one would be wrong for every actual consumer. */
|
|
2284
|
+
'aria-label': string;
|
|
2285
|
+
/** Renders the indicator. Defaults to SwipableMobileCarouselDots. Pass
|
|
2286
|
+
* `null` to render no indicator at all. */
|
|
2287
|
+
renderIndicator?: null | ((props: SwipableMobileCarouselIndicatorProps) => ReactNode);
|
|
2288
|
+
className?: string;
|
|
2289
|
+
/** Must be SwipableMobileCarouselSlide elements (nullish/boolean children
|
|
2290
|
+
* are safely skipped for conditional slides). A dev-mode warning is
|
|
2291
|
+
* logged for any other child type — see resolveSlides below. */
|
|
2292
|
+
children: ReactNode;
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* SwipableMobileCarousel — a compound component for swiping between full-
|
|
2296
|
+
* width slides (each an independent route/view on mobile), wrapping
|
|
2297
|
+
* useSnapCarousel. This library's first compound component: every other
|
|
2298
|
+
* component here is flat/prop-based, but slide content is large, arbitrary,
|
|
2299
|
+
* per-plugin subtrees that need a header-known/body-loading split an
|
|
2300
|
+
* array-of-objects prop would only express with worse JSX ergonomics.
|
|
2301
|
+
*
|
|
2302
|
+
* Owns rendering and mount-window mechanics only — it has no opinion on
|
|
2303
|
+
* where slide data lives. Do NOT: (a) aggregate cross-slide data inside a
|
|
2304
|
+
* SwipableMobileCarouselSlideBody (that recomputes on every slide render
|
|
2305
|
+
* regardless of which slide is active — belongs in the parent, which
|
|
2306
|
+
* already knows every slide exists); (b) mount a detail overlay (Sheet,
|
|
2307
|
+
* Dialog) inside a Slide's children — mount it as a sibling of
|
|
2308
|
+
* SwipableMobileCarousel instead, controlled by the same state that would
|
|
2309
|
+
* otherwise drive a routed page's overlay. sovereign-tasks' current
|
|
2310
|
+
* hand-rolled carousel does both and is measurably laggier than
|
|
2311
|
+
* sovereign-shopper's equivalent, which keeps its overlay in the routed pane.
|
|
2312
|
+
*/
|
|
2313
|
+
declare function SwipableMobileCarousel({ activeIndex, onSettle, prefetchDistance, settleDebounceMs, 'aria-label': ariaLabel, renderIndicator, className, children, }: SwipableMobileCarouselProps): react.JSX.Element;
|
|
2314
|
+
|
|
2315
|
+
interface SwipableMobileCarouselSlideProps {
|
|
2316
|
+
/** Stable identity for this slide, independent of its position among
|
|
2317
|
+
* siblings — required. This is what makes SwipableMobileCarousel's
|
|
2318
|
+
* reorder-jump fix possible: a plain array index can't distinguish "this
|
|
2319
|
+
* slide moved to position 2" from "this slide was removed and a
|
|
2320
|
+
* different one is now at position 2." Reuse whatever id the caller
|
|
2321
|
+
* already keys their `.map()` on (e.g. a list's own id). */
|
|
2322
|
+
slideKey: string;
|
|
2323
|
+
/** Accessible label surfaced to the default dots indicator (e.g.
|
|
2324
|
+
* "Groceries"). Falls back to "Slide N of count" if omitted — omitting it
|
|
2325
|
+
* logs a dev-mode warning, since every real slide usually has a title
|
|
2326
|
+
* available synchronously already. */
|
|
2327
|
+
label?: string;
|
|
2328
|
+
/** SwipableMobileCarouselSlideHeader/Body/Footer, in any order/subset. */
|
|
2329
|
+
children: ReactNode;
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* SwipableMobileCarouselSlide — a single slide of a SwipableMobileCarousel.
|
|
2333
|
+
* Renders its children only while within the carousel's mount window (the
|
|
2334
|
+
* active slide ± its configured prefetchDistance); outside that window it
|
|
2335
|
+
* renders nothing, matching both sovereign-tasks and sovereign-shopper's
|
|
2336
|
+
* existing "never fetch/render a non-neighbor slide" behavior, now
|
|
2337
|
+
* centralized instead of hand-rolled per plugin.
|
|
2338
|
+
*/
|
|
2339
|
+
declare function SwipableMobileCarouselSlide({ children }: SwipableMobileCarouselSlideProps): react.JSX.Element | null;
|
|
2340
|
+
|
|
2341
|
+
interface SwipableMobileCarouselSlideHeaderProps {
|
|
2342
|
+
children: ReactNode;
|
|
2343
|
+
className?: string;
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* SwipableMobileCarouselSlideHeader — always renders its children, no
|
|
2347
|
+
* loading concept. Meant for content already known synchronously (a list's
|
|
2348
|
+
* title, color, item count from data the caller already has) so it can
|
|
2349
|
+
* render immediately even while SwipableMobileCarouselSlideBody's own
|
|
2350
|
+
* content is still loading — see that component's doc comment.
|
|
2351
|
+
*/
|
|
2352
|
+
declare function SwipableMobileCarouselSlideHeader({ children, className, }: SwipableMobileCarouselSlideHeaderProps): react.JSX.Element;
|
|
2353
|
+
|
|
2354
|
+
interface SwipableMobileCarouselSlideBodyProps {
|
|
2355
|
+
children: ReactNode;
|
|
2356
|
+
/** While true, renders `loadingFallback` INSTEAD OF children — scoped to
|
|
2357
|
+
* just this region, not the whole slide. This is what fixes the
|
|
2358
|
+
* "whole slide blanks out, title included, until its own fetch resolves"
|
|
2359
|
+
* bug: a caller renders `<SwipableMobileCarouselSlideHeader>` from
|
|
2360
|
+
* already-known metadata unconditionally, and gates only this component's
|
|
2361
|
+
* `loading` on the slower fetch — the natural way to compose
|
|
2362
|
+
* Header/Body/Footer as siblings already produces that behavior, instead
|
|
2363
|
+
* of gating the entire slide behind one boolean. */
|
|
2364
|
+
loading?: boolean;
|
|
2365
|
+
loadingFallback?: ReactNode;
|
|
2366
|
+
className?: string;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* SwipableMobileCarouselSlideBody — the scrollable region of a slide.
|
|
2370
|
+
*
|
|
2371
|
+
* Deliberately does not set `touch-action: pan-y` on its scroll container
|
|
2372
|
+
* (see SwipableMobileCarouselSlideParts.module.css's `.body` rule) — that
|
|
2373
|
+
* would intersect-to-empty against the carousel's own horizontal
|
|
2374
|
+
* touch-action handling (docs/architecture-rules.md's touch-action
|
|
2375
|
+
* intersection rule), breaking both the vertical scroll here and the
|
|
2376
|
+
* horizontal swipe between slides.
|
|
2377
|
+
*/
|
|
2378
|
+
declare function SwipableMobileCarouselSlideBody({ children, loading, loadingFallback, className, }: SwipableMobileCarouselSlideBodyProps): react.JSX.Element;
|
|
2379
|
+
|
|
2380
|
+
interface SwipableMobileCarouselSlideFooterProps {
|
|
2381
|
+
children: ReactNode;
|
|
2382
|
+
className?: string;
|
|
2383
|
+
}
|
|
2384
|
+
/**
|
|
2385
|
+
* SwipableMobileCarouselSlideFooter — always renders its children, no
|
|
2386
|
+
* loading concept. See SwipableMobileCarouselSlideHeader's doc comment.
|
|
2387
|
+
*/
|
|
2388
|
+
declare function SwipableMobileCarouselSlideFooter({ children, className, }: SwipableMobileCarouselSlideFooterProps): react.JSX.Element;
|
|
2389
|
+
|
|
2390
|
+
export { Accordion, type AccordionItem, type AccordionProps, Alert, type AlertProps, type AlertVariant, AspectRatio, type AspectRatioProps, Avatar, type AvatarProps, Badge, type BadgeProps, type BadgeStatus, type BadgeVariant, BalanceChip, type BalanceChipProps, type BannerVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, Calendar, type CalendarProps, Card, type CardProps, CheckableListRow, type CheckableListRowProps, Checkbox, type CheckboxProps, CodeTextarea, type CodeTextareaProps, Collapsible, type CollapsibleProps, Combobox, type ComboboxOption, type ComboboxProps, Command, type CommandItem, type CommandProps, type CommitOnEnterOrBlurHandlers, ConfirmDialog, type ConfirmDialogProps, ContextMenu, type ContextMenuProps, CurrencyInput, type CurrencyInputProps, DataTable, type DataTableColumn, type DataTableProps, DatePicker, type DatePickerProps, DeviceOnlyGate, type DeviceOnlyGateProps, DeviceStorageKeyGate, type DeviceStorageKeyGateProps, type DeviceStorageKeyGateStatus, Dialog, type DialogProps, type DialogSize, DragHandleRow, type DragHandleRowProps, Drawer, type DrawerProps, EmptyState, type EmptyStateProps, FileDropzone, type FileDropzoneProps, type FooterIcon, FormField, type FormFieldProps, type FormFieldRenderProps, HoverCard, type HoverCardProps, Icon, type IconName, IconPicker, type IconPickerProps, type IconProps, Input, type InputProps, Item, type ItemProps, Kbd, type KbdProps, Label, type LabelProps, LegalLinks, type LegalLinksProps, type LongPressHandlers, MOBILE_BREAKPOINT_PX, Markdown, type MarkdownProps, Marker, type MarkerProps, MemberMultiSelect, type MemberMultiSelectOption, type MemberMultiSelectProps, Menu, MenuEntries, type MenuEntry, type MenuItem, type MenuLabel, type MenuProps, type MenuSeparator, Menubar, type MenubarMenu, type MenubarProps, Message, type MessageProps, MessageScroller, type MessageScrollerProps, type MessageSender, MobileAppsDrawer, type MobileAppsDrawerItem, type MobileAppsDrawerProps, MobileFooter, type MobileFooterProps, MobileHeader, type MobileHeaderProps, type NavTabItem, type NavTabLinkProps, NavTabs, type NavTabsProps, NavigationMenu, type NavigationMenuItem, type NavigationMenuProps, OfflineGate, type OfflineGateProps, type OfflineTileState, OverlayHeader, type OverlayHeaderProps, PageContainer, type PageContainerProps, PageHeader, type PageHeaderProps, Pagination, type PaginationProps, Popover, type PopoverProps, Progress, type ProgressProps, QuantityStepper, type QuantityStepperProps, RadioGroup, type RadioGroupItem, type RadioGroupProps, type ResizableDirection, ResizableHandle, type ResizableHandleProps, ResizablePanel, ResizablePanelGroup, type ResizablePanelGroupProps, type ResizablePanelProps, ResponsiveSurface, type ResponsiveSurfaceProps, ScrollArea, type ScrollAreaProps, SegmentedControl, type SegmentedControlProps, type SegmentedOption, Select, type SelectProps, Sheet, type SheetProps, Slider, type SliderProps, type SortDirection, Spinner, type SpinnerProps, type SplitMethod, SplitMethodSelector, type SplitMethodSelectorProps, SplitPane, type SplitPaneProps, StatusBadge, type StatusBadgeProps, type StatusBadgeStatus, SuggestionInput, type SuggestionInputProps, type SuggestionOption, SwipableMobileCarousel, SwipableMobileCarouselDots, type SwipableMobileCarouselDotsProps, type SwipableMobileCarouselIndicatorProps, type SwipableMobileCarouselProps, SwipableMobileCarouselSlide, SwipableMobileCarouselSlideBody, type SwipableMobileCarouselSlideBodyProps, SwipableMobileCarouselSlideFooter, type SwipableMobileCarouselSlideFooterProps, SwipableMobileCarouselSlideHeader, type SwipableMobileCarouselSlideHeaderProps, type SwipableMobileCarouselSlideProps, SystemBanner, type SystemBannerProps, type TabItem, Table, TableBody, TableCell, TableHead, TableHeaderCell, type TableProps, TableRow, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ToastContextValue, type ToastItem, ToastProvider, Toggle, type ToggleProps, Tooltip, type TooltipProps, Typography, type TypographyProps, type TypographyVariant, type UseCarouselRouteSyncOptions, type UseCarouselRouteSyncResult, type UseLongPressOptions, type UseResponsiveLayoutOptions, type UseResponsiveLayoutResult, type UseSnapCarouselOptions, type UseSnapCarouselResult, type UseSwipeRevealHandlers, type UseSwipeRevealOptions, type UseSwipeRevealResult, useCarouselRouteSync, useCommitOnEnterOrBlur, useDoubleTapHandler, useIsMobile, useIsOffline, useLongPress, useOfflineTileState, useOverlaySecondRow, useResponsiveLayout, useSingleOrDoubleTap, useSnapCarousel, useSwipeReveal, useToast };
|