@sia.soul/sia-react-ui 0.1.12 → 0.1.13

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/dist/core.css CHANGED
@@ -4443,6 +4443,22 @@ fieldset:disabled .sia-input-number__floating-placeholder {
4443
4443
  transition: none;
4444
4444
  }
4445
4445
  }
4446
+ .sia-table-settings__item.is-dragging {
4447
+ border-color: var(--sia-color-primary);
4448
+ background: var(--sia-color-primary-light);
4449
+ opacity: .65;
4450
+ outline: 1px dashed var(--sia-color-primary);
4451
+ }
4452
+ .sia-table .sia-table__summary-cell--label {
4453
+ padding-inline: 4px;
4454
+ }
4455
+ .sia-table__summary-label {
4456
+ display: block;
4457
+ min-width: 0;
4458
+ overflow: hidden;
4459
+ white-space: nowrap;
4460
+ text-overflow: ellipsis;
4461
+ }
4446
4462
 
4447
4463
  /* src/components-extra.css */
4448
4464
  .sia-flex {
package/dist/index.cjs CHANGED
@@ -11543,87 +11543,163 @@ function applyColumnSettingsToTree(columns, items) {
11543
11543
  }).sort((left, right) => left.order - right.order);
11544
11544
  return walk(columns).map((entry) => entry.column);
11545
11545
  }
