Browse Source

Improve some events (#6360)

script-events
Artur Arseniev 1 year ago
committed by GitHub
parent
commit
cdeae9cb09
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      packages/core/src/dom_components/model/Components.ts
  2. 16
      packages/core/src/editor/index.ts
  3. 37
      packages/core/src/editor/model/Editor.ts
  4. 66
      packages/core/src/editor/types.ts
  5. 5
      packages/core/src/editor/view/EditorView.ts
  6. 14
      packages/core/src/parser/index.ts
  7. 16
      packages/core/src/parser/model/ParserCss.ts
  8. 6
      packages/core/src/parser/model/ParserHtml.ts
  9. 25
      packages/core/src/parser/types.ts
  10. 3
      packages/core/src/undo_manager/index.ts

2
packages/core/src/dom_components/model/Components.ts

@ -267,7 +267,7 @@ Component> {
if (isWrapper && parsed.doctype) { if (isWrapper && parsed.doctype) {
const root = parent as ComponentWrapper; const root = parent as ComponentWrapper;
const { components: bodyCmps, ...restBody } = (parsed.html as ComponentDefinitionDefined) || {}; const { components: bodyCmps = [], ...restBody } = (parsed.html as ComponentDefinitionDefined) || {};
const { components: headCmps, ...restHead } = parsed.head || {}; const { components: headCmps, ...restHead } = parsed.head || {};
components = bodyCmps!; components = bodyCmps!;
root.set(restBody as any, opt); root.set(restBody as any, opt);

16
packages/core/src/editor/index.ts

@ -8,19 +8,7 @@
* }); * });
* ``` * ```
* *
* ## Available Events * {REPLACE_EVENTS}
*
* You can make use of available events in this way
* ```js
* editor.on('EVENT-NAME', (some, argument) => {
* // do something
* })
* ```
*
* * `update` - The structure of the template is updated (its HTML/CSS)
* * `undo` - Undo executed
* * `redo` - Redo executed
* * `load` - Editor is loaded
* *
* ### Components * ### Components
* Check the [Components](/api/components.html) module. * Check the [Components](/api/components.html) module.
@ -91,6 +79,7 @@ import UtilsModule from '../utils';
import html from '../utils/html'; import html from '../utils/html';
import defConfig, { EditorConfig, EditorConfigKeys } from './config/config'; import defConfig, { EditorConfig, EditorConfigKeys } from './config/config';
import EditorModel, { EditorLoadOptions } from './model/Editor'; import EditorModel, { EditorLoadOptions } from './model/Editor';
import { EditorEvents } from './types';
import EditorView from './view/EditorView'; import EditorView from './view/EditorView';
export type ParsedRule = { export type ParsedRule = {
@ -130,6 +119,7 @@ export default class Editor implements IBaseModule<EditorConfig> {
$: any; $: any;
em: EditorModel; em: EditorModel;
config: EditorConfigType; config: EditorConfigType;
events = EditorEvents;
constructor(config: EditorConfig = {}, opts: any = {}) { constructor(config: EditorConfig = {}, opts: any = {}) {
const defaults = defConfig(); const defaults = defConfig();

37
packages/core/src/editor/model/Editor.ts

@ -3,7 +3,7 @@ import Backbone from 'backbone';
import $ from '../../utils/cash-dom'; import $ from '../../utils/cash-dom';
import Extender from '../../utils/extender'; import Extender from '../../utils/extender';
import { hasWin, isEmptyObj, wait } from '../../utils/mixins'; import { hasWin, isEmptyObj, wait } from '../../utils/mixins';
import { AddOptions, Model, Collection, ObjectAny } from '../../common'; import { Model, Collection, ObjectAny } from '../../common';
import Selected from './Selected'; import Selected from './Selected';
import FrameView from '../../canvas/view/FrameView'; import FrameView from '../../canvas/view/FrameView';
import Editor from '..'; import Editor from '..';
@ -45,6 +45,7 @@ import { CanvasSpotBuiltInTypes } from '../../canvas/model/CanvasSpot';
import DataSourceManager from '../../data_sources'; import DataSourceManager from '../../data_sources';
import { ComponentsEvents } from '../../dom_components/types'; import { ComponentsEvents } from '../../dom_components/types';
import { InitEditorConfig } from '../..'; import { InitEditorConfig } from '../..';
import { EditorEvents } from '../types';
Backbone.$ = $; Backbone.$ = $;
@ -89,6 +90,7 @@ const logs = {
export interface EditorLoadOptions { export interface EditorLoadOptions {
/** Clear the editor state (eg. dirty counter, undo manager, etc.). */ /** Clear the editor state (eg. dirty counter, undo manager, etc.). */
clear?: boolean; clear?: boolean;
initial?: boolean;
} }
export default class EditorModel extends Model { export default class EditorModel extends Model {
@ -333,6 +335,7 @@ export default class EditorModel extends Model {
*/ */
loadOnStart() { loadOnStart() {
const { projectData, headless } = this.config; const { projectData, headless } = this.config;
const loadOpts: EditorLoadOptions = { initial: true };
const sm = this.Storage; const sm = this.Storage;
// In `onLoad`, the module will try to load the data from its configurations. // In `onLoad`, the module will try to load the data from its configurations.
@ -345,16 +348,16 @@ export default class EditorModel extends Model {
}; };
if (headless) { if (headless) {
projectData && this.loadData(projectData); projectData && this.loadData(projectData, loadOpts);
postLoad(); postLoad();
} else { } else {
// Defer for storage load events. // Defer for storage load events.
this._storageTimeout = setTimeout(async () => { this._storageTimeout = setTimeout(async () => {
if (projectData) { if (projectData) {
this.loadData(projectData); this.loadData(projectData, loadOpts);
} else if (sm?.canAutoload()) { } else if (sm?.canAutoload()) {
try { try {
await this.load(); await this.load({}, loadOpts);
} catch (error) { } catch (error) {
this.logError(error as string); this.logError(error as string);
} }
@ -388,7 +391,7 @@ export default class EditorModel extends Model {
if (!opts.isClear) { if (!opts.isClear) {
this.updateItr && clearTimeout(this.updateItr); this.updateItr && clearTimeout(this.updateItr);
this.updateItr = setTimeout(() => this.trigger('update')); this.updateItr = setTimeout(() => this.trigger(EditorEvents.update));
} }
if (this.config.noticeOnUnload) { if (this.config.noticeOnUnload) {
@ -851,7 +854,7 @@ export default class EditorModel extends Model {
*/ */
async load<T extends StorageOptions>(options?: T, loadOptions: EditorLoadOptions = {}) { async load<T extends StorageOptions>(options?: T, loadOptions: EditorLoadOptions = {}) {
const result = await this.Storage.load(options); const result = await this.Storage.load(options);
this.loadData(result); this.loadData(result, loadOptions);
// Wait in order to properly update the dirty counter (#5385) // Wait in order to properly update the dirty counter (#5385)
await wait(); await wait();
@ -875,12 +878,15 @@ export default class EditorModel extends Model {
return JSON.parse(JSON.stringify(result)); return JSON.parse(JSON.stringify(result));
} }
loadData(data: ProjectData = {}): ProjectData { loadData(project: ProjectData = {}, opts: EditorLoadOptions = {}): ProjectData {
if (!isEmptyObj(data)) { let loaded = false;
if (!isEmptyObj(project)) {
this.storables.forEach((module) => module.clear()); this.storables.forEach((module) => module.clear());
this.storables.forEach((module) => module.load(data)); this.storables.forEach((module) => module.load(project));
loaded = true;
} }
return data; this.trigger(EditorEvents.projectLoad, { project, loaded, initial: !!opts.initial });
return project;
} }
/** /**
@ -1025,7 +1031,7 @@ export default class EditorModel extends Model {
*/ */
destroyAll() { destroyAll() {
const { config, view } = this; const { config, view } = this;
this.trigger('destroy'); this.trigger(EditorEvents.destroy);
const editor = this.getEditor(); const editor = this.getEditor();
// @ts-ignore // @ts-ignore
const { editors = [] } = config.grapesjs || {}; const { editors = [] } = config.grapesjs || {};
@ -1048,7 +1054,7 @@ export default class EditorModel extends Model {
editors.splice(editors.indexOf(editor), 1); editors.splice(editors.indexOf(editor), 1);
//@ts-ignore //@ts-ignore
hasWin() && $(config.el).empty().attr(this.attrsOrig); hasWin() && $(config.el).empty().attr(this.attrsOrig);
this.trigger('destroyed'); this.trigger(EditorEvents.destroyed);
} }
getEditing(): Component | undefined { getEditing(): Component | undefined {
@ -1066,12 +1072,13 @@ export default class EditorModel extends Model {
} }
log(msg: string, opts: any = {}) { log(msg: string, opts: any = {}) {
const logEvent = EditorEvents.log;
const { ns, level = 'debug' } = opts; const { ns, level = 'debug' } = opts;
this.trigger('log', msg, opts); this.trigger(logEvent, msg, opts);
level && this.trigger(`log:${level}`, msg, opts); level && this.trigger(`${logEvent}:${level}`, msg, opts);
if (ns) { if (ns) {
const logNs = `log-${ns}`; const logNs = `${logEvent}-${ns}`;
this.trigger(logNs, msg, opts); this.trigger(logNs, msg, opts);
level && this.trigger(`${logNs}:${level}`, msg, opts); level && this.trigger(`${logNs}:${level}`, msg, opts);
} }

66
packages/core/src/editor/types.ts

@ -0,0 +1,66 @@
/**{START_EVENTS}*/
export enum EditorEvents {
/**
* @event `update` Event triggered on any change of the project (eg. component added/removed, style changes, etc.)
* @example
* editor.on('update', () => { ... });
*/
update = 'update',
/**
* @event `undo` Undo executed.
* @example
* editor.on('undo', () => { ... });
*/
undo = 'undo',
/**
* @event `redo` Redo executed.
* @example
* editor.on('redo', () => { ... });
*/
redo = 'redo',
/**
* @event `load` Editor is loaded. At this stage, the project is loaded in the editor and elements in the canvas are rendered.
* @example
* editor.on('load', () => { ... });
*/
load = 'load',
/**
* @event `project:load` Project JSON loaded in the editor. The event is triggered on the initial load and on the `editor.loadProjectData` method.
* @example
* editor.on('project:load', ({ project, initial }) => { ... });
*/
projectLoad = 'project:load',
/**
* @event `log` Log message triggered.
* @example
* editor.on('log', (msg, opts) => { ... });
*/
log = 'log',
/**
* @event `telemetry:init` Initial telemetry data are sent.
* @example
* editor.on('telemetry:init', () => { ... });
*/
telemetryInit = 'telemetry:init',
/**
* @event `destroy` Editor started destroy (on `editor.destroy()`).
* @example
* editor.on('destroy', () => { ... });
*/
destroy = 'destroy',
/**
* @event `destroyed` Editor destroyed.
* @example
* editor.on('destroyed', () => { ... });
*/
destroyed = 'destroyed',
}
/**{END_EVENTS}*/

5
packages/core/src/editor/view/EditorView.ts

@ -2,6 +2,7 @@ import { View, $ } from '../../common';
import { getHostName } from '../../utils/host-name'; import { getHostName } from '../../utils/host-name';
import { appendStyles } from '../../utils/mixins'; import { appendStyles } from '../../utils/mixins';
import EditorModel from '../model/Editor'; import EditorModel from '../model/Editor';
import { EditorEvents } from '../types';
export default class EditorView extends View<EditorModel> { export default class EditorView extends View<EditorModel> {
constructor(model: EditorModel) { constructor(model: EditorModel) {
@ -20,7 +21,7 @@ export default class EditorView extends View<EditorModel> {
} }
setTimeout(() => { setTimeout(() => {
model.trigger('load', model.Editor); model.trigger(EditorEvents.load, model.Editor);
model.clearDirtyCount(); model.clearDirtyCount();
}); });
}); });
@ -91,6 +92,6 @@ export default class EditorView extends View<EditorModel> {
} }
}); });
this.trigger('telemetry:sent'); this.trigger(EditorEvents.telemetryInit);
} }
} }

14
packages/core/src/parser/index.ts

@ -13,9 +13,8 @@
* ```js * ```js
* const { Parser } = editor; * const { Parser } = editor;
* ``` * ```
* ## Available Events *
* * `parse:html` - On HTML parse, an object containing the input and the output of the parser is passed as an argument * {REPLACE_EVENTS}
* * `parse:css` - On CSS parse, an object containing the input and the output of the parser is passed as an argument
* *
* ## Methods * ## Methods
* * [getConfig](#getconfig) * * [getConfig](#getconfig)
@ -25,14 +24,17 @@
* @module Parser * @module Parser
*/ */
import { Module } from '../abstract'; import { Module } from '../abstract';
import { ObjectAny } from '../common';
import EditorModel from '../editor/model/Editor'; import EditorModel from '../editor/model/Editor';
import defConfig, { HTMLParserOptions, ParserConfig } from './config/config'; import defConfig, { HTMLParserOptions, ParserConfig } from './config/config';
import ParserCss from './model/ParserCss'; import ParserCss from './model/ParserCss';
import ParserHtml from './model/ParserHtml'; import ParserHtml from './model/ParserHtml';
import { ParserEvents } from './types';
export default class ParserModule extends Module<ParserConfig & { name?: string }> { export default class ParserModule extends Module<ParserConfig & { name?: string }> {
parserHtml: ReturnType<typeof ParserHtml>; parserHtml: ReturnType<typeof ParserHtml>;
parserCss: ReturnType<typeof ParserCss>; parserCss: ReturnType<typeof ParserCss>;
events = ParserEvents;
constructor(em: EditorModel) { constructor(em: EditorModel) {
super(em, 'Parser', defConfig()); super(em, 'Parser', defConfig());
@ -85,5 +87,11 @@ export default class ParserModule extends Module<ParserConfig & { name?: string
return this.parserCss.parse(input); return this.parserCss.parse(input);
} }
__emitEvent(event: string, data: ObjectAny) {
const { em, events } = this;
em.trigger(event, data);
em.trigger(events.all, { event, ...data });
}
destroy() {} destroy() {}
} }

16
packages/core/src/parser/model/ParserCss.ts

@ -3,6 +3,7 @@ import { CssRuleJSON } from '../../css_composer/model/CssRule';
import EditorModel from '../../editor/model/Editor'; import EditorModel from '../../editor/model/Editor';
import { ParsedCssRule, ParserConfig } from '../config/config'; import { ParsedCssRule, ParserConfig } from '../config/config';
import BrowserCssParser, { parseSelector, createNode } from './BrowserParserCss'; import BrowserCssParser, { parseSelector, createNode } from './BrowserParserCss';
import { ParserEvents } from '../types';
const ParserCss = (em?: EditorModel, config: ParserConfig = {}) => ({ const ParserCss = (em?: EditorModel, config: ParserConfig = {}) => ({
/** /**
@ -10,13 +11,22 @@ const ParserCss = (em?: EditorModel, config: ParserConfig = {}) => ({
* @param {String} input CSS string * @param {String} input CSS string
* @return {Array<Object>} * @return {Array<Object>}
*/ */
parse(input: string) { parse(input: string, opts: { throwOnError?: boolean } = {}) {
let output: CssRuleJSON[] = []; let output: CssRuleJSON[] = [];
const { parserCss } = config; const { parserCss } = config;
const editor = em?.Editor; const editor = em?.Editor;
const nodes = parserCss ? parserCss(input, editor!) : BrowserCssParser(input); let nodes: CssRuleJSON[] | ParsedCssRule[] = [];
let error: unknown;
try {
nodes = parserCss ? parserCss(input, editor!) : BrowserCssParser(input);
} catch (err) {
error = err;
if (opts.throwOnError) throw err;
}
nodes.forEach((node) => (output = output.concat(this.checkNode(node)))); nodes.forEach((node) => (output = output.concat(this.checkNode(node))));
em?.trigger('parse:css', { input, output, nodes }); em?.Parser?.__emitEvent(ParserEvents.css, { input, output, nodes, error });
return output; return output;
}, },

6
packages/core/src/parser/model/ParserHtml.ts

@ -6,9 +6,9 @@ import { HTMLParseResult, HTMLParserOptions, ParseNodeOptions, ParserConfig } fr
import BrowserParserHtml from './BrowserParserHtml'; import BrowserParserHtml from './BrowserParserHtml';
import { doctypeToString } from '../../utils/dom'; import { doctypeToString } from '../../utils/dom';
import { isDef } from '../../utils/mixins'; import { isDef } from '../../utils/mixins';
import { ParserEvents } from '../types';
const modelAttrStart = 'data-gjs-'; const modelAttrStart = 'data-gjs-';
const event = 'parse:html';
const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boolean } = {}) => { const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boolean } = {}) => {
return { return {
@ -365,7 +365,7 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo
if (styleStr) res.css = parserCss.parse(styleStr); if (styleStr) res.css = parserCss.parse(styleStr);
} }
em?.trigger(`${event}:root`, { input, root: root }); em?.Parser?.__emitEvent(ParserEvents.htmlRoot, { input, root });
let resHtml: HTMLParseResult['html'] = []; let resHtml: HTMLParseResult['html'] = [];
if (asDocument) { if (asDocument) {
@ -379,7 +379,7 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo
} }
res.html = resHtml; res.html = resHtml;
em?.trigger(event, { input, output: res, options }); em?.Parser?.__emitEvent(ParserEvents.html, { input, output: res, options });
return res; return res;
}, },

25
packages/core/src/parser/types.ts

@ -0,0 +1,25 @@
/**{START_EVENTS}*/
export enum ParserEvents {
/**
* @event `parse:html` On HTML parse, an object containing the input and the output of the parser is passed as an argument.
* @example
* editor.on('parse:html', ({ input, output }) => { ... });
*/
html = 'parse:html',
htmlRoot = 'parse:html:root',
/**
* @event `parse:css` On CSS parse, an object containing the input and the output of the parser is passed as an argument.
* @example
* editor.on('parse:css', ({ input, output }) => { ... });
*/
css = 'parse:css',
/**
* @event `parse` Catch-all event for all the events mentioned above. An object containing all the available data about the triggered event is passed as an argument to the callback.
* @example
* editor.on('parse', ({ event, ... }) => { ... });
*/
all = 'parse',
}
/**{END_EVENTS}*/

3
packages/core/src/undo_manager/index.ts

@ -29,6 +29,7 @@ import { isArray, isBoolean, isEmpty, unique, times } from 'underscore';
import { Module } from '../abstract'; import { Module } from '../abstract';
import EditorModel from '../editor/model/Editor'; import EditorModel from '../editor/model/Editor';
import defConfig, { UndoManagerConfig } from './config'; import defConfig, { UndoManagerConfig } from './config';
import { EditorEvents } from '../editor/types';
export interface UndoGroup { export interface UndoGroup {
index: number; index: number;
@ -144,7 +145,7 @@ export default class UndoManagerModule extends Module<UndoManagerConfig & { name
this.um.on('undo redo', () => { this.um.on('undo redo', () => {
em.getSelectedAll().map((c) => c.trigger('rerender:layer')); em.getSelectedAll().map((c) => c.trigger('rerender:layer'));
}); });
['undo', 'redo'].forEach((ev) => this.um.on(ev, () => em.trigger(ev))); [EditorEvents.undo, EditorEvents.redo].forEach((ev) => this.um.on(ev, () => em.trigger(ev)));
} }
postLoad() { postLoad() {

Loading…
Cancel
Save