@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 +37 -0
- package/README.md +42 -2
- package/dist/array/index.d.ts +3 -0
- package/dist/array/index.js +51 -0
- package/dist/array/types.d.ts +39 -0
- package/dist/array/use-array-source.d.ts +39 -0
- package/dist/array/use-array-source.test.d.ts +1 -0
- package/dist/{flex-table-DuQWDNlT.js → flex-table-2xx4M0ti.js} +246 -295
- package/dist/flex-table.js +1 -1
- package/dist/odata/index.js +2 -83
- package/dist/react.js +1 -1
- package/dist/sorting-CjfjRxwL.js +51 -0
- package/dist/use-odata-source-5gOJiMFj.js +83 -0
- package/package.json +6 -1
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:
|
|
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,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 {};
|