feat: add-task usability improvements #2767

Merged
konrad merged 1 commits from dpschen/frontend:feature/improve-add-task into main 2023-01-04 15:54:10 +00:00
10 changed files with 331 additions and 17 deletions

View File

@ -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",

View File

@ -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}

View 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

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.

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.
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

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

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.

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.

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?

Yss, should be fine then.

Yss, should be fine then.

Todo: add comments

Todo: add comments

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

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).

Don't we already have a variable for this?

Don't we already have a variable for this?

There is the global $transition-duration but with 150ms it felt to short.

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

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>

View File

@ -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"

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.

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

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.

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)

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

What about this?

What about this?
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) {

I don't get what this does. Also which is deprecated

I don't get what this does. Also [`which` is deprecated](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/which)

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).

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).

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).

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).

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,

If that's the whole purpose of the selector we might as well just us an mb-0 utility on the div instead.

If that's the whole purpose of the selector we might as well just us an `mb-0` utility on the div instead.

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.

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>

View File

@ -1,5 +1,5 @@
<template>
<div v-if="available">
<div v-if="mode !== 'disabled' && prefixes !== undefined">
dpschen marked this conversation as resolved Outdated

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.

Can't we move prefixes !== undefined to the available computed? Or better not?

Can't we move `prefixes !== undefined` to the `available` computed? Or better not?

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.

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.

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>

View File

@ -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

View 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)
}

View File

@ -1,3 +1,4 @@
import './polyfills'
import {createApp} from 'vue'
import pinia from './pinia'

4
src/polyfills.ts Normal file
View 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)

View File

@ -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,
},
}),
],
},
},