@iyulab/flex-table 0.45.0 → 0.47.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/CHANGELOG.md +50 -0
- package/README.md +9 -4
- package/dist/array/types.d.ts +2 -1
- package/dist/core/source-error.d.ts +35 -0
- package/dist/{flex-table-Bv41ls8b.js → flex-table-h2noRChg.js} +250 -159
- package/dist/flex-table.d.ts +13 -0
- package/dist/flex-table.js +1 -1
- package/dist/odata/types.d.ts +6 -1
- package/dist/react.d.ts +1 -0
- package/dist/react.js +82 -55
- package/package.json +3 -2
- package/skills/iyulab-flex-table/SKILL.md +2 -2
- package/skills/iyulab-flex-table/references/react.md +2 -2
package/dist/flex-table.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LitElement, type PropertyValues } from 'lit';
|
|
2
|
+
import '@iyulab/components/dist/components/date-picker/UDatePicker.js';
|
|
2
3
|
import type { ExportFormat } from './export/export.js';
|
|
3
4
|
import type { CellPosition, CellRange } from './core/selection.js';
|
|
4
5
|
import type { SortCriteria } from './core/sorting.js';
|
|
@@ -395,6 +396,12 @@ export declare class FlexTable extends LitElement {
|
|
|
395
396
|
private _handleCopy;
|
|
396
397
|
private _handlePaste;
|
|
397
398
|
private _readClipboardText;
|
|
399
|
+
/**
|
|
400
|
+
* After a paste, the cells it wrote are the selection — as in spreadsheets — so the reader sees
|
|
401
|
+
* what changed and can copy, clear or undo it as one block. The active cell stays where the
|
|
402
|
+
* paste began (the same shape as selecting a column: range to the far corner, focus at the start).
|
|
403
|
+
*/
|
|
404
|
+
private _selectPastedRange;
|
|
398
405
|
private _expandRowsForPaste;
|
|
399
406
|
private _applyPasteData;
|
|
400
407
|
private _handleFillDown;
|
|
@@ -485,6 +492,12 @@ export declare class FlexTable extends LitElement {
|
|
|
485
492
|
private _numberFilterState;
|
|
486
493
|
private _dateFilterState;
|
|
487
494
|
private _emptyFilterState;
|
|
495
|
+
/**
|
|
496
|
+
* From/to bounds as two `u-date-picker`s — the cell editor's control, for the same reason: the native
|
|
497
|
+
* date inputs show the browser's UI language (`10/02/2026` in an English browser) while the table
|
|
498
|
+
* shows ISO. Two pickers, not one `u-date-range-picker`: either bound may be left open ("from this
|
|
499
|
+
* day on"), which a range value cannot say. Each bound's calendar stops at the other bound.
|
|
500
|
+
*/
|
|
488
501
|
private _renderDateFilter;
|
|
489
502
|
private _applyDateFilter;
|
|
490
503
|
private _renderBooleanFilter;
|
package/dist/flex-table.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-
|
|
1
|
+
import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./flex-table-h2noRChg.js";
|
|
2
2
|
export { o as FlexTable, e as RowSelectionState, t as UndoStack, n as effectiveAlign, i as exportData, r as flexTableLocale, a as renderCell };
|
package/dist/odata/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SortCriteria } from '../core/sorting.js';
|
|
2
|
+
import type { SourceError } from '../core/source-error.js';
|
|
2
3
|
export interface UseODataSourceOptions {
|
|
3
4
|
pageSize?: number;
|
|
4
5
|
defaultOrderBy?: string;
|
|
@@ -43,7 +44,11 @@ export interface UseODataSourceResult<T> {
|
|
|
43
44
|
data: T[];
|
|
44
45
|
totalCount: number;
|
|
45
46
|
loading: boolean;
|
|
46
|
-
|
|
47
|
+
/**
|
|
48
|
+
* 마지막 요청의 실패, 없으면 `null`. 화면에는 `error.message` 를 그린다. «어떤 실패인가» 는
|
|
49
|
+
* `status`(HTTP) · `code`(서버의 거절 코드) · `details` 로 가른다 — 401 은 `onUnauthorized` 도 부른다.
|
|
50
|
+
*/
|
|
51
|
+
error: SourceError | null;
|
|
47
52
|
page: number;
|
|
48
53
|
setPage: (page: number) => void;
|
|
49
54
|
sortCriteria: SortCriteria[];
|
package/dist/react.d.ts
CHANGED
|
@@ -69,3 +69,4 @@ export { useODataSource } from './odata/use-odata-source.js';
|
|
|
69
69
|
export type { UseODataSourceOptions, UseODataSourceResult } from './odata/types.js';
|
|
70
70
|
export { useArraySource } from './array/use-array-source.js';
|
|
71
71
|
export type { UseArraySourceOptions, UseArraySourceResult } from './array/types.js';
|
|
72
|
+
export type { SourceError, SourceErrorDetail } from './core/source-error.js';
|
package/dist/react.js
CHANGED
|
@@ -1,47 +1,74 @@
|
|
|
1
|
-
import { t as e } from "./flex-table-
|
|
1
|
+
import { t as e } from "./flex-table-h2noRChg.js";
|
|
2
2
|
import { i as t, t as n } from "./query-Dw4iO0ir.js";
|
|
3
3
|
import { t as r } from "./view-DiLr-TdY.js";
|
|
4
4
|
import i, { useCallback as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
|
|
5
5
|
import { createComponent as u } from "@lit/react";
|
|
6
|
+
//#region src/core/source-error.ts
|
|
7
|
+
async function d(e) {
|
|
8
|
+
let t = await e.text().catch(() => ""), n = {
|
|
9
|
+
message: `Request failed (${e.status})`,
|
|
10
|
+
status: e.status
|
|
11
|
+
};
|
|
12
|
+
if (!t) return n;
|
|
13
|
+
let r = t;
|
|
14
|
+
try {
|
|
15
|
+
r = JSON.parse(t);
|
|
16
|
+
} catch {}
|
|
17
|
+
if (n.body = r, !r || typeof r != "object") return n;
|
|
18
|
+
let i = r, a = i.error && typeof i.error == "object" ? i.error : i;
|
|
19
|
+
typeof a.message == "string" && a.message ? n.message = a.message : typeof i.message == "string" && i.message && (n.message = i.message), typeof a.code == "string" && a.code && (n.code = a.code);
|
|
20
|
+
let o = f(a.details);
|
|
21
|
+
return o && (n.details = o), n;
|
|
22
|
+
}
|
|
23
|
+
function f(e) {
|
|
24
|
+
if (!Array.isArray(e)) return;
|
|
25
|
+
let t = e.filter((e) => {
|
|
26
|
+
if (!e || typeof e != "object") return !1;
|
|
27
|
+
let t = e;
|
|
28
|
+
return typeof t.code == "string" && typeof t.message == "string";
|
|
29
|
+
});
|
|
30
|
+
return t.length > 0 ? t : void 0;
|
|
31
|
+
}
|
|
32
|
+
var p = class extends Error {
|
|
33
|
+
constructor(e) {
|
|
34
|
+
super(e.message), this.name = "SourceRequestError", this.failure = e;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function m(e) {
|
|
38
|
+
return e instanceof p ? e.failure : e instanceof Error ? { message: e.message } : { message: String(e) };
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
6
41
|
//#region src/odata/use-odata-source.ts
|
|
7
|
-
function
|
|
8
|
-
let { pageSize: i = 20, defaultOrderBy: s, initialPage: u = 0, initialSearch:
|
|
42
|
+
function h(e, r = {}) {
|
|
43
|
+
let { pageSize: i = 20, defaultOrderBy: s, initialPage: u = 0, initialSearch: f = "", initialSort: h, fixedFilter: g, baseUrl: _, fetcher: v = fetch, onUnauthorized: y, enabled: b = !0 } = r, x = g ? JSON.stringify(g) : "", [S, C] = l([]), [w, T] = l(0), [E, D] = l(!1), [O, k] = l(null), A = t({
|
|
9
44
|
defaultOrderBy: s,
|
|
10
45
|
initialPage: u,
|
|
11
|
-
initialSearch:
|
|
12
|
-
initialSort:
|
|
13
|
-
}), [
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
}, []),
|
|
18
|
-
|
|
19
|
-
}, []),
|
|
46
|
+
initialSearch: f,
|
|
47
|
+
initialSort: h
|
|
48
|
+
}), [j, M] = l(A.page), [N, P] = l(A.sortCriteria), [F, I] = l(A.search), [L, R] = l(0), [z, B] = l(x);
|
|
49
|
+
x !== z && (B(x), M(0));
|
|
50
|
+
let V = c(null), H = a(() => {
|
|
51
|
+
R((e) => e + 1);
|
|
52
|
+
}, []), U = a((e) => {
|
|
53
|
+
I(e), M(0);
|
|
54
|
+
}, []), W = a((e) => {
|
|
20
55
|
let t = e.detail?.criteria;
|
|
21
|
-
t && (
|
|
56
|
+
t && (P(t), M(0));
|
|
22
57
|
}, []);
|
|
23
58
|
return o(() => {
|
|
24
|
-
if (
|
|
59
|
+
if (V.current?.abort(), !b) return;
|
|
25
60
|
let t = new AbortController();
|
|
26
|
-
|
|
61
|
+
V.current = t, D(!0), k(null);
|
|
27
62
|
let r = n({
|
|
28
|
-
page:
|
|
63
|
+
page: j,
|
|
29
64
|
pageSize: i,
|
|
30
|
-
sortCriteria:
|
|
65
|
+
sortCriteria: N,
|
|
31
66
|
defaultOrderBy: s,
|
|
32
|
-
search:
|
|
33
|
-
fixedFilter:
|
|
34
|
-
}), a = `${
|
|
35
|
-
let n = await
|
|
36
|
-
if (!n.ok)
|
|
37
|
-
n.status === 401 && g && g(n);
|
|
38
|
-
let e = await n.text().catch(() => ""), t = `Request failed (${n.status})`;
|
|
39
|
-
try {
|
|
40
|
-
let n = JSON.parse(e);
|
|
41
|
-
t = n?.error?.message ?? n?.message ?? t;
|
|
42
|
-
} catch {}
|
|
43
|
-
throw Error(t);
|
|
44
|
-
}
|
|
67
|
+
search: F,
|
|
68
|
+
fixedFilter: g
|
|
69
|
+
}), a = `${_ ?? window.location.origin}${e}${r}`, o = async (e) => {
|
|
70
|
+
let n = await v(e, { signal: t.signal });
|
|
71
|
+
if (!n.ok) throw n.status === 401 && y && y(n), new p(await d(n));
|
|
45
72
|
return n.json();
|
|
46
73
|
};
|
|
47
74
|
return (async () => {
|
|
@@ -61,42 +88,42 @@ function d(e, r = {}) {
|
|
|
61
88
|
})().then(({ count: e, rows: n }) => {
|
|
62
89
|
if (t.signal.aborted) return;
|
|
63
90
|
let r = e ?? 0;
|
|
64
|
-
|
|
91
|
+
C(n), T(r), k(null);
|
|
65
92
|
let a = r === 0 ? 0 : Math.ceil(r / i) - 1;
|
|
66
|
-
|
|
93
|
+
j > a && M(a);
|
|
67
94
|
}).catch((e) => {
|
|
68
|
-
t.signal.aborted || e.name !== "AbortError" && (
|
|
95
|
+
t.signal.aborted || e.name !== "AbortError" && (k(m(e)), C([]), T(0));
|
|
69
96
|
}).finally(() => {
|
|
70
|
-
t.signal.aborted ||
|
|
97
|
+
t.signal.aborted || D(!1);
|
|
71
98
|
}), () => t.abort();
|
|
72
99
|
}, [
|
|
73
|
-
|
|
100
|
+
b,
|
|
74
101
|
e,
|
|
75
|
-
|
|
102
|
+
j,
|
|
76
103
|
i,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
104
|
+
N,
|
|
105
|
+
F,
|
|
106
|
+
x,
|
|
80
107
|
s,
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
L,
|
|
109
|
+
_
|
|
83
110
|
]), {
|
|
84
|
-
data:
|
|
85
|
-
totalCount:
|
|
86
|
-
loading:
|
|
87
|
-
error:
|
|
88
|
-
page:
|
|
89
|
-
setPage:
|
|
90
|
-
sortCriteria:
|
|
91
|
-
onSortChange:
|
|
92
|
-
search:
|
|
93
|
-
setSearch:
|
|
94
|
-
refresh:
|
|
111
|
+
data: S,
|
|
112
|
+
totalCount: w,
|
|
113
|
+
loading: E || !b,
|
|
114
|
+
error: O,
|
|
115
|
+
page: j,
|
|
116
|
+
setPage: M,
|
|
117
|
+
sortCriteria: N,
|
|
118
|
+
onSortChange: W,
|
|
119
|
+
search: F,
|
|
120
|
+
setSearch: U,
|
|
121
|
+
refresh: H
|
|
95
122
|
};
|
|
96
123
|
}
|
|
97
124
|
//#endregion
|
|
98
125
|
//#region src/array/use-array-source.ts
|
|
99
|
-
function
|
|
126
|
+
function g(e, n = {}) {
|
|
100
127
|
let { pageSize: i = 20, defaultOrderBy: o, initialPage: c = 0, initialSearch: u = "", initialSort: d, columns: f, searchFields: p } = n, m = t({
|
|
101
128
|
defaultOrderBy: o,
|
|
102
129
|
initialPage: c,
|
|
@@ -137,7 +164,7 @@ function f(e, n = {}) {
|
|
|
137
164
|
refresh: C
|
|
138
165
|
};
|
|
139
166
|
}
|
|
140
|
-
var
|
|
167
|
+
var _ = u({
|
|
141
168
|
tagName: "flex-table",
|
|
142
169
|
elementClass: e,
|
|
143
170
|
react: i,
|
|
@@ -176,4 +203,4 @@ var p = u({
|
|
|
176
203
|
}
|
|
177
204
|
});
|
|
178
205
|
//#endregion
|
|
179
|
-
export {
|
|
206
|
+
export { _ as FlexTableReact, g as useArraySource, h as useODataSource };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/flex-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"description": "A minimalist, input-centric data grid web component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/flex-table.js",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"build": "npm run typecheck && vite build && tsc --emitDeclarationOnly",
|
|
46
46
|
"build:demo": "vite build --config vite.config.demo.ts",
|
|
47
47
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
48
|
+
"pretest": "node -e \"if(require('fs').existsSync('../../scripts/sibling-dist-check.js'))require('child_process').execFileSync('node',['../../scripts/sibling-dist-check.js'],{stdio:'inherit'})\"",
|
|
48
49
|
"test": "vitest run",
|
|
49
50
|
"test:watch": "vitest",
|
|
50
51
|
"lint": "eslint src/",
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
"odata-query": "^8.1.0"
|
|
73
74
|
},
|
|
74
75
|
"peerDependencies": {
|
|
75
|
-
"@iyulab/components": ">=
|
|
76
|
+
"@iyulab/components": ">=2.0.1",
|
|
76
77
|
"@lit/react": "^1.0.8",
|
|
77
78
|
"react": "^18.0.0 || ^19.0.0"
|
|
78
79
|
},
|
|
@@ -66,7 +66,7 @@ as `text`). See `references/api.md` for every field.
|
|
|
66
66
|
- `editor` returns a Lit template containing an element with class `ft-editor`; its `.value` is committed.
|
|
67
67
|
- `validator` returns `null` when valid, or an error message (the edit is rejected and `validation-error` fires).
|
|
68
68
|
- `number` cells, the number filter and paste read numbers in the active `Locale` (`1,5` is 1.5 on a comma-decimal page); an edit that is not a number is rejected with `validation-error`.
|
|
69
|
-
- `date` cells edit
|
|
69
|
+
- `date` cells edit with `u-date-picker` (a `YYYY-MM-DD` text box in every browser language plus a calendar) (also `20261002`, `2026/10/2`, `10-02`); pasted dates are normalized to ISO. `datetime` cells edit as `YYYY-MM-DD HH:mm` (local) and store `YYYY-MM-DDTHH:mm`.
|
|
70
70
|
- `conditionalRules` applies `{ when, style }` rules in order; later matches override earlier ones.
|
|
71
71
|
- `pinned: 'left' | 'right'` freezes a column during horizontal scroll.
|
|
72
72
|
|
|
@@ -127,7 +127,7 @@ Server paging from OData or an in-memory array uses the same binding — see `re
|
|
|
127
127
|
- Styles for elements returned by `render` must be passed through the `stylesheets` property
|
|
128
128
|
(constructable `CSSStyleSheet[]`); document CSS does not cross the shadow boundary.
|
|
129
129
|
- `useODataSource` `fetcher` / `onUnauthorized` must be stable references (`useCallback`).
|
|
130
|
-
- Always render `error` from `useODataSource`; a failed request otherwise leaves the grid empty.
|
|
130
|
+
- Always render `error.message` from `useODataSource`; a failed request otherwise leaves the grid empty. Branch on `error.status` / `error.code` rather than parsing the message.
|
|
131
131
|
|
|
132
132
|
## References
|
|
133
133
|
|
|
@@ -73,7 +73,7 @@ function Orders() {
|
|
|
73
73
|
return (
|
|
74
74
|
<>
|
|
75
75
|
<input value={source.search} onChange={(e) => source.setSearch(e.target.value)} />
|
|
76
|
-
{source.error && <p role="alert">{source.error}</p>}
|
|
76
|
+
{source.error && <p role="alert">{source.error.message}</p>}
|
|
77
77
|
<FlexTableReact<Order>
|
|
78
78
|
dataMode="server"
|
|
79
79
|
columns={columns}
|
|
@@ -111,7 +111,7 @@ function Orders() {
|
|
|
111
111
|
|---|---|
|
|
112
112
|
| `data`, `totalCount` | Current page and `@odata.count`; `@odata.nextLink` is followed to fill a page |
|
|
113
113
|
| `loading` | Request in flight |
|
|
114
|
-
| `error` | `
|
|
114
|
+
| `error` | `SourceError \| null` — `{ message, status?, code?, details?, body? }`; render `error.message`, branch on `status` / `code` (OData `error.code`) |
|
|
115
115
|
| `page`, `setPage` | Zero-based |
|
|
116
116
|
| `sortCriteria`, `onSortChange` | Pass `onSortChange` to the table's `sort-change` (resets to page 0) |
|
|
117
117
|
| `search`, `setSearch` | Literal text (resets to page 0) |
|