@loykin/designkit 0.0.1-dev.1 → 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,10 +5,10 @@ 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
13
  Import the styles in your global CSS:
14
14
 
@@ -17,29 +17,31 @@ Import the styles in your global CSS:
17
17
  @import "@loykin/designkit/styles";
18
18
  ```
19
19
 
20
- This handles Tailwind class scanning automatically no additional `@source` configuration needed.
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.
21
28
 
22
29
  ## Quick Start
23
30
 
24
31
  ```tsx
25
- import { DataBodyTemplate, DataGridView, PageTopBar, type DataGridColumnDef } from '@loykin/designkit'
32
+ import { DataBodyTemplate, PageTopBar, Button } from '@loykin/designkit'
26
33
  import '@loykin/designkit/styles'
27
34
 
28
- type User = { id: string; name: string; email: string }
29
-
30
- const columns: DataGridColumnDef<User>[] = [
31
- { id: 'name', accessorKey: 'name', header: 'Name' },
32
- { id: 'email', accessorKey: 'email', header: 'Email' },
33
- ]
34
-
35
35
  export function UsersPage() {
36
36
  return (
37
37
  <DataBodyTemplate
38
38
  topBar={<PageTopBar left="Admin / Users" />}
39
39
  title="Users"
40
+ description="Manage your team members."
41
+ actions={<Button>Add User</Button>}
40
42
  >
41
43
  <DataBodyTemplate.Body>
42
- <DataGridView data={data} columns={columns} getRowId={(row) => row.id} />
44
+ {/* your content */}
43
45
  </DataBodyTemplate.Body>
44
46
  </DataBodyTemplate>
45
47
  )
@@ -52,7 +54,7 @@ export function UsersPage() {
52
54
 
53
55
  ### DataBodyTemplate
54
56
 
55
- 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.
56
58
 
57
59
  ```tsx
58
60
  <DataBodyTemplate
@@ -70,33 +72,33 @@ The general-purpose page shell. Accepts three kinds of child slots — `.Body`,
70
72
  | `topBar` | `ReactNode` | Top breadcrumb bar. Pass `<PageTopBar left="..." />` or omit. |
71
73
  | `title` | `ReactNode` | Page title |
72
74
  | `description` | `ReactNode` | Subtitle below the title |
73
- | `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 |
74
76
  | `toolbarLeft` / `toolbarRight` | `ReactNode` | Toolbar slots above the content area |
75
77
  | `theme` | `CSSProperties` | Inline CSS variable overrides |
76
78
  | `className` | `string` | Class applied to the page root |
77
79
 
78
80
  #### DataBodyTemplate.Body
79
81
 
80
- Single-pane content. No navigation. Use for full-height grid or custom layouts.
82
+ Single-pane content. Use for full-height layouts.
81
83
 
82
84
  ```tsx
83
85
  <DataBodyTemplate title="Users">
84
86
  <DataBodyTemplate.Body>
85
- <DataGridView data={data} columns={columns} getRowId={(row) => row.id} />
87
+ {/* your content */}
86
88
  </DataBodyTemplate.Body>
87
89
  </DataBodyTemplate>
88
90
  ```
89
91
 
90
- > 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.
91
93
 
92
94
  #### DataBodyTemplate.Tab
93
95
 
94
- Creates a tabbed page layout. Add multiple `.Tab` children to enable the tab bar.
96
+ Creates a tabbed page layout.
95
97
 
96
98
  ```tsx
97
99
  <DataBodyTemplate title="Users">
98
100
  <DataBodyTemplate.Tab id="list" label="List" count={42}>
99
- <DataGridView data={data} columns={columns} getRowId={(row) => row.id} />
101
+ {/* list content */}
100
102
  </DataBodyTemplate.Tab>
101
103
  <DataBodyTemplate.Tab id="settings" label="Settings">
102
104
  <SettingsForm />
@@ -106,7 +108,7 @@ Creates a tabbed page layout. Add multiple `.Tab` children to enable the tab bar
106
108
 
107
109
  #### DataBodyTemplate.Section
108
110
 
109
- 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.
110
112
 
111
113
  ```tsx
112
114
  <DataBodyTemplate title="Settings">
@@ -129,12 +131,12 @@ Settings-style layout with left navigation and right content panel. Add multiple
129
131
 
130
132
  Groups content within a tab or section. The `layout` prop controls the visual structure.
131
133
 
132
- | layout | Use case | Default wrapper |
133
- |---|---|---|
134
- | `horizontal` | Label on left, input on right (settings form) | Card |
135
- | `stacked` | Label above, input below | plain |
136
- | `inline` | Table-style rows (detail view, inline form) | bordered |
137
- | `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 |
138
140
 
139
141
  ```tsx
140
142
  <DataBodyTemplate.Tab id="settings" label="Settings">
@@ -151,7 +153,7 @@ Groups content within a tab or section. The `layout` prop controls the visual st
151
153
 
152
154
  #### DataBodyTemplate.Field
153
155
 
154
- Read-only key-value display. Use with `Group layout="inline"` for detail pages.
156
+ Read-only key-value display.
155
157
 
156
158
  ```tsx
157
159
  <DataBodyTemplate.Group layout="inline" title="Identity">
@@ -162,7 +164,7 @@ Read-only key-value display. Use with `Group layout="inline"` for detail pages.
162
164
 
163
165
  #### DataBodyTemplate.Summary
164
166
 
165
- A pinned summary area below the header, above tabs.
167
+ Pinned summary area below the header, above tabs.
166
168
 
167
169
  ```tsx
168
170
  <DataBodyTemplate title="Overview">
@@ -175,49 +177,132 @@ A pinned summary area below the header, above tabs.
175
177
 
176
178
  ---
177
179
 
178
- ### DataGridView
180
+ ### DashboardBodyTemplate
179
181
 
180
- 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.
181
183
 
182
- | variant | Description |
183
- |---|---|
184
- | `standard` (default) | Standard data table with pagination |
185
- | `infinity` | Infinite scroll |
186
- | `drag` | Row drag-to-reorder |
187
- | `card` | Card grid |
188
- | `card-list` | Card list |
184
+ Requires [`@loykin/dashboardkit`](https://github.com/loykin/dashboardkit) 0.0.6 or newer for the grid and variable system:
189
185
 
190
- ```tsx
191
- import { DataBodyTemplate, DataGridView, PageTopBar, type DataGridColumnDef } from '@loykin/designkit'
186
+ ```bash
187
+ npm install @loykin/dashboardkit@^0.0.6 react-grid-layout
188
+ ```
192
189
 
193
- type User = { id: string; name: string; email: string }
190
+ Also import DashboardKit's grid CSS in your app:
194
191
 
195
- const columns: DataGridColumnDef<User>[] = [
196
- { id: 'name', accessorKey: 'name', header: 'Name' },
197
- { id: 'email', accessorKey: 'email', header: 'Email' },
198
- ]
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)
199
237
 
200
- export function UsersPage() {
201
238
  return (
202
- <DataBodyTemplate topBar={<PageTopBar left="Admin / Users" />} title="Users">
203
- <DataBodyTemplate.Body>
204
- <DataGridView
205
- data={data}
206
- columns={columns}
207
- getRowId={(row) => row.id}
208
- tableHeight={420}
209
- />
210
- </DataBodyTemplate.Body>
211
- </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>
212
269
  )
213
270
  }
214
271
  ```
215
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
+
216
301
  ---
217
302
 
218
303
  ### FormWizardBodyTemplate
219
304
 
220
- 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.
221
306
 
222
307
  ```tsx
223
308
  import { useState } from 'react'
@@ -244,7 +329,7 @@ export function OnboardingPage() {
244
329
  }
245
330
  ```
246
331
 
247
- For per-step validation with `react-hook-form`, call `trigger()` inside `onNext` before advancing:
332
+ For per-step validation with `react-hook-form`:
248
333
 
249
334
  ```tsx
250
335
  onNext={async () => {
@@ -253,13 +338,13 @@ onNext={async () => {
253
338
  }}
254
339
  ```
255
340
 
256
- 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'`.
257
342
 
258
343
  ---
259
344
 
260
345
  ### LoginBodyTemplate
261
346
 
262
- Authentication page shell. Renders centered or split-panel login layouts.
347
+ Authentication page shell.
263
348
 
264
349
  ```tsx
265
350
  import { LoginBodyTemplate } from '@loykin/designkit'
@@ -286,39 +371,46 @@ export function SignInPage() {
286
371
  | Prop | Type | Default | Description |
287
372
  |---|---|---|---|
288
373
  | `layout` | `'centered' \| 'split'` | `'centered'` | Centered card or split-panel with brand side |
289
- | `card` | `'card' \| 'plain'` | `'plain'` | Wrap the form content in a card border |
290
- | `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 |
291
376
  | `bg` | `'default' \| 'subtle' \| 'none'` | `'default'` | Background style |
292
- | `side` | `'left' \| 'right'` | `'left'` | Side the brand panel appears on (split layout only) |
377
+ | `side` | `'left' \| 'right'` | `'left'` | Brand panel side (split layout only) |
293
378
  | `brand` | `ReactNode` | built-in | Custom brand/logo panel content |
294
379
 
295
380
  ---
296
381
 
297
382
  ## UI Components
298
383
 
299
- Base components for use within page templates.
300
-
301
384
  ```tsx
302
385
  import {
303
- Badge, Button, Card, CardContent,
304
- Input, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
305
- Switch, Checkbox, Slider,
386
+ Avatar, AvatarFallback, AvatarImage,
387
+ Badge,
388
+ Button,
389
+ Card, CardContent, CardHeader, CardTitle,
390
+ Checkbox,
391
+ DropdownMenu,
306
392
  EmptyState,
307
- Tabs, TabsList, TabsTrigger, TabsContent,
393
+ Input,
394
+ Label,
395
+ NavigationMenu,
396
+ Popover,
397
+ ScrollArea,
398
+ Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
399
+ Separator,
308
400
  Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger,
401
+ Sidebar,
402
+ Skeleton,
403
+ Slider,
404
+ Switch,
405
+ Table,
406
+ Tabs, TabsList, TabsTrigger, TabsContent,
309
407
  Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
310
- Avatar, AvatarFallback, AvatarImage,
311
- Separator, Skeleton,
312
- Breadcrumb, DropdownMenu, NavigationMenu,
313
- Popover, ScrollArea, Sidebar, Table,
314
408
  PageTopBar,
315
409
  } from '@loykin/designkit'
316
410
  ```
317
411
 
318
412
  ### EmptyState
319
413
 
320
- Displays empty data, no search results, or error states.
321
-
322
414
  ```tsx
323
415
  import { EmptyState } from '@loykin/designkit'
324
416
  import { Users } from 'lucide-react'
@@ -335,13 +427,7 @@ import { Users } from 'lucide-react'
335
427
 
336
428
  ## Theming
337
429
 
338
- Designkit maps shadcn/ui CSS variables (`--primary`, `--background`, `--radius`, etc.) onto its own `--dk-*` tokens. Changing your shadcn theme automatically updates all designkit components.
339
-
340
- For additional control, override `--dk-*` variables directly in `globals.css`.
341
-
342
- ### Customization scope
343
-
344
- 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.
345
431
 
346
432
  | What | How |
347
433
  |---|---|
@@ -351,48 +437,39 @@ CSS variables are the customization layer. The library is opinionated about stru
351
437
 
352
438
  ```css
353
439
  :root {
354
- --dk-radius: 0.375rem;
355
- --dk-primary: oklch(0.52 0.2 250);
356
-
357
440
  --dk-density: 1; /* 0.85 compact / 1 default / 1.15 comfortable */
358
441
  --dk-page-padding-x: 1.5rem;
359
442
  --dk-page-padding-y: 1rem;
360
443
  --dk-panel-gap: 1rem;
361
- --dk-toolbar-height: 2.75rem;
362
444
  }
363
445
  ```
364
446
 
365
- To override per page, combine with `className`:
447
+ Per-page override via `className`:
366
448
 
367
449
  ```css
368
- .layout-settings {
369
- --dk-radius: 0.5rem;
370
- --dk-density: 1.15;
450
+ .layout-dashboard {
451
+ --dk-density: 0.85;
371
452
  }
372
453
  ```
373
454
 
374
455
  ```tsx
375
- <DataBodyTemplate className="layout-settings" title="Settings">
376
- ...
377
- </DataBodyTemplate>
456
+ <DashboardBodyTemplate className="layout-dashboard" ...>
378
457
  ```
379
458
 
380
- Or use the `theme` prop for inline overrides:
459
+ Or via `theme` prop:
381
460
 
382
461
  ```tsx
383
462
  <DataBodyTemplate
384
- theme={{ '--dk-radius': '0.5rem', '--dk-density': '1.15' } as React.CSSProperties}
463
+ theme={{ '--dk-density': '1.15' } as React.CSSProperties}
385
464
  title="Settings"
386
465
  >
387
- ...
388
- </DataBodyTemplate>
389
466
  ```
390
467
 
391
468
  ---
392
469
 
393
470
  ## Playground
394
471
 
395
- An interactive development tool to preview templates and generate CSS/component code.
472
+ Interactive development tool to preview templates and generate code.
396
473
 
397
474
  ```bash
398
475
  git clone https://github.com/loykin/designkit
@@ -401,8 +478,6 @@ pnpm install
401
478
  pnpm dev
402
479
  ```
403
480
 
404
- Open `http://localhost:5173` to browse templates and export code.
405
-
406
481
  ---
407
482
 
408
483
  ## License