diff --git a/src/ContextMenu.ts b/src/ContextMenu.ts index 56f9a790c..926e7ef7d 100644 --- a/src/ContextMenu.ts +++ b/src/ContextMenu.ts @@ -1,28 +1,21 @@ -import type { IContextMenuOptions, IContextMenuValue } from "./interfaces" +import type { ContextMenuDivElement, IContextMenuOptions, IContextMenuValue } from "./interfaces" import { LiteGraph } from "./litegraph" -interface ContextMenuDivElement extends HTMLDivElement { - value?: IContextMenuValue | string - onclick_callback?: never - closing_timer?: number -} - // TODO: Replace this pattern with something more modern. -// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -export interface ContextMenu { - constructor: new (...args: ConstructorParameters) => ContextMenu +export interface ContextMenu { + constructor: new (...args: ConstructorParameters>) => ContextMenu } /** * ContextMenu from LiteGUI */ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging -export class ContextMenu { - options: IContextMenuOptions - parentMenu?: ContextMenu - root: ContextMenuDivElement - current_submenu?: ContextMenu +export class ContextMenu { + options: IContextMenuOptions + parentMenu?: ContextMenu + root: ContextMenuDivElement + current_submenu?: ContextMenu lock?: boolean /** @@ -35,7 +28,7 @@ export class ContextMenu { * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position */ - constructor(values: (IContextMenuValue | string | null)[], options: IContextMenuOptions) { + constructor(values: (string | IContextMenuValue | null)[], options: IContextMenuOptions) { options ||= {} this.options = options @@ -68,7 +61,7 @@ export class ContextMenu { options.event = undefined } - const root: ContextMenuDivElement = document.createElement("div") + const root: ContextMenuDivElement = document.createElement("div") let classes = "litegraph litecontextmenu litemenubar-panel" if (options.className) classes += " " + options.className root.className = classes @@ -189,12 +182,12 @@ export class ContextMenu { addItem( name: string | null, - value: IContextMenuValue | string | null, - options: IContextMenuOptions, + value: string | IContextMenuValue | null, + options: IContextMenuOptions, ): HTMLElement { options ||= {} - const element: ContextMenuDivElement = document.createElement("div") + const element: ContextMenuDivElement = document.createElement("div") element.className = "litemenu-entry submenu" let disabled = false @@ -246,7 +239,7 @@ export class ContextMenu { element.setAttribute("aria-expanded", "true") } - function inner_over(this: ContextMenuDivElement, e: MouseEvent) { + function inner_over(this: ContextMenuDivElement, e: MouseEvent) { const value = this.value if (!value || !(value as IContextMenuValue).has_submenu) return @@ -257,7 +250,7 @@ export class ContextMenu { // menu option clicked const that = this - function inner_onclick(this: ContextMenuDivElement, e: MouseEvent) { + function inner_onclick(this: ContextMenuDivElement, e: MouseEvent) { const value = this.value let close_parent = true @@ -358,7 +351,7 @@ export class ContextMenu { } // returns the top most menu - getTopMenu(): ContextMenu { + getTopMenu(): ContextMenu { return this.options.parentMenu ? this.options.parentMenu.getTopMenu() : this diff --git a/src/LGraphCanvas.ts b/src/LGraphCanvas.ts index 6538b86cd..0edeee320 100644 --- a/src/LGraphCanvas.ts +++ b/src/LGraphCanvas.ts @@ -4,6 +4,7 @@ import type { CanvasColour, ColorOption, ConnectingLink, + ContextMenuDivElement, Dictionary, Direction, IBoundaryNodes, @@ -13,7 +14,7 @@ import type { INodeInputSlot, INodeOutputSlot, INodeSlot, - IOptionalSlotData, + INodeSlotContextItem, ISlotType, LinkSegment, NullableProperties, @@ -437,8 +438,8 @@ export class LGraphCanvas implements ConnectionColorContext { autoresize: boolean static active_canvas: LGraphCanvas static onMenuNodeOutputs?( - entries: (IOptionalSlotData | null)[], - ): (IOptionalSlotData | null)[] + entries: (IContextMenuValue | null)[], + ): (IContextMenuValue | null)[] frame = 0 last_draw_time = 0 render_time = 0 @@ -746,7 +747,7 @@ export class LGraphCanvas implements ConnectionColorContext { value: IContextMenuValue, options: IContextMenuOptions, event: MouseEvent, - prev_menu: ContextMenu, + prev_menu: ContextMenu, node: LGraphNode, ): void { new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], { @@ -769,7 +770,7 @@ export class LGraphCanvas implements ConnectionColorContext { value: IContextMenuValue, options: IContextMenuOptions, event: MouseEvent, - prev_menu: ContextMenu, + prev_menu: ContextMenu, ): void { new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], { event: event, @@ -790,7 +791,7 @@ export class LGraphCanvas implements ConnectionColorContext { value: IContextMenuValue, options: IContextMenuOptions, event: MouseEvent, - prev_menu: ContextMenu, + prev_menu: ContextMenu, ): void { new LiteGraph.ContextMenu(["Vertically", "Horizontally"], { event, @@ -809,8 +810,8 @@ export class LGraphCanvas implements ConnectionColorContext { node: LGraphNode, options: IContextMenuOptions, e: MouseEvent, - prev_menu: ContextMenu, - callback?: (node: LGraphNode) => void, + prev_menu: ContextMenu, + callback?: (node: LGraphNode | null) => void, ): boolean | undefined { const canvas = LGraphCanvas.active_canvas const ref_window = canvas.getCanvasWindow() @@ -820,16 +821,16 @@ export class LGraphCanvas implements ConnectionColorContext { inner_onMenuAdded("", prev_menu) return false - type AddNodeMenu = Omit & { + type AddNodeMenu = Omit, "callback"> & { callback: ( value: { value: string }, event: Event, mouseEvent: MouseEvent, - contextMenu: ContextMenu + contextMenu: ContextMenu ) => void } - function inner_onMenuAdded(base_category: string, prev_menu?: ContextMenu): void { + function inner_onMenuAdded(base_category: string, prev_menu?: ContextMenu): void { if (!graph) return const categories = LiteGraph @@ -913,7 +914,7 @@ export class LGraphCanvas implements ConnectionColorContext { /** Unused - immediately overwritten */ _options: boolean, e: MouseEvent, - prev_menu: ContextMenu, + prev_menu: ContextMenu, node: LGraphNode, ): boolean | undefined { if (!node) return @@ -927,7 +928,7 @@ export class LGraphCanvas implements ConnectionColorContext { ? node.onGetInputs() : undefined - let entries: (IOptionalSlotData | null)[] = [] + let entries: (IContextMenuValue | null)[] = [] if (options) { for (const entry of options) { if (!entry) { @@ -942,7 +943,7 @@ export class LGraphCanvas implements ConnectionColorContext { } entry[2].removable = true - const data: IOptionalSlotData = { content: label, value: entry } + const data: IContextMenuValue = { content: label, value: entry } if (entry[1] == LiteGraph.ACTION) { data.className = "event" } @@ -958,7 +959,7 @@ export class LGraphCanvas implements ConnectionColorContext { return } - new LiteGraph.ContextMenu( + new LiteGraph.ContextMenu( entries, { event: e, @@ -970,7 +971,7 @@ export class LGraphCanvas implements ConnectionColorContext { ref_window, ) - function inner_clicked(v, e, prev) { + function inner_clicked(v: IContextMenuValue, e: MouseEvent, prev: ContextMenu) { if (!node) return v.callback?.call(that, node, v, e, prev) @@ -1009,7 +1010,7 @@ export class LGraphCanvas implements ConnectionColorContext { ? node.onGetOutputs() : undefined - let entries: (IOptionalSlotData | null)[] = [] + let entries: (IContextMenuValue | null)[] = [] if (options) { for (const entry of options) { if (!entry) { @@ -1032,7 +1033,7 @@ export class LGraphCanvas implements ConnectionColorContext { label = entry[2].label } entry[2].removable = true - const data: IOptionalSlotData = { content: label, value: entry } + const data: IContextMenuValue = { content: label, value: entry } if (entry[1] == LiteGraph.EVENT) { data.className = "event" } @@ -1044,7 +1045,6 @@ export class LGraphCanvas implements ConnectionColorContext { if (LiteGraph.do_add_triggers_slots) { // canvas.allow_addOutSlot_onExecuted if (node.findOutputSlot("onExecuted") == -1) { - // @ts-expect-error Events entries.push({ content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, { nameLocked: true }], className: "event" }) } } @@ -1066,7 +1066,7 @@ export class LGraphCanvas implements ConnectionColorContext { ref_window, ) - function inner_clicked(v, e, prev) { + function inner_clicked(v: IContextMenuValue, e: any, prev: any) { if (!node) return // TODO: This is a static method, so the below "that" appears broken. @@ -1112,7 +1112,7 @@ export class LGraphCanvas implements ConnectionColorContext { value: unknown, options: unknown, e: MouseEvent, - prev_menu: ContextMenu, + prev_menu: ContextMenu, node: LGraphNode, ): boolean | undefined { if (!node || !node.properties) return @@ -1120,7 +1120,7 @@ export class LGraphCanvas implements ConnectionColorContext { const canvas = LGraphCanvas.active_canvas const ref_window = canvas.getCanvasWindow() - const entries = [] + const entries: IContextMenuValue[] = [] for (const i in node.properties) { value = node.properties[i] !== undefined ? node.properties[i] : " " if (typeof value == "object") @@ -1145,7 +1145,7 @@ export class LGraphCanvas implements ConnectionColorContext { return } - new LiteGraph.ContextMenu( + new LiteGraph.ContextMenu( entries, { event: e, @@ -1158,7 +1158,7 @@ export class LGraphCanvas implements ConnectionColorContext { ref_window, ) - function inner_clicked(v: { value: any }) { + function inner_clicked(this: ContextMenuDivElement, v: { value: any }) { if (!node) return const rect = this.getBoundingClientRect() @@ -1203,10 +1203,10 @@ export class LGraphCanvas implements ConnectionColorContext { // TODO refactor :: this is used fot title but not for properties! static onShowPropertyEditor( - item: { property: string, type: string }, + item: { property: keyof LGraphNode, type: string }, options: IContextMenuOptions, e: MouseEvent, - menu: ContextMenu, + menu: ContextMenu, node: LGraphNode, ): void { const property = item.property || "title" @@ -1299,6 +1299,7 @@ export class LGraphCanvas implements ConnectionColorContext { } else if (item.type == "Boolean") { value = Boolean(value) } + // @ts-expect-error Requires refactor. node[property] = value dialog.remove() canvas.setDirty(true, true) @@ -1418,15 +1419,15 @@ export class LGraphCanvas implements ConnectionColorContext { /** @param value Parameter is never used */ static onMenuNodeColors( - value: IContextMenuValue, + value: IContextMenuValue, options: IContextMenuOptions, e: MouseEvent, - menu: ContextMenu, + menu: ContextMenu, node: LGraphNode, ): boolean { if (!node) throw "no node for color" - const values: IContextMenuValue[] = [] + const values: IContextMenuValue[] = [] values.push({ value: null, content: "No color", @@ -1446,14 +1447,14 @@ export class LGraphCanvas implements ConnectionColorContext { } values.push(value) } - new LiteGraph.ContextMenu(values, { + new LiteGraph.ContextMenu(values, { event: e, callback: inner_clicked, parentMenu: menu, node: node, }) - function inner_clicked(v: { value: string | null }) { + function inner_clicked(v: IContextMenuValue) { if (!node) return const fApplyColor = function (item: IColorable) { @@ -5650,7 +5651,7 @@ export class LGraphCanvas implements ConnectionColorContext { const title = "data" in segment && segment.data != null ? segment.data.constructor.name : null - const menu = new LiteGraph.ContextMenu(options, { + const menu = new LiteGraph.ContextMenu(options, { event: e, title, callback: inner_clicked.bind(this), @@ -5905,7 +5906,7 @@ export class LGraphCanvas implements ConnectionColorContext { } // build menu - const menu = new LiteGraph.ContextMenu(options, { + const menu = new LiteGraph.ContextMenu(options, { event: opts.e, title: (slotX && slotX.name != "" @@ -7222,7 +7223,7 @@ export class LGraphCanvas implements ConnectionColorContext { // called by processContextMenu to extract the menu list getNodeMenuOptions(node: LGraphNode): IContextMenuValue[] { - let options: IContextMenuValue[] = null + let options: IContextMenuValue[] = null if (node.getMenuOptions) { options = node.getMenuOptions(this) @@ -7358,7 +7359,7 @@ export class LGraphCanvas implements ConnectionColorContext { const ref_window = canvas.getCanvasWindow() // TODO: Remove type kludge - let menu_info: (IContextMenuValue | string)[] + let menu_info: (IContextMenuValue | string | null)[] const options: IContextMenuOptions = { event: event, callback: inner_option_clicked, diff --git a/src/LGraphGroup.ts b/src/LGraphGroup.ts index 86593c548..2be988b32 100644 --- a/src/LGraphGroup.ts +++ b/src/LGraphGroup.ts @@ -300,7 +300,7 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable { this.resizeTo([...this.children, ...this._nodes, ...nodes], padding) } - getMenuOptions(): (IContextMenuValue | null)[] { + getMenuOptions(): (IContextMenuValue | null)[] { return [ { content: this.pinned ? "Unpin" : "Pin", diff --git a/src/LGraphNode.ts b/src/LGraphNode.ts index 752ccad04..88669fc8d 100644 --- a/src/LGraphNode.ts +++ b/src/LGraphNode.ts @@ -11,7 +11,7 @@ import type { INodeInputSlot, INodeOutputSlot, INodeSlot, - IOptionalSlotData, + INodeSlotContextItem, IPinnable, ISlotType, Point, @@ -516,14 +516,14 @@ export class LGraphNode implements Positionable, IPinnable, IColorable { onNodeInputAdd?(this: LGraphNode, value: unknown): void onMenuNodeInputs?( this: LGraphNode, - entries: IOptionalSlotData[], - ): IOptionalSlotData[] + entries: (IContextMenuValue | null)[], + ): (IContextMenuValue | null)[] onMenuNodeOutputs?( this: LGraphNode, - entries: IOptionalSlotData[], - ): IOptionalSlotData[] - onGetInputs?(this: LGraphNode): INodeInputSlot[] - onGetOutputs?(this: LGraphNode): INodeOutputSlot[] + entries: (IContextMenuValue | null)[], + ): (IContextMenuValue | null)[] + onGetInputs?(this: LGraphNode): INodeSlotContextItem[] + onGetOutputs?(this: LGraphNode): INodeSlotContextItem[] onMouseUp?(this: LGraphNode, e: CanvasMouseEvent, pos: Point): void onMouseEnter?(this: LGraphNode, e: CanvasMouseEvent): void /** Blocks drag if return value is truthy. @param pos Offset from {@link LGraphNode.pos}. */ diff --git a/src/interfaces.ts b/src/interfaces.ts index 96955061f..98b32ce62 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -289,22 +289,15 @@ export interface ConnectingLink extends IInputOrOutput { afterRerouteId?: RerouteId } -interface IContextMenuBase { +interface IContextMenuBase { title?: string className?: string - callback?( - value?: unknown, - options?: unknown, - event?: MouseEvent, - previous_menu?: ContextMenu, - extra?: TExtra, - ): void | boolean } /** ContextMenu */ -export interface IContextMenuOptions extends IContextMenuBase { +export interface IContextMenuOptions extends IContextMenuBase { ignore_item_callbacks?: boolean - parentMenu?: ContextMenu + parentMenu?: ContextMenu event?: MouseEvent extra?: unknown /** @deprecated Context menu scrolling is now controlled by the browser */ @@ -315,19 +308,42 @@ export interface IContextMenuOptions extends IContextMenuBase { scale?: number node?: LGraphNode autoopen?: boolean + callback?( + value?: string | IContextMenuValue, + options?: unknown, + event?: MouseEvent, + previous_menu?: ContextMenu, + extra?: unknown, + ): void | boolean } -export interface IContextMenuValue extends IContextMenuBase { - value?: string +export interface IContextMenuValue extends IContextMenuBase { + value?: TValue content: string | undefined has_submenu?: boolean disabled?: boolean - submenu?: IContextMenuSubmenu + submenu?: IContextMenuSubmenu property?: string type?: string slot?: IFoundSlot + callback?( + this: ContextMenuDivElement, + value?: TCallbackValue, + options?: unknown, + event?: MouseEvent, + previous_menu?: ContextMenu, + extra?: TExtra, + ): void | boolean } -export interface IContextMenuSubmenu extends IContextMenuOptions { - options: ConstructorParameters[0] +export interface IContextMenuSubmenu extends IContextMenuOptions { + options: ConstructorParameters>[0] } + +export interface ContextMenuDivElement extends HTMLDivElement { + value?: string | IContextMenuValue + onclick_callback?: never + closing_timer?: number +} + +export type INodeSlotContextItem = [string, ISlotType, Partial] diff --git a/src/litegraph.ts b/src/litegraph.ts index 320cbab05..7dfd48bf1 100644 --- a/src/litegraph.ts +++ b/src/litegraph.ts @@ -46,7 +46,7 @@ export type ContextMenuEventListener = ( value: IContextMenuItem, options: IContextMenuOptions, event: MouseEvent, - parentMenu: ContextMenu | undefined, + parentMenu: ContextMenu | undefined, node: LGraphNode, ) => boolean | void