grid-cell-selection 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # Grid Cell Selection
2
+
3
+ A React hook for grid cell selection. A simple headless hook that.
4
+
5
+ # Made by [Atticus](https://atticusthomson.com)
6
+
7
+ ![Grid Cell Selection](https://github.com/attithom/grid-cell-selection/blob/main/example.gif)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install grid-cell-selection
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```tsx
18
+ import React from "react";
19
+ import { useGridCellSelection } from "grid-cell-selection";
20
+
21
+ function App() {
22
+ const columns = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"];
23
+ const rows = 20;
24
+
25
+ const { isCellSelected, handleMouseDown, handleMouseEnter, handleMouseUp } = useGridCellSelection("multiple");
26
+
27
+ return (
28
+ <>
29
+ <table onMouseUp={handleMouseUp}>
30
+ <tbody>
31
+ {Array.from({ length: rows }, (_, row) => (
32
+ <tr key={row}>
33
+ {columns.map((column, col) => (
34
+ <td
35
+ onMouseEnter={(event) => handleMouseEnter({ row, col }, event)}
36
+ onMouseDown={(event) => handleMouseDown({ row, col }, event)}
37
+ className={`${isCellSelected({ row, col }) ? "selected" : ""}`}
38
+ >
39
+ {column}
40
+ {row}
41
+ </td>
42
+ ))}
43
+ </tr>
44
+ ))}
45
+ </tbody>
46
+ </table>
47
+ </>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## Cell Selection Behavior
53
+
54
+ ### Single Cell Selection
55
+
56
+ - Click on a cell to select it.
57
+ - Clicking on a different cell deselects any previously selected cells.
58
+
59
+ ### Range Selection
60
+
61
+ - Click and drag to select a range of cells
62
+
63
+ ### Multiple Range Selection
64
+
65
+ - Hold Ctrl (or Cmd on Mac) or Shift to select multiple ranges
package/dist/App.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare function App(): React.JSX.Element;
3
+ export default App;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ declare function App(): React.JSX.Element;
3
+ export default App;
@@ -0,0 +1 @@
1
+ import "./index.css";
File without changes
@@ -0,0 +1,9 @@
1
+ /// <reference types="react" />
2
+ import { CellIdentifier } from "../types";
3
+ export declare const useGridCellSelection: () => {
4
+ selectedCells: Set<string>;
5
+ isCellSelected: (cell: CellIdentifier) => boolean;
6
+ handleMouseDown: (cell: CellIdentifier, event: import("react").MouseEvent<Element, MouseEvent>) => void;
7
+ handleMouseEnter: (cell: CellIdentifier, event: import("react").MouseEvent<Element, MouseEvent>) => void;
8
+ handleMouseUp: () => void;
9
+ };
@@ -0,0 +1,6 @@
1
+ import { CellIdentifier } from "../types";
2
+ export declare const useMouseDragSelection: (toggleCellSelection: (cell: CellIdentifier, ctrlKey: boolean, shiftKey: boolean, newSelection: boolean) => void) => {
3
+ handleMouseDown: (cell: CellIdentifier, event: React.MouseEvent) => void;
4
+ handleMouseEnter: (cell: CellIdentifier, event: React.MouseEvent) => void;
5
+ handleMouseUp: () => void;
6
+ };
@@ -0,0 +1,3 @@
1
+ export { useGridCellSelection } from "./hooks/useCellSelection";
2
+ export { useMouseDragSelection } from "./hooks/useMouseDragSelection";
3
+ export * from "./types";
@@ -0,0 +1,119 @@
1
+ import { useState } from 'react';
2
+
3
+ /******************************************************************************
4
+ Copyright (c) Microsoft Corporation.
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any
7
+ purpose with or without fee is hereby granted.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
16
+ ***************************************************************************** */
17
+
18
+ var __assign = function() {
19
+ __assign = Object.assign || function __assign(t) {
20
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
21
+ s = arguments[i];
22
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
23
+ }
24
+ return t;
25
+ };
26
+ return __assign.apply(this, arguments);
27
+ };
28
+
29
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
30
+ var e = new Error(message);
31
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
32
+ };
33
+
34
+ var useMouseDragSelection = function (toggleCellSelection) {
35
+ var _a = useState(false), isDragging = _a[0], setIsDragging = _a[1];
36
+ var handleMouseDown = function (cell, event) {
37
+ setIsDragging(true);
38
+ toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, true);
39
+ };
40
+ var handleMouseEnter = function (cell, event) {
41
+ if (isDragging) {
42
+ toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, false);
43
+ }
44
+ };
45
+ var handleMouseUp = function () {
46
+ setIsDragging(false);
47
+ };
48
+ return {
49
+ handleMouseDown: handleMouseDown,
50
+ handleMouseEnter: handleMouseEnter,
51
+ handleMouseUp: handleMouseUp,
52
+ };
53
+ };
54
+
55
+ var getCellKey = function (cell) {
56
+ return "".concat(cell.row, "-").concat(cell.col);
57
+ };
58
+ var useGridCellSelection = function () {
59
+ var _a = useState({
60
+ selectedCells: new Set(),
61
+ }), selectionState = _a[0], setSelectionState = _a[1];
62
+ var toggleCellSelection = function (cell, ctrlKey, shiftKey, newSelection) {
63
+ if (ctrlKey === void 0) { ctrlKey = false; }
64
+ if (shiftKey === void 0) { shiftKey = false; }
65
+ if (newSelection === void 0) { newSelection = false; }
66
+ setSelectionState(function (prevState) {
67
+ var selectedCells = new Set(prevState.selectedCells);
68
+ var cellKey = getCellKey(cell);
69
+ if (prevState.startCell && !newSelection) {
70
+ handleRangeSelection(selectedCells, prevState.startCell, cell);
71
+ }
72
+ else if ((shiftKey || ctrlKey) && newSelection) {
73
+ handleSingleOrMultipleSelection(selectedCells, cellKey);
74
+ }
75
+ else {
76
+ selectedCells.clear();
77
+ selectedCells.add(cellKey);
78
+ }
79
+ return __assign(__assign({}, prevState), { selectedCells: selectedCells, startCell: newSelection ? cell : prevState.startCell });
80
+ });
81
+ };
82
+ var isCellSelected = function (cell) {
83
+ return selectionState.selectedCells.has(getCellKey(cell));
84
+ };
85
+ var _b = useMouseDragSelection(toggleCellSelection), handleMouseDown = _b.handleMouseDown, handleMouseEnter = _b.handleMouseEnter, handleMouseUp = _b.handleMouseUp;
86
+ return {
87
+ selectedCells: selectionState.selectedCells,
88
+ isCellSelected: isCellSelected,
89
+ handleMouseDown: handleMouseDown,
90
+ handleMouseEnter: handleMouseEnter,
91
+ handleMouseUp: handleMouseUp,
92
+ };
93
+ };
94
+ // New helper functions
95
+ function handleRangeSelection(selectedCells, startCell, endCell) {
96
+ var action = selectedCells.has(getCellKey(startCell)) ? "add" : "delete";
97
+ var _a = [startCell.row, startCell.col], startRow = _a[0], startCol = _a[1];
98
+ var _b = [endCell.row, endCell.col], endRow = _b[0], endCol = _b[1];
99
+ var minRow = Math.min(startRow, endRow);
100
+ var maxRow = Math.max(startRow, endRow);
101
+ var minCol = Math.min(startCol, endCol);
102
+ var maxCol = Math.max(startCol, endCol);
103
+ for (var row = minRow; row <= maxRow; row++) {
104
+ for (var col = minCol; col <= maxCol; col++) {
105
+ selectedCells[action](getCellKey({ row: row, col: col }));
106
+ }
107
+ }
108
+ }
109
+ function handleSingleOrMultipleSelection(selectedCells, cellKey) {
110
+ if (selectedCells.has(cellKey)) {
111
+ selectedCells.delete(cellKey);
112
+ }
113
+ else {
114
+ selectedCells.add(cellKey);
115
+ }
116
+ }
117
+
118
+ export { useGridCellSelection, useMouseDragSelection };
119
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/hooks/useMouseDragSelection.ts","../src/hooks/useCellSelection.ts"],"sourcesContent":["import { useState } from \"react\";\nimport { CellIdentifier } from \"../types\";\n\nexport const useMouseDragSelection = (\n toggleCellSelection: (cell: CellIdentifier, ctrlKey: boolean, shiftKey: boolean, newSelection: boolean) => void\n) => {\n const [isDragging, setIsDragging] = useState(false);\n\n const handleMouseDown = (cell: CellIdentifier, event: React.MouseEvent) => {\n setIsDragging(true);\n toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, true);\n };\n\n const handleMouseEnter = (cell: CellIdentifier, event: React.MouseEvent) => {\n if (isDragging) {\n toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, false);\n }\n };\n\n const handleMouseUp = () => {\n setIsDragging(false);\n };\n\n return {\n handleMouseDown,\n handleMouseEnter,\n handleMouseUp,\n };\n};\n","import { useState } from \"react\";\nimport { CellIdentifier, SelectionState } from \"../types\";\nimport { useMouseDragSelection } from \"./useMouseDragSelection\";\n\nconst getCellKey = (cell: Omit<CellIdentifier, \"id\">): string => {\n return `${cell.row}-${cell.col}`;\n};\n\nexport const useGridCellSelection = () => {\n const [selectionState, setSelectionState] = useState<SelectionState>({\n selectedCells: new Set(),\n });\n\n const toggleCellSelection = (cell: CellIdentifier, ctrlKey = false, shiftKey = false, newSelection = false) => {\n setSelectionState((prevState) => {\n const selectedCells = new Set(prevState.selectedCells);\n const cellKey = getCellKey(cell);\n\n if (prevState.startCell && !newSelection) {\n handleRangeSelection(selectedCells, prevState.startCell, cell);\n } else if ((shiftKey || ctrlKey) && newSelection) {\n handleSingleOrMultipleSelection(selectedCells, cellKey);\n } else {\n selectedCells.clear();\n selectedCells.add(cellKey);\n }\n\n return {\n ...prevState,\n selectedCells,\n startCell: newSelection ? cell : prevState.startCell,\n };\n });\n };\n\n const isCellSelected = (cell: CellIdentifier) => {\n return selectionState.selectedCells.has(getCellKey(cell));\n };\n\n const { handleMouseDown, handleMouseEnter, handleMouseUp } = useMouseDragSelection(toggleCellSelection);\n\n return {\n selectedCells: selectionState.selectedCells,\n isCellSelected,\n handleMouseDown,\n handleMouseEnter,\n handleMouseUp,\n };\n};\n\n// New helper functions\nfunction handleRangeSelection(selectedCells: Set<string>, startCell: CellIdentifier, endCell: CellIdentifier) {\n const action = selectedCells.has(getCellKey(startCell)) ? \"add\" : \"delete\";\n const [startRow, startCol] = [startCell.row, startCell.col];\n const [endRow, endCol] = [endCell.row, endCell.col];\n\n const minRow = Math.min(startRow, endRow);\n const maxRow = Math.max(startRow, endRow);\n const minCol = Math.min(startCol, endCol);\n const maxCol = Math.max(startCol, endCol);\n\n for (let row = minRow; row <= maxRow; row++) {\n for (let col = minCol; col <= maxCol; col++) {\n selectedCells[action](getCellKey({ row, col }));\n }\n }\n}\n\nfunction handleSingleOrMultipleSelection(selectedCells: Set<string>, cellKey: string) {\n if (selectedCells.has(cellKey)) {\n selectedCells.delete(cellKey);\n } else {\n selectedCells.add(cellKey);\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var react = require('react');
6
+
7
+ /******************************************************************************
8
+ Copyright (c) Microsoft Corporation.
9
+
10
+ Permission to use, copy, modify, and/or distribute this software for any
11
+ purpose with or without fee is hereby granted.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
+ PERFORMANCE OF THIS SOFTWARE.
20
+ ***************************************************************************** */
21
+
22
+ var __assign = function() {
23
+ __assign = Object.assign || function __assign(t) {
24
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
25
+ s = arguments[i];
26
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
27
+ }
28
+ return t;
29
+ };
30
+ return __assign.apply(this, arguments);
31
+ };
32
+
33
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
34
+ var e = new Error(message);
35
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
36
+ };
37
+
38
+ var useMouseDragSelection = function (toggleCellSelection) {
39
+ var _a = react.useState(false), isDragging = _a[0], setIsDragging = _a[1];
40
+ var handleMouseDown = function (cell, event) {
41
+ setIsDragging(true);
42
+ toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, true);
43
+ };
44
+ var handleMouseEnter = function (cell, event) {
45
+ if (isDragging) {
46
+ toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, false);
47
+ }
48
+ };
49
+ var handleMouseUp = function () {
50
+ setIsDragging(false);
51
+ };
52
+ return {
53
+ handleMouseDown: handleMouseDown,
54
+ handleMouseEnter: handleMouseEnter,
55
+ handleMouseUp: handleMouseUp,
56
+ };
57
+ };
58
+
59
+ var getCellKey = function (cell) {
60
+ return "".concat(cell.row, "-").concat(cell.col);
61
+ };
62
+ var useGridCellSelection = function () {
63
+ var _a = react.useState({
64
+ selectedCells: new Set(),
65
+ }), selectionState = _a[0], setSelectionState = _a[1];
66
+ var toggleCellSelection = function (cell, ctrlKey, shiftKey, newSelection) {
67
+ if (ctrlKey === void 0) { ctrlKey = false; }
68
+ if (shiftKey === void 0) { shiftKey = false; }
69
+ if (newSelection === void 0) { newSelection = false; }
70
+ setSelectionState(function (prevState) {
71
+ var selectedCells = new Set(prevState.selectedCells);
72
+ var cellKey = getCellKey(cell);
73
+ if (prevState.startCell && !newSelection) {
74
+ handleRangeSelection(selectedCells, prevState.startCell, cell);
75
+ }
76
+ else if ((shiftKey || ctrlKey) && newSelection) {
77
+ handleSingleOrMultipleSelection(selectedCells, cellKey);
78
+ }
79
+ else {
80
+ selectedCells.clear();
81
+ selectedCells.add(cellKey);
82
+ }
83
+ return __assign(__assign({}, prevState), { selectedCells: selectedCells, startCell: newSelection ? cell : prevState.startCell });
84
+ });
85
+ };
86
+ var isCellSelected = function (cell) {
87
+ return selectionState.selectedCells.has(getCellKey(cell));
88
+ };
89
+ var _b = useMouseDragSelection(toggleCellSelection), handleMouseDown = _b.handleMouseDown, handleMouseEnter = _b.handleMouseEnter, handleMouseUp = _b.handleMouseUp;
90
+ return {
91
+ selectedCells: selectionState.selectedCells,
92
+ isCellSelected: isCellSelected,
93
+ handleMouseDown: handleMouseDown,
94
+ handleMouseEnter: handleMouseEnter,
95
+ handleMouseUp: handleMouseUp,
96
+ };
97
+ };
98
+ // New helper functions
99
+ function handleRangeSelection(selectedCells, startCell, endCell) {
100
+ var action = selectedCells.has(getCellKey(startCell)) ? "add" : "delete";
101
+ var _a = [startCell.row, startCell.col], startRow = _a[0], startCol = _a[1];
102
+ var _b = [endCell.row, endCell.col], endRow = _b[0], endCol = _b[1];
103
+ var minRow = Math.min(startRow, endRow);
104
+ var maxRow = Math.max(startRow, endRow);
105
+ var minCol = Math.min(startCol, endCol);
106
+ var maxCol = Math.max(startCol, endCol);
107
+ for (var row = minRow; row <= maxRow; row++) {
108
+ for (var col = minCol; col <= maxCol; col++) {
109
+ selectedCells[action](getCellKey({ row: row, col: col }));
110
+ }
111
+ }
112
+ }
113
+ function handleSingleOrMultipleSelection(selectedCells, cellKey) {
114
+ if (selectedCells.has(cellKey)) {
115
+ selectedCells.delete(cellKey);
116
+ }
117
+ else {
118
+ selectedCells.add(cellKey);
119
+ }
120
+ }
121
+
122
+ exports.useGridCellSelection = useGridCellSelection;
123
+ exports.useMouseDragSelection = useMouseDragSelection;
124
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/hooks/useMouseDragSelection.ts","../src/hooks/useCellSelection.ts"],"sourcesContent":["import { useState } from \"react\";\nimport { CellIdentifier } from \"../types\";\n\nexport const useMouseDragSelection = (\n toggleCellSelection: (cell: CellIdentifier, ctrlKey: boolean, shiftKey: boolean, newSelection: boolean) => void\n) => {\n const [isDragging, setIsDragging] = useState(false);\n\n const handleMouseDown = (cell: CellIdentifier, event: React.MouseEvent) => {\n setIsDragging(true);\n toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, true);\n };\n\n const handleMouseEnter = (cell: CellIdentifier, event: React.MouseEvent) => {\n if (isDragging) {\n toggleCellSelection(cell, event.ctrlKey || event.metaKey, event.shiftKey, false);\n }\n };\n\n const handleMouseUp = () => {\n setIsDragging(false);\n };\n\n return {\n handleMouseDown,\n handleMouseEnter,\n handleMouseUp,\n };\n};\n","import { useState } from \"react\";\nimport { CellIdentifier, SelectionState } from \"../types\";\nimport { useMouseDragSelection } from \"./useMouseDragSelection\";\n\nconst getCellKey = (cell: Omit<CellIdentifier, \"id\">): string => {\n return `${cell.row}-${cell.col}`;\n};\n\nexport const useGridCellSelection = () => {\n const [selectionState, setSelectionState] = useState<SelectionState>({\n selectedCells: new Set(),\n });\n\n const toggleCellSelection = (cell: CellIdentifier, ctrlKey = false, shiftKey = false, newSelection = false) => {\n setSelectionState((prevState) => {\n const selectedCells = new Set(prevState.selectedCells);\n const cellKey = getCellKey(cell);\n\n if (prevState.startCell && !newSelection) {\n handleRangeSelection(selectedCells, prevState.startCell, cell);\n } else if ((shiftKey || ctrlKey) && newSelection) {\n handleSingleOrMultipleSelection(selectedCells, cellKey);\n } else {\n selectedCells.clear();\n selectedCells.add(cellKey);\n }\n\n return {\n ...prevState,\n selectedCells,\n startCell: newSelection ? cell : prevState.startCell,\n };\n });\n };\n\n const isCellSelected = (cell: CellIdentifier) => {\n return selectionState.selectedCells.has(getCellKey(cell));\n };\n\n const { handleMouseDown, handleMouseEnter, handleMouseUp } = useMouseDragSelection(toggleCellSelection);\n\n return {\n selectedCells: selectionState.selectedCells,\n isCellSelected,\n handleMouseDown,\n handleMouseEnter,\n handleMouseUp,\n };\n};\n\n// New helper functions\nfunction handleRangeSelection(selectedCells: Set<string>, startCell: CellIdentifier, endCell: CellIdentifier) {\n const action = selectedCells.has(getCellKey(startCell)) ? \"add\" : \"delete\";\n const [startRow, startCol] = [startCell.row, startCell.col];\n const [endRow, endCol] = [endCell.row, endCell.col];\n\n const minRow = Math.min(startRow, endRow);\n const maxRow = Math.max(startRow, endRow);\n const minCol = Math.min(startCol, endCol);\n const maxCol = Math.max(startCol, endCol);\n\n for (let row = minRow; row <= maxRow; row++) {\n for (let col = minCol; col <= maxCol; col++) {\n selectedCells[action](getCellKey({ row, col }));\n }\n }\n}\n\nfunction handleSingleOrMultipleSelection(selectedCells: Set<string>, cellKey: string) {\n if (selectedCells.has(cellKey)) {\n selectedCells.delete(cellKey);\n } else {\n selectedCells.add(cellKey);\n }\n}\n"],"names":["useState"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,qBAAqB,GAAG,UACnC,mBAA+G,EAAA;IAEzG,IAAA,EAAA,GAA8BA,cAAQ,CAAC,KAAK,CAAC,EAA5C,UAAU,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,aAAa,GAAA,EAAA,CAAA,CAAA,CAAmB,CAAC;AAEpD,IAAA,IAAM,eAAe,GAAG,UAAC,IAAoB,EAAE,KAAuB,EAAA;QACpE,aAAa,CAAC,IAAI,CAAC,CAAC;AACpB,QAAA,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AAClF,KAAC,CAAC;AAEF,IAAA,IAAM,gBAAgB,GAAG,UAAC,IAAoB,EAAE,KAAuB,EAAA;AACrE,QAAA,IAAI,UAAU,EAAE;AACd,YAAA,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAClF,SAAA;AACH,KAAC,CAAC;AAEF,IAAA,IAAM,aAAa,GAAG,YAAA;QACpB,aAAa,CAAC,KAAK,CAAC,CAAC;AACvB,KAAC,CAAC;IAEF,OAAO;AACL,QAAA,eAAe,EAAA,eAAA;AACf,QAAA,gBAAgB,EAAA,gBAAA;AAChB,QAAA,aAAa,EAAA,aAAA;KACd,CAAC;AACJ;;ACxBA,IAAM,UAAU,GAAG,UAAC,IAAgC,EAAA;IAClD,OAAO,EAAA,CAAA,MAAA,CAAG,IAAI,CAAC,GAAG,cAAI,IAAI,CAAC,GAAG,CAAE,CAAC;AACnC,CAAC,CAAC;AAEW,IAAA,oBAAoB,GAAG,YAAA;IAC5B,IAAA,EAAA,GAAsCA,cAAQ,CAAiB;QACnE,aAAa,EAAE,IAAI,GAAG,EAAE;AACzB,KAAA,CAAC,EAFK,cAAc,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,iBAAiB,QAEtC,CAAC;IAEH,IAAM,mBAAmB,GAAG,UAAC,IAAoB,EAAE,OAAe,EAAE,QAAgB,EAAE,YAAoB,EAAA;AAAvD,QAAA,IAAA,OAAA,KAAA,KAAA,CAAA,EAAA,EAAA,OAAe,GAAA,KAAA,CAAA,EAAA;AAAE,QAAA,IAAA,QAAA,KAAA,KAAA,CAAA,EAAA,EAAA,QAAgB,GAAA,KAAA,CAAA,EAAA;AAAE,QAAA,IAAA,YAAA,KAAA,KAAA,CAAA,EAAA,EAAA,YAAoB,GAAA,KAAA,CAAA,EAAA;QACxG,iBAAiB,CAAC,UAAC,SAAS,EAAA;YAC1B,IAAM,aAAa,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;AACvD,YAAA,IAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;AAEjC,YAAA,IAAI,SAAS,CAAC,SAAS,IAAI,CAAC,YAAY,EAAE;gBACxC,oBAAoB,CAAC,aAAa,EAAE,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAChE,aAAA;AAAM,iBAAA,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,YAAY,EAAE;AAChD,gBAAA,+BAA+B,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AACzD,aAAA;AAAM,iBAAA;gBACL,aAAa,CAAC,KAAK,EAAE,CAAC;AACtB,gBAAA,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC5B,aAAA;AAED,YAAA,OAAA,QAAA,CAAA,QAAA,CAAA,EAAA,EACK,SAAS,CACZ,EAAA,EAAA,aAAa,eAAA,EACb,SAAS,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,CAAC,SAAS,EACpD,CAAA,CAAA;AACJ,SAAC,CAAC,CAAC;AACL,KAAC,CAAC;IAEF,IAAM,cAAc,GAAG,UAAC,IAAoB,EAAA;QAC1C,OAAO,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,KAAC,CAAC;AAEI,IAAA,IAAA,EAAuD,GAAA,qBAAqB,CAAC,mBAAmB,CAAC,EAA/F,eAAe,GAAA,EAAA,CAAA,eAAA,EAAE,gBAAgB,GAAA,EAAA,CAAA,gBAAA,EAAE,aAAa,mBAA+C,CAAC;IAExG,OAAO;QACL,aAAa,EAAE,cAAc,CAAC,aAAa;AAC3C,QAAA,cAAc,EAAA,cAAA;AACd,QAAA,eAAe,EAAA,eAAA;AACf,QAAA,gBAAgB,EAAA,gBAAA;AAChB,QAAA,aAAa,EAAA,aAAA;KACd,CAAC;AACJ,EAAE;AAEF;AACA,SAAS,oBAAoB,CAAC,aAA0B,EAAE,SAAyB,EAAE,OAAuB,EAAA;AAC1G,IAAA,IAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,GAAG,QAAQ,CAAC;AACrE,IAAA,IAAA,KAAuB,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,EAApD,QAAQ,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,QAAQ,QAAkC,CAAC;AACtD,IAAA,IAAA,KAAmB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAA5C,MAAM,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,MAAM,QAA8B,CAAC;IAEpD,IAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE1C,KAAK,IAAI,GAAG,GAAG,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,EAAE,EAAE;QAC3C,KAAK,IAAI,GAAG,GAAG,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,EAAE,EAAE;AAC3C,YAAA,aAAa,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,EAAE,GAAG,EAAA,GAAA,EAAE,GAAG,EAAA,GAAA,EAAE,CAAC,CAAC,CAAC;AACjD,SAAA;AACF,KAAA;AACH,CAAC;AAED,SAAS,+BAA+B,CAAC,aAA0B,EAAE,OAAe,EAAA;AAClF,IAAA,IAAI,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;AAC9B,QAAA,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC/B,KAAA;AAAM,SAAA;AACL,QAAA,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC5B,KAAA;AACH;;;;;"}
package/dist/main.d.ts ADDED
@@ -0,0 +1 @@
1
+ import "./index.css";
@@ -0,0 +1,8 @@
1
+ export type CellIdentifier = {
2
+ row: number;
3
+ col: number;
4
+ };
5
+ export interface SelectionState {
6
+ selectedCells: Set<string>;
7
+ startCell?: CellIdentifier;
8
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "grid-cell-selection",
3
+ "version": "1.0.0",
4
+ "description": "A React hook for grid cell selection",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "dev": "vite",
13
+ "build": "rollup -c && tsc --emitDeclarationOnly",
14
+ "lint": "eslint .",
15
+ "preview": "vite preview",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "dependencies": {
19
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
20
+ "react-dom": "^18.3.1"
21
+ },
22
+ "peerDependencies": {
23
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@eslint/js": "^9.11.1",
27
+ "@types/react": "^18.0.0",
28
+ "@types/react-dom": "^18.3.0",
29
+ "@vitejs/plugin-react": "^4.3.2",
30
+ "eslint": "^9.11.1",
31
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
32
+ "eslint-plugin-react-refresh": "^0.4.12",
33
+ "globals": "^15.9.0",
34
+ "rollup": "^2.79.1",
35
+ "rollup-plugin-typescript2": "^0.31.2",
36
+ "typescript": "^4.9.5",
37
+ "typescript-eslint": "^8.7.0",
38
+ "vite": "^5.4.8"
39
+ },
40
+ "keywords": [
41
+ "react",
42
+ "hook",
43
+ "grid",
44
+ "cell",
45
+ "selection"
46
+ ],
47
+ "author": "Atticus Thomson",
48
+ "license": "MIT",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/attithom/grid-cell-selection.git"
52
+ }
53
+ }