Added the ability to set custom pixel values in the combobox
This commit is contained in:
parent
3be006dcaf
commit
b519235e0a
|
|
@ -3,6 +3,7 @@
|
||||||
import * as Command from '$lib/components/ui/command';
|
import * as Command from '$lib/components/ui/command';
|
||||||
import * as Popover from '$lib/components/ui/popover';
|
import * as Popover from '$lib/components/ui/popover';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
|
import { Input } from '$lib/components/ui/input';
|
||||||
import { cn } from '$lib/components/ui/utils';
|
import { cn } from '$lib/components/ui/utils';
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
import { CUSTOM_WIDTH_PRESETS } from '$lib/utils/chat-width';
|
import { CUSTOM_WIDTH_PRESETS } from '$lib/utils/chat-width';
|
||||||
|
|
@ -14,20 +15,41 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let { value = $bindable(''), onChange, disabled = false }: Props = $props();
|
let { value = $bindable(''), onChange, disabled = false }: Props = $props();
|
||||||
|
console.log('Rendering CustomWidthCombobox with value:', value);
|
||||||
|
|
||||||
let open = $state(false);
|
let open = $state(false);
|
||||||
|
let showCustomPixelInput = $state(false);
|
||||||
|
let customPixelValue = $state('');
|
||||||
|
let invalidCustomPixel = $state(false);
|
||||||
|
|
||||||
let triggerRef = $state<HTMLButtonElement>(null!);
|
let triggerRef = $state<HTMLButtonElement>(null!);
|
||||||
console.log("Rendering CustomWidthCombobox with value:", value);
|
let customPixelInputRef = $state<HTMLInputElement>(null!);
|
||||||
|
|
||||||
const widthPresets = Object.entries(CUSTOM_WIDTH_PRESETS).map(([key, pixelValue]) => ({
|
const widthPresets = Object.entries(CUSTOM_WIDTH_PRESETS).map(([key, pixelValue]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: `${key} (${pixelValue}px)`
|
label: `${key} (${pixelValue}px)`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const isValueANumber = $derived.by(() => {
|
||||||
|
const numValue = Number(value);
|
||||||
|
if (!isNaN(numValue) && numValue > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
const displayValue = $derived.by(() => {
|
const displayValue = $derived.by(() => {
|
||||||
|
if (showCustomPixelInput) {
|
||||||
|
return 'Set pixel value';
|
||||||
|
}
|
||||||
|
|
||||||
const selectedWidthPreset = widthPresets.find((preset) => preset.value === value);
|
const selectedWidthPreset = widthPresets.find((preset) => preset.value === value);
|
||||||
if (selectedWidthPreset) return selectedWidthPreset.label;
|
if (selectedWidthPreset) return selectedWidthPreset.label;
|
||||||
|
|
||||||
|
if (isValueANumber) {
|
||||||
|
return `Custom (${Number(value)}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
return 'Select width...';
|
return 'Select width...';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -40,9 +62,50 @@
|
||||||
|
|
||||||
function handleSelectPreset(newValue: string) {
|
function handleSelectPreset(newValue: string) {
|
||||||
value = newValue;
|
value = newValue;
|
||||||
|
showCustomPixelInput = false;
|
||||||
onChange(newValue);
|
onChange(newValue);
|
||||||
closeAndFocusTrigger();
|
closeAndFocusTrigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleShowCustomPixelInput() {
|
||||||
|
open = false;
|
||||||
|
showCustomPixelInput = true;
|
||||||
|
|
||||||
|
tick().then(() => {
|
||||||
|
if (customPixelInputRef) customPixelInputRef.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelCustomPixelInput() {
|
||||||
|
showCustomPixelInput = false;
|
||||||
|
customPixelValue = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSaveCustomPixelInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSaveCustomPixelInput() {
|
||||||
|
if (!customPixelValue) {
|
||||||
|
invalidCustomPixel = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const number = Number(customPixelValue);
|
||||||
|
const isValid = !isNaN(number) && number >= 300 && number <= 10000;
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
value = String(number);
|
||||||
|
onChange(String(number));
|
||||||
|
invalidCustomPixel = false;
|
||||||
|
showCustomPixelInput = false;
|
||||||
|
} else {
|
||||||
|
invalidCustomPixel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex w-full flex-col gap-2">
|
<div class="flex w-full flex-col gap-2">
|
||||||
|
|
@ -70,10 +133,25 @@
|
||||||
<Command.Empty>No presets found.</Command.Empty>
|
<Command.Empty>No presets found.</Command.Empty>
|
||||||
|
|
||||||
<Command.Group value="width-presets">
|
<Command.Group value="width-presets">
|
||||||
|
<Command.Item value="set-custom-pixel-value" onSelect={handleShowCustomPixelInput}>
|
||||||
|
<Check
|
||||||
|
class={cn(
|
||||||
|
'mr-2 h-4 w-4',
|
||||||
|
showCustomPixelInput || isValueANumber ? 'opacity-100' : 'opacity-0'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
Set pixel value
|
||||||
|
</Command.Item>
|
||||||
|
|
||||||
|
<Command.Separator class="my-1" />
|
||||||
|
|
||||||
{#each widthPresets as preset (preset.value)}
|
{#each widthPresets as preset (preset.value)}
|
||||||
<Command.Item value={preset.value} onSelect={() => handleSelectPreset(preset.value)}>
|
<Command.Item value={preset.value} onSelect={() => handleSelectPreset(preset.value)}>
|
||||||
<Check
|
<Check
|
||||||
class={cn('mr-2 h-4 w-4', value === preset.value ? 'opacity-100' : 'opacity-0')}
|
class={cn(
|
||||||
|
'mr-2 h-4 w-4',
|
||||||
|
value === preset.value && !showCustomPixelInput ? 'opacity-100' : 'opacity-0'
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{preset.label}
|
{preset.label}
|
||||||
</Command.Item>
|
</Command.Item>
|
||||||
|
|
@ -83,4 +161,33 @@
|
||||||
</Command.Root>
|
</Command.Root>
|
||||||
</Popover.Content>
|
</Popover.Content>
|
||||||
</Popover.Root>
|
</Popover.Root>
|
||||||
|
|
||||||
|
{#if showCustomPixelInput}
|
||||||
|
<div class="animate-in fade-in slide-in-from-top-1 flex items-center gap-2 duration-200">
|
||||||
|
<Input
|
||||||
|
bind:ref={customPixelInputRef}
|
||||||
|
bind:value={customPixelValue}
|
||||||
|
onkeydown={handleKeydown}
|
||||||
|
type="number"
|
||||||
|
placeholder="e.g. 800"
|
||||||
|
class={cn(
|
||||||
|
'flex-1',
|
||||||
|
invalidCustomPixel && 'border-destructive focus-visible:ring-destructive'
|
||||||
|
)}
|
||||||
|
step="1"
|
||||||
|
min="300"
|
||||||
|
max="10000"
|
||||||
|
/>
|
||||||
|
<Button onclick={handleSaveCustomPixelInput}>Save</Button>
|
||||||
|
<Button variant="outline" onclick={handleCancelCustomPixelInput}>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class={cn(
|
||||||
|
'ml-1 text-[0.8rem]',
|
||||||
|
invalidCustomPixel ? 'text-destructive' : 'text-muted-foreground'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Enter a value between 300 and 10000 pixels.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue