This commit is contained in:
Julien Nahum
2023-10-24 11:00:54 +02:00
parent d8c0371d43
commit 437644584a
34 changed files with 784 additions and 668 deletions

View File

@@ -132,7 +132,7 @@ export default {
methods: {
clearUrl () {
this.$set(this.form, this.name, null)
this.form[this.name] = null
},
onUploadDragoverEvent (e) {
this.uploadDragoverEvent = true

View File

@@ -50,11 +50,11 @@ export default {
this.onEnd()
},
onEnd () {
if(this.disabled){
if (this.disabled) {
this.$refs.signaturePad.clearSignature()
}else{
} else {
const { isEmpty, data } = this.$refs.signaturePad.saveSignature()
this.$set(this.form, this.name, (!isEmpty && data) ? data : null)
this.form[this.name] = (!isEmpty && data) ? data : null
}
}
}

View File

@@ -1,19 +1,14 @@
<template>
<div :class="wrapperClass" :style="inputStyle">
<slot name="label">
<input-label v-if="label"
:label="label"
:theme="theme"
:required="true"
:native-for="id?id:name"
:uppercase-labels="uppercaseLabels"
/>
</slot>
<input-help v-if="help && helpPosition=='above_input'" :help="help" :theme="theme">
<template #help>
<slot name="help" />
</template>
</input-help>
<input-wrapper
v-bind="$props"
>
<template #label>
<slot name="label" />
</template>
<template v-if="helpPosition==='above_input'" #help>
<slot name="help" />
</template>
<input :id="id?id:name" v-model="compVal" :disabled="disabled"
:type="nativeType"
:pattern="pattern"
@@ -23,25 +18,34 @@
:placeholder="placeholder" :min="min" :max="max" :maxlength="maxCharLimit"
@change="onChange" @keydown.enter.prevent="onEnterPress"
>
<!-- <input-help v-if="(help && helpPosition=='below_input') || showCharLimit" :help="help" :theme="theme">-->
<!-- TODO: fix this in the case of below input there's something off -->
<!-- <input-help v-if="helpPosition==='below_input' || showCharLimit" :help="help" :theme="theme">-->
<!-- <template #help>-->
<!-- <slot name="help" />-->
<!-- </template>-->
<!-- <template v-if="showCharLimit" #after-help>-->
<!-- <small v-if="showCharLimit && maxCharLimit" :class="theme.default.help">-->
<!-- {{ charCount }}/{{ maxCharLimit }}-->
<!-- </small>-->
<!-- </template>-->
<!-- </input-help>-->
<has-error v-if="hasValidation" :form="form" :field="name" />
</div>
<template #error>
<slot name="error" />
</template>
</input-wrapper>
</template>
<script>
import { inputProps, useFormInput } from './useFormInput.js'
import InputLabel from './components/InputLabel.vue'
import InputHelp from './components/InputHelp.vue'
import InputWrapper from './components/InputWrapper.vue'
export default {
name: 'TextInput',
components: { InputHelp, InputLabel },
components: { InputWrapper, InputHelp, InputLabel },
props: {
...inputProps,
@@ -54,11 +58,15 @@ export default {
pattern: { type: String, default: null }
},
setup (props) {
const { compVal, inputStyle, hasValidation, hasError } = useFormInput(props)
setup (props, context) {
const {
compVal,
inputStyle,
hasValidation,
hasError
} = useFormInput(props, context, props.nativeType === 'file' ? 'file-' : null)
const onChange = (event) => {
console.log(props)
if (props.nativeType !== 'file') return
const file = event.target.files[0]
@@ -77,6 +85,11 @@ export default {
hasValidation,
hasError
}
},
computed: {
charCount () {
return (this.compVal) ? this.compVal.length : 0
}
}
}
</script>

View File

@@ -1,36 +1,50 @@
<template>
<div :class="wrapperClass">
<small v-if="help && helpPosition=='above_input'" :class="theme.default.help" class="flex mb-1">
<slot name="help"><span class="field-help" v-html="help" /></slot>
</small>
<div class="flex">
<v-switch :id="id?id:name" v-model="compVal" class="inline-block mr-2" :disabled="disabled" :name="name" @input="$emit('input',$event)" />
<slot name="label">
<span>{{ label }} <span v-if="required" class="text-red-500 required-dot">*</span></span>
</slot>
</div>
<small v-if="help && helpPosition=='below_input'" :class="theme.default.help">
<slot name="help"><span class="field-help" v-html="help" /></slot>
</small>
<has-error v-if="hasValidation" :form="form" :field="name" />
<div :class="wrapperClass">
<input-help v-if="help && helpPosition=='above_input'" :help="help" :theme="theme">
<template #help>
<slot name="help" />
</template>
</input-help>
<div class="flex">
<v-switch :id="id?id:name" v-model="compVal" class="inline-block mr-2" :disabled="disabled" />
<slot name="label">
<span>{{ label }} <span v-if="required" class="text-red-500 required-dot">*</span></span>
</slot>
</div>
</template>
<script>
import inputMixin from '~/mixins/forms/input.js'
import VSwitch from './components/VSwitch.vue'
export default {
name: 'ToggleSwitchInput',
components: { VSwitch },
mixins: [inputMixin],
props: {},
mounted () {
this.compVal = !!this.compVal
this.$emit('input', !!this.compVal)
<input-help v-if="help && helpPosition=='below_input'" :help="help" :theme="theme">
<template #help>
<slot name="help" />
</template>
</input-help>
<has-error v-if="hasValidation" :form="form" :field="name" />
</div>
</template>
<script>
import { inputProps, useFormInput } from './useFormInput.js'
import InputHelp from './components/InputHelp.vue'
import VSwitch from './components/VSwitch.vue'
export default {
name: 'ToggleSwitchInput',
components: { InputHelp, VSwitch },
props: {
...inputProps
},
setup (props, context) {
const { compVal, inputStyle, hasValidation, hasError } = useFormInput(props, context)
return {
compVal,
inputStyle,
hasValidation,
hasError
}
},
mounted () {
this.compVal = !!this.compVal
}
</script>
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex mb-1">
<div class="flex mb-1 input-help">
<small :class="theme.default.help" class="grow flex">
<slot name="help"><span class="field-help" v-html="help" /></slot>
</small>
@@ -15,7 +15,7 @@ export default {
props: {
theme: { type: Object, required: true },
help: { type: String, required: true }
help: { type: String, required: false }
}
}
</script>

View File

@@ -1,9 +1,12 @@
<template>
<label :for="nativeFor"
class="input-label"
:class="[theme.default.label,{'uppercase text-xs': uppercaseLabels, 'text-sm': !uppercaseLabels}]"
>
{{ label }}
<span v-if="required" class="text-red-500 required-dot">*</span>
<slot>
{{ label }}
<span v-if="required" class="text-red-500 required-dot">*</span>
</slot>
</label>
</template>

View File

@@ -0,0 +1,47 @@
<template>
<div :class="wrapperClass" :style="inputStyle">
<slot name="label">
<input-label v-if="label"
:label="label"
:theme="theme"
:required="true"
:native-for="id?id:name"
:uppercase-labels="uppercaseLabels"
/>
</slot>
<slot v-if="help && helpPosition==='above_input'" name="help">
<input-help :help="help" :theme="theme" />
</slot>
<slot />
<slot v-if="help && helpPosition==='below_input'" name="help">
<input-help :help="help" :theme="theme" />
</slot>
<slot name="error">
<has-error v-if="hasValidation" :form="form" :field="name" />
</slot>
</div>
</template>
<script>
import InputLabel from './InputLabel.vue'
import InputHelp from './InputHelp.vue'
export default {
name: 'InputWrapper',
components: { InputLabel, InputHelp },
props: {
id: { type: String, required: false },
name: { type: String, required: false },
theme: { type: Object, required: true },
wrapperClass: { type: String, required: false },
inputStyle: { type: Object, required: false },
help: { type: String, required: false },
label: { type: String, required: false },
helpPosition: { type: String, default: 'below_input' },
uppercaseLabels: { type: Boolean, default: true },
hasValidation: { type: Boolean, default: true },
form: { type: Object, required: false }
}
}
</script>

View File

@@ -1,45 +1,23 @@
<template>
<div @click="onClick">
<div class="inline-flex items-center h-6 w-12 p-1 bg-gray-300 border rounded-full cursor-pointer focus:outline-none transition-all transform ease-in-out duration-100" :class="{'bg-nt-blue': internalValue}">
<div class="inline-block h-4 w-4 rounded-full bg-white shadow transition-all transform ease-in-out duration-150 rounded-2xl scale-100" :class="{'translate-x-5.5': internalValue}" />
<div role="button" @click="onClick">
<div class="inline-flex items-center h-6 w-12 p-1 bg-gray-300 border rounded-full cursor-pointer focus:outline-none transition-all transform ease-in-out duration-100" :class="{'bg-nt-blue': modelValue}">
<div class="inline-block h-4 w-4 rounded-full bg-white shadow transition-all transform ease-in-out duration-150 rounded-2xl scale-100" :class="{'translate-x-5.5': modelValue}" />
</div>
</div>
</template>
<script>
export default {
name: 'VSwitch',
components: { },
<script setup>
import { defineProps, defineEmits } from 'vue'
props: {
value: { type: Boolean, default: false },
disabled: { type: Boolean, default: false }
},
const { modelValue, disabled } = defineProps({
modelValue: { type: Boolean, default: false },
disabled: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue'])
data () {
return {
internalValue: this.value
}
},
computed: {},
watch: {
value (val) {
this.internalValue = val
}
},
mounted () {
this.internalValue = this.value
},
methods: {
onClick () {
if(this.disabled) return
this.$emit('input', !this.internalValue)
this.internalValue = !this.internalValue
}
}
const onClick = () => {
if (disabled) return
console.log('ok emiting', !modelValue)
emit('update:modelValue', !modelValue)
}
</script>
</script>

View File

@@ -1,4 +1,4 @@
import Vue from 'vue'
import { defineAsyncComponent } from 'vue'
import HasError from './validation/HasError.vue'
import AlertError from './validation/AlertError.vue'
@@ -16,29 +16,38 @@ import RatingInput from './RatingInput.vue'
import FlatSelectInput from './FlatSelectInput.vue'
import ToggleSwitchInput from './ToggleSwitchInput.vue'
// Components that are registered globaly.
[
HasError,
AlertError,
AlertSuccess,
VCheckbox,
VSelect,
CheckboxInput,
ColorInput,
TextInput,
SelectInput,
TextAreaInput,
FileInput,
ImageInput,
RatingInput,
FlatSelectInput,
ToggleSwitchInput
].forEach(Component => {
Vue.component(Component.name, Component)
})
export function registerComponents (app) {
[
HasError,
AlertError,
AlertSuccess,
VCheckbox,
VSelect,
CheckboxInput,
ColorInput,
TextInput,
SelectInput,
TextAreaInput,
FileInput,
ImageInput,
RatingInput,
FlatSelectInput,
ToggleSwitchInput
].forEach(Component => {
app.component(Component.name, Component)
})
// Lazy load some heavy component
Vue.component('SignatureInput', () => import('./SignatureInput.vue'))
Vue.component('RichTextAreaInput', () => import('./RichTextAreaInput.vue'))
Vue.component('DateInput', () => import('./DateInput.vue'))
Vue.component('PhoneInput', () => import('./PhoneInput.vue'))
// Register async components
app.component('SignatureInput', defineAsyncComponent(() =>
import('./SignatureInput.vue')
))
app.component('RichTextAreaInput', defineAsyncComponent(() =>
import('./RichTextAreaInput.vue')
))
app.component('PhoneInput', defineAsyncComponent(() =>
import('./PhoneInput.vue')
))
app.component('DateInput', defineAsyncComponent(() =>
import('./DateInput.vue')
))
}

View File

@@ -1,4 +1,4 @@
import { ref, computed, watch, defineEmits } from 'vue'
import { ref, computed, watch } from 'vue'
import { themes } from '~/config/form-themes.js'
export const inputProps = {
@@ -18,7 +18,7 @@ export const inputProps = {
wrapperClass: { type: String, default: 'relative mb-3' }
}
export function useFormInput (props) {
export function useFormInput (props, context, formPrefixKey = null) {
const content = ref(props.modelValue)
const inputStyle = computed(() => {
@@ -38,13 +38,13 @@ export function useFormInput (props) {
const compVal = computed({
get: () => {
if (props.form) {
return props.form[props.name]
return props.form[(formPrefixKey || '') + props.name]
}
return content.value
},
set: (val) => {
if (props.form) {
props.form[props.name] = val
props.form[(formPrefixKey || '') + props.name] = val
} else {
content.value = val
}
@@ -53,7 +53,7 @@ export function useFormInput (props) {
props.form.errors.clear(props.name)
}
defineEmits('update:modelValue', compVal.value)
context.emit('update:modelValue', compVal.value)
}
})