@shiguri/solid-grid 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 shiguri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,42 +1,59 @@
1
1
  # solid-grid
2
2
 
3
- Headless spreadsheet-like grid for SolidJS. Works with a 2D array (`T[][]`) and gives you full control over rendering and styling.
3
+ A headless, plugin-driven grid component for [SolidJS](https://www.solidjs.com/).
4
+ Designed for **T[][]** data and custom rendering.
4
5
 
5
- ## Install
6
+ > **Work in progress**: The API is still evolving and may include breaking changes.
7
+
8
+ ## Installation
6
9
 
7
10
  ```bash
8
11
  npm install @shiguri/solid-grid
9
12
  ```
10
13
 
11
- ## Quick Start
14
+ ## Usage
12
15
 
13
16
  ```tsx
14
- import { Gridsheet } from "@shiguri/solid-grid";
15
17
  import { createSignal } from "solid-js";
18
+ import {
19
+ Gridsheet,
20
+ createPluginHost,
21
+ selectionPlugin,
22
+ editingPlugin,
23
+ } from "@shiguri/solid-grid";
16
24
 
17
25
  const [data, setData] = createSignal([
18
26
  ["A1", "B1"],
19
27
  ["A2", "B2"],
20
28
  ]);
21
29
 
30
+ const plugins = createPluginHost([
31
+ selectionPlugin(),
32
+ editingPlugin({ triggerKeys: ["Enter"] }),
33
+ ]);
34
+
22
35
  <Gridsheet
23
36
  data={data()}
24
- onDataChange={setData}
25
- renderCell={(ctx) =>
26
- ctx.isEditing ? (
27
- <input
28
- value={ctx.value}
29
- onKeyDown={(e) =>
30
- e.key === "Enter" && ctx.commitEdit(e.currentTarget.value)
31
- }
32
- />
33
- ) : (
34
- <span>{ctx.value}</span>
35
- )
37
+ onCellsChange={(patches) =>
38
+ setData((prev) => {
39
+ const next = prev.map((row) => row.slice());
40
+ for (const { pos, value } of patches) {
41
+ next[pos.row][pos.col] = value;
42
+ }
43
+ return next;
44
+ })
36
45
  }
46
+ renderCell={(ctx) => <span>{ctx.value}</span>}
47
+ onEvent={plugins.onEvent}
37
48
  />;
38
49
  ```
39
50
 
40
- ## More Info
51
+ ## Documentation
52
+
53
+ - `docs/guide.md`
54
+ - `docs/recipes.md`
55
+ - `docs/styling.md`
56
+
57
+ ---
41
58
 
42
- See full docs in the root repository `docs/`.
59
+ © 2025 shiguri | [MIT License](./LICENSE)
@@ -0,0 +1,138 @@
1
+ import { JSX } from "solid-js/jsx-runtime";
2
+
3
+ //#region src/gridsheet.d.ts
4
+ /** Zero-based cell position. */
5
+ type CellPosition = {
6
+ row: number;
7
+ col: number;
8
+ };
9
+ /** Inclusive range of cells. */
10
+ type CellRange = {
11
+ min: CellPosition;
12
+ max: CellPosition;
13
+ };
14
+ /** Normalize two positions into an inclusive range. */
15
+ declare function normalizeRange(pos1: CellPosition, pos2: CellPosition): CellRange;
16
+ /** Render-time context for a single cell. */
17
+ interface CellRenderContext<T> {
18
+ row: number;
19
+ col: number;
20
+ value: T;
21
+ isActive: boolean;
22
+ isSelected: boolean;
23
+ isEditing: boolean;
24
+ cellRef: HTMLTableCellElement | undefined;
25
+ beginEdit: () => void;
26
+ commitEdit: (value: T) => void;
27
+ cancelEditing: () => void;
28
+ }
29
+ /** Events emitted from Gridsheet to plugins/handlers. */
30
+ type GridEvent = {
31
+ type: "cell:pointerdown";
32
+ pos: CellPosition;
33
+ e: MouseEvent;
34
+ } | {
35
+ type: "cell:pointerover";
36
+ pos: CellPosition;
37
+ e: MouseEvent;
38
+ } | {
39
+ type: "cell:dblclick";
40
+ pos: CellPosition;
41
+ e: MouseEvent;
42
+ } | {
43
+ type: "key:down";
44
+ e: KeyboardEvent;
45
+ } | {
46
+ type: "corner:click";
47
+ e: MouseEvent;
48
+ } | {
49
+ type: "rowheader:pointerdown";
50
+ row: number;
51
+ e: MouseEvent;
52
+ } | {
53
+ type: "rowheader:pointerover";
54
+ row: number;
55
+ e: MouseEvent;
56
+ } | {
57
+ type: "colheader:pointerdown";
58
+ col: number;
59
+ e: MouseEvent;
60
+ } | {
61
+ type: "colheader:pointerover";
62
+ col: number;
63
+ e: MouseEvent;
64
+ } | {
65
+ type: "pointer:up";
66
+ e: PointerEvent | MouseEvent;
67
+ };
68
+ /** Return true to stop further handling. */
69
+ type GridEventHandler<T> = (ev: GridEvent, api: GridApi<T>) => boolean | undefined;
70
+ /** Props for the Gridsheet component. */
71
+ interface GridsheetProps<T> {
72
+ data: T[][];
73
+ /** Apply patches to external data store. */
74
+ onCellsChange?: (patches: CellPatch<T>[]) => void;
75
+ renderCell: (ctx: CellRenderContext<T>) => JSX.Element;
76
+ renderRowHeader?: (ctx: {
77
+ index: number;
78
+ isSelected: boolean;
79
+ }) => JSX.Element;
80
+ renderColHeader?: (ctx: {
81
+ index: number;
82
+ isSelected: boolean;
83
+ }) => JSX.Element;
84
+ activeCell?: CellPosition | null;
85
+ onActiveCellChange?: (pos: CellPosition | null) => void;
86
+ selection?: CellRange | null;
87
+ onSelectionChange?: (range: CellRange | null) => void;
88
+ isEditing?: boolean;
89
+ onIsEditingChange?: (isEditing: boolean) => void;
90
+ /**
91
+ * 本体は仕様を持たず “イベントを投げる” のが仕事。
92
+ * 既定の挙動は plugin 側で組み立てる
93
+ */
94
+ onEvent?: GridEventHandler<T>;
95
+ ref?: HTMLTableElement | ((el: HTMLTableElement) => void) | undefined;
96
+ class?: string;
97
+ style?: JSX.CSSProperties | string;
98
+ classes?: {
99
+ cell?: string | ((ctx: CellRenderContext<T>) => string);
100
+ row?: string | ((ctx: {
101
+ rowIndex: number;
102
+ }) => string);
103
+ rowHeader?: string | ((ctx: {
104
+ rowIndex: number;
105
+ isSelected: boolean;
106
+ }) => string);
107
+ colHeader?: string | ((ctx: {
108
+ colIndex: number;
109
+ isSelected: boolean;
110
+ }) => string);
111
+ corner?: string;
112
+ header?: string;
113
+ body?: string;
114
+ };
115
+ }
116
+ /** Patch for a single cell update. */
117
+ type CellPatch<T> = {
118
+ pos: CellPosition;
119
+ value: T;
120
+ };
121
+ /** API exposed to plugins and external handlers. */
122
+ type GridApi<T> = {
123
+ numRows: () => number;
124
+ numCols: () => number;
125
+ activeCell: () => CellPosition | null;
126
+ selection: () => CellRange | null;
127
+ isEditing: () => boolean;
128
+ setActiveCell: (pos: CellPosition | null) => void;
129
+ setSelection: (range: CellRange | null) => void;
130
+ beginEdit: (pos: CellPosition) => void;
131
+ cancelEdit: () => void;
132
+ commitEdit: (pos: CellPosition, value: T) => void;
133
+ updateCells: (patches: CellPatch<T>[]) => void;
134
+ };
135
+ declare function Gridsheet<T>(props: GridsheetProps<T>): JSX.Element;
136
+ //#endregion
137
+ export { GridApi as a, Gridsheet as c, CellRenderContext as i, GridsheetProps as l, CellPosition as n, GridEvent as o, CellRange as r, GridEventHandler as s, CellPatch as t, normalizeRange as u };
138
+ //# sourceMappingURL=gridsheet-D9A0kuhj.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,73 +1,77 @@
1
- import { JSX } from "solid-js/jsx-runtime";
1
+ import { a as GridApi, c as Gridsheet, i as CellRenderContext, l as GridsheetProps, n as CellPosition, o as GridEvent, r as CellRange, s as GridEventHandler, t as CellPatch, u as normalizeRange } from "./gridsheet-D9A0kuhj.js";
2
2
 
3
- //#region src/index.d.ts
4
- interface CellPosition {
5
- row: number;
6
- col: number;
7
- }
8
- interface CellRange {
9
- min: CellPosition;
10
- max: CellPosition;
11
- }
12
- interface CellRenderContext<T> {
13
- /** Row index */
14
- row: number;
15
- /** Column index */
16
- col: number;
17
- /** Cell value */
18
- value: T;
19
- /** Is this the active cell (focused) */
20
- isActive: boolean;
21
- /** Is this cell currently selected */
22
- isSelected: boolean;
23
- /** Is this cell currently being edited */
24
- isEditing: boolean;
25
- /** Solid ref for scrolling, focusing, measuring, etc. */
26
- cellRef: HTMLTableCellElement | undefined;
27
- /** Enter editing mode for this cell */
28
- beginEdit: () => void;
29
- /** Commit edit with new value */
30
- commitEdit: (value: T) => void;
31
- /** Exit editing mode for this cell */
32
- cancelEditing: () => void;
33
- }
34
- interface GridsheetProps<T> {
35
- /** 2D matrix of values */
3
+ //#region src/plugin.d.ts
4
+ /** Plugin interface for extending grid behavior. */
5
+ type GridPlugin<T> = {
6
+ name: string;
7
+ onEvent?: GridEventHandler<T>;
8
+ };
9
+ /** Compose multiple plugins into a single event handler. */
10
+ declare function createPluginHost<T>(plugins: GridPlugin<T>[]): {
11
+ onEvent: GridEventHandler<T>;
12
+ };
13
+ //#endregion
14
+ //#region src/plugins/clipboard-memory.d.ts
15
+ type ClipboardData<T> = {
36
16
  data: T[][];
37
- onDataChange?: (next: T[][]) => void;
38
- /** Renderer for each cell */
39
- renderCell: (ctx: CellRenderContext<T>) => JSX.Element;
40
- /** Currently focused cell */
41
- activeCell?: CellPosition | null;
42
- onActiveCellChange?: (pos: CellPosition | null) => void;
43
- /** Current selection range */
44
- selection?: CellRange | null;
45
- onSelectionChange?: (range: CellRange | null) => void;
46
- /** Is the active cell in editing mode */
47
- isEditing?: boolean;
48
- onIsEditingChange?: (isEditing: boolean) => void;
49
- ref?: HTMLTableElement | ((el: HTMLTableElement) => void) | undefined;
50
- class?: string;
51
- style?: JSX.CSSProperties | string;
52
- classes?: {
53
- cell?: string | ((ctx: CellRenderContext<T>) => string);
54
- row?: string | ((ctx: {
55
- rowIndex: number;
56
- }) => string);
57
- rowHeader?: string | ((ctx: {
58
- rowIndex: number;
59
- isSelected: boolean;
60
- }) => string);
61
- colHeader?: string | ((ctx: {
62
- colIndex: number;
63
- isSelected: boolean;
64
- }) => string);
65
- corner?: string;
66
- header?: string;
67
- body?: string;
68
- };
69
- }
70
- declare function Gridsheet<T>(props: GridsheetProps<T>): JSX.Element;
17
+ range: CellRange;
18
+ };
19
+ /** Options for in-memory clipboard behavior. */
20
+ type ClipboardPluginOptions<T> = {
21
+ getData: () => T[][];
22
+ onCopy?: (data: T[][], range: CellRange) => boolean | undefined | Promise<boolean | undefined>;
23
+ onCut?: (data: T[][], range: CellRange) => boolean | undefined | Promise<boolean | undefined>;
24
+ onPaste?: (clipboardData: T[][], targetPosition: CellPosition) => CellPatch<T>[] | false | undefined | Promise<CellPatch<T>[] | false | undefined>;
25
+ onClipboardChange?: (clipboard: ClipboardData<T> | null) => void;
26
+ emptyValue?: T;
27
+ getEmptyValue?: (pos: CellPosition) => T;
28
+ copyKeys?: string[];
29
+ cutKeys?: string[];
30
+ pasteKeys?: string[];
31
+ };
32
+ /** In-memory clipboard with optional cut clearing. */
33
+ declare function clipboardMemoryPlugin<T>(options: ClipboardPluginOptions<T>): GridPlugin<T>;
34
+ //#endregion
35
+ //#region src/plugins/clipboard-text.d.ts
36
+ /** Options for system clipboard text/TSV behavior. */
37
+ type ClipboardTextPluginOptions<T> = {
38
+ getData: () => T[][];
39
+ toText?: (data: T[][]) => string;
40
+ fromText?: (text: string, target: CellPosition) => CellPatch<T>[] | false;
41
+ parseCell?: (raw: string) => T;
42
+ formatCell?: (value: T) => string;
43
+ readText?: () => Promise<string>;
44
+ writeText?: (text: string) => Promise<void>;
45
+ emptyValue?: T;
46
+ getEmptyValue?: (pos: CellPosition) => T;
47
+ copyKeys?: string[];
48
+ cutKeys?: string[];
49
+ pasteKeys?: string[];
50
+ };
51
+ /** System clipboard integration with TSV defaults. */
52
+ declare function clipboardTextPlugin<T>(options: ClipboardTextPluginOptions<T>): GridPlugin<T>;
53
+ //#endregion
54
+ //#region src/plugins/delete.d.ts
55
+ /** Options for delete behavior. */
56
+ type DeletePluginOptions<T> = {
57
+ keys?: string[];
58
+ emptyValue?: T;
59
+ getEmptyValue?: (pos: CellPosition) => T;
60
+ onDelete?: (range: CellRange) => CellPatch<T>[] | false | undefined;
61
+ };
62
+ /** Clears selected range with provided empty values. */
63
+ declare function deletePlugin<T>(options: DeletePluginOptions<T>): GridPlugin<T>;
64
+ //#endregion
65
+ //#region src/plugins/editing.d.ts
66
+ type EditingPluginOptions = {
67
+ triggerKeys?: string[];
68
+ };
69
+ /** Starts editing via double click or configured keys. */
70
+ declare function editingPlugin<T>(options?: EditingPluginOptions): GridPlugin<T>;
71
+ //#endregion
72
+ //#region src/plugins/selection.d.ts
73
+ /** Selection, drag, and arrow-key navigation. */
74
+ declare function selectionPlugin<T>(): GridPlugin<T>;
71
75
  //#endregion
72
- export { CellPosition, CellRange, CellRenderContext, Gridsheet, GridsheetProps };
76
+ export { type CellPatch, type CellPosition, type CellRange, type CellRenderContext, type GridApi, type GridEvent, type GridEventHandler, type GridPlugin, Gridsheet, type GridsheetProps, clipboardMemoryPlugin, clipboardTextPlugin, createPluginHost, deletePlugin, editingPlugin, normalizeRange, selectionPlugin };
73
77
  //# sourceMappingURL=index.d.ts.map