@crispy-seed/data-table 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +30 -0
  3. package/README.md +103 -0
  4. package/dist/DataTable-12s-8LMBtS2u.cjs +1532 -0
  5. package/dist/DataTable-12s-Bd20OuMs.js +1482 -0
  6. package/dist/DataTable-12s-Zml1x9Kr-12s-CTRzgvMT.cjs +161 -0
  7. package/dist/DataTable-12s-Zml1x9Kr-12s-Coi-3lUa.js +153 -0
  8. package/dist/Menu-12s-Db_zr1AP.cjs +104 -0
  9. package/dist/Menu-12s-Di5JADW4.js +78 -0
  10. package/dist/Select-12s-1Uh7XbkG.js +219 -0
  11. package/dist/Select-12s-D8DdKT-U.cjs +250 -0
  12. package/dist/body-cells-12s-BnOVUTKN.js +453 -0
  13. package/dist/body-cells-12s-BoOTcg-l.cjs +462 -0
  14. package/dist/column-calc-12s-C6IPSOaF.js +129 -0
  15. package/dist/column-calc-12s-CKuSpe-l.cjs +149 -0
  16. package/dist/column-layout-12s-CTu5yT_D.cjs +120 -0
  17. package/dist/column-layout-12s-s7wnxkMN.js +99 -0
  18. package/dist/column-menu-12s-BPE9N1gs.cjs +427 -0
  19. package/dist/column-menu-12s-mRBk-4oa.js +407 -0
  20. package/dist/data-table-recipe.css +442 -0
  21. package/dist/data-table-recipe.layered.css +505 -0
  22. package/dist/editors-12s-BO9Mp034.js +525 -0
  23. package/dist/editors-12s-DO8TZkYV.cjs +555 -0
  24. package/dist/index.cjs +14 -0
  25. package/dist/index.d.ts +323 -0
  26. package/dist/index.js +10 -0
  27. package/dist/resize-12s-jQY-PX67.cjs +58 -0
  28. package/dist/resize-12s-nsMvrmLt.js +58 -0
  29. package/dist/states-12s-CLO_uVue.cjs +125 -0
  30. package/dist/states-12s-DEFSxJCO.js +122 -0
  31. package/dist/styles.css +14660 -0
  32. package/dist/toolbar-12s-AVU-sbDW.js +69 -0
  33. package/dist/toolbar-12s-CaLgJrDo.cjs +71 -0
  34. package/dist/useDataTable-12s-BHiDUubw-12s-CBeCwTe7.js +61 -0
  35. package/dist/useDataTable-12s-BHiDUubw-12s-Dgvt7XDk.cjs +61 -0
  36. package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-CvD14JYo.cjs +207 -0
  37. package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-DsEse3uq.js +183 -0
  38. package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CD6goR59.js +289 -0
  39. package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CUk_CpYT.cjs +292 -0
  40. package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-BoOHq3GB.js +98 -0
  41. package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-De6YHBba.cjs +98 -0
  42. package/dist/useDataTableFilter-12s-ZzbmWMin-12s-CWyBeFna.js +74 -0
  43. package/dist/useDataTableFilter-12s-ZzbmWMin-12s-lpjzZY3_.cjs +74 -0
  44. package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-BexjP6iO.js +104 -0
  45. package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-Bt8gojr9.cjs +104 -0
  46. package/dist/useDataTableRow-12s-CPYaDa1p-12s-BmUSAnPe.js +30 -0
  47. package/dist/useDataTableRow-12s-CPYaDa1p-12s-D-DRjwAe.cjs +30 -0
  48. package/dist/useDataTableScroll-12s-BKDCr0en-12s-C7f2vHeG.js +61 -0
  49. package/dist/useDataTableScroll-12s-BKDCr0en-12s-CUvUYMtt.cjs +61 -0
  50. package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-B-65V6gH.js +37 -0
  51. package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-DhVfN2Hq.cjs +37 -0
  52. package/dist/useDataTableSelection-12s-BIDtiRQq-12s-IS__VAnU.cjs +71 -0
  53. package/dist/useDataTableSelection-12s-BIDtiRQq-12s-O6M_BhrH.js +71 -0
  54. package/dist/useDataTableSort-12s-DvJYtmeS-12s-C5VGnsq4.cjs +78 -0
  55. package/dist/useDataTableSort-12s-DvJYtmeS-12s-DABSNt6U.js +78 -0
  56. package/package.json +78 -0
