UNPKG

web-ui-pack

Version:
151 lines (150 loc) 8.13 kB
import { PopupOpenCases, PopupCloseCases } from "./popupElement.types"; import { AttributeMap } from "../baseElement"; import WUPPopupArrowElement from "./popupArrowElement"; import PopupListener from "./popupListener"; import WUPBaseModal from "../baseModal"; declare const tagName = "wup-popup"; declare global { interface HTMLElementTagNameMap { [tagName]: WUPPopupElement; } } declare module "react" { namespace JSX { interface IntrinsicElements { /** Popup element * @see {@link WUPPopupElement} */ [tagName]: WUP.Base.ReactHTML<WUPPopupElement> & WUP.BaseModal.JSXProps & WUP.Popup.Attributes; } } } declare module "preact/jsx-runtime" { namespace JSX { interface HTMLAttributes<RefType> { } interface IntrinsicElements { /** Popup element * @see {@link WUPPopupElement} */ [tagName]: HTMLAttributes<WUPPopupElement> & WUP.Modal.JSXProps; } } } /** Popup element * @see demo {@link https://yegorich555.github.io/web-ui-pack/popup} * @example * JS/TS * ```js * WUPPopupElement.$defaults.arrowEnable = true; * * const el = document.createElement('wup-popup'); * el.$options.openCase = PopupOpenCases.onClick | PopupOpenCases.onFocus; * el.$options.target = document.querySelector('button'); * // if placement impossible according to rules rest of possible rules will be applied * el.$options.placement = [ * WUPPopupElement.$placements.$top.$middle, * WUPPopupElement.$placements.$bottom.$middle, * WUPPopupElement.$placements.$bottom.$middle.$adjust, // adjust means 'ignore align to fit layout` * WUPPopupElement.$placements.$bottom.$middle.$adjust.$resizeHeight, // resize means 'allow to resize to fit layout' * ]; * document.body.append(el); * // or * const btn = document.querySelector('button'); * // this is the most recommended way because $attach appends popup only by onShow and removes byHide * const detach = WUPPopupElement.$attach( { target: btn, text: "Some text content here", openCase: PopupOpenCases.onFocus | PopupOpenCases.onClick }, (popup) => { popup.className = "popup-class-here"; } )' *``` * HTML * ```html * <button id="btn1">Target</button> * <!-- You can skip pointing attribute 'target' if popup appended after target --> * <wup-popup w-target="#btn1" w-placement="top-start">Some content here</wup-popup> * ``` * @tutorial Troubleshooting: * * You can set minWidth, minHeight to prevent squeezing of popup or don't use rule '.$adjust' * * Don't override styles: display, transform (possible to override only for animation) * * Don't use inline styles: maxWidth, maxHeight, minWidth, minHeight * * If target removed (when popup $isOpened) and appended again you need to update $options.target (because $options.target cleared) * * Popup has overflow 'auto'; If you change to 'visible' it will apply maxWidth/maxHeight to first children (because popup must be restricted by maxSize to avoid layout issues) * * During the closing attr 'hide' is appended only if css-animation-duration is detected * * Popup can't be more than 100vw & 100vh (impossible to disable the rule) * * known issue: popup can be positioned wrong if parent has transfrom style: https://stackoverflow.com/revisions/15256339/2 this is css-core issue. To fix: place popup outside such parent or remove transform style on parent */ export default class WUPPopupElement<TOptions extends WUP.Popup.Options = WUP.Popup.Options, Events extends WUP.Popup.EventMap = WUP.Popup.EventMap> extends WUPBaseModal<TOptions, Events> { #private; /** Returns this.constructor // watch-fix: https://github.com/Microsoft/TypeScript/issues/3841#issuecomment-337560146 */ static get observedOptions(): Array<keyof WUP.Popup.Options>; static get observedAttributes(): Array<string>; static get mappedAttributes(): Record<string, AttributeMap>; static $placements: { $top: WUP.Popup.Place.EdgeFunc; $bottom: WUP.Popup.Place.EdgeFunc; $left: WUP.Popup.Place.EdgeFunc; $right: WUP.Popup.Place.EdgeFunc; }; /** Returns placement */ static $placementAttrs: (attr: WUP.Popup.Attributes["w-placement"]) => Array<WUP.Popup.Place.PlaceFunc> | undefined; static get $styleRoot(): string; static get $style(): string; /** Default options. Change it to configure default behavior */ static $defaults: WUP.Popup.Options; static cloneDefaults<T extends Record<string, any>>(): T; /** Listen for target according to openCase and create/remove popup when it's required (by open/close). * This helps to avoid tons of hidden popups on HTML; * Firing detach doesn't required if target removed by target.remove() or target.parent.removeChild(target); * If target is removed via changing innerHTML you should fire detach() to avoid memoryLeak * @returns detach-function (hide,remove popup and remove eventListeners) * @example * const detach = WUPPopupElement.$attach( * { * target: document.querySelector("button") as HTMLElement, * text: "Some text here", * openCase: PopupOpenCases.onClick, * }, * // (el) => el.class = "popup-attached" * ); * @tutorial Troubleshooting: * * $attach doesn't work with openCase.always it doesn't make sense * * every new attach on the same target > re-init previous (1 attach per target is possible) * * Firing detach() doesn't required if target removed by `target.remove()` or `target.parent.removeChild(target)`; * * If popup is hidden and target is removed via `target.parent.innerHTML="another content"` you should fire detach() to avoid memoryLeak */ static $attach<T extends WUPPopupElement>(options: WUP.Popup.AttachOptions, /** Fires when popup is added to document */ callback?: (el: T) => void): () => void; /** Returns arrowElement if $options.arrowEnable=true and after popup $isOpened */ $refArrow?: WUPPopupArrowElement; /** Force to update position. Call this if related styles are changed & need to re-calc position */ $refresh(): void; protected gotReady(): void; protected gotRender(): void; _refListener?: PopupListener; /** Called after gotReady() and $open() (to re-init according to options) */ protected init(): void; protected gotChanges(propsChanged: Array<string> | null): void; /** Defines target on show; @returns Element | Error */ defineTarget(): HTMLElement | SVGElement; protected setMaxHeight(px: number | null): void; protected setMaxWidth(px: number | null): void; /** Collect/calc all required values into #state (when menu shows) */ protected buildState(): void; /** Required to stop previous animations/timeouts (for case when option animation is changed) */ _stopAnimation?: () => void; protected goAnimate(animTime: number, isHide: boolean): Promise<boolean>; goOpen(openCase: PopupOpenCases, ev: MouseEvent | FocusEvent | null): Promise<boolean>; gotOpen(openCase: PopupOpenCases, ev: MouseEvent | FocusEvent | null): void; goClose(closeCase: PopupCloseCases, ev: MouseEvent | FocusEvent | KeyboardEvent | null): Promise<boolean>; /** Hide popup. @closeCase as reason of hide(). Calling 2nd time at once will stop previous hide-animation */ gotClose(closeCase: PopupCloseCases, ev: MouseEvent | FocusEvent | KeyboardEvent | null, immediately?: boolean): void; /** Returns `target.getBoundingClientRect()` Use function to change placement logic based on target-rect * @WARN it's called with screen-frequency (per frame) */ getTargetRect(target: Element): DOMRect; /** Update position of popup. Call this method in cases when you changed options */ protected updatePosition(): DOMRect | undefined; protected resetState(): void; protected gotRemoved(): void; /** Call when need to re-init */ protected disposeListener(): void; protected dispose(): void; } export {};