mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-24 02:25:00 +02:00
Tracker: View options - Grouping (#1442)
Signed-off-by: Artyom Grigorovich <grigorovichartyom@gmail.com>
This commit is contained in:
@@ -7,4 +7,8 @@
|
||||
export let currentSpace: Ref<Team>
|
||||
</script>
|
||||
|
||||
<Issues {currentSpace} categories={[IssueStatus.InProgress, IssueStatus.Todo]} title={tracker.string.ActiveIssues} />
|
||||
<Issues
|
||||
{currentSpace}
|
||||
includedGroups={{ status: [IssueStatus.InProgress, IssueStatus.Todo] }}
|
||||
title={tracker.string.ActiveIssues}
|
||||
/>
|
||||
|
||||
@@ -20,18 +20,60 @@
|
||||
import { eventToHTMLElement, showPopup, Tooltip } from '@anticrm/ui'
|
||||
|
||||
import tracker from '../../plugin'
|
||||
import { IntlString, translate } from '@anticrm/platform'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let value: WithLookup<Issue>
|
||||
export let currentSpace: Ref<Team> | undefined = undefined
|
||||
|
||||
$: employee = value?.$lookup?.assignee as Employee | undefined
|
||||
$: avatar = employee?.avatar
|
||||
$: formattedName = employee?.name ? formatName(employee.name) : ''
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
export let defaultName: IntlString | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let defaultNameString: string = ''
|
||||
let assignee: Employee | undefined = undefined
|
||||
|
||||
$: employee = (value?.$lookup?.assignee ?? assignee) as Employee | undefined
|
||||
$: avatar = employee?.avatar
|
||||
$: formattedName = employee?.name ? formatName(employee.name) : defaultNameString
|
||||
$: label = employee ? tracker.string.AssignedTo : tracker.string.AssignTo
|
||||
|
||||
$: findEmployeeById(value.assignee)
|
||||
$: getDefaultNameString = async () => {
|
||||
if (!defaultName) {
|
||||
return
|
||||
}
|
||||
|
||||
const result = await translate(defaultName, {})
|
||||
|
||||
if (!result) {
|
||||
return
|
||||
}
|
||||
|
||||
defaultNameString = result
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
getDefaultNameString()
|
||||
})
|
||||
|
||||
const findEmployeeById = async (id: Ref<Employee> | null) => {
|
||||
if (!id) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const current = await client.findOne(contact.class.Employee, { _id: id })
|
||||
|
||||
if (current === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
assignee = current
|
||||
}
|
||||
|
||||
const handleAssigneeChanged = async (result: Employee | null | undefined) => {
|
||||
if (result === undefined) {
|
||||
if (!isEditable || result === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -47,6 +89,9 @@
|
||||
}
|
||||
|
||||
const handleAssigneeEditorOpened = async (event: MouseEvent) => {
|
||||
if (!isEditable) {
|
||||
return
|
||||
}
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
@@ -61,10 +106,36 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Tooltip label={employee ? tracker.string.AssignedTo : tracker.string.AssignTo} props={{ value: formattedName }}>
|
||||
<div class="flex-presenter" on:click={handleAssigneeEditorOpened}>
|
||||
{#if isEditable}
|
||||
<Tooltip {label} props={{ value: formattedName }}>
|
||||
<div class="flex-presenter" on:click={handleAssigneeEditorOpened}>
|
||||
<div class="icon">
|
||||
<Avatar size={'x-small'} {avatar} />
|
||||
</div>
|
||||
{#if shouldShowLabel}
|
||||
<div class="label nowrap ml-2">
|
||||
{formattedName}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<div class="presenter">
|
||||
<div class="icon">
|
||||
<Avatar size={'x-small'} {avatar} />
|
||||
</div>
|
||||
{#if shouldShowLabel}
|
||||
<div class="label nowrap ml-2">
|
||||
{formattedName}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.presenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
export let currentSpace: Ref<Team>
|
||||
</script>
|
||||
|
||||
<Issues title={tracker.string.BacklogIssues} {currentSpace} categories={[IssueStatus.Backlog]} />
|
||||
<Issues title={tracker.string.BacklogIssues} {currentSpace} includedGroups={{ status: [IssueStatus.Backlog] }} />
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import { DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import { Button, eventToHTMLElement, Icon, IconAdd, Label, Scroller, showPopup, Tooltip } from '@anticrm/ui'
|
||||
import { Issue, Team } from '@anticrm/tracker'
|
||||
import { Component, Button, eventToHTMLElement, IconAdd, Scroller, showPopup, Tooltip } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { issueStatuses } from '../../utils'
|
||||
import { IssuesGroupByKeys, IssuesOrderByKeys, issuesGroupPresenterMap, issuesSortOrderMap } from '../../utils'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
import IssuesList from './IssuesList.svelte'
|
||||
|
||||
export let query: DocumentQuery<Issue>
|
||||
export let category: IssueStatus
|
||||
export let groupBy: { key: IssuesGroupByKeys | undefined; group: Issue[IssuesGroupByKeys] | undefined }
|
||||
export let orderBy: IssuesOrderByKeys
|
||||
export let currentSpace: Ref<Team> | undefined = undefined
|
||||
export let currentTeam: Team
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const options: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee
|
||||
@@ -24,28 +24,40 @@
|
||||
|
||||
let issuesAmount = 0
|
||||
|
||||
$: grouping = groupBy.key !== undefined && groupBy.group !== undefined ? { [groupBy.key]: groupBy.group } : {}
|
||||
$: headerComponent = groupBy.key !== undefined ? issuesGroupPresenterMap[groupBy.key] : null
|
||||
|
||||
const handleNewIssueAdded = (event: MouseEvent) => {
|
||||
if (!currentSpace) {
|
||||
return
|
||||
}
|
||||
|
||||
showPopup(CreateIssue, { space: currentSpace, issueStatus: category }, eventToHTMLElement(event))
|
||||
showPopup(CreateIssue, { space: currentSpace, ...grouping }, eventToHTMLElement(event))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="category" class:visible={issuesAmount > 0}>
|
||||
<div class="header categoryHeader flex-between label">
|
||||
<div class="flex-row-center gap-2">
|
||||
<Icon icon={issueStatuses[category].icon} size={'small'} />
|
||||
<span class="lines-limit-2"><Label label={issueStatuses[category].label} /></span>
|
||||
<span class="eLabelCounter ml-2">{issuesAmount}</span>
|
||||
{#if headerComponent}
|
||||
<div class="header categoryHeader flex-between label">
|
||||
<div class="flex-row-center gap-2">
|
||||
<Component
|
||||
is={headerComponent}
|
||||
props={{
|
||||
isEditable: false,
|
||||
shouldShowLabel: true,
|
||||
value: grouping,
|
||||
defaultName: groupBy.key === 'assignee' ? tracker.string.NoAssignee : undefined
|
||||
}}
|
||||
/>
|
||||
<span class="eLabelCounter ml-2">{issuesAmount}</span>
|
||||
</div>
|
||||
<div class="flex mr-1">
|
||||
<Tooltip label={tracker.string.AddIssueTooltip} direction={'left'}>
|
||||
<Button icon={IconAdd} kind={'transparent'} on:click={handleNewIssueAdded} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mr-1">
|
||||
<Tooltip label={tracker.string.AddIssueTooltip} direction={'left'}>
|
||||
<Button icon={IconAdd} kind={'transparent'} on:click={handleNewIssueAdded} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<Scroller>
|
||||
<IssuesList
|
||||
_class={tracker.class.Issue}
|
||||
@@ -60,8 +72,8 @@
|
||||
{ key: 'modifiedOn', presenter: tracker.component.ModificationDatePresenter },
|
||||
{ key: '', presenter: tracker.component.AssigneePresenter, props: { currentSpace } }
|
||||
]}
|
||||
{options}
|
||||
query={{ ...query, status: category }}
|
||||
options={{ ...options, sort: { [orderBy]: issuesSortOrderMap[orderBy] } }}
|
||||
query={{ ...query, ...grouping }}
|
||||
on:content={(evt) => {
|
||||
issuesAmount = evt.detail.length
|
||||
dispatch('content', issuesAmount)
|
||||
@@ -81,7 +93,7 @@
|
||||
.categoryHeader {
|
||||
height: 2.5rem;
|
||||
background-color: var(--theme-table-bg-hover);
|
||||
padding-left: 2rem;
|
||||
padding-left: 2.3rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
||||
@@ -13,59 +13,134 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import type { DocumentQuery, Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import { Label, ScrollBox } from '@anticrm/ui'
|
||||
import { Issue, Team, IssuesGrouping, IssuesOrdering } from '@anticrm/tracker'
|
||||
import { Button, Label, ScrollBox, IconOptions, showPopup, eventToHTMLElement } from '@anticrm/ui'
|
||||
import CategoryPresenter from './CategoryPresenter.svelte'
|
||||
import tracker from '../../plugin'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import ViewOptionsPopup from './ViewOptionsPopup.svelte'
|
||||
import { IssuesGroupByKeys, issuesGroupKeyMap, issuesOrderKeyMap } from '../../utils'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
export let categories = [
|
||||
IssueStatus.InProgress,
|
||||
IssueStatus.Todo,
|
||||
IssueStatus.Backlog,
|
||||
IssueStatus.Done,
|
||||
IssueStatus.Canceled
|
||||
]
|
||||
export let title: IntlString = tracker.string.AllIssues
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
export let search: string = ''
|
||||
export let groupingKey: IssuesGrouping = IssuesGrouping.Status
|
||||
export let orderingKey: IssuesOrdering = IssuesOrdering.LastUpdated
|
||||
export let includedGroups: Partial<Record<IssuesGroupByKeys, Array<any>>> = {}
|
||||
|
||||
const ENTRIES_LIMIT = 200
|
||||
const spaceQuery = createQuery()
|
||||
const issuesQuery = createQuery()
|
||||
const issuesMap: { [status: string]: number } = {}
|
||||
let currentTeam: Team | undefined
|
||||
let issues: Issue[] = []
|
||||
|
||||
$: getTotalIssues = () => {
|
||||
$: totalIssues = getTotalIssues(issuesMap)
|
||||
|
||||
$: resultQuery =
|
||||
search === ''
|
||||
? { space: currentSpace, ...includedIssuesQuery, ...query }
|
||||
: { $search: search, space: currentSpace, ...includedIssuesQuery, ...query }
|
||||
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
})
|
||||
|
||||
$: groupByKey = issuesGroupKeyMap[groupingKey]
|
||||
$: categories = getCategories(groupByKey, issues)
|
||||
$: displayedCategories = (categories as any[]).filter((x: ReturnType<typeof getCategories>) => {
|
||||
return (
|
||||
groupByKey === undefined || includedGroups[groupByKey] === undefined || includedGroups[groupByKey]?.includes(x)
|
||||
)
|
||||
})
|
||||
$: includedIssuesQuery = getIncludedIssues(includedGroups)
|
||||
|
||||
const getIncludedIssues = (groups: Partial<Record<IssuesGroupByKeys, Array<any>>>) => {
|
||||
const resultMap: { [p: string]: { $in: any[] } } = {}
|
||||
|
||||
for (const [key, value] of Object.entries(groups)) {
|
||||
resultMap[key] = { $in: value }
|
||||
}
|
||||
|
||||
return resultMap
|
||||
}
|
||||
|
||||
$: issuesQuery.query<Issue>(
|
||||
tracker.class.Issue,
|
||||
{ ...includedIssuesQuery },
|
||||
(result) => {
|
||||
issues = result
|
||||
},
|
||||
{ limit: ENTRIES_LIMIT, lookup: { assignee: contact.class.Employee } }
|
||||
)
|
||||
|
||||
const getCategories = (key: IssuesGroupByKeys | undefined, elements: Issue[]) => {
|
||||
if (!key) {
|
||||
return [undefined]
|
||||
}
|
||||
|
||||
return Array.from(
|
||||
new Set(
|
||||
elements.map((x) => {
|
||||
return x[key]
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const getTotalIssues = (map: { [status: string]: number }) => {
|
||||
let total = 0
|
||||
|
||||
for (const issuesAmount of Object.values(issuesMap)) {
|
||||
total += issuesAmount
|
||||
for (const amount of Object.values(map)) {
|
||||
total += amount
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
$: resultQuery =
|
||||
search === '' ? { space: currentSpace, ...query } : { $search: search, space: currentSpace, ...query }
|
||||
const handleOptionsUpdated = (result: { orderBy: IssuesOrdering; groupBy: IssuesGrouping } | undefined) => {
|
||||
if (result === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
let currentTeam: Team | undefined
|
||||
for (const prop of Object.getOwnPropertyNames(issuesMap)) {
|
||||
delete issuesMap[prop]
|
||||
}
|
||||
|
||||
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
|
||||
currentTeam = res.shift()
|
||||
})
|
||||
groupingKey = result.groupBy
|
||||
orderingKey = result.orderBy
|
||||
}
|
||||
|
||||
const handleOptionsEditorOpened = (event: MouseEvent) => {
|
||||
if (!currentSpace) {
|
||||
return
|
||||
}
|
||||
|
||||
showPopup(
|
||||
ViewOptionsPopup,
|
||||
{ groupBy: groupingKey, orderBy: orderingKey },
|
||||
eventToHTMLElement(event),
|
||||
undefined,
|
||||
handleOptionsUpdated
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if currentTeam}
|
||||
<ScrollBox vertical stretch>
|
||||
<div class="fs-title">
|
||||
<Label label={title} params={{ value: getTotalIssues() }} />
|
||||
<div class="fs-title flex-between mt-1 mr-1 ml-1">
|
||||
<Label label={title} params={{ value: totalIssues }} />
|
||||
<Button icon={IconOptions} kind={'link'} on:click={handleOptionsEditorOpened} />
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
{#each categories as category}
|
||||
{#each displayedCategories as category}
|
||||
<CategoryPresenter
|
||||
{category}
|
||||
groupBy={{ key: groupByKey, group: category }}
|
||||
orderBy={issuesOrderKeyMap[orderingKey]}
|
||||
query={resultQuery}
|
||||
{currentSpace}
|
||||
{currentTeam}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, DocumentQuery, FindOptions, Ref, getObjectValue } from '@anticrm/core'
|
||||
import { SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CheckBox, Loading, showPopup, Spinner, IconMoreV, Tooltip } from '@anticrm/ui'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
@@ -36,7 +35,6 @@
|
||||
|
||||
const DOCS_MAX_AMOUNT = 200
|
||||
const liveQuery = createQuery()
|
||||
const sort = { modifiedOn: SortingOrder.Descending }
|
||||
|
||||
let selectedIssueIds = new Set<Ref<Doc>>()
|
||||
let selectedRowIndex: number | undefined
|
||||
@@ -61,7 +59,7 @@
|
||||
dispatch('content', docObjects)
|
||||
isLoading = false
|
||||
},
|
||||
{ sort, ...options, limit: DOCS_MAX_AMOUNT }
|
||||
{ ...options, limit: DOCS_MAX_AMOUNT }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -239,9 +237,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.03rem;
|
||||
border-radius: 0.25rem;
|
||||
background-color: rgba(247, 248, 248, 0.5);
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
|
||||
export let value: Issue
|
||||
export let currentSpace: Ref<Team> | undefined = undefined
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handlePriorityChanged = async (newPriority: IssuePriority | undefined) => {
|
||||
if (newPriority === undefined) {
|
||||
if (!isEditable || newPriority === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -41,12 +43,22 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SetPriority}>
|
||||
{#if isEditable}
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SetPriority}>
|
||||
<PrioritySelector
|
||||
kind={'icon'}
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
priority={value.priority}
|
||||
onPriorityChange={handlePriorityChanged}
|
||||
/>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<PrioritySelector
|
||||
kind={'icon'}
|
||||
shouldShowLabel={false}
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
priority={value.priority}
|
||||
onPriorityChange={handlePriorityChanged}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
|
||||
export let value: Issue
|
||||
export let currentSpace: Ref<Team> | undefined = undefined
|
||||
export let isEditable: boolean = true
|
||||
export let shouldShowLabel: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const handleStatusChanged = async (newStatus: IssueStatus | undefined) => {
|
||||
if (newStatus === undefined) {
|
||||
if (!isEditable || newStatus === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -41,7 +43,23 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SetStatus}>
|
||||
<StatusSelector kind={'icon'} shouldShowLabel={false} status={value.status} onStatusChange={handleStatusChanged} />
|
||||
</Tooltip>
|
||||
{#if isEditable}
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SetStatus}>
|
||||
<StatusSelector
|
||||
kind={'icon'}
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
status={value.status}
|
||||
onStatusChange={handleStatusChanged}
|
||||
/>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<StatusSelector
|
||||
kind={'icon'}
|
||||
{isEditable}
|
||||
{shouldShowLabel}
|
||||
status={value.status}
|
||||
onStatusChange={handleStatusChanged}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<!--
|
||||
// Copyright © 2022 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 { IssuesGrouping, IssuesOrdering } from '@anticrm/tracker'
|
||||
import { Label } from '@anticrm/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { issuesGroupByOptions, issuesOrderByOptions } from '../../utils'
|
||||
import DropdownNative from '../DropdownNative.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let groupBy: IssuesGrouping | undefined = undefined
|
||||
export let orderBy: IssuesOrdering | undefined = undefined
|
||||
|
||||
const groupByItems = issuesGroupByOptions
|
||||
const orderByItems = issuesOrderByOptions
|
||||
|
||||
$: dispatch('update', { groupBy, orderBy })
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="sortingContainer">
|
||||
<div class="viewOption">
|
||||
<div class="label">
|
||||
<Label label={tracker.string.Grouping} />
|
||||
</div>
|
||||
<div class="dropdownContainer">
|
||||
<DropdownNative items={groupByItems} bind:selected={groupBy} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="viewOption">
|
||||
<div class="label">
|
||||
<Label label={tracker.string.Ordering} />
|
||||
</div>
|
||||
<div class="dropdownContainer">
|
||||
<DropdownNative items={orderByItems} bind:selected={orderBy} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 17rem;
|
||||
background-color: var(--board-card-bg-color);
|
||||
}
|
||||
|
||||
.sortingContainer {
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: 1px solid var(--popup-divider);
|
||||
}
|
||||
|
||||
.viewOption {
|
||||
display: flex;
|
||||
min-height: 2rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 5rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
}
|
||||
|
||||
.dropdownContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user