@qeetrix/react 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +288 -0
- package/README.md +240 -4
- package/dist/accordion/accordion.d.ts +42 -0
- package/dist/accordion/accordion.js +72 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +1 -0
- package/dist/avatar/avatar.d.ts +21 -0
- package/dist/avatar/avatar.js +43 -0
- package/dist/avatar/index.d.ts +2 -0
- package/dist/avatar/index.js +1 -0
- package/dist/badge/badge.d.ts +14 -0
- package/dist/badge/badge.js +25 -0
- package/dist/badge/index.d.ts +2 -0
- package/dist/badge/index.js +1 -0
- package/dist/box/box.d.ts +27 -0
- package/dist/box/box.js +21 -0
- package/dist/box/index.d.ts +2 -0
- package/dist/box/index.js +1 -0
- package/dist/button/button.d.ts +50 -0
- package/dist/button/button.js +78 -0
- package/dist/button/index.d.ts +2 -0
- package/dist/button/index.js +1 -0
- package/dist/card/card.d.ts +30 -0
- package/dist/card/card.js +56 -0
- package/dist/card/index.d.ts +2 -0
- package/dist/card/index.js +1 -0
- package/dist/checkbox/checkbox.d.ts +33 -0
- package/dist/checkbox/checkbox.js +61 -0
- package/dist/checkbox/index.d.ts +2 -0
- package/dist/checkbox/index.js +1 -0
- package/dist/css/accordion.css +73 -0
- package/dist/css/avatar.css +50 -0
- package/dist/css/badge.css +59 -0
- package/dist/css/box.css +163 -0
- package/dist/css/button.css +243 -0
- package/dist/css/card.css +75 -0
- package/dist/css/checkbox.css +67 -0
- package/dist/css/dialog.css +128 -0
- package/dist/css/drawer.css +129 -0
- package/dist/css/experimental/alert.css +89 -0
- package/dist/css/experimental/app-shell.css +258 -0
- package/dist/css/experimental/breadcrumb.css +67 -0
- package/dist/css/experimental/copy-button.css +13 -0
- package/dist/css/experimental/empty-state.css +63 -0
- package/dist/css/experimental/otp-input.css +84 -0
- package/dist/css/experimental/page-header.css +110 -0
- package/dist/css/experimental/password-input.css +21 -0
- package/dist/css/experimental/sidebar.css +234 -0
- package/dist/css/experimental/stat.css +115 -0
- package/dist/css/experimental/top-bar.css +299 -0
- package/dist/css/field.css +121 -0
- package/dist/css/fieldset.css +37 -0
- package/dist/css/grid.css +86 -0
- package/dist/css/heading.css +93 -0
- package/dist/css/inline.css +85 -0
- package/dist/css/input.css +108 -0
- package/dist/css/link.css +45 -0
- package/dist/css/menu.css +166 -0
- package/dist/css/popover.css +72 -0
- package/dist/css/progress.css +102 -0
- package/dist/css/radio.css +54 -0
- package/dist/css/scroll-area.css +70 -0
- package/dist/css/select.css +189 -0
- package/dist/css/separator.css +35 -0
- package/dist/css/skeleton.css +39 -0
- package/dist/css/skip-link.css +33 -0
- package/dist/css/slider.css +108 -0
- package/dist/css/spinner.css +55 -0
- package/dist/css/stack.css +76 -0
- package/dist/css/switch.css +112 -0
- package/dist/css/table.css +68 -0
- package/dist/css/tabs.css +123 -0
- package/dist/css/text-field.css +4 -0
- package/dist/css/text.css +106 -0
- package/dist/css/toast.css +115 -0
- package/dist/css/toggle.css +125 -0
- package/dist/css/tooltip.css +47 -0
- package/dist/css/visually-hidden.css +15 -0
- package/dist/dialog/dialog.d.ts +54 -0
- package/dist/dialog/dialog.js +121 -0
- package/dist/dialog/index.d.ts +2 -0
- package/dist/dialog/index.js +1 -0
- package/dist/drawer/drawer.d.ts +49 -0
- package/dist/drawer/drawer.js +102 -0
- package/dist/drawer/index.d.ts +2 -0
- package/dist/drawer/index.js +1 -0
- package/dist/experimental/alert/alert.d.ts +29 -0
- package/dist/experimental/alert/alert.js +72 -0
- package/dist/experimental/alert/index.d.ts +2 -0
- package/dist/experimental/app-shell/app-shell.d.ts +60 -0
- package/dist/experimental/app-shell/app-shell.js +224 -0
- package/dist/experimental/app-shell/index.d.ts +2 -0
- package/dist/experimental/breadcrumb/breadcrumb.d.ts +27 -0
- package/dist/experimental/breadcrumb/breadcrumb.js +48 -0
- package/dist/experimental/breadcrumb/index.d.ts +2 -0
- package/dist/experimental/copy-button/copy-button.d.ts +27 -0
- package/dist/experimental/copy-button/copy-button.js +61 -0
- package/dist/experimental/copy-button/index.d.ts +2 -0
- package/dist/experimental/empty-state/data-state.d.ts +23 -0
- package/dist/experimental/empty-state/data-state.js +40 -0
- package/dist/experimental/empty-state/empty-state.d.ts +26 -0
- package/dist/experimental/empty-state/empty-state.js +46 -0
- package/dist/experimental/empty-state/index.d.ts +3 -0
- package/dist/experimental/index.d.ts +24 -0
- package/dist/experimental/index.js +13 -0
- package/dist/experimental/otp-input/index.d.ts +2 -0
- package/dist/experimental/otp-input/otp-input.d.ts +27 -0
- package/dist/experimental/otp-input/otp-input.js +39 -0
- package/dist/experimental/page-header/index.d.ts +2 -0
- package/dist/experimental/page-header/page-header.d.ts +32 -0
- package/dist/experimental/page-header/page-header.js +73 -0
- package/dist/experimental/password-input/index.d.ts +2 -0
- package/dist/experimental/password-input/password-input.d.ts +26 -0
- package/dist/experimental/password-input/password-input.js +103 -0
- package/dist/experimental/sidebar/index.d.ts +2 -0
- package/dist/experimental/sidebar/sidebar.d.ts +64 -0
- package/dist/experimental/sidebar/sidebar.js +200 -0
- package/dist/experimental/stat/index.d.ts +2 -0
- package/dist/experimental/stat/stat.d.ts +31 -0
- package/dist/experimental/stat/stat.js +90 -0
- package/dist/experimental/top-bar/index.d.ts +2 -0
- package/dist/experimental/top-bar/top-bar.d.ts +60 -0
- package/dist/experimental/top-bar/top-bar.js +162 -0
- package/dist/field/choice.d.ts +21 -0
- package/dist/field/choice.js +67 -0
- package/dist/field/field.d.ts +31 -0
- package/dist/field/field.js +78 -0
- package/dist/field/index.d.ts +3 -0
- package/dist/field/index.js +1 -0
- package/dist/fieldset/fieldset.d.ts +19 -0
- package/dist/fieldset/fieldset.js +29 -0
- package/dist/fieldset/index.d.ts +2 -0
- package/dist/fieldset/index.js +1 -0
- package/dist/format/format.d.ts +12 -0
- package/dist/format/format.js +50 -0
- package/dist/format/index.d.ts +2 -0
- package/dist/format/index.js +1 -0
- package/dist/grid/grid.d.ts +30 -0
- package/dist/grid/grid.js +36 -0
- package/dist/grid/index.d.ts +2 -0
- package/dist/grid/index.js +1 -0
- package/dist/heading/heading.d.ts +23 -0
- package/dist/heading/heading.js +30 -0
- package/dist/heading/index.d.ts +2 -0
- package/dist/heading/index.js +1 -0
- package/dist/index.d.ts +84 -1
- package/dist/index.js +40 -0
- package/dist/inline/index.d.ts +2 -0
- package/dist/inline/index.js +1 -0
- package/dist/inline/inline.d.ts +19 -0
- package/dist/inline/inline.js +16 -0
- package/dist/input/index.d.ts +2 -0
- package/dist/input/index.js +1 -0
- package/dist/input/input.d.ts +37 -0
- package/dist/input/input.js +63 -0
- package/dist/internal/control.d.ts +9 -0
- package/dist/internal/control.js +4 -0
- package/dist/internal/cx.js +7 -0
- package/dist/internal/media.js +15 -0
- package/dist/internal/messages.d.ts +75 -0
- package/dist/internal/messages.js +61 -0
- package/dist/internal/refs.js +23 -0
- package/dist/internal/render.d.ts +6 -0
- package/dist/internal/render.js +22 -0
- package/dist/internal/space.d.ts +9 -0
- package/dist/internal/text.d.ts +5 -0
- package/dist/internal/text.js +18 -0
- package/dist/link/index.d.ts +2 -0
- package/dist/link/index.js +1 -0
- package/dist/link/link.d.ts +19 -0
- package/dist/link/link.js +32 -0
- package/dist/locales/hi.d.ts +8 -0
- package/dist/locales/hi.js +60 -0
- package/dist/menu/index.d.ts +2 -0
- package/dist/menu/index.js +1 -0
- package/dist/menu/menu.d.ts +76 -0
- package/dist/menu/menu.js +172 -0
- package/dist/popover/index.d.ts +2 -0
- package/dist/popover/index.js +1 -0
- package/dist/popover/popover.d.ts +33 -0
- package/dist/popover/popover.js +48 -0
- package/dist/progress/index.d.ts +2 -0
- package/dist/progress/index.js +1 -0
- package/dist/progress/progress.d.ts +26 -0
- package/dist/progress/progress.js +62 -0
- package/dist/provider/index.d.ts +3 -0
- package/dist/provider/index.js +1 -0
- package/dist/provider/provider.d.ts +46 -0
- package/dist/provider/provider.js +90 -0
- package/dist/radio/index.d.ts +2 -0
- package/dist/radio/index.js +1 -0
- package/dist/radio/radio.d.ts +31 -0
- package/dist/radio/radio.js +47 -0
- package/dist/scroll-area/index.d.ts +2 -0
- package/dist/scroll-area/index.js +1 -0
- package/dist/scroll-area/scroll-area.d.ts +18 -0
- package/dist/scroll-area/scroll-area.js +39 -0
- package/dist/select/index.d.ts +2 -0
- package/dist/select/index.js +1 -0
- package/dist/select/select.d.ts +36 -0
- package/dist/select/select.js +82 -0
- package/dist/separator/index.d.ts +2 -0
- package/dist/separator/index.js +1 -0
- package/dist/separator/separator.d.ts +16 -0
- package/dist/separator/separator.js +16 -0
- package/dist/skeleton/index.d.ts +2 -0
- package/dist/skeleton/index.js +1 -0
- package/dist/skeleton/skeleton.d.ts +12 -0
- package/dist/skeleton/skeleton.js +18 -0
- package/dist/skip-link/index.d.ts +2 -0
- package/dist/skip-link/index.js +1 -0
- package/dist/skip-link/skip-link.d.ts +13 -0
- package/dist/skip-link/skip-link.js +18 -0
- package/dist/slider/index.d.ts +2 -0
- package/dist/slider/index.js +1 -0
- package/dist/slider/slider.d.ts +20 -0
- package/dist/slider/slider.js +58 -0
- package/dist/spinner/index.d.ts +2 -0
- package/dist/spinner/index.js +1 -0
- package/dist/spinner/spinner.d.ts +15 -0
- package/dist/spinner/spinner.js +19 -0
- package/dist/stack/index.d.ts +2 -0
- package/dist/stack/index.js +1 -0
- package/dist/stack/stack.d.ts +17 -0
- package/dist/stack/stack.js +15 -0
- package/dist/styles.css +4548 -0
- package/dist/switch/index.d.ts +2 -0
- package/dist/switch/index.js +1 -0
- package/dist/switch/switch.d.ts +18 -0
- package/dist/switch/switch.js +31 -0
- package/dist/table/index.d.ts +2 -0
- package/dist/table/index.js +1 -0
- package/dist/table/table.d.ts +36 -0
- package/dist/table/table.js +69 -0
- package/dist/tabs/index.d.ts +2 -0
- package/dist/tabs/index.js +1 -0
- package/dist/tabs/tabs.d.ts +43 -0
- package/dist/tabs/tabs.js +66 -0
- package/dist/text/index.d.ts +3 -0
- package/dist/text/index.js +1 -0
- package/dist/text/text.d.ts +24 -0
- package/dist/text/text.js +24 -0
- package/dist/text-field/index.d.ts +2 -0
- package/dist/text-field/index.js +1 -0
- package/dist/text-field/text-field.d.ts +15 -0
- package/dist/text-field/text-field.js +43 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +1 -0
- package/dist/toast/toast.d.ts +57 -0
- package/dist/toast/toast.js +124 -0
- package/dist/toggle/index.d.ts +2 -0
- package/dist/toggle/index.js +1 -0
- package/dist/toggle/toggle.d.ts +48 -0
- package/dist/toggle/toggle.js +54 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/tooltip/tooltip.d.ts +26 -0
- package/dist/tooltip/tooltip.js +39 -0
- package/dist/visually-hidden/index.d.ts +2 -0
- package/dist/visually-hidden/index.js +1 -0
- package/dist/visually-hidden/visually-hidden.d.ts +8 -0
- package/dist/visually-hidden/visually-hidden.js +12 -0
- package/package.json +170 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,293 @@
|
|
|
1
1
|
# @qeetrix/react
|
|
2
2
|
|
|
3
|
+
## 0.0.3
|
|
4
|
+
|
|
5
|
+
The first two Phase 5 increments, from `@qeetrix/react/experimental`: C1, the console frame, and
|
|
6
|
+
C2, secure entry. Experimental parts may change in a patch release; each moves to the root export
|
|
7
|
+
once it has shipped in its product (07-components.md).
|
|
8
|
+
|
|
9
|
+
### C1 · Console frame
|
|
10
|
+
|
|
11
|
+
- **`AppShell`** (design B · Ruled, chosen from three researched concepts; 07-components.md):
|
|
12
|
+
- the frame of every Qeet console: a skip link, the sticky TopBar across the full width, the
|
|
13
|
+
navigation column on the canvas tone, the page, and an end-edge panel; every seam a 1px rule;
|
|
14
|
+
- from 840px the navigation collapses to a 48px rail (`sidebarCollapsed` /
|
|
15
|
+
`defaultSidebarCollapsed` / `onSidebarCollapsedChange`) that expands over the page while hovered
|
|
16
|
+
or focused; below 840px it opens as a drawer from the start edge;
|
|
17
|
+
- `drawers` (help, assistant, activity), opened from the TopBar's tools: one at a time, pushing
|
|
18
|
+
the page from 1200px; `notifications` above the page; `density`, compact by default;
|
|
19
|
+
- `useAppShell()` tells content inside the frame when the sidebar is a rail.
|
|
20
|
+
- **`TopBar`, `TopBarSwitcher`, `TopBarSearch`:**
|
|
21
|
+
- black in light and dark (the dark scheme on itself), topped by a 2px Ember line;
|
|
22
|
+
- product to global, left to right: launcher cell, product, the scope path with drawn slashes,
|
|
23
|
+
centred search, panel triggers and utilities, account;
|
|
24
|
+
- `TopBarSwitcher` is a step of the path (organisation mark, environment status dot, always a
|
|
25
|
+
word) and works as a Menu trigger; `TopBarSearch` is a field-shaped button with its shortcut.
|
|
26
|
+
- **`PageHeader`, `PageContent`:** breadcrumb, `title.lg` title with count and status, actions, a
|
|
27
|
+
`summary` strip bled to the page edges, and tabs at the foot; a 32px page gutter.
|
|
28
|
+
- **`Stat`, `StatGroup`:** key numbers in ruled cells; numeric values formatted for the locale; an
|
|
29
|
+
optional decorative trend line whose last point is the Ember mark. Server-safe.
|
|
30
|
+
- **`Sidebar`, `SidebarGroup`, `SidebarItem`:**
|
|
31
|
+
- links only, one level of nesting, every item in the Tab order;
|
|
32
|
+
- square rows edge to edge with round icons; the current page gets `aria-current`, the 3px Ember
|
|
33
|
+
bar, weight 600 and an Ember (optionally filled) icon;
|
|
34
|
+
- groups between hairlines under tracked small capitals (`typography.label.caps`; untracked for
|
|
35
|
+
Indic text, which has no case); an optional `title`, `filter` and `footer`;
|
|
36
|
+
- in the rail, icons stay where they are and labels become tooltips.
|
|
37
|
+
- **`Breadcrumb`, `BreadcrumbItem`:** server-safe; mirrored chevrons; long labels truncate and keep a
|
|
38
|
+
title.
|
|
39
|
+
- **`EmptyState`:** a 24px icon, a title (a heading when given a `level`), a description and actions,
|
|
40
|
+
in `page` or `section` size and `neutral` or `danger` tone. Server-safe.
|
|
41
|
+
- **`DataState`:** loading, then error, then empty, then the content. The error state never shows the
|
|
42
|
+
raw error, and `onRetry` adds "Try again".
|
|
43
|
+
- **Colour scheme:**
|
|
44
|
+
- `QeetrixProvider` takes `colorScheme`, `defaultColorScheme` and `onColorSchemeChange`, writes
|
|
45
|
+
`data-qx-color-scheme` on `<html>`, and follows the operating system while `system`;
|
|
46
|
+
- `useColorScheme()` returns the scheme and a setter. The product persists the choice.
|
|
47
|
+
- **`Button` and `IconButton`:**
|
|
48
|
+
- a `neutral` tone for ghost utility actions in toolbars and headers, whose icons stay neutral
|
|
49
|
+
(design rules §16);
|
|
50
|
+
- a Button's icon sits in its own zone (the new `button-zone` slot), behind a 1px rule as tall as
|
|
51
|
+
the icon, in the label's colour (design rules §7).
|
|
52
|
+
- **Strings:** new `appShell`, `sidebar`, `breadcrumb` and `dataState` groups, in English and Hindi.
|
|
53
|
+
- **Peer dependency:** `@qeetrix/tokens` `^0.0.7`, for `typography.label.caps`. Since tokens 0.0.6
|
|
54
|
+
the stylesheet also loads Qeet Sans and Fira Code, so the tests now render in the real typefaces.
|
|
55
|
+
- **Source check:** every `var(--qx-*)` must name a token in the installed `@qeetrix/tokens` or a
|
|
56
|
+
property the package declares itself, so a token from a newer tokens release can't ship
|
|
57
|
+
unresolved.
|
|
58
|
+
- **Platform:**
|
|
59
|
+
- the CSS build, the dist check and the screenshot coverage test understand
|
|
60
|
+
`src/experimental/<family>/`;
|
|
61
|
+
- the test helper treats cancelled animations as settled.
|
|
62
|
+
|
|
63
|
+
### C2 · Secure entry
|
|
64
|
+
|
|
65
|
+
- **`CopyButton`:** copies a value (or what a function returns, even asynchronously); the icon
|
|
66
|
+
turns into a check, "Copied" is announced politely, focus stays, and it resets after 2 seconds.
|
|
67
|
+
It fails visibly where there's no clipboard. An IconButton named by `label`, or a Button with a
|
|
68
|
+
visible label.
|
|
69
|
+
- **`Alert`:** `info`, `success`, `warning` or `danger` on the status tint with the 3px status edge;
|
|
70
|
+
a title (led by a hidden tone word), a description, actions and an optional Dismiss. `announce`
|
|
71
|
+
makes it `role="status"` or `role="alert"` only when it appears after something happens. It never
|
|
72
|
+
takes focus or disappears by itself.
|
|
73
|
+
- **`PasswordInput`:** an Input with a pressed-state Show password toggle, announced; hidden by
|
|
74
|
+
default and hidden again when its form submits; `current-password` / `new-password` / `off`;
|
|
75
|
+
spelling and capitalisation tools off; a Caps Lock warning; `endAdornment` for a CopyButton.
|
|
76
|
+
- **`OTPInput`:** one-time codes in slots on Base UI's OTP Field: typing moves on, paste fills
|
|
77
|
+
every slot, SMS autofill lands at once; numeric by default; `groups`, `md` and `lg` sizes; the
|
|
78
|
+
first slot takes the field's label, the rest are "Character 2 of 6".
|
|
79
|
+
- **Strings:** new `copyButton`, `alert`, `passwordInput` and `otpInput` groups, in English and
|
|
80
|
+
Hindi.
|
|
81
|
+
|
|
82
|
+
## 0.0.2
|
|
83
|
+
|
|
84
|
+
Every primitive, in six increments: the foundation, actions, form controls, overlays, selection and
|
|
85
|
+
disclosure, and display and status. Then the Phase 4 close-out: localisation, forced colours,
|
|
86
|
+
right-to-left, reduced motion and the sign-in exit screen.
|
|
87
|
+
|
|
88
|
+
### Foundation
|
|
89
|
+
|
|
90
|
+
All nine are server-safe and render in React Server Components.
|
|
91
|
+
|
|
92
|
+
- **Layout:**
|
|
93
|
+
- `Box`: a token-only surface (padding, background, border, corners);
|
|
94
|
+
- `Stack` and `Inline`: vertical and horizontal flows with token gaps;
|
|
95
|
+
- `Grid`: responsive equal columns on the window size classes.
|
|
96
|
+
- **Type:**
|
|
97
|
+
- `Text`: body roles, weights, tones, monospace, truncation;
|
|
98
|
+
- `Heading`: the level sets the element, the size sets the look; Geo letterforms for display.
|
|
99
|
+
- **`Separator`:** 1px, horizontal or vertical, optionally decorative.
|
|
100
|
+
- **Accessibility:**
|
|
101
|
+
- `VisuallyHidden`: screen-reader-only content;
|
|
102
|
+
- `SkipLink`: the first focusable element on a page, hidden until focused.
|
|
103
|
+
- **Shared types:** `Space`, `Inset`, `TextAlign`, `RenderProp`.
|
|
104
|
+
- **Imports:** every component also has its own entry point (`@qeetrix/react/stack`) and stylesheet
|
|
105
|
+
(`@qeetrix/react/css/stack.css`).
|
|
106
|
+
|
|
107
|
+
### Actions
|
|
108
|
+
|
|
109
|
+
- **`Button`** (on Base UI Button):
|
|
110
|
+
- the label comes first, with an optional sharp trailing icon;
|
|
111
|
+
- variants `solid` (Ember primary), `soft` (warm-graphite secondary) and `ghost`; tones `brand` and
|
|
112
|
+
`danger`; sizes that follow the density;
|
|
113
|
+
- `loading` sets `aria-busy` and shows a spinner in the icon's place;
|
|
114
|
+
- a disabled button stays focusable, so its reason can be read.
|
|
115
|
+
- **`IconButton`:** square and icon-only; its `aria-label` is required by its type.
|
|
116
|
+
- **`Link`:** default, subtle and inherit tones. `external` opens a new tab with
|
|
117
|
+
`rel="noopener noreferrer"`, an arrow mark and a screen-reader note.
|
|
118
|
+
- **`Toggle` and `ToggleGroup`** (on Base UI):
|
|
119
|
+
- pressed state with `onPressedChange`, and `filledWhenPressed` swaps to the icon's filled drawing;
|
|
120
|
+
- groups are segmented or separate, single-choice or `multiple`, with arrow-key focus.
|
|
121
|
+
- **`Spinner`:** four sizes. It is announced as a status unless `decorative`, and fades instead of
|
|
122
|
+
spinning under reduced motion.
|
|
123
|
+
- **Shared types:** `ControlSize`, `QeetrixIcon`.
|
|
124
|
+
- **Dependencies:**
|
|
125
|
+
- new peer `@base-ui/react` ^1.9.0;
|
|
126
|
+
- new dependency `@qeetrix/icons` ^1.0.12, for the external-link mark.
|
|
127
|
+
|
|
128
|
+
### Form controls
|
|
129
|
+
|
|
130
|
+
All are client components on Base UI's Field, Fieldset, Input, Checkbox, Radio Group and Switch, so
|
|
131
|
+
labels, descriptions and errors link to their controls without ids. On every form control, `ref` and
|
|
132
|
+
extra props go to the control, while `className` and `style` place the row.
|
|
133
|
+
|
|
134
|
+
- **`Field`:**
|
|
135
|
+
- a label, help text and error around any control;
|
|
136
|
+
- `optional` adds "(optional)", and Qeetrix never marks required fields;
|
|
137
|
+
- `error` shows a message now (from the server, say) and marks the field invalid. Without it, the
|
|
138
|
+
browser's or `validate`'s message shows with a sharp alert icon;
|
|
139
|
+
- the error replaces the help text;
|
|
140
|
+
- `hideLabel` is for toolbar searches only.
|
|
141
|
+
- **`Input`:** the filled box with a bottom line, a leading `icon`, and start and end adornments.
|
|
142
|
+
It takes its focus, invalid, read-only and disabled looks from the input.
|
|
143
|
+
- **`Textarea`:** the same look. It grows with its content between `rows` and `maxRows` through CSS
|
|
144
|
+
`field-sizing`, with no script.
|
|
145
|
+
- **`TextField`:** Field and Input in one, the default form row.
|
|
146
|
+
- **`Fieldset`:** a section of fields under a `title.sm` legend, spaced by the density's form gap.
|
|
147
|
+
`disabled` disables everything inside.
|
|
148
|
+
- **`Checkbox` and `CheckboxGroup`:**
|
|
149
|
+
- the 16px box with a sharp tick, and the mixed state with a dash;
|
|
150
|
+
- a `parent` checkbox selects the whole group;
|
|
151
|
+
- the label row is at least 24px tall.
|
|
152
|
+
- **`Radio` and `RadioGroup`:** the 5px Ember ring. Arrow keys move and select, and RadioGroup is
|
|
153
|
+
generic over its value type.
|
|
154
|
+
- **`Switch`:** a 32×18 track with a square thumb that moves to the end side, in either text
|
|
155
|
+
direction.
|
|
156
|
+
- With no visible label, Checkbox, Radio and Switch require `aria-label` or `aria-labelledby` by
|
|
157
|
+
their type.
|
|
158
|
+
- **Shared types:** `FieldMessageProps`, `ChoiceLabelProps`, `ChoiceOrientation`.
|
|
159
|
+
|
|
160
|
+
### Overlays
|
|
161
|
+
|
|
162
|
+
All are client components on Base UI. Each has a root, a trigger that renders your control, and a
|
|
163
|
+
content part that assembles the portal, positioner and popup.
|
|
164
|
+
|
|
165
|
+
- **`Popover`:** an anchored panel with an optional title and description. Focus moves in on open
|
|
166
|
+
and returns to the trigger on close.
|
|
167
|
+
- **`Tooltip`:** one component around its trigger. It opens after 500ms of hover or at once on
|
|
168
|
+
focus. `TooltipProvider` groups neighbours, so the next one opens at once.
|
|
169
|
+
- **`Menu`:**
|
|
170
|
+
- items with sharp icons, shortcuts and a `danger` tone;
|
|
171
|
+
- checkbox and radio items in a 16px mark column;
|
|
172
|
+
- group labels, separators and submenus;
|
|
173
|
+
- the 2px Ember start bar on the highlighted item, which follows the text direction.
|
|
174
|
+
- **`Dialog`:**
|
|
175
|
+
- `intent` is `standard`, `danger` or `warning`, the last two as alert dialogs with a red or amber
|
|
176
|
+
intent line;
|
|
177
|
+
- `dismissible`, off by default for `danger`, so only its actions close it;
|
|
178
|
+
- sizes 400, 560 and 800px;
|
|
179
|
+
- the close button comes last in the focus order, so a dialog opens on its first field or its
|
|
180
|
+
least destructive action.
|
|
181
|
+
- **`Drawer`:**
|
|
182
|
+
- a full-height side panel at the `end` or `start` edge;
|
|
183
|
+
- ruled header and footer, and a scrolling body;
|
|
184
|
+
- focus goes to the first field, except on touch;
|
|
185
|
+
- it can be swiped away.
|
|
186
|
+
- **`ToastProvider` and `useToast`:**
|
|
187
|
+
- `add`, `close`, `update` and `promise`;
|
|
188
|
+
- tones `info`, `success`, `warning` and `danger`, each with a 3px status edge and a sharp icon;
|
|
189
|
+
- one action at most;
|
|
190
|
+
- `danger` toasts stay until dismissed and are announced at once.
|
|
191
|
+
- **`IconButton`** now shows its `aria-label` as a tooltip. `tooltip={false}` turns it off, and
|
|
192
|
+
other text replaces it.
|
|
193
|
+
- **Stacking:** anchored layers use `z.popover`, so a menu opened in a dialog sits above it.
|
|
194
|
+
|
|
195
|
+
### Selection and disclosure
|
|
196
|
+
|
|
197
|
+
- **`Select`:**
|
|
198
|
+
- a field-style trigger with a chevron, and a menu-style list below it;
|
|
199
|
+
- the chosen option carries an Ember check;
|
|
200
|
+
- `items` can be flat or grouped, and `multiple` lists the chosen labels;
|
|
201
|
+
- generic over the value. A Field labels it.
|
|
202
|
+
- **`Tabs`, `TabsList`, `Tab`, `TabsPanel`:**
|
|
203
|
+
- the 3px Ember bar slides under the selected tab, and `TabsList` draws it;
|
|
204
|
+
- a selected tab has weight 600 and can fill its icon;
|
|
205
|
+
- padding is 12px at compact density and 16px otherwise;
|
|
206
|
+
- `activateOnFocus` is on by default;
|
|
207
|
+
- vertical tabs carry the line and bar on the start edge.
|
|
208
|
+
- **`Slider`:**
|
|
209
|
+
- a 4px track, the Ember range, and a 16px square thumb ringed in the surface colour;
|
|
210
|
+
- `label` and `showValue` with `Intl` formatting;
|
|
211
|
+
- a range renders one thumb per value, named Minimum and Maximum.
|
|
212
|
+
- **`Accordion`, `AccordionItem`:**
|
|
213
|
+
- ruled sections with a `title.sm` trigger inside a heading (`headingLevel`), and a turning
|
|
214
|
+
chevron;
|
|
215
|
+
- the panel's height animates;
|
|
216
|
+
- find in page opens a closed section that holds the match.
|
|
217
|
+
- **`Collapsible`, `CollapsibleTrigger`, `CollapsiblePanel`:** one unruled section.
|
|
218
|
+
- **`ScrollArea`:** 8px scrollbars that show on hover, while scrolling and on keyboard focus. The
|
|
219
|
+
region takes focus when it overflows.
|
|
220
|
+
|
|
221
|
+
### Display and status
|
|
222
|
+
|
|
223
|
+
- **`Card`:**
|
|
224
|
+
- a raised surface with a 1px line, 4px corners and no shadow;
|
|
225
|
+
- a ruled header with `title`, the required heading `level`, `description` and `actions`, and an
|
|
226
|
+
optional ruled `footer`;
|
|
227
|
+
- a titled card is a section labelled by its title, and a table in the body runs edge to edge.
|
|
228
|
+
- Server-safe.
|
|
229
|
+
- **`Table`, `TableCaption`, `TableHeader`, `TableBody`, `TableRow`, `TableHead`, `TableCell`:**
|
|
230
|
+
- a neutral header band and row heights from the density;
|
|
231
|
+
- `numeric` cells use tabular figures, end-aligned;
|
|
232
|
+
- `selected` rows keep their Ember tint under the pointer.
|
|
233
|
+
- Server-safe.
|
|
234
|
+
- **`Badge`:** pills in six tones with an optional sharp icon. The `brand` fill is
|
|
235
|
+
`bg.brand.subtle`, so its text meets AA. Server-safe.
|
|
236
|
+
- **`Avatar`:** a circle with the photo, or initials while it loads or when it fails. The initials
|
|
237
|
+
use whole graphemes, so Indic names stay intact. It can be `decorative`.
|
|
238
|
+
- **`Progress` and `Meter`:** Slider's header, a 4px track and an Ember indicator (Meter takes a
|
|
239
|
+
status tone). Indeterminate progress slides, and fades under reduced motion.
|
|
240
|
+
- **`Skeleton`:** `text`, `block` or `circle`. It pulses, holds still under reduced motion, and is
|
|
241
|
+
always hidden from assistive technology. Server-safe.
|
|
242
|
+
|
|
243
|
+
### Phase 4 close-out
|
|
244
|
+
|
|
245
|
+
- **`QeetrixProvider`:**
|
|
246
|
+
- `locale` (default `en-IN`) for formatting;
|
|
247
|
+
- `direction`, which wraps Base UI's DirectionProvider;
|
|
248
|
+
- `messages`, merged over the English defaults group by group.
|
|
249
|
+
- Nested providers inherit what they don't set. `useLocale()` reads the locale.
|
|
250
|
+
- **Strings:**
|
|
251
|
+
- every library string comes from one typed catalogue (`QeetrixMessages`);
|
|
252
|
+
- Field, TextField, DialogContent, DrawerContent, ToastProvider, Slider and Link take a `messages`
|
|
253
|
+
prop that wins over the provider;
|
|
254
|
+
- Link stays server-safe, so it takes its string per instance.
|
|
255
|
+
- **Locale packs:** Hindi (`@qeetrix/react/locales/hi`) is the first. It's a separate entry point,
|
|
256
|
+
typed as the complete catalogue, and uses Qeet ID's own Hindi words.
|
|
257
|
+
- **Formatting:** `formatNumber`, `formatCurrency` (lakh and crore grouping by default) and
|
|
258
|
+
`formatRelativeTime`. They're pure, server-safe and cache their formatters. Slider, Progress and
|
|
259
|
+
Meter format in the provider's locale.
|
|
260
|
+
- **Forced colours (Windows High Contrast):**
|
|
261
|
+
- surfaces that showed only a fill or a shadow get a system-colour edge;
|
|
262
|
+
- on states use `Highlight`: pressed Toggle, Switch, checked Checkbox, the Slider and Progress
|
|
263
|
+
fills, the Tabs bar, and highlighted Menu and Select items;
|
|
264
|
+
- separators and the tab list's base line stay visible.
|
|
265
|
+
- **Right-to-left:** tests cover arrow keys in Tabs, ToggleGroup, RadioGroup, Slider and the Menu
|
|
266
|
+
submenu, plus the Drawer edge, the Menu bar side and the Input adornment order.
|
|
267
|
+
- **Reduced motion:** a test checks that durations collapse and that Spinner and indeterminate
|
|
268
|
+
Progress fade instead.
|
|
269
|
+
- **Slider:** a slider named by `aria-label` (no visible label) now passes the name on to its
|
|
270
|
+
thumbs, which are what carry the slider role. A range adds Minimum and Maximum.
|
|
271
|
+
- **Sign-in exit screen:** built from primitives alone. Its keyboard order, axe, and its light,
|
|
272
|
+
dark, right-to-left and forced-colours screenshots are all tested.
|
|
273
|
+
|
|
274
|
+
### Platform
|
|
275
|
+
|
|
276
|
+
- **Source check:**
|
|
277
|
+
- it bans physical CSS properties and system colours outside forced-colours blocks;
|
|
278
|
+
- it ignores comments when looking for client-only code.
|
|
279
|
+
|
|
280
|
+
- Each family's stylesheet (`css/<family>.css`) now imports the base layer and the stylesheets of
|
|
281
|
+
the families it renders, so it works on its own.
|
|
282
|
+
- Screenshots wait for transitions to finish, and overlays are captured across the whole viewport.
|
|
283
|
+
The accessibility check also waits, since contrast measured mid-fade isn't real. It skips Base
|
|
284
|
+
UI's aria-hidden focus guards, which hand focus on the moment it lands.
|
|
285
|
+
- A type-only import between families no longer pulls in the other family's stylesheet.
|
|
286
|
+
- Screenshots freeze endless animations (spinner, skeleton, indeterminate progress), so they match
|
|
287
|
+
every run.
|
|
288
|
+
- The nightly Firefox and WebKit run tests without V8 coverage, which works only in Chromium.
|
|
289
|
+
Every browser test passes in all three engines.
|
|
290
|
+
|
|
3
291
|
## 0.0.1
|
|
4
292
|
|
|
5
293
|
The engineering platform, with no components yet:
|
package/README.md
CHANGED
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
The React components of **Qeetrix**, the Qeet Group design system: accessible, token-driven
|
|
4
4
|
primitives built on [Base UI](https://base-ui.com), styled by the Qeetrix design language.
|
|
5
5
|
|
|
6
|
-
> **Status: 0.0.x
|
|
7
|
-
>
|
|
8
|
-
>
|
|
6
|
+
> **Status: 0.0.x.** Every primitive is here: foundation, actions, form controls, overlays, selection
|
|
7
|
+
> and disclosure, and display and status. Components and patterns follow as Qeet products ask for
|
|
8
|
+
> them.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
bun add @qeetrix/react @qeetrix/tokens
|
|
13
|
+
bun add @qeetrix/react @qeetrix/tokens @base-ui/react @qeetrix/icons
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
`@base-ui/react` powers the interactive components. Icons come from `@qeetrix/icons`: a component's
|
|
17
|
+
`icon` prop takes any of its icons, e.g. `icon={PlusIcon}`.
|
|
18
|
+
|
|
16
19
|
## Styles
|
|
17
20
|
|
|
18
21
|
Load the tokens, then the component styles, once at the root of your app:
|
|
@@ -26,6 +29,239 @@ The stylesheet is precompiled. Your Tailwind setup doesn't need to scan this pac
|
|
|
26
29
|
utility classes. Qeetrix styles sit in the `qx.base` and `qx.components` cascade layers, so your own
|
|
27
30
|
layout utilities override them without `!important`.
|
|
28
31
|
|
|
32
|
+
## Use
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { PlusIcon } from "@qeetrix/icons/icons/plus";
|
|
36
|
+
import { Box, Button, Grid, Heading, Inline, SkipLink, Stack, Text } from "@qeetrix/react";
|
|
37
|
+
|
|
38
|
+
export function Invoices() {
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<SkipLink />
|
|
42
|
+
<main id="main">
|
|
43
|
+
<Stack gap="xl">
|
|
44
|
+
<Inline justify="between">
|
|
45
|
+
<Heading level={1}>Invoices</Heading>
|
|
46
|
+
<Button icon={PlusIcon}>New invoice</Button>
|
|
47
|
+
</Inline>
|
|
48
|
+
<Grid columns={{ base: 1, medium: 2, large: 3 }} gap="md">
|
|
49
|
+
<Box padding="lg" background="raised" border="default" radius="surface">
|
|
50
|
+
<Text tone="subtle">Due this month</Text>
|
|
51
|
+
</Box>
|
|
52
|
+
</Grid>
|
|
53
|
+
</Stack>
|
|
54
|
+
</main>
|
|
55
|
+
</>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **Server-safe:** layout, type, `Link`, `Spinner`, `Card`, `Table`, `Badge` and `Skeleton` render in
|
|
61
|
+
React Server Components. Everything built on Base UI is a client component; its `"use client"`
|
|
62
|
+
directive ships in the build.
|
|
63
|
+
- **`render` prop:** renders a different element with the same props, e.g. `render={<section />}`.
|
|
64
|
+
- **Per-component entry points:** `@qeetrix/react/stack`, plus its own stylesheet at
|
|
65
|
+
`@qeetrix/react/css/stack.css` if you'd rather not load the full one. A family's stylesheet brings
|
|
66
|
+
the base styles and those of the components it renders (`css/text-field.css` brings Field and
|
|
67
|
+
Input).
|
|
68
|
+
|
|
69
|
+
| Component | For |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `Box` | A token-only surface: padding, background, border, corners |
|
|
72
|
+
| `Stack`, `Inline` | Vertical and horizontal flows with token gaps (`none` … `4xl`) |
|
|
73
|
+
| `Grid` | Equal columns per window size class (600 / 840 / 1200 / 1600px) |
|
|
74
|
+
| `Text` | Body text: size, weight, tone, monospace, truncation |
|
|
75
|
+
| `Heading` | `level` sets h1–h6; `size` sets the look (display, heading, title) |
|
|
76
|
+
| `Separator` | A 1px line, horizontal or vertical |
|
|
77
|
+
| `VisuallyHidden` | Content for screen readers only |
|
|
78
|
+
| `SkipLink` | "Skip to main content", hidden until focused |
|
|
79
|
+
| `Button` | Label first, an optional trailing icon; solid (primary), soft (secondary), ghost; brand or danger; `loading` |
|
|
80
|
+
| `IconButton` | A square, icon-only button; `aria-label` is required by its type and shows as a tooltip |
|
|
81
|
+
| `Link` | Inline navigation; `external` opens a new tab safely and says so to screen readers |
|
|
82
|
+
| `Toggle`, `ToggleGroup` | Pressed or not; segmented or separate groups, single or `multiple` |
|
|
83
|
+
| `Spinner` | An indeterminate wait, announced as a status unless `decorative` |
|
|
84
|
+
| `TextField` | Label, input, help text and error in one: the default form row |
|
|
85
|
+
| `Field` | The label, help text and error around any control; `optional` adds "(optional)" |
|
|
86
|
+
| `Input`, `Textarea` | The filled text box with a bottom line; Textarea grows with its content |
|
|
87
|
+
| `Fieldset` | A section of fields under a heading, spaced by the density's form gap |
|
|
88
|
+
| `Checkbox`, `CheckboxGroup` | On/off or several of a set; mixed state; a `parent` checkbox selects all |
|
|
89
|
+
| `Radio`, `RadioGroup` | One of a set; arrow keys move and select |
|
|
90
|
+
| `Switch` | A setting that applies at once |
|
|
91
|
+
| `Popover` | A panel anchored to a trigger: filters, details, a short form |
|
|
92
|
+
| `Tooltip` | A short label for a control; `TooltipProvider` groups neighbours |
|
|
93
|
+
| `Menu` | Actions and options from a trigger: icons, shortcuts, checkbox and radio items, submenus |
|
|
94
|
+
| `Dialog` | A focused task or decision; `intent="danger"` makes a confirmation only its actions close |
|
|
95
|
+
| `Drawer` | A side panel for details or an edit form, swipeable on touch |
|
|
96
|
+
| `ToastProvider`, `useToast` | Brief notifications with a status edge; errors stay until dismissed |
|
|
97
|
+
| `Select` | One option, or several, from up to about 15; looks like a field, opens like a menu |
|
|
98
|
+
| `Tabs` | Views of one context, with the sliding Ember bar |
|
|
99
|
+
| `Slider` | A number or a range on a scale, with a formatted value |
|
|
100
|
+
| `Accordion`, `Collapsible` | Ruled sections, or one section, that show and hide |
|
|
101
|
+
| `ScrollArea` | A scrolling region with Qeetrix scrollbars |
|
|
102
|
+
| `Card` | A raised, ruled surface with a title, actions and footer; a table inside runs edge to edge |
|
|
103
|
+
| `Table` | Semantic table parts with the header band, density row heights and selected rows |
|
|
104
|
+
| `Badge` | A static status pill in six tones |
|
|
105
|
+
| `Avatar` | A person's photo, or their initials |
|
|
106
|
+
| `Progress`, `Meter` | A task advancing, or a measurement within a range |
|
|
107
|
+
| `Skeleton` | A placeholder shaped like the content that's loading |
|
|
108
|
+
|
|
109
|
+
**Layout:** `className` is for placement only (margin, width, grid placement). Visual changes go
|
|
110
|
+
through props, so every Qeet product looks the same. On a form control, `className` and `style` place
|
|
111
|
+
the whole row, while `ref` and every other prop go to the control itself.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { Checkbox, CheckboxGroup, Fieldset, Radio, RadioGroup, TextField } from "@qeetrix/react";
|
|
115
|
+
|
|
116
|
+
<Fieldset legend="Company">
|
|
117
|
+
<TextField label="Trade name" name="tradeName" optional />
|
|
118
|
+
<TextField label="GSTIN" name="gstin" required description="15 characters." />
|
|
119
|
+
<RadioGroup label="Billing cycle" name="cycle" defaultValue="monthly">
|
|
120
|
+
<Radio value="monthly" label="Monthly" />
|
|
121
|
+
<Radio value="yearly" label="Yearly" description="Two months free." />
|
|
122
|
+
</RadioGroup>
|
|
123
|
+
<CheckboxGroup label="Notify me by" name="notify" orientation="horizontal">
|
|
124
|
+
<Checkbox value="email" label="Email" />
|
|
125
|
+
<Checkbox value="sms" label="SMS" />
|
|
126
|
+
</CheckboxGroup>
|
|
127
|
+
</Fieldset>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Locale, direction and strings
|
|
131
|
+
|
|
132
|
+
Wrap the app in `QeetrixProvider` to set the locale (used for formatting), the reading direction and
|
|
133
|
+
the library's own strings ("Close", "(optional)", "Loading"…). Locale packs are separate imports, so
|
|
134
|
+
a language you don't use never ships:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { QeetrixProvider } from "@qeetrix/react";
|
|
138
|
+
import { hi } from "@qeetrix/react/locales/hi";
|
|
139
|
+
|
|
140
|
+
<QeetrixProvider locale="hi-IN" messages={hi}>{app}</QeetrixProvider>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- **Override one string:** pass a `messages` prop to the component, e.g.
|
|
144
|
+
`<Field messages={{ optional: "(if any)" }}>`.
|
|
145
|
+
- **Right-to-left:** use `direction="rtl"`, and set `dir="rtl"` on `<html>` too.
|
|
146
|
+
- **Server-safe components** (Link, Spinner, SkipLink) can't read the provider. Pass their strings
|
|
147
|
+
per instance.
|
|
148
|
+
|
|
149
|
+
Formatting helpers are plain functions and work on the server too:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { formatCurrency, formatNumber, formatRelativeTime } from "@qeetrix/react";
|
|
153
|
+
|
|
154
|
+
formatCurrency(124500, "INR"); // "₹1,24,500.00"
|
|
155
|
+
formatNumber(1234567); // "12,34,567"
|
|
156
|
+
formatRelativeTime(updatedAt, "en-IN", now); // "2 minutes ago"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Every primitive also works in Windows High Contrast (forced colours) and under reduced motion. Tests
|
|
160
|
+
emulate both.
|
|
161
|
+
|
|
162
|
+
## Experimental: the console frame
|
|
163
|
+
|
|
164
|
+
Components and patterns in their first product ship from `@qeetrix/react/experimental`. They may
|
|
165
|
+
change in a patch release until they're Ready.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import {
|
|
169
|
+
AppShell, PageContent, PageHeader, Sidebar, SidebarGroup, SidebarItem, Stat, StatGroup,
|
|
170
|
+
TopBar, TopBarSearch, TopBarSwitcher,
|
|
171
|
+
} from "@qeetrix/react/experimental";
|
|
172
|
+
import { Menu, MenuContent, MenuItem, MenuTrigger } from "@qeetrix/react";
|
|
173
|
+
|
|
174
|
+
<AppShell
|
|
175
|
+
topBar={
|
|
176
|
+
<TopBar
|
|
177
|
+
logo={<QeetLogo variant="dark" height={20} />}
|
|
178
|
+
product="Qeet ID"
|
|
179
|
+
context={
|
|
180
|
+
<>
|
|
181
|
+
<Menu>
|
|
182
|
+
<MenuTrigger render={<TopBarSwitcher mark="AC" label="Acme Pvt Ltd" />} />
|
|
183
|
+
<MenuContent>{organisations}</MenuContent>
|
|
184
|
+
</Menu>
|
|
185
|
+
<TopBarSwitcher status="success" label="Production" />
|
|
186
|
+
</>
|
|
187
|
+
}
|
|
188
|
+
search={<TopBarSearch placeholder="Search or jump to…" shortcut={["⌘", "K"]} onClick={openPalette} />}
|
|
189
|
+
account={accountMenu}
|
|
190
|
+
/>
|
|
191
|
+
}
|
|
192
|
+
sidebar={
|
|
193
|
+
<Sidebar label="Qeet ID">
|
|
194
|
+
<SidebarGroup label="Directory">
|
|
195
|
+
<SidebarItem label="Users" icon={UsersIcon} current render={<Link to="/users" />} />
|
|
196
|
+
</SidebarGroup>
|
|
197
|
+
</Sidebar>
|
|
198
|
+
}
|
|
199
|
+
drawers={[{ id: "help", label: "Help", icon: CircleQuestionMarkIcon, content: help }]}
|
|
200
|
+
>
|
|
201
|
+
<PageHeader
|
|
202
|
+
title="Users"
|
|
203
|
+
actions={<Button icon={PlusIcon}>Invite users</Button>}
|
|
204
|
+
summary={
|
|
205
|
+
<StatGroup>
|
|
206
|
+
<Stat label="Total users" value={1284} note="+38 this week" tone="positive" trend={weekly} />
|
|
207
|
+
</StatGroup>
|
|
208
|
+
}
|
|
209
|
+
/>
|
|
210
|
+
<PageContent>{table}</PageContent>
|
|
211
|
+
</AppShell>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The bar is black in light and dark, topped by a 2px Ember line; the navigation collapses to a 48px
|
|
215
|
+
rail that expands while hovered. Also there: `Breadcrumb`, `EmptyState` and `DataState` (loading,
|
|
216
|
+
error, empty, content), and the secure-entry parts:
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
<Alert tone="warning" title="Copy this client secret now">It can't be shown again.</Alert>
|
|
220
|
+
<Field label="Client secret">
|
|
221
|
+
<PasswordInput readOnly autoComplete="off" defaultValue={secret}
|
|
222
|
+
endAdornment={<CopyButton value={secret} label="Copy client secret" size="sm" />} />
|
|
223
|
+
</Field>
|
|
224
|
+
<Field label="Verification code"><OTPInput size="lg" onValueComplete={verify} /></Field>
|
|
225
|
+
```
|
|
226
|
+
Colour-scheme switching lives in `QeetrixProvider`
|
|
227
|
+
(`defaultColorScheme="system"`, `useColorScheme()`).
|
|
228
|
+
|
|
229
|
+
## Overlays
|
|
230
|
+
|
|
231
|
+
Overlays share one shape: a root that holds the state, a trigger that renders your control, and a
|
|
232
|
+
content part that assembles everything else. Wrap the app once in `ToastProvider` (and, if you like,
|
|
233
|
+
`TooltipProvider`).
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import {
|
|
237
|
+
Button, Dialog, DialogClose, DialogContent, DialogFooter, DialogTrigger, useToast,
|
|
238
|
+
} from "@qeetrix/react";
|
|
239
|
+
|
|
240
|
+
function RevokeKey({ onRevoke }: { onRevoke: () => Promise<void> }) {
|
|
241
|
+
const toast = useToast();
|
|
242
|
+
return (
|
|
243
|
+
<Dialog intent="danger">
|
|
244
|
+
<DialogTrigger render={<Button tone="danger">Revoke key</Button>} />
|
|
245
|
+
<DialogContent title="Revoke this API key?" description="Apps using it stop working at once.">
|
|
246
|
+
<DialogFooter>
|
|
247
|
+
<DialogClose render={<Button variant="ghost">Cancel</Button>} />
|
|
248
|
+
<DialogClose
|
|
249
|
+
render={<Button tone="danger">Revoke key</Button>}
|
|
250
|
+
onClick={() =>
|
|
251
|
+
toast.promise(onRevoke(), {
|
|
252
|
+
loading: { title: "Revoking…" },
|
|
253
|
+
success: { title: "Key revoked" },
|
|
254
|
+
error: { title: "Couldn't revoke the key" },
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
/>
|
|
258
|
+
</DialogFooter>
|
|
259
|
+
</DialogContent>
|
|
260
|
+
</Dialog>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
29
265
|
## License
|
|
30
266
|
|
|
31
267
|
[MIT](https://github.com/qeetgroup/qeetrix-react/blob/main/LICENSE) © Qeet Group
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { HeadingLevel } from "../heading/heading.js";
|
|
2
|
+
import { Accordion } from "@base-ui/react/accordion";
|
|
3
|
+
import { Collapsible } from "@base-ui/react/collapsible";
|
|
4
|
+
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
5
|
+
export interface AccordionProps<Value = string> extends Omit<Accordion.Root.Props<Value>, "className" | "style"> {
|
|
6
|
+
/** Placement only. */
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
}
|
|
10
|
+
export interface AccordionItemProps extends Omit<Accordion.Item.Props, "className" | "style" | "title" | "children"> {
|
|
11
|
+
/** The section's title: the trigger's text. */
|
|
12
|
+
title: ReactNode;
|
|
13
|
+
/** The heading level that wraps the trigger. Default 3. */
|
|
14
|
+
headingLevel?: Exclude<HeadingLevel, 1>;
|
|
15
|
+
/** The section's content. */
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
className?: string;
|
|
18
|
+
style?: CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
export type CollapsibleProps = Collapsible.Root.Props;
|
|
21
|
+
export type CollapsibleTriggerProps = Collapsible.Trigger.Props;
|
|
22
|
+
export interface CollapsiblePanelProps extends Omit<Collapsible.Panel.Props, "className" | "style"> {
|
|
23
|
+
className?: string;
|
|
24
|
+
style?: CSSProperties;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Stacked sections that show and hide, ruled between (06-component-specs.md, increment 5):
|
|
28
|
+
* `value` / `defaultValue` / `onValueChange(value[], details)`, `multiple`, `disabled`.
|
|
29
|
+
*/
|
|
30
|
+
declare function Accordion$1<Value = string>({ className, ...props }: AccordionProps<Value>): ReactElement;
|
|
31
|
+
/**
|
|
32
|
+
* One section: a full-width trigger with a chevron, inside a heading, then the panel. Find in page
|
|
33
|
+
* opens a closed section that holds the match.
|
|
34
|
+
*/
|
|
35
|
+
export declare function AccordionItem({ title, headingLevel, className, children, ...props }: AccordionItemProps): ReactElement;
|
|
36
|
+
/** One section without the rules, for "Show more" or advanced options. */
|
|
37
|
+
declare function Collapsible$1(props: CollapsibleProps): ReactElement;
|
|
38
|
+
/** Shows and hides the panel. Render your own control: `render={<Button variant="ghost" />}`. */
|
|
39
|
+
export declare function CollapsibleTrigger(props: CollapsibleTriggerProps): ReactElement;
|
|
40
|
+
/** The content that shows and hides, with the accordion's height animation. */
|
|
41
|
+
export declare function CollapsiblePanel({ className, ...props }: CollapsiblePanelProps): ReactElement;
|
|
42
|
+
export { Accordion$1 as Accordion, Collapsible$1 as Collapsible };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../internal/cx.js";
|
|
3
|
+
import { Accordion } from "@base-ui/react/accordion";
|
|
4
|
+
import { Collapsible } from "@base-ui/react/collapsible";
|
|
5
|
+
import { ChevronDownIcon } from "@qeetrix/icons/icons/chevron-down";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
// src/accordion/accordion.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Stacked sections that show and hide, ruled between (06-component-specs.md, increment 5):
|
|
10
|
+
* `value` / `defaultValue` / `onValueChange(value[], details)`, `multiple`, `disabled`.
|
|
11
|
+
*/
|
|
12
|
+
function Accordion$1({ className, ...props }) {
|
|
13
|
+
return /* @__PURE__ */ jsx(Accordion.Root, {
|
|
14
|
+
...props,
|
|
15
|
+
className: cx("qx-accordion", className),
|
|
16
|
+
"data-slot": "accordion"
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One section: a full-width trigger with a chevron, inside a heading, then the panel. Find in page
|
|
21
|
+
* opens a closed section that holds the match.
|
|
22
|
+
*/
|
|
23
|
+
function AccordionItem({ title, headingLevel = 3, className, children, ...props }) {
|
|
24
|
+
const Heading = `h${headingLevel}`;
|
|
25
|
+
return /* @__PURE__ */ jsxs(Accordion.Item, {
|
|
26
|
+
...props,
|
|
27
|
+
className: cx("qx-accordion-item", className),
|
|
28
|
+
"data-slot": "accordion-item",
|
|
29
|
+
children: [/* @__PURE__ */ jsx(Accordion.Header, {
|
|
30
|
+
render: /* @__PURE__ */ jsx(Heading, {}),
|
|
31
|
+
className: "qx-accordion-header",
|
|
32
|
+
children: /* @__PURE__ */ jsxs(Accordion.Trigger, {
|
|
33
|
+
className: "qx-accordion-trigger",
|
|
34
|
+
"data-slot": "accordion-trigger",
|
|
35
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
36
|
+
className: "qx-accordion-title",
|
|
37
|
+
children: title
|
|
38
|
+
}), /* @__PURE__ */ jsx(ChevronDownIcon, {
|
|
39
|
+
shape: "sharp",
|
|
40
|
+
size: 16,
|
|
41
|
+
"aria-hidden": true,
|
|
42
|
+
className: "qx-accordion-icon"
|
|
43
|
+
})]
|
|
44
|
+
})
|
|
45
|
+
}), /* @__PURE__ */ jsx(Accordion.Panel, {
|
|
46
|
+
hiddenUntilFound: true,
|
|
47
|
+
className: "qx-accordion-panel",
|
|
48
|
+
"data-slot": "accordion-panel",
|
|
49
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
50
|
+
className: "qx-accordion-content",
|
|
51
|
+
children
|
|
52
|
+
})
|
|
53
|
+
})]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** One section without the rules, for "Show more" or advanced options. */
|
|
57
|
+
function Collapsible$1(props) {
|
|
58
|
+
return /* @__PURE__ */ jsx(Collapsible.Root, { ...props });
|
|
59
|
+
}
|
|
60
|
+
/** Shows and hides the panel. Render your own control: `render={<Button variant="ghost" />}`. */
|
|
61
|
+
function CollapsibleTrigger(props) {
|
|
62
|
+
return /* @__PURE__ */ jsx(Collapsible.Trigger, { ...props });
|
|
63
|
+
}
|
|
64
|
+
/** The content that shows and hides, with the accordion's height animation. */
|
|
65
|
+
function CollapsiblePanel({ className, ...props }) {
|
|
66
|
+
return /* @__PURE__ */ jsx(Collapsible.Panel, {
|
|
67
|
+
...props,
|
|
68
|
+
className: cx("qx-collapsible-panel", className),
|
|
69
|
+
"data-slot": "collapsible-panel"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export { Accordion$1 as Accordion, AccordionItem, Collapsible$1 as Collapsible, CollapsiblePanel, CollapsibleTrigger };
|