import { TemplateResult } from 'lit'; import { DirectiveResult } from 'lit/directive.js'; import { HTMLElementTags, HTMLAttributes, GlobalEventHandlersCamelCase, SvgElementTags, DOMAttributes, AriaAttributes } from './generatedTypes.ts'; import { CustomAttributes } from './baseTypes.ts'; /** * The "h" namespace is used to import JSX types for elements and attributes. * It is imported in order to avoid conflicting global JSX issues. */ export declare namespace h { export function h(sel: any, data?: any, text?: any): TemplateResult; export type { LuminaJsx as JSX }; } /** * The references to this function are removed at build time. */ export declare const Fragment: (props: { children?: JsxNode; }) => TemplateResult; /** * @private * The references to this function are removed at build time. You do not need * to import it directly */ export declare function jsx(type: string, props: unknown, key?: unknown): JsxNode; /** * @private * The references to this function are removed at build time. You do not need * to import it directly */ export declare function jsxs(type: string, props: unknown, key?: unknown): JsxNode; /** * The JSX to lit-html conversion has a heuristic to determine whether a prop * should be converted to a property or attribute at runtime. In cases where * that heuristic is not sufficient, you can use the `bindAttribute()` function to * explicitly tell the conversion to treat the prop as an attribute. * * This function call will be erased at build-time, thus it has no runtime * impact. But that also means you should not call this function anywhere other * than at the top level of a JSX prop value * * @example * ```tsx * * ``` * * Will be converted at build time into: * ```ts * html`` * ``` * * @remarks * If you often encounter cases where the JSX to lit-html incorrectly converts * the prop to a property necessitating the use of `bindAttribute()`, please open * an issue for Lumina. */ export declare const bindAttribute: (value: T) => T; /** * The JSX to lit-html conversion has a heuristic to determine whether a prop * should be converted to a property, attribute or boolean attribute at runtime. * In cases where that heuristic is not sufficient, you can use the * `bindBooleanAttribute()` function to explicitly tell the conversion to treat the * prop as a boolean attribute. * * This function call will be erased at build-time, thus it has no runtime * impact. But that also means you should not call this function anywhere other * than at the top level of a JSX prop value * * @see https://lit.dev/docs/templates/expressions/#boolean-attribute-expressions * * @example * ```tsx * * ``` * * Will be converted at build time into: * ```ts * html`` * ``` * * @remarks * If you often encounter cases where the JSX to lit-html incorrectly converts * the prop necessitating the use of `bindBooleanAttribute()`, please open * an issue for Lumina. */ export declare const bindBooleanAttribute: (value: T) => T; /** * The JSX to lit-html conversion has a heuristic to determine whether a prop * should be converted to a property or attribute at runtime. In cases where * that heuristic is not sufficient, you can use the `bindProperty()` function to * explicitly tell the conversion to treat the prop as a property. * * This function call will be erased at build-time, thus it has no runtime * impact. But that also means you should not call this function anywhere other * than at the top level of a JSX prop value * * @example * ```tsx * * ``` * * Will be converted at build time into: * ```ts * html`` * ``` * * @remarks * If you often encounter cases where the JSX to lit-html incorrectly converts * the prop to an attribute necessitating the use of `bindProperty()`, please open * an issue for Lumina. * * @remarks * This function is not named `property()` because that is already taken by * Lit's \@property() decorator */ export declare const bindProperty: (value: T) => T; /** * The `bindEvent()` function lets you customize the event binding behavior by * providing options like passive, capture, once and other options that you * normally can provide to `addEventListener` * * This function call will be erased at build-time, thus it has no runtime * impact. * * @example * ```tsx * * ```tsx * * ```ts * html`` * ``` * * @remarks * This function is a _JSX to lit-html_ adaptation of the Lit's event listener * customization syntax. * See https://lit.dev/docs/components/events/#event-options-decorator * * * @remarks * This function is not named `event` because there is a legacy `window.event` * global that was interfering with auto-imports of this function. */ export declare const bindEvent: (descriptor: T | (AddEventListenerOptions & { handleEvent: T; })) => T; export type JsxNode = DirectiveResult | JsxNodeArray | Node | TemplateResult | boolean | number | (NonNullable & string) | null | undefined; interface JsxNodeArray extends Array { } /** * By not requiring to have some sort of typings generation watcher to run * in the background and generate types for the components based on the * exposed properties, we improve developer experience a lot. * (no need to run a watcher in the background, no need to commit an * autogenerated file, fewer merge conflicts...) * * The trade-off is that we can't tell in TypeScript if component's public * properties have a `@property()` decorator or not, so have to expose them * all. To reduce the noise, we exclude some properties that we know are * definitely not props (as they are coming from LitElement) * * An ESLint rule that mandates all properties to be private/protected or else * to have a `@property()` decorator would mitigate the issue to a large * extent. * * This does not impact the typings we publish - at build time we have all the * information and can generate the typings that expose only the actual * properties component declared. * * @remarks * Do not exclude "manager" and "componentOnReady" since these properties are * available both on lazy and non-lazy instance. */ type ExcludedProperties = "addController" | "attributeChangedCallback" | "connectedCallback" | "disconnectedCallback" | "el" | "hasUpdated" | "isUpdatePending" | "listen" | "load" | "loaded" | "removeController" | "render" | "renderOptions" | "renderRoot" | "requestUpdate" | "updateComplete" | "updated" | "willUpdate"; /** * this.el property on a component only has the public properties of the * component. All internal methods, properties, as well as LitElement methods * are excluded. This type approximates what the public API of the component * looks like. * * @example * Usually, you don't need to use this type directly. If you have an * `ArcgisCounter` element, you can get its el type by using * `ArcgisCounter["el"]` * * @remarks * By using Omit<>, TypeScript also "forgets" all private members of the * passed in type, which is convenient for us. */ export type ToElement = Omit; /** * Utility type for getting the JSX prop types for a given component. In particular: * * - Excludes LitElement properties that should not be exposed to the end-user * - Converts event .emit() properties into event callbacks * - Replaces original HTMLElement property typings with the ones we provide for * greater type-safety and control * - For native events listeners, makes currentTarget equal to the component's * HTML instance * * We mark all component properties as optional because there is no easy * way for us to differentiate between public component properties and just * regular members that don't have `@property()` decorator (presence or * absence of decorator does not change the type). Thus, if we don't mark all * properties as optional, TypeScript will force us to provide a value for all * methods, and regular properties component may have. */ export type ToJsx = HTMLAttributes & Partial>> & RemapEvents; /** * From event emitters create event listener callbacks */ type RemapEvents = { [Key in keyof Component as `on${Key & string}`]?: unknown extends Component[Key] ? never : Component[Key] extends { emit: (...rest: never[]) => infer PayloadType; } ? (event: PayloadType & { currentTarget: Component["el"]; target: Component["el"]; }) => void : never; }; /** * This interface will be automatically extended in src/lumina.ts files to add * typings for Stencil elements usages in Lumina. You do not need to manually * extend this interface. * * @private * @example * ```ts * import type { JSX as CalciteJSX } from "@esri/calcite-components/dist/types/components"; * import type { JSX as CommonComponentsJsx } from "@arcgis/common-components/dist/types/components"; * * declare module "@arcgis/lumina" { * interface ImportStencilElements * extends CalciteJSX.IntrinsicElements, * CommonComponentsJsx.IntrinsicElements { * } * } * ``` */ export interface ImportStencilElements { } /** * Get the type of all events for a given component. Includes native DOM events * and custom events. * * @example * ```tsx * render() { * return ; * } * _handleViewClick(event: ToEvents["arcgisViewClick"]) { * // event.detail * // event.currentTarget * } * ``` * * @remarks * This helper is intended to be used on components defined within the same * package. For external components, you can use * `HTMLArcgisMapElement["arcgisViewClick"]` directly, without need for * `ToEvents<>`. * * @remarks * Alternative implementation of this type that is simpler, and potentially * more performant, but looses "go to definition" information. * * ```tsx * export type ToEvents = { * [Key in keyof ToJsx as Key extends `on${infer EventName}` * ? Uncapitalize * : never]-?: ListenerToPayloadType[Key]>; * }; * * type ListenerToPayloadType = Listener extends (event: infer EventType) => unknown ? EventType : never; * ``` * * TypeScript issues that may fix this: * - https://github.com/microsoft/TypeScript/issues/49909 * - https://github.com/microsoft/TypeScript/issues/50715 */ export type ToEvents = GlobalEventTypes> & { [Key in keyof Component as ListenerToPayloadType extends BaseEvent ? Key : never]-?: ListenerToPayloadType & { currentTarget: MaybeEl; target: MaybeEl; }; }; type MaybeEl = Component extends { el: unknown; } ? Component["el"] : Component; export interface TargetedEvent extends BaseEvent { /** * Returns any custom data event was created with. * * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail) */ readonly detail: Payload; /** * Returns the object whose event listener's callback is currently being invoked. * * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/currentTarget) */ readonly currentTarget: Target; /** * Returns the object to which event is dispatched (its target). * * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Event/target) */ readonly target: Target; } interface BaseEvent extends Omit { /** @deprecated */ initCustomEvent(type: string, bubbles?: boolean, cancelable?: boolean, detail?: any): void; } /** * From "GlobalEventHandlersCamelCase" extract event names and their payloads. * Not using "HTMLElementEventMap" because that map has all events in lowercase. */ type GlobalEventTypes = { [Key in keyof GlobalEventHandlersCamelCase as Key extends `on${infer EventName}` ? Uncapitalize : never]-?: Parameters[Key]>>[0]; }; type ListenerToPayloadType = unknown extends Listener ? void : Listener extends { emit: (...rest: never[]) => infer PayloadType; } ? PayloadType : Listener extends BaseEvent ? Listener : void; /** * Defined Lumina custom elements. This interface is used only for internal * type-checking. */ export interface DeclareElements { } type ReMappedComponents = { [Key in keyof Components]: Components[Key] extends { el: unknown; } ? ToJsx : never; }; /** * For common properties, add Lumina's types rather than use Stencil's types */ type ReMapStencilComponents = { [Key in keyof Pick]: HTMLAttributes & ReMapStencilComponent; }; type ReMapStencilComponent = { [Key in keyof Component as FixupStencilEventCasing]: Component[Key]; }; type FixupStencilEventCasing = PropertyName extends `on${infer EventName}` ? `on${Uncapitalize}` : PropertyName; /** * These typings are based on dom-expressions typings: * https://github.com/ryansolid/dom-expressions/blob/main/packages/dom-expressions/src/jsx.d.ts * * They in turn based the typings on Surplus and Inferno: * - https://github.com/adamhaile/surplus/blob/master/index.d.ts * - https://github.com/infernojs/inferno/blob/master/packages/inferno/src/core/types.ts * * Documentation about how to type JSX in TypeScript: * https://www.typescriptlang.org/docs/handbook/jsx.html */ export declare namespace LuminaJsx { export type Element = TemplateResult; export type ElementType = JsxNode; export interface ElementClass { } export interface ElementAttributesProperty { } export interface IntrinsicClassAttributes { } export interface ElementChildrenAttribute { children: JsxNode; } export interface IntrinsicAttributes { /** * The `key` is a special attribute that can be set on any element. * * At build-time it is translated into the `keyed()` directive: * https://lit.dev/docs/templates/directives/#keyed * * @remarks * Unlike in React or Stencil, any JavaScript value is acceptable as a key */ key?: unknown; } export interface IntrinsicElements extends HTMLElementTags, SvgElementTags, ReMappedComponents, ReMapStencilComponents { } export type { CustomAttributes, DOMAttributes, HTMLAttributes, HTMLElementTags, SvgElementTags, AriaAttributes }; } export {};