@bsuite/page-builder 0.2.5 → 0.2.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AAIA,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAGxC,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAkEnE,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,OAAO,EACP,UAAU,EACV,SAAS,EACT,WAAmB,EACnB,aAAsC,EACtC,yBAAiE,EACjE,kBAAkB,EAClB,sBAAsB,GACvB,EAAE,mBAAmB,2CA2RrB"}
1
+ {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AAIA,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAGxC,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAqHnE,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,OAAO,EACP,UAAU,EACV,SAAS,EAMT,WAAkB,EAClB,aAAsC,EACtC,yBAAiE,EACjE,kBAAkB,EAClB,sBAAsB,GACvB,EAAE,mBAAmB,2CA6XrB"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Eye, EyeOff, Layers, LayoutGrid, Plus, RotateCcw, Save, Settings2 } from 'lucide-react';
3
- import React, { startTransition, useEffect, useMemo, useState } from 'react';
3
+ import React, { startTransition, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { Responsive } from 'react-grid-layout';
5
5
  import { gridBounds, maxSize, minMaxSize, minSize } from 'react-grid-layout/core';
6
6
  import 'react-grid-layout/css/styles.css';
@@ -10,17 +10,59 @@ import { cn } from './utils.js';
10
10
  const DEFAULT_RESIZE_BOUNDS = { minW: 2, minH: 1, maxW: 12, maxH: 16 };
11
11
  const DEFAULT_RESIZE_HANDLES = ['se'];
12
12
  const DEFAULT_ADD_ENTITY_WIDGET_EVENT_NAMES = ['bsu-add-entity-widget', 'crm7-add-entity-widget'];
13
+ /**
14
+ * Custom resize handle for react-grid-layout v2.
15
+ *
16
+ * Why this exists (resize bug fix, 2026-05-12)
17
+ * --------------------------------------------
18
+ * react-grid-layout v2 dropped the auto-rendered `.react-resizable-handle`
19
+ * element that v1 appended to every resizable item. With v2, items still
20
+ * get the `react-resizable` CSS class (so the existing override CSS targets
21
+ * the right element) but no handle DOM is created unless the consumer
22
+ * supplies `resizeConfig.handleComponent`.
23
+ *
24
+ * Symptom: operator reported "I can drag but I can't make the cards bigger"
25
+ * across 5+ apps and 50+ flagged occurrences. Live DOM inspection on
26
+ * crm.crm7.app/reports confirmed `react-grid-item` had `.react-resizable`
27
+ * class but 0 `.react-resizable-handle` children — handles literally
28
+ * weren't in the DOM, so the carefully-tuned override CSS in
29
+ * `react-grid-layout-overrides.css` had nothing to style.
30
+ *
31
+ * Fix: provide a `forwardRef` handle component that renders a span with
32
+ * the exact class names the override CSS targets (`react-resizable-handle`
33
+ * + `react-resizable-handle-<axis>`). data-no-drag prevents the parent
34
+ * drag handler from intercepting pointer-down on the handle.
35
+ */
36
+ const RGLResizeHandle = React.forwardRef(function RGLResizeHandle({ axis = 'se', className: injectedClassName, ...rest }, ref) {
37
+ return (_jsx("span", { ref: ref, "data-no-drag": true, className: `react-resizable-handle react-resizable-handle-${axis}${injectedClassName ? ' ' + injectedClassName : ''}`, ...rest }));
38
+ });
13
39
  const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, isEditing, label, onRemove, className: injectedClassName, style: injectedStyle, ...rest }, ref) {
14
- return (_jsx("div", { ref: ref, className: cn('relative group', injectedClassName), style: injectedStyle, ...rest, children: _jsxs("div", { className: "h-full w-full relative", children: [isEditing && _jsx("div", { className: "drag-handle absolute inset-0 z-20 cursor-move bg-transparent" }), isEditing && (_jsx("div", { className: "absolute inset-0 z-10 pointer-events-none rounded-3xl border-2 border-transparent group-hover:border-primary/50 transition-colors bg-black/5" })), isEditing && (_jsx("div", { className: "absolute top-2 left-2 z-30 flex items-center gap-1 pointer-events-none", children: _jsx("span", { className: "text-[10px] px-1.5 py-0.5 rounded-md opacity-80 font-medium bg-muted text-muted-foreground", children: label }) })), isEditing && (_jsx("button", { className: "absolute top-2 right-2 z-30 h-6 w-6 rounded-full flex items-center justify-center bg-destructive/80 hover:bg-destructive text-white shadow transition-colors", onClick: (event) => {
40
+ // DnD root-cause fix (2026-05-07): react-draggable@4 (used internally by
41
+ // react-grid-layout@2) checks the drag handle via
42
+ // `matchesSelectorAndParentsTo(target, handle, baseNode)` — it walks UP
43
+ // the ancestor chain from the click target. The previous implementation
44
+ // rendered `.drag-handle` as a SIBLING of the card content, so clicks on
45
+ // any visible card content never matched (ancestors are: card-content →
46
+ // .rounded-3xl → .h-full.w-full.relative → .relative.group; none carry
47
+ // the class). Drag silently failed.
48
+ //
49
+ // Fix: put `.drag-handle` on the outer container. Now any click inside
50
+ // the GridItem has it as an ancestor → drag fires. Interactive elements
51
+ // (buttons, inputs, textarea, select, links, [data-no-drag]) are still
52
+ // protected via the `cancel` selector below in the <Responsive> render.
53
+ const outerClass = cn('relative group', isEditing && 'drag-handle cursor-move', injectedClassName);
54
+ return (_jsx("div", { ref: ref, className: outerClass, style: injectedStyle, ...rest, children: _jsxs("div", { className: "h-full w-full relative", children: [isEditing && (_jsx("div", { className: "absolute inset-0 z-10 pointer-events-none rounded-3xl border-2 border-transparent group-hover:border-primary/50 transition-colors bg-black/5" })), isEditing && (_jsx("div", { className: "absolute top-2 left-2 z-30 flex items-center gap-1 pointer-events-none", children: _jsx("span", { className: "text-[10px] px-1.5 py-0.5 rounded-md opacity-80 font-medium bg-muted text-muted-foreground", children: label }) })), isEditing && (_jsx("button", { className: "absolute top-2 right-2 z-30 h-6 w-6 rounded-full flex items-center justify-center bg-destructive/80 hover:bg-destructive text-white shadow transition-colors", "data-no-drag": true, onPointerDown: (event) => event.stopPropagation(), onClick: (event) => {
15
55
  event.stopPropagation();
16
56
  onRemove(id);
17
- }, title: `Hide ${label}`, "aria-label": `Hide ${label}`, children: _jsx(EyeOff, { className: "h-3 w-3", "aria-hidden": "true" }) })), _jsx("div", { className: "h-full w-full rounded-3xl transition-all flex flex-col", style: {
18
- background: 'var(--bg-panel, var(--background, var(--card, #fff)))',
19
- boxShadow: 'var(--shadow-shell-glow, 0 1px 3px rgb(0 0 0 / 0.1))',
20
- contain: 'paint',
21
- }, children: content })] }) }));
57
+ }, title: `Hide ${label}`, "aria-label": `Hide ${label}`, children: _jsx(EyeOff, { className: "h-3 w-3", "aria-hidden": "true" }) })), _jsx("div", { className: "h-full w-full rounded-3xl transition-all flex flex-col bg-card shadow-sm", style: { contain: 'paint' }, children: content })] }) }));
22
58
  }));
