Vue 3 better animation (#257)

* vue-3-better-animation

* Working on migration to vueuse/motion

* Form sidebar animations

* Clean code

* Added animations for modal

* Finished implementing better animations

---------

Co-authored-by: Forms Dev <chirag+new@notionforms.io>
This commit is contained in:
Julien Nahum
2023-12-08 19:21:04 +01:00
committed by GitHub
parent 24276f0b95
commit f970557b76
23 changed files with 1756 additions and 870 deletions

View File

@@ -49,27 +49,25 @@
</div>
</div>
<div class="w-full flex grow overflow-y-scroll relative">
<div class="relative w-full shrink-0 overflow-y-scroll border-r md:w-1/2 md:max-w-sm lg:w-2/5">
<div class="w-full flex grow overflow-y-scroll relative bg-gray-50">
<div class="relative w-full bg-white shrink-0 overflow-y-scroll border-r md:w-1/2 md:max-w-sm lg:w-2/5">
<div class="border-b bg-blue-50 p-5 text-nt-blue-dark md:hidden">
Please create this form on a device with a larger screen. That will allow you to preview your form changes.
</div>
<form-information/>
<form-structure/>
<form-customization/>
<form-notifications/>
<form-about-submission/>
<form-information />
<form-structure />
<form-customization />
<form-notifications />
<form-about-submission />
<form-access />
<form-security-privacy/>
<form-security-privacy />
<form-custom-seo />
<form-custom-code/>
<form-custom-code />
</div>
<form-editor-preview />
<form-field-edit-sidebar />
<add-form-block-sidebar />
<form-editor-sidebar />
<!-- Form Error Modal -->
<form-error-modal
@@ -86,12 +84,11 @@
<script>
import { computed } from 'vue'
import { useAuthStore } from '../../../../stores/auth';
import { useFormsStore } from '../../../../stores/forms';
import { useWorkingFormStore } from '../../../../stores/working_form';
import { useWorkspacesStore } from '../../../../stores/workspaces';
import AddFormBlockSidebar from './form-components/AddFormBlockSidebar.vue'
import FormFieldEditSidebar from '../fields/FormFieldEditSidebar.vue'
import { useAuthStore } from '../../../../stores/auth'
import { useFormsStore } from '../../../../stores/forms'
import { useWorkingFormStore } from '../../../../stores/working_form'
import { useWorkspacesStore } from '../../../../stores/workspaces'
import FormEditorSidebar from './form-components/FormEditorSidebar.vue'
import FormErrorModal from './form-components/FormErrorModal.vue'
import FormInformation from './form-components/FormInformation.vue'
import FormStructure from './form-components/FormStructure.vue'
@@ -109,8 +106,7 @@ import fieldsLogic from '../../../../mixins/forms/fieldsLogic.js'
export default {
name: 'FormEditor',
components: {
AddFormBlockSidebar,
FormFieldEditSidebar,
FormEditorSidebar,
FormEditorPreview,
FormNotifications,
FormAboutSubmission,
@@ -156,7 +152,7 @@ export default {
formsStore,
workingFormStore,
workspacesStore,
user : computed(() => authStore.user)
user: computed(() => authStore.user)
}
},

View File

@@ -1,7 +1,5 @@
<template>
<div v-if="showSidebar"
class="absolute shadow-lg shadow-blue-800/30 top-0 h-[calc(100vh-45px)] right-0 lg:shadow-none lg:relative bg-white w-full md:w-1/2 lg:w-2/5 border-l overflow-y-scroll md:max-w-[20rem] flex-shrink-0"
>
<div>
<div class="p-4 border-b sticky top-0 z-10 bg-white">
<div class="flex">
<button class="text-gray-500 hover:text-gray-900 cursor-pointer" @click.prevent="closeSidebar">
@@ -69,7 +67,7 @@ import { computed } from 'vue'
import { useWorkingFormStore } from '../../../../../stores/working_form'
export default {
name: 'AddFormBlockSidebar',
name: 'AddFormBlock',
components: {},
props: {},
@@ -77,8 +75,7 @@ export default {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex),
showAddFieldSidebar : computed(() => workingFormStore.showAddFieldSidebar)
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex)
}
},
@@ -183,9 +180,6 @@ export default {
this.workingFormStore.set(value)
}
},
showSidebar () {
return (this.form && this.showAddFieldSidebar) ?? false
},
defaultBlockNames () {
return {

View File

@@ -0,0 +1,49 @@
<template>
<editor-right-sidebar :show="form && (showEditFieldSidebar || showAddFieldSidebar)">
<transition mode="out-in">
<form-field-edit v-if="showEditFieldSidebar" :key="editFieldIndex" v-motion-fade="'fade'" />
<add-form-block v-else-if="showAddFieldSidebar" v-motion-fade="'fade'" />
</transition>
</editor-right-sidebar>
</template>
<script>
import { computed } from 'vue'
import { useWorkingFormStore } from '../../../../../stores/working_form'
import EditorRightSidebar from '../../../editors/EditorRightSidebar.vue'
import FormFieldEdit from '../../fields/FormFieldEdit.vue'
import AddFormBlock from './AddFormBlock.vue'
export default {
name: 'FormEditorSidebar',
components: { EditorRightSidebar, AddFormBlock, FormFieldEdit },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
editFieldIndex: computed(() => workingFormStore.selectedFieldIndex),
showEditFieldSidebar: computed(() => workingFormStore.showEditFieldSidebar),
showAddFieldSidebar: computed(() => workingFormStore.showAddFieldSidebar)
}
},
data () {
return {}
},
computed: {
form: {
get () {
return this.workingFormStore.content
},
/* We add a setter */
set (value) {
this.workingFormStore.set(value)
}
}
},
watch: {},
mounted () {
},
methods: {}
}
</script>

