mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
feat(editor-ui): Resizable main panel (#3980)
* Introduce node deprecation (#3930) ✨ Introduce node deprecation * 🚧 Scaffold out Code node * 👕 Fix lint * 📘 Create types file * 🚚 Rename theme * 🔥 Remove unneeded prop * ⚡ Override keybindings * ⚡ Expand lintings * ⚡ Create editor content getter * 🚚 Ensure all helpers use `$` * ✨ Add autocompletion * ♻️ Refactore Resize UI lib component, allow to use it in different than n8n-sticky context * 🚧 Use variable width for node settings and allow for resizing * ✨ Use store to keep track of wide and regular main panel widths * ♻️ Extract Resize wrapper from the Sticky and create a story for it * 🐛 Fixed cherry-pick conflicts * ⚡ Filter out welcome note node * ⚡ Convey error line number * ⚡ Highlight error line * ⚡ Restore logging from node * ✨ More autocompletions * ⚡ Streamline completions * 💄 Fix drag-button border * ✏️ Update placeholders * ⚡ Update linter to new methods * ✨ Preserve main panel width in local storage * 🐛 Fallback to max size size if window is too big * 🔥 Remove `$nodeItem` completions * ⚡ Re-update placeholders * 🎨 Fix formatting * 📦 Update `package-lock.json` * ⚡ Refresh with multi-line empty string * ♻️ Refactored DraggablePanels to use relative units and implemented independent resizing, cleaned store * 🐛 Re-implement dragging indicators and move border styles to NDVDraggablePanels component * 🚨 Fix semis * 🚨 Remove unsused UI state props * ♻️ Use only relative left position and calculate right based on it, fix quirks * 🚨Fix linting error * ♻️ Store and retrieve main panel dimensions from store to make them persistable in the same app mount session * 🐛 Prevent resizing of unknown nodes * ♻️ Add typings for `nodeType` prop, remove unused `convertRemToPixels` import * 🏷️ Add typings for `nodeType` prop in NodeSettings.vue * 🐛 Prevent the main panel resize below 280px * 🐛 Fix inputless panel left position * ✨ Resize resource locator on main panel size change * 🐛 Resize resource locator on window resize Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
parent
8eeed77edb
commit
d01f7d4d93
62
package-lock.json
generated
62
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.194.0",
|
"version": "0.195.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.194.0",
|
"version": "0.195.3",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
"packages/@n8n_io/*"
|
"packages/@n8n_io/*"
|
||||||
|
@ -52142,7 +52142,7 @@
|
||||||
},
|
},
|
||||||
"packages/cli": {
|
"packages/cli": {
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.194.0",
|
"version": "0.195.3",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oclif/command": "^1.5.18",
|
"@oclif/command": "^1.5.18",
|
||||||
|
@ -52186,10 +52186,10 @@
|
||||||
"lodash.split": "^4.4.2",
|
"lodash.split": "^4.4.2",
|
||||||
"lodash.unset": "^4.5.2",
|
"lodash.unset": "^4.5.2",
|
||||||
"mysql2": "~2.3.0",
|
"mysql2": "~2.3.0",
|
||||||
"n8n-core": "~0.134.0",
|
"n8n-core": "~0.135.0",
|
||||||
"n8n-editor-ui": "~0.160.0",
|
"n8n-editor-ui": "~0.161.1",
|
||||||
"n8n-nodes-base": "~0.192.0",
|
"n8n-nodes-base": "~0.193.1",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"nodemailer": "^6.7.1",
|
"nodemailer": "^6.7.1",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"open": "^7.0.0",
|
"open": "^7.0.0",
|
||||||
|
@ -52268,7 +52268,7 @@
|
||||||
},
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "n8n-core",
|
"name": "n8n-core",
|
||||||
"version": "0.134.0",
|
"version": "0.135.0",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
@ -52280,7 +52280,7 @@
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"qs": "^6.10.1",
|
"qs": "^6.10.1",
|
||||||
|
@ -52367,7 +52367,7 @@
|
||||||
},
|
},
|
||||||
"packages/design-system": {
|
"packages/design-system": {
|
||||||
"name": "n8n-design-system",
|
"name": "n8n-design-system",
|
||||||
"version": "0.34.0",
|
"version": "0.35.0",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"element-ui": "~2.15.7",
|
"element-ui": "~2.15.7",
|
||||||
|
@ -52434,14 +52434,14 @@
|
||||||
},
|
},
|
||||||
"packages/editor-ui": {
|
"packages/editor-ui": {
|
||||||
"name": "n8n-editor-ui",
|
"name": "n8n-editor-ui",
|
||||||
"version": "0.160.0",
|
"version": "0.161.1",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/open-sans": "^4.5.0",
|
"@fontsource/open-sans": "^4.5.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||||
"luxon": "^2.3.0",
|
"luxon": "^2.3.0",
|
||||||
"monaco-editor": "^0.30.1",
|
"monaco-editor": "^0.30.1",
|
||||||
"n8n-design-system": "~0.34.0",
|
"n8n-design-system": "~0.35.0",
|
||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"v-click-outside": "^3.1.2",
|
"v-click-outside": "^3.1.2",
|
||||||
"vue-fragment": "1.5.1",
|
"vue-fragment": "1.5.1",
|
||||||
|
@ -52487,7 +52487,7 @@
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"normalize-wheel": "^1.0.1",
|
"normalize-wheel": "^1.0.1",
|
||||||
"prismjs": "^1.17.1",
|
"prismjs": "^1.17.1",
|
||||||
"quill": "^2.0.0-dev.3",
|
"quill": "^2.0.0-dev.3",
|
||||||
|
@ -52513,7 +52513,7 @@
|
||||||
},
|
},
|
||||||
"packages/node-dev": {
|
"packages/node-dev": {
|
||||||
"name": "n8n-node-dev",
|
"name": "n8n-node-dev",
|
||||||
"version": "0.73.0",
|
"version": "0.74.0",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oclif/command": "^1.5.18",
|
"@oclif/command": "^1.5.18",
|
||||||
|
@ -52521,8 +52521,8 @@
|
||||||
"change-case": "^4.1.1",
|
"change-case": "^4.1.1",
|
||||||
"fast-glob": "^3.2.5",
|
"fast-glob": "^3.2.5",
|
||||||
"inquirer": "^7.0.1",
|
"inquirer": "^7.0.1",
|
||||||
"n8n-core": "~0.134.0",
|
"n8n-core": "~0.135.0",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"replace-in-file": "^6.0.0",
|
"replace-in-file": "^6.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
|
@ -52544,7 +52544,7 @@
|
||||||
},
|
},
|
||||||
"packages/nodes-base": {
|
"packages/nodes-base": {
|
||||||
"name": "n8n-nodes-base",
|
"name": "n8n-nodes-base",
|
||||||
"version": "0.192.0",
|
"version": "0.193.1",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kafkajs/confluent-schema-registry": "1.0.6",
|
"@kafkajs/confluent-schema-registry": "1.0.6",
|
||||||
|
@ -52579,7 +52579,7 @@
|
||||||
"mqtt": "4.2.6",
|
"mqtt": "4.2.6",
|
||||||
"mssql": "^8.1.2",
|
"mssql": "^8.1.2",
|
||||||
"mysql2": "~2.3.0",
|
"mysql2": "~2.3.0",
|
||||||
"n8n-core": "~0.134.0",
|
"n8n-core": "~0.135.0",
|
||||||
"node-html-markdown": "^1.1.3",
|
"node-html-markdown": "^1.1.3",
|
||||||
"node-ssh": "^12.0.0",
|
"node-ssh": "^12.0.0",
|
||||||
"nodemailer": "^6.7.1",
|
"nodemailer": "^6.7.1",
|
||||||
|
@ -52636,7 +52636,7 @@
|
||||||
"eslint-plugin-n8n-nodes-base": "^1.9.3",
|
"eslint-plugin-n8n-nodes-base": "^1.9.3",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"jest": "^27.4.7",
|
"jest": "^27.4.7",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.3",
|
||||||
"tslint": "^6.1.2",
|
"tslint": "^6.1.2",
|
||||||
"typescript": "~4.6.0"
|
"typescript": "~4.6.0"
|
||||||
|
@ -52686,7 +52686,7 @@
|
||||||
},
|
},
|
||||||
"packages/workflow": {
|
"packages/workflow": {
|
||||||
"name": "n8n-workflow",
|
"name": "n8n-workflow",
|
||||||
"version": "0.116.0",
|
"version": "0.117.0",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@n8n_io/riot-tmpl": "^1.0.1",
|
"@n8n_io/riot-tmpl": "^1.0.1",
|
||||||
|
@ -81496,10 +81496,10 @@
|
||||||
"lodash.split": "^4.4.2",
|
"lodash.split": "^4.4.2",
|
||||||
"lodash.unset": "^4.5.2",
|
"lodash.unset": "^4.5.2",
|
||||||
"mysql2": "~2.3.0",
|
"mysql2": "~2.3.0",
|
||||||
"n8n-core": "~0.134.0",
|
"n8n-core": "~0.135.0",
|
||||||
"n8n-editor-ui": "~0.160.0",
|
"n8n-editor-ui": "~0.161.1",
|
||||||
"n8n-nodes-base": "~0.192.0",
|
"n8n-nodes-base": "~0.193.1",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"nodemailer": "^6.7.1",
|
"nodemailer": "^6.7.1",
|
||||||
"nodemon": "^2.0.2",
|
"nodemon": "^2.0.2",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
|
@ -81553,7 +81553,7 @@
|
||||||
"jest": "^27.4.7",
|
"jest": "^27.4.7",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"qs": "^6.10.1",
|
"qs": "^6.10.1",
|
||||||
|
@ -81699,8 +81699,8 @@
|
||||||
"luxon": "^2.3.0",
|
"luxon": "^2.3.0",
|
||||||
"monaco-editor": "^0.30.1",
|
"monaco-editor": "^0.30.1",
|
||||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||||
"n8n-design-system": "~0.34.0",
|
"n8n-design-system": "~0.35.0",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"normalize-wheel": "^1.0.1",
|
"normalize-wheel": "^1.0.1",
|
||||||
"prismjs": "^1.17.1",
|
"prismjs": "^1.17.1",
|
||||||
"quill": "^2.0.0-dev.3",
|
"quill": "^2.0.0-dev.3",
|
||||||
|
@ -81746,8 +81746,8 @@
|
||||||
"change-case": "^4.1.1",
|
"change-case": "^4.1.1",
|
||||||
"fast-glob": "^3.2.5",
|
"fast-glob": "^3.2.5",
|
||||||
"inquirer": "^7.0.1",
|
"inquirer": "^7.0.1",
|
||||||
"n8n-core": "~0.134.0",
|
"n8n-core": "~0.135.0",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"replace-in-file": "^6.0.0",
|
"replace-in-file": "^6.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
|
@ -81824,8 +81824,8 @@
|
||||||
"mqtt": "4.2.6",
|
"mqtt": "4.2.6",
|
||||||
"mssql": "^8.1.2",
|
"mssql": "^8.1.2",
|
||||||
"mysql2": "~2.3.0",
|
"mysql2": "~2.3.0",
|
||||||
"n8n-core": "~0.134.0",
|
"n8n-core": "~0.135.0",
|
||||||
"n8n-workflow": "~0.116.0",
|
"n8n-workflow": "~0.117.0",
|
||||||
"node-html-markdown": "^1.1.3",
|
"node-html-markdown": "^1.1.3",
|
||||||
"node-ssh": "^12.0.0",
|
"node-ssh": "^12.0.0",
|
||||||
"nodemailer": "^6.7.1",
|
"nodemailer": "^6.7.1",
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import N8nResizeWrapper from './ResizeWrapper.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Atoms/ResizeWrapper',
|
||||||
|
component: N8nResizeWrapper,
|
||||||
|
};
|
||||||
|
|
||||||
|
const methods = {
|
||||||
|
onInput: action('input'),
|
||||||
|
onResize(resizeData) {
|
||||||
|
action('resize', resizeData);
|
||||||
|
this.newHeight = resizeData.height;
|
||||||
|
this.newWidth = resizeData.width;
|
||||||
|
},
|
||||||
|
onResizeEnd: action('resizeend'),
|
||||||
|
onResizeStart: action('resizestart'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = (args, { argTypes }) => ({
|
||||||
|
props: Object.keys(argTypes),
|
||||||
|
components: {
|
||||||
|
N8nResizeWrapper,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
newWidth: this.width,
|
||||||
|
newHeight: this.height,
|
||||||
|
background: "linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%)",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
containerStyles() {
|
||||||
|
return {
|
||||||
|
width: `${this.newWidth}px`,
|
||||||
|
height: `${this.newHeight}px`,
|
||||||
|
background: this.background,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template:
|
||||||
|
`<div style="width: fit-content; height: fit-content">
|
||||||
|
<n8n-resize-wrapper
|
||||||
|
v-bind="$props"
|
||||||
|
@resize="onResize"
|
||||||
|
@resizeend="onResizeEnd"
|
||||||
|
@resizestart="onResizeStart"
|
||||||
|
@input="onInput"
|
||||||
|
:width="newWidth"
|
||||||
|
:height="newHeight"
|
||||||
|
>
|
||||||
|
<div :style="containerStyles" />
|
||||||
|
</n8n-resize-wrapper>
|
||||||
|
</div>`,
|
||||||
|
methods,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Resize = Template.bind({});
|
||||||
|
Resize.args = {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
minWidth: 200,
|
||||||
|
minHeight: 200,
|
||||||
|
scale: 1,
|
||||||
|
gridSize: 20,
|
||||||
|
isResizingEnabled: true,
|
||||||
|
supportedDirections: [
|
||||||
|
"right",
|
||||||
|
"top",
|
||||||
|
"bottom",
|
||||||
|
"left",
|
||||||
|
"topLeft",
|
||||||
|
"topRight",
|
||||||
|
"bottomLeft",
|
||||||
|
"bottomRight",
|
||||||
|
],
|
||||||
|
};
|
|
@ -1,34 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.resize">
|
<div :class="$style.resize">
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="right" :class="[$style.resizer, $style.right]" />
|
<div
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="left" :class="[$style.resizer, $style.left]" />
|
v-for="direction in enabledDirections"
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="top" :class="[$style.resizer, $style.top]" />
|
:key="direction"
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="bottom" :class="[$style.resizer, $style.bottom]" />
|
:data-dir="direction"
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="top-left" :class="[$style.resizer, $style.topLeft]" />
|
:class="[$style.resizer, $style[direction]]"
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="top-right" :class="[$style.resizer, $style.topRight]" />
|
@mousedown="resizerMove"
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="bottom-left" :class="[$style.resizer, $style.bottomLeft]" />
|
/>
|
||||||
<div v-if="isResizingEnabled" @mousedown="resizerMove" data-dir="bottom-right" :class="[$style.resizer, $style.bottomRight]" />
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
const cursorMap: { [key: string]: string } = {
|
import Vue from 'vue';
|
||||||
right: 'ew-resize',
|
|
||||||
top: 'ns-resize',
|
|
||||||
bottom: 'ns-resize',
|
|
||||||
left: 'ew-resize',
|
|
||||||
'top-left': 'nw-resize',
|
|
||||||
'top-right' : 'ne-resize',
|
|
||||||
'bottom-left': 'sw-resize',
|
|
||||||
'bottom-right': 'se-resize',
|
|
||||||
};
|
|
||||||
|
|
||||||
function closestNumber(value: number, divisor: number): number {
|
function closestNumber(value: number, divisor: number): number {
|
||||||
let q = value / divisor;
|
const q = value / divisor;
|
||||||
let n1 = divisor * q;
|
const n1 = divisor * q;
|
||||||
|
|
||||||
let n2 = (value * divisor) > 0 ?
|
const n2 = (value * divisor) > 0 ?
|
||||||
(divisor * (q + 1)) : (divisor * (q - 1));
|
(divisor * (q + 1)) : (divisor * (q - 1));
|
||||||
|
|
||||||
if (Math.abs(value - n1) < Math.abs(value - n2))
|
if (Math.abs(value - n1) < Math.abs(value - n2))
|
||||||
|
@ -37,7 +27,7 @@ function closestNumber(value: number, divisor: number): number {
|
||||||
return n2;
|
return n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSize(delta: number, min: number, virtual: number, gridSize: number): number {
|
function getSize(min: number, virtual: number, gridSize: number): number {
|
||||||
const target = closestNumber(virtual, gridSize);
|
const target = closestNumber(virtual, gridSize);
|
||||||
if (target >= min && virtual > 0) {
|
if (target >= min && virtual > 0) {
|
||||||
return target;
|
return target;
|
||||||
|
@ -46,7 +36,16 @@ function getSize(delta: number, min: number, virtual: number, gridSize: number):
|
||||||
return min;
|
return min;
|
||||||
};
|
};
|
||||||
|
|
||||||
import Vue from 'vue';
|
const directionsCursorMaps: { [key: string]: string } = {
|
||||||
|
right: 'ew-resize',
|
||||||
|
top: 'ns-resize',
|
||||||
|
bottom: 'ns-resize',
|
||||||
|
left: 'ew-resize',
|
||||||
|
topLeft: 'nw-resize',
|
||||||
|
topRight : 'ne-resize',
|
||||||
|
bottomLeft: 'sw-resize',
|
||||||
|
bottomRight: 'se-resize',
|
||||||
|
};
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'n8n-resize',
|
name: 'n8n-resize',
|
||||||
|
@ -74,9 +73,14 @@ export default Vue.extend({
|
||||||
gridSize: {
|
gridSize: {
|
||||||
type: Number,
|
type: Number,
|
||||||
},
|
},
|
||||||
|
supportedDirections: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
directionsCursorMaps,
|
||||||
dir: '',
|
dir: '',
|
||||||
dHeight: 0,
|
dHeight: 0,
|
||||||
dWidth: 0,
|
dWidth: 0,
|
||||||
|
@ -86,6 +90,16 @@ export default Vue.extend({
|
||||||
y: 0,
|
y: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
enabledDirections() {
|
||||||
|
const availableDirections = Object.keys(directionsCursorMaps);
|
||||||
|
|
||||||
|
if(this.isResizingEnabled === false) return [];
|
||||||
|
if(this.supportedDirections.length === 0) return availableDirections;
|
||||||
|
|
||||||
|
return this.supportedDirections;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resizerMove(event: MouseEvent) {
|
resizerMove(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -93,10 +107,10 @@ export default Vue.extend({
|
||||||
|
|
||||||
const targetResizer = event.target as { dataset: { dir: string } } | null;
|
const targetResizer = event.target as { dataset: { dir: string } } | null;
|
||||||
if (targetResizer) {
|
if (targetResizer) {
|
||||||
this.dir = targetResizer.dataset.dir;
|
this.dir = targetResizer.dataset.dir.toLocaleLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.style.cursor = cursorMap[this.dir];
|
document.body.style.cursor = directionsCursorMaps[this.dir];
|
||||||
|
|
||||||
this.x = event.pageX;
|
this.x = event.pageX;
|
||||||
this.y = event.pageY;
|
this.y = event.pageY;
|
||||||
|
@ -137,17 +151,20 @@ export default Vue.extend({
|
||||||
|
|
||||||
this.vHeight = this.vHeight + deltaHeight;
|
this.vHeight = this.vHeight + deltaHeight;
|
||||||
this.vWidth = this.vWidth + deltaWidth;
|
this.vWidth = this.vWidth + deltaWidth;
|
||||||
const height = getSize(deltaHeight, this.minHeight, this.vHeight, this.gridSize);
|
const height = getSize(this.minHeight, this.vHeight, this.gridSize);
|
||||||
const width = getSize(deltaWidth, this.minWidth, this.vWidth, this.gridSize);
|
const width = getSize(this.minWidth, this.vWidth, this.gridSize);
|
||||||
|
|
||||||
const dX = left && width !== this.width ? -1 * (width - this.width) : 0;
|
const dX = left && width !== this.width ? -1 * (width - this.width) : 0;
|
||||||
const dY = top && height !== this.height ? -1 * (height - this.height): 0;
|
const dY = top && height !== this.height ? -1 * (height - this.height): 0;
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
const direction = this.dir;
|
||||||
|
|
||||||
this.$emit('resize', { height, width, dX, dY });
|
this.$emit('resize', { height, width, dX, dY, x, y, direction });
|
||||||
this.dHeight = dHeight;
|
this.dHeight = dHeight;
|
||||||
this.dWidth = dWidth;
|
this.dWidth = dWidth;
|
||||||
},
|
},
|
||||||
mouseUp(event: Event) {
|
mouseUp(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.$emit('resizeend');
|
this.$emit('resizeend');
|
||||||
|
@ -162,7 +179,7 @@ export default Vue.extend({
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.resize {
|
.resize {
|
||||||
position: absolute;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
@ -170,7 +187,7 @@ export default Vue.extend({
|
||||||
|
|
||||||
.resizer {
|
.resizer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
|
@ -211,7 +228,6 @@ export default Vue.extend({
|
||||||
top: -3px;
|
top: -3px;
|
||||||
left: -3px;
|
left: -3px;
|
||||||
cursor: nw-resize;
|
cursor: nw-resize;
|
||||||
z-index: 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.topRight {
|
.topRight {
|
||||||
|
@ -220,7 +236,6 @@ export default Vue.extend({
|
||||||
top: -3px;
|
top: -3px;
|
||||||
right: -3px;
|
right: -3px;
|
||||||
cursor: ne-resize;
|
cursor: ne-resize;
|
||||||
z-index: 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottomLeft {
|
.bottomLeft {
|
||||||
|
@ -229,7 +244,6 @@ export default Vue.extend({
|
||||||
bottom: -3px;
|
bottom: -3px;
|
||||||
left: -3px;
|
left: -3px;
|
||||||
cursor: sw-resize;
|
cursor: sw-resize;
|
||||||
z-index: 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottomRight {
|
.bottomRight {
|
||||||
|
@ -238,6 +252,5 @@ export default Vue.extend({
|
||||||
bottom: -3px;
|
bottom: -3px;
|
||||||
right: -3px;
|
right: -3px;
|
||||||
cursor: se-resize;
|
cursor: se-resize;
|
||||||
z-index: 3;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,3 @@
|
||||||
|
import ResizeWrapper from './ResizeWrapper.vue';
|
||||||
|
|
||||||
|
export default ResizeWrapper;
|
|
@ -4,7 +4,7 @@
|
||||||
:style="styles"
|
:style="styles"
|
||||||
@keydown.prevent
|
@keydown.prevent
|
||||||
>
|
>
|
||||||
<resize
|
<n8n-resize-wrapper
|
||||||
:isResizingEnabled="!readOnly"
|
:isResizingEnabled="!readOnly"
|
||||||
:height="height"
|
:height="height"
|
||||||
:width="width"
|
:width="width"
|
||||||
|
@ -60,14 +60,14 @@
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</resize>
|
</n8n-resize-wrapper>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import N8nInput from '../N8nInput';
|
import N8nInput from '../N8nInput';
|
||||||
import N8nMarkdown from '../N8nMarkdown';
|
import N8nMarkdown from '../N8nMarkdown';
|
||||||
import Resize from './Resize.vue';
|
import N8nResizeWrapper from '../N8nResizeWrapper';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import Locale from '../../mixins/locale';
|
import Locale from '../../mixins/locale';
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
@ -121,7 +121,7 @@ export default mixins(Locale).extend({
|
||||||
components: {
|
components: {
|
||||||
N8nInput,
|
N8nInput,
|
||||||
N8nMarkdown,
|
N8nMarkdown,
|
||||||
Resize,
|
N8nResizeWrapper,
|
||||||
N8nText,
|
N8nText,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import N8nTree from '../components/N8nTree';
|
||||||
import N8nUserInfo from '../components/N8nUserInfo';
|
import N8nUserInfo from '../components/N8nUserInfo';
|
||||||
import N8nUserSelect from '../components/N8nUserSelect';
|
import N8nUserSelect from '../components/N8nUserSelect';
|
||||||
import N8nUsersList from '../components/N8nUsersList';
|
import N8nUsersList from '../components/N8nUsersList';
|
||||||
|
import N8nResizeWrapper from '../components/N8nResizeWrapper';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
install: (app: typeof Vue, options?: {}) => {
|
install: (app: typeof Vue, options?: {}) => {
|
||||||
|
@ -84,5 +85,6 @@ export default {
|
||||||
app.component('n8n-tree', N8nTree);
|
app.component('n8n-tree', N8nTree);
|
||||||
app.component('n8n-users-list', N8nUsersList);
|
app.component('n8n-users-list', N8nUsersList);
|
||||||
app.component('n8n-user-select', N8nUserSelect);
|
app.component('n8n-user-select', N8nUserSelect);
|
||||||
|
app.component('n8n-resize-wrapper', N8nResizeWrapper);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -934,6 +934,7 @@ export interface IUiState {
|
||||||
modals: {
|
modals: {
|
||||||
[key: string]: IModalState;
|
[key: string]: IModalState;
|
||||||
};
|
};
|
||||||
|
mainPanelDimensions: {[key: string]: {[key: string]: number}};
|
||||||
isPageLoading: boolean;
|
isPageLoading: boolean;
|
||||||
currentView: string;
|
currentView: string;
|
||||||
ndv: {
|
ndv: {
|
||||||
|
|
|
@ -7,31 +7,61 @@
|
||||||
<slot name="output"></slot>
|
<slot name="output"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.mainPanel" :style="mainPanelStyles">
|
<div :class="$style.mainPanel" :style="mainPanelStyles">
|
||||||
<div :class="$style.dragButtonContainer" @click="close">
|
<n8n-resize-wrapper
|
||||||
<PanelDragButton
|
:isResizingEnabled="currentNodePaneType !== 'unknown'"
|
||||||
:class="{ [$style.draggable]: true, [$style.visible]: isDragging }"
|
:width="relativeWidthToPx(mainPanelDimensions.relativeWidth)"
|
||||||
v-if="!hideInputAndOutput && isDraggable"
|
:minWidth="MIN_PANEL_WIDTH"
|
||||||
:canMoveLeft="canMoveLeft"
|
:gridSize="20"
|
||||||
:canMoveRight="canMoveRight"
|
@resize="onResize"
|
||||||
@dragstart="onDragStart"
|
@resizestart="onResizeStart"
|
||||||
@drag="onDrag"
|
@resizeend="onResizeEnd"
|
||||||
@dragend="onDragEnd"
|
:supportedDirections="supportedResizeDirections"
|
||||||
/>
|
>
|
||||||
</div>
|
<div :class="$style.dragButtonContainer">
|
||||||
<slot name="main"></slot>
|
<PanelDragButton
|
||||||
|
:class="{ [$style.draggable]: true, [$style.visible]: isDragging }"
|
||||||
|
:canMoveLeft="canMoveLeft"
|
||||||
|
:canMoveRight="canMoveRight"
|
||||||
|
v-if="!hideInputAndOutput && isDraggable"
|
||||||
|
@dragstart="onDragStart"
|
||||||
|
@drag="onDrag"
|
||||||
|
@dragend="onDragEnd"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div :class="{ [$style.mainPanelInner]: true, [$style.dragging]: isDragging }">
|
||||||
|
<slot name="main" />
|
||||||
|
</div>
|
||||||
|
</n8n-resize-wrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue, { PropType } from 'vue';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
|
import { INodeTypeDescription } from 'n8n-workflow';
|
||||||
import PanelDragButton from './PanelDragButton.vue';
|
import PanelDragButton from './PanelDragButton.vue';
|
||||||
|
|
||||||
const MAIN_PANEL_WIDTH = 360;
|
import {
|
||||||
|
LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH,
|
||||||
|
MAIN_NODE_PANEL_WIDTH,
|
||||||
|
} from '@/constants';
|
||||||
|
|
||||||
|
|
||||||
const SIDE_MARGIN = 24;
|
const SIDE_MARGIN = 24;
|
||||||
const FIXED_PANEL_WIDTH = 320;
|
const SIDE_PANELS_MARGIN = 80;
|
||||||
const FIXED_PANEL_WIDTH_LARGE = 420;
|
const MIN_PANEL_WIDTH = 280;
|
||||||
const MINIMUM_INPUT_PANEL_WIDTH = 320;
|
const PANEL_WIDTH = 320;
|
||||||
|
const PANEL_WIDTH_LARGE = 420;
|
||||||
|
|
||||||
|
const initialMainPanelWidth:{ [key: string]: number } = {
|
||||||
|
regular: MAIN_NODE_PANEL_WIDTH,
|
||||||
|
dragless: MAIN_NODE_PANEL_WIDTH,
|
||||||
|
unknown: MAIN_NODE_PANEL_WIDTH,
|
||||||
|
inputless: MAIN_NODE_PANEL_WIDTH,
|
||||||
|
wide: MAIN_NODE_PANEL_WIDTH * 2,
|
||||||
|
};
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'NDVDraggablePanels',
|
name: 'NDVDraggablePanels',
|
||||||
|
@ -48,124 +78,271 @@ export default Vue.extend({
|
||||||
position: {
|
position: {
|
||||||
type: Number,
|
type: Number,
|
||||||
},
|
},
|
||||||
|
nodeType: {
|
||||||
|
type: Object as PropType<INodeTypeDescription>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data(): { windowWidth: number, isDragging: boolean, MIN_PANEL_WIDTH: number} {
|
||||||
return {
|
return {
|
||||||
windowWidth: 0,
|
windowWidth: 1,
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
|
MIN_PANEL_WIDTH,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.setTotalWidth();
|
this.setTotalWidth();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Only set(or restore) initial position if `mainPanelDimensions`
|
||||||
|
is at the default state({relativeLeft:1, relativeRight: 1, relativeWidth: 1}) to make sure we use store values if they are set
|
||||||
|
*/
|
||||||
|
if(this.mainPanelDimensions.relativeLeft === 1 && this.mainPanelDimensions.relativeRight === 1) {
|
||||||
|
this.setMainPanelWidth();
|
||||||
|
this.setPositions(this.getInitialLeftPosition(this.mainPanelDimensions.relativeWidth));
|
||||||
|
this.restorePositionData();
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', this.setTotalWidth);
|
window.addEventListener('resize', this.setTotalWidth);
|
||||||
this.$emit('init', { position: this.getRelativePosition() });
|
this.$emit('init', { position: this.mainPanelDimensions.relativeLeft });
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
window.removeEventListener('resize', this.setTotalWidth);
|
window.removeEventListener('resize', this.setTotalWidth);
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fixedPanelWidth() {
|
mainPanelDimensions(): {
|
||||||
if (this.windowWidth > 1700) {
|
relativeWidth: number,
|
||||||
return FIXED_PANEL_WIDTH_LARGE;
|
relativeLeft: number,
|
||||||
}
|
relativeRight: number
|
||||||
|
} {
|
||||||
return FIXED_PANEL_WIDTH;
|
return this.$store.getters['ui/mainPanelDimensions'](this.currentNodePaneType);
|
||||||
},
|
},
|
||||||
mainPanelPosition(): number {
|
supportedResizeDirections() {
|
||||||
if (typeof this.position === 'number') {
|
const supportedDirections = ['right'];
|
||||||
return this.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.isDraggable) {
|
if(this.isDraggable) supportedDirections.push('left');
|
||||||
return this.fixedPanelWidth + MAIN_PANEL_WIDTH / 2 + SIDE_MARGIN;
|
return supportedDirections;
|
||||||
}
|
},
|
||||||
|
currentNodePaneType() {
|
||||||
const relativePosition = this.$store.getters['ui/mainPanelPosition'] as number;
|
if(!this.hasInputSlot) return 'inputless';
|
||||||
|
if(!this.isDraggable) return 'dragless';
|
||||||
return relativePosition * this.windowWidth;
|
if(this.nodeType === null) return 'unknown';
|
||||||
|
return get(this, 'nodeType.parameterPane') || 'regular';
|
||||||
|
},
|
||||||
|
hasInputSlot() {
|
||||||
|
return this.$slots.input !== undefined;
|
||||||
},
|
},
|
||||||
inputPanelMargin(): number {
|
inputPanelMargin(): number {
|
||||||
return !this.isDraggable? 0 : 80;
|
return this.pxToRelativeWidth(SIDE_PANELS_MARGIN);
|
||||||
|
},
|
||||||
|
minWindowWidth() {
|
||||||
|
return 2 * (SIDE_MARGIN + SIDE_PANELS_MARGIN) + MIN_PANEL_WIDTH;
|
||||||
},
|
},
|
||||||
minimumLeftPosition(): number {
|
minimumLeftPosition(): number {
|
||||||
return SIDE_MARGIN + this.inputPanelMargin;
|
if(this.windowWidth < this.minWindowWidth) return this.pxToRelativeWidth(1);
|
||||||
|
|
||||||
|
if(!this.hasInputSlot) return this.pxToRelativeWidth(SIDE_MARGIN);
|
||||||
|
return this.pxToRelativeWidth(SIDE_MARGIN + 20) + this.inputPanelMargin;
|
||||||
},
|
},
|
||||||
maximumRightPosition(): number {
|
maximumRightPosition(): number {
|
||||||
return this.windowWidth - MAIN_PANEL_WIDTH - this.minimumLeftPosition;
|
if(this.windowWidth < this.minWindowWidth) return this.pxToRelativeWidth(1);
|
||||||
},
|
|
||||||
mainPanelFinalPositionPx(): number {
|
|
||||||
const padding = this.minimumLeftPosition;
|
|
||||||
let pos = this.mainPanelPosition + MAIN_PANEL_WIDTH / 2;
|
|
||||||
pos = Math.max(padding, pos - MAIN_PANEL_WIDTH);
|
|
||||||
pos = Math.min(pos, this.maximumRightPosition);
|
|
||||||
|
|
||||||
return pos;
|
return this.pxToRelativeWidth(SIDE_MARGIN + 20) + this.inputPanelMargin;
|
||||||
},
|
},
|
||||||
canMoveLeft(): boolean {
|
canMoveLeft(): boolean {
|
||||||
return this.mainPanelFinalPositionPx > this.minimumLeftPosition;
|
return this.mainPanelDimensions.relativeLeft > this.minimumLeftPosition;
|
||||||
},
|
},
|
||||||
canMoveRight(): boolean {
|
canMoveRight(): boolean {
|
||||||
return this.mainPanelFinalPositionPx < this.maximumRightPosition;
|
return this.mainPanelDimensions.relativeRight > this.maximumRightPosition;
|
||||||
},
|
},
|
||||||
mainPanelStyles(): { left: string } {
|
mainPanelStyles(): { left: string, right: string } {
|
||||||
return {
|
return {
|
||||||
left: `${this.mainPanelFinalPositionPx}px`,
|
'left': `${this.relativeWidthToPx(this.mainPanelDimensions.relativeLeft)}px`,
|
||||||
|
'right': `${this.relativeWidthToPx(this.mainPanelDimensions.relativeRight)}px`,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inputPanelStyles(): { width: string } {
|
inputPanelStyles():{ right: string } {
|
||||||
if (!this.isDraggable) {
|
return {
|
||||||
return {
|
right: `${this.relativeWidthToPx(this.calculatedPositions.inputPanelRelativeRight)}px`,
|
||||||
width: `${this.fixedPanelWidth}px`,
|
};
|
||||||
};
|
},
|
||||||
|
outputPanelStyles(): { left: string, transform: string} {
|
||||||
|
return {
|
||||||
|
left: `${this.relativeWidthToPx(this.calculatedPositions.outputPanelRelativeLeft)}px`,
|
||||||
|
transform: `translateX(-${this.relativeWidthToPx(this.outputPanelRelativeTranslate)}px)`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
calculatedPositions():{ inputPanelRelativeRight: number, outputPanelRelativeLeft: number } {
|
||||||
|
const hasInput = this.$slots.input !== undefined;
|
||||||
|
const outputPanelRelativeLeft = this.mainPanelDimensions.relativeLeft + this.mainPanelDimensions.relativeWidth;
|
||||||
|
|
||||||
|
const inputPanelRelativeRight = hasInput
|
||||||
|
? 1 - outputPanelRelativeLeft + this.mainPanelDimensions.relativeWidth
|
||||||
|
: (1 - this.pxToRelativeWidth(SIDE_MARGIN));
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputPanelRelativeRight,
|
||||||
|
outputPanelRelativeLeft,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
outputPanelRelativeTranslate():number {
|
||||||
|
const panelMinLeft = 1 - this.pxToRelativeWidth(MIN_PANEL_WIDTH + SIDE_MARGIN);
|
||||||
|
const currentRelativeLeftDelta = this.calculatedPositions.outputPanelRelativeLeft - panelMinLeft;
|
||||||
|
return currentRelativeLeftDelta > 0 ? currentRelativeLeftDelta : 0;
|
||||||
|
},
|
||||||
|
hasDoubleWidth() {
|
||||||
|
return get(this, 'nodeType.parameterPane') === 'wide';
|
||||||
|
},
|
||||||
|
fixedPanelWidth(): number {
|
||||||
|
const multiplier = this.hasDoubleWidth ? 2 : 1;
|
||||||
|
|
||||||
|
if (this.windowWidth > 1700) {
|
||||||
|
return PANEL_WIDTH_LARGE * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = this.mainPanelPosition - MAIN_PANEL_WIDTH / 2 - SIDE_MARGIN;
|
return PANEL_WIDTH * multiplier;
|
||||||
width = Math.min(
|
|
||||||
width,
|
|
||||||
this.windowWidth - SIDE_MARGIN * 2 - this.inputPanelMargin - MAIN_PANEL_WIDTH,
|
|
||||||
);
|
|
||||||
width = Math.max(320, width);
|
|
||||||
return {
|
|
||||||
width: `${width}px`,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
outputPanelStyles(): { width: string } {
|
isBelowMinWidthMainPanel(): boolean {
|
||||||
let width = this.windowWidth - this.mainPanelPosition - MAIN_PANEL_WIDTH / 2 - SIDE_MARGIN;
|
const minRelativeWidth = this.pxToRelativeWidth(MIN_PANEL_WIDTH);
|
||||||
width = Math.min(
|
return this.mainPanelDimensions.relativeWidth < minRelativeWidth;
|
||||||
width,
|
},
|
||||||
this.windowWidth - SIDE_MARGIN * 2 - this.inputPanelMargin - MAIN_PANEL_WIDTH,
|
},
|
||||||
);
|
watch: {
|
||||||
width = Math.max(MINIMUM_INPUT_PANEL_WIDTH, width);
|
windowWidth(windowWidth) {
|
||||||
return {
|
const minRelativeWidth = this.pxToRelativeWidth(MIN_PANEL_WIDTH);
|
||||||
width: `${width}px`,
|
// Prevent the panel resizing below MIN_PANEL_WIDTH whhile maintaing position
|
||||||
};
|
if(this.isBelowMinWidthMainPanel) {
|
||||||
|
this.setMainPanelWidth(minRelativeWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBelowMinLeft = this.minimumLeftPosition > this.mainPanelDimensions.relativeLeft;
|
||||||
|
const isMaxRight = this.maximumRightPosition > this.mainPanelDimensions.relativeRight;
|
||||||
|
|
||||||
|
// When user is resizing from non-supported view(sub ~488px) we need to refit the panels
|
||||||
|
if((windowWidth > this.minWindowWidth) && isBelowMinLeft && isMaxRight) {
|
||||||
|
this.setMainPanelWidth(minRelativeWidth);
|
||||||
|
this.setPositions(this.getInitialLeftPosition(this.mainPanelDimensions.relativeWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPositions(this.mainPanelDimensions.relativeLeft);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getRelativePosition() {
|
getInitialLeftPosition(width: number) {
|
||||||
const current = this.mainPanelFinalPositionPx + MAIN_PANEL_WIDTH / 2 - this.windowWidth / 2;
|
if(this.currentNodePaneType === 'dragless') return this.pxToRelativeWidth(SIDE_MARGIN + 1 + this.fixedPanelWidth);
|
||||||
|
|
||||||
const pos = Math.floor(
|
return this.hasInputSlot
|
||||||
(current / ((this.maximumRightPosition - this.minimumLeftPosition) / 2)) * 100,
|
? 0.5 - (width / 2)
|
||||||
|
: this.minimumLeftPosition;
|
||||||
|
},
|
||||||
|
setMainPanelWidth(relativeWidth?: number) {
|
||||||
|
const mainPanelRelativeWidth = relativeWidth || this.pxToRelativeWidth(initialMainPanelWidth[this.currentNodePaneType]);
|
||||||
|
|
||||||
|
this.$store.commit('ui/setMainPanelDimensions', {
|
||||||
|
panelType: this.currentNodePaneType,
|
||||||
|
dimensions: {
|
||||||
|
relativeWidth: mainPanelRelativeWidth,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setPositions(relativeLeft: number) {
|
||||||
|
const mainPanelRelativeLeft = relativeLeft || 1 - this.calculatedPositions.inputPanelRelativeRight;
|
||||||
|
const mainPanelRelativeRight = 1 - mainPanelRelativeLeft - this.mainPanelDimensions.relativeWidth;
|
||||||
|
|
||||||
|
const isMaxRight = this.maximumRightPosition > mainPanelRelativeRight;
|
||||||
|
const isMinLeft = this.minimumLeftPosition > mainPanelRelativeLeft;
|
||||||
|
const isInputless = this.currentNodePaneType === 'inputless';
|
||||||
|
|
||||||
|
if(isMinLeft) {
|
||||||
|
this.$store.commit('ui/setMainPanelDimensions', {
|
||||||
|
panelType: this.currentNodePaneType,
|
||||||
|
dimensions: {
|
||||||
|
relativeLeft: this.minimumLeftPosition,
|
||||||
|
relativeRight: 1 - this.mainPanelDimensions.relativeWidth - this.minimumLeftPosition,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isMaxRight) {
|
||||||
|
this.$store.commit('ui/setMainPanelDimensions', {
|
||||||
|
panelType: this.currentNodePaneType,
|
||||||
|
dimensions: {
|
||||||
|
relativeLeft: 1 - this.mainPanelDimensions.relativeWidth - this.maximumRightPosition,
|
||||||
|
relativeRight: this.maximumRightPosition,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit('ui/setMainPanelDimensions', {
|
||||||
|
panelType: this.currentNodePaneType,
|
||||||
|
dimensions: {
|
||||||
|
relativeLeft: isInputless ? this.minimumLeftPosition : mainPanelRelativeLeft,
|
||||||
|
relativeRight: mainPanelRelativeRight,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
pxToRelativeWidth(px: number) {
|
||||||
|
return px / this.windowWidth;
|
||||||
|
},
|
||||||
|
relativeWidthToPx(relativeWidth: number) {
|
||||||
|
return relativeWidth * this.windowWidth;
|
||||||
|
},
|
||||||
|
onResizeStart() {
|
||||||
|
this.setTotalWidth();
|
||||||
|
},
|
||||||
|
onResizeEnd() {
|
||||||
|
this.storePositionData();
|
||||||
|
},
|
||||||
|
onResize({ direction, x, width }: { direction: string, x: number, width: number}) {
|
||||||
|
const relativeDistance = this.pxToRelativeWidth(x);
|
||||||
|
const relativeWidth = this.pxToRelativeWidth(width);
|
||||||
|
|
||||||
|
if(direction === "left" && relativeDistance <= this.minimumLeftPosition) return;
|
||||||
|
if(direction === "right" && (1 - relativeDistance) <= this.maximumRightPosition) return;
|
||||||
|
if(width <= MIN_PANEL_WIDTH) return;
|
||||||
|
|
||||||
|
this.setMainPanelWidth(relativeWidth);
|
||||||
|
this.setPositions(direction === 'left'
|
||||||
|
? relativeDistance
|
||||||
|
: this.mainPanelDimensions.relativeLeft,
|
||||||
);
|
);
|
||||||
return pos;
|
},
|
||||||
|
restorePositionData() {
|
||||||
|
const storedPanelWidthData = window.localStorage.getItem(`${LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH}_${this.currentNodePaneType}`);
|
||||||
|
|
||||||
|
if(storedPanelWidthData) {
|
||||||
|
const parsedWidth = parseFloat(storedPanelWidthData);
|
||||||
|
this.setMainPanelWidth(parsedWidth);
|
||||||
|
const initialPosition = this.getInitialLeftPosition(parsedWidth);
|
||||||
|
|
||||||
|
this.setPositions(initialPosition);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
storePositionData() {
|
||||||
|
window.localStorage.setItem(`${LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH}_${this.currentNodePaneType}`, this.mainPanelDimensions.relativeWidth.toString());
|
||||||
},
|
},
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
this.isDragging = true;
|
this.isDragging = true;
|
||||||
this.$emit('dragstart', { position: this.getRelativePosition() });
|
this.$emit('dragstart', { position: this.mainPanelDimensions.relativeLeft });
|
||||||
},
|
},
|
||||||
onDrag(e: {x: number, y: number}) {
|
onDrag(e: {x: number, y: number}) {
|
||||||
const relativePosition = e.x / this.windowWidth;
|
const relativeLeft = this.pxToRelativeWidth(e.x) - (this.mainPanelDimensions.relativeWidth / 2);
|
||||||
this.$store.commit('ui/setMainPanelRelativePosition', relativePosition);
|
|
||||||
|
this.setPositions(relativeLeft);
|
||||||
},
|
},
|
||||||
onDragEnd() {
|
onDragEnd() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
this.$emit('dragend', {
|
this.$emit('dragend', {
|
||||||
windowWidth: this.windowWidth,
|
windowWidth: this.windowWidth,
|
||||||
position: this.getRelativePosition(),
|
position: this.mainPanelDimensions.relativeLeft,
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
|
this.storePositionData();
|
||||||
},
|
},
|
||||||
setTotalWidth() {
|
setTotalWidth() {
|
||||||
this.windowWidth = window.innerWidth;
|
this.windowWidth = window.innerWidth;
|
||||||
|
@ -178,14 +355,13 @@ export default Vue.extend({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
$--main-panel-width: 360px;
|
|
||||||
|
|
||||||
.dataPanel {
|
.dataPanel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: calc(100% - 2 * var(--spacing-l));
|
height: calc(100% - 2 * var(--spacing-l));
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--spacing-l);
|
top: var(--spacing-l);
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
min-width: 280px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputPanel {
|
.inputPanel {
|
||||||
|
@ -200,7 +376,6 @@ $--main-panel-width: 360px;
|
||||||
.outputPanel {
|
.outputPanel {
|
||||||
composes: dataPanel;
|
composes: dataPanel;
|
||||||
right: var(--spacing-l);
|
right: var(--spacing-l);
|
||||||
width: $--main-panel-width;
|
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
border-radius: 0 var(--border-radius-large) var(--border-radius-large) 0;
|
border-radius: 0 var(--border-radius-large) var(--border-radius-large) 0;
|
||||||
|
@ -218,18 +393,35 @@ $--main-panel-width: 360px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mainPanelInner {
|
||||||
|
height: 100%;
|
||||||
|
border: var(--border-base);
|
||||||
|
border-radius: var(--border-radius-large);
|
||||||
|
box-shadow: 0 4px 16px rgb(50 61 85 / 10%);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
box-shadow: 0px 6px 16px rgba(255, 74, 51, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.draggable {
|
.draggable {
|
||||||
position: absolute;
|
|
||||||
left: 40%;
|
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dragButtonContainer {
|
.dragButtonContainer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -12px;
|
top: -12px;
|
||||||
width: $--main-panel-width;
|
width: 100%;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
.draggable {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
&:hover .draggable {
|
&:hover .draggable {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
:hideInputAndOutput="activeNodeType === null"
|
:hideInputAndOutput="activeNodeType === null"
|
||||||
:position="isTriggerNode && !showTriggerPanel ? 0 : undefined"
|
:position="isTriggerNode && !showTriggerPanel ? 0 : undefined"
|
||||||
:isDraggable="!isTriggerNode"
|
:isDraggable="!isTriggerNode"
|
||||||
|
:nodeType="activeNodeType"
|
||||||
@close="close"
|
@close="close"
|
||||||
@init="onPanelsInit"
|
@init="onPanelsInit"
|
||||||
@dragstart="onDragStart"
|
@dragstart="onDragStart"
|
||||||
|
@ -82,6 +83,7 @@
|
||||||
:eventBus="settingsEventBus"
|
:eventBus="settingsEventBus"
|
||||||
:dragging="isDragging"
|
:dragging="isDragging"
|
||||||
:sessionId="sessionId"
|
:sessionId="sessionId"
|
||||||
|
:nodeType="activeNodeType"
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
@execute="onNodeExecute"
|
@execute="onNodeExecute"
|
||||||
@activate="onWorkflowActivate"
|
@activate="onWorkflowActivate"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{'node-settings': true, 'dragging': dragging}" @keydown.stop>
|
<div :class="{
|
||||||
|
'node-settings': true, 'dragging': dragging }" @keydown.stop>
|
||||||
<div :class="$style.header">
|
<div :class="$style.header">
|
||||||
<div class="header-side-menu">
|
<div class="header-side-menu">
|
||||||
<NodeTitle class="node-name" :value="node && node.name" :nodeType="nodeType" @input="nameChanged" :readOnly="isReadOnly"></NodeTitle>
|
<NodeTitle class="node-name" :value="node && node.name" :nodeType="nodeType" @input="nameChanged" :readOnly="isReadOnly"></NodeTitle>
|
||||||
|
@ -96,7 +97,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue, { PropType } from 'vue';
|
||||||
import {
|
import {
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
|
@ -113,7 +114,8 @@ import {
|
||||||
import {
|
import {
|
||||||
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
||||||
CUSTOM_NODES_DOCS_URL,
|
CUSTOM_NODES_DOCS_URL,
|
||||||
} from '../constants';
|
MAIN_NODE_PANEL_WIDTH,
|
||||||
|
} from '@/constants';
|
||||||
|
|
||||||
import NodeTitle from '@/components/NodeTitle.vue';
|
import NodeTitle from '@/components/NodeTitle.vue';
|
||||||
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
||||||
|
@ -148,13 +150,6 @@ export default mixins(
|
||||||
NodeExecuteButton,
|
NodeExecuteButton,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
nodeType (): INodeTypeDescription | null {
|
|
||||||
if (this.node) {
|
|
||||||
return this.$store.getters['nodeTypes/getNodeType'](this.node.type, this.node.typeVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
nodeTypeName(): string {
|
nodeTypeName(): string {
|
||||||
if (this.nodeType) {
|
if (this.nodeType) {
|
||||||
const shortNodeType = this.$locale.shortNodeType(this.nodeType.name);
|
const shortNodeType = this.$locale.shortNodeType(this.nodeType.name);
|
||||||
|
@ -224,6 +219,9 @@ export default mixins(
|
||||||
sessionId: {
|
sessionId: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
nodeType: {
|
||||||
|
type: Object as PropType<INodeTypeDescription>,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -336,6 +334,7 @@ export default mixins(
|
||||||
] as INodeProperties[],
|
] as INodeProperties[],
|
||||||
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
COMMUNITY_NODES_INSTALLATION_DOCS_URL,
|
||||||
CUSTOM_NODES_DOCS_URL,
|
CUSTOM_NODES_DOCS_URL,
|
||||||
|
MAIN_NODE_PANEL_WIDTH,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -641,13 +640,9 @@ export default mixins(
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.node-settings {
|
.node-settings {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-width: 360px;
|
|
||||||
max-width: 360px;
|
|
||||||
background-color: var(--color-background-xlight);
|
background-color: var(--color-background-xlight);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: var(--border-base);
|
width: 100%;
|
||||||
border-radius: var(--border-radius-large);
|
|
||||||
box-shadow: 0 4px 16px rgb(50 61 85 / 10%);
|
|
||||||
|
|
||||||
.no-parameters {
|
.no-parameters {
|
||||||
margin-top: var(--spacing-xs);
|
margin-top: var(--spacing-xs);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Draggable type="panel-resize" @drag="onDrag" @dragstart="onDragStart" @dragend="onDragEnd">
|
<Draggable type="panel-resize" @drag="onDrag" @dragstart="onDragStart" @dragend="onDragEnd" :class="$style.dragContainer">
|
||||||
<template v-slot="{ isDragging }">
|
<template v-slot="{ isDragging }">
|
||||||
<div
|
<div
|
||||||
:class="{ [$style.dragButton]: true }"
|
:class="{ [$style.dragButton]: true }"
|
||||||
|
@ -63,6 +63,9 @@ export default mixins(dragging).extend({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
.dragContainer {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
.dragButton {
|
.dragButton {
|
||||||
background-color: var(--color-background-base);
|
background-color: var(--color-background-base);
|
||||||
width: 64px;
|
width: 64px;
|
||||||
|
@ -74,6 +77,8 @@ export default mixins(dragging).extend({
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.leftArrow, .rightArrow {
|
.leftArrow, .rightArrow {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="resource-locator">
|
<div class="resource-locator" ref="container">
|
||||||
<resource-locator-dropdown
|
<resource-locator-dropdown
|
||||||
:value="value ? value.value: ''"
|
:value="value ? value.value: ''"
|
||||||
:show="showResourceDropdown"
|
:show="showResourceDropdown"
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
:filter="searchFilter"
|
:filter="searchFilter"
|
||||||
:hasMore="currentQueryHasMore"
|
:hasMore="currentQueryHasMore"
|
||||||
:errorView="currentQueryError"
|
:errorView="currentQueryError"
|
||||||
|
:width="width"
|
||||||
@input="onListItemSelected"
|
@input="onListItemSelected"
|
||||||
@hide="onDropdownHide"
|
@hide="onDropdownHide"
|
||||||
@filter="onSearchFilter"
|
@filter="onSearchFilter"
|
||||||
|
@ -251,10 +252,12 @@ export default mixins(debounceHelper, workflowHelpers, nodeHelpers).extend({
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
mainPanelMutationSubscription: () => {},
|
||||||
showResourceDropdown: false,
|
showResourceDropdown: false,
|
||||||
searchFilter: '',
|
searchFilter: '',
|
||||||
cachedResponses: {} as { [key: string]: IResourceLocatorQuery },
|
cachedResponses: {} as { [key: string]: IResourceLocatorQuery },
|
||||||
hasCompletedASearch: false,
|
hasCompletedASearch: false,
|
||||||
|
width: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -423,8 +426,23 @@ export default mixins(debounceHelper, workflowHelpers, nodeHelpers).extend({
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$on('refreshList', this.refreshList);
|
this.$on('refreshList', this.refreshList);
|
||||||
|
this.setWidth();
|
||||||
|
window.addEventListener('resize', this.setWidth);
|
||||||
|
this.mainPanelMutationSubscription = this.$store.subscribe(this.setWidthOnMainPanelResize);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// Unsubscribe
|
||||||
|
this.mainPanelMutationSubscription();
|
||||||
|
window.removeEventListener('resize', this.setWidth);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setWidth() {
|
||||||
|
this.width = (this.$refs.container as HTMLElement).offsetWidth;
|
||||||
|
},
|
||||||
|
setWidthOnMainPanelResize(mutation: { type: string }) {
|
||||||
|
// Update the width when main panel dimension change
|
||||||
|
if(mutation.type === 'ui/setMainPanelDimensions') this.setWidth();
|
||||||
|
},
|
||||||
getLinkAlt(entity: string) {
|
getLinkAlt(entity: string) {
|
||||||
if (this.selectedMode === 'list' && entity) {
|
if (this.selectedMode === 'list' && entity) {
|
||||||
return this.$locale.baseText('resourceLocator.openSpecificResource', { interpolate: { entity, appName: this.appName } });
|
return this.$locale.baseText('resourceLocator.openSpecificResource', { interpolate: { entity, appName: this.appName } });
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<n8n-popover
|
<n8n-popover
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
width="318"
|
:width="width"
|
||||||
:popper-class="$style.popover"
|
:popper-class="$style.popover"
|
||||||
:value="show"
|
:value="show"
|
||||||
trigger="manual"
|
trigger="manual"
|
||||||
|
@ -90,6 +90,9 @@ export default Vue.extend({
|
||||||
filterRequired: {
|
filterRequired: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -254,7 +257,7 @@ export default Vue.extend({
|
||||||
--input-font-size: var(--font-size-2xs);
|
--input-font-size: var(--font-size-2xs);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 316px;
|
width: 100%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,3 +118,7 @@ export function isValueExpression (parameter: INodeProperties, paramValue: NodeP
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertRemToPixels(rem: string) {
|
||||||
|
return parseInt(rem, 10) * parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
}
|
||||||
|
|
|
@ -233,6 +233,7 @@ export const LOCAL_STORAGE_ACTIVATION_FLAG = 'N8N_HIDE_ACTIVATION_ALERT';
|
||||||
export const LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG = 'N8N_PIN_DATA_DISCOVERY_NDV';
|
export const LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG = 'N8N_PIN_DATA_DISCOVERY_NDV';
|
||||||
export const LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG = 'N8N_PIN_DATA_DISCOVERY_CANVAS';
|
export const LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG = 'N8N_PIN_DATA_DISCOVERY_CANVAS';
|
||||||
export const LOCAL_STORAGE_MAPPING_FLAG = 'N8N_MAPPING_ONBOARDED';
|
export const LOCAL_STORAGE_MAPPING_FLAG = 'N8N_MAPPING_ONBOARDED';
|
||||||
|
export const LOCAL_STORAGE_MAIN_PANEL_RELATIVE_WIDTH = 'N8N_MAIN_PANEL_RELATIVE_WIDTH';
|
||||||
export const BASE_NODE_SURVEY_URL = 'https://n8n-community.typeform.com/to/BvmzxqYv#nodename=';
|
export const BASE_NODE_SURVEY_URL = 'https://n8n-community.typeform.com/to/BvmzxqYv#nodename=';
|
||||||
|
|
||||||
export const HIRING_BANNER = `
|
export const HIRING_BANNER = `
|
||||||
|
@ -317,3 +318,4 @@ export const DEFAULT_STICKY_WIDTH = 240;
|
||||||
export enum EnterpriseEditionFeature {
|
export enum EnterpriseEditionFeature {
|
||||||
Sharing = 'sharing',
|
Sharing = 'sharing',
|
||||||
}
|
}
|
||||||
|
export const MAIN_NODE_PANEL_WIDTH = 360;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
|
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
|
||||||
FAKE_DOOR_FEATURES,
|
FAKE_DOOR_FEATURES,
|
||||||
COMMUNITY_PACKAGE_MANAGE_ACTIONS,
|
COMMUNITY_PACKAGE_MANAGE_ACTIONS,
|
||||||
|
MAIN_NODE_PANEL_WIDTH,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { ActionContext, Module } from 'vuex';
|
import { ActionContext, Module } from 'vuex';
|
||||||
|
@ -109,6 +110,7 @@ const module: Module<IUiState, IRootState> = {
|
||||||
sidebarMenuCollapsed: true,
|
sidebarMenuCollapsed: true,
|
||||||
isPageLoading: true,
|
isPageLoading: true,
|
||||||
currentView: '',
|
currentView: '',
|
||||||
|
mainPanelDimensions: {},
|
||||||
ndv: {
|
ndv: {
|
||||||
sessionId: '',
|
sessionId: '',
|
||||||
input: {
|
input: {
|
||||||
|
@ -205,10 +207,22 @@ const module: Module<IUiState, IRootState> = {
|
||||||
draggableType: (state: IUiState) => state.draggable.type,
|
draggableType: (state: IUiState) => state.draggable.type,
|
||||||
draggableData: (state: IUiState) => state.draggable.data,
|
draggableData: (state: IUiState) => state.draggable.data,
|
||||||
canDraggableDrop: (state: IUiState) => state.draggable.canDrop,
|
canDraggableDrop: (state: IUiState) => state.draggable.canDrop,
|
||||||
|
mainPanelDimensions: (state: IUiState) => (panelType: string) => {
|
||||||
|
const defaults = { relativeRight: 1, relativeLeft: 1, relativeWidth: 1 };
|
||||||
|
|
||||||
|
return {...defaults, ...state.mainPanelDimensions[panelType]};
|
||||||
|
},
|
||||||
draggableStickyPos: (state: IUiState) => state.draggable.stickyPosition,
|
draggableStickyPos: (state: IUiState) => state.draggable.stickyPosition,
|
||||||
mappingTelemetry: (state: IUiState) => state.ndv.mappingTelemetry,
|
mappingTelemetry: (state: IUiState) => state.ndv.mappingTelemetry,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
setMainPanelDimensions: (state: IUiState, params: { panelType:string, dimensions: { relativeLeft?: number, relativeRight?: number, relativeWidth?: number }}) => {
|
||||||
|
Vue.set(
|
||||||
|
state.mainPanelDimensions,
|
||||||
|
params.panelType,
|
||||||
|
{...state.mainPanelDimensions[params.panelType], ...params.dimensions },
|
||||||
|
);
|
||||||
|
},
|
||||||
setMode: (state: IUiState, params: {name: string, mode: string}) => {
|
setMode: (state: IUiState, params: {name: string, mode: string}) => {
|
||||||
const { name, mode } = params;
|
const { name, mode } = params;
|
||||||
Vue.set(state.modals[name], 'mode', mode);
|
Vue.set(state.modals[name], 'mode', mode);
|
||||||
|
@ -259,9 +273,6 @@ const module: Module<IUiState, IRootState> = {
|
||||||
setOutputPanelEditModeValue: (state: IUiState, payload: string) => {
|
setOutputPanelEditModeValue: (state: IUiState, payload: string) => {
|
||||||
Vue.set(state.ndv.output.editMode, 'value', payload);
|
Vue.set(state.ndv.output.editMode, 'value', payload);
|
||||||
},
|
},
|
||||||
setMainPanelRelativePosition(state: IUiState, relativePosition: number) {
|
|
||||||
state.mainPanelPosition = relativePosition;
|
|
||||||
},
|
|
||||||
setMappableNDVInputFocus(state: IUiState, paramName: string) {
|
setMappableNDVInputFocus(state: IUiState, paramName: string) {
|
||||||
Vue.set(state.ndv, 'focusedMappableInput', paramName);
|
Vue.set(state.ndv, 'focusedMappableInput', paramName);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue