diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/README.md b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/README.md index 04336ea3dc..8ccc705531 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/README.md +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/README.md @@ -1,8 +1,19 @@ -# Workspace Context Counter Example +# Workspace Extensions Complete Example -This example demonstrates the essence of the Workspace Context. +The Workspace Context serves as the central communication hub for all workspace extensions. In this example, the context manages a counter that can be manipulated and displayed by different extension types, showcasing the power of shared state management in workspace extensions. -The Workspace Context is available for everything within the Workspace, giving any extension within the ability to communicate through this. -In this example, the Workspace Context houses a counter, which can be incremented by a Workspace Action and shown in the Workspace View. +## Extension types included -To demonstrate this, the example comes with: A Workspace Context, A Workspace Action and a Workspace View. +This complete example includes: + +- **Workspace Context** - Manages shared counter state and provides communication between extensions +- **Workspace Action** - Primary "Increment" button that increases the counter value +- **Workspace Action Menu Item** - "Reset Counter" dropdown option that resets the counter to zero +- **Workspace View** - Dedicated tab that displays the current counter value +- **Workspace Footer App** - Status indicator showing the counter value in the workspace footer + +## How it works + +All extensions communicate through the shared workspace context. When you increment or reset the counter using the actions, the workspace view and footer app automatically update to show the new value, demonstrating reactive state management across workspace extensions. + +This pattern shows how workspace extensions can work together to create cohesive functionality within Umbraco workspaces. \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-status-footer-app.element.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-status-footer-app.element.ts new file mode 100644 index 0000000000..5910e5c7a2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-status-footer-app.element.ts @@ -0,0 +1,38 @@ +import { customElement, html, state, LitElement } from '@umbraco-cms/backoffice/external/lit'; +import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; +import { EXAMPLE_COUNTER_CONTEXT } from './counter-workspace-context.js'; + +@customElement('example-counter-status-footer-app') +export class ExampleCounterStatusFooterAppElement extends UmbElementMixin(LitElement) { + @state() + private _counter = 0; + + constructor() { + super(); + this.#observeCounter(); + } + + async #observeCounter() { + const context = await this.getContext(EXAMPLE_COUNTER_CONTEXT); + if (!context) return; + + this.observe( + context.counter, + (counter: number) => { + this._counter = counter; + }, + ); + } + + override render() { + return html`Counter: ${this._counter}`; + } +} + +export default ExampleCounterStatusFooterAppElement; + +declare global { + interface HTMLElementTagNameMap { + 'example-counter-status-footer-app': ExampleCounterStatusFooterAppElement; + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts index 5f3d813f54..203a6f1328 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-context.ts @@ -17,6 +17,10 @@ export class WorkspaceContextCounterElement extends UmbContextBase { increment() { this.#counter.setValue(this.#counter.value + 1); } + + reset() { + this.#counter.setValue(0); + } } // Declare a api export, so Extension Registry can initialize this class: diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/index.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/index.ts index edc248b4a9..0f169bc244 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/index.ts +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/index.ts @@ -50,4 +50,30 @@ export const manifests: Array = [ }, ], }, + { + type: 'workspaceActionMenuItem', + kind: 'default', + alias: 'example.workspaceActionMenuItem.resetCounter', + name: 'Reset Counter Menu Item', + api: () => import('./reset-counter-menu-item.action.js'), + forWorkspaceActions: 'example.workspaceAction.incrementor', + weight: 100, + meta: { + label: 'Reset Counter', + icon: 'icon-refresh', + }, + }, + { + type: 'workspaceFooterApp', + alias: 'example.workspaceFooterApp.counterStatus', + name: 'Counter Status Footer App', + element: () => import('./counter-status-footer-app.element.js'), + weight: 900, + conditions: [ + { + alias: UMB_WORKSPACE_CONDITION_ALIAS, + match: 'Umb.Workspace.Document', + }, + ], + }, ]; diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/reset-counter-menu-item.action.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/reset-counter-menu-item.action.ts new file mode 100644 index 0000000000..e6db48c8bd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/reset-counter-menu-item.action.ts @@ -0,0 +1,20 @@ +import { EXAMPLE_COUNTER_CONTEXT } from './counter-workspace-context.js'; +import { UmbWorkspaceActionMenuItemBase } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceActionMenuItem } from '@umbraco-cms/backoffice/workspace'; + +export class ExampleResetCounterMenuItemAction extends UmbWorkspaceActionMenuItemBase implements UmbWorkspaceActionMenuItem { + /** + * This method is executed when the menu item is clicked + */ + override async execute() { + const context = await this.getContext(EXAMPLE_COUNTER_CONTEXT); + if (!context) { + throw new Error('Could not get the counter context'); + } + + // Reset the counter to 0 + context.reset(); + } +} + +export const api = ExampleResetCounterMenuItemAction; \ No newline at end of file