Group/ungroup feature

This commit is contained in:
space-nuko
2023-04-29 15:21:33 -07:00
parent 2f53758045
commit 95133df3d9
3 changed files with 174 additions and 32 deletions

View File

@@ -47,6 +47,34 @@
console.warn($layoutState)
}
function groupWidgets() {
const items = layoutState.getCurrentSelection()
$layoutState.currentSelection = []
layoutState.groupItems(items)
}
let canUngroup = false;
let isDeleteGroup = false;
$: canUngroup = $layoutState.currentSelection.length === 1
&& layoutState.getCurrentSelection()[0].type === "container"
$: if (canUngroup) {
const dragItem = layoutState.getCurrentSelection()[0];
const entry = $layoutState.allItems[dragItem.id];
isDeleteGroup = entry.children.length === 0
}
else {
isDeleteGroup = false
}
function ungroup() {
const item = layoutState.getCurrentSelection()[0]
if (item.type !== "container")
return;
$layoutState.currentSelection = []
layoutState.ungroup(item as ContainerLayout)
}
let menuPos = { x: 0, y: 0 };
let showMenu = false;
@@ -78,20 +106,13 @@
{#if showMenu}
<Menu {...menuPos} on:click={closeMenu} on:clickoutside={closeMenu}>
<MenuOption
on:click={console.log}
text="Do nothing" />
isDisabled={$layoutState.currentSelection.length === 0}
on:click={groupWidgets}
text="Group" />
<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>
isDisabled={!canUngroup}
on:click={ungroup}
text={isDeleteGroup ? "Delete Group" : "Ungroup"} />
</Menu>
{/if}

View File

@@ -26,7 +26,14 @@
const flipDurationMs = 100;
$: if (dragItem) {
if (dragItem.type === "container") {
if (!$layoutState.allItems[dragItem.id]) {
dragItem = null;
widget = null;
widgetState = null;
children = null;
container = null;
}
else if (dragItem.type === "container") {
container = dragItem as ContainerLayout;
children = $layoutState.allItems[dragItem.id].children;
widget = null;
@@ -54,11 +61,16 @@
};
const startDrag = (evt: MouseEvent) => {
if (evt.button === 2)
return;
const dragItemId: string = evt.target.dataset["dragItemId"];
if (evt.button !== 0) {
if ($layoutState.currentSelection.length <= 1 && !$layoutState.isMenuOpen)
$layoutState.currentSelection = [dragItemId]
return;
}
const item = $layoutState.allItems[dragItemId].dragItem
if (evt.ctrlKey) {
const index = $layoutState.currentSelection.indexOf(item.id)
if (index === -1)
@@ -103,7 +115,6 @@
<div class="v-pane"
class:empty={children.length === 0}
class:edit={$uiState.uiEditMode === "widgets" && zIndex > 1}
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId === container.attrs.associatedNode}
use:dndzone="{{
items: children,
flipDurationMs,
@@ -137,6 +148,10 @@
class:selected={$uiState.uiEditMode !== "disabled" && $layoutState.currentSelection.includes(widget.id)}
class:is-executing={$queueState.runningNodeId && $queueState.runningNodeId == widget.attrs.associatedNode}
>
{#if widget.attrs.associatedNode}
{@const node = $nodeState[widget.attrs.associatedNode].node}
<span class="node-type">({node.type})</span>
{/if}
<svelte:component this={getComponentForWidgetState(widgetState)} item={widgetState} />
</div>
{#if showHandles}
@@ -261,12 +276,12 @@
}
.edit-title-label {
z-index: 10000;
position: relative;
z-index: var(--layer-1);
}
.edit-title {
z-index: var(--layer-1);
z-index: 10000;
display: block;
position: relative;
outline: none !important;

View File

@@ -48,12 +48,15 @@ export interface WidgetLayout extends IDragItem {
type DragItemID = string;
type LayoutStateOps = {
addContainer: (parentId: DragItemID, attrs: Partial<Attributes>) => ContainerLayout,
addContainer: (parentId: DragItemID, attrs: Partial<Attributes>, index: number) => ContainerLayout,
addWidget: (parentId: DragItemID, node: LGraphNode, widget: IWidget<any, any>, attrs: Partial<Attributes>, index: number) => WidgetLayout,
findDefaultContainerForInsertion: () => ContainerLayout | null,
addWidget: (parentId: DragItemID, node: LGraphNode, widget: IWidget<any, any>, attrs: Partial<Attributes>) => WidgetLayout,
updateChildren: (parent: IDragItem, children: IDragItem[]) => IDragItem[],
nodeAdded: (node: LGraphNode) => void,
nodeRemoved: (node: LGraphNode) => void,
groupItems: (dragItems: IDragItem[]) => ContainerLayout,
ungroup: (container: ContainerLayout) => void,
getCurrentSelection: () => IDragItem[],
clear: () => void,
resetLayout: () => void,
}
@@ -87,7 +90,7 @@ function findDefaultContainerForInsertion(): ContainerLayout | null {
return null
}
function addContainer(parentId: DragItemID | null, attrs: Partial<Attributes> = {}): ContainerLayout {
function addContainer(parentId: DragItemID | null, attrs: Partial<Attributes> = {}, index: number = -1): ContainerLayout {
const state = get(store);
const dragItem: ContainerLayout = {
type: "container",
@@ -106,13 +109,16 @@ function addContainer(parentId: DragItemID | null, attrs: Partial<Attributes> =
state.allItems[dragItem.id] = entry;
if (parent) {
parent.children ||= []
parent.children.push(dragItem)
if (index)
parent.children.splice(index, 0, dragItem)
else
parent.children.push(dragItem)
}
store.set(state)
return dragItem;
}
function addWidget(parentId: DragItemID, node: LGraphNode, widget: IWidget<any, any>, attrs: Partial<Attributes> = {}): WidgetLayout {
function addWidget(parentId: DragItemID, node: LGraphNode, widget: IWidget<any, any>, attrs: Partial<Attributes> = {}, index: number = -1): WidgetLayout {
const state = get(store);
const dragItem: WidgetLayout = {
type: "widget",
@@ -132,21 +138,25 @@ function addWidget(parentId: DragItemID, node: LGraphNode, widget: IWidget<any,
const entry: DragItemEntry = { dragItem, children: [], parent: parent.dragItem };
state.allItems[dragItem.id] = entry;
parent.children ||= []
parent.children.push(dragItem)
if (index !== -1)
parent.children.splice(index, 0, dragItem)
else
parent.children.push(dragItem)
store.set(state)
return dragItem;
}
function updateChildren(parent: IDragItem, children: IDragItem[]): IDragItem[] {
function updateChildren(parent: IDragItem, newChildren?: IDragItem[]): IDragItem[] {
const state = get(store);
state.allItems[parent.id].children = children;
for (const child of children) {
if (newChildren)
state.allItems[parent.id].children = newChildren;
for (const child of state.allItems[parent.id].children) {
if (child.id === SHADOW_PLACEHOLDER_ITEM_ID)
continue;
state.allItems[child.id].parent = parent;
}
store.set(state)
return children
return state.allItems[parent.id].children
}
function nodeAdded(node: LGraphNode) {
@@ -207,6 +217,99 @@ function nodeRemoved(node: LGraphNode) {
store.set(state)
}
function moveItem(target: IDragItem, to: ContainerLayout, index: number = -1) {
const state = get(store)
const entry = state.allItems[target.id]
if (entry.parent && entry.parent.id === to.id)
return;
if (entry.parent) {
const parentEntry = state.allItems[entry.parent.id];
const index = parentEntry.children.indexOf(target)
if (index !== -1) {
parentEntry.children.splice(index, 1)
}
else {
console.error(parentEntry)
console.error(target)
throw "Child not found in parent!"
}
}
const toEntry = state.allItems[to.id];
if (index !== -1)
toEntry.children.splice(index, 0, target)
else
toEntry.children.push(target)
state.allItems[target.id].parent = toEntry.dragItem;
console.debug("[layoutState] Move child", target, toEntry, index)
store.set(state)
}
function getCurrentSelection(): IDragItem[] {
const state = get(store)
return state.currentSelection.map(id => state.allItems[id].dragItem)
}
function groupItems(dragItems: IDragItem[]): ContainerLayout {
if (dragItems.length === 0)
return;
const state = get(store)
const parent = state.allItems[dragItems[0].id].parent || findDefaultContainerForInsertion();
if (parent === null || parent.type !== "container")
return;
let index = undefined;
if (parent) {
const indexFound = state.allItems[parent.id].children.indexOf(dragItems[0])
if (indexFound !== -1)
index = indexFound
}
const container = addContainer(parent.id, { title: "Group" }, index)
for (const item of dragItems) {
moveItem(item, container)
}
store.set(state)
return container
}
function ungroup(container: ContainerLayout) {
const state = get(store)
const parent = state.allItems[container.id].parent;
if (!parent || parent.type !== "container") {
console.warn("No parent to ungroup into!", container)
return;
}
let index = undefined;
const parentChildren = state.allItems[parent.id].children;
const indexFound = parentChildren.indexOf(container)
if (indexFound !== -1)
index = indexFound
const containerEntry = state.allItems[container.id]
console.debug("[layoutState] About to ungroup", containerEntry, parent, parentChildren, index)
const children = [...containerEntry.children]
for (const item of children) {
moveItem(item, parent as ContainerLayout, index)
}
removeEntry(state, container.id)
console.debug("[layoutState] Ungrouped", containerEntry, parent, parentChildren, index)
store.set(state)
}
function clear() {
}
@@ -214,7 +317,7 @@ function resetLayout() {
// TODO
}
const uiStateStore: WritableLayoutStateStore =
const layoutStateStore: WritableLayoutStateStore =
{
...store,
addContainer,
@@ -223,7 +326,10 @@ const uiStateStore: WritableLayoutStateStore =
updateChildren,
nodeAdded,
nodeRemoved,
getCurrentSelection,
groupItems,
ungroup,
clear,
resetLayout
}
export default uiStateStore;
export default layoutStateStore;