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