From 668537e593efde04c5cf8b315caaa9929d0ae0f9 Mon Sep 17 00:00:00 2001 From: Artem Savchenko Date: Tue, 14 Oct 2025 13:37:00 +0700 Subject: [PATCH 1/3] Use navigation by types instead of spaces Signed-off-by: Artem Savchenko --- models/card/src/index.ts | 27 ++++--- .../src/components/CardFeedView.svelte | 13 +++- .../card-resources/src/components/Main.svelte | 10 ++- .../components/navigator/TagHierarchy.svelte | 12 +++- .../navigator/TypesNavigator.svelte | 72 +++++++++++++++++++ plugins/card-resources/src/index.ts | 2 + plugins/card-resources/src/plugin.ts | 1 + plugins/card-resources/src/utils.ts | 3 +- .../src/components/Navigator.svelte | 11 ++- .../src/components/Workbench.svelte | 6 ++ plugins/workbench/src/types.ts | 16 +++++ 11 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 plugins/card-resources/src/components/navigator/TypesNavigator.svelte diff --git a/models/card/src/index.ts b/models/card/src/index.ts index d46e1fbd961..9a589b6573c 100644 --- a/models/card/src/index.ts +++ b/models/card/src/index.ts @@ -447,15 +447,24 @@ export function createModel (builder: Builder): void { position: 'top' } ], - spaces: [ + spaces: [], + groups: [ { - id: 'spaces', - label: core.string.Spaces, - spaceClass: card.class.CardSpace, - addSpaceLabel: core.string.Space, - icon: card.icon.Space, - // intentionally left empty in order to make space presenter working - specials: [] + id: 'types', + label: card.string.MasterTags, + groupByClass: card.class.MasterTag, + icon: card.icon.MasterTags, + component: card.component.TypesNavigator, + specials: [ + { + id: 'type', + label: card.string.Cards, + component: card.component.Main, + componentProps: { + defaultViewletDescriptor: card.viewlet.CardFeedDescriptor + } + } + ] } ] }, @@ -658,7 +667,7 @@ export function createModel (builder: Builder): void { ) builder.mixin(card.class.Card, core.class.Class, view.mixin.ClassFilters, { - filters: [], + filters: ['space'], ignoreKeys: ['parent'] }) diff --git a/plugins/card-resources/src/components/CardFeedView.svelte b/plugins/card-resources/src/components/CardFeedView.svelte index 8bad60c4354..e292e3add9b 100644 --- a/plugins/card-resources/src/components/CardFeedView.svelte +++ b/plugins/card-resources/src/components/CardFeedView.svelte @@ -63,6 +63,7 @@ } } ) + $: cardSpace = space ?? getSpaceFromFilter(resultQuery) $: hasNextPage = total > cards.length @@ -76,6 +77,16 @@ } } + function getSpaceFromFilter (_query: DocumentQuery): Ref | undefined { + if (_query.space != null) { + if (typeof _query.space === 'object' && '$in' in _query.space && Array.isArray(_query.space.$in)) { + return _query.space.$in[0] as Ref + } + return _query.space as Ref + } + return undefined + } + function getFormatDateId (timestamp: number): string { const now = new Date() const date = new Date(timestamp) @@ -110,7 +121,7 @@
- +
{#each cards as card, index} {@const previousCard = cards[index - 1]} diff --git a/plugins/card-resources/src/components/Main.svelte b/plugins/card-resources/src/components/Main.svelte index c06841ae68a..adac8183024 100644 --- a/plugins/card-resources/src/components/Main.svelte +++ b/plugins/card-resources/src/components/Main.svelte @@ -14,7 +14,7 @@ --> + +
+ + { + selectType(e.detail) + }} + /> + +
diff --git a/plugins/card-resources/src/index.ts b/plugins/card-resources/src/index.ts index c2f622b0414..be1d2809504 100644 --- a/plugins/card-resources/src/index.ts +++ b/plugins/card-resources/src/index.ts @@ -53,6 +53,7 @@ import CreateCardButton from './components/CreateCardButton.svelte' import CardArrayEditor from './components/CardArrayEditor.svelte' import NewCardHeader from './components/navigator/NewCardHeader.svelte' import SpacePresenter from './components/navigator/SpacePresenter.svelte' +import TypesNavigator from './components/navigator/TypesNavigator.svelte' import LabelsPresenter from './components/LabelsPresenter.svelte' import RolesSection from './components/settings/RolesSection.svelte' import EditRole from './components/settings/EditRole.svelte' @@ -111,6 +112,7 @@ export default async (): Promise => ({ CardArrayEditor, NewCardHeader, SpacePresenter, + TypesNavigator, LabelsPresenter, RolesSection, EditRole, diff --git a/plugins/card-resources/src/plugin.ts b/plugins/card-resources/src/plugin.ts index 51f19dda86a..0c1a677e948 100644 --- a/plugins/card-resources/src/plugin.ts +++ b/plugins/card-resources/src/plugin.ts @@ -46,6 +46,7 @@ export default mergeIds(cardId, card, { CreateCardButton: '' as AnyComponent, NewCardHeader: '' as AnyComponent, SpacePresenter: '' as AnyComponent, + TypesNavigator: '' as AnyComponent, RolesSection: '' as AnyComponent, EditRole: '' as AnyComponent, CardWidget: '' as AnyComponent, diff --git a/plugins/card-resources/src/utils.ts b/plugins/card-resources/src/utils.ts index 142967acfb0..eefae66a7da 100644 --- a/plugins/card-resources/src/utils.ts +++ b/plugins/card-resources/src/utils.ts @@ -98,7 +98,8 @@ export async function resolveLocation (loc: Location): Promise s._id === currentSpace)} /> {/each} + + {#if model.groups && model.groups.length > 0} +
+ {#each model.groups as group (group.id)} + {#if group.component} + + {/if} + {/each} + {/if} {/if} diff --git a/plugins/workbench-resources/src/components/Workbench.svelte b/plugins/workbench-resources/src/components/Workbench.svelte index 41254cb769c..bd423d4e547 100644 --- a/plugins/workbench-resources/src/components/Workbench.svelte +++ b/plugins/workbench-resources/src/components/Workbench.svelte @@ -642,6 +642,12 @@ return sp } } + for (const g of navigatorModel?.groups ?? []) { + const sp = g.specials?.find((x) => x.id === id) + if (sp !== undefined) { + return sp + } + } } let cover: HTMLElement diff --git a/plugins/workbench/src/types.ts b/plugins/workbench/src/types.ts index e2ff83790f0..0370bb35b0f 100644 --- a/plugins/workbench/src/types.ts +++ b/plugins/workbench/src/types.ts @@ -114,6 +114,7 @@ export interface ApplicationNavModel extends Doc { spaces?: SpacesNavModel[] specials?: SpecialNavModel[] + groups?: GroupsNavModel[] } /** @public */ @@ -136,10 +137,25 @@ export interface SpacesNavModel { visibleIf?: Resource<(space: Space) => Promise> } +/** @public */ +export interface GroupsNavModel { + id: string // Id could be used for extending of navigation model + label?: IntlString + groupByClass: Ref> // Any class to group by (MasterTag, Project, etc.) + icon?: Asset + component?: AnyComponent // Component to render the group navigation (e.g., TypesNavigator) + + // Child special items. + specials?: SpecialNavModel[] + + visibleIf?: Resource<(docs: Doc[]) => Promise> +} + /** @public */ export interface NavigatorModel { spaces: SpacesNavModel[] specials?: SpecialNavModel[] + groups?: GroupsNavModel[] } /** @public */ From 038becaf3bdf8d01b0f460b1a68a04e8b5fb8cf0 Mon Sep 17 00:00:00 2001 From: Artyom Savchenko Date: Tue, 14 Oct 2025 16:37:31 +0700 Subject: [PATCH 2/3] Build type path Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Artyom Savchenko --- .../src/components/navigator/TypesNavigator.svelte | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/card-resources/src/components/navigator/TypesNavigator.svelte b/plugins/card-resources/src/components/navigator/TypesNavigator.svelte index 58521f5ee83..6d2c2d2d366 100644 --- a/plugins/card-resources/src/components/navigator/TypesNavigator.svelte +++ b/plugins/card-resources/src/components/navigator/TypesNavigator.svelte @@ -36,12 +36,15 @@ return _classes.filter((it) => it.extends === card.class.Card) } + function buildTypePath(currentPath: any[], type: Ref): any[] { + // Copy the first three segments, then add 'type' and the selected type + return [...currentPath.slice(0, 3), 'type', type]; + } + function selectType (type: Ref): void { - const loc = getCurrentLocation() - loc.path[3] = 'type' - loc.path[4] = type - loc.path.length = 5 - navigate(loc) + const loc = getCurrentLocation(); + loc.path = buildTypePath(loc.path, type); + navigate(loc); } $: _class = $locationStore.path[4] as Ref> From 8a49d8902fed8cb4a2b1025e74ecf7c4b8599ee9 Mon Sep 17 00:00:00 2001 From: Artem Savchenko Date: Tue, 14 Oct 2025 16:39:50 +0700 Subject: [PATCH 3/3] Fix formatting Signed-off-by: Artem Savchenko --- plugins/card-resources/src/components/Main.svelte | 1 - .../src/components/navigator/TypesNavigator.svelte | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/card-resources/src/components/Main.svelte b/plugins/card-resources/src/components/Main.svelte index adac8183024..340fc0850e5 100644 --- a/plugins/card-resources/src/components/Main.svelte +++ b/plugins/card-resources/src/components/Main.svelte @@ -26,7 +26,6 @@ onDestroy( location.subscribe((loc) => { - // Check if we're using type-based navigation: /card/type/{typeId} const isTypeSpecified = loc.path[3] === 'type' _class = isTypeSpecified ? loc.path[4] : card.class.Card }) diff --git a/plugins/card-resources/src/components/navigator/TypesNavigator.svelte b/plugins/card-resources/src/components/navigator/TypesNavigator.svelte index 6d2c2d2d366..10a88cb3b2b 100644 --- a/plugins/card-resources/src/components/navigator/TypesNavigator.svelte +++ b/plugins/card-resources/src/components/navigator/TypesNavigator.svelte @@ -36,15 +36,14 @@ return _classes.filter((it) => it.extends === card.class.Card) } - function buildTypePath(currentPath: any[], type: Ref): any[] { - // Copy the first three segments, then add 'type' and the selected type - return [...currentPath.slice(0, 3), 'type', type]; + function buildTypePath (currentPath: any[], type: Ref): any[] { + return [...currentPath.slice(0, 3), 'type', type] } function selectType (type: Ref): void { - const loc = getCurrentLocation(); - loc.path = buildTypePath(loc.path, type); - navigate(loc); + const loc = getCurrentLocation() + loc.path = buildTypePath(loc.path, type) + navigate(loc) } $: _class = $locationStore.path[4] as Ref>