xontable 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # xontable
2
2
 
3
- A spreadsheet-like React table component for editable, Excel-style grids.
3
+ A spreadsheet-like React table component with Excel-style editing, selection, clipboard, fill handle, validation, filters, select dropdowns, checkbox cells, and column groups.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,7 +8,7 @@ A spreadsheet-like React table component for editable, Excel-style grids.
8
8
  npm install xontable
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Quick Start
12
12
 
13
13
  ```tsx
14
14
  import React, { useState } from "react";
@@ -31,15 +31,83 @@ export default function App() {
31
31
  }
32
32
  ```
33
33
 
34
- ## Column Types
35
- - `text`
36
- - `number`
37
- - `date`
38
- - `select`
39
- - `checkbox`
34
+ ## Styles (Required)
35
+
36
+ Always import the styles once in your app:
37
+
38
+ ```ts
39
+ import "xontable/styles";
40
+ ```
41
+
42
+ ## Props
43
+
44
+ - `columns`: Column definitions
45
+ - `rows`: Data rows
46
+ - `onChange(nextRows, meta)`: Updated rows with change meta
47
+ - `readOnly`: Disable editing
48
+ - `theme`: `"light" | "dark"`
49
+
50
+ ## Column Definition
51
+
52
+ ```ts
53
+ type ColumnDef<Row> = {
54
+ key: keyof Row | string;
55
+ label: string;
56
+ width?: number;
57
+ editable?: boolean;
58
+ type?: "text" | "number" | "date" | "select" | "checkbox";
59
+ validator?: (value: string, row: Row) => string | null;
60
+ group?: string;
61
+ groupCollapsible?: boolean;
62
+ options?: { value: string; label: string }[];
63
+ getOptions?: (row: Row) => Promise<{ value: string; label: string }[]>;
64
+ dependsOn?: string; // for cascading selects
65
+ };
66
+ ```
67
+
68
+ ## Data Model
69
+
70
+ Rows are objects:
71
+
72
+ ```ts
73
+ type Row = { id: string; [key: string]: any };
74
+ ```
75
+
76
+ `onChange` receives a **new rows array** whenever edits/paste/fill happen.
77
+
78
+ ## Editing
79
+ - Single click selects
80
+ - Enter or double-click to edit
81
+ - Typing starts edit with typed character
82
+ - Enter commits, Esc cancels, Tab commits and moves
83
+
84
+ ## Keyboard Navigation
85
+ - Arrow keys move selection
86
+ - Tab / Shift+Tab moves horizontally
87
+ - Shift + arrows extends selection
88
+
89
+ ## Copy / Paste
90
+ - TSV compatible with Excel/Google Sheets
91
+ - Use Ctrl/Cmd+C to copy selection
92
+ - Use Ctrl/Cmd+V to paste into table
93
+
94
+ ## Fill Handle
95
+ - Drag the dot at bottom-right of active cell
96
+ - Fills down or across with repeated value
97
+
98
+ ## Validation
99
+
100
+ Per-column validation:
101
+
102
+ ```ts
103
+ { key: "qty", label: "Qty", type: "number", validator: (v) => v ? null : "Required" }
104
+ ```
105
+
106
+ Built-in number validation if `type: "number"`.
40
107
 
41
108
  ## Select Dropdowns
42
- Use `options` or `getOptions`:
109
+
110
+ Static options:
43
111
 
44
112
  ```ts
45
113
  { key: "city", label: "City", type: "select", options: [
@@ -47,26 +115,80 @@ Use `options` or `getOptions`:
47
115
  ] }
48
116
  ```
49
117
 
50
- ## Validation
51
- Use `validator` per column:
118
+ Async options:
52
119
 
53
120
  ```ts
54
- { key: "qty", label: "Qty", type: "number", validator: (v) => v ? null : "Required" }
121
+ { key: "group", label: "Group", type: "select", getOptions: async () => groupOptions }
55
122
  ```
56
123
 
