@iyulab/flex-table 0.38.2 → 0.39.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.39.0] - 2026-09-27
4
+
5
+ ### Added
6
+
7
+ - **`enabled` option on `useODataSource`** (default `true`). While `false` the hook makes no
8
+ request and reports `loading: true`; the first request goes out when it turns `true`, with that
9
+ render's conditions. For lists whose query depends on a value that arrives asynchronously (a
10
+ server-decided default filter, the current user, a selected parent): without it the first render
11
+ sent a wasted request with the wrong conditions — often the heaviest one, unfiltered — before the
12
+ real one. Turning it `false` mid-request cancels the request; `refresh()` does nothing while
13
+ disabled.
14
+
3
15
  ## [0.38.2] - 2026-09-24
4
16
 
5
17
  ### Fixed
package/README.md CHANGED
@@ -688,6 +688,7 @@ const source = useODataSource('/api/orders', {
688
688
  | `baseUrl` | `window.location.origin` | Override the request origin (proxy/BFF setups) |
689
689
  | `fetcher` | global `fetch` | Custom transport — pass a wrapper that injects auth headers |
690
690
  | `onUnauthorized` | — | Called on `401`/`403` responses, before the generic error is set |
691
+ | `enabled` | `true` | While `false`, no request is made and `loading` stays `true` — see below |
691
692
 
692
693
  `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.
693
694
 
@@ -704,6 +705,22 @@ rows, a `refresh` landing after a change), and holding the old `$skip` would lea
704
705
  table next to a non-zero total. Reaching that state costs one extra request, only in that
705
706
  case; the page only ever moves down, so it cannot loop.
706
707
 
708
+ Set `enabled: false` while the query depends on a value that has not arrived yet — a default
709
+ filter the server decides, the current user, a selected parent record. Without it, the first
710
+ render requests with the wrong conditions (often the heaviest query: no filter) and then again
711
+ once the value arrives. The first request goes out the moment `enabled` turns `true`, with the
712
+ conditions of that render; until then `loading` is `true`, so the table shows loading rather
713
+ than "no data". Turning it `false` mid-request cancels that request, and `refresh()` does nothing
714
+ while disabled.
715
+
716
+ ```tsx
717
+ const season = useDefaultSeason(); // resolves asynchronously
718
+ const source = useODataSource('/api/orders', {
719
+ fixedFilter: season ? { Season: season } : undefined,
720
+ enabled: season !== undefined,
721
+ });
722
+ ```
723
+
707
724
  The three `initial*` options are read **on the first render only** (the same contract
708
725
  `defaultOrderBy` has always had); use `setPage`/`setSearch` to move afterwards. Reach for
709
726
  them when a list has to come back the way the user left it — going to a detail screen and
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "../sorting-CjfjRxwL.js";
2
- import { r as t } from "../use-odata-source-l9dVCi9l.js";
2
+ import { r as t } from "../use-odata-source-BiZrIEVV.js";
3
3
  import { useCallback as n, useMemo as r, useState as i } from "react";
4
4
  //#region src/array/use-array-source.ts
5
5
  function a(t, { search: n, sortCriteria: r, page: i, pageSize: a, columns: o, searchFields: s }) {
@@ -1,2 +1,2 @@
1
- import { i as e, n as t, t as n } from "../use-odata-source-l9dVCi9l.js";
1
+ import { i as e, n as t, t as n } from "../use-odata-source-BiZrIEVV.js";
2
2
  export { n as buildSearchExpression, t as parseOrderBy, e as useODataSource };
@@ -25,6 +25,16 @@ export interface UseODataSourceOptions {
25
25
  fetcher?: (input: string, init: RequestInit) => Promise<Response>;
26
26
  /** 응답이 401/403일 때 호출(세션 만료 리다이렉트 등). 호출 후에도 기존 에러 처리는 계속 진행된다. */
27
27
  onUnauthorized?: (response: Response) => void;
28
+ /**
29
+ * `false` 인 동안 요청하지 않는다. 기본값 `true`.
30
+ *
31
+ * 조회 조건이 다른 비동기 값(현재 사용자·기본 필터·선택된 부모 레코드)에 달린 목록에서
32
+ * 그 값이 오기 전에 «틀린 조건의 첫 조회» 가 나가지 않게 한다. `false → true` 가 되는 순간
33
+ * 첫 조회가 나가고, 그 사이 `loading` 은 `true` 다(표가 «데이터 없음» 이 아니라 «불러오는 중»
34
+ * 을 보인다). 조회 중에 `false` 가 되면 진행 중 요청을 취소한다. `false` 인 동안의 `refresh()` 는
35
+ * 아무 일도 하지 않는다 — 켜지는 순간의 조회가 이미 최신 조건이다.
36
+ */
37
+ enabled?: boolean;
28
38
  }
29
39
  export interface UseODataSourceResult<T> {
30
40
  data: T[];
@@ -14,32 +14,32 @@ function o(e) {
14
14
  };
15
15
  }
16
16
  function s(s, c = {}) {
17
- let { pageSize: l = 20, defaultOrderBy: u, initialPage: d = 0, initialSearch: f = "", initialSort: p, fixedFilter: m, baseUrl: h, fetcher: g = fetch, onUnauthorized: _ } = c, v = m ? JSON.stringify(m) : "", [y, b] = r([]), [x, S] = r(0), [C, w] = r(!1), [T, E] = r(null), D = o({
17
+ let { pageSize: l = 20, defaultOrderBy: u, initialPage: d = 0, initialSearch: f = "", initialSort: p, fixedFilter: m, baseUrl: h, fetcher: g = fetch, onUnauthorized: _, enabled: v = !0 } = c, y = m ? JSON.stringify(m) : "", [b, x] = r([]), [S, C] = r(0), [w, T] = r(!1), [E, D] = r(null), O = o({
18
18
  defaultOrderBy: u,
19
19
  initialPage: d,
20
20
  initialSearch: f,
21
21
  initialSort: p
22
- }), [O, k] = r(D.page), [A, j] = r(D.sortCriteria), [M, N] = r(D.search), [P, F] = r(0), [I, L] = r(v);
23
- v !== I && (L(v), k(0));
24
- let R = n(null), z = e(() => {
25
- F((e) => e + 1);
26
- }, []), B = e((e) => {
27
- N(e), k(0);
22
+ }), [k, A] = r(O.page), [j, M] = r(O.sortCriteria), [N, P] = r(O.search), [F, I] = r(0), [L, R] = r(y);
23
+ y !== L && (R(y), A(0));
24
+ let z = n(null), B = e(() => {
25
+ I((e) => e + 1);
28
26
  }, []), V = e((e) => {
27
+ P(e), A(0);
28
+ }, []), H = e((e) => {
29
29
  let t = e.detail?.criteria;
30
- t && (j(t), k(0));
30
+ t && (M(t), A(0));
31
31
  }, []);
32
32
  return t(() => {
33
- R.current?.abort();
33
+ if (z.current?.abort(), !v) return;
34
34
  let e = new AbortController();
35
- R.current = e, w(!0), E(null);
36
- let t = A.length > 0 ? A.map((e) => `${e.key} ${e.direction}`).join(", ") : u, n = {
35
+ z.current = e, T(!0), D(null);
36
+ let t = j.length > 0 ? j.map((e) => `${e.key} ${e.direction}`).join(", ") : u, n = {
37
37
  top: l,
38
- skip: O * l,
38
+ skip: k * l,
39
39
  count: !0
40
40
  };
41
- if (t && (n.orderBy = t), m && (n.filter = m), M) {
42
- let e = a(M);
41
+ if (t && (n.orderBy = t), m && (n.filter = m), N) {
42
+ let e = a(N);
43
43
  e && (n.search = e);
44
44
  }
45
45
  let r = i(n), o = `${h ?? window.location.origin}${s}${r}`, c = async (t) => {
@@ -72,36 +72,37 @@ function s(s, c = {}) {
72
72
  })().then(({ count: t, rows: n }) => {
73
73
  if (e.signal.aborted) return;
74
74
  let r = t ?? 0;
75
- b(n), S(r), E(null);
75
+ x(n), C(r), D(null);
76
76
  let i = r === 0 ? 0 : Math.ceil(r / l) - 1;
77
- O > i && k(i);
77
+ k > i && A(i);
78
78
  }).catch((t) => {
79
- e.signal.aborted || t.name !== "AbortError" && (E(t.message), b([]), S(0));
79
+ e.signal.aborted || t.name !== "AbortError" && (D(t.message), x([]), C(0));
80
80
  }).finally(() => {
81
- e.signal.aborted || w(!1);
81
+ e.signal.aborted || T(!1);
82
82
  }), () => e.abort();
83
83
  }, [
84
+ v,
84
85
  s,
85
- O,
86
+ k,
86
87
  l,
87
- A,
88
- M,
89
- v,
88
+ j,
89
+ N,
90
+ y,
90
91
  u,
91
- P,
92
+ F,
92
93
  h
93
94
  ]), {
94
- data: y,
95
- totalCount: x,
96
- loading: C,
97
- error: T,
98
- page: O,
99
- setPage: k,
100
- sortCriteria: A,
101
- onSortChange: V,
102
- search: M,
103
- setSearch: B,
104
- refresh: z
95
+ data: b,
96
+ totalCount: S,
97
+ loading: w || !v,
98
+ error: E,
99
+ page: k,
100
+ setPage: A,
101
+ sortCriteria: j,
102
+ onSortChange: H,
103
+ search: N,
104
+ setSearch: V,
105
+ refresh: B
105
106
  };
106
107
  }
107
108
  function c(e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/flex-table",
3
- "version": "0.38.2",
3
+ "version": "0.39.0",
4
4
  "description": "A minimalist, input-centric data grid web component",
5
5
  "type": "module",
6
6
  "main": "./dist/flex-table.js",