forked from vikunja/frontend
feat: add-task usability improvements (#2767)
Co-authored-by: Dominik Pschenitschni <mail@celement.de> Reviewed-on: vikunja/frontend#2767 Reviewed-by: konrad <k@knt.li> Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de> Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
This commit is contained in:
parent
5a89bc0183
commit
4be53b098c
@ -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",
|
||||
|
62
pnpm-lock.yaml
generated
62
pnpm-lock.yaml
generated
@ -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}
|
||||
|
179
src/components/base/Expandable.vue
Normal file
179
src/components/base/Expandable.vue
Normal file
@ -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
|
||||
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 = ''
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
.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: "";
|
||||
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"
|
||||
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">
|
||||
<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)
|
||||
|
||||
// enable only if we don't have a modal
|
||||
// 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) {
|
||||
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,
|
||||
// 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">
|
||||
<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
|
||||
|
24
src/helpers/getInheritedBackgroundColor.ts
Normal file
24
src/helpers/getInheritedBackgroundColor.ts
Normal file
@ -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'
|
||||
|
4
src/polyfills.ts
Normal file
4
src/polyfills.ts
Normal file
@ -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,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user