import { CSSResultGroup, CSSResultOrNative, PropertyValues, ReactiveController, LitElement as OriginalLitElement } from 'lit'; import { Runtime } from './makeRuntime.ts'; import { ProxyComponent } from './lazyLoad.ts'; import { ToElement } from './jsx/types.ts'; import { BaseController, LuminaPropertyDeclaration } from './controllers/types.ts'; import { ControllerManager } from './controllers/ControllerManager.ts'; import { Controller } from './controllers/Controller.ts'; type ComponentLifecycle = { connectedCallback?: () => void; disconnectedCallback?: () => void; load?: () => Promise | void; loaded?: () => void; }; /** * Base Lit class that should be used by all components instead of the original * Lit element. * * This class: * - Handles being used in a lazy-loading build * - Connects to the Controller Manager * - Provides load/loaded lifecycle hooks * (like componentWillLoad and componentDidLoad in Stencil) * - Provides a .listen() method like Stencil's \@Listen decorator * * @remarks * Even though all components will extend this class, it is not exposed in the * public typings as we do not want to expose internal methods like * addController() and connectedCallback() to the public typings. * * Instead, `@arcgis/lumina-compiler` will make it appear that your component * extends `PublicLitElement`. * * `PublicLitElement` will never be called at runtime - it exists for typings * only. */ export declare class LitElement extends OriginalLitElement implements ComponentLifecycle { #private; /** * @private * @remarks * We prefix private variables with double underscore to avoid collisions with * subclasses. All runtime properties that start with underscore are mangled * in production build, so collision avoidance is mainly important to avoid * triggering TypeScript errors. */ static __runtime: Runtime; /** @private */ static __tagName: string; static elementProperties: Map; /** * Customize Lit's default style handling to support non-shadow-root styles */ static finalizeStyles(styles?: CSSResultGroup): CSSResultOrNative[]; static createProperty(name: PropertyKey, /** * While in vanilla Lit this type is always LuminaPropertyDeclaration, * in Lumina it is always number or * [number,LuminaPropertyDeclaration], so we don't even check for the * LuminaPropertyDeclaration case. LuminaPropertyDeclaration is here * only to satisfy the type checker. */ options?: LuminaPropertyDeclaration | number | [number, LuminaPropertyDeclaration]): void; protected static getPropertyDescriptor(name: PropertyKey, key: string | symbol, options: LuminaPropertyDeclaration): PropertyDescriptor | undefined; /** * Since we can't pass arguments to web component constructor, before lazy * loading logic calls document.createElement(), it temporary sets this static * property. * * @private * */ static __lazy: ProxyComponent | undefined; static readonly lumina = true; /** * In lazy build, the actual DOM element differs from the class instance: * - "this.el" is a proxy custom element - it's physically present in the DOM * even before the Lit component is loaded. * - "this" is the actual Lit component - in case of Lazy builds, it's * never directly attached to the DOM. Instead, all interactions with the * proxy are forwarded to the actual Lit component. And, when Lit wants to * render, it renders into the shadow root of the proxy. * * "this.el" should be used instead of "this" for all things involving the * DOM (addEventListener, querySelector, children, setAttribute, * MutationObserver, etc...) * * @example * ```ts * // Generally, you shouldn't have to write logic specific to lazy or non-lazy * // build, but if you have to, you can detect if you are in a lazy build like so: * const isLazy = this.el !== this; * ``` */ readonly el: ToElement; /** * Controller does not need to be an instance of the Controller class. Any * Lit's Reactive controller will work as well. See examples: * https://lit.dev/docs/composition/controllers/ * * @private * @privateRemarks * For most private names I tried to prefix with __ to not conflict with * private names on subclasses. However, Lit already has __controllers private. */ readonly _controllers: (BaseController | Controller)[]; /** * Controller Manager orchestrates all controllers used by this component, * connecting their lifecycle hooks and providing context information. */ readonly manager: ControllerManager; /** @private */ __postLoadDeferred: ProxyComponent["__postLoadDeferred"]; /** * Direct offspring that should be awaited before loaded() is emitted. * * `attachToAncestor()` will add elements to this array * * @private */ __offspringComponents: ProxyComponent["__offspringComponents"]; constructor(); connectedCallback(): void; disconnectedCallback(): void; /** * Overwrite Lit's default behavior of attaching shadow root to the lit * element, and instead use this.el to support lazy builds. * * Also, support the case when component asked to not use shadow root */ protected createRenderRoot(): DocumentFragment | HTMLElement; /** * Overwriting default shouldUpdate simply to get access to * "changedProperties" so that we can later provide it to ControllerManager */ protected shouldUpdate(_changedProperties: PropertyValues): boolean; protected update(changedProperties: PropertyValues): void; /** * A helper for setting event listener on the current component. * * The event listener will be removed automatically when component * disconnects, and added back if component re-comments, thus you don't have * to clean it up manually. * * @remarks * This listens for any event on the component or one of it's children. * If you only want to listen for events originating from the component itself * use `const isSelf = event.target === this.el;` to check. * * @example * ```tsx * constructor() { * // Handle click will only be called while component is connected * this.listen('click', this.handleClick); * } * handleClick(event: MouseEvent):void { * console.log('clicked'); * } * ``` */ listen(name: K, listener: (this: this, event: HTMLElementEventMap[K]) => unknown, options?: AddEventListenerOptions | boolean): void; listen(name: string, listener: (this: this, event: Event) => unknown, options?: AddEventListenerOptions | boolean): void; listen['arcgisClick']>() to get type-checked payload type">>(name: string, /** * The "NoInfer" here forces type argument to be specified explicitly. * Without it, the following would be allowed: * ```tsx * this.listen("focus",(event:NotFocusEvent)=>{....}) * ``` */ listener: (this: this, event: NoInfer) => unknown, options?: AddEventListenerOptions | boolean): void; /** * A helper for setting even listener on any element (or window / document). * * The event listener will be removed automatically when component * disconnects, and added back if component re-comments, thus you don't have * to clean it up manually. * * Use cases: * - Listening for pointer events on the top-most element * - Listening to events that are emitted exclusively on document/window/body * - Imperatively listening for events on children components * - Listening for events on non-Element objects (like Worker, WebSocket, etc) * - Your component could emit a custom event, and then listen for that event * on the window. This lets consumer of the component handle the event and * stop propagation - and if they didn't and event reached the window, your * component can handle the event itself using the default behavior. * * @example * ```tsx * constructor() { * // Handle click will only be called while component is connected * this.listenOn(window, 'click', this.handleWindowClick); * } * handleWindowClick(event: MouseEvent):void { * console.log('clicked'); * } * ``` */ listenOn(target: Window, name: Name, listener: Listener, options?: AddEventListenerOptions | boolean): void; listenOn(target: Document, name: Name, listener: Listener, options?: AddEventListenerOptions | boolean): void; listenOn(target: HTMLElement, name: Name, listener: Listener, options?: AddEventListenerOptions | boolean): void; listenOn['arcgisClick']>() to get type-checked payload type">, Target = EventTarget>(target: Target, name: string, /** * The "NoInfer" here forces type argument to be specified explicitly. * Without it, the following would be allowed: * ```tsx * this.listen("focus",(event:NotFocusEvent)=>{....}) * ``` */ listener: Listener & { currentTarget: Target; }>, options?: AddEventListenerOptions | boolean): void; /** * Creates a promise that resolves once the component is fully loaded. * * @example * const map = document.createElement('arcgis-map'); * document.body.append(map); * map.componentOnReady().then(() => { * console.log('Map is ready to go!'); * }); */ componentOnReady(): Promise; /** * Adds a controller to the host, which connects the controller's lifecycle * methods to the host's lifecycle. * * @remarks * Even though Lit's LitElement already has addController, * we overwrite it with a compatible version to have more control over * timing, and to add support for load/loaded lifecycle hooks. */ addController(controller: BaseController | ReactiveController): void; /** * Removes a controller from the host. */ removeController(controller: BaseController | ReactiveController): void; } type Listener = ((this: ThisType, event: EventType) => unknown) | { handleEvent(event: EventType): unknown; }; /** * List of tag names defined by this library. * * @private */ export type GlobalThisWithOwnTagNames = typeof globalThis & { devOnly$ownTagNames?: Set; }; /** * Lumina tracks accesses to JSAPI Accessor properties during render() and * automatically re-renders the component when any tracked property changes. * * If your render() has imperative rendering logic or relies on specific render() * call timing, you can temporarily disable this reactiveUtils integration to * give you time to complete the migration and refactor the code. * * @deprecated Use this as a **temporary** workaround only. * @example * ```ts * import { disableReactiveUtilsIntegration } from "@arcgis/lumina"; * class MyComponent extends LitElement { * // ... * } * disableReactiveUtilsIntegration(MyComponent); * ``` */ export declare function disableReactiveUtilsIntegration(componentClass: typeof LitElement): void; export {};