Browse Source

Up example ref

pull/5106/head
Artur Arseniev 3 years ago
parent
commit
de4d8e3f42
  1. 359
      docs/modules/Pages.md

359
docs/modules/Pages.md

@ -149,276 +149,133 @@ In the example below you can see an quick implementation of the Page Manager UI.
<!-- Demo template, here for reference
<style>
.layer-manager {
position: relative;
text-align: left;
}
.layer-item.hidden {
opacity: 0.5;
}
.layer-item-icon {
width: 15px;
cursor: pointer;
}
.layer-item-eye {
}
.layer-item-chevron {
transform: rotate(90deg);
}
.layer-item-chevron.open {
transform: rotate(180deg);
}
.layer-item-chevron.hidden {
opacity: 0;
pointer-events: none;
}
.layer-item-row {
display: flex;
align-items: center;
user-select: none;
gap: 8px;
padding: 5px 8px;
border-bottom: 1px solid rgba(0,0,0,0.35);
}
.layer-item-row.selected {
background-color: rgba(255,255,255,0.15);
}
.layer-item-row.hovered {
background-color: rgba(255,255,255,0.05);
}
.layer-item-name {
margin-left: 3px;
}
.layer-item-name.editing {
background-color: white;
color: #555;
padding: 0 3px;
}
.layer-item-name-cnt {
display: flex;
align-items: center;
flex-grow: 1;
}
.layer-drag-indicator {
position: absolute;
width: 100%;
height: 1px;
left: 0;
background-color: #3b97e3;
}
.app-wrap {
height: 100%;
width: 100%;
display: flex;
}
.editor-wrap {
widtH: 100%;
height: 100%;
}
.pages-wrp, .pages {
display: flex;
flex-direction: column
}
.pages-wrp {
background: #333;
padding: 5px;
}
.add-page {
background: #444444;
color: white;
padding: 5px;
border-radius: 2px;
cursor: pointer;
white-space: nowrap;
margin-bottom: 10px;
}
.page {
background-color: #444;
color: white;
padding: 5px;
margin-bottom: 5px;
border-radius: 2px;
cursor: pointer;
&.selected {
background-color: #706f6f
}
}
.page-close {
opacity: 0.5;
float: right;
background-color: #2c2c2c;
height: 20px;
display: inline-block;
width: 17px;
text-align: center;
border-radius: 3px;
&:hover {
opacity: 1;
}
}
</style>
<div style="display: none">
<div
class="layer-manager"
@pointerdown="onDragStart"
@pointermove="onDragMove"
@pointerup="onDragEnd"
>
<layer-item v-if="root" :component="root" :level="0"></layer-item>
<div
v-if="dragIndicator.show"
class="layer-drag-indicator"
:style="{ top: `${dragIndicator.y}px`, marginLeft: `${dragIndicator.offset}px`, width: `calc(100% - ${dragIndicator.offset}px)` }"></div>
</div>
<div id="layer-item-template" style="display: none;">
<div :class="['layer-item', !visible && 'hidden']">
<div
:class="['layer-item-row', selected && 'selected', hovered && 'hovered']"
@click="setSelected"
@mouseenter="setHover(true)"
@mouseleave="setHover(false)"
ref="layerRef"
data-layer-item
>
<div class="layer-item-icon layer-item-eye" @click.stop="toggleVisibility()">
<svg v-if="visible" viewBox="0 0 24 24"><path fill="currentColor" d="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z" /></svg>
<svg v-else viewBox="0 0 24 24"><path fill="currentColor" d="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z" /></svg>
</div>
<div class="layer-item-name-cnt" :style="{ marginLeft: `${level*10}px` }">
<div :class="['layer-item-icon layer-item-chevron', open && 'open', !components.length && 'hidden']" @click.stop="toggleOpen()">
<svg viewBox="0 0 24 24"><path fill="currentColor" d="M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z" /></svg>
</div>
<div ref="nameInput"
:class="['layer-item-name', editing && 'editing']"
:contenteditable="editing"
@dblclick.stop="setEditing(true)"
@blur.stop="setEditing(false)"
@keydown.enter="setEditing(false)"
>
{{ name }}
<div style="height: 100%">
<div class="app-wrap">
<div class="pages-wrp">
<div class="add-page" @click="addPage">Add new page</div>
<div class="pages">
<div v-for="page in pages" :key="page.id" :class="{page: 1, selected: isSelected(page) }" @click="selectPage(page.id)">
{{ page.get('name') || page.id }} <span v-if="!isSelected(page)" @click="removePage(page.id)" class="page-close">&Cross;</span>
</div>
</div>
<div v-if="component.get('draggable')" class="layer-item-icon layer-item-move" data-layer-move>
<svg viewBox="0 0 24 24"><path fill="currentColor" d="M13,6V11H18V7.75L22.25,12L18,16.25V13H13V18H16.25L12,22.25L7.75,18H11V13H6V16.25L1.75,12L6,7.75V11H11V6H7.75L12,1.75L16.25,6H13Z"/></svg>
</div>
</div>
<div v-if="open" class="layer-items">
<layer-item v-for="cmp in components" :key="cmp.getId()" :component="cmp" :level="level + 1"/>
</div>
</div>
<div class="editor-wrap">
<div id="gjs"></div>
</div>
</div>
</div>
<script>
const { Components, Layers } = editor;
const cmpElMap = new WeakMap();
Vue.component('layer-item', {
template: '#layer-item-template',
props: { component: Object, level: Number },
data() {
return {
name: '',
components: [],
visible: true,
open: false,
selected: false,
hovered: false,
editing: false,
}
},
mounted() {
this.updateLayer(Layers.getLayerData(this.component));
cmpElMap.set(this.$refs.layerRef, this.component);
editor.on('layer:component', this.onLayerComponentUpdate);
},
destroyed() {
editor.off('layer:component', this.onLayerComponentUpdate);
const editor = grapesjs.init({
container: '#gjs',
height: '100%',
storageManager: false,
plugins: ['gjs-blocks-basic'],
pageManager: {
pages: [{
id: 'page-1',
name: 'Page 1',
component: '<div id="comp1">Page 1</div>',
styles: `#comp1 { color: red }`,
}, {
id: 'page-2',
name: 'Page 2',
component: '<div id="comp2">Page 2</div>',
styles: `#comp2 { color: green }`,
}, {
id: 'page-3',
name: 'Page 3',
component: '<div id="comp3">Page 3</div>',
styles: `#comp3 { color: blue }`,
}]
},
methods: {
onLayerComponentUpdate(cmp) {
if (cmp === this.component) {
this.updateLayer(Layers.getLayerData(cmp));
}
},
updateLayer(data) {
this.name = data.name;
this.components = data.components;
this.visible = data.visible;
this.open = data.open;
this.selected = data.selected;
this.hovered = data.hovered;
},
toggleVisibility() {
const { component } = this;
Layers.setVisible(this.component, !this.visible);
},
toggleOpen() {
const { component } = this;
Layers.setOpen(this.component, !this.open);
},
setHover(hovered) {
Layers.setLayerData(this.component, { hovered })
},
setSelected(event) {
Layers.setLayerData(this.component, { selected: true }, { event })
},
setEditing(value) {
this.editing = value;
const el = this.$refs.nameInput;
if (!value) {
Layers.setName(this.component, el.innerText)
} else {
setTimeout(() => el.focus())
}
},
}
});
const pm = editor.Pages;
const app = new Vue({
el: '.layer-manager',
data: {
root: null,
isDragging: false,
draggingCmp: null,
draggingOverCmp: null,
dragIndicator: {},
canMoveRes: {},
},
el: '.pages-wrp',
data: { pages: [] },
mounted() {
editor.on('layer:custom', this.handleCustom);
editor.on('layer:root', this.handleRootChange);
},
destroyed() {
editor.off('layer:custom', this.handleCustom);
editor.off('layer:root', this.handleRootChange);
this.setPages(pm.getAll());
editor.on('page', () => {
this.pages = [...pm.getAll()];
});
},
methods: {
handleCustom(props = {}) {
const { container, root } = props;
container && container.appendChild(this.$el);
this.handleRootChange(root);
},
handleRootChange(root) {
console.log('root update', root);
this.root = root;
setPages(pages) {
this.pages = [...pages];
},
getDragTarget(ev) {
const el = document.elementFromPoint(ev.clientX, ev.clientY);
const dragEl = el?.closest('[data-layer-move]');
const elLayer = el?.closest('[data-layer-item]');
return {
dragEl,
elLayer,
cmp: cmpElMap.get(elLayer),
}
isSelected(page) {
return pm.getSelected().id == page.id;
},
onDragStart(ev) {
if (this.getDragTarget(ev).dragEl) {
this.isDragging = true;
}
selectPage(pageId) {
return pm.select(pageId);
},
onDragMove(ev) {
if (!this.isDragging) return;
const { cmp, elLayer } = this.getDragTarget(ev);
if (!cmp || !elLayer) return;
const { draggingCmp } = this;
const layerRect = elLayer.getBoundingClientRect();
const layerH = elLayer.offsetHeight;
const layerY = elLayer.offsetTop;
const pointerY = ev.clientY;
const isBefore = pointerY < (layerRect.y + layerH / 2);
const cmpSource = !draggingCmp ? cmp : draggingCmp;
const cmpTarget = cmp.parent();
const cmpIndex = cmp.index() + (isBefore ? 0 : 1);
this.draggingCmp = !draggingCmp ? cmp : draggingCmp;
this.draggingOverCmp = cmp;
const canMove = Components.canMove(cmpTarget, cmpSource, cmpIndex);
const canMoveInside = Components.canMove(cmp, cmpSource);
const canMoveRes = {
...canMove,
canMoveInside,
index: cmpIndex,
};
// if (
// canMoveInside.result &&
// (
// pointerY > (layerRect.y + LAYER_PAD)
// && pointerY < (layerRect.y + layerH - LAYER_PAD))
// ) {
// pointerInside = true;
// canMoveRes.target = cmp;
// delete canMoveRes.index;
// }
// setDragParent(pointerInside ? cmp : undefined);
this.canMoveRes = canMoveRes;
const dragLevel = (cmp ? cmp.parents() : []).length;
this.dragIndicator = {
y: layerY + (isBefore ? 0 : layerH),
h: layerH,
offset: dragLevel * 10 + 20,
show: !!(this.draggingCmp && canMoveRes?.result),
};
removePage(pageId) {
return pm.remove(pageId);
},
onDragEnd(ev) {
const { canMoveRes } = this;
canMoveRes.result && canMoveRes.source.move(canMoveRes.target, { at: canMoveRes.index });
this.isDragging = false;
this.draggingCmp = null;
this.draggingOverCmp = null;
this.dragIndicator = {};
this.canMoveRes = {};
addPage() {
const len = pm.getAll().length;
pm.add({
name: `Page ${len + 1}`,
component: '<div>New page</div>',
});
},
}
});

Loading…
Cancel
Save