mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
UBER-413: Allow extensible navigator model (#3477)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
03b9be571b
commit
2869a8e189
@ -149,6 +149,7 @@ export function createModel (builder: Builder): void {
|
||||
navigatorModel: {
|
||||
spaces: [
|
||||
{
|
||||
id: 'boards',
|
||||
label: board.string.MyBoards,
|
||||
spaceClass: board.class.Board,
|
||||
addSpaceLabel: board.string.BoardCreateLabel,
|
||||
|
@ -421,12 +421,14 @@ export function createModel (builder: Builder, options = { addApplication: true
|
||||
],
|
||||
spaces: [
|
||||
{
|
||||
id: 'channels',
|
||||
label: chunter.string.Channels,
|
||||
spaceClass: chunter.class.Channel,
|
||||
addSpaceLabel: chunter.string.CreateChannel,
|
||||
createComponent: chunter.component.CreateChannel
|
||||
},
|
||||
{
|
||||
id: 'directMessages',
|
||||
label: chunter.string.DirectMessages,
|
||||
spaceClass: chunter.class.DirectMessage,
|
||||
addSpaceLabel: chunter.string.NewDirectMessage,
|
||||
|
@ -163,6 +163,7 @@ export function createModel (builder: Builder): void {
|
||||
],
|
||||
spaces: [
|
||||
{
|
||||
id: 'funnels',
|
||||
label: lead.string.Funnels,
|
||||
spaceClass: lead.class.Funnel,
|
||||
addSpaceLabel: lead.string.CreateFunnel,
|
||||
|
@ -543,7 +543,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
key: 'assignee',
|
||||
presenter: tracker.component.AssigneeEditor,
|
||||
displayProps: { key: 'assigee', fixed: 'right' },
|
||||
displayProps: { key: 'assignee', fixed: 'right' },
|
||||
props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' }
|
||||
}
|
||||
],
|
||||
@ -1084,6 +1084,7 @@ export function createModel (builder: Builder): void {
|
||||
],
|
||||
spaces: [
|
||||
{
|
||||
id: 'projects',
|
||||
label: tracker.string.Projects,
|
||||
spaceClass: tracker.class.Project,
|
||||
addSpaceLabel: tracker.string.CreateProject,
|
||||
|
@ -19,7 +19,13 @@ import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import { createAction } from '@hcengineering/model-view'
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import view, { KeyBinding } from '@hcengineering/view'
|
||||
import type { Application, HiddenApplication, SpaceView, ViewConfiguration } from '@hcengineering/workbench'
|
||||
import type {
|
||||
Application,
|
||||
ApplicationNavModel,
|
||||
HiddenApplication,
|
||||
SpaceView,
|
||||
ViewConfiguration
|
||||
} from '@hcengineering/workbench'
|
||||
|
||||
import core, { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import workbench from './plugin'
|
||||
@ -37,6 +43,12 @@ export class TApplication extends TDoc implements Application {
|
||||
hidden!: boolean
|
||||
}
|
||||
|
||||
@Model(workbench.class.ApplicationNavModel, core.class.Doc, DOMAIN_MODEL)
|
||||
@UX(workbench.string.Application)
|
||||
export class TApplicationNavModel extends TDoc implements ApplicationNavModel {
|
||||
extends!: Ref<Application>
|
||||
}
|
||||
|
||||
@Model(workbench.class.HiddenApplication, preference.class.Preference)
|
||||
export class THiddenApplication extends TPreference implements HiddenApplication {
|
||||
@Prop(TypeRef(workbench.class.Application), workbench.string.HiddenApplication)
|
||||
@ -49,7 +61,7 @@ export class TSpaceView extends TClass implements SpaceView {
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TApplication, TSpaceView, THiddenApplication)
|
||||
builder.createModel(TApplication, TSpaceView, THiddenApplication, TApplicationNavModel)
|
||||
builder.mixin(workbench.class.Application, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: workbench.component.ApplicationPresenter
|
||||
})
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { DocumentUpdate, Hierarchy, MixinData, MixinUpdate, ModelDb, toFindResult } from '.'
|
||||
import type {
|
||||
Account,
|
||||
@ -15,7 +16,7 @@ import type {
|
||||
import { Client } from './client'
|
||||
import core from './component'
|
||||
import type { DocumentQuery, FindOptions, FindResult, TxResult, WithLookup } from './storage'
|
||||
import { DocumentClassQuery, Tx, TxCUD, TxFactory } from './tx'
|
||||
import { DocumentClassQuery, Tx, TxCUD, TxFactory, TxProcessor } from './tx'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -269,6 +270,57 @@ export class TxOperations implements Omit<Client, 'notify'> {
|
||||
apply (scope: string): ApplyOperations {
|
||||
return new ApplyOperations(this, scope)
|
||||
}
|
||||
|
||||
async diffUpdate (doc: Doc, raw: Doc | Data<Doc>, date: Timestamp): Promise<Doc> {
|
||||
// We need to update fields if they are different.
|
||||
const documentUpdate: DocumentUpdate<Doc> = {}
|
||||
for (const [k, v] of Object.entries(raw)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space', 'attachedTo', 'attachedToClass'].includes(k)) {
|
||||
continue
|
||||
}
|
||||
const dv = (doc as any)[k]
|
||||
if (!deepEqual(dv, v) && v != null) {
|
||||
;(documentUpdate as any)[k] = v
|
||||
}
|
||||
}
|
||||
if (Object.keys(documentUpdate).length > 0) {
|
||||
await this.update(doc, documentUpdate, false, date, doc.modifiedBy)
|
||||
TxProcessor.applyUpdate(doc, documentUpdate)
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
async mixinDiffUpdate (
|
||||
doc: Doc,
|
||||
raw: Doc | Data<Doc>,
|
||||
mixin: Ref<Class<Mixin<Doc>>>,
|
||||
modifiedBy: Ref<Account>,
|
||||
modifiedOn: Timestamp
|
||||
): Promise<Doc> {
|
||||
// We need to update fields if they are different.
|
||||
|
||||
if (!this.getHierarchy().hasMixin(doc, mixin)) {
|
||||
await this.createMixin(doc._id, doc._class, doc.space, mixin, raw as MixinData<Doc, Doc>, modifiedOn, modifiedBy)
|
||||
TxProcessor.applyUpdate(this.getHierarchy().as(doc, mixin), raw)
|
||||
return doc
|
||||
}
|
||||
|
||||
const documentUpdate: MixinUpdate<Doc, Doc> = {}
|
||||
for (const [k, v] of Object.entries(raw)) {
|
||||
if (['_class', '_id', 'modifiedBy', 'modifiedOn', 'space', 'attachedTo', 'attachedToClass'].includes(k)) {
|
||||
continue
|
||||
}
|
||||
const dv = (doc as any)[k]
|
||||
if (!deepEqual(dv, v) && v != null) {
|
||||
;(documentUpdate as any)[k] = v
|
||||
}
|
||||
}
|
||||
if (Object.keys(documentUpdate).length > 0) {
|
||||
await this.updateMixin(doc._id, doc._class, doc.space, mixin, documentUpdate, modifiedOn, modifiedBy)
|
||||
TxProcessor.applyUpdate(this.getHierarchy().as(doc, mixin), documentUpdate)
|
||||
}
|
||||
return doc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,6 +14,8 @@
|
||||
import view, { Viewlet } from '@hcengineering/view'
|
||||
import {
|
||||
FilterBar,
|
||||
SpaceHeader,
|
||||
ViewletContentView,
|
||||
ViewletSettingButton,
|
||||
activeViewlet,
|
||||
getViewOptions,
|
||||
@ -23,8 +25,7 @@
|
||||
} from '@hcengineering/view-resources'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesContent from './IssuesContent.svelte'
|
||||
import IssuesHeader from './IssuesHeader.svelte'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
@ -88,7 +89,8 @@
|
||||
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
|
||||
</script>
|
||||
|
||||
<IssuesHeader
|
||||
<SpaceHeader
|
||||
_class={tracker.class.Issue}
|
||||
bind:viewlet
|
||||
bind:search
|
||||
showLabelSelector={$$slots.label_selector}
|
||||
@ -117,12 +119,21 @@
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</IssuesHeader>
|
||||
</SpaceHeader>
|
||||
<FilterBar _class={tracker.class.Issue} query={searchQuery} {viewOptions} on:change={(e) => (resultQuery = e.detail)} />
|
||||
<slot name="afterHeader" />
|
||||
<div class="popupPanel rowContent">
|
||||
{#if viewlet}
|
||||
<IssuesContent {viewlet} query={resultQuery} {space} {viewOptions} />
|
||||
<ViewletContentView
|
||||
_class={tracker.class.Issue}
|
||||
{viewlet}
|
||||
query={resultQuery}
|
||||
{space}
|
||||
{viewOptions}
|
||||
createItemDialog={CreateIssue}
|
||||
createItemLabel={tracker.string.AddIssueTooltip}
|
||||
createItemDialogProps={{ shouldSaveDraft: true }}
|
||||
/>
|
||||
{/if}
|
||||
{#if $$slots.aside !== undefined && asideShown}
|
||||
<div class="popupPanel-body__aside" class:shown={asideShown}>
|
||||
|
@ -23,9 +23,10 @@
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { NavLink, TreeNode } from '@hcengineering/view-resources'
|
||||
import { SpacesNavModel } from '@hcengineering/workbench'
|
||||
import { SpacesNavModel, SpecialNavModel } from '@hcengineering/workbench'
|
||||
import { SpecialElement } from '@hcengineering/workbench-resources'
|
||||
import tracker from '../../plugin'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
|
||||
export let space: Project
|
||||
export let model: SpacesNavModel
|
||||
@ -38,9 +39,30 @@
|
||||
const getSpaceCollapsedKey = () => `${getCurrentLocation().path[1]}_${space._id}_collapsed`
|
||||
|
||||
$: collapsed = localStorage.getItem(getSpaceCollapsedKey()) === COLLAPSED
|
||||
|
||||
let specials: SpecialNavModel[] = []
|
||||
|
||||
async function updateSpecials (model: SpacesNavModel, space: Project): Promise<void> {
|
||||
const newSpecials: SpecialNavModel[] = []
|
||||
for (const sp of model.specials ?? []) {
|
||||
let shouldAdd = true
|
||||
if (sp.visibleIf !== undefined) {
|
||||
const visibleIf = await getResource(sp.visibleIf)
|
||||
if (visibleIf !== undefined) {
|
||||
shouldAdd = await visibleIf([space])
|
||||
}
|
||||
}
|
||||
if (shouldAdd) {
|
||||
newSpecials.push(sp)
|
||||
}
|
||||
}
|
||||
specials = newSpecials
|
||||
}
|
||||
|
||||
$: updateSpecials(model, space)
|
||||
</script>
|
||||
|
||||
{#if model.specials}
|
||||
{#if specials}
|
||||
<TreeNode
|
||||
{collapsed}
|
||||
icon={space?.icon === tracker.component.IconWithEmoji ? IconWithEmoji : space?.icon ?? model.icon}
|
||||
@ -56,7 +78,7 @@
|
||||
actions={() => getActions(space)}
|
||||
on:click={() => localStorage.setItem(getSpaceCollapsedKey(), collapsed ? '' : COLLAPSED)}
|
||||
>
|
||||
{#each model.specials as special}
|
||||
{#each specials as special}
|
||||
<NavLink space={space._id} special={special.id}>
|
||||
<SpecialElement
|
||||
indent={'ml-2'}
|
||||
|
@ -15,6 +15,7 @@
|
||||
import view, { Viewlet } from '@hcengineering/view'
|
||||
import {
|
||||
FilterBar,
|
||||
SpaceHeader,
|
||||
ViewletSettingButton,
|
||||
activeViewlet,
|
||||
getViewOptions,
|
||||
@ -24,7 +25,6 @@
|
||||
} from '@hcengineering/view-resources'
|
||||
import { onDestroy } from 'svelte'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesHeader from '../issues/IssuesHeader.svelte'
|
||||
import CreateIssueTemplate from './CreateIssueTemplate.svelte'
|
||||
import IssueTemplatesContent from './IssueTemplatesContent.svelte'
|
||||
|
||||
@ -91,7 +91,15 @@
|
||||
$: viewOptions = getViewOptions(viewlet, $viewOptionStore)
|
||||
</script>
|
||||
|
||||
<IssuesHeader {space} {viewlets} {label} bind:viewlet bind:search showLabelSelector={$$slots.label_selector}>
|
||||
<SpaceHeader
|
||||
_class={tracker.class.IssueTemplate}
|
||||
{space}
|
||||
{viewlets}
|
||||
{label}
|
||||
bind:viewlet
|
||||
bind:search
|
||||
showLabelSelector={$$slots.label_selector}
|
||||
>
|
||||
<svelte:fragment slot="label_selector">
|
||||
<slot name="label_selector" />
|
||||
</svelte:fragment>
|
||||
@ -115,7 +123,7 @@
|
||||
<ViewletSettingButton bind:viewOptions {viewlet} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</IssuesHeader>
|
||||
</SpaceHeader>
|
||||
<slot name="afterHeader" />
|
||||
<FilterBar
|
||||
_class={tracker.class.IssueTemplate}
|
||||
|
@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Ref, Space } from '@hcengineering/core'
|
||||
import { Class, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import { TabList, SearchEdit, IModeSelector, ModeSelector } from '@hcengineering/ui'
|
||||
import { Viewlet } from '@hcengineering/view'
|
||||
import { FilterButton, setActiveViewletId } from '@hcengineering/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { setActiveViewletId } from '../utils'
|
||||
import FilterButton from './filter/FilterButton.svelte'
|
||||
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let viewlet: WithLookup<Viewlet> | undefined
|
||||
export let viewlets: WithLookup<Viewlet>[] = []
|
||||
export let label: string
|
||||
@ -65,7 +66,7 @@
|
||||
<SearchEdit bind:value={search} on:change={() => {}} />
|
||||
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
|
||||
<div class="buttons-divider" />
|
||||
<FilterButton _class={tracker.class.Issue} {space} />
|
||||
<FilterButton {_class} {space} />
|
||||
</div>
|
||||
<div class="ac-header-full medium-gap">
|
||||
<slot name="extra" />
|
@ -1,18 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { Issue } from '@hcengineering/tracker'
|
||||
import { Component, Loading } from '@hcengineering/ui'
|
||||
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { AnySvelteComponent, Component, Loading } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference, ViewOptions } from '@hcengineering/view'
|
||||
import tracker from '../../plugin'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
|
||||
export let viewlet: WithLookup<Viewlet>
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc> = {}
|
||||
export let space: Ref<Space> | undefined
|
||||
|
||||
export let viewOptions: ViewOptions
|
||||
|
||||
export let createItemDialog: AnySvelteComponent | undefined = undefined
|
||||
export let createItemLabel: IntlString | undefined = undefined
|
||||
export let createItemDialogProps = { shouldSaveDraft: true }
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
let preference: ViewletPreference | undefined
|
||||
let loading = true
|
||||
@ -29,10 +32,6 @@
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
const createItemDialog = CreateIssue
|
||||
const createItemLabel = tracker.string.AddIssueTooltip
|
||||
const createItemDialogProps = { shouldSaveDraft: true }
|
||||
</script>
|
||||
|
||||
{#if viewlet?.$lookup?.descriptor?.component}
|
||||
@ -42,7 +41,7 @@
|
||||
<Component
|
||||
is={viewlet.$lookup.descriptor.component}
|
||||
props={{
|
||||
_class: tracker.class.Issue,
|
||||
_class,
|
||||
config: preference?.config ?? viewlet.config,
|
||||
options: viewlet.options,
|
||||
createItemDialog,
|
@ -77,6 +77,8 @@ import ValueSelector from './components/ValueSelector.svelte'
|
||||
import ViewletSettingButton from './components/ViewletSettingButton.svelte'
|
||||
import DateFilterPresenter from './components/filter/DateFilterPresenter.svelte'
|
||||
import ArrayFilter from './components/filter/ArrayFilter.svelte'
|
||||
import SpaceHeader from './components/SpaceHeader.svelte'
|
||||
import ViewletContentView from './components/ViewletContentView.svelte'
|
||||
|
||||
import {
|
||||
afterResult,
|
||||
@ -175,7 +177,9 @@ export {
|
||||
DocNavLink,
|
||||
EnumEditor,
|
||||
StringPresenter,
|
||||
EditBoxPopup
|
||||
EditBoxPopup,
|
||||
SpaceHeader,
|
||||
ViewletContentView
|
||||
}
|
||||
|
||||
function PositionElementAlignment (e?: Event): PopupAlignment | undefined {
|
||||
|
@ -63,7 +63,7 @@
|
||||
import { getContext, onDestroy, onMount, tick } from 'svelte'
|
||||
import { subscribeMobile } from '../mobile'
|
||||
import workbench from '../plugin'
|
||||
import { workspacesStore } from '../utils'
|
||||
import { buildNavModel, workspacesStore } from '../utils'
|
||||
import AccountPopup from './AccountPopup.svelte'
|
||||
import AppItem from './AppItem.svelte'
|
||||
import AppSwitcher from './AppSwitcher.svelte'
|
||||
@ -326,7 +326,7 @@
|
||||
clear(1)
|
||||
currentAppAlias = app
|
||||
currentApplication = await client.findOne(workbench.class.Application, { alias: app })
|
||||
navigatorModel = currentApplication?.navigatorModel
|
||||
navigatorModel = await buildNavModel(client, currentApplication)
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -17,11 +17,12 @@
|
||||
import core from '@hcengineering/core'
|
||||
import { DocUpdates } from '@hcengineering/notification'
|
||||
import { NotificationClientImpl } from '@hcengineering/notification-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { IntlString, getResource } from '@hcengineering/platform'
|
||||
import preference from '@hcengineering/preference'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
Action,
|
||||
AnyComponent,
|
||||
AnySvelteComponent,
|
||||
IconAdd,
|
||||
IconEdit,
|
||||
@ -49,14 +50,14 @@
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const addSpace: Action = {
|
||||
label: model.addSpaceLabel,
|
||||
const addSpace = (addSpaceLabel: IntlString, createComponent: AnyComponent): Action => ({
|
||||
label: addSpaceLabel,
|
||||
icon: IconAdd,
|
||||
action: async (_id: Ref<Doc>): Promise<void> => {
|
||||
dispatch('open')
|
||||
showPopup(model.createComponent, {}, 'top')
|
||||
showPopup(createComponent, {}, 'top')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const browseSpaces: Action = {
|
||||
label: plugin.string.BrowseSpaces,
|
||||
@ -108,7 +109,11 @@
|
||||
}
|
||||
|
||||
function getParentActions (): Action[] {
|
||||
return hasSpaceBrowser ? [browseSpaces, addSpace] : [addSpace]
|
||||
const result = hasSpaceBrowser ? [browseSpaces] : []
|
||||
if (model.addSpaceLabel !== undefined && model.createComponent !== undefined) {
|
||||
result.push(addSpace(model.addSpaceLabel, model.createComponent))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function getPresenter (_class: Ref<Class<Doc>>): Promise<AnySvelteComponent | undefined> {
|
||||
|
@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Class, Client, Doc, Obj, Ref, Space } from '@hcengineering/core'
|
||||
import type { Class, Client, Doc, Obj, Ref, Space, TxOperations } from '@hcengineering/core'
|
||||
import core from '@hcengineering/core'
|
||||
import type { Workspace } from '@hcengineering/login'
|
||||
import type { Asset } from '@hcengineering/platform'
|
||||
@ -146,3 +146,38 @@ export async function showApplication (app: Application): Promise<void> {
|
||||
}
|
||||
|
||||
export const workspacesStore = writable<Workspace[]>([])
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function buildNavModel (
|
||||
client: TxOperations,
|
||||
currentApplication?: Application
|
||||
): Promise<NavigatorModel | undefined> {
|
||||
let newNavModel = currentApplication?.navigatorModel
|
||||
if (currentApplication !== undefined) {
|
||||
const models = await client.findAll(workbench.class.ApplicationNavModel, { extends: currentApplication._id })
|
||||
for (const nm of models) {
|
||||
const spaces = newNavModel?.spaces ?? []
|
||||
// Check for extending
|
||||
for (const sp of spaces) {
|
||||
const extend = (nm.spaces ?? []).find((p) => p.id === sp.id)
|
||||
if (extend !== undefined) {
|
||||
sp.label = sp.label ?? extend.label
|
||||
sp.createComponent = sp.createComponent ?? extend.createComponent
|
||||
sp.addSpaceLabel = sp.addSpaceLabel ?? extend.addSpaceLabel
|
||||
sp.icon = sp.icon ?? extend.icon
|
||||
sp.visibleIf = sp.visibleIf ?? extend.visibleIf
|
||||
sp.specials = [...(sp.specials ?? []), ...(extend.specials ?? [])]
|
||||
}
|
||||
}
|
||||
const newSpaces = (nm.spaces ?? []).filter((it) => !spaces.some((sp) => sp.id === it.id))
|
||||
newNavModel = {
|
||||
spaces: [...spaces, ...newSpaces],
|
||||
specials: [...(newNavModel?.specials ?? []), ...(nm.specials ?? [])],
|
||||
aside: newNavModel?.aside ?? nm?.aside
|
||||
}
|
||||
}
|
||||
}
|
||||
return newNavModel
|
||||
}
|
||||
|
@ -16,9 +16,9 @@
|
||||
import type { Class, Doc, Mixin, Obj, Ref, Space } from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Metadata, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { Preference } from '@hcengineering/preference'
|
||||
import { AnyComponent, Location, ResolvedLocation } from '@hcengineering/ui'
|
||||
import { ViewAction } from '@hcengineering/view'
|
||||
import type { Preference } from '@hcengineering/preference'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -28,7 +28,10 @@ export interface Application extends Doc {
|
||||
alias: string
|
||||
icon: Asset
|
||||
hidden: boolean
|
||||
|
||||
// Also attached ApplicationNavModel will be joined after this one main.
|
||||
navigatorModel?: NavigatorModel
|
||||
|
||||
locationResolver?: Resource<(loc: Location) => Promise<ResolvedLocation | undefined>>
|
||||
|
||||
// Component will be displayed in case navigator model is not defined, or nothing is selected in navigator model
|
||||
@ -40,6 +43,17 @@ export interface Application extends Doc {
|
||||
navFooterComponent?: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ApplicationNavModel extends Doc {
|
||||
extends: Ref<Application>
|
||||
|
||||
spaces?: SpacesNavModel[]
|
||||
specials?: SpecialNavModel[]
|
||||
aside?: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -51,10 +65,11 @@ export interface HiddenApplication extends Preference {
|
||||
* @public
|
||||
*/
|
||||
export interface SpacesNavModel {
|
||||
label: IntlString
|
||||
id: string // Id could be used for extending of navigation model
|
||||
label?: IntlString
|
||||
spaceClass: Ref<Class<Space>>
|
||||
addSpaceLabel: IntlString
|
||||
createComponent: AnyComponent
|
||||
addSpaceLabel?: IntlString
|
||||
createComponent?: AnyComponent
|
||||
icon?: Asset
|
||||
|
||||
// Child special items.
|
||||
@ -118,6 +133,7 @@ export const workbenchId = 'workbench' as Plugin
|
||||
export default plugin(workbenchId, {
|
||||
class: {
|
||||
Application: '' as Ref<Class<Application>>,
|
||||
ApplicationNavModel: '' as Ref<Class<ApplicationNavModel>>,
|
||||
HiddenApplication: '' as Ref<Class<HiddenApplication>>
|
||||
},
|
||||
mixin: {
|
||||
|
Loading…
Reference in New Issue
Block a user