mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-10 18:38:22 +00:00
Add EnumInput (WIP)
- Also Slight linting adjustment - Slight form style adustment - Visually descendant fields version
This commit is contained in:
parent
b3bfbefa5a
commit
7d2ef675f7
4 changed files with 101 additions and 1 deletions
|
@ -89,6 +89,7 @@
|
||||||
"vue/first-attribute-linebreak": "off",
|
"vue/first-attribute-linebreak": "off",
|
||||||
"vue/max-attributes-per-line": "off",
|
"vue/max-attributes-per-line": "off",
|
||||||
"vue/html-closing-bracket-newline": "off",
|
"vue/html-closing-bracket-newline": "off",
|
||||||
|
"vue/no-dupe-keys": "off",
|
||||||
"vue/singleline-html-element-content-newline": "off",
|
"vue/singleline-html-element-content-newline": "off",
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
|
|
84
client/src/components/inputs/EnumInput.vue
Normal file
84
client/src/components/inputs/EnumInput.vue
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Index, MaybeIndex, equals } from '../../util';
|
||||||
|
import NicerForm, { Fields } from './NicerForm.vue';
|
||||||
|
|
||||||
|
export type Variant = {
|
||||||
|
fields?: Fields,
|
||||||
|
display?: string,
|
||||||
|
icon?: Component
|
||||||
|
};
|
||||||
|
export type Variants = Record<Index, Variant>;
|
||||||
|
export type EnumValueWithFields = { [index: Index]: Record<Index, any> }
|
||||||
|
export type EnumValue = Index | EnumValueWithFields;
|
||||||
|
export type MaybeEnumValue = EnumValue | null;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
// Two-Way Bindings
|
||||||
|
modelValue?: MaybeEnumValue,
|
||||||
|
|
||||||
|
// One-Way Bindings
|
||||||
|
variants?: Variants,
|
||||||
|
}>(), {
|
||||||
|
modelValue: null,
|
||||||
|
variants: () => ({}),
|
||||||
|
});
|
||||||
|
const { variants } = $(props);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: MaybeEnumValue): void
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Local Variables for Two-Way bindings
|
||||||
|
let modelValue: MaybeEnumValue = $ref(null);
|
||||||
|
// Sync from v-model
|
||||||
|
onMounted(() => {
|
||||||
|
watch(() => props.modelValue, (val) => {
|
||||||
|
if (equals(val, modelValue)) return;
|
||||||
|
[currentVariant, formValue] = val ?
|
||||||
|
typeof val === 'string'
|
||||||
|
? [val, {}]
|
||||||
|
: [Object.keys(val)[0], (val as EnumValueWithFields)[Object.keys(val)[0]]]
|
||||||
|
: [null, {}];
|
||||||
|
if (modelValue && typeof val === 'object' && typeof modelValue === 'object') modelValue = Object.assign(modelValue, val);
|
||||||
|
else modelValue = val;
|
||||||
|
}, { deep: true, immediate: true });
|
||||||
|
});
|
||||||
|
// Sync to v-model
|
||||||
|
watch($$(modelValue), (val) => {
|
||||||
|
if (equals(val, props.modelValue)) return;
|
||||||
|
emit('update:modelValue', typeof val === 'string' ? val : Object.assign({}, val));
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
let currentVariant: MaybeIndex = $ref(null);
|
||||||
|
let formValue: Record<Index, any> = $ref({});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!currentVariant) modelValue = null;
|
||||||
|
else modelValue = variants[currentVariant].fields
|
||||||
|
? { [currentVariant]: formValue ?? {} }
|
||||||
|
: currentVariant;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="enum-input">
|
||||||
|
<div class="pillbar">
|
||||||
|
<button class="variant" v-for="[index, variant] of Object.entries(variants)" :key="index" :class="{selected: currentVariant === index}" @click="() => currentVariant = index">
|
||||||
|
<component v-if="variant.icon" :is="variant.icon"/>
|
||||||
|
<template v-else>{{ variant.display }}</template>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<NicerForm class="enum-fields" v-if="currentVariant && variants[currentVariant]?.fields" :fields="variants[currentVariant].fields" v-model="formValue" :key="currentVariant"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.pillbar {
|
||||||
|
flex-flow: nowrap;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
.variant { padding: 0.25rem; gap: 0.25rem; }
|
||||||
|
.selected { background-color: var(--cl-bg-sl); }
|
||||||
|
</style>
|
|
@ -59,5 +59,7 @@ watch($$(modelValue), (val) => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
label::after {
|
||||||
|
content: ":";
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,6 +3,8 @@ import SingleSelect from '../components/inputs/SingleSelect.vue';
|
||||||
import { SearchProvider, Options } from '../components/inputs/DropdownInput.vue';
|
import { SearchProvider, Options } from '../components/inputs/DropdownInput.vue';
|
||||||
import MultiSelect from '../components/inputs/MultiSelect.vue';
|
import MultiSelect from '../components/inputs/MultiSelect.vue';
|
||||||
import NicerForm from '../components/inputs/NicerForm.vue';
|
import NicerForm from '../components/inputs/NicerForm.vue';
|
||||||
|
import EnumInput from '../components/inputs/EnumInput.vue';
|
||||||
|
import TextBox from '../components/inputs/TextBox.vue';
|
||||||
|
|
||||||
const testValues: Options = {
|
const testValues: Options = {
|
||||||
1: { display: 'Option 1' },
|
1: { display: 'Option 1' },
|
||||||
|
@ -28,9 +30,20 @@ function genSP(indexIsChar: boolean): SearchProvider {
|
||||||
<NicerForm :fields="{
|
<NicerForm :fields="{
|
||||||
Single: { is: SingleSelect, label: 'SingleSelect', props: { options: testValues, searchProvider: genSP(true) } },
|
Single: { is: SingleSelect, label: 'SingleSelect', props: { options: testValues, searchProvider: genSP(true) } },
|
||||||
Multiple: { is: MultiSelect, label: 'Multiselect', props: { options: testValues, searchProvider: genSP(false) } },
|
Multiple: { is: MultiSelect, label: 'Multiselect', props: { options: testValues, searchProvider: genSP(false) } },
|
||||||
|
IP: { is: EnumInput, label: 'IP Address', props: { variants: {
|
||||||
|
'dhcp': { display: 'DHCP' },
|
||||||
|
'static-ipv4': {
|
||||||
|
display: 'Static (IPV4)',
|
||||||
|
fields: {
|
||||||
|
ip: { is: TextBox, label: 'IP Address + CIDR' },
|
||||||
|
gw: { is: TextBox, label: 'Gateway Address' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
}" v-model="vm"/>
|
}" 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>
|
||||||
|
<button @click="() => { vm.IP = {'static-ipv4': {ip: '192.168.69.2/24', gw: '192.168.69.1' }} }">Click me but kinda pog wth</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Add table
Reference in a new issue