23
- export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className, isResizable = false, resizeHandles = DEFAULT_RESIZE_HANDLES, addEntityWidgetEventNames = DEFAULT_ADD_ENTITY_WIDGET_EVENT_NAMES, createEntityWidget, onRegisterEntityWidget, }) {
59
+ export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className,
60
+ // Resize default flipped 2026-05-12: rgl v2 dropped auto-handle rendering
61
+ // (see RGLResizeHandle above). Previously this defaulted false; consumer
62
+ // adapters then defaulted it back to true. With v2's handle gap fixed,
63
+ // the safe default is true so any consumer who forgets to set it still
64
+ // gets a usable canvas. Opt out per-page with isResizable={false}.
65
+ isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, addEntityWidgetEventNames = DEFAULT_ADD_ENTITY_WIDGET_EVENT_NAMES, createEntityWidget, onRegisterEntityWidget, }) {
24
66
  const { currentLayouts, layoutCols, isEditing, setIsEditing, activeCols, activeCompactor, onLayoutChange, handleColumnChange, handleCompact, handleReset, addWidget, removeWidget, resetConfirmOpen, setResetConfirmOpen, containerRef, containerWidth, } = usePageGridLayout({
25
67
  pageKey,
26
68
  defaultLayouts,
@@ -100,18 +142,73 @@ export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVer
100
142
  ]);
101
143
  const visibleKeys = useMemo(() => new Set((activeLayouts.lg ?? []).map((item) => item.i)), [activeLayouts.lg]);
102
144
  const hiddenWidgetKeys = useMemo(() => [...renderableWidgetKeys].filter((key) => !visibleKeys.has(key)), [renderableWidgetKeys, visibleKeys]);
103
- return (_jsxs("div", { className: className, children: [isEditing && (_jsxs("div", { className: "flex flex-col gap-3 p-4 rounded-xl shadow-lg border-2 mb-4", style: { backgroundColor: 'var(--bg-panel, var(--card, #fff))', borderColor: 'var(--accent-primary, var(--primary, #2563eb))' }, children: [_jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "h-10 w-10 rounded-full flex items-center justify-center animate-pulse", style: { backgroundColor: 'var(--bg-shell-accent, rgb(37 99 235 / 0.1))' }, children: _jsx(Settings2, { className: "h-5 w-5 text-primary" }) }), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold text-lg", style: { color: 'var(--text-heading, var(--foreground, #111827))' }, children: "Canvas Editor Active" }), _jsx("span", { className: "text-sm", style: { color: 'var(--text-secondary, var(--muted-foreground, #6b7280))' }, children: "Drag anywhere on a card to move it. Resize with the bottom-right handle." })] })] }), _jsxs("button", { type: "button", className: "shrink-0 inline-flex items-center rounded-md px-3 py-2 text-sm font-medium bg-primary text-primary-foreground shadow", onClick: () => startTransition(() => setIsEditing(false)), children: [_jsx(Save, { className: "h-4 w-4 mr-2" }), "Save & Exit"] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-2 pt-2 border-t", style: { borderColor: 'var(--border-shell, var(--border, #e5e7eb))' }, children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LayoutGrid, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "text-sm shrink-0 text-muted-foreground", children: "Columns:" }), _jsx("input", { type: "range", min: 1, max: 24, value: layoutCols, onChange: (event) => handleColumnChange(Number(event.target.value)), className: "w-24 cursor-pointer accent-primary", "aria-label": "Column count" }), _jsx("span", { className: "text-sm font-mono w-5 text-center tabular-nums", children: layoutCols })] }), _jsx("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Column presets", children: [1, 2, 3, 4, 6, 12].map((columnCount) => (_jsx("button", { type: "button", onClick: () => handleColumnChange(columnCount), className: "px-2 py-0.5 rounded text-xs font-medium transition-colors", style: {
104
- backgroundColor: layoutCols === columnCount ? 'var(--accent-primary, var(--primary, #2563eb))' : 'var(--bg-tertiary, var(--muted, #f3f4f6))',
105
- color: layoutCols === columnCount ? '#fff' : 'var(--text-secondary, var(--muted-foreground, #6b7280))',
106
- border: '1px solid var(--border-shell, var(--border, #e5e7eb))',
107
- }, "aria-pressed": layoutCols === columnCount, children: columnCount }, columnCount))) }), hiddenWidgetKeys.length > 0 && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Eye, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "text-sm shrink-0 text-muted-foreground", children: "Add widget:" }), _jsx("div", { className: "flex flex-wrap gap-1", children: hiddenWidgetKeys.map((key) => {
145
+ // Reset-confirmation dialog: full ARIA APG dialog-modal pattern.
146
+ // - Escape key closes the dialog.
147
+ // - Tab / Shift+Tab are trapped within the two dialog buttons (cancel ↔ confirm).
148
+ // - Initial focus moves to the Cancel button (safe default for destructive actions).
149
+ // - Focus returns to the invoking element when the dialog closes.
150
+ const resetCancelButtonRef = useRef(null);
151
+ const resetConfirmButtonRef = useRef(null);
152
+ useEffect(() => {
153
+ if (!resetConfirmOpen || typeof window === 'undefined')
154
+ return;
155
+ const handleKeyDown = (event) => {
156
+ if (event.key === 'Escape') {
157
+ event.stopPropagation();
158
+ setResetConfirmOpen(false);
159
+ return;
160
+ }
161
+ if (event.key === 'Tab') {
162
+ const cancel = resetCancelButtonRef.current;
163
+ const confirm = resetConfirmButtonRef.current;
164
+ if (!cancel || !confirm)
165
+ return;
166
+ if (event.shiftKey) {
167
+ if (document.activeElement === cancel) {
168
+ event.preventDefault();
169
+ confirm.focus();
170
+ }
171
+ }
172
+ else {
173
+ if (document.activeElement === confirm) {
174
+ event.preventDefault();
175
+ cancel.focus();
176
+ }
177
+ }
178
+ }
179
+ };
180
+ window.addEventListener('keydown', handleKeyDown);
181
+ return () => {
182
+ window.removeEventListener('keydown', handleKeyDown);
183
+ };
184
+ }, [resetConfirmOpen, setResetConfirmOpen]);
185
+ useEffect(() => {
186
+ if (!resetConfirmOpen || typeof document === 'undefined')
187
+ return;
188
+ const previousActiveElement = document.activeElement;
189
+ resetCancelButtonRef.current?.focus();
190
+ return () => {
191
+ previousActiveElement?.focus?.();
192
+ };
193
+ }, [resetConfirmOpen]);
194
+ return (_jsxs("div", { className: className, children: [isEditing && (_jsxs("div", { className: "flex flex-col gap-3 p-4 rounded-xl shadow-lg border-2 mb-4 bg-card border-primary", children: [_jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "h-10 w-10 rounded-full flex items-center justify-center animate-pulse bg-primary/10", children: _jsx(Settings2, { className: "h-5 w-5 text-primary" }) }), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold text-lg text-foreground", children: "Canvas Editor Active" }), _jsx("span", { className: "text-sm text-muted-foreground", children: "Drag anywhere on a card to move it. Resize with the bottom-right handle." })] })] }), _jsxs("button", { type: "button", className: "shrink-0 inline-flex items-center rounded-md px-3 py-2 text-sm font-medium bg-primary text-primary-foreground shadow transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1", onClick: () => startTransition(() => setIsEditing(false)), children: [_jsx(Save, { className: "h-4 w-4 mr-2" }), "Save & Exit"] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-2 pt-2 border-t border-border", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LayoutGrid, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "text-sm shrink-0 text-muted-foreground", children: "Columns:" }), _jsx("input", { type: "range", min: 1, max: 24, value: layoutCols, onChange: (event) => handleColumnChange(Number(event.target.value)), className: "w-24 cursor-pointer accent-primary", "aria-label": "Column count" }), _jsx("span", { className: "text-sm font-mono w-5 text-center tabular-nums", children: layoutCols })] }), _jsx("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Column presets", children: [1, 2, 3, 4, 6, 12].map((columnCount) => (_jsx("button", { type: "button", onClick: () => handleColumnChange(columnCount), "data-active": layoutCols === columnCount || undefined, "aria-pressed": layoutCols === columnCount, className: cn('px-2 py-0.5 rounded text-xs font-medium transition-colors border', 'bg-muted text-muted-foreground border-border', 'hover:bg-muted/80 hover:text-foreground', 'data-[active]:bg-primary data-[active]:text-primary-foreground data-[active]:border-primary', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1'), children: columnCount }, columnCount))) }), hiddenWidgetKeys.length > 0 && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Eye, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "text-sm shrink-0 text-muted-foreground", children: "Add widget:" }), _jsx("div", { className: "flex flex-wrap gap-1", children: hiddenWidgetKeys.map((key) => {
108
195
  const meta = widgetMeta?.[key];
109
196
  const Icon = meta?.icon;
110
- return (_jsxs("button", { type: "button", onClick: () => addWidget(key, meta?.defaultSize), className: "flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium border transition-colors hover:border-primary/60 bg-muted text-muted-foreground", children: [_jsx(Plus, { className: "h-3 w-3" }), Icon && _jsx(Icon, { className: "h-3 w-3" }), meta?.label ?? key] }, key));
111
- }) })] })), _jsxs("button", { type: "button", onClick: handleCompact, className: "ml-auto inline-flex items-center rounded-md border px-2 py-1 text-sm text-muted-foreground", children: [_jsx(Layers, { className: "h-4 w-4 mr-1" }), "Compact Layout"] }), _jsxs("button", { type: "button", onClick: () => setResetConfirmOpen(true), className: "inline-flex items-center rounded-md border px-2 py-1 text-sm text-muted-foreground", children: [_jsx(RotateCcw, { className: "h-4 w-4 mr-1" }), "Reset to Default"] })] })] })), resetConfirmOpen && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4", role: "dialog", "aria-modal": "true", "aria-labelledby": "page-grid-reset-title", children: _jsxs("div", { className: "max-w-md rounded-lg bg-card p-5 shadow-lg border", children: [_jsx("h2", { id: "page-grid-reset-title", className: "text-lg font-semibold", children: "Reset page layout?" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "This will discard your current layout and restore all widgets to their default positions." }), _jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [_jsx("button", { type: "button", className: "rounded-md border px-3 py-2 text-sm", onClick: () => setResetConfirmOpen(false), children: "Cancel" }), _jsx("button", { type: "button", className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground", onClick: () => {
197
+ return (_jsxs("button", { type: "button", onClick: () => addWidget(key, meta?.defaultSize), className: cn('flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium border transition-colors', 'bg-muted text-muted-foreground border-border', 'hover:bg-muted/80 hover:text-foreground hover:border-primary/60', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1'), children: [_jsx(Plus, { className: "h-3 w-3" }), Icon && _jsx(Icon, { className: "h-3 w-3" }), meta?.label ?? key] }, key));
198
+ }) })] })), _jsxs("button", { type: "button", onClick: handleCompact, className: cn('ml-auto inline-flex items-center rounded-md border border-border px-2 py-1 text-sm transition-colors', 'text-muted-foreground hover:bg-muted hover:text-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1'), children: [_jsx(Layers, { className: "h-4 w-4 mr-1" }), "Compact Layout"] }), _jsxs("button", { type: "button", onClick: () => setResetConfirmOpen(true), className: cn('inline-flex items-center rounded-md border border-border px-2 py-1 text-sm transition-colors', 'text-muted-foreground hover:bg-muted hover:text-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1'), children: [_jsx(RotateCcw, { className: "h-4 w-4 mr-1" }), "Reset to Default"] })] })] })), resetConfirmOpen && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4", role: "dialog", "aria-modal": "true", "aria-labelledby": "page-grid-reset-title", "aria-describedby": "page-grid-reset-description", children: _jsxs("div", { className: "max-w-md rounded-lg bg-card p-5 shadow-lg border border-border", children: [_jsx("h2", { id: "page-grid-reset-title", className: "text-lg font-semibold", children: "Reset page layout?" }), _jsx("p", { id: "page-grid-reset-description", className: "mt-2 text-sm text-muted-foreground", children: "This will discard your current layout and restore all widgets to their default positions." }), _jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [_jsx("button", { type: "button", ref: resetCancelButtonRef, onClick: () => setResetConfirmOpen(false), className: cn('inline-flex items-center rounded-md border border-border px-3 py-2 text-sm transition-colors', 'bg-card text-foreground hover:bg-muted hover:text-foreground', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1'), children: "Cancel" }), _jsx("button", { type: "button", ref: resetConfirmButtonRef, onClick: () => {
112
199
  handleReset();