11546
- function ColumnSettingPanel({ open, items, columns, onClose, onApply }) {
11546
+ function orderColumnSettings(items) {
11547
+ const rank = (item) => item.fixed === "left" ? 0 : item.fixed === "right" ? 2 : 1;
11548
+ return [...items].sort((a, b) => rank(a) - rank(b)).map((item, order) => ({ ...item, order }));
11549
+ }
11550
+ function ColumnSettingPanel({ open, items, columns, loading, loadError, onRetry, onClose, onApply }) {
11547
11551
  const [draft, setDraft] = (0, import_react44.useState)(items);
11548
- const dragIndex = (0, import_react44.useRef)(null);
11552
+ const drag = (0, import_react44.useRef)(null);
11553
+ const [draggingKey, setDraggingKey] = (0, import_react44.useState)(null);
11549
11554
  (0, import_react44.useEffect)(() => {
11550
- if (open) setDraft(items);
11555
+ if (open) setDraft(orderColumnSettings(items));
11556
+ drag.current = null;
11557
+ setDraggingKey(null);
11551
11558
  }, [items, open]);
11552
11559
  const labels = new Map(flattenTableColumns(columns).map((column) => [column.key, getColumnLabel(column)]));
11553
11560
  const visibleCount = draft.filter((item) => item.visible).length;
11554
- function move(index, offset) {
11555
- const target = index + offset;
11556
- if (target < 0 || target >= draft.length) return;
11557
- const next = [...draft];
11558
- const [item] = next.splice(index, 1);
11559
- next.splice(target, 0, item);
11560
- setDraft(next.map((entry, order) => ({ ...entry, order })));
11561
- }
11562
11561
  function setFixed(key, fixed) {
11563
- setDraft((previous) => previous.map((item) => item.key === key ? { ...item, fixed } : item));
11562
+ setDraft((previous) => {
11563
+ const item = previous.find((entry) => entry.key === key);
11564
+ if (!item) return previous;
11565
+ const next = previous.filter((entry) => entry.key !== key);
11566
+ const updated = { ...item, fixed };
11567
+ const target = fixed === "left" ? next.filter((entry) => entry.fixed === "left").length : fixed === "right" ? next.findIndex((entry) => entry.fixed === "right") : item.fixed === "left" ? next.filter((entry) => entry.fixed === "left").length : next.findIndex((entry) => entry.fixed === "right");
11568
+ next.splice(target < 0 ? next.length : target, 0, updated);
11569
+ return orderColumnSettings(next);
11570
+ });
11571
+ }
11572
+ function previewMove(key, clientY, element) {
11573
+ const active = drag.current;
11574
+ if (!active || active.key === key || loading || loadError) return;
11575
+ const rect = element.getBoundingClientRect();
11576
+ setDraft((previous) => {
11577
+ const from = previous.findIndex((entry) => entry.key === active.key);
11578
+ const to = previous.findIndex((entry) => entry.key === key);
11579
+ if (from < 0 || to < 0 || previous[from].fixed !== previous[to].fixed) return previous;
11580
+ if (from < to ? clientY < rect.top + rect.height / 2 : clientY > rect.top + rect.height / 2) return previous;
11581
+ const next = [...previous];
11582
+ const [moved] = next.splice(from, 1);
11583
+ next.splice(to, 0, moved);
11584
+ return orderColumnSettings(next);
11585
+ });
11564
11586
  }
11565
11587
  return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Drawer2, { open, title: "\u5217\u8BBE\u7F6E", size: 440, onOpenChange: (next) => {
11566
11588
  if (!next) onClose();
11567
11589
  }, footer: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
11568
11590
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { onClick: onClose, children: "\u53D6\u6D88" }),
11569
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { variant: "primary", onClick: () => onApply(draft), children: "\u5E94\u7528" })
11591
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { variant: "primary", disabled: loading || loadError, onClick: () => onApply(draft), children: "\u5E94\u7528" })
11570
11592
  ] }), children: [
11571
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "sia-table-settings__all", children: [
11572
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableCheckbox, { label: "\u5168\u9009\u5217", checked: visibleCount === draft.length && draft.length > 0, indeterminate: visibleCount > 0 && visibleCount < draft.length, onChange: (checked) => setDraft((previous) => previous.map((item) => ({ ...item, visible: checked }))) }),
11573
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { children: [
11574
- "\u5168\u9009\uFF08",
11575
- visibleCount,
11576
- "/",
11577
- draft.length,
11578
- "\uFF09"
11579
- ] })
11580
- ] }),
11581
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "sia-table-settings__list", children: draft.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "sia-table-settings__item", draggable: true, onDragStart: () => {
11582
- dragIndex.current = index;
11583
- }, onDragOver: (event) => event.preventDefault(), onDrop: () => {
11584
- if (dragIndex.current == null || dragIndex.current === index) return;
11585
- const next = [...draft];
11586
- const [dragged] = next.splice(dragIndex.current, 1);
11587
- next.splice(index, 0, dragged);
11588
- setDraft(next.map((entry, order) => ({ ...entry, order })));
11589
- dragIndex.current = null;
11590
- }, children: [
11591
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Icon, { name: "menu", size: 15 }),
11592
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableCheckbox, { label: `\u663E\u793A${labels.get(item.key) ?? item.key}\u5217`, checked: item.visible, onChange: (checked) => setDraft((previous) => previous.map((entry) => entry.key === item.key ? { ...entry, visible: checked } : entry)) }),
11593
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sia-table-settings__name", title: labels.get(item.key) ?? item.key, children: labels.get(item.key) ?? item.key }),
11594
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "sia-table-settings__actions", children: [
11595
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { size: "small", variant: "text", icon: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Icon, { name: "arrow-up", size: 13 }), "aria-label": "\u4E0A\u79FB", disabled: index === 0, onClick: () => move(index, -1) }),
11596
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { size: "small", variant: "text", icon: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Icon, { name: "arrow-down", size: 13 }), "aria-label": "\u4E0B\u79FB", disabled: index === draft.length - 1, onClick: () => move(index, 1) }),
11597
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { size: "small", variant: item.fixed === "left" ? "primary" : "text", "aria-label": "\u5DE6\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "left" ? void 0 : "left"), children: "\u5DE6" }),
11598
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { size: "small", variant: item.fixed === "right" ? "primary" : "text", "aria-label": "\u53F3\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "right" ? void 0 : "right"), children: "\u53F3" })
11599
- ] })
11600
- ] }, item.key)) })
11593
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u5217\u914D\u7F6E\u2026" }) : null,
11594
+ loadError ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("p", { role: "alert", children: [
11595
+ "\u8BFB\u53D6\u5217\u914D\u7F6E\u5931\u8D25\u3002",
11596
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { variant: "link", onClick: onRetry, children: "\u91CD\u8BD5" })
11597
+ ] }) : null,
11598
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("fieldset", { disabled: loading || loadError, style: { border: 0, padding: 0, margin: 0, minWidth: 0 }, "aria-busy": loading, children: [
11599
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "sia-table-settings__all", children: [
11600
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableCheckbox, { label: "\u5168\u9009\u5217", checked: visibleCount === draft.length && draft.length > 0, indeterminate: visibleCount > 0 && visibleCount < draft.length, onChange: (checked) => setDraft((previous) => previous.map((item) => ({ ...item, visible: checked }))) }),
11601
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { children: [
11602
+ "\u5168\u9009\uFF08",
11603
+ visibleCount,
11604
+ "/",
11605
+ draft.length,
11606
+ "\uFF09"
11607
+ ] })
11608
+ ] }),
11609
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "sia-table-settings__list", onDragOver: (event) => {
11610
+ if (drag.current) event.preventDefault();
11611
+ }, onDrop: (event) => {
11612
+ event.preventDefault();
11613
+ if (drag.current) drag.current.dropped = true;
11614
+ }, children: draft.map((item) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
11615
+ "div",
11616
+ {
11617
+ "data-column-key": item.key,
11618
+ className: `sia-table-settings__item${draggingKey === item.key ? " is-dragging" : ""}`,
11619
+ draggable: !loading && !loadError,
11620
+ onDragStart: (event) => {
11621
+ drag.current = { key: item.key, original: draft, dropped: false };
11622
+ event.dataTransfer.effectAllowed = "move";
11623
+ event.dataTransfer.setData("text/plain", item.key);
11624
+ setDraggingKey(item.key);
11625
+ },
11626
+ onDragOver: (event) => {
11627
+ event.preventDefault();
11628
+ event.dataTransfer.dropEffect = "move";
11629
+ previewMove(item.key, event.clientY, event.currentTarget);
11630
+ },
11631
+ onDragEnd: () => {
11632
+ if (drag.current && !drag.current.dropped) setDraft(drag.current.original);
11633
+ drag.current = null;
11634
+ setDraggingKey(null);
11635
+ },
11636
+ children: [
11637
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Icon, { name: "menu", size: 15 }),
11638
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableCheckbox, { label: `\u663E\u793A${labels.get(item.key) ?? item.key}\u5217`, checked: item.visible, onChange: (checked) => setDraft((previous) => previous.map((entry) => entry.key === item.key ? { ...entry, visible: checked } : entry)) }),
11639
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sia-table-settings__name", title: labels.get(item.key) ?? item.key, children: labels.get(item.key) ?? item.key }),
11640
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "sia-table-settings__actions", children: [
11641
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { size: "small", variant: item.fixed === "left" ? "primary" : "text", "aria-label": "\u5DE6\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "left" ? void 0 : "left"), children: "\u5DE6" }),
11642
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { size: "small", variant: item.fixed === "right" ? "primary" : "text", "aria-label": "\u53F3\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "right" ? void 0 : "right"), children: "\u53F3" })
11643
+ ] })
11644
+ ]
11645
+ },
11646
+ item.key
11647
+ )) })
11648
+ ] })
11601
11649
  ] });
