From b9235840756e0516939c8ce2fe256d8a0ff380f1 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Mon, 5 May 2025 16:14:48 +0400 Subject: [PATCH] Custom render refactor (#6510) --- packages/core/src/canvas/config/config.ts | 10 ++++++- packages/core/src/canvas/view/FrameView.ts | 28 +++++++++++++------ .../src/dom_components/model/Component.ts | 2 +- packages/core/src/utils/Droppable.ts | 26 +++-------------- packages/core/src/utils/sorter/Sorter.ts | 19 +++++++------ 5 files changed, 43 insertions(+), 42 deletions(-) diff --git a/packages/core/src/canvas/config/config.ts b/packages/core/src/canvas/config/config.ts index de374e03a..01266fc68 100644 --- a/packages/core/src/canvas/config/config.ts +++ b/packages/core/src/canvas/config/config.ts @@ -5,6 +5,14 @@ import { CanvasSpotBuiltInTypes } from '../model/CanvasSpot'; import Frame from '../model/Frame'; import FrameView from '../view/FrameView'; +export interface CustomRendererProps { + editor: Editor; + frame: Frame; + window: Window; + frameView: FrameView; + onMount: (view: ComponentView) => void; +} + export interface CanvasConfig { stylePrefix?: string; @@ -118,7 +126,7 @@ export interface CanvasConfig { * reactRoot.render(); * } */ - customRenderer?: (options: { editor: Editor; frame: Frame; window: Window; frameView: FrameView }) => ComponentView; + customRenderer?: (props: CustomRendererProps) => void; } const config: () => CanvasConfig = () => ({ diff --git a/packages/core/src/canvas/view/FrameView.ts b/packages/core/src/canvas/view/FrameView.ts index 7eb88d3c3..123127dcf 100644 --- a/packages/core/src/canvas/view/FrameView.ts +++ b/packages/core/src/canvas/view/FrameView.ts @@ -1,18 +1,18 @@ -import { bindAll, debounce, isString, isUndefined } from 'underscore'; +import { bindAll, debounce, isFunction, isString } from 'underscore'; import { ModuleView } from '../../abstract'; import { BoxRect, ObjectAny } from '../../common'; import CssRulesView from '../../css_composer/view/CssRulesView'; -import ComponentWrapperView from '../../dom_components/view/ComponentWrapperView'; -import ComponentView from '../../dom_components/view/ComponentView'; import { type as typeHead } from '../../dom_components/model/ComponentHead'; +import ComponentView from '../../dom_components/view/ComponentView'; +import ComponentWrapperView from '../../dom_components/view/ComponentWrapperView'; +import AutoScroller from '../../utils/AutoScroller'; import Droppable from '../../utils/Droppable'; import { append, appendVNodes, createCustomEvent, createEl, motionsEv, off, on } from '../../utils/dom'; import { hasDnd, setViewEl } from '../../utils/mixins'; import Canvas from '../model/Canvas'; import Frame from '../model/Frame'; -import FrameWrapView from './FrameWrapView'; import CanvasEvents from '../types'; -import AutoScroller from '../../utils/AutoScroller'; +import FrameWrapView from './FrameWrapView'; export default class FrameView extends ModuleView { /** @ts-ignore */ @@ -339,7 +339,6 @@ export default class FrameView extends ModuleView { renderBody() { const { config, em, model, ppfx } = this; - const doc = this.getDoc(); const body = this.getBody(); const win = this.getWindow(); const hasAutoHeight = model.hasAutoHeight(); @@ -429,12 +428,16 @@ export default class FrameView extends ModuleView { const { view } = em?.Components?.getType('wrapper') || {}; if (!view) return; - if (typeof config.customRenderer === 'function') { - this.wrapper = config.customRenderer({ + if (isFunction(config.customRenderer)) { + config.customRenderer({ editor: em.Editor, frame: model, window: win, frameView: this, + onMount: (rootView) => { + this.wrapper = rootView; + this._onRootMount(rootView); + }, }); } else { this.wrapper = new view({ @@ -445,8 +448,15 @@ export default class FrameView extends ModuleView { frameView: this, }, }).render(); + this._onRootMount(this.wrapper!); } - append(body, this.wrapper?.el!); + } + + _onRootMount(rootView: ComponentView) { + const { config, em, model } = this; + const doc = this.getDoc(); + const body = doc.body; + append(body, rootView.el); append( body, new CssRulesView({ diff --git a/packages/core/src/dom_components/model/Component.ts b/packages/core/src/dom_components/model/Component.ts index 8e04c5091..ffaff45ed 100644 --- a/packages/core/src/dom_components/model/Component.ts +++ b/packages/core/src/dom_components/model/Component.ts @@ -323,7 +323,7 @@ export default class Component extends StyleableModel { // Register global updates for collection properties ['classes', 'traits', 'components'].forEach((name) => { - const events = `add remove ${name !== 'components' ? 'change' : ''}`; + const events = `add remove reset ${name !== 'components' ? 'change' : ''}`; this.listenTo(this.get(name), events.trim(), (...args) => this.emitUpdate(name, ...args)); }); diff --git a/packages/core/src/utils/Droppable.ts b/packages/core/src/utils/Droppable.ts index c38396064..368503165 100644 --- a/packages/core/src/utils/Droppable.ts +++ b/packages/core/src/utils/Droppable.ts @@ -187,9 +187,10 @@ export default class Droppable { sorter.eventHandlers.legacyOnEnd = sorterOptions.legacyOnEnd; sorter.containerContext.customTarget = sorterOptions.customTarget; } - let dropModel = this.getTempDropModel(content); - const el = dropModel.view?.el; - const sources = el ? [{ element: el, dragSource: dragSourceOrigin }] : []; + const shallowCmp = em.Components.getShallowWrapper(); + const model = shallowCmp?.append(content, { temporary: true })[0]; + const element = model?.getEl(); + const sources = [{ element, dragSource: { model, ...dragSourceOrigin } }]; sorter.startSort(sources); this.sorter = sorter; this.draggedNode = sorter.sourceNodes?.[0]; @@ -206,25 +207,6 @@ export default class Droppable { em.trigger('canvas:dragenter', dt, content); } - /** - * Generates a temporary model of the content being dragged for use with the sorter. - * @returns The temporary model representing the dragged content. - */ - private getTempDropModel(content?: any) { - const comps = this.em.Components.getComponents(); - const opts = { - avoidChildren: 1, - avoidStore: 1, - avoidUpdateStyle: 1, - }; - const tempModel = comps.add(content, { ...opts, temporary: true }); - let dropModel = comps.remove(tempModel, { ...opts, temporary: true } as any); - // @ts-ignore - dropModel = dropModel instanceof Array ? dropModel[0] : dropModel; - dropModel.view?.$el.data('model', dropModel); - return dropModel; - } - handleDragEnd(model: any, dt: any) { const { em } = this; this.over = false; diff --git a/packages/core/src/utils/sorter/Sorter.ts b/packages/core/src/utils/sorter/Sorter.ts index c1505db17..78897968f 100644 --- a/packages/core/src/utils/sorter/Sorter.ts +++ b/packages/core/src/utils/sorter/Sorter.ts @@ -17,6 +17,11 @@ import { import Dimension from './Dimension'; import { SorterOptions } from './types'; +interface SorterSource { + element?: HTMLElement; + dragSource?: DragSource; +} + export default class Sorter> { em: EditorModel; treeClass: new (model: T, dragSource?: DragSource) => NodeType; @@ -70,7 +75,7 @@ export default class Sorter> { * Picking components to move * @param {HTMLElement[]} sources[] * */ - startSort(sources: { element?: HTMLElement; dragSource?: DragSource }[]) { + startSort(sources: SorterSource[]) { const { sourceNodes, sourcesWithModel } = this.getSourceNodes(sources); this.sourceNodes = sourceNodes; this.dropLocationDeterminer.startSort(sourceNodes); @@ -93,11 +98,7 @@ export default class Sorter> { this.em.trigger('sorter:drag:start', sources[0], sourcesWithModel[0]); } - validTarget( - targetEl: HTMLElement | undefined, - sources: { element?: HTMLElement; dragSource?: DragSource }[], - index: number, - ): boolean { + validTarget(targetEl: HTMLElement | undefined, sources: SorterSource[], index: number): boolean { if (!targetEl) return false; const targetModel = $(targetEl).data('model'); if (!targetModel) return false; @@ -108,12 +109,12 @@ export default class Sorter> { return canMove; } - private getSourceNodes(sources: { element?: HTMLElement; dragSource?: DragSource }[]) { + private getSourceNodes(sources: SorterSource[]) { const validSources = sources.filter((source) => !!source.dragSource || this.findValidSourceElement(source.element)); const sourcesWithModel: { model: T; content?: any }[] = validSources.map((source) => { return { - model: $(source.element)?.data('model'), + model: source.dragSource?.model || $(source.element)?.data('model'), content: source.dragSource, }; }); @@ -188,7 +189,7 @@ export default class Sorter> { /** * Finds the closest valid source element within the container context. - + * @param sourceElement - The initial source element to check. * @returns The closest valid source element, or null if none is found. */