@qeetrix/react 0.0.2 → 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.
Files changed (104) hide show
  1. package/CHANGELOG.md +116 -1
  2. package/README.md +99 -0
  3. package/dist/button/button.d.ts +6 -2
  4. package/dist/button/button.js +5 -1
  5. package/dist/css/avatar.css +6 -0
  6. package/dist/css/badge.css +6 -0
  7. package/dist/css/button.css +81 -6
  8. package/dist/css/checkbox.css +9 -0
  9. package/dist/css/dialog.css +7 -0
  10. package/dist/css/drawer.css +6 -0
  11. package/dist/css/experimental/alert.css +89 -0
  12. package/dist/css/experimental/app-shell.css +258 -0
  13. package/dist/css/experimental/breadcrumb.css +67 -0
  14. package/dist/css/experimental/copy-button.css +13 -0
  15. package/dist/css/experimental/empty-state.css +63 -0
  16. package/dist/css/experimental/otp-input.css +84 -0
  17. package/dist/css/experimental/page-header.css +110 -0
  18. package/dist/css/experimental/password-input.css +21 -0
  19. package/dist/css/experimental/sidebar.css +234 -0
  20. package/dist/css/experimental/stat.css +115 -0
  21. package/dist/css/experimental/top-bar.css +299 -0
  22. package/dist/css/input.css +10 -0
  23. package/dist/css/menu.css +17 -0
  24. package/dist/css/progress.css +14 -0
  25. package/dist/css/scroll-area.css +7 -0
  26. package/dist/css/select.css +16 -0
  27. package/dist/css/separator.css +7 -0
  28. package/dist/css/skeleton.css +6 -0
  29. package/dist/css/slider.css +22 -0
  30. package/dist/css/switch.css +33 -0
  31. package/dist/css/tabs.css +11 -0
  32. package/dist/css/toast.css +7 -0
  33. package/dist/css/toggle.css +13 -0
  34. package/dist/css/tooltip.css +6 -0
  35. package/dist/dialog/dialog.d.ts +4 -1
  36. package/dist/dialog/dialog.js +4 -3
  37. package/dist/drawer/drawer.d.ts +4 -1
  38. package/dist/drawer/drawer.js +5 -4
  39. package/dist/experimental/alert/alert.d.ts +29 -0
  40. package/dist/experimental/alert/alert.js +72 -0
  41. package/dist/experimental/alert/index.d.ts +2 -0
  42. package/dist/experimental/app-shell/app-shell.d.ts +60 -0
  43. package/dist/experimental/app-shell/app-shell.js +224 -0
  44. package/dist/experimental/app-shell/index.d.ts +2 -0
  45. package/dist/experimental/breadcrumb/breadcrumb.d.ts +27 -0
  46. package/dist/experimental/breadcrumb/breadcrumb.js +48 -0
  47. package/dist/experimental/breadcrumb/index.d.ts +2 -0
  48. package/dist/experimental/copy-button/copy-button.d.ts +27 -0
  49. package/dist/experimental/copy-button/copy-button.js +61 -0
  50. package/dist/experimental/copy-button/index.d.ts +2 -0
  51. package/dist/experimental/empty-state/data-state.d.ts +23 -0
  52. package/dist/experimental/empty-state/data-state.js +40 -0
  53. package/dist/experimental/empty-state/empty-state.d.ts +26 -0
  54. package/dist/experimental/empty-state/empty-state.js +46 -0
  55. package/dist/experimental/empty-state/index.d.ts +3 -0
  56. package/dist/experimental/index.d.ts +24 -0
  57. package/dist/experimental/index.js +13 -0
  58. package/dist/experimental/otp-input/index.d.ts +2 -0
  59. package/dist/experimental/otp-input/otp-input.d.ts +27 -0
  60. package/dist/experimental/otp-input/otp-input.js +39 -0
  61. package/dist/experimental/page-header/index.d.ts +2 -0
  62. package/dist/experimental/page-header/page-header.d.ts +32 -0
  63. package/dist/experimental/page-header/page-header.js +73 -0
  64. package/dist/experimental/password-input/index.d.ts +2 -0
  65. package/dist/experimental/password-input/password-input.d.ts +26 -0
  66. package/dist/experimental/password-input/password-input.js +103 -0
  67. package/dist/experimental/sidebar/index.d.ts +2 -0
  68. package/dist/experimental/sidebar/sidebar.d.ts +64 -0
  69. package/dist/experimental/sidebar/sidebar.js +200 -0
  70. package/dist/experimental/stat/index.d.ts +2 -0
  71. package/dist/experimental/stat/stat.d.ts +31 -0
  72. package/dist/experimental/stat/stat.js +90 -0
  73. package/dist/experimental/top-bar/index.d.ts +2 -0
  74. package/dist/experimental/top-bar/top-bar.d.ts +60 -0
  75. package/dist/experimental/top-bar/top-bar.js +162 -0
  76. package/dist/field/field.d.ts +4 -1
  77. package/dist/field/field.js +6 -5
  78. package/dist/format/format.d.ts +12 -0
  79. package/dist/format/format.js +50 -0
  80. package/dist/format/index.d.ts +2 -0
  81. package/dist/format/index.js +1 -0
  82. package/dist/index.d.ts +6 -2
  83. package/dist/index.js +3 -1
  84. package/dist/internal/media.js +15 -0
  85. package/dist/internal/messages.d.ts +75 -0
  86. package/dist/internal/messages.js +44 -5
  87. package/dist/link/link.d.ts +7 -1
  88. package/dist/link/link.js +3 -2
  89. package/dist/locales/hi.d.ts +8 -0
  90. package/dist/locales/hi.js +60 -0
  91. package/dist/progress/progress.d.ts +2 -2
  92. package/dist/progress/progress.js +7 -2
  93. package/dist/provider/index.d.ts +3 -0
  94. package/dist/provider/index.js +1 -0
  95. package/dist/provider/provider.d.ts +46 -0
  96. package/dist/provider/provider.js +90 -0
  97. package/dist/slider/slider.d.ts +5 -2
  98. package/dist/slider/slider.js +10 -3
  99. package/dist/styles.css +1704 -104
  100. package/dist/text-field/text-field.d.ts +2 -2
  101. package/dist/text-field/text-field.js +4 -2
  102. package/dist/toast/toast.d.ts +4 -1
  103. package/dist/toast/toast.js +7 -6
  104. package/package.json +18 -2
