Skip to content

Commit 206880f

Browse files
Use navigation by types instead of spaces (#10083)
1 parent 1fa5105 commit 206880f

File tree

11 files changed

+153
-21
lines changed

11 files changed

+153
-21
lines changed

models/card/src/index.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -447,15 +447,24 @@ export function createModel (builder: Builder): void {
447447
position: 'top'
448448
}
449449
],
450-
spaces: [
450+
spaces: [],
451+
groups: [
451452
{
452-
id: 'spaces',
453-
label: core.string.Spaces,
454-
spaceClass: card.class.CardSpace,
455-
addSpaceLabel: core.string.Space,
456-
icon: card.icon.Space,
457-
// intentionally left empty in order to make space presenter working
458-
specials: []
453+
id: 'types',
454+
label: card.string.MasterTags,
455+
groupByClass: card.class.MasterTag,
456+
icon: card.icon.MasterTags,
457+
component: card.component.TypesNavigator,
458+
specials: [
459+
{
460+
id: 'type',
461+
label: card.string.Cards,
462+
component: card.component.Main,
463+
componentProps: {
464+
defaultViewletDescriptor: card.viewlet.CardFeedDescriptor
465+
}
466+
}
467+
]
459468
}
460469
]
461470
},
@@ -658,7 +667,7 @@ export function createModel (builder: Builder): void {
658667
)
659668

660669
builder.mixin(card.class.Card, core.class.Class, view.mixin.ClassFilters, {
661-
filters: [],
670+
filters: ['space'],
662671
ignoreKeys: ['parent']
663672
})
664673

