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 { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
||||||
import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils';
|
import { getVisiblePopper, getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
@ -66,6 +66,8 @@ describe('Resource Locator', () => {
|
||||||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' });
|
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' });
|
||||||
|
|
||||||
ndv.getters.resourceLocatorInput('rlc').click();
|
ndv.getters.resourceLocatorInput('rlc').click();
|
||||||
|
|
||||||
|
cy.getByTestId('rlc-item').should('exist');
|
||||||
getVisiblePopper()
|
getVisiblePopper()
|
||||||
.should('have.length', 1)
|
.should('have.length', 1)
|
||||||
.findChildByTestId('rlc-item')
|
.findChildByTestId('rlc-item')
|
||||||
|
@ -73,9 +75,11 @@ describe('Resource Locator', () => {
|
||||||
|
|
||||||
ndv.actions.setInvalidExpression({ fieldName: 'fieldId' });
|
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();
|
ndv.getters.resourceLocatorInput('rlc').click();
|
||||||
|
|
||||||
|
cy.getByTestId('rlc-item').should('exist');
|
||||||
getVisiblePopper()
|
getVisiblePopper()
|
||||||
.should('have.length', 1)
|
.should('have.length', 1)
|
||||||
.findChildByTestId('rlc-item')
|
.findChildByTestId('rlc-item')
|
||||||
|
|
|
@ -302,7 +302,7 @@ describe('NDV', () => {
|
||||||
|
|
||||||
ndv.actions.setInvalidExpression({ fieldName: 'fieldId', delay: 200 });
|
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();
|
ndv.getters.parameterInput('remoteOptions').click();
|
||||||
|
|
||||||
|
@ -320,7 +320,7 @@ describe('NDV', () => {
|
||||||
|
|
||||||
ndv.actions.setInvalidExpression({ fieldName: 'otherField', delay: 50 });
|
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();
|
ndv.getters.parameterInput('remoteOptions').click();
|
||||||
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
|
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
|
||||||
|
@ -360,6 +360,15 @@ describe('NDV', () => {
|
||||||
ndv.getters.nodeExecuteButton().should('be.visible');
|
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', () => {
|
it('should not retrieve remote options when a parameter value changes', () => {
|
||||||
cy.intercept('/rest/dynamic-node-parameters/options?**', cy.spy().as('fetchParameterOptions'));
|
cy.intercept('/rest/dynamic-node-parameters/options?**', cy.spy().as('fetchParameterOptions'));
|
||||||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
||||||
|
@ -370,106 +379,126 @@ describe('NDV', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('floating nodes', () => {
|
describe('floating nodes', () => {
|
||||||
function getFloatingNodeByPosition(position: 'inputMain' | 'outputMain' | 'outputSub'| 'inputSub') {
|
function getFloatingNodeByPosition(
|
||||||
|
position: 'inputMain' | 'outputMain' | 'outputSub' | 'inputSub',
|
||||||
|
) {
|
||||||
return cy.get(`[data-node-placement=${position}]`);
|
return cy.get(`[data-node-placement=${position}]`);
|
||||||
}
|
}
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.createFixtureWorkflow('Floating_Nodes.json', `Floating Nodes`);
|
cy.createFixtureWorkflow('Floating_Nodes.json', `Floating Nodes`);
|
||||||
workflowPage.getters.canvasNodes().first().dblclick()
|
workflowPage.getters.canvasNodes().first().dblclick();
|
||||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||||
getFloatingNodeByPosition("outputMain").should('exist');
|
getFloatingNodeByPosition('outputMain').should('exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should traverse floating nodes with mouse', () => {
|
it('should traverse floating nodes with mouse', () => {
|
||||||
// Traverse 4 connected node forwards
|
// Traverse 4 connected node forwards
|
||||||
Array.from(Array(4).keys()).forEach(i => {
|
Array.from(Array(4).keys()).forEach((i) => {
|
||||||
getFloatingNodeByPosition("outputMain").click({ force: true});
|
getFloatingNodeByPosition('outputMain').click({ force: true });
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
||||||
getFloatingNodeByPosition("inputMain").should('exist');
|
getFloatingNodeByPosition('inputMain').should('exist');
|
||||||
getFloatingNodeByPosition("outputMain").should('exist');
|
getFloatingNodeByPosition('outputMain').should('exist');
|
||||||
ndv.actions.close();
|
ndv.actions.close();
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
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();
|
workflowPage.getters.selectedNodes().first().dblclick();
|
||||||
})
|
});
|
||||||
|
|
||||||
getFloatingNodeByPosition("outputMain").click({ force: true});
|
getFloatingNodeByPosition('outputMain').click({ force: true });
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||||
getFloatingNodeByPosition("inputSub").should('exist');
|
getFloatingNodeByPosition('inputSub').should('exist');
|
||||||
getFloatingNodeByPosition("inputSub").click({ force: true});
|
getFloatingNodeByPosition('inputSub').click({ force: true });
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
||||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||||
getFloatingNodeByPosition("outputMain").should('not.exist');
|
getFloatingNodeByPosition('outputMain').should('not.exist');
|
||||||
getFloatingNodeByPosition("outputSub").should('exist');
|
getFloatingNodeByPosition('outputSub').should('exist');
|
||||||
ndv.actions.close();
|
ndv.actions.close();
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||||
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
||||||
workflowPage.getters.selectedNodes().first().dblclick();
|
workflowPage.getters.selectedNodes().first().dblclick();
|
||||||
getFloatingNodeByPosition("outputSub").click({ force: true});
|
getFloatingNodeByPosition('outputSub').click({ force: true });
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||||
|
|
||||||
// Traverse 4 connected node backwards
|
// Traverse 4 connected node backwards
|
||||||
Array.from(Array(4).keys()).forEach(i => {
|
Array.from(Array(4).keys()).forEach((i) => {
|
||||||
getFloatingNodeByPosition("inputMain").click({ force: true});
|
getFloatingNodeByPosition('inputMain').click({ force: true });
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - (i)}`);
|
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
||||||
getFloatingNodeByPosition("outputMain").should('exist');
|
getFloatingNodeByPosition('outputMain').should('exist');
|
||||||
getFloatingNodeByPosition("inputMain").should('exist');
|
getFloatingNodeByPosition('inputMain').should('exist');
|
||||||
})
|
});
|
||||||
getFloatingNodeByPosition("inputMain").click({ force: true});
|
getFloatingNodeByPosition('inputMain').click({ force: true });
|
||||||
workflowPage.getters.selectedNodes().first().should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
workflowPage.getters
|
||||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
.selectedNodes()
|
||||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
.first()
|
||||||
getFloatingNodeByPosition("outputSub").should('not.exist');
|
.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();
|
ndv.actions.close();
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
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', () => {
|
it('should traverse floating nodes with mouse', () => {
|
||||||
// Traverse 4 connected node forwards
|
// Traverse 4 connected node forwards
|
||||||
Array.from(Array(4).keys()).forEach(i => {
|
Array.from(Array(4).keys()).forEach((i) => {
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight'])
|
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
||||||
getFloatingNodeByPosition("inputMain").should('exist');
|
getFloatingNodeByPosition('inputMain').should('exist');
|
||||||
getFloatingNodeByPosition("outputMain").should('exist');
|
getFloatingNodeByPosition('outputMain').should('exist');
|
||||||
ndv.actions.close();
|
ndv.actions.close();
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
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();
|
workflowPage.getters.selectedNodes().first().dblclick();
|
||||||
})
|
});
|
||||||
|
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight'])
|
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||||
getFloatingNodeByPosition("inputSub").should('exist');
|
getFloatingNodeByPosition('inputSub').should('exist');
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowDown'])
|
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowDown']);
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
||||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||||
getFloatingNodeByPosition("outputMain").should('not.exist');
|
getFloatingNodeByPosition('outputMain').should('not.exist');
|
||||||
getFloatingNodeByPosition("outputSub").should('exist');
|
getFloatingNodeByPosition('outputSub').should('exist');
|
||||||
ndv.actions.close();
|
ndv.actions.close();
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||||
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
||||||
workflowPage.getters.selectedNodes().first().dblclick();
|
workflowPage.getters.selectedNodes().first().dblclick();
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowUp'])
|
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowUp']);
|
||||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||||
|
|
||||||
// Traverse 4 connected node backwards
|
// Traverse 4 connected node backwards
|
||||||
Array.from(Array(4).keys()).forEach(i => {
|
Array.from(Array(4).keys()).forEach((i) => {
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft'])
|
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
||||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - (i)}`);
|
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
||||||
getFloatingNodeByPosition("outputMain").should('exist');
|
getFloatingNodeByPosition('outputMain').should('exist');
|
||||||
getFloatingNodeByPosition("inputMain").should('exist');
|
getFloatingNodeByPosition('inputMain').should('exist');
|
||||||
})
|
});
|
||||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft'])
|
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
||||||
workflowPage.getters.selectedNodes().first().should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
workflowPage.getters
|
||||||
getFloatingNodeByPosition("inputMain").should('not.exist');
|
.selectedNodes()
|
||||||
getFloatingNodeByPosition("inputSub").should('not.exist');
|
.first()
|
||||||
getFloatingNodeByPosition("outputSub").should('not.exist');
|
.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();
|
ndv.actions.close();
|
||||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
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.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: '/' });
|
cy.document().trigger('keyup', { key: '/' });
|
||||||
ndv.getters.searchInput().filter(':focus').type('<lib');
|
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).should('include.text', 'JSON');
|
||||||
ndv.getters.outputDisplayMode().find('label').eq(1).click();
|
ndv.getters.outputDisplayMode().find('label').eq(1).click();
|
||||||
|
|
||||||
ndv.getters.outputDataContainer().find('.json-data').should('be.visible');
|
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
|
||||||
ndv.getters.outputDataContainer().find('mark').should('have.text', '<lib')
|
.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).should('include.text', 'Schema');
|
||||||
ndv.getters.outputDisplayMode().find('label').eq(2).click({force: true});
|
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
|
||||||
|
.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', () => {
|
it('should properly show node execution indicator', () => {
|
||||||
|
@ -546,7 +586,10 @@ describe('NDV', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should handle mismatched option attributes', () => {
|
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
|
// Add some attributes in Create operation
|
||||||
cy.getByTestId('parameter-item').contains('Add Attributes').click();
|
cy.getByTestId('parameter-item').contains('Add Attributes').click();
|
||||||
ndv.actions.changeNodeOperation('Update');
|
ndv.actions.changeNodeOperation('Update');
|
||||||
|
@ -556,7 +599,10 @@ describe('NDV', () => {
|
||||||
|
|
||||||
it('Should keep RLC values after operation change', () => {
|
it('Should keep RLC values after operation change', () => {
|
||||||
const TEST_DOC_ID = '1111';
|
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.setRLCValue('documentId', TEST_DOC_ID);
|
||||||
ndv.actions.changeNodeOperation('Update Row');
|
ndv.actions.changeNodeOperation('Update Row');
|
||||||
ndv.getters.resourceLocatorInput('documentId').find('input').should('have.value', TEST_DOC_ID);
|
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'),
|
pagination: () => cy.getByTestId('ndv-data-pagination'),
|
||||||
nodeVersion: () => cy.getByTestId('node-version'),
|
nodeVersion: () => cy.getByTestId('node-version'),
|
||||||
nodeSettingsTab: () => cy.getByTestId('tab-settings'),
|
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'),
|
nodeRunSuccessIndicator: () => cy.getByTestId('node-run-info-success'),
|
||||||
nodeRunErrorIndicator: () => cy.getByTestId('node-run-info-danger'),
|
nodeRunErrorIndicator: () => cy.getByTestId('node-run-info-danger'),
|
||||||
};
|
};
|
||||||
|
@ -251,9 +254,15 @@ export class NDV extends BasePage {
|
||||||
openSettings: () => {
|
openSettings: () => {
|
||||||
this.getters.nodeSettingsTab().click();
|
this.getters.nodeSettingsTab().click();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openCodeEditorFullscreen: () => {
|
||||||
|
this.getters.codeEditorFullscreenButton().click({ force: true });
|
||||||
|
},
|
||||||
changeNodeOperation: (operation: string) => {
|
changeNodeOperation: (operation: string) => {
|
||||||
this.getters.parameterInput('operation').click();
|
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);
|
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');
|
cy.get(draggableSelector).trigger('mousedown');
|
||||||
}
|
}
|
||||||
// We don't chain these commands to make sure cy.get is re-trying correctly
|
// 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).realMouseMove(pageX, pageY);
|
||||||
cy.get(droppableSelector).realHover();
|
cy.get(droppableSelector).realHover();
|
||||||
cy.get(droppableSelector).realMouseUp();
|
cy.get(droppableSelector).realMouseUp();
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: inherit;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +61,6 @@
|
||||||
|
|
||||||
@include mixins.e(header) {
|
@include mixins.e(header) {
|
||||||
padding: var.$dialog-padding-primary;
|
padding: var.$dialog-padding-primary;
|
||||||
padding-bottom: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include mixins.e(headerbtn) {
|
@include mixins.e(headerbtn) {
|
||||||
|
@ -95,6 +95,7 @@
|
||||||
|
|
||||||
@include mixins.e(body) {
|
@include mixins.e(body) {
|
||||||
padding: var.$dialog-padding-primary;
|
padding: var.$dialog-padding-primary;
|
||||||
|
padding-top: 0;
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
name="code"
|
name="code"
|
||||||
data-test-id="code-node-tab-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>
|
||||||
<el-tab-pane
|
<el-tab-pane
|
||||||
:label="$locale.baseText('codeNodeEditor.tabs.askAi')"
|
:label="$locale.baseText('codeNodeEditor.tabs.askAi')"
|
||||||
|
@ -35,7 +39,10 @@
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<!-- If AskAi not enabled, there's no point in rendering 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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -82,6 +89,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
fillParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
mode: {
|
mode: {
|
||||||
type: String as PropType<CodeExecutionMode>,
|
type: String as PropType<CodeExecutionMode>,
|
||||||
validator: (value: CodeExecutionMode): boolean => CODE_EXECUTION_MODES.includes(value),
|
validator: (value: CodeExecutionMode): boolean => CODE_EXECUTION_MODES.includes(value),
|
||||||
|
@ -193,7 +204,12 @@ export default defineComponent({
|
||||||
...readOnlyEditorExtensions,
|
...readOnlyEditorExtensions,
|
||||||
EditorState.readOnly.of(isReadOnly),
|
EditorState.readOnly.of(isReadOnly),
|
||||||
EditorView.editable.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) {
|
if (!isReadOnly) {
|
||||||
|
@ -384,15 +400,9 @@ export default defineComponent({
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.code-node-editor-container {
|
.code-node-editor-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& > div {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ask-ai-button {
|
.fillHeight {
|
||||||
position: absolute;
|
height: 100%;
|
||||||
top: var(--spacing-2xs);
|
|
||||||
right: var(--spacing-2xs);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,12 +6,14 @@ import {
|
||||||
highlightSpecialChars,
|
highlightSpecialChars,
|
||||||
keymap,
|
keymap,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
|
type KeyBinding,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
||||||
import { acceptCompletion } from '@codemirror/autocomplete';
|
import { acceptCompletion, selectedCompletion } from '@codemirror/autocomplete';
|
||||||
import {
|
import {
|
||||||
history,
|
history,
|
||||||
indentWithTab,
|
indentLess,
|
||||||
|
indentMore,
|
||||||
insertNewlineAndIndent,
|
insertNewlineAndIndent,
|
||||||
toggleComment,
|
toggleComment,
|
||||||
redo,
|
redo,
|
||||||
|
@ -19,7 +21,7 @@ import {
|
||||||
undo,
|
undo,
|
||||||
} from '@codemirror/commands';
|
} from '@codemirror/commands';
|
||||||
import { lintGutter } from '@codemirror/lint';
|
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';
|
import { codeInputHandler } from '@/plugins/codemirror/inputHandlers/code.inputHandler';
|
||||||
|
|
||||||
|
@ -29,6 +31,42 @@ export const readOnlyEditorExtensions: readonly Extension[] = [
|
||||||
highlightSpecialChars(),
|
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[] = [
|
export const writableEditorExtensions: readonly Extension[] = [
|
||||||
history(),
|
history(),
|
||||||
lintGutter(),
|
lintGutter(),
|
||||||
|
@ -39,14 +77,14 @@ export const writableEditorExtensions: readonly Extension[] = [
|
||||||
bracketMatching(),
|
bracketMatching(),
|
||||||
highlightActiveLine(),
|
highlightActiveLine(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
keymap.of([
|
Prec.highest(
|
||||||
{ key: 'Enter', run: insertNewlineAndIndent },
|
keymap.of([
|
||||||
{ key: 'Tab', run: acceptCompletion },
|
...tabKeyMap,
|
||||||
{ key: 'Enter', run: acceptCompletion },
|
...enterKeyMap,
|
||||||
{ key: 'Mod-/', run: toggleComment },
|
{ key: 'Mod-/', run: toggleComment },
|
||||||
{ key: 'Mod-z', run: undo },
|
{ key: 'Mod-z', run: undo },
|
||||||
{ key: 'Mod-Shift-z', run: redo },
|
{ key: 'Mod-Shift-z', run: redo },
|
||||||
{ key: 'Backspace', run: deleteCharBackward, shift: deleteCharBackward },
|
{ key: 'Backspace', run: deleteCharBackward, shift: deleteCharBackward },
|
||||||
indentWithTab,
|
]),
|
||||||
]),
|
),
|
||||||
];
|
];
|
||||||
|
|
|
@ -29,14 +29,18 @@ const BASE_STYLING = {
|
||||||
|
|
||||||
interface ThemeSettings {
|
interface ThemeSettings {
|
||||||
isReadOnly?: boolean;
|
isReadOnly?: boolean;
|
||||||
customMaxHeight?: string;
|
maxHeight?: string;
|
||||||
customMinHeight?: number;
|
minHeight?: string;
|
||||||
|
rows?: number;
|
||||||
|
highlightColors?: 'default' | 'html';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codeNodeEditorTheme = ({
|
export const codeNodeEditorTheme = ({
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
customMaxHeight,
|
minHeight,
|
||||||
customMinHeight,
|
maxHeight,
|
||||||
|
rows,
|
||||||
|
highlightColors,
|
||||||
}: ThemeSettings) => [
|
}: ThemeSettings) => [
|
||||||
EditorView.theme({
|
EditorView.theme({
|
||||||
'&': {
|
'&': {
|
||||||
|
@ -85,11 +89,13 @@ export const codeNodeEditorTheme = ({
|
||||||
},
|
},
|
||||||
'.cm-scroller': {
|
'.cm-scroller': {
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
|
maxHeight: maxHeight ?? '100%',
|
||||||
maxHeight: customMaxHeight ?? '100%',
|
|
||||||
...(isReadOnly
|
...(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': {
|
'.cm-diagnosticAction': {
|
||||||
backgroundColor: BASE_STYLING.diagnosticButton.backgroundColor,
|
backgroundColor: BASE_STYLING.diagnosticButton.backgroundColor,
|
||||||
|
@ -106,47 +112,97 @@ export const codeNodeEditorTheme = ({
|
||||||
color: 'var(--color-text-base)',
|
color: 'var(--color-text-base)',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
syntaxHighlighting(
|
highlightColors === 'html'
|
||||||
HighlightStyle.define([
|
? syntaxHighlighting(
|
||||||
{
|
HighlightStyle.define([
|
||||||
tag: tags.comment,
|
{ tag: tags.keyword, color: '#c678dd' },
|
||||||
color: 'var(--color-code-tags-comment)',
|
{
|
||||||
},
|
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||||
{
|
color: '#e06c75',
|
||||||
tag: [tags.string, tags.special(tags.brace)],
|
},
|
||||||
color: 'var(--color-code-tags-string)',
|
{ tag: [tags.function(tags.variableName), tags.labelName], color: '#61afef' },
|
||||||
},
|
{
|
||||||
{
|
tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)],
|
||||||
tag: [tags.number, tags.self, tags.bool, tags.null],
|
color: '#d19a66',
|
||||||
color: 'var(--color-code-tags-primitive)',
|
},
|
||||||
},
|
{ tag: [tags.definition(tags.name), tags.separator], color: '#abb2bf' },
|
||||||
{
|
{
|
||||||
tag: tags.keyword,
|
tag: [
|
||||||
color: 'var(--color-code-tags-keyword)',
|
tags.typeName,
|
||||||
},
|
tags.className,
|
||||||
{
|
tags.number,
|
||||||
tag: tags.operator,
|
tags.changed,
|
||||||
color: 'var(--color-code-tags-operator)',
|
tags.annotation,
|
||||||
},
|
tags.modifier,
|
||||||
{
|
tags.self,
|
||||||
tag: [
|
tags.namespace,
|
||||||
tags.variableName,
|
],
|
||||||
tags.propertyName,
|
color: '#e06c75',
|
||||||
tags.attributeName,
|
},
|
||||||
tags.regexp,
|
{
|
||||||
tags.className,
|
tag: [
|
||||||
tags.typeName,
|
tags.operator,
|
||||||
],
|
tags.operatorKeyword,
|
||||||
color: 'var(--color-code-tags-variable)',
|
tags.url,
|
||||||
},
|
tags.escape,
|
||||||
{
|
tags.regexp,
|
||||||
tag: [
|
tags.link,
|
||||||
tags.definition(tags.typeName),
|
tags.special(tags.string),
|
||||||
tags.definition(tags.propertyName),
|
],
|
||||||
tags.function(tags.variableName),
|
color: '#56b6c2',
|
||||||
],
|
},
|
||||||
color: 'var(--color-code-tags-definition)',
|
{ 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,
|
||||||
|
color: 'var(--color-code-tags-comment)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.string, tags.special(tags.brace)],
|
||||||
|
color: 'var(--color-code-tags-string)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.number, tags.self, tags.bool, tags.null],
|
||||||
|
color: 'var(--color-code-tags-primitive)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: tags.keyword,
|
||||||
|
color: 'var(--color-code-tags-keyword)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: tags.operator,
|
||||||
|
color: 'var(--color-code-tags-operator)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [
|
||||||
|
tags.variableName,
|
||||||
|
tags.propertyName,
|
||||||
|
tags.attributeName,
|
||||||
|
tags.regexp,
|
||||||
|
tags.className,
|
||||||
|
tags.typeName,
|
||||||
|
],
|
||||||
|
color: 'var(--color-code-tags-variable)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [
|
||||||
|
tags.definition(tags.typeName),
|
||||||
|
tags.definition(tags.propertyName),
|
||||||
|
tags.function(tags.variableName),
|
||||||
|
],
|
||||||
|
color: 'var(--color-code-tags-definition)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,44 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="htmlEditor"></div>
|
<div :class="$style.editor">
|
||||||
|
<div ref="htmlEditor"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 { autocompletion } from '@codemirror/autocomplete';
|
||||||
import { indentWithTab, insertNewlineAndIndent, history, redo, undo } from '@codemirror/commands';
|
import { history, redo, undo } from '@codemirror/commands';
|
||||||
import {
|
import {
|
||||||
|
LanguageSupport,
|
||||||
bracketMatching,
|
bracketMatching,
|
||||||
ensureSyntaxTree,
|
ensureSyntaxTree,
|
||||||
foldGutter,
|
foldGutter,
|
||||||
indentOnInput,
|
indentOnInput,
|
||||||
LanguageSupport,
|
|
||||||
} from '@codemirror/language';
|
} from '@codemirror/language';
|
||||||
import type { Extension } from '@codemirror/state';
|
import type { Extension } from '@codemirror/state';
|
||||||
import { EditorState } from '@codemirror/state';
|
import { EditorState, Prec } from '@codemirror/state';
|
||||||
import type { ViewUpdate } from '@codemirror/view';
|
import type { ViewUpdate } from '@codemirror/view';
|
||||||
import {
|
import {
|
||||||
dropCursor,
|
|
||||||
EditorView,
|
EditorView,
|
||||||
|
dropCursor,
|
||||||
highlightActiveLine,
|
highlightActiveLine,
|
||||||
highlightActiveLineGutter,
|
highlightActiveLineGutter,
|
||||||
keymap,
|
keymap,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
} from '@codemirror/view';
|
} 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 { n8nCompletionSources } from '@/plugins/codemirror/completions/addCompletions';
|
||||||
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
|
import { expressionInputHandler } from '@/plugins/codemirror/inputHandlers/expression.inputHandler';
|
||||||
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
|
import { highlighter } from '@/plugins/codemirror/resolvableHighlighter';
|
||||||
import { htmlEditorEventBus } from '@/event-bus';
|
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||||
import { expressionManager } from '@/mixins/expressionManager';
|
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||||
import { theme } from './theme';
|
|
||||||
import { nonTakenRanges } from './utils';
|
|
||||||
import type { Range, Section } from './types';
|
import type { Range, Section } from './types';
|
||||||
|
import { nonTakenRanges } from './utils';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'HtmlEditor',
|
name: 'HtmlEditor',
|
||||||
|
@ -52,6 +56,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
fillParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 4,
|
default: 4,
|
||||||
|
@ -90,15 +98,21 @@ export default defineComponent({
|
||||||
this.disableExpressionCompletions ? html() : htmlWithCompletions(),
|
this.disableExpressionCompletions ? html() : htmlWithCompletions(),
|
||||||
autoCloseTags,
|
autoCloseTags,
|
||||||
expressionInputHandler(),
|
expressionInputHandler(),
|
||||||
keymap.of([
|
Prec.highest(
|
||||||
indentWithTab,
|
keymap.of([
|
||||||
{ key: 'Enter', run: insertNewlineAndIndent },
|
...tabKeyMap,
|
||||||
{ key: 'Mod-z', run: undo },
|
...enterKeyMap,
|
||||||
{ key: 'Mod-Shift-z', run: redo },
|
{ key: 'Mod-z', run: undo },
|
||||||
]),
|
{ key: 'Mod-Shift-z', run: redo },
|
||||||
|
]),
|
||||||
|
),
|
||||||
indentOnInput(),
|
indentOnInput(),
|
||||||
theme({
|
codeNodeEditorTheme({
|
||||||
isReadOnly: this.isReadOnly,
|
isReadOnly: this.isReadOnly,
|
||||||
|
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||||
|
minHeight: '20vh',
|
||||||
|
rows: this.rows,
|
||||||
|
highlightColors: 'html',
|
||||||
}),
|
}),
|
||||||
lineNumbers(),
|
lineNumbers(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
|
@ -288,4 +302,12 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
</script>
|
</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>
|
<template>
|
||||||
<div ref="jsEditor" class="ph-no-capture js-editor"></div>
|
<div :class="$style.editor">
|
||||||
|
<div ref="jsEditor" class="ph-no-capture js-editor"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { autocompletion } from '@codemirror/autocomplete';
|
||||||
import { acceptCompletion, autocompletion } from '@codemirror/autocomplete';
|
import { history, redo, toggleComment, undo } from '@codemirror/commands';
|
||||||
import { indentWithTab, history, redo, toggleComment, undo } from '@codemirror/commands';
|
|
||||||
import { foldGutter, indentOnInput } from '@codemirror/language';
|
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { foldGutter, indentOnInput } from '@codemirror/language';
|
||||||
import { lintGutter } from '@codemirror/lint';
|
import { lintGutter } from '@codemirror/lint';
|
||||||
import type { Extension } from '@codemirror/state';
|
import type { Extension } from '@codemirror/state';
|
||||||
import { EditorState } from '@codemirror/state';
|
import { EditorState, Prec } from '@codemirror/state';
|
||||||
import type { ViewUpdate } from '@codemirror/view';
|
import type { ViewUpdate } from '@codemirror/view';
|
||||||
import {
|
import {
|
||||||
dropCursor,
|
|
||||||
EditorView,
|
EditorView,
|
||||||
|
dropCursor,
|
||||||
highlightActiveLine,
|
highlightActiveLine,
|
||||||
highlightActiveLineGutter,
|
highlightActiveLineGutter,
|
||||||
keymap,
|
keymap,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||||
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -34,6 +38,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
fillParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 4,
|
default: 4,
|
||||||
|
@ -57,18 +65,25 @@ export default defineComponent({
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
EditorState.readOnly.of(isReadOnly),
|
EditorState.readOnly.of(isReadOnly),
|
||||||
EditorView.editable.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) {
|
if (!isReadOnly) {
|
||||||
extensions.push(
|
extensions.push(
|
||||||
history(),
|
history(),
|
||||||
keymap.of([
|
Prec.highest(
|
||||||
{ key: 'Mod-z', run: undo },
|
keymap.of([
|
||||||
{ key: 'Mod-Shift-z', run: redo },
|
...tabKeyMap,
|
||||||
{ key: 'Mod-/', run: toggleComment },
|
...enterKeyMap,
|
||||||
{ key: 'Tab', run: acceptCompletion },
|
{ key: 'Mod-z', run: undo },
|
||||||
indentWithTab,
|
{ key: 'Mod-Shift-z', run: redo },
|
||||||
]),
|
{ key: 'Mod-/', run: toggleComment },
|
||||||
|
]),
|
||||||
|
),
|
||||||
lintGutter(),
|
lintGutter(),
|
||||||
autocompletion(),
|
autocompletion(),
|
||||||
indentOnInput(),
|
indentOnInput(),
|
||||||
|
@ -93,3 +108,13 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.editor {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,26 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="jsonEditor" class="ph-no-capture json-editor"></div>
|
<div :class="$style.editor">
|
||||||
|
<div ref="jsonEditor" class="ph-no-capture json-editor"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { autocompletion } from '@codemirror/autocomplete';
|
import { autocompletion } from '@codemirror/autocomplete';
|
||||||
import { indentWithTab, history, redo, undo } from '@codemirror/commands';
|
import { history, redo, undo } from '@codemirror/commands';
|
||||||
import { bracketMatching, foldGutter, indentOnInput } from '@codemirror/language';
|
|
||||||
import { json, jsonParseLinter } from '@codemirror/lang-json';
|
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 type { Extension } from '@codemirror/state';
|
||||||
import { EditorState } from '@codemirror/state';
|
import { EditorState, Prec } from '@codemirror/state';
|
||||||
import type { ViewUpdate } from '@codemirror/view';
|
import type { ViewUpdate } from '@codemirror/view';
|
||||||
import {
|
import {
|
||||||
dropCursor,
|
|
||||||
EditorView,
|
EditorView,
|
||||||
|
dropCursor,
|
||||||
highlightActiveLine,
|
highlightActiveLine,
|
||||||
highlightActiveLineGutter,
|
highlightActiveLineGutter,
|
||||||
keymap,
|
keymap,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
import { enterKeyMap, tabKeyMap } from '../CodeNodeEditor/baseExtensions';
|
||||||
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
import { codeNodeEditorTheme } from '../CodeNodeEditor/theme';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -34,6 +38,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
fillParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 4,
|
default: 4,
|
||||||
|
@ -57,16 +65,24 @@ export default defineComponent({
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
EditorState.readOnly.of(isReadOnly),
|
EditorState.readOnly.of(isReadOnly),
|
||||||
EditorView.editable.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) {
|
if (!isReadOnly) {
|
||||||
extensions.push(
|
extensions.push(
|
||||||
history(),
|
history(),
|
||||||
keymap.of([
|
Prec.highest(
|
||||||
indentWithTab,
|
keymap.of([
|
||||||
{ key: 'Mod-z', run: undo },
|
...tabKeyMap,
|
||||||
{ key: 'Mod-Shift-z', run: redo },
|
...enterKeyMap,
|
||||||
]),
|
{ key: 'Mod-z', run: undo },
|
||||||
|
{ key: 'Mod-Shift-z', run: redo },
|
||||||
|
]),
|
||||||
|
),
|
||||||
createLinter(jsonParseLinter()),
|
createLinter(jsonParseLinter()),
|
||||||
lintGutter(),
|
lintGutter(),
|
||||||
autocompletion(),
|
autocompletion(),
|
||||||
|
@ -93,3 +109,13 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.editor {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1166,7 +1166,7 @@ export default defineComponent({
|
||||||
|
|
||||||
.node-parameters-wrapper {
|
.node-parameters-wrapper {
|
||||||
overflow-y: auto;
|
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;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:redact-values="shouldRedactValue"
|
:redact-values="shouldRedactValue"
|
||||||
@closeDialog="closeExpressionEditDialog"
|
@closeDialog="closeExpressionEditDialog"
|
||||||
@update:modelValue="expressionUpdated"
|
@update:model-value="expressionUpdated"
|
||||||
></ExpressionEdit>
|
></ExpressionEdit>
|
||||||
<div class="parameter-input ignore-key-press" :style="parameterInputWrapperStyle">
|
<div class="parameter-input ignore-key-press" :style="parameterInputWrapperStyle">
|
||||||
<ResourceLocator
|
<ResourceLocator
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
:node="node"
|
:node="node"
|
||||||
:path="path"
|
:path="path"
|
||||||
:event-bus="eventBus"
|
:event-bus="eventBus"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
@modalOpenerClick="openExpressionEditorModal"
|
@modalOpenerClick="openExpressionEditorModal"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
:path="path"
|
:path="path"
|
||||||
:additional-expression-data="additionalExpressionData"
|
:additional-expression-data="additionalExpressionData"
|
||||||
:class="{ 'ph-no-capture': shouldRedactValue }"
|
:class="{ 'ph-no-capture': shouldRedactValue }"
|
||||||
@update:modelValue="expressionUpdated"
|
@update:model-value="expressionUpdated"
|
||||||
@modalOpenerClick="openExpressionEditorModal"
|
@modalOpenerClick="openExpressionEditorModal"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -62,23 +62,60 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-if="codeEditDialogVisible"
|
:model-value="codeEditDialogVisible"
|
||||||
:model-value="true"
|
|
||||||
append-to-body
|
append-to-body
|
||||||
:close-on-click-modal="false"
|
|
||||||
width="80%"
|
width="80%"
|
||||||
:title="`${i18n.baseText('codeEdit.edit')} ${$locale
|
:title="`${i18n.baseText('codeEdit.edit')} ${$locale
|
||||||
.nodeText()
|
.nodeText()
|
||||||
.inputLabelDisplayName(parameter, path)}`"
|
.inputLabelDisplayName(parameter, path)}`"
|
||||||
:before-close="closeCodeEditDialog"
|
: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
|
<CodeNodeEditor
|
||||||
|
v-if="editorType === 'codeNodeEditor'"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:default-value="parameter.default"
|
:default-value="parameter.default"
|
||||||
:language="editorLanguage"
|
:language="editorLanguage"
|
||||||
:is-read-only="isReadOnly"
|
: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>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
@ -90,11 +127,12 @@
|
||||||
:path="path"
|
:path="path"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
@closeDialog="closeTextEditDialog"
|
@closeDialog="closeTextEditDialog"
|
||||||
@update:modelValue="expressionUpdated"
|
@update:model-value="expressionUpdated"
|
||||||
></TextEdit>
|
></TextEdit>
|
||||||
|
|
||||||
<CodeNodeEditor
|
<CodeNodeEditor
|
||||||
v-if="editorType === 'codeNodeEditor' && isCodeNode(node)"
|
v-if="editorType === 'codeNodeEditor' && isCodeNode(node)"
|
||||||
|
:key="codeEditDialogVisible"
|
||||||
:mode="node.parameters.mode"
|
:mode="node.parameters.mode"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:default-value="parameter.default"
|
:default-value="parameter.default"
|
||||||
|
@ -102,43 +140,102 @@
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:rows="getArgument('rows')"
|
:rows="getArgument('rows')"
|
||||||
:ai-button-enabled="settingsStore.isCloudDeployment"
|
: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
|
<HtmlEditor
|
||||||
v-else-if="editorType === 'htmlEditor'"
|
v-else-if="editorType === 'htmlEditor'"
|
||||||
|
:key="codeEditDialogVisible"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:rows="getArgument('rows')"
|
:rows="getArgument('rows')"
|
||||||
:disable-expression-coloring="!isHtmlNode(node)"
|
:disable-expression-coloring="!isHtmlNode(node)"
|
||||||
:disable-expression-completions="!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
|
<SqlEditor
|
||||||
v-else-if="editorType === 'sqlEditor'"
|
v-else-if="editorType === 'sqlEditor'"
|
||||||
|
:key="codeEditDialogVisible"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:dialect="getArgument('sqlDialect')"
|
:dialect="getArgument('sqlDialect')"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:rows="getArgument('rows')"
|
: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
|
<JsEditor
|
||||||
v-else-if="editorType === 'jsEditor'"
|
v-else-if="editorType === 'jsEditor'"
|
||||||
|
:key="codeEditDialogVisible"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:rows="getArgument('rows')"
|
: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
|
<JsonEditor
|
||||||
v-else-if="parameter.type === 'json'"
|
v-else-if="parameter.type === 'json'"
|
||||||
|
:key="codeEditDialogVisible"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:rows="getArgument('rows')"
|
: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()">
|
<div v-else-if="editorType" class="readonly-code clickable" @click="displayEditDialog()">
|
||||||
<CodeNodeEditor
|
<CodeNodeEditor
|
||||||
|
@ -161,7 +258,7 @@
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:placeholder="getPlaceholder()"
|
:placeholder="getPlaceholder()"
|
||||||
@update:modelValue="valueChanged($event) && onUpdateTextInput($event)"
|
@update:model-value="valueChanged($event) && onUpdateTextInput($event)"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -194,7 +291,7 @@
|
||||||
:show-alpha="getArgument('showAlpha')"
|
:show-alpha="getArgument('showAlpha')"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
/>
|
/>
|
||||||
<n8n-input
|
<n8n-input
|
||||||
v-model="tempValue"
|
v-model="tempValue"
|
||||||
|
@ -202,7 +299,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -226,7 +323,7 @@
|
||||||
"
|
"
|
||||||
:picker-options="dateTimePickerOptions"
|
:picker-options="dateTimePickerOptions"
|
||||||
:class="{ 'ph-no-capture': shouldRedactValue }"
|
:class="{ 'ph-no-capture': shouldRedactValue }"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
|
@ -245,7 +342,7 @@
|
||||||
:class="{ 'ph-no-capture': shouldRedactValue }"
|
:class="{ 'ph-no-capture': shouldRedactValue }"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:placeholder="parameter.placeholder"
|
:placeholder="parameter.placeholder"
|
||||||
@update:modelValue="onUpdateTextInput"
|
@update:model-value="onUpdateTextInput"
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
|
@ -262,7 +359,7 @@
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:display-title="displayTitle"
|
:display-title="displayTitle"
|
||||||
@credentialSelected="credentialSelected"
|
@credentialSelected="credentialSelected"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
@setFocus="setFocus"
|
@setFocus="setFocus"
|
||||||
@onBlur="onBlur"
|
@onBlur="onBlur"
|
||||||
>
|
>
|
||||||
|
@ -283,7 +380,7 @@
|
||||||
:loading="remoteParameterOptionsLoading"
|
:loading="remoteParameterOptionsLoading"
|
||||||
:disabled="isReadOnly || remoteParameterOptionsLoading"
|
:disabled="isReadOnly || remoteParameterOptionsLoading"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -321,7 +418,7 @@
|
||||||
:disabled="isReadOnly || remoteParameterOptionsLoading"
|
:disabled="isReadOnly || remoteParameterOptionsLoading"
|
||||||
:title="displayTitle"
|
:title="displayTitle"
|
||||||
:placeholder="i18n.baseText('parameterInput.select')"
|
:placeholder="i18n.baseText('parameterInput.select')"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
|
@ -358,7 +455,7 @@
|
||||||
active-color="#13ce66"
|
active-color="#13ce66"
|
||||||
:model-value="displayValue"
|
:model-value="displayValue"
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
@update:modelValue="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1056,7 +1153,7 @@ export default defineComponent({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.editorType) {
|
if (this.editorType || this.parameter.type === 'json') {
|
||||||
this.codeEditDialogVisible = true;
|
this.codeEditDialogVisible = true;
|
||||||
} else {
|
} else {
|
||||||
this.textEditDialogVisible = true;
|
this.textEditDialogVisible = true;
|
||||||
|
@ -1418,4 +1515,12 @@ export default defineComponent({
|
||||||
.invalid {
|
.invalid {
|
||||||
border-color: var(--color-danger);
|
border-color: var(--color-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.code-edit-dialog {
|
||||||
|
height: 70vh;
|
||||||
|
|
||||||
|
.code-node-editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-on-click-outside="onBlur" :class="$style.sqlEditor">
|
<div v-on-click-outside="onBlur" :class="$style.sqlEditor">
|
||||||
<div ref="sqlEditor" data-test-id="sql-editor-container"></div>
|
<div ref="sqlEditor" data-test-id="sql-editor-container"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
<InlineExpressionEditorOutput
|
<InlineExpressionEditorOutput
|
||||||
|
v-if="!fillParent"
|
||||||
:segments="segments"
|
:segments="segments"
|
||||||
:is-read-only="isReadOnly"
|
:is-read-only="isReadOnly"
|
||||||
:visible="isFocused"
|
:visible="isFocused"
|
||||||
|
@ -11,41 +13,42 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import InlineExpressionEditorOutput from '@/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue';
|
||||||
import { acceptCompletion, autocompletion, ifNotIn } from '@codemirror/autocomplete';
|
import { EXPRESSIONS_DOCS_URL } from '@/constants';
|
||||||
import { indentWithTab, history, redo, toggleComment, undo } from '@codemirror/commands';
|
import { codeNodeEditorEventBus } from '@/event-bus';
|
||||||
import { bracketMatching, foldGutter, indentOnInput, LanguageSupport } from '@codemirror/language';
|
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 { EditorState } from '@codemirror/state';
|
||||||
import type { Line, Extension } from '@codemirror/state';
|
import type { ViewUpdate } from '@codemirror/view';
|
||||||
import {
|
import {
|
||||||
dropCursor,
|
|
||||||
EditorView,
|
EditorView,
|
||||||
|
dropCursor,
|
||||||
highlightActiveLine,
|
highlightActiveLine,
|
||||||
highlightActiveLineGutter,
|
highlightActiveLineGutter,
|
||||||
keymap,
|
keymap,
|
||||||
lineNumbers,
|
lineNumbers,
|
||||||
} from '@codemirror/view';
|
} from '@codemirror/view';
|
||||||
import type { ViewUpdate } from '@codemirror/view';
|
import type { SQLDialect as SQLDialectType } from '@n8n/codemirror-lang-sql';
|
||||||
import {
|
import {
|
||||||
MSSQL,
|
|
||||||
MySQL,
|
|
||||||
PostgreSQL,
|
|
||||||
StandardSQL,
|
|
||||||
MariaSQL,
|
|
||||||
SQLite,
|
|
||||||
Cassandra,
|
Cassandra,
|
||||||
|
MSSQL,
|
||||||
|
MariaSQL,
|
||||||
|
MySQL,
|
||||||
PLSQL,
|
PLSQL,
|
||||||
|
PostgreSQL,
|
||||||
|
SQLite,
|
||||||
|
StandardSQL,
|
||||||
keywordCompletionSource,
|
keywordCompletionSource,
|
||||||
} from '@n8n/codemirror-lang-sql';
|
} 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 { 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 = {
|
const SQL_DIALECTS = {
|
||||||
StandardSQL,
|
StandardSQL,
|
||||||
|
@ -88,6 +91,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
fillParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
rows: {
|
rows: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 4,
|
default: 4,
|
||||||
|
@ -129,8 +136,9 @@ export default defineComponent({
|
||||||
expressionInputHandler(),
|
expressionInputHandler(),
|
||||||
codeNodeEditorTheme({
|
codeNodeEditorTheme({
|
||||||
isReadOnly: this.isReadOnly,
|
isReadOnly: this.isReadOnly,
|
||||||
customMaxHeight: '350px',
|
maxHeight: this.fillParent ? '100%' : '40vh',
|
||||||
customMinHeight: this.rows,
|
minHeight: '10vh',
|
||||||
|
rows: this.rows,
|
||||||
}),
|
}),
|
||||||
lineNumbers(),
|
lineNumbers(),
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
|
@ -146,13 +154,15 @@ export default defineComponent({
|
||||||
if (!this.isReadOnly) {
|
if (!this.isReadOnly) {
|
||||||
extensions.push(
|
extensions.push(
|
||||||
history(),
|
history(),
|
||||||
keymap.of([
|
Prec.highest(
|
||||||
{ key: 'Mod-z', run: undo },
|
keymap.of([
|
||||||
{ key: 'Mod-Shift-z', run: redo },
|
...tabKeyMap,
|
||||||
{ key: 'Mod-/', run: toggleComment },
|
...enterKeyMap,
|
||||||
{ key: 'Tab', run: acceptCompletion },
|
{ key: 'Mod-z', run: undo },
|
||||||
indentWithTab,
|
{ key: 'Mod-Shift-z', run: redo },
|
||||||
]),
|
{ key: 'Mod-/', run: toggleComment },
|
||||||
|
]),
|
||||||
|
),
|
||||||
autocompletion(),
|
autocompletion(),
|
||||||
indentOnInput(),
|
indentOnInput(),
|
||||||
highlightActiveLine(),
|
highlightActiveLine(),
|
||||||
|
@ -233,5 +243,10 @@ export default defineComponent({
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.sqlEditor {
|
.sqlEditor {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -192,15 +192,7 @@
|
||||||
border: var(--border-base);
|
border: var(--border-base);
|
||||||
box-shadow: 0px 6px 16px rgb(68 28 23 / 6%);
|
box-shadow: 0px 6px 16px rgb(68 28 23 / 6%);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-top: 15vh;
|
|
||||||
|
|
||||||
@media (max-height: 1050px) {
|
|
||||||
margin: 4em auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 930px) {
|
|
||||||
margin: 1em auto !important;
|
|
||||||
}
|
|
||||||
&.classic {
|
&.classic {
|
||||||
.el-dialog__header {
|
.el-dialog__header {
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
|
|
|
@ -7,7 +7,6 @@ const commonDescription: INodeProperties = {
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'codeNodeEditor',
|
editor: 'codeNodeEditor',
|
||||||
editorLanguage: 'javaScript',
|
editorLanguage: 'javaScript',
|
||||||
rows: 5,
|
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -7,7 +7,6 @@ const commonDescription: INodeProperties = {
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'codeNodeEditor',
|
editor: 'codeNodeEditor',
|
||||||
editorLanguage: 'python',
|
editorLanguage: 'python',
|
||||||
rows: 5,
|
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -20,7 +20,6 @@ const properties: INodeProperties[] = [
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
hide: {
|
hide: {
|
||||||
|
@ -39,7 +38,6 @@ const properties: INodeProperties[] = [
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
|
|
|
@ -88,7 +88,6 @@ export class MicrosoftSql implements INodeType {
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'MSSQL',
|
sqlDialect: 'MSSQL',
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
|
|
|
@ -79,7 +79,6 @@ const versionDescription: INodeTypeDescription = {
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'MySQL',
|
sqlDialect: 'MySQL',
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
|
|
|
@ -26,7 +26,6 @@ const properties: INodeProperties[] = [
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'MySQL',
|
sqlDialect: 'MySQL',
|
||||||
},
|
},
|
||||||
hint: 'Consider using query parameters to prevent SQL injection attacks. Add them in the options below',
|
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,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'PostgreSQL',
|
sqlDialect: 'PostgreSQL',
|
||||||
},
|
},
|
||||||
displayOptions: {
|
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.",
|
"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: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'PostgreSQL',
|
sqlDialect: 'PostgreSQL',
|
||||||
},
|
},
|
||||||
hint: 'Consider using query parameters to prevent SQL injection attacks. Add them in the options below',
|
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,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'PostgreSQL',
|
sqlDialect: 'PostgreSQL',
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
|
|
|
@ -19,7 +19,7 @@ const properties: INodeProperties[] = [
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
rows: 5,
|
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',
|
validateType: 'object',
|
||||||
ignoreValidationDuringExecution: true,
|
ignoreValidationDuringExecution: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -69,7 +69,6 @@ export class Snowflake implements INodeType {
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
|
|
|
@ -67,7 +67,6 @@ export class TimescaleDb implements INodeType {
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
editor: 'sqlEditor',
|
editor: 'sqlEditor',
|
||||||
rows: 5,
|
|
||||||
sqlDialect: 'PostgreSQL',
|
sqlDialect: 'PostgreSQL',
|
||||||
},
|
},
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
|
|
Loading…
Reference in a new issue