diff --git a/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.stories.ts b/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.stories.ts new file mode 100644 index 0000000000..fe344ed483 --- /dev/null +++ b/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.stories.ts @@ -0,0 +1,69 @@ +import { action } from '@storybook/addon-actions'; +import type { StoryFn } from '@storybook/vue3'; +import N8nResizeableSticky from './ResizeableSticky.vue'; + +export default { + title: 'Atoms/ResizeableSticky', + component: N8nResizeableSticky, + argTypes: { + content: { + control: { + control: 'text', + }, + }, + height: { + control: { + control: 'number', + }, + }, + minHeight: { + control: { + control: 'number', + }, + }, + minWidth: { + control: { + control: 'number', + }, + }, + readOnly: { + control: { + control: 'Boolean', + }, + }, + width: { + control: { + control: 'number', + }, + }, + }, +}; + +const methods = { + onInput: action('update:modelValue'), + onResize: action('resize'), + onResizeEnd: action('resizeend'), + onResizeStart: action('resizestart'), +}; + +const Template: StoryFn = (args, { argTypes }) => ({ + setup: () => ({ args }), + props: Object.keys(argTypes), + components: { + N8nResizeableSticky, + }, + template: + '', + methods, +}); + +export const ResizeableSticky = Template.bind({}); +ResizeableSticky.args = { + height: 160, + width: 150, + modelValue: + "## I'm a note \n**Double click** to edit me. [Guide](https://docs.n8n.io/workflows/sticky-notes/)", + minHeight: 80, + minWidth: 150, + readOnly: false, +}; diff --git a/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue b/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue new file mode 100644 index 0000000000..c70dfe89e8 --- /dev/null +++ b/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue @@ -0,0 +1,61 @@ + + + diff --git a/packages/design-system/src/components/N8nResizeableSticky/index.ts b/packages/design-system/src/components/N8nResizeableSticky/index.ts new file mode 100644 index 0000000000..a0346c10f5 --- /dev/null +++ b/packages/design-system/src/components/N8nResizeableSticky/index.ts @@ -0,0 +1,3 @@ +import ResizeableSticky from './ResizeableSticky.vue'; + +export default ResizeableSticky; diff --git a/packages/design-system/src/components/N8nSticky/Sticky.stories.ts b/packages/design-system/src/components/N8nSticky/Sticky.stories.ts index 6a4e05d830..1411f2c05e 100644 --- a/packages/design-system/src/components/N8nSticky/Sticky.stories.ts +++ b/packages/design-system/src/components/N8nSticky/Sticky.stories.ts @@ -61,9 +61,7 @@ export const Sticky = Template.bind({}); Sticky.args = { height: 160, width: 150, - content: - "## I'm a note \n**Double click** to edit me. [Guide](https://docs.n8n.io/workflows/sticky-notes/)", - defaultText: + modelValue: "## I'm a note \n**Double click** to edit me. [Guide](https://docs.n8n.io/workflows/sticky-notes/)", minHeight: 80, minWidth: 150, diff --git a/packages/design-system/src/components/N8nSticky/Sticky.vue b/packages/design-system/src/components/N8nSticky/Sticky.vue index b258171fda..c53fb012f9 100644 --- a/packages/design-system/src/components/N8nSticky/Sticky.vue +++ b/packages/design-system/src/components/N8nSticky/Sticky.vue @@ -9,52 +9,40 @@ :style="styles" @keydown.prevent > - + + +
-
- -
-
- -
-
- - - -
- + +
+
+ + + +
@@ -62,45 +50,17 @@ import { computed, ref, watch } from 'vue'; import N8nInput from '../N8nInput'; import N8nMarkdown from '../N8nMarkdown'; -import N8nResizeWrapper, { type ResizeData } from '../N8nResizeWrapper/ResizeWrapper.vue'; import N8nText from '../N8nText'; import { useI18n } from '../../composables/useI18n'; +import { defaultStickyProps } from './constants'; +import type { StickyProps } from './types'; -interface StickyProps { - modelValue?: string; - height?: number; - width?: number; - minHeight?: number; - minWidth?: number; - scale?: number; - gridSize?: number; - id?: string; - defaultText?: string; - editMode?: boolean; - readOnly?: boolean; - backgroundColor?: number | string; -} - -const props = withDefaults(defineProps(), { - height: 180, - width: 240, - minHeight: 80, - minWidth: 150, - scale: 1, - gridSize: 20, - id: '0', - editMode: false, - readOnly: false, - backgroundColor: 1, -}); +const props = withDefaults(defineProps(), defaultStickyProps); const emit = defineEmits<{ edit: [editing: boolean]; 'update:modelValue': [value: string]; 'markdown-click': [link: string, e: Event]; - resize: [values: ResizeData]; - resizestart: []; - resizeend: []; }>(); const { t } = useI18n(); @@ -115,6 +75,8 @@ const resWidth = computed((): number => { return props.width < props.minWidth ? props.minWidth : props.width; }); +const inputName = computed(() => (props.id ? `${props.id}-input` : undefined)); + const styles = computed((): { height: string; width: string } => ({ height: `${resHeight.value}px`, width: `${resWidth.value}px`, @@ -152,20 +114,6 @@ const onMarkdownClick = (link: string, event: Event) => { emit('markdown-click', link, event); }; -const onResize = (values: ResizeData) => { - emit('resize', values); -}; - -const onResizeStart = () => { - isResizing.value = true; - emit('resizestart'); -}; - -const onResizeEnd = () => { - isResizing.value = false; - emit('resizeend'); -}; - const onInputScroll = (event: WheelEvent) => { // Pass through zoom events but hold regular scrolling if (!event.ctrlKey && !event.metaKey) { diff --git a/packages/design-system/src/components/N8nSticky/constants.ts b/packages/design-system/src/components/N8nSticky/constants.ts new file mode 100644 index 0000000000..274d51a63f --- /dev/null +++ b/packages/design-system/src/components/N8nSticky/constants.ts @@ -0,0 +1,10 @@ +export const defaultStickyProps = { + height: 180, + width: 240, + minHeight: 80, + minWidth: 150, + id: '0', + editMode: false, + readOnly: false, + backgroundColor: 1, +}; diff --git a/packages/design-system/src/components/N8nSticky/types.ts b/packages/design-system/src/components/N8nSticky/types.ts new file mode 100644 index 0000000000..276a785a95 --- /dev/null +++ b/packages/design-system/src/components/N8nSticky/types.ts @@ -0,0 +1,12 @@ +export interface StickyProps { + modelValue?: string; + height?: number; + width?: number; + minHeight?: number; + minWidth?: number; + id?: string; + defaultText?: string; + editMode?: boolean; + readOnly?: boolean; + backgroundColor?: number | string; +} diff --git a/packages/design-system/src/components/index.ts b/packages/design-system/src/components/index.ts index d917339b5e..d4f44016f7 100644 --- a/packages/design-system/src/components/index.ts +++ b/packages/design-system/src/components/index.ts @@ -40,6 +40,7 @@ export { default as N8nResizeWrapper } from './N8nResizeWrapper'; export { default as N8nSelect } from './N8nSelect'; export { default as N8nSpinner } from './N8nSpinner'; export { default as N8nSticky } from './N8nSticky'; +export { default as N8nResizeableSticky } from './N8nResizeableSticky'; export { default as N8nTabs } from './N8nTabs'; export { default as N8nTag } from './N8nTag'; export { default as N8nTags } from './N8nTags'; diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index da463b11d3..e06a493039 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -40,6 +40,7 @@ "@vue-flow/controls": "^1.1.1", "@vue-flow/core": "^1.33.5", "@vue-flow/minimap": "^1.4.0", + "@vue-flow/node-resizer": "^1.4.0", "@vueuse/components": "^10.11.0", "@vueuse/core": "^10.11.0", "axios": "1.6.7", diff --git a/packages/editor-ui/src/__tests__/data/canvas.ts b/packages/editor-ui/src/__tests__/data/canvas.ts index b7ac64fedc..40f0060fe2 100644 --- a/packages/editor-ui/src/__tests__/data/canvas.ts +++ b/packages/editor-ui/src/__tests__/data/canvas.ts @@ -5,6 +5,7 @@ import { CanvasNodeRenderType } from '@/types'; export function createCanvasNodeData({ id = 'node', + name = 'Test Node', type = 'test', typeVersion = 1, disabled = false, @@ -21,13 +22,14 @@ export function createCanvasNodeData({ }, }: Partial = {}): CanvasNodeData { return { + id, + name, + type, + typeVersion, execution, issues, pinnedData, runData, - id, - type, - typeVersion, disabled, inputs, outputs, diff --git a/packages/editor-ui/src/__tests__/mocks.ts b/packages/editor-ui/src/__tests__/mocks.ts index 5b7bac23af..dad814f6eb 100644 --- a/packages/editor-ui/src/__tests__/mocks.ts +++ b/packages/editor-ui/src/__tests__/mocks.ts @@ -23,6 +23,7 @@ import { MANUAL_TRIGGER_NODE_TYPE, NO_OP_NODE_TYPE, SET_NODE_TYPE, + STICKY_NODE_TYPE, } from '@/constants'; import type { INodeUi } from '@/Interface'; @@ -34,6 +35,7 @@ export const mockNode = ({ disabled = false, issues = undefined, typeVersion = 1, + parameters = {}, }: { id?: INodeUi['id']; name: INodeUi['name']; @@ -42,7 +44,8 @@ export const mockNode = ({ disabled?: INodeUi['disabled']; issues?: INodeIssues; typeVersion?: INodeUi['typeVersion']; -}) => mock({ id, name, type, position, disabled, issues, typeVersion }); + parameters?: INodeUi['parameters']; +}) => mock({ id, name, type, position, disabled, issues, typeVersion, parameters }); export const mockNodeTypeDescription = ({ name, @@ -90,6 +93,7 @@ export const mockNodes = [ mockNode({ name: 'Rename', type: SET_NODE_TYPE }), mockNode({ name: 'Chat Trigger', type: CHAT_TRIGGER_NODE_TYPE }), mockNode({ name: 'Agent', type: AGENT_NODE_TYPE }), + mockNode({ name: 'Sticky', type: STICKY_NODE_TYPE }), mockNode({ name: 'End', type: NO_OP_NODE_TYPE }), ]; diff --git a/packages/editor-ui/src/components/Sticky.vue b/packages/editor-ui/src/components/Sticky.vue index 38b3cc1620..31627984d7 100644 --- a/packages/editor-ui/src/components/Sticky.vue +++ b/packages/editor-ui/src/components/Sticky.vue @@ -24,7 +24,7 @@ @click.left="mouseLeftClick" @contextmenu="onContextMenu" > - ]; 'run:node': [id: string]; 'delete:node': [id: string]; 'delete:connection': [connection: Connection]; @@ -57,10 +58,14 @@ const { getSelectedEdges, getSelectedNodes, viewportRef, fitView, project } = us function onNodeDragStop(e: NodeDragEvent) { e.nodes.forEach((node) => { - emit('update:node:position', node.id, node.position); + onUpdateNodePosition(node.id, node.position); }); } +function onUpdateNodePosition(id: string, position: XYPosition) { + emit('update:node:position', id, position); +} + function onSelectionDragStop(e: NodeDragEvent) { onNodeDragStop(e); } @@ -82,6 +87,10 @@ function onDeleteNode(id: string) { emit('delete:node', id); } +function onUpdateNodeParameters(id: string, parameters: Record) { + emit('update:node:parameters', id, parameters); +} + /** * Connections */ @@ -222,6 +231,8 @@ onUnmounted(() => { @select="onSelectNode" @toggle="onToggleNodeEnabled" @activate="onSetNodeActive" + @update="onUpdateNodeParameters" + @move="onUpdateNodePosition" /> diff --git a/packages/editor-ui/src/components/canvas/elements/buttons/CanvasRunWorkflowButton.vue b/packages/editor-ui/src/components/canvas/elements/buttons/CanvasRunWorkflowButton.vue index f68f69ecac..8f05b5a2b4 100644 --- a/packages/editor-ui/src/components/canvas/elements/buttons/CanvasRunWorkflowButton.vue +++ b/packages/editor-ui/src/components/canvas/elements/buttons/CanvasRunWorkflowButton.vue @@ -10,8 +10,8 @@ defineEmits<{ }>(); const props = defineProps<{ - waitingForWebhook: boolean; - executing: boolean; + waitingForWebhook?: boolean; + executing?: boolean; disabled?: boolean; }>(); diff --git a/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.vue b/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.vue index 831f27d5bb..5f419e511b 100644 --- a/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.vue +++ b/packages/editor-ui/src/components/canvas/elements/nodes/CanvasNode.vue @@ -1,5 +1,4 @@