plugins/card-resources/src/components/CardFeedView.svelte

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
}
6464
}
6565
)
66+
$: cardSpace = space ?? getSpaceFromFilter(resultQuery)
6667
6768
$: hasNextPage = total > cards.length
6869
@@ -76,6 +77,16 @@
7677
}
7778
}
7879
80+
function getSpaceFromFilter (_query: DocumentQuery<Card>): Ref<CardSpace> | undefined {
81+
if (_query.space != null) {
82+
if (typeof _query.space === 'object' && '$in' in _query.space && Array.isArray(_query.space.$in)) {
83+
return _query.space.$in[0] as Ref<CardSpace>
84+
}
85+
return _query.space as Ref<CardSpace>
86+
}
87+
return undefined
88+
}
89+
7990
function getFormatDateId (timestamp: number): string {
8091
const now = new Date()
8192
const date = new Date(timestamp)
@@ -110,7 +121,7 @@
110121

111122
<Scroller bind:divScroll {onScroll} padding="2rem 4rem">
112123
<div class="home">
113-
<NewCardForm type={_class !== card.class.Card ? _class : undefined} {space} />
124+
<NewCardForm type={_class !== card.class.Card ? _class : undefined} space={cardSpace} />
114125
<div class="body flex-gap-2">
115126
{#each cards as card, index}
116127
{@const previousCard = cards[index - 1]}

plugins/card-resources/src/components/Main.svelte

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,20 @@
1414
-->
1515
<script lang="ts">
1616
import { MasterTag } from '@hcengineering/card'
17-
import { Class, Doc, Ref, Space } from '@hcengineering/core'
17+
import { Class, Doc, Ref } from '@hcengineering/core'
1818
import { IntlString } from '@hcengineering/platform'
1919
import { createQuery } from '@hcengineering/presentation'
2020
import { location } from '@hcengineering/ui'
2121
import { SpecialView } from '@hcengineering/workbench-resources'
2222
import { onDestroy } from 'svelte'
2323
import card from '../plugin'
2424
25-
export let currentSpace: Ref<Space>
26-
2725
let _class: Ref<Class<Doc>> | undefined
2826
2927
onDestroy(
3028
location.subscribe((loc) => {
31-
_class = loc.path[4]
29+
const isTypeSpecified = loc.path[3] === 'type'
30+
_class = isTypeSpecified ? loc.path[4] : card.class.Card
3231
})
3332
)
3433
@@ -51,8 +50,6 @@
5150
{#if clazz !== undefined && label !== undefined}
5251
<SpecialView
5352
_class={clazz._id}
54-
baseQuery={{ space: currentSpace }}
55-
space={currentSpace}
5653
defaultViewletDescriptor={card.viewlet.CardFeedDescriptor}
5754
{label}
5855
icon={card.icon.Card}

plugins/card-resources/src/components/navigator/TagHierarchy.svelte

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,23 @@
1313
// limitations under the License.
1414
-->
1515
<script lang="ts">
16+
import { createEventDispatcher } from 'svelte'
1617
import { MasterTag } from '@hcengineering/card'
1718
import { Class, Doc, Ref, Space } from '@hcengineering/core'
1819
import { IconWithEmoji, getClient } from '@hcengineering/presentation'
1920
import { NavItem, getCurrentLocation, navigate } from '@hcengineering/ui'
20-
import card from '../../plugin'
2121
import view from '@hcengineering/view'
22+
import card from '../../plugin'
2223
23-
export let space: Ref<Space>
24+
export let space: Ref<Space> | undefined
2425
export let classes: MasterTag[] = []
2526
export let allClasses: MasterTag[] = []
2627
export let _class: Ref<Class<Doc>> | undefined
2728
export let level: number = 0
2829
export let currentSpace: Ref<Space> | undefined
2930
3031
const client = getClient()
32+
const dispatch = createEventDispatcher()
3133
let descendants = new Map<Ref<Class<Doc>>, MasterTag[]>()
3234
3335
function getDescendants (_class: Ref<MasterTag>): MasterTag[] {
@@ -72,7 +74,11 @@
7274
{level}
7375
selected={clazz._id === _class && currentSpace === space}
7476
on:click={() => {
75-
select(clazz._id, space)
77+
if (space !== undefined) {
78+
select(clazz._id, space)
79+
} else {
80+
dispatch('select', clazz._id)
81+
}
7682
}}
7783
>
7884
<svelte:fragment slot="dropbox">
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<!--
2+
// Copyright © 2025 Hardcore Engineering Inc.
3+
//
4+
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License. You may
6+
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
//
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
-->
15+
<script lang="ts">
16+
import { Class, Doc, Ref } from '@hcengineering/core'
17+
import { createQuery } from '@hcengineering/presentation'
18+
import { getCurrentLocation, navigate, location as locationStore } from '@hcengineering/ui'
19+
import { MasterTag } from '@hcengineering/card'
20+
import { TreeNode } from '@hcengineering/view-resources'
21+
import { GroupsNavModel } from '@hcengineering/workbench'
22+
import TagHierarchy from './TagHierarchy.svelte'
23+
import card from '../../plugin'
24+
25+
export let model: GroupsNavModel
26+
27+
let classes: MasterTag[] = []
28+
let _class: Ref<Class<Doc>> | undefined
29+
30+
const query = createQuery()
31+
query.query(card.class.MasterTag, {}, (res) => {
32+
classes = res.filter((it) => it.removed !== true).sort((a, b) => a.label.localeCompare(b.label))
33+
})
34+
35+
function getRootClasses (_classes: MasterTag[]): MasterTag[] {
36+
return _classes.filter((it) => it.extends === card.class.Card)
37+
}
38+
39+
function buildTypePath (currentPath: any[], type: Ref<MasterTag>): any[] {
40+
return [...currentPath.slice(0, 3), 'type', type]
41+
}
42+
43+
function selectType (type: Ref<MasterTag>): void {
44+
const loc = getCurrentLocation()
45+
loc.path = buildTypePath(loc.path, type)
46+
navigate(loc)
47+
}
48+
49+
$: _class = $locationStore.path[4] as Ref<Class<Doc>>
50+
$: selectedClass = classes.find((it) => it._id === _class)
51+
$: rootClasses = getRootClasses(classes)
52+
$: empty = rootClasses === undefined || rootClasses.length === 0
53+
</script>
54+
55+
<div class="flex-col w-full">
56+
<TreeNode
57+
_id={'tree-' + model.id}
58+
label={model.label}
59+
highlighted={selectedClass !== undefined}
60+
isFold={!empty}
61+
{empty}
62+
>
63+
<TagHierarchy
64+
classes={rootClasses}
65+
allClasses={classes}
66+
{_class}
67+
space={undefined}
68+
currentSpace={undefined}
69+
on:select={(e) => {
70+
selectType(e.detail)
71+
}}
72+
/>
73+
</TreeNode>
74+
</div>

plugins/card-resources/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import CreateCardButton from './components/CreateCardButton.svelte'
5353
import CardArrayEditor from './components/CardArrayEditor.svelte'
5454
import NewCardHeader from './components/navigator/NewCardHeader.svelte'
5555
import SpacePresenter from './components/navigator/SpacePresenter.svelte'
56+
import TypesNavigator from './components/navigator/TypesNavigator.svelte'
5657
import LabelsPresenter from './components/LabelsPresenter.svelte'
5758
import RolesSection from './components/settings/RolesSection.svelte'
5859
import EditRole from './components/settings/EditRole.svelte'
@@ -111,6 +112,7 @@ export default async (): Promise<Resources> => ({
111112
CardArrayEditor,
112113
NewCardHeader,
113114
SpacePresenter,
115+
TypesNavigator,
114116
LabelsPresenter,
115117
RolesSection,
116118
EditRole,

plugins/card-resources/src/plugin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default mergeIds(cardId, card, {
4646
CreateCardButton: '' as AnyComponent,
4747
NewCardHeader: '' as AnyComponent,
4848
SpacePresenter: '' as AnyComponent,
49+
TypesNavigator: '' as AnyComponent,
4950
RolesSection: '' as AnyComponent,
5051
EditRole: '' as AnyComponent,
5152
CardWidget: '' as AnyComponent,

plugins/card-resources/src/utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ export async function resolveLocation (loc: Location): Promise<ResolvedLocation
9898
}
9999

100100
const id = loc.path[3]
101-
if (loc.path[4] === undefined && id !== undefined && id !== 'browser') {
101+
const specialItems = ['browser', 'type', 'all']
102+
if (loc.path[4] === undefined && id !== undefined && !specialItems.includes(id)) {
102103
return await generateLocation(loc, id)
103104
}
104105
}

plugins/workbench-resources/src/components/Navigator.svelte

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import { getResource } from '@hcengineering/platform'
1818
import preference, { SpacePreference } from '@hcengineering/preference'
1919
import { createQuery, getClient, isAdminUser } from '@hcengineering/presentation'
20-
import { Scroller, NavItem } from '@hcengineering/ui'
20+
import { Scroller, NavItem, Component } from '@hcengineering/ui'
2121
import { NavLink } from '@hcengineering/view-resources'
2222
import type { Application, NavigatorModel, SpecialNavModel } from '@hcengineering/workbench'
2323
import { getSpecialSpaceClass } from '../utils'
@@ -198,5 +198,14 @@
198198
deselect={menuSelection || starred.some((s) => s._id === currentSpace)}
199199
/>
200200
{/each}
201+
202+
{#if model.groups && model.groups.length > 0}
203+
<div class="min-h-3 flex-no-shrink" />
204+
{#each model.groups as group (group.id)}
205+
{#if group.component}
206+
<Component is={group.component} props={{ model: group }} />
207+
{/if}
208+
{/each}
209+
{/if}
201210
</Scroller>
202211
{/if}

plugins/workbench-resources/src/components/Workbench.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,12 @@
642642
return sp
643643
}
644644
}
645+
for (const g of navigatorModel?.groups ?? []) {
646+
const sp = g.specials?.find((x) => x.id === id)
647+
if (sp !== undefined) {
648+
return sp
649+
}
650+
}
645651
}
646652
647653
let cover: HTMLElement

0 commit comments

Comments
 (0)