@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,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 };
@@ -1,3 +1,4 @@
1
+ import { QeetrixMessages } from "../internal/messages.js";
1
2
  import { CSSProperties, ReactElement, ReactNode } from "react";
2
3
  import { Field } from "@base-ui/react/field";
3
4
  /** The help text and error every labelled field shares. */
@@ -17,6 +18,8 @@ export interface FieldProps extends Omit<Field.Root.Props, "className" | "style"
17
18
  optional?: boolean;
18
19
  /** Hides the label visually and keeps it for assistive technology. Only for a toolbar search. */
19
20
  hideLabel?: boolean;
21
+ /** This field's strings, over QeetrixProvider's and the English defaults. */
22
+ messages?: Partial<QeetrixMessages["field"]>;
20
23
  /** Placement only (margin, width, grid placement). */
21
24
  className?: string;
22
25
  style?: CSSProperties;
@@ -24,5 +27,5 @@ export interface FieldProps extends Omit<Field.Root.Props, "className" | "style"
24
27
  children?: ReactNode;
25
28
  }
26
29
  /** The label, help text and error for one control (06-component-specs.md §2). */
27
- declare function Field$1({ label, description, error, optional, hideLabel, invalid, className, children, ...props }: FieldProps): ReactElement;
30
+ declare function Field$1({ label, description, error, optional, hideLabel, invalid, messages, className, children, ...props }: FieldProps): ReactElement;
28
31
  export { Field$1 as Field };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { cx } from "../internal/cx.js";
3
- import { messages } from "../internal/messages.js";
3
+ import { useMessages } from "../provider/provider.js";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { createContext } from "react";
6
6
  import { Field } from "@base-ui/react/field";
@@ -37,7 +37,7 @@ function FieldMessages({ description, error }) {
37
37
  })] });
38
38
  }
39
39
  /** The label text, with "(optional)" when the field may be left empty. */
