@dashnex/ui 0.5.2
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 +522 -0
- package/dashnex.json +1 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.js +12 -0
- package/dist/components/Loading.d.ts +3 -0
- package/dist/components/Loading.js +4 -0
- package/dist/components/tailwind/alert.d.ts +28 -0
- package/dist/components/tailwind/alert.js +30 -0
- package/dist/components/tailwind/auth-layout.d.ts +4 -0
- package/dist/components/tailwind/auth-layout.js +4 -0
- package/dist/components/tailwind/avatar.d.ts +14 -0
- package/dist/components/tailwind/avatar.js +18 -0
- package/dist/components/tailwind/badge.d.ts +33 -0
- package/dist/components/tailwind/badge.js +33 -0
- package/dist/components/tailwind/button.d.ts +58 -0
- package/dist/components/tailwind/button.js +169 -0
- package/dist/components/tailwind/checkbox.d.ts +36 -0
- package/dist/components/tailwind/checkbox.js +83 -0
- package/dist/components/tailwind/combobox.d.ts +19 -0
- package/dist/components/tailwind/combobox.js +86 -0
- package/dist/components/tailwind/description-list.d.ts +3 -0
- package/dist/components/tailwind/description-list.js +11 -0
- package/dist/components/tailwind/dialog.d.ts +28 -0
- package/dist/components/tailwind/dialog.js +30 -0
- package/dist/components/tailwind/divider.d.ts +3 -0
- package/dist/components/tailwind/divider.js +5 -0
- package/dist/components/tailwind/dropdown.d.ts +32 -0
- package/dist/components/tailwind/dropdown.js +78 -0
- package/dist/components/tailwind/fieldset.d.ts +21 -0
- package/dist/components/tailwind/fieldset.js +24 -0
- package/dist/components/tailwind/heading.d.ts +6 -0
- package/dist/components/tailwind/heading.js +10 -0
- package/dist/components/tailwind/index.d.ts +27 -0
- package/dist/components/tailwind/index.js +28 -0
- package/dist/components/tailwind/input.d.ts +10 -0
- package/dist/components/tailwind/input.js +58 -0
- package/dist/components/tailwind/link.d.ts +11 -0
- package/dist/components/tailwind/link.js +13 -0
- package/dist/components/tailwind/listbox.d.ts +15 -0
- package/dist/components/tailwind/listbox.js +84 -0
- package/dist/components/tailwind/navbar.d.ts +14 -0
- package/dist/components/tailwind/navbar.js +42 -0
- package/dist/components/tailwind/pagination.d.ts +17 -0
- package/dist/components/tailwind/pagination.js +21 -0
- package/dist/components/tailwind/radio.d.ts +37 -0
- package/dist/components/tailwind/radio.js +86 -0
- package/dist/components/tailwind/select.d.ts +5 -0
- package/dist/components/tailwind/select.js +40 -0
- package/dist/components/tailwind/sidebar-layout.d.ts +5 -0
- package/dist/components/tailwind/sidebar-layout.js +18 -0
- package/dist/components/tailwind/sidebar.d.ts +18 -0
- package/dist/components/tailwind/sidebar.js +56 -0
- package/dist/components/tailwind/stacked-layout.d.ts +5 -0
- package/dist/components/tailwind/stacked-layout.js +18 -0
- package/dist/components/tailwind/switch.d.ts +36 -0
- package/dist/components/tailwind/switch.js +146 -0
- package/dist/components/tailwind/table.d.ts +16 -0
- package/dist/components/tailwind/table.js +40 -0
- package/dist/components/tailwind/text.d.ts +5 -0
- package/dist/components/tailwind/text.js +15 -0
- package/dist/components/tailwind/textarea.d.ts +6 -0
- package/dist/components/tailwind/textarea.js +36 -0
- package/dist/components/theme/index.d.ts +2 -0
- package/dist/components/theme/index.js +2 -0
- package/dist/components/theme/theme-provider.d.ts +14 -0
- package/dist/components/theme/theme-provider.js +83 -0
- package/dist/components/theme/theme-switcher.d.ts +1 -0
- package/dist/components/theme/theme-switcher.js +12 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/pages/index.js +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +10 -0
- package/dist/tailwind.d.ts +1 -0
- package/dist/tailwind.js +1 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# @dashnex/ui
|
|
2
|
+
|
|
3
|
+
React UI component library for the Dashnex framework. Provides accessible Tailwind-based components (Headless UI), theme (light/dark/system), Heroicons re-export, and layout primitives for admin and app UIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
<details>
|
|
8
|
+
<summary>pnpm</summary>
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add @dashnex/ui
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
</details>
|
|
15
|
+
|
|
16
|
+
<details>
|
|
17
|
+
<summary>npm</summary>
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @dashnex/ui
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
<details>
|
|
26
|
+
<summary>yarn</summary>
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add @dashnex/ui
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
</details>
|
|
33
|
+
|
|
34
|
+
**Usage in code**
|
|
35
|
+
|
|
36
|
+
- **Client (pages, client components):** `import { Button, ThemeProvider } from '@dashnex/ui/client'`
|
|
37
|
+
- **Server or tailwind-only:** `import { Button } from '@dashnex/ui'` or `import { Button } from '@dashnex/ui/tailwind'`
|
|
38
|
+
|
|
39
|
+
## Exported client functions, types, services
|
|
40
|
+
|
|
41
|
+
- **useTheme()** — hook; returns `{ theme, isDark, setTheme, toggleTheme, mounted }`. Use only inside `ThemeProvider`.
|
|
42
|
+
|
|
43
|
+
## Exported UI components
|
|
44
|
+
|
|
45
|
+
**Layout:** `AuthLayout`, `StackedLayout`, `SidebarLayout`, `Sidebar`, `SidebarHeader`, `SidebarBody`, `SidebarFooter`, `SidebarSection`, `SidebarDivider`, `SidebarSpacer`, `SidebarHeading`, `SidebarItem`, `SidebarLabel`, `Navbar`, `NavbarDivider`, `NavbarSection`, `NavbarSpacer`, `NavbarItem`, `NavbarLabel`, `StackedLayout`, `SidebarLayout`.
|
|
46
|
+
|
|
47
|
+
**Forms / controls:** `Input`, `InputGroup`, `Select`, `Textarea`, `Checkbox`, `CheckboxGroup`, `CheckboxField`, `Radio`, `RadioGroup`, `RadioField`, `Switch`, `SwitchGroup`, `SwitchField`, `Fieldset`, `Legend`, `FieldGroup`, `Field`, `Label`, `Description`, `ErrorMessage`, `Combobox`, `ComboboxOption`, `ComboboxLabel`, `ComboboxDescription`, `Listbox`, `ListboxOption`, `ListboxLabel`, `ListboxDescription`.
|
|
48
|
+
|
|
49
|
+
**Overlays / feedback:** `Dialog`, `DialogTitle`, `DialogDescription`, `DialogBody`, `DialogActions`, `Alert`, `AlertTitle`, `AlertDescription`, `AlertBody`, `AlertActions`, `Dropdown`, `DropdownButton`, `DropdownMenu`, `DropdownItem`, `DropdownHeader`, `DropdownSection`, `DropdownHeading`, `DropdownDivider`, `DropdownLabel`, `DropdownDescription`, `DropdownShortcut`.
|
|
50
|
+
|
|
51
|
+
**Display:** `Button`, `TouchTarget`, `Link`, `Badge`, `BadgeButton`, `Avatar`, `AvatarButton`, `Text`, `TextLink`, `Strong`, `Code`, `Heading`, `Subheading`, `Divider`, `DescriptionList`, `DescriptionTerm`, `DescriptionDetails`, `Table`, `TableHead`, `TableBody`, `TableRow`, `TableHeader`, `TableCell`, `Pagination`, `PaginationPrevious`, `PaginationNext`, `PaginationList`, `PaginationPage`, `PaginationGap`.
|
|
52
|
+
|
|
53
|
+
**Theme:** `ThemeProvider`, `ThemeSwitcher`.
|
|
54
|
+
|
|
55
|
+
**Other:** `Loading`.
|
|
56
|
+
|
|
57
|
+
<details>
|
|
58
|
+
<summary>Link</summary>
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { Link } from '@dashnex/ui/client'
|
|
62
|
+
|
|
63
|
+
<Link href="/page">Go to page</Link>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
</details>
|
|
67
|
+
|
|
68
|
+
<details>
|
|
69
|
+
<summary>Button — solid, outline, plain, link</summary>
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { Button } from '@dashnex/ui/client'
|
|
73
|
+
|
|
74
|
+
// Solid (default). Optional: color (e.g. 'blue', 'red', 'zinc')
|
|
75
|
+
<Button color="blue">Save</Button>
|
|
76
|
+
|
|
77
|
+
// Outline
|
|
78
|
+
<Button outline>Cancel</Button>
|
|
79
|
+
|
|
80
|
+
// Plain (minimal)
|
|
81
|
+
<Button plain>Skip</Button>
|
|
82
|
+
|
|
83
|
+
// As link (pass href)
|
|
84
|
+
<Button href="/dashboard">Dashboard</Button>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary>Input and InputGroup</summary>
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Input, InputGroup } from '@dashnex/ui/client'
|
|
94
|
+
|
|
95
|
+
<Input type="email" placeholder="Email" />
|
|
96
|
+
|
|
97
|
+
// With leading icon (use data-slot="icon" on the icon element)
|
|
98
|
+
<InputGroup>
|
|
99
|
+
<span data-slot="icon">…</span>
|
|
100
|
+
<Input type="search" placeholder="Search" />
|
|
101
|
+
</InputGroup>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
</details>
|
|
105
|
+
|
|
106
|
+
<details>
|
|
107
|
+
<summary>Dialog</summary>
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { Dialog, DialogTitle, DialogDescription, DialogBody, DialogActions } from '@dashnex/ui/client'
|
|
111
|
+
|
|
112
|
+
<Dialog open={open} onClose={() => setOpen(false)} size="lg">
|
|
113
|
+
<DialogTitle>Title</DialogTitle>
|
|
114
|
+
<DialogDescription>Description text.</DialogDescription>
|
|
115
|
+
<DialogBody>Content here.</DialogBody>
|
|
116
|
+
<DialogActions>
|
|
117
|
+
<Button plain onClick={() => setOpen(false)}>Cancel</Button>
|
|
118
|
+
<Button color="blue">Confirm</Button>
|
|
119
|
+
</DialogActions>
|
|
120
|
+
</Dialog>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`size` is optional: `'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl'` (default `'lg'`).
|
|
124
|
+
|
|
125
|
+
</details>
|
|
126
|
+
|
|
127
|
+
<details>
|
|
128
|
+
<summary>Alert (modal dialog style)</summary>
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { Alert, AlertTitle, AlertDescription, AlertBody, AlertActions } from '@dashnex/ui/client'
|
|
132
|
+
|
|
133
|
+
<Alert open={open} onClose={() => setOpen(false)} size="md">
|
|
134
|
+
<AlertTitle>Alert</AlertTitle>
|
|
135
|
+
<AlertDescription>Message.</AlertDescription>
|
|
136
|
+
<AlertBody>Extra content.</AlertBody>
|
|
137
|
+
<AlertActions>
|
|
138
|
+
<Button color="blue" onClick={() => setOpen(false)}>OK</Button>
|
|
139
|
+
</AlertActions>
|
|
140
|
+
</Alert>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`size` optional, same as Dialog.
|
|
144
|
+
|
|
145
|
+
</details>
|
|
146
|
+
|
|
147
|
+
<details>
|
|
148
|
+
<summary>Select</summary>
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { Select } from '@dashnex/ui/client'
|
|
152
|
+
|
|
153
|
+
<Select value={value} onChange={(e) => setValue(e.target.value)}>
|
|
154
|
+
<option value="a">Option A</option>
|
|
155
|
+
<option value="b">Option B</option>
|
|
156
|
+
</Select>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Optional: `multiple` for multi-select.
|
|
160
|
+
|
|
161
|
+
</details>
|
|
162
|
+
|
|
163
|
+
<details>
|
|
164
|
+
<summary>Table (optional striped, dense, grid, clickable row)</summary>
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from '@dashnex/ui/client'
|
|
168
|
+
|
|
169
|
+
<Table striped dense grid>
|
|
170
|
+
<TableHead>
|
|
171
|
+
<TableRow>
|
|
172
|
+
<TableHeader>Name</TableHeader>
|
|
173
|
+
<TableHeader>Status</TableHeader>
|
|
174
|
+
</TableRow>
|
|
175
|
+
</TableHead>
|
|
176
|
+
<TableBody>
|
|
177
|
+
<TableRow href="/item/1" title="View">
|
|
178
|
+
<TableCell>Item 1</TableCell>
|
|
179
|
+
<TableCell>Active</TableCell>
|
|
180
|
+
</TableRow>
|
|
181
|
+
</TableBody>
|
|
182
|
+
</Table>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Optional props on `Table`: `bleed`, `dense`, `grid`, `striped`. Optional on `TableRow`: `href`, `target`, `title` for clickable rows.
|
|
186
|
+
|
|
187
|
+
</details>
|
|
188
|
+
|
|
189
|
+
<details>
|
|
190
|
+
<summary>Badge and BadgeButton</summary>
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
import { Badge, BadgeButton } from '@dashnex/ui/client'
|
|
194
|
+
|
|
195
|
+
<Badge color="green">Active</Badge>
|
|
196
|
+
|
|
197
|
+
<BadgeButton color="zinc" onClick={handleClick}>Tag</BadgeButton>
|
|
198
|
+
<BadgeButton href="/tags/1" color="blue">Tag link</BadgeButton>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`color` optional (e.g. `'red'`, `'green'`, `'zinc'`), default `'zinc'`.
|
|
202
|
+
|
|
203
|
+
</details>
|
|
204
|
+
|
|
205
|
+
<details>
|
|
206
|
+
<summary>Avatar and AvatarButton</summary>
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { Avatar, AvatarButton } from '@dashnex/ui/client'
|
|
210
|
+
|
|
211
|
+
<Avatar src="/photo.jpg" alt="User" />
|
|
212
|
+
<Avatar initials="JD" alt="John Doe" />
|
|
213
|
+
<Avatar square initials="AB" />
|
|
214
|
+
|
|
215
|
+
<AvatarButton src="/photo.jpg" onClick={handleClick} />
|
|
216
|
+
<AvatarButton href="/profile" initials="JD" />
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Optional: `src`, `square`, `initials`, `alt`. As link use `href`.
|
|
220
|
+
|
|
221
|
+
</details>
|
|
222
|
+
|
|
223
|
+
<details>
|
|
224
|
+
<summary>Dropdown</summary>
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
import { Dropdown, DropdownButton, DropdownMenu, DropdownItem } from '@dashnex/ui/client'
|
|
228
|
+
|
|
229
|
+
<Dropdown>
|
|
230
|
+
<DropdownButton>Actions</DropdownButton>
|
|
231
|
+
<DropdownMenu anchor="bottom">
|
|
232
|
+
<DropdownItem onClick={handleEdit}>Edit</DropdownItem>
|
|
233
|
+
<DropdownItem href="/view">View</DropdownItem>
|
|
234
|
+
</DropdownMenu>
|
|
235
|
+
</Dropdown>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`DropdownMenu` optional `anchor`: `'bottom'` (default) or other Headless anchor values. `DropdownItem` can be button (onClick) or link (href).
|
|
239
|
+
|
|
240
|
+
</details>
|
|
241
|
+
|
|
242
|
+
<details>
|
|
243
|
+
<summary>Combobox (searchable select)</summary>
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
import { Combobox, ComboboxOption, ComboboxLabel } from '@dashnex/ui/client'
|
|
247
|
+
|
|
248
|
+
const options = [{ id: '1', name: 'Alpha' }, { id: '2', name: 'Beta' }]
|
|
249
|
+
|
|
250
|
+
<Combobox
|
|
251
|
+
value={selected}
|
|
252
|
+
onChange={setSelected}
|
|
253
|
+
options={options}
|
|
254
|
+
displayValue={(v) => v?.name}
|
|
255
|
+
filter={(opt, q) => opt.name.toLowerCase().includes(q.toLowerCase())}
|
|
256
|
+
placeholder="Search…"
|
|
257
|
+
aria-label="Choose option"
|
|
258
|
+
>
|
|
259
|
+
{(option) => (
|
|
260
|
+
<ComboboxOption key={option.id} value={option}>
|
|
261
|
+
<ComboboxLabel>{option.name}</ComboboxLabel>
|
|
262
|
+
</ComboboxOption>
|
|
263
|
+
)}
|
|
264
|
+
</Combobox>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`filter` is optional; default is case-insensitive match on `displayValue(option)`. `anchor` optional `'top' | 'bottom'`.
|
|
268
|
+
|
|
269
|
+
</details>
|
|
270
|
+
|
|
271
|
+
<details>
|
|
272
|
+
<summary>Listbox (single select)</summary>
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
import { Listbox, ListboxOption, ListboxLabel } from '@dashnex/ui/client'
|
|
276
|
+
|
|
277
|
+
const options = [{ id: '1', name: 'One' }, { id: '2', name: 'Two' }]
|
|
278
|
+
|
|
279
|
+
<Listbox value={selected} onChange={setSelected} placeholder="Select" aria-label="Choose">
|
|
280
|
+
<ListboxOption value={options[0]}>
|
|
281
|
+
<ListboxLabel>{options[0].name}</ListboxLabel>
|
|
282
|
+
</ListboxOption>
|
|
283
|
+
<ListboxOption value={options[1]}>
|
|
284
|
+
<ListboxLabel>{options[1].name}</ListboxLabel>
|
|
285
|
+
</ListboxOption>
|
|
286
|
+
</Listbox>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
</details>
|
|
290
|
+
|
|
291
|
+
<details>
|
|
292
|
+
<summary>Navbar and NavbarItem</summary>
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
import { Navbar, NavbarItem, NavbarSpacer } from '@dashnex/ui/client'
|
|
296
|
+
|
|
297
|
+
<Navbar>
|
|
298
|
+
<NavbarItem href="/" current>Home</NavbarItem>
|
|
299
|
+
<NavbarItem href="/settings">Settings</NavbarItem>
|
|
300
|
+
<NavbarSpacer />
|
|
301
|
+
<NavbarItem onClick={logout}>Log out</NavbarItem>
|
|
302
|
+
</Navbar>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`current` optional; shows current-indicator. Use `href` for link or `onClick` for button.
|
|
306
|
+
|
|
307
|
+
</details>
|
|
308
|
+
|
|
309
|
+
<details>
|
|
310
|
+
<summary>Pagination</summary>
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
import { Pagination, PaginationPrevious, PaginationNext, PaginationList, PaginationPage, PaginationGap } from '@dashnex/ui/client'
|
|
314
|
+
|
|
315
|
+
<Pagination aria-label="Page navigation">
|
|
316
|
+
<PaginationPrevious href={prevUrl} />
|
|
317
|
+
<PaginationList>
|
|
318
|
+
<PaginationPage href="/?p=1" current>1</PaginationPage>
|
|
319
|
+
<PaginationGap />
|
|
320
|
+
<PaginationPage href="/?p=5">5</PaginationPage>
|
|
321
|
+
</PaginationList>
|
|
322
|
+
<PaginationNext href={nextUrl} />
|
|
323
|
+
</Pagination>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Use `href={null}` for disabled Previous/Next.
|
|
327
|
+
|
|
328
|
+
</details>
|
|
329
|
+
|
|
330
|
+
<details>
|
|
331
|
+
<summary>Heading, Subheading, Text</summary>
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
import { Heading, Subheading, Text, TextLink, Strong, Code } from '@dashnex/ui/client'
|
|
335
|
+
|
|
336
|
+
<Heading level={1}>Page title</Heading>
|
|
337
|
+
<Subheading level={2}>Section</Subheading>
|
|
338
|
+
|
|
339
|
+
<Text>Body text.</Text>
|
|
340
|
+
<TextLink href="/more">Link</TextLink>
|
|
341
|
+
<Strong>Bold</Strong>
|
|
342
|
+
<Code>code</Code>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`level` optional: 1–6 (Heading default 1, Subheading default 2).
|
|
346
|
+
|
|
347
|
+
</details>
|
|
348
|
+
|
|
349
|
+
<details>
|
|
350
|
+
<summary>AuthLayout</summary>
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
import { AuthLayout } from '@dashnex/ui/client'
|
|
354
|
+
|
|
355
|
+
<AuthLayout>
|
|
356
|
+
<form>…</form>
|
|
357
|
+
</AuthLayout>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
</details>
|
|
361
|
+
|
|
362
|
+
<details>
|
|
363
|
+
<summary>StackedLayout and SidebarLayout</summary>
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
import { StackedLayout, SidebarLayout, Navbar, Sidebar } from '@dashnex/ui/client'
|
|
367
|
+
|
|
368
|
+
// Stacked: navbar on top, sidebar in drawer on mobile
|
|
369
|
+
<StackedLayout
|
|
370
|
+
navbar={<Navbar>…</Navbar>}
|
|
371
|
+
sidebar={<Sidebar>…</Sidebar>}
|
|
372
|
+
>
|
|
373
|
+
Page content
|
|
374
|
+
</StackedLayout>
|
|
375
|
+
|
|
376
|
+
// SidebarLayout: fixed sidebar on desktop, drawer on mobile
|
|
377
|
+
<SidebarLayout navbar={…} sidebar={…}>
|
|
378
|
+
Page content
|
|
379
|
+
</SidebarLayout>
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
</details>
|
|
383
|
+
|
|
384
|
+
<details>
|
|
385
|
+
<summary>Sidebar and SidebarItem</summary>
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
import { Sidebar, SidebarHeader, SidebarBody, SidebarFooter, SidebarSection, SidebarItem, SidebarLabel } from '@dashnex/ui/client'
|
|
389
|
+
|
|
390
|
+
<Sidebar>
|
|
391
|
+
<SidebarHeader>…</SidebarHeader>
|
|
392
|
+
<SidebarBody>
|
|
393
|
+
<SidebarSection>
|
|
394
|
+
<SidebarItem href="/" current><SidebarLabel>Home</SidebarLabel></SidebarItem>
|
|
395
|
+
</SidebarSection>
|
|
396
|
+
</SidebarBody>
|
|
397
|
+
<SidebarFooter>…</SidebarFooter>
|
|
398
|
+
</Sidebar>
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
</details>
|
|
402
|
+
|
|
403
|
+
<details>
|
|
404
|
+
<summary>Divider, Fieldset, DescriptionList</summary>
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
import { Divider, Fieldset, Legend, FieldGroup, Field, Label, Description, ErrorMessage } from '@dashnex/ui/client'
|
|
408
|
+
import { DescriptionList, DescriptionTerm, DescriptionDetails } from '@dashnex/ui/client'
|
|
409
|
+
|
|
410
|
+
<Divider soft />
|
|
411
|
+
|
|
412
|
+
<Fieldset disabled={false}>
|
|
413
|
+
<Legend>Form section</Legend>
|
|
414
|
+
<FieldGroup>
|
|
415
|
+
<Field>
|
|
416
|
+
<Label>Name</Label>
|
|
417
|
+
<Input />
|
|
418
|
+
<Description>Helper text.</Description>
|
|
419
|
+
<ErrorMessage>Error text.</ErrorMessage>
|
|
420
|
+
</Field>
|
|
421
|
+
</FieldGroup>
|
|
422
|
+
</Fieldset>
|
|
423
|
+
|
|
424
|
+
<DescriptionList>
|
|
425
|
+
<DescriptionTerm>Label</DescriptionTerm>
|
|
426
|
+
<DescriptionDetails>Value</DescriptionDetails>
|
|
427
|
+
</DescriptionList>
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
`Divider` optional `soft` (lighter border).
|
|
431
|
+
|
|
432
|
+
</details>
|
|
433
|
+
|
|
434
|
+
<details>
|
|
435
|
+
<summary>Checkbox, Radio, Switch</summary>
|
|
436
|
+
|
|
437
|
+
```tsx
|
|
438
|
+
import { Checkbox, CheckboxField, CheckboxGroup, Label, Description } from '@dashnex/ui/client'
|
|
439
|
+
import { Radio, RadioGroup, RadioField } from '@dashnex/ui/client'
|
|
440
|
+
import { Switch, SwitchGroup, SwitchField } from '@dashnex/ui/client'
|
|
441
|
+
|
|
442
|
+
<CheckboxGroup>
|
|
443
|
+
<CheckboxField>
|
|
444
|
+
<Checkbox color="dark/zinc" checked={v} onChange={setV} />
|
|
445
|
+
<Label>Option</Label>
|
|
446
|
+
<Description>Optional description.</Description>
|
|
447
|
+
</CheckboxField>
|
|
448
|
+
</CheckboxGroup>
|
|
449
|
+
|
|
450
|
+
<RadioGroup value={v} onChange={setV} name="choice">
|
|
451
|
+
<RadioField>
|
|
452
|
+
<Radio value="a" color="dark/zinc" />
|
|
453
|
+
<Label>A</Label>
|
|
454
|
+
</RadioField>
|
|
455
|
+
</RadioGroup>
|
|
456
|
+
|
|
457
|
+
<SwitchField>
|
|
458
|
+
<Switch color="dark/zinc" checked={on} onChange={setOn} />
|
|
459
|
+
<Label>Toggle</Label>
|
|
460
|
+
</SwitchField>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
`color` optional on Checkbox, Radio, Switch (e.g. `'dark/zinc'`, `'blue'`).
|
|
464
|
+
|
|
465
|
+
</details>
|
|
466
|
+
|
|
467
|
+
<details>
|
|
468
|
+
<summary>Textarea</summary>
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
import { Textarea } from '@dashnex/ui/client'
|
|
472
|
+
|
|
473
|
+
<Textarea placeholder="Message" resizable />
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
`resizable` optional (default `true`).
|
|
477
|
+
|
|
478
|
+
</details>
|
|
479
|
+
|
|
480
|
+
<details>
|
|
481
|
+
<summary>ThemeProvider and ThemeSwitcher</summary>
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
import { ThemeProvider, ThemeSwitcher, useTheme } from '@dashnex/ui/client'
|
|
485
|
+
|
|
486
|
+
<ThemeProvider>
|
|
487
|
+
<ThemeSwitcher />
|
|
488
|
+
<App />
|
|
489
|
+
</ThemeProvider>
|
|
490
|
+
|
|
491
|
+
// Anywhere under ThemeProvider
|
|
492
|
+
const { theme, setTheme, toggleTheme, isDark, mounted } = useTheme()
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
</details>
|
|
496
|
+
|
|
497
|
+
<details>
|
|
498
|
+
<summary>Loading</summary>
|
|
499
|
+
|
|
500
|
+
```tsx
|
|
501
|
+
import { Loading } from '@dashnex/ui/client'
|
|
502
|
+
|
|
503
|
+
<Loading />
|
|
504
|
+
<Loading>Loading message…</Loading>
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
`children` optional; shows under the spinner.
|
|
508
|
+
|
|
509
|
+
</details>
|
|
510
|
+
|
|
511
|
+
## Exported server exports
|
|
512
|
+
|
|
513
|
+
Same UI components and default export as client. Import from `@dashnex/ui` (or `@dashnex/ui/tailwind` for components only). No server-only functions or singletons.
|
|
514
|
+
|
|
515
|
+
## External libraries used
|
|
516
|
+
|
|
517
|
+
| Package | Purpose |
|
|
518
|
+
|--------|----------|
|
|
519
|
+
| `@headlessui/react` | Accessible unstyled primitives (Dialog, Menu, Listbox, Combobox, etc.) |
|
|
520
|
+
| `@heroicons/react` | Icons (re-exported from `@dashnex/ui/client` as 20/solid) |
|
|
521
|
+
| `clsx` | Conditional class names |
|
|
522
|
+
| `framer-motion` | Layout and transition animations (e.g. NavbarItem, SidebarItem) |
|
package/dashnex.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './components/tailwind/index.js';
|
|
2
|
+
export * from './components/theme/index.js';
|
|
3
|
+
export { Loading } from './components/Loading.js';
|
|
4
|
+
export * from '@heroicons/react/20/solid';
|
|
5
|
+
declare const _default: {
|
|
6
|
+
name: string;
|
|
7
|
+
version: string;
|
|
8
|
+
description: string;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import packageJson from '../package.json' with { type: "json" };
|
|
2
|
+
import dashnexConfig from '../dashnex.json' with { type: "json" };
|
|
3
|
+
export * from './components/tailwind/index.js';
|
|
4
|
+
export * from './components/theme/index.js';
|
|
5
|
+
export { Loading } from './components/Loading.js';
|
|
6
|
+
export * from '@heroicons/react/20/solid';
|
|
7
|
+
export default {
|
|
8
|
+
name: packageJson.name,
|
|
9
|
+
version: packageJson.version,
|
|
10
|
+
description: packageJson.description,
|
|
11
|
+
...dashnexConfig,
|
|
12
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function Loading({ children }) {
|
|
3
|
+
return (_jsx("div", { className: "flex h-screen w-screen items-center justify-center", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsx("div", { className: "h-10 w-10 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 dark:border-gray-600 dark:border-t-blue-400" }), children] }) }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DialogProps, DialogTitleProps, DescriptionProps } from '@headlessui/react';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
import { Text } from './text.js';
|
|
4
|
+
declare const sizes: {
|
|
5
|
+
xs: string;
|
|
6
|
+
sm: string;
|
|
7
|
+
md: string;
|
|
8
|
+
lg: string;
|
|
9
|
+
xl: string;
|
|
10
|
+
'2xl': string;
|
|
11
|
+
'3xl': string;
|
|
12
|
+
'4xl': string;
|
|
13
|
+
'5xl': string;
|
|
14
|
+
};
|
|
15
|
+
export declare function Alert({ size, className, children, ...props }: {
|
|
16
|
+
size?: keyof typeof sizes;
|
|
17
|
+
className?: string;
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
} & Omit<DialogProps, 'as' | 'className'>): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function AlertTitle({ className, ...props }: {
|
|
21
|
+
className?: string;
|
|
22
|
+
} & Omit<DialogTitleProps, 'as' | 'className'>): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export declare function AlertDescription({ className, ...props }: {
|
|
24
|
+
className?: string;
|
|
25
|
+
} & Omit<DescriptionProps<typeof Text>, 'as' | 'className'>): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function AlertBody({ className, ...props }: React.ComponentPropsWithoutRef<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export declare function AlertActions({ className, ...props }: React.ComponentPropsWithoutRef<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description } from '@headlessui/react';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { Text } from './text.js';
|
|
5
|
+
const sizes = {
|
|
6
|
+
xs: 'sm:max-w-xs',
|
|
7
|
+
sm: 'sm:max-w-sm',
|
|
8
|
+
md: 'sm:max-w-md',
|
|
9
|
+
lg: 'sm:max-w-lg',
|
|
10
|
+
xl: 'sm:max-w-xl',
|
|
11
|
+
'2xl': 'sm:max-w-2xl',
|
|
12
|
+
'3xl': 'sm:max-w-3xl',
|
|
13
|
+
'4xl': 'sm:max-w-4xl',
|
|
14
|
+
'5xl': 'sm:max-w-5xl',
|
|
15
|
+
};
|
|
16
|
+
export function Alert({ size = 'md', className, children, ...props }) {
|
|
17
|
+
return (_jsxs(Dialog, { ...props, children: [_jsx(DialogBackdrop, { transition: true, className: "fixed inset-0 flex w-screen justify-center overflow-y-auto bg-zinc-950/15 px-2 py-2 transition duration-100 focus:outline-0 data-closed:opacity-0 data-enter:ease-out data-leave:ease-in sm:px-6 sm:py-8 lg:px-8 lg:py-16 dark:bg-zinc-950/50" }), _jsx("div", { className: "fixed inset-0 w-screen overflow-y-auto pt-6 sm:pt-0", children: _jsx("div", { className: "grid min-h-full grid-rows-[1fr_auto_1fr] justify-items-center p-8 sm:grid-rows-[1fr_auto_3fr] sm:p-4", children: _jsx(DialogPanel, { transition: true, className: clsx(className, sizes[size], 'row-start-2 w-full rounded-2xl bg-white p-8 shadow-lg ring-1 ring-zinc-950/10 sm:rounded-2xl sm:p-6 dark:bg-zinc-900 dark:ring-white/10 forced-colors:outline', 'transition duration-100 will-change-transform data-closed:opacity-0 data-enter:ease-out data-closed:data-enter:scale-95 data-leave:ease-in'), children: children }) }) })] }));
|
|
18
|
+
}
|
|
19
|
+
export function AlertTitle({ className, ...props }) {
|
|
20
|
+
return (_jsx(DialogTitle, { ...props, className: clsx(className, 'text-center text-base/6 font-semibold text-balance text-zinc-950 sm:text-left sm:text-sm/6 sm:text-wrap dark:text-white') }));
|
|
21
|
+
}
|
|
22
|
+
export function AlertDescription({ className, ...props }) {
|
|
23
|
+
return (_jsx(Description, { as: Text, ...props, className: clsx(className, 'mt-2 text-center text-pretty sm:text-left') }));
|
|
24
|
+
}
|
|
25
|
+
export function AlertBody({ className, ...props }) {
|
|
26
|
+
return _jsx("div", { ...props, className: clsx(className, 'mt-4') });
|
|
27
|
+
}
|
|
28
|
+
export function AlertActions({ className, ...props }) {
|
|
29
|
+
return (_jsx("div", { ...props, className: clsx(className, 'mt-6 flex flex-col-reverse items-center justify-end gap-3 *:w-full sm:mt-4 sm:flex-row sm:*:w-auto') }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export function AuthLayout({ children }) {
|
|
3
|
+
return (_jsx("main", { className: "flex min-h-dvh flex-col p-2", children: _jsx("div", { className: "flex grow items-center justify-center p-6 lg:rounded-lg lg:bg-white lg:p-10 lg:shadow-xs lg:ring-1 lg:ring-zinc-950/5 dark:lg:bg-zinc-900 dark:lg:ring-white/10", children: children }) }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as Headless from '@headlessui/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
type AvatarProps = {
|
|
4
|
+
src?: string | null;
|
|
5
|
+
square?: boolean;
|
|
6
|
+
initials?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function Avatar({ src, square, initials, alt, className, ...props }: AvatarProps & React.ComponentPropsWithoutRef<'span'>): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const AvatarButton: React.ForwardRefExoticComponent<(AvatarProps & (Omit<Headless.ButtonProps<"button">, "className" | "as"> | Omit<Omit<{
|
|
12
|
+
href: string;
|
|
13
|
+
} & Omit<React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "ref"> & React.RefAttributes<HTMLAnchorElement>, "ref">, "className">)) & React.RefAttributes<HTMLElement>>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as Headless from '@headlessui/react';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { forwardRef } from 'react';
|
|
5
|
+
import Image from 'next/image.js';
|
|
6
|
+
import { TouchTarget } from './button.js';
|
|
7
|
+
import { Link } from './link.js';
|
|
8
|
+
export function Avatar({ src = null, square = false, initials, alt = '', className, ...props }) {
|
|
9
|
+
return (_jsxs("span", { "data-slot": "avatar", ...props, className: clsx(className,
|
|
10
|
+
// Basic layout
|
|
11
|
+
'inline-grid shrink-0 align-middle [--avatar-radius:20%] *:col-start-1 *:row-start-1', 'outline -outline-offset-1 outline-black/10 dark:outline-white/10', 'overflow-hidden',
|
|
12
|
+
// Border radius
|
|
13
|
+
square ? 'rounded-(--avatar-radius) *:rounded-(--avatar-radius)' : 'rounded-full *:rounded-full'), children: [initials && (_jsxs("svg", { className: "size-full fill-current p-[5%] text-[48px] font-medium uppercase select-none", viewBox: "0 0 100 100", "aria-hidden": alt ? undefined : 'true', children: [alt && _jsx("title", { children: alt }), _jsx("text", { x: "50%", y: "50%", alignmentBaseline: "middle", dominantBaseline: "middle", textAnchor: "middle", dy: ".125em", children: initials })] })), src && _jsx(Image, { className: "size-full", src: src, alt: alt, width: 100, height: 100 })] }));
|
|
14
|
+
}
|
|
15
|
+
export const AvatarButton = forwardRef(function AvatarButton({ src, square = false, initials, alt, className, ...props }, ref) {
|
|
16
|
+
const classes = clsx(className, square ? 'rounded-[20%]' : 'rounded-full', 'relative inline-grid focus:not-data-focus:outline-hidden data-focus:outline-2 data-focus:outline-offset-2 data-focus:outline-blue-500');
|
|
17
|
+
return 'href' in props ? (_jsx(Link, { ...props, className: classes, ref: ref, children: _jsx(TouchTarget, { children: _jsx(Avatar, { src: src, square: square, initials: initials, alt: alt }) }) })) : (_jsx(Headless.Button, { ...props, className: classes, ref: ref, children: _jsx(TouchTarget, { children: _jsx(Avatar, { src: src, square: square, initials: initials, alt: alt }) }) }));
|
|
18
|
+
});
|