combobox: default icon support, smart icon width
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
import { Portal } from '@jsrob/svelte-portal';
|
||||
import { browser } from '$app/environment';
|
||||
import { scale } from 'svelte/transition';
|
||||
import { generateIdentifier } from './util';
|
||||
import { generateIdentifier, type IconDef } from './util';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
import { matchSorter } from 'match-sorter';
|
||||
import Spinner from './Spinner.svelte';
|
||||
@@ -40,6 +40,8 @@
|
||||
invalidMessage?: string | null;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
/** displayed by default if no item is selected that has its own icon */
|
||||
icon?: IconDef;
|
||||
/**
|
||||
* enables loading spinner and fallback text if no items are found. may be changed
|
||||
* internally if lazy is enabled. loading is, however, not bindable, so don't expect
|
||||
@@ -76,6 +78,7 @@
|
||||
invalidMessage = 'Please select an option',
|
||||
label,
|
||||
placeholder,
|
||||
icon,
|
||||
loading = false,
|
||||
lazy = false,
|
||||
compact = false,
|
||||
@@ -98,6 +101,7 @@
|
||||
let searchValue = $state('');
|
||||
let pickerPosition = $state<'top' | 'bottom'>('bottom');
|
||||
let searching = $state(false);
|
||||
let iconWidth = $state<number | undefined>(undefined);
|
||||
|
||||
let searchInput = $state<HTMLInputElement | null>(null);
|
||||
let searchContainer = $state<HTMLDivElement | null>(null);
|
||||
@@ -121,13 +125,12 @@
|
||||
});
|
||||
|
||||
/** controls whether an icon should be displayed */
|
||||
let iconVisible = $derived.by(() => {
|
||||
return (
|
||||
(open && highlighted && highlighted.icon) ||
|
||||
let iconVisible = $derived(
|
||||
(open && highlighted && highlighted.icon) ||
|
||||
(value && value.icon && searchValue === '') ||
|
||||
loading
|
||||
);
|
||||
});
|
||||
loading ||
|
||||
icon !== undefined
|
||||
);
|
||||
|
||||
/** controls whether the highlighted option should be used in the selection preview */
|
||||
let useHighlighted = $derived.by(() => {
|
||||
@@ -494,9 +497,11 @@
|
||||
{#if iconVisible}
|
||||
<div
|
||||
class={[
|
||||
(iconWidth === undefined || iconWidth === 0) && 'opacity-0',
|
||||
'pointer-events-none absolute top-1/2 left-3.5 -translate-y-1/2 transform select-none'
|
||||
]}
|
||||
transition:scale
|
||||
bind:clientWidth={iconWidth}
|
||||
>
|
||||
{#if loading}
|
||||
<Spinner class="stroke-sui-accent! -mt-0.5" size="1em" />
|
||||
@@ -504,6 +509,8 @@
|
||||
{@render highlighted.icon(highlighted)}
|
||||
{:else if value?.icon}
|
||||
{@render value.icon(value)}
|
||||
{:else if icon}
|
||||
<icon.component {...icon.props} />
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
@@ -511,7 +518,8 @@
|
||||
{/if}
|
||||
|
||||
<StyledRawInput
|
||||
class={[iconVisible && 'pl-10', caret && 'pr-9', !valid && 'border-red-500!']}
|
||||
style={iconWidth && iconVisible ? `padding-left: ${iconWidth + 14 + 10}px` : undefined}
|
||||
class={[caret && 'pr-9', !valid && 'border-red-500!']}
|
||||
{compact}
|
||||
type="text"
|
||||
name={name + '_search'}
|
||||
|
||||
@@ -151,6 +151,12 @@
|
||||
/>
|
||||
|
||||
<Combobox
|
||||
icon={{
|
||||
component: Plus,
|
||||
props: {
|
||||
size: '1.2em'
|
||||
}
|
||||
}}
|
||||
label="Lazy combobox"
|
||||
placeholder="Choose..."
|
||||
options={lazyOptions}
|
||||
|
||||
Reference in New Issue
Block a user