Compare commits

..

85 Commits

Author SHA1 Message Date
733f0c1e19
fix: passing readonly projects data to navigation
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-01 11:24:22 +02:00
c915652e65
chore: move loader class 2023-04-01 11:24:22 +02:00
e9bc1e9253
chore: export favorite projects from store 2023-04-01 11:24:22 +02:00
c36103a5ca
chore: remove unnecessary map 2023-04-01 11:24:22 +02:00
1f8f13846a
chore: export not archived root projects 2023-04-01 11:24:22 +02:00
c629e2499d
fix: show favorite on hover 2023-04-01 11:24:22 +02:00
aa77f91b9c
fix: don't show > for top-level projects 2023-04-01 11:24:22 +02:00
6192746602
feat: allow selecting a parent project when editing a project 2023-04-01 11:24:22 +02:00
c092ae4043
feat: allow selecting a parent project when creating a project 2023-04-01 11:24:22 +02:00
99d5bfd898
feat: allow selecting a parent project when duplicating a project 2023-04-01 11:24:22 +02:00
e2a7f9aff5
feat: don't handle child projects and instead only save the ids 2023-04-01 11:24:21 +02:00
16a300d9c4
fix: make computed side-effect free 2023-04-01 11:24:21 +02:00
e2dd82a7e6
chore: refactor get parents project and move to projects store 2023-04-01 11:24:21 +02:00
fc72f47751
feat: show all parent projects in project search 2023-04-01 11:24:21 +02:00
e10d1a63e5
feat: show all parent projects in task detail view 2023-04-01 11:24:21 +02:00
f6e2648b71
fix: add await 2023-04-01 11:24:21 +02:00
a627960191
fix(filters): load projects after updating a filter 2023-04-01 11:24:21 +02:00
8ef2d8957e
fix(filters): load projects after deleting a filter 2023-04-01 11:24:21 +02:00
0732d6befc
fix(filters): load projects after creating a filter 2023-04-01 11:24:21 +02:00
4dba97c958
chore(task): move toggleFavorite to store 2023-04-01 11:24:21 +02:00
c1f244089d
feat(projects): move hasProjects check to store 2023-04-01 11:24:20 +02:00
6c46f5504a
feat: wrap projects navigation in a <Suspense> so that we can use top level await 2023-04-01 11:24:20 +02:00
5702781b6b
chore: use long variable name 2023-04-01 11:24:20 +02:00
d90458c17a
chore: rename archived message key 2023-04-01 11:24:20 +02:00
34238e29df
fix: use correct shortcut to open projects overview 2023-04-01 11:24:20 +02:00
9ce963ff75
fix: simplify sort 2023-04-01 11:24:20 +02:00
592e58adbd
chore: export projects as array directly from projects store 2023-04-01 11:24:20 +02:00
fea0aab9fa
chore: rename prop 2023-04-01 11:24:20 +02:00
fddcae71da
feat(tests): add project tests derived from old namespace tests 2023-04-01 11:24:20 +02:00
fe5f844baa
fix(projects): make sure the project hierarchy is properly updated when moving projects between parents 2023-04-01 11:24:20 +02:00
055d82f22a
feat(navigation): show favorite projects on top 2023-04-01 11:24:20 +02:00
89b9dfcb73
fix(navigation): make sure updating a project's state works for sub projects as well. 2023-04-01 11:24:19 +02:00
c7d81de2e7
fix(navigation): make marking a project as favorite work 2023-04-01 11:24:19 +02:00
dce448ca9a
fix(navigation): make sure the Favorites project shows up when marking or unmarking a task as favorite 2023-04-01 11:24:19 +02:00
4ec8f533d5
fix(navigation): favorites project 2023-04-01 11:24:19 +02:00
1db0a86752
fix(task detail view): make project display show the task's project 2023-04-01 11:24:19 +02:00
f2a4e5eb66
fix: make check if projects are available work again 2023-04-01 11:24:19 +02:00
6b971fc9ee
fix: cleanup unused translation strings 2023-04-01 11:24:19 +02:00
212041bef2
fix: make tests work again 2023-04-01 11:24:17 +02:00
3c38d912f7
chore: cleanup namespace leftovers 2023-04-01 11:24:12 +02:00
c9fa00f80a
fix(navigation): hide left ul border 2023-04-01 11:24:12 +02:00
b13a0c67b8
feat(navigation): make dragging a project under another project work 2023-04-01 11:24:12 +02:00
63e7b5f5a3
feat(navigation): allow dragging a project out from its parent project 2023-04-01 11:24:12 +02:00
9d041cf600
feat(navigation): make dragging a project to a parent work 2023-04-01 11:24:12 +02:00
6f5e5ac88f
fix(navigation): hover state of other menu items 2023-04-01 11:24:11 +02:00
c33a0778e8
feat(navigation): add hiding child projects 2023-04-01 11:24:11 +02:00
239c965b3c
feat: translate inbox project title 2023-04-01 11:24:11 +02:00
a6146f8ad5
chore: format 2023-04-01 11:24:11 +02:00
8366dda718
feat(navigation): correctly show child projects 2023-04-01 11:24:11 +02:00
e0b8116fe4
fix(navigation): make the styles work again 2023-04-01 11:24:11 +02:00
0d0e355a25
fix(navigation): watcher 2023-04-01 11:24:11 +02:00
5fe350327d
feat: rebuild main navigation so that it works recursively with projects 2023-04-01 11:24:11 +02:00
2dfac1063e
fix: remove namespace routes 2023-04-01 11:24:11 +02:00
95033662ae
fix: remove namespace store reference 2023-04-01 11:24:11 +02:00
f357ffc1f7
feat: remove all namespace leftovers 2023-04-01 11:24:10 +02:00
e9fff13c90
fix: route to create new project 2023-04-01 11:24:10 +02:00
c27226e40e
feat: move namespaces list to projects list 2023-04-01 11:24:10 +02:00
ed84651046 chore(deps): update dependency postcss-preset-env to v8.3.0
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-03-31 17:04:54 +00:00
7468ed21fa chore(deps): update dependency typescript to v5.0.3
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-03-30 21:05:23 +00:00
d8015913c3 fix(deps): update sentry-javascript monorepo to v7.46.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-30 14:04:41 +00:00
Frederick [Bot]
78789834f0 [skip ci] Updated translations via Crowdin 2023-03-30 00:06:17 +00:00
739fe0caa1
fix: move types to dev dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-29 22:23:40 +02:00
4703f9c4d5
fix: has-pseudo-class polyfill
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-29 17:22:06 +02:00
fd699ad777
fix: checkbox label size based on icon 2023-03-29 17:17:51 +02:00
0acf44778d
fix: undo further nesting of interactive items
This is really bad for UX and accessability
2023-03-29 17:17:50 +02:00
8fc254d2db
feat: abstract BaseCheckbox 2023-03-29 17:17:49 +02:00
Dominik Pschenitschni
7d3b97d422 feat: prepare for pnpm 8 (#3331)
Some checks failed
continuous-integration/drone/push Build is failing
Related: #3317

Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: #3331
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-03-29 12:02:34 +00:00
4a34f245db
chore(deps): update flake
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-29 13:41:52 +02:00
973ea39a64 chore(deps): update dependency eslint to v8.37.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-29 07:56:52 +00:00
f94a65ce7a chore(deps): update dependency @types/node to v18.15.11
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 22:05:12 +00:00
432fbbea78 chore(deps): update dependency cypress to v12.9.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 19:05:20 +00:00
e483f1cd2e chore(deps): update dependency vitest to v0.29.8
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 14:05:19 +00:00
eb34f6e136 chore(deps): update dependency postcss-preset-env to v8.2.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 12:05:42 +00:00
91e9eef582 fix: use strict comparison
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 10:49:34 +00:00
dea1789a00
feat: type i18n improvements
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 12:35:19 +02:00
30adad5ae6 feat: mark undone if task moved from isDoneBucket (#3291)
All checks were successful
continuous-integration/drone/push Build is passing
Addresses #545 (not completely)

Reviewed-on: #3291
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Reviewed-by: konrad <k@knt.li>
Co-authored-by: WofWca <wofwca@protonmail.com>
Co-committed-by: WofWca <wofwca@protonmail.com>
2023-03-28 10:21:19 +00:00
3ed6f939e5 chore(deps): update dependency vite-plugin-pwa to v0.14.7
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 10:05:08 +00:00
a337d22c1f fix(deps): update font awesome to v6.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-27 18:27:34 +00:00
addfcf2510 chore(deps): update typescript-eslint monorepo to v5.57.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-27 18:05:13 +00:00
303034f02c chore(deps): update pnpm to v7.30.5
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-27 14:04:54 +00:00
0fd44e9484 chore(deps): update dependency caniuse-lite to v1.0.30001470
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-27 06:06:31 +00:00
04040f20ba chore(deps): update dependency netlify-cli to v13.2.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-27 00:06:51 +00:00
6c999ad148 fix: ensure same protocol for configured api url (#3303)
All checks were successful
continuous-integration/drone/push Build is passing
Resolves https://github.com/go-vikunja/frontend/issues/109

Vikunja would save the api url with `http` instead of `https` when the frontend was accessed via https. This was fine in most cases when the server would redirect all requests made to http to the secure https variant. However, in newer Firefox versions (and soon, Chrome probably as well) the browser would not follow that redirect anymore. Hence, we need to make sure to only make api requests to the same protocol. Doing API requests from an https hosted fronted to an http hosted api would probably fail already anyway.

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: #3303
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-authored-by: konrad <k@knt.li>
Co-committed-by: konrad <k@knt.li>
2023-03-26 19:18:47 +00:00
cc519e6773 chore(deps): update dependency esbuild to v0.17.14
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-26 03:05:27 +00:00
f9dcae4f65 chore(deps): update dependency @types/node to v18.15.10
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-25 23:05:50 +00:00
22 changed files with 4251 additions and 3412 deletions

14
.npmrc
View File

@ -1,2 +1,14 @@
auto-install-peers=true
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
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

View File

@ -22,10 +22,10 @@ describe('Project View Table', () => {
cy.get('.project-table .filter-container .items .button')
.contains('Columns')
.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')
.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')
.click()

View File

@ -93,7 +93,7 @@ describe('Task', () => {
TaskFactory.create(1)
cy.visit('/projects/1/list')
cy.get('.tasks .task .fancycheckbox label.check')
cy.get('.tasks .task .fancycheckbox')
.first()
.click()
cy.get('.global-notification')

10
env.d.ts vendored
View File

@ -3,6 +3,16 @@
/// <reference types="cypress" />
/// <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 {
readonly VITE_IS_ONLINE: boolean
}

View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1664753041,
"narHash": "sha256-0ogaD8PaGHluARFeupofvk1Nq9gpVeZdlFM0Kcwguys=",
"lastModified": 1680030621,
"narHash": "sha256-qQa1NeS5Rvk2lgK5lSk986PC6I72yIHejzM8PFu+dHs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a62844b302507c7531ad68a86cb7aa54704c9cb4",
"rev": "402cc3633cc60dfc50378197305c984518b30773",
"type": "github"
},
"original": {

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@7.30.3",
"packageManager": "pnpm@7.30.5",
"keywords": [
"todo",
"productivity",
@ -45,19 +45,16 @@
"story:preview": "histoire preview"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "6.3.0",
"@fortawesome/free-regular-svg-icons": "6.3.0",
"@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-regular-svg-icons": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
"@fortawesome/vue-fontawesome": "3.0.3",
"@github/hotkey": "2.0.1",
"@infectoone/vue-ganttastic": "2.1.4",
"@intlify/unplugin-vue-i18n": "0.10.0",
"@kyvg/vue3-notification": "2.9.0",
"@sentry/tracing": "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",
"@sentry/tracing": "7.46.0",
"@sentry/vue": "7.46.0",
"@vueuse/core": "9.13.0",
"axios": "1.3.4",
"blurhash": "2.0.5",
@ -72,7 +69,6 @@
"flatpickr": "4.6.13",
"flexsearch": "0.7.31",
"floating-vue": "2.0.0-beta.20",
"focus-within": "3.0.2",
"highlight.js": "11.7.0",
"is-touch-device": "1.0.1",
"klona": "2.0.6",
@ -102,13 +98,14 @@
"@types/codemirror": "5.60.7",
"@types/dompurify": "3.0.0",
"@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/marked": "4.0.8",
"@types/node": "18.15.9",
"@types/node": "18.15.11",
"@types/postcss-preset-env": "7.7.0",
"@typescript-eslint/eslint-plugin": "5.56.0",
"@typescript-eslint/parser": "5.56.0",
"@types/sortablejs": "1.15.1",
"@typescript-eslint/eslint-plugin": "5.57.0",
"@typescript-eslint/parser": "5.57.0",
"@vitejs/plugin-legacy": "4.0.2",
"@vitejs/plugin-vue": "4.1.0",
"@vue/eslint-config-typescript": "11.0.2",
@ -116,29 +113,31 @@
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.14",
"browserslist": "4.21.5",
"caniuse-lite": "1.0.30001468",
"caniuse-lite": "1.0.30001470",
"css-has-pseudo": "5.0.2",
"csstype": "3.1.1",
"cypress": "12.8.1",
"esbuild": "0.17.13",
"eslint": "8.36.0",
"cypress": "12.9.0",
"esbuild": "0.17.14",
"eslint": "8.37.0",
"eslint-plugin-vue": "9.10.0",
"happy-dom": "8.9.0",
"histoire": "0.15.9",
"netlify-cli": "13.1.6",
"netlify-cli": "13.2.1",
"postcss": "8.4.21",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "3.0.1",
"postcss-preset-env": "8.1.0",
"postcss-focus-within": "7.0.2",
"postcss-preset-env": "8.3.0",
"rollup": "3.20.2",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.60.0",
"start-server-and-test": "2.0.0",
"typescript": "5.0.2",
"typescript": "5.0.3",
"vite": "4.2.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",
"vitest": "0.29.7",
"vitest": "0.29.8",
"vue-tsc": "1.2.0",
"wait-on": "7.0.1",
"workbox-cli": "6.5.4"

File diff suppressed because it is too large Load Diff

4
src/assets/checkbox.svg Normal file
View File

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

View File

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

View File

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

View File

@ -1,66 +1,42 @@
<template>
<div :class="{'is-disabled': disabled}" class="fancycheckbox">
<input
:checked="checked"
:disabled="disabled || undefined"
:id="checkBoxId"
@change="(event: Event) => updateData((event.target as HTMLInputElement).checked)"
type="checkbox"
/>
<label :for="checkBoxId" class="check" @click.prevent="check">
<svg height="18px" viewBox="0 0 18 18" width="18px">
<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"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
<span>
<slot></slot>
<BaseCheckbox
class="fancycheckbox"
:class="{
'is-disabled': disabled,
'is-block': isBlock,
}"
:disabled="disabled"
:model-value="modelValue"
@update:model-value="value => emit('update:modelValue', value)"
>
<CheckboxIcon class="fancycheckbox__icon" />
<span v-if="$slots.default" class="fancycheckbox__content">
<slot/>
</span>
</label>
</div>
</BaseCheckbox>
</template>
<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)
const checkBoxId = `fancycheckbox_${createRandomID()}`
const props = defineProps({
defineProps({
modelValue: {
type: Boolean,
required: false,
},
disabled: {
type: Boolean,
required: false,
},
isBlock: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:modelValue', 'change'])
const modelValue = toRef(props, 'modelValue')
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)
}
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void
}>()
</script>
@ -70,75 +46,54 @@ function check() {
padding-right: 5px;
padding-top: 3px;
// FIXME: should be a prop
&.is-block {
display: block;
margin: .5rem .2rem;
}
}
input[type=checkbox] {
display: none;
}
.check {
cursor: pointer;
position: relative;
margin: auto;
width: 18px;
height: 18px;
-webkit-tap-highlight-color: transparent;
transform: translate3d(0, 0, 0);
}
span {
.fancycheckbox__content {
font-size: 0.8rem;
vertical-align: top;
padding-left: .5rem;
}
svg {
.fancycheckbox__icon:deep() {
position: relative;
z-index: 1;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #c8ccd4;
stroke-width: 1.5;
stroke: var(--stroke-color, #c8ccd4);
transform: translate3d(0, 0, 0);
transition: all 0.2s ease;
}
.check:hover svg {
stroke: var(--primary);
}
.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);
path,
polyline {
transition: all 0.2s linear, color 0.2s ease;
}
}
.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 {
stroke-dashoffset: 60;
transition: all 0.3s linear;
}
polyline {
stroke-dashoffset: 42;
transition: all 0.2s linear;
transition-delay: 0.15s;
}
}

View File

@ -1,12 +1,8 @@
<template>
<router-link
:to="taskDetailRoute"
:class="{'is-loading': taskService.loading}"
class="task loader-container"
>
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
<fancycheckbox
:disabled="(isArchived || disabled) && !canMarkAsDone"
@change="markAsDone"
@update:model-value="markAsDone"
v-model="task.done"
/>
@ -16,7 +12,8 @@
class="mr-1"
/>
<div
<router-link
:to="taskDetailRoute"
:class="{ 'done': task.done, 'show-project': showProject && project !== null}"
class="tasktext"
>
@ -96,7 +93,7 @@
</span>
<checklist-summary :task="task"/>
</div>
</router-link>
<progress
class="progress is-small"
@ -117,14 +114,14 @@
<BaseButton
:class="{'is-favorite': task.isFavorite}"
@click.prevent="toggleFavorite"
@click="toggleFavorite"
class="favorite"
>
<icon icon="star" v-if="task.isFavorite"/>
<icon :icon="['far', 'star']" v-else/>
</BaseButton>
<slot />
</router-link>
</div>
</template>
<script setup lang="ts">
@ -284,11 +281,7 @@ function hideDeferDueDatePopup(e) {
border-radius: $radius;
border: 2px solid transparent;
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
background-color: var(--grey-100);
}
@ -334,6 +327,15 @@ function hideDeferDueDatePopup(e) {
}
a {
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
}
}
.favorite {
opacity: 1;
text-align: center;

View File

@ -6,13 +6,11 @@ declare global {
}
}
const cypressDirective: Directive = {
mounted(el, {value}) {
if (
(window.Cypress || import.meta.env.DEV) &&
value
) {
el.setAttribute('data-cy', value)
const cypressDirective = <Directive<HTMLElement,string>>{
mounted(el, {arg, value}) {
const testingId = arg || value
if ((window.Cypress || import.meta.env.DEV) && testingId) {
el.setAttribute('data-cy', testingId)
}
},
beforeUnmount(el) {

View File

@ -6,16 +6,16 @@ export const ERROR_NO_API_URL = 'noApiUrlProvided'
export const checkAndSetApiUrl = (url: string): Promise<string> => {
if(url.startsWith('/')) {
if (url.startsWith('/')) {
url = window.location.host + url
}
// Check if the url has an http prefix
// Check if the url has a http prefix
if (
!url.startsWith('http://') &&
!url.startsWith('https://')
) {
url = `http://${url}`
url = `${window.location.protocol}//${url}`
}
const urlToCheck: URL = new URL(url)
@ -41,15 +41,6 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
}
throw e
})
.catch(e => {
// Check if it has a port and if not check if it is reachable at https
if (urlToCheck.protocol === 'http:') {
urlToCheck.protocol = 'https:'
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
@ -66,7 +57,6 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
.catch(e => {
// Check if it is reachable at port API_DEFAULT_PORT and https
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'https:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return updateConfig()
@ -74,30 +64,7 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
throw e
})
.catch(e => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at port API_DEFAULT_PORT and http
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'http:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&

View File

@ -1,4 +1,5 @@
import { defineSetupVue3 } from '@histoire/plugin-vue'
import './polyfills'
import {defineSetupVue3} from '@histoire/plugin-vue'
import {i18n} from './i18n'
// import './histoire.css' // Import global CSS
@ -6,18 +7,21 @@ import './styles/global.scss'
import {createPinia} from 'pinia'
import cypress from '@/directives/cypress'
import FontAwesomeIcon from '@/components/misc/Icon'
import XButton from '@/components/input/button.vue'
import Modal from '@/components/misc/modal.vue'
import Card from '@/components/misc/card.vue'
export const setupVue3 = defineSetupVue3(({ app }) => {
// Add Pinia store
const pinia = createPinia()
app.use(pinia)
app.use(i18n)
app.directive('cy', cypress)
app.component('icon', FontAwesomeIcon)
app.component('XButton', XButton)
app.component('modal', Modal)

View File

@ -15,7 +15,7 @@ export const SUPPORTED_LOCALES = {
'pt-PT': 'Português',
'zh-CN': 'Chinese',
'no-NO': 'Norsk Bokmål',
} as Record<string, string>
} as const
export type SupportedLocale = keyof typeof SUPPORTED_LOCALES
@ -23,12 +23,12 @@ export const DEFAULT_LANGUAGE: SupportedLocale= 'en'
export type ISOLanguage = string
// we load all messsages async
// we load all messages async
export const i18n = createI18n({
fallbackLocale: DEFAULT_LANGUAGE,
legacy: false,
messages: {
en: langEN,
[DEFAULT_LANGUAGE]: langEN,
} as Record<SupportedLocale, any>,
})
@ -54,16 +54,16 @@ export async function setLanguage(lang: SupportedLocale = getCurrentLanguage()):
}
export function getCurrentLanguage(): SupportedLocale {
const savedLanguage = localStorage.getItem('language')
const savedLanguage = localStorage.getItem('language') as SupportedLocale | null
if (savedLanguage !== null) {
return savedLanguage
}
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 + '-')
})
}) as SupportedLocale | undefined
return language || DEFAULT_LANGUAGE
}

1056
src/i18n/lang/ja-JP.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
// 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)

View File

@ -22,7 +22,7 @@
<x-button @click="setDefaultFilters">Reset</x-button>
</div>
</div>
<fancycheckbox class="is-block" v-model="filters.showTasksWithoutDates">
<fancycheckbox is-block v-model="filters.showTasksWithoutDates">
{{ $t('project.gantt.showTasksWithoutDates') }}
</fancycheckbox>
</div>

View File

@ -415,6 +415,7 @@ async function updateTaskPosition(e) {
: e.newIndex
const task = newBucket.tasks[newTaskIndex]
const oldBucket = buckets.value.find(b => b.id === task.bucketId)
const taskBefore = newBucket.tasks[newTaskIndex - 1] ?? null
const taskAfter = newBucket.tasks[newTaskIndex + 1] ?? null
taskUpdating.value[task.id] = true
@ -425,6 +426,13 @@ async function updateTaskPosition(e) {
taskBefore !== null ? taskBefore.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 {
await taskStore.update(newTask)

View File

@ -11,10 +11,10 @@
</x-button>
</template>
</datepicker-with-range>
<fancycheckbox @change="setShowNulls" class="mr-2">
<fancycheckbox @update:model-value="setShowNulls" class="mr-2">
{{ $t('task.show.noDates') }}
</fancycheckbox>
<fancycheckbox @change="setShowOverdue">
<fancycheckbox @update:model-value="setShowOverdue">
{{ $t('task.show.overdue') }}
</fancycheckbox>
</p>

View File

@ -81,6 +81,7 @@ export default defineConfig(({mode}) => {
// See also './src/polyfills.ts'
features: {
'focus-within-pseudo-class': true,
'has-pseudo-class': true,
},
}),
],