/** * You can customize the initial state of the module from the editor initialization, by passing the following [Configuration Object](https://github.com/GrapesJS/grapesjs/blob/master/src/modal_dialog/config/config.ts) * ```js * const editor = grapesjs.init({ * modal: { * // options * } * }) * ``` * * Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance * * ```js * const modal = editor.Modal; * ``` * * {REPLACE_EVENTS} * * ## Methods * * [open](#open) * * [close](#close) * * [isOpen](#isopen) * * [setTitle](#settitle) * * [getTitle](#gettitle) * * [setContent](#setcontent) * * [getContent](#getcontent) * * [onceClose](#onceclose) * * [onceOpen](#onceopen) * * @module Modal */ import { debounce, isFunction, isString } from 'underscore'; import { Module } from '../abstract'; import { EventHandler } from '../common'; import EditorModel from '../editor/model/Editor'; import EditorView from '../editor/view/EditorView'; import { createText } from '../utils/dom'; import defConfig, { ModalConfig } from './config/config'; import ModalM from './model/Modal'; import { ModalEvents } from './types'; import ModalView from './view/ModalView'; export type { ModalEvent } from './types'; export default class ModalModule extends Module { modal?: ModalView; events = ModalEvents; /** * Initialize module. Automatically called with a new instance of the editor * @param {Object} config Configurations * @private */ constructor(em: EditorModel) { super(em, 'Modal', defConfig()); const { events } = this; this.model = new ModalM(this); this.model.on('change:open', (m: ModalM, enable: boolean) => { em.trigger(enable ? events.open : events.close); }); this.model.on( 'change', debounce(() => { const data = this._evData(); const { custom } = this.config; //@ts-ignore isFunction(custom) && custom(data); em.trigger(events.all, data); }, 0), ); return this; } _evData() { const titl = this.getTitle(); const cnt = this.getContent(); const { open, attributes } = this.model.attributes; return { open, attributes, title: isString(titl) ? createText(titl) : titl, //@ts-ignore content: isString(cnt) ? createText(cnt) : cnt.get ? cnt.get(0) : cnt, close: () => { this.close(); }, }; } postRender(view: EditorView) { const el = view.model.config.el || view.el; const res = this.render(); res && el?.appendChild(res); } /** * Open the modal window * @param {Object} [opts={}] Options * @param {String|HTMLElement} [opts.title] Title to set for the modal * @param {String|HTMLElement} [opts.content] Content to set for the modal * @param {Object} [opts.attributes] Updates the modal wrapper with custom attributes * @returns {this} * @example * modal.open({ * title: 'My title', * content: 'My content', * attributes: { class: 'my-class' }, * }); */ open(opts: any = {}) { const attr = opts.attributes || {}; opts.title && this.setTitle(opts.title); opts.content && this.setContent(opts.content); this.model.set('attributes', attr); this.model.open(); this.modal && this.modal.updateAttr(attr); return this; } /** * Close the modal window * @returns {this} * @example * modal.close(); */ close() { this.model.close(); return this; } /** * Execute callback when the modal will be closed. * The callback will be called one only time * @param {Function} clb Callback to call * @returns {this} * @example * modal.onceClose(() => { * console.log('The modal is closed'); * }); */ onceClose(clb: EventHandler) { const { em, events } = this; em.once(events.close, clb); return this; } /** * Execute callback when the modal will be opened. * The callback will be called one only time * @param {Function} clb Callback to call * @returns {this} * @example * modal.onceOpen(() => { * console.log('The modal is opened'); * }); */ onceOpen(clb: EventHandler) { const { em, events } = this; em.once(events.open, clb); return this; } /** * Checks if the modal window is open * @returns {Boolean} * @example * modal.isOpen(); // true | false */ isOpen() { return !!this.model.get('open'); } /** * Set the title to the modal window * @param {string | HTMLElement} title Title * @returns {this} * @example * // pass a string * modal.setTitle('Some title'); * // or an HTMLElement * const el = document.createElement('div'); * el.innerText = 'New title'; * modal.setTitle(el); */ setTitle(title: string) { this.model.set('title', title); return this; } /** * Returns the title of the modal window * @returns {string | HTMLElement} * @example * modal.getTitle(); */ getTitle() { return this.model.get('title'); } /** * Set the content of the modal window * @param {string | HTMLElement} content Content * @returns {this} * @example * // pass a string * modal.setContent('Some content'); * // or an HTMLElement * const el = document.createElement('div'); * el.innerText = 'New content'; * modal.setContent(el); */ setContent(content: string | HTMLElement) { this.model.set('content', ' '); this.model.set('content', content); return this; } /** * Get the content of the modal window * @returns {string | HTMLElement} * @example * modal.getContent(); */ getContent(): string | HTMLElement { return this.model.get('content'); } /** * Returns content element * @return {HTMLElement} * @private */ getContentEl(): HTMLElement | undefined { return this.modal?.getContent().get(0); } /** * Returns modal model * @return {Model} * @private */ getModel() { return this.model; } /** * Render the modal window * @return {HTMLElement} * @private */ render(): HTMLElement | undefined { if (this.config.custom) return; const View = ModalView.extend(this.config.extend); const el = this.modal && this.modal.el; this.modal = new View({ el, model: this.model, config: this.config, }); return this.modal?.render().el; } destroy() { this.modal?.remove(); } }