up
This commit is contained in:
120
css/sigpro.css
120
css/sigpro.css
@@ -1417,6 +1417,89 @@
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.tabs-lift {
|
||||
@layer daisyui.l1.l2 {
|
||||
--tabs-height: auto;
|
||||
--tabs-direction: row;
|
||||
> .tab {
|
||||
--tab-border: 0 0 var(--border) 0;
|
||||
--tab-radius-ss: var(--tab-radius-limit);
|
||||
--tab-radius-se: var(--tab-radius-limit);
|
||||
--tab-radius-es: 0;
|
||||
--tab-radius-ee: 0;
|
||||
--tab-paddings: var(--border) var(--tab-p) 0 var(--tab-p);
|
||||
--tab-border-colors: #0000 #0000 var(--tab-border-color) #0000;
|
||||
--tab-corner-width: calc(100% + var(--tab-radius-limit) * 2);
|
||||
--tab-corner-height: var(--tab-radius-limit);
|
||||
--tab-corner-position: top left, top right;
|
||||
border-width: var(--tab-border);
|
||||
border-start-start-radius: var(--tab-radius-ss);
|
||||
border-start-end-radius: var(--tab-radius-se);
|
||||
border-end-start-radius: var(--tab-radius-es);
|
||||
border-end-end-radius: var(--tab-radius-ee);
|
||||
padding: var(--tab-paddings);
|
||||
border-color: var(--tab-border-colors);
|
||||
&:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]):not( .tab-disabled, [disabled] ), &:is(input:checked, label:has(:checked)) {
|
||||
--tab-border: var(--border) var(--border) 0 var(--border);
|
||||
--tab-border-colors: var(--tab-border-color) var(--tab-border-color) #0000
|
||||
var(--tab-border-color);
|
||||
--tab-paddings: 0 calc(var(--tab-p) - var(--border)) var(--border)
|
||||
calc(var(--tab-p) - var(--border));
|
||||
--tab-inset: auto auto 0 auto;
|
||||
--radius-start: radial-gradient(circle at top left, var(--tab-radius-grad));
|
||||
--radius-end: radial-gradient(circle at top right, var(--tab-radius-grad));
|
||||
background-color: var(--tab-bg);
|
||||
&:before {
|
||||
z-index: 1;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: var(--tab-corner-width);
|
||||
height: var(--tab-corner-height);
|
||||
background-position: var(--tab-corner-position);
|
||||
background-image: var(--radius-start), var(--radius-end);
|
||||
background-size: var(--tab-radius-limit) var(--tab-radius-limit);
|
||||
background-repeat: no-repeat;
|
||||
inset: var(--tab-inset);
|
||||
}
|
||||
&:first-child:before {
|
||||
--radius-start: none;
|
||||
}
|
||||
[dir="rtl"] &:first-child:before {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
&:last-child:before {
|
||||
--radius-end: none;
|
||||
}
|
||||
[dir="rtl"] &:last-child:before {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:has(> .tab-content) {
|
||||
> .tab:first-child {
|
||||
&:not(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) {
|
||||
--tab-border-colors: var(--tab-border-color) var(--tab-border-color) #0000
|
||||
var(--tab-border-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
> .tab-content {
|
||||
--tabcontent-margin: calc(-1 * var(--border)) 0 0 0;
|
||||
--tabcontent-radius-ss: 0;
|
||||
--tabcontent-radius-se: var(--radius-box);
|
||||
--tabcontent-radius-es: var(--radius-box);
|
||||
--tabcontent-radius-ee: var(--radius-box);
|
||||
}
|
||||
:checked, label:has(:checked), :is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) {
|
||||
& + .tab-content {
|
||||
&:nth-child(1), &:nth-child(n + 3) {
|
||||
--tabcontent-radius-ss: var(--radius-box);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.list {
|
||||
@layer daisyui.l1.l2.l3 {
|
||||
display: flex;
|
||||
@@ -2025,6 +2108,32 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabs-border {
|
||||
@layer daisyui.l1.l2 {
|
||||
> .tab {
|
||||
--tab-border-color: #0000 #0000 var(--tab-border-color) #0000;
|
||||
position: relative;
|
||||
border-radius: var(--radius-field);
|
||||
&:before {
|
||||
content: "";
|
||||
background-color: var(--tab-border-color);
|
||||
transition: background-color 0.2s ease;
|
||||
width: 80%;
|
||||
height: 3px;
|
||||
border-radius: var(--radius-field);
|
||||
bottom: 0;
|
||||
left: 10%;
|
||||
position: absolute;
|
||||
}
|
||||
&:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]):not( .tab-disabled, [disabled] ), &:is(input:checked), &:is(label:has(:checked)) {
|
||||
&:before {
|
||||
--tab-border-color: currentColor;
|
||||
border-top: 3px solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.select {
|
||||
@layer daisyui.l1.l2.l3 {
|
||||
border: var(--border) solid #0000;
|
||||
@@ -6020,6 +6129,11 @@
|
||||
.filter {
|
||||
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||
}
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
.transition-all {
|
||||
transition-property: all;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
@@ -6793,16 +6907,16 @@
|
||||
}
|
||||
.floating-label span {
|
||||
color: oklch(30% 0.01 260);
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.floating-label:focus-within span {
|
||||
color: oklch(25% 0.02 260);
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.floating-label:has(input:not(:placeholder-shown)) span {
|
||||
color: oklch(28% 0.01 260);
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
@layer base {
|
||||
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
|
||||
|
||||
2
css/sigpro.min.css
vendored
2
css/sigpro.min.css
vendored
File diff suppressed because one or more lines are too long
135
dist/sigpro-ui.esm.js
vendored
135
dist/sigpro-ui.esm.js
vendored
@@ -73,7 +73,6 @@ var createEffect = (fn, isComputed = false) => {
|
||||
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||
effect._mounts = [];
|
||||
effect._parent = activeOwner;
|
||||
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||
if (activeOwner)
|
||||
(activeOwner._children ||= new Set).add(effect);
|
||||
return effect;
|
||||
@@ -230,19 +229,6 @@ var cleanupNode = (node) => {
|
||||
if (node.childNodes)
|
||||
node.childNodes.forEach(cleanupNode);
|
||||
};
|
||||
var provide = (key, value) => {
|
||||
if (activeOwner)
|
||||
activeOwner._provisions[key] = value;
|
||||
};
|
||||
var inject = (key, defaultValue) => {
|
||||
let ctx = activeOwner;
|
||||
while (ctx) {
|
||||
if (key in ctx._provisions)
|
||||
return ctx._provisions[key];
|
||||
ctx = ctx._parent;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||
var validateAttr = (key, val) => {
|
||||
@@ -263,16 +249,9 @@ var Tag2 = (tag, props = {}, children = []) => {
|
||||
props = {};
|
||||
}
|
||||
if (isFunc(tag)) {
|
||||
const ctx = {
|
||||
_mounts: [],
|
||||
_cleanups: new Set,
|
||||
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||
};
|
||||
const ctx = { _mounts: [], _cleanups: new Set };
|
||||
const effect = createEffect(() => {
|
||||
const result2 = tag(props, {
|
||||
children,
|
||||
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||
});
|
||||
const result2 = tag(props, { children, emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) });
|
||||
effect._result = result2;
|
||||
return result2;
|
||||
});
|
||||
@@ -444,26 +423,30 @@ var For2 = (src, itemFn, keyFn) => {
|
||||
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||
let cache = new Map;
|
||||
Watch2(() => (isFunc(src) ? src() : src) || [], (items) => {
|
||||
const next = new Map, order = [];
|
||||
for (let i = 0;i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const key = keyFn ? keyFn(item, i) : i;
|
||||
const nextCache = new Map;
|
||||
const nextOrder = [];
|
||||
const newItems = items || [];
|
||||
for (let i = 0;i < newItems.length; i++) {
|
||||
const item = newItems[i];
|
||||
const key = keyFn ? keyFn(item, i) : item?.id ?? i;
|
||||
let view = cache.get(key);
|
||||
if (!view)
|
||||
view = Render(() => itemFn(item, i));
|
||||
next.set(key, view);
|
||||
order.push(key);
|
||||
cache.delete(key);
|
||||
else
|
||||
cache.delete(key);
|
||||
nextCache.set(key, view);
|
||||
nextOrder.push(view);
|
||||
}
|
||||
cache.forEach((v) => v.destroy());
|
||||
cache = next;
|
||||
let ref = anchor;
|
||||
for (let i = order.length - 1;i >= 0; i--) {
|
||||
const view = next.get(order[i]);
|
||||
if (view.container.nextSibling !== ref)
|
||||
root.insertBefore(view.container, ref);
|
||||
ref = view.container;
|
||||
cache.forEach((view) => view.destroy());
|
||||
let lastRef = anchor;
|
||||
for (let i = nextOrder.length - 1;i >= 0; i--) {
|
||||
const view = nextOrder[i];
|
||||
const node = view.container;
|
||||
if (node.nextSibling !== lastRef)
|
||||
root.insertBefore(node, lastRef);
|
||||
lastRef = node;
|
||||
}
|
||||
cache = nextCache;
|
||||
});
|
||||
return root;
|
||||
};
|
||||
@@ -511,7 +494,7 @@ var Mount2 = (comp, target) => {
|
||||
MOUNTED_NODES.set(t, inst);
|
||||
return inst;
|
||||
};
|
||||
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, untrack, onMount, onUnmount, provide, inject });
|
||||
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, onMount, onUnmount });
|
||||
if (typeof window !== "undefined") {
|
||||
Object.assign(window, SigPro);
|
||||
"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||
@@ -823,7 +806,7 @@ var Autocomplete = (props) => {
|
||||
...rest
|
||||
}),
|
||||
Tag("ul", {
|
||||
class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
class: "absolute dropdown-menu left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
style: () => isOpen() && list().length ? "display:block" : "display:none"
|
||||
}, [
|
||||
For(list, (opt, i) => Tag("li", {}, [
|
||||
@@ -1755,50 +1738,38 @@ var Tabs = (props) => {
|
||||
if (idx !== -1 && idx !== activeIndex())
|
||||
activeIndex(idx);
|
||||
});
|
||||
return Tag("div", { ...rest, class: "w-full" }, [
|
||||
Tag("div", {
|
||||
role: "tablist",
|
||||
class: ui("tabs", className || "tabs-box")
|
||||
}, () => {
|
||||
const list = itemsSignal();
|
||||
return list.map((it, idx) => {
|
||||
const isSelected = () => activeIndex() === idx;
|
||||
const tab = Tag("button", {
|
||||
role: "tab",
|
||||
class: () => ui("tab", isSelected() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(it.disabled)) {
|
||||
if (it.onclick)
|
||||
it.onclick();
|
||||
activeIndex(idx);
|
||||
}
|
||||
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
||||
const list = itemsSignal();
|
||||
const elements = [];
|
||||
for (let i = 0;i < list.length; i++) {
|
||||
const item = list[i];
|
||||
const isActive = () => activeIndex() === i;
|
||||
const button = Tag("button", {
|
||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(item.disabled)) {
|
||||
if (item.onclick)
|
||||
item.onclick();
|
||||
activeIndex(i);
|
||||
}
|
||||
});
|
||||
Watch(() => {
|
||||
const content = val(it.label);
|
||||
if (content instanceof Node) {
|
||||
tab.replaceChildren(content);
|
||||
} else {
|
||||
tab.textContent = String(content);
|
||||
}
|
||||
});
|
||||
return tab;
|
||||
}
|
||||
});
|
||||
}),
|
||||
Tag("div", { class: "tab-panels" }, () => {
|
||||
return itemsSignal().map((it, idx) => {
|
||||
const isVisible = () => activeIndex() === idx;
|
||||
return Tag("div", {
|
||||
role: "tabpanel",
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isVisible() ? "display: block" : "display: none"
|
||||
}, [
|
||||
() => typeof it.content === "function" ? it.content() : it.content
|
||||
]);
|
||||
});
|
||||
})
|
||||
]);
|
||||
const label = val(item.label);
|
||||
if (label instanceof Node) {
|
||||
button.replaceChildren(label);
|
||||
} else {
|
||||
button.textContent = String(label);
|
||||
}
|
||||
elements.push(button);
|
||||
const panel = Tag("div", {
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isActive() ? "display: block" : "display: none"
|
||||
}, () => val(item.content));
|
||||
elements.push(panel);
|
||||
}
|
||||
return elements;
|
||||
});
|
||||
};
|
||||
|
||||
// src/components/Timeline.js
|
||||
|
||||
6
dist/sigpro-ui.esm.min.js
vendored
6
dist/sigpro-ui.esm.min.js
vendored
File diff suppressed because one or more lines are too long
135
dist/sigpro-ui.js
vendored
135
dist/sigpro-ui.js
vendored
@@ -141,7 +141,6 @@
|
||||
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||
effect._mounts = [];
|
||||
effect._parent = activeOwner;
|
||||
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||
if (activeOwner)
|
||||
(activeOwner._children ||= new Set).add(effect);
|
||||
return effect;
|
||||
@@ -298,19 +297,6 @@
|
||||
if (node.childNodes)
|
||||
node.childNodes.forEach(cleanupNode);
|
||||
};
|
||||
var provide = (key, value) => {
|
||||
if (activeOwner)
|
||||
activeOwner._provisions[key] = value;
|
||||
};
|
||||
var inject = (key, defaultValue) => {
|
||||
let ctx = activeOwner;
|
||||
while (ctx) {
|
||||
if (key in ctx._provisions)
|
||||
return ctx._provisions[key];
|
||||
ctx = ctx._parent;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||
var validateAttr = (key, val) => {
|
||||
@@ -331,16 +317,9 @@
|
||||
props = {};
|
||||
}
|
||||
if (isFunc(tag)) {
|
||||
const ctx = {
|
||||
_mounts: [],
|
||||
_cleanups: new Set,
|
||||
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||
};
|
||||
const ctx = { _mounts: [], _cleanups: new Set };
|
||||
const effect = createEffect(() => {
|
||||
const result2 = tag(props, {
|
||||
children,
|
||||
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||
});
|
||||
const result2 = tag(props, { children, emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) });
|
||||
effect._result = result2;
|
||||
return result2;
|
||||
});
|
||||
@@ -512,26 +491,30 @@
|
||||
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||
let cache = new Map;
|
||||
Watch2(() => (isFunc(src) ? src() : src) || [], (items) => {
|
||||
const next = new Map, order = [];
|
||||
for (let i = 0;i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const key = keyFn ? keyFn(item, i) : i;
|
||||
const nextCache = new Map;
|
||||
const nextOrder = [];
|
||||
const newItems = items || [];
|
||||
for (let i = 0;i < newItems.length; i++) {
|
||||
const item = newItems[i];
|
||||
const key = keyFn ? keyFn(item, i) : item?.id ?? i;
|
||||
let view = cache.get(key);
|
||||
if (!view)
|
||||
view = Render(() => itemFn(item, i));
|
||||
next.set(key, view);
|
||||
order.push(key);
|
||||
cache.delete(key);
|
||||
else
|
||||
cache.delete(key);
|
||||
nextCache.set(key, view);
|
||||
nextOrder.push(view);
|
||||
}
|
||||
cache.forEach((v) => v.destroy());
|
||||
cache = next;
|
||||
let ref = anchor;
|
||||
for (let i = order.length - 1;i >= 0; i--) {
|
||||
const view = next.get(order[i]);
|
||||
if (view.container.nextSibling !== ref)
|
||||
root.insertBefore(view.container, ref);
|
||||
ref = view.container;
|
||||
cache.forEach((view) => view.destroy());
|
||||
let lastRef = anchor;
|
||||
for (let i = nextOrder.length - 1;i >= 0; i--) {
|
||||
const view = nextOrder[i];
|
||||
const node = view.container;
|
||||
if (node.nextSibling !== lastRef)
|
||||
root.insertBefore(node, lastRef);
|
||||
lastRef = node;
|
||||
}
|
||||
cache = nextCache;
|
||||
});
|
||||
return root;
|
||||
};
|
||||
@@ -579,7 +562,7 @@
|
||||
MOUNTED_NODES.set(t, inst);
|
||||
return inst;
|
||||
};
|
||||
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, untrack, onMount, onUnmount, provide, inject });
|
||||
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, onMount, onUnmount });
|
||||
if (typeof window !== "undefined") {
|
||||
Object.assign(window, SigPro);
|
||||
"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||
@@ -891,7 +874,7 @@
|
||||
...rest
|
||||
}),
|
||||
Tag("ul", {
|
||||
class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
class: "absolute dropdown-menu left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
style: () => isOpen() && list().length ? "display:block" : "display:none"
|
||||
}, [
|
||||
For(list, (opt, i) => Tag("li", {}, [
|
||||
@@ -1823,50 +1806,38 @@
|
||||
if (idx !== -1 && idx !== activeIndex())
|
||||
activeIndex(idx);
|
||||
});
|
||||
return Tag("div", { ...rest, class: "w-full" }, [
|
||||
Tag("div", {
|
||||
role: "tablist",
|
||||
class: ui("tabs", className || "tabs-box")
|
||||
}, () => {
|
||||
const list = itemsSignal();
|
||||
return list.map((it, idx) => {
|
||||
const isSelected = () => activeIndex() === idx;
|
||||
const tab = Tag("button", {
|
||||
role: "tab",
|
||||
class: () => ui("tab", isSelected() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(it.disabled)) {
|
||||
if (it.onclick)
|
||||
it.onclick();
|
||||
activeIndex(idx);
|
||||
}
|
||||
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
||||
const list = itemsSignal();
|
||||
const elements = [];
|
||||
for (let i = 0;i < list.length; i++) {
|
||||
const item = list[i];
|
||||
const isActive = () => activeIndex() === i;
|
||||
const button = Tag("button", {
|
||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(item.disabled)) {
|
||||
if (item.onclick)
|
||||
item.onclick();
|
||||
activeIndex(i);
|
||||
}
|
||||
});
|
||||
Watch(() => {
|
||||
const content = val(it.label);
|
||||
if (content instanceof Node) {
|
||||
tab.replaceChildren(content);
|
||||
} else {
|
||||
tab.textContent = String(content);
|
||||
}
|
||||
});
|
||||
return tab;
|
||||
}
|
||||
});
|
||||
}),
|
||||
Tag("div", { class: "tab-panels" }, () => {
|
||||
return itemsSignal().map((it, idx) => {
|
||||
const isVisible = () => activeIndex() === idx;
|
||||
return Tag("div", {
|
||||
role: "tabpanel",
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isVisible() ? "display: block" : "display: none"
|
||||
}, [
|
||||
() => typeof it.content === "function" ? it.content() : it.content
|
||||
]);
|
||||
});
|
||||
})
|
||||
]);
|
||||
const label = val(item.label);
|
||||
if (label instanceof Node) {
|
||||
button.replaceChildren(label);
|
||||
} else {
|
||||
button.textContent = String(label);
|
||||
}
|
||||
elements.push(button);
|
||||
const panel = Tag("div", {
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isActive() ? "display: block" : "display: none"
|
||||
}, () => val(item.content));
|
||||
elements.push(panel);
|
||||
}
|
||||
return elements;
|
||||
});
|
||||
};
|
||||
|
||||
// src/components/Timeline.js
|
||||
|
||||
6
dist/sigpro-ui.min.js
vendored
6
dist/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -372,6 +372,7 @@ const VariantsDemo = () => {
|
||||
const active1 = $('tab1');
|
||||
const active2 = $('tab1');
|
||||
const active3 = $('tab1');
|
||||
const active4 = $('tab4');
|
||||
|
||||
const createItems = (active) => [
|
||||
{
|
||||
@@ -391,6 +392,12 @@ const VariantsDemo = () => {
|
||||
active: () => active() === 'tab3',
|
||||
onclick: () => active('tab3'),
|
||||
content: Div({ class: 'p-4' }, 'Content 3')
|
||||
},
|
||||
{
|
||||
label: 'Tab 4',
|
||||
active: () => active() === 'tab4',
|
||||
onclick: () => active('tab4'),
|
||||
content: Div({ class: 'p-4' }, 'Content 4')
|
||||
}
|
||||
];
|
||||
|
||||
@@ -402,7 +409,10 @@ const VariantsDemo = () => {
|
||||
Tabs({ items: createItems(active2), class: 'tabs-box' }),
|
||||
|
||||
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
|
||||
Tabs({ items: createItems(active3), class: 'tabs-lifted' })
|
||||
Tabs({ items: createItems(active3), class: 'tabs-lift' }),
|
||||
|
||||
Div({ class: 'text-sm font-bold mt-4' }, 'Bordered Tabs'),
|
||||
Tabs({ items: createItems(active4), class: 'tabs-border' })
|
||||
]);
|
||||
};
|
||||
Mount(VariantsDemo, '#demo-variants');
|
||||
|
||||
135
docs/sigpro-ui.min.js
vendored
135
docs/sigpro-ui.min.js
vendored
@@ -141,7 +141,6 @@
|
||||
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||
effect._mounts = [];
|
||||
effect._parent = activeOwner;
|
||||
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||
if (activeOwner)
|
||||
(activeOwner._children ||= new Set).add(effect);
|
||||
return effect;
|
||||
@@ -298,19 +297,6 @@
|
||||
if (node.childNodes)
|
||||
node.childNodes.forEach(cleanupNode);
|
||||
};
|
||||
var provide = (key, value) => {
|
||||
if (activeOwner)
|
||||
activeOwner._provisions[key] = value;
|
||||
};
|
||||
var inject = (key, defaultValue) => {
|
||||
let ctx = activeOwner;
|
||||
while (ctx) {
|
||||
if (key in ctx._provisions)
|
||||
return ctx._provisions[key];
|
||||
ctx = ctx._parent;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
|
||||
var validateAttr = (key, val) => {
|
||||
@@ -331,16 +317,9 @@
|
||||
props = {};
|
||||
}
|
||||
if (isFunc(tag)) {
|
||||
const ctx = {
|
||||
_mounts: [],
|
||||
_cleanups: new Set,
|
||||
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||
};
|
||||
const ctx = { _mounts: [], _cleanups: new Set };
|
||||
const effect = createEffect(() => {
|
||||
const result2 = tag(props, {
|
||||
children,
|
||||
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||
});
|
||||
const result2 = tag(props, { children, emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) });
|
||||
effect._result = result2;
|
||||
return result2;
|
||||
});
|
||||
@@ -512,26 +491,30 @@
|
||||
const root = Tag2("div", { style: "display:contents" }, [anchor]);
|
||||
let cache = new Map;
|
||||
Watch2(() => (isFunc(src) ? src() : src) || [], (items) => {
|
||||
const next = new Map, order = [];
|
||||
for (let i = 0;i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const key = keyFn ? keyFn(item, i) : i;
|
||||
const nextCache = new Map;
|
||||
const nextOrder = [];
|
||||
const newItems = items || [];
|
||||
for (let i = 0;i < newItems.length; i++) {
|
||||
const item = newItems[i];
|
||||
const key = keyFn ? keyFn(item, i) : item?.id ?? i;
|
||||
let view = cache.get(key);
|
||||
if (!view)
|
||||
view = Render(() => itemFn(item, i));
|
||||
next.set(key, view);
|
||||
order.push(key);
|
||||
cache.delete(key);
|
||||
else
|
||||
cache.delete(key);
|
||||
nextCache.set(key, view);
|
||||
nextOrder.push(view);
|
||||
}
|
||||
cache.forEach((v) => v.destroy());
|
||||
cache = next;
|
||||
let ref = anchor;
|
||||
for (let i = order.length - 1;i >= 0; i--) {
|
||||
const view = next.get(order[i]);
|
||||
if (view.container.nextSibling !== ref)
|
||||
root.insertBefore(view.container, ref);
|
||||
ref = view.container;
|
||||
cache.forEach((view) => view.destroy());
|
||||
let lastRef = anchor;
|
||||
for (let i = nextOrder.length - 1;i >= 0; i--) {
|
||||
const view = nextOrder[i];
|
||||
const node = view.container;
|
||||
if (node.nextSibling !== lastRef)
|
||||
root.insertBefore(node, lastRef);
|
||||
lastRef = node;
|
||||
}
|
||||
cache = nextCache;
|
||||
});
|
||||
return root;
|
||||
};
|
||||
@@ -579,7 +562,7 @@
|
||||
MOUNTED_NODES.set(t, inst);
|
||||
return inst;
|
||||
};
|
||||
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, untrack, onMount, onUnmount, provide, inject });
|
||||
var SigPro = Object.freeze({ $: $2, $$, Watch: Watch2, Tag: Tag2, Render, If: If2, For: For2, Router, Mount: Mount2, onMount, onUnmount });
|
||||
if (typeof window !== "undefined") {
|
||||
Object.assign(window, SigPro);
|
||||
"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||
@@ -891,7 +874,7 @@
|
||||
...rest
|
||||
}),
|
||||
Tag("ul", {
|
||||
class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
class: "absolute dropdown-menu left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
style: () => isOpen() && list().length ? "display:block" : "display:none"
|
||||
}, [
|
||||
For(list, (opt, i) => Tag("li", {}, [
|
||||
@@ -1823,50 +1806,38 @@
|
||||
if (idx !== -1 && idx !== activeIndex())
|
||||
activeIndex(idx);
|
||||
});
|
||||
return Tag("div", { ...rest, class: "w-full" }, [
|
||||
Tag("div", {
|
||||
role: "tablist",
|
||||
class: ui("tabs", className || "tabs-box")
|
||||
}, () => {
|
||||
const list = itemsSignal();
|
||||
return list.map((it, idx) => {
|
||||
const isSelected = () => activeIndex() === idx;
|
||||
const tab = Tag("button", {
|
||||
role: "tab",
|
||||
class: () => ui("tab", isSelected() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(it.disabled)) {
|
||||
if (it.onclick)
|
||||
it.onclick();
|
||||
activeIndex(idx);
|
||||
}
|
||||
return Tag("div", { ...rest, class: ui("tabs", className) }, () => {
|
||||
const list = itemsSignal();
|
||||
const elements = [];
|
||||
for (let i = 0;i < list.length; i++) {
|
||||
const item = list[i];
|
||||
const isActive = () => activeIndex() === i;
|
||||
const button = Tag("button", {
|
||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(item.disabled)) {
|
||||
if (item.onclick)
|
||||
item.onclick();
|
||||
activeIndex(i);
|
||||
}
|
||||
});
|
||||
Watch(() => {
|
||||
const content = val(it.label);
|
||||
if (content instanceof Node) {
|
||||
tab.replaceChildren(content);
|
||||
} else {
|
||||
tab.textContent = String(content);
|
||||
}
|
||||
});
|
||||
return tab;
|
||||
}
|
||||
});
|
||||
}),
|
||||
Tag("div", { class: "tab-panels" }, () => {
|
||||
return itemsSignal().map((it, idx) => {
|
||||
const isVisible = () => activeIndex() === idx;
|
||||
return Tag("div", {
|
||||
role: "tabpanel",
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isVisible() ? "display: block" : "display: none"
|
||||
}, [
|
||||
() => typeof it.content === "function" ? it.content() : it.content
|
||||
]);
|
||||
});
|
||||
})
|
||||
]);
|
||||
const label = val(item.label);
|
||||
if (label instanceof Node) {
|
||||
button.replaceChildren(label);
|
||||
} else {
|
||||
button.textContent = String(label);
|
||||
}
|
||||
elements.push(button);
|
||||
const panel = Tag("div", {
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isActive() ? "display: block" : "display: none"
|
||||
}, () => val(item.content));
|
||||
elements.push(panel);
|
||||
}
|
||||
return elements;
|
||||
});
|
||||
};
|
||||
|
||||
// src/components/Timeline.js
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -79,7 +79,7 @@ export const Autocomplete = (props) => {
|
||||
Tag(
|
||||
"ul",
|
||||
{
|
||||
class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
class: "absolute dropdown-menu left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||
style: () => (isOpen() && list().length ? "display:block" : "display:none"),
|
||||
},
|
||||
[
|
||||
|
||||
@@ -6,7 +6,7 @@ import { val, ui } from "../core/utils.js";
|
||||
* Tabs component
|
||||
*
|
||||
* daisyUI classes used:
|
||||
* - tabs, tabs-box, tabs-lifted, tabs-bordered
|
||||
* - tabs, tabs-box, tabs-lift, tabs-border
|
||||
* - tab, tab-content
|
||||
* - bg-base-100, border-base-300, p-6
|
||||
*/
|
||||
@@ -20,56 +20,43 @@ export const Tabs = (props) => {
|
||||
if (idx !== -1 && idx !== activeIndex()) activeIndex(idx);
|
||||
});
|
||||
|
||||
return Tag("div", { ...rest, class: "w-full" }, [
|
||||
// 1. Tab List: Aplanamos los botones para que sean hijos directos
|
||||
Tag("div", {
|
||||
role: "tablist",
|
||||
class: ui('tabs', className || 'tabs-box')
|
||||
}, () => {
|
||||
const list = itemsSignal();
|
||||
return list.map((it, idx) => {
|
||||
const isSelected = () => activeIndex() === idx;
|
||||
|
||||
const tab = Tag("button", {
|
||||
role: "tab",
|
||||
class: () => ui("tab", isSelected() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(it.disabled)) {
|
||||
if (it.onclick) it.onclick();
|
||||
activeIndex(idx);
|
||||
}
|
||||
// Contenedor principal con las clases de DaisyUI
|
||||
return Tag("div", { ...rest, class: ui('tabs', className) }, () => {
|
||||
const list = itemsSignal();
|
||||
const elements = [];
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const item = list[i];
|
||||
const isActive = () => activeIndex() === i;
|
||||
|
||||
// Botón (tab)
|
||||
const button = Tag("button", {
|
||||
class: () => ui("tab", isActive() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(item.disabled)) {
|
||||
if (item.onclick) item.onclick();
|
||||
activeIndex(i);
|
||||
}
|
||||
});
|
||||
|
||||
// Mantenemos el watch para el label por si es dinámico
|
||||
Watch(() => {
|
||||
const content = val(it.label);
|
||||
if (content instanceof Node) {
|
||||
tab.replaceChildren(content);
|
||||
} else {
|
||||
tab.textContent = String(content);
|
||||
}
|
||||
});
|
||||
|
||||
return tab;
|
||||
}
|
||||
});
|
||||
}),
|
||||
|
||||
// 2. Tab Content: Aquí el display:contents no molesta tanto,
|
||||
// pero lo aplanamos por consistencia
|
||||
Tag("div", { class: "tab-panels" }, () => {
|
||||
return itemsSignal().map((it, idx) => {
|
||||
const isVisible = () => activeIndex() === idx;
|
||||
|
||||
return Tag("div", {
|
||||
role: "tabpanel",
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isVisible() ? "display: block" : "display: none"
|
||||
}, [
|
||||
() => typeof it.content === "function" ? it.content() : it.content
|
||||
]);
|
||||
});
|
||||
})
|
||||
]);
|
||||
// Asignar etiqueta (puede ser texto o nodo)
|
||||
const label = val(item.label);
|
||||
if (label instanceof Node) {
|
||||
button.replaceChildren(label);
|
||||
} else {
|
||||
button.textContent = String(label);
|
||||
}
|
||||
elements.push(button);
|
||||
|
||||
// Contenido (tab-content) - debe ir inmediatamente después del botón
|
||||
const panel = Tag("div", {
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isActive() ? "display: block" : "display: none"
|
||||
}, () => val(item.content));
|
||||
elements.push(panel);
|
||||
}
|
||||
|
||||
return elements;
|
||||
});
|
||||
};
|
||||
@@ -97,16 +97,16 @@
|
||||
|
||||
.floating-label span {
|
||||
color: oklch(30% 0.01 260); /* Gris más oscuro (30% es más oscuro que 45%) */
|
||||
font-size: 1.2rem; /* text-base: más grande que 0.875rem */
|
||||
font-size: 1.1rem; /* text-base: más grande que 0.875rem */
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.floating-label:focus-within span {
|
||||
color: oklch(25% 0.02 260); /* Aún más oscuro al enfocar */
|
||||
font-size: 1.2rem; /* Mantiene el mismo tamaño */
|
||||
font-size: 1.1rem; /* Mantiene el mismo tamaño */
|
||||
}
|
||||
|
||||
.floating-label:has(input:not(:placeholder-shown)) span {
|
||||
color: oklch(28% 0.01 260); /* Gris oscuro cuando tiene valor */
|
||||
font-size: 1.2rem; /* Mantiene el mismo tamaño */
|
||||
font-size: 1.1rem; /* Mantiene el mismo tamaño */
|
||||
}
|
||||
@@ -47,7 +47,6 @@ const createEffect = (fn, isComputed = false) => {
|
||||
effect._depth = activeEffect ? activeEffect._depth + 1 : 0;
|
||||
effect._mounts = [];
|
||||
effect._parent = activeOwner;
|
||||
effect._provisions = activeOwner ? { ...activeOwner._provisions } : {};
|
||||
if (activeOwner) (activeOwner._children ||= new Set()).add(effect);
|
||||
return effect;
|
||||
};
|
||||
@@ -162,20 +161,6 @@ const cleanupNode = node => {
|
||||
if (node.childNodes) node.childNodes.forEach(cleanupNode);
|
||||
};
|
||||
|
||||
// provide/inject
|
||||
const provide = (key, value) => {
|
||||
if (activeOwner) activeOwner._provisions[key] = value;
|
||||
};
|
||||
|
||||
const inject = (key, defaultValue) => {
|
||||
let ctx = activeOwner;
|
||||
while (ctx) {
|
||||
if (key in ctx._provisions) return ctx._provisions[key];
|
||||
ctx = ctx._parent;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
// --- Seguridad optimizada ---
|
||||
const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||
const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on');
|
||||
@@ -196,16 +181,9 @@ const validateAttr = (key, val) => {
|
||||
const Tag = (tag, props = {}, children = []) => {
|
||||
if (props instanceof Node || isArr(props) || !isObj(props)) { children = props; props = {}; }
|
||||
if (isFunc(tag)) {
|
||||
const ctx = {
|
||||
_mounts: [],
|
||||
_cleanups: new Set(),
|
||||
_provisions: activeOwner?._provisions ? { ...activeOwner._provisions } : {}
|
||||
};
|
||||
const ctx = { _mounts: [], _cleanups: new Set() };
|
||||
const effect = createEffect(() => {
|
||||
const result = tag(props, {
|
||||
children,
|
||||
emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args)
|
||||
});
|
||||
const result = tag(props, { children, emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) });
|
||||
effect._result = result;
|
||||
return result;
|
||||
});
|
||||
@@ -359,29 +337,28 @@ const For = (src, itemFn, keyFn) => {
|
||||
const anchor = doc.createTextNode("");
|
||||
const root = Tag("div", { style: "display:contents" }, [anchor]);
|
||||
let cache = new Map();
|
||||
Watch(
|
||||
() => (isFunc(src) ? src() : src) || [],
|
||||
(items) => {
|
||||
const next = new Map(), order = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const key = keyFn ? keyFn(item, i) : i;
|
||||
let view = cache.get(key);
|
||||
if (!view) view = Render(() => itemFn(item, i));
|
||||
next.set(key, view);
|
||||
order.push(key);
|
||||
cache.delete(key);
|
||||
}
|
||||
cache.forEach(v => v.destroy());
|
||||
cache = next;
|
||||
let ref = anchor;
|
||||
for (let i = order.length - 1; i >= 0; i--) {
|
||||
const view = next.get(order[i]);
|
||||
if (view.container.nextSibling !== ref) root.insertBefore(view.container, ref);
|
||||
ref = view.container;
|
||||
}
|
||||
Watch(() => (isFunc(src) ? src() : src) || [], (items) => {
|
||||
const nextCache = new Map();
|
||||
const nextOrder = [];
|
||||
const newItems = items || [];
|
||||
for (let i = 0; i < newItems.length; i++) {
|
||||
const item = newItems[i];
|
||||
const key = keyFn ? keyFn(item, i) : (item?.id ?? i);
|
||||
let view = cache.get(key);
|
||||
if (!view) view = Render(() => itemFn(item, i)); else cache.delete(key);
|
||||
nextCache.set(key, view);
|
||||
nextOrder.push(view);
|
||||
}
|
||||
);
|
||||
cache.forEach(view => view.destroy());
|
||||
let lastRef = anchor;
|
||||
for (let i = nextOrder.length - 1; i >= 0; i--) {
|
||||
const view = nextOrder[i];
|
||||
const node = view.container;
|
||||
if (node.nextSibling !== lastRef) root.insertBefore(node, lastRef);
|
||||
lastRef = node;
|
||||
}
|
||||
cache = nextCache;
|
||||
});
|
||||
return root;
|
||||
};
|
||||
|
||||
@@ -428,12 +405,12 @@ const Mount = (comp, target) => {
|
||||
return inst;
|
||||
};
|
||||
|
||||
const SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, provide, inject });
|
||||
const SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount });
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
Object.assign(window, SigPro);
|
||||
"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg"
|
||||
.split(" ").forEach(t => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c));
|
||||
}
|
||||
export { $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, provide, inject };
|
||||
export { $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount };
|
||||
export default SigPro;
|
||||
|
||||
440
src/sigpro1.js
Normal file
440
src/sigpro1.js
Normal file
@@ -0,0 +1,440 @@
|
||||
let activeEffect = null;
|
||||
let currentOwner = null;
|
||||
const effectQueue = new Set();
|
||||
let isFlushing = false;
|
||||
const MOUNTED_NODES = new WeakMap();
|
||||
|
||||
const doc = document;
|
||||
const isArr = Array.isArray;
|
||||
const assign = Object.assign;
|
||||
const createEl = (t) => doc.createElement(t);
|
||||
const createText = (t) => doc.createTextNode(String(t ?? ""));
|
||||
const isFunc = (f) => typeof f === "function";
|
||||
const isObj = (o) => typeof o === "object" && o !== null;
|
||||
|
||||
const runWithContext = (effect, callback) => {
|
||||
const previousEffect = activeEffect;
|
||||
activeEffect = effect;
|
||||
try { return callback(); }
|
||||
finally { activeEffect = previousEffect; }
|
||||
};
|
||||
|
||||
const cleanupNode = (node) => {
|
||||
if (node._cleanups) {
|
||||
node._cleanups.forEach((dispose) => dispose());
|
||||
node._cleanups.clear();
|
||||
}
|
||||
node.childNodes?.forEach(cleanupNode);
|
||||
};
|
||||
|
||||
const flushEffects = () => {
|
||||
if (isFlushing) return;
|
||||
isFlushing = true;
|
||||
while (effectQueue.size > 0) {
|
||||
const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0));
|
||||
effectQueue.clear();
|
||||
for (const effect of sortedEffects) {
|
||||
if (!effect._deleted) effect();
|
||||
}
|
||||
}
|
||||
isFlushing = false;
|
||||
};
|
||||
|
||||
const trackSubscription = (subscribers) => {
|
||||
if (activeEffect && !activeEffect._deleted) {
|
||||
subscribers.add(activeEffect);
|
||||
activeEffect._deps.add(subscribers);
|
||||
}
|
||||
};
|
||||
|
||||
const triggerUpdate = (subscribers) => {
|
||||
subscribers.forEach((effect) => {
|
||||
if (effect === activeEffect || effect._deleted) return;
|
||||
if (effect._isComputed) {
|
||||
effect.markDirty();
|
||||
if (effect._subs) triggerUpdate(effect._subs);
|
||||
} else {
|
||||
effectQueue.add(effect);
|
||||
}
|
||||
});
|
||||
if (!isFlushing) queueMicrotask(flushEffects);
|
||||
};
|
||||
|
||||
const Render = (renderFn) => {
|
||||
const cleanups = new Set();
|
||||
const previousOwner = currentOwner;
|
||||
const container = createEl("div");
|
||||
container.style.display = "contents";
|
||||
currentOwner = { cleanups };
|
||||
|
||||
const processResult = (result) => {
|
||||
if (!result) return;
|
||||
if (result._isRuntime) {
|
||||
cleanups.add(result.destroy);
|
||||
container.appendChild(result.container);
|
||||
} else if (isArr(result)) {
|
||||
result.forEach(processResult);
|
||||
} else {
|
||||
container.appendChild(result instanceof Node ? result : createText(result));
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) }));
|
||||
} finally { currentOwner = previousOwner; }
|
||||
|
||||
return {
|
||||
_isRuntime: true,
|
||||
container,
|
||||
destroy: () => {
|
||||
cleanups.forEach((fn) => fn());
|
||||
cleanupNode(container);
|
||||
container.remove();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const $ = (initialValue, storageKey = null) => {
|
||||
const subscribers = new Set();
|
||||
|
||||
if (isFunc(initialValue)) {
|
||||
let cachedValue, isDirty = true;
|
||||
const effect = () => {
|
||||
if (effect._deleted) return;
|
||||
effect._deps.forEach((dep) => dep.delete(effect));
|
||||
effect._deps.clear();
|
||||
|
||||
runWithContext(effect, () => {
|
||||
const newValue = initialValue();
|
||||
if (!Object.is(cachedValue, newValue) || isDirty) {
|
||||
cachedValue = newValue;
|
||||
isDirty = false;
|
||||
triggerUpdate(subscribers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
assign(effect, {
|
||||
_deps: new Set(),
|
||||
_isComputed: true,
|
||||
_subs: subscribers,
|
||||
_deleted: false,
|
||||
markDirty: () => (isDirty = true),
|
||||
stop: () => {
|
||||
effect._deleted = true;
|
||||
effect._deps.forEach((dep) => dep.delete(effect));
|
||||
subscribers.clear();
|
||||
}
|
||||
});
|
||||
|
||||
if (currentOwner) currentOwner.cleanups.add(effect.stop);
|
||||
return () => { if (isDirty) effect(); trackSubscription(subscribers); return cachedValue; };
|
||||
}
|
||||
|
||||
let value = initialValue;
|
||||
if (storageKey) {
|
||||
try {
|
||||
const saved = localStorage.getItem(storageKey);
|
||||
if (saved !== null) value = JSON.parse(saved);
|
||||
} catch (e) { console.warn("SigPro Storage Lock", e); }
|
||||
}
|
||||
|
||||
return (...args) => {
|
||||
if (args.length) {
|
||||
const nextValue = isFunc(args[0]) ? args[0](value) : args[0];
|
||||
if (!Object.is(value, nextValue)) {
|
||||
value = nextValue;
|
||||
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(value));
|
||||
triggerUpdate(subscribers);
|
||||
}
|
||||
}
|
||||
trackSubscription(subscribers);
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
const $$ = (object, cache = new WeakMap()) => {
|
||||
if (!isObj(object)) return object;
|
||||
if (cache.has(object)) return cache.get(object);
|
||||
|
||||
const keySubscribers = {};
|
||||
const proxy = new Proxy(object, {
|
||||
get(target, key) {
|
||||
if (activeEffect) trackSubscription(keySubscribers[key] ??= new Set());
|
||||
const value = Reflect.get(target, key);
|
||||
return isObj(value) ? $$(value, cache) : value;
|
||||
},
|
||||
set(target, key, value) {
|
||||
if (Object.is(target[key], value)) return true;
|
||||
const success = Reflect.set(target, key, value);
|
||||
if (keySubscribers[key]) triggerUpdate(keySubscribers[key]);
|
||||
return success;
|
||||
}
|
||||
});
|
||||
|
||||
cache.set(object, proxy);
|
||||
return proxy;
|
||||
};
|
||||
|
||||
const Watch = (target, callbackFn) => {
|
||||
const isExplicit = isArr(target);
|
||||
const callback = isExplicit ? callbackFn : target;
|
||||
if (!isFunc(callback)) return () => { };
|
||||
|
||||
const owner = currentOwner;
|
||||
const runner = () => {
|
||||
if (runner._deleted) return;
|
||||
runner._deps.forEach((dep) => dep.delete(runner));
|
||||
runner._deps.clear();
|
||||
runner._cleanups.forEach((cleanup) => cleanup());
|
||||
runner._cleanups.clear();
|
||||
|
||||
const previousOwner = currentOwner;
|
||||
runner.depth = activeEffect ? activeEffect.depth + 1 : 0;
|
||||
|
||||
runWithContext(runner, () => {
|
||||
currentOwner = { cleanups: runner._cleanups };
|
||||
if (isExplicit) {
|
||||
runWithContext(null, callback);
|
||||
target.forEach((dep) => isFunc(dep) && dep());
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
currentOwner = previousOwner;
|
||||
});
|
||||
};
|
||||
|
||||
assign(runner, {
|
||||
_deps: new Set(),
|
||||
_cleanups: new Set(),
|
||||
_deleted: false,
|
||||
stop: () => {
|
||||
if (runner._deleted) return;
|
||||
runner._deleted = true;
|
||||
effectQueue.delete(runner);
|
||||
runner._deps.forEach((dep) => dep.delete(runner));
|
||||
runner._cleanups.forEach((cleanup) => cleanup());
|
||||
if (owner) owner.cleanups.delete(runner.stop);
|
||||
}
|
||||
});
|
||||
|
||||
if (owner) owner.cleanups.add(runner.stop);
|
||||
runner();
|
||||
return runner.stop;
|
||||
};
|
||||
|
||||
const Tag = (tag, props = {}, children = []) => {
|
||||
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||
children = props; props = {};
|
||||
}
|
||||
|
||||
const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag);
|
||||
const element = isSVG
|
||||
? doc.createElementNS("http://www.w3.org/2000/svg", tag)
|
||||
: createEl(tag);
|
||||
|
||||
element._cleanups = new Set();
|
||||
element.onUnmount = (fn) => element._cleanups.add(fn);
|
||||
const booleanAttributes = ["disabled", "checked", "required", "readonly", "selected", "multiple", "autofocus"];
|
||||
|
||||
const updateAttribute = (name, value) => {
|
||||
const sanitized = (name === 'src' || name === 'href') && String(value).toLowerCase().includes('javascript:') ? '#' : value;
|
||||
if (booleanAttributes.includes(name)) {
|
||||
element[name] = !!sanitized;
|
||||
sanitized ? element.setAttribute(name, "") : element.removeAttribute(name);
|
||||
} else {
|
||||
sanitized == null ? element.removeAttribute(name) : element.setAttribute(name, sanitized);
|
||||
}
|
||||
};
|
||||
|
||||
for (let [key, value] of Object.entries(props)) {
|
||||
if (key === "ref") { (isFunc(value) ? value(element) : (value.current = element)); continue; }
|
||||
|
||||
const isSignal = isFunc(value);
|
||||
if (key.startsWith("on")) {
|
||||
const eventName = key.slice(2).toLowerCase().split(".")[0];
|
||||
element.addEventListener(eventName, value);
|
||||
element._cleanups.add(() => element.removeEventListener(eventName, value));
|
||||
} else if (isSignal) {
|
||||
element._cleanups.add(Watch(() => {
|
||||
const currentVal = value();
|
||||
key === "class" ? (element.className = currentVal || "") : updateAttribute(key, currentVal);
|
||||
}));
|
||||
if (["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName) && (key === "value" || key === "checked")) {
|
||||
const event = key === "checked" ? "change" : "input";
|
||||
const handler = (e) => value(e.target[key]);
|
||||
element.addEventListener(event, handler);
|
||||
element._cleanups.add(() => element.removeEventListener(event, handler));
|
||||
}
|
||||
} else {
|
||||
updateAttribute(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
const appendChildNode = (child) => {
|
||||
if (isArr(child)) return child.forEach(appendChildNode);
|
||||
if (isFunc(child)) {
|
||||
const marker = createText("");
|
||||
element.appendChild(marker);
|
||||
let currentNodes = [];
|
||||
element._cleanups.add(Watch(() => {
|
||||
const result = child();
|
||||
const nextNodes = (isArr(result) ? result : [result]).map((node) =>
|
||||
node?._isRuntime ? node.container : node instanceof Node ? node : createText(node)
|
||||
);
|
||||
currentNodes.forEach((node) => { cleanupNode(node); node.remove(); });
|
||||
nextNodes.forEach((node) => marker.parentNode?.insertBefore(node, marker));
|
||||
currentNodes = nextNodes;
|
||||
}));
|
||||
} else {
|
||||
element.appendChild(child instanceof Node ? child : createText(child));
|
||||
}
|
||||
};
|
||||
|
||||
appendChildNode(children);
|
||||
return element;
|
||||
};
|
||||
|
||||
const If = (condition, thenVal, otherwiseVal = null, transition = null) => {
|
||||
const marker = createText("");
|
||||
const container = Tag("div", { style: "display:contents" }, [marker]);
|
||||
let currentView = null, lastState = null;
|
||||
|
||||
Watch(() => {
|
||||
const state = !!(isFunc(condition) ? condition() : condition);
|
||||
if (state === lastState) return;
|
||||
lastState = state;
|
||||
|
||||
const dispose = () => { if (currentView) currentView.destroy(); currentView = null; };
|
||||
|
||||
if (currentView && !state && transition?.out) {
|
||||
transition.out(currentView.container, dispose);
|
||||
} else {
|
||||
dispose();
|
||||
}
|
||||
|
||||
const branch = state ? thenVal : otherwiseVal;
|
||||
if (branch) {
|
||||
currentView = Render(() => isFunc(branch) ? branch() : branch);
|
||||
container.insertBefore(currentView.container, marker);
|
||||
if (state && transition?.in) transition.in(currentView.container);
|
||||
}
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
const For = (source, renderFn, keyFn, tag = "div", props = { style: "display:contents" }) => {
|
||||
const marker = createText("");
|
||||
const container = Tag(tag, props, [marker]);
|
||||
let viewCache = new Map();
|
||||
|
||||
Watch(() => {
|
||||
const items = (isFunc(source) ? source() : source) || [];
|
||||
const nextCache = new Map();
|
||||
const order = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const key = keyFn ? keyFn(item, i) : i;
|
||||
let view = viewCache.get(key);
|
||||
|
||||
if (!view) {
|
||||
const result = renderFn(item, i);
|
||||
view = result instanceof Node
|
||||
? { container: result, destroy: () => { cleanupNode(result); result.remove(); } }
|
||||
: Render(() => result);
|
||||
}
|
||||
|
||||
viewCache.delete(key);
|
||||
nextCache.set(key, view);
|
||||
order.push(key);
|
||||
}
|
||||
|
||||
viewCache.forEach(v => v.destroy());
|
||||
|
||||
let anchor = marker;
|
||||
for (let i = order.length - 1; i >= 0; i--) {
|
||||
const view = nextCache.get(order[i]);
|
||||
if (view.container.nextSibling !== anchor) {
|
||||
container.insertBefore(view.container, anchor);
|
||||
}
|
||||
anchor = view.container;
|
||||
}
|
||||
viewCache = nextCache;
|
||||
});
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
const Router = (routes) => {
|
||||
const currentPath = $(window.location.hash.replace(/^#/, "") || "/");
|
||||
window.addEventListener("hashchange", () => currentPath(window.location.hash.replace(/^#/, "") || "/"));
|
||||
const outlet = Tag("div", { class: "router-transition" });
|
||||
let currentView = null;
|
||||
|
||||
Watch([currentPath], async () => {
|
||||
const path = currentPath();
|
||||
const route = routes.find(r => {
|
||||
const routeParts = r.path.split("/").filter(Boolean);
|
||||
const pathParts = path.split("/").filter(Boolean);
|
||||
return routeParts.length === pathParts.length && routeParts.every((part, i) => part.startsWith(":") || part === pathParts[i]);
|
||||
}) || routes.find(r => r.path === "*");
|
||||
|
||||
if (route) {
|
||||
let component = route.component;
|
||||
if (isFunc(component) && component.toString().includes('import')) {
|
||||
component = (await component()).default || (await component());
|
||||
}
|
||||
|
||||
const params = {};
|
||||
route.path.split("/").filter(Boolean).forEach((part, i) => {
|
||||
if (part.startsWith(":")) params[part.slice(1)] = path.split("/").filter(Boolean)[i];
|
||||
});
|
||||
|
||||
if (currentView) currentView.destroy();
|
||||
if (Router.params) Router.params(params);
|
||||
|
||||
currentView = Render(() => {
|
||||
try {
|
||||
return isFunc(component) ? component(params) : component;
|
||||
} catch (e) {
|
||||
return Tag("div", { class: "p-4 text-error" }, "Error loading view");
|
||||
}
|
||||
});
|
||||
outlet.appendChild(currentView.container);
|
||||
}
|
||||
});
|
||||
return outlet;
|
||||
};
|
||||
|
||||
Router.params = $({});
|
||||
Router.to = (path) => (window.location.hash = path.replace(/^#?\/?/, "#/"));
|
||||
Router.back = () => window.history.back();
|
||||
Router.path = () => window.location.hash.replace(/^#/, "") || "/";
|
||||
|
||||
const Mount = (component, target) => {
|
||||
const targetEl = typeof target === "string" ? doc.querySelector(target) : target;
|
||||
if (!targetEl) return;
|
||||
if (MOUNTED_NODES.has(targetEl)) MOUNTED_NODES.get(targetEl).destroy();
|
||||
const instance = Render(isFunc(component) ? component : () => component);
|
||||
targetEl.replaceChildren(instance.container);
|
||||
MOUNTED_NODES.set(targetEl, instance);
|
||||
return instance;
|
||||
};
|
||||
|
||||
|
||||
const SigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
assign(window, SigPro);
|
||||
const tags = `div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter`.split(" ");
|
||||
tags.forEach((tag) => {
|
||||
const helper = tag[0].toUpperCase() + tag.slice(1);
|
||||
if (!(helper in window)) window[helper] = (p, c) => Tag(tag, p, c);
|
||||
});
|
||||
window.SigPro = Object.freeze(SigPro);
|
||||
}
|
||||
|
||||
export { $, $$, Render, Watch, Tag, If, For, Router, Mount };
|
||||
export default SigPro;
|
||||
Reference in New Issue
Block a user