@sia.soul/sia-react-ui 0.1.31 → 0.1.33

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/chart.cjs CHANGED
@@ -247,6 +247,7 @@ function installTitleTooltips(doc) {
247
247
  documents.add(doc);
248
248
  let hovered = null;
249
249
  let focused = null;
250
+ let keyboardFocus = true;
250
251
  let active = null;
251
252
  let pending = null;
252
253
  let timer;
@@ -337,19 +338,30 @@ function installTitleTooltips(doc) {
337
338
  else show(hovered ?? focused);
338
339
  }, true);
339
340
  doc.addEventListener("focusin", (event) => {
340
- focused = candidate(event.target);
341
+ focused = keyboardFocus ? candidate(event.target) : null;
341
342
  if (hasExplicitTooltip(event.target)) hide();
342
- else show(focused ?? hovered);
343
+ else if (keyboardFocus) show(focused ?? hovered);
343
344
  }, true);
344
345
  doc.addEventListener("focusout", (event) => {
345
- focused = candidate(event.relatedTarget);
346
+ focused = keyboardFocus ? candidate(event.relatedTarget) : null;
346
347
  show(hovered ?? focused);
347
348
  }, true);
348
- doc.addEventListener("pointerdown", hide, true);
349
+ doc.addEventListener("pointerdown", () => {
350
+ keyboardFocus = false;
351
+ focused = null;
352
+ hide();
353
+ }, true);
349
354
  doc.addEventListener("keydown", (event) => {
350
355
  if (event.key === "Escape") hide();
356
+ else if (event.key === "Tab") keyboardFocus = true;
351
357
  }, true);
352
358
  doc.addEventListener("scroll", hide, true);
359
+ doc.addEventListener("visibilitychange", () => {
360
+ if (doc.hidden) {
361
+ hovered = focused = null;
362
+ hide();
363
+ }
364
+ });
353
365
  doc.defaultView?.addEventListener("blur", () => {
354
366
  hovered = focused = null;
355
367
  hide();
package/dist/chart.js CHANGED
@@ -56,10 +56,10 @@ import {
56
56
  smoothSvgPath,
57
57
  svgArcPath,
58
58
  transferCompositeValue
59
- } from "./chunk-TZROBD65.js";
60
- import "./chunk-YQDHCORW.js";
61
- import "./chunk-IYNS423D.js";
62
- import "./chunk-RAO3JHDL.js";
59
+ } from "./chunk-Q4FDV6KP.js";
60
+ import "./chunk-SMCYEPBL.js";
61
+ import "./chunk-IKAMZNCK.js";
62
+ import "./chunk-H7IAG72O.js";
63
63
  export {
64
64
  BarChart,
65
65
  CHART_VIEWBOX_HEIGHT,
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  FloatingScrollbarProvider,
3
3
  Select
4
- } from "./chunk-AHBC5UXJ.js";
4
+ } from "./chunk-QAVQEBIS.js";
5
5
  import {
6
6
  Icon
7
- } from "./chunk-YF2I6SDB.js";
7
+ } from "./chunk-HOU5JLNJ.js";
8
8
  import {
9
9
  PopupPortal,
10
10
  useControlSize,
11
11
  useControllableState,
12
12
  useFloatingPlaceholder,
13
13
  withTitleTooltip
14
- } from "./chunk-RAO3JHDL.js";
14
+ } from "./chunk-H7IAG72O.js";
15
15
 
16
16
  // src/components/DatePicker.tsx
17
17
  import { useEffect, useId, useMemo, useRef as useRef2, useState } from "react";
@@ -13,6 +13,7 @@ function installTitleTooltips(doc) {
13
13
  documents.add(doc);
14
14
  let hovered = null;
15
15
  let focused = null;
16
+ let keyboardFocus = true;
16
17
  let active = null;
17
18
  let pending = null;
18
19
  let timer;
@@ -103,19 +104,30 @@ function installTitleTooltips(doc) {
103
104
  else show(hovered ?? focused);
104
105
  }, true);
105
106
  doc.addEventListener("focusin", (event) => {
106
- focused = candidate(event.target);
107
+ focused = keyboardFocus ? candidate(event.target) : null;
107
108
  if (hasExplicitTooltip(event.target)) hide();
108
- else show(focused ?? hovered);
109
+ else if (keyboardFocus) show(focused ?? hovered);
109
110
  }, true);
110
111
  doc.addEventListener("focusout", (event) => {
111
- focused = candidate(event.relatedTarget);
112
+ focused = keyboardFocus ? candidate(event.relatedTarget) : null;
112
113
  show(hovered ?? focused);
113
114
  }, true);
