Browse Source

feat(nc-gui): flip transition and show current step

pull/3669/head
braks 2 years ago committed by Raju Udava
parent
commit
965785dead
  1. 4
      packages/nc-gui/pages/[projectType]/form/[viewId]/index.vue
  2. 210
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

4
packages/nc-gui/pages/[projectType]/form/[viewId]/index.vue

@ -5,10 +5,10 @@ const { passwordDlg, password, loadSharedView } = useSharedFormStoreOrThrow()
</script>
<template>
<div class="nc-form-view md:bg-primary bg-opacity-5 h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signin">
<div class="nc-form-view relative md:bg-primary bg-opacity-5 h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signin">
<NuxtPage />
<div class="self-end prose-xs text-gray-400 mx-auto my-4 relative flex items-center gap-2">Powered by NocoDB</div>
<div class="self-end text-xs text-gray-400 mx-auto my-4">Powered by NocoDB</div>
<a-modal
v-model:visible="passwordDlg"

210
packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

@ -1,8 +1,8 @@
<script lang="ts" setup>
import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import { computed, ref, useEventListener, useSharedFormStoreOrThrow, useStepper } from '#imports'
import { computed, onKeyStroke, ref, useEventListener, useSharedFormStoreOrThrow, useStepper, watch } from '#imports'
const { v$, formState, formColumns } = useSharedFormStoreOrThrow()
const { v$, formState, formColumns, submitForm, submitted, secondsRemain } = useSharedFormStoreOrThrow()
function isRequired(_columnObj: Record<string, any>, required = false) {
let columnObj = _columnObj
@ -26,7 +26,9 @@ const steps = computed(() => {
}, [] as string[])
})
const { index, goToPrevious, goToNext, isFirst, isLast } = useStepper(steps)
const { index, goToPrevious, goToNext, isFirst, isLast, steps: _steps } = useStepper(steps)
const isTransitioning = ref(false)
const transitionName = ref<'left' | 'right'>('left')
@ -34,18 +36,34 @@ const field = computed(() => formColumns.value?.[index.value])
const el = ref<HTMLDivElement>()
const goNext = () => {
const transition = (direction: 'left' | 'right') => {
isTransitioning.value = true
transitionName.value = direction
setTimeout(() => {
transitionName.value = transitionName.value === 'left' ? 'right' : 'left'
}, 500)
setTimeout(() => {
isTransitioning.value = false
}, 1000)
}
const goNext = async () => {
if (isLast.value) return
transitionName.value = 'left'
const isValid = await v$.value.localState[field.value!.title!].$validate()
if (!isValid) return
transition('left')
goToNext()
}
const goPrevious = () => {
const goPrevious = async () => {
if (isFirst.value) return
transitionName.value = 'right'
transition('right')
goToPrevious()
}
@ -56,9 +74,11 @@ useEventListener('wheel', (event) => {
return
}
if (event.deltaX < -10) {
if (isTransitioning.value) return
if (event.deltaX < -15) {
goPrevious()
} else if (event.deltaX > 10) {
} else if (event.deltaX > 15) {
goNext()
}
})
@ -68,93 +88,125 @@ onKeyStroke(['ArrowRight', 'ArrowUp', 'Enter', 'Space'], goNext)
</script>
<template>
<Transition :name="`slide-${transitionName}`" mode="out-in">
<div
ref="el"
:key="field.title"
class="bg-white relative flex flex-col justify-center gap-4 w-full lg:max-w-1/2 max-w-500px m-auto px-8 pt-8 pb-1 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<div v-if="field" class="flex flex-col gap-2">
<div class="flex nc-form-column-label">
<SmartsheetHeaderVirtualCell
v-if="isVirtualCol(field)"
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
<SmartsheetHeaderCell
v-else
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
</div>
<div v-if="isVirtualCol(field)">
<SmartsheetVirtualCell
class="mt-0 nc-input"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`"
:column="field"
/>
<div v-if="field.description" class="text-gray-500 text-[10px] mb-2 ml-1">{{ field.description }}</div>
<template v-if="v$.virtual.$dirty && v$.virtual?.[field.title]">
<div v-for="error of v$.virtual[field.title].$errors" :key="error" class="text-xs text-red-500">
{{ error.$message }}
<div class="w-full h-full flex flex-col justify-center items-center">
<Transition :name="`slide-${transitionName}`" mode="out-in">
<div
ref="el"
:key="field.title"
class="bg-white flex flex-col justify-center gap-4 w-full lg:max-w-1/2 max-w-500px m-auto px-8 pt-8 pb-4 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<div v-if="field" class="flex flex-col gap-2">
<div class="flex nc-form-column-label">
<SmartsheetHeaderVirtualCell
v-if="isVirtualCol(field)"
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
<SmartsheetHeaderCell
v-else
:column="{ ...field, title: field.label || field.title }"
:required="isRequired(field, field.required)"
:hide-menu="true"
/>
</div>
<div v-if="isVirtualCol(field)">
<SmartsheetVirtualCell
class="mt-0 nc-input"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`"
:column="field"
/>
<div v-if="field.description" class="text-gray-500 text-[10px] mb-2 ml-1">{{ field.description }}</div>
<template v-if="v$.virtual.$dirty && v$.virtual?.[field.title]">
<div v-for="error of v$.virtual[field.title].$errors" :key="error" class="text-xs text-red-500">
{{ error.$message }}
</div>
</template>
</div>
<div v-else>
<SmartsheetCell
v-model="formState[field.title]"
class="nc-input"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`"
:column="field"
:edit-enabled="true"
/>
<div class="flex flex-col gap-2 text-gray-500 text-xs my-2 px-1">
<div v-for="error of v$.localState[field.title].$errors" :key="error" class="text-red-500">
{{ error.$message }}
</div>
{{ field.description }}
</div>
</template>
</div>
</div>
<div v-else>
<SmartsheetCell
v-model="formState[field.title]"
class="nc-input"
:class="`nc-form-input-${field.title.replaceAll(' ', '')}`"
:column="field"
:edit-enabled="true"
/>
<div v-if="field.description" class="text-gray-500 text-[10px] mb-2 ml-1">{{ field.description }}</div>
<template v-if="v$.localState.$dirty && v$.localState?.[field.title]">
<div v-for="error of v$.localState[field.title].$errors" :key="error" class="text-xs text-red-500">
{{ error.$message }}
<div v-if="!isFirst || !isLast" class="flex w-full text-lg mt-2">
<a-tooltip v-if="!isFirst" title="Go to previous" :mouse-enter-delay="1" :mouse-leave-delay="0">
<button
class="group color-transition transform hover:scale-110 absolute left-5 top-1/2 md:static"
@click="goPrevious"
>
<MdiChevronLeft class="group-hover:text-accent text-2xl md:text-md" />
</button>
</a-tooltip>
<div class="flex-1">
<div v-if="isLast && !submitted && !v$.$invalid" class="text-center my-4">
<button type="submit" class="submit !px-4 !py-2 prose-sm" @click="submitForm">
{{ $t('general.submit') }}
</button>
</div>
</template>
</div>
<a-tooltip
v-if="!isLast"
placement="left"
:title="v$.localState[field.title]?.$error ? v$.localState[field.title].$errors[0].$message : 'Go to next'"
:mouse-enter-delay="v$.localState[field.title]?.$error ? 0 : 1"
:mouse-leave-delay="0"
>
<button class="group color-transition transform absolute right-5 top-1/2 md:static hover:scale-110" @click="goNext">
<TransitionGroup name="layout">
<MdiCloseCircleOutline v-if="v$.localState[field.title]?.$error" class="text-red-500 text-2xl md:text-md" />
<MdiChevronRight v-else class="group-hover:text-accent text-2xl md:text-md" />
</TransitionGroup>
</button>
</a-tooltip>
</div>
</div>
</Transition>
<div v-if="!isFirst || !isLast" class="flex w-full text-lg">
<a-tooltip v-if="!isFirst" title="Go to previous" :mouse-enter-delay="1" :mouse-leave-delay="0">
<button class="group color-transition transform hover:scale-110" @click="goPrevious">
<MdiChevronLeft class="group-hover:text-accent" />
</button>
</a-tooltip>
<div class="flex-1" />
<a-tooltip v-if="!isLast" title="Go to next" :mouse-enter-delay="1" :mouse-leave-delay="0">
<button class="group color-transition transform hover:scale-110" @click="goNext">
<MdiChevronRight class="group-hover:text-accent" />
</button>
</a-tooltip>
</div>
</div>
</Transition>
<div class="absolute bottom-12 right-12">{{ index }} / {{ formColumns?.length }}</div>
</div>
</template>
<style scoped>
<style lang="scss" scoped>
:global(html, body) {
@apply overscroll-x-none;
}
:deep(.nc-form-column-label) {
> * {
@apply !prose-lg;
}
.nc-icon {
@apply mr-2;
}
}
:deep(.nc-cell-attachment) {
@apply p-0;
.nc-attachment-cell {
@apply px-4 h-1/3 min-h-[100px];
@apply px-4 min-h-[75px] w-full h-full;
.nc-attachment {
@apply md:(w-[50px] h-[50px]) lg:(w-[75px] h-[75px]) min-h-[50px] min-w-[50px];

Loading…
Cancel
Save