@lion/ui
Version:
A package of extendable web components
461 lines • 16.2 kB
TypeScript
/**
* 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