Status filters in table view (#693)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov
2021-12-21 10:36:50 +01:00
committed by GitHub
parent f5aa8249dc
commit cb011b50af
18 changed files with 766 additions and 490 deletions
@@ -0,0 +1,155 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Ref, SortingOrder } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import task, { SpaceWithStates, State } from '@anticrm/task'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import StatesBarElement from './StatesBarElement.svelte'
export let space: Ref<SpaceWithStates>
export let state: Ref<State> | undefined = undefined
let states: State[] = []
let div: HTMLElement
let maskLeft: boolean = false
let maskRight: boolean = false
let mask: 'left' | 'right' | 'both' | 'none' = 'none'
const dispatch = createEventDispatcher()
const statesQuery = createQuery()
statesQuery.query(
task.class.State,
{ space },
(res) => {
states = res
},
{
sort: {
rank: SortingOrder.Ascending
}
}
)
const checkMask = (): void => {
maskLeft = !!(div && div.scrollLeft > 1)
maskRight = !!(div && div.scrollWidth - div.scrollLeft - div.clientWidth > 1)
if (maskLeft || maskRight) {
if (maskLeft && maskRight) mask = 'both'
else if (maskLeft) mask = 'left'
else if (maskRight) mask = 'right'
} else mask = 'none'
}
const selectItem = (ev: Event, item: State): void => {
const el: HTMLElement = ev.currentTarget as HTMLElement
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
if (state === item._id) {
state = undefined
} else {
state = item._id
}
dispatch('change')
}
onMount(() => {
if (div) {
checkMask()
div.addEventListener('scroll', checkMask)
}
})
onDestroy(() => {
if (div) div.removeEventListener('scroll', checkMask)
})
</script>
<div bind:this={div} class="flex-row-center statusesbar-container {mask}">
{#each states as item, i}
<div
class="flex-row-center cursor-pointer step-lr25"
class:selected={item._id === state}
on:click={(ev) => {
selectItem(ev, item)
}}
>
<StatesBarElement side={'left'} kind={i ? 'arrow' : 'round'} selected={item._id === state} color={item.color} />
<div
class="flex-row-center overflow-label label"
style={item._id === state ? `background-color: ${item.color};` : ''}
>
{item.title}
</div>
<StatesBarElement
side={'right'}
kind={i < states.length - 1 ? 'arrow' : 'round'}
selected={item._id === state}
color={item.color}
/>
</div>
{/each}
</div>
<style lang="scss">
.statusesbar-container {
overflow-x: auto;
padding: 0.125rem 0;
&::-webkit-scrollbar:horizontal {
height: 0.125rem;
}
&::-webkit-scrollbar-track {
margin: 0.25rem;
}
&::-webkit-scrollbar-thumb {
background-color: var(--theme-bg-accent-color);
}
}
.label {
padding: 0.5rem 2rem;
height: 2.25rem;
max-height: 2.25rem;
color: var(--theme-caption-color);
background-color: var(--theme-button-bg-enabled);
border-top: 1px solid var(--theme-button-border-enabled);
border-bottom: 1px solid var(--theme-button-border-enabled);
}
.selected {
.label {
border-color: transparent;
}
}
.left {
mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1rem);
}
.right {
mask-image: linear-gradient(to left, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1rem);
}
.both {
mask-image: linear-gradient(
to right,
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 1) 1rem,
rgba(0, 0, 0, 1) calc(100% - 1rem),
rgba(0, 0, 0, 0) 100%
);
}
.none {
mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 1);
}
</style>
@@ -0,0 +1,67 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
export let side: 'left' | 'right'
export let kind: 'round' | 'arrow'
export let selected: boolean = false
export let color: string
</script>
<svg width=".5rem" height="2.25rem" viewBox="0 0 8 36" class:selected xmlns="http://www.w3.org/2000/svg">
{#if side === 'left' && kind === 'arrow'}
<path
class="bg"
style={selected ? `fill: ${color}` : ''}
d="M1,0C0.3,0-0.2,0.7,0.1,1.4l6,15.9c0.2,0.5,0.2,1,0,1.4l-6,15.9C-0.2,35.3,0.3,36,1,36h7V0H1z"
/>
<path
class="border"
d="M1,35l6-15.9c0.3-0.7,0.3-1.4,0-2.1L1,1h7V0H1C0.7,0,0.4,0.2,0.2,0.4C0,0.7-0.1,1,0.1,1.4 l6,15.9c0.2,0.5,0.2,1,0,1.4l-6,15.9C-0.1,35,0,35.3,0.2,35.6S0.7,36,1,36h7v-1H1z"
/>
{:else if side === 'left' && kind === 'round'}
<path class="bg" style={selected ? `fill: ${color}` : ''} d="M0,8v10v10c0,4.4,3.6,8,8,8V0C3.6,0,0,3.6,0,8z" />
<path class="border" d="M1,28V8c0-3.9,3.1-7,7-7V0C3.6,0,0,3.6,0,8v20c0,4.4,3.6,8,8,8v-1C4.1,35,1,31.9,1,28z" />
{:else if side === 'right' && kind === 'arrow'}
<path
class="bg"
style={selected ? `fill: ${color}` : ''}
d="M1.8,1.3C1.5,0.5,0.8,0,0,0v36c0.8,0,1.5-0.5,1.8-1.3l6.1-16c0.2-0.5,0.2-1,0-1.4L1.8,1.3z"
/>
<path
class="border"
d="M1.8,1.3L1.8,1.3L1.8,1.3c0,0-0.1-0.2-0.1-0.3L1.6,0.8C1.2,0.3,0.6,0,0,0v1 c0.4,0,0.7,0.3,0.9,0.6l6.1,16c0.1,0.2,0.1,0.5,0,0.7l-6.1,16C0.7,34.7,0.4,35,0,35v1c0.8,0,1.5-0.5,1.8-1.3l6.1-16 c0.2-0.5,0.2-1,0-1.4L1.8,1.3z"
/>
{:else if side === 'right' && kind === 'round'}
<path class="bg" style={selected ? `fill: ${color}` : ''} d="M0,0v36c4.4,0,8-3.6,8-8V8C8,3.6,4.4,0,0,0z" />
<path class="border" d="M0,0v1c3.9,0,7,3.1,7,7v20c0,3.9-3.1,7-7,7v1c4.4,0,8-3.6,8-8V8C8,3.6,4.4,0,0,0z" />
{:else}
<rect x={0} y={0} width={'100%'} height={'100%'} fill={'red'} opacity={0.5} />
{/if}
</svg>
<style lang="scss">
.bg {
fill: var(--theme-button-bg-enabled);
}
.border {
fill: var(--theme-button-border-enabled);
}
.selected {
.border {
fill: transparent;
}
}
</style>
@@ -13,9 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { Ref } from '@anticrm/core'
import { Ref, SortingOrder } from '@anticrm/core'
import { createQuery } from '@anticrm/presentation'
import task, { SpaceWithStates, State } from '@anticrm/task'
import { createEventDispatcher } from 'svelte'
@@ -24,14 +23,29 @@
let states: State[] = []
const dispatch = createEventDispatcher()
const statesQuery = createQuery()
statesQuery.query(task.class.State, { space }, (res) => { states = res })
statesQuery.query(
task.class.State,
{ space },
(res) => {
states = res
},
{
sort: {
rank: SortingOrder.Ascending
}
}
)
</script>
<div class="flex-col statuses-container">
{#each states as state}
<div class="flex-row-center state" on:click={() => { dispatch('close', state) }}>
<div class="color" style="background-color: {state.color}"></div>
<div
class="flex-row-center state"
on:click={() => {
dispatch('close', state)
}}
>
<div class="color" style="background-color: {state.color}" />
{state.title}
</div>
{/each}
@@ -39,28 +53,30 @@
<style lang="scss">
.statuses-container {
padding: .5rem;
padding: 0.5rem;
max-height: 100%;
min-width: 10rem;
color: var(--theme-caption-color);
background-color: var(--theme-button-bg-focused);
border: 1px solid var(--theme-button-border-enabled);
border-radius: .75rem;
border-radius: 0.75rem;
user-select: none;
filter: drop-shadow(0 1.5rem 4rem rgba(0, 0, 0, .35));
filter: drop-shadow(0 1.5rem 4rem rgba(0, 0, 0, 0.35));
.state {
padding: .5rem;
border-radius: .5rem;
padding: 0.5rem;
border-radius: 0.5rem;
cursor: pointer;
&:hover { background-color: var(--theme-button-bg-hovered); }
&:hover {
background-color: var(--theme-button-bg-hovered);
}
.color {
margin-right: .75rem;
margin-right: 0.75rem;
width: 1rem;
height: 1rem;
border-radius: .25rem;
border-radius: 0.25rem;
}
}
}
</style>
</style>