@bsuite/page-builder 1.0.7 → 2.0.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.
@@ -2,7 +2,14 @@ import type { ReactNode } from 'react';
2
2
  export interface CanvasCardProps {
3
3
  /** Stable widget key used by PageGridLayout for layout persistence. Required. */
4
4
  cardKey: string;
5
- /** Default grid width in columns (12-col grid). Defaults to 12. */
5
+ /**
6
+ * Grid width in columns on the 12-column grid.
7
+ *
8
+ * Defaults to **6** — half the grid, two cards per row. It defaulted to 12
9
+ * up to 1.0.7, which meant every card that omitted `w` rendered full width
10
+ * and stacked; 1,068 of 1,729 usages across the estate omit it. Say `w={12}`
11
+ * when a card genuinely wants the full row.
12
+ */
6
13
  w?: 2 | 3 | 4 | 6 | 8 | 12;
7
14
  /** Minimum width in columns when the user resizes. Defaults to 4. */
8
15
  minW?: 2 | 3 | 4 | 6 | 8 | 12;
@@ -1 +1 @@
1
- {"version":3,"file":"CanvasCard.d.ts","sourceRoot":"","sources":["../src/CanvasCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAC3B,qEAAqE;IACrE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAC9B,4FAA4F;IAC5F,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kEAAkE;IAClE,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAExD;yBAFe,UAAU"}
1
+ {"version":3,"file":"CanvasCard.d.ts","sourceRoot":"","sources":["../src/CanvasCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAC3B,qEAAqE;IACrE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAC9B,4FAA4F;IAC5F,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kEAAkE;IAClE,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAExD;yBAFe,UAAU"}
@@ -105,6 +105,14 @@ export type DraggableCardPageProps = Omit<PageGridLayoutProps, 'widgets' | 'defa
105
105
  * the element it toggles was never mounted.
106
106
  */
107
107
  onDroppedChildren?: (dropped: string[], pageKey: string) => void;
108
+ /**
109
+ * Width, in grid columns, for cards that do not set `w` themselves.
110
+ *
111
+ * Defaults to `DEFAULT_CANVAS_CARD_WIDTH` (6 — half the grid, two cards per
112
+ * row) as of 2.0.0. Pass 12 to restore the pre-2.0.0 full-width stack while
113
+ * an app migrates its pages.
114
+ */
115
+ defaultCardWidth?: number;
108
116
  /**
109
117
  * CanvasCard children. Falsy entries are ignored. Fragments are flattened so
110
118
  * a conditional multi-card branch still registers each CanvasCard.
@@ -1 +1 @@
1
- {"version":3,"file":"DraggableCardPage.d.ts","sourceRoot":"","sources":["../src/DraggableCardPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EAAW,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAItD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,mBAAmB,EACnB,SAAS,GAAG,gBAAgB,CAC7B,GAAG;IACF;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;IACnD;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAgCF,wBAAgB,iBAAiB,CAAC,EAChC,WAAe,EACf,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,GAAG,SAAS,EACb,EAAE,sBAAsB,+BA2BxB;yBAjCe,iBAAiB"}
1
+ {"version":3,"file":"DraggableCardPage.d.ts","sourceRoot":"","sources":["../src/DraggableCardPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EAAW,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAItD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,mBAAmB,EACnB,SAAS,GAAG,gBAAgB,CAC7B,GAAG;IACF;;;;;;;;;;;;;;;;;;;OAmBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;IACnD;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAgCF,wBAAgB,iBAAiB,CAAC,EAChC,WAAe,EACf,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,GAAG,SAAS,EACb,EAAE,sBAAsB,+BA2BxB;yBAjCe,iBAAiB"}
@@ -2,5 +2,5 @@ import React from 'react';
2
2
  import 'react-grid-layout/css/styles.css';
3
3
  import 'react-resizable/css/styles.css';
4
4
  import type { PageGridLayoutProps } from './types.js';
5
- export declare function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className, isResizable, resizeHandles, tenantId, defaultAutoHeight, addEntityWidgetEventNames, createEntityWidget, onRegisterEntityWidget, relationshipCatalog, addRelationshipWidgetEventNames, createRelationshipWidget, onRegisterRelationshipWidget, onRelationshipWidgetRejected, }: PageGridLayoutProps): React.JSX.Element;
5
+ export declare function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className, isResizable, resizeHandles, tenantId, defaultAutoHeight, itemChrome, addEntityWidgetEventNames, createEntityWidget, onRegisterEntityWidget, relationshipCatalog, addRelationshipWidgetEventNames, createRelationshipWidget, onRegisterRelationshipWidget, onRelationshipWidgetRejected, }: PageGridLayoutProps): React.JSX.Element;
6
6
  //# sourceMappingURL=PageGridLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AACA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAMxC,OAAO,KAAK,EAAe,mBAAmB,EAA4B,MAAM,YAAY,CAAC;AA8U7F,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,QAAQ,EACR,iBAAiB,EACjB,yBAAiE,EACjE,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,+BAA6E,EAC7E,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,GAC7B,EAAE,mBAAmB,qBAwzBrB"}
1
+ {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AACA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAMxC,OAAO,KAAK,EAAe,mBAAmB,EAA4B,MAAM,YAAY,CAAC;AA4W7F,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,QAAQ,EACR,iBAAiB,EAKjB,UAAkB,EAClB,yBAAiE,EACjE,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,+BAA6E,EAC7E,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,GAC7B,EAAE,mBAAmB,qBAyzBrB"}
@@ -84,7 +84,7 @@ const DEFAULT_ADD_RELATIONSHIP_WIDGET_EVENT_NAMES = [
84
84
  const RGLResizeHandle = React.forwardRef(function RGLResizeHandle({ axis = 'se', className: injectedClassName, ...rest }, ref) {
85
85
  return (_jsx("span", { ref: ref, "data-no-drag": true, className: `react-resizable-handle react-resizable-handle-${axis}${injectedClassName ? ' ' + injectedClassName : ''}`, ...rest }));
86
86
  });
87
- const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, isEditing, label, onHide, autoHeight, onAutoHeightChange, children: injectedChildren, className: injectedClassName, style: injectedStyle, ...rest }, ref) {
87
+ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, isEditing, label, onHide, autoHeight, onAutoHeightChange, chrome = false, children: injectedChildren, className: injectedClassName, style: injectedStyle, ...rest }, ref) {
88
88
  // Auto-height measurement (blueprint amendment A1). `measureRef` wraps
89
89
  // `content` with NO height constraint of its own — its parent has
90
90
  // `overflow-hidden` + `flex-1 min-h-0` (clips *painting* to the current
@@ -214,7 +214,18 @@ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, is
214
214
  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-primary/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-destructive-foreground shadow transition-colors", "data-no-drag": true, onPointerDown: (event) => event.stopPropagation(), onClick: (event) => {
215
215
  event.stopPropagation();
216
216
  onHide(id);
217
- }, 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 border border-border shadow-sm dark:shadow-[var(--glow-card,none)]", style: { contain: 'layout style' }, children: _jsx("div", { className: cn('flex-1 min-h-0', autoHeight ? 'overflow-hidden' : 'overflow-auto'), children: autoHeight ? _jsx("div", { ref: measureRef, children: content }) : content }) })] }), injectedChildren] }));
217
+ }, title: `Hide ${label}`, "aria-label": `Hide ${label}`, children: _jsx(EyeOff, { className: "h-3 w-3", "aria-hidden": "true" }) })), _jsx("div", { "data-slot": "grid-item-surface", "data-chrome": chrome ? 'on' : 'off', className: chrome
218
+ ? // ONE radius token, read by the grid item AND available to any
219
+ // app card that opts into the same surface. Two surfaces
220
+ // reading the SAME var is what makes their corners align, and
221
+ // it is also what keeps the radius tenant-configurable —
222
+ // a literal here would align them and lock white-label out.
223
+ // `--radius-card` defaults to today's value (1.5rem =
224
+ // rounded-3xl), so this is byte-identical until a tenant sets
225
+ // one.
226
+ 'h-full w-full rounded-[var(--radius-card,1.5rem)] transition-all flex flex-col bg-card border border-border shadow-sm dark:shadow-[var(--glow-card,none)]'
227
+ : // Chrome OFF: layout only. Identical box, no paint.
228
+ 'h-full w-full transition-all flex flex-col', style: { contain: 'layout style' }, children: _jsx("div", { className: cn('flex-1 min-h-0', autoHeight ? 'overflow-hidden' : 'overflow-auto'), children: autoHeight ? _jsx("div", { ref: measureRef, children: content }) : content }) })] }), injectedChildren] }));
218
229
  }));
219
230
  export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className,
220
231
  // Resize default flipped 2026-05-12: rgl v2 dropped auto-handle rendering
@@ -222,7 +233,12 @@ export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVer
222
233
  // adapters then defaulted it back to true. With v2's handle gap fixed,
223
234
  // the safe default is true so any consumer who forgets to set it still
224
235
  // gets a usable canvas. Opt out per-page with isResizable={false}.
225
- isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, tenantId, defaultAutoHeight, addEntityWidgetEventNames = DEFAULT_ADD_ENTITY_WIDGET_EVENT_NAMES, createEntityWidget, onRegisterEntityWidget, relationshipCatalog, addRelationshipWidgetEventNames = DEFAULT_ADD_RELATIONSHIP_WIDGET_EVENT_NAMES, createRelationshipWidget, onRegisterRelationshipWidget, onRelationshipWidgetRejected, }) {
236
+ isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, tenantId, defaultAutoHeight,
237
+ // CHROME OFF BY DEFAULT — the inversion. See `GridItemProps.chrome`.
238
+ // An app that is not ready to migrate its own card surfaces passes
239
+ // `itemChrome` to get the pre-2.0.0 behaviour back for every slot, and
240
+ // migrates page by page with the per-item `chrome` flag.
241
+ itemChrome = false, addEntityWidgetEventNames = DEFAULT_ADD_ENTITY_WIDGET_EVENT_NAMES, createEntityWidget, onRegisterEntityWidget, relationshipCatalog, addRelationshipWidgetEventNames = DEFAULT_ADD_RELATIONSHIP_WIDGET_EVENT_NAMES, createRelationshipWidget, onRegisterRelationshipWidget, onRelationshipWidgetRejected, }) {
226
242
  const { currentLayouts, layoutCols, isEditing, setIsEditing, activeCols, activeCompactor, onLayoutChange, handleColumnChange, handleBreakpointChange, handleCompact, handleReset, addWidget, moveWidget, setWidgetLocked, applyAutoHeightRows, autoHeightRows, resetConfirmOpen, setResetConfirmOpen, containerRef, containerWidth, } = usePageGridLayout({
227
243
  pageKey,
228
244
  defaultLayouts,
@@ -693,6 +709,6 @@ isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, tenantId, defaultAut
693
709
  const content = allWidgets[layoutItem.i];
694
710
  if (content === undefined || content === null)
695
711
  return null;
696
- return (_jsx(GridItem, { id: layoutItem.i, content: content, isEditing: isEditing, label: layerNames[layoutItem.i] || widgetMeta?.[layoutItem.i]?.label || layoutItem.i, onHide: hideLayer, autoHeight: layoutItem.autoHeight, onAutoHeightChange: handleAutoHeightChange }, layoutItem.i));
712
+ return (_jsx(GridItem, { id: layoutItem.i, content: content, isEditing: isEditing, label: layerNames[layoutItem.i] || widgetMeta?.[layoutItem.i]?.label || layoutItem.i, onHide: hideLayer, autoHeight: layoutItem.autoHeight, onAutoHeightChange: handleAutoHeightChange, chrome: layoutItem.chrome ?? itemChrome }, layoutItem.i));
697
713
  }) }) }) })] }));
698
714
  }
@@ -12,6 +12,26 @@ import type { CanvasCardProps } from './CanvasCard.js';
12
12
  import type { GridLayouts } from './types.js';
13
13
  /** The 12-column grid every BSuite page canvas is laid out against. */
14
14
  export declare const CANVAS_GRID_COLUMNS = 12;
15
+ /**
16
+ * The width a `CanvasCard` gets when its author does not say.
17
+ *
18
+ * SIX, i.e. half the grid — changed from twelve in 2.0.0. Twelve on a
19
+ * twelve-column grid means every card that omits `w` renders full width and
20
+ * the page becomes a single vertical stack. Measured across the estate on
21
+ * 2026-08-25: **1,068 of 1,729 CanvasCard usages omit `w`** (BSU and conduit
22
+ * omit it 100% of the time), so 62% of every card surface in BSuite was
23
+ * stacked by a DEFAULT, not by a layout decision. That is the operator's
24
+ * long-standing "the cards don't use the available space" report, and it is
25
+ * one number in one file rather than 1,068 page edits.
26
+ *
27
+ * Six rather than four: at a 1680px container half the grid is still ~820px,
28
+ * which comfortably holds a data table. Four would have traded a stacking
29
+ * complaint for a cramping one.
30
+ *
31
+ * A page that genuinely wants a full-width card still says `w={12}` — an
32
+ * explicit width always wins, and 661 usages already do exactly that.
33
+ */
34
+ export declare const DEFAULT_CANVAS_CARD_WIDTH = 6;
15
35
  export declare function isCanvasCardElement(node: ReactNode): node is ReactElement<CanvasCardProps>;
16
36
  /**
17
37
  * Best-effort display name for a node that is about to be discarded.
@@ -58,5 +78,7 @@ export interface CanvasCardLayoutResult {
58
78
  * item. (This is the fact that disproves the "dynamic card lists cannot be
59
79
  * split into static widget keys" objection recorded in several app ledgers.)
60
80
  */
61
- export declare function buildCanvasCardLayout(children: ReactNode): CanvasCardLayoutResult;
81
+ export declare function buildCanvasCardLayout(children: ReactNode, options?: {
82
+ defaultWidth?: number;
83
+ }): CanvasCardLayoutResult;
62
84
  //# sourceMappingURL=canvasCardLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"canvasCardLayout.d.ts","sourceRoot":"","sources":["../src/canvasCardLayout.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,GACd,IAAI,IAAI,YAAY,CAAC,eAAe,CAAC,CAKvC;AAQD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAQpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG;IACpD,KAAK,EAAE,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;IACvC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAqBA;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAI,EAAE,GAAG,SAAsB,UAE7E;AAED,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,yEAAyE;IACzE,OAAO,EAAE,WAAW,CAAC;IACrB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,SAAS,GAClB,sBAAsB,CA0CxB"}
1
+ {"version":3,"file":"canvasCardLayout.d.ts","sourceRoot":"","sources":["../src/canvasCardLayout.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,GACd,IAAI,IAAI,YAAY,CAAC,eAAe,CAAC,CAKvC;AAQD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAQpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG;IACpD,KAAK,EAAE,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;IACvC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAqBA;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAI,EAAE,GAAG,SAAsB,UAE7E;AAED,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,yEAAyE;IACzE,OAAO,EAAE,WAAW,CAAC;IACrB,+EAA+E;IAC/E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,SAAS,EACnB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,sBAAsB,CA2CxB"}
@@ -11,6 +11,26 @@ import { jsx as _jsx } from "react/jsx-runtime";
11
11
  import { Children, Fragment, isValidElement, } from 'react';
12
12
  /** The 12-column grid every BSuite page canvas is laid out against. */
13
13
  export const CANVAS_GRID_COLUMNS = 12;
14
+ /**
15
+ * The width a `CanvasCard` gets when its author does not say.
16
+ *
17
+ * SIX, i.e. half the grid — changed from twelve in 2.0.0. Twelve on a
18
+ * twelve-column grid means every card that omits `w` renders full width and
19
+ * the page becomes a single vertical stack. Measured across the estate on
20
+ * 2026-08-25: **1,068 of 1,729 CanvasCard usages omit `w`** (BSU and conduit
21
+ * omit it 100% of the time), so 62% of every card surface in BSuite was
22
+ * stacked by a DEFAULT, not by a layout decision. That is the operator's
23
+ * long-standing "the cards don't use the available space" report, and it is
24
+ * one number in one file rather than 1,068 page edits.
25
+ *
26
+ * Six rather than four: at a 1680px container half the grid is still ~820px,
27
+ * which comfortably holds a data table. Four would have traded a stacking
28
+ * complaint for a cramping one.
29
+ *
30
+ * A page that genuinely wants a full-width card still says `w={12}` — an
31
+ * explicit width always wins, and 661 usages already do exactly that.
32
+ */
33
+ export const DEFAULT_CANVAS_CARD_WIDTH = 6;
14
34
  export function isCanvasCardElement(node) {
15
35
  return (isValidElement(node) &&
16
36
  node.type.displayName === 'CanvasCard');
@@ -84,7 +104,8 @@ export function clampColumns(value, min = 1, max = CANVAS_GRID_COLUMNS) {
84
104
  * item. (This is the fact that disproves the "dynamic card lists cannot be
85
105
  * split into static widget keys" objection recorded in several app ledgers.)
86
106
  */
87
- export function buildCanvasCardLayout(children) {
107
+ export function buildCanvasCardLayout(children, options = {}) {
108
+ const defaultWidth = clampColumns(options.defaultWidth ?? DEFAULT_CANVAS_CARD_WIDTH);
88
109
  const widgets = {};
89
110
  const lg = [];
90
111
  let x = 0;
@@ -96,7 +117,7 @@ export function buildCanvasCardLayout(children) {
96
117
  // a card explicitly opts out for a virtualized list. See CanvasCardProps.
97
118
  const { cardKey, h = 6, minH = 2, minW = 4, autoHeight = true, children: body, } = child.props;
98
119
  const normalizedMinW = clampColumns(minW);
99
- const width = clampColumns(child.props.w ?? CANVAS_GRID_COLUMNS, normalizedMinW);
120
+ const width = clampColumns(child.props.w ?? defaultWidth, normalizedMinW);
100
121
  if (x + width > CANVAS_GRID_COLUMNS) {
101
122
  y += rowHeight;
102
123
  x = 0;
@@ -0,0 +1,62 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * The card-surface defect classes, made VISIBLE and DIFFABLE.
4
+ *
5
+ * `@bsuite/page-builder` 1.0.4, 1.0.5 and 1.0.6 each shipped GREEN AND BROKEN.
6
+ * Types, lint, unit tests and CI cannot see a 28px card sitting inside a 24px
7
+ * frame; only a rendered surface can. These stories exist so the chrome
8
+ * inversion has something to be checked against that is not a number.
9
+ *
10
+ * Read them in pairs: the story named "…(the defect)" is what shipped up to
11
+ * 1.0.7, and the one beside it is what 2.0.0 renders.
12
+ */
13
+ /**
14
+ * Plain CSF, no `Meta`/`StoryObj` type imports.
15
+ *
16
+ * `@storybook/react-vite` is a devDependency of `@bsuite/ui`, which owns the
17
+ * Storybook config; this package does not depend on Storybook and must not
18
+ * start to, or `pnpm typecheck` here fails on a type-only import for a tool
19
+ * that is not installed. CSF is a plain-object format — the indexer reads the
20
+ * default export and the named exports, and the types are optional sugar.
21
+ */
22
+ type Story = {
23
+ name?: string;
24
+ render: () => ReactNode;
25
+ };
26
+ declare const _default: {
27
+ title: string;
28
+ parameters: {
29
+ layout: string;
30
+ };
31
+ };
32
+ export default _default;
33
+ /**
34
+ * THE DEFECT, exactly as it shipped. A card inside the grid item's card:
35
+ * two radii, two 1px borders, two backgrounds. 316+ files render this.
36
+ */
37
+ export declare const NestedCardDoubleFrame: Story;
38
+ /** The same content on 2.0.0 defaults: ONE border, ONE radius. */
39
+ export declare const NestedCardSingleFrame: Story;
40
+ /** Bare content with chrome OFF — the regression the inversion could cause. */
41
+ export declare const BareContentNoChrome: Story;
42
+ /** The same bare content opting IN per item. This is the migration for the 10%. */
43
+ export declare const BareContentOptsIn: Story;
44
+ /**
45
+ * Every card width side by side. `w` defaulted to 12, so 1,068 of 1,729 usages
46
+ * rendered like the last row here — one card per row, the rest of the screen
47
+ * empty. That is the "cards don't use the available space" report.
48
+ */
49
+ export declare const WidthLadder: Story;
50
+ /**
51
+ * Card headings. `text-gradient-accent` is on 183 of 235 crm7 h1 page titles
52
+ * and 0 of 310 crm7 h2/h3 CARD headings — the element the operator keeps
53
+ * asking about. The pairing matters: the solid `text-(--role-text-heading)`
54
+ * sits UNDER the gradient as the fallback layer.
55
+ *
56
+ * `width: fit-content` inside the utility is load-bearing. Anything that
57
+ * overrides width makes a short heading sample only the first ~15% of the
58
+ * gradient and render flat — indistinguishable from having no gradient at all,
59
+ * which is why "is the class present" is not a check.
60
+ */
61
+ export declare const CardHeadingGradient: Story;
62
+ //# sourceMappingURL=cardSurfaces.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cardSurfaces.stories.d.ts","sourceRoot":"","sources":["../src/cardSurfaces.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC;;;;;;;;;;GAUG;AACH;;;;;;;;GAQG;AACH,KAAK,KAAK,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,SAAS,CAAA;CAAE,CAAA;;;;;;;AAEvD,wBAGC;AA0BD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAYnC,CAAA;AAED,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,EAAE,KAWnC,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,EAAE,KAWjC,CAAA;AAED,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,EAAE,KAW/B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,KAyBzB,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAoBjC,CAAA"}
@@ -0,0 +1,76 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PageGridLayout } from './PageGridLayout.js';
3
+ export default {
4
+ title: 'Page Builder/Card surfaces',
5
+ parameters: { layout: 'fullscreen' },
6
+ };
7
+ /** What an app renders inside a slot: its own card. Border, radius, background. */
8
+ function AppCard({ title, children }) {
9
+ return (_jsxs("div", { className: "h-full rounded-[var(--radius-card,1.5rem)] border border-border bg-card p-6 shadow-sm", children: [_jsx("h3", { className: "mb-2 text-lg font-semibold text-(--role-text-heading)", children: title }), _jsx("div", { className: "text-sm text-muted-foreground", children: children })] }));
10
+ }
11
+ /** Bare content: no surface of its own. This is the 10% that NEEDS the chrome. */
12
+ function BareContent({ title }) {
13
+ return (_jsxs("div", { className: "p-6", children: [_jsx("h3", { className: "mb-2 text-lg font-semibold text-(--role-text-heading)", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: "No surface of its own \u2014 this slot is the one that should opt IN to chrome." })] }));
14
+ }
15
+ const oneSlot = { lg: [{ i: 'a', x: 0, y: 0, w: 6, h: 6 }] };
16
+ /**
17
+ * THE DEFECT, exactly as it shipped. A card inside the grid item's card:
18
+ * two radii, two 1px borders, two backgrounds. 316+ files render this.
19
+ */
20
+ export const NestedCardDoubleFrame = {
21
+ name: 'Nested card — DOUBLE FRAME (the defect)',
22
+ render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/nested-defect", defaultLayouts: oneSlot, itemChrome: true, widgets: { a: _jsx(AppCard, { title: "Quick Actions", children: "Grid item chrome ON + app card." }) } }) })),
23
+ };
24
+ /** The same content on 2.0.0 defaults: ONE border, ONE radius. */
25
+ export const NestedCardSingleFrame = {
26
+ name: 'Nested card — ONE frame (2.0.0 default)',
27
+ render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/nested-fixed", defaultLayouts: oneSlot, widgets: { a: _jsx(AppCard, { title: "Quick Actions", children: "Grid item chrome OFF; the app card is the only surface." }) } }) })),
28
+ };
29
+ /** Bare content with chrome OFF — the regression the inversion could cause. */
30
+ export const BareContentNoChrome = {
31
+ name: 'Bare content, chrome OFF — the regression to watch for',
32
+ render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/bare-off", defaultLayouts: oneSlot, widgets: { a: _jsx(BareContent, { title: "Pipeline Overview" }) } }) })),
33
+ };
34
+ /** The same bare content opting IN per item. This is the migration for the 10%. */
35
+ export const BareContentOptsIn = {
36
+ name: 'Bare content, per-item chrome ON — the migration',
37
+ render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/bare-on", defaultLayouts: { lg: [{ i: 'a', x: 0, y: 0, w: 6, h: 6, chrome: true }] }, widgets: { a: _jsx(BareContent, { title: "Pipeline Overview" }) } }) })),
38
+ };
39
+ /**
40
+ * Every card width side by side. `w` defaulted to 12, so 1,068 of 1,729 usages
41
+ * rendered like the last row here — one card per row, the rest of the screen
42
+ * empty. That is the "cards don't use the available space" report.
43
+ */
44
+ export const WidthLadder = {
45
+ name: 'Width ladder — w = 2 / 3 / 4 / 6 / 12',
46
+ render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/widths", defaultLayouts: {
47
+ lg: [
48
+ { i: 'w2', x: 0, y: 0, w: 2, h: 4 },
49
+ { i: 'w3', x: 2, y: 0, w: 3, h: 4 },
50
+ { i: 'w4', x: 5, y: 0, w: 4, h: 4 },
51
+ { i: 'w6', x: 0, y: 4, w: 6, h: 4 },
52
+ { i: 'w12', x: 0, y: 8, w: 12, h: 4 },
53
+ ],
54
+ }, widgets: {
55
+ w2: _jsx(AppCard, { title: "w=2" }),
56
+ w3: _jsx(AppCard, { title: "w=3" }),
57
+ w4: _jsx(AppCard, { title: "w=4" }),
58
+ w6: _jsx(AppCard, { title: "w=6 \u2014 the new default" }),
59
+ w12: _jsx(AppCard, { title: "w=12 \u2014 the old default, one card per row" }),
60
+ } }) })),
61
+ };
62
+ /**
63
+ * Card headings. `text-gradient-accent` is on 183 of 235 crm7 h1 page titles
64
+ * and 0 of 310 crm7 h2/h3 CARD headings — the element the operator keeps
65
+ * asking about. The pairing matters: the solid `text-(--role-text-heading)`
66
+ * sits UNDER the gradient as the fallback layer.
67
+ *
68
+ * `width: fit-content` inside the utility is load-bearing. Anything that
69
+ * overrides width makes a short heading sample only the first ~15% of the
70
+ * gradient and render flat — indistinguishable from having no gradient at all,
71
+ * which is why "is the class present" is not a check.
72
+ */
73
+ export const CardHeadingGradient = {
74
+ name: 'Card headings — with and without the gradient',
75
+ render: () => (_jsxs("div", { className: "grid gap-8 p-8 md:grid-cols-2", children: [_jsxs("div", { className: "rounded-[var(--radius-card,1.5rem)] border border-border bg-card p-6", children: [_jsx("h3", { className: "mb-2 text-lg font-semibold text-(--role-text-heading)", children: "Quick Actions \u2014 flat (today, 0 of 310)" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "No gradient." })] }), _jsxs("div", { className: "rounded-[var(--radius-card,1.5rem)] border border-border bg-card p-6", children: [_jsx("h3", { className: "text-gradient-accent mb-2 text-lg font-semibold text-(--role-text-heading)", children: "Quick Actions \u2014 gradient (G2 target)" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "`text-gradient-accent` over the solid heading token." })] })] })),
76
+ };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { CanvasCard } from './CanvasCard.js';
4
4
  export type { CanvasCardProps } from './CanvasCard.js';
5
5
  export { DraggableCardPage } from './DraggableCardPage.js';
6
6
  export type { DraggableCardPageProps } from './DraggableCardPage.js';
7
- export { buildCanvasCardLayout, flattenCanvasCards, isCanvasCardElement, describeNode, clampColumns, CANVAS_GRID_COLUMNS, } from './canvasCardLayout.js';
7
+ export { buildCanvasCardLayout, DEFAULT_CANVAS_CARD_WIDTH, flattenCanvasCards, isCanvasCardElement, describeNode, clampColumns, CANVAS_GRID_COLUMNS, } from './canvasCardLayout.js';
8
8
  export type { CanvasCardLayoutResult } from './canvasCardLayout.js';
9
9
  export type { WidgetConfig, PageEditorLauncherProps } from './PageEditorLauncher.js';
10
10
  export { usePageGridLayout, DEFAULT_EDITOR_EVENT_NAMES, PAGE_GRID_EDITING_EVENT, } from './usePageGridLayout.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,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,YAAY,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AACpE,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,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,yBAAyB,EACzB,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,gCAAgC,EAChC,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,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,YAAY,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AACpE,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,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,yBAAyB,EACzB,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,gCAAgC,EAChC,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export { PageGridLayout } from './PageGridLayout.js';
2
2
  export { PageEditorLauncher } from './PageEditorLauncher.js';
3
3
  export { CanvasCard } from './CanvasCard.js';
4
4
  export { DraggableCardPage } from './DraggableCardPage.js';
5
- export { buildCanvasCardLayout, flattenCanvasCards, isCanvasCardElement, describeNode, clampColumns, CANVAS_GRID_COLUMNS, } from './canvasCardLayout.js';
5
+ export { buildCanvasCardLayout, DEFAULT_CANVAS_CARD_WIDTH, flattenCanvasCards, isCanvasCardElement, describeNode, clampColumns, CANVAS_GRID_COLUMNS, } from './canvasCardLayout.js';
6
6
  export { usePageGridLayout, DEFAULT_EDITOR_EVENT_NAMES, PAGE_GRID_EDITING_EVENT, } from './usePageGridLayout.js';
7
7
  export { rescaleLayout } from './rescaleLayout.js';
8
8
  export { computeAutoHeightRows } from './autoHeight.js';
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Which headings are CARD headings, and do they carry the gradient?
3
+ *
4
+ * WHY THIS EXISTS. The estate's heading gradient was implemented as a
5
+ * PAGE-TITLE convention and shipped: 183 of 232 crm7 `<h1>` page titles carry
6
+ * `.text-gradient-accent`. Card headings were never in scope, and the numbers
7
+ * say so plainly — 0 of 304 crm7 `<h2>`/`<h3>`, 15 of 734 estate-wide. The work
8
+ * was done, declared complete, and covered a DIFFERENT ELEMENT than the one the
9
+ * operator keeps pointing at. On the very dashboard he screenshotted,
10
+ * "Dashboard" (h1) has the gradient and "Quick Actions", "Communication Center"
11
+ * and "Pipeline Overview" (h2/h3) do not.
12
+ *
13
+ * SO WHY NOT JUST GREP `<h2`? Because `<h2>` is not the same thing as "card
14
+ * heading", and a sweep that treats them as equal gradients dialog titles,
15
+ * empty-state titles, section labels and table captions along with the cards.
16
+ * The operator asked for card headings. A detector that cannot tell a card
17
+ * heading from any other heading would deliver 734 changes to satisfy a request
18
+ * about roughly 600 of them, and the excess is what gets noticed.
19
+ *
20
+ * A heading is a CARD heading here when an enclosing element paints card chrome
21
+ * — a card component, or a border+radius+background surface — which is the same
22
+ * definition `cardSurfaceScanner` already uses for nested chrome. One definition
23
+ * of "card", two scanners.
24
+ *
25
+ * FAIL CLOSED. A file whose JSX tags do not balance is UNKNOWN, not clean: a
26
+ * tag stack that has lost its place cannot say what encloses what, and
27
+ * "checked nothing" must not share an exit code with "found nothing" (D-92).
28
+ *
29
+ * Node-only: filesystem access, exported from `@bsuite/page-builder/scanner`.
30
+ */
31
+ export interface CardHeadingFinding {
32
+ file: string;
33
+ /** `h2` or `h3`. */
34
+ tag: string;
35
+ /** 1-based line of the heading's opening tag. */
36
+ line: number;
37
+ /** The chrome element that makes this a card heading. */
38
+ enclosingSurface: string;
39
+ /** Does it already carry a gradient class? */
40
+ hasGradient: boolean;
41
+ /**
42
+ * Classes that fight `width: fit-content`.
43
+ *
44
+ * `.text-gradient-accent` sets `width: fit-content`, and that is load-bearing
45
+ * — `background-clip: text` paints across the ELEMENT BOX, so a block-level
46
+ * heading spanning its container samples only the first slice of the gradient
47
+ * and renders flat, which is indistinguishable from having no gradient at
48
+ * all. But a heading that is `truncate`, `flex-1`, `w-full` or `line-clamp-*`
49
+ * DEPENDS on filling its box, and shrink-wrapping it changes the layout.
50
+ * Those headings need a decision, not a codemod.
51
+ */
52
+ widthDependentClasses: string[];
53
+ }
54
+ export interface CardHeadingScanResult {
55
+ findings: CardHeadingFinding[];
56
+ filesScanned: number;
57
+ scanRootsResolved: string[];
58
+ /** Files whose JSX could not be walked. UNKNOWN is not clean. */
59
+ unknownFiles: string[];
60
+ summary: string;
61
+ }
62
+ export interface CardHeadingScannerConfig {
63
+ projectRoot: string;
64
+ scanRoots: string[];
65
+ /** Component names that count as card chrome. */
66
+ cardTags: string[];
67
+ /** Extra chrome components, as in `cardSurfaceScanner`. */
68
+ nestedChromeTags?: string[];
69
+ /** Heading tags to consider. Defaults to h2 and h3 — h1 is the page title. */
70
+ headingTags?: string[];
71
+ /** Classes that already paint a heading gradient. */
72
+ gradientClasses?: string[];
73
+ extensions?: string[];
74
+ }
75
+ interface JsxTag {
76
+ kind: 'open' | 'close' | 'self';
77
+ tag: string;
78
+ attrs: string;
79
+ index: number;
80
+ }
81
+ /**
82
+ * Tokenise JSX tags with their kind and offset.
83
+ *
84
+ * `openingTags` in the sibling module is deliberately kind-blind — it only has
85
+ * to find chrome inside a known block. Ancestry needs a stack, and a stack
86
+ * needs to know which tags close.
87
+ */
88
+ export declare function jsxTags(source: string): JsxTag[];
89
+ export declare function scanCardHeadings(config: CardHeadingScannerConfig): CardHeadingScanResult;
90
+ export {};
91
+ //# sourceMappingURL=cardHeadingScanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cardHeadingScanner.d.ts","sourceRoot":"","sources":["../../src/scanner/cardHeadingScanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,WAAW,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;OAUG;IACH,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,iEAAiE;IACjE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAkDD,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAqDhD;AAsCD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,wBAAwB,GAAG,qBAAqB,CA+FxF"}