@bsuite/page-builder 0.4.0 → 0.4.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.
@@ -1,5 +1,6 @@
1
+ import React from 'react';
1
2
  import 'react-grid-layout/css/styles.css';
2
3
  import 'react-resizable/css/styles.css';
3
4
  import type { PageGridLayoutProps } from './types.js';
4
- export declare function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className, isResizable, resizeHandles, addEntityWidgetEventNames, createEntityWidget, onRegisterEntityWidget, }: PageGridLayoutProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className, isResizable, resizeHandles, addEntityWidgetEventNames, createEntityWidget, onRegisterEntityWidget, }: PageGridLayoutProps): React.JSX.Element;
5
6
  //# sourceMappingURL=PageGridLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AAIA,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAIxC,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA+KnE,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,2CA+frB"}
1
+ {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AACA,OAAO,KAAgE,MAAM,OAAO,CAAC;AAGrF,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAIxC,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA+KnE,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,qBAwkBrB"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ArrowDown, ArrowUp, Eye, EyeOff, Layers, LayoutGrid, Lock, Plus, RotateCcw, Save, Settings2, Unlock } from 'lucide-react';
2
+ import { ArrowDown, ArrowUp, ChevronDown, ChevronUp, Eye, EyeOff, Layers, LayoutGrid, Lock, Plus, RotateCcw, Save, Settings2, Unlock } from 'lucide-react';
3
3
  import React, { startTransition, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { Responsive } from 'react-grid-layout';
5
5
  import { gridBounds, minMaxSize, minSize } from 'react-grid-layout/core';
@@ -69,11 +69,11 @@ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, is
69
69
  // the GridItem has it as an ancestor → drag fires. Interactive elements
70
70
  // (buttons, inputs, textarea, select, links, [data-no-drag]) are still
71
71
  // protected via the `cancel` selector below in the <Responsive> render.
72
- const outerClass = cn('relative group', isEditing && 'drag-handle cursor-move', injectedClassName);
72
+ const outerClass = cn('relative group overflow-visible', isEditing && 'drag-handle cursor-move', injectedClassName);
73
73
  return (_jsxs("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) => {
74
74
  event.stopPropagation();
75
75
  onHide(id);
76
- }, 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: _jsx("div", { className: "flex-1 min-h-0 overflow-auto", children: content }) })] }), injectedChildren] }));
76
+ }, 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: 'layout style' }, children: _jsx("div", { className: "flex-1 min-h-0 overflow-auto", children: content }) })] }), injectedChildren] }));
77
77
  }));
78
78
  export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className,
79
79
  // Resize default flipped 2026-05-12: rgl v2 dropped auto-handle rendering
@@ -212,6 +212,50 @@ isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, addEntityWidgetEvent
212
212
  // - Focus returns to the invoking element when the dialog closes.
213
213
  const resetCancelButtonRef = useRef(null);
214
214
  const resetConfirmButtonRef = useRef(null);
215
+ // Collapsed state for the sticky editor banner. On tall pages the expanded
216
+ // banner (columns + full Layers list + actions) can obscure most of the
217
+ // viewport while the user is arranging cards below it (operator-reported).
218
+ // Collapsing reduces it to a single thin row (title + Save & Exit + expand)
219
+ // so the canvas underneath stays visible. Defaults expanded; auto-collapses
220
+ // once the user scrolls the banner's scroll-ancestor past a small threshold
221
+ // so it stops blocking the cards being arranged.
222
+ const [controlsCollapsed, setControlsCollapsed] = useState(false);
223
+ const editorBannerRef = useRef(null);
224
+ const userToggledCollapseRef = useRef(false);
225
+ useEffect(() => {
226
+ if (!isEditing || typeof window === 'undefined')
227
+ return;
228
+ // Reset collapse state each time the editor opens (unless the user has
229
+ // explicitly toggled it this session).
230
+ userToggledCollapseRef.current = false;
231
+ setControlsCollapsed(false);
232
+ const bannerEl = editorBannerRef.current;
233
+ if (!bannerEl)
234
+ return;
235
+ // Find the nearest scrolling ancestor so we collapse on the same scroll
236
+ // the sticky banner is anchored to (may be the window or an inner pane).
237
+ const findScrollParent = (node) => {
238
+ let current = node?.parentElement ?? null;
239
+ while (current) {
240
+ const style = window.getComputedStyle(current);
241
+ if (/(auto|scroll|overlay)/.test(style.overflowY))
242
+ return current;
243
+ current = current.parentElement;
244
+ }
245
+ return window;
246
+ };
247
+ const scrollParent = findScrollParent(bannerEl);
248
+ const readScrollTop = () => scrollParent === window
249
+ ? window.scrollY
250
+ : scrollParent.scrollTop;
251
+ const handleScroll = () => {
252
+ if (userToggledCollapseRef.current)
253
+ return;
254
+ setControlsCollapsed(readScrollTop() > 120);
255
+ };
256
+ scrollParent.addEventListener('scroll', handleScroll, { passive: true });
257
+ return () => scrollParent.removeEventListener('scroll', handleScroll);
258
+ }, [isEditing]);
215
259
  useEffect(() => {
216
260
  if (!resetConfirmOpen || typeof window === 'undefined')
217
261
  return;
@@ -254,24 +298,31 @@ isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, addEntityWidgetEvent
254
298
  previousActiveElement?.focus?.();
255
299
  };
256
300
  }, [resetConfirmOpen]);
