mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-11 02:48:21 +00:00
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:
parent
cc16f81067
commit
e749f68fef
3 changed files with 50 additions and 39 deletions
|
@ -43,42 +43,47 @@ const emit = defineEmits<{
|
||||||
(e: 'update:options', value: Options): void,
|
(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);
|
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);
|
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), () => {
|
watch($$(search), () => {
|
||||||
emit('update:search', search);
|
emit('update:search', search);
|
||||||
expand();
|
expand();
|
||||||
}, { deep: true });
|
}, { 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($$(options), () => emit('update:options', options), { deep: true });
|
||||||
|
|
||||||
watch($$(multiple), () => modelValue = multiple ? [] : null );
|
watch($$(multiple), () => modelValue = multiple ? [] : null );
|
||||||
|
|
||||||
// --- Everything Else ---
|
// --- Everything Else ---
|
||||||
|
@ -200,7 +205,7 @@ function handleKeydown(e: KeyboardEvent) {
|
||||||
<Transition name="fade-fast">
|
<Transition name="fade-fast">
|
||||||
<div tabindex="-1" class="dropdown" v-if="expanded">
|
<div tabindex="-1" class="dropdown" v-if="expanded">
|
||||||
<div v-for="([key, option], index) in Object.entries(options)" :key="key"
|
<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)">
|
@click="() => toggle(key)">
|
||||||
<template v-if="multiple">
|
<template v-if="multiple">
|
||||||
<i-material-symbols-check-box-outline v-if="modelValue?.includes(key)" width="1em" height="1em"/>
|
<i-material-symbols-check-box-outline v-if="modelValue?.includes(key)" width="1em" height="1em"/>
|
||||||
|
|
|
@ -25,17 +25,18 @@ input {
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form, .form {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
form > :is(button, .button, h1) {
|
:is(form, .form) > :is(button, .button, h1) {
|
||||||
grid-column: 1 / 3;
|
grid-column: 1 / 3;
|
||||||
}
|
}
|
||||||
form > :is(label) {
|
:is(form, .form) > label {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
|
padding: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<script setup lang="ts">
|
<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 = {
|
const testValues: Options = {
|
||||||
1: { display: 'Option 1' },
|
1: { display: 'Option 1' },
|
||||||
|
@ -8,7 +11,7 @@ const testValues: Options = {
|
||||||
a: { display: 'Option a' },
|
a: { display: 'Option a' },
|
||||||
z: { display: 'Option z' },
|
z: { display: 'Option z' },
|
||||||
};
|
};
|
||||||
let vm: any = $ref({});
|
let vm: any = $ref({Multiple: [1]});
|
||||||
|
|
||||||
function genSP(indexIsChar: boolean): SearchProvider {
|
function genSP(indexIsChar: boolean): SearchProvider {
|
||||||
return async (o) => {
|
return async (o) => {
|
||||||
|
@ -22,10 +25,12 @@ function genSP(indexIsChar: boolean): SearchProvider {
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<PageHeader title="Test Page"/>
|
<PageHeader title="Test Page"/>
|
||||||
<SingleSelect :search-provider="genSP(true)" v-model="vm['Single']"/>
|
<NicerForm :fields="{
|
||||||
<MultiSelect :search-provider="genSP(false)" v-model="vm['Multiple']"/>
|
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 }}
|
{{ vm }}
|
||||||
<button @click="() => vm.Multiple = [1]">Click me</button>
|
<button @click="() => { vm.Multiple = [1]; }">Click me</button>
|
||||||
<button @click="() => vm.Multiple = [42]">Click me but EEEEVIL</button>
|
<button @click="() => { vm.Multiple = [42]; }">Click me but EEEEVIL</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Add table
Reference in a new issue