GrapesJS Custom Selector Manager
by artur_arseniev
HTML
<script src="https://unpkg.com/grapesjs"></script>
<link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">
<script src="https://unpkg.com/grapesjs-blocks-basic"></script>
<div id="gjs">
<div style="padding: 25px">Custom Selector Manager</div>
<div class="class-a">Element A</div>
<div class="class-a class-b">Element A-B</div>
<div class="class-a class-b class-c">Element A-B-C</div>
<style>
.class-a { color: red }
.class-b { color: green }
.class-c { color: blue }
</style>
</div>
<div style="display: none;">
<!-- Vue app -->
<div class="selectors-wrp">
<div class="selectors-head">
<div class="selectors-title">Classes</div>
<div class="selectors-states">
<select @change="changeState" :value="state">
<option value="">- State -</option>
<option v-for="state in states" :key="state.getName()">
{{ state.getLabel() }}
</option>
</select>
</div>
</div>
<div class="selectors">
<div class="selector-add" @click="addNew">+</div>
<div v-for="sel in selectors" :key="sel.toString()" class="selector">
<div class="selector-name">{{ sel.getLabel() }}</div>
<div class="selector-rm" @click="remove(sel)">⨯</div>
</div>
</div>
<div class="selectors-targets">
Selected: {{ targets }}
</div>
</div>
</div>
CSS
body, html {
margin: 0;
height: 100%;
}
.selectors-wrp {
padding: 10px 5px;
}
.selectors-head {
display: flex;
align-items: center;
}
.selectors-title {
flex-grow: 1;
text-align: left;
}
.selectors {
display: flex;
flex-wrap: wrap;
background-color: rgb(0 0 0 / 15%);
padding: 3px 5px;
border-radius: 3px;
margin: 10px 0;
gap: 5px;
}
.selector, .selector-add {
display: flex;
gap: 3px;
background-color: #8f4a79;
color: white;
padding: 2px 5px 3px;
border-radius: 3px;
align-items: center;
white-space: nowrap;
}
.selector-add {
font-size: 1rem;
padding: 2px 8px 3px;
background-color: rgb(255 255 255 / 25%);
cursor: pointer;
}
.selector-rm {
font-size: 1rem;
cursor: pointer;
}
.selectors-targets {
text-align: left;
}
Babel + JSX
const editor = grapesjs.init({
container: '#gjs',
fromElement: true,
height: '100%',
storageManager: false,
noticeOnUnload: false,
selectorManager: {
custom: true,
componentFirst: true,
},
});
// Load the Vue app
const sm = editor.Selectors;
const app = new Vue({
el: '.selectors-wrp',
data: { selectors: [], states: [], state: null, targets: '' },
mounted() {
editor.on('selector:custom', this.handleCustom);
},
destroyed() {
editor.off('selector:custom', this.handleCustom);
},
methods: {
handleCustom(props) {
if (props.container) {
props.container.appendChild(this.$el);
}
this.selectors = sm.getSelected();
this.states = sm.getStates();
this.state = sm.getState();
this.targets = sm.getSelectedTargets()
.map(target => target.getSelectorsString()).join(', ');
},
changeState(ev) {
sm.setState(ev.target.value);
},
remove(selector) {
sm.removeSelected(selector);
},
addNew() {
const len = sm.getAll().length + 1;
sm.addSelected({ name: `new-${len}`, label: `New ${len}` });
},
}
});