xontable 0.1.4 → 0.2.1

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
@@ -46,6 +46,8 @@ import "xontable/styles";
46
46
  - `onChange(nextRows, meta)`: Updated rows with change meta
47
47
  - `readOnly`: Disable editing
48
48
  - `theme`: `"light" | "dark"`
49
+ - `showStatusBar`: Show validation summary bar
50
+ - `darkThemeColors`: Override dark theme colors
49
51
 
50
52
  ## Column Definition
51
53
 
@@ -65,27 +67,12 @@ type ColumnDef<Row> = {
65
67
  };
66
68
  ```
67
69
 
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
70
  ## Editing
79
71
  - Single click selects
80
72
  - Enter or double-click to edit
81
73
  - Typing starts edit with typed character
82
74
  - Enter commits, Esc cancels, Tab commits and moves
83
75
 
84
- ## Keyboard Navigation
85
- - Arrow keys move selection
86
- - Tab / Shift+Tab moves horizontally
87
- - Shift + arrows extends selection
88
-
89
76
  ## Copy / Paste
90
77
  - TSV compatible with Excel/Google Sheets
91
78
  - Use Ctrl/Cmd+C to copy selection
@@ -103,11 +90,13 @@ Per-column validation:
103
90
  { key: "qty", label: "Qty", type: "number", validator: (v) => v ? null : "Required" }
104
91
  ```
105
92
 
106
- Built-in number validation if `type: "number"`.
93
+ ## Status Bar
107
94
 
108
- ## Select Dropdowns
95
+ ```tsx
96
+ <XOnTable showStatusBar />
97
+ ```
109
98
 
110
- Static options:
99
+ ## Select Dropdowns
111
100
 
112
101
  ```ts
113
102
  { key: "city", label: "City", type: "select", options: [
@@ -115,32 +104,14 @@ Static options:
115
104
  ] }
116
105
  ```
117
106
 
118
- Async options:
119
-
120
- ```ts
121
- { key: "group", label: "Group", type: "select", getOptions: async () => groupOptions }
122
- ```
123
-
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
107
  ## Checkbox Cells
133
108
 
134
109
  ```ts
135
110
  { key: "active", label: "Active", type: "checkbox" }
136
111
  ```
137
112
 
138
- Values are `true` / `false`.
139
-
140
113
  ## Column Groups
141
114
 
142
- Group columns by giving the same `group` name:
143
-
144
115
  ```ts
145
116
  const columns: ColumnDef<Row>[] = [
146
117
  { key: "name", label: "Name", group: "User" },
@@ -151,38 +122,24 @@ const columns: ColumnDef<Row>[] = [
151
122
  ];
152
123
  ```
153
124
 
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
125
+ ## Theme
170
126
 
171
127
  ```tsx
172
- <XOnTable readOnly />
128
+ <XOnTable theme="dark" />
173
129
  ```
174
130
 
175
- Readonly keeps selection and scrolling but disables editing.
176
-
177
- ## Theme
131
+ ### Dark Theme Colors (Props)
178
132
 
179
133
  ```tsx
180
- <XOnTable theme="dark" />
134
+ <XOnTable
135
+ theme="dark"
136
+ darkThemeColors={{ bg: "#111318", headBg: "#1c1f26", accent: "#7aa2ff" }}
137
+ />
181
138
  ```
182
139
 
183
140
  ## Requirements
184
141
  - React 19+
185
- - Peer deps: `react`, `react-dom`, `lucide-react`
142
+ - Peer deps: `react`, `react-dom`
186
143
 
187
144
  ## Troubleshooting
188
145
 
@@ -1 +1 @@
1
- {"version":3,"file":"XOnTable.d.ts","sourceRoot":"","sources":["../src/XOnTable.tsx"],"names":[],"mappings":"AACA,OAAO,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C,wBAAgB,QAAQ,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,2CAkIlF"}
1
+ {"version":3,"file":"XOnTable.d.ts","sourceRoot":"","sources":["../src/XOnTable.tsx"],"names":[],"mappings":"AACA,OAAO,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C,wBAAgB,QAAQ,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,2CA8IlF"}
package/dist/XOnTable.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import "./styles/xontable.css";
4
- import { SelectMenu, XOnTableGrid } from "./components";
4
+ import { SelectMenu, XOnTableGrid, XOnTableStatusBar } from "./components";
5
5
  import { useAutoRows, useClipboardCatcher, useColumnFilters, useColumnGroups, useColumnResize, useEditorOverlay, useFillHandle, useGridKeydown, useOutsideClick, useRangeSelection, useSelectOptions, useTableModel } from "./hooks";
6
6
  const clamp = (n, min, max) => Math.max(min, Math.min(max, n));
