@bsuite/page-builder 2.6.1 → 2.7.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.
- package/dist/DraggableCardPage.d.ts.map +1 -1
- package/dist/DraggableCardPage.js +31 -1
- package/dist/PageGridLayout.d.ts +1 -1
- package/dist/PageGridLayout.d.ts.map +1 -1
- package/dist/PageGridLayout.js +2 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/layoutMigrations.d.ts +81 -0
- package/dist/layoutMigrations.d.ts.map +1 -0
- package/dist/layoutMigrations.js +102 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/usePageGridLayout.d.ts +31 -2
- package/dist/usePageGridLayout.d.ts.map +1 -1
- package/dist/usePageGridLayout.js +135 -7
- package/package.json +1 -1
- package/dist/cardSurfaces.stories.d.ts +0 -68
- package/dist/cardSurfaces.stories.d.ts.map +0 -1
- package/dist/cardSurfaces.stories.js +0 -126
|
@@ -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,
|
|
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,EAAmB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAIvE,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,+BAyDxB;yBA/De,iBAAiB"}
|
|
@@ -105,6 +105,36 @@ export function DraggableCardPage({ layoutEpoch = 0, gridComponent, onDroppedChi
|
|
|
105
105
|
// spaces" surface had no white spaces because the grid was 1 column wide.
|
|
106
106
|
const defaultCols = pageProps.defaultCols ?? 12;
|
|
107
107
|
const Grid = gridComponent ?? DefaultPageGridLayout;
|
|
108
|
-
|
|
108
|
+
/*
|
|
109
|
+
* `layoutMigrations` keys live in the same version space as `layoutVersion`,
|
|
110
|
+
* so they cross the epoch boundary with it. A page writes the number it
|
|
111
|
+
* bumped — `layoutVersion={2}` with `{ 2: … }` — and never has to know this
|
|
112
|
+
* app's epoch, or the package's.
|
|
113
|
+
*
|
|
114
|
+
* Getting this wrong fails SILENTLY and in the worst direction: an unshifted
|
|
115
|
+
* key simply never matches the step being migrated, the version gate finds
|
|
116
|
+
* nothing registered, and it falls back to the wholesale discard the
|
|
117
|
+
* migration existed to prevent. Pinned by
|
|
118
|
+
* `DraggableCardPage.layoutMigrations.test.tsx`.
|
|
119
|
+
*
|
|
120
|
+
* Memoised because the shifted map is an effect dependency inside
|
|
121
|
+
* `usePageGridLayout`; a fresh object per render would re-run an effect that
|
|
122
|
+
* writes state.
|
|
123
|
+
*/
|
|
124
|
+
const layoutMigrations = useMemo(() => {
|
|
125
|
+
const declared = pageProps.layoutMigrations;
|
|
126
|
+
if (!declared || layoutEpoch === 0)
|
|
127
|
+
return declared;
|
|
128
|
+
const shifted = {};
|
|
129
|
+
for (const [version, migration] of Object.entries(declared)) {
|
|
130
|
+
shifted[Number(version) + layoutEpoch] = migration;
|
|
131
|
+
}
|
|
132
|
+
return shifted;
|
|
133
|
+
}, [pageProps.layoutMigrations, layoutEpoch]);
|
|
134
|
+
return (_jsx(Grid, { ...pageProps, layoutVersion: (pageProps.layoutVersion ?? 1) + layoutEpoch,
|
|
135
|
+
// AFTER the spread, deliberately — `pageProps` still carries the
|
|
136
|
+
// consumer's unshifted map, and letting the spread lay it back over this
|
|
137
|
+
// would look wired and silently discard instead of migrate.
|
|
138
|
+
layoutMigrations: layoutMigrations, defaultCols: defaultCols, widgets: widgets, defaultLayouts: layouts }));
|
|
109
139
|
}
|
|
110
140
|
DraggableCardPage.displayName = 'DraggableCardPage';
|
package/dist/PageGridLayout.d.ts
CHANGED
|
@@ -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, itemChrome, 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, layoutMigrations, 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":"AAEA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAsBxC,OAAO,KAAK,EAAe,mBAAmB,EAA4B,MAAM,YAAY,CAAC;AAmmB7F,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,
|
|
1
|
+
{"version":3,"file":"PageGridLayout.d.ts","sourceRoot":"","sources":["../src/PageGridLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AAsBxC,OAAO,KAAK,EAAe,mBAAmB,EAA4B,MAAM,YAAY,CAAC;AAmmB7F,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,qBAu+BrB"}
|
package/dist/PageGridLayout.js
CHANGED
|
@@ -354,7 +354,7 @@ export function PageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVer
|
|
|
354
354
|
// adapters then defaulted it back to true. With v2's handle gap fixed,
|
|
355
355
|
// the safe default is true so any consumer who forgets to set it still
|
|
356
356
|
// gets a usable canvas. Opt out per-page with isResizable={false}.
|
|
357
|
-
isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, tenantId, defaultAutoHeight,
|
|
357
|
+
isResizable = true, resizeHandles = DEFAULT_RESIZE_HANDLES, tenantId, defaultAutoHeight, layoutMigrations,
|
|
358
358
|
// CHROME OFF BY DEFAULT — the inversion. See `GridItemProps.chrome`.
|
|
359
359
|
// An app that is not ready to migrate its own card surfaces passes
|
|
360
360
|
// `itemChrome` to get the pre-2.0.0 behaviour back for every slot, and
|
|
@@ -369,6 +369,7 @@ itemChrome = false, addEntityWidgetEventNames = DEFAULT_ADD_ENTITY_WIDGET_EVENT_
|
|
|
369
369
|
editorEventNames,
|
|
370
370
|
preferenceAdapter,
|
|
371
371
|
defaultAutoHeight,
|
|
372
|
+
layoutMigrations,
|
|
372
373
|
});
|
|
373
374
|
// Auto-height dispatcher (blueprint amendment A1, hardened per quality
|
|
374
375
|
// review 2026-07-14). `GridItem`'s ResizeObserver calls this per-widget
|
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,10 @@ export type { DraggableCardPageProps } from './DraggableCardPage.js';
|
|
|
7
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
|
-
export { usePageGridLayout, DEFAULT_EDITOR_EVENT_NAMES, PAGE_GRID_EDITING_EVENT, } from './usePageGridLayout.js';
|
|
10
|
+
export { usePageGridLayout, migrateSavedLayout, DEFAULT_EDITOR_EVENT_NAMES, PACKAGE_LAYOUT_EPOCH, PAGE_GRID_EDITING_EVENT, } from './usePageGridLayout.js';
|
|
11
11
|
export type { PageGridEditingEventDetail } from './usePageGridLayout.js';
|
|
12
|
+
export { adoptUnchangedDefaults } from './layoutMigrations.js';
|
|
13
|
+
export type { PreviousItemDefault } from './layoutMigrations.js';
|
|
12
14
|
export { rescaleLayout } from './rescaleLayout.js';
|
|
13
15
|
export { computeAutoHeightRows } from './autoHeight.js';
|
|
14
16
|
export type { ComputeAutoHeightRowsOptions } from './autoHeight.js';
|
|
@@ -20,7 +22,7 @@ export { useLocalPreference, defaultPreferenceAdapter } from './preferences.js';
|
|
|
20
22
|
export { RelationshipField } from './RelationshipField.js';
|
|
21
23
|
export type { RelationshipFieldProps } from './RelationshipField.js';
|
|
22
24
|
export { isRelationshipWritable } from './relationshipCatalog.js';
|
|
23
|
-
export type { EntityWidgetDetail, EntityWidgetFactoryOptions, GridLayoutItem, GridLayouts, PageGridLayoutProps, PageGridPreferenceAdapter, PageGridPreferenceFactory, RelationshipCatalog, RelationshipCatalogEntry, RelationshipFieldOption, RelationshipWidgetDetail, RelationshipWidgetFactoryOptions, UsePageGridLayoutOptions, UsePageGridLayoutResult, WidgetMeta, } from './types.js';
|
|
25
|
+
export type { EntityWidgetDetail, EntityWidgetFactoryOptions, GridLayoutItem, GridLayouts, PageGridLayoutProps, PageGridPreferenceAdapter, PageGridPreferenceFactory, RelationshipCatalog, RelationshipCatalogEntry, RelationshipFieldOption, RelationshipWidgetDetail, RelationshipWidgetFactoryOptions, LayoutMigration, UsePageGridLayoutOptions, UsePageGridLayoutResult, WidgetMeta, } from './types.js';
|
|
24
26
|
export { BORDER_TONES, BORDER_STYLES, DEFAULT_CARD_STYLE, RADIUS_RANGE, BORDER_WIDTH_RANGE, PADDING_RANGE, normaliseCardStyle, isDefaultCardStyle, toCssVars as cardStyleToCssVars, describeCardStyle, } from './cardStyle.js';
|
|
25
27
|
export type { CardStyle, BorderTone, BorderStyle, Elevation } from './cardStyle.js';
|
|
26
28
|
export { ElementScopeProvider, useElementScope, elementRef, describeElement, parseElementRef, } from './elementScope.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;AAwB7C,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;AACpB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,SAAS,IAAI,kBAAkB,EAC/B,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQpF,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,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;AAwB7C,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,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjE,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,eAAe,EACf,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,SAAS,IAAI,kBAAkB,EAC/B,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQpF,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ export { PageEditorLauncher } from './PageEditorLauncher.js';
|
|
|
3
3
|
export { CanvasCard } from './CanvasCard.js';
|
|
4
4
|
export { DraggableCardPage } from './DraggableCardPage.js';
|
|
5
5
|
export { buildCanvasCardLayout, DEFAULT_CANVAS_CARD_WIDTH, flattenCanvasCards, isCanvasCardElement, describeNode, clampColumns, CANVAS_GRID_COLUMNS, } from './canvasCardLayout.js';
|
|
6
|
-
export { usePageGridLayout, DEFAULT_EDITOR_EVENT_NAMES, PAGE_GRID_EDITING_EVENT, } from './usePageGridLayout.js';
|
|
6
|
+
export { usePageGridLayout, migrateSavedLayout, DEFAULT_EDITOR_EVENT_NAMES, PACKAGE_LAYOUT_EPOCH, PAGE_GRID_EDITING_EVENT, } from './usePageGridLayout.js';
|
|
7
|
+
export { adoptUnchangedDefaults } from './layoutMigrations.js';
|
|
7
8
|
export { rescaleLayout } from './rescaleLayout.js';
|
|
8
9
|
export { computeAutoHeightRows } from './autoHeight.js';
|
|
9
10
|
export { WYSIWYG_REQUIRED_PRIMITIVES, WYSIWYG_SURFACE_CONTRACTS, assertWysiwygPrimitiveCoverage, getWysiwygSurfaceContract, } from './wysiwygContract.js';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrations for a `layoutVersion` bump — the alternative to throwing the
|
|
3
|
+
* user's arrangement away.
|
|
4
|
+
*
|
|
5
|
+
* `usePageGridLayout` had exactly one response to a version bump: overwrite
|
|
6
|
+
* the stored layout with `defaultLayouts`. That is right for the reason
|
|
7
|
+
* crm7's dashboard epoch 8 was raised — a saved layout referencing widgets
|
|
8
|
+
* that no longer exist cannot be repaired — and wrong for the reason a
|
|
9
|
+
* version usually gets bumped, which is that a DEFAULT changed. crm7#2490
|
|
10
|
+
* bumped four routes purely to change default card widths; measured on one
|
|
11
|
+
* account, that discarded 14 stored layouts.
|
|
12
|
+
*
|
|
13
|
+
* The doctrine is already in `usePageGridLayout.ts`, in the derived-breakpoint
|
|
14
|
+
* heal: "`lg` … is preserved untouched, so nobody loses the arrangement they
|
|
15
|
+
* made." A migration is how a version bump keeps that promise.
|
|
16
|
+
*/
|
|
17
|
+
import type { LayoutMigration } from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* The subset of an item's OLD default a migration compares against.
|
|
20
|
+
*
|
|
21
|
+
* `w` is required, because the whole test is "does the stored value still equal
|
|
22
|
+
* what the page used to author?". `minW` and `position` are optional: state
|
|
23
|
+
* them only where the bump actually moved them, because stating a value is what
|
|
24
|
+
* makes it a candidate for replacement.
|
|
25
|
+
*
|
|
26
|
+
* `position` is `{x, y}` and not two loose fields ON PURPOSE. Half a match is
|
|
27
|
+
* not a match: an item whose `x` still equals the old default but whose `y` does
|
|
28
|
+
* not was moved, and adopting only `x` would invent a third position that
|
|
29
|
+
* neither the user nor the page ever chose. Making the pair a single object
|
|
30
|
+
* means a caller cannot record half of it by accident.
|
|
31
|
+
*/
|
|
32
|
+
export interface PreviousItemDefault {
|
|
33
|
+
w: number;
|
|
34
|
+
minW?: number;
|
|
35
|
+
position?: {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A migration for a bump that changed DEFAULTS — widths, positions, or both.
|
|
42
|
+
*
|
|
43
|
+
* One rule, applied per item: **adopt what the user never chose, keep what they
|
|
44
|
+
* did.** A stored value still equal to the old default was never a decision; it
|
|
45
|
+
* is inherited furniture.
|
|
46
|
+
*
|
|
47
|
+
* saved position === the OLD default position
|
|
48
|
+
* -> the user never placed this card. Adopt the new default `x` and `y`
|
|
49
|
+
* (atomically — never one without the other), and go on to consider
|
|
50
|
+
* its width.
|
|
51
|
+
* saved position !== the OLD default position
|
|
52
|
+
* -> the user placed this card. It is IMMUNE: position and width are both
|
|
53
|
+
* left alone. A card someone put somewhere by hand must not silently
|
|
54
|
+
* change size underneath them either, and reflowing around a
|
|
55
|
+
* hand-placed card is how a deliberate arrangement stops making sense.
|
|
56
|
+
*
|
|
57
|
+
* saved w === the OLD default w -> adopt the new default width
|
|
58
|
+
* saved w !== the OLD default w -> the user sized it; keep it
|
|
59
|
+
*
|
|
60
|
+
* and the same comparison for `minW`. Height, `autoHeight`, `chrome`,
|
|
61
|
+
* `hUserSet` and everything else are never touched.
|
|
62
|
+
*
|
|
63
|
+
* An entry that records no `position` is a WIDTH-ONLY migration: nothing moves.
|
|
64
|
+
* That is the pre-existing behaviour and is kept so a migration written before
|
|
65
|
+
* the position rule existed cannot start relocating cards.
|
|
66
|
+
*
|
|
67
|
+
* Why position is in scope at all, since it did not start that way: treating
|
|
68
|
+
* width as inherited while treating position as sacred splits one rule into two,
|
|
69
|
+
* and produces a layout nobody authored. Measured on `/clients` — the four stat
|
|
70
|
+
* cards asked for `w={3}` and stored 4 (the default `minW` clamps `w` up), so
|
|
71
|
+
* three filled the row and the fourth wrapped. Adopting only widths narrowed
|
|
72
|
+
* them to 3 but left them at x 0/4/8 with the fourth still wrapped: neither the
|
|
73
|
+
* arrangement the user had nor the 0/3/6/9 the page now authors. Pinned in
|
|
74
|
+
* `layoutVersionMigration.test.tsx`.
|
|
75
|
+
*
|
|
76
|
+
* @param previousDefaults keyed by layout item id (`i` / `cardKey`). An item
|
|
77
|
+
* with no entry is passed through untouched, so a bump only has to name the
|
|
78
|
+
* items whose defaults actually moved.
|
|
79
|
+
*/
|
|
80
|
+
export declare function adoptUnchangedDefaults(previousDefaults: Readonly<Record<string, PreviousItemDefault>>): LayoutMigration;
|
|
81
|
+
//# sourceMappingURL=layoutMigrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layoutMigrations.d.ts","sourceRoot":"","sources":["../src/layoutMigrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAA+B,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,sBAAsB,CACpC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,GAC9D,eAAe,CA2CjB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migrations for a `layoutVersion` bump — the alternative to throwing the
|
|
3
|
+
* user's arrangement away.
|
|
4
|
+
*
|
|
5
|
+
* `usePageGridLayout` had exactly one response to a version bump: overwrite
|
|
6
|
+
* the stored layout with `defaultLayouts`. That is right for the reason
|
|
7
|
+
* crm7's dashboard epoch 8 was raised — a saved layout referencing widgets
|
|
8
|
+
* that no longer exist cannot be repaired — and wrong for the reason a
|
|
9
|
+
* version usually gets bumped, which is that a DEFAULT changed. crm7#2490
|
|
10
|
+
* bumped four routes purely to change default card widths; measured on one
|
|
11
|
+
* account, that discarded 14 stored layouts.
|
|
12
|
+
*
|
|
13
|
+
* The doctrine is already in `usePageGridLayout.ts`, in the derived-breakpoint
|
|
14
|
+
* heal: "`lg` … is preserved untouched, so nobody loses the arrangement they
|
|
15
|
+
* made." A migration is how a version bump keeps that promise.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* A migration for a bump that changed DEFAULTS — widths, positions, or both.
|
|
19
|
+
*
|
|
20
|
+
* One rule, applied per item: **adopt what the user never chose, keep what they
|
|
21
|
+
* did.** A stored value still equal to the old default was never a decision; it
|
|
22
|
+
* is inherited furniture.
|
|
23
|
+
*
|
|
24
|
+
* saved position === the OLD default position
|
|
25
|
+
* -> the user never placed this card. Adopt the new default `x` and `y`
|
|
26
|
+
* (atomically — never one without the other), and go on to consider
|
|
27
|
+
* its width.
|
|
28
|
+
* saved position !== the OLD default position
|
|
29
|
+
* -> the user placed this card. It is IMMUNE: position and width are both
|
|
30
|
+
* left alone. A card someone put somewhere by hand must not silently
|
|
31
|
+
* change size underneath them either, and reflowing around a
|
|
32
|
+
* hand-placed card is how a deliberate arrangement stops making sense.
|
|
33
|
+
*
|
|
34
|
+
* saved w === the OLD default w -> adopt the new default width
|
|
35
|
+
* saved w !== the OLD default w -> the user sized it; keep it
|
|
36
|
+
*
|
|
37
|
+
* and the same comparison for `minW`. Height, `autoHeight`, `chrome`,
|
|
38
|
+
* `hUserSet` and everything else are never touched.
|
|
39
|
+
*
|
|
40
|
+
* An entry that records no `position` is a WIDTH-ONLY migration: nothing moves.
|
|
41
|
+
* That is the pre-existing behaviour and is kept so a migration written before
|
|
42
|
+
* the position rule existed cannot start relocating cards.
|
|
43
|
+
*
|
|
44
|
+
* Why position is in scope at all, since it did not start that way: treating
|
|
45
|
+
* width as inherited while treating position as sacred splits one rule into two,
|
|
46
|
+
* and produces a layout nobody authored. Measured on `/clients` — the four stat
|
|
47
|
+
* cards asked for `w={3}` and stored 4 (the default `minW` clamps `w` up), so
|
|
48
|
+
* three filled the row and the fourth wrapped. Adopting only widths narrowed
|
|
49
|
+
* them to 3 but left them at x 0/4/8 with the fourth still wrapped: neither the
|
|
50
|
+
* arrangement the user had nor the 0/3/6/9 the page now authors. Pinned in
|
|
51
|
+
* `layoutVersionMigration.test.tsx`.
|
|
52
|
+
*
|
|
53
|
+
* @param previousDefaults keyed by layout item id (`i` / `cardKey`). An item
|
|
54
|
+
* with no entry is passed through untouched, so a bump only has to name the
|
|
55
|
+
* items whose defaults actually moved.
|
|
56
|
+
*/
|
|
57
|
+
export function adoptUnchangedDefaults(previousDefaults) {
|
|
58
|
+
return (saved, defaults) => {
|
|
59
|
+
const migrated = { lg: [] };
|
|
60
|
+
for (const breakpoint of Object.keys(saved)) {
|
|
61
|
+
const defaultItems = defaults[breakpoint] ??
|
|
62
|
+
defaults.lg ??
|
|
63
|
+
[];
|
|
64
|
+
const defaultByKey = new Map(defaultItems.map((item) => [item.i, item]));
|
|
65
|
+
migrated[breakpoint] = (saved[breakpoint] ?? []).map((item) => {
|
|
66
|
+
const previous = previousDefaults[item.i];
|
|
67
|
+
const current = defaultByKey.get(item.i);
|
|
68
|
+
// No recorded old default, or the item is gone from the new defaults:
|
|
69
|
+
// there is nothing to compare against, so nothing is a candidate.
|
|
70
|
+
if (!previous || !current)
|
|
71
|
+
return item;
|
|
72
|
+
// Position is decided FIRST, because a moved item is immune to the
|
|
73
|
+
// width rule as well — returning the item itself, not a copy, so the
|
|
74
|
+
// immunity is visible in the object identity too.
|
|
75
|
+
const placed = previous.position !== undefined &&
|
|
76
|
+
(item.x !== previous.position.x || item.y !== previous.position.y);
|
|
77
|
+
if (placed)
|
|
78
|
+
return item;
|
|
79
|
+
// A fresh object every time — the caller's array is state that other
|
|
80
|
+
// renders still hold, and mutating it in place would edit the "before"
|
|
81
|
+
// out from under anyone comparing the two.
|
|
82
|
+
const next = { ...item };
|
|
83
|
+
if (previous.position !== undefined) {
|
|
84
|
+
next.x = current.x;
|
|
85
|
+
next.y = current.y;
|
|
86
|
+
}
|
|
87
|
+
if (item.w === previous.w)
|
|
88
|
+
next.w = current.w;
|
|
89
|
+
if (previous.minW !== undefined && item.minW === previous.minW) {
|
|
90
|
+
if (current.minW === undefined)
|
|
91
|
+
delete next.minW;
|
|
92
|
+
else
|
|
93
|
+
next.minW = current.minW;
|
|
94
|
+
}
|
|
95
|
+
return next;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (!migrated.lg)
|
|
99
|
+
migrated.lg = saved.lg ?? [];
|
|
100
|
+
return migrated;
|
|
101
|
+
};
|
|
102
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -53,6 +53,17 @@ export interface GridLayouts {
|
|
|
53
53
|
lg: GridLayoutItem[];
|
|
54
54
|
[key: string]: GridLayoutItem[];
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Transforms a saved layout from the version immediately below a bump to the
|
|
58
|
+
* version at it. Pure: it is handed the stored layout and the NEW authored
|
|
59
|
+
* defaults, and returns the layout to store.
|
|
60
|
+
*
|
|
61
|
+
* Registered per target version in {@link UsePageGridLayoutOptions.layoutMigrations}.
|
|
62
|
+
* A step with no registered migration falls back to the wholesale discard,
|
|
63
|
+
* which is the correct answer when a bump's reason is that saved layouts
|
|
64
|
+
* reference widgets that no longer exist.
|
|
65
|
+
*/
|
|
66
|
+
export type LayoutMigration = (saved: GridLayouts, defaults: GridLayouts) => GridLayouts;
|
|
56
67
|
export interface PageGridPreferenceAdapter<T> {
|
|
57
68
|
value: T;
|
|
58
69
|
setValue: (value: T | ((previous: T) => T)) => void;
|
|
@@ -75,6 +86,40 @@ export interface UsePageGridLayoutOptions {
|
|
|
75
86
|
* can always opt out with an explicit `autoHeight: false`.
|
|
76
87
|
*/
|
|
77
88
|
defaultAutoHeight?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Migrations that TRANSFORM a stored layout across a `layoutVersion` bump
|
|
91
|
+
* instead of destroying it, keyed by the version each one produces.
|
|
92
|
+
*
|
|
93
|
+
* Without this, every bump has exactly one outcome: the stored layout is
|
|
94
|
+
* overwritten with `defaultLayouts` and every card position the user ever
|
|
95
|
+
* dragged on that page is gone, for every user. That is the right answer
|
|
96
|
+
* when the bump's reason is that saved layouts reference widgets which no
|
|
97
|
+
* longer exist (crm7's dashboard epoch 8), and the wrong one for the reason
|
|
98
|
+
* a version usually moves — a DEFAULT changed. crm7#2490 bumped four routes
|
|
99
|
+
* purely to change default card widths and, measured on one account, cost 14
|
|
100
|
+
* stored layouts.
|
|
101
|
+
*
|
|
102
|
+
* Keys are in the SAME version space as `layoutVersion` on the component you
|
|
103
|
+
* pass them to — `layoutVersion={2}` pairs with `{ 2: … }`. Any app-level
|
|
104
|
+
* epoch (`DraggableCardPage`'s `layoutEpoch`) and the package-level
|
|
105
|
+
* `PACKAGE_LAYOUT_EPOCH` are applied to these keys exactly as they are
|
|
106
|
+
* applied to `layoutVersion`, so a page author never writes an epoch down.
|
|
107
|
+
*
|
|
108
|
+
* Every step from the stored version up to the current one must have a
|
|
109
|
+
* registered migration; they are applied in ascending order. If ANY step in
|
|
110
|
+
* that span has none, the whole span falls back to the wholesale discard —
|
|
111
|
+
* a missing migration means "nobody has said this transition is safe", and
|
|
112
|
+
* guessing is how a layout gets silently corrupted rather than reset. A
|
|
113
|
+
* `PACKAGE_LAYOUT_EPOCH` bump therefore discards by construction, which is
|
|
114
|
+
* what that lever is for.
|
|
115
|
+
*
|
|
116
|
+
* Use a module-level constant, not an object literal in the render body:
|
|
117
|
+
* this is an effect dependency.
|
|
118
|
+
*
|
|
119
|
+
* @see adoptUnchangedDefaults for the common case — a bump that only moved
|
|
120
|
+
* default widths and/or default positions.
|
|
121
|
+
*/
|
|
122
|
+
layoutMigrations?: Readonly<Record<number, LayoutMigration>>;
|
|
78
123
|
}
|
|
79
124
|
export interface WidgetMeta {
|
|
80
125
|
label: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEjF,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,cAAc,EAAE,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC;IACpD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,EACxC,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,CAAC,KACR,yBAAyB,CAAC,CAAC,CAAC,CAAC;AAElC,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;IAC9C;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UsePageGridLayoutOptions, UsePageGridLayoutResult } from './types.js';
|
|
1
|
+
import type { GridLayouts, LayoutMigration, UsePageGridLayoutOptions, UsePageGridLayoutResult } from './types.js';
|
|
2
2
|
export declare const DEFAULT_EDITOR_EVENT_NAMES: readonly ["bsuite-open-page-editor", "bsu-open-page-editor", "crm7-open-page-editor", "conduit-open-page-editor", "r80-open-page-editor"];
|
|
3
3
|
/**
|
|
4
4
|
* Event name broadcast on `window` whenever any PageGridLayout transitions
|
|
@@ -38,9 +38,38 @@ export declare const PAGE_GRID_EDITING_EVENT = "bsuite-page-grid-editing";
|
|
|
38
38
|
* floor.
|
|
39
39
|
*/
|
|
40
40
|
export declare const PACKAGE_LAYOUT_EPOCH = 2000;
|
|
41
|
+
/**
|
|
42
|
+
* Walk a stored layout from the version it was written at up to the version
|
|
43
|
+
* the page now declares, applying one registered migration per step.
|
|
44
|
+
*
|
|
45
|
+
* Returns the migrated layout, or `null` meaning "this span cannot be
|
|
46
|
+
* migrated — discard". Discarding is not a failure mode here; it is the
|
|
47
|
+
* pre-existing behaviour, kept deliberately as the fallback. crm7's dashboard
|
|
48
|
+
* epoch 8 was raised because saved layouts referenced widgets that no longer
|
|
49
|
+
* existed, and no transformation can repair that. A step nobody registered a
|
|
50
|
+
* migration for means nobody has said the transition is safe, and inventing
|
|
51
|
+
* one silently corrupts a layout instead of resetting it.
|
|
52
|
+
*
|
|
53
|
+
* Consequences that fall out of this rule, all of them wanted:
|
|
54
|
+
* - A first visit (`from` 0) has no registered step and no stored layout to
|
|
55
|
+
* preserve, so it discards onto the defaults exactly as before.
|
|
56
|
+
* - A `PACKAGE_LAYOUT_EPOCH` bump moves the target by a thousand, so the span
|
|
57
|
+
* cannot be covered and every consumer resets — which is what that lever
|
|
58
|
+
* exists to do.
|
|
59
|
+
*
|
|
60
|
+
* Exported for direct unit testing: this is the decision, and it should be
|
|
61
|
+
* assertable without a React tree around it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function migrateSavedLayout({ saved, defaults, from, to, migrations, }: {
|
|
64
|
+
saved: GridLayouts | undefined;
|
|
65
|
+
defaults: GridLayouts;
|
|
66
|
+
from: number;
|
|
67
|
+
to: number;
|
|
68
|
+
migrations: ReadonlyMap<number, LayoutMigration> | null;
|
|
69
|
+
}): GridLayouts | null;
|
|
41
70
|
export interface PageGridEditingEventDetail {
|
|
42
71
|
pageKey: string;
|
|
43
72
|
editing: boolean;
|
|
44
73
|
}
|
|
45
|
-
export declare function usePageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, defaultAutoHeight, }: UsePageGridLayoutOptions): UsePageGridLayoutResult;
|
|
74
|
+
export declare function usePageGridLayout({ pageKey, defaultLayouts, defaultCols, layoutVersion, canEditPage, editorEventNames, preferenceAdapter, defaultAutoHeight, layoutMigrations, }: UsePageGridLayoutOptions): UsePageGridLayoutResult;
|
|
46
75
|
//# sourceMappingURL=usePageGridLayout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePageGridLayout.d.ts","sourceRoot":"","sources":["../src/usePageGridLayout.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"usePageGridLayout.d.ts","sourceRoot":"","sources":["../src/usePageGridLayout.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAEV,WAAW,EACX,eAAe,EACf,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,0BAA0B,2IAM7B,CAAC;AAEX;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAiBzC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,EAAE,EACF,UAAU,GACX,EAAE;IACD,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,QAAQ,EAAE,WAAW,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,IAAI,CAAC;CACzD,GAAG,WAAW,GAAG,IAAI,CA+BrB;AAED,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,EACjB,gBAAgB,GACjB,EAAE,wBAAwB,GAAG,uBAAuB,CA8yBpD"}
|
|
@@ -62,7 +62,65 @@ export const PACKAGE_LAYOUT_EPOCH = 2000;
|
|
|
62
62
|
* opts out with an explicit `autoHeight: false`.
|
|
63
63
|
*/
|
|
64
64
|
const DEFAULT_ITEM_AUTO_HEIGHT = true;
|
|
65
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Walk a stored layout from the version it was written at up to the version
|
|
67
|
+
* the page now declares, applying one registered migration per step.
|
|
68
|
+
*
|
|
69
|
+
* Returns the migrated layout, or `null` meaning "this span cannot be
|
|
70
|
+
* migrated — discard". Discarding is not a failure mode here; it is the
|
|
71
|
+
* pre-existing behaviour, kept deliberately as the fallback. crm7's dashboard
|
|
72
|
+
* epoch 8 was raised because saved layouts referenced widgets that no longer
|
|
73
|
+
* existed, and no transformation can repair that. A step nobody registered a
|
|
74
|
+
* migration for means nobody has said the transition is safe, and inventing
|
|
75
|
+
* one silently corrupts a layout instead of resetting it.
|
|
76
|
+
*
|
|
77
|
+
* Consequences that fall out of this rule, all of them wanted:
|
|
78
|
+
* - A first visit (`from` 0) has no registered step and no stored layout to
|
|
79
|
+
* preserve, so it discards onto the defaults exactly as before.
|
|
80
|
+
* - A `PACKAGE_LAYOUT_EPOCH` bump moves the target by a thousand, so the span
|
|
81
|
+
* cannot be covered and every consumer resets — which is what that lever
|
|
82
|
+
* exists to do.
|
|
83
|
+
*
|
|
84
|
+
* Exported for direct unit testing: this is the decision, and it should be
|
|
85
|
+
* assertable without a React tree around it.
|
|
86
|
+
*/
|
|
87
|
+
export function migrateSavedLayout({ saved, defaults, from, to, migrations, }) {
|
|
88
|
+
if (!migrations || migrations.size === 0)
|
|
89
|
+
return null;
|
|
90
|
+
if (from >= to)
|
|
91
|
+
return null;
|
|
92
|
+
// Nothing stored (or an empty object) is not an arrangement worth carrying
|
|
93
|
+
// across — and `rawLayouts` already treats an empty stored layout as "use
|
|
94
|
+
// the defaults", so migrating it would be a no-op wearing a cost.
|
|
95
|
+
if (!saved || Object.keys(saved).length === 0)
|
|
96
|
+
return null;
|
|
97
|
+
// O(1) refusal before the loop: a span wider than the number of registered
|
|
98
|
+
// migrations must contain a step with none.
|
|
99
|
+
//
|
|
100
|
+
// This is a COST guard, not a correctness one — the loop below reaches the
|
|
101
|
+
// same `null` without it, because it bails at the first version with no
|
|
102
|
+
// migration. What it saves is AT MOST `migrations.size + 1` registry lookups,
|
|
103
|
+
// reached only when the registry happens to be contiguous from `from + 1`;
|
|
104
|
+
// measured, size 1 -> 2, size 5 -> 6, size 500 -> 501. For crm7#2490's shape
|
|
105
|
+
// — one migration keyed 2103, a first visit from 0 — it is ONE lookup, since
|
|
106
|
+
// the very first miss ends the walk.
|
|
107
|
+
//
|
|
108
|
+
// An earlier version of this comment claimed a first visit would "spin two
|
|
109
|
+
// thousand times". That was false: the work is bounded by the REGISTRY, never
|
|
110
|
+
// by the span. The guard is still worth keeping, and it is worth keeping for
|
|
111
|
+
// the size it actually has.
|
|
112
|
+
if (to - from > migrations.size)
|
|
113
|
+
return null;
|
|
114
|
+
let working = saved;
|
|
115
|
+
for (let version = from + 1; version <= to; version += 1) {
|
|
116
|
+
const migration = migrations.get(version);
|
|
117
|
+
if (!migration)
|
|
118
|
+
return null;
|
|
119
|
+
working = migration(working, defaults);
|
|
120
|
+
}
|
|
121
|
+
return working;
|
|
122
|
+
}
|
|
123
|
+
export function usePageGridLayout({ pageKey, defaultLayouts, defaultCols = 12, layoutVersion = 1, canEditPage = true, editorEventNames = DEFAULT_EDITOR_EVENT_NAMES, preferenceAdapter = defaultPreferenceAdapter, defaultAutoHeight, layoutMigrations, }) {
|
|
66
124
|
const effectiveLayoutVersion = layoutVersion + PACKAGE_LAYOUT_EPOCH;
|
|
67
125
|
const containerRef = useRef(null);
|
|
68
126
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
@@ -105,21 +163,91 @@ export function usePageGridLayout({ pageKey, defaultLayouts, defaultCols = 12, l
|
|
|
105
163
|
const { value: savedLayoutCols, setValue: setLayoutCols } = preferenceAdapter(`page:${pageKey}_grid_cols`, defaultCols);
|
|
106
164
|
const { value: savedBaseCols, setValue: setBaseCols } = preferenceAdapter(`page:${pageKey}_grid_base_cols`, defaultCols);
|
|
107
165
|
const prefsLoaded = layoutLoaded && versionLoaded;
|
|
166
|
+
/*
|
|
167
|
+
* Migration keys arrive in the CONSUMER's version space — `layoutVersion={2}`
|
|
168
|
+
* pairs with `{ 2: … }` — and are shifted onto the stored (effective) space
|
|
169
|
+
* by exactly the same epoch that shifts `layoutVersion`. That symmetry is the
|
|
170
|
+
* point: a page author writes the number they bumped, and never has to know
|
|
171
|
+
* `PACKAGE_LAYOUT_EPOCH` exists. `DraggableCardPage` applies its own
|
|
172
|
+
* `layoutEpoch` to both in the same way, one level up.
|
|
173
|
+
*/
|
|
174
|
+
const effectiveMigrations = useMemo(() => {
|
|
175
|
+
if (!layoutMigrations)
|
|
176
|
+
return null;
|
|
177
|
+
const byEffectiveVersion = new Map();
|
|
178
|
+
for (const [version, migration] of Object.entries(layoutMigrations)) {
|
|
179
|
+
byEffectiveVersion.set(Number(version) + PACKAGE_LAYOUT_EPOCH, migration);
|
|
180
|
+
}
|
|
181
|
+
return byEffectiveVersion;
|
|
182
|
+
}, [layoutMigrations]);
|
|
183
|
+
/*
|
|
184
|
+
* The stored layout, mirrored for the version gate below — which must READ it
|
|
185
|
+
* without DEPENDING on it.
|
|
186
|
+
*
|
|
187
|
+
* Depending on it would re-run the gate on every drag that saves a layout,
|
|
188
|
+
* and worse, would re-run it in the window between the gate writing the
|
|
189
|
+
* migrated layout and the new version landing: an adapter that surfaces those
|
|
190
|
+
* two writes in separate renders would then apply the same migration twice.
|
|
191
|
+
* `adoptUnchangedDefaults` happens to be idempotent; a migration in general is
|
|
192
|
+
* not, and a contract that only holds for the migrations that exist today is
|
|
193
|
+
* not a contract.
|
|
194
|
+
*
|
|
195
|
+
* `useLayoutEffect`, not a render-phase assignment and not `useEffect`: all
|
|
196
|
+
* layout effects for a commit run before any passive effect, so on mount this
|
|
197
|
+
* is populated before the gate's `useEffect` reads it. Declared here, above
|
|
198
|
+
* the gate, so that ordering is visible rather than inferred.
|
|
199
|
+
*/
|
|
200
|
+
const savedLayoutForVersionGateRef = useRef(savedLayout);
|
|
201
|
+
useLayoutEffect(() => {
|
|
202
|
+
savedLayoutForVersionGateRef.current = savedLayout;
|
|
203
|
+
}, [savedLayout]);
|
|
108
204
|
useEffect(() => {
|
|
109
205
|
if (!prefsLoaded)
|
|
110
206
|
return;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
207
|
+
const storedVersion = savedLayoutVersion ?? 0;
|
|
208
|
+
if (storedVersion >= effectiveLayoutVersion)
|
|
209
|
+
return;
|
|
210
|
+
/*
|
|
211
|
+
* MIGRATE, DO NOT DISCARD.
|
|
212
|
+
*
|
|
213
|
+
* This used to have one branch: overwrite the stored layout with
|
|
214
|
+
* `defaultLayouts`. So ANY `layoutVersion` bump threw away every card
|
|
215
|
+
* position the user had ever dragged on that page, for every user — and
|
|
216
|
+
* the commonest reason to bump a version is that a DEFAULT WIDTH changed,
|
|
217
|
+
* which is a transformation, not a reason to lose an arrangement.
|
|
218
|
+
* crm7#2490 bumped four routes purely to change default widths; measured on
|
|
219
|
+
* one account (braden@braden.com.au), that was 14 layouts.
|
|
220
|
+
*
|
|
221
|
+
* The doctrine is forty lines below, in the derived-breakpoint heal: "`lg`
|
|
222
|
+
* … is preserved untouched, so nobody loses the arrangement they made."
|
|
223
|
+
* `migrateSavedLayout` returning null keeps the discard for the case that
|
|
224
|
+
* genuinely needs it.
|
|
225
|
+
*/
|
|
226
|
+
const migrated = migrateSavedLayout({
|
|
227
|
+
saved: savedLayoutForVersionGateRef.current,
|
|
228
|
+
defaults: defaultLayouts,
|
|
229
|
+
from: storedVersion,
|
|
230
|
+
to: effectiveLayoutVersion,
|
|
231
|
+
migrations: effectiveMigrations,
|
|
232
|
+
});
|
|
233
|
+
startTransition(() => {
|
|
234
|
+
setSavedLayout(migrated ?? defaultLayouts);
|
|
235
|
+
// Column choice is part of the arrangement, not part of the layout array:
|
|
236
|
+
// a migration preserves what the user set up, so it must not reset these
|
|
237
|
+
// either. A discard still does — the layout it is restoring is authored
|
|
238
|
+
// at `defaultCols`, and leaving a stale `baseCols` behind would rescale
|
|
239
|
+
// the fresh defaults against a column count nothing authored them at.
|
|
240
|
+
if (migrated === null) {
|
|
114
241
|
setLayoutCols(defaultCols);
|
|
115
242
|
setBaseCols(defaultCols);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
243
|
+
}
|
|
244
|
+
setSavedLayoutVersion(effectiveLayoutVersion);
|
|
245
|
+
});
|
|
119
246
|
}, [
|
|
120
247
|
defaultCols,
|
|
121
248
|
defaultLayouts,
|
|
122
249
|
effectiveLayoutVersion,
|
|
250
|
+
effectiveMigrations,
|
|
123
251
|
prefsLoaded,
|
|
124
252
|
savedLayoutVersion,
|
|
125
253
|
setBaseCols,
|
package/package.json
CHANGED
|
@@ -1,68 +0,0 @@
|
|
|
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
|
-
export declare const CardStyleDefault: Story;
|
|
63
|
-
export declare const CardStyleThickBorder: Story;
|
|
64
|
-
export declare const CardStyleDashedAccent: Story;
|
|
65
|
-
export declare const CardStyleSquare: Story;
|
|
66
|
-
export declare const CardStyleBorderless: Story;
|
|
67
|
-
export declare const CardStyleElevationLadder: Story;
|
|
68
|
-
//# sourceMappingURL=cardSurfaces.stories.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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;AAKtC;;;;;;;;;;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;AA2CD,eAAO,MAAM,gBAAgB,EAAE,KAK9B,CAAA;AAED,eAAO,MAAM,oBAAoB,EAAE,KAGlC,CAAA;AAED,eAAO,MAAM,qBAAqB,EAAE,KAQnC,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,KAG7B,CAAA;AAED,eAAO,MAAM,mBAAmB,EAAE,KAQjC,CAAA;AAED,eAAO,MAAM,wBAAwB,EAAE,KAatC,CAAA"}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { PageGridLayout } from './PageGridLayout.js';
|
|
3
|
-
import { DEFAULT_CARD_STYLE, describeCardStyle, toCssVars } from './cardStyle.js';
|
|
4
|
-
export default {
|
|
5
|
-
title: 'Page Builder/Card surfaces',
|
|
6
|
-
parameters: { layout: 'fullscreen' },
|
|
7
|
-
};
|
|
8
|
-
/** What an app renders inside a slot: its own card. Border, radius, background. */
|
|
9
|
-
function AppCard({ title, children }) {
|
|
10
|
-
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 })] }));
|
|
11
|
-
}
|
|
12
|
-
/** Bare content: no surface of its own. This is the 10% that NEEDS the chrome. */
|
|
13
|
-
function BareContent({ title }) {
|
|
14
|
-
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." })] }));
|
|
15
|
-
}
|
|
16
|
-
const oneSlot = { lg: [{ i: 'a', x: 0, y: 0, w: 6, h: 6 }] };
|
|
17
|
-
/**
|
|
18
|
-
* THE DEFECT, exactly as it shipped. A card inside the grid item's card:
|
|
19
|
-
* two radii, two 1px borders, two backgrounds. 316+ files render this.
|
|
20
|
-
*/
|
|
21
|
-
export const NestedCardDoubleFrame = {
|
|
22
|
-
name: 'Nested card — DOUBLE FRAME (the defect)',
|
|
23
|
-
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." }) } }) })),
|
|
24
|
-
};
|
|
25
|
-
/** The same content on 2.0.0 defaults: ONE border, ONE radius. */
|
|
26
|
-
export const NestedCardSingleFrame = {
|
|
27
|
-
name: 'Nested card — ONE frame (2.0.0 default)',
|
|
28
|
-
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." }) } }) })),
|
|
29
|
-
};
|
|
30
|
-
/** Bare content with chrome OFF — the regression the inversion could cause. */
|
|
31
|
-
export const BareContentNoChrome = {
|
|
32
|
-
name: 'Bare content, chrome OFF — the regression to watch for',
|
|
33
|
-
render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/bare-off", defaultLayouts: oneSlot, widgets: { a: _jsx(BareContent, { title: "Pipeline Overview" }) } }) })),
|
|
34
|
-
};
|
|
35
|
-
/** The same bare content opting IN per item. This is the migration for the 10%. */
|
|
36
|
-
export const BareContentOptsIn = {
|
|
37
|
-
name: 'Bare content, per-item chrome ON — the migration',
|
|
38
|
-
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" }) } }) })),
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* Every card width side by side. `w` defaulted to 12, so 1,068 of 1,729 usages
|
|
42
|
-
* rendered like the last row here — one card per row, the rest of the screen
|
|
43
|
-
* empty. That is the "cards don't use the available space" report.
|
|
44
|
-
*/
|
|
45
|
-
export const WidthLadder = {
|
|
46
|
-
name: 'Width ladder — w = 2 / 3 / 4 / 6 / 12',
|
|
47
|
-
render: () => (_jsx("div", { className: "p-8", children: _jsx(PageGridLayout, { pageKey: "sb/widths", defaultLayouts: {
|
|
48
|
-
lg: [
|
|
49
|
-
{ i: 'w2', x: 0, y: 0, w: 2, h: 4 },
|
|
50
|
-
{ i: 'w3', x: 2, y: 0, w: 3, h: 4 },
|
|
51
|
-
{ i: 'w4', x: 5, y: 0, w: 4, h: 4 },
|
|
52
|
-
{ i: 'w6', x: 0, y: 4, w: 6, h: 4 },
|
|
53
|
-
{ i: 'w12', x: 0, y: 8, w: 12, h: 4 },
|
|
54
|
-
],
|
|
55
|
-
}, widgets: {
|
|
56
|
-
w2: _jsx(AppCard, { title: "w=2" }),
|
|
57
|
-
w3: _jsx(AppCard, { title: "w=3" }),
|
|
58
|
-
w4: _jsx(AppCard, { title: "w=4" }),
|
|
59
|
-
w6: _jsx(AppCard, { title: "w=6 \u2014 the new default" }),
|
|
60
|
-
w12: _jsx(AppCard, { title: "w=12 \u2014 the old default, one card per row" }),
|
|
61
|
-
} }) })),
|
|
62
|
-
};
|
|
63
|
-
/**
|
|
64
|
-
* Card headings. `text-gradient-accent` is on 183 of 235 crm7 h1 page titles
|
|
65
|
-
* and 0 of 310 crm7 h2/h3 CARD headings — the element the operator keeps
|
|
66
|
-
* asking about. The pairing matters: the solid `text-(--role-text-heading)`
|
|
67
|
-
* sits UNDER the gradient as the fallback layer.
|
|
68
|
-
*
|
|
69
|
-
* `width: fit-content` inside the utility is load-bearing. Anything that
|
|
70
|
-
* overrides width makes a short heading sample only the first ~15% of the
|
|
71
|
-
* gradient and render flat — indistinguishable from having no gradient at all,
|
|
72
|
-
* which is why "is the class present" is not a check.
|
|
73
|
-
*/
|
|
74
|
-
export const CardHeadingGradient = {
|
|
75
|
-
name: 'Card headings — with and without the gradient',
|
|
76
|
-
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." })] })] })),
|
|
77
|
-
};
|
|
78
|
-
/* ------------------------------------------------------------------ *
|
|
79
|
-
* OPERATOR-EDITABLE CARD APPEARANCE
|
|
80
|
-
*
|
|
81
|
-
* The border, corners, shadow and padding are set by the operator in the
|
|
82
|
-
* canvas editor and persisted per page (see cardStyle.ts). These stories are
|
|
83
|
-
* the reference for what each setting actually paints, so a change to the
|
|
84
|
-
* chrome can be diffed against a picture rather than argued about.
|
|
85
|
-
*
|
|
86
|
-
* `toCssVars` returns ONLY the properties that differ from the default, so
|
|
87
|
-
* `Default` below emits no custom properties at all — it is the control, and
|
|
88
|
-
* it must look exactly like `BareContentOptsIn`. If those two ever diverge,
|
|
89
|
-
* the "costs nothing until you touch it" guarantee has been broken.
|
|
90
|
-
* ------------------------------------------------------------------ */
|
|
91
|
-
/** Renders one slot with chrome on, under a given operator card style. */
|
|
92
|
-
function StyledSurface({ style, note }) {
|
|
93
|
-
const resolved = { ...DEFAULT_CARD_STYLE, ...style };
|
|
94
|
-
const vars = toCssVars(resolved);
|
|
95
|
-
return (_jsxs("div", { className: "p-8", style: {
|
|
96
|
-
...vars,
|
|
97
|
-
// The editor applies elevation and padding as real declarations,
|
|
98
|
-
// because each has to beat a class. Mirrored here so the story shows
|
|
99
|
-
// what the page shows.
|
|
100
|
-
...('--card-shadow' in vars ? { ['--story-shadow']: vars['--card-shadow'] } : null),
|
|
101
|
-
}, children: [_jsx("p", { className: "mb-3 text-sm text-muted-foreground", children: note }), _jsx("p", { className: "mb-3 text-sm font-medium text-foreground", children: describeCardStyle(resolved) }), _jsx(PageGridLayout, { pageKey: `card-style-${note.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}`, defaultLayouts: oneSlot, itemChrome: true, widgets: { a: _jsx(BareContent, { title: "Operator-styled card" }) } })] }));
|
|
102
|
-
}
|
|
103
|
-
export const CardStyleDefault = {
|
|
104
|
-
name: 'Card style — default (emits no CSS)',
|
|
105
|
-
render: () => (_jsx(StyledSurface, { style: {}, note: "The control. No custom properties are emitted at all." })),
|
|
106
|
-
};
|
|
107
|
-
export const CardStyleThickBorder = {
|
|
108
|
-
name: 'Card style — 4px border',
|
|
109
|
-
render: () => _jsx(StyledSurface, { style: { borderWidth: 4 }, note: "Border width is a slider, 0-8px." }),
|
|
110
|
-
};
|
|
111
|
-
export const CardStyleDashedAccent = {
|
|
112
|
-
name: 'Card style — dashed accent border',
|
|
113
|
-
render: () => (_jsx(StyledSurface, { style: { borderWidth: 2, borderStyle: 'dashed', borderTone: 'accent' }, note: "Colour comes from a THEME TOKEN, never a literal, so white-labelling still applies." })),
|
|
114
|
-
};
|
|
115
|
-
export const CardStyleSquare = {
|
|
116
|
-
name: 'Card style — square corners',
|
|
117
|
-
render: () => _jsx(StyledSurface, { style: { radius: 0 }, note: "Corner radius is a slider, 0-48px." }),
|
|
118
|
-
};
|
|
119
|
-
export const CardStyleBorderless = {
|
|
120
|
-
name: 'Card style — no border',
|
|
121
|
-
render: () => (_jsx(StyledSurface, { style: { borderStyle: 'none' }, note: "A borderless card still keeps its background and radius." })),
|
|
122
|
-
};
|
|
123
|
-
export const CardStyleElevationLadder = {
|
|
124
|
-
name: 'Card style — elevation 0 to 4',
|
|
125
|
-
render: () => (_jsx("div", { className: "space-y-2", children: [0, 1, 2, 3, 4].map((level) => (_jsx(StyledSurface, { style: { elevation: level }, note: `--shadow-elev-${level}, from @bsuite/theme.` }, level))) })),
|
|
126
|
-
};
|