@getdashfy/ui 0.1.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/CHANGELOG.md +10 -0
- package/LICENSE +661 -0
- package/README.md +461 -0
- package/dist/index.cjs +6662 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1408 -0
- package/dist/index.d.ts +1408 -0
- package/dist/index.js +6355 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +10 -0
- package/package.json +152 -0
package/README.md
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# `@getdashfy/ui`
|
|
2
|
+
|
|
3
|
+
> Dashfy UI component library for building Dashfy dashboards.
|
|
4
|
+
|
|
5
|
+
## Introduction
|
|
6
|
+
|
|
7
|
+
`@getdashfy/ui` is the frontend runtime for Dashfy dashboards. It provides a complete React component library for rendering dashboards, managing WebSocket connections, handling real-time data subscriptions, and providing an extensible widget system.
|
|
8
|
+
|
|
9
|
+
The UI library acts as the client-side orchestrator that:
|
|
10
|
+
|
|
11
|
+
- Connects to the Dashfy server via WebSockets
|
|
12
|
+
- Renders dashboard layouts and widgets
|
|
13
|
+
- Manages real-time data subscriptions
|
|
14
|
+
- Provides theme support and customization
|
|
15
|
+
- Handles user interactions and keyboard shortcuts
|
|
16
|
+
- Manages application state with Zustand
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
Install with your favorite package manager:
|
|
21
|
+
|
|
22
|
+
#### `npm`
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @getdashfy/ui
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
#### `pnpm`
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @getdashfy/ui
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### `yarn`
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
yarn add @getdashfy/ui
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### `bun`
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bun add @getdashfy/ui
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
Create a Dashfy client and register extension widgets:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { CustomJson, JsonKeys, JsonStatus } from '@getdashfy/ext-json'
|
|
52
|
+
import {
|
|
53
|
+
Branches,
|
|
54
|
+
CommitActivityLine,
|
|
55
|
+
ContributorsStats,
|
|
56
|
+
Gitmap,
|
|
57
|
+
OrgBadge,
|
|
58
|
+
PullRequests,
|
|
59
|
+
RepoBadge,
|
|
60
|
+
Status,
|
|
61
|
+
UserBadge,
|
|
62
|
+
} from '@getdashfy/ext-github'
|
|
63
|
+
import { Dashfy, WidgetRegistry } from '@getdashfy/ui'
|
|
64
|
+
|
|
65
|
+
// Register GitHub extension widgets
|
|
66
|
+
WidgetRegistry.addExtension('github', {
|
|
67
|
+
Branches,
|
|
68
|
+
CommitActivityLine,
|
|
69
|
+
ContributorsStats,
|
|
70
|
+
Gitmap,
|
|
71
|
+
OrgBadge,
|
|
72
|
+
PullRequests,
|
|
73
|
+
RepoBadge,
|
|
74
|
+
Status,
|
|
75
|
+
UserBadge,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// Register JSON extension widgets
|
|
79
|
+
WidgetRegistry.addExtension('json', {
|
|
80
|
+
CustomJson,
|
|
81
|
+
JsonKeys,
|
|
82
|
+
JsonStatus,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
export const App = () => {
|
|
86
|
+
return <Dashfy serverUrl="http://localhost:5001" />
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Core Features
|
|
91
|
+
|
|
92
|
+
#### » Widget System
|
|
93
|
+
|
|
94
|
+
Extensible widget registry for registering and managing dashboard widgets:
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { WidgetRegistry } from '@getdashfy/ui'
|
|
98
|
+
import { MyCustomWidget, AnotherWidget } from './widgets'
|
|
99
|
+
|
|
100
|
+
// Register a single widget
|
|
101
|
+
WidgetRegistry.register('myextension:CustomWidget', MyCustomWidget)
|
|
102
|
+
|
|
103
|
+
// Register multiple widgets from an extension
|
|
104
|
+
WidgetRegistry.addExtension('myextension', {
|
|
105
|
+
CustomWidget: MyCustomWidget,
|
|
106
|
+
AnotherWidget,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Get a registered widget
|
|
110
|
+
const Widget = WidgetRegistry.getComponent('myextension', 'CustomWidget')
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Widget Lifecycle:**
|
|
114
|
+
|
|
115
|
+
1. Widgets are registered via `WidgetRegistry`
|
|
116
|
+
2. Dashboard configuration references widgets by `extension:widget` name
|
|
117
|
+
3. Dashfy component dynamically loads and renders widgets
|
|
118
|
+
4. Widgets subscribe to API data via `useApiSubscription` hook
|
|
119
|
+
5. Real-time updates flow through WebSocket to widget components
|
|
120
|
+
|
|
121
|
+
#### » Real-time Data Subscriptions
|
|
122
|
+
|
|
123
|
+
Built-in hooks for subscribing to API data from the server:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import {
|
|
127
|
+
useApiSubscription,
|
|
128
|
+
Widget,
|
|
129
|
+
WidgetHeader,
|
|
130
|
+
WidgetBody,
|
|
131
|
+
WidgetLoader,
|
|
132
|
+
WidgetError,
|
|
133
|
+
} from '@getdashfy/ui'
|
|
134
|
+
|
|
135
|
+
export const MyWidget = ({ repository }: { repository: string }) => {
|
|
136
|
+
const { data, error, loading } = useApiSubscription<RepoData>({
|
|
137
|
+
api: 'github',
|
|
138
|
+
endpoint: 'repo',
|
|
139
|
+
params: { repository },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (loading) return <WidgetLoader />
|
|
143
|
+
if (error) return <WidgetError error={error} />
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<Widget>
|
|
147
|
+
<WidgetHeader title="GitHub Repository" />
|
|
148
|
+
<WidgetBody>
|
|
149
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
150
|
+
</WidgetBody>
|
|
151
|
+
</Widget>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Subscription Features:**
|
|
157
|
+
|
|
158
|
+
- Automatic subscription on mount
|
|
159
|
+
- Automatic unsubscription on unmount
|
|
160
|
+
- Loading and error states
|
|
161
|
+
- Type-safe data handling
|
|
162
|
+
- Shared subscriptions (multiple widgets can subscribe to same data)
|
|
163
|
+
|
|
164
|
+
#### » WebSocket Connection
|
|
165
|
+
|
|
166
|
+
Managed [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) connection with automatic reconnection:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { useWebSocket } from '@getdashfy/ui'
|
|
170
|
+
|
|
171
|
+
const socket = useWebSocket({
|
|
172
|
+
url: 'http://localhost:5001', // Server URL
|
|
173
|
+
autoConnect: true,
|
|
174
|
+
reconnect: true,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
if (!socket) {
|
|
178
|
+
return <div>Connecting...</div>
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Socket is ready to use
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Connection Features:**
|
|
185
|
+
|
|
186
|
+
- Automatic reconnection with configurable attempts and delays
|
|
187
|
+
- Connection status tracking (via store)
|
|
188
|
+
- Automatic resubscription of API endpoints on reconnect
|
|
189
|
+
- Integration with notification system
|
|
190
|
+
- Graceful error handling and cleanup
|
|
191
|
+
|
|
192
|
+
#### » Theme System
|
|
193
|
+
|
|
194
|
+
Built-in theme support with light/dark mode:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { ThemeRegistry } from '@getdashfy/ui'
|
|
198
|
+
import { defaultTheme, nordTheme } from '@getdashfy/themes'
|
|
199
|
+
import { myCustomTheme } from './themes/custom'
|
|
200
|
+
|
|
201
|
+
// Load all built-in themes from @getdashfy/themes
|
|
202
|
+
ThemeRegistry.loadAllThemes()
|
|
203
|
+
|
|
204
|
+
// Or register themes individually
|
|
205
|
+
ThemeRegistry.add(myCustomTheme)
|
|
206
|
+
|
|
207
|
+
// Or register multiple themes at once
|
|
208
|
+
ThemeRegistry.addAll([defaultTheme, nordTheme])
|
|
209
|
+
|
|
210
|
+
// Set default theme
|
|
211
|
+
ThemeRegistry.defaultTheme = 'nord'
|
|
212
|
+
|
|
213
|
+
// Get all theme IDs
|
|
214
|
+
const themeIds = ThemeRegistry.list()
|
|
215
|
+
|
|
216
|
+
// Get a specific theme
|
|
217
|
+
const theme = ThemeRegistry.get('nord')
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Theme Features:**
|
|
221
|
+
|
|
222
|
+
- Built-in themes from `@getdashfy/themes` package
|
|
223
|
+
- Light/dark mode support
|
|
224
|
+
- CSS variable-based theming
|
|
225
|
+
- LocalStorage persistence
|
|
226
|
+
- Theme switching via settings panel
|
|
227
|
+
|
|
228
|
+
#### » Keyboard Shortcuts
|
|
229
|
+
|
|
230
|
+
Built-in keyboard shortcuts for dashboard control:
|
|
231
|
+
|
|
232
|
+
| Shortcut | Action |
|
|
233
|
+
| --------------------- | ------------------------ |
|
|
234
|
+
| `Space` | Start/Pause rotation |
|
|
235
|
+
| `←` / `→` | Navigate dashboards |
|
|
236
|
+
| `F` | Toggle fullscreen |
|
|
237
|
+
| `S` or `Cmd/Ctrl + ,` | Open settings |
|
|
238
|
+
| `Esc` | Close settings/exit full |
|
|
239
|
+
| `Cmd/Ctrl + P` | Open bottom panel |
|
|
240
|
+
|
|
241
|
+
#### » Bottom Panel
|
|
242
|
+
|
|
243
|
+
Tabbed bottom panel with built-in tabs:
|
|
244
|
+
|
|
245
|
+
- **Connection:** WebSocket connection status and server info
|
|
246
|
+
- **Console:** Real-time logs and API events
|
|
247
|
+
- **Notifications:** System notifications and alerts
|
|
248
|
+
|
|
249
|
+
#### » State Management
|
|
250
|
+
|
|
251
|
+
Centralized state management with [Zustand](https://github.com/pmndrs/zustand):
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { useDashfyStore } from '@getdashfy/ui'
|
|
255
|
+
|
|
256
|
+
// Access store slices
|
|
257
|
+
const config = useDashfyStore((state) => state.config)
|
|
258
|
+
const dashboards = useDashfyStore((state) => state.dashboards)
|
|
259
|
+
const currentTheme = useDashfyStore((state) => state.currentTheme)
|
|
260
|
+
|
|
261
|
+
// Dispatch actions
|
|
262
|
+
const setTheme = useDashfyStore((state) => state.setTheme)
|
|
263
|
+
const nextDashboard = useDashfyStore((state) => state.nextDashboard)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Widget Architecture
|
|
267
|
+
|
|
268
|
+
#### » Widget Props
|
|
269
|
+
|
|
270
|
+
Widgets receive props from dashboard configuration. The `WidgetConfig` interface defines the standard properties:
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
interface WidgetConfig {
|
|
274
|
+
extension: string
|
|
275
|
+
widget: string
|
|
276
|
+
title?: string
|
|
277
|
+
columns: number
|
|
278
|
+
rows: number
|
|
279
|
+
x: number
|
|
280
|
+
y: number
|
|
281
|
+
// Custom props defined in configuration
|
|
282
|
+
[key: string]: unknown
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
When creating widgets, you can define your own prop interface that extends the configuration properties you need.
|
|
287
|
+
|
|
288
|
+
#### » Widget Structure
|
|
289
|
+
|
|
290
|
+
Recommended widget structure:
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
import {
|
|
294
|
+
useApiSubscription,
|
|
295
|
+
Widget,
|
|
296
|
+
WidgetHeader,
|
|
297
|
+
WidgetBody,
|
|
298
|
+
WidgetLoader,
|
|
299
|
+
WidgetError,
|
|
300
|
+
WidgetEmpty,
|
|
301
|
+
} from '@getdashfy/ui'
|
|
302
|
+
|
|
303
|
+
interface MyWidgetProps {
|
|
304
|
+
title?: string
|
|
305
|
+
repository: string
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export const MyWidget = ({ title, repository }: MyWidgetProps) => {
|
|
309
|
+
const { data, error, loading } = useApiSubscription<RepoData>({
|
|
310
|
+
api: 'github',
|
|
311
|
+
endpoint: 'repo',
|
|
312
|
+
params: { repository },
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
if (loading) return <WidgetLoader />
|
|
316
|
+
if (error) return <WidgetError error={error} />
|
|
317
|
+
if (!data) return <WidgetEmpty message="No data available" />
|
|
318
|
+
|
|
319
|
+
return (
|
|
320
|
+
<Widget>
|
|
321
|
+
<WidgetHeader title={title || data.name} />
|
|
322
|
+
<WidgetBody>
|
|
323
|
+
<div className="space-y-2">
|
|
324
|
+
<p>{data.description}</p>
|
|
325
|
+
<div className="flex gap-4">
|
|
326
|
+
<span>⭐ {data.stars}</span>
|
|
327
|
+
<span>🍴 {data.forks}</span>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</WidgetBody>
|
|
331
|
+
</Widget>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Widget Best Practices:**
|
|
337
|
+
|
|
338
|
+
- Use `useApiSubscription` for data fetching
|
|
339
|
+
- Handle loading, error, and empty states
|
|
340
|
+
- Use base widget components for consistent styling
|
|
341
|
+
- Keep widgets focused and composable
|
|
342
|
+
- Provide TypeScript types for props
|
|
343
|
+
- Use error boundaries for resilience
|
|
344
|
+
|
|
345
|
+
#### » Widget Registration
|
|
346
|
+
|
|
347
|
+
Register widgets before rendering the Dashfy component:
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import { WidgetRegistry } from '@getdashfy/ui'
|
|
351
|
+
import { MyWidget } from './widgets/MyWidget'
|
|
352
|
+
|
|
353
|
+
// Register extension with multiple widgets
|
|
354
|
+
WidgetRegistry.addExtension('myextension', {
|
|
355
|
+
MyWidget,
|
|
356
|
+
AnotherWidget,
|
|
357
|
+
ThirdWidget,
|
|
358
|
+
})
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### » Widget Configuration
|
|
362
|
+
|
|
363
|
+
Reference widgets in dashboard configuration:
|
|
364
|
+
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"dashboards": [
|
|
368
|
+
{
|
|
369
|
+
"title": "My Dashboard",
|
|
370
|
+
"columns": 3,
|
|
371
|
+
"rows": 2,
|
|
372
|
+
"widgets": [
|
|
373
|
+
{
|
|
374
|
+
"extension": "myextension",
|
|
375
|
+
"widget": "MyWidget",
|
|
376
|
+
"x": 0,
|
|
377
|
+
"y": 0,
|
|
378
|
+
"columns": 1,
|
|
379
|
+
"rows": 1,
|
|
380
|
+
"title": "Custom Title",
|
|
381
|
+
"repository": "facebook/react"
|
|
382
|
+
}
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Styling
|
|
390
|
+
|
|
391
|
+
The UI library uses [Tailwind CSS](https://tailwindcss.com) with custom theme tokens:
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
// Use Tailwind utility classes
|
|
395
|
+
<div className="bg-background text-foreground p-4 rounded-lg border">
|
|
396
|
+
Content
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
// Use theme CSS variables
|
|
400
|
+
<div style={{ color: 'hsl(var(--primary))' }}>
|
|
401
|
+
Themed text
|
|
402
|
+
</div>
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Theme Variables:**
|
|
406
|
+
|
|
407
|
+
- `--background`, `--foreground`
|
|
408
|
+
- `--primary`, `--primary-foreground`
|
|
409
|
+
- `--secondary`, `--secondary-foreground`
|
|
410
|
+
- `--muted`, `--muted-foreground`
|
|
411
|
+
- `--accent`, `--accent-foreground`
|
|
412
|
+
- `--destructive`, `--destructive-foreground`
|
|
413
|
+
- `--border`, `--input`, `--ring`
|
|
414
|
+
- `--card`, `--card-foreground`
|
|
415
|
+
- `--popover`, `--popover-foreground`
|
|
416
|
+
|
|
417
|
+
## TypeScript Support
|
|
418
|
+
|
|
419
|
+
Fully typed with TypeScript:
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
import type { DashfyConfig, DashboardConfig, WidgetConfig } from '@getdashfy/ui'
|
|
423
|
+
|
|
424
|
+
const dashfyConfig: DashfyConfig = {
|
|
425
|
+
dashboards: [
|
|
426
|
+
/* ... */
|
|
427
|
+
],
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Development
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
# Install dependencies
|
|
435
|
+
pnpm install
|
|
436
|
+
|
|
437
|
+
# Build
|
|
438
|
+
pnpm build
|
|
439
|
+
|
|
440
|
+
# Watch mode
|
|
441
|
+
pnpm dev
|
|
442
|
+
|
|
443
|
+
# Run Storybook
|
|
444
|
+
pnpm storybook
|
|
445
|
+
|
|
446
|
+
# Run tests
|
|
447
|
+
pnpm test
|
|
448
|
+
|
|
449
|
+
# Type check
|
|
450
|
+
pnpm typecheck
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
## Community
|
|
454
|
+
|
|
455
|
+
Join the community on [Dashfy's Discord server](https://dashfy.dev/discord) to discuss the project, ask questions, or get help.
|
|
456
|
+
|
|
457
|
+
Join the conversation on X (Twitter) and follow [@dashfydev](https://x.com/dashfydev) for updates and announcements.
|
|
458
|
+
|
|
459
|
+
## License
|
|
460
|
+
|
|
461
|
+
This project is licensed under the AGPL-3.0 License - see the [LICENSE](./LICENSE) file for details.
|