57
- ## Readonly & Theme
124
+ Cascading select with `dependsOn`:
125
+
126
+ ```ts
127
+ { key: "subgroup", label: "Subgroup", type: "select", dependsOn: "group", getOptions: async (row) => options[row.group] }
128
+ ```
129
+
130
+ Invalid values will show validation color.
131
+
132
+ ## Checkbox Cells
133
+
134
+ ```ts
135
+ { key: "active", label: "Active", type: "checkbox" }
136
+ ```
137
+
138
+ Values are `true` / `false`.
139
+
140
+ ## Column Groups
141
+
142
+ Group columns by giving the same `group` name:
143
+
144
+ ```ts
145
+ const columns: ColumnDef<Row>[] = [
146
+ { key: "name", label: "Name", group: "User" },
147
+ { key: "active", label: "Active", type: "checkbox", group: "User" },
148
+ { key: "group", label: "Group", type: "select", group: "Account Details" },
149
+ { key: "subgroup", label: "Subgroup", type: "select", group: "Account Details" },
150
+ { key: "city", label: "City", type: "select", group: "Login info" },
151
+ ];
152
+ ```
153
+
154
+ Collapsible groups:
155
+
156
+ ```ts
157
+ { key: "active", label: "Active", group: "User", groupCollapsible: true }
158
+ ```
159
+
160
+ Notes:
161
+ - The top header row shows group labels.
162
+ - If any column in a group has `groupCollapsible: true`, the group is collapsible.
163
+
164
+ ## Filters
165
+ Each column header shows a filter icon.
166
+ - Search inside the filter menu
167
+ - Toggle values on/off
168
+
169
+ ## Readonly Mode
58
170
 
59
171
  ```tsx
60
- <XOnTable readOnly theme="dark" />
172
+ <XOnTable readOnly />
173
+ ```
174
+
175
+ Readonly keeps selection and scrolling but disables editing.
176
+
177
+ ## Theme
178
+
179
+ ```tsx
180
+ <XOnTable theme="dark" />
61
181
  ```
62
182
 
63
183
  ## Requirements
64
184
  - React 19+
65
185
  - Peer deps: `react`, `react-dom`, `lucide-react`
66
186
 
67
- ## Styles
68
- Required:
187
+ ## Troubleshooting
69
188
 
70
- ```ts
71
- import "xontable/styles";
72
- ```
189
+ **Error: Could not resolve "./styles/xontable.css"**
190
+ - Ensure you installed the latest version and it was built/published correctly.
191
+ - Clear Vite cache: delete `node_modules/.vite` and run `npm run dev -- --force`.
192
+
193
+ ## License
194
+ MIT
package/dist/XOnTable.js CHANGED
@@ -171,7 +171,7 @@ export function XOnTable(props) {
171
171
  onGridKeyDown(e);
172
172
  }, [active.c, active.r, onGridKeyDown, selection]);
173
173
  useOutsideClick({ isOpen: filters.filterOpenKey != null, onClose: filters.closeFilter });
174
- return (_jsxs("div", { className: `xontable-wrap theme-${theme}${readOnly ? " is-readonly" : ""}`, children: [_jsx("textarea", { ref: clipRef, className: "xontable-clip", name: "xontable-clip", "aria-hidden": "true", tabIndex: -1, onCopy: onCopy, onPaste: onPaste, onKeyDown: onGridKeyDownWithCopy, readOnly: true }), _jsx("div", { className: "xontable-surface", tabIndex: 0, onFocus: (e) => {
174
+ return (_jsxs("div", { className: `xontable-wrap theme-${theme}${readOnly ? " is-readonly" : ""}`, children: [_jsx("textarea", { ref: clipRef, className: "xontable-clip", name: "xontable-clip", "aria-hidden": "true", tabIndex: -1, onCopy: onCopy, onPaste: onPaste, onKeyDown: onGridKeyDownWithCopy, readOnly: true }), _jsx("div", { className: `xontable-surface${filters.filterOpenKey ? " is-filter-open" : ""}`, tabIndex: 0, onFocus: (e) => {
175
175
  const target = e.target;
176
176
  if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA"))
177
177
  return;
@@ -1,5 +1,6 @@
1
1
  .xontable-wrap { display: block; width: 100%; height: 100%; border: 1px solid #e3e5ea; border-radius: 8px; overflow: hidden; background: #fff; --xontable-range: #1a73e8; --xontable-copy: #1a1a1a; }
2
- .xontable-surface { outline: none; width: 100%; height: 100%; overflow: auto; scrollbar-width: thin; scrollbar-color: #b9c1cd transparent; }
2
+ .xontable-surface { outline: none; width: 100%; height: 100%; min-height: 220px; overflow: auto; scrollbar-width: thin; scrollbar-color: #b9c1cd transparent; }
3
+ .xontable-surface.is-filter-open { overflow-x: auto; overflow-y: visible; }
3
4
  .xontable-surface::-webkit-scrollbar { width: 8px; height: 8px; }
4
5
  .xontable-surface::-webkit-scrollbar-thumb { background: #b9c1cd; border-radius: 999px; }
5
6
  .xontable-surface::-webkit-scrollbar-track { background: transparent; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xontable",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",