@jacshuo/onyx 2.0.0 → 2.2.0
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 +358 -361
- package/dist/Chart/BarChart.cjs +1 -1
- package/dist/Chart/BarChart.js +1 -1
- package/dist/Chart/LineChart.cjs +1 -1
- package/dist/Chart/LineChart.js +1 -1
- package/dist/Chart/PieChart.cjs +1 -1
- package/dist/Chart/PieChart.js +1 -1
- package/dist/Chart/ScatterChart.cjs +1 -1
- package/dist/Chart/ScatterChart.js +1 -1
- package/dist/Chart/index.cjs +1 -1
- package/dist/Chart/index.js +1 -1
- package/dist/DataDisplay/CodeBlock.cjs +2 -2
- package/dist/DataDisplay/CodeBlock.js +2 -2
- package/dist/DataDisplay/List.cjs +1 -1
- package/dist/DataDisplay/List.js +1 -1
- package/dist/DataDisplay/MetricCard.cjs +1 -0
- package/dist/DataDisplay/MetricCard.d.cts +3 -0
- package/dist/DataDisplay/MetricCard.d.ts +3 -0
- package/dist/DataDisplay/MetricCard.js +1 -0
- package/dist/DataDisplay/Stat.cjs +1 -0
- package/dist/DataDisplay/Stat.d.cts +3 -0
- package/dist/DataDisplay/Stat.d.ts +3 -0
- package/dist/DataDisplay/Stat.js +1 -0
- package/dist/DataDisplay/Table.cjs +1 -1
- package/dist/DataDisplay/Table.js +1 -1
- package/dist/DataDisplay/Tree.cjs +1 -1
- package/dist/DataDisplay/Tree.js +1 -1
- package/dist/DataDisplay/index.cjs +2 -2
- package/dist/DataDisplay/index.d.cts +6 -0
- package/dist/DataDisplay/index.d.ts +6 -0
- package/dist/DataDisplay/index.js +2 -2
- package/dist/Extras/CommandPalette.cjs +1 -0
- package/dist/Extras/CommandPalette.css +1 -0
- package/dist/Extras/CommandPalette.d.cts +4 -0
- package/dist/Extras/CommandPalette.d.ts +4 -0
- package/dist/Extras/CommandPalette.js +1 -0
- package/dist/Extras/DateTimePicker.cjs +1 -0
- package/dist/Extras/DateTimePicker.css +1 -0
- package/dist/Extras/DateTimePicker.d.cts +5 -0
- package/dist/Extras/DateTimePicker.d.ts +5 -0
- package/dist/Extras/DateTimePicker.js +1 -0
- package/dist/Extras/FileExplorer.cjs +2 -2
- package/dist/Extras/FileExplorer.js +2 -2
- package/dist/Extras/Timeline.cjs +1 -0
- package/dist/Extras/Timeline.css +1 -0
- package/dist/Extras/Timeline.d.cts +6 -0
- package/dist/Extras/Timeline.d.ts +6 -0
- package/dist/Extras/Timeline.js +1 -0
- package/dist/Extras/index.cjs +4 -4
- package/dist/Extras/index.css +1 -1
- package/dist/Extras/index.d.cts +14 -0
- package/dist/Extras/index.d.ts +14 -0
- package/dist/Extras/index.js +4 -4
- package/dist/Feedback/Alert.cjs +1 -1
- package/dist/Feedback/Alert.js +1 -1
- package/dist/Feedback/ProgressBar.cjs +1 -1
- package/dist/Feedback/ProgressBar.js +1 -1
- package/dist/Feedback/Skeleton.cjs +1 -0
- package/dist/Feedback/Skeleton.d.cts +2 -0
- package/dist/Feedback/Skeleton.d.ts +2 -0
- package/dist/Feedback/Skeleton.js +1 -0
- package/dist/Feedback/Spin.cjs +2 -2
- package/dist/Feedback/Spin.js +2 -2
- package/dist/Feedback/Toast.cjs +1 -0
- package/dist/Feedback/Toast.d.cts +11 -0
- package/dist/Feedback/Toast.d.ts +11 -0
- package/dist/Feedback/Toast.js +1 -0
- package/dist/Feedback/index.cjs +2 -2
- package/dist/Feedback/index.d.cts +11 -0
- package/dist/Feedback/index.d.ts +11 -0
- package/dist/Feedback/index.js +2 -2
- package/dist/Forms/Select.cjs +1 -0
- package/dist/Forms/Select.css +1 -0
- package/dist/Forms/Select.d.cts +3 -0
- package/dist/Forms/Select.d.ts +3 -0
- package/dist/Forms/Select.js +1 -0
- package/dist/Navigation/Breadcrumb.cjs +1 -0
- package/dist/Navigation/Breadcrumb.d.cts +3 -0
- package/dist/Navigation/Breadcrumb.d.ts +3 -0
- package/dist/Navigation/Breadcrumb.js +1 -0
- package/dist/Navigation/Header.cjs +1 -1
- package/dist/Navigation/Header.js +1 -1
- package/dist/Navigation/Pagination.cjs +1 -0
- package/dist/Navigation/Pagination.d.cts +2 -0
- package/dist/Navigation/Pagination.d.ts +2 -0
- package/dist/Navigation/Pagination.js +1 -0
- package/dist/Navigation/RibbonBar.cjs +1 -0
- package/dist/Navigation/RibbonBar.d.cts +6 -0
- package/dist/Navigation/RibbonBar.d.ts +6 -0
- package/dist/Navigation/RibbonBar.js +1 -0
- package/dist/Navigation/SideNav.cjs +1 -1
- package/dist/Navigation/SideNav.js +1 -1
- package/dist/Navigation/index.cjs +2 -2
- package/dist/Navigation/index.d.cts +8 -0
- package/dist/Navigation/index.d.ts +8 -0
- package/dist/Navigation/index.js +2 -2
- package/dist/Overlay/ContextMenu.cjs +1 -0
- package/dist/Overlay/ContextMenu.d.cts +3 -0
- package/dist/Overlay/ContextMenu.d.ts +3 -0
- package/dist/Overlay/ContextMenu.js +1 -0
- package/dist/Overlay/Drawer.cjs +1 -0
- package/dist/Overlay/Drawer.css +1 -0
- package/dist/Overlay/Drawer.d.cts +10 -0
- package/dist/Overlay/Drawer.d.ts +10 -0
- package/dist/Overlay/Drawer.js +1 -0
- package/dist/Overlay/index.cjs +1 -1
- package/dist/Overlay/index.css +1 -0
- package/dist/Overlay/index.d.cts +8 -0
- package/dist/Overlay/index.d.ts +8 -0
- package/dist/Overlay/index.js +1 -1
- package/dist/Primitives/Avatar.cjs +1 -0
- package/dist/Primitives/Avatar.d.cts +4 -0
- package/dist/Primitives/Avatar.d.ts +4 -0
- package/dist/Primitives/Avatar.js +1 -0
- package/dist/Primitives/Badge.cjs +1 -1
- package/dist/Primitives/Badge.js +1 -1
- package/dist/Primitives/Button.cjs +1 -1
- package/dist/Primitives/Button.js +1 -1
- package/dist/Primitives/Checkbox.cjs +1 -1
- package/dist/Primitives/Checkbox.js +1 -1
- package/dist/Primitives/Dropdown.cjs +1 -1
- package/dist/Primitives/Dropdown.js +1 -1
- package/dist/Primitives/DropdownButton.cjs +1 -1
- package/dist/Primitives/DropdownButton.js +1 -1
- package/dist/Primitives/Indicator.cjs +1 -1
- package/dist/Primitives/Indicator.js +1 -1
- package/dist/Primitives/Input.cjs +1 -1
- package/dist/Primitives/Input.js +1 -1
- package/dist/Primitives/Label.cjs +1 -1
- package/dist/Primitives/Label.js +1 -1
- package/dist/Primitives/Radio.cjs +1 -1
- package/dist/Primitives/Radio.js +1 -1
- package/dist/Primitives/Slider.cjs +1 -0
- package/dist/Primitives/Slider.css +1 -0
- package/dist/Primitives/Slider.d.cts +4 -0
- package/dist/Primitives/Slider.d.ts +4 -0
- package/dist/Primitives/Slider.js +1 -0
- package/dist/Primitives/Switch.cjs +1 -1
- package/dist/Primitives/Switch.js +1 -1
- package/dist/Primitives/Tag.cjs +1 -0
- package/dist/Primitives/Tag.d.cts +2 -0
- package/dist/Primitives/Tag.d.ts +2 -0
- package/dist/Primitives/Tag.js +1 -0
- package/dist/Primitives/index.cjs +1 -1
- package/dist/Primitives/index.css +1 -0
- package/dist/Primitives/index.d.cts +6 -0
- package/dist/Primitives/index.d.ts +6 -0
- package/dist/Primitives/index.js +1 -1
- package/dist/_tsup-dts-rollup.d.cts +1130 -3
- package/dist/_tsup-dts-rollup.d.ts +1130 -3
- package/dist/index.cjs +5 -5
- package/dist/index.css +1 -1
- package/dist/index.d.cts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +5 -5
- package/dist/styles/DataDisplay/MetricCard.css +1 -0
- package/dist/styles/DataDisplay/Stat.css +1 -0
- package/dist/styles/Extras/CommandPalette.css +65 -0
- package/dist/styles/Extras/DateTimePicker.css +566 -0
- package/dist/styles/Extras/Timeline.css +52 -0
- package/dist/styles/Feedback/Skeleton.css +37 -0
- package/dist/styles/Feedback/Toast.css +77 -0
- package/dist/styles/Forms/Select.css +36 -0
- package/dist/styles/Navigation/RibbonBar.css +1 -0
- package/dist/styles/Overlay/ContextMenu.css +1 -0
- package/dist/styles/Overlay/Drawer.css +52 -0
- package/dist/styles/Primitives/Slider.css +4 -0
- package/dist/styles/Primitives/Tag.css +1 -0
- package/dist/styles/base.css +1059 -20
- package/dist/styles.css +1726 -18
- package/dist/theme.cjs +1 -1
- package/dist/theme.d.cts +7 -0
- package/dist/theme.d.ts +7 -0
- package/dist/theme.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
<img src="https://img.shields.io/npm/l/@jacshuo/onyx?style=flat-square" alt="license" />
|
|
4
4
|
<img src="https://img.shields.io/github/actions/workflow/status/jacshuo/OnyxUI/ci.yml?branch=main&style=flat-square&label=CI" alt="CI" />
|
|
5
5
|
<img src="https://img.shields.io/npm/dm/@jacshuo/onyx?color=10b981&style=flat-square" alt="downloads" />
|
|
6
|
+
<img src="https://img.shields.io/badge/React-18%2B-61dafb?style=flat-square&logo=react" alt="React 18+" />
|
|
7
|
+
<img src="https://img.shields.io/badge/Tailwind_CSS-v4-38bdf8?style=flat-square&logo=tailwindcss" alt="Tailwind CSS v4" />
|
|
8
|
+
<img src="https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square&logo=typescript" alt="TypeScript" />
|
|
6
9
|
</p>
|
|
7
10
|
|
|
8
11
|
<p align="right">
|
|
@@ -11,9 +14,9 @@
|
|
|
11
14
|
|
|
12
15
|
# @jacshuo/onyx
|
|
13
16
|
|
|
14
|
-
A **React UI component library** built
|
|
17
|
+
A **cross-platform React UI component library** built on Tailwind CSS v4 — designed for responsive web apps, content-rich dashboards, and Electron desktop applications. Ships 55+ production-ready components as individually tree-shakeable ESM + CJS bundles with full TypeScript declarations.
|
|
15
18
|
|
|
16
|
-
Born from a passion for **polished cross-platform experiences**, Onyx delivers a consistent look and feel from mobile screens to 4K displays — with dark mode, keyboard navigation, and touch interactions built in from day one.
|
|
19
|
+
Born from a passion for **polished cross-platform experiences**, Onyx delivers a consistent look and feel from mobile screens to 4K displays — with dark mode, keyboard navigation, accessible form wiring, and touch interactions built in from day one.
|
|
17
20
|
|
|
18
21
|
> **Live Demo →** [jacshuo.github.io/OnyxUI](https://jacshuo.github.io/OnyxUI)
|
|
19
22
|
|
|
@@ -21,29 +24,39 @@ Born from a passion for **polished cross-platform experiences**, Onyx delivers a
|
|
|
21
24
|
|
|
22
25
|
## Why Onyx?
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
27
|
+
### Responsive without compromise
|
|
28
|
+
Every component adapts from a phone screen to a 4K display — without a single extra media query from you. Headers fold to hamburger menus, sidebars become draggable mobile drawers, dialogs become bottom sheets, and table layouts reflow gracefully. Size variants (`sm` / `md` / `lg`) and CSS custom property tokens make density adjustments trivial.
|
|
29
|
+
|
|
30
|
+
### Desktop & Electron — first-class
|
|
31
|
+
Onyx treats Electron and desktop apps as primary targets. Components are optimized for keyboard navigation, pointer interactions, content-dense layouts, and drag interactions — territory where most mobile-first libraries underdeliver. `CinePlayer`, `MiniPlayer`, `FileExplorer`, `CommandPalette`, `RibbonBar`, and `FilmReel` are purpose-built for desktop-class applications.
|
|
32
|
+
|
|
33
|
+
### Accessible by construction
|
|
34
|
+
`FormItem` automatically injects `id`, `aria-describedby`, and `aria-invalid` into child controls — no manual wiring. Every interactive component follows WCAG label-for semantics. Screen readers, keyboard users, and autofill all work correctly out of the box.
|
|
35
|
+
|
|
36
|
+
### Zero-config theming
|
|
37
|
+
No build tool plugins. No `tailwind.config.js`. Override any design decision via `@theme {}` tokens or CSS custom properties — including at media query breakpoints. Dark mode is class-based (`.dark` on any ancestor) and works everywhere.
|
|
38
|
+
|
|
39
|
+
### Minimal footprint, maximum control
|
|
40
|
+
No runtime CSS-in-JS. No styled-components. Just Tailwind CSS v4 utility classes and CSS custom properties. Import only what you use — per-component subpath exports ensure unused components never reach your bundle.
|
|
29
41
|
|
|
30
42
|
---
|
|
31
43
|
|
|
32
|
-
## Features
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
## Features at a Glance
|
|
45
|
+
|
|
46
|
+
| | |
|
|
47
|
+
|---|---|
|
|
48
|
+
| 🧩 **55+ components** | Primitives → Charts → CinePlayer → DataTable, covering the full UI spectrum |
|
|
49
|
+
| 📱 **Responsive by default** | Built-in breakpoint layouts, adaptive component modes, touch-friendly tap targets |
|
|
50
|
+
| ♿ **Accessible by default** | WCAG label-for, ARIA roles, keyboard navigation, and sr-only wiring throughout |
|
|
51
|
+
| 🌗 **Dark / Light mode** | Class-based dark mode, works on any subtree |
|
|
52
|
+
| 📊 **Charts included** | BarChart, LineChart, PieChart, ScatterChart — no extra charting library needed |
|
|
53
|
+
| ⚡ **Tailwind CSS v4** | Zero config — `@theme` tokens, pure CSS design system |
|
|
54
|
+
| 📦 **Tree-shakeable** | Per-component ESM subpath exports — import only what you use |
|
|
55
|
+
| 🎨 **Modular CSS** | Full bundle, base-only, or per-component CSS — pick what you need |
|
|
56
|
+
| 🖥️ **Electron ready** | Keyboard shortcuts, drag interactions, content-dense layouts |
|
|
57
|
+
| 🔤 **Full TypeScript** | Every prop, variant, and event is strictly typed |
|
|
58
|
+
| 🧩 **Composable API** | Compound component patterns for full layout control |
|
|
59
|
+
| ✅ **519 tests** | 36 test files — Vitest + jsdom + Testing Library |
|
|
47
60
|
|
|
48
61
|
---
|
|
49
62
|
|
|
@@ -57,13 +70,7 @@ pnpm add @jacshuo/onyx
|
|
|
57
70
|
yarn add @jacshuo/onyx
|
|
58
71
|
```
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
npm install react react-dom
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
> Requires **React ≥ 18.0.0**.
|
|
73
|
+
Requires **React ≥ 18.0.0** and **react-dom ≥ 18.0.0**.
|
|
67
74
|
|
|
68
75
|
---
|
|
69
76
|
|
|
@@ -72,7 +79,6 @@ npm install react react-dom
|
|
|
72
79
|
**1. Import the stylesheet** (once, at your app entry point):
|
|
73
80
|
|
|
74
81
|
```tsx
|
|
75
|
-
// main.tsx or App.tsx
|
|
76
82
|
import '@jacshuo/onyx/styles.css';
|
|
77
83
|
```
|
|
78
84
|
|
|
@@ -99,140 +105,211 @@ function App() {
|
|
|
99
105
|
|
|
100
106
|
## Import Strategies
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
Choose the style that fits your bundler and performance requirements.
|
|
103
109
|
|
|
104
|
-
###
|
|
110
|
+
### Flat import (simplest)
|
|
105
111
|
|
|
106
|
-
|
|
112
|
+
Modern bundlers (Vite, Next.js, webpack 5) tree-shake unused components automatically.
|
|
107
113
|
|
|
108
114
|
```tsx
|
|
109
|
-
import { Button, Dialog, Tabs } from '@jacshuo/onyx';
|
|
115
|
+
import { Button, Dialog, Tabs, Form, FormItem, Input } from '@jacshuo/onyx';
|
|
110
116
|
import '@jacshuo/onyx/styles.css';
|
|
111
117
|
```
|
|
112
118
|
|
|
113
119
|
### Per-component import (maximum tree-shaking)
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Components are organized under category subpaths (`Primitives`, `Overlay`, `Disclosure`, `DataDisplay`, `Navigation`, `Layout`, `Feedback`, `Forms`, `Extras`):
|
|
121
|
+
Guarantees only the code you use is included, even with bundlers that don't tree-shake well.
|
|
118
122
|
|
|
119
123
|
```tsx
|
|
120
|
-
// Individual component
|
|
121
124
|
import { Button } from '@jacshuo/onyx/Primitives/Button';
|
|
122
125
|
import { Dialog, DialogContent } from '@jacshuo/onyx/Overlay/Dialog';
|
|
123
|
-
import {
|
|
126
|
+
import { LineChart } from '@jacshuo/onyx/Chart/LineChart';
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Category namespace
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { Primitives, Overlay, Chart } from '@jacshuo/onyx';
|
|
124
133
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
import { Alert } from '@jacshuo/onyx/Feedback';
|
|
134
|
+
<Primitives.Button intent="primary">Save</Primitives.Button>
|
|
135
|
+
<Chart.BarChart data={data} />
|
|
128
136
|
```
|
|
129
137
|
|
|
130
138
|
### CSS options
|
|
131
139
|
|
|
132
|
-
| Import |
|
|
133
|
-
|
|
134
|
-
| `@jacshuo/onyx/styles.css` |
|
|
135
|
-
| `@jacshuo/onyx/styles/base.css` |
|
|
136
|
-
| `@jacshuo/onyx/styles/tailwind.css` |
|
|
137
|
-
| `@jacshuo/onyx/styles/tokens.css` |
|
|
138
|
-
| `@jacshuo/onyx/styles/CinePlayer.css` |
|
|
139
|
-
| `@jacshuo/onyx/styles/MiniPlayer.css` |
|
|
140
|
-
| `@jacshuo/onyx/styles/FileExplorer.css` |
|
|
141
|
-
| `@jacshuo/onyx/styles/FilmReel.css` |
|
|
142
|
-
|
|
143
|
-
#### Using with your own Tailwind CSS v4 setup
|
|
140
|
+
| Import | Description |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `@jacshuo/onyx/styles.css` | Full pre-compiled bundle — all utilities + all component CSS. Simplest setup. |
|
|
143
|
+
| `@jacshuo/onyx/styles/base.css` | Tailwind utilities + core design tokens. No component-specific keyframes. |
|
|
144
|
+
| `@jacshuo/onyx/styles/tailwind.css` | **For projects with their own Tailwind CSS v4.** Includes `@source` + tokens + dark variant. |
|
|
145
|
+
| `@jacshuo/onyx/styles/tokens.css` | Raw `@theme` tokens only. |
|
|
146
|
+
| `@jacshuo/onyx/styles/CinePlayer.css` | CinePlayer keyframes & `--cp-*` tokens |
|
|
147
|
+
| `@jacshuo/onyx/styles/MiniPlayer.css` | MiniPlayer keyframes & `--mp-*` tokens |
|
|
148
|
+
| `@jacshuo/onyx/styles/FileExplorer.css` | FileExplorer `--fe-*` tokens |
|
|
149
|
+
| `@jacshuo/onyx/styles/FilmReel.css` | FilmReel keyframes |
|
|
144
150
|
|
|
145
|
-
|
|
151
|
+
#### Using alongside your own Tailwind CSS v4
|
|
146
152
|
|
|
147
153
|
```css
|
|
148
154
|
/* your app's CSS entry */
|
|
149
155
|
@import "tailwindcss";
|
|
150
156
|
@import "@jacshuo/onyx/styles/tailwind.css";
|
|
151
157
|
|
|
152
|
-
/*
|
|
158
|
+
/* add per-component CSS as needed */
|
|
153
159
|
@import "@jacshuo/onyx/styles/CinePlayer.css";
|
|
154
160
|
```
|
|
155
161
|
|
|
156
|
-
>
|
|
157
|
-
>
|
|
158
|
-
> **Do NOT** use `tokens.css` alone — it only provides design tokens without the `@source` directive, so component styles will be incomplete.
|
|
159
|
-
|
|
160
|
-
**Example — minimal setup with CinePlayer only:**
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
import '@jacshuo/onyx/styles/base.css';
|
|
164
|
-
import '@jacshuo/onyx/styles/CinePlayer.css';
|
|
165
|
-
import { CinePlayer } from '@jacshuo/onyx/Extras/CinePlayer';
|
|
166
|
-
```
|
|
162
|
+
> Use `tailwind.css` — not `tokens.css` — so that Tailwind's scanner picks up class names from Onyx's compiled JS via the included `@source` directive.
|
|
167
163
|
|
|
168
164
|
---
|
|
169
165
|
|
|
170
|
-
##
|
|
166
|
+
## Component Library
|
|
171
167
|
|
|
172
168
|
### Primitives
|
|
173
169
|
|
|
174
170
|
| Component | Description |
|
|
175
171
|
|---|---|
|
|
176
|
-
| **Button** |
|
|
177
|
-
| **Input** |
|
|
172
|
+
| **Button** | 6 intents (primary, secondary, danger, warning, ghost, outline) × 3 sizes |
|
|
173
|
+
| **Input** | Text input with prefix, suffix, action button, and size variants |
|
|
174
|
+
| **TextBox** | Textarea with live word/character count and CJK-aware counting |
|
|
175
|
+
| **Dropdown** | Single & multi-select with search, grouped options, clearable, and keyboard nav |
|
|
176
|
+
| **Checkbox** | Tri-state (checked, unchecked, indeterminate) with label |
|
|
177
|
+
| **Radio / RadioGroup** | Grouped radio buttons with intent and size variants |
|
|
178
|
+
| **Switch** | Toggle switch with checked/unchecked slot content |
|
|
179
|
+
| **Slider / SliderRange** | Single-value and range sliders with keyboard control |
|
|
180
|
+
| **Badge** | Inline status badge with dot, outline, and pill variants |
|
|
181
|
+
| **Tag / Chip** | Dismissible tag for selections and filters |
|
|
178
182
|
| **Label** | Form label with size variants |
|
|
179
|
-
| **
|
|
180
|
-
| **
|
|
181
|
-
| **DropdownButton** | Button with a dropdown menu |
|
|
183
|
+
| **Avatar** | User avatar with image, initials fallback, and status indicator |
|
|
184
|
+
| **Indicator** | Numeric badge overlay for icons and avatars |
|
|
182
185
|
|
|
183
186
|
### Layout
|
|
184
187
|
|
|
185
188
|
| Component | Description |
|
|
186
189
|
|---|---|
|
|
187
|
-
| **Card** | Card
|
|
188
|
-
| **
|
|
189
|
-
| **
|
|
190
|
-
| **Panel** | Collapsible panel with header |
|
|
190
|
+
| **Card** | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` |
|
|
191
|
+
| **ImageCard** | Image-first card with overlay actions and hover states |
|
|
192
|
+
| **Panel** | Collapsible content panel with header |
|
|
191
193
|
|
|
192
194
|
### Data Display
|
|
193
195
|
|
|
194
196
|
| Component | Description |
|
|
195
197
|
|---|---|
|
|
196
|
-
| **Table** | Basic table primitives
|
|
197
|
-
| **
|
|
198
|
-
| **
|
|
199
|
-
| **
|
|
200
|
-
| **
|
|
201
|
-
| **
|
|
202
|
-
| **
|
|
198
|
+
| **Table / DataTable** | Basic table primitives + full-featured sortable/selectable/paginated data table |
|
|
199
|
+
| **List / ListItem** | Styled list with leading icon/avatar, title, description, and trailing action |
|
|
200
|
+
| **Tree / TreeItem** | Expandable tree view with keyboard navigation |
|
|
201
|
+
| **Chat** | Chat message thread with sent/received bubbles and timestamps |
|
|
202
|
+
| **CodeBlock** | Shiki-powered syntax highlighting — 20+ languages, line numbers, live-editable mode |
|
|
203
|
+
| **MetricCard** | KPI card with trend indicator and sparkline slot |
|
|
204
|
+
| **Stat** | Compact statistic display with label, value, and change indicator |
|
|
203
205
|
|
|
204
206
|
### Navigation
|
|
205
207
|
|
|
206
208
|
| Component | Description |
|
|
207
209
|
|---|---|
|
|
208
|
-
| **
|
|
209
|
-
| **
|
|
210
|
-
| **NavLink** | Semantic
|
|
211
|
-
| **
|
|
210
|
+
| **Header** | App header with responsive hamburger collapse, nav items, and action buttons |
|
|
211
|
+
| **SideNav** | Collapsible sidebar — expanded / icon-only / rail modes; mobile drawer with draggable pull-tab and `mobileDrawerSlot` |
|
|
212
|
+
| **NavLink** | Semantic link with auto external-link detection, intent/size/underline variants |
|
|
213
|
+
| **Breadcrumb** | Breadcrumb trail with truncation |
|
|
214
|
+
| **Pagination** | Page number navigation with first/last/prev/next |
|
|
215
|
+
| **RibbonBar** | Office-style ribbon toolbar with grouped commands |
|
|
212
216
|
|
|
213
217
|
### Disclosure
|
|
214
218
|
|
|
215
219
|
| Component | Description |
|
|
216
220
|
|---|---|
|
|
217
|
-
| **Accordion** | Expandable
|
|
218
|
-
| **Tabs** | TabList
|
|
221
|
+
| **Accordion** | Expandable sections with animated open/close |
|
|
222
|
+
| **Tabs** | `TabList`, `TabTrigger`, `TabPanels`, `TabContent` with sliding indicator |
|
|
219
223
|
|
|
220
224
|
### Overlay
|
|
221
225
|
|
|
222
226
|
| Component | Description |
|
|
223
227
|
|---|---|
|
|
224
|
-
| **Dialog** | Modal dialog
|
|
225
|
-
| **
|
|
226
|
-
| **
|
|
228
|
+
| **Dialog** | Modal dialog — centered on desktop, bottom sheet on mobile; stacking support |
|
|
229
|
+
| **Drawer** | Side drawer with responsive swipe-to-dismiss |
|
|
230
|
+
| **Tooltip** | Hover tooltip with configurable placement and delay |
|
|
231
|
+
| **ContextMenu** | Right-click context menu with submenus |
|
|
232
|
+
|
|
233
|
+
### Feedback
|
|
234
|
+
|
|
235
|
+
| Component | Description |
|
|
236
|
+
|---|---|
|
|
237
|
+
| **Alert / useAlert()** | Toast-style alerts with `useAlert()` hook — success, error, warning, info |
|
|
238
|
+
| **ProgressBar** | Determinate and indeterminate progress |
|
|
239
|
+
| **Skeleton** | Loading placeholder with pulse animation |
|
|
240
|
+
| **Spin** | Spinner with intent and size variants |
|
|
241
|
+
| **Toast** | Standalone toast notification |
|
|
242
|
+
|
|
243
|
+
### Forms
|
|
244
|
+
|
|
245
|
+
| Component | Description |
|
|
246
|
+
|---|---|
|
|
247
|
+
| **Form / FormItem / FormSection** | Stacked & inline layouts, card/inset appearance, auto `id`/`aria-describedby` injection, bulk validation via `onValues` |
|
|
248
|
+
| **Select** | Native-style accessible select with custom styling |
|
|
249
|
+
|
|
250
|
+
### Charts
|
|
251
|
+
|
|
252
|
+
| Component | Description |
|
|
253
|
+
|---|---|
|
|
254
|
+
| **BarChart** | Vertical/horizontal bar chart with tooltip and legend |
|
|
255
|
+
| **LineChart** | Multi-series line chart with area fill option |
|
|
256
|
+
| **PieChart** | Pie/donut chart with animated segments |
|
|
257
|
+
| **ScatterChart** | Scatter plot with configurable point size and color |
|
|
227
258
|
|
|
228
259
|
### Extras
|
|
229
260
|
|
|
230
261
|
| Component | Description |
|
|
231
262
|
|---|---|
|
|
232
|
-
| **
|
|
233
|
-
| **MiniPlayer** | Floating
|
|
234
|
-
| **
|
|
235
|
-
| **
|
|
263
|
+
| **CinePlayer** | Full-featured video player — cinema mode, playlist, keyboard shortcuts, accent color |
|
|
264
|
+
| **MiniPlayer** | Floating music player — dock, playlist, shuffle, loop, accent color |
|
|
265
|
+
| **FileExplorer** | File manager — drag-select, resize, dock, multi-select, `Delete` key with confirmation |
|
|
266
|
+
| **FilmReel** | Cinematic photo gallery with lightbox and keyboard navigation |
|
|
267
|
+
| **CommandPalette** | Spotlight-style command palette with fuzzy search |
|
|
268
|
+
| **DateTimePicker** | Date and time picker with calendar grid and time sliders |
|
|
269
|
+
| **Timeline** | Vertical timeline with icon, status, and time slots |
|
|
270
|
+
| **Masonry** | Responsive masonry grid layout |
|
|
271
|
+
| **TypewriterText** | Animated typewriter text with configurable speed and cursor |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Forms & Accessibility
|
|
276
|
+
|
|
277
|
+
`FormItem` is the connectivity layer between labels and controls. It injects `id`, `aria-describedby`, and `aria-invalid` into its first child automatically — every labelable control (`Input`, `TextBox`, `Dropdown`, `Switch`, `Checkbox`, `Slider`) receives correct WCAG label-for wiring without any manual props.
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { Form, FormItem, Input, Dropdown, Switch, Button } from '@jacshuo/onyx';
|
|
281
|
+
|
|
282
|
+
<Form layout="inline">
|
|
283
|
+
<FormItem label="Email" required>
|
|
284
|
+
<Input type="email" placeholder="you@example.com" />
|
|
285
|
+
</FormItem>
|
|
286
|
+
|
|
287
|
+
<FormItem label="Role">
|
|
288
|
+
<Dropdown options={roles} placeholder="Select a role…" />
|
|
289
|
+
</FormItem>
|
|
290
|
+
|
|
291
|
+
<FormItem label="Notifications">
|
|
292
|
+
{/* id injected directly onto Switch's native <input> */}
|
|
293
|
+
<Switch label="Receive email notifications" />
|
|
294
|
+
</FormItem>
|
|
295
|
+
</Form>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Validation
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
<FormItem
|
|
302
|
+
label="Username"
|
|
303
|
+
required
|
|
304
|
+
onValidate={(value) =>
|
|
305
|
+
value.length >= 3
|
|
306
|
+
? { result: true, reason: 'Username is available.' }
|
|
307
|
+
: { result: false, reason: 'Must be at least 3 characters.' }
|
|
308
|
+
}
|
|
309
|
+
>
|
|
310
|
+
<Input placeholder="jane_doe" />
|
|
311
|
+
</FormItem>
|
|
312
|
+
```
|
|
236
313
|
|
|
237
314
|
---
|
|
238
315
|
|
|
@@ -240,55 +317,50 @@ import { CinePlayer } from '@jacshuo/onyx/Extras/CinePlayer';
|
|
|
240
317
|
|
|
241
318
|
### Dark Mode
|
|
242
319
|
|
|
243
|
-
|
|
320
|
+
Class-based dark mode — add `.dark` to any ancestor:
|
|
244
321
|
|
|
245
322
|
```html
|
|
246
323
|
<html class="dark">
|
|
247
|
-
<!-- all
|
|
324
|
+
<!-- all Onyx components render in dark mode -->
|
|
248
325
|
</html>
|
|
249
326
|
```
|
|
250
327
|
|
|
251
|
-
###
|
|
252
|
-
|
|
253
|
-
Many components accept an `accent` prop (any CSS color string):
|
|
254
|
-
|
|
255
|
-
```tsx
|
|
256
|
-
<MiniPlayer accent="#3b82f6" playlist={tracks} />
|
|
257
|
-
<CinePlayer accent="#f43f5e" playlist={videos} />
|
|
258
|
-
<FileExplorer accent="#10b981" files={files} />
|
|
259
|
-
```
|
|
328
|
+
### Override Design Tokens
|
|
260
329
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
All component colors are defined as CSS custom properties in `:root` and `.dark`, making them fully overridable:
|
|
330
|
+
All colors, spacing, and sizing values are CSS custom properties overridable without ejecting:
|
|
264
331
|
|
|
265
332
|
```css
|
|
266
|
-
/*
|
|
267
|
-
:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
333
|
+
/* Widen label column on desktop */
|
|
334
|
+
@media (min-width: 768px) {
|
|
335
|
+
:root {
|
|
336
|
+
--form-label-w-md: 9rem;
|
|
337
|
+
--form-item-gap-md: 1rem;
|
|
338
|
+
}
|
|
271
339
|
}
|
|
272
340
|
|
|
273
|
-
/*
|
|
341
|
+
/* Retheme CinePlayer */
|
|
274
342
|
:root {
|
|
275
|
-
--
|
|
276
|
-
--
|
|
277
|
-
}
|
|
278
|
-
.dark {
|
|
279
|
-
--mp-bg: rgba(20, 18, 30, 0.95);
|
|
280
|
-
--mp-text: #ffffff;
|
|
343
|
+
--cp-bg: #0a0a0a;
|
|
344
|
+
--cp-surface-hover: rgba(255, 255, 255, 0.15);
|
|
281
345
|
}
|
|
282
346
|
|
|
283
|
-
/*
|
|
284
|
-
:root
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
347
|
+
/* Retheme MiniPlayer */
|
|
348
|
+
:root { --mp-bg: rgba(255, 255, 255, 0.92); }
|
|
349
|
+
.dark { --mp-bg: rgba(18, 15, 28, 0.96); }
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Accent Colors
|
|
353
|
+
|
|
354
|
+
Media-rich components accept a CSS color string for branding consistency:
|
|
355
|
+
|
|
356
|
+
```tsx
|
|
357
|
+
<CinePlayer accent="#f43f5e" playlist={videos} />
|
|
358
|
+
<MiniPlayer accent="#8b5cf6" playlist={tracks} />
|
|
359
|
+
<FileExplorer accent="#10b981" files={files} />
|
|
288
360
|
```
|
|
289
361
|
|
|
290
362
|
<details>
|
|
291
|
-
<summary><strong>Full token reference</strong></summary>
|
|
363
|
+
<summary><strong>Full component token reference</strong></summary>
|
|
292
364
|
|
|
293
365
|
#### CinePlayer (`--cp-*`)
|
|
294
366
|
| Token | Default | Description |
|
|
@@ -333,176 +405,104 @@ All component colors are defined as CSS custom properties in `:root` and `.dark`
|
|
|
333
405
|
|
|
334
406
|
## Responsive Design
|
|
335
407
|
|
|
336
|
-
Onyx
|
|
337
|
-
|
|
338
|
-
### Responsive Header
|
|
408
|
+
Onyx handles responsive behavior internally — you don't write breakpoint logic.
|
|
339
409
|
|
|
340
|
-
|
|
410
|
+
### Header — automatic hamburger collapse
|
|
341
411
|
|
|
342
412
|
```tsx
|
|
343
|
-
import { Header } from '@jacshuo/onyx';
|
|
344
|
-
|
|
345
|
-
// On ≥md screens: full nav bar + action buttons
|
|
346
|
-
// On <md screens: hamburger menu (nav) + overflow menu (actions) — automatic
|
|
347
413
|
<Header
|
|
348
414
|
brand="My App"
|
|
349
415
|
mobileMenu
|
|
350
416
|
navItems={[
|
|
351
417
|
{ label: 'Home', href: '/' },
|
|
352
418
|
{ label: 'Docs', href: '/docs' },
|
|
353
|
-
{ label: 'Changelog', href: '/changelog' },
|
|
354
|
-
]}
|
|
355
|
-
actions={[
|
|
356
|
-
{ icon: <UserIcon />, 'aria-label': 'Sign in', onClick: () => navigate('/login') },
|
|
357
419
|
]}
|
|
420
|
+
actions={[{ icon: <SearchIcon />, 'aria-label': 'Search', onClick: openSearch }]}
|
|
358
421
|
/>
|
|
422
|
+
// ≥md: full nav bar <md: hamburger menu — no extra props
|
|
359
423
|
```
|
|
360
424
|
|
|
361
|
-
###
|
|
362
|
-
|
|
363
|
-
`SideNav` ships with three collapse modes. Wire them to a responsive state to get a desktop-to-mobile transition with one state variable:
|
|
425
|
+
### SideNav — mobile drawer with draggable pull-tab
|
|
364
426
|
|
|
365
427
|
```tsx
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
<aside className="hidden md:block shrink-0">
|
|
376
|
-
<SideNav
|
|
377
|
-
items={navItems}
|
|
378
|
-
collapsible
|
|
379
|
-
collapseMode={mode}
|
|
380
|
-
onCollapseModeChange={setMode}
|
|
381
|
-
/>
|
|
382
|
-
</aside>
|
|
383
|
-
|
|
384
|
-
{/* Slide-over drawer on mobile */}
|
|
385
|
-
<aside className="md:hidden">
|
|
386
|
-
<SideNav items={navItems} />
|
|
387
|
-
</aside>
|
|
388
|
-
|
|
389
|
-
<main className="flex-1 overflow-y-auto p-4 md:p-8">
|
|
390
|
-
{/* page content */}
|
|
391
|
-
</main>
|
|
392
|
-
</div>
|
|
393
|
-
);
|
|
394
|
-
}
|
|
428
|
+
<SideNav
|
|
429
|
+
items={navItems}
|
|
430
|
+
collapsible
|
|
431
|
+
mobileDrawerSlot={
|
|
432
|
+
<Input prefix={<SearchIcon />} placeholder="Search…" />
|
|
433
|
+
}
|
|
434
|
+
/>
|
|
435
|
+
// Desktop: expanded / icon-only / rail collapse modes
|
|
436
|
+
// Mobile: slide-out drawer, draggable repositionable pull-tab
|
|
395
437
|
```
|
|
396
438
|
|
|
397
|
-
### Dialog —
|
|
398
|
-
|
|
399
|
-
`Dialog` automatically renders as a bottom sheet on small screens, keeping the dismiss-by-swipe pattern users expect on mobile:
|
|
439
|
+
### Dialog — bottom sheet on mobile
|
|
400
440
|
|
|
401
441
|
```tsx
|
|
402
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, Button } from '@jacshuo/onyx';
|
|
403
|
-
|
|
404
|
-
// On ≥md screens: centered modal dialog
|
|
405
|
-
// On <md screens: slides up from bottom as a full-width sheet — no extra props
|
|
406
442
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
407
443
|
<DialogContent size="sm">
|
|
408
|
-
<DialogHeader>
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
<p>Are you sure you want to proceed?</p>
|
|
412
|
-
<div className="flex justify-end gap-2">
|
|
444
|
+
<DialogHeader><DialogTitle>Confirm</DialogTitle></DialogHeader>
|
|
445
|
+
<p>Are you sure?</p>
|
|
446
|
+
<DialogFooter>
|
|
413
447
|
<Button intent="ghost" onClick={() => setOpen(false)}>Cancel</Button>
|
|
414
448
|
<Button intent="primary" onClick={() => setOpen(false)}>Confirm</Button>
|
|
415
|
-
</
|
|
449
|
+
</DialogFooter>
|
|
416
450
|
</DialogContent>
|
|
417
451
|
</Dialog>
|
|
452
|
+
// ≥md: centered modal <md: slides up from bottom
|
|
418
453
|
```
|
|
419
454
|
|
|
420
|
-
###
|
|
421
|
-
|
|
422
|
-
All components expose a `size` prop (`sm` / `md` / `lg`) for adapting density to the target context — tight mobile layouts or spacious desktop dashboards:
|
|
423
|
-
|
|
424
|
-
```tsx
|
|
425
|
-
import { DataTable, Button, Tabs, TabList, TabTrigger } from '@jacshuo/onyx';
|
|
426
|
-
|
|
427
|
-
// Compact for mobile
|
|
428
|
-
<DataTable columns={columns} data={rows} size="sm" />
|
|
429
|
-
|
|
430
|
-
// Comfortable for desktop
|
|
431
|
-
<DataTable columns={columns} data={rows} size="lg" />
|
|
432
|
-
|
|
433
|
-
// Mix sizes to match your layout density
|
|
434
|
-
<Tabs defaultValue="a">
|
|
435
|
-
<TabList size="sm"> {/* compact tabs */}
|
|
436
|
-
<TabTrigger value="a">Tab A</TabTrigger>
|
|
437
|
-
<TabTrigger value="b">Tab B</TabTrigger>
|
|
438
|
-
</TabList>
|
|
439
|
-
</Tabs>
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
### Token Overrides at Breakpoints
|
|
443
|
-
|
|
444
|
-
All sizing and spacing values are CSS custom properties. Override them at any breakpoint for precisely tuned responsive behavior:
|
|
455
|
+
### Token overrides at breakpoints
|
|
445
456
|
|
|
446
457
|
```css
|
|
447
|
-
/*
|
|
458
|
+
/* Mobile-first defaults */
|
|
448
459
|
:root {
|
|
449
460
|
--form-label-w-md: 5rem;
|
|
450
461
|
--form-item-gap-md: 0.5rem;
|
|
451
|
-
--form-row-gap-md: 0.75rem;
|
|
452
462
|
}
|
|
453
463
|
|
|
454
|
-
/* Wider
|
|
464
|
+
/* Wider labels and more breathing room on desktop */
|
|
455
465
|
@media (min-width: 768px) {
|
|
456
466
|
:root {
|
|
457
|
-
--form-label-w-md:
|
|
458
|
-
--form-item-gap-md:
|
|
459
|
-
--form-row-gap-md: 1.25rem;
|
|
467
|
+
--form-label-w-md: 9rem;
|
|
468
|
+
--form-item-gap-md: 1rem;
|
|
460
469
|
}
|
|
461
470
|
}
|
|
462
471
|
```
|
|
463
472
|
|
|
464
473
|
---
|
|
465
474
|
|
|
466
|
-
## Usage Examples
|
|
475
|
+
## Selected Usage Examples
|
|
467
476
|
|
|
468
|
-
###
|
|
477
|
+
### Charts
|
|
469
478
|
|
|
470
479
|
```tsx
|
|
471
|
-
import {
|
|
472
|
-
|
|
473
|
-
<Button intent="primary" size="lg">Save</Button>
|
|
474
|
-
<Button intent="danger">Delete</Button>
|
|
475
|
-
<Button intent="ghost">Cancel</Button>
|
|
476
|
-
<Button intent="outline">Settings</Button>
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
### Dialog
|
|
480
|
+
import { BarChart, LineChart, PieChart } from '@jacshuo/onyx';
|
|
480
481
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
482
|
+
<BarChart
|
|
483
|
+
data={[
|
|
484
|
+
{ label: 'Jan', value: 420 },
|
|
485
|
+
{ label: 'Feb', value: 380 },
|
|
486
|
+
{ label: 'Mar', value: 510 },
|
|
487
|
+
]}
|
|
488
|
+
/>
|
|
484
489
|
|
|
485
|
-
|
|
486
|
-
|
|
490
|
+
<LineChart
|
|
491
|
+
series={[
|
|
492
|
+
{ name: 'Revenue', data: [120, 180, 240, 310] },
|
|
493
|
+
{ name: 'Costs', data: [80, 100, 130, 160] },
|
|
494
|
+
]}
|
|
495
|
+
labels={['Q1', 'Q2', 'Q3', 'Q4']}
|
|
496
|
+
/>
|
|
487
497
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
<p>Are you sure you want to proceed?</p>
|
|
497
|
-
<DialogFooter>
|
|
498
|
-
<Button intent="ghost" onClick={() => setOpen(false)}>Cancel</Button>
|
|
499
|
-
<Button intent="primary" onClick={() => setOpen(false)}>Confirm</Button>
|
|
500
|
-
</DialogFooter>
|
|
501
|
-
</DialogContent>
|
|
502
|
-
</Dialog>
|
|
503
|
-
</>
|
|
504
|
-
);
|
|
505
|
-
}
|
|
498
|
+
<PieChart
|
|
499
|
+
segments={[
|
|
500
|
+
{ label: 'Product A', value: 45 },
|
|
501
|
+
{ label: 'Product B', value: 30 },
|
|
502
|
+
{ label: 'Other', value: 25 },
|
|
503
|
+
]}
|
|
504
|
+
donut
|
|
505
|
+
/>
|
|
506
506
|
```
|
|
507
507
|
|
|
508
508
|
### DataTable
|
|
@@ -510,15 +510,42 @@ function ConfirmDialog() {
|
|
|
510
510
|
```tsx
|
|
511
511
|
import { DataTable, type ColumnDef } from '@jacshuo/onyx';
|
|
512
512
|
|
|
513
|
-
type User = { id: number; name: string; email: string };
|
|
513
|
+
type User = { id: number; name: string; email: string; role: string };
|
|
514
514
|
|
|
515
515
|
const columns: ColumnDef<User>[] = [
|
|
516
|
-
{ key: 'id',
|
|
517
|
-
{ key: 'name',
|
|
516
|
+
{ key: 'id', header: 'ID', width: 60 },
|
|
517
|
+
{ key: 'name', header: 'Name', sortable: true },
|
|
518
518
|
{ key: 'email', header: 'Email', sortable: true },
|
|
519
|
+
{ key: 'role', header: 'Role' },
|
|
519
520
|
];
|
|
520
521
|
|
|
521
|
-
<DataTable
|
|
522
|
+
<DataTable
|
|
523
|
+
columns={columns}
|
|
524
|
+
data={users}
|
|
525
|
+
selectionMode="multi"
|
|
526
|
+
pageSize={10}
|
|
527
|
+
/>
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Alert (Toast)
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
import { useAlert, Button } from '@jacshuo/onyx';
|
|
534
|
+
|
|
535
|
+
function SaveButton() {
|
|
536
|
+
const alert = useAlert();
|
|
537
|
+
|
|
538
|
+
return (
|
|
539
|
+
<Button
|
|
540
|
+
intent="primary"
|
|
541
|
+
onClick={() =>
|
|
542
|
+
alert({ title: 'Saved!', description: 'Your changes have been saved.', variant: 'success' })
|
|
543
|
+
}
|
|
544
|
+
>
|
|
545
|
+
Save
|
|
546
|
+
</Button>
|
|
547
|
+
);
|
|
548
|
+
}
|
|
522
549
|
```
|
|
523
550
|
|
|
524
551
|
### Tabs
|
|
@@ -538,22 +565,6 @@ import { Tabs, TabList, TabTrigger, TabPanels, TabContent } from '@jacshuo/onyx'
|
|
|
538
565
|
</Tabs>
|
|
539
566
|
```
|
|
540
567
|
|
|
541
|
-
### Alert (Toast)
|
|
542
|
-
|
|
543
|
-
```tsx
|
|
544
|
-
import { useAlert, Button } from '@jacshuo/onyx';
|
|
545
|
-
|
|
546
|
-
function NotifyButton() {
|
|
547
|
-
const alert = useAlert();
|
|
548
|
-
|
|
549
|
-
return (
|
|
550
|
-
<Button onClick={() => alert({ title: 'Saved!', description: 'Your changes have been saved.', variant: 'success' })}>
|
|
551
|
-
Save
|
|
552
|
-
</Button>
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
```
|
|
556
|
-
|
|
557
568
|
### NavLink
|
|
558
569
|
|
|
559
570
|
```tsx
|
|
@@ -562,28 +573,20 @@ import { NavLink } from '@jacshuo/onyx';
|
|
|
562
573
|
{/* Internal link */}
|
|
563
574
|
<NavLink href="/about">About</NavLink>
|
|
564
575
|
|
|
565
|
-
{/* Auto-detected external — shows icon + sets target="_blank"
|
|
576
|
+
{/* Auto-detected external — shows icon + sets target="_blank" */}
|
|
566
577
|
<NavLink href="https://github.com">GitHub</NavLink>
|
|
567
578
|
|
|
568
|
-
{/* Suppress external icon */}
|
|
569
|
-
<NavLink href="https://example.com" external={false}>Example</NavLink>
|
|
570
|
-
|
|
571
579
|
{/* Variants */}
|
|
572
580
|
<NavLink href="/docs" intent="secondary" size="lg" underline="always">Docs</NavLink>
|
|
573
581
|
```
|
|
574
582
|
|
|
575
583
|
### CodeBlock
|
|
576
584
|
|
|
577
|
-
> **Note:** `CodeBlock` uses [Shiki](https://shiki.style/) for syntax highlighting. Shiki is included as a direct dependency and installed automatically with Onyx — no extra steps needed.
|
|
578
|
-
|
|
579
585
|
```tsx
|
|
580
586
|
import { CodeBlock } from '@jacshuo/onyx';
|
|
581
587
|
|
|
582
|
-
{/*
|
|
583
|
-
<CodeBlock code={`const x = 42;`} language="typescript" />
|
|
584
|
-
|
|
585
|
-
{/* With line numbers */}
|
|
586
|
-
<CodeBlock code={sourceCode} language="tsx" lineNumbers />
|
|
588
|
+
{/* Syntax highlighting (Shiki — included, no extra install) */}
|
|
589
|
+
<CodeBlock code={`const x: number = 42;`} language="typescript" lineNumbers />
|
|
587
590
|
|
|
588
591
|
{/* Live editable editor */}
|
|
589
592
|
function Editor() {
|
|
@@ -600,22 +603,35 @@ function Editor() {
|
|
|
600
603
|
}
|
|
601
604
|
```
|
|
602
605
|
|
|
606
|
+
### CommandPalette
|
|
607
|
+
|
|
608
|
+
```tsx
|
|
609
|
+
import { CommandPalette } from '@jacshuo/onyx';
|
|
610
|
+
|
|
611
|
+
<CommandPalette
|
|
612
|
+
open={open}
|
|
613
|
+
onOpenChange={setOpen}
|
|
614
|
+
commands={[
|
|
615
|
+
{ id: 'new-file', label: 'New File', icon: <FileIcon />, action: newFile },
|
|
616
|
+
{ id: 'open-prefs', label: 'Preferences', icon: <SettingsIcon />, action: openPrefs },
|
|
617
|
+
{ id: 'git-commit', label: 'Commit Changes', icon: <GitIcon />, action: commit },
|
|
618
|
+
]}
|
|
619
|
+
/>
|
|
620
|
+
```
|
|
621
|
+
|
|
603
622
|
### MiniPlayer
|
|
604
623
|
|
|
605
624
|
```tsx
|
|
606
625
|
import { MiniPlayer } from '@jacshuo/onyx';
|
|
607
626
|
|
|
608
|
-
const tracks = [
|
|
609
|
-
{ title: 'Midnight City', artist: 'M83', src: '/audio/midnight.mp3', cover: '/covers/m83.jpg' },
|
|
610
|
-
{ title: 'Intro', artist: 'The xx', src: '/audio/intro.mp3' },
|
|
611
|
-
];
|
|
612
|
-
|
|
613
627
|
<MiniPlayer
|
|
614
|
-
playlist={
|
|
628
|
+
playlist={[
|
|
629
|
+
{ title: 'Midnight City', artist: 'M83', src: '/audio/midnight.mp3', cover: '/covers/m83.jpg' },
|
|
630
|
+
{ title: 'Intro', artist: 'The xx', src: '/audio/intro.mp3' },
|
|
631
|
+
]}
|
|
615
632
|
position="bottom-right"
|
|
616
633
|
accent="#8b5cf6"
|
|
617
634
|
shuffle
|
|
618
|
-
autoPlay
|
|
619
635
|
/>
|
|
620
636
|
```
|
|
621
637
|
|
|
@@ -624,14 +640,10 @@ const tracks = [
|
|
|
624
640
|
```tsx
|
|
625
641
|
import { CinePlayer } from '@jacshuo/onyx';
|
|
626
642
|
|
|
627
|
-
const videos = [
|
|
628
|
-
{ title: 'Big Buck Bunny', src: 'https://example.com/bunny.mp4', subtitle: 'Open source' },
|
|
629
|
-
];
|
|
630
|
-
|
|
631
643
|
<CinePlayer
|
|
632
|
-
playlist={
|
|
644
|
+
playlist={[{ title: 'Product Demo', src: '/video/demo.mp4' }]}
|
|
633
645
|
accent="#f43f5e"
|
|
634
|
-
onPlayChange={(playing, index) =>
|
|
646
|
+
onPlayChange={(playing, index) => trackAnalytics(playing, index)}
|
|
635
647
|
/>
|
|
636
648
|
```
|
|
637
649
|
|
|
@@ -641,7 +653,7 @@ const videos = [
|
|
|
641
653
|
import { FileExplorer, type FileExplorerItem } from '@jacshuo/onyx';
|
|
642
654
|
|
|
643
655
|
const files: FileExplorerItem[] = [
|
|
644
|
-
{ name: 'src',
|
|
656
|
+
{ name: 'src', path: '/src', type: 'directory' },
|
|
645
657
|
{ name: 'index.ts', path: '/src/index.ts', type: 'file', size: 2048, extension: '.ts' },
|
|
646
658
|
];
|
|
647
659
|
|
|
@@ -649,8 +661,8 @@ const files: FileExplorerItem[] = [
|
|
|
649
661
|
files={files}
|
|
650
662
|
accent="#10b981"
|
|
651
663
|
dockable
|
|
652
|
-
onFileOpen={(f) =>
|
|
653
|
-
onDelete={(items) =>
|
|
664
|
+
onFileOpen={(f) => openEditor(f.path)}
|
|
665
|
+
onDelete={(items) => confirmDelete(items)}
|
|
654
666
|
/>
|
|
655
667
|
```
|
|
656
668
|
|
|
@@ -658,49 +670,41 @@ const files: FileExplorerItem[] = [
|
|
|
658
670
|
|
|
659
671
|
## Keyboard Shortcuts
|
|
660
672
|
|
|
661
|
-
### FileExplorer
|
|
662
|
-
| Key | Action |
|
|
663
|
-
|---|---|
|
|
664
|
-
| `Click` | Select file |
|
|
665
|
-
| `Ctrl+Click` | Multi-select |
|
|
666
|
-
| `Ctrl+A` | Select all |
|
|
667
|
-
| `Delete` | Delete selected (with confirmation dialog) |
|
|
668
|
-
| `Escape` | Clear selection |
|
|
669
|
-
| `Double-click` | Open file / Navigate directory |
|
|
670
|
-
|
|
671
673
|
### CinePlayer
|
|
672
674
|
| Key | Action |
|
|
673
675
|
|---|---|
|
|
674
676
|
| `Space` | Play / Pause |
|
|
675
|
-
|
|
|
676
|
-
|
|
|
677
|
+
| `← / →` | Seek ±5 s |
|
|
678
|
+
| `↑ / ↓` | Volume ±5% |
|
|
677
679
|
| `F` | Toggle fullscreen |
|
|
678
680
|
| `C` | Toggle cinema mode |
|
|
679
681
|
| `L` | Toggle playlist |
|
|
680
682
|
| `M` | Mute / Unmute |
|
|
681
|
-
| `N` | Next track |
|
|
682
|
-
| `P` | Previous track |
|
|
683
|
+
| `N / P` | Next / Previous track |
|
|
683
684
|
| `S` | Toggle shuffle |
|
|
684
685
|
|
|
686
|
+
### FileExplorer
|
|
687
|
+
| Key | Action |
|
|
688
|
+
|---|---|
|
|
689
|
+
| `Click` | Select |
|
|
690
|
+
| `Ctrl+Click` | Multi-select |
|
|
691
|
+
| `Ctrl+A` | Select all |
|
|
692
|
+
| `Delete` | Delete selected (with confirmation) |
|
|
693
|
+
| `Escape` | Clear selection |
|
|
694
|
+
| `Double-click` | Open file / navigate directory |
|
|
695
|
+
|
|
685
696
|
---
|
|
686
697
|
|
|
687
698
|
## Development
|
|
688
699
|
|
|
689
700
|
```bash
|
|
690
|
-
#
|
|
691
|
-
npm
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
npm run
|
|
695
|
-
|
|
696
|
-
#
|
|
697
|
-
npm run dist
|
|
698
|
-
|
|
699
|
-
# Build demo site (dist-demo/)
|
|
700
|
-
npm run build:demo
|
|
701
|
-
|
|
702
|
-
# Typecheck
|
|
703
|
-
npm run typecheck
|
|
701
|
+
npm install # install dependencies
|
|
702
|
+
npm run dev # demo dev server → http://localhost:3001
|
|
703
|
+
npm run build # library build → dist/
|
|
704
|
+
npm run build:demo # demo site build → dist-demo/
|
|
705
|
+
npm run test # run all 519 tests (Vitest)
|
|
706
|
+
npm run typecheck # tsc --noEmit
|
|
707
|
+
npm run lint # eslint src demo
|
|
704
708
|
```
|
|
705
709
|
|
|
706
710
|
---
|
|
@@ -708,33 +712,30 @@ npm run typecheck
|
|
|
708
712
|
## Project Structure
|
|
709
713
|
|
|
710
714
|
```
|
|
711
|
-
|
|
712
|
-
├──
|
|
713
|
-
│ ├──
|
|
714
|
-
│ ├──
|
|
715
|
-
│
|
|
716
|
-
│
|
|
717
|
-
│
|
|
718
|
-
│
|
|
719
|
-
│
|
|
720
|
-
│
|
|
721
|
-
│
|
|
722
|
-
│
|
|
723
|
-
|
|
724
|
-
│
|
|
725
|
-
├──
|
|
726
|
-
│ ├──
|
|
727
|
-
│ ├──
|
|
728
|
-
│ └──
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
├──
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
│ ├── styles.css # Full pre-compiled CSS bundle
|
|
736
|
-
│ └── styles/ # Modular CSS files
|
|
737
|
-
└── dist-demo/ # Demo build output
|
|
715
|
+
src/
|
|
716
|
+
├── components/ 55+ components across 10 categories
|
|
717
|
+
│ ├── Primitives/ Button, Input, Dropdown, Switch, Slider, Checkbox, Radio…
|
|
718
|
+
│ ├── Layout/ Card, ImageCard, Panel
|
|
719
|
+
│ ├── DataDisplay/ Table, DataTable, List, Tree, Chat, CodeBlock, MetricCard…
|
|
720
|
+
│ ├── Navigation/ Header, SideNav, NavLink, Breadcrumb, RibbonBar, Pagination
|
|
721
|
+
│ ├── Disclosure/ Accordion, Tabs
|
|
722
|
+
│ ├── Overlay/ Dialog, Drawer, Tooltip, ContextMenu
|
|
723
|
+
│ ├── Feedback/ Alert, ProgressBar, Skeleton, Spin, Toast
|
|
724
|
+
│ ├── Forms/ Form, FormItem, FormSection, Select
|
|
725
|
+
│ ├── Extras/ CinePlayer, MiniPlayer, FileExplorer, CommandPalette…
|
|
726
|
+
│ └── Chart/ BarChart, LineChart, PieChart, ScatterChart
|
|
727
|
+
├── styles/
|
|
728
|
+
│ ├── tokens/core.css @theme semantic color tokens
|
|
729
|
+
│ ├── tokens/animations.css Shared keyframes & animation utilities
|
|
730
|
+
│ ├── theme/ CVA variant functions (one file per category)
|
|
731
|
+
│ ├── index.css Full bundle entry
|
|
732
|
+
│ └── tailwind.css Consumer integration entry
|
|
733
|
+
└── __tests__/ 36 test files, 519 tests
|
|
734
|
+
demo/ Interactive demo site (GitHub Pages)
|
|
735
|
+
.github/
|
|
736
|
+
├── workflows/ci.yml PR checks: typecheck + build
|
|
737
|
+
└── workflows/release.yml Manual release: patch / minor / major
|
|
738
|
+
dist/ Published library (ESM + CJS + DTS + CSS)
|
|
738
739
|
```
|
|
739
740
|
|
|
740
741
|
---
|
|
@@ -742,16 +743,12 @@ OnyxUI/
|
|
|
742
743
|
## Contributing
|
|
743
744
|
|
|
744
745
|
1. Fork the repository
|
|
745
|
-
2. Create a feature branch
|
|
746
|
-
3. Commit your changes
|
|
747
|
-
4. Push
|
|
748
|
-
5. Open a Pull Request
|
|
749
|
-
|
|
750
|
-
### Community & Security
|
|
746
|
+
2. Create a feature branch: `git checkout -b feat/my-feature`
|
|
747
|
+
3. Commit your changes following [Conventional Commits](https://www.conventionalcommits.org/)
|
|
748
|
+
4. Push and open a Pull Request
|
|
751
749
|
|
|
752
750
|
- Contribution guide: [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
753
|
-
-
|
|
754
|
-
- Security policy: [SECURITY.md](./.github/SECURITY.md)
|
|
751
|
+
- Security policy: [SECURITY.md](./SECURITY.md)
|
|
755
752
|
|
|
756
753
|
---
|
|
757
754
|
|