Before repair nav components
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 4s
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 4s
This commit is contained in:
735
components/All.js
Normal file
735
components/All.js
Normal file
@@ -0,0 +1,735 @@
|
|||||||
|
// All base components
|
||||||
|
import { h, each, watch, when, fx, mount, $ } from "sigpro";
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
export const get = val => typeof val === "function" ? val() : val;
|
||||||
|
export const getBy = (item, field = 'label') => (item && typeof item === 'object') ? item[field] : item;
|
||||||
|
export const cls = (...classes) => classes.filter(Boolean).join(' ').trim();
|
||||||
|
export const isFn = f => typeof f === "function";
|
||||||
|
export const filterBy = (items, query, field = 'label') => {
|
||||||
|
const searchTerm = String(query).toLowerCase();
|
||||||
|
const list = get(items);
|
||||||
|
return !searchTerm ? list : list.filter(item => {
|
||||||
|
const text = (item && typeof item === 'object') ? item[field] : item;
|
||||||
|
return String(text).toLowerCase().includes(searchTerm);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const listKey = (items, isOpen) => {
|
||||||
|
const cursor = $(-1);
|
||||||
|
watch(() => { if (!get(isOpen)) cursor(-1) });
|
||||||
|
const onKey = (e, select) => {
|
||||||
|
const list = get(items), i = cursor(), len = list.length;
|
||||||
|
if (!len) return;
|
||||||
|
const k = e.key;
|
||||||
|
k === 'ArrowDown' ? (e.preventDefault(), isOpen(true), cursor(Math.min(i + 1, len - 1))) :
|
||||||
|
k === 'ArrowUp' ? (e.preventDefault(), cursor(Math.max(i - 1, 0))) :
|
||||||
|
k === 'Enter' ? (i >= 0 && (e.preventDefault(), select(list[i]))) :
|
||||||
|
k === 'Escape' ? isOpen(false) : null;
|
||||||
|
};
|
||||||
|
return { cursor, onKey };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Alert = (p, c) => h("div", { ...p, class: cls("alert", p.class) }, c);
|
||||||
|
export const Badge = (p, c) => h("span", { ...p, class: cls("badge", p.class) }, c);
|
||||||
|
export const Button = (p, c) => h("button", { ...p, class: cls("btn", p.class) }, c);
|
||||||
|
export const Card = (p, c) => h("div", { ...p, class: cls("card", p.class) }, c);
|
||||||
|
export const CardTitle = (p, c) => h("div", { ...p, class: cls("card-title", p.class) }, c);
|
||||||
|
export const CardBody = (p, c) => h("div", { ...p, class: cls("card-body", p.class) }, c);
|
||||||
|
export const CardActions = (p, c) => h("div", { ...p, class: cls("card-actions", p.class) }, c);
|
||||||
|
export const Carousel = (p, c) => h("div", { ...p, class: cls("carousel", p.class) }, c);
|
||||||
|
export const CarouselItem = (p, c) => h("div", { ...p, class: cls("carousel-item", p.class) }, c);
|
||||||
|
export const Chat = (p, c) => h("div", { ...p, class: cls("chat", p.class) }, c);
|
||||||
|
export const ChatBubble = (p, c) => h("div", { ...p, class: cls("chat-bubble", p.class) }, c);
|
||||||
|
export const ChatFooter = (p, c) => h("div", { ...p, class: cls("chat-footer", p.class) }, c);
|
||||||
|
export const ChatHeader = (p, c) => h("div", { ...p, class: cls("chat-header", p.class) }, c);
|
||||||
|
export const ChatImage = (p, c) => h("div", { ...p, class: cls("chat-image avatar", p.class) }, h("div", { class: "w-10 rounded-full" }, typeof c === "string" ? h("img", { src: c, alt: "avatar" }) : c));
|
||||||
|
export const Checkbox = (p) => h("input", { ...p, type: "checkbox", class: cls("checkbox", p.class) });
|
||||||
|
export const Divider = (p) => h("div", { ...p, class: cls("divider", p.class) });
|
||||||
|
export const Fab = (p, c) => h("div", { ...p, class: cls("fab", p.class) }, c);
|
||||||
|
export const Fieldset = (p, c) => h("fieldset", { ...p, class: cls("fieldset", p.class) }, [p.legend && h("legend", { class: "fieldset-legend" }, p.legend), c]);
|
||||||
|
export const Icon = (p) => h("span", { class: p.startsWith("icon-") ? p : "" }, p.startsWith("icon-") ? null : p);
|
||||||
|
export const Indicator = (p, c) => h("div", { ...p, class: cls("indicator", p.class) }, [p.value && h("span", { class: cls("indicator-item badge", p.class) }, p.value), c]);
|
||||||
|
export const Kbd = (p, c) => h("kbd", { ...p, class: cls("kbd", p.class) }, c);
|
||||||
|
export const Loading = (p, c) => h("span", { ...p, class: cls("loading loading-spinner", p.class) }, c);
|
||||||
|
export const Navbar = (p, c) => h("div", { ...p, class: cls("navbar", p.class) }, c);
|
||||||
|
export const Progress = (p) => h("progress", { ...p, class: cls("progress", p.class) });
|
||||||
|
export const Radial = (p, c) => h("div", { ...p, class: cls("radial-progress", p.class), style: `--value:${p.value ?? 0};${p.style ?? ''}`, role: "progressbar", "aria-valuenow": p.value ?? 0 }, c ?? `${p.value ?? 0}%`)
|
||||||
|
export const Radio = (p) => h("input", { ...p, type: "radio", class: cls("radio", p.class) });
|
||||||
|
export const Range = (p) => h("input", { ...p, type: "range", class: cls("range", p.class) });
|
||||||
|
export const Skeleton = (p) => h("div", { ...p, class: cls("skeleton", p.class) });
|
||||||
|
export const SkeletonText = (p) => h("span", { ...p, class: cls("skeleton skeleton-text", p.class) });
|
||||||
|
export const Stack = (p, c) => h("div", { ...p, class: cls("stack", p.class) }, c);
|
||||||
|
export const Steps = (p, c) => h("ul", { ...p, class: cls("steps", p.class) }, c);
|
||||||
|
export const Step = (p, c) => h("li", { ...p, class: cls("step", p.class), "data-content": p.dataContent }, c);
|
||||||
|
export const Swap = (p) => h("label", { ...p, class: cls("swap", p.class) }, [
|
||||||
|
h("input", { type: "checkbox", checked: () => get(p.value), onchange: (e) => isFn(p.value) && p.value(e.target.checked) }),
|
||||||
|
h("div", { class: "swap-on" }, p.on),
|
||||||
|
h("div", { class: "swap-off" }, p.off)
|
||||||
|
]);
|
||||||
|
export const Textarea = (p) => h("textarea", { ...p, class: cls("textarea", p.class) });
|
||||||
|
export const TextRotate = (p) => {
|
||||||
|
const words = Array.isArray(p.words) ? p.words : (typeof p.words === 'string' ? p.words.split(',') : []);
|
||||||
|
return h("span", { ...p, class: cls("text-rotate", p.class) }, h("span", {}, words.map(w => h("span", {}, w))));
|
||||||
|
};
|
||||||
|
export const Timeline = (p, c) => h("ul", {
|
||||||
|
...p,
|
||||||
|
class: cls("timeline",
|
||||||
|
p.vertical !== false ? 'timeline-vertical' : 'timeline-horizontal',
|
||||||
|
p.compact ? 'timeline-compact' : '',
|
||||||
|
p.class
|
||||||
|
)
|
||||||
|
}, c);
|
||||||
|
export const Toggle = (p) => h("input", { ...p, type: "checkbox", class: cls("toggle", p.class) });
|
||||||
|
export const Tooltip = (p, c) => h("div", { ...p, class: cls("tooltip", p.class), "data-tip": p.tip }, c);
|
||||||
|
|
||||||
|
|
||||||
|
// Complex Components
|
||||||
|
|
||||||
|
// Accordion
|
||||||
|
export const Accordion = (p) => {
|
||||||
|
const name = p.name || `acc-${Math.random().toString(36).slice(2)}`
|
||||||
|
const base = cls('collapse', p.variant && `collapse-${p.variant}`, p.class)
|
||||||
|
const itemFn = (it) => {
|
||||||
|
const t = getBy(it, 'title')
|
||||||
|
const c = it.content
|
||||||
|
return p.type === 'details'
|
||||||
|
? h('details', { class: base, name, open: it.open || undefined },
|
||||||
|
h('summary', { class: 'collapse-title font-semibold' }, t),
|
||||||
|
c ? h('div', { class: 'collapse-content text-sm' }, c) : null)
|
||||||
|
: h('div', { class: base },
|
||||||
|
h('input', { type: 'radio', name, checked: it.open || undefined }),
|
||||||
|
h('div', { class: 'collapse-title font-semibold' }, t),
|
||||||
|
c ? h('div', { class: 'collapse-content text-sm' }, c) : null)
|
||||||
|
}
|
||||||
|
return isFn(p.items) ? each(p.items, itemFn, (it, i) => it?.id ?? i) : (p.items || []).map(itemFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table
|
||||||
|
export const Table = (p) => {
|
||||||
|
if (p.children !== undefined) return h('table', { class: cls('table', p.class), ...p }, p.children)
|
||||||
|
const { items, columns = [], header = true, keyFn, ...rest } = p
|
||||||
|
const hd = header !== false && columns.some(c => c.label) ? h('thead', {}, h('tr', {}, columns.map(c => h('th', { class: c.class }, c.label)))) : null
|
||||||
|
const bd = h('tbody', {}, each(
|
||||||
|
() => get(items) || [],
|
||||||
|
(it, idx) => h('tr', {}, columns.map(c => {
|
||||||
|
const v = c.render ? c.render(it, idx) : it[c.key]
|
||||||
|
return h('td', { class: c.class }, v)
|
||||||
|
})),
|
||||||
|
keyFn || ((it, idx) => it?.id ?? idx)
|
||||||
|
))
|
||||||
|
return h('table', { class: cls('table', rest.class), ...rest }, [hd, bd])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tabs
|
||||||
|
export const Tabs = (p) => {
|
||||||
|
if (p.children !== undefined) return h('div', { class: cls('tabs', p.class), ...p }, p.children)
|
||||||
|
const { items, activeIndex, onClose, ...rest } = p
|
||||||
|
const closeHandler = onClose || (isFn(items) ? (idx) => {
|
||||||
|
const arr = get(items)
|
||||||
|
const newArr = arr.filter((_, i) => i !== idx)
|
||||||
|
items(newArr)
|
||||||
|
if (activeIndex() >= newArr.length) activeIndex(Math.max(0, newArr.length - 1))
|
||||||
|
} : null)
|
||||||
|
|
||||||
|
return h('div', { class: cls('tabs', p.class), ...rest },
|
||||||
|
each(
|
||||||
|
() => get(items) || [],
|
||||||
|
(it, idx) => {
|
||||||
|
const act = () => activeIndex() === idx
|
||||||
|
return [
|
||||||
|
h('a', {
|
||||||
|
role: 'tab',
|
||||||
|
class: () => `tab ${act() ? 'tab-active' : ''} ${it.class || ''}`,
|
||||||
|
onclick: (e) => { e.preventDefault(); activeIndex(idx); it.onclick?.(e) }
|
||||||
|
}, getBy(it), it.closable ? h('span', {
|
||||||
|
class: 'icon-[lucide--x] w-3.5 h-3.5 ml-2 cursor-pointer hover:opacity-70',
|
||||||
|
onclick: (e) => { e.stopPropagation(); closeHandler?.(idx) }
|
||||||
|
}) : null),
|
||||||
|
h('div', {
|
||||||
|
class: `tab-content ${it.contentClass || ''}`,
|
||||||
|
style: () => `display: ${act() ? 'block' : 'none'};`
|
||||||
|
}, isFn(it.content) ? it.content() : it.content)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
(it, idx) => it.id ?? idx
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rating
|
||||||
|
export const Rating = (p) => {
|
||||||
|
const name = `rating-${Math.random().toString(36).slice(2, 7)}`
|
||||||
|
const stars = p.children ?? Array.from({ length: p.count || 5 }, (_, i) => {
|
||||||
|
const v = i + 1
|
||||||
|
return h('input', {
|
||||||
|
type: 'radio',
|
||||||
|
name,
|
||||||
|
class: cls('mask', p.mask || 'mask-star'),
|
||||||
|
checked: () => get(p.value) === v,
|
||||||
|
onchange: () => isFn(p.value) ? p.value(v) : p.onchange?.(v)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return h('div', { class: cls('rating', p.class), ...p }, stars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
export const Menu = (p) => {
|
||||||
|
if (p.children !== undefined) return h('ul', { class: cls('menu', p.class), ...p }, p.children)
|
||||||
|
const { items, keyFn = (it, idx) => it?.id ?? idx, ...rest } = p
|
||||||
|
const render = (item) => item.children
|
||||||
|
? h('li', {}, h('details', {}, h('summary', {}, getBy(item)), h('ul', {}, Menu({ items: item.children }))))
|
||||||
|
: h('li', {}, h('a', {
|
||||||
|
href: item.href,
|
||||||
|
onclick: item.onclick ? (e) => { if (!item.href) e.preventDefault(); item.onclick(e) } : null
|
||||||
|
}, getBy(item)))
|
||||||
|
return h('ul', { class: cls('menu', rest.class), ...rest },
|
||||||
|
each(() => get(items) || [], render, keyFn)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Drawer
|
||||||
|
export const Drawer = (p, c) => {
|
||||||
|
const id = p.id || `drawer-${Math.random().toString(36).slice(2, 9)}`
|
||||||
|
return h('div', { class: cls('drawer', p.class) }, [
|
||||||
|
h('input', {
|
||||||
|
id,
|
||||||
|
type: 'checkbox',
|
||||||
|
class: 'drawer-toggle',
|
||||||
|
checked: () => get(p.open),
|
||||||
|
onchange: (e) => isFn(p.open) && p.open(e.target.checked)
|
||||||
|
}),
|
||||||
|
h('div', { class: 'drawer-content' }, c),
|
||||||
|
h('div', { class: 'drawer-side' }, [
|
||||||
|
h('label', {
|
||||||
|
for: id,
|
||||||
|
class: 'drawer-overlay',
|
||||||
|
onclick: () => isFn(p.open) && p.open(false)
|
||||||
|
}),
|
||||||
|
h('div', { class: 'min-h-full bg-base-200 w-80 p-4' }, () => get(p.side))
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
//Dropdown
|
||||||
|
export const Dropdown = (p, c) => {
|
||||||
|
const { trigger, items, ...rest } = p
|
||||||
|
const content = c || (items ? h('ul', {
|
||||||
|
class: 'menu dropdown-content bg-base-100 rounded-box z-[1] w-52 p-2 shadow'
|
||||||
|
}, each(
|
||||||
|
() => get(items) || [],
|
||||||
|
(item) => h('li', {},
|
||||||
|
h('a', {
|
||||||
|
onclick: (e) => {
|
||||||
|
item.onclick?.(e)
|
||||||
|
e.currentTarget.closest('details').open = false
|
||||||
|
}
|
||||||
|
}, getBy(item))
|
||||||
|
),
|
||||||
|
(it, idx) => it.id ?? idx
|
||||||
|
)) : null)
|
||||||
|
|
||||||
|
return h('details', { class: cls('dropdown', rest.class), ...rest }, [
|
||||||
|
h('summary', { class: 'btn m-1' }, trigger || 'Dropdown'),
|
||||||
|
content
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
//Select
|
||||||
|
export const Select = (p, c) => {
|
||||||
|
if (c !== undefined) return h('select', { class: cls('select', p.class), ...p }, c)
|
||||||
|
|
||||||
|
const { label, float, placeholder, placeholderDisabled = true, value, left, right, hint, items, keyFn, ...rest } = p
|
||||||
|
|
||||||
|
const opts = () => {
|
||||||
|
const raw = get(items) || []
|
||||||
|
const ph = placeholder ? [{ disabled: placeholderDisabled, label: placeholder, value: '' }] : []
|
||||||
|
return [...ph, ...raw]
|
||||||
|
}
|
||||||
|
|
||||||
|
return h('label', { class: float ? 'floating-label' : '' }, [
|
||||||
|
float && h('span', {}, label),
|
||||||
|
h('label', { class: cls('select', rest.class) }, [
|
||||||
|
label && !float && h('span', { class: 'label' }, label),
|
||||||
|
left ?? null,
|
||||||
|
h('select', {
|
||||||
|
value: () => get(value),
|
||||||
|
onchange: (e) => isFn(value) ? value(e.target.value) : rest.onchange?.(e)
|
||||||
|
},
|
||||||
|
each(opts, (item) => {
|
||||||
|
const val = getBy(item, item.value !== undefined ? 'value' : undefined)
|
||||||
|
const lab = getBy(item, 'label')
|
||||||
|
return h('option', { value: val, disabled: item.disabled || undefined }, lab)
|
||||||
|
}, (it, i) => it?.id ?? (typeof it === 'string' ? it : it.value) ?? i)
|
||||||
|
),
|
||||||
|
right ?? null
|
||||||
|
]),
|
||||||
|
hint && h('div', { class: 'validator-hint' }, hint)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autocomplete
|
||||||
|
export const Autocomplete = ({ items, value, onselect, placeholder = 'Buscar...', ...props }) => {
|
||||||
|
const query = $(get(value) || '');
|
||||||
|
const isOpen = $(false);
|
||||||
|
const filtered = $(() => filterBy(items, query()));
|
||||||
|
const { cursor, onKey } = listKey(filtered, isOpen);
|
||||||
|
|
||||||
|
const pick = (item) => {
|
||||||
|
const display = getBy(item);
|
||||||
|
const actual = typeof item === 'string' ? item : item.value;
|
||||||
|
query(display);
|
||||||
|
if (isFn(value)) value(actual);
|
||||||
|
onselect?.(item);
|
||||||
|
isOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return h('div', { class: 'relative w-full' }, [
|
||||||
|
Input({
|
||||||
|
...props,
|
||||||
|
type: 'text',
|
||||||
|
placeholder,
|
||||||
|
value: query,
|
||||||
|
left: h('span', { class: 'icon-[lucide--search]' }),
|
||||||
|
oninput: (e) => {
|
||||||
|
query(e.target.value);
|
||||||
|
if (isFn(value)) value(e.target.value);
|
||||||
|
isOpen(true);
|
||||||
|
},
|
||||||
|
onfocus: () => isOpen(true),
|
||||||
|
onblur: () => setTimeout(() => isOpen(false), 150),
|
||||||
|
onkeydown: (e) => onKey(e, pick)
|
||||||
|
}),
|
||||||
|
|
||||||
|
when(isOpen, () =>
|
||||||
|
fx({ duration: 200, slide: true },
|
||||||
|
h('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 flex-col flex-nowrap'
|
||||||
|
}, [
|
||||||
|
each(filtered, (item, idx) =>
|
||||||
|
h('li', {}, [
|
||||||
|
h('a', {
|
||||||
|
class: () => cursor() === idx ? 'active bg-primary text-primary-content' : '',
|
||||||
|
onmousedown: (e) => e.preventDefault(), // evita que el blur cierre antes del click
|
||||||
|
onclick: () => pick(item),
|
||||||
|
onmouseenter: () => cursor(idx)
|
||||||
|
}, getBy(item))
|
||||||
|
]),
|
||||||
|
(item, idx) => getBy(item) + idx
|
||||||
|
),
|
||||||
|
() => filtered().length === 0
|
||||||
|
? h('li', { class: 'p-4 opacity-50 text-center' }, 'Sin resultados')
|
||||||
|
: null
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Input
|
||||||
|
export const Input = (p) => {
|
||||||
|
const { label, icon, float, placeholder, value, left, right, rule, hint, content, ...rest } = p;
|
||||||
|
|
||||||
|
const showPassword = $(false);
|
||||||
|
const isFocused = $(false);
|
||||||
|
const isPassword = p.type === 'password';
|
||||||
|
const pattern = rule ?? null;
|
||||||
|
|
||||||
|
const inputType = () => isPassword
|
||||||
|
? (get(showPassword) ? 'text' : 'password')
|
||||||
|
: (p.type || 'text');
|
||||||
|
|
||||||
|
return h("div", {
|
||||||
|
class: "input-container",
|
||||||
|
onfocusin: () => isFocused(true),
|
||||||
|
onfocusout: (e) => {
|
||||||
|
if (!e.currentTarget.contains(e.relatedTarget)) { isFocused(false); }
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
h('label', { class: float ? 'floating-label' : '' }, [
|
||||||
|
float ? h("span", {}, label) : null,
|
||||||
|
h("label", { pattern: pattern, class: () => cls('input validator', p.class) },
|
||||||
|
[
|
||||||
|
label && !float ? h('span', { class: 'label' }, label) : null,
|
||||||
|
left ?? null,
|
||||||
|
h('input', { ...rest, type: inputType, class: 'grow', pattern: pattern, placeholder: placeholder || label || ' ', value: value }), right ?? null,
|
||||||
|
isPassword ? h('label', { class: 'swap swap-rotate ml-2' }, [
|
||||||
|
h('input', { type: 'checkbox', onchange: (e) => showPassword(e.target.checked) }),
|
||||||
|
h('span', { class: 'swap-on icon-[lucide--eye]' }),
|
||||||
|
h('span', { class: 'swap-off icon-[lucide--eye-off]' })
|
||||||
|
]) : null
|
||||||
|
]),
|
||||||
|
hint ? h('div', { class: "validator-hint" }, hint) : null,
|
||||||
|
|
||||||
|
when(isFocused, () => fx({ duration: 300, slide: true },
|
||||||
|
h('div', { class: 'input-content', onmousedown: e => e.preventDefault() },
|
||||||
|
[
|
||||||
|
isFn(content) ? content(isFocused) : content
|
||||||
|
])
|
||||||
|
))
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Colorpicker
|
||||||
|
export const Colorpicker = (p) => {
|
||||||
|
const isOpen = $(false)
|
||||||
|
const current = () => get(p.value) || '#000000'
|
||||||
|
|
||||||
|
const palette = [
|
||||||
|
'#000', '#1A1A1A', '#333', '#4D4D4D', '#666', '#808080', '#B3B3B3', '#FFF',
|
||||||
|
'#450a0a', '#7f1d1d', '#991b1b', '#b91c1c', '#dc2626', '#ef4444', '#f87171', '#fca5a5',
|
||||||
|
'#431407', '#7c2d12', '#9a3412', '#c2410c', '#ea580c', '#f97316', '#fb923c', '#ffedd5',
|
||||||
|
'#713f12', '#a16207', '#ca8a04', '#eab308', '#facc15', '#fde047', '#fef08a', '#fff9c4',
|
||||||
|
'#064e3b', '#065f46', '#059669', '#10b981', '#34d399', '#4ade80', '#84cc16', '#d9f99d',
|
||||||
|
'#082f49', '#075985', '#0284c7', '#0ea5e9', '#38bdf8', '#7dd3fc', '#22d3ee', '#cffafe',
|
||||||
|
'#1e1b4b', '#312e81', '#4338ca', '#4f46e5', '#6366f1', '#818cf8', '#a5b4fc', '#e0e7ff',
|
||||||
|
'#2e1065', '#4c1d95', '#6d28d9', '#7c3aed', '#8b5cf6', '#a855f7', '#d946ef', '#fae8ff'
|
||||||
|
]
|
||||||
|
|
||||||
|
const pick = (c) => { isFn(p.value) ? p.value(c) : p.onchange?.(c); isOpen(false) }
|
||||||
|
|
||||||
|
return h('div', { class: cls('relative w-fit', p.class) }, [
|
||||||
|
h('button', {
|
||||||
|
type: 'button',
|
||||||
|
class: 'btn px-3 bg-base-100 border-base-300 hover:border-primary/50 flex items-center gap-2 shadow-sm font-normal normal-case',
|
||||||
|
onclick: (e) => { e.stopPropagation(); isOpen(!isOpen()) }
|
||||||
|
}, [
|
||||||
|
h('div', { class: 'size-5 rounded-sm shadow-inner border border-black/10 shrink-0', style: () => `background-color: ${current()}` }),
|
||||||
|
p.label && h('span', { class: 'opacity-80' }, p.label)
|
||||||
|
]),
|
||||||
|
|
||||||
|
when(isOpen, () => [
|
||||||
|
h('div', {
|
||||||
|
class: 'fixed inset-0 z-[100]',
|
||||||
|
onclick: () => isOpen(false)
|
||||||
|
}),
|
||||||
|
h('div', { class: 'absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none' },
|
||||||
|
h('div', { class: 'grid grid-cols-8 gap-1' },
|
||||||
|
palette.map(c =>
|
||||||
|
h('button', {
|
||||||
|
type: 'button',
|
||||||
|
style: `background-color: ${c}`,
|
||||||
|
class: () => {
|
||||||
|
const act = current().toLowerCase() === c.toLowerCase()
|
||||||
|
return `size-6 rounded-sm cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-95 outline-none border border-black/5 p-0 min-h-0 ${act ? 'ring-2 ring-offset-1 ring-primary z-10 scale-110' : ''}`
|
||||||
|
},
|
||||||
|
onclick: () => pick(c)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calendar
|
||||||
|
export const Calendar = (p) => {
|
||||||
|
const internalDate = $(new Date())
|
||||||
|
const hoverDate = $(null)
|
||||||
|
const startHour = $(0)
|
||||||
|
const endHour = $(0)
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||||
|
|
||||||
|
const fmt = d => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
||||||
|
const rangeMode = () => get(p.range) === true
|
||||||
|
const current = () => get(p.value)
|
||||||
|
|
||||||
|
const selectDate = (date) => {
|
||||||
|
const s = fmt(date)
|
||||||
|
const v = current()
|
||||||
|
if (rangeMode()) {
|
||||||
|
if (!v?.start || (v.start && v.end)) {
|
||||||
|
p.onChange?.({ start: s, end: null, ...(p.hour && { startHour: startHour() }) })
|
||||||
|
} else {
|
||||||
|
const start = v.start
|
||||||
|
const nv = s < start ? { start: s, end: start } : { start, end: s }
|
||||||
|
if (p.hour) { nv.startHour = v.startHour ?? startHour(); nv.endHour = endHour() }
|
||||||
|
p.onChange?.(nv)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.onChange?.(p.hour ? `${s}T${String(startHour()).padStart(2, '0')}:00:00` : s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const move = (m) => { const d = internalDate(); internalDate(new Date(d.getFullYear(), d.getMonth() + m, 1)) }
|
||||||
|
const moveYear = (y) => { const d = internalDate(); internalDate(new Date(d.getFullYear() + y, d.getMonth(), 1)) }
|
||||||
|
|
||||||
|
const HourSlider = ({ value: hVal, onChange: onH }) => h('div', { class: 'flex-1' }, [
|
||||||
|
h('div', { class: 'flex gap-2 items-center' }, [
|
||||||
|
h('input', { type: 'range', min: 0, max: 23, value: hVal, class: 'range range-xs flex-1', oninput: e => onH(+e.target.value) }),
|
||||||
|
h('span', { class: 'text-sm font-mono min-w-[48px] text-center' }, () => String(get(hVal)).padStart(2, '0') + ':00')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
return h('div', { class: cls('p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box w-80 select-none', p.class) }, [
|
||||||
|
h('div', { class: 'flex justify-between items-center mb-4 gap-1' }, [
|
||||||
|
h('div', { class: 'flex gap-0.5' }, [
|
||||||
|
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => moveYear(-1) }, h('span', { class: 'icon-[lucide--chevrons-left]' })),
|
||||||
|
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => move(-1) }, h('span', { class: 'icon-[lucide--chevron-left]' }))
|
||||||
|
]),
|
||||||
|
h('span', { class: 'font-bold uppercase flex-1 text-center' }, () => internalDate().toLocaleString('es-ES', { month: 'short', year: 'numeric' })),
|
||||||
|
h('div', { class: 'flex gap-0.5' }, [
|
||||||
|
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => move(1) }, h('span', { class: 'icon-[lucide--chevron-right]' })),
|
||||||
|
h('button', { type: 'button', class: 'btn btn-ghost btn-xs px-1', onclick: () => moveYear(1) }, h('span', { class: 'icon-[lucide--chevrons-right]' }))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('div', { class: 'grid grid-cols-7 gap-1', onmouseleave: () => hoverDate(null) }, [
|
||||||
|
...['L', 'M', 'X', 'J', 'V', 'S', 'D'].map(d => h('div', { class: 'text-[10px] opacity-40 font-bold text-center' }, d)),
|
||||||
|
() => {
|
||||||
|
const d = internalDate(), y = d.getFullYear(), m = d.getMonth()
|
||||||
|
const firstDay = new Date(y, m, 1).getDay()
|
||||||
|
const offset = firstDay === 0 ? 6 : firstDay - 1
|
||||||
|
const dim = new Date(y, m + 1, 0).getDate()
|
||||||
|
const cells = []
|
||||||
|
for (let i = 0; i < offset; i++) cells.push(h('div'))
|
||||||
|
for (let i = 1; i <= dim; i++) {
|
||||||
|
const date = new Date(y, m, i), ds = fmt(date)
|
||||||
|
cells.push(h('button', {
|
||||||
|
type: 'button',
|
||||||
|
class: () => {
|
||||||
|
const v = current(), h = hoverDate()
|
||||||
|
const isStart = typeof v === 'string' ? v.split('T')[0] === ds : v?.start === ds
|
||||||
|
const isEnd = v?.end === ds
|
||||||
|
let inRange = false
|
||||||
|
if (rangeMode() && v?.start) {
|
||||||
|
const start = v.start
|
||||||
|
if (!v.end && h) inRange = (ds > start && ds <= h) || (ds < start && ds >= h)
|
||||||
|
else if (v.end) inRange = ds > start && ds < v.end
|
||||||
|
}
|
||||||
|
const base = 'btn btn-xs p-0 aspect-square min-h-0 h-auto font-normal relative'
|
||||||
|
const st = isStart || isEnd ? 'btn-primary z-10' : inRange ? 'bg-primary/20 border-none rounded-none' : 'btn-ghost'
|
||||||
|
const today = ds === todayStr ? 'ring-1 ring-primary ring-inset font-black text-primary' : ''
|
||||||
|
return cls(base, st, today)
|
||||||
|
},
|
||||||
|
onmouseenter: () => rangeMode() && hoverDate(ds),
|
||||||
|
onclick: () => selectDate(date)
|
||||||
|
}, i.toString()))
|
||||||
|
}
|
||||||
|
return cells
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
|
||||||
|
p.hour ? h('div', { class: 'mt-3 pt-2 border-t border-base-300' },
|
||||||
|
rangeMode()
|
||||||
|
? h('div', { class: 'flex gap-4' }, [HourSlider({ value: startHour, onChange: h => startHour(h) }), HourSlider({ value: endHour, onChange: h => endHour(h) })])
|
||||||
|
: HourSlider({ value: startHour, onChange: h => startHour(h) })
|
||||||
|
) : null
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
//Datepicker.js
|
||||||
|
export const Datepicker = (p) => {
|
||||||
|
const isOpen = $(false)
|
||||||
|
const displayValue = $("")
|
||||||
|
const rangeMode = () => get(p.range) === true
|
||||||
|
|
||||||
|
watch(() => {
|
||||||
|
const v = get(p.value)
|
||||||
|
if (!v) return displayValue("")
|
||||||
|
let text = ""
|
||||||
|
if (typeof v === "string") {
|
||||||
|
text = p.hour && v.includes("T") ? v.replace("T", " ") : v
|
||||||
|
} else if (v.start && v.end) {
|
||||||
|
const startStr = p.hour && v.startHour != null ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start
|
||||||
|
const endStr = p.hour && v.endHour != null ? `${v.end} ${String(v.endHour).padStart(2, "0")}:00` : v.end
|
||||||
|
text = `${startStr} - ${endStr}`
|
||||||
|
} else if (v.start) {
|
||||||
|
const startStr = p.hour && v.startHour != null ? `${v.start} ${String(v.startHour).padStart(2, "0")}:00` : v.start
|
||||||
|
text = `${startStr}...`
|
||||||
|
}
|
||||||
|
displayValue(text)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleChange = (val) => {
|
||||||
|
if (isFn(p.value)) p.value(val)
|
||||||
|
else p.onChange?.(val)
|
||||||
|
if (!rangeMode() || val?.end != null) isOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h('div', { class: cls('relative w-full', p.class) }, [
|
||||||
|
h('label', { class: 'input input-bordered w-full', onclick: (e) => { e.stopPropagation(); isOpen(!isOpen()) } }, [
|
||||||
|
h('span', { class: 'icon-[lucide--calendar]' }),
|
||||||
|
h('input', {
|
||||||
|
...p,
|
||||||
|
type: 'text',
|
||||||
|
class: 'grow',
|
||||||
|
value: displayValue,
|
||||||
|
readonly: true,
|
||||||
|
placeholder: p.placeholder || (rangeMode() ? 'Seleccionar rango...' : 'Seleccionar fecha...')
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
|
||||||
|
when(isOpen, () => [
|
||||||
|
h('div', { class: 'fixed inset-0 z-[90]', onclick: () => isOpen(false) }),
|
||||||
|
h('div', { class: 'absolute left-0 mt-2 z-[100]', onclick: (e) => e.stopPropagation() },
|
||||||
|
Calendar({ value: p.value, range: rangeMode(), hour: p.hour, onChange: handleChange })
|
||||||
|
)
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fileinput
|
||||||
|
export const Fileinput = (p) => {
|
||||||
|
const files = $([])
|
||||||
|
const drag = $(false)
|
||||||
|
const error = $(null)
|
||||||
|
const maxBytes = (p.max || 2) * 1024 * 1024
|
||||||
|
|
||||||
|
const process = (fileList) => {
|
||||||
|
const arr = Array.from(fileList)
|
||||||
|
error(null)
|
||||||
|
if (arr.some(f => f.size > maxBytes)) {
|
||||||
|
error(`Máx ${p.max || 2}MB`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const updated = [...files(), ...arr]
|
||||||
|
files(updated)
|
||||||
|
if (isFn(p.onselect)) p.onselect(updated)
|
||||||
|
else if (isFn(p.value)) p.value(updated)
|
||||||
|
}
|
||||||
|
|
||||||
|
const remove = (idx) => {
|
||||||
|
const updated = files().filter((_, i) => i !== idx)
|
||||||
|
files(updated)
|
||||||
|
if (isFn(p.onselect)) p.onselect(updated)
|
||||||
|
else if (isFn(p.value)) p.value(updated)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h('div', { class: cls('fieldset w-full p-0', p.class) }, [
|
||||||
|
h('label', {
|
||||||
|
class: () => `relative flex items-center justify-between w-full h-12 px-4 border-2 border-dashed rounded-lg cursor-pointer transition-all duration-200 ${drag() ? 'border-primary bg-primary/10' : 'border-base-content/20 bg-base-100 hover:bg-base-200'}`,
|
||||||
|
ondragover: (e) => { e.preventDefault(); drag(true) },
|
||||||
|
ondragleave: () => drag(false),
|
||||||
|
ondrop: (e) => { e.preventDefault(); drag(false); process(e.dataTransfer.files) }
|
||||||
|
}, [
|
||||||
|
h('div', { class: 'flex items-center gap-3 w-full' }, [
|
||||||
|
h('span', { class: 'icon-[lucide--upload]' }),
|
||||||
|
h('span', { class: 'text-sm opacity-70 truncate grow text-left' }, 'Arrastra o selecciona archivos...'),
|
||||||
|
h('span', { class: 'text-[10px] opacity-40 shrink-0' }, `Máx ${p.max || 2}MB`)
|
||||||
|
]),
|
||||||
|
h('input', {
|
||||||
|
type: 'file',
|
||||||
|
multiple: true,
|
||||||
|
accept: p.accept || '*',
|
||||||
|
class: 'hidden',
|
||||||
|
onchange: (e) => process(e.target.files)
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
|
||||||
|
() => error() && h('span', { class: 'text-[10px] text-error mt-1 px-1 font-medium' }, error()),
|
||||||
|
|
||||||
|
when(() => files().length > 0, () =>
|
||||||
|
h('ul', { class: 'mt-2 space-y-1' },
|
||||||
|
each(files, (file, idx) =>
|
||||||
|
h('li', { class: 'flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300' }, [
|
||||||
|
h('div', { class: 'flex items-center gap-2 truncate' }, [
|
||||||
|
h('span', { class: 'opacity-50' }, '📄'),
|
||||||
|
h('span', { class: 'truncate font-medium max-w-[200px]' }, file.name),
|
||||||
|
h('span', { class: 'text-[9px] opacity-40' }, `(${(file.size / 1024).toFixed(0)} KB)`)
|
||||||
|
]),
|
||||||
|
h('button', {
|
||||||
|
type: 'button',
|
||||||
|
class: 'btn btn-ghost btn-xs btn-circle',
|
||||||
|
onclick: (e) => { e.preventDefault(); remove(idx) }
|
||||||
|
}, h('span', { class: 'icon-[lucide--x]' }))
|
||||||
|
]),
|
||||||
|
(file) => file.name + file.lastModified
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
//Toast
|
||||||
|
export const Toast = (message, type = "alert-success", duration = 3500) => {
|
||||||
|
let container = document.getElementById("sigpro-toast-container");
|
||||||
|
if (!container) {
|
||||||
|
container = h("div", {
|
||||||
|
id: "sigpro-toast-container",
|
||||||
|
class: "fixed top-0 right-0 z-[9999] p-4 flex flex-col gap-2 pointer-events-none"
|
||||||
|
});
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = h("div", { style: "display: contents" });
|
||||||
|
container.appendChild(host);
|
||||||
|
|
||||||
|
let closeFn, timer, enterTimer;
|
||||||
|
|
||||||
|
const ToastComponent = () => {
|
||||||
|
const visible = $(false);
|
||||||
|
const leaving = $(false);
|
||||||
|
|
||||||
|
closeFn = () => {
|
||||||
|
if (leaving()) return;
|
||||||
|
clearTimeout(timer);
|
||||||
|
clearTimeout(enterTimer);
|
||||||
|
leaving(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
instance.destroy();
|
||||||
|
host.remove();
|
||||||
|
if (!container.hasChildNodes()) container.remove();
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
enterTimer = setTimeout(() => visible(true), 0);
|
||||||
|
|
||||||
|
const content = typeof message === 'function' ? get(message) : message;
|
||||||
|
const msgNode = typeof content === 'string' ? h("span", {}, content) : content;
|
||||||
|
|
||||||
|
return h("div", {
|
||||||
|
class: () => {
|
||||||
|
if (leaving()) return `alert alert-soft ${type} shadow-lg transition-all duration-300 translate-x-full opacity-0 pointer-events-auto`;
|
||||||
|
if (visible()) return `alert alert-soft ${type} shadow-lg transition-all duration-300 translate-x-0 opacity-100 pointer-events-auto`;
|
||||||
|
return `alert alert-soft ${type} shadow-lg transition-all duration-300 translate-x-10 opacity-0 pointer-events-auto`;
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
msgNode,
|
||||||
|
h("button", {
|
||||||
|
class: "btn btn-xs btn-circle btn-ghost",
|
||||||
|
onclick: closeFn
|
||||||
|
}, h("span", { class: "icon-[lucide--x]" }))
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const instance = mount(ToastComponent, host);
|
||||||
|
|
||||||
|
if (duration > 0) timer = setTimeout(closeFn, duration);
|
||||||
|
|
||||||
|
return closeFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Modal
|
||||||
|
export const Modal = (p) => {
|
||||||
|
let dialogRef = null;
|
||||||
|
|
||||||
|
watch(() => {
|
||||||
|
const isOpen = get(p.open);
|
||||||
|
if (!dialogRef) return;
|
||||||
|
isOpen ? dialogRef.showModal() : dialogRef.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const close = () => isFn(p.open) && p.open(false);
|
||||||
|
|
||||||
|
return h("dialog", {
|
||||||
|
...p,
|
||||||
|
ref: el => dialogRef = el,
|
||||||
|
class: cls('modal', p.class),
|
||||||
|
onclose: close,
|
||||||
|
oncancel: close
|
||||||
|
}, [
|
||||||
|
h("div", { class: "modal-box" }, [
|
||||||
|
p.title && h("h3", { class: "text-lg font-bold" }, p.title),
|
||||||
|
p.children,
|
||||||
|
h("div", { class: "modal-action" }, [
|
||||||
|
p.actions || Button({ class: 'btn', onclick: close }, 'Cerrar')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
h("form", { method: "dialog", class: "modal-backdrop" }, [
|
||||||
|
h("button", {}, "close")
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
};
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
// components/Autocomplete.js
|
|
||||||
import { $, h, each, watch } from "sigpro";
|
|
||||||
|
|
||||||
export const Autocomplete = (props) => {
|
|
||||||
const query = $("");
|
|
||||||
const isOpen = $(false);
|
|
||||||
const cursor = $(-1);
|
|
||||||
const filteredItems = $([]);
|
|
||||||
|
|
||||||
watch(() => {
|
|
||||||
const v = typeof props.value === "function" ? props.value() : props.value;
|
|
||||||
return v || "";
|
|
||||||
}, (newVal) => setTimeout(() => query(newVal), 0));
|
|
||||||
|
|
||||||
watch(() => {
|
|
||||||
const q = String(query()).toLowerCase();
|
|
||||||
const allItems = typeof props.items === "function" ? props.items() : props.items;
|
|
||||||
const filtered = q
|
|
||||||
? allItems.filter((item) =>
|
|
||||||
(typeof item === "string" ? item : item.label).toLowerCase().includes(q)
|
|
||||||
)
|
|
||||||
: allItems;
|
|
||||||
filteredItems(filtered);
|
|
||||||
});
|
|
||||||
|
|
||||||
const pick = (item) => {
|
|
||||||
const display = typeof item === "string" ? item : item.label;
|
|
||||||
const actual = typeof item === "string" ? item : item.value;
|
|
||||||
query(display);
|
|
||||||
if (typeof props.value === "function") props.value(actual);
|
|
||||||
props.onselect?.(item);
|
|
||||||
isOpen(false);
|
|
||||||
cursor(-1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyDown = (e) => {
|
|
||||||
const list = filteredItems();
|
|
||||||
if (e.key === "ArrowDown") {
|
|
||||||
e.preventDefault();
|
|
||||||
isOpen(true);
|
|
||||||
cursor(Math.min(cursor() + 1, list.length - 1));
|
|
||||||
} else if (e.key === "ArrowUp") {
|
|
||||||
e.preventDefault();
|
|
||||||
cursor(Math.max(cursor() - 1, 0));
|
|
||||||
} else if (e.key === "Enter" && cursor() >= 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
pick(list[cursor()]);
|
|
||||||
} else if (e.key === "Escape") {
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return h("div", { class: `relative w-full ${props.class ?? ''}` }, [
|
|
||||||
h("label", { class: "input input-bordered w-full" }, [
|
|
||||||
h("span", { class: "icon-[lucide--search]" }),
|
|
||||||
h("input", {
|
|
||||||
...props,
|
|
||||||
type: "text",
|
|
||||||
class: "grow",
|
|
||||||
placeholder: props.placeholder || "Buscar...",
|
|
||||||
value: query,
|
|
||||||
onfocus: () => isOpen(true),
|
|
||||||
onblur: () => setTimeout(() => isOpen(false), 150),
|
|
||||||
onkeydown: handleKeyDown,
|
|
||||||
oninput: (e) => {
|
|
||||||
const newVal = e.target.value;
|
|
||||||
query(newVal);
|
|
||||||
if (typeof props.value === "function") props.value(newVal);
|
|
||||||
isOpen(true);
|
|
||||||
cursor(-1);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
|
|
||||||
h("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",
|
|
||||||
style: () => `display: ${isOpen() && filteredItems().length ? "block" : "none"};`
|
|
||||||
}, [
|
|
||||||
each(filteredItems, (item, idx) =>
|
|
||||||
h("li", {}, [
|
|
||||||
h("a", {
|
|
||||||
class: () => `block w-full ${cursor() === idx ? "active bg-primary text-primary-content" : ""}`,
|
|
||||||
onclick: () => pick(item),
|
|
||||||
onmouseenter: () => cursor(idx)
|
|
||||||
}, typeof item === "string" ? item : item.label)
|
|
||||||
]),
|
|
||||||
(item, idx) => (typeof item === "string" ? item : item.value) + idx
|
|
||||||
),
|
|
||||||
() => filteredItems().length === 0 ? h("li", { class: "flex justify-center p-4 opacity-50" }, h("span", { class: "icon-[lucide--search-x] text-2xl" })) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
145
components/Editor.js
Normal file
145
components/Editor.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { h, $ } from "sigpro"
|
||||||
|
import { get, cls, isFn } from "./All.js"
|
||||||
|
|
||||||
|
export const Editor = (p) => {
|
||||||
|
const { value, class: extraClass } = p
|
||||||
|
let editorRef = null
|
||||||
|
|
||||||
|
const isSource = $(false)
|
||||||
|
const source = $("")
|
||||||
|
|
||||||
|
const notify = () => {
|
||||||
|
if (!editorRef) return
|
||||||
|
const html = editorRef.innerHTML
|
||||||
|
if (isFn(value)) value(html)
|
||||||
|
else p.onchange?.(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
const exec = (cmd, val = null) => {
|
||||||
|
if (!editorRef) return
|
||||||
|
editorRef.focus()
|
||||||
|
document.execCommand(cmd, false, val)
|
||||||
|
notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryState = (cmd, val = null) => {
|
||||||
|
if (!editorRef) return false
|
||||||
|
try {
|
||||||
|
if (cmd === 'formatBlock') {
|
||||||
|
const sel = window.getSelection()
|
||||||
|
if (!sel.rangeCount) return false
|
||||||
|
let node = sel.getRangeAt(0).commonAncestorContainer
|
||||||
|
while (node && node !== editorRef) {
|
||||||
|
if (node.nodeType === 1 && node.tagName === val) return true
|
||||||
|
node = node.parentNode
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return document.queryCommandState(cmd)
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolbar = h("div", { class: "flex flex-wrap items-center gap-1 p-2 border-b border-base-300 bg-base-200" }, [
|
||||||
|
h("div", { class: "flex flex-wrap gap-1 flex-1" }, [
|
||||||
|
h("button", {
|
||||||
|
type: "button",
|
||||||
|
class: () => `btn btn-ghost btn-xs ${queryState('bold') ? 'btn-active' : ''}`,
|
||||||
|
onclick: () => exec("bold")
|
||||||
|
}, h("span", { class: "icon-[lucide--bold]" })),
|
||||||
|
|
||||||
|
h("button", {
|
||||||
|
type: "button",
|
||||||
|
class: () => `btn btn-ghost btn-xs ${queryState('italic') ? 'btn-active' : ''}`,
|
||||||
|
onclick: () => exec("italic")
|
||||||
|
}, h("span", { class: "icon-[lucide--italic]" })),
|
||||||
|
|
||||||
|
h("button", {
|
||||||
|
type: "button",
|
||||||
|
class: () => `btn btn-ghost btn-xs ${queryState('underline') ? 'btn-active' : ''}`,
|
||||||
|
onclick: () => exec("underline")
|
||||||
|
}, h("span", { class: "icon-[lucide--underline]" })),
|
||||||
|
|
||||||
|
h("button", {
|
||||||
|
type: "button",
|
||||||
|
class: () => `btn btn-ghost btn-xs ${queryState('strikeThrough') ? 'btn-active' : ''}`,
|
||||||
|
onclick: () => exec("strikeThrough")
|
||||||
|
}, h("span", { class: "icon-[lucide--strikethrough]" })),
|
||||||
|
|
||||||
|
h("span", { class: "w-px h-5 bg-base-300 mx-1" }),
|
||||||
|
|
||||||
|
h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("insertUnorderedList") }, h("span", { class: "icon-[lucide--list]" })),
|
||||||
|
h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("insertOrderedList") }, h("span", { class: "icon-[lucide--list-ordered]" })),
|
||||||
|
|
||||||
|
h("span", { class: "w-px h-5 bg-base-300 mx-1" }),
|
||||||
|
|
||||||
|
h("button", {
|
||||||
|
type: "button",
|
||||||
|
class: () => `btn btn-ghost btn-xs ${queryState('formatBlock', 'BLOCKQUOTE') ? 'btn-active' : ''}`,
|
||||||
|
onclick: () => exec("formatBlock", queryState('formatBlock', 'BLOCKQUOTE') ? 'P' : 'BLOCKQUOTE')
|
||||||
|
}, h("span", { class: "icon-[lucide--quote]" })),
|
||||||
|
|
||||||
|
h("span", { class: "w-px h-5 bg-base-300 mx-1" }),
|
||||||
|
|
||||||
|
h("select", { class: "select select-xs w-16", onchange: (e) => exec("fontSize", e.target.value), value: "3" }, [
|
||||||
|
h("option", { value: "1" }, "1"),
|
||||||
|
h("option", { value: "2" }, "2"),
|
||||||
|
h("option", { value: "3" }, "3"),
|
||||||
|
h("option", { value: "4" }, "4"),
|
||||||
|
h("option", { value: "5" }, "5"),
|
||||||
|
h("option", { value: "6" }, "6"),
|
||||||
|
h("option", { value: "7" }, "7"),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h("span", { class: "w-px h-5 bg-base-300 mx-1" }),
|
||||||
|
|
||||||
|
h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("undo") }, h("span", { class: "icon-[lucide--undo-2]" })),
|
||||||
|
h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("redo") }, h("span", { class: "icon-[lucide--redo-2]" })),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h("div", { class: "flex" }, [
|
||||||
|
h("button", {
|
||||||
|
type: "button",
|
||||||
|
class: () => `btn btn-ghost btn-xs ${isSource() ? 'btn-active' : ''}`,
|
||||||
|
onclick: () => {
|
||||||
|
const wasSource = isSource()
|
||||||
|
if (!wasSource) {
|
||||||
|
source(editorRef?.innerHTML || "")
|
||||||
|
} else {
|
||||||
|
if (editorRef) {
|
||||||
|
editorRef.innerHTML = source()
|
||||||
|
notify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isSource(!wasSource)
|
||||||
|
}
|
||||||
|
}, h("span", { class: "icon-[lucide--code-2]" }))
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
return h("div", { class: cls("border border-base-300 rounded-box bg-base-100 overflow-hidden", extraClass) }, [
|
||||||
|
toolbar,
|
||||||
|
h("div", { class: "relative" }, [
|
||||||
|
h("div", {
|
||||||
|
ref: el => {
|
||||||
|
if (!editorRef && el) {
|
||||||
|
editorRef = el
|
||||||
|
el.innerHTML = get(value) || ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: () => `min-height:10rem;${isSource() ? 'display:none' : ''}`,
|
||||||
|
class: "p-3 outline-none text-base-content [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:list-item [&_p]:m-0 [&_div]:m-0 [&_br]:content-[''] [&_br]:block [&_br]:h-[1em]",
|
||||||
|
contenteditable: "true",
|
||||||
|
oninput: notify,
|
||||||
|
onpaste: () => setTimeout(notify, 0)
|
||||||
|
}),
|
||||||
|
h("textarea", {
|
||||||
|
class: "w-full min-h-[10rem] p-3 outline-none font-mono text-sm bg-base-200 border-0",
|
||||||
|
style: () => isSource() ? '' : 'display:none',
|
||||||
|
value: source,
|
||||||
|
oninput: (e) => source(e.target.value)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { h, $, when, fx } from 'sigpro';
|
|
||||||
import { get, cls, isFunc } from './_core.js';
|
|
||||||
|
|
||||||
export const Input = (props) => {
|
|
||||||
const { label, icon, float, placeholder, value, left, right, content, ...rest } = props;
|
|
||||||
|
|
||||||
const showPassword = $(false);
|
|
||||||
const isFocused = $(false);
|
|
||||||
const isPassword = props.type === 'password';
|
|
||||||
|
|
||||||
const inputType = () => isPassword
|
|
||||||
? (get(showPassword) ? 'text' : 'password')
|
|
||||||
: (props.type || 'text');
|
|
||||||
|
|
||||||
return h("div", {
|
|
||||||
class: "input-container",
|
|
||||||
onfocusin: () => isFocused(true),
|
|
||||||
onfocusout: (e) => {
|
|
||||||
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
||||||
isFocused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
h('label', { class: "floating-label" }, [
|
|
||||||
float ? h("span", {}, label) : null,
|
|
||||||
h("label", {
|
|
||||||
class: () => cls('input', props.class)
|
|
||||||
}, [
|
|
||||||
label && !float ? h('span', { class: 'label' }, label) : null,
|
|
||||||
left ?? null,
|
|
||||||
h('input', {
|
|
||||||
...rest,
|
|
||||||
type: inputType,
|
|
||||||
class: 'grow',
|
|
||||||
placeholder: placeholder || label || ' ',
|
|
||||||
value: value
|
|
||||||
}),
|
|
||||||
right ?? null,
|
|
||||||
|
|
||||||
isPassword ? h('label', { class: 'swap swap-rotate ml-2' }, [
|
|
||||||
h('input', {
|
|
||||||
type: 'checkbox',
|
|
||||||
onchange: (e) => showPassword(e.target.checked)
|
|
||||||
}),
|
|
||||||
h('span', { class: 'swap-on icon-[lucide--eye]' }),
|
|
||||||
h('span', { class: 'swap-off icon-[lucide--eye-off]' })
|
|
||||||
]) : null
|
|
||||||
]),
|
|
||||||
|
|
||||||
when(isFocused, () => fx({ duration: 300, slide: true },
|
|
||||||
h('div', {
|
|
||||||
class: 'input-content',
|
|
||||||
onmousedown: e => e.preventDefault()
|
|
||||||
}, [
|
|
||||||
isFunc(content) ? content(isFocused) : content
|
|
||||||
])
|
|
||||||
))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import { h, $ } from 'sigpro.js';
|
|
||||||
import { ui, cls, get } from './_core.js';
|
|
||||||
|
|
||||||
export const InputBase = ({
|
|
||||||
loading = false,
|
|
||||||
ui: uiOptions = '',
|
|
||||||
class: className = '',
|
|
||||||
type = 'text',
|
|
||||||
value,
|
|
||||||
onInput,
|
|
||||||
...inputProps
|
|
||||||
}) => {
|
|
||||||
const isPassword = type === 'password';
|
|
||||||
const showPassword = $(false);
|
|
||||||
|
|
||||||
const inputClasses = cls(
|
|
||||||
ui('input', uiOptions),
|
|
||||||
className,
|
|
||||||
isPassword && 'pr-10',
|
|
||||||
loading && 'loading loading-spinner loading-xs absolute right-2 top-1/2 -translate-y-1/2'
|
|
||||||
);
|
|
||||||
|
|
||||||
let inputEl = h('input', {
|
|
||||||
class: inputClasses + (isPassword ? ' pr-10' : ''),
|
|
||||||
type: isPassword ? 'text' : 'password',
|
|
||||||
value,
|
|
||||||
oninput: onInput,
|
|
||||||
...inputProps
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isPassword) {
|
|
||||||
const toggle = h('label', {
|
|
||||||
class: 'swap swap-rotate absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer'
|
|
||||||
}, [
|
|
||||||
h('input', {
|
|
||||||
type: 'checkbox',
|
|
||||||
checked: showPassword(),
|
|
||||||
onchange: (e) => showPassword(e.target.checked)
|
|
||||||
}),
|
|
||||||
h('span', { class: 'swap-on icon-[lucide--eye]' }),
|
|
||||||
h('span', { class: 'swap-off icon-[lucide--eye-off]' })
|
|
||||||
]);
|
|
||||||
inputEl = h('div', { class: 'relative w-full' }, [inputEl, toggle]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icon) {
|
|
||||||
const isIconClass = icon.includes('-') || icon.includes('icon-');
|
|
||||||
const iconElement = isIconClass
|
|
||||||
? h('span', { class: icon })
|
|
||||||
: h('span', { class: 'opacity-50' }, icon);
|
|
||||||
content = h('label', { class: ui('input', uiOptions).replace('input-', '') }, [
|
|
||||||
iconElement,
|
|
||||||
h('span', { class: 'opacity-70' })
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return inputEl;
|
|
||||||
};
|
|
||||||
61
components/discarted/Autocomplete.js
Normal file
61
components/discarted/Autocomplete.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { $, h, when, fx } from 'sigpro';
|
||||||
|
import { Input } from '../Input.js';
|
||||||
|
import { filterBy, listKey, getBy } from '../All.js';
|
||||||
|
|
||||||
|
|
||||||
|
export const Autocomplete = ({ items, value, onselect, placeholder = 'Buscar...', ...props }) => {
|
||||||
|
const query = $(value ? (typeof value === 'function' ? value() : value) : '');
|
||||||
|
const isOpen = $(false);
|
||||||
|
const filtered = $(() => filterBy(items, query()));
|
||||||
|
const { cursor, onKey } = listKey(filtered, isOpen);
|
||||||
|
|
||||||
|
const pick = (item) => {
|
||||||
|
const display = getBy(item);
|
||||||
|
const actual = typeof item === 'string' ? item : item.value;
|
||||||
|
query(display);
|
||||||
|
if (typeof value === 'function') value(actual);
|
||||||
|
onselect?.(item);
|
||||||
|
isOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return h('div', { class: 'relative w-full' }, [
|
||||||
|
Input({
|
||||||
|
...props,
|
||||||
|
type: 'text',
|
||||||
|
placeholder,
|
||||||
|
value: query,
|
||||||
|
left: h('span', { class: 'icon-[lucide--search]' }),
|
||||||
|
oninput: (e) => {
|
||||||
|
query(e.target.value);
|
||||||
|
if (typeof value === 'function') value(e.target.value);
|
||||||
|
isOpen(true);
|
||||||
|
},
|
||||||
|
onfocus: () => isOpen(true),
|
||||||
|
onblur: () => setTimeout(() => isOpen(false), 150),
|
||||||
|
onkeydown: (e) => onKey(e, pick)
|
||||||
|
}),
|
||||||
|
|
||||||
|
when(isOpen, () =>
|
||||||
|
fx({ duration: 200, slide: true },
|
||||||
|
h('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 flex-col flex-nowrap'
|
||||||
|
}, [
|
||||||
|
each(filtered, (item, idx) =>
|
||||||
|
h('li', {}, [
|
||||||
|
h('a', {
|
||||||
|
class: () => cursor() === idx ? 'active bg-primary text-primary-content' : '',
|
||||||
|
onmousedown: (e) => e.preventDefault(), // evita que el blur cierre antes del click
|
||||||
|
onclick: () => pick(item),
|
||||||
|
onmouseenter: () => cursor(idx)
|
||||||
|
}, getBy(item))
|
||||||
|
]),
|
||||||
|
(item, idx) => getBy(item) + idx
|
||||||
|
),
|
||||||
|
() => filtered().length === 0
|
||||||
|
? h('li', { class: 'p-4 opacity-50 text-center' }, 'Sin resultados')
|
||||||
|
: null
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
};
|
||||||
46
components/discarted/Input.js
Normal file
46
components/discarted/Input.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { h, $, when, fx } from 'sigpro';
|
||||||
|
import { get, cls, isFn } from '../All.js';
|
||||||
|
|
||||||
|
export const Input = (props) => {
|
||||||
|
const { label, icon, float, placeholder, value, left, right, rule, hint, content, ...rest } = props;
|
||||||
|
|
||||||
|
const showPassword = $(false);
|
||||||
|
const isFocused = $(false);
|
||||||
|
const isPassword = props.type === 'password';
|
||||||
|
const pattern = rule ?? null;
|
||||||
|
|
||||||
|
const inputType = () => isPassword
|
||||||
|
? (get(showPassword) ? 'text' : 'password')
|
||||||
|
: (props.type || 'text');
|
||||||
|
|
||||||
|
return h("div", {
|
||||||
|
class: "input-container",
|
||||||
|
onfocusin: () => isFocused(true),
|
||||||
|
onfocusout: (e) => {
|
||||||
|
if (!e.currentTarget.contains(e.relatedTarget)) { isFocused(false); }
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
h('label', { class: "floating-label" }, [
|
||||||
|
float ? h("span", {}, label) : null,
|
||||||
|
h("label", { pattern: pattern, class: () => cls('input validator', props.class) },
|
||||||
|
[
|
||||||
|
label && !float ? h('span', { class: 'label' }, label) : null,
|
||||||
|
left ?? null,
|
||||||
|
h('input', { ...rest, type: inputType, class: 'grow', pattern: pattern, placeholder: placeholder || label || ' ', value: value }), right ?? null,
|
||||||
|
isPassword ? h('label', { class: 'swap swap-rotate ml-2' }, [
|
||||||
|
h('input', { type: 'checkbox', onchange: (e) => showPassword(e.target.checked) }),
|
||||||
|
h('span', { class: 'swap-on icon-[lucide--eye]' }),
|
||||||
|
h('span', { class: 'swap-off icon-[lucide--eye-off]' })
|
||||||
|
]) : null
|
||||||
|
]),
|
||||||
|
hint ? h('div', { class: "validator-hint" }, hint) : null,
|
||||||
|
|
||||||
|
when(isFocused, () => fx({ duration: 300, slide: true },
|
||||||
|
h('div', { class: 'input-content', onmousedown: e => e.preventDefault() },
|
||||||
|
[
|
||||||
|
isFn(content) ? content(isFocused) : content
|
||||||
|
])
|
||||||
|
))
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
};
|
||||||
30
components/discarted/Select.js
Normal file
30
components/discarted/Select.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// components/Select.js
|
||||||
|
import { h, each } from "sigpro";
|
||||||
|
|
||||||
|
export const Select = (props) => {
|
||||||
|
const { items, placeholder, placeholderDisabled = true, keyFn, children, ...rest } = props;
|
||||||
|
|
||||||
|
if (children !== undefined) {
|
||||||
|
return h("select", { ...rest, class: `select ${rest.class ?? ''}` }, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
const placeholderOption = placeholder
|
||||||
|
? h("option", { disabled: placeholderDisabled, selected: true }, placeholder)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const dynamicOptions = each(
|
||||||
|
() => [...(typeof items === "function" ? items() : items || [])],
|
||||||
|
(item) => {
|
||||||
|
const value = typeof item === "string" ? item : item.value;
|
||||||
|
const label = typeof item === "string" ? item : item.label;
|
||||||
|
return h("option", { value }, label);
|
||||||
|
},
|
||||||
|
keyFn || ((item) => (typeof item === "string" ? item : item.value))
|
||||||
|
);
|
||||||
|
|
||||||
|
const options = placeholderOption
|
||||||
|
? [placeholderOption, dynamicOptions]
|
||||||
|
: dynamicOptions;
|
||||||
|
|
||||||
|
return h("select", { ...rest, class: `select ${rest.class ?? ''}` }, options);
|
||||||
|
};
|
||||||
@@ -23,6 +23,7 @@ export const filterBy = (items, query, field = 'label') => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const listKey = (items, isOpen) => {
|
export const listKey = (items, isOpen) => {
|
||||||
const cursor = $(-1);
|
const cursor = $(-1);
|
||||||
|
|
||||||
@@ -65,4 +65,5 @@ export const Fileinput = (props) => {
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
4
components/discarted/tooltip.js
Normal file
4
components/discarted/tooltip.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// components/Tooltip.js
|
||||||
|
import { h } from "sigpro";
|
||||||
|
|
||||||
|
export const Tooltip = (p, c) => h("div", { ...p, class: `tooltip ${p.class ?? ''}`, "data-tip": p.tip }, c);
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// components/Select.js
|
|
||||||
import { h, each } from "sigpro";
|
|
||||||
|
|
||||||
export const Select = (props, children) => {
|
|
||||||
children === undefined && (children = props, props = {});
|
|
||||||
return h("select", { ...props, class: `select ${props.class ?? ''}` }, children);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SelectItems = (props) => {
|
|
||||||
const placeholderOption = props.placeholder
|
|
||||||
? h("option", { disabled: props.placeholderDisabled ?? true, selected: true }, props.placeholder)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const dynamicOptions = each(
|
|
||||||
() => [...(typeof props.items === "function" ? props.items() : props.items || [])],
|
|
||||||
(item) => {
|
|
||||||
const val = typeof item === "string" ? item : item.value;
|
|
||||||
const label = typeof item === "string" ? item : item.label;
|
|
||||||
return h("option", { value: val }, label);
|
|
||||||
},
|
|
||||||
props.keyFn || ((item) => (typeof item === "string" ? item : item.value))
|
|
||||||
);
|
|
||||||
|
|
||||||
return placeholderOption ? [placeholderOption, dynamicOptions] : dynamicOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SelectLabel = (props, children) => h("label", { class: `${props.float ? 'floating-label' : 'select'}` },
|
|
||||||
[
|
|
||||||
h("span", { class: props.float ? '' : 'label opacity-50' }, props.label),
|
|
||||||
props.left ?? null,
|
|
||||||
h("select", { ...props, class: `${props.float ? 'select' : ''} ${props.class ?? ''}` }, children),
|
|
||||||
props.right ?? null
|
|
||||||
]
|
|
||||||
);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// components/Tooltip.js
|
|
||||||
import { h } from "sigpro";
|
|
||||||
|
|
||||||
export const Tooltip = (props, children) => {
|
|
||||||
children === undefined && (children = props, props = {});
|
|
||||||
return h("div", { ...props, class: `tooltip ${props.class ?? ''}`, "data-tip": props.tip }, children);
|
|
||||||
};
|
|
||||||
352
dist/sigpro-ui.css
vendored
352
dist/sigpro-ui.css
vendored
@@ -13,6 +13,7 @@
|
|||||||
--spacing: 0.25rem;
|
--spacing: 0.25rem;
|
||||||
--container-xs: 20rem;
|
--container-xs: 20rem;
|
||||||
--container-md: 28rem;
|
--container-md: 28rem;
|
||||||
|
--container-2xl: 42rem;
|
||||||
--container-3xl: 48rem;
|
--container-3xl: 48rem;
|
||||||
--container-5xl: 64rem;
|
--container-5xl: 64rem;
|
||||||
--container-6xl: 72rem;
|
--container-6xl: 72rem;
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
--font-weight-light: 300;
|
--font-weight-light: 300;
|
||||||
--font-weight-normal: 400;
|
--font-weight-normal: 400;
|
||||||
--font-weight-medium: 500;
|
--font-weight-medium: 500;
|
||||||
|
--font-weight-semibold: 600;
|
||||||
--font-weight-bold: 700;
|
--font-weight-bold: 700;
|
||||||
--font-weight-black: 900;
|
--font-weight-black: 900;
|
||||||
--tracking-tighter: -0.05em;
|
--tracking-tighter: -0.05em;
|
||||||
@@ -1392,6 +1394,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.validator-hint {
|
||||||
|
@layer daisyui.l1.l2.l3 {
|
||||||
|
visibility: hidden;
|
||||||
|
margin-top: calc(0.25rem * 2);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.validator {
|
||||||
|
@layer daisyui.l1.l2.l3 {
|
||||||
|
&:user-valid, &:has(:user-valid) {
|
||||||
|
&, &:focus, &:checked, &[aria-checked="true"], &:focus-within {
|
||||||
|
--input-color: var(--color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) {
|
||||||
|
&, &:focus, &:checked, &[aria-checked="true"], &:focus-within {
|
||||||
|
--input-color: var(--color-error);
|
||||||
|
}
|
||||||
|
& ~ .validator-hint {
|
||||||
|
visibility: visible;
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) {
|
||||||
|
& ~ .validator-hint {
|
||||||
|
display: revert-layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.collapse {
|
.collapse {
|
||||||
visibility: collapse;
|
visibility: collapse;
|
||||||
}
|
}
|
||||||
@@ -3101,9 +3133,6 @@
|
|||||||
.top-0 {
|
.top-0 {
|
||||||
top: calc(var(--spacing) * 0);
|
top: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
.top-1\/2 {
|
|
||||||
top: calc(1 / 2 * 100%);
|
|
||||||
}
|
|
||||||
.top-2 {
|
.top-2 {
|
||||||
top: calc(var(--spacing) * 2);
|
top: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
@@ -3125,9 +3154,6 @@
|
|||||||
.right-1\/4 {
|
.right-1\/4 {
|
||||||
right: calc(1 / 4 * 100%);
|
right: calc(1 / 4 * 100%);
|
||||||
}
|
}
|
||||||
.right-2 {
|
|
||||||
right: calc(var(--spacing) * 2);
|
|
||||||
}
|
|
||||||
.right-3 {
|
.right-3 {
|
||||||
right: calc(var(--spacing) * 3);
|
right: calc(var(--spacing) * 3);
|
||||||
}
|
}
|
||||||
@@ -3879,6 +3905,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.mx-1 {
|
||||||
|
margin-inline: calc(var(--spacing) * 1);
|
||||||
|
}
|
||||||
.mx-auto {
|
.mx-auto {
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
@@ -4133,6 +4162,19 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01'/%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m21.73 18l-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3M12 9v4m0 4h.01'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--bold\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--calendar\] {
|
.icon-\[lucide--calendar\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4224,6 +4266,32 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 17l5-5l-5-5m7 10l5-5l-5-5'/%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 17l5-5l-5-5m7 10l5-5l-5-5'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--clock\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 6v6l4 2'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
.icon-\[lucide--code-2\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m18 16l4-4l-4-4M6 8l-4 4l4 4m8.5-12l-5 16'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--eye-off\] {
|
.icon-\[lucide--eye-off\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4289,6 +4357,19 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4m0-4h.01'/%3E%3C/g%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4m0-4h.01'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--italic\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 4h-9m4 16H5M15 4L9 20'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--link\] {
|
.icon-\[lucide--link\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4302,6 +4383,32 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'/%3E%3C/g%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--list-ordered\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M11 5h10m-10 7h10m-10 7h10M4 4h1v5M4 9h2m.5 11H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
.icon-\[lucide--list\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M3 5h.01M3 12h.01M3 19h.01M8 5h13M8 12h13M8 19h13'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--lock\] {
|
.icon-\[lucide--lock\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4341,7 +4448,7 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233a14 14 0 0 0 6.392 6.384'/%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233a14 14 0 0 0 6.392 6.384'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
.icon-\[lucide--search-x\] {
|
.icon-\[lucide--plus\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
@@ -4352,7 +4459,33 @@
|
|||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
-webkit-mask-size: 100% 100%;
|
-webkit-mask-size: 100% 100%;
|
||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='m13.5 8.5l-5 5m0-5l5 5'/%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21l-4.3-4.3'/%3E%3C/g%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 12h14m-7-7v14'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
.icon-\[lucide--quote\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2a1 1 0 0 1 1 1v1a2 2 0 0 1-2 2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1a6 6 0 0 0 6-6V5a2 2 0 0 0-2-2zM5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2a1 1 0 0 1 1 1v1a2 2 0 0 1-2 2a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1a6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
.icon-\[lucide--redo-2\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='m15 14l5-5l-5-5'/%3E%3Cpath d='M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
.icon-\[lucide--search\] {
|
.icon-\[lucide--search\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -4367,6 +4500,32 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='m21 21l-4.34-4.34'/%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3C/g%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='m21 21l-4.34-4.34'/%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--settings\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M9.671 4.136a2.34 2.34 0 0 1 4.659 0a2.34 2.34 0 0 0 3.319 1.915a2.34 2.34 0 0 1 2.33 4.033a2.34 2.34 0 0 0 0 3.831a2.34 2.34 0 0 1-2.33 4.033a2.34 2.34 0 0 0-3.319 1.915a2.34 2.34 0 0 1-4.659 0a2.34 2.34 0 0 0-3.32-1.915a2.34 2.34 0 0 1-2.33-4.033a2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
.icon-\[lucide--strikethrough\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M16 4H9a3 3 0 0 0-2.83 4M14 12a4 4 0 0 1 0 8H6m-2-8h16'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--text\] {
|
.icon-\[lucide--text\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4380,6 +4539,32 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M15 18H3M17 6H3m18 6H3'/%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M15 18H3M17 6H3m18 6H3'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--underline\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 4v6a6 6 0 0 0 12 0V4M4 20h16'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
.icon-\[lucide--undo-2\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M9 14L4 9l5-5'/%3E%3Cpath d='M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--upload\] {
|
.icon-\[lucide--upload\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4393,6 +4578,19 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 3v12m5-7l-5-5l-5 5m14 7v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/%3E%3C/svg%3E");
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 3v12m5-7l-5-5l-5 5m14 7v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
.icon-\[lucide--user\] {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cg fill='none' stroke='black' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Cpath d='M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
.icon-\[lucide--x\] {
|
.icon-\[lucide--x\] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@@ -4687,6 +4885,9 @@
|
|||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.inline {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
.inline-block {
|
.inline-block {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@@ -4776,6 +4977,9 @@
|
|||||||
.h-4 {
|
.h-4 {
|
||||||
height: calc(var(--spacing) * 4);
|
height: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
|
.h-5 {
|
||||||
|
height: calc(var(--spacing) * 5);
|
||||||
|
}
|
||||||
.h-8 {
|
.h-8 {
|
||||||
height: calc(var(--spacing) * 8);
|
height: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
@@ -4827,6 +5031,9 @@
|
|||||||
.min-h-0 {
|
.min-h-0 {
|
||||||
min-height: calc(var(--spacing) * 0);
|
min-height: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
|
.min-h-\[10rem\] {
|
||||||
|
min-height: 10rem;
|
||||||
|
}
|
||||||
.min-h-\[300px\] {
|
.min-h-\[300px\] {
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
@@ -4850,11 +5057,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.loading-xs {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
width: calc(var(--size-selector, 0.25rem) * 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.w-3\.5 {
|
.w-3\.5 {
|
||||||
width: calc(var(--spacing) * 3.5);
|
width: calc(var(--spacing) * 3.5);
|
||||||
}
|
}
|
||||||
@@ -4903,6 +5105,12 @@
|
|||||||
.w-full {
|
.w-full {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.w-px {
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
.max-w-2xl {
|
||||||
|
max-width: var(--container-2xl);
|
||||||
|
}
|
||||||
.max-w-3xl {
|
.max-w-3xl {
|
||||||
max-width: var(--container-3xl);
|
max-width: var(--container-3xl);
|
||||||
}
|
}
|
||||||
@@ -4949,6 +5157,10 @@
|
|||||||
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
--tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
}
|
}
|
||||||
|
.translate-x-0 {
|
||||||
|
--tw-translate-x: calc(var(--spacing) * 0);
|
||||||
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
|
}
|
||||||
.translate-x-2 {
|
.translate-x-2 {
|
||||||
--tw-translate-x: calc(var(--spacing) * 2);
|
--tw-translate-x: calc(var(--spacing) * 2);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
@@ -4965,10 +5177,6 @@
|
|||||||
--tw-translate-x: 100%;
|
--tw-translate-x: 100%;
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
}
|
}
|
||||||
.-translate-y-1\/2 {
|
|
||||||
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
|
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
|
||||||
}
|
|
||||||
.translate-y-2 {
|
.translate-y-2 {
|
||||||
--tw-translate-y: calc(var(--spacing) * 2);
|
--tw-translate-y: calc(var(--spacing) * 2);
|
||||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||||
@@ -5098,6 +5306,9 @@
|
|||||||
.flex-col-reverse {
|
.flex-col-reverse {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
|
.flex-nowrap {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
.flex-wrap {
|
.flex-wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
@@ -5107,6 +5318,9 @@
|
|||||||
.items-end {
|
.items-end {
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
.items-start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
.items-stretch {
|
.items-stretch {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
@@ -5283,6 +5497,10 @@
|
|||||||
border-style: var(--tw-border-style);
|
border-style: var(--tw-border-style);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
|
.border-0 {
|
||||||
|
border-style: var(--tw-border-style);
|
||||||
|
border-width: 0px;
|
||||||
|
}
|
||||||
.border-2 {
|
.border-2 {
|
||||||
border-style: var(--tw-border-style);
|
border-style: var(--tw-border-style);
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
@@ -5553,6 +5771,17 @@
|
|||||||
--tw-gradient-position: to right in oklab;
|
--tw-gradient-position: to right in oklab;
|
||||||
background-image: linear-gradient(var(--tw-gradient-stops));
|
background-image: linear-gradient(var(--tw-gradient-stops));
|
||||||
}
|
}
|
||||||
|
.skeleton-text {
|
||||||
|
@layer daisyui.l1.l2 {
|
||||||
|
background-clip: text;
|
||||||
|
webkit-background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
background-image: linear-gradient( 105deg, var(--color-base-content) 0% 40%, var(--color-base-content) 50%, var(--color-base-content) 60% 100% );
|
||||||
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
|
background-image: linear-gradient( 105deg, color-mix(in oklab, var(--color-base-content) 20%, transparent) 0% 40%, var(--color-base-content) 50%, color-mix(in oklab, var(--color-base-content) 20%, transparent) 60% 100% );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.alert-soft {
|
.alert-soft {
|
||||||
@layer daisyui.l1 {
|
@layer daisyui.l1 {
|
||||||
color: var(--alert-color, var(--color-base-content));
|
color: var(--alert-color, var(--color-base-content));
|
||||||
@@ -6009,6 +6238,10 @@
|
|||||||
--tw-font-weight: var(--font-weight-normal);
|
--tw-font-weight: var(--font-weight-normal);
|
||||||
font-weight: var(--font-weight-normal);
|
font-weight: var(--font-weight-normal);
|
||||||
}
|
}
|
||||||
|
.font-semibold {
|
||||||
|
--tw-font-weight: var(--font-weight-semibold);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
}
|
||||||
.tracking-tight {
|
.tracking-tight {
|
||||||
--tw-tracking: var(--tracking-tight);
|
--tw-tracking: var(--tracking-tight);
|
||||||
letter-spacing: var(--tracking-tight);
|
letter-spacing: var(--tracking-tight);
|
||||||
@@ -6293,6 +6526,9 @@
|
|||||||
.line-through {
|
.line-through {
|
||||||
text-decoration-line: line-through;
|
text-decoration-line: line-through;
|
||||||
}
|
}
|
||||||
|
.underline {
|
||||||
|
text-decoration-line: underline;
|
||||||
|
}
|
||||||
.swap-active {
|
.swap-active {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
.swap-off {
|
.swap-off {
|
||||||
@@ -6327,6 +6563,9 @@
|
|||||||
.opacity-80 {
|
.opacity-80 {
|
||||||
opacity: 80%;
|
opacity: 80%;
|
||||||
}
|
}
|
||||||
|
.opacity-100 {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
.shadow {
|
.shadow {
|
||||||
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
@@ -6400,6 +6639,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.blur {
|
||||||
|
--tw-blur: blur(8px);
|
||||||
|
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,);
|
||||||
|
}
|
||||||
.blur-3xl {
|
.blur-3xl {
|
||||||
--tw-blur: blur(var(--blur-3xl));
|
--tw-blur: blur(var(--blur-3xl));
|
||||||
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,);
|
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,);
|
||||||
@@ -7139,6 +7382,57 @@
|
|||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.\[\&_br\]\:block {
|
||||||
|
& br {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_br\]\:h-\[1em\] {
|
||||||
|
& br {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_br\]\:content-\[\'\'\] {
|
||||||
|
& br {
|
||||||
|
--tw-content: '';
|
||||||
|
content: var(--tw-content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_div\]\:m-0 {
|
||||||
|
& div {
|
||||||
|
margin: calc(var(--spacing) * 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_li\]\:list-item {
|
||||||
|
& li {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_ol\]\:list-decimal {
|
||||||
|
& ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_ol\]\:pl-6 {
|
||||||
|
& ol {
|
||||||
|
padding-left: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_p\]\:m-0 {
|
||||||
|
& p {
|
||||||
|
margin: calc(var(--spacing) * 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_ul\]\:list-disc {
|
||||||
|
& ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.\[\&_ul\]\:pl-6 {
|
||||||
|
& ul {
|
||||||
|
padding-left: calc(var(--spacing) * 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
:root {
|
:root {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -7170,14 +7464,32 @@
|
|||||||
animation: tabFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
animation: tabFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
}
|
}
|
||||||
|
.input-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.input-container .input {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.input-content {
|
.input-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 50;
|
||||||
|
background: oklch(var(--b1));
|
||||||
|
border: 1px solid oklch(var(--bc) / 0.2);
|
||||||
|
border-radius: var(--rounded-box, 1rem);
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.input-content .menu {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 100;
|
|
||||||
background: white;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
@keyframes tabFadeIn {
|
@keyframes tabFadeIn {
|
||||||
from {
|
from {
|
||||||
|
|||||||
1835
dist/sigpro-ui.esm.js
vendored
1835
dist/sigpro-ui.esm.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/sigpro-ui.esm.min.js
vendored
2
dist/sigpro-ui.esm.min.js
vendored
File diff suppressed because one or more lines are too long
1835
dist/sigpro-ui.js
vendored
1835
dist/sigpro-ui.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/sigpro-ui.min.css
vendored
2
dist/sigpro-ui.min.css
vendored
File diff suppressed because one or more lines are too long
2
dist/sigpro-ui.min.js
vendored
2
dist/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -5,41 +5,8 @@
|
|||||||
* **Introduction**
|
* **Introduction**
|
||||||
* [Installation](install.md)
|
* [Installation](install.md)
|
||||||
* [Quick Reference](quick.md)
|
* [Quick Reference](quick.md)
|
||||||
|
* [Forms & Inputs](demo_forms.md)
|
||||||
* **Forms & Inputs**
|
* [Data & Dysplay](demo_display.md)
|
||||||
* [Autocomplete](components/autocomplete.md)
|
* [Feedback & Overlays](demo_overlay.md)
|
||||||
* [Button](components/button.md)
|
* [Navigation & Layout](demo_layout.md)
|
||||||
* [Checkbox](components/checkbox.md)
|
* [WYSIWYG Editor](demo_editor.md)
|
||||||
* [Colorpicker](components/colorpicker.md)
|
|
||||||
* [Datepicker](components/datepicker.md)
|
|
||||||
* [Input](components/input.md)
|
|
||||||
* [Radio](components/radio.md)
|
|
||||||
* [Range](components/range.md)
|
|
||||||
* [Rating](components/rating.md)
|
|
||||||
* [Select](components/select.md)
|
|
||||||
* [Swap](components/swap.md)
|
|
||||||
|
|
||||||
* **Data Display**
|
|
||||||
* [Badge](components/badge.md)
|
|
||||||
* [Indicator](components/indicator.md)
|
|
||||||
* [List](components/list.md)
|
|
||||||
* [Stack](components/stack.md)
|
|
||||||
* [Stat](components/stat.md)
|
|
||||||
* [Table](components/table.md)
|
|
||||||
* [Timeline](components/timeline.md)
|
|
||||||
|
|
||||||
* **Feedback & Overlays**
|
|
||||||
* [Alert](components/alert.md)
|
|
||||||
* [Modal](components/modal.md)
|
|
||||||
* [Toast](components/toast.md)
|
|
||||||
* [Tooltip](components/tooltip.md)
|
|
||||||
|
|
||||||
* **Navigation & Layout**
|
|
||||||
* [Accordion](components/accordion.md)
|
|
||||||
* [Drawer](components/drawer.md)
|
|
||||||
* [Dropdown](components/dropdown.md)
|
|
||||||
* [Fab](components/fab.md)
|
|
||||||
* [Fieldset](components/fieldset.md)
|
|
||||||
* [Menu](components/menu.md)
|
|
||||||
* [Navbar](components/navbar.md)
|
|
||||||
* [Tabs](components/tabs.md)
|
|
||||||
@@ -8,51 +8,108 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const paises = [
|
// const App = () => {
|
||||||
"España",
|
// const selected = $('');
|
||||||
"México",
|
// const filterType = $('all');
|
||||||
"Argentina",
|
|
||||||
"Colombia",
|
// const allItems = {
|
||||||
"Chile",
|
// fruits: ['Apple', 'Banana', 'Orange', 'Mango'],
|
||||||
"Perú",
|
// vegetables: ['Carrot', 'Broccoli', 'Spinach', 'Potato'],
|
||||||
"Venezuela",
|
// all: ['Apple', 'Banana', 'Orange', 'Mango', 'Carrot', 'Broccoli', 'Spinach', 'Potato']
|
||||||
{ label: "Estados Unidos", value: "US" },
|
// };
|
||||||
{ label: "Canadá", value: "CA" },
|
|
||||||
{ label: "Reino Unido", value: "UK" },
|
// return div({ class: 'flex flex-col gap-4 w-full' }, [
|
||||||
];
|
// Select({
|
||||||
|
// items: [
|
||||||
|
// { value: 'all', label: 'All items' },
|
||||||
|
// { value: 'fruits', label: 'Fruits' },
|
||||||
|
// { value: 'vegetables', label: 'Vegetables' }
|
||||||
|
// ],
|
||||||
|
// value: filterType,
|
||||||
|
// onchange: (e) => filterType(e.target.value)
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// Autocomplete({
|
||||||
|
// items: () => allItems[filterType()],
|
||||||
|
// value: selected,
|
||||||
|
// onselect: (value) => selected(value)
|
||||||
|
// })
|
||||||
|
// ]);
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const password = $("");
|
const password = $("");
|
||||||
|
const selected = $('');
|
||||||
|
|
||||||
|
const countries = [
|
||||||
|
'España', 'México', 'Argentina', 'Colombia', 'Chile',
|
||||||
|
{ label: 'Estados Unidos', value: 'US' },
|
||||||
|
{ label: 'Canadá', value: 'CA' },
|
||||||
|
];
|
||||||
|
|
||||||
// Lógica de validación sencilla
|
// Lógica de validación sencilla
|
||||||
const requirements = [
|
const requirements = [
|
||||||
{ label: "Mínimo 8 caracteres", check: () => password().length >= 8 },
|
{ label: "Mínimo 8 caracteres", check: () => password().length >= 8 },
|
||||||
{ label: "Incluye un número", check: () => /\d/.test(password()) },
|
{ label: "Incluye un número", check: () => /\d/.test(password()) },
|
||||||
{ label: "Símbolo especial", check: () => /[^A-Za-z0-9]/.test(password()) }
|
{ label: "Símbolo especial", check: () => /[^A-Za-z0-9]/.test(password()) },
|
||||||
];
|
];
|
||||||
|
const paisSelected = $("");
|
||||||
return div({ class: "p-10 max-w-md" }, [
|
return div({ class: "p-10 max-w-md" }, [
|
||||||
|
Autocomplete({
|
||||||
|
label: 'País',
|
||||||
|
float: true,
|
||||||
|
placeholder: 'Escribe para buscar...',
|
||||||
|
items: countries,
|
||||||
|
value: selected,
|
||||||
|
onselect: (item) => console.log('Seleccionado:', item),
|
||||||
|
}),
|
||||||
|
|
||||||
|
h('p', { class: 'text-sm opacity-70' }, () =>
|
||||||
|
`Valor actual: ${selected() || 'ninguno'}`
|
||||||
|
),
|
||||||
Input({
|
Input({
|
||||||
type: "password",
|
type: "password",
|
||||||
value: password,
|
value: password,
|
||||||
class: "input-warning",
|
rule: "[0-9]*",
|
||||||
|
hint: "Debes escribir numeros del 0 al 9",
|
||||||
oninput: (e) => password(e.target.value),
|
oninput: (e) => password(e.target.value),
|
||||||
label: "Pass",
|
label: "Pass",
|
||||||
float: true,
|
float: true,
|
||||||
left: span({ class: "icon-[lucide--lock] mr-2" }),
|
left: span({ class: "icon-[lucide--lock] mr-2" }),
|
||||||
// El contenido que se animará con fx() dentro del Input
|
// El contenido que se animará con fx() dentro del Input
|
||||||
content: div({ class: "mt-2 p-4 bg-base-200 rounded-lg shadow-xl border border-base-300" }, [
|
content: div(
|
||||||
span({ class: "text-xs font-bold uppercase opacity-50" }, "Seguridad:"),
|
{
|
||||||
ul({ class: "mt-2 space-y-1" },
|
class:
|
||||||
requirements.map(req => h('li', {
|
"mt-2 p-4 bg-base-200 rounded-lg shadow-xl border border-base-300",
|
||||||
class: () => `flex items-center text-sm ${req.check() ? 'text-success' : 'text-error opacity-50'}`
|
},
|
||||||
}, [
|
[
|
||||||
span({ class: () => `mr-2 ${req.check() ? 'icon-[lucide--check]' : 'icon-[lucide--x]'}` }),
|
span(
|
||||||
req.label
|
{ class: "text-xs font-bold uppercase opacity-50" },
|
||||||
]))
|
"Seguridad:",
|
||||||
)
|
),
|
||||||
])
|
ul(
|
||||||
})
|
{ class: "mt-2 space-y-1" },
|
||||||
|
requirements.map((req) =>
|
||||||
|
h(
|
||||||
|
"li",
|
||||||
|
{
|
||||||
|
class: () =>
|
||||||
|
`flex items-center text-sm ${req.check() ? "text-success" : "text-error opacity-50"}`,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
span({
|
||||||
|
class: () =>
|
||||||
|
`mr-2 ${req.check() ? "icon-[lucide--check]" : "icon-[lucide--x]"}`,
|
||||||
|
}),
|
||||||
|
req.label,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
100
docs/demo_display.md
Normal file
100
docs/demo_display.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Display
|
||||||
|
|
||||||
|
## Badge
|
||||||
|
<div id="demo-badge"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Badge({ class: 'badge-primary' }, 'New'),
|
||||||
|
'#demo-badge'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Indicator
|
||||||
|
<div id="demo-indicator"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Indicator({ value: '5' },
|
||||||
|
Button({ class: 'btn btn-sm' }, 'Notifications')
|
||||||
|
),
|
||||||
|
'#demo-indicator'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
<div id="demo-stack"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Stack({ class: 'w-32 h-20' }, [
|
||||||
|
div({ class: 'bg-primary text-primary-content p-2' }, 'Top'),
|
||||||
|
div({ class: 'bg-secondary text-secondary-content p-2' }, 'Bottom')
|
||||||
|
]),
|
||||||
|
'#demo-stack'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stat
|
||||||
|
<div id="demo-stat"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Stat is a simple wrapper for the DaisyUI stat component
|
||||||
|
const Stat = (p, c) => div({ ...p, class: cls('stat', p.class) }, c)
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'stats shadow' },
|
||||||
|
Stat({ class: 'stat' }, [
|
||||||
|
div({ class: 'stat-title' }, 'Total Downloads'),
|
||||||
|
div({ class: 'stat-value' }, '12.5K'),
|
||||||
|
div({ class: 'stat-desc' }, '21% more than last month')
|
||||||
|
])
|
||||||
|
),
|
||||||
|
'#demo-stat'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Table
|
||||||
|
<div id="demo-table"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const users = [
|
||||||
|
{ id: 1, name: 'Alice', role: 'Admin' },
|
||||||
|
{ id: 2, name: 'Bob', role: 'Editor' },
|
||||||
|
{ id: 3, name: 'Charlie', role: 'Viewer' }
|
||||||
|
];
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Table({
|
||||||
|
items: users,
|
||||||
|
columns: [
|
||||||
|
{ key: 'name', label: 'Name' },
|
||||||
|
{ key: 'role', label: 'Role' },
|
||||||
|
{ render: (it) => Button({ class: 'btn-xs' }, 'Edit') }
|
||||||
|
],
|
||||||
|
class: 'table-zebra'
|
||||||
|
}),
|
||||||
|
'#demo-table'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
<div id="demo-timeline"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Timeline({ vertical: true }, [
|
||||||
|
li({}, [
|
||||||
|
div({ class: 'timeline-start' }, '2024'),
|
||||||
|
div({ class: 'timeline-middle' }, span({ class: 'icon-[lucide--check]' })),
|
||||||
|
div({ class: 'timeline-end timeline-box' }, 'Project started')
|
||||||
|
]),
|
||||||
|
li({}, [
|
||||||
|
div({ class: 'timeline-start' }, '2025'),
|
||||||
|
div({ class: 'timeline-middle' }, span({ class: 'icon-[lucide--clock]' })),
|
||||||
|
div({ class: 'timeline-end timeline-box' }, 'First prototype')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-timeline'
|
||||||
|
);
|
||||||
|
```
|
||||||
19
docs/demo_editor.md
Normal file
19
docs/demo_editor.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Editor
|
||||||
|
<div id="demo-editor"></div>
|
||||||
|
|
||||||
|
<div id="demo-editor-output" class="mt-4 p-4 border border-base-300 rounded-box bg-base-200"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const content = $('<p><strong>Hello</strong> world!</p>');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex flex-col gap-4' }, [
|
||||||
|
Editor({
|
||||||
|
value: content,
|
||||||
|
placeholder: 'Escribe tu historia…',
|
||||||
|
class: 'max-w-2xl'
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
'#demo-editor'
|
||||||
|
);
|
||||||
|
```
|
||||||
181
docs/demo_forms.md
Normal file
181
docs/demo_forms.md
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# Forms
|
||||||
|
|
||||||
|
## Autocomplete
|
||||||
|
<div id="demo-autocomplete"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const selected = $('');
|
||||||
|
const items = ['Apple', 'Banana', 'Orange', 'Mango', 'Carrot', 'Broccoli', 'Spinach', 'Potato'];
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Autocomplete({
|
||||||
|
items,
|
||||||
|
value: selected,
|
||||||
|
placeholder: 'Search...',
|
||||||
|
onselect: (val) => console.log('Selected:', val)
|
||||||
|
}),
|
||||||
|
'#demo-autocomplete'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Button
|
||||||
|
<div id="demo-button"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const count = $(0);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Button({
|
||||||
|
class: 'btn-primary',
|
||||||
|
onclick: () => count(count() + 1)
|
||||||
|
}, () => `Clicked ${count()} times`),
|
||||||
|
'#demo-button'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Checkbox
|
||||||
|
<div id="demo-checkbox"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const checked = $(false);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex items-center gap-2' }, [
|
||||||
|
Checkbox({
|
||||||
|
checked,
|
||||||
|
onchange: (e) => checked(e.target.checked),
|
||||||
|
}),
|
||||||
|
span({}, 'Accept terms')
|
||||||
|
]),
|
||||||
|
'#demo-checkbox'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Colorpicker
|
||||||
|
<div id="demo-colorpicker"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const color = $('');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Colorpicker({
|
||||||
|
value: color,
|
||||||
|
label: 'Pick a color',
|
||||||
|
onchange: (c) => console.log('Color:', c)
|
||||||
|
}),
|
||||||
|
'#demo-colorpicker'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Datepicker
|
||||||
|
<div id="demo-datepicker"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const date = $('');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Datepicker({
|
||||||
|
value: date,
|
||||||
|
placeholder: 'Select date',
|
||||||
|
onChange: (val) => console.log('Date:', val)
|
||||||
|
}),
|
||||||
|
'#demo-datepicker'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Input
|
||||||
|
<div id="demo-input"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const text = $('');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Input({
|
||||||
|
type: 'text',
|
||||||
|
label: 'Username',
|
||||||
|
float: true,
|
||||||
|
value: text,
|
||||||
|
left: Icon('icon-[lucide--user]')
|
||||||
|
}),
|
||||||
|
'#demo-input'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Radio
|
||||||
|
<div id="demo-radio"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const option = $('');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex gap-2' }, [
|
||||||
|
Radio({ name: 'option', value: 'a', checked: () => option() === 'a', onchange: () => option('a') }),
|
||||||
|
Radio({ name: 'option', value: 'b', checked: () => option() === 'b', onchange: () => option('b') }),
|
||||||
|
]),
|
||||||
|
'#demo-radio'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Range
|
||||||
|
<div id="demo-range"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const rangeValue = $(50);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex flex-col gap-2' }, [
|
||||||
|
Range({ min: 0, max: 100, value: rangeValue, oninput: (e) => rangeValue(+e.target.value) }),
|
||||||
|
span({}, () => `Value: ${rangeValue()}`)
|
||||||
|
]),
|
||||||
|
'#demo-range'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rating
|
||||||
|
<div id="demo-rating"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const stars = $('');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Rating({
|
||||||
|
value: stars,
|
||||||
|
count: 5,
|
||||||
|
mask: 'mask-star',
|
||||||
|
onchange: (v) => console.log('Rated:', v)
|
||||||
|
}),
|
||||||
|
'#demo-rating'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Select
|
||||||
|
<div id="demo-select"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const choice = $('');
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Select({
|
||||||
|
items: ['Option 1', 'Option 2', 'Option 3'],
|
||||||
|
placeholder: 'Choose...',
|
||||||
|
value: choice,
|
||||||
|
}),
|
||||||
|
'#demo-select'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Swap
|
||||||
|
<div id="demo-swap"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const swapOn = $(false);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
Swap({
|
||||||
|
value: swapOn,
|
||||||
|
on: span({ class: 'text-success' }, 'ON'),
|
||||||
|
off: span({ class: 'text-error' }, 'OFF'),
|
||||||
|
}),
|
||||||
|
'#demo-swap'
|
||||||
|
);
|
||||||
|
```
|
||||||
201
docs/demo_layout.md
Normal file
201
docs/demo_layout.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# Layout
|
||||||
|
|
||||||
|
## Accordion
|
||||||
|
<div id="demo-accordion"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const accItems = $([
|
||||||
|
{ title: 'What is SigPro?', content: 'A lightweight UI library built on DaisyUI and a fine‑grained reactivity system.' },
|
||||||
|
{ title: 'Why use it?', content: 'No build step, minimal boilerplate, and all components are just JavaScript functions.' },
|
||||||
|
{ title: 'Browser support?', content: 'All modern browsers that support ES2020+' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex flex-col gap-8' }, [
|
||||||
|
// Example 1: radio type with arrow variant
|
||||||
|
Accordion({
|
||||||
|
variant: 'arrow',
|
||||||
|
items: [
|
||||||
|
{ title: 'Radio with arrow', content: 'This uses collapse‑arrow' },
|
||||||
|
{ title: 'Open by default', content: 'This one is open', open: true },
|
||||||
|
'Simple string item (no content)'
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
// Example 2: details type with plus variant
|
||||||
|
Accordion({
|
||||||
|
type: 'details',
|
||||||
|
variant: 'plus',
|
||||||
|
items: [
|
||||||
|
{ title: 'Details with plus', content: 'Uses the native <details> element' },
|
||||||
|
{ title: 'Another detail', content: 'Accordion style but with plus icon' }
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
// Example 3: reactive items (signal)
|
||||||
|
Accordion({ items: accItems })
|
||||||
|
]),
|
||||||
|
'#demo-accordion'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Drawer
|
||||||
|
<div id="demo-drawer"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const drawerOpen = $(false);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({}, [
|
||||||
|
Button({ class: 'btn', onclick: () => drawerOpen(true) }, 'Open Drawer'),
|
||||||
|
Drawer({ open: drawerOpen, side: Menu({ items: [
|
||||||
|
{ label: 'Dashboard', onclick: () => drawerOpen(false) },
|
||||||
|
{ label: 'Settings' },
|
||||||
|
{ label: 'Help' }
|
||||||
|
]}) }, [
|
||||||
|
div({ class: 'p-4' }, [
|
||||||
|
h3({ class: 'text-lg font-bold' }, 'Main Content'),
|
||||||
|
p({}, 'This is the main page. Click the button above to open the drawer.')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-drawer'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dropdown
|
||||||
|
<div id="demo-dropdown"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex gap-4' }, [
|
||||||
|
// Example 1: automatic items
|
||||||
|
Dropdown({
|
||||||
|
trigger: 'Options',
|
||||||
|
items: [
|
||||||
|
{ label: 'Edit', onclick: () => console.log('Edit') },
|
||||||
|
{ label: 'Delete', onclick: () => console.log('Delete') },
|
||||||
|
{ label: 'Archive' }
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
// Example 2: custom children (manual)
|
||||||
|
Dropdown({ trigger: 'More' }, [
|
||||||
|
h('ul', { class: 'menu dropdown-content bg-base-100 rounded-box w-40 p-2 shadow' }, [
|
||||||
|
li({}, a({}, 'Profile')),
|
||||||
|
li({}, a({}, 'Logout'))
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-dropdown'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fab
|
||||||
|
<div id="demo-fab"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex gap-4' }, [
|
||||||
|
Fab({ class: 'fab-bottom-left' }, span({ class: 'icon-[lucide--plus]' })),
|
||||||
|
Fab({ class: 'fab-top-right', style: 'position:relative' }, span({ class: 'icon-[lucide--settings]' }))
|
||||||
|
]),
|
||||||
|
'#demo-fab'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fieldset
|
||||||
|
<div id="demo-fieldset"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex gap-4' }, [
|
||||||
|
Fieldset({ legend: 'Personal Info' }, [
|
||||||
|
Input({ label: 'Name', float: true, value: $('') }),
|
||||||
|
Select({ label: 'Country', float: true, items: ['Spain', 'France', 'Italy'], value: $('') })
|
||||||
|
]),
|
||||||
|
Fieldset({ class: 'bg-base-200 p-4 rounded-box' }, [
|
||||||
|
div({}, 'Any content without legend')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-fieldset'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
<div id="demo-menu"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const menuItems = $([
|
||||||
|
{ label: 'Home' },
|
||||||
|
{ label: 'Products', children: [
|
||||||
|
{ label: 'Laptops' },
|
||||||
|
{ label: 'Phones' }
|
||||||
|
]},
|
||||||
|
{ label: 'About', onclick: () => console.log('About clicked') }
|
||||||
|
]);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex gap-4' }, [
|
||||||
|
// Example 1: automatic from items (signal)
|
||||||
|
Menu({ items: menuItems, class: 'menu-vertical' }),
|
||||||
|
// Example 2: manual children
|
||||||
|
Menu({ class: 'menu-horizontal bg-base-200 rounded-box' }, [
|
||||||
|
li({}, a({}, 'One')),
|
||||||
|
li({}, a({}, 'Two'))
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-menu'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Navbar
|
||||||
|
<div id="demo-navbar"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Navbar({ class: 'bg-base-200 rounded-box' }, [
|
||||||
|
div({ class: 'flex-1' },
|
||||||
|
a({ class: 'btn btn-ghost text-xl' }, 'SigPro')
|
||||||
|
),
|
||||||
|
div({ class: 'flex-none gap-2' }, [
|
||||||
|
Button({ class: 'btn btn-ghost' }, 'Login'),
|
||||||
|
Button({ class: 'btn btn-primary' }, 'Sign up')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-navbar'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tabs
|
||||||
|
<div id="demo-tabs"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const activeTab = $(0);
|
||||||
|
const tabsData = $([
|
||||||
|
{ label: 'Tab A', content: 'Content of tab A' },
|
||||||
|
{ label: 'Tab B', content: 'Content of tab B', closable: true },
|
||||||
|
{ label: 'Tab C', content: 'Content of tab C' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex flex-col gap-4' }, [
|
||||||
|
// Example 1: reactive tabs with closable
|
||||||
|
Tabs({
|
||||||
|
items: tabsData,
|
||||||
|
activeIndex: activeTab,
|
||||||
|
class: "tabs-box",
|
||||||
|
onClose: (idx) => {
|
||||||
|
const newTabs = tabsData().filter((_, i) => i !== idx);
|
||||||
|
tabsData(newTabs);
|
||||||
|
if (activeTab() >= newTabs.length) activeTab(Math.max(0, newTabs.length - 1));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// Example 2: manual tabs with custom content
|
||||||
|
Tabs({}, [
|
||||||
|
a({ class: () => `tab ${activeTab() === 0 ? 'tab-active' : ''}`, onclick: () => activeTab(0) }, 'Manual A'),
|
||||||
|
a({ class: () => `tab ${activeTab() === 1 ? 'tab-active' : ''}`, onclick: () => activeTab(1) }, 'Manual B'),
|
||||||
|
div({ class: 'tab-content', style: () => `display:${activeTab() === 0 ? 'block' : 'none'}` }, 'Content for manual A'),
|
||||||
|
div({ class: 'tab-content', style: () => `display:${activeTab() === 1 ? 'block' : 'none'}` }, 'Content for manual B')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-tabs'
|
||||||
|
);
|
||||||
|
```
|
||||||
84
docs/demo_overlay.md
Normal file
84
docs/demo_overlay.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Overlays
|
||||||
|
|
||||||
|
## Alert
|
||||||
|
<div id="demo-alert"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Alert({ class: 'alert-success' }, [
|
||||||
|
span({}, 'Operation completed successfully!'),
|
||||||
|
Button({ class: 'btn-sm' }, 'Undo')
|
||||||
|
]),
|
||||||
|
'#demo-alert'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modal
|
||||||
|
<div id="demo-modal"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
const modalOpen = $(false);
|
||||||
|
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex flex-col items-start gap-2' }, [
|
||||||
|
Button({
|
||||||
|
class: 'btn',
|
||||||
|
onclick: () => modalOpen(true)
|
||||||
|
}, 'Abrir modal'),
|
||||||
|
Modal({
|
||||||
|
open: modalOpen,
|
||||||
|
title: 'Congratulations!',
|
||||||
|
backdrop: true,
|
||||||
|
actions: [
|
||||||
|
form({ method: 'dialog' },
|
||||||
|
Button({ class: 'btn', onclick: () => modalOpen(false) }, 'Close')
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
p({}, 'You have successfully created a reactive DaisyUI modal with SigPro.')
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
'#demo-modal'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Toast
|
||||||
|
<div id="demo-toast"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
div({ class: 'flex flex-wrap gap-2' }, [
|
||||||
|
Button({ class: 'btn', onclick: () => Toast('File saved!') }, 'Simple'),
|
||||||
|
Button({ class: 'btn', onclick: () => Toast('Error', 'alert-error', 5000) }, 'Error (5s)'),
|
||||||
|
Button({ class: 'btn', onclick: () => Toast(
|
||||||
|
div({ class: 'flex items-center gap-2' }, [
|
||||||
|
span({ class: 'icon-[lucide--check] text-lg' }),
|
||||||
|
span({}, 'Report generated successfully')
|
||||||
|
]),
|
||||||
|
'alert-success'
|
||||||
|
) }, 'With icon'),
|
||||||
|
Button({ class: 'btn', onclick: () => Toast(
|
||||||
|
h('div', { class: 'flex flex-col' }, [
|
||||||
|
h('strong', {}, '¡ATTENTION!'),
|
||||||
|
h('span', {}, 'Error saving!'),
|
||||||
|
h('button', { class: 'btn btn-xs mt-1', onclick: () => console.log('retry') }, 'Retry')
|
||||||
|
]),
|
||||||
|
'alert-warning',
|
||||||
|
7000
|
||||||
|
) }, 'Complex')
|
||||||
|
]),
|
||||||
|
'#demo-toast'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tooltip
|
||||||
|
<div id="demo-tooltip"></div>
|
||||||
|
|
||||||
|
```js
|
||||||
|
mount(
|
||||||
|
Tooltip({ tip: 'Click to save', class: 'tooltip-right' },
|
||||||
|
Button({ class: 'btn' }, 'Save')
|
||||||
|
),
|
||||||
|
'#demo-tooltip'
|
||||||
|
);
|
||||||
|
```
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
repo: "",
|
repo: "",
|
||||||
loadSidebar: true,
|
loadSidebar: true,
|
||||||
sidebarDisplayLevel: 1,
|
sidebarDisplayLevel: 1,
|
||||||
|
subMaxLevel: 3,
|
||||||
executeScript: true,
|
executeScript: true,
|
||||||
copyCode: {
|
copyCode: {
|
||||||
buttonText:
|
buttonText:
|
||||||
@@ -38,26 +39,24 @@
|
|||||||
successText:
|
successText:
|
||||||
'<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg>',
|
'<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg>',
|
||||||
},
|
},
|
||||||
|
search: {
|
||||||
|
placeholder: "Type to search",
|
||||||
|
noData: "No Results!",
|
||||||
|
depth: 3,
|
||||||
|
hideOtherSidebarContent: true,
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
function (hook, vm) {
|
function (hook, vm) {
|
||||||
hook.doneEach(function () {
|
hook.doneEach(function () {
|
||||||
// Seleccionamos solo los bloques marcados con ```js
|
|
||||||
const codeBlocks = document.querySelectorAll(
|
const codeBlocks = document.querySelectorAll(
|
||||||
'pre[data-lang="js"] code',
|
'pre[data-lang="js"] code',
|
||||||
);
|
);
|
||||||
|
|
||||||
codeBlocks.forEach((code) => {
|
codeBlocks.forEach((code) => {
|
||||||
try {
|
try {
|
||||||
// Usamos un bloque anónimo para evitar colisiones de variables const/let
|
const scriptContent = `(function() { ${code.innerText} })();`;
|
||||||
// si el usuario ejecuta el mismo código varias veces.
|
|
||||||
const scriptContent = `(function() {
|
|
||||||
${code.innerText}
|
|
||||||
})();`;
|
|
||||||
|
|
||||||
const runDemo = new Function(scriptContent);
|
const runDemo = new Function(scriptContent);
|
||||||
runDemo();
|
runDemo();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Un error común es que el bloque de código esté vacío o mal formado
|
|
||||||
console.error("Error ejecutando demo de SigPro:", err);
|
console.error("Error ejecutando demo de SigPro:", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -66,7 +65,7 @@
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify@4.13.0/lib/docsify.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify@4.13.0/lib/docsify.min.js"></script>
|
||||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||||
<script src="./sigpro-ui.min.js"></script>
|
<script src="./sigpro-ui.min.js"></script>
|
||||||
|
|||||||
743
docs/quick.md
743
docs/quick.md
@@ -1,227 +1,610 @@
|
|||||||
# SigPro-UI Quick Reference
|
# SigPro Components – Quick Reference
|
||||||
|
|
||||||
**Status:** Active / WIP
|
All simple components use the pattern `ComponentName(props, children?)` and accept any additional HTML attributes.
|
||||||
|
|
||||||
|
## Accordion
|
||||||
|
`Accordion(props)`
|
||||||
|
|
||||||
## Global Initialization
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
```javascript
|
| `items` | array \| signal | `[]` | Array of items. Each item: `{ title, content?, open? }` or a string. |
|
||||||
import "sigpro-ui";
|
| `type` | `'radio'` \| `'details'` | `'radio'` | Collapse mode |
|
||||||
import "sigpro-ui/css";
|
| `variant` | string | `''` | `'arrow'` or `'plus'` for an icon variant |
|
||||||
|
| `name` | string | auto‑generated | Group name for radio inputs |
|
||||||
// All components (Button, Input, Table, Toast, etc.) are now globally available.
|
| `class` | string | - | Extra classes merged with `collapse` |
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Core Components
|
## Alert
|
||||||
|
`Alert(props, children)`
|
||||||
|
|
||||||
| Component | Purpose | Basic Example |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| **Button** | Styled button with DaisyUI | `button({ class: "btn-primary" }, "Submit")` |
|
| `class` | string | - | Extra classes merged with `alert` |
|
||||||
| **Input** | Reactive text field with validation | `input({ value: $name, validate: (v) => !v ? "Required" : "" })` |
|
|
||||||
| **Select** | Dropdown selection menu | `Select({ options: ["Admin", "User"], value: $role })` |
|
|
||||||
| **Checkbox** | Binary toggle (boolean) | `Checkbox({ label: "Active", checked: $isActive })` |
|
|
||||||
| **Table** | Data grid with column rendering | `Table({ items: $data, columns: [...] })` |
|
|
||||||
| **Modal** | Overlay dialog controlled by a Signal | `Modal({ open: $show, title: "Alert" }, "Message")` |
|
|
||||||
| **Badge** | Small status indicator or tag | `Badge({ class: "badge-outline" }, "Beta")` |
|
|
||||||
| **Alert** | Contextual notification | `Alert({ type: "info" }, "Update available")` |
|
|
||||||
| **Dropdown** | Contextual overlay menu | `Dropdown({ label: "Menu" }, [Link1, Link2])` |
|
|
||||||
| **Tabs** | Reactive tab-based navigation | `Tabs({ items: ["Home", "Settings"], active: $index })` |
|
|
||||||
| **Stat** | Statistical data block (KPIs) | `Stat({ label: "Sales", value: "$400" })` |
|
|
||||||
| **Toast** | Temporary floating notification | `Toast("Done!", "alert-success", 3000)` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Forms & Inputs
|
## Autocomplete
|
||||||
|
`Autocomplete(props)`
|
||||||
|
|
||||||
| Component | Description | Example |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| **Input** | Text input with floating label, validation, password toggle | `input({ label: "Email", type: "email", value: $email, validate: validateEmail })` |
|
| `items` | array \| signal | `[]` | Autocomplete items |
|
||||||
| **Select** | Dropdown selector | `Select({ label: "Role", options: ["Admin", "User"], value: $role })` |
|
| `value` | signal | - | Selected value |
|
||||||
| **Autocomplete** | Searchable dropdown with filtering | `autocomplete({ label: "Country", options: countryList, value: $country })` |
|
| `onselect` | function | - | Called when an item is selected |
|
||||||
| **Datepicker** | Date picker (single or range mode) | `Datepicker({ label: "Date", value: $date, range: false })` |
|
| `placeholder` | string | `'Buscar...'` | Input placeholder |
|
||||||
| **Colorpicker** | Visual color picker with palette | `Colorpicker({ label: "Theme", value: $color })` |
|
| `class` | string | - | Extra classes |
|
||||||
| **Checkbox** | Checkbox or toggle switch | `Checkbox({ label: "Remember me", value: $remember })` |
|
| `...props` | | | All other props are passed to the internal `Input` |
|
||||||
| **Radio** | Radio button | `Radio({ label: "Option 1", value: $selected, name: "group" })` |
|
|
||||||
| **Range** | Slider control | `Range({ label: "Volume", min: 0, max: 100, value: $volume })` |
|
|
||||||
| **Rating** | Star rating component | `Rating({ value: $stars, count: 5 })` |
|
|
||||||
| **Swap** | Toggle between two states (sun/moon) | `Swap({ on: "🌞", off: "🌙", value: $isDark })` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Input Validation
|
## Badge
|
||||||
|
`Badge(props, children)`
|
||||||
|
|
||||||
The `Input` component supports real-time validation via the `validate` prop:
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
```javascript
|
| `class` | string | - | Extra classes merged with `badge` |
|
||||||
const email = $('');
|
|
||||||
|
|
||||||
input({
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
icon: 'icon-[lucide--mail]',
|
|
||||||
validate: (value) => {
|
|
||||||
if (!value) return '';
|
|
||||||
if (!value.includes('@')) return 'Email must contain @';
|
|
||||||
if (!value.includes('.')) return 'Email must contain .';
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**How it works:**
|
|
||||||
- Returns `''` or `null` → no error
|
|
||||||
- Returns a string → shows error message and adds `input-error` class
|
|
||||||
- Validates on every keystroke
|
|
||||||
- No external state needed for error messages
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Data Display
|
## Button
|
||||||
|
`Button(props, children)`
|
||||||
|
|
||||||
| Component | Description | Example |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| **Table** | Reactive data grid with columns | `Table({ items: $users, columns: [{ label: "Name", key: "name" }] })` |
|
| `class` | string | - | Extra classes merged with `button` |
|
||||||
| **List** | Vertical list with custom rendering | `List({ items: $items, render: (item) => item.name })` |
|
| `disabled` | boolean | - | Standard HTML attribute |
|
||||||
| **Badge** | Small status indicators | `Badge({ class: "badge-primary" }, "New")` |
|
| *any* | | | All other standard `<button>` attributes |
|
||||||
| **Stat** | Statistical data blocks (KPIs) | `Stat({ label: "Total", value: "1.2k", desc: "Monthly" })` |
|
|
||||||
| **Timeline** | Vertical/horizontal timeline | `Timeline({ items: [{ title: "Step 1", detail: "Completed" }] })` |
|
|
||||||
| **Stack** | Stacked elements | `Stack({}, [Card1, Card2, Card3])` |
|
|
||||||
| **Indicator** | Badge on corner of element | `Indicator({ value: () => count() }, button(...))` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Feedback & Overlays
|
## Calendar
|
||||||
|
`Calendar(props)`
|
||||||
|
|
||||||
| Component | Description | Example |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| **Alert** | Inline contextual notification | `Alert({ type: "success" }, "Changes saved!")` |
|
| `value` | signal \| string \| object | - | Selected date(s) |
|
||||||
| **Modal** | Dialog overlay | `Modal({ open: $isOpen, title: "Confirm" }, "Are you sure?")` |
|
| `range` | signal \| boolean | `false` | Enable range mode |
|
||||||
| **Toast** | Floating notification (auto-stacking) | `Toast("Action completed", "alert-info", 3000)` |
|
| `hour` | boolean | `false` | Show hour sliders |
|
||||||
| **Tooltip** | Hover tooltip wrapper | `Tooltip({ tip: "Help text", ui: "tooltip-top" }, button(...))` |
|
| `onChange` | function | - | Called when selection changes |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Navigation & Layout
|
## Card
|
||||||
|
`Card(props, children)`
|
||||||
|
|
||||||
| Component | Description | Example |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| **Navbar** | Top navigation bar | `Navbar({}, [Logo, Menu, Avatar])` |
|
| `class` | string | - | Extra classes merged with `card` |
|
||||||
| **Menu** | Vertical navigation menu | `Menu({ items: [{ label: "Home", onclick: goHome }] })` |
|
|
||||||
| **Drawer** | Side drawer (off-canvas) | `Drawer({ id: "my-drawer", open: $isOpen, content: Main, side: SideMenu })` |
|
|
||||||
| **Tabs** | Content switching | `Tabs({ items: [{ label: "Tab1", content: Panel1 }] })` |
|
|
||||||
| **Accordion** | Collapsible sections | `Accordion({ title: "Details" }, "Hidden content")` |
|
|
||||||
| **Dropdown** | Contextual menus | `Dropdown({ label: "Options" }, [MenuLink("Edit")])` |
|
|
||||||
| **Fieldset** | Form grouping with legend | `Fieldset({ legend: "Personal Info" }, [input(...)])` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Interaction & Utilities
|
## CardActions
|
||||||
|
`CardActions(props, children)`
|
||||||
|
|
||||||
| Component | Description | Example |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| **Fab** | Floating Action Button with actions | `Fab({ icon: "+", actions: [{ label: "Add", onclick: add }] })` |
|
| `class` | string | - | Extra classes merged with `card-actions` |
|
||||||
| **Indicator** | Badge indicator wrapper | `Indicator({ value: () => unread() }, button(...))` |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Internationalization (i18n)
|
## CardBody
|
||||||
|
`CardBody(props, children)`
|
||||||
|
|
||||||
Built-in locale system with Spanish and English support.
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
```javascript
|
| `class` | string | - | Extra classes merged with `card-body` |
|
||||||
// Set global UI language
|
|
||||||
Locale("en");
|
|
||||||
|
|
||||||
// Get translated string (returns a reactive signal)
|
|
||||||
const closeText = tt("close"); // "Close" or "Cerrar"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Implementation Notes
|
## CardTitle
|
||||||
|
`CardTitle(props, children)`
|
||||||
|
|
||||||
1. **Atomic Reactivity**: Components accepting `value` or `checked` bind directly to **SigPro Signals**. Updates happen instantly without full page re-renders.
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
2. **DaisyUI v5**: Pass any daisyUI styling via the `class` attribute.
|
| `class` | string | - | Extra classes merged with `card-title` |
|
||||||
|
|
||||||
```javascript
|
|
||||||
button({ class: "btn-primary btn-sm rounded-full shadow-lg" }, "Click")
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Zero Imports (Global Mode)**: After calling `UI()`, all components are attached to `window`. No manual imports needed.
|
|
||||||
|
|
||||||
4. **Self-Cleaning**: Components internally manage `_cleanups` to destroy observers and event listeners when removed from the DOM.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Advanced Examples
|
## Carousel
|
||||||
|
`Carousel(props, children)`
|
||||||
|
|
||||||
### Reactive Form with Validation
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
```javascript
|
| `class` | string | - | Extra classes merged with `carousel` |
|
||||||
const name = $("");
|
|
||||||
|
|
||||||
input({
|
|
||||||
value: name,
|
|
||||||
placeholder: "Name",
|
|
||||||
validate: (value) => {
|
|
||||||
if (!value) return "Name is required";
|
|
||||||
if (value.length < 3) return "Name too short";
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
oninput: (e) => name(e.target.value)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Request Pattern
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const userId = $("123");
|
|
||||||
const userData = $(null);
|
|
||||||
|
|
||||||
watch(userId, async (id) => {
|
|
||||||
loading(true);
|
|
||||||
userData(await fetch(`/api/user/${id}`).then(r => r.json()));
|
|
||||||
loading(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// In template
|
|
||||||
when(() => userData(), () => Alert({ type: "success" }, userData()?.name))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Modal with Confirm Action
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const showModal = $(false);
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: showModal,
|
|
||||||
title: "Delete Item",
|
|
||||||
buttons: [
|
|
||||||
button({ class: "btn-error", onclick: () => { deleteItem(); showModal(false); } }, "Delete")
|
|
||||||
]
|
|
||||||
}, "Are you sure you want to delete this item?");
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Component Props Quick Reference
|
## CarouselItem
|
||||||
|
`CarouselItem(props, children)`
|
||||||
|
|
||||||
| Component | Key Props |
|
| Prop | Type | Default | Description |
|
||||||
| :--- | :--- |
|
|------|------|---------|-------------|
|
||||||
| `Button` | `class`, `disabled`, `loading`, `icon` |
|
| `class` | string | - | Extra classes merged with `carousel-item` |
|
||||||
| `Input` | `value`, `validate`, `type`, `placeholder`, `icon`, `disabled` |
|
|
||||||
| `Select` | `label`, `options`, `value`, `disabled` |
|
---
|
||||||
| `Modal` | `open`, `title`, `buttons` |
|
|
||||||
| `Table` | `items`, `columns`, `zebra`, `pinRows`, `empty` |
|
## Chat
|
||||||
| `Alert` | `type` (info/success/warning/error), `soft`, `actions` |
|
`Chat(props, children)`
|
||||||
| `Toast` | `message`, `type`, `duration` |
|
|
||||||
| `Datepicker` | `value`, `range`, `label`, `placeholder` |
|
| Prop | Type | Default | Description |
|
||||||
| `Autocomplete` | `options`, `value`, `onselect`, `label` |
|
|------|------|---------|-------------|
|
||||||
| `Indicator` | `value` (function that returns number/string) |
|
| `class` | string | - | Extra classes merged with `chat` |
|
||||||
| `Tooltip` | `tip`, `ui` (tooltip-top/bottom/left/right) |
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ChatBubble
|
||||||
|
`ChatBubble(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `chat-bubble` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ChatFooter
|
||||||
|
`ChatFooter(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `chat-footer` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ChatHeader
|
||||||
|
`ChatHeader(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `chat-header` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ChatImage
|
||||||
|
`ChatImage(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `chat-image avatar` |
|
||||||
|
| *(children)* | string \| node | - | If a string, used as `src` of an `<img>` inside a rounded `div`; otherwise passed as content. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checkbox
|
||||||
|
`Checkbox(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `checkbox` |
|
||||||
|
| `checked` | boolean \| signal | - | Checked state (can be a signal) |
|
||||||
|
| `onchange` | function | - | Change handler |
|
||||||
|
| *any* | | | All standard `<input>` attributes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Colorpicker
|
||||||
|
`Colorpicker(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `value` | signal \| string | `'#000000'` | Selected color |
|
||||||
|
| `label` | string | - | Optional label inside the trigger |
|
||||||
|
| `onchange` | function | - | Called when a color is picked (if `value` is not a signal) |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Datepicker
|
||||||
|
`Datepicker(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `value` | signal | - | Selected date(s) |
|
||||||
|
| `range` | signal \| boolean | `false` | Range mode |
|
||||||
|
| `hour` | boolean | `false` | Enable hour selection |
|
||||||
|
| `onChange` | function | - | Change handler (if `value` is not a signal) |
|
||||||
|
| `placeholder` | string | auto | Placeholder text |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Divider
|
||||||
|
`Divider(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `divider` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Drawer
|
||||||
|
`Drawer(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `open` | signal \| boolean | - | Drawer open state |
|
||||||
|
| `side` | node \| signal | - | Sidebar content (can be a signal) |
|
||||||
|
| `id` | string | auto‑generated | Drawer ID |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
**children** – Main content (`.drawer-content`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dropdown
|
||||||
|
`Dropdown(props, children?)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `trigger` | node | `'Dropdown'` | Summary content |
|
||||||
|
| `items` | array \| signal | - | Dropdown items: `{ label, onclick? }` |
|
||||||
|
| `class` | string | - | Extra classes merged with `dropdown` |
|
||||||
|
|
||||||
|
If `children` is provided, it replaces the auto‑generated menu.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fab
|
||||||
|
`Fab(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `fab` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fieldset
|
||||||
|
`Fieldset(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `legend` | string | - | Legend text (rendered as `<legend>`) |
|
||||||
|
| `class` | string | - | Extra classes merged with `fieldset` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fileinput
|
||||||
|
`Fileinput(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `max` | number | `2` | Max file size in MB |
|
||||||
|
| `accept` | string | `'*'` | Accepted file types |
|
||||||
|
| `onselect` | function | - | Called when files change (receives array) |
|
||||||
|
| `value` | signal | - | Alternative to `onselect` for signal binding |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Icon
|
||||||
|
`Icon(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | If the value starts with `icon-`, used as class; otherwise becomes content |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Indicator
|
||||||
|
`Indicator(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `value` | node | - | Content of the indicator badge |
|
||||||
|
| `class` | string | - | Extra classes merged with `indicator` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Input
|
||||||
|
`Input(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `label` | string | - | Label text |
|
||||||
|
| `float` | boolean | `false` | Floating label |
|
||||||
|
| `left` | node | - | Element on the left |
|
||||||
|
| `right` | node | - | Element on the right |
|
||||||
|
| `hint` | string | - | Validation hint |
|
||||||
|
| `content` | node \| function | - | Content shown when focused (can be a function receiving `isFocused`) |
|
||||||
|
| `rule` | string | - | Validation pattern |
|
||||||
|
| `type` | string | `'text'` | Input type (`text`, `password`, etc.) |
|
||||||
|
| `value` | signal \| string | - | Input value |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
| *any* | | | All other props are passed to the `<input>` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kbd
|
||||||
|
`Kbd(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `kbd` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LabelFloat
|
||||||
|
`LabelFloat(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `floating-label` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LabelInput
|
||||||
|
`LabelInput(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `input` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LabelSelect
|
||||||
|
`LabelSelect(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `select` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Loading
|
||||||
|
`Loading(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `loading loading-spinner` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
`Menu(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `items` | array \| signal | `[]` | Menu items: `{ label, href?, onclick?, children? }` |
|
||||||
|
| `keyFn` | function | `(it, idx) => it?.id ?? idx` | Key function for reactivity |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
If `children` is provided, manual mode.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Navbar
|
||||||
|
`Navbar(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `navbar` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
`Progress(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `progress` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Radial
|
||||||
|
`Radial(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `value` | number | `0` | Progress value (sets CSS `--value` and default text) |
|
||||||
|
| `style` | string | - | Additional inline styles appended to the defaults |
|
||||||
|
| `class` | string | - | Extra classes merged with `radial-progress` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Radio
|
||||||
|
`Radio(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `radio` |
|
||||||
|
| `checked` | boolean | - | Checked state |
|
||||||
|
| `onchange` | function | - | Change handler |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Range
|
||||||
|
`Range(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `range` |
|
||||||
|
| `min` | number | - | Standard range attribute |
|
||||||
|
| `max` | number | - | Standard range attribute |
|
||||||
|
| `value` | number | - | Standard range attribute |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rating
|
||||||
|
`Rating(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `value` | number \| signal | - | Selected rating |
|
||||||
|
| `count` | number | `5` | Number of stars |
|
||||||
|
| `mask` | string | `'mask-star'` | Shape class (e.g., `'mask-heart'`) |
|
||||||
|
| `onchange` | function | - | Called when a star is clicked |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Select
|
||||||
|
`Select(props, children?)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `items` | array \| signal | - | Options list |
|
||||||
|
| `placeholder` | string | - | Placeholder option text |
|
||||||
|
| `placeholderDisabled` | boolean | `true` | Whether the placeholder is disabled |
|
||||||
|
| `label` | string | - | Label text |
|
||||||
|
| `float` | boolean | `false` | Floating label style |
|
||||||
|
| `left` | node | - | Element left of select |
|
||||||
|
| `right` | node | - | Element right of select |
|
||||||
|
| `hint` | string | - | Validation hint |
|
||||||
|
| `value` | signal \| string | - | Selected value |
|
||||||
|
| `keyFn` | function | auto | Key function for reactivity |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
If `children` is provided, manual mode.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Skeleton
|
||||||
|
`Skeleton(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `skeleton` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SkeletonText
|
||||||
|
`SkeletonText(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `skeleton skeleton-text` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
`Stack(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `stack` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step
|
||||||
|
`Step(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `dataContent` | string | - | Value for the `data-content` attribute |
|
||||||
|
| `class` | string | - | Extra classes merged with `step` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
`Steps(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `steps` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Swap
|
||||||
|
`Swap(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `value` | signal \| boolean | - | Checked state (can be a signal) |
|
||||||
|
| `on` | node | - | Content for the “on” state |
|
||||||
|
| `off` | node | - | Content for the “off” state |
|
||||||
|
| `class` | string | - | Extra classes merged with `swap` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table
|
||||||
|
`Table(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `items` | array \| signal | `[]` | Data rows |
|
||||||
|
| `columns` | array | `[]` | Column definitions: `{ key?, label?, class?, render? }` |
|
||||||
|
| `header` | boolean | `true` | Show header row |
|
||||||
|
| `keyFn` | function | `(item, idx) => item?.id ?? idx` | Key function for reactivity |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
If `children` is provided, manual mode (ignores `items`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tabs
|
||||||
|
`Tabs(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `items` | array \| signal | `[]` | Tab items: `{ label, content, closable?, class?, contentClass?, onclick? }` |
|
||||||
|
| `activeIndex` | signal | - | Index of the active tab |
|
||||||
|
| `onClose` | function | - | Called when a closable tab is closed: `(idx, item)` |
|
||||||
|
| `class` | string | - | Extra classes |
|
||||||
|
|
||||||
|
If `children` is provided, manual mode.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TextRotate
|
||||||
|
`TextRotate(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `words` | array \| string | - | Array of words or comma‑separated string |
|
||||||
|
| `class` | string | - | Extra classes merged with `text-rotate` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Textarea
|
||||||
|
`Textarea(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `textarea` |
|
||||||
|
| *any* | | | All standard `<textarea>` attributes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
`Timeline(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `vertical` | boolean | `true` | If `false`, horizontal layout |
|
||||||
|
| `compact` | boolean | `false` | Compact mode |
|
||||||
|
| `class` | string | - | Extra classes merged with `timeline` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Toast
|
||||||
|
`Toast(message, type?, duration?)`
|
||||||
|
|
||||||
|
| Parameter | Type | Default | Description |
|
||||||
|
|-----------|------|---------|-------------|
|
||||||
|
| `message` | string \| signal | - | Toast text |
|
||||||
|
| `type` | string | `'alert-success'` | Alert type class |
|
||||||
|
| `duration` | number | `3500` | Auto‑close time in ms (0 for manual close) |
|
||||||
|
|
||||||
|
Returns a `close` function.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Toggle
|
||||||
|
`Toggle(props)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `class` | string | - | Extra classes merged with `toggle` |
|
||||||
|
| `checked` | boolean \| signal | - | Checked state |
|
||||||
|
| `onchange` | function | - | Change handler |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tooltip
|
||||||
|
`Tooltip(props, children)`
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| `tip` | string | - | Tooltip text (sets `data-tip`) |
|
||||||
|
| `class` | string | - | Extra classes merged with `tooltip` |
|
||||||
2
docs/sigpro-ui.min.css
vendored
2
docs/sigpro-ui.min.css
vendored
File diff suppressed because one or more lines are too long
2
docs/sigpro-ui.min.js
vendored
2
docs/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
172
index.js
172
index.js
@@ -1,92 +1,96 @@
|
|||||||
import * as AccordionModule from './components/accordion.js';
|
import * as AllModule from './components/All.js';
|
||||||
import * as AlertModule from './components/alert.js';
|
import * as EditorModule from './components/Editor.js';
|
||||||
import * as AutocompleteModule from './components/Autocomplete.js';
|
// import * as AccordionModule from './components/accordion.js';
|
||||||
import * as BadgeModule from './components/badge.js';
|
// import * as AlertModule from './components/alert.js';
|
||||||
import * as ButtonModule from './components/button.js';
|
// import * as AutocompleteModule from './components/discarted/Autocomplete.js';
|
||||||
import * as CalendarModule from './components/Calendar.js';
|
// import * as BadgeModule from './components/badge.js';
|
||||||
import * as CardModule from './components/card.js';
|
// import * as ButtonModule from './components/button.js';
|
||||||
import * as CarouselModule from './components/carousel.js';
|
// import * as CalendarModule from './components/Calendar.js';
|
||||||
import * as ChatModule from './components/chat.js';
|
// import * as CardModule from './components/card.js';
|
||||||
import * as CheckboxModule from './components/checkbox.js';
|
// import * as CarouselModule from './components/carousel.js';
|
||||||
import * as ColorpickerModule from './components/colorpicker.js';
|
// import * as ChatModule from './components/chat.js';
|
||||||
import * as DatepickerModule from './components/Datepicker.js';
|
// import * as CheckboxModule from './components/checkbox.js';
|
||||||
import * as DrawerModule from './components/drawer.js';
|
// import * as ColorpickerModule from './components/Colorpicker.js';
|
||||||
import * as DropdownModule from './components/dropdown.js';
|
// import * as DatepickerModule from './components/Datepicker.js';
|
||||||
import * as FabModule from './components/fab.js';
|
// import * as DrawerModule from './components/drawer.js';
|
||||||
import * as FieldsetModule from './components/fieldset.js';
|
// import * as DropdownModule from './components/dropdown.js';
|
||||||
import * as FileinputModule from './components/fileinput.js';
|
// import * as FabModule from './components/fab.js';
|
||||||
import * as IconModule from './components/icon.js';
|
// import * as FieldsetModule from './components/fieldset.js';
|
||||||
import * as IndicatorModule from './components/indicator.js';
|
// import * as FileinputModule from './components/fileinput.js';
|
||||||
import * as InputModule from './components/Input.js';
|
// import * as IconModule from './components/icon.js';
|
||||||
import * as KdbModule from './components/kbd.js';
|
// import * as IndicatorModule from './components/indicator.js';
|
||||||
|
// import * as InputModule from './components/Input.js';
|
||||||
|
// import * as KdbModule from './components/kbd.js';
|
||||||
// import * as ListModule from './components/List.js';
|
// import * as ListModule from './components/List.js';
|
||||||
import * as LoadingModule from './components/loading.js';
|
// import * as LoadingModule from './components/loading.js';
|
||||||
import * as MenuModule from './components/menu.js';
|
// import * as MenuModule from './components/menu.js';
|
||||||
import * as ModalModule from './components/modal.js';
|
// import * as ModalModule from './components/modal.js';
|
||||||
import * as NavbarModule from './components/navbar.js';
|
// import * as NavbarModule from './components/navbar.js';
|
||||||
import * as RadialModule from './components/radial.js';
|
// import * as RadialModule from './components/radial.js';
|
||||||
import * as RadioModule from './components/radio.js';
|
// import * as RadioModule from './components/radio.js';
|
||||||
import * as RangeModule from './components/range.js';
|
// import * as RangeModule from './components/range.js';
|
||||||
import * as RatingModule from './components/rating.js';
|
// import * as RatingModule from './components/rating.js';
|
||||||
import * as SkeletonModule from './components/skeleton.js';
|
// import * as SkeletonModule from './components/skeleton.js';
|
||||||
import * as SelectModule from './components/select.js';
|
// import * as SelectModule from './components/discarted/Select.js';
|
||||||
import * as StackModule from './components/stack.js';
|
// import * as StackModule from './components/stack.js';
|
||||||
import * as StatModule from './components/stat.js';
|
// import * as StatModule from './components/stat.js';
|
||||||
import * as StepsModule from './components/stat.js';
|
// import * as StepsModule from './components/stat.js';
|
||||||
import * as SwapModule from './components/swap.js';
|
// import * as SwapModule from './components/swap.js';
|
||||||
import * as TableModule from './components/table.js';
|
// import * as TableModule from './components/table.js';
|
||||||
import * as TabsModule from './components/tabs.js';
|
// import * as TabsModule from './components/tabs.js';
|
||||||
import * as TextareaModule from './components/textarea.js';
|
// import * as TextareaModule from './components/textarea.js';
|
||||||
import * as TextrotateModule from './components/textrotate.js';
|
// import * as TextrotateModule from './components/textrotate.js';
|
||||||
import * as TimelineModule from './components/timeline.js';
|
// import * as TimelineModule from './components/timeline.js';
|
||||||
import * as ToastModule from './components/toast.js';
|
// import * as ToastModule from './components/toast.js';
|
||||||
import * as TooltipModule from './components/tooltip.js';
|
// import * as TooltipModule from './components/tooltip.js';
|
||||||
import { Locale, tt } from './utils.js';
|
import { Locale, tt } from './utils.js';
|
||||||
|
|
||||||
export const Components = {
|
export const Components = {
|
||||||
...AccordionModule,
|
...AllModule,
|
||||||
...AlertModule,
|
...EditorModule,
|
||||||
...AutocompleteModule,
|
// ...AccordionModule,
|
||||||
...BadgeModule,
|
// ...AlertModule,
|
||||||
...ButtonModule,
|
// ...AutocompleteModule,
|
||||||
...CalendarModule,
|
// ...BadgeModule,
|
||||||
...CardModule,
|
// ...ButtonModule,
|
||||||
...CarouselModule,
|
// ...CalendarModule,
|
||||||
...ChatModule,
|
// ...CardModule,
|
||||||
...CheckboxModule,
|
// ...CarouselModule,
|
||||||
...ColorpickerModule,
|
// ...ChatModule,
|
||||||
...DatepickerModule,
|
// ...CheckboxModule,
|
||||||
...DrawerModule,
|
// ...ColorpickerModule,
|
||||||
...DropdownModule,
|
// ...DatepickerModule,
|
||||||
...FabModule,
|
// ...DrawerModule,
|
||||||
...FieldsetModule,
|
// ...DropdownModule,
|
||||||
...FileinputModule,
|
// ...FabModule,
|
||||||
...IconModule,
|
// ...FieldsetModule,
|
||||||
...IndicatorModule,
|
// ...FileinputModule,
|
||||||
...InputModule,
|
// ...IconModule,
|
||||||
...KdbModule,
|
// ...IndicatorModule,
|
||||||
|
// ...InputModule,
|
||||||
|
// ...KdbModule,
|
||||||
// ...ListModule,
|
// ...ListModule,
|
||||||
...LoadingModule,
|
// ...LoadingModule,
|
||||||
...MenuModule,
|
// ...MenuModule,
|
||||||
...ModalModule,
|
// ...ModalModule,
|
||||||
...NavbarModule,
|
// ...NavbarModule,
|
||||||
...RadialModule,
|
// ...RadialModule,
|
||||||
...RadioModule,
|
// ...RadioModule,
|
||||||
...RangeModule,
|
// ...RangeModule,
|
||||||
...RatingModule,
|
// ...RatingModule,
|
||||||
...SkeletonModule,
|
// ...SkeletonModule,
|
||||||
...SelectModule,
|
// ...SelectModule,
|
||||||
...StackModule,
|
// ...StackModule,
|
||||||
...StatModule,
|
// ...StatModule,
|
||||||
...StepsModule,
|
// ...StepsModule,
|
||||||
...SwapModule,
|
// ...SwapModule,
|
||||||
...TableModule,
|
// ...TableModule,
|
||||||
...TabsModule,
|
// ...TabsModule,
|
||||||
...TextareaModule,
|
// ...TextareaModule,
|
||||||
...TextrotateModule,
|
// ...TextrotateModule,
|
||||||
...TimelineModule,
|
// ...TimelineModule,
|
||||||
...ToastModule,
|
// ...ToastModule,
|
||||||
...TooltipModule
|
// ...TooltipModule
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Utils = {
|
export const Utils = {
|
||||||
|
|||||||
98
sigpro.css
98
sigpro.css
@@ -99,14 +99,39 @@
|
|||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 1. El contenedor manda */
|
||||||
|
.input-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. El label de DaisyUI debe ocupar todo el ancho */
|
||||||
|
.input-container .input {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. El panel flotante */
|
||||||
.input-content {
|
.input-content {
|
||||||
position: absolute; /* Lo saca del flujo para que no empuje nada */
|
position: absolute;
|
||||||
top: 100%; /* Lo pega justo al borde inferior del input */
|
top: 100%; /* Justo debajo */
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%; /* Para que mida lo mismo que el input */
|
right: 0; /* Esto lo obliga a estirarse de izquierda a derecha */
|
||||||
z-index: 100; /* Para que pase por encima de otros botones/inputs */
|
z-index: 50;
|
||||||
background: white; /* Para que no sea transparente y se lea bien */
|
background: oklch(var(--b1)); /* Fondo del tema */
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Opcional: para darle profundidad */
|
border: 1px solid oklch(var(--bc) / 0.2);
|
||||||
|
border-radius: var(--rounded-box, 1rem);
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden; /* Para que el 'menu' no se salga de los bordes redondeados */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. La lista interna */
|
||||||
|
.input-content .menu {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes tabFadeIn {
|
@keyframes tabFadeIn {
|
||||||
@@ -148,67 +173,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @layer utilities {
|
|
||||||
button {
|
|
||||||
@apply btn;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not([type="radio"]):not([type="checkbox"]):not([type="range"]):not(
|
|
||||||
[type="color"]
|
|
||||||
),
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
@apply input;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="radio"] {
|
|
||||||
@apply radio;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
@apply checkbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="range"] {
|
|
||||||
@apply range;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
@apply select;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
@apply textarea;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
@apply divider;
|
|
||||||
}
|
|
||||||
|
|
||||||
progress {
|
|
||||||
@apply progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
@apply table;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog {
|
|
||||||
@apply modal;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-tip] {
|
|
||||||
@apply tooltip;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
@apply navbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
[role="alert"] {
|
|
||||||
@apply alert;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* sigpro-ui daisyUI classes - extracted from components */
|
/* sigpro-ui daisyUI classes - extracted from components */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user