diff --git a/editor/_json-editor.scss b/editor/_json-editor.scss new file mode 100644 index 0000000..7a62f51 --- /dev/null +++ b/editor/_json-editor.scss @@ -0,0 +1,72 @@ +.editor-container { + font-size: 14px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + --gap: 8px; + + h1 {} + h2 {} + h3 {} + p { + margin: 0 0 var(--gap) 0; + } + input[type=text], + input[type=email] { + padding: 2px 4px; + } + textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + line-height: 1.3; + padding: var(--gap); + } + + // override theme-default + .je-indented-panel { + padding-left: var(--gap); + margin-left: var(--gap); + border-left: 1px solid #ccc + } + + .je-indented-panel--top { + padding-left: var(--gap); + margin-left: var(--gap); + } + + .je-object__container { + + } + + .form-control { + margin-bottom: var(--gap); + } + + .je-form-input-label { + display: block; + margin-bottom: calc(var(--gap) / 2); + font-weight: bold; + } + .je-form-input-description { + margin: 0; + font-size: 12px; + color: #999; + } + .je-child-editor-holder { + margin-bottom: var(--gap); + } + .je-header-button-holder { + display: inline-block; + margin-left: 10px; + font-size: 0.8em; + vertical-align: middle; + } + .je-table { + margin-bottom: 5px; + border-bottom: 1px solid #ccc; + } + + .je-textarea { + height: 150px; + min-height: 150px; + max-height: 500px; + resize: vertical; + } +} diff --git a/editor/index.html b/editor/index.html index 177fbf6..35c7cd8 100644 --- a/editor/index.html +++ b/editor/index.html @@ -11,9 +11,9 @@

JSONCV Editor

-
+
-
+
diff --git a/editor/main.js b/editor/main.js index 9e113dc..6b43d1b 100644 --- a/editor/main.js +++ b/editor/main.js @@ -1,14 +1,17 @@ +import objectPath from 'object-path'; + import { JSONEditor } from '@json-editor/json-editor/dist/jsoneditor'; import * as exampleData from '../sample.resume.json'; import * as jsoncvSchema from '../schema/jsoncv.schema.json'; +import { registerTheme } from './theme'; import { createElement } from './utils'; const propertiesInOrder = ['basics', 'education', 'work', 'skills', 'projects', 'languages', 'interests', 'references', 'awards', 'publications', 'volunteer'] const basicsPropertiesInOrder = ['name', 'label', 'email', 'phone', 'url', 'summary', 'image', 'location', 'profiles'] // toc elements -const elToc = document.querySelector('#editor-toc') +const elToc = document.querySelector('.editor-toc') const tocUl = createElement('ul', { parent: elToc }) @@ -47,10 +50,20 @@ basicsPropertiesInOrder.forEach((name, index) => { }) }) +// add format to schema +const keyFormatMap = { + 'basics.properties.summary': 'textarea', +} +for (const [key, format] of Object.entries(keyFormatMap)) { + objectPath.get(jsoncvSchema.properties, key).format = format +} + // initialize editor -const elEditorContainer = document.querySelector('#editor-container') +registerTheme(JSONEditor) +const elEditorContainer = document.querySelector('.editor-container') const editor = new JSONEditor(elEditorContainer, { schema: jsoncvSchema, + theme: 'mytheme', }); editor.on('ready',() => { editor.setValue(exampleData) diff --git a/editor/styles.scss b/editor/styles.scss index ab45113..da39492 100644 --- a/editor/styles.scss +++ b/editor/styles.scss @@ -1,21 +1,24 @@ +@use 'json-editor'; + #main { display: flex; .left { flex-grow: 1; + padding-right: 16px; } .right { width: 300px; } } -#editor-container { +.editor-container { [data-schemapath]:target { animation: bgFade 2s forwards; } } -#editor-toc { +.editor-toc { position: fixed; } diff --git a/editor/theme.js b/editor/theme.js new file mode 100644 index 0000000..8aa0385 --- /dev/null +++ b/editor/theme.js @@ -0,0 +1,71 @@ +import { AbstractTheme } from '@json-editor/json-editor/src/theme.js'; + +export class MyTheme extends AbstractTheme { + getFormInputLabel (text, req) { + const el = super.getFormInputLabel(text, req) + el.classList.add('je-form-input-label') + return el + } + + getFormInputDescription (text) { + const el = super.getFormInputDescription(text) + el.classList.add('je-form-input-description') + return el + } + + getIndentedPanel () { + const el = super.getIndentedPanel() + el.classList.add('je-indented-panel') + return el + } + + getTopIndentedPanel () { + return this.getIndentedPanel() + } + + getChildEditorHolder () { + const el = super.getChildEditorHolder() + el.classList.add('je-child-editor-holder') + return el + } + + getHeaderButtonHolder () { + const el = this.getButtonHolder() + el.classList.add('je-header-button-holder') + return el + } + + getTable () { + const el = super.getTable() + el.classList.add('je-table') + return el + } + + addInputError (input, text) { + const group = this.closest(input, '.form-control') || input.controlgroup + + if (!input.errmsg) { + input.errmsg = document.createElement('div') + input.errmsg.setAttribute('class', 'errmsg') + input.errmsg.style = input.errmsg.style || {} + input.errmsg.style.color = 'red' + group.appendChild(input.errmsg) + } else { + input.errmsg.style.display = 'block' + } + + input.errmsg.innerHTML = '' + input.errmsg.appendChild(document.createTextNode(text)) + } + + removeInputError (input) { + if (input.style) { + input.style.borderColor = '' + } + if (input.errmsg) input.errmsg.style.display = 'none' + } +} + +export function registerTheme(JSONEditor) { + JSONEditor.defaults.themes['mytheme'] = MyTheme +}