@ratio-ui/core 0.1.0 → 0.2.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/LICENSE +21 -0
- package/README.md +3 -1
- package/dist/catalogue.json +1719 -43
- package/dist/components/app-shell/AppShell.d.ts +22 -0
- package/dist/components/app-shell/AppShell.js +39 -0
- package/dist/components/app-shell/context.js +5 -0
- package/dist/components/app-shell/index.d.ts +2 -0
- package/dist/components/app-shell/index.js +1 -0
- package/dist/components/avatar/Avatar.d.ts +22 -0
- package/dist/components/avatar/Avatar.js +38 -0
- package/dist/components/avatar/index.d.ts +2 -0
- package/dist/components/avatar/index.js +1 -0
- package/dist/components/button/Button.d.ts +37 -0
- package/dist/components/button/Button.js +41 -0
- package/dist/components/button/index.d.ts +2 -0
- package/dist/components/button/index.js +1 -0
- package/dist/components/card/Card.d.ts +33 -0
- package/dist/components/card/Card.js +53 -0
- package/dist/components/card/index.d.ts +2 -0
- package/dist/components/card/index.js +1 -0
- package/dist/components/checkbox/Checkbox.d.ts +10 -0
- package/dist/components/checkbox/Checkbox.js +28 -0
- package/dist/components/checkbox/index.d.ts +2 -0
- package/dist/components/checkbox/index.js +1 -0
- package/dist/components/data-table/DataTable.d.ts +65 -0
- package/dist/components/data-table/DataTable.js +141 -0
- package/dist/components/data-table/index.d.ts +2 -0
- package/dist/components/data-table/index.js +1 -0
- package/dist/components/definition/Definition.d.ts +14 -0
- package/dist/components/definition/Definition.js +29 -0
- package/dist/components/definition/index.d.ts +2 -0
- package/dist/components/definition/index.js +1 -0
- package/dist/components/dialog/Dialog.d.ts +44 -0
- package/dist/components/dialog/Dialog.js +79 -0
- package/dist/components/dialog/index.d.ts +2 -0
- package/dist/components/dialog/index.js +1 -0
- package/dist/components/dropdown-menu/DropdownMenu.d.ts +73 -0
- package/dist/components/dropdown-menu/DropdownMenu.js +116 -0
- package/dist/components/dropdown-menu/index.d.ts +2 -0
- package/dist/components/dropdown-menu/index.js +1 -0
- package/dist/components/empty-state/EmptyState.d.ts +15 -0
- package/dist/components/empty-state/EmptyState.js +30 -0
- package/dist/components/empty-state/index.d.ts +2 -0
- package/dist/components/empty-state/index.js +1 -0
- package/dist/components/filter-tabs/FilterTabs.d.ts +28 -0
- package/dist/components/filter-tabs/FilterTabs.js +37 -0
- package/dist/components/filter-tabs/index.d.ts +2 -0
- package/dist/components/filter-tabs/index.js +1 -0
- package/dist/components/global-search/GlobalSearch.d.ts +51 -0
- package/dist/components/global-search/GlobalSearch.js +144 -0
- package/dist/components/global-search/index.d.ts +2 -0
- package/dist/components/global-search/index.js +1 -0
- package/dist/components/icon/registry.d.ts +3 -2
- package/dist/components/icon/registry.js +53 -1
- package/dist/components/input/Input.d.ts +19 -0
- package/dist/components/input/Input.js +26 -0
- package/dist/components/input/index.d.ts +2 -0
- package/dist/components/input/index.js +1 -0
- package/dist/components/input-group/InputGroup.d.ts +42 -0
- package/dist/components/input-group/InputGroup.js +83 -0
- package/dist/components/input-group/index.d.ts +2 -0
- package/dist/components/input-group/index.js +1 -0
- package/dist/components/kbd/Kbd.d.ts +17 -0
- package/dist/components/kbd/Kbd.js +21 -0
- package/dist/components/kbd/index.d.ts +2 -0
- package/dist/components/kbd/index.js +1 -0
- package/dist/components/link-provider/LinkProvider.d.ts +29 -0
- package/dist/components/link-provider/LinkProvider.js +15 -0
- package/dist/components/link-provider/RatioLink.js +18 -0
- package/dist/components/link-provider/context.js +4 -0
- package/dist/components/link-provider/index.d.ts +2 -0
- package/dist/components/link-provider/index.js +1 -0
- package/dist/components/metric-strip/MetricStrip.d.ts +23 -0
- package/dist/components/metric-strip/MetricStrip.js +39 -0
- package/dist/components/metric-strip/index.d.ts +2 -0
- package/dist/components/metric-strip/index.js +1 -0
- package/dist/components/page-header/PageHeader.d.ts +37 -0
- package/dist/components/page-header/PageHeader.js +62 -0
- package/dist/components/page-header/index.d.ts +2 -0
- package/dist/components/page-header/index.js +1 -0
- package/dist/components/pagination/Pagination.d.ts +18 -0
- package/dist/components/pagination/Pagination.js +59 -0
- package/dist/components/pagination/index.d.ts +2 -0
- package/dist/components/pagination/index.js +1 -0
- package/dist/components/popover/Popover.d.ts +27 -0
- package/dist/components/popover/Popover.js +47 -0
- package/dist/components/popover/index.d.ts +2 -0
- package/dist/components/popover/index.js +1 -0
- package/dist/components/search-field/SearchField.d.ts +23 -0
- package/dist/components/search-field/SearchField.js +66 -0
- package/dist/components/search-field/index.d.ts +2 -0
- package/dist/components/search-field/index.js +1 -0
- package/dist/components/side-nav/SideNav.d.ts +64 -0
- package/dist/components/side-nav/SideNav.js +194 -0
- package/dist/components/side-nav/index.d.ts +2 -0
- package/dist/components/side-nav/index.js +1 -0
- package/dist/components/skeleton/Skeleton.d.ts +13 -0
- package/dist/components/skeleton/Skeleton.js +21 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/index.js +1 -0
- package/dist/components/spinner/Spinner.d.ts +11 -0
- package/dist/components/spinner/Spinner.js +16 -0
- package/dist/components/spinner/index.d.ts +2 -0
- package/dist/components/spinner/index.js +1 -0
- package/dist/components/status-pill/StatusPill.d.ts +14 -0
- package/dist/components/status-pill/StatusPill.js +19 -0
- package/dist/components/status-pill/index.d.ts +2 -0
- package/dist/components/status-pill/index.js +1 -0
- package/dist/components/table/Table.d.ts +46 -0
- package/dist/components/table/Table.js +87 -0
- package/dist/components/table/index.d.ts +2 -0
- package/dist/components/table/index.js +1 -0
- package/dist/components/table-loader/TableLoader.d.ts +31 -0
- package/dist/components/table-loader/TableLoader.js +35 -0
- package/dist/components/table-loader/index.d.ts +2 -0
- package/dist/components/table-loader/index.js +1 -0
- package/dist/components/tabs/Tabs.d.ts +23 -0
- package/dist/components/tabs/Tabs.js +41 -0
- package/dist/components/tabs/index.d.ts +2 -0
- package/dist/components/tabs/index.js +1 -0
- package/dist/components/toaster/Toaster.d.ts +32 -0
- package/dist/components/toaster/Toaster.js +89 -0
- package/dist/components/toaster/index.d.ts +2 -0
- package/dist/components/toaster/index.js +1 -0
- package/dist/components/tooltip/Tooltip.d.ts +18 -0
- package/dist/components/tooltip/Tooltip.js +35 -0
- package/dist/components/tooltip/index.d.ts +2 -0
- package/dist/components/tooltip/index.js +1 -0
- package/dist/components/top-bar/StoreSwitcher.d.ts +26 -0
- package/dist/components/top-bar/StoreSwitcher.js +48 -0
- package/dist/components/top-bar/TopBar.d.ts +24 -0
- package/dist/components/top-bar/TopBar.js +53 -0
- package/dist/components/top-bar/index.d.ts +3 -0
- package/dist/components/top-bar/index.js +3 -0
- package/dist/index.d.ts +65 -1
- package/dist/index.js +35 -2
- package/dist/lib/cn.js +32 -27
- package/dist/lib/format.d.ts +31 -0
- package/dist/lib/format.js +65 -0
- package/dist/styles.css +1 -1
- package/package.json +129 -4
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { StatusPillTone } from "../status-pill/StatusPill.js";
|
|
2
|
+
import "../status-pill/index.js";
|
|
3
|
+
import { JSX, ReactNode } from "react";
|
|
4
|
+
/** One entry of the overflow menu. */
|
|
5
|
+
export type PageHeaderMenuItem = {
|
|
6
|
+
/** The text of the item. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Runs when the item is chosen. */
|
|
9
|
+
onSelect: () => void;
|
|
10
|
+
/** Draws the item in the danger colour, for an action that deletes. */
|
|
11
|
+
destructive?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/** Props of PageHeader. */
|
|
14
|
+
export type PageHeaderProps = {
|
|
15
|
+
/** The page title, shown as the level 1 heading. */
|
|
16
|
+
title: string;
|
|
17
|
+
/** An icon or logo shown before the title. */
|
|
18
|
+
icon?: ReactNode;
|
|
19
|
+
/** The state of the record, one word, beside the title. */
|
|
20
|
+
status?: {
|
|
21
|
+
label: string;
|
|
22
|
+
tone?: StatusPillTone;
|
|
23
|
+
};
|
|
24
|
+
/** Where the back link goes. The link shows only with this. On a router, LinkProvider draws it with the app's own link. */
|
|
25
|
+
backHref?: string;
|
|
26
|
+
/** The text of the back link, such as the parent page's name. Defaults to Back. */
|
|
27
|
+
backLabel?: string;
|
|
28
|
+
/** Buttons on the right, such as Export and Add product. */
|
|
29
|
+
actions?: ReactNode;
|
|
30
|
+
/** Entries of the overflow menu. The More actions button shows only when there is at least one. */
|
|
31
|
+
menuItems?: PageHeaderMenuItem[];
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The top of a page: an optional back link, then an icon, the title and a status pill on the left, and the actions with
|
|
35
|
+
* an overflow menu on the right. It wraps on a phone.
|
|
36
|
+
*/
|
|
37
|
+
export declare function PageHeader({ title, icon, status, backHref, backLabel, actions, menuItems }: PageHeaderProps): JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Button } from "../button/Button.js";
|
|
2
|
+
import { Icon } from "../icon/Icon.js";
|
|
3
|
+
import { RatioLink } from "../link-provider/RatioLink.js";
|
|
4
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../dropdown-menu/DropdownMenu.js";
|
|
5
|
+
import { StatusPill } from "../status-pill/StatusPill.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
// src/components/page-header/PageHeader.tsx
|
|
8
|
+
/**
|
|
9
|
+
* The top of a page: an optional back link, then an icon, the title and a status pill on the left, and the actions with
|
|
10
|
+
* an overflow menu on the right. It wraps on a phone.
|
|
11
|
+
*/
|
|
12
|
+
function PageHeader({ title, icon, status, backHref, backLabel = "Back", actions, menuItems }) {
|
|
13
|
+
const hasMenu = menuItems !== void 0 && menuItems.length > 0;
|
|
14
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
15
|
+
className: "rt:flex rt:flex-col rt:gap-2",
|
|
16
|
+
children: [backHref !== void 0 && /* @__PURE__ */ jsxs(RatioLink, {
|
|
17
|
+
href: backHref,
|
|
18
|
+
className: "rt:inline-flex rt:w-fit rt:items-center rt:gap-1 rt:rounded-sm rt:text-b1 rt:text-muted-foreground rt:transition-colors rt:hover:text-foreground rt:focus-visible:focus-ring",
|
|
19
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
20
|
+
name: "chevron-left",
|
|
21
|
+
size: 8
|
|
22
|
+
}), backLabel]
|
|
23
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
24
|
+
className: "rt:flex rt:flex-wrap rt:items-center rt:justify-between rt:gap-x-3 rt:gap-y-2",
|
|
25
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
26
|
+
className: "rt:flex rt:min-w-0 rt:items-center rt:gap-3",
|
|
27
|
+
children: [
|
|
28
|
+
icon !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
29
|
+
className: "rt:flex rt:shrink-0 rt:items-center",
|
|
30
|
+
children: icon
|
|
31
|
+
}),
|
|
32
|
+
/* @__PURE__ */ jsx("h1", {
|
|
33
|
+
className: "rt:m-0 rt:min-w-0 rt:truncate rt:text-h5 rt:text-foreground-strong",
|
|
34
|
+
children: title
|
|
35
|
+
}),
|
|
36
|
+
status !== void 0 && /* @__PURE__ */ jsx(StatusPill, {
|
|
37
|
+
tone: status.tone,
|
|
38
|
+
children: status.label
|
|
39
|
+
})
|
|
40
|
+
]
|
|
41
|
+
}), (actions !== void 0 || hasMenu) && /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: "rt:flex rt:flex-wrap rt:items-center rt:gap-2",
|
|
43
|
+
children: [actions, hasMenu && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
44
|
+
render: /* @__PURE__ */ jsx(Button, { variant: "secondary" }),
|
|
45
|
+
children: ["More actions", /* @__PURE__ */ jsx(Icon, {
|
|
46
|
+
name: "nav-caret-down",
|
|
47
|
+
size: 8
|
|
48
|
+
})]
|
|
49
|
+
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
50
|
+
side: "bottom",
|
|
51
|
+
align: "end",
|
|
52
|
+
children: menuItems.map((item) => /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
53
|
+
variant: item.destructive ? "destructive" : void 0,
|
|
54
|
+
onClick: item.onSelect,
|
|
55
|
+
children: item.label
|
|
56
|
+
}, item.label))
|
|
57
|
+
})] })]
|
|
58
|
+
})]
|
|
59
|
+
})]
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export { PageHeader };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PageHeader } from "./PageHeader.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ComponentProps, JSX } from "react";
|
|
2
|
+
/** The props of Pagination: the attributes of a nav element, except className, style and children, and the page being shown. */
|
|
3
|
+
export type PaginationProps = Omit<ComponentProps<'nav'>, 'className' | 'style' | 'children'> & {
|
|
4
|
+
/** The page being shown, counting from 1. */
|
|
5
|
+
page: number;
|
|
6
|
+
/** How many results one page holds. */
|
|
7
|
+
pageSize: number;
|
|
8
|
+
/** How many results there are in all, across every page. */
|
|
9
|
+
total: number;
|
|
10
|
+
/** Called with the page to show when Previous or Next is pressed. */
|
|
11
|
+
onPageChange: (page: number) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The footer under a table: Previous and Next arrows and the range on show, written "1 – 20 of 84 results". An arrow is
|
|
15
|
+
* dimmed to 50% and disabled (aria-disabled, still focusable) when there is no page that way. The parent owns the page: it passes `page` and updates it in
|
|
16
|
+
* `onPageChange`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Pagination({ page, pageSize, total, onPageChange, 'aria-label': label, ...rest }: PaginationProps): JSX.Element;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Icon } from "../icon/Icon.js";
|
|
2
|
+
import { formatCount } from "../../lib/format.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
// src/components/pagination/Pagination.tsx
|
|
5
|
+
const ARROW = "rt:inline-flex rt:size-7 rt:items-center rt:justify-center rt:rounded-md rt:bg-background rt:text-foreground-subdued rt:transition-colors rt:not-aria-disabled:cursor-pointer rt:not-aria-disabled:hover:bg-muted rt:focus-visible:focus-ring rt:aria-disabled:opacity-50";
|
|
6
|
+
/**
|
|
7
|
+
* One arrow. At the end of the list it stays focusable and is marked aria-disabled, so pressing Next onto the last page
|
|
8
|
+
* leaves focus on it rather than dropping it to the page; a press on it then does nothing.
|
|
9
|
+
*/
|
|
10
|
+
function Arrow({ label, icon, disabled, onPress }) {
|
|
11
|
+
return /* @__PURE__ */ jsx("button", {
|
|
12
|
+
type: "button",
|
|
13
|
+
"aria-label": label,
|
|
14
|
+
"aria-disabled": disabled ? true : void 0,
|
|
15
|
+
className: ARROW,
|
|
16
|
+
onClick: disabled ? void 0 : onPress,
|
|
17
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18
|
+
name: icon,
|
|
19
|
+
size: 8
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The footer under a table: Previous and Next arrows and the range on show, written "1 – 20 of 84 results". An arrow is
|
|
25
|
+
* dimmed to 50% and disabled (aria-disabled, still focusable) when there is no page that way. The parent owns the page: it passes `page` and updates it in
|
|
26
|
+
* `onPageChange`.
|
|
27
|
+
*/
|
|
28
|
+
function Pagination({ page, pageSize, total, onPageChange, "aria-label": label = "Pagination", ...rest }) {
|
|
29
|
+
const size = Math.max(1, pageSize);
|
|
30
|
+
const lastPage = Math.max(1, Math.ceil(total / size));
|
|
31
|
+
const current = Math.min(Math.max(1, page), lastPage);
|
|
32
|
+
const from = (current - 1) * size + 1;
|
|
33
|
+
const to = Math.min(current * size, total);
|
|
34
|
+
const summary = total === 0 ? "0 results" : `${formatCount(from)} – ${formatCount(to)} of ${formatCount(total)} results`;
|
|
35
|
+
return /* @__PURE__ */ jsxs("nav", {
|
|
36
|
+
"aria-label": label,
|
|
37
|
+
className: "rt:flex rt:h-13 rt:items-center rt:gap-2 rt:bg-background rt:py-3",
|
|
38
|
+
...rest,
|
|
39
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
40
|
+
className: "rt:flex rt:w-14 rt:shrink-0",
|
|
41
|
+
children: [/* @__PURE__ */ jsx(Arrow, {
|
|
42
|
+
label: "Previous page",
|
|
43
|
+
icon: "chevron-left",
|
|
44
|
+
disabled: current <= 1,
|
|
45
|
+
onPress: () => onPageChange(current - 1)
|
|
46
|
+
}), /* @__PURE__ */ jsx(Arrow, {
|
|
47
|
+
label: "Next page",
|
|
48
|
+
icon: "chevron-right",
|
|
49
|
+
disabled: current >= lastPage,
|
|
50
|
+
onPress: () => onPageChange(current + 1)
|
|
51
|
+
})]
|
|
52
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
53
|
+
role: "status",
|
|
54
|
+
className: "rt:min-w-26 rt:px-2 rt:text-c1 rt:whitespace-nowrap rt:text-foreground-subdued",
|
|
55
|
+
children: summary
|
|
56
|
+
})]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
export { Pagination };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Pagination } from "./Pagination.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ComponentProps } from "react";
|
|
2
|
+
import { Popover } from "@base-ui/react/popover";
|
|
3
|
+
/** The props of Popover: Base UI's root props, such as open, defaultOpen and onOpenChange. */
|
|
4
|
+
export type PopoverProps = Popover.Root.Props;
|
|
5
|
+
/** A panel that opens from a trigger and holds more than text, such as a list of notifications. Put a PopoverTrigger and a PopoverContent inside it. */
|
|
6
|
+
declare function Popover$1(props: PopoverProps): import("react").JSX.Element;
|
|
7
|
+
/** The props of PopoverTrigger: Base UI's trigger props, except className and style. Use render to put the trigger on your own element. */
|
|
8
|
+
export type PopoverTriggerProps = Omit<Popover.Trigger.Props, 'className' | 'style'>;
|
|
9
|
+
/** The element that opens the popover on click or Enter. It renders a button unless render says otherwise. */
|
|
10
|
+
export declare function PopoverTrigger(props: PopoverTriggerProps): import("react").JSX.Element;
|
|
11
|
+
/** The props of PopoverContent: the popup props plus where the popover sits. */
|
|
12
|
+
export type PopoverContentProps = Omit<Popover.Popup.Props, 'className' | 'style'> & Pick<Popover.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>;
|
|
13
|
+
/** The popover panel: a white panel with a 1px outline and no shadow, 288px wide. It opens below the trigger by default. */
|
|
14
|
+
export declare function PopoverContent({ align, alignOffset, side, sideOffset, ...props }: PopoverContentProps): import("react").JSX.Element;
|
|
15
|
+
/** The props of PopoverHeader: the attributes of a div, except className and style. */
|
|
16
|
+
export type PopoverHeaderProps = Omit<ComponentProps<'div'>, 'className' | 'style'>;
|
|
17
|
+
/** Groups a title and a description at the top of the panel. */
|
|
18
|
+
export declare function PopoverHeader(props: PopoverHeaderProps): import("react").JSX.Element;
|
|
19
|
+
/** The props of PopoverTitle: Base UI's title props. */
|
|
20
|
+
export type PopoverTitleProps = Omit<Popover.Title.Props, 'className' | 'style'>;
|
|
21
|
+
/** The title of the panel; it names the dialog for screen readers. */
|
|
22
|
+
export declare function PopoverTitle(props: PopoverTitleProps): import("react").JSX.Element;
|
|
23
|
+
/** The props of PopoverDescription: Base UI's description props. */
|
|
24
|
+
export type PopoverDescriptionProps = Omit<Popover.Description.Props, 'className' | 'style'>;
|
|
25
|
+
/** The supporting text under the title, in the muted colour. */
|
|
26
|
+
export declare function PopoverDescription(props: PopoverDescriptionProps): import("react").JSX.Element;
|
|
27
|
+
export { Popover$1 as Popover };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Popover } from "@base-ui/react/popover";
|
|
3
|
+
// src/components/popover/Popover.tsx
|
|
4
|
+
/** A panel that opens from a trigger and holds more than text, such as a list of notifications. Put a PopoverTrigger and a PopoverContent inside it. */
|
|
5
|
+
function Popover$1(props) {
|
|
6
|
+
return /* @__PURE__ */ jsx(Popover.Root, { ...props });
|
|
7
|
+
}
|
|
8
|
+
/** The element that opens the popover on click or Enter. It renders a button unless render says otherwise. */
|
|
9
|
+
function PopoverTrigger(props) {
|
|
10
|
+
return /* @__PURE__ */ jsx(Popover.Trigger, { ...props });
|
|
11
|
+
}
|
|
12
|
+
/** The popover panel: a white panel with a 1px outline and no shadow, 288px wide. It opens below the trigger by default. */
|
|
13
|
+
function PopoverContent({ align = "center", alignOffset = 0, side = "bottom", sideOffset = 4, ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
15
|
+
align,
|
|
16
|
+
alignOffset,
|
|
17
|
+
side,
|
|
18
|
+
sideOffset,
|
|
19
|
+
className: "rt:isolate rt:z-50",
|
|
20
|
+
children: /* @__PURE__ */ jsx(Popover.Popup, {
|
|
21
|
+
className: "rt:flex rt:w-72 rt:flex-col rt:gap-2 rt:rounded-lg rt:border rt:border-border rt:bg-popover rt:p-3 rt:text-b3 rt:text-popover-foreground rt:outline-hidden",
|
|
22
|
+
...props
|
|
23
|
+
})
|
|
24
|
+
}) });
|
|
25
|
+
}
|
|
26
|
+
/** Groups a title and a description at the top of the panel. */
|
|
27
|
+
function PopoverHeader(props) {
|
|
28
|
+
return /* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "rt:flex rt:flex-col rt:gap-0.5",
|
|
30
|
+
...props
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** The title of the panel; it names the dialog for screen readers. */
|
|
34
|
+
function PopoverTitle(props) {
|
|
35
|
+
return /* @__PURE__ */ jsx(Popover.Title, {
|
|
36
|
+
className: "rt:text-b4",
|
|
37
|
+
...props
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** The supporting text under the title, in the muted colour. */
|
|
41
|
+
function PopoverDescription(props) {
|
|
42
|
+
return /* @__PURE__ */ jsx(Popover.Description, {
|
|
43
|
+
className: "rt:text-muted-foreground",
|
|
44
|
+
...props
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export { Popover$1 as Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Popover, PopoverContent, PopoverContentProps, PopoverDescription, PopoverDescriptionProps, PopoverHeader, PopoverHeaderProps, PopoverProps, PopoverTitle, PopoverTitleProps, PopoverTrigger, PopoverTriggerProps } from "./Popover.js";
|
|
2
|
+
export { Popover, PopoverContent, type PopoverContentProps, PopoverDescription, type PopoverDescriptionProps, PopoverHeader, type PopoverHeaderProps, type PopoverProps, PopoverTitle, type PopoverTitleProps, PopoverTrigger, type PopoverTriggerProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from "./Popover.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { InputGroupSize } from "../input-group/InputGroup.js";
|
|
2
|
+
import "../input-group/index.js";
|
|
3
|
+
import { InputHTMLAttributes, JSX, Ref } from "react";
|
|
4
|
+
/** The props of SearchField: the HTML attributes of an input, except className, style, size, type and onChange. */
|
|
5
|
+
export type SearchFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'style' | 'size' | 'type' | 'onChange' | 'value' | 'defaultValue'> & {
|
|
6
|
+
/** The accessible name of the field, such as "Search products". Required because the field has no visible label. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** The text in the field. Pass it with onValueChange to control the field. */
|
|
9
|
+
value?: string;
|
|
10
|
+
/** The starting text of an uncontrolled field. */
|
|
11
|
+
defaultValue?: string;
|
|
12
|
+
/** Called with the new text on every change and when the field is cleared. It is not debounced: debounce in the app. */
|
|
13
|
+
onValueChange?: (value: string) => void;
|
|
14
|
+
/** The height in px: 36 above a listing, 32 inside a table. Defaults to 36. */
|
|
15
|
+
size?: InputGroupSize;
|
|
16
|
+
/** Receives the input element. */
|
|
17
|
+
ref?: Ref<HTMLInputElement>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The search field above a listing: a search icon, the input and a clear button that appears once there is text.
|
|
21
|
+
* Escape clears it too. The field reports every change; the debounce stays in the app.
|
|
22
|
+
*/
|
|
23
|
+
export declare function SearchField({ label, value, defaultValue, onValueChange, size, ref, onKeyDown, disabled, readOnly, ...rest }: SearchFieldProps): JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Icon } from "../icon/Icon.js";
|
|
2
|
+
import { InputGroup, InputGroupAddon, InputGroupInput } from "../input-group/InputGroup.js";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/components/search-field/SearchField.tsx
|
|
6
|
+
/**
|
|
7
|
+
* The search field above a listing: a search icon, the input and a clear button that appears once there is text.
|
|
8
|
+
* Escape clears it too. The field reports every change; the debounce stays in the app.
|
|
9
|
+
*/
|
|
10
|
+
function SearchField({ label, value, defaultValue = "", onValueChange, size = 36, ref, onKeyDown, disabled, readOnly, ...rest }) {
|
|
11
|
+
const [inner, setInner] = useState(defaultValue);
|
|
12
|
+
const current = value ?? inner;
|
|
13
|
+
const inputRef = useRef(null);
|
|
14
|
+
const change = (next) => {
|
|
15
|
+
if (value === void 0) setInner(next);
|
|
16
|
+
onValueChange?.(next);
|
|
17
|
+
};
|
|
18
|
+
const clear = () => {
|
|
19
|
+
change("");
|
|
20
|
+
inputRef.current?.focus();
|
|
21
|
+
};
|
|
22
|
+
const handleKeyDown = (event) => {
|
|
23
|
+
onKeyDown?.(event);
|
|
24
|
+
if (event.key === "Escape" && current !== "" && readOnly !== true && !event.defaultPrevented) {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
change("");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return /* @__PURE__ */ jsxs(InputGroup, {
|
|
30
|
+
size,
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, {
|
|
33
|
+
name: "search",
|
|
34
|
+
tone: "icon"
|
|
35
|
+
}) }),
|
|
36
|
+
/* @__PURE__ */ jsx(InputGroupInput, {
|
|
37
|
+
...rest,
|
|
38
|
+
ref: (node) => {
|
|
39
|
+
inputRef.current = node;
|
|
40
|
+
if (typeof ref === "function") ref(node);
|
|
41
|
+
else if (ref) ref.current = node;
|
|
42
|
+
},
|
|
43
|
+
disabled,
|
|
44
|
+
readOnly,
|
|
45
|
+
role: "searchbox",
|
|
46
|
+
inputMode: "search",
|
|
47
|
+
autoComplete: "off",
|
|
48
|
+
"aria-label": label,
|
|
49
|
+
value: current,
|
|
50
|
+
onChange: (event) => change(event.target.value),
|
|
51
|
+
onKeyDown: handleKeyDown
|
|
52
|
+
}),
|
|
53
|
+
current !== "" && disabled !== true && readOnly !== true ? /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
54
|
+
align: "end",
|
|
55
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
"aria-label": "Clear search",
|
|
58
|
+
onClick: clear,
|
|
59
|
+
className: "rt:inline-flex rt:size-5 rt:items-center rt:justify-center rt:rounded-sm rt:text-icon rt:outline-none rt:transition-colors rt:hover:bg-muted rt:focus-visible:shadow-ring-focus",
|
|
60
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "x" })
|
|
61
|
+
})
|
|
62
|
+
}) : null
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export { SearchField };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SearchField } from "./SearchField.js";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { IconName } from "../icon/registry.js";
|
|
2
|
+
import "../icon/index.js";
|
|
3
|
+
import { JSX, ReactNode } from "react";
|
|
4
|
+
/** One row of the navigation. With `children` it is a group that opens and closes. */
|
|
5
|
+
export type SideNavItem = {
|
|
6
|
+
/** The value passed to onSelect and compared with `active`. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** The text of the row. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** A glyph before the label. */
|
|
11
|
+
icon?: IconName;
|
|
12
|
+
/** A count shown at the end of the row, such as 12 or 10,001+. */
|
|
13
|
+
count?: string | number;
|
|
14
|
+
/** Makes the row a link to this address instead of a button. On a router, LinkProvider draws it with the app's own link. */
|
|
15
|
+
href?: string;
|
|
16
|
+
/** Nested rows. The row then opens and closes them. */
|
|
17
|
+
children?: SideNavItem[];
|
|
18
|
+
};
|
|
19
|
+
/** A block of rows, with an optional label that can collapse the block. */
|
|
20
|
+
export type SideNavSection = {
|
|
21
|
+
/** Identifies the section. */
|
|
22
|
+
id: string;
|
|
23
|
+
/** The small label above the rows. Without one the rows sit unlabelled. */
|
|
24
|
+
label?: string;
|
|
25
|
+
/** Whether the label collapses the section. Needs a label. */
|
|
26
|
+
collapsible?: boolean;
|
|
27
|
+
/** Whether a collapsible section starts closed. Defaults to false. */
|
|
28
|
+
defaultCollapsed?: boolean;
|
|
29
|
+
/** The rows. */
|
|
30
|
+
items: SideNavItem[];
|
|
31
|
+
};
|
|
32
|
+
/** The account switcher at the top. */
|
|
33
|
+
export type SideNavAccount = {
|
|
34
|
+
/** The store or person name. */
|
|
35
|
+
name: string;
|
|
36
|
+
/** The line under the name. */
|
|
37
|
+
role?: string;
|
|
38
|
+
/** Called when the switcher is pressed. */
|
|
39
|
+
onClick?: () => void;
|
|
40
|
+
};
|
|
41
|
+
/** The props of SideNav. */
|
|
42
|
+
export type SideNavProps = {
|
|
43
|
+
/** The accessible name of the navigation landmark. */
|
|
44
|
+
label: string;
|
|
45
|
+
/** The blocks of rows, top to bottom. */
|
|
46
|
+
sections: SideNavSection[];
|
|
47
|
+
/** The id of the current row. It is marked and its group opens. */
|
|
48
|
+
active?: string;
|
|
49
|
+
/** Called with the id of the row pressed. */
|
|
50
|
+
onSelect?: (id: string) => void;
|
|
51
|
+
/** The account switcher shown above the rows. */
|
|
52
|
+
account?: SideNavAccount;
|
|
53
|
+
/** Content pinned at the bottom, such as Settings. */
|
|
54
|
+
footer?: ReactNode;
|
|
55
|
+
/** On a phone, whether the drawer is open. */
|
|
56
|
+
open?: boolean;
|
|
57
|
+
/** On a phone, called when the drawer asks to open or close. */
|
|
58
|
+
onOpenChange?: (open: boolean) => void;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The 240px side navigation of the admin: an account switcher, blocks of rows with nested groups, counts and collapsible
|
|
62
|
+
* labels, and a footer pinned at the bottom. On a phone it becomes a drawer: inside AppShell the menu button in TopBar opens it and choosing a row closes it; used alone, the caller opens it with `open` and closes it in `onOpenChange`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function SideNav(props: SideNavProps): JSX.Element | null;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { ShellNavContext } from "../app-shell/context.js";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { Avatar } from "../avatar/Avatar.js";
|
|
4
|
+
import { Icon } from "../icon/Icon.js";
|
|
5
|
+
import { RatioLink } from "../link-provider/RatioLink.js";
|
|
6
|
+
import { use, useState, useSyncExternalStore } from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
9
|
+
// src/components/side-nav/SideNav.tsx
|
|
10
|
+
const PHONE_QUERY = "(max-width: 767px)";
|
|
11
|
+
function subscribe(notify) {
|
|
12
|
+
const mq = window.matchMedia(PHONE_QUERY);
|
|
13
|
+
mq.addEventListener("change", notify);
|
|
14
|
+
return () => mq.removeEventListener("change", notify);
|
|
15
|
+
}
|
|
16
|
+
function useIsPhone() {
|
|
17
|
+
return useSyncExternalStore(subscribe, () => window.matchMedia(PHONE_QUERY).matches, () => false);
|
|
18
|
+
}
|
|
19
|
+
const ROW = "rt:flex rt:h-sidenav-row rt:w-full rt:items-center rt:gap-2 rt:rounded-md rt:text-left rt:text-b3 rt:text-foreground rt:transition-colors rt:hover:bg-muted rt:focus-visible:focus-ring";
|
|
20
|
+
function containsActive(item, active) {
|
|
21
|
+
return item.children?.some((c) => c.id === active || containsActive(c, active)) ?? false;
|
|
22
|
+
}
|
|
23
|
+
function Row({ item, active, depth, onSelect }) {
|
|
24
|
+
const isActive = item.id === active;
|
|
25
|
+
const [toggled, setToggled] = useState(null);
|
|
26
|
+
const children = item.children ?? [];
|
|
27
|
+
const hasChildren = children.length > 0;
|
|
28
|
+
const open = toggled && toggled.forActive === active ? toggled.open : containsActive(item, active);
|
|
29
|
+
const cls = cn(ROW, depth > 0 && "rt:pl-8", isActive && "rt:bg-muted rt:text-primary rt:font-medium");
|
|
30
|
+
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
31
|
+
item.icon && /* @__PURE__ */ jsx(Icon, {
|
|
32
|
+
name: item.icon,
|
|
33
|
+
size: 16,
|
|
34
|
+
tone: isActive ? "primary" : "icon"
|
|
35
|
+
}),
|
|
36
|
+
/* @__PURE__ */ jsx("span", {
|
|
37
|
+
className: "rt:min-w-0 rt:flex-1 rt:truncate",
|
|
38
|
+
children: item.label
|
|
39
|
+
}),
|
|
40
|
+
item.count !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
41
|
+
className: "rt:text-c1 rt:text-foreground-subdued",
|
|
42
|
+
children: item.count
|
|
43
|
+
}),
|
|
44
|
+
hasChildren && /* @__PURE__ */ jsx(Icon, {
|
|
45
|
+
name: "nav-chevron",
|
|
46
|
+
size: 8,
|
|
47
|
+
tone: "icon"
|
|
48
|
+
})
|
|
49
|
+
] });
|
|
50
|
+
return /* @__PURE__ */ jsxs("li", { children: [hasChildren ? /* @__PURE__ */ jsx("button", {
|
|
51
|
+
type: "button",
|
|
52
|
+
className: cls,
|
|
53
|
+
"aria-expanded": open,
|
|
54
|
+
onClick: () => setToggled({
|
|
55
|
+
open: !open,
|
|
56
|
+
forActive: active
|
|
57
|
+
}),
|
|
58
|
+
children: inner
|
|
59
|
+
}) : item.href ? /* @__PURE__ */ jsx(RatioLink, {
|
|
60
|
+
href: item.href,
|
|
61
|
+
className: cls,
|
|
62
|
+
"aria-current": isActive ? "page" : void 0,
|
|
63
|
+
onClick: () => onSelect?.(item.id),
|
|
64
|
+
children: inner
|
|
65
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
66
|
+
type: "button",
|
|
67
|
+
className: cls,
|
|
68
|
+
"aria-current": isActive ? "page" : void 0,
|
|
69
|
+
onClick: () => onSelect?.(item.id),
|
|
70
|
+
children: inner
|
|
71
|
+
}), hasChildren && open && /* @__PURE__ */ jsx("ul", {
|
|
72
|
+
className: "rt:flex rt:flex-col",
|
|
73
|
+
children: children.map((c) => /* @__PURE__ */ jsx(Row, {
|
|
74
|
+
item: c,
|
|
75
|
+
active,
|
|
76
|
+
depth: depth + 1,
|
|
77
|
+
onSelect
|
|
78
|
+
}, c.id))
|
|
79
|
+
})] });
|
|
80
|
+
}
|
|
81
|
+
function Section({ section, active, onSelect }) {
|
|
82
|
+
const [collapsed, setCollapsed] = useState(!!section.defaultCollapsed);
|
|
83
|
+
const collapsible = !!section.collapsible && !!section.label;
|
|
84
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
85
|
+
className: "rt:flex rt:flex-col",
|
|
86
|
+
children: [section.label && (collapsible ? /* @__PURE__ */ jsxs("button", {
|
|
87
|
+
type: "button",
|
|
88
|
+
"aria-expanded": !collapsed,
|
|
89
|
+
onClick: () => setCollapsed((c) => !c),
|
|
90
|
+
className: "rt:flex rt:h-sidenav-label rt:items-center rt:justify-between rt:text-c1 rt:text-foreground-subdued rt:focus-visible:focus-ring",
|
|
91
|
+
children: [section.label, /* @__PURE__ */ jsx(Icon, {
|
|
92
|
+
name: "nav-chevron",
|
|
93
|
+
size: 8,
|
|
94
|
+
tone: "icon"
|
|
95
|
+
})]
|
|
96
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
97
|
+
className: "rt:flex rt:h-sidenav-label rt:items-center rt:text-c1 rt:text-foreground-subdued",
|
|
98
|
+
children: section.label
|
|
99
|
+
})), !(collapsible && collapsed) && /* @__PURE__ */ jsx("ul", {
|
|
100
|
+
className: "rt:flex rt:flex-col",
|
|
101
|
+
children: section.items.map((item) => /* @__PURE__ */ jsx(Row, {
|
|
102
|
+
item,
|
|
103
|
+
active,
|
|
104
|
+
depth: 0,
|
|
105
|
+
onSelect
|
|
106
|
+
}, item.id))
|
|
107
|
+
})]
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function Panel({ label, sections, active, onSelect, account, footer }) {
|
|
111
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
112
|
+
"data-slot": "side-nav",
|
|
113
|
+
className: "rt:flex rt:h-full rt:w-60 rt:shrink-0 rt:flex-col rt:bg-sidebar rt:text-sidebar-foreground",
|
|
114
|
+
children: [
|
|
115
|
+
account && /* @__PURE__ */ jsx("div", {
|
|
116
|
+
className: "rt:flex rt:shrink-0 rt:items-start rt:px-3.5 rt:pt-2",
|
|
117
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
118
|
+
type: "button",
|
|
119
|
+
onClick: account.onClick,
|
|
120
|
+
className: "rt:flex rt:h-11 rt:w-full rt:items-center rt:gap-2 rt:rounded-lg rt:px-1.5 rt:text-left rt:hover:bg-muted rt:focus-visible:focus-ring",
|
|
121
|
+
children: [
|
|
122
|
+
/* @__PURE__ */ jsx(Avatar, {
|
|
123
|
+
name: account.name,
|
|
124
|
+
shape: "square",
|
|
125
|
+
size: "sm"
|
|
126
|
+
}),
|
|
127
|
+
/* @__PURE__ */ jsxs("span", {
|
|
128
|
+
className: "rt:flex rt:min-w-0 rt:flex-1 rt:flex-col rt:text-c1",
|
|
129
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
130
|
+
className: "rt:truncate rt:text-c2",
|
|
131
|
+
children: account.name
|
|
132
|
+
}), account.role && /* @__PURE__ */ jsx("span", {
|
|
133
|
+
className: "rt:truncate",
|
|
134
|
+
children: account.role
|
|
135
|
+
})]
|
|
136
|
+
}),
|
|
137
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
138
|
+
name: "nav-caret-down",
|
|
139
|
+
size: 8,
|
|
140
|
+
tone: "icon"
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
})
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ jsx("nav", {
|
|
146
|
+
"aria-label": label,
|
|
147
|
+
className: "rt:flex rt:min-h-0 rt:flex-1 rt:flex-col rt:sidenav-gap rt:overflow-y-auto rt:sidenav-pad-x rt:sidenav-pad-top rt:sidenav-pad-bottom",
|
|
148
|
+
children: sections.map((s) => /* @__PURE__ */ jsx(Section, {
|
|
149
|
+
section: s,
|
|
150
|
+
active,
|
|
151
|
+
onSelect
|
|
152
|
+
}, s.id))
|
|
153
|
+
}),
|
|
154
|
+
footer && /* @__PURE__ */ jsx("div", {
|
|
155
|
+
"data-slot": "side-nav-footer",
|
|
156
|
+
className: "rt:flex rt:shrink-0 rt:items-center rt:gap-2 rt:border-t rt:border-divider rt:px-6 rt:py-3 rt:text-b3",
|
|
157
|
+
children: footer
|
|
158
|
+
})
|
|
159
|
+
]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The 240px side navigation of the admin: an account switcher, blocks of rows with nested groups, counts and collapsible
|
|
164
|
+
* labels, and a footer pinned at the bottom. On a phone it becomes a drawer: inside AppShell the menu button in TopBar opens it and choosing a row closes it; used alone, the caller opens it with `open` and closes it in `onOpenChange`.
|
|
165
|
+
*/
|
|
166
|
+
function SideNav(props) {
|
|
167
|
+
const phone = useIsPhone();
|
|
168
|
+
const shell = use(ShellNavContext);
|
|
169
|
+
if (!phone) return /* @__PURE__ */ jsx(Panel, { ...props });
|
|
170
|
+
const open = props.open ?? shell?.open ?? false;
|
|
171
|
+
const onOpenChange = (next) => {
|
|
172
|
+
props.onOpenChange?.(next);
|
|
173
|
+
shell?.setOpen(next);
|
|
174
|
+
};
|
|
175
|
+
const onSelect = (id) => {
|
|
176
|
+
props.onSelect?.(id);
|
|
177
|
+
shell?.setOpen(false);
|
|
178
|
+
};
|
|
179
|
+
return /* @__PURE__ */ jsx(Dialog.Root, {
|
|
180
|
+
open,
|
|
181
|
+
onOpenChange,
|
|
182
|
+
children: /* @__PURE__ */ jsx(Dialog.Portal, { children: /* @__PURE__ */ jsx(Dialog.Popup, {
|
|
183
|
+
id: shell?.id,
|
|
184
|
+
"aria-label": props.label,
|
|
185
|
+
"data-slot": "side-nav-drawer",
|
|
186
|
+
className: "rt:fixed rt:inset-y-0 rt:left-0 rt:z-50 rt:h-full rt:shadow-ring-strong rt:focus-visible:focus-ring",
|
|
187
|
+
children: /* @__PURE__ */ jsx(Panel, {
|
|
188
|
+
...props,
|
|
189
|
+
onSelect
|
|
190
|
+
})
|
|
191
|
+
}) })
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
export { SideNav };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SideNav } from "./SideNav.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HTMLAttributes, JSX } from "react";
|
|
2
|
+
/** The shape of a placeholder: a line of text, a block such as a tile or an image, or a round avatar. */
|
|
3
|
+
export type SkeletonShape = 'line' | 'block' | 'circle';
|
|
4
|
+
/** The props of Skeleton: the HTML attributes of its div, except className, style and children, with the shape. */
|
|
5
|
+
export type SkeletonProps = Omit<HTMLAttributes<HTMLDivElement>, 'className' | 'style' | 'children'> & {
|
|
6
|
+
/** The shape of the placeholder. Defaults to line. A line and a block fill the width of their parent. */
|
|
7
|
+
shape?: SkeletonShape;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A static placeholder for content that is still loading on a first load. It is hidden from assistive technology: the
|
|
11
|
+
* container that holds it says it is busy. Its fill and animation come from the loading tokens, so it does not move.
|
|
12
|
+
*/
|
|
13
|
+
export declare function Skeleton({ shape, ...rest }: SkeletonProps): JSX.Element;
|