/* * Copyright 2023 Comcast Cable Communications Management, LLC * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 */ // blits file type reference /// import {type ShaderEffect as RendererShaderEffect, type WebGlCoreShader, type RendererMainSettings} from '@lightningjs/renderer' declare module '@lightningjs/blits' { export interface AnnouncerUtteranceOptions { /** * Language code (BCP 47 format, e.g., 'en-US', 'fr-FR') * * @default 'en-US' */ lang?: string, /** * Voice pitch (0 to 2, where 1 is normal) * * @default 1 */ pitch?: number, /** * Speech rate (0.1 to 10, where 1 is normal) * * @default 1 */ rate?: number, /** * Voice to use (obtained from `speechSynthesis.getVoices()`) * * @default null */ voice?: SpeechSynthesisVoice | null, /** * Volume level (0 to 1, where 1 is full volume) * * @default 1 */ volume?: number, } export interface AnnouncerUtterance extends Promise { /** * Removes a specific message from the announcement queue, * to make sure it isn't spoke out. * * Does not interupt the message when it's already being announced. */ cancel() /** * Interrupts a specific message as it is being spoken out by the Text to Speech * engine. */ stop() } export interface Announcer { /** * Instruct the Announcer to speak a message. Will add the message * to the end of announcement queue by default * * When a message is added with politeness set to `assertive` the message * will be added to the beginning of the queue * * @param message - The message to be spoken * @param politeness - Politeness level ('off', 'polite', or 'assertive') * @param options - Optional utterance options (rate, pitch, lang, voice, volume) */ speak(message: string | number, politeness?: 'off' | 'polite' | 'assertive', options?: AnnouncerUtteranceOptions): AnnouncerUtterance; /** * Instruct the Announcer to speak a message with 'polite' politeness level. * Will add the message to the end of announcement queue. * * @param message - The message to be spoken * @param options - Optional utterance options (rate, pitch, lang, voice, volume) */ polite(message: string | number, options?: AnnouncerUtteranceOptions): AnnouncerUtterance; /** * Instruct the Announcer to speak a message with 'assertive' politeness level. * Will add the message to the beginning of announcement queue. * * @param message - The message to be spoken * @param options - Optional utterance options (rate, pitch, lang, voice, volume) */ assertive(message: string | number, options?: AnnouncerUtteranceOptions): AnnouncerUtterance; /** * Configure global default utterance options that will be applied to all * subsequent announcements unless overridden by per-call options. * * @param options - Default utterance options (rate, pitch, lang, voice, volume) */ configure(options?: AnnouncerUtteranceOptions): void; /** * Instruct the Announcer to add a pause of a certain duration (in ms). Will add this pause * to the end of announcement queue * */ pause(delay: number): AnnouncerUtterance; /** * Interupts and instantly stops any running text to speech utterance * */ stop(): void; /** * Clears out the announcement queue of messages. */ clear(): void; /** * Enables the announcer. */ enable(): void; /** * Disables the announcer. Any messages passed in the announcer.speak() message * will not be added to the queue */ disable(): void; /** * Toggles the announcer based on the passed toggle value (Boolean) */ toggle(toggle: Boolean): void; } export interface Hooks { /** * Fires when the Component is being instantiated * At this moment child elements will not be available yet */ init?: () => void; /** * Fires when the Component is fully initialized and ready for interaction. */ ready?: () => void; /** * Triggers when the Component receives focus. * * This event can fire multiple times during the component's lifecycle */ focus?: () => void; /** * Triggers when the Component loses focus. * * This event can fire multiple times during the component's lifecycle */ unfocus?: () => void; /** * Fires when the Component is being destroyed and removed. */ destroy?: () => void; /** * Fires upon each frame start (allowing you to tap directly into the renderloop) * * Note: This hook will fire continuously, multiple times per second! */ frameTick?: (data: {time: number, delta: number}) => void; /** * Fires when the component enters the viewport _margin_ and is attached to the render tree * * This event can fire multiple times during the component's lifecycle */ attach?: () => void; /** * Fires when the component leaves the viewport _margin_ and is detached from the render tree * * This event can fire multiple times during the component's lifecycle */ detach?: () => void; /** * Fires when the component enters the visible viewport * * This event can fire multiple times during the component's lifecycle */ enter?: () => void; /** * Fires when the component leaves the visible viewport * * This event can fire multiple times during the component's lifecycle */ exit?: () => void; /** * Fires when the renderer is done rendering and enters an idle state * * Note: This event can fire multiple times */ idle?: () => void; /** * Fires at a predefined interval and reports the current FPS value * * Note: This event fire multiple times */ fpsUpdate?: (fps: number) => void; } export interface Input { [key: string]: (event: KeyboardEvent) => void | undefined | unknown, /** * Catch all input function * * Will be invoked when there is no dedicated function for a certain key */ // @ts-ignore any?: (event: KeyboardEvent) => void, /** * Intercept key presses on the root Application component before being handled * by the currently focused component. * * Only when a KeyboardEvent (the original one, or a modified one) is returned from the * intercept function, the Input event is passed on to the Component with focus. * * The intercept function can be asynchronous. * * Note: the intercept input handler is only available on the Root App component (i.e. Blits.Application) */ intercept?: (event: KeyboardEvent) => KeyboardEvent | Promise | any } export interface Log { /** * Log an info message */ info(...args): typeof console.info /** * Log an error message */ error(...args): typeof console.error /** * Log a warning */ warn(...args): typeof console.warn /** * Log a debug message */ debug(...args): typeof console.debug } export interface RouteData { [key: string]: any } /** * Router Options that can be used at the same time. */ interface ConcurrentRouteOpts { /** * Whether the page navigation should be added to the history stack * used when navigating back using `this.$router.back()` * * @default true */ inHistory?: boolean, passFocus?: boolean, } /** * Route Options that can't be true at the same time */ type MutualExclusiveRouteOpts = { /** * Whether the router should reuse the current page component instance (when matching with the Component * specified for the route that we're routing to). * * @default true */ reuseComponent?: true, /** * Whether the page should be kept alive when navigating away. Can be useful * for a homepage where the state should be fully retained when navigating back * from a details page * * @default false */ keepAlive?: false } | { /** * Whether the router should reuse the current page component instance (when matching with the Component * specified for the route that we're routing to). * * @default true */ reuseComponent?: false, /** * Whether the page should be kept alive when navigating away. Can be useful * for a homepage where the state should be fully retained when navigating back * from a details page * * @default false */ keepAlive?: true } export type RouteOptions = ConcurrentRouteOpts & MutualExclusiveRouteOpts; export interface Router { /** * Navigate to a different location * * @param {string} */ to(location: string, data?: RouteData, options?: RouteOptions): void; /** * Navigate to the previous location */ back(): boolean; /** * Get the current route read-only */ readonly currentRoute: Route; /** * Get the list of all routes */ readonly routes: Route[]; /** * Get navigating state */ readonly navigating: boolean; /** * Reactive router state */ state: { /** * Path of the current route * * Can be used in: * - a template as `$$router.state.path` * - inside business logic as `this.$router.state.path` * - as a watcher as `$router.state.path(v) {}` */ readonly path: string /** * Whether or not the router is currently in the process of navigating * between pages * * Can be used in: * - a template as `$$router.state.navihating` * - inside business logic as `this.$router.state.navigating` * - as a watcher as `$router.state.navigating(v) {}` */ readonly navigating: boolean } } export type ComponentBase = { /** * Indicates whether the component currently has focus * * @returns Boolean */ hasFocus: boolean, /** * Listen to events emitted by other components */ $listen: { (event: string, callback: (args: any) => void, priority?: number): void; } /** * Remove an event listener previously registered with $listen */ $unlisten: { (event: string): void; } /** * Emit events that other components can listen to * @param name - name of the event to be emitted * @param data - optional data to be passed along * @param byReference - whether or not to pass the data by reference. * The default behaviour is passing the data object by reference (`true`). * When explicitely passing `false` the object will be recursively cloned * and cleaned from any potential reactivity before emitting */ $emit(name: string, data?: any, byReference?: boolean): void; /** * Remove all listeners for this component from all events */ $clearListeners: { (): void; } /** * Set a timeout that is automatically cleaned upon component destroy */ $setTimeout: (callback: (args: any) => void, ms?: number | undefined) => ReturnType /** * Clear a timeout */ $clearTimeout: (id: ReturnType) => void /** * Set an interval that is automatically cleaned upon component destroy */ $setInterval: (callback: (args: any) => void, ms?: number | undefined) => ReturnType /** * Clear a interval */ $clearInterval: (id: ReturnType) => void /** * Log to the console with prettier output and configurable debug levels in Settings */ $log: Log /** * Set focus to the Component, optionally pass a KeyboardEvent for instant event bubbling */ $focus: (event?: KeyboardEvent) => void /** * Handle a keyboard event on this component without changing focus * @param event - The keyboard event to handle * @returns Returns true if this component or a parent component handled the event, false otherwise */ $input: (event: KeyboardEvent) => boolean /** * @deprecated * Deprecated: use `this.$focus()` instead */ focus: (event?: KeyboardEvent) => void /** * Select a child Element or Component by ref * * Elements and Components in the template can have an optional ref argument. * Returns an Element Instance or Component Instance. * Useful for passing on the focus to a Child component. * * @example * ```js * const menu = this.$select('Menu') * if(menu) { * menu.$focus() * } * ``` */ $select: (ref: string) => ComponentBase /** * @deprecated * Deprecated: use `this.$select()` instead */ select: (ref: string) => ComponentBase /** * Announcer methods for screen reader support */ $announcer: Announcer /** * Triggers a forced update on state variables. */ $trigger: (key: string) => void /** * @deprecated * * Triggers a forced update on state variables. * Deprecated: use `this.$trigger()` instead */ trigger: (key: string) => void /** * Router instance */ $router: Router /** * Dynamically set the size of a component holder node */ $size: (dimensions: { /** * Component width */ w: number, /** * Component height */ h: number }) => void } /** * Prop object */ export type PropObject = { /** * Name of the prop */ key: string, /** * Whether the prop is required to be passed */ required?: boolean, /** * Default value for the prop when omited */ default?: any, /** * Cast the value of the prop * * @example * ```js * { * cast: Number, // casts to a number * cast: (v) => v.toUpperCase() // casts to uppercase * } * ``` */ cast?: () => any }; // Props Array export type Props = (string | PropObject)[]; // Extract the prop names from the props array type ExtractPropNames

= { readonly [K in P[number] as K extends string ? K : K extends { key: infer Key } ? Key : never]: any; }; // Update the PropsDefinition to handle props as strings or objects export type PropsDefinition

= ExtractPropNames

; export type ComponentContext

= ThisType & S & M & C & ComponentBase> export interface ComponentConfig

{ components?: { [key: string]: ComponentFactory, }, /** * XML-based template string of the Component * * @example * ```xml * * *