11602
11650
  }
11651
+ var columnSettingLoadTokens = /* @__PURE__ */ new WeakMap();
11603
11652
  function createColumnSettingPlugin(options = {}) {
11604
11653
  const baseItems = (context) => createColumnSettingItems(context.baseColumns, options.defaultVisibleKeys);
11605
11654
  const currentItems = (context) => context.state.items?.length ? context.state.items : baseItems(context);
11606
11655
  const save = (snapshot) => options.adapter && options.scope ? options.adapter.save(options.scope, snapshot).catch(() => void 0) : Promise.resolve();
11607
11656
  function apply(items, context, persist = true) {
11608
- const normalized = [...items].sort((a, b) => a.order - b.order).map((item, order) => ({ ...item, order }));
11657
+ const normalized = orderColumnSettings([...items].sort((a, b) => a.order - b.order));
11609
11658
  context.setState((state) => ({ ...state, items: normalized, open: false }));
11610
11659
  const columnMap = new Map(flattenTableColumns(context.baseColumns).map((column) => [column.key, column]));
11611
11660
  options.onVisibleChange?.(normalized.filter((item) => item.visible).map((item) => columnMap.get(item.key)).filter(Boolean));
11612
11661
  options.onOrderChange?.(normalized.map((item) => columnMap.get(item.key)).filter(Boolean));
11613
11662
  if (persist) void save({ columns: normalized, pageSize: context.state.pageSize, version: 1 });
11614
11663
  }
11664
+ function loadSettings(context, open = false) {
11665
+ const fallback = baseItems(context);
11666
+ const token = /* @__PURE__ */ Symbol("column-settings-load");
11667
+ columnSettingLoadTokens.set(context.setPluginState, token);
11668
+ let cancelled = false;
11669
+ const hasAdapter = Boolean(options.adapter && options.scope);
11670
+ context.setState((state) => ({
11671
+ ...state,
11672
+ open: open || state.open,
11673
+ items: state.items.length ? state.items : fallback,
11674
+ loading: hasAdapter,
11675
+ loaded: !hasAdapter,
11676
+ loadError: false,
11677
+ loadToken: token
11678
+ }));
11679
+ if (hasAdapter) void Promise.resolve().then(() => options.adapter.load(options.scope)).then((snapshot) => {
11680
+ if (cancelled || columnSettingLoadTokens.get(context.setPluginState) !== token) return;
11681
+ if (snapshot?.pageSize) context.setPluginState("pagination", (state) => ({ ...state, pageSize: snapshot.pageSize }));
11682
+ context.setState((state) => state.loadToken !== token ? state : {
11683
+ ...state,
11684
+ items: snapshot?.columns?.length ? snapshot.columns : fallback,
11685
+ pageSize: snapshot?.pageSize,
11686
+ loaded: true,
11687
+ loading: false,
11688
+ loadError: false
11689
+ });
11690
+ }).catch(() => {
11691
+ if (!cancelled) context.setState((state) => state.loadToken !== token ? state : { ...state, loading: false, loaded: true, loadError: true });
11692
+ });
11693
+ return () => {
11694
+ cancelled = true;
11695
+ };
11696
+ }
11615
11697
  return {
11616
11698
  id: "columnSetting",
11617
11699
  order: 10,
11618
11700
  initialState: { open: false, items: [], loaded: false },
11619
11701
  onMount(context) {
11620
- const fallback = baseItems(context);
11621
- context.setState((state) => ({ ...state, items: state.items.length ? state.items : fallback, loaded: !options.adapter }));
11622
- if (options.adapter && options.scope) void options.adapter.load(options.scope).then((snapshot) => {
11623
- const merged = snapshot?.columns?.length ? snapshot.columns : fallback;
11624
- context.setState((state) => ({ ...state, items: merged, pageSize: snapshot?.pageSize, loaded: true }));
11625
- if (snapshot?.pageSize) context.setPluginState("pagination", (state) => ({ ...state, pageSize: snapshot.pageSize }));
11626
- }).catch(() => context.setState((state) => ({ ...state, items: fallback, loaded: true })));
11702
+ return loadSettings(context);
11627
11703
  },
11628
11704
  transformColumns(columns, context) {
11629
11705
  return applyColumnSettingsToTree(columns, currentItems(context));
@@ -11661,7 +11737,7 @@ function createColumnSettingPlugin(options = {}) {
11661
11737
  ];
11662
11738
  return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Dropdown, { disabled: options.buttonProps?.disabled || options.buttonProps?.loading, trigger: ["hover", "click"], placement: "bottomRight", popupClassName: "sia-table__settings-dropdown", menu: { items, onClick: ({ key }) => {
11663
11739
  if (key === "builtin:export") void exportApi().download();
11664
- else if (key === "builtin:columns") context.setState((state) => ({ ...state, open: true }));
11740
+ else if (key === "builtin:columns") loadSettings(context, true);
11665
11741
  else void customItems.find((item) => "custom:" + item.key === key)?.onClick?.(context);
11666
11742
  } }, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
11667
11743
  Button,
@@ -11675,8 +11751,10 @@ function createColumnSettingPlugin(options = {}) {
11675
11751
  }
11676
11752
  ) });
11677
11753
  },
