@ratio-ui/core 0.1.1 → 0.3.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/README.md +2 -0
- package/dist/catalogue.json +2829 -72
- package/dist/components/alert/Alert.d.ts +18 -0
- package/dist/components/alert/Alert.js +67 -0
- package/dist/components/alert/index.d.ts +2 -0
- package/dist/components/alert/index.js +1 -0
- package/dist/components/alert-dialog/AlertDialog.d.ts +48 -0
- package/dist/components/alert-dialog/AlertDialog.js +76 -0
- package/dist/components/alert-dialog/index.d.ts +2 -0
- package/dist/components/alert-dialog/index.js +1 -0
- 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/calendar/Calendar.d.ts +20 -0
- package/dist/components/calendar/Calendar.js +119 -0
- package/dist/components/calendar/index.d.ts +2 -0
- package/dist/components/calendar/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/checkbox-list/CheckboxList.d.ts +36 -0
- package/dist/components/checkbox-list/CheckboxList.js +93 -0
- package/dist/components/checkbox-list/index.d.ts +2 -0
- package/dist/components/checkbox-list/index.js +1 -0
- package/dist/components/collapsible/Collapsible.d.ts +24 -0
- package/dist/components/collapsible/Collapsible.js +58 -0
- package/dist/components/collapsible/index.d.ts +2 -0
- package/dist/components/collapsible/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/date-picker/DatePicker.d.ts +33 -0
- package/dist/components/date-picker/DatePicker.js +104 -0
- package/dist/components/date-picker/index.d.ts +2 -0
- package/dist/components/date-picker/index.js +1 -0
- package/dist/components/date-range-picker/DateRangePicker.d.ts +39 -0
- package/dist/components/date-range-picker/DateRangePicker.js +142 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/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/field/Field.d.ts +35 -0
- package/dist/components/field/Field.js +67 -0
- package/dist/components/field/Label.d.ts +8 -0
- package/dist/components/field/Label.js +13 -0
- package/dist/components/field/index.d.ts +3 -0
- package/dist/components/field/index.js +3 -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 +57 -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/Link.d.ts +22 -0
- package/dist/components/link/Link.js +31 -0
- package/dist/components/link/index.d.ts +2 -0
- package/dist/components/link/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/media-grid/MediaGrid.d.ts +49 -0
- package/dist/components/media-grid/MediaGrid.js +303 -0
- package/dist/components/media-grid/index.d.ts +2 -0
- package/dist/components/media-grid/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/money-input/MoneyInput.d.ts +30 -0
- package/dist/components/money-input/MoneyInput.js +72 -0
- package/dist/components/money-input/index.d.ts +2 -0
- package/dist/components/money-input/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/save-bar/SaveBar.d.ts +27 -0
- package/dist/components/save-bar/SaveBar.js +37 -0
- package/dist/components/save-bar/index.d.ts +2 -0
- package/dist/components/save-bar/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/select/Select.d.ts +40 -0
- package/dist/components/select/Select.js +84 -0
- package/dist/components/select/index.d.ts +2 -0
- package/dist/components/select/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/summary-chips/SummaryChips.d.ts +17 -0
- package/dist/components/summary-chips/SummaryChips.js +19 -0
- package/dist/components/summary-chips/index.d.ts +2 -0
- package/dist/components/summary-chips/index.js +1 -0
- package/dist/components/switch/Switch.d.ts +10 -0
- package/dist/components/switch/Switch.js +20 -0
- package/dist/components/switch/index.d.ts +2 -0
- package/dist/components/switch/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/tag-input/TagInput.d.ts +44 -0
- package/dist/components/tag-input/TagInput.js +102 -0
- package/dist/components/tag-input/index.d.ts +2 -0
- package/dist/components/tag-input/index.js +1 -0
- package/dist/components/textarea/Textarea.d.ts +15 -0
- package/dist/components/textarea/Textarea.js +21 -0
- package/dist/components/textarea/index.d.ts +2 -0
- package/dist/components/textarea/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/components/unit-input/UnitInput.d.ts +33 -0
- package/dist/components/unit-input/UnitInput.js +47 -0
- package/dist/components/unit-input/index.d.ts +2 -0
- package/dist/components/unit-input/index.js +1 -0
- package/dist/index.d.ts +102 -1
- package/dist/index.js +54 -2
- package/dist/lib/cn.js +38 -27
- package/dist/lib/format.d.ts +92 -0
- package/dist/lib/format.js +222 -0
- package/dist/styles.css +1 -1
- package/package.json +205 -3
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentProps, JSX } from "react";
|
|
2
|
+
import { Tabs } from "@base-ui/react/tabs";
|
|
3
|
+
/** The props of Tabs: Base UI's tabs root without className and style. */
|
|
4
|
+
export type TabsProps = Omit<ComponentProps<typeof Tabs.Root>, 'className' | 'style'>;
|
|
5
|
+
/** The props of TabsList: Base UI's tab list without className and style. */
|
|
6
|
+
export type TabsListProps = Omit<ComponentProps<typeof Tabs.List>, 'className' | 'style'>;
|
|
7
|
+
/** The props of TabsTrigger: Base UI's tab without className and style. Its value ties it to the TabsContent with the same value. */
|
|
8
|
+
export type TabsTriggerProps = Omit<ComponentProps<typeof Tabs.Tab>, 'className' | 'style'>;
|
|
9
|
+
/** The props of TabsContent: Base UI's tab panel without className and style. Its value ties it to the TabsTrigger with the same value. */
|
|
10
|
+
export type TabsContentProps = Omit<ComponentProps<typeof Tabs.Panel>, 'className' | 'style'>;
|
|
11
|
+
/**
|
|
12
|
+
* Sections of one page, switched by an underlined row of tabs: ABC analysis, the sections of a settings page. Holds the
|
|
13
|
+
* selected value, uncontrolled with defaultValue or controlled with value and onValueChange. To filter a list use
|
|
14
|
+
* FilterTabs, which looks like the design system's chip tab.
|
|
15
|
+
*/
|
|
16
|
+
declare function Tabs$1(props: TabsProps): JSX.Element;
|
|
17
|
+
/** The row of tabs, with a hairline under it. Give it an aria-label that names the sections. */
|
|
18
|
+
export declare function TabsList(props: TabsListProps): JSX.Element;
|
|
19
|
+
/** One tab: 36px tall, with a 2px blue underline and a blue semibold label when selected. A disabled tab is at 50%. */
|
|
20
|
+
export declare function TabsTrigger(props: TabsTriggerProps): JSX.Element;
|
|
21
|
+
/** The panel of the tab with the same value; only the selected one is shown. */
|
|
22
|
+
export declare function TabsContent(props: TabsContentProps): JSX.Element;
|
|
23
|
+
export { Tabs$1 as Tabs };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Tabs } from "@base-ui/react/tabs";
|
|
4
|
+
// src/components/tabs/Tabs.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Sections of one page, switched by an underlined row of tabs: ABC analysis, the sections of a settings page. Holds the
|
|
7
|
+
* selected value, uncontrolled with defaultValue or controlled with value and onValueChange. To filter a list use
|
|
8
|
+
* FilterTabs, which looks like the design system's chip tab.
|
|
9
|
+
*/
|
|
10
|
+
function Tabs$1(props) {
|
|
11
|
+
return /* @__PURE__ */ jsx(Tabs.Root, {
|
|
12
|
+
"data-slot": "tabs",
|
|
13
|
+
className: cn("rt:flex rt:flex-col rt:gap-3"),
|
|
14
|
+
...props
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** The row of tabs, with a hairline under it. Give it an aria-label that names the sections. */
|
|
18
|
+
function TabsList(props) {
|
|
19
|
+
return /* @__PURE__ */ jsx(Tabs.List, {
|
|
20
|
+
"data-slot": "tabs-list",
|
|
21
|
+
className: cn("rt:flex rt:items-center rt:gap-6 rt:border-b rt:border-border"),
|
|
22
|
+
...props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** One tab: 36px tall, with a 2px blue underline and a blue semibold label when selected. A disabled tab is at 50%. */
|
|
26
|
+
function TabsTrigger(props) {
|
|
27
|
+
return /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
28
|
+
"data-slot": "tabs-trigger",
|
|
29
|
+
className: cn("rt:relative rt:inline-flex rt:h-9 rt:cursor-pointer rt:items-center rt:justify-center rt:gap-2 rt:whitespace-nowrap", "rt:text-b3 rt:text-tab-inactive rt:transition-colors rt:hover:text-foreground", "rt:data-active:font-semibold rt:data-active:text-primary", "rt:focus-visible:focus-ring rt:data-disabled:cursor-default rt:data-disabled:opacity-50", "rt:after:absolute rt:after:inset-x-0 rt:after:-bottom-px rt:after:h-0.5 rt:after:bg-primary rt:after:opacity-0", "rt:data-active:after:opacity-100 rt:svg:text-icon"),
|
|
30
|
+
...props
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** The panel of the tab with the same value; only the selected one is shown. */
|
|
34
|
+
function TabsContent(props) {
|
|
35
|
+
return /* @__PURE__ */ jsx(Tabs.Panel, {
|
|
36
|
+
"data-slot": "tabs-content",
|
|
37
|
+
className: cn("rt:flex-1 rt:text-b3 rt:focus-visible:focus-ring"),
|
|
38
|
+
...props
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export { Tabs$1 as Tabs, TabsContent, TabsList, TabsTrigger };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Tabs, TabsContent, TabsList, TabsTrigger } from "./Tabs.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JSX, Ref } from "react";
|
|
2
|
+
/** The props of TagInput. Field's wiring (id, aria-describedby, aria-invalid, aria-required, disabled) lands on the picker. */
|
|
3
|
+
export type TagInputProps = {
|
|
4
|
+
/** The existing values the picker offers, such as S, M, L, XL and XXL. */
|
|
5
|
+
options: readonly string[];
|
|
6
|
+
/** The chosen values, in order, when the owner holds the state. */
|
|
7
|
+
value?: readonly string[];
|
|
8
|
+
/** The values chosen at first, when the component holds the state. */
|
|
9
|
+
defaultValue?: readonly string[];
|
|
10
|
+
/** Called with every new list of values: after a pick, an added value or a removal. */
|
|
11
|
+
onValueChange?: (value: string[]) => void;
|
|
12
|
+
/** Shows a small field that adds a typed value on Enter. Defaults to true. */
|
|
13
|
+
creatable?: boolean;
|
|
14
|
+
/** The placeholder and accessible name of the small field. Defaults to "Add value". */
|
|
15
|
+
addLabel?: string;
|
|
16
|
+
/** The placeholder of the picker, shown while nothing is chosen. */
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
/** The line the list shows when no value matches the typed text. Defaults to "No matching values". */
|
|
19
|
+
emptyText?: string;
|
|
20
|
+
/** The form field name: one hidden input per chosen value is submitted under it. */
|
|
21
|
+
name?: string;
|
|
22
|
+
/** Dims the component and stops every change. */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/** The id of the picker's input, which a label points to. */
|
|
25
|
+
id?: string;
|
|
26
|
+
/** The accessible name of the picker, when no label points to it. */
|
|
27
|
+
'aria-label'?: string;
|
|
28
|
+
/** The id of the element that names the picker. */
|
|
29
|
+
'aria-labelledby'?: string;
|
|
30
|
+
/** The ids of the hint or error that describe the component. */
|
|
31
|
+
'aria-describedby'?: string;
|
|
32
|
+
/** Marks the values as invalid, which draws the error ring. */
|
|
33
|
+
'aria-invalid'?: boolean | 'true' | 'false';
|
|
34
|
+
/** Marks the values as required. */
|
|
35
|
+
'aria-required'?: boolean | 'true' | 'false';
|
|
36
|
+
/** Receives the picker's input element. */
|
|
37
|
+
ref?: Ref<HTMLInputElement>;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Several values as chips in one field in the input's look. The picker offers the existing values (typing filters them),
|
|
41
|
+
* a small field below adds a new one on Enter, and a chip goes with its remove button or with Backspace in the empty
|
|
42
|
+
* picker. Base UI's combobox cannot accept a value it does not list, which is why new values have their own field.
|
|
43
|
+
*/
|
|
44
|
+
export declare function TagInput({ options, value, defaultValue, onValueChange, creatable, addLabel, placeholder, emptyText, name, disabled, id, ref, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired }: TagInputProps): JSX.Element;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { Icon } from "../icon/Icon.js";
|
|
3
|
+
import { Input } from "../input/Input.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useRef, useState } from "react";
|
|
6
|
+
import { Combobox } from "@base-ui/react/combobox";
|
|
7
|
+
// src/components/tag-input/TagInput.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Several values as chips in one field in the input's look. The picker offers the existing values (typing filters them),
|
|
10
|
+
* a small field below adds a new one on Enter, and a chip goes with its remove button or with Backspace in the empty
|
|
11
|
+
* picker. Base UI's combobox cannot accept a value it does not list, which is why new values have their own field.
|
|
12
|
+
*/
|
|
13
|
+
function TagInput({ options, value, defaultValue, onValueChange, creatable = true, addLabel = "Add value", placeholder, emptyText = "No matching values", name, disabled, id, ref, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired }) {
|
|
14
|
+
const [own, setOwn] = useState(defaultValue ?? []);
|
|
15
|
+
const [draft, setDraft] = useState("");
|
|
16
|
+
const anchorRef = useRef(null);
|
|
17
|
+
const current = value ?? own;
|
|
18
|
+
const items = [...options, ...current.filter((item) => !options.includes(item))];
|
|
19
|
+
const change = (next) => {
|
|
20
|
+
if (value === void 0) setOwn(next);
|
|
21
|
+
onValueChange?.(next);
|
|
22
|
+
};
|
|
23
|
+
const add = (event) => {
|
|
24
|
+
if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
const text = draft.trim();
|
|
27
|
+
setDraft("");
|
|
28
|
+
const same = (item) => item.toLowerCase() === text.toLowerCase();
|
|
29
|
+
if (text === "" || current.some(same)) return;
|
|
30
|
+
change([...current, options.find(same) ?? text]);
|
|
31
|
+
};
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
33
|
+
className: "rt:flex rt:w-full rt:flex-col rt:gap-1.5",
|
|
34
|
+
children: [/* @__PURE__ */ jsxs(Combobox.Root, {
|
|
35
|
+
multiple: true,
|
|
36
|
+
items,
|
|
37
|
+
value: [...current],
|
|
38
|
+
onValueChange: change,
|
|
39
|
+
disabled,
|
|
40
|
+
name,
|
|
41
|
+
children: [/* @__PURE__ */ jsxs(Combobox.Chips, {
|
|
42
|
+
ref: anchorRef,
|
|
43
|
+
"data-slot": "tag-input-chips",
|
|
44
|
+
className: cn("rt:flex rt:min-h-9 rt:w-full rt:min-w-0 rt:flex-wrap rt:items-center rt:gap-1 rt:rounded-lg rt:bg-background rt:py-2 rt:pr-3 rt:shadow-ring-subtle rt:transition-colors rt:forced-outline rt:focus-within:shadow-ring-focus rt:has-aria-invalid:shadow-ring-invalid rt:has-aria-invalid:focus-within:shadow-ring-invalid rt:has-disabled:opacity-50", current.length > 0 ? "rt:pl-2" : "rt:pl-3"),
|
|
45
|
+
children: [current.map((item) => /* @__PURE__ */ jsxs(Combobox.Chip, {
|
|
46
|
+
className: "rt:inline-flex rt:h-5 rt:items-center rt:gap-1 rt:rounded-sm rt:border rt:border-status-neutral-border rt:bg-status-neutral-background rt:pr-0.5 rt:pl-2 rt:text-c1 rt:whitespace-nowrap rt:text-status-neutral-foreground rt:outline-none rt:focus-visible:focus-ring",
|
|
47
|
+
children: [item, /* @__PURE__ */ jsx(Combobox.ChipRemove, {
|
|
48
|
+
"aria-label": `Remove ${item}`,
|
|
49
|
+
className: "rt:flex rt:size-4 rt:items-center rt:justify-center rt:rounded-sm rt:text-muted-foreground rt:transition-colors rt:hover:text-foreground rt:focus-visible:focus-ring rt:disabled:pointer-events-none",
|
|
50
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "x" })
|
|
51
|
+
})]
|
|
52
|
+
}, item)), /* @__PURE__ */ jsx(Combobox.Input, {
|
|
53
|
+
ref,
|
|
54
|
+
id,
|
|
55
|
+
"aria-label": ariaLabel,
|
|
56
|
+
"aria-labelledby": ariaLabelledBy,
|
|
57
|
+
"aria-describedby": ariaDescribedBy,
|
|
58
|
+
"aria-invalid": ariaInvalid,
|
|
59
|
+
"aria-required": ariaRequired,
|
|
60
|
+
placeholder: current.length === 0 ? placeholder : void 0,
|
|
61
|
+
className: "rt:h-5 rt:min-w-16 rt:flex-1 rt:bg-transparent rt:text-b3 rt:text-foreground rt:outline-none rt:placeholder:text-muted-foreground rt:disabled:cursor-not-allowed"
|
|
62
|
+
})]
|
|
63
|
+
}), /* @__PURE__ */ jsx(Combobox.Portal, { children: /* @__PURE__ */ jsx(Combobox.Positioner, {
|
|
64
|
+
anchor: anchorRef,
|
|
65
|
+
side: "bottom",
|
|
66
|
+
sideOffset: 4,
|
|
67
|
+
align: "start",
|
|
68
|
+
className: "rt:isolate rt:z-50",
|
|
69
|
+
children: /* @__PURE__ */ jsxs(Combobox.Popup, {
|
|
70
|
+
className: "rt:max-h-available rt:w-anchor rt:max-w-available rt:overflow-y-auto rt:rounded-lg rt:border rt:border-border rt:bg-popover rt:py-1.5 rt:text-b3 rt:text-popover-foreground rt:outline-hidden rt:transition-colors",
|
|
71
|
+
children: [/* @__PURE__ */ jsx(Combobox.Empty, {
|
|
72
|
+
className: "rt:px-3 rt:py-1 rt:text-b3 rt:text-muted-foreground rt:empty:hidden",
|
|
73
|
+
children: emptyText
|
|
74
|
+
}), /* @__PURE__ */ jsx(Combobox.List, { children: (item) => /* @__PURE__ */ jsxs(Combobox.Item, {
|
|
75
|
+
value: item,
|
|
76
|
+
className: "rt:relative rt:flex rt:h-7 rt:cursor-default rt:items-center rt:gap-2 rt:pr-8 rt:pl-3 rt:text-b3 rt:text-foreground rt:outline-none rt:select-none rt:transition-colors rt:data-highlighted:bg-accent rt:data-highlighted:text-accent-foreground rt:data-disabled:pointer-events-none rt:data-disabled:opacity-50",
|
|
77
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
78
|
+
className: "rt:flex-1 rt:truncate",
|
|
79
|
+
children: item
|
|
80
|
+
}), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
|
|
81
|
+
render: /* @__PURE__ */ jsx("span", { className: "rt:pointer-events-none rt:absolute rt:right-3 rt:flex" }),
|
|
82
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "check" })
|
|
83
|
+
})]
|
|
84
|
+
}, item) })]
|
|
85
|
+
})
|
|
86
|
+
}) })]
|
|
87
|
+
}), creatable ? /* @__PURE__ */ jsx("div", {
|
|
88
|
+
className: "rt:w-40",
|
|
89
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
90
|
+
size: 32,
|
|
91
|
+
"aria-label": addLabel,
|
|
92
|
+
placeholder: addLabel,
|
|
93
|
+
"aria-describedby": ariaDescribedBy,
|
|
94
|
+
value: draft,
|
|
95
|
+
onChange: (event) => setDraft(event.target.value),
|
|
96
|
+
onKeyDown: add,
|
|
97
|
+
disabled
|
|
98
|
+
})
|
|
99
|
+
}) : null]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export { TagInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TagInput } from "./TagInput.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { JSX, Ref, TextareaHTMLAttributes } from "react";
|
|
2
|
+
/** The minimum height of the field in px: 88 for a description, 64 for a compact one. */
|
|
3
|
+
export type TextareaSize = 88 | 64;
|
|
4
|
+
/**
|
|
5
|
+
* The props of Textarea: the HTML attributes of a textarea, except className, style and the HTML size, with the height.
|
|
6
|
+
* Mark an invalid field with aria-invalid, which draws the error ring.
|
|
7
|
+
*/
|
|
8
|
+
export type TextareaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'className' | 'style'> & {
|
|
9
|
+
/** The height in px: 88 by default, 64 compact. The field can still be dragged taller. */
|
|
10
|
+
size?: TextareaSize;
|
|
11
|
+
/** Receives the textarea element. */
|
|
12
|
+
ref?: Ref<HTMLTextAreaElement>;
|
|
13
|
+
};
|
|
14
|
+
/** A multi-line text field in the input's look: 8px radius, 1px ring that turns blue on focus, 10px 12px padding, 88px tall (64px compact). */
|
|
15
|
+
export declare function Textarea({ size, ref, ...rest }: TextareaProps): JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
// src/components/textarea/Textarea.tsx
|
|
5
|
+
const textareaVariants = cva("rt:block rt:w-full rt:min-w-0 rt:resize-y rt:rounded-lg rt:bg-background rt:border-y-2 rt:border-transparent rt:px-3 rt:py-2 rt:text-b3 rt:text-foreground rt:placeholder:text-muted-foreground rt:shadow-ring-subtle rt:outline-none rt:transition-colors rt:forced-outline rt:focus-visible:shadow-ring-focus rt:aria-invalid:shadow-ring-invalid rt:aria-invalid:focus-visible:shadow-ring-invalid rt:disabled:pointer-events-none rt:disabled:cursor-not-allowed rt:disabled:opacity-50", {
|
|
6
|
+
variants: { size: {
|
|
7
|
+
88: "rt:min-h-22",
|
|
8
|
+
64: "rt:min-h-16"
|
|
9
|
+
} },
|
|
10
|
+
defaultVariants: { size: 88 }
|
|
11
|
+
});
|
|
12
|
+
/** A multi-line text field in the input's look: 8px radius, 1px ring that turns blue on focus, 10px 12px padding, 88px tall (64px compact). */
|
|
13
|
+
function Textarea({ size = 88, ref, ...rest }) {
|
|
14
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
15
|
+
"data-slot": "textarea",
|
|
16
|
+
ref,
|
|
17
|
+
className: cn(textareaVariants({ size })),
|
|
18
|
+
...rest
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export { Textarea };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Textarea } from "./Textarea.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { JSX, ReactNode } from "react";
|
|
2
|
+
/** What a toast can carry besides its title. */
|
|
3
|
+
export interface ToastOptions {
|
|
4
|
+
/** A second line under the title. */
|
|
5
|
+
description?: ReactNode;
|
|
6
|
+
/** How long it stays, in ms. Defaults to 5000; 0 keeps it until it is closed. */
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Shows a message after an action. Call toast.success, toast.info or toast.error with the title, from anywhere; a Toaster
|
|
11
|
+
* must be mounted once in the app. Each is announced to screen readers through a polite live region.
|
|
12
|
+
*/
|
|
13
|
+
export declare const toast: {
|
|
14
|
+
/** Shows a success message and returns its id. */
|
|
15
|
+
success: (title: ReactNode, options?: ToastOptions) => string;
|
|
16
|
+
/** Shows an info message and returns its id. */
|
|
17
|
+
info: (title: ReactNode, options?: ToastOptions) => string;
|
|
18
|
+
/** Shows an error message and returns its id. */
|
|
19
|
+
error: (title: ReactNode, options?: ToastOptions) => string;
|
|
20
|
+
/** Closes one toast by id, or all when none is given. */
|
|
21
|
+
dismiss: (id?: string) => void;
|
|
22
|
+
};
|
|
23
|
+
/** Props of Toaster. */
|
|
24
|
+
export interface ToasterProps {
|
|
25
|
+
/** The accessible name of each close button. Defaults to Close notification. */
|
|
26
|
+
closeLabel?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Mounts the toast viewport, bottom right, once for the app. Messages from toast() appear in it as plain panels with the
|
|
30
|
+
* ring outline, stacked, with no entrance animation.
|
|
31
|
+
*/
|
|
32
|
+
export declare function Toaster({ closeLabel }: ToasterProps): JSX.Element;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { Icon } from "../icon/Icon.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Toast } from "@base-ui/react/toast";
|
|
5
|
+
// src/components/toaster/Toaster.tsx
|
|
6
|
+
const manager = Toast.createToastManager();
|
|
7
|
+
function show(type, title, options = {}) {
|
|
8
|
+
return manager.add({
|
|
9
|
+
type,
|
|
10
|
+
title,
|
|
11
|
+
description: options.description,
|
|
12
|
+
timeout: options.timeout,
|
|
13
|
+
priority: "low"
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Shows a message after an action. Call toast.success, toast.info or toast.error with the title, from anywhere; a Toaster
|
|
18
|
+
* must be mounted once in the app. Each is announced to screen readers through a polite live region.
|
|
19
|
+
*/
|
|
20
|
+
const toast = {
|
|
21
|
+
/** Shows a success message and returns its id. */
|
|
22
|
+
success: (title, options) => show("success", title, options),
|
|
23
|
+
/** Shows an info message and returns its id. */
|
|
24
|
+
info: (title, options) => show("info", title, options),
|
|
25
|
+
/** Shows an error message and returns its id. */
|
|
26
|
+
error: (title, options) => show("error", title, options),
|
|
27
|
+
/** Closes one toast by id, or all when none is given. */
|
|
28
|
+
dismiss: (id) => manager.close(id)
|
|
29
|
+
};
|
|
30
|
+
const ICONS = {
|
|
31
|
+
success: "check",
|
|
32
|
+
info: "info-circle",
|
|
33
|
+
error: "alert-triangle"
|
|
34
|
+
};
|
|
35
|
+
function ToastList({ closeLabel }) {
|
|
36
|
+
const { toasts } = Toast.useToastManager();
|
|
37
|
+
return /* @__PURE__ */ jsx(Fragment, { children: toasts.map((item) => {
|
|
38
|
+
const kind = item.type ?? "info";
|
|
39
|
+
return /* @__PURE__ */ jsxs(Toast.Root, {
|
|
40
|
+
toast: item,
|
|
41
|
+
"data-slot": "toast",
|
|
42
|
+
className: "rt:pointer-events-auto rt:flex rt:w-full rt:items-start rt:gap-3 rt:rounded-lg rt:bg-popover rt:p-3 rt:text-popover-foreground rt:shadow-ring-strong rt:forced-outline",
|
|
43
|
+
children: [
|
|
44
|
+
/* @__PURE__ */ jsx("span", {
|
|
45
|
+
"data-slot": "toast-icon",
|
|
46
|
+
className: cn("rt:flex rt:h-5 rt:items-center", kind === "error" ? "rt:text-error-icon" : "rt:text-icon"),
|
|
47
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
48
|
+
name: ICONS[kind],
|
|
49
|
+
size: 16
|
|
50
|
+
})
|
|
51
|
+
}),
|
|
52
|
+
/* @__PURE__ */ jsxs(Toast.Content, {
|
|
53
|
+
className: "rt:flex rt:min-w-0 rt:flex-1 rt:flex-col rt:gap-1",
|
|
54
|
+
children: [/* @__PURE__ */ jsx(Toast.Title, {
|
|
55
|
+
"data-slot": "toast-title",
|
|
56
|
+
className: cn("rt:text-b4", kind === "error" ? "rt:text-error" : "rt:text-foreground-strong")
|
|
57
|
+
}), /* @__PURE__ */ jsx(Toast.Description, {
|
|
58
|
+
"data-slot": "toast-description",
|
|
59
|
+
className: "rt:text-b3 rt:text-foreground-subdued"
|
|
60
|
+
})]
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ jsx(Toast.Close, {
|
|
63
|
+
"aria-label": closeLabel,
|
|
64
|
+
"data-slot": "toast-close",
|
|
65
|
+
className: "rt:flex rt:size-5 rt:shrink-0 rt:items-center rt:justify-center rt:rounded-md rt:text-icon rt:transition-colors rt:hover:bg-primary-tint rt:focus-visible:focus-ring",
|
|
66
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
67
|
+
name: "x",
|
|
68
|
+
size: 12
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
}, item.id);
|
|
73
|
+
}) });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Mounts the toast viewport, bottom right, once for the app. Messages from toast() appear in it as plain panels with the
|
|
77
|
+
* ring outline, stacked, with no entrance animation.
|
|
78
|
+
*/
|
|
79
|
+
function Toaster({ closeLabel = "Close notification" }) {
|
|
80
|
+
return /* @__PURE__ */ jsx(Toast.Provider, {
|
|
81
|
+
toastManager: manager,
|
|
82
|
+
children: /* @__PURE__ */ jsx(Toast.Portal, { children: /* @__PURE__ */ jsx(Toast.Viewport, {
|
|
83
|
+
"data-slot": "toast-viewport",
|
|
84
|
+
className: "rt:pointer-events-none rt:fixed rt:right-4 rt:bottom-4 rt:z-50 rt:flex rt:w-90 rt:max-w-full rt:flex-col rt:gap-2",
|
|
85
|
+
children: /* @__PURE__ */ jsx(ToastList, { closeLabel })
|
|
86
|
+
}) })
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
export { Toaster, toast };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Toaster, toast } from "./Toaster.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Tooltip } from "@base-ui/react/tooltip";
|
|
2
|
+
/** The props of TooltipProvider: Base UI's provider props. */
|
|
3
|
+
export type TooltipProviderProps = Tooltip.Provider.Props;
|
|
4
|
+
/** Shares the open delay between the tooltips inside it. Wrap the app, or a group of tooltips, once. The delay defaults to 0 ms. */
|
|
5
|
+
export declare function TooltipProvider({ delay, ...props }: TooltipProviderProps): import("react").JSX.Element;
|
|
6
|
+
/** The props of Tooltip: Base UI's root props, such as open, defaultOpen and onOpenChange. */
|
|
7
|
+
export type TooltipProps = Tooltip.Root.Props;
|
|
8
|
+
/** A short hint for a control or term, shown on hover and keyboard focus. Put a TooltipTrigger and a TooltipContent inside it. */
|
|
9
|
+
declare function Tooltip$1(props: TooltipProps): import("react").JSX.Element;
|
|
10
|
+
/** The props of TooltipTrigger: Base UI's trigger props, except className and style. Use render to put the trigger on your own element. */
|
|
11
|
+
export type TooltipTriggerProps = Omit<Tooltip.Trigger.Props, 'className' | 'style'>;
|
|
12
|
+
/** The element that opens the tooltip on hover and focus. It renders a button unless render says otherwise. */
|
|
13
|
+
export declare function TooltipTrigger(props: TooltipTriggerProps): import("react").JSX.Element;
|
|
14
|
+
/** The props of TooltipContent: the popup props plus where the tooltip sits. */
|
|
15
|
+
export type TooltipContentProps = Omit<Tooltip.Popup.Props, 'className' | 'style'> & Pick<Tooltip.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>;
|
|
16
|
+
/** The tooltip text: a dark panel with light text, no shadow and no animation. It sits above the trigger by default. */
|
|
17
|
+
export declare function TooltipContent({ side, sideOffset, align, alignOffset, children, ...props }: TooltipContentProps): import("react").JSX.Element;
|
|
18
|
+
export { Tooltip$1 as Tooltip };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Tooltip } from "@base-ui/react/tooltip";
|
|
4
|
+
// src/components/tooltip/Tooltip.tsx
|
|
5
|
+
/** Shares the open delay between the tooltips inside it. Wrap the app, or a group of tooltips, once. The delay defaults to 0 ms. */
|
|
6
|
+
function TooltipProvider({ delay = 0, ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(Tooltip.Provider, {
|
|
8
|
+
delay,
|
|
9
|
+
...props
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/** A short hint for a control or term, shown on hover and keyboard focus. Put a TooltipTrigger and a TooltipContent inside it. */
|
|
13
|
+
function Tooltip$1(props) {
|
|
14
|
+
return /* @__PURE__ */ jsx(Tooltip.Root, { ...props });
|
|
15
|
+
}
|
|
16
|
+
/** The element that opens the tooltip on hover and focus. It renders a button unless render says otherwise. */
|
|
17
|
+
function TooltipTrigger(props) {
|
|
18
|
+
return /* @__PURE__ */ jsx(Tooltip.Trigger, { ...props });
|
|
19
|
+
}
|
|
20
|
+
/** The tooltip text: a dark panel with light text, no shadow and no animation. It sits above the trigger by default. */
|
|
21
|
+
function TooltipContent({ side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
|
|
23
|
+
align,
|
|
24
|
+
alignOffset,
|
|
25
|
+
side,
|
|
26
|
+
sideOffset,
|
|
27
|
+
className: "rt:isolate rt:z-50",
|
|
28
|
+
children: /* @__PURE__ */ jsx(Tooltip.Popup, {
|
|
29
|
+
className: cn("rt:inline-flex rt:w-fit rt:max-w-72 rt:items-center rt:gap-1.5 rt:rounded-md rt:bg-foreground rt:px-3 rt:py-1.5 rt:text-b1 rt:text-background"),
|
|
30
|
+
...props,
|
|
31
|
+
children
|
|
32
|
+
})
|
|
33
|
+
}) });
|
|
34
|
+
}
|
|
35
|
+
export { Tooltip$1 as Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Tooltip, TooltipContent, TooltipContentProps, TooltipProps, TooltipProvider, TooltipProviderProps, TooltipTrigger, TooltipTriggerProps } from "./Tooltip.js";
|
|
2
|
+
export { Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, type TooltipProviderProps, TooltipTrigger, type TooltipTriggerProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./Tooltip.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { JSX } from "react";
|
|
2
|
+
/** One store the person can switch to. */
|
|
3
|
+
export type StoreOption = {
|
|
4
|
+
/** The value passed to onStoreChange and compared with storeId. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** The store name, shown in the bar and the menu. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** The store's logo; without one the avatar shows its initials. */
|
|
9
|
+
src?: string;
|
|
10
|
+
};
|
|
11
|
+
/** The props of StoreSwitcher. */
|
|
12
|
+
export type StoreSwitcherProps = {
|
|
13
|
+
/** The stores the person can switch between, as the app passes them. */
|
|
14
|
+
stores: StoreOption[];
|
|
15
|
+
/** The id of the current store. */
|
|
16
|
+
storeId: string;
|
|
17
|
+
/** Called with the id of the store chosen. */
|
|
18
|
+
onStoreChange?: (id: string) => void;
|
|
19
|
+
/** The caption of its menu. Defaults to Switch store. The button is named by the store name. */
|
|
20
|
+
label?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The store switcher of TopBar: a square avatar, the store name and a chevron that open a menu of the stores the app
|
|
24
|
+
* passes. On a phone only the avatar shows. Pass it to TopBar's `storeSwitcher` slot.
|
|
25
|
+
*/
|
|
26
|
+
export declare function StoreSwitcher({ stores, storeId, onStoreChange, label }: StoreSwitcherProps): JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Icon } from "../icon/Icon.js";
|
|
2
|
+
import { Avatar } from "../avatar/Avatar.js";
|
|
3
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger } from "../dropdown-menu/DropdownMenu.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/components/top-bar/StoreSwitcher.tsx
|
|
6
|
+
/**
|
|
7
|
+
* The store switcher of TopBar: a square avatar, the store name and a chevron that open a menu of the stores the app
|
|
8
|
+
* passes. On a phone only the avatar shows. Pass it to TopBar's `storeSwitcher` slot.
|
|
9
|
+
*/
|
|
10
|
+
function StoreSwitcher({ stores, storeId, onStoreChange, label = "Switch store" }) {
|
|
11
|
+
const current = stores.find((store) => store.id === storeId);
|
|
12
|
+
const name = current?.name ?? label;
|
|
13
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
14
|
+
render: /* @__PURE__ */ jsx("button", {
|
|
15
|
+
type: "button",
|
|
16
|
+
className: "rt:flex rt:h-9 rt:items-center rt:gap-2 rt:rounded-lg rt:px-1.5 rt:text-b3 rt:text-foreground rt:hover:bg-muted rt:focus-visible:focus-ring"
|
|
17
|
+
}),
|
|
18
|
+
children: [
|
|
19
|
+
/* @__PURE__ */ jsx(Avatar, {
|
|
20
|
+
name,
|
|
21
|
+
src: current?.src,
|
|
22
|
+
shape: "square",
|
|
23
|
+
size: "sm"
|
|
24
|
+
}),
|
|
25
|
+
/* @__PURE__ */ jsx("span", {
|
|
26
|
+
className: "rt:hidden rt:max-w-40 rt:truncate rt:md:inline",
|
|
27
|
+
children: name
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
30
|
+
name: "nav-caret-down",
|
|
31
|
+
size: 8,
|
|
32
|
+
tone: "icon"
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
36
|
+
align: "end",
|
|
37
|
+
children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: label }), /* @__PURE__ */ jsx(DropdownMenuRadioGroup, {
|
|
38
|
+
value: storeId,
|
|
39
|
+
onValueChange: (value) => onStoreChange?.(String(value)),
|
|
40
|
+
children: stores.map((store) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
|
|
41
|
+
value: store.id,
|
|
42
|
+
closeOnClick: true,
|
|
43
|
+
children: store.name
|
|
44
|
+
}, store.id))
|
|
45
|
+
})] })
|
|
46
|
+
})] });
|
|
47
|
+
}
|
|
48
|
+
export { StoreSwitcher };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JSX, ReactNode } from "react";
|
|
2
|
+
/** The props of TopBar. */
|
|
3
|
+
export type TopBarProps = {
|
|
4
|
+
/** The logo, usually an image. When it is left out the bar shows the text wordmark instead (--rt-wordmark). */
|
|
5
|
+
logo?: ReactNode;
|
|
6
|
+
/** The accessible name of the text wordmark, shown when there is no logo. Defaults to Ratio. */
|
|
7
|
+
wordmarkLabel?: string;
|
|
8
|
+
/** The accessible name of the phone menu button that opens the side navigation. Defaults to Open navigation menu. */
|
|
9
|
+
menuLabel?: string;
|
|
10
|
+
/** The global search, for example GlobalSearch. It takes the free space in the middle. */
|
|
11
|
+
search?: ReactNode;
|
|
12
|
+
/** The buttons at the right, such as help and notifications. */
|
|
13
|
+
actions?: ReactNode;
|
|
14
|
+
/** The store switcher, for example StoreSwitcher. It sits at the right, after the actions. */
|
|
15
|
+
storeSwitcher?: ReactNode;
|
|
16
|
+
/** The account at the far right, such as an Avatar or a menu of the signed-in person. */
|
|
17
|
+
account?: ReactNode;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The bar across the top of every page: logo, search, actions, the store switcher and the account. It is 60px high on a
|
|
21
|
+
* desktop and 52px on a phone, with a hairline under it. The parts are slots, so the bar does not depend on their
|
|
22
|
+
* components. Inside an AppShell with a side nav it shows a menu button on phones, which opens the side nav drawer.
|
|
23
|
+
*/
|
|
24
|
+
export declare function TopBar({ logo, wordmarkLabel, menuLabel, search, actions, storeSwitcher, account }: TopBarProps): JSX.Element;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Icon } from "../icon/Icon.js";
|
|
2
|
+
import { ShellNavContext } from "../app-shell/context.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { use } from "react";
|
|
5
|
+
// src/components/top-bar/TopBar.tsx
|
|
6
|
+
/**
|
|
7
|
+
* The bar across the top of every page: logo, search, actions, the store switcher and the account. It is 60px high on a
|
|
8
|
+
* desktop and 52px on a phone, with a hairline under it. The parts are slots, so the bar does not depend on their
|
|
9
|
+
* components. Inside an AppShell with a side nav it shows a menu button on phones, which opens the side nav drawer.
|
|
10
|
+
*/
|
|
11
|
+
function TopBar({ logo, wordmarkLabel = "Ratio", menuLabel = "Open navigation menu", search, actions, storeSwitcher, account }) {
|
|
12
|
+
const nav = use(ShellNavContext);
|
|
13
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
14
|
+
className: "rt:flex rt:h-13 rt:md:h-15 rt:shrink-0 rt:items-center rt:gap-3 rt:border-b rt:border-border rt:bg-page rt:px-3 rt:md:px-6 rt:font-sans",
|
|
15
|
+
children: [
|
|
16
|
+
nav && /* @__PURE__ */ jsx("button", {
|
|
17
|
+
type: "button",
|
|
18
|
+
"aria-label": menuLabel,
|
|
19
|
+
"aria-expanded": nav.open,
|
|
20
|
+
"aria-controls": nav.id,
|
|
21
|
+
onClick: () => nav.setOpen(!nav.open),
|
|
22
|
+
className: "rt:-ml-1.5 rt:flex rt:size-9 rt:shrink-0 rt:items-center rt:justify-center rt:rounded-lg rt:text-foreground rt:hover:bg-muted rt:focus-visible:focus-ring rt:md:hidden",
|
|
23
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
24
|
+
name: "menu",
|
|
25
|
+
size: 16,
|
|
26
|
+
tone: "icon"
|
|
27
|
+
})
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ jsx("div", {
|
|
30
|
+
className: "rt:flex rt:shrink-0 rt:items-center rt:text-b4 rt:text-foreground",
|
|
31
|
+
children: logo ?? /* @__PURE__ */ jsx("span", {
|
|
32
|
+
"data-slot": "wordmark",
|
|
33
|
+
role: "img",
|
|
34
|
+
"aria-label": wordmarkLabel,
|
|
35
|
+
className: "rt:wordmark"
|
|
36
|
+
})
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsx("div", {
|
|
39
|
+
className: "rt:flex rt:min-w-0 rt:flex-1 rt:justify-center",
|
|
40
|
+
children: search
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ jsxs("div", {
|
|
43
|
+
className: "rt:flex rt:shrink-0 rt:items-center rt:gap-2",
|
|
44
|
+
children: [
|
|
45
|
+
actions,
|
|
46
|
+
storeSwitcher,
|
|
47
|
+
account
|
|
48
|
+
]
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export { TopBar };
|