@iyulab/flex-table 0.30.0 → 0.31.2

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,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.2] - 2026-09-02
4
+
5
+ ### Fixed
6
+
7
+ - **`ColumnDefinition`'s README reference was missing three real, shipped
8
+ fields**: `headerAlign` (per-column header alignment, added in a later
9
+ commit than the rest of the snippet), `options`/`autocomplete` (select-type
10
+ columns and autocomplete editing, since `0.13.0`). The `select` value of
11
+ `ColumnType` was also missing from its inline comment. None of these had
12
+ any mention anywhere in the README — a consumer had no way to discover
13
+ select columns or autocomplete existed short of reading the source.
14
+
15
+ ## [0.31.1] - 2026-09-01
16
+
17
+ ### Fixed
18
+
19
+ - **`0.31.0`'s release pipeline never actually published to npm** — the repo's
20
+ `preversion` guard script assumed a monorepo layout this repo doesn't have,
21
+ so the workflow's own version-sync step failed before publishing. No package
22
+ code changed; this release exists to get `0.31.0`'s content onto the
23
+ registry with a working pipeline.
24
+
25
+ ## [0.31.0] - 2026-08-31
26
+
27
+ ### Added
28
+
29
+ - **`useArraySource`, a client-array sibling to `useODataSource`.** `useODataSource`
30
+ is server-mode only, so a lookup table needing a client-side join (a display
31
+ field living on a different endpoint than the row) had no library-level path
32
+ and required reimplementing filter/sort/pagination by hand. `useArraySource`
33
+ returns the exact same shape (`data`/`totalCount`/`loading`/`error`/`page`/
34
+ `setPage`/`sortCriteria`/`onSortChange`/`search`/`setSearch`/`refresh`) driven
35
+ off a local array instead of a fetch, so the same `<FlexTableReact
36
+ dataMode="server" ...>` binding code works with either source. Sorting reuses
37
+ the grid's own `computeSortedIndices` for value-aware (not string) comparison.
38
+ New `./array` subpath export, mirroring `./odata`'s shape.
39
+
3
40
  ## [0.30.0] - 2026-08-28
4
41
 
5
42
  ### Added
