From 019213f01c69123be77e4e297709428a7104086d Mon Sep 17 00:00:00 2001 From: Natxo <1172351+natxocc@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:07:28 +0200 Subject: [PATCH] Implement Input component with dynamic features --- src/components/Input.js | 77 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/components/Input.js diff --git a/src/components/Input.js b/src/components/Input.js new file mode 100644 index 0000000..1dc49ff --- /dev/null +++ b/src/components/Input.js @@ -0,0 +1,77 @@ +import { $, $html } from "sigpro"; +import { val, joinClass } from "../core/utils.js"; +import { tt } from "../core/i18n.js"; +import { + iconAbc, + iconLock, + iconCalendar, + icon123, + iconMail, + iconShow, + iconHide +} from "../core/icons.js"; + +/** INPUT */ +export const Input = (props) => { + const { label, tip, value, error, isSearch, icon, type = "text", ...rest } = props; + const isPassword = type === "password"; + const visible = $(false); + + const iconsByType = { + text: iconAbc, + password: iconLock, + date: iconCalendar, + number: icon123, + email: iconMail, + }; + + const inputEl = $html("input", { + ...rest, + type: () => (isPassword ? (visible() ? "text" : "password") : type), + placeholder: props.placeholder || label || (isSearch ? tt("search")() : " "), + class: joinClass("grow order-2 focus:outline-none", props.class), + value: value, + oninput: (e) => props.oninput?.(e), + disabled: () => val(props.disabled), + }); + + const leftIcon = icon ? icon : iconsByType[type] ? $html("img", { src: iconsByType[type], class: "opacity-50", alt: type }) : null; + + return $html( + "label", + { + class: () => joinClass("input input-bordered floating-label flex items-center gap-2 w-full relative", val(error) ? "input-error" : ""), + }, + [ + leftIcon ? $html("div", { class: "order-1 shrink-0" }, leftIcon) : null, + label ? $html("span", { class: "text-base-content/60 order-0" }, label) : null, + inputEl, + isPassword + ? $html( + "button", + { + type: "button", + class: "order-3 btn btn-ghost btn-xs btn-circle opacity-50 hover:opacity-100", + onclick: (e) => { + e.preventDefault(); + visible(!visible()); + }, + }, + () => + $html("img", { + class: "w-5 h-5", + src: visible() ? iconShow : iconHide, + }), + ) + : null, + tip + ? $html( + "div", + { class: "tooltip tooltip-left order-4", "data-tip": tip }, + $html("span", { class: "badge badge-ghost badge-xs cursor-help" }, "?"), + ) + : null, + () => (val(error) ? $html("span", { class: "text-error text-[10px] absolute -bottom-5 left-2" }, val(error)) : null), + ], + ); +};