add muted and suffixSnippet props to Select component
This commit is contained in:
parent
0a2a3894bf
commit
e7dfca992a
1 changed files with 143 additions and 52 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
<svelte:options runes={true} />
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
<script lang="ts" generics="T extends string | number">
|
<script lang="ts" generics="T extends string | number">
|
||||||
|
import type { Snippet } from 'svelte'
|
||||||
import { Select as SelectPrimitive } from 'bits-ui'
|
import { Select as SelectPrimitive } from 'bits-ui'
|
||||||
import { Label } from 'bits-ui'
|
import { Label } from 'bits-ui'
|
||||||
import Icon from '../Icon.svelte'
|
import Icon from '../Icon.svelte'
|
||||||
|
|
@ -12,6 +13,10 @@
|
||||||
image?: string
|
image?: string
|
||||||
/** CSS color for a colored dot indicator */
|
/** CSS color for a colored dot indicator */
|
||||||
color?: string
|
color?: string
|
||||||
|
/** Optional suffix text displayed in muted style */
|
||||||
|
suffix?: string
|
||||||
|
/** Display label in muted/tertiary color */
|
||||||
|
muted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -26,7 +31,10 @@
|
||||||
label?: string
|
label?: string
|
||||||
error?: string
|
error?: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
portal?: boolean
|
||||||
class?: string
|
class?: string
|
||||||
|
/** Custom snippet for rendering suffix area - receives the option */
|
||||||
|
suffixSnippet?: Snippet<[Option]>
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|
@ -41,7 +49,9 @@
|
||||||
label,
|
label,
|
||||||
error,
|
error,
|
||||||
required = false,
|
required = false,
|
||||||
class: className = ''
|
portal = false,
|
||||||
|
class: className = '',
|
||||||
|
suffixSnippet
|
||||||
}: Props = $props()
|
}: Props = $props()
|
||||||
|
|
||||||
// Convert options to string values for Bits UI (which expects strings internally)
|
// Convert options to string values for Bits UI (which expects strings internally)
|
||||||
|
|
@ -105,6 +115,8 @@
|
||||||
<Icon name="chevron-down-small" size={14} class="chevron" />
|
<Icon name="chevron-down-small" size={14} class="chevron" />
|
||||||
</SelectPrimitive.Trigger>
|
</SelectPrimitive.Trigger>
|
||||||
|
|
||||||
|
{#if portal}
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
<SelectPrimitive.Content class="content">
|
<SelectPrimitive.Content class="content">
|
||||||
<SelectPrimitive.Viewport>
|
<SelectPrimitive.Viewport>
|
||||||
{#each options as option}
|
{#each options as option}
|
||||||
|
|
@ -119,7 +131,10 @@
|
||||||
{:else if option.image}
|
{:else if option.image}
|
||||||
<img src={option.image} alt={option.label} class="image" />
|
<img src={option.image} alt={option.label} class="image" />
|
||||||
{/if}
|
{/if}
|
||||||
<span class="text">{option.label}</span>
|
<span class="text" class:muted={option.muted}>{option.label}</span>
|
||||||
|
{#if option.suffix}
|
||||||
|
<span class="suffix">{option.suffix}</span>
|
||||||
|
{/if}
|
||||||
{#if selected}
|
{#if selected}
|
||||||
<span class="indicator">
|
<span class="indicator">
|
||||||
<Icon name="check" size={14} />
|
<Icon name="check" size={14} />
|
||||||
|
|
@ -130,6 +145,37 @@
|
||||||
{/each}
|
{/each}
|
||||||
</SelectPrimitive.Viewport>
|
</SelectPrimitive.Viewport>
|
||||||
</SelectPrimitive.Content>
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
{:else}
|
||||||
|
<SelectPrimitive.Content class="content">
|
||||||
|
<SelectPrimitive.Viewport>
|
||||||
|
{#each options as option}
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
value={String(option.value)}
|
||||||
|
{...option.disabled !== undefined ? { disabled: option.disabled } : {}}
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
{#snippet children({ selected })}
|
||||||
|
{#if option.color}
|
||||||
|
<span class="color-dot" style="background-color: {option.color}"></span>
|
||||||
|
{:else if option.image}
|
||||||
|
<img src={option.image} alt={option.label} class="image" />
|
||||||
|
{/if}
|
||||||
|
<span class="text" class:muted={option.muted}>{option.label}</span>
|
||||||
|
{#if option.suffix}
|
||||||
|
<span class="suffix">{option.suffix}</span>
|
||||||
|
{/if}
|
||||||
|
{#if selected}
|
||||||
|
<span class="indicator">
|
||||||
|
<Icon name="check" size={14} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
{/each}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
{/if}
|
||||||
</SelectPrimitive.Root>
|
</SelectPrimitive.Root>
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
|
|
@ -154,6 +200,8 @@
|
||||||
<Icon name="chevron-down-small" size={14} class="chevron" />
|
<Icon name="chevron-down-small" size={14} class="chevron" />
|
||||||
</SelectPrimitive.Trigger>
|
</SelectPrimitive.Trigger>
|
||||||
|
|
||||||
|
{#if portal}
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
<SelectPrimitive.Content class="content">
|
<SelectPrimitive.Content class="content">
|
||||||
<SelectPrimitive.Viewport>
|
<SelectPrimitive.Viewport>
|
||||||
{#each options as option}
|
{#each options as option}
|
||||||
|
|
@ -168,7 +216,10 @@
|
||||||
{:else if option.image}
|
{:else if option.image}
|
||||||
<img src={option.image} alt={option.label} class="image" />
|
<img src={option.image} alt={option.label} class="image" />
|
||||||
{/if}
|
{/if}
|
||||||
<span class="text">{option.label}</span>
|
<span class="text" class:muted={option.muted}>{option.label}</span>
|
||||||
|
{#if option.suffix}
|
||||||
|
<span class="suffix">{option.suffix}</span>
|
||||||
|
{/if}
|
||||||
{#if selected}
|
{#if selected}
|
||||||
<span class="indicator">
|
<span class="indicator">
|
||||||
<Icon name="check" size={14} />
|
<Icon name="check" size={14} />
|
||||||
|
|
@ -179,6 +230,37 @@
|
||||||
{/each}
|
{/each}
|
||||||
</SelectPrimitive.Viewport>
|
</SelectPrimitive.Viewport>
|
||||||
</SelectPrimitive.Content>
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
{:else}
|
||||||
|
<SelectPrimitive.Content class="content">
|
||||||
|
<SelectPrimitive.Viewport>
|
||||||
|
{#each options as option}
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
value={String(option.value)}
|
||||||
|
{...option.disabled !== undefined ? { disabled: option.disabled } : {}}
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
{#snippet children({ selected })}
|
||||||
|
{#if option.color}
|
||||||
|
<span class="color-dot" style="background-color: {option.color}"></span>
|
||||||
|
{:else if option.image}
|
||||||
|
<img src={option.image} alt={option.label} class="image" />
|
||||||
|
{/if}
|
||||||
|
<span class="text" class:muted={option.muted}>{option.label}</span>
|
||||||
|
{#if option.suffix}
|
||||||
|
<span class="suffix">{option.suffix}</span>
|
||||||
|
{/if}
|
||||||
|
{#if selected}
|
||||||
|
<span class="indicator">
|
||||||
|
<Icon name="check" size={14} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
{/each}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
{/if}
|
||||||
</SelectPrimitive.Root>
|
</SelectPrimitive.Root>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
@ -326,7 +408,7 @@
|
||||||
min-width: var(--bits-select-anchor-width);
|
min-width: var(--bits-select-anchor-width);
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
z-index: 50;
|
z-index: 102;
|
||||||
animation: fadeIn $duration-opacity-fade ease-out;
|
animation: fadeIn $duration-opacity-fade ease-out;
|
||||||
|
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
|
|
@ -373,6 +455,15 @@
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
&.muted {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suffix {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: $font-small;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue