mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-12 10:25:51 +00:00
183 lines
5.7 KiB
Svelte
183 lines
5.7 KiB
Svelte
<!--
|
|
// Copyright © 2025 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 { Card, CardSpace, MasterTag } from '@hcengineering/card'
|
|
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
|
import { createLabelsQuery, createNotificationContextsQuery, createQuery, getClient } from '@hcengineering/presentation'
|
|
import uiNext, { Button, ButtonVariant, NavItem } from '@hcengineering/ui-next'
|
|
import { createEventDispatcher } from 'svelte'
|
|
import { Label, NotificationContext } from '@hcengineering/communication-types'
|
|
|
|
import type { CardsNavigatorConfig } from '../../types'
|
|
import cardPlugin from '../../plugin'
|
|
import NavigatorType from './NavigatorType.svelte'
|
|
|
|
export let type: MasterTag
|
|
export let config: CardsNavigatorConfig
|
|
export let space: CardSpace | undefined = undefined
|
|
export let selectedType: Ref<MasterTag> | undefined = undefined
|
|
export let selectedCard: Ref<Card> | undefined = undefined
|
|
|
|
const dispatch = createEventDispatcher()
|
|
const client = getClient()
|
|
const hierarchy = client.getHierarchy()
|
|
|
|
const cardsQuery = createQuery()
|
|
const labelsQuery = createLabelsQuery()
|
|
const notificationContextsQuery = createNotificationContextsQuery()
|
|
|
|
let cards: WithLookup<Card>[] = []
|
|
let sortedCards: WithLookup<Card>[] = []
|
|
let total = -1
|
|
let contextByCard = new Map<Ref<Card>, NotificationContext>()
|
|
let labels: Label[] = []
|
|
let isLabelsLoaded = false
|
|
let isLoading: boolean = true
|
|
|
|
let sort: 'alphabetical' | 'recent' | undefined
|
|
$:sort = config.specialSorting?.[type._id] ?? config.defaultSorting ?? 'alphabetical'
|
|
|
|
let limit = config.limit
|
|
|
|
$: if ((config.labelFilter?.length ?? 0) > 0) {
|
|
labelsQuery.query(
|
|
{
|
|
label: config.labelFilter,
|
|
cardType: hierarchy.getDescendants(type._id),
|
|
limit: Math.max(limit, 100)
|
|
},
|
|
(res) => {
|
|
labels = res
|
|
isLabelsLoaded = true
|
|
}
|
|
)
|
|
} else {
|
|
labelsQuery.unsubscribe()
|
|
labels = []
|
|
isLabelsLoaded = true
|
|
}
|
|
|
|
$: ids = (config.labelFilter?.length ?? 0) > 0 ? labels.map((it) => it.card) : undefined
|
|
|
|
$: if (isLabelsLoaded) {
|
|
cardsQuery.query<Card>(
|
|
type._id,
|
|
{
|
|
...(ids === undefined ? {} : { _id: { $in: ids as Ref<Card>[] } }),
|
|
...(space !== undefined ? { space: space._id } : {})
|
|
},
|
|
(res) => {
|
|
cards = res
|
|
total = res.total
|
|
isLoading = false
|
|
},
|
|
{
|
|
total: true,
|
|
limit,
|
|
sort: sort === 'alphabetical' ? { title: SortingOrder.Ascending } : { modifiedOn: SortingOrder.Descending },
|
|
lookup: {
|
|
parent: cardPlugin.class.Card
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
$: if ((ids?.length ?? 0) > 0 || cards.length > 0) {
|
|
notificationContextsQuery.query(
|
|
{
|
|
card: ids ?? cards.map((it) => it._id),
|
|
notifications: {
|
|
order: SortingOrder.Descending,
|
|
read: false,
|
|
limit: 10
|
|
}
|
|
},
|
|
(res) => {
|
|
contextByCard = new Map(res.getResult().map((it) => [it.card, it]))
|
|
}
|
|
)
|
|
} else {
|
|
notificationContextsQuery.unsubscribe()
|
|
contextByCard = new Map()
|
|
}
|
|
|
|
$: empty = total === 0
|
|
|
|
$: sortedCards = sortCards(cards, contextByCard)
|
|
|
|
function sortCards (cards: Card[], contextByCard: Map<Ref<Card>, NotificationContext>): Card[] {
|
|
const sort = config.specialSorting?.[type._id] ?? config.defaultSorting ?? 'alphabetical'
|
|
if (sort === 'alphabetical') {
|
|
return cards.sort((a, b) => a.title.localeCompare(b.title))
|
|
} else if (sort === 'recent') {
|
|
return cards.sort((card1, card2) => {
|
|
const context1 = contextByCard.get(card1._id)
|
|
const context2 = contextByCard.get(card2._id)
|
|
const lastUpdate1 = context1?.lastUpdate.getTime() ?? 0
|
|
const lastUpdate2 = context2?.lastUpdate.getTime() ?? 0
|
|
|
|
return lastUpdate2 - lastUpdate1
|
|
})
|
|
}
|
|
|
|
return cards
|
|
}
|
|
</script>
|
|
|
|
{#if !isLoading}
|
|
{#if !empty || config.hideEmpty !== true || config.fixedTypes?.includes(type._id)}
|
|
<NavigatorType {type} {config} {space} {selectedType} {empty} bold on:selectType on:selectCard>
|
|
{#each sortedCards as card (card._id)}
|
|
{@const clazz = hierarchy.getClass(card._class)}
|
|
{@const context = contextByCard.get(card._id)}
|
|
<NavItem
|
|
label={card.title}
|
|
secondLabel={card.$lookup?.parent?.title}
|
|
icon={clazz.icon ?? cardPlugin.icon.Card}
|
|
selected={selectedCard === card._id}
|
|
paddingLeft="1.75rem"
|
|
notify={context && context.lastUpdate.getTime() > context.lastView.getTime()}
|
|
notificationsCount={context?.notifications?.length ?? 0}
|
|
on:click={(e) => {
|
|
e.stopPropagation()
|
|
e.preventDefault()
|
|
dispatch('selectCard', card)
|
|
}}
|
|
/>
|
|
{/each}
|
|
|
|
{#if total > cards.length}
|
|
<div class="all-button">
|
|
<Button
|
|
labelIntl={uiNext.string.All}
|
|
labelParams={{ count: total }}
|
|
variant={ButtonVariant.Ghost}
|
|
on:click={() => {
|
|
limit += config.limit
|
|
}}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</NavigatorType>
|
|
{/if}
|
|
{/if}
|
|
|
|
<style lang="scss">
|
|
.all-button {
|
|
margin-left: 1.75rem;
|
|
}
|
|
</style>
|