@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.
- package/CHANGELOG.md +116 -1
- package/README.md +99 -0
- package/dist/button/button.d.ts +6 -2
- package/dist/button/button.js +5 -1
- package/dist/css/avatar.css +6 -0
- package/dist/css/badge.css +6 -0
- package/dist/css/button.css +81 -6
- package/dist/css/checkbox.css +9 -0
- package/dist/css/dialog.css +7 -0
- package/dist/css/drawer.css +6 -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/input.css +10 -0
- package/dist/css/menu.css +17 -0
- package/dist/css/progress.css +14 -0
- package/dist/css/scroll-area.css +7 -0
- package/dist/css/select.css +16 -0
- package/dist/css/separator.css +7 -0
- package/dist/css/skeleton.css +6 -0
- package/dist/css/slider.css +22 -0
- package/dist/css/switch.css +33 -0
- package/dist/css/tabs.css +11 -0
- package/dist/css/toast.css +7 -0
- package/dist/css/toggle.css +13 -0
- package/dist/css/tooltip.css +6 -0
- package/dist/dialog/dialog.d.ts +4 -1
- package/dist/dialog/dialog.js +4 -3
- package/dist/drawer/drawer.d.ts +4 -1
- package/dist/drawer/drawer.js +5 -4
- 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/field.d.ts +4 -1
- package/dist/field/field.js +6 -5
- 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/index.d.ts +6 -2
- package/dist/index.js +3 -1
- package/dist/internal/media.js +15 -0
- package/dist/internal/messages.d.ts +75 -0
- package/dist/internal/messages.js +44 -5
- package/dist/link/link.d.ts +7 -1
- package/dist/link/link.js +3 -2
- package/dist/locales/hi.d.ts +8 -0
- package/dist/locales/hi.js +60 -0
- package/dist/progress/progress.d.ts +2 -2
- package/dist/progress/progress.js +7 -2
- 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/slider/slider.d.ts +5 -2
- package/dist/slider/slider.js +10 -3
- package/dist/styles.css +1704 -104
- package/dist/text-field/text-field.d.ts +2 -2
- package/dist/text-field/text-field.js +4 -2
- package/dist/toast/toast.d.ts +4 -1
- package/dist/toast/toast.js +7 -6
- package/package.json +18 -2
|
@@ -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,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,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,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,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,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;
|