@wordpress/grid 0.1.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/CHANGELOG.md +33 -0
- package/LICENSE.md +788 -0
- package/README.md +534 -0
- package/build/dashboard-grid/grid-item.cjs +308 -0
- package/build/dashboard-grid/grid-item.cjs.map +7 -0
- package/build/dashboard-grid/index.cjs +591 -0
- package/build/dashboard-grid/index.cjs.map +7 -0
- package/build/dashboard-grid/resolve-fill-widths.cjs +189 -0
- package/build/dashboard-grid/resolve-fill-widths.cjs.map +7 -0
- package/build/dashboard-grid/types.cjs +19 -0
- package/build/dashboard-grid/types.cjs.map +7 -0
- package/build/dashboard-lanes/index.cjs +558 -0
- package/build/dashboard-lanes/index.cjs.map +7 -0
- package/build/dashboard-lanes/lane-placement.cjs +110 -0
- package/build/dashboard-lanes/lane-placement.cjs.map +7 -0
- package/build/dashboard-lanes/lanes-item.cjs +295 -0
- package/build/dashboard-lanes/lanes-item.cjs.map +7 -0
- package/build/dashboard-lanes/types.cjs +19 -0
- package/build/dashboard-lanes/types.cjs.map +7 -0
- package/build/dashboard-lanes/use-lane-placement.cjs +206 -0
- package/build/dashboard-lanes/use-lane-placement.cjs.map +7 -0
- package/build/index.cjs +34 -0
- package/build/index.cjs.map +7 -0
- package/build/shared/drag-overlay-drop-animation.cjs +70 -0
- package/build/shared/drag-overlay-drop-animation.cjs.map +7 -0
- package/build/shared/grid-item-key.cjs +31 -0
- package/build/shared/grid-item-key.cjs.map +7 -0
- package/build/shared/grid-overlay.cjs +187 -0
- package/build/shared/grid-overlay.cjs.map +7 -0
- package/build/shared/item-exit-overlay.cjs +150 -0
- package/build/shared/item-exit-overlay.cjs.map +7 -0
- package/build/shared/resize-handle.cjs +224 -0
- package/build/shared/resize-handle.cjs.map +7 -0
- package/build/shared/resize-snap.cjs +47 -0
- package/build/shared/resize-snap.cjs.map +7 -0
- package/build/shared/types.cjs +19 -0
- package/build/shared/types.cjs.map +7 -0
- package/build/shared/use-item-exit-animation.cjs +148 -0
- package/build/shared/use-item-exit-animation.cjs.map +7 -0
- package/build/shared/use-layout-shift-animation.cjs +167 -0
- package/build/shared/use-layout-shift-animation.cjs.map +7 -0
- package/build-module/dashboard-grid/grid-item.mjs +273 -0
- package/build-module/dashboard-grid/grid-item.mjs.map +7 -0
- package/build-module/dashboard-grid/index.mjs +579 -0
- package/build-module/dashboard-grid/index.mjs.map +7 -0
- package/build-module/dashboard-grid/resolve-fill-widths.mjs +164 -0
- package/build-module/dashboard-grid/resolve-fill-widths.mjs.map +7 -0
- package/build-module/dashboard-grid/types.mjs +1 -0
- package/build-module/dashboard-grid/types.mjs.map +7 -0
- package/build-module/dashboard-lanes/index.mjs +547 -0
- package/build-module/dashboard-lanes/index.mjs.map +7 -0
- package/build-module/dashboard-lanes/lane-placement.mjs +85 -0
- package/build-module/dashboard-lanes/lane-placement.mjs.map +7 -0
- package/build-module/dashboard-lanes/lanes-item.mjs +260 -0
- package/build-module/dashboard-lanes/lanes-item.mjs.map +7 -0
- package/build-module/dashboard-lanes/types.mjs +1 -0
- package/build-module/dashboard-lanes/types.mjs.map +7 -0
- package/build-module/dashboard-lanes/use-lane-placement.mjs +181 -0
- package/build-module/dashboard-lanes/use-lane-placement.mjs.map +7 -0
- package/build-module/index.mjs +8 -0
- package/build-module/index.mjs.map +7 -0
- package/build-module/shared/drag-overlay-drop-animation.mjs +47 -0
- package/build-module/shared/drag-overlay-drop-animation.mjs.map +7 -0
- package/build-module/shared/grid-item-key.mjs +6 -0
- package/build-module/shared/grid-item-key.mjs.map +7 -0
- package/build-module/shared/grid-overlay.mjs +152 -0
- package/build-module/shared/grid-overlay.mjs.map +7 -0
- package/build-module/shared/item-exit-overlay.mjs +125 -0
- package/build-module/shared/item-exit-overlay.mjs.map +7 -0
- package/build-module/shared/resize-handle.mjs +193 -0
- package/build-module/shared/resize-handle.mjs.map +7 -0
- package/build-module/shared/resize-snap.mjs +21 -0
- package/build-module/shared/resize-snap.mjs.map +7 -0
- package/build-module/shared/types.mjs +1 -0
- package/build-module/shared/types.mjs.map +7 -0
- package/build-module/shared/use-item-exit-animation.mjs +128 -0
- package/build-module/shared/use-item-exit-animation.mjs.map +7 -0
- package/build-module/shared/use-layout-shift-animation.mjs +140 -0
- package/build-module/shared/use-layout-shift-animation.mjs.map +7 -0
- package/build-types/dashboard-grid/grid-item.d.ts +3 -0
- package/build-types/dashboard-grid/grid-item.d.ts.map +1 -0
- package/build-types/dashboard-grid/index.d.ts +35 -0
- package/build-types/dashboard-grid/index.d.ts.map +1 -0
- package/build-types/dashboard-grid/resolve-fill-widths.d.ts +26 -0
- package/build-types/dashboard-grid/resolve-fill-widths.d.ts.map +1 -0
- package/build-types/dashboard-grid/stories/index.story.d.ts +98 -0
- package/build-types/dashboard-grid/stories/index.story.d.ts.map +1 -0
- package/build-types/dashboard-grid/types.d.ts +232 -0
- package/build-types/dashboard-grid/types.d.ts.map +1 -0
- package/build-types/dashboard-lanes/index.d.ts +40 -0
- package/build-types/dashboard-lanes/index.d.ts.map +1 -0
- package/build-types/dashboard-lanes/lane-placement.d.ts +126 -0
- package/build-types/dashboard-lanes/lane-placement.d.ts.map +1 -0
- package/build-types/dashboard-lanes/lanes-item.d.ts +52 -0
- package/build-types/dashboard-lanes/lanes-item.d.ts.map +1 -0
- package/build-types/dashboard-lanes/stories/index.story.d.ts +64 -0
- package/build-types/dashboard-lanes/stories/index.story.d.ts.map +1 -0
- package/build-types/dashboard-lanes/types.d.ts +151 -0
- package/build-types/dashboard-lanes/types.d.ts.map +1 -0
- package/build-types/dashboard-lanes/use-lane-placement.d.ts +74 -0
- package/build-types/dashboard-lanes/use-lane-placement.d.ts.map +1 -0
- package/build-types/index.d.ts +6 -0
- package/build-types/index.d.ts.map +1 -0
- package/build-types/shared/drag-overlay-drop-animation.d.ts +13 -0
- package/build-types/shared/drag-overlay-drop-animation.d.ts.map +1 -0
- package/build-types/shared/grid-item-key.d.ts +6 -0
- package/build-types/shared/grid-item-key.d.ts.map +1 -0
- package/build-types/shared/grid-overlay.d.ts +19 -0
- package/build-types/shared/grid-overlay.d.ts.map +1 -0
- package/build-types/shared/item-exit-overlay.d.ts +20 -0
- package/build-types/shared/item-exit-overlay.d.ts.map +1 -0
- package/build-types/shared/resize-handle.d.ts +23 -0
- package/build-types/shared/resize-handle.d.ts.map +1 -0
- package/build-types/shared/resize-snap.d.ts +41 -0
- package/build-types/shared/resize-snap.d.ts.map +1 -0
- package/build-types/shared/types.d.ts +144 -0
- package/build-types/shared/types.d.ts.map +1 -0
- package/build-types/shared/use-item-exit-animation.d.ts +37 -0
- package/build-types/shared/use-item-exit-animation.d.ts.map +1 -0
- package/build-types/shared/use-layout-shift-animation.d.ts +77 -0
- package/build-types/shared/use-layout-shift-animation.d.ts.map +1 -0
- package/package.json +80 -0
- package/src/dashboard-grid/grid-item.module.css +94 -0
- package/src/dashboard-grid/grid-item.tsx +205 -0
- package/src/dashboard-grid/grid.module.css +134 -0
- package/src/dashboard-grid/index.tsx +713 -0
- package/src/dashboard-grid/resolve-fill-widths.ts +224 -0
- package/src/dashboard-grid/stories/index.story.tsx +930 -0
- package/src/dashboard-grid/test/keyboard-activation.test.tsx +76 -0
- package/src/dashboard-grid/test/resolve-fill-widths.test.ts +250 -0
- package/src/dashboard-grid/types.ts +271 -0
- package/src/dashboard-lanes/index.tsx +629 -0
- package/src/dashboard-lanes/lane-placement.ts +245 -0
- package/src/dashboard-lanes/lanes-item.module.css +93 -0
- package/src/dashboard-lanes/lanes-item.tsx +236 -0
- package/src/dashboard-lanes/lanes.module.css +152 -0
- package/src/dashboard-lanes/stories/index.story.tsx +518 -0
- package/src/dashboard-lanes/test/keyboard-activation.test.tsx +71 -0
- package/src/dashboard-lanes/test/lane-placement.test.ts +442 -0
- package/src/dashboard-lanes/test/use-lane-placement.test.tsx +358 -0
- package/src/dashboard-lanes/types.ts +176 -0
- package/src/dashboard-lanes/use-lane-placement.ts +313 -0
- package/src/index.ts +17 -0
- package/src/shared/actionable-area-slot.module.css +16 -0
- package/src/shared/drag-overlay-drop-animation.ts +66 -0
- package/src/shared/grid-item-key.ts +5 -0
- package/src/shared/grid-overlay.module.css +82 -0
- package/src/shared/grid-overlay.tsx +93 -0
- package/src/shared/item-exit-animation.module.css +49 -0
- package/src/shared/item-exit-overlay.tsx +57 -0
- package/src/shared/layout-shift-animation.module.css +16 -0
- package/src/shared/resize-handle.module.css +88 -0
- package/src/shared/resize-handle.tsx +163 -0
- package/src/shared/resize-snap.ts +63 -0
- package/src/shared/test/resize-snap.test.ts +35 -0
- package/src/shared/types.ts +164 -0
- package/src/shared/use-item-exit-animation.ts +199 -0
- package/src/shared/use-layout-shift-animation.ts +284 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type RectSnapshot = {
|
|
2
|
+
left: number;
|
|
3
|
+
top: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
};
|
|
7
|
+
type UseLayoutShiftAnimationOptions = {
|
|
8
|
+
/**
|
|
9
|
+
* Surface root that contains grid tiles.
|
|
10
|
+
*/
|
|
11
|
+
container: HTMLElement | null;
|
|
12
|
+
/**
|
|
13
|
+
* When false, snapshots are cleared and no transforms run.
|
|
14
|
+
*/
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Serialized layout/placement state. The hook runs FLIP when this
|
|
18
|
+
* value changes while `enabled` is true.
|
|
19
|
+
*/
|
|
20
|
+
layoutFingerprint: string;
|
|
21
|
+
/**
|
|
22
|
+
* Item key to skip (the tile being dragged or resized).
|
|
23
|
+
*/
|
|
24
|
+
excludeItemKey?: string | null;
|
|
25
|
+
};
|
|
26
|
+
type UseLayoutShiftAnimationResult = {
|
|
27
|
+
/**
|
|
28
|
+
* Capture tile positions synchronously **before** a layout update
|
|
29
|
+
* (call immediately before `setTemporaryLayout` / similar).
|
|
30
|
+
*/
|
|
31
|
+
captureLayoutSnapshot: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Container-relative rects from the last committed paint (settled, no
|
|
34
|
+
* FLIP invert transforms).
|
|
35
|
+
*/
|
|
36
|
+
getLastPositions: () => ReadonlyMap<string, RectSnapshot> | null;
|
|
37
|
+
/**
|
|
38
|
+
* Tile positions immediately before the latest layout commit. Used
|
|
39
|
+
* by item-exit animation when keys drop out of `layout`.
|
|
40
|
+
*/
|
|
41
|
+
getPositionsBeforeLastChange: () => ReadonlyMap<string, RectSnapshot> | null;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Animates sibling tiles when grid layout reflows during drag or resize
|
|
45
|
+
* using a FLIP transform (see `layout-shift-animation.module.css`).
|
|
46
|
+
*
|
|
47
|
+
* @param root0 Hook options.
|
|
48
|
+
* @param root0.container Surface root that contains grid tiles.
|
|
49
|
+
* @param root0.enabled When false, snapshots are cleared and no transforms run.
|
|
50
|
+
* @param root0.layoutFingerprint Serialized layout/placement state.
|
|
51
|
+
* @param root0.excludeItemKey Item key to skip (the tile being dragged or resized).
|
|
52
|
+
* @return Snapshot capture callback for use before layout updates.
|
|
53
|
+
*/
|
|
54
|
+
export declare function useLayoutShiftAnimation({ container, enabled, layoutFingerprint, excludeItemKey }: UseLayoutShiftAnimationOptions): UseLayoutShiftAnimationResult;
|
|
55
|
+
/**
|
|
56
|
+
* Stable fingerprint for {@link useLayoutShiftAnimation}. Width/height
|
|
57
|
+
* values may be numbers or layout keywords (`'fill'`, `'full'`).
|
|
58
|
+
*
|
|
59
|
+
* @param layout Layout items to serialize.
|
|
60
|
+
* @return Fingerprint string.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getLayoutFingerprint(layout: ReadonlyArray<{
|
|
63
|
+
key: string;
|
|
64
|
+
width?: number | string;
|
|
65
|
+
height?: number;
|
|
66
|
+
order?: number;
|
|
67
|
+
lane?: number;
|
|
68
|
+
}>): string;
|
|
69
|
+
/**
|
|
70
|
+
* Placement fingerprint for lanes polyfill / explicit grid positions.
|
|
71
|
+
*
|
|
72
|
+
* @param itemStyles Per-item inline placement styles.
|
|
73
|
+
* @return Fingerprint string.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getPlacementFingerprint(itemStyles: Map<string, React.CSSProperties>): string;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=use-layout-shift-animation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-layout-shift-animation.d.ts","sourceRoot":"","sources":["../../src/shared/use-layout-shift-animation.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,8BAA8B,GAAG;IACrC;;OAEG;IACH,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAE9B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACpC;;;OAGG;IACH,qBAAqB,EAAE,MAAM,IAAI,CAAC;IAElC;;;OAGG;IACH,gBAAgB,EAAE,MAAM,WAAW,CAAE,MAAM,EAAE,YAAY,CAAE,GAAG,IAAI,CAAC;IAEnE;;;OAGG;IACH,4BAA4B,EAAE,MAAM,WAAW,CAC9C,MAAM,EACN,YAAY,CACZ,GAAG,IAAI,CAAC;CACT,CAAC;AA0EF;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAE,EACxC,SAAS,EACT,OAAO,EACP,iBAAiB,EACjB,cAAqB,EACrB,EAAE,8BAA8B,GAAI,6BAA6B,CAkFjE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,aAAa,CAAE;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACd,CAAE,GACD,MAAM,CASR;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,GAAG,CAAE,MAAM,EAAE,KAAK,CAAC,aAAa,CAAE,GAC5C,MAAM,CAaR"}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wordpress/grid",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Grid layout components for arranging tiles in dashboard-style surfaces, with drag-to-reorder and tile resizing.",
|
|
5
|
+
"author": "The WordPress Contributors",
|
|
6
|
+
"license": "GPL-2.0-or-later",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"wordpress",
|
|
9
|
+
"gutenberg",
|
|
10
|
+
"grid",
|
|
11
|
+
"dashboard"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/packages/grid/README.md",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/WordPress/gutenberg.git",
|
|
17
|
+
"directory": "packages/grid"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/WordPress/gutenberg/issues"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20.10.0",
|
|
24
|
+
"npm": ">=10.2.3"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"src",
|
|
28
|
+
"build",
|
|
29
|
+
"build-module",
|
|
30
|
+
"build-types",
|
|
31
|
+
"*.md"
|
|
32
|
+
],
|
|
33
|
+
"main": "build/index.cjs",
|
|
34
|
+
"module": "build-module/index.mjs",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./build-types/index.d.ts",
|
|
38
|
+
"import": "./build-module/index.mjs",
|
|
39
|
+
"default": "./build/index.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
"wpScript": false,
|
|
44
|
+
"types": "build-types",
|
|
45
|
+
"sideEffects": [
|
|
46
|
+
"src/**/*.module.css"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@dnd-kit/core": "^6.3.1",
|
|
50
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
51
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
52
|
+
"@wordpress/compose": "^8.2.0",
|
|
53
|
+
"@wordpress/element": "^8.1.0",
|
|
54
|
+
"@wordpress/style-runtime": "^0.5.0",
|
|
55
|
+
"clsx": "^2.1.1"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@storybook/react-vite": "^10.4.3",
|
|
59
|
+
"@testing-library/dom": "^10.4.1",
|
|
60
|
+
"@testing-library/react": "^16.3.2",
|
|
61
|
+
"@types/jest": "^29.5.14",
|
|
62
|
+
"@wordpress/icons": "^15.0.0",
|
|
63
|
+
"@wordpress/ui": "^0.16.0",
|
|
64
|
+
"storybook": "^10.4.3"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@types/react": "^18.3.27",
|
|
68
|
+
"react": "^18.0.0",
|
|
69
|
+
"react-dom": "^18.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependenciesMeta": {
|
|
72
|
+
"@types/react": {
|
|
73
|
+
"optional": true
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
79
|
+
"gitHead": "0e7112a4f4fde4ea15bd9060489b8f6fe11eb6ca"
|
|
80
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
.item {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.item-content {
|
|
6
|
+
position: relative;
|
|
7
|
+
height: 100%;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.is-resizing {
|
|
11
|
+
overflow: visible;
|
|
12
|
+
z-index: 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.is-resizing .item-content {
|
|
16
|
+
position: relative;
|
|
17
|
+
z-index: 2;
|
|
18
|
+
overflow: visible;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* During drag, the original item acts as a placeholder in its grid
|
|
23
|
+
* cell while `<DragOverlay>` renders a clone that follows the cursor.
|
|
24
|
+
* Fading the placeholder and outlining it makes the destination visible
|
|
25
|
+
* without any scaling or translation on the original element.
|
|
26
|
+
*
|
|
27
|
+
* Placeholder chrome waits until `data-wp-grid-dragging` is set and the
|
|
28
|
+
* drag-preview enter animation (`--wpds-motion-duration-sm`) finishes
|
|
29
|
+
* so the dashed outline does not flash under the lifting clone.
|
|
30
|
+
*/
|
|
31
|
+
.is-dragging {
|
|
32
|
+
pointer-events: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
:global([data-wp-grid-dragging]) .is-dragging {
|
|
36
|
+
border-radius: var(--wp-grid-placeholder-radius, 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@media not (prefers-reduced-motion: reduce) {
|
|
40
|
+
:global([data-wp-grid-dragging]) .is-dragging {
|
|
41
|
+
opacity: 1;
|
|
42
|
+
outline-width: 0;
|
|
43
|
+
outline-style: var(--wp-grid-placeholder-outline-style, dashed);
|
|
44
|
+
outline-color: transparent;
|
|
45
|
+
animation:
|
|
46
|
+
wp-grid-item-placeholder-in 0ms linear
|
|
47
|
+
var(--wpds-motion-duration-sm) forwards;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@keyframes wp-grid-item-placeholder-in {
|
|
51
|
+
to {
|
|
52
|
+
opacity: var(--wp-grid-placeholder-opacity, 0.4);
|
|
53
|
+
outline-width: var(--wpds-border-width-sm);
|
|
54
|
+
outline-color: var(--wp-grid-placeholder-outline-color, var(--wpds-color-stroke-interactive-brand));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media (prefers-reduced-motion: reduce) {
|
|
60
|
+
:global([data-wp-grid-dragging]) .is-dragging {
|
|
61
|
+
opacity: var(--wp-grid-placeholder-opacity, 0.4);
|
|
62
|
+
outline:
|
|
63
|
+
var(--wpds-border-width-sm)
|
|
64
|
+
var(--wp-grid-placeholder-outline-style, dashed)
|
|
65
|
+
var(--wp-grid-placeholder-outline-color, var(--wpds-color-stroke-interactive-brand));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@media (forced-colors: active) {
|
|
70
|
+
:global([data-wp-grid-dragging]) .is-dragging {
|
|
71
|
+
--wp-grid-placeholder-outline-color: Highlight;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.preview-overlay {
|
|
76
|
+
position: absolute;
|
|
77
|
+
top: 0;
|
|
78
|
+
inset-inline-start: 0;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
pointer-events: none;
|
|
81
|
+
z-index: 0;
|
|
82
|
+
border:
|
|
83
|
+
var(--wpds-border-width-sm)
|
|
84
|
+
var(--wp-grid-resize-preview-outline-style, solid)
|
|
85
|
+
var(--wp-grid-placeholder-outline-color, var(--wpds-color-stroke-interactive-brand));
|
|
86
|
+
background: transparent;
|
|
87
|
+
border-radius: var(--wp-grid-placeholder-radius, 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@media (forced-colors: active) {
|
|
91
|
+
.preview-overlay {
|
|
92
|
+
border-color: Highlight;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useSortable } from '@dnd-kit/sortable';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* WordPress dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { useState, useRef } from '@wordpress/element';
|
|
11
|
+
import { useMergeRefs } from '@wordpress/compose';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Internal dependencies
|
|
15
|
+
*/
|
|
16
|
+
import actionableAreaStyles from '../shared/actionable-area-slot.module.css';
|
|
17
|
+
import { GRID_ITEM_DATA_KEY } from '../shared/grid-item-key';
|
|
18
|
+
import ResizeHandle from '../shared/resize-handle';
|
|
19
|
+
import { clampResizeDelta, type ResizeSnapSize } from '../shared/resize-snap';
|
|
20
|
+
import type { ResizeDelta } from '../shared/types';
|
|
21
|
+
import type { GridItemProps } from './types';
|
|
22
|
+
import styles from './grid-item.module.css';
|
|
23
|
+
|
|
24
|
+
function getItemCursor(
|
|
25
|
+
disabled: boolean,
|
|
26
|
+
interacting: boolean
|
|
27
|
+
): React.CSSProperties[ 'cursor' ] {
|
|
28
|
+
if ( disabled ) {
|
|
29
|
+
return 'default';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if ( interacting ) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 'grab';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function GridItem( {
|
|
40
|
+
item,
|
|
41
|
+
maxColumns,
|
|
42
|
+
disabled = false,
|
|
43
|
+
verticalResizable = true,
|
|
44
|
+
interacting = false,
|
|
45
|
+
dragging = false,
|
|
46
|
+
children,
|
|
47
|
+
actionableArea = null,
|
|
48
|
+
onResize,
|
|
49
|
+
onResizeEnd,
|
|
50
|
+
resizeSnapPreview = null,
|
|
51
|
+
minResizeWidthPx,
|
|
52
|
+
minResizeHeightPx,
|
|
53
|
+
renderResizeHandle,
|
|
54
|
+
}: GridItemProps ) {
|
|
55
|
+
const [ resizeDelta, setResizeDelta ] = useState< ResizeDelta | null >(
|
|
56
|
+
null
|
|
57
|
+
);
|
|
58
|
+
const [ initialContentSize, setInitialContentSize ] = useState< {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
} | null >( null );
|
|
62
|
+
const itemRef = useRef< HTMLDivElement >( null );
|
|
63
|
+
const contentRef = useRef< HTMLDivElement >( null );
|
|
64
|
+
const {
|
|
65
|
+
attributes,
|
|
66
|
+
listeners,
|
|
67
|
+
setNodeRef,
|
|
68
|
+
setActivatorNodeRef,
|
|
69
|
+
isDragging,
|
|
70
|
+
} = useSortable( {
|
|
71
|
+
id: item.key,
|
|
72
|
+
disabled,
|
|
73
|
+
} );
|
|
74
|
+
const mergedRef = useMergeRefs( [ itemRef, setNodeRef ] );
|
|
75
|
+
const contentMergedRef = useMergeRefs( [ contentRef ] );
|
|
76
|
+
/*
|
|
77
|
+
* With `<DragOverlay>` handling the cursor-following clone, the
|
|
78
|
+
* sortable item stays put in its grid cell and acts as a
|
|
79
|
+
* placeholder. No `transform` is applied here — applying one
|
|
80
|
+
* would double-move the placeholder alongside the overlay.
|
|
81
|
+
*/
|
|
82
|
+
const style = {
|
|
83
|
+
gridColumnEnd: `span ${
|
|
84
|
+
item.width === 'full'
|
|
85
|
+
? maxColumns
|
|
86
|
+
: Math.min(
|
|
87
|
+
typeof item.width === 'number' ? item.width : 1,
|
|
88
|
+
maxColumns
|
|
89
|
+
)
|
|
90
|
+
}`,
|
|
91
|
+
gridRowEnd: `span ${ item.height || 1 }`,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const isResizing = resizeDelta !== null;
|
|
95
|
+
const itemClassName = clsx(
|
|
96
|
+
styles.item,
|
|
97
|
+
isDragging && styles[ 'is-dragging' ],
|
|
98
|
+
isResizing && styles[ 'is-resizing' ]
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const handleResize = ( delta: ResizeDelta ) => {
|
|
102
|
+
const contentNode = contentRef.current;
|
|
103
|
+
let baselineSize = initialContentSize;
|
|
104
|
+
if ( contentNode && ! baselineSize ) {
|
|
105
|
+
const { width, height } = contentNode.getBoundingClientRect();
|
|
106
|
+
baselineSize = { width, height };
|
|
107
|
+
setInitialContentSize( baselineSize );
|
|
108
|
+
}
|
|
109
|
+
let clamped: ResizeDelta = {
|
|
110
|
+
width: delta.width,
|
|
111
|
+
height: verticalResizable ? delta.height : 0,
|
|
112
|
+
};
|
|
113
|
+
if ( baselineSize ) {
|
|
114
|
+
clamped = clampResizeDelta( clamped, baselineSize, {
|
|
115
|
+
width: minResizeWidthPx,
|
|
116
|
+
height: verticalResizable ? minResizeHeightPx : undefined,
|
|
117
|
+
} );
|
|
118
|
+
}
|
|
119
|
+
setResizeDelta( clamped );
|
|
120
|
+
onResize( item.key, clamped );
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleResizeEnd = () => {
|
|
124
|
+
setResizeDelta( null );
|
|
125
|
+
setInitialContentSize( null );
|
|
126
|
+
onResizeEnd();
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const continuousContentStyle: React.CSSProperties | undefined =
|
|
130
|
+
resizeDelta && initialContentSize
|
|
131
|
+
? {
|
|
132
|
+
width: initialContentSize.width + resizeDelta.width,
|
|
133
|
+
height: verticalResizable
|
|
134
|
+
? initialContentSize.height + resizeDelta.height
|
|
135
|
+
: undefined,
|
|
136
|
+
}
|
|
137
|
+
: undefined;
|
|
138
|
+
|
|
139
|
+
const previewOverlay = resizeSnapPreview ? (
|
|
140
|
+
<SnapPreviewOverlay snap={ resizeSnapPreview } />
|
|
141
|
+
) : null;
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div
|
|
145
|
+
ref={ mergedRef }
|
|
146
|
+
className={ itemClassName }
|
|
147
|
+
style={ style }
|
|
148
|
+
{ ...{ [ GRID_ITEM_DATA_KEY ]: item.key } }
|
|
149
|
+
data-wp-grid-item-resizing={ isResizing || undefined }
|
|
150
|
+
>
|
|
151
|
+
{ actionableArea ? (
|
|
152
|
+
<div
|
|
153
|
+
className={ actionableAreaStyles[ 'actionable-area-slot' ] }
|
|
154
|
+
>
|
|
155
|
+
<div
|
|
156
|
+
style={ { display: 'contents' } }
|
|
157
|
+
{ ...( dragging ? { inert: '' } : {} ) }
|
|
158
|
+
>
|
|
159
|
+
{ actionableArea }
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
) : null }
|
|
163
|
+
|
|
164
|
+
<div
|
|
165
|
+
ref={ setActivatorNodeRef }
|
|
166
|
+
{ ...attributes }
|
|
167
|
+
{ ...listeners }
|
|
168
|
+
style={ {
|
|
169
|
+
height: '100%',
|
|
170
|
+
cursor: getItemCursor( disabled, interacting ),
|
|
171
|
+
} }
|
|
172
|
+
>
|
|
173
|
+
<div
|
|
174
|
+
ref={ contentMergedRef }
|
|
175
|
+
className={ styles[ 'item-content' ] }
|
|
176
|
+
style={ continuousContentStyle }
|
|
177
|
+
>
|
|
178
|
+
{ children }
|
|
179
|
+
{ ! disabled && (
|
|
180
|
+
<ResizeHandle
|
|
181
|
+
itemId={ item.key }
|
|
182
|
+
verticalResizable={ verticalResizable }
|
|
183
|
+
onResize={ handleResize }
|
|
184
|
+
onResizeEnd={ handleResizeEnd }
|
|
185
|
+
renderResizeHandle={ renderResizeHandle }
|
|
186
|
+
/>
|
|
187
|
+
) }
|
|
188
|
+
</div>
|
|
189
|
+
{ previewOverlay }
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function SnapPreviewOverlay( { snap }: { snap: ResizeSnapSize } ) {
|
|
196
|
+
return (
|
|
197
|
+
<div
|
|
198
|
+
className={ styles[ 'preview-overlay' ] }
|
|
199
|
+
style={ {
|
|
200
|
+
width: snap.widthPx,
|
|
201
|
+
height: snap.heightPx ?? '100%',
|
|
202
|
+
} }
|
|
203
|
+
/>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
.grid {
|
|
2
|
+
display: grid;
|
|
3
|
+
gap: var(--wp-grid-gap, var(--wpds-dimension-gap-xl));
|
|
4
|
+
position: relative;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Functional frame for the drag-preview clone inside `<DragOverlay>`.
|
|
9
|
+
* Always applied, including when the consumer passes a
|
|
10
|
+
* `renderDragPreview` wrapper. The outer frame has no `transform` so
|
|
11
|
+
* @dnd-kit’s drop translation matches the placeholder; scale lives on
|
|
12
|
+
* `__lift` (see dnd-kit #398). Owns elevation, radius, cursor, and
|
|
13
|
+
* pointer pass-through. Further chrome belongs to the consumer. Drag
|
|
14
|
+
* elevation animates `xs` → `md` so it reads above edit tiles at `xs`.
|
|
15
|
+
*/
|
|
16
|
+
.drag-preview-frame {
|
|
17
|
+
height: 100%;
|
|
18
|
+
border-radius: var(--wp-grid-drag-preview-radius, 0);
|
|
19
|
+
cursor: grabbing;
|
|
20
|
+
pointer-events: none;
|
|
21
|
+
box-shadow: var(--wpds-elevation-md);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.drag-preview-frame__lift {
|
|
25
|
+
height: 100%;
|
|
26
|
+
transform: scale(var(--wp-grid-drag-preview-scale, 1.05));
|
|
27
|
+
transform-origin: center;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@media not (prefers-reduced-motion: reduce) {
|
|
31
|
+
.drag-preview-frame {
|
|
32
|
+
animation:
|
|
33
|
+
wp-grid-drag-preview-shadow-enter
|
|
34
|
+
var(--wpds-motion-duration-sm) var(--wpds-motion-easing-balanced) both;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.drag-preview-frame__lift {
|
|
38
|
+
animation:
|
|
39
|
+
wp-grid-drag-preview-scale-enter
|
|
40
|
+
var(--wpds-motion-duration-sm) var(--wpds-motion-easing-balanced) both;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@media not (prefers-reduced-motion: reduce) {
|
|
45
|
+
@keyframes wp-grid-drag-preview-shadow-enter {
|
|
46
|
+
from {
|
|
47
|
+
box-shadow: var(--wpds-elevation-xs);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
to {
|
|
51
|
+
box-shadow: var(--wpds-elevation-md);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes wp-grid-drag-preview-scale-enter {
|
|
56
|
+
from {
|
|
57
|
+
transform: scale(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
to {
|
|
61
|
+
transform: scale(var(--wp-grid-drag-preview-scale, 1.05));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* Applied by @dnd-kit `DragOverlay` drop side-effects while the
|
|
68
|
+
* default overlay transform runs; duration matches
|
|
69
|
+
* `createDashboardDragDropAnimation` (200ms / md token). Use keyframed
|
|
70
|
+
* exit (not transition) so scale does not snap when the enter animation
|
|
71
|
+
* is replaced.
|
|
72
|
+
*/
|
|
73
|
+
@media not (prefers-reduced-motion: reduce) {
|
|
74
|
+
.drag-preview-frame.dragPreviewFrameExiting {
|
|
75
|
+
animation:
|
|
76
|
+
wp-grid-drag-preview-shadow-exit
|
|
77
|
+
var(--wpds-motion-duration-md) var(--wpds-motion-easing-balanced)
|
|
78
|
+
forwards;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.drag-preview-frame.dragPreviewFrameExiting .drag-preview-frame__lift {
|
|
82
|
+
animation:
|
|
83
|
+
wp-grid-drag-preview-scale-exit
|
|
84
|
+
var(--wpds-motion-duration-md) var(--wpds-motion-easing-balanced)
|
|
85
|
+
forwards;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@keyframes wp-grid-drag-preview-shadow-exit {
|
|
89
|
+
from {
|
|
90
|
+
box-shadow: var(--wpds-elevation-md);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
to {
|
|
94
|
+
box-shadow: var(--wpds-elevation-xs);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@keyframes wp-grid-drag-preview-scale-exit {
|
|
99
|
+
from {
|
|
100
|
+
transform: scale(var(--wp-grid-drag-preview-scale, 1.05));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
to {
|
|
104
|
+
transform: scale(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.drag-preview-frame.dragPreviewFrameExiting {
|
|
110
|
+
box-shadow: var(--wpds-elevation-xs);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.drag-preview-frame.dragPreviewFrameExiting .drag-preview-frame__lift {
|
|
114
|
+
transform: scale(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@media (prefers-reduced-motion: reduce) {
|
|
118
|
+
.drag-preview-frame {
|
|
119
|
+
box-shadow: var(--wpds-elevation-md);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.drag-preview-frame__lift {
|
|
123
|
+
transform: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.drag-preview-frame.dragPreviewFrameExiting {
|
|
127
|
+
box-shadow: var(--wpds-elevation-xs);
|
|
128
|
+
transition: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.drag-preview-frame.dragPreviewFrameExiting .drag-preview-frame__lift {
|
|
132
|
+
transition: none;
|
|
133
|
+
}
|
|
134
|
+
}
|