diff --git a/internal/ui/app.ts b/internal/ui/app.ts index 9575ab3..44745db 100644 --- a/internal/ui/app.ts +++ b/internal/ui/app.ts @@ -1,6 +1,9 @@ import "htmx.org"; import Alpine from "alpinejs"; +import initToastComponent from "./components/toast/toast"; window.Alpine = Alpine; +initToastComponent(); + Alpine.start(); diff --git a/internal/ui/components/toast/toast.templ b/internal/ui/components/toast/toast.templ index 48f1540..38e88e3 100644 --- a/internal/ui/components/toast/toast.templ +++ b/internal/ui/components/toast/toast.templ @@ -3,52 +3,37 @@ package toast const toastContainerID = "toast-container" templ Container() { -
+
} -templ toast() { -
-
+templ toast(toastType string) { +
+ { children... } -
+
} templ success(msg string) { - @toast() { -
- { msg } -
+ @toast("success") { + { msg } } } templ info(msg string) { - @toast() { -
- { msg } -
+ @toast("info") { + { msg } } } templ warning(msg string) { - @toast() { -
- { msg } -
+ @toast("warning") { + { msg } } } templ err(msg string) { - @toast() { -
- { msg } -
+ @toast("error") { + { msg } } } diff --git a/internal/ui/components/toast/toast.ts b/internal/ui/components/toast/toast.ts index 3cde401..813a76e 100644 --- a/internal/ui/components/toast/toast.ts +++ b/internal/ui/components/toast/toast.ts @@ -1,23 +1,201 @@ +const ALERT_TYPE_CLASSES: Record = { + "success": "alert-success", + "info": "alert-info", + "warning": "alert-warning", + "error": "alert-error", +} + +const DEFAULT_TOAST_DURATION = 5000; +const TOAST_SWIPE_VELOCITY_THRESHOLD = window.screen.availWidth / 2; // px / s + +// recreating: +// x-data="{ +// timeoutId: 0, +// close() { $el.remove();} +// }" +// x-init="timeoutId = setTimeout(() => { close() }, 5000);" +// @click="clearTimeout(timeoutId); close();" +// +// additional features: progress bar, pause on hover, swiping away on mobile + class Toast extends HTMLElement { - constructor() { - super(); - this.attachShadow({ mode: "open" }); + constructor() { + super(); + this.#children = Array.from(this.children).map(el=>el.cloneNode(true)); + } + + #progressAnimation: Animation | undefined; + #lastTouchEvent: TouchEvent | undefined; + // [px, timestamp] + #lastTouchChanges: number[][] = []; + #children: Node[]; + + #getAlertElement = (): HTMLElement => { + return this.children[0]; + } + + #parseLeft = (): number => { + const currentLeft = this.#getAlertElement().style.left; + if(currentLeft !== ""){ + return parseInt(currentLeft.slice(0, -2)) + } + return 0; + } + + connectedCallback() { + this.render(); + + const durationAttr = this.getAttribute("duration") || ""; + let toastDuration = parseInt(durationAttr); + if (Number.isNaN(toastDuration)) { + toastDuration = DEFAULT_TOAST_DURATION; + } + + this.#progressAnimation = this.querySelector(".toast-alert-progress")!.animate([ + { + width: "0%" + }, + { + width: "100%" + } + ], { + duration: toastDuration, + fill: "forwards" + }); + + this.#progressAnimation?.addEventListener("finish", ()=>this.triggerClose(0)); + + this.addEventListener("mouseenter", this.onMouseEnter); + this.addEventListener("mouseleave", this.onMouseLeave); + this.addEventListener("click", ()=>this.triggerClose(0)); + this.addEventListener("touchstart", this.onTouchStart); + this.addEventListener("touchmove", this.onTouchMove); + this.addEventListener("touchend", this.onTouchEnd); + } + + disconnectedCallback(){ + this.removeEventListener("mouseenter", this.onMouseEnter); + this.removeEventListener("mouseleave", this.onMouseLeave); + this.removeEventListener("click", ()=>this.triggerClose(0)); + this.removeEventListener("touchstart", this.onTouchStart); + this.removeEventListener("touchmove", this.onTouchMove); + this.removeEventListener("touchend", this.onTouchEnd); + } + + onMouseEnter = () => { + this.#progressAnimation?.pause(); + } + + onMouseLeave = () => { + this.#progressAnimation?.play(); + } + + onTouchStart = (e: TouchEvent) => { + this.#lastTouchEvent = e; + this.#lastTouchChanges = []; + this.#progressAnimation?.pause(); + } + + onTouchMove = (e: TouchEvent) => { + let diffX = e.changedTouches[0].clientX - this.#lastTouchEvent!.changedTouches[0].clientX; + + // reset lastTouchChanges when the direction changes so that swiping in one direction and then another still closes + // the toast + if(this.#lastTouchChanges.length > 0 && this.#lastTouchChanges[this.#lastTouchChanges.length - 1][0] * diffX < 0){ + this.#lastTouchChanges = []; } - connectedCallback() { - this.render(); + // saving x traveled and current timestamp for velocity calculations + this.#lastTouchChanges.push([diffX, performance.now()]); + + this.#lastTouchEvent = e; + this.#lastTouchChanges = this.#lastTouchChanges.slice(-5); + this.#getAlertElement().style.left = `${diffX + this.#parseLeft()}px`; + } + + onTouchEnd = () => { + const left = this.#parseLeft(); + if(this.#lastTouchChanges.length > 0){ + // calculate total x movement + const x = this.#lastTouchChanges.reduce((cur, prev) => cur + prev[0], 0); + // calculate timespan + const t = performance.now() - this.#lastTouchChanges[0][1]; + // calculate velocity + const v = x / t; + + if(Math.abs(v * 1000) >= TOAST_SWIPE_VELOCITY_THRESHOLD) return this.triggerClose(v); } - render() { - this.shadowRoot.innerHTML = ` - -
- -
- `; + this.#progressAnimation?.play(); + this.#getAlertElement().style.left = ""; + this.#getAlertElement().animate([ + { + left: `${left}px` + }, + { + left: `0px` + } + ], 250); + } + + triggerClose = (velocity: number) => { + if(!this.#progressAnimation) return; + + // we dont want to close the toast if there is text selected + const sel = window.getSelection(); + if(sel && sel.rangeCount > 0 && sel.type === "Range"){ + if(this.contains(sel?.focusNode)) return; + for(let i = 0; i < sel.rangeCount; i++){ + if(this.contains(sel.getRangeAt(i).startContainer)) return; + } } + + this.#progressAnimation = undefined; + + const left = this.#parseLeft(); + const animationDuration = 250; + + this.#getAlertElement().animate([ + { + left: `${left}px` + }, + { + // velocity is in px per ms + left: `${left + (velocity * animationDuration)}px` + } + ], { + duration: animationDuration, + fill: "forwards" + }); + this.animate([ + { + scale: 1, + opacity: 1 + }, + { + scale: 0.9, + opacity: 0 + } + ], { + duration: animationDuration, + easing: "ease-out", + fill: "forwards" + }).addEventListener("finish", () => { + this.remove(); + }); + } + + render() { + const alertType = this.getAttribute('type') || ""; + this.innerHTML = ` +
+
+
+ `; + this.#getAlertElement().prepend(...this.#children); + } } -customElements.define("toast-container", Toast); +export default function initToastComponent() { + customElements.define("toast-element", Toast); +} diff --git a/internal/ui/components/toast/toast_templ.go b/internal/ui/components/toast/toast_templ.go index bd780a6..6da5ab8 100644 --- a/internal/ui/components/toast/toast_templ.go +++ b/internal/ui/components/toast/toast_templ.go @@ -44,7 +44,7 @@ func Container() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" class=\"toast toast-top toast-center\">
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -52,7 +52,7 @@ func Container() templ.Component { }) } -func toast() templ.Component { +func toast(toastType string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -86,7 +86,20 @@ func toast() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" hx-swap-oob=\"afterbegin\">
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" class=\"toast-container\" hx-swap-oob=\"afterbegin\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -94,7 +107,7 @@ func toast() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -118,12 +131,12 @@ func success(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var5 := templ.GetChildren(ctx) - if templ_7745c5c3_Var5 == nil { - templ_7745c5c3_Var5 = templ.NopComponent + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -135,26 +148,26 @@ func success(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(msg) + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(msg) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 27, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 19, Col: 19} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = toast().Render(templ.WithChildren(ctx, templ_7745c5c3_Var6), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = toast("success").Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -178,12 +191,12 @@ func info(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var8 := templ.GetChildren(ctx) - if templ_7745c5c3_Var8 == nil { - templ_7745c5c3_Var8 = templ.NopComponent + templ_7745c5c3_Var9 := templ.GetChildren(ctx) + if templ_7745c5c3_Var9 == nil { + templ_7745c5c3_Var9 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var10 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -195,26 +208,26 @@ func info(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(msg) + var templ_7745c5c3_Var11 string + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(msg) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 35, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 25, Col: 19} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = toast().Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = toast("info").Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -238,12 +251,12 @@ func warning(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var11 := templ.GetChildren(ctx) - if templ_7745c5c3_Var11 == nil { - templ_7745c5c3_Var11 = templ.NopComponent + templ_7745c5c3_Var12 := templ.GetChildren(ctx) + if templ_7745c5c3_Var12 == nil { + templ_7745c5c3_Var12 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var12 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var13 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -255,26 +268,26 @@ func warning(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(msg) + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(msg) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 43, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 31, Col: 19} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = toast().Render(templ.WithChildren(ctx, templ_7745c5c3_Var12), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = toast("warning").Render(templ.WithChildren(ctx, templ_7745c5c3_Var13), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -298,12 +311,12 @@ func err(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var14 := templ.GetChildren(ctx) - if templ_7745c5c3_Var14 == nil { - templ_7745c5c3_Var14 = templ.NopComponent + templ_7745c5c3_Var15 := templ.GetChildren(ctx) + if templ_7745c5c3_Var15 == nil { + templ_7745c5c3_Var15 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var15 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var16 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -315,26 +328,26 @@ func err(msg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var16 string - templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(msg) + var templ_7745c5c3_Var17 string + templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(msg) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 51, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/ui/components/toast/toast.templ`, Line: 37, Col: 19} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = toast().Render(templ.WithChildren(ctx, templ_7745c5c3_Var15), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = toast("error").Render(templ.WithChildren(ctx, templ_7745c5c3_Var16), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/ui/styles.css b/internal/ui/styles.css index e4f365c..b0af83b 100644 --- a/internal/ui/styles.css +++ b/internal/ui/styles.css @@ -3,6 +3,33 @@ themes: light --default, dark --prefersdark; } +/*alert-[status] isn't found in source very easily, so best to force add to css*/ +/*reference: https://tailwindcss.com/docs/detecting-classes-in-source-files#safelisting-specific-utilities*/ +@source inline("alert-success"); +@source inline("alert-warning"); +@source inline("alert-error"); +@source inline("alert-info"); + [x-cloak] { display: none !important; } + +@layer components{ + .toast-alert{ + @apply alert alert-vertical sm:alert-horizontal relative overflow-hidden; + } + + .toast-alert-progress{ + @apply h-0.5 absolute; + + + bottom: 0; + left: 0; + z-index: 10; + background: currentColor; + } + + #toast-container{ + @apply toast sm:toast-top flex-col-reverse sm:flex-col toast-center z-50; + } +} diff --git a/package-lock.json b/package-lock.json index d73caac..585c6ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@tailwindcss/cli": "^4.1.12", "@types/alpinejs": "^3.13.11", "bun": "^1.2.21", - "daisyui": "^5.0.51", + "daisyui": "^5.4.4", "tailwindcss": "^4.1.12" } }, @@ -923,9 +923,9 @@ } }, "node_modules/daisyui": { - "version": "5.0.51", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.0.51.tgz", - "integrity": "sha512-lhB0BBOjt43/t5S1my0XChMy3ClXfmGlDU/XmSlx+N0h2y7cyWF+cnheeemguxNHb9TjqI66mxKI9qiFsOU3mA==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.4.4.tgz", + "integrity": "sha512-FEfdwmGdb3ZbtYN3OqaZn/RMarlZTPOv6mWpGUo7KX42RkcBanEZk5tgYp2ZgirKl7QvH8pCiwpuy55wi2dHyQ==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index f1c911c..54c66bb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@tailwindcss/cli": "^4.1.12", "@types/alpinejs": "^3.13.11", "bun": "^1.2.21", - "daisyui": "^5.0.51", + "daisyui": "^5.4.4", "tailwindcss": "^4.1.12" }, "dependencies": { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..76c0e8b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +}