@loykin/designkit 0.0.1-dev.0 → 0.0.1-dev.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 CHANGED
@@ -5,39 +5,43 @@ React page template library. Provides ready-to-use page layouts for admin and da
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @loykin/designkit @loykin/gridkit
8
+ npm install @loykin/designkit
9
9
  ```
10
10
 
11
- Requires React 19 and Tailwind CSS v4. UI components are based on [shadcn/ui](https://ui.shadcn.com) — if your app has shadcn set up, theming integrates automatically via shared CSS variables (`--primary`, `--background`, `--radius`, etc.). You can customize every token through shadcn's theme without touching designkit directly.
11
+ Requires React 19 and Tailwind CSS v4. UI components are based on [shadcn/ui](https://ui.shadcn.com) — if your app has shadcn set up, theming integrates automatically via shared CSS variables (`--primary`, `--background`, `--radius`, etc.).
12
12
 
13
- Add the following to your global CSS so Tailwind scans the designkit bundle:
13
+ Import the styles in your global CSS:
14
14
 
15
15
  ```css
16
16
  /* globals.css */
17
- @source "node_modules/@loykin/designkit/dist/index.js";
17
+ @import "@loykin/designkit/styles";
18
18
  ```
19
19
 
20
+ If your app builds Tailwind utilities from package source, include designkit in
21
+ your Tailwind scan sources:
22
+
23
+ ```css
24
+ @source "@loykin/designkit";
25
+ ```
26
+
27
+ You can also import the stylesheet from your app entry, as shown below.
28
+
20
29
  ## Quick Start
21
30
 
22
31
  ```tsx
23
- import { DataBodyTemplate, DataGridView, PageTopBar, type DataGridColumnDef } from '@loykin/designkit'
32
+ import { DataBodyTemplate, PageTopBar, Button } from '@loykin/designkit'
24
33
  import '@loykin/designkit/styles'
25
34
 
