This repository has been archived on 2024-02-08. You can view files and clone it, but cannot push or open issues or pull requests.
frontend/src/views/tasks/TaskDetailView.vue

940 lines
25 KiB
Vue
Raw Normal View History

2019-11-24 13:16:24 +00:00
<template>
<div :class="{ 'is-loading': taskService.loading, 'visible': visible}" class="loader-container task-view-container">
2019-11-24 13:16:24 +00:00
<div class="task-view">
<heading v-model="task" :can-write="canWrite" ref="heading"/>
<h6 class="subtitle" v-if="parent && parent.namespace && parent.list">
{{ getNamespaceTitle(parent.namespace) }} >
<router-link :to="{ name: 'list.index', params: { listId: parent.list.id } }">
{{ getListTitle(parent.list) }}
</router-link>
</h6>
<checklist-summary :task="task"/>
2019-11-24 13:16:24 +00:00
<!-- Content and buttons -->
<div class="columns mt-2">
2019-11-24 13:16:24 +00:00
<!-- Content -->
<div :class="{'is-two-thirds': canWrite}" class="column">
2019-11-24 13:16:24 +00:00
<div class="columns details">
<div class="column assignees" v-if="activeFields.assignees">
<!-- Assignees -->
<div class="detail-title">
<icon icon="users"/>
{{ $t('task.attributes.assignees') }}
2019-11-24 13:16:24 +00:00
</div>
<edit-assignees
:disabled="!canWrite"
:list-id="task.listId"
:task-id="task.id"
ref="assignees"
v-model="task.assignees"
2019-11-24 13:16:24 +00:00
/>
</div>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.priority">
<!-- Priority -->
<div class="detail-title">
<icon icon="exclamation"/>
{{ $t('task.attributes.priority') }}
</div>
<priority-select
:disabled="!canWrite"
@change="saveTask"
ref="priority"
v-model="task.priority"/>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.dueDate">
<!-- Due Date -->
<div class="detail-title">
<icon icon="calendar"/>
{{ $t('task.attributes.dueDate') }}
</div>
<div class="date-input">
<datepicker
v-model="task.dueDate"
@close-on-change="() => saveTask()"
:choose-date-label="$t('task.detail.chooseDueDate')"
:disabled="taskService.loading || !canWrite"
ref="dueDate"
/>
<a
@click="() => {task.dueDate = null;saveTask()}"
v-if="task.dueDate && canWrite"
class="remove">
<span class="icon is-small">
<icon icon="times"></icon>
</span>
</a>
</div>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.percentDone">
<!-- Progress -->
<div class="detail-title">
<icon icon="percent"/>
{{ $t('task.attributes.percentDone') }}
</div>
<percent-done-select
:disabled="!canWrite"
@change="saveTask"
ref="percentDone"
v-model="task.percentDone"/>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.startDate">
<!-- Start Date -->
<div class="detail-title">
<icon icon="play"/>
{{ $t('task.attributes.startDate') }}
</div>
<div class="date-input">
<datepicker
v-model="task.startDate"
@close-on-change="() => saveTask()"
:choose-date-label="$t('task.detail.chooseStartDate')"
:disabled="taskService.loading || !canWrite"
ref="startDate"
/>
<a
@click="() => {task.startDate = null;saveTask()}"
v-if="task.startDate && canWrite"
class="remove"
>
<span class="icon is-small">
<icon icon="times"></icon>
</span>
</a>
</div>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.endDate">
<!-- End Date -->
<div class="detail-title">
<icon icon="stop"/>
{{ $t('task.attributes.endDate') }}
</div>
<div class="date-input">
<datepicker
v-model="task.endDate"
@close-on-change="() => saveTask()"
:choose-date-label="$t('task.detail.chooseEndDate')"
:disabled="taskService.loading || !canWrite"
ref="endDate"
/>
<a
@click="() => {task.endDate = null;saveTask()}"
v-if="task.endDate && canWrite"
class="remove">
<span class="icon is-small">
<icon icon="times"></icon>
</span>
</a>
</div>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.reminders">
<!-- Reminders -->
<div class="detail-title">
<icon :icon="['far', 'clock']"/>
{{ $t('task.attributes.reminders') }}
</div>
<reminders
:disabled="!canWrite"
@change="saveTask"
ref="reminders"
v-model="task.reminderDates"/>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.repeatAfter">
<!-- Repeat after -->
<div class="detail-title">
<icon icon="history"/>
{{ $t('task.attributes.repeat') }}
</div>
<repeat-after
:disabled="!canWrite"
@change="saveTask"
ref="repeatAfter"
v-model="task"/>
2019-11-24 13:16:24 +00:00
</div>
</transition>
<transition name="flash-background" appear>
<div class="column" v-if="activeFields.color">
<!-- Color -->
<div class="detail-title">
<icon icon="fill-drip"/>
{{ $t('task.attributes.color') }}
</div>
<color-picker
@change="saveTask"
menu-position="bottom"
ref="color"
v-model="taskColor"/>
</div>
</transition>
2019-11-24 13:16:24 +00:00
</div>
<!-- Labels -->
<div class="labels-list details" v-if="activeFields.labels">
<div class="detail-title">
<span class="icon is-grey">
<icon icon="tags"/>
</span>
{{ $t('task.attributes.labels') }}
2019-11-24 13:16:24 +00:00
</div>
<edit-labels :disabled="!canWrite" :task-id="taskId" ref="labels" v-model="task.labels"/>
2019-11-24 13:16:24 +00:00
</div>
<!-- Description -->
2021-01-16 20:55:43 +00:00
<div class="details content description">
<description
v-model="task"
:can-write="canWrite"
:attachment-upload="attachmentUpload"
/>
2019-11-24 13:16:24 +00:00
</div>
<!-- Attachments -->
<div class="content attachments" v-if="activeFields.attachments || hasAttachments">
2019-11-24 13:16:24 +00:00
<attachments
:edit-enabled="canWrite"
:task-id="taskId"
ref="attachments"
2019-11-24 13:16:24 +00:00
/>
</div>
<!-- Related Tasks -->
2021-01-16 20:55:43 +00:00
<div class="content details mb-0" v-if="activeFields.relatedTasks">
2019-11-24 13:16:24 +00:00
<h3>
<span class="icon is-grey">
<icon icon="sitemap"/>
2019-11-24 13:16:24 +00:00
</span>
{{ $t('task.attributes.relatedTasks') }}
2019-11-24 13:16:24 +00:00
</h3>
<related-tasks
:edit-enabled="canWrite"
:initial-related-tasks="task.relatedTasks"
:list-id="task.listId"
:show-no-relations-notice="true"
:task-id="taskId"
ref="relatedTasks"
2019-11-24 13:16:24 +00:00
/>
</div>
2020-04-18 12:39:56 +00:00
<!-- Move Task -->
2021-01-16 20:55:43 +00:00
<div class="content details" v-if="activeFields.moveList">
2020-04-18 12:39:56 +00:00
<h3>
<span class="icon is-grey">
<icon icon="list"/>
</span>
{{ $t('task.detail.move') }}
2020-04-18 12:39:56 +00:00
</h3>
<div class="field has-addons">
<div class="control is-expanded">
<list-search @update:modelValue="changeList" ref="moveList"/>
2020-04-18 12:39:56 +00:00
</div>
</div>
</div>
<!-- Comments -->
<comments :can-write="canWrite" :task-id="taskId"/>
2019-11-24 13:16:24 +00:00
</div>
<div class="column is-one-third action-buttons" v-if="canWrite || shouldShowClosePopup">
<BaseButton @click="$router.back()" class="is-fullwidth is-block has-text-centered mb-4 has-text-primary" v-if="shouldShowClosePopup">
<icon icon="arrow-left"/>
{{ $t('task.detail.closePopup') }}
</BaseButton>
<template v-if="canWrite">
<x-button
:class="{'is-success': !task.done}"
:shadow="task.done"
@click="toggleTaskDone()"
class="is-outlined has-no-border"
icon="check-double"
variant="secondary"
2022-03-02 20:59:51 +00:00
v-shortcut="'t'"
>
{{ task.done ? $t('task.detail.undone') : $t('task.detail.done') }}
</x-button>
<task-subscription
2022-01-30 22:18:13 +00:00
v-if="task.subscription"
entity="task"
:entity-id="task.id"
:subscription="task.subscription"
@change="sub => task.subscription = sub"
/>
<x-button
@click="setFieldActive('assignees')"
variant="secondary"
v-shortcut="'a'"
v-cy="'taskDetail.assign'"
>
<span class="icon is-small"><icon icon="users"/></span>
{{ $t('task.detail.actions.assign') }}
</x-button>
<x-button
@click="setFieldActive('labels')"
variant="secondary"
icon="tags"
v-shortcut="'l'"
>
{{ $t('task.detail.actions.label') }}
</x-button>
<x-button
@click="setFieldActive('priority')"
variant="secondary"
icon="exclamation"
>
{{ $t('task.detail.actions.priority') }}
</x-button>
<x-button
@click="setFieldActive('dueDate')"
variant="secondary"
icon="calendar"
v-shortcut="'d'"
>
{{ $t('task.detail.actions.dueDate') }}
</x-button>
<x-button
@click="setFieldActive('startDate')"
variant="secondary"
icon="play"
>
{{ $t('task.detail.actions.startDate') }}
</x-button>
<x-button
@click="setFieldActive('endDate')"
variant="secondary"
icon="stop"
>
{{ $t('task.detail.actions.endDate') }}
</x-button>
<x-button
@click="setFieldActive('reminders')"
variant="secondary"
:icon="['far', 'clock']"
>
{{ $t('task.detail.actions.reminders') }}
</x-button>
<x-button
@click="setFieldActive('repeatAfter')"
variant="secondary"
icon="history"
>
{{ $t('task.detail.actions.repeatAfter') }}
</x-button>
<x-button
@click="setFieldActive('percentDone')"
variant="secondary"
icon="percent"
>
{{ $t('task.detail.actions.percentDone') }}
</x-button>
<x-button
@click="setFieldActive('attachments')"
variant="secondary"
icon="paperclip"
v-shortcut="'f'"
>
{{ $t('task.detail.actions.attachments') }}
</x-button>
<x-button
@click="setFieldActive('relatedTasks')"
variant="secondary"
icon="sitemap"
v-shortcut="'r'"
>
{{ $t('task.detail.actions.relatedTasks') }}
</x-button>
<x-button
@click="setFieldActive('moveList')"
variant="secondary"
icon="list"
2022-03-02 20:59:51 +00:00
v-shortcut="'m'"
>
{{ $t('task.detail.actions.moveList') }}
</x-button>
<x-button
@click="setFieldActive('color')"
variant="secondary"
icon="fill-drip"
2022-03-02 20:59:51 +00:00
v-shortcut="'c'"
>
{{ $t('task.detail.actions.color') }}
</x-button>
<x-button
@click="toggleFavorite"
variant="secondary"
:icon="task.isFavorite ? 'star' : ['far', 'star']"
>
{{
task.isFavorite ? $t('task.detail.actions.unfavorite') : $t('task.detail.actions.favorite')
}}
</x-button>
<x-button
@click="showDeleteModal = true"
icon="trash-alt"
:shadow="false"
class="is-danger is-outlined has-no-border"
>
{{ $t('task.detail.actions.delete') }}
</x-button>
</template>
2020-10-25 11:25:08 +00:00
<!-- Created / Updated [by] -->
<created-updated :task="task"/>
2019-11-24 13:16:24 +00:00
</div>
</div>
<!-- Created / Updated [by] -->
<created-updated :task="task" v-if="!canWrite && !shouldShowClosePopup"/>
2019-11-24 13:16:24 +00:00
</div>
<transition name="modal">
<modal
@close="showDeleteModal = false"
@submit="deleteTask()"
v-if="showDeleteModal"
>
<template #header><span>{{ $t('task.detail.delete.header') }}</span></template>
<template #text>
<p>{{ $t('task.detail.delete.text1') }}<br/>
{{ $t('task.detail.delete.text2') }}</p>
</template>
</modal>
</transition>
2019-11-24 13:16:24 +00:00
</div>
</template>
2022-02-15 12:07:34 +00:00
<script lang="ts">
import TaskService from '../../services/task'
import TaskModel from '../../models/task'
2019-11-24 13:16:24 +00:00
import priorites from '../../models/constants/priorities.json'
import rights from '../../models/constants/rights.json'
2019-11-24 13:16:24 +00:00
import PrioritySelect from '../../components/tasks/partials/prioritySelect'
import PercentDoneSelect from '../../components/tasks/partials/percentDoneSelect'
import EditLabels from '../../components/tasks/partials/editLabels'
import EditAssignees from '../../components/tasks/partials/editAssignees'
import Attachments from '../../components/tasks/partials/attachments'
import RelatedTasks from '../../components/tasks/partials/relatedTasks'
import RepeatAfter from '../../components/tasks/partials/repeatAfter'
import Reminders from '../../components/tasks/partials/reminders'
import Comments from '../../components/tasks/partials/comments'
import ListSearch from '../../components/tasks/partials/listSearch'
import description from '@/components/tasks/partials/description.vue'
import ColorPicker from '../../components/input/colorPicker'
import heading from '@/components/tasks/partials/heading.vue'
import Datepicker from '@/components/input/datepicker.vue'
import BaseButton from '@/components/base/BaseButton'
import {playPop} from '@/helpers/playPop'
import TaskSubscription from '@/components/misc/subscription.vue'
import {CURRENT_LIST} from '@/store/mutation-types'
2019-11-24 13:16:24 +00:00
import {uploadFile} from '@/helpers/attachments'
import ChecklistSummary from '../../components/tasks/partials/checklist-summary'
import CreatedUpdated from '@/components/tasks/partials/createdUpdated'
2021-11-30 19:48:48 +00:00
export default {
name: 'TaskDetailView',
2021-11-30 19:48:48 +00:00
compatConfig: { ATTR_FALSE_VALUE: false },
components: {
BaseButton,
CreatedUpdated,
ChecklistSummary,
TaskSubscription,
Better reminders (#308) Fix setting the new reminder component to null after adding a new date Add "close on change" event which only fires if the component closed and the value actually changed Hide the "today" option after 21:00 Add "confirm" button to close the component Use disabled in reminders Add a disabled property to the datepicker Cleanup workarounds for flatpickr Use the new datepicker for end dates Use the new datepicker for start date Use the new datepicker for due dates Mobile styling Format Sync flatpickr when clicking on choose a date Make sure to only hide the popup when not clicked something inside of it Make flatpickr dates work Use datepicker component for reminders Merge branch 'master' into feature/better-reminders Fix bottom padding of inline flatpickr Set time Add method to calculate the neares time Move time helpers in separate folder Remove separate flatpickr date Cleanup Set the flatpickr date when setting changing the date Better formatting of the chosen date Bubble Set date when choosing one Fix test Show correct weekday in preview Change hover background color Make label to show if selected date is null configurable Use a different icon for weekend Ignore test files when linting Add tests to dron Move day interval calculation to separate file and test it Add next date calculation Add basic date picker component Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/308 Co-Authored-By: konrad <konrad@kola-entertainments.de> Co-Committed-By: konrad <konrad@kola-entertainments.de>
2020-11-28 13:59:27 +00:00
Datepicker,
ColorPicker,
ListSearch,
Reminders,
RepeatAfter,
RelatedTasks,
Attachments,
EditAssignees,
EditLabels,
PercentDoneSelect,
PrioritySelect,
Comments,
description,
heading,
},
2021-12-10 14:29:28 +00:00
props: {
taskId: {
type: Number,
required: true,
},
},
data() {
return {
taskService: new TaskService(),
task: new TaskModel(),
// We doubled the task color property here because verte does not have a real change property, leading
// to the color property change being triggered when the # is removed from it, leading to an update,
// which leads in turn to a change... This creates an infinite loop in which the task is updated, changed,
// updated, changed, updated and so on.
// To prevent this, we put the task color property in a seperate value which is set to the task color
// when it is saved and loaded.
taskColor: '',
2019-11-24 13:16:24 +00:00
showDeleteModal: false,
// Used to avoid flashing of empty elements if the task content is not yet loaded.
visible: false,
2019-11-24 13:16:24 +00:00
priorities: priorites,
activeFields: {
assignees: false,
priority: false,
dueDate: false,
percentDone: false,
startDate: false,
endDate: false,
reminders: false,
repeatAfter: false,
labels: false,
attachments: false,
relatedTasks: false,
moveList: false,
color: false,
},
}
},
watch: {
taskId: {
handler: 'loadTask',
immediate: true,
},
parent: {
handler(parent) {
const parentList = parent !== null ? parent.list : null
if (parentList !== null) {
this.$store.commit(CURRENT_LIST, parentList)
}
},
immediate: true,
},
// Using a watcher here because the header component handles saving the task with the api but we want to decouple
// it from the page title.
'task.title': {
handler(title) {
this.setTitle(title)
},
},
},
computed: {
currentList() {
return this.$store.state[CURRENT_LIST]
},
parent() {
if (!this.task.listId) {
return {
namespace: null,
list: null,
}
}
if (!this.$store.getters['namespaces/getListAndNamespaceById']) {
return null
}
return this.$store.getters['namespaces/getListAndNamespaceById'](this.task.listId)
},
canWrite() {
return typeof this.task !== 'undefined' && typeof this.task.maxRight !== 'undefined' && this.task.maxRight > rights.READ
},
hasAttachments() {
return this.$store.state.attachments.attachments.length > 0
},
shouldShowClosePopup() {
return this.$route.name.includes('kanban')
},
},
methods: {
attachmentUpload(...args) {
return uploadFile(this.taskId, ...args)
},
async loadTask(taskId) {
if (taskId === undefined) {
return
}
try {
this.task = await this.taskService.get({id: taskId})
this.$store.commit('attachments/set', this.task.attachments)
this.taskColor = this.task.hexColor
this.setActiveFields()
this.setTitle(this.task.title)
} finally {
this.scrollToHeading()
await this.$nextTick()
this.visible = true
}
},
scrollToHeading() {
if(!this.$refs?.heading?.$el) {
return
}
this.$refs.heading.$el.scrollIntoView({block: 'center'})
},
setActiveFields() {
2020-02-08 17:37:23 +00:00
this.task.startDate = this.task.startDate ? this.task.startDate : null
this.task.endDate = this.task.endDate ? this.task.endDate : null
2020-02-08 17:37:23 +00:00
// Set all active fields based on values in the model
this.activeFields.assignees = this.task.assignees.length > 0
this.activeFields.priority = this.task.priority !== priorites.UNSET
this.activeFields.dueDate = this.task.dueDate !== null
this.activeFields.percentDone = this.task.percentDone > 0
this.activeFields.startDate = this.task.startDate !== null
this.activeFields.endDate = this.task.endDate !== null
this.activeFields.reminders = this.task.reminderDates.length > 0
this.activeFields.repeatAfter = this.task.repeatAfter.amount > 0
this.activeFields.labels = this.task.labels.length > 0
this.activeFields.attachments = this.task.attachments.length > 0
this.activeFields.relatedTasks = Object.keys(this.task.relatedTasks).length > 0
},
async saveTask(showNotification = true, undoCallback = null) {
if (!this.canWrite) {
return
}
// We're doing the whole update in a nextTick because sometimes race conditions can occur when
// setting the due date on mobile which leads to no due date change being saved.
await this.$nextTick()
this.task.hexColor = this.taskColor
// If no end date is being set, but a start date and due date,
// use the due date as the end date
if (this.task.endDate === null && this.task.startDate !== null && this.task.dueDate !== null) {
this.task.endDate = this.task.dueDate
}
this.task = await this.$store.dispatch('tasks/update', this.task)
if (!showNotification) {
return
}
let actions = []
if (undoCallback !== null) {
actions = [{
title: 'Undo',
callback: undoCallback,
}]
}
this.$message.success({message: this.$t('task.detail.updateSuccess')}, actions)
},
setFieldActive(fieldName) {
this.activeFields[fieldName] = true
this.$nextTick(() => {
if (this.$refs[fieldName]) {
this.$refs[fieldName].$el.focus()
// scroll the field to the center of the screen if not in viewport already
const boundingRect = this.$refs[fieldName].$el.getBoundingClientRect()
if (boundingRect.top > (window.scrollY + window.innerHeight) || boundingRect.top < window.scrollY)
this.$refs[fieldName].$el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
})
}
})
},
async deleteTask() {
await this.$store.dispatch('tasks/delete', this.task)
this.$message.success({message: this.$t('task.detail.deleteSuccess')})
this.$router.push({name: 'list.index', params: {listId: this.task.listId}})
},
toggleTaskDone() {
this.task.done = !this.task.done
if (this.task.done) {
playPop()
}
this.saveTask(true, this.toggleTaskDone)
},
async changeList(list) {
2021-10-11 19:40:36 +00:00
this.$store.commit('kanban/removeTaskInBucket', this.task)
this.task.listId = list.id
await this.saveTask()
2019-11-24 13:16:24 +00:00
},
async toggleFavorite() {
this.task.isFavorite = !this.task.isFavorite
this.task = await this.taskService.update(this.task)
this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist')
},
},
}
2019-11-24 13:16:24 +00:00
</script>
<style lang="scss" scoped>
$flash-background-duration: 750ms;
.task-view {
// This is a workaround to hide the llama background from the top on the task detail page
margin-top: -1.5rem;
padding: 1rem;
background-color: var(--site-background);
@media screen and (max-width: $desktop) {
padding-bottom: 0;
}
.subtitle {
color: var(--grey-500);
margin-bottom: 1rem;
a {
color: var(--grey-800);
}
}
h3 .button {
vertical-align: middle;
}
.icon.is-grey {
color: var(--grey-400);
}
:deep(.heading) {
display: flex;
justify-content: space-between;
text-transform: none;
align-items: center;
@media screen and (max-width: $tablet) {
flex-direction: column;
align-items: start;
}
.title {
margin-bottom: 0;
}
.title.input {
// 1.8rem is the font-size, 1.125 is the line-height, .3rem padding everywhere, 1px border around the whole thing.
min-height: calc(1.8rem * 1.125 + .6rem + 2px);
@media screen and (max-width: $tablet) {
margin: 0 -.3rem .5rem -.3rem; // the title has 0.3rem padding - this make the text inside of it align with the rest
}
}
.title.task-id {
color: var(--grey-400);
white-space: nowrap;
}
}
.date-input {
display: flex;
align-items: center;
a.remove {
color: var(--danger);
vertical-align: middle;
padding-left: .5rem;
line-height: 1;
}
}
:deep(.datepicker) {
width: 100%;
a.show {
color: var(--text);
padding: .25rem .5rem;
transition: background-color $transition;
border-radius: $radius;
display: block;
margin: .1rem 0;
&:hover {
background: var(--white);
}
}
&.disabled a.show:hover {
background: transparent;
}
}
.details {
padding-bottom: 0.75rem;
flex-flow: row wrap;
margin-bottom: 0;
.detail-title {
display: block;
color: var(--grey-400);
}
.none {
font-style: italic;
}
// Break after the 2nd element
.column:nth-child(2n) {
page-break-after: always; // CSS 2.1 syntax
break-after: always; // New syntax
}
&.labels-list,
.assignees {
:deep(.multiselect) {
.input-wrapper {
&:not(:focus-within):not(:hover) {
background: transparent !important;
border-color: transparent !important;
}
}
}
}
}
:deep(.details),
:deep(.heading) {
.input:not(.has-defaults),
.textarea,
.select:not(.has-defaults) select {
border-color: transparent;
background: transparent;
cursor: pointer;
transition: all $transition-duration;
&::placeholder {
color: var(--text-light);
opacity: 1;
font-style: italic;
}
&:not(:disabled) {
&:hover,
&:active,
&:focus {
background: var(--scheme-main);
border-color: var(--border);
cursor: text;
}
&:hover,
&:active {
cursor: text;
border-color: var(--link)
}
}
}
.select:not(.has-defaults):after {
opacity: 0;
}
.select:not(.has-defaults):hover:after {
opacity: 1;
}
}
.attachments {
margin-bottom: 0;
table tr:last-child td {
border-bottom: none;
}
}
.action-buttons {
2022-01-08 14:11:55 +00:00
.button {
width: 100%;
margin-bottom: .5rem;
justify-content: left;
}
}
.created {
font-size: .75rem;
color: var(--grey-500);
text-align: right;
}
.checklist-summary {
margin-left: .25rem;
}
}
.task-view-container {
padding-bottom: 1rem;
@media screen and (max-width: $desktop) {
padding-bottom: 0;
}
.task-view * {
opacity: 0;
transition: opacity 50ms ease;
}
&.is-loading {
opacity: 1;
.task-view * {
opacity: 0;
}
}
&.visible:not(.is-loading) .task-view * {
opacity: 1;
}
}
.task-view-container {
// simulate sass lighten($primary, 30) by increasing lightness 30% to 73%
--primary-light: hsla(var(--primary-h), var(--primary-s), 73%, var(--primary-a));
}
.flash-background-enter-from,
.flash-background-enter-active {
animation: flash-background $flash-background-duration ease 1;
}
@keyframes flash-background {
0% {
background: var(--primary-light);
}
100% {
background: transparent;
}
}
2021-11-08 14:46:39 +00:00
@media (prefers-reduced-motion: reduce) {
@keyframes flash-background {
0% {
background: transparent;
}
}
}
2021-11-08 14:46:39 +00:00
@include modal-transition();
</style>