Migrate more stuff

This commit is contained in:
Jon Staab
2025-02-03 16:37:14 -08:00
parent 0f705c459a
commit 8d3433b167
150 changed files with 2001 additions and 1205 deletions
+4 -7
View File
@@ -2,13 +2,11 @@
import {onMount} from "svelte"
import Icon from "@lib/components/Icon.svelte"
export let src = ""
export let size = 7
export let icon = "user-rounded"
let {src = "", size = 7, icon = "user-rounded", style = "", ...restProps} = $props()
let element: HTMLElement
$: rem = size * 4
const rem = $derived(size * 4)
onMount(() => {
if (src) {
@@ -25,8 +23,7 @@
<div
bind:this={element}
class="{$$props.class} relative !flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-cover bg-center"
style="width: {rem}px; height: {rem}px; min-width: {rem}px; background-image: url({src}); {$$props.style ||
''}">
class="{restProps.class} relative !flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-cover bg-center"
style="width: {rem}px; height: {rem}px; min-width: {rem}px; background-image: url({src}); {style}">
<Icon {icon} class={src ? "hidden" : ""} size={Math.round(size * 0.8)} />
</div>
+8 -4
View File
@@ -1,15 +1,19 @@
<script lang="ts">
export let type: "button" | "submit" = "button"
interface Props {
type?: "button" | "submit"
}
$: className = `text-left ${$$props.class}`
let {type = "button", ...restProps} = $props()
const className = $derived(`text-left ${restProps.class}`)
</script>
{#if type === "submit"}
<button {...$$props} {type} class={className}>
<button {...restProps} {type} class={className}>
<slot />
</button>
{:else}
<button on:click|stopPropagation|preventDefault {...$$props} {type} class={className}>
<button on:click|stopPropagation|preventDefault {...restProps} {type} class={className}>
<slot />
</button>
{/if}
+12 -4
View File
@@ -1,18 +1,26 @@
<script lang="ts">
import Icon from "@lib/components/Icon.svelte"
interface Props {
icon?: import("svelte").Snippet
title?: import("svelte").Snippet
info?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div class="btn btn-neutral flex h-[unset] w-full flex-nowrap py-4 text-left {$$props.class}">
<div class="btn btn-neutral flex h-[unset] w-full flex-nowrap py-4 text-left {props.class}">
<div class="flex flex-grow flex-row items-start gap-1 sm:pl-2">
<div class="flex h-14 w-12 flex-shrink-0 items-center">
<slot name="icon" />
{@render props.icon?.()}
</div>
<div class="flex flex-col gap-1">
<p class="text-bold text-lg">
<slot name="title" />
{@render props.title?.()}
</p>
<p class="text-sm">
<slot name="info" />
{@render props.info?.()}
</p>
</div>
</div>
+14 -6
View File
@@ -1,27 +1,35 @@
<script lang="ts">
import {slide} from "@lib/transition"
import Icon from "@lib/components/Icon.svelte"
interface Props {
title?: import("svelte").Snippet
description?: import("svelte").Snippet
children?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
const toggle = () => {
isOpen = !isOpen
}
let isOpen = false
let isOpen = $state(false)
</script>
<div class="relative flex flex-col gap-4 {$$props.class}">
<div class="relative flex flex-col gap-4 {props.class}">
<button
type="button"
class="absolute right-8 top-8 h-4 w-4 cursor-pointer transition-all"
class:rotate-90={!isOpen}
on:click={toggle}>
onclick={toggle}>
<Icon icon="alt-arrow-down" />
</button>
<slot name="title" />
<slot name="description" />
{@render props.title?.()}
{@render props.description?.()}
{#if isOpen}
<div transition:slide>
<slot />
{@render props.children?.()}
</div>
{/if}
</div>
+18 -8
View File
@@ -1,16 +1,22 @@
<script lang="ts">
import {preventDefault} from "svelte/legacy"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
export let title = "Are you sure?"
export let subtitle = ""
export let message
export let confirm
interface Props {
title?: string
subtitle?: string
message: any
confirm: any
}
let loading = false
let {title = "Are you sure?", subtitle = "", message, confirm}: Props = $props()
let loading = $state(false)
const tryConfirm = async () => {
loading = true
@@ -25,10 +31,14 @@
const back = () => history.back()
</script>
<form class="column gap-4" on:submit|preventDefault={tryConfirm}>
<form class="column gap-4" onsubmit={preventDefault(tryConfirm)}>
<ModalHeader>
<div slot="title">{title}</div>
<div slot="info">{subtitle}</div>
{#snippet title()}
<div>{title}</div>
{/snippet}
{#snippet info()}
<div>{subtitle}</div>
{/snippet}
</ModalHeader>
<p>{message}</p>
<ModalFooter>
+13 -3
View File
@@ -1,12 +1,22 @@
<div class="col-2 content-padding-t content-padding-x h-full {$$props.class}">
<script lang="ts">
interface Props {
input?: import("svelte").Snippet
content?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div class="col-2 content-padding-t content-padding-x h-full {props.class}">
<div class="z-feature">
<div class="content-sizing">
<slot name="input" />
{@render props.input?.()}
</div>
</div>
<div class="scroll-container overflow-auto pt-2">
<div class="content-sizing">
<slot name="content" />
{@render props.content?.()}
</div>
</div>
</div>
+7 -2
View File
@@ -3,8 +3,13 @@
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
export let initialValue: Date | undefined = undefined
export let value: Date | undefined = initialValue
interface Props {
initialValue?: Date | undefined
value?: Date | undefined
}
let {initialValue = undefined, value = $bindable<Date | undefined>(initialValue)}: Props =
$props()
const init = () => {
if (!value) {
+12 -6
View File
@@ -2,12 +2,18 @@
import {noop} from "@welshman/lib"
import {fade, fly} from "@lib/transition"
export let onClose: any = noop
export let fullscreen = false
interface Props {
onClose?: any
fullscreen?: boolean
children?: import("svelte").Snippet
}
$: extraClass =
let {onClose = noop, fullscreen = false, children}: Props = $props()
let extraClass = $derived(
!fullscreen &&
"card2 bg-alt max-h-[90vh] w-[90vw] overflow-auto text-base-content sm:w-[520px] shadow-xl"
"card2 bg-alt max-h-[90vh] w-[90vw] overflow-auto text-base-content sm:w-[520px] shadow-xl",
)
</script>
<div class="center fixed inset-0 z-modal">
@@ -15,9 +21,9 @@
aria-label="Close dialog"
class="absolute inset-0 cursor-pointer bg-black opacity-75"
transition:fade={{duration: 300}}
on:click={onClose}>
onclick={onClose}>
</button>
<div class="scroll-container relative {extraClass}" transition:fly={{duration: 300}}>
<slot />
{@render children?.()}
</div>
</div>
+10 -2
View File
@@ -1,7 +1,15 @@
<script lang="ts">
interface Props {
children?: import("svelte").Snippet
}
let {children}: Props = $props()
</script>
<div class="flex items-center gap-2 p-2 text-xs uppercase opacity-50">
<div class="h-px flex-grow bg-base-content opacity-25"></div>
{#if $$slots.default}
<p><slot /></p>
{#if children}
<p>{@render children?.()}</p>
<div class="h-px flex-grow bg-base-content opacity-25"></div>
{/if}
</div>
+3 -3
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import {fade, translate} from "@lib/transition"
export let onClose
let {onClose, children} = $props()
</script>
<div class="drawer fixed inset-0 z-modal">
@@ -9,11 +9,11 @@
aria-label="Close drawer"
class="absolute inset-0 cursor-pointer bg-black opacity-50"
transition:fade
on:click={onClose}>
onclick={onClose}>
</button>
<div
class="scroll-container saiy sair absolute bottom-0 right-0 top-0 w-80 overflow-auto bg-base-200 text-base-content lg:w-96"
transition:translate={{axis: "x", duration: 300}}>
<slot />
{@render children?.()}
</div>
</div>
+8 -8
View File
@@ -6,13 +6,13 @@
import Tippy from "@lib/components/Tippy.svelte"
import EmojiPicker from "@lib/components/EmojiPicker.svelte"
export let onEmoji
let {...props} = $props()
const open = () => popover.show()
const open = () => popover?.show()
const onClick = (emoji: NativeEmoji) => {
onEmoji(emoji)
popover.hide()
props.onEmoji(emoji)
popover?.hide()
}
const onMouseMove = throttle(300, ({clientX, clientY}: any) => {
@@ -25,17 +25,17 @@
}
})
let popover: Instance
let popover: Instance | undefined = $state()
</script>
<svelte:document on:mousemove={onMouseMove} />
<svelte:document onmousemove={onMouseMove} />
<Tippy
bind:popover
component={EmojiPicker}
props={{onClick}}
params={{trigger: "manual", interactive: true}}>
<Button on:click={open} class={$$props.class}>
<slot />
<Button on:click={open} class={props.class}>
{@render props.children?.()}
</Button>
</Tippy>
+7 -3
View File
@@ -13,12 +13,16 @@
import type {Emoji} from "emoji-picker-element/shared"
import {onMount} from "svelte"
export let onClick: (emoji: Emoji) => void
interface Props {
onClick: (emoji: Emoji) => void
}
let element: Element
let {onClick}: Props = $props()
let element: Element | undefined = $state()
onMount(() => {
element.addEventListener("emoji-click", (event: any) => onClick(event.detail as Emoji))
element?.addEventListener("emoji-click", (event: any) => onClick(event.detail as Emoji))
})
</script>
+17 -6
View File
@@ -1,13 +1,24 @@
<div class="flex flex-col gap-2 {$$props.class}">
{#if $$slots.label}
<script lang="ts">
interface Props {
label?: import("svelte").Snippet
input?: import("svelte").Snippet
info?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div class="flex flex-col gap-2 {props.class}">
{#if props.label}
<label class="flex items-center gap-2 font-bold">
<slot name="label" />
{@render props.label?.()}
</label>
{/if}
<slot name="input" />
{#if $$slots.info}
{@render props.input?.()}
{#if props.info}
<p class="text-sm">
<slot name="info" />
{@render props.info?.()}
</p>
{/if}
</div>
+16 -5
View File
@@ -1,13 +1,24 @@
<div class="grid grid-cols-1 gap-2 md:grid-cols-3 {$$props.class}">
<script lang="ts">
interface Props {
label?: import("svelte").Snippet
input?: import("svelte").Snippet
info?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div class="grid grid-cols-1 gap-2 md:grid-cols-3 {props.class}">
<label class="flex items-center gap-2 font-bold">
<slot name="label" />
{@render props.label?.()}
</label>
<div class="col-span-2 flex items-center gap-2">
<slot name="input" />
{@render props.input?.()}
</div>
<p class="flex-end text-sm md:col-span-3">
{#if $$slots.info}
<slot name="info" />
{#if props.info}
{@render props.info?.()}
{/if}
</p>
</div>
+7 -3
View File
@@ -84,8 +84,12 @@
import WidgetAdd from "@assets/icons/Widget Add.svg?dataurl"
import WiFiRouterRound from "@assets/icons/Wi-Fi Router Round.svg?dataurl"
export let icon
export let size = 5
interface Props {
icon: string
size: number
}
let {icon, size = 5, ...restProps} = $props()
const px = size * 4
@@ -173,6 +177,6 @@
</script>
<div
class={cx("inline-block", $$props.class)}
class="inline-block {restProps.class}"
style="mask-image: url({data}); width: {px}px; height: {px}px; min-width: {px}px; min-height: {px}px; background-color: currentcolor;">
</div>
+19 -13
View File
@@ -1,9 +1,15 @@
<script lang="ts">
import {run, preventDefault, stopPropagation} from "svelte/legacy"
import {randomId} from "@welshman/lib"
import Icon from "@lib/components/Icon.svelte"
export let file: File | null = null
export let url: string | null = null
interface Props {
file?: File | null
url?: string | null
}
let {file = $bindable(null), url = $bindable(null)}: Props = $props()
const id = randomId()
@@ -35,10 +41,10 @@
url = null
}
let active = false
let initialUrl = url
let active = $state(false)
let initialUrl = $state(url)
$: {
run(() => {
if (file) {
const reader = new FileReader()
@@ -53,21 +59,21 @@
} else {
url = initialUrl
}
}
})
</script>
<form>
<input {id} type="file" accept="image/*" on:change={onChange} class="hidden" />
<input {id} type="file" accept="image/*" onchange={onChange} class="hidden" />
<label
for={id}
aria-label="Drag and drop files here."
style="background-image: url({url});"
class="relative flex h-24 w-24 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 border-dashed border-base-content bg-base-300 bg-cover bg-center transition-all"
class:border-primary={active}
on:dragenter|preventDefault|stopPropagation={onDragEnter}
on:dragover|preventDefault|stopPropagation={onDragOver}
on:dragleave|preventDefault|stopPropagation={onDragLeave}
on:drop|preventDefault|stopPropagation={onDrop}>
ondragenter={stopPropagation(preventDefault(onDragEnter))}
ondragover={stopPropagation(preventDefault(onDragOver))}
ondragleave={stopPropagation(preventDefault(onDragLeave))}
ondrop={stopPropagation(preventDefault(onDrop))}>
<div
class="absolute right-0 top-0 h-5 w-5 overflow-hidden rounded-full bg-primary"
class:bg-error={file}
@@ -76,8 +82,8 @@
<span
role="button"
tabindex="-1"
on:mousedown|stopPropagation={onClear}
on:touchstart|stopPropagation={onClear}>
onmousedown={stopPropagation(onClear)}
ontouchstart={stopPropagation(onClear)}>
<Icon icon="close-circle" class="scale-150 !bg-base-300" />
</span>
{:else}
+19 -13
View File
@@ -1,9 +1,15 @@
<script lang="ts">
import {run, preventDefault, stopPropagation} from "svelte/legacy"
import {randomId} from "@welshman/lib"
import Icon from "@lib/components/Icon.svelte"
export let file: File | null = null
export let url: string | null = null
interface Props {
file?: File | null
url?: string | null
}
let {file = $bindable(null), url = $bindable(null)}: Props = $props()
const id = randomId()
@@ -35,10 +41,10 @@
url = null
}
let active = false
let initialUrl = url
let active = $state(false)
let initialUrl = $state(url)
$: {
run(() => {
if (file) {
const reader = new FileReader()
@@ -53,11 +59,11 @@
} else {
url = initialUrl
}
}
})
</script>
<form>
<input {id} type="file" accept="image/*" on:change={onChange} class="hidden" />
<input {id} type="file" accept="image/*" onchange={onChange} class="hidden" />
<label
for={id}
aria-label="Drag and drop files here."
@@ -65,10 +71,10 @@
class="relative flex h-24 w-24 shrink-0 cursor-pointer items-center justify-center rounded-full border-2 border-solid border-base-content bg-base-300 bg-cover bg-center transition-all"
class:transparent={!url}
class:border-primary={active}
on:dragenter|preventDefault|stopPropagation={onDragEnter}
on:dragover|preventDefault|stopPropagation={onDragOver}
on:dragleave|preventDefault|stopPropagation={onDragLeave}
on:drop|preventDefault|stopPropagation={onDrop}>
ondragenter={stopPropagation(preventDefault(onDragEnter))}
ondragover={stopPropagation(preventDefault(onDragOver))}
ondragleave={stopPropagation(preventDefault(onDragLeave))}
ondrop={stopPropagation(preventDefault(onDrop))}>
<div
class="absolute right-0 top-0 h-5 w-5 overflow-hidden rounded-full bg-primary"
class:bg-error={url}
@@ -77,8 +83,8 @@
<span
role="button"
tabindex="-1"
on:mousedown|stopPropagation={onClear}
on:touchstart|stopPropagation={onClear}>
onmousedown={stopPropagation(onClear)}
ontouchstart={stopPropagation(onClear)}>
<Icon icon="close-circle" class="scale-150 !bg-base-300" />
</span>
{:else}
+9 -5
View File
@@ -1,9 +1,13 @@
<script lang="ts">
import {goto} from "$app/navigation"
export let href
export let external = false
export let replaceState = false
interface Props {
href: string
external: boolean
replaceState: boolean
}
let {href, external = false, replaceState = false, ...restProps} = $props()
const go = (e: Event) => {
if (!external) {
@@ -16,9 +20,9 @@
<a
{href}
{...$$props}
{...restProps}
on:click|stopPropagation={go}
class="cursor-pointer {$$props.class}"
class="cursor-pointer {restProps.class}"
rel={external ? "noopener noreferer" : ""}
target={external ? "_blank" : ""}>
<slot />
+11 -8
View File
@@ -1,9 +1,12 @@
<script lang="ts">
export let onLongPress
import {createBubbler} from "svelte/legacy"
const bubble = createBubbler()
let {...props} = $props()
const onTouchStart = (event: any) => {
touch = event.touches[0]
timeout = setTimeout(onLongPress, 500)
timeout = setTimeout(props.onLongPress, 500)
}
const onTouchMove = (event: any) => {
@@ -27,10 +30,10 @@
<div
role="button"
tabindex="0"
on:click
on:touchstart={onTouchStart}
on:touchmove={onTouchMove}
on:touchend={onTouchEnd}
{...$$props}>
<slot />
onclick={bubble("click")}
ontouchstart={onTouchStart}
ontouchmove={onTouchMove}
ontouchend={onTouchEnd}
{...props}>
{@render props.children?.()}
</div>
+9 -1
View File
@@ -1,3 +1,11 @@
<script lang="ts">
interface Props {
children?: import("svelte").Snippet
}
let {children}: Props = $props()
</script>
<div class="row-4 mt-4 items-center justify-between">
<slot />
{@render children?.()}
</div>
+11 -2
View File
@@ -1,4 +1,13 @@
<script lang="ts">
interface Props {
title?: import("svelte").Snippet
info?: import("svelte").Snippet
}
let {title, info}: Props = $props()
</script>
<div class="column m-auto max-w-xs gap-2 py-4">
<h1 class="heading"><slot name="title" /></h1>
<p class="text-center"><slot name="info" /></p>
<h1 class="heading">{@render title?.()}</h1>
<p class="text-center">{@render info?.()}</p>
</div>
+11 -2
View File
@@ -1,4 +1,13 @@
<script lang="ts">
interface Props {
children?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div
class="sait saib sair scroll-container max-h-screen flex-grow overflow-auto bg-base-200 pb-14 md:pb-0 {$$props.class}">
<slot />
class="sait saib sair scroll-container max-h-screen flex-grow overflow-auto bg-base-200 pb-14 md:pb-0 {props.class}">
{@render props.children?.()}
</div>
+15 -4
View File
@@ -1,10 +1,21 @@
<div class="relative z-feature mx-2 rounded-xl pt-4 {$$props.class}">
<script lang="ts">
interface Props {
icon?: import("svelte").Snippet
title?: import("svelte").Snippet
action?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div class="relative z-feature mx-2 rounded-xl pt-4 {props.class}">
<div
class="flex min-h-12 items-center justify-between gap-4 rounded-xl bg-base-100 px-4 shadow-xl">
<div class="flex items-center gap-4">
<slot name="icon" />
<slot name="title" />
{@render props.icon?.()}
{@render props.title?.()}
</div>
<slot name="action" />
{@render props.action?.()}
</div>
</div>
+11 -2
View File
@@ -1,4 +1,13 @@
<script lang="ts">
interface Props {
title?: import("svelte").Snippet
info?: import("svelte").Snippet
}
let {title, info}: Props = $props()
</script>
<div class="column gap-4 py-12">
<h1 class="superheading"><slot name="title" /></h1>
<p class="text-center"><slot name="info" /></p>
<h1 class="superheading">{@render title?.()}</h1>
<p class="text-center">{@render info?.()}</p>
</div>
+12 -6
View File
@@ -1,11 +1,17 @@
<script lang="ts">
import type {Snippet} from "svelte"
import {fly} from "@lib/transition"
export let onClose
export let hideOnClick = false
interface Props {
onClose: any
hideOnClick?: boolean
children?: Snippet
}
let {onClose, hideOnClick = false, children}: Props = $props()
const onMouseUp = (e: any) => {
if (hideOnClick || !element.contains(e.target)) {
if (hideOnClick || !element?.contains(e.target)) {
setTimeout(onClose)
}
}
@@ -16,13 +22,13 @@
}
}
let element: HTMLElement
let element: HTMLElement | undefined = $state()
</script>
<svelte:window on:mouseup={onMouseUp} on:keydown={onKeyDown} />
<svelte:window onmouseup={onMouseUp} onkeydown={onKeyDown} />
<div class="relative w-full" bind:this={element}>
<div transition:fly|local class="absolute z-popover w-full">
<slot />
{@render children?.()}
</div>
</div>
+4 -7
View File
@@ -2,18 +2,15 @@
import {page} from "$app/stores"
import Button from "@lib/components/Button.svelte"
export let title = ""
export let href = ""
export let prefix = ""
export let notification = false
let {title = "", href = "", prefix = "", notification = false, ...restProps} = $props()
$: active = $page.url?.pathname?.startsWith(prefix || href || "bogus")
const active = $derived($page.url?.pathname?.startsWith(prefix || href || "bogus"))
</script>
{#if href}
<a {href} class="relative z-nav-item flex h-14 w-14 items-center justify-center">
<div
class="avatar cursor-pointer rounded-full p-1 {$$props.class} transition-colors hover:bg-base-300"
class="avatar cursor-pointer rounded-full p-1 {restProps.class} transition-colors hover:bg-base-300"
class:bg-base-300={active}
class:tooltip={title}
data-tip={title}>
@@ -26,7 +23,7 @@
{:else}
<Button on:click class="relative z-nav-item flex h-14 w-14 items-center justify-center">
<div
class="avatar cursor-pointer rounded-full p-1 {$$props.class} transition-colors hover:bg-base-300"
class="avatar cursor-pointer rounded-full p-1 {restProps.class} transition-colors hover:bg-base-300"
class:bg-base-300={active}
class:tooltip={title}
data-tip={title}>
+15 -12
View File
@@ -7,13 +7,16 @@
import Icon from "@lib/components/Icon.svelte"
import Tippy from "@lib/components/Tippy.svelte"
export let value: string
export let options: string[] = []
export let allowCreate = false
interface Props {
value: string
options: string[]
allowCreate?: boolean
}
let input: Element
let popover: Instance
let instance: any
let {value, options, allowCreate = false, ...restProps} = $props()
let input: Element | undefined = $state()
let popover: Instance | undefined = $state()
let instance: any = $state()
const search = readable(
createSearch(options, {
@@ -23,26 +26,26 @@
)
const select = (newValue: string) => {
popover.hide()
popover?.hide()
value = newValue
}
const onKeyDown = (e: Event) => {
if (instance.onKeyDown(e)) {
if (instance?.onKeyDown(e)) {
e.preventDefault()
}
}
const onFocus = () => {
popover.show()
popover?.show()
}
const onBlur = () => {
popover.hide()
popover?.hide()
}
</script>
<div class={$$props.class}>
<div class={restProps.class}>
<label class="input input-bordered flex w-full items-center gap-3" bind:this={input}>
<slot name="before" />
<input
@@ -71,6 +74,6 @@
trigger: "manual",
interactive: true,
maxWidth: "none",
getReferenceClientRect: () => input.getBoundingClientRect(),
getReferenceClientRect: () => input!.getBoundingClientRect(),
}} />
</div>
+9 -1
View File
@@ -1,4 +1,12 @@
<script lang="ts">
interface Props {
children?: import("svelte").Snippet
}
let {children}: Props = $props()
</script>
<div
class="sail sait saib hidden max-h-screen w-60 flex-shrink-0 flex-col gap-1 bg-base-300 md:flex">
<slot />
{@render children?.()}
</div>
+9 -1
View File
@@ -1,3 +1,11 @@
<script lang="ts">
interface Props {
children?: import("svelte").Snippet
}
let {children}: Props = $props()
</script>
<div class="flex items-center justify-between px-4 py-2 text-sm font-bold uppercase">
<slot />
{@render children?.()}
</div>
+6 -14
View File
@@ -21,25 +21,20 @@
</style>
<script lang="ts">
import cx from "classnames"
import {fade} from "@lib/transition"
import {page} from "$app/stores"
export let href: string = ""
export let notification = false
let {href = "", notification = "false", ...restProps} = $props()
$: active = $page.url.pathname === href
const active = $derived($page.url.pathname === href)
</script>
{#if href}
<a
{...$$props}
{...restProps}
{href}
on:click
class={cx(
$$props.class,
"relative flex items-center gap-3 text-left transition-all hover:bg-base-100 hover:text-base-content",
)}
class="{restProps.class} relative flex items-center gap-3 text-left transition-all hover:bg-base-100 hover:text-base-content"
class:text-base-content={active}
class:bg-base-100={active}>
<slot />
@@ -49,12 +44,9 @@
</a>
{:else}
<button
{...$$props}
{...restProps}
on:click
class={cx(
$$props.class,
"relative flex w-full items-center gap-3 text-left transition-all hover:bg-base-100 hover:text-base-content",
)}
class="{restProps.class} relative flex w-full items-center gap-3 text-left transition-all hover:bg-base-100 hover:text-base-content"
class:text-base-content={active}
class:bg-base-100={active}>
{#if !active && notification}
+11 -2
View File
@@ -1,3 +1,12 @@
<div class="flex flex-col gap-1 px-2 py-4 {$$props.class}">
<slot />
<script lang="ts">
interface Props {
children?: import("svelte").Snippet
[key: string]: any
}
let {...props}: Props = $props()
</script>
<div class="flex flex-col gap-1 px-2 py-4 {props.class}">
{@render props.children?.()}
</div>
+7 -2
View File
@@ -1,7 +1,12 @@
<script lang="ts">
import {slide, fade} from "svelte/transition"
export let loading = false
interface Props {
loading?: boolean
children?: import("svelte").Snippet
}
let {loading = false, children}: Props = $props()
</script>
<span class="flex min-h-10 items-center">
@@ -10,5 +15,5 @@
<span class="loading loading-spinner" transition:fade|local={{duration: 100}}></span>
</span>
{/if}
<slot />
{@render children?.()}
</span>
-1
View File
@@ -32,7 +32,6 @@
return () => {
popover?.destroy()
unmount(instance)
}
})
</script>
+11 -7
View File
@@ -16,17 +16,21 @@
<script lang="ts">
import {clamp} from "@welshman/lib"
export let score
export let max = 100
export let active = false
interface Props {
score: any
max?: number
active?: boolean
}
let {score, max = 100, active = false}: Props = $props()
const radius = 6
const center = radius + 1
$: normalizedScore = clamp([0, max], score) / max
$: dashOffset = 100 - 44 * normalizedScore
$: style = `transform: rotate(${135 - normalizedScore * 180}deg)`
$: stroke = active ? "var(--primary)" : "var(--base-content)"
let normalizedScore = $derived(clamp([0, max], score) / max)
let dashOffset = $derived(100 - 44 * normalizedScore)
let style = $derived(`transform: rotate(${135 - normalizedScore * 180}deg)`)
let stroke = $derived(active ? "var(--primary)" : "var(--base-content)")
</script>
<div class="relative h-[14px] w-[14px]">