diff --git a/src/LGraph.ts b/src/LGraph.ts index 5dcc289296..69ef05ffe7 100644 --- a/src/LGraph.ts +++ b/src/LGraph.ts @@ -1477,11 +1477,12 @@ export class LGraph implements LinkNetwork, BaseLGraph, Serialisable>( name: string, type: ISlotType, - extra_info?: Partial, - ): INodeOutputSlot { - const output = new NodeOutputSlot({ name, type, links: null }, this) - if (extra_info) Object.assign(output, extra_info) + extra_info?: TProperties, + ): INodeOutputSlot & TProperties { + const output = Object.assign( + new NodeOutputSlot({ name, type, links: null }, this), + extra_info, + ) this.outputs ||= [] this.outputs.push(output) @@ -1470,10 +1472,13 @@ export class LGraphNode implements Positionable, IPinnable, IColorable { * @param type string defining the input type ("vec3","number",...), it its a generic one use 0 * @param extra_info this can be used to have special properties of an input (label, color, position, etc) */ - addInput(name: string, type: ISlotType, extra_info?: Partial): INodeInputSlot { - type = type || 0 - const input = new NodeInputSlot({ name: name, type: type, link: null }, this) - if (extra_info) Object.assign(input, extra_info) + addInput>(name: string, type: ISlotType, extra_info?: TProperties): INodeInputSlot & TProperties { + type ||= 0 + + const input = Object.assign( + new NodeInputSlot({ name, type, link: null }, this), + extra_info, + ) this.inputs ||= [] this.inputs.push(input) diff --git a/src/LLink.ts b/src/LLink.ts index 99876bf6eb..7f3f0bc2f5 100644 --- a/src/LLink.ts +++ b/src/LLink.ts @@ -22,7 +22,7 @@ export type SerialisedLLinkArray = [ type: ISlotType, ] -interface ResolvedConnection { +export interface ResolvedConnection { inputNode: LGraphNode | undefined outputNode: LGraphNode | undefined input: INodeInputSlot | undefined diff --git a/src/infrastructure/CustomEventTarget.ts b/src/infrastructure/CustomEventTarget.ts index d660657fa1..9008c3fa06 100644 --- a/src/infrastructure/CustomEventTarget.ts +++ b/src/infrastructure/CustomEventTarget.ts @@ -1,21 +1,84 @@ -/** {@link Omit} all properties that evaluate to `never`. */ -type NeverNever = { - [K in keyof T as T[K] extends never ? never : K]: T[K] -} - -/** {@link Pick} only properties that evaluate to `never`. */ -type PickNevers = { - [K in keyof T as T[K] extends never ? K : never]: T[K] -} +import type { NeverNever, PickNevers } from "@/types/utility" type EventListeners = { readonly [K in keyof T]: ((this: EventTarget, ev: CustomEvent) => any) | EventListenerObject | null } +/** + * Has strongly-typed overrides of {@link EventTarget.addEventListener} and {@link EventTarget.removeEventListener}. + */ +export interface ICustomEventTarget< + EventMap extends Record, + Keys extends keyof EventMap & string = keyof EventMap & string, +> { + addEventListener( + type: K, + listener: EventListeners[K], + options?: boolean | AddEventListenerOptions, + ): void + + removeEventListener( + type: K, + listener: EventListeners[K], + options?: boolean | EventListenerOptions, + ): void + + /** @deprecated Use {@link dispatch}. */ + dispatchEvent(event: never): boolean +} + +/** + * Capable of dispatching strongly-typed events via {@link dispatch}. + * Overloads are used to ensure detail param is correctly optional. + */ +export interface CustomEventDispatcher< + EventMap extends Record, + Keys extends keyof EventMap & string = keyof EventMap & string, +> { + dispatch>(type: T, detail: EventMap[T]): boolean + dispatch>(type: T): boolean +} + +/** + * A strongly-typed, custom {@link EventTarget} that can dispatch and listen for events. + * + * 1. Define an event map + * ```ts + * export interface CustomEventMap { + * "my-event": { message: string } + * "simple-event": never + * } + * ``` + * + * 2. Create an event emitter + * ```ts + * // By subclassing + * class MyClass extends CustomEventTarget { + * // ... + * } + * + * // Or simply create an instance: + * const events = new CustomEventTarget() + * ``` + * + * 3. Dispatch events + * ```ts + * // Extended class + * const myClass = new MyClass() + * myClass.dispatch("my-event", { message: "Hello, world!" }) + * myClass.dispatch("simple-event") + * + * // Instance + * const events = new CustomEventTarget() + * events.dispatch("my-event", { message: "Hello, world!" }) + * events.dispatch("simple-event") + * ``` + */ export class CustomEventTarget< EventMap extends Record, Keys extends keyof EventMap & string = keyof EventMap & string, -> extends EventTarget { +> + extends EventTarget implements ICustomEventTarget { /** * Type-safe event dispatching. * @see {@link EventTarget.dispatchEvent} diff --git a/src/infrastructure/LGraphCanvasEventMap.ts b/src/infrastructure/LGraphCanvasEventMap.ts new file mode 100644 index 0000000000..aa12d00f2c --- /dev/null +++ b/src/infrastructure/LGraphCanvasEventMap.ts @@ -0,0 +1,28 @@ +import type { ConnectingLink } from "@/interfaces" +import type { LGraphGroup } from "@/LGraphGroup" +import type { LGraphNode } from "@/LGraphNode" +import type { CanvasPointerEvent } from "@/types/events" + +export interface LGraphCanvasEventMap { + "litegraph:canvas": + | { subType: "before-change" | "after-change" } + | { + subType: "empty-release" + originalEvent?: CanvasPointerEvent + linkReleaseContext?: { links: ConnectingLink[] } + } + | { + subType: "group-double-click" + originalEvent?: CanvasPointerEvent + group: LGraphGroup + } + | { + subType: "empty-double-click" + originalEvent?: CanvasPointerEvent + } + | { + subType: "node-double-click" + originalEvent?: CanvasPointerEvent + node: LGraphNode + } +} diff --git a/src/interfaces.ts b/src/interfaces.ts index 2f49849586..145a994d4e 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -417,3 +417,23 @@ export interface DefaultConnectionColors { getConnectedColor(type: ISlotType): CanvasColour getDisconnectedColor(type: ISlotType): CanvasColour } +/** + * Shorthand for {@link Parameters} of optional callbacks. + * @example + * ```ts + * const { onClick } = CustomClass.prototype + * CustomClass.prototype.onClick = function (...args: CallbackParams) { + * const r = onClick?.apply(this, args) + * // ... + * return r + * } + * ``` + */ +export type CallbackParams any) | undefined> = + Parameters> + +/** + * Shorthand for {@link ReturnType} of optional callbacks. + * @see {@link CallbackParams} + */ +export type CallbackReturn any) | undefined> = ReturnType> diff --git a/src/litegraph.ts b/src/litegraph.ts index 536c319cdd..c3ec6e1da6 100644 --- a/src/litegraph.ts +++ b/src/litegraph.ts @@ -82,7 +82,6 @@ export interface LGraphNodeConstructor { title_color?: string title_text_color?: string keepAllLinksOnBypass: boolean - nodeData: any } // End backwards compat diff --git a/src/types/events.ts b/src/types/events.ts index a59e758039..64cf9eb209 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -4,8 +4,7 @@ import type { LGraphGroup } from "../LGraphGroup" import type { LGraphNode } from "../LGraphNode" -import type { ConnectingLink, LinkReleaseContextExtended } from "../litegraph" -import type { IWidget } from "./widgets" +import type { LinkReleaseContextExtended } from "../litegraph" /** For Canvas*Event - adds graph space co-ordinates (property names are shipped) */ export interface ICanvasPosition { @@ -57,12 +56,9 @@ export interface CanvasDragEvent extends export type CanvasEventDetail = | GenericEventDetail - | DragggingCanvasEventDetail - | ReadOnlyEventDetail | GroupDoubleClickEventDetail | NodeDoubleClickEventDetail | EmptyDoubleClickEventDetail - | ConnectingWidgetLinkEventDetail | EmptyReleaseEventDetail export interface GenericEventDetail { @@ -78,13 +74,6 @@ export interface EmptyReleaseEventDetail extends OriginalEvent { linkReleaseContext: LinkReleaseContextExtended } -export interface ConnectingWidgetLinkEventDetail { - subType: "connectingWidgetLink" - link: ConnectingLink - node: LGraphNode - widget: IWidget -} - export interface EmptyDoubleClickEventDetail extends OriginalEvent { subType: "empty-double-click" } @@ -98,13 +87,3 @@ export interface NodeDoubleClickEventDetail extends OriginalEvent { subType: "node-double-click" node: LGraphNode } - -export interface DragggingCanvasEventDetail { - subType: "dragging-canvas" - draggingCanvas: boolean -} - -export interface ReadOnlyEventDetail { - subType: "read-only" - readOnly: boolean -} diff --git a/src/types/serialisation.ts b/src/types/serialisation.ts index ee8363feb3..40115726b6 100644 --- a/src/types/serialisation.ts +++ b/src/types/serialisation.ts @@ -34,7 +34,7 @@ export interface BaseExportedGraph { /** Unique graph ID. Automatically generated if not provided. */ id: UUID revision: number - config: LGraphConfig + config?: LGraphConfig /** Details of the appearance and location of subgraphs shown in this graph. Similar to */ subgraphs?: ExportedSubgraphInstance[] /** Definitions of re-usable objects that are referenced elsewhere in this exported graph. */ @@ -161,7 +161,7 @@ export interface ISerialisedGroup { title: string bounding: number[] color?: string - font_size: number + font_size?: number flags?: IGraphGroupFlags } diff --git a/src/types/utility.ts b/src/types/utility.ts new file mode 100644 index 0000000000..6aaad44155 --- /dev/null +++ b/src/types/utility.ts @@ -0,0 +1,13 @@ +/** + * General-purpose, TypeScript utility types. + */ + +/** {@link Pick} only properties that evaluate to `never`. */ +export type PickNevers = { + [K in keyof T as T[K] extends never ? K : never]: T[K] +} + +/** {@link Omit} all properties that evaluate to `never`. */ +export type NeverNever = { + [K in keyof T as T[K] extends never ? never : K]: T[K] +}