@bsuite/page-builder 0.7.0 → 0.9.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.
@@ -0,0 +1,289 @@
1
+ /**
2
+ * The card-surface contract scanner — ONE implementation, shipped from the
3
+ * package, replacing five divergent hand-rolled copies.
4
+ *
5
+ * Why this exists
6
+ * ---------------
7
+ * crm7 wrote a card-unglue contract test. BSU, conduit, throughput and braden
8
+ * each PORTED it, and each silently dropped checks during the port. The
9
+ * divergence was not cosmetic:
10
+ *
11
+ * - `findUngriddedMultiCard` — the check crm7's own comment calls "the actual
12
+ * root cause of the operator's platform-wide complaint" — was ABSENT from
13
+ * BSU, throughput and braden, and present only in a narrowed form in
14
+ * conduit. In throughput and braden it was absent structurally: their glue
15
+ * scan is gated on the file containing `CanvasCard` at all, so a page with
16
+ * two cards and no grid primitive is invisible by construction.
17
+ * - conduit's header documents THREE card idioms and its regexes detect TWO.
18
+ * The third (`rounded-lg border p-4` section panels) has no detector, which
19
+ * is how five known-glued conduit pages pass as an empty ledger.
20
+ * - BSU's scan only walks files containing the literal string
21
+ * `PageGridLayout`, so every BSU page that never adopted the grid is
22
+ * structurally invisible to BSU's own gate.
23
+ *
24
+ * A gate that cannot tell "checked nothing" from "found nothing" is not a gate
25
+ * (D-92). This module therefore ASSERTS ITS INPUTS: it fails closed if a scan
26
+ * root is missing or matches zero files, and it reports how it reached its
27
+ * verdict rather than only the verdict.
28
+ *
29
+ * What stays per-app
30
+ * ------------------
31
+ * Scan roots, card vocabulary, class-based surface patterns, and the exclusion
32
+ * ledgers. Those are operational truth about a specific app. Everything else —
33
+ * the detection logic — lives here so it can only be fixed once.
34
+ *
35
+ * Node-only: this module touches the filesystem and is exported from
36
+ * `@bsuite/page-builder/scanner`, NOT from the package root, so it never
37
+ * reaches a browser bundle.
38
+ */
39
+ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
40
+ import { join, relative, sep } from 'node:path';
41
+ const DEFAULT_GRID_PRIMITIVES = [
42
+ 'CanvasCard',
43
+ 'DraggableCardPage',
44
+ 'PageGridLayout',
45
+ 'usePageGridLayout',
46
+ ];
47
+ /**
48
+ * Strip block comments, line comments and JSX comments before matching.
49
+ *
50
+ * Three of the five hand-rolled copies matched against raw file content, which
51
+ * produced two opposite errors at once: a commented-out `<Card/><Card/>`
52
+ * example false-POSITIVES as glue, and a file that merely MENTIONS
53
+ * `CanvasCard` in a TODO comment false-NEGATIVES as "already gridded" and is
54
+ * skipped entirely. Stripping first fixes both.
55
+ */
56
+ export function stripComments(source) {
57
+ return source
58
+ .replace(/\{\s*\/\*[\s\S]*?\*\/\s*\}/g, '') // {/* JSX comment */}
59
+ .replace(/\/\*[\s\S]*?\*\//g, '') // /* block */
60
+ .replace(/(^|[^:])\/\/[^\n]*/g, '$1'); // // line, but not http://
61
+ }
62
+ function tagPattern(tags) {
63
+ // `[\s/>]` not `[\s>]`: `<Card/>` with no space before the slash was missed
64
+ // by three of the five copies. The trailing alternation keeps `<Card>` and
65
+ // `<Card ...>` matching too.
66
+ return new RegExp(`<(${tags.join('|')})[\\s/>]`, 'g');
67
+ }
68
+ function countCardTags(source, config) {
69
+ let count = (source.match(tagPattern(config.cardTags)) ?? []).length;
70
+ for (const re of config.classSurfaces ?? []) {
71
+ const global = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g');
72
+ count += (source.match(global) ?? []).length;
73
+ }
74
+ return count;
75
+ }
76
+ /**
77
+ * Does this source render cards from a `.map()`?
78
+ *
79
+ * The non-greedy `[\s\S]*?` form is deliberate. Three copies used
80
+ * `\.map\s*\([^)]*\)\s*=>` which requires a PARENTHESISED parameter, so
81
+ * `items.map(item => <Card/>)` — a bare param with an expression body — was
82
+ * missed. That gap is currently dormant across the estate but it is a landmine,
83
+ * not a theoretical concern.
84
+ */
85
+ function hasMappedCards(source, config) {
86
+ const tags = config.cardTags.join('|');
87
+ if (new RegExp(`\\.map\\s*\\([\\s\\S]*?=>\\s*\\(?\\s*<(${tags})[\\s/>]`).test(source) ||
88
+ new RegExp(`\\.map\\s*\\([\\s\\S]*?\\{[\\s\\S]*?return\\s*\\(?\\s*<(${tags})[\\s/>]`).test(source))
89
+ return true;
90
+ // A mapped CLASS surface is still a mapped card.
91
+ //
92
+ // This was missed on the first pass: the tag alternation above is built from
93
+ // `cardTags` only, so `.map(api => <div className="glass-card">…)` produced N
94
+ // cards inside one grid slot and read as clean. BSU's `Government.tsx#apis`
95
+ // is exactly that shape, and only its hand-maintained ledger knew — which is
96
+ // the failure mode this whole module exists to end. `countCardTags` already
97
+ // counts class surfaces; the mapped check has to as well or the two disagree.
98
+ for (const re of config.classSurfaces ?? []) {
99
+ const src = re.source;
100
+ if (new RegExp(`\\.map\\s*\\([\\s\\S]*?=>\\s*\\(?\\s*<[^>]*?${src}`).test(source) ||
101
+ new RegExp(`\\.map\\s*\\([\\s\\S]*?\\{[\\s\\S]*?return\\s*\\(?\\s*<[^>]*?${src}`).test(source))
102
+ return true;
103
+ }
104
+ return false;
105
+ }
106
+ /** Extract the body of every `<CanvasCard …> … </CanvasCard>` block. */
107
+ function extractCanvasCardBlocks(source) {
108
+ const blocks = [];
109
+ const re = /<CanvasCard[\s>][\s\S]*?<\/CanvasCard>/g;
110
+ let m;
111
+ while ((m = re.exec(source)) !== null)
112
+ blocks.push(m[0]);
113
+ return blocks;
114
+ }
115
+ /**
116
+ * Extract each value of an inline `widgets={{ key: <jsx/> }}` object literal.
117
+ * Brace-balanced rather than regex-terminated, because a widget body contains
118
+ * arbitrary nested JSX and braces.
119
+ */
120
+ function extractWidgetEntries(source) {
121
+ const out = [];
122
+ const start = /widgets=\{\{/g;
123
+ let m;
124
+ while ((m = start.exec(source)) !== null) {
125
+ let depth = 2;
126
+ let i = m.index + m[0].length;
127
+ const objStart = i;
128
+ while (i < source.length && depth > 0) {
129
+ if (source[i] === '{')
130
+ depth++;
131
+ else if (source[i] === '}')
132
+ depth--;
133
+ i++;
134
+ }
135
+ const obj = source.slice(objStart, i - 2);
136
+ // Split top-level `key:` entries at depth 0.
137
+ let d = 0;
138
+ let keyStart = 0;
139
+ let currentKey = null;
140
+ let bodyStart = 0;
141
+ for (let j = 0; j < obj.length; j++) {
142
+ const c = obj[j];
143
+ if (c === '{' || c === '(' || c === '[')
144
+ d++;
145
+ else if (c === '}' || c === ')' || c === ']')
146
+ d--;
147
+ else if (c === ':' && d === 0 && currentKey === null) {
148
+ currentKey = obj.slice(keyStart, j).trim().replace(/['"]/g, '');
149
+ bodyStart = j + 1;
150
+ }
151
+ else if (c === ',' && d === 0 && currentKey !== null) {
152
+ out.push({ key: currentKey, body: obj.slice(bodyStart, j) });
153
+ currentKey = null;
154
+ keyStart = j + 1;
155
+ }
156
+ }
157
+ if (currentKey !== null)
158
+ out.push({ key: currentKey, body: obj.slice(bodyStart) });
159
+ }
160
+ return out;
161
+ }
162
+ function walk(dir, extensions, acc) {
163
+ for (const entry of readdirSync(dir)) {
164
+ if (entry === 'node_modules' || entry === 'dist' || entry === '.git')
165
+ continue;
166
+ const full = join(dir, entry);
167
+ const st = statSync(full);
168
+ if (st.isDirectory())
169
+ walk(full, extensions, acc);
170
+ else if (extensions.some((e) => entry.endsWith(e)) && !/\.(test|spec)\./.test(entry))
171
+ acc.push(full);
172
+ }
173
+ }
174
+ export function scanCardSurfaces(config) {
175
+ const extensions = config.extensions ?? ['.tsx'];
176
+ const gridPrimitives = config.gridPrimitives ?? DEFAULT_GRID_PRIMITIVES;
177
+ const findings = [];
178
+ const files = [];
179
+ const scanRootsResolved = [];
180
+ // D-92: assert the inputs were present BEFORE reporting any verdict. A
181
+ // missing scan root must fail loudly, not produce a confident zero.
182
+ for (const root of config.scanRoots) {
183
+ const abs = join(config.projectRoot, root);
184
+ if (!existsSync(abs))
185
+ throw new Error(`[cardSurfaceScanner] scan root "${root}" does not exist at ${abs}. ` +
186
+ `A scanner that silently skips a missing root reports a false pass, which is worse than a failure.`);
187
+ const before = files.length;
188
+ walk(abs, extensions, files);
189
+ if (files.length === before)
190
+ throw new Error(`[cardSurfaceScanner] scan root "${root}" matched ZERO files with extensions ${extensions.join(', ')}. ` +
191
+ `Refusing to report a verdict from an empty scan.`);
192
+ scanRootsResolved.push(root);
193
+ }
194
+ // Retired single-widget primitives must not exist and must not be imported.
195
+ for (const retired of config.retiredPrimitivePaths ?? []) {
196
+ const abs = join(config.projectRoot, retired);
197
+ if (existsSync(abs))
198
+ findings.push({
199
+ file: retired,
200
+ idiom: 'retired-primitive',
201
+ detail: `${retired} still exists. It builds ONE grid item for the whole page, so every card on it ` +
202
+ `moves as a single block — the exact defect this contract exists to prevent.`,
203
+ });
204
+ const base = retired.split(sep).pop()?.replace(/\.tsx?$/, '');
205
+ if (base) {
206
+ for (const file of files) {
207
+ const src = stripComments(readFileSync(file, 'utf8'));
208
+ if (new RegExp(`\\b${base}\\b`).test(src) && !file.endsWith(retired))
209
+ findings.push({
210
+ file: relative(config.projectRoot, file),
211
+ idiom: 'retired-primitive',
212
+ detail: `imports or references the retired primitive ${base}`,
213
+ });
214
+ }
215
+ }
216
+ }
217
+ for (const file of files) {
218
+ const rel = relative(config.projectRoot, file);
219
+ const raw = readFileSync(file, 'utf8');
220
+ const src = stripComments(raw);
221
+ // --- glued widget: 2+ cards inside ONE grid slot -----------------------
222
+ const slots = [
223
+ ...extractCanvasCardBlocks(src).map((b, i) => ({ label: `CanvasCard#${i + 1}`, body: b })),
224
+ ...extractWidgetEntries(src).map((e) => ({ label: `widgets.${e.key}`, body: e.body })),
225
+ ];
226
+ for (const slot of slots) {
227
+ const n = countCardTags(slot.body, config);
228
+ const mapped = hasMappedCards(slot.body, config);
229
+ if (n > 1 || mapped) {
230
+ if (config.gluedExclusions?.[rel])
231
+ continue;
232
+ findings.push({
233
+ file: rel,
234
+ idiom: 'glued-widget',
235
+ cardCount: n,
236
+ detail: `${slot.label} packs ${mapped ? 'a .map() of cards' : `${n} card-like components`} into one grid slot. ` +
237
+ `Dragging any one of them moves them all.`,
238
+ });
239
+ }
240
+ }
241
+ // --- ungridded multi-card: the check three of five ports DROPPED -------
242
+ // Gate on the COMMENT-STRIPPED source so a TODO mentioning CanvasCard does
243
+ // not mask a page that never actually reaches the grid.
244
+ const usesGrid = gridPrimitives.some((p) => new RegExp(`\\b${p}\\b`).test(src));
245
+ if (!usesGrid) {
246
+ const n = countCardTags(src, config);
247
+ if (n > 1 && !config.ungriddedExclusions?.[rel]) {
248
+ findings.push({
249
+ file: rel,
250
+ idiom: 'ungridded-multi-card',
251
+ cardCount: n,
252
+ detail: `renders ${n} card-like surfaces and never reaches a grid primitive, so none of them is draggable at all. ` +
253
+ `This is the idiom crm7's scanner calls "the actual root cause of the operator's platform-wide complaint".`,
254
+ });
255
+ }
256
+ }
257
+ // --- autoHeight opt-out + clip invariant -------------------------------
258
+ const optOut = /autoHeight\s*[:=]\s*\{?\s*false/g;
259
+ if (optOut.test(src) && !(config.autoHeightEscapeHatch?.test(src) ?? false)) {
260
+ findings.push({
261
+ file: rel,
262
+ idiom: 'autoheight-optout',
263
+ detail: `opts out of autoHeight. That is reserved for genuinely virtualized/windowed lists that own their own scroll; ` +
264
+ `an ordinary table must expand so every row is visible.`,
265
+ });
266
+ }
267
+ // A card that opts out AND seeds h<=2 (64px) cannot show its content.
268
+ if (/autoHeight\s*[:=]\s*\{?\s*false/.test(src) && /\bh=\{?[12]\}?/.test(src)) {
269
+ findings.push({
270
+ file: rel,
271
+ idiom: 'clipped-card',
272
+ detail: `autoHeight={false} with h<=2 (<=64px) — the card is guaranteed to clip its content.`,
273
+ });
274
+ }
275
+ }
276
+ // Stale ledger entries: a claim that is no longer true is a claim that will
277
+ // be trusted next time. An exclusions ledger is a claim, not a fact.
278
+ const hit = new Set(findings.map((f) => `${f.idiom}:${f.file}`));
279
+ const staleExclusions = [
280
+ ...Object.keys(config.gluedExclusions ?? {}).filter((f) => !hit.has(`glued-widget:${f}`) && !hit.has(`autoheight-optout:${f}`)),
281
+ ...Object.keys(config.ungriddedExclusions ?? {}).filter((f) => !hit.has(`ungridded-multi-card:${f}`)),
282
+ ];
283
+ const summary = `scanned ${files.length} files across ${scanRootsResolved.length} root(s) [${scanRootsResolved.join(', ')}] ` +
284
+ `with cardTags [${config.cardTags.join(', ')}]` +
285
+ `${config.classSurfaces?.length ? ` + ${config.classSurfaces.length} class surface pattern(s)` : ''}; ` +
286
+ `${findings.length} finding(s); ${Object.keys(config.blindSpots ?? {}).length} declared blind spot(s); ` +
287
+ `${staleExclusions.length} stale exclusion(s).`;
288
+ return { findings, filesScanned: files.length, scanRootsResolved, staleExclusions, summary };
289
+ }
@@ -0,0 +1,3 @@
1
+ export { scanCardSurfaces, stripComments } from './cardSurfaceScanner.js';
2
+ export type { CardSurfaceScannerConfig, CardSurfaceScanResult, CardSurfaceFinding, CardSurfaceIdiom, } from './cardSurfaceScanner.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC1E,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1 @@
1
+ export { scanCardSurfaces, stripComments } from './cardSurfaceScanner.js';
package/dist/types.d.ts CHANGED
@@ -34,6 +34,14 @@ export interface UsePageGridLayoutOptions {
34
34
  canEditPage?: boolean;
35
35
  editorEventNames?: readonly string[];
36
36
  preferenceAdapter?: PageGridPreferenceFactory;
37
+ /**
38
+ * Value used for `GridLayoutItem.autoHeight` on any item that does not state
39
+ * one. Defaults to `true` — cards grow to fit their content rather than
40
+ * clipping it behind an inner scrollbar (D-76). Pass `false` for a surface
41
+ * whose items own their own scroll (a virtualized list); an individual item
42
+ * can always opt out with an explicit `autoHeight: false`.
43
+ */
44
+ defaultAutoHeight?: boolean;
37
45
  }
38
46
  export interface WidgetMeta {
39
47
  label: string;
@@ -162,6 +170,14 @@ export interface UsePageGridLayoutResult {
162
170
  activeCompactor: Compactor;
163
171
  onLayoutChange: (_layout: unknown, layouts: unknown) => void;
164
172
  handleColumnChange: (newCols: number) => void;
173
+ /**
174
+ * Wire to `<Responsive onBreakpointChange>`. The hook needs to know which
175
+ * breakpoint react-grid-layout is rendering so a gesture is written back to
176
+ * the breakpoint that owns it — see `canonicaliseLayoutForPersist`. Without
177
+ * this the hook assumes `lg`, and every edit made at a narrower container
178
+ * width lands in a derived breakpoint that is then discarded (D-75).
179
+ */
180
+ handleBreakpointChange: (breakpoint: string) => void;
165
181
  handleCompact: () => void;
166
182
  handleReset: () => void;
167
183
  addWidget: (widgetKey: string, initialSize?: Partial<Pick<GridLayoutItem, 'w' | 'h' | 'minW' | 'minH'>>) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -10,9 +10,30 @@ export declare const DEFAULT_EDITOR_EVENT_NAMES: readonly ["bsuite-open-page-edi
10
10
  * launchers scope by page if they handle multiple grids on one screen.
11
11
  */
12
12
  export declare const PAGE_GRID_EDITING_EVENT = "bsuite-page-grid-editing";
13
+ /**
14
+ * Package-wide layout epoch, added to EVERY consumer's `layoutVersion`.
15
+ *
16
+ * When a default that governs all grids changes, a saved layout produced under
17
+ * the old default has to be invalidated — and there is no way to reach ~330
18
+ * call sites across six apps to hand-bump each one. crm7's `DraggableCardPage`
19
+ * already solved this locally with its own `LAYOUT_EPOCH`; this is the same
20
+ * lever one level down, so it also covers the raw `PageGridLayout` consumers
21
+ * (Dashboard, Billing, GTO, Analytics, the throughput and braden pages) that
22
+ * no app-level epoch can see.
23
+ *
24
+ * 0 -> 1000 (2026-08-13, D-75/D-76): two all-grids defaults changed in the same
25
+ * release — `autoHeight` now defaults to true for raw consumers, and md/sm no
26
+ * longer collapse to a full-width stack. Every stored layout predates both and
27
+ * was additionally polluted with frozen derived breakpoints, so all of them
28
+ * reset once to the corrected defaults.
29
+ *
30
+ * Per-page `layoutVersion` bumps keep working on top; this only moves the
31
+ * floor.
32
+ */
33
+ export declare const PACKAGE_LAYOUT_EPOCH = 1000;
13
34
  export interface PageGridEditingEventDetail {
14
35
  pageKey: string;
15
36
  editing: boolean;
16
37
  }
17
- export declare function usePageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, }: UsePageGridLayoutOptions): UsePageGridLayoutResult;
38
+ export declare function usePageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, defaultAutoHeight, }: UsePageGridLayoutOptions): UsePageGridLayoutResult;
18
39
  //# sourceMappingURL=usePageGridLayout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePageGridLayout.d.ts","sourceRoot":"","sources":["../src/usePageGridLayout.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAGV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,0BAA0B,2IAM7B,CAAC;AAEX;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,cAAc,EACd,WAAgB,EAChB,aAAiB,EACjB,WAAkB,EAClB,gBAA6C,EAC7C,iBAA4C,GAC7C,EAAE,wBAAwB,GAAG,uBAAuB,CA+fpD"}
1
+ {"version":3,"file":"usePageGridLayout.d.ts","sourceRoot":"","sources":["../src/usePageGridLayout.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAGV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,0BAA0B,2IAM7B,CAAC;AAEX;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAiBzC,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,cAAc,EACd,WAAgB,EAChB,aAAiB,EACjB,WAAkB,EAClB,gBAA6C,EAC7C,iBAA4C,EAC5C,iBAAiB,GAClB,EAAE,wBAAwB,GAAG,uBAAuB,CAkmBpD"}