From a535c3ba3d068866d75ce6adf47232e5684cf0eb Mon Sep 17 00:00:00 2001 From: Vincent Fiduccia Date: Mon, 2 Oct 2017 18:25:04 -0700 Subject: [PATCH] More metadata editor, tab/untab with selection --- .../container/new-edit/component.js | 7 +- .../container/new-edit/template.hbs | 16 +-- app/components/form-metadata/component.js | 29 ++--- app/components/form-metadata/template.hbs | 2 +- app/components/json-editor/component.js | 115 +++++++++++++----- 5 files changed, 114 insertions(+), 55 deletions(-) diff --git a/app/components/container/new-edit/component.js b/app/components/container/new-edit/component.js index a7d6b56a7..7edad2786 100644 --- a/app/components/container/new-edit/component.js +++ b/app/components/container/new-edit/component.js @@ -96,18 +96,20 @@ export default Ember.Component.extend(NewOrEdit, { this.set('launchConfig.secrets', []); } + if ( !this.get('launchConfig.metadata') ) { + this.set('launchConfig.metadata', {}); + }; + if ( this.get('isService') && !this.get('isSidekick') ) { this.setProperties({ name: this.get('service.name'), description: this.get('service.description'), scale: this.get('service.scale'), - metadata: this.get('service.metadata') || {}, }); } else { this.setProperties({ name: this.get('launchConfig.name'), description: this.get('launchConfig.description'), - metadata: {}, }); } @@ -264,7 +266,6 @@ export default Ember.Component.extend(NewOrEdit, { pr = this.get('service').clone(); nameResource = pr; pr.set('launchConfig', lc); - pr.set('metadata', this.get('metadata')) pr.set('scale', this.get('scale')); } else { // Convert the launch config to a container diff --git a/app/components/container/new-edit/template.hbs b/app/components/container/new-edit/template.hbs index 3ebc9f504..61762022d 100644 --- a/app/components/container/new-edit/template.hbs +++ b/app/components/container/new-edit/template.hbs @@ -152,16 +152,16 @@ expandFn=expandFn }} + {{form-metadata + classNames="accordion-wrapper" + instance=launchConfig + errors=metadataErrors + expandAll=al.expandAll + expandFn=expandFn + }} + {{#if isService}} {{#unless isSidekick}} - {{form-metadata - classNames="accordion-wrapper" - instance=metadata - errors=metadataErrors - expandAll=al.expandAll - expandFn=expandFn - }} - {{container/form-service-links service=service expandAll=al.expandAll diff --git a/app/components/form-metadata/component.js b/app/components/form-metadata/component.js index cdbb9e6e4..44777747e 100644 --- a/app/components/form-metadata/component.js +++ b/app/components/form-metadata/component.js @@ -4,13 +4,12 @@ import { STATUS, STATUS_INTL_KEY, classForStatus } from 'ui/components/accordion export default Ember.Component.extend({ intl: Ember.inject.service(), - classNames: ['accordion-wrapper'], - - detailKey: 'formMetadata.detail', - instance: null, + detailKey: 'formMetadata.detail', errors: null, - invalid: false, + valid: true, + + classNames: ['accordion-wrapper'], didReceiveAttrs() { if (!this.get('expandFn')) { @@ -22,26 +21,28 @@ export default Ember.Component.extend({ validate: function () { let intl = this.get('intl'); - if (this.get('invalid')) { - this.set('errors', [intl.t('formMetadata.errors.invalidJSON')]) - } else if (['object', 'null'].indexOf(Ember.typeOf(this.get('instance'))) === -1) { - this.set('errors', [intl.t('formMetadata.errors.topLevelValueInvalid')]); + if ( this.get('valid') ) { + if ( ['object', 'null'].indexOf(Ember.typeOf(this.get('instance.metadata'))) === -1 ) { + this.set('errors', [intl.t('formMetadata.errors.topLevelValueInvalid')]); + } else { + this.set('errors', []); + } } else { - this.set('errors', []); + this.set('errors', [intl.t('formMetadata.errors.invalidJSON')]); } - }.observes('invalid', 'instance'), + }.observes('valid', 'instance.metadata'), statusClass: null, status: function () { let k; - if (this.get('invalid') || ['object', 'null'].indexOf(Ember.typeOf(this.get('instance'))) === -1) { + if (this.get('errors.length') ) { k = STATUS.ERROR; - } else if (!Ember.isNone(this.get('instance')) && Object.keys(this.get('instance')).length > 0) { + } else if (!Ember.isNone(this.get('instance.metadata')) && Object.keys(this.get('instance.metadata')).length > 0) { k = STATUS.CONFIGURED; } else { k = STATUS.NOTCONFIGURED; } this.set('statusClass', classForStatus(k)); return this.get('intl').t(`${STATUS_INTL_KEY}.${k}`); - }.property('invalid'), + }.property('errors.length'), }); diff --git a/app/components/form-metadata/template.hbs b/app/components/form-metadata/template.hbs index 0bd09e152..29486fd28 100644 --- a/app/components/form-metadata/template.hbs +++ b/app/components/form-metadata/template.hbs @@ -6,5 +6,5 @@ expandAll=expandAll expand=(action expandFn) }} - {{json-editor json=instance isInvalid=invalid}} + {{json-editor json=instance.metadata isValid=valid}} {{/accordion-list-item}} diff --git a/app/components/json-editor/component.js b/app/components/json-editor/component.js index a886b74c7..3dcadf56a 100644 --- a/app/components/json-editor/component.js +++ b/app/components/json-editor/component.js @@ -2,51 +2,108 @@ import Ember from 'ember'; import sanitize from 'json-sanitizer'; import C from 'ui/utils/constants'; +const TAB_SIZE = 2; +const TAB_STR = (new Array(TAB_SIZE+1)).join(' '); + export default Ember.Component.extend({ - json: {}, - isInvalid: false, + jsonString: null, tagName: 'div', classNames: ['jsoneditor-component'], - jsonString: function () { - return JSON.stringify(this.get('json'), undefined, 4); - }.property('json'), + json: {}, + isValid: true, - onChange: function () { - try { - const json = this.parseJSON(this.jsonString) - this.set('json', json); - this.set('isInvalid', false) - } catch (err) { - this.set('isInvalid', true) - } - }.observes('jsonString'), - - parseJSON: function (jsonString) { - try { - return JSON.parse(jsonString); - } catch (err) { - return JSON.parse(sanitize(jsonString)); - } + init() { + this._super(); + this.focusOut(); }, focusOut() { - if (!this.get('isInvalid')) { - this.set('jsonString', JSON.stringify(this.get('json'), undefined, 4)); + if ( this.get('isValid') ) { + this.set('jsonString', JSON.stringify(this.get('json'), undefined, TAB_SIZE)); } }, - keyDown: function (event) { + keyDown(event) { const keyCode = event.which; if (keyCode === C.KEY.TAB) { event.preventDefault(); const el = $(this).get(0).childViews.get(0).element; - const start = el.selectionStart; - const end = el.selectionEnd; - $(el).val($(el).val().substring(0, start) + " " + $(el).val().substring(end)); - el.selectionStart = start + 4; - el.selectionEnd = start + 4; + const val = $(el).val(); + + let start = el.selectionStart; + let end = el.selectionEnd; + const origStart = start; + const origEnd = end; + + // Move start to the beginning of the line + while ( start > 0 && val.charAt(start-1) !== '\n' ) { + start--; + } + + // And end to the end of the line + while ( end < val.length && val.charAt(end) !== '\n') { + end++; + } + + let lines = val.substring(start,end).split(/\n/); + + let sub; + let direction; + if ( event.shiftKey ) { + const re = new RegExp("^(\\s{"+ TAB_SIZE + "}|\\t)"); + let found = false; + sub = lines.map((x) => { + let out = x.replace(re,''); + if ( out !== x ) { + found = true; + } + return out; + }); + + if ( found ) { + direction = -1; + } else { + // If no lines moved, this will prevent the cursor from moving back + direction = 0; + } + } else { + sub = lines.map(x => TAB_STR+x); + direction = 1; + } + + let replaceStr = sub.join("\n"); + $(el).val(val.substring(0, start) + replaceStr + val.substring(end)); + + if ( origStart === origEnd ) { + el.selectionStart = el.selectionEnd = origStart + direction*TAB_SIZE*sub.length; + } else { + el.selectionStart = start; + el.selectionEnd = start + replaceStr.length; + } + } + }, + + onChange: function() { + const [json, isValid] = this.parse(this.get('jsonString')); + this.setProperties({ + json, + isValid, + }); + }.observes('jsonString'), + + parse(jsonString) { + try { + const json = JSON.parse(jsonString); + return [json,true]; + } catch (err) { + try { + const json = JSON.parse(sanitize(jsonString)); + return [json,true]; + } catch (err) { + return [null,false]; + } } }, });