114
- doc.addEventListener("pointerdown", hide, true);
115
+ doc.addEventListener("pointerdown", () => {
116
+ keyboardFocus = false;
117
+ focused = null;
118
+ hide();
119
+ }, true);
115
120
  doc.addEventListener("keydown", (event) => {
116
121
  if (event.key === "Escape") hide();
122
+ else if (event.key === "Tab") keyboardFocus = true;
117
123
  }, true);
118
124
  doc.addEventListener("scroll", hide, true);
125
+ doc.addEventListener("visibilitychange", () => {
126
+ if (doc.hidden) {
127
+ hovered = focused = null;
128
+ hide();
129
+ }
130
+ });
119
131
  doc.defaultView?.addEventListener("blur", () => {
120
132
  hovered = focused = null;
121
133
  hide();
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  Button
3
- } from "./chunk-IYNS423D.js";
3
+ } from "./chunk-IKAMZNCK.js";
4
4
  import {
5
5
  PopupPortal,
6
6
  useControlSize,
7
7
  useControllableState,
8
8
  usePlaceholderMode,
9
9
  withTitleTooltip
10
- } from "./chunk-RAO3JHDL.js";
10
+ } from "./chunk-H7IAG72O.js";
11
11
 
12
12
  // src/components/Icon.tsx
13
13
  import { forwardRef } from "react";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  useControlSize,
3
3
  withTitleTooltip
4
- } from "./chunk-RAO3JHDL.js";
4
+ } from "./chunk-H7IAG72O.js";
5
5
 
6
6
  // src/components/Button.tsx
7
7
  import { forwardRef, useRef } from "react";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  useFloatingPlaceholder,
3
3
  withTitleTooltip
4
- } from "./chunk-RAO3JHDL.js";
4
+ } from "./chunk-H7IAG72O.js";
5
5
 
6
6
  // src/components/MarkdownEditor.tsx
7
7
  import { useEffect, useRef, useState } from "react";
@@ -2,14 +2,14 @@ import {
2
2
  Dropdown,
3
3
  Icon,
4
4
  Input
5
- } from "./chunk-YF2I6SDB.js";
5
+ } from "./chunk-HOU5JLNJ.js";
6
6
  import {
7
7
  Button
8
- } from "./chunk-IYNS423D.js";
8
+ } from "./chunk-IKAMZNCK.js";
9
9
  import {
10
10
  useFloatingPlaceholder,
11
11
  withTitleTooltip
12
- } from "./chunk-RAO3JHDL.js";
12
+ } from "./chunk-H7IAG72O.js";
13
13
 
14
14
  // src/components/RichTextEditor.tsx
15
15
  import { useEffect, useMemo, useRef, useState } from "react";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  TextArea
3
- } from "./chunk-YQDHCORW.js";
3
+ } from "./chunk-SMCYEPBL.js";
4
4
  import {
5
5
  Button
6
- } from "./chunk-IYNS423D.js";
6
+ } from "./chunk-IKAMZNCK.js";
7
7
  import {
8
8
  withTitleTooltip
9
- } from "./chunk-RAO3JHDL.js";
9
+ } from "./chunk-H7IAG72O.js";
10
10
 
11
11
  // src/components/ChartWhirlingLoading.tsx
12
12
  import { useEffect, useState } from "react";
@@ -2,10 +2,10 @@ import {
2
2
  Dropdown,
3
3
  Icon,
4
4
  Input
5
- } from "./chunk-YF2I6SDB.js";
5
+ } from "./chunk-HOU5JLNJ.js";
6
6
  import {
7
7
  Button
8
- } from "./chunk-IYNS423D.js";
8
+ } from "./chunk-IKAMZNCK.js";
9
9
  import {
10
10
  ControlSizeContext,
11
11
  PopupPortal,
@@ -13,7 +13,7 @@ import {
13
13
  useControllableState,
14
14
  useFloatingPlaceholder,
15
15
  withTitleTooltip
16
- } from "./chunk-RAO3JHDL.js";
16
+ } from "./chunk-H7IAG72O.js";
17
17
 
18
18
  // src/components/Checkbox.tsx
19
19
  import { createContext, forwardRef, useContext, useEffect, useId, useMemo, useRef } from "react";
@@ -2,7 +2,7 @@ import {
2
2
  useControlSize,
3
3
  useFloatingPlaceholder,
4
4
  withTitleTooltip
5
- } from "./chunk-RAO3JHDL.js";
5
+ } from "./chunk-H7IAG72O.js";
6
6
 
7
7
  // src/components/TextArea.tsx
8
8
  import { forwardRef, useId, useState } from "react";