@@ -0,0 +1,323 @@
1
+ import { PrimitiveProps } from '@seed-design/react-primitive';
2
+ import * as react from 'react';
3
+
4
+ declare interface DataTableVariant {
5
+ /**
6
+ * @default "card"
7
+ */
8
+ surface: "card" | "bleed";
9
+ /**
10
+ * @default false
11
+ */
12
+ interactive: boolean;
13
+ }
14
+
15
+ declare type DataTableVariantProps = Partial<DataTableVariant>;
16
+
17
+ type DataTableSortDirection = "asc" | "desc";
18
+ interface DataTableSortState {
19
+ key: string;
20
+ dir: DataTableSortDirection;
21
+ }
22
+
23
+ type DataTableFilterMode = "is" | "isnot";
24
+ interface DataTableColumnFilter {
25
+ mode: DataTableFilterMode;
26
+ /**
27
+ * Checked values. One checked value reads as "is X"/"is not X"; several read as
28
+ * Attio's "is any of X, Y"/"is not any of X, Y" -- the exact same predicate, just
29
+ * more values in the set (crisp's own `applyFilters`, `tabletoolbar.tsx`, treats
30
+ * `isanyof` as membership-in-`values` too). An empty array means the picker is open
31
+ * with nothing checked yet, not "match nothing" -- see `isFilterActive` below.
32
+ */
33
+ values: string[];
34
+ }
35
+ type DataTableFilterState = Record<string, DataTableColumnFilter>;
36
+
37
+ /**
38
+ * The six aggregations DATATABLE-ATTIO-SPEC.md's ColumnCalc footer offers (crisp's own
39
+ * `ColumnCalc` ships nine -- adding percent-empty/-filled and a "none" clearing op --
40
+ * but this table's contract is exactly the six named in the spec doc: count /
41
+ * countFilled / sum / average / min / max). `count`/`countFilled` apply to every
42
+ * column; `sum`/`average`/`min`/`max` only make sense on a numeric column, so the
43
+ * styled layer only offers those four when the column's cell type is number/currency
44
+ * (see DataTable.tsx's `ColumnCalcMenu`).
45
+ */
46
+ type DataTableCalcOp = "count" | "countFilled" | "sum" | "average" | "min" | "max";
47
+
48
+ type DataTableCellType = "text" | "entity" | "url" | "email" | "tags" | "status" | "number" | "currency" | "date" | "datetime" | "rating" | "checkbox";
49
+
50
+ /** A `select`/`reference`/`actor` editor's own choice list -- `value`/`label` are
51
+ * required (the committed string and what the row reads), `avatar` is read only by
52
+ * `reference`/`actor` (a photo URL; absent renders a generic person glyph, matching
53
+ * crisp's own `DataReferenceEditor` fallback). */
54
+ interface DataTableEditOption {
55
+ value: string;
56
+ label: string;
57
+ avatar?: string;
58
+ }
59
+
60
+ interface DataTableColumn<TRow> {
61
+ /**
62
+ * `data`의 각 행에서 이 열의 값을 찾는 key입니다. `render`가 없으면 `type`에 맞는
63
+ * 기본 렌더러(또는 문자열 표시)로 값을 그립니다.
64
+ */
65
+ key: string;
66
+ /** 헤더 셀에 표시할 내용입니다. */
67
+ header: react.ReactNode;
68
+ /** 이 열을 클릭해 정렬할 수 있는지 여부입니다. */
69
+ sortable?: boolean;
70
+ /**
71
+ * Attio 셀 타입입니다. 생략하면 `"text"`(그대로 문자열 표시)로 취급합니다.
72
+ * `render`가 있으면 `type`은 (정렬/정렬-정렬 이외의) 표시에는 쓰이지 않습니다.
73
+ */
74
+ type?: DataTableCellType;
75
+ /** 셀 내용을 직접 그립니다. 지정하면 `type`의 기본 렌더러 대신 이것을 사용합니다.
76
+ * `editable`이어도 편집/always-live 상태에는 영향이 없습니다 -- 이 필드는 REST
77
+ * 상태의 표시만 바꿉니다. */
78
+ render?: (row: TRow) => react.ReactNode;
79
+ /** `type: "currency"`의 통화 기호입니다. @default "$" */
80
+ symbol?: string;
81
+ /** `type: "rating"`의 별 개수입니다. @default 5 */
82
+ maxRating?: number;
83
+ /**
84
+ * 정렬/검색/필터/내보내기가 공유하는 단 하나의 값 접근자입니다 (DATATABLE-ATTIO-
85
+ * SPEC.md의 Column API). 생략하면 `row[key]`를 그대로 씁니다 -- `render`는 표시만
86
+ * 바꿀 뿐 이 접근자를 대체하지 않습니다 (표시용 JSX와 비교 가능한 값은 다를 수
87
+ * 있음).
88
+ */
89
+ value?: (row: TRow) => unknown;
90
+ /** 툴바의 검색창이 이 열도 검색할지 여부입니다. 기본은 검색 대상에서 제외입니다
91
+ * (GRC의 `Column<T>` 계약과 동일) -- 명시적으로 표시한 열만 검색됩니다. */
92
+ searchable?: boolean;
93
+ /**
94
+ * 이 열의 값 목록입니다. 지정하면 헤더에 "is / is not / is any of" 체크박스
95
+ * 필터 피커가 붙습니다 -- 자유 텍스트 열은 생략합니다(필터 UI 없음).
96
+ */
97
+ filterValues?: string[];
98
+ /**
99
+ * 이 열의 초기 너비(px)입니다. 생략하면 다른 미지정 열들과 폭을 균등하게 나눠
100
+ * 갖는(flex: 1) 기존 동작을 유지합니다 -- 드래그로 리사이즈하거나 `columnWidths`로
101
+ * 제어하면 이 값 대신 그 너비를 씁니다.
102
+ */
103
+ width?: number;
104
+ /** 이 열의 드래그 리사이즈를 끕니다 (테이블 전체의 `resizable`이 켜져 있어도).
105
+ * @default true */
106
+ resizable?: boolean;
107
+ /** ColumnCalc 합계 푸터에 이 열의 트리거를 표시합니다 (테이블 전체의
108
+ * `columns.some(c => c.summary)`가 푸터 행 자체를 렌더링할지를 결정합니다). */
109
+ summary?: boolean;
110
+ /** 아직 아무 것도 고르지 않았을 때 ColumnCalc가 시작하는 연산입니다. 생략하면
111
+ * "Add calculation"으로 비어 있는 채 시작합니다. */
112
+ summaryDefaultOp?: DataTableCalcOp;
113
+ /**
114
+ * 인라인 편집을 켭니다 (DATATABLE-ATTIO-SPEC.md 트랜치 5). `editor`가 없는 플레인
115
+ * `editable` 열은 더블클릭으로 텍스트/숫자 입력(`cellType`이 없거나 `"number"`)
116
+ * 또는 날짜/일시 입력(`cellType: "date"`/`"datetime"`)을 엽니다 -- Enter/blur
117
+ * 커밋, Escape 취소. `editor`가 있으면 그 편집기가 대신 열립니다. `type: "rating"`/
118
+ * `"checkbox"`, `editor: "switch"`는 always-live(더블클릭 없이 바로 조작 =
119
+ * 커밋)입니다. 커밋은 테이블의 `onEdit`으로 갑니다. */
120
+ editable?: boolean;
121
+ /** 플레인 `editable` 열(=`editor` 없음)이 여는 편집기의 종류입니다. 생략하면
122
+ * 텍스트 입력(seed `TextField`)입니다. `"number"`는 같은 `TextField`를
123
+ * `type="number"`로 엽니다. `"date"`/`"datetime"`는 네이티브
124
+ * `date`/`datetime-local` 입력을 엽니다. `type`(표시 렌더러)과는 독립적입니다 --
125
+ * 예: `type: "currency"` + `cellType: "number"`는 화면엔 통화로 보이지만
126
+ * 편집기는 평범한 숫자 입력입니다. GRC의 `Column<T>.cellType`과 동일한 계약. */
127
+ cellType?: "number" | "date" | "datetime";
128
+ /** `editable`이 여는 편집기를 `cellType` 대신 지정합니다. `"select"`/`"reference"`/
129
+ * `"actor"`는 더블클릭으로 seed `Select`를 엽니다(`reference`/`actor`는 검색 +
130
+ * 아바타). `"switch"`는 always-live seed `Switch`입니다. */
131
+ editor?: "select" | "switch" | "reference" | "actor";
132
+ /** `editor: "select"`의 선택지입니다. */
133
+ options?: DataTableEditOption[];
134
+ /** `editor: "reference"`/`"actor"`의 선택지입니다 (`avatar`가 있으면 아바타로,
135
+ * 없으면 기본 사람 아이콘으로 표시). */
136
+ refOptions?: DataTableEditOption[];
137
+ /**
138
+ * 편집기가 읽는 raw 값입니다 (`value`와 다름 -- `value`는 정렬/검색/필터/내보내기가
139
+ * 공유하는, 표시용으로 가공됐을 수 있는 접근자인 반면 `editValue`는 편집기 위젯이
140
+ * 그대로 기대하는 타입, 예: `editor: "switch"`/`type: "checkbox"`의 실제
141
+ * `boolean`, `type: "rating"`의 `number`, `select`/`reference`/`actor`가 고를
142
+ * 옵션의 문자열 그 자체입니다). 생략하면 `value`(또는 `row[key]`)를 그대로 씁니다.
143
+ */
144
+ editValue?: (row: TRow) => string | number | boolean;
145
+ }
146
+ interface DataTableProps<TRow extends {
147
+ id: string;
148
+ }> extends DataTableVariantProps, Omit<PrimitiveProps, "asChild">, Omit<react.HTMLAttributes<HTMLDivElement>, "children"> {
149
+ columns: DataTableColumn<TRow>[];
150
+ data: TRow[];
151
+ /** `type`이 있는 열의 number/currency/date/datetime 표시에 쓰는 로캘입니다. @default "en-US" */
152
+ locale?: string;
153
+ /** 제어할 정렬 상태입니다. */
154
+ sort?: DataTableSortState;
155
+ /** 비제어 방식으로 사용할 때의 초기 정렬 상태입니다. */
156
+ defaultSort?: DataTableSortState;
157
+ /** 정렬 가능한 열을 클릭해 정렬 상태가 바뀔 때 호출됩니다. */
158
+ onSortChange?: (sort: DataTableSortState) => void;
159
+ /**
160
+ * 정렬 아이콘입니다. `idle`은 정렬 가능하지만 아직 정렬되지 않은 열의 헤더에
161
+ * (호버 시에만 드러나는) 기본 표시로 쓰이고, `asc`/`desc`는 실제로 정렬된 열에
162
+ * 씁니다. 생략하면 karrot 아이콘 셋의 chevron을 씁니다.
163
+ */
164
+ sortIcon?: {
165
+ idle?: react.ReactNode;
166
+ asc?: react.ReactNode;
167
+ desc?: react.ReactNode;
168
+ };
169
+ /** 왼쪽에 40px짜리 체크박스 선택 열을 추가합니다. */
170
+ selectable?: boolean;
171
+ /** 제어할 선택된 행 id 목록입니다. */
172
+ selectedIds?: string[];
173
+ /** 비제어 방식으로 사용할 때의 초기 선택된 행 id 목록입니다. */
174
+ defaultSelectedIds?: string[];
175
+ /** 선택된 행이 바뀔 때 호출됩니다. */
176
+ onSelectionChange?: (selectedIds: string[]) => void;
177
+ /** 전체 선택 체크박스의 접근성 이름입니다. */
178
+ selectAllLabel?: string;
179
+ /** 각 행 체크박스의 접근성 이름입니다. */
180
+ getRowSelectionLabel?: (row: TRow) => string;
181
+ /** 선택 체크박스가 checked일 때 표시할 아이콘입니다. 생략하면 karrot의 체크 아이콘을
182
+ * 씁니다. */
183
+ selectionCheckedIcon?: react.ReactNode;
184
+ /** 전체 선택 체크박스가 일부만 선택됐을 때(indeterminate) 표시할 아이콘입니다.
185
+ * 생략하면 karrot의 minus 아이콘을 씁니다. */
186
+ selectionIndeterminateIcon?: react.ReactNode;
187
+ /** 제어할 검색어입니다. */
188
+ query?: string;
189
+ /** 비제어 방식으로 사용할 때의 초기 검색어입니다. */
190
+ defaultQuery?: string;
191
+ /** 검색어가 바뀔 때 호출됩니다. */
192
+ onQueryChange?: (query: string) => void;
193
+ /** 검색창의 placeholder입니다. */
194
+ searchPlaceholder?: string;
195
+ /** 검색창의 접근성 이름입니다. */
196
+ searchLabel?: string;
197
+ /** 제어할 열별 필터 상태입니다. */
198
+ filters?: DataTableFilterState;
199
+ /** 비제어 방식으로 사용할 때의 초기 필터 상태입니다. */
200
+ defaultFilters?: DataTableFilterState;
201
+ /** 필터 상태가 바뀔 때 호출됩니다. */
202
+ onFiltersChange?: (filters: DataTableFilterState) => void;
203
+ /** 툴바에 행 개수를 표시할지 여부입니다. @default true */
204
+ showCount?: boolean;
205
+ /**
206
+ * 행 개수 텍스트를 직접 만듭니다. 생략하면 crisp의 기본 형식을 씁니다: 검색/필터로
207
+ * 줄지 않았으면 "N rows"(단수는 "row"), 줄었으면 "N of M".
208
+ */
209
+ countLabel?: (shown: number, total: number) => react.ReactNode;
210
+ /** 헤더의 열별 메뉴(이름 변경 / 숨기기 / 왼쪽·오른쪽으로 이동)를 켭니다, seed `Menu`. */
211
+ columnMenu?: boolean;
212
+ /** 헤더를 드래그해 열 순서를 바꿀 수 있게 합니다. @default true */
213
+ reorderable?: boolean;
214
+ /** 열 경계를 드래그해 너비를 바꿀 수 있게 합니다 (열별로 `resizable: false`로 끌 수
215
+ * 있습니다). @default true */
216
+ resizable?: boolean;
217
+ /** 제어할 열 순서(key 목록)입니다. */
218
+ columnOrder?: string[];
219
+ /** 비제어 방식으로 사용할 때의 초기 열 순서입니다. */
220
+ defaultColumnOrder?: string[];
221
+ /** 열 순서가 바뀔 때(드래그 재배치, 메뉴의 이동) 호출됩니다. */
222
+ onColumnOrderChange?: (order: string[]) => void;
223
+ /** 제어할, 숨겨진 열의 key 목록입니다. */
224
+ hiddenColumnKeys?: string[];
225
+ /** 비제어 방식으로 사용할 때의 초기 숨김 목록입니다. */
226
+ defaultHiddenColumnKeys?: string[];
227
+ /** 숨김 목록이 바뀔 때 호출됩니다. */
228
+ onHiddenColumnKeysChange?: (hidden: string[]) => void;
229
+ /** 제어할 열 너비(px) 맵입니다. */
230
+ columnWidths?: Record<string, number>;
231
+ /** 비제어 방식으로 사용할 때의 초기 열 너비 맵입니다. */
232
+ defaultColumnWidths?: Record<string, number>;
233
+ /** 열 너비가 바뀔 때(드래그 리사이즈) 호출됩니다. */
234
+ onColumnWidthsChange?: (widths: Record<string, number>) => void;
235
+ /** 제어할 열 이름 오버라이드 맵입니다 (열 메뉴의 인라인 이름 변경). */
236
+ columnLabels?: Record<string, string>;
237
+ /** 비제어 방식으로 사용할 때의 초기 열 이름 오버라이드입니다. */
238
+ defaultColumnLabels?: Record<string, string>;
239
+ /** 열 이름이 바뀔 때 호출됩니다. */
240
+ onColumnLabelsChange?: (labels: Record<string, string>) => void;
241
+ /** 순서/숨김/이름 중 무엇이든 바뀌어 "실제로 보여줄 열 목록"이 달라질 때마다,
242
+ * 그 결과(순서·숨김·이름이 반영된 `columns`)를 통째로 받는 편의 콜백입니다 --
243
+ * 위 세 쌍의 콜백을 각각 다루는 대신 한 곳에서 "지금 보이는 열 구성"을 저장하고
244
+ * 싶은 소비자를 위한 것입니다. */
245
+ onColumnsChange?: (columns: DataTableColumn<TRow>[]) => void;
246
+ /** 제어할, 열별 ColumnCalc 연산입니다. */
247
+ columnCalcOps?: Record<string, DataTableCalcOp>;
248
+ /** 비제어 방식으로 사용할 때의 초기 열별 ColumnCalc 연산입니다. */
249
+ defaultColumnCalcOps?: Record<string, DataTableCalcOp>;
250
+ /** 한 열의 ColumnCalc 연산이 바뀔 때 호출됩니다. */
251
+ onColumnCalcOpsChange?: (ops: Record<string, DataTableCalcOp>) => void;
252
+ /** 가로 스크롤 중 맨 앞 열(선택 열이 있으면 그것까지 포함)을 고정하고, 스크롤
253
+ * 그림자를 보여줍니다. */
254
+ stickyFirst?: boolean;
255
+ /** 가로 스크롤 중 맨 뒤 열을 고정합니다. */
256
+ stickyLast?: boolean;
257
+ /**
258
+ * `editable` 셀의 인라인 편집 커밋입니다. `Promise`를 반환하면 그 동안 셀이
259
+ * pending 상태(위젯 옆 스피너)를 보이고, resolve되면 편집이 닫히고, reject되면
260
+ * 마지막으로 알려진 값으로 되돌아가면서 셀 아래 거절 메시지를 보여줍니다 (crisp의
261
+ * 비동기 인식 `onEdit`과 동일한 계약). 동기(값 반환 없음)면 그 자리에서 즉시
262
+ * 커밋됩니다.
263
+ */
264
+ onEdit?: (id: string, key: string, value: string) => Promise<void> | void;
265
+ /** 행을 클릭할 수 있게 하는 호버 표시입니다 (crisp의 3%/4% 포그라운드 틴트). 커서
266
+ * 모양은 `onRowClick`이 있는지에 따로 달려 있습니다 -- 이 값과 독립적으로 켤 수
267
+ * 있습니다(crisp의 두 prop, `interactive`/`onRowClick`도 서로 독립적입니다). */
268
+ interactive?: boolean;
269
+ /** 행(선택/편집 대상이 아닌 셀)을 클릭했을 때 호출됩니다 -- 나중에 레코드 패널을
270
+ * 여는 용도 등에 씁니다. */
271
+ onRowClick?: (row: TRow) => void;
272
+ /** 주어지면 맨 아래에 crisp가 측정한 "+ New" 추가 행 버튼을 렌더링합니다. */
273
+ onAddRow?: () => void;
274
+ /** 추가 행 버튼의 라벨입니다. @default "New record" */
275
+ addRowLabel?: react.ReactNode;
276
+ /** 전체 본문을 이 상태로 대체합니다(로딩 아이콘 + `loadingLabel`) -- 필터/정렬
277
+ * 재조회나 최초 로드처럼 행 전체가 아직 없을 때를 위한 것입니다. `error`/빈 상태
278
+ * 보다 우선합니다. 인라인 스크롤 로딩 표시기(`onLoadMore`)와는 별개입니다. */
279
+ loading?: boolean;
280
+ /** `loading`일 때 스피너 옆에 보이는 텍스트입니다. @default "Loading..." */
281
+ loadingLabel?: react.ReactNode;
282
+ /** 주어지면 행 대신 이 내용을 본문에 렌더링합니다(crisp의 `.crisp-datatable-empty`
283
+ * 칸 재사용). `loading`보다는 아래, 빈 상태보다는 위 우선순위입니다. */
284
+ error?: react.ReactNode;
285
+ /** 검색/필터를 거치고도 행이 하나도 없을 때 본문에 렌더링할 내용입니다.
286
+ * @default "No results." */
287
+ empty?: react.ReactNode;
288
+ /**
289
+ * Attio 스타일 무한 스크롤: 스크롤 컨테이너 바닥 근처의 sentinel 행이 뷰에 들어오면
290
+ * 호출됩니다. `Promise`를 반환하면 그동안 자동으로 `loadingMore`가 켜집니다(직접
291
+ * 제어하려면 `loadingMore`를 명시하세요). 생략하면 무한 스크롤 자체가 꺼집니다 --
292
+ * sentinel 행도, observer도 전혀 만들어지지 않습니다. 수동 페이지네이션 UI는 없습니다.
293
+ */
294
+ onLoadMore?: () => void | Promise<void>;
295
+ /** 다음 페이지가 더 있는지 여부입니다. `false`가 되면 sentinel 행이 사라지고 더는
296
+ * 트리거되지 않습니다. @default true */
297
+ hasMore?: boolean;
298
+ /** 이미 로드가 진행 중인지 여부(제어형) -- 생략하면 `onLoadMore`가 반환한
299
+ * `Promise`로부터 내부적으로 계산합니다. */
300
+ loadingMore?: boolean;
301
+ /**
302
+ * 스크롤 컨테이너(`.crisp-table-scroll`에 대응하는 요소) 자체에 얹는 props입니다 --
303
+ * crisp의 `scrollProps` 계약과 동일: 예) `scrollProps={{ style: { maxHeight:
304
+ * "calc(100vh - 220px)" } }}`로 바운드된(내부 스크롤하는) 테이블을 만듭니다.
305
+ * `ref`/`onScroll`은 내부에서 소유합니다(가로 스크롤 그림자 추적용이라 전달해도
306
+ * 무시됩니다). 생략하면(기본값) 이 요소는 높이를 스스로 갖지 않고 콘텐츠 높이만큼
307
+ * 자라며, `flex: 1; min-height: 0`이 항상 켜져 있으므로 `<DataTable>` 자신에게
308
+ * `style={{ height: "100%" }}` 같은 바운드를 주는 것만으로도 "전체 높이" 모드가
309
+ * 됩니다 -- 새 prop 없이 기존 `style` 전달만으로 두 모드 다 지원됩니다.
310
+ */
311
+ scrollProps?: Omit<react.ComponentPropsWithoutRef<"div">, "ref" | "onScroll">;
312
+ }
313
+ /**
314
+ * @see https://seed-design.io/react/components/data-table
315
+ */
316
+ declare const DataTable: <TRow extends {
317
+ id: string;
318
+ }>(props: DataTableProps<TRow> & {
319
+ ref?: react.ForwardedRef<HTMLDivElement>;
320
+ }) => react.ReactElement;
321
+
322
+ export { DataTable };
323
+ export type { DataTableColumn, DataTableProps };
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ function __insertCSS(code) {
2
+ if (!code || typeof document == 'undefined') return
3
+ let head = document.head || document.getElementsByTagName('head')[0]
4
+ let style = document.createElement('style')
5
+ style.type = 'text/css'
6
+ head.appendChild(style)
7
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
8
+ }
9
+
10
+ export { E as DataTable } from './DataTable-12s-Bd20OuMs.js';
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ var jsxRuntime = require('react/jsx-runtime');
12
+ var DataTable12sZml1x9Kr12s = require('./DataTable-12s-Zml1x9Kr-12s-CTRzgvMT.cjs');
13
+
14
+ /**
15
+ * The column-edge resize grip -- geometry ported from crisp's `resizablepanel`
16
+ * recipe's "column" variant (`resizablepanel.recipe.ts`): a 7px hit target straddling
17
+ * the 1px grid line (not a 1px target of its own -- far too thin to reliably grab),
18
+ * its 3px inner bar invisible at rest and brand-solid on hover/drag. Uses native
19
+ * Pointer Capture (`setPointerCapture`) rather than a `window`-level listener pair:
20
+ * once captured, this SAME element keeps receiving `pointermove`/`pointerup` even
21
+ * after the cursor leaves it, so the drag math can live entirely in this one
22
+ * handler set with no effect/cleanup to get wrong.
23
+ */ function ColumnResizeHandle({ columnKey, width }) {
24
+ const { beginColumnResize, updateColumnResize, endColumnResize, isColumnResizing } = DataTable12sZml1x9Kr12s.useDataTableContext();
25
+ return /*#__PURE__*/ jsxRuntime.jsx("span", {
26
+ // Mouse/pointer-drag only for now (no keyboard equivalent yet) -- `aria-hidden`
27
+ // rather than `role="separator"` + `aria-label`, which would otherwise fold
28
+ // "Resize column" into the OWNING header cell's own accessible name (a
29
+ // columnheader's name is the concatenation of its own text plus every
30
+ // descendant's), turning "Name" into "Name Resize column" for anyone using an
31
+ // accessible-name query -- including this file's own tests.
32
+ "aria-hidden": "true",
33
+ className: "seed-data-table-resize-handle",
34
+ "data-resizing": isColumnResizing(columnKey),
35
+ draggable: false,
36
+ onPointerDown: (event)=>{
37
+ event.stopPropagation();
38
+ event.preventDefault();
39
+ event.currentTarget.setPointerCapture(event.pointerId);
40
+ const startWidth = width ?? event.currentTarget.closest('[role="columnheader"]')?.getBoundingClientRect().width ?? 150;
41
+ beginColumnResize(columnKey, event.clientX, startWidth);
42
+ },
43
+ onPointerMove: (event)=>{
44
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
45
+ updateColumnResize(event.clientX);
46
+ }
47
+ },
48
+ onPointerUp: (event)=>{
49
+ event.currentTarget.releasePointerCapture(event.pointerId);
50
+ endColumnResize();
51
+ },
52
+ children: /*#__PURE__*/ jsxRuntime.jsx("span", {
53
+ className: "seed-data-table-resize-handle-inner"
54
+ })
55
+ });
56
+ }
57
+
58
+ exports.ColumnResizeHandle = ColumnResizeHandle;
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ import { jsx } from 'react/jsx-runtime';
12
+ import { u as useDataTableContext } from './DataTable-12s-Zml1x9Kr-12s-Coi-3lUa.js';
13
+
14
+ /**
15
+ * The column-edge resize grip -- geometry ported from crisp's `resizablepanel`
16
+ * recipe's "column" variant (`resizablepanel.recipe.ts`): a 7px hit target straddling
17
+ * the 1px grid line (not a 1px target of its own -- far too thin to reliably grab),
18
+ * its 3px inner bar invisible at rest and brand-solid on hover/drag. Uses native
19
+ * Pointer Capture (`setPointerCapture`) rather than a `window`-level listener pair:
20
+ * once captured, this SAME element keeps receiving `pointermove`/`pointerup` even
21
+ * after the cursor leaves it, so the drag math can live entirely in this one
22
+ * handler set with no effect/cleanup to get wrong.
23
+ */ function ColumnResizeHandle({ columnKey, width }) {
24
+ const { beginColumnResize, updateColumnResize, endColumnResize, isColumnResizing } = useDataTableContext();
25
+ return /*#__PURE__*/ jsx("span", {
26
+ // Mouse/pointer-drag only for now (no keyboard equivalent yet) -- `aria-hidden`
27
+ // rather than `role="separator"` + `aria-label`, which would otherwise fold
28
+ // "Resize column" into the OWNING header cell's own accessible name (a
29
+ // columnheader's name is the concatenation of its own text plus every
30
+ // descendant's), turning "Name" into "Name Resize column" for anyone using an
31
+ // accessible-name query -- including this file's own tests.
32
+ "aria-hidden": "true",
33
+ className: "seed-data-table-resize-handle",
34
+ "data-resizing": isColumnResizing(columnKey),
35
+ draggable: false,
36
+ onPointerDown: (event)=>{
37
+ event.stopPropagation();
38
+ event.preventDefault();
39
+ event.currentTarget.setPointerCapture(event.pointerId);
40
+ const startWidth = width ?? event.currentTarget.closest('[role="columnheader"]')?.getBoundingClientRect().width ?? 150;
41
+ beginColumnResize(columnKey, event.clientX, startWidth);
42
+ },
43
+ onPointerMove: (event)=>{
44
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
45
+ updateColumnResize(event.clientX);
46
+ }
47
+ },
48
+ onPointerUp: (event)=>{
49
+ event.currentTarget.releasePointerCapture(event.pointerId);
50
+ endColumnResize();
51
+ },
52
+ children: /*#__PURE__*/ jsx("span", {
53
+ className: "seed-data-table-resize-handle-inner"
54
+ })
55
+ });
56
+ }
57
+
58
+ export { ColumnResizeHandle as C };
@@ -0,0 +1,125 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ var jsxRuntime = require('react/jsx-runtime');
12
+ require('react');
13
+ var DataTable12s = require('./DataTable-12s-8LMBtS2u.cjs');
14
+ var DataTable12sZml1x9Kr12s = require('./DataTable-12s-Zml1x9Kr-12s-CTRzgvMT.cjs');
15
+
16
+ /**
17
+ * A small inline spinner, not seed's own `ProgressCircle` -- that component's
18
+ * smallest size (24px) is already bigger than crisp's own measured 16px loading
19
+ * icon, the same gap `editors.tsx`'s `EditPendingIndicator` already works around for
20
+ * its own (smaller still, 14px) in-cell pending spinner. Same shape, one size up,
21
+ * reusing the SAME global `rotate` keyframe (this file's own qvism recipe slot
22
+ * nests `.seed-data-table-loading-spinner` under it) rather than a third near-
23
+ * identical spinner.
24
+ */ function LoadingSpinner() {
25
+ return /*#__PURE__*/ jsxRuntime.jsxs("svg", {
26
+ className: "seed-data-table-loading-spinner",
27
+ width: 16,
28
+ height: 16,
29
+ viewBox: "0 0 16 16",
30
+ fill: "none",
31
+ role: "presentation",
32
+ "aria-hidden": "true",
33
+ children: [
34
+ /*#__PURE__*/ jsxRuntime.jsx("circle", {
35
+ cx: 8,
36
+ cy: 8,
37
+ r: 6.5,
38
+ stroke: "currentColor",
39
+ strokeOpacity: 0.25,
40
+ strokeWidth: 2
41
+ }),
42
+ /*#__PURE__*/ jsxRuntime.jsx("path", {
43
+ d: "M14.5 8a6.5 6.5 0 0 0-6.5-6.5",
44
+ stroke: "currentColor",
45
+ strokeWidth: 2,
46
+ strokeLinecap: "round"
47
+ })
48
+ ]
49
+ });
50
+ }
51
+ /** The full-body empty/error row's own content -- one `<tr>`/`<td colSpan>` wrapping
52
+ * this, rendered by `DataTable.tsx` itself (it already owns `totalCols`). Shared by
53
+ * `empty` and `error` alike, matching crisp's own single `.crisp-datatable-empty`
54
+ * class doing double duty for both. */ function EmptyStateContent({ children, classNames }) {
55
+ return /*#__PURE__*/ jsxRuntime.jsx("div", {
56
+ className: classNames.emptyState,
57
+ children: children
58
+ });
59
+ }
60
+ /** The full-body loading row's own content (spinner + `loadingLabel`). */ function LoadingStateContent({ label, classNames }) {
61
+ return /*#__PURE__*/ jsxRuntime.jsxs("div", {
62
+ className: classNames.loadingState,
63
+ children: [
64
+ /*#__PURE__*/ jsxRuntime.jsx(LoadingSpinner, {}),
65
+ label
66
+ ]
67
+ });
68
+ }
69
+ /**
70
+ * Infinite scroll's own sentinel + "loading more" row -- the LAST child of
71
+ * `<tbody>` while `hasMore` (the headless `useDataTableInfiniteScroll`'s own
72
+ * return, read off context the same way `SelectAllCheckbox` reads `selectionState`).
73
+ * Renders nothing once `hasMore` is false -- there is no manual pagination
74
+ * fallback, matching DATATABLE-ATTIO-SPEC.md's auto-load-only contract. While a
75
+ * fetch is in flight (`loadingMore`) the SAME row shows the compact spinner
76
+ * (`loadingState`'s own `[data-inline]` modifier, a normal 36px row); at rest it's
77
+ * an empty sentinel row the IntersectionObserver watches.
78
+ */ function InfiniteScrollRow({ colSpan, classNames, loadingLabel }) {
79
+ const { sentinelRef, hasMore, loadingMore } = DataTable12sZml1x9Kr12s.useDataTableContext();
80
+ if (!hasMore) return null;
81
+ return /*#__PURE__*/ jsxRuntime.jsx("tr", {
82
+ ref: sentinelRef,
83
+ "aria-hidden": loadingMore ? undefined : "true",
84
+ children: /*#__PURE__*/ jsxRuntime.jsx(DataTable12s.DataTableCell, {
85
+ colSpan: colSpan,
86
+ children: loadingMore && /*#__PURE__*/ jsxRuntime.jsxs("div", {
87
+ className: classNames.loadingState,
88
+ "data-inline": "",
89
+ children: [
90
+ /*#__PURE__*/ jsxRuntime.jsx(LoadingSpinner, {}),
91
+ loadingLabel
92
+ ]
93
+ })
94
+ })
95
+ });
96
+ }
97
+ /** The trailing "+ New" add-row footer -- crisp's own `.crisp-datatable-addrow`
98
+ * inside a zero-padding `.crisp-datatable-addcell`, its own `<tfoot>` (a table can
99
+ * have more than one; the ColumnCalc summary footer, when both are present, is a
100
+ * separate one, matching crisp's own two independent `<TableFooter>` rows). */ function AddRowFooter({ onAddRow, addRowLabel, colSpan, classNames }) {
101
+ return /*#__PURE__*/ jsxRuntime.jsx("tfoot", {
102
+ children: /*#__PURE__*/ jsxRuntime.jsx("tr", {
103
+ children: /*#__PURE__*/ jsxRuntime.jsx("td", {
104
+ className: classNames.addRowCell,
105
+ colSpan: colSpan,
106
+ children: /*#__PURE__*/ jsxRuntime.jsxs("button", {
107
+ type: "button",
108
+ className: classNames.addRow,
109
+ onClick: onAddRow,
110
+ children: [
111
+ /*#__PURE__*/ jsxRuntime.jsx(DataTable12s.IconPlusLine_default, {
112
+ size: 15
113
+ }),
114
+ addRowLabel
115
+ ]
116
+ })
117
+ })
118
+ })
119
+ });
120
+ }
121
+
122
+ exports.AddRowFooter = AddRowFooter;
123
+ exports.EmptyStateContent = EmptyStateContent;
124
+ exports.InfiniteScrollRow = InfiniteScrollRow;
125
+ exports.LoadingStateContent = LoadingStateContent;