26
- type User = { id: string; name: string; email: string }
27
-
28
- const columns: DataGridColumnDef<User>[] = [
29
- { id: 'name', accessorKey: 'name', header: 'Name' },
30
- { id: 'email', accessorKey: 'email', header: 'Email' },
31
- ]
32
-
33
35
  export function UsersPage() {
34
36
  return (
35
37
  <DataBodyTemplate
36
38
  topBar={<PageTopBar left="Admin / Users" />}
37
39
  title="Users"
40
+ description="Manage your team members."
41
+ actions={<Button>Add User</Button>}
38
42
  >
39
43
  <DataBodyTemplate.Body>
40
- <DataGridView data={data} columns={columns} getRowId={(row) => row.id} />
44
+ {/* your content */}
41
45
  </DataBodyTemplate.Body>
42
46
  </DataBodyTemplate>
43
47
  )
@@ -50,7 +54,7 @@ export function UsersPage() {
50
54
 
51
55
  ### DataBodyTemplate
52
56
 
53
- The general-purpose page shell. Accepts three kinds of child slots — `.Body`, `.Tab`, and `.Section` which determine the layout mode automatically.
57
+ General-purpose page shell. Accepts `.Body`, `.Tab`, and `.Section` child slots which determine the layout mode automatically.
54
58
 
55
59
  ```tsx
56
60
  <DataBodyTemplate
@@ -68,33 +72,33 @@ The general-purpose page shell. Accepts three kinds of child slots — `.Body`,
68
72
  | `topBar` | `ReactNode` | Top breadcrumb bar. Pass `<PageTopBar left="..." />` or omit. |
69
73
  | `title` | `ReactNode` | Page title |
70
74
  | `description` | `ReactNode` | Subtitle below the title |
71
- | `actions` | `ReactNode` | Page-level actions (Add, Export, etc.) next to the title. Not for form Save buttons. |
75
+ | `actions` | `ReactNode` | Page-level actions (Add, Export, etc.) next to the title |
72
76
  | `toolbarLeft` / `toolbarRight` | `ReactNode` | Toolbar slots above the content area |
73
77
  | `theme` | `CSSProperties` | Inline CSS variable overrides |
74
78
  | `className` | `string` | Class applied to the page root |
75
79
 
76
80
  #### DataBodyTemplate.Body
77
81
 
78
- Single-pane content. No navigation. Use for full-height grid or custom layouts.
82
+ Single-pane content. Use for full-height layouts.
79
83
 
80
84
  ```tsx
81
85
  <DataBodyTemplate title="Users">
82
86
  <DataBodyTemplate.Body>
83
- <DataGridView data={data} columns={columns} getRowId={(row) => row.id} />
87
+ {/* your content */}
84
88
  </DataBodyTemplate.Body>
85
89
  </DataBodyTemplate>
86
90
  ```
87
91
 
88
- > Passing children directly without any slot wrapper also renders as a body, but `.Body` makes the layout intent explicit.
92
+ > Passing children directly without a slot wrapper also renders as a body, but `.Body` makes the intent explicit.
89
93
 
90
94
  #### DataBodyTemplate.Tab
91
95
 
92
- Creates a tabbed page layout. Add multiple `.Tab` children to enable the tab bar.
96
+ Creates a tabbed page layout.
93
97
 
94
98
  ```tsx
95
99
  <DataBodyTemplate title="Users">
96
100
  <DataBodyTemplate.Tab id="list" label="List" count={42}>
97
- <DataGridView data={data} columns={columns} getRowId={(row) => row.id} />
101
+ {/* list content */}
98
102
  </DataBodyTemplate.Tab>
99
103
  <DataBodyTemplate.Tab id="settings" label="Settings">
100
104
  <SettingsForm />
@@ -104,7 +108,7 @@ Creates a tabbed page layout. Add multiple `.Tab` children to enable the tab bar
104
108
 
105
109
  #### DataBodyTemplate.Section
106
110
 
107
- Settings-style layout with left navigation and right content panel. Add multiple `.Section` children to enable the side nav.
111
+ Settings-style layout with left navigation and right content panel.
108
112
 
109
113
  ```tsx
110
114
  <DataBodyTemplate title="Settings">
@@ -127,12 +131,12 @@ Settings-style layout with left navigation and right content panel. Add multiple
127
131
 
128
132
  Groups content within a tab or section. The `layout` prop controls the visual structure.
129
133
 
130
- | layout | Use case | Default wrapper |
131
- |---|---|---|
132
- | `horizontal` | Label on left, input on right (settings form) | Card |
133
- | `stacked` | Label above, input below | plain |
134
- | `inline` | Table-style rows (detail view, inline form) | bordered |
135
- | `split` | Left list + right detail | bordered |
134
+ | layout | Use case |
135
+ |---|---|
136
+ | `horizontal` | Label on left, input on right (settings form) |
137
+ | `stacked` | Label above, input below |
138
+ | `inline` | Table-style rows (detail view) |
139
+ | `split` | Left list + right detail |
136
140
 
137
141
  ```tsx
138
142
  <DataBodyTemplate.Tab id="settings" label="Settings">
@@ -149,7 +153,7 @@ Groups content within a tab or section. The `layout` prop controls the visual st
149
153
 
150
154
  #### DataBodyTemplate.Field
151
155
 
152
- Read-only key-value display. Use with `Group layout="inline"` for detail pages.
156
+ Read-only key-value display.
153
157
 
154
158
  ```tsx
155
159
  <DataBodyTemplate.Group layout="inline" title="Identity">
@@ -160,7 +164,7 @@ Read-only key-value display. Use with `Group layout="inline"` for detail pages.
160
164
 
161
165
  #### DataBodyTemplate.Summary
162
166
 
163
- A pinned summary area below the header, above tabs.
167
+ Pinned summary area below the header, above tabs.
164
168
 
165
169
  ```tsx
166
170
  <DataBodyTemplate title="Overview">
@@ -173,49 +177,132 @@ A pinned summary area below the header, above tabs.
173
177
 
174
178
  ---
175
179
 
176
- ### DataGridView
180
+ ### DashboardBodyTemplate
177
181
 
178
- Data table component. Use inside `DataBodyTemplate.Body` or any `.Tab` / `.Section`.
182
+ Dashboard page shell. Provides the chrome top bar, variable bar, panel grid area — while the app owns panel content and the data engine.
179
183
 
180
- | variant | Description |
181
- |---|---|
182
- | `standard` (default) | Standard data table with pagination |
183
- | `infinity` | Infinite scroll |
184
- | `drag` | Row drag-to-reorder |
185
- | `card` | Card grid |
186
- | `card-list` | Card list |
184
+ Requires [`@loykin/dashboardkit`](https://github.com/loykin/dashboardkit) 0.0.6 or newer for the grid and variable system:
187
185
 
188
- ```tsx
189
- import { DataBodyTemplate, DataGridView, PageTopBar, type DataGridColumnDef } from '@loykin/designkit'
186
+ ```bash
187
+ npm install @loykin/dashboardkit@^0.0.6 react-grid-layout
188
+ ```
190
189
 
191
- type User = { id: string; name: string; email: string }
190
+ Also import DashboardKit's grid CSS in your app:
192
191
 
193
- const columns: DataGridColumnDef<User>[] = [
194
- { id: 'name', accessorKey: 'name', header: 'Name' },
195
- { id: 'email', accessorKey: 'email', header: 'Email' },
196
- ]
192
+ ```ts
193
+ import '@loykin/dashboardkit/styles'
194
+ ```
195
+
196
+ For rounded design systems, tune DashboardKit's resize handle variables so the
197
+ handle stays inside the clipped panel corner:
198
+
199
+ ```css
200
+ .layout-dashboard .react-grid-item:not(.react-grid-placeholder) {
201
+ --dk-resize-handle-size: clamp(20px, calc(var(--radius) * 1.6), 40px);
202
+ --dk-resize-handle-inset: clamp(4px, calc(var(--radius) * 0.45), 16px);
203
+ --dk-resize-handle-mark-size: clamp(6px, calc(var(--radius) * 0.55), 12px);
204
+ --dk-resize-handle-color: rgba(0, 0, 0, 0.35);
205
+ --dk-resize-handle-mark-radius: min(var(--radius), var(--dk-resize-handle-mark-size));
206
+ }
207
+
208
+ .dark .layout-dashboard .react-grid-item:not(.react-grid-placeholder) {
209
+ --dk-resize-handle-color: rgba(255, 255, 255, 0.4);
210
+ }
211
+ ```
212
+
213
+ ```tsx
214
+ import React, { useState, useMemo } from 'react'
215
+ import { DashboardBodyTemplate, DashboardPanel, PageTopBar } from '@loykin/designkit'
216
+ import { createDashboardEngine, definePanel } from '@loykin/dashboardkit'
217
+ import { useLoadDashboard, useVariable, DashboardGrid } from '@loykin/dashboardkit/react'
218
+ import type { PanelViewerProps } from '@loykin/dashboardkit'
219
+
220
+ // Register panel types once at module level
221
+ const engine = createDashboardEngine()
222
+ engine.registerPanel(definePanel({
223
+ id: 'stat',
224
+ name: 'Stat',
225
+ optionsSchema: {},
226
+ viewer({ data, loading }: PanelViewerProps<unknown, unknown>) {
227
+ if (loading) return null
228
+ return <div className="text-2xl font-bold">{String(data ?? '—')}</div>
229
+ },
230
+ }))
231
+
232
+ export function MyDashboard() {
233
+ useLoadDashboard(engine, config)
234
+ const envVar = useVariable(engine, 'env')
235
+ const variables = useMemo(() => ({ env: (envVar.value as string) ?? 'production' }), [envVar.value])
236
+ const [editable, setEditable] = useState(false)
197
237
 
198
- export function UsersPage() {
199
238
  return (
200
- <DataBodyTemplate topBar={<PageTopBar left="Admin / Users" />} title="Users">
201
- <DataBodyTemplate.Body>
202
- <DataGridView
203
- data={data}
204
- columns={columns}
205
- getRowId={(row) => row.id}
206
- tableHeight={420}
207
- />
208
- </DataBodyTemplate.Body>
209
- </DataBodyTemplate>
239
+ <DashboardBodyTemplate
240
+ topBar={<PageTopBar left="My Dashboard" right={/* controls */} />}
241
+ variableBar={/* <VariableSelect> components */}
242
+ >
243
+ <DashboardGrid engine={engine} editable={editable}>
244
+ {({ panelType, config, data, rawData, loading, error, ref }) => {
245
+ const Viewer = engine.getPanelPlugin(panelType)?.viewer as React.FC<PanelViewerProps<unknown, unknown>> | undefined
246
+ return (
247
+ <DashboardPanel
248
+ ref={ref}
249
+ title={config.title}
250
+ loading={loading}
251
+ error={error ?? undefined}
252
+ editable={editable}
253
+ style={{ height: '100%' }}
254
+ >
255
+ {Viewer && (
256
+ <Viewer
257
+ panel={config} options={config.options}
258
+ data={data} rawData={rawData}
259
+ width={0} height={0}
260
+ loading={loading} error={error}
261
+ variables={variables}
262
+ />
263
+ )}
264
+ </DashboardPanel>
265
+ )
266
+ }}
267
+ </DashboardGrid>
268
+ </DashboardBodyTemplate>
210
269
  )
211
270
  }
212
271
  ```
213
272
 
273
+ **DashboardBodyTemplate props:**
274
+
275
+ | Prop | Type | Description |
276
+ |---|---|---|
277
+ | `topBar` | `ReactNode` | Top bar — breadcrumb, edit/refresh controls |
278
+ | `variableBar` | `ReactNode` | Variable dropdown strip between top bar and panels |
279
+ | `title` | `ReactNode` | Dashboard title (omit if topBar covers it) |
280
+ | `description` | `ReactNode` | Subtitle |
281
+ | `toolbar` | `ReactNode` | Toolbar slot next to the title |
282
+ | `theme` | `CSSProperties` | Inline CSS variable overrides |
283
+ | `className` | `string` | Class applied to the page root |
284
+ | `contentClassName` | `string` | Class applied to the panel grid area |
285
+
286
+ **DashboardPanel props:**
287
+
288
+ Panel card component. Wrap your panel viewer in `DashboardPanel` for consistent chrome across all panels.
289
+
290
+ | Prop | Type | Description |
291
+ |---|---|---|
292
+ | `title` | `string` | Panel title |
293
+ | `description` | `string` | Panel subtitle |
294
+ | `loading` | `boolean` | Shows loading spinner overlay |
295
+ | `error` | `string` | Shows error state, hides children |
296
+ | `editable` | `boolean` | Shows drag handle and edit ring |
297
+ | `headerRight` | `ReactNode` | Action slot in panel header — hidden until hover |
298
+ | `transparent` | `boolean` | Removes card border and background |
299
+ | `ref` | forwarded | Attach `DashboardGrid`'s `ref` for viewport virtualization |
300
+
214
301
  ---
215
302
 
216
303
  ### FormWizardBodyTemplate
217
304
 
218
- Multi-step input wizard. The template wraps each step's `content` in a `<form>` element automatically, so the Continue / Finish button acts as a submit trigger. Press Enter or click Continue to advance.
305
+ Multi-step input wizard. Wraps each step's `content` in a `<form>` element automatically.
219
306
 
220
307
  ```tsx
221
308
  import { useState } from 'react'
@@ -242,7 +329,7 @@ export function OnboardingPage() {
242
329
  }
243
330
  ```
244
331
 
245
- For per-step validation with `react-hook-form`, call `trigger()` inside `onNext` before advancing:
332
+ For per-step validation with `react-hook-form`:
246
333
 
247
334
  ```tsx
248
335
  onNext={async () => {
@@ -251,13 +338,13 @@ onNext={async () => {
251
338
  }}
252
339
  ```
253
340
 
254
- The `variant` prop switches between `'plain'` (default) and `'card'` to wrap each step in a Card.
341
+ The `variant` prop switches between `'plain'` (default) and `'card'`.
255
342
 
256
343
  ---
257
344
 
258
345
  ### LoginBodyTemplate
259
346
 
260
- Authentication page shell. Renders centered or split-panel login layouts.
347
+ Authentication page shell.
261
348
 
262
349
  ```tsx
263
350
  import { LoginBodyTemplate } from '@loykin/designkit'
@@ -284,39 +371,46 @@ export function SignInPage() {
284
371
  | Prop | Type | Default | Description |
285
372
  |---|---|---|---|
286
373
  | `layout` | `'centered' \| 'split'` | `'centered'` | Centered card or split-panel with brand side |
287
- | `card` | `'card' \| 'plain'` | `'plain'` | Wrap the form content in a card border |
288
- | `cardWidth` | `'sm' \| 'md' \| 'lg'` | `'md'` | Width of the form card |
374
+ | `card` | `'card' \| 'plain'` | `'plain'` | Wrap form content in a card border |
375
+ | `cardWidth` | `'sm' \| 'md' \| 'lg'` | `'md'` | Form card width |
289
376
  | `bg` | `'default' \| 'subtle' \| 'none'` | `'default'` | Background style |
290
- | `side` | `'left' \| 'right'` | `'left'` | Side the brand panel appears on (split layout only) |
377
+ | `side` | `'left' \| 'right'` | `'left'` | Brand panel side (split layout only) |
291
378
  | `brand` | `ReactNode` | built-in | Custom brand/logo panel content |
292
379
 
293
380
  ---
294
381
 
295
382
  ## UI Components
296
383
 
297
- Base components for use within page templates.
298
-
299
384
  ```tsx
300
385
  import {
301
- Badge, Button, Card, CardContent,
302
- Input, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
303
- Switch, Checkbox, Slider,
386
+ Avatar, AvatarFallback, AvatarImage,
387
+ Badge,
388
+ Button,
389
+ Card, CardContent, CardHeader, CardTitle,
390
+ Checkbox,
391
+ DropdownMenu,
304
392
  EmptyState,
305
- Tabs, TabsList, TabsTrigger, TabsContent,
393
+ Input,
394
+ Label,
395
+ NavigationMenu,
396
+ Popover,
397
+ ScrollArea,
398
+ Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
399
+ Separator,
306
400
  Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger,
401
+ Sidebar,
402
+ Skeleton,
403
+ Slider,
404
+ Switch,
405
+ Table,
406
+ Tabs, TabsList, TabsTrigger, TabsContent,
307
407
  Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
308
- Avatar, AvatarFallback, AvatarImage,
309
- Separator, Skeleton,
310
- Breadcrumb, DropdownMenu, NavigationMenu,
311
- Popover, ScrollArea, Sidebar, Table,
312
408
  PageTopBar,
313
409
  } from '@loykin/designkit'
314
410
  ```
315
411
 
316
412
  ### EmptyState
317
413
 
318
- Displays empty data, no search results, or error states.
319
-
320
414
  ```tsx
321
415
  import { EmptyState } from '@loykin/designkit'
322
416
  import { Users } from 'lucide-react'
@@ -333,13 +427,7 @@ import { Users } from 'lucide-react'
333
427
 
334
428
  ## Theming
335
429
 
336
- Designkit maps shadcn/ui CSS variables (`--primary`, `--background`, `--radius`, etc.) onto its own `--dk-*` tokens. Changing your shadcn theme automatically updates all designkit components.
337
-
338
- For additional control, override `--dk-*` variables directly in `globals.css`.
339
-
340
- ### Customization scope
341
-
342
- CSS variables are the customization layer. The library is opinionated about structure and ships its own UI components — visual appearance is controlled entirely through tokens.
430
+ Designkit maps shadcn/ui CSS variables onto its own `--dk-*` tokens. Changing your shadcn theme automatically updates all designkit components.
343
431
 
344
432
  | What | How |
345
433
  |---|---|
@@ -349,48 +437,39 @@ CSS variables are the customization layer. The library is opinionated about stru
349
437
 
350
438
  ```css
351
439
  :root {
352
- --dk-radius: 0.375rem;
353
- --dk-primary: oklch(0.52 0.2 250);
354
-
355
440
  --dk-density: 1; /* 0.85 compact / 1 default / 1.15 comfortable */
356
441
  --dk-page-padding-x: 1.5rem;
357
442
  --dk-page-padding-y: 1rem;
358
443
  --dk-panel-gap: 1rem;
359
- --dk-toolbar-height: 2.75rem;
360
444
  }
361
445
  ```
362
446
 
363
- To override per page, combine with `className`:
447
+ Per-page override via `className`:
364
448
 
365
449
  ```css
366
- .layout-settings {
367
- --dk-radius: 0.5rem;
368
- --dk-density: 1.15;
450
+ .layout-dashboard {
451
+ --dk-density: 0.85;
369
452
  }
370
453
  ```
371
454
 
372
455
  ```tsx
373
- <DataBodyTemplate className="layout-settings" title="Settings">
374
- ...
375
- </DataBodyTemplate>
456
+ <DashboardBodyTemplate className="layout-dashboard" ...>
376
457
  ```
377
458
 
378
- Or use the `theme` prop for inline overrides:
459
+ Or via `theme` prop:
379
460
 
380
461
  ```tsx
381
462
  <DataBodyTemplate
382
- theme={{ '--dk-radius': '0.5rem', '--dk-density': '1.15' } as React.CSSProperties}
463
+ theme={{ '--dk-density': '1.15' } as React.CSSProperties}
383
464
  title="Settings"
384
465
  >
385
- ...
386
- </DataBodyTemplate>
387
466
  ```
388
467
 
389
468
  ---
390
469
 
391
470
  ## Playground
392
471
 
393
- An interactive development tool to preview templates and generate CSS/component code.
472
+ Interactive development tool to preview templates and generate code.
394
473
 
395
474
  ```bash
396
475
  git clone https://github.com/loykin/designkit
@@ -399,8 +478,6 @@ pnpm install
399
478
  pnpm dev
400
479
  ```
401
480
 
402
- Open `http://localhost:5173` to browse templates and export code.
403
-
404
481
  ---
405
482
 
406
483
  ## License