Skip to content
Merged
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
7 changes: 7 additions & 0 deletions models/card/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
type CreateCardExtension,
DOMAIN_CARD,
type FavoriteCard,
type FavoriteType,
type MasterTag,
type ParentInfo,
type Role,
Expand Down Expand Up @@ -171,6 +172,11 @@ export class TFavoriteCard extends TPreference implements FavoriteCard {
application!: string
}

@Model(card.class.FavoriteType, preference.class.Preference)
export class TFavoriteType extends TPreference implements FavoriteType {
declare attachedTo: Ref<MasterTag>
}

@Mixin(card.mixin.CreateCardExtension, card.class.MasterTag)
export class TCreateCardExtension extends TMasterTag implements CreateCardExtension {
component?: AnyComponent
Expand Down Expand Up @@ -347,6 +353,7 @@ export function createModel (builder: Builder): void {
TCardSection,
TCardViewDefaults,
TFavoriteCard,
TFavoriteType,
TCreateCardExtension
)

Expand Down
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Skrýt detaily",
"CardContent": "Co chcete sdílet?",
"Feed": "Kanál",
"AllCards": "Všechny karty"
"AllCards": "Všechny karty",
"Favorites": "Oblíbené"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Weniger anzeigen",
"CardContent": "Was möchten Sie teilen?",
"Feed": "Feed",
"AllCards": "Alle Karten"
"AllCards": "Alle Karten",
"Favorites": "Favoriten"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Show less",
"CardContent": "What do you want to share?",
"Feed": "Feed",
"AllCards": "All Cards"
"AllCards": "All Cards",
"Favorites": "Favorites"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Mostrar menos",
"CardContent": "¿Qué quieres compartir?",
"Feed": "Feed",
"AllCards": "Todas las Tarjetas"
"AllCards": "Todas las Tarjetas",
"Favorites": "Favoritos"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Afficher moins",
"CardContent": "Que voulez-vous partager ?",
"Feed": "Flux",
"AllCards": "Toutes les Cartes"
"AllCards": "Toutes les Cartes",
"Favorites": "Favoris"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Mostra meno",
"CardContent": "Cosa vuoi condividere?",
"Feed": "Feed",
"AllCards": "Tutte le Carte"
"AllCards": "Tutte le Carte",
"Favorites": "Preferiti"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "詳細を隠す",
"CardContent": "何を共有しますか?",
"Feed": "フィード",
"AllCards": "すべてのカード"
"AllCards": "すべてのカード",
"Favorites": "お気に入り"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Mostrar menos",
"CardContent": "O que você quer compartilhar?",
"Feed": "Feed",
"AllCards": "Todos os Cartões"
"AllCards": "Todos os Cartões",
"Favorites": "Favoritos"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Скрыть детали",
"CardContent": "Чем вы хотите поделиться?",
"Feed": "Лента",
"AllCards": "Все карты"
"AllCards": "Все карты",
"Favorites": "Избранное"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "Daha az göster",
"CardContent": "Ne paylaşmak istiyorsunuz?",
"Feed": "Akış",
"AllCards": "Tüm Kartlar"
"AllCards": "Tüm Kartlar",
"Favorites": "Favoriler"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"ShowLess": "收起详情",
"CardContent": "你想分享什么?",
"Feed": "动态",
"AllCards": "所有卡片"
"AllCards": "所有卡片",
"Favorites": "收藏夹"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { MasterTag } from '@hcengineering/card'
import { Class, Doc, Ref, Space } from '@hcengineering/core'
import { IconWithEmoji, getClient } from '@hcengineering/presentation'
import { NavItem, getCurrentLocation, navigate } from '@hcengineering/ui'
import { Action, ButtonIcon, NavItem, getCurrentLocation, navigate } from '@hcengineering/ui'
import view from '@hcengineering/view'
import card from '../../plugin'
Expand All @@ -27,6 +27,8 @@
export let _class: Ref<Class<Doc>> | undefined
export let level: number = 0
export let currentSpace: Ref<Space> | undefined
export let getItemActions: ((typeId: Ref<MasterTag>) => Action[]) | undefined = undefined
export let excludedClasses: Ref<MasterTag>[] = []
const client = getClient()
const dispatch = createEventDispatcher()
Expand All @@ -38,21 +40,26 @@
const desc = hierarchy.getDescendants(_class)
for (const clazz of desc) {
const cls = hierarchy.getClass(clazz) as MasterTag
if (cls.extends === _class && cls._class === card.class.MasterTag && cls.removed !== true) {
if (
cls.extends === _class &&
cls._class === card.class.MasterTag &&
cls.removed !== true &&
!excludedClasses.includes(cls._id)
) {
result.push(cls)
}
}
return result.sort((a, b) => a.label.localeCompare(b.label))
}
function fillDescendants (classes: MasterTag[]): void {
function fillDescendants (classes: MasterTag[], _excludedClasses: Ref<MasterTag>[]): void {
for (const cl of classes) {
descendants.set(cl._id, getDescendants(cl._id))
}
descendants = descendants
}
$: fillDescendants(allClasses)
$: fillDescendants(allClasses, excludedClasses)
function select (clazz: Ref<Class<Doc>>, space: Ref<Space>): void {
const loc = getCurrentLocation()
Expand Down Expand Up @@ -81,6 +88,23 @@
}
}}
>
<svelte:fragment slot="actions">
{#if getItemActions !== undefined}
{#each getItemActions(clazz._id) as action}
<ButtonIcon
icon={action.icon ?? view.icon.Edit}
size="extra-small"
kind="tertiary"
tooltip={{ label: action.label }}
on:click={(e) => {
e.stopPropagation()
e.preventDefault()
void action.action(action.props, e)
}}
/>
{/each}
{/if}
</svelte:fragment>
<svelte:fragment slot="dropbox">
{#if (descendants.get(clazz._id)?.length ?? 0) > 0}
<svelte:self
Expand All @@ -89,6 +113,8 @@
{currentSpace}
{_class}
{allClasses}
{getItemActions}
{excludedClasses}
level={level + 1}
on:select
/>
Expand Down
137 changes: 113 additions & 24 deletions plugins/card-resources/src/components/navigator/TypesNavigator.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,59 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, Doc, Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { getCurrentLocation, navigate, location as locationStore } from '@hcengineering/ui'
import { MasterTag } from '@hcengineering/card'
import core, { Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Action, getCurrentLocation, navigate, location as locationStore } from '@hcengineering/ui'
import { MasterTag, FavoriteType } from '@hcengineering/card'
import { TreeNode } from '@hcengineering/view-resources'
import { GroupsNavModel } from '@hcengineering/workbench'
import view from '@hcengineering/view'
import preference from '@hcengineering/preference'
import TagHierarchy from './TagHierarchy.svelte'
import card from '../../plugin'
export let model: GroupsNavModel
let classes: MasterTag[] = []
let _class: Ref<Class<Doc>> | undefined
let favoriteTypes: Ref<MasterTag>[] = []
let favorites = new Map<Ref<MasterTag>, FavoriteType>()
const query = createQuery()
query.query(card.class.MasterTag, {}, (res) => {
const client = getClient()
const typesQuery = createQuery()
const favoritesQuery = createQuery()
const me = getCurrentAccount()
typesQuery.query(card.class.MasterTag, {}, (res) => {
classes = res.filter((it) => it.removed !== true).sort((a, b) => a.label.localeCompare(b.label))
})
function getRootClasses (_classes: MasterTag[]): MasterTag[] {
return _classes.filter((it) => it.extends === card.class.Card)
}
function getFavoriteClasses (_classes: MasterTag[], _favoriteTypes: Ref<MasterTag>[]): MasterTag[] {
const hierarchy = client.getHierarchy()
const rootClasses = getRootClasses(_classes)
const rootFavorites = rootClasses.filter((it) => _favoriteTypes.includes(it._id))
const nonRootFavorites = _classes.filter(
(it) => _favoriteTypes.includes(it._id) && !rootClasses.some((root) => root._id === it._id)
)
// Add non-root favorites that aren't descendants of existing root favorites
const additionalFavorites = nonRootFavorites.filter((nonRoot) => {
return !rootFavorites.some((rootFav) => {
try {
return hierarchy.isDerived(nonRoot._id, rootFav._id)
} catch {
return false
}
})
})
return [...rootFavorites, ...additionalFavorites].sort((a, b) => a.label.localeCompare(b.label))
}
function buildTypePath (currentPath: any[], type: Ref<MasterTag>): any[] {
return [...currentPath.slice(0, 3), 'type', type]
}
Expand All @@ -46,29 +76,88 @@
navigate(loc)
}
favoritesQuery.query(card.class.FavoriteType, { createdBy: { $in: me.socialIds } }, (res) => {
favoriteTypes = res.map((item: FavoriteType) => item.attachedTo)
favorites = new Map(res.map((fav) => [fav.attachedTo, fav]))
})
function toggleFavoriteType (typeId: Ref<MasterTag>): void {
const favorite = favorites.get(typeId)
if (favorite !== undefined) {
void client.remove(favorite)
} else {
void client.createDoc(card.class.FavoriteType, core.space.Workspace, {
attachedTo: typeId
})
}
}
function getItemActions (typeId: Ref<MasterTag>): Action[] {
const favorite = favorites.get(typeId)
const isFavorite = favorite !== undefined
return [
{
id: 'toggle-favorite',
label: isFavorite ? preference.string.Unstar : preference.string.Star,
icon: view.icon.Star,
action: async (): Promise<void> => {
toggleFavoriteType(typeId)
}
}
]
}
$: _class = $locationStore.path[4] as Ref<Class<Doc>>
$: selectedClass = classes.find((it) => it._id === _class)
$: rootClasses = getRootClasses(classes)
$: favoriteRootClasses = getFavoriteClasses(classes, favoriteTypes)
$: nonFavoriteRootClasses = rootClasses.filter((it) => !favoriteTypes.includes(it._id))
$: empty = rootClasses === undefined || rootClasses.length === 0
</script>

<div class="flex-col w-full">
<TreeNode
_id={'tree-' + model.id}
label={model.label}
highlighted={selectedClass !== undefined}
isFold={!empty}
{empty}
>
<TagHierarchy
classes={rootClasses}
allClasses={classes}
{_class}
space={undefined}
currentSpace={undefined}
on:select={(e) => {
selectType(e.detail)
}}
/>
</TreeNode>
{#if favoriteRootClasses.length > 0}
<TreeNode
_id={'tree-favorites-' + model.id}
label={card.string.Favorites}
highlighted={selectedClass !== undefined && favoriteTypes.includes(selectedClass._id)}
isFold
empty={false}
>
<TagHierarchy
classes={favoriteRootClasses}
allClasses={classes}
{_class}
space={undefined}
currentSpace={undefined}
{getItemActions}
on:select={(e) => {
selectType(e.detail)
}}
/>
</TreeNode>
{/if}

{#if nonFavoriteRootClasses.length > 0}
<TreeNode
_id={'tree-' + model.id}
label={model.label}
highlighted={selectedClass !== undefined && !favoriteTypes.includes(selectedClass._id)}
isFold={!empty}
empty={false}
>
<TagHierarchy
classes={nonFavoriteRootClasses}
allClasses={classes}
{_class}
space={undefined}
currentSpace={undefined}
{getItemActions}
excludedClasses={favoriteTypes}
on:select={(e) => {
selectType(e.detail)
}}
/>
</TreeNode>
{/if}
</div>
Loading
Loading