@canvas-components/react-list-table 0.2.6 → 0.3.1

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/README.md CHANGED
@@ -2,6 +2,187 @@
2
2
 
3
3
  `@canvas-components/react-list-table` 是 `@canvas-components/list-table` 的 React 适配层,负责把原生 Canvas 表格接入 React 应用,并提供更接近业务开发习惯的组件化入口。
4
4
 
5
+ ## 示例效果
6
+
7
+ ![React List Table 基础示例](./assets/examples/basic.gif)
8
+
9
+ ### 完整示例图集
10
+
11
+ <details>
12
+ <summary>100w 数据渲染</summary>
13
+
14
+ ![100w 数据渲染](./assets/examples/million-rows.gif)
15
+
16
+ </details>
17
+
18
+ <details>
19
+ <summary>行选择</summary>
20
+
21
+ ![行选择](./assets/examples/row-selection.gif)
22
+
23
+ </details>
24
+
25
+ <details>
26
+ <summary>单元格内容</summary>
27
+
28
+ ![单元格内容](./assets/examples/cell-content.gif)
29
+
30
+ </details>
31
+
32
+ <details>
33
+ <summary>斑马条纹</summary>
34
+
35
+ ![斑马条纹](./assets/examples/striped.gif)
36
+
37
+ </details>
38
+
39
+ <details>
40
+ <summary>主题切换</summary>
41
+
42
+ ![主题切换](./assets/examples/theme.gif)
43
+
44
+ </details>
45
+
46
+ <details>
47
+ <summary>高亮模式</summary>
48
+
49
+ ![高亮模式](./assets/examples/highlight-mode.gif)
50
+
51
+ </details>
52
+
53
+ <details>
54
+ <summary>右键菜单</summary>
55
+
56
+ ![右键菜单](./assets/examples/context-menu.gif)
57
+
58
+ </details>
59
+
60
+ <details>
61
+ <summary>数据格式</summary>
62
+
63
+ ![数据格式](./assets/examples/data-format.gif)
64
+
65
+ </details>
66
+
67
+ <details>
68
+ <summary>省略提示</summary>
69
+
70
+ ![省略提示](./assets/examples/ellipsis-tooltip.gif)
71
+
72
+ </details>
73
+
74
+ <details>
75
+ <summary>可展开行</summary>
76
+
77
+ ![可展开行](./assets/examples/expandable.gif)
78
+
79
+ </details>
80
+
81
+ <details>
82
+ <summary>单元格编辑</summary>
83
+
84
+ ![单元格编辑](./assets/examples/editable-cell.gif)
85
+
86
+ </details>
87
+
88
+ <details>
89
+ <summary>远程查询</summary>
90
+
91
+ ![远程查询](./assets/examples/remote-query.gif)
92
+
93
+ </details>
94
+
95
+ <details>
96
+ <summary>表格分页</summary>
97
+
98
+ ![表格分页](./assets/examples/pagination.gif)
99
+
100
+ </details>
101
+
102
+ <details>
103
+ <summary>空状态</summary>
104
+
105
+ ![空状态](./assets/examples/empty-state.gif)
106
+
107
+ </details>
108
+
109
+ <details>
110
+ <summary>宽高配置</summary>
111
+
112
+ ![宽高配置](./assets/examples/sized.gif)
113
+
114
+ </details>
115
+
116
+ <details>
117
+ <summary>表头排序、过滤、固定</summary>
118
+
119
+ ![表头排序、过滤、固定](./assets/examples/header-actions.gif)
120
+
121
+ </details>
122
+
123
+ <details>
124
+ <summary>多级表头</summary>
125
+
126
+ ![多级表头](./assets/examples/multi-header.gif)
127
+
128
+ </details>
129
+
130
+ <details>
131
+ <summary>汇总行</summary>
132
+
133
+ ![汇总行](./assets/examples/summary-row.gif)
134
+
135
+ </details>
136
+
137
+ <details>
138
+ <summary>合并行列</summary>
139
+
140
+ ![合并行列](./assets/examples/merged-cells.gif)
141
+
142
+ </details>
143
+
144
+ <details>
145
+ <summary>拖动改变列宽</summary>
146
+
147
+ ![拖动改变列宽](./assets/examples/resize.gif)
148
+
149
+ </details>
150
+
151
+ <details>
152
+ <summary>轮播滚动</summary>
153
+
154
+ ![轮播滚动](./assets/examples/carousel-scroll.gif)
155
+
156
+ </details>
157
+
158
+ <details>
159
+ <summary>缓存配置</summary>
160
+
161
+ ![缓存配置](./assets/examples/cached-columns.gif)
162
+
163
+ </details>
164
+
165
+ <details>
166
+ <summary>拖拽表头排序</summary>
167
+
168
+ ![拖拽表头排序](./assets/examples/draggable.gif)
169
+
170
+ </details>
171
+
172
+ <details>
173
+ <summary>跨表行拖拽</summary>
174
+
175
+ ![跨表行拖拽](./assets/examples/row-drag.gif)
176
+
177
+ </details>
178
+
179
+ <details>
180
+ <summary>超级示例</summary>
181
+
182
+ ![超级示例](./assets/examples/super.gif)
183
+
184
+ </details>
185
+
5
186
  ## 功能作用
6
187
 
7
188
  - 提供 `ReactListTable` 组件
@@ -93,20 +274,20 @@ export function DemoTable() {
93
274
 
94
275
  ### `ReactListTableToolbarOptions`
95
276
 
96
- | 属性 | 类型 | 说明 |
97
- | -------------------- | ---------------------------- | ---------------------------- |
98
- | `showRefresh` | `boolean` | 是否显示刷新按钮 |
99
- | `showFullscreen` | `boolean` | 是否显示全屏按钮 |
100
- | `showDensity` | `boolean` | 是否显示密度切换 |
101
- | `showColumnSetting` | `boolean` | 是否显示列设置 |
102
- | `columnSettingWidth` | `number \| string` | 列设置面板宽度 |
103
- | `columnSettingHeaderExtra` | `ReactNode` | 列设置标题区中重置按钮前的扩展内容 |
104
- | `showExport` | `boolean` | 是否显示导出按钮 |
105
- | `exportOptions` | `ListTableExportXlsxOptions` | 默认导出的文件名和汇总行配置 |
106
- | `onRefresh` | `() => void` | 点击刷新回调 |
107
- | `onExport` | `() => void` | 自定义导出回调 |
108
- | `getFullscreenNode` | `() => HTMLElement \| null` | 自定义全屏节点 |
109
- | `extra` | `ReactNode` | 工具栏右侧额外内容 |
277
+ | 属性 | 类型 | 说明 |
278
+ | -------------------------- | ---------------------------- | ---------------------------------- |
279
+ | `showRefresh` | `boolean` | 是否显示刷新按钮 |
280
+ | `showFullscreen` | `boolean` | 是否显示全屏按钮 |
281
+ | `showDensity` | `boolean` | 是否显示密度切换 |
282
+ | `showColumnSetting` | `boolean` | 是否显示列设置 |
283
+ | `columnSettingWidth` | `number \| string` | 列设置面板宽度 |
284
+ | `columnSettingHeaderExtra` | `ReactNode` | 列设置标题区中重置按钮前的扩展内容 |
285
+ | `showExport` | `boolean` | 是否显示导出按钮 |
286
+ | `exportOptions` | `ListTableExportXlsxOptions` | 默认导出的文件名和汇总行配置 |
287
+ | `onRefresh` | `() => void` | 点击刷新回调 |
288
+ | `onExport` | `() => void` | 自定义导出回调 |
289
+ | `getFullscreenNode` | `() => HTMLElement \| null` | 自定义全屏节点 |
290
+ | `extra` | `ReactNode` | 工具栏右侧额外内容 |
110
291
 
111
292
  ## 方法说明
112
293
 
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/index.cjs CHANGED
@@ -588,6 +588,14 @@ function DensityMenu(props) {
588
588
  onClick: () => {
589
589
  onChange("compact");
590
590
  }
591
+ },
592
+ {
593
+ key: "density-auto",
594
+ label: "\u81EA\u9002\u5E94",
595
+ selected: value === "auto",
596
+ onClick: () => {
597
+ onChange("auto");
598
+ }
591
599
  }
592
600
  ]
