Repair combo
This commit is contained in:
@@ -3,7 +3,7 @@ Blazing fast, zero-overhead, vanilla JS renderer with atomic reactivity.
|
|||||||
# `SigPro`
|
# `SigPro`
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/sigpro)
|
[](https://www.npmjs.com/package/sigpro)
|
||||||

|

|
||||||
[](https://github.com/natxocc/sigpro/blob/main/LICENSE)
|
[](https://github.com/natxocc/sigpro/blob/main/LICENSE)
|
||||||
|
|
||||||
[**Explore the Docs →**](https://sigpro.natxocc.com/#/)
|
[**Explore the Docs →**](https://sigpro.natxocc.com/#/)
|
||||||
|
|||||||
2
dist/sigpro.ui.css
vendored
2
dist/sigpro.ui.css
vendored
File diff suppressed because one or more lines are too long
2
dist/sigpro.ui.js
vendored
2
dist/sigpro.ui.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
138
docs/ui.md
138
docs/ui.md
@@ -9,72 +9,92 @@ const rango = $({ start: null, end: null });
|
|||||||
const pais = $("");
|
const pais = $("");
|
||||||
|
|
||||||
// ===== OPCIONES =====
|
// ===== OPCIONES =====
|
||||||
const frutas = ["Manzana", "Pera", "Plátano", "Fresa", "Mango", "Sandía", "Melón", "Uva"];
|
const frutas = [
|
||||||
|
"Manzana",
|
||||||
|
"Pera",
|
||||||
|
"Plátano",
|
||||||
|
"Fresa",
|
||||||
|
"Mango",
|
||||||
|
"Sandía",
|
||||||
|
"Melón",
|
||||||
|
"Uva",
|
||||||
|
];
|
||||||
const paises = [
|
const paises = [
|
||||||
{ label: "🇪🇸 España", value: "ES" },
|
{ label: "🇪🇸 España", value: "ES" },
|
||||||
{ label: "🇲🇽 México", value: "MX" },
|
{ label: "🇲🇽 México", value: "MX" },
|
||||||
{ label: "🇦🇷 Argentina", value: "AR" },
|
{ label: "🇦🇷 Argentina", value: "AR" },
|
||||||
{ label: "🇨🇴 Colombia", value: "CO" },
|
{ label: "🇨🇴 Colombia", value: "CO" },
|
||||||
{ label: "🇨🇱 Chile", value: "CL" }
|
{ label: "🇨🇱 Chile", value: "CL" },
|
||||||
];
|
];
|
||||||
|
|
||||||
mount(() =>
|
mount(
|
||||||
div({ class: "p-8 max-w-md mx-auto flex flex-col gap-4" }, [
|
() =>
|
||||||
h1({ class: "text-2xl font-bold" }, "Field Components"),
|
div({ class: "p-8 max-w-md mx-auto flex flex-col gap-4" }, [
|
||||||
|
h1({ class: "text-2xl font-bold" }, "Field Components"),
|
||||||
|
|
||||||
// Autocomplete simple
|
// Autocomplete simple
|
||||||
ui.autocomplete({
|
ui.autocomplete({
|
||||||
label: "Fruta favorita",
|
label: "Fruta favorita",
|
||||||
items: frutas,
|
items: frutas,
|
||||||
value: fruta,
|
value: fruta,
|
||||||
placeholder: "Buscar fruta..."
|
placeholder: "Buscar fruta...",
|
||||||
}),
|
|
||||||
|
|
||||||
// Autocomplete con objetos
|
|
||||||
ui.autocomplete({
|
|
||||||
label: "País",
|
|
||||||
items: paises,
|
|
||||||
value: pais,
|
|
||||||
placeholder: "Elige un país..."
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Datepicker simple
|
|
||||||
ui.datepicker({
|
|
||||||
label: "Fecha de nacimiento",
|
|
||||||
value: fecha,
|
|
||||||
placeholder: "Selecciona fecha..."
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Datepicker rango
|
|
||||||
ui.datepicker({
|
|
||||||
label: "Estancia",
|
|
||||||
range: true,
|
|
||||||
value: rango,
|
|
||||||
placeholder: "Check-in → Check-out"
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Colorpicker
|
|
||||||
ui.colorpicker({
|
|
||||||
label: "Color favorito",
|
|
||||||
value: color,
|
|
||||||
placeholder: "Elige un color..."
|
|
||||||
}),
|
|
||||||
|
|
||||||
ui.theme(),
|
|
||||||
// Preview
|
|
||||||
div({ class: "bg-base-200 rounded-box p-4 flex flex-col gap-2 text-sm" }, [
|
|
||||||
div({}, () => `🍎 Fruta: ${val(fruta) || "—"}`),
|
|
||||||
div({}, () => `🌍 País: ${val(pais) || "—"}`),
|
|
||||||
div({}, () => `📅 Fecha: ${val(fecha) || "—"}`),
|
|
||||||
div({}, () => {
|
|
||||||
const r = val(rango);
|
|
||||||
return r.start && r.end ? `🏨 Estancia: ${r.start} → ${r.end}` : "🏨 Estancia: —";
|
|
||||||
}),
|
}),
|
||||||
div({ class: "flex items-center gap-2" }, [
|
|
||||||
span({}, "🎨 Color:"),
|
// Autocomplete con objetos
|
||||||
div({ class: "w-6 h-6 rounded border border-base-300", style: () => `background:${val(color)}` })
|
ui.autocomplete({
|
||||||
])
|
label: "País",
|
||||||
])
|
items: paises,
|
||||||
])
|
value: pais,
|
||||||
, "#ui");
|
placeholder: "Elige un país...",
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Datepicker simple
|
||||||
|
ui.datepicker({
|
||||||
|
label: "Fecha de nacimiento",
|
||||||
|
value: fecha,
|
||||||
|
placeholder: "Selecciona fecha...",
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Datepicker rango
|
||||||
|
ui.datepicker({
|
||||||
|
label: "Estancia",
|
||||||
|
range: true,
|
||||||
|
value: rango,
|
||||||
|
placeholder: "Check-in → Check-out",
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Colorpicker
|
||||||
|
ui.colorpicker({
|
||||||
|
label: "Color favorito",
|
||||||
|
value: color,
|
||||||
|
placeholder: "Elige un color...",
|
||||||
|
}),
|
||||||
|
|
||||||
|
ui.password({}),
|
||||||
|
ui.theme(),
|
||||||
|
// Preview
|
||||||
|
div(
|
||||||
|
{ class: "bg-base-200 rounded-box p-4 flex flex-col gap-2 text-sm" },
|
||||||
|
[
|
||||||
|
div({}, () => `🍎 Fruta: ${val(fruta) || "—"}`),
|
||||||
|
div({}, () => `🌍 País: ${val(pais) || "—"}`),
|
||||||
|
div({}, () => `📅 Fecha: ${val(fecha) || "—"}`),
|
||||||
|
div({}, () => {
|
||||||
|
const r = val(rango);
|
||||||
|
return r.start && r.end
|
||||||
|
? `🏨 Estancia: ${r.start} → ${r.end}`
|
||||||
|
: "🏨 Estancia: —";
|
||||||
|
}),
|
||||||
|
div({ class: "flex items-center gap-2" }, [
|
||||||
|
span({}, "🎨 Color:"),
|
||||||
|
div({
|
||||||
|
class: "w-6 h-6 rounded border border-base-300",
|
||||||
|
style: () => `background:${val(color)}`,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
"#ui",
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
// export const createTag = (tag, defaultProps = {}) => {
|
|
||||||
// const fn = (p, c) => {
|
|
||||||
// const props = { ...defaultProps, ...p };
|
|
||||||
// return h(tag, props, c);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return new Proxy(fn, {
|
|
||||||
// get(_, className) {
|
|
||||||
// const realClass = className.replace(/_/g, '-');
|
|
||||||
// return (p, c) => {
|
|
||||||
// const classProp = p?.class ? `${realClass} ${p.class}` : realClass;
|
|
||||||
// return fn({ ...p, class: classProp }, c);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
const { h } = window.SigPro;
|
|
||||||
|
|
||||||
const htmlTags = "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video";
|
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
htmlTags.split(" ").forEach(tag => {
|
|
||||||
window[tag] = (props, children) => h(tag, props, children);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ const { $, h, mount, val, isF, isO } = window.SigPro;
|
|||||||
export const hide = () => document.activeElement?.blur();
|
export const hide = () => document.activeElement?.blur();
|
||||||
|
|
||||||
export const ui = {
|
export const ui = {
|
||||||
_label: (p, c) => h("label", { class: "floating-label" }, [h("span", {}, p.label ?? null), c]),
|
|
||||||
accordion: (p, c) => h("div", { ...p, class: `collapse ${p.class || ''}` }, [h("input", { type: "radio", name: p.name, checked: p.checked }), c]),
|
accordion: (p, c) => h("div", { ...p, class: `collapse ${p.class || ''}` }, [h("input", { type: "radio", name: p.name, checked: p.checked }), c]),
|
||||||
accordionTitle: (p, c) => h("div", { ...p, class: `collapse-title ${p.class || ''}` }, c),
|
accordionTitle: (p, c) => h("div", { ...p, class: `collapse-title ${p.class || ''}` }, c),
|
||||||
accordionContent: (p, c) => h("div", { ...p, class: `collapse-content ${p.class || ''}` }, c),
|
accordionContent: (p, c) => h("div", { ...p, class: `collapse-content ${p.class || ''}` }, c),
|
||||||
@@ -48,7 +47,8 @@ export const ui = {
|
|||||||
chatBubble: (p, c) => h("div", { ...p, class: `chat-bubble ${p.class || ''}` }, c),
|
chatBubble: (p, c) => h("div", { ...p, class: `chat-bubble ${p.class || ''}` }, c),
|
||||||
chatFooter: (p, c) => h("div", { ...p, class: `chat-footer ${p.class || ''}` }, c),
|
chatFooter: (p, c) => h("div", { ...p, class: `chat-footer ${p.class || ''}` }, c),
|
||||||
checkbox: (p) => h("input", { ...p, type: "checkbox", class: `checkbox ${p.class || ''}` }),
|
checkbox: (p) => h("input", { ...p, type: "checkbox", class: `checkbox ${p.class || ''}` }),
|
||||||
colorpicker: (p) => ui.combo({ ...p, style: ()=>`background:${val(p.value) || '#000'}`, custom: () => h("span", {
|
colorpicker: (p) => ui.combo({
|
||||||
|
...p, custom: () => h("span", {
|
||||||
class: "w-4 h-4 rounded border border-base-300",
|
class: "w-4 h-4 rounded border border-base-300",
|
||||||
style: `background:${val(p.value) || '#000'}`
|
style: `background:${val(p.value) || '#000'}`
|
||||||
})
|
})
|
||||||
@@ -57,12 +57,12 @@ export const ui = {
|
|||||||
),
|
),
|
||||||
combo: (p, c) => {
|
combo: (p, c) => {
|
||||||
const { placeholder = "", class: cls = "" } = p;
|
const { placeholder = "", class: cls = "" } = p;
|
||||||
const query = $("");
|
const query = isF(p.value) ? p.value : $(p.value ?? "");
|
||||||
let inputEl, open = $(false);
|
let inputEl, open = $(false);
|
||||||
|
|
||||||
return ui._label({ label: p.label }, [
|
return ui.float({ label: p.label }, [
|
||||||
h("div", { class: `dropdown ${cls} ${val(open) ? "dropdown-open" : ""}` }, [
|
h("div", { class: `dropdown w-full ${cls} ${val(open) ? "dropdown-open" : ""}` }, [
|
||||||
h("label", { class: "input" }, [
|
h("label", { class: "input w-full" }, [
|
||||||
h("span", { class: p.icon ?? "icon-[lucide--search]" }),
|
h("span", { class: p.icon ?? "icon-[lucide--search]" }),
|
||||||
p.custom ?? null,
|
p.custom ?? null,
|
||||||
h("input", {
|
h("input", {
|
||||||
@@ -73,7 +73,7 @@ export const ui = {
|
|||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
h("div", {
|
h("div", {
|
||||||
class: "dropdown-content bg-base-100 rounded-box z-50 w-full p-2 shadow-sm",
|
class: "dropdown-content bg-base-100 rounded-box z-50 max-w-80 shadow-sm",
|
||||||
onmousedown: e => e.preventDefault()
|
onmousedown: e => e.preventDefault()
|
||||||
}, () => val(open) && typeof c === "function"
|
}, () => val(open) && typeof c === "function"
|
||||||
? c({ query, open, close: () => { open(false); inputEl?.blur(); }, setValue: v => query(v) })
|
? c({ query, open, close: () => { open(false); inputEl?.blur(); }, setValue: v => query(v) })
|
||||||
@@ -82,21 +82,30 @@ export const ui = {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
datepicker: (p) => ui.combo(p, ({ close, setValue }) => {
|
datepicker: (p) => {
|
||||||
const range = p.range;
|
const range = isF(p.range) ? p.range() : p.range;
|
||||||
return h("div", { class: "w-80" }, [
|
if (!range) return ui.combo({ value: (isF(p.value) ? p.value() : p.value) || '', ...p },
|
||||||
calendar({
|
({ close, setValue }) => h("div", { class: "w-80" },
|
||||||
...p,
|
calendar({ ...p, class: "w-full", onChange: v => { setValue(v); close(); if (isF(p.value)) p.value(v) } })
|
||||||
class: "w-full",
|
)
|
||||||
onChange: (v) => {
|
);
|
||||||
if (isF(p.value)) p.value(v);
|
const v = $(isF(p.value) ? p.value() : p.value || { start: null, end: null });
|
||||||
if (!range) { setValue(v); close(); }
|
const start = $((v() || {}).start || ''), end = $((v() || {}).end || '');
|
||||||
else if (v.start && v.end) { setValue(`${v.start} → ${v.end}`); close(); }
|
const sync = () => { v({ start: start(), end: end() }); if (isF(p.value)) p.value(v()); };
|
||||||
else if (v.start) setValue(`${v.start} → ...`);
|
const cal = (key, sig, ph, dis) => ui.combo({ value: sig, placeholder: ph, class: "flex-1", disabled: dis },
|
||||||
}
|
({ close, setValue }) => h("div", { class: "w-72" },
|
||||||
})
|
calendar({
|
||||||
|
...p, class: "w-full", value: v, range: true, onChange: r => {
|
||||||
|
v(r); start(r?.start || ''); end(r?.end || ''); setValue(r?.[key] || ''); if (r?.end) close(); if (isF(p.value)) p.value(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return h("div", { class: `flex gap-1 ${p.class || ''}`, onchange: sync }, [
|
||||||
|
cal('start', start, p.fromPlaceholder || "Inicio"),
|
||||||
|
cal('end', end, p.toPlaceholder || "Fin", () => !v()?.start)
|
||||||
]);
|
]);
|
||||||
}),
|
},
|
||||||
divider: (p) => h("div", { ...p, class: `divider ${p.class || ''}` }),
|
divider: (p) => h("div", { ...p, class: `divider ${p.class || ''}` }),
|
||||||
drawer: (p, c) => h("div", { ...p, class: `drawer ${p.class || ''}` }, c),
|
drawer: (p, c) => h("div", { ...p, class: `drawer ${p.class || ''}` }, c),
|
||||||
drawerToggle: (p) => h("input", { ...p, type: "checkbox", class: `drawer-toggle ${p.class || ''}` }),
|
drawerToggle: (p) => h("input", { ...p, type: "checkbox", class: `drawer-toggle ${p.class || ''}` }),
|
||||||
@@ -129,20 +138,16 @@ export const ui = {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
fileError: (p) => h("div", { class: `text-[10px] text-error mt-1 px-1 ${p.class || ''}` }, p.message),
|
fileError: (p) => h("div", { class: `text-[10px] text-error mt-1 px-1 ${p.class || ''}` }, p.message),
|
||||||
|
float: (p, c) => h("label", { class: "floating-label" }, [h("span", {}, p.label ?? null), c]),
|
||||||
icon: (p) => h("span", { class: p || '' }),
|
icon: (p) => h("span", { class: p || '' }),
|
||||||
indicator: (p, c) => h("div", { ...p, class: `indicator ${p.class || ''}` }, [p.value && h("span", { class: `indicator-item badge ${p.badgeClass || ''}` }, p.value), c]),
|
indicator: (p, c) => h("div", { ...p, class: `indicator ${p.class || ''}` }, [p.value && h("span", { class: `indicator-item badge ${p.badgeClass || ''}` }, p.value), c]),
|
||||||
input: (p) => h("input", { ...p, class: `input ${p.class || ''}` }),
|
input: (p) => ui.float({ label: p.label }, [
|
||||||
inputPass: (p) => {
|
h("label", { class: "input w-full" }, [
|
||||||
const show = $(false);
|
ui.icon(p.icon ?? ""),
|
||||||
return [
|
h("input", { ...p, class: `w-full ${p.class || ''}` }),
|
||||||
ui.input({ ...p, type: () => val(show) ? "text" : "password" }),
|
p.right || null
|
||||||
ui.swap({ class: "ml-2 swap-rotate" }, [
|
])
|
||||||
ui.checkbox({ checked: show }),
|
]),
|
||||||
ui.swapOn({}, ui.icon("icon-[lucide--eye]")),
|
|
||||||
ui.swapOff({}, ui.icon("icon-[lucide--eye-off]"))
|
|
||||||
])
|
|
||||||
];
|
|
||||||
},
|
|
||||||
kbd: (p, c) => h("kbd", { ...p, class: `kbd ${p.class || ''}` }, c),
|
kbd: (p, c) => h("kbd", { ...p, class: `kbd ${p.class || ''}` }, c),
|
||||||
label: (p, c) => h("span", { ...p, class: `label ${p.class || ''}` }, c),
|
label: (p, c) => h("span", { ...p, class: `label ${p.class || ''}` }, c),
|
||||||
loading: (p) => h("span", { ...p, class: `loading loading-spinner ${p.class || ''}` }),
|
loading: (p) => h("span", { ...p, class: `loading loading-spinner ${p.class || ''}` }),
|
||||||
@@ -163,6 +168,20 @@ export const ui = {
|
|||||||
modalAction: (p, c) => h("div", { ...p, class: `modal-action ${p.class || ''}` }, c),
|
modalAction: (p, c) => h("div", { ...p, class: `modal-action ${p.class || ''}` }, c),
|
||||||
navbar: (p, c) => h("div", { ...p, class: `navbar ${p.class || ''}` }, c),
|
navbar: (p, c) => h("div", { ...p, class: `navbar ${p.class || ''}` }, c),
|
||||||
option: (p, c) => h("option", { ...p }, c),
|
option: (p, c) => h("option", { ...p }, c),
|
||||||
|
password: (p) => {
|
||||||
|
const show = $(false);
|
||||||
|
const { right, ...rest } = p;
|
||||||
|
return ui.input({
|
||||||
|
...rest,
|
||||||
|
type: () => val(show) ? "text" : "password",
|
||||||
|
icon: "icon-[lucide--lock]",
|
||||||
|
right: ui.swap({ class: "swap swap-rotate" }, [
|
||||||
|
h('input', { type: "checkbox", checked: show }),
|
||||||
|
ui.swapOn({}, ui.icon("icon-[lucide--eye]")),
|
||||||
|
ui.swapOff({}, ui.icon("icon-[lucide--eye-off]"))
|
||||||
|
])
|
||||||
|
});
|
||||||
|
},
|
||||||
progress: (p) => h("progress", { ...p, class: `progress ${p.class || ''}` }),
|
progress: (p) => h("progress", { ...p, class: `progress ${p.class || ''}` }),
|
||||||
radial: (p) => h("div", { ...p, class: `radial-progress ${p.class || ''}`, style: `--value:${val(p.value) ?? 0}`, role: "progressbar" }, p.value ?? ""),
|
radial: (p) => h("div", { ...p, class: `radial-progress ${p.class || ''}`, style: `--value:${val(p.value) ?? 0}`, role: "progressbar" }, p.value ?? ""),
|
||||||
radio: (p) => h("input", { ...p, type: "radio", class: `radio ${p.class || ''}` }),
|
radio: (p) => h("input", { ...p, type: "radio", class: `radio ${p.class || ''}` }),
|
||||||
@@ -231,7 +250,7 @@ export const ui = {
|
|||||||
|
|
||||||
export const calendar = p => {
|
export const calendar = p => {
|
||||||
let [d, hv, sh, eh] = [$(new Date()), $(0), $(0), $(0)], now = new Date(),
|
let [d, hv, sh, eh] = [$(new Date()), $(0), $(0), $(0)], now = new Date(),
|
||||||
F = v => v?.toISOString().slice(0, 10),
|
F = v => v ? `${v.getFullYear()}-${String(v.getMonth() + 1).padStart(2, '0')}-${String(v.getDate()).padStart(2, '0')}` : '',
|
||||||
P = n => (n < 10 ? '0' : '') + n,
|
P = n => (n < 10 ? '0' : '') + n,
|
||||||
M = (m, y = 0) => d(new Date(d().getFullYear() + y, d().getMonth() + m, 1)),
|
M = (m, y = 0) => d(new Date(d().getFullYear() + y, d().getMonth() + m, 1)),
|
||||||
V = () => typeof p.value == 'function' ? p.value() : p.value,
|
V = () => typeof p.value == 'function' ? p.value() : p.value,
|
||||||
@@ -248,7 +267,7 @@ export const calendar = p => {
|
|||||||
h('span', { class: 'text-sm font-mono' }, () => P(v()) + ':00')
|
h('span', { class: 'text-sm font-mono' }, () => P(v()) + ':00')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return h('div', { class: `p-4 bg-base-100 border shadow-2xl rounded-box w-80 select-none ${p.class || ''}` }, [
|
return h('div', { class: `p-4 bg-base-100 rounded-box w-80 select-none ${p.class || ''}` }, [
|
||||||
h('div', { class: 'flex justify-between items-center mb-4' }, [
|
h('div', { class: 'flex justify-between items-center mb-4' }, [
|
||||||
h('div', { class: 'flex' }, [['-1y', -1, 1], ['-1m', -1, 0]].map(([_, m, y]) => h('button', { class: 'btn btn-ghost btn-xs', onclick: () => M(m, y) }, h('span', { class: `icon-[lucide--chevron${y ? 's' : ''}-left]` })))),
|
h('div', { class: 'flex' }, [['-1y', -1, 1], ['-1m', -1, 0]].map(([_, m, y]) => h('button', { class: 'btn btn-ghost btn-xs', onclick: () => M(m, y) }, h('span', { class: `icon-[lucide--chevron${y ? 's' : ''}-left]` })))),
|
||||||
h('span', { class: 'font-bold uppercase' }, () => d().toLocaleString('es', { month: 'short', year: 'numeric' })),
|
h('span', { class: 'font-bold uppercase' }, () => d().toLocaleString('es', { month: 'short', year: 'numeric' })),
|
||||||
|
|||||||
Reference in New Issue
Block a user