feat: Add sticky notes support to the new canvas (no-changelog) (#10031)
This commit is contained in:
@@ -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:
|
||||
'<n8n-resizeable-sticky v-bind="args" @resize="onResize" @resizeend="onResizeEnd" @resizeStart="onResizeStart" @input="onInput"></n8n-resizeable-sticky>',
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<N8nResizeWrapper
|
||||
:is-resizing-enabled="!readOnly"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:min-height="minHeight"
|
||||
:min-width="minWidth"
|
||||
:scale="scale"
|
||||
:grid-size="gridSize"
|
||||
@resizeend="onResizeEnd"
|
||||
@resize="onResize"
|
||||
@resizestart="onResizeStart"
|
||||
>
|
||||
<N8nSticky v-bind="stickyBindings" />
|
||||
</N8nResizeWrapper>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, useAttrs } from 'vue';
|
||||
import N8nResizeWrapper, { type ResizeData } from '../N8nResizeWrapper/ResizeWrapper.vue';
|
||||
import N8nSticky from '../N8nSticky/Sticky.vue';
|
||||
import type { StickyProps } from '../N8nSticky/types';
|
||||
import { defaultStickyProps } from '../N8nSticky/constants';
|
||||
|
||||
type ResizeableStickyProps = StickyProps & {
|
||||
scale?: number;
|
||||
gridSize?: number;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<ResizeableStickyProps>(), {
|
||||
...defaultStickyProps,
|
||||
scale: 1,
|
||||
gridSize: 20,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
resize: [values: ResizeData];
|
||||
resizestart: [];
|
||||
resizeend: [];
|
||||
}>();
|
||||
|
||||
const attrs = useAttrs();
|
||||
|
||||
const stickyBindings = computed(() => ({ ...props, ...attrs }));
|
||||
|
||||
const isResizing = ref(false);
|
||||
|
||||
const onResize = (values: ResizeData) => {
|
||||
emit('resize', values);
|
||||
};
|
||||
|
||||
const onResizeStart = () => {
|
||||
isResizing.value = true;
|
||||
emit('resizestart');
|
||||
};
|
||||
|
||||
const onResizeEnd = () => {
|
||||
isResizing.value = false;
|
||||
emit('resizeend');
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import ResizeableSticky from './ResizeableSticky.vue';
|
||||
|
||||
export default ResizeableSticky;
|
||||
@@ -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,
|
||||
|
||||
@@ -9,52 +9,40 @@
|
||||
:style="styles"
|
||||
@keydown.prevent
|
||||
>
|
||||
<N8nResizeWrapper
|
||||
:is-resizing-enabled="!readOnly"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:min-height="minHeight"
|
||||
:min-width="minWidth"
|
||||
:scale="scale"
|
||||
:grid-size="gridSize"
|
||||
@resizeend="onResizeEnd"
|
||||
@resize="onResize"
|
||||
@resizestart="onResizeStart"
|
||||
<div v-show="!editMode" :class="$style.wrapper" @dblclick.stop="onDoubleClick">
|
||||
<N8nMarkdown
|
||||
theme="sticky"
|
||||
:content="modelValue"
|
||||
:with-multi-breaks="true"
|
||||
@markdown-click="onMarkdownClick"
|
||||
@update-content="onUpdateModelValue"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="editMode"
|
||||
:class="{ 'full-height': !shouldShowFooter, 'sticky-textarea': true }"
|
||||
@click.stop
|
||||
@mousedown.stop
|
||||
@mouseup.stop
|
||||
@keydown.esc="onInputBlur"
|
||||
@keydown.stop
|
||||
>
|
||||
<div v-show="!editMode" :class="$style.wrapper" @dblclick.stop="onDoubleClick">
|
||||
<N8nMarkdown
|
||||
theme="sticky"
|
||||
:content="modelValue"
|
||||
:with-multi-breaks="true"
|
||||
@markdown-click="onMarkdownClick"
|
||||
@update-content="onUpdateModelValue"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="editMode"
|
||||
:class="{ 'full-height': !shouldShowFooter, 'sticky-textarea': true }"
|
||||
@click.stop
|
||||
@mousedown.stop
|
||||
@mouseup.stop
|
||||
@keydown.esc="onInputBlur"
|
||||
@keydown.stop
|
||||
>
|
||||
<N8nInput
|
||||
ref="input"
|
||||
:model-value="modelValue"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
@blur="onInputBlur"
|
||||
@update:model-value="onUpdateModelValue"
|
||||
@wheel="onInputScroll"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
||||
<N8nText size="xsmall" align="right">
|
||||
<span v-html="t('sticky.markdownHint')"></span>
|
||||
</N8nText>
|
||||
</div>
|
||||
</N8nResizeWrapper>
|
||||
<N8nInput
|
||||
ref="input"
|
||||
:model-value="modelValue"
|
||||
:name="inputName"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
@blur="onInputBlur"
|
||||
@update:model-value="onUpdateModelValue"
|
||||
@wheel="onInputScroll"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
||||
<N8nText size="xsmall" align="right">
|
||||
<span v-html="t('sticky.markdownHint')"></span>
|
||||
</N8nText>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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<StickyProps>(), {
|
||||
height: 180,
|
||||
width: 240,
|
||||
minHeight: 80,
|
||||
minWidth: 150,
|
||||
scale: 1,
|
||||
gridSize: 20,
|
||||
id: '0',
|
||||
editMode: false,
|
||||
readOnly: false,
|
||||
backgroundColor: 1,
|
||||
});
|
||||
const props = withDefaults(defineProps<StickyProps>(), 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) {
|
||||
|
||||
10
packages/design-system/src/components/N8nSticky/constants.ts
Normal file
10
packages/design-system/src/components/N8nSticky/constants.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const defaultStickyProps = {
|
||||
height: 180,
|
||||
width: 240,
|
||||
minHeight: 80,
|
||||
minWidth: 150,
|
||||
id: '0',
|
||||
editMode: false,
|
||||
readOnly: false,
|
||||
backgroundColor: 1,
|
||||
};
|
||||
12
packages/design-system/src/components/N8nSticky/types.ts
Normal file
12
packages/design-system/src/components/N8nSticky/types.ts
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user