@@ -0,0 +1,2 @@
1
+ import { AppShell, AppShellDensity, AppShellDrawer, AppShellProps, AppShellState, useAppShell } from "./app-shell.js";
2
+ export { AppShell, type AppShellDensity, type AppShellDrawer, type AppShellProps, type AppShellState, useAppShell };
@@ -0,0 +1,27 @@
1
+ import { RenderProp } from "../../internal/render.js";
2
+ import { ComponentProps, ReactElement, ReactNode } from "react";
3
+ export interface BreadcrumbProps extends ComponentProps<"nav"> {
4
+ /**
5
+ * Names the navigation landmark. Default "Breadcrumb". Breadcrumb is server-safe, so
6
+ * QeetrixProvider's strings don't reach it: pass a translation here.
7
+ */
8
+ label?: string;
9
+ /** BreadcrumbItems, the current page last. */
10
+ children?: ReactNode;
11
+ }
12
+ export interface BreadcrumbItemProps extends Omit<ComponentProps<"li">, "children"> {
13
+ /** Where it goes. The current item takes none. */
14
+ href?: string;
15
+ /** A router link instead of `<a>`, e.g. `render={<Link to="/users" />}`. */
16
+ render?: RenderProp;
17
+ /** The current page: plain text with `aria-current="page"`. */
18
+ current?: boolean;
19
+ children: ReactNode;
20
+ }
21
+ /**
22
+ * Where the page sits, in a console header (07-components.md, C1): an ordered list of links,
23
+ * separated by sharp chevrons that mirror in right-to-left text. Server-safe.
24
+ */
25
+ export declare function Breadcrumb({ label, className, children, ...props }: BreadcrumbProps): ReactElement;
26
+ /** One step. A long label truncates and keeps its full text as a title. */
27
+ export declare function BreadcrumbItem({ href, render, current, className, children, ...props }: BreadcrumbItemProps): ReactElement;
@@ -0,0 +1,48 @@
1
+ import { cx } from "../../internal/cx.js";
2
+ import { renderElement } from "../../internal/render.js";
3
+ import { messages } from "../../internal/messages.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { ChevronRightIcon } from "@qeetrix/icons/icons/chevron-right";
6
+ // src/experimental/breadcrumb/breadcrumb.tsx
7
+ /**
8
+ * Where the page sits, in a console header (07-components.md, C1): an ordered list of links,
9
+ * separated by sharp chevrons that mirror in right-to-left text. Server-safe.
10
+ */
11
+ function Breadcrumb({ label = messages.breadcrumb.label, className, children, ...props }) {
12
+ return /* @__PURE__ */ jsx("nav", {
13
+ ...props,
14
+ "aria-label": label,
15
+ className: cx("qx-breadcrumb", className),
16
+ "data-slot": "breadcrumb",
17
+ children: /* @__PURE__ */ jsx("ol", {
18
+ className: "qx-breadcrumb-list",
19
+ children
20
+ })
21
+ });
22
+ }
23
+ /** One step. A long label truncates and keeps its full text as a title. */
24
+ function BreadcrumbItem({ href, render, current, className, children, ...props }) {
25
+ const title = typeof children === "string" ? children : void 0;
26
+ return /* @__PURE__ */ jsxs("li", {
27
+ ...props,
28
+ className: cx("qx-breadcrumb-item", className),
29
+ "data-slot": "breadcrumb-item",
30
+ children: [/* @__PURE__ */ jsx(ChevronRightIcon, {
31
+ shape: "sharp",
32
+ size: 12,
33
+ "aria-hidden": true,
34
+ className: "qx-breadcrumb-separator"
35
+ }), current ? /* @__PURE__ */ jsx("span", {
36
+ className: "qx-breadcrumb-current",
37
+ "aria-current": "page",
38
+ title,
39
+ children
40
+ }) : renderElement("a", render, {
41
+ href,
42
+ className: "qx-breadcrumb-link",
43
+ title,
44
+ children
45
+ })]
46
+ });
47
+ }
48
+ export { Breadcrumb, BreadcrumbItem };
@@ -0,0 +1,2 @@
1
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbItemProps, BreadcrumbProps } from "./breadcrumb.js";
2
+ export { Breadcrumb, BreadcrumbItem, type BreadcrumbItemProps, type BreadcrumbProps };
@@ -0,0 +1,27 @@
1
+ import { IconButtonProps } from "../../button/button.js";
2
+ import { QeetrixMessages } from "../../internal/messages.js";
3
+ import { ReactElement, ReactNode } from "react";
4
+ /** Where a copy stands: ready, just copied, or failed (no clipboard access). */
5
+ export type CopyButtonState = "idle" | "copied" | "failed";
6
+ export interface CopyButtonProps extends Omit<IconButtonProps, "icon" | "aria-label" | "tooltip" | "loading" | "onClick" | "onCopy" | "value"> {
7
+ /** The text to copy, or a function that returns it (it may be async, e.g. a fresh token). */
8
+ value: string | (() => string | Promise<string>);
9
+ /** What it copies, as the button's name and tooltip: "Copy user ID". Default "Copy". */
10
+ label?: string;
11
+ /** A visible label makes it a Button (label first, icon last) instead of an IconButton. */
12
+ children?: ReactNode;
13
+ /** Called with the text once it's on the clipboard. */
14
+ onCopied?: (value: string) => void;
15
+ /** Called when copying fails, e.g. no clipboard access outside a secure context. */
16
+ onCopyError?: (error: unknown) => void;
17
+ /** How long "Copied" (or the failure) shows before it resets, in milliseconds. Default 2000. */
18
+ resetAfter?: number;
19
+ /** This button's strings, over QeetrixProvider's and the English defaults. */
20
+ messages?: Partial<QeetrixMessages["copyButton"]>;
21
+ }
22
+ /**
23
+ * Copies text to the clipboard (07-components.md, C2). Focus stays on the button; the icon turns
24
+ * into a check, and "Copied" is announced politely, then it resets. It fails visibly, never
25
+ * silently, where the page has no clipboard access.
26
+ */
27
+ export declare function CopyButton({ value, label, children, onCopied, onCopyError, resetAfter, messages, variant, tone, className, ...props }: CopyButtonProps): ReactElement;
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { cx } from "../../internal/cx.js";
3
+ import { Button, IconButton } from "../../button/button.js";
4
+ import { useMessages } from "../../provider/provider.js";
5
+ import { VisuallyHidden } from "../../visually-hidden/visually-hidden.js";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { useEffect, useRef, useState } from "react";
8
+ import { CheckIcon } from "@qeetrix/icons/icons/check";
9
+ import { CircleAlertIcon } from "@qeetrix/icons/icons/circle-alert";
10
+ import { CopyIcon } from "@qeetrix/icons/icons/copy";
11
+ // src/experimental/copy-button/copy-button.tsx
12
+ /**
13
+ * Copies text to the clipboard (07-components.md, C2). Focus stays on the button; the icon turns
14
+ * into a check, and "Copied" is announced politely, then it resets. It fails visibly, never
15
+ * silently, where the page has no clipboard access.
16
+ */
17
+ function CopyButton({ value, label, children, onCopied, onCopyError, resetAfter = 2e3, messages, variant = "ghost", tone = "neutral", className, ...props }) {
18
+ const strings = useMessages("copyButton", messages);
19
+ const [state, setState] = useState("idle");
20
+ const timer = useRef(void 0);
21
+ useEffect(() => () => window.clearTimeout(timer.current), []);
22
+ const copy = async () => {
23
+ window.clearTimeout(timer.current);
24
+ setState("idle");
25
+ try {
26
+ const text = typeof value === "function" ? await value() : value;
27
+ if (!navigator.clipboard?.writeText) throw new Error("The clipboard isn't available here.");
28
+ await navigator.clipboard.writeText(text);
29
+ setState("copied");
30
+ onCopied?.(text);
31
+ } catch (error) {
32
+ setState("failed");
33
+ onCopyError?.(error);
34
+ }
35
+ timer.current = window.setTimeout(() => setState("idle"), resetAfter);
36
+ };
37
+ const Icon = state === "copied" ? CheckIcon : state === "failed" ? CircleAlertIcon : CopyIcon;
38
+ const announcement = state === "copied" ? strings.copied : state === "failed" ? strings.failed : "";
39
+ const name = label ?? strings.copy;
40
+ const shared = {
41
+ ...props,
42
+ variant,
43
+ tone,
44
+ icon: Icon,
45
+ className: cx("qx-copy-button", className),
46
+ "data-state": state,
47
+ onClick: copy
48
+ };
49
+ return /* @__PURE__ */ jsxs(Fragment, { children: [children != null ? /* @__PURE__ */ jsx(Button, {
50
+ ...shared,
51
+ children
52
+ }) : /* @__PURE__ */ jsx(IconButton, {
53
+ ...shared,
54
+ "aria-label": name,
55
+ tooltip: announcement || name
56
+ }), /* @__PURE__ */ jsx(VisuallyHidden, {
57
+ role: "status",
58
+ children: announcement
59
+ })] });
60
+ }
61
+ export { CopyButton };
@@ -0,0 +1,2 @@
1
+ import { CopyButton, CopyButtonProps, CopyButtonState } from "./copy-button.js";
2
+ export { CopyButton, type CopyButtonProps, type CopyButtonState };
@@ -0,0 +1,23 @@
1
+ import { QeetrixMessages } from "../../internal/messages.js";
2
+ import { ReactElement, ReactNode } from "react";
3
+ export interface DataStateProps {
4
+ /** The content is loading. It wins over every other state. */
5
+ loading?: boolean;
6
+ /** Anything truthy shows the error state. The raw error is never shown: log it in the product. */
7
+ error?: unknown;
8
+ /** The content loaded and there's nothing in it. */
9
+ empty?: boolean;
10
+ /** The EmptyState to show when empty. */
11
+ emptyState?: ReactNode;
12
+ /** Adds a "Try again" button to the error state. */
13
+ onRetry?: () => void;
14
+ /** The content, shown when it's loaded and not empty. */
15
+ children?: ReactNode;
16
+ /** The error state's strings, over QeetrixProvider's and the English defaults. */
17
+ messages?: Partial<QeetrixMessages["dataState"]>;
18
+ }
19
+ /**
20
+ * The four states of loaded content in one place (07-components.md, C1): loading, then error, then
21
+ * empty, then the content.
22
+ */
23
+ export declare function DataState({ loading, error, empty, emptyState, onRetry, children, messages }: DataStateProps): ReactElement;
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { Spinner } from "../../spinner/spinner.js";
3
+ import { Button } from "../../button/button.js";
4
+ import { useMessages } from "../../provider/provider.js";
5
+ import { EmptyState } from "./empty-state.js";
6
+ import { Fragment, jsx } from "react/jsx-runtime";
7
+ import { CircleAlertIcon } from "@qeetrix/icons/icons/circle-alert";
8
+ // src/experimental/empty-state/data-state.tsx
9
+ /**
10
+ * The four states of loaded content in one place (07-components.md, C1): loading, then error, then
11
+ * empty, then the content.
12
+ */
13
+ function DataState({ loading, error, empty, emptyState, onRetry, children, messages }) {
14
+ const strings = useMessages("dataState", messages);
15
+ const spinner = useMessages("spinner");
16
+ if (loading) return /* @__PURE__ */ jsx("div", {
17
+ className: "qx-data-state",
18
+ "data-slot": "data-state",
19
+ "aria-busy": "true",
20
+ children: /* @__PURE__ */ jsx(Spinner, {
21
+ size: "lg",
22
+ label: spinner.label
23
+ })
24
+ });
25
+ if (error) return /* @__PURE__ */ jsx(EmptyState, {
26
+ size: "section",
27
+ tone: "danger",
28
+ icon: CircleAlertIcon,
29
+ title: strings.errorTitle,
30
+ description: strings.errorDescription,
31
+ actions: onRetry && /* @__PURE__ */ jsx(Button, {
32
+ variant: "soft",
33
+ onClick: onRetry,
34
+ children: strings.retry
35
+ })
36
+ });
37
+ if (empty) return /* @__PURE__ */ jsx(Fragment, { children: emptyState });
38
+ return /* @__PURE__ */ jsx(Fragment, { children });
39
+ }
40
+ export { DataState };
@@ -0,0 +1,26 @@
1
+ import { HeadingLevel } from "../../heading/heading.js";
2
+ import { QeetrixIcon } from "../../internal/control.js";
3
+ import { ComponentProps, ReactElement, ReactNode } from "react";
4
+ export type EmptyStateSize = "page" | "section";
5
+ export type EmptyStateTone = "neutral" | "danger";
6
+ export interface EmptyStateProps extends Omit<ComponentProps<"div">, "title"> {
7
+ /** A 24px sharp icon from `@qeetrix/icons`. */
8
+ icon?: QeetrixIcon;
9
+ /** What's missing or what went wrong, in a few words: "No users yet". */
10
+ title: ReactNode;
11
+ /** One or two sentences: why, and what to do. */
12
+ description?: ReactNode;
13
+ /** Buttons, one primary at most. */
14
+ actions?: ReactNode;
15
+ /** `page` (default) for a whole view; `section` inside a card or table. */
16
+ size?: EmptyStateSize;
17
+ /** `danger` colours the icon for an error. Default `neutral`. */
18
+ tone?: EmptyStateTone;
19
+ /** Make the title a heading, when this is the page's main message. Otherwise it's styled text. */
20
+ level?: HeadingLevel;
21
+ }
22
+ /**
23
+ * The message where content would be: nothing yet, no results, or an error (07-components.md, C1).
24
+ * Centred, at most 480px wide. Server-safe.
25
+ */
26
+ export declare function EmptyState({ icon: Icon, title, description, actions, size, tone, level, className, ...props }: EmptyStateProps): ReactElement;
@@ -0,0 +1,46 @@
1
+ import { cx } from "../../internal/cx.js";
2
+ import { Heading } from "../../heading/heading.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/experimental/empty-state/empty-state.tsx
5
+ /**
6
+ * The message where content would be: nothing yet, no results, or an error (07-components.md, C1).
7
+ * Centred, at most 480px wide. Server-safe.
8
+ */
9
+ function EmptyState({ icon: Icon, title, description, actions, size = "page", tone = "neutral", level, className, ...props }) {
10
+ return /* @__PURE__ */ jsxs("div", {
11
+ ...props,
12
+ className: cx("qx-empty-state", className),
13
+ "data-slot": "empty-state",
14
+ "data-size": size,
15
+ "data-tone": tone,
16
+ children: [
17
+ Icon && /* @__PURE__ */ jsx(Icon, {
18
+ shape: "sharp",
19
+ size: 24,
20
+ "aria-hidden": true,
21
+ className: "qx-empty-state-icon"
22
+ }),
23
+ level !== void 0 ? /* @__PURE__ */ jsx(Heading, {
24
+ level,
25
+ size: size === "page" ? "title-md" : "title-sm",
26
+ align: "center",
27
+ children: title
28
+ }) : /* @__PURE__ */ jsx("p", {
29
+ className: "qx-empty-state-title",
30
+ "data-slot": "empty-state-title",
31
+ children: title
32
+ }),
33
+ description != null && /* @__PURE__ */ jsx("p", {
34
+ className: "qx-empty-state-description",
35
+ "data-slot": "empty-state-description",
36
+ children: description
37
+ }),
38
+ actions != null && /* @__PURE__ */ jsx("div", {
39
+ className: "qx-empty-state-actions",
40
+ "data-slot": "empty-state-actions",
41
+ children: actions
42
+ })
43
+ ]
44
+ });
45
+ }
46
+ export { EmptyState };
@@ -0,0 +1,3 @@
1
+ import { DataState, DataStateProps } from "./data-state.js";
2
+ import { EmptyState, EmptyStateProps, EmptyStateSize, EmptyStateTone } from "./empty-state.js";
3
+ export { DataState, type DataStateProps, EmptyState, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone };
@@ -0,0 +1,24 @@
1
+ import { Alert, AlertProps, AlertTone } from "./alert/alert.js";
2
+ import "./alert/index.js";
3
+ import { AppShell, AppShellDensity, AppShellDrawer, AppShellProps, AppShellState, useAppShell } from "./app-shell/app-shell.js";
4
+ import "./app-shell/index.js";
5
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbItemProps, BreadcrumbProps } from "./breadcrumb/breadcrumb.js";
6
+ import "./breadcrumb/index.js";
7
+ import { CopyButton, CopyButtonProps, CopyButtonState } from "./copy-button/copy-button.js";
8
+ import "./copy-button/index.js";
9
+ import { DataState, DataStateProps } from "./empty-state/data-state.js";
10
+ import { EmptyState, EmptyStateProps, EmptyStateSize, EmptyStateTone } from "./empty-state/empty-state.js";
11
+ import "./empty-state/index.js";
12
+ import { OTPInput, OTPInputProps, OTPInputSize } from "./otp-input/otp-input.js";
13
+ import "./otp-input/index.js";
14
+ import { PageContent, PageContentProps, PageContentWidth, PageHeader, PageHeaderProps } from "./page-header/page-header.js";
15
+ import "./page-header/index.js";
16
+ import { PasswordInput, PasswordInputProps } from "./password-input/password-input.js";
17
+ import "./password-input/index.js";
18
+ import { Sidebar, SidebarGroup, SidebarGroupProps, SidebarItem, SidebarItemProps, SidebarProps } from "./sidebar/sidebar.js";
19
+ import "./sidebar/index.js";
20
+ import { Stat, StatGroup, StatGroupProps, StatProps, StatTone } from "./stat/stat.js";
21
+ import "./stat/index.js";
22
+ import { TopBar, TopBarProps, TopBarSearch, TopBarSearchProps, TopBarStatus, TopBarSwitcher, TopBarSwitcherProps } from "./top-bar/top-bar.js";
23
+ import "./top-bar/index.js";
24
+ export { Alert, type AlertProps, type AlertTone, AppShell, type AppShellDensity, type AppShellDrawer, type AppShellProps, type AppShellState, Breadcrumb, BreadcrumbItem, type BreadcrumbItemProps, type BreadcrumbProps, CopyButton, type CopyButtonProps, type CopyButtonState, DataState, type DataStateProps, EmptyState, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone, OTPInput, type OTPInputProps, type OTPInputSize, PageContent, type PageContentProps, type PageContentWidth, PageHeader, type PageHeaderProps, PasswordInput, type PasswordInputProps, Sidebar, SidebarGroup, type SidebarGroupProps, SidebarItem, type SidebarItemProps, type SidebarProps, Stat, StatGroup, type StatGroupProps, type StatProps, type StatTone, TopBar, type TopBarProps, TopBarSearch, type TopBarSearchProps, type TopBarStatus, TopBarSwitcher, type TopBarSwitcherProps, useAppShell };
@@ -0,0 +1,13 @@
1
+ import { Alert } from "./alert/alert.js";
2
+ import { AppShell, useAppShell } from "./app-shell/app-shell.js";
3
+ import { Breadcrumb, BreadcrumbItem } from "./breadcrumb/breadcrumb.js";
4
+ import { CopyButton } from "./copy-button/copy-button.js";
5
+ import { EmptyState } from "./empty-state/empty-state.js";
6
+ import { DataState } from "./empty-state/data-state.js";
7
+ import { OTPInput } from "./otp-input/otp-input.js";
8
+ import { PageContent, PageHeader } from "./page-header/page-header.js";
9
+ import { PasswordInput } from "./password-input/password-input.js";
10
+ import { Sidebar, SidebarGroup, SidebarItem } from "./sidebar/sidebar.js";
11
+ import { Stat, StatGroup } from "./stat/stat.js";
12
+ import { TopBar, TopBarSearch, TopBarSwitcher } from "./top-bar/top-bar.js";
13
+ export { Alert, AppShell, Breadcrumb, BreadcrumbItem, CopyButton, DataState, EmptyState, OTPInput, PageContent, PageHeader, PasswordInput, Sidebar, SidebarGroup, SidebarItem, Stat, StatGroup, TopBar, TopBarSearch, TopBarSwitcher, useAppShell };
@@ -0,0 +1,2 @@
1
+ import { OTPInput, OTPInputProps, OTPInputSize } from "./otp-input.js";
2
+ export { OTPInput, type OTPInputProps, type OTPInputSize };
@@ -0,0 +1,27 @@
1
+ import { QeetrixMessages } from "../../internal/messages.js";
2
+ import { CSSProperties, ReactElement } from "react";
3
+ import { OTPField } from "@base-ui/react/otp-field";
4
+ export type OTPInputSize = "md" | "lg";
5
+ export interface OTPInputProps extends Omit<OTPField.Root.Props, "className" | "style" | "children" | "render" | "length"> {
6
+ /** How many characters. Default 6. */
7
+ length?: number;
8
+ /** Split the slots into groups, e.g. `[3, 3]` shows 123 – 456. The sizes must add up to `length`. */
9
+ groups?: readonly number[];
10
+ /** `md` (default) in consoles; `lg` on sign-in pages. */
11
+ size?: OTPInputSize;
12
+ /** Names the first slot when there's no Field label. */
13
+ "aria-label"?: string;
14
+ "aria-labelledby"?: string;
15
+ /** Placement only. */
16
+ className?: string;
17
+ style?: CSSProperties;
18
+ /** This input's strings, over QeetrixProvider's and the English defaults. */
19
+ messages?: Partial<QeetrixMessages["otpInput"]>;
20
+ }
21
+ /**
22
+ * A one-time code in separate slots (07-components.md, C2), on Base UI's OTP Field: typing moves on,
23
+ * Backspace moves back, pasting fills every slot, and the phone's SMS autofill
24
+ * (`autocomplete="one-time-code"`) lands in one go. Numeric by default. The first slot takes the
25
+ * field's label; the others are announced as "Character 2 of 6".
26
+ */
27
+ export declare function OTPInput({ length, groups, size, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, style, messages, ...props }: OTPInputProps): ReactElement;
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { cx } from "../../internal/cx.js";
3
+ import { useMessages } from "../../provider/provider.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment as Fragment$1 } from "react";
6
+ import { OTPField } from "@base-ui/react/otp-field";
7
+ // src/experimental/otp-input/otp-input.tsx
8
+ /**
9
+ * A one-time code in separate slots (07-components.md, C2), on Base UI's OTP Field: typing moves on,
10
+ * Backspace moves back, pasting fills every slot, and the phone's SMS autofill
11
+ * (`autocomplete="one-time-code"`) lands in one go. Numeric by default. The first slot takes the
12
+ * field's label; the others are announced as "Character 2 of 6".
13
+ */
14
+ function OTPInput({ length = 6, groups, size = "md", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, style, messages, ...props }) {
15
+ const strings = useMessages("otpInput", messages);
16
+ const breaks = /* @__PURE__ */ new Set();
17
+ let at = 0;
18
+ for (const group of groups ?? []) {
19
+ at += group;
20
+ if (at < length) breaks.add(at);
21
+ }
22
+ return /* @__PURE__ */ jsx(OTPField.Root, {
23
+ ...props,
24
+ length,
25
+ className: cx("qx-otp-input", className),
26
+ style,
27
+ "data-slot": "otp-input",
28
+ "data-size": size,
29
+ children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [breaks.has(index) && /* @__PURE__ */ jsx(OTPField.Separator, { className: "qx-otp-input-separator" }), /* @__PURE__ */ jsx(OTPField.Input, {
30
+ className: "qx-otp-input-slot",
31
+ "data-slot": "otp-input-slot",
32
+ ...index === 0 ? {
33
+ "aria-label": ariaLabel,
34
+ "aria-labelledby": ariaLabelledBy
35
+ } : { "aria-label": strings.character.replace("{index}", String(index + 1)).replace("{length}", String(length)) }
36
+ })] }, index))
37
+ });
38
+ }
39
+ export { OTPInput };
@@ -0,0 +1,2 @@
1
+ import { PageContent, PageContentProps, PageContentWidth, PageHeader, PageHeaderProps } from "./page-header.js";
2
+ export { PageContent, type PageContentProps, type PageContentWidth, PageHeader, type PageHeaderProps };
@@ -0,0 +1,32 @@
1
+ import { ComponentProps, ReactElement, ReactNode } from "react";
2
+ export interface PageHeaderProps extends Omit<ComponentProps<"div">, "title"> {
3
+ /** Where the page sits: a Breadcrumb. */
4
+ breadcrumb?: ReactNode;
5
+ /** The page title: the page's one `<h1>`. */
6
+ title: ReactNode;
7
+ /** A number beside the title, e.g. the table's total; numbers are formatted in `en-IN`. */
8
+ count?: ReactNode;
9
+ /** A Badge beside the title. */
10
+ status?: ReactNode;
11
+ /** One line under the title. */
12
+ description?: ReactNode;
13
+ /** Buttons at the end: the primary last; at most one solid and one soft, the rest in a menu. */
14
+ actions?: ReactNode;
15
+ /** Key numbers for the page: a StatGroup, ruled edge to edge under the title. */
16
+ summary?: ReactNode;
17
+ /** A TabsList, attached to the header's foot. */
18
+ tabs?: ReactNode;
19
+ }
20
+ export type PageContentWidth = "full" | "narrow";
21
+ export interface PageContentProps extends ComponentProps<"div"> {
22
+ /** `full` (default) for tables and dashboards; `narrow` (800px) for forms and settings. */
23
+ width?: PageContentWidth;
24
+ }
25
+ /**
26
+ * The head of a console page (07-components.md, C1 · Ruled): breadcrumb, title with its count and
27
+ * status, actions, an optional strip of key numbers, and tabs, on a ruled band above the work
28
+ * surface. Server-safe.
29
+ */
30
+ export declare function PageHeader({ breadcrumb, title, count, status, description, actions, summary, tabs, className, ...props }: PageHeaderProps): ReactElement;
31
+ /** The work surface under a PageHeader. Server-safe. */
32
+ export declare function PageContent({ width, className, ...props }: PageContentProps): ReactElement;
@@ -0,0 +1,73 @@
1
+ import { cx } from "../../internal/cx.js";
2
+ import { Heading } from "../../heading/heading.js";
3
+ import { formatNumber } from "../../format/format.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ // src/experimental/page-header/page-header.tsx
6
+ /**
7
+ * The head of a console page (07-components.md, C1 · Ruled): breadcrumb, title with its count and
8
+ * status, actions, an optional strip of key numbers, and tabs, on a ruled band above the work
9
+ * surface. Server-safe.
10
+ */
11
+ function PageHeader({ breadcrumb, title, count, status, description, actions, summary, tabs, className, ...props }) {
12
+ return /* @__PURE__ */ jsxs("div", {
13
+ ...props,
14
+ className: cx("qx-page-header", className),
15
+ "data-slot": "page-header",
16
+ "data-foot": tabs != null ? "tabs" : summary != null ? "summary" : void 0,
17
+ children: [
18
+ breadcrumb != null && /* @__PURE__ */ jsx("div", {
19
+ className: "qx-page-header-breadcrumb",
20
+ children: breadcrumb
21
+ }),
22
+ /* @__PURE__ */ jsxs("div", {
23
+ className: "qx-page-header-row",
24
+ children: [/* @__PURE__ */ jsxs("div", {
25
+ className: "qx-page-header-titles",
26
+ children: [/* @__PURE__ */ jsxs("div", {
27
+ className: "qx-page-header-title",
28
+ children: [
29
+ /* @__PURE__ */ jsx(Heading, {
30
+ level: 1,
31
+ size: "title-lg",
32
+ children: title
33
+ }),
34
+ count != null && /* @__PURE__ */ jsx("span", {
35
+ className: "qx-page-header-count",
36
+ "data-slot": "page-header-count",
37
+ children: typeof count === "number" ? formatNumber(count) : count
38
+ }),
39
+ status
40
+ ]
41
+ }), description != null && /* @__PURE__ */ jsx("p", {
42
+ className: "qx-page-header-description",
43
+ "data-slot": "page-header-description",
44
+ children: description
45
+ })]
46
+ }), actions != null && /* @__PURE__ */ jsx("div", {
47
+ className: "qx-page-header-actions",
48
+ "data-slot": "page-header-actions",
49
+ children: actions
50
+ })]
51
+ }),
52
+ summary != null && /* @__PURE__ */ jsx("div", {
53
+ className: "qx-page-header-summary",
54
+ "data-slot": "page-header-summary",
55
+ children: summary
56
+ }),
57
+ tabs != null && /* @__PURE__ */ jsx("div", {
58
+ className: "qx-page-header-tabs",
59
+ children: tabs
60
+ })
61
+ ]
62
+ });
63
+ }
64
+ /** The work surface under a PageHeader. Server-safe. */
65
+ function PageContent({ width = "full", className, ...props }) {
66
+ return /* @__PURE__ */ jsx("div", {
67
+ ...props,
68
+ className: cx("qx-page-content", className),
69
+ "data-slot": "page-content",
70
+ "data-width": width
71
+ });
72
+ }
73
+ export { PageContent, PageHeader };
@@ -0,0 +1,2 @@
1
+ import { PasswordInput, PasswordInputProps } from "./password-input.js";
2
+ export { PasswordInput, type PasswordInputProps };
@@ -0,0 +1,26 @@
1
+ import { QeetrixMessages } from "../../internal/messages.js";
2
+ import { InputProps } from "../../input/input.js";
3
+ import { ReactElement } from "react";
4
+ export interface PasswordInputProps extends Omit<InputProps, "type"> {
5
+ /**
6
+ * `current-password` (default) to sign in, `new-password` to create one, so password managers
7
+ * fill or suggest; `off` for a secret that isn't the user's password, such as an API key.
8
+ */
9
+ autoComplete?: "current-password" | "new-password" | "off";
10
+ /** Whether the value shows as text, controlled. */
11
+ visible?: boolean;
12
+ /** Whether it starts visible when uncontrolled. Default `false`: people may not be alone. */
13
+ defaultVisible?: boolean;
14
+ /** Called with the new visibility. */
15
+ onVisibleChange?: (visible: boolean) => void;
16
+ /** Show "Caps Lock is on" while typing with it on. Default `true`. */
17
+ capsLockWarning?: boolean;
18
+ /** This input's strings, over QeetrixProvider's and the English defaults. */
19
+ messages?: Partial<QeetrixMessages["passwordInput"]>;
20
+ }
21
+ /**
22
+ * A password or secret field (07-components.md, C2): an Input with a Show password toggle. It
23
+ * hides again when its form is submitted, so the browser never stores it as plain text; spelling
24
+ * and capitalisation tools stay off; paste always works, and there's no length cap.
25
+ */
26
+ export declare function PasswordInput({ autoComplete, visible, defaultVisible, onVisibleChange, capsLockWarning, messages, endAdornment, disabled, ref, onKeyDown, onKeyUp, onBlur, className, ...props }: PasswordInputProps): ReactElement;