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

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
  }
@@ -3768,6 +3867,12 @@ function SettingOutlined() {
3768
3867
  function HolderOutlined() {
3769
3868
  return /* @__PURE__ */ jsxRuntime.jsx(ToolbarIcon, { path: "M300 276.5a56 56 0 1056-97 56 56 0 00-56 97zm0 284a56 56 0 1056-97 56 56 0 00-56 97zM640 228a56 56 0 10112 0 56 56 0 00-112 0zm0 284a56 56 0 10112 0 56 56 0 00-112 0zM300 844.5a56 56 0 1056-97 56 56 0 00-56 97zM640 796a56 56 0 10112 0 56 56 0 00-112 0z" });
3770
3869
  }
3870
+
3871
+ // src/components/fullscreen-portal.ts
3872
+ function resolveFullscreenPortalContainer(host) {
3873
+ const fullscreenElement = document.fullscreenElement;
3874
+ return fullscreenElement instanceof HTMLElement && host && fullscreenElement.contains(host) ? fullscreenElement : document.body;
3875
+ }
3771
3876
  function SortableList(props) {
3772
3877
  const { items, onOrderChange } = props;
3773
3878
  const rootRef = react.useRef(null);
@@ -3892,7 +3997,7 @@ function SortableList(props) {
3892
3997
  })
3893
3998
  }
3894
3999
  ),
3895
- document.body
4000
+ resolveFullscreenPortalContainer(rootRef.current)
3896
4001
  ) : null
3897
4002
  ] });
3898
4003
  }
