Started melt combobox implementation
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<script lang="ts">
|
||||
import { _ } from '$lib/i18n';
|
||||
import { mergeAttrs } from 'melt';
|
||||
import { Combobox } from 'melt/builders';
|
||||
|
||||
type ComboOption = { label: string; value: string };
|
||||
|
||||
let {
|
||||
options,
|
||||
label,
|
||||
defaultValue = undefined,
|
||||
onChange = undefined
|
||||
}: {
|
||||
options: ComboOption[];
|
||||
label: string;
|
||||
defaultValue?: string;
|
||||
onChange?: (value: string) => void;
|
||||
} = $props();
|
||||
|
||||
let initialValueChange = true;
|
||||
const combobox = new Combobox<string>({
|
||||
onValueChange: (value) => {
|
||||
if (initialValueChange && typeof defaultValue !== 'undefined') {
|
||||
initialValueChange = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value) onChange?.(value);
|
||||
}
|
||||
});
|
||||
|
||||
if (defaultValue) combobox.select(defaultValue);
|
||||
|
||||
let valueToLabel: Record<string, string> = {};
|
||||
|
||||
setValuesToLabel();
|
||||
|
||||
function setValuesToLabel() {
|
||||
for (const option of options) {
|
||||
valueToLabel[option.value] = option.label;
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
setValuesToLabel();
|
||||
});
|
||||
|
||||
const filtered = $derived.by(() => {
|
||||
if (!combobox.touched) return options;
|
||||
return options.filter((o) =>
|
||||
o.label.toLowerCase().includes(combobox.inputValue.trim().toLowerCase())
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="field label suffix surface-container-highest">
|
||||
<input
|
||||
{...mergeAttrs(combobox.input, {
|
||||
value: valueToLabel[combobox.input.value]
|
||||
})}
|
||||
/>
|
||||
<label {...combobox.label}>{label}</label>
|
||||
<i>keyboard_arrow_down</i>
|
||||
|
||||
<div {...combobox.content}>
|
||||
{#each filtered as option (option)}
|
||||
<div {...combobox.getOption(option.value)}>
|
||||
{option.label}
|
||||
{#if combobox.isSelected(option.value)}
|
||||
<i>check</i>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<span>{$_('noResult')}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,6 +35,7 @@
|
||||
} from '../../store';
|
||||
import { tick } from 'svelte';
|
||||
import { isOwnBackend } from '$lib/shared';
|
||||
import ComboBox from '../ComboBox.svelte';
|
||||
|
||||
let invidiousInstance = $state(get(invidiousInstanceStore));
|
||||
let region = $state(get(interfaceRegionStore));
|
||||
@@ -81,10 +82,8 @@
|
||||
location.reload();
|
||||
}
|
||||
|
||||
async function setBackend(event: Event) {
|
||||
const select = event.target as HTMLSelectElement;
|
||||
backendInUseStore.set(select.value as 'ivg' | 'yt');
|
||||
|
||||
async function setBackend(backend: string) {
|
||||
backendInUseStore.set(backend as 'ivg' | 'yt');
|
||||
await timeout(100);
|
||||
location.reload();
|
||||
}
|
||||
@@ -120,14 +119,15 @@
|
||||
</script>
|
||||
|
||||
{#if isUnrestrictedPlatform()}
|
||||
<div class="field label suffix surface-container-highest">
|
||||
<select name="backend-in-use" onchange={setBackend}>
|
||||
<option selected={$backendInUseStore === 'ivg'} value="ivg">Invidious</option>
|
||||
<option selected={$backendInUseStore === 'yt'} value="yt">YouTube (Experimental)</option>
|
||||
</select>
|
||||
<label for="backend-in-use">{$_('backend')}</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
<ComboBox
|
||||
label={$_('backend')}
|
||||
defaultValue={$backendInUseStore}
|
||||
onChange={setBackend}
|
||||
options={[
|
||||
{ label: 'Invidious', value: 'ivg' },
|
||||
{ label: 'YouTube (Experimental)', value: 'yt' }
|
||||
]}
|
||||
/>
|
||||
|
||||
{#if $backendInUseStore === 'ivg'}
|
||||
<form onsubmit={setInstance}>
|
||||
|
||||
@@ -194,6 +194,22 @@ menu.player-settings {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
[data-melt-combobox-content] {
|
||||
background-color: var(--surface-container-high);
|
||||
padding: 1rem;
|
||||
font-size: 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
[data-melt-combobox-option] {
|
||||
padding: 0.25rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-highlighted] {
|
||||
background-color: var(--surface-container-highest);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px) {
|
||||
menu.mobile {
|
||||
position: fixed !important;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user