593
601
  }
@@ -3354,7 +3362,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
3354
3362
  }
3355
3363
 
3356
3364
  // src/features/toolbar/style.css
3357
- styleInject('.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, .14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 6px 10px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu button.active::after {\n content: "\\2713";\n margin-left: 12px;\n font-size: 12px;\n}\n.react-list-table-tool-menu button:hover,\n.react-list-table-tool-menu button.active {\n color: #1677ff;\n background: #e6f4ff;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 12px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n');
3365
+ styleInject('.react-list-table-shell {\n background: #fff;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, .14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 6px 10px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu button.active::after {\n content: "\\2713";\n margin-left: 12px;\n font-size: 12px;\n}\n.react-list-table-tool-menu button:hover,\n.react-list-table-tool-menu button.active {\n color: #1677ff;\n background: #e6f4ff;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 12px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n');
3358
3366
  function ReactTableToolbar(props) {
3359
3367
  const { toolbar, title } = props;
3360
3368
  const [densityOpen, setDensityOpen] = react.useState(false);
@@ -3378,11 +3386,14 @@ function ReactTableToolbar(props) {
3378
3386
  react.useEffect(() => {
3379
3387
  const handleFullscreenChange = () => {
3380
3388
  const fullscreenNode = (typeof toolbar === "object" ? toolbar.getFullscreenNode?.() : null) ?? props.hostRef.current;
3381
- setIsFullscreen(document.fullscreenElement === fullscreenNode);
3389
+ const nextIsFullscreen = document.fullscreenElement === fullscreenNode;
3390
+ setIsFullscreen(nextIsFullscreen);
3391
+ props.onFullscreenChange?.(nextIsFullscreen);
3382
3392
  };
3393
+ handleFullscreenChange();
3383
3394
  document.addEventListener("fullscreenchange", handleFullscreenChange);
3384
3395
  return () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
3385
- }, [props.hostRef, toolbar]);
3396
+ }, [props.hostRef, props.onFullscreenChange, toolbar]);
3386
3397
  if (!title && !hasToolbar) return null;
3387
3398
  const config = typeof toolbar === "object" ? toolbar : {};
3388
3399
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-list-table-titlebar", ref: rootRef, children: [
@@ -3714,6 +3725,12 @@ function useReactListTableState(options) {
3714
3725
  }
3715
3726
 
3716
3727
  // src/components/build-list-table-options.ts
