Add group select

This commit is contained in:
philipp lang 2023-12-31 14:20:32 +01:00
parent 9bdd1447ad
commit 307796dfcb
7 changed files with 2522 additions and 2337 deletions

View File

@ -8,8 +8,8 @@
<body style="padding: 50px"> <body style="padding: 50px">
<event-form <event-form
style="--primary: hsl(181, 75%, 26%); --secondary: hsl(181, 75%, 35%); --font: hsl(181, 84%, 78%); --circle: hsl(181, 86%, 16%)" style="--primary: hsl(181, 75%, 26%); --secondary: hsl(181, 75%, 35%); --font: hsl(181, 84%, 78%); --circle: hsl(181, 86%, 16%)"
fields='[{"id":"TextField","name":"Text","default":{"name":"","type":"TextField","columns":{"mobile":2,"tablet":4,"desktop":12},"default":"","required":false}}]' value='{"sections":[{"name":"ff","intro":"ff","fields":[{"name":"sdsdsd","type":"CheckboxField","columns":{"mobile":2,"tablet":4,"desktop":6},"default":false,"required":true,"description":"sds","key":"sdsdsd"},{"name":"Bezirk","type":"GroupField","columns":{"mobile":2,"tablet":4,"desktop":6},"default":"","required":true,"parent_field":null,"parent_group":120,"key":"bezirk"},{"name":"Stamm","type":"GroupField","columns":{"mobile":2,"tablet":4,"desktop":6},"default":"","required":true,"parent_field":"bezirk","parent_group":null,"key":"stamm"}]}]}'
value='{"sections":[{"name":"a","fields":[{"name":"test","type":"CheckboxesField","columns":{"mobile":2,"tablet":4,"desktop":6},"default":[],"required":false,"options":["a","b"],"key":"test"},{"name":"eee","type":"CheckboxField","columns":{"mobile":2,"tablet":4,"desktop":5},"default":false,"required":true,"description":"test","key":"eee"},{"name":"dd","type":"DropdownField","columns":{"mobile":1,"tablet":2,"desktop":4},"default":null,"required":true,"options":["a","b","c"],"key":"dd"},{"name":"aaaa","type":"RadioField","columns":{"mobile":2,"tablet":4,"desktop":6},"default":null,"required":true,"options":["a","d"],"key":"aaaa"},{"name":"assa","type":"TextareaField","columns":{"mobile":2,"tablet":4,"desktop":6},"default":null,"required":true,"rows":5,"key":"assa"}]}]}' base-url="http://localhost:8000"
editable editable
></event-form> ></event-form>
<script type="module" src="src/main.js"></script> <script type="module" src="src/main.js"></script>

4755
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/container-queries": "^0.1.1",
"@vitejs/plugin-vue": "^4.5.2", "@vitejs/plugin-vue": "^4.5.2",
"vite": "^5.0.8", "vite": "^5.0.8",
"vue3-carousel": "^0.3.1" "vue3-carousel": "^0.3.1",
"axios": "^1.6.3"
} }
} }

View File

