Merge pull request #4037 from xyflow/svelte-multiple-key-handler

Svelte Flow: add multiple keys
This commit is contained in:
Moritz Klack
2024-04-03 16:54:57 +02:00
committed by GitHub
4 changed files with 55 additions and 118 deletions
@@ -189,6 +189,7 @@
autoPanOnNodeDrag autoPanOnNodeDrag
connectionMode={ConnectionMode.Strict} connectionMode={ConnectionMode.Strict}
attributionPosition={'top-center'} attributionPosition={'top-center'}
deleteKey={['Backspace', 'd']}
> >
<Controls> <Controls>
<ControlButton slot="before">xy</ControlButton> <ControlButton slot="before">xy</ControlButton>
@@ -1,5 +1,9 @@
<script lang="ts"> <script lang="ts">
import { shortcut, type ShortcutModifierDefinition } from '@svelte-put/shortcut'; import {
shortcut,
type ShortcutEventDetail,
type ShortcutModifierDefinition
} from '@svelte-put/shortcut';
import { isInputDOMNode, isMacOs } from '@xyflow/system'; import { isInputDOMNode, isMacOs } from '@xyflow/system';
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
@@ -19,7 +23,8 @@
multiselectionKeyPressed, multiselectionKeyPressed,
deleteKeyPressed, deleteKeyPressed,
panActivationKeyPressed, panActivationKeyPressed,
zoomActivationKeyPressed zoomActivationKeyPressed,
selectionRect
} = useStore(); } = useStore();
function isKeyObject(key?: KeyDefinition | null): key is KeyDefinitionObject { function isKeyObject(key?: KeyDefinition | null): key is KeyDefinitionObject {
@@ -40,150 +45,81 @@
return isKeyObject(key) ? key.key : key; return isKeyObject(key) ? key.key : key;
} }
function resetAll() { function getShortcutTrigger(
key: KeyDefinition | KeyDefinition[] | null | undefined,
callback: (detail: ShortcutEventDetail) => void
) {
const keys = Array.isArray(key) ? key : [key];
return keys.map((_key) => {
const keyString = getKeyString(_key);
return {
key: keyString,
modifier: getModifier(_key),
enabled: keyString !== null,
callback
};
});
}
function resetKeysAndSelection() {
selectionRect.set(null);
selectionKeyPressed.set(false); selectionKeyPressed.set(false);
multiselectionKeyPressed.set(false); multiselectionKeyPressed.set(false);
deleteKeyPressed.set(false); deleteKeyPressed.set(false);
panActivationKeyPressed.set(false); panActivationKeyPressed.set(false);
zoomActivationKeyPressed.set(false); zoomActivationKeyPressed.set(false);
} }
$: selectionKeyDefinition = {
key: getKeyString(selectionKey),
modifier: getModifier(selectionKey)
};
$: multiSelectionKeyDefinition = {
key: getKeyString(multiSelectionKey),
modifier: getModifier(multiSelectionKey)
};
$: deleteKeyDefinition = {
key: getKeyString(deleteKey),
modifier: getModifier(deleteKey)
};
$: panActivationKeyDefinition = {
key: getKeyString(panActivationKey),
modifier: getModifier(panActivationKey)
};
$: zoomActivationKeyDefinition = {
key: getKeyString(zoomActivationKey),
modifier: getModifier(zoomActivationKey)
};
</script> </script>
<svelte:window <svelte:window
on:blur={resetAll} on:blur={resetKeysAndSelection}
on:contextmenu={resetAll} on:contextmenu={resetKeysAndSelection}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(selectionKey, () => selectionKeyPressed.set(true)),
{
...selectionKeyDefinition,
enabled: selectionKeyDefinition.key !== null,
callback: () => selectionKeyPressed.set(true)
}
],
type: 'keydown' type: 'keydown'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(selectionKey, () => selectionKeyPressed.set(false)),
{
...selectionKeyDefinition,
enabled: selectionKeyDefinition.key !== null,
callback: () => selectionKeyPressed.set(false)
}
],
type: 'keyup' type: 'keyup'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(multiSelectionKey, () => multiselectionKeyPressed.set(true)),
{
...multiSelectionKeyDefinition,
enabled: multiSelectionKeyDefinition.key !== null,
callback: () => multiselectionKeyPressed.set(true)
}
],
type: 'keydown' type: 'keydown'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(multiSelectionKey, () => multiselectionKeyPressed.set(false)),
{
...multiSelectionKeyDefinition,
enabled: multiSelectionKeyDefinition.key !== null,
callback: () => multiselectionKeyPressed.set(false)
}
],
type: 'keyup' type: 'keyup'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(deleteKey, (detail) => {
{ const isModifierKey =
...deleteKeyDefinition, detail.originalEvent.ctrlKey ||
enabled: deleteKeyDefinition.key !== null, detail.originalEvent.metaKey ||
callback: (detail) => { detail.originalEvent.shiftKey;
const isModifierKey = if (!isModifierKey && !isInputDOMNode(detail.originalEvent)) {
detail.originalEvent.ctrlKey || deleteKeyPressed.set(true);
detail.originalEvent.metaKey ||
detail.originalEvent.shiftKey;
if (!isModifierKey && !isInputDOMNode(detail.originalEvent)) {
deleteKeyPressed.set(true);
}
}
} }
], }),
type: 'keydown' type: 'keydown'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(deleteKey, () => deleteKeyPressed.set(false)),
{
...deleteKeyDefinition,
enabled: deleteKeyDefinition.key !== null,
callback: () => deleteKeyPressed.set(false)
}
],
type: 'keyup' type: 'keyup'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(panActivationKey, () => panActivationKeyPressed.set(true)),
{
...panActivationKeyDefinition,
enabled: panActivationKeyDefinition.key !== null,
callback: () => panActivationKeyPressed.set(true)
}
],
type: 'keydown' type: 'keydown'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(panActivationKey, () => panActivationKeyPressed.set(false)),
{
...panActivationKeyDefinition,
enabled: panActivationKeyDefinition.key !== null,
callback: () => panActivationKeyPressed.set(false)
}
],
type: 'keyup' type: 'keyup'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(zoomActivationKey, () => zoomActivationKeyPressed.set(true)),
{
...zoomActivationKeyDefinition,
enabled: zoomActivationKeyDefinition.key !== null,
callback: () => zoomActivationKeyPressed.set(true)
}
],
type: 'keydown' type: 'keydown'
}} }}
use:shortcut={{ use:shortcut={{
trigger: [ trigger: getShortcutTrigger(zoomActivationKey, () => zoomActivationKeyPressed.set(false)),
{
...zoomActivationKeyDefinition,
enabled: zoomActivationKeyDefinition.key !== null,
callback: () => zoomActivationKeyPressed.set(false)
}
],
type: 'keyup' type: 'keyup'
}} }}
/> />
@@ -1,9 +1,9 @@
import type { KeyDefinition } from '$lib/types'; import type { KeyDefinition } from '$lib/types';
export type KeyHandlerProps = { export type KeyHandlerProps = {
selectionKey?: KeyDefinition | null; selectionKey?: KeyDefinition | KeyDefinition[] | null;
multiSelectionKey?: KeyDefinition | null; multiSelectionKey?: KeyDefinition | KeyDefinition[] | null;
deleteKey?: KeyDefinition | null; deleteKey?: KeyDefinition | KeyDefinition[] | null;
panActivationKey?: KeyDefinition | null; panActivationKey?: KeyDefinition | KeyDefinition[] | null;
zoomActivationKey?: KeyDefinition | null; zoomActivationKey?: KeyDefinition | KeyDefinition[] | null;
}; };
@@ -86,27 +86,27 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
/** Pressing down this key you can select multiple elements with a selection box. /** Pressing down this key you can select multiple elements with a selection box.
* @default 'Shift' * @default 'Shift'
*/ */
selectionKey?: KeyDefinition | null; selectionKey?: KeyDefinition | KeyDefinition[] | null;
/** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false. /** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false.
* *
* By setting this prop to null you can disable this functionality. * By setting this prop to null you can disable this functionality.
* @default 'Space' * @default 'Space'
*/ */
panActivationKey?: KeyDefinition | null; panActivationKey?: KeyDefinition | KeyDefinition[] | null;
/** Pressing down this key deletes all selected nodes & edges. /** Pressing down this key deletes all selected nodes & edges.
* @default 'Backspace' * @default 'Backspace'
*/ */
deleteKey?: KeyDefinition | null; deleteKey?: KeyDefinition | KeyDefinition[] | null;
/** Pressing down this key you can select multiple elements by clicking. /** Pressing down this key you can select multiple elements by clicking.
* @default 'Meta' for macOS, "Ctrl" for other systems * @default 'Meta' for macOS, "Ctrl" for other systems
*/ */
multiSelectionKey?: KeyDefinition | null; multiSelectionKey?: KeyDefinition | KeyDefinition[] | null;
/** If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false. /** If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false.
* *
* By setting this prop to null you can disable this functionality. * By setting this prop to null you can disable this functionality.
* @default 'Meta' for macOS, "Ctrl" for other systems * @default 'Meta' for macOS, "Ctrl" for other systems
* */ * */
zoomActivationKey?: KeyDefinition | null; zoomActivationKey?: KeyDefinition | KeyDefinition[] | null;
/** If set, initial viewport will show all nodes & edges */ /** If set, initial viewport will show all nodes & edges */
fitView?: boolean; fitView?: boolean;
/** Options to be used in combination with fitView /** Options to be used in combination with fitView