@dash-builder/react 0.0.0-stage → 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 ADDED
@@ -0,0 +1,7 @@
1
+ # @dash-builder/react
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 37f3acf: First public release: a dashboard grid where widgets flow into a fixed number of columns and can be dragged and resized, with four drop modes (insert, block, shrink, swap), drag handles, locked widgets, a row limit and a narrow (stacked) view. `@dash-builder/core` works on its own; `@dash-builder/angular` and `@dash-builder/react` wrap it.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dash-builder contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,142 @@
1
- # Temporary Holding Version
1
+ # @dash-builder/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ React bindings for [`@dash-builder/core`](https://www.npmjs.com/package/@dash-builder/core), a dashboard grid where widgets flow left to right into a fixed number of columns and can be dragged and resized. The layout is an ordered list: array order is position, there are no x/y coordinates.
4
+
5
+ Requires React 19. Safe under `<StrictMode>` and for server rendering: the grid touches the DOM only in effects, and React does not re-render while you drag.
6
+
7
+ ## Install
8
+
9
+ `react`, `react-dom` and `@dash-builder/core` are peer dependencies, so install them together:
10
+
11
+ ```sh
12
+ pnpm add @dash-builder/react @dash-builder/core react react-dom
13
+ # or: npm install @dash-builder/react @dash-builder/core react react-dom
14
+ ```
15
+
16
+ ## Styles
17
+
18
+ Import the grid stylesheet once, in your entry file:
19
+
20
+ ```ts
21
+ import '@dash-builder/core/grid.css';
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ import { useState } from 'react';
28
+ import { Grid, Widget, type LayoutItem } from '@dash-builder/react';
29
+ import '@dash-builder/core/grid.css';
30
+
31
+ export function Dashboard() {
32
+ const [layout, setLayout] = useState<LayoutItem[]>([
33
+ { id: 'a', w: 2, h: 2 },
34
+ { id: 'b', w: 1, h: 1 },
35
+ { id: 'c', w: 1, h: 1, locked: true },
36
+ ]);
37
+
38
+ return (
39
+ <Grid columns={4} dropMode="insert" layout={layout} onLayoutChange={setLayout}>
40
+ {layout.map((i) => (
41
+ <Widget key={i.id} id={i.id}>
42
+ <Widget.Handle>{i.id}</Widget.Handle>
43
+ <p>Widget content</p>
44
+ </Widget>
45
+ ))}
46
+ </Grid>
47
+ );
48
+ }
49
+ ```
50
+
51
+ - `<Grid>` renders the grid container (a `div` with the class `dash-grid` and `display: grid`). Each `<Widget id>` is a widget; the id must match a layout entry.
52
+ - `Widget.Handle` marks the only element a widget can be dragged by. Without one, the whole widget is the drag surface.
53
+
54
+ ### Rules
55
+
56
+ - **The layout is controlled.** You own `layout`; update it from `onLayoutChange`. The grid never keeps its own copy. Changing `layout` from outside never calls `onLayoutChange`.
57
+ - **Widgets must be `<Widget>` elements that are direct DOM children of the grid.** A wrapper component is fine as long as it renders the `Widget` element itself (`function Card(p) { return <Widget id={p.id}>...</Widget>; }`), including one that shows, hides or re-ids its widget from its own state, or a `<Suspense>` boundary around widgets: every `Widget` tells the grid when it commits or unmounts, so the grid picks it up without re-rendering. An extra wrapper `<div>` between the grid and the widgets is not fine: those widgets are not found. A plain `<div data-grid-id>` is not a supported widget either.
58
+ - **Key widgets by id** (`key={item.id}`). The grid emits copies of the items, so keying by object identity or by index would remount widgets after every drop.
59
+ - **Interactive controls don't start drags.** In a widget without a handle, `input`, `textarea`, `select`, `button`, `option`, `label`, `a[href]`, `contenteditable` elements and anything inside `[data-grid-no-drag]` ignore pointer-down for dragging. For a custom control (a canvas, a map), add `data-grid-no-drag` to it, or give the widget a `Widget.Handle`. A widget root that is itself a link, label or button can still be dragged; put `data-grid-no-drag` on the root to opt out.
60
+ - **Locked widgets** (`locked: true`) can't be dragged or resized and keep their index.
61
+
62
+ ## API
63
+
64
+ ### `<Grid>`
65
+
66
+ Extra props (`className`, `style`, `data-*`, ...) go to the container `div`. `className` is appended to `dash-grid`; `display: grid` is always applied, so `<Grid hidden>` stays visible: wrap the grid in an element you hide, or hide it with CSS (`display: none !important`).
67
+
68
+ | Prop | Type | Default | Description |
69
+ | --- | --- | --- | --- |
70
+ | `layout` | `LayoutItem[]` | required | The layout, owned by the parent. |
71
+ | `columns` | `number` | required | Number of columns. |
72
+ | `onLayoutChange` | `(layout: LayoutItem[]) => void` | none | A drop or resize is released, `addItem` / `removeItem` succeed, or a `columns` change normalizes widths. |
73
+ | `dropMode` | `'insert' \| 'block' \| 'shrink' \| 'swap'` | `'insert'` | Changeable at runtime. |
74
+ | `maxRows` | `number` | none | Hard row limit for drops, resizes and `addItem`. |
75
+ | `rowHeight` | `number` | `80` | Row height in px. |
76
+ | `gap` | `number` | `8` | Gap between cells in px. |
77
+ | `breakpoint` | `number` | `640` | Container width in px below which the narrow view is used. |
78
+ | `ghost` | `(el: HTMLElement) => HTMLElement` | sanitized clone | Builds the drag ghost (see the core README). |
79
+ | `onReject` | `(reason: RejectReason) => void` | none | An `addItem` is refused (`'max-rows'`, `'duplicate'` or `'invalid'`). |
80
+ | `ref` | `Ref<GridHandle>` | none | Imperative `addItem` / `removeItem`, see below. |
81
+
82
+ ### `<Widget>` and `Widget.Handle`
83
+
84
+ `<Widget id>` renders a `div` with the class `dash-item` and `data-grid-id={id}` (not a DOM `id`); `Widget.Handle` renders a `span` with `data-grid-handle`. Other props go to the element, but the grid attributes always win: `className` is appended to `dash-item`.
85
+
86
+ - Changing a widget's `className` while it is being dragged drops the core's drag-state classes (`dash-item--placeholder`, `dash-item--swap-target`, `dash-item--swap-blocked`) until the pointer moves again (swap target) or the next drag (placeholder). The drag and its drop are not affected.
87
+ - `dangerouslySetInnerHTML` on a `Widget` is not supported: the grid adds its own resize handle inside the widget, and React's inner HTML replaces the widget's whole content. Set it on an element inside the widget instead.
88
+
89
+ ### Adding widgets: use the ref
90
+
91
+ ```tsx
92
+ import { useRef, useState } from 'react';
93
+ import { Grid, Widget, type GridHandle, type LayoutItem } from '@dash-builder/react';
94
+
95
+ export function Dashboard() {
96
+ const grid = useRef<GridHandle>(null);
97
+ const [layout, setLayout] = useState<LayoutItem[]>([]);
98
+
99
+ function add() {
100
+ const result = grid.current!.addItem({ id: crypto.randomUUID(), w: 1, h: 1 });
101
+ if (!result.ok) console.warn('not added:', result.reason);
102
+ }
103
+
104
+ return (
105
+ <>
106
+ <button type="button" onClick={add}>
107
+ Add
108
+ </button>
109
+ <Grid ref={grid} columns={4} layout={layout} onLayoutChange={setLayout}>
110
+ {layout.map((i) => (
111
+ <Widget key={i.id} id={i.id}>
112
+ {i.id}
113
+ </Widget>
114
+ ))}
115
+ </Grid>
116
+ </>
117
+ );
118
+ }
119
+ ```
120
+
121
+ | Method | Description |
122
+ | --- | --- |
123
+ | `addItem(item: LayoutItem, index?: number): DropResult` | Inserts at `index` (default: append). On success calls `onLayoutChange` with the new layout; otherwise calls `onReject` and returns `{ ok: false, reason }`. Consecutive calls in one handler build on each other. |
124
+ | `removeItem(id: string): void` | Removes the item and calls `onLayoutChange`; an unknown id does nothing. |
125
+
126
+ Prefer `addItem` over `setLayout([...layout, item])`: it enforces `maxRows`, refuses duplicate ids and reports the refusal through `onReject`. A plain `setLayout` bypasses all of that, because the grid shows whatever layout it is given.
127
+
128
+ ## Narrow view
129
+
130
+ When the grid is narrower than `breakpoint` (default 640 px), widgets stack in a single column in list order and resizing is disabled. Dragging still reorders them, but only temporarily: a narrow-view reorder never calls `onLayoutChange`, so your layout is untouched. Set `breakpoint={0}` to turn the narrow view off. Near the edges of a scroll container, dragging autoscrolls.
131
+
132
+ ## Drop modes
133
+
134
+ `insert`, `block`, `shrink` and `swap` are described in the [core README](https://www.npmjs.com/package/@dash-builder/core#drop-modes), together with locked widgets, `maxRows` and the layout JSON format.
135
+
136
+ ## Demo
137
+
138
+ From the repository root: `pnpm --filter @dash-builder/core build && pnpm --filter @dash-builder/react demo`.
139
+
140
+ ## License
141
+
142
+ MIT
@@ -0,0 +1,8 @@
1
+ export interface GridContextValue {
2
+ /**
3
+ * Asks the grid to rescan and re-render its widgets. Calls coalesce into one refresh;
4
+ * outside a `<Grid>` (or during server rendering) this does nothing.
5
+ */
6
+ requestRefresh(): void;
7
+ }
8
+ export declare const GridContext: import("react").Context<GridContextValue>;
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'react';
2
+ export const GridContext = createContext({ requestRefresh() { } });
package/dist/grid.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode, Ref } from 'react';
2
+ import type { DropMode, DropResult, GridOptions, LayoutItem, RejectReason } from '@dash-builder/core';
3
+ export interface GridProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
4
+ columns: number;
5
+ /** The layout, owned by the parent: update it from `onLayoutChange`. */
6
+ layout: LayoutItem[];
7
+ onLayoutChange?: (layout: LayoutItem[]) => void;
8
+ /** Default `'insert'`. */
9
+ dropMode?: DropMode;
10
+ maxRows?: number;
11
+ /** Default 80 (px). */
12
+ rowHeight?: number;
13
+ /** Default 8 (px). */
14
+ gap?: number;
15
+ /** Below this container width the grid stacks in one column. Default 640 (px). */
16
+ breakpoint?: number;
17
+ ghost?: GridOptions['ghost'];
18
+ onReject?: (reason: RejectReason) => void;
19
+ children?: ReactNode;
20
+ ref?: Ref<GridHandle>;
21
+ }
22
+ export interface GridHandle {
23
+ /**
24
+ * Adds `item` at `index` (default: append), enforcing `maxRows`. On success
25
+ * `onLayoutChange` is called with the new layout; otherwise `onReject` gets the reason.
26
+ */
27
+ addItem(item: LayoutItem, index?: number): DropResult;
28
+ /** Removes the item with `id`; an unknown id changes nothing and calls nothing. */
29
+ removeItem(id: string): void;
30
+ }
31
+ /**
32
+ * A dashboard grid around the `@dash-builder/core` controller. The rendered div is the
33
+ * grid container; its direct `<Widget>` children are the widgets. Controlled: the
34
+ * parent owns `layout`.
35
+ */
36
+ export declare function Grid(props: GridProps): import("react").JSX.Element;
package/dist/grid.js ADDED
@@ -0,0 +1,148 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useImperativeHandle, useLayoutEffect, useRef, useState } from 'react';
3
+ import { addItem as planAdd, createGrid, normalizeLayout, removeItem as planRemove } from '@dash-builder/core';
4
+ import { GridContext } from './context.js';
5
+ function shallowEqual(a, b) {
6
+ if (a === b)
7
+ return true;
8
+ if (!a || !b)
9
+ return false;
10
+ const ka = Object.keys(a);
11
+ return ka.length === Object.keys(b).length && ka.every((k) => Object.is(a[k], b[k]));
12
+ }
13
+ /**
14
+ * A dashboard grid around the `@dash-builder/core` controller. The rendered div is the
15
+ * grid container; its direct `<Widget>` children are the widgets. Controlled: the
16
+ * parent owns `layout`.
17
+ */
18
+ export function Grid(props) {
19
+ const { columns, layout, onLayoutChange, dropMode = 'insert', maxRows, rowHeight = 80, gap = 8, breakpoint = 640, ghost, onReject, children, ref, className, style, ...divProps } = props;
20
+ const opts = { columns, dropMode, maxRows, rowHeight, gap, breakpoint, ghost };
21
+ const container = useRef(null);
22
+ const controller = useRef(undefined);
23
+ /** The layout last handed to the controller. */
24
+ const applied = useRef(undefined);
25
+ /** Set while setOptions runs before a new layout replaces the current one. */
26
+ const superseded = useRef(false);
27
+ /** A widget committed (or unmounted) since the controller last rendered. */
28
+ const dirty = useRef(false);
29
+ /** The className and style the container was last rendered with. */
30
+ const lastLook = useRef({ className, style });
31
+ /**
32
+ * The newest committed props, so callbacks never go stale and never re-create the
33
+ * controller. addItem/removeItem also advance `layout` to what they emitted, so calls
34
+ * chained before the parent re-renders compose; the next commit overwrites it.
35
+ */
36
+ const latest = useRef({ opts, layout, onLayoutChange, onReject });
37
+ // Widgets call this after each of their commits and when they unmount. When the Grid
38
+ // commits too, its sync effect below (which runs after its children's effects) refreshes
39
+ // at once; otherwise (a wrapper's own state, a Suspense boundary resolving) the queued
40
+ // microtask does, before the browser paints. Many requests make one refresh. Once the
41
+ // controller is destroyed (unmount), a queued refresh finds none and does nothing.
42
+ const [context] = useState(() => {
43
+ let queued = false;
44
+ return {
45
+ requestRefresh() {
46
+ dirty.current = true;
47
+ if (queued)
48
+ return;
49
+ queued = true;
50
+ queueMicrotask(() => {
51
+ queued = false;
52
+ if (!dirty.current || !controller.current)
53
+ return;
54
+ dirty.current = false;
55
+ controller.current.refresh();
56
+ });
57
+ },
58
+ };
59
+ });
60
+ // Declared first, so the effects below and anything after the commit see these props.
61
+ useLayoutEffect(() => {
62
+ latest.current = { opts, layout, onLayoutChange, onReject };
63
+ });
64
+ useLayoutEffect(() => {
65
+ const el = container.current;
66
+ const { opts: o, layout: l } = latest.current;
67
+ applied.current = l;
68
+ controller.current = createGrid(el, {
69
+ ...o,
70
+ layout: l,
71
+ onLayoutChange: (next) => {
72
+ if (!superseded.current)
73
+ latest.current.onLayoutChange?.(next);
74
+ },
75
+ onReject: (r) => latest.current.onReject?.(r),
76
+ });
77
+ dirty.current = false; // createGrid has bound and placed the widgets
78
+ return () => {
79
+ controller.current?.destroy();
80
+ controller.current = undefined;
81
+ };
82
+ }, []);
83
+ // After every commit: apply options and layout, then refresh if a widget committed or the
84
+ // container was restyled. The core skips equal options and layouts.
85
+ useLayoutEffect(() => {
86
+ const g = controller.current;
87
+ if (!g)
88
+ return;
89
+ if (layout !== applied.current) {
90
+ applied.current = layout;
91
+ // The layout re-normalized for new columns is about to be replaced: don't emit it.
92
+ superseded.current = true;
93
+ try {
94
+ g.setOptions(opts);
95
+ }
96
+ finally {
97
+ superseded.current = false;
98
+ }
99
+ g.setLayout(layout); // normalized against the new columns; no-op on our own echo
100
+ }
101
+ else {
102
+ g.setOptions(opts); // a columns change may normalize the layout and emit
103
+ }
104
+ // A className or style change makes React rewrite the class attribute or style keys,
105
+ // dropping what core set imperatively; core's render restores dash-grid--narrow and the
106
+ // --dash-* variables (dash-grid and display: grid are rendered by React below).
107
+ // dash-grid--dragging, set during a drag, is not restored: a className change mid-drag
108
+ // drops it until the next drag (it only disables text selection / page scroll).
109
+ const look = lastLook.current;
110
+ const restyled = className !== look.className || !shallowEqual(style, look.style);
111
+ lastLook.current = { className, style };
112
+ // Every widget added, removed or re-id'd commits (or unmounts) itself, so `dirty` covers
113
+ // what scanning the children's ids here would; a reorder needs no refresh (the core
114
+ // places widgets by layout order, not DOM order).
115
+ if (dirty.current || restyled) {
116
+ dirty.current = false;
117
+ g.refresh();
118
+ }
119
+ });
120
+ useImperativeHandle(ref, () => ({
121
+ addItem(item, index) {
122
+ const { opts: o, layout: l, onLayoutChange: change, onReject: reject } = latest.current;
123
+ const config = { columns: o.columns, maxRows: o.maxRows, mode: o.dropMode };
124
+ // Plan on what the core renders (normalized), so maxRows holds for e.g. minH > h.
125
+ const result = planAdd(normalizeLayout(l, o.columns), config, item, index);
126
+ if (result.ok) {
127
+ latest.current.layout = result.layout; // a following add/remove builds on this
128
+ change?.(result.layout);
129
+ }
130
+ else {
131
+ reject?.(result.reason);
132
+ }
133
+ return result;
134
+ },
135
+ removeItem(id) {
136
+ const { layout: l, onLayoutChange: change } = latest.current;
137
+ const next = planRemove(l, id);
138
+ if (next === l)
139
+ return;
140
+ latest.current.layout = next; // a following add/remove builds on this
141
+ change?.(next);
142
+ },
143
+ }), []);
144
+ return (_jsx("div", { ...divProps,
145
+ // Core styles hang off dash-grid and need display: grid; rendering them here keeps them
146
+ // when React rewrites the class attribute or style (and applies them in SSR markup).
147
+ className: className ? `dash-grid ${className}` : 'dash-grid', style: { ...style, display: 'grid' }, ref: container, children: _jsx(GridContext, { value: context, children: children }) }));
148
+ }
@@ -0,0 +1,5 @@
1
+ export { Grid } from './grid.js';
2
+ export type { GridProps, GridHandle } from './grid.js';
3
+ export { Widget, WidgetHandle } from './widget.js';
4
+ export type { WidgetProps } from './widget.js';
5
+ export type { LayoutItem, DropMode, RejectReason, DropResult } from '@dash-builder/core';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { Grid } from './grid.js';
2
+ export { Widget, WidgetHandle } from './widget.js';
@@ -0,0 +1,27 @@
1
+ import type { ComponentPropsWithRef } from 'react';
2
+ export type WidgetProps = {
3
+ id: string;
4
+ } & Omit<ComponentPropsWithRef<'div'>, 'id'>;
5
+ /**
6
+ * A grid widget. `id` is the layout item id; it is exposed as `data-grid-id`, not as a DOM `id`.
7
+ *
8
+ * React owns the element, so a commit can undo what the core set on it imperatively:
9
+ * - `dash-item` is rendered here, because a `className` change rewrites the class attribute
10
+ * and the core adds `dash-item` only when it first binds an element. The drag-state classes
11
+ * (`dash-item--placeholder`, `--swap-target`, `--swap-blocked`) are set by the active drag,
12
+ * not by the core's render, so a `className` change in the middle of a drag drops them:
13
+ * the swap-target classes come back on the next pointer move, the placeholder class
14
+ * only with the next drag. The drag itself and its drop are unaffected.
15
+ * - Text children are written with `textContent`, which deletes the core's resize handle.
16
+ * - The widget may mount, unmount or change id without the `<Grid>` re-rendering (a wrapper
17
+ * with its own state, a Suspense boundary resolving).
18
+ * So after every commit, and when it unmounts, a widget asks the grid for a refresh: the
19
+ * core rescans its children and re-renders placement and handles. Requests coalesce into
20
+ * one refresh, which does not cancel an active drag or resize.
21
+ */
22
+ export declare function Widget({ id, className, ...rest }: WidgetProps): import("react").JSX.Element;
23
+ export declare namespace Widget {
24
+ export { WidgetHandle as Handle };
25
+ }
26
+ /** Marks the part of a widget that starts a drag (when the grid uses handles). */
27
+ export declare function WidgetHandle(props: ComponentPropsWithRef<'span'>): import("react").JSX.Element;
package/dist/widget.js ADDED
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext, useLayoutEffect } from 'react';
3
+ import { GridContext } from './context.js';
4
+ /**
5
+ * A grid widget. `id` is the layout item id; it is exposed as `data-grid-id`, not as a DOM `id`.
6
+ *
7
+ * React owns the element, so a commit can undo what the core set on it imperatively:
8
+ * - `dash-item` is rendered here, because a `className` change rewrites the class attribute
9
+ * and the core adds `dash-item` only when it first binds an element. The drag-state classes
10
+ * (`dash-item--placeholder`, `--swap-target`, `--swap-blocked`) are set by the active drag,
11
+ * not by the core's render, so a `className` change in the middle of a drag drops them:
12
+ * the swap-target classes come back on the next pointer move, the placeholder class
13
+ * only with the next drag. The drag itself and its drop are unaffected.
14
+ * - Text children are written with `textContent`, which deletes the core's resize handle.
15
+ * - The widget may mount, unmount or change id without the `<Grid>` re-rendering (a wrapper
16
+ * with its own state, a Suspense boundary resolving).
17
+ * So after every commit, and when it unmounts, a widget asks the grid for a refresh: the
18
+ * core rescans its children and re-renders placement and handles. Requests coalesce into
19
+ * one refresh, which does not cancel an active drag or resize.
20
+ */
21
+ export function Widget({ id, className, ...rest }) {
22
+ const { requestRefresh } = useContext(GridContext);
23
+ useLayoutEffect(() => {
24
+ requestRefresh();
25
+ return requestRefresh;
26
+ });
27
+ return _jsx("div", { ...rest, className: className ? `dash-item ${className}` : 'dash-item', "data-grid-id": id });
28
+ }
29
+ /** Marks the part of a widget that starts a drag (when the grid uses handles). */
30
+ export function WidgetHandle(props) {
31
+ return _jsx("span", { ...props, "data-grid-handle": "" });
32
+ }
33
+ Widget.Handle = WidgetHandle;
package/package.json CHANGED
@@ -1,6 +1,71 @@
1
1
  {
2
2
  "name": "@dash-builder/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "React bindings for @dash-builder/core: a dashboard grid with draggable, resizable widgets in a fixed number of columns.",
5
+ "license": "MIT",
6
+ "author": "Konstantin Penzin",
7
+ "homepage": "https://constantine-24.github.io/dash-builder/",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Constantine-24/dash-builder.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Constantine-24/dash-builder/issues"
15
+ },
16
+ "keywords": [
17
+ "dashboard",
18
+ "grid",
19
+ "layout",
20
+ "widgets",
21
+ "drag-and-drop",
22
+ "resizable",
23
+ "draggable",
24
+ "react"
25
+ ],
26
+ "type": "module",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ }
33
+ },
34
+ "sideEffects": false,
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE",
39
+ "CHANGELOG.md"
40
+ ],
41
+ "peerDependencies": {
42
+ "@dash-builder/core": "0.x",
43
+ "react": "^19.0.0",
44
+ "react-dom": "^19.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@dash-builder/core": "0.1.0",
48
+ "@testing-library/dom": "^10.4.2",
49
+ "@testing-library/react": "^16.3.3",
50
+ "@types/react": "^19.3.0",
51
+ "@types/react-dom": "^19.3.0",
52
+ "@vitejs/plugin-react": "^6.1.2",
53
+ "happy-dom": "^21.0.4",
54
+ "react": "^19.3.1",
55
+ "react-dom": "^19.3.1",
56
+ "typescript": "^7.0.2",
57
+ "vite": "^8.3.5",
58
+ "vitest": "^5.0.5"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public"
62
+ },
63
+ "scripts": {
64
+ "build": "tsc -p tsconfig.build.json",
65
+ "test": "vitest run",
66
+ "typecheck": "tsc --noEmit -p tsconfig.json",
67
+ "demo": "vite",
68
+ "build:demo": "vite build",
69
+ "test:smoke": "pnpm build && node scripts/smoke.mjs"
70
+ }
6
71
  }