7
7
  export function XOnTable(props) {
8
- const { columns, rows, rowIdKey = "id", onChange, readOnly = false, theme = "light" } = props;
8
+ const { columns, rows, rowIdKey = "id", onChange, readOnly = false, theme = "light", showStatusBar = false, darkThemeColors } = props;
9
9
  const activeCellRef = React.useRef(null);
10
10
  const [editingRowId, setEditingRowId] = React.useState(null);
11
11
  const { normalizedRows, handleChange, createRow } = useAutoRows(columns, rows, rowIdKey, onChange, editingRowId);
@@ -15,7 +15,7 @@ export function XOnTable(props) {
15
15
  const selection = useRangeSelection();
16
16
  const [copiedBounds, setCopiedBounds] = React.useState(null);
17
17
  React.useEffect(() => { resetWidths(); }, [columns, resetWidths]);
18
- const { data, active, setActive, getValue, updateCells, moveActive, rowCount, colCount, hasError, getError, setCellErrorView, undo, redo } = useTableModel({
18
+ const { data, active, setActive, getValue, updateCells, moveActive, rowCount, colCount, hasError, getError, setCellErrorView, errorList, undo, redo } = useTableModel({
19
19
  columns: visibleColumns.map((v) => v.col),
20
20
  rows: normalizedRows,
21
21
  rowFilter: filters.rowFilter,
@@ -171,12 +171,24 @@ 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${filters.filterOpenKey ? " is-filter-open" : ""}`, tabIndex: 0, onFocus: (e) => {
174
+ const darkVars = theme === "dark" && darkThemeColors ? {
175
+ "--xontable-dark-bg": darkThemeColors.bg, "--xontable-dark-border": darkThemeColors.border, "--xontable-dark-text": darkThemeColors.text,
176
+ "--xontable-dark-cell-bg": darkThemeColors.cellBg, "--xontable-dark-head-bg": darkThemeColors.headBg, "--xontable-dark-rownum-bg": darkThemeColors.rownumBg,
177
+ "--xontable-dark-group-bg": darkThemeColors.groupBg, "--xontable-dark-zebra-bg": darkThemeColors.zebraBg, "--xontable-dark-active-head-bg": darkThemeColors.activeHeadBg,
178
+ "--xontable-dark-range": darkThemeColors.range, "--xontable-dark-copy": darkThemeColors.copy, "--xontable-dark-accent": darkThemeColors.accent,
179
+ "--xontable-dark-select": darkThemeColors.select, "--xontable-dark-invalid-bg": darkThemeColors.invalidBg, "--xontable-dark-invalid-border": darkThemeColors.invalidBorder,
180
+ "--xontable-dark-editor-bg": darkThemeColors.editorBg, "--xontable-dark-editor-text": darkThemeColors.editorText,
181
+ "--xontable-dark-readonly-bg": darkThemeColors.readonlyBg, "--xontable-dark-readonly-border": darkThemeColors.readonlyBorder,
182
+ "--xontable-dark-readonly-head-bg": darkThemeColors.readonlyHeadBg, "--xontable-dark-readonly-rownum-bg": darkThemeColors.readonlyRownumBg,
183
+ "--xontable-dark-readonly-zebra-bg": darkThemeColors.readonlyZebraBg,
184
+ } : undefined;
185
+ const onSelectError = React.useCallback((r, c) => { setActive({ r, c }); selection.startSelection({ r, c }); focusClipboard(); }, [focusClipboard, selection, setActive]);
186
+ return (_jsxs("div", { className: `xontable-wrap theme-${theme}${readOnly ? " is-readonly" : ""}`, style: darkVars, 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
187
  const target = e.target;
176
188
  if (target && (target.tagName === "INPUT" || target.tagName === "TEXTAREA"))
177
189
  return;
178
190
  focusClipboard();
179
191
  }, onKeyDown: onGridKeyDownWithCopy, children: _jsx(XOnTableGrid, { columns: visibleColumns, groups: groupHeaders.some((g) => g.label) ? groupHeaders : undefined, rowNumberWidth: 44, data: data, rowIdKey: rowIdKey, active: active, activeCol: active.c, isEditing: isEditing, readOnly: readOnly, selectionBounds: selection.getBounds(), copiedBounds: copiedBounds, getColWidth: getColWidth, getValue: getValue, hasError: hasError, getError: getError, isPreview: isPreview, activeCellRef: activeCellRef, onCellMouseDown: (r, c, ev) => { ev.preventDefault(); setActive({ r, c }); selection.startSelection({ r, c }); setCopiedBounds(null); focusClipboard(); if (filters.filterOpenKey)
180
192
  filters.closeFilter(); }, onCellMouseEnter: (r, c) => { if (selection.isSelecting)
181
- selection.updateSelection({ r, c }); }, onCellDoubleClick: (r, c) => { setActive({ r, c }); startEditCell(); }, onCheckboxToggle: toggleCheckbox, onSelectOpen: openSelectAt, onFillStart: (r, c, ev) => { ev.preventDefault(); ev.stopPropagation(); startDrag(r, c); }, onResizeStart: (c, ev) => { ev.preventDefault(); ev.stopPropagation(); startResize(c, ev.clientX); }, onResizeDoubleClick: (c, ev) => { ev.preventDefault(); ev.stopPropagation(); autoFitCol(c); }, onGroupToggle: toggleGroup, filterOpenKey: filters.filterOpenKey, filterSearch: filters.filterSearch, getFilterOptions: filters.getFilterOptions, isFilterChecked: filters.isFilterChecked, isFilterAllChecked: filters.isAllChecked, onFilterOpen: filters.openFilter, onFilterSearch: filters.setFilterSearch, onFilterToggle: filters.toggleFilterValue, onFilterToggleAll: filters.toggleAll }) }), isEditing && editorRect && (_jsx("input", { ref: editorRef, className: "xontable-editor", name: "xontable-editor", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: onEditorKeyDown, onBlur: () => { commitEdit(); validateSelect(active.r, active.c, draft, activeRow); }, style: { position: "fixed", left: editorRect.left, top: editorRect.top, width: editorRect.width, height: editorRect.height } })), _jsx(SelectMenu, { isOpen: Boolean(isEditing && activeCol?.type === "select"), rect: editorRect, options: activeRow && activeCol ? getOptions(activeRow, activeCol) : [], loading: Boolean(activeRow && activeCol && isLoading(activeRow, activeCol)), filter: draft, onSelect: (v) => { setDraft(v); commitEdit(v); validateSelect(active.r, active.c, v, activeRow); } })] }));
193
+ selection.updateSelection({ r, c }); }, onCellDoubleClick: (r, c) => { setActive({ r, c }); startEditCell(); }, onCheckboxToggle: toggleCheckbox, onSelectOpen: openSelectAt, onFillStart: (r, c, ev) => { ev.preventDefault(); ev.stopPropagation(); startDrag(r, c); }, onResizeStart: (c, ev) => { ev.preventDefault(); ev.stopPropagation(); startResize(c, ev.clientX); }, onResizeDoubleClick: (c, ev) => { ev.preventDefault(); ev.stopPropagation(); autoFitCol(c); }, onGroupToggle: toggleGroup, filterOpenKey: filters.filterOpenKey, filterSearch: filters.filterSearch, getFilterOptions: filters.getFilterOptions, isFilterChecked: filters.isFilterChecked, isFilterAllChecked: filters.isAllChecked, onFilterOpen: filters.openFilter, onFilterSearch: filters.setFilterSearch, onFilterToggle: filters.toggleFilterValue, onFilterToggleAll: filters.toggleAll }) }), showStatusBar && _jsx(XOnTableStatusBar, { errors: errorList, columns: visibleColumns, onSelect: onSelectError }), isEditing && editorRect && (_jsx("input", { ref: editorRef, className: "xontable-editor", name: "xontable-editor", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: onEditorKeyDown, onBlur: () => { commitEdit(); validateSelect(active.r, active.c, draft, activeRow); }, style: { position: "fixed", left: editorRect.left, top: editorRect.top, width: editorRect.width, height: editorRect.height } })), _jsx(SelectMenu, { isOpen: Boolean(isEditing && activeCol?.type === "select"), rect: editorRect, options: activeRow && activeCol ? getOptions(activeRow, activeCol) : [], loading: Boolean(activeRow && activeCol && isLoading(activeRow, activeCol)), filter: draft, onSelect: (v) => { setDraft(v); commitEdit(v); validateSelect(active.r, active.c, v, activeRow); } })] }));
182
194
  }
@@ -1 +1 @@
1
- {"version":3,"file":"XOnTableHeader.d.ts","sourceRoot":"","sources":["../../src/components/XOnTableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,KAAK,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3G,KAAK,WAAW,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI;IAClD,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACzD,mBAAmB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC5C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AAEF,wBAAgB,cAAc,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,2CAiDtF"}
1
+ {"version":3,"file":"XOnTableHeader.d.ts","sourceRoot":"","sources":["../../src/components/XOnTableHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,KAAK,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3G,KAAK,WAAW,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI;IAClD,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IACzD,mBAAmB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC5C,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AAEF,wBAAgB,cAAc,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,2CAgEtF"}
@@ -1,7 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Filter, Minus, Plus } from "lucide-react";
3
2
  import { ColumnFilterMenu } from "./ColumnFilterMenu";
4
3
  export function XOnTableHeader(props) {
5
4
  const { columns, groups, rowNumberWidth, activeCol, getColWidth, onResizeStart, onResizeDoubleClick, onGroupToggle, filterOpenKey, filterSearch, getFilterOptions, isFilterChecked, isFilterAllChecked, onFilterOpen, onFilterSearch, onFilterToggle, onFilterToggleAll } = props;
6
- return (_jsxs(_Fragment, { children: [groups && groups.length > 0 && (_jsxs("div", { className: "xontable-row xontable-group-row", children: [_jsx("div", { className: "xontable-cell xontable-rownum-cell xontable-group-cell", style: { width: rowNumberWidth } }), groups.map((g) => (_jsxs("div", { className: "xontable-cell xontable-group-cell", style: { width: g.width }, children: [_jsx("span", { className: "xontable-group-label", children: g.label }), g.collapsible && (_jsx("button", { type: "button", className: "xontable-group-toggle", onClick: () => onGroupToggle(g.key), title: g.collapsed ? "Expand" : "Collapse", children: g.collapsed ? _jsx(Plus, { size: 12 }) : _jsx(Minus, { size: 12 }) }))] }, g.key)))] })), _jsxs("div", { className: "xontable-row xontable-head", children: [_jsx("div", { className: "xontable-cell xontable-rownum-cell xontable-head-cell", style: { width: rowNumberWidth } }), columns.map(({ col, idx }, c) => (_jsxs("div", { className: ["xontable-cell", "xontable-head-cell", c === activeCol ? "is-active-col-head" : ""].join(" "), style: { width: getColWidth(c) }, children: [_jsx("span", { className: "xontable-head-label", children: col.label }), idx != null && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "xontable-filter-btn", onClick: () => onFilterOpen(col.key), title: "Filter", children: _jsx(Filter, { size: 14 }) }), _jsx("div", { className: "xontable-col-resizer", onMouseDown: (ev) => onResizeStart(c, ev), onDoubleClick: (ev) => onResizeDoubleClick(c, ev), title: "Drag to resize" }), _jsx(ColumnFilterMenu, { isOpen: filterOpenKey === col.key, search: filterSearch, options: getFilterOptions(col.key), allChecked: isFilterAllChecked(col.key), isChecked: (v) => isFilterChecked(col.key, v), onSearchChange: onFilterSearch, onToggle: (v) => onFilterToggle(col.key, v), onToggleAll: () => onFilterToggleAll(col.key) })] }))] }, col.key + String(idx ?? c))))] })] }));
5
+ const FilterIcon = () => (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M3 5h18M6 11h12M10 17h4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
6
+ const PlusIcon = () => (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M12 5v14M5 12h14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
7
+ const MinusIcon = () => (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M5 12h14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
8
+ return (_jsxs(_Fragment, { children: [groups && groups.length > 0 && (_jsxs("div", { className: "xontable-row xontable-group-row", children: [_jsx("div", { className: "xontable-cell xontable-rownum-cell xontable-group-cell", style: { width: rowNumberWidth } }), groups.map((g) => (_jsxs("div", { className: "xontable-cell xontable-group-cell", style: { width: g.width }, children: [_jsx("span", { className: "xontable-group-label", children: g.label }), g.collapsible && (_jsx("button", { type: "button", className: "xontable-group-toggle", onClick: () => onGroupToggle(g.key), title: g.collapsed ? "Expand" : "Collapse", children: g.collapsed ? _jsx(PlusIcon, {}) : _jsx(MinusIcon, {}) }))] }, g.key)))] })), _jsxs("div", { className: "xontable-row xontable-head", children: [_jsx("div", { className: "xontable-cell xontable-rownum-cell xontable-head-cell", style: { width: rowNumberWidth } }), columns.map(({ col, idx }, c) => (_jsxs("div", { className: ["xontable-cell", "xontable-head-cell", c === activeCol ? "is-active-col-head" : ""].join(" "), style: { width: getColWidth(c) }, children: [_jsx("span", { className: "xontable-head-label", children: col.label }), idx != null && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "xontable-filter-btn", onClick: () => onFilterOpen(col.key), title: "Filter", children: _jsx(FilterIcon, {}) }), _jsx("div", { className: "xontable-col-resizer", onMouseDown: (ev) => onResizeStart(c, ev), onDoubleClick: (ev) => onResizeDoubleClick(c, ev), title: "Drag to resize" }), _jsx(ColumnFilterMenu, { isOpen: filterOpenKey === col.key, search: filterSearch, options: getFilterOptions(col.key), allChecked: isFilterAllChecked(col.key), isChecked: (v) => isFilterChecked(col.key, v), onSearchChange: onFilterSearch, onToggle: (v) => onFilterToggle(col.key, v), onToggleAll: () => onFilterToggleAll(col.key) })] }))] }, col.key + String(idx ?? c))))] })] }));
7
9
  }
@@ -0,0 +1,17 @@
1
+ import type { ColumnDef } from "../types";
2
+ type ErrorItem = {
3
+ r: number;
4
+ c: number;
5
+ message: string;
6
+ };
7
+ type StatusBarProps<Row extends Record<string, any>> = {
8
+ errors: ErrorItem[];
9
+ columns: Array<{
10
+ col: ColumnDef<Row>;
11
+ idx: number | null;
12
+ }>;
13
+ onSelect: (r: number, c: number) => void;
14
+ };
15
+ export declare function XOnTableStatusBar<Row extends Record<string, any>>(props: StatusBarProps<Row>): import("react/jsx-runtime").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=XOnTableStatusBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XOnTableStatusBar.d.ts","sourceRoot":"","sources":["../../src/components/XOnTableStatusBar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,KAAK,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,KAAK,cAAc,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI;IACrD,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC5D,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,2CAsB5F"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function XOnTableStatusBar(props) {
3
+ const { errors, columns, onSelect } = props;
4
+ const total = errors.length;
5
+ const visible = errors.slice(0, 3);
6
+ const countLabel = total ? "Errors" : "No errors";
7
+ return (_jsxs("div", { className: "xontable-status", children: [_jsxs("span", { className: ["xontable-status-count", total ? "is-error" : ""].join(" "), children: [_jsx("span", { className: "xontable-status-num", children: total }), _jsx("span", { className: "xontable-status-label", children: countLabel })] }), visible.map((e, idx) => {
8
+ const col = columns[e.c]?.col;
9
+ const label = col?.label ?? `Col ${e.c + 1}`;
10
+ return (_jsx("button", { type: "button", className: ["xontable-status-item", "is-error", `is-chip-${idx % 3}`].join(" "), onClick: () => onSelect(e.r, e.c), title: e.message, children: `Row ${e.r + 1}, ${label}: ${e.message}` }, `${e.r}:${e.c}`));
11
+ })] }));
12
+ }
@@ -2,4 +2,5 @@ export { ColumnFilterMenu } from "./ColumnFilterMenu";
2
2
  export { SelectMenu } from "./SelectMenu";
3
3
  export { XOnTableGrid } from "./XOnTableGrid";
4
4
  export { XOnTableHeader } from "./XOnTableHeader";
5
+ export { XOnTableStatusBar } from "./XOnTableStatusBar";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -2,3 +2,4 @@ export { ColumnFilterMenu } from "./ColumnFilterMenu";
2
2
  export { SelectMenu } from "./SelectMenu";
3
3
  export { XOnTableGrid } from "./XOnTableGrid";
4
4
  export { XOnTableHeader } from "./XOnTableHeader";
5
+ export { XOnTableStatusBar } from "./XOnTableStatusBar";
@@ -24,6 +24,11 @@ export declare function useTableModel<Row extends Record<string, any>>(options:
24
24
  hasError: (r: number, c: number) => boolean;
25
25
  getError: (r: number, c: number) => string | null;
26
26
  setCellErrorView: (r: number, c: number, msg: string | null) => void;
27
+ errorList: {
28
+ r: number;
29
+ c: number;
30
+ message: string;
31
+ }[];
27
32
  undo: () => void;
28
33
  redo: () => void;
29
34
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useTableModel.d.ts","sourceRoot":"","sources":["../../src/hooks/useTableModel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGjE,KAAK,UAAU,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC;AAEvD,KAAK,iBAAiB,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI;IACxD,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC;CACvB,CAAC;AAIF,wBAAgB,aAAa,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC;;;;;kBAsB3D,MAAM,KAAK,MAAM;2BAgBR,UAAU,EAAE,QAAQ,YAAY;qBAoBtC,MAAM,MAAM,MAAM;;;kBAmBjB,MAAM,KAAK,MAAM;kBAIjB,MAAM,KAAK,MAAM;0BApDb,MAAM,KAAK,MAAM,OAAO,MAAM,GAAG,IAAI;;;EAwE/E"}
1
+ {"version":3,"file":"useTableModel.d.ts","sourceRoot":"","sources":["../../src/hooks/useTableModel.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGjE,KAAK,UAAU,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC;AAEvD,KAAK,iBAAiB,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI;IACxD,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC;CACvB,CAAC;AAIF,wBAAgB,aAAa,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC;;;;;kBAsB3D,MAAM,KAAK,MAAM;2BAgBR,UAAU,EAAE,QAAQ,YAAY;qBAoBtC,MAAM,MAAM,MAAM;;;kBAmBjB,MAAM,KAAK,MAAM;kBAIjB,MAAM,KAAK,MAAM;0BApDb,MAAM,KAAK,MAAM,OAAO,MAAM,GAAG,IAAI;;;;;;;;EAmF/E"}
@@ -32,7 +32,7 @@ export function useTableModel(options) {
32
32
  const v = row && col ? row[col.key] : "";
33
33
  return v == null ? "" : String(v);
34
34
  }, [columns, view.map]);
35
- const { validateCell, setCellError, hasError, getError } = useValidation(columns, getRow, getValue);
35
+ const { validateCell, setCellError, hasError, getError, errors } = useValidation(columns, getRow, getValue);
36
36
  const setCellErrorView = useCallback((r, c, msg) => {
37
37
  const real = view.map[r];
38
38
  if (real == null)
@@ -116,6 +116,16 @@ export function useTableModel(options) {
116
116
  const real = view.map[r];
117
117
  return real == null ? null : getError(real, c);
118
118
  }, [getError, view.map]);
119
+ const errorList = useMemo(() => {
120
+ const map = new Map();
121
+ view.map.forEach((real, idx) => { if (real != null)
122
+ map.set(real, idx); });
123
+ return Object.entries(errors).flatMap(([key, message]) => {
124
+ const [r, c] = key.split(":").map(Number);
125
+ const vr = map.get(r);
126
+ return vr == null || Number.isNaN(c) ? [] : [{ r: vr, c, message }];
127
+ });
128
+ }, [errors, view.map]);
119
129
  return {
120
130
  data: view.list,
121
131
  rawData: dataRef.current,
@@ -129,6 +139,7 @@ export function useTableModel(options) {
129
139
  hasError: hasErrorView,
130
140
  getError: getErrorView,
131
141
  setCellErrorView,
142
+ errorList,
132
143
  undo,
133
144
  redo,
134
145
  };
@@ -45,8 +45,8 @@
45
45
  }
46
46
  .xontable-head-cell.is-active-col-head,
47
47
  .xontable-rownum-cell.is-active-rownum { background: #cfe3ff; }
48
- .xontable-cell.is-invalid { background: #ffe5e5; border: 1px solid #d92d2d; box-shadow: inset 0 0 0 1px #d92d2d; }
49
- .xontable-cell.is-invalid:hover { background: #ffd6d6; }
48
+ .xontable-cell.is-invalid { background: #fff1f1; border: 1px solid #f3b1b1; box-shadow: inset 0 0 0 0 #f3b1b1; }
49
+ .xontable-cell.is-invalid:hover { background: #ffe7e7; }
50
50
  .xontable-cell.is-fill-preview { background: #e8f0fe; }
51
51
  .xontable-fill-handle { position: absolute; width: 14px; height: 14px; right: -7px; bottom: -7px; background: #0b65d4; border: 1px solid #fff; border-radius: 4px; cursor: crosshair; }
52
52
  .xontable-editor { box-sizing: border-box; border: 2px solid #1a73e8; padding: 2px 6px 2px 8px; font: inherit; background: #fff; outline: none; line-height: 1.2; }
@@ -54,6 +54,19 @@
54
54
  .xontable-select-menu { background: #fff; border: 1px solid #e1e4ea; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12); border-radius: 4px; padding: 4px 0; z-index: 30; max-height: 220px; overflow: auto; }
55
55
  .xontable-select-item { width: 100%; text-align: left; padding: 6px 10px; border: 0; background: transparent; cursor: pointer; font-size: 12px; }
56
56
  .xontable-select-item:hover, .xontable-select-item.is-active { background: #e9f1ff; }
57
+ .xontable-status { display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 12px; border-top: 1px solid #e6e8ee; background: #f9fafb; color: #374151; }
58
+ .xontable-status-count { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; background: #eef2ff; color: #3730a3; padding: 2px 8px; border-radius: 999px; }
59
+ .xontable-status-count.is-error { background: #fff1f2; color: #b91c1c; }
60
+ .xontable-status-num { width: 18px; height: 18px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; background: #3730a3; color: #fff; font-size: 11px; }
61
+ .xontable-status-count.is-error .xontable-status-num { background: #dc2626; }
62
+ .xontable-status-label { letter-spacing: 0.2px; }
63
+ .xontable-status-item { border: 1px solid #e6e8ee; background: #fff; color: #1f2937; cursor: pointer; padding: 2px 6px; border-radius: 999px; text-align: left; white-space: nowrap; }
64
+ .xontable-status-item.is-error { background: #ffe7e7; border-color: #dc2626; color: #a10d0d; }
65
+ .xontable-status-item:hover { background: #eef2ff; border-color: #c7d2fe; }
66
+ .xontable-status-item.is-error:hover { background: #ffdada; border-color: #f2a0a0; }
67
+ .xontable-status-item.is-chip-0 { background: #eef2ff; border-color: #c7d2fe; color: #3730a3; }
68
+ .xontable-status-item.is-chip-1 { background: #ecfdf5; border-color: #a7f3d0; color: #065f46; }
69
+ .xontable-status-item.is-chip-2 { background: #fff7ed; border-color: #fed7aa; color: #9a3412; }
57
70
 
58
71
  .xontable-wrap.is-readonly { background: #fff; border-color: #e4e7ec; }
59
72
  .xontable-wrap.is-readonly .xontable { color: #222; }
@@ -69,4 +82,4 @@
69
82
  .xontable-wrap.is-readonly .xontable-fill-handle { display: none; }
70
83
  .xontable-wrap.is-readonly .xontable-select-trigger { display: none; }
71
84
  .xontable-wrap.is-readonly .xontable-checkbox { accent-color: #49b86e; }
72
- .xontable-wrap.is-readonly .xontable-cell.is-invalid { background: #ffecec; border-color: #e07a7a; box-shadow: inset 0 0 0 1px #e07a7a; }
85
+ .xontable-wrap.is-readonly .xontable-cell.is-invalid { background: #fff1f1; border-color: #f4c2c2; box-shadow: inset 0 0 0 0 #f4c2c2; }
@@ -63,3 +63,18 @@
63
63
  gap: 6px;
64
64
  font-size: 12px;
65
65
  }
66
+
67
+ .xontable-wrap.theme-dark .xontable-filter-menu {
68
+ background: var(--xontable-dark-head-bg, #2b2d31);
69
+ border-color: var(--xontable-dark-border, #3a3a3a);
70
+ color: var(--xontable-dark-text, #e6e6e6);
71
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
72
+ }
73
+ .xontable-wrap.theme-dark .xontable-filter-search {
74
+ background: var(--xontable-dark-cell-bg, #1e1f22);
75
+ border-color: var(--xontable-dark-border, #3a3a3a);
76
+ color: var(--xontable-dark-text, #e6e6e6);
77
+ }
78
+ .xontable-wrap.theme-dark .xontable-filter-item span {
79
+ color: var(--xontable-dark-text, #e6e6e6);
80
+ }
@@ -1,42 +1,54 @@
1
- .xontable-wrap.theme-dark { border-color: #3a3a3a; background: #1e1f22; --xontable-range: #8ab4f8; --xontable-copy: #e6e6e6; }
2
- .xontable-wrap.theme-dark .xontable { color: #e6e6e6; background: transparent; }
1
+ .xontable-wrap.theme-dark { border-color: var(--xontable-dark-border, #3a3a3a); background: var(--xontable-dark-bg, #1e1f22); --xontable-range: var(--xontable-dark-range, #8ab4f8); --xontable-copy: var(--xontable-dark-copy, #e6e6e6); }
2
+ .xontable-wrap.theme-dark .xontable { color: var(--xontable-dark-text, #e6e6e6); background: transparent; }
3
3
 
4
4
  .xontable-wrap.theme-dark .xontable-cell {
5
- border-color: #3a3a3a;
6
- background: #1e1f22;
5
+ border-color: var(--xontable-dark-border, #3a3a3a);
6
+ background: var(--xontable-dark-cell-bg, #1e1f22);
7
7
  }
8
8
  .xontable-wrap.theme-dark .xontable-surface { scrollbar-color: #4b5563 transparent; }
9
9
  .xontable-wrap.theme-dark .xontable-surface::-webkit-scrollbar-thumb { background: #4b5563; }
10
- .xontable-wrap.theme-dark .xontable-select-trigger::after { border-top-color: #b6bcc5; }
11
- .xontable-wrap.theme-dark .xontable-checkbox { accent-color: #8ab4f8; }
10
+ .xontable-wrap.theme-dark .xontable-select-trigger::after { border-top-color: var(--xontable-dark-select, #b6bcc5); }
11
+ .xontable-wrap.theme-dark .xontable-checkbox { accent-color: var(--xontable-dark-accent, #8ab4f8); }
12
12
 
13
13
  .xontable-wrap.theme-dark .xontable-cell.is-range { background: #1f3526; }
14
- .xontable-wrap.theme-dark .xontable-cell.is-invalid { background: #4a1f1f; border-color: #e05b5b; box-shadow: inset 0 0 0 1px #e05b5b; }
14
+ .xontable-wrap.theme-dark .xontable-cell.is-invalid { background: var(--xontable-dark-invalid-bg, #3a1f1f); border-color: var(--xontable-dark-invalid-border, #efb1b1); box-shadow: inset 0 0 0 0 var(--xontable-dark-invalid-border, #efb1b1); }
15
15
  .xontable-wrap.theme-dark .xontable-cell.is-copied { outline: 1px dotted #7ad27a; outline-offset: -1px; }
16
- .xontable-wrap.theme-dark .xontable-editor { background: #1e1f22; color: #e6e6e6; border-color: #8ab4f8; }
16
+ .xontable-wrap.theme-dark .xontable-editor { background: var(--xontable-dark-editor-bg, #1e1f22); color: var(--xontable-dark-editor-text, #e6e6e6); border-color: var(--xontable-dark-accent, #8ab4f8); }
17
17
 
18
18
  .xontable-wrap.theme-dark .xontable-row.is-zebra .xontable-cell {
19
- background: #232428;
19
+ background: var(--xontable-dark-zebra-bg, #232428);
20
20
  }
21
21
 
22
22
  .xontable-wrap.theme-dark .xontable-head-cell,
23
23
  .xontable-wrap.theme-dark .xontable-rownum-cell,
24
24
  .xontable-wrap.theme-dark .xontable-group-cell {
25
- background: #2b2d31;
26
- color: #e6e6e6;
25
+ background: var(--xontable-dark-head-bg, #2b2d31);
26
+ color: var(--xontable-dark-text, #e6e6e6);
27
27
  }
28
28
 
29
29
  .xontable-wrap.theme-dark .xontable-head-cell.is-active-col-head,
30
30
  .xontable-wrap.theme-dark .xontable-rownum-cell.is-active-rownum {
31
- background: #344769;
31
+ background: var(--xontable-dark-active-head-bg, #344769);
32
32
  }
33
+ .xontable-wrap.theme-dark .xontable-status { background: var(--xontable-dark-head-bg, #2b2d31); border-top-color: var(--xontable-dark-border, #3a3a3a); color: var(--xontable-dark-text, #e6e6e6); }
34
+ .xontable-wrap.theme-dark .xontable-status-count { background: #2a3552; color: #c7d2fe; }
35
+ .xontable-wrap.theme-dark .xontable-status-count.is-error { background: #3a1f1f; color: #fca5a5; }
36
+ .xontable-wrap.theme-dark .xontable-status-num { background: #374151; color: #fff; }
37
+ .xontable-wrap.theme-dark .xontable-status-count.is-error .xontable-status-num { background: #dc2626; }
38
+ .xontable-wrap.theme-dark .xontable-status-item { color: var(--xontable-dark-text, #e6e6e6); background: #1e222b; border-color: var(--xontable-dark-border, #3a3a3a); }
39
+ .xontable-wrap.theme-dark .xontable-status-item:hover { background: #2a3552; border-color: #3b4a6b; }
40
+ .xontable-wrap.theme-dark .xontable-status-item.is-error { background: #3a1f1f; border-color: #dc2626; color: #fca5a5; }
41
+ .xontable-wrap.theme-dark .xontable-status-item.is-chip-0 { background: #23304a; border-color: #3b4a6b; color: #c7d2fe; }
42
+ .xontable-wrap.theme-dark .xontable-status-item.is-chip-1 { background: #1d2f28; border-color: #2f5f4a; color: #6ee7b7; }
43
+ .xontable-wrap.theme-dark .xontable-status-item.is-chip-2 { background: #2b251d; border-color: #6b4a2f; color: #fdba74; }
33
44
 
34
- .xontable-wrap.theme-dark.is-readonly { border-color: #2c2f36; background: #1b1c1f; }
35
- .xontable-wrap.theme-dark.is-readonly .xontable { color: #e6e6e6; }
36
- .xontable-wrap.theme-dark.is-readonly .xontable-cell { border-bottom-color: #2f333b; background: #1b1c1f; }
45
+ .xontable-wrap.theme-dark.is-readonly { border-color: var(--xontable-dark-readonly-border, #2c2f36); background: var(--xontable-dark-readonly-bg, #1b1c1f); }
46
+ .xontable-wrap.theme-dark.is-readonly .xontable { color: var(--xontable-dark-text, #e6e6e6); }
47
+ .xontable-wrap.theme-dark.is-readonly .xontable-cell { border-bottom-color: #2f333b; background: var(--xontable-dark-readonly-bg, #1b1c1f); }
37
48
  .xontable-wrap.theme-dark.is-readonly .xontable-head-cell,
38
49
  .xontable-wrap.theme-dark.is-readonly .xontable-rownum-cell,
39
- .xontable-wrap.theme-dark.is-readonly .xontable-group-cell { background: #23252b; color: #e6e6e6; }
40
- .xontable-wrap.theme-dark.is-readonly .xontable-row.is-zebra .xontable-cell { background: #202329; }
41
- .xontable-wrap.theme-dark.is-readonly .xontable-row.is-zebra .xontable-rownum-cell { background: #1e2025; }
42
- .xontable-wrap.theme-dark.is-readonly .xontable-cell.is-active { outline-color: #8ab4f8; }
50
+ .xontable-wrap.theme-dark.is-readonly .xontable-group-cell { background: var(--xontable-dark-readonly-head-bg, #23252b); color: var(--xontable-dark-text, #e6e6e6); }
51
+ .xontable-wrap.theme-dark.is-readonly .xontable-rownum-cell { background: var(--xontable-dark-readonly-rownum-bg, #23252b); }
52
+ .xontable-wrap.theme-dark.is-readonly .xontable-row.is-zebra .xontable-cell { background: var(--xontable-dark-readonly-zebra-bg, #202329); }
53
+ .xontable-wrap.theme-dark.is-readonly .xontable-row.is-zebra .xontable-rownum-cell { background: var(--xontable-dark-readonly-zebra-bg, #1e2025); }
54
+ .xontable-wrap.theme-dark.is-readonly .xontable-cell.is-active { outline-color: var(--xontable-dark-accent, #8ab4f8); }
package/dist/types.d.ts CHANGED
@@ -32,6 +32,31 @@ export type XOnTableProps<Row extends Record<string, any> = any> = {
32
32
  rowIdKey?: keyof Row;
33
33
  readOnly?: boolean;
34
34
  theme?: "light" | "dark";
35
+ showStatusBar?: boolean;
36
+ darkThemeColors?: Partial<{
37
+ bg: string;
38
+ border: string;
39
+ text: string;
40
+ cellBg: string;
41
+ headBg: string;
42
+ rownumBg: string;
43
+ groupBg: string;
44
+ zebraBg: string;
45
+ activeHeadBg: string;
46
+ range: string;
47
+ copy: string;
48
+ accent: string;
49
+ select: string;
50
+ invalidBg: string;
51
+ invalidBorder: string;
52
+ editorBg: string;
53
+ editorText: string;
54
+ readonlyBg: string;
55
+ readonlyBorder: string;
56
+ readonlyHeadBg: string;
57
+ readonlyRownumBg: string;
58
+ readonlyZebraBg: string;
59
+ }>;
35
60
  onChange?: (rows: Row[], meta: XOnTableMeta) => void;
36
61
  };
37
62
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE5E,MAAM,MAAM,SAAS,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClD,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI;IACjE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACtD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE5E,MAAM,MAAM,SAAS,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClD,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI;IACjE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;QACxB,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACtD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xontable",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,8 +26,7 @@
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": "^19.0.0",
29
- "react-dom": "^19.0.0",
30
- "lucide-react": "^0.468.0"
29
+ "react-dom": "^19.0.0"
31
30
  },
32
31
  "devDependencies": {
33
32
  "typescript": "^5.9.3"