DEV: Update JSON Editor to Glimmer and use new modalAPI (#22421)

This commit is contained in:
Keegan George 2023-07-05 02:27:29 -07:00 committed by GitHub
parent d458ca154c
commit ad1b466cd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 126 additions and 145 deletions

View File

@ -2,7 +2,7 @@
<Textarea @value={{this.value}} class="input-setting-textarea" />
{{else if this.setting.json_schema}}
<DButton
@action={{action "launchJsonEditorModal"}}
@action={{fn (mut this.showJsonEditorModal) true}}
@icon="pencil-alt"
@label="admin.site_settings.json_schema.edit"
/>
@ -13,4 +13,14 @@
{{/if}}
<SettingValidationMessage @message={{this.validationMessage}} />
<div class="desc">{{html-safe this.setting.description}}</div>
<div class="desc">{{html-safe this.setting.description}}</div>
{{#if this.showJsonEditorModal}}
<Modal::JsonSchemaEditor
@updateValue={{fn (mut this.value)}}
@value={{this.value}}
@settingName={{this.setting.setting}}
@jsonSchema={{this.setting.json_schema}}
@closeModal={{fn (mut this.showJsonEditorModal) false}}
/>
{{/if}}

View File

@ -1,20 +1,3 @@
import { action } from "@ember/object";
import Component from "@ember/component";
import showModal from "discourse/lib/show-modal";
export default class String extends Component {
@action
launchJsonEditorModal() {
const schemaModal = showModal("json-schema-editor", {
model: {
value: this.value,
settingName: this.setting.setting,
jsonSchema: this.setting.json_schema,
},
});
schemaModal.set("onClose", () => {
this.set("value", schemaModal.model.value);
});
}
}
export default class String extends Component {}

View File

@ -1,16 +0,0 @@
<DModalBody
@rawTitle={{i18n
"admin.site_settings.json_schema.modal_title"
name=this.settingName
}}
>
<div id="json-editor-holder"></div>
</DModalBody>
<div class="modal-footer">
<DButton
@class="btn-primary"
@action={{action "saveChanges"}}
@label="save"
/>
</div>

View File

@ -1,91 +0,0 @@
import { action } from "@ember/object";
import Component from "@ember/component";
import { create } from "virtual-dom";
import discourseComputed from "discourse-common/utils/decorators";
import { iconNode } from "discourse-common/lib/icon-library";
import loadScript from "discourse/lib/load-script";
import { schedule } from "@ember/runloop";
export default Component.extend({
className: "json-editor-holder",
editor: null,
saveChangesCallback: null,
didInsertElement() {
this._super(...arguments);
loadScript("/javascripts/jsoneditor.js").then(() => {
schedule("afterRender", () => {
let { JSONEditor } = window;
JSONEditor.defaults.options.theme = "bootstrap4";
JSONEditor.defaults.iconlibs = {
discourseIcons: DiscourseJsonSchemaEditorIconlib,
};
JSONEditor.defaults.options.iconlib = "discourseIcons";
const el = document.querySelector("#json-editor-holder");
this.editor = new JSONEditor(el, {
schema: this.model.jsonSchema,
disable_array_delete_all_rows: true,
disable_array_delete_last_row: true,
disable_array_reorder: false,
disable_array_copy: false,
enable_array_copy: true,
disable_edit_json: true,
disable_properties: true,
disable_collapse: false,
remove_button_labels: true,
show_errors: "never",
startval: this.model.value ? JSON.parse(this.model.value) : null,
});
});
});
},
@discourseComputed("model.settingName")
settingName(name) {
return name.replace(/\_/g, " ");
},
@action
saveChanges() {
const errors = this.editor.validate();
if (!errors.length) {
const fieldValue = JSON.stringify(this.editor.getValue());
this.saveChangesCallback(fieldValue);
} else {
this.appEvents.trigger("modal-body:flash", {
text: errors.mapBy("message").join("\n"),
messageClass: "error",
});
}
},
willDestroyElement() {
this._super(...arguments);
this.editor?.destroy();
},
});
class DiscourseJsonSchemaEditorIconlib {
constructor() {
this.mapping = {
delete: "trash-alt",
add: "plus",
moveup: "arrow-up",
movedown: "arrow-down",
copy: "copy",
collapse: "chevron-down",
expand: "chevron-up",
};
}
getIcon(key) {
if (!this.mapping[key]) {
return;
}
return create(iconNode(this.mapping[key]));
}
}

View File

@ -0,0 +1,22 @@
<DModal
@flash={{this.flash}}
@flashType={{this.flashType}}
@closeModal={{@closeModal}}
@title={{i18n
"admin.site_settings.json_schema.modal_title"
name=@settingName
}}
class="json-schema-editor-modal"
>
<:body>
<div
id="json-editor-holder"
{{did-insert this.buildJsonEditor}}
{{will-destroy this.teardownJsonEditor}}
></div>
</:body>
<:footer>
<DButton @class="btn-primary" @action={{this.saveChanges}} @label="save" />
</:footer>
</DModal>

View File

@ -0,0 +1,91 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { afterRender } from "discourse-common/utils/decorators";
import { create } from "virtual-dom";
import { iconNode } from "discourse-common/lib/icon-library";
import loadScript from "discourse/lib/load-script";
import { tracked } from "@glimmer/tracking";
export default class JsonSchemaEditorModal extends Component {
@tracked editor = null;
@tracked value = this.args.value;
@tracked flash;
@tracked flashType;
get settingName() {
return this.args.settingName.replace(/\_/g, " ");
}
@action
buildJsonEditor(editor) {
loadScript("/javascripts/jsoneditor.js").then(
this._loadEditor.bind(this, editor)
);
}
@action
teardownJsonEditor() {
this.editor?.destroy();
}
@action
saveChanges() {
const errors = this.editor.validate();
if (!errors.length) {
this.value = JSON.stringify(this.editor.getValue());
this.args.updateValue(this.value);
this.args.closeModal();
} else {
this.flash = errors.mapBy("message").join("\n");
this.flashType = "error";
}
}
@afterRender
_loadEditor(editor) {
let { JSONEditor } = window;
JSONEditor.defaults.options.theme = "bootstrap4";
JSONEditor.defaults.iconlibs = {
discourseIcons: DiscourseJsonSchemaEditorIconlib,
};
JSONEditor.defaults.options.iconlib = "discourseIcons";
this.editor = new JSONEditor(editor, {
schema: this.args.jsonSchema,
disable_array_delete_all_rows: true,
disable_array_delete_last_row: true,
disable_array_reorder: false,
disable_array_copy: false,
enable_array_copy: true,
disable_edit_json: true,
disable_properties: true,
disable_collapse: false,
remove_button_labels: true,
show_errors: "never",
startval: this.value ? JSON.parse(this.value) : null,
});
}
}
class DiscourseJsonSchemaEditorIconlib {
constructor() {
this.mapping = {
delete: "trash-alt",
add: "plus",
moveup: "arrow-up",
movedown: "arrow-down",
copy: "copy",
collapse: "chevron-down",
expand: "chevron-up",
};
}
getIcon(key) {
if (!this.mapping[key]) {
return;
}
return create(iconNode(this.mapping[key]));
}
}

View File

@ -1,13 +0,0 @@
import { action } from "@ember/object";
import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
export default Controller.extend(ModalFunctionality, {
editor: null,
@action
saveChanges(value) {
this.set("model.value", value);
this.send("closeModal");
},
});

View File

@ -1,5 +0,0 @@
<JsonEditor
@model={{this.model}}
@saveChangesCallback={{action "saveChanges"}}
@tagName=""
/>