Merge branch 'main' into main
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
commit
85f91c784f
14
.npmrc
14
.npmrc
|
@ -1,2 +1,14 @@
|
||||||
|
fetch-timeout=100000
|
||||||
|
|
||||||
|
# pnpm settings
|
||||||
|
# The following settings prepare for the new default value of pnpm 8
|
||||||
|
# they can be removed directly after having moved to pnpm 8
|
||||||
auto-install-peers=true
|
auto-install-peers=true
|
||||||
fetch-timeout=100000
|
dedupe-peer-dependents=true
|
||||||
|
resolve-peers-from-workspace-root=true
|
||||||
|
save-workspace-protocol=rolling
|
||||||
|
resolution-mode=lowest-direct
|
||||||
|
publishConfig.linkDirectory=true
|
||||||
|
|
||||||
|
# remove some time after having moved to pnpm 8
|
||||||
|
use-lockfile-v6=true
|
||||||
|
|
|
@ -120,7 +120,7 @@ describe('Namepaces', () => {
|
||||||
.should('not.contain', 'Archived')
|
.should('not.contain', 'Archived')
|
||||||
|
|
||||||
// Show archived
|
// Show archived
|
||||||
cy.get('[data-cy="show-archived-check"] label.check span')
|
cy.get('[data-cy="show-archived-check"] .fancycheckbox__content')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click()
|
.click()
|
||||||
cy.get('[data-cy="show-archived-check"] input')
|
cy.get('[data-cy="show-archived-check"] input')
|
||||||
|
@ -129,7 +129,7 @@ describe('Namepaces', () => {
|
||||||
.should('contain', 'Archived')
|
.should('contain', 'Archived')
|
||||||
|
|
||||||
// Don't show archived
|
// Don't show archived
|
||||||
cy.get('[data-cy="show-archived-check"] label.check span')
|
cy.get('[data-cy="show-archived-check"] .fancycheckbox__content')
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.click()
|
.click()
|
||||||
cy.get('[data-cy="show-archived-check"] input')
|
cy.get('[data-cy="show-archived-check"] input')
|
||||||
|
|
|
@ -22,10 +22,10 @@ describe('Project View Table', () => {
|
||||||
cy.get('.project-table .filter-container .items .button')
|
cy.get('.project-table .filter-container .items .button')
|
||||||
.contains('Columns')
|
.contains('Columns')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
|
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox')
|
||||||
.contains('Priority')
|
.contains('Priority')
|
||||||
.click()
|
.click()
|
||||||
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
|
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox')
|
||||||
.contains('Done')
|
.contains('Done')
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ describe('Task', () => {
|
||||||
TaskFactory.create(1)
|
TaskFactory.create(1)
|
||||||
|
|
||||||
cy.visit('/projects/1/list')
|
cy.visit('/projects/1/list')
|
||||||
cy.get('.tasks .task .fancycheckbox label.check')
|
cy.get('.tasks .task .fancycheckbox')
|
||||||
.first()
|
.first()
|
||||||
.click()
|
.click()
|
||||||
cy.get('.global-notification')
|
cy.get('.global-notification')
|
||||||
|
|
|
@ -3,6 +3,16 @@
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
/// <reference types="@histoire/plugin-vue/components" />
|
/// <reference types="@histoire/plugin-vue/components" />
|
||||||
|
|
||||||
|
declare module 'postcss-focus-within/browser' {
|
||||||
|
import focusWithinInit from 'postcss-focus-within/browser'
|
||||||
|
export default focusWithinInit
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'css-has-pseudo/browser' {
|
||||||
|
import cssHasPseudo from 'css-has-pseudo/browser'
|
||||||
|
export default cssHasPseudo
|
||||||
|
}
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly VITE_IS_ONLINE: boolean
|
readonly VITE_IS_ONLINE: boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1664753041,
|
"lastModified": 1680030621,
|
||||||
"narHash": "sha256-0ogaD8PaGHluARFeupofvk1Nq9gpVeZdlFM0Kcwguys=",
|
"narHash": "sha256-qQa1NeS5Rvk2lgK5lSk986PC6I72yIHejzM8PFu+dHs=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "a62844b302507c7531ad68a86cb7aa54704c9cb4",
|
"rev": "402cc3633cc60dfc50378197305c984518b30773",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
21
package.json
21
package.json
|
@ -55,9 +55,6 @@
|
||||||
"@kyvg/vue3-notification": "2.9.0",
|
"@kyvg/vue3-notification": "2.9.0",
|
||||||
"@sentry/tracing": "7.45.0",
|
"@sentry/tracing": "7.45.0",
|
||||||
"@sentry/vue": "7.45.0",
|
"@sentry/vue": "7.45.0",
|
||||||
"@types/is-touch-device": "1.0.0",
|
|
||||||
"@types/lodash.clonedeep": "4.5.7",
|
|
||||||
"@types/sortablejs": "1.15.1",
|
|
||||||
"@vueuse/core": "9.13.0",
|
"@vueuse/core": "9.13.0",
|
||||||
"axios": "1.3.4",
|
"axios": "1.3.4",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
|
@ -72,7 +69,6 @@
|
||||||
"flatpickr": "4.6.13",
|
"flatpickr": "4.6.13",
|
||||||
"flexsearch": "0.7.31",
|
"flexsearch": "0.7.31",
|
||||||
"floating-vue": "2.0.0-beta.20",
|
"floating-vue": "2.0.0-beta.20",
|
||||||
"focus-within": "3.0.2",
|
|
||||||
"highlight.js": "11.7.0",
|
"highlight.js": "11.7.0",
|
||||||
"is-touch-device": "1.0.1",
|
"is-touch-device": "1.0.1",
|
||||||
"klona": "2.0.6",
|
"klona": "2.0.6",
|
||||||
|
@ -102,11 +98,12 @@
|
||||||
"@types/codemirror": "5.60.7",
|
"@types/codemirror": "5.60.7",
|
||||||
"@types/dompurify": "3.0.0",
|
"@types/dompurify": "3.0.0",
|
||||||
"@types/flexsearch": "0.7.3",
|
"@types/flexsearch": "0.7.3",
|
||||||
"@types/focus-within": "1.0.1",
|
"@types/is-touch-device": "1.0.0",
|
||||||
"@types/lodash.debounce": "4.0.7",
|
"@types/lodash.debounce": "4.0.7",
|
||||||
"@types/marked": "4.0.8",
|
"@types/marked": "4.0.8",
|
||||||
"@types/node": "18.15.10",
|
"@types/node": "18.15.11",
|
||||||
"@types/postcss-preset-env": "7.7.0",
|
"@types/postcss-preset-env": "7.7.0",
|
||||||
|
"@types/sortablejs": "1.15.1",
|
||||||
"@typescript-eslint/eslint-plugin": "5.57.0",
|
"@typescript-eslint/eslint-plugin": "5.57.0",
|
||||||
"@typescript-eslint/parser": "5.57.0",
|
"@typescript-eslint/parser": "5.57.0",
|
||||||
"@vitejs/plugin-legacy": "4.0.2",
|
"@vitejs/plugin-legacy": "4.0.2",
|
||||||
|
@ -117,10 +114,11 @@
|
||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.14",
|
||||||
"browserslist": "4.21.5",
|
"browserslist": "4.21.5",
|
||||||
"caniuse-lite": "1.0.30001470",
|
"caniuse-lite": "1.0.30001470",
|
||||||
|
"css-has-pseudo": "5.0.2",
|
||||||
"csstype": "3.1.1",
|
"csstype": "3.1.1",
|
||||||
"cypress": "12.8.1",
|
"cypress": "12.9.0",
|
||||||
"esbuild": "0.17.14",
|
"esbuild": "0.17.14",
|
||||||
"eslint": "8.36.0",
|
"eslint": "8.37.0",
|
||||||
"eslint-plugin-vue": "9.10.0",
|
"eslint-plugin-vue": "9.10.0",
|
||||||
"happy-dom": "8.9.0",
|
"happy-dom": "8.9.0",
|
||||||
"histoire": "0.15.9",
|
"histoire": "0.15.9",
|
||||||
|
@ -128,7 +126,8 @@
|
||||||
"postcss": "8.4.21",
|
"postcss": "8.4.21",
|
||||||
"postcss-easing-gradients": "3.0.1",
|
"postcss-easing-gradients": "3.0.1",
|
||||||
"postcss-easings": "3.0.1",
|
"postcss-easings": "3.0.1",
|
||||||
"postcss-preset-env": "8.1.0",
|
"postcss-focus-within": "7.0.2",
|
||||||
|
"postcss-preset-env": "8.2.0",
|
||||||
"rollup": "3.20.2",
|
"rollup": "3.20.2",
|
||||||
"rollup-plugin-visualizer": "5.9.0",
|
"rollup-plugin-visualizer": "5.9.0",
|
||||||
"sass": "1.60.0",
|
"sass": "1.60.0",
|
||||||
|
@ -136,9 +135,9 @@
|
||||||
"typescript": "5.0.2",
|
"typescript": "5.0.2",
|
||||||
"vite": "4.2.1",
|
"vite": "4.2.1",
|
||||||
"vite-plugin-inject-preload": "1.3.1",
|
"vite-plugin-inject-preload": "1.3.1",
|
||||||
"vite-plugin-pwa": "0.14.6",
|
"vite-plugin-pwa": "0.14.7",
|
||||||
"vite-svg-loader": "4.0.0",
|
"vite-svg-loader": "4.0.0",
|
||||||
"vitest": "0.29.7",
|
"vitest": "0.29.8",
|
||||||
"vue-tsc": "1.2.0",
|
"vue-tsc": "1.2.0",
|
||||||
"wait-on": "7.0.1",
|
"wait-on": "7.0.1",
|
||||||
"workbox-cli": "6.5.4"
|
"workbox-cli": "6.5.4"
|
||||||
|
|
5843
pnpm-lock.yaml
5843
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 18 18" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
|
||||||
|
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z" stroke-dasharray="60"></path>
|
||||||
|
<polyline points="1 9 7 14 15 4" stroke-dasharray="22" stroke-dashoffset="66"></polyline>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 420 B |
|
@ -0,0 +1,54 @@
|
||||||
|
<template>
|
||||||
|
<div class="base-checkbox" v-cy="'checkbox'">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:id="checkboxId"
|
||||||
|
class="is-sr-only"
|
||||||
|
:checked="modelValue"
|
||||||
|
@change="(event) => emit('update:modelValue', (event.target as HTMLInputElement).checked)"
|
||||||
|
:disabled="disabled || undefined"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<slot name="label" :checkboxId="checkboxId">
|
||||||
|
<label :for="checkboxId" class="base-checkbox__label">
|
||||||
|
<slot/>
|
||||||
|
</label>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {ref} from 'vue'
|
||||||
|
import {createRandomID} from '@/helpers/randomId'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'update:modelValue', value: boolean): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const checkboxId = ref(`fancycheckbox_${createRandomID()}`)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.base-checkbox__label {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-checkbox:has(input:disabled) .base-checkbox__label {
|
||||||
|
cursor:not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref} from 'vue'
|
||||||
|
import {logEvent} from 'histoire/client'
|
||||||
|
import FancyCheckbox from './fancycheckbox.vue'
|
||||||
|
|
||||||
|
const isDisabled = ref<boolean | undefined>()
|
||||||
|
|
||||||
|
const isChecked = ref(false)
|
||||||
|
|
||||||
|
const isCheckedInitiallyEnabled = ref(true)
|
||||||
|
|
||||||
|
const isCheckedDisabled = ref(false)
|
||||||
|
|
||||||
|
const withoutInitialState = ref<boolean | undefined>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Story :layout="{ type: 'grid', width: '200px' }">
|
||||||
|
<Variant title="Default">
|
||||||
|
<FancyCheckbox
|
||||||
|
v-model="isChecked"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
>
|
||||||
|
This is probably not important
|
||||||
|
</FancyCheckbox>
|
||||||
|
|
||||||
|
Visualisation
|
||||||
|
<input type="checkbox" v-model="isChecked">
|
||||||
|
{{ isChecked }}
|
||||||
|
</Variant>
|
||||||
|
<Variant title="Enabled Initially">
|
||||||
|
<FancyCheckbox
|
||||||
|
:disabled="isDisabled"
|
||||||
|
v-model="isCheckedInitiallyEnabled"
|
||||||
|
>
|
||||||
|
We want you to use this option
|
||||||
|
</FancyCheckbox>
|
||||||
|
|
||||||
|
Visualisation
|
||||||
|
<input type="checkbox" v-model="isCheckedInitiallyEnabled">
|
||||||
|
{{ isCheckedInitiallyEnabled }}
|
||||||
|
</Variant>
|
||||||
|
<Variant title="Disabled">
|
||||||
|
<FancyCheckbox
|
||||||
|
disabled
|
||||||
|
:modelValue="isCheckedDisabled"
|
||||||
|
@update:model-value="logEvent('Setting disabled: This should never happen', $event)"
|
||||||
|
>
|
||||||
|
You can't change this
|
||||||
|
</FancyCheckbox>
|
||||||
|
|
||||||
|
Visualisation
|
||||||
|
<input type="checkbox" v-model="isCheckedDisabled" disabled>
|
||||||
|
{{ isCheckedDisabled }}
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<Variant title="Undefined initial State">
|
||||||
|
<FancyCheckbox
|
||||||
|
v-model="withoutInitialState"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
>
|
||||||
|
Not sure what the value should be
|
||||||
|
</FancyCheckbox>
|
||||||
|
|
||||||
|
Visualisation
|
||||||
|
<input type="checkbox" v-model="withoutInitialState" disabled>
|
||||||
|
{{ withoutInitialState }}
|
||||||
|
</Variant>
|
||||||
|
</Story>
|
||||||
|
</template>
|
|
@ -1,66 +1,42 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{'is-disabled': disabled}" class="fancycheckbox">
|
<BaseCheckbox
|
||||||
<input
|
class="fancycheckbox"
|
||||||
:checked="checked"
|
:class="{
|
||||||
:disabled="disabled || undefined"
|
'is-disabled': disabled,
|
||||||
:id="checkBoxId"
|
'is-block': isBlock,
|
||||||
@change="(event: Event) => updateData((event.target as HTMLInputElement).checked)"
|
}"
|
||||||
type="checkbox"
|
:disabled="disabled"
|
||||||
/>
|
:model-value="modelValue"
|
||||||
<label :for="checkBoxId" class="check" @click.prevent="check">
|
@update:model-value="value => emit('update:modelValue', value)"
|
||||||
<svg height="18px" viewBox="0 0 18 18" width="18px">
|
>
|
||||||
<path
|
<CheckboxIcon class="fancycheckbox__icon" />
|
||||||
d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
|
<span v-if="$slots.default" class="fancycheckbox__content">
|
||||||
<polyline points="1 9 7 14 15 4"></polyline>
|
<slot/>
|
||||||
</svg>
|
</span>
|
||||||
<span>
|
</BaseCheckbox>
|
||||||
<slot></slot>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref, toRef, watch} from 'vue'
|
import CheckboxIcon from '@/assets/checkbox.svg?component'
|
||||||
|
|
||||||
import {createRandomID} from '@/helpers/randomId'
|
import BaseCheckbox from '@/components/base/BaseCheckbox.vue'
|
||||||
|
|
||||||
const checked = ref(false)
|
defineProps({
|
||||||
const checkBoxId = `fancycheckbox_${createRandomID()}`
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
},
|
||||||
|
isBlock: {
|
||||||
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['update:modelValue', 'change'])
|
|
||||||
|
|
||||||
const modelValue = toRef(props, 'modelValue')
|
const emit = defineEmits<{
|
||||||
|
(event: 'update:modelValue', value: boolean): void
|
||||||
watch(
|
}>()
|
||||||
modelValue,
|
|
||||||
newValue => {
|
|
||||||
checked.value = newValue
|
|
||||||
},
|
|
||||||
{immediate: true},
|
|
||||||
)
|
|
||||||
|
|
||||||
function updateData(newChecked: boolean) {
|
|
||||||
checked.value = newChecked
|
|
||||||
emit('update:modelValue', newChecked)
|
|
||||||
emit('change', newChecked)
|
|
||||||
}
|
|
||||||
|
|
||||||
function check() {
|
|
||||||
checked.value = !checked.value
|
|
||||||
updateData(checked.value)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,75 +46,54 @@ function check() {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
|
|
||||||
// FIXME: should be a prop
|
|
||||||
&.is-block {
|
&.is-block {
|
||||||
|
display: block;
|
||||||
margin: .5rem .2rem;
|
margin: .5rem .2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=checkbox] {
|
.fancycheckbox__content {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.check {
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
margin: auto;
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
padding-left: .5rem;
|
padding-left: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
.fancycheckbox__icon:deep() {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
fill: none;
|
stroke: var(--stroke-color, #c8ccd4);
|
||||||
stroke-linecap: round;
|
|
||||||
stroke-linejoin: round;
|
|
||||||
stroke: #c8ccd4;
|
|
||||||
stroke-width: 1.5;
|
|
||||||
transform: translate3d(0, 0, 0);
|
transform: translate3d(0, 0, 0);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
|
||||||
|
|
||||||
.check:hover svg {
|
path,
|
||||||
stroke: var(--primary);
|
polyline {
|
||||||
}
|
transition: all 0.2s linear, color 0.2s ease;
|
||||||
|
|
||||||
.is-disabled .check:hover svg {
|
|
||||||
stroke: #c8ccd4;
|
|
||||||
}
|
|
||||||
|
|
||||||
path {
|
|
||||||
stroke-dasharray: 60;
|
|
||||||
stroke-dashoffset: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
polyline {
|
|
||||||
stroke-dasharray: 22;
|
|
||||||
stroke-dashoffset: 66;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=checkbox]:checked + .check {
|
|
||||||
svg {
|
|
||||||
stroke: var(--primary);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fancycheckbox:not(:has(input:disabled)):hover .fancycheckbox__icon,
|
||||||
|
.fancycheckbox:has(input:checked) .fancycheckbox__icon {
|
||||||
|
--stroke-color: var(--primary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// Since css-has-pseudo doesn't work with deep classes,
|
||||||
|
// the following rules can't be scoped
|
||||||
|
|
||||||
|
.fancycheckbox:has(:not(input:checked)) .fancycheckbox__icon {
|
||||||
|
path {
|
||||||
|
transition-delay: 0.05s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fancycheckbox:has(input:checked) .fancycheckbox__icon {
|
||||||
path {
|
path {
|
||||||
stroke-dashoffset: 60;
|
stroke-dashoffset: 60;
|
||||||
transition: all 0.3s linear;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
polyline {
|
polyline {
|
||||||
stroke-dashoffset: 42;
|
stroke-dashoffset: 42;
|
||||||
transition: all 0.2s linear;
|
|
||||||
transition-delay: 0.15s;
|
transition-delay: 0.15s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
|
||||||
:to="taskDetailRoute"
|
|
||||||
:class="{'is-loading': taskService.loading}"
|
|
||||||
class="task loader-container"
|
|
||||||
>
|
|
||||||
<fancycheckbox
|
<fancycheckbox
|
||||||
:disabled="(isArchived || disabled) && !canMarkAsDone"
|
:disabled="(isArchived || disabled) && !canMarkAsDone"
|
||||||
@change="markAsDone"
|
@update:model-value="markAsDone"
|
||||||
v-model="task.done"
|
v-model="task.done"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -16,7 +12,8 @@
|
||||||
class="mr-1"
|
class="mr-1"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<router-link
|
||||||
|
:to="taskDetailRoute"
|
||||||
:class="{ 'done': task.done, 'show-project': showProject && project !== null}"
|
:class="{ 'done': task.done, 'show-project': showProject && project !== null}"
|
||||||
class="tasktext"
|
class="tasktext"
|
||||||
>
|
>
|
||||||
|
@ -96,7 +93,7 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<checklist-summary :task="task"/>
|
<checklist-summary :task="task"/>
|
||||||
</div>
|
</router-link>
|
||||||
|
|
||||||
<progress
|
<progress
|
||||||
class="progress is-small"
|
class="progress is-small"
|
||||||
|
@ -117,14 +114,14 @@
|
||||||
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
:class="{'is-favorite': task.isFavorite}"
|
:class="{'is-favorite': task.isFavorite}"
|
||||||
@click.prevent="toggleFavorite"
|
@click="toggleFavorite"
|
||||||
class="favorite"
|
class="favorite"
|
||||||
>
|
>
|
||||||
<icon icon="star" v-if="task.isFavorite"/>
|
<icon icon="star" v-if="task.isFavorite"/>
|
||||||
<icon :icon="['far', 'star']" v-else/>
|
<icon :icon="['far', 'star']" v-else/>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<slot />
|
<slot />
|
||||||
</router-link>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -288,11 +285,7 @@ function hideDeferDueDatePopup(e) {
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
|
|
||||||
color: var(--text);
|
|
||||||
transition: color ease $transition-duration;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--grey-900);
|
|
||||||
background-color: var(--grey-100);
|
background-color: var(--grey-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,6 +331,15 @@ function hideDeferDueDatePopup(e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text);
|
||||||
|
transition: color ease $transition-duration;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--grey-900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.favorite {
|
.favorite {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -6,13 +6,11 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cypressDirective: Directive = {
|
const cypressDirective = <Directive<HTMLElement,string>>{
|
||||||
mounted(el, {value}) {
|
mounted(el, {arg, value}) {
|
||||||
if (
|
const testingId = arg || value
|
||||||
(window.Cypress || import.meta.env.DEV) &&
|
if ((window.Cypress || import.meta.env.DEV) && testingId) {
|
||||||
value
|
el.setAttribute('data-cy', testingId)
|
||||||
) {
|
|
||||||
el.setAttribute('data-cy', value)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeUnmount(el) {
|
beforeUnmount(el) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { defineSetupVue3 } from '@histoire/plugin-vue'
|
import './polyfills'
|
||||||
|
import {defineSetupVue3} from '@histoire/plugin-vue'
|
||||||
import {i18n} from './i18n'
|
import {i18n} from './i18n'
|
||||||
|
|
||||||
// import './histoire.css' // Import global CSS
|
// import './histoire.css' // Import global CSS
|
||||||
|
@ -6,18 +7,21 @@ import './styles/global.scss'
|
||||||
|
|
||||||
import {createPinia} from 'pinia'
|
import {createPinia} from 'pinia'
|
||||||
|
|
||||||
|
import cypress from '@/directives/cypress'
|
||||||
|
|
||||||
import FontAwesomeIcon from '@/components/misc/Icon'
|
import FontAwesomeIcon from '@/components/misc/Icon'
|
||||||
import XButton from '@/components/input/button.vue'
|
import XButton from '@/components/input/button.vue'
|
||||||
import Modal from '@/components/misc/modal.vue'
|
import Modal from '@/components/misc/modal.vue'
|
||||||
import Card from '@/components/misc/card.vue'
|
import Card from '@/components/misc/card.vue'
|
||||||
|
|
||||||
|
|
||||||
export const setupVue3 = defineSetupVue3(({ app }) => {
|
export const setupVue3 = defineSetupVue3(({ app }) => {
|
||||||
// Add Pinia store
|
// Add Pinia store
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
|
|
||||||
|
app.directive('cy', cypress)
|
||||||
|
|
||||||
app.component('icon', FontAwesomeIcon)
|
app.component('icon', FontAwesomeIcon)
|
||||||
app.component('XButton', XButton)
|
app.component('XButton', XButton)
|
||||||
app.component('modal', Modal)
|
app.component('modal', Modal)
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const SUPPORTED_LOCALES = {
|
||||||
'pt-PT': 'Português',
|
'pt-PT': 'Português',
|
||||||
'zh-CN': 'Chinese',
|
'zh-CN': 'Chinese',
|
||||||
'no-NO': 'Norsk Bokmål',
|
'no-NO': 'Norsk Bokmål',
|
||||||
} as Record<string, string>
|
} as const
|
||||||
|
|
||||||
export type SupportedLocale = keyof typeof SUPPORTED_LOCALES
|
export type SupportedLocale = keyof typeof SUPPORTED_LOCALES
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@ export const DEFAULT_LANGUAGE: SupportedLocale= 'en'
|
||||||
|
|
||||||
export type ISOLanguage = string
|
export type ISOLanguage = string
|
||||||
|
|
||||||
// we load all messsages async
|
// we load all messages async
|
||||||
export const i18n = createI18n({
|
export const i18n = createI18n({
|
||||||
fallbackLocale: DEFAULT_LANGUAGE,
|
fallbackLocale: DEFAULT_LANGUAGE,
|
||||||
legacy: false,
|
legacy: false,
|
||||||
messages: {
|
messages: {
|
||||||
en: langEN,
|
[DEFAULT_LANGUAGE]: langEN,
|
||||||
} as Record<SupportedLocale, any>,
|
} as Record<SupportedLocale, any>,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -54,16 +54,16 @@ export async function setLanguage(lang: SupportedLocale = getCurrentLanguage()):
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentLanguage(): SupportedLocale {
|
export function getCurrentLanguage(): SupportedLocale {
|
||||||
const savedLanguage = localStorage.getItem('language')
|
const savedLanguage = localStorage.getItem('language') as SupportedLocale | null
|
||||||
if (savedLanguage !== null) {
|
if (savedLanguage !== null) {
|
||||||
return savedLanguage
|
return savedLanguage
|
||||||
}
|
}
|
||||||
|
|
||||||
const browserLanguage = navigator.language
|
const browserLanguage = navigator.language
|
||||||
|
|
||||||
const language: SupportedLocale | undefined = Object.keys(SUPPORTED_LOCALES).find(langKey => {
|
const language = Object.keys(SUPPORTED_LOCALES).find(langKey => {
|
||||||
return langKey === browserLanguage || langKey.startsWith(browserLanguage + '-')
|
return langKey === browserLanguage || langKey.startsWith(browserLanguage + '-')
|
||||||
})
|
}) as SupportedLocale | undefined
|
||||||
|
|
||||||
return language || DEFAULT_LANGUAGE
|
return language || DEFAULT_LANGUAGE
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// in order to use postcss-preset-env correctly we need some client side plugins
|
// in order to use postcss-preset-env correctly we need some client side plugins
|
||||||
import focusWithin from 'focus-within'
|
import focusWithinInit from 'postcss-focus-within/browser'
|
||||||
|
import cssHasPseudo from 'css-has-pseudo/browser'
|
||||||
|
|
||||||
focusWithin(document)
|
focusWithinInit()
|
||||||
|
cssHasPseudo(document)
|
|
@ -22,7 +22,7 @@
|
||||||
<x-button @click="setDefaultFilters">Reset</x-button>
|
<x-button @click="setDefaultFilters">Reset</x-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<fancycheckbox class="is-block" v-model="filters.showTasksWithoutDates">
|
<fancycheckbox is-block v-model="filters.showTasksWithoutDates">
|
||||||
{{ $t('project.gantt.showTasksWithoutDates') }}
|
{{ $t('project.gantt.showTasksWithoutDates') }}
|
||||||
</fancycheckbox>
|
</fancycheckbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -415,6 +415,7 @@ async function updateTaskPosition(e) {
|
||||||
: e.newIndex
|
: e.newIndex
|
||||||
|
|
||||||
const task = newBucket.tasks[newTaskIndex]
|
const task = newBucket.tasks[newTaskIndex]
|
||||||
|
const oldBucket = buckets.value.find(b => b.id === task.bucketId)
|
||||||
const taskBefore = newBucket.tasks[newTaskIndex - 1] ?? null
|
const taskBefore = newBucket.tasks[newTaskIndex - 1] ?? null
|
||||||
const taskAfter = newBucket.tasks[newTaskIndex + 1] ?? null
|
const taskAfter = newBucket.tasks[newTaskIndex + 1] ?? null
|
||||||
taskUpdating.value[task.id] = true
|
taskUpdating.value[task.id] = true
|
||||||
|
@ -425,6 +426,13 @@ async function updateTaskPosition(e) {
|
||||||
taskBefore !== null ? taskBefore.kanbanPosition : null,
|
taskBefore !== null ? taskBefore.kanbanPosition : null,
|
||||||
taskAfter !== null ? taskAfter.kanbanPosition : null,
|
taskAfter !== null ? taskAfter.kanbanPosition : null,
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe.
|
||||||
|
newBucket.id !== oldBucket.id &&
|
||||||
|
newBucket.isDoneBucket !== oldBucket.isDoneBucket
|
||||||
|
) {
|
||||||
|
newTask.done = newBucket.isDoneBucket
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await taskStore.update(newTask)
|
await taskStore.update(newTask)
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
</x-button>
|
</x-button>
|
||||||
</template>
|
</template>
|
||||||
</datepicker-with-range>
|
</datepicker-with-range>
|
||||||
<fancycheckbox @change="setShowNulls" class="mr-2">
|
<fancycheckbox @update:model-value="setShowNulls" class="mr-2">
|
||||||
{{ $t('task.show.noDates') }}
|
{{ $t('task.show.noDates') }}
|
||||||
</fancycheckbox>
|
</fancycheckbox>
|
||||||
<fancycheckbox @change="setShowOverdue">
|
<fancycheckbox @update:model-value="setShowOverdue">
|
||||||
{{ $t('task.show.overdue') }}
|
{{ $t('task.show.overdue') }}
|
||||||
</fancycheckbox>
|
</fancycheckbox>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -81,6 +81,7 @@ export default defineConfig(({mode}) => {
|
||||||
// See also './src/polyfills.ts'
|
// See also './src/polyfills.ts'
|
||||||
features: {
|
features: {
|
||||||
'focus-within-pseudo-class': true,
|
'focus-within-pseudo-class': true,
|
||||||
|
'has-pseudo-class': true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
Reference in New Issue