Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve saving indicator #3609

Closed
37 changes: 26 additions & 11 deletions frontend/src/components/activity/content/LAThematicArea.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
<template>
<card-content-node v-bind="$props">
<v-list three-line class="mx-n4">
<v-list-item-group>
<v-list three-line class="mx-n4 ec-lathematicarea">
<v-list-item-group multiple :value="model">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is v-list-item-group really the correct component to use here? When navigating via keyboard, I can now focus either the checkbox or the whole list item. If I try to "activate" the list item via spacebar, it plays an animation but has no effect on the checkbox. Could you either make sure that only the checkboxes are focusable, or at least make the list item toggle the checkbox when "clicked" via keyboard?

<v-list-item
v-for="(option, key) in contentNode.data.options"
:key="key"
tag="label"
:value="key"
:disabled="layoutMode || disabled"
>
<v-list-item-action>
<v-list-item-action class="ml-0">
<api-checkbox
:id="contentNode.id + key"
:fieldname="`data.options.${key}.checked`"
:uri="contentNode._meta.self"
/>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>{{
$tc(`contentNode.laThematicArea.entity.option.${key}.name`)
}}</v-list-item-title>
<v-list-item-subtitle>{{
$tc(`contentNode.laThematicArea.entity.option.${key}.description`)
}}</v-list-item-subtitle>
<v-list-item-content tag="label" :for="contentNode.id + key">
<v-list-item-title
>{{ $tc(`contentNode.laThematicArea.entity.option.${key}.name`) }}
</v-list-item-title>
<v-list-item-subtitle
>{{ $tc(`contentNode.laThematicArea.entity.option.${key}.description`) }}
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
Expand All @@ -37,10 +38,24 @@ export default {
name: 'LAThematicArea',
components: { CardContentNode, ApiCheckbox },
mixins: [contentNodeMixin],
computed: {
model() {
return Object.entries(this.contentNode.data.options)
.filter(([, option]) => option.checked)
.map(([key]) => key)
},
},
methods: {
async refreshContent() {
await this.api.reload(this.contentNode)
},
},
}
</script>

<style scoped>
.ec-lathematicarea .theme--light.v-list-item--active:hover::before,
.ec-lathematicarea .theme--light.v-list-item--active::before {
opacity: 0;
}
</style>
11 changes: 1 addition & 10 deletions frontend/src/components/buttons/IconButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ export default {
<style scoped>
/*noinspection CssUnusedSymbol*/
.v-icon.animate {
animation: spin 0.5s infinite;
}

@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
animation: spin-only 0.5s infinite;
}
</style>
69 changes: 65 additions & 4 deletions frontend/src/components/form/api/ApiCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ Displays a field as a e-checkbox + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<ApiWrapperCheckboxAppend
v-if="
wrapper.hasServerError ||
(wrapper.autoSave && wrapper.dirty) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be !wrapper.autoSave.
The second part && wrapper.dirty depends on my other comment, on whether we want to display a cancel button or not.

Copy link
Contributor

@BacLuc BacLuc Jul 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jest would like that you don't use multi line expressions in the vue html.
please extract this into a computed property

We don't use jest anymore since #3268

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can also leave it, found the problem.
The transformation jest uses does not allow optional chaining (?.), this is the problem in PrintConfigurator.vue and Invitation.vue.

wrapper.hasLoadingError
"
:wrapper="wrapper"
/>
</template>
</e-checkbox>
</api-wrapper>
Expand All @@ -25,11 +32,11 @@ Displays a field as a e-checkbox + write access via API wrapper
<script>
import { apiPropsMixin } from '@/mixins/apiPropsMixin.js'
import ApiWrapper from './ApiWrapper.vue'
import ApiWrapperAppend from './ApiWrapperAppend.vue'
import ApiWrapperCheckboxAppend from '@/components/form/api/ApiWrapperCheckboxAppend.vue'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could keep a relative import here


