Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve some events #6360

Merged
merged 7 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/dom_components/model/Components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Component> {

if (isWrapper && parsed.doctype) {
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 || {};
components = bodyCmps!;
root.set(restBody as any, opt);
Expand Down
16 changes: 3 additions & 13 deletions packages/core/src/editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,7 @@
* });
* ```
*
* ## Available 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
* {REPLACE_EVENTS}
*
* ### Components
* Check the [Components](/api/components.html) module.
Expand Down Expand Up @@ -91,6 +79,7 @@ import UtilsModule from '../utils';
import html from '../utils/html';
import defConfig, { EditorConfig, EditorConfigKeys } from './config/config';
import EditorModel, { EditorLoadOptions } from './model/Editor';
import { EditorEvents } from './types';
import EditorView from './view/EditorView';

export type ParsedRule = {
Expand Down Expand Up @@ -130,6 +119,7 @@ export default class Editor implements IBaseModule<EditorConfig> {
$: any;
em: EditorModel;
config: EditorConfigType;
events = EditorEvents;

constructor(config: EditorConfig = {}, opts: any = {}) {
const defaults = defConfig();
Expand Down
37 changes: 22 additions & 15 deletions packages/core/src/editor/model/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Backbone from 'backbone';
import $ from '../../utils/cash-dom';
import Extender from '../../utils/extender';
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 FrameView from '../../canvas/view/FrameView';
import Editor from '..';
Expand Down Expand Up @@ -45,6 +45,7 @@ import { CanvasSpotBuiltInTypes } from '../../canvas/model/CanvasSpot';
import DataSourceManager from '../../data_sources';
import { ComponentsEvents } from '../../dom_components/types';
import { InitEditorConfig } from '../..';
import { EditorEvents } from '../types';

Backbone.$ = $;

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

export default class EditorModel extends Model {
Expand Down Expand Up @@ -333,6 +335,7 @@ export default class EditorModel extends Model {
*/
loadOnStart() {
const { projectData, headless } = this.config;
const loadOpts: EditorLoadOptions = { initial: true };
const sm = this.Storage;

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

if (headless) {
projectData && this.loadData(projectData);
projectData && this.loadData(projectData, loadOpts);
postLoad();
} else {
// Defer for storage load events.
this._storageTimeout = setTimeout(async () => {
if (projectData) {
this.loadData(projectData);
this.loadData(projectData, loadOpts);
} else if (sm?.canAutoload()) {
try {
await this.load();
await this.load({}, loadOpts);
} catch (error) {
this.logError(error as string);
}
Expand Down Expand Up @@ -388,7 +391,7 @@ export default class EditorModel extends Model {

if (!opts.isClear) {
this.updateItr && clearTimeout(this.updateItr);
this.updateItr = setTimeout(() => this.trigger('update'));
this.updateItr = setTimeout(() => this.trigger(EditorEvents.update));
}

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

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

loadData(data: ProjectData = {}): ProjectData {
if (!isEmptyObj(data)) {
loadData(project: ProjectData = {}, opts: EditorLoadOptions = {}): ProjectData {
let loaded = false;
if (!isEmptyObj(project)) {
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;
}

/**
Expand Down Expand Up @@ -1025,7 +1031,7 @@ export default class EditorModel extends Model {
*/
destroyAll() {
const { config, view } = this;
this.trigger('destroy');
this.trigger(EditorEvents.destroy);
const editor = this.getEditor();
// @ts-ignore
const { editors = [] } = config.grapesjs || {};
Expand All @@ -1048,7 +1054,7 @@ export default class EditorModel extends Model {
editors.splice(editors.indexOf(editor), 1);
//@ts-ignore
hasWin() && $(config.el).empty().attr(this.attrsOrig);
this.trigger('destroyed');
this.trigger(EditorEvents.destroyed);
}

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

log(msg: string, opts: any = {}) {
const logEvent = EditorEvents.log;
const { ns, level = 'debug' } = opts;
this.trigger('log', msg, opts);
level && this.trigger(`log:${level}`, msg, opts);
this.trigger(logEvent, msg, opts);
level && this.trigger(`${logEvent}:${level}`, msg, opts);

if (ns) {
const logNs = `log-${ns}`;
const logNs = `${logEvent}-${ns}`;
this.trigger(logNs, msg, opts);
level && this.trigger(`${logNs}:${level}`, msg, opts);
}
Expand Down
66 changes: 66 additions & 0 deletions packages/core/src/editor/types.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 2 deletions packages/core/src/editor/view/EditorView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { View, $ } from '../../common';
import { getHostName } from '../../utils/host-name';
import { appendStyles } from '../../utils/mixins';
import EditorModel from '../model/Editor';
import { EditorEvents } from '../types';

export default class EditorView extends View<EditorModel> {
constructor(model: EditorModel) {
Expand All @@ -20,7 +21,7 @@ export default class EditorView extends View<EditorModel> {
}

setTimeout(() => {
model.trigger('load', model.Editor);
model.trigger(EditorEvents.load, model.Editor);
model.clearDirtyCount();
});
});
Expand Down Expand Up @@ -91,6 +92,6 @@ export default class EditorView extends View<EditorModel> {
}
});

this.trigger('telemetry:sent');
this.trigger(EditorEvents.telemetryInit);
}
}
14 changes: 11 additions & 3 deletions packages/core/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
* ```js
* 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
* * `parse:css` - On CSS parse, an object containing the input and the output of the parser is passed as an argument
*
* {REPLACE_EVENTS}
*
* ## Methods
* * [getConfig](#getconfig)
Expand All @@ -25,14 +24,17 @@
* @module Parser
*/
import { Module } from '../abstract';
import { ObjectAny } from '../common';
import EditorModel from '../editor/model/Editor';
import defConfig, { HTMLParserOptions, ParserConfig } from './config/config';
import ParserCss from './model/ParserCss';
import ParserHtml from './model/ParserHtml';
import { ParserEvents } from './types';

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

constructor(em: EditorModel) {
super(em, 'Parser', defConfig());
Expand Down Expand Up @@ -85,5 +87,11 @@ export default class ParserModule extends Module<ParserConfig & { name?: string
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() {}
}
16 changes: 13 additions & 3 deletions packages/core/src/parser/model/ParserCss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,30 @@ import { CssRuleJSON } from '../../css_composer/model/CssRule';
import EditorModel from '../../editor/model/Editor';
import { ParsedCssRule, ParserConfig } from '../config/config';
import BrowserCssParser, { parseSelector, createNode } from './BrowserParserCss';
import { ParserEvents } from '../types';

const ParserCss = (em?: EditorModel, config: ParserConfig = {}) => ({
/**
* Parse CSS string to a desired model object
* @param {String} input CSS string
* @return {Array<Object>}
*/
parse(input: string) {
parse(input: string, opts: { throwOnError?: boolean } = {}) {
let output: CssRuleJSON[] = [];
const { parserCss } = config;
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))));
em?.trigger('parse:css', { input, output, nodes });
em?.Parser?.__emitEvent(ParserEvents.css, { input, output, nodes, error });

return output;
},
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/parser/model/ParserHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { HTMLParseResult, HTMLParserOptions, ParseNodeOptions, ParserConfig } fr
import BrowserParserHtml from './BrowserParserHtml';
import { doctypeToString } from '../../utils/dom';
import { isDef } from '../../utils/mixins';
import { ParserEvents } from '../types';

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

const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boolean } = {}) => {
return {
Expand Down Expand Up @@ -365,7 +365,7 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo
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'] = [];

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

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

return res;
},
Expand Down
25 changes: 25 additions & 0 deletions packages/core/src/parser/types.ts
Original file line number Diff line number Diff line change
@@ -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}*/
Loading
Loading