GrapesJS Custom Trait 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>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"></script>
<div id="gjs">
<div style="padding: 25px" data-gjs-type="demo-cmp">
Custom Trait Manager (select me and check my traits)
</div>
</div>
<div style="display: none;">
<!-- Vue app -->
<div class="vue-app">
<v-app>
<v-main>
<v-expansion-panels accordion multiple v-model="panels">
<v-expansion-panel v-for="trc in traitCategories">
<v-expansion-panel-header v-if="trc.category">
{{ trc.category.getLabel() }}
</v-expansion-panel-header>
<v-expansion-panel-header class="no-cat-header" v-else></v-expansion-panel-header>
<v-expansion-panel-content>
<v-row>
<trait-field v-for="trait in trc.items" :key="trait.id" :trait="trait"/>
</v-row>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</v-main>
</v-app>
</div>
<!-- Trait Field template -->
<div id="trait-field" style="display: none;">
<v-col :class="['py-0 px-1 mb-1', trait.get('full') && 'mb-3']" :cols="12">
<v-row class="flex-nowrap" v-if="isTypeNeedLabel">
<v-col cols="auto pr-0">{{ trait.getLabel() }}</v-col>
</v-row>
<div v-if="type === 'number'">
<v-text-field :placeholder="placeholder" :value="inputValue" @change="handleChange" outlined dense/>
</div>
<div v-else-if="type === 'checkbox'">
<v-checkbox...
CSS
body, html {
margin: 0;
height: 100%;
}
.trait-input-color {
width: 16px !important;
height: 15px !important;
opacity: 0 !important;
}
/* Vuetify overrides */
.v-application {
background: transparent !important;
}
.v-application--wrap {
min-height: auto;
}
.v-input__slot {
font-size: 12px;
min-height: 10px !important;
color-scheme: dark;
}
.v-select__selections {
flex-wrap: nowrap;
}
.v-text-field .v-input__slot {
padding: 0 10px !important;
}
.v-input--selection-controls {
margin-top: 0;
}
.v-text-field__details, .v-messages {
display: none;
}
.no-cat-header {
opacity: 0;
padding: 10px;
max-height: 10px;
pointer-events: none;
min-height: auto !important;
}
.trait-color-prv {
border: 1px solid rgba(255,255,255,0.5);
border-radius: 3px;
}
Babel + JSX
const myPlugin = (editor) => {
const category1 = { id: 'first', label: 'First category' };
const category2 = { id: 'second', label: 'Second category' };
// Demo component with some traits
editor.Components.addType('demo-cmp', {
model: {
defaults: {
traits: [
{
type: 'date',
name: 'date-trait',
label: 'Date trait',
placeholder: 'Insert date',
},
{
type: 'text',
name: 'text-trait',
label: false,
placeholder: 'Insert text',
category: category1,
},
{
type: 'select',
name: 'select-trait',
label: 'Select 1',
category: category1,
default: 'opt1',
options: [
{ id: 'opt1', name: 'Option 1'},
{ id: 'opt2', name: 'Option 2'},
{ id: 'opt3', name: 'Option 3'},
]
},
{
type: 'select',
name: 'select-trait2',
label: 'Select 2',
category: category1,
default: 'opt2',
options: [
{ id: 'opt1', name: 'Option 1'},
{ id: 'opt2', name: 'Option 2'},
{ id: 'opt3', name: 'Option 3'},
]
},
{
type: 'number',
name: 'number-trait',
placeholder: '0-100',
min: 0,
max: 100,
step: 5,
},
{
type: 'color',
label: 'Color trait',
name: 'color-trait',
},
{
type: 'checkbox',
label: 'Checkbox trait',
name: 'checkbox-trait',
valueTrue: 'YES',
valueFalse: 'NO',
},
{
type: 'checkbox',
name: 'open',
category: category2,
},
{
type: 'button',
...