UNPKG

@base-framework/ui

Version:

This is a UI package that adds components and atoms that use Tailwind CSS and a theme based on Shadcn.

1,706 lines 60.7 kB
import { Div as n, H5 as Z, P as b, I as x, Li as H, Span as a, Ul as q, Button as f, OnState as T, Label as P, H2 as S, Form as tt, Header as I, Footer as L, A as V, H3 as et, Checkbox as st, Input as m, Img as G, Nav as ot, UseParent as W, OnStateOpen as O, Time as nt, Dialog as rt } from "@base-framework/atoms"; import { Atom as c, Component as w, Html as A, Dom as it, base as lt, Data as B, Builder as _, Jot as C, Events as p, DateTime as M } from "@base-framework/base"; import { P as D, b as at, R as ct } from "./range-calendar-D5Xazc80.js"; import { C as dt, F as ut } from "./form-group-BB7dLJir.js"; import { B as h, I as g } from "./buttons-ByzNooiu.js"; import { Icons as u } from "./icons.es.js"; import { Timer as ht, List as mt, DynamicTime as ft } from "@base-framework/organisms"; import { a as v } from "./image-BB__4s0g.js"; import { F as Y } from "./format-BmrNQptv.js"; const z = { info: { borderColor: "border-blue-500", bgColor: "bg-muted/10", iconColor: "text-blue-500" }, warning: { bgColor: "bg-muted/10", borderColor: "border-warning", iconColor: "text-warning" }, destructive: { bgColor: "bg-muted/10", borderColor: "border-destructive", iconColor: "text-red-500" }, success: { bgColor: "bg-muted/10", borderColor: "border-emerald-500", iconColor: "text-emerald-500" }, default: { borderColor: "border", bgColor: "bg-muted/10", iconColor: "text-muted-foreground" } }, gt = (t, e) => n({ class: `flex items-center justify-center h-6 w-6 mr-3 ${e}` }, [ x({ html: t }) ]), pt = (t) => Z({ class: "font-semibold" }, t), bt = (t) => b({ class: "text-sm text-muted-foreground" }, t), Le = c(({ title: t, description: e, icon: s, type: o = "default" }) => { const { borderColor: r, bgColor: i, iconColor: l } = z[o] || z.default; return n({ class: `flex items-start p-4 border rounded-lg ${i} ${r}` }, [ // Icon and content s && gt(s, l), n({ class: "flex flex-col" }, [ pt(t), bt(e) ]) ]); }); class Oe extends w { /** * This will declare the props for the compiler. * * @returns {void} */ declareProps() { this.removingClass = ""; } /** * This will remove the component from the DOM after a delay. * * @returns {void} */ remove() { this.prepareDestroy(), this.removeContext(); const e = this.panel, s = this.removingClass; if (!s) { A.removeElement(e); return; } it.addClass(e, s), lt.on("animationend", e, (o) => A.removeElement(e)); } } const xt = (t) => a({ class: "ml-auto text-xs tracking-widest opacity-60" }, t), yt = (t) => a({ class: "flex w-4 h-4", html: t }), wt = (t) => a({ class: "flex-auto" }, t), Ct = (t, e) => H({ class: "relative flex cursor-pointer hover:bg-muted/50 select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", click: () => e(t) }, [ t.icon && yt(t.icon), wt(t.label), t.shortcut && xt(t.shortcut) ]), vt = (t, e) => q({ class: "grid gap-2" }, [ t.map((s) => Ct(s, e)) ]), St = (t) => n({ class: "w-full z-10" }, [ n({ class: "max-h-60 border rounded-md overflow-y-auto p-1 grid gap-2 divide-y divide-border", for: ["groups", (e) => vt(e, t)] }) ]), Dt = ({ label: t, icon: e, toggleDropdown: s }) => f({ cache: "button", class: `inline-flex items-center justify-between rounded-md border px-2 py-2 text-sm font-medium hover:bg-muted focus:outline-none transition duration-150 ease-in-out`, click: s }, [ t && a(t), e && x({ html: e }) ]), kt = ({ onSelect: t }) => n([ T( "open", (e, s, o) => e ? new D({ cache: "dropdown", parent: o, button: o.button }, [ St(t) ]) : null ) ]); class Ae extends w { /** * This will declare the props for the compiler. * * @returns {void} */ declareProps() { this.label = null, this.icon = null, this.onSelect = null, this.groups = []; } /** * Initializes component data. * * @returns {Data} */ setData() { return new B({ groups: this.groups || [] }); } /** * Initializes the component state. * * @returns {object} */ setupStates() { return { open: !1, selectedItem: null }; } /** * Toggles the dropdown open state. * * @returns {void} */ toggleDropdown() { this.state.toggle("open"); } /** * Handles item selection within the dropdown. * * @param {object} item - The selected item object * @returns {void} */ handleSelect(e) { this.state.selectedItem = e, this.state.open = !1, typeof this.onSelect == "function" && this.onSelect(e); } /** * Renders the Dropdown component. * * @returns {object} */ render() { return n({ class: "relative" }, [ Dt({ label: this.label, icon: this.icon, toggleDropdown: this.toggleDropdown.bind(this) }), kt({ onSelect: this.handleSelect.bind(this) }) ]); } } const Tt = c((t, e) => n({ ...t, class: "flex flex-auto flex-col gap-y-2" }, e)), It = c((t, e) => P({ ...t, class: "flex auto text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" }, e)), Bt = c((t, e) => b({ ...t, class: "text-sm text-muted-foreground italic" }, e)), Ft = c((t, e) => b({ ...t, class: "text-sm text-destructive" }, e)), Ye = c((t, e = []) => dt({ class: "flex flex-auto flex-col gap-y-0", margin: "m-0", padding: "p-0" }, [ t.title && S({ class: "text-lg font-semibold py-4 px-6" }, t.title), t.description && b({ class: "text-sm text-muted-foreground pb-4 px-6 max-w-[700px]" }, t.description), ...e ])), ze = c((t, e = []) => ut({ label: t.label, description: t.description, class: "py-4 px-6", border: t.border }, [ n({ class: "flex flex-col gap-y-6" }, e) ])), je = c((t, e = []) => { const s = t.border ? "border-t" : ""; return n({ ...t, class: `flex flex-col pb-4 px-6 gap-y-4 ${s} ${t.class || ""}` }, e); }), $t = (t) => t.tag === "input" || t.tag === "select" || t.tag === "textarea", J = (t, e, s) => t.map((o) => !o || (o.children && o.children.length > 0 && (o.children = J(o.children, e, s)), !o.required) ? o : $t(o) ? { ...o, aria: { invalid: ["hasError"] }, invalid: s, input: e } : o), Mt = c((t, e) => { const r = J(e, (i) => { i.target.checkValidity() && t.setError(null); }, (i) => { t.setError(i.target.validationMessage); }); return n({ ...t, class: "w-full" }, r); }), Ee = v( { /** * The initial state of the FormField. * * @member {object} state * @returns {object} */ state() { return { error: null, hasError: !1, value: this.defaultValue ?? "" }; }, /** * Renders the FormField component. * * @returns {object} */ render() { const t = this.name, e = this.getId(`${t}`), { label: s, description: o } = this, r = (i) => { this.state.error = i, this.state.hasError = !!i; }; return n({ class: "flex flex-auto gap-y-4" }, [ Tt([ It({ htmlFor: e }, s), Mt({ id: e, name: t, value: this.state.value, setError: r }, this.children), o && Bt({ id: this.getId("description") }, o), n({ onState: ["error", (i) => i && Ft(i)] }) ]) ]); } } ), Pt = (t, e, s = null) => { t.target.checkValidity() && (t.preventDefault(), s && s(t, e)); }, K = c( (t, e) => tt({ ...t, submit: (s, o) => Pt(s, o, t.submit), class: `w-full ${t.class ?? ""}` }, e) ), Ne = c((t, e) => n({ ...t, class: `flex flex-auto flex-col gap-y-6 p-4 md:p-6 divide-y ${t.class || ""}` }, e)), Ue = c((t, e = []) => n({ class: "flex flex-col gap-y-3 py-4" }, [ t.title && S({ class: "font-semibold" }, t.title), ...e ])), Re = (t, e) => n({ class: "flex justify-between" }, [ a({ class: "text-muted-foreground" }, t), a(e) ]), He = (t, e) => n({ class: "flex" }, [ a({ class: "text-muted-foreground mr-8 w-24 truncate text-nowrap" }, t), a(e) ]), Lt = c(({ title: t, description: e, back: s, icon: o, options: r = [] }) => I({ class: "modal-header bg-background/80 backdrop-blur-md sticky flex flex-none items-center py-4 px-6 z-10 min-w-0" }, [ /** * Back Button (Mobile Only) */ s && h({ variant: "icon", icon: u.arrows.left, class: "mr-2 p-0 flex sm:hidden", click: (i, l) => l.close() }), /** * Icon Container */ o && n({ class: "mr-2 w-12 h-12 rounded-full bg-muted flex flex-none items-center justify-center" }, [ g({ size: "md" }, o) ]), /** * Title and Description Container */ n({ class: "flex flex-auto flex-row justify-between ml-2 gap-2 min-w-0" }, [ n({ class: "flex flex-auto flex-col min-w-0" }, [ S({ class: "text-lg font-semibold m-0 truncate" }, t), e && n({ class: "text-sm text-muted-foreground truncate" }, e) ]), n({ class: "flex flex-none items-center gap-2" }, r) ]) ])), Q = c((t, e) => { const s = t.class?.includes("drawer"); return n({ popover: "manual", class: `modal ${s ? "" : "m-auto top-0 right-0 bottom-0 left-0"} ${s ? "" : "h-full max-h-screen"} fixed z-20 grid w-full gap-2 lg:border bg-background text-foreground shadow-xl break-words p-0 overflow-y-auto ${t.class}`, click: (i, l) => { i.target === l.panel && (i.preventDefault(), i.stopPropagation(), l.state.open = !1); } }, [ K({ class: "modal-content relative bg-background z-[1] flex flex-auto flex-col gap-y-4 min-w-0", submit: (i, l) => t.onSubmit && t.onSubmit(l), cache: "modalContent", ...t.gestureHandlers }, [ Lt(t), n({ class: "modal-body flex flex-grow flex-col py-0 px-6 z-0 overflow-y-auto", cache: "modalBody" }, e), !t.hideFooter && L({ class: "modal-footer sticky bottom-0 bg-background/80 backdrop-blur-md flex flex-none justify-between py-4 px-6 z-10" }, t.buttons) ]) ]); }), Ot = (t) => _.render(t, app.root); class At extends w { /** * This will declare the props for the compiler. * * @returns {void} */ declareProps() { this.title = null, this.description = null, this.size = null, this.type = null, this.hidePrimaryButton = !1, this.hideFooter = !1, this.icon = null, this.onSubmit = null, this.onClose = null, this.back = !1; } /** * This will render the modal component. * * @returns {object} */ render() { const e = this.getMainClass(), s = this.title || "", o = this.description || null; return Q( { class: e, title: s, description: o, options: this.headerOptions(), buttons: this.getButtons(), hideFooter: this.hideFooter, onSubmit: (r) => { let i = !0; this.onSubmit && (i = this.onSubmit(r)), i !== !1 && this.destroy(); }, icon: this.icon, back: this.back ?? !1, aria: { expanded: ["open"] } }, this.children ); } /** * This will setup the states. * * @returns {object} */ setupStates() { return { open: { state: !1, callBack: (e) => { e || this.destroy(); } } }; } /** * This will get the header options. * * @returns {Array<object>} */ headerOptions() { return []; } /** * This will get the buttons for the modal. * * @returns {array} */ getButtons() { return [ h({ variant: "outline", click: () => this.destroy() }, "Cancel"), this.hidePrimaryButton !== !0 && h({ variant: "primary", type: "submit" }, "Save") ]; } /** * This will check if the click was outside the component. * * @param {object} element * @returns {boolean} */ isOutsideClick(e) { return !this.panel.contains(e); } /** * This will get the size class. * * @returns {string} */ getSizeClass() { switch (this.size) { // case 'sm': // return 'sm max-w-[646px]'; case "lg": return "lg max-w-[900px]"; case "xl": return "xl max-w-[1400px]"; default: return "md max-w-[760px]"; } } /** * This will get the type class. * * @returns {string} */ getTypeClass() { switch (this.type) { case "right": return "right right-0"; case "left": return "left left-0"; case "drawer": return "drawer"; default: return ""; } } /** * This will get the modal class. * * @returns {string} */ getMainClass() { return this.getSizeClass() + " " + this.getTypeClass(); } /** * This will override the set up to use the app shell. * * @param {object} container */ setContainer(e) { this.container = app.root; } /** * This will open the modal. * * @returns {void} */ open() { Ot(this), this.showModal(); } /** * This will destroy the modal. * * @returns {void} */ close() { this.destroy(); } /** * This will show the modal. * * @protected * @returns {void} */ showModal() { globalThis.setTimeout(() => this.panel.showPopover(), 10), this.state.open = !0, document.documentElement.style.overflowY = "hidden"; } /** * This will hide the modal. * * @protected * @returns {void} */ beforeDestroy() { this.panel.hidePopover(), this.state.open = !1, typeof this.onClose == "function" && this.onClose(this), document.documentElement.style.overflowY = "auto"; } } class Yt { /** * Creates an instance of DrawerGesture. * * @param {object} [options={}] * @param {HTMLElement} [options.modal=null] - The modal element (panel) reference * @param {HTMLElement} [options.modalContent=null] - The modal content element reference * @param {HTMLElement} [options.modalBody=null] - The scrollable body element reference * @param {number} [options.closeThreshold=150] - Pixels to drag before closing * @param {number} [options.snapThreshold=50] - Pixels to drag before snapping * @param {Function} [options.onClose=null] - Callback when drawer should close */ constructor(e = {}) { const { modal: s = null, modalContent: o = null, modalBody: r = null, closeThreshold: i = 150, snapThreshold: l = 50, onClose: d = null } = e; this.modal = s, this.modalContent = o, this.modalBody = r, this.closeThreshold = i, this.snapThreshold = l, this.onClose = d, this.reset(); } /** * Resets the drag state to initial values * * @returns {void} */ reset() { this.state = { isDragging: !1, startY: 0, currentY: 0, startScrollTop: 0, canDrag: !1 }; } /** * Checks if the viewport is mobile size * * @returns {boolean} */ isMobile() { return window.innerWidth < 1024; } /** * Handles touch start event * * @param {TouchEvent} e - The touch event * @returns {void} */ handleTouchStart(e) { if (!this.modalBody) return; const s = e.touches[0]; this.state.startY = s.clientY, this.state.currentY = s.clientY, this.state.startScrollTop = this.modalBody.scrollTop, this.state.canDrag = this.modalBody.scrollTop === 0; } /** * Handles touch move event * * @param {TouchEvent} e - The touch event * @returns {void} */ handleTouchMove(e) { if (!this.modalContent || !this.modalBody) return; const s = e.touches[0]; this.state.currentY = s.clientY; const o = this.getDeltaY(); if (!this.state.isDragging && this.state.canDrag && o > 0 && this.modalBody.scrollTop === 0 && (this.state.isDragging = !0), this.state.isDragging && o > 0) { e.preventDefault(); const r = this.calculateTranslateY(o); this.modalContent.style.transform = `translateY(${r}px)`, this.modalContent.style.transition = "none"; const i = this.calculateBackdropOpacity(o); this.updateBackdropOpacity(i); } else this.modalBody.scrollTop > 0 && (this.state.canDrag = !1); } /** * Handles touch end event * * @param {TouchEvent} e - The touch event * @returns {void} */ handleTouchEnd(e) { if (!this.modalContent) return; const s = this.getDeltaY(); this.state.isDragging && (this.modalContent.style.transition = "transform 0.3s ease-out, opacity 0.3s ease-out", s > this.closeThreshold ? this.animateClose() : this.snapBack()), this.state.isDragging = !1, this.state.canDrag = !1; } /** * Gets current drag delta Y * * @returns {number} The vertical drag distance in pixels */ getDeltaY() { return this.state.currentY - this.state.startY; } /** * Calculates translateY with rubber band damping effect. * The further you drag, the more resistance is applied. * * @param {number} deltaY - The raw drag distance * @returns {number} The damped translation distance */ calculateTranslateY(e) { const s = 1 - e / (window.innerHeight * 2); return e * Math.max(s, 0.5); } /** * Calculates backdrop opacity based on drag distance. * Opacity decreases as the drawer is dragged down. * * @param {number} deltaY - The drag distance * @returns {number} Opacity value between 0 and 1 */ calculateBackdropOpacity(e) { return Math.max(0, 1 - e / this.closeThreshold); } /** * Updates the backdrop opacity via CSS custom property * * @param {number} opacity - The opacity value (0-1) * @returns {void} */ updateBackdropOpacity(e) { this.modal && this.modal.style.setProperty("--backdrop-opacity", e.toString()); } /** * Animates the drawer closing by translating the entire modal off-screen * * @returns {void} */ animateClose() { !this.modal || !this.modalContent || (this.modalContent.style.transform = "translateY(0)", this.modalContent.style.transition = "none", this.modal.style.transition = "transform 0.3s ease-out, opacity 0.3s ease-out", this.modal.style.transform = "translateY(100%)", this.updateBackdropOpacity(0), setTimeout(() => { this.onClose && this.onClose(); }, 300)); } /** * Snaps the drawer back to its original position * * @returns {void} */ snapBack() { this.modalContent && (this.modalContent.style.transform = "translateY(0)", this.updateBackdropOpacity(1)); } /** * Checks if currently dragging * * @returns {boolean} */ isDragging() { return this.state.isDragging; } /** * Cleans up resources and resets state * * @returns {void} */ destroy() { this.reset(), this.modal = null, this.modalContent = null, this.modalBody = null, this.onClose = null; } } class qe extends At { /** * Declares the component props * * @returns {void} */ declareProps() { super.declareProps(), this.type = "drawer", this.swipeToClose = !0, this.closeThreshold = 150, this.snapThreshold = 50, this.gesture = null, this.modalContent = null, this.modalBody = null; } /** * Gets extra props to pass to ModalContainer * * @returns {object} */ getContainerProps() { const e = {}; return this.swipeToClose && (e.gestureHandlers = this.getGestureHandlers()), e; } /** * Renders the drawer with gesture handlers * * @returns {object} */ render() { const e = this.getMainClass(), s = this.title || "", o = this.description || null, r = this.getContainerProps(); return Q( { class: e, title: s, description: o, options: this.headerOptions(), buttons: this.getButtons(), hideFooter: this.hideFooter, onSubmit: (i) => { let l = !0; this.onSubmit && (l = this.onSubmit(i)), l !== !1 && this.destroy(); }, icon: this.icon, back: this.back ?? !1, aria: { expanded: ["open"] }, ...r }, this.children ); } /** * Shows the modal and initializes gesture handling * * @protected * @returns {void} */ showModal() { super.showModal(), this.swipeToClose && !this.gesture && (this.gesture = new Yt({ modal: this.panel, modalContent: this.modalContent, modalBody: this.modalBody, closeThreshold: this.closeThreshold, snapThreshold: this.snapThreshold, onClose: () => this.destroy() })); } /** * Gets gesture event handlers for modal content. * Returns event props to be spread onto the modal-content element. * Handlers are bound methods that will work once gesture is initialized. * * @returns {object} */ getGestureHandlers() { return this.swipeToClose ? { touchstart: (e) => this.gesture?.handleTouchStart(e), touchmove: (e) => this.gesture?.handleTouchMove(e), touchend: (e) => this.gesture?.handleTouchEnd(e) } : {}; } /** * Cleans up before destroy * * @protected * @returns {void} */ beforeDestroy() { this.gesture && (this.gesture.destroy(), this.gesture = null), super.beforeDestroy(); } } const j = { info: { bgColor: "bg-muted/10", borderColor: "border-blue-500", iconColor: "text-blue-500" }, warning: { bgColor: "bg-muted/10", borderColor: "border-warning", iconColor: "text-warning" }, destructive: { bgColor: "bg-muted/10", borderColor: "border-destructive", iconColor: "text-red-500" }, success: { bgColor: "bg-muted/10", borderColor: "border-emerald-500", iconColor: "text-emerald-500" }, default: { bgColor: "bg-muted/10", borderColor: "border", iconColor: "text-muted-foreground" } }, zt = (t) => I({ class: "flex justify-center" }, [ et({ class: "text-lg font-bold mb-0" }, t) ]), jt = c(({ href: t, class: e }, s) => V({ class: `bg-popover text-popover-foreground relative flex flex-auto flex-col justify-start shadow-lg pointer-events-auto p-4 border rounded-md min-w-[340px] max-w-[450px] mt-4 ${e}`, href: t, animateIn: "pullRightIn", animateOut: "pullRight", role: "alert" }, s)), Et = c(({ close: t, class: e }, s) => n({ class: `pullRightIn bg-popover text-popover-foreground relative flex flex-auto flex-col justify-start shadow-lg pointer-events-auto p-4 border rounded-md min-w-[340px] max-w-[450px] mt-4 ${e}`, click: () => t(), animateIn: "pullRightIn", animateOut: "pullRight", role: "alert" }, s)); class Nt extends w { /** * This will declare the props for the compiler. * * @returns {void} */ declareProps() { this.secondaryAction = null, this.primaryAction = null, this.primary = !1, this.secondary = !1, this.title = null, this.description = null, this.icon = null, this.onClick = null; } /** * This will be called when the component is created. * * @returns {void} */ onCreated() { this.duration = this.duration || 4e3; } /** * This will render the component. * * @returns {object} */ render() { const { bgColor: e, borderColor: s, iconColor: o } = this.getTypeStyles(), r = this.href || null, i = this.getChildren(o); return r ? jt({ href: r, class: `${e} ${s}` }, i) : Et({ close: this.close.bind(this), class: `${e} ${s}` }, i); } /** * This will be called after the component is set up. * * @returns {void} */ afterSetup() { const e = this.duration; e !== "infinite" && (this.timer = new ht(e, this.close.bind(this)), this.timer.start()); } /** * This will get the style properties based on the notification type. * * @returns {object} */ getTypeStyles() { const e = this.type || "default"; return j[e] || j.default; } /** * This will get the buttons for the notification. * * @returns {array} */ getButtons() { return [ n({ class: "flex flex-row mt-6 gap-2" }, [ this.secondary && h({ variant: "outline", click: () => this.secondaryAction && this.secondaryAction() }, this.secondary), this.primary && h({ click: () => this.primaryAction && this.primaryAction() }, this.primary) ]) ]; } /** * This will get the children for the notification. * * @param {string} iconColor * @returns {array} */ getChildren(e) { return [ n({ class: "flex items-start" }, [ this.icon && x({ class: `mr-4 ${e}`, html: this.icon }), n({ class: "flex flex-auto flex-col" }, [ n({ class: "flex flex-auto flex-row items-center w-full pr-12" }, [ this.title && zt(this.title) ]), b({ class: "text-base text-muted-foreground m-0 pr-12" }, this.description), (this.primary || this.secondary) && L({ class: "margin-top-24 flex align-center" }, this.getButtons()) ]) ]), h({ class: "absolute top-[12px] right-[12px]", variant: "icon", icon: u.x, click: this.close.bind(this) }) ]; } /** * This will close the notification. * * @param {object} e The event object. * @returns {void} */ close(e) { e && e.stopPropagation(), this.duration !== "infinite" && this.timer.stop(), this.onClick && this.onClick(), this.destroy(); } } let Ut = 0; class Ve extends w { /** * This will render the component. * * @returns {object} */ render() { return n({ class: "notification-container pointer-events-none inset-auto bg-transparent backdrop:bg-transparent overflow-visible fixed bottom-[80px] right-0 z-50 p-5", popover: "manual" }, [ new mt({ cache: "list", key: "id", role: "list", rowItem: (e) => new Nt(e) }) ]); } /** * This will add a notification. * * @param {object} props * @returns {void} */ addNotice(e = {}) { e.id = Ut++, e.callBack = () => this.removeNotice(e), this.list.append([e]), this.panel.hidePopover(), this.panel.showPopover(); } /** * This will remove a notification. * * @param {object} notice * @returns {void} */ removeNotice(e) { this.list.delete(e.id), this.list.isEmpty() && this.panel.hidePopover(); } } const E = globalThis.matchMedia, F = c(({ value: t, label: e, icon: s }) => f({ class: 'text-sm gap-1 font-medium leading-none disabled:cursor-not-allowed disabled:opacity-70 flex flex-col items-center justify-between rounded-md border-2 bg-popover p-4 hover:bg-accent hover:text-accent-foreground data-[state="active"]:border-primary [&:has([data-state="active"])]:border-primary', onState: ["method", { active: t }], dataSet: ["method", ["state", t, "active"]], click: (o, { state: r }) => { r.method = t, localStorage.setItem("theme", t), t === "system" && localStorage.removeItem("theme"), Rt(t); } }, [ g(s), a(e) ])), Rt = (t) => { const e = document.documentElement; if (t === "system" && (t = globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light"), E && !E("(prefers-color-scheme: " + t + ")").matches) { e.classList.add(t); return; } const s = t === "light" ? "dark" : "light"; e.classList.remove(s); }, Ge = C( { /** * This will render the component. * * @returns {object} */ render() { return n({ class: "flex flex-auto flex-col" }, [ n({ class: "grid grid-cols-3 gap-4" }, [ F({ label: "System", value: "system", icon: u.adjustments.horizontal }), F({ label: "Light", value: "light", icon: u.sun }), F({ label: "Dark", value: "dark", icon: u.moon }) ]) ]); }, /** * This will setup the states. * * @returns {object} */ state() { return { method: globalThis.localStorage.getItem("theme") ?? "system" }; } } ), We = v( { /** * The initial state of the Toggle. * * @returns {object} */ state() { return { active: this.active ?? !1 }; }, /** * This is added to check the checkbox after the component is rendered. * to see if the bind updated the checked value. * * @returns {void} */ after() { this.state.active = this.checkbox.checked; }, /** * Renders the Toggle component. * * @returns {object} */ render() { return f({ class: "relative inline-flex h-6 w-11 min-w-11 items-center rounded-full bg-muted transition-colors focus:outline-none", onState: ["active", { "bg-primary": !0, "bg-muted": !1 }], click: (e, { state: s }) => { s.toggle("active"), this.checkbox.checked = s.active, this.change && this.change(s.active, e, this); } }, [ st({ cache: "checkbox", class: "opacity-0 absolute top-0 left-0 bottom-0 right-0 w-full h-full", /** * This will add the default checked before binding. * If binding it will override the default checked value. */ checked: this.state.active, bind: this.bind, required: this.required }), a({ class: "absolute h-5 w-5 bg-background rounded-full shadow-md transform transition-transform", onState: ["active", { "translate-x-[22px]": !0, "translate-x-[2px]": !1 }] }) ]); } } ), k = { ONLINE: "online", OFFLINE: "offline", BUSY: "busy", AWAY: "away" }, y = { ONLINE: "bg-green-500", OFFLINE: "bg-gray-500", BUSY: "bg-red-500", AWAY: "bg-yellow-500" }, Ht = (t = "") => (t = t.toUpperCase(), y[t] || y.OFFLINE), _e = (t) => n({ class: `absolute bottom-0 right-0 w-3 h-3 border-2 rounded-full ${Ht(t)}` }), Je = ({ propName: t = "status" } = {}) => n({ class: "absolute bottom-0 right-0 w-3 h-3 border-2 rounded-full", onSet: [t, { [y.ONLINE]: k.ONLINE, [y.OFFLINE]: k.OFFLINE, [y.BUSY]: k.BUSY, [y.AWAY]: k.AWAY }] }), qt = (t) => t.indexOf(".") === -1 && t.indexOf("[[") === -1 && t.indexOf("blob:") === -1, Ke = C( { /** * Get the initial state for the component. * * @returns {object} Initial state for the component */ state() { return { // @ts-ignore loaded: !!this.src }; }, /** * This will open the file browse dialog. * * @returns {void} */ openFileBrowse() { const t = this.input; t && (t.value = "", p.trigger("click", t)); }, /** * Get the URL for the uploaded file. * * @param {File} file - The file to get the URL for. * @returns {string} The object URL for the file. */ getFileUrl(t) { return this.url && URL.revokeObjectURL(this.url), this.url = URL.createObjectURL(t); }, /** * Render the component. * * @returns {object} Rendered component */ render() { const t = "image-upload", e = this.onChange || null; return n({ class: "flex-none items-center" }, [ m({ id: t, cache: "input", type: "file", accept: "image/*", class: "hidden", change: (s) => { const o = s.target.files?.[0]; o && e && (this.state.loaded = !1, e(o, this.parent), this.src = this.getFileUrl(o), this.state.loaded = !0); } }), n({ class: "relative w-32 h-32 rounded-full border flex items-center justify-center cursor-pointer hover:bg-muted transition-colors duration-150 overflow-hidden group", click: (s) => { s.preventDefault(), s.stopPropagation(), this.openFileBrowse(); } }, [ T("loaded", (s) => s === !1 || qt(this.src) ? P({ htmlFor: t, class: "z-10 flex flex-col items-center justify-center text-sm text-muted-foreground group-hover:text-primary" }, [ g(u.upload), n("Upload Image") ]) : G({ // @ts-ignore src: this.src, class: "absolute inset-0 w-full h-full object-cover rounded-full" })) ]) ]); } } ), Qe = C( { /** * Get the initial state for the component. * * @returns {object} Initial state for the component */ state() { return { // @ts-ignore loaded: !!this.src }; }, /** * This will open the file browse dialog. * * @returns {void} */ openFileBrowse() { const t = this.input; t && (t.value = "", p.trigger("click", t)); }, /** * Get the URL for the uploaded file. * * @param {File} file - The file to get the URL for. * @returns {string} The object URL for the file. */ getFileUrl(t) { return this.url && URL.revokeObjectURL(this.url), this.url = URL.createObjectURL(t); }, /** * Render the component. * * @returns {object} Rendered component */ render() { const t = "logo-upload", e = this.onChange || null; return n({ class: "flex-none items-center" }, [ m({ id: t, cache: "input", type: "file", accept: "image/*", class: "hidden", change: (s) => { const o = s.target.files?.[0]; o && e && (this.state.loaded = !1, e(o, this.parent), this.src = this.getFileUrl(o), this.state.loaded = !0); } }), n({ class: "relative w-32 h-32 rounded-full border flex items-center justify-center cursor-pointer hover:bg-muted transition-colors duration-150 overflow-hidden group", click: (s) => { s.preventDefault(), s.stopPropagation(), this.openFileBrowse(); } }, [ T( "loaded", (s) => s ? G({ // @ts-ignore src: this.src, class: "absolute inset-0 w-full h-full object-cover rounded-full" }) : P({ htmlFor: t, class: "z-10 flex flex-col items-center justify-center text-sm text-muted-foreground group-hover:text-primary" }, [ g(u.upload), n("Upload logo") ]) ) ]) ]); } } ), Vt = (t, e) => V( { href: t, "aria-current": e === "Breadcrumb" && "page", // Only set aria-current on the last item class: "text-muted-foreground font-medium hover:text-foreground" }, [a(e)] ), Gt = () => g({ class: "mx-3 text-muted-foreground", "aria-hidden": !0, size: "xs" }, u.chevron.single.right), Wt = (t) => n({ class: "flex items-center" }, [ t.href ? Vt(t.href, t.label) : a(t.label), t.separator && Gt() ]), Xe = C( { /** * Set initial data * * @returns {Data} */ setData() { return new B({ // @ts-ignore items: this.items || [] }); }, /** * Render Breadcrumb Component * * @returns {object} */ render() { const t = this.data.items.length - 1; return ot( { "aria-label": "Breadcrumb", class: "flex items-center gap-x-1 text-sm" }, [ n({ role: "list", class: "flex items-center", for: ["items", (e, s) => Wt({ href: e.href, label: e.label, separator: s < t })] }) ] ); } } ), N = { xs: "h-1 w-1", sm: "h-2 w-2", md: "h-4 w-4", lg: "h-8 w-8", xl: "h-12 w-12", "2xl": "h-16 w-16", "3xl": "h-24 w-24", default: "h-4 w-4" }, _t = (t) => N[t] || N.default, Jt = ({ index: t, size: e }) => n({ class: `${e} rounded-full bg-muted cursor-pointer` }, [ a({ class: "block w-full h-full rounded-full transition-colors", onSet: ["activeIndex", { "bg-primary": t, "shadow-md": t }], click: (s, { data: o, onClick: r }) => { o.activeIndex = t, r && r(t); } }) ]), Kt = (t, e) => Array.from({ length: t }, (s, o) => Jt({ index: o, size: e })), Ze = C( { /** * Defines component data (props). * * @returns {Data} */ setData() { return new B({ // @ts-ignore count: this.count || 4, // total dots // @ts-ignore activeIndex: this.activeIndex || 0 }); }, /** * Renders the dots. * * @returns {object} */ render() { const t = this.gap || "gap-2", e = _t(this.size || "sm"), s = Kt(this.data.count, e); return n( { class: "flex justify-center items-center py-2" }, [ n({ class: `flex ${t}` }, s) ] ); } } ), Qt = ({ toggleDropdown: t }) => f( { cache: "button", class: "relative z-[2] inline-flex items-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border bg-input hover:bg-muted h-10 px-4 py-2 justify-between", click: t }, [ a({ onState: ["selectedLabel", (e) => e || "Select item..."] }), x({ html: u.chevron.upDown }) ] ), Xt = (t, e, s) => H({ class: "flex flex-auto items-center cursor-pointer p-2 hover:bg-muted/50 rounded-sm", click: () => e(t), onState: [s, "selectedValue", { "bg-secondary": t.value }] }, [ t.icon && a({ class: "mr-2 flex items-baseline" }, [g({ size: "xs" }, t.icon)]), a({ class: "text-base font-normal" }, t.label) ]), Zt = (t, e) => n({ class: "w-full border rounded-md" }, [ q({ class: "max-h-60 overflow-y-auto p-2 grid gap-1", for: ["items", (s) => Xt(s, t, e)] }) ]), te = ({ onSelect: t, state: e }) => n({ class: "flex flex-auto flex-col" }, [ T( "open", (s, o, r) => s ? new D({ cache: "dropdown", parent: r, button: r.button }, [ Zt(t, e) ]) : null ) ]), ts = C( { /** * This will set up the data. * * @returns {Data} */ setData() { return new B({ // @ts-ignore items: this.items || [] }); }, /** * This will set up the states. * * @returns {object} */ state() { return { open: !1, selectedLabel: "", selectedValue: "" }; }, /** * This will set the state item. * * @param {object} item * @returns {void} */ setStateItem(t) { const e = this.state; e.selectedValue = t.value, e.selectedLabel = t.label, e.open = !1; }, /** * Handles the selection of an item. * * @param {object} item * @returns {void} */ select(t) { this.setStateItem(t), typeof this.onSelect == "function" && this.onSelect(t, this.parent); }, /** * Selects the first item in the list. * * @returns {void} */ selectFirstItem() { const t = this.data.items[0]; this.select(t); }, /** * Toggles the dropdown open state. * * @returns {void} */ toggleDropdown() { this.state.toggle("open"); }, /** * This will run after the component is set up. * * @returns {void} */ after() { if (this.selectFirst === !0 && this.state.selectedValue === "") { const t = this.data.items[0]; t && this.setStateItem(t); } }, /** * This will render the component. * * @returns {object} */ render() { const t = this.class || "", e = this.maxWidth || "max-w-[250px]", s = this.width || "w-full"; return n({ class: `relative ${s} flex flex-auto flex-col ${e} ${t}` }, [ // @ts-ignore Qt({ toggleDropdown: this.toggleDropdown.bind(this) }), te({ // @ts-ignore state: this.state, // @ts-ignore onSelect: this.select.bind(this) }), // Hidden required input for form validation // @ts-ignore this.required && m({ class: "opacity-0 absolute top-0 left-0 z-[1]", type: "text", // @ts-ignore name: this.name, required: !0, // @ts-ignore value: ["[[selectedValue]]", this.state] }) ]); } } ), X = ({ icon: t, click: e, ariaLabel: s }) => h({ variant: "icon", class: "flex flex-none", click: e, icon: t, "aria-label": s }), ee = ({ click: t }) => X({ icon: u.circleMinus, click: t, ariaLabel: "Decrement" }), se = ({ click: t }) => X({ icon: u.circlePlus, click: t, ariaLabel: "Increment" }), oe = ({ bind: t, min: e, max: s, readonly: o = !1 }) => W(({ state: r }) => m({ value: ["[[count]]", r], bind: t, blur: (i, { state: l }) => { let d = parseInt(i.target.value, 10); isNaN(d) && (d = e ?? 0), e !== void 0 && (d = Math.max(d, e)), s !== void 0 && (d = Math.min(d, s)), l.count = d; }, class: "flex flex-auto text-lg font-medium bg-input text-center border min-w-0", readonly: o, min: e, max: s, type: "number", "aria-label": "Counter" })), es = v( { /** * Initial state for the counter component. * * @member {object} state */ state() { return { count: { state: this.initialCount ?? 0, callBack: (t) => this.change && this.change(t) } }; }, /** * Renders the Counter component. * * @returns {object} */ render() { const t = this.class ?? ""; return n({ class: `flex flex-auto items-center justify-between gap-x-4 p-4 ${t}` }, [ ee({ click: () => this.state.decrement("count") }), oe({ bind: this.bind, readonly: this.readonly, min: this.min, max: this.max }), se({ click: () => this.state.increment("count") }) ]); } } ), ne = (t) => { let e = ""; return t.length > 0 && (e = t.substring(0, 2), t.length > 2 && (e += "/" + t.substring(2, 4), t.length > 4 && (e += "/" + t.substring(4, 8)))), e; }, re = (t) => { if (t.length !== 8) return null; const e = parseInt(t.substring(0, 2), 10), s = parseInt(t.substring(2, 4), 10), o = parseInt(t.substring(4, 8), 10); return e >= 1 && e <= 12 && s >= 1 && s <= 31 && o >= 1900 ? `${o}-${e.toString().padStart(2, "0")}-${s.toString().padStart(2, "0")}` : null; }, ie = ({ bind: t, required: e }) => m({ cache: "input", class: "opacity-0 absolute top-0 left-0 w-full h-full pointer-events-none", bind: t, required: e }), le = ({ placeholder: t, handleInputChange: e, handleInputFocus: s }) => m({ cache: "dateInput", class: "flex-1 bg-transparent outline-none placeholder:text-muted-foreground border-0", placeholder: t || "mm/dd/yyyy", input: e, focus: s, onState: ["selectedDate", (o) => o ? M.format("standard", o) : ""] }), ae = ({ bind: t, required: e, toggleOpen: s, handleInputChange: o, handleInputFocus: r, placeholder: i }) => n( { class: "relative flex items-center gap-2 w-full justify-between border bg-input hover:bg-muted rounded-md h-10 pr-4 py-2" }, [ ie({ bind: t, required: e }), le({ placeholder: i, handleInputChange: o, handleInputFocus: r }), f( { class: "flex-shrink-0 hover:bg-muted/50 rounded p-1 cursor-pointer", click: s }, [ x({ html: u.calendar.days }) ] ) ] ), ce = ({ handleDateSelect: t, blockPriorDates: e }) => O( (s, o, r) => new D({ cache: "dropdown", parent: r, button: r.panel, size: "fit" }, [ new at({ selectedDate: r.state.selectedDate, selectedCallBack: t, blockPriorDates: e }) ]) ), ss = v( { /** * The initial state of the DatePicker. * * @member {object} state */ state() { return { selectedDate: this.selectedDate ?? null, open: !1 }; }, /** * This is added to check the input after the component is rendered. * to see if the bind updated the input value. * * @returns {void} */ after() { this.input.value && (this.state.selectedDate = this.input.value); }, /** * Handles direct input changes and formats the date. * * @param {Event} e */ handleInputChange(t) { const s = t.target.value.replace(/\D/g, ""); t.target.value = ne(s); const o = re(s); o && (this.state.selectedDate = o, this.input.value = o, p.trigger("change", this.input), typeof this.onChange == "function" && this.onChange(o)); }, /** * Handles input focus - select all text for easy editing. * * @param {Event} e */ handleInputFocus(t) { t.target.select(); }, /** * Handles date selection from calendar. * * @param {string} date */ handleDateSelect(t) { this.state.selectedDate = t, this.state.open = !1, this.input.value = t, p.trigger("change", this.input), typeof this.onChange == "function" && this.onChange(t); }, /** * Toggles the calendar popover. * * @param {Event} e * @param {object} context */ toggleOpen(t, { state: e }) { e.toggle("open"); }, /** * Renders the DatePicker component. * * @returns {object} */ render() { return n( { class: "relative w-full max-w-[320px]" }, [ ae({ toggleOpen: this.toggleOpen.bind(this), bind: this.bind, required: this.required, handleInputChange: this.handleInputChange.bind(this), handleInputFocus: this.handleInputFocus.bind(this), placeholder: this.placeholder }), ce({ handleDateSelect: this.handleDateSelect.bind(this), blockPriorDates: this.blockPriorDates || !1 }) ] ); } } ), de = ({ bind: t, required: e }) => m({ cache: "input", class: "opacity-0 absolute top-0 left-0 w-full h-full pointer-events-none", bind: t, required: e }), ue = ({ bind: t, required: e, toggleOpen: s }) => f({ class: "relative flex items-center gap-2 w-full justify-between border bg-input hover:bg-muted rounded-md h-10 px-4 py-2", click: s }, [ de({ bind: t, required: e }), W(({ state: o }) => [ a(Y.date(["[[start]]", o], "Start Date")), a(" - "), a(Y.date(["[[end]]", o], "End Date")) ]), x({ html: u.calendar.days }) ]), he = ({ handleDateSelect: t, blockPriorDates: e }) => O((s, o, r) => new D({ cache: "dropdown", parent: r, button: r.panel, size: "xl" }, [ new ct({ startDate: r.state.start, endDate: r.state.end, onRangeSelect: t, blockPriorDates: e }) ])), os = v( { /** * The initial state of the DateRangePicker. * * @member {object} state */ state() { return { start: this.start ?? null, end: this.end ?? null, open: !1 }; }, /** * This is added to check the input after the component is rendered. * to see if the bind updated the input value. * * @returns {void} */ after() { if (this.input.value) { const t = this.input.value.split("-"); this.state.start = t[0], this.state.end = t[1]; } }, /** * Renders the DatePicker component. * * @returns {object} */ render() { const t = (s, { state: o }) => o.toggle("open"), e = (s, o) => { this.state.start = s, this.state.end = o, this.state.open = !1, this.input.value = `${s}-${o}`, p.trigger("change", this.input), typeof this.onChange == "function" && this.onChange({ start: s, end: o }, this.parent); }; return n({ class: "relative w-full max-w-[320px]" }, [ ue({ toggleOpen: t, bind: this.bind, required: this.required }), he({ handleDateSelect: e, blockPriorDates: this.blockPriorDates || !1 }) ]); } } ), ns = ({ dateTime: t = "", remoteTimeZone: e = "America/Denver", filter: s = null }) => nt([ new ft({ dateTime: t, filter: s || ((o) => { const r = M.getLocalTime(o, !0, !1, e); return M.getTimeFrame(r); }) }) ]); function me(t) { let e = ""; return t.length > 0 && (e = t.substring(0, 2), t.length > 2 && (e += ":" + t.substring(2, 4))), e; } function fe(t) { if (t.length < 4) return { formattedTime: null, hour: null, minute: null, meridian: null }; const e = parseInt(t.substring(0, 2), 10), s = parseInt(t.substring(2, 4), 10); if (e < 0 || e > 23 || s < 0 || s > 59) return { formattedTime: null, hour: null, minute: null, meridian: null }; let o = e, r = "AM"; return e === 0 ? o = 12 : e > 12 ? (o = e - 12, r = "PM") : e === 12 && (r = "PM"), { formattedTime: `${o.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")} ${r}`, hour: o.toString().padStart(2, "0"), minute: s.toString().padStart(2, "0"), meridian: r }; } function ge({ bind: t, required: e }) { return m({ cache: "input", class: "opacity-0 absolute top-0 left-0 w-full h-full pointer-events-none", bind: t, required: e }); } function pe({ placeholder: t, handleInputChange: e, handleInputFocus: s }) { return m({ cache: "timeInput", class: "flex-1 bg-transparent outline-none placeholder:text-muted-foreground border-0", placeholder: t || "hh:mm AM/PM", input: e, focus: s, onState: ["selectedTime", (o) => o || ""] }); } function be({ bind: t, required: e, toggleOpen: s, handleInputChange: o, handleInputFocus: r, placeholder: i }) { return n( { class: "relative flex items-center gap-2 w-full justify-between border bg-input hover:bg-muted rounded-md h-10 pr-4 py-2" }, [ ge({ bind: t, required: e }), pe({ placeholder: i, handleIn