@bsuite/page-builder 2.8.0-next.0 → 2.8.0-next.1

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.
@@ -7,5 +7,14 @@ export declare const PAGE_GRID_SAVE_EVENT = "bsuite-page-grid-save";
7
7
  export interface PageGridSaveEventDetail {
8
8
  pageKey: string;
9
9
  }
10
+ /**
11
+ * True when a chrome-on surface's content brings no inset of its own.
12
+ *
13
+ * Walk down pure wrappers (no padding, exactly one element child) to the
14
+ * first element that does layout work, then call it bare only if it has no
15
+ * left and no top padding. A slot is never bare while anything inside it is a
16
+ * Card or a PageHeader. The in-edit label strip is skipped, not measured.
17
+ */
18
+ export declare function hasBareContent(surface: HTMLElement): boolean;
10
19
  export declare function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, widgets, widgetMeta, className, isResizable, resizeHandles, tenantId, defaultAutoHeight, layoutMigrations, itemChrome, addEntityWidgetEventNames, createEntityWidget, onRegisterEntityWidget, relationshipCatalog, addRelationshipWidgetEventNames, createRelationshipWidget, onRegisterRelationshipWidget, onRelationshipWidgetRejected, }: PageGridLayoutProps): React.JSX.Element;
11
20
  //# sourceMappingURL=PageGridLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAsBxC,OAAO,KAAK,EAAe,mBAAmB,EAA4B,MAAM,YAAY,CAAC;AAE7F,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAC5D,MAAM,WAAW,uBAAuB;IAAG,OAAO,EAAE,MAAM,CAAA;CAAE;AAmsB5D,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,gBAAgB,EAKhB,UAAkB,EAClB,yBAAiE,EACjE,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,+BAA6E,EAC7E,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,GAC7B,EAAE,mBAAmB,qBAoqCrB"}
1
+ {"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AAGA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAsBxC,OAAO,KAAK,EAAe,mBAAmB,EAA4B,MAAM,YAAY,CAAC;AAE7F,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAC5D,MAAM,WAAW,uBAAuB;IAAG,OAAO,EAAE,MAAM,CAAA;CAAE;AAyR5D;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAoB5D;AAscD,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,gBAAgB,EAKhB,UAAkB,EAClB,yBAAiE,EACjE,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,+BAA6E,EAC7E,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,GAC7B,EAAE,mBAAmB,qBAoqCrB"}
@@ -169,8 +169,42 @@ function warnOnceAboutCollapsedContent(el) {
169
169
  + '(an aspect ratio, a px/rem height, or min-height), or pass autoHeight={false} on '
170
170
  + 'this card so it renders into the constrained scrollable wrapper instead.');
171
171
  }
172
- /** Inset for a chrome-on surface whose content declares no surface of its own. */
173
- const CHROME_BARE_CONTENT_INSET = '[&:not(:has([data-slot=card])):not(:has([data-slot=page-header]))]:p-6 md:[&:not(:has([data-slot=card])):not(:has([data-slot=page-header]))]:p-7';
172
+ /** Content elements that declare a surface, and therefore an inset, of their own. */
173
+ const SELF_SURFACED = '[data-slot="card"], [data-slot="page-header"]';
174
+ /**
175
+ * True when a chrome-on surface's content brings no inset of its own.
176
+ *
177
+ * Walk down pure wrappers (no padding, exactly one element child) to the
178
+ * first element that does layout work, then call it bare only if it has no
179
+ * left and no top padding. A slot is never bare while anything inside it is a
180
+ * Card or a PageHeader. The in-edit label strip is skipped, not measured.
181
+ */
182
+ export function hasBareContent(surface) {
183
+ if (surface.querySelector(SELF_SURFACED))
184
+ return false;
185
+ const root = surface.querySelector('[data-slot="grid-item-body"]') ?? surface;
186
+ const firstContent = (el) => {
187
+ for (const child of Array.from(el.children)) {
188
+ if (child.getAttribute('data-slot') === 'grid-item-editor-chrome')
189
+ continue;
190
+ // The autoHeight measure wrapper is structure, not content.
191
+ if (child.classList.contains('flow-root') && el === root)
192
+ return firstContent(child);
193
+ return child;
194
+ }
195
+ return null;
196
+ };
197
+ let el = firstContent(root);
198
+ if (!el)
199
+ return false;
200
+ const padded = (node) => {
201
+ const cs = getComputedStyle(node);
202
+ return (Number.parseFloat(cs.paddingLeft) || 0) > 0 || (Number.parseFloat(cs.paddingTop) || 0) > 0;
203
+ };
204
+ while (!padded(el) && el.children.length === 1)
205
+ el = el.children[0];
206
+ return !padded(el);
207
+ }
174
208
  const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, isEditing, label, pageKey, onHide, editingPaused = false, autoHeight, onAutoHeightChange, chrome = false, cardStyleVars, children: injectedChildren, className: injectedClassName, style: injectedStyle, ...rest }, ref) {
175
209
  // Auto-height measurement (blueprint amendment A1). `measureRef` wraps
176
210
  // `content` with NO height constraint of its own — its parent has
@@ -196,6 +230,41 @@ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, is
196
230
  const scopedContent = (_jsx(ElementScopeProvider, { scope: { pageKey, cardKey: id, cardLabel: label, isEditing }, children: _jsx(CardPaddingBoundary, { padding: cardPadding, children: content }) }));
197
231
  const measureRef = useRef(null);
198
232
  const surfaceRef = useRef(null);
233
+ /**
234
+ * Does this chrome-on slot hold content that brings no inset of its own?
235
+ * (crm7#2734). The chrome surface paints a border and no padding, so a bare
236
+ * filter bar or hand-rolled header sat on the border. A blanket inset is
237
+ * wrong twice over: most slots nest their own Card, and many widgets pad
238
+ * themselves (`p-6`) without declaring any marker. The first release used
239
+ * a CSS `:has()` guard and double-padded exactly those widgets, which CSS
240
+ * cannot see. So the decision is MEASURED: see `hasBareContent`.
241
+ */
242
+ const [bareContent, setBareContent] = useState(false);
243
+ useLayoutEffect(() => {
244
+ const surface = surfaceRef.current;
245
+ if (!chrome || !surface) {
246
+ setBareContent(false);
247
+ return;
248
+ }
249
+ const detect = () => setBareContent(hasBareContent(surface));
250
+ detect();
251
+ if (typeof MutationObserver === 'undefined')
252
+ return;
253
+ let frame = null;
254
+ const schedule = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'
255
+ ? window.requestAnimationFrame.bind(window)
256
+ : (cb) => setTimeout(cb, 0);
257
+ const mo = new MutationObserver(() => {
258
+ if (frame !== null)
259
+ return;
260
+ frame = schedule(() => {
261
+ frame = null;
262
+ detect();
263
+ });
264
+ });
265
+ mo.observe(surface, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'data-slot', 'style'] });
266
+ return () => mo.disconnect();
267
+ }, [chrome]);
199
268
  const lastReportedRowsRef = useRef(null);
