From 32509457737e0cf2cf9ead1f99cf55b765e3a257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Fri, 12 Apr 2024 13:34:43 +0200 Subject: [PATCH 1/9] initial changes for component type defs --- src/component.d.ts | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/component.d.ts b/src/component.d.ts index d5e46f35..18b3c91e 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -50,13 +50,22 @@ declare namespace Component { } type Props = string | AdvancedProp + type PropsArray = T[] + + // type ExtractPropNames = T extends (infer U)[] + // ? U extends string + // ? U + // : U extends AdvancedProp + // ? U['key'] + // : never + // : never; type NotFunction = T extends Function ? never : T; /** * Internal state of a Component instance */ - interface StateObject { + interface State { [key: string]: any } @@ -74,31 +83,25 @@ declare namespace Component { interface Input { [key: string]: (this: ComponentInstance, event: KeyboardEvent) => void, - /** - * Catch all input function - * - * Will be invoked when there is no dedicated function for a certain key - */ - 'any'?: (this: ComponentInstance, event: KeyboardEvent) => void, } interface Log { /** * Log an info message */ - info: typeof console.info + info(...args): typeof console.info /** * Log an error message */ - error: typeof console.error + error(...args): typeof console.error /** * Log a warning */ - warn: typeof console.warn + warn(...args): typeof console.warn /** * Log a debug message */ - debug: typeof console.debug + debug(...args): typeof console.debug } interface Hooks { @@ -225,7 +228,7 @@ declare namespace Component { focus?: () => void } - export interface ComponentConfig { + export interface ComponentConfig { components?: any, /** * XML-based template string of the Component @@ -261,7 +264,7 @@ declare namespace Component { * } * ``` */ - state?: (this: ComponentInstance) => StateObject, + state?: (this: { [K in Props]: any}) => State, /** * Allowed props to be passed into the Component by the parent * @@ -279,27 +282,27 @@ declare namespace Component { * }] * ``` */ - props?: Props[], + props?: PropsArray | AdvancedProp[], /** * Computed properties */ - computed?: Computed, + computed?: Computed & ThisType, /** * Watchers for changes to state variables, props or computed properties */ - watch?: Watch, + watch?: Watch & ThisType, /** * Hooking into Lifecycle events */ - hooks?: Hooks, + hooks?: Hooks & ThisType, /** * Methods for abstracting more complex business logic into separate function */ - methods?: Methods, + methods?: Methods & ThisType, /** * Tapping into user input */ - input?: Input + input?: Input & ThisType, } } @@ -307,9 +310,9 @@ declare namespace Component { /** * Blits.Component() */ -declare function Component( +declare function Component( name: Component.Name, - config: Component.ComponentConfig, + config: Component.ComponentConfig, ) : Component.ComponentInstance export default Component; From 46d8dfdb9ec03035711029988489dcf1cab2d20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Fri, 12 Apr 2024 15:04:29 +0200 Subject: [PATCH 2/9] updated component.d.ts --- src/component.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component.d.ts b/src/component.d.ts index 18b3c91e..528a279f 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -310,7 +310,7 @@ declare namespace Component { /** * Blits.Component() */ -declare function Component( +declare function Component( name: Component.Name, config: Component.ComponentConfig, ) : Component.ComponentInstance From f2e5f85afd99238f42400e2aed4e9096cd39997c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Fri, 12 Apr 2024 15:31:19 +0200 Subject: [PATCH 3/9] fixed advancedprop type def --- src/component.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/component.d.ts b/src/component.d.ts index 528a279f..6a4ee4c8 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -48,9 +48,8 @@ declare namespace Component { */ cast?: () => any } - - type Props = string | AdvancedProp type PropsArray = T[] + type AdvancedProps = AdvancedProp[] // type ExtractPropNames = T extends (infer U)[] // ? U extends string From 894b96b125aa03bfe373b21a3c665bdc5dde4b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Mon, 15 Apr 2024 23:10:39 +0200 Subject: [PATCH 4/9] alternative type definitions for components --- src/application.d.ts | 5 ++-- src/component.d.ts | 65 ++++++++++++++++++++++++++------------------ src/component.js | 7 +++++ 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/application.d.ts b/src/application.d.ts index 2b14c5ed..b3e8082b 100644 --- a/src/application.d.ts +++ b/src/application.d.ts @@ -16,7 +16,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {default as Component, ComponentInstance} from './component' +import {default as Component} from './component' +import ComponentInstance from './component' declare namespace Application { @@ -144,7 +145,7 @@ declare namespace Application { export interface ApplicationInstance extends ComponentInstance {} - export interface ApplicationConfig extends Component.ComponentConfig { + export interface ApplicationConfig extends Component.ComponentConfig { /** * Routes definition * diff --git a/src/component.d.ts b/src/component.d.ts index 6a4ee4c8..6d0765eb 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -68,20 +68,27 @@ declare namespace Component { [key: string]: any } - interface Computed { - [key: string]: (this: ComponentInstance) => any + interface Computed { + [key: string]: (this: ComponentInstance & S & M & P) => any } - interface Watch { - [key: string]: (this: ComponentInstance, value: any, oldValue: any) => void + interface Watch { + [key: string]: (this: ComponentInstance & S & M & P, value: any, oldValue: any) => void } interface Methods { - [key: string]: (this: ComponentInstance) => any + [methodName: string]: (this: ComponentInstance & S & M & P) => any; } - interface Input { - [key: string]: (this: ComponentInstance, event: KeyboardEvent) => void, + interface Input { + [key: string]: ((this: ComponentInstance & S & M & P, event: KeyboardEvent) => void) | undefined, + + /** + * Catch all input function + * + * Will be invoked when there is no dedicated function for a certain key + */ + any?: (this: ComponentInstance, event: KeyboardEvent) => void, } interface Log { @@ -103,62 +110,62 @@ declare namespace Component { debug(...args): typeof console.debug } - interface Hooks { + interface Hooks { /** * Fires when the Component is being instantiated. * At this moment child elements will not be available yet */ - init?: (this: ComponentInstance) => void + init?: (this: ComponentInstance & S & M & P) => void /** * Fires when the Component is fully initialized and ready for interaction. */ - ready?: (this: ComponentInstance) => void + ready?: (this: ComponentInstance & S & M & P) => void /** * Triggers when the Component receives focus. * * This event can fire multiple times during the component's lifecycle */ - focus?: (this: ComponentInstance) => void + focus?: (this: ComponentInstance & S & M & P) => void /** * Triggers when the Component loses focus. * * This event can fire multiple times during the component's lifecycle */ - unfocus?: (this: ComponentInstance) => void + unfocus?: (this: ComponentInstance & S & M & P) => void /** * Fires when the Component is being destroyed and removed. */ - destroy?: (this: ComponentInstance) => void, + destroy?: (this: ComponentInstance & S & M & P) => 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?: (this: ComponentInstance) => void, + frameTick?: (this: ComponentInstance & S & M & P) => 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?: (this: ComponentInstance) => void, + attach?: (this: ComponentInstance & S & M & P) => 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?: (this: ComponentInstance) => void, + detach?: (this: ComponentInstance & S & M & P) => void, /** * Fires when the component enters the visible viewport * * This event can fire multiple times during the component's lifecycle */ - enter?: (this: ComponentInstance) => void, + enter?: (this: ComponentInstance & S & M & P) => void, /** * Fires when the component leaves the visible viewport * * This event can fire multiple times during the component's lifecycle */ - exit?: (this: ComponentInstance) => void, + exit?: (this: ComponentInstance & S & M & P) => void, } export interface ComponentInstance { @@ -227,7 +234,7 @@ declare namespace Component { focus?: () => void } - export interface ComponentConfig { + export interface ComponentConfig { components?: any, /** * XML-based template string of the Component @@ -263,7 +270,7 @@ declare namespace Component { * } * ``` */ - state?: (this: { [K in Props]: any}) => State, + state?: (this: { [K in Props]: any}) => S, /** * Allowed props to be passed into the Component by the parent * @@ -285,23 +292,23 @@ declare namespace Component { /** * Computed properties */ - computed?: Computed & ThisType, + computed?: Computed, /** * Watchers for changes to state variables, props or computed properties */ - watch?: Watch & ThisType, + watch?: Watch, /** * Hooking into Lifecycle events */ - hooks?: Hooks & ThisType, + hooks?: Hooks, // & ThisType, /** * Methods for abstracting more complex business logic into separate function */ - methods?: Methods & ThisType, + methods?: M & ThisType, /** * Tapping into user input */ - input?: Input & ThisType, + input?: Input } } @@ -309,9 +316,13 @@ declare namespace Component { /** * Blits.Component() */ -declare function Component( +declare function Component< + Props extends string, + S extends Component.State, + M extends Component.Methods + >( name: Component.Name, - config: Component.ComponentConfig, + config: Component.ComponentConfig, ) : Component.ComponentInstance export default Component; diff --git a/src/component.js b/src/component.js index c3fe13cd..2a50b97d 100644 --- a/src/component.js +++ b/src/component.js @@ -45,6 +45,13 @@ const required = (name) => { throw new Error(`Parameter ${name} is required`) } +/** + * Component factory function + * @param {string} name - The name of the component + * @param {object} config - The configuration object for the component + * @returns {function} - A factory function that creates a new component instance + * + */ const Component = (name = required('name'), config = required('config')) => { const setupComponent = (parentComponent) => { // code generation From a9fbd89a0cce35b0b8a8a914bbf1c81850582eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Tue, 16 Apr 2024 00:16:25 +0200 Subject: [PATCH 5/9] fixed input's any method in component type defs --- src/component.d.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/component.d.ts b/src/component.d.ts index 6a4ee4c8..c9d7c687 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -81,7 +81,14 @@ declare namespace Component { } interface Input { - [key: string]: (this: ComponentInstance, event: KeyboardEvent) => void, + [key: string]: ((this: ComponentInstance, event: KeyboardEvent) => void) | undefined, + + /** + * Catch all input function + * + * Will be invoked when there is no dedicated function for a certain key + */ + any?: (this: ComponentInstance, event: KeyboardEvent) => void, } interface Log { From 266408d96e8f32becfde8e7b154d97e0a52ecc67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Tue, 16 Apr 2024 11:42:14 +0200 Subject: [PATCH 6/9] removed generics from methods --- src/component.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component.d.ts b/src/component.d.ts index 6d0765eb..8fe00739 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -77,7 +77,7 @@ declare namespace Component { } interface Methods { - [methodName: string]: (this: ComponentInstance & S & M & P) => any; + [methodName: string]: (this: ComponentInstance) => any; } interface Input { From 52c16f7c6f5ace1487141f2378413d36de636d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Wed, 17 Apr 2024 16:16:50 +0200 Subject: [PATCH 7/9] removed temp type from component instance --- src/component.d.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/component.d.ts b/src/component.d.ts index 8fe00739..6fecc0a3 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -226,8 +226,6 @@ declare namespace Component { */ select: (ref: string) => ComponentInstance | ElementInstance - // tmp - [key: string]: any } export interface ElementInstance { From d600b01378cbddb3c03b737595715a485853948e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Tue, 7 May 2024 10:21:02 +0200 Subject: [PATCH 8/9] added router, announcer and did some updates --- src/component.d.ts | 139 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 16 deletions(-) diff --git a/src/component.d.ts b/src/component.d.ts index 6fecc0a3..abf52e81 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -22,6 +22,47 @@ declare namespace Component { */ type Name = string + + interface Announcer { + /** + * Announce a message to screen readers by specifying the politeness level + */ + speak: (message: string, politeness?: 'polite' | 'assertive') => void + + /** + * Announce a message to screen readers with a polite politeness level + */ + polite: (message: string) => void + + /** + * Announce a message to screen readers with an assertive politeness level + */ + assertive: (message: string) => void + + /** + * Stop all announcements + */ + stop: () => void + } + + 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 + } interface AdvancedProp { /** * Name of the prop @@ -77,9 +118,13 @@ declare namespace Component { } interface Methods { - [methodName: string]: (this: ComponentInstance) => any; + [key: string]: (this: ComponentInstance & State & Methods, ...args: any) => any; } + // interface MethodsExtended extends Methods { + // [key: string]: (this: ComponentInstance & S & M & P) => any; + // } + interface Input { [key: string]: ((this: ComponentInstance & S & M & P, event: KeyboardEvent) => void) | undefined, @@ -88,7 +133,7 @@ declare namespace Component { * * Will be invoked when there is no dedicated function for a certain key */ - any?: (this: ComponentInstance, event: KeyboardEvent) => void, + any?: (this: ComponentInstance & S & M & P, event: KeyboardEvent) => void, } interface Log { @@ -115,57 +160,103 @@ declare namespace Component { * Fires when the Component is being instantiated. * At this moment child elements will not be available yet */ - init?: (this: ComponentInstance & S & M & P) => void + init?: (this: ComponentInstance & S & M & P, ...args: any) => void /** * Fires when the Component is fully initialized and ready for interaction. */ - ready?: (this: ComponentInstance & S & M & P) => void + ready?: (this: ComponentInstance & S & M & P, ...args: any) => void /** * Triggers when the Component receives focus. * * This event can fire multiple times during the component's lifecycle */ - focus?: (this: ComponentInstance & S & M & P) => void + focus?: (this: ComponentInstance & S & M & P, ...args: any) => void /** * Triggers when the Component loses focus. * * This event can fire multiple times during the component's lifecycle */ - unfocus?: (this: ComponentInstance & S & M & P) => void + unfocus?: (this: ComponentInstance & S & M & P, ...args: any) => void /** * Fires when the Component is being destroyed and removed. */ - destroy?: (this: ComponentInstance & S & M & P) => void, + destroy?: (this: ComponentInstance & S & M & P, ...args: any) => 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?: (this: ComponentInstance & S & M & P) => void, + frameTick?: (this: ComponentInstance & S & M & P, ...args: any) => 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?: (this: ComponentInstance & S & M & P) => void, + attach?: (this: ComponentInstance & S & M & P, ...args: any) => 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?: (this: ComponentInstance & S & M & P) => void, + detach?: (this: ComponentInstance & S & M & P, ...args: any) => void, /** * Fires when the component enters the visible viewport * * This event can fire multiple times during the component's lifecycle */ - enter?: (this: ComponentInstance & S & M & P) => void, + enter?: (this: ComponentInstance & S & M & P, ...args: any) => void, /** * Fires when the component leaves the visible viewport * * This event can fire multiple times during the component's lifecycle */ - exit?: (this: ComponentInstance & S & M & P) => void, + exit?: (this: ComponentInstance & S & M & P, ...args: any) => void, + } + + /** + * Route object + */ + interface Route { + path: string; + params: Record; + options?: { + inHistory?: boolean; + keepAlive?: boolean; + [key: string]: any; + }; + data?: Record; + hooks?: { + before?: (route: Route, previousRoute: Route) => Promise; + }; + } + + + interface Router { + /** + * Navigate to a different location + */ + to(location: string, data?: Record, options?: Record): 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; + } export interface ComponentInstance { @@ -226,13 +317,29 @@ declare namespace Component { */ select: (ref: string) => ComponentInstance | ElementInstance + /** + * Announcer methods for screen reader support + */ + $announcer: Announcer + + /** + * Triggers a forced update on state variables. + */ + $trigger: (key: string) => void + trigger: (key: string) => void + + /** + * Router instance + */ + $router: Router + } export interface ElementInstance { focus?: () => void } - export interface ComponentConfig { + export interface ComponentConfig { components?: any, /** * XML-based template string of the Component @@ -298,11 +405,11 @@ declare namespace Component { /** * Hooking into Lifecycle events */ - hooks?: Hooks, // & ThisType, + hooks?: Hooks, /** * Methods for abstracting more complex business logic into separate function */ - methods?: M & ThisType, + methods?: M //MethodsExtended, /** * Tapping into user input */ @@ -320,7 +427,7 @@ declare function Component< M extends Component.Methods >( name: Component.Name, - config: Component.ComponentConfig, + config: Component.ComponentConfig ) : Component.ComponentInstance export default Component; From 73cd27ef68e41bb8a09da7a7d77844177cd9787b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Aslan?= Date: Tue, 7 May 2024 11:47:50 +0200 Subject: [PATCH 9/9] updated application.d.ts --- src/application.d.ts | 19 +++++++++---------- src/component.d.ts | 19 ++++++++++--------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/application.d.ts b/src/application.d.ts index b3e8082b..d2635907 100644 --- a/src/application.d.ts +++ b/src/application.d.ts @@ -17,7 +17,6 @@ */ import {default as Component} from './component' -import ComponentInstance from './component' declare namespace Application { @@ -85,10 +84,6 @@ declare namespace Application { politeness?: 'off' | 'polite' | 'assertive' } - function Application( - config: Application.ApplicationConfig - ) : Application.ApplicationInstance - type RequireAtLeastOne = { [K in keyof T]-?: Required> & Partial>> }[keyof T] @@ -105,7 +100,7 @@ declare namespace Application { /** * Component to load when activating the route */ - component: typeof ComponentInstance, + component: Component, /** * Transition configuration for the route */ @@ -143,9 +138,9 @@ declare namespace Application { } } - export interface ApplicationInstance extends ComponentInstance {} + export interface ApplicationInstance extends Component.ComponentInstance {} - export interface ApplicationConfig extends Component.ComponentConfig { + export interface ApplicationConfig extends Component.ComponentConfig { /** * Routes definition * @@ -168,8 +163,12 @@ declare namespace Application { * * Root App component */ -declare function Application( - config: Application.ApplicationConfig +declare function Application< + Props extends string, + S extends Component.State, + M extends Component.Methods + >( + config: Application.ApplicationConfig ) : Application.ApplicationInstance export default Application; diff --git a/src/component.d.ts b/src/component.d.ts index abf52e81..1701c1ae 100644 --- a/src/component.d.ts +++ b/src/component.d.ts @@ -217,17 +217,18 @@ declare namespace Component { * Route object */ interface Route { - path: string; - params: Record; + path: string, + params: Record, options?: { - inHistory?: boolean; - keepAlive?: boolean; - [key: string]: any; - }; - data?: Record; + inHistory?: boolean, + keepAlive?: boolean, + [key: string]: any, + }, + data?: Record, + component?: (args: { props: any }, holder: any, context: any) => Promise, hooks?: { - before?: (route: Route, previousRoute: Route) => Promise; - }; + before?: (route: Route, previousRoute: Route) => Promise, + } }