Files
ComfyUI_frontend/src/composables/graph/useMoreOptionsMenu.ts
Johnpaul Chiwetelu ac107b45ea Floating Selection Toolbox Improvements (#5218)
* WIP

* WIP: UI design for right click menu

* feat: add composable for node customization and information handling

* fix: correct v-show directive in MaskEditorButton and enhance MoreOptions functionality

* feat: add selection and subgraph operations composables for enhanced graph management

* fix: update computed properties to use 'void' for non-reactive calls and add MenuOptionItem component

* feat: add composables for More Options menu and submenu positioning logic

* feat: refactor MoreOptions component to use MenuOptionItem for menu rendering and streamline submenu handling

* feat: implement SubmenuPopover component for enhanced submenu functionality and selection handling

* feat: add 'More Options' label and enhance shape options in localization file

* refactor: simplify shape name handling by removing Pascal case conversion and using localized names

* refactor: enhance submenu handling by dynamically setting refs and improving key assignment

* feat: implement useNodeArrangement composable for node alignment and distribution functionality

* feat: enhance useMoreOptionsMenu with image node operations and alignment options

* feat: localize context menu options and enhance submenu handling

* refactor: improve type safety for title assignment in selection operations and enhance color option retrieval in node customization

* fix: adjust component order in SelectionToolbox for improved layout

* feat: update FrameNodes button visibility and tooltip, and add localization for frameNodes

* feat: enhance button visibility logic in SelectionToolbox based on selection types

* refactor: reorganize properties panel option in More Options menu for single nodes

* remove excessive logging and alerts

* fix component tests

* ad browser tests

* feat: enhance popover behavior in MoreOptions component to manage visibility state during selection overlay changes

* refactor: update visibility logic for buttons in SelectionToolbox and ExecuteButton components

* refactor: remove duplicate shape option and clean up shapeOptions array

* refactor: update help toggle logic in InfoButton and useMoreOptionsMenu to manage sidebar and help state

* refactor: streamline node info handling and integrate output node filtering in useNodeInfo and useMoreOptionsMenu

* Added useSelectionState composable consolidating all selection-derived state and the node help toggle

* Updated toolbox buttons (InfoButton, BookmarkButton, BypassButton, MaskEditorButton, ConvertToSubgraphButton, PinButton, DeleteButton, ColorPickerButton, ExecuteButton, FrameNodes, Load3DViewerButton) to remove duplicated selection logic and use useSelectionState

* Introduced HideReason ('manual' | 'drag') to differentiate drag-induced hides from manual/outside hides in MoreOptions

* refactor: enhance popover visibility handling during drag events using canvas state

* fix: update shape option name from 'default' to 'box' and add localization for 'box'

* refactor: streamline BypassButton logic and enhance MoreOptions menu with state bumping

* refactor: remove toast notifications from subgraph operations for cleaner logic

* refactor: ensure menu options re-compute when selection flags change

* feat: Enhance MoreOptions behavior with drag-and-drop support

* fix: Update mask icon class for consistent styling in MaskEditorButton

* refactor: Standardize icon sizes and classes across selection toolbox buttons

* refactor: Update layout and styling in SelectionToolbox and MoreOptions components

* refactor: Improve selection toolbox behavior with more options state management

* Refactor: Remove unused imports and conditionally add subgraph option in menu

* Enhance popover behavior: add show/hide event handlers and improve positioning logic

* Cleanup: Remove debug comments from popover functions for clarity

* Refactor: Clean up FrameNodes component and add MenuOptionBadge for better option display

* Cleanup: Remove debug comments from useSelectionToolboxPosition for clarity

* Add useFrameNodes composable for grouping selected nodes

* Refactor: Update shape options in useNodeCustomization and localize frame nodes label

* fix tests

* Cleanup: Remove packageManager entry from package.json

* Refactor: Replace ILucide icons with named imports from lucide-vue-next

* Refactor: Update shape selection and improve color picker behavior in selection toolbox

* Update test expectations [skip ci]

* feat: Enhance More Options Menu for group node management and update localization strings

* refactor: Comment out PublishButton

* refactor: Comment out test for bookmark button visibility in SelectionToolbox

* refactor: Update class names for dark theme compatibility in ExecuteButton and MenuOptionItem components

* refactor: Modularize menu options by creating dedicated composables for group, image, node, and selection operations

* refactor: Update selectors in tests to match design changes

* refactor: Update help button selector in Node Help tests

* refactor: Update getGroupColorOptions to accept groupContext and bump parameters

* Update test expectations [skip ci]

* refactor: Center KSampler node before interaction in More Options submenu tests

* refactor: Adjust KSampler node positioning and simplify button click in More Options submenu tests

* refactor: Rename comfyPageFixture import for clarity

* refactor: use gap-1 instead of the explicit gap-[4px]

* refactor: Replace app.canvas with canvasStore.getCanvas for  state management

* refactor: Simplify prop access by removing 'props.' prefix in MenuOptionItem component

* refactor: Remove explicit type annotation for item in buildSelectionSignature function

* refactor: Replace Lucide icons with string-based icon references in menu options

* refactor: Remove export from interface declarations for improved clarity

* refactor: Simplify class binding in BypassButton component for improved readability

* refactor: Update button class for consistent sizing in ExecuteButton component

* refactor: Update help button locator class for consistency in Node Help tests

* fix node help test

* refactor: Remove unused imports and simplify visibility conditions in selection toolbox components

* feat: Add 3D node selection logic and cleanup on unmount for selection toolbox

* refactor: Update help button locator to use consistent data-testid in Node Help tests

* fix: Correct help button locator syntax in Node Help tests

* refactor: Change resetMoreOptionsState to an internal function in useSelectionToolboxPosition

* test: Add Load3D node visibility logic for ColorPickerButton and remove redundant test case

* fix: Increase tooltip show delay for ColorPickerButton

* fix: Update selectedOutputNodes computation to filter by isLGraphNode

* fix: Remove unused nodeDef reference from InfoButton and submenu trigger from MenuOptionItem

* fix: Update showInfoButton logic to depend on nodeDef value

* refactor: Remove deprecated getBasicNodeOptions function for cleaner code

* refactor: Replace useNodeInfo with useSelectedNodeActions

* refactor: Integrate useNodeDefStore for improved node definition handling in SelectionToolbox and InfoButton tests

* refactor: Introduce useCanvasRefresh composable for consistent canvas refresh logic across node operations

* refactor: Remove irrelevant append-to attribute from Popover

* refactor: Use storeToRefs for selectedItems in useSelectionState and add tests for selection logic

* refactor: Update ExecuteButton to use hasOutputNodesSelected for visibility and remove unnecessary computed property

* refactor: move display of execution button tests to selectionToolbox

---------

Co-authored-by: github-actions <github-actions@github.com>
2025-09-13 22:52:30 -07:00

187 lines
5.1 KiB
TypeScript

import { computed, ref } from 'vue'
import { type LGraphGroup } from '@/lib/litegraph/src/litegraph'
import { isLGraphGroup } from '@/utils/litegraphUtil'
import { useGroupMenuOptions } from './useGroupMenuOptions'
import { useImageMenuOptions } from './useImageMenuOptions'
import { useNodeMenuOptions } from './useNodeMenuOptions'
import { useSelectionMenuOptions } from './useSelectionMenuOptions'
import { useSelectionState } from './useSelectionState'
export interface MenuOption {
label?: string
icon?: string
shortcut?: string
hasSubmenu?: boolean
type?: 'divider'
action?: () => void
submenu?: SubMenuOption[]
badge?: BadgeVariant
}
export interface SubMenuOption {
label: string
icon?: string
action: () => void
color?: string
}
export enum BadgeVariant {
NEW = 'new',
DEPRECATED = 'deprecated'
}
/**
* Composable for managing the More Options menu configuration
* Refactored to use smaller, focused composables for better maintainability
*/
export function useMoreOptionsMenu() {
const {
selectedItems,
selectedNodes,
nodeDef,
showNodeHelp,
hasSubgraphs: hasSubgraphsComputed,
hasImageNode,
hasOutputNodesSelected,
hasMultipleSelection,
computeSelectionFlags
} = useSelectionState()
const { getImageMenuOptions } = useImageMenuOptions()
const {
getNodeInfoOption,
getAdjustSizeOption,
getNodeVisualOptions,
getPinOption,
getBypassOption,
getRunBranchOption
} = useNodeMenuOptions()
const {
getFitGroupToNodesOption,
getGroupShapeOptions,
getGroupColorOptions,
getGroupModeOptions
} = useGroupMenuOptions()
const {
getBasicSelectionOptions,
getSubgraphOptions,
getMultipleNodesOptions,
getDeleteOption,
getAlignmentOptions
} = useSelectionMenuOptions()
const hasSubgraphs = hasSubgraphsComputed
const hasMultipleNodes = hasMultipleSelection
// Internal version to force menu rebuild after state mutations
const optionsVersion = ref(0)
const bump = () => {
optionsVersion.value++
}
const menuOptions = computed((): MenuOption[] => {
// Reference selection flags to ensure re-computation when they change
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
optionsVersion.value
const states = computeSelectionFlags()
// Detect single group selection context (and no nodes explicitly selected)
const selectedGroups = selectedItems.value.filter(
isLGraphGroup
) as LGraphGroup[]
const groupContext: LGraphGroup | null =
selectedGroups.length === 1 && selectedNodes.value.length === 0
? selectedGroups[0]
: null
const hasSubgraphsSelected = hasSubgraphs.value
const options: MenuOption[] = []
// Section 1: Basic selection operations (Rename, Copy, Duplicate)
options.push(...getBasicSelectionOptions())
options.push({ type: 'divider' })
// Section 2: Node Info & Size Adjustment
if (nodeDef.value) {
options.push(getNodeInfoOption(showNodeHelp))
}
if (groupContext) {
options.push(getFitGroupToNodesOption(groupContext))
} else {
options.push(getAdjustSizeOption())
}
// Section 3: Collapse/Shape/Color
if (groupContext) {
// Group context: Shape, Color, Divider
options.push(getGroupShapeOptions(groupContext, bump))
options.push(getGroupColorOptions(groupContext, bump))
options.push({ type: 'divider' })
} else {
// Node context: Expand/Minimize, Shape, Color, Divider
options.push(...getNodeVisualOptions(states, bump))
options.push({ type: 'divider' })
}
// Section 4: Image operations (if image node)
if (hasImageNode.value && selectedNodes.value.length > 0) {
options.push(...getImageMenuOptions(selectedNodes.value[0]))
}
// Section 5: Subgraph operations
options.push(...getSubgraphOptions(hasSubgraphsSelected))
// Section 6: Multiple nodes operations
if (hasMultipleNodes.value) {
options.push(...getMultipleNodesOptions())
}
// Section 7: Divider
options.push({ type: 'divider' })
// Section 8: Pin/Unpin (non-group only)
if (!groupContext) {
options.push(getPinOption(states, bump))
}
// Section 9: Alignment (if multiple nodes)
if (hasMultipleNodes.value) {
options.push(...getAlignmentOptions())
}
// Section 10: Mode operations
if (groupContext) {
// Group mode operations
options.push(...getGroupModeOptions(groupContext, bump))
} else {
// Bypass option for nodes
options.push(getBypassOption(states, bump))
}
// Section 11: Run Branch (if output nodes)
if (hasOutputNodesSelected.value) {
options.push(getRunBranchOption())
}
// Section 12: Final divider and Delete
options.push({ type: 'divider' })
options.push(getDeleteOption())
return options
})
// Computed property to get only menu items with submenus
const menuOptionsWithSubmenu = computed(() =>
menuOptions.value.filter((option) => option.hasSubmenu && option.submenu)
)
return {
menuOptions,
menuOptionsWithSubmenu,
bump,
hasSubgraphs
}
}