@ -96,7 +96,7 @@
[colClasses.desktop[field.columns.desktop]]: true, [colClasses.desktop[field.columns.desktop]]: true,
}" }"
> >
<component :is="resolveComponentName(field)" v-model="payload[field.key]" :field="field"></component> <component :is="resolveComponentName(field)" v-model="payload[field.key]" :payload="payload" :field="field"></component>
<div v-if="editable" class="right-0 top-0 -mr-2 -mt-2 absolute hidden space-x-2 group-hover:flex"> <div v-if="editable" class="right-0 top-0 -mr-2 -mt-2 absolute hidden space-x-2 group-hover:flex">
<a href="#" class="bg-edit rounded-full flex w-5 h-5 items-center justify-center text-font ml-2" @click.prevent.stop="$emit('editField', index, findex)"> <a href="#" class="bg-edit rounded-full flex w-5 h-5 items-center justify-center text-font ml-2" @click.prevent.stop="$emit('editField', index, findex)">
<edit-icon class="w-3 h-3 fill-current"></edit-icon> <edit-icon class="w-3 h-3 fill-current"></edit-icon>
@ -137,6 +137,7 @@ import FieldText from './components/fields/Text.vue';
import FieldDate from './components/fields/Date.vue'; import FieldDate from './components/fields/Date.vue';
import FieldTextarea from './components/fields/Textarea.vue'; import FieldTextarea from './components/fields/Textarea.vue';
import FieldDropdown from './components/fields/Dropdown.vue'; import FieldDropdown from './components/fields/Dropdown.vue';
import FieldGroup from './components/fields/Group.vue';
import FieldCheckboxes from './components/fields/Checkboxes.vue'; import FieldCheckboxes from './components/fields/Checkboxes.vue';
import FieldCheckbox from './components/fields/Checkbox.vue'; import FieldCheckbox from './components/fields/Checkbox.vue';
import FieldRadio from './components/fields/Radio.vue'; import FieldRadio from './components/fields/Radio.vue';
@ -184,9 +185,11 @@ const props = defineProps({
type: String, type: String,
default: () => '{}', default: () => '{}',
}, },
fields: {}, baseUrl: {},
}); });
axios.defaults.baseURL = props.baseUrl;
const active = ref(0); const active = ref(0);
const payload = ref({}); const payload = ref({});
const loaded = ref(false); const loaded = ref(false);
@ -211,7 +214,6 @@ const v = computed(() => {
emits('active', values.sections.length ? active.value : null); emits('active', values.sections.length ? active.value : null);
return values; return values;
}); });
const f = computed(() => JSON.parse(props.fields));
const {back, next, backable, nextable} = useNav(active, v.value.sections.length); const {back, next, backable, nextable} = useNav(active, v.value.sections.length);
@ -221,6 +223,7 @@ function resolveComponentName(field) {
DateField: FieldDate, DateField: FieldDate,
TextareaField: FieldTextarea, TextareaField: FieldTextarea,
DropdownField: FieldDropdown, DropdownField: FieldDropdown,
GroupField: FieldGroup,
RadioField: FieldRadio, RadioField: FieldRadio,
CheckboxesField: FieldCheckboxes, CheckboxesField: FieldCheckboxes,
CheckboxField: FieldCheckbox, CheckboxField: FieldCheckbox,

View File

@ -0,0 +1,84 @@
<template>
<label class="w-full border border-solid border-gray-500 focus-within:border-primary rounded-lg relative flex" :for="field.key">
<select
:id="field.key"
v-model="inner"
:disabled="disabled"
:name="field.key"
class="bg-white rounded-lg focus:outline-none text-gray-600 text-left peer py-1 px-2 @sm:py-2 text-sm @sm:text-base @sm:px-3 w-full"
>
<option :value="null">-- kein --</option>
<option v-for="(option, index) in options" :key="index" :value="option.id" v-text="option.name"></option>
</select>
<field-label :name="field.name" :required="field.required"></field-label>
</label>
</template>
<script setup>
import {computed, ref, watch} from 'vue';
import FieldLabel from '../FieldLabel.vue';
const emit = defineEmits(['update:modelValue']);
const props = defineProps({
modelValue: {
required: true,
validator: (value) => value === null || typeof value === 'string' || typeof value === 'number',
},
field: {
required: true,
validator: (value) =>
hasKeys(value, ['required', 'type', 'key', 'columns', 'name', 'default', 'parent_field', 'parent_group']) &&
typeof value.required === 'boolean' &&
typeof value.key === 'string' &&
value.key.length > 0 &&
typeof value.name === 'string' &&
value.name.length > 0 &&
hasKeys(value.columns, ['mobile', 'desktop', 'tablet']),
},
payload: {},
});
const inner = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v),
});
const options = ref([]);
const disabled = ref(false);
async function refreshOptions() {
if (props.field.parent_group !== null) {
emit('update:modelValue', null);
options.value = (await axios.get('/api/group/' + props.field.parent_group)).data.data;
disabled.value = false;
return;
}
if (props.field.parent_field !== null && props.field.parent_field in props.payload && !props.payload[props.field.parent_field]) {
options.value = [];
emit('update:modelValue', null);
disabled.value = true;
return;
}
if (props.field.parent_field !== null && props.field.parent_field in props.payload && props.payload[props.field.parent_field]) {
emit('update:modelValue', null);
options.value = (await axios.get('/api/group/' + props.payload[props.field.parent_field])).data.data;
disabled.value = false;
return;
}
}
refreshOptions();
if (props.field.parent_field) {
watch(
() => props.payload[props.field.parent_field],
function (oldValue, newValue) {
if (oldValue !== newValue) {
refreshOptions();
}
}
);
}
</script>

View File

@ -0,0 +1 @@
export default {};

View File

@ -4,6 +4,9 @@ import carousel from 'vue3-carousel/dist/carousel.css?inline';
import carouselStyle from './carousel.css?inline'; import carouselStyle from './carousel.css?inline';
import Navigation from './components/Navigation.vue'; import Navigation from './components/Navigation.vue';
import axios from 'axios';
window.axios = axios;
window.hasKeys = function (object, expected) { window.hasKeys = function (object, expected) {
return typeof object === 'object' && JSON.stringify(Object.keys(object).sort()) === JSON.stringify(expected.sort()); return typeof object === 'object' && JSON.stringify(Object.keys(object).sort()) === JSON.stringify(expected.sort());
}; };