@qeetrix/react 0.0.1 → 0.0.4
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/CHANGELOG.md +288 -0
- package/README.md +240 -4
- package/dist/accordion/accordion.d.ts +42 -0
- package/dist/accordion/accordion.js +72 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +1 -0
- package/dist/avatar/avatar.d.ts +21 -0
- package/dist/avatar/avatar.js +43 -0
- package/dist/avatar/index.d.ts +2 -0
- package/dist/avatar/index.js +1 -0
- package/dist/badge/badge.d.ts +14 -0
- package/dist/badge/badge.js +25 -0
- package/dist/badge/index.d.ts +2 -0
- package/dist/badge/index.js +1 -0
- package/dist/box/box.d.ts +27 -0
- package/dist/box/box.js +21 -0
- package/dist/box/index.d.ts +2 -0
- package/dist/box/index.js +1 -0
- package/dist/button/button.d.ts +50 -0
- package/dist/button/button.js +78 -0
- package/dist/button/index.d.ts +2 -0
- package/dist/button/index.js +1 -0
- package/dist/card/card.d.ts +30 -0
- package/dist/card/card.js +56 -0
- package/dist/card/index.d.ts +2 -0
- package/dist/card/index.js +1 -0
- package/dist/checkbox/checkbox.d.ts +33 -0
- package/dist/checkbox/checkbox.js +61 -0
- package/dist/checkbox/index.d.ts +2 -0
- package/dist/checkbox/index.js +1 -0
- package/dist/css/accordion.css +73 -0
- package/dist/css/avatar.css +50 -0
- package/dist/css/badge.css +59 -0
- package/dist/css/box.css +163 -0
- package/dist/css/button.css +243 -0
- package/dist/css/card.css +75 -0
- package/dist/css/checkbox.css +67 -0
- package/dist/css/dialog.css +128 -0
- package/dist/css/drawer.css +129 -0
- package/dist/css/experimental/alert.css +89 -0
- package/dist/css/experimental/app-shell.css +258 -0
- package/dist/css/experimental/breadcrumb.css +67 -0
- package/dist/css/experimental/copy-button.css +13 -0
- package/dist/css/experimental/empty-state.css +63 -0
- package/dist/css/experimental/otp-input.css +84 -0
- package/dist/css/experimental/page-header.css +110 -0
- package/dist/css/experimental/password-input.css +21 -0
- package/dist/css/experimental/sidebar.css +234 -0
- package/dist/css/experimental/stat.css +115 -0
- package/dist/css/experimental/top-bar.css +299 -0
- package/dist/css/field.css +121 -0
- package/dist/css/fieldset.css +37 -0
- package/dist/css/grid.css +86 -0
- package/dist/css/heading.css +93 -0
- package/dist/css/inline.css +85 -0
- package/dist/css/input.css +108 -0
- package/dist/css/link.css +45 -0
- package/dist/css/menu.css +166 -0
- package/dist/css/popover.css +72 -0
- package/dist/css/progress.css +102 -0
- package/dist/css/radio.css +54 -0
- package/dist/css/scroll-area.css +70 -0
- package/dist/css/select.css +189 -0
- package/dist/css/separator.css +35 -0
- package/dist/css/skeleton.css +39 -0
- package/dist/css/skip-link.css +33 -0
- package/dist/css/slider.css +108 -0
- package/dist/css/spinner.css +55 -0
- package/dist/css/stack.css +76 -0
- package/dist/css/switch.css +112 -0
- package/dist/css/table.css +68 -0
- package/dist/css/tabs.css +123 -0
- package/dist/css/text-field.css +4 -0
- package/dist/css/text.css +106 -0
- package/dist/css/toast.css +115 -0
- package/dist/css/toggle.css +125 -0
- package/dist/css/tooltip.css +47 -0
- package/dist/css/visually-hidden.css +15 -0
- package/dist/dialog/dialog.d.ts +54 -0
- package/dist/dialog/dialog.js +121 -0
- package/dist/dialog/index.d.ts +2 -0
- package/dist/dialog/index.js +1 -0
- package/dist/drawer/drawer.d.ts +49 -0
- package/dist/drawer/drawer.js +102 -0
- package/dist/drawer/index.d.ts +2 -0
- package/dist/drawer/index.js +1 -0
- package/dist/experimental/alert/alert.d.ts +29 -0
- package/dist/experimental/alert/alert.js +72 -0
- package/dist/experimental/alert/index.d.ts +2 -0
- package/dist/experimental/app-shell/app-shell.d.ts +60 -0
- package/dist/experimental/app-shell/app-shell.js +224 -0
- package/dist/experimental/app-shell/index.d.ts +2 -0
- package/dist/experimental/breadcrumb/breadcrumb.d.ts +27 -0
- package/dist/experimental/breadcrumb/breadcrumb.js +48 -0
- package/dist/experimental/breadcrumb/index.d.ts +2 -0
- package/dist/experimental/copy-button/copy-button.d.ts +27 -0
- package/dist/experimental/copy-button/copy-button.js +61 -0
- package/dist/experimental/copy-button/index.d.ts +2 -0
- package/dist/experimental/empty-state/data-state.d.ts +23 -0
- package/dist/experimental/empty-state/data-state.js +40 -0
- package/dist/experimental/empty-state/empty-state.d.ts +26 -0
- package/dist/experimental/empty-state/empty-state.js +46 -0
- package/dist/experimental/empty-state/index.d.ts +3 -0
- package/dist/experimental/index.d.ts +24 -0
- package/dist/experimental/index.js +13 -0
- package/dist/experimental/otp-input/index.d.ts +2 -0
- package/dist/experimental/otp-input/otp-input.d.ts +27 -0
- package/dist/experimental/otp-input/otp-input.js +39 -0
- package/dist/experimental/page-header/index.d.ts +2 -0
- package/dist/experimental/page-header/page-header.d.ts +32 -0
- package/dist/experimental/page-header/page-header.js +73 -0
- package/dist/experimental/password-input/index.d.ts +2 -0
- package/dist/experimental/password-input/password-input.d.ts +26 -0
- package/dist/experimental/password-input/password-input.js +103 -0
- package/dist/experimental/sidebar/index.d.ts +2 -0
- package/dist/experimental/sidebar/sidebar.d.ts +64 -0
- package/dist/experimental/sidebar/sidebar.js +200 -0
- package/dist/experimental/stat/index.d.ts +2 -0
- package/dist/experimental/stat/stat.d.ts +31 -0
- package/dist/experimental/stat/stat.js +90 -0
- package/dist/experimental/top-bar/index.d.ts +2 -0
- package/dist/experimental/top-bar/top-bar.d.ts +60 -0
- package/dist/experimental/top-bar/top-bar.js +162 -0
- package/dist/field/choice.d.ts +21 -0
- package/dist/field/choice.js +67 -0
- package/dist/field/field.d.ts +31 -0
- package/dist/field/field.js +78 -0
- package/dist/field/index.d.ts +3 -0
- package/dist/field/index.js +1 -0
- package/dist/fieldset/fieldset.d.ts +19 -0
- package/dist/fieldset/fieldset.js +29 -0
- package/dist/fieldset/index.d.ts +2 -0
- package/dist/fieldset/index.js +1 -0
- package/dist/format/format.d.ts +12 -0
- package/dist/format/format.js +50 -0
- package/dist/format/index.d.ts +2 -0
- package/dist/format/index.js +1 -0
- package/dist/grid/grid.d.ts +30 -0
- package/dist/grid/grid.js +36 -0
- package/dist/grid/index.d.ts +2 -0
- package/dist/grid/index.js +1 -0
- package/dist/heading/heading.d.ts +23 -0
- package/dist/heading/heading.js +30 -0
- package/dist/heading/index.d.ts +2 -0
- package/dist/heading/index.js +1 -0
- package/dist/index.d.ts +84 -1
- package/dist/index.js +40 -0
- package/dist/inline/index.d.ts +2 -0
- package/dist/inline/index.js +1 -0
- package/dist/inline/inline.d.ts +19 -0
- package/dist/inline/inline.js +16 -0
- package/dist/input/index.d.ts +2 -0
- package/dist/input/index.js +1 -0
- package/dist/input/input.d.ts +37 -0
- package/dist/input/input.js +63 -0
- package/dist/internal/control.d.ts +9 -0
- package/dist/internal/control.js +4 -0
- package/dist/internal/cx.js +7 -0
- package/dist/internal/media.js +15 -0
- package/dist/internal/messages.d.ts +75 -0
- package/dist/internal/messages.js +61 -0
- package/dist/internal/refs.js +23 -0
- package/dist/internal/render.d.ts +6 -0
- package/dist/internal/render.js +22 -0
- package/dist/internal/space.d.ts +9 -0
- package/dist/internal/text.d.ts +5 -0
- package/dist/internal/text.js +18 -0
- package/dist/link/index.d.ts +2 -0
- package/dist/link/index.js +1 -0
- package/dist/link/link.d.ts +19 -0
- package/dist/link/link.js +32 -0
- package/dist/locales/hi.d.ts +8 -0
- package/dist/locales/hi.js +60 -0
- package/dist/menu/index.d.ts +2 -0
- package/dist/menu/index.js +1 -0
- package/dist/menu/menu.d.ts +76 -0
- package/dist/menu/menu.js +172 -0
- package/dist/popover/index.d.ts +2 -0
- package/dist/popover/index.js +1 -0
- package/dist/popover/popover.d.ts +33 -0
- package/dist/popover/popover.js +48 -0
- package/dist/progress/index.d.ts +2 -0
- package/dist/progress/index.js +1 -0
- package/dist/progress/progress.d.ts +26 -0
- package/dist/progress/progress.js +62 -0
- package/dist/provider/index.d.ts +3 -0
- package/dist/provider/index.js +1 -0
- package/dist/provider/provider.d.ts +46 -0
- package/dist/provider/provider.js +90 -0
- package/dist/radio/index.d.ts +2 -0
- package/dist/radio/index.js +1 -0
- package/dist/radio/radio.d.ts +31 -0
- package/dist/radio/radio.js +47 -0
- package/dist/scroll-area/index.d.ts +2 -0
- package/dist/scroll-area/index.js +1 -0
- package/dist/scroll-area/scroll-area.d.ts +18 -0
- package/dist/scroll-area/scroll-area.js +39 -0
- package/dist/select/index.d.ts +2 -0
- package/dist/select/index.js +1 -0
- package/dist/select/select.d.ts +36 -0
- package/dist/select/select.js +82 -0
- package/dist/separator/index.d.ts +2 -0
- package/dist/separator/index.js +1 -0
- package/dist/separator/separator.d.ts +16 -0
- package/dist/separator/separator.js +16 -0
- package/dist/skeleton/index.d.ts +2 -0
- package/dist/skeleton/index.js +1 -0
- package/dist/skeleton/skeleton.d.ts +12 -0
- package/dist/skeleton/skeleton.js +18 -0
- package/dist/skip-link/index.d.ts +2 -0
- package/dist/skip-link/index.js +1 -0
- package/dist/skip-link/skip-link.d.ts +13 -0
- package/dist/skip-link/skip-link.js +18 -0
- package/dist/slider/index.d.ts +2 -0
- package/dist/slider/index.js +1 -0
- package/dist/slider/slider.d.ts +20 -0
- package/dist/slider/slider.js +58 -0
- package/dist/spinner/index.d.ts +2 -0
- package/dist/spinner/index.js +1 -0
- package/dist/spinner/spinner.d.ts +15 -0
- package/dist/spinner/spinner.js +19 -0
- package/dist/stack/index.d.ts +2 -0
- package/dist/stack/index.js +1 -0
- package/dist/stack/stack.d.ts +17 -0
- package/dist/stack/stack.js +15 -0
- package/dist/styles.css +4548 -0
- package/dist/switch/index.d.ts +2 -0
- package/dist/switch/index.js +1 -0
- package/dist/switch/switch.d.ts +18 -0
- package/dist/switch/switch.js +31 -0
- package/dist/table/index.d.ts +2 -0
- package/dist/table/index.js +1 -0
- package/dist/table/table.d.ts +36 -0
- package/dist/table/table.js +69 -0
- package/dist/tabs/index.d.ts +2 -0
- package/dist/tabs/index.js +1 -0
- package/dist/tabs/tabs.d.ts +43 -0
- package/dist/tabs/tabs.js +66 -0
- package/dist/text/index.d.ts +3 -0
- package/dist/text/index.js +1 -0
- package/dist/text/text.d.ts +24 -0
- package/dist/text/text.js +24 -0
- package/dist/text-field/index.d.ts +2 -0
- package/dist/text-field/index.js +1 -0
- package/dist/text-field/text-field.d.ts +15 -0
- package/dist/text-field/text-field.js +43 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +1 -0
- package/dist/toast/toast.d.ts +57 -0
- package/dist/toast/toast.js +124 -0
- package/dist/toggle/index.d.ts +2 -0
- package/dist/toggle/index.js +1 -0
- package/dist/toggle/toggle.d.ts +48 -0
- package/dist/toggle/toggle.js +54 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/tooltip/tooltip.d.ts +26 -0
- package/dist/tooltip/tooltip.js +39 -0
- package/dist/visually-hidden/index.d.ts +2 -0
- package/dist/visually-hidden/index.js +1 -0
- package/dist/visually-hidden/visually-hidden.d.ts +8 -0
- package/dist/visually-hidden/visually-hidden.js +12 -0
- package/package.json +170 -2
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { RenderProp } from "../../internal/render.js";
|
|
2
|
+
import { ComponentProps, ReactElement, ReactNode } from "react";
|
|
3
|
+
import { Button } from "@base-ui/react/button";
|
|
4
|
+
export interface TopBarProps extends Omit<ComponentProps<"header">, "children"> {
|
|
5
|
+
/** The suite launcher: a Menu of Qeet products behind a neutral IconButton. */
|
|
6
|
+
launcher?: ReactNode;
|
|
7
|
+
/** The product's mark, e.g. `<QeetLogo variant="dark" height={20} />`. */
|
|
8
|
+
logo?: ReactNode;
|
|
9
|
+
/** The product's name, beside the mark: "Qeet ID". */
|
|
10
|
+
product: string;
|
|
11
|
+
/** The identity's link home. */
|
|
12
|
+
href?: string;
|
|
13
|
+
/** A router link for the identity instead of `<a>`. */
|
|
14
|
+
render?: RenderProp;
|
|
15
|
+
/**
|
|
16
|
+
* The scope, outermost first: organisation, then project or environment. Usually
|
|
17
|
+
* TopBarSwitchers; a slash is drawn before each.
|
|
18
|
+
*/
|
|
19
|
+
context?: ReactNode;
|
|
20
|
+
/** The global search, centred: a TopBarSearch. Hidden below 840px. */
|
|
21
|
+
search?: ReactNode;
|
|
22
|
+
/** Utility IconButtons in the neutral tone, after the AppShell's panel triggers. */
|
|
23
|
+
actions?: ReactNode;
|
|
24
|
+
/** The account menu: an Avatar trigger, after a rule. */
|
|
25
|
+
account?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
/** An environment's colour: production, staging, development… The word says which. */
|
|
28
|
+
export type TopBarStatus = "neutral" | "success" | "info" | "warning" | "danger";
|
|
29
|
+
export interface TopBarSwitcherProps extends Omit<Button.Props, "children" | "className"> {
|
|
30
|
+
/** The current organisation, project or environment. */
|
|
31
|
+
label: ReactNode;
|
|
32
|
+
/** A small square before the label: the organisation's initials or logo. */
|
|
33
|
+
mark?: ReactNode;
|
|
34
|
+
/** A dot before the label, for an environment. Never colour alone: the label names it. */
|
|
35
|
+
status?: TopBarStatus;
|
|
36
|
+
className?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface TopBarSearchProps extends Omit<Button.Props, "children" | "className"> {
|
|
39
|
+
/** The prompt, which is also the button's name: "Search or jump to…". */
|
|
40
|
+
placeholder: string;
|
|
41
|
+
/** Keys shown at the end, e.g. `["⌘", "K"]`. Wire the shortcut itself in the product. */
|
|
42
|
+
shortcut?: readonly string[];
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The scope bar of a Qeet console (07-components.md, C1): launcher, product, the scope path,
|
|
47
|
+
* search, tools and account across the full width. It's black in light and dark alike (it renders
|
|
48
|
+
* the dark scheme on itself), topped by a 2px Ember line.
|
|
49
|
+
*/
|
|
50
|
+
export declare function TopBar({ launcher, logo, product, href, render, context, search, actions, account, className, ...props }: TopBarProps): ReactElement;
|
|
51
|
+
/**
|
|
52
|
+
* A step of the TopBar's scope path: the current organisation or environment, which opens a Menu
|
|
53
|
+
* to switch it. Use it as the trigger: `<MenuTrigger render={<TopBarSwitcher label="Acme" />} />`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function TopBarSwitcher({ label, mark, status, className, ...props }: TopBarSwitcherProps): ReactElement;
|
|
56
|
+
/**
|
|
57
|
+
* The global search in the TopBar: a field-shaped button that opens the product's search or command
|
|
58
|
+
* palette, with its shortcut shown at the end.
|
|
59
|
+
*/
|
|
60
|
+
export declare function TopBarSearch({ placeholder, shortcut, className, ...props }: TopBarSearchProps): ReactElement;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../../internal/cx.js";
|
|
3
|
+
import { renderElement } from "../../internal/render.js";
|
|
4
|
+
import { IconButton } from "../../button/button.js";
|
|
5
|
+
import { useShellState } from "../app-shell/app-shell.js";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { Children, Fragment as Fragment$1, isValidElement } from "react";
|
|
8
|
+
import { Button } from "@base-ui/react/button";
|
|
9
|
+
import { SearchIcon } from "@qeetrix/icons/icons/search";
|
|
10
|
+
import { ChevronsUpDownIcon } from "@qeetrix/icons/icons/chevrons-up-down";
|
|
11
|
+
import { MenuIcon } from "@qeetrix/icons/icons/menu";
|
|
12
|
+
// src/experimental/top-bar/top-bar.tsx
|
|
13
|
+
/** Context items one by one, looking through fragments, so each gets its slash. */
|
|
14
|
+
function items(nodes) {
|
|
15
|
+
return Children.toArray(nodes).flatMap((node) => isValidElement(node) && node.type === Fragment$1 ? items(node.props.children) : [node]);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The scope bar of a Qeet console (07-components.md, C1): launcher, product, the scope path,
|
|
19
|
+
* search, tools and account across the full width. It's black in light and dark alike (it renders
|
|
20
|
+
* the dark scheme on itself), topped by a 2px Ember line.
|
|
21
|
+
*/
|
|
22
|
+
function TopBar({ launcher, logo, product, href, render, context, search, actions, account, className, ...props }) {
|
|
23
|
+
const shell = useShellState();
|
|
24
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
25
|
+
...props,
|
|
26
|
+
className: cx("qx-top-bar", className),
|
|
27
|
+
"data-slot": "top-bar",
|
|
28
|
+
"data-qx-color-scheme": "dark",
|
|
29
|
+
"data-search": search != null ? "" : void 0,
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ jsxs("div", {
|
|
32
|
+
className: "qx-top-bar-start",
|
|
33
|
+
children: [
|
|
34
|
+
shell.narrow && /* @__PURE__ */ jsx("div", {
|
|
35
|
+
className: "qx-top-bar-cell",
|
|
36
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
37
|
+
icon: MenuIcon,
|
|
38
|
+
"aria-label": shell.strings.open,
|
|
39
|
+
tone: "neutral",
|
|
40
|
+
tooltip: false,
|
|
41
|
+
onClick: shell.openNavigation
|
|
42
|
+
})
|
|
43
|
+
}),
|
|
44
|
+
launcher != null && /* @__PURE__ */ jsx("div", {
|
|
45
|
+
className: "qx-top-bar-cell",
|
|
46
|
+
"data-slot": "top-bar-launcher",
|
|
47
|
+
children: launcher
|
|
48
|
+
}),
|
|
49
|
+
renderElement("a", render, {
|
|
50
|
+
href,
|
|
51
|
+
className: "qx-top-bar-identity",
|
|
52
|
+
"data-slot": "top-bar-identity",
|
|
53
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [logo, /* @__PURE__ */ jsx("span", {
|
|
54
|
+
className: "qx-top-bar-product",
|
|
55
|
+
children: product
|
|
56
|
+
})] })
|
|
57
|
+
}),
|
|
58
|
+
items(context).map((item, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
59
|
+
className: "qx-top-bar-slash",
|
|
60
|
+
"aria-hidden": true
|
|
61
|
+
}), item] }, index))
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
search != null && /* @__PURE__ */ jsx("div", {
|
|
65
|
+
className: "qx-top-bar-search",
|
|
66
|
+
"data-slot": "top-bar-search",
|
|
67
|
+
children: search
|
|
68
|
+
}),
|
|
69
|
+
/* @__PURE__ */ jsxs("div", {
|
|
70
|
+
className: "qx-top-bar-end",
|
|
71
|
+
children: [
|
|
72
|
+
shell.drawers.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
73
|
+
role: "group",
|
|
74
|
+
"aria-label": shell.strings.tools,
|
|
75
|
+
className: "qx-top-bar-tools",
|
|
76
|
+
children: shell.drawers.map((drawer) => /* @__PURE__ */ jsx("span", {
|
|
77
|
+
className: "qx-top-bar-tool",
|
|
78
|
+
"data-badge": drawer.badge ? "" : void 0,
|
|
79
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
80
|
+
icon: drawer.icon,
|
|
81
|
+
"aria-label": drawer.label,
|
|
82
|
+
"aria-pressed": shell.activeDrawer === drawer.id,
|
|
83
|
+
tone: "neutral",
|
|
84
|
+
onClick: () => shell.toggleDrawer(drawer.id)
|
|
85
|
+
})
|
|
86
|
+
}, drawer.id))
|
|
87
|
+
}),
|
|
88
|
+
actions,
|
|
89
|
+
account != null && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
90
|
+
className: "qx-top-bar-rule",
|
|
91
|
+
"aria-hidden": true
|
|
92
|
+
}), account] })
|
|
93
|
+
]
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A step of the TopBar's scope path: the current organisation or environment, which opens a Menu
|
|
100
|
+
* to switch it. Use it as the trigger: `<MenuTrigger render={<TopBarSwitcher label="Acme" />} />`.
|
|
101
|
+
*/
|
|
102
|
+
function TopBarSwitcher({ label, mark, status, className, ...props }) {
|
|
103
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
104
|
+
...props,
|
|
105
|
+
className: cx("qx-top-bar-switcher", className),
|
|
106
|
+
"data-slot": "top-bar-switcher",
|
|
107
|
+
children: [
|
|
108
|
+
mark != null && /* @__PURE__ */ jsx("span", {
|
|
109
|
+
className: "qx-top-bar-mark",
|
|
110
|
+
"aria-hidden": true,
|
|
111
|
+
children: mark
|
|
112
|
+
}),
|
|
113
|
+
status && /* @__PURE__ */ jsx("span", {
|
|
114
|
+
className: "qx-top-bar-status",
|
|
115
|
+
"data-status": status,
|
|
116
|
+
"aria-hidden": true
|
|
117
|
+
}),
|
|
118
|
+
/* @__PURE__ */ jsx("span", {
|
|
119
|
+
className: "qx-top-bar-switcher-label",
|
|
120
|
+
children: label
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ jsx(ChevronsUpDownIcon, {
|
|
123
|
+
shape: "round",
|
|
124
|
+
size: 14,
|
|
125
|
+
"aria-hidden": true,
|
|
126
|
+
className: "qx-top-bar-caret"
|
|
127
|
+
})
|
|
128
|
+
]
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The global search in the TopBar: a field-shaped button that opens the product's search or command
|
|
133
|
+
* palette, with its shortcut shown at the end.
|
|
134
|
+
*/
|
|
135
|
+
function TopBarSearch({ placeholder, shortcut, className, ...props }) {
|
|
136
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
137
|
+
...props,
|
|
138
|
+
className: cx("qx-top-bar-search-trigger", className),
|
|
139
|
+
"data-slot": "top-bar-search-trigger",
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ jsx(SearchIcon, {
|
|
142
|
+
shape: "round",
|
|
143
|
+
size: 16,
|
|
144
|
+
"aria-hidden": true,
|
|
145
|
+
className: "qx-top-bar-search-icon"
|
|
146
|
+
}),
|
|
147
|
+
/* @__PURE__ */ jsx("span", {
|
|
148
|
+
className: "qx-top-bar-search-text",
|
|
149
|
+
children: placeholder
|
|
150
|
+
}),
|
|
151
|
+
shortcut && shortcut.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
152
|
+
className: "qx-top-bar-keys",
|
|
153
|
+
"aria-hidden": true,
|
|
154
|
+
children: shortcut.map((key) => /* @__PURE__ */ jsx("kbd", {
|
|
155
|
+
className: "qx-top-bar-key",
|
|
156
|
+
children: key
|
|
157
|
+
}, key))
|
|
158
|
+
})
|
|
159
|
+
]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
export { TopBar, TopBarSearch, TopBarSwitcher };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "./field.js";
|
|
2
|
+
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* How a checkbox, radio or switch is named: by its visible `label`, or — with no visible label, as
|
|
5
|
+
* in a table row — by `aria-label` or `aria-labelledby`, which the type then requires.
|
|
6
|
+
*/
|
|
7
|
+
export type ChoiceLabelProps = {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
"aria-label"?: string;
|
|
10
|
+
"aria-labelledby"?: string;
|
|
11
|
+
} | {
|
|
12
|
+
label?: undefined;
|
|
13
|
+
"aria-label": string;
|
|
14
|
+
"aria-labelledby"?: string;
|
|
15
|
+
} | {
|
|
16
|
+
label?: undefined;
|
|
17
|
+
"aria-label"?: string;
|
|
18
|
+
"aria-labelledby": string;
|
|
19
|
+
};
|
|
20
|
+
/** Options stacked (`vertical`) or in a wrapping row (`horizontal`, for short options). */
|
|
21
|
+
export type ChoiceOrientation = "vertical" | "horizontal";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../internal/cx.js";
|
|
3
|
+
import { FieldMessages, InFieldContext } from "./field.js";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { use } from "react";
|
|
6
|
+
import { Field } from "@base-ui/react/field";
|
|
7
|
+
import { Fieldset } from "@base-ui/react/fieldset";
|
|
8
|
+
// src/field/choice.tsx
|
|
9
|
+
/**
|
|
10
|
+
* One option: the control and its text inside a label (the whole row is the click target), then an
|
|
11
|
+
* optional description, both linked to the control by Base UI. Inside a group or a Field the row is
|
|
12
|
+
* a Field.Item; on its own it is a Field.Root. With no visible label, the control renders alone.
|
|
13
|
+
*/
|
|
14
|
+
function ChoiceRow({ kind, label, description, disabled, className, style, children }) {
|
|
15
|
+
const inField = use(InFieldContext);
|
|
16
|
+
if (label == null) return children;
|
|
17
|
+
const row = {
|
|
18
|
+
disabled,
|
|
19
|
+
className: cx("qx-choice", className),
|
|
20
|
+
style,
|
|
21
|
+
"data-slot": kind,
|
|
22
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Field.Label, {
|
|
23
|
+
className: "qx-choice-label",
|
|
24
|
+
"data-slot": `${kind}-label`,
|
|
25
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
26
|
+
className: "qx-choice-text",
|
|
27
|
+
children: label
|
|
28
|
+
})]
|
|
29
|
+
}), description != null && /* @__PURE__ */ jsx(Field.Description, {
|
|
30
|
+
className: "qx-choice-description",
|
|
31
|
+
"data-slot": `${kind}-description`,
|
|
32
|
+
children: description
|
|
33
|
+
})] })
|
|
34
|
+
};
|
|
35
|
+
return inField ? /* @__PURE__ */ jsx(Field.Item, { ...row }) : /* @__PURE__ */ jsx(Field.Root, { ...row });
|
|
36
|
+
}
|
|
37
|
+
/** A question with options: Field › Fieldset rendered as the group › legend, options, messages. */
|
|
38
|
+
function ChoiceGroup({ kind, label, description, error, name, disabled, orientation, className, style, group, children }) {
|
|
39
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
40
|
+
name,
|
|
41
|
+
disabled,
|
|
42
|
+
invalid: error != null ? true : void 0,
|
|
43
|
+
className: cx("qx-field qx-choice-group", className),
|
|
44
|
+
style,
|
|
45
|
+
"data-slot": `${kind}-group`,
|
|
46
|
+
"data-orientation": orientation,
|
|
47
|
+
children: [/* @__PURE__ */ jsxs(Fieldset.Root, {
|
|
48
|
+
render: group,
|
|
49
|
+
className: "qx-choice-group-set",
|
|
50
|
+
children: [/* @__PURE__ */ jsx(Fieldset.Legend, {
|
|
51
|
+
className: "qx-field-label",
|
|
52
|
+
"data-slot": `${kind}-group-legend`,
|
|
53
|
+
children: label
|
|
54
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
55
|
+
className: "qx-choice-group-options",
|
|
56
|
+
children: /* @__PURE__ */ jsx(InFieldContext, {
|
|
57
|
+
value: true,
|
|
58
|
+
children
|
|
59
|
+
})
|
|
60
|
+
})]
|
|
61
|
+
}), /* @__PURE__ */ jsx(FieldMessages, {
|
|
62
|
+
description,
|
|
63
|
+
error
|
|
64
|
+
})]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export { ChoiceGroup, ChoiceRow };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { QeetrixMessages } from "../internal/messages.js";
|
|
2
|
+
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
|
+
import { Field } from "@base-ui/react/field";
|
|
4
|
+
/** The help text and error every labelled field shares. */
|
|
5
|
+
export interface FieldMessageProps {
|
|
6
|
+
/** Help text under the control, e.g. a format ("15 characters"). Hidden while an error shows. */
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* An error to show now, e.g. from the server; it also marks the field invalid. Without it, the
|
|
10
|
+
* browser's or `validate`'s message shows whenever the field is invalid.
|
|
11
|
+
*/
|
|
12
|
+
error?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export interface FieldProps extends Omit<Field.Root.Props, "className" | "style" | "render" | "children">, FieldMessageProps {
|
|
15
|
+
/** The label. Required: a placeholder is never a label. */
|
|
16
|
+
label: ReactNode;
|
|
17
|
+
/** Appends "(optional)". Qeetrix marks optional fields, never required ones. */
|
|
18
|
+
optional?: boolean;
|
|
19
|
+
/** Hides the label visually and keeps it for assistive technology. Only for a toolbar search. */
|
|
20
|
+
hideLabel?: boolean;
|
|
21
|
+
/** This field's strings, over QeetrixProvider's and the English defaults. */
|
|
22
|
+
messages?: Partial<QeetrixMessages["field"]>;
|
|
23
|
+
/** Placement only (margin, width, grid placement). */
|
|
24
|
+
className?: string;
|
|
25
|
+
style?: CSSProperties;
|
|
26
|
+
/** The control: an Input, a Textarea, or any Base UI form control. */
|
|
27
|
+
children?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
/** The label, help text and error for one control (06-component-specs.md §2). */
|
|
30
|
+
declare function Field$1({ label, description, error, optional, hideLabel, invalid, messages, className, children, ...props }: FieldProps): ReactElement;
|
|
31
|
+
export { Field$1 as Field };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../internal/cx.js";
|
|
3
|
+
import { useMessages } from "../provider/provider.js";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { createContext } from "react";
|
|
6
|
+
import { Field } from "@base-ui/react/field";
|
|
7
|
+
import { CircleAlertIcon } from "@qeetrix/icons/icons/circle-alert";
|
|
8
|
+
// src/field/field.tsx
|
|
9
|
+
/**
|
|
10
|
+
* True inside a Field or a choice group. A checkbox, radio or switch there is an item of that field
|
|
11
|
+
* (Base UI Field.Item); on its own it is a field of its own (Field.Root).
|
|
12
|
+
*/
|
|
13
|
+
const InFieldContext = createContext(false);
|
|
14
|
+
function ErrorRow({ children, ...props }) {
|
|
15
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
16
|
+
...props,
|
|
17
|
+
children: [/* @__PURE__ */ jsx(CircleAlertIcon, {
|
|
18
|
+
shape: "sharp",
|
|
19
|
+
size: 16,
|
|
20
|
+
"aria-hidden": true,
|
|
21
|
+
className: "qx-field-error-icon"
|
|
22
|
+
}), /* @__PURE__ */ jsx("span", { children })]
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** The description and error under a control; Base UI links both to it with aria-describedby. */
|
|
26
|
+
function FieldMessages({ description, error }) {
|
|
27
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [description != null && /* @__PURE__ */ jsx(Field.Description, {
|
|
28
|
+
className: "qx-field-description",
|
|
29
|
+
"data-slot": "field-description",
|
|
30
|
+
children: description
|
|
31
|
+
}), /* @__PURE__ */ jsx(Field.Error, {
|
|
32
|
+
className: "qx-field-error",
|
|
33
|
+
"data-slot": "field-error",
|
|
34
|
+
match: error != null ? true : void 0,
|
|
35
|
+
render: /* @__PURE__ */ jsx(ErrorRow, {}),
|
|
36
|
+
...error != null ? { children: error } : {}
|
|
37
|
+
})] });
|
|
38
|
+
}
|
|
39
|
+
/** The label text, with "(optional)" when the field may be left empty. */
|
|
40
|
+
function labelText(label, optional, optionalText) {
|
|
41
|
+
if (!optional) return label;
|
|
42
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
43
|
+
label,
|
|
44
|
+
" ",
|
|
45
|
+
/* @__PURE__ */ jsx("span", {
|
|
46
|
+
className: "qx-field-optional",
|
|
47
|
+
"data-slot": "field-optional",
|
|
48
|
+
children: optionalText
|
|
49
|
+
})
|
|
50
|
+
] });
|
|
51
|
+
}
|
|
52
|
+
/** The label, help text and error for one control (06-component-specs.md §2). */
|
|
53
|
+
function Field$1({ label, description, error, optional, hideLabel, invalid, messages, className, children, ...props }) {
|
|
54
|
+
const strings = useMessages("field", messages);
|
|
55
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
56
|
+
...props,
|
|
57
|
+
invalid: invalid ?? (error != null ? true : void 0),
|
|
58
|
+
className: cx("qx-field", className),
|
|
59
|
+
"data-slot": "field",
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsx(Field.Label, {
|
|
62
|
+
className: "qx-field-label",
|
|
63
|
+
"data-slot": "field-label",
|
|
64
|
+
"data-hidden": hideLabel ? "" : void 0,
|
|
65
|
+
children: labelText(label, optional, strings.optional)
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ jsx(InFieldContext, {
|
|
68
|
+
value: true,
|
|
69
|
+
children
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsx(FieldMessages, {
|
|
72
|
+
description,
|
|
73
|
+
error
|
|
74
|
+
})
|
|
75
|
+
]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export { Field$1 as Field, FieldMessages, InFieldContext, labelText };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Field } from "./field.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
2
|
+
import { Fieldset } from "@base-ui/react/fieldset";
|
|
3
|
+
export interface FieldsetProps extends Omit<Fieldset.Root.Props, "className" | "style" | "children"> {
|
|
4
|
+
/** The section's heading, e.g. "Billing address". */
|
|
5
|
+
legend: ReactNode;
|
|
6
|
+
/** A line under the legend. */
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
/** Placement only (margin, width, grid placement). */
|
|
9
|
+
className?: string;
|
|
10
|
+
style?: CSSProperties;
|
|
11
|
+
/** The fields, stacked with the density's form gap. */
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A section of fields under a heading. `disabled` disables every field inside. A choice question is
|
|
16
|
+
* a CheckboxGroup or RadioGroup, not a Fieldset.
|
|
17
|
+
*/
|
|
18
|
+
declare function Fieldset$1({ legend, description, className, children, ...props }: FieldsetProps): ReactElement;
|
|
19
|
+
export { Fieldset$1 as Fieldset };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../internal/cx.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fieldset } from "@base-ui/react/fieldset";
|
|
5
|
+
// src/fieldset/fieldset.tsx
|
|
6
|
+
/**
|
|
7
|
+
* A section of fields under a heading. `disabled` disables every field inside. A choice question is
|
|
8
|
+
* a CheckboxGroup or RadioGroup, not a Fieldset.
|
|
9
|
+
*/
|
|
10
|
+
function Fieldset$1({ legend, description, className, children, ...props }) {
|
|
11
|
+
return /* @__PURE__ */ jsxs(Fieldset.Root, {
|
|
12
|
+
...props,
|
|
13
|
+
className: cx("qx-fieldset", className),
|
|
14
|
+
"data-slot": "fieldset",
|
|
15
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
16
|
+
className: "qx-fieldset-header",
|
|
17
|
+
children: [/* @__PURE__ */ jsx(Fieldset.Legend, {
|
|
18
|
+
className: "qx-fieldset-legend",
|
|
19
|
+
"data-slot": "fieldset-legend",
|
|
20
|
+
children: legend
|
|
21
|
+
}), description != null && /* @__PURE__ */ jsx("p", {
|
|
22
|
+
className: "qx-fieldset-description",
|
|
23
|
+
"data-slot": "fieldset-description",
|
|
24
|
+
children: description
|
|
25
|
+
})]
|
|
26
|
+
}), children]
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export { Fieldset$1 as Fieldset };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Fieldset } from "./fieldset.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** A number in the locale's digits and grouping: 1234567 → "12,34,567" in `en-IN`. */
|
|
2
|
+
export declare function formatNumber(value: number, locale?: string, options?: Intl.NumberFormatOptions): string;
|
|
3
|
+
/**
|
|
4
|
+
* An amount of money: `formatCurrency(124500, "INR")` → "₹1,24,500.00". Whole amounts can drop the
|
|
5
|
+
* decimals with `{ maximumFractionDigits: 0 }`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatCurrency(value: number, currency: string, locale?: string, options?: Intl.NumberFormatOptions): string;
|
|
8
|
+
/**
|
|
9
|
+
* How long ago or how soon, in the largest whole unit: "2 minutes ago", "in 3 days", "yesterday".
|
|
10
|
+
* `now` is a parameter so server and client render the same text.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatRelativeTime(date: Date | number, locale?: string, now?: Date | number): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { DEFAULT_LOCALE } from "../internal/messages.js";
|
|
2
|
+
// src/format/format.ts
|
|
3
|
+
/**
|
|
4
|
+
* Locale-aware formatting on Intl (decision D-13). Pure and server-safe: on the client, pass
|
|
5
|
+
* `useLocale()`. Formatters are cached per locale and options, since constructing one is the slow
|
|
6
|
+
* part.
|
|
7
|
+
*/
|
|
8
|
+
const cache = /* @__PURE__ */ new Map();
|
|
9
|
+
function cached(key, create) {
|
|
10
|
+
let formatter = cache.get(key);
|
|
11
|
+
if (!formatter) {
|
|
12
|
+
formatter = create();
|
|
13
|
+
cache.set(key, formatter);
|
|
14
|
+
}
|
|
15
|
+
return formatter;
|
|
16
|
+
}
|
|
17
|
+
/** A number in the locale's digits and grouping: 1234567 → "12,34,567" in `en-IN`. */
|
|
18
|
+
function formatNumber(value, locale = DEFAULT_LOCALE, options = {}) {
|
|
19
|
+
return cached(`n|${locale}|${JSON.stringify(options)}`, () => new Intl.NumberFormat(locale, options)).format(value);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* An amount of money: `formatCurrency(124500, "INR")` → "₹1,24,500.00". Whole amounts can drop the
|
|
23
|
+
* decimals with `{ maximumFractionDigits: 0 }`.
|
|
24
|
+
*/
|
|
25
|
+
function formatCurrency(value, currency, locale = DEFAULT_LOCALE, options = {}) {
|
|
26
|
+
return formatNumber(value, locale, {
|
|
27
|
+
style: "currency",
|
|
28
|
+
currency,
|
|
29
|
+
...options
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const UNITS = [
|
|
33
|
+
["year", 31536e6],
|
|
34
|
+
["month", 2592e6],
|
|
35
|
+
["week", 6048e5],
|
|
36
|
+
["day", 864e5],
|
|
37
|
+
["hour", 36e5],
|
|
38
|
+
["minute", 6e4],
|
|
39
|
+
["second", 1e3]
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* How long ago or how soon, in the largest whole unit: "2 minutes ago", "in 3 days", "yesterday".
|
|
43
|
+
* `now` is a parameter so server and client render the same text.
|
|
44
|
+
*/
|
|
45
|
+
function formatRelativeTime(date, locale = DEFAULT_LOCALE, now = Date.now()) {
|
|
46
|
+
const delta = Number(date) - Number(now);
|
|
47
|
+
const [unit, size] = UNITS.find(([, ms]) => Math.abs(delta) >= ms) ?? ["second", 1e3];
|
|
48
|
+
return cached(`r|${locale}`, () => new Intl.RelativeTimeFormat(locale, { numeric: "auto" })).format(Math.round(delta / size), unit);
|
|
49
|
+
}
|
|
50
|
+
export { formatCurrency, formatNumber, formatRelativeTime };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { formatCurrency, formatNumber, formatRelativeTime } from "./format.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RenderProp } from "../internal/render.js";
|
|
2
|
+
import { Space } from "../internal/space.js";
|
|
3
|
+
import { ComponentProps, ReactElement } from "react";
|
|
4
|
+
export type GridColumnCount = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
|
5
|
+
/** Columns per window size class. Each class inherits the nearest smaller one that is set. */
|
|
6
|
+
export interface GridColumns {
|
|
7
|
+
/** Below 600px, and the starting point for the others. */
|
|
8
|
+
base?: GridColumnCount;
|
|
9
|
+
/** From 600px. */
|
|
10
|
+
medium?: GridColumnCount;
|
|
11
|
+
/** From 840px. */
|
|
12
|
+
expanded?: GridColumnCount;
|
|
13
|
+
/** From 1200px. */
|
|
14
|
+
large?: GridColumnCount;
|
|
15
|
+
/** From 1600px. */
|
|
16
|
+
xlarge?: GridColumnCount;
|
|
17
|
+
}
|
|
18
|
+
export type GridAlign = "start" | "center" | "end" | "stretch";
|
|
19
|
+
export interface GridProps extends ComponentProps<"div"> {
|
|
20
|
+
/** Equal columns: one count, or one per window size class. Default 1. */
|
|
21
|
+
columns?: GridColumnCount | GridColumns;
|
|
22
|
+
/** Space between cells, from the space scale. Default `md` (12px). */
|
|
23
|
+
gap?: Space;
|
|
24
|
+
/** Vertical alignment of cells in a row. Default `stretch`. */
|
|
25
|
+
align?: GridAlign;
|
|
26
|
+
/** Render a different element, e.g. `<ul />`. */
|
|
27
|
+
render?: RenderProp;
|
|
28
|
+
}
|
|
29
|
+
/** Responsive equal columns on the window size classes, with token gaps. */
|
|
30
|
+
export declare function Grid({ columns, gap, align, render, className, style, ...props }: GridProps): ReactElement;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cx } from "../internal/cx.js";
|
|
2
|
+
import { renderElement } from "../internal/render.js";
|
|
3
|
+
// src/grid/grid.tsx
|
|
4
|
+
const SIZE_CLASSES = [
|
|
5
|
+
"base",
|
|
6
|
+
"medium",
|
|
7
|
+
"expanded",
|
|
8
|
+
"large",
|
|
9
|
+
"xlarge"
|
|
10
|
+
];
|
|
11
|
+
/** A whole number from 1 to 12; the value becomes a CSS variable, so it is never trusted as-is. */
|
|
12
|
+
const count = (value) => Math.min(12, Math.max(1, Math.trunc(value) || 1));
|
|
13
|
+
function columnVariables(columns) {
|
|
14
|
+
const perClass = typeof columns === "number" ? { base: columns } : columns;
|
|
15
|
+
const variables = {};
|
|
16
|
+
for (const sizeClass of SIZE_CLASSES) {
|
|
17
|
+
const value = perClass[sizeClass];
|
|
18
|
+
if (value !== void 0) variables[`--qx-grid-${sizeClass}`] = count(value);
|
|
19
|
+
}
|
|
20
|
+
return variables;
|
|
21
|
+
}
|
|
22
|
+
/** Responsive equal columns on the window size classes, with token gaps. */
|
|
23
|
+
function Grid({ columns = 1, gap = "md", align, render, className, style, ...props }) {
|
|
24
|
+
return renderElement("div", render, {
|
|
25
|
+
...props,
|
|
26
|
+
className: cx("qx-grid", className),
|
|
27
|
+
style: {
|
|
28
|
+
...columnVariables(columns),
|
|
29
|
+
...style
|
|
30
|
+
},
|
|
31
|
+
"data-slot": "grid",
|
|
32
|
+
"data-gap": gap,
|
|
33
|
+
"data-align": align
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export { Grid };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Grid } from "./grid.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RenderProp } from "../internal/render.js";
|
|
2
|
+
import { TextAlign } from "../internal/space.js";
|
|
3
|
+
import { Truncate } from "../internal/text.js";
|
|
4
|
+
import { ComponentProps, ReactElement } from "react";
|
|
5
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
6
|
+
export type HeadingSize = "display-lg" | "display-md" | "display-sm" | "heading-lg" | "heading-md" | "heading-sm" | "title-lg" | "title-md" | "title-sm";
|
|
7
|
+
export type HeadingTone = "default" | "subtle" | "inverse";
|
|
8
|
+
export interface HeadingProps extends ComponentProps<"h2"> {
|
|
9
|
+
/** The document level: renders `<h1>`…`<h6>`. Required, so the outline is always intended. */
|
|
10
|
+
level: HeadingLevel;
|
|
11
|
+
/** The look. Default by level: 1 heading-lg, 2 heading-md, 3 heading-sm, 4 title-lg, 5 title-md, 6 title-sm. */
|
|
12
|
+
size?: HeadingSize;
|
|
13
|
+
/** `default`, `subtle` or `inverse`. */
|
|
14
|
+
tone?: HeadingTone;
|
|
15
|
+
/** `true` for one line with an ellipsis, or a number of lines. */
|
|
16
|
+
truncate?: Truncate;
|
|
17
|
+
/** Logical alignment. */
|
|
18
|
+
align?: TextAlign;
|
|
19
|
+
/** Render a different element. Prefer `level`; use this for a link that looks like a heading. */
|
|
20
|
+
render?: RenderProp;
|
|
21
|
+
}
|
|
22
|
+
/** A section heading: the level sets the element, the size sets the look. */
|
|
23
|
+
export declare function Heading({ level, size, tone, truncate, align, render, className, style, ...props }: HeadingProps): ReactElement;
|