mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(editor): Migrate deviceSupportHelpers mixin to useDeviceSupport composable (no-changelog) (#8289)
This commit is contained in:
parent
8a7c629ea1
commit
d32e6a60da
|
@ -0,0 +1,80 @@
|
|||
import { useDeviceSupport } from '@/composables/useDeviceSupport';
|
||||
|
||||
describe('useDeviceSupport()', () => {
|
||||
beforeEach(() => {
|
||||
global.window = Object.create(window);
|
||||
global.navigator = { userAgent: 'test-agent', maxTouchPoints: 0 };
|
||||
});
|
||||
|
||||
describe('isTouchDevice', () => {
|
||||
it('should be true if ontouchstart is in window', () => {
|
||||
Object.defineProperty(window, 'ontouchstart', {});
|
||||
const { isTouchDevice } = useDeviceSupport();
|
||||
expect(isTouchDevice).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be true if navigator.maxTouchPoints > 0', () => {
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', { value: 1 });
|
||||
const { isTouchDevice } = useDeviceSupport();
|
||||
expect(isTouchDevice).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be false if no touch support', () => {
|
||||
delete window.ontouchstart;
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', { value: 0 });
|
||||
const { isTouchDevice } = useDeviceSupport();
|
||||
expect(isTouchDevice).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isMacOs', () => {
|
||||
it('should be true for macOS user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'macintosh' });
|
||||
const { isMacOs } = useDeviceSupport();
|
||||
expect(isMacOs).toEqual(true);
|
||||
});
|
||||
|
||||
it('should be false for non-macOS user agent', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'windows' });
|
||||
const { isMacOs } = useDeviceSupport();
|
||||
expect(isMacOs).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('controlKeyCode', () => {
|
||||
it('should return Meta on macOS', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'macintosh' });
|
||||
const { controlKeyCode } = useDeviceSupport();
|
||||
expect(controlKeyCode).toEqual('Meta');
|
||||
});
|
||||
|
||||
it('should return Control on non-macOS', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'windows' });
|
||||
const { controlKeyCode } = useDeviceSupport();
|
||||
expect(controlKeyCode).toEqual('Control');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isCtrlKeyPressed()', () => {
|
||||
it('should return true for metaKey press on macOS', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'macintosh' });
|
||||
const { isCtrlKeyPressed } = useDeviceSupport();
|
||||
const event = new KeyboardEvent('keydown', { metaKey: true });
|
||||
expect(isCtrlKeyPressed(event)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true for ctrlKey press on non-macOS', () => {
|
||||
Object.defineProperty(navigator, 'userAgent', { value: 'windows' });
|
||||
const { isCtrlKeyPressed } = useDeviceSupport();
|
||||
const event = new KeyboardEvent('keydown', { ctrlKey: true });
|
||||
expect(isCtrlKeyPressed(event)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true for touch device on MouseEvent', () => {
|
||||
Object.defineProperty(window, 'ontouchstart', { value: {} });
|
||||
const { isCtrlKeyPressed } = useDeviceSupport();
|
||||
const mockEvent = new MouseEvent('click');
|
||||
expect(isCtrlKeyPressed(mockEvent)).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,14 +1,7 @@
|
|||
import { ref } from 'vue';
|
||||
|
||||
interface DeviceSupportHelpers {
|
||||
isTouchDevice: boolean;
|
||||
isMacOs: boolean;
|
||||
controlKeyCode: string;
|
||||
isCtrlKeyPressed: (e: MouseEvent | KeyboardEvent) => boolean;
|
||||
}
|
||||
|
||||
export function useDeviceSupport(): DeviceSupportHelpers {
|
||||
const isTouchDevice = ref('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
||||
export function useDeviceSupport() {
|
||||
const isTouchDevice = ref(window.hasOwnProperty('ontouchstart') || navigator.maxTouchPoints > 0);
|
||||
const userAgent = ref(navigator.userAgent.toLowerCase());
|
||||
const isMacOs = ref(
|
||||
userAgent.value.includes('macintosh') ||
|
||||
|
|
|
@ -62,13 +62,15 @@ import { onBeforeMount, onBeforeUnmount } from 'vue';
|
|||
import { storeToRefs } from 'pinia';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
|
||||
const canvasStore = useCanvasStore();
|
||||
const { zoomToFit, zoomIn, zoomOut, resetZoom } = canvasStore;
|
||||
const { nodeViewScale, isDemo } = storeToRefs(canvasStore);
|
||||
const deviceSupport = useDeviceSupport();
|
||||
|
||||
const keyDown = (e: KeyboardEvent) => {
|
||||
const isCtrlKeyPressed = e.metaKey || e.ctrlKey;
|
||||
const isCtrlKeyPressed = deviceSupport.isCtrlKeyPressed(e);
|
||||
if ((e.key === '=' || e.key === '+') && !isCtrlKeyPressed) {
|
||||
zoomIn();
|
||||
} else if ((e.key === '_' || e.key === '-') && !isCtrlKeyPressed) {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
:class="{
|
||||
'node-default': true,
|
||||
'touch-active': isTouchActive,
|
||||
'is-touch-device': isTouchDevice,
|
||||
'is-touch-device': deviceSupport.isTouchDevice,
|
||||
'menu-open': isContextMenuOpen,
|
||||
'disable-pointer-events': disablePointerEvents,
|
||||
}"
|
||||
|
@ -187,6 +187,7 @@ import { type ContextMenuTarget, useContextMenu } from '@/composables/useContext
|
|||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||
import { usePinnedData } from '@/composables/usePinnedData';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
import { useDebounce } from '@/composables/useDebounce';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -218,9 +219,17 @@ export default defineComponent({
|
|||
const nodeHelpers = useNodeHelpers();
|
||||
const node = workflowsStore.getNodeByName(props.name);
|
||||
const pinnedData = usePinnedData(node);
|
||||
const deviceSupport = useDeviceSupport();
|
||||
const { callDebounced } = useDebounce();
|
||||
|
||||
return { contextMenu, externalHooks, nodeHelpers, pinnedData, callDebounced };
|
||||
return {
|
||||
contextMenu,
|
||||
externalHooks,
|
||||
nodeHelpers,
|
||||
pinnedData,
|
||||
deviceSupport,
|
||||
callDebounced,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNodeTypesStore, useNDVStore, useUIStore, useWorkflowsStore),
|
||||
|
@ -698,7 +707,7 @@ export default defineComponent({
|
|||
this.pinDataDiscoveryTooltipVisible = false;
|
||||
},
|
||||
touchStart() {
|
||||
if (this.isTouchDevice === true && !this.isMacOs && !this.isTouchActive) {
|
||||
if (this.deviceSupport.isTouchDevice && !this.deviceSupport.isMacOs && !this.isTouchActive) {
|
||||
this.isTouchActive = true;
|
||||
setTimeout(() => {
|
||||
this.isTouchActive = false;
|
||||
|
|
|
@ -169,7 +169,7 @@ import { useNDVStore } from '@/stores/ndv.store';
|
|||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useDeviceSupport } from 'n8n-design-system/composables/useDeviceSupport';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
:class="{
|
||||
'sticky-default': true,
|
||||
'touch-active': isTouchActive,
|
||||
'is-touch-device': isTouchDevice,
|
||||
'is-touch-device': deviceSupport.isTouchDevice,
|
||||
'is-read-only': isReadOnly,
|
||||
}"
|
||||
:style="stickySize"
|
||||
|
@ -122,6 +122,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
|||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { useContextMenu } from '@/composables/useContextMenu';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Sticky',
|
||||
|
@ -135,6 +136,7 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
setup() {
|
||||
const deviceSupport = useDeviceSupport();
|
||||
const colorPopoverTrigger = ref<HTMLDivElement>();
|
||||
const forceActions = ref(false);
|
||||
const setForceActions = (value: boolean) => {
|
||||
|
@ -147,7 +149,7 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
|
||||
return { colorPopoverTrigger, contextMenu, forceActions, setForceActions };
|
||||
return { deviceSupport, colorPopoverTrigger, contextMenu, forceActions, setForceActions };
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNodeTypesStore, useNDVStore, useUIStore, useWorkflowsStore),
|
||||
|
@ -318,7 +320,7 @@ export default defineComponent({
|
|||
this.workflowsStore.updateNodeProperties(updateInformation);
|
||||
},
|
||||
touchStart() {
|
||||
if (this.isTouchDevice === true && !this.isMacOs && !this.isTouchActive) {
|
||||
if (this.deviceSupport.isTouchDevice === true && !this.isMacOs && !this.isTouchActive) {
|
||||
this.isTouchActive = true;
|
||||
setTimeout(() => {
|
||||
this.isTouchActive = false;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { INodeUi, XYPosition } from '@/Interface';
|
||||
|
||||
import { useDeviceSupport } from 'n8n-design-system/composables/useDeviceSupport';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { getMousePosition, getRelativePosition } from '@/utils/nodeViewUtils';
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useHistoryStore } from '@/stores/history.store';
|
|||
import { useUIStore } from '@/stores/ui.store';
|
||||
|
||||
import { onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import { useDeviceSupport } from 'n8n-design-system/composables/useDeviceSupport';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
import { getNodeViewTab } from '@/utils/canvasUtils';
|
||||
import type { Route } from 'vue-router';
|
||||
import { useTelemetry } from './useTelemetry';
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { defineComponent } from 'vue';
|
||||
|
||||
export const deviceSupportHelpers = defineComponent({
|
||||
data() {
|
||||
return {
|
||||
// @ts-ignore msMaxTouchPoints is deprecated but must fix tablet bugs before fixing this.. otherwise breaks touchscreen computers
|
||||
isTouchDevice: 'ontouchstart' in window || navigator.msMaxTouchPoints,
|
||||
isMacOs: /(ipad|iphone|ipod|mac)/i.test(navigator.platform), // TODO: `platform` deprecated
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// TODO: Check if used anywhere
|
||||
controlKeyCode(): string {
|
||||
if (this.isMacOs) {
|
||||
return 'Meta';
|
||||
}
|
||||
return 'Control';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isCtrlKeyPressed(e: MouseEvent | KeyboardEvent): boolean {
|
||||
if (this.isTouchDevice === true && e instanceof MouseEvent) {
|
||||
return true;
|
||||
}
|
||||
if (this.isMacOs) {
|
||||
return e.metaKey;
|
||||
}
|
||||
return e.ctrlKey;
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,12 +1,11 @@
|
|||
import { defineComponent } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
|
||||
import { deviceSupportHelpers } from '@/mixins/deviceSupportHelpers';
|
||||
import { getMousePosition } from '@/utils/nodeViewUtils';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
|
||||
export const moveNodeWorkflow = defineComponent({
|
||||
mixins: [deviceSupportHelpers],
|
||||
data() {
|
||||
return {
|
||||
moveLastPosition: [0, 0],
|
||||
|
@ -30,7 +29,9 @@ export const moveNodeWorkflow = defineComponent({
|
|||
this.moveLastPosition[1] = y;
|
||||
},
|
||||
mouseDownMoveWorkflow(e: MouseEvent, moveButtonPressed: boolean) {
|
||||
if (!this.isCtrlKeyPressed(e) && !moveButtonPressed) {
|
||||
const deviceSupport = useDeviceSupport();
|
||||
|
||||
if (!deviceSupport.isCtrlKeyPressed(e) && !moveButtonPressed) {
|
||||
// We only care about it when the ctrl key is pressed at the same time.
|
||||
// So we exit when it is not pressed.
|
||||
return;
|
||||
|
|
|
@ -3,7 +3,6 @@ import type { PropType } from 'vue';
|
|||
import { mapStores } from 'pinia';
|
||||
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import { deviceSupportHelpers } from '@/mixins/deviceSupportHelpers';
|
||||
import {
|
||||
NO_OP_NODE_TYPE,
|
||||
NODE_CONNECTION_TYPE_ALLOW_MULTIPLE,
|
||||
|
@ -28,6 +27,7 @@ import * as NodeViewUtils from '@/utils/nodeViewUtils';
|
|||
import { useHistoryStore } from '@/stores/history.store';
|
||||
import { useCanvasStore } from '@/stores/canvas.store';
|
||||
import type { EndpointSpec } from '@jsplumb/common';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
|
||||
const createAddInputEndpointSpec = (
|
||||
connectionName: NodeConnectionType,
|
||||
|
@ -56,7 +56,6 @@ const createDiamondOutputEndpointSpec = (): EndpointSpec => ({
|
|||
});
|
||||
|
||||
export const nodeBase = defineComponent({
|
||||
mixins: [deviceSupportHelpers],
|
||||
data() {
|
||||
return {
|
||||
inputs: [] as Array<ConnectionTypes | INodeInputConfiguration>,
|
||||
|
@ -615,13 +614,16 @@ export const nodeBase = defineComponent({
|
|||
return createSupplementalConnectionType(connectionType);
|
||||
},
|
||||
touchEnd(e: MouseEvent) {
|
||||
if (this.isTouchDevice) {
|
||||
const deviceSupport = useDeviceSupport();
|
||||
if (deviceSupport.isTouchDevice) {
|
||||
if (this.uiStore.isActionActive('dragActive')) {
|
||||
this.uiStore.removeActiveAction('dragActive');
|
||||
}
|
||||
}
|
||||
},
|
||||
mouseLeftClick(e: MouseEvent) {
|
||||
const deviceSupport = useDeviceSupport();
|
||||
|
||||
// @ts-ignore
|
||||
const path = e.path || (e.composedPath && e.composedPath());
|
||||
for (let index = 0; index < path.length; index++) {
|
||||
|
@ -634,11 +636,11 @@ export const nodeBase = defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
if (!this.isTouchDevice) {
|
||||
if (!deviceSupport.isTouchDevice) {
|
||||
if (this.uiStore.isActionActive('dragActive')) {
|
||||
this.uiStore.removeActiveAction('dragActive');
|
||||
} else {
|
||||
if (!this.isCtrlKeyPressed(e)) {
|
||||
if (!deviceSupport.isCtrlKeyPressed(e)) {
|
||||
this.$emit('deselectAllNodes');
|
||||
}
|
||||
|
||||
|
|
|
@ -377,6 +377,7 @@ import { useExternalHooks } from '@/composables/useExternalHooks';
|
|||
import { useClipboard } from '@/composables/useClipboard';
|
||||
import { usePinnedData } from '@/composables/usePinnedData';
|
||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import { useDeviceSupport } from 'n8n-design-system';
|
||||
import { useDebounce } from '@/composables/useDebounce';
|
||||
|
||||
interface AddNodeOptions {
|
||||
|
@ -476,6 +477,7 @@ export default defineComponent({
|
|||
const clipboard = useClipboard();
|
||||
const { activeNode } = storeToRefs(ndvStore);
|
||||
const pinnedData = usePinnedData(activeNode);
|
||||
const deviceSupport = useDeviceSupport();
|
||||
const { callDebounced } = useDebounce();
|
||||
|
||||
return {
|
||||
|
@ -486,6 +488,7 @@ export default defineComponent({
|
|||
externalHooks,
|
||||
clipboard,
|
||||
pinnedData,
|
||||
deviceSupport,
|
||||
callDebounced,
|
||||
...useCanvasMouseSelect(),
|
||||
...useGlobalLinkActions(),
|
||||
|
@ -1378,7 +1381,7 @@ export default defineComponent({
|
|||
this.collaborationStore.notifyWorkflowOpened(workflow.id);
|
||||
},
|
||||
touchTap(e: MouseEvent | TouchEvent) {
|
||||
if (this.isTouchDevice) {
|
||||
if (this.deviceSupport.isTouchDevice) {
|
||||
this.mouseDown(e);
|
||||
}
|
||||
},
|
||||
|
@ -1403,7 +1406,7 @@ export default defineComponent({
|
|||
this.mouseUpMoveWorkflow(e);
|
||||
},
|
||||
keyUp(e: KeyboardEvent) {
|
||||
if (e.key === this.controlKeyCode) {
|
||||
if (e.key === this.deviceSupport.controlKeyCode) {
|
||||
this.ctrlKeyPressed = false;
|
||||
}
|
||||
if (e.key === ' ') {
|
||||
|
@ -1413,10 +1416,10 @@ export default defineComponent({
|
|||
async keyDown(e: KeyboardEvent) {
|
||||
this.contextMenu.close();
|
||||
|
||||
const ctrlModifier = this.isCtrlKeyPressed(e) && !e.shiftKey && !e.altKey;
|
||||
const shiftModifier = e.shiftKey && !e.altKey && !this.isCtrlKeyPressed(e);
|
||||
const ctrlAltModifier = this.isCtrlKeyPressed(e) && e.altKey && !e.shiftKey;
|
||||
const noModifierKeys = !this.isCtrlKeyPressed(e) && !e.shiftKey && !e.altKey;
|
||||
const ctrlModifier = this.deviceSupport.isCtrlKeyPressed(e) && !e.shiftKey && !e.altKey;
|
||||
const shiftModifier = e.shiftKey && !e.altKey && !this.deviceSupport.isCtrlKeyPressed(e);
|
||||
const ctrlAltModifier = this.deviceSupport.isCtrlKeyPressed(e) && e.altKey && !e.shiftKey;
|
||||
const noModifierKeys = !this.deviceSupport.isCtrlKeyPressed(e) && !e.shiftKey && !e.altKey;
|
||||
const readOnly = this.isReadOnlyRoute || this.readOnlyEnv;
|
||||
|
||||
if (e.key === 's' && ctrlModifier && !readOnly) {
|
||||
|
@ -1497,7 +1500,7 @@ export default defineComponent({
|
|||
void this.onRunWorkflow();
|
||||
} else if (e.key === 'S' && shiftModifier && !readOnly) {
|
||||
void this.onAddNodes({ nodes: [{ type: STICKY_NODE_TYPE }], connections: [] });
|
||||
} else if (e.key === this.controlKeyCode) {
|
||||
} else if (e.key === this.deviceSupport.controlKeyCode) {
|
||||
this.ctrlKeyPressed = true;
|
||||
} else if (e.key === ' ') {
|
||||
this.moveCanvasKeyPressed = true;
|
||||
|
|
Loading…
Reference in a new issue