11678
- renderOverlay: (context) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ColumnSettingPanel, { open: context.state.open, items: currentItems(context), columns: context.baseColumns, onClose: () => context.setState((state) => ({ ...state, open: false })), onApply: (items) => apply(items, context) }),
11679
- getApi: (context) => ({ open: () => context.setState((state) => ({ ...state, open: true })), close: () => context.setState((state) => ({ ...state, open: false })), getSettings: () => currentItems(context), apply: (items) => apply(items, context) })
11754
+ renderOverlay: (context) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ColumnSettingPanel, { open: context.state.open, loading: context.state.loading, loadError: context.state.loadError, onRetry: () => loadSettings(context, true), items: currentItems(context), columns: context.baseColumns, onClose: () => context.setState((state) => ({ ...state, open: false })), onApply: (items) => apply(items, context) }),
11755
+ getApi: (context) => ({ open: () => {
11756
+ loadSettings(context, true);
11757
+ }, close: () => context.setState((state) => ({ ...state, open: false })), getSettings: () => currentItems(context), apply: (items) => apply(items, context) })
11680
11758
  };
11681
11759
  }
11682
11760
  function createLocalStorageTableSettingsAdapter(prefix = "sia-table") {
@@ -12374,11 +12452,12 @@ function TableInner(props, ref) {
12374
12452
  let labelRendered = false;
12375
12453
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("tr", { className: `sia-table__summary-row ${summary.className ?? ""}`.trim(), children: leafColumns.map((column) => {
12376
12454
  const value = summary.values[column.key];
12377
- const content = value !== void 0 ? value : !labelRendered && summary.label !== void 0 ? (labelRendered = true, summary.label) : null;
12455
+ const isLabel = value === void 0 && !labelRendered && summary.label !== void 0;
12456
+ const content = value !== void 0 ? value : isLabel ? (labelRendered = true, summary.label) : null;
12378
12457
  const fixed = normalizeFixed(column.fixed);
12379
12458
  const fixedEdgeClass = fixed === "left" && column.key === leftFixedEdgeKey ? " sia-table__cell--fixed-left-edge" : fixed === "right" && column.key === rightFixedEdgeKey ? " sia-table__cell--fixed-right-edge" : "";
12380
12459
  const autoWidthClass = column.autoWidth ? " sia-table__cell--auto-width" : "";
12381
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("td", { "data-column-key": column.key, className: `sia-table__cell sia-table__cell--${column.align ?? "left"} sia-table__summary-cell${fixed ? ` sia-table__cell--fixed sia-table__cell--fixed-${fixed}` : ""}${fixedEdgeClass}${autoWidthClass}`, style: getFixedStyle(column), children: content }, column.key);
12460
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("td", { "data-column-key": column.key, className: `sia-table__cell sia-table__cell--${column.align ?? "left"} sia-table__summary-cell${isLabel ? " sia-table__summary-cell--label" : ""}${fixed ? ` sia-table__cell--fixed sia-table__cell--fixed-${fixed}` : ""}${fixedEdgeClass}${autoWidthClass}`, style: getFixedStyle(column), children: isLabel ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "sia-table__summary-label", title: typeof content === "string" ? content : void 0, children: content }) : content }, column.key);
12382
12461
  }) }, summary.key);
12383
12462
  }
12384
12463
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
package/dist/index.css CHANGED
@@ -4443,6 +4443,22 @@ fieldset:disabled .sia-input-number__floating-placeholder {
4443
4443
  transition: none;
4444
4444
  }
4445
4445
  }
4446
+ .sia-table-settings__item.is-dragging {
4447
+ border-color: var(--sia-color-primary);
4448
+ background: var(--sia-color-primary-light);
4449
+ opacity: .65;
4450
+ outline: 1px dashed var(--sia-color-primary);
4451
+ }
4452
+ .sia-table .sia-table__summary-cell--label {
4453
+ padding-inline: 4px;
4454
+ }
4455
+ .sia-table__summary-label {
4456
+ display: block;
4457
+ min-width: 0;
4458
+ overflow: hidden;
4459
+ white-space: nowrap;
4460
+ text-overflow: ellipsis;
4461
+ }
4446
4462
 
4447
4463
  /* src/components-extra.css */
