Amunt!
This commit is contained in:
19
src/App.js
19
src/App.js
@@ -7,11 +7,9 @@ import { $ } from "./sigpro";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const $valor = $("");
|
const $valor = $("");
|
||||||
const $toggle = $(false);
|
const $toggle = $(false);
|
||||||
// const consoleToggle = $(()=>console.log($toggle()))
|
// const consoleToggle = $(()=>console.log($toggle()))
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
|
|
||||||
|
|
||||||
const miCheck = $(false); // Creamos la señal
|
const miCheck = $(false); // Creamos la señal
|
||||||
return Div({ class: "prose" }, [
|
return Div({ class: "prose" }, [
|
||||||
H1("Dashboard Principal"),
|
H1("Dashboard Principal"),
|
||||||
@@ -43,6 +41,8 @@ const Home = () => {
|
|||||||
|
|
||||||
const Profile = (params) => {
|
const Profile = (params) => {
|
||||||
const miFecha = $({ start: null, end: null });
|
const miFecha = $({ start: null, end: null });
|
||||||
|
const selectedFruit = $("Apple");
|
||||||
|
const fruits = ["Apple", "Banana", "Cherry", "Dragonfruit", "Elderberry"];
|
||||||
|
|
||||||
const textoInput = $(() => {
|
const textoInput = $(() => {
|
||||||
const f = miFecha;
|
const f = miFecha;
|
||||||
@@ -52,7 +52,16 @@ const Profile = (params) => {
|
|||||||
|
|
||||||
return Div({ class: "p-4 space-y-4" }, [
|
return Div({ class: "p-4 space-y-4" }, [
|
||||||
H2({ class: "text-xl font-bold" }, `Perfil: ${params.id}`),
|
H2({ class: "text-xl font-bold" }, `Perfil: ${params.id}`),
|
||||||
|
Autocomplete({
|
||||||
|
label: "Selecciona una fruta",
|
||||||
|
$value: selectedFruit,
|
||||||
|
options: fruits,
|
||||||
|
onSelect: (val) => console.log("Seleccionado:", val),
|
||||||
|
}),
|
||||||
|
Input({type: "number", label: "Number", icon: "🔍"}),
|
||||||
|
Input({type: "date", label: "Date"}),
|
||||||
|
Input({type: "password", label: "Password"}),
|
||||||
|
$.html("p", {}, () => `Has elegido: ${selectedFruit()}`),
|
||||||
Div({ class: "pt-4" }, [
|
Div({ class: "pt-4" }, [
|
||||||
Button(
|
Button(
|
||||||
{
|
{
|
||||||
@@ -72,7 +81,7 @@ export const App = () => {
|
|||||||
const isDark = $(false, "sigpro-theme");
|
const isDark = $(false, "sigpro-theme");
|
||||||
|
|
||||||
// Efecto para cambiar el tema en el HTML
|
// Efecto para cambiar el tema en el HTML
|
||||||
$(() => {
|
$$(() => {
|
||||||
document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light");
|
document.documentElement.setAttribute("data-theme", isDark() ? "dark" : "light");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
147
src/sigpro-ui.js
147
src/sigpro-ui.js
@@ -51,7 +51,7 @@ export const UI = ($, defaultLang = "es") => {
|
|||||||
const container = $.html("div", { style: "display:contents" }, [marker]);
|
const container = $.html("div", { style: "display:contents" }, [marker]);
|
||||||
const cache = new Map();
|
const cache = new Map();
|
||||||
|
|
||||||
$(() => {
|
$$(() => {
|
||||||
const items = val(source) || [];
|
const items = val(source) || [];
|
||||||
const newKeys = new Set();
|
const newKeys = new Set();
|
||||||
|
|
||||||
@@ -181,16 +181,20 @@ export const UI = ($, defaultLang = "es") => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** INPUT */
|
/** INPUT */
|
||||||
ui.Input = (props) => {
|
ui.Input = (props) => {
|
||||||
const { label, tip, $value, $error, isSearch, ...rest } = props;
|
const { label, tip, $value, $error, isSearch, icon, ...rest } = props;
|
||||||
|
|
||||||
|
// Estado local para alternar visibilidad si es password
|
||||||
|
const isPassword = props.type === "password";
|
||||||
|
const showPassword = $(false);
|
||||||
|
|
||||||
const inputEl = $.html("input", {
|
const inputEl = $.html("input", {
|
||||||
...rest,
|
...rest,
|
||||||
placeholder: props.placeholder || (isSearch ? tt("search") : " "),
|
// El tipo cambia dinámicamente si es password
|
||||||
class: joinClass("input input-bordered w-full", props.$class || props.class),
|
type: () => (isPassword ? (showPassword() ? "text" : "password") : (props.type || "text")),
|
||||||
// 1. Vinculamos el valor a la señal
|
placeholder: props.placeholder || (isSearch ? tt("search")() : " "),
|
||||||
|
class: joinClass("grow order-2", props.$class || props.class),
|
||||||
$value: $value || props.value,
|
$value: $value || props.value,
|
||||||
// 2. ACTUALIZAMOS la señal al escribir para que no se bloquee
|
|
||||||
oninput: (e) => {
|
oninput: (e) => {
|
||||||
if (typeof $value === "function") $value(e.target.value);
|
if (typeof $value === "function") $value(e.target.value);
|
||||||
if (typeof props.oninput === "function") props.oninput(e);
|
if (typeof props.oninput === "function") props.oninput(e);
|
||||||
@@ -198,15 +202,33 @@ export const UI = ($, defaultLang = "es") => {
|
|||||||
$disabled: () => val(props.$disabled) || val(props.disabled),
|
$disabled: () => val(props.$disabled) || val(props.disabled),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!label && !tip && !$error) return inputEl;
|
return $.html("label", {
|
||||||
|
class: "input input-bordered floating-label flex items-center gap-2 w-full relative"
|
||||||
|
}, [
|
||||||
|
// 1. Icono Izquierda (opcional)
|
||||||
|
icon ? $.html("div", { class: "order-1 flex items-center opacity-50 shrink-0" }, icon) : null,
|
||||||
|
|
||||||
return $.html("label", { class: "input floating-label fieldset-label flex flex-col gap-1" }, [
|
// 2. Texto del Label
|
||||||
label ? $.html("span", {}, label) : null,
|
label ? $.html("span", { class: "order-0" }, label) : null,
|
||||||
tip ? $.html("div", { class: "tooltip tooltip-right", "data-tip": tip }, $.html("span", { class: "badge badge-ghost badge-xs" }, "?")) : null,
|
|
||||||
|
// 3. Input
|
||||||
inputEl,
|
inputEl,
|
||||||
() => (val($error) ? $.html("span", { class: "text-error text-xs" }, val($error)) : null),
|
|
||||||
|
// 4. Botón Ojo (Solo si es type="password")
|
||||||
|
isPassword ? $.html("button", {
|
||||||
|
type: "button",
|
||||||
|
class: "order-3 btn btn-ghost btn-xs btn-circle opacity-50 hover:opacity-100",
|
||||||
|
onclick: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
showPassword(!showPassword());
|
||||||
|
}
|
||||||
|
}, () => (showPassword() ? "🙈" : "👁️")) : null,
|
||||||
|
|
||||||
|
// 5. Tooltip/Error
|
||||||
|
tip ? $.html("div", { class: "tooltip tooltip-right order-4", "data-tip": tip }, $.html("span", { class: "badge badge-ghost badge-xs" }, "?")) : null,
|
||||||
|
() => (val($error) ? $.html("span", { class: "text-error text-xs absolute -bottom-5 left-0" }, val($error)) : null),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** SELECT */
|
/** SELECT */
|
||||||
ui.Select = (props) => {
|
ui.Select = (props) => {
|
||||||
@@ -240,6 +262,105 @@ export const UI = ($, defaultLang = "es") => {
|
|||||||
return $.html("label", { class: "fieldset-label flex flex-col gap-1" }, [$.html("span", {}, label), selectEl]);
|
return $.html("label", { class: "fieldset-label flex flex-col gap-1" }, [$.html("span", {}, label), selectEl]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** AUTOCOMPLETE */
|
||||||
|
ui.Autocomplete = (props) => {
|
||||||
|
const { options = [], $value, onSelect, label, placeholder, ...rest } = props;
|
||||||
|
|
||||||
|
const query = $(val($value) || "");
|
||||||
|
const isOpen = $(false);
|
||||||
|
const highlightedIndex = $(-1);
|
||||||
|
|
||||||
|
const filtered = $(() => {
|
||||||
|
const q = query().toLowerCase();
|
||||||
|
const list = val(options) || [];
|
||||||
|
if (!q) return list;
|
||||||
|
return list.filter((opt) => {
|
||||||
|
const text = typeof opt === "string" ? opt : opt.label;
|
||||||
|
return text.toLowerCase().includes(q);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const select = (opt) => {
|
||||||
|
const v = typeof opt === "string" ? opt : opt.value;
|
||||||
|
const l = typeof opt === "string" ? opt : opt.label;
|
||||||
|
query(l);
|
||||||
|
if (typeof $value === "function") $value(v);
|
||||||
|
onSelect?.(opt);
|
||||||
|
isOpen(false);
|
||||||
|
highlightedIndex(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyDown = (e) => {
|
||||||
|
const list = filtered();
|
||||||
|
if (e.key === "ArrowDown") {
|
||||||
|
e.preventDefault();
|
||||||
|
isOpen(true);
|
||||||
|
highlightedIndex((prev) => Math.min(prev + 1, list.length - 1));
|
||||||
|
} else if (e.key === "ArrowUp") {
|
||||||
|
e.preventDefault();
|
||||||
|
highlightedIndex((prev) => Math.max(prev - 1, 0));
|
||||||
|
} else if (e.key === "Enter" && highlightedIndex() >= 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
select(list[highlightedIndex()]);
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
isOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return $.html(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "relative w-full",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
ui.Input({
|
||||||
|
label,
|
||||||
|
placeholder: placeholder || tt("search")(),
|
||||||
|
$value: query,
|
||||||
|
onfocus: () => isOpen(true),
|
||||||
|
onblur: () => setTimeout(() => isOpen(false), 200),
|
||||||
|
onkeydown: onKeyDown,
|
||||||
|
oninput: (e) => {
|
||||||
|
query(e.target.value);
|
||||||
|
isOpen(true);
|
||||||
|
highlightedIndex(-1);
|
||||||
|
},
|
||||||
|
...rest,
|
||||||
|
}),
|
||||||
|
|
||||||
|
$.html(
|
||||||
|
"ul",
|
||||||
|
{
|
||||||
|
// AÑADIDO: w-full y box-border para que no se pase ni un píxel
|
||||||
|
class: "absolute left-0 w-80 max-w-[95vw] menu bg-base-100 rounded-box z-[100] mt-1 p-2 shadow-2xl max-h-60 overflow-y-auto border border-base-300",
|
||||||
|
style: () => (isOpen() && filtered().length > 0 ? "display: block" : "display: none"),
|
||||||
|
},
|
||||||
|
[
|
||||||
|
ui.For(
|
||||||
|
filtered,
|
||||||
|
(opt, i) =>
|
||||||
|
$.html("li", { class: "w-full" }, [
|
||||||
|
// li al 100%
|
||||||
|
$.html(
|
||||||
|
"a",
|
||||||
|
{
|
||||||
|
// AÑADIDO: block w-full para que el azul cubra todo el ancho
|
||||||
|
class: () => joinClass("block w-full", highlightedIndex() === i && "active bg-primary text-primary-content"),
|
||||||
|
onclick: () => select(opt),
|
||||||
|
onmouseenter: () => highlightedIndex(i),
|
||||||
|
},
|
||||||
|
typeof opt === "string" ? opt : opt.label,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
(opt, i) => (typeof opt === "string" ? opt : opt.value) + i,
|
||||||
|
),
|
||||||
|
() => (filtered().length === 0 ? $.html("li", { class: "disabled p-2 text-center opacity-50" }, "No hay resultados") : null),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/** CHECKBOX */
|
/** CHECKBOX */
|
||||||
ui.CheckBox = (props) => {
|
ui.CheckBox = (props) => {
|
||||||
const { $value, tooltip, toggle, ...rest } = props;
|
const { $value, tooltip, toggle, ...rest } = props;
|
||||||
|
|||||||
139
src/sigpro.js
139
src/sigpro.js
@@ -51,21 +51,25 @@
|
|||||||
return subs;
|
return subs;
|
||||||
};
|
};
|
||||||
|
|
||||||
const $ = (initial, key) => {
|
const $ = (initial) => {
|
||||||
if (isObj(initial) && !key && typeof initial !== "function") {
|
if (initial && typeof initial === "object" && !(initial instanceof Node)) {
|
||||||
if (PROXIES.has(initial)) return PROXIES.get(initial);
|
if (PROXIES.has(initial)) return PROXIES.get(initial);
|
||||||
|
|
||||||
const proxy = new Proxy(initial, {
|
const proxy = new Proxy(initial, {
|
||||||
get(t, p, r) {
|
get(t, p, r) {
|
||||||
track(getPropSubs(t, p));
|
track(getPropSubs(t, p));
|
||||||
const val = Reflect.get(t, p, r);
|
const val = Reflect.get(t, p, r);
|
||||||
return isObj(val) ? $(val) : val;
|
return val && typeof val === "object" ? $(val) : val;
|
||||||
},
|
},
|
||||||
set(t, p, v, r) {
|
set(t, p, v, r) {
|
||||||
const old = Reflect.get(t, p, r);
|
const old = Reflect.get(t, p, r);
|
||||||
if (Object.is(old, v)) return true;
|
if (Object.is(old, v)) return true;
|
||||||
const res = Reflect.set(t, p, v, r);
|
const res = Reflect.set(t, p, v, r);
|
||||||
|
|
||||||
trigger(getPropSubs(t, p));
|
trigger(getPropSubs(t, p));
|
||||||
if (Array.isArray(t) && p !== "length") trigger(getPropSubs(t, "length"));
|
if (Array.isArray(t) && p !== "length") {
|
||||||
|
trigger(getPropSubs(t, "length"));
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
deleteProperty(t, p) {
|
deleteProperty(t, p) {
|
||||||
@@ -74,29 +78,26 @@
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
PROXIES.set(initial, proxy);
|
PROXIES.set(initial, proxy);
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof initial === "function") {
|
if (typeof initial === "function") {
|
||||||
const subs = new Set();
|
const subs = new Set();
|
||||||
let cached,
|
let cached;
|
||||||
dirty = true;
|
let dirty = true;
|
||||||
|
|
||||||
const effect = () => {
|
const effect = () => {
|
||||||
if (effect._deleted) return;
|
if (effect._deleted) return;
|
||||||
effect._cleanups.forEach((c) => c());
|
|
||||||
effect._cleanups.clear();
|
|
||||||
effect._deps.forEach((s) => s.delete(effect));
|
effect._deps.forEach((s) => s.delete(effect));
|
||||||
effect._deps.clear();
|
effect._deps.clear();
|
||||||
|
|
||||||
const prev = activeEffect;
|
const prev = activeEffect;
|
||||||
activeEffect = effect;
|
activeEffect = effect;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let maxD = 0;
|
|
||||||
effect._deps.forEach((s) => {
|
|
||||||
if (s._d > maxD) maxD = s._d;
|
|
||||||
});
|
|
||||||
effect.depth = maxD + 1;
|
|
||||||
subs._d = effect.depth;
|
|
||||||
const val = initial();
|
const val = initial();
|
||||||
if (!Object.is(cached, val) || dirty) {
|
if (!Object.is(cached, val) || dirty) {
|
||||||
cached = val;
|
cached = val;
|
||||||
@@ -107,24 +108,22 @@
|
|||||||
activeEffect = prev;
|
activeEffect = prev;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
effect._isComputed = true;
|
|
||||||
effect._deps = new Set();
|
effect._deps = new Set();
|
||||||
effect._cleanups = new Set();
|
effect._isComputed = true;
|
||||||
effect._subs = subs;
|
effect._subs = subs;
|
||||||
|
effect._deleted = false;
|
||||||
effect.markDirty = () => (dirty = true);
|
effect.markDirty = () => (dirty = true);
|
||||||
|
|
||||||
effect.stop = () => {
|
effect.stop = () => {
|
||||||
effect._deleted = true;
|
effect._deleted = true;
|
||||||
effectQueue.delete(effect);
|
|
||||||
effect._cleanups.forEach((c) => c());
|
|
||||||
effect._deps.forEach((s) => s.delete(effect));
|
effect._deps.forEach((s) => s.delete(effect));
|
||||||
|
effect._deps.clear();
|
||||||
subs.clear();
|
subs.clear();
|
||||||
};
|
};
|
||||||
if (currentOwner) {
|
|
||||||
currentOwner.cleanups.add(effect.stop);
|
if (currentOwner) currentOwner.cleanups.add(effect.stop);
|
||||||
effect._isComputed = false;
|
|
||||||
effect();
|
|
||||||
return () => {};
|
|
||||||
}
|
|
||||||
return () => {
|
return () => {
|
||||||
if (dirty) effect();
|
if (dirty) effect();
|
||||||
track(subs);
|
track(subs);
|
||||||
@@ -132,31 +131,65 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let value = initial;
|
||||||
const subs = new Set();
|
const subs = new Set();
|
||||||
subs._d = 0;
|
|
||||||
if (key) {
|
|
||||||
try {
|
|
||||||
const s = localStorage.getItem(key);
|
|
||||||
if (s !== null) initial = JSON.parse(s);
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const next = typeof args[0] === "function" ? args[0](initial) : args[0];
|
const next = typeof args[0] === "function" ? args[0](value) : args[0];
|
||||||
if (!Object.is(initial, next)) {
|
|
||||||
initial = next;
|
if (!Object.is(value, next)) {
|
||||||
if (key)
|
value = next;
|
||||||
try {
|
|
||||||
localStorage.setItem(key, JSON.stringify(initial));
|
|
||||||
} catch (e) {}
|
|
||||||
trigger(subs);
|
trigger(subs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
track(subs);
|
track(subs);
|
||||||
return initial;
|
return value;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $$ = (fn) => {
|
||||||
|
const effect = () => {
|
||||||
|
if (effect._deleted) return;
|
||||||
|
|
||||||
|
effect._deps.forEach((s) => s.delete(effect));
|
||||||
|
effect._deps.clear();
|
||||||
|
effect._cleanups.forEach((c) => c());
|
||||||
|
effect._cleanups.clear();
|
||||||
|
|
||||||
|
const prevEffect = activeEffect;
|
||||||
|
const prevOwner = currentOwner;
|
||||||
|
activeEffect = effect;
|
||||||
|
currentOwner = { cleanups: effect._cleanups };
|
||||||
|
|
||||||
|
effect.depth = prevEffect ? prevEffect.depth + 1 : 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
} finally {
|
||||||
|
activeEffect = prevEffect;
|
||||||
|
currentOwner = prevOwner;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
effect._deps = new Set();
|
||||||
|
effect._cleanups = new Set();
|
||||||
|
effect._deleted = false;
|
||||||
|
|
||||||
|
effect.stop = () => {
|
||||||
|
effect._deleted = true;
|
||||||
|
effectQueue.delete(effect);
|
||||||
|
effect._deps.forEach((s) => s.delete(effect));
|
||||||
|
effect._deps.clear();
|
||||||
|
effect._cleanups.forEach((c) => c());
|
||||||
|
effect._cleanups.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentOwner) currentOwner.cleanups.add(effect.stop);
|
||||||
|
effect();
|
||||||
|
return effect.stop;
|
||||||
|
};
|
||||||
|
|
||||||
const sweep = (node) => {
|
const sweep = (node) => {
|
||||||
if (node._cleanups) {
|
if (node._cleanups) {
|
||||||
node._cleanups.forEach((f) => f());
|
node._cleanups.forEach((f) => f());
|
||||||
@@ -205,7 +238,6 @@
|
|||||||
el._cleanups = new Set();
|
el._cleanups = new Set();
|
||||||
|
|
||||||
for (let [k, v] of Object.entries(props)) {
|
for (let [k, v] of Object.entries(props)) {
|
||||||
// 1. GESTIÓN DE EVENTOS (onchange, onclick...)
|
|
||||||
if (k.startsWith("on")) {
|
if (k.startsWith("on")) {
|
||||||
const name = k.slice(2).toLowerCase().split(".")[0];
|
const name = k.slice(2).toLowerCase().split(".")[0];
|
||||||
const mods = k.slice(2).toLowerCase().split(".").slice(1);
|
const mods = k.slice(2).toLowerCase().split(".").slice(1);
|
||||||
@@ -216,12 +248,10 @@
|
|||||||
};
|
};
|
||||||
el.addEventListener(name, handler, { once: mods.includes("once") });
|
el.addEventListener(name, handler, { once: mods.includes("once") });
|
||||||
el._cleanups.add(() => el.removeEventListener(name, handler));
|
el._cleanups.add(() => el.removeEventListener(name, handler));
|
||||||
}
|
} else if (k.startsWith("$")) {
|
||||||
|
|
||||||
else if (k.startsWith("$")) {
|
|
||||||
const attr = k.slice(1);
|
const attr = k.slice(1);
|
||||||
|
|
||||||
const stopAttr = $(() => {
|
const stopAttr = $$(() => {
|
||||||
const val = typeof v === "function" ? v() : v;
|
const val = typeof v === "function" ? v() : v;
|
||||||
if (el[attr] === val) return;
|
if (el[attr] === val) return;
|
||||||
|
|
||||||
@@ -237,11 +267,9 @@
|
|||||||
el.addEventListener(evt, h);
|
el.addEventListener(evt, h);
|
||||||
el._cleanups.add(() => el.removeEventListener(evt, h));
|
el._cleanups.add(() => el.removeEventListener(evt, h));
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
else {
|
|
||||||
if (typeof v === "function") {
|
if (typeof v === "function") {
|
||||||
const stopAttr = $(() => {
|
const stopAttr = $$(() => {
|
||||||
const val = v();
|
const val = v();
|
||||||
if (k === "class" || k === "className") el.className = val || "";
|
if (k === "class" || k === "className") el.className = val || "";
|
||||||
else if (typeof val === "boolean") el.toggleAttribute(k, val);
|
else if (typeof val === "boolean") el.toggleAttribute(k, val);
|
||||||
@@ -262,7 +290,7 @@
|
|||||||
const marker = document.createTextNode("");
|
const marker = document.createTextNode("");
|
||||||
el.appendChild(marker);
|
el.appendChild(marker);
|
||||||
let nodes = [];
|
let nodes = [];
|
||||||
const stopList = $(() => {
|
const stopList = $$(() => {
|
||||||
const res = c();
|
const res = c();
|
||||||
const next = (Array.isArray(res) ? res : [res]).map((i) =>
|
const next = (Array.isArray(res) ? res : [res]).map((i) =>
|
||||||
i?._isRuntime ? i.container : i instanceof Node ? i : document.createTextNode(i ?? ""),
|
i?._isRuntime ? i.container : i instanceof Node ? i : document.createTextNode(i ?? ""),
|
||||||
@@ -298,15 +326,12 @@
|
|||||||
const outlet = Div({ class: "router-outlet" });
|
const outlet = Div({ class: "router-outlet" });
|
||||||
let current = null;
|
let current = null;
|
||||||
|
|
||||||
// ESTE ES EL ÚNICO EFECTO: Solo observa sPath
|
$$(() => {
|
||||||
$(() => {
|
const path = sPath();
|
||||||
const path = sPath(); // Suscripción a la URL
|
|
||||||
|
|
||||||
// 1. Limpieza total de la página anterior
|
|
||||||
if (current) current.destroy();
|
if (current) current.destroy();
|
||||||
outlet.innerHTML = "";
|
outlet.innerHTML = "";
|
||||||
|
|
||||||
// 2. Buscamos la ruta
|
|
||||||
const parts = path.split("/").filter(Boolean);
|
const parts = path.split("/").filter(Boolean);
|
||||||
const route =
|
const route =
|
||||||
routes.find((r) => {
|
routes.find((r) => {
|
||||||
@@ -323,9 +348,6 @@
|
|||||||
if (p.startsWith(":")) params[p.slice(1)] = parts[i];
|
if (p.startsWith(":")) params[p.slice(1)] = parts[i];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. EJECUCIÓN AISLADA
|
|
||||||
// Usamos $.ignore para que el Router NO se suscriba a las señales de Home()
|
|
||||||
// Pero el $.view permite que Home() sea reactivo internamente.
|
|
||||||
current = $.ignore(() =>
|
current = $.ignore(() =>
|
||||||
$.view(() => {
|
$.view(() => {
|
||||||
const res = route.component(params);
|
const res = route.component(params);
|
||||||
@@ -360,5 +382,6 @@
|
|||||||
window[t.charAt(0).toUpperCase() + t.slice(1)] = (p, c) => $.html(t, p, c);
|
window[t.charAt(0).toUpperCase() + t.slice(1)] = (p, c) => $.html(t, p, c);
|
||||||
});
|
});
|
||||||
window.$ = $;
|
window.$ = $;
|
||||||
|
window.$$ = $$;
|
||||||
})();
|
})();
|
||||||
export const { $ } = window;
|
export const { $, $$ } = window;
|
||||||
|
|||||||
Reference in New Issue
Block a user