mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
feat(editor): Add fullscreen view to code editor (#8084)
## Summary <img width="1240" alt="image" src="https://github.com/n8n-io/n8n/assets/8850410/2819f4ce-c343-431a-8a88-a1bc9c4b572a"> <img width="2649" alt="image" src="https://github.com/n8n-io/n8n/assets/8850410/36862aaf-cc4c-4668-bdc8-cf5a6f00babe"> 1. Add code node and open it 3. Click the fullscreen button in the bottom right 4. A fullscreen dialog should appear and allow editing the code 5. Changes made in the fullscreen dialog should be applied to the original code editor when closed It should work the same way for HTML/SQL/JSON editors ⚠️ Modal layout was updated so that modals/dialogs are centered, try to test some modals ## Related tickets and issues https://linear.app/n8n/issue/NODE-1009/add-fullscreen-view-to-code-node ## Review / Merge checklist - [ ] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [ ] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. > A feature is not complete without tests. --------- Co-authored-by: Giulio Andreini <andreini@netseven.it>
This commit is contained in:
parent
8f22a265d6
commit
071e6d6b6e
|
@ -1,5 +1,5 @@
|
|||
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
||||
import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils';
|
||||
import { getVisiblePopper, getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
|
@ -66,6 +66,8 @@ describe('Resource Locator', () => {
|
|||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' });
|
||||
|
||||
ndv.getters.resourceLocatorInput('rlc').click();
|
||||
|
||||
cy.getByTestId('rlc-item').should('exist');
|
||||
getVisiblePopper()
|
||||
.should('have.length', 1)
|
||||
.findChildByTestId('rlc-item')
|
||||
|
@ -73,9 +75,11 @@ describe('Resource Locator', () => {
|
|||
|
||||
ndv.actions.setInvalidExpression({ fieldName: 'fieldId' });
|
||||
|
||||
ndv.getters.container().click(); // remove focus from input, hide expression preview
|
||||
ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.getters.resourceLocatorInput('rlc').click();
|
||||
|
||||
cy.getByTestId('rlc-item').should('exist');
|
||||
getVisiblePopper()
|
||||
.should('have.length', 1)
|
||||
.findChildByTestId('rlc-item')
|
||||
|
|
|
@ -302,7 +302,7 @@ describe('NDV', () => {
|
|||
|
||||
ndv.actions.setInvalidExpression({ fieldName: 'fieldId', delay: 200 });
|
||||
|
||||
ndv.getters.container().click(); // remove focus from input, hide expression preview
|
||||
ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.getters.parameterInput('remoteOptions').click();
|
||||
|
||||
|
@ -320,7 +320,7 @@ describe('NDV', () => {
|
|||
|
||||
ndv.actions.setInvalidExpression({ fieldName: 'otherField', delay: 50 });
|
||||
|
||||
ndv.getters.container().click(); // remove focus from input, hide expression preview
|
||||
ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.getters.parameterInput('remoteOptions').click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
|
||||
|
@ -360,6 +360,15 @@ describe('NDV', () => {
|
|||
ndv.getters.nodeExecuteButton().should('be.visible');
|
||||
});
|
||||
|
||||
it('should allow editing code in fullscreen in the Code node', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Code', { keepNdvOpen: true });
|
||||
ndv.actions.openCodeEditorFullscreen();
|
||||
|
||||
ndv.getters.codeEditorFullscreen().type('{selectall}').type('{backspace}').type('foo()');
|
||||
ndv.getters.codeEditorDialog().find('.el-dialog__close').click();
|
||||
ndv.getters.parameterInput('jsCode').get('.cm-content').should('contain.text', 'foo()');
|
||||
});
|
||||
|
||||
it('should not retrieve remote options when a parameter value changes', () => {
|
||||
cy.intercept('/rest/dynamic-node-parameters/options?**', cy.spy().as('fetchParameterOptions'));
|
||||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
||||
|
@ -370,106 +379,126 @@ describe('NDV', () => {
|
|||
});
|
||||
|
||||
describe('floating nodes', () => {
|
||||
function getFloatingNodeByPosition(position: 'inputMain' | 'outputMain' | 'outputSub'| 'inputSub') {
|
||||
function getFloatingNodeByPosition(
|
||||
position: 'inputMain' | 'outputMain' | 'outputSub' | 'inputSub',
|
||||
) {
|
||||
return cy.get(`[data-node-placement=${position}]`);
|
||||
}
|
||||
beforeEach(() => {
|
||||
cy.createFixtureWorkflow('Floating_Nodes.json', `Floating Nodes`);
|
||||
workflowPage.getters.canvasNodes().first().dblclick()
|
||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("outputMain").should('exist');
|
||||
workflowPage.getters.canvasNodes().first().dblclick();
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
});
|
||||
|
||||
it('should traverse floating nodes with mouse', () => {
|
||||
// Traverse 4 connected node forwards
|
||||
Array.from(Array(4).keys()).forEach(i => {
|
||||
getFloatingNodeByPosition("outputMain").click({ force: true});
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
getFloatingNodeByPosition('outputMain').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
||||
getFloatingNodeByPosition("inputMain").should('exist');
|
||||
getFloatingNodeByPosition("outputMain").should('exist');
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', `Node ${i + 1}`);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', `Node ${i + 1}`);
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
})
|
||||
});
|
||||
|
||||
getFloatingNodeByPosition("outputMain").click({ force: true});
|
||||
getFloatingNodeByPosition('outputMain').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
getFloatingNodeByPosition("inputSub").should('exist');
|
||||
getFloatingNodeByPosition("inputSub").click({ force: true});
|
||||
getFloatingNodeByPosition('inputSub').should('exist');
|
||||
getFloatingNodeByPosition('inputSub').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("outputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("outputSub").should('exist');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
getFloatingNodeByPosition("outputSub").click({ force: true});
|
||||
getFloatingNodeByPosition('outputSub').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
|
||||
// Traverse 4 connected node backwards
|
||||
Array.from(Array(4).keys()).forEach(i => {
|
||||
getFloatingNodeByPosition("inputMain").click({ force: true});
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - (i)}`);
|
||||
getFloatingNodeByPosition("outputMain").should('exist');
|
||||
getFloatingNodeByPosition("inputMain").should('exist');
|
||||
})
|
||||
getFloatingNodeByPosition("inputMain").click({ force: true});
|
||||
workflowPage.getters.selectedNodes().first().should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
||||
getFloatingNodeByPosition("outputSub").should('not.exist');
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
getFloatingNodeByPosition('inputMain').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
});
|
||||
getFloatingNodeByPosition('inputMain').click({ force: true });
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('not.exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
});
|
||||
|
||||
it('should traverse floating nodes with mouse', () => {
|
||||
// Traverse 4 connected node forwards
|
||||
Array.from(Array(4).keys()).forEach(i => {
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight'])
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
||||
getFloatingNodeByPosition("inputMain").should('exist');
|
||||
getFloatingNodeByPosition("outputMain").should('exist');
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', `Node ${i + 1}`);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', `Node ${i + 1}`);
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
})
|
||||
});
|
||||
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight'])
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
getFloatingNodeByPosition("inputSub").should('exist');
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowDown'])
|
||||
getFloatingNodeByPosition('inputSub').should('exist');
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowDown']);
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("outputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("outputSub").should('exist');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowUp'])
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowUp']);
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
|
||||
// Traverse 4 connected node backwards
|
||||
Array.from(Array(4).keys()).forEach(i => {
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft'])
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - (i)}`);
|
||||
getFloatingNodeByPosition("outputMain").should('exist');
|
||||
getFloatingNodeByPosition("inputMain").should('exist');
|
||||
})
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft'])
|
||||
workflowPage.getters.selectedNodes().first().should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
||||
getFloatingNodeByPosition("outputSub").should('not.exist');
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
});
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('not.exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -501,23 +530,34 @@ describe('NDV', () => {
|
|||
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
||||
|
||||
ndv.getters.outputTableRow(1).should('include.text', '<?xml version="1.0" encoding="UTF-8"?> <library>');
|
||||
ndv.getters
|
||||
.outputTableRow(1)
|
||||
.should('include.text', '<?xml version="1.0" encoding="UTF-8"?> <library>');
|
||||
|
||||
cy.document().trigger('keyup', { key: '/' });
|
||||
ndv.getters.searchInput().filter(':focus').type('<lib');
|
||||
|
||||
ndv.getters.outputTableRow(1).find('mark').should('have.text', '<lib')
|
||||
ndv.getters.outputTableRow(1).find('mark').should('have.text', '<lib');
|
||||
|
||||
ndv.getters.outputDisplayMode().find('label').eq(1).should('include.text', 'JSON');
|
||||
ndv.getters.outputDisplayMode().find('label').eq(1).click();
|
||||
|
||||
ndv.getters.outputDataContainer().find('.json-data').should('be.visible');
|
||||
ndv.getters.outputDataContainer().should('have.text', '[{"body": "<?xml version="1.0" encoding="UTF-8"?> <library> <book> <title>Introduction to XML</title> <author>John Doe</author> <publication_year>2020</publication_year> <isbn>1234567890</isbn> </book> <book> <title>Data Science Basics</title> <author>Jane Smith</author> <publication_year>2019</publication_year> <isbn>0987654321</isbn> </book> <book> <title>Programming in Python</title> <author>Bob Johnson</author> <publication_year>2021</publication_year> <isbn>5432109876</isbn> </book> </library>"}]');
|
||||
ndv.getters.outputDataContainer().find('mark').should('have.text', '<lib')
|
||||
ndv.getters.outputDataContainer().find('.json-data').should('exist');
|
||||
ndv.getters
|
||||
.outputDataContainer()
|
||||
.should(
|
||||
'have.text',
|
||||
'[{"body": "<?xml version="1.0" encoding="UTF-8"?> <library> <book> <title>Introduction to XML</title> <author>John Doe</author> <publication_year>2020</publication_year> <isbn>1234567890</isbn> </book> <book> <title>Data Science Basics</title> <author>Jane Smith</author> <publication_year>2019</publication_year> <isbn>0987654321</isbn> </book> <book> <title>Programming in Python</title> <author>Bob Johnson</author> <publication_year>2021</publication_year> <isbn>5432109876</isbn> </book> </library>"}]',
|
||||
);
|
||||
ndv.getters.outputDataContainer().find('mark').should('have.text', '<lib');
|
||||
|
||||
ndv.getters.outputDisplayMode().find('label').eq(2).should('include.text', 'Schema');
|
||||
ndv.getters.outputDisplayMode().find('label').eq(2).click({force: true});
|
||||
ndv.getters.outputDataContainer().findChildByTestId('run-data-schema-item').find('> span').should('include.text', '<?xml version="1.0" encoding="UTF-8"?>');
|
||||
ndv.getters.outputDisplayMode().find('label').eq(2).click({ force: true });
|
||||
ndv.getters
|
||||
.outputDataContainer()
|
||||
.findChildByTestId('run-data-schema-item')
|
||||
.find('> span')
|
||||
.should('include.text', '<?xml version="1.0" encoding="UTF-8"?>');
|
||||
});
|
||||
|
||||
it('should properly show node execution indicator', () => {
|
||||
|
@ -546,7 +586,10 @@ describe('NDV', () => {
|
|||
});
|
||||
|
||||
it('Should handle mismatched option attributes', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('LDAP', { keepNdvOpen: true, action: 'Create a new entry' });
|
||||
workflowPage.actions.addInitialNodeToCanvas('LDAP', {
|
||||
keepNdvOpen: true,
|
||||
action: 'Create a new entry',
|
||||
});
|
||||
// Add some attributes in Create operation
|
||||
cy.getByTestId('parameter-item').contains('Add Attributes').click();
|
||||
ndv.actions.changeNodeOperation('Update');
|
||||
|
@ -556,7 +599,10 @@ describe('NDV', () => {
|
|||
|
||||
it('Should keep RLC values after operation change', () => {
|
||||
const TEST_DOC_ID = '1111';
|
||||
workflowPage.actions.addInitialNodeToCanvas('Google Sheets', { keepNdvOpen: true, action: 'Append row in sheet' });
|
||||
workflowPage.actions.addInitialNodeToCanvas('Google Sheets', {
|
||||
keepNdvOpen: true,
|
||||
action: 'Append row in sheet',
|
||||
});
|
||||
ndv.actions.setRLCValue('documentId', TEST_DOC_ID);
|
||||
ndv.actions.changeNodeOperation('Update Row');
|
||||
ndv.getters.resourceLocatorInput('documentId').find('input').should('have.value', TEST_DOC_ID);
|
||||
|
|
|
@ -98,6 +98,9 @@ export class NDV extends BasePage {
|
|||
pagination: () => cy.getByTestId('ndv-data-pagination'),
|
||||
nodeVersion: () => cy.getByTestId('node-version'),
|
||||
nodeSettingsTab: () => cy.getByTestId('tab-settings'),
|
||||
codeEditorFullscreenButton: () => cy.getByTestId('code-editor-fullscreen-button'),
|
||||
codeEditorDialog: () => cy.getByTestId('code-editor-fullscreen'),
|
||||
codeEditorFullscreen: () => this.getters.codeEditorDialog().find('.cm-content'),
|
||||
nodeRunSuccessIndicator: () => cy.getByTestId('node-run-info-success'),
|
||||
nodeRunErrorIndicator: () => cy.getByTestId('node-run-info-danger'),
|
||||
};
|
||||
|
@ -251,9 +254,15 @@ export class NDV extends BasePage {
|
|||
openSettings: () => {
|
||||
this.getters.nodeSettingsTab().click();
|
||||
},
|
||||
|
||||
openCodeEditorFullscreen: () => {
|
||||
this.getters.codeEditorFullscreenButton().click({ force: true });
|
||||
},
|
||||
changeNodeOperation: (operation: string) => {
|
||||
this.getters.parameterInput('operation').click();
|
||||
cy.get('.el-select-dropdown__item').contains(new RegExp(`^${operation}$`)).click({ force: true });
|
||||
cy.get('.el-select-dropdown__item')
|
||||
.contains(new RegExp(`^${operation}$`))
|
||||
.click({ force: true });
|
||||
this.getters.parameterInput('operation').find('input').should('have.value', operation);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -160,6 +160,7 @@ Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => {
|
|||
cy.get(draggableSelector).trigger('mousedown');
|
||||
}
|
||||
// We don't chain these commands to make sure cy.get is re-trying correctly
|
||||
cy.get(droppableSelector).realMouseMove(0, 0);
|
||||
cy.get(droppableSelector).realMouseMove(pageX, pageY);
|
||||
cy.get(droppableSelector).realHover();
|
||||
cy.get(droppableSelector).realMouseUp();
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: inherit;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@ -60,7 +61,6 @@
|
|||
|
||||
@include mixins.e(header) {
|
||||
padding: var.$dialog-padding-primary;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
@include mixins.e(headerbtn) {
|
||||
|
@ -95,6 +95,7 @@
|
|||
|
||||
@include mixins.e(body) {
|
||||
padding: var.$dialog-padding-primary;
|
||||
padding-top: 0;
|
||||
color: var(--color-text-base);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
name="code"
|
||||
data-test-id="code-node-tab-code"
|
||||
>
|
||||
<div ref="codeNodeEditor" class="code-node-editor-input ph-no-capture code-editor-tabs" />
|
||||
<div
|
||||
ref="codeNodeEditor"
|
||||
:class="['ph-no-capture', 'code-editor-tabs', $style.editorInput]"
|
||||
/>
|
||||
<slot name="suffix" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane
|
||||
:label="$locale.baseText('codeNodeEditor.tabs.askAi')"
|
||||
|
@ -35,7 +39,10 @@
|
|||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- If AskAi not enabled, there's no point in rendering tabs -->
|
||||
<div v-else ref="codeNodeEditor" class="code-node-editor-input ph-no-capture" />
|
||||
<div v-else :class="$style.fillHeight">
|
||||
<div ref="codeNodeEditor" :class="['ph-no-capture', $style.fillHeight]" />
|
||||
<slot name="suffix" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -82,6 +89,10 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fillParent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
mode: {
|
||||
type: String as PropType<CodeExecutionMode>,
|
||||
validator: (value: CodeExecutionMode): boolean => CODE_EXECUTION_MODES.includes(value),
|
||||
|
@ -193,7 +204,12 @@ export default defineComponent({
|
|||
...readOnlyEditorExtensions,
|
||||
EditorState.readOnly.of(isReadOnly),
|
||||
EditorView.editable.of(!isReadOnly),
|
||||
codeNodeEditorTheme({ isReadOnly, customMinHeight: this.rows }),
|
||||
codeNodeEditorTheme({
|
||||
isReadOnly,
|
||||
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||
minHeight: '20vh',
|
||||
rows: this.rows,
|
||||
}),
|
||||
];
|
||||
|
||||
if (!isReadOnly) {
|
||||
|
@ -384,15 +400,9 @@ export default defineComponent({
|
|||
<style lang="scss" module>
|
||||
.code-node-editor-container {
|
||||
position: relative;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ask-ai-button {
|
||||
position: absolute;
|
||||
top: var(--spacing-2xs);
|
||||
right: var(--spacing-2xs);
|
||||
.fillHeight {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,12 +6,14 @@ import {
|
|||
highlightSpecialChars,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
type KeyBinding,
|
||||
} from '@codemirror/view';
|
||||
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
||||
import { acceptCompletion } from '@codemirror/autocomplete';
|
||||
import { acceptCompletion, selectedCompletion } from '@codemirror/autocomplete';
|
||||
import {
|
||||
history,
|
||||
indentWithTab,
|
||||
indentLess,
|
||||
indentMore,
|
||||
insertNewlineAndIndent,
|
||||
toggleComment,
|
||||
redo,
|
||||
|
@ -19,7 +21,7 @@ import {
|
|||
undo,
|
||||
} from '@codemirror/commands';
|
||||
import { lintGutter } from '@codemirror/lint';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { type Extension, Prec } from '@codemirror/state';
|
||||
|
||||
import { codeInputHandler } from '@/plugins/codemirror/inputHandlers/code.inputHandler';
|
||||
|
||||
|
@ -29,6 +31,42 @@ export const readOnlyEditorExtensions: readonly Extension[] = [
|
|||
highlightSpecialChars(),
|
||||
];
|
||||
|
||||
export const tabKeyMap: KeyBinding[] = [
|
||||
{
|
||||
any(editor, event) {
|
||||
if (event.key === 'Tab' || (event.key === 'Escape' && selectedCompletion(editor.state))) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'Tab',
|
||||
run: (editor) => {
|
||||
if (selectedCompletion(editor.state)) {
|
||||
return acceptCompletion(editor);
|
||||
}
|
||||
|
||||
return indentMore(editor);
|
||||
},
|
||||
},
|
||||
{ key: 'Shift-Tab', run: indentLess },
|
||||
];
|
||||
|
||||
export const enterKeyMap: KeyBinding[] = [
|
||||
{
|
||||
key: 'Enter',
|
||||
run: (editor) => {
|
||||
if (selectedCompletion(editor.state)) {
|
||||
return acceptCompletion(editor);
|
||||
}
|
||||
|
||||
return insertNewlineAndIndent(editor);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const writableEditorExtensions: readonly Extension[] = [
|
||||
history(),
|
||||
lintGutter(),
|
||||
|
@ -39,14 +77,14 @@ export const writableEditorExtensions: readonly Extension[] = [
|
|||
bracketMatching(),
|
||||
highlightActiveLine(),
|
||||
highlightActiveLineGutter(),
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
{ key: 'Enter', run: insertNewlineAndIndent },
|
||||
{ key: 'Tab', run: acceptCompletion },
|
||||
{ key: 'Enter', run: acceptCompletion },
|
||||
...tabKeyMap,
|
||||
...enterKeyMap,
|
||||
{ key: 'Mod-/', run: toggleComment },
|
||||
{ key: 'Mod-z', run: undo },
|
||||
{ key: 'Mod-Shift-z', run: redo },
|
||||
{ key: 'Backspace', run: deleteCharBackward, shift: deleteCharBackward },
|
||||
indentWithTab,
|
||||
]),
|
||||
),
|
||||
];
|
||||
|
|
|
@ -29,14 +29,18 @@ const BASE_STYLING = {
|
|||
|
||||
interface ThemeSettings {
|
||||
isReadOnly?: boolean;
|
||||
customMaxHeight?: string;
|
||||
customMinHeight?: number;
|
||||
maxHeight?: string;
|
||||
minHeight?: string;
|
||||
rows?: number;
|
||||
highlightColors?: 'default' | 'html';
|
||||
}
|
||||
|
||||
export const codeNodeEditorTheme = ({
|
||||
isReadOnly,
|
||||
customMaxHeight,
|
||||
customMinHeight,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
rows,
|
||||
highlightColors,
|
||||
}: ThemeSettings) => [
|
||||
EditorView.theme({
|
||||
'&': {
|
||||
|
@ -85,11 +89,13 @@ export const codeNodeEditorTheme = ({
|
|||
},
|
||||
'.cm-scroller': {
|
||||
overflow: 'auto',
|
||||
|
||||
maxHeight: customMaxHeight ?? '100%',
|
||||
maxHeight: maxHeight ?? '100%',
|
||||
...(isReadOnly
|
||||
? {}
|
||||
: { minHeight: customMinHeight ? `${Number(customMinHeight) * 1.3}em` : '10em' }),
|
||||
: { minHeight: rows && rows !== -1 ? `${Number(rows + 1) * 1.3}em` : 'auto' }),
|
||||
},
|
||||
'.cm-gutter,.cm-content': {
|
||||
minHeight: rows && rows !== -1 ? 'auto' : minHeight ?? 'calc(35vh - var(--spacing-2xl))',
|
||||
},
|
||||
'.cm-diagnosticAction': {
|
||||
backgroundColor: BASE_STYLING.diagnosticButton.backgroundColor,
|
||||
|
@ -106,7 +112,57 @@ export const codeNodeEditorTheme = ({
|
|||
color: 'var(--color-text-base)',
|
||||
},
|
||||
}),
|
||||
syntaxHighlighting(
|
||||
highlightColors === 'html'
|
||||
? syntaxHighlighting(
|
||||
HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: '#c678dd' },
|
||||
{
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
color: '#e06c75',
|
||||
},
|
||||
{ tag: [tags.function(tags.variableName), tags.labelName], color: '#61afef' },
|
||||
{
|
||||
tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)],
|
||||
color: '#d19a66',
|
||||
},
|
||||
{ tag: [tags.definition(tags.name), tags.separator], color: '#abb2bf' },
|
||||
{
|
||||
tag: [
|
||||
tags.typeName,
|
||||
tags.className,
|
||||
tags.number,
|
||||
tags.changed,
|
||||
tags.annotation,
|
||||
tags.modifier,
|
||||
tags.self,
|
||||
tags.namespace,
|
||||
],
|
||||
color: '#e06c75',
|
||||
},
|
||||
{
|
||||
tag: [
|
||||
tags.operator,
|
||||
tags.operatorKeyword,
|
||||
tags.url,
|
||||
tags.escape,
|
||||
tags.regexp,
|
||||
tags.link,
|
||||
tags.special(tags.string),
|
||||
],
|
||||
color: '#56b6c2',
|
||||
},
|
||||
{ tag: [tags.meta, tags.comment], color: '#7d8799' },
|
||||
{ tag: tags.strong, fontWeight: 'bold' },
|
||||
{ tag: tags.emphasis, fontStyle: 'italic' },
|
||||
{ tag: tags.strikethrough, textDecoration: 'line-through' },
|
||||
{ tag: tags.link, color: '#7d8799', textDecoration: 'underline' },
|
||||
{ tag: tags.heading, fontWeight: 'bold', color: '#e06c75' },
|
||||
{ tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: '#d19a66' },
|
||||
{ tag: [tags.processingInstruction, tags.string, tags.inserted], color: '#98c379' },
|
||||
{ tag: tags.invalid, color: 'red', 'font-weight': 'bold' },
|
||||
]),
|
||||
)
|
||||
: syntaxHighlighting(
|
||||
HighlightStyle.define([
|
||||
{
|
||||
tag: tags.comment,
|
||||
|
|
|
@ -1,44 +1,48 @@
|
|||
<template>
|
||||
<div :class="$style.editor">
|
||||
<div ref="htmlEditor"></div>
|
||||
<slot name="suffix" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { format } from 'prettier';
|
||||
import htmlParser from 'prettier/plugins/html';
|
||||
import cssParser from 'prettier/plugins/postcss';
|
||||
import jsParser from 'prettier/plugins/babel';
|
||||
import * as estree from 'prettier/plugins/estree';
|
||||
import { htmlLanguage, autoCloseTags, html } from 'codemirror-lang-html-n8n';
|
||||
import { autocompletion } from '@codemirror/autocomplete';
|
||||
import { indentWithTab, insertNewlineAndIndent, history, redo, undo } from '@codemirror/commands';
|
||||
import { history, redo, undo } from '@codemirror/commands';
|
||||
import {
|
||||
LanguageSupport,
|
||||
bracketMatching,
|
||||
ensureSyntaxTree,
|
||||
foldGutter,
|
||||
indentOnInput,
|
||||
LanguageSupport,
|
||||
} from '@codemirror/language';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { EditorState, Prec } from '@codemirror/state';
|
||||
import type { ViewUpdate } from '@codemirror/view';
|
||||
import {
|
||||
dropCursor,
|
||||
EditorView,
|
||||
dropCursor,
|
||||
highlightActiveLine,
|
||||
highlightActiveLineGutter,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
} from '@codemirror/view';
|
||||
import { autoCloseTags, html, htmlLanguage } from 'codemirror-lang-html-n8n';
|
||||
import { format } from 'prettier';
|
||||
import jsParser from 'prettier/plugins/babel';
|
||||
import * as estree from 'prettier/plugins/estree';
|
||||
import htmlParser from 'prettier/plugins/html';
|
||||
import cssParser from 'prettier/plugins/postcss';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { htmlEditorEventBus } from '@/event-bus';
|
||||
import { expressionManager } from '@/mixins/expressionManager';
|
||||
import { n8nCompletionSources } from '@/plugins/codemirror/completions/addCompletions';
|
||||
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
|
||||
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
|
||||
import { htmlEditorEventBus } from '@/event-bus';
|
||||
import { expressionManager } from '@/mixins/expressionManager';
|
||||
import { theme } from './theme';
|
||||
import { nonTakenRanges } from './utils';
|
||||
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||
import type { Range, Section } from './types';
|
||||
import { nonTakenRanges } from './utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HtmlEditor',
|
||||
|
@ -52,6 +56,10 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fillParent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
|
@ -90,15 +98,21 @@ export default defineComponent({
|
|||
this.disableExpressionCompletions ? html() : htmlWithCompletions(),
|
||||
autoCloseTags,
|
||||
expressionInputHandler(),
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
indentWithTab,
|
||||
{ key: 'Enter', run: insertNewlineAndIndent },
|
||||
...tabKeyMap,
|
||||
...enterKeyMap,
|
||||
{ key: 'Mod-z', run: undo },
|
||||
{ key: 'Mod-Shift-z', run: redo },
|
||||
]),
|
||||
),
|
||||
indentOnInput(),
|
||||
theme({
|
||||
codeNodeEditorTheme({
|
||||
isReadOnly: this.isReadOnly,
|
||||
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||
minHeight: '20vh',
|
||||
rows: this.rows,
|
||||
highlightColors: 'html',
|
||||
}),
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
|
@ -288,4 +302,12 @@ export default defineComponent({
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module></style>
|
||||
<style lang="scss" module>
|
||||
.editor {
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import { tags } from '@lezer/highlight';
|
||||
|
||||
export const theme = ({ isReadOnly }: { isReadOnly: boolean }) => [
|
||||
EditorView.theme({
|
||||
'&': {
|
||||
'font-size': '0.8em',
|
||||
border: 'var(--border-base)',
|
||||
borderRadius: 'var(--border-radius-base)',
|
||||
backgroundColor: 'var(--color-code-background)',
|
||||
color: 'var(--color-code-foreground)',
|
||||
},
|
||||
'.cm-content': {
|
||||
fontFamily: "Menlo, Consolas, 'DejaVu Sans Mono', monospace !important",
|
||||
caretColor: 'var(--color-code-caret)',
|
||||
},
|
||||
'.cm-cursor, .cm-dropCursor': {
|
||||
borderLeftColor: 'var(--color-code-caret)',
|
||||
},
|
||||
'&.cm-editor': {
|
||||
...(isReadOnly ? { backgroundColor: 'var(--color-code-background-readonly)' } : {}),
|
||||
borderColor: 'var(--border-color-base)',
|
||||
},
|
||||
'&.cm-editor.cm-focused': {
|
||||
outline: '0',
|
||||
},
|
||||
'&.cm-focused .cm-selectionBackgroundm .cm-selectionBackground, .cm-content ::selection': {
|
||||
backgroundColor: 'var(--color-code-selection)',
|
||||
},
|
||||
'.cm-activeLine': {
|
||||
backgroundColor: 'var(--color-code-lineHighlight)',
|
||||
},
|
||||
'.cm-activeLineGutter': {
|
||||
backgroundColor: 'var(--color-code-lineHighlight)',
|
||||
},
|
||||
'.cm-gutters': {
|
||||
backgroundColor: isReadOnly
|
||||
? 'var(--color-code-background-readonly)'
|
||||
: 'var(--color-code-gutterBackground)',
|
||||
color: 'var(--color-code-gutterForeground)',
|
||||
borderTopLeftRadius: 'var(--border-radius-base)',
|
||||
borderBottomLeftRadius: 'var(--border-radius-base)',
|
||||
borderRightColor: 'var(--border-color-base)',
|
||||
},
|
||||
'.cm-scroller': {
|
||||
overflow: 'auto',
|
||||
maxHeight: '350px',
|
||||
},
|
||||
}),
|
||||
syntaxHighlighting(
|
||||
HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: '#c678dd' },
|
||||
{
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
color: '#e06c75',
|
||||
},
|
||||
{ tag: [tags.function(tags.variableName), tags.labelName], color: '#61afef' },
|
||||
{ tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: '#d19a66' },
|
||||
{ tag: [tags.definition(tags.name), tags.separator], color: '#abb2bf' },
|
||||
{
|
||||
tag: [
|
||||
tags.typeName,
|
||||
tags.className,
|
||||
tags.number,
|
||||
tags.changed,
|
||||
tags.annotation,
|
||||
tags.modifier,
|
||||
tags.self,
|
||||
tags.namespace,
|
||||
],
|
||||
color: '#e06c75',
|
||||
},
|
||||
{
|
||||
tag: [
|
||||
tags.operator,
|
||||
tags.operatorKeyword,
|
||||
tags.url,
|
||||
tags.escape,
|
||||
tags.regexp,
|
||||
tags.link,
|
||||
tags.special(tags.string),
|
||||
],
|
||||
color: '#56b6c2',
|
||||
},
|
||||
{ tag: [tags.meta, tags.comment], color: '#7d8799' },
|
||||
{ tag: tags.strong, fontWeight: 'bold' },
|
||||
{ tag: tags.emphasis, fontStyle: 'italic' },
|
||||
{ tag: tags.strikethrough, textDecoration: 'line-through' },
|
||||
{ tag: tags.link, color: '#7d8799', textDecoration: 'underline' },
|
||||
{ tag: tags.heading, fontWeight: 'bold', color: '#e06c75' },
|
||||
{ tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: '#d19a66' },
|
||||
{ tag: [tags.processingInstruction, tags.string, tags.inserted], color: '#98c379' },
|
||||
{ tag: tags.invalid, color: 'red', 'font-weight': 'bold' },
|
||||
]),
|
||||
),
|
||||
];
|
|
@ -1,26 +1,30 @@
|
|||
<template>
|
||||
<div :class="$style.editor">
|
||||
<div ref="jsEditor" class="ph-no-capture js-editor"></div>
|
||||
<slot name="suffix" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { acceptCompletion, autocompletion } from '@codemirror/autocomplete';
|
||||
import { indentWithTab, history, redo, toggleComment, undo } from '@codemirror/commands';
|
||||
import { foldGutter, indentOnInput } from '@codemirror/language';
|
||||
import { autocompletion } from '@codemirror/autocomplete';
|
||||
import { history, redo, toggleComment, undo } from '@codemirror/commands';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { foldGutter, indentOnInput } from '@codemirror/language';
|
||||
import { lintGutter } from '@codemirror/lint';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { EditorState, Prec } from '@codemirror/state';
|
||||
import type { ViewUpdate } from '@codemirror/view';
|
||||
import {
|
||||
dropCursor,
|
||||
EditorView,
|
||||
dropCursor,
|
||||
highlightActiveLine,
|
||||
highlightActiveLineGutter,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
} from '@codemirror/view';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -34,6 +38,10 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fillParent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
|
@ -57,18 +65,25 @@ export default defineComponent({
|
|||
EditorView.lineWrapping,
|
||||
EditorState.readOnly.of(isReadOnly),
|
||||
EditorView.editable.of(!isReadOnly),
|
||||
codeNodeEditorTheme({ isReadOnly, customMinHeight: this.rows }),
|
||||
codeNodeEditorTheme({
|
||||
isReadOnly,
|
||||
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||
minHeight: '20vh',
|
||||
rows: this.rows,
|
||||
}),
|
||||
];
|
||||
if (!isReadOnly) {
|
||||
extensions.push(
|
||||
history(),
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
...tabKeyMap,
|
||||
...enterKeyMap,
|
||||
{ key: 'Mod-z', run: undo },
|
||||
{ key: 'Mod-Shift-z', run: redo },
|
||||
{ key: 'Mod-/', run: toggleComment },
|
||||
{ key: 'Tab', run: acceptCompletion },
|
||||
indentWithTab,
|
||||
]),
|
||||
),
|
||||
lintGutter(),
|
||||
autocompletion(),
|
||||
indentOnInput(),
|
||||
|
@ -93,3 +108,13 @@ export default defineComponent({
|
|||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.editor {
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
<template>
|
||||
<div :class="$style.editor">
|
||||
<div ref="jsonEditor" class="ph-no-capture json-editor"></div>
|
||||
<slot name="suffix" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { autocompletion } from '@codemirror/autocomplete';
|
||||
import { indentWithTab, history, redo, undo } from '@codemirror/commands';
|
||||
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
||||
import { history, redo, undo } from '@codemirror/commands';
|
||||
import { json, jsonParseLinter } from '@codemirror/lang-json';
|
||||
import { lintGutter, linter as createLinter } from '@codemirror/lint';
|
||||
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
||||
import { linter as createLinter, lintGutter } from '@codemirror/lint';
|
||||
import type { Extension } from '@codemirror/state';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { EditorState, Prec } from '@codemirror/state';
|
||||
import type { ViewUpdate } from '@codemirror/view';
|
||||
import {
|
||||
dropCursor,
|
||||
EditorView,
|
||||
dropCursor,
|
||||
highlightActiveLine,
|
||||
highlightActiveLineGutter,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
} from '@codemirror/view';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -34,6 +38,10 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fillParent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
|
@ -57,16 +65,24 @@ export default defineComponent({
|
|||
EditorView.lineWrapping,
|
||||
EditorState.readOnly.of(isReadOnly),
|
||||
EditorView.editable.of(!isReadOnly),
|
||||
codeNodeEditorTheme({ isReadOnly, customMinHeight: this.rows }),
|
||||
codeNodeEditorTheme({
|
||||
isReadOnly,
|
||||
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||
minHeight: '20vh',
|
||||
rows: this.rows,
|
||||
}),
|
||||
];
|
||||
if (!isReadOnly) {
|
||||
extensions.push(
|
||||
history(),
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
indentWithTab,
|
||||
...tabKeyMap,
|
||||
...enterKeyMap,
|
||||
{ key: 'Mod-z', run: undo },
|
||||
{ key: 'Mod-Shift-z', run: redo },
|
||||
]),
|
||||
),
|
||||
createLinter(jsonParseLinter()),
|
||||
lintGutter(),
|
||||
autocompletion(),
|
||||
|
@ -93,3 +109,13 @@ export default defineComponent({
|
|||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.editor {
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1166,7 +1166,7 @@ export default defineComponent({
|
|||
|
||||
.node-parameters-wrapper {
|
||||
overflow-y: auto;
|
||||
padding: 0 var(--spacing-m) 200px var(--spacing-m);
|
||||
padding: 0 var(--spacing-m) var(--spacing-l) var(--spacing-m);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
:is-read-only="isReadOnly"
|
||||
:redact-values="shouldRedactValue"
|
||||
@closeDialog="closeExpressionEditDialog"
|
||||
@update:modelValue="expressionUpdated"
|
||||
@update:model-value="expressionUpdated"
|
||||
></ExpressionEdit>
|
||||
<div class="parameter-input ignore-key-press" :style="parameterInputWrapperStyle">
|
||||
<ResourceLocator
|
||||
|
@ -34,7 +34,7 @@
|
|||
:node="node"
|
||||
:path="path"
|
||||
:event-bus="eventBus"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
@modalOpenerClick="openExpressionEditorModal"
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
|
@ -50,7 +50,7 @@
|
|||
:path="path"
|
||||
:additional-expression-data="additionalExpressionData"
|
||||
:class="{ 'ph-no-capture': shouldRedactValue }"
|
||||
@update:modelValue="expressionUpdated"
|
||||
@update:model-value="expressionUpdated"
|
||||
@modalOpenerClick="openExpressionEditorModal"
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
|
@ -62,23 +62,60 @@
|
|||
"
|
||||
>
|
||||
<el-dialog
|
||||
v-if="codeEditDialogVisible"
|
||||
:model-value="true"
|
||||
:model-value="codeEditDialogVisible"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
width="80%"
|
||||
:title="`${i18n.baseText('codeEdit.edit')} ${$locale
|
||||
.nodeText()
|
||||
.inputLabelDisplayName(parameter, path)}`"
|
||||
:before-close="closeCodeEditDialog"
|
||||
data-test-id="code-editor-fullscreen"
|
||||
>
|
||||
<div class="ignore-key-press">
|
||||
<div :key="codeEditDialogVisible" class="ignore-key-press code-edit-dialog">
|
||||
<CodeNodeEditor
|
||||
v-if="editorType === 'codeNodeEditor'"
|
||||
:model-value="modelValue"
|
||||
:default-value="parameter.default"
|
||||
:language="editorLanguage"
|
||||
:is-read-only="isReadOnly"
|
||||
@update:modelValue="expressionUpdated"
|
||||
fill-parent
|
||||
@update:model-value="valueChangedDebounced"
|
||||
/>
|
||||
<HtmlEditor
|
||||
v-else-if="editorType === 'htmlEditor'"
|
||||
:model-value="modelValue"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
:disable-expression-coloring="!isHtmlNode(node)"
|
||||
:disable-expression-completions="!isHtmlNode(node)"
|
||||
fill-parent
|
||||
@update:model-value="valueChangedDebounced"
|
||||
/>
|
||||
<SqlEditor
|
||||
v-else-if="editorType === 'sqlEditor'"
|
||||
:model-value="modelValue"
|
||||
:dialect="getArgument('sqlDialect')"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
fill-parent
|
||||
@update:model-value="valueChangedDebounced"
|
||||
/>
|
||||
<JsEditor
|
||||
v-else-if="editorType === 'jsEditor'"
|
||||
:model-value="modelValue"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
fill-parent
|
||||
@update:model-value="valueChangedDebounced"
|
||||
/>
|
||||
|
||||
<JsonEditor
|
||||
v-else-if="parameter.type === 'json'"
|
||||
:model-value="modelValue"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
fill-parent
|
||||
@update:model-value="valueChangedDebounced"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
@ -90,11 +127,12 @@
|
|||
:path="path"
|
||||
:is-read-only="isReadOnly"
|
||||
@closeDialog="closeTextEditDialog"
|
||||
@update:modelValue="expressionUpdated"
|
||||
@update:model-value="expressionUpdated"
|
||||
></TextEdit>
|
||||
|
||||
<CodeNodeEditor
|
||||
v-if="editorType === 'codeNodeEditor' && isCodeNode(node)"
|
||||
:key="codeEditDialogVisible"
|
||||
:mode="node.parameters.mode"
|
||||
:model-value="modelValue"
|
||||
:default-value="parameter.default"
|
||||
|
@ -102,43 +140,102 @@
|
|||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
:ai-button-enabled="settingsStore.isCloudDeployment"
|
||||
@update:modelValue="valueChangedDebounced"
|
||||
@update:model-value="valueChangedDebounced"
|
||||
>
|
||||
<template #suffix>
|
||||
<n8n-icon
|
||||
data-test-id="code-editor-fullscreen-button"
|
||||
icon="external-link-alt"
|
||||
size="xsmall"
|
||||
class="textarea-modal-opener"
|
||||
:title="$locale.baseText('parameterInput.openEditWindow')"
|
||||
@click="displayEditDialog()"
|
||||
/>
|
||||
</template>
|
||||
</CodeNodeEditor>
|
||||
|
||||
<HtmlEditor
|
||||
v-else-if="editorType === 'htmlEditor'"
|
||||
:key="codeEditDialogVisible"
|
||||
:model-value="modelValue"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
:disable-expression-coloring="!isHtmlNode(node)"
|
||||
:disable-expression-completions="!isHtmlNode(node)"
|
||||
@update:modelValue="valueChangedDebounced"
|
||||
@update:model-value="valueChangedDebounced"
|
||||
>
|
||||
<template #suffix>
|
||||
<n8n-icon
|
||||
data-test-id="code-editor-fullscreen-button"
|
||||
icon="external-link-alt"
|
||||
size="xsmall"
|
||||
class="textarea-modal-opener"
|
||||
:title="$locale.baseText('parameterInput.openEditWindow')"
|
||||
@click="displayEditDialog()"
|
||||
/>
|
||||
</template>
|
||||
</HtmlEditor>
|
||||
|
||||
<SqlEditor
|
||||
v-else-if="editorType === 'sqlEditor'"
|
||||
:key="codeEditDialogVisible"
|
||||
:model-value="modelValue"
|
||||
:dialect="getArgument('sqlDialect')"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
@update:modelValue="valueChangedDebounced"
|
||||
@update:model-value="valueChangedDebounced"
|
||||
>
|
||||
<template #suffix>
|
||||
<n8n-icon
|
||||
data-test-id="code-editor-fullscreen-button"
|
||||
icon="external-link-alt"
|
||||
size="xsmall"
|
||||
class="textarea-modal-opener"
|
||||
:title="$locale.baseText('parameterInput.openEditWindow')"
|
||||
@click="displayEditDialog()"
|
||||
/>
|
||||
</template>
|
||||
</SqlEditor>
|
||||
|
||||
<JsEditor
|
||||
v-else-if="editorType === 'jsEditor'"
|
||||
:key="codeEditDialogVisible"
|
||||
:model-value="modelValue"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
@update:modelValue="valueChangedDebounced"
|
||||
@update:model-value="valueChangedDebounced"
|
||||
>
|
||||
<template #suffix>
|
||||
<n8n-icon
|
||||
data-test-id="code-editor-fullscreen-button"
|
||||
icon="external-link-alt"
|
||||
size="xsmall"
|
||||
class="textarea-modal-opener"
|
||||
:title="$locale.baseText('parameterInput.openEditWindow')"
|
||||
@click="displayEditDialog()"
|
||||
/>
|
||||
</template>
|
||||
</JsEditor>
|
||||
|
||||
<JsonEditor
|
||||
v-else-if="parameter.type === 'json'"
|
||||
:key="codeEditDialogVisible"
|
||||
:model-value="modelValue"
|
||||
:is-read-only="isReadOnly"
|
||||
:rows="getArgument('rows')"
|
||||
@update:modelValue="valueChangedDebounced"
|
||||
@update:model-value="valueChangedDebounced"
|
||||
>
|
||||
<template #suffix>
|
||||
<n8n-icon
|
||||
data-test-id="code-editor-fullscreen-button"
|
||||
icon="external-link-alt"
|
||||
size="xsmall"
|
||||
class="textarea-modal-opener"
|
||||
:title="$locale.baseText('parameterInput.openEditWindow')"
|
||||
@click="displayEditDialog()"
|
||||
/>
|
||||
</template>
|
||||
</JsonEditor>
|
||||
|
||||
<div v-else-if="editorType" class="readonly-code clickable" @click="displayEditDialog()">
|
||||
<CodeNodeEditor
|
||||
|
@ -161,7 +258,7 @@
|
|||
:disabled="isReadOnly"
|
||||
:title="displayTitle"
|
||||
:placeholder="getPlaceholder()"
|
||||
@update:modelValue="valueChanged($event) && onUpdateTextInput($event)"
|
||||
@update:model-value="valueChanged($event) && onUpdateTextInput($event)"
|
||||
@keydown.stop
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
|
@ -194,7 +291,7 @@
|
|||
:show-alpha="getArgument('showAlpha')"
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
/>
|
||||
<n8n-input
|
||||
v-model="tempValue"
|
||||
|
@ -202,7 +299,7 @@
|
|||
type="text"
|
||||
:disabled="isReadOnly"
|
||||
:title="displayTitle"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
@keydown.stop
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
|
@ -226,7 +323,7 @@
|
|||
"
|
||||
:picker-options="dateTimePickerOptions"
|
||||
:class="{ 'ph-no-capture': shouldRedactValue }"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
@keydown.stop
|
||||
|
@ -245,7 +342,7 @@
|
|||
:class="{ 'ph-no-capture': shouldRedactValue }"
|
||||
:title="displayTitle"
|
||||
:placeholder="parameter.placeholder"
|
||||
@update:modelValue="onUpdateTextInput"
|
||||
@update:model-value="onUpdateTextInput"
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
@keydown.stop
|
||||
|
@ -262,7 +359,7 @@
|
|||
:is-read-only="isReadOnly"
|
||||
:display-title="displayTitle"
|
||||
@credentialSelected="credentialSelected"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
@setFocus="setFocus"
|
||||
@onBlur="onBlur"
|
||||
>
|
||||
|
@ -283,7 +380,7 @@
|
|||
:loading="remoteParameterOptionsLoading"
|
||||
:disabled="isReadOnly || remoteParameterOptionsLoading"
|
||||
:title="displayTitle"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
@keydown.stop
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
|
@ -321,7 +418,7 @@
|
|||
:disabled="isReadOnly || remoteParameterOptionsLoading"
|
||||
:title="displayTitle"
|
||||
:placeholder="i18n.baseText('parameterInput.select')"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
@keydown.stop
|
||||
@focus="setFocus"
|
||||
@blur="onBlur"
|
||||
|
@ -358,7 +455,7 @@
|
|||
active-color="#13ce66"
|
||||
:model-value="displayValue"
|
||||
:disabled="isReadOnly"
|
||||
@update:modelValue="valueChanged"
|
||||
@update:model-value="valueChanged"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -1056,7 +1153,7 @@ export default defineComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.editorType) {
|
||||
if (this.editorType || this.parameter.type === 'json') {
|
||||
this.codeEditDialogVisible = true;
|
||||
} else {
|
||||
this.textEditDialogVisible = true;
|
||||
|
@ -1418,4 +1515,12 @@ export default defineComponent({
|
|||
.invalid {
|
||||
border-color: var(--color-danger);
|
||||
}
|
||||
|
||||
.code-edit-dialog {
|
||||
height: 70vh;
|
||||
|
||||
.code-node-editor {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<div v-on-click-outside="onBlur" :class="$style.sqlEditor">
|
||||
<div ref="sqlEditor" data-test-id="sql-editor-container"></div>
|
||||
<slot name="suffix" />
|
||||
<InlineExpressionEditorOutput
|
||||
v-if="!fillParent"
|
||||
:segments="segments"
|
||||
:is-read-only="isReadOnly"
|
||||
:visible="isFocused"
|
||||
|
@ -11,41 +13,42 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { acceptCompletion, autocompletion, ifNotIn } from '@codemirror/autocomplete';
|
||||
import { indentWithTab, history, redo, toggleComment, undo } from '@codemirror/commands';
|
||||
import { bracketMatching, foldGutter, indentOnInput, LanguageSupport } from '@codemirror/language';
|
||||
import InlineExpressionEditorOutput from '@/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue';
|
||||
import { EXPRESSIONS_DOCS_URL } from '@/constants';
|
||||
import { codeNodeEditorEventBus } from '@/event-bus';
|
||||
import { expressionManager } from '@/mixins/expressionManager';
|
||||
import { n8nCompletionSources } from '@/plugins/codemirror/completions/addCompletions';
|
||||
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
|
||||
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
|
||||
import { autocompletion, ifNotIn } from '@codemirror/autocomplete';
|
||||
import { history, redo, toggleComment, undo } from '@codemirror/commands';
|
||||
import { LanguageSupport, bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
||||
import { type Extension, type Line, Prec } from '@codemirror/state';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import type { Line, Extension } from '@codemirror/state';
|
||||
import type { ViewUpdate } from '@codemirror/view';
|
||||
import {
|
||||
dropCursor,
|
||||
EditorView,
|
||||
dropCursor,
|
||||
highlightActiveLine,
|
||||
highlightActiveLineGutter,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
} from '@codemirror/view';
|
||||
import type { ViewUpdate } from '@codemirror/view';
|
||||
import type { SQLDialect as SQLDialectType } from '@n8n/codemirror-lang-sql';
|
||||
import {
|
||||
MSSQL,
|
||||
MySQL,
|
||||
PostgreSQL,
|
||||
StandardSQL,
|
||||
MariaSQL,
|
||||
SQLite,
|
||||
Cassandra,
|
||||
MSSQL,
|
||||
MariaSQL,
|
||||
MySQL,
|
||||
PLSQL,
|
||||
PostgreSQL,
|
||||
SQLite,
|
||||
StandardSQL,
|
||||
keywordCompletionSource,
|
||||
} from '@n8n/codemirror-lang-sql';
|
||||
import type { SQLDialect as SQLDialectType } from '@n8n/codemirror-lang-sql';
|
||||
import { defineComponent } from 'vue';
|
||||
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||
import { n8nCompletionSources } from '@/plugins/codemirror/completions/addCompletions';
|
||||
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
|
||||
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
|
||||
import { expressionManager } from '@/mixins/expressionManager';
|
||||
import InlineExpressionEditorOutput from '@/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue';
|
||||
import { EXPRESSIONS_DOCS_URL } from '@/constants';
|
||||
import { codeNodeEditorEventBus } from '@/event-bus';
|
||||
|
||||
const SQL_DIALECTS = {
|
||||
StandardSQL,
|
||||
|
@ -88,6 +91,10 @@ export default defineComponent({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
fillParent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
|
@ -129,8 +136,9 @@ export default defineComponent({
|
|||
expressionInputHandler(),
|
||||
codeNodeEditorTheme({
|
||||
isReadOnly: this.isReadOnly,
|
||||
customMaxHeight: '350px',
|
||||
customMinHeight: this.rows,
|
||||
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||
minHeight: '10vh',
|
||||
rows: this.rows,
|
||||
}),
|
||||
lineNumbers(),
|
||||
EditorView.lineWrapping,
|
||||
|
@ -146,13 +154,15 @@ export default defineComponent({
|
|||
if (!this.isReadOnly) {
|
||||
extensions.push(
|
||||
history(),
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
...tabKeyMap,
|
||||
...enterKeyMap,
|
||||
{ key: 'Mod-z', run: undo },
|
||||
{ key: 'Mod-Shift-z', run: redo },
|
||||
{ key: 'Mod-/', run: toggleComment },
|
||||
{ key: 'Tab', run: acceptCompletion },
|
||||
indentWithTab,
|
||||
]),
|
||||
),
|
||||
autocompletion(),
|
||||
indentOnInput(),
|
||||
highlightActiveLine(),
|
||||
|
@ -233,5 +243,10 @@ export default defineComponent({
|
|||
<style module lang="scss">
|
||||
.sqlEditor {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -192,15 +192,7 @@
|
|||
border: var(--border-base);
|
||||
box-shadow: 0px 6px 16px rgb(68 28 23 / 6%);
|
||||
border-radius: 8px;
|
||||
margin-top: 15vh;
|
||||
|
||||
@media (max-height: 1050px) {
|
||||
margin: 4em auto !important;
|
||||
}
|
||||
|
||||
@media (max-height: 930px) {
|
||||
margin: 1em auto !important;
|
||||
}
|
||||
&.classic {
|
||||
.el-dialog__header {
|
||||
padding: 15px 20px;
|
||||
|
|
|
@ -7,7 +7,6 @@ const commonDescription: INodeProperties = {
|
|||
typeOptions: {
|
||||
editor: 'codeNodeEditor',
|
||||
editorLanguage: 'javaScript',
|
||||
rows: 5,
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
|
|
|
@ -7,7 +7,6 @@ const commonDescription: INodeProperties = {
|
|||
typeOptions: {
|
||||
editor: 'codeNodeEditor',
|
||||
editorLanguage: 'python',
|
||||
rows: 5,
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
|
|
|
@ -20,7 +20,6 @@ const properties: INodeProperties[] = [
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
},
|
||||
displayOptions: {
|
||||
hide: {
|
||||
|
@ -39,7 +38,6 @@ const properties: INodeProperties[] = [
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
|
|
|
@ -88,7 +88,6 @@ export class MicrosoftSql implements INodeType {
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'MSSQL',
|
||||
},
|
||||
displayOptions: {
|
||||
|
|
|
@ -79,7 +79,6 @@ const versionDescription: INodeTypeDescription = {
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'MySQL',
|
||||
},
|
||||
displayOptions: {
|
||||
|
|
|
@ -26,7 +26,6 @@ const properties: INodeProperties[] = [
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'MySQL',
|
||||
},
|
||||
hint: 'Consider using query parameters to prevent SQL injection attacks. Add them in the options below',
|
||||
|
|
|
@ -77,7 +77,6 @@ const versionDescription: INodeTypeDescription = {
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'PostgreSQL',
|
||||
},
|
||||
displayOptions: {
|
||||
|
|
|
@ -26,7 +26,6 @@ const properties: INodeProperties[] = [
|
|||
"The SQL query to execute. You can use n8n expressions and $1, $2, $3, etc to refer to the 'Query Parameters' set in options below.",
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'PostgreSQL',
|
||||
},
|
||||
hint: 'Consider using query parameters to prevent SQL injection attacks. Add them in the options below',
|
||||
|
|
|
@ -63,7 +63,6 @@ export class QuestDb implements INodeType {
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'PostgreSQL',
|
||||
},
|
||||
displayOptions: {
|
||||
|
|
|
@ -19,7 +19,7 @@ const properties: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
rows: 5,
|
||||
},
|
||||
default: '{\n "my_field_1": "value",\n "my_field_2": 1\n}',
|
||||
default: '{\n "my_field_1": "value",\n "my_field_2": 1\n}\n',
|
||||
validateType: 'object',
|
||||
ignoreValidationDuringExecution: true,
|
||||
},
|
||||
|
|
|
@ -69,7 +69,6 @@ export class Snowflake implements INodeType {
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
|
|
|
@ -67,7 +67,6 @@ export class TimescaleDb implements INodeType {
|
|||
noDataExpression: true,
|
||||
typeOptions: {
|
||||
editor: 'sqlEditor',
|
||||
rows: 5,
|
||||
sqlDialect: 'PostgreSQL',
|
||||
},
|
||||
displayOptions: {
|
||||
|
|
Loading…
Reference in a new issue