Context menu thing
This commit is contained in:
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
background: var(--color-yellow-200);
|
||||
}
|
||||
.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 {
|
||||
|
||||
47
src/lib/components/menu/ContextMenu.svelte
Normal file
47
src/lib/components/menu/ContextMenu.svelte
Normal 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} />
|
||||
3
src/lib/components/menu/Icon.svelte
Normal file
3
src/lib/components/menu/Icon.svelte
Normal 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 |
45
src/lib/components/menu/Menu.svelte
Normal file
45
src/lib/components/menu/Menu.svelte
Normal 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>
|
||||
9
src/lib/components/menu/MenuDivider.svelte
Normal file
9
src/lib/components/menu/MenuDivider.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<hr />
|
||||
|
||||
<style>
|
||||
hr {
|
||||
border-top: 1px solid #0003;
|
||||
width: 100%;
|
||||
margin: 2px 0;
|
||||
}
|
||||
</style>
|
||||
50
src/lib/components/menu/MenuOption.svelte
Normal file
50
src/lib/components/menu/MenuOption.svelte
Normal 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>
|
||||
3
src/lib/components/menu/menu.ts
Normal file
3
src/lib/components/menu/menu.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const key = {};
|
||||
|
||||
export { key };
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user