panelgrid 0.1.3 → 0.1.4
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/PanelGridProvider.cjs +4 -1
- package/dist/PanelGridProvider.cjs.map +1 -1
- package/dist/PanelGridProvider.d.cts +4 -1
- package/dist/PanelGridProvider.d.mts +4 -1
- package/dist/PanelGridProvider.mjs +4 -1
- package/dist/PanelGridProvider.mjs.map +1 -1
- package/dist/PanelGridRenderer.cjs +7 -4
- package/dist/PanelGridRenderer.cjs.map +1 -1
- package/dist/PanelGridRenderer.mjs +7 -4
- package/dist/PanelGridRenderer.mjs.map +1 -1
- package/dist/styles.css +94 -4
- package/dist/types.d.cts +2 -1
- package/dist/types.d.mts +2 -1
- package/dist/usePanelGrid.cjs +49 -12
- package/dist/usePanelGrid.cjs.map +1 -1
- package/dist/usePanelGrid.d.cts +3 -1
- package/dist/usePanelGrid.d.mts +3 -1
- package/dist/usePanelGrid.mjs +49 -12
- package/dist/usePanelGrid.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -5,13 +5,15 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
5
5
|
//#region src/PanelGridProvider.tsx
|
|
6
6
|
const PanelGridStateContext = (0, react.createContext)(void 0);
|
|
7
7
|
const PanelGridControlsContext = (0, react.createContext)(void 0);
|
|
8
|
-
|
|
8
|
+
const DEFAULT_RESIZE_HANDLE_POSITIONS = ["se"];
|
|
9
|
+
function PanelGridProvider({ panels: initialPanels, columnCount, gap, children, resizeHandlePositions, rearrangement }) {
|
|
9
10
|
const [baseSize, setBaseSize] = (0, react.useState)(null);
|
|
10
11
|
const { panels, panelMap, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState, ghostPanelRef } = require_usePanelGrid.usePanelGrid({
|
|
11
12
|
panels: initialPanels,
|
|
12
13
|
columnCount,
|
|
13
14
|
baseSize: baseSize || 256,
|
|
14
15
|
gap,
|
|
16
|
+
resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,
|
|
15
17
|
rearrangement
|
|
16
18
|
});
|
|
17
19
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelGridStateContext.Provider, {
|
|
@@ -21,6 +23,7 @@ function PanelGridProvider({ panels: initialPanels, columnCount, gap, children,
|
|
|
21
23
|
columnCount,
|
|
22
24
|
gap,
|
|
23
25
|
baseSize,
|
|
26
|
+
resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,
|
|
24
27
|
ghostPanelRef
|
|
25
28
|
},
|
|
26
29
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelGridControlsContext.Provider, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PanelGridProvider.cjs","names":["usePanelGrid"],"sources":["../src/PanelGridProvider.tsx"],"sourcesContent":["import { createContext, useContext, useState } from \"react\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction } from \"./types\";\nimport { usePanelGrid } from \"./usePanelGrid\";\n\ntype PanelGridState = ReturnType<typeof usePanelGrid>;\n\ninterface PanelGridContextType {\n panels: PanelGridState[\"panels\"];\n panelMap: PanelGridState[\"panelMap\"];\n baseSize: number | null;\n columnCount: number;\n gap: number;\n ghostPanelRef: React.RefObject<HTMLDivElement | null>;\n}\n\ninterface PanelGridControlsContextType {\n setBaseSize: (baseSize: number) => void;\n addPanel: (panel: Partial<PanelCoordinate>) => void;\n removePanel: (id: PanelId) => void;\n lockPanelSize: (id: PanelId) => void;\n unlockPanelSize: (id: PanelId) => void;\n exportState: () => PanelCoordinate[];\n}\n\nconst PanelGridStateContext = createContext<PanelGridContextType | undefined>(undefined);\nconst PanelGridControlsContext = createContext<PanelGridControlsContextType | undefined>(undefined);\n\ninterface PanelGridProviderProps {\n panels: PanelCoordinate[];\n columnCount: number;\n gap: number;\n children: React.ReactNode;\n /**\n * Optional custom rearrangement function to override default collision resolution logic\n * If provided, this function will be called instead of the default rearrangePanels\n */\n rearrangement?: RearrangementFunction;\n}\n\nexport function PanelGridProvider({\n panels: initialPanels,\n columnCount,\n gap,\n children,\n rearrangement,\n}: PanelGridProviderProps) {\n const [baseSize, setBaseSize] = useState<number | null>(null);\n\n const { panels, panelMap, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState, ghostPanelRef } =\n usePanelGrid({\n panels: initialPanels,\n columnCount,\n baseSize: baseSize || 256,\n gap,\n rearrangement,\n });\n\n return (\n <PanelGridStateContext.Provider
|
|
1
|
+
{"version":3,"file":"PanelGridProvider.cjs","names":["DEFAULT_RESIZE_HANDLE_POSITIONS: ResizeHandlePosition[]","usePanelGrid"],"sources":["../src/PanelGridProvider.tsx"],"sourcesContent":["import { createContext, useContext, useState } from \"react\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from \"./types\";\nimport { usePanelGrid } from \"./usePanelGrid\";\n\ntype PanelGridState = ReturnType<typeof usePanelGrid>;\n\ninterface PanelGridContextType {\n panels: PanelGridState[\"panels\"];\n panelMap: PanelGridState[\"panelMap\"];\n baseSize: number | null;\n columnCount: number;\n gap: number;\n resizeHandlePositions: ResizeHandlePosition[];\n ghostPanelRef: React.RefObject<HTMLDivElement | null>;\n}\n\ninterface PanelGridControlsContextType {\n setBaseSize: (baseSize: number) => void;\n addPanel: (panel: Partial<PanelCoordinate>) => void;\n removePanel: (id: PanelId) => void;\n lockPanelSize: (id: PanelId) => void;\n unlockPanelSize: (id: PanelId) => void;\n exportState: () => PanelCoordinate[];\n}\n\nconst PanelGridStateContext = createContext<PanelGridContextType | undefined>(undefined);\nconst PanelGridControlsContext = createContext<PanelGridControlsContextType | undefined>(undefined);\n\ninterface PanelGridProviderProps {\n panels: PanelCoordinate[];\n columnCount: number;\n gap: number;\n resizeHandlePositions?: ResizeHandlePosition[];\n children: React.ReactNode;\n /**\n * Optional custom rearrangement function to override default collision resolution logic\n * If provided, this function will be called instead of the default rearrangePanels\n */\n rearrangement?: RearrangementFunction;\n}\n\nconst DEFAULT_RESIZE_HANDLE_POSITIONS: ResizeHandlePosition[] = [\"se\"];\n\nexport function PanelGridProvider({\n panels: initialPanels,\n columnCount,\n gap,\n children,\n resizeHandlePositions,\n rearrangement,\n}: PanelGridProviderProps) {\n const [baseSize, setBaseSize] = useState<number | null>(null);\n\n const { panels, panelMap, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState, ghostPanelRef } =\n usePanelGrid({\n panels: initialPanels,\n columnCount,\n baseSize: baseSize || 256,\n gap,\n resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,\n rearrangement,\n });\n\n return (\n <PanelGridStateContext.Provider\n value={{\n panels,\n panelMap,\n columnCount,\n gap,\n baseSize,\n resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,\n ghostPanelRef,\n }}\n >\n <PanelGridControlsContext.Provider\n value={{ setBaseSize, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState }}\n >\n {children}\n </PanelGridControlsContext.Provider>\n </PanelGridStateContext.Provider>\n );\n}\n\nexport function usePanelGridState() {\n const context = useContext(PanelGridStateContext);\n if (!context) {\n throw new Error(\"usePanelGridState must be used within a PanelGridProvider\");\n }\n return context;\n}\n\nexport function usePanelGridControls() {\n const context = useContext(PanelGridControlsContext);\n if (!context) {\n throw new Error(\"usePanelGridControls must be used within a PanelGridProvider\");\n }\n return context;\n}\n"],"mappings":";;;;;AAyBA,MAAM,iDAAwE,OAAU;AACxF,MAAM,oDAAmF,OAAU;AAenG,MAAMA,kCAA0D,CAAC,KAAK;AAEtE,SAAgB,kBAAkB,EAChC,QAAQ,eACR,aACA,KACA,UACA,uBACA,iBACyB;CACzB,MAAM,CAAC,UAAU,mCAAuC,KAAK;CAE7D,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,eAAe,iBAAiB,aAAa,kBAC5FC,kCAAa;EACX,QAAQ;EACR;EACA,UAAU,YAAY;EACtB;EACA,uBAAuB,yBAAyB;EAChD;EACD,CAAC;AAEJ,QACE,2CAAC,sBAAsB;EACrB,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,uBAAuB,yBAAyB;GAChD;GACD;YAED,2CAAC,yBAAyB;GACxB,OAAO;IAAE;IAAa;IAAU;IAAa;IAAe;IAAiB;IAAa;GAEzF;IACiC;GACL;;AAIrC,SAAgB,oBAAoB;CAClC,MAAM,gCAAqB,sBAAsB;AACjD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,4DAA4D;AAE9E,QAAO;;AAGT,SAAgB,uBAAuB;CACrC,MAAM,gCAAqB,yBAAyB;AACpD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,+DAA+D;AAEjF,QAAO"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PanelCoordinate, PanelId, RearrangementFunction } from "./types.cjs";
|
|
1
|
+
import { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from "./types.cjs";
|
|
2
2
|
import { usePanelGrid } from "./usePanelGrid.cjs";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -10,6 +10,7 @@ interface PanelGridContextType {
|
|
|
10
10
|
baseSize: number | null;
|
|
11
11
|
columnCount: number;
|
|
12
12
|
gap: number;
|
|
13
|
+
resizeHandlePositions: ResizeHandlePosition[];
|
|
13
14
|
ghostPanelRef: React.RefObject<HTMLDivElement | null>;
|
|
14
15
|
}
|
|
15
16
|
interface PanelGridControlsContextType {
|
|
@@ -24,6 +25,7 @@ interface PanelGridProviderProps {
|
|
|
24
25
|
panels: PanelCoordinate[];
|
|
25
26
|
columnCount: number;
|
|
26
27
|
gap: number;
|
|
28
|
+
resizeHandlePositions?: ResizeHandlePosition[];
|
|
27
29
|
children: React.ReactNode;
|
|
28
30
|
/**
|
|
29
31
|
* Optional custom rearrangement function to override default collision resolution logic
|
|
@@ -36,6 +38,7 @@ declare function PanelGridProvider({
|
|
|
36
38
|
columnCount,
|
|
37
39
|
gap,
|
|
38
40
|
children,
|
|
41
|
+
resizeHandlePositions,
|
|
39
42
|
rearrangement
|
|
40
43
|
}: PanelGridProviderProps): react_jsx_runtime0.JSX.Element;
|
|
41
44
|
declare function usePanelGridState(): PanelGridContextType;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PanelCoordinate, PanelId, RearrangementFunction } from "./types.mjs";
|
|
1
|
+
import { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from "./types.mjs";
|
|
2
2
|
import { usePanelGrid } from "./usePanelGrid.mjs";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -10,6 +10,7 @@ interface PanelGridContextType {
|
|
|
10
10
|
baseSize: number | null;
|
|
11
11
|
columnCount: number;
|
|
12
12
|
gap: number;
|
|
13
|
+
resizeHandlePositions: ResizeHandlePosition[];
|
|
13
14
|
ghostPanelRef: React.RefObject<HTMLDivElement | null>;
|
|
14
15
|
}
|
|
15
16
|
interface PanelGridControlsContextType {
|
|
@@ -24,6 +25,7 @@ interface PanelGridProviderProps {
|
|
|
24
25
|
panels: PanelCoordinate[];
|
|
25
26
|
columnCount: number;
|
|
26
27
|
gap: number;
|
|
28
|
+
resizeHandlePositions?: ResizeHandlePosition[];
|
|
27
29
|
children: React.ReactNode;
|
|
28
30
|
/**
|
|
29
31
|
* Optional custom rearrangement function to override default collision resolution logic
|
|
@@ -36,6 +38,7 @@ declare function PanelGridProvider({
|
|
|
36
38
|
columnCount,
|
|
37
39
|
gap,
|
|
38
40
|
children,
|
|
41
|
+
resizeHandlePositions,
|
|
39
42
|
rearrangement
|
|
40
43
|
}: PanelGridProviderProps): react_jsx_runtime0.JSX.Element;
|
|
41
44
|
declare function usePanelGridState(): PanelGridContextType;
|
|
@@ -5,13 +5,15 @@ import { jsx } from "react/jsx-runtime";
|
|
|
5
5
|
//#region src/PanelGridProvider.tsx
|
|
6
6
|
const PanelGridStateContext = createContext(void 0);
|
|
7
7
|
const PanelGridControlsContext = createContext(void 0);
|
|
8
|
-
|
|
8
|
+
const DEFAULT_RESIZE_HANDLE_POSITIONS = ["se"];
|
|
9
|
+
function PanelGridProvider({ panels: initialPanels, columnCount, gap, children, resizeHandlePositions, rearrangement }) {
|
|
9
10
|
const [baseSize, setBaseSize] = useState(null);
|
|
10
11
|
const { panels, panelMap, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState, ghostPanelRef } = usePanelGrid({
|
|
11
12
|
panels: initialPanels,
|
|
12
13
|
columnCount,
|
|
13
14
|
baseSize: baseSize || 256,
|
|
14
15
|
gap,
|
|
16
|
+
resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,
|
|
15
17
|
rearrangement
|
|
16
18
|
});
|
|
17
19
|
return /* @__PURE__ */ jsx(PanelGridStateContext.Provider, {
|
|
@@ -21,6 +23,7 @@ function PanelGridProvider({ panels: initialPanels, columnCount, gap, children,
|
|
|
21
23
|
columnCount,
|
|
22
24
|
gap,
|
|
23
25
|
baseSize,
|
|
26
|
+
resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,
|
|
24
27
|
ghostPanelRef
|
|
25
28
|
},
|
|
26
29
|
children: /* @__PURE__ */ jsx(PanelGridControlsContext.Provider, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PanelGridProvider.mjs","names":[],"sources":["../src/PanelGridProvider.tsx"],"sourcesContent":["import { createContext, useContext, useState } from \"react\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction } from \"./types\";\nimport { usePanelGrid } from \"./usePanelGrid\";\n\ntype PanelGridState = ReturnType<typeof usePanelGrid>;\n\ninterface PanelGridContextType {\n panels: PanelGridState[\"panels\"];\n panelMap: PanelGridState[\"panelMap\"];\n baseSize: number | null;\n columnCount: number;\n gap: number;\n ghostPanelRef: React.RefObject<HTMLDivElement | null>;\n}\n\ninterface PanelGridControlsContextType {\n setBaseSize: (baseSize: number) => void;\n addPanel: (panel: Partial<PanelCoordinate>) => void;\n removePanel: (id: PanelId) => void;\n lockPanelSize: (id: PanelId) => void;\n unlockPanelSize: (id: PanelId) => void;\n exportState: () => PanelCoordinate[];\n}\n\nconst PanelGridStateContext = createContext<PanelGridContextType | undefined>(undefined);\nconst PanelGridControlsContext = createContext<PanelGridControlsContextType | undefined>(undefined);\n\ninterface PanelGridProviderProps {\n panels: PanelCoordinate[];\n columnCount: number;\n gap: number;\n children: React.ReactNode;\n /**\n * Optional custom rearrangement function to override default collision resolution logic\n * If provided, this function will be called instead of the default rearrangePanels\n */\n rearrangement?: RearrangementFunction;\n}\n\nexport function PanelGridProvider({\n panels: initialPanels,\n columnCount,\n gap,\n children,\n rearrangement,\n}: PanelGridProviderProps) {\n const [baseSize, setBaseSize] = useState<number | null>(null);\n\n const { panels, panelMap, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState, ghostPanelRef } =\n usePanelGrid({\n panels: initialPanels,\n columnCount,\n baseSize: baseSize || 256,\n gap,\n rearrangement,\n });\n\n return (\n <PanelGridStateContext.Provider
|
|
1
|
+
{"version":3,"file":"PanelGridProvider.mjs","names":["DEFAULT_RESIZE_HANDLE_POSITIONS: ResizeHandlePosition[]"],"sources":["../src/PanelGridProvider.tsx"],"sourcesContent":["import { createContext, useContext, useState } from \"react\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from \"./types\";\nimport { usePanelGrid } from \"./usePanelGrid\";\n\ntype PanelGridState = ReturnType<typeof usePanelGrid>;\n\ninterface PanelGridContextType {\n panels: PanelGridState[\"panels\"];\n panelMap: PanelGridState[\"panelMap\"];\n baseSize: number | null;\n columnCount: number;\n gap: number;\n resizeHandlePositions: ResizeHandlePosition[];\n ghostPanelRef: React.RefObject<HTMLDivElement | null>;\n}\n\ninterface PanelGridControlsContextType {\n setBaseSize: (baseSize: number) => void;\n addPanel: (panel: Partial<PanelCoordinate>) => void;\n removePanel: (id: PanelId) => void;\n lockPanelSize: (id: PanelId) => void;\n unlockPanelSize: (id: PanelId) => void;\n exportState: () => PanelCoordinate[];\n}\n\nconst PanelGridStateContext = createContext<PanelGridContextType | undefined>(undefined);\nconst PanelGridControlsContext = createContext<PanelGridControlsContextType | undefined>(undefined);\n\ninterface PanelGridProviderProps {\n panels: PanelCoordinate[];\n columnCount: number;\n gap: number;\n resizeHandlePositions?: ResizeHandlePosition[];\n children: React.ReactNode;\n /**\n * Optional custom rearrangement function to override default collision resolution logic\n * If provided, this function will be called instead of the default rearrangePanels\n */\n rearrangement?: RearrangementFunction;\n}\n\nconst DEFAULT_RESIZE_HANDLE_POSITIONS: ResizeHandlePosition[] = [\"se\"];\n\nexport function PanelGridProvider({\n panels: initialPanels,\n columnCount,\n gap,\n children,\n resizeHandlePositions,\n rearrangement,\n}: PanelGridProviderProps) {\n const [baseSize, setBaseSize] = useState<number | null>(null);\n\n const { panels, panelMap, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState, ghostPanelRef } =\n usePanelGrid({\n panels: initialPanels,\n columnCount,\n baseSize: baseSize || 256,\n gap,\n resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,\n rearrangement,\n });\n\n return (\n <PanelGridStateContext.Provider\n value={{\n panels,\n panelMap,\n columnCount,\n gap,\n baseSize,\n resizeHandlePositions: resizeHandlePositions || DEFAULT_RESIZE_HANDLE_POSITIONS,\n ghostPanelRef,\n }}\n >\n <PanelGridControlsContext.Provider\n value={{ setBaseSize, addPanel, removePanel, lockPanelSize, unlockPanelSize, exportState }}\n >\n {children}\n </PanelGridControlsContext.Provider>\n </PanelGridStateContext.Provider>\n );\n}\n\nexport function usePanelGridState() {\n const context = useContext(PanelGridStateContext);\n if (!context) {\n throw new Error(\"usePanelGridState must be used within a PanelGridProvider\");\n }\n return context;\n}\n\nexport function usePanelGridControls() {\n const context = useContext(PanelGridControlsContext);\n if (!context) {\n throw new Error(\"usePanelGridControls must be used within a PanelGridProvider\");\n }\n return context;\n}\n"],"mappings":";;;;;AAyBA,MAAM,wBAAwB,cAAgD,OAAU;AACxF,MAAM,2BAA2B,cAAwD,OAAU;AAenG,MAAMA,kCAA0D,CAAC,KAAK;AAEtE,SAAgB,kBAAkB,EAChC,QAAQ,eACR,aACA,KACA,UACA,uBACA,iBACyB;CACzB,MAAM,CAAC,UAAU,eAAe,SAAwB,KAAK;CAE7D,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,eAAe,iBAAiB,aAAa,kBAC5F,aAAa;EACX,QAAQ;EACR;EACA,UAAU,YAAY;EACtB;EACA,uBAAuB,yBAAyB;EAChD;EACD,CAAC;AAEJ,QACE,oBAAC,sBAAsB;EACrB,OAAO;GACL;GACA;GACA;GACA;GACA;GACA,uBAAuB,yBAAyB;GAChD;GACD;YAED,oBAAC,yBAAyB;GACxB,OAAO;IAAE;IAAa;IAAU;IAAa;IAAe;IAAiB;IAAa;GAEzF;IACiC;GACL;;AAIrC,SAAgB,oBAAoB;CAClC,MAAM,UAAU,WAAW,sBAAsB;AACjD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,4DAA4D;AAE9E,QAAO;;AAGT,SAAgB,uBAAuB;CACrC,MAAM,UAAU,WAAW,yBAAyB;AACpD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,+DAA+D;AAEjF,QAAO"}
|
|
@@ -5,7 +5,7 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
5
5
|
|
|
6
6
|
//#region src/PanelGridRenderer.tsx
|
|
7
7
|
function PanelGridRenderer({ itemRenderer: ItemRenderer }) {
|
|
8
|
-
const { panels, columnCount, gap, baseSize, ghostPanelRef } = require_PanelGridProvider.usePanelGridState();
|
|
8
|
+
const { panels, columnCount, gap, baseSize, resizeHandlePositions, ghostPanelRef } = require_PanelGridProvider.usePanelGridState();
|
|
9
9
|
const { setBaseSize } = require_PanelGridProvider.usePanelGridControls();
|
|
10
10
|
const containerRef = (0, react.useRef)(null);
|
|
11
11
|
const rowCount = require_gridCalculations.getGridRowCount(panels.map(({ panelProps: p }) => ({
|
|
@@ -61,9 +61,12 @@ function PanelGridRenderer({ itemRenderer: ItemRenderer }) {
|
|
|
61
61
|
"data-pg-y": y,
|
|
62
62
|
"data-pg-w": w,
|
|
63
63
|
"data-pg-h": h,
|
|
64
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ItemRenderer, { id: key }),
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ItemRenderer, { id: key }), resizeHandlePositions.map((pos) => {
|
|
65
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
66
|
+
className: `panelgrid-resize-handle panelgrid-resize-handle--${pos}`,
|
|
67
|
+
...resizeHandleProps,
|
|
68
|
+
"data-pg-resize-handle": pos
|
|
69
|
+
}, pos);
|
|
67
70
|
})]
|
|
68
71
|
}, key);
|
|
69
72
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PanelGridRenderer.cjs","names":["usePanelGridState","usePanelGridControls","getGridRowCount"],"sources":["../src/PanelGridRenderer.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from \"react\";\nimport { getGridRowCount } from \"./helpers/gridCalculations\";\nimport { usePanelGridControls, usePanelGridState } from \"./PanelGridProvider\";\nimport type { PanelId } from \"./types\";\n\ninterface PanelGridRendererProps {\n itemRenderer: React.ComponentType<{ id: PanelId }>;\n}\n\nexport function PanelGridRenderer({ itemRenderer: ItemRenderer }: PanelGridRendererProps) {\n const { panels, columnCount, gap, baseSize, ghostPanelRef } = usePanelGridState();\n const { setBaseSize } = usePanelGridControls();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const rowCount = getGridRowCount(\n panels.map(({ panelProps: p }) => ({\n id: p.key,\n x: p.positionData.x,\n y: p.positionData.y,\n w: p.positionData.w,\n h: p.positionData.h,\n }))\n );\n const count = Math.max(columnCount * (rowCount + 1), columnCount * columnCount);\n\n useLayoutEffect(() => {\n if (!containerRef.current) return;\n const observer = new ResizeObserver((entries) => {\n const [entry] = entries;\n const rect = entry.contentRect;\n const baseSize = Math.floor((rect.width - gap * (columnCount - 1)) / columnCount);\n setBaseSize(baseSize);\n });\n observer.observe(containerRef.current);\n return () => observer.disconnect();\n }, [columnCount, gap, setBaseSize]);\n\n return (\n <div\n className=\"panelgrid-renderer\"\n style={{\n \"--column-count\": `${columnCount}`,\n \"--gap\": `${gap}px`,\n opacity: baseSize ? 1 : 0,\n }}\n ref={containerRef}\n >\n {Array.from({ length: count }).map((_, i) => {\n return <div key={i} className=\"panelgrid-panel-placeholder\" />;\n })}\n\n <div className=\"panelgrid-panel-ghost\" ref={ghostPanelRef}></div>\n\n {panels.map((panel) => {\n const { panelProps, resizeHandleProps } = panel;\n const { key, lockSize, style, positionData, ref, onMouseDown } = panelProps;\n\n const className = lockSize ? \"panelgrid-panel panelgrid-panel--size-locked\" : \"panelgrid-panel\";\n const { x, y, w, h } = positionData;\n\n return (\n <div\n key={key}\n className={className}\n style={style}\n ref={ref}\n onMouseDown={onMouseDown}\n data-panel-id={key}\n data-pg-x={x}\n data-pg-y={y}\n data-pg-w={w}\n data-pg-h={h}\n >\n <ItemRenderer id={key} />\n {
|
|
1
|
+
{"version":3,"file":"PanelGridRenderer.cjs","names":["usePanelGridState","usePanelGridControls","getGridRowCount"],"sources":["../src/PanelGridRenderer.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from \"react\";\nimport { getGridRowCount } from \"./helpers/gridCalculations\";\nimport { usePanelGridControls, usePanelGridState } from \"./PanelGridProvider\";\nimport type { PanelId } from \"./types\";\n\ninterface PanelGridRendererProps {\n itemRenderer: React.ComponentType<{ id: PanelId }>;\n}\n\nexport function PanelGridRenderer({ itemRenderer: ItemRenderer }: PanelGridRendererProps) {\n const { panels, columnCount, gap, baseSize, resizeHandlePositions, ghostPanelRef } = usePanelGridState();\n const { setBaseSize } = usePanelGridControls();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const rowCount = getGridRowCount(\n panels.map(({ panelProps: p }) => ({\n id: p.key,\n x: p.positionData.x,\n y: p.positionData.y,\n w: p.positionData.w,\n h: p.positionData.h,\n }))\n );\n const count = Math.max(columnCount * (rowCount + 1), columnCount * columnCount);\n\n useLayoutEffect(() => {\n if (!containerRef.current) return;\n const observer = new ResizeObserver((entries) => {\n const [entry] = entries;\n const rect = entry.contentRect;\n const baseSize = Math.floor((rect.width - gap * (columnCount - 1)) / columnCount);\n setBaseSize(baseSize);\n });\n observer.observe(containerRef.current);\n return () => observer.disconnect();\n }, [columnCount, gap, setBaseSize]);\n\n return (\n <div\n className=\"panelgrid-renderer\"\n style={{\n \"--column-count\": `${columnCount}`,\n \"--gap\": `${gap}px`,\n opacity: baseSize ? 1 : 0,\n }}\n ref={containerRef}\n >\n {Array.from({ length: count }).map((_, i) => {\n return <div key={i} className=\"panelgrid-panel-placeholder\" />;\n })}\n\n <div className=\"panelgrid-panel-ghost\" ref={ghostPanelRef}></div>\n\n {panels.map((panel) => {\n const { panelProps, resizeHandleProps } = panel;\n const { key, lockSize, style, positionData, ref, onMouseDown } = panelProps;\n\n const className = lockSize ? \"panelgrid-panel panelgrid-panel--size-locked\" : \"panelgrid-panel\";\n const { x, y, w, h } = positionData;\n\n return (\n <div\n key={key}\n className={className}\n style={style}\n ref={ref}\n onMouseDown={onMouseDown}\n data-panel-id={key}\n data-pg-x={x}\n data-pg-y={y}\n data-pg-w={w}\n data-pg-h={h}\n >\n <ItemRenderer id={key} />\n {resizeHandlePositions.map((pos) => {\n return (\n <span\n key={pos}\n className={`panelgrid-resize-handle panelgrid-resize-handle--${pos}`}\n {...resizeHandleProps}\n data-pg-resize-handle={pos}\n />\n );\n })}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;AASA,SAAgB,kBAAkB,EAAE,cAAc,gBAAwC;CACxF,MAAM,EAAE,QAAQ,aAAa,KAAK,UAAU,uBAAuB,kBAAkBA,6CAAmB;CACxG,MAAM,EAAE,gBAAgBC,gDAAsB;CAC9C,MAAM,iCAA6C,KAAK;CACxD,MAAM,WAAWC,yCACf,OAAO,KAAK,EAAE,YAAY,SAAS;EACjC,IAAI,EAAE;EACN,GAAG,EAAE,aAAa;EAClB,GAAG,EAAE,aAAa;EAClB,GAAG,EAAE,aAAa;EAClB,GAAG,EAAE,aAAa;EACnB,EAAE,CACJ;CACD,MAAM,QAAQ,KAAK,IAAI,eAAe,WAAW,IAAI,cAAc,YAAY;AAE/E,kCAAsB;AACpB,MAAI,CAAC,aAAa,QAAS;EAC3B,MAAM,WAAW,IAAI,gBAAgB,YAAY;GAC/C,MAAM,CAAC,SAAS;GAChB,MAAM,OAAO,MAAM;AAEnB,eADiB,KAAK,OAAO,KAAK,QAAQ,OAAO,cAAc,MAAM,YAAY,CAC5D;IACrB;AACF,WAAS,QAAQ,aAAa,QAAQ;AACtC,eAAa,SAAS,YAAY;IACjC;EAAC;EAAa;EAAK;EAAY,CAAC;AAEnC,QACE,4CAAC;EACC,WAAU;EACV,OAAO;GACL,kBAAkB,GAAG;GACrB,SAAS,GAAG,IAAI;GAChB,SAAS,WAAW,IAAI;GACzB;EACD,KAAK;;GAEJ,MAAM,KAAK,EAAE,QAAQ,OAAO,CAAC,CAAC,KAAK,GAAG,MAAM;AAC3C,WAAO,2CAAC,SAAY,WAAU,iCAAb,EAA6C;KAC9D;GAEF,2CAAC;IAAI,WAAU;IAAwB,KAAK;KAAqB;GAEhE,OAAO,KAAK,UAAU;IACrB,MAAM,EAAE,YAAY,sBAAsB;IAC1C,MAAM,EAAE,KAAK,UAAU,OAAO,cAAc,KAAK,gBAAgB;IAEjE,MAAM,YAAY,WAAW,iDAAiD;IAC9E,MAAM,EAAE,GAAG,GAAG,GAAG,MAAM;AAEvB,WACE,4CAAC;KAEY;KACJ;KACF;KACQ;KACb,iBAAe;KACf,aAAW;KACX,aAAW;KACX,aAAW;KACX,aAAW;gBAEX,2CAAC,gBAAa,IAAI,MAAO,EACxB,sBAAsB,KAAK,QAAQ;AAClC,aACE,2CAAC;OAEC,WAAW,oDAAoD;OAC/D,GAAI;OACJ,yBAAuB;SAHlB,IAIL;OAEJ;OArBG,IAsBD;KAER;;GACE"}
|
|
@@ -5,7 +5,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
|
|
6
6
|
//#region src/PanelGridRenderer.tsx
|
|
7
7
|
function PanelGridRenderer({ itemRenderer: ItemRenderer }) {
|
|
8
|
-
const { panels, columnCount, gap, baseSize, ghostPanelRef } = usePanelGridState();
|
|
8
|
+
const { panels, columnCount, gap, baseSize, resizeHandlePositions, ghostPanelRef } = usePanelGridState();
|
|
9
9
|
const { setBaseSize } = usePanelGridControls();
|
|
10
10
|
const containerRef = useRef(null);
|
|
11
11
|
const rowCount = getGridRowCount(panels.map(({ panelProps: p }) => ({
|
|
@@ -61,9 +61,12 @@ function PanelGridRenderer({ itemRenderer: ItemRenderer }) {
|
|
|
61
61
|
"data-pg-y": y,
|
|
62
62
|
"data-pg-w": w,
|
|
63
63
|
"data-pg-h": h,
|
|
64
|
-
children: [/* @__PURE__ */ jsx(ItemRenderer, { id: key }),
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
children: [/* @__PURE__ */ jsx(ItemRenderer, { id: key }), resizeHandlePositions.map((pos) => {
|
|
65
|
+
return /* @__PURE__ */ jsx("span", {
|
|
66
|
+
className: `panelgrid-resize-handle panelgrid-resize-handle--${pos}`,
|
|
67
|
+
...resizeHandleProps,
|
|
68
|
+
"data-pg-resize-handle": pos
|
|
69
|
+
}, pos);
|
|
67
70
|
})]
|
|
68
71
|
}, key);
|
|
69
72
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PanelGridRenderer.mjs","names":[],"sources":["../src/PanelGridRenderer.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from \"react\";\nimport { getGridRowCount } from \"./helpers/gridCalculations\";\nimport { usePanelGridControls, usePanelGridState } from \"./PanelGridProvider\";\nimport type { PanelId } from \"./types\";\n\ninterface PanelGridRendererProps {\n itemRenderer: React.ComponentType<{ id: PanelId }>;\n}\n\nexport function PanelGridRenderer({ itemRenderer: ItemRenderer }: PanelGridRendererProps) {\n const { panels, columnCount, gap, baseSize, ghostPanelRef } = usePanelGridState();\n const { setBaseSize } = usePanelGridControls();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const rowCount = getGridRowCount(\n panels.map(({ panelProps: p }) => ({\n id: p.key,\n x: p.positionData.x,\n y: p.positionData.y,\n w: p.positionData.w,\n h: p.positionData.h,\n }))\n );\n const count = Math.max(columnCount * (rowCount + 1), columnCount * columnCount);\n\n useLayoutEffect(() => {\n if (!containerRef.current) return;\n const observer = new ResizeObserver((entries) => {\n const [entry] = entries;\n const rect = entry.contentRect;\n const baseSize = Math.floor((rect.width - gap * (columnCount - 1)) / columnCount);\n setBaseSize(baseSize);\n });\n observer.observe(containerRef.current);\n return () => observer.disconnect();\n }, [columnCount, gap, setBaseSize]);\n\n return (\n <div\n className=\"panelgrid-renderer\"\n style={{\n \"--column-count\": `${columnCount}`,\n \"--gap\": `${gap}px`,\n opacity: baseSize ? 1 : 0,\n }}\n ref={containerRef}\n >\n {Array.from({ length: count }).map((_, i) => {\n return <div key={i} className=\"panelgrid-panel-placeholder\" />;\n })}\n\n <div className=\"panelgrid-panel-ghost\" ref={ghostPanelRef}></div>\n\n {panels.map((panel) => {\n const { panelProps, resizeHandleProps } = panel;\n const { key, lockSize, style, positionData, ref, onMouseDown } = panelProps;\n\n const className = lockSize ? \"panelgrid-panel panelgrid-panel--size-locked\" : \"panelgrid-panel\";\n const { x, y, w, h } = positionData;\n\n return (\n <div\n key={key}\n className={className}\n style={style}\n ref={ref}\n onMouseDown={onMouseDown}\n data-panel-id={key}\n data-pg-x={x}\n data-pg-y={y}\n data-pg-w={w}\n data-pg-h={h}\n >\n <ItemRenderer id={key} />\n {
|
|
1
|
+
{"version":3,"file":"PanelGridRenderer.mjs","names":[],"sources":["../src/PanelGridRenderer.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from \"react\";\nimport { getGridRowCount } from \"./helpers/gridCalculations\";\nimport { usePanelGridControls, usePanelGridState } from \"./PanelGridProvider\";\nimport type { PanelId } from \"./types\";\n\ninterface PanelGridRendererProps {\n itemRenderer: React.ComponentType<{ id: PanelId }>;\n}\n\nexport function PanelGridRenderer({ itemRenderer: ItemRenderer }: PanelGridRendererProps) {\n const { panels, columnCount, gap, baseSize, resizeHandlePositions, ghostPanelRef } = usePanelGridState();\n const { setBaseSize } = usePanelGridControls();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const rowCount = getGridRowCount(\n panels.map(({ panelProps: p }) => ({\n id: p.key,\n x: p.positionData.x,\n y: p.positionData.y,\n w: p.positionData.w,\n h: p.positionData.h,\n }))\n );\n const count = Math.max(columnCount * (rowCount + 1), columnCount * columnCount);\n\n useLayoutEffect(() => {\n if (!containerRef.current) return;\n const observer = new ResizeObserver((entries) => {\n const [entry] = entries;\n const rect = entry.contentRect;\n const baseSize = Math.floor((rect.width - gap * (columnCount - 1)) / columnCount);\n setBaseSize(baseSize);\n });\n observer.observe(containerRef.current);\n return () => observer.disconnect();\n }, [columnCount, gap, setBaseSize]);\n\n return (\n <div\n className=\"panelgrid-renderer\"\n style={{\n \"--column-count\": `${columnCount}`,\n \"--gap\": `${gap}px`,\n opacity: baseSize ? 1 : 0,\n }}\n ref={containerRef}\n >\n {Array.from({ length: count }).map((_, i) => {\n return <div key={i} className=\"panelgrid-panel-placeholder\" />;\n })}\n\n <div className=\"panelgrid-panel-ghost\" ref={ghostPanelRef}></div>\n\n {panels.map((panel) => {\n const { panelProps, resizeHandleProps } = panel;\n const { key, lockSize, style, positionData, ref, onMouseDown } = panelProps;\n\n const className = lockSize ? \"panelgrid-panel panelgrid-panel--size-locked\" : \"panelgrid-panel\";\n const { x, y, w, h } = positionData;\n\n return (\n <div\n key={key}\n className={className}\n style={style}\n ref={ref}\n onMouseDown={onMouseDown}\n data-panel-id={key}\n data-pg-x={x}\n data-pg-y={y}\n data-pg-w={w}\n data-pg-h={h}\n >\n <ItemRenderer id={key} />\n {resizeHandlePositions.map((pos) => {\n return (\n <span\n key={pos}\n className={`panelgrid-resize-handle panelgrid-resize-handle--${pos}`}\n {...resizeHandleProps}\n data-pg-resize-handle={pos}\n />\n );\n })}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;AASA,SAAgB,kBAAkB,EAAE,cAAc,gBAAwC;CACxF,MAAM,EAAE,QAAQ,aAAa,KAAK,UAAU,uBAAuB,kBAAkB,mBAAmB;CACxG,MAAM,EAAE,gBAAgB,sBAAsB;CAC9C,MAAM,eAAe,OAA8B,KAAK;CACxD,MAAM,WAAW,gBACf,OAAO,KAAK,EAAE,YAAY,SAAS;EACjC,IAAI,EAAE;EACN,GAAG,EAAE,aAAa;EAClB,GAAG,EAAE,aAAa;EAClB,GAAG,EAAE,aAAa;EAClB,GAAG,EAAE,aAAa;EACnB,EAAE,CACJ;CACD,MAAM,QAAQ,KAAK,IAAI,eAAe,WAAW,IAAI,cAAc,YAAY;AAE/E,uBAAsB;AACpB,MAAI,CAAC,aAAa,QAAS;EAC3B,MAAM,WAAW,IAAI,gBAAgB,YAAY;GAC/C,MAAM,CAAC,SAAS;GAChB,MAAM,OAAO,MAAM;AAEnB,eADiB,KAAK,OAAO,KAAK,QAAQ,OAAO,cAAc,MAAM,YAAY,CAC5D;IACrB;AACF,WAAS,QAAQ,aAAa,QAAQ;AACtC,eAAa,SAAS,YAAY;IACjC;EAAC;EAAa;EAAK;EAAY,CAAC;AAEnC,QACE,qBAAC;EACC,WAAU;EACV,OAAO;GACL,kBAAkB,GAAG;GACrB,SAAS,GAAG,IAAI;GAChB,SAAS,WAAW,IAAI;GACzB;EACD,KAAK;;GAEJ,MAAM,KAAK,EAAE,QAAQ,OAAO,CAAC,CAAC,KAAK,GAAG,MAAM;AAC3C,WAAO,oBAAC,SAAY,WAAU,iCAAb,EAA6C;KAC9D;GAEF,oBAAC;IAAI,WAAU;IAAwB,KAAK;KAAqB;GAEhE,OAAO,KAAK,UAAU;IACrB,MAAM,EAAE,YAAY,sBAAsB;IAC1C,MAAM,EAAE,KAAK,UAAU,OAAO,cAAc,KAAK,gBAAgB;IAEjE,MAAM,YAAY,WAAW,iDAAiD;IAC9E,MAAM,EAAE,GAAG,GAAG,GAAG,MAAM;AAEvB,WACE,qBAAC;KAEY;KACJ;KACF;KACQ;KACb,iBAAe;KACf,aAAW;KACX,aAAW;KACX,aAAW;KACX,aAAW;gBAEX,oBAAC,gBAAa,IAAI,MAAO,EACxB,sBAAsB,KAAK,QAAQ;AAClC,aACE,oBAAC;OAEC,WAAW,oDAAoD;OAC/D,GAAI;OACJ,yBAAuB;SAHlB,IAIL;OAEJ;OArBG,IAsBD;KAER;;GACE"}
|
package/dist/styles.css
CHANGED
|
@@ -32,6 +32,20 @@
|
|
|
32
32
|
border-radius: 4px;
|
|
33
33
|
cursor: grab;
|
|
34
34
|
overflow: auto;
|
|
35
|
+
|
|
36
|
+
& .panelgrid-resize-handle {
|
|
37
|
+
opacity: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:hover .panelgrid-resize-handle {
|
|
41
|
+
opacity: 0.4;
|
|
42
|
+
transition: opacity 0.2s ease-in;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&:hover .panelgrid-resize-handle:hover {
|
|
46
|
+
opacity: 1;
|
|
47
|
+
transition: opacity 0.2s ease-in;
|
|
48
|
+
}
|
|
35
49
|
}
|
|
36
50
|
|
|
37
51
|
.panelgrid-panel--dragging {
|
|
@@ -50,19 +64,95 @@
|
|
|
50
64
|
|
|
51
65
|
.panelgrid-resize-handle {
|
|
52
66
|
position: absolute;
|
|
53
|
-
right: 0;
|
|
54
|
-
bottom: 0;
|
|
55
67
|
display: inline-block;
|
|
56
68
|
width: 24px;
|
|
57
69
|
aspect-ratio: 1 / 1;
|
|
58
|
-
background-
|
|
59
|
-
|
|
70
|
+
background-image: url('data:image/svg+xml,%3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M21 15L15 21M21 8L8 21" stroke="%23000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E');
|
|
71
|
+
background-size: contain;
|
|
72
|
+
background-repeat: no-repeat;
|
|
73
|
+
background-position: center;
|
|
74
|
+
|
|
75
|
+
&:hover {
|
|
76
|
+
opacity: 1;
|
|
77
|
+
transition: opacity 0.2s ease-in;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
60
80
|
|
|
81
|
+
.panelgrid-resize-handle--nw {
|
|
82
|
+
top: 0;
|
|
83
|
+
left: 0;
|
|
84
|
+
right: auto;
|
|
85
|
+
bottom: auto;
|
|
86
|
+
transform: rotate(180deg);
|
|
87
|
+
&:hover {
|
|
88
|
+
cursor: nwse-resize;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.panelgrid-resize-handle--ne {
|
|
93
|
+
top: 0;
|
|
94
|
+
right: 0;
|
|
95
|
+
bottom: auto;
|
|
96
|
+
left: auto;
|
|
97
|
+
transform: rotate(-90deg);
|
|
98
|
+
&:hover {
|
|
99
|
+
cursor: nesw-resize;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.panelgrid-resize-handle--se {
|
|
104
|
+
right: 0;
|
|
105
|
+
bottom: 0;
|
|
61
106
|
&:hover {
|
|
62
107
|
cursor: nwse-resize;
|
|
63
108
|
}
|
|
64
109
|
}
|
|
65
110
|
|
|
111
|
+
.panelgrid-resize-handle--sw {
|
|
112
|
+
left: 0;
|
|
113
|
+
bottom: 0;
|
|
114
|
+
transform: rotate(90deg);
|
|
115
|
+
&:hover {
|
|
116
|
+
cursor: nesw-resize;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.panelgrid-resize-handle--n {
|
|
121
|
+
top: 0;
|
|
122
|
+
left: 50%;
|
|
123
|
+
transform: translate3D(-50%, -50%, 0) rotate(45deg);
|
|
124
|
+
&:hover {
|
|
125
|
+
cursor: ns-resize;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.panelgrid-resize-handle--s {
|
|
130
|
+
bottom: 0;
|
|
131
|
+
left: 50%;
|
|
132
|
+
transform: translate3D(-50%, 50%, 0) rotate(225deg);
|
|
133
|
+
&:hover {
|
|
134
|
+
cursor: ns-resize;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.panelgrid-resize-handle--e {
|
|
139
|
+
top: 50%;
|
|
140
|
+
right: 0;
|
|
141
|
+
transform: translate3D(50%, -50%, 0) rotate(135deg);
|
|
142
|
+
&:hover {
|
|
143
|
+
cursor: ew-resize;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.panelgrid-resize-handle--w {
|
|
148
|
+
top: 50%;
|
|
149
|
+
left: 0;
|
|
150
|
+
transform: translate3D(-50%, -50%, 0) rotate(-45deg);
|
|
151
|
+
&:hover {
|
|
152
|
+
cursor: ew-resize;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
66
156
|
.panelgrid-resizing,
|
|
67
157
|
.panelgrid-dragging {
|
|
68
158
|
user-select: none;
|
package/dist/types.d.cts
CHANGED
|
@@ -21,6 +21,7 @@ interface PanelCoordinate {
|
|
|
21
21
|
* Returns the new panel arrangement after resolving collisions
|
|
22
22
|
*/
|
|
23
23
|
type RearrangementFunction = (movingPanel: PanelCoordinate, allPanels: PanelCoordinate[], columnCount: number) => PanelCoordinate[];
|
|
24
|
+
type ResizeHandlePosition = "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw";
|
|
24
25
|
//#endregion
|
|
25
|
-
export { PanelCoordinate, PanelId, RearrangementFunction };
|
|
26
|
+
export { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition };
|
|
26
27
|
//# sourceMappingURL=types.d.cts.map
|
package/dist/types.d.mts
CHANGED
|
@@ -21,6 +21,7 @@ interface PanelCoordinate {
|
|
|
21
21
|
* Returns the new panel arrangement after resolving collisions
|
|
22
22
|
*/
|
|
23
23
|
type RearrangementFunction = (movingPanel: PanelCoordinate, allPanels: PanelCoordinate[], columnCount: number) => PanelCoordinate[];
|
|
24
|
+
type ResizeHandlePosition = "n" | "e" | "s" | "w" | "ne" | "nw" | "se" | "sw";
|
|
24
25
|
//#endregion
|
|
25
|
-
export { PanelCoordinate, PanelId, RearrangementFunction };
|
|
26
|
+
export { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition };
|
|
26
27
|
//# sourceMappingURL=types.d.mts.map
|
package/dist/usePanelGrid.cjs
CHANGED
|
@@ -48,7 +48,17 @@ function panelGridReducer(state, action) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
const ANIMATION_DURATION = 300;
|
|
51
|
-
|
|
51
|
+
const RESIZE_CURSOR_MAP = {
|
|
52
|
+
nw: "nwse-resize",
|
|
53
|
+
ne: "nesw-resize",
|
|
54
|
+
se: "nwse-resize",
|
|
55
|
+
sw: "nesw-resize",
|
|
56
|
+
n: "ns-resize",
|
|
57
|
+
s: "ns-resize",
|
|
58
|
+
e: "ew-resize",
|
|
59
|
+
w: "ew-resize"
|
|
60
|
+
};
|
|
61
|
+
function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePositions: _resizeHandlePositions, rearrangement }) {
|
|
52
62
|
const [state, dispatch] = (0, react.useReducer)(panelGridReducer, { panels });
|
|
53
63
|
const ghostPanelRef = (0, react.useRef)(null);
|
|
54
64
|
const animationTimeoutsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
@@ -194,12 +204,19 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
194
204
|
if (!draggingElement) return;
|
|
195
205
|
const startX = e.clientX;
|
|
196
206
|
const startY = e.clientY;
|
|
207
|
+
const initialTop = draggingElement.offsetTop;
|
|
208
|
+
const initialLeft = draggingElement.offsetLeft;
|
|
197
209
|
const initialWidth = draggingElement.offsetWidth;
|
|
198
210
|
const initialHeight = draggingElement.offsetHeight;
|
|
199
211
|
const initialZIndex = draggingElement.style.zIndex;
|
|
200
212
|
const initialCursor = draggingElement.style.cursor;
|
|
213
|
+
const handlePosition = e.currentTarget.dataset.pgResizeHandle;
|
|
214
|
+
const northSideResizeEnabled = handlePosition?.includes("n");
|
|
215
|
+
const westSideResizeEnabled = handlePosition?.includes("w");
|
|
216
|
+
const isVerticalResizeOnly = handlePosition === "n" || handlePosition === "s";
|
|
217
|
+
const isHorizontalResizeOnly = handlePosition === "e" || handlePosition === "w";
|
|
201
218
|
document.body.classList.add("panelgrid-resizing");
|
|
202
|
-
draggingElement.style.cursor = "nwse-resize";
|
|
219
|
+
draggingElement.style.cursor = RESIZE_CURSOR_MAP[handlePosition || "se"] || "nwse-resize";
|
|
203
220
|
draggingElement.style.transition = "";
|
|
204
221
|
showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);
|
|
205
222
|
const mouseMoveController = new AbortController();
|
|
@@ -209,21 +226,36 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
209
226
|
if (!draggingElement) return;
|
|
210
227
|
const deltaX = e$1.clientX - startX;
|
|
211
228
|
const deltaY = e$1.clientY - startY;
|
|
212
|
-
|
|
213
|
-
|
|
229
|
+
const newWidth = westSideResizeEnabled ? Math.max(initialWidth - deltaX, 1) : isVerticalResizeOnly ? initialWidth : initialWidth + deltaX;
|
|
230
|
+
const newHeight = northSideResizeEnabled ? Math.max(initialHeight - deltaY, 1) : isHorizontalResizeOnly ? initialHeight : initialHeight + deltaY;
|
|
231
|
+
draggingElement.style.width = `${newWidth}px`;
|
|
232
|
+
draggingElement.style.height = `${newHeight}px`;
|
|
214
233
|
draggingElement.style.zIndex = "calc(infinity)";
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
|
|
234
|
+
if (northSideResizeEnabled) draggingElement.style.top = `${initialTop + deltaY}px`;
|
|
235
|
+
if (westSideResizeEnabled) draggingElement.style.left = `${initialLeft + deltaX}px`;
|
|
236
|
+
const currentLeft = westSideResizeEnabled ? initialLeft + deltaX : initialLeft;
|
|
237
|
+
const currentTop = northSideResizeEnabled ? initialTop + deltaY : initialTop;
|
|
238
|
+
const nextGridX = require_gridCalculations.pixelsToGridPosition(currentLeft, baseSize, gap, columnCount, panel.w);
|
|
239
|
+
const nextGridY = require_gridCalculations.pixelsToGridPosition(currentTop, baseSize, gap);
|
|
240
|
+
const nextGridW = require_gridCalculations.pixelsToGridSize(newWidth, baseSize, gap, columnCount, nextGridX);
|
|
241
|
+
const nextGridH = require_gridCalculations.pixelsToGridSize(newHeight, baseSize, gap, columnCount, nextGridY);
|
|
242
|
+
const snappedWidth = require_gridCalculations.gridToPixels(nextGridW, baseSize, gap);
|
|
243
|
+
const snappedHeight = require_gridCalculations.gridToPixels(nextGridH, baseSize, gap);
|
|
244
|
+
updateGhostPanelPosition(require_gridCalculations.gridPositionToPixels(nextGridX, baseSize, gap), require_gridCalculations.gridPositionToPixels(nextGridY, baseSize, gap));
|
|
245
|
+
updateGhostPanelSize(snappedWidth, snappedHeight);
|
|
220
246
|
};
|
|
221
247
|
const onMouseUp = () => {
|
|
222
248
|
if (!draggingElement) return;
|
|
223
249
|
hideGhostPanel();
|
|
224
250
|
const rect = draggingElement.getBoundingClientRect();
|
|
225
|
-
const
|
|
226
|
-
const
|
|
251
|
+
const droppedLeft = parseFloat(draggingElement.style.left) || 0;
|
|
252
|
+
const droppedTop = parseFloat(draggingElement.style.top) || 0;
|
|
253
|
+
const nextGridX = require_gridCalculations.pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);
|
|
254
|
+
const nextGridY = require_gridCalculations.pixelsToGridPosition(droppedTop, baseSize, gap);
|
|
255
|
+
const nextGridW = require_gridCalculations.pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);
|
|
256
|
+
const nextGridH = require_gridCalculations.pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);
|
|
257
|
+
const left = require_gridCalculations.gridPositionToPixels(nextGridX, baseSize, gap);
|
|
258
|
+
const top = require_gridCalculations.gridPositionToPixels(nextGridY, baseSize, gap);
|
|
227
259
|
const width = require_gridCalculations.gridToPixels(nextGridW, baseSize, gap);
|
|
228
260
|
const height = require_gridCalculations.gridToPixels(nextGridH, baseSize, gap);
|
|
229
261
|
draggingElement.style.width = `${rect.width}px`;
|
|
@@ -231,13 +263,17 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
231
263
|
draggingElement.style.cursor = initialCursor;
|
|
232
264
|
draggingElement.style.transition = "";
|
|
233
265
|
window.requestAnimationFrame(() => {
|
|
266
|
+
draggingElement.style.top = `${top}px`;
|
|
267
|
+
draggingElement.style.left = `${left}px`;
|
|
234
268
|
draggingElement.style.width = `${width}px`;
|
|
235
269
|
draggingElement.style.height = `${height}px`;
|
|
236
270
|
draggingElement.style.zIndex = initialZIndex;
|
|
237
|
-
draggingElement.style.transition = "width 0.1s ease-out, height 0.1s ease-out";
|
|
271
|
+
draggingElement.style.transition = "width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out";
|
|
238
272
|
});
|
|
239
273
|
updatePanelsWithAnimation({
|
|
240
274
|
...panel,
|
|
275
|
+
x: nextGridX,
|
|
276
|
+
y: nextGridY,
|
|
241
277
|
w: nextGridW,
|
|
242
278
|
h: nextGridH
|
|
243
279
|
}, state.panels);
|
|
@@ -256,6 +292,7 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
256
292
|
state.panels,
|
|
257
293
|
updatePanelsWithAnimation,
|
|
258
294
|
showGhostPanel,
|
|
295
|
+
updateGhostPanelPosition,
|
|
259
296
|
updateGhostPanelSize,
|
|
260
297
|
hideGhostPanel,
|
|
261
298
|
columnCount
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePanelGrid.cjs","names":["findNewPositionToAddPanel","newPanelCoordinate: PanelCoordinate","rearrangePanels","detectAnimatingPanels","e","pixelsToGridPosition","gridPositionToPixels","pixelsToGridSize","gridToPixels"],"sources":["../src/usePanelGrid.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport {\n applySnapAnimation,\n detectAnimatingPanels,\n gridPositionToPixels,\n gridToPixels,\n pixelsToGridPosition,\n pixelsToGridSize,\n rearrangePanels,\n} from \"./helpers\";\nimport { findNewPositionToAddPanel } from \"./helpers/rearrangement\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction } from \"./types\";\n\ninterface PanelGridOptions {\n panels: PanelCoordinate[];\n columnCount: number;\n baseSize: number;\n gap: number;\n rearrangement?: RearrangementFunction;\n}\n\nexport interface PanelGridState {\n panels: PanelCoordinate[];\n}\n\ninterface InternalPanelState {\n activePanelId: PanelId | null;\n draggableElements: Record<PanelId, HTMLElement | null>;\n animatingPanels: Set<PanelId>;\n}\n\nexport type PanelGridAction =\n | { type: \"UPDATE_PANELS\"; newPanels: PanelCoordinate[] }\n | { type: \"ADD_PANEL\"; newPanel: Partial<PanelCoordinate>; columnCount: number }\n | { type: \"REMOVE_PANEL\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_SIZE\"; panelId: PanelId };\n\nexport function panelGridReducer(state: PanelGridState, action: PanelGridAction): PanelGridState {\n switch (action.type) {\n case \"UPDATE_PANELS\":\n return {\n ...state,\n panels: action.newPanels,\n };\n case \"ADD_PANEL\": {\n const { newPanel, columnCount } = action;\n const newPosition = findNewPositionToAddPanel(newPanel, state.panels, columnCount);\n const newPanelCoordinate: PanelCoordinate = {\n id: newPanel.id || Math.random().toString(36).substring(2, 15),\n x: newPosition.x,\n y: newPosition.y,\n w: newPanel.w || 1,\n h: newPanel.h || 1,\n };\n return {\n ...state,\n panels: [...state.panels, newPanelCoordinate],\n };\n }\n case \"REMOVE_PANEL\":\n return {\n ...state,\n panels: state.panels.filter((panel) => panel.id !== action.panelId),\n };\n case \"LOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: true } : panel)),\n };\n case \"UNLOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: false } : panel)),\n };\n default:\n return state;\n }\n}\n\nconst ANIMATION_DURATION = 300;\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nexport function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }: PanelGridOptions) {\n const [state, dispatch] = useReducer(panelGridReducer, {\n panels,\n });\n const ghostPanelRef = useRef<HTMLDivElement | null>(null);\n const animationTimeoutsRef = useRef<Set<TimeoutId>>(new Set());\n\n const panelMap = useMemo(() => {\n const map = new Map<PanelId, PanelCoordinate>();\n state.panels.forEach((panel) => {\n map.set(panel.id, panel);\n });\n return map;\n }, [state.panels]);\n\n const internalState = useRef<InternalPanelState>({\n activePanelId: null,\n draggableElements: {},\n animatingPanels: new Set(),\n }).current;\n\n // Cleanup animation timeouts on unmount\n useEffect(() => {\n return () => {\n animationTimeoutsRef.current.forEach((timeoutId) => clearTimeout(timeoutId));\n animationTimeoutsRef.current.clear();\n };\n }, []);\n\n // Ghost panel helper functions\n // Direct DOM manipulation is intentionally used here for performance.\n // This avoids React re-renders during high-frequency mousemove events.\n const showGhostPanel = useCallback((left: number, top: number, width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"block\";\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n ghostPanelRef.current.style.outline = \"1px dashed rgba(0, 0, 0, 0.2)\";\n }, []);\n\n const updateGhostPanelPosition = useCallback((left: number, top: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n }, []);\n\n const updateGhostPanelSize = useCallback((width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n }, []);\n\n const hideGhostPanel = useCallback(() => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"none\";\n }, []);\n\n // Callback to update panels and trigger animations\n const updatePanelsWithAnimation = useCallback(\n (updatedPanel: PanelCoordinate, currentPanels: PanelCoordinate[]) => {\n // Use custom rearrangement function if provided, otherwise use default\n const rearrange = rearrangement || rearrangePanels;\n const nextPanels = rearrange(updatedPanel, currentPanels, columnCount);\n\n // Detect which panels have been rearranged\n internalState.animatingPanels = detectAnimatingPanels({\n oldPanels: currentPanels,\n newPanels: nextPanels,\n excludePanelId: updatedPanel.id,\n });\n\n dispatch({ type: \"UPDATE_PANELS\", newPanels: nextPanels });\n\n // Clear animating panels after animation completes\n const timeoutId = setTimeout(() => {\n internalState.animatingPanels.clear();\n animationTimeoutsRef.current.delete(timeoutId);\n }, ANIMATION_DURATION);\n animationTimeoutsRef.current.add(timeoutId);\n },\n [columnCount, internalState, rearrangement]\n );\n\n // Create drag handler for a specific panel\n const createDragHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLDivElement>) => {\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n let isDragging = true;\n const initialX = e.clientX;\n const initialY = e.clientY;\n const offsetX = draggingElement.offsetLeft;\n const offsetY = draggingElement.offsetTop;\n const originalTransition = draggingElement.style.transition;\n\n document.body.classList.add(\"panelgrid-dragging\");\n\n draggingElement.classList.add(\"panelgrid-panel--dragging\");\n draggingElement.style.transition = \"\";\n\n showGhostPanel(offsetX, offsetY, draggingElement.offsetWidth, draggingElement.offsetHeight);\n\n const mouseUpListenerCtrl = new AbortController();\n const mouseMoveListenerCtrl = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return;\n if (!draggingElement) return;\n\n const currentX = e.clientX;\n const currentY = e.clientY;\n const deltaX = currentX - initialX;\n const deltaY = currentY - initialY;\n\n draggingElement.style.left = offsetX + deltaX + \"px\";\n draggingElement.style.top = offsetY + deltaY + \"px\";\n\n // Update ghost panel position to snap to grid\n const droppedLeft = offsetX + deltaX;\n const droppedTop = offsetY + deltaY;\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(nextLeft, nextTop);\n\n e.preventDefault(); // Prevent text selection during drag\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n isDragging = false;\n draggingElement.classList.remove(\"panelgrid-panel--dragging\");\n\n hideGhostPanel();\n\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n // Apply snap-back animation\n applySnapAnimation({\n element: draggingElement,\n droppedLeft,\n droppedTop,\n nextLeft,\n nextTop,\n originalTransition,\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY }, state.panels);\n\n document.body.classList.remove(\"panelgrid-dragging\");\n internalState.activePanelId = null;\n\n mouseMoveListenerCtrl.abort();\n mouseUpListenerCtrl.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveListenerCtrl.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpListenerCtrl.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create resize handler for a specific panel\n const createResizeHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLSpanElement>) => {\n e.stopPropagation();\n let isResizing = true;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const initialWidth = draggingElement.offsetWidth;\n const initialHeight = draggingElement.offsetHeight;\n const initialZIndex = draggingElement.style.zIndex;\n const initialCursor = draggingElement.style.cursor;\n\n document.body.classList.add(\"panelgrid-resizing\");\n\n draggingElement.style.cursor = \"nwse-resize\";\n draggingElement.style.transition = \"\";\n\n showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);\n\n const mouseMoveController = new AbortController();\n const mouseUpController = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n if (!draggingElement) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n draggingElement.style.width = `${initialWidth + deltaX}px`;\n draggingElement.style.height = `${initialHeight + deltaY}px`;\n draggingElement.style.zIndex = \"calc(infinity)\";\n\n // Update ghost panel size to snap to grid\n const newWidth = initialWidth + deltaX;\n const newHeight = initialHeight + deltaY;\n const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, panel.x);\n const nextGridH = pixelsToGridSize(newHeight, baseSize, gap);\n const snappedWidth = gridToPixels(nextGridW, baseSize, gap);\n const snappedHeight = gridToPixels(nextGridH, baseSize, gap);\n\n updateGhostPanelSize(snappedWidth, snappedHeight);\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n hideGhostPanel();\n\n const rect = draggingElement.getBoundingClientRect();\n const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, panel.x);\n const nextGridH = pixelsToGridSize(rect.height, baseSize, gap);\n\n const width = gridToPixels(nextGridW, baseSize, gap);\n const height = gridToPixels(nextGridH, baseSize, gap);\n\n draggingElement.style.width = `${rect.width}px`;\n draggingElement.style.height = `${rect.height}px`;\n draggingElement.style.cursor = initialCursor;\n draggingElement.style.transition = \"\";\n\n window.requestAnimationFrame(() => {\n draggingElement.style.width = `${width}px`;\n draggingElement.style.height = `${height}px`;\n draggingElement.style.zIndex = initialZIndex;\n draggingElement.style.transition = \"width 0.1s ease-out, height 0.1s ease-out\";\n });\n\n updatePanelsWithAnimation({ ...panel, w: nextGridW, h: nextGridH }, state.panels);\n\n isResizing = false;\n internalState.activePanelId = null;\n document.body.classList.remove(\"panelgrid-resizing\");\n\n mouseMoveController.abort();\n mouseUpController.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveController.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpController.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelSize,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create ref callback for panel elements\n const createRefCallback = useCallback(\n (panelId: PanelId) => (element: HTMLElement | null) => {\n if (!element) return;\n if (!internalState.draggableElements[panelId]) {\n internalState.draggableElements[panelId] = element;\n }\n },\n [internalState]\n );\n\n // Memoize panel props to avoid recreating on every render\n const panelsWithProps = useMemo(() => {\n return state.panels.map((panel) => {\n const isAnimating = internalState.animatingPanels.has(panel.id);\n const isActive = internalState.activePanelId === panel.id;\n\n return {\n panelProps: {\n key: panel.id,\n lockSize: panel.lockSize,\n positionData: {\n x: panel.x,\n y: panel.y,\n w: panel.w,\n h: panel.h,\n },\n style: {\n top: gridPositionToPixels(panel.y, baseSize, gap),\n left: gridPositionToPixels(panel.x, baseSize, gap),\n width: gridToPixels(panel.w, baseSize, gap),\n height: gridToPixels(panel.h, baseSize, gap),\n transition:\n isAnimating && !isActive\n ? \"top 0.3s ease-out, left 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out\"\n : undefined,\n },\n ref: createRefCallback(panel.id),\n onMouseDown: createDragHandler(panel),\n },\n resizeHandleProps: panel.lockSize\n ? undefined\n : {\n onMouseDown: createResizeHandler(panel),\n },\n };\n });\n }, [\n state.panels,\n baseSize,\n gap,\n internalState.animatingPanels,\n internalState.activePanelId,\n createRefCallback,\n createDragHandler,\n createResizeHandler,\n ]);\n\n const addPanel = useCallback(\n (panel: Partial<PanelCoordinate>) => {\n dispatch({\n type: \"ADD_PANEL\",\n newPanel: {\n ...panel,\n id: panel.id || Math.random().toString(36).substring(2, 15),\n },\n columnCount,\n });\n },\n [columnCount]\n );\n\n const removePanel = useCallback((id: PanelId) => {\n dispatch({ type: \"REMOVE_PANEL\", panelId: id });\n }, []);\n\n const lockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const unlockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const exportState = useCallback(() => {\n return state.panels;\n }, [state.panels]);\n\n return {\n panels: panelsWithProps,\n panelMap,\n ghostPanelRef,\n addPanel,\n removePanel,\n lockPanelSize,\n unlockPanelSize,\n exportState,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"usePanelGrid.cjs","names":["findNewPositionToAddPanel","newPanelCoordinate: PanelCoordinate","RESIZE_CURSOR_MAP: Record<string, string>","rearrangePanels","detectAnimatingPanels","e","pixelsToGridPosition","gridPositionToPixels","pixelsToGridSize","gridToPixels"],"sources":["../src/usePanelGrid.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport {\n applySnapAnimation,\n detectAnimatingPanels,\n gridPositionToPixels,\n gridToPixels,\n pixelsToGridPosition,\n pixelsToGridSize,\n rearrangePanels,\n} from \"./helpers\";\nimport { findNewPositionToAddPanel } from \"./helpers/rearrangement\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from \"./types\";\n\ninterface PanelGridOptions {\n panels: PanelCoordinate[];\n columnCount: number;\n baseSize: number;\n gap: number;\n resizeHandlePositions: ResizeHandlePosition[];\n rearrangement?: RearrangementFunction;\n}\n\nexport interface PanelGridState {\n panels: PanelCoordinate[];\n}\n\ninterface InternalPanelState {\n activePanelId: PanelId | null;\n draggableElements: Record<PanelId, HTMLElement | null>;\n animatingPanels: Set<PanelId>;\n}\n\nexport type PanelGridAction =\n | { type: \"UPDATE_PANELS\"; newPanels: PanelCoordinate[] }\n | { type: \"ADD_PANEL\"; newPanel: Partial<PanelCoordinate>; columnCount: number }\n | { type: \"REMOVE_PANEL\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_SIZE\"; panelId: PanelId };\n\nexport function panelGridReducer(state: PanelGridState, action: PanelGridAction): PanelGridState {\n switch (action.type) {\n case \"UPDATE_PANELS\":\n return {\n ...state,\n panels: action.newPanels,\n };\n case \"ADD_PANEL\": {\n const { newPanel, columnCount } = action;\n const newPosition = findNewPositionToAddPanel(newPanel, state.panels, columnCount);\n const newPanelCoordinate: PanelCoordinate = {\n id: newPanel.id || Math.random().toString(36).substring(2, 15),\n x: newPosition.x,\n y: newPosition.y,\n w: newPanel.w || 1,\n h: newPanel.h || 1,\n };\n return {\n ...state,\n panels: [...state.panels, newPanelCoordinate],\n };\n }\n case \"REMOVE_PANEL\":\n return {\n ...state,\n panels: state.panels.filter((panel) => panel.id !== action.panelId),\n };\n case \"LOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: true } : panel)),\n };\n case \"UNLOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: false } : panel)),\n };\n default:\n return state;\n }\n}\n\nconst ANIMATION_DURATION = 300;\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nconst RESIZE_CURSOR_MAP: Record<string, string> = {\n nw: \"nwse-resize\",\n ne: \"nesw-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\",\n n: \"ns-resize\",\n s: \"ns-resize\",\n e: \"ew-resize\",\n w: \"ew-resize\",\n};\n\nexport function usePanelGrid({\n panels,\n columnCount,\n baseSize,\n gap,\n resizeHandlePositions: _resizeHandlePositions,\n rearrangement,\n}: PanelGridOptions) {\n const [state, dispatch] = useReducer(panelGridReducer, {\n panels,\n });\n const ghostPanelRef = useRef<HTMLDivElement | null>(null);\n const animationTimeoutsRef = useRef<Set<TimeoutId>>(new Set());\n\n const panelMap = useMemo(() => {\n const map = new Map<PanelId, PanelCoordinate>();\n state.panels.forEach((panel) => {\n map.set(panel.id, panel);\n });\n return map;\n }, [state.panels]);\n\n const internalState = useRef<InternalPanelState>({\n activePanelId: null,\n draggableElements: {},\n animatingPanels: new Set(),\n }).current;\n\n // Cleanup animation timeouts on unmount\n useEffect(() => {\n return () => {\n animationTimeoutsRef.current.forEach((timeoutId) => clearTimeout(timeoutId));\n animationTimeoutsRef.current.clear();\n };\n }, []);\n\n // Ghost panel helper functions\n // Direct DOM manipulation is intentionally used here for performance.\n // This avoids React re-renders during high-frequency mousemove events.\n const showGhostPanel = useCallback((left: number, top: number, width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"block\";\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n ghostPanelRef.current.style.outline = \"1px dashed rgba(0, 0, 0, 0.2)\";\n }, []);\n\n const updateGhostPanelPosition = useCallback((left: number, top: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n }, []);\n\n const updateGhostPanelSize = useCallback((width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n }, []);\n\n const hideGhostPanel = useCallback(() => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"none\";\n }, []);\n\n // Callback to update panels and trigger animations\n const updatePanelsWithAnimation = useCallback(\n (updatedPanel: PanelCoordinate, currentPanels: PanelCoordinate[]) => {\n // Use custom rearrangement function if provided, otherwise use default\n const rearrange = rearrangement || rearrangePanels;\n const nextPanels = rearrange(updatedPanel, currentPanels, columnCount);\n\n // Detect which panels have been rearranged\n internalState.animatingPanels = detectAnimatingPanels({\n oldPanels: currentPanels,\n newPanels: nextPanels,\n excludePanelId: updatedPanel.id,\n });\n\n dispatch({ type: \"UPDATE_PANELS\", newPanels: nextPanels });\n\n // Clear animating panels after animation completes\n const timeoutId = setTimeout(() => {\n internalState.animatingPanels.clear();\n animationTimeoutsRef.current.delete(timeoutId);\n }, ANIMATION_DURATION);\n animationTimeoutsRef.current.add(timeoutId);\n },\n [columnCount, internalState, rearrangement]\n );\n\n // Create drag handler for a specific panel\n const createDragHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLDivElement>) => {\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n let isDragging = true;\n const initialX = e.clientX;\n const initialY = e.clientY;\n const offsetX = draggingElement.offsetLeft;\n const offsetY = draggingElement.offsetTop;\n const originalTransition = draggingElement.style.transition;\n\n document.body.classList.add(\"panelgrid-dragging\");\n\n draggingElement.classList.add(\"panelgrid-panel--dragging\");\n draggingElement.style.transition = \"\";\n\n showGhostPanel(offsetX, offsetY, draggingElement.offsetWidth, draggingElement.offsetHeight);\n\n const mouseUpListenerCtrl = new AbortController();\n const mouseMoveListenerCtrl = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return;\n if (!draggingElement) return;\n\n const currentX = e.clientX;\n const currentY = e.clientY;\n const deltaX = currentX - initialX;\n const deltaY = currentY - initialY;\n\n draggingElement.style.left = offsetX + deltaX + \"px\";\n draggingElement.style.top = offsetY + deltaY + \"px\";\n\n // Update ghost panel position to snap to grid\n const droppedLeft = offsetX + deltaX;\n const droppedTop = offsetY + deltaY;\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(nextLeft, nextTop);\n\n e.preventDefault(); // Prevent text selection during drag\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n isDragging = false;\n draggingElement.classList.remove(\"panelgrid-panel--dragging\");\n\n hideGhostPanel();\n\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n // Apply snap-back animation\n applySnapAnimation({\n element: draggingElement,\n droppedLeft,\n droppedTop,\n nextLeft,\n nextTop,\n originalTransition,\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY }, state.panels);\n\n document.body.classList.remove(\"panelgrid-dragging\");\n internalState.activePanelId = null;\n\n mouseMoveListenerCtrl.abort();\n mouseUpListenerCtrl.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveListenerCtrl.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpListenerCtrl.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create resize handler for a specific panel\n const createResizeHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLSpanElement>) => {\n e.stopPropagation();\n let isResizing = true;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const initialTop = draggingElement.offsetTop;\n const initialLeft = draggingElement.offsetLeft;\n const initialWidth = draggingElement.offsetWidth;\n const initialHeight = draggingElement.offsetHeight;\n const initialZIndex = draggingElement.style.zIndex;\n const initialCursor = draggingElement.style.cursor;\n\n const resizeHandle = e.currentTarget;\n const handlePosition = resizeHandle.dataset.pgResizeHandle;\n\n const northSideResizeEnabled = handlePosition?.includes(\"n\");\n const westSideResizeEnabled = handlePosition?.includes(\"w\");\n const isVerticalResizeOnly = handlePosition === \"n\" || handlePosition === \"s\";\n const isHorizontalResizeOnly = handlePosition === \"e\" || handlePosition === \"w\";\n\n document.body.classList.add(\"panelgrid-resizing\");\n\n draggingElement.style.cursor = RESIZE_CURSOR_MAP[handlePosition || \"se\"] || \"nwse-resize\";\n draggingElement.style.transition = \"\";\n\n showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);\n\n const mouseMoveController = new AbortController();\n const mouseUpController = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n if (!draggingElement) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n // Calculate dimensions once, accounting for all resize directions\n const newWidth = westSideResizeEnabled\n ? Math.max(initialWidth - deltaX, 1)\n : isVerticalResizeOnly\n ? initialWidth\n : initialWidth + deltaX;\n\n const newHeight = northSideResizeEnabled\n ? Math.max(initialHeight - deltaY, 1)\n : isHorizontalResizeOnly\n ? initialHeight\n : initialHeight + deltaY;\n\n draggingElement.style.width = `${newWidth}px`;\n draggingElement.style.height = `${newHeight}px`;\n draggingElement.style.zIndex = \"calc(infinity)\";\n\n // Update position for north/west resizing\n if (northSideResizeEnabled) {\n draggingElement.style.top = `${initialTop + deltaY}px`;\n }\n if (westSideResizeEnabled) {\n draggingElement.style.left = `${initialLeft + deltaX}px`;\n }\n\n // Calculate current position (needed for grid calculations)\n const currentLeft = westSideResizeEnabled ? initialLeft + deltaX : initialLeft;\n const currentTop = northSideResizeEnabled ? initialTop + deltaY : initialTop;\n\n // Update ghost panel - calculate grid position BEFORE grid size\n const nextGridX = pixelsToGridPosition(currentLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(currentTop, baseSize, gap);\n const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(newHeight, baseSize, gap, columnCount, nextGridY);\n\n const snappedWidth = gridToPixels(nextGridW, baseSize, gap);\n const snappedHeight = gridToPixels(nextGridH, baseSize, gap);\n const snappedLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const snappedTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(snappedLeft, snappedTop);\n updateGhostPanelSize(snappedWidth, snappedHeight);\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n hideGhostPanel();\n\n const rect = draggingElement.getBoundingClientRect();\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);\n\n const left = gridPositionToPixels(nextGridX, baseSize, gap);\n const top = gridPositionToPixels(nextGridY, baseSize, gap);\n const width = gridToPixels(nextGridW, baseSize, gap);\n const height = gridToPixels(nextGridH, baseSize, gap);\n\n draggingElement.style.width = `${rect.width}px`;\n draggingElement.style.height = `${rect.height}px`;\n draggingElement.style.cursor = initialCursor;\n draggingElement.style.transition = \"\";\n\n window.requestAnimationFrame(() => {\n draggingElement.style.top = `${top}px`;\n draggingElement.style.left = `${left}px`;\n draggingElement.style.width = `${width}px`;\n draggingElement.style.height = `${height}px`;\n draggingElement.style.zIndex = initialZIndex;\n draggingElement.style.transition =\n \"width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out\";\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY, w: nextGridW, h: nextGridH }, state.panels);\n\n isResizing = false;\n internalState.activePanelId = null;\n document.body.classList.remove(\"panelgrid-resizing\");\n\n mouseMoveController.abort();\n mouseUpController.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveController.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpController.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n updateGhostPanelSize,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create ref callback for panel elements\n const createRefCallback = useCallback(\n (panelId: PanelId) => (element: HTMLElement | null) => {\n if (!element) return;\n if (!internalState.draggableElements[panelId]) {\n internalState.draggableElements[panelId] = element;\n }\n },\n [internalState]\n );\n\n // Memoize panel props to avoid recreating on every render\n const panelsWithProps = useMemo(() => {\n return state.panels.map((panel) => {\n const isAnimating = internalState.animatingPanels.has(panel.id);\n const isActive = internalState.activePanelId === panel.id;\n\n return {\n panelProps: {\n key: panel.id,\n lockSize: panel.lockSize,\n positionData: {\n x: panel.x,\n y: panel.y,\n w: panel.w,\n h: panel.h,\n },\n style: {\n top: gridPositionToPixels(panel.y, baseSize, gap),\n left: gridPositionToPixels(panel.x, baseSize, gap),\n width: gridToPixels(panel.w, baseSize, gap),\n height: gridToPixels(panel.h, baseSize, gap),\n transition:\n isAnimating && !isActive\n ? \"top 0.3s ease-out, left 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out\"\n : undefined,\n },\n ref: createRefCallback(panel.id),\n onMouseDown: createDragHandler(panel),\n },\n resizeHandleProps: panel.lockSize\n ? undefined\n : {\n onMouseDown: createResizeHandler(panel),\n },\n };\n });\n }, [\n state.panels,\n baseSize,\n gap,\n internalState.animatingPanels,\n internalState.activePanelId,\n createRefCallback,\n createDragHandler,\n createResizeHandler,\n ]);\n\n const addPanel = useCallback(\n (panel: Partial<PanelCoordinate>) => {\n dispatch({\n type: \"ADD_PANEL\",\n newPanel: {\n ...panel,\n id: panel.id || Math.random().toString(36).substring(2, 15),\n },\n columnCount,\n });\n },\n [columnCount]\n );\n\n const removePanel = useCallback((id: PanelId) => {\n dispatch({ type: \"REMOVE_PANEL\", panelId: id });\n }, []);\n\n const lockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const unlockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const exportState = useCallback(() => {\n return state.panels;\n }, [state.panels]);\n\n return {\n panels: panelsWithProps,\n panelMap,\n ghostPanelRef,\n addPanel,\n removePanel,\n lockPanelSize,\n unlockPanelSize,\n exportState,\n };\n}\n"],"mappings":"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"}
|
package/dist/usePanelGrid.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PanelCoordinate, PanelId, RearrangementFunction } from "./types.cjs";
|
|
1
|
+
import { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from "./types.cjs";
|
|
2
2
|
import * as react0 from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/usePanelGrid.d.ts
|
|
@@ -7,6 +7,7 @@ interface PanelGridOptions {
|
|
|
7
7
|
columnCount: number;
|
|
8
8
|
baseSize: number;
|
|
9
9
|
gap: number;
|
|
10
|
+
resizeHandlePositions: ResizeHandlePosition[];
|
|
10
11
|
rearrangement?: RearrangementFunction;
|
|
11
12
|
}
|
|
12
13
|
declare function usePanelGrid({
|
|
@@ -14,6 +15,7 @@ declare function usePanelGrid({
|
|
|
14
15
|
columnCount,
|
|
15
16
|
baseSize,
|
|
16
17
|
gap,
|
|
18
|
+
resizeHandlePositions: _resizeHandlePositions,
|
|
17
19
|
rearrangement
|
|
18
20
|
}: PanelGridOptions): {
|
|
19
21
|
panels: {
|
package/dist/usePanelGrid.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PanelCoordinate, PanelId, RearrangementFunction } from "./types.mjs";
|
|
1
|
+
import { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from "./types.mjs";
|
|
2
2
|
import * as react0 from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/usePanelGrid.d.ts
|
|
@@ -7,6 +7,7 @@ interface PanelGridOptions {
|
|
|
7
7
|
columnCount: number;
|
|
8
8
|
baseSize: number;
|
|
9
9
|
gap: number;
|
|
10
|
+
resizeHandlePositions: ResizeHandlePosition[];
|
|
10
11
|
rearrangement?: RearrangementFunction;
|
|
11
12
|
}
|
|
12
13
|
declare function usePanelGrid({
|
|
@@ -14,6 +15,7 @@ declare function usePanelGrid({
|
|
|
14
15
|
columnCount,
|
|
15
16
|
baseSize,
|
|
16
17
|
gap,
|
|
18
|
+
resizeHandlePositions: _resizeHandlePositions,
|
|
17
19
|
rearrangement
|
|
18
20
|
}: PanelGridOptions): {
|
|
19
21
|
panels: {
|
package/dist/usePanelGrid.mjs
CHANGED
|
@@ -48,7 +48,17 @@ function panelGridReducer(state, action) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
const ANIMATION_DURATION = 300;
|
|
51
|
-
|
|
51
|
+
const RESIZE_CURSOR_MAP = {
|
|
52
|
+
nw: "nwse-resize",
|
|
53
|
+
ne: "nesw-resize",
|
|
54
|
+
se: "nwse-resize",
|
|
55
|
+
sw: "nesw-resize",
|
|
56
|
+
n: "ns-resize",
|
|
57
|
+
s: "ns-resize",
|
|
58
|
+
e: "ew-resize",
|
|
59
|
+
w: "ew-resize"
|
|
60
|
+
};
|
|
61
|
+
function usePanelGrid({ panels, columnCount, baseSize, gap, resizeHandlePositions: _resizeHandlePositions, rearrangement }) {
|
|
52
62
|
const [state, dispatch] = useReducer(panelGridReducer, { panels });
|
|
53
63
|
const ghostPanelRef = useRef(null);
|
|
54
64
|
const animationTimeoutsRef = useRef(/* @__PURE__ */ new Set());
|
|
@@ -194,12 +204,19 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
194
204
|
if (!draggingElement) return;
|
|
195
205
|
const startX = e.clientX;
|
|
196
206
|
const startY = e.clientY;
|
|
207
|
+
const initialTop = draggingElement.offsetTop;
|
|
208
|
+
const initialLeft = draggingElement.offsetLeft;
|
|
197
209
|
const initialWidth = draggingElement.offsetWidth;
|
|
198
210
|
const initialHeight = draggingElement.offsetHeight;
|
|
199
211
|
const initialZIndex = draggingElement.style.zIndex;
|
|
200
212
|
const initialCursor = draggingElement.style.cursor;
|
|
213
|
+
const handlePosition = e.currentTarget.dataset.pgResizeHandle;
|
|
214
|
+
const northSideResizeEnabled = handlePosition?.includes("n");
|
|
215
|
+
const westSideResizeEnabled = handlePosition?.includes("w");
|
|
216
|
+
const isVerticalResizeOnly = handlePosition === "n" || handlePosition === "s";
|
|
217
|
+
const isHorizontalResizeOnly = handlePosition === "e" || handlePosition === "w";
|
|
201
218
|
document.body.classList.add("panelgrid-resizing");
|
|
202
|
-
draggingElement.style.cursor = "nwse-resize";
|
|
219
|
+
draggingElement.style.cursor = RESIZE_CURSOR_MAP[handlePosition || "se"] || "nwse-resize";
|
|
203
220
|
draggingElement.style.transition = "";
|
|
204
221
|
showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);
|
|
205
222
|
const mouseMoveController = new AbortController();
|
|
@@ -209,21 +226,36 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
209
226
|
if (!draggingElement) return;
|
|
210
227
|
const deltaX = e$1.clientX - startX;
|
|
211
228
|
const deltaY = e$1.clientY - startY;
|
|
212
|
-
|
|
213
|
-
|
|
229
|
+
const newWidth = westSideResizeEnabled ? Math.max(initialWidth - deltaX, 1) : isVerticalResizeOnly ? initialWidth : initialWidth + deltaX;
|
|
230
|
+
const newHeight = northSideResizeEnabled ? Math.max(initialHeight - deltaY, 1) : isHorizontalResizeOnly ? initialHeight : initialHeight + deltaY;
|
|
231
|
+
draggingElement.style.width = `${newWidth}px`;
|
|
232
|
+
draggingElement.style.height = `${newHeight}px`;
|
|
214
233
|
draggingElement.style.zIndex = "calc(infinity)";
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
|
|
234
|
+
if (northSideResizeEnabled) draggingElement.style.top = `${initialTop + deltaY}px`;
|
|
235
|
+
if (westSideResizeEnabled) draggingElement.style.left = `${initialLeft + deltaX}px`;
|
|
236
|
+
const currentLeft = westSideResizeEnabled ? initialLeft + deltaX : initialLeft;
|
|
237
|
+
const currentTop = northSideResizeEnabled ? initialTop + deltaY : initialTop;
|
|
238
|
+
const nextGridX = pixelsToGridPosition(currentLeft, baseSize, gap, columnCount, panel.w);
|
|
239
|
+
const nextGridY = pixelsToGridPosition(currentTop, baseSize, gap);
|
|
240
|
+
const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, nextGridX);
|
|
241
|
+
const nextGridH = pixelsToGridSize(newHeight, baseSize, gap, columnCount, nextGridY);
|
|
242
|
+
const snappedWidth = gridToPixels(nextGridW, baseSize, gap);
|
|
243
|
+
const snappedHeight = gridToPixels(nextGridH, baseSize, gap);
|
|
244
|
+
updateGhostPanelPosition(gridPositionToPixels(nextGridX, baseSize, gap), gridPositionToPixels(nextGridY, baseSize, gap));
|
|
245
|
+
updateGhostPanelSize(snappedWidth, snappedHeight);
|
|
220
246
|
};
|
|
221
247
|
const onMouseUp = () => {
|
|
222
248
|
if (!draggingElement) return;
|
|
223
249
|
hideGhostPanel();
|
|
224
250
|
const rect = draggingElement.getBoundingClientRect();
|
|
225
|
-
const
|
|
226
|
-
const
|
|
251
|
+
const droppedLeft = parseFloat(draggingElement.style.left) || 0;
|
|
252
|
+
const droppedTop = parseFloat(draggingElement.style.top) || 0;
|
|
253
|
+
const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);
|
|
254
|
+
const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);
|
|
255
|
+
const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);
|
|
256
|
+
const nextGridH = pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);
|
|
257
|
+
const left = gridPositionToPixels(nextGridX, baseSize, gap);
|
|
258
|
+
const top = gridPositionToPixels(nextGridY, baseSize, gap);
|
|
227
259
|
const width = gridToPixels(nextGridW, baseSize, gap);
|
|
228
260
|
const height = gridToPixels(nextGridH, baseSize, gap);
|
|
229
261
|
draggingElement.style.width = `${rect.width}px`;
|
|
@@ -231,13 +263,17 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
231
263
|
draggingElement.style.cursor = initialCursor;
|
|
232
264
|
draggingElement.style.transition = "";
|
|
233
265
|
window.requestAnimationFrame(() => {
|
|
266
|
+
draggingElement.style.top = `${top}px`;
|
|
267
|
+
draggingElement.style.left = `${left}px`;
|
|
234
268
|
draggingElement.style.width = `${width}px`;
|
|
235
269
|
draggingElement.style.height = `${height}px`;
|
|
236
270
|
draggingElement.style.zIndex = initialZIndex;
|
|
237
|
-
draggingElement.style.transition = "width 0.1s ease-out, height 0.1s ease-out";
|
|
271
|
+
draggingElement.style.transition = "width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out";
|
|
238
272
|
});
|
|
239
273
|
updatePanelsWithAnimation({
|
|
240
274
|
...panel,
|
|
275
|
+
x: nextGridX,
|
|
276
|
+
y: nextGridY,
|
|
241
277
|
w: nextGridW,
|
|
242
278
|
h: nextGridH
|
|
243
279
|
}, state.panels);
|
|
@@ -256,6 +292,7 @@ function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
|
|
|
256
292
|
state.panels,
|
|
257
293
|
updatePanelsWithAnimation,
|
|
258
294
|
showGhostPanel,
|
|
295
|
+
updateGhostPanelPosition,
|
|
259
296
|
updateGhostPanelSize,
|
|
260
297
|
hideGhostPanel,
|
|
261
298
|
columnCount
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePanelGrid.mjs","names":["newPanelCoordinate: PanelCoordinate","e"],"sources":["../src/usePanelGrid.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport {\n applySnapAnimation,\n detectAnimatingPanels,\n gridPositionToPixels,\n gridToPixels,\n pixelsToGridPosition,\n pixelsToGridSize,\n rearrangePanels,\n} from \"./helpers\";\nimport { findNewPositionToAddPanel } from \"./helpers/rearrangement\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction } from \"./types\";\n\ninterface PanelGridOptions {\n panels: PanelCoordinate[];\n columnCount: number;\n baseSize: number;\n gap: number;\n rearrangement?: RearrangementFunction;\n}\n\nexport interface PanelGridState {\n panels: PanelCoordinate[];\n}\n\ninterface InternalPanelState {\n activePanelId: PanelId | null;\n draggableElements: Record<PanelId, HTMLElement | null>;\n animatingPanels: Set<PanelId>;\n}\n\nexport type PanelGridAction =\n | { type: \"UPDATE_PANELS\"; newPanels: PanelCoordinate[] }\n | { type: \"ADD_PANEL\"; newPanel: Partial<PanelCoordinate>; columnCount: number }\n | { type: \"REMOVE_PANEL\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_SIZE\"; panelId: PanelId };\n\nexport function panelGridReducer(state: PanelGridState, action: PanelGridAction): PanelGridState {\n switch (action.type) {\n case \"UPDATE_PANELS\":\n return {\n ...state,\n panels: action.newPanels,\n };\n case \"ADD_PANEL\": {\n const { newPanel, columnCount } = action;\n const newPosition = findNewPositionToAddPanel(newPanel, state.panels, columnCount);\n const newPanelCoordinate: PanelCoordinate = {\n id: newPanel.id || Math.random().toString(36).substring(2, 15),\n x: newPosition.x,\n y: newPosition.y,\n w: newPanel.w || 1,\n h: newPanel.h || 1,\n };\n return {\n ...state,\n panels: [...state.panels, newPanelCoordinate],\n };\n }\n case \"REMOVE_PANEL\":\n return {\n ...state,\n panels: state.panels.filter((panel) => panel.id !== action.panelId),\n };\n case \"LOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: true } : panel)),\n };\n case \"UNLOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: false } : panel)),\n };\n default:\n return state;\n }\n}\n\nconst ANIMATION_DURATION = 300;\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nexport function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }: PanelGridOptions) {\n const [state, dispatch] = useReducer(panelGridReducer, {\n panels,\n });\n const ghostPanelRef = useRef<HTMLDivElement | null>(null);\n const animationTimeoutsRef = useRef<Set<TimeoutId>>(new Set());\n\n const panelMap = useMemo(() => {\n const map = new Map<PanelId, PanelCoordinate>();\n state.panels.forEach((panel) => {\n map.set(panel.id, panel);\n });\n return map;\n }, [state.panels]);\n\n const internalState = useRef<InternalPanelState>({\n activePanelId: null,\n draggableElements: {},\n animatingPanels: new Set(),\n }).current;\n\n // Cleanup animation timeouts on unmount\n useEffect(() => {\n return () => {\n animationTimeoutsRef.current.forEach((timeoutId) => clearTimeout(timeoutId));\n animationTimeoutsRef.current.clear();\n };\n }, []);\n\n // Ghost panel helper functions\n // Direct DOM manipulation is intentionally used here for performance.\n // This avoids React re-renders during high-frequency mousemove events.\n const showGhostPanel = useCallback((left: number, top: number, width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"block\";\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n ghostPanelRef.current.style.outline = \"1px dashed rgba(0, 0, 0, 0.2)\";\n }, []);\n\n const updateGhostPanelPosition = useCallback((left: number, top: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n }, []);\n\n const updateGhostPanelSize = useCallback((width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n }, []);\n\n const hideGhostPanel = useCallback(() => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"none\";\n }, []);\n\n // Callback to update panels and trigger animations\n const updatePanelsWithAnimation = useCallback(\n (updatedPanel: PanelCoordinate, currentPanels: PanelCoordinate[]) => {\n // Use custom rearrangement function if provided, otherwise use default\n const rearrange = rearrangement || rearrangePanels;\n const nextPanels = rearrange(updatedPanel, currentPanels, columnCount);\n\n // Detect which panels have been rearranged\n internalState.animatingPanels = detectAnimatingPanels({\n oldPanels: currentPanels,\n newPanels: nextPanels,\n excludePanelId: updatedPanel.id,\n });\n\n dispatch({ type: \"UPDATE_PANELS\", newPanels: nextPanels });\n\n // Clear animating panels after animation completes\n const timeoutId = setTimeout(() => {\n internalState.animatingPanels.clear();\n animationTimeoutsRef.current.delete(timeoutId);\n }, ANIMATION_DURATION);\n animationTimeoutsRef.current.add(timeoutId);\n },\n [columnCount, internalState, rearrangement]\n );\n\n // Create drag handler for a specific panel\n const createDragHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLDivElement>) => {\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n let isDragging = true;\n const initialX = e.clientX;\n const initialY = e.clientY;\n const offsetX = draggingElement.offsetLeft;\n const offsetY = draggingElement.offsetTop;\n const originalTransition = draggingElement.style.transition;\n\n document.body.classList.add(\"panelgrid-dragging\");\n\n draggingElement.classList.add(\"panelgrid-panel--dragging\");\n draggingElement.style.transition = \"\";\n\n showGhostPanel(offsetX, offsetY, draggingElement.offsetWidth, draggingElement.offsetHeight);\n\n const mouseUpListenerCtrl = new AbortController();\n const mouseMoveListenerCtrl = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return;\n if (!draggingElement) return;\n\n const currentX = e.clientX;\n const currentY = e.clientY;\n const deltaX = currentX - initialX;\n const deltaY = currentY - initialY;\n\n draggingElement.style.left = offsetX + deltaX + \"px\";\n draggingElement.style.top = offsetY + deltaY + \"px\";\n\n // Update ghost panel position to snap to grid\n const droppedLeft = offsetX + deltaX;\n const droppedTop = offsetY + deltaY;\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(nextLeft, nextTop);\n\n e.preventDefault(); // Prevent text selection during drag\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n isDragging = false;\n draggingElement.classList.remove(\"panelgrid-panel--dragging\");\n\n hideGhostPanel();\n\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n // Apply snap-back animation\n applySnapAnimation({\n element: draggingElement,\n droppedLeft,\n droppedTop,\n nextLeft,\n nextTop,\n originalTransition,\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY }, state.panels);\n\n document.body.classList.remove(\"panelgrid-dragging\");\n internalState.activePanelId = null;\n\n mouseMoveListenerCtrl.abort();\n mouseUpListenerCtrl.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveListenerCtrl.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpListenerCtrl.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create resize handler for a specific panel\n const createResizeHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLSpanElement>) => {\n e.stopPropagation();\n let isResizing = true;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const initialWidth = draggingElement.offsetWidth;\n const initialHeight = draggingElement.offsetHeight;\n const initialZIndex = draggingElement.style.zIndex;\n const initialCursor = draggingElement.style.cursor;\n\n document.body.classList.add(\"panelgrid-resizing\");\n\n draggingElement.style.cursor = \"nwse-resize\";\n draggingElement.style.transition = \"\";\n\n showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);\n\n const mouseMoveController = new AbortController();\n const mouseUpController = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n if (!draggingElement) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n draggingElement.style.width = `${initialWidth + deltaX}px`;\n draggingElement.style.height = `${initialHeight + deltaY}px`;\n draggingElement.style.zIndex = \"calc(infinity)\";\n\n // Update ghost panel size to snap to grid\n const newWidth = initialWidth + deltaX;\n const newHeight = initialHeight + deltaY;\n const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, panel.x);\n const nextGridH = pixelsToGridSize(newHeight, baseSize, gap);\n const snappedWidth = gridToPixels(nextGridW, baseSize, gap);\n const snappedHeight = gridToPixels(nextGridH, baseSize, gap);\n\n updateGhostPanelSize(snappedWidth, snappedHeight);\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n hideGhostPanel();\n\n const rect = draggingElement.getBoundingClientRect();\n const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, panel.x);\n const nextGridH = pixelsToGridSize(rect.height, baseSize, gap);\n\n const width = gridToPixels(nextGridW, baseSize, gap);\n const height = gridToPixels(nextGridH, baseSize, gap);\n\n draggingElement.style.width = `${rect.width}px`;\n draggingElement.style.height = `${rect.height}px`;\n draggingElement.style.cursor = initialCursor;\n draggingElement.style.transition = \"\";\n\n window.requestAnimationFrame(() => {\n draggingElement.style.width = `${width}px`;\n draggingElement.style.height = `${height}px`;\n draggingElement.style.zIndex = initialZIndex;\n draggingElement.style.transition = \"width 0.1s ease-out, height 0.1s ease-out\";\n });\n\n updatePanelsWithAnimation({ ...panel, w: nextGridW, h: nextGridH }, state.panels);\n\n isResizing = false;\n internalState.activePanelId = null;\n document.body.classList.remove(\"panelgrid-resizing\");\n\n mouseMoveController.abort();\n mouseUpController.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveController.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpController.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelSize,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create ref callback for panel elements\n const createRefCallback = useCallback(\n (panelId: PanelId) => (element: HTMLElement | null) => {\n if (!element) return;\n if (!internalState.draggableElements[panelId]) {\n internalState.draggableElements[panelId] = element;\n }\n },\n [internalState]\n );\n\n // Memoize panel props to avoid recreating on every render\n const panelsWithProps = useMemo(() => {\n return state.panels.map((panel) => {\n const isAnimating = internalState.animatingPanels.has(panel.id);\n const isActive = internalState.activePanelId === panel.id;\n\n return {\n panelProps: {\n key: panel.id,\n lockSize: panel.lockSize,\n positionData: {\n x: panel.x,\n y: panel.y,\n w: panel.w,\n h: panel.h,\n },\n style: {\n top: gridPositionToPixels(panel.y, baseSize, gap),\n left: gridPositionToPixels(panel.x, baseSize, gap),\n width: gridToPixels(panel.w, baseSize, gap),\n height: gridToPixels(panel.h, baseSize, gap),\n transition:\n isAnimating && !isActive\n ? \"top 0.3s ease-out, left 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out\"\n : undefined,\n },\n ref: createRefCallback(panel.id),\n onMouseDown: createDragHandler(panel),\n },\n resizeHandleProps: panel.lockSize\n ? undefined\n : {\n onMouseDown: createResizeHandler(panel),\n },\n };\n });\n }, [\n state.panels,\n baseSize,\n gap,\n internalState.animatingPanels,\n internalState.activePanelId,\n createRefCallback,\n createDragHandler,\n createResizeHandler,\n ]);\n\n const addPanel = useCallback(\n (panel: Partial<PanelCoordinate>) => {\n dispatch({\n type: \"ADD_PANEL\",\n newPanel: {\n ...panel,\n id: panel.id || Math.random().toString(36).substring(2, 15),\n },\n columnCount,\n });\n },\n [columnCount]\n );\n\n const removePanel = useCallback((id: PanelId) => {\n dispatch({ type: \"REMOVE_PANEL\", panelId: id });\n }, []);\n\n const lockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const unlockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const exportState = useCallback(() => {\n return state.panels;\n }, [state.panels]);\n\n return {\n panels: panelsWithProps,\n panelMap,\n ghostPanelRef,\n addPanel,\n removePanel,\n lockPanelSize,\n unlockPanelSize,\n exportState,\n };\n}\n"],"mappings":";;;;;;;AAsCA,SAAgB,iBAAiB,OAAuB,QAAyC;AAC/F,SAAQ,OAAO,MAAf;EACE,KAAK,gBACH,QAAO;GACL,GAAG;GACH,QAAQ,OAAO;GAChB;EACH,KAAK,aAAa;GAChB,MAAM,EAAE,UAAU,gBAAgB;GAClC,MAAM,cAAc,0BAA0B,UAAU,MAAM,QAAQ,YAAY;GAClF,MAAMA,qBAAsC;IAC1C,IAAI,SAAS,MAAM,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,GAAG,GAAG;IAC9D,GAAG,YAAY;IACf,GAAG,YAAY;IACf,GAAG,SAAS,KAAK;IACjB,GAAG,SAAS,KAAK;IAClB;AACD,UAAO;IACL,GAAG;IACH,QAAQ,CAAC,GAAG,MAAM,QAAQ,mBAAmB;IAC9C;;EAEH,KAAK,eACH,QAAO;GACL,GAAG;GACH,QAAQ,MAAM,OAAO,QAAQ,UAAU,MAAM,OAAO,OAAO,QAAQ;GACpE;EACH,KAAK,kBACH,QAAO;GACL,GAAG;GACH,QAAQ,MAAM,OAAO,KAAK,UAAW,MAAM,OAAO,OAAO,UAAU;IAAE,GAAG;IAAO,UAAU;IAAM,GAAG,MAAO;GAC1G;EACH,KAAK,oBACH,QAAO;GACL,GAAG;GACH,QAAQ,MAAM,OAAO,KAAK,UAAW,MAAM,OAAO,OAAO,UAAU;IAAE,GAAG;IAAO,UAAU;IAAO,GAAG,MAAO;GAC3G;EACH,QACE,QAAO;;;AAIb,MAAM,qBAAqB;AAG3B,SAAgB,aAAa,EAAE,QAAQ,aAAa,UAAU,KAAK,iBAAmC;CACpG,MAAM,CAAC,OAAO,YAAY,WAAW,kBAAkB,EACrD,QACD,CAAC;CACF,MAAM,gBAAgB,OAA8B,KAAK;CACzD,MAAM,uBAAuB,uBAAuB,IAAI,KAAK,CAAC;CAE9D,MAAM,WAAW,cAAc;EAC7B,MAAM,sBAAM,IAAI,KAA+B;AAC/C,QAAM,OAAO,SAAS,UAAU;AAC9B,OAAI,IAAI,MAAM,IAAI,MAAM;IACxB;AACF,SAAO;IACN,CAAC,MAAM,OAAO,CAAC;CAElB,MAAM,gBAAgB,OAA2B;EAC/C,eAAe;EACf,mBAAmB,EAAE;EACrB,iCAAiB,IAAI,KAAK;EAC3B,CAAC,CAAC;AAGH,iBAAgB;AACd,eAAa;AACX,wBAAqB,QAAQ,SAAS,cAAc,aAAa,UAAU,CAAC;AAC5E,wBAAqB,QAAQ,OAAO;;IAErC,EAAE,CAAC;CAKN,MAAM,iBAAiB,aAAa,MAAc,KAAa,OAAe,WAAmB;AAC/F,MAAI,CAAC,cAAc,QAAS;AAC5B,gBAAc,QAAQ,MAAM,UAAU;AACtC,gBAAc,QAAQ,MAAM,OAAO,GAAG,KAAK;AAC3C,gBAAc,QAAQ,MAAM,MAAM,GAAG,IAAI;AACzC,gBAAc,QAAQ,MAAM,QAAQ,GAAG,MAAM;AAC7C,gBAAc,QAAQ,MAAM,SAAS,GAAG,OAAO;AAC/C,gBAAc,QAAQ,MAAM,UAAU;IACrC,EAAE,CAAC;CAEN,MAAM,2BAA2B,aAAa,MAAc,QAAgB;AAC1E,MAAI,CAAC,cAAc,QAAS;AAC5B,gBAAc,QAAQ,MAAM,OAAO,GAAG,KAAK;AAC3C,gBAAc,QAAQ,MAAM,MAAM,GAAG,IAAI;IACxC,EAAE,CAAC;CAEN,MAAM,uBAAuB,aAAa,OAAe,WAAmB;AAC1E,MAAI,CAAC,cAAc,QAAS;AAC5B,gBAAc,QAAQ,MAAM,QAAQ,GAAG,MAAM;AAC7C,gBAAc,QAAQ,MAAM,SAAS,GAAG,OAAO;IAC9C,EAAE,CAAC;CAEN,MAAM,iBAAiB,kBAAkB;AACvC,MAAI,CAAC,cAAc,QAAS;AAC5B,gBAAc,QAAQ,MAAM,UAAU;IACrC,EAAE,CAAC;CAGN,MAAM,4BAA4B,aAC/B,cAA+B,kBAAqC;EAGnE,MAAM,cADY,iBAAiB,iBACN,cAAc,eAAe,YAAY;AAGtE,gBAAc,kBAAkB,sBAAsB;GACpD,WAAW;GACX,WAAW;GACX,gBAAgB,aAAa;GAC9B,CAAC;AAEF,WAAS;GAAE,MAAM;GAAiB,WAAW;GAAY,CAAC;EAG1D,MAAM,YAAY,iBAAiB;AACjC,iBAAc,gBAAgB,OAAO;AACrC,wBAAqB,QAAQ,OAAO,UAAU;KAC7C,mBAAmB;AACtB,uBAAqB,QAAQ,IAAI,UAAU;IAE7C;EAAC;EAAa;EAAe;EAAc,CAC5C;CAGD,MAAM,oBAAoB,aACvB,WAA4B,MAAwC;AACnE,gBAAc,gBAAgB,MAAM;EACpC,MAAM,kBAAkB,cAAc,kBAAkB,MAAM;AAC9D,MAAI,CAAC,gBAAiB;EAEtB,IAAI,aAAa;EACjB,MAAM,WAAW,EAAE;EACnB,MAAM,WAAW,EAAE;EACnB,MAAM,UAAU,gBAAgB;EAChC,MAAM,UAAU,gBAAgB;EAChC,MAAM,qBAAqB,gBAAgB,MAAM;AAEjD,WAAS,KAAK,UAAU,IAAI,qBAAqB;AAEjD,kBAAgB,UAAU,IAAI,4BAA4B;AAC1D,kBAAgB,MAAM,aAAa;AAEnC,iBAAe,SAAS,SAAS,gBAAgB,aAAa,gBAAgB,aAAa;EAE3F,MAAM,sBAAsB,IAAI,iBAAiB;EACjD,MAAM,wBAAwB,IAAI,iBAAiB;EAEnD,MAAM,eAAe,QAAkB;AACrC,OAAI,CAAC,WAAY;AACjB,OAAI,CAAC,gBAAiB;GAEtB,MAAM,WAAWC,IAAE;GACnB,MAAM,WAAWA,IAAE;GACnB,MAAM,SAAS,WAAW;GAC1B,MAAM,SAAS,WAAW;AAE1B,mBAAgB,MAAM,OAAO,UAAU,SAAS;AAChD,mBAAgB,MAAM,MAAM,UAAU,SAAS;GAG/C,MAAM,cAAc,UAAU;GAC9B,MAAM,aAAa,UAAU;GAC7B,MAAM,YAAY,qBAAqB,aAAa,UAAU,KAAK,aAAa,MAAM,EAAE;GACxF,MAAM,YAAY,qBAAqB,YAAY,UAAU,IAAI;AAIjE,4BAHiB,qBAAqB,WAAW,UAAU,IAAI,EAC/C,qBAAqB,WAAW,UAAU,IAAI,CAEnB;AAE3C,OAAE,gBAAgB;;EAGpB,MAAM,kBAAkB;AACtB,OAAI,CAAC,gBAAiB;AAEtB,gBAAa;AACb,mBAAgB,UAAU,OAAO,4BAA4B;AAE7D,mBAAgB;GAEhB,MAAM,cAAc,WAAW,gBAAgB,MAAM,KAAK,IAAI;GAC9D,MAAM,aAAa,WAAW,gBAAgB,MAAM,IAAI,IAAI;GAE5D,MAAM,YAAY,qBAAqB,aAAa,UAAU,KAAK,aAAa,MAAM,EAAE;GACxF,MAAM,YAAY,qBAAqB,YAAY,UAAU,IAAI;AAMjE,sBAAmB;IACjB,SAAS;IACT;IACA;IACA,UARe,qBAAqB,WAAW,UAAU,IAAI;IAS7D,SARc,qBAAqB,WAAW,UAAU,IAAI;IAS5D;IACD,CAAC;AAEF,6BAA0B;IAAE,GAAG;IAAO,GAAG;IAAW,GAAG;IAAW,EAAE,MAAM,OAAO;AAEjF,YAAS,KAAK,UAAU,OAAO,qBAAqB;AACpD,iBAAc,gBAAgB;AAE9B,yBAAsB,OAAO;AAC7B,uBAAoB,OAAO;;AAG7B,WAAS,iBAAiB,aAAa,aAAa,EAClD,QAAQ,sBAAsB,QAC/B,CAAC;AACF,WAAS,iBAAiB,WAAW,WAAW,EAC9C,QAAQ,oBAAoB,QAC7B,CAAC;IAEJ;EACE;EACA;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;EACA;EACD,CACF;CAGD,MAAM,sBAAsB,aACzB,WAA4B,MAAyC;AACpE,IAAE,iBAAiB;EACnB,IAAI,aAAa;AACjB,gBAAc,gBAAgB,MAAM;EACpC,MAAM,kBAAkB,cAAc,kBAAkB,MAAM;AAC9D,MAAI,CAAC,gBAAiB;EAEtB,MAAM,SAAS,EAAE;EACjB,MAAM,SAAS,EAAE;EACjB,MAAM,eAAe,gBAAgB;EACrC,MAAM,gBAAgB,gBAAgB;EACtC,MAAM,gBAAgB,gBAAgB,MAAM;EAC5C,MAAM,gBAAgB,gBAAgB,MAAM;AAE5C,WAAS,KAAK,UAAU,IAAI,qBAAqB;AAEjD,kBAAgB,MAAM,SAAS;AAC/B,kBAAgB,MAAM,aAAa;AAEnC,iBAAe,gBAAgB,YAAY,gBAAgB,WAAW,cAAc,cAAc;EAElG,MAAM,sBAAsB,IAAI,iBAAiB;EACjD,MAAM,oBAAoB,IAAI,iBAAiB;EAE/C,MAAM,eAAe,QAAkB;AACrC,OAAI,CAAC,WAAY;AACjB,OAAI,CAAC,gBAAiB;GAEtB,MAAM,SAASA,IAAE,UAAU;GAC3B,MAAM,SAASA,IAAE,UAAU;AAE3B,mBAAgB,MAAM,QAAQ,GAAG,eAAe,OAAO;AACvD,mBAAgB,MAAM,SAAS,GAAG,gBAAgB,OAAO;AACzD,mBAAgB,MAAM,SAAS;GAG/B,MAAM,WAAW,eAAe;GAChC,MAAM,YAAY,gBAAgB;GAClC,MAAM,YAAY,iBAAiB,UAAU,UAAU,KAAK,aAAa,MAAM,EAAE;GACjF,MAAM,YAAY,iBAAiB,WAAW,UAAU,IAAI;AAI5D,wBAHqB,aAAa,WAAW,UAAU,IAAI,EACrC,aAAa,WAAW,UAAU,IAAI,CAEX;;EAGnD,MAAM,kBAAkB;AACtB,OAAI,CAAC,gBAAiB;AAEtB,mBAAgB;GAEhB,MAAM,OAAO,gBAAgB,uBAAuB;GACpD,MAAM,YAAY,iBAAiB,KAAK,OAAO,UAAU,KAAK,aAAa,MAAM,EAAE;GACnF,MAAM,YAAY,iBAAiB,KAAK,QAAQ,UAAU,IAAI;GAE9D,MAAM,QAAQ,aAAa,WAAW,UAAU,IAAI;GACpD,MAAM,SAAS,aAAa,WAAW,UAAU,IAAI;AAErD,mBAAgB,MAAM,QAAQ,GAAG,KAAK,MAAM;AAC5C,mBAAgB,MAAM,SAAS,GAAG,KAAK,OAAO;AAC9C,mBAAgB,MAAM,SAAS;AAC/B,mBAAgB,MAAM,aAAa;AAEnC,UAAO,4BAA4B;AACjC,oBAAgB,MAAM,QAAQ,GAAG,MAAM;AACvC,oBAAgB,MAAM,SAAS,GAAG,OAAO;AACzC,oBAAgB,MAAM,SAAS;AAC/B,oBAAgB,MAAM,aAAa;KACnC;AAEF,6BAA0B;IAAE,GAAG;IAAO,GAAG;IAAW,GAAG;IAAW,EAAE,MAAM,OAAO;AAEjF,gBAAa;AACb,iBAAc,gBAAgB;AAC9B,YAAS,KAAK,UAAU,OAAO,qBAAqB;AAEpD,uBAAoB,OAAO;AAC3B,qBAAkB,OAAO;;AAG3B,WAAS,iBAAiB,aAAa,aAAa,EAClD,QAAQ,oBAAoB,QAC7B,CAAC;AACF,WAAS,iBAAiB,WAAW,WAAW,EAC9C,QAAQ,kBAAkB,QAC3B,CAAC;IAEJ;EACE;EACA;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;EACA;EACD,CACF;CAGD,MAAM,oBAAoB,aACvB,aAAsB,YAAgC;AACrD,MAAI,CAAC,QAAS;AACd,MAAI,CAAC,cAAc,kBAAkB,SACnC,eAAc,kBAAkB,WAAW;IAG/C,CAAC,cAAc,CAChB;AA+ED,QAAO;EACL,QA7EsB,cAAc;AACpC,UAAO,MAAM,OAAO,KAAK,UAAU;IACjC,MAAM,cAAc,cAAc,gBAAgB,IAAI,MAAM,GAAG;IAC/D,MAAM,WAAW,cAAc,kBAAkB,MAAM;AAEvD,WAAO;KACL,YAAY;MACV,KAAK,MAAM;MACX,UAAU,MAAM;MAChB,cAAc;OACZ,GAAG,MAAM;OACT,GAAG,MAAM;OACT,GAAG,MAAM;OACT,GAAG,MAAM;OACV;MACD,OAAO;OACL,KAAK,qBAAqB,MAAM,GAAG,UAAU,IAAI;OACjD,MAAM,qBAAqB,MAAM,GAAG,UAAU,IAAI;OAClD,OAAO,aAAa,MAAM,GAAG,UAAU,IAAI;OAC3C,QAAQ,aAAa,MAAM,GAAG,UAAU,IAAI;OAC5C,YACE,eAAe,CAAC,WACZ,qFACA;OACP;MACD,KAAK,kBAAkB,MAAM,GAAG;MAChC,aAAa,kBAAkB,MAAM;MACtC;KACD,mBAAmB,MAAM,WACrB,SACA,EACE,aAAa,oBAAoB,MAAM,EACxC;KACN;KACD;KACD;GACD,MAAM;GACN;GACA;GACA,cAAc;GACd,cAAc;GACd;GACA;GACA;GACD,CAAC;EAkCA;EACA;EACA,UAlCe,aACd,UAAoC;AACnC,YAAS;IACP,MAAM;IACN,UAAU;KACR,GAAG;KACH,IAAI,MAAM,MAAM,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,GAAG,GAAG;KAC5D;IACD;IACD,CAAC;KAEJ,CAAC,YAAY,CACd;EAuBC,aArBkB,aAAa,OAAgB;AAC/C,YAAS;IAAE,MAAM;IAAgB,SAAS;IAAI,CAAC;KAC9C,EAAE,CAAC;EAoBJ,eAlBoB,aAAa,OAAgB;AACjD,YAAS;IAAE,MAAM;IAAmB,SAAS;IAAI,CAAC;KACjD,EAAE,CAAC;EAiBJ,iBAfsB,aAAa,OAAgB;AACnD,YAAS;IAAE,MAAM;IAAqB,SAAS;IAAI,CAAC;KACnD,EAAE,CAAC;EAcJ,aAZkB,kBAAkB;AACpC,UAAO,MAAM;KACZ,CAAC,MAAM,OAAO,CAAC;EAWjB"}
|
|
1
|
+
{"version":3,"file":"usePanelGrid.mjs","names":["newPanelCoordinate: PanelCoordinate","RESIZE_CURSOR_MAP: Record<string, string>","e"],"sources":["../src/usePanelGrid.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useReducer, useRef } from \"react\";\nimport {\n applySnapAnimation,\n detectAnimatingPanels,\n gridPositionToPixels,\n gridToPixels,\n pixelsToGridPosition,\n pixelsToGridSize,\n rearrangePanels,\n} from \"./helpers\";\nimport { findNewPositionToAddPanel } from \"./helpers/rearrangement\";\nimport type { PanelCoordinate, PanelId, RearrangementFunction, ResizeHandlePosition } from \"./types\";\n\ninterface PanelGridOptions {\n panels: PanelCoordinate[];\n columnCount: number;\n baseSize: number;\n gap: number;\n resizeHandlePositions: ResizeHandlePosition[];\n rearrangement?: RearrangementFunction;\n}\n\nexport interface PanelGridState {\n panels: PanelCoordinate[];\n}\n\ninterface InternalPanelState {\n activePanelId: PanelId | null;\n draggableElements: Record<PanelId, HTMLElement | null>;\n animatingPanels: Set<PanelId>;\n}\n\nexport type PanelGridAction =\n | { type: \"UPDATE_PANELS\"; newPanels: PanelCoordinate[] }\n | { type: \"ADD_PANEL\"; newPanel: Partial<PanelCoordinate>; columnCount: number }\n | { type: \"REMOVE_PANEL\"; panelId: PanelId }\n | { type: \"LOCK_PANEL_SIZE\"; panelId: PanelId }\n | { type: \"UNLOCK_PANEL_SIZE\"; panelId: PanelId };\n\nexport function panelGridReducer(state: PanelGridState, action: PanelGridAction): PanelGridState {\n switch (action.type) {\n case \"UPDATE_PANELS\":\n return {\n ...state,\n panels: action.newPanels,\n };\n case \"ADD_PANEL\": {\n const { newPanel, columnCount } = action;\n const newPosition = findNewPositionToAddPanel(newPanel, state.panels, columnCount);\n const newPanelCoordinate: PanelCoordinate = {\n id: newPanel.id || Math.random().toString(36).substring(2, 15),\n x: newPosition.x,\n y: newPosition.y,\n w: newPanel.w || 1,\n h: newPanel.h || 1,\n };\n return {\n ...state,\n panels: [...state.panels, newPanelCoordinate],\n };\n }\n case \"REMOVE_PANEL\":\n return {\n ...state,\n panels: state.panels.filter((panel) => panel.id !== action.panelId),\n };\n case \"LOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: true } : panel)),\n };\n case \"UNLOCK_PANEL_SIZE\":\n return {\n ...state,\n panels: state.panels.map((panel) => (panel.id === action.panelId ? { ...panel, lockSize: false } : panel)),\n };\n default:\n return state;\n }\n}\n\nconst ANIMATION_DURATION = 300;\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nconst RESIZE_CURSOR_MAP: Record<string, string> = {\n nw: \"nwse-resize\",\n ne: \"nesw-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\",\n n: \"ns-resize\",\n s: \"ns-resize\",\n e: \"ew-resize\",\n w: \"ew-resize\",\n};\n\nexport function usePanelGrid({\n panels,\n columnCount,\n baseSize,\n gap,\n resizeHandlePositions: _resizeHandlePositions,\n rearrangement,\n}: PanelGridOptions) {\n const [state, dispatch] = useReducer(panelGridReducer, {\n panels,\n });\n const ghostPanelRef = useRef<HTMLDivElement | null>(null);\n const animationTimeoutsRef = useRef<Set<TimeoutId>>(new Set());\n\n const panelMap = useMemo(() => {\n const map = new Map<PanelId, PanelCoordinate>();\n state.panels.forEach((panel) => {\n map.set(panel.id, panel);\n });\n return map;\n }, [state.panels]);\n\n const internalState = useRef<InternalPanelState>({\n activePanelId: null,\n draggableElements: {},\n animatingPanels: new Set(),\n }).current;\n\n // Cleanup animation timeouts on unmount\n useEffect(() => {\n return () => {\n animationTimeoutsRef.current.forEach((timeoutId) => clearTimeout(timeoutId));\n animationTimeoutsRef.current.clear();\n };\n }, []);\n\n // Ghost panel helper functions\n // Direct DOM manipulation is intentionally used here for performance.\n // This avoids React re-renders during high-frequency mousemove events.\n const showGhostPanel = useCallback((left: number, top: number, width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"block\";\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n ghostPanelRef.current.style.outline = \"1px dashed rgba(0, 0, 0, 0.2)\";\n }, []);\n\n const updateGhostPanelPosition = useCallback((left: number, top: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.left = `${left}px`;\n ghostPanelRef.current.style.top = `${top}px`;\n }, []);\n\n const updateGhostPanelSize = useCallback((width: number, height: number) => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.width = `${width}px`;\n ghostPanelRef.current.style.height = `${height}px`;\n }, []);\n\n const hideGhostPanel = useCallback(() => {\n if (!ghostPanelRef.current) return;\n ghostPanelRef.current.style.display = \"none\";\n }, []);\n\n // Callback to update panels and trigger animations\n const updatePanelsWithAnimation = useCallback(\n (updatedPanel: PanelCoordinate, currentPanels: PanelCoordinate[]) => {\n // Use custom rearrangement function if provided, otherwise use default\n const rearrange = rearrangement || rearrangePanels;\n const nextPanels = rearrange(updatedPanel, currentPanels, columnCount);\n\n // Detect which panels have been rearranged\n internalState.animatingPanels = detectAnimatingPanels({\n oldPanels: currentPanels,\n newPanels: nextPanels,\n excludePanelId: updatedPanel.id,\n });\n\n dispatch({ type: \"UPDATE_PANELS\", newPanels: nextPanels });\n\n // Clear animating panels after animation completes\n const timeoutId = setTimeout(() => {\n internalState.animatingPanels.clear();\n animationTimeoutsRef.current.delete(timeoutId);\n }, ANIMATION_DURATION);\n animationTimeoutsRef.current.add(timeoutId);\n },\n [columnCount, internalState, rearrangement]\n );\n\n // Create drag handler for a specific panel\n const createDragHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLDivElement>) => {\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n let isDragging = true;\n const initialX = e.clientX;\n const initialY = e.clientY;\n const offsetX = draggingElement.offsetLeft;\n const offsetY = draggingElement.offsetTop;\n const originalTransition = draggingElement.style.transition;\n\n document.body.classList.add(\"panelgrid-dragging\");\n\n draggingElement.classList.add(\"panelgrid-panel--dragging\");\n draggingElement.style.transition = \"\";\n\n showGhostPanel(offsetX, offsetY, draggingElement.offsetWidth, draggingElement.offsetHeight);\n\n const mouseUpListenerCtrl = new AbortController();\n const mouseMoveListenerCtrl = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isDragging) return;\n if (!draggingElement) return;\n\n const currentX = e.clientX;\n const currentY = e.clientY;\n const deltaX = currentX - initialX;\n const deltaY = currentY - initialY;\n\n draggingElement.style.left = offsetX + deltaX + \"px\";\n draggingElement.style.top = offsetY + deltaY + \"px\";\n\n // Update ghost panel position to snap to grid\n const droppedLeft = offsetX + deltaX;\n const droppedTop = offsetY + deltaY;\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(nextLeft, nextTop);\n\n e.preventDefault(); // Prevent text selection during drag\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n isDragging = false;\n draggingElement.classList.remove(\"panelgrid-panel--dragging\");\n\n hideGhostPanel();\n\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const nextTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n // Apply snap-back animation\n applySnapAnimation({\n element: draggingElement,\n droppedLeft,\n droppedTop,\n nextLeft,\n nextTop,\n originalTransition,\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY }, state.panels);\n\n document.body.classList.remove(\"panelgrid-dragging\");\n internalState.activePanelId = null;\n\n mouseMoveListenerCtrl.abort();\n mouseUpListenerCtrl.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveListenerCtrl.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpListenerCtrl.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create resize handler for a specific panel\n const createResizeHandler = useCallback(\n (panel: PanelCoordinate) => (e: React.MouseEvent<HTMLSpanElement>) => {\n e.stopPropagation();\n let isResizing = true;\n internalState.activePanelId = panel.id;\n const draggingElement = internalState.draggableElements[panel.id];\n if (!draggingElement) return;\n\n const startX = e.clientX;\n const startY = e.clientY;\n const initialTop = draggingElement.offsetTop;\n const initialLeft = draggingElement.offsetLeft;\n const initialWidth = draggingElement.offsetWidth;\n const initialHeight = draggingElement.offsetHeight;\n const initialZIndex = draggingElement.style.zIndex;\n const initialCursor = draggingElement.style.cursor;\n\n const resizeHandle = e.currentTarget;\n const handlePosition = resizeHandle.dataset.pgResizeHandle;\n\n const northSideResizeEnabled = handlePosition?.includes(\"n\");\n const westSideResizeEnabled = handlePosition?.includes(\"w\");\n const isVerticalResizeOnly = handlePosition === \"n\" || handlePosition === \"s\";\n const isHorizontalResizeOnly = handlePosition === \"e\" || handlePosition === \"w\";\n\n document.body.classList.add(\"panelgrid-resizing\");\n\n draggingElement.style.cursor = RESIZE_CURSOR_MAP[handlePosition || \"se\"] || \"nwse-resize\";\n draggingElement.style.transition = \"\";\n\n showGhostPanel(draggingElement.offsetLeft, draggingElement.offsetTop, initialWidth, initialHeight);\n\n const mouseMoveController = new AbortController();\n const mouseUpController = new AbortController();\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n if (!draggingElement) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n // Calculate dimensions once, accounting for all resize directions\n const newWidth = westSideResizeEnabled\n ? Math.max(initialWidth - deltaX, 1)\n : isVerticalResizeOnly\n ? initialWidth\n : initialWidth + deltaX;\n\n const newHeight = northSideResizeEnabled\n ? Math.max(initialHeight - deltaY, 1)\n : isHorizontalResizeOnly\n ? initialHeight\n : initialHeight + deltaY;\n\n draggingElement.style.width = `${newWidth}px`;\n draggingElement.style.height = `${newHeight}px`;\n draggingElement.style.zIndex = \"calc(infinity)\";\n\n // Update position for north/west resizing\n if (northSideResizeEnabled) {\n draggingElement.style.top = `${initialTop + deltaY}px`;\n }\n if (westSideResizeEnabled) {\n draggingElement.style.left = `${initialLeft + deltaX}px`;\n }\n\n // Calculate current position (needed for grid calculations)\n const currentLeft = westSideResizeEnabled ? initialLeft + deltaX : initialLeft;\n const currentTop = northSideResizeEnabled ? initialTop + deltaY : initialTop;\n\n // Update ghost panel - calculate grid position BEFORE grid size\n const nextGridX = pixelsToGridPosition(currentLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(currentTop, baseSize, gap);\n const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(newHeight, baseSize, gap, columnCount, nextGridY);\n\n const snappedWidth = gridToPixels(nextGridW, baseSize, gap);\n const snappedHeight = gridToPixels(nextGridH, baseSize, gap);\n const snappedLeft = gridPositionToPixels(nextGridX, baseSize, gap);\n const snappedTop = gridPositionToPixels(nextGridY, baseSize, gap);\n\n updateGhostPanelPosition(snappedLeft, snappedTop);\n updateGhostPanelSize(snappedWidth, snappedHeight);\n };\n\n const onMouseUp = () => {\n if (!draggingElement) return;\n\n hideGhostPanel();\n\n const rect = draggingElement.getBoundingClientRect();\n const droppedLeft = parseFloat(draggingElement.style.left) || 0;\n const droppedTop = parseFloat(draggingElement.style.top) || 0;\n\n const nextGridX = pixelsToGridPosition(droppedLeft, baseSize, gap, columnCount, panel.w);\n const nextGridY = pixelsToGridPosition(droppedTop, baseSize, gap);\n\n const nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, nextGridX);\n const nextGridH = pixelsToGridSize(rect.height, baseSize, gap, columnCount, nextGridY);\n\n const left = gridPositionToPixels(nextGridX, baseSize, gap);\n const top = gridPositionToPixels(nextGridY, baseSize, gap);\n const width = gridToPixels(nextGridW, baseSize, gap);\n const height = gridToPixels(nextGridH, baseSize, gap);\n\n draggingElement.style.width = `${rect.width}px`;\n draggingElement.style.height = `${rect.height}px`;\n draggingElement.style.cursor = initialCursor;\n draggingElement.style.transition = \"\";\n\n window.requestAnimationFrame(() => {\n draggingElement.style.top = `${top}px`;\n draggingElement.style.left = `${left}px`;\n draggingElement.style.width = `${width}px`;\n draggingElement.style.height = `${height}px`;\n draggingElement.style.zIndex = initialZIndex;\n draggingElement.style.transition =\n \"width 0.1s ease-out, height 0.1s ease-out, top 0.1s ease-out, left 0.1s ease-out\";\n });\n\n updatePanelsWithAnimation({ ...panel, x: nextGridX, y: nextGridY, w: nextGridW, h: nextGridH }, state.panels);\n\n isResizing = false;\n internalState.activePanelId = null;\n document.body.classList.remove(\"panelgrid-resizing\");\n\n mouseMoveController.abort();\n mouseUpController.abort();\n };\n\n document.addEventListener(\"mousemove\", onMouseMove, {\n signal: mouseMoveController.signal,\n });\n document.addEventListener(\"mouseup\", onMouseUp, {\n signal: mouseUpController.signal,\n });\n },\n [\n baseSize,\n gap,\n internalState,\n state.panels,\n updatePanelsWithAnimation,\n showGhostPanel,\n updateGhostPanelPosition,\n updateGhostPanelSize,\n hideGhostPanel,\n columnCount,\n ]\n );\n\n // Create ref callback for panel elements\n const createRefCallback = useCallback(\n (panelId: PanelId) => (element: HTMLElement | null) => {\n if (!element) return;\n if (!internalState.draggableElements[panelId]) {\n internalState.draggableElements[panelId] = element;\n }\n },\n [internalState]\n );\n\n // Memoize panel props to avoid recreating on every render\n const panelsWithProps = useMemo(() => {\n return state.panels.map((panel) => {\n const isAnimating = internalState.animatingPanels.has(panel.id);\n const isActive = internalState.activePanelId === panel.id;\n\n return {\n panelProps: {\n key: panel.id,\n lockSize: panel.lockSize,\n positionData: {\n x: panel.x,\n y: panel.y,\n w: panel.w,\n h: panel.h,\n },\n style: {\n top: gridPositionToPixels(panel.y, baseSize, gap),\n left: gridPositionToPixels(panel.x, baseSize, gap),\n width: gridToPixels(panel.w, baseSize, gap),\n height: gridToPixels(panel.h, baseSize, gap),\n transition:\n isAnimating && !isActive\n ? \"top 0.3s ease-out, left 0.3s ease-out, width 0.3s ease-out, height 0.3s ease-out\"\n : undefined,\n },\n ref: createRefCallback(panel.id),\n onMouseDown: createDragHandler(panel),\n },\n resizeHandleProps: panel.lockSize\n ? undefined\n : {\n onMouseDown: createResizeHandler(panel),\n },\n };\n });\n }, [\n state.panels,\n baseSize,\n gap,\n internalState.animatingPanels,\n internalState.activePanelId,\n createRefCallback,\n createDragHandler,\n createResizeHandler,\n ]);\n\n const addPanel = useCallback(\n (panel: Partial<PanelCoordinate>) => {\n dispatch({\n type: \"ADD_PANEL\",\n newPanel: {\n ...panel,\n id: panel.id || Math.random().toString(36).substring(2, 15),\n },\n columnCount,\n });\n },\n [columnCount]\n );\n\n const removePanel = useCallback((id: PanelId) => {\n dispatch({ type: \"REMOVE_PANEL\", panelId: id });\n }, []);\n\n const lockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"LOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const unlockPanelSize = useCallback((id: PanelId) => {\n dispatch({ type: \"UNLOCK_PANEL_SIZE\", panelId: id });\n }, []);\n\n const exportState = useCallback(() => {\n return state.panels;\n }, [state.panels]);\n\n return {\n panels: panelsWithProps,\n panelMap,\n ghostPanelRef,\n addPanel,\n removePanel,\n lockPanelSize,\n unlockPanelSize,\n exportState,\n };\n}\n"],"mappings":"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"}
|