Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ label.permalink=Permalink
label.listView=List
label.boardView=Board
label.ganttView=Plan
label.viewOptions=View options
label.cardsView=Cards
label.start.adding.tasks=Add Tasks

Expand Down
90 changes: 39 additions & 51 deletions webapps/src/main/webapp/skin/css/tasks.less
Original file line number Diff line number Diff line change
Expand Up @@ -1125,11 +1125,47 @@
}
.projectTasksDashboard {
min-height: calc(~"100vh - 117px")!important;
.projectTasksTabFilter {
flex: 0 1 70%;
.projectBoardToolbar {
/* Match the agenda header height/spacing (its application-toolbar is a
default v-toolbar) while staying a plain flex row, so the view-switcher
menu is not trapped in a v-toolbar stacking context. */
min-height: 64px;
margin-bottom: 4px;
/* Match the board columns' horizontal padding (px-3 = 12px) so the back
arrow lines up with the first column content and the right-hand icons
line up with the last column content. */
padding-inline: 12px;
/* Normalise the favorite/AI extension icons + their hover area to the
same size as the view / search / advanced-filter icons (36x36). */
.boardHeaderExtensions {
.v-icon {
font-size: 20px !important;
}
.v-btn {
width: 36px !important;
height: 36px !important;
/* Drop the components' own horizontal margins (e.g. the AI button's
me-2) so all the header icons are evenly spaced. */
margin-inline-start: 0 !important;
margin-inline-end: 0 !important;
}
}
.tasksViewSwitcher {
position: relative;
}
.tasksViewSwitcherMenu {
position: absolute;
top: calc(100% + 4px);
inset-inline-end: 0;
z-index: 1000;
min-width: 160px;
/* The kanban cards use 3D transforms (rotateY/backface) which put them
on compositing layers; promote the menu to its own layer too so it
composites above the board instead of being painted behind it. */
transform: translateZ(0);
}
}
.taskViewBreadcrumb {
flex: 0 1 30%;
a {
i {
color: @greyColorLighten1;
Expand All @@ -1142,43 +1178,6 @@
}
}
}
.tasksToolbar {
height: auto!important;
.v-toolbar__content {
height: auto!important;
padding: 0!important;
.taskDisplay {
.projectTasksViewTabs {
.v-tabs-bar {
height: auto!important;
.taskTabBoard {
border: 1px solid @borderColor;
border-radius: 5px 0 0 5px;
height: 38px;
min-height: 38px;
.uiIconBoard {
.far();
.fa-clipboard();
}
}
.taskTabList {
border-radius: 0;
}
.taskTabGantt {
border: 1px solid @borderColor;
border-radius: 0 5px 5px 0;
height: 38px;
min-height: 38px;
.uiIconGantt {
.fas();
.fa-stream();
}
}
}
}
}
}
}
.tasksView {
.projectTaskItem {
position: relative;
Expand Down Expand Up @@ -2536,17 +2535,6 @@
}
}
}
@media (max-width: 599px) {
.projectTasksDashboard {
.taskViewBreadcrumb {
flex: 0 1 80% !important;
}
.projectTasksTabFilter{
flex: 0 1 20% !important;
}
}
}

@media (max-width: 399px) {
.taskDrawer {
.drawerTitle .taskProjectName .projectName {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,26 @@
:ok-label="$t('label.ok')"
:cancel-label="$t('popup.cancel')"
@ok="deleteConfirm()" />
<div class="projectTasksWrapper d-flex justify-space-between">
<div class="taskViewBreadcrumb d-flex text-truncate pe-4 pt-1 pb-5">
<a
:title="project.name"
class="text-color text-truncate flex-shrink-1 flex-grow-0"
@click="hideProjectDetails()">
<i class="uiIcon uiBackIcon"></i>
<span>{{ project.name }}</span>
</a>
<extension-registry-components
:params="{
project,
}"
name="TaskProjectBoard"
type="task-board-header"
parent-element="div"
element="div"
class="d-flex align-center flex-shrink-0 ms-4" />
</div>
<div class="projectTasksTabFilter">
<tasks-view-toolbar
:project="project"
:status-list="statusList"
:task-card-tab-view="'#tasks-view-board'"
:task-list-tab-view="'#tasks-view-list'"
:task-gantt-tab-view="'#tasks-view-gantt'"
:show-completed-tasks="showCompletedTasks"
@keyword-changed="filterByKeyword"
@taskViewChangeTab="getChangeTabValue"
@filter-task-dashboard="filterTaskDashboard"
@reset-filter-task-dashboard="resetFiltertaskDashboard" />
</div>
</div>
<tasks-view-toolbar
:project="project"
:status-list="statusList"
:show-completed-tasks="showCompletedTasks"
@keyword-changed="filterByKeyword"
@taskViewChangeTab="getChangeTabValue"
@filter-task-dashboard="filterTaskDashboard"
@reset-filter-task-dashboard="resetFiltertaskDashboard">
<template #left>
<div class="taskViewBreadcrumb d-flex align-center text-truncate">
<a
:title="project.name"
class="text-color text-truncate flex-shrink-1 flex-grow-0"
@click="hideProjectDetails()">
<i class="uiIcon uiBackIcon"></i>
<span>{{ project.name }}</span>
</a>
</div>
</template>
</tasks-view-toolbar>
<div v-if="filterProjectActive && groupName && groupName.projectName" class="px-0 ">
<div
v-for="(projectItem,i) in groupName.projectName"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<!--
This file is part of the Meeds project (https://meeds.io/).
Copyright (C) 2022 Meeds Association
contact@meeds.io
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-->
<!-- Compact view selector (Board/List/Plan) styled like agenda's AgendaSwitchView.
The dropdown is moved to document.body and positioned with fixed coordinates
so it always renders above the kanban board (the board content otherwise
paints over an in-place dropdown regardless of z-index). -->
<template>
<div class="tasksViewSwitcher d-flex align-center">
<v-btn
:title="$t('label.viewOptions')"
small
min-height="36"
elevation="0"
class="px-0"
@click="toggle">
<v-icon
v-if="selectedOption"
:class="selectedOption.icon"
class="text-light-color"
size="20" />
<v-icon class="ps-2 text-light-color" size="12">fa-chevron-down</v-icon>
</v-btn>
<v-card
ref="menuCard"
v-show="menu"
class="tasksViewSwitcherMenu"
elevation="4">
<v-list class="pa-0" dense>
<v-list-item
v-for="item in viewOptions"
:key="item.value"
:class="item.value === view && 'background-grey-primary'"
dense
@click="select(item)">
<v-list-item-icon class="me-2 my-0 align-self-center">
<v-icon
:class="[item.icon, 'text-light-color']"
size="16" />
</v-list-item-icon>
<v-list-item-title>{{ item.label }}</v-list-item-title>
</v-list-item>
</v-list>
</v-card>
</div>
</template>
<script>
export default {
props: {
view: {
type: String,
default: 'board',
},
},
data: () => ({
menu: false,
cardEl: null,
}),
computed: {
viewOptions() {
return [
{ value: 'board', icon: 'far fa-clipboard', label: this.$t('label.boardView') },
{ value: 'list', icon: 'fas fa-list', label: this.$t('label.listView') },
{ value: 'gantt', icon: 'fas fa-stream', label: this.$t('label.ganttView') },
];
},
selectedOption() {
return this.viewOptions.find(o => o.value === this.view) || this.viewOptions[0];
},
},
mounted() {
// Relocate the dropdown high in the Vuetify app root so it escapes the
// board's stacking context but stays inside .v-application (keeping the
// theme background; a plain <body> child would render transparent).
this.cardEl = this.$refs.menuCard && this.$refs.menuCard.$el;
if (this.cardEl) {
this.cardEl.style.position = 'fixed';
this.cardEl.style.zIndex = '9999';
this.cardEl.style.minWidth = '160px';
const appRoot = document.querySelector('.v-application') || document.body;
appRoot.appendChild(this.cardEl);
}
document.addEventListener('click', this.onDocumentClick, true);
window.addEventListener('scroll', this.onScroll, true);
},
beforeDestroy() {
document.removeEventListener('click', this.onDocumentClick, true);
window.removeEventListener('scroll', this.onScroll, true);
if (this.cardEl && this.cardEl.parentNode) {
this.cardEl.parentNode.removeChild(this.cardEl);

Check warning on line 103 in webapps/src/main/webapp/vue-app/tasks-management/components/ProjectTasks/TasksViewSwitcher.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `childNode.remove()` over `parentNode.removeChild(childNode)`.

See more on https://sonarcloud.io/project/issues?id=io.meeds.task&issues=AZ8YVn1z8UEq8O3Qq5EI&open=AZ8YVn1z8UEq8O3Qq5EI&pullRequest=616
}
},
methods: {
toggle() {
this.menu = !this.menu;
if (this.menu) {
this.$nextTick(this.positionMenu);
}
},
positionMenu() {
if (!this.cardEl) {
return;
}
const rect = this.$el.getBoundingClientRect();
const width = this.cardEl.offsetWidth || 160;
this.cardEl.style.top = `${Math.round(rect.bottom + 4)}px`;
this.cardEl.style.left = `${Math.round(rect.right - width)}px`;
},
onScroll() {
if (this.menu) {
this.menu = false;
}
},
onDocumentClick(event) {
if (this.menu
&& !this.$el.contains(event.target)
&& !(this.cardEl && this.cardEl.contains(event.target))) {
this.menu = false;
}
},
select(item) {
this.menu = false;
if (item.value !== this.view) {
this.$emit('change', item.value);
}
},
},
};
</script>
Loading
Loading