UNPKG

@lion/ui

Version:

A package of extendable web components

461 lines 16.2 kB
/** * OverlayController is the fundament for every single type of overlay. With the right * configuration, it can be used to build (modal) dialogs, tooltips, dropdowns, popovers, * bottom/top/left/right sheets etc. * */ export class OverlayController extends EventTarget { /** * @constructor * @param {OverlayConfig} config initial config. Will be remembered as shared config * when `.updateConfig()` is called. */ constructor(config?: OverlayConfig, manager?: import("./OverlaysManager.js").OverlaysManager); manager: import("./OverlaysManager.js").OverlaysManager; /** @private */ private __sharedConfig; /** @private */ private __activeElementRightBeforeHide; /** @type {OverlayConfig} */ config: OverlayConfig; /** * @type {OverlayConfig} * @protected */ protected _defaultConfig: OverlayConfig; /** @protected */ protected _contentId: string; /** @private */ private __originalAttrs; /** @private */ private __hasActiveTrapsKeyboardFocus; /** @private */ private __hasActiveBackdrop; /** * @param {KeyboardEvent} event * @returns {void} */ __escKeyHandler(event: KeyboardEvent): void; /** * When the overlay is a modal dialog hidesOnEsc works out of the box, so we prevent that. * * There is currently a bug in chrome that makes the dialog close when pressing Esc the second time * @private */ private __cancelHandler; /** * The invokerNode * @type {HTMLElement | undefined} */ get invoker(): HTMLElement | undefined; /** * The contentWrapperNode * @type {HTMLDialogElement | HTMLDivElement} */ get content(): HTMLDialogElement | HTMLDivElement; /** * Determines the connection point in DOM (body vs next to invoker). * @type {'global' | 'local' | undefined} */ get placementMode(): "global" | "local" | undefined; /** * The interactive element (usually a button) invoking the dialog or tooltip * @type {HTMLElement | undefined} */ get invokerNode(): HTMLElement | undefined; /** * The element that is used to position the overlay content relative to. Usually, * this is the same element as invokerNode. Should only be provided when invokerNode should not * be positioned against. * @type {HTMLElement} */ get referenceNode(): HTMLElement; /** * The most important element: the overlay itself * @type {HTMLElement} */ get contentNode(): HTMLElement; /** * The wrapper element of contentNode, used to supply inline positioning styles. When a Popper * arrow is needed, it acts as parent of the arrow node. Will be automatically created for global * and non projected contentNodes. Required when used in shadow dom mode or when Popper arrow is * supplied. Essential for allowing webcomponents to style their projected contentNodes * @type {HTMLElement} */ get contentWrapperNode(): HTMLElement; /** * The element that is placed behind the contentNode. When not provided and `hasBackdrop` is true, * a backdropNode will be automatically created * @type {HTMLElement} */ get backdropNode(): HTMLElement; /** * The element that should be called `.focus()` on after dialog closes * @type {HTMLElement} */ get elementToFocusAfterHide(): HTMLElement; /** * Whether it should have a backdrop (currently exclusive to globalOverlayController) * @type {boolean} */ get hasBackdrop(): boolean; /** * Hides other overlays when mutiple are opened (currently exclusive to globalOverlayController) * @type {boolean} */ get isBlocking(): boolean; /** * Hides other overlays when mutiple are opened (currently exclusive to globalOverlayController) * @type {boolean} */ get preventsScroll(): boolean; /** * Rotates tab, implicitly set when 'isModal' * @type {boolean} */ get trapsKeyboardFocus(): boolean; /** * Hides the overlay when pressing [ esc ] * @type {boolean} */ get hidesOnEsc(): boolean; /** * Hides the overlay when clicking next to it, exluding invoker * @type {boolean} */ get hidesOnOutsideClick(): boolean; /** * Hides the overlay when pressing esc, even when contentNode has no focus * @type {boolean} */ get hidesOnOutsideEsc(): boolean; /** * Will align contentNode with referenceNode (invokerNode by default) for local overlays. * Usually needed for dropdowns. 'max' will prevent contentNode from exceeding width of * referenceNode, 'min' guarantees that contentNode will be at least as wide as referenceNode. * 'full' will make sure that the invoker width always is the same. * @type {'max' | 'full' | 'min' | 'none' | undefined } */ get inheritsReferenceWidth(): "none" | "max" | "full" | "min" | undefined; /** * For non `isTooltip`: * - sets aria-expanded="true/false" and aria-haspopup="true" on invokerNode * - sets aria-controls on invokerNode * - returns focus to invokerNode on hide * - sets focus to overlay content(?) * * For `isTooltip`: * - sets role="tooltip" and aria-labelledby/aria-describedby on the content * * @type {boolean} */ get handlesAccessibility(): boolean; /** * Has a totally different interaction- and accessibility pattern from all other overlays. * Will behave as role="tooltip" element instead of a role="dialog" element * @type {boolean} */ get isTooltip(): boolean; /** * The alertdialog role is to be used on modal alert dialogs that interrupt a user's workflow * to communicate an important message and require a response. */ get isAlertDialog(): boolean; /** * By default, the tooltip content is a 'description' for the invoker (uses aria-describedby). * Setting this property to 'label' makes the content function as a label (via aria-labelledby) * @type {'label' | 'description'| undefined} */ get invokerRelation(): "label" | "description" | undefined; /** * Popper configuration. Will be used when placementMode is 'local' * @type {PopperOptions} */ get popperConfig(): import("@popperjs/core").Options; /** * Viewport configuration. Will be used when placementMode is 'global' * @type {ViewportConfig} */ get viewportConfig(): import("@lion/ui/types/overlays.js").ViewportConfig; get visibilityTriggerFunction(): Function; /** * @desc The element our local overlay will be positioned relative to. * @type {HTMLElement | undefined} * @protected */ protected get _referenceNode(): HTMLElement | undefined; /** * @param {number} value */ set elevation(arg: number); /** * @type {number} */ get elevation(): number; /** * Allows to dynamically change the overlay configuration. Needed in case the * presentation of the overlay changes depending on screen size. * Note that this method is the only allowed way to update a configuration of an * OverlayController instance. * @param { OverlayConfig } cfgToAdd */ updateConfig(cfgToAdd: OverlayConfig): void; /** * @type {OverlayConfig} * @private */ private __prevConfig; /** @private */ private __elementToFocusAfterHide; /** * @param {OverlayConfig} newConfig * @private */ private __validateConfiguration; /** * @protected */ protected _init(): void; __contentHasBeenInitialized: boolean | undefined; /** * @param {{ phase: OverlayPhase }} config * @private */ private __handleOverlayStyles; /** * Here we arrange our content node via: * 1. HTMLDialogElement: the content will always be painted to the browser's top layer * - no matter what context the contentNode lives in, the overlay will be painted correctly via the <dialog> element, * even if 'overflow:hidden' or a css transform is applied in its parent hierarchy. * - the dialog element will be unstyled, but will span the whole screen * - a backdrop element will be a child of the dialog element, so it leverages the capabilities of the parent * (filling the whole screen if wanted an always painted to top layer) * 2. ContentWrapper: the content receives the right positioning styles in a clean/non conflicting way: * - local positioning: receive inline (position) styling that can never conflict with the already existing computed styles * - global positioning: receive flex (child) classes that position the content correctly relative to the viewport * * The resulting structure that will be created looks like this: * * ... * <dialog role="none"> * <div id="optional-backdrop"></div> * <div id="content-wrapper-node"> * <!-- this was the (slot for) original content node --> * <slot name="content"></slot> * </div> * </dialog> * ... * * @private */ private __initContentDomStructure; __wrappingDialogNode: HTMLDialogElement | HTMLDivElement | undefined; __contentWrapperNode: HTMLDivElement | undefined; /** * Display local overlays on top of elements with no z-index that appear later in the DOM * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleZIndex({ phase }: { phase: OverlayPhase; }): void; /** * @param {{ phase: OverlayPhase }} config * @private */ private __setupTeardownAccessibility; /** * @param {HTMLElement} node * @param {string[]} attrs * @private */ private __storeOriginalAttrs; /** @private */ private __restoreOriginalAttrs; get isShown(): boolean; /** * @event before-show right before the overlay shows. Used for animations and switching overlays * @event show right after the overlay is shown * @param {HTMLElement} elementToFocusAfterHide */ show(elementToFocusAfterHide?: HTMLElement): Promise<void>; _showComplete: Promise<any> | undefined; _showResolve: ((value: any) => void) | undefined; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handlePosition({ phase }: { phase: OverlayPhase; }): Promise<void>; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _keepBodySize({ phase }: { phase: OverlayPhase; }): void; __bodyClientWidth: number | undefined; __bodyClientHeight: number | undefined; __bodyMarginRightInline: string | undefined; __bodyMarginBottomInline: string | undefined; __bodyMarginRight: number | undefined; __bodyMarginBottom: number | undefined; /** * @event before-hide right before the overlay hides. Used for animations and switching overlays * @event hide right after the overlay is hidden */ hide(): Promise<void>; _hideComplete: Promise<any> | undefined; _hideResolve: ((value: any) => void) | undefined; /** * Method to be overriden by subclassers * * @param {{backdropNode:HTMLElement, contentNode:HTMLElement}} hideConfig */ transitionHide(hideConfig: { backdropNode: HTMLElement; contentNode: HTMLElement; }): Promise<void>; /** * @param {{backdropNode:HTMLElement, contentNode:HTMLElement}} hideConfig * @protected */ protected _transitionHide({ backdropNode, contentNode }: { backdropNode: HTMLElement; contentNode: HTMLElement; }): Promise<void>; /** * To be overridden by subclassers * * @param {{backdropNode:HTMLElement; contentNode:HTMLElement}} showConfig */ transitionShow(showConfig: { backdropNode: HTMLElement; contentNode: HTMLElement; }): Promise<void>; /** * @param {{backdropNode:HTMLElement; contentNode:HTMLElement}} showConfig */ _transitionShow(showConfig: { backdropNode: HTMLElement; contentNode: HTMLElement; }): Promise<void>; /** @protected */ protected _restoreFocus(): void; toggle(): Promise<void>; /** * All features are handled here. * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleFeatures({ phase }: { phase: OverlayPhase; }): void; /** * @param {{ phase: OverlayPhase }} config */ _handleVisibilityTriggers({ phase }: { phase: OverlayPhase; }): void; __visibilityTriggerHandler: any; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handlePreventsScroll({ phase }: { phase: OverlayPhase; }): void; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleBlocking({ phase }: { phase: OverlayPhase; }): void; get hasActiveBackdrop(): boolean; /** * Sets up backdrop on the given overlay. If there was a backdrop on another element * it is removed. Otherwise this is the first time displaying a backdrop, so a animation-in * animation is played. * @param {{ animation?: boolean, phase: OverlayPhase }} config * @protected */ protected _handleBackdrop({ phase }: { animation?: boolean | undefined; phase: OverlayPhase; }): void; __backdropNode: HTMLDivElement | undefined; __backdropInitialized: boolean | undefined; get hasActiveTrapsKeyboardFocus(): boolean; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleTrapsKeyboardFocus({ phase }: { phase: OverlayPhase; }): void; enableTrapsKeyboardFocus(): void; _containFocusHandler: { disconnect: () => void; } | undefined; disableTrapsKeyboardFocus({ findNewTrap }?: { findNewTrap?: boolean | undefined; }): void; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleHidesOnEsc({ phase }: { phase: OverlayPhase; }): void; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleHidesOnOutsideEsc({ phase }: { phase: OverlayPhase; }): void; /** @protected */ protected _handleInheritsReferenceWidth(): void; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleHidesOnOutsideClick({ phase }: { phase: OverlayPhase; }): void; /** @type {EventListenerOrEventListenerObject} */ __onInsideMouseDown: EventListenerOrEventListenerObject | undefined; __onInsideMouseUp: (() => void) | undefined; /** @type {EventListenerOrEventListenerObject} */ __onDocumentMouseUp: EventListenerOrEventListenerObject | undefined; /** @type {EventListenerOrEventListenerObject} */ __onWindowBlur: EventListenerOrEventListenerObject | undefined; /** * @param {{ phase: OverlayPhase }} config * @protected */ protected _handleAccessibility({ phase }: { phase: OverlayPhase; }): void; teardown(): void; /** @private */ private __createPopperInstance; _popper: any; _hasDisabledInvoker(): boolean; #private; } export namespace OverlayController { const popperModule: Promise<PopperModule> | undefined; } export type OverlayPhase = 'setup' | 'init' | 'teardown' | 'before-show' | 'show' | 'hide' | 'add' | 'remove'; export type ViewportConfig = import('@lion/ui/types/overlays.js').ViewportConfig; export type OverlayConfig = import('@lion/ui/types/overlays.js').OverlayConfig; export type PopperOptions = import('@popperjs/core').Options; export type Placement = import('@popperjs/core').Placement; export type Popper = any; export type PopperModule = { createPopper: Popper; }; //# sourceMappingURL=OverlayController.d.ts.map