Skip to content

Commit cdeae9c

Browse files
authored
Improve some events (#6360)
1 parent 21f51ae commit cdeae9c

File tree

10 files changed

+149
-41
lines changed

10 files changed

+149
-41
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ Component> {
267267

268268
if (isWrapper && parsed.doctype) {
269269
const root = parent as ComponentWrapper;
270-
const { components: bodyCmps, ...restBody } = (parsed.html as ComponentDefinitionDefined) || {};
270+
const { components: bodyCmps = [], ...restBody } = (parsed.html as ComponentDefinitionDefined) || {};
271271
const { components: headCmps, ...restHead } = parsed.head || {};
272272
components = bodyCmps!;
273273
root.set(restBody as any, opt);

packages/core/src/editor/index.ts

+3-13
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,7 @@
88
* });
99
* ```
1010
*
11-
* ## Available Events
12-
*
13-
* You can make use of available events in this way
14-
* ```js
15-
* editor.on('EVENT-NAME', (some, argument) => {
16-
* // do something
17-
* })
18-
* ```
19-
*
20-
* * `update` - The structure of the template is updated (its HTML/CSS)
21-
* * `undo` - Undo executed
22-
* * `redo` - Redo executed
23-
* * `load` - Editor is loaded
11+
* {REPLACE_EVENTS}
2412
*
2513
* ### Components
2614
* Check the [Components](/api/components.html) module.
@@ -91,6 +79,7 @@ import UtilsModule from '../utils';
9179
import html from '../utils/html';
9280
import defConfig, { EditorConfig, EditorConfigKeys } from './config/config';
9381
import EditorModel, { EditorLoadOptions } from './model/Editor';
82+
import { EditorEvents } from './types';
9483
import EditorView from './view/EditorView';
9584

9685
export type ParsedRule = {
@@ -130,6 +119,7 @@ export default class Editor implements IBaseModule<EditorConfig> {
130119
$: any;
131120
em: EditorModel;
132121
config: EditorConfigType;
122+
events = EditorEvents;
133123

134124
constructor(config: EditorConfig = {}, opts: any = {}) {
135125
const defaults = defConfig();

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

+22-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Backbone from 'backbone';
33
import $ from '../../utils/cash-dom';
44
import Extender from '../../utils/extender';
55
import { hasWin, isEmptyObj, wait } from '../../utils/mixins';
6-
import { AddOptions, Model, Collection, ObjectAny } from '../../common';
6+
import { Model, Collection, ObjectAny } from '../../common';
77
import Selected from './Selected';
88
import FrameView from '../../canvas/view/FrameView';
99
import Editor from '..';
@@ -45,6 +45,7 @@ import { CanvasSpotBuiltInTypes } from '../../canvas/model/CanvasSpot';
4545
import DataSourceManager from '../../data_sources';
4646
import { ComponentsEvents } from '../../dom_components/types';
4747
import { InitEditorConfig } from '../..';
48+
import { EditorEvents } from '../types';
4849

4950
Backbone.$ = $;
5051

@@ -89,6 +90,7 @@ const logs = {
8990
export interface EditorLoadOptions {
9091
/** Clear the editor state (eg. dirty counter, undo manager, etc.). */
9192
clear?: boolean;
93+
initial?: boolean;
9294
}
9395

9496
export default class EditorModel extends Model {
@@ -333,6 +335,7 @@ export default class EditorModel extends Model {
333335
*/
334336
loadOnStart() {
335337
const { projectData, headless } = this.config;
338+
const loadOpts: EditorLoadOptions = { initial: true };
336339
const sm = this.Storage;
337340

338341
// In `onLoad`, the module will try to load the data from its configurations.
@@ -345,16 +348,16 @@ export default class EditorModel extends Model {
345348
};
346349

347350
if (headless) {
348-
projectData && this.loadData(projectData);
351+
projectData && this.loadData(projectData, loadOpts);
349352
postLoad();
350353
} else {
351354
// Defer for storage load events.
352355
this._storageTimeout = setTimeout(async () => {
353356
if (projectData) {
354-
this.loadData(projectData);
357+
this.loadData(projectData, loadOpts);
355358
} else if (sm?.canAutoload()) {
356359
try {
357-
await this.load();
360+
await this.load({}, loadOpts);
358361
} catch (error) {
359362
this.logError(error as string);
360363
}
@@ -388,7 +391,7 @@ export default class EditorModel extends Model {
388391

389392
if (!opts.isClear) {
390393
this.updateItr && clearTimeout(this.updateItr);
391-
this.updateItr = setTimeout(() => this.trigger('update'));
394+
this.updateItr = setTimeout(() => this.trigger(EditorEvents.update));
392395
}
393396

394397
if (this.config.noticeOnUnload) {
@@ -851,7 +854,7 @@ export default class EditorModel extends Model {
851854
*/
852855
async load<T extends StorageOptions>(options?: T, loadOptions: EditorLoadOptions = {}) {
853856
const result = await this.Storage.load(options);
854-
this.loadData(result);
857+
this.loadData(result, loadOptions);
855858
// Wait in order to properly update the dirty counter (#5385)
856859
await wait();
857860

@@ -875,12 +878,15 @@ export default class EditorModel extends Model {
875878
return JSON.parse(JSON.stringify(result));
876879
}
877880

878-
loadData(data: ProjectData = {}): ProjectData {
879-
if (!isEmptyObj(data)) {
881+
loadData(project: ProjectData = {}, opts: EditorLoadOptions = {}): ProjectData {
882+
let loaded = false;
883+
if (!isEmptyObj(project)) {
880884
this.storables.forEach((module) => module.clear());
881-
this.storables.forEach((module) => module.load(data));
885+
this.storables.forEach((module) => module.load(project));
886+
loaded = true;
882887
}
883-
return data;
888+
this.trigger(EditorEvents.projectLoad, { project, loaded, initial: !!opts.initial });
889+
return project;
884890
}
885891

886892
/**
@@ -1025,7 +1031,7 @@ export default class EditorModel extends Model {
10251031
*/
10261032
destroyAll() {
10271033
const { config, view } = this;
1028-
this.trigger('destroy');
1034+
this.trigger(EditorEvents.destroy);
10291035
const editor = this.getEditor();
10301036
// @ts-ignore
10311037
const { editors = [] } = config.grapesjs || {};
@@ -1048,7 +1054,7 @@ export default class EditorModel extends Model {
10481054
editors.splice(editors.indexOf(editor), 1);
10491055
//@ts-ignore
10501056
hasWin() && $(config.el).empty().attr(this.attrsOrig);
1051-
this.trigger('destroyed');
1057+
this.trigger(EditorEvents.destroyed);
10521058
}
10531059

10541060
getEditing(): Component | undefined {
@@ -1066,12 +1072,13 @@ export default class EditorModel extends Model {
10661072
}
10671073

10681074
log(msg: string, opts: any = {}) {
1075+
const logEvent = EditorEvents.log;
10691076
const { ns, level = 'debug' } = opts;
1070-
this.trigger('log', msg, opts);
1071-
level && this.trigger(`log:${level}`, msg, opts);
1077+
this.trigger(logEvent, msg, opts);
1078+
level && this.trigger(`${logEvent}:${level}`, msg, opts);
10721079

10731080
if (ns) {
1074-
const logNs = `log-${ns}`;
1081+
const logNs = `${logEvent}-${ns}`;
10751082
this.trigger(logNs, msg, opts);
10761083
level && this.trigger(`${logNs}:${level}`, msg, opts);
10771084
}

packages/core/src/editor/types.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**{START_EVENTS}*/
2+
export enum EditorEvents {
3+
/**
4+
* @event `update` Event triggered on any change of the project (eg. component added/removed, style changes, etc.)
5+
* @example
6+
* editor.on('update', () => { ... });
7+
*/
8+
update = 'update',
9+
10+
/**
11+
* @event `undo` Undo executed.
12+
* @example
13+
* editor.on('undo', () => { ... });
14+
*/
15+
undo = 'undo',
16+
17+
/**
18+
* @event `redo` Redo executed.
19+
* @example
20+
* editor.on('redo', () => { ... });
21+
*/
22+
redo = 'redo',
23+
24+
/**
25+
* @event `load` Editor is loaded. At this stage, the project is loaded in the editor and elements in the canvas are rendered.
26+
* @example
27+
* editor.on('load', () => { ... });
28+
*/
29+
load = 'load',
30+
31+
/**
32+
* @event `project:load` Project JSON loaded in the editor. The event is triggered on the initial load and on the `editor.loadProjectData` method.
33+
* @example
34+
* editor.on('project:load', ({ project, initial }) => { ... });
35+
*/
36+
projectLoad = 'project:load',
37+
38+
/**
39+
* @event `log` Log message triggered.
40+
* @example
41+
* editor.on('log', (msg, opts) => { ... });
42+
*/
43+
log = 'log',
44+
45+
/**
46+
* @event `telemetry:init` Initial telemetry data are sent.
47+
* @example
48+
* editor.on('telemetry:init', () => { ... });
49+
*/
50+
telemetryInit = 'telemetry:init',
51+
52+
/**
53+
* @event `destroy` Editor started destroy (on `editor.destroy()`).
54+
* @example
55+
* editor.on('destroy', () => { ... });
56+
*/
57+
destroy = 'destroy',
58+
59+
/**
60+
* @event `destroyed` Editor destroyed.
61+
* @example
62+
* editor.on('destroyed', () => { ... });
63+
*/
64+
destroyed = 'destroyed',
65+
}
66+
/**{END_EVENTS}*/

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { View, $ } from '../../common';
22
import { getHostName } from '../../utils/host-name';
33
import { appendStyles } from '../../utils/mixins';
44
import EditorModel from '../model/Editor';
5+
import { EditorEvents } from '../types';
56

67
export default class EditorView extends View<EditorModel> {
78
constructor(model: EditorModel) {
@@ -20,7 +21,7 @@ export default class EditorView extends View<EditorModel> {
2021
}
2122

2223
setTimeout(() => {
23-
model.trigger('load', model.Editor);
24+
model.trigger(EditorEvents.load, model.Editor);
2425
model.clearDirtyCount();
2526
});
2627
});
@@ -91,6 +92,6 @@ export default class EditorView extends View<EditorModel> {
9192
}
9293
});
9394

94-
this.trigger('telemetry:sent');
95+
this.trigger(EditorEvents.telemetryInit);
9596
}
9697
}

packages/core/src/parser/index.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
* ```js
1414
* const { Parser } = editor;
1515
* ```
16-
* ## Available Events
17-
* * `parse:html` - On HTML parse, an object containing the input and the output of the parser is passed as an argument
18-
* * `parse:css` - On CSS parse, an object containing the input and the output of the parser is passed as an argument
16+
*
17+
* {REPLACE_EVENTS}
1918
*
2019
* ## Methods
2120
* * [getConfig](#getconfig)
@@ -25,14 +24,17 @@
2524
* @module Parser
2625
*/
2726
import { Module } from '../abstract';
27+
import { ObjectAny } from '../common';
2828
import EditorModel from '../editor/model/Editor';
2929
import defConfig, { HTMLParserOptions, ParserConfig } from './config/config';
3030
import ParserCss from './model/ParserCss';
3131
import ParserHtml from './model/ParserHtml';
32+
import { ParserEvents } from './types';
3233

3334
export default class ParserModule extends Module<ParserConfig & { name?: string }> {
3435
parserHtml: ReturnType<typeof ParserHtml>;
3536
parserCss: ReturnType<typeof ParserCss>;
37+
events = ParserEvents;
3638

3739
constructor(em: EditorModel) {
3840
super(em, 'Parser', defConfig());
@@ -85,5 +87,11 @@ export default class ParserModule extends Module<ParserConfig & { name?: string
8587
return this.parserCss.parse(input);
8688
}
8789

90+
__emitEvent(event: string, data: ObjectAny) {
91+
const { em, events } = this;
92+
em.trigger(event, data);
93+
em.trigger(events.all, { event, ...data });
94+
}
95+
8896
destroy() {}
8997
}

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,30 @@ import { CssRuleJSON } from '../../css_composer/model/CssRule';
33
import EditorModel from '../../editor/model/Editor';
44
import { ParsedCssRule, ParserConfig } from '../config/config';
55
import BrowserCssParser, { parseSelector, createNode } from './BrowserParserCss';
6+
import { ParserEvents } from '../types';
67

78
const ParserCss = (em?: EditorModel, config: ParserConfig = {}) => ({
89
/**
910
* Parse CSS string to a desired model object
1011
* @param {String} input CSS string
1112
* @return {Array<Object>}
1213
*/
13-
parse(input: string) {
14+
parse(input: string, opts: { throwOnError?: boolean } = {}) {
1415
let output: CssRuleJSON[] = [];
1516
const { parserCss } = config;
1617
const editor = em?.Editor;
17-
const nodes = parserCss ? parserCss(input, editor!) : BrowserCssParser(input);
18+
let nodes: CssRuleJSON[] | ParsedCssRule[] = [];
19+
let error: unknown;
20+
21+
try {
22+
nodes = parserCss ? parserCss(input, editor!) : BrowserCssParser(input);
23+
} catch (err) {
24+
error = err;
25+
if (opts.throwOnError) throw err;
26+
}
27+
1828
nodes.forEach((node) => (output = output.concat(this.checkNode(node))));
19-
em?.trigger('parse:css', { input, output, nodes });
29+
em?.Parser?.__emitEvent(ParserEvents.css, { input, output, nodes, error });
2030

2131
return output;
2232
},

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { HTMLParseResult, HTMLParserOptions, ParseNodeOptions, ParserConfig } fr
66
import BrowserParserHtml from './BrowserParserHtml';
77
import { doctypeToString } from '../../utils/dom';
88
import { isDef } from '../../utils/mixins';
9+
import { ParserEvents } from '../types';
910

1011
const modelAttrStart = 'data-gjs-';
11-
const event = 'parse:html';
1212

1313
const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boolean } = {}) => {
1414
return {
@@ -365,7 +365,7 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo
365365
if (styleStr) res.css = parserCss.parse(styleStr);
366366
}
367367

368-
em?.trigger(`${event}:root`, { input, root: root });
368+
em?.Parser?.__emitEvent(ParserEvents.htmlRoot, { input, root });
369369
let resHtml: HTMLParseResult['html'] = [];
370370

371371
if (asDocument) {
@@ -379,7 +379,7 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo
379379
}
380380

381381
res.html = resHtml;
382-
em?.trigger(event, { input, output: res, options });
382+
em?.Parser?.__emitEvent(ParserEvents.html, { input, output: res, options });
383383

384384
return res;
385385
},

packages/core/src/parser/types.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**{START_EVENTS}*/
2+
export enum ParserEvents {
3+
/**
4+
* @event `parse:html` On HTML parse, an object containing the input and the output of the parser is passed as an argument.
5+
* @example
6+
* editor.on('parse:html', ({ input, output }) => { ... });
7+
*/
8+
html = 'parse:html',
9+
htmlRoot = 'parse:html:root',
10+
11+
/**
12+
* @event `parse:css` On CSS parse, an object containing the input and the output of the parser is passed as an argument.
13+
* @example
14+
* editor.on('parse:css', ({ input, output }) => { ... });
15+
*/
16+
css = 'parse:css',
17+
18+
/**
19+
* @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.
20+
* @example
21+
* editor.on('parse', ({ event, ... }) => { ... });
22+
*/
23+
all = 'parse',
24+
}
25+
/**{END_EVENTS}*/

0 commit comments

Comments
 (0)