combobox: improve icon padding behaviour

Tween the value and make it more consistent with the underlying
StyledRawInput type.
This commit is contained in:
Elijah Duffy
2026-03-10 14:55:44 -07:00
parent 7576f32e86
commit 3d240048c7

View File

@@ -50,6 +50,8 @@
import { matchSorter } from 'match-sorter';
import Spinner from './Spinner.svelte';
import type { KeyOption } from 'match-sorter';
import { Tween } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
interface Props {
/**
@@ -147,7 +149,6 @@
*/
searchKeys?: ('value' | 'label' | 'preview' | 'infotext')[];
// TODO: implement searchKeys above.
/** Additional classes applied to the persistent div container */
class?: ClassValue | null | undefined;
/** Optional action applied to the main input */
@@ -216,7 +217,6 @@
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);
@@ -264,6 +264,29 @@
/** validation options build from props */
const validateOpts: ValidatorOptions = $derived({ required });
/** calculates padding for the input based on icon visibility and size */
const calculatePadding = () => {
const gap = 5; // gap between icon and text
// base padding when no icon is visible, see StyledRawInput padding
const basePadding = compact ? 12 : 18;
// if icon is visible, padding is icon width + gap + base padding,
// otherwise it's just base padding
return iconVisible ? iconWidth + gap + basePadding : basePadding;
};
let iconWidth = $state<number>(0);
/** tweens main input padding */
const inputPadding = new Tween(calculatePadding(), {
duration: 150,
easing: cubicOut
});
$effect(() => {
if (iconWidth >= 0) {
untrack(() => {
inputPadding.target = calculatePadding();
});
}
});
/*** HELPER FUNCTIONS ***/
/** returns the index of the highlighted item in filteredItems */
@@ -637,8 +660,8 @@
{#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'
'pointer-events-none absolute top-1/2 left-3.5 -translate-y-1/2 transform select-none',
iconWidth === 0 && 'opacity-0'
]}
transition:scale
bind:clientWidth={iconWidth}
@@ -663,7 +686,7 @@
<!-- Combobox input box -->
<StyledRawInput
style={iconWidth && iconVisible ? `padding-left: ${iconWidth + 14 + 10}px` : undefined}
style={`padding-left: ${inputPadding.current}px`}
class={[caret && 'pr-9', !valid && 'border-red-500!']}
{compact}
type="text"