Compare commits

...

2 Commits

Author SHA1 Message Date
philipp lang 8b9b38ef48 Add Radio field 2023-12-24 23:26:37 +01:00
philipp lang 056e4c4bec update fields 2023-12-24 23:12:41 +01:00
6 changed files with 109 additions and 7 deletions

View File

@ -12,6 +12,7 @@
{"name": "Personal", "intro": "Jaöaöd", "fields": [ {"name": "Personal", "intro": "Jaöaöd", "fields": [
{"name": "Vorname", "type": "text", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": "", "required": true}, {"name": "Vorname", "type": "text", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": "", "required": true},
{"name": "Geschlecht", "type": "select", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": null, "required": true, "options": ["A","Bb","Cc"]}, {"name": "Geschlecht", "type": "select", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": null, "required": true, "options": ["A","Bb","Cc"]},
{"name": "Essgewohnheiten", "type": "radio", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": null, "required": true, "options": ["A","Bb","Cc"]},
{"name": "Nachname", "type": "text", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": "", "required": true} {"name": "Nachname", "type": "text", "columns": {"mobile": 2, "tablet": 3, "desktop": 6}, "default": "", "required": true}
]}, ]},
{"name": "Veranstaltung", "intro": "Jaöaöd", "fields": [ {"name": "Veranstaltung", "intro": "Jaöaöd", "fields": [

View File

@ -86,6 +86,8 @@ import {camelCase} from 'change-case';
import Navigation from './components/Navigation.vue'; import Navigation from './components/Navigation.vue';
import useNav from './composables/useNav.js'; import useNav from './composables/useNav.js';
import FieldText from './components/fields/Text.vue'; import FieldText from './components/fields/Text.vue';
import FieldSelect from './components/fields/Select.vue';
import FieldRadio from './components/fields/Radio.vue';
const props = defineProps({ const props = defineProps({
eventId: { eventId: {
@ -110,6 +112,7 @@ function resolveComponentName(field) {
return { return {
text: FieldText, text: FieldText,
select: FieldSelect, select: FieldSelect,
radio: FieldRadio,
}[field.type]; }[field.type];
} }

View File

@ -0,0 +1,16 @@
<template>
<span class="absolute text-gray-600 flex bg-white items-center -top-3 px-1 text-xs xs:text-sm" :class="{'left-0': inline, 'left-2': !inline}">
<span v-text="name"></span> <span v-show="required" class="text-red-800 ml-1">*</span>
</span>
</template>
<script setup>
defineProps({
name: {},
required: {},
inline: {
type: Boolean,
default: () => false,
},
});
</script>

View File

@ -0,0 +1,44 @@
<template>
<div class="relative">
<div class="grid grid-cols-1 gap-2 mt-3">
<label v-for="(option, index) in field.options" :key="index" :for="`${field.key}-${index}`" class="block relative flex items-center">
<input type="radio" :name="field.key" :value="option" v-model="inner" class="peer absolute invisible" :id="`${field.key}-${index}`" />
<span class="border-neutral-400 border-4 border-solid peer-checked:border-primary absolute left-0 w-6 h-6 rounded-full block"></span>
<span class="peer-checked:bg-primary left-2 w-2 h-2 absolute rounded-full block"></span>
<span class="pl-8" v-text="option"></span>
</label>
</div>
<field-label :name="field.name" :required="field.required" inline></field-label>
</div>
</template>
<script setup>
import {computed} 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',
},
field: {
required: true,
validator: (value) =>
hasKeys(value, ['required', 'type', 'key', 'columns', 'name', 'default', 'options']) &&
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']) &&
typeof value.options === 'object',
},
});
const inner = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v),
});
</script>

View File

@ -0,0 +1,39 @@
<template>
<label class="w-full border border-solid border-gray-500 focus-within:border-primary rounded-lg relative flex" :for="field.key">
<select :name="field.key" :id="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" v-model="inner">
<option :value="null">-- kein --</option>
<option v-for="(option, index) in field.options" :key="index" :value="option" v-text="option"></option>
</select>
<field-label :name="field.name" :required="field.required"></field-label>
</label>
</template>
<script setup>
import {computed} 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',
},
field: {
required: true,
validator: (value) =>
hasKeys(value, ['required', 'type', 'key', 'columns', 'name', 'default', 'options']) &&
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']) &&
typeof value.options === 'object',
},
});
const inner = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v),
});
</script>

View File

@ -5,18 +5,17 @@
v-model="inner" v-model="inner"
:name="field.key" :name="field.key"
type="text" type="text"
placeholder=" " placeholder=""
class="bg-white rounded-lg focus:outline-none text-gray-600 text-left placeholder-white peer py-1 px-2 sm:py-2 text-sm sm:text-base sm:px-3 w-full" class="bg-white rounded-lg focus:outline-none text-gray-600 text-left py-1 px-2 sm:py-2 text-sm sm:text-base sm:px-3 w-full"
/> />
<span <field-label :name="field.name" :required="field.required"></field-label>
class="transition-all duration-200 absolute text-gray-600 left-2 flex bg-white items-center -top-3 px-1 peer-placeholder-shown:bottom-0 peer-placeholder-shown:-top-0 text-xs xs:text-sm peer-placeholder-shown:text-sm xs:peer-placeholder-shown:text-base peer-focus:text-xs xs:peer-focus:text-sm peer-focus:-top-3 peer-focus:bottom-auto"
><span v-text="field.name"></span> <span v-if="field.required" class="text-red-800 ml-1">*</span></span
>
</label> </label>
</template> </template>
<script setup> <script setup>
import {computed} from 'vue'; import {computed} from 'vue';
import FieldLabel from '../FieldLabel.vue';
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -26,7 +25,7 @@ const props = defineProps({
field: { field: {
required: true, required: true,
validator: (value) => validator: (value) =>
hasKeys(value, ['required', 'key', 'columns', 'name', 'default']) && hasKeys(value, ['required', 'type', 'key', 'columns', 'name', 'default']) &&
typeof value.required === 'boolean' && typeof value.required === 'boolean' &&
typeof value.key === 'string' && typeof value.key === 'string' &&
value.key.length > 0 && value.key.length > 0 &&