Skip to content
Snippets Groups Projects
Commit dc79086c authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt :koala:
Browse files

feat: add save visualization for FormGroup component

parent 120a4c95
No related branches found
No related tags found
No related merge requests found
<template>
<div class="form-group tw-block" :class="formGroupClass">
<label
v-if="label"
:for="id"
class="tw-text-gray-500 tw-font-medium tw-pt-1 tw-flex tw-gap-2 tw-items-center"
>
<span>{{ label }}</span>
<button v-if="withEditButton" type="button" class="btn btn-sm tw-p-0" @click="emit('edit')">
<icon-system-uicons-pen />
</button>
</label>
<div class="tw-flex tw-items-baseline tw-gap-2">
<label
v-if="label"
:for="id"
class="tw-text-gray-500 tw-font-medium tw-pt-1 tw-flex tw-gap-2 tw-items-center tw-grow"
>
<span>{{ label }}</span>
<button v-if="withEditButton" type="button" class="btn btn-sm tw-p-0" @click="emit('edit')">
<icon-system-uicons-pen />
</button>
</label>
<Transition
enter-active-class="tw-transition tw-duration-200 tw-ease-out"
enter-from-class="tw-opacity-0"
enter-to-class="tw-opacity-100"
leave-active-class="tw-transition tw-duration-150 tw-ease-in"
leave-from-class="tw-opacity-100"
leave-to-class="tw-opacity-0"
>
<SaveIndicator v-if="isSavingDebounced" />
</Transition>
</div>
<div class="tw-flex tw-flex-col">
<slot v-bind="controlAttributes" />
<div
......@@ -35,9 +48,10 @@
</template>
<script lang="ts" setup>
import { computed, inject } from 'vue'
import { useId } from '@/util'
import { computed, inject, ref, watchEffect } from 'vue'
import { computedDebounced, useId } from '@/util'
import { useI18n } from '@/i18n'
import SaveIndicator from '@/components/generic/SaveIndicator.vue'
defineOptions({ compatConfig: { MODE: 3 } })
......@@ -51,6 +65,7 @@ const props = defineProps<{
customControl?: boolean
errors?: undefined | (Error | undefined | null)[]
withEditButton?: boolean
isSaving?: boolean
}>()
const emit = defineEmits<{
edit: []
......@@ -70,4 +85,18 @@ const controlAttributes = computed(() => ({
'aria-describedby': hasErrors.value ? errorsId.value : '',
id: id.value,
}))
let isSavingStart = new Date()
const isSavingDebounced = computedDebounced(
() => props.isSaving,
(isSaving) => {
if (isSaving) return 0
const now = new Date()
const timePassed = (now.getTime() - isSavingStart.getTime()) / 1000
return timePassed < 1 ? 1 - timePassed : 0
},
)
watchEffect(() => {
if (props.isSaving) isSavingStart = new Date()
})
</script>
<template>
<span
class="tw-bg-green-100 tw-text-xs tw-text-green-800 tw-px-[1em] tw-py-[0.25em] tw-leading-normal tw-rounded-full tw-flex tw-items-center tw-gap-[.5em] tw-shrink-[100] tw-min-w-[3em]"
>
<span
class="tw-animate-spin tw-h-[1em] tw-w-[1em] tw-border-2 tw-border-b-transparent tw-border-solid tw-border-green-800/30 tw-rounded-full tw-block tw-flex-none"
/>
<span class="tw-truncate tw-min-w-0 tw-max-w-full">{{ t('saveIndicator.label') }}</span>
</span>
</template>
<script lang="ts" setup>
import { useI18n } from '@/i18n'
defineOptions({
compatConfig: { MODE: 3 },
})
const { t } = useI18n()
</script>
......@@ -390,6 +390,10 @@ export default {
},
},
saveIndicator: {
label: 'Wird gespeichert',
},
showCreator: {
title: 'Neue Sendereihe erstellen',
missingShowTypes:
......
......@@ -382,6 +382,10 @@ export default {
},
},
saveIndicator: {
label: 'Saving',
},
showCreator: {
title: 'Create new show',
missingShowTypes:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment