From 4b90f0522c54a7f6feeece545305047b70bc72f7 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 30 May 2022 20:29:39 +0200 Subject: [PATCH 1/8] get content data from mock service worker --- .../src/content/content-editor.element.ts | 11 +- .../src/content/content-section.element.ts | 13 +- .../src/content/content-tree.element.ts | 60 ++----- .../src/content/content.service.ts | 153 +++--------------- .../src/mocks/data/content.data.ts | 136 ++++++++++++++++ .../src/mocks/domains/content.handlers.ts | 17 ++ .../src/mocks/handlers.ts | 2 + 7 files changed, 209 insertions(+), 183 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts create mode 100644 src/Umbraco.Web.UI.Client/src/mocks/domains/content.handlers.ts diff --git a/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts index 9e529e051c..d8c962bf44 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts @@ -2,8 +2,9 @@ import { css, html, LitElement } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbContextConsumerMixin } from '../core/context'; -import { DocumentNode, UmbContentService } from './content.service'; +import { UmbContentService } from './content.service'; import { Subscription } from 'rxjs'; +import { DocumentNode } from '../mocks/data/content.data'; @customElement('umb-content-editor') class UmbContentEditor extends UmbContextConsumerMixin(LitElement) { @@ -44,12 +45,12 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) { `, ]; - @state() - _node?: DocumentNode; - @property() id!: string; + @state() + _node?: DocumentNode; + private _contentService?: UmbContentService; private _nodeSubscription?: Subscription; @@ -73,7 +74,7 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) { private _useNode() { this._nodeSubscription?.unsubscribe(); - this._nodeSubscription = this._contentService?.getById(this.id).subscribe(node => { + this._nodeSubscription = this._contentService?.getById(parseInt(this.id)).subscribe(node => { if (!node) return; this._node = node; }); diff --git a/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts index f4e23e93c3..7982d59352 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts @@ -4,6 +4,7 @@ import { customElement } from 'lit/decorators.js'; import { UmbContextConsumerMixin, UmbContextProviderMixin } from '../core/context'; import { UmbRouteLocation, UmbRouter } from '../core/router'; import { UmbContentService } from './content.service'; +import { Subscription } from 'rxjs'; import './content-tree.element'; import './content-dashboards.element'; @@ -23,6 +24,7 @@ export class UmbContentSection extends UmbContextProviderMixin(UmbContextConsume ]; private _router?: UmbRouter; + private _locationSubscription?: Subscription; private _outlet?: HTMLElement; constructor () { @@ -36,8 +38,10 @@ export class UmbContentSection extends UmbContextProviderMixin(UmbContextConsume }); } - private _useLocation () { - this._router?.location + private _useLocation () { + this._locationSubscription?.unsubscribe(); + + this._locationSubscription = this._router?.location .subscribe((location: UmbRouteLocation) => { // TODO: temp outlet solution const nodeId = location.params.nodeId; @@ -56,6 +60,11 @@ export class UmbContentSection extends UmbContextProviderMixin(UmbContextConsume }); } + disconnectedCallback(): void { + super.disconnectedCallback(); + this._locationSubscription?.unsubscribe(); + } + render() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/content/content-tree.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-tree.element.ts index 8f917f4ebc..aae89e458d 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-tree.element.ts @@ -4,8 +4,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { UmbContextConsumerMixin } from '../core/context'; import { UmbRouteLocation, UmbRouter } from '../core/router'; import { Subscription } from 'rxjs'; +import { data } from '../mocks/data/content.data'; import { UUIMenuItemElement } from '@umbraco-ui/uui'; -import { data } from './content.service'; @customElement('umb-content-tree') class UmbContentTree extends UmbContextConsumerMixin(LitElement) { @@ -27,7 +27,7 @@ class UmbContentTree extends UmbContextConsumerMixin(LitElement) { _section?: string; @state() - _currentNodeId?: string; + _currentNodeId?: number; private _router?: UmbRouter; private _location?: UmbRouteLocation; @@ -51,10 +51,15 @@ class UmbContentTree extends UmbContextConsumerMixin(LitElement) { this._locationSubscription = this._router?.location.subscribe(location => { this._location = location; this._section = location.params.section; - this._currentNodeId = location.params.nodeId; + this._currentNodeId = parseInt(location.params.nodeId); }); } + disconnectedCallback(): void { + super.disconnectedCallback(); + this._locationSubscription?.unsubscribe(); + } + /* TODO: there are some problems with menu items and click events. They can happen on element inside and outside of the shadow dom which makes it difficult to find the right href in the router. It might make sense to make it possible to use your own anchor tag or button inside a label slot instead. @@ -62,6 +67,8 @@ class UmbContentTree extends UmbContextConsumerMixin(LitElement) { */ private _handleMenuItemClick (e: PointerEvent) { e.preventDefault(); + e.stopPropagation(); + const target = e.target as UUIMenuItemElement; if (!target) return; @@ -71,11 +78,6 @@ class UmbContentTree extends UmbContextConsumerMixin(LitElement) { this._router?.push(href); } - disconnectedCallback(): void { - super.disconnectedCallback(); - this._locationSubscription?.unsubscribe(); - } - render () { return html` @@ -86,54 +88,14 @@ class UmbContentTree extends UmbContextConsumerMixin(LitElement) { ${ this._tree.map(item => html` `)} - - `; } diff --git a/src/Umbraco.Web.UI.Client/src/content/content.service.ts b/src/Umbraco.Web.UI.Client/src/content/content.service.ts index d456307c59..5c37053309 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content.service.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content.service.ts @@ -1,138 +1,37 @@ import { BehaviorSubject, map, Observable } from 'rxjs'; - -export interface DocumentNode { - id: string; - key: string; - name: string; - alias: string; - icon: string; // TODO: should come from the doc type? - properties: NodeProperty[]; - data: any; // TODO: define data type - layout?: any; // TODO: define layout type - make it non-optional -} - -export interface NodeProperty { - alias: string; - label: string; - description: string; - dataTypeAlias: string; - tempValue: string; // TODO: remove this - only used for testing -} - -export const data: Array = [ - { - id: '1', - key: '74e4008a-ea4f-4793-b924-15e02fd380d3', - name: 'Document 1', - alias: 'document1', - icon: 'document', - properties: [ - { - alias: 'myHeadline', - label: 'Textarea label', - description: 'this is a textarea property', - dataTypeAlias: 'myTextStringEditor', - tempValue: 'hello world 1' - }, - { - alias: 'myDescription', - label: 'Text string label', - description: 'This is the a text string property', - dataTypeAlias: 'myTextAreaEditor', - tempValue: 'Tex areaaaa 1' - }, - ], - data: [ - { - alias: 'myHeadline', - value: 'hello world', - }, - { - alias: 'myDescription', - value: 'Teeeeexxxt areaaaaaa', - }, - ], - /* - layout: [ - { - type: 'group', - children: [ - { - type: 'property', - alias: 'myHeadline' - }, - { - type: 'property', - alias: 'myDescription' - } - ] - } - ], - */ - }, - { - id: '2', - key: '74e4008a-ea4f-4793-b924-15e02fd380d3', - name: 'Document 2', - alias: 'document2', - icon: 'favorite', - properties: [ - { - alias: 'myHeadline', - label: 'Textarea label', - description: 'this is a textarea property', - dataTypeAlias: 'myTextStringEditor', - tempValue: 'hello world 2' - }, - { - alias: 'myDescription', - label: 'Text string label', - description: 'This is the a text string property', - dataTypeAlias: 'myTextAreaEditor', - tempValue: 'Tex areaaaa 2' - }, - ], - data: [ - { - alias: 'myHeadline', - value: 'hello world', - }, - { - alias: 'myDescription', - value: 'Teeeeexxxt areaaaaaa', - }, - ], - /* - layout: [ - { - type: 'group', - children: [ - { - type: 'property', - alias: 'myHeadline' - }, - { - type: 'property', - alias: 'myDescription' - } - ] - } - ], - */ - } -]; +import { DocumentNode } from '../mocks/data/content.data'; export class UmbContentService { - private _nodes: BehaviorSubject> = new BehaviorSubject(>[]); public readonly nodes: Observable> = this._nodes.asObservable(); - constructor () { - this._nodes.next(data); - } + getById (id: number): Observable { + // fetch from server and update store + fetch(`/umbraco/backoffice/content/${id}`) + .then(res => res.json()) + .then(data => { + this._updateStore(data); + }); - getById (id: string): Observable { return this.nodes.pipe(map(((nodes: Array) => nodes.find((node: DocumentNode) => node.id === id) || null))); } + _updateStore (fetchedNodes: Array) { + const storedNodes = this._nodes.getValue(); + let updated: any = [...storedNodes]; + + fetchedNodes.forEach(fetchedNode => { + const index = storedNodes.map(storedNode => storedNode.id).indexOf(fetchedNode.id); + + if (index !== -1) { + // If the node is already in the store, update it + updated[index] = fetchedNode; + } else { + // If the node is not in the store, add it + updated = [...updated, fetchedNode]; + } + }) + + this._nodes.next([...updated]); + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts new file mode 100644 index 0000000000..34f2b84e0b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts @@ -0,0 +1,136 @@ +export interface DocumentNode { + id: number; + key: string; + name: string; + alias: string; + icon: string; // TODO: should come from the doc type? + properties: NodeProperty[]; + data: any; // TODO: define data type + layout?: any; // TODO: define layout type - make it non-optional +} + +export interface NodeProperty { + alias: string; + label: string; + description: string; + dataTypeAlias: string; + tempValue: string; // TODO: remove this - only used for testing +} + +export const data = [ + { + id: 1, + key: '74e4008a-ea4f-4793-b924-15e02fd380d3', + name: 'Document 1', + alias: 'document1', + icon: 'document', + properties: [ + { + alias: 'myHeadline', + label: 'Textarea label', + description: 'this is a textarea property', + dataTypeAlias: 'myTextStringEditor', + tempValue: 'hello world 1' + }, + { + alias: 'myDescription', + label: 'Text string label', + description: 'This is the a text string property', + dataTypeAlias: 'myTextAreaEditor', + tempValue: 'Tex areaaaa 1' + }, + ], + data: [ + { + alias: 'myHeadline', + value: 'hello world', + }, + { + alias: 'myDescription', + value: 'Teeeeexxxt areaaaaaa', + }, + ], + /* + layout: [ + { + type: 'group', + children: [ + { + type: 'property', + alias: 'myHeadline' + }, + { + type: 'property', + alias: 'myDescription' + } + ] + } + ], + */ + }, + { + id: 2, + key: '74e4008a-ea4f-4793-b924-15e02fd380d3', + name: 'Document 2', + alias: 'document2', + icon: 'favorite', + properties: [ + { + alias: 'myHeadline', + label: 'Textarea label', + description: 'this is a textarea property', + dataTypeAlias: 'myTextStringEditor', + tempValue: 'hello world 2' + }, + { + alias: 'myDescription', + label: 'Text string label', + description: 'This is the a text string property', + dataTypeAlias: 'myTextAreaEditor', + tempValue: 'Tex areaaaa 2' + }, + ], + data: [ + { + alias: 'myHeadline', + value: 'hello world', + }, + { + alias: 'myDescription', + value: 'Teeeeexxxt areaaaaaa', + }, + ], + /* + layout: [ + { + type: 'group', + children: [ + { + type: 'property', + alias: 'myHeadline' + }, + { + type: 'property', + alias: 'myDescription' + } + ] + } + ], + */ + } +]; + +// Temp mocked database +class UmbContentData { + private _data: Array = []; + + constructor () { + this._data = data; + } + + getById (id: number) { + return this._data.find(item => item.id === id); + } +} + +export const umbContentData = new UmbContentData(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/content.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/content.handlers.ts new file mode 100644 index 0000000000..8354a74589 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/content.handlers.ts @@ -0,0 +1,17 @@ +import { rest } from 'msw'; +import { umbContentData } from '../data/content.data'; + +// TODO: add schema +export const handlers = [ + rest.get('/umbraco/backoffice/content/:id', (req, res, ctx) => { + const id = req.params.id as string; + if (!id) return; + + const int = parseInt(id); + const document = umbContentData.getById(int); + return res( + ctx.status(200), + ctx.json([document]) + ); + }), +]; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts index 2a98e94547..c973a63407 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts @@ -1,5 +1,6 @@ import { rest } from 'msw'; import { InitResponse } from '../models'; +import { handlers as contentHandlers } from './domains/content.handlers'; import { handlers as installHandlers } from './domains/install.handlers'; import { handlers as manifestsHandlers } from './domains/manifests.handlers'; import { handlers as userHandlers } from './domains/user.handlers'; @@ -15,6 +16,7 @@ export const handlers = [ }) ); }), + ...contentHandlers, ...installHandlers, ...manifestsHandlers, ...userHandlers, From 59d2386822a9abebc0f9cd2443e02818497d0d71 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 30 May 2022 20:36:55 +0200 Subject: [PATCH 2/8] delete media section for now --- src/Umbraco.Web.UI.Client/src/index.ts | 12 +---------- .../src/media/media-section.element.ts | 21 ------------------- .../src/mocks/domains/user.handlers.ts | 2 +- 3 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/media/media-section.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/index.ts b/src/Umbraco.Web.UI.Client/src/index.ts index 46dc8c6eb8..8c14284e68 100644 --- a/src/Umbraco.Web.UI.Client/src/index.ts +++ b/src/Umbraco.Web.UI.Client/src/index.ts @@ -37,16 +37,6 @@ const registerInternalManifests = async () => { weight: 50 } }, - { - type: 'section', - alias: 'Umb.Section.Media', - name: 'Media', - elementName: 'umb-media-section', - js: () => import('./media/media-section.element'), - meta: { - weight: 40 - } - }, { type: 'section', alias: 'Umb.Section.Members', @@ -87,7 +77,7 @@ const registerInternalManifests = async () => { sections: ['Umb.Section.Content'], weight: 10 } - } + }, ]; manifests.forEach((manifest: UmbExtensionManifest) => extensionRegistry.register(manifest)); diff --git a/src/Umbraco.Web.UI.Client/src/media/media-section.element.ts b/src/Umbraco.Web.UI.Client/src/media/media-section.element.ts deleted file mode 100644 index edb74eb32c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/media/media-section.element.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; -import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; -import { css, html, LitElement } from 'lit'; - -@defineElement('umb-media-section') -export class UmbMediaSection extends LitElement { - static styles = [ - UUITextStyles, - css``, - ]; - - render() { - return html`
Media Section
`; - } -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-media-section': UmbMediaSection; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts index d3c4c30cb0..57df91e22f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts @@ -46,7 +46,7 @@ export const handlers = [ return res( ctx.status(200), ctx.json({ - sections: ['Umb.Section.Content', 'Umb.Section.Media', 'Umb.Section.Settings', 'My.Section.Custom'], + sections: ['Umb.Section.Content', 'Umb.Section.Settings', 'My.Section.Custom'], }) ); }), From a41e03ecd1dafb95c8b737c3d3dd910e3c42c67f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 30 May 2022 20:43:44 +0200 Subject: [PATCH 3/8] remove unused fullpath --- src/Umbraco.Web.UI.Client/src/core/router/router.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/router/router.ts b/src/Umbraco.Web.UI.Client/src/core/router/router.ts index 5068b7aa65..cd1cbc2229 100644 --- a/src/Umbraco.Web.UI.Client/src/core/router/router.ts +++ b/src/Umbraco.Web.UI.Client/src/core/router/router.ts @@ -12,7 +12,6 @@ export interface UmbRoute { export interface UmbRouteLocation { pathname: string; params: Record; - fullPath: string; route: UmbRoute; } @@ -75,7 +74,6 @@ export class UmbRouter { } public push(pathname: string) { - history.pushState(null, '', pathname); this._navigate(pathname); } @@ -137,7 +135,6 @@ export class UmbRouter { location = { pathname: result.pathname.input, params: result.pathname.groups, - fullPath: result.pathname.input, route, }; } From 394fa7f107142e6e45ad676f7c32c0347e03c8ed Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 30 May 2022 20:47:17 +0200 Subject: [PATCH 4/8] move api and models into core --- src/Umbraco.Web.UI.Client/src/app.ts | 2 +- src/Umbraco.Web.UI.Client/src/auth/login/login.element.ts | 2 +- .../src/backoffice/backoffice-header.element.ts | 2 +- src/Umbraco.Web.UI.Client/src/{ => core}/api/fetcher.ts | 2 +- src/Umbraco.Web.UI.Client/src/{ => core}/models/index.ts | 2 +- .../src/installer/installer-database.element.ts | 2 +- .../src/installer/installer-user.element.ts | 2 +- src/Umbraco.Web.UI.Client/src/installer/installer.element.ts | 4 ++-- .../src/mocks/domains/install.handlers.ts | 2 +- src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts | 2 +- .../src/mocks/domains/version.handlers.ts | 2 +- src/Umbraco.Web.UI.Client/src/mocks/handlers.ts | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) rename src/Umbraco.Web.UI.Client/src/{ => core}/api/fetcher.ts (92%) rename src/Umbraco.Web.UI.Client/src/{ => core}/models/index.ts (91%) diff --git a/src/Umbraco.Web.UI.Client/src/app.ts b/src/Umbraco.Web.UI.Client/src/app.ts index 2d86a0f1b1..3e9aa9c0b4 100644 --- a/src/Umbraco.Web.UI.Client/src/app.ts +++ b/src/Umbraco.Web.UI.Client/src/app.ts @@ -14,7 +14,7 @@ import { css, html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { Subscription } from 'rxjs'; -import { getInitStatus } from './api/fetcher'; +import { getInitStatus } from './core/api/fetcher'; import { UmbContextProviderMixin } from './core/context'; import { isUmbRouterBeforeEnterEvent, diff --git a/src/Umbraco.Web.UI.Client/src/auth/login/login.element.ts b/src/Umbraco.Web.UI.Client/src/auth/login/login.element.ts index 1af0293e7d..f81f4f4210 100644 --- a/src/Umbraco.Web.UI.Client/src/auth/login/login.element.ts +++ b/src/Umbraco.Web.UI.Client/src/auth/login/login.element.ts @@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; -import { postUserLogin } from '../../api/fetcher'; +import { postUserLogin } from '../../core/api/fetcher'; import { UmbContextConsumerMixin } from '../../core/context'; import { UmbRouter } from '../../core/router'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts index 818be6ee88..d40e137b28 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts @@ -4,7 +4,7 @@ import { css, CSSResultGroup, html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; -import { getUserSections } from '../api/fetcher'; +import { getUserSections } from '../core/api/fetcher'; import { UmbExtensionManifest, UmbManifestSectionMeta } from '../core/extension'; import { UmbRouteLocation, UmbRouter } from '../core/router'; import { UmbSectionContext } from '../section.context'; diff --git a/src/Umbraco.Web.UI.Client/src/api/fetcher.ts b/src/Umbraco.Web.UI.Client/src/core/api/fetcher.ts similarity index 92% rename from src/Umbraco.Web.UI.Client/src/api/fetcher.ts rename to src/Umbraco.Web.UI.Client/src/core/api/fetcher.ts index 5f4e8c0d0b..c4c3b471f4 100644 --- a/src/Umbraco.Web.UI.Client/src/api/fetcher.ts +++ b/src/Umbraco.Web.UI.Client/src/core/api/fetcher.ts @@ -1,6 +1,6 @@ import { Fetcher } from 'openapi-typescript-fetch'; -import { paths } from '../../schemas/generated-schema'; +import { paths } from '../../../schemas/generated-schema'; const fetcher = Fetcher.for(); diff --git a/src/Umbraco.Web.UI.Client/src/models/index.ts b/src/Umbraco.Web.UI.Client/src/core/models/index.ts similarity index 91% rename from src/Umbraco.Web.UI.Client/src/models/index.ts rename to src/Umbraco.Web.UI.Client/src/core/models/index.ts index 3cf0865934..5bf240ffe8 100644 --- a/src/Umbraco.Web.UI.Client/src/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/core/models/index.ts @@ -1,4 +1,4 @@ -import { components } from '../../schemas/generated-schema'; +import { components } from '../../../schemas/generated-schema'; export type PostInstallRequest = components['schemas']['UmbracoPerformInstallRequest']; export type InitResponse = components['schemas']['InitResponse']; diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer-database.element.ts b/src/Umbraco.Web.UI.Client/src/installer/installer-database.element.ts index a40adc1cfd..44a16c682c 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer-database.element.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/installer-database.element.ts @@ -1,7 +1,7 @@ import { css, CSSResultGroup, html, LitElement } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UUIBooleanInputEvent, UUISelectElement } from '@umbraco-ui/uui'; -import { PostInstallRequest, UmbracoInstallerDatabaseModel } from '../models'; +import { PostInstallRequest, UmbracoInstallerDatabaseModel } from '../core/models'; @customElement('umb-installer-database') export class UmbInstallerDatabase extends LitElement { diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer-user.element.ts b/src/Umbraco.Web.UI.Client/src/installer/installer-user.element.ts index 4bbc979476..1c041a5f32 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer-user.element.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/installer-user.element.ts @@ -1,6 +1,6 @@ import { css, CSSResultGroup, html, LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { PostInstallRequest, UmbracoInstallerUserModel } from '../models'; +import { PostInstallRequest, UmbracoInstallerUserModel } from '../core/models'; @customElement('umb-installer-user') export class UmbInstallerUser extends LitElement { diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.element.ts b/src/Umbraco.Web.UI.Client/src/installer/installer.element.ts index d71e2b0e02..2da2509bb1 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.element.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.element.ts @@ -6,8 +6,8 @@ import './installer-user.element'; import { css, CSSResultGroup, html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { getInstall, postInstall } from '../api/fetcher'; -import { PostInstallRequest, UmbracoInstaller } from '../models'; +import { getInstall, postInstall } from '../core/api/fetcher'; +import { PostInstallRequest, UmbracoInstaller } from '../core/models'; @customElement('umb-installer') export class UmbInstaller extends LitElement { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/install.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/install.handlers.ts index 85315287d0..3a6862402f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/domains/install.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/install.handlers.ts @@ -1,5 +1,5 @@ import { rest } from 'msw'; -import { UmbracoInstaller } from '../../models'; +import { UmbracoInstaller } from '../../core/models'; export const handlers = [ rest.get('/umbraco/backoffice/install', (_req, res, ctx) => { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts index 57df91e22f..c870781a0f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/user.handlers.ts @@ -1,5 +1,5 @@ import { rest } from 'msw'; -import { AllowedSectionsResponse, UserResponse } from '../../models'; +import { AllowedSectionsResponse, UserResponse } from '../../core/models'; export const handlers = [ rest.post('/umbraco/backoffice/user/login', (_req, res, ctx) => { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/version.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/version.handlers.ts index ad226310b8..c6af657ca8 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/domains/version.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/version.handlers.ts @@ -1,5 +1,5 @@ import { rest } from 'msw'; -import { VersionResponse } from '../../models'; +import { VersionResponse } from '../../core/models'; // TODO: set up schema export const handlers = [ diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts index c973a63407..28f80e2f09 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers.ts @@ -1,5 +1,5 @@ import { rest } from 'msw'; -import { InitResponse } from '../models'; +import { InitResponse } from '../core/models'; import { handlers as contentHandlers } from './domains/content.handlers'; import { handlers as installHandlers } from './domains/install.handlers'; import { handlers as manifestsHandlers } from './domains/manifests.handlers'; From 6c9fae3008f81aa117744b9534202760bacfc80b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 31 May 2022 09:23:04 +0200 Subject: [PATCH 5/8] pretty urls --- src/Umbraco.Web.UI.Client/src/app.ts | 2 +- .../src/backoffice/backoffice-header.element.ts | 6 ++---- .../src/content/content-dashboards.element.ts | 9 ++++----- .../src/core/extension/extension.registry.ts | 4 +++- src/Umbraco.Web.UI.Client/src/index.ts | 5 +++++ .../src/mocks/domains/manifests.handlers.ts | 1 + 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/app.ts b/src/Umbraco.Web.UI.Client/src/app.ts index 3e9aa9c0b4..4ef3a5db44 100644 --- a/src/Umbraco.Web.UI.Client/src/app.ts +++ b/src/Umbraco.Web.UI.Client/src/app.ts @@ -125,7 +125,7 @@ export class UmbApp extends UmbContextProviderMixin(LitElement) { if (!this._isAuthorized() || window.location.pathname === '/install') { this._router.push('/login'); } else { - const next = window.location.pathname === '/' ? '/section/Content' : window.location.pathname; + const next = window.location.pathname === '/' ? '/section/content' : window.location.pathname; this._router.push(next); } } catch (error) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts index d40e137b28..ecaa9e793b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts @@ -133,7 +133,7 @@ export class UmbBackofficeHeader extends UmbContextConsumerMixin(LitElement) { } // TODO: this could maybe be handled by an anchor tag - this._router?.push(`/section/${section.name}`); + this._router?.push(`/section/${section.meta.pathname}`); this._sectionContext?.setCurrent(section.alias); } @@ -177,9 +177,7 @@ export class UmbBackofficeHeader extends UmbContextConsumerMixin(LitElement) { .subscribe((sectionExtensions: any) => { this._sections = sectionExtensions.filter((section: any) => this._allowedSection.includes(section.alias)); this._visibleSections = this._sections; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const currentSectionAlias = this._sections.find(section => section.name === this._location?.params?.section)?.alias; + const currentSectionAlias = this._sections.find(section => section.meta.pathname === this._location?.params?.section)?.alias; if (!currentSectionAlias) return; this._sectionContext?.setCurrent(currentSectionAlias); }); diff --git a/src/Umbraco.Web.UI.Client/src/content/content-dashboards.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-dashboards.element.ts index 62ff4e7929..f14f71af3a 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-dashboards.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-dashboards.element.ts @@ -72,15 +72,15 @@ export class UmbContentDashboards extends UmbContextConsumerMixin(LitElement) { // TODO: Temp redirect solution if (dashboardLocation === 'undefined') { - this._router?.push(`/section/${sectionLocation}/dashboard/${this._dashboards[0].name}`); + this._router?.push(`/section/${sectionLocation}/dashboard/${this._dashboards[0].meta.pathname}`); this._setCurrent(this._dashboards[0]); return; } - const dashboard = this._dashboards.find(dashboard => dashboard.name === dashboardLocation); + const dashboard = this._dashboards.find(dashboard => dashboard.meta.pathname === dashboardLocation); if (!dashboard) { - this._router?.push(`/section/${sectionLocation}/dashboard/${this._dashboards[0].name}`); + this._router?.push(`/section/${sectionLocation}/dashboard/${this._dashboards[0].meta.pathname}`); this._setCurrent(this._dashboards[0]); return; } @@ -92,7 +92,7 @@ export class UmbContentDashboards extends UmbContextConsumerMixin(LitElement) { private _handleTabClick(e: PointerEvent, dashboard: UmbExtensionManifest) { // TODO: this could maybe be handled by an anchor tag const section = this._location?.params?.section; - this._router?.push(`/section/${section}/dashboard/${dashboard.name}`); + this._router?.push(`/section/${section}/dashboard/${dashboard.meta.pathname}`); this._setCurrent(dashboard); } @@ -124,7 +124,6 @@ export class UmbContentDashboards extends UmbContextConsumerMixin(LitElement) { label=${dashboard.name} ?active="${this._current === dashboard.name}" @click="${(e: PointerEvent) => this._handleTabClick(e, dashboard)}"> - `)} ${ this._outlet } diff --git a/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts b/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts index 1f5aebb181..ee8a78929c 100644 --- a/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts +++ b/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts @@ -10,7 +10,7 @@ export interface UmbExtensionManifestBase { name: string; js?: string | (() => Promise); elementName?: string; - meta: unknown; + meta: any; } export type UmbExtensionManifestSection = { @@ -31,6 +31,7 @@ export type UmbExtensionManifestDashboard = { export type UmbExtensionManifest = UmbExtensionManifestBase | UmbExtensionManifestSection | UmbExtensionManifestPropertyEditor; export interface UmbManifestSectionMeta { + pathname: string, // TODO: how to we want to support pretty urls? weight: number; } @@ -43,6 +44,7 @@ export interface UmbManifestPropertyEditorMeta { export interface UmbManifestDashboardMeta { sections: Array; + pathname: string; // TODO: how to we want to support pretty urls? weight: number; } diff --git a/src/Umbraco.Web.UI.Client/src/index.ts b/src/Umbraco.Web.UI.Client/src/index.ts index 8c14284e68..8900c9de0f 100644 --- a/src/Umbraco.Web.UI.Client/src/index.ts +++ b/src/Umbraco.Web.UI.Client/src/index.ts @@ -34,6 +34,7 @@ const registerInternalManifests = async () => { elementName: 'umb-content-section', js: () => import('./content/content-section.element'), meta: { + pathname: 'content', // TODO: how to we want to support pretty urls? weight: 50 } }, @@ -43,6 +44,7 @@ const registerInternalManifests = async () => { name: 'Members', elementName: 'umb-members-section', meta: { + pathname: 'members', weight: 30 } }, @@ -53,6 +55,7 @@ const registerInternalManifests = async () => { elementName: 'umb-settings-section', js: () => import('./settings/settings-section.element'), meta: { + pathname: 'settings', // TODO: how to we want to support pretty urls? weight: 20 } }, @@ -64,6 +67,7 @@ const registerInternalManifests = async () => { js: () => import('./dashboards/dashboard-welcome.element'), meta: { sections: ['Umb.Section.Content'], + pathname: 'welcome', // TODO: how to we want to support pretty urls? weight: 20 } }, @@ -75,6 +79,7 @@ const registerInternalManifests = async () => { js: () => import('./dashboards/dashboard-redirect-management.element'), meta: { sections: ['Umb.Section.Content'], + pathname: 'redirect-management', // TODO: how to we want to support pretty urls? weight: 10 } }, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts index e25e02dfc0..13e5bd0707 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts @@ -14,6 +14,7 @@ export const handlers = [ name: 'Custom', elementName: 'umb-custom-section', meta: { + pathname: 'my-custom', weight: 30, }, }, From 33f194db393384c7f4db810b61cd18e7d6ff9ceb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 31 May 2022 09:44:03 +0200 Subject: [PATCH 6/8] remove fixed todo --- .../src/backoffice/backoffice.element.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts index 3e0c6b7890..013de137c6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -20,18 +20,9 @@ export class UmbBackoffice extends LitElement { font-size: 14px; box-sizing: border-box; } - - #main { - display: flex; - flex: 1; - overflow: hidden; - } `, ]; - // TODO: main div and then side and main again within? I propose the backoffice-main begin renamed to something less main-ish. - // TODO: I would think umb-backoffice-header would be outside the router outlet? so its always present. - render() { return html` From cf12e684a9a2aa272fd35765cf90f9d53c59ecb9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 31 May 2022 10:41:29 +0200 Subject: [PATCH 7/8] split sections and tools from header element --- .../backoffice-header-sections.element.ts | 199 ++++++++++++++++ .../backoffice-header-tools.element.ts | 44 ++++ .../backoffice/backoffice-header.element.ts | 216 +----------------- .../src/backoffice/backoffice.element.ts | 8 +- 4 files changed, 255 insertions(+), 212 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-sections.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-tools.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-sections.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-sections.element.ts new file mode 100644 index 0000000000..e34c0ce43a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-sections.element.ts @@ -0,0 +1,199 @@ +import { Subscription } from 'rxjs'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { css, CSSResultGroup, html, LitElement } from 'lit'; +import { customElement, state } from 'lit/decorators.js'; +import { when } from 'lit/directives/when.js'; + +import { getUserSections } from '../core/api/fetcher'; +import { UmbExtensionManifest } from '../core/extension'; +import { UmbRouteLocation, UmbRouter } from '../core/router'; +import { UmbSectionContext } from '../section.context'; +import { UmbContextConsumerMixin } from '../core/context'; + +@customElement('umb-backoffice-header-sections') +export class UmbBackofficeHeaderSections extends UmbContextConsumerMixin(LitElement) { + static styles: CSSResultGroup = [ + UUITextStyles, + css` + #tabs { + color: var(--uui-look-primary-contrast); + height: 60px; + font-size: 16px; + --uui-tab-text: var(--uui-look-primary-contrast); + --uui-tab-text-hover: var(--uui-look-primary-contrast-hover); + --uui-tab-text-active: var(--uui-interface-active); + --uui-tab-background: var(--uui-look-primary-surface); + } + + #dropdown { + background-color: white; + border-radius: var(--uui-border-radius); + width: 100%; + height: 100%; + box-sizing: border-box; + box-shadow: var(--uui-shadow-depth-3); + min-width: 200px; + color: black; /* Change to variable */ + } + `, + ]; + + @state() + private _open = false; + + @state() + private _allowedSection: Array = []; + + @state() + private _sections: Array = []; + + @state() + private _visibleSections: Array = []; + + @state() + private _extraSections: Array = []; + + @state() + private _currentSectionAlias = ''; + + private _router?: UmbRouter; + private _sectionContext?: UmbSectionContext; + private _sectionSubscription?: Subscription; + private _currentSectionSubscription?: Subscription; + private _locationSubscription?: Subscription; + private _location? : UmbRouteLocation; + + constructor () { + super(); + + this.consumeContext('umbRouter', (_instance: UmbRouter) => { + this._router = _instance; + this._useLocation(); + }); + + this.consumeContext('umbSectionContext', (_instance: UmbSectionContext) => { + this._sectionContext = _instance; + this._useCurrentSection(); + this._useSections(); + }); + } + + private _handleMore(e: MouseEvent) { + e.stopPropagation(); + this._open = !this._open; + } + + private _handleTabClick(e: PointerEvent, section: UmbExtensionManifest) { + const tab = e.currentTarget as any; + + // TODO: we need to be able to prevent the tab from setting the active state + if (tab.id === 'moreTab') { + return; + } + + // TODO: this could maybe be handled by an anchor tag + this._router?.push(`/section/${section.meta.pathname}`); + this._sectionContext?.setCurrent(section.alias); + } + + private _handleLabelClick(e: PointerEvent) { + const label = (e.target as any).label; + + // TODO: set current section + //this._sectionContext?.setCurrent(section.alias); + + const moreTab = this.shadowRoot?.getElementById('moreTab'); + moreTab?.setAttribute('active', 'true'); + + this._open = false; + } + + private _useLocation () { + this._locationSubscription?.unsubscribe(); + + this._locationSubscription = this._router?.location + .subscribe((location: UmbRouteLocation) => { + this._location = location; + }); + } + + private _useCurrentSection () { + this._currentSectionSubscription?.unsubscribe(); + + this._currentSectionSubscription = this._sectionContext?.getCurrent() + .subscribe(section => { + this._currentSectionAlias = section.alias; + }); + } + + private async _useSections() { + this._sectionSubscription?.unsubscribe(); + + const { data } = await getUserSections({}); + this._allowedSection = data.sections; + + this._sectionSubscription = this._sectionContext?.getSections() + .subscribe((sectionExtensions: any) => { + this._sections = sectionExtensions.filter((section: any) => this._allowedSection.includes(section.alias)); + this._visibleSections = this._sections; + const currentSectionAlias = this._sections.find(section => section.meta.pathname === this._location?.params?.section)?.alias; + if (!currentSectionAlias) return; + this._sectionContext?.setCurrent(currentSectionAlias); + }); + } + + disconnectedCallback(): void { + super.disconnectedCallback(); + this._locationSubscription?.unsubscribe(); + this._sectionSubscription?.unsubscribe(); + this._currentSectionSubscription?.unsubscribe(); + } + + render() { + return html` + + ${this._visibleSections.map( + (section) => html` + + ` + )} + ${this._renderExtraSections()} + + `; + } + + private _renderExtraSections() { + return when( + this._extraSections.length > 0, + () => html` + + + + + + + + + + ` + ); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-backoffice-header-sections': UmbBackofficeHeaderSections; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-tools.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-tools.element.ts new file mode 100644 index 0000000000..fdb90b3ce9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header-tools.element.ts @@ -0,0 +1,44 @@ + +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { css, CSSResultGroup, html, LitElement } from 'lit'; +import { customElement } from 'lit/decorators.js'; + +@customElement('umb-backoffice-header-tools') +export class UmbBackofficeHeaderTools extends LitElement { + static styles: CSSResultGroup = [ + UUITextStyles, + css` + #tools { + display: flex; + align-items: center; + gap: var(--uui-size-space-2); + } + + .tool { + font-size: 18px; + } + `, + ]; + + render() { + return html` +
+ + + + + + + + + +
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-backoffice-header-tools': UmbBackofficeHeaderTools; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts index ecaa9e793b..393cb15405 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice-header.element.ts @@ -1,25 +1,19 @@ -import { Subscription } from 'rxjs'; + import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, CSSResultGroup, html, LitElement } from 'lit'; -import { customElement, state } from 'lit/decorators.js'; -import { when } from 'lit/directives/when.js'; - -import { getUserSections } from '../core/api/fetcher'; -import { UmbExtensionManifest, UmbManifestSectionMeta } from '../core/extension'; -import { UmbRouteLocation, UmbRouter } from '../core/router'; -import { UmbSectionContext } from '../section.context'; -import { UmbContextConsumerMixin } from '../core/context'; - -// TODO: umb or not umb in file name? +import { customElement } from 'lit/decorators.js'; +import './backoffice-header-sections.element'; +import './backoffice-header-tools.element'; @customElement('umb-backoffice-header') -export class UmbBackofficeHeader extends UmbContextConsumerMixin(LitElement) { +export class UmbBackofficeHeader extends LitElement { static styles: CSSResultGroup = [ UUITextStyles, css` :host { width: 100%; } + #appHeader { background-color: var(--uui-look-primary-surface); display: flex; @@ -41,181 +35,10 @@ export class UmbBackofficeHeader extends UmbContextConsumerMixin(LitElement) { #sections { flex: 1 1 auto; - display: flex; - align-items: center; - gap: var(--uui-size-space-2); - } - - #tabs { - color: var(--uui-look-primary-contrast); - height: 60px; - font-size: 16px; - --uui-tab-text: var(--uui-look-primary-contrast); - --uui-tab-text-hover: var(--uui-look-primary-contrast-hover); - --uui-tab-text-active: var(--uui-interface-active); - --uui-tab-background: var(--uui-look-primary-surface); - } - - #tools { - display: flex; - align-items: center; - gap: var(--uui-size-space-2); - } - - .tool { - font-size: 18px; - } - - #dropdown { - background-color: white; - border-radius: var(--uui-border-radius); - width: 100%; - height: 100%; - box-sizing: border-box; - box-shadow: var(--uui-shadow-depth-3); - min-width: 200px; - color: black; /* Change to variable */ } `, ]; - @state() - private _open = false; - - @state() - private _allowedSection: Array = []; - - @state() - private _sections: Array = []; - - @state() - private _visibleSections: Array = []; - - @state() - private _extraSections: Array = []; - - @state() - private _currentSectionAlias = ''; - - private _router?: UmbRouter; - private _sectionContext?: UmbSectionContext; - private _sectionSubscription?: Subscription; - private _currentSectionSubscription?: Subscription; - private _locationSubscription?: Subscription; - private _location? : UmbRouteLocation; - - constructor () { - super(); - - this.consumeContext('umbRouter', (_instance: UmbRouter) => { - this._router = _instance; - this._useLocation(); - }); - - this.consumeContext('umbSectionContext', (_instance: UmbSectionContext) => { - this._sectionContext = _instance; - this._useCurrentSection(); - this._useSections(); - }); - } - - private _handleMore(e: MouseEvent) { - e.stopPropagation(); - this._open = !this._open; - } - - private _handleTabClick(e: PointerEvent, section: UmbExtensionManifest) { - const tab = e.currentTarget as any; - - // TODO: we need to be able to prevent the tab from setting the active state - if (tab.id === 'moreTab') { - return; - } - - // TODO: this could maybe be handled by an anchor tag - this._router?.push(`/section/${section.meta.pathname}`); - this._sectionContext?.setCurrent(section.alias); - } - - private _handleLabelClick(e: PointerEvent) { - const label = (e.target as any).label; - - // TODO: set current section - //this._sectionContext?.setCurrent(section.alias); - - const moreTab = this.shadowRoot?.getElementById('moreTab'); - moreTab?.setAttribute('active', 'true'); - - this._open = false; - } - - private _useLocation () { - this._locationSubscription?.unsubscribe(); - - this._locationSubscription = this._router?.location - .subscribe((location: UmbRouteLocation) => { - this._location = location; - }); - } - - private _useCurrentSection () { - this._currentSectionSubscription?.unsubscribe(); - - this._currentSectionSubscription = this._sectionContext?.getCurrent() - .subscribe(section => { - this._currentSectionAlias = section.alias; - }); - } - - private async _useSections() { - this._sectionSubscription?.unsubscribe(); - - const { data } = await getUserSections({}); - this._allowedSection = data.sections; - - this._sectionSubscription = this._sectionContext?.getSections() - .subscribe((sectionExtensions: any) => { - this._sections = sectionExtensions.filter((section: any) => this._allowedSection.includes(section.alias)); - this._visibleSections = this._sections; - const currentSectionAlias = this._sections.find(section => section.meta.pathname === this._location?.params?.section)?.alias; - if (!currentSectionAlias) return; - this._sectionContext?.setCurrent(currentSectionAlias); - }); - } - - disconnectedCallback(): void { - super.disconnectedCallback(); - this._locationSubscription?.unsubscribe(); - this._sectionSubscription?.unsubscribe(); - this._currentSectionSubscription?.unsubscribe(); - } - - private _renderExtraSections() { - return when( - this._extraSections.length > 0, - () => html` - - - - - - - - - - ` - ); - } - render() { return html`
@@ -223,31 +46,8 @@ export class UmbBackofficeHeader extends UmbContextConsumerMixin(LitElement) { Umbraco -
- - ${this._visibleSections.map( - (section) => html` - - ` - )} - ${this._renderExtraSections()} - -
- -
- - - - - - - - - -
+ +
`; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts index 013de137c6..7a67950cac 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -1,11 +1,11 @@ -import './backoffice-header.element'; -import './backoffice-sidebar.element'; -import './backoffice-main.element'; - import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, LitElement } from 'lit'; +import './backoffice-header.element'; +import './backoffice-sidebar.element'; +import './backoffice-main.element'; + @defineElement('umb-backoffice') export class UmbBackoffice extends LitElement { static styles = [ From 41d55490053a5a6a7b8f8c19651433910243602c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 31 May 2022 12:01:48 +0200 Subject: [PATCH 8/8] nodes.store --- .../src/content/content-editor.element.ts | 8 ++++---- .../src/content/content-section.element.ts | 4 ++-- .../content.service.ts => core/stores/nodes.store.ts} | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) rename src/Umbraco.Web.UI.Client/src/{content/content.service.ts => core/stores/nodes.store.ts} (88%) diff --git a/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts index d8c962bf44..387b6ca4d0 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts @@ -2,7 +2,7 @@ import { css, html, LitElement } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbContextConsumerMixin } from '../core/context'; -import { UmbContentService } from './content.service'; +import { UmbNodesStore } from '../core/stores/nodes.store'; import { Subscription } from 'rxjs'; import { DocumentNode } from '../mocks/data/content.data'; @@ -51,13 +51,13 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) { @state() _node?: DocumentNode; - private _contentService?: UmbContentService; + private _contentService?: UmbNodesStore; private _nodeSubscription?: Subscription; constructor () { super(); - this.consumeContext('umbContentService', (contentService: UmbContentService) => { + this.consumeContext('umbContentService', (contentService: UmbNodesStore) => { this._contentService = contentService; this._useNode(); }); @@ -75,7 +75,7 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) { this._nodeSubscription?.unsubscribe(); this._nodeSubscription = this._contentService?.getById(parseInt(this.id)).subscribe(node => { - if (!node) return; + if (!node) return; // TODO: Handle nicely if there is no node. this._node = node; }); } diff --git a/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts index 7982d59352..f07f64d2b3 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts @@ -3,7 +3,7 @@ import { css, html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { UmbContextConsumerMixin, UmbContextProviderMixin } from '../core/context'; import { UmbRouteLocation, UmbRouter } from '../core/router'; -import { UmbContentService } from './content.service'; +import { UmbNodesStore } from '../core/stores/nodes.store'; import { Subscription } from 'rxjs'; import './content-tree.element'; @@ -30,7 +30,7 @@ export class UmbContentSection extends UmbContextProviderMixin(UmbContextConsume constructor () { super(); - this.provideContext('umbContentService', new UmbContentService()); + this.provideContext('umbContentService', new UmbNodesStore()); this.consumeContext('umbRouter', (_instance: UmbRouter) => { this._router = _instance; diff --git a/src/Umbraco.Web.UI.Client/src/content/content.service.ts b/src/Umbraco.Web.UI.Client/src/core/stores/nodes.store.ts similarity index 88% rename from src/Umbraco.Web.UI.Client/src/content/content.service.ts rename to src/Umbraco.Web.UI.Client/src/core/stores/nodes.store.ts index 5c37053309..6c733470eb 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/nodes.store.ts @@ -1,7 +1,8 @@ import { BehaviorSubject, map, Observable } from 'rxjs'; -import { DocumentNode } from '../mocks/data/content.data'; +import { DocumentNode } from '../../mocks/data/content.data'; + +export class UmbNodesStore { -export class UmbContentService { private _nodes: BehaviorSubject> = new BehaviorSubject(>[]); public readonly nodes: Observable> = this._nodes.asObservable(); @@ -16,7 +17,7 @@ export class UmbContentService { return this.nodes.pipe(map(((nodes: Array) => nodes.find((node: DocumentNode) => node.id === id) || null))); } - _updateStore (fetchedNodes: Array) { + private _updateStore (fetchedNodes: Array) { const storedNodes = this._nodes.getValue(); let updated: any = [...storedNodes];