'panel-devices' and 'panel-switcher' conflicting
@mickeyDominic you're placing all the default panels under the same object Where it should be one object per panel
Read full answer below βQuestion
GrapesJS version
- I confirm to use the latest version of GrapesJS
What browser are you using?
Chrome Version 103.0.5060.114
Reproducible demo link
https://codepen.io/Dominic_M/pen/eYMgqyw
Describe the bug
How to reproduce the bug?
- Follow GrapesJS "Getting Started" >> https://grapesjs.com/docs/getting-started.html
- Continue with tutorial until "Responsive templates (https://grapesjs.com/docs/getting-started.html#responsive-templates)" where you will realize that after adding a panel for switching between Mobile and Desktop view one panel replaces the other.
What is the expected behavior? 'panel-devices' and 'panel-switcher' should appear side by side without fault
What is the current behavior? 'panel-devices' and 'panel-switcher'do NOT should appear side by side without fault
If is necessary to execute some code in order to reproduce the bug, paste it here below:
// your code here
<!DOCTYPE html>
<html>
<head>
<title>CMS...</title>
<meta charset="UTF-8">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">
<script src="https://unpkg.com/[email protected]/dist/grapes.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="panel__top">
<div class="panel__basic-actions"></div>
<div class="panel__devices"></div>
<div class="panel__switcher"></div>
</div>
<div class="editor-row">
<div class="editor-canvas">
<div id="gjs"><h1>Hello World Component!</h1></div>
</div>
<div class="panel__right">
<div class="layers-container"></div>
<div class="styles-container"></div>
<div class="traits-container"></div>
</div>
</div>
<div id="blocks"></div>
<script src="script.js"></script>
</body>
</html>
// your code here
/* Let's highlight canvas boundaries */
#gjs {
border: 3px solid #444;
}
/* Reset some default styling */
.gjs-cv-canvas {
top: 0;
width: 100%;
height: 100%;
}
.gjs-block {
width: auto;
height: auto;
min-height: auto;
}
.panel__top {
padding: 0;
width: 100%;
display: flex;
position: initial;
justify-content: center;
justify-content: space-between;
}
.panel__basic-actions {
position: initial;
}
.editor-row {
display: flex;
justify-content: flex-start;
align-items: stretch;
flex-wrap: nowrap;
height: 300px;
}
.editor-canvas {
flex-grow: 1;
}
.panel__right {
flex-basis: 230px;
position: relative;
overflow-y: auto;
}
.panel__switcher {
position: initial;
}
.panel__devices {
position: initial;
}
// your code here
const editor = grapesjs.init({
// Indicate where to init the editor. You can also pass an HTMLElement
container: '#gjs',
// Get the content for the canvas directly from the element
// As an alternative we could use: `components: '<h1>Hello World Component!</h1>'`,
fromElement: true,
// Size of the editor
height: '300px',
width: 'auto',
// Disable the storage manager for the moment
storageManager: false,
/*// Avoid any default panel
panels: { defaults: [] },*/
blockManager: {
appendTo: '#blocks',
blocks: [
{
id: 'section', // id is mandatory
label: '<b>Section</b>', // You can use HTML/SVG inside labels
attributes: { class:'gjs-block-section' },
content: '<section><h1>This is a simple title</h1><div>This is just a Lorem text: Lorem ipsum dolor sit amet</div></section>',
},
{
id: 'text',
label: 'Text',
content: '<div data-gjs-type="text">Insert your text here</div>',
},
{
id: 'image',
label: 'Image',
// Select the component once it's dropped
select: true,
// You can pass components as a JSON instead of a simple HTML string,
// in this case we also use a defined component type `image`
content: { type: 'image' },
// This triggers `active` event on dropped components and the `image`
// reacts by opening the AssetManager
activate: true,
}
]
},
layerManager: {
appendTo: '.layers-container'
},
deviceManager: {
devices: [{
name: 'Desktop',
width: '', // default size
}, {
name: 'Mobile',
width: '320px', // this value will be used on canvas width
widthMedia: '480px', // this value will be used in CSS @media
}]
},
// We define a default panel as a sidebar to contain layers
panels: {
defaults: [{
id: 'layers',
el: '.panel__right',
// Make the panel resizable
resizable:
{
maxDim: 350,
minDim: 200,
tc: 0, // Top handler
cl: 1, // Left handler
cr: 0, // Right handler
bc: 0, // Bottom handler
// Being a flex child we need to change `flex-basis` property
// instead of the `width` (default)
keyWidth: 'flex-basis',
},
id: 'panel-switcher',
el: '.panel__switcher',
buttons: [
{
id: 'show-layers',
active: true,
label: 'Layers',
command: 'show-layers',
// Once activated disable the possibility to turn it off
togglable: false,
},
{
id: 'show-style',
active: true,
label: 'Styles',
command: 'show-styles',
togglable: false,
},
{
id: 'show-traits',
active: true,
label: 'Traits',
command: 'show-traits',
togglable: false,
}
],
id: 'panel-devices',
el: '.panel__devices',
buttons: [{
id: 'device-desktop',
label: 'D',
command: 'set-device-desktop',
active: true,
togglable: false,
}, {
id: 'device-mobile',
label: 'M',
command: 'set-device-mobile',
togglable: false,
}],
}]
},
traitManager: {
appendTo: '.traits-container',
},
// The Selector Manager allows to assign classes and
// different states (eg. :hover) on components.
// Generally, it's used in conjunction with Style Manager
// but it's not mandatory
selectorManager: {
appendTo: '.styles-container'
},
styleManager: {
appendTo: '.styles-container',
sectors: [{
name: 'Dimension',
open: false,
// Use built-in properties
buildProps: ['width', 'min-height', 'padding'],
// Use `properties` to define/override single property
properties: [
{
// Type of the input,
// options: integer | radio | select | color | slider | file | composite | stack
type: 'integer',
name: 'The width', // Label for the property
property: 'width', // CSS property (if buildProps contains it will be extended)
units: ['px', '%'], // Units, available only for 'integer' types
defaults: 'auto', // Default value
min: 0, // Min value, available only for 'integer' types
}
]
},{
name: 'Extra',
open: false,
buildProps: ['background-color', 'box-shadow', 'custom-prop'],
properties: [{
id: 'custom-prop',
name: 'Custom Label',
property: 'font-size',
type: 'select',
defaults: '32px',
// List of options, available only for 'select' and 'radio' types
options: [
{ value: '12px', name: 'Tiny' },
{ value: '18px', name: 'Medium' },
{ value: '32px', name: 'Big' },
],
}]
}]
},
});
editor.Panels.addPanel({
id: 'panel-top',
el: '.panel__top',
});
editor.Panels.addPanel({
id: 'basic-actions',
el: '.panel__basic-actions',
buttons: [
{
id: 'visibility',
active: true, // active by default
className: 'btn-toggle-borders',
label: '<u>B</u>',
command: 'sw-visibility', // Built-in command
}, {
id: 'export',
className: 'btn-open-export',
label: 'Exp',
command: 'export-template',
context: 'export-template', // For grouping context of buttons from the same panel
}, {
id: 'show-json',
className: 'btn-show-json',
label: 'JSON',
context: 'show-json',
command(editor) {
editor.Modal.setTitle('Components JSON')
.setContent(`<textarea style="width:100%; height: 250px;">
${JSON.stringify(editor.getComponents())}
</textarea>`)
.open();
},
}
],
});
// Define commands
editor.Commands.add('show-layers', {
getRowEl(editor) { return editor.getContainer().closest('.editor-row'); },
getLayersEl(row) { return row.querySelector('.layers-container') },
run(editor, sender) {
const lmEl = this.getLayersEl(this.getRowEl(editor));
lmEl.style.display = '';
},
stop(editor, sender) {
const lmEl = this.getLayersEl(this.getRowEl(editor));
lmEl.style.display = 'none';
},
});
editor.Commands.add('show-styles', {
getRowEl(editor) { return editor.getContainer().closest('.editor-row'); },
getStyleEl(row) { return row.querySelector('.styles-container') },
run(editor, sender) {
const smEl = this.getStyleEl(this.getRowEl(editor));
smEl.style.display = '';
},
stop(editor, sender) {
const smEl = this.getStyleEl(this.getRowEl(editor));
smEl.style.display = 'none';
},
});
editor.Commands.add('show-traits', {
getTraitsEl(editor) {
const row = editor.getContainer().closest('.editor-row');
return row.querySelector('.traits-container');
},
run(editor, sender) {
this.getTraitsEl(editor).style.display = '';
},
stop(editor, sender) {
this.getTraitsEl(editor).style.display = 'none';
},
});
editor.Commands.add('set-device-desktop', {
run: editor => editor.setDevice('Desktop')
});
editor.Commands.add('set-device-mobile', {
run: editor => editor.setDevice('Mobile')
});
This is what is expected
And this is the current result
Code of Conduct
- I agree to follow this project's Code of Conduct
Answers (3)
@mickeyDominic you're placing all the default panels under the same object
panels: {
defaults: [{
id: 'layers',
el: '.panel__right',
// ...
id: 'panel-switcher',
el: '.panel__switcher',
}]
}
Where it should be one object per panel
panels: {
defaults: [
{
id: 'layers',
el: '.panel__right',
// ...
}, {
id: 'panel-switcher',
el: '.panel__switcher',
// ...
}
]
}
Thank you @artf.
Silly me :)
Thanks for reporting this, @mickeyDominic.
Great question about 'panel-devices' and 'panel-switcher' conflicting. The recommended approach with ProseMirror is to use the event-driven API.
Start here:
- Check the GrapesJS documentation for your specific module
- Look for the
on()event listener method - Most operations can be achieved by listening to editor and component events
Common patterns:
// Listen for changes
editor.on('change', () => console.log('something changed'));
// Component lifecycle
editor.on('component:mount', (c) => console.log('component ready', c));
editor.on('component:update', (c) => console.log('component updated', c));
If you're still stuck:
- Share a minimal CodeSandbox reproduction
- Include what you've already tried
- Mention your GrapesJS version
- The community is here to help!
Related Questions and Answers
Continue research with similar issue discussions.
Issue #4447
Style manager does not accurately reflect inherited color
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? 103.0.5060.114 Reproducible demo link http...
Issue #4191
We are trying to store the data from grapesJS editor in Angular 10 application, getting CORS error, image attached below
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome 99.0.4844.5 Reproducible demo link...
Issue #6494
Create and destroy repeatedly throws error
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome Reproducible demo link https://code...
Issue #6458
sector.setName doesn't work
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome Reproducible demo link https://jsfi...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Check the open-source GrapesJS plugins on GitHub or run a quick search in our free catalog.
Browse free plugins βPremium plugins ship with support, regular updates, and production-ready features β save days of integration work.
Browse premium plugins βBrowse Plugin Categories
Jump directly to plugin category pages on the marketplace.