Next Preview Work Final
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// components/Accordion.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui, val } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Alert.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui, getIcon } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Autocomplete.js
|
||||
import { $, $html, $for } from "sigpro";
|
||||
import { $, $html, $for } from "../sigpro.js";
|
||||
import { val } from "../core/utils.js";
|
||||
import { tt } from "../core/i18n.js";
|
||||
import { Input } from "./Input.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Badge.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Button.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui, val, getIcon } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Checkbox.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Colorpicker.js
|
||||
import { $, $html, $if } from "sigpro";
|
||||
import { $, $html, $if } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Datepicker.js
|
||||
import { $, $html, $if } from "sigpro";
|
||||
import { $, $html, $if } from "../sigpro.js";
|
||||
import { val, ui, getIcon } from "../core/utils.js";
|
||||
import { Input } from "./Input.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Drawer.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Dropdown.js
|
||||
// import { $html, $for, $watch } from "sigpro";
|
||||
// import { $html, $for, $watch } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Fab.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui, getIcon } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Fieldset.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Fileinput.js
|
||||
import { $, $html, $if, $for } from "sigpro";
|
||||
import { $, $html, $if, $for } from "../sigpro.js";
|
||||
import { ui, getIcon } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Indicator.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Input.js
|
||||
import { $, $html } from "sigpro";
|
||||
import { $, $html, $watch } from "../sigpro.js";
|
||||
import { val, ui, getIcon } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
@@ -20,12 +20,14 @@ export const Input = (props) => {
|
||||
oninput,
|
||||
placeholder,
|
||||
disabled,
|
||||
size, // para poder pasar el tamaño también al botón
|
||||
size,
|
||||
validate,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const isPassword = type === "password";
|
||||
const visible = $(false);
|
||||
const errorMsg = $(null);
|
||||
|
||||
const iconMap = {
|
||||
text: "icon-[lucide--text]",
|
||||
@@ -39,7 +41,6 @@ export const Input = (props) => {
|
||||
};
|
||||
|
||||
const leftIcon = icon ? getIcon(icon) : (iconMap[type] ? getIcon(iconMap[type]) : null);
|
||||
|
||||
const getPasswordIcon = () => getIcon(visible() ? "icon-[lucide--eye-off]" : "icon-[lucide--eye]");
|
||||
|
||||
const paddingLeft = leftIcon ? "pl-10" : "";
|
||||
@@ -52,25 +53,43 @@ export const Input = (props) => {
|
||||
return 'btn-md';
|
||||
};
|
||||
|
||||
const handleInput = (e) => {
|
||||
const newValue = e.target.value;
|
||||
if (validate) {
|
||||
const result = validate(newValue);
|
||||
errorMsg(result || null);
|
||||
}
|
||||
oninput?.(e);
|
||||
};
|
||||
|
||||
const hasError = () => errorMsg() && errorMsg() !== '';
|
||||
|
||||
const inputClasses = () => {
|
||||
let classes = `input w-full ${paddingLeft} ${paddingRight}`;
|
||||
if (className) classes += ` ${className}`;
|
||||
if (hasError()) classes += ' input-error';
|
||||
return classes.trim();
|
||||
};
|
||||
|
||||
const inputElement = $html("input", {
|
||||
...rest,
|
||||
type: () => (isPassword ? (visible() ? "text" : "password") : type),
|
||||
placeholder: placeholder || " ",
|
||||
class: inputClasses,
|
||||
value: value,
|
||||
oninput: handleInput,
|
||||
disabled: () => val(disabled),
|
||||
"aria-invalid": () => hasError() ? "true" : "false",
|
||||
});
|
||||
|
||||
return $html(
|
||||
"div",
|
||||
{ class: "relative w-full" },
|
||||
() => [
|
||||
// Input
|
||||
$html("input", {
|
||||
...rest,
|
||||
type: () => (isPassword ? (visible() ? "text" : "password") : type),
|
||||
placeholder: placeholder || " ",
|
||||
class: ui('input w-full', `${paddingLeft} ${paddingRight} ${className || ''}`.trim()),
|
||||
value: value,
|
||||
oninput: (e) => oninput?.(e),
|
||||
disabled: () => val(disabled),
|
||||
}),
|
||||
|
||||
inputElement,
|
||||
leftIcon ? $html("div", {
|
||||
class: "absolute left-3 inset-y-0 flex items-center pointer-events-none text-base-content/60",
|
||||
}, leftIcon) : null,
|
||||
|
||||
isPassword ? $html("button", {
|
||||
type: "button",
|
||||
class: ui(
|
||||
@@ -83,6 +102,9 @@ export const Input = (props) => {
|
||||
visible(!visible());
|
||||
}
|
||||
}, () => getPasswordIcon()) : null,
|
||||
$html("div", {
|
||||
class: "text-error text-xs mt-1 px-3 absolute -bottom-5 left-0",
|
||||
}, () => hasError() ? errorMsg() : null),
|
||||
]
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Label.js
|
||||
import { $, $html } from "sigpro";
|
||||
import { $, $html } from "../sigpro.js";
|
||||
import { ui, val } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/List.js
|
||||
import { $html, $if, $for } from "sigpro";
|
||||
import { $html, $if, $for } from "../sigpro.js";
|
||||
import { ui, val } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,7 @@ import { ui, val } from "../core/utils.js";
|
||||
* - flex, items-center, gap-2
|
||||
*/
|
||||
export const List = (props) => {
|
||||
const { class: className, items, header, render, keyFn = (item, index) => index, ...rest } = props;
|
||||
const { class: className, items, header, render, keyFn = (item, index) => item.id ?? index, ...rest } = props;
|
||||
|
||||
const listItems = $for(
|
||||
items,
|
||||
@@ -20,12 +20,8 @@ export const List = (props) => {
|
||||
keyFn
|
||||
);
|
||||
|
||||
return $html(
|
||||
"ul",
|
||||
{
|
||||
...rest,
|
||||
class: ui('list bg-base-100 rounded-box shadow-md', className),
|
||||
},
|
||||
header ? [$if(header, () => $html("li", { class: "p-4 pb-2 text-xs opacity-60" }, [val(header)])), listItems] : listItems
|
||||
);
|
||||
return $html("ul", {
|
||||
...rest,
|
||||
class: ui('list bg-base-100 rounded-box shadow-md', className),
|
||||
}, header ? [$if(header, () => $html("li", { class: "p-4 pb-2 text-xs opacity-60" }, [val(header)])), listItems] : listItems);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Menu.js
|
||||
import { $html, $for } from "sigpro";
|
||||
import { $html, $for } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Modal.js
|
||||
import { $html, $watch } from "sigpro";
|
||||
import { $html, $watch } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
import { tt } from "../core/i18n.js";
|
||||
import { Button } from "./Button.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Navbar.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Radio.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Range.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Rating.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Select.js
|
||||
import { $html, $for } from "sigpro";
|
||||
import { $html, $for } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Stack.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Stat.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Swap.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui, val } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Table.js
|
||||
import { $html, $for, $if } from "sigpro";
|
||||
import { $html, $for, $if } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
import { tt } from "../core/i18n.js";
|
||||
|
||||
@@ -22,6 +22,8 @@ export const Table = (props) => {
|
||||
return ui('table', className, zebraClass, pinRowsClass);
|
||||
};
|
||||
|
||||
const getInternalKeyFn = keyFn || ((item, idx) => item.id || idx);
|
||||
|
||||
return $html("div", { class: "overflow-x-auto w-full bg-base-100 rounded-box border border-base-300" }, [
|
||||
$html("table", { ...rest, class: tableClass }, [
|
||||
$html("thead", {}, [
|
||||
@@ -31,17 +33,24 @@ export const Table = (props) => {
|
||||
]),
|
||||
$html("tbody", {}, [
|
||||
$for(items, (item, index) => {
|
||||
|
||||
const it = () => {
|
||||
const currentItems = val(items);
|
||||
const key = getInternalKeyFn(item, index);
|
||||
return currentItems.find((u, i) => getInternalKeyFn(u, i) === key) || item;
|
||||
};
|
||||
|
||||
return $html("tr", { class: "hover" },
|
||||
columns.map(col => {
|
||||
const cellContent = () => {
|
||||
if (col.render) return col.render(item, index);
|
||||
const value = item[col.key];
|
||||
return val(value);
|
||||
const latestItem = it();
|
||||
if (col.render) return col.render(latestItem, index);
|
||||
return val(latestItem[col.key]);
|
||||
};
|
||||
return $html("td", { class: col.class || "" }, [cellContent]);
|
||||
})
|
||||
);
|
||||
}, keyFn || ((item, idx) => item.id || idx)),
|
||||
}, getInternalKeyFn),
|
||||
|
||||
$if(() => val(items).length === 0, () =>
|
||||
$html("tr", {}, [
|
||||
@@ -50,14 +59,7 @@ export const Table = (props) => {
|
||||
])
|
||||
])
|
||||
)
|
||||
]),
|
||||
$if(() => columns.some(c => c.footer), () =>
|
||||
$html("tfoot", {}, [
|
||||
$html("tr", {},
|
||||
columns.map(col => $html("th", {}, col.footer || ""))
|
||||
)
|
||||
])
|
||||
)
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Tabs.js
|
||||
import { $, $html, $for } from "sigpro";
|
||||
import { $, $html, $for } from "../sigpro.js";
|
||||
import { val, ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
@@ -13,58 +13,63 @@ import { val, ui } from "../core/utils.js";
|
||||
export const Tabs = (props) => {
|
||||
const { items, class: className, ...rest } = props;
|
||||
const itemsSignal = typeof items === "function" ? items : () => items || [];
|
||||
const name = `tabs-${Math.random().toString(36).slice(2, 9)}`;
|
||||
const activeIndex = $(0);
|
||||
|
||||
// Encontrar el índice activo
|
||||
const getActiveIndex = () => {
|
||||
const arr = itemsSignal();
|
||||
const idx = arr.findIndex(it => val(it.active) === true);
|
||||
return idx === -1 ? 0 : idx;
|
||||
};
|
||||
$watch(() => {
|
||||
const idx = itemsSignal().findIndex(it => val(it.active) === true);
|
||||
if (idx !== -1 && idx !== activeIndex()) activeIndex(idx);
|
||||
});
|
||||
|
||||
const activeIndex = $(getActiveIndex);
|
||||
|
||||
const updateActiveIndex = () => {
|
||||
const newIndex = getActiveIndex();
|
||||
if (newIndex !== activeIndex()) activeIndex(newIndex);
|
||||
};
|
||||
|
||||
$watch(() => updateActiveIndex());
|
||||
|
||||
return $html("div", {
|
||||
...rest,
|
||||
class: ui('tabs', className || 'tabs-box')
|
||||
}, [
|
||||
$for(itemsSignal, (it, idx) => {
|
||||
const isChecked = () => activeIndex() === idx;
|
||||
const getLabelText = () => {
|
||||
const label = typeof it.label === "function" ? it.label() : it.label;
|
||||
return typeof label === "string" ? label : `Tab ${idx + 1}`;
|
||||
};
|
||||
|
||||
return [
|
||||
$html("input", {
|
||||
type: "radio",
|
||||
name: name,
|
||||
class: "tab",
|
||||
"aria-label": getLabelText(),
|
||||
checked: isChecked, // ← función reactiva, no string hardcodeado
|
||||
disabled: () => val(it.disabled),
|
||||
onchange: (e) => {
|
||||
if (e.target.checked && !val(it.disabled)) {
|
||||
return $html("div", { ...rest, class: "w-full" }, [
|
||||
// 1. Tab List: Aplanamos los botones para que sean hijos directos
|
||||
$html("div", {
|
||||
role: "tablist",
|
||||
class: ui('tabs', className || 'tabs-box')
|
||||
}, () => {
|
||||
const list = itemsSignal();
|
||||
return list.map((it, idx) => {
|
||||
const isSelected = () => activeIndex() === idx;
|
||||
|
||||
const tab = $html("button", {
|
||||
role: "tab",
|
||||
class: () => ui("tab", isSelected() ? "tab-active" : ""),
|
||||
onclick: (e) => {
|
||||
e.preventDefault();
|
||||
if (!val(it.disabled)) {
|
||||
if (it.onclick) it.onclick();
|
||||
if (typeof it.active === "function") it.active(true);
|
||||
activeIndex(idx);
|
||||
}
|
||||
}
|
||||
}),
|
||||
$html("div", {
|
||||
});
|
||||
|
||||
// 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
|
||||
$html("div", { class: "tab-panels" }, () => {
|
||||
return itemsSignal().map((it, idx) => {
|
||||
const isVisible = () => activeIndex() === idx;
|
||||
|
||||
return $html("div", {
|
||||
role: "tabpanel",
|
||||
class: "tab-content bg-base-100 border-base-300 p-6",
|
||||
style: () => isChecked() ? "display: block" : "display: none"
|
||||
style: () => isVisible() ? "display: block" : "display: none"
|
||||
}, [
|
||||
typeof it.content === "function" ? it.content() : it.content
|
||||
])
|
||||
];
|
||||
}, (it, idx) => idx)
|
||||
() => typeof it.content === "function" ? it.content() : it.content
|
||||
]);
|
||||
});
|
||||
})
|
||||
]);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Timeline.js
|
||||
import { $html, $for } from "sigpro";
|
||||
import { $html, $for } from "../sigpro.js";
|
||||
import { val, ui, getIcon } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
@@ -21,38 +21,39 @@ export const Timeline = (props) => {
|
||||
error: "icon-[lucide--alert-circle]",
|
||||
};
|
||||
|
||||
const itemsSource = typeof items === "function" ? items : () => items || [];
|
||||
|
||||
return $html(
|
||||
"ul",
|
||||
{
|
||||
...rest,
|
||||
class: () => ui(
|
||||
`timeline ${val(vertical) ? "timeline-vertical" : "timeline-horizontal"} ${val(compact) ? "timeline-compact" : ""}`, className),
|
||||
},
|
||||
[
|
||||
$for(
|
||||
itemsSource,
|
||||
(item, i) => {
|
||||
const isFirst = i === 0;
|
||||
const isLast = i === itemsSource().length - 1;
|
||||
const itemType = item.type || "success";
|
||||
const renderSlot = (content) => (typeof content === "function" ? content() : content);
|
||||
|
||||
return $html("li", { class: "flex-1" }, [
|
||||
!isFirst ? $html("hr", { class: () => item.completed ? "bg-primary" : "" }) : null,
|
||||
$html("div", { class: "timeline-start" }, () => renderSlot(item.title)),
|
||||
$html("div", { class: "timeline-middle" }, () => [
|
||||
item.icon
|
||||
? getIcon(item.icon)
|
||||
: getIcon(iconMap[itemType] || iconMap.success)
|
||||
]),
|
||||
$html("div", { class: "timeline-end timeline-box shadow-sm" }, () => renderSlot(item.detail)),
|
||||
!isLast ? $html("hr", { class: () => item.completed ? "bg-primary" : "" }) : null,
|
||||
]);
|
||||
},
|
||||
(item, i) => item.id || i,
|
||||
`timeline ${val(vertical) ? "timeline-vertical" : "timeline-horizontal"} ${val(compact) ? "timeline-compact" : ""}`,
|
||||
className
|
||||
),
|
||||
],
|
||||
},
|
||||
() => {
|
||||
const list = (typeof items === "function" ? items() : items) || [];
|
||||
|
||||
return list.map((item, i) => {
|
||||
const isFirst = i === 0;
|
||||
const isLast = i === list.length - 1;
|
||||
const itemType = item.type || "success";
|
||||
|
||||
const isCompleted = () => val(item.completed);
|
||||
// Nueva lógica: La línea de entrada se pinta si el ANTERIOR estaba completado
|
||||
const prevCompleted = () => i > 0 && val(list[i - 1].completed);
|
||||
|
||||
const renderSlot = (content) => (typeof content === "function" ? content() : content);
|
||||
|
||||
return $html("li", { class: "flex-1" }, [
|
||||
!isFirst ? $html("hr", { class: () => prevCompleted() ? "bg-primary" : "" }) : null,
|
||||
$html("div", { class: "timeline-start" }, [() => renderSlot(item.title)]),
|
||||
$html("div", { class: "timeline-middle" }, [
|
||||
() => item.icon ? getIcon(item.icon) : getIcon(iconMap[itemType] || iconMap.success)
|
||||
]),
|
||||
$html("div", { class: "timeline-end timeline-box shadow-sm" }, [() => renderSlot(item.detail)]),
|
||||
!isLast ? $html("hr", { class: () => isCompleted() ? "bg-primary" : "" }) : null,
|
||||
]);
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Toast.js
|
||||
import { $html, $mount } from "sigpro";
|
||||
import { $html, $mount } from "../sigpro.js";
|
||||
import { getIcon } from "../core/utils.js";
|
||||
import { Button } from "./Button.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// components/Tooltip.js
|
||||
import { $html } from "sigpro";
|
||||
import { $html } from "../sigpro.js";
|
||||
import { ui } from "../core/utils.js";
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user