@sia.soul/sia-react-ui 0.1.38 → 0.1.40
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/{chunk-DQ7WGTKA.js → chunk-CEZ4GW3X.js} +97 -44
- package/dist/{core-BzBVS9md.d.cts → core-BAHoW7_e.d.cts} +2 -2
- package/dist/{core-CBURAp9W.d.ts → core-fn8h4yRg.d.ts} +2 -2
- package/dist/core.cjs +254 -201
- package/dist/core.css +63 -23
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/index.cjs +536 -483
- package/dist/index.css +63 -23
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -190,6 +190,7 @@ function Tag({
|
|
|
190
190
|
// src/components/Tabs.tsx
|
|
191
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
|
+
import { createElement } from "react";
|
|
193
194
|
function Tabs({
|
|
194
195
|
items,
|
|
195
196
|
activeKey,
|
|
@@ -224,6 +225,42 @@ function Tabs({
|
|
|
224
225
|
const sliding = ["capsule", "line", "underline", "tech", "tech-line"].includes(type);
|
|
225
226
|
const listRef = useRef2(null);
|
|
226
227
|
const indicatorRef = useRef2(null);
|
|
228
|
+
const moreRef = useRef2(null);
|
|
229
|
+
const [overflow, setOverflow] = useState3({ count: items.length, width: 0 });
|
|
230
|
+
const [moreOpen, setMoreOpen] = useState3(false);
|
|
231
|
+
const visibleCount = resolvedOrientation === "horizontal" ? overflow.count : items.length;
|
|
232
|
+
const hiddenItems = items.slice(visibleCount);
|
|
233
|
+
useLayoutEffect2(() => {
|
|
234
|
+
const list = listRef.current;
|
|
235
|
+
if (!list || resolvedOrientation !== "horizontal") return;
|
|
236
|
+
const entries = Array.from(list.children).filter((node) => node instanceof HTMLElement && node.hasAttribute("data-tab-entry"));
|
|
237
|
+
const measure = () => {
|
|
238
|
+
const css = getComputedStyle(list);
|
|
239
|
+
const gap = parseFloat(css.columnGap) || 0;
|
|
240
|
+
const padding = (parseFloat(css.paddingLeft) || 0) + (parseFloat(css.paddingRight) || 0);
|
|
241
|
+
const widths = entries.map((node) => node.getBoundingClientRect().width);
|
|
242
|
+
const total = widths.reduce((sum, width2) => sum + width2, 0) + gap * Math.max(0, widths.length - 1);
|
|
243
|
+
const available = Math.max(0, list.clientWidth - padding);
|
|
244
|
+
let count = widths.length;
|
|
245
|
+
if (total > available + 0.5) {
|
|
246
|
+
const moreWidth = moreRef.current?.getBoundingClientRect().width ?? 36;
|
|
247
|
+
let used = moreWidth;
|
|
248
|
+
count = 0;
|
|
249
|
+
for (const width2 of widths) {
|
|
250
|
+
if (used + gap + width2 > available + 0.5) break;
|
|
251
|
+
used += gap + width2;
|
|
252
|
+
count++;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const width = Math.ceil(total + padding + (parseFloat(css.borderLeftWidth) || 0) + (parseFloat(css.borderRightWidth) || 0));
|
|
256
|
+
setOverflow((previous) => previous.count === count && previous.width === width ? previous : { count, width });
|
|
257
|
+
};
|
|
258
|
+
measure();
|
|
259
|
+
const observer = new ResizeObserver(measure);
|
|
260
|
+
observer.observe(list);
|
|
261
|
+
entries.forEach((entry) => observer.observe(entry));
|
|
262
|
+
return () => observer.disconnect();
|
|
263
|
+
}, [items, type, size, closable, resolvedOrientation, tabBarTitle, tabBarExtraContent]);
|
|
227
264
|
const [contentState, setContentState] = useState3(() => ({ key: selectedKey, visited: /* @__PURE__ */ new Set([selectedKey]) }));
|
|
228
265
|
const settledKeyRef = useRef2(selectedKey);
|
|
229
266
|
const afterChangeRef = useRef2(onAfterChange);
|
|
@@ -238,13 +275,14 @@ function Tabs({
|
|
|
238
275
|
function updateIndicator() {
|
|
239
276
|
if (!list || !indicator) return;
|
|
240
277
|
const active = list.querySelector('[role="tab"][aria-selected="true"]');
|
|
241
|
-
if (!active) {
|
|
278
|
+
if (!active || active.closest('[data-tab-hidden="true"]')) {
|
|
242
279
|
indicator.style.visibility = "hidden";
|
|
243
280
|
return;
|
|
244
281
|
}
|
|
245
282
|
const listRect = list.getBoundingClientRect();
|
|
246
283
|
const rect = active.getBoundingClientRect();
|
|
247
|
-
|
|
284
|
+
const currentRect = active.getBoundingClientRect();
|
|
285
|
+
indicator.style.transform = `translate(${currentRect.left - listRect.left - list.clientLeft + list.scrollLeft}px, ${currentRect.top - listRect.top - list.clientTop + list.scrollTop}px)`;
|
|
248
286
|
indicator.style.width = `${rect.width}px`;
|
|
249
287
|
indicator.style.height = `${rect.height}px`;
|
|
250
288
|
indicator.style.visibility = "visible";
|
|
@@ -258,7 +296,7 @@ function Tabs({
|
|
|
258
296
|
observer.observe(list);
|
|
259
297
|
list.querySelectorAll('[role="tab"]').forEach((tab) => observer.observe(tab));
|
|
260
298
|
return () => observer.disconnect();
|
|
261
|
-
}, [sliding, type, selectedKey, items, size, resolvedPosition, centered, closable]);
|
|
299
|
+
}, [sliding, type, selectedKey, items, size, resolvedPosition, centered, closable, visibleCount]);
|
|
262
300
|
useEffect(() => {
|
|
263
301
|
let cancelled = false;
|
|
264
302
|
let frame = 0;
|
|
@@ -292,7 +330,8 @@ function Tabs({
|
|
|
292
330
|
}
|
|
293
331
|
function focusAndSelect(key) {
|
|
294
332
|
selectTab(key);
|
|
295
|
-
|
|
333
|
+
if (items.findIndex((item) => item.key === key) >= visibleCount) moreRef.current?.focus();
|
|
334
|
+
else document.getElementById(`${baseId}-tab-${key}`)?.focus();
|
|
296
335
|
}
|
|
297
336
|
function handleKeyDown(event, key) {
|
|
298
337
|
const previousKey = resolvedOrientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
|
|
@@ -318,49 +357,49 @@ function Tabs({
|
|
|
318
357
|
tabBarTitle,
|
|
319
358
|
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
|
|
320
359
|
] }) : null,
|
|
321
|
-
/* @__PURE__ */ jsxs4("div", { ref: listRef, className: `sia-tabs__list${sliding ? " sia-tabs__list--sliding" : ""}`, role: "tablist", "aria-orientation": resolvedOrientation, children: [
|
|
360
|
+
/* @__PURE__ */ jsxs4("div", { ref: listRef, style: resolvedOrientation === "horizontal" && overflow.width ? { width: overflow.width } : void 0, className: `sia-tabs__list${sliding ? " sia-tabs__list--sliding" : ""}`, role: "tablist", "aria-orientation": resolvedOrientation, children: [
|
|
322
361
|
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,
|
|
323
|
-
items.map((item) => {
|
|
362
|
+
items.map((item, index) => {
|
|
324
363
|
const selected = item.key === selectedKey;
|
|
325
364
|
const canClose = item.closable ?? closable;
|
|
326
|
-
const tabNode = /* @__PURE__ */ jsxs4("span", { className: `sia-tabs__tab-wrap${canClose ? " is-closable" : ""}`, children: [
|
|
327
|
-
/* @__PURE__ */ jsxs4(
|
|
328
|
-
"button",
|
|
329
|
-
{
|
|
330
|
-
id: `${baseId}-tab-${item.key}`,
|
|
331
|
-
type: "button",
|
|
332
|
-
className: "sia-tabs__tab",
|
|
333
|
-
role: "tab",
|
|
334
|
-
"aria-selected": selected,
|
|
335
|
-
"aria-controls": hasPanels ? `${baseId}-panel-${item.key}` : void 0,
|
|
336
|
-
tabIndex: selected ? 0 : -1,
|
|
337
|
-
disabled: item.disabled,
|
|
338
|
-
onClick: () => selectTab(item.key),
|
|
339
|
-
onKeyDown: (event) => handleKeyDown(event, item.key),
|
|
340
|
-
children: [
|
|
341
|
-
type === "tech-line" ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__tab-shape", "aria-hidden": "true" }) : null,
|
|
342
|
-
item.icon ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__icon", children: item.icon }) : null,
|
|
343
|
-
/* @__PURE__ */ jsx4("span", { children: item.label })
|
|
344
|
-
]
|
|
345
|
-
}
|
|
346
|
-
),
|
|
347
|
-
canClose ? /* @__PURE__ */ jsx4(
|
|
348
|
-
"button",
|
|
349
|
-
{
|
|
350
|
-
type: "button",
|
|
351
|
-
className: "sia-tabs__close",
|
|
352
|
-
"aria-label": `\u5173\u95ED${typeof item.label === "string" ? item.label : "\u6807\u7B7E\u9875"}`,
|
|
353
|
-
disabled: item.disabled,
|
|
354
|
-
onClick: () => onEdit?.(item.key, "remove"),
|
|
355
|
-
children: /* @__PURE__ */ jsx4(Icon, { name: "close", size: 12 })
|
|
356
|
-
}
|
|
357
|
-
) : null
|
|
358
|
-
] }, item.key);
|
|
359
365
|
const contextMenuItems = typeof tabContextMenu === "function" ? tabContextMenu(item) : tabContextMenu;
|
|
366
|
+
const entryProps = { "data-tab-entry": "", "data-tab-hidden": index >= visibleCount, "aria-hidden": index >= visibleCount || void 0 };
|
|
367
|
+
const tabNode = /* @__PURE__ */ createElement("span", { ...!contextMenuItems?.length ? entryProps : {}, className: `sia-tabs__tab-wrap${canClose ? " is-closable" : ""}`, key: item.key }, /* @__PURE__ */ jsxs4(
|
|
368
|
+
"button",
|
|
369
|
+
{
|
|
370
|
+
id: `${baseId}-tab-${item.key}`,
|
|
371
|
+
type: "button",
|
|
372
|
+
className: "sia-tabs__tab",
|
|
373
|
+
role: "tab",
|
|
374
|
+
"aria-selected": selected,
|
|
375
|
+
"aria-controls": hasPanels ? `${baseId}-panel-${item.key}` : void 0,
|
|
376
|
+
tabIndex: selected ? 0 : -1,
|
|
377
|
+
disabled: item.disabled,
|
|
378
|
+
onClick: () => selectTab(item.key),
|
|
379
|
+
onKeyDown: (event) => handleKeyDown(event, item.key),
|
|
380
|
+
children: [
|
|
381
|
+
type === "tech-line" ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__tab-shape", "aria-hidden": "true" }) : null,
|
|
382
|
+
item.icon ? /* @__PURE__ */ jsx4("span", { className: "sia-tabs__icon", children: item.icon }) : null,
|
|
383
|
+
/* @__PURE__ */ jsx4("span", { children: item.label })
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
), canClose ? /* @__PURE__ */ jsx4(
|
|
387
|
+
"button",
|
|
388
|
+
{
|
|
389
|
+
type: "button",
|
|
390
|
+
className: "sia-tabs__close",
|
|
391
|
+
"aria-label": `\u5173\u95ED${typeof item.label === "string" ? item.label : "\u6807\u7B7E\u9875"}`,
|
|
392
|
+
disabled: item.disabled,
|
|
393
|
+
onClick: () => onEdit?.(item.key, "remove"),
|
|
394
|
+
children: /* @__PURE__ */ jsx4(Icon, { name: "close", size: 12 })
|
|
395
|
+
}
|
|
396
|
+
) : null);
|
|
360
397
|
if (!contextMenuItems?.length) return tabNode;
|
|
361
|
-
return /* @__PURE__ */
|
|
398
|
+
return /* @__PURE__ */ createElement(
|
|
362
399
|
Dropdown,
|
|
363
400
|
{
|
|
401
|
+
...entryProps,
|
|
402
|
+
key: item.key,
|
|
364
403
|
className: "sia-tabs__tab-context-trigger",
|
|
365
404
|
popupClassName: "sia-tabs__tab-context-menu",
|
|
366
405
|
trigger: ["contextMenu"],
|
|
@@ -369,12 +408,26 @@ function Tabs({
|
|
|
369
408
|
items: contextMenuItems,
|
|
370
409
|
selectable: false,
|
|
371
410
|
onClick: (info) => onTabContextMenuClick?.({ ...info, tabKey: item.key, tab: item })
|
|
372
|
-
}
|
|
373
|
-
children: tabNode
|
|
411
|
+
}
|
|
374
412
|
},
|
|
375
|
-
|
|
413
|
+
tabNode
|
|
376
414
|
);
|
|
377
|
-
})
|
|
415
|
+
}),
|
|
416
|
+
resolvedOrientation === "horizontal" ? /* @__PURE__ */ jsx4(
|
|
417
|
+
Dropdown,
|
|
418
|
+
{
|
|
419
|
+
className: "sia-tabs__more",
|
|
420
|
+
"data-tab-hidden": !hiddenItems.length,
|
|
421
|
+
trigger: ["hover", "click"],
|
|
422
|
+
placement: "bottomRight",
|
|
423
|
+
showTriggerIcon: false,
|
|
424
|
+
autoFocus: true,
|
|
425
|
+
open: moreOpen && hiddenItems.length > 0,
|
|
426
|
+
onOpenChange: setMoreOpen,
|
|
427
|
+
menu: { items: hiddenItems.map((item) => ({ key: item.key, label: item.label, icon: item.icon, disabled: item.disabled })), selectedKeys: [selectedKey], onClick: (info) => selectTab(info.key) },
|
|
428
|
+
children: /* @__PURE__ */ jsx4(Button, { ref: moreRef, variant: "text", icon: /* @__PURE__ */ jsx4(Icon, { name: "more-horizontal" }), "aria-label": "\u66F4\u591A\u9009\u9879\u5361", onKeyDown: (event) => handleKeyDown(event, selectedKey), className: hiddenItems.some((item) => item.key === selectedKey) ? "is-active" : void 0 })
|
|
429
|
+
}
|
|
430
|
+
) : null
|
|
378
431
|
] }),
|
|
379
432
|
tabBarExtraContent ? /* @__PURE__ */ jsx4("div", { className: "sia-tabs__extra", children: tabBarExtraContent }) : null
|
|
380
433
|
] }),
|
|
@@ -721,7 +721,7 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
|
721
721
|
* @defaultValue false
|
|
722
722
|
*/
|
|
723
723
|
centered?: boolean;
|
|
724
|
-
/**
|
|
724
|
+
/** 标签栏左侧标题,支持文本或 React 节点;科技样式附带装饰线。 */
|
|
725
725
|
tabBarTitle?: ReactNode;
|
|
726
726
|
/** 标签栏与内容面板之间的间距,单位 px,支持 0;未指定时上下布局为 16px,左右布局为 20px。仅影响当前 Tabs,不传递给嵌套实例。 */
|
|
727
727
|
contentGap?: number;
|
|
@@ -744,7 +744,7 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
|
744
744
|
/** 点击选项卡右键菜单时调用。 */
|
|
745
745
|
onTabContextMenuClick?: (info: TabsContextMenuClickInfo) => void;
|
|
746
746
|
}
|
|
747
|
-
/**
|
|
747
|
+
/** 选项卡组件;横向空间不足时,剩余标签收起到末尾的更多菜单,支持悬停或点击展开。 */
|
|
748
748
|
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;
|
|
749
749
|
|
|
750
750
|
/** 打开下拉菜单的触发方式。 */
|
|
@@ -721,7 +721,7 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
|
721
721
|
* @defaultValue false
|
|
722
722
|
*/
|
|
723
723
|
centered?: boolean;
|
|
724
|
-
/**
|
|
724
|
+
/** 标签栏左侧标题,支持文本或 React 节点;科技样式附带装饰线。 */
|
|
725
725
|
tabBarTitle?: ReactNode;
|
|
726
726
|
/** 标签栏与内容面板之间的间距,单位 px,支持 0;未指定时上下布局为 16px,左右布局为 20px。仅影响当前 Tabs,不传递给嵌套实例。 */
|
|
727
727
|
contentGap?: number;
|
|
@@ -744,7 +744,7 @@ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
|
744
744
|
/** 点击选项卡右键菜单时调用。 */
|
|
745
745
|
onTabContextMenuClick?: (info: TabsContextMenuClickInfo) => void;
|
|
746
746
|
}
|
|
747
|
-
/**
|
|
747
|
+
/** 选项卡组件;横向空间不足时,剩余标签收起到末尾的更多菜单,支持悬停或点击展开。 */
|
|
748
748
|
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;
|
|
749
749
|
|
|
750
750
|
/** 打开下拉菜单的触发方式。 */
|