WIP DropdownInput

- Moved some setup to onMounted
- Added local prop bindings
- Strict to loose key comparison
- Generalized form style for .form as well
- Test page updated to include NicerForm and Selects
This commit is contained in:
adroslice 2023-10-22 22:16:08 +02:00
parent cc16f81067
commit e749f68fef
3 changed files with 50 additions and 39 deletions

View file

@ -43,42 +43,47 @@ const emit = defineEmits<{
(e: 'update:options', value: Options): void,
}>();
// Hook up two-way bindings
// Local Variables for Two-Way bindings
let modelValue = $ref(multiple ? props.modelValue ?? [] : props.modelValue);
watch(() => props.modelValue, async (val) => {
if (equals(val, modelValue)) return;
if (isNullish(val)) return modelValue = val; // Cant be unknown
// Run search provider if key unknown, log and reject if still so
let knownKeys = Object.keys(options);
if (multiple) {
let unknownKeys = (val as Index[]).filter(key => !knownKeys.includes(key.toString()));
if (!unknownKeys.length) return modelValue = val;
await performSearch(unknownKeys);
knownKeys = Object.keys(options);
unknownKeys = (val as Index[]).filter(key => !knownKeys.includes(key.toString()));
for (let key of unknownKeys) console.warn(`Unknown key in DropdownInput:`, key/*, options*/);
return modelValue = (val as Index[]).filter(key => knownKeys.includes(key.toString()));
}
if (!knownKeys.includes(val.toString())) {
await performSearch([val]);
knownKeys = Object.keys(options);
if (!knownKeys.includes(val.toString()))
return console.warn(`Unknown key in DropdownInput:`, val/*, options*/);
}
modelValue = val;
}, { deep: true, immediate: true });
watch($$(modelValue), () => emit('update:modelValue', modelValue), { deep: true });
let search = $ref(props.search);
watch(() => props.search, (val) => { if (!equals(val, search)) search = val; }, { deep: true });
let options = $ref(props.options);
// Sync from v-model
onMounted(() => {
watch(() => props.modelValue, async (val) => {
if (equals(val, modelValue)) return;
if (isNullish(val)) return modelValue = val; // Cant be unknown
// Run search provider if key unknown, log and reject if still so
let knownKeys = Object.keys(options);
if (multiple) {
let unknownKeys = (val as Index[]).filter(key => !knownKeys.includes(key.toString()));
if (!unknownKeys.length) return modelValue = val;
await performSearch(unknownKeys);
knownKeys = Object.keys(options);
unknownKeys = (val as Index[]).filter(key => !knownKeys.includes(key.toString()));
for (let key of unknownKeys) console.warn(`Unknown key in DropdownInput:`, key/*, options*/);
return modelValue = (val as Index[]).filter(key => knownKeys.includes(key.toString()));
}
if (!knownKeys.includes(val.toString())) {
await performSearch([val]);
knownKeys = Object.keys(options);
if (!knownKeys.includes(val.toString()))
return console.warn(`Unknown key in DropdownInput:`, val/*, options*/);
}
modelValue = val;
}, { deep: true, immediate: true });
watch(() => props.search, (val) => { if (!equals(val, search)) search = val; }, { deep: true });
watch(() => props.options, (val) => { if (!equals(val, options)) options = val; }, { deep: true });
});
// Sync to v-model
watch($$(modelValue), () => emit('update:modelValue', modelValue), { deep: true });
watch($$(search), () => {
emit('update:search', search);
expand();
}, { deep: true });
let options = $ref(props.options);
watch(() => props.options, (val) => { if (!equals(val, options)) options = val; }, { deep: true });
watch($$(options), () => emit('update:options', options), { deep: true });
watch($$(multiple), () => modelValue = multiple ? [] : null );
// --- Everything Else ---
@ -200,7 +205,7 @@ function handleKeydown(e: KeyboardEvent) {
<Transition name="fade-fast">
<div tabindex="-1" class="dropdown" v-if="expanded">
<div v-for="([key, option], index) in Object.entries(options)" :key="key"
:class="{selected: multiple ? modelValue?.includes(key) : key === modelValue, navigated: navigated === index + 1}"
:class="{selected: multiple ? modelValue?.includes(key) : key == modelValue, navigated: navigated === index + 1}"
@click="() => toggle(key)">
<template v-if="multiple">
<i-material-symbols-check-box-outline v-if="modelValue?.includes(key)" width="1em" height="1em"/>

View file

@ -25,17 +25,18 @@ input {
padding: 0.25rem;
}
form {
form, .form {
display: grid;
grid-template-columns: auto 1fr;
padding: 0.5rem;
gap: 0.5rem;
}
form > :is(button, .button, h1) {
:is(form, .form) > :is(button, .button, h1) {
grid-column: 1 / 3;
}
form > :is(label) {
:is(form, .form) > label {
grid-column: 1;
padding: 0.25rem;
}
table {

View file

@ -1,5 +1,8 @@
<script setup lang="ts">
import { SearchProvider, Options } from '~/components/inputs/DropdownInput.vue';
import SingleSelect from '../components/inputs/SingleSelect.vue';
import { SearchProvider, Options } from '../components/inputs/DropdownInput.vue';
import MultiSelect from '../components/inputs/MultiSelect.vue';
import NicerForm from '../components/inputs/NicerForm.vue';
const testValues: Options = {
1: { display: 'Option 1' },
@ -8,7 +11,7 @@ const testValues: Options = {
a: { display: 'Option a' },
z: { display: 'Option z' },
};
let vm: any = $ref({});
let vm: any = $ref({Multiple: [1]});
function genSP(indexIsChar: boolean): SearchProvider {
return async (o) => {
@ -22,10 +25,12 @@ function genSP(indexIsChar: boolean): SearchProvider {
<template>
<div>
<PageHeader title="Test Page"/>
<SingleSelect :search-provider="genSP(true)" v-model="vm['Single']"/>
<MultiSelect :search-provider="genSP(false)" v-model="vm['Multiple']"/>
<NicerForm :fields="{
Single: { is: SingleSelect, label: 'SingleSelect', props: { options: testValues, searchProvider: genSP(true) } },
Multiple: { is: MultiSelect, label: 'Multiselect', props: { options: testValues, searchProvider: genSP(false) } },
}" v-model="vm"/>
{{ vm }}
<button @click="() => vm.Multiple = [1]">Click me</button>
<button @click="() => vm.Multiple = [42]">Click me but EEEEVIL</button>
<button @click="() => { vm.Multiple = [1]; }">Click me</button>
<button @click="() => { vm.Multiple = [42]; }">Click me but EEEEVIL</button>
</div>
</template>