@bsuite/page-builder 0.2.1 → 0.2.4

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.
@@ -100,7 +100,7 @@ export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVer
100
100
  ]);
101
101
  const visibleKeys = useMemo(() => new Set((activeLayouts.lg ?? []).map((item) => item.i)), [activeLayouts.lg]);
102
102
  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: 2, 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: [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: {
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
104
  backgroundColor: layoutCols === columnCount ? 'var(--accent-primary, var(--primary, #2563eb))' : 'var(--bg-tertiary, var(--muted, #f3f4f6))',
105
105
  color: layoutCols === columnCount ? '#fff' : 'var(--text-secondary, var(--muted-foreground, #6b7280))',
106
106
  border: '1px solid var(--border-shell, var(--border, #e5e7eb))',
@@ -111,7 +111,7 @@ export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVer
111
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: () => {
112
112
  handleReset();
113
113
  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: resizeConstraints, compactor: activeCompactor, cols: activeCols, margin: [6, 6], children: activeLayouts.lg.map((layoutItem) => {
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) => {
115
115
  const content = allWidgets[layoutItem.i];
116
116
  if (content === undefined || content === null)
117
117
  return null;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export { PageGridLayout } from './PageGridLayout.js';
2
2
  export { usePageGridLayout, DEFAULT_EDITOR_EVENT_NAMES } from './usePageGridLayout.js';
3
3
  export { rescaleLayout } from './rescaleLayout.js';
4
+ export { WYSIWYG_REQUIRED_PRIMITIVES, WYSIWYG_SURFACE_CONTRACTS, assertWysiwygPrimitiveCoverage, getWysiwygSurfaceContract, } from './wysiwygContract.js';
5
+ export type { WysiwygPrimitive, WysiwygSurfaceContract, WysiwygSurfaceId, } from './wysiwygContract.js';
4
6
  export { buildResponsiveLayouts, RESPONSIVE_BREAKPOINTS, } from './buildResponsiveLayouts.js';
5
7
  export type { BuildResponsiveLayoutsOptions, ResponsiveBreakpoint, } from './buildResponsiveLayouts.js';
6
8
  export { useLocalPreference, defaultPreferenceAdapter } from './preferences.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,6BAA6B,EAC7B,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAChF,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,8BAA8B,EAC9B,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,6BAA6B,EAC7B,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAChF,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { PageGridLayout } from './PageGridLayout.js';
2
2
  export { usePageGridLayout, DEFAULT_EDITOR_EVENT_NAMES } from './usePageGridLayout.js';
3
3
  export { rescaleLayout } from './rescaleLayout.js';
4
+ export { WYSIWYG_REQUIRED_PRIMITIVES, WYSIWYG_SURFACE_CONTRACTS, assertWysiwygPrimitiveCoverage, getWysiwygSurfaceContract, } from './wysiwygContract.js';
4
5
  export { buildResponsiveLayouts, RESPONSIVE_BREAKPOINTS, } from './buildResponsiveLayouts.js';
5
6
  export { useLocalPreference, defaultPreferenceAdapter } from './preferences.js';
@@ -62,8 +62,8 @@ export function usePageGridLayout({ pageKey, defaultLayouts, defaultCols = 12, l
62
62
  setSavedLayout,
63
63
  setSavedLayoutVersion,
64
64
  ]);
65
- const layoutCols = Math.max(2, savedLayoutCols ?? defaultCols);
66
- const baseCols = Math.max(2, savedBaseCols ?? defaultCols);
65
+ const layoutCols = Math.max(1, savedLayoutCols ?? defaultCols);
66
+ const baseCols = Math.max(1, savedBaseCols ?? defaultCols);
67
67
  const rawLayouts = useMemo(() => {
68
68
  if (Object.keys(savedLayout || {}).length === 0)
69
69
  return defaultLayouts;
@@ -0,0 +1,43 @@
1
+ export type WysiwygPrimitive = 'react-flow' | 'tanstack-query' | 'resizable-cards' | 'dnd-kit';
2
+ export type WysiwygSurfaceId = 'schema-builder' | 'page-builder' | 'form-layout-builder' | 'custom-page-renderer';
3
+ export interface WysiwygSurfaceContract {
4
+ id: WysiwygSurfaceId;
5
+ ownerPackage: '@bsuite/schema-builder' | '@bsuite/page-builder' | 'crm7';
6
+ requiredPrimitives: readonly WysiwygPrimitive[];
7
+ interactionPrimitive: WysiwygPrimitive | 'read-only';
8
+ persistencePrimitive: WysiwygPrimitive | 'custom-pages-read-only' | 'page-grid-preference-adapter';
9
+ }
10
+ export declare const WYSIWYG_REQUIRED_PRIMITIVES: readonly ["react-flow", "tanstack-query", "resizable-cards", "dnd-kit"];
11
+ export declare const WYSIWYG_SURFACE_CONTRACTS: {
12
+ readonly 'schema-builder': {
13
+ readonly id: "schema-builder";
14
+ readonly ownerPackage: "@bsuite/schema-builder";
15
+ readonly requiredPrimitives: readonly ["react-flow", "tanstack-query"];
16
+ readonly interactionPrimitive: "react-flow";
17
+ readonly persistencePrimitive: "tanstack-query";
18
+ };
19
+ readonly 'page-builder': {
20
+ readonly id: "page-builder";
21
+ readonly ownerPackage: "@bsuite/page-builder";
22
+ readonly requiredPrimitives: readonly ["resizable-cards", "tanstack-query"];
23
+ readonly interactionPrimitive: "resizable-cards";
24
+ readonly persistencePrimitive: "page-grid-preference-adapter";
25
+ };
26
+ readonly 'form-layout-builder': {
27
+ readonly id: "form-layout-builder";
28
+ readonly ownerPackage: "crm7";
29
+ readonly requiredPrimitives: readonly ["dnd-kit", "tanstack-query"];
30
+ readonly interactionPrimitive: "dnd-kit";
31
+ readonly persistencePrimitive: "tanstack-query";
32
+ };
33
+ readonly 'custom-page-renderer': {
34
+ readonly id: "custom-page-renderer";
35
+ readonly ownerPackage: "@bsuite/page-builder";
36
+ readonly requiredPrimitives: readonly ["tanstack-query"];
37
+ readonly interactionPrimitive: "read-only";
38
+ readonly persistencePrimitive: "custom-pages-read-only";
39
+ };
40
+ };
41
+ export declare function getWysiwygSurfaceContract(surfaceId: WysiwygSurfaceId): WysiwygSurfaceContract;
42
+ export declare function assertWysiwygPrimitiveCoverage(contracts?: Record<WysiwygSurfaceId, WysiwygSurfaceContract>): void;
43
+ //# sourceMappingURL=wysiwygContract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wysiwygContract.d.ts","sourceRoot":"","sources":["../src/wysiwygContract.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE/F,MAAM,MAAM,gBAAgB,GACxB,gBAAgB,GAChB,cAAc,GACd,qBAAqB,GACrB,sBAAsB,CAAC;AAE3B,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,gBAAgB,CAAC;IACrB,YAAY,EAAE,wBAAwB,GAAG,sBAAsB,GAAG,MAAM,CAAC;IACzE,kBAAkB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAChD,oBAAoB,EAAE,gBAAgB,GAAG,WAAW,CAAC;IACrD,oBAAoB,EAAE,gBAAgB,GAAG,wBAAwB,GAAG,8BAA8B,CAAC;CACpG;AAED,eAAO,MAAM,2BAA2B,yEAKQ,CAAC;AAEjD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B+B,CAAC;AAEtE,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,gBAAgB,GAAG,sBAAsB,CAE7F;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,GAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAA6B,GACtF,IAAI,CAcN"}
@@ -0,0 +1,51 @@
1
+ export const WYSIWYG_REQUIRED_PRIMITIVES = [
2
+ 'react-flow',
3
+ 'tanstack-query',
4
+ 'resizable-cards',
5
+ 'dnd-kit',
6
+ ];
7
+ export const WYSIWYG_SURFACE_CONTRACTS = {
8
+ 'schema-builder': {
9
+ id: 'schema-builder',
10
+ ownerPackage: '@bsuite/schema-builder',
11
+ requiredPrimitives: ['react-flow', 'tanstack-query'],
12
+ interactionPrimitive: 'react-flow',
13
+ persistencePrimitive: 'tanstack-query',
14
+ },
15
+ 'page-builder': {
16
+ id: 'page-builder',
17
+ ownerPackage: '@bsuite/page-builder',
18
+ requiredPrimitives: ['resizable-cards', 'tanstack-query'],
19
+ interactionPrimitive: 'resizable-cards',
20
+ persistencePrimitive: 'page-grid-preference-adapter',
21
+ },
22
+ 'form-layout-builder': {
23
+ id: 'form-layout-builder',
24
+ ownerPackage: 'crm7',
25
+ requiredPrimitives: ['dnd-kit', 'tanstack-query'],
26
+ interactionPrimitive: 'dnd-kit',
27
+ persistencePrimitive: 'tanstack-query',
28
+ },
29
+ 'custom-page-renderer': {
30
+ id: 'custom-page-renderer',
31
+ ownerPackage: '@bsuite/page-builder',
32
+ requiredPrimitives: ['tanstack-query'],
33
+ interactionPrimitive: 'read-only',
34
+ persistencePrimitive: 'custom-pages-read-only',
35
+ },
36
+ };
37
+ export function getWysiwygSurfaceContract(surfaceId) {
38
+ return WYSIWYG_SURFACE_CONTRACTS[surfaceId];
39
+ }
40
+ export function assertWysiwygPrimitiveCoverage(contracts = WYSIWYG_SURFACE_CONTRACTS) {
41
+ const covered = new Set();
42
+ for (const contract of Object.values(contracts)) {
43
+ for (const primitive of contract.requiredPrimitives) {
44
+ covered.add(primitive);
45
+ }
46
+ }
47
+ const missing = WYSIWYG_REQUIRED_PRIMITIVES.filter((primitive) => !covered.has(primitive));
48
+ if (missing.length > 0) {
49
+ throw new Error(`WYSIWYG primitive coverage missing: ${missing.join(', ')}`);
50
+ }
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/page-builder",
3
- "version": "0.2.1",
3
+ "version": "0.2.4",
4
4
  "description": "Shared BSuite responsive page-builder grid and layout persistence primitives",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -58,13 +58,14 @@
58
58
  "@types/react": "^19.2.14",
59
59
  "@types/react-dom": "^19.2.3",
60
60
  "@vitejs/plugin-react": "^5.2.0",
61
- "jsdom": "^28.1.0",
62
- "lucide-react": "^0.576.0",
63
- "react": "^19.2.4",
64
- "react-dom": "^19.2.4",
65
- "react-grid-layout": "^2.2.2",
66
- "react-resizable": "^3.0.5",
67
- "typescript": "~5.7.3",
68
- "vitest": "^3.2.4"
61
+ "jsdom": "^29.1.1",
62
+ "lucide-react": "^1.14.0",
63
+ "react": "^19.2.5",
64
+ "react-dom": "^19.2.5",
65
+ "react-grid-layout": "^2.2.3",
66
+ "react-resizable": "^3.1.3",
67
+ "typescript": "~6.0.3",
68
+ "vite": "^6.4.2",
69
+ "vitest": "^4.1.5"
69
70
  }
70
71
  }