@youp-grid/core 0.2.2 → 0.2.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Seungyoup Baek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -9,3 +9,17 @@ npm install @youp-grid/core
9
9
  ```ts
10
10
  import { buildRowModel, type ColumnDef } from "@youp-grid/core";
11
11
  ```
12
+
13
+ ## What It Owns
14
+
15
+ - column normalization
16
+ - row model generation
17
+ - sorting, filtering, and pagination helpers
18
+ - selection and column state helpers
19
+ - clipboard, fill handle, history, CSV, aggregation, and grouping utilities
20
+
21
+ UI adapters should reuse this package instead of duplicating data semantics.
22
+
23
+ ## License
24
+
25
+ MIT. See the repository license.
package/dist/index.d.ts CHANGED
@@ -13,9 +13,11 @@ export { applyPagination } from "./pagination.ts";
13
13
  export { buildRowModel } from "./row-model.ts";
14
14
  export { applyRowGrouping, isRowGroupNode } from "./row-grouping.ts";
15
15
  export { applySorting } from "./sorting.ts";
16
+ export { applyTreeData } from "./tree-data.ts";
17
+ export type { ApplyTreeDataOptions } from "./tree-data.ts";
16
18
  export { clearSelection, setRowSelected, setSelectedRows, toggleRowSelected } from "./selection.ts";
17
- export { clearFilter, clearSort, acknowledgeRemoteCache, cancelRemoteRequest, createRemoteCacheKey, createGridState, failRemoteRequest, finishRemoteRequest, invalidateRemoteCache, isActiveRemoteRequest, setCursorPage, setCursorPageSize, setCursorPagination, setAggregation, setFilter, setPagination, setRemoteCache, setRowGrouping, setSort, startRemoteRequest, toggleRowGroupExpanded, toggleSort, } from "./state.ts";
19
+ export { clearFilter, clearSort, acknowledgeRemoteCache, cancelRemoteRequest, createRemoteCacheKey, createGridState, failRemoteRequest, finishRemoteRequest, invalidateRemoteCache, isActiveRemoteRequest, setCursorPage, setCursorPageSize, setCursorPagination, setAggregation, setFilter, setPagination, setRemoteCache, setRowGrouping, setSort, setTreeExpandedRows, startRemoteRequest, toggleRowGroupExpanded, toggleTreeRowExpanded, toggleSort, } from "./state.ts";
18
20
  export { getVirtualRange } from "./virtualizer.ts";
19
21
  export { createValueHistoryState, invertValueHistoryEntry, pushValueHistoryEntry, redoValueHistory, undoValueHistory, } from "./history.ts";
20
22
  export type { GridCellValueHistoryChange, GridValueHistoryEntry, GridValueHistoryState, } from "./history.ts";
21
- export type { Accessor, AggregationFunctionName, AggregationResult, AggregationRule, BuildRowModelOptions, ColumnAlign, ColumnPin, ColumnState, ColumnComparator, ColumnDef, ColumnEditor, ColumnEditorOption, ColumnEditorOptionValue, ColumnFilterPredicate, CursorPaginationState, FilterOperator, FilterRule, GridRowId, GridRowModelType, GridState, InfiniteScrollTrigger, InfiniteScrollTriggerOptions, PaginationState, ResolvedColumnDef, RemoteCacheState, RemoteRequestState, RemoteRequestStatus, RowModel, RowDisplayNode, RowGroupNode, RowGroupingState, RowNode, SortDirection, SortRule, ValueFormatter, ValueParser, VirtualItem, VirtualRange, VirtualRangeOptions, } from "./types.ts";
23
+ export type { Accessor, AggregationFunctionName, AggregationResult, AggregationRule, BuildRowModelOptions, ColumnAlign, ColumnPin, ColumnState, ColumnComparator, ColumnDef, ColumnEditor, ColumnEditorOption, ColumnEditorOptionValue, ColumnFilterPredicate, CursorPaginationState, FilterOperator, FilterRule, GridRowId, GridRowModelType, GridState, InfiniteScrollTrigger, InfiniteScrollTriggerOptions, PaginationState, ResolvedColumnDef, RemoteCacheState, RemoteRequestState, RemoteRequestStatus, RowModel, RowDisplayNode, RowGroupNode, RowGroupingState, RowNode, SortDirection, SortRule, TreeDataState, ValueFormatter, ValueParser, VirtualItem, VirtualRange, VirtualRangeOptions, } from "./types.ts";
package/dist/index.js CHANGED
@@ -10,7 +10,8 @@ export { applyPagination } from "./pagination.js";
10
10
  export { buildRowModel } from "./row-model.js";
