@canvas-components/react-list-table 0.3.20 → 0.3.21

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
@@ -321,6 +321,8 @@ import {
321
321
  | `scrollTo` | `(options) => void` | 滚动到指定行 |
322
322
  | `getDebugSnapshot` | `() => ListTableDebugSnapshot \| null` | 获取调试快照 |
323
323
 
324
+ 导出文件名弹窗使用 `react-components` 的 `ReactModal`、`ReactInput`,默认开启,文件后缀不可编辑。通过表格 `exportOptions={{ fileName: "报表", promptFileName: false }}` 可配置默认名称并跳过弹窗;工具栏 `toolbar.exportOptions` 或实例 `exportXlsx` 的同名选项可覆盖默认配置。取消不会生成文件。XLSX 默认追加当前已显示的汇总行,传入 `includeSummary: false` 可关闭。
325
+
324
326
  ## 类型别名
325
327
 
326
328
  | 类型 | 说明 |
package/dist/index.cjs CHANGED
@@ -2680,6 +2680,58 @@ var toolButtonStyle = {
2680
2680
  lineHeight: 1,
2681
2681
  cursor: "pointer"
2682
2682
  };
2683
+ function ReactListTableExportNameDialog({
2684
+ request
2685
+ }) {
2686
+ const extension = request.fileName.match(/\.(xlsx|csv|txt|json)$/i)?.[0] ?? "";
2687
+ const [fileName, setFileName] = react.useState(
2688
+ extension ? request.fileName.slice(0, -extension.length) : request.fileName
2689
+ );
2690
+ const [invalid, setInvalid] = react.useState(false);
2691
+ const confirm = () => {
2692
+ if (!fileName.trim()) {
2693
+ setInvalid(true);
2694
+ return;
2695
+ }
2696
+ request.onConfirm(`${fileName.trim()}${extension}`);
2697
+ };
2698
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2699
+ reactComponents.ReactModal,
2700
+ {
2701
+ open: true,
2702
+ title: "\u5BFC\u51FA\u6587\u4EF6",
2703
+ width: 420,
2704
+ fullscreenable: false,
2705
+ onCancel: request.onCancel,
2706
+ onOk: confirm,
2707
+ okText: "\u5BFC\u51FA",
2708
+ cancelText: "\u53D6\u6D88",
2709
+ children: [
2710
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2711
+ "\u6587\u4EF6\u540D\u79F0",
2712
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2713
+ /* @__PURE__ */ jsxRuntime.jsx(
2714
+ reactComponents.ReactInput,
2715
+ {
2716
+ autoFocus: true,
2717
+ value: fileName,
2718
+ placeholder: "\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0",
2719
+ onFocus: (event) => event.target.select(),
2720
+ onChange: (event) => {
2721
+ setFileName(event.target.value);
2722
+ setInvalid(false);
2723
+ },
2724
+ onPressEnter: confirm
2725
+ }
2726
+ ),
2727
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": "\u6587\u4EF6\u540E\u7F00", style: { flexShrink: 0 }, children: extension })
2728
+ ] })
2729
+ ] }),
2730
+ invalid ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", style: { color: "#ff4d4f", marginTop: 8 }, children: "\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0" }) : null
2731
+ ]
2732
+ }
2733
+ );
2734
+ }
2683
2735
 
2684
2736
  // src/features/editor/utils.ts
2685
2737
  function toNumberValue(value) {
@@ -2781,12 +2833,14 @@ function ReactTableEditorOverlay(props) {
2781
2833
  if (!input) {
2782
2834
  return;
2783
2835
  }
2784
- focusAndSelectEditorInput(input);
2836
+ focusAndSelectEditorInput(input, request.selectOnFocus);
2785
2837
  }, [request]);
2786
2838
  return /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ReactContentScaleProvider, { scale: contentZoom, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: editorHostRef, children: renderEditorByType(request, style) }, request.editorKey) });
2787
2839
  }
