feat: add-task usability improvements #2767
|
@ -49,6 +49,7 @@
|
|||
"flatpickr": "4.6.13",
|
||||
"flexsearch": "0.7.21",
|
||||
"floating-vue": "2.0.0-beta.20",
|
||||
"focus-within": "3.0.2",
|
||||
"highlight.js": "11.7.0",
|
||||
"is-touch-device": "1.0.1",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
|
@ -77,6 +78,7 @@
|
|||
"@types/codemirror": "5.60.6",
|
||||
"@types/dompurify": "2.4.0",
|
||||
"@types/flexsearch": "0.7.3",
|
||||
"@types/focus-within": "1.0.1",
|
||||
"@types/lodash.debounce": "4.0.7",
|
||||
"@types/marked": "4.0.8",
|
||||
"@types/node": "18.11.18",
|
||||
|
@ -99,6 +101,8 @@
|
|||
"happy-dom": "8.1.1",
|
||||
"netlify-cli": "12.5.0",
|
||||
"postcss": "8.4.20",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
"postcss-easings": "3.0.1",
|
||||
"postcss-preset-env": "7.8.3",
|
||||
"rollup": "3.9.1",
|
||||
"rollup-plugin-visualizer": "5.9.0",
|
||||
|
|
|
@ -19,6 +19,7 @@ specifiers:
|
|||
'@types/codemirror': 5.60.6
|
||||
'@types/dompurify': 2.4.0
|
||||
'@types/flexsearch': 0.7.3
|
||||
'@types/focus-within': 1.0.1
|
||||
'@types/is-touch-device': 1.0.0
|
||||
'@types/lodash.clonedeep': 4.5.7
|
||||
'@types/lodash.debounce': 4.0.7
|
||||
|
@ -55,6 +56,7 @@ specifiers:
|
|||
flatpickr: 4.6.13
|
||||
flexsearch: 0.7.21
|
||||
floating-vue: 2.0.0-beta.20
|
||||
focus-within: 3.0.2
|
||||
happy-dom: 8.1.1
|
||||
highlight.js: 11.7.0
|
||||
is-touch-device: 1.0.1
|
||||
|
@ -65,6 +67,8 @@ specifiers:
|
|||
netlify-cli: 12.5.0
|
||||
pinia: 2.0.28
|
||||
postcss: 8.4.20
|
||||
postcss-easing-gradients: 3.0.1
|
||||
postcss-easings: 3.0.1
|
||||
postcss-preset-env: 7.8.3
|
||||
register-service-worker: 1.7.2
|
||||
rollup: 3.9.1
|
||||
|
@ -118,6 +122,7 @@ dependencies:
|
|||
flatpickr: 4.6.13
|
||||
flexsearch: 0.7.21
|
||||
floating-vue: 2.0.0-beta.20_vue@3.2.45
|
||||
focus-within: 3.0.2
|
||||
highlight.js: 11.7.0
|
||||
is-touch-device: 1.0.1
|
||||
lodash.clonedeep: 4.5.0
|
||||
|
@ -146,6 +151,7 @@ devDependencies:
|
|||
'@types/codemirror': 5.60.6
|
||||
'@types/dompurify': 2.4.0
|
||||
'@types/flexsearch': 0.7.3
|
||||
'@types/focus-within': 1.0.1
|
||||
'@types/lodash.debounce': 4.0.7
|
||||
'@types/marked': 4.0.8
|
||||
'@types/node': 18.11.18
|
||||
|
@ -168,6 +174,8 @@ devDependencies:
|
|||
happy-dom: 8.1.1
|
||||
netlify-cli: 12.5.0_@types+node@18.11.18
|
||||
postcss: 8.4.20
|
||||
postcss-easing-gradients: 3.0.1
|
||||
postcss-easings: 3.0.1_postcss@8.4.20
|
||||
postcss-preset-env: 7.8.3_postcss@8.4.20
|
||||
rollup: 3.9.1
|
||||
rollup-plugin-visualizer: 5.9.0_rollup@3.9.1
|
||||
|
@ -3253,6 +3261,10 @@ packages:
|
|||
resolution: {integrity: sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg==}
|
||||
dev: true
|
||||
|
||||
/@types/focus-within/1.0.1:
|
||||
resolution: {integrity: sha512-ClIiYA9fOJUcZzb8nQXlvwta5obDHd5aQw4I2L/b1lvFSPXXImgiN7ueRVfMlIEkpAvc22hMjbu3g3RiPzZEUQ==}
|
||||
dev: true
|
||||
|
||||
/@types/glob/7.2.0:
|
||||
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
|
||||
dependencies:
|
||||
|
@ -4869,6 +4881,10 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/chroma-js/1.4.1:
|
||||
resolution: {integrity: sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ==}
|
||||
dev: true
|
||||
|
||||
/ci-info/2.0.0:
|
||||
resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
|
||||
dev: true
|
||||
|
@ -6105,6 +6121,10 @@ packages:
|
|||
resolution: {integrity: sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==}
|
||||
dev: true
|
||||
|
||||
/easing-coordinates/2.0.2:
|
||||
resolution: {integrity: sha512-uQpJaLQX2CKVnN27YvN4sL4pXyEFGAv00y4zgrC46H0EBHrDhJc/8OsT2CQ5/6yz6+d+u8ACGd9bo4v83FNVlg==}
|
||||
dev: true
|
||||
|
||||
/eastasianwidth/0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
dev: true
|
||||
|
@ -7069,6 +7089,14 @@ packages:
|
|||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||
dev: true
|
||||
|
||||
/focus-within/3.0.2:
|
||||
resolution: {integrity: sha512-TMn2sLUwi02dSPElXFPyzBkiQN+Xo1oYta5Jd2zC54MQoBOCMht6xar7gJgw5a9+GtFxkG9c2k4ptI6cU37soA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
postcss: 7.0.39
|
||||
dev: false
|
||||
|
||||
/folder-walker/3.2.0:
|
||||
resolution: {integrity: sha512-VjAQdSLsl6AkpZNyrQJfO7BXLo4chnStqb055bumZMbRUPpVuPN3a4ktsnRCmrFZjtMlYLkyXiR5rAs4WOpC4Q==}
|
||||
dependencies:
|
||||
|
@ -10489,6 +10517,9 @@ packages:
|
|||
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
||||
dev: true
|
||||
|
||||
/picocolors/0.2.1:
|
||||
resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==}
|
||||
|
||||
/picocolors/1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
|
||||
|
@ -10662,6 +10693,26 @@ packages:
|
|||
postcss-value-parser: 4.2.0
|
||||
dev: true
|
||||
|
||||
/postcss-easing-gradients/3.0.1:
|
||||
resolution: {integrity: sha512-UrOKb4cenjGmMmrheETw7Cjnn/IKn3xgTvHs92b0sSwMhKgeZKxJpduGRjYZ8wgpu3zOzzgQpRwOLhhtMofayA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dependencies:
|
||||
chroma-js: 1.4.1
|
||||
easing-coordinates: 2.0.2
|
||||
postcss: 7.0.39
|
||||
postcss-value-parser: 3.3.1
|
||||
dev: true
|
||||
|
||||
/postcss-easings/3.0.1_postcss@8.4.20:
|
||||
resolution: {integrity: sha512-n3bG/X3iB0m8d845vhFg/62/ECeT8jY8gE8F2A41z8Mty41spYA4vzMLezha7icVjtGjqlxgO3QE+uOzpDqeww==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
postcss: ^8.1.0
|
||||
dependencies:
|
||||
postcss: 8.4.20
|
||||
postcss-value-parser: 4.2.0
|
||||
dev: true
|
||||
|
||||
/postcss-env-function/4.0.6_postcss@8.4.20:
|
||||
resolution: {integrity: sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==}
|
||||
engines: {node: ^12 || ^14 || >=16}
|
||||
|
@ -10894,6 +10945,10 @@ packages:
|
|||
util-deprecate: 1.0.2
|
||||
dev: true
|
||||
|
||||
/postcss-value-parser/3.3.1:
|
||||
resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==}
|
||||
dev: true
|
||||
|
||||
/postcss-value-parser/4.2.0:
|
||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
||||
dev: true
|
||||
|
@ -10910,6 +10965,13 @@ packages:
|
|||
quote-unquote: 1.0.0
|
||||
dev: true
|
||||
|
||||
/postcss/7.0.39:
|
||||
resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
dependencies:
|
||||
picocolors: 0.2.1
|
||||
source-map: 0.6.1
|
||||
|
||||
/postcss/8.4.20:
|
||||
resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<transition
|
||||
name="expandable-slide"
|
||||
@before-enter="beforeEnter"
|
||||
@enter="enter"
|
||||
@after-enter="afterEnter"
|
||||
@enter-cancelled="enterCancelled"
|
||||
@before-leave="beforeLeave"
|
||||
@leave="leave"
|
||||
@after-leave="afterLeave"
|
||||
@leave-cancelled="leaveCancelled"
|
||||
>
|
||||
<div
|
||||
|
||||
v-if="initialHeight"
|
||||
class="expandable-initial-height"
|
||||
:style="{ maxHeight: `${initialHeight}px` }"
|
||||
:class="{ 'expandable-initial-height--expanded': open }"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<div v-else-if="open" class="expandable">
|
||||
<slot />
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// the logic of this component is loosly based on this article
|
||||
// https://gomakethings.com/how-to-add-transition-animations-to-vanilla-javascript-show-and-hide-methods/#putting-it-all-together
|
||||
|
||||
import {computed, ref} from 'vue'
|
||||
import {getInheritedBackgroundColor} from '@/helpers/getInheritedBackgroundColor'
|
||||
|
||||
const props = defineProps({
|
||||
/** Wheather the Expandable is open or not */
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/** If there is too much content, content will be cut of here. */
|
||||
initialHeight: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
/** The hidden content is indicated by a gradient. This is the color that the gradient fades to.
|
||||
* Makes only sense if `initialHeight` is set. */
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
},
|
||||
})
|
||||
|
||||
const wrapper = ref<HTMLElement | null>(null)
|
||||
|
||||
const computedBackgroundColor = computed(() => {
|
||||
if (wrapper.value === null) {
|
||||
return props.backgroundColor || '#fff'
|
||||
}
|
||||
return props.backgroundColor || getInheritedBackgroundColor(wrapper.value)
|
||||
})
|
||||
|
||||
/**
|
||||
* Get the natural height of the element
|
||||
*/
|
||||
function getHeight(el: HTMLElement) {
|
||||
const { display } = el.style // save display property
|
||||
el.style.display = 'block' // Make it visible
|
||||
const height = `${el.scrollHeight}px` // Get its height
|
||||
el.style.display = display // revert to original display property
|
||||
dpschen marked this conversation as resolved
Outdated
konrad
commented
I think this comment can be removed. I think this comment can be removed.
|
||||
return height
|
||||
}
|
||||
|
||||
/**
|
||||
* force layout of element changes
|
||||
* https://gist.github.com/paulirish/5d52fb081b3570c81e3a
|
||||
*/
|
||||
function forceLayout(el: HTMLElement) {
|
||||
el.offsetTop
|
||||
}
|
||||
|
||||
/* ######################################################################
|
||||
# The following functions are called by the js hooks of the transitions.
|
||||
# They follow the orignal hook order of the vue transition component
|
||||
# see: https://vuejs.org/guide/built-ins/transition.html#javascript-hooks
|
||||
###################################################################### */
|
||||
|
||||
function beforeEnter(el: HTMLElement) {
|
||||
el.style.height = '0'
|
||||
el.style.willChange = 'height'
|
||||
el.style.backfaceVisibility = 'hidden'
|
||||
forceLayout(el)
|
||||
}
|
||||
|
||||
// the done callback is optional when
|
||||
// used in combination with CSS
|
||||
function enter(el: HTMLElement) {
|
||||
const height = getHeight(el) // Get the natural height
|
||||
el.style.height = height // Update the height
|
||||
}
|
||||
|
||||
function afterEnter(el: HTMLElement) {
|
||||
removeHeight(el)
|
||||
}
|
||||
|
||||
function enterCancelled(el: HTMLElement) {
|
||||
removeHeight(el)
|
||||
}
|
||||
|
||||
function beforeLeave(el: HTMLElement) {
|
||||
// Give the element a height to change from
|
||||
el.style.height = `${el.scrollHeight}px`
|
||||
forceLayout(el)
|
||||
}
|
||||
|
||||
function leave(el: HTMLElement) {
|
||||
// Set the height back to 0
|
||||
el.style.height = '0'
|
||||
el.style.willChange = ''
|
||||
el.style.backfaceVisibility = ''
|
||||
}
|
||||
|
||||
dpschen marked this conversation as resolved
Outdated
konrad
commented
Can you group all functions which just call Can you group all functions which just call `removeHeight` like this one? There's probably a good reason to define them with different names even though they all do the same, but it would be nice if they were grouped together so that it's all obvious in one place.
dpschen
commented
I kept the orignal hook order of the vue transition component. Is it fine if I add some comments? I kept the [orignal hook order of the vue transition component](https://vuejs.org/guide/built-ins/transition.html#javascript-hooks). Is it fine if I add some comments?
konrad
commented
Yss, should be fine then. Yss, should be fine then.
dpschen
commented
Todo: add comments Todo: add comments
dpschen
commented
Done. Done.
|
||||
function afterLeave(el: HTMLElement) {
|
||||
removeHeight(el)
|
||||
}
|
||||
|
||||
function leaveCancelled(el: HTMLElement) {
|
||||
removeHeight(el)
|
||||
}
|
||||
|
||||
function removeHeight(el: HTMLElement) {
|
||||
el.style.height = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$transition-time: 300ms;
|
||||
dpschen marked this conversation as resolved
Outdated
dpschen
commented
These functions are possible with the postcss-easings plugin. There is somewhere a working draft for this (couldn't find, but I know it exists). These functions are possible with the postcss-easings plugin. There is somewhere a working draft for this (couldn't find, but I know it exists).
konrad
commented
Don't we already have a variable for this? Don't we already have a variable for this?
dpschen
commented
There is the global There is the global `$transition-duration` but with 150ms it felt to short.
|
||||
|
||||
.expandable-slide-enter-active,
|
||||
.expandable-slide-leave-active {
|
||||
transition:
|
||||
opacity $transition-time ease-in-quint,
|
||||
height $transition-time ease-in-out-quint;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.expandable-slide-enter,
|
||||
.expandable-slide-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.expandable-initial-height {
|
||||
padding: 5px;
|
||||
margin: -5px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
dpschen marked this conversation as resolved
Outdated
dpschen
commented
We use here easing gradients. Because it's visually the only thing that makes sense if you want to fade out content. We use here [easing gradients](https://larsenwork.com/easing-gradients/). Because it's visually the only thing that makes sense if you want to fade out content.
|
||||
display: block;
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
ease-in-out
|
||||
v-bind(computedBackgroundColor)
|
||||
);
|
||||
position: absolute;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.expandable-initial-height--expanded {
|
||||
height: 100% !important;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,9 +1,8 @@
|
|||
<template>
|
||||
<div class="task-add">
|
||||
<div class="field is-grouped">
|
||||
<div class="task-add" ref="taskAdd">
|
||||
<div class="add-task__field field is-grouped">
|
||||
<p class="control has-icons-left is-expanded">
|
||||
<textarea
|
||||
:disabled="loading || undefined"
|
||||
dpschen
commented
You can always write in this field. But you may not be able so submit. You can always write in this field. But you may not be able so submit.
This makes the initial loading of the home page calmer.
|
||||
class="add-task-textarea input"
|
||||
:class="{'textarea-empty': newTaskTitle === ''}"
|
||||
:placeholder="$t('list.list.addPlaceholder')"
|
||||
|
@ -33,27 +32,34 @@
|
|||
</x-button>
|
||||
</p>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<quick-add-magic v-else/>
|
||||
<Expandable :open="errorMessage !== '' || taskAddFocused || taskAddHovered && debouncedTaskAddHovered">
|
||||
dpschen marked this conversation as resolved
Outdated
dpschen
commented
By combining By combining `taskAddHovered` with `debouncedTaskAddHovered` we make sure that the content is shown on hover, but only if it's debounced. We make also sure that if there is no focus or error and the mouse leaves the area the content will immediately be hidden.
|
||||
<p class="pt-3 mt-0 help is-danger" v-if="errorMessage !== ''">
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<quick-add-magic v-else class="quick-add-magic" />
|
||||
</Expandable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {refDebounced, useElementHover, useFocusWithin} from '@vueuse/core'
|
||||
|
||||
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
|
||||
import {RELATION_KIND} from '@/types/IRelationKind'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
|
||||
import Expandable from '@/components/base/Expandable.vue'
|
||||
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
|
||||
import {parseSubtasksViaIndention} from '@/helpers/parseSubtasksViaIndention'
|
||||
import TaskRelationService from '@/services/taskRelation'
|
||||
import TaskRelationModel from '@/models/taskRelation'
|
||||
import {RELATION_KIND} from '@/types/IRelationKind'
|
||||
import {getLabelsFromPrefix} from '@/modules/parseTaskText'
|
||||
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea'
|
||||
import {getLabelsFromPrefix} from '@/modules/parseTaskText'
|
||||
|
||||
const props = defineProps({
|
||||
defaultPosition: {
|
||||
|
@ -71,9 +77,24 @@ const {t} = useI18n({useScope: 'global'})
|
|||
const authStore = useAuthStore()
|
||||
const taskStore = useTaskStore()
|
||||
|
||||
const taskAdd = ref<HTMLTextAreaElement | null>(null)
|
||||
|
||||
dpschen
commented
This is really cool. But I can only enable this in the moment that we have a reliable way to detect if there is a modal open. E.g. This fails currently for the quick-actions. This is really cool. But I can only enable this in the moment that we have a reliable way to detect if there is a modal open. E.g. This fails currently for the quick-actions.
|
||||
// enable only if we don't have a modal
|
||||
konrad
commented
What about this? What about this?
dpschen
commented
See: #2767 (comment) See: https://kolaente.dev/vikunja/frontend/pulls/2767#issuecomment-41260
|
||||
// onStartTyping(() => {
|
||||
// if (newTaskInput.value === null || document.activeElement === newTaskInput.value) {
|
||||
// return
|
||||
// }
|
||||
// newTaskInput.value.focus()
|
||||
// })
|
||||
|
||||
const { focused: taskAddFocused } = useFocusWithin(taskAdd)
|
||||
|
||||
const taskAddHovered = useElementHover(taskAdd)
|
||||
const debouncedTaskAddHovered = refDebounced(taskAddHovered, 500)
|
||||
|
||||
const errorMessage = ref('')
|
||||
|
||||
function resetEmptyTitleError(e) {
|
||||
function resetEmptyTitleError(e: KeyboardEvent) {
|
||||
dpschen
commented
I don't get what this does. Also I don't get what this does. Also [`which` is deprecated](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/which)
konrad
commented
I think it resets the error message if any letter was pressed. (iirc the I think it resets the error message if any letter was pressed. (iirc the `change` event on the checkbox triggers even if you press something like esc or shift).
dpschen
commented
I still don't get this. This is called from a Also I understand where the specific values from the range come from / which keys they stand for. The range includes many more then just two codes (as implied by your "esc or shift" comment). I still don't get this. This is called from a `<textarea>` not from a checkbox.
Also I understand where the specific values from the range come from / which keys they stand for. The range includes many more then just two codes (as implied by your "esc or shift" comment).
konrad
commented
Maybe we can actually remove this? (in a follow-up PR) Maybe we can actually remove this? (in a follow-up PR)
|
||||
if (
|
||||
(e.which <= 90 && e.which >= 48 || e.which >= 96 && e.which <= 105)
|
||||
&& newTaskTitle.value !== ''
|
||||
|
@ -192,7 +213,9 @@ defineExpose({
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.task-add {
|
||||
.task-add,
|
||||
konrad
commented
If that's the whole purpose of the selector we might as well just us an If that's the whole purpose of the selector we might as well just us an `mb-0` utility on the div instead.
dpschen
commented
I would agree if we would add space here and not reset bulma styles. I would agree if we would _add_ space here and not reset bulma styles.
But since we reset something that bulma did, where I'm not sure if that makes so much sence in the first place I would keep it here, so that if we remove this part of bulma we can easily remove the rules.
|
||||
// overwrite bulma styles
|
||||
.task-add .add-task__field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
@ -220,4 +243,8 @@ defineExpose({
|
|||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.quick-add-magic {
|
||||
padding-top: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-if="available">
|
||||
<div v-if="mode !== 'disabled' && prefixes !== undefined">
|
||||
dpschen marked this conversation as resolved
Outdated
dpschen
commented
This is mostly for typescript, because it didn't get that prefixes won't be used if available is false. This is mostly for typescript, because it didn't get that prefixes won't be used if available is false.
konrad
commented
Can't we move Can't we move `prefixes !== undefined` to the `available` computed? Or better not?
dpschen
commented
I saw the available as I saw the available as `isNotDisabled` which is what is value is.
Will rename a bit and maybe add a third computed to make it clearer.
dpschen
commented
I removed the computed and put the calculation inline instead. I removed the computed and put the calculation inline instead.
|
||||
<p class="help has-text-grey">
|
||||
{{ $t('task.quickAddMagic.hint') }}.
|
||||
<ButtonLink @click="() => visible = true">{{ $t('task.quickAddMagic.what') }}</ButtonLink>
|
||||
|
@ -100,6 +100,5 @@ import {PREFIXES} from '@/modules/parseTaskText'
|
|||
const visible = ref(false)
|
||||
const mode = ref(getQuickAddMagicMode())
|
||||
|
||||
const available = computed(() => mode.value !== 'disabled')
|
||||
const prefixes = computed(() => PREFIXES[mode.value])
|
||||
</script>
|
||||
|
|
|
@ -4,11 +4,11 @@ import {debouncedWatch, tryOnMounted, useWindowSize, type MaybeRef} from '@vueus
|
|||
// TODO: also add related styles
|
||||
// OR: replace with vueuse function
|
||||
export function useAutoHeightTextarea(value: MaybeRef<string>) {
|
||||
const textarea = ref<HTMLInputElement>()
|
||||
const textarea = ref<HTMLTextAreaElement | null>(null)
|
||||
const minHeight = ref(0)
|
||||
|
||||
// adapted from https://github.com/LeaVerou/stretchy/blob/47f5f065c733029acccb755cae793009645809e2/src/stretchy.js#L34
|
||||
function resize(textareaEl: HTMLInputElement | undefined) {
|
||||
function resize(textareaEl: HTMLTextAreaElement | null) {
|
||||
if (!textareaEl) return
|
||||
|
||||
let empty
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
function getDefaultBackground() {
|
||||
const div = document.createElement('div')
|
||||
document.head.appendChild(div)
|
||||
const bg = window.getComputedStyle(div).backgroundColor
|
||||
document.head.removeChild(div)
|
||||
return bg
|
||||
}
|
||||
|
||||
// get default style for current browser
|
||||
const defaultStyle = getDefaultBackground() // typically "rgba(0, 0, 0, 0)"
|
||||
|
||||
// based on https://stackoverflow.com/a/62630563/15522256
|
||||
export function getInheritedBackgroundColor(el: HTMLElement): string {
|
||||
const backgroundColor = window.getComputedStyle(el).backgroundColor
|
||||
|
||||
if (backgroundColor !== defaultStyle) return backgroundColor
|
||||
|
||||
if (!el.parentElement) {
|
||||
// we reached the top parent el without getting an explicit color
|
||||
return defaultStyle
|
||||
}
|
||||
|
||||
return getInheritedBackgroundColor(el.parentElement)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import './polyfills'
|
||||
import {createApp} from 'vue'
|
||||
|
||||
import pinia from './pinia'
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
// in order to use postcss-preset-env correctly we need some client side plugins
|
||||
import focusWithin from 'focus-within'
|
||||
|
||||
focusWithin(document)
|
|
@ -10,6 +10,8 @@ import {VitePWA} from 'vite-plugin-pwa'
|
|||
import {visualizer} from 'rollup-plugin-visualizer'
|
||||
import svgLoader from 'vite-svg-loader'
|
||||
import postcssPresetEnv from 'postcss-preset-env'
|
||||
import postcssEasings from 'postcss-easings'
|
||||
import postcssEasingGradients from 'postcss-easing-gradients'
|
||||
|
||||
|
||||
const pathSrc = fileURLToPath(new URL('./src', import.meta.url))
|
||||
|
@ -46,7 +48,19 @@ export default defineConfig({
|
|||
},
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssPresetEnv(),
|
||||
postcssEasings(),
|
||||
postcssEasingGradients(),
|
||||
postcssPresetEnv({
|
||||
// These plugins are enabled by default but require
|
||||
// a polyfill that we don't include
|
||||
// see also './src/polyfills.ts'
|
||||
features: {
|
||||
'blank-pseudo-class': false,
|
||||
'focus-visible-pseudo-class': false,
|
||||
'has-pseudo-class': false,
|
||||
'prefers-color-scheme-query': false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
The
initialHeight
might be currently broken. I still kept it in since I'm not sure how to remove that in a way that makes sense.