fix(svelte): repair selection, add multi selection closes #3557
This commit is contained in:
@@ -23,28 +23,6 @@
|
|||||||
|
|
||||||
const bgColor = writable('#1A192B');
|
const bgColor = writable('#1A192B');
|
||||||
|
|
||||||
const onChange: ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
||||||
nodes.update((nds) =>
|
|
||||||
nds.map((node) => {
|
|
||||||
if (node.type !== 'colorNode') {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
const color = (event.target as HTMLInputElement)?.value;
|
|
||||||
|
|
||||||
bgColor.set(color);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...node,
|
|
||||||
data: {
|
|
||||||
...node.data,
|
|
||||||
color
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodes = writable<Node[]>([
|
const nodes = writable<Node[]>([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
@@ -56,17 +34,9 @@
|
|||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'colorNode',
|
type: 'colorNode',
|
||||||
data: { onChange: onChange, color: $bgColor },
|
data: { colorStore: bgColor },
|
||||||
style: 'border: 1px solid #777; padding: 10px',
|
|
||||||
position: { x: 250, y: 50 }
|
position: { x: 250, y: 50 }
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: '2a',
|
|
||||||
type: 'colorNode',
|
|
||||||
data: { onChange: onChange, color: $bgColor },
|
|
||||||
style: 'border: 1px solid #777; padding: 10px',
|
|
||||||
position: { x: 250, y: 520 }
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
@@ -115,7 +85,7 @@
|
|||||||
{nodes}
|
{nodes}
|
||||||
{edges}
|
{edges}
|
||||||
{nodeTypes}
|
{nodeTypes}
|
||||||
style="--bgcolor: {$bgColor}"
|
style="--background-color: {$bgColor}"
|
||||||
fitView
|
fitView
|
||||||
on:connect={onConnect}
|
on:connect={onConnect}
|
||||||
>
|
>
|
||||||
@@ -123,9 +93,3 @@
|
|||||||
<Background variant={BackgroundVariant.Dots} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
</SvelteFlow>
|
</SvelteFlow>
|
||||||
|
|
||||||
<style>
|
|
||||||
:global(.svelte-flow) {
|
|
||||||
background: var(--bgcolor);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { Writable } from 'svelte/store';
|
||||||
import { Handle, Position, type NodeProps } from '@xyflow/svelte';
|
import { Handle, Position, type NodeProps } from '@xyflow/svelte';
|
||||||
|
|
||||||
type $$Props = NodeProps;
|
type $$Props = NodeProps;
|
||||||
|
|
||||||
export let data: { color: string; onChange: () => void } = { color: '#111', onChange: () => {} };
|
export let data: { colorStore: Writable<string> };
|
||||||
|
|
||||||
|
const { colorStore } = data;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Handle type="target" position={Position.Left} on:connect />
|
<div class="custom">
|
||||||
<div>
|
<Handle type="target" position={Position.Left} on:connect />
|
||||||
Custom Color Picker Node: <strong>{data.color}</strong>
|
<div>
|
||||||
|
Custom Color Picker Node: <strong>{$colorStore}</strong>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
class="nodrag"
|
||||||
|
type="color"
|
||||||
|
on:input={(evt) => colorStore.set(evt.currentTarget.value)}
|
||||||
|
value={$colorStore}
|
||||||
|
/>
|
||||||
|
<Handle type="source" position={Position.Right} id="a" style="top: 20px;" on:connect />
|
||||||
|
<Handle
|
||||||
|
type="source"
|
||||||
|
position={Position.Right}
|
||||||
|
id="b"
|
||||||
|
style="top: auto; bottom: 10px;"
|
||||||
|
on:connect
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<input class="nodrag" type="color" on:input={data.onChange} value={data.color} />
|
|
||||||
<Handle type="source" position={Position.Right} id="a" style="top: 10px;" on:connect />
|
<style>
|
||||||
<Handle
|
.custom {
|
||||||
type="source"
|
background-color: white;
|
||||||
position={Position.Right}
|
padding: 10px;
|
||||||
id="b"
|
border: 1px solid #777;
|
||||||
style="top: auto; bottom: 10px;"
|
border-radius: 20px;
|
||||||
on:connect
|
}
|
||||||
/>
|
</style>
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
|
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
|
||||||
|
|
||||||
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
|
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
|
||||||
// this handler gets called within the drag start event when selectNodesOnDrag=true
|
// this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
||||||
|
// here we only need to call it when selectNodesOnDrag=false
|
||||||
handleNodeClick({
|
handleNodeClick({
|
||||||
id,
|
id,
|
||||||
store,
|
store,
|
||||||
|
|||||||
@@ -23,14 +23,12 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
|
|||||||
xyDrag.current = XYDrag({
|
xyDrag.current = XYDrag({
|
||||||
domNode: nodeRef.current,
|
domNode: nodeRef.current,
|
||||||
getStoreItems: () => store.getState(),
|
getStoreItems: () => store.getState(),
|
||||||
onNodeClick: () => {
|
onNodeMouseDown: (id: string) => {
|
||||||
if (nodeId) {
|
handleNodeClick({
|
||||||
handleNodeClick({
|
id,
|
||||||
id: nodeId,
|
store,
|
||||||
store,
|
nodeRef: nodeRef as RefObject<HTMLDivElement>,
|
||||||
nodeRef: nodeRef as RefObject<HTMLDivElement>,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onDragStart: () => {
|
onDragStart: () => {
|
||||||
setDragging(true);
|
setDragging(true);
|
||||||
|
|||||||
@@ -13,15 +13,17 @@ export type UseDragParams = {
|
|||||||
onDrag?: OnDrag;
|
onDrag?: OnDrag;
|
||||||
onDragStart?: OnDrag;
|
onDragStart?: OnDrag;
|
||||||
onDragStop?: OnDrag;
|
onDragStop?: OnDrag;
|
||||||
|
onNodeMouseDown?: (id: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function drag(domNode: Element, params: UseDragParams) {
|
export default function drag(domNode: Element, params: UseDragParams) {
|
||||||
const { store, onDrag, onDragStart, onDragStop } = params;
|
const { store, onDrag, onDragStart, onDragStop, onNodeMouseDown } = params;
|
||||||
const dragInstance = XYDrag({
|
const dragInstance = XYDrag({
|
||||||
domNode,
|
domNode,
|
||||||
onDrag,
|
onDrag,
|
||||||
onDragStart,
|
onDragStart,
|
||||||
onDragStop,
|
onDragStop,
|
||||||
|
onNodeMouseDown,
|
||||||
getStoreItems: () => {
|
getStoreItems: () => {
|
||||||
const snapGrid = get(store.snapGrid);
|
const snapGrid = get(store.snapGrid);
|
||||||
const vp = get(store.viewport);
|
const vp = get(store.viewport);
|
||||||
@@ -33,7 +35,7 @@ export default function drag(domNode: Element, params: UseDragParams) {
|
|||||||
snapGrid: snapGrid ? snapGrid : [0, 0],
|
snapGrid: snapGrid ? snapGrid : [0, 0],
|
||||||
snapToGrid: !!snapGrid,
|
snapToGrid: !!snapGrid,
|
||||||
nodeOrigin: [0, 0],
|
nodeOrigin: [0, 0],
|
||||||
multiSelectionActive: false,
|
multiSelectionActive: get(store.multiselectionKeyPressed),
|
||||||
domNode: get(store.domNode),
|
domNode: get(store.domNode),
|
||||||
transform: [vp.x, vp.y, vp.zoom],
|
transform: [vp.x, vp.y, vp.zoom],
|
||||||
autoPanOnNodeDrag: get(store.autoPanOnNodeDrag),
|
autoPanOnNodeDrag: get(store.autoPanOnNodeDrag),
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import { getMarkerId } from '@xyflow/system';
|
import { errorMessages, getMarkerId } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||||
import type { EdgeLayouted, Edge } from '$lib/types';
|
import type { EdgeLayouted, Edge } from '$lib/types';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
type $$Props = EdgeLayouted;
|
type $$Props = EdgeLayouted;
|
||||||
|
|
||||||
@@ -41,7 +42,16 @@
|
|||||||
let className: string = '';
|
let className: string = '';
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
const { edges, edgeTypes, flowId, addSelectedEdges } = useStore();
|
const {
|
||||||
|
edges,
|
||||||
|
edgeTypes,
|
||||||
|
flowId,
|
||||||
|
selectionRect,
|
||||||
|
selectionRectMode,
|
||||||
|
multiselectionKeyPressed,
|
||||||
|
addSelectedEdges,
|
||||||
|
unselectNodesAndEdges
|
||||||
|
} = useStore();
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
edgeclick: { edge: Edge; event: MouseEvent | TouchEvent };
|
edgeclick: { edge: Edge; event: MouseEvent | TouchEvent };
|
||||||
edgecontextmenu: { edge: Edge; event: MouseEvent };
|
edgecontextmenu: { edge: Edge; event: MouseEvent };
|
||||||
@@ -52,15 +62,25 @@
|
|||||||
$: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined;
|
$: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined;
|
||||||
|
|
||||||
function onClick(event: MouseEvent | TouchEvent) {
|
function onClick(event: MouseEvent | TouchEvent) {
|
||||||
if (selectable) {
|
|
||||||
addSelectedEdges([id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const edge = $edges.find((e) => e.id === id);
|
const edge = $edges.find((e) => e.id === id);
|
||||||
|
|
||||||
if (edge) {
|
if (!edge) {
|
||||||
dispatch('edgeclick', { event, edge });
|
console.warn('012', errorMessages['error012'](id));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectable) {
|
||||||
|
selectionRect.set(null);
|
||||||
|
selectionRectMode.set(null);
|
||||||
|
|
||||||
|
if (!edge.selected) {
|
||||||
|
addSelectedEdges([id]);
|
||||||
|
} else if (edge.selected && get(multiselectionKeyPressed)) {
|
||||||
|
unselectNodesAndEdges({ nodes: [], edges: [edge] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch('edgeclick', { event, edge });
|
||||||
}
|
}
|
||||||
|
|
||||||
function onContextMenu(event: MouseEvent) {
|
function onContextMenu(event: MouseEvent) {
|
||||||
|
|||||||
@@ -4,14 +4,21 @@
|
|||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import type { KeyHandlerProps } from './types';
|
import type { KeyHandlerProps } from './types';
|
||||||
import type { KeyDefinition, KeyDefinitionObject } from '$lib/types';
|
import type { KeyDefinition, KeyDefinitionObject } from '$lib/types';
|
||||||
|
import { isMacOs } from '@xyflow/system';
|
||||||
|
|
||||||
type $$Props = KeyHandlerProps;
|
type $$Props = KeyHandlerProps;
|
||||||
|
|
||||||
export let selectionKey: $$Props['selectionKey'] = 'Shift';
|
export let selectionKey: $$Props['selectionKey'] = 'Shift';
|
||||||
|
export let multiSelectionKey: $$Props['multiSelectionKey'] = isMacOs() ? 'Meta' : 'Control';
|
||||||
export let deleteKey: $$Props['deleteKey'] = 'Backspace';
|
export let deleteKey: $$Props['deleteKey'] = 'Backspace';
|
||||||
export let panActivationKey: $$Props['panActivationKey'] = ' ';
|
export let panActivationKey: $$Props['panActivationKey'] = ' ';
|
||||||
|
|
||||||
const { selectionKeyPressed, deleteKeyPressed, panActivationKeyPressed } = useStore();
|
const {
|
||||||
|
selectionKeyPressed,
|
||||||
|
multiselectionKeyPressed,
|
||||||
|
deleteKeyPressed,
|
||||||
|
panActivationKeyPressed
|
||||||
|
} = useStore();
|
||||||
|
|
||||||
function isKeyObject(key?: KeyDefinition): key is KeyDefinitionObject {
|
function isKeyObject(key?: KeyDefinition): key is KeyDefinitionObject {
|
||||||
return typeof key === 'object';
|
return typeof key === 'object';
|
||||||
@@ -20,6 +27,10 @@
|
|||||||
$: selectionKeyString = typeof selectionKey === 'string' ? selectionKey : selectionKey!.key;
|
$: selectionKeyString = typeof selectionKey === 'string' ? selectionKey : selectionKey!.key;
|
||||||
$: selectionKeyModifier = isKeyObject(selectionKey) ? selectionKey?.modifier : [];
|
$: selectionKeyModifier = isKeyObject(selectionKey) ? selectionKey?.modifier : [];
|
||||||
|
|
||||||
|
$: multiSelectionKeyString =
|
||||||
|
typeof multiSelectionKey === 'string' ? multiSelectionKey : multiSelectionKey!.key;
|
||||||
|
$: multiSelectionKeyModifier = isKeyObject(multiSelectionKey) ? multiSelectionKey?.modifier : [];
|
||||||
|
|
||||||
$: deleteKeyString = typeof deleteKey === 'string' ? deleteKey : deleteKey!.key;
|
$: deleteKeyString = typeof deleteKey === 'string' ? deleteKey : deleteKey!.key;
|
||||||
$: deleteKeyModifier = isKeyObject(deleteKey) ? deleteKey?.modifier : [];
|
$: deleteKeyModifier = isKeyObject(deleteKey) ? deleteKey?.modifier : [];
|
||||||
|
|
||||||
@@ -49,6 +60,26 @@
|
|||||||
],
|
],
|
||||||
type: 'keyup'
|
type: 'keyup'
|
||||||
}}
|
}}
|
||||||
|
use:shortcut={{
|
||||||
|
trigger: [
|
||||||
|
{
|
||||||
|
key: multiSelectionKeyString,
|
||||||
|
modifier: multiSelectionKeyModifier,
|
||||||
|
callback: () => multiselectionKeyPressed.set(true)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
type: 'keydown'
|
||||||
|
}}
|
||||||
|
use:shortcut={{
|
||||||
|
trigger: [
|
||||||
|
{
|
||||||
|
key: multiSelectionKeyString,
|
||||||
|
modifier: multiSelectionKeyModifier,
|
||||||
|
callback: () => multiselectionKeyPressed.set(false)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
type: 'keyup'
|
||||||
|
}}
|
||||||
use:shortcut={{
|
use:shortcut={{
|
||||||
trigger: [
|
trigger: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { KeyDefinition } from '$lib/types';
|
|||||||
|
|
||||||
export type KeyHandlerProps = {
|
export type KeyHandlerProps = {
|
||||||
selectionKey?: KeyDefinition;
|
selectionKey?: KeyDefinition;
|
||||||
|
multiSelectionKey?: KeyDefinition;
|
||||||
deleteKey?: KeyDefinition;
|
deleteKey?: KeyDefinition;
|
||||||
panActivationKey?: KeyDefinition;
|
panActivationKey?: KeyDefinition;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
SvelteComponent,
|
SvelteComponent,
|
||||||
type ComponentType
|
type ComponentType
|
||||||
} from 'svelte';
|
} from 'svelte';
|
||||||
import cc from 'classcat';
|
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
|
import cc from 'classcat';
|
||||||
import { errorMessages, Position, type NodeProps } from '@xyflow/system';
|
import { errorMessages, Position, type NodeProps } from '@xyflow/system';
|
||||||
|
|
||||||
import drag from '$lib/actions/drag';
|
import drag from '$lib/actions/drag';
|
||||||
@@ -44,7 +44,13 @@
|
|||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const { nodeTypes, nodeDragThreshold, addSelectedNodes, updateNodeDimensions } = store;
|
const {
|
||||||
|
nodeTypes,
|
||||||
|
nodeDragThreshold,
|
||||||
|
selectNodesOnDrag,
|
||||||
|
handleNodeSelection,
|
||||||
|
updateNodeDimensions
|
||||||
|
} = store;
|
||||||
const nodeType = type || 'default';
|
const nodeType = type || 'default';
|
||||||
|
|
||||||
let nodeRef: HTMLDivElement;
|
let nodeRef: HTMLDivElement;
|
||||||
@@ -56,7 +62,6 @@
|
|||||||
|
|
||||||
const nodeComponent: ComponentType<SvelteComponent<NodeProps>> =
|
const nodeComponent: ComponentType<SvelteComponent<NodeProps>> =
|
||||||
$nodeTypes[nodeType] || DefaultNode;
|
$nodeTypes[nodeType] || DefaultNode;
|
||||||
const selectNodesOnDrag = false;
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
nodeclick: { node: Node; event: MouseEvent | TouchEvent };
|
nodeclick: { node: Node; event: MouseEvent | TouchEvent };
|
||||||
nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent };
|
nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent };
|
||||||
@@ -113,12 +118,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function onSelectNodeHandler(event: MouseEvent | TouchEvent) {
|
function onSelectNodeHandler(event: MouseEvent | TouchEvent) {
|
||||||
if (selectable && (!selectNodesOnDrag || !draggable || get(nodeDragThreshold) > 0)) {
|
if (selectable && (!get(selectNodesOnDrag) || !draggable || get(nodeDragThreshold) > 0)) {
|
||||||
// this handler gets called within the drag start event when selectNodesOnDrag=true
|
// this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
||||||
addSelectedNodes([id]);
|
// here we only need to call it when selectNodesOnDrag=false
|
||||||
|
handleNodeSelection(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo: support multiselection
|
|
||||||
dispatch('nodeclick', { node, event });
|
dispatch('nodeclick', { node, event });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +140,7 @@
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
handleSelector: dragHandle,
|
handleSelector: dragHandle,
|
||||||
noDragClass: 'nodrag',
|
noDragClass: 'nodrag',
|
||||||
|
onNodeMouseDown: handleNodeSelection,
|
||||||
onDrag: (event, _, node, nodes) => {
|
onDrag: (event, _, node, nodes) => {
|
||||||
dispatch('nodedrag', { event, node, nodes });
|
dispatch('nodedrag', { event, node, nodes });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import {
|
|||||||
type CoordinateExtent,
|
type CoordinateExtent,
|
||||||
type UpdateConnection,
|
type UpdateConnection,
|
||||||
type NodeBase,
|
type NodeBase,
|
||||||
type NodeDragItem
|
type NodeDragItem,
|
||||||
|
errorMessages
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { addEdge as addEdgeUtil } from '$lib/utils';
|
import { addEdge as addEdgeUtil } from '$lib/utils';
|
||||||
@@ -173,25 +174,29 @@ export function createStore({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetSelectedItem<T extends Node | Edge>(item: T) {
|
function resetSelectedItem<T extends Node | Edge>(ids: string[]) {
|
||||||
if (item.selected) {
|
return (item: T) => {
|
||||||
return {
|
if (item.selected && ids.includes(item.id)) {
|
||||||
...item,
|
return {
|
||||||
selected: false
|
...item,
|
||||||
};
|
selected: false
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function unselectNodesAndEdges() {
|
function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) {
|
||||||
if (get(store.nodes).some((node) => node.selected)) {
|
const nodeIdsToUnselect = (params?.nodes ? params.nodes : get(store.nodes)).map(
|
||||||
store.nodes.update((ns) => ns.map(resetSelectedItem));
|
(item) => item.id
|
||||||
}
|
);
|
||||||
|
const edgeIdsToUnselect = (params?.edges ? params.edges : get(store.edges)).map(
|
||||||
|
(item) => item.id
|
||||||
|
);
|
||||||
|
|
||||||
if (get(store.edges).some((edge) => edge.selected)) {
|
store.nodes.update((ns) => ns.map(resetSelectedItem(nodeIdsToUnselect)));
|
||||||
store.edges.update((es) => es.map(resetSelectedItem));
|
store.edges.update((es) => es.map(resetSelectedItem(edgeIdsToUnselect)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
store.deleteKeyPressed.subscribe((deleteKeyPressed) => {
|
store.deleteKeyPressed.subscribe((deleteKeyPressed) => {
|
||||||
@@ -220,36 +225,74 @@ export function createStore({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function addSelectedNodes(ids: string[]) {
|
function addSelectedNodes(ids: string[]) {
|
||||||
store.selectionRect.set(null);
|
const isMultiSelection = get(store.multiselectionKeyPressed);
|
||||||
store.selectionRectMode.set(null);
|
|
||||||
|
|
||||||
if (get(store.multiselectionKeyPressed)) {
|
|
||||||
// @todo handle multiselection key
|
|
||||||
}
|
|
||||||
|
|
||||||
store.nodes.update((ns) =>
|
store.nodes.update((ns) =>
|
||||||
ns.map((node) => {
|
ns.map((node) => {
|
||||||
return {
|
const nodeWillBeSelected = ids.includes(node.id);
|
||||||
...node,
|
const selected = isMultiSelection
|
||||||
selected: ids.includes(node.id)
|
? node.selected || nodeWillBeSelected
|
||||||
};
|
: nodeWillBeSelected;
|
||||||
|
|
||||||
|
// we need to mutate the node here in order to have the correct selected state in the drag handler
|
||||||
|
node.selected = selected;
|
||||||
|
|
||||||
|
return node;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!isMultiSelection) {
|
||||||
|
store.edges.update((es) =>
|
||||||
|
es.map((edge) => {
|
||||||
|
edge.selected = false;
|
||||||
|
return edge;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSelectedEdges(ids: string[]) {
|
function addSelectedEdges(ids: string[]) {
|
||||||
if (get(store.multiselectionKeyPressed)) {
|
const isMultiSelection = get(store.multiselectionKeyPressed);
|
||||||
// @todo handle multiselection key
|
|
||||||
}
|
|
||||||
|
|
||||||
store.edges.update((edges) =>
|
store.edges.update((edges) =>
|
||||||
edges.map((edge) => {
|
edges.map((edge) => {
|
||||||
return {
|
const edgeWillBeSelected = ids.includes(edge.id);
|
||||||
...edge,
|
const selected = isMultiSelection
|
||||||
selected: ids.includes(edge.id)
|
? edge.selected || edgeWillBeSelected
|
||||||
};
|
: edgeWillBeSelected;
|
||||||
|
|
||||||
|
edge.selected = selected;
|
||||||
|
|
||||||
|
return edge;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!isMultiSelection) {
|
||||||
|
store.nodes.update((ns) =>
|
||||||
|
ns.map((node) => {
|
||||||
|
node.selected = false;
|
||||||
|
return node;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNodeSelection(id: string) {
|
||||||
|
const node = get(store.nodes)?.find((n) => n.id === id);
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
console.warn('012', errorMessages['error012'](id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
store.selectionRect.set(null);
|
||||||
|
store.selectionRectMode.set(null);
|
||||||
|
|
||||||
|
if (!node.selected) {
|
||||||
|
addSelectedNodes([id]);
|
||||||
|
} else if (node.selected && get(store.multiselectionKeyPressed)) {
|
||||||
|
unselectNodesAndEdges({ nodes: [node], edges: [] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function panBy(delta: XYPosition) {
|
function panBy(delta: XYPosition) {
|
||||||
@@ -326,6 +369,7 @@ export function createStore({
|
|||||||
unselectNodesAndEdges,
|
unselectNodesAndEdges,
|
||||||
addSelectedNodes,
|
addSelectedNodes,
|
||||||
addSelectedEdges,
|
addSelectedEdges,
|
||||||
|
handleNodeSelection,
|
||||||
panBy,
|
panBy,
|
||||||
updateConnection,
|
updateConnection,
|
||||||
cancelConnection,
|
cancelConnection,
|
||||||
|
|||||||
@@ -28,9 +28,10 @@ export type SvelteFlowStoreActions = {
|
|||||||
fitView: (options?: FitViewOptions) => boolean;
|
fitView: (options?: FitViewOptions) => boolean;
|
||||||
updateNodePositions: UpdateNodePositions;
|
updateNodePositions: UpdateNodePositions;
|
||||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
||||||
unselectNodesAndEdges: () => void;
|
unselectNodesAndEdges: (params?: { nodes?: Node[]; edges?: Edge[] }) => void;
|
||||||
addSelectedNodes: (ids: string[]) => void;
|
addSelectedNodes: (ids: string[]) => void;
|
||||||
addSelectedEdges: (ids: string[]) => void;
|
addSelectedEdges: (ids: string[]) => void;
|
||||||
|
handleNodeSelection: (id: string) => void;
|
||||||
panBy: (delta: XYPosition) => boolean;
|
panBy: (delta: XYPosition) => boolean;
|
||||||
updateConnection: UpdateConnection;
|
updateConnection: UpdateConnection;
|
||||||
cancelConnection: () => void;
|
cancelConnection: () => void;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ type StoreItems = {
|
|||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean;
|
||||||
nodeDragThreshold: number;
|
nodeDragThreshold: number;
|
||||||
panBy: PanBy;
|
panBy: PanBy;
|
||||||
unselectNodesAndEdges: () => void;
|
unselectNodesAndEdges: (params?: { nodes?: NodeBase[]; edges?: EdgeBase[] }) => void;
|
||||||
onError?: OnError;
|
onError?: OnError;
|
||||||
onNodeDragStart?: OnNodeDrag;
|
onNodeDragStart?: OnNodeDrag;
|
||||||
onNodeDrag?: OnNodeDrag;
|
onNodeDrag?: OnNodeDrag;
|
||||||
@@ -63,7 +63,7 @@ export type XYDragParams = {
|
|||||||
onDragStart?: OnDrag;
|
onDragStart?: OnDrag;
|
||||||
onDrag?: OnDrag;
|
onDrag?: OnDrag;
|
||||||
onDragStop?: OnDrag;
|
onDragStop?: OnDrag;
|
||||||
onNodeClick?: () => void;
|
onNodeMouseDown?: (id: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type XYDragInstance = {
|
export type XYDragInstance = {
|
||||||
@@ -81,7 +81,7 @@ export type DragUpdateParams = {
|
|||||||
|
|
||||||
export function XYDrag({
|
export function XYDrag({
|
||||||
domNode,
|
domNode,
|
||||||
onNodeClick,
|
onNodeMouseDown,
|
||||||
getStoreItems,
|
getStoreItems,
|
||||||
onDragStart,
|
onDragStart,
|
||||||
onDrag,
|
onDrag,
|
||||||
@@ -217,8 +217,8 @@ export function XYDrag({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSelectable && selectNodesOnDrag) {
|
if (isSelectable && selectNodesOnDrag && nodeId) {
|
||||||
onNodeClick?.();
|
onNodeMouseDown?.(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
const pointerPos = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
||||||
|
|||||||
Reference in New Issue
Block a user