@bzync/rui 0.0.5 → 0.0.9
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/README.md +476 -57
- package/dist/autocomplete-D6z5B-WU.cjs +1 -0
- package/dist/{autocomplete-BgKbj6M4.js → autocomplete-bgySuP8X.js} +149 -142
- package/dist/calendar-CQumLNgS.cjs +1 -0
- package/dist/{calendar-j2_zvum1.js → calendar-nkYCMEy3.js} +9 -9
- package/dist/charts-Cot5lCgC.cjs +1 -0
- package/dist/{charts-CxEy2c3M.js → charts-Dscl6UC3.js} +122 -145
- package/dist/cn-B6R3HKzx.cjs +1 -0
- package/dist/{cn-DpgY2leY.js → cn-BJyZJP-y.js} +389 -389
- package/dist/{component-styles-Bgy44fc8.js → component-styles-C2CbHEEA.js} +2 -2
- package/dist/component-styles-DY4wigiq.cjs +1 -0
- package/dist/components/accordion.cjs +1 -1
- package/dist/components/accordion.js +2 -2
- package/dist/components/alert.cjs +1 -1
- package/dist/components/alert.js +3 -3
- package/dist/components/aspect-ratio.cjs +1 -1
- package/dist/components/aspect-ratio.js +1 -1
- package/dist/components/auth-backdrop.cjs +1 -1
- package/dist/components/auth-backdrop.d.ts +4 -1
- package/dist/components/auth-backdrop.js +12 -8
- package/dist/components/autocomplete/dropdown.d.ts +4 -1
- package/dist/components/autocomplete/multi-trigger.d.ts +3 -1
- package/dist/components/autocomplete/single-trigger.d.ts +3 -1
- package/dist/components/autocomplete/types.d.ts +15 -0
- package/dist/components/autocomplete.cjs +1 -1
- package/dist/components/autocomplete.js +1 -1
- package/dist/components/avatar-group.cjs +1 -1
- package/dist/components/avatar-group.js +1 -1
- package/dist/components/avatar.cjs +1 -1
- package/dist/components/avatar.js +2 -2
- package/dist/components/badge.cjs +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/billing-interval-toggle.cjs +1 -1
- package/dist/components/billing-interval-toggle.d.ts +4 -6
- package/dist/components/billing-interval-toggle.js +26 -19
- package/dist/components/blockquote.cjs +1 -1
- package/dist/components/blockquote.js +3 -3
- package/dist/components/breadcrumb.cjs +1 -1
- package/dist/components/breadcrumb.js +2 -2
- package/dist/components/button-group.cjs +1 -1
- package/dist/components/button-group.js +1 -1
- package/dist/components/button.cjs +1 -1
- package/dist/components/button.d.ts +2 -0
- package/dist/components/button.js +79 -36
- package/dist/components/calendar.cjs +1 -1
- package/dist/components/calendar.js +1 -1
- package/dist/components/callout.cjs +1 -1
- package/dist/components/callout.js +14 -14
- package/dist/components/card.cjs +1 -1
- package/dist/components/card.js +1 -1
- package/dist/components/charts/shared.d.ts +15 -0
- package/dist/components/charts.cjs +1 -1
- package/dist/components/charts.js +1 -1
- package/dist/components/checkbox.cjs +1 -1
- package/dist/components/checkbox.js +3 -3
- package/dist/components/code.cjs +3 -3
- package/dist/components/code.js +4 -4
- package/dist/components/command.cjs +1 -1
- package/dist/components/command.d.ts +8 -1
- package/dist/components/command.js +84 -83
- package/dist/components/confirm-dialog.cjs +1 -1
- package/dist/components/confirm-dialog.d.ts +8 -1
- package/dist/components/confirm-dialog.js +37 -31
- package/dist/components/copy-button.cjs +1 -1
- package/dist/components/copy-button.js +3 -2
- package/dist/components/currency.cjs +1 -1
- package/dist/components/currency.js +1 -1
- package/dist/components/datatable/types.d.ts +4 -0
- package/dist/components/datatable.cjs +1 -1
- package/dist/components/datatable.d.ts +1 -5
- package/dist/components/datatable.js +1 -1
- package/dist/components/datepicker.cjs +1 -1
- package/dist/components/datepicker.d.ts +3 -3
- package/dist/components/datepicker.js +234 -105
- package/dist/components/description-list.cjs +1 -1
- package/dist/components/description-list.js +1 -1
- package/dist/components/divider.cjs +1 -1
- package/dist/components/divider.js +1 -1
- package/dist/components/drawer.cjs +1 -1
- package/dist/components/drawer.d.ts +6 -1
- package/dist/components/drawer.js +128 -2
- package/dist/components/dropdown-menu-panel.cjs +1 -0
- package/dist/components/dropdown-menu-panel.d.ts +24 -0
- package/dist/components/dropdown-menu-panel.js +84 -0
- package/dist/components/dropdown-menu.cjs +1 -1
- package/dist/components/dropdown-menu.d.ts +5 -1
- package/dist/components/dropdown-menu.js +38 -93
- package/dist/components/empty-state.cjs +1 -1
- package/dist/components/empty-state.d.ts +5 -1
- package/dist/components/empty-state.js +9 -9
- package/dist/components/error-state.cjs +1 -1
- package/dist/components/error-state.js +2 -2
- package/dist/components/file-upload.cjs +1 -1
- package/dist/components/file-upload.js +4 -4
- package/dist/components/form-field.cjs +1 -1
- package/dist/components/form-field.d.ts +9 -0
- package/dist/components/form-field.js +49 -14
- package/dist/components/info-button.cjs +1 -1
- package/dist/components/info-button.d.ts +5 -3
- package/dist/components/info-button.js +33 -29
- package/dist/components/input.cjs +1 -1
- package/dist/components/input.js +3 -3
- package/dist/components/kbd.cjs +1 -1
- package/dist/components/kbd.js +1 -1
- package/dist/components/label.cjs +1 -1
- package/dist/components/label.js +2 -2
- package/dist/components/layout.cjs +1 -1
- package/dist/components/layout.js +1 -1
- package/dist/components/link.cjs +1 -1
- package/dist/components/link.js +1 -1
- package/dist/components/list.cjs +1 -1
- package/dist/components/list.js +1 -1
- package/dist/components/modal.cjs +1 -1
- package/dist/components/modal.d.ts +8 -1
- package/dist/components/modal.js +74 -120
- package/dist/components/navigation.cjs +1 -1
- package/dist/components/navigation.js +2 -2
- package/dist/components/number-input.cjs +1 -1
- package/dist/components/number-input.js +3 -3
- package/dist/components/otp-input.cjs +1 -1
- package/dist/components/otp-input.js +2 -2
- package/dist/components/overlay.cjs +1 -0
- package/dist/components/overlay.d.ts +52 -0
- package/dist/components/overlay.js +87 -0
- package/dist/components/pagination.cjs +1 -1
- package/dist/components/pagination.js +2 -2
- package/dist/components/popover.cjs +1 -1
- package/dist/components/popover.d.ts +2 -1
- package/dist/components/popover.js +41 -40
- package/dist/components/progressbar.cjs +1 -1
- package/dist/components/progressbar.js +1 -1
- package/dist/components/radio.cjs +1 -1
- package/dist/components/radio.js +3 -3
- package/dist/components/rating.cjs +1 -1
- package/dist/components/rating.js +3 -3
- package/dist/components/richtext.cjs +1 -1
- package/dist/components/richtext.js +45 -18
- package/dist/components/scroll-area.cjs +1 -1
- package/dist/components/scroll-area.js +3 -3
- package/dist/components/select/option-list.d.ts +3 -1
- package/dist/components/select/types.d.ts +4 -0
- package/dist/components/select.cjs +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/seo.cjs +1 -1
- package/dist/components/seo.js +1 -1
- package/dist/components/separator.cjs +1 -1
- package/dist/components/separator.js +1 -1
- package/dist/components/skeleton.cjs +1 -1
- package/dist/components/skeleton.d.ts +14 -1
- package/dist/components/skeleton.js +56 -4
- package/dist/components/slider.cjs +1 -1
- package/dist/components/slider.js +2 -2
- package/dist/components/slot.cjs +1 -0
- package/dist/components/slot.d.ts +12 -0
- package/dist/components/slot.js +16 -0
- package/dist/components/snackbar-viewport.cjs +1 -0
- package/dist/components/snackbar-viewport.d.ts +27 -0
- package/dist/components/snackbar-viewport.js +181 -0
- package/dist/components/snackbar.cjs +1 -1
- package/dist/components/snackbar.d.ts +5 -1
- package/dist/components/snackbar.js +36 -200
- package/dist/components/spinner.cjs +1 -1
- package/dist/components/spinner.js +1 -1
- package/dist/components/stat.cjs +1 -1
- package/dist/components/stat.js +1 -1
- package/dist/components/status-dot.cjs +1 -1
- package/dist/components/status-dot.js +1 -1
- package/dist/components/stepper.cjs +1 -1
- package/dist/components/stepper.js +5 -5
- package/dist/components/switch.cjs +1 -1
- package/dist/components/switch.js +37 -43
- package/dist/components/table.cjs +1 -1
- package/dist/components/table.js +1 -1
- package/dist/components/tabs.cjs +1 -1
- package/dist/components/tabs.d.ts +6 -4
- package/dist/components/tabs.js +51 -81
- package/dist/components/tag.cjs +1 -1
- package/dist/components/tag.js +1 -1
- package/dist/components/terminal.cjs +1 -1
- package/dist/components/terminal.js +1 -1
- package/dist/components/textarea.cjs +1 -1
- package/dist/components/textarea.js +3 -3
- package/dist/components/theme-provider.cjs +1 -1
- package/dist/components/theme-provider.js +1 -1
- package/dist/components/theme-toggle.cjs +1 -1
- package/dist/components/theme-toggle.js +27 -3
- package/dist/components/time-picker.cjs +1 -1
- package/dist/components/time-picker.d.ts +2 -0
- package/dist/components/time-picker.js +206 -148
- package/dist/components/timeline.cjs +1 -1
- package/dist/components/timeline.js +1 -1
- package/dist/components/toggle.cjs +1 -1
- package/dist/components/toggle.js +2 -2
- package/dist/components/tooltip.cjs +1 -1
- package/dist/components/tooltip.d.ts +2 -1
- package/dist/components/tooltip.js +37 -36
- package/dist/components/tree.cjs +1 -1
- package/dist/components/tree.js +1 -1
- package/dist/components/typography.cjs +1 -1
- package/dist/components/typography.d.ts +2 -3
- package/dist/components/typography.js +37 -24
- package/dist/components/visually-hidden.cjs +1 -0
- package/dist/components/visually-hidden.d.ts +16 -0
- package/dist/components/visually-hidden.js +13 -0
- package/dist/{datatable-yBN4M3UV.js → datatable-BJoZMrKI.js} +6 -6
- package/dist/datatable-t0U7t_xh.cjs +1 -0
- package/dist/icons-Dfetr1L2.cjs +1 -0
- package/dist/icons-Dk7VZ4Ph.js +51 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +126 -99
- package/dist/lib/cn.d.ts +13 -1
- package/dist/lib/component-styles.d.ts +14 -2
- package/dist/lib/icons.d.ts +12 -0
- package/dist/lib/motion.d.ts +58 -0
- package/dist/motion-B2shivEB.cjs +1 -0
- package/dist/motion-gnRzFS5f.js +62 -0
- package/dist/select-ByjnuEcE.cjs +1 -0
- package/dist/{select-ClWrcj3R.js → select-CiMzLhUK.js} +128 -122
- package/dist/styles.css +1 -1
- package/dist/{terminal-CpraMs50.cjs → terminal-1U0ftG3i.cjs} +2 -2
- package/dist/{terminal-BOR2cFi1.js → terminal-VXdxVoAv.js} +4 -4
- package/dist/use-focus-trap-CPLT49gN.cjs +1 -0
- package/dist/use-focus-trap-CS6BXJAB.js +77 -0
- package/dist/{utils-CQ8pvCtk.js → utils-DEw5mQrH.js} +1 -1
- package/dist/{utils-DOFJTw8o.cjs → utils-MnnCTelh.cjs} +1 -1
- package/package.json +56 -9
- package/dist/autocomplete-Bk4pRQ2R.cjs +0 -1
- package/dist/calendar-CUXpVrVE.cjs +0 -1
- package/dist/charts-UDqS1BH1.cjs +0 -1
- package/dist/cn-BdjraxxU.cjs +0 -1
- package/dist/component-styles-DOUctc8_.cjs +0 -1
- package/dist/createLucideIcon-DDY8HuQR.js +0 -42
- package/dist/createLucideIcon-b_X_3fj-.cjs +0 -1
- package/dist/datatable-rzDgQ3r6.cjs +0 -1
- package/dist/drawer-CTeBsYrO.cjs +0 -1
- package/dist/drawer-ixuzyLVS.js +0 -189
- package/dist/select-BFwx--uK.cjs +0 -1
- package/dist/theme-toggle-Bb0BYo51.js +0 -71
- package/dist/theme-toggle-Cld8tktJ.cjs +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
|
-
# rui
|
|
1
|
+
# @bzync/rui
|
|
2
2
|
|
|
3
|
-
Composable React UI components with scoped, customizable light and dark themes.
|
|
3
|
+
Composable React UI components with scoped, fully customizable light and dark themes.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@bzync/rui)
|
|
6
|
+
[](https://www.npmjs.com/package/@bzync/rui)
|
|
7
|
+
[](https://bundlephobia.com/package/@bzync/rui)
|
|
8
|
+
[](https://www.npmjs.com/package/@bzync/rui)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
|
|
11
|
+
`@bzync/rui` (**r**eact **ui**) is the component library behind [Bzync](https://www.bzync.com).
|
|
12
|
+
It ships 79 components plus a small SVG chart set, a token-driven theming system, and
|
|
13
|
+
production-grade lifecycle primitives — with **zero runtime dependencies**.
|
|
14
|
+
|
|
15
|
+
- **Zero runtime dependencies.** `dependencies` is empty. `react`, `react-dom`, and
|
|
16
|
+
`framer-motion` are peer dependencies you already control.
|
|
17
|
+
- **Scoped theming.** `ThemeProvider` renders a `.rui-theme` scope, so you can run
|
|
18
|
+
multiple themes on one page or theme a subtree without touching `<html>`.
|
|
19
|
+
- **Token-driven.** Override accent and neutral scales, radii, fonts, spacing, shadows,
|
|
20
|
+
or any CSS variable through a single `palette` prop — per theme.
|
|
21
|
+
- **Light and dark, controlled or uncontrolled.** System-preference tracking and
|
|
22
|
+
persistent selection are built in.
|
|
23
|
+
- **Accessible by default.** Native semantics, keyboard behavior, focus management,
|
|
24
|
+
focus trapping, scroll-lock restoration, and `prefers-reduced-motion` support.
|
|
25
|
+
- **ESM and CJS.** Dual builds, per-component subpath exports, `sideEffects` metadata
|
|
26
|
+
for tree-shaking, and readable (unminified) published output.
|
|
27
|
+
- **Typed.** Written in TypeScript; declaration files ship with the package.
|
|
28
|
+
- **RSC-friendly.** Client components are marked `"use client"`; server components can
|
|
29
|
+
import and render them directly.
|
|
30
|
+
- **React 18.2 and React 19.**
|
|
31
|
+
|
|
32
|
+
## Table of contents
|
|
33
|
+
|
|
34
|
+
- [Installation](#installation)
|
|
35
|
+
- [Quick start](#quick-start)
|
|
36
|
+
- [The stylesheet](#the-stylesheet)
|
|
37
|
+
- [Theming](#theming)
|
|
38
|
+
- [Importing components](#importing-components)
|
|
39
|
+
- [Server components and SSR](#server-components-and-ssr)
|
|
40
|
+
- [Accessibility and motion](#accessibility-and-motion)
|
|
41
|
+
- [Component catalog](#component-catalog)
|
|
42
|
+
- [Common recipes](#common-recipes)
|
|
43
|
+
- [Charts](#charts)
|
|
44
|
+
- [Hooks and utilities](#hooks-and-utilities)
|
|
45
|
+
- [TypeScript](#typescript)
|
|
46
|
+
- [Browser and React support](#browser-and-react-support)
|
|
47
|
+
- [Versioning](#versioning)
|
|
48
|
+
- [Documentation](#documentation)
|
|
49
|
+
- [Contributing](#contributing)
|
|
50
|
+
- [Security](#security)
|
|
51
|
+
- [License](#license)
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npm install @bzync/rui framer-motion
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
pnpm add @bzync/rui framer-motion
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
yarn add @bzync/rui framer-motion
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`react` and `react-dom` (`^18.2.0 || ^19.0.0`) and `framer-motion` (`^13.1.0`) are peer
|
|
68
|
+
dependencies. `framer-motion` powers the animated overlays (Modal, Drawer, Popover,
|
|
69
|
+
Snackbar); it is kept off the critical path and loaded only by the components that use it.
|
|
70
|
+
|
|
71
|
+
## Quick start
|
|
4
72
|
|
|
5
73
|
```tsx
|
|
6
74
|
import { Button, ThemeProvider, ThemeToggle } from "@bzync/rui"
|
|
@@ -27,94 +95,445 @@ export function App() {
|
|
|
27
95
|
}
|
|
28
96
|
```
|
|
29
97
|
|
|
30
|
-
Every component accepts `className` (and native element props where applicable), so
|
|
31
|
-
changes compose with the defaults
|
|
32
|
-
system preference, persistent selection, scoped themes, custom accent/neutral scales, and any
|
|
33
|
-
CSS variable through `tokens`. `ThemeToggle` accepts custom icons, labels, and classes.
|
|
98
|
+
Every component accepts `className` (and native element props where applicable), so
|
|
99
|
+
one-off changes compose with the defaults instead of fighting them.
|
|
34
100
|
|
|
35
|
-
##
|
|
101
|
+
## The stylesheet
|
|
36
102
|
|
|
37
|
-
|
|
38
|
-
|
|
103
|
+
Import the core stylesheet once, near the application root:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import "@bzync/rui/styles.css"
|
|
39
107
|
```
|
|
40
108
|
|
|
41
|
-
|
|
109
|
+
It defines the semantic design tokens, the `.rui-theme` scope, the dark-mode variant,
|
|
110
|
+
and component styles. It does **not** download or bundle webfonts — `@bzync/rui` uses
|
|
111
|
+
system fallbacks by default to keep the stylesheet small and avoid unexpected network
|
|
112
|
+
requests. Load any font in your application and override `--font-sans`, `--font-display`,
|
|
113
|
+
and `--font-mono` on `.rui-theme` (or via `ThemeProvider`'s `fonts` option).
|
|
114
|
+
|
|
115
|
+
## Theming
|
|
116
|
+
|
|
117
|
+
### ThemeProvider
|
|
118
|
+
|
|
119
|
+
`ThemeProvider` establishes a theme scope. Place one near the root, or wrap any subtree
|
|
120
|
+
to give it its own theme.
|
|
42
121
|
|
|
43
122
|
```tsx
|
|
44
|
-
import "@bzync/rui
|
|
123
|
+
import { ThemeProvider } from "@bzync/rui"
|
|
124
|
+
|
|
125
|
+
<ThemeProvider
|
|
126
|
+
defaultTheme="system" // "light" | "dark" | "system"
|
|
127
|
+
storageKey="app-theme" // localStorage key, or false to disable persistence
|
|
128
|
+
applyToRoot // also toggle the `dark` class on <html>
|
|
129
|
+
palette={{ accent, neutral, radius, fonts, spacing, shadows, colors, tokens }}
|
|
130
|
+
lightPalette={{ /* overrides applied only in light mode */ }}
|
|
131
|
+
darkPalette={{ /* overrides applied only in dark mode */ }}
|
|
132
|
+
>
|
|
133
|
+
{children}
|
|
134
|
+
</ThemeProvider>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Controlled usage is supported by passing `theme` and `onThemeChange` instead of
|
|
138
|
+
`defaultTheme`.
|
|
139
|
+
|
|
140
|
+
**Palette shape** (`ThemePalette`, all fields optional):
|
|
141
|
+
|
|
142
|
+
| Field | Type | Purpose |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| `accent` | `Partial<Record<50‥950, string>>` | Primary/action color scale |
|
|
145
|
+
| `neutral` | `Partial<Record<50‥950, string>>` | Grayscale / surfaces / borders |
|
|
146
|
+
| `colors` | `ThemeColors` | Named semantic colors (bg, surface, foreground, border, status colors, focus ring…) |
|
|
147
|
+
| `radius` | `Partial<Record<"sm"‥"2xl" \| "full", string>>` | Corner radii |
|
|
148
|
+
| `fonts` | `ThemeFonts` | `sans`, `display`, `mono` families |
|
|
149
|
+
| `spacing` | `Record<string, string>` | Spacing scale entries |
|
|
150
|
+
| `shadows` | `Partial<Record<"xs"‥"2xl", string>>` | Elevation shadows |
|
|
151
|
+
| `tokens` | `Partial<Record<\`--${string}\`, string \| number>>` | Any CSS custom property, escape hatch |
|
|
152
|
+
|
|
153
|
+
### useTheme
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { useTheme } from "@bzync/rui"
|
|
157
|
+
|
|
158
|
+
function Example() {
|
|
159
|
+
const { theme, resolvedTheme, setTheme, toggleTheme } = useTheme()
|
|
160
|
+
// theme: "light" | "dark" | "system" (the user's selection)
|
|
161
|
+
// resolvedTheme: "light" | "dark" (after resolving "system")
|
|
162
|
+
return <button onClick={toggleTheme}>Now: {resolvedTheme}</button>
|
|
163
|
+
}
|
|
45
164
|
```
|
|
46
165
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
166
|
+
`useTheme` must be called inside a `ThemeProvider`.
|
|
167
|
+
|
|
168
|
+
### ThemeToggle
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { ThemeToggle } from "@bzync/rui"
|
|
172
|
+
|
|
173
|
+
<ThemeToggle
|
|
174
|
+
showLabel
|
|
175
|
+
lightLabel="Light"
|
|
176
|
+
darkLabel="Dark"
|
|
177
|
+
lightIcon={<SunIcon />}
|
|
178
|
+
darkIcon={<MoonIcon />}
|
|
179
|
+
/>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Renders a button with `aria-pressed` and an `aria-label` that reflects the current state.
|
|
183
|
+
|
|
184
|
+
### Semantic tokens
|
|
185
|
+
|
|
186
|
+
Author custom components against the semantic tokens rather than raw palette values:
|
|
187
|
+
`bg`, `surface`, `surface-raised`, `surface-muted`, `foreground`, `muted-foreground`,
|
|
188
|
+
`border`, `border-strong`, `primary`, `primary-foreground`, `destructive`, the status
|
|
189
|
+
colors, and `focus-ring`. They adapt to light/dark and to any palette override
|
|
190
|
+
automatically.
|
|
191
|
+
|
|
192
|
+
## Importing components
|
|
193
|
+
|
|
194
|
+
The root entry re-exports everything for convenience:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { Button, Modal, DataTable } from "@bzync/rui"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Per-component subpath entries let a bundler pull in only what an application uses:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import { Button } from "@bzync/rui/button"
|
|
204
|
+
import { Modal } from "@bzync/rui/modal"
|
|
205
|
+
import { BarChart } from "@bzync/rui/charts"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Both forms are tree-shakeable — the package sets `"sideEffects": ["**/*.css"]` and ships
|
|
209
|
+
ESM — but subpath imports keep dependency graphs smallest and are the recommended default
|
|
210
|
+
for libraries and performance-sensitive apps.
|
|
211
|
+
|
|
212
|
+
## Server components and SSR
|
|
213
|
+
|
|
214
|
+
Client components are marked with the `"use client"` directive, so React Server
|
|
215
|
+
Components and frameworks like Next.js App Router can import and render them directly.
|
|
216
|
+
`ThemeProvider` reads `localStorage` only on the client and resolves `"system"` with
|
|
217
|
+
`matchMedia`; render it in a client boundary and pass a `defaultTheme` so the server and
|
|
218
|
+
first client paint agree.
|
|
50
219
|
|
|
51
220
|
## Accessibility and motion
|
|
52
221
|
|
|
53
|
-
Interactive components expose native semantics and keyboard behavior.
|
|
54
|
-
content, trap keyboard focus, close
|
|
55
|
-
|
|
56
|
-
|
|
222
|
+
- Interactive components expose native element semantics and keyboard behavior.
|
|
223
|
+
- Modal dialogs label their content, trap keyboard focus, close on <kbd>Escape</kbd>,
|
|
224
|
+
restore focus to the previously focused element on close, and preserve the page's
|
|
225
|
+
prior scroll-lock state.
|
|
226
|
+
- Menus, listboxes, tabs, and toggles implement roving focus and arrow-key navigation.
|
|
227
|
+
- Non-essential animation inside the `.rui-theme` scope is disabled when the user has
|
|
228
|
+
`prefers-reduced-motion: reduce` set.
|
|
229
|
+
- Icon-only controls (`IconButton`, `InfoButton`, `CopyButton`) require an accessible
|
|
230
|
+
`label`.
|
|
231
|
+
|
|
232
|
+
## Component catalog
|
|
233
|
+
|
|
234
|
+
Import any of these from the root or from `@bzync/rui/<kebab-name>`.
|
|
235
|
+
|
|
236
|
+
### Providers and theming
|
|
237
|
+
|
|
238
|
+
| Component / export | Notes |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `ThemeProvider` | Token-driven theme scope; light/dark, controlled or uncontrolled, persistence, `applyToRoot` |
|
|
241
|
+
| `useTheme()` | `{ theme, resolvedTheme, setTheme, toggleTheme }` |
|
|
242
|
+
| `ThemeToggle` | Accessible light/dark switch with custom icons and labels |
|
|
243
|
+
| `SnackbarProvider` / `useSnackbar()` | Programmatic toast notifications |
|
|
244
|
+
| `CommandProvider` / `CommandPalette` | App-wide searchable command palette |
|
|
245
|
+
| `cn()` | `clsx` + `tailwind-merge` class combiner |
|
|
246
|
+
|
|
247
|
+
### Actions
|
|
248
|
+
|
|
249
|
+
| Component | Key props |
|
|
250
|
+
|---|---|
|
|
251
|
+
| `Button` | `variant: primary \| secondary \| ghost \| outline \| destructive \| link`, `size: sm \| md \| lg \| icon`, `loading`, `icon`, `iconPosition` |
|
|
252
|
+
| `ButtonGroup` | `orientation`, `aria-label`; wraps `Button` children with `role="group"` |
|
|
253
|
+
| `IconButton` / `InfoButton` | `label` (required for a11y), icon |
|
|
254
|
+
| `CopyButton` | `value` (text to copy), `label`, `timeout` |
|
|
255
|
+
| `Toggle` / `ToggleGroup` / `ToggleGroupItem` | `pressed` / `defaultPressed`, `onPressedChange`, `type: single \| multiple`, `variant`, `size`, `orientation`, `loop` |
|
|
256
|
+
| `BillingIntervalToggle` | `value: monthly \| yearly`, `onChange` |
|
|
57
257
|
|
|
58
|
-
|
|
258
|
+
### Forms
|
|
59
259
|
|
|
60
|
-
|
|
260
|
+
| Component | Key props |
|
|
261
|
+
|---|---|
|
|
262
|
+
| `Input` | `label`, `hint`, `error`, `prefix` / `suffix`, `size` |
|
|
263
|
+
| `Textarea` | `label`, `hint`, `error`, `rows` |
|
|
264
|
+
| `NumberInput` | `value` / `defaultValue`, `min`, `max`, `step`, `onChange` |
|
|
265
|
+
| `OtpInput` | `length`, `value`, `onChange`, `label` |
|
|
266
|
+
| `Select` | `options`, `label`, `multiple`, groups, searchable, `onChange` |
|
|
267
|
+
| `Autocomplete` | `options`, `multiple`, custom filter, single/multi triggers |
|
|
268
|
+
| `Checkbox` | `label`, `checked` / `defaultChecked`, `onCheckedChange` |
|
|
269
|
+
| `Radio` / `RadioGroup` | `value`, `onChange`, `options` |
|
|
270
|
+
| `Switch` | `label`, `checked`, `onCheckedChange` |
|
|
271
|
+
| `Slider` | `value` / `defaultValue`, `min`, `max`, `step`, `label` |
|
|
272
|
+
| `Rating` | `value` / `defaultValue`, `max`, `size`, `readOnly`, `onValueChange` |
|
|
273
|
+
| `DatePicker` | `label`, `value`, `onChange` |
|
|
274
|
+
| `Calendar` | `value: Date`, `onChange`, month/week views |
|
|
275
|
+
| `TimePicker` | `value` / `defaultValue`, `format: 12 \| 24`, `minuteStep`, `showSeconds`, `min` / `max`, `clearable` |
|
|
276
|
+
| `FileUpload` | `label`, `accept`, `multiple`, `onFilesChange` |
|
|
277
|
+
| `Label` | `htmlFor`, `required`, `hint` |
|
|
278
|
+
| `FormField` | `label`, `htmlFor`, `required`, `hint`, `error` — wraps a control |
|
|
279
|
+
| `Stepper` | `steps`, `activeStep` |
|
|
280
|
+
| `Kbd` | keyboard-key children |
|
|
281
|
+
|
|
282
|
+
### Display
|
|
283
|
+
|
|
284
|
+
| Component | Key props |
|
|
285
|
+
|---|---|
|
|
286
|
+
| `Badge` / `Tag` | `variant`, `dot`; `Tag` adds `onRemove` |
|
|
287
|
+
| `Avatar` / `AvatarGroup` / `AvatarGroupOverflow` | `name`, `src`, `size`, initials fallback; group `spacing`, `count` |
|
|
288
|
+
| `Card` (`CardHeader` / `CardTitle` / `CardDescription` / `CardBody` / `CardFooter`) | composition |
|
|
289
|
+
| `Callout` / `Alert` | `title`, `variant`; `Alert` adds `dismissable`, `onDismiss` |
|
|
290
|
+
| `Stat` / `StatusDot` | `Stat`: `label`, `value`, `trend`, `trendValue`; `StatusDot`: `status`, `label` |
|
|
291
|
+
| `Tooltip` | `content`, trigger child |
|
|
292
|
+
| `Link` | `href`, `variant` |
|
|
293
|
+
| `Typography`: `Heading` / `Text` / `Prose` / `Time` | `Heading`: `as h1‥h6`, `size`, `tone`, `weight`, `balance`; `Text`: `variant`, `size`, number/date/currency formatting; `Time`: renders `<time datetime>` |
|
|
294
|
+
| `Code` / `InlineCode` / `CodeBlock` / `CodeEditor` | `code`, `filename`, `showLineNumbers`, `value` / `onChange` |
|
|
295
|
+
| `Blockquote` | `variant`, `size`, `cite`, `source` / `sourceHref` |
|
|
296
|
+
| `Currency` | `value: number \| bigint`, `currency`, `locale`, `accounting`, `tone`, `size` |
|
|
297
|
+
| `DescriptionList` (`DescriptionItem` / `DescriptionTerm` / `DescriptionDetails`) | `columns: 1 \| 2 \| 3`, `density`, `orientation` |
|
|
298
|
+
| `List` / `ListItem`, `Timeline`, `Tree` | item collections; `Tree` is expandable |
|
|
299
|
+
| `Table` (`TableHeader` / `TableHead` / `TableBody` / `TableRow` / `TableCell`) | styled primitive table |
|
|
300
|
+
| `Divider` / `Separator` | `orientation`, `variant`, `spacing`, optional `label` |
|
|
301
|
+
| `ScrollArea` | `orientation: vertical \| horizontal \| both`, `hideScrollbar`, `keyboardNavigable` |
|
|
302
|
+
| `AspectRatio` | `ratio: number` |
|
|
303
|
+
| `Skeleton` (+ `SkeletonAvatar` / `SkeletonCard` / `SkeletonTable` / `SkeletonText` / `SkeletonTopbar`) | loading placeholders |
|
|
304
|
+
| `Spinner` / `Progressbar` | `size`; `value`, `max` |
|
|
305
|
+
| `EmptyState` / `ErrorState` | `title`, `description`, `error`, action slot |
|
|
306
|
+
| `RichText` / `RichTextEditor` | rendered content and editor |
|
|
307
|
+
| `AuthBackdrop` | decorative auth-screen background |
|
|
308
|
+
|
|
309
|
+
### Overlays and feedback
|
|
310
|
+
|
|
311
|
+
| Component | Key props |
|
|
312
|
+
|---|---|
|
|
313
|
+
| `Modal` (`ModalHeader` / `ModalTitle` / `ModalDescription` / `ModalBody` / `ModalFooter`) | `open`, `onClose`, `title`, `size: sm‥7xl \| full`, `ModalBody` `scrollable` |
|
|
314
|
+
| `Drawer` | `open`, `onClose`, `title`, `size`, focus trap |
|
|
315
|
+
| `ConfirmDialog` | `open`, `onConfirm`, `onCancel`, `title` |
|
|
316
|
+
| `Popover` / `PopoverContent` | trigger, `open` / `onOpenChange` |
|
|
317
|
+
| `DropdownMenu` | `trigger`, `items: { label, onClick }[]` |
|
|
318
|
+
| `Command` / `CommandPalette` / `CommandProvider` | searchable command palette |
|
|
319
|
+
| `SnackbarProvider` / `useSnackbar()` | queue and dismiss toasts programmatically |
|
|
320
|
+
| `Tabs` (`TabsList` / `TabsTrigger` / `TabsContent`) | `value`, `onValueChange` |
|
|
321
|
+
| `Accordion` | `items: { id, trigger, content }[]` |
|
|
322
|
+
| `Pagination` | `page`, `totalPages`, `onPageChange` |
|
|
323
|
+
| `Terminal` / `TerminalBlock` / `TerminalEmulator` | virtual filesystem, runnable shell commands |
|
|
324
|
+
|
|
325
|
+
### Navigation and layout
|
|
326
|
+
|
|
327
|
+
| Component | Key props |
|
|
328
|
+
|---|---|
|
|
329
|
+
| `AppShell` (`AppShellHeader` / `AppShellBody` / `AppShellMain` / `Footer`) | app frame; `sticky`, `scrollable`, `fixed` |
|
|
330
|
+
| `Container` | `size: sm‥xl \| full`, `gutter` |
|
|
331
|
+
| `Stack` / `Inline` / `PageHeader` | spacing and header primitives |
|
|
332
|
+
| `Navbar` / `Sidebar` / `Topbar` / `BottomBar` | `items: NavigationItem[]`, `activeId`, `onSelect` |
|
|
333
|
+
| `NavigationLink` | `id`, `label`, `href`, `icon`, `badge`, `active`, `compact` |
|
|
334
|
+
| `Breadcrumb` | `items` |
|
|
335
|
+
| `SEO` | document head tags |
|
|
336
|
+
|
|
337
|
+
### Data
|
|
338
|
+
|
|
339
|
+
| Component | Key props |
|
|
340
|
+
|---|---|
|
|
341
|
+
| `DataTable<T>` | `columns: ColumnDef<T>[]` (`{ key, header, cell, sortable?, searchable?, align?, width? }`), `data: T[]`, `searchable`, `searchPlaceholder`, `pageSizeOptions`, `density`, `loading`, `emptyMessage`, `onRowClick`, `unstyled` — built-in sort, search, and pagination. `T` must have an `id`. |
|
|
342
|
+
|
|
343
|
+
## Common recipes
|
|
344
|
+
|
|
345
|
+
### Button
|
|
61
346
|
|
|
62
347
|
```tsx
|
|
63
348
|
import { Button } from "@bzync/rui/button"
|
|
349
|
+
|
|
350
|
+
<Button variant="primary" size="lg" loading={isSaving} onClick={save}>
|
|
351
|
+
Save changes
|
|
352
|
+
</Button>
|
|
353
|
+
|
|
354
|
+
<Button variant="outline" icon={<PlusIcon />} iconPosition="left">
|
|
355
|
+
New item
|
|
356
|
+
</Button>
|
|
64
357
|
```
|
|
65
358
|
|
|
66
|
-
|
|
359
|
+
### A labelled, validated field
|
|
67
360
|
|
|
68
|
-
|
|
361
|
+
```tsx
|
|
362
|
+
import { FormField } from "@bzync/rui/form-field"
|
|
363
|
+
import { Input } from "@bzync/rui/input"
|
|
69
364
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
365
|
+
<FormField label="Email" htmlFor="email" required error={errors.email}>
|
|
366
|
+
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
367
|
+
</FormField>
|
|
368
|
+
```
|
|
74
369
|
|
|
75
|
-
|
|
370
|
+
### Select
|
|
76
371
|
|
|
77
|
-
|
|
78
|
-
|
|
372
|
+
```tsx
|
|
373
|
+
import { Select } from "@bzync/rui/select"
|
|
374
|
+
|
|
375
|
+
<Select
|
|
376
|
+
label="Environment"
|
|
377
|
+
options={[
|
|
378
|
+
{ label: "Production", value: "prod" },
|
|
379
|
+
{ label: "Staging", value: "staging" },
|
|
380
|
+
{ label: "Development", value: "dev" },
|
|
381
|
+
]}
|
|
382
|
+
value={env}
|
|
383
|
+
onChange={setEnv}
|
|
384
|
+
/>
|
|
385
|
+
```
|
|
79
386
|
|
|
80
|
-
|
|
387
|
+
### Modal
|
|
81
388
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
389
|
+
```tsx
|
|
390
|
+
import { Modal, ModalBody, ModalFooter } from "@bzync/rui/modal"
|
|
391
|
+
import { Button } from "@bzync/rui/button"
|
|
85
392
|
|
|
86
|
-
|
|
393
|
+
const [open, setOpen] = useState(false)
|
|
87
394
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
395
|
+
<Modal open={open} onClose={() => setOpen(false)} title="Delete project" size="sm">
|
|
396
|
+
<ModalBody>This action cannot be undone.</ModalBody>
|
|
397
|
+
<ModalFooter>
|
|
398
|
+
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
|
|
399
|
+
<Button variant="destructive" onClick={confirmDelete}>Delete</Button>
|
|
400
|
+
</ModalFooter>
|
|
401
|
+
</Modal>
|
|
92
402
|
```
|
|
93
403
|
|
|
94
|
-
|
|
404
|
+
### Toasts
|
|
95
405
|
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
|
|
406
|
+
```tsx
|
|
407
|
+
import { SnackbarProvider, useSnackbar } from "@bzync/rui/snackbar"
|
|
408
|
+
|
|
409
|
+
function Root() {
|
|
410
|
+
return (
|
|
411
|
+
<SnackbarProvider>
|
|
412
|
+
<App />
|
|
413
|
+
</SnackbarProvider>
|
|
414
|
+
)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function SaveButton() {
|
|
418
|
+
const { show } = useSnackbar()
|
|
419
|
+
return <Button onClick={() => show({ title: "Saved", variant: "success" })}>Save</Button>
|
|
420
|
+
}
|
|
99
421
|
```
|
|
100
422
|
|
|
101
|
-
`
|
|
102
|
-
|
|
103
|
-
instead of `patch` when appropriate. Before publishing, GitHub Actions runs the
|
|
104
|
-
complete release check. It also generates npm provenance when the source
|
|
105
|
-
repository is public; npm does not support provenance from private GitHub
|
|
106
|
-
repositories.
|
|
423
|
+
`useSnackbar()` returns `{ show, dismiss, dismissAll }`. `show(opts)` returns the toast id;
|
|
424
|
+
pass a stable `id` in `opts` to replace an existing toast in place instead of stacking.
|
|
107
425
|
|
|
108
|
-
###
|
|
426
|
+
### DataTable
|
|
109
427
|
|
|
110
|
-
|
|
111
|
-
|
|
428
|
+
```tsx
|
|
429
|
+
import { DataTable } from "@bzync/rui/datatable"
|
|
112
430
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
431
|
+
type Row = { id: string; name: string; role: string; seats: number }
|
|
432
|
+
|
|
433
|
+
<DataTable<Row>
|
|
434
|
+
data={members}
|
|
435
|
+
columns={[
|
|
436
|
+
{ key: "name", header: "Name", cell: (r) => r.name, sortable: true, searchable: true },
|
|
437
|
+
{ key: "role", header: "Role", cell: (r) => r.role, sortable: true },
|
|
438
|
+
{ key: "seats", header: "Seats", cell: (r) => r.seats, align: "right", sortable: true },
|
|
439
|
+
]}
|
|
440
|
+
searchable
|
|
441
|
+
pageSizeOptions={[10, 25, 50]}
|
|
442
|
+
onRowClick={(row) => open(row.id)}
|
|
443
|
+
/>
|
|
118
444
|
```
|
|
119
445
|
|
|
120
|
-
|
|
446
|
+
## Charts
|
|
447
|
+
|
|
448
|
+
The chart set is a separate subpath entry so it stays out of the main graph unless used.
|
|
449
|
+
Every chart renders plain SVG and takes data arrays — no canvas, no chart engine.
|
|
450
|
+
|
|
451
|
+
```tsx
|
|
452
|
+
import { BarChart, LineChart, DonutChart } from "@bzync/rui/charts"
|
|
453
|
+
|
|
454
|
+
<BarChart data={[{ label: "Jan", value: 42 }, { label: "Feb", value: 55 }]} />
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Available: `BarChart`, `LineChart`, `MultiLineChart`, `DonutChart`, `ScatterChart`,
|
|
458
|
+
`GanttChart`, `HeatmapChart`, `RadarChart`, `FunnelChart`, `WaterfallChart`. See
|
|
459
|
+
[`src/components/charts`](./src/components/charts) for exact per-chart props.
|
|
460
|
+
|
|
461
|
+
## Hooks and utilities
|
|
462
|
+
|
|
463
|
+
Lifecycle-correct hooks (mount/update/unmount with cleanup), re-exported from the root:
|
|
464
|
+
|
|
465
|
+
| Hook | Purpose |
|
|
466
|
+
|---|---|
|
|
467
|
+
| `useIsMounted()` | Guard async setState after unmount |
|
|
468
|
+
| `useIsomorphicLayoutEffect()` | `useLayoutEffect` on the client, `useEffect` on the server |
|
|
469
|
+
| `usePrevious(value)` | Previous render's value |
|
|
470
|
+
| `useUpdateEffect(fn, deps)` | Effect that skips the first render |
|
|
471
|
+
| `useEventCallback(fn)` | Stable callback identity with fresh closure |
|
|
472
|
+
| `useControllableState(opts)` | Controlled/uncontrolled state pattern |
|
|
473
|
+
| `useMediaQuery(query)` | Subscribe to a media query |
|
|
474
|
+
| `useAbortSignal()` | Abort in-flight work on unmount |
|
|
475
|
+
| `useFocusTrap(ref, active)` | Trap focus within a container |
|
|
476
|
+
| `useOutsideClick(ref, handler)` | Detect clicks outside an element |
|
|
477
|
+
|
|
478
|
+
Utilities: `cn()`, focus helpers, `Portal`, and assertion helpers from `@bzync/rui/utils`;
|
|
479
|
+
`ErrorBoundary` from the root; `createSafeEffect` and mount helpers from `@bzync/rui`'s
|
|
480
|
+
lifecycle exports; `KEY`, `DURATIONS`, and `FOCUSABLE_SELECTOR` constants.
|
|
481
|
+
|
|
482
|
+
## TypeScript
|
|
483
|
+
|
|
484
|
+
`@bzync/rui` is written in TypeScript and ships `.d.ts` files for the root and every
|
|
485
|
+
subpath entry. Prop types, variant unions (`ButtonVariant`, `ButtonSize`, …), and the
|
|
486
|
+
theming types (`Theme`, `ThemePalette`, `ThemeColors`, `ColorShade`, …) are all exported.
|
|
487
|
+
No `@types/*` package is required.
|
|
488
|
+
|
|
489
|
+
## Browser and React support
|
|
490
|
+
|
|
491
|
+
- **React** `^18.2.0 || ^19.0.0`
|
|
492
|
+
- **Modern evergreen browsers.** The library relies on CSS custom properties, `matchMedia`,
|
|
493
|
+
and standard DOM APIs; no polyfills are bundled.
|
|
494
|
+
- **SSR / RSC** via the `"use client"` boundary described above.
|
|
495
|
+
|
|
496
|
+
## Bundle and dependencies
|
|
497
|
+
|
|
498
|
+
- `dependencies`: **none**. `clsx`/`tailwind-merge` logic is inlined; icons are inlined SVG.
|
|
499
|
+
- Published output is **not minified** — readable ESM and CJS ship to the registry so the
|
|
500
|
+
code is auditable; your bundler minifies the final app build.
|
|
501
|
+
- `sideEffects` is limited to `**/*.css`, so unused components are dropped by any
|
|
502
|
+
tree-shaking bundler.
|
|
503
|
+
|
|
504
|
+
## Versioning
|
|
505
|
+
|
|
506
|
+
`@bzync/rui` follows [semantic versioning](https://semver.org/). While the major version
|
|
507
|
+
is `0`, minor releases may contain breaking changes; pin a version or a tight range and
|
|
508
|
+
review the release notes before upgrading. Releases are published from CI with npm
|
|
509
|
+
[provenance](https://docs.npmjs.com/generating-provenance-statements) attestations.
|
|
510
|
+
|
|
511
|
+
## Documentation
|
|
512
|
+
|
|
513
|
+
Full component documentation and live, themeable demos are published at
|
|
514
|
+
**[bzync.github.io/rui](https://bzync.github.io/rui/)**. A push to `main` deploys the
|
|
515
|
+
latest docs via GitHub Pages.
|
|
516
|
+
|
|
517
|
+
## Contributing
|
|
518
|
+
|
|
519
|
+
Bug reports, accessibility fixes, documentation improvements, and focused component
|
|
520
|
+
contributions are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for the development
|
|
521
|
+
setup, the verification gate, and the release process, and
|
|
522
|
+
[CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
|
523
|
+
|
|
524
|
+
## Security
|
|
525
|
+
|
|
526
|
+
Report vulnerabilities privately using the process in [SECURITY.md](./SECURITY.md).
|
|
527
|
+
Please do not open public issues for security reports.
|
|
528
|
+
|
|
529
|
+
## License
|
|
530
|
+
|
|
531
|
+
[ISC](./LICENSE) © 2026 Bzync
|
|
532
|
+
|
|
533
|
+
## Maintainer
|
|
534
|
+
|
|
535
|
+
`@bzync/rui` is maintained by [Rayan Reynaldo](https://www.bzync.com), Founder of Bzync
|
|
536
|
+
([www.bzync.com](https://www.bzync.com)).
|
|
537
|
+
|
|
538
|
+
If `@bzync/rui` is useful to you, you can support its continued development on
|
|
539
|
+
[Buy Me a Coffee](https://buymeacoffee.com/adminjw).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require("./cn-B6R3HKzx.cjs"),t=require("./component-styles-DY4wigiq.cjs"),n=require("./utils-MnnCTelh.cjs"),r=require("./use-event-callback-Du9_9eAi.cjs");let i=require("framer-motion"),a=require("react"),o=require("react/jsx-runtime"),s=require("react-dom");var c=({size:e=12})=>(0,o.jsx)(`svg`,{width:e,height:e,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:(0,o.jsx)(`path`,{d:`M18 6 6 18M6 6l12 12`})}),l=()=>(0,o.jsxs)(`svg`,{className:`animate-spin shrink-0 w-3.5 h-3.5 text-slate-500`,viewBox:`0 0 24 24`,fill:`none`,children:[(0,o.jsx)(`circle`,{className:`opacity-20`,cx:`12`,cy:`12`,r:`10`,stroke:`currentColor`,strokeWidth:`3`}),(0,o.jsx)(`path`,{className:`opacity-70`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z`})]});function u({text:e,query:t}){if(!t)return(0,o.jsx)(o.Fragment,{children:e});let n=e.toLowerCase().indexOf(t.toLowerCase());return n===-1?(0,o.jsx)(o.Fragment,{children:e}):(0,o.jsxs)(o.Fragment,{children:[e.slice(0,n),(0,o.jsx)(`mark`,{className:`bg-accent-500/25 text-accent-700 dark:text-accent-200 rounded-sm not-italic`,children:e.slice(n,n+t.length)}),e.slice(n+t.length)]})}function d({inputRef:n,inputId:r,listId:i,open:a,activeIdx:s,inputText:u,prefix:d,placeholder:f,disabled:p,error:m,loading:h,showClear:g,onInputChange:_,onFocus:v,onKeyDown:y,onClear:b,messageId:x,inputProps:S,className:C,inputClassName:w}){return(0,o.jsxs)(`div`,{className:e.t(`flex h-9 items-center gap-2 rounded-[var(--radius-md)] px-3`,t.t,m&&`border-destructive/60 hover:border-destructive/70 focus-within:border-destructive focus-within:ring-destructive/25`,p&&`opacity-50 cursor-not-allowed`,C),children:[d&&(0,o.jsx)(`span`,{className:`text-slate-500 shrink-0 text-sm`,children:d}),(0,o.jsx)(`input`,{...S,ref:n,id:r,role:`combobox`,"aria-autocomplete":`list`,"aria-expanded":a,"aria-controls":i,"aria-activedescendant":s>=0?`${i}-opt-${s}`:void 0,"aria-invalid":m?!0:void 0,"aria-describedby":x,autoComplete:`off`,value:u,onChange:_,onFocus:v,onKeyDown:y,placeholder:f,disabled:p,className:e.t(`flex-1 min-w-0 bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none disabled:cursor-not-allowed`,w)}),h&&(0,o.jsx)(l,{}),g&&!h&&(0,o.jsx)(`button`,{type:`button`,onClick:b,className:`shrink-0 text-slate-500 hover:text-slate-600 dark:hover:text-slate-300 cursor-pointer transition-colors`,children:(0,o.jsx)(c,{})})]})}function f({inputRef:n,inputId:r,listId:i,open:a,activeIdx:s,inputText:u,prefix:d,placeholder:f,disabled:p,error:m,loading:h,showClear:g,selected:_,onContainerClick:v,onInputChange:y,onFocus:b,onKeyDown:x,onRemove:S,onClearAll:C,messageId:w,inputProps:T,className:ee,inputClassName:E}){return(0,o.jsxs)(`div`,{onClick:v,className:e.t(`flex min-h-9 w-full flex-wrap items-center gap-1.5 rounded-[var(--radius-md)] px-2.5 py-1.5 cursor-text`,t.t,m&&`border-destructive/60 hover:border-destructive/70 focus-within:border-destructive focus-within:ring-destructive/25`,p&&`opacity-50 cursor-not-allowed`,ee),children:[d&&(0,o.jsx)(`span`,{className:`text-slate-500 shrink-0 text-sm`,children:d}),_.map(e=>(0,o.jsxs)(`span`,{className:`inline-flex max-w-[200px] items-center gap-1 rounded-md border border-border bg-surface-muted px-2 py-0.5 text-xs font-medium text-foreground`,children:[e.icon&&(0,o.jsx)(`span`,{className:`shrink-0 text-slate-500 dark:text-slate-400`,children:e.icon}),(0,o.jsx)(`span`,{className:`truncate`,children:e.label}),(0,o.jsx)(`button`,{type:`button`,tabIndex:-1,onMouseDown:e=>e.preventDefault(),onClick:t=>{t.stopPropagation(),S(e)},disabled:p,className:`shrink-0 text-slate-500 dark:text-slate-400 hover:text-foreground transition-colors cursor-pointer ml-0.5`,"aria-label":`Remove ${e.label}`,children:(0,o.jsx)(c,{size:10})})]},String(e.value))),(0,o.jsx)(`input`,{...T,ref:n,id:r,role:`combobox`,"aria-autocomplete":`list`,"aria-expanded":a,"aria-controls":i,"aria-activedescendant":s>=0?`${i}-opt-${s}`:void 0,"aria-invalid":m?!0:void 0,"aria-describedby":w,autoComplete:`off`,value:u,onChange:y,onFocus:b,onKeyDown:x,placeholder:_.length===0?f:void 0,disabled:p,className:e.t(`flex-1 min-w-[80px] bg-transparent text-sm text-foreground placeholder:text-muted-foreground outline-none disabled:cursor-not-allowed py-0.5`,E)}),(0,o.jsxs)(`span`,{className:`ml-auto flex items-center gap-1.5 shrink-0`,children:[h&&(0,o.jsx)(l,{}),g&&!h&&(0,o.jsx)(`button`,{type:`button`,onMouseDown:e=>e.preventDefault(),onClick:e=>{e.stopPropagation(),C()},className:`text-slate-500 hover:text-slate-600 dark:hover:text-slate-300 transition-colors`,"aria-label":`Clear all`,children:(0,o.jsx)(c,{})}),_.length>0&&(0,o.jsx)(`span`,{className:`text-[10px] font-medium text-slate-500 tabular-nums`,children:_.length})]})]})}function p({listRef:t,listId:n,open:r,pos:a,multiple:c,loading:d,filtered:f,emptyMessage:p,activeIdx:m,setActiveIdx:h,inputText:g,selectedValues:_,singleValue:v,onSelectSingle:y,onToggleMulti:b,className:x,optionClassName:S,loadingClassName:C,emptyClassName:w}){return typeof document>`u`?null:(0,s.createPortal)((0,o.jsx)(i.AnimatePresence,{children:r&&a&&(0,o.jsx)(i.motion.ul,{ref:t,id:n,role:`listbox`,"aria-multiselectable":c,initial:{opacity:0,y:a.placement===`bottom`?-4:4},animate:{opacity:1,y:0},exit:{opacity:0,y:a.placement===`bottom`?-4:4},transition:{duration:.13},style:{position:`fixed`,top:a.top,left:a.left,width:a.width,maxHeight:a.maxHeight},className:e.t(`z-[110] overflow-y-auto rounded-[var(--radius-lg)] border border-border bg-surface-raised py-1 shadow-floating`,x),children:d?(0,o.jsxs)(`li`,{className:e.t(`px-4 py-3 text-sm text-slate-600 flex items-center gap-2`,C),children:[(0,o.jsx)(l,{}),`Loading…`]}):f.length===0?(0,o.jsx)(`li`,{className:e.t(`px-4 py-3 text-sm text-slate-600`,w),children:p}):f.map((t,r)=>{let i=r===m,a=c?_.includes(t.value):t.value===v;return(0,o.jsxs)(`li`,{id:`${n}-opt-${r}`,role:`option`,"aria-selected":a,onMouseDown:e=>e.preventDefault(),onClick:()=>c?b(t):y(t),onMouseEnter:()=>h(r),className:e.t(`flex min-h-9 items-center gap-3 px-3 py-2 cursor-pointer transition-colors`,i&&`bg-surface-muted`,a&&!i&&`bg-accent-50 dark:bg-accent-500/10`,S),children:[t.icon&&(0,o.jsx)(`span`,{className:`shrink-0 text-slate-500 dark:text-slate-400`,children:t.icon}),(0,o.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,o.jsx)(`p`,{className:e.t(`text-sm`,a?`text-accent-700 dark:text-accent-300 font-medium`:`text-foreground`),children:(0,o.jsx)(u,{text:t.label,query:g})}),t.description&&(0,o.jsx)(`p`,{className:`text-xs text-slate-500 mt-0.5 truncate`,children:t.description})]}),a&&(0,o.jsx)(`svg`,{"aria-hidden":`true`,width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2.5`,strokeLinecap:`round`,strokeLinejoin:`round`,className:`text-accent-600 shrink-0`,children:(0,o.jsx)(`path`,{d:`m20 6-11 11-5-5`})})]},String(t.value))})})}),document.body)}function m(i,s){let{options:c,inputValue:l,onInputChange:u,label:m,hint:h,error:g,prefix:_,clearable:v=!0,loading:y=!1,emptyMessage:b=`No results`,maxVisible:x=8,className:S,wrapperClassName:C,triggerClassName:w,inputClassName:T,labelClassName:ee,messageClassName:E,listClassName:te,optionClassName:ne,loadingClassName:re,emptyClassName:ie,placeholder:D,disabled:O,id:ae,multiple:k,unstyled:oe=!1,...se}=i,{value:ce,onSelect:le,onDeselect:ue,...A}=se,de=(0,a.useId)(),j=ae??de,M=`${j}-list`,N=`${j}-message`,[fe,pe]=(0,a.useState)(``),P=l===void 0?fe:l,me=r.t(e=>u?.(e)),F=(0,a.useCallback)(e=>{l===void 0?pe(e):me(e)},[l,me]),[he,I]=(0,a.useState)([]),L=(0,a.useMemo)(()=>{if(!k)return[];let e=i;return e.value===void 0?he:c.filter(t=>e.value.includes(t.value))},[k,c,i,he]),R=(0,a.useMemo)(()=>L.map(e=>e.value),[L]),[z,B]=(0,a.useState)(!1),[V,H]=(0,a.useState)(-1),U=(0,a.useRef)(null),W=(0,a.useRef)(null),G=(0,a.useRef)(null),[K,q]=(0,a.useState)(``),J=(0,a.useMemo)(()=>{let e=K.trim().toLowerCase();return c.filter(t=>!t.disabled&&t.label.toLowerCase().includes(e)).slice(0,x)},[c,K,x]),Y=(0,a.useCallback)(e=>{i.onSelect?.(e),F(e.label),B(!1),H(-1)},[i,F]),ge=(0,a.useCallback)(e=>{let t=i,n=[...L,e];t.value===void 0&&I(n),t.onSelect?.(e,n),F(``),H(-1),W.current?.focus()},[L,i,F]),X=(0,a.useCallback)(e=>{let t=i,n=L.filter(t=>t.value!==e.value);t.value===void 0&&I(n),t.onDeselect?.(e,n)},[L,i]),Z=(0,a.useCallback)(e=>{R.includes(e.value)?X(e):ge(e)},[R,X,ge]),_e=(0,a.useCallback)(()=>{F(``),i.onSelect?.({value:``,label:``}),B(!1)},[i,F]),ve=(0,a.useCallback)(()=>{i.value===void 0&&I([]),F(``)},[i,F]),ye=(0,a.useCallback)(e=>{if(k&&e.key===`Backspace`&&P===``&&L.length>0){X(L[L.length-1]);return}if(!z){(e.key===`ArrowDown`||e.key===`ArrowUp`)&&(B(!0),q(``));return}if(e.key===`ArrowDown`)e.preventDefault(),H(e=>Math.min(e+1,J.length-1));else if(e.key===`ArrowUp`)e.preventDefault(),H(e=>Math.max(e-1,0));else if(e.key===`Enter`){e.preventDefault();let t=J[V];if(!t)return;k?Z(t):Y(t)}else e.key===`Escape`&&(B(!1),H(-1))},[k,P,L,z,J,V,Z,Y,X]);(0,a.useEffect)(()=>{V<0||!G.current||G.current.children[V]?.scrollIntoView({block:`nearest`})},[V]),(0,a.useEffect)(()=>{function e(e){let t=e.target;!U.current?.contains(t)&&!G.current?.contains(t)&&B(!1)}return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[]);let be=k?void 0:i.value,xe=!!(!k&&v&&P.length>0&&!O),Se=!!(k&&v&&(L.length>0||P.length>0)&&!O),Ce=n.o(z,U),Q=(0,a.useCallback)(e=>{F(e.target.value),q(e.target.value),B(!0),H(-1)},[F]),$=(0,a.useCallback)(()=>{B(!0),q(``)},[]);return oe?(0,o.jsxs)(`div`,{ref:U,className:e.t(S,C),children:[(0,o.jsx)(`input`,{ref:s,id:j,value:P,onChange:Q,placeholder:D,disabled:O}),z&&(0,o.jsx)(`ul`,{ref:G,id:M,className:te,children:J.map(e=>(0,o.jsx)(`li`,{onClick:()=>k?Z(e):Y(e),children:e.label},String(e.value)))})]}):(0,o.jsxs)(`div`,{ref:U,className:e.t(`relative`,t.o,C,S),children:[m&&(0,o.jsxs)(`label`,{htmlFor:j,className:e.t(`text-xs font-semibold leading-5 text-foreground`,ee),children:[m,se.required&&(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(`span`,{"aria-hidden":`true`,className:`ml-1 text-destructive`,children:`*`}),(0,o.jsx)(`span`,{className:`sr-only`,children:` (required)`})]})]}),!k&&(0,o.jsx)(d,{inputRef:s,inputId:j,listId:M,open:z,activeIdx:V,inputText:P,prefix:_,placeholder:D,disabled:O,error:g,loading:y,showClear:xe,onInputChange:Q,onFocus:$,onKeyDown:ye,onClear:_e,messageId:g||h?N:void 0,inputProps:A,className:w,inputClassName:T}),k&&(0,o.jsx)(f,{inputRef:e=>{W.current=e,typeof s==`function`?s(e):s&&(s.current=e)},inputId:j,listId:M,open:z,activeIdx:V,inputText:P,prefix:_,placeholder:D,disabled:O,error:g,loading:y,showClear:Se,selected:L,onContainerClick:()=>{O||(W.current?.focus(),B(!0),q(``))},onInputChange:Q,onFocus:$,onKeyDown:ye,onRemove:X,onClearAll:ve,messageId:g||h?N:void 0,inputProps:A,className:w,inputClassName:T}),(0,o.jsx)(p,{listRef:G,listId:M,open:z,pos:Ce,multiple:k,loading:y,filtered:J,emptyMessage:b,activeIdx:V,setActiveIdx:H,inputText:K,selectedValues:R,singleValue:be,onSelectSingle:Y,onToggleMulti:Z,className:te,optionClassName:ne,loadingClassName:re,emptyClassName:ie}),g&&(0,o.jsx)(`p`,{id:N,"aria-live":`polite`,className:e.t(`text-xs leading-5 text-destructive`,E),children:g}),h&&!g&&(0,o.jsx)(`p`,{id:N,className:e.t(`text-xs leading-5 text-muted-foreground`,E),children:h})]})}var h=(0,a.forwardRef)(m);Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return h}});
|