Context menu thing

This commit is contained in:
space-nuko
2023-04-29 14:12:20 -07:00
parent 311ee192b2
commit 2f53758045
10 changed files with 234 additions and 17 deletions

View File

@@ -177,7 +177,6 @@
<select id="enable-ui-editing" name="enable-ui-editing" bind:value={$uiState.uiEditMode}>
<option value="disabled">Disabled</option>
<option value="widgets">Widgets</option>
<option value="containers">Containers</option>
</select>
</div>
<LightboxModal />

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { tick } from 'svelte'
import { get } from "svelte/store";
import { LGraphNode, LGraph } from "@litegraph-ts/core";
import type { IWidget } from "@litegraph-ts/core";
@@ -7,6 +8,12 @@
import WidgetContainer from "./WidgetContainer.svelte";
import nodeState from "$lib/stores/nodeState";
import layoutState, { type ContainerLayout, type DragItem } from "$lib/stores/layoutState";
import uiState from "$lib/stores/uiState";
import Menu from './menu/Menu.svelte';
import MenuOption from './menu/MenuOption.svelte';
import MenuDivider from './menu/MenuDivider.svelte';
import Icon from './menu/Icon.svelte'
export let app: ComfyApp;
let dragConfigured: boolean = false;
@@ -39,18 +46,60 @@
}
console.warn($layoutState)
}
let menuPos = { x: 0, y: 0 };
let showMenu = false;
$: $layoutState.isMenuOpen = showMenu;
async function onRightClick(e) {
if ($uiState.uiEditMode === "disabled")
return;
e.preventDefault();
if (showMenu) {
showMenu = false;
await new Promise(res => setTimeout(res, 100));
}
menuPos = { x: e.clientX, y: e.clientY };
showMenu = true;
}
function closeMenu() {
showMenu = false;
}
</script>
<div id="comfy-ui-panes" >
<div id="comfy-ui-panes" on:contextmenu={onRightClick}>
<WidgetContainer bind:dragItem={$layoutState.root} classes={["root-container"]} />
</div>
{#if showMenu}
<Menu {...menuPos} on:click={closeMenu} on:clickoutside={closeMenu}>
<MenuOption
on:click={console.log}
text="Do nothing" />
<MenuOption
on:click={console.log}
text="Do nothing, but twice" />
<MenuDivider />
<MenuOption
isDisabled={true}
on:click={console.log}
text="Whoops, disabled!" />
<MenuOption on:click={console.log}>
<Icon />
<span>Look! An icon!</span>
</MenuOption>
</Menu>
{/if}
<style lang="scss">
#comfy-ui-panes {
width: 100%;
height: 100%;
overflow: scroll;
overflow: auto;
}
</style>

View File