257
- return (_jsxs("div", { className: className, children: [isEditing && (_jsxs("div", { className: cn(
301
+ return (_jsxs("div", { className: cn('relative', isEditing && 'isolate', className), children: [isEditing && (_jsxs("div", { ref: editorBannerRef, "data-page-grid-editor-controls": true, "data-collapsed": controlsCollapsed || undefined, className: cn(
258
302
  // Sticky overlay banner — sits at the top of the scroll container
259
303
  // without pushing the form down. `top-0` anchors to the nearest
260
- // scrolling ancestor; `z-30` keeps it above grid items but below
304
+ // scrolling ancestor; `z-40` keeps it above grid items but below
261
305
  // app-level overlays (toaster, dialogs are z-50+).
262
- 'sticky top-0 z-30 flex flex-col gap-3 p-4 rounded-xl shadow-lg border-2 mb-4',
306
+ 'sticky top-0 z-40 isolate flex flex-col gap-3 rounded-xl shadow-lg border-2 mb-4',
307
+ // Collapsed → thin single-row bar so the canvas below stays visible
308
+ // (operator-reported: expanded banner obscured most of the viewport
309
+ // while arranging cards). Expanded → full controls.
310
+ controlsCollapsed ? 'p-2' : 'p-4',
263
311
  // Subtle translucent background so the form behind it stays
264
312
  // partially visible — mitigates Issue 2 (banner consuming
265
313
  // vertical space). `bg-card/95` + `backdrop-blur` keeps text
266
314
  // legible while showing form context behind the banner.
267
- 'bg-card/95 supports-[backdrop-filter]:bg-card/80 backdrop-blur border-primary'), role: "region", "aria-label": "Canvas editor controls", 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) => {
315
+ 'bg-card/95 supports-[backdrop-filter]:bg-card/80 backdrop-blur border-primary'), role: "region", "aria-label": "Canvas editor controls", 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: cn('rounded-full flex items-center justify-center bg-primary/10', controlsCollapsed ? 'h-8 w-8' : 'h-10 w-10 animate-pulse'), children: _jsx(Settings2, { className: cn('text-primary', controlsCollapsed ? 'h-4 w-4' : 'h-5 w-5') }) }), _jsxs("div", { children: [_jsx("h3", { className: cn('font-semibold text-foreground', controlsCollapsed ? 'text-sm' : 'text-lg'), children: "Canvas Editor Active" }), !controlsCollapsed && (_jsx("span", { className: "text-sm text-muted-foreground", children: "Drag anywhere on a card to move it. Resize with the bottom-right handle." }))] })] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [_jsxs("button", { type: "button", className: "inline-flex items-center gap-1 rounded-md border border-border px-2 py-2 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1", "aria-expanded": !controlsCollapsed, "aria-controls": "page-grid-editor-controls-body", onClick: () => {
316
+ userToggledCollapseRef.current = true;
317
+ setControlsCollapsed((previous) => !previous);
318
+ }, children: [controlsCollapsed ? _jsx(ChevronDown, { className: "h-4 w-4" }) : _jsx(ChevronUp, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only sm:not-sr-only", children: controlsCollapsed ? 'Expand' : 'Collapse' })] }), _jsxs("button", { type: "button", className: "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", { id: "page-grid-editor-controls-body", hidden: controlsCollapsed, 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) => {
268
319
  const meta = widgetMeta?.[key];
269
320
  const Icon = meta?.icon;
270
321
  return (_jsxs("button", { type: "button", onClick: () => showLayer(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" }), layerNames[key] || meta?.label || key] }, key));
271
322
  }) })] })), layerItems.length > 0 && (_jsxs("div", { className: "basis-full rounded-md border border-border bg-background/80 p-2", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: [_jsx(Layers, { className: "h-3.5 w-3.5" }), "Layers"] }), _jsx("div", { className: "flex flex-col gap-1", children: layerItems.map((layer) => (_jsxs("div", { className: "flex items-center gap-1 rounded border border-border bg-card px-2 py-1", children: [_jsx("input", { type: "text", value: layer.label, onChange: (event) => handleLayerRename(layer.id, event.target.value), className: "min-w-0 flex-1 bg-transparent text-xs text-foreground outline-none", "aria-label": `Rename ${layer.label}` }), _jsx("button", { type: "button", onClick: () => moveWidget(layer.id, 'up'), disabled: !layer.canMoveUp, "aria-label": `Move ${layer.label} up`, className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(ArrowUp, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => moveWidget(layer.id, 'down'), disabled: !layer.canMoveDown, "aria-label": `Move ${layer.label} down`, className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-40", children: _jsx(ArrowDown, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => setWidgetLocked(layer.id, !layer.locked), "aria-label": `${layer.locked ? 'Unlock' : 'Lock'} ${layer.label}`, className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", children: layer.locked ? _jsx(Lock, { className: "h-3.5 w-3.5" }) : _jsx(Unlock, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => hideLayer(layer.id), "aria-label": `Hide ${layer.label}`, className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground", children: _jsx(EyeOff, { className: "h-3.5 w-3.5" }) })] }, layer.id))) })] })), _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: () => {
272
323
  handleReset();
273
324
  setResetConfirmOpen(false);
274
- }, 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' }, "data-page-grid-editing": isEditing || undefined, 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: {
325
+ }, 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: cn('relative z-0 overflow-visible', isEditing && 'min-h-[200px] bg-muted/30'), "data-page-grid-editing": isEditing || undefined, children: _jsx("div", { className: "relative z-0", style: { opacity: containerWidth > 0 ? 1 : 0 }, "aria-busy": containerWidth <= 0, children: _jsx(Responsive, { width: Math.max(containerWidth, 1), className: "layout page-grid-canvas", layouts: activeLayouts, breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }, rowHeight: 32, onLayoutChange: onLayoutChange, dragConfig: {
275
326
  enabled: isEditing,
276
327
  handle: '.drag-handle',
277
328
  bounded: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/page-builder",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Shared BSuite responsive page-builder grid and layout persistence primitives",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -39,7 +39,7 @@
39
39
  "react": ">=18 <21",
40
40
  "react-dom": ">=18 <21",
41
41
  "react-grid-layout": "^2",
42
- "react-resizable": "^3"
42
+ "react-resizable": "^3 || ^4"
43
43
  },
44
44
  "peerDependenciesMeta": {
45
45
  "@dnd-kit/core": {
@@ -55,17 +55,17 @@
55
55
  "devDependencies": {
56
56
  "@testing-library/jest-dom": "^6.9.1",
57
57
  "@testing-library/react": "^16.3.2",
58
- "@types/react": "^19.2.14",
58
+ "@types/react": "^19.2.17",
59
59
  "@types/react-dom": "^19.2.3",
60
- "@vitejs/plugin-react": "^6.0.1",
60
+ "@vitejs/plugin-react": "^6.0.2",
61
61
  "jsdom": "^29.1.1",
62
- "lucide-react": "^1.14.0",
63
- "react": "^19.2.5",
64
- "react-dom": "^19.2.5",
62
+ "lucide-react": "^1.17.0",
63
+ "react": "^19.2.7",
64
+ "react-dom": "^19.2.7",
65
65
  "react-grid-layout": "^2.2.3",
66
- "react-resizable": "^3.1.3",
66
+ "react-resizable": "^4.0.1",
67
67
  "typescript": "~6.0.3",
68
- "vite": "^8.0.10",
69
- "vitest": "^4.1.5"
68
+ "vite": "^8.0.16",
69
+ "vitest": "^4.1.8"
70
70
  }
71
71
  }
@@ -14,16 +14,29 @@
14
14
 
15
15
  .react-grid-layout {
16
16
  position: relative;
17
+ isolation: isolate;
17
18
  transition: height 200ms ease;
18
19
  }
19
20
 
20
21
  .react-grid-item {
22
+ overflow: visible;
21
23
  transition: transform 200ms ease, width 200ms ease, height 200ms ease;
24
+ z-index: 1;
25
+ }
26
+
27
+ .react-grid-layout .react-grid-item.react-draggable-dragging,
28
+ .react-grid-layout .react-grid-item.resizing {
29
+ z-index: 20;
30
+ }
31
+
32
+ .react-grid-layout .react-grid-item.react-grid-placeholder {
33
+ z-index: 0;
34
+ pointer-events: none;
22
35
  }
23
36
 
24
37
  .react-grid-item.react-grid-placeholder {
25
38
  border-radius: 1.5rem;
26
- background: var(--accent-primary, var(--primary, #2563eb));
39
+ background: var(--accent-primary, var(--role-primary, var(--primary, oklch(0.546 0.215 262.9))));
27
40
  opacity: 0.16;
28
41
  }
29
42
 
@@ -59,12 +72,12 @@
59
72
  bottom: 3px;
60
73
  width: 6px;
61
74
  height: 6px;
62
- border-right: 2px solid var(--text-secondary, rgba(0, 0, 0, 0.45));
63
- border-bottom: 2px solid var(--text-secondary, rgba(0, 0, 0, 0.45));
75
+ border-right: 2px solid var(--text-secondary, oklch(0 0 0 / 0.45));
76
+ border-bottom: 2px solid var(--text-secondary, oklch(0 0 0 / 0.45));
64
77
  }
65
78
 
66
79
  /* Highlight the marker when hovering the handle itself. */
67
80
  .react-grid-item > .react-resizable-handle:hover::after {
68
- border-right-color: var(--accent-primary, var(--primary, #2563eb));
69
- border-bottom-color: var(--accent-primary, var(--primary, #2563eb));
81
+ border-right-color: var(--accent-primary, var(--role-primary, var(--primary, oklch(0.546 0.215 262.9))));
82
+ border-bottom-color: var(--accent-primary, var(--role-primary, var(--primary, oklch(0.546 0.215 262.9))));
70
83
  }