mirror of https://github.com/artf/grapesjs.git
Browse Source
* Move Layers to TS * Update root update in LayerManager. Closes #4083 * Update Layer view * Update ItemsView * Move layer opened container * Clean ItemView * Update import ItemsView * Update layer manager module * Add getComponents to Layers * Add visibility methods to Layers * Add locked check in Layers * Add locked property to Component * Add rename in LayerManager * Up layer listeners * Update layer selection * Update children counter in ItemView * Update visibility methods in ItemView * Add open methods to layers * Update selection * Update hover in layers * Fix layer * Update TS model in LayerManager * Layer config TS * Move ItemView to TS * Move ItemsView to TS * Update TS LayerManager * Update Module and Layers init * Update layer tests * Up item viewpull/4343/head
committed by
GitHub
10 changed files with 543 additions and 374 deletions
@ -1,108 +0,0 @@ |
|||||
import { isElement } from 'underscore'; |
|
||||
import defaults from './config/config'; |
|
||||
import View from './view/ItemView'; |
|
||||
|
|
||||
export default () => { |
|
||||
let em; |
|
||||
let layers; |
|
||||
let config = {}; |
|
||||
|
|
||||
return { |
|
||||
name: 'LayerManager', |
|
||||
|
|
||||
init(opts = {}) { |
|
||||
config = { ...defaults, ...opts }; |
|
||||
config.stylePrefix = opts.pStylePrefix; |
|
||||
em = config.em; |
|
||||
return this; |
|
||||
}, |
|
||||
|
|
||||
getConfig() { |
|
||||
return config; |
|
||||
}, |
|
||||
|
|
||||
onLoad() { |
|
||||
em && em.on('component:selected', this.componentChanged); |
|
||||
this.componentChanged(); |
|
||||
}, |
|
||||
|
|
||||
postRender() { |
|
||||
const elTo = config.appendTo; |
|
||||
const root = config.root; |
|
||||
root && this.setRoot(root); |
|
||||
|
|
||||
if (elTo) { |
|
||||
const el = isElement(elTo) ? elTo : document.querySelector(elTo); |
|
||||
el.appendChild(this.render()); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Set new root for layers |
|
||||
* @param {HTMLElement|Component|String} el Component to be set as the root |
|
||||
* @return {self} |
|
||||
*/ |
|
||||
setRoot(el) { |
|
||||
layers && layers.setRoot(el); |
|
||||
return this; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get the root of layers |
|
||||
* @return {Component} |
|
||||
*/ |
|
||||
getRoot() { |
|
||||
return layers && layers.model; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Return the view of layers |
|
||||
* @return {View} |
|
||||
*/ |
|
||||
getAll() { |
|
||||
return layers; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Triggered when the selected component is changed |
|
||||
* @private |
|
||||
*/ |
|
||||
componentChanged(selected, opts = {}) { |
|
||||
if (opts.fromLayers) return; |
|
||||
const opened = em.get('opened'); |
|
||||
const model = em.getSelected(); |
|
||||
const scroll = config.scrollLayers; |
|
||||
let parent = model && model.collection ? model.collection.parent : null; |
|
||||
for (let cid in opened) opened[cid].set('open', 0); |
|
||||
|
|
||||
while (parent) { |
|
||||
parent.set('open', 1); |
|
||||
opened[parent.cid] = parent; |
|
||||
parent = parent.collection ? parent.collection.parent : null; |
|
||||
} |
|
||||
|
|
||||
if (model && scroll) { |
|
||||
const el = model.viewLayer && model.viewLayer.el; |
|
||||
el && el.scrollIntoView(scroll); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
render() { |
|
||||
const ItemView = View.extend(config.extend); |
|
||||
layers && layers.remove(); |
|
||||
layers = new ItemView({ |
|
||||
ItemView, |
|
||||
level: 0, |
|
||||
config, |
|
||||
opened: config.opened || {}, |
|
||||
model: em.get('DomComponents').getWrapper(), |
|
||||
}); |
|
||||
return layers.render().el; |
|
||||
}, |
|
||||
|
|
||||
destroy() { |
|
||||
layers && layers.remove(); |
|
||||
[em, layers, config].forEach(i => (i = {})); |
|
||||
}, |
|
||||
}; |
|
||||
}; |
|
||||
@ -0,0 +1,286 @@ |
|||||
|
import { isString, bindAll } from 'underscore'; |
||||
|
import { Model } from '../abstract'; |
||||
|
import Module from '../abstract/Module'; |
||||
|
import Component from '../dom_components/model/Component'; |
||||
|
import EditorModel from '../editor/model/Editor'; |
||||
|
import { hasWin, isComponent, isDef } from '../utils/mixins'; |
||||
|
import defaults from './config/config'; |
||||
|
import View from './view/ItemView'; |
||||
|
|
||||
|
interface LayerData { |
||||
|
name: string, |
||||
|
open: boolean, |
||||
|
selected: boolean, |
||||
|
hovered: boolean, |
||||
|
visible: boolean, |
||||
|
locked: boolean, |
||||
|
components: Component[], |
||||
|
} |
||||
|
|
||||
|
export const evAll = 'layer'; |
||||
|
export const evPfx = `${evAll}:`; |
||||
|
export const evRoot = `${evPfx}root`; |
||||
|
export const evComponent = `${evPfx}component`; |
||||
|
|
||||
|
const events = { |
||||
|
all: evAll, |
||||
|
root: evRoot, |
||||
|
component: evComponent, |
||||
|
}; |
||||
|
|
||||
|
const styleOpts = { mediaText: '' }; |
||||
|
|
||||
|
const propsToListen = ['open', 'status', 'locked', 'custom-name', 'components', 'classes'] |
||||
|
.map(p => `component:update:${p}`).join(' '); |
||||
|
|
||||
|
const isStyleHidden = (style: any = {}) => { |
||||
|
return (style.display || '').trim().indexOf('none') === 0; |
||||
|
}; |
||||
|
|
||||
|
export default class LayerManager extends Module<typeof defaults> { |
||||
|
model!: Model; |
||||
|
|
||||
|
view?: View; |
||||
|
|
||||
|
events = events; |
||||
|
|
||||
|
constructor(em: EditorModel) { |
||||
|
super(em, 'LayerManager', defaults); |
||||
|
bindAll(this, 'componentChanged', '__onRootChange', '__onComponent'); |
||||
|
this.model = new Model(this, { opened: {} }); |
||||
|
// @ts-ignore
|
||||
|
this.config.stylePrefix = this.config.pStylePrefix; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
onLoad() { |
||||
|
const { em, config, model } = this; |
||||
|
model.listenTo(em, 'component:selected', this.componentChanged); |
||||
|
model.listenToOnce(em, 'load', () => this.setRoot(config.root)); |
||||
|
model.on('change:root', this.__onRootChange); |
||||
|
model.listenTo(em, propsToListen, this.__onComponent); |
||||
|
this.componentChanged(); |
||||
|
} |
||||
|
|
||||
|
postRender() { |
||||
|
this.__appendTo(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Set new root for layers |
||||
|
* @param {Component|string} component Component to be set as the root |
||||
|
* @return {Component} |
||||
|
*/ |
||||
|
setRoot(component: Component | string): Component { |
||||
|
const wrapper: Component = this.em.getWrapper(); |
||||
|
let root = isComponent(component) ? component as Component : wrapper; |
||||
|
|
||||
|
if (component && isString(component) && hasWin()) { |
||||
|
root = wrapper.find(component)[0] || wrapper; |
||||
|
} |
||||
|
|
||||
|
this.model.set('root', root); |
||||
|
|
||||
|
return root; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the root of layers |
||||
|
* @return {Component} |
||||
|
*/ |
||||
|
getRoot(): Component { |
||||
|
return this.model.get('root'); |
||||
|
} |
||||
|
|
||||
|
getLayerData(component: any): LayerData { |
||||
|
const status = component.get('status'); |
||||
|
|
||||
|
return { |
||||
|
name: component.getName(), |
||||
|
open: this.isOpen(component), |
||||
|
selected: status === 'selected', |
||||
|
hovered: status === 'hovered', // || this.em.getHovered() === component,
|
||||
|
visible: this.isVisible(component), |
||||
|
locked: this.isLocked(component), |
||||
|
components: this.getComponents(component), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
setLayerData(component: any, data: Partial<Omit<LayerData, 'components'>>, opts = {}) { |
||||
|
const { em, config } = this; |
||||
|
const { open, selected, hovered, visible, locked, name } = data; |
||||
|
const cmpOpts = { fromLayers: true, ...opts }; |
||||
|
|
||||
|
if (isDef(open)) { |
||||
|
this.setOpen(component, open!); |
||||
|
} |
||||
|
if (isDef(selected)) { |
||||
|
if (selected) { |
||||
|
em.setSelected(component, cmpOpts); |
||||
|
const scroll = config.scrollCanvas; |
||||
|
scroll && component.views.forEach((view: any) => view.scrollIntoView(scroll)); |
||||
|
} else { |
||||
|
em.removeSelected(component, cmpOpts); |
||||
|
} |
||||
|
} |
||||
|
if (isDef(hovered) && config.showHover) { |
||||
|
hovered ? em.setHovered(component, cmpOpts) : em.setHovered(null, cmpOpts); |
||||
|
} |
||||
|
if (isDef(visible)) { |
||||
|
visible !== this.isVisible(component) && this.setVisible(component, visible!); |
||||
|
} |
||||
|
if (isDef(locked)) { |
||||
|
this.setLocked(component, locked!); |
||||
|
} |
||||
|
if (isDef(name)) { |
||||
|
this.setName(component, name!); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
getComponents(component: Component): Component[] { |
||||
|
return component.components().filter((cmp: any) => this.__isLayerable(cmp)); |
||||
|
} |
||||
|
|
||||
|
setOpen(component: Component, value: boolean) { |
||||
|
component.set('open', value); |
||||
|
} |
||||
|
|
||||
|
isOpen(component: Component): boolean { |
||||
|
return !!component.get('open'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Update component visibility |
||||
|
* */ |
||||
|
setVisible(component: Component, value: boolean) { |
||||
|
const prevDspKey = '__prev-display'; |
||||
|
const style: any = component.getStyle(styleOpts); |
||||
|
const { display } = style; |
||||
|
|
||||
|
if (value) { |
||||
|
const prevDisplay = component.get(prevDspKey); |
||||
|
delete style.display; |
||||
|
|
||||
|
if (prevDisplay) { |
||||
|
style.display = prevDisplay; |
||||
|
component.unset(prevDspKey); |
||||
|
} |
||||
|
} else { |
||||
|
display && component.set(prevDspKey, display); |
||||
|
style.display = 'none'; |
||||
|
} |
||||
|
|
||||
|
component.setStyle(style, styleOpts); |
||||
|
this.updateLayer(component); |
||||
|
this.em.trigger('component:toggled'); // Updates Style Manager #2938
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check if the component is visible |
||||
|
* */ |
||||
|
isVisible(component: Component): boolean { |
||||
|
return !isStyleHidden(component.getStyle(styleOpts)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Update component locked value |
||||
|
* */ |
||||
|
setLocked(component: Component, value: boolean) { |
||||
|
component.set('locked', value); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check if the component is locked |
||||
|
* */ |
||||
|
isLocked(component: Component): boolean { |
||||
|
return component.get('locked'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Update component name |
||||
|
* */ |
||||
|
setName(component: Component, value: string) { |
||||
|
component.set('custom-name', value); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Return the view of layers |
||||
|
* @return {View} |
||||
|
* @private |
||||
|
*/ |
||||
|
getAll() { |
||||
|
return this.view; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Triggered when the selected component is changed |
||||
|
* @private |
||||
|
*/ |
||||
|
componentChanged(sel?: Component, opts = {}) { |
||||
|
// @ts-ignore
|
||||
|
if (opts.fromLayers) return; |
||||
|
const { em, config } = this; |
||||
|
const { scrollLayers } = config; |
||||
|
const opened = this.model.get('opened'); |
||||
|
const selected = em.getSelected(); |
||||
|
let parent = selected?.parent(); |
||||
|
|
||||
|
for (let cid in opened) { |
||||
|
opened[cid].set('open', false); |
||||
|
delete opened[cid]; |
||||
|
} |
||||
|
|
||||
|
while (parent) { |
||||
|
parent.set('open', true); |
||||
|
opened[parent.cid] = parent; |
||||
|
parent = parent.parent(); |
||||
|
} |
||||
|
|
||||
|
if (selected && scrollLayers) { |
||||
|
// @ts-ignore
|
||||
|
const el = selected.viewLayer?.el; |
||||
|
el?.scrollIntoView(scrollLayers); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { config, model } = this; |
||||
|
const ItemView = View.extend(config.extend); |
||||
|
this.view = new ItemView({ |
||||
|
el: this.view?.el, |
||||
|
ItemView, |
||||
|
level: 0, |
||||
|
config, |
||||
|
opened: model.get('opened'), |
||||
|
model: this.getRoot(), |
||||
|
module: this, |
||||
|
}); |
||||
|
return this.view?.render().el as HTMLElement; |
||||
|
} |
||||
|
|
||||
|
destroy() { |
||||
|
this.view?.remove(); |
||||
|
} |
||||
|
|
||||
|
__onRootChange() { |
||||
|
const root = this.getRoot(); |
||||
|
this.view?.setRoot(root); |
||||
|
this.em.trigger(evRoot, root); |
||||
|
} |
||||
|
|
||||
|
__onComponent(component: Component) { |
||||
|
this.updateLayer(component); |
||||
|
} |
||||
|
|
||||
|
__isLayerable(cmp: Component): boolean { |
||||
|
const tag = cmp.get('tagName'); |
||||
|
const hideText = this.config.hideTextnode; |
||||
|
const isValid = !hideText || (!cmp.is('textnode') && tag !== 'br'); |
||||
|
|
||||
|
return isValid && cmp.get('layerable'); |
||||
|
} |
||||
|
|
||||
|
updateLayer(component: Component, opts?: any) { |
||||
|
this.em.trigger(evComponent, component, opts); |
||||
|
} |
||||
|
}; |
||||
Loading…
Reference in new issue