@@ -22,6 +22,7 @@
let widget: WidgetLayout | null = null;
let widgetState: WidgetUIState | null = null;
let children: IDragItem[] | null = null;
let showHandles: boolean = false;
const flipDurationMs = 100;
$: if (dragItem) {
@@ -38,6 +39,10 @@
}
}
$: showHandles = $uiState.uiEditMode === "widgets" // TODO
&& zIndex > 1
&& !$layoutState.isMenuOpen
function handleConsider(evt: any) {
children = layoutState.updateChildren(dragItem, evt.detail.items)
// console.log(dragItems);
@@ -49,6 +54,9 @@
};
const startDrag = (evt: MouseEvent) => {
if (evt.button === 2)
return;
const dragItemId: string = evt.target.dataset["dragItemId"];
const item = $layoutState.allItems[dragItemId].dragItem
if (evt.ctrlKey) {
@@ -79,12 +87,12 @@
<div class="container {container.attrs.direction} {container.attrs.classes} {classes.join(' ')}"
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(container.id)}
class:root-container={isRoot}
class:container-edit-outline={$uiState.uiEditMode === "containers" && zIndex > 1}>
class:container-edit-outline={$uiState.uiEditMode === "widgets" && zIndex > 1}>
<Block>
{#if container.attrs.showTitle}
<label for={String(id)} class={$uiState.uiEditMode === "containers" ? "edit-title-label" : ""}>
<label for={String(id)} class={$uiState.uiEditMode === "widgets" ? "edit-title-label" : ""}>
<BlockTitle>
{#if $uiState.uiEditMode === "containers"}
{#if $uiState.uiEditMode === "widgets"}
<input class="edit-title" bind:value={container.attrs.title} type="text" minlength="1" />
{:else}
{container.attrs.title}
@@ -94,7 +102,7 @@
{/if}
<div class="v-pane"
class:empty={children.length === 0}
class:edit={$uiState.uiEditMode === "containers" && zIndex > 1}
class:edit={$uiState.uiEditMode === "widgets" && zIndex > 1}
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId === container.attrs.associatedNode}
use:dndzone="{{
items: children,
@@ -119,7 +127,7 @@
</div>
{/each}
</div>
{#if $uiState.uiEditMode === "containers" && zIndex > 1}
{#if showHandles}
<div class="handle handle-container" style="z-index: {zIndex+100}" data-drag-item-id={container.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
{/if}
</Block>
@@ -131,7 +139,7 @@
>
<svelte:component this={getComponentForWidgetState(widgetState)} item={widgetState} />
</div>
{#if $uiState.uiEditMode ==="widgets" && zIndex > 1}
{#if showHandles}
<div class="handle handle-widget" style="z-index: {zIndex+100}" data-drag-item-id={widget.id} on:mousedown={startDrag} on:touchstart={startDrag} on:mouseup={stopDrag} on:touchend={stopDrag}/>
{/if}
{/if}
@@ -197,10 +205,12 @@
}
}
.container, .widget {
&.selected {
.widget.selected {
background: var(--color-yellow-200);
}
.container.selected > :global(.block) {
background: var(--color-yellow-300);
}
.is-executing {
@@ -286,7 +296,7 @@
border-width: 2px;
border-style: dashed !important;
margin: 0.2em;
padding: 1.2em;
padding: 0.2em;
}
.widget-edit-outline {

View File

@@ -0,0 +1,47 @@
<script>
import Menu from './Menu.svelte';
import MenuOption from './MenuOption.svelte';
import MenuDivider from './MenuDivider.svelte';
import { tick } from 'svelte'
import Icon from './Icon.svelte'
let pos = { x: 0, y: 0 };
let showMenu = false;
async function onRightClick(e) {
if (showMenu) {
showMenu = false;
await new Promise(res => setTimeout(res, 100));
}
pos = { x: e.clientX, y: e.clientY };
showMenu = true;
}
function closeMenu() {
showMenu = false;
}
</script>
{#if showMenu}
<Menu {...pos} on:click={closeMenu} on:clickoutside={closeMenu}>
<MenuOption
on:click={console.log}
text="Do nothing" />
<MenuOption
on:click={console.log}
text="Do nothing, but twice" />
<MenuDivider />
<MenuOption
isDisabled={true}
on:click={console.log}
text="Whoops, disabled!" />
<MenuOption on:click={console.log}>
<Icon />
<span>Look! An icon!</span>
</MenuOption>
</Menu>
{/if}
<svelte:body on:contextmenu|preventDefault={onRightClick} />

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M11.2584 13.039C10.3201 13.647 9.2013 14 8 14C4.68629 14 2 11.3137 2 8C2 6.66837 2.4338 5.43806 3.1678 4.44268L11.2584 13.039ZM12.7332 11.6878L4.60537 3.05194C5.57075 2.38838 6.74002 2 8 2C11.3137 2 14 4.68629 14 8C14 9.39043 13.527 10.6704 12.7332 11.6878ZM16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 548 B

View File

@@ -0,0 +1,45 @@
<script>
import { setContext, createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import { key } from './menu.ts';
export let x;
export let y;
// whenever x and y is changed, restrict box to be within bounds
$: (() => {
if (!menuEl) return;
const rect = menuEl.getBoundingClientRect();
x = Math.min(window.innerWidth - rect.width, x);
if (y > window.innerHeight - rect.height) y -= rect.height;
})(x, y);
const dispatch = createEventDispatcher();
setContext(key, {
dispatchClick: () => dispatch('click')
});
let menuEl;
function onPageClick(e) {
if (e.target === menuEl || menuEl.contains(e.target)) return;
dispatch('clickoutside');
}
</script>
<svelte:body on:click={onPageClick} />
<div class="menu" bind:this={menuEl} style="top: {y}px; left: {x}px;">
<slot />
</div>
<style>
.menu {
z-index: var(--layer-top);
position: absolute;
display: grid;
border: 1px solid #0003;
box-shadow: 2px 2px 5px 0px #0002;
background: white;
}
</style>

View File

@@ -0,0 +1,9 @@
<hr />
<style>
hr {
border-top: 1px solid #0003;
width: 100%;
margin: 2px 0;
}
</style>

View File

@@ -0,0 +1,50 @@
<script>
import { onMount, getContext } from 'svelte';
import { key } from './menu.ts';
export let isDisabled = false;
export let text = '';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
const { dispatchClick } = getContext(key);
const handleClick = e => {
if (isDisabled) return;
dispatch('click');
dispatchClick();
}
</script>
<div
class:disabled={isDisabled}
on:click={handleClick}
>
{#if text}
{text}
{:else}
<slot />
{/if}
</div>
<style>
div {
padding: 4px 15px;
cursor: default;
font-size: 14px;
display: flex;
align-items: center;
grid-gap: 5px;
}
div:hover {
background: #0002;
}
div.disabled {
color: #0006;
}
div.disabled:hover {
background: white;
}
</style>

View File

@@ -0,0 +1,3 @@
const key = {};
export { key };

View File

@@ -16,7 +16,8 @@ export type LayoutState = {
root: IDragItem | null,
allItems: Record<DragItemID, DragItemEntry>,
currentId: number,
currentSelection: IDragItem[]
currentSelection: DragItemID[],
isMenuOpen: boolean
}
export type Attributes = {
@@ -62,7 +63,8 @@ const store: Writable<LayoutState> = writable({
root: null,
allItems: [],
currentId: 0,
currentSelection: []
currentSelection: [],
isMenuOpen: false
})
function findDefaultContainerForInsertion(): ContainerLayout | null {