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.
@@ -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
- function PanelGridProvider({ panels: initialPanels, columnCount, gap, children, rearrangement }) {
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 value={{ panels, panelMap, columnCount, gap, baseSize, ghostPanelRef }}>\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":";;;;;AAwBA,MAAM,iDAAwE,OAAU;AACxF,MAAM,oDAAmF,OAAU;AAcnG,SAAgB,kBAAkB,EAChC,QAAQ,eACR,aACA,KACA,UACA,iBACyB;CACzB,MAAM,CAAC,UAAU,mCAAuC,KAAK;CAE7D,MAAM,EAAE,QAAQ,UAAU,UAAU,aAAa,eAAe,iBAAiB,aAAa,kBAC5FA,kCAAa;EACX,QAAQ;EACR;EACA,UAAU,YAAY;EACtB;EACA;EACD,CAAC;AAEJ,QACE,2CAAC,sBAAsB;EAAS,OAAO;GAAE;GAAQ;GAAU;GAAa;GAAK;GAAU;GAAe;YACpG,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
+ {"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
- function PanelGridProvider({ panels: initialPanels, columnCount, gap, children, rearrangement }) {
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 value={{ panels, panelMap, columnCount, gap, baseSize, ghostPanelRef }}>\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":";;;;;AAwBA,MAAM,wBAAwB,cAAgD,OAAU;AACxF,MAAM,2BAA2B,cAAwD,OAAU;AAcnG,SAAgB,kBAAkB,EAChC,QAAQ,eACR,aACA,KACA,UACA,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;EACD,CAAC;AAEJ,QACE,oBAAC,sBAAsB;EAAS,OAAO;GAAE;GAAQ;GAAU;GAAa;GAAK;GAAU;GAAe;YACpG,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"}
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 }), resizeHandleProps && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
65
- className: "panelgrid-resize-handle",
66
- ...resizeHandleProps
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 {resizeHandleProps && <span className=\"panelgrid-resize-handle\" {...resizeHandleProps}></span>}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;AASA,SAAgB,kBAAkB,EAAE,cAAc,gBAAwC;CACxF,MAAM,EAAE,QAAQ,aAAa,KAAK,UAAU,kBAAkBA,6CAAmB;CACjF,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,qBAAqB,2CAAC;MAAK,WAAU;MAA0B,GAAI;OAA0B;OAZzF,IAaD;KAER;;GACE"}
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 }), resizeHandleProps && /* @__PURE__ */ jsx("span", {
65
- className: "panelgrid-resize-handle",
66
- ...resizeHandleProps
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 {resizeHandleProps && <span className=\"panelgrid-resize-handle\" {...resizeHandleProps}></span>}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;;AASA,SAAgB,kBAAkB,EAAE,cAAc,gBAAwC;CACxF,MAAM,EAAE,QAAQ,aAAa,KAAK,UAAU,kBAAkB,mBAAmB;CACjF,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,qBAAqB,oBAAC;MAAK,WAAU;MAA0B,GAAI;OAA0B;OAZzF,IAaD;KAER;;GACE"}
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-color: rgba(0, 0, 0, 0.12);
59
- clip-path: polygon(0 100%, 100% 100%, 100% 0);
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
@@ -48,7 +48,17 @@ function panelGridReducer(state, action) {
48
48
  }
49
49
  }
50
50
  const ANIMATION_DURATION = 300;
51
- function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
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
- draggingElement.style.width = `${initialWidth + deltaX}px`;
213
- draggingElement.style.height = `${initialHeight + deltaY}px`;
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
- const newWidth = initialWidth + deltaX;
216
- const newHeight = initialHeight + deltaY;
217
- const nextGridW = require_gridCalculations.pixelsToGridSize(newWidth, baseSize, gap, columnCount, panel.x);
218
- const nextGridH = require_gridCalculations.pixelsToGridSize(newHeight, baseSize, gap);
219
- updateGhostPanelSize(require_gridCalculations.gridToPixels(nextGridW, baseSize, gap), require_gridCalculations.gridToPixels(nextGridH, baseSize, gap));
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 nextGridW = require_gridCalculations.pixelsToGridSize(rect.width, baseSize, gap, columnCount, panel.x);
226
- const nextGridH = require_gridCalculations.pixelsToGridSize(rect.height, baseSize, gap);
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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"}
@@ -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: {
@@ -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: {
@@ -48,7 +48,17 @@ function panelGridReducer(state, action) {
48
48
  }
49
49
  }
50
50
  const ANIMATION_DURATION = 300;
51
- function usePanelGrid({ panels, columnCount, baseSize, gap, rearrangement }) {
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
- draggingElement.style.width = `${initialWidth + deltaX}px`;
213
- draggingElement.style.height = `${initialHeight + deltaY}px`;
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
- const newWidth = initialWidth + deltaX;
216
- const newHeight = initialHeight + deltaY;
217
- const nextGridW = pixelsToGridSize(newWidth, baseSize, gap, columnCount, panel.x);
218
- const nextGridH = pixelsToGridSize(newHeight, baseSize, gap);
219
- updateGhostPanelSize(gridToPixels(nextGridW, baseSize, gap), gridToPixels(nextGridH, baseSize, gap));
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 nextGridW = pixelsToGridSize(rect.width, baseSize, gap, columnCount, panel.x);
226
- const nextGridH = pixelsToGridSize(rect.height, baseSize, gap);
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":"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"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panelgrid",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A flexible and performant React grid layout library with drag-and-drop and resize capabilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",