Compare commits

..

85 Commits

Author SHA1 Message Date
kolaente 733f0c1e19
fix: passing readonly projects data to navigation
continuous-integration/drone/pr Build is failing Details
2023-04-01 11:24:22 +02:00
kolaente c915652e65
chore: move loader class 2023-04-01 11:24:22 +02:00
kolaente e9bc1e9253
chore: export favorite projects from store 2023-04-01 11:24:22 +02:00
kolaente c36103a5ca
chore: remove unnecessary map 2023-04-01 11:24:22 +02:00
kolaente 1f8f13846a
chore: export not archived root projects 2023-04-01 11:24:22 +02:00
kolaente c629e2499d
fix: show favorite on hover 2023-04-01 11:24:22 +02:00
kolaente aa77f91b9c
fix: don't show > for top-level projects 2023-04-01 11:24:22 +02:00
kolaente 6192746602
feat: allow selecting a parent project when editing a project 2023-04-01 11:24:22 +02:00
kolaente c092ae4043
feat: allow selecting a parent project when creating a project 2023-04-01 11:24:22 +02:00
kolaente 99d5bfd898
feat: allow selecting a parent project when duplicating a project 2023-04-01 11:24:22 +02:00
kolaente e2a7f9aff5
feat: don't handle child projects and instead only save the ids 2023-04-01 11:24:21 +02:00
kolaente 16a300d9c4
fix: make computed side-effect free 2023-04-01 11:24:21 +02:00
kolaente e2dd82a7e6
chore: refactor get parents project and move to projects store 2023-04-01 11:24:21 +02:00
kolaente fc72f47751
feat: show all parent projects in project search 2023-04-01 11:24:21 +02:00
kolaente e10d1a63e5
feat: show all parent projects in task detail view 2023-04-01 11:24:21 +02:00
kolaente f6e2648b71
fix: add await 2023-04-01 11:24:21 +02:00
kolaente a627960191
fix(filters): load projects after updating a filter 2023-04-01 11:24:21 +02:00
kolaente 8ef2d8957e
fix(filters): load projects after deleting a filter 2023-04-01 11:24:21 +02:00
kolaente 0732d6befc
fix(filters): load projects after creating a filter 2023-04-01 11:24:21 +02:00
kolaente 4dba97c958
chore(task): move toggleFavorite to store 2023-04-01 11:24:21 +02:00
kolaente c1f244089d
feat(projects): move hasProjects check to store 2023-04-01 11:24:20 +02:00
kolaente 6c46f5504a
feat: wrap projects navigation in a <Suspense> so that we can use top level await 2023-04-01 11:24:20 +02:00
kolaente 5702781b6b
chore: use long variable name 2023-04-01 11:24:20 +02:00
kolaente d90458c17a
chore: rename archived message key 2023-04-01 11:24:20 +02:00
kolaente 34238e29df
fix: use correct shortcut to open projects overview 2023-04-01 11:24:20 +02:00
kolaente 9ce963ff75
fix: simplify sort 2023-04-01 11:24:20 +02:00
kolaente 592e58adbd
chore: export projects as array directly from projects store 2023-04-01 11:24:20 +02:00
kolaente fea0aab9fa
chore: rename prop 2023-04-01 11:24:20 +02:00
kolaente fddcae71da
feat(tests): add project tests derived from old namespace tests 2023-04-01 11:24:20 +02:00
kolaente fe5f844baa
fix(projects): make sure the project hierarchy is properly updated when moving projects between parents 2023-04-01 11:24:20 +02:00
kolaente 055d82f22a
feat(navigation): show favorite projects on top 2023-04-01 11:24:20 +02:00
kolaente 89b9dfcb73
fix(navigation): make sure updating a project's state works for sub projects as well. 2023-04-01 11:24:19 +02:00
kolaente c7d81de2e7
fix(navigation): make marking a project as favorite work 2023-04-01 11:24:19 +02:00
kolaente 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
kolaente 4ec8f533d5
fix(navigation): favorites project 2023-04-01 11:24:19 +02:00
kolaente 1db0a86752
fix(task detail view): make project display show the task's project 2023-04-01 11:24:19 +02:00
kolaente f2a4e5eb66
fix: make check if projects are available work again 2023-04-01 11:24:19 +02:00
kolaente 6b971fc9ee
fix: cleanup unused translation strings 2023-04-01 11:24:19 +02:00
kolaente 212041bef2
fix: make tests work again 2023-04-01 11:24:17 +02:00
kolaente 3c38d912f7
chore: cleanup namespace leftovers 2023-04-01 11:24:12 +02:00
kolaente c9fa00f80a
fix(navigation): hide left ul border 2023-04-01 11:24:12 +02:00
kolaente b13a0c67b8
feat(navigation): make dragging a project under another project work 2023-04-01 11:24:12 +02:00
kolaente 63e7b5f5a3
feat(navigation): allow dragging a project out from its parent project 2023-04-01 11:24:12 +02:00
kolaente 9d041cf600
feat(navigation): make dragging a project to a parent work 2023-04-01 11:24:12 +02:00
kolaente 6f5e5ac88f
fix(navigation): hover state of other menu items 2023-04-01 11:24:11 +02:00
kolaente c33a0778e8
feat(navigation): add hiding child projects 2023-04-01 11:24:11 +02:00
kolaente 239c965b3c
feat: translate inbox project title 2023-04-01 11:24:11 +02:00
kolaente a6146f8ad5
chore: format 2023-04-01 11:24:11 +02:00
kolaente 8366dda718
feat(navigation): correctly show child projects 2023-04-01 11:24:11 +02:00
kolaente e0b8116fe4
fix(navigation): make the styles work again 2023-04-01 11:24:11 +02:00
kolaente 0d0e355a25
fix(navigation): watcher 2023-04-01 11:24:11 +02:00
kolaente 5fe350327d
feat: rebuild main navigation so that it works recursively with projects 2023-04-01 11:24:11 +02:00
kolaente 2dfac1063e
fix: remove namespace routes 2023-04-01 11:24:11 +02:00
kolaente 95033662ae
fix: remove namespace store reference 2023-04-01 11:24:11 +02:00
kolaente f357ffc1f7
feat: remove all namespace leftovers 2023-04-01 11:24:10 +02:00
kolaente e9fff13c90
fix: route to create new project 2023-04-01 11:24:10 +02:00
kolaente c27226e40e
feat: move namespaces list to projects list 2023-04-01 11:24:10 +02:00
renovate ed84651046 chore(deps): update dependency postcss-preset-env to v8.3.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-03-31 17:04:54 +00:00
renovate 7468ed21fa chore(deps): update dependency typescript to v5.0.3
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details
2023-03-30 21:05:23 +00:00
renovate d8015913c3 fix(deps): update sentry-javascript monorepo to v7.46.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
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
Dominik Pschenitschni 739fe0caa1
fix: move types to dev dependencies
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-29 22:23:40 +02:00
Dominik Pschenitschni 4703f9c4d5
fix: has-pseudo-class polyfill
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-29 17:22:06 +02:00
Dominik Pschenitschni fd699ad777
fix: checkbox label size based on icon 2023-03-29 17:17:51 +02:00
Dominik Pschenitschni 0acf44778d
fix: undo further nesting of interactive items
This is really bad for UX and accessability
2023-03-29 17:17:50 +02:00
Dominik Pschenitschni 8fc254d2db
feat: abstract BaseCheckbox 2023-03-29 17:17:49 +02:00
Dominik Pschenitschni 7d3b97d422 feat: prepare for pnpm 8 (#3331)
continuous-integration/drone/push Build is failing Details
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
kolaente 4a34f245db
chore(deps): update flake
continuous-integration/drone/push Build is passing Details
2023-03-29 13:41:52 +02:00
renovate 973ea39a64 chore(deps): update dependency eslint to v8.37.0
continuous-integration/drone/push Build is passing Details
2023-03-29 07:56:52 +00:00
renovate f94a65ce7a chore(deps): update dependency @types/node to v18.15.11
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 22:05:12 +00:00
renovate 432fbbea78 chore(deps): update dependency cypress to v12.9.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 19:05:20 +00:00
renovate e483f1cd2e chore(deps): update dependency vitest to v0.29.8
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 14:05:19 +00:00
renovate eb34f6e136 chore(deps): update dependency postcss-preset-env to v8.2.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 12:05:42 +00:00
Dominik Pschenitschni 91e9eef582 fix: use strict comparison
continuous-integration/drone/push Build is passing Details
2023-03-28 10:49:34 +00:00
Dominik Pschenitschni dea1789a00
feat: type i18n improvements
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 12:35:19 +02:00
WofWca 30adad5ae6 feat: mark undone if task moved from isDoneBucket (#3291)
continuous-integration/drone/push Build is passing Details
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
renovate 3ed6f939e5 chore(deps): update dependency vite-plugin-pwa to v0.14.7
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-28 10:05:08 +00:00
renovate a337d22c1f fix(deps): update font awesome to v6.4.0
continuous-integration/drone/push Build is passing Details
2023-03-27 18:27:34 +00:00
renovate addfcf2510 chore(deps): update typescript-eslint monorepo to v5.57.0
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-27 18:05:13 +00:00
renovate 303034f02c chore(deps): update pnpm to v7.30.5
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-27 14:04:54 +00:00
renovate 0fd44e9484 chore(deps): update dependency caniuse-lite to v1.0.30001470
continuous-integration/drone/push Build is passing Details
2023-03-27 06:06:31 +00:00
renovate 04040f20ba chore(deps): update dependency netlify-cli to v13.2.1
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-27 00:06:51 +00:00
konrad 6c999ad148 fix: ensure same protocol for configured api url (#3303)
continuous-integration/drone/push Build is passing Details
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
renovate cc519e6773 chore(deps): update dependency esbuild to v0.17.14
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-26 03:05:27 +00:00
renovate f9dcae4f65 chore(deps): update dependency @types/node to v18.15.10
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
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 @@
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
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

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>
</span>
</label>
</div>
<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>
</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') &&
@ -118,7 +85,7 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
localStorage.setItem('API_URL', window.API_URL)
return window.API_URL
}
throw new Error(ERROR_NO_API_URL)
})
}

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