Issue #4455Opened July 18, 2022by mickeyDominic0 reactions

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?

  1. Follow GrapesJS "Getting Started" >> https://grapesjs.com/docs/getting-started.html
  2. 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 expected GJS

And this is the current result result GJS

Code of Conduct

  • I agree to follow this project's Code of Conduct

Answers (2)

artfJuly 19, 20220 reactions

@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',
      // ...
    }
  ]
}
mickeyDominicJuly 21, 20220 reactions

Thank you @artf.

Silly me :)

Related Questions and Answers

Continue research with similar issue discussions.

Paid Plugins That Match This Issue

Curated by issue keywords and label relevance to help you ship faster.

View all plugins

Loading paid plugin recommendations...

Browse Plugin Categories

Jump directly to plugin category pages on the marketplace.