40
- function labelText(label, optional) {
40
+ function labelText(label, optional, optionalText) {
41
41
  if (!optional) return label;
42
42
  return /* @__PURE__ */ jsxs(Fragment, { children: [
43
43
  label,
@@ -45,12 +45,13 @@ function labelText(label, optional) {
45
45
  /* @__PURE__ */ jsx("span", {
46
46
  className: "qx-field-optional",
47
47
  "data-slot": "field-optional",
48
- children: messages.field.optional
48
+ children: optionalText
49
49
  })
50
50
  ] });
51
51
  }
52
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, className, children, ...props }) {
53
+ function Field$1({ label, description, error, optional, hideLabel, invalid, messages, className, children, ...props }) {
54
+ const strings = useMessages("field", messages);
54
55
  return /* @__PURE__ */ jsxs(Field.Root, {
55
56
  ...props,
56
57
  invalid: invalid ?? (error != null ? true : void 0),
@@ -61,7 +62,7 @@ function Field$1({ label, description, error, optional, hideLabel, invalid, clas
61
62
  className: "qx-field-label",
62
63
  "data-slot": "field-label",
63
64
  "data-hidden": hideLabel ? "" : void 0,
64
- children: labelText(label, optional)
65
+ children: labelText(label, optional, strings.optional)
65
66
  }),
66
67
  /* @__PURE__ */ jsx(InFieldContext, {
67
68
  value: true,
@@ -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,2 @@
1
+ import { formatCurrency, formatNumber, formatRelativeTime } from "./format.js";
2
+ export { formatCurrency, formatNumber, formatRelativeTime };
@@ -0,0 +1 @@
1
+ export { formatCurrency, formatNumber, formatRelativeTime } from "./format.js";
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ import { Button, ButtonProps, ButtonTone, ButtonVariant, IconButton, IconButtonP
15
15
  import "./button/index.js";
16
16
  import { Card, CardProps } from "./card/card.js";
17
17
  import "./card/index.js";
18
+ import { QeetrixMessages, QeetrixMessagesInput } from "./internal/messages.js";
18
19
  import { Field, FieldMessageProps, FieldProps } from "./field/field.js";
19
20
  import { ChoiceLabelProps, ChoiceOrientation } from "./field/choice.js";
20
21
  import { Checkbox, CheckboxGroup, CheckboxGroupProps, CheckboxProps } from "./checkbox/checkbox.js";
@@ -23,16 +24,17 @@ import { Dialog, DialogBody, DialogBodyProps, DialogClose, DialogCloseProps, Dia
23
24
  import "./dialog/index.js";
24
25
  import { Drawer, DrawerBody, DrawerBodyProps, DrawerClose, DrawerCloseProps, DrawerContent, DrawerContentProps, DrawerFooter, DrawerFooterProps, DrawerProps, DrawerSide, DrawerSize, DrawerTrigger, DrawerTriggerProps } from "./drawer/drawer.js";
25
26
  import "./drawer/index.js";
27
+ import { Input, InputProps, Textarea, TextareaProps } from "./input/input.js";
26
28
  import "./field/index.js";
27
29
  import { Fieldset, FieldsetProps } from "./fieldset/fieldset.js";
28
30
  import "./fieldset/index.js";
31
+ import { formatCurrency, formatNumber, formatRelativeTime } from "./format/format.js";
29
32
  import { Grid, GridAlign, GridColumnCount, GridColumns, GridProps } from "./grid/grid.js";
30
33
  import "./grid/index.js";
31
34
  import "./heading/index.js";
32
35
  import { FlowJustify, Stack, StackAlign, StackProps } from "./stack/stack.js";
33
36
  import { Inline, InlineAlign, InlineProps } from "./inline/inline.js";
34
37
  import "./inline/index.js";
35
- import { Input, InputProps, Textarea, TextareaProps } from "./input/input.js";
36
38
  import "./input/index.js";
37
39
  import { Link, LinkProps, LinkTone } from "./link/link.js";
38
40
  import "./link/index.js";
@@ -42,6 +44,8 @@ import { Popover, PopoverClose, PopoverCloseProps, PopoverContent, PopoverConten
42
44
  import "./popover/index.js";
43
45
  import { Meter, MeterProps, MeterTone, Progress, ProgressProps } from "./progress/progress.js";
44
46
  import "./progress/index.js";
47
+ import { ColorScheme, ColorSchemeState, Direction, QeetrixProvider, QeetrixProviderProps, ResolvedColorScheme, useColorScheme, useLocale } from "./provider/provider.js";
48
+ import "./provider/index.js";
45
49
  import { Radio, RadioGroup, RadioGroupProps, RadioProps } from "./radio/radio.js";
46
50
  import "./radio/index.js";
47
51
  import { ScrollArea, ScrollAreaOrientation, ScrollAreaProps } from "./scroll-area/scroll-area.js";
@@ -77,4 +81,4 @@ import { Tooltip, TooltipProps, TooltipProvider, TooltipProviderProps } from "./
77
81
  import "./tooltip/index.js";
78
82
  import { VisuallyHidden, VisuallyHiddenProps } from "./visually-hidden/visually-hidden.js";
79
83
  import "./visually-hidden/index.js";
80
- export { Accordion, AccordionItem, type AccordionItemProps, type AccordionProps, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeTone, Box, type BoxBackground, type BoxBorder, type BoxProps, type BoxRadius, Button, type ButtonProps, type ButtonTone, type ButtonVariant, Card, type CardProps, Checkbox, CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, type ChoiceLabelProps, type ChoiceOrientation, Collapsible, CollapsiblePanel, type CollapsiblePanelProps, type CollapsibleProps, CollapsibleTrigger, type CollapsibleTriggerProps, type ControlSize, Dialog, DialogBody, type DialogBodyProps, DialogClose, type DialogCloseProps, DialogContent, type DialogContentProps, DialogFooter, type DialogFooterProps, type DialogIntent, type DialogProps, type DialogSize, DialogTrigger, type DialogTriggerProps, Drawer, DrawerBody, type DrawerBodyProps, DrawerClose, type DrawerCloseProps, DrawerContent, type DrawerContentProps, DrawerFooter, type DrawerFooterProps, type DrawerProps, type DrawerSide, type DrawerSize, DrawerTrigger, type DrawerTriggerProps, Field, type FieldMessageProps, type FieldProps, Fieldset, type FieldsetProps, type FlowJustify, Grid, type GridAlign, type GridColumnCount, type GridColumns, type GridProps, Heading, type HeadingLevel, type HeadingProps, type HeadingSize, type HeadingTone, IconButton, type IconButtonProps, Inline, type InlineAlign, type InlineProps, Input, type InputProps, type Inset, Link, type LinkProps, type LinkTone, Menu, MenuCheckboxItem, type MenuCheckboxItemProps, MenuContent, type MenuContentProps, MenuGroup, MenuGroupLabel, type MenuGroupLabelProps, type MenuGroupProps, MenuItem, type MenuItemProps, type MenuItemTone, type MenuProps, MenuRadioGroup, type MenuRadioGroupProps, MenuRadioItem, type MenuRadioItemProps, MenuSeparator, type MenuSeparatorProps, MenuSubmenu, type MenuSubmenuProps, MenuSubmenuTrigger, type MenuSubmenuTriggerProps, MenuTrigger, type MenuTriggerProps, Meter, type MeterProps, type MeterTone, Popover, PopoverClose, type PopoverCloseProps, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type PopoverTriggerProps, Progress, type ProgressProps, type QeetrixIcon, Radio, RadioGroup, type RadioGroupProps, type RadioProps, type RenderProp, ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, Select, type SelectItem, type SelectItemGroup, type SelectProps, Separator, type SeparatorOrientation, type SeparatorProps, type SeparatorTone, Skeleton, type SkeletonProps, type SkeletonVariant, SkipLink, type SkipLinkProps, Slider, type SliderProps, type Space, Spinner, type SpinnerProps, type SpinnerSize, type SpinnerTone, Stack, type StackAlign, type StackProps, Switch, type SwitchProps, Tab, type TabProps, Table, type TableAlign, TableBody, type TableBodyProps, TableCaption, type TableCaptionProps, TableCell, type TableCellProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, Tabs, TabsList, type TabsListProps, TabsPanel, type TabsPanelProps, type TabsProps, Text, type TextAlign, TextField, type TextFieldProps, type TextProps, type TextSize, type TextTone, type TextWeight, Textarea, type TextareaProps, type ToastApi, type ToastOptions, type ToastPromiseOptions, ToastProvider, type ToastProviderProps, type ToastTone, Toggle, ToggleGroup, type ToggleGroupProps, type ToggleGroupVariant, type ToggleProps, Tooltip, type TooltipProps, TooltipProvider, type TooltipProviderProps, type Truncate, VisuallyHidden, type VisuallyHiddenProps, useToast };
84
+ export { Accordion, AccordionItem, type AccordionItemProps, type AccordionProps, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeTone, Box, type BoxBackground, type BoxBorder, type BoxProps, type BoxRadius, Button, type ButtonProps, type ButtonTone, type ButtonVariant, Card, type CardProps, Checkbox, CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, type ChoiceLabelProps, type ChoiceOrientation, Collapsible, CollapsiblePanel, type CollapsiblePanelProps, type CollapsibleProps, CollapsibleTrigger, type CollapsibleTriggerProps, type ColorScheme, type ColorSchemeState, type ControlSize, Dialog, DialogBody, type DialogBodyProps, DialogClose, type DialogCloseProps, DialogContent, type DialogContentProps, DialogFooter, type DialogFooterProps, type DialogIntent, type DialogProps, type DialogSize, DialogTrigger, type DialogTriggerProps, type Direction, Drawer, DrawerBody, type DrawerBodyProps, DrawerClose, type DrawerCloseProps, DrawerContent, type DrawerContentProps, DrawerFooter, type DrawerFooterProps, type DrawerProps, type DrawerSide, type DrawerSize, DrawerTrigger, type DrawerTriggerProps, Field, type FieldMessageProps, type FieldProps, Fieldset, type FieldsetProps, type FlowJustify, Grid, type GridAlign, type GridColumnCount, type GridColumns, type GridProps, Heading, type HeadingLevel, type HeadingProps, type HeadingSize, type HeadingTone, IconButton, type IconButtonProps, Inline, type InlineAlign, type InlineProps, Input, type InputProps, type Inset, Link, type LinkProps, type LinkTone, Menu, MenuCheckboxItem, type MenuCheckboxItemProps, MenuContent, type MenuContentProps, MenuGroup, MenuGroupLabel, type MenuGroupLabelProps, type MenuGroupProps, MenuItem, type MenuItemProps, type MenuItemTone, type MenuProps, MenuRadioGroup, type MenuRadioGroupProps, MenuRadioItem, type MenuRadioItemProps, MenuSeparator, type MenuSeparatorProps, MenuSubmenu, type MenuSubmenuProps, MenuSubmenuTrigger, type MenuSubmenuTriggerProps, MenuTrigger, type MenuTriggerProps, Meter, type MeterProps, type MeterTone, Popover, PopoverClose, type PopoverCloseProps, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, type PopoverTriggerProps, Progress, type ProgressProps, type QeetrixIcon, type QeetrixMessages, type QeetrixMessagesInput, QeetrixProvider, type QeetrixProviderProps, Radio, RadioGroup, type RadioGroupProps, type RadioProps, type RenderProp, type ResolvedColorScheme, ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, Select, type SelectItem, type SelectItemGroup, type SelectProps, Separator, type SeparatorOrientation, type SeparatorProps, type SeparatorTone, Skeleton, type SkeletonProps, type SkeletonVariant, SkipLink, type SkipLinkProps, Slider, type SliderProps, type Space, Spinner, type SpinnerProps, type SpinnerSize, type SpinnerTone, Stack, type StackAlign, type StackProps, Switch, type SwitchProps, Tab, type TabProps, Table, type TableAlign, TableBody, type TableBodyProps, TableCaption, type TableCaptionProps, TableCell, type TableCellProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps, Tabs, TabsList, type TabsListProps, TabsPanel, type TabsPanelProps, type TabsProps, Text, type TextAlign, TextField, type TextFieldProps, type TextProps, type TextSize, type TextTone, type TextWeight, Textarea, type TextareaProps, type ToastApi, type ToastOptions, type ToastPromiseOptions, ToastProvider, type ToastProviderProps, type ToastTone, Toggle, ToggleGroup, type ToggleGroupProps, type ToggleGroupVariant, type ToggleProps, Tooltip, type TooltipProps, TooltipProvider, type TooltipProviderProps, type Truncate, VisuallyHidden, type VisuallyHiddenProps, formatCurrency, formatNumber, formatRelativeTime, useColorScheme, useLocale, useToast };
package/dist/index.js CHANGED
@@ -7,11 +7,13 @@ import { Tooltip, TooltipProvider } from "./tooltip/tooltip.js";
7
7
  import { Button, IconButton } from "./button/button.js";
8
8
  import { Heading } from "./heading/heading.js";
9
9
  import { Card } from "./card/card.js";
10
+ import { QeetrixProvider, useColorScheme, useLocale } from "./provider/provider.js";
10
11
  import { Field } from "./field/field.js";
11
12
  import { Checkbox, CheckboxGroup } from "./checkbox/checkbox.js";
12
13
  import { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogTrigger } from "./dialog/dialog.js";
13
14
  import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerTrigger } from "./drawer/drawer.js";
14
15
  import { Fieldset } from "./fieldset/fieldset.js";
16
+ import { formatCurrency, formatNumber, formatRelativeTime } from "./format/format.js";
15
17
  import { Grid } from "./grid/grid.js";
16
18
  import { Inline } from "./inline/inline.js";
17
19
  import { Input, Textarea } from "./input/input.js";
@@ -35,4 +37,4 @@ import { Text } from "./text/text.js";
35
37
  import { TextField } from "./text-field/text-field.js";
36
38
  import { ToastProvider, useToast } from "./toast/toast.js";
37
39
  import { Toggle, ToggleGroup } from "./toggle/toggle.js";
38
- export { Accordion, AccordionItem, Avatar, Badge, Box, Button, Card, Checkbox, CheckboxGroup, Collapsible, CollapsiblePanel, CollapsibleTrigger, Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogTrigger, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerTrigger, Field, Fieldset, Grid, Heading, IconButton, Inline, Input, Link, Menu, MenuCheckboxItem, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, Popover, PopoverClose, PopoverContent, PopoverTrigger, Progress, Radio, RadioGroup, ScrollArea, Select, Separator, Skeleton, SkipLink, Slider, Spinner, Stack, Switch, Tab, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsList, TabsPanel, Text, TextField, Textarea, ToastProvider, Toggle, ToggleGroup, Tooltip, TooltipProvider, VisuallyHidden, useToast };
40
+ export { Accordion, AccordionItem, Avatar, Badge, Box, Button, Card, Checkbox, CheckboxGroup, Collapsible, CollapsiblePanel, CollapsibleTrigger, Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogTrigger, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerTrigger, Field, Fieldset, Grid, Heading, IconButton, Inline, Input, Link, Menu, MenuCheckboxItem, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, Popover, PopoverClose, PopoverContent, PopoverTrigger, Progress, QeetrixProvider, Radio, RadioGroup, ScrollArea, Select, Separator, Skeleton, SkipLink, Slider, Spinner, Stack, Switch, Tab, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsList, TabsPanel, Text, TextField, Textarea, ToastProvider, Toggle, ToggleGroup, Tooltip, TooltipProvider, VisuallyHidden, formatCurrency, formatNumber, formatRelativeTime, useColorScheme, useLocale, useToast };
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { useSyncExternalStore } from "react";
3
+ // src/internal/media.ts
4
+ /**
5
+ * Whether a media query matches, kept in sync as it changes. On the server, and in the first client
6
+ * render, it's `serverValue`, so server and client markup agree.
7
+ */
8
+ function useMediaQuery(query, serverValue) {
9
+ return useSyncExternalStore((onChange) => {
10
+ const list = window.matchMedia(query);
11
+ list.addEventListener("change", onChange);
12
+ return () => list.removeEventListener("change", onChange);
13
+ }, () => window.matchMedia(query).matches, () => serverValue);
14
+ }
15
+ export { useMediaQuery };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Every user-facing string in the library, in English (decision D-13). A string resolves from the
3
+ * component's own `messages` prop, then QeetrixProvider, then these defaults. Locale packs
4
+ * (src/locales/) are typed as the complete catalogue, so a missing key fails the typecheck.
5
+ */
6
+ export declare const messages: {
7
+ readonly skipLink: {
8
+ readonly label: "Skip to main content";
9
+ };
10
+ readonly spinner: {
11
+ readonly label: "Loading";
12
+ };
13
+ readonly link: {
14
+ readonly external: "(opens in a new tab)";
15
+ };
16
+ readonly field: {
17
+ readonly optional: "(optional)";
18
+ };
19
+ readonly overlay: {
20
+ readonly close: "Close";
21
+ };
22
+ readonly toast: {
23
+ readonly close: "Dismiss";
24
+ readonly region: "Notifications";
25
+ };
26
+ readonly slider: {
27
+ readonly minimum: "Minimum";
28
+ readonly maximum: "Maximum";
29
+ };
30
+ readonly appShell: {
31
+ readonly collapse: "Collapse";
32
+ readonly expand: "Expand";
33
+ readonly open: "Open navigation";
34
+ readonly navigation: "Navigation";
35
+ readonly tools: "Tools";
36
+ readonly closePanel: "Close panel";
37
+ };
38
+ readonly sidebar: {
39
+ readonly filter: "Filter navigation";
40
+ readonly noMatches: "No pages match";
41
+ };
42
+ readonly breadcrumb: {
43
+ readonly label: "Breadcrumb";
44
+ };
45
+ readonly dataState: {
46
+ readonly errorTitle: "Couldn't load this";
47
+ readonly errorDescription: "Try again, or check your connection.";
48
+ readonly retry: "Try again";
49
+ };
50
+ readonly copyButton: {
51
+ readonly copy: "Copy";
52
+ readonly copied: "Copied";
53
+ readonly failed: "Couldn't copy";
54
+ };
55
+ readonly alert: {
56
+ readonly dismiss: "Dismiss";
57
+ readonly info: "Information";
58
+ readonly success: "Success";
59
+ readonly warning: "Warning";
60
+ readonly danger: "Error";
61
+ };
62
+ readonly passwordInput: {
63
+ readonly show: "Show password";
64
+ readonly shown: "Password shown";
65
+ readonly hidden: "Password hidden";
66
+ readonly capsLock: "Caps Lock is on";
67
+ };
68
+ readonly otpInput: {
69
+ readonly character: "Character {index} of {length}";
70
+ };
71
+ };
72
+ /** The complete catalogue, as a locale pack provides it. */
73
+ export type QeetrixMessages = { [Group in keyof typeof messages]: { [Key in keyof (typeof messages)[Group]]: string; }; };
74
+ /** Any subset of the catalogue, group by group: what a provider or a component takes. */
75
+ export type QeetrixMessagesInput = { [Group in keyof QeetrixMessages]?: Partial<QeetrixMessages[Group]>; };
@@ -1,8 +1,8 @@
1
1
  // src/internal/messages.ts
2
2
  /**
3
- * The default English strings (decision D-13). Components take overrides through their own props;
4
- * the provider and locale packs arrive with the first component that needs a translated string it
5
- * can't take as a prop.
3
+ * Every user-facing string in the library, in English (decision D-13). A string resolves from the
4
+ * component's own `messages` prop, then QeetrixProvider, then these defaults. Locale packs
5
+ * (src/locales/) are typed as the complete catalogue, so a missing key fails the typecheck.
6
6
  */
7
7
  const messages = {
8
8
  skipLink: { label: "Skip to main content" },
@@ -17,6 +17,45 @@ const messages = {
17
17
  slider: {
18
18
  minimum: "Minimum",
19
19
  maximum: "Maximum"
20
- }
20
+ },
21
+ appShell: {
22
+ collapse: "Collapse",
23
+ expand: "Expand",
24
+ open: "Open navigation",
25
+ navigation: "Navigation",
26
+ tools: "Tools",
27
+ closePanel: "Close panel"
28
+ },
29
+ sidebar: {
30
+ filter: "Filter navigation",
31
+ noMatches: "No pages match"
32
+ },
33
+ breadcrumb: { label: "Breadcrumb" },
34
+ dataState: {
35
+ errorTitle: "Couldn't load this",
36
+ errorDescription: "Try again, or check your connection.",
37
+ retry: "Try again"
38
+ },
39
+ copyButton: {
40
+ copy: "Copy",
41
+ copied: "Copied",
42
+ failed: "Couldn't copy"
43
+ },
44
+ alert: {
45
+ dismiss: "Dismiss",
46
+ info: "Information",
47
+ success: "Success",
48
+ warning: "Warning",
49
+ danger: "Error"
50
+ },
51
+ passwordInput: {
52
+ show: "Show password",
53
+ shown: "Password shown",
54
+ hidden: "Password hidden",
55
+ capsLock: "Caps Lock is on"
56
+ },
57
+ otpInput: { character: "Character {index} of {length}" }
21
58
  };
22
- export { messages };
59
+ /** The default locale: English with Indian number grouping (lakh, crore). */
60
+ const DEFAULT_LOCALE = "en-IN";
61
+ export { DEFAULT_LOCALE, messages };
@@ -1,4 +1,5 @@
1
1
  import { RenderProp } from "../internal/render.js";
2
+ import { QeetrixMessages } from "../internal/messages.js";
2
3
  import { ComponentProps, ReactElement } from "react";
3
4
  export type LinkTone = "default" | "subtle" | "inherit";
4
5
  export interface LinkProps extends ComponentProps<"a"> {
@@ -8,6 +9,11 @@ export interface LinkProps extends ComponentProps<"a"> {
8
9
  external?: boolean;
9
10
  /** Render a router link, e.g. `render={<RouterLink to="/x" />}`. */
10
11
  render?: RenderProp;
12
+ /**
13
+ * The screen-reader note on an external link, in place of "(opens in a new tab)". Link is
14
+ * server-safe, so QeetrixProvider's strings don't reach it: pass them here.
15
+ */
16
+ messages?: Partial<QeetrixMessages["link"]>;
11
17
  }
12
18
  /** Navigation to a page, a section, a file or a site. An action that changes data is a Button. */
13
- export declare function Link({ tone, external, render, rel, className, children, ...props }: LinkProps): ReactElement;
19
+ export declare function Link({ tone, external, render, rel, messages, className, children, ...props }: LinkProps): ReactElement;
package/dist/link/link.js CHANGED
@@ -6,7 +6,8 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { ArrowUpRightIcon } from "@qeetrix/icons/icons/arrow-up-right";
7
7
  // src/link/link.tsx
8
8
  /** Navigation to a page, a section, a file or a site. An action that changes data is a Button. */
9
- function Link({ tone, external, render, rel, className, children, ...props }) {
9
+ function Link({ tone, external, render, rel, messages: messages$1, className, children, ...props }) {
10
+ const externalNote = messages$1?.external ?? messages.link.external;
10
11
  return renderElement("a", render, {
11
12
  ...external ? { target: "_blank" } : {},
12
13
  ...props,
@@ -24,7 +25,7 @@ function Link({ tone, external, render, rel, className, children, ...props }) {
24
25
  className: "qx-link-icon",
25
26
  "data-slot": "link-icon"
26
27
  }),
27
- /* @__PURE__ */ jsxs(VisuallyHidden, { children: [" ", messages.link.external] })
28
+ /* @__PURE__ */ jsxs(VisuallyHidden, { children: [" ", externalNote] })
28
29
  ] }) : children
29
30
  });
30
31
  }
@@ -0,0 +1,8 @@
1
+ import { QeetrixMessages } from "../internal/messages.js";
2
+ /**
3
+ * Hindi. The words match Qeet ID's own Hindi strings ("खारिज करें" for Dismiss, "लोड हो रहा है" for
4
+ * Loading), so a product and the library never disagree.
5
+ *
6
+ * `<QeetrixProvider locale="hi-IN" messages={hi}>`
7
+ */
8
+ export declare const hi: QeetrixMessages;
@@ -0,0 +1,60 @@
1
+ // src/locales/hi.ts
2
+ /**
3
+ * Hindi. The words match Qeet ID's own Hindi strings ("खारिज करें" for Dismiss, "लोड हो रहा है" for
4
+ * Loading), so a product and the library never disagree.
5
+ *
6
+ * `<QeetrixProvider locale="hi-IN" messages={hi}>`
7
+ */
8
+ const hi = {
9
+ skipLink: { label: "मुख्य सामग्री पर जाएँ" },
10
+ spinner: { label: "लोड हो रहा है" },
11
+ link: { external: "(नए टैब में खुलता है)" },
12
+ field: { optional: "(वैकल्पिक)" },
13
+ overlay: { close: "बंद करें" },
14
+ toast: {
15
+ close: "खारिज करें",
16
+ region: "सूचनाएँ"
17
+ },
18
+ slider: {
19
+ minimum: "न्यूनतम",
20
+ maximum: "अधिकतम"
21
+ },
22
+ appShell: {
23
+ collapse: "छोटा करें",
24
+ expand: "बड़ा करें",
25
+ open: "नेविगेशन खोलें",
26
+ navigation: "नेविगेशन",
27
+ tools: "टूल",
28
+ closePanel: "पैनल बंद करें"
29
+ },
30
+ sidebar: {
31
+ filter: "नेविगेशन में खोजें",
32
+ noMatches: "कोई पेज नहीं मिला"
33
+ },
34
+ breadcrumb: { label: "पेज का पथ" },
35
+ dataState: {
36
+ errorTitle: "यह लोड नहीं हो सका",
37
+ errorDescription: "फिर से कोशिश करें, या अपना कनेक्शन जाँचें।",
38
+ retry: "फिर से कोशिश करें"
39
+ },
40
+ copyButton: {
41
+ copy: "कॉपी करें",
42
+ copied: "कॉपी हो गया",
43
+ failed: "कॉपी नहीं हो सका"
44
+ },
45
+ alert: {
46
+ dismiss: "खारिज करें",
47
+ info: "जानकारी",
48
+ success: "सफल",
49
+ warning: "चेतावनी",
50
+ danger: "त्रुटि"
51
+ },
52
+ passwordInput: {
53
+ show: "पासवर्ड दिखाएँ",
54
+ shown: "पासवर्ड दिख रहा है",
55
+ hidden: "पासवर्ड छिपा है",
56
+ capsLock: "कैप्स लॉक चालू है"
57
+ },
58
+ otpInput: { character: "{length} में से अक्षर {index}" }
59
+ };
60
+ export { hi };
@@ -20,7 +20,7 @@ export interface MeterProps extends Omit<Meter.Root.Props, "className" | "style"
20
20
  * A task advancing, such as an upload (06-component-specs.md, increment 6). `value={null}` is
21
21
  * indeterminate: a segment slides along the track, and fades instead under reduced motion.
22
22
  */
23
- declare function Progress$1({ label, showValue, className, ...props }: ProgressProps): ReactElement;
23
+ declare function Progress$1({ label, showValue, locale, className, ...props }: ProgressProps): ReactElement;
24
24
  /** A measurement within a range, such as storage used. It looks like Progress; it isn't a task. */
25
- declare function Meter$1({ label, showValue, tone, className, ...props }: MeterProps): ReactElement;
25
+ declare function Meter$1({ label, showValue, tone, locale, className, ...props }: MeterProps): ReactElement;
26
26
  export { Meter$1 as Meter, Progress$1 as Progress };