export default {
name: 'ApiCheckbox',
components: { ApiWrapper, ApiWrapperAppend },
components: { ApiWrapperCheckboxAppend, ApiWrapper },
mixins: [apiPropsMixin],
props: {
// disable delay per default
Expand All @@ -41,4 +48,58 @@ export default {
}
</script>

<style scoped></style>
<style scoped lang="scss">
.ec-api-wrapper--saving ::v-deep .v-input--selection-controls__input .v-icon {
transition: color 0.2s ease;
color: rgba(0, 0, 0, 0.5) !important;
}

.ec-api-wrapper ::v-deep .v-input--checkbox .v-input--selection-controls__input::before {
border: 2px solid transparent;
border-radius: 50%;
position: absolute;
height: 24px;
width: 24px;
content: '\F0131';
color: transparent;
transition: transform 0.2s ease-out, border-color 0.2s ease;
transform: scale(1.9);
pointer-events: none;
}

.ec-api-wrapper--saving
::v-deep
.v-input--checkbox
.v-input--selection-controls__input::before {
border-color: map-get($blue, 'base');
-webkit-mask: conic-gradient(from 0deg at 50% 50%, #0000 0%, #000);
transform: scale(1.2);
animation: spin 1s linear infinite both;
transition: transform 0.2s ease-out;
}

@keyframes spin {
from {
transform: scale(1.5) rotate(0deg);
}
to {
transform: scale(1.5) rotate(360deg);
}
}

.ec-api-wrapper--success
::v-deep
.v-input--checkbox
.v-input--selection-controls__input::before {
border-color: map-get($green, 'lighten-2');
transform: scale(1.5);
transition: none;
}

.ec-api-wrapper--server-error
::v-deep
.v-input--checkbox
.v-input--selection-controls__input::before {
transform: scale(1.5);
}
</style>
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiColorPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Displays a field as a color picker + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-color-picker>
</api-wrapper>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiDatePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Displays a field as a date picker + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-date-picker>
</api-wrapper>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiRichtext.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Displays a field as a e-textarea + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-richtext>
</api-wrapper>
Expand Down
14 changes: 9 additions & 5 deletions frontend/src/components/form/api/ApiSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Displays a field as a e-select + write access via API wrapper
:readonly="wrapper.readonly"
:disabled="disabled"
:error-messages="wrapper.errorMessages"
:loading="wrapper.isSaving || wrapper.isLoading ? 'secondary' : false"
:loading="wrapper.isLoading ? 'secondary' : false"
:outlined="outlined"
:filled="filled"
:dense="dense"
Expand All @@ -24,7 +24,7 @@ Displays a field as a e-select + write access via API wrapper
>
<template #append>
<v-icon>mdi-menu-down</v-icon>
<api-wrapper-append :wrapper="wrapper" />
<ApiWrapperSelectAppend :wrapper="wrapper" :resettable="resettable" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I click "Retry" in the Select, the drowdown opens. Do we need to have some @click.stop somewhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed this in #3144 (the very last point)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This happened with the checkbox as well because of the label. I'll have a look.

</template>
</e-select>
</api-wrapper>
Expand All @@ -33,11 +33,11 @@ Displays a field as a e-select + write access via API wrapper
<script>
import { apiPropsMixin } from '@/mixins/apiPropsMixin.js'
import ApiWrapper from './ApiWrapper.vue'
import ApiWrapperAppend from './ApiWrapperAppend.vue'
import ApiWrapperSelectAppend from '@/components/form/api/ApiWrapperSelectAppend.vue'

export default {
name: 'ApiSelect',
components: { ApiWrapper, ApiWrapperAppend },
components: { ApiWrapperSelectAppend, ApiWrapper },
mixins: [apiPropsMixin],
props: {
// disable delay per default
Expand All @@ -62,4 +62,8 @@ export default {
}
</script>

<style scoped></style>
<style scoped>
::v-deep .v-input__append-inner {
position: relative;
}
</style>
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiSwitch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Displays a field as a e-checkbox + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-switch>
</api-wrapper>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiTextField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Displays a field as a e-text-field + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-text-field>
</api-wrapper>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiTextarea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Displays a field as a e-textarea + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-textarea>
</api-wrapper>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/form/api/ApiTimePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Displays a field as a time picker + write access via API wrapper
@input="wrapper.on.input"
>
<template #append>
<api-wrapper-append :wrapper="wrapper" />
<api-wrapper-append :wrapper="wrapper" :resettable="resettable" />
</template>
</e-time-picker>
</api-wrapper>
Expand Down
15 changes: 10 additions & 5 deletions frontend/src/components/form/api/ApiWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ Wrapper component for form components to save data back to API
<template>
<ValidationObserver ref="validationObserver" v-slot="validationObserver" slim>
<v-form
:class="[{ 'api-wrapper--inline': !autoSave && !readonly && !separateButtons }]"
class="e-form-container"
:class="{
'ec-api-wrapper--inline': !autoSave && !readonly && !separateButtons,
'ec-api-wrapper--success': status === 'success',
'ec-api-wrapper--saving': status === 'saving',
'ec-api-wrapper--server-error': hasServerError,
}"
class="e-form-container ec-api-wrapper"
@submit.prevent="onEnter"
>
<slot
Expand Down Expand Up @@ -247,18 +252,18 @@ export default {
</script>

<style lang="scss" scoped>
.api-wrapper--inline .v-btn--last-instance {
.ec-api-wrapper--inline .v-btn--last-instance {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.api-wrapper--inline .v-btn {
.ec-api-wrapper--inline .v-btn {
border-top: 1px solid rgba(0, 0, 0, 0.38);
border-bottom: 1px solid rgba(0, 0, 0, 0.38);
}
</style>

<style lang="scss">
.api-wrapper--inline .v-text-field {
.ec-api-wrapper--inline .v-text-field {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
Expand Down
Loading