UNPKG

@tachui/primitives

Version:

Basic UI components for tachUI framework

1,652 lines (1,651 loc) 50.3 kB
import { withModifiers as T, ComponentWithCSSClasses as K, createSignal as z, useLifecycle as X, createEffect as W, isSignal as l, ColorAsset as A, h as a, text as p, ConcatenatedComponent as R, createComputed as Z } from "@tachui/core"; import { T as V } from "./Text-DYyFBJ6i.js"; var q = Object.defineProperty, J = (s, e, t) => e in s ? q(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, v = (s, e, t) => J(s, typeof e != "symbol" ? e + "" : e, t); const Q = { colors: { primary: "#007AFF", secondary: "#5856D6", destructive: "#FF3B30", background: "#F2F2F7", surface: "#FFFFFF", onPrimary: "#FFFFFF", onSecondary: "#FFFFFF", onSurface: "#000000", border: "#C7C7CC", disabled: "#8E8E93" }, spacing: { small: 8, medium: 12, large: 16 }, borderRadius: { small: 6, medium: 8, large: 12 }, typography: { small: { size: 14, weight: "500" }, medium: { size: 16, weight: "500" }, large: { size: 18, weight: "600" } } }; class ee extends K { constructor(e, t = Q) { super(), this.props = e, v(this, "type", "component"), v(this, "id"), v(this, "mounted", !1), v(this, "cleanup", []), v(this, "stateSignal"), v(this, "setState"), v(this, "theme"), this.id = `button-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, this.theme = t; const [i, r] = z("normal"); this.stateSignal = i, this.setState = r, this.setupDOMEventListeners(); } /** * Set up DOM event listeners for button interactions */ setupDOMEventListeners() { X(this, { onDOMReady: (e, t) => { t instanceof HTMLButtonElement && (this.attachInteractionEvents(t), this.setupReactiveStyles(t)); } }); } /** * Set up reactive style updates based on state changes */ setupReactiveStyles(e) { const t = W(() => { this.stateSignal(), this.isEnabled(), this.isLoading(); const { tint: i, backgroundColor: r, foregroundColor: n } = this.props; i && l(i) ? i() : i instanceof A && i.resolve(), r && l(r) ? r() : r instanceof A && r.resolve(), n && l(n) ? n() : n instanceof A && n.resolve(); const o = this.getButtonStyles(); this.applyStylesToElement(e, o); }); this.cleanup.push(() => { t && typeof t.dispose == "function" && t.dispose(); }); } /** * Apply computed styles to the button element, respecting modifier-applied styles */ applyStylesToElement(e, t) { Object.entries(t).forEach(([i, r]) => { const n = this.camelToKebabCase(i); if (typeof r == "string" || typeof r == "number") { const o = e.style.getPropertyValue(n); n === "transform" ? e.style.setProperty(n, String(r)) : o && o !== "" && o !== "inherit" || e.style.setProperty(n, String(r)); } }); } /** * Convert camelCase to kebab-case for CSS properties */ camelToKebabCase(e) { return e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); } /** * Resolve color value from string, signal, or asset */ resolveColorValue(e) { if (e) { if (typeof e == "string") return e; if (l(e)) return e(); if (e instanceof A) return e.resolve(); } } /** * Attach interaction event listeners to the button element */ attachInteractionEvents(e) { const t = () => { const c = this.isEnabled(); return typeof c == "boolean" ? c : c(); }; let i = null; const r = (c) => { t() && c.button === 0 && (this.setState("pressed"), i = () => { this.stateSignal() === "pressed" && this.setState("normal"), i && (document.removeEventListener("mouseup", i), i = null); }, document.addEventListener("mouseup", i)); }, n = () => { t() && this.stateSignal() === "pressed" && (this.setState("normal"), i && (document.removeEventListener("mouseup", i), i = null)); }, o = () => { t() && (this.setState("normal"), i && (document.removeEventListener("mouseup", i), i = null)); }, d = () => { t() && this.stateSignal() !== "pressed" && this.setState("focused"); }, u = () => { t() && this.stateSignal() === "focused" && this.setState("normal"); }, y = (c) => { t() && (c.key === " " || c.key === "Enter") && (c.preventDefault(), this.setState("pressed")); }, f = (c) => { t() && (c.key === " " || c.key === "Enter") && (c.preventDefault(), this.setState("normal"), this.props.action?.()); }; e.addEventListener("mousedown", r), e.addEventListener("mouseup", n), e.addEventListener("mouseleave", o), e.addEventListener("focus", d), e.addEventListener("blur", u), e.addEventListener("keydown", y), e.addEventListener("keyup", f), this.cleanup.push(() => { e.removeEventListener("mousedown", r), e.removeEventListener("mouseup", n), e.removeEventListener("mouseleave", o), e.removeEventListener("focus", d), e.removeEventListener("blur", u), e.removeEventListener("keydown", y), e.removeEventListener("keyup", f), i && (document.removeEventListener("mouseup", i), i = null); }); } /** * Check if button is enabled */ isEnabled() { const { isEnabled: e } = this.props; return e === void 0 ? !0 : typeof e == "boolean" || l(e) ? e : !0; } /** * Render the button component */ render() { const e = this.isEnabled(), t = []; this.props.systemImage && t.push( a( "span", { class: "button-icon", style: { marginRight: "8px", fontSize: "1.2em" } }, p(this.props.systemImage) ) ), t.push( a( "span", { class: "button-title" }, p(this.props.title || "") ) ); const i = ["tachui-button"]; return [{ type: "element", tag: "button", props: { className: this.createClassString(this.props, i), type: "button", disabled: typeof e == "boolean" ? !e : () => !e(), // Invert enabled to disabled onClick: this.props.action ? () => { try { this.props.action?.(); } catch (o) { console.error("Button action error:", o); } } : void 0, // Pass through debug label for debug system ...this.props.debugLabel && { debugLabel: this.props.debugLabel } }, children: t, // Attach component metadata for modifier processing componentMetadata: { id: this.id, type: "Button" // Note: modifiers are attached automatically by the modifier system } }]; } /** * Check if button is in loading state */ isLoading() { const { isLoading: e } = this.props; return e === void 0 ? !1 : typeof e == "boolean" ? e : l(e) ? e() : !1; } /** * Check if the button has color-related modifiers applied */ hasColorModifiers() { let e = this.modifiers; return !e && this.modifierBuilder && (e = this.modifierBuilder.modifiers), !e && this.modifiableComponent && (e = this.modifiableComponent.modifiers), !e || !Array.isArray(e) ? !1 : e.some((t) => t.type === "appearance" || t.constructor?.name === "AppearanceModifier" ? t.properties && (t.properties.foregroundColor !== void 0 || t.properties.color !== void 0) : t.properties ? ["foregroundColor", "color", "textColor"].some((r) => t.properties[r] !== void 0) : !1); } /** * Check if the button has typography-related modifiers applied */ hasTypographyModifiers() { let e = this.modifiers; return !e && this.modifierBuilder && (e = this.modifierBuilder.modifiers), !e && this.modifiableComponent && (e = this.modifiableComponent.modifiers), !e || !Array.isArray(e) ? !1 : e.some((i) => i.type === "typography" || i.constructor?.name === "TypographyModifier" ? i.properties && (i.properties.transform !== void 0 || i.properties.textTransform !== void 0) : !1); } /** * Get computed button styles based on variant, size, role, and state */ // biome-ignore lint/suspicious/noExplicitAny: CSS styles require flexible property types getButtonStyles() { const { variant: e, size: t, role: i = "none", tint: r, backgroundColor: n, foregroundColor: o } = this.props, d = this.stateSignal(), u = this.isLoading(), y = this.isEnabled(), f = this.hasColorModifiers(), c = this.hasTypographyModifiers(), C = { // Only set color property if no modifiers will handle it // This prevents conflicts between Button styles and AppearanceModifier ...!f && !o && !e && { color: "inherit" }, // Only set text-related properties if no typography modifiers will handle them // This prevents conflicts between Button styles and TypographyModifier ...!c && { fontStyle: "inherit", lineHeight: "inherit", textTransform: "inherit", textDecoration: "inherit", textIndent: "inherit", textShadow: "inherit", wordSpacing: "inherit", textOrientation: "inherit", writingMode: "inherit", direction: "inherit" } // Let modifiers control: fontFamily, fontSize, fontWeight, letterSpacing, textAlign }; if (t) { const k = this.theme.spacing[t] || this.theme.spacing.medium, j = this.theme.borderRadius[t] || this.theme.borderRadius.medium, H = this.theme.typography[t] || this.theme.typography.medium, Y = { small: "32px", medium: "40px", large: "48px" }; C.padding = `${k}px ${k * 2}px`, C.borderRadius = `${j}px`, C.fontSize = `${H.size}px`, C.fontWeight = H.weight, C.minHeight = Y[t]; } let g = "transparent", S = "transparent", _ = "1px", L; const G = this.resolveColorValue(r), D = this.resolveColorValue(n), O = this.resolveColorValue(o); if (e) { const k = G || this.theme.colors.primary; switch (e) { case "filled": i === "destructive" ? g = this.theme.colors.destructive : i === "cancel" ? g = this.theme.colors.secondary : g = k, L = this.theme.colors.onPrimary; break; case "outlined": S = i === "destructive" ? this.theme.colors.destructive : k, L = i === "destructive" ? this.theme.colors.destructive : k; break; case "bordered": g = this.theme.colors.background, S = this.theme.colors.border; break; case "borderedProminent": g = this.theme.colors.surface, S = this.theme.colors.primary, _ = "2px"; break; } } D && (g = D), O && (L = O); let F = "1", P = "auto", M, B = "none"; y ? u ? (F = "0.6", P = "none") : d === "pressed" ? (g = this.darkenColor(g, 0.1), S = this.darkenColor(S, 0.1), M = "scale(0.95)") : d === "focused" && (B = "0 0 0 3px #007AFF40") : (g = this.theme.colors.disabled, S = this.theme.colors.disabled, L = this.theme.colors.disabled, F = "0.6", P = "none"); const h = { ...C // Size-based styles (only if size provided) }; return e && (h.backgroundColor = g, h.borderColor = S, h.borderWidth = _, L !== void 0 && (h.color = L)), D && (h.backgroundColor = D), O && (h.color = O), h.cursor = y ? "pointer" : "not-allowed", h.opacity = F, h.pointerEvents = P, h.transform = M !== void 0 ? M : "none", h.boxShadow = B, h.transition = "all 0.2s ease", h; } /** * Handle button press with proper state management */ async handlePress() { if (!(!this.isEnabled() || this.isLoading()) && (this.triggerHapticFeedback(), this.setState("pressed"), setTimeout(() => { this.isEnabled() && this.setState("normal"); }, 150), this.props.action)) try { const e = this.props.action(); e && typeof e.then == "function" && await e; } catch (e) { console.error("Button action failed:", e); } } /** * Trigger haptic feedback (mobile Safari support) */ triggerHapticFeedback() { if (this.props.hapticFeedback !== !1 && typeof window < "u" && "navigator" in window) { const e = window.navigator; e.vibrate && e.vibrate(10); } } /** * Helper to darken a color for pressed states */ darkenColor(e, t) { if (e === "transparent") return e; if (e.startsWith("#")) { const i = e.slice(1), r = parseInt(i, 16), n = Math.max(0, Math.floor((r >> 16) * (1 - t))), o = Math.max(0, Math.floor((r >> 8 & 255) * (1 - t))), d = Math.max(0, Math.floor((r & 255) * (1 - t))); return `#${(n << 16 | o << 8 | d).toString(16).padStart(6, "0")}`; } return e; } // ============================================================================ // Concatenation Support (Phase 3.1) // ============================================================================ /** * Concatenate this button with another concatenatable component */ concat(e) { const t = this.toSegment(), i = e.toSegment(), r = { totalSegments: e instanceof R ? e.segments.length + 1 : 2, accessibilityRole: e instanceof R ? this.mergeAccessibilityRoles( "composite", e.metadata.accessibilityRole ) : this.determineAccessibilityRole(e), semanticStructure: "inline" // Buttons are typically inline in concatenation }; return new R([t, i], r); } /** * Convert this button to a segment for concatenation */ toSegment() { return { id: this.id, component: this, modifiers: [], // Buttons don't typically have concatenation-specific modifiers render: () => { const e = this.render(); return Array.isArray(e) ? e[0] : e; } }; } /** * Check if this component supports concatenation */ isConcatenatable() { return !0; } /** * Determine accessibility role for concatenation */ determineAccessibilityRole(e) { switch (e.constructor.name) { case "EnhancedText": return "composite"; // Button + Text = composite (interactive content) case "EnhancedImage": return "composite"; // Button + Image = composite (interactive content) case "EnhancedButton": case "EnhancedLink": return "composite"; // Button + Interactive = composite default: return "composite"; } } /** * Merge accessibility roles when combining components */ mergeAccessibilityRoles(e, t) { return "composite"; } } function E(s, e, t = {}) { const i = { ...t, title: s, ...e && { action: e } }, r = new ee(i); return T(r); } const he = { /** * Filled button (primary) */ Filled: (s, e, t = {}) => E(s, e, { ...t, variant: "filled" }), /** * Outlined button */ Outlined: (s, e, t = {}) => E(s, e, { ...t, variant: "outlined" }), /** * Plain button (text only) */ Plain: (s, e, t = {}) => E(s, e, { ...t, variant: "plain" }), /** * Bordered button */ Bordered: (s, e, t = {}) => E(s, e, { ...t, variant: "bordered" }), /** * Destructive button */ Destructive: (s, e, t = {}) => E(s, e, { ...t, role: "destructive" }), /** * Cancel button */ Cancel: (s, e, t = {}) => E(s, e, { ...t, role: "cancel" }) }, te = ` @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } `; if (typeof document < "u") { const s = document.createElement("style"); s.textContent = te, document.head.appendChild(s); } var ie = Object.defineProperty, se = (s, e, t) => e in s ? ie(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, I = (s, e, t) => se(s, typeof e != "symbol" ? e + "" : e, t); class U { constructor(e) { I(this, "type", "component"), I(this, "id"), I(this, "props"), this.props = e, this.id = `enhanced-link-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } resolveValue(e) { return l(e) ? e() : e; } getDestination() { const e = this.resolveValue(this.props.destination); return e instanceof URL ? e.toString() : e; } getTarget() { const e = this.resolveValue(this.props.target); if (e) return e; const t = this.resolveValue(this.props.destination), i = t instanceof URL ? t.toString() : t; return this.isExternalURL(i) ? "_blank" : "_self"; } getRoutingMode() { return this.resolveValue(this.props.routingMode) || "auto"; } getRel() { const e = this.resolveValue(this.props.rel), t = this.getTarget(), i = this.getDestination(), n = this.isExternalURL(i) && t === "_blank" ? "noopener noreferrer" : ""; return e && n ? `${e} ${n}`.trim() : e || n; } getDownload() { return this.resolveValue(this.props.download); } isDisabled() { return this.resolveValue(this.props.disabled) || !1; } getAccessibilityLabel() { return this.resolveValue(this.props.accessibilityLabel); } getAccessibilityHint() { return this.resolveValue(this.props.accessibilityHint); } getAccessibilityRole() { return this.resolveValue(this.props.accessibilityRole); } shouldUseInternalRouting(e) { const t = this.getRoutingMode(); return t === "internal" ? !0 : t === "external" ? !1 : t === "spa" ? !0 : !(this.isSpecialScheme(e) || this.isExternalURL(e)); } isSpecialScheme(e) { return /^(mailto|tel|sms|ftp|file):/.test(e); } async handleClick(e) { if (this.isDisabled()) { e.preventDefault(); return; } if (this.props.onPress) try { this.props.onPress(e); } catch (n) { console.error("Link onPress error:", n); } const t = this.getDestination(); if (this.isSpecialScheme(t) || (e.preventDefault(), this.props.openURLAction && this.props.openURLAction(t) === "handled")) return; if (this.props.onBeforeNavigation) try { if (!await this.props.onBeforeNavigation(t)) return; } catch (n) { throw console.error("Link onBeforeNavigation error:", n), n; } const i = this.getTarget(), r = this.getRoutingMode(); if (this.shouldUseInternalRouting(t)) { if (this.props.onInternalNavigation) try { if (await this.props.onInternalNavigation(t)) return; } catch (n) { console.error("Link onInternalNavigation error:", n); } r === "spa" ? this.handleSPANavigation(t) : i === "_blank" ? window.open(t, i) : window.location.href = t; } else i === "_blank" || i === "_top" || i === "_parent" ? window.open(t, i) : window.location.href = t; } handleSPANavigation(e) { typeof window < "u" && window.history && window.history.pushState ? (window.history.pushState(null, "", e), window.dispatchEvent(new PopStateEvent("popstate"))) : window.location.href = e; } isExternalURL(e) { try { return new URL(e, window.location.href).hostname !== window.location.hostname; } catch { return !1; } } renderContent() { if (this.props.label) { const n = this.props.label().render(); return Array.isArray(n) ? n : [n]; } if (this.props.children) return this.renderChildren(); let e; this.props.text ? e = this.props.text : l(this.props.destination) ? e = Z(() => this.getDestination()) : e = this.getDestination(); const i = V(e).render(); return Array.isArray(i) ? i : [i]; } renderChildren() { const { children: e } = this.props; return e ? Array.isArray(e) ? e.map((t) => this.renderChild(t)).flat() : this.renderChild(e) : []; } renderChild(e) { if (typeof e == "string") { const i = V(e).render(); return Array.isArray(i) ? i : [i]; } else { const t = e.render(); return Array.isArray(t) ? t : [t]; } } render() { const e = this.renderContent(), t = this.isDisabled(), i = { id: this.id, "data-component": "enhanced-link" }; l(this.props.destination) ? i.href = () => this.getDestination() : i.href = this.getDestination(), this.props.target && (l(this.props.target) ? i.target = () => this.getTarget() : i.target = this.getTarget()); const r = this.getRel(); r && (l(this.props.rel) || l(this.props.target) ? i.rel = () => this.getRel() : i.rel = r); const n = this.getDownload(); n !== void 0 && (l(this.props.download) ? i.download = () => this.getDownload() : n === !0 ? i.download = !0 : typeof n == "string" && (i.download = n)); const o = this.getAccessibilityLabel(); o && (l(this.props.accessibilityLabel) ? i["aria-label"] = () => this.getAccessibilityLabel() : i["aria-label"] = o); const d = this.getAccessibilityHint(); d && (l(this.props.accessibilityHint) ? i["aria-describedby"] = () => this.getAccessibilityHint() : i["aria-describedby"] = d); const u = this.getAccessibilityRole(); return u && (l(this.props.accessibilityRole) ? i.role = () => this.getAccessibilityRole() : i.role = u), t && (i["aria-disabled"] = "true", i.tabindex = "-1"), i.onclick = (f) => this.handleClick(f), i.style = { textDecoration: "none", color: "inherit", cursor: t ? "not-allowed" : "pointer", opacity: t ? 0.6 : 1 }, a("a", i, ...e); } // Concatenation support concat(e) { const t = this.toSegment(), i = e.toSegment(), r = { totalSegments: 2, accessibilityRole: "composite", semanticStructure: "inline" }; return new R([t, i], r); } toSegment() { return { id: this.id, component: this, modifiers: [], render: () => this.render() }; } isConcatenatable() { return !0; } } function ue(s, e) { if (e !== void 0) { const r = { text: s, destination: e }, n = new U(r); return T(n); } const t = s, i = new U(t); return T(i); } const x = { /** * Create an external link that opens in a new tab */ external(s, e, t = {}) { return { destination: s, children: e, target: "_blank", routingMode: "external", rel: "noopener noreferrer external", accessibilityHint: "Opens in a new tab", ...t }; }, /** * Create an internal link for same-domain navigation */ internal(s, e, t = {}) { return { destination: s, children: e, routingMode: "internal", target: "_self", ...t }; }, /** * Create a SPA link using History API */ spa(s, e, t = {}) { return { destination: s, children: e, routingMode: "spa", target: "_self", accessibilityHint: "Navigates within the app", ...t }; }, /** * Create an email link with optional subject and body */ email(s, e, t, i) { let r = `mailto:${s}`; const n = new URLSearchParams(); return e && n.append("subject", e), t && n.append("body", t), n.toString() && (r += `?${n.toString()}`), { destination: r, children: i || s, accessibilityLabel: `Send email to ${s}`, accessibilityHint: "Opens your email app" }; }, /** * Create a phone link with formatted display */ phone(s, e) { return { destination: `tel:${s.replace(/[^\d+]/g, "")}`, children: e || s, accessibilityLabel: `Call ${s}`, accessibilityHint: "Opens your phone app" }; }, /** * Create a download link */ download(s, e, t) { return { destination: s, children: t || `Download ${e || "file"}`, download: e || !0, accessibilityLabel: `Download ${e || "file"}`, accessibilityHint: "Downloads file to your device" }; }, /** * Social media link utilities */ social: { twitter(s, e) { return x.external( `https://twitter.com/${s}`, e || `@${s}`, { accessibilityLabel: `Visit ${s} on Twitter` } ); }, github(s, e) { return x.external( `https://github.com/${s}`, e || s, { accessibilityLabel: `Visit ${s} on GitHub` } ); }, linkedin(s, e) { return x.external( `https://linkedin.com/in/${s}`, e || s, { accessibilityLabel: `Visit ${s} on LinkedIn` } ); }, instagram(s, e) { return x.external( `https://instagram.com/${s}`, e || `@${s}`, { accessibilityLabel: `Visit ${s} on Instagram` } ); }, facebook(s, e) { return x.external( `https://facebook.com/${s}`, e || s, { accessibilityLabel: `Visit ${s} on Facebook` } ); } }, /** * App store link utilities */ appStore: { ios(s, e) { return x.external( `https://apps.apple.com/app/id${s}`, e || "Download from App Store", { accessibilityLabel: "Download from the iOS App Store", accessibilityHint: "Opens App Store app or website" } ); }, android(s, e) { return x.external( `https://play.google.com/store/apps/details?id=${s}`, e || "Get it on Google Play", { accessibilityLabel: "Download from Google Play Store", accessibilityHint: "Opens Google Play Store app or website" } ); } }, /** * Create a custom OpenURL action */ openURLAction(s) { return (e) => s(e) ? "handled" : "systemAction"; } }; var ne = Object.defineProperty, re = (s, e, t) => e in s ? ne(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, w = (s, e, t) => re(s, typeof e != "symbol" ? e + "" : e, t); class oe { constructor(e) { this.props = e, w(this, "type", "component"), w(this, "id"), w(this, "mounted", !1), w(this, "cleanup", []), w(this, "toggleElement", null), w(this, "setIsAnimating"), w(this, "handleToggle", async (i) => { if (i.preventDefault(), this.isDisabled()) return; const r = !this.getIsOn(); this.props.animated !== !1 && (this.setIsAnimating(!0), setTimeout(() => { this.setIsAnimating(!1); }, 200)), this.props.onToggle && this.props.onToggle(r); }), this.id = `toggle-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const [, t] = z(!1); this.setIsAnimating = t, W(() => { const i = this.getIsOn(); this.toggleElement && this.toggleElement.checked !== i && (this.toggleElement.checked = i); }); } /** * Get current toggle state */ getIsOn() { const { isOn: e } = this.props; return l(e) ? e() : e; } // /** // * Get toggle state as signal or static value for DOM renderer // */ // private getIsOnForDOM(): boolean | (() => boolean) { // const { isOn } = this.props // if (isSignal(isOn)) { // return isOn as () => boolean // } // return isOn as boolean // } /** * Check if toggle is disabled */ isDisabled() { const { disabled: e } = this.props; return typeof e == "boolean" ? e : l(e) ? e() : !1; } // /** // * Get disabled state as signal or static value for DOM renderer // */ // private getDisabledForDOM(): boolean | (() => boolean) { // const { disabled } = this.props // if (typeof disabled === 'boolean') return disabled // if (isSignal(disabled)) return disabled as () => boolean // return false // } /** * Resolve label content */ resolveLabel() { const { label: e } = this.props; return e ? typeof e == "string" ? e : typeof e == "function" || l(e) ? e() : e : null; } /** * Helper to render component content safely */ renderComponentContent(e) { const t = e.render(); return Array.isArray(t) ? t : [t]; } /** * Get toggle size styles */ getSizeStyles() { const { size: e = "medium" } = this.props; switch (e) { case "small": return { width: "36px", height: "20px", thumbSize: "16px", fontSize: "14px" }; case "large": return { width: "56px", height: "32px", thumbSize: "28px", fontSize: "18px" }; default: return { width: "46px", height: "26px", thumbSize: "22px", fontSize: "16px" }; } } /** * Render switch variant */ renderSwitch() { const e = this.getIsOn(), t = this.isDisabled(), i = this.getSizeStyles(), { color: r = "#007AFF", offColor: n = "#e2e8f0", thumbColor: o = "#ffffff", animated: d = !0 } = this.props, u = e ? r : n, y = e ? `translateX(${parseInt(i.width) - parseInt(i.thumbSize) - 2}px)` : "translateX(2px)"; return a( "div", { style: { position: "relative", display: "inline-block" } }, // Hidden input for form integration a("input", { ref: (f) => { this.toggleElement = f, f && !this.mounted && (f.addEventListener("change", this.handleToggle), this.cleanup.push(() => { f.removeEventListener("change", this.handleToggle); })); }, type: "checkbox", checked: e, disabled: t, style: { position: "absolute", opacity: 0, width: "100%", height: "100%", margin: 0, cursor: t ? "not-allowed" : "pointer" }, "aria-label": this.props.accessibilityLabel, "aria-describedby": this.props.accessibilityHint ? `${this.id}-hint` : void 0 }), // Switch track a( "div", { style: { width: i.width, height: i.height, backgroundColor: u, borderRadius: i.height, position: "relative", cursor: t ? "not-allowed" : "pointer", opacity: t ? 0.5 : 1, transition: d ? "background-color 0.2s ease" : "none", border: "1px solid rgba(0,0,0,0.1)", boxShadow: "inset 0 1px 2px rgba(0,0,0,0.1)" }, onClick: this.handleToggle }, // Switch thumb a("div", { style: { width: i.thumbSize, height: i.thumbSize, backgroundColor: o, borderRadius: "50%", position: "absolute", top: "50%", transform: `translateY(-50%) ${y}`, transition: d ? "transform 0.2s ease" : "none", boxShadow: "0 2px 4px rgba(0,0,0,0.2)", border: "1px solid rgba(0,0,0,0.1)" } }) ) ); } /** * Render checkbox variant */ renderCheckbox() { const e = this.getIsOn(), t = this.isDisabled(), i = this.getSizeStyles(), { color: r = "#007AFF" } = this.props; return a( "div", { style: { position: "relative", display: "inline-block" } }, // Hidden input a("input", { ref: (n) => { this.toggleElement = n, n && !this.mounted && (n.addEventListener("change", this.handleToggle), this.cleanup.push(() => { n.removeEventListener("change", this.handleToggle); })); }, type: "checkbox", checked: e, disabled: t, style: { position: "absolute", opacity: 0, width: "100%", height: "100%", margin: 0, cursor: t ? "not-allowed" : "pointer" } }), // Checkbox visual a( "div", { style: { width: i.thumbSize, height: i.thumbSize, border: `2px solid ${e ? r : "#d1d5db"}`, borderRadius: "4px", backgroundColor: e ? r : "#ffffff", display: "flex", alignItems: "center", justifyContent: "center", cursor: t ? "not-allowed" : "pointer", opacity: t ? 0.5 : 1, transition: "all 0.2s ease" }, onClick: this.handleToggle }, ...e ? [ // Checkmark a("div", { style: { width: "6px", height: "10px", border: "2px solid white", borderTop: "none", borderLeft: "none", transform: "rotate(45deg) translateY(-1px)", opacity: e ? 1 : 0, transition: "opacity 0.1s ease" } }) ] : [] ) ); } /** * Render button variant */ renderButton() { const e = this.getIsOn(), t = this.isDisabled(), i = this.getSizeStyles(), { color: r = "#007AFF", offColor: n = "#f3f4f6" } = this.props; return a( "button", { ref: (o) => { o && !this.mounted && (o.addEventListener("click", this.handleToggle), this.cleanup.push(() => { o.removeEventListener("click", this.handleToggle); })); }, type: "button", disabled: t, style: { padding: "8px 16px", backgroundColor: e ? r : n, color: e ? "#ffffff" : "#374151", border: `1px solid ${e ? r : "#d1d5db"}`, borderRadius: "6px", fontSize: i.fontSize, fontWeight: "500", cursor: t ? "not-allowed" : "pointer", opacity: t ? 0.5 : 1, transition: "all 0.2s ease", outline: "none" }, "aria-pressed": e, "aria-label": this.props.accessibilityLabel }, p(e ? "ON" : "OFF") ); } /** * Render toggle control based on variant */ renderToggleControl() { const { variant: e = "switch" } = this.props; switch (e) { case "checkbox": return this.renderCheckbox(); case "button": return this.renderButton(); default: return this.renderSwitch(); } } /** * Render label content */ renderLabel() { const e = this.resolveLabel(); if (!e) return null; const t = this.getSizeStyles(); return a( "span", { style: { fontSize: t.fontSize, color: this.isDisabled() ? "#9ca3af" : "#374151", cursor: this.isDisabled() ? "not-allowed" : "pointer" }, onClick: this.isDisabled() ? void 0 : this.handleToggle }, ...typeof e == "string" ? [p(e)] : this.renderComponentContent(e) ); } render() { const { labelPosition: e = "trailing", spacing: t = 8, accessibilityHint: i } = this.props, r = this.renderLabel(), n = this.renderToggleControl(), o = e === "leading" && r ? [r, n] : r ? [n, r] : [n]; return a( "div", { style: { display: "flex", alignItems: "center", gap: r ? `${t}px` : "0", cursor: this.isDisabled() ? "not-allowed" : "pointer" }, "aria-describedby": i ? `${this.id}-hint` : void 0 }, ...o, ...i ? [ a( "div", { id: `${this.id}-hint`, style: { fontSize: "12px", color: "#6b7280", marginTop: "4px", display: "block", width: "100%" } }, p(i) ) ] : [] ); } } function m(s, e = {}) { const t = { ...e, isOn: s }, i = new oe(t); return T(i); } function N(s, e, t = {}) { return m(e, { ...t, label: s }); } const fe = { /** * Switch toggle (default) */ Switch(s, e = {}) { return m(s, { ...e, variant: "switch" }); }, /** * Checkbox toggle */ Checkbox(s, e = {}) { return m(s, { ...e, variant: "checkbox" }); }, /** * Button toggle */ Button(s, e = {}) { return m(s, { ...e, variant: "button" }); }, /** * Small toggle */ Small(s, e = {}) { return m(s, { ...e, size: "small" }); }, /** * Large toggle */ Large(s, e = {}) { return m(s, { ...e, size: "large" }); }, /** * Custom color toggle */ CustomColor(s, e, t = {}) { return m(s, { ...t, color: e }); }, /** * Toggle with leading label */ WithLeadingLabel(s, e, t = {}) { return m(e, { ...t, label: s, labelPosition: "leading" }); }, /** * Toggle with trailing label (default) */ WithTrailingLabel(s, e, t = {}) { return m(e, { ...t, label: s, labelPosition: "trailing" }); } }, ge = { /** * Create a toggle group with multiple options */ createGroup(s, e = {}) { return s.map( (t) => N(t.label, t.isOn, { ...e, onToggle: t.onToggle }) ); }, /** * Create exclusive toggle group (radio-like behavior) */ createExclusiveGroup(s, e, t, i = {}) { const r = () => l(e) ? e() : e; return s.map( (n) => N(n.label, r() === n.key, { ...i, onToggle: (o) => { o && t && t(n.key); } }) ); }, /** * Batch toggle operations */ batch: { /** * Toggle all items in a group */ toggleAll(s, e) { s.forEach((t) => { t.onToggle && t.onToggle(e); }); }, /** * Get state of all toggles in a group */ getStates(s) { return s.map((e) => l(e.isOn) ? e.isOn() : e.isOn); }, /** * Check if all toggles are on */ allOn(s) { return this.getStates(s).every((e) => e); }, /** * Check if any toggle is on */ anyOn(s) { return this.getStates(s).some((e) => e); } } }; var ae = Object.defineProperty, le = (s, e, t) => e in s ? ae(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, b = (s, e, t) => le(s, typeof e != "symbol" ? e + "" : e, t); class de { constructor(e) { this.props = e, b(this, "type", "component"), b(this, "id"), b(this, "mounted", !1), b(this, "cleanup", []), b(this, "isOpen"), b(this, "setIsOpen"), b(this, "searchTerm"), b(this, "setSearchTerm"), b(this, "handleSelection", (o) => { this.props.onSelectionChange && this.props.onSelectionChange(o), (this.props.variant === "dropdown" || this.props.variant === "menu") && this.setIsOpen(!1), this.setSearchTerm(""); }), b(this, "toggleOpen", () => { this.isDisabled() || this.setIsOpen(!this.isOpen()); }), this.id = `picker-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const [t, i] = z(!1), [r, n] = z(""); if (this.isOpen = t, this.setIsOpen = i, this.searchTerm = r, this.setSearchTerm = n, typeof window < "u") { const o = (d) => { const u = document.getElementById(this.id); u && !u.contains(d.target) && this.setIsOpen(!1); }; document.addEventListener("click", o), this.cleanup.push(() => { document.removeEventListener("click", o); }); } } /** * Get current selection value */ getSelection() { const { selection: e } = this.props; return l(e) ? e() : e; } /** * Get current options */ getOptions() { const { options: e } = this.props; return l(e) ? e() : e; } /** * Filter options based on search term */ getFilteredOptions() { const e = this.getOptions(), t = this.searchTerm().toLowerCase(); return !t || !this.props.searchable ? e : e.filter( (i) => i.label.toLowerCase().includes(t) || i.description?.toLowerCase().includes(t) ); } /** * Check if picker is disabled */ isDisabled() { const { disabled: e } = this.props; return typeof e == "boolean" ? e : l(e) ? e() : !1; } /** * Get selected option */ getSelectedOption() { const e = this.getSelection(); return this.getOptions().find((i) => i.value === e) || null; } /** * Get picker size styles */ getSizeStyles() { const { size: e = "medium" } = this.props; switch (e) { case "small": return { fontSize: "14px", padding: "6px 12px", minHeight: "32px" }; case "large": return { fontSize: "18px", padding: "12px 16px", minHeight: "48px" }; default: return { fontSize: "16px", padding: "8px 12px", minHeight: "40px" }; } } /** * Render dropdown variant */ renderDropdown() { const e = this.getSelectedOption(), t = this.getFilteredOptions(), i = this.getSizeStyles(), r = this.isOpen(); return a( "div", { id: this.id, style: { position: "relative", display: "inline-block", minWidth: "200px" } }, // Trigger button a( "button", { type: "button", onClick: this.toggleOpen, disabled: this.isDisabled(), style: { ...i, width: "100%", display: "flex", alignItems: "center", justifyContent: "space-between", backgroundColor: "#ffffff", border: "1px solid #d1d5db", borderRadius: "6px", cursor: this.isDisabled() ? "not-allowed" : "pointer", opacity: this.isDisabled() ? 0.6 : 1, ...r && { borderColor: this.props.tint || "#007AFF", boxShadow: `0 0 0 1px ${this.props.tint || "#007AFF"}` } } }, a( "span", { style: { color: e ? "#1a1a1a" : "#6b7280" } }, p( e?.label || this.props.placeholder || "Select an option" ) ), a( "span", { style: { fontSize: "12px", color: "#6b7280", transform: r ? "rotate(180deg)" : "rotate(0deg)", transition: "transform 0.2s" } }, p("▼") ) ), ...r ? [ a( "div", { style: { position: "absolute", top: "100%", left: "0", right: "0", zIndex: 1e3, backgroundColor: "#ffffff", border: "1px solid #d1d5db", borderRadius: "6px", boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)", maxHeight: "200px", overflowY: "auto", marginTop: "2px" } }, ...this.props.searchable ? [ a( "div", { style: { padding: "8px", borderBottom: "1px solid #e5e7eb" } }, a("input", { type: "text", placeholder: "Search options...", value: this.searchTerm(), onInput: (n) => { const o = n.target; this.setSearchTerm(o.value); }, style: { width: "100%", padding: "6px 8px", border: "1px solid #d1d5db", borderRadius: "4px", fontSize: "14px" } }) ) ] : [], ...t.map( (n) => a( "button", { type: "button", key: String(n.value), onClick: () => this.handleSelection(n.value), disabled: n.disabled, style: { width: "100%", padding: "8px 12px", textAlign: "left", backgroundColor: n.value === this.getSelection() ? "#f3f4f6" : "transparent", border: "none", cursor: n.disabled ? "not-allowed" : "pointer", opacity: n.disabled ? 0.5 : 1, fontSize: "14px", display: "flex", alignItems: "center", gap: "8px", ":hover": { backgroundColor: "#f9fafb" } } }, ...n.icon ? [ a( "span", { style: { fontSize: "16px" } }, p(n.icon) ) ] : [], a( "div", {}, a("div", {}, p(n.label)), ...n.description ? [ a( "div", { style: { fontSize: "12px", color: "#6b7280", marginTop: "2px" } }, p(n.description) ) ] : [] ) ) ), ...t.length === 0 ? [ a( "div", { style: { padding: "12px", textAlign: "center", color: "#6b7280", fontSize: "14px" } }, p("No options found") ) ] : [] ) ] : [] ); } /** * Render segmented variant */ renderSegmented() { const e = this.getOptions(), t = this.getSelection(), i = this.getSizeStyles(); return a( "div", { style: { display: "flex", backgroundColor: "#f3f4f6", borderRadius: "8px", padding: "2px" }, role: "radiogroup", "aria-label": this.props.accessibilityLabel }, ...e.map( (r, n) => a( "button", { type: "button", key: String(r.value), onClick: () => this.handleSelection(r.value), disabled: r.disabled || this.isDisabled(), role: "radio", "aria-checked": r.value === t, style: { ...i, flex: 1, border: "none", borderRadius: "6px", backgroundColor: r.value === t ? "#ffffff" : "transparent", color: r.value === t ? "#1a1a1a" : "#6b7280", fontWeight: r.value === t ? "600" : "400", cursor: r.disabled || this.isDisabled() ? "not-allowed" : "pointer", opacity: r.disabled || this.isDisabled() ? 0.5 : 1, boxShadow: r.value === t ? "0 1px 2px rgba(0, 0, 0, 0.1)" : "none", transition: "all 0.2s", margin: "0", ...n === 0 && { marginRight: "1px" }, ...n === e.length - 1 && { marginLeft: "1px" } } }, ...r.icon ? [ a( "span", { style: { marginRight: "6px", fontSize: "14px" } }, p(r.icon) ) ] : [], p(r.label) ) ) ); } /** * Render wheel variant (simplified) */ renderWheel() { const e = this.getOptions(), t = this.getSelection(); return a( "select", { value: String(t), onChange: (i) => { const r = i.target, n = e.find( (o) => String(o.value) === r.value ); n && this.handleSelection(n.value); }, disabled: this.isDisabled(), style: { ...this.getSizeStyles(), minWidth: "200px", backgroundColor: "#ffffff", border: "1px solid #d1d5db", borderRadius: "6px", cursor: this.isDisabled() ? "not-allowed" : "pointer", opacity: this.isDisabled() ? 0.6 : 1 } }, ...e.map( (i) => a( "option", { key: String(i.value), value: String(i.value), disabled: i.disabled }, p(i.label) ) ) ); } render() { const { variant: e = "dropdown", accessibilityLabel: t, accessibilityHint: i } = this.props, r = { "aria-label": t, "aria-describedby": i ? `${this.id}-hint` : void 0 }; let n; switch (e) { case "segmented": n = this.renderSegmented(); break; case "wheel": n = this.renderWheel(); break; default: n = this.renderDropdown(); break; } return a( "div", r, n, ...i ? [ a( "div", { id: `${this.id}-hint`, style: { fontSize: "12px", color: "#6b7280", marginTop: "4px" } }, p(i) ) ] : [] ); } } function $(s, e, t = {}) { const i = { ...t, selection: s, options: e }, r = new de(i); return T(r); } const be = { /** * Dropdown picker (default) */ // biome-ignore lint/suspicious/noExplicitAny: Generic dropdown picker requires flexible type Dropdown(s, e, t = {}) { return $(s, e, { ...t, variant: "dropdown" }); }, /** * Segmented picker */ // biome-ignore lint/suspicious/noExplicitAny: Generic segmented picker requires flexible type Segmented(s, e, t = {}) { return $(s, e, { ...t, variant: "segmented" }); }, /** * Wheel picker (native select) */ // biome-ignore lint/suspicious/noExplicitAny: Generic wheel picker requires flexible type Wheel(s, e, t = {}) { return $(s, e, { ...t, variant: "wheel" }); }, /** * Menu picker */ // biome-ignore lint/suspicious/noExplicitAny: Generic menu picker requires flexible type Menu(s, e, t = {}) { return $(s, e, { ...t, variant: "menu" }); } }, me = { /** * Create options from simple array */ fromArray(s, e) { return s.map((t) => ({ value: t, label: String(e && typeof t == "object" && t !== null ? t[e] : t) })); }, /** * Create options from enum */ fromEnum(s) { return Object.entries(s).map(([e, t]) => ({ value: t, label: e.replace(/([A-Z])/g, " $1").trim() })); }, /** * Group options by category */ grouped(s) { return s.reduce( (e, t) => {