add ActionSelect component
This commit is contained in:
109
src/lib/ActionSelect.svelte
Normal file
109
src/lib/ActionSelect.svelte
Normal file
@@ -0,0 +1,109 @@
|
||||
<script lang="ts" module>
|
||||
export type ButtonSelectOption = {
|
||||
icon?:
|
||||
| { component: Component; props: Record<string, any> }
|
||||
| Snippet<[opt: ButtonSelectOption]>;
|
||||
title: string | Snippet<[opt: ButtonSelectOption]>;
|
||||
onchoose?: (opt: ButtonSelectOption) => void;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Select } from 'melt/builders';
|
||||
import type { Component, Snippet } from 'svelte';
|
||||
import Label from './Label.svelte';
|
||||
import Button from './Button.svelte';
|
||||
import { CaretDown, Check } from 'phosphor-svelte';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
interface Props {
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
stateless?: boolean;
|
||||
options: ButtonSelectOption[];
|
||||
class?: ClassValue | null | undefined;
|
||||
}
|
||||
|
||||
let {
|
||||
label,
|
||||
placeholder = 'Choose an action',
|
||||
stateless = true,
|
||||
options,
|
||||
class: classValue
|
||||
}: Props = $props();
|
||||
|
||||
const select = new Select<ButtonSelectOption>({
|
||||
onValueChange: (value) => {
|
||||
value?.onchoose?.(value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if label}
|
||||
<Label {...select.label}>{label}</Label>
|
||||
{/if}
|
||||
|
||||
<Button class={['flex w-full items-center rounded-xl!', classValue]} {...select.trigger}>
|
||||
{#if stateless || !select.value}
|
||||
{placeholder}
|
||||
{:else}
|
||||
{@render icon(select.value)}
|
||||
{@render title(select.value)}
|
||||
{/if}
|
||||
|
||||
<CaretDown
|
||||
weight="bold"
|
||||
size="1.1em"
|
||||
class={[select.open && '-scale-y-100', 'ml-auto transition-transform']}
|
||||
/>
|
||||
</Button>
|
||||
|
||||
<div class={['border-sui-accent space-y-1 rounded-xl border p-2 shadow-md']} {...select.content}>
|
||||
{#each options as option}
|
||||
<button
|
||||
class={[
|
||||
'flex w-full items-center gap-3 rounded px-5 py-2 text-base font-medium transition-colors',
|
||||
stateless
|
||||
? [select.highlighted?.title === option.title && 'bg-sui-text-100', 'cursor-pointer']
|
||||
: [
|
||||
select.highlighted?.title === option.title &&
|
||||
'bg-sui-accent/80 text-sui-background',
|
||||
select.value?.title === option.title && 'bg-sui-accent text-sui-background'
|
||||
]
|
||||
]}
|
||||
{...select.getOption(option, typeof option.title === 'string' ? option.title : undefined)}
|
||||
>
|
||||
{@render icon(option)}
|
||||
{@render title(option)}
|
||||
|
||||
{#if !stateless}
|
||||
<Check
|
||||
size="1.1em"
|
||||
weight="bold"
|
||||
class={[
|
||||
select.value?.title === option.title ? 'opacity-100' : 'opacity-0',
|
||||
'ml-auto transition-opacity'
|
||||
]}
|
||||
/>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#snippet title(opt: ButtonSelectOption)}
|
||||
{#if typeof opt.title === 'string'}
|
||||
{opt.title}
|
||||
{:else}
|
||||
{@render opt.title(opt)}
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet icon(opt: ButtonSelectOption)}
|
||||
{#if opt.icon && typeof opt.icon === 'object'}
|
||||
<opt.icon.component {...opt.icon.props} />
|
||||
{:else if opt.icon}
|
||||
{@render opt.icon(opt)}
|
||||
{/if}
|
||||
{/snippet}
|
||||
@@ -144,5 +144,6 @@
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
animation: ripple 0.5s linear;
|
||||
transform: scale(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Reexport your entry components here
|
||||
export { default as ActionSelect } from './ActionSelect.svelte';
|
||||
export { default as Button } from './Button.svelte';
|
||||
export { default as CenterBox } from './CenterBox.svelte';
|
||||
export { default as Checkbox, type CheckboxState } from './Checkbox.svelte';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/Button.svelte';
|
||||
import ActionSelect from '$lib/ActionSelect.svelte';
|
||||
import Checkbox, { type CheckboxState } from '$lib/Checkbox.svelte';
|
||||
import Combobox from '$lib/Combobox.svelte';
|
||||
import DateInput from '$lib/DateInput.svelte';
|
||||
@@ -49,6 +50,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="component">
|
||||
<p class="title">Button Select</p>
|
||||
|
||||
<ActionSelect
|
||||
label="Action"
|
||||
options={[{ title: 'Yeet' }, { title: 'Yote' }, { title: 'Yote and Yeet' }]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="component">
|
||||
<p class="title">Combobox</p>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user