Migrating new components
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 3s

This commit is contained in:
2026-04-20 23:31:14 +02:00
parent 6fc32614b1
commit d900659d88
59 changed files with 1824 additions and 1554 deletions

75
components/Tabs.js Normal file
View File

@@ -0,0 +1,75 @@
// components/Tabs.js
import { Tag, $, Watch } from "sigpro";
export const Tabs = (props) => {
const { items, class: className, onTabClose, ...rest } = props;
const itemsSignal = typeof items === "function" ? items : () => items || [];
const activeIndex = $(0);
Watch(() => {
const list = itemsSignal();
const idx = list.findIndex(it => {
const active = it.active;
return typeof active === "function" ? active() : active;
});
if (idx !== -1 && activeIndex() !== idx) activeIndex(idx);
});
const removeTab = (idx, item) => {
item.onClose?.();
onTabClose?.(item, idx);
const current = itemsSignal();
if (typeof items !== "function" || items._isComputed) return;
const newItems = current.filter((_, i) => i !== idx);
items(newItems);
if (newItems.length) {
let newIdx = activeIndex();
if (idx < newIdx) newIdx--;
else if (idx === newIdx) newIdx = Math.min(newIdx, newItems.length - 1);
activeIndex(newIdx);
}
};
return Tag("div", { ...rest, class: `tabs ${className || ''}`.trim() }, () => {
const list = itemsSignal();
const elements = [];
for (let i = 0; i < list.length; i++) {
const item = list[i];
const label = typeof item.label === "function" ? item.label() : item.label;
const closable = typeof item.closable === "function" ? item.closable() : item.closable;
const btnContent = closable
? Tag("span", { class: "flex items-center" }, [
label,
Tag("span", {
class: "icon-[lucide--x] w-3.5 h-3.5 ml-2 cursor-pointer hover:opacity-70",
onclick: (e) => { e.stopPropagation(); removeTab(i, item); }
})
])
: label;
const tabBtn = Tag("button", {
class: () => `tab ${activeIndex() === i ? 'tab-active' : ''}`,
onclick: (e) => {
e.preventDefault();
const disabled = typeof item.disabled === "function" ? item.disabled() : item.disabled;
if (!disabled) {
item.onclick?.();
activeIndex(i);
}
}
}, btnContent);
elements.push(item.tip ? Tag("div", { class: "tooltip", "data-tip": item.tip }, tabBtn) : tabBtn);
const content = typeof item.content === "function" ? item.content() : item.content;
elements.push(
Tag("div", {
class: "tab-content bg-base-100 border-base-300 p-6",
style: () => `display: ${activeIndex() === i ? 'block' : 'none'}`
}, content)
);
}
return elements;
});
};