2788
2840
  function renderEditorByType(request, style) {
2789
2841
  switch (request.config.valueType ?? "text") {
2842
+ case "tree-select":
2843
+ return /* @__PURE__ */ jsxRuntime.jsx(TreeSelectEditor, { request, style });
2790
2844
  case "cascader":
2791
2845
  return /* @__PURE__ */ jsxRuntime.jsx(CascaderEditor, { request, style });
2792
2846
  case "number":
@@ -2828,7 +2882,7 @@ function TextEditor(props) {
2828
2882
  const skipBlurCommitRef = react.useRef(false);
2829
2883
  react.useLayoutEffect(() => {
2830
2884
  const input = hostRef.current?.querySelector("input");
2831
- focusAndSelectEditorInput(input);
2885
+ focusAndSelectEditorInput(input, request.selectOnFocus);
2832
2886
  }, [request]);
2833
2887
  react.useEffect(() => {
2834
2888
  setValue(String(request.value ?? ""));
@@ -2868,7 +2922,7 @@ function NumberEditor(props) {
2868
2922
  const skipBlurCommitRef = react.useRef(false);
2869
2923
  react.useLayoutEffect(() => {
2870
2924
  const input = hostRef.current?.querySelector("input");
2871
- focusAndSelectEditorInput(input);
2925
+ focusAndSelectEditorInput(input, request.selectOnFocus);
2872
2926
  }, [request]);
2873
2927
  react.useEffect(() => {
2874
2928
  setValue(toNumberValue(request.value));
@@ -3077,14 +3131,56 @@ function DateEditor(props) {
3077
3131
  }
3078
3132
  ) });
3079
3133
  }
3080
- function CascaderEditor({
3134
+ function TreeSelectEditor({
3081
3135
  request,
3082
3136
  style
3083
3137
  }) {
3084
3138
  const [value, setValue] = react.useState(
3085
- Array.isArray(request.value) ? request.value : []
3139
+ request.value ?? null
3086
3140
  );
3087
3141
  const skipBlurCommitRef = react.useRef(false);
3142
+ react.useEffect(() => {
3143
+ setValue(request.value ?? null);
3144
+ }, [request.value]);
3145
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style, children: /* @__PURE__ */ jsxRuntime.jsx(
3146
+ reactComponents.ReactTreeSelect,
3147
+ {
3148
+ autoFocus: true,
3149
+ value,
3150
+ treeData: request.config.treeData,
3151
+ multiple: request.config.multiple,
3152
+ showSearch: request.config.showSearch,
3153
+ treeDefaultExpandAll: request.config.treeDefaultExpandAll,
3154
+ treeDefaultExpandedKeys: request.config.treeDefaultExpandedKeys,
3155
+ allowClear: request.config.allowClear,
3156
+ placeholder: request.config.placeholder,
3157
+ style: getEditorInputStyle(request),
3158
+ onChange: (next, _nodes, info) => {
3159
+ setValue(next);
3160
+ request.onChange(next);
3161
+ if (info.complete && info.trigger !== "clear") {
3162
+ skipBlurCommitRef.current = true;
3163
+ request.onCommit();
3164
+ }
3165
+ },
3166
+ onBlur: (event) => handleEditorBlur(event, skipBlurCommitRef, request),
3167
+ onKeyDown: (event) => {
3168
+ if (event.key === "Tab")
3169
+ handleEditorNavigationKey(event, request, skipBlurCommitRef);
3170
+ if (event.key === "Escape") {
3171
+ skipBlurCommitRef.current = true;
3172
+ handleEditorEscapeKey(event, request);
3173
+ }
3174
+ }
3175
+ }
3176
+ ) });
3177
+ }
3178
+ function CascaderEditor({
3179
+ request,
3180
+ style
3181
+ }) {
3182
+ const [value, setValue] = react.useState(Array.isArray(request.value) ? request.value : []);
3183
+ const skipBlurCommitRef = react.useRef(false);
3088
3184
  react.useEffect(() => {
3089
3185
  setValue(Array.isArray(request.value) ? request.value : []);
3090
3186
  }, [request.value]);
@@ -3098,6 +3194,8 @@ function CascaderEditor({
3098
3194
  showSearch: request.config.showSearch,
3099
3195
  allowClear: request.config.allowClear,
3100
3196
  changeOnSelect: request.config.changeOnSelect,
3197
+ multiple: request.config.multiple,
3198
+ showCheckedStrategy: request.config.showCheckedStrategy,
3101
3199
  expandTrigger: request.config.expandTrigger,
3102
3200
  displayRender: request.config.displayRender,
3103
3201
  style: getEditorInputStyle(request),
@@ -3451,13 +3549,14 @@ function handleRangeInputTab(event) {
3451
3549
  focusAndSelectEditorInput(nextInput);
3452
3550
  return true;
3453
3551
  }
3454
- function focusAndSelectEditorInput(input) {
3552
+ function focusAndSelectEditorInput(input, selectOnFocus = true) {
3455
3553
  if (!input) {
3456
3554
  return;
3457
3555
  }
3458
3556
  input.focus();
3459
3557
  try {
3460
- input.select();
3558
+ if (selectOnFocus) input.select();
3559
+ else input.setSelectionRange(input.value.length, input.value.length);
3461
3560
  } catch {
3462
3561
  }
3463
3562
  }
@@ -4223,9 +4322,14 @@ function useReactListTableState(options) {
4223
4322
  const [searchRequest, setSearchRequest] = react.useState(null);
4224
4323
  const [imagePreviewRequest, setImagePreviewRequest] = react.useState(null);
4225
4324
  const [dangerConfirmRequest, setDangerConfirmRequest] = react.useState(null);
4325
+ const [exportNameRequest, setExportNameRequest] = react.useState(null);
4226
4326
  const uiOptions = react.useMemo(
4227
4327
  () => ({
4228
4328
  ...options.ui,
4329
+ onExportNameRequest: (request) => {
4330
+ setExportNameRequest(request);
4331
+ options.ui?.onExportNameRequest?.(request);
4332
+ },
4229
4333
  onEditorRequest: (request) => {
4230
4334
  setEditorRequest(request);
4231
4335
  options.ui?.onEditorRequest?.(request);
@@ -4293,6 +4397,7 @@ function useReactListTableState(options) {
4293
4397
  setSearchRequest(null);
4294
4398
  setImagePreviewRequest(null);
4295
4399
  setDangerConfirmRequest(null);
4400
+ setExportNameRequest(null);
4296
4401
  };
4297
4402
  }, []);
4298
4403
  react.useEffect(() => {
@@ -4314,6 +4419,7 @@ function useReactListTableState(options) {
4314
4419
  searchRequest,
4315
4420
  imagePreviewRequest,
4316
4421
  dangerConfirmRequest,
4422
+ exportNameRequest,
4317
4423
  closeDangerConfirmRequest: () => {
4318
4424
  setDangerConfirmRequest(null);
4319
4425
  options.ui?.onDangerConfirmRequest?.(null);
@@ -4418,7 +4524,8 @@ function ReactListTableInner(props, ref) {
4418
4524
  rowDrag,
4419
4525
  debug,
4420
4526
  title,
4421
- toolbar
4527
+ toolbar,
4528
+ exportOptions
4422
4529
  } = props;
4423
4530
  const inheritedDebugEnabled = useReactListTableDebugEnabled();
4424
4531
  const resolvedDebug = react.useMemo(
@@ -4459,6 +4566,7 @@ function ReactListTableInner(props, ref) {
4459
4566
  );
4460
4567
  const options = react.useMemo(() => {
4461
4568
  const nextOptions = buildListTableOptions({
4569
+ exportOptions,
4462
4570
  width,
4463
4571
  height,
4464
4572
  emptyText,
@@ -4520,6 +4628,7 @@ function ReactListTableInner(props, ref) {
4520
4628
  }
4521
4629
  };
4522
4630
  }, [
4631
+ exportOptions,
4523
4632
  width,
4524
4633
  height,
4525
4634
  emptyText,
@@ -4575,6 +4684,7 @@ function ReactListTableInner(props, ref) {
4575
4684
  searchRequest,
4576
4685
  imagePreviewRequest,
4577
4686
  dangerConfirmRequest,
4687
+ exportNameRequest,
4578
4688
  closeDangerConfirmRequest
4579
4689
  } = useReactListTableState(options);
4580
4690
  const hasPagination = paginationConfig != null;
@@ -4754,6 +4864,10 @@ function ReactListTableInner(props, ref) {
4754
4864
  /* @__PURE__ */ jsxRuntime.jsx(ReactListTableSearchDialog, { request: searchRequest }),
4755
4865
  document.body
4756
4866
  ) : null,
4867
+ exportNameRequest ? reactDom.createPortal(
4868
+ /* @__PURE__ */ jsxRuntime.jsx(ReactListTableExportNameDialog, { request: exportNameRequest }),
4869
+ document.body
4870
+ ) : null,
4757
4871
  dangerConfirmRequest ? reactDom.createPortal(
4758
4872
  /* @__PURE__ */ jsxRuntime.jsx(
4759
4873
  ReactTableDangerConfirmView,