|
|
|
@ -2,6 +2,7 @@ |
|
|
|
// and adapted to the GrapesJS's need
|
|
|
|
|
|
|
|
import { isString } from 'underscore'; |
|
|
|
import RichTextEditorModule from '..'; |
|
|
|
import EditorModel from '../../editor/model/Editor'; |
|
|
|
import { on, off, getPointerEvent, getModel } from '../../utils/mixins'; |
|
|
|
|
|
|
|
@ -29,6 +30,7 @@ export interface RichTextEditorOptions { |
|
|
|
actionbar?: HTMLElement; |
|
|
|
actionbarContainer?: HTMLElement; |
|
|
|
styleWithCSS?: boolean; |
|
|
|
module?: RichTextEditorModule; |
|
|
|
} |
|
|
|
|
|
|
|
type EffectOptions = { |
|
|
|
@ -120,7 +122,7 @@ export default class RichTextEditor { |
|
|
|
em: EditorModel; |
|
|
|
settings: RichTextEditorOptions; |
|
|
|
classes!: Record<string, string>; |
|
|
|
actionbar!: HTMLElement; |
|
|
|
actionbar?: HTMLElement; |
|
|
|
actions!: RichTextEditorAction[]; |
|
|
|
el!: HTMLElement; |
|
|
|
doc!: Document; |
|
|
|
@ -168,11 +170,13 @@ export default class RichTextEditor { |
|
|
|
this.actions = actions; |
|
|
|
|
|
|
|
if (!actionbar) { |
|
|
|
const actionbarCont = settings.actionbarContainer; |
|
|
|
actionbar = document.createElement('div'); |
|
|
|
actionbar.className = classes.actionbar; |
|
|
|
actionbarCont?.appendChild(actionbar); |
|
|
|
this.actionbar = actionbar; |
|
|
|
if (!this.isCustom(settings.module)) { |
|
|
|
const actionbarCont = settings.actionbarContainer; |
|
|
|
actionbar = document.createElement('div'); |
|
|
|
actionbar.className = classes.actionbar; |
|
|
|
actionbarCont?.appendChild(actionbar); |
|
|
|
this.actionbar = actionbar; |
|
|
|
} |
|
|
|
actions.forEach(action => this.addAction(action)); |
|
|
|
} |
|
|
|
|
|
|
|
@ -180,6 +184,11 @@ export default class RichTextEditor { |
|
|
|
return this; |
|
|
|
} |
|
|
|
|
|
|
|
isCustom(module?: RichTextEditorModule) { |
|
|
|
const rte = module || this.em.RichTextEditor; |
|
|
|
return !!rte?.config.custom; |
|
|
|
} |
|
|
|
|
|
|
|
destroy() {} |
|
|
|
|
|
|
|
setEl(el: HTMLElement) { |
|
|
|
@ -190,37 +199,41 @@ export default class RichTextEditor { |
|
|
|
updateActiveActions() { |
|
|
|
const actions = this.getActions(); |
|
|
|
actions.forEach(action => { |
|
|
|
const { update } = action; |
|
|
|
const btn = action.btn!; |
|
|
|
const { update, btn } = action; |
|
|
|
const { active, inactive, disabled } = this.classes; |
|
|
|
const state = action.state; |
|
|
|
const name = action.name; |
|
|
|
const doc = this.doc; |
|
|
|
btn.className = btn.className.replace(active, '').trim(); |
|
|
|
btn.className = btn.className.replace(inactive, '').trim(); |
|
|
|
btn.className = btn.className.replace(disabled, '').trim(); |
|
|
|
let currentState = RichTextEditorActionState.INACTIVE; |
|
|
|
|
|
|
|
if (btn) { |
|
|
|
btn.className = btn.className.replace(active, '').trim(); |
|
|
|
btn.className = btn.className.replace(inactive, '').trim(); |
|
|
|
btn.className = btn.className.replace(disabled, '').trim(); |
|
|
|
} |
|
|
|
|
|
|
|
// if there is a state function, which depicts the state,
|
|
|
|
// i.e. `active`, `disabled`, then call it
|
|
|
|
if (state) { |
|
|
|
const newState = state(this, doc); |
|
|
|
currentState = newState; |
|
|
|
switch (newState) { |
|
|
|
case btnState.ACTIVE: |
|
|
|
btn.className += ` ${active}`; |
|
|
|
break; |
|
|
|
case btnState.INACTIVE: |
|
|
|
btn.className += ` ${inactive}`; |
|
|
|
break; |
|
|
|
case btnState.DISABLED: |
|
|
|
btn.className += ` ${disabled}`; |
|
|
|
break; |
|
|
|
if (btn) { |
|
|
|
switch (newState) { |
|
|
|
case btnState.ACTIVE: |
|
|
|
btn.className += ` ${active}`; |
|
|
|
break; |
|
|
|
case btnState.INACTIVE: |
|
|
|
btn.className += ` ${inactive}`; |
|
|
|
break; |
|
|
|
case btnState.DISABLED: |
|
|
|
btn.className += ` ${disabled}`; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// otherwise default to checking if the name command is supported & enabled
|
|
|
|
if (doc.queryCommandSupported(name) && doc.queryCommandState(name)) { |
|
|
|
btn.className += ` ${active}`; |
|
|
|
btn && (btn.className += ` ${active}`); |
|
|
|
currentState = RichTextEditorActionState.ACTIVE; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -243,7 +256,8 @@ export default class RichTextEditor { |
|
|
|
__toggleEffects(enable = false, opts: EffectOptions = {}) { |
|
|
|
const method = enable ? on : off; |
|
|
|
const { el, doc } = this; |
|
|
|
this.actionbarEl().style.display = enable ? '' : 'none'; |
|
|
|
const actionbar = this.actionbarEl(); |
|
|
|
actionbar && (actionbar.style.display = enable ? '' : 'none'); |
|
|
|
el.contentEditable = `${!!enable}`; |
|
|
|
method(el, 'mouseup keyup', this.updateActiveActions); |
|
|
|
method(doc, 'keydown', this.__onKeydown); |
|
|
|
@ -311,11 +325,13 @@ export default class RichTextEditor { |
|
|
|
if (this.actionbar) { |
|
|
|
if (!action.state || (action.state && action.state(this, this.doc) >= 0)) { |
|
|
|
const event = action.event || 'click'; |
|
|
|
// @ts-ignore
|
|
|
|
action.btn[`on${event}`] = () => { |
|
|
|
action.result(this, action); |
|
|
|
this.updateActiveActions(); |
|
|
|
}; |
|
|
|
const { btn } = action; |
|
|
|
if (btn) { |
|
|
|
(btn as any)[`on${event}`] = () => { |
|
|
|
action.result(this, action); |
|
|
|
this.updateActiveActions(); |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
@ -328,23 +344,26 @@ export default class RichTextEditor { |
|
|
|
*/ |
|
|
|
addAction(action: RichTextEditorAction, opts: { sync?: boolean } = {}) { |
|
|
|
const { sync } = opts; |
|
|
|
const btn = document.createElement('span'); |
|
|
|
const icon = action.icon; |
|
|
|
const attr = action.attributes || {}; |
|
|
|
btn.className = this.classes.button; |
|
|
|
action.btn = btn; |
|
|
|
|
|
|
|
for (let key in attr) { |
|
|
|
btn.setAttribute(key, attr[key]); |
|
|
|
} |
|
|
|
const actionbar = this.actionbarEl(); |
|
|
|
|
|
|
|
if (typeof icon == 'string') { |
|
|
|
btn.innerHTML = icon; |
|
|
|
} else { |
|
|
|
btn.appendChild(icon); |
|
|
|
} |
|
|
|
if (actionbar) { |
|
|
|
const { icon, attributes: attr = {} } = action; |
|
|
|
const btn = document.createElement('span'); |
|
|
|
btn.className = this.classes.button; |
|
|
|
action.btn = btn; |
|
|
|
|
|
|
|
for (let key in attr) { |
|
|
|
btn.setAttribute(key, attr[key]); |
|
|
|
} |
|
|
|
|
|
|
|
this.actionbarEl().appendChild(btn); |
|
|
|
if (typeof icon == 'string') { |
|
|
|
btn.innerHTML = icon; |
|
|
|
} else { |
|
|
|
btn.appendChild(icon); |
|
|
|
} |
|
|
|
|
|
|
|
actionbar.appendChild(btn); |
|
|
|
} |
|
|
|
|
|
|
|
if (sync) { |
|
|
|
this.actions.push(action); |
|
|
|
|