@gravity-ui/dashkit 10.3.0 → 10.4.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 CHANGED
@@ -629,5 +629,36 @@ server {
629
629
  proxy_redirect off;
630
630
  }
631
631
  }
632
-
633
632
  ```
633
+
634
+ ## License
635
+
636
+ Distributed under the MIT License. See [LICENSE](LICENSE) for details.
637
+
638
+ ## For AI agents
639
+
640
+ A dashboard grid composer that arranges resizable, draggable widgets in a responsive grid via a plugin system — reach for it when you build a user-editable dashboard (add/move/resize/delete widgets) instead of placing individual charts or panels by hand.
641
+
642
+ ### When to use
643
+
644
+ - Rendering a configurable dashboard where widgets are positioned, resized, and rearranged on a grid (built on `react-grid-layout`).
645
+ - User-editable layouts: adding/removing widgets from an action panel, drag-and-drop, edit mode with overlay controls.
646
+ - Plugin-based widgets where each widget type (title, text, chart, custom) is registered once and driven by a `config`.
647
+
648
+ ### When not to use
649
+
650
+ - For a single, fixed chart or panel, use [`@gravity-ui/charts`](https://gravity-ui.com/charts) or [`@gravity-ui/chartkit`](https://github.com/gravity-ui/chartkit) directly — the grid/plugin machinery is overhead for one widget.
651
+ - For a general-purpose responsive grid that is not a widget dashboard, use `react-grid-layout` directly.
652
+ - For embedding ChartKit-backed chart widgets inside a DashKit dashboard, DashKit is the shell; it still relies on [`@gravity-ui/chartkit`](https://github.com/gravity-ui/chartkit) to render the actual charts.
653
+
654
+ ### Common pitfalls
655
+
656
+ - **Hallucinated component `<Dashboard>`** — the export is `<DashKit>` (the drag-and-drop shell is `<DashKitDnDWrapper>` wrapping `<DashKit>` + `<ActionPanel>`).
657
+ - **Mutating `config` instead of using helpers** — use the static `DashKit.setItem({...})` / `DashKit.removeItem({...})` helpers to add/change/remove items so layout and ids stay consistent.
658
+ - **Forgetting `DashKit.setSettings` / `DashKit.registerPlugins`** — the component must be configured (language, grid settings, plugin registration) before it is rendered, or widgets show nothing.
659
+ - **Confusing the two param props** — `defaultGlobalParams` (dashboard-level defaults) vs `globalParams` (URL-overridable globals); both flow into the params generation queue consumed by ChartKit.
660
+ - **Calling `onChange` manually with the `change` event** — when you `event.preventDefault()` in the experimental `change` handler, DashKit keeps the visual state internally; re-setting `config.layout` from props resets that baseline.
661
+
662
+ ## Documentation for AI agents
663
+
664
+ Agent-readable documentation for the installed version is located in `node_modules/@gravity-ui/dashkit/build/docs/INDEX.md`.
@@ -1 +1 @@
1
- {"version":"10.3.0","type":"commonjs","sideEffects":["*.scss","*.css"]}
1
+ {"version":"10.4.0","type":"commonjs","sideEffects":["*.scss","*.css"]}
@@ -0,0 +1,534 @@
1
+ # @gravity-ui/dashkit documentation
2
+
3
+ Documentation for the **installed** version of `@gravity-ui/dashkit`.
4
+ Your training data may be outdated — these files are the source of truth.
5
+
6
+ Paths are relative to this file (`node_modules/@gravity-ui/dashkit/build/docs/`).
7
+
8
+ ## For AI agents
9
+
10
+ A dashboard grid composer that arranges resizable, draggable widgets in a responsive grid via a plugin system — reach for it when you build a user-editable dashboard (add/move/resize/delete widgets) instead of placing individual charts or panels by hand.
11
+
12
+ ### When to use
13
+
14
+ - Rendering a configurable dashboard where widgets are positioned, resized, and rearranged on a grid (built on `react-grid-layout`).
15
+ - User-editable layouts: adding/removing widgets from an action panel, drag-and-drop, edit mode with overlay controls.
16
+ - Plugin-based widgets where each widget type (title, text, chart, custom) is registered once and driven by a `config`.
17
+
18
+ ### When not to use
19
+
20
+ - For a single, fixed chart or panel, use [`@gravity-ui/charts`](https://gravity-ui.com/charts) or [`@gravity-ui/chartkit`](https://github.com/gravity-ui/chartkit) directly — the grid/plugin machinery is overhead for one widget.
21
+ - For a general-purpose responsive grid that is not a widget dashboard, use `react-grid-layout` directly.
22
+ - For embedding ChartKit-backed chart widgets inside a DashKit dashboard, DashKit is the shell; it still relies on [`@gravity-ui/chartkit`](https://github.com/gravity-ui/chartkit) to render the actual charts.
23
+
24
+ ### Common pitfalls
25
+
26
+ - **Hallucinated component `<Dashboard>`** — the export is `<DashKit>` (the drag-and-drop shell is `<DashKitDnDWrapper>` wrapping `<DashKit>` + `<ActionPanel>`).
27
+ - **Mutating `config` instead of using helpers** — use the static `DashKit.setItem({...})` / `DashKit.removeItem({...})` helpers to add/change/remove items so layout and ids stay consistent.
28
+ - **Forgetting `DashKit.setSettings` / `DashKit.registerPlugins`** — the component must be configured (language, grid settings, plugin registration) before it is rendered, or widgets show nothing.
29
+ - **Confusing the two param props** — `defaultGlobalParams` (dashboard-level defaults) vs `globalParams` (URL-overridable globals); both flow into the params generation queue consumed by ChartKit.
30
+ - **Calling `onChange` manually with the `change` event** — when you `event.preventDefault()` in the experimental `change` handler, DashKit keeps the visual state internally; re-setting `config.layout` from props resets that baseline.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ npm i @gravity-ui/dashkit @gravity-ui/uikit
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ### DashKit configuration
41
+
42
+ Before using `DashKit` as a react component, it must be configured.
43
+
44
+ - set language
45
+
46
+ ```js
47
+ import {configure, Lang} from '@gravity-ui/uikit';
48
+
49
+ configure({lang: Lang.En});
50
+ ```
51
+
52
+ - DashKit.setSettings
53
+
54
+ Used for global DashKit settings (such as margins between widgets, default widget sizes and widget overlay menu)
55
+
56
+ ```js
57
+ import {DashKit} from '@gravity-ui/dashkit';
58
+
59
+ DashKit.setSettings({
60
+ gridLayout: {margin: [8, 8]},
61
+ isMobile: true,
62
+ // menu: [] as Array<MenuItem>,
63
+ });
64
+ ```
65
+
66
+ - DashKit.registerPlugins
67
+
68
+ Registering and configuring plugins
69
+
70
+ ```js
71
+ import {DashKit} from '@gravity-ui/dashkit';
72
+ import {pluginTitle, pluginText} from '@gravity-ui/dashkit';
73
+
74
+ DashKit.registerPlugins(
75
+ pluginTitle,
76
+ pluginText.setSettings({
77
+ apiHandler({text}) {
78
+ return api.getMarkdown(text);
79
+ },
80
+ }),
81
+ );
82
+
83
+ DashKit.registerPlugins({
84
+ type: 'custom',
85
+ defaultLayout: {
86
+ w: 10,
87
+ h: 8,
88
+ },
89
+ renderer: function CustomPlugin() {
90
+ return <div>Custom widget with custom controls</div>;
91
+ },
92
+ });
93
+ ```
94
+
95
+ ### Config
96
+
97
+ ```ts
98
+ export interface Config {
99
+ salt: string; // to form a unique id
100
+ counter: number; // to form a unique id, only increases
101
+ items: ConfigItem[]; // initial widget states
102
+ layout: ConfigLayout[]; // widget position on the grid https://github.com/react-grid-layout
103
+ aliases: ConfigAliases; // aliases for parameters see #Params
104
+ connections: ConfigConnection[]; // links between widgets see #Params
105
+ }
106
+ ```
107
+
108
+ Config example:
109
+
110
+ ```ts
111
+ import {DashKitProps} from '@gravity-ui/dashkit';
112
+
113
+ const config: DashKitProps['config'] = {
114
+ salt: '0.46703554571365613',
115
+ counter: 4,
116
+ items: [
117
+ {
118
+ id: 'tT',
119
+ data: {
120
+ size: 'm',
121
+ text: 'Caption',
122
+ showInTOC: true,
123
+ },
124
+ type: 'title',
125
+ namespace: 'default',
126
+ orderId: 1,
127
+ },
128
+ {
129
+ id: 'Ea',
130
+ data: {
131
+ text: 'mode _editActive',
132
+ _editActive: true,
133
+ },
134
+ type: 'text',
135
+ namespace: 'default',
136
+ },
137
+ {
138
+ id: 'zR',
139
+ data: {
140
+ text: '### Text',
141
+ },
142
+ type: 'text',
143
+ namespace: 'default',
144
+ orderId: 0,
145
+ },
146
+ {
147
+ id: 'Dk',
148
+ data: {
149
+ foo: 'bar',
150
+ },
151
+ type: 'custom',
152
+ namespace: 'default',
153
+ orderId: 5,
154
+ },
155
+ ],
156
+ layout: [
157
+ {
158
+ h: 2,
159
+ i: 'tT',
160
+ w: 36,
161
+ x: 0,
162
+ y: 0,
163
+ },
164
+ {
165
+ h: 6,
166
+ i: 'Ea',
167
+ w: 12,
168
+ x: 0,
169
+ y: 2,
170
+ },
171
+ {
172
+ h: 6,
173
+ i: 'zR',
174
+ w: 12,
175
+ x: 12,
176
+ y: 2,
177
+ },
178
+ {
179
+ h: 4,
180
+ i: 'Dk',
181
+ w: 8,
182
+ x: 0,
183
+ y: 8,
184
+ },
185
+ ],
186
+ aliases: {},
187
+ connections: [],
188
+ };
189
+ ```
190
+
191
+ Add a new item to the config:
192
+
193
+ ```ts
194
+ const newLayout = updateLayout: [
195
+ {
196
+ h: 6,
197
+ i: 'Ea',
198
+ w: 12,
199
+ x: 0,
200
+ y: 6,
201
+ },
202
+ {
203
+ h: 4,
204
+ i: 'Dk',
205
+ w: 8,
206
+ x: 0,
207
+ y: 12,
208
+ },
209
+ ];
210
+
211
+ const newConfig = DashKit.setItem({
212
+ item: {
213
+ data: {
214
+ text: `Some text`,
215
+ },
216
+ namespace: 'default',
217
+ type: 'text',
218
+ // Optional. If new item needed to be inserted in current layout with predefined dimensions
219
+ layout: { // Current item inseterted before 'Ea'
220
+ h: 6,
221
+ w: 12,
222
+ x: 0,
223
+ y: 2,
224
+ },,
225
+ },
226
+ config: config,
227
+ options: {
228
+ // Optional. New layout values for existing items when new element is dropped from ActionPanel
229
+ updateLayout: newLayout,
230
+ },
231
+ });
232
+ ```
233
+
234
+ Change an existing item in the config:
235
+
236
+ ```ts
237
+ const newConfig = DashKit.setItem({
238
+ item: {
239
+ id: 'tT', // item.id
240
+ data: {
241
+ size: 'm',
242
+ text: `New caption`,
243
+ },
244
+ namespace: 'default',
245
+ type: 'title',
246
+ },
247
+ config: config,
248
+ });
249
+ ```
250
+
251
+ Delete an item from the config:
252
+
253
+ ```ts
254
+ import {DashKitProps} from '@gravity-ui/dashkit';
255
+
256
+ const oldItemsStateAndParams: DashKitProps['itemsStateAndParams'] = {};
257
+
258
+ const {config: newConfig, itemsStateAndParams} = DashKit.removeItem({
259
+ id: 'tT', // item.id
260
+ config: config,
261
+ itemsStateAndParams: this.state.itemsStateAndParams,
262
+ });
263
+ ```
264
+
265
+ ### Params
266
+
267
+ ```ts
268
+ type Params = Record<string, string | string[]>;
269
+ ```
270
+
271
+ `DashKit` generates parameters according to the default parameters for widgets, links, and aliases. These parameters are required for the [ChartKit](https://github.com/gravity-ui/chartkit) library.
272
+
273
+ Generation order:
274
+
275
+ 1. `defaultGlobalParams`
276
+ 2. Default widget parameters `item.default`
277
+ 3. `globalParams`
278
+ 4. Parameters from [itemsStateAndParams](#itemsStateAndParams) according to the queue.
279
+
280
+ ### itemsStateAndParams
281
+
282
+ Object that stores widget parameters and states as well as a parameter change queue.
283
+ It has a `__meta__` field for storing queue and meta information.
284
+
285
+ ```ts
286
+ interface StateAndParamsMeta = {
287
+ __meta__: {
288
+ queue: {id: string}[]; // queue
289
+ version: number; // current version itemsStateAndParams
290
+ };
291
+ }
292
+ ```
293
+
294
+ And also widget states and parameters:
295
+
296
+ ```ts
297
+ interface ItemsStateAndParamsBase {
298
+ [itemId: string]: {
299
+ state?: Record<string, any>;
300
+ params?: Params;
301
+ };
302
+ }
303
+ ```
304
+
305
+ ```ts
306
+ type ItemsStateAndParams = StateAndParamsMeta & ItemsStateAndParamsBase;
307
+ ```
308
+
309
+ ### Experimental DashKit events
310
+
311
+ > Experimental: this API can change in minor releases.
312
+
313
+ `DashKit` exposes an experimental instance event API. Use a component ref and subscribe with `dashkitRef.current?.on(eventName, handler)`. The method returns an unsubscribe callback.
314
+
315
+ The first supported event is `change`. It is emitted when the layout changes, before `onChange` is called. The handler can read the full next and previous layouts, read layout patches, or call `preventDefault()` to stop the default `onChange` call.
316
+
317
+ ```tsx
318
+ import React from 'react';
319
+ import {DashKit} from '@gravity-ui/dashkit';
320
+ import type {DashKitChangeEvent} from '@gravity-ui/dashkit';
321
+
322
+ function Dashboard() {
323
+ const dashkitRef = React.useRef<DashKit>(null);
324
+
325
+ React.useEffect(() => {
326
+ const unsubscribe = dashkitRef.current?.on('change', (event: DashKitChangeEvent) => {
327
+ console.log(event.patches);
328
+
329
+ if (event.patches.length > 0) {
330
+ event.preventDefault();
331
+ }
332
+ });
333
+
334
+ return () => unsubscribe?.();
335
+ }, []);
336
+
337
+ return <DashKit ref={dashkitRef} config={config} editMode={true} onChange={onChange} />;
338
+ }
339
+ ```
340
+
341
+ ```ts
342
+ type DashKitLayoutPatch = Pick<ConfigLayout, 'i'> &
343
+ Partial<Pick<ConfigLayout, 'x' | 'y' | 'w' | 'h' | 'parent'>>;
344
+
345
+ type DashKitChangeEvent = {
346
+ patches: DashKitLayoutPatch[];
347
+ layout: ConfigLayout[];
348
+ previousLayout: ConfigLayout[];
349
+ preventDefault: () => void;
350
+ readonly defaultPrevented: boolean;
351
+ };
352
+ ```
353
+
354
+ #### Event-driven layout updates
355
+
356
+ If you use `preventDefault()` in the `change` event handler, you can now handle layout updates without re-initializing the config prop. DashKit maintains an internal baseline and computes patches incrementally:
357
+
358
+ ```tsx
359
+ function Dashboard() {
360
+ const [config, setConfig] = useState(initialConfig);
361
+ const dashkitRef = useRef<DashKit>(null);
362
+
363
+ useEffect(() => {
364
+ const unsubscribe = dashkitRef.current?.on('change', (event) => {
365
+ event.preventDefault(); // Don't call onChange
366
+
367
+ // Send only the incremental patches to your backend
368
+ sendPatches(event.patches);
369
+
370
+ // No need to call setConfig({ ...config, layout: event.layout })
371
+ // DashKit maintains the visual state internally
372
+ });
373
+
374
+ return unsubscribe;
375
+ }, []);
376
+
377
+ return <DashKit ref={dashkitRef} config={config} editMode onChange={() => {}} />;
378
+ }
379
+ ```
380
+
381
+ **Important:** If you later update `config.layout` from props (e.g., from server sync), DashKit will reset its internal baseline to match the new prop. This ensures compatibility with both event-driven and controlled workflows.
382
+
383
+ ### Menu
384
+
385
+ You can specify custom DashKit widget overlay menu in edit mode
386
+
387
+ ```ts
388
+ type MenuItem = {
389
+ id: string; // uniq id
390
+ title?: string; // string title
391
+ icon?: ReactNode; // node of icon
392
+ iconSize?: number | string; // icon size in px as number or as string with units
393
+ handler?: (item: ConfigItem) => void; // custom item action handler
394
+ visible?: (item: ConfigItem) => boolean; // optional visibility handler for filtering menu items
395
+ className?: string; // custom class property
396
+ };
397
+
398
+ // use array of menu items in settings
399
+ <Dashkit overlayMenuItems={[] as Array<MenuItem> | null} />
400
+
401
+ [deprecated]
402
+ // overlayMenuItems property has greater priority over setSettings menu
403
+ DashKit.setSettings({menu: [] as Array<MenuItem>});
404
+ ```
405
+
406
+ ### Draggable items from ActionPanel
407
+
408
+ #### DashKitDnDWrapper
409
+
410
+ ```ts
411
+ type DraggedOverItem = {
412
+ h: number;
413
+ w: number;
414
+ type: string;
415
+ parent: string;
416
+ i?: number;
417
+ };
418
+
419
+ interface DashKitDnDWrapperProps {
420
+ dragImageSrc?: string;
421
+ onDragStart?: (dragProps: ItemDragProps) => void;
422
+ onDragEnd?: () => void;
423
+ onDropDragOver?: (
424
+ draggedItem: DraggedOverItem,
425
+ sharedItem: DraggedOverItem | null,
426
+ ) => void | boolean;
427
+ }
428
+ ```
429
+
430
+ - **dragImageSrc**: Drag image preview, by default used transparent 1px png base64
431
+ - **onDragStart**: Callback called when element is dragged from ActionPanel
432
+ - **onDragEnd**: Callback called when element dropped or drag canceled
433
+
434
+ ```ts
435
+ type ItemDragProps = {
436
+ type: string; // Plugin type
437
+ layout?: {
438
+ // Optional. Layout item size for preview and init
439
+ w?: number;
440
+ h?: number;
441
+ };
442
+ extra?: any; // Custom user context
443
+ };
444
+ ```
445
+
446
+ ```ts
447
+ type ItemDropProps = {
448
+ commit: () => void; // Callback should be called after all config operations are made
449
+ dragProps: ItemDragProps; // Item drag props
450
+ itemLayout: ConfigLayout; // Calculated item layout dimensions
451
+ newLayout: ConfigLayout[]; // New layout after element is dropped
452
+ };
453
+ ```
454
+
455
+ #### Example:
456
+
457
+ ```jsx
458
+ const overlayMenuItems = [
459
+ {
460
+ id: 'chart',
461
+ icon: <Icon data={ChartColumn} />,
462
+ title: 'Chart',
463
+ qa: 'chart',
464
+ dragProps: { // ItemDragProps
465
+ type: 'custom', // Registered plugin type
466
+ },
467
+ }
468
+ ]
469
+
470
+ const onDrop = (dropProps: ItemDropProps) => {
471
+ // ... add element to your config
472
+ dropProps.commit();
473
+ }
474
+
475
+ <DashKitDnDWrapper>
476
+ <DashKit editMode={true} config={config} onChange={onChange} onDrop={onDrop} />
477
+ <ActionPanel items={overlayMenuItems} />
478
+ </DashKitDnDWrapper>
479
+ ```
480
+
481
+ ### CSS API
482
+
483
+ | Name | Description |
484
+ | :--------------------------------------------- | :-------------------- |
485
+ | Action panel variables | |
486
+ | `--dashkit-action-panel-color` | Background color |
487
+ | `--dashkit-action-panel-border-color` | Border color |
488
+ | `--dashkit-action-panel-border-radius` | Border radius |
489
+ | Action panel item variables | |
490
+ | `--dashkit-action-panel-item-color` | Backgroud color |
491
+ | `--dashkit-action-panel-item-text-color` | Text color |
492
+ | `--dashkit-action-panel-item-color-hover` | Hover backgroud color |
493
+ | `--dashkit-action-panel-item-text-color-hover` | Hover text color |
494
+ | Overlay variables | |
495
+ | `--dashkit-overlay-border-color` | Border color |
496
+ | `--dashkit-overlay-color` | Background color |
497
+ | `--dashkit-overlay-opacity` | Opacity |
498
+ | Grid item variables | |
499
+ | `--dashkit-grid-item-edit-opacity` | Opacity |
500
+ | `--dashkit-grid-item-border-radius` | Border radius |
501
+ | Placeholder variables | |
502
+ | `--dashkit-placeholder-color` | Background color |
503
+ | `--dashkit-placeholder-opacity` | Opacity |
504
+
505
+ #### Usage example
506
+
507
+ ```css
508
+ .custom-theme-wrapper {
509
+ --dashkit-grid-item-edit-opacit: 1;
510
+ --dashkit-overlay-color: var(--g-color-base-float);
511
+ --dashkit-overlay-border-color: var(--g-color-base-float);
512
+ --dashkit-overlay-opacity: 0.5;
513
+
514
+ --dashkit-action-panel-border-color: var(--g-color-line-info);
515
+ --dashkit-action-panel-color: var(--g-color-base-float-accent);
516
+ --dashkit-action-panel-border-radius: var(--g-border-radius-xxl);
517
+ }
518
+ ```
519
+
520
+ ```tsx
521
+ // ....
522
+
523
+ const CustomThemeWrapper = (props: {
524
+ dashkitProps: DashkitProps;
525
+ actionPanelProps: ActionPanelProps;
526
+ }) => {
527
+ return (
528
+ <div className="custom-theme-wrapper">
529
+ <Dashkit {...props.dashkitProps} />
530
+ <ActionPanel {...props.actionPanelProps} />
531
+ </div>
532
+ );
533
+ };
534
+ ```
@@ -1 +1 @@
1
- {"version":"10.3.0","type":"module","sideEffects":["*.scss","*.css"]}
1
+ {"version":"10.4.0","type":"module","sideEffects":["*.scss","*.css"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dashkit",
3
- "version": "10.3.0",
3
+ "version": "10.4.0",
4
4
  "description": "Library for rendering dashboard grid layout",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -63,6 +63,7 @@
63
63
  "test": "jest",
64
64
  "clean": "gulp clean",
65
65
  "build": "gulp",
66
+ "build:docs": "gulp copy-docs",
66
67
  "build:watch": "gulp watch",
67
68
  "start": "storybook dev -p 7120",
68
69
  "typecheck": "tsc --noEmit",
@@ -91,6 +92,7 @@
91
92
  "@gravity-ui/gulp-utils": "^1.0.2",
92
93
  "@gravity-ui/icons": "^2.13.0",
93
94
  "@gravity-ui/prettier-config": "^1.1.0",
95
+ "@gravity-ui/readme-validator": "^1.3.0",
94
96
  "@gravity-ui/stylelint-config": "^4.0.1",
95
97
  "@gravity-ui/tsconfig": "^1.0.0",
96
98
  "@gravity-ui/uikit": "^7.2.0",