200
269
  const measureRafRef = useRef(null);
201
270
  /**
@@ -274,7 +343,7 @@ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, is
274
343
  warnOnceAboutCollapsedContent(el);
275
344
  // Re-measure when edit chrome mounts/unmounts so the strip is in the
276
345
  // row count in edit mode and gone again on lossless return.
277
- }, [autoHeight, onAutoHeightChange, reportRows, isEditing, cardStyleVars]);
346
+ }, [autoHeight, onAutoHeightChange, reportRows, isEditing, cardStyleVars, bareContent]);
278
347
  useEffect(() => {
279
348
  if (!autoHeight || !onAutoHeightChange)
280
349
  return;
@@ -326,7 +395,7 @@ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, is
326
395
  const outerClass = cn('relative group overflow-visible', isEditing && 'drag-handle',
327
396
  // Paused editing (bsuite#3277) cannot move a card, so it must not promise to.
328
397
  isEditing && !editingPaused && 'cursor-move', injectedClassName);
329
- 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" })), _jsx("div", { "data-slot": "grid-item-surface", "data-chrome": chrome ? 'on' : 'off', "data-grid-slot-key": id, ref: surfaceRef, "data-card-padding": !chrome && cardStyleVars && '--card-padding' in cardStyleVars ? '' : undefined, className: chrome
398
+ 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" })), _jsx("div", { "data-slot": "grid-item-surface", "data-chrome": chrome ? 'on' : 'off', "data-bare-content": chrome && bareContent ? '' : undefined, "data-grid-slot-key": id, ref: surfaceRef, "data-card-padding": !chrome && cardStyleVars && '--card-padding' in cardStyleVars ? '' : undefined, className: chrome
330
399
  ? // ONE radius token, read by the grid item AND available to any
331
400
  // app card that opts into the same surface. Two surfaces
332
401
  // reading the SAME var is what makes their corners align, and
@@ -366,18 +435,9 @@ const GridItem = React.memo(React.forwardRef(function GridItem({ id, content, is
366
435
  // manually-resized slot h-full is still right: the user chose that
367
436
  // height and the chrome should fill it.
368
437
  autoHeight ? 'h-fit' : 'h-full',
369
- // THE INSET, ONLY FOR CONTENT THAT BRINGS NONE (crm7#2734).
370
- // The surface painted no padding, so content that is not
371
- // itself a card sat on the border: hand-rolled headers,
372
- // filter bars, plain sections, 182 slots in crm7 alone. A
373
- // blanket inset is wrong, because most slots nest their own
374
- // Card and would gain a gutter. So the surface pads only
375
- // when nothing inside it declares its own surface or inset:
376
- // `data-slot="card"` (shadcn's convention) or
377
- // `data-slot="page-header"`. The value is PageHeader's own
378
- // p-6 md:p-7, so a bare slot and a header line up. An
379
- // operator-set --card-padding is inline style and still wins.
380
- CHROME_BARE_CONTENT_INSET)
438
+ // PageHeader's own inset, so a bare slot and a header line up.
439
+ // An operator --card-padding is inline style and still wins.
440
+ bareContent && 'p-6 md:p-7')
381
441
  : // Chrome OFF: layout only. Identical box, no paint.
382
442
  cn('w-full transition-all flex flex-col', autoHeight ? 'h-fit' : 'h-full'),
383
443
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/page-builder",
3
- "version": "2.8.0-next.0",
3
+ "version": "2.8.0-next.1",
4
4
  "description": "Shared BSuite responsive page-builder grid and layout persistence primitives",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",