diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts deleted file mode 100644 index 87872bc289..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; -import { css, html, nothing } from 'lit'; -import { customElement, state } from 'lit/decorators.js'; -import { first, map } from 'rxjs'; -import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context'; -import type { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent, IRoutingInfo } from '@umbraco-cms/router'; -import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; -import type { ManifestDashboard, ManifestDashboardCollection } from '@umbraco-cms/models'; - -import { UmbLitElement } from '@umbraco-cms/element'; - -@customElement('umb-section-dashboards') -export class UmbSectionDashboardsElement extends UmbLitElement { - static styles = [ - UUITextStyles, - css` - :host { - display: flex; - flex-direction: column; - height: 100%; - } - - #tabs { - background-color: var(--uui-color-surface); - height: 70px; - border-bottom: 1px solid var(--uui-color-border); - box-sizing: border-box; - } - - #scroll-container { - flex: 1; - position: relative; - } - - #router-slot { - box-sizing: border-box; - display: block; - padding: var(--uui-size-5); - } - - #header { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - min-height: 60px; - box-sizing: border-box; - margin: 0; - padding: 0 var(--uui-size-5); - background-color: var(--uui-color-surface); - border-bottom: 1px solid var(--uui-color-border); - } - `, - ]; - - @state() - private _dashboards?: Array; - - @state() - private _routes: Array = []; - - @state() - private _routerPath?: string; - - @state() - private _activePath?: string; - - private _currentSectionAlias?: string; - private _sectionContext?: UmbSectionContext; - - constructor() { - super(); - - this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (context) => { - this._sectionContext = context; - this._observeSectionContext(); - }); - } - - private _observeSectionContext() { - if (!this._sectionContext) return; - - this.observe(this._sectionContext.alias.pipe(first()), (alias) => { - this._currentSectionAlias = alias; - this._observeDashboards(); - }); - } - - private _observeDashboards() { - if (!this._currentSectionAlias) return; - - this.observe( - umbExtensionsRegistry - ?.extensionsOfTypes(['dashboard', 'dashboardCollection']) - .pipe( - map((extensions) => - extensions.filter((extension) => extension.conditions.sections.includes(this._currentSectionAlias ?? '')) - ) - ), - (dashboards) => { - this._dashboards = dashboards || undefined; - this._createRoutes(); - } - ); - } - - private _createRoutes() { - this._routes = []; - - if (this._dashboards) { - this._routes = this._dashboards.map((dashboard) => { - return { - path: `${dashboard.meta.pathname}`, - component: () => { - if (dashboard.type === 'dashboardCollection') { - return import('src/backoffice/shared/collection/dashboards/dashboard-collection.element'); - } - return createExtensionElement(dashboard); - }, - setup: (component: Promise | HTMLElement, info: IRoutingInfo) => { - // When its using import, we get an element, when using createExtensionElement we get a Promise. - // TODO: this is a bit hacky, can we do it in a more appropriate way: - if ((component as any).then) { - (component as any).then((el: any) => (el.manifest = dashboard)); - } else { - (component as any).manifest = dashboard; - } - }, - }; - }); - - this._routes.push({ - path: '**', - redirectTo: this._dashboards?.[0]?.meta.pathname, - }); - } - } - - private _renderNavigation() { - return html` - ${this._dashboards && this._dashboards.length > 1 - ? html` - - ${this._dashboards.map( - (dashboard) => html` - - ` - )} - - ` - : this._dashboards?.length === 1 - ? html`` - : nothing} - `; - } - - render() { - return html` - ${this._renderNavigation()} - - { - this._routerPath = event.target.absoluteRouterPath; - }} - @change=${(event: UmbRouterSlotChangeEvent) => { - this._activePath = event.target.localActiveViewPath; - }}> - - `; - } -} - -export default UmbSectionDashboardsElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-section-dashboards': UmbSectionDashboardsElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.stories.ts deleted file mode 100644 index 28c3096f16..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.stories.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Meta, Story } from '@storybook/web-components'; -import { html } from 'lit-html'; -import { manifests } from '../../../../documents/section.manifests'; -import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context'; -import type { UmbSectionDashboardsElement } from './section-dashboards.element'; -import type { ManifestSection } from '@umbraco-cms/models'; -import './section-dashboards.element'; - -const contentSectionManifest = manifests.find((m) => m.alias === 'Umb.Section.Content') as ManifestSection; - -export default { - title: 'Sections/Shared/Section Dashboards', - component: 'umb-section-dashboards', - id: 'umb-section-dashboards', - decorators: [ - (story) => - html` - ${story()} - `, - ], -} as Meta; - -export const AAAOverview: Story = () => - html` `; -AAAOverview.storyName = 'Overview'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts index 4ee4405469..5dba7bfcba 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts @@ -2,13 +2,14 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { map, of } from 'rxjs'; -import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/router'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context'; -import type { ManifestSectionView } from '@umbraco-cms/models'; +import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/router'; +import type { ManifestDashboard, ManifestSectionView } from '@umbraco-cms/models'; import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; import { UmbObserverController } from '@umbraco-cms/observable-api'; +// TODO: this might need a new name, since it's both view and dashboard now @customElement('umb-section-views') export class UmbSectionViewsElement extends UmbLitElement { static styles = [ @@ -17,14 +18,17 @@ export class UmbSectionViewsElement extends UmbLitElement { #header { background-color: var(--uui-color-surface); border-bottom: 1px solid var(--uui-color-divider-standalone); + display: flex; + justify-content: space-between; + align-items: center; } - uui-tab-group { + #views { justify-content: flex-end; --uui-tab-divider: var(--uui-color-divider-standalone); } - uui-tab-group uui-tab:first-child { + #views uui-tab:first-child { border-left: 1px solid var(--uui-color-divider-standalone); } `, @@ -36,6 +40,9 @@ export class UmbSectionViewsElement extends UmbLitElement { @state() private _views: Array = []; + @state() + private _dashboards: Array = []; + @state() private _routerPath?: string; @@ -47,49 +54,76 @@ export class UmbSectionViewsElement extends UmbLitElement { private _sectionContext?: UmbSectionContext; private _extensionsObserver?: UmbObserverController; + private _viewsObserver?: UmbObserverController; + private _dashboardObserver?: UmbObserverController; constructor() { super(); this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionContext) => { this._sectionContext = sectionContext; - this._observeViews(); + this._observeSectionAlias(); }); } - async #createRoutes(viewManifests: Array) { - if (!viewManifests) return; - const routes = viewManifests.map((manifest) => { + async #createRoutes() { + const dashboardRoutes = this._dashboards?.map((manifest) => { return { - path: manifest.meta.pathname, + path: 'dashboard/' + manifest.meta.pathname, component: () => createExtensionElement(manifest), }; }); - this._routes = [...routes, { path: '**', redirectTo: routes[0].path }]; + const viewRoutes = this._views?.map((manifest) => { + return { + path: 'view/' + manifest.meta.pathname, + component: () => createExtensionElement(manifest), + }; + }); + + const routes = [...dashboardRoutes, ...viewRoutes]; + this._routes = routes?.length > 0 ? [...routes, { path: '**', redirectTo: routes?.[0]?.path }] : []; } - private _observeViews() { + private _observeSectionAlias() { if (!this._sectionContext) return; this.observe( this._sectionContext.alias, (sectionAlias) => { - this._observeExtensions(sectionAlias); + this._observeViews(sectionAlias); + this._observeDashboards(sectionAlias); }, 'viewsObserver' ); } - private _observeExtensions(sectionAlias?: string) { - this._extensionsObserver?.destroy(); + + private _observeViews(sectionAlias?: string) { + this._viewsObserver?.destroy(); if (sectionAlias) { - this._extensionsObserver = this.observe( + this._viewsObserver = this.observe( umbExtensionsRegistry ?.extensionsOfType('sectionView') .pipe(map((views) => views.filter((view) => view.conditions.sections.includes(sectionAlias)))) ?? of([]), (views) => { this._views = views; - this.#createRoutes(views); + this.#createRoutes(); + } + ); + } + } + + private _observeDashboards(sectionAlias?: string) { + this._dashboardObserver?.destroy(); + + if (sectionAlias) { + this._dashboardObserver = this.observe( + umbExtensionsRegistry + ?.extensionsOfType('dashboard') + .pipe(map((views) => views.filter((view) => view.conditions.sections.includes(sectionAlias)))) ?? of([]), + (views) => { + this._dashboards = views; + this.#createRoutes(); } ); } @@ -97,9 +131,9 @@ export class UmbSectionViewsElement extends UmbLitElement { render() { return html` - ${this._views.length > 0 + ${this._routes.length > 0 ? html` - + { @@ -114,15 +148,32 @@ export class UmbSectionViewsElement extends UmbLitElement { `; } - #renderTabs() { + #renderDashboards() { return html` - + + ${this._dashboards.map( + (dashboard) => html` + + ${dashboard.meta.label || dashboard.name} + + ` + )} + + `; + } + + #renderViews() { + return html` + ${this._views.map( - (view: ManifestSectionView) => html` + (view) => html` + href="${this._routerPath}/view/${view.meta.pathname}" + ?active="${this._activePath === 'view/' + view.meta.pathname}"> ${view.meta.label || view.name} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts index ac57c973c1..e45c54f975 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts @@ -2,12 +2,10 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { map } from 'rxjs'; - -import type { IRoutingInfo } from '@umbraco-cms/router'; import type { UmbWorkspaceElement } from '../workspace/workspace.element'; -import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from './section.context'; import type { UmbSectionViewsElement } from './section-views/section-views.element'; -import type { ManifestSectionView, ManifestMenuSectionSidebarApp, ManifestSection } from '@umbraco-cms/models'; +import type { IRoutingInfo } from '@umbraco-cms/router'; +import type { ManifestMenuSectionSidebarApp, ManifestSection } from '@umbraco-cms/models'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -49,39 +47,16 @@ export class UmbSectionElement extends UmbLitElement { @state() private _menus?: Array; - @state() - private _views?: Array; - - private _sectionContext?: UmbSectionContext; - private _sectionAlias?: string; - - constructor() { - super(); - - this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (instance) => { - this._sectionContext = instance; - - // TODO: currently they don't corporate, as they overwrite each other... - this.#observeSectionAlias(); - this.#createRoutes(); - }); + connectedCallback() { + super.connectedCallback(); + this.#observeSectionSidebarApps(); + this.#createRoutes(); } #createRoutes() { this._routes = []; this._routes = [ - { - path: 'dashboard', - component: () => import('./section-dashboards/section-dashboards.element'), - }, - { - path: 'view', - component: () => import('../section/section-views/section-views.element'), - setup: (element: UmbSectionViewsElement) => { - element.sectionAlias = this.manifest?.alias; - }, - }, { path: 'workspace/:entityType', component: () => import('../workspace/workspace.element'), @@ -91,35 +66,27 @@ export class UmbSectionElement extends UmbLitElement { }, { path: '**', - redirectTo: 'view', + component: () => import('../section/section-views/section-views.element'), + setup: (element: UmbSectionViewsElement) => { + element.sectionAlias = this.manifest?.alias; + }, }, ]; } - #observeSectionAlias() { - if (!this._sectionContext) return; - - this.observe(this._sectionContext.alias, (alias) => { - this._sectionAlias = alias; - this.#observeSectionSidebarApps(alias); - }); - } - - #observeSectionSidebarApps(sectionAlias?: string) { - if (sectionAlias) { - this.observe( - umbExtensionsRegistry - ?.extensionsOfType('menuSectionSidebarApp') - .pipe( - map((manifests) => manifests.filter((manifest) => manifest.conditions.sections.includes(sectionAlias))) - ), - (manifests) => { - this._menus = manifests; - } - ); - } else { - this._menus = []; - } + #observeSectionSidebarApps() { + this.observe( + umbExtensionsRegistry + ?.extensionsOfType('menuSectionSidebarApp') + .pipe( + map((manifests) => + manifests.filter((manifest) => manifest.conditions.sections.includes(this.manifest?.alias || '')) + ) + ), + (manifests) => { + this._menus = manifests; + } + ); } render() { @@ -131,12 +98,12 @@ export class UmbSectionElement extends UmbLitElement { - items.conditions.sections.includes(this._sectionAlias || '')}> + items.conditions.sections.includes(this.manifest?.alias || '')}> - items.conditions.sections.includes(this._sectionAlias || '')} + items.conditions.sections.includes(this.manifest?.alias || '')} default-element="umb-section-sidebar-menu"> `