js: make div code editable (#59608)
gitea/godo.js/pipeline/head There was a failure building this commit
Details
gitea/godo.js/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
5179282b9f
commit
d36fc01459
|
@ -1,35 +1,37 @@
|
|||
.godo {
|
||||
--menu-item-font-size: 14px;
|
||||
--menu-item-height: calc( var(--menu-item-font-size) * 2);
|
||||
}
|
||||
|
||||
.godo--editor {
|
||||
border: 1px solid gray;
|
||||
background-color: white;
|
||||
padding: 1em;
|
||||
min-height: 10em;
|
||||
white-space: pre-wrap;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
.godo--editor > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Menus
|
||||
*/
|
||||
|
||||
.menuicons {
|
||||
font-size: 14px;
|
||||
font-size: var(--menu-item-font-size);
|
||||
position: relative;
|
||||
min-width: 2em;
|
||||
display: inline-flex;
|
||||
}
|
||||
.menuicon, .menuicon:hover {
|
||||
padding: .16em .33em;
|
||||
--border-width: 0px;
|
||||
padding: calc( var(--menu-item-height) / 4 - var(--border-width) ) .33em;
|
||||
color: inherit;
|
||||
background-color: #fff;
|
||||
min-width: 2em;
|
||||
border: 0 none;
|
||||
min-width: var(--menu-item-height);
|
||||
border: var(--border-width) none;
|
||||
color: inherit;
|
||||
font-family: sans-serif;
|
||||
line-height: 1;
|
||||
}
|
||||
.menuicon {
|
||||
font-weight: 500;
|
||||
|
@ -48,13 +50,11 @@
|
|||
top: 0;
|
||||
background-color: white;
|
||||
}
|
||||
.godo-blocks-menu > .menuicons {
|
||||
column-gap: .33em;
|
||||
}
|
||||
.godo-blocks-menu .menuicon {
|
||||
--border-width: 1px;
|
||||
color: #555;
|
||||
background-color: white;
|
||||
border: 1px solid #eee;
|
||||
border: var(--border-width) solid #eee;
|
||||
}
|
||||
.godo-blocks-menu .menuicon:hover {
|
||||
color: black;
|
||||
|
@ -214,3 +214,43 @@ div.godo .ProseMirror.godo--editor ul li p {
|
|||
div.godo .ProseMirror.godo--editor a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Editor is form widget
|
||||
*/
|
||||
|
||||
.is-form-widget .godo--editor {
|
||||
border: 1px solid gray;
|
||||
background-color: white;
|
||||
padding: 1em;
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor for HTML Element
|
||||
*/
|
||||
.html-edition {
|
||||
position: relative;
|
||||
}
|
||||
.html-edition .godo--editor {
|
||||
outline: none;
|
||||
}
|
||||
.html-edition--show {
|
||||
--background-color: hsla(0, 0%, 100%, 0.5);
|
||||
--padding: 1em;
|
||||
outline: 1px solid;
|
||||
background-color: var(--background-color);
|
||||
margin: calc( -1 * var(--padding) );
|
||||
margin-top: calc( -1 * var(--menu-item-height) - var(--padding) );
|
||||
margin-bottom: 0;
|
||||
padding: var(--padding);
|
||||
padding-top: 0;
|
||||
}
|
||||
.html-edition--show .godo--editor {
|
||||
padding-top: var(--padding);
|
||||
}
|
||||
.html-edition--show .godo-blocks-menu {
|
||||
// transform: translateY(-1em);
|
||||
background-color: transparent;
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.wrapper {
|
||||
padding: 2em;
|
||||
|
@ -56,7 +57,7 @@
|
|||
|
||||
<h2>Basic Godo</h2>
|
||||
<p>Just p, strong, em & a</p>
|
||||
<form>
|
||||
<form class="form-test">
|
||||
<div class="grid-2cols">
|
||||
<div>
|
||||
<h3>Textarea</h3>
|
||||
|
@ -84,16 +85,16 @@
|
|||
<godo-editor
|
||||
schema="basic"
|
||||
linked-source="basic-godo-source"
|
||||
instant-update
|
||||
update-event="wcs:change"
|
||||
></godo-editor>
|
||||
</div>
|
||||
</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
|
||||
<h2>Full Godo</h2>
|
||||
<p>p, h1 -> h2, strong, em, a, ul</p>
|
||||
<form action="">
|
||||
<form class="form-test">
|
||||
<div class="grid-2cols">
|
||||
<div>
|
||||
<h3>Textarea</h3>
|
||||
|
@ -120,13 +121,48 @@
|
|||
<h3>Editor</h3>
|
||||
<godo-editor
|
||||
linked-source="full-godo-source"
|
||||
heading-level="1,2"
|
||||
heading-levels="1,2"
|
||||
instant-update
|
||||
update-event="wcs:change"
|
||||
></godo-editor>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h2>Full Godo ‑ div as linked source</h2>
|
||||
|
||||
<h3>DIV</h3>
|
||||
|
||||
<div class="editableDiv-for-godo" id="div-godo-source">
|
||||
<p>This text is div childs</p>
|
||||
|
||||
<h1>Quamquam haec quidem <strong>praeposita</strong></h1>
|
||||
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Haeret in salebra. <a href='http://loripsum.net/' target='_blank'>Sed tamen intellego quid velit</a>.</p>
|
||||
|
||||
<p>Qualem igitur <strong>hominem natura</strong> inchoavit ? Duo Reges: constructio interrete. Summum a vobis bonum voluptas dicitur. Huius, Lyco, oratione locuples, rebus ipsis ielunior. Cui Tubuli nomen odio non est? Ea possunt paria non esse. Aliter autem vobis placet. </p>
|
||||
|
||||
<h2>Recte et <em>reiecta dicere</em> licebit</h2>
|
||||
|
||||
<ul>
|
||||
<li>Haec mihi <em>videtur delicatior</em>, ut ita dicam, molliorque ratio, quam virtutis vis gravitasque postulat.</li>
|
||||
<li>Mihi quidem Antiochum, quem audis, satis belle videris attendere.</li>
|
||||
<li>Nam illud quidem adduci vix possum, ut ea, quae senserit ille, tibi non vera videantur.</li>
|
||||
<li>Sed id ne cogitari quidem potest quale sit, ut non repugnet ipsum sibi.</li>
|
||||
<li>Cum autem in quo sapienter dicimus, id a primo rectissime dicitur.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<godo-editor
|
||||
tabindex="0"
|
||||
linked-source="div-godo-source"
|
||||
heading-levels="1,2"
|
||||
id="directHTMLEditor"
|
||||
></godo-editor>
|
||||
<div>
|
||||
<!-- <button id="editDiv">Edit</button> -->
|
||||
<button id="cancelEditDiv">Cancel</button>
|
||||
<button id="SaveEdition">Save</button>
|
||||
</div>
|
||||
</main>
|
||||
</div><!-- #wrapper -->
|
||||
|
||||
|
@ -142,6 +178,60 @@
|
|||
textarea.dispatchEvent(new CustomEvent(btn.dataset.event))
|
||||
})
|
||||
})
|
||||
|
||||
const formsTest = document.querySelectorAll('.form-test')
|
||||
formsTest.forEach((form) => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault()
|
||||
})
|
||||
})
|
||||
|
||||
// add buttons events for div editing
|
||||
const editor = document.getElementById('directHTMLEditor')
|
||||
const cancelBtn = document.getElementById('cancelEditDiv')
|
||||
const saveBtn = document.getElementById('SaveEdition')
|
||||
|
||||
cancelBtn.hidden = true
|
||||
saveBtn.hidden = true
|
||||
|
||||
editor.addEventListener('focusin', () => {
|
||||
if (editor.view.hasFocus) {
|
||||
editor.showEdition()
|
||||
cancelBtn.hidden = false
|
||||
saveBtn.hidden = false
|
||||
}
|
||||
})
|
||||
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
editor.cancelEdition()
|
||||
cancelBtn.hidden = true
|
||||
saveBtn.hidden = true
|
||||
})
|
||||
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
const url = 'https://jsonplaceholder.typicode.com/posts'
|
||||
const text = editor.getHTML()
|
||||
|
||||
const request = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
'text': text,
|
||||
'token': 'pasaran'
|
||||
})
|
||||
})
|
||||
|
||||
if (!request.ok) {
|
||||
throw new Error(`HTTP error! status: ${request.status}`)
|
||||
}
|
||||
|
||||
editor.validEdition()
|
||||
cancelBtn.hidden = true
|
||||
saveBtn.hidden = true
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -81,6 +81,14 @@ const languageContent = {
|
|||
text: 'italique',
|
||||
},
|
||||
},
|
||||
'edit': {
|
||||
en: {
|
||||
text: 'edit',
|
||||
},
|
||||
fr: {
|
||||
text: 'éditer',
|
||||
},
|
||||
},
|
||||
'ok': {
|
||||
en: {
|
||||
text: 'validate',
|
||||
|
|
|
@ -32,7 +32,7 @@ function changeEventPlugin (editor) {
|
|||
if (tr.docChanged) {
|
||||
clearTimeout(callback)
|
||||
callback = setTimeout(() => {
|
||||
editor.linkedSource.value = editor.getHTML(tr.doc.content)
|
||||
editor.sourceContent = editor.getHTML(tr.doc.content)
|
||||
editor.linkedSource.dispatchEvent(new Event('change', { 'bubbles': true }))
|
||||
}, 300)
|
||||
}
|
||||
|
@ -45,13 +45,23 @@ function changeEventPlugin (editor) {
|
|||
export default class Godo extends window.HTMLElement {
|
||||
connectedCallback () {
|
||||
const linkedSourceId = this.getAttribute('linked-source')
|
||||
this.linkedSource = document.querySelector(`#${linkedSourceId}`)
|
||||
this.linkedSource = document.getElementById(linkedSourceId)
|
||||
|
||||
if (this.linkedSource.form) {
|
||||
this.on()
|
||||
this.editorWrapper.classList.add('is-form-widget')
|
||||
} else {
|
||||
this.initHTMLEditor()
|
||||
}
|
||||
}
|
||||
|
||||
on () {
|
||||
this.options = {
|
||||
schema: this.getAttribute('schema') || 'full',
|
||||
instantUpdate: this.getAttribute('instant-update') !== null,
|
||||
updateEvent: this.getAttribute('update-event'),
|
||||
headingLevels: this.getAttribute('heading-levels'),
|
||||
editable: !this.linkedSource.readOnly,
|
||||
}
|
||||
|
||||
const componentsDefinitions = [base, fontMarks, link]
|
||||
|
@ -70,31 +80,31 @@ export default class Godo extends window.HTMLElement {
|
|||
this.blocksMenu = menuPlugin(blocks(components, this.schema))
|
||||
this.menuDialog = document.body.appendChild(dialog)
|
||||
|
||||
let pluginsList = [
|
||||
this.pluginsList = [
|
||||
history(),
|
||||
keymap(buildKeymap(components, this.schema, null)),
|
||||
keymap(baseKeymap),
|
||||
this.marksMenu,
|
||||
]
|
||||
|
||||
if (this.options.schema !== 'basic' && !this.linkedSource.readOnly) pluginsList.push(this.blocksMenu)
|
||||
if (this.options.instantUpdate) pluginsList.push(changeEventPlugin(this))
|
||||
if (this.options.schema !== 'basic') this.pluginsList.push(this.blocksMenu)
|
||||
if (this.options.instantUpdate) this.pluginsList.push(changeEventPlugin(this))
|
||||
|
||||
const stateConfig = () => {
|
||||
this.stateConfig = () => {
|
||||
return {
|
||||
schema: this.schema,
|
||||
doc: DOMParser.fromSchema(this.schema).parse(this.getSourceContent()),
|
||||
plugins: pluginsList,
|
||||
doc: DOMParser.fromSchema(this.schema).parse(this.sourceContent),
|
||||
plugins: this.options.editable ? this.pluginsList : [],
|
||||
}
|
||||
}
|
||||
|
||||
this.state = EditorState.create(stateConfig())
|
||||
this.state = EditorState.create(this.stateConfig())
|
||||
|
||||
const labelSelector = `label[for=${this.linkedSource.id}]`
|
||||
const label = this.linkedSource.id ? document.querySelector(labelSelector) : undefined
|
||||
this.view = new EditorView(this.editorWrapper, {
|
||||
state: this.state,
|
||||
editable: () => !this.linkedSource.readOnly,
|
||||
editable: () => this.options.editable,
|
||||
attributes: {
|
||||
class: 'godo--editor',
|
||||
role: 'textbox',
|
||||
|
@ -105,26 +115,60 @@ export default class Godo extends window.HTMLElement {
|
|||
|
||||
if (this.linkedSource.form) {
|
||||
this.linkedSource.form.addEventListener('submit', () => {
|
||||
this.linkedSource.value = this.getHTML(this.view.state.doc.content)
|
||||
this.sourceContent = this.getHTML()
|
||||
})
|
||||
}
|
||||
|
||||
const updateEvent = this.options.updateEvent
|
||||
if (updateEvent) {
|
||||
this.linkedSource.addEventListener(updateEvent, () => {
|
||||
this.state = EditorState.create(stateConfig())
|
||||
this.view.updateState(this.state)
|
||||
this.resetFromSource()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getSourceContent () {
|
||||
initHTMLEditor () {
|
||||
this.on()
|
||||
this.activePlugins = false
|
||||
this.linkedSource.hidden = true
|
||||
this.editorWrapper.classList.add('html-edition')
|
||||
|
||||
this.showEdition = () => {
|
||||
if (!this.activePlugins) {
|
||||
this.activePlugins = true
|
||||
this.editorWrapper.classList.add('html-edition--show')
|
||||
}
|
||||
}
|
||||
|
||||
this.cancelEdition = () => {
|
||||
this.resetFromSource()
|
||||
this.activePlugins = false
|
||||
this.editorWrapper.classList.remove('html-edition--show')
|
||||
}
|
||||
|
||||
this.validEdition = () => {
|
||||
this.sourceContent = this.getHTML()
|
||||
this.activePlugins = false
|
||||
this.editorWrapper.classList.remove('html-edition--show')
|
||||
}
|
||||
}
|
||||
|
||||
get sourceContent () {
|
||||
const w = document.createElement('div')
|
||||
w.innerHTML = this.linkedSource.value
|
||||
w.innerHTML = this.linkedSource.form ? this.linkedSource.value : this.linkedSource.innerHTML
|
||||
return w
|
||||
}
|
||||
|
||||
set sourceContent (newContent) {
|
||||
if (this.linkedSource.form) {
|
||||
this.linkedSource.value = newContent
|
||||
} else {
|
||||
this.linkedSource.innerHTML = newContent
|
||||
}
|
||||
}
|
||||
|
||||
getHTML (from) {
|
||||
from = from || this.view.state.doc.content
|
||||
const div = document.createElement('div')
|
||||
const fragment = DOMSerializer
|
||||
.fromSchema(this.schema)
|
||||
|
@ -132,6 +176,24 @@ export default class Godo extends window.HTMLElement {
|
|||
div.appendChild(fragment)
|
||||
return div.innerHTML
|
||||
}
|
||||
|
||||
get activePlugins () {
|
||||
return this._activePlugins
|
||||
}
|
||||
|
||||
set activePlugins (bool) {
|
||||
const newState = this.view.state.reconfigure({
|
||||
plugins: (bool) ? this.pluginsList : [],
|
||||
})
|
||||
this.view.updateState(newState)
|
||||
this.view.updatePluginViews()
|
||||
this._activePlugins = bool
|
||||
}
|
||||
|
||||
resetFromSource () {
|
||||
this.state = EditorState.create(this.stateConfig())
|
||||
this.view.updateState(this.state)
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('godo-editor', Godo)
|
||||
|
|
|
@ -19,9 +19,11 @@ function headingComponents () {
|
|||
domTest('Heading levels option', ({appendToDom}) => {
|
||||
const dom = appendToDom(`
|
||||
<div>
|
||||
<textarea hidden id="marshmallow">
|
||||
<h1>Initial Content</h1>
|
||||
</textarea>
|
||||
<form>
|
||||
<textarea hidden id="marshmallow">
|
||||
<h1>Initial Content</h1>
|
||||
</textarea>
|
||||
</form>
|
||||
<godo-editor linked-source="marshmallow" heading-levels="4,5,6" >
|
||||
</godo-editor>
|
||||
</div>
|
||||
|
|
|
@ -7,9 +7,11 @@ domTest('content is moved from linked source', ({appendToDom}) => {
|
|||
// the input value
|
||||
const dom = appendToDom(`
|
||||
<div>
|
||||
<textarea hidden id="marshmallow">
|
||||
<invalid-tag>Initial Content</invalid-tag>
|
||||
</textarea>
|
||||
<form>
|
||||
<textarea hidden id="marshmallow">
|
||||
<invalid-tag>Initial Content</invalid-tag>
|
||||
</textarea>
|
||||
</form>
|
||||
<godo-editor linked-source="marshmallow">
|
||||
</godo-editor>
|
||||
</div>
|
||||
|
@ -23,8 +25,10 @@ domTest('godo content is updated from source on update-event', ({appendToDom}) =
|
|||
// the text area value
|
||||
const dom = appendToDom(`
|
||||
<div>
|
||||
<textarea hidden id="marshmallow">
|
||||
</textarea>
|
||||
<form>
|
||||
<textarea hidden id="marshmallow">
|
||||
</textarea>
|
||||
</form>
|
||||
<godo-editor update-event="update-me" linked-source="marshmallow" >
|
||||
</godo-editor>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue