@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
JavaScript
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