@@ -4223,9 +4328,14 @@ function useReactListTableState(options) {
4223
4328
  const [searchRequest, setSearchRequest] = react.useState(null);
4224
4329
  const [imagePreviewRequest, setImagePreviewRequest] = react.useState(null);
4225
4330
  const [dangerConfirmRequest, setDangerConfirmRequest] = react.useState(null);
4331
+ const [exportNameRequest, setExportNameRequest] = react.useState(null);
4226
4332
  const uiOptions = react.useMemo(
4227
4333
  () => ({
4228
4334
  ...options.ui,
4335
+ onExportNameRequest: (request) => {
4336
+ setExportNameRequest(request);
4337
+ options.ui?.onExportNameRequest?.(request);
4338
+ },
4229
4339
  onEditorRequest: (request) => {
4230
4340
  setEditorRequest(request);
4231
4341
  options.ui?.onEditorRequest?.(request);
@@ -4293,6 +4403,7 @@ function useReactListTableState(options) {
4293
4403
  setSearchRequest(null);
4294
4404
  setImagePreviewRequest(null);
4295
4405
  setDangerConfirmRequest(null);
4406
+ setExportNameRequest(null);
4296
4407
  };
4297
4408
  }, []);
4298
4409
  react.useEffect(() => {
@@ -4314,6 +4425,7 @@ function useReactListTableState(options) {
4314
4425
  searchRequest,
4315
4426
  imagePreviewRequest,
4316
4427
  dangerConfirmRequest,
4428
+ exportNameRequest,
4317
4429
  closeDangerConfirmRequest: () => {
4318
4430
  setDangerConfirmRequest(null);
4319
4431
  options.ui?.onDangerConfirmRequest?.(null);
@@ -4418,7 +4530,8 @@ function ReactListTableInner(props, ref) {
4418
4530
  rowDrag,
4419
4531
  debug,
4420
4532
  title,
4421
- toolbar
4533
+ toolbar,
4534
+ exportOptions
4422
4535
  } = props;
4423
4536
  const inheritedDebugEnabled = useReactListTableDebugEnabled();
4424
4537
  const resolvedDebug = react.useMemo(
@@ -4459,6 +4572,7 @@ function ReactListTableInner(props, ref) {
4459
4572
  );
4460
4573
  const options = react.useMemo(() => {
4461
4574
  const nextOptions = buildListTableOptions({
4575
+ exportOptions,
4462
4576
  width,
4463
4577
  height,
4464
4578
  emptyText,
@@ -4520,6 +4634,7 @@ function ReactListTableInner(props, ref) {
4520
4634
  }
4521
4635
  };
4522
4636
  }, [
4637
+ exportOptions,
4523
4638
  width,
4524
4639
  height,
4525
4640
  emptyText,
@@ -4575,10 +4690,12 @@ function ReactListTableInner(props, ref) {
4575
4690
  searchRequest,
4576
4691
  imagePreviewRequest,
4577
4692
  dangerConfirmRequest,
4693
+ exportNameRequest,
4578
4694
  closeDangerConfirmRequest
4579
4695
  } = useReactListTableState(options);
4580
4696
  const hasPagination = paginationConfig != null;
4581
4697
  const paginationLayout = resolveReactListTablePaginationLayout(resolvedRowHeight);
4698
+ const portalContainer = resolveFullscreenPortalContainer(shellRef.current);
4582
4699
  react.useImperativeHandle(
4583
4700
  ref,
4584
4701
  () => ({
@@ -4736,23 +4853,27 @@ function ReactListTableInner(props, ref) {
4736
4853
  ),
4737
4854
  tooltipRequest ? reactDom.createPortal(
4738
4855
  /* @__PURE__ */ jsxRuntime.jsx(ReactTableTooltipView, { request: tooltipRequest }),
4739
- document.body
4856
+ portalContainer
4740
4857
  ) : null,
4741
4858
  filterPanelRequest ? reactDom.createPortal(
4742
4859
  /* @__PURE__ */ jsxRuntime.jsx(ReactTableFilterPanelView, { request: filterPanelRequest }),
4743
- document.body
4860
+ portalContainer
4744
4861
  ) : null,
4745
4862
  fixedPopupRequest ? reactDom.createPortal(
4746
4863
  /* @__PURE__ */ jsxRuntime.jsx(ReactTableFixedPopupView, { request: fixedPopupRequest }),
4747
- document.body
4864
+ portalContainer
4748
4865
  ) : null,
4749
4866
  headerContextMenuRequest ? reactDom.createPortal(
4750
4867
  /* @__PURE__ */ jsxRuntime.jsx(ReactTableContextMenuView, { request: headerContextMenuRequest }),
4751
- document.body
4868
+ portalContainer
4752
4869
  ) : null,
4753
4870
  searchRequest ? reactDom.createPortal(
4754
4871
  /* @__PURE__ */ jsxRuntime.jsx(ReactListTableSearchDialog, { request: searchRequest }),
4755
- document.body
4872
+ portalContainer
4873
+ ) : null,
4874
+ exportNameRequest ? reactDom.createPortal(
4875
+ /* @__PURE__ */ jsxRuntime.jsx(ReactListTableExportNameDialog, { request: exportNameRequest }),
4876
+ portalContainer
4756
4877
  ) : null,
4757
4878
  dangerConfirmRequest ? reactDom.createPortal(
4758
4879
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4762,11 +4883,11 @@ function ReactListTableInner(props, ref) {
4762
4883
  onClose: closeDangerConfirmRequest
4763
4884
  }
4764
4885
  ),
4765
- document.body
4886
+ portalContainer
4766
4887
  ) : null,
4767
4888
  editorRequest ? reactDom.createPortal(
4768
4889
  /* @__PURE__ */ jsxRuntime.jsx(ReactTableEditorOverlay, { request: editorRequest }),
4769
- editorRequest.hostElement ?? document.body
4890
+ editorRequest.hostElement && portalContainer.contains(editorRequest.hostElement) ? editorRequest.hostElement : portalContainer
4770
4891
  ) : null,
4771
4892
  imagePreviewRequest ? reactDom.createPortal(
4772
4893
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4778,7 +4899,7 @@ function ReactListTableInner(props, ref) {
4778
4899
  onClose: imagePreviewRequest.onClose
4779
4900
  }
4780
4901
  ),
4781
- document.body
4902
+ portalContainer
4782
4903
  ) : null
4783
4904
  ] });
4784
4905
  }