3728
+ function resolveDensityLinkedHeight(densityOverride, configuredHeight) {
3729
+ return typeof densityOverride === "number" ? densityOverride : configuredHeight;
3730
+ }
3731
+ function resolveFullscreenTableSize(width, height, isFullscreen) {
3732
+ return isFullscreen ? { width: "100%", height: "100%" } : { width, height };
3733
+ }
3717
3734
  function buildListTableOptions(props) {
3718
3735
  const {
3719
3736
  width,
@@ -3728,6 +3745,7 @@ function buildListTableOptions(props) {
3728
3745
  return {
3729
3746
  type: "list",
3730
3747
  ...restProps,
3748
+ rowHeight: restProps.rowHeight ?? 32,
3731
3749
  width: scroll?.x === true ? "100%" : scroll?.x ?? width,
3732
3750
  height: scroll?.y ?? height,
3733
3751
  emptyText: locale?.emptyText ?? emptyText,
@@ -3778,9 +3796,8 @@ function ReactListTableInner(props, ref) {
3778
3796
  toolbar
3779
3797
  } = props;
3780
3798
  const shellRef = react.useRef(null);
3781
- const [densityOverride, setDensityOverride] = react.useState(
3782
- null
3783
- );
3799
+ const [isFullscreen, setIsFullscreen] = react.useState(false);
3800
+ const [densityOverride, setDensityOverride] = react.useState(null);
3784
3801
  const [columnVisibility, setColumnVisibility] = react.useState(() => /* @__PURE__ */ new Map());
3785
3802
  const [columnOrders, setColumnOrders] = react.useState(
3786
3803
  () => /* @__PURE__ */ new Map()
@@ -3796,8 +3813,14 @@ function ReactListTableInner(props, ref) {
3796
3813
  [columns, columnVisibility, columnOrders, columnConfig]
3797
3814
  );
3798
3815
  const resolvedRowHeight = densityOverride ?? rowHeight;
3799
- const resolvedHeaderRowHeight = densityOverride ?? headerRowHeight;
3800
- const resolvedSummaryRowHeight = densityOverride ?? summaryRowHeight;
3816
+ const resolvedHeaderRowHeight = resolveDensityLinkedHeight(
3817
+ densityOverride,
3818
+ headerRowHeight
3819
+ );
3820
+ const resolvedSummaryRowHeight = resolveDensityLinkedHeight(
3821
+ densityOverride,
3822
+ summaryRowHeight
3823
+ );
3801
3824
  const options = react.useMemo(() => {
3802
3825
  const nextOptions = buildListTableOptions({
3803
3826
  width,
@@ -3834,8 +3857,14 @@ function ReactListTableInner(props, ref) {
3834
3857
  rowDrag,
3835
3858
  debug
3836
3859
  });
3860
+ const fullscreenSize = resolveFullscreenTableSize(
3861
+ nextOptions.width,
3862
+ nextOptions.height,
3863
+ isFullscreen
3864
+ );
3837
3865
  return {
3838
3866
  ...nextOptions,
3867
+ ...fullscreenSize,
3839
3868
  ui: {
3840
3869
  ...nextOptions.ui,
3841
3870
  onDensityChange: setDensityOverride
@@ -3874,7 +3903,8 @@ function ReactListTableInner(props, ref) {
3874
3903
  draggable,
3875
3904
  tableId,
3876
3905
  rowDrag,
3877
- debug
3906
+ debug,
3907
+ isFullscreen
3878
3908
  ]);
3879
3909
  const {
3880
3910
  containerRef,
@@ -3917,7 +3947,11 @@ function ReactListTableInner(props, ref) {
3917
3947
  "div",
3918
3948
  {
3919
3949
  ref: shellRef,
3920
- className,
3950
+ className: [
3951
+ "react-list-table-shell",
3952
+ isFullscreen ? "react-list-table-shell-fullscreen" : "",
3953
+ className
3954
+ ].filter(Boolean).join(" "),
3921
3955
  style: {
3922
3956
  position: "relative",
3923
3957
  display: "flex",
@@ -3932,8 +3966,9 @@ function ReactListTableInner(props, ref) {
3932
3966
  title,
3933
3967
  toolbar,
3934
3968
  columns: resolvedColumns,
3935
- density: resolvedRowHeight === "auto" ? 32 : resolvedRowHeight ?? 32,
3969
+ density: resolvedRowHeight ?? 32,
3936
3970
  hostRef: shellRef,
3971
+ onFullscreenChange: setIsFullscreen,
3937
3972
  onExport: (options2) => tableRef.current?.exportXlsx(options2),
3938
3973
  onDensityChange: (density) => {
3939
3974
  tableRef.current?.changeDensity(density);
@@ -3987,6 +4022,7 @@ function ReactListTableInner(props, ref) {
3987
4022
  "div",
3988
4023
  {
3989
4024
  ref: containerRef,
4025
+ className: "react-list-table-host",
3990
4026
  style: { flex: 1, minWidth: 0, width: "100%", minHeight: 0 },
3991
4027
  children: /* @__PURE__ */ jsxRuntime.jsx(ReactTablePaginationView, { config: paginationConfig })
3992
4028
  }