Skip to content

Commit

Permalink
feat(editor): SQL editor overhaul (n8n-io#6282)
Browse files Browse the repository at this point in the history
* Draft setup
* ⚡ Implemented expression evaluation in Postgres node, minor SQL editor UI improvements, minor refacring
* ⚡ Added initial version of expression preview for SQL editor
* ⚡ Linking npm package for codemirror sql grammar instead of a local file
* ⚡ Moving expression editor wrapper elements to the component
* ⚡ Using expression preview in SQL editor
* Use SQL parser skipping whitespace
* ✨ Added support for custom skipped segments specification
* ✨ Fixing highlight problems with dots and expressions that resolve to zero
* 👕 Fixing linting error
* ✨ Added current item support
* ⚡ Added expression support to more nodes with sql editor
* ✨ Added expression support for other nodes
* ✨ Implemented different SQL dialect support
* 🐛 Fixing hard-coded parameter names for editors
* ✨ Fixing preview for nested queries, updating query when input data changes, adding keyboard shortcut to toggle comments
* ✨ Adding a custom automcomplete notice for different editors
* ⚡ Updating SQL autocomplete notice
* ✅ Added unit tests for SQL editor
* ⚡ Using latest grammar
* 🐛 Fixing code node editor rendering
* 💄 SQL preview dropdown matches editor width. Removing unnecessary css
* ⚡ Addressing PR review feedback
* 👌 Addressing PR review feedback pt2
* 👌 Added path alias for utils in nodes-base package
* 👌 Addressing more PR review feedback
* ✅ Adding tests for `getResolvables` utility function
* ⚡Fixing lodash imports
* 👌 Better focus handling, adding more plugins to the editor, other minor imrovements
* ⚡ Not showing SQL autocomplete suggestions inside expressions
* ⚡ Using npm package for sql grammar
* ⚡ Removing autocomplete notice, adding line highlight on syntax error
* 👌 Addressing code review feedback
---------
Co-authored-by: Milorad Filipovic <[email protected]>
  • Loading branch information
ivov authored Jun 22, 2023
1 parent d431117 commit beedfb6
Show file tree
Hide file tree
Showing 68 changed files with 653 additions and 287 deletions.
2 changes: 1 addition & 1 deletion packages/editor-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"@codemirror/lang-javascript": "^6.1.2",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-python": "^6.1.2",
"@codemirror/lang-sql": "^6.4.1",
"@codemirror/language": "^6.2.1",
"@codemirror/lint": "^6.0.0",
"@codemirror/state": "^6.1.4",
Expand All @@ -46,6 +45,7 @@
"@jsplumb/connector-bezier": "^5.13.2",
"@jsplumb/core": "^5.13.2",
"@jsplumb/util": "^5.13.2",
"@n8n/codemirror-lang-sql": "^1.0.0",
"axios": "^0.21.1",
"codemirror-lang-html-n8n": "^1.0.0",
"codemirror-lang-n8n-expression": "^0.2.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/editor-ui/src/components/CodeNodeEditor/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ const cssStyleDeclaration = getComputedStyle(document.documentElement);

interface ThemeSettings {
isReadOnly?: boolean;
customMaxHeight?: string;
}

export const codeNodeEditorTheme = ({ isReadOnly }: ThemeSettings) => [
export const codeNodeEditorTheme = ({ isReadOnly, customMaxHeight }: ThemeSettings) => [
EditorView.theme({
'&': {
'font-size': BASE_STYLING.fontSize,
Expand Down Expand Up @@ -79,7 +80,7 @@ export const codeNodeEditorTheme = ({ isReadOnly }: ThemeSettings) => [
},
'.cm-scroller': {
overflow: 'auto',
maxHeight: '100%',
maxHeight: customMaxHeight ?? '100%',
...(isReadOnly ? {} : { minHeight: '10em' }),
},
'.cm-diagnosticAction': {
Expand Down
107 changes: 9 additions & 98 deletions packages/editor-ui/src/components/ExpressionParameterInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,13 @@
/>
</div>

<div :class="isFocused ? $style.dropdown : $style.hidden">
<n8n-text size="small" compact :class="$style.header">
{{ $locale.baseText('parameterInput.resultForItem') }} {{ hoveringItemNumber }}
</n8n-text>
<n8n-text :class="$style.body">
<InlineExpressionEditorOutput
:value="value"
:isReadOnly="isReadOnly"
:segments="segments"
/>
</n8n-text>
<div :class="$style.footer">
<n8n-text size="small" compact>
{{ $locale.baseText('parameterInput.anythingInside') }}
</n8n-text>
<div :class="$style['expression-syntax-example']" v-text="`{{ }}`"></div>
<n8n-text size="small" compact>
{{ $locale.baseText('parameterInput.isJavaScript') }}
</n8n-text>
<n8n-link
:class="$style['learn-more']"
size="small"
underline
theme="text"
:to="expressionsDocsUrl"
>
{{ $locale.baseText('parameterInput.learnMore') }}
</n8n-link>
</div>
</div>
<InlineExpressionEditorOutput
:segments="segments"
:value="value"
:isReadOnly="isReadOnly"
:visible="isFocused"
:hoveringItemNumber="hoveringItemNumber"
/>
</div>
</template>

Expand All @@ -74,7 +51,6 @@ import InlineExpressionEditorInput from '@/components/InlineExpressionEditor/Inl
import InlineExpressionEditorOutput from '@/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue';
import ExpressionFunctionIcon from '@/components/ExpressionFunctionIcon.vue';
import { createExpressionTelemetryPayload } from '@/utils/telemetryUtils';
import { EXPRESSIONS_DOCS_URL } from '@/constants';
import type { Segment } from '@/types/expressions';
import type { TargetItem } from '@/Interface';
Expand All @@ -92,7 +68,6 @@ export default defineComponent({
return {
isFocused: false,
segments: [] as Segment[],
expressionsDocsUrl: EXPRESSIONS_DOCS_URL,
};
},
props: {
Expand All @@ -114,14 +89,10 @@ export default defineComponent({
computed: {
...mapStores(useNDVStore, useWorkflowsStore),
hoveringItemNumber(): number {
return (this.hoveringItem?.itemIndex ?? 0) + 1;
return this.ndvStore.hoveringItemNumber;
},
hoveringItem(): TargetItem | null {
if (this.ndvStore.isInputParentOfActiveNode) {
return this.ndvStore.hoveringItem;
}
return null;
return this.ndvStore.getHoveringItem;
},
isDragging(): boolean {
return this.ndvStore.isDraggableDragging;
Expand Down Expand Up @@ -241,64 +212,4 @@ export default defineComponent({
border-bottom-right-radius: 0;
background-color: white;
}
.hidden {
display: none;
}
.dropdown {
display: flex;
flex-direction: column;
position: absolute;
z-index: 2; // cover tooltips
background: white;
border: var(--border-base);
border-top: none;
width: 100%;
box-shadow: 0 2px 6px 0 rgba(#441c17, 0.1);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
.header,
.body,
.footer {
padding: var(--spacing-3xs);
}
.header {
color: var(--color-text-dark);
font-weight: var(--font-weight-bold);
padding-left: var(--spacing-2xs);
padding-top: var(--spacing-2xs);
}
.body {
padding-top: 0;
padding-left: var(--spacing-2xs);
color: var(--color-text-dark);
}
.footer {
border-top: var(--border-base);
padding: var(--spacing-4xs);
padding-left: var(--spacing-2xs);
padding-top: 0;
line-height: var(--font-line-height-regular);
color: var(--color-text-base);
.expression-syntax-example {
display: inline-block;
font-size: var(--font-size-2xs);
height: var(--font-size-m);
background-color: #f0f0f0;
margin-left: var(--spacing-5xs);
margin-right: var(--spacing-5xs);
}
.learn-more {
line-height: 1;
white-space: nowrap;
}
}
}
</style>
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
<template>
<div ref="root" class="ph-no-capture" data-test-id="inline-expression-editor-output"></div>
<div :class="visible ? $style.dropdown : $style.hidden">
<n8n-text size="small" compact :class="$style.header">
{{ $locale.baseText('parameterInput.resultForItem') }} {{ hoveringItemNumber }}
</n8n-text>
<n8n-text :class="$style.body">
<div ref="root" class="ph-no-capture" data-test-id="inline-expression-editor-output"></div>
</n8n-text>
<div :class="$style.footer">
<n8n-text size="small" compact>
{{ $locale.baseText('parameterInput.anythingInside') }}
</n8n-text>
<div :class="$style['expression-syntax-example']" v-text="`{{ }}`"></div>
<n8n-text size="small" compact>
{{ $locale.baseText('parameterInput.isJavaScript') }}
</n8n-text>
<n8n-link
:class="$style['learn-more']"
size="small"
underline
theme="text"
:to="expressionsDocsUrl"
>
{{ $locale.baseText('parameterInput.learnMore') }}
</n8n-link>
</div>
</div>
</template>

<script lang="ts">
Expand All @@ -13,12 +38,29 @@ import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
import { outputTheme } from './theme';
import type { Plaintext, Resolved, Segment } from '@/types/expressions';
import { EXPRESSIONS_DOCS_URL } from '@/constants';
export default defineComponent({
name: 'InlineExpressionEditorOutput',
props: {
segments: {
type: Array as PropType<Segment[]>,
required: true,
},
value: {
type: String,
},
isReadOnly: {
type: Boolean,
default: false,
},
visible: {
type: Boolean,
default: false,
},
hoveringItemNumber: {
type: Number,
required: true,
},
},
watch: {
Expand All @@ -36,6 +78,7 @@ export default defineComponent({
data() {
return {
editor: null as EditorView | null,
expressionsDocsUrl: EXPRESSIONS_DOCS_URL,
};
},
mounted() {
Expand Down Expand Up @@ -87,4 +130,64 @@ export default defineComponent({
});
</script>

<style lang="scss"></style>
<style lang="scss" module>
.hidden {
display: none;
}
.dropdown {
display: flex;
flex-direction: column;
position: absolute;
z-index: 2; // cover tooltips
background: white;
border: var(--border-base);
border-top: none;
width: 100%;
box-shadow: 0 2px 6px 0 rgba(#441c17, 0.1);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
.header,
.body,
.footer {
padding: var(--spacing-3xs);
}
.header {
color: var(--color-text-dark);
font-weight: var(--font-weight-bold);
padding-left: var(--spacing-2xs);
padding-top: var(--spacing-2xs);
}
.body {
padding-top: 0;
padding-left: var(--spacing-2xs);
color: var(--color-text-dark);
}
.footer {
border-top: var(--border-base);
padding: var(--spacing-4xs);
padding-left: var(--spacing-2xs);
padding-top: 0;
line-height: var(--font-line-height-regular);
color: var(--color-text-base);
.expression-syntax-example {
display: inline-block;
font-size: var(--font-size-2xs);
height: var(--font-size-m);
background-color: #f0f0f0;
margin-left: var(--spacing-5xs);
margin-right: var(--spacing-5xs);
}
.learn-more {
line-height: 1;
white-space: nowrap;
}
}
}
</style>
2 changes: 1 addition & 1 deletion packages/editor-ui/src/components/ParameterOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default defineComponent({
return false;
}
if (this.parameter.typeOptions?.editor === 'codeNodeEditor') {
if (['codeNodeEditor', 'sqlEditor'].includes(this.parameter.typeOptions?.editor)) {
return false;
}
Expand Down
Loading

0 comments on commit beedfb6

Please sign in to comment.