@@ -1,23 +1,23 @@
1
1
  import {
2
2
  TextArea
3
- } from "./chunk-YQDHCORW.js";
3
+ } from "./chunk-SMCYEPBL.js";
4
4
  import {
5
5
  Popover,
6
6
  Tooltip,
7
7
  useOverlayLifecycle
8
- } from "./chunk-AHBC5UXJ.js";
8
+ } from "./chunk-QAVQEBIS.js";
9
9
  import {
10
10
  Dropdown,
11
11
  Icon
12
- } from "./chunk-YF2I6SDB.js";
12
+ } from "./chunk-HOU5JLNJ.js";
13
13
  import {
14
14
  Button
15
- } from "./chunk-IYNS423D.js";
15
+ } from "./chunk-IKAMZNCK.js";
16
16
  import {
17
17
  useControlSize,
18
18
  useControllableState,
19
19
  withTitleTooltip
20
- } from "./chunk-RAO3JHDL.js";
20
+ } from "./chunk-H7IAG72O.js";
21
21
 
22
22
  // src/components/Toolbar.tsx
23
23
  import { useLayoutEffect, useRef, useState } from "react";
@@ -188,13 +188,15 @@ function Tag({
188
188
  }
189
189
 
190
190
  // src/components/Tabs.tsx
191
- import { useId, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef2, useState as useState3 } from "react";
191
+ import { useEffect, useId, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef2, useState as useState3 } from "react";
192
192
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
193
193
  function Tabs({
194
194
  items,
195
195
  activeKey,
196
196
  defaultActiveKey,
197
197
  onChange,
198
+ onAfterChange,
199
+ deferContent = false,
198
200
  type = "line",
199
201
  size = "medium",
200
202
  tabPosition,
@@ -202,6 +204,7 @@ function Tabs({
202
204
  centered = false,
203
205
  tabBarTitle,
204
206
  tabBarExtraContent,
207
+ contentGap,
205
208
  destroyInactiveTabPane = true,
206
209
  closable = false,
207
210
  onEdit,
@@ -221,6 +224,12 @@ function Tabs({
221
224
  const sliding = ["capsule", "line", "underline", "tech", "tech-line"].includes(type);
222
225
  const listRef = useRef2(null);
223
226
  const indicatorRef = useRef2(null);
227
+ const [contentState, setContentState] = useState3(() => ({ key: selectedKey, visited: /* @__PURE__ */ new Set([selectedKey]) }));
228
+ const settledKeyRef = useRef2(selectedKey);
229
+ const afterChangeRef = useRef2(onAfterChange);
230
+ useLayoutEffect2(() => {
231
+ afterChangeRef.current = onAfterChange;
232
+ }, [onAfterChange]);
224
233
  useLayoutEffect2(() => {
225
234
  if (!sliding) return;
226
235
  const list = listRef.current;
@@ -250,6 +259,32 @@ function Tabs({
250
259
  list.querySelectorAll('[role="tab"]').forEach((tab) => observer.observe(tab));
251
260
  return () => observer.disconnect();
252
261
  }, [sliding, type, selectedKey, items, size, resolvedPosition, centered, closable]);
262
+ useEffect(() => {
263
+ let cancelled = false;
264
+ let frame = 0;
265
+ frame = requestAnimationFrame(() => {
266
+ const indicator = indicatorRef.current;
267
+ if (indicator) getComputedStyle(indicator).transform;
268
+ const animations = indicator?.getAnimations() ?? [];
269
+ const complete = () => {
270
+ if (cancelled) return;
271
+ setContentState((previous) => ({ key: selectedKey, visited: new Set([...previous.visited].filter((key) => items.some((item) => item.key === key))).add(selectedKey) }));
272
+ if (settledKeyRef.current !== selectedKey) {
273
+ settledKeyRef.current = selectedKey;
274
+ afterChangeRef.current?.(selectedKey);
275
+ }
276
+ };
277
+ if (!animations.length) complete();
278
+ else Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {
279
+ if (!cancelled) frame = requestAnimationFrame(complete);
280
+ });
281
+ });
282
+ return () => {
283
+ cancelled = true;
284
+ cancelAnimationFrame(frame);
285
+ };
286
+ }, [selectedKey, type, deferContent]);
287
+ const contentKey = deferContent ? contentState.key : selectedKey;
253
288
  function selectTab(key) {
254
289
  if (key === selectedKey) return;
255
290
  if (activeKey === void 0) setInternalActiveKey(key);
@@ -281,26 +316,10 @@ function Tabs({
281
316
  /* @__PURE__ */ jsxs4("div", { className: "sia-tabs__header", children: [
282
317
  tabBarTitle ? /* @__PURE__ */ jsxs4("div", { className: "sia-tabs__title", children: [
283
318
  tabBarTitle,
284
- type === "tech-line" ? /* @__PURE__ */ jsx4("svg", { className: "sia-tabs__title-connector", viewBox: "0 0 32 40", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M0 39 H5 Q8 39 10 35 L28 3 Q29 1 32 1", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }) : null
319
+ type === "tech-line" ? /* @__PURE__ */ jsx4("svg", { className: "sia-tabs__title-connector", viewBox: "0 0 32 40", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M0 39.25 H5 Q8 39.25 10 35.25 L28 3.75 Q29 1.75 32 1.75", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }) }) : null
285
320
  ] }) : null,
286
321
  /* @__PURE__ */ jsxs4("div", { ref: listRef, className: `sia-tabs__list${sliding ? " sia-tabs__list--sliding" : ""}`, role: "tablist", "aria-orientation": resolvedOrientation, children: [
287
- sliding ? /* @__PURE__ */ jsx4("span", { ref: indicatorRef, className: `sia-tabs__indicator${type === "capsule" ? " sia-tabs__capsule-indicator" : ""}`, "aria-hidden": "true", children: type === "tech-line" ? /* @__PURE__ */ jsxs4("svg", { className: "sia-tabs__indicator-shape", viewBox: "0 0 100 34", preserveAspectRatio: "none", "aria-hidden": "true", children: [
288
- /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("linearGradient", { id: `${baseId}-tab-fill-indicator`, x1: "0", y1: "0", x2: "1", y2: "0", children: [
289
- /* @__PURE__ */ jsx4("stop", { offset: "0", stopColor: "var(--sia-tabs-line-fill-edge, var(--sia-tabs-line-fill))" }),
290
- /* @__PURE__ */ jsx4("stop", { offset: ".53", stopColor: "var(--sia-tabs-line-fill-center, var(--sia-tabs-line-fill))" }),
291
- /* @__PURE__ */ jsx4("stop", { offset: "1", stopColor: "var(--sia-tabs-line-fill-edge, var(--sia-tabs-line-fill))" })
292
- ] }) }),
293
- /* @__PURE__ */ jsx4(
294
- "path",
295
- {
296
- d: "M19 1 H96 Q99 1 97.5 4 L83.5 30 Q82 33 79 33 H4 Q1 33 2.5 30 L16.5 4 Q18 1 19 1 Z",
297
- fill: `url(#${baseId}-tab-fill-indicator)`,
298
- stroke: "currentColor",
299
- strokeWidth: "1.5",
300
- vectorEffect: "non-scaling-stroke"
301
- }
302
- )
303
- ] }) : null }, type) : null,
322
+ sliding ? /* @__PURE__ */ jsx4("span", { ref: indicatorRef, className: `sia-tabs__indicator${type === "capsule" ? " sia-tabs__capsule-indicator" : ""}`, "aria-hidden": "true", children: type === "tech-line" ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__indicator-shape", "aria-hidden": "true" }) : null }, type) : null,
304
323
  items.map((item) => {
305
324
  const selected = item.key === selectedKey;
306
325
  const canClose = item.closable ?? closable;
@@ -319,23 +338,7 @@ function Tabs({
319
338
  onClick: () => selectTab(item.key),
320
339
  onKeyDown: (event) => handleKeyDown(event, item.key),
321
340
  children: [
322
- type === "tech-line" ? /* @__PURE__ */ jsxs4("svg", { className: "sia-tabs__tab-shape", viewBox: "0 0 100 34", preserveAspectRatio: "none", "aria-hidden": "true", children: [
323
- /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("linearGradient", { id: `${baseId}-tab-fill-${item.key}`, x1: "0", y1: "0", x2: "1", y2: "0", children: [
324
- /* @__PURE__ */ jsx4("stop", { offset: "0", stopColor: "var(--sia-tabs-line-fill-edge, var(--sia-tabs-line-fill))" }),
325
- /* @__PURE__ */ jsx4("stop", { offset: ".53", stopColor: "var(--sia-tabs-line-fill-center, var(--sia-tabs-line-fill))" }),
326
- /* @__PURE__ */ jsx4("stop", { offset: "1", stopColor: "var(--sia-tabs-line-fill-edge, var(--sia-tabs-line-fill))" })
327
- ] }) }),
328
- /* @__PURE__ */ jsx4(
329
- "path",
330
- {
331
- d: "M19 1 H96 Q99 1 97.5 4 L83.5 30 Q82 33 79 33 H4 Q1 33 2.5 30 L16.5 4 Q18 1 19 1 Z",
332
- fill: `url(#${baseId}-tab-fill-${item.key})`,
333
- stroke: "currentColor",
334
- strokeWidth: "1.5",
335
- vectorEffect: "non-scaling-stroke"
336
- }
337
- )
338
- ] }) : null,
341
+ type === "tech-line" ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__tab-shape", "aria-hidden": "true" }) : null,
339
342
  item.icon ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__icon", children: item.icon }) : null,
340
343
  /* @__PURE__ */ jsx4("span", { children: item.label })
341
344
  ]
@@ -375,9 +378,13 @@ function Tabs({
375
378
  ] }),
376
379
  tabBarExtraContent ? /* @__PURE__ */ jsx4("div", { className: "sia-tabs__extra", children: tabBarExtraContent }) : null
377
380
  ] }),
378
- hasPanels ? /* @__PURE__ */ jsx4("div", { className: "sia-tabs__panels", children: items.map((item) => {
379
- const selected = item.key === selectedKey;
381
+ hasPanels ? /* @__PURE__ */ jsx4("div", { className: "sia-tabs__panels", style: contentGap === void 0 ? void 0 : {
382
+ // 仅设置面板靠近导航的一侧,保留各方向布局原有的其余边界。
383
+ [resolvedPosition === "top" ? "paddingTop" : resolvedPosition === "bottom" ? "paddingBottom" : resolvedPosition === "left" ? "paddingLeft" : "paddingRight"]: contentGap
384
+ }, "aria-busy": deferContent && contentKey !== selectedKey || void 0, children: items.map((item) => {
385
+ const selected = item.key === contentKey;
380
386
  if (destroyInactiveTabPane && !selected) return null;
387
+ if (deferContent && !selected && !contentState.visited.has(item.key)) return null;
381
388
  return /* @__PURE__ */ jsx4(
382
389
  "div",
383
390
  {
@@ -398,7 +405,7 @@ function Tabs({
398
405
  }
399
406
 
400
407
  // src/components/Feedback.tsx
401
- import { useEffect, useMemo as useMemo2, useState as useState4 } from "react";
408
+ import { useEffect as useEffect2, useMemo as useMemo2, useState as useState4 } from "react";
402
409
  import { createPortal } from "react-dom";
403
410
  import { createRoot } from "react-dom/client";
404
411
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
@@ -498,7 +505,7 @@ function Progress({
498
505
  }
499
506
  function Spin({ spinning = true, size = "default", tip, indicator, delay = 0, fullscreen = false, className = "", children, ...props }) {
500
507
  const [visible, setVisible] = useState4(delay === 0 && spinning);
501
- useEffect(() => {
508
+ useEffect2(() => {
502
509
  if (!spinning) {
503
510
  setVisible(false);
504
511
  return;
@@ -557,7 +564,7 @@ function closeNotification(key) {
557
564
  }
558
565
  function NotificationHost() {
559
566
  const [entries, setEntries] = useState4(notificationEntries);
560
- useEffect(() => {
567
+ useEffect2(() => {
561
568
  notificationListeners.add(setEntries);
562
569
  return () => {
563
570
  notificationListeners.delete(setEntries);
@@ -618,7 +625,7 @@ var notification = {
618
625
  function Tour({ open = false, steps, current, defaultCurrent = 0, mask = true, closable = true, disabledInteraction = false, onChange, onClose, onFinish }) {
619
626
  const [index, setIndex] = useControllableState({ value: current, defaultValue: defaultCurrent, onChange });
620
627
  const [, force] = useState4(0);
621
- useEffect(() => {
628
+ useEffect2(() => {
622
629
  if (!open) return;
623
630
  const update = () => force((value) => value + 1);
624
631
  window.addEventListener("resize", update);
@@ -661,7 +668,7 @@ function Tour({ open = false, steps, current, defaultCurrent = 0, mask = true, c
661
668
  }
662
669
 
663
670
  // src/components/DataDisplay.tsx
664
- import { Children, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
671
+ import { Children, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
665
672
  import { createPortal as createPortal2 } from "react-dom";
666
673
  import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
667
674
  function Rate({ value, size: sizeProp, defaultValue = 0, count = 5, allowHalf = false, allowClear = true, disabled = false, character, tooltips, onChange, onHoverChange, className = "", ...props }) {
@@ -743,7 +750,7 @@ function Calendar({ value, defaultValue, mode = "month", fullscreen = true, vali
743
750
  const date = parseDate(value ?? defaultValue);
744
751
  return new Date(date.getFullYear(), date.getMonth(), 1);
745
752
  });
746
- useEffect2(() => {
753
+ useEffect3(() => {
747
754
  if (value) {
748
755
  const date = parseDate(value);
749
756
  setPanel(new Date(date.getFullYear(), date.getMonth(), 1));
@@ -811,7 +818,7 @@ function Carousel({ autoplay = false, autoplaySpeed = 3e3, arrows = false, dots
811
818
  setCurrent(resolved);
812
819
  afterChange?.(resolved);
813
820
  }
814
- useEffect2(() => {
821
+ useEffect3(() => {
815
822
  if (!autoplay || paused || slides.length < 2) return;
816
823
  const timer = window.setInterval(() => go(current + 1), autoplaySpeed);
817
824
  return () => window.clearInterval(timer);
@@ -832,7 +839,7 @@ function CollapsePanel({
832
839
  children
833
840
  }) {
834
841
  const [keepChildren, setKeepChildren] = useState5(open || forceRender || !destroyInactivePanel);
835
- useEffect2(() => {
842
+ useEffect3(() => {
836
843
  if (open || forceRender || !destroyInactivePanel) {
837
844
  setKeepChildren(true);
838
845
  return void 0;
@@ -916,7 +923,7 @@ function Image({ fallback, placeholder, preview = true, rootClassName = "", clas
916
923
  }
917
924
 
918
925
  // src/components/Upload.tsx
919
- import { forwardRef, useEffect as useEffect3, useImperativeHandle, useRef as useRef5, useState as useState6 } from "react";
926
+ import { forwardRef, useEffect as useEffect4, useImperativeHandle, useRef as useRef5, useState as useState6 } from "react";
920
927
 
921
928
  // src/components/uploadFiles.ts
922
929
  function acceptsUploadFile(file, accept) {
@@ -1008,7 +1015,7 @@ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-run
1008
1015
  var LIST_IGNORE = /* @__PURE__ */ Symbol("SIA_UPLOAD_LIST_IGNORE");
1009
1016
  function useThumbnail(file) {
1010
1017
  const [local, setLocal] = useState6();
1011
- useEffect3(() => {
1018
+ useEffect4(() => {
1012
1019
  if (!file?.thumbUrl && file?.originFileObj?.type.startsWith("image/")) {
1013
1020
  const url = URL.createObjectURL(file.originFileObj);
1014
1021
  setLocal(url);
@@ -1021,7 +1028,7 @@ function useThumbnail(file) {
1021
1028
  function UploadItem({ file, files, props, actions }) {
1022
1029
  const source = useThumbnail(file);
1023
1030
  const [failed, setFailed] = useState6(false);
1024
- useEffect3(() => setFailed(false), [source]);
1031
+ useEffect4(() => setFailed(false), [source]);
1025
1032
  const node = /* @__PURE__ */ jsxs7("div", { className: `sia-upload__item sia-upload__item--${file.status ?? "ready"}`, children: [
1026
1033
  props.listType !== "text" ? /* @__PURE__ */ jsx7(Button, { variant: "text", className: "sia-upload__thumbnail", "aria-label": `\u9884\u89C8 ${file.name}`, onClick: actions.preview, disabled: !source && !props.onPreview, children: source && !failed ? /* @__PURE__ */ jsx7("img", { src: source, alt: file.name, onError: () => setFailed(true) }) : /* @__PURE__ */ jsx7(Icon, { name: file.type?.startsWith("image/") ? "image" : "file", size: 28 }) }) : /* @__PURE__ */ jsx7(Icon, { name: file.status === "done" ? "circle-check" : "file", size: 16 }),
1027
1034
  /* @__PURE__ */ jsx7("span", { className: "sia-upload__name", ...withTitleTooltip({ title: file.relativePath || file.name }), children: file.url ? /* @__PURE__ */ jsx7("a", { href: file.url, target: "_blank", rel: "noreferrer", children: file.name }) : file.name }),
@@ -1093,14 +1100,14 @@ var UploadRoot = forwardRef(function UploadRoot2({
1093
1100
  }
1094
1101
  task?.finish();
1095
1102
  }
1096
- useEffect3(() => {
1103
+ useEffect4(() => {
1097
1104
  mounted.current = true;
1098
1105
  return () => {
1099
1106
  mounted.current = false;
1100
1107
  [...tasks.current.keys()].forEach(cancel);
1101
1108
  };
1102
1109
  }, []);
1103
- useEffect3(() => {
1110
+ useEffect4(() => {
1104
1111
  for (const uid of tasks.current.keys()) if (!files.some((file) => file.uid === uid)) cancel(uid);
1105
1112
  if (preview && !files.some((file) => file.uid === preview.uid)) setPreview(void 0);
1106
1113
  }, [files, preview]);
@@ -1467,7 +1474,7 @@ function Breadcrumb({ items, separator = "/", itemRender, className = "", ...pro
1467
1474
  }
1468
1475
 
1469
1476
  // src/components/Typography.tsx
1470
- import { useEffect as useEffect4, useLayoutEffect as useLayoutEffect3, useRef as useRef6, useState as useState7 } from "react";
1477
+ import { useEffect as useEffect5, useLayoutEffect as useLayoutEffect3, useRef as useRef6, useState as useState7 } from "react";
1471
1478
  import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1472
1479
  function TypographyContent({
1473
1480
  as: Tag2 = "span",
@@ -1502,10 +1509,10 @@ function TypographyContent({
1502
1509
  const requestedRows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
1503
1510
  const rows = Number.isFinite(requestedRows) ? Math.max(1, Math.floor(requestedRows)) : 1;
1504
1511
  const expandable = typeof ellipsis === "object" && ellipsis.expandable;
1505
- useEffect4(() => {
1512
+ useEffect5(() => {
1506
1513
  setLocalText(void 0);
1507
1514
  }, [children]);
1508
- useEffect4(() => () => clearTimeout(timer.current), []);
1515
+ useEffect5(() => () => clearTimeout(timer.current), []);
1509
1516
  useLayoutEffect3(() => {
1510
1517
  const element = contentRef.current;
1511
1518
  if (!element || !ellipsis || editing || expanded) return;
@@ -1624,7 +1631,7 @@ var Typography = Object.assign(TypographyRoot, {
1624
1631
  });
1625
1632
 
1626
1633
  // src/components/FloatButton.tsx
1627
- import { createContext, forwardRef as forwardRef2, useContext, useEffect as useEffect5, useId as useId2, useRef as useRef7, useState as useState8 } from "react";
1634
+ import { createContext, forwardRef as forwardRef2, useContext, useEffect as useEffect6, useId as useId2, useRef as useRef7, useState as useState8 } from "react";
1628
1635
  import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1629
1636
  var GroupShape = createContext(void 0);
1630
1637
  var FloatButtonRoot = forwardRef2(function FloatButton({
@@ -1681,7 +1688,7 @@ function FloatButtonGroup({
1681
1688
  const rootRef = useRef7(null);
1682
1689
  const triggerRef = useRef7(null);
1683
1690
  const id = useId2();
1684
- useEffect5(() => {
1691
+ useEffect6(() => {
1685
1692
  if (!trigger || !visible) return;
1686
1693
  const close = (event) => {
1687
1694
  if (!rootRef.current?.contains(event.target)) setVisible(false);
@@ -1736,7 +1743,7 @@ function FloatButtonGroup({
1736
1743
  }
1737
1744
  function FloatButtonBackTop({ target, visibilityHeight = 400, behavior = "smooth", onClick, icon, tooltip = "\u8FD4\u56DE\u9876\u90E8", ...props }) {
1738
1745
  const [visible, setVisible] = useState8(false);
1739
- useEffect5(() => {
1746
+ useEffect6(() => {
1740
1747
  const element = target ? target() : window;
1741
1748
  if (!element) return;
1742
1749
  const update = () => setVisible((element === window ? window.scrollY : element.scrollTop) >= visibilityHeight);
@@ -689,10 +689,14 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
689
689
  activeKey?: string;
690
690
  /** 非受控模式下初始激活的项目标识。 */
691
691
  defaultActiveKey?: string;
692
- /** 激活项变化回调 */
692
+ /** 用户切换标签时立即调用,参数为目标 key;受控模式需同步更新 activeKey。重内容或路由切换可放在 onAfterChange 中,避免阻塞选中框动画。 */
693
693
  onChange?: (key: string) => void;
694
+ /** 选中框动画结束且目标面板获准显示时调用,参数为最终目标 key;首次渲染不调用,连续切换仅通知最终目标。无动画或系统减少动态效果时在下一帧完成,不等待面板数据请求;也适用于业务在 Tabs 外渲染内容或切换路由。 */
695
+ onAfterChange?: (key: string) => void;
696
+ /** 是否等选中框动画结束后再切换 items[].children,等待期间保留旧面板。默认 false,立即切换;true 时配合 destroyInactiveTabPane=false 仅在首次激活后挂载并保留面板。不能延迟父组件已执行的同步计算或 onChange 中的渲染,请将外部重内容更新放到 onAfterChange。 */
697
+ deferContent?: boolean;
694
698
  /**
695
- * 线型、卡片型、胶囊型、圆角胶囊、紧凑下划线或科技样式
699
+ * 线型、卡片型、胶囊型、圆角胶囊、紧凑下划线或科技样式;tech-line 使用固定倾角的圆角斜切选中框,长标题仅扩展中段宽度;位移与尺寸在 300ms 内同步过渡,系统减少动态效果时直接切换。
696
700
  * @defaultValue "line"
697
701
  */
698
702
  type?: TabsType;
@@ -713,8 +717,10 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
713
717
  * @defaultValue false
714
718
  */
715
719
  centered?: boolean;
716
- /** 标签栏左侧标题,科技大屏型会附带装饰线 */
720
+ /** 标签栏左侧标题,科技大屏型会附带装饰线;折线科技型横向布局按标题内容及装饰留白确定宽度,窄屏时标题独占一行。 */
717
721
  tabBarTitle?: ReactNode;
722
+ /** 标签栏与内容面板之间的间距,单位 px,支持 0;未指定时上下布局为 16px,左右布局为 20px。仅影响当前 Tabs,不传递给嵌套实例。 */
723
+ contentGap?: number;
718
724
  /** 标签栏右侧额外操作 */
719
725
  tabBarExtraContent?: ReactNode;
720
726
  /**
@@ -735,7 +741,7 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
735
741
  onTabContextMenuClick?: (info: TabsContextMenuClickInfo) => void;
736
742
  }
737
743
  /** 选项卡组件。 */
738
- declare function Tabs({ items, activeKey, defaultActiveKey, onChange, type, size, tabPosition, orientation, centered, tabBarTitle, tabBarExtraContent, destroyInactiveTabPane, closable, onEdit, tabContextMenu, onTabContextMenuClick, className, ...props }: TabsProps): react.JSX.Element;
744
+ declare function Tabs({ items, activeKey, defaultActiveKey, onChange, onAfterChange, deferContent, type, size, tabPosition, orientation, centered, tabBarTitle, tabBarExtraContent, contentGap, destroyInactiveTabPane, closable, onEdit, tabContextMenu, onTabContextMenuClick, className, ...props }: TabsProps): react.JSX.Element;
739
745
 
740
746
  /** 打开下拉菜单的触发方式。 */
741
747
  type DropdownTrigger = "click" | "hover" | "contextMenu";
@@ -689,10 +689,14 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
689
689
  activeKey?: string;
690
690
  /** 非受控模式下初始激活的项目标识。 */
691
691
  defaultActiveKey?: string;
692
- /** 激活项变化回调 */
692
+ /** 用户切换标签时立即调用,参数为目标 key;受控模式需同步更新 activeKey。重内容或路由切换可放在 onAfterChange 中,避免阻塞选中框动画。 */
693
693
  onChange?: (key: string) => void;
694
+ /** 选中框动画结束且目标面板获准显示时调用,参数为最终目标 key;首次渲染不调用,连续切换仅通知最终目标。无动画或系统减少动态效果时在下一帧完成,不等待面板数据请求;也适用于业务在 Tabs 外渲染内容或切换路由。 */
695
+ onAfterChange?: (key: string) => void;
696
+ /** 是否等选中框动画结束后再切换 items[].children,等待期间保留旧面板。默认 false,立即切换;true 时配合 destroyInactiveTabPane=false 仅在首次激活后挂载并保留面板。不能延迟父组件已执行的同步计算或 onChange 中的渲染,请将外部重内容更新放到 onAfterChange。 */
697
+ deferContent?: boolean;
694
698
  /**
695
- * 线型、卡片型、胶囊型、圆角胶囊、紧凑下划线或科技样式
699
+ * 线型、卡片型、胶囊型、圆角胶囊、紧凑下划线或科技样式;tech-line 使用固定倾角的圆角斜切选中框,长标题仅扩展中段宽度;位移与尺寸在 300ms 内同步过渡,系统减少动态效果时直接切换。
696
700
  * @defaultValue "line"
697
701
  */
698
702
  type?: TabsType;
@@ -713,8 +717,10 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
713
717
  * @defaultValue false
714
718
  */
715
719
  centered?: boolean;
716
- /** 标签栏左侧标题,科技大屏型会附带装饰线 */
720
+ /** 标签栏左侧标题,科技大屏型会附带装饰线;折线科技型横向布局按标题内容及装饰留白确定宽度,窄屏时标题独占一行。 */
717
721
  tabBarTitle?: ReactNode;
722
+ /** 标签栏与内容面板之间的间距,单位 px,支持 0;未指定时上下布局为 16px,左右布局为 20px。仅影响当前 Tabs,不传递给嵌套实例。 */
723
+ contentGap?: number;
718
724
  /** 标签栏右侧额外操作 */
719
725
  tabBarExtraContent?: ReactNode;
720
726
  /**
@@ -735,7 +741,7 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
735
741
  onTabContextMenuClick?: (info: TabsContextMenuClickInfo) => void;
736
742
  }
737
743
  /** 选项卡组件。 */
738
- declare function Tabs({ items, activeKey, defaultActiveKey, onChange, type, size, tabPosition, orientation, centered, tabBarTitle, tabBarExtraContent, destroyInactiveTabPane, closable, onEdit, tabContextMenu, onTabContextMenuClick, className, ...props }: TabsProps): react.JSX.Element;
744
+ declare function Tabs({ items, activeKey, defaultActiveKey, onChange, onAfterChange, deferContent, type, size, tabPosition, orientation, centered, tabBarTitle, tabBarExtraContent, contentGap, destroyInactiveTabPane, closable, onEdit, tabContextMenu, onTabContextMenuClick, className, ...props }: TabsProps): react.JSX.Element;
739
745
 
740
746
  /** 打开下拉菜单的触发方式。 */
741
747
  type DropdownTrigger = "click" | "hover" | "contextMenu";