View File

@@ -6,13 +6,13 @@
</div>
<SelectInput v-model="content.operator" class="w-full" :options="operators"
:name="'operator_'+property.id" placeholder="Comparison operator"
@input="operatorChanged()"
@update:modelValue="operatorChanged()"
/>
<template v-if="hasInput">
<component v-bind="inputComponentData" :is="inputComponentData.component" v-model="content.value" class="w-full"
:name="'value_'+property.id" placeholder="Filter Value"
@input="$emit('input',castContent(content))"
@update:modelValue="emitInput()"
/>
</template>
</div>
@@ -131,7 +131,7 @@ export default {
} else if (typeof this.content.value === 'boolean' || typeof this.content.value === 'object') {
this.content.value = null
}
this.$emit('input', this.castContent(this.content))
this.emitInput()
},
needsInput () {
const operator = this.selectedOperator()
@@ -165,6 +165,9 @@ export default {
return key.split('_').map(function (item) {
return item.charAt(0).toUpperCase() + item.substring(1)
}).join(' ')
},
emitInput () {
this.$emit('update:modelValue', this.castContent(this.content))
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<query-builder v-model="query" :rules="rules" :config="config" @input="onChange">
<query-builder v-model="query" :rules="rules" :config="config" @update:modelValue="onChange">
<template #groupOperator="props">
<div class="query-builder-group-slot__group-selection flex items-center px-5 border-b py-1 mb-1 flex">
<p class="mr-2 font-semibold">
@@ -7,13 +7,13 @@
</p>
<select-input
wrapper-class="relative"
:value="props.currentOperator"
:model-value="props.currentOperator"
:options="props.operators"
emit-key="identifier"
option-key="identifier"
name="operator-input"
margin-bottom=""
@input="props.updateCurrentOperator($event)"
@update:modelValue="props.updateCurrentOperator($event)"
/>
</div>
</template>
@@ -23,17 +23,17 @@
<template #rule="ruleCtrl">
<component
:is="ruleCtrl.ruleComponent"
:value="ruleCtrl.ruleData"
@input="ruleCtrl.updateRuleData"
:model-value="ruleCtrl.ruleData"
@update:modelValue="ruleCtrl.updateRuleData"
/>
</template>
</query-builder>
</template>
<script>
import { defineComponent } from 'vue'
import QueryBuilder from 'query-builder-vue-3'
import ColumnCondition from './ColumnCondition.vue'
import Vue from 'vue'
import GroupControlSlot from './GroupControlSlot.vue'
export default {
@@ -66,7 +66,8 @@ export default {
identifier: property.id,
name: property.name,
component: (function () {
return Vue.extend(ColumnCondition).extend({
return defineComponent({
extends: ColumnCondition,
computed: {
property () {
return property
@@ -111,7 +112,7 @@ export default {
methods: {
onChange () {
this.$emit('input', this.query)
this.$emit('update:modelValue', this.query)
}
}
}

View File

@@ -67,7 +67,6 @@
</template>
<script>
import ProTag from '../../../../common/ProTag.vue'
import ConditionEditor from './ConditionEditor.vue'
import Modal from '../../../../Modal.vue'
import SelectInput from '../../../../forms/SelectInput.vue'
@@ -75,7 +74,7 @@ import clonedeep from 'clone-deep'
export default {
name: 'FormBlockLogicEditor',
components: { SelectInput, Modal, ProTag, ConditionEditor },
components: { SelectInput, Modal, ConditionEditor },
props: {
field: {
type: Object,

View File

@@ -1,7 +1,5 @@
<template>
<div v-if="showSidebar"
class="absolute shadow-lg shadow-blue-800/30 top-0 h-[calc(100vh-45px)] right-0 lg:shadow-none lg:relative bg-white w-full md:w-1/2 lg:w-2/5 border-l overflow-y-scroll md:max-w-[20rem] flex-shrink-0 overflow-x-hidden"
>
<div>
<div class="p-4 border-b sticky top-0 z-10 bg-white">
<button v-if="!field" class="text-gray-500 hover:text-gray-900 cursor-pointer" @click.prevent="closeSidebar">
<svg class="h-6 w-6" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -78,15 +76,14 @@ import FieldOptions from './components/FieldOptions.vue'
import BlockOptions from './components/BlockOptions.vue'
export default {
name: 'FormFieldEditSidebar',
name: 'FormFieldEdit',
components: { ChangeFieldType, FieldOptions, BlockOptions },
props: {},
setup () {
const workingFormStore = useWorkingFormStore()
return {
workingFormStore,
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex),
showEditFieldSidebar : computed(() => workingFormStore.showEditFieldSidebar)
selectedFieldIndex : computed(() => workingFormStore.selectedFieldIndex)
}
},
data () {
@@ -108,9 +105,6 @@ export default {
field () {
return (this.form && this.selectedFieldIndex !== null) ? this.form.properties[this.selectedFieldIndex] : null
},
showSidebar () {
return (this.form && this.selectedFieldIndex !== null) ? (this.form.properties[this.selectedFieldIndex] && this.showEditFieldSidebar) : false
},
isBlockField () {
return this.field && this.field.type.startsWith('nf')
},

View File

@@ -86,17 +86,16 @@
</div>
<!-- Logic Block -->
<form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />
<!-- <form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />-->
</div>
</template>
<script>
import CodeInput from '../../../../forms/CodeInput.vue'
const FormBlockLogicEditor = () => import('../../components/form-logic-components/FormBlockLogicEditor.vue')
export default {
name: 'BlockOptions',
components: { FormBlockLogicEditor, CodeInput },
components: { CodeInput },
props: {
field: {
type: Object,

View File

@@ -1,7 +1,7 @@
<template>
<dropdown dusk="nav-dropdown" v-if="changeTypeOptions.length > 0">
<dropdown v-if="changeTypeOptions.length > 0" dusk="nav-dropdown">
<template #trigger="{toggle}">
<v-button class="relative" :class="btnClasses" size="small" color="light-gray" @click="toggle">
<v-button class="relative" :class="btnClasses" size="small" color="light-gray" @click.stop="toggle">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="h-4 w-4 text-blue-600 inline mr-1 -mt-1">
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" />
</svg>
@@ -9,7 +9,7 @@
</v-button>
</template>
<a href="#" v-for="(op, index) in changeTypeOptions" :key="index"
<a v-for="(op, index) in changeTypeOptions" :key="index" href="#"
class="block px-4 py-2 text-md text-gray-700 dark:text-white hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:text-white dark:hover:bg-gray-600 flex items-center"
@click.prevent="changeType(op.value)"
>
@@ -23,7 +23,7 @@ import Dropdown from '../../../../common/Dropdown.vue'
export default {
name: 'ChangeFieldType',
components: {Dropdown},
components: { Dropdown },
props: {
field: {
type: Object,
@@ -34,25 +34,25 @@ export default {
required: true
}
},
data() {
data () {
return {}
},
computed: {
changeTypeOptions() {
var newTypes = []
changeTypeOptions () {
let newTypes = []
if (['text', 'email', 'phone', 'number'].includes(this.field.type)) {
newTypes = [
{'name': 'Text Input', 'value': 'text'},
{'name': 'Email Input', 'value': 'email'},
{'name': 'Phone Input', 'value': 'phone'},
{'name': 'Number Input', 'value': 'number'}
{ name: 'Text Input', value: 'text' },
{ name: 'Email Input', value: 'email' },
{ name: 'Phone Input', value: 'phone' },
{ name: 'Number Input', value: 'number' }
]
}
if (['select', 'multi_select'].includes(this.field.type)) {
newTypes = [
{'name': 'Select Input', 'value': 'select'},
{'name': 'Multi-Select Input', 'value': 'multi_select'}
{ name: 'Select Input', value: 'select' },
{ name: 'Multi-Select Input', value: 'multi_select' }
]
}
return newTypes.filter((item) => {
@@ -68,11 +68,11 @@ export default {
watch: {},
mounted() {
mounted () {
},
methods: {
changeType(newType) {
changeType (newType) {
if (newType) {
this.$emit('changeType', newType)
}

View File

@@ -293,7 +293,7 @@
<file-input v-else-if="field.type==='files'" name="prefill" class="mt-4"
:form="field"
label="Pre-filled file"
:multiple="field.multiple===true" :moveToFormAssets="true"
:multiple="field.multiple===true" :move-to-form-assets="true"
/>
<text-input v-else-if="!['files', 'signature'].includes(field.type)" name="prefill" class="mt-3"
:form="field"
@@ -382,7 +382,7 @@
</div>
<!-- Logic Block -->
<form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />
<!-- <form-block-logic-editor class="py-2 px-4 border-b" :form="form" :field="field" />-->
</div>
</template>
@@ -391,11 +391,9 @@ import timezones from '../../../../../../data/timezones.json'
import countryCodes from '../../../../../../data/country_codes.json'
import CountryFlag from 'vue-country-flag-next'
const FormBlockLogicEditor = () => import('../../components/form-logic-components/FormBlockLogicEditor.vue')
export default {
name: 'FieldOptions',
components: { FormBlockLogicEditor, CountryFlag },
components: { CountryFlag },
props: {
field: {
type: Object,
@@ -533,7 +531,7 @@ export default {
this.field.hidden = true
}
},
initRating() {
initRating () {
if (this.field.is_rating) {
this.$set(this.field, 'is_scale', false)
if (!this.field.rating_max_value) {