11
11
  export { applyRowGrouping, isRowGroupNode } from "./row-grouping.js";
12
12
  export { applySorting } from "./sorting.js";
13
+ export { applyTreeData } from "./tree-data.js";
13
14
  export { clearSelection, setRowSelected, setSelectedRows, toggleRowSelected } from "./selection.js";
14
- export { clearFilter, clearSort, acknowledgeRemoteCache, cancelRemoteRequest, createRemoteCacheKey, createGridState, failRemoteRequest, finishRemoteRequest, invalidateRemoteCache, isActiveRemoteRequest, setCursorPage, setCursorPageSize, setCursorPagination, setAggregation, setFilter, setPagination, setRemoteCache, setRowGrouping, setSort, startRemoteRequest, toggleRowGroupExpanded, toggleSort, } from "./state.js";
15
+ export { clearFilter, clearSort, acknowledgeRemoteCache, cancelRemoteRequest, createRemoteCacheKey, createGridState, failRemoteRequest, finishRemoteRequest, invalidateRemoteCache, isActiveRemoteRequest, setCursorPage, setCursorPageSize, setCursorPagination, setAggregation, setFilter, setPagination, setRemoteCache, setRowGrouping, setSort, setTreeExpandedRows, startRemoteRequest, toggleRowGroupExpanded, toggleTreeRowExpanded, toggleSort, } from "./state.js";
15
16
  export { getVirtualRange } from "./virtualizer.js";
16
17
  export { createValueHistoryState, invertValueHistoryEntry, pushValueHistoryEntry, redoValueHistory, undoValueHistory, } from "./history.js";
package/dist/row-model.js CHANGED
@@ -5,6 +5,7 @@ import { applyFilters } from "./filtering.js";
5
5
  import { applyPagination } from "./pagination.js";
6
6
  import { applyRowGrouping } from "./row-grouping.js";
7
7
  import { applySorting } from "./sorting.js";
8
+ import { applyTreeData } from "./tree-data.js";
8
9
  export function buildRowModel(options) {
9
10
  const columns = applyColumnState(normalizeColumns(options.columns), options.state?.columns);
10
11
  const visibleColumns = getVisibleColumns(columns);
@@ -15,13 +16,20 @@ export function buildRowModel(options) {
15
16
  columns,
16
17
  visibleColumns,
17
18
  state: options.state,
19
+ treeData: options.treeData,
20
+ getParentRowId: options.getParentRowId,
18
21
  serverRowCount: options.serverRowCount,
19
22
  serverFilteredRowCount: options.serverFilteredRowCount,
20
23
  });
21
24
  }
22
25
  const filteredRows = applyFilters(allRows, columns, options.state?.filters);
23
26
  const sortedRows = applySorting(filteredRows, columns, options.state?.sort);
24
- const paginated = applyPagination(sortedRows, options.state?.pagination);
27
+ const treeRows = applyTreeData(sortedRows, {
28
+ enabled: options.treeData,
29
+ state: options.state?.treeData,
30
+ getParentRowId: options.getParentRowId,
31
+ });
32
+ const paginated = applyPagination(treeRows, options.state?.pagination);
25
33
  const aggregation = applyAggregation(filteredRows, columns, options.state?.aggregation);
