From fbcb9fa14174618e90e4ed3e353603a0bb6de5e3 Mon Sep 17 00:00:00 2001 From: Davide Mininni Date: Thu, 25 Apr 2024 11:25:09 +0200 Subject: [PATCH] feat: add overlay base class (WIP) --- src/components/core/base-elements.ts | 1 + .../base-elements/overlay-base-element.ts | 47 +++++++++++++++++++ .../dialog/dialog/dialog-base-element.ts | 44 +++-------------- src/components/dialog/dialog/dialog.ts | 2 +- src/components/dialog/dialog/readme.md | 8 ++-- src/components/overlay/overlay.ts | 2 +- src/components/overlay/readme.md | 8 ++-- 7 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 src/components/core/base-elements/overlay-base-element.ts diff --git a/src/components/core/base-elements.ts b/src/components/core/base-elements.ts index b369db0b79..47238397a1 100644 --- a/src/components/core/base-elements.ts +++ b/src/components/core/base-elements.ts @@ -1,3 +1,4 @@ export * from './base-elements/action-base-element.js'; export * from './base-elements/button-base-element.js'; export * from './base-elements/link-base-element.js'; +export * from './base-elements/overlay-base-element.js'; diff --git a/src/components/core/base-elements/overlay-base-element.ts b/src/components/core/base-elements/overlay-base-element.ts new file mode 100644 index 0000000000..27bc9967d8 --- /dev/null +++ b/src/components/core/base-elements/overlay-base-element.ts @@ -0,0 +1,47 @@ +import { LitElement } from 'lit'; + +import { EventEmitter } from '../eventing/event-emitter.js'; +import type { SbbOpenedClosedState } from '../interfaces.js'; + +/** Base class for overlay components. */ +export abstract class SbbOverlayBaseElement extends LitElement { + public static readonly events = { + willOpen: 'willOpen', + didOpen: 'didOpen', + willClose: 'willClose', + didClose: 'didClose', + } as const; + + /** The state of the component. */ + protected set state(state: SbbOpenedClosedState) { + this.setAttribute('data-state', state); + } + protected get state(): SbbOpenedClosedState { + return this.getAttribute('data-state') as SbbOpenedClosedState; + } + + /** Emits whenever the component starts the opening transition. */ + protected willOpen: EventEmitter = new EventEmitter(this, SbbOverlayBaseElement.events.willOpen); + + /** Emits whenever the component is opened. */ + protected didOpen: EventEmitter = new EventEmitter(this, SbbOverlayBaseElement.events.didOpen); + + /** Emits whenever the component begins the closing transition. */ + protected willClose: EventEmitter = new EventEmitter( + this, + SbbOverlayBaseElement.events.willClose, + ); + + /** Emits whenever the component is closed. */ + protected didClose: EventEmitter = new EventEmitter(this, SbbOverlayBaseElement.events.didClose); + + /** Opens the component. */ + public abstract open(): void; + /** Closes the component. */ + public abstract close(): void; + + public override connectedCallback(): void { + super.connectedCallback(); + this.state ||= 'closed'; + } +} diff --git a/src/components/dialog/dialog/dialog-base-element.ts b/src/components/dialog/dialog/dialog-base-element.ts index bab670a40d..bd60552501 100644 --- a/src/components/dialog/dialog/dialog-base-element.ts +++ b/src/components/dialog/dialog/dialog-base-element.ts @@ -1,12 +1,13 @@ -import { LitElement, type PropertyValues } from 'lit'; +import { type PropertyValues } from 'lit'; import { property } from 'lit/decorators.js'; import { SbbFocusHandler } from '../../core/a11y.js'; +import { SbbOverlayBaseElement } from '../../core/base-elements.js'; import { SbbLanguageController } from '../../core/controllers.js'; import { hostContext, SbbScrollHandler } from '../../core/dom.js'; import { EventEmitter } from '../../core/eventing.js'; import { i18nDialog } from '../../core/i18n.js'; -import type { SbbDialogCloseEventDetails, SbbOpenedClosedState } from '../../core/interfaces.js'; +import type { SbbDialogCloseEventDetails } from '../../core/interfaces.js'; import { SbbNegativeMixin } from '../../core/mixins.js'; import { applyInertMechanism, removeInertMechanism } from '../../core/overlay.js'; import type { SbbScreenReaderOnlyElement } from '../../screen-reader-only.js'; @@ -14,42 +15,12 @@ import type { SbbScreenReaderOnlyElement } from '../../screen-reader-only.js'; // A global collection of existing dialogs export const dialogRefs: SbbDialogBaseElement[] = []; -export abstract class SbbDialogBaseElement extends SbbNegativeMixin(LitElement) { - public static readonly events = { - willOpen: 'willOpen', - didOpen: 'didOpen', - willClose: 'willClose', - didClose: 'didClose', - } as const; - +export abstract class SbbDialogBaseElement extends SbbNegativeMixin(SbbOverlayBaseElement) { /** This will be forwarded as aria-label to the relevant nested element. */ @property({ attribute: 'accessibility-label' }) public accessibilityLabel: string | undefined; - /** The state of the overlay. */ - protected set state(state: SbbOpenedClosedState) { - this.setAttribute('data-state', state); - } - protected get state(): SbbOpenedClosedState { - return this.getAttribute('data-state') as SbbOpenedClosedState; - } - - /** Emits whenever the `sbb-dialog` starts the opening transition. */ - protected willOpen: EventEmitter = new EventEmitter( - this, - SbbDialogBaseElement.events.willOpen, - ); - - /** Emits whenever the `sbb-dialog` is opened. */ - protected didOpen: EventEmitter = new EventEmitter( - this, - SbbDialogBaseElement.events.didOpen, - ); - - /** Emits whenever the `sbb-dialog` begins the closing transition. */ - protected willClose: EventEmitter = new EventEmitter(this, SbbDialogBaseElement.events.willClose); - /** Emits whenever the `sbb-dialog` is closed. */ - protected didClose: EventEmitter = new EventEmitter( + protected override didClose: EventEmitter = new EventEmitter( this, SbbDialogBaseElement.events.didClose, ); @@ -66,13 +37,11 @@ export abstract class SbbDialogBaseElement extends SbbNegativeMixin(LitElement) protected ariaLiveRef!: SbbScreenReaderOnlyElement; protected language = new SbbLanguageController(this); - /** Opens the dialog element. */ - public abstract open(): void; protected abstract onDialogAnimationEnd(event: AnimationEvent): void; protected abstract setDialogFocus(): void; protected abstract closeAttribute: string; - /** Closes the dialog element. */ + /** Closes the component. */ public close(result?: any, target?: HTMLElement): any { if (this.state !== 'opened') { return; @@ -94,7 +63,6 @@ export abstract class SbbDialogBaseElement extends SbbNegativeMixin(LitElement) public override connectedCallback(): void { super.connectedCallback(); - this.state ||= 'closed'; this.dialogController?.abort(); this.dialogController = new AbortController(); diff --git a/src/components/dialog/dialog/dialog.ts b/src/components/dialog/dialog/dialog.ts index b51ac5d607..e42f360ad0 100644 --- a/src/components/dialog/dialog/dialog.ts +++ b/src/components/dialog/dialog/dialog.ts @@ -53,7 +53,7 @@ export class SbbDialogElement extends SbbDialogBaseElement { private _dialogId = `sbb-dialog-${nextId++}`; protected closeAttribute: string = 'sbb-dialog-close'; - /** Opens the dialog element. */ + /** Opens the component. */ public open(): void { if (this.state !== 'closed') { return; diff --git a/src/components/dialog/dialog/readme.md b/src/components/dialog/dialog/readme.md index 73cf6f383c..e9c2628ccf 100644 --- a/src/components/dialog/dialog/readme.md +++ b/src/components/dialog/dialog/readme.md @@ -98,10 +98,10 @@ The `sbb-dialog` component may visually hide the title thanks to the `hideOnScro ## Methods -| Name | Privacy | Description | Parameters | Return | Inherited From | -| ------- | ------- | -------------------------- | ---------------------------------- | ------ | -------------------- | -| `open` | public | Opens the dialog element. | | `void` | SbbDialogBaseElement | -| `close` | public | Closes the dialog element. | `result: any, target: HTMLElement` | `any` | SbbDialogBaseElement | +| Name | Privacy | Description | Parameters | Return | Inherited From | +| ------- | ------- | --------------------- | ---------------------------------- | ------ | --------------------- | +| `open` | public | Opens the component. | | `void` | SbbOverlayBaseElement | +| `close` | public | Closes the component. | `result: any, target: HTMLElement` | `any` | SbbOverlayBaseElement | ## Events diff --git a/src/components/overlay/overlay.ts b/src/components/overlay/overlay.ts index 77becf116f..04a1c9ae0c 100644 --- a/src/components/overlay/overlay.ts +++ b/src/components/overlay/overlay.ts @@ -69,7 +69,7 @@ export class SbbOverlayElement extends SbbDialogBaseElement { ); private _overlayContentElement: HTMLElement | null = null; - /** Opens the overlay element. */ + /** Opens the component. */ public open(): void { if (this.state !== 'closed') { return; diff --git a/src/components/overlay/readme.md b/src/components/overlay/readme.md index 219f486f11..2b312d5bd0 100644 --- a/src/components/overlay/readme.md +++ b/src/components/overlay/readme.md @@ -85,10 +85,10 @@ When using a button to trigger the overlay, ensure to manage the appropriate ARI ## Methods -| Name | Privacy | Description | Parameters | Return | Inherited From | -| ------- | ------- | -------------------------- | ---------------------------------- | ------ | -------------------- | -| `open` | public | Opens the overlay element. | | `void` | SbbDialogBaseElement | -| `close` | public | Closes the dialog element. | `result: any, target: HTMLElement` | `any` | SbbDialogBaseElement | +| Name | Privacy | Description | Parameters | Return | Inherited From | +| ------- | ------- | --------------------- | ---------------------------------- | ------ | --------------------- | +| `open` | public | Opens the component. | | `void` | SbbOverlayBaseElement | +| `close` | public | Closes the component. | `result: any, target: HTMLElement` | `any` | SbbOverlayBaseElement | ## Events