113
200
  setResetConfirmOpen(false);
114
- }, children: "Reset to Default" })] })] }) })), _jsx("div", { ref: containerRef, className: isEditing ? 'min-h-[200px]' : '', style: { backgroundColor: isEditing ? 'rgb(0 0 0 / 0.03)' : 'transparent' }, children: _jsx("div", { style: { opacity: containerWidth > 0 ? 1 : 0 }, "aria-busy": containerWidth <= 0, children: _jsx(Responsive, { width: Math.max(containerWidth, 1), className: "layout", layouts: activeLayouts, breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }, rowHeight: 32, onLayoutChange: onLayoutChange, dragConfig: { enabled: isEditing, handle: '.drag-handle', bounded: false, cancel: '.react-resizable-handle' }, resizeConfig: { enabled: resizeEnabled, handles: resizeHandles }, constraints: resizeEnabled ? resizeConstraints : undefined, compactor: activeCompactor, cols: activeCols, margin: [6, 6], children: activeLayouts.lg.map((layoutItem) => {
201
+ }, className: cn('inline-flex items-center rounded-md px-3 py-2 text-sm font-medium transition-colors', 'bg-destructive text-white shadow hover:bg-destructive/90', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-destructive/40 focus-visible:ring-offset-1'), children: "Reset to Default" })] })] }) })), _jsx("div", { ref: containerRef, className: isEditing ? 'min-h-[200px]' : '', style: { backgroundColor: isEditing ? 'rgb(0 0 0 / 0.03)' : 'transparent' }, children: _jsx("div", { style: { opacity: containerWidth > 0 ? 1 : 0 }, "aria-busy": containerWidth <= 0, children: _jsx(Responsive, { width: Math.max(containerWidth, 1), className: "layout", layouts: activeLayouts, breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }, rowHeight: 32, onLayoutChange: onLayoutChange, dragConfig: {
202
+ enabled: isEditing,
203
+ handle: '.drag-handle',
204
+ bounded: false,
205
+ cancel: '.react-resizable-handle, button, input, textarea, select, [contenteditable="true"], [data-no-drag], a[href], [role="button"], [role="combobox"], [role="menuitem"], [role="tab"], [role="checkbox"], [role="switch"], [role="slider"], [role="textbox"]',
206
+ }, resizeConfig: {
207
+ enabled: resizeEnabled,
208
+ handles: resizeHandles,
209
+ // Required for v2 — see RGLResizeHandle comment above.
210
+ handleComponent: (axis, ref) => (_jsx(RGLResizeHandle, { axis: axis, ref: ref })),
211
+ }, constraints: resizeEnabled ? resizeConstraints : undefined, compactor: activeCompactor, cols: activeCols, margin: [6, 6], children: activeLayouts.lg.map((layoutItem) => {
115
212
  const content = allWidgets[layoutItem.i];
116
213
  if (content === undefined || content === null)
117
214
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/page-builder",
3
- "version": "0.2.5",
3
+ "version": "0.2.8",
4
4
  "description": "Shared BSuite responsive page-builder grid and layout persistence primitives",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -57,7 +57,7 @@
57
57
  "@testing-library/react": "^16.3.2",
58
58
  "@types/react": "^19.2.14",
59
59
  "@types/react-dom": "^19.2.3",
60
- "@vitejs/plugin-react": "^5.2.0",
60
+ "@vitejs/plugin-react": "^6.0.1",
61
61
  "jsdom": "^29.1.1",
62
62
  "lucide-react": "^1.14.0",
63
63
  "react": "^19.2.5",
@@ -65,7 +65,7 @@
65
65
  "react-grid-layout": "^2.2.3",
66
66
  "react-resizable": "^3.1.3",
67
67
  "typescript": "~6.0.3",
68
- "vite": "^6.4.2",
68
+ "vite": "^8.0.10",
69
69
  "vitest": "^4.1.5"
70
70
  }
71
71
  }