mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 00:06:14 +00:00
MultiToggle cool animation + clearable
This commit is contained in:
@@ -2,38 +2,76 @@
|
||||
<script lang="ts" generics="T">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { getLabel } from './select/utils.svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
|
||||
let {
|
||||
items,
|
||||
value = $bindable(),
|
||||
class: className,
|
||||
error,
|
||||
disabled
|
||||
disabled,
|
||||
clearable = false
|
||||
}: {
|
||||
items: { label?: string; value: T }[]
|
||||
value: T
|
||||
class?: string
|
||||
error?: boolean
|
||||
disabled?: boolean
|
||||
clearable?: boolean
|
||||
} = $props()
|
||||
|
||||
let btnRefs = $state<Record<number, HTMLButtonElement | undefined>>({})
|
||||
let btnWidths: { w: number; offsetX: number }[] = $state([])
|
||||
$effect(() => {
|
||||
btnRefs
|
||||
for (const item of items) {
|
||||
item.label
|
||||
item.value
|
||||
}
|
||||
setTimeout(() => {
|
||||
btnWidths = []
|
||||
for (let i = 0; i in btnRefs; i++) {
|
||||
btnWidths.push({
|
||||
w: btnRefs[i]?.getBoundingClientRect().width ?? 0,
|
||||
offsetX: btnRefs[i]?.offsetLeft ?? 0
|
||||
})
|
||||
}
|
||||
}, 0)
|
||||
})
|
||||
let selectedIdx = $derived(items.findIndex((item) => item.value === value))
|
||||
let selectedBtnBox: (typeof btnWidths)[number] | undefined = $derived(btnWidths[selectedIdx])
|
||||
</script>
|
||||
|
||||
<ul
|
||||
class={twMerge(
|
||||
'flex text-nowrap bg-gray-300 rounded-full items-center select-none border',
|
||||
'flex text-nowrap bg-gray-300 rounded-full items-center border relative transition-colors',
|
||||
error ? 'border-red-300 bg-red-100' : '',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{#each items as item}
|
||||
<li class="h-6 mx-[1px] text-xs flex items-center">
|
||||
<!-- White animated selector -->
|
||||
{#if selectedBtnBox}
|
||||
<div
|
||||
class={twMerge('h-[22px] bg-white rounded-full absolute transition-all')}
|
||||
transition:fade={{ duration: 120 }}
|
||||
style={`width: ${selectedBtnBox?.w ?? 22}px; left: ${selectedBtnBox?.offsetX ?? 0}px;`}
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
{#each items as item, i}
|
||||
<li class="h-6 text-xs flex items-center">
|
||||
<button
|
||||
class={twMerge(
|
||||
'rounded-full px-1.5 h-[22px] min-w-[22px] flex items-center justify-center',
|
||||
value === item.value ? 'bg-white' : ''
|
||||
)}
|
||||
bind:this={btnRefs[i]}
|
||||
class="z-10 rounded-full px-1.5 h-6 min-w-6 flex items-center justify-center"
|
||||
{disabled}
|
||||
onclick={() => !disabled && (value = item.value)}
|
||||
onclick={() => {
|
||||
if (disabled) return
|
||||
if (clearable && value === item.value) {
|
||||
value = undefined as any
|
||||
} else {
|
||||
value = item.value
|
||||
}
|
||||
}}
|
||||
>
|
||||
{getLabel(item)}
|
||||
</button>
|
||||
|
||||
@@ -166,13 +166,15 @@
|
||||
items={safeSelectItems(['r', 'w', 'rw'])}
|
||||
error={!asset.access_type && !alternativeAccessType}
|
||||
disabled={!!asset.access_type}
|
||||
clearable
|
||||
bind:value={
|
||||
() => asset.access_type ?? alternativeAccessType,
|
||||
async (access_type) => {
|
||||
alternativeAccessTypes = alternativeAccessTypes?.filter((a) => !assetEq(a, asset))
|
||||
alternativeAccessTypes ??= []
|
||||
await tick()
|
||||
alternativeAccessTypes.push({ ...asset, access_type })
|
||||
let val = alternativeAccessTypes?.filter((a) => !assetEq(a, asset))
|
||||
val.push({ ...asset, access_type })
|
||||
alternativeAccessTypes = val
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user