package/README.md CHANGED
@@ -107,13 +107,16 @@ npm install @iyulab/flex-table
107
107
  interface ColumnDefinition {
108
108
  key: string; // Unique key matching data property names
109
109
  header: string; // Display header text
110
- type?: ColumnType; // 'text' | 'number' | 'boolean' | 'date' | 'datetime'
111
- width?: number; // Column width in pixels (default: 120)
110
+ type?: ColumnType; // 'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'select' (any other string falls back to 'text')
111
+ width?: number; // Column width in pixels (default: auto)
112
112
  minWidth?: number; // Minimum width in pixels (default: 40, enforced in rendering)
113
113
  hidden?: boolean; // Hide column from view
114
114
  sortable?: boolean; // Enable sorting (default: true)
115
+ headerAlign?: 'start' | 'center' | 'end'; // Header label alignment (default: 'start'), independent of cell content alignment
115
116
  editable?: boolean; // Per-column edit control (follows global editable)
116
117
  pinned?: 'left' | 'right'; // Freeze column during horizontal scroll
118
+ options?: string[] | SelectOption[]; // Allowed values for type: 'select' (SelectOption = { label, value })
119
+ autocomplete?: boolean | 'strict'; // Suggest existing column values while editing; 'strict' rejects values not in the list
117
120
  format?: string | ((value, row, col) => string); // Display format, see "format vs renderer" below
118
121
  renderer?: CellRenderer; // Custom cell render: (value, row, col) => TemplateResult | string
119
122
  editor?: CellEditor; // Custom cell editor: (value, row, col) => TemplateResult
@@ -616,6 +619,43 @@ buildSearchExpression(''); // undefined
616
619
  parseOrderBy('name desc'); // [{ key: 'name', direction: 'desc' }]
617
620
  ```
618
621
 
622
+ ### Array Source Hook (React)
623
+
624
+ `useArraySource(data, options)` runs search/sort/pagination over an in-memory array and
625
+ returns the **same shape** as `useODataSource` — `data`/`totalCount`/`loading`/`error`/
626
+ `page`/`setPage`/`sortCriteria`/`onSortChange`/`search`/`setSearch`/`refresh` — so the
627
+ same `<FlexTableReact dataMode="server" ...>` binding code works with either source.
628
+
629
+ Reach for this when the rows come from a client-side join a server query can't express —
630
+ e.g. a lookup table whose display name lives on a different endpoint than the row itself,
631
+ so search/sort has to run after the join, in memory:
632
+
633
+ ```tsx
634
+ import { useArraySource } from '@iyulab/flex-table/array';
635
+
636
+ const joined = useMemo(
637
+ () => seasonPrices.map(p => ({ ...p, productName: productsById[p.productId]?.name ?? '' })),
638
+ [seasonPrices, productsById]
639
+ );
640
+
641
+ const source = useArraySource(joined, {
642
+ pageSize: 20,
643
+ columns, // pass the same ColumnDefinition[] used by <FlexTableReact> for value-aware sort
644
+ });
645
+ ```
646
+
647
+ | Option | Default | Description |
648
+ |---|---|---|
649
+ | `pageSize` | `20` | Rows per page |
650
+ | `defaultOrderBy` | — | Initial sort (e.g. `'name asc'`), same syntax as `useODataSource` |
651
+ | `columns` | — | `ColumnDefinition[]` — enables value-aware sort (numbers/dates/booleans compared by value, not as text). Omit and every column sorts as text. |
652
+ | `searchFields` | all values | `(row) => value[]` — narrows or widens what free-text search matches; the default searches every property on the row, including client-joined ones |
653
+
654
+ The returned fields mean the same thing as `useODataSource`'s, except `totalCount` is the
655
+ count after search (not a server-reported total), and `loading`/`error` are always
656
+ `false`/`null` — there's no request to fail. `refresh` is a no-op kept only so a
657
+ "refresh" button wired unconditionally against either hook doesn't need a branch.
658
+
619
659
  ## Development
620
660
 
621
661
  ```bash
@@ -0,0 +1,3 @@
1
+ export { useArraySource, computeArrayView } from './use-array-source.js';
2
+ export type { UseArraySourceOptions, UseArraySourceResult } from './types.js';
3
+ export type { ComputeArrayViewOptions } from './use-array-source.js';
@@ -0,0 +1,51 @@
1
+ import { t as e } from "../sorting-CjfjRxwL.js";
2
+ import { n as t } from "../use-odata-source-5gOJiMFj.js";
3
+ import { useCallback as n, useMemo as r, useState as i } from "react";
4
+ //#region src/array/use-array-source.ts
5
+ function a(t, { search: n, sortCriteria: r, page: i, pageSize: a, columns: o, searchFields: s }) {
6
+ let c = n.trim().toLowerCase(), l = c ? t.filter((e) => (s ? s(e) : Object.values(e)).some((e) => e != null && String(e).toLowerCase().includes(c))) : t, u = l;
7
+ r.length > 0 && (u = e(l, r, o ?? []).map((e) => l[e]));
8
+ let d = u.length, f = i * a;
9
+ return {
10
+ data: u.slice(f, f + a),
11
+ totalCount: d
12
+ };
13
+ }
14
+ function o(e, o = {}) {
15
+ let { pageSize: s = 20, defaultOrderBy: c, columns: l, searchFields: u } = o, [d, f] = i(0), [p, m] = i(() => c ? t(c) : []), [h, g] = i(""), _ = n((e) => {
16
+ g(e), f(0);
17
+ }, []), v = n((e) => {
18
+ let t = e.detail?.criteria;
19
+ t && (m(t), f(0));
20
+ }, []), y = n(() => {}, []), { data: b, totalCount: x } = r(() => a(e, {
21
+ search: h,
22
+ sortCriteria: p,
23
+ page: d,
24
+ pageSize: s,
25
+ columns: l,
26
+ searchFields: u
27
+ }), [
28
+ e,
29
+ h,
30
+ p,
31
+ d,
32
+ s,
33
+ l,
34
+ u
35
+ ]);
36
+ return {
37
+ data: b,
38
+ totalCount: x,
39
+ loading: !1,
40
+ error: null,
41
+ page: d,
42
+ setPage: f,
43
+ sortCriteria: p,
44
+ onSortChange: v,
45
+ search: h,
46
+ setSearch: _,
47
+ refresh: y
48
+ };
49
+ }
50
+ //#endregion
51
+ export { a as computeArrayView, o as useArraySource };
@@ -0,0 +1,39 @@
1
+ import type { SortCriteria } from '../core/sorting.js';
2
+ export interface UseArraySourceOptions<T> {
3
+ /** 페이지당 행 수. `useODataSource`와 동일 기본값. */
4
+ pageSize?: number;
5
+ /** 초기 정렬(`'a asc, b desc'` 형식, `useODataSource`와 동일 문법). */
6
+ defaultOrderBy?: string;
7
+ /**
8
+ * 타입 인지 정렬 비교(숫자/불리언/날짜/텍스트)에 쓸 컬럼 정의. 생략하면 전부
9
+ * 텍스트로 비교한다(숫자 컬럼도 문자열 정렬 순서를 따름) — 그리드에 이미 넘기는
10
+ * `columns`를 그대로 전달하면 된다.
11
+ */
12
+ columns?: import('../models/types.js').ColumnDefinition<T>[];
13
+ /**
14
+ * 자유 텍스트 검색이 대조할 값들을 행에서 뽑아낸다. 생략하면 행의 모든 값
15
+ * (`Object.values`)을 대상으로 한다 — 서버 read 모델에 없는 클라이언트 조인
16
+ * 파생 필드(예: 조인해 붙인 상품명)도 이 행 객체에 실제로 들어있는 한 기본
17
+ * 동작만으로 검색된다. 명시적으로 검색 범위를 좁히거나 넓히고 싶을 때만 지정.
18
+ */
19
+ searchFields?: (row: T) => Array<string | number | boolean | null | undefined>;
20
+ }
21
+ export interface UseArraySourceResult<T> {
22
+ /** 현재 페이지의 행(검색+정렬 적용 후 slice). */
23
+ data: T[];
24
+ /** 검색+정렬 적용 후, 페이지 나누기 전의 총 건수(`useODataSource`의 `@odata.count`와 동일 의미). */
25
+ totalCount: number;
26
+ /** 항상 `false` — 로컬 배열은 동기 처리라 로딩 상태가 없다. `useODataSource`와의 반환 형태 동일성을 위해 유지. */
27
+ loading: boolean;
28
+ /** 항상 `null` — 로컬 배열 처리는 실패하지 않는다. 위와 같은 이유로 유지. */
29
+ error: string | null;
30
+ page: number;
31
+ setPage: (page: number) => void;
32
+ sortCriteria: SortCriteria[];
33
+ onSortChange: (e: CustomEvent) => void;
34
+ setSearch: (term: string) => void;
35
+ search: string;
36
+ /** no-op — 로컬 배열에는 다시 불러올 원격 상태가 없다. 소비자가 `useODataSource`와
37
+ * 같은 자리에 무조건 배선한 refresh 버튼이 있어도 안전하게 아무 일도 하지 않는다. */
38
+ refresh: () => void;
39
+ }
@@ -0,0 +1,39 @@
1
+ import type { SortCriteria } from '../core/sorting.js';
2
+ import type { ColumnDefinition, DataRow } from '../models/types.js';
3
+ import type { UseArraySourceOptions, UseArraySourceResult } from './types.js';
4
+ export interface ComputeArrayViewOptions<T> {
5
+ search: string;
6
+ sortCriteria: SortCriteria[];
7
+ page: number;
8
+ pageSize: number;
9
+ columns?: ColumnDefinition<T>[];
10
+ searchFields?: (row: T) => Array<string | number | boolean | null | undefined>;
11
+ }
12
+ /**
13
+ * `useArraySource`의 검색→정렬→페이지 파이프라인을 React 없이 순수하게 계산한다
14
+ * (훅은 이 함수를 `useMemo`로 감싸기만 한다) — React 훅 렌더 인프라 없이 이 패키지의
15
+ * 다른 순수 헬퍼(`buildSearchExpression`/`parseOrderBy`)와 같은 방식으로 단위
16
+ * 테스트하기 위해 분리했다.
17
+ *
18
+ * 정렬은 그리드 자신의 클라이언트 정렬 파이프라인이 쓰는 `computeSortedIndices`를
19
+ * 그대로 재사용한다 — `columns`를 넘기면 숫자/날짜/불리언도 값 기준으로 비교되고,
20
+ * 생략하면 전부 텍스트 비교로 낮아진다.
21
+ */
22
+ export declare function computeArrayView<T extends DataRow>(data: T[], { search, sortCriteria, page, pageSize, columns, searchFields }: ComputeArrayViewOptions<T>): {
23
+ data: T[];
24
+ totalCount: number;
25
+ };
26
+ /**
27
+ * 클라이언트 배열(이미 메모리에 있는 데이터, 흔히 여러 출처를 조인해 만든 파생
28
+ * 행 배열) 전용 React 훅. `useODataSource`와 **같은 반환 형태**(`data`/`totalCount`/
29
+ * `loading`/`error`/`page`/`setPage`/`sortCriteria`/`onSortChange`/`search`/
30
+ * `setSearch`/`refresh`)를 제공해, `dataMode="server"` + `<FlexTableReact>` 배선
31
+ * 코드를 서버·클라이언트 소스 사이에서 그대로 재사용할 수 있게 한다.
32
+ *
33
+ * `useODataSource`가 서버에 `$search`/`$orderby`/`$top`/`$skip`을 보내 처리를
34
+ * 위임하는 것과 달리, 이 훅은 검색·정렬·페이지 나누기를 전부 로컬에서 계산한다
35
+ * (`computeArrayView`) — 서버 read 모델에 없는 클라이언트 조인 파생 필드(예: 별도
36
+ * 엔드포인트에서 가져와 붙인 상품명)로 검색/정렬해야 해서 서버 쿼리로 표현할 수
37
+ * 없는 lookup 테이블에 쓴다.
38
+ */
39
+ export declare function useArraySource<T extends DataRow = DataRow>(data: T[], options?: UseArraySourceOptions<T>): UseArraySourceResult<T>;
@@ -0,0 +1 @@
1
+ export {};