web-ui-pack
Version:
Web package with UI elements
151 lines (150 loc) • 8.13 kB
TypeScript
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 {};