BUG: 'panel-devices' and 'panel-switcher' conflicting
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 (2)
@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 :)
Related Questions and Answers
Continue research with similar issue discussions.
Issue #3770
[Feature]: Improve and fix absolute dragMode
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Chrome v93 Reproducible demo link https://...
Issue #6549
BUG: Couldnt Resize image
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome Reproducible demo link https://code...
Issue #6518
BUG: Getting Started doesn't show Blocks as written
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome 136.0.7103.93 Reproducible demo lin...
Issue #6263
BUG: Duplicated links are added using rte
GrapesJS version[X] I confirm to use the latest version of GrapesJSWhat browser are you using? ChromeReproducible demo link https://codepen...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.