26
34
  const displayRows = applyRowGrouping(paginated.rows, columns, options.state?.rowGrouping);
27
35
  return {
@@ -42,18 +50,23 @@ export function buildRowModel(options) {
42
50
  function buildServerRowModel(context) {
43
51
  const totalRowCount = context.serverRowCount ?? context.allRows.length;
44
52
  const filteredRowCount = context.serverFilteredRowCount ?? totalRowCount;
53
+ const visibleRows = applyTreeData(context.allRows, {
54
+ enabled: context.treeData,
55
+ state: context.state?.treeData,
56
+ getParentRowId: context.getParentRowId,
57
+ });
45
58
  return {
46
59
  columns: context.columns,
47
60
  visibleColumns: context.visibleColumns,
48
61
  allRows: context.allRows,
49
62
  filteredRows: context.allRows,
50
63
  sortedRows: context.allRows,
51
- visibleRows: context.allRows,
52
- displayRows: applyRowGrouping(context.allRows, context.columns, context.state?.rowGrouping),
64
+ visibleRows,
65
+ displayRows: applyRowGrouping(visibleRows, context.columns, context.state?.rowGrouping),
53
66
  aggregation: applyAggregation(context.allRows, context.columns, context.state?.aggregation),
54
67
  totalRowCount,
55
68
  filteredRowCount,
56
- visibleRowCount: context.allRows.length,
69
+ visibleRowCount: visibleRows.length,
57
70
  pageCount: getServerPageCount(filteredRowCount, context.state?.pagination),
58
71
  };
59
72
  }
package/dist/state.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AggregationRule, CursorPaginationState, FilterRule, GridState, PaginationState, RemoteCacheState, RowGroupingState, SortDirection } from "./types.ts";
1
+ import type { AggregationRule, CursorPaginationState, FilterRule, GridRowId, GridState, PaginationState, RemoteCacheState, RowGroupingState, SortDirection } from "./types.ts";
2
2
  export declare function createGridState(state?: GridState): GridState;
3
3
  export declare function toggleSort(state: GridState, columnId: string, options?: {
4
4
  multi?: boolean;
@@ -17,6 +17,8 @@ export declare function setCursorPageSize(state: GridState, pageSize: number): G
17
17
  export declare function setAggregation(state: GridState, aggregation: readonly AggregationRule[]): GridState;
18
18
  export declare function setRowGrouping(state: GridState, rowGrouping: RowGroupingState | undefined): GridState;
19
19
  export declare function toggleRowGroupExpanded(state: GridState, groupId: string): GridState;
20
+ export declare function setTreeExpandedRows(state: GridState, rowIds: readonly GridRowId[]): GridState;
21
+ export declare function toggleTreeRowExpanded(state: GridState, rowId: GridRowId): GridState;
20
22
  export declare function startRemoteRequest(state: GridState, requestId: string): GridState;
21
23
  export declare function finishRemoteRequest(state: GridState, requestId: string): GridState;
22
24
  export declare function failRemoteRequest(state: GridState, requestId: string, error?: string): GridState;
package/dist/state.js CHANGED
@@ -12,6 +12,13 @@ export function createGridState(state = {}) {
12
12
  : undefined,
13
13
  }
14
14
  : undefined,
15
+ treeData: state.treeData
16
+ ? {
17
+ expandedRowIds: state.treeData.expandedRowIds
18
+ ? [...state.treeData.expandedRowIds]
19
+ : undefined,
20
+ }
21
+ : undefined,
15
22
  pagination: state.pagination ? { ...state.pagination } : undefined,
16
23
  cursorPagination: state.cursorPagination ? { ...state.cursorPagination } : undefined,
17
24
  remoteRequest: state.remoteRequest ? { ...state.remoteRequest } : undefined,
@@ -158,6 +165,26 @@ export function toggleRowGroupExpanded(state, groupId) {
158
165
  },
159
166
  };
160
167
  }
168
+ export function setTreeExpandedRows(state, rowIds) {
169
+ const expandedRowIds = [...new Set(rowIds)];
170
+ return {
171
+ ...state,
172
+ treeData: {
173
+ ...(state.treeData ?? {}),
174
+ expandedRowIds: expandedRowIds.length > 0 ? expandedRowIds : undefined,
175
+ },
176
+ };
177
+ }
178
+ export function toggleTreeRowExpanded(state, rowId) {
179
+ const expandedRowIds = new Set(state.treeData?.expandedRowIds ?? []);
180
+ if (expandedRowIds.has(rowId)) {
181
+ expandedRowIds.delete(rowId);
182
+ }
183
+ else {
184
+ expandedRowIds.add(rowId);
185
+ }
186
+ return setTreeExpandedRows(state, [...expandedRowIds]);
187
+ }
161
188
  export function startRemoteRequest(state, requestId) {
162
189
  return {
163
190
  ...state,
@@ -208,6 +235,7 @@ export function createRemoteCacheKey(state) {
208
235
  filters: state.filters ?? [],
209
236
  aggregation: state.aggregation ?? [],
210
237
  rowGrouping: state.rowGrouping,
238
+ treeData: state.treeData,
211
239
  pagination: state.pagination,
212
240
  cursorPagination: state.cursorPagination,
213
241
  });
@@ -0,0 +1,7 @@
1
+ import type { GridRowId, RowNode, TreeDataState } from "./types.ts";
2
+ export type ApplyTreeDataOptions<TRow> = {
3
+ enabled?: boolean;
4
+ state?: TreeDataState;
5
+ getParentRowId?: (row: TRow, index: number) => GridRowId | null | undefined;
6
+ };
7
+ export declare function applyTreeData<TRow>(rows: RowNode<TRow>[], options?: ApplyTreeDataOptions<TRow>): RowNode<TRow>[];
@@ -0,0 +1,78 @@
1
+ export function applyTreeData(rows, options = {}) {
2
+ if (!options.enabled || !options.getParentRowId) {
3
+ return rows;
4
+ }
5
+ const rowById = new Map();
6
+ const parentIdByRowId = new Map();
7
+ for (const row of rows) {
8
+ rowById.set(row.id, row);
9
+ }
10
+ const roots = [];
11
+ const childrenByParentId = new Map();
12
+ for (const row of rows) {
13
+ const parentId = options.getParentRowId(row.original, row.index) ?? undefined;
14
+ const validParentId = parentId !== undefined && parentId !== row.id && rowById.has(parentId)
15
+ ? parentId
16
+ : undefined;
17
+ parentIdByRowId.set(row.id, validParentId);
18
+ if (validParentId === undefined) {
19
+ roots.push(row);
20
+ continue;
21
+ }
22
+ const siblings = childrenByParentId.get(validParentId);
23
+ if (siblings) {
24
+ siblings.push(row);
25
+ }
26
+ else {
27
+ childrenByParentId.set(validParentId, [row]);
28
+ }
29
+ }
30
+ const expandedRowIds = new Set(options.state?.expandedRowIds ?? []);
31
+ const visibleRows = [];
32
+ const emitted = new Set();
33
+ const reachable = new Set();
34
+ const markReachable = (row, visiting) => {
35
+ if (visiting.has(row.id) || reachable.has(row.id)) {
36
+ return;
37
+ }
38
+ const nextVisiting = new Set(visiting);
39
+ nextVisiting.add(row.id);
40
+ reachable.add(row.id);
41
+ for (const child of childrenByParentId.get(row.id) ?? []) {
42
+ markReachable(child, nextVisiting);
43
+ }
44
+ };
45
+ const appendRow = (row, depth, visiting) => {
46
+ if (visiting.has(row.id) || emitted.has(row.id)) {
47
+ return;
48
+ }
49
+ const children = childrenByParentId.get(row.id) ?? [];
50
+ const expanded = children.length > 0 && expandedRowIds.has(row.id);
51
+ const nextVisiting = new Set(visiting);
52
+ nextVisiting.add(row.id);
53
+ visibleRows.push({
54
+ ...row,
55
+ depth,
56
+ parentId: parentIdByRowId.get(row.id),
57
+ hasChildren: children.length > 0,
58
+ expanded,
59
+ });
60
+ emitted.add(row.id);
61
+ if (!expanded) {
62
+ return;
63
+ }
64
+ for (const child of children) {
65
+ appendRow(child, depth + 1, nextVisiting);
66
+ }
67
+ };
68
+ for (const root of roots) {
69
+ markReachable(root, new Set());
70
+ appendRow(root, 0, new Set());
71
+ }
72
+ for (const row of rows) {
73
+ if (!reachable.has(row.id)) {
74
+ appendRow(row, 0, new Set());
75
+ }
76
+ }
77
+ return visibleRows;
78
+ }
package/dist/types.d.ts CHANGED
@@ -77,6 +77,9 @@ export type RowGroupingState = {
77
77
  columnIds: string[];
78
78
  collapsedGroupIds?: string[];
79
79
  };
80
+ export type TreeDataState = {
81
+ expandedRowIds?: GridRowId[];
82
+ };
80
83
  export type PaginationState = {
81
84
  pageIndex: number;
82
85
  pageSize: number;
@@ -108,6 +111,7 @@ export type GridState = {
108
111
  filters?: FilterRule[];
109
112
  aggregation?: AggregationRule[];
110
113
  rowGrouping?: RowGroupingState;
114
+ treeData?: TreeDataState;
111
115
  pagination?: PaginationState;
112
116
  cursorPagination?: CursorPaginationState;
113
117
  remoteRequest?: RemoteRequestState;
@@ -118,6 +122,10 @@ export type RowNode<TRow> = {
118
122
  id: GridRowId;
119
123
  index: number;
120
124
  original: TRow;
125
+ depth?: number;
126
+ parentId?: GridRowId;
127
+ hasChildren?: boolean;
128
+ expanded?: boolean;
121
129
  };
122
130
  export type RowGroupNode = {
123
131
  type: "group";
@@ -137,6 +145,8 @@ export type BuildRowModelOptions<TRow> = {
137
145
  columns: readonly ColumnDef<TRow>[];
138
146
  state?: GridState;
139
147
  getRowId?: (row: TRow, index: number) => GridRowId;
148
+ treeData?: boolean;
149
+ getParentRowId?: (row: TRow, index: number) => GridRowId | null | undefined;
140
150
  rowModelType?: GridRowModelType;
141
151
  serverRowCount?: number;
142
152
  serverFilteredRowCount?: number;
package/package.json CHANGED
@@ -1,8 +1,17 @@
1
1
  {
2
2
  "name": "@youp-grid/core",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Framework-agnostic data grid core for Youp Grid.",
5
+ "license": "MIT",
6
+ "author": "SeungyoupBaek",
5
7
  "type": "module",
8
+ "keywords": [
9
+ "data-grid",
10
+ "grid",
11
+ "typescript",
12
+ "table",
13
+ "headless"
14
+ ],
6
15
  "main": "./dist/index.js",
7
16
  "types": "./dist/index.d.ts",
8
17
  "exports": {
@@ -13,13 +22,18 @@
13
22
  },
14
23
  "files": [
15
24
  "dist",
16
- "README.md"
25
+ "README.md",
26
+ "LICENSE"
17
27
  ],
18
28
  "repository": {
19
29
  "type": "git",
20
30
  "url": "git+ssh://git@github.com/SeungyoupBaek/youp-grid.git",
21
31
  "directory": "packages/core"
22
32
  },
33
+ "bugs": {
34
+ "url": "https://github.com/SeungyoupBaek/youp-grid/issues"
35
+ },
36
+ "homepage": "https://github.com/SeungyoupBaek/youp-grid/tree/main/packages/core#readme",
23
37
  "publishConfig": {
24
38
  "access": "public"
25
39
  },