@iyulab/flex-table 0.20.1 → 0.21.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 +19 -1
- package/dist/{flex-table-BzvFlHmD.js → flex-table-DNUTTZ1m.js} +6 -3
- package/dist/flex-table.js +1 -1
- package/dist/odata/index.js +46 -39
- package/dist/odata/use-odata-source.d.ts +23 -0
- package/dist/odata/use-odata-source.test.d.ts +1 -0
- package/dist/react.js +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -524,12 +524,30 @@ const source = useODataSource('/api/orders', {
|
|
|
524
524
|
|
|
525
525
|
`fetcher`/`onUnauthorized` should be stable references (e.g. wrap in `useCallback`) — they are intentionally excluded from the hook's internal effect dependencies to avoid refetch loops on every render.
|
|
526
526
|
|
|
527
|
+
The hook returns:
|
|
528
|
+
|
|
529
|
+
| Field | Description |
|
|
530
|
+
|---|---|
|
|
531
|
+
| `data` / `totalCount` | Current page rows and the server's total (`@odata.count`) |
|
|
532
|
+
| `loading` | A request is in flight |
|
|
533
|
+
| `error` | Message of the last failed request, or `null`. **Render this** — a failed request otherwise leaves the grid silently empty |
|
|
534
|
+
| `page` / `setPage` | Zero-based page index |
|
|
535
|
+
| `sortCriteria` / `onSortChange` | Bind `onSortChange` to the table's `sort-change` event |
|
|
536
|
+
| `search` / `setSearch` | Current search term and its setter (resets to page 0) |
|
|
537
|
+
| `refresh` | Re-run the current request |
|
|
538
|
+
|
|
539
|
+
#### Search semantics
|
|
540
|
+
|
|
541
|
+
`setSearch` takes **literal text, not an OData search expression**. Each whitespace-separated token is sent as a quoted `$search` phrase joined with `AND` — `red shirt` becomes `$search="red" AND "shirt"`, matching rows that contain both terms.
|
|
542
|
+
|
|
543
|
+
Terms are always quoted because OData 4.0 only allows letters in an unquoted `searchWord`, so `2026` or `ZT-E2E-A` would be rejected by servers that follow it (4.01 relaxed this, but [Microsoft.OData still lexes as 4.0](https://github.com/OData/odata.net/issues/2445)). Quoting keeps any term valid regardless of server version. Since a `$search` phrase cannot contain `"` and OData defines no escape for it, double quotes are stripped from the term.
|
|
544
|
+
|
|
527
545
|
## Development
|
|
528
546
|
|
|
529
547
|
```bash
|
|
530
548
|
npm install
|
|
531
549
|
npm run dev # Dev server with demo
|
|
532
|
-
npm test # Run tests
|
|
550
|
+
npm test # Run tests
|
|
533
551
|
npm run build # Build library
|
|
534
552
|
npm run lint # ESLint check
|
|
535
553
|
```
|
|
@@ -20,7 +20,10 @@ var s = t`
|
|
|
20
20
|
--ft-bool-color: #2196f3;
|
|
21
21
|
--ft-sort-indicator-color: #5f6368;
|
|
22
22
|
--ft-editor-bg: #fff;
|
|
23
|
-
--ft-
|
|
23
|
+
/* #999 was 2.85 against --ft-bg (#fff); AA needs 4.5. The dark value (#9aa0a6 on
|
|
24
|
+
#1e1e2e = 6.31) was already fine, so this was a light-only defect. #5f6368 is the
|
|
25
|
+
same grey the sort indicator uses, which keeps the palette to one family. */
|
|
26
|
+
--ft-empty-color: #5f6368;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
/* --- Dark Theme (auto via prefers-color-scheme) --- */
|
|
@@ -1222,7 +1225,7 @@ function k(e, t) {
|
|
|
1222
1225
|
//#endregion
|
|
1223
1226
|
//#region src/export/xlsx-writer.ts
|
|
1224
1227
|
var te = (() => {
|
|
1225
|
-
let e = new Uint32Array(256);
|
|
1228
|
+
let e = /* @__PURE__ */ new Uint32Array(256);
|
|
1226
1229
|
for (let t = 0; t < 256; t++) {
|
|
1227
1230
|
let n = t;
|
|
1228
1231
|
for (let e = 0; e < 8; e++) n = n & 1 ? 3988292384 ^ n >>> 1 : n >>> 1;
|
|
@@ -1480,7 +1483,7 @@ async function be(e) {
|
|
|
1480
1483
|
};
|
|
1481
1484
|
}
|
|
1482
1485
|
//#endregion
|
|
1483
|
-
//#region \0@oxc-project+runtime@0.
|
|
1486
|
+
//#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorate.js
|
|
1484
1487
|
function J(e, t, n, r) {
|
|
1485
1488
|
var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
|
|
1486
1489
|
if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
|
package/dist/flex-table.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, r, t as i } from "./flex-table-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./flex-table-DNUTTZ1m.js";
|
|
2
2
|
export { i as FlexTable, t as RowSelectionState, r as UndoStack, n as exportData, e as renderCell };
|
package/dist/odata/index.js
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
import { useCallback as e, useEffect as t, useRef as n, useState as r } from "react";
|
|
2
2
|
import i from "odata-query";
|
|
3
3
|
//#region src/odata/use-odata-source.ts
|
|
4
|
-
function a(
|
|
5
|
-
let
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
function a(e) {
|
|
5
|
+
let t = e.split(/\s+/).map((e) => e.replace(/"/g, "")).filter((e) => e.length > 0);
|
|
6
|
+
if (t.length !== 0) return t.map((e) => `"${e}"`).join(" AND ");
|
|
7
|
+
}
|
|
8
|
+
function o(o, c = {}) {
|
|
9
|
+
let { pageSize: l = 20, defaultOrderBy: u, fixedFilter: d, baseUrl: f, fetcher: p = fetch, onUnauthorized: m } = c, h = d ? JSON.stringify(d) : "", [g, _] = r([]), [v, y] = r(0), [b, x] = r(!1), [S, C] = r(null), [w, T] = r(0), [E, D] = r(() => u ? s(u) : []), [O, k] = r(""), [A, j] = r(0), M = n(null), N = e(() => {
|
|
10
|
+
j((e) => e + 1);
|
|
9
11
|
}, []), P = e((e) => {
|
|
12
|
+
k(e), T(0);
|
|
13
|
+
}, []), F = e((e) => {
|
|
10
14
|
let t = e.detail?.criteria;
|
|
11
|
-
t && (
|
|
15
|
+
t && (D(t), T(0));
|
|
12
16
|
}, []);
|
|
13
17
|
return t(() => {
|
|
14
|
-
|
|
18
|
+
M.current?.abort();
|
|
15
19
|
let e = new AbortController();
|
|
16
|
-
|
|
17
|
-
let t =
|
|
18
|
-
top:
|
|
19
|
-
skip:
|
|
20
|
+
M.current = e, x(!0), C(null);
|
|
21
|
+
let t = E.length > 0 ? E.map((e) => `${e.key} ${e.direction}`).join(", ") : u, n = {
|
|
22
|
+
top: l,
|
|
23
|
+
skip: w * l,
|
|
20
24
|
count: !0
|
|
21
25
|
};
|
|
22
|
-
t && (n.orderBy = t),
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
if (t && (n.orderBy = t), d && (n.filter = d), O) {
|
|
27
|
+
let e = a(O);
|
|
28
|
+
e && (n.search = e);
|
|
29
|
+
}
|
|
30
|
+
let r = i(n), s = `${f ?? window.location.origin}${o}${r}`;
|
|
31
|
+
return p(s, { signal: e.signal }).then(async (e) => {
|
|
25
32
|
if (!e.ok) {
|
|
26
|
-
(e.status === 401 || e.status === 403) &&
|
|
33
|
+
(e.status === 401 || e.status === 403) && m && m(e);
|
|
27
34
|
let t = await e.text().catch(() => ""), n = `요청 실패 (${e.status})`;
|
|
28
35
|
try {
|
|
29
36
|
let e = JSON.parse(t);
|
|
@@ -33,37 +40,37 @@ function a(a, s = {}) {
|
|
|
33
40
|
}
|
|
34
41
|
return e.json();
|
|
35
42
|
}).then((t) => {
|
|
36
|
-
e.signal.aborted || (
|
|
43
|
+
e.signal.aborted || (_(t.value ?? t), y(t["@odata.count"] ?? 0), C(null));
|
|
37
44
|
}).catch((t) => {
|
|
38
|
-
e.signal.aborted || t.name !== "AbortError" && (
|
|
45
|
+
e.signal.aborted || t.name !== "AbortError" && (C(t.message), _([]), y(0));
|
|
39
46
|
}).finally(() => {
|
|
40
|
-
e.signal.aborted ||
|
|
47
|
+
e.signal.aborted || x(!1);
|
|
41
48
|
}), () => e.abort();
|
|
42
49
|
}, [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
c,
|
|
46
|
-
T,
|
|
47
|
-
D,
|
|
48
|
-
m,
|
|
50
|
+
o,
|
|
51
|
+
w,
|
|
49
52
|
l,
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
E,
|
|
54
|
+
O,
|
|
55
|
+
h,
|
|
56
|
+
u,
|
|
57
|
+
A,
|
|
58
|
+
f
|
|
52
59
|
]), {
|
|
53
|
-
data:
|
|
54
|
-
totalCount:
|
|
55
|
-
loading:
|
|
56
|
-
error:
|
|
57
|
-
page:
|
|
58
|
-
setPage:
|
|
59
|
-
sortCriteria:
|
|
60
|
-
onSortChange:
|
|
61
|
-
search:
|
|
62
|
-
setSearch:
|
|
63
|
-
refresh:
|
|
60
|
+
data: g,
|
|
61
|
+
totalCount: v,
|
|
62
|
+
loading: b,
|
|
63
|
+
error: S,
|
|
64
|
+
page: w,
|
|
65
|
+
setPage: T,
|
|
66
|
+
sortCriteria: E,
|
|
67
|
+
onSortChange: F,
|
|
68
|
+
search: O,
|
|
69
|
+
setSearch: P,
|
|
70
|
+
refresh: N
|
|
64
71
|
};
|
|
65
72
|
}
|
|
66
|
-
function
|
|
73
|
+
function s(e) {
|
|
67
74
|
return e.split(",").map((e) => {
|
|
68
75
|
let t = e.trim().split(/\s+/);
|
|
69
76
|
return {
|
|
@@ -73,4 +80,4 @@ function o(e) {
|
|
|
73
80
|
});
|
|
74
81
|
}
|
|
75
82
|
//#endregion
|
|
76
|
-
export {
|
|
83
|
+
export { o as useODataSource };
|
|
@@ -1,6 +1,29 @@
|
|
|
1
|
+
import type { SortCriteria } from '../core/sorting.js';
|
|
1
2
|
import type { UseODataSourceOptions, UseODataSourceResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 검색어를 OData `$search` 표현식으로 인코딩한다 (`red shirt` → `"red" AND "shirt"`).
|
|
5
|
+
*
|
|
6
|
+
* 토큰을 인용하는 이유: OData 4.0의 `searchWord`는 문자(Unicode L/Nl)만 허용해
|
|
7
|
+
* `2026`·`ZT-E2E-A` 같은 검색어가 거부된다. 4.01이 숫자·하이픈을 허용하도록 완화했으나
|
|
8
|
+
* Microsoft.OData 렉서는 아직 4.0 규칙이다(odata.net#2445). `searchPhrase`는 두 버전
|
|
9
|
+
* 모두에서 적법하므로 서버 버전과 무관하게 안전하다.
|
|
10
|
+
*
|
|
11
|
+
* 통째로가 아니라 토큰별로 감싸는 이유: 인용 없는 다중 단어는 암묵 AND로 파싱되므로
|
|
12
|
+
* (`searchAndExpr = RWS [ 'AND' RWS ] searchExpr`), 전체를 한 phrase로 감싸면 연속
|
|
13
|
+
* 문자열 매칭으로 의미가 바뀐다. 토큰별 인용은 기존 의미론을 그대로 보존한다.
|
|
14
|
+
*
|
|
15
|
+
* `"`는 phrase 안에 넣을 수 없고 이스케이프 규칙도 없어(`qchar-no-AMP-DQUOTE`) 제거한다.
|
|
16
|
+
*
|
|
17
|
+
* @returns `$search` 표현식, 또는 유효 토큰이 없으면 `undefined`
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildSearchExpression(term: string): string | undefined;
|
|
2
20
|
/**
|
|
3
21
|
* OData v4 서버 사이드 데이터소스 React 훅.
|
|
4
22
|
* flex-table의 dataMode="server"와 함께 사용한다.
|
|
5
23
|
*/
|
|
6
24
|
export declare function useODataSource<T = Record<string, unknown>>(url: string, options?: UseODataSourceOptions): UseODataSourceResult<T>;
|
|
25
|
+
/**
|
|
26
|
+
* `$orderby` 문자열을 정렬 기준 배열로 파싱한다 (`'a asc, b desc'`).
|
|
27
|
+
* 방향이 생략되거나 `desc`가 아니면 `asc`로 본다(OData 기본값).
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseOrderBy(orderBy: string): SortCriteria[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/react.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/flex-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "A minimalist, input-centric data grid web component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/flex-table.js",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"lit": "^3.3.3",
|
|
56
|
-
"odata-query": "^8.0
|
|
56
|
+
"odata-query": "^8.1.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@lit/react": "^1.0.0",
|
|
@@ -69,17 +69,17 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@lit/react": "^1.0.8",
|
|
72
|
-
"@types/react": "^19.2.
|
|
73
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
74
|
-
"@typescript-eslint/parser": "^8.
|
|
72
|
+
"@types/react": "^19.2.17",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
74
|
+
"@typescript-eslint/parser": "^8.64.0",
|
|
75
75
|
"eslint": "^10.1.0",
|
|
76
76
|
"eslint-plugin-lit": "^2.3.1",
|
|
77
77
|
"jsdom": "^29.1.1",
|
|
78
78
|
"loupe": "^3.2.1",
|
|
79
|
-
"react": "^19.2.
|
|
79
|
+
"react": "^19.2.7",
|
|
80
80
|
"strip-literal": "^3.1.0",
|
|
81
81
|
"typescript": "~5.7.0",
|
|
82
|
-
"vite": "^8.
|
|
83
|
-
"vitest": "^4.1.
|
|
82
|
+
"vite": "^8.1.4",
|
|
83
|
+
"vitest": "^4.1.10"
|
|
84
84
|
}
|
|
85
85
|
}
|