From e2fd0efc6460bf13d7b70b130a52ed78cb4f945b Mon Sep 17 00:00:00 2001 From: chintankavathia Date: Tue, 5 May 2026 15:29:27 +0530 Subject: [PATCH] chore(dashboards-demo): add native web component widget to showcase framework-agnostic integration Add `native-note-widget` and `native-note-widget-editor` as vanilla custom elements. Registered in the flexible dashboard widget catalog alongside existing Angular-based web component widgets. --- .../pages/dashboard/dashboard.component.ts | 19 ++ .../webcomponents/src/app/app.module.ts | 1 + .../native-note-widget/native-note-widget.ts | 162 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 projects/dashboards-demo/webcomponents/src/app/native-note-widget/native-note-widget.ts diff --git a/projects/dashboards-demo/src/app/pages/dashboard/dashboard.component.ts b/projects/dashboards-demo/src/app/pages/dashboard/dashboard.component.ts index a00d8765a2..818f3efbfe 100644 --- a/projects/dashboards-demo/src/app/pages/dashboard/dashboard.component.ts +++ b/projects/dashboards-demo/src/app/pages/dashboard/dashboard.component.ts @@ -94,6 +94,25 @@ export class DashboardPageComponent { } } }, + { + id: 'native-note-widget-web-component', + name: 'Note (native web-component)', + description: + 'Framework-agnostic native web component widget showcasing integration of vanilla custom elements in the flexible dashboard', + componentFactory: { + factoryType: 'web-component', + url: `${environment.webComponentsBaseUrl}/webcomponent-widgets.js`, + componentName: 'native-note-widget', + editorComponentName: 'native-note-widget-editor' + }, + defaults: { + width: 6, + height: 2 + }, + payload: { + message: 'Private notes (native web-component)' + } + }, DOWNLOAD_WIDGET, UPLOAD_WIDGET ]; diff --git a/projects/dashboards-demo/webcomponents/src/app/app.module.ts b/projects/dashboards-demo/webcomponents/src/app/app.module.ts index 8443f0f4c4..e40c71cd1b 100644 --- a/projects/dashboards-demo/webcomponents/src/app/app.module.ts +++ b/projects/dashboards-demo/webcomponents/src/app/app.module.ts @@ -12,6 +12,7 @@ import { SiThemeService } from '@siemens/element-ng/theme'; import { ChartWidgetComponent } from './chart-widget/chart-widget.component'; import { ContactWidgetEditorComponent } from './contact-widget-editor/contact-widget-editor.component'; import { ContactWidgetComponent } from './contact-widget/contact-widget.component'; +import './native-note-widget/native-note-widget'; import { NoteWidgetEditorComponent } from './note-widget-editor/note-widget-editor.component'; import { NoteWidgetComponent } from './note-widget/note-widget.component'; diff --git a/projects/dashboards-demo/webcomponents/src/app/native-note-widget/native-note-widget.ts b/projects/dashboards-demo/webcomponents/src/app/native-note-widget/native-note-widget.ts new file mode 100644 index 0000000000..0874b485ea --- /dev/null +++ b/projects/dashboards-demo/webcomponents/src/app/native-note-widget/native-note-widget.ts @@ -0,0 +1,162 @@ +/** + * Copyright (c) Siemens 2016 - 2026 + * SPDX-License-Identifier: MIT + * + * Framework-agnostic native web component widgets for use in SiFlexibleDashboard. + * Demonstrates that dashboard widgets can be implemented without any framework dependency. + * No Shadow DOM — reuses CSS classes from the parent Angular application. + */ + +interface WidgetConfig { + id?: string; + heading?: string; + payload?: Record; +} + +// --- Native Note Widget --- + +class NativeNoteWidget extends HTMLElement { + private _config: WidgetConfig = {}; + private _editable = false; + private messageEl!: HTMLParagraphElement; + private editHintEl!: HTMLParagraphElement; + + get config(): WidgetConfig { + return this._config; + } + + set config(value: WidgetConfig) { + this._config = value ?? {}; + this.render(); + } + + get editable(): boolean { + return this._editable; + } + + set editable(value: boolean) { + this._editable = value; + if (this.editHintEl) { + this.editHintEl.hidden = !value; + } + } + + private render(): void { + if (!this.messageEl) { + return; + } + const message = (this.config?.payload?.message as string) ?? ''; + this.messageEl.textContent = message; + } + + connectedCallback(): void { + this.innerHTML = ` +

+ + `; + this.messageEl = this.querySelector('.message')!; + this.editHintEl = this.querySelector('.edit-hint')!; + this.editHintEl.hidden = !this._editable; + this.render(); + this.dispatchEvent( + new CustomEvent('configChange', { + detail: { + primaryActions: [ + { + type: 'action', + label: 'User', + icon: 'element-user', + action: () => alert('Custom primary action') + } + ], + secondaryActions: [ + { + type: 'action', + label: 'Greenleaf', + icon: 'element-greenleaf', + action: () => alert('Custom secondary action') + } + ], + primaryEditActions: [ + { + type: 'action', + label: 'Custom Action', + icon: 'element-airquality-good', + action: () => alert('Widget specific edit action') + } + ], + secondaryEditActions: [ + { + type: 'action', + label: 'Light', + icon: 'element-light-ceiling', + action: () => alert('Widget specific secondary edit action') + } + ] + } + }) + ); + } +} + +// --- Native Note Widget Editor --- + +class NativeNoteWidgetEditor extends HTMLElement { + private _config: WidgetConfig = {}; + private headingInput!: HTMLInputElement; + private messageInput!: HTMLInputElement; + + get config(): WidgetConfig { + return this._config; + } + + set config(value: WidgetConfig) { + this._config = value ?? {}; + this.render(); + } + + private render(): void { + if (!this.headingInput) { + return; + } + this.headingInput.value = this.config?.heading ?? ''; + this.messageInput.value = (this.config?.payload?.message as string) ?? ''; + } + + private onChange(): void { + const heading = this.headingInput.value; + const message = this.messageInput.value; + + this._config.heading = heading; + this._config.payload ??= {}; + this._config.payload.message = message; + + this.dispatchEvent(new CustomEvent('configChange', { detail: this._config })); + this.dispatchEvent( + new CustomEvent('statusChanges', { + detail: { invalid: heading.trim().length === 0, modified: true } + }) + ); + } + + connectedCallback(): void { + this.innerHTML = ` +
+ + +
+
+ + +
+ `; + this.headingInput = this.querySelector('#native-heading')!; + this.messageInput = this.querySelector('#native-message')!; + this.headingInput.addEventListener('input', () => this.onChange()); + this.messageInput.addEventListener('input', () => this.onChange()); + this.render(); + } +} + +customElements.define('native-note-widget', NativeNoteWidget); +customElements.define('native-note-widget-editor', NativeNoteWidgetEditor);