4448
4464
  .sia-flex {
package/dist/index.d.cts CHANGED
@@ -1209,6 +1209,9 @@ interface ColumnSettingState {
1209
1209
  open: boolean;
1210
1210
  items: readonly TableColumnSettingItem[];
1211
1211
  loaded: boolean;
1212
+ loading?: boolean;
1213
+ loadError?: boolean;
1214
+ loadToken?: symbol;
1212
1215
  pageSize?: number;
1213
1216
  }
1214
1217
  interface TableColumnSettingApi {
package/dist/index.d.ts CHANGED
@@ -1209,6 +1209,9 @@ interface ColumnSettingState {
1209
1209
  open: boolean;
1210
1210
  items: readonly TableColumnSettingItem[];
1211
1211
  loaded: boolean;
1212
+ loading?: boolean;
1213
+ loadError?: boolean;
1214
+ loadToken?: symbol;
1212
1215
  pageSize?: number;
1213
1216
  }
1214
1217
  interface TableColumnSettingApi {
package/dist/index.js CHANGED
@@ -5786,87 +5786,163 @@ function applyColumnSettingsToTree(columns, items) {
5786
5786
  }).sort((left, right) => left.order - right.order);
5787
5787
  return walk(columns).map((entry) => entry.column);
5788
5788
  }
5789
- function ColumnSettingPanel({ open, items, columns, onClose, onApply }) {
5789
+ function orderColumnSettings(items) {
5790
+ const rank = (item) => item.fixed === "left" ? 0 : item.fixed === "right" ? 2 : 1;
5791
+ return [...items].sort((a, b) => rank(a) - rank(b)).map((item, order) => ({ ...item, order }));
5792
+ }
5793
+ function ColumnSettingPanel({ open, items, columns, loading, loadError, onRetry, onClose, onApply }) {
5790
5794
  const [draft, setDraft] = useState16(items);
5791
- const dragIndex = useRef14(null);
5795
+ const drag = useRef14(null);
5796
+ const [draggingKey, setDraggingKey] = useState16(null);
5792
5797
  useEffect12(() => {
5793
- if (open) setDraft(items);
5798
+ if (open) setDraft(orderColumnSettings(items));
5799
+ drag.current = null;
5800
+ setDraggingKey(null);
5794
5801
  }, [items, open]);
5795
5802
  const labels = new Map(flattenTableColumns(columns).map((column) => [column.key, getColumnLabel(column)]));
5796
5803
  const visibleCount = draft.filter((item) => item.visible).length;
5797
- function move(index, offset) {
5798
- const target = index + offset;
5799
- if (target < 0 || target >= draft.length) return;
5800
- const next = [...draft];
5801
- const [item] = next.splice(index, 1);
5802
- next.splice(target, 0, item);
5803
- setDraft(next.map((entry, order) => ({ ...entry, order })));
5804
- }
5805
5804
  function setFixed(key, fixed) {
5806
- setDraft((previous) => previous.map((item) => item.key === key ? { ...item, fixed } : item));
5805
+ setDraft((previous) => {
5806
+ const item = previous.find((entry) => entry.key === key);
5807
+ if (!item) return previous;
5808
+ const next = previous.filter((entry) => entry.key !== key);
5809
+ const updated = { ...item, fixed };
5810
+ const target = fixed === "left" ? next.filter((entry) => entry.fixed === "left").length : fixed === "right" ? next.findIndex((entry) => entry.fixed === "right") : item.fixed === "left" ? next.filter((entry) => entry.fixed === "left").length : next.findIndex((entry) => entry.fixed === "right");
5811
+ next.splice(target < 0 ? next.length : target, 0, updated);
5812
+ return orderColumnSettings(next);
5813
+ });
5814
+ }
5815
+ function previewMove(key, clientY, element) {
5816
+ const active = drag.current;
5817
+ if (!active || active.key === key || loading || loadError) return;
5818
+ const rect = element.getBoundingClientRect();
5819
+ setDraft((previous) => {
5820
+ const from = previous.findIndex((entry) => entry.key === active.key);
5821
+ const to = previous.findIndex((entry) => entry.key === key);
5822
+ if (from < 0 || to < 0 || previous[from].fixed !== previous[to].fixed) return previous;
5823
+ if (from < to ? clientY < rect.top + rect.height / 2 : clientY > rect.top + rect.height / 2) return previous;
5824
+ const next = [...previous];
5825
+ const [moved] = next.splice(from, 1);
5826
+ next.splice(to, 0, moved);
5827
+ return orderColumnSettings(next);
5828
+ });
5807
5829
  }
5808
5830
  return /* @__PURE__ */ jsxs19(Drawer2, { open, title: "\u5217\u8BBE\u7F6E", size: 440, onOpenChange: (next) => {
5809
5831
  if (!next) onClose();
5810
5832
  }, footer: /* @__PURE__ */ jsxs19(Fragment6, { children: [
5811
5833
  /* @__PURE__ */ jsx20(Button, { onClick: onClose, children: "\u53D6\u6D88" }),
5812
- /* @__PURE__ */ jsx20(Button, { variant: "primary", onClick: () => onApply(draft), children: "\u5E94\u7528" })
5834
+ /* @__PURE__ */ jsx20(Button, { variant: "primary", disabled: loading || loadError, onClick: () => onApply(draft), children: "\u5E94\u7528" })
5813
5835
  ] }), children: [
5814
- /* @__PURE__ */ jsxs19("label", { className: "sia-table-settings__all", children: [
5815
- /* @__PURE__ */ jsx20(TableCheckbox, { label: "\u5168\u9009\u5217", checked: visibleCount === draft.length && draft.length > 0, indeterminate: visibleCount > 0 && visibleCount < draft.length, onChange: (checked) => setDraft((previous) => previous.map((item) => ({ ...item, visible: checked }))) }),
5816
- /* @__PURE__ */ jsxs19("span", { children: [
5817
- "\u5168\u9009\uFF08",
5818
- visibleCount,
5819
- "/",
5820
- draft.length,
5821
- "\uFF09"
5822
- ] })
5823
- ] }),
5824
- /* @__PURE__ */ jsx20("div", { className: "sia-table-settings__list", children: draft.map((item, index) => /* @__PURE__ */ jsxs19("div", { className: "sia-table-settings__item", draggable: true, onDragStart: () => {
5825
- dragIndex.current = index;
5826
- }, onDragOver: (event) => event.preventDefault(), onDrop: () => {
5827
- if (dragIndex.current == null || dragIndex.current === index) return;
5828
- const next = [...draft];
5829
- const [dragged] = next.splice(dragIndex.current, 1);
5830
- next.splice(index, 0, dragged);
5831
- setDraft(next.map((entry, order) => ({ ...entry, order })));
5832
- dragIndex.current = null;
5833
- }, children: [
5834
- /* @__PURE__ */ jsx20(Icon, { name: "menu", size: 15 }),
5835
- /* @__PURE__ */ jsx20(TableCheckbox, { label: `\u663E\u793A${labels.get(item.key) ?? item.key}\u5217`, checked: item.visible, onChange: (checked) => setDraft((previous) => previous.map((entry) => entry.key === item.key ? { ...entry, visible: checked } : entry)) }),
5836
- /* @__PURE__ */ jsx20("span", { className: "sia-table-settings__name", title: labels.get(item.key) ?? item.key, children: labels.get(item.key) ?? item.key }),
5837
- /* @__PURE__ */ jsxs19("div", { className: "sia-table-settings__actions", children: [
5838
- /* @__PURE__ */ jsx20(Button, { size: "small", variant: "text", icon: /* @__PURE__ */ jsx20(Icon, { name: "arrow-up", size: 13 }), "aria-label": "\u4E0A\u79FB", disabled: index === 0, onClick: () => move(index, -1) }),
5839
- /* @__PURE__ */ jsx20(Button, { size: "small", variant: "text", icon: /* @__PURE__ */ jsx20(Icon, { name: "arrow-down", size: 13 }), "aria-label": "\u4E0B\u79FB", disabled: index === draft.length - 1, onClick: () => move(index, 1) }),
5840
- /* @__PURE__ */ jsx20(Button, { size: "small", variant: item.fixed === "left" ? "primary" : "text", "aria-label": "\u5DE6\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "left" ? void 0 : "left"), children: "\u5DE6" }),
5841
- /* @__PURE__ */ jsx20(Button, { size: "small", variant: item.fixed === "right" ? "primary" : "text", "aria-label": "\u53F3\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "right" ? void 0 : "right"), children: "\u53F3" })
5842
- ] })
5843
- ] }, item.key)) })
5836
+ loading ? /* @__PURE__ */ jsx20("p", { role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u5217\u914D\u7F6E\u2026" }) : null,
5837
+ loadError ? /* @__PURE__ */ jsxs19("p", { role: "alert", children: [
5838
+ "\u8BFB\u53D6\u5217\u914D\u7F6E\u5931\u8D25\u3002",
5839
+ /* @__PURE__ */ jsx20(Button, { variant: "link", onClick: onRetry, children: "\u91CD\u8BD5" })
5840
+ ] }) : null,
5841
+ /* @__PURE__ */ jsxs19("fieldset", { disabled: loading || loadError, style: { border: 0, padding: 0, margin: 0, minWidth: 0 }, "aria-busy": loading, children: [
5842
+ /* @__PURE__ */ jsxs19("label", { className: "sia-table-settings__all", children: [
5843
+ /* @__PURE__ */ jsx20(TableCheckbox, { label: "\u5168\u9009\u5217", checked: visibleCount === draft.length && draft.length > 0, indeterminate: visibleCount > 0 && visibleCount < draft.length, onChange: (checked) => setDraft((previous) => previous.map((item) => ({ ...item, visible: checked }))) }),
5844
+ /* @__PURE__ */ jsxs19("span", { children: [
5845
+ "\u5168\u9009\uFF08",
5846
+ visibleCount,
5847
+ "/",
5848
+ draft.length,
5849
+ "\uFF09"
5850
+ ] })
5851
+ ] }),
5852
+ /* @__PURE__ */ jsx20("div", { className: "sia-table-settings__list", onDragOver: (event) => {
5853
+ if (drag.current) event.preventDefault();
5854
+ }, onDrop: (event) => {
5855
+ event.preventDefault();
5856
+ if (drag.current) drag.current.dropped = true;
5857
+ }, children: draft.map((item) => /* @__PURE__ */ jsxs19(
5858
+ "div",
5859
+ {
5860
+ "data-column-key": item.key,
5861
+ className: `sia-table-settings__item${draggingKey === item.key ? " is-dragging" : ""}`,
5862
+ draggable: !loading && !loadError,
5863
+ onDragStart: (event) => {
5864
+ drag.current = { key: item.key, original: draft, dropped: false };
5865
+ event.dataTransfer.effectAllowed = "move";
5866
+ event.dataTransfer.setData("text/plain", item.key);
5867
+ setDraggingKey(item.key);
5868
+ },
5869
+ onDragOver: (event) => {
5870
+ event.preventDefault();
5871
+ event.dataTransfer.dropEffect = "move";
5872
+ previewMove(item.key, event.clientY, event.currentTarget);
5873
+ },
5874
+ onDragEnd: () => {
5875
+ if (drag.current && !drag.current.dropped) setDraft(drag.current.original);
5876
+ drag.current = null;
5877
+ setDraggingKey(null);
5878
+ },
5879
+ children: [
5880
+ /* @__PURE__ */ jsx20(Icon, { name: "menu", size: 15 }),
5881
+ /* @__PURE__ */ jsx20(TableCheckbox, { label: `\u663E\u793A${labels.get(item.key) ?? item.key}\u5217`, checked: item.visible, onChange: (checked) => setDraft((previous) => previous.map((entry) => entry.key === item.key ? { ...entry, visible: checked } : entry)) }),
5882
+ /* @__PURE__ */ jsx20("span", { className: "sia-table-settings__name", title: labels.get(item.key) ?? item.key, children: labels.get(item.key) ?? item.key }),
5883
+ /* @__PURE__ */ jsxs19("div", { className: "sia-table-settings__actions", children: [
5884
+ /* @__PURE__ */ jsx20(Button, { size: "small", variant: item.fixed === "left" ? "primary" : "text", "aria-label": "\u5DE6\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "left" ? void 0 : "left"), children: "\u5DE6" }),
5885
+ /* @__PURE__ */ jsx20(Button, { size: "small", variant: item.fixed === "right" ? "primary" : "text", "aria-label": "\u53F3\u56FA\u5B9A", onClick: () => setFixed(item.key, item.fixed === "right" ? void 0 : "right"), children: "\u53F3" })
5886
+ ] })
5887
+ ]
5888
+ },
5889
+ item.key
5890
+ )) })
5891
+ ] })
5844
5892
  ] });
5845
5893
  }
5894
+ var columnSettingLoadTokens = /* @__PURE__ */ new WeakMap();
5846
5895
  function createColumnSettingPlugin(options = {}) {
5847
5896
  const baseItems = (context) => createColumnSettingItems(context.baseColumns, options.defaultVisibleKeys);
5848
5897
  const currentItems = (context) => context.state.items?.length ? context.state.items : baseItems(context);
5849
5898
  const save = (snapshot) => options.adapter && options.scope ? options.adapter.save(options.scope, snapshot).catch(() => void 0) : Promise.resolve();
5850
5899
  function apply(items, context, persist = true) {
5851
- const normalized = [...items].sort((a, b) => a.order - b.order).map((item, order) => ({ ...item, order }));
5900
+ const normalized = orderColumnSettings([...items].sort((a, b) => a.order - b.order));
5852
5901
  context.setState((state) => ({ ...state, items: normalized, open: false }));
5853
5902
  const columnMap = new Map(flattenTableColumns(context.baseColumns).map((column) => [column.key, column]));
5854
5903
  options.onVisibleChange?.(normalized.filter((item) => item.visible).map((item) => columnMap.get(item.key)).filter(Boolean));
5855
5904
  options.onOrderChange?.(normalized.map((item) => columnMap.get(item.key)).filter(Boolean));
5856
5905
  if (persist) void save({ columns: normalized, pageSize: context.state.pageSize, version: 1 });
5857
5906
  }
5907
+ function loadSettings(context, open = false) {
5908
+ const fallback = baseItems(context);
5909
+ const token = /* @__PURE__ */ Symbol("column-settings-load");
5910
+ columnSettingLoadTokens.set(context.setPluginState, token);
5911
+ let cancelled = false;
5912
+ const hasAdapter = Boolean(options.adapter && options.scope);
5913
+ context.setState((state) => ({
5914
+ ...state,
5915
+ open: open || state.open,
5916
+ items: state.items.length ? state.items : fallback,
5917
+ loading: hasAdapter,
5918
+ loaded: !hasAdapter,
5919
+ loadError: false,
5920
+ loadToken: token
5921
+ }));
5922
+ if (hasAdapter) void Promise.resolve().then(() => options.adapter.load(options.scope)).then((snapshot) => {
5923
+ if (cancelled || columnSettingLoadTokens.get(context.setPluginState) !== token) return;
5924
+ if (snapshot?.pageSize) context.setPluginState("pagination", (state) => ({ ...state, pageSize: snapshot.pageSize }));
5925
+ context.setState((state) => state.loadToken !== token ? state : {
5926
+ ...state,
5927
+ items: snapshot?.columns?.length ? snapshot.columns : fallback,
5928
+ pageSize: snapshot?.pageSize,
5929
+ loaded: true,
5930
+ loading: false,
5931
+ loadError: false
5932
+ });
5933
+ }).catch(() => {
5934
+ if (!cancelled) context.setState((state) => state.loadToken !== token ? state : { ...state, loading: false, loaded: true, loadError: true });
5935
+ });
5936
+ return () => {
5937
+ cancelled = true;
5938
+ };
5939
+ }
5858
5940
  return {
5859
5941
  id: "columnSetting",
5860
5942
  order: 10,
5861
5943
  initialState: { open: false, items: [], loaded: false },
5862
5944
  onMount(context) {
5863
- const fallback = baseItems(context);
5864
- context.setState((state) => ({ ...state, items: state.items.length ? state.items : fallback, loaded: !options.adapter }));
5865
- if (options.adapter && options.scope) void options.adapter.load(options.scope).then((snapshot) => {
5866
- const merged = snapshot?.columns?.length ? snapshot.columns : fallback;
5867
- context.setState((state) => ({ ...state, items: merged, pageSize: snapshot?.pageSize, loaded: true }));
5868
- if (snapshot?.pageSize) context.setPluginState("pagination", (state) => ({ ...state, pageSize: snapshot.pageSize }));
5869
- }).catch(() => context.setState((state) => ({ ...state, items: fallback, loaded: true })));
5945
+ return loadSettings(context);
5870
5946
  },
5871
5947
  transformColumns(columns, context) {
5872
5948
  return applyColumnSettingsToTree(columns, currentItems(context));
@@ -5904,7 +5980,7 @@ function createColumnSettingPlugin(options = {}) {
5904
5980
  ];
5905
5981
  return /* @__PURE__ */ jsx20(Dropdown, { disabled: options.buttonProps?.disabled || options.buttonProps?.loading, trigger: ["hover", "click"], placement: "bottomRight", popupClassName: "sia-table__settings-dropdown", menu: { items, onClick: ({ key }) => {
5906
5982
  if (key === "builtin:export") void exportApi().download();
5907
- else if (key === "builtin:columns") context.setState((state) => ({ ...state, open: true }));
5983
+ else if (key === "builtin:columns") loadSettings(context, true);
5908
5984
  else void customItems.find((item) => "custom:" + item.key === key)?.onClick?.(context);
5909
5985
  } }, children: /* @__PURE__ */ jsx20(
5910
5986
  Button,
@@ -5918,8 +5994,10 @@ function createColumnSettingPlugin(options = {}) {
5918
5994
  }
5919
5995
  ) });
5920
5996
  },
5921
- renderOverlay: (context) => /* @__PURE__ */ jsx20(ColumnSettingPanel, { open: context.state.open, items: currentItems(context), columns: context.baseColumns, onClose: () => context.setState((state) => ({ ...state, open: false })), onApply: (items) => apply(items, context) }),
5922
- getApi: (context) => ({ open: () => context.setState((state) => ({ ...state, open: true })), close: () => context.setState((state) => ({ ...state, open: false })), getSettings: () => currentItems(context), apply: (items) => apply(items, context) })
5997
+ renderOverlay: (context) => /* @__PURE__ */ jsx20(ColumnSettingPanel, { open: context.state.open, loading: context.state.loading, loadError: context.state.loadError, onRetry: () => loadSettings(context, true), items: currentItems(context), columns: context.baseColumns, onClose: () => context.setState((state) => ({ ...state, open: false })), onApply: (items) => apply(items, context) }),
5998
+ getApi: (context) => ({ open: () => {
5999
+ loadSettings(context, true);
6000
+ }, close: () => context.setState((state) => ({ ...state, open: false })), getSettings: () => currentItems(context), apply: (items) => apply(items, context) })
5923
6001
  };
5924
6002
  }
5925
6003
  function createLocalStorageTableSettingsAdapter(prefix = "sia-table") {
@@ -6617,11 +6695,12 @@ function TableInner(props, ref) {
6617
6695
  let labelRendered = false;
6618
6696
  return /* @__PURE__ */ jsx21("tr", { className: `sia-table__summary-row ${summary.className ?? ""}`.trim(), children: leafColumns.map((column) => {
6619
6697
  const value = summary.values[column.key];
6620
- const content = value !== void 0 ? value : !labelRendered && summary.label !== void 0 ? (labelRendered = true, summary.label) : null;
6698
+ const isLabel = value === void 0 && !labelRendered && summary.label !== void 0;
6699
+ const content = value !== void 0 ? value : isLabel ? (labelRendered = true, summary.label) : null;
6621
6700
  const fixed = normalizeFixed(column.fixed);
6622
6701
  const fixedEdgeClass = fixed === "left" && column.key === leftFixedEdgeKey ? " sia-table__cell--fixed-left-edge" : fixed === "right" && column.key === rightFixedEdgeKey ? " sia-table__cell--fixed-right-edge" : "";
6623
6702
  const autoWidthClass = column.autoWidth ? " sia-table__cell--auto-width" : "";
6624
- return /* @__PURE__ */ jsx21("td", { "data-column-key": column.key, className: `sia-table__cell sia-table__cell--${column.align ?? "left"} sia-table__summary-cell${fixed ? ` sia-table__cell--fixed sia-table__cell--fixed-${fixed}` : ""}${fixedEdgeClass}${autoWidthClass}`, style: getFixedStyle(column), children: content }, column.key);
6703
+ return /* @__PURE__ */ jsx21("td", { "data-column-key": column.key, className: `sia-table__cell sia-table__cell--${column.align ?? "left"} sia-table__summary-cell${isLabel ? " sia-table__summary-cell--label" : ""}${fixed ? ` sia-table__cell--fixed sia-table__cell--fixed-${fixed}` : ""}${fixedEdgeClass}${autoWidthClass}`, style: getFixedStyle(column), children: isLabel ? /* @__PURE__ */ jsx21("span", { className: "sia-table__summary-label", title: typeof content === "string" ? content : void 0, children: content }) : content }, column.key);
6625
6704
  }) }, summary.key);
6626
6705
  }
6627
6706
  return /* @__PURE__ */ jsxs20(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sia.soul/sia-react-ui",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Sia Design components and tokens for React",
5
5
  "publishConfig": {
6
6
  "access": "public",