Issue #4455💬 RespondidoAbierto el 18 de julio de 2022por mickeyDominicReacciones 0

'panel-dispositivos' y 'panel-switcher' en conflicto

Respuesta rápidapor artf

@mickeyDominic colocas todos los paneles por defecto bajo el mismo objeto '''js Paneles: { Valores predeterminados: [{ id: 'capas', EL: '.panelright', // ... id: 'conmutador de panel', EL: '.panelswitcher', }] }

Lee la respuesta completa abajo ↓

Pregunta

Versión GrapesJS

  • Confirmo que se debe usar la última versión de GrapesJS

¿Qué navegador usas?

Versión de Chrome 103.0.5060.114

Enlace de demo reproducible

https://codepen.io/Dominic_M/pen/eYMgqyw

Describe el bicho

¿Cómo reproducir el bicho?

  1. Sigue GrapesJS "Comenzando" >> https://grapesjs.com/docs/getting-started.html
  2. Continúa con el tutorial hasta "Plantillas Responsive (https://grapesjs.com/docs/getting-started.html#responsive-templates)", donde te darás cuenta de que tras añadir un panel para cambiar entre la vista móvil y la de escritorio, un panel reemplaza al otro.

¿Cuál es el comportamiento esperado? 'panel-dispositivos' y 'panel-switcher' deberían aparecer uno al lado del otro sin fallos

¿Cuál es el comportamiento actual? 'panel-dispositivos' y 'panel-switcher' NO deberían aparecer uno al lado del otro sin fallos

Si es necesario ejecutar algo de código para reproducir el error, pégalo aquí abajo: '''html Tu código aquí

<!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>¡Hola Componente Mundial!</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="bloques"></div> <script src="script.js"></script> </body> </html>
'''css
Tu código aquí
/* Resaltemos los límites del lienzo */
#gjs {
  borde: 3px sólido #444;
}

/* Reiniciar algo de estilo predeterminado */
.gjs-cv-canvas {
  arriba: 0;
  ancho: 100%;
  altura: 100%;
}
.gjs-block {
  Ancho: Auto;
  Altura: Auto;
  Altura mínima: Auto;
}
.panel__top {
  relleno: 0;
  ancho: 100%;
  pantalla: flex;
  posición: inicial;
  justificar-contenido: centro;
  justificar-contenido: espacio-entre;
}
.panel__basic-acciones {
  posición: inicial;
}
.editor-row {
  pantalla: flex;
  Justificar-contenido: Inicio flexible;
  Alinear-ítems: estiramiento;
  Flex-wrap: sin wrap;
  Altura: 300px;
}

.editor-canvas {
  flex-grow: 1;
}

.panel__right {
  Base flexible: 230px;
  posición: relativa;
  desbordamiento: auto;
}
.panel__switcher {
  posición: inicial;
}
.panel__devices {
  posición: inicial;
}

'''js Tu código aquí const editor = grapesjs.init({ Indica dónde iniciar el editor en el editor. También puedes aprobar un HTMLElement Contenedor: '#gjs', Consigue el contenido del lienzo directamente del elemento Como alternativa, podríamos usar: 'components: '<h1>¡Hello World Component!</h1>' `, fromElement: cierto, Tamaño del editor Altura: '300px', Ancho: 'Auto', Desactiva el gestor de almacenamiento por el momento storageManager: false, /// Evita cualquier panel por defecto paneles: { predeterminados: [] },/ blockManager: { appendTo: '#blocks', Bloqueos: [ { ID: 'Sección', // ID es obligatorio label: '<b>Section</b>', // Puedes usar HTML/SVG dentro de las etiquetas atributos: { Class:'GJS-Block-section' }, contenido: '<section><h1>Este es un título sencillo</h1><div>Esto es solo un texto de Lolorem: Lorem ipsum dolor sit amet</div></section>', }, { id: 'texto', etiqueta: 'Texto', contenido: '<div data-gjs-type="text">Inserta tu texto aquí</div>', }, { id: 'imagen', etiqueta: 'Image', Selecciona el componente una vez que se haya soltado select: cierto, Puedes pasar componentes como JSON en lugar de una simple cadena HTML, En este caso también usamos un tipo de componente definido llamado 'imagen' Contenido: { Tipo: 'Imagen' }, Esto activa un evento 'activo' en componentes caídos y en la 'imagen' reacciona abriendo el AssetManager ACTIVAR: Cierto, } ] }, layerManager: { appendTo: '.layers-container' }, deviceManager: { dispositivos: [{ nombre: 'Escritorio', Ancho: '', // tamaño por defecto }, { nombre: 'Mobile', Ancho: '320px', // Este valor se usará en el ancho del lienzo widthMedia: '480px', // este valor se usará en CSS @media }] }, Definimos un panel por defecto como una barra lateral para contener capas Paneles: { Valores predeterminados: [{ id: 'capas', EL: '.panel__right', Haz que el panel sea redimensionable Redimensionable: { maxDim: 350, minDim: 200, TC: 0, // Mejor manejador cl: 1, // Manejador izquierdo cr: 0, // Manejador derecho bc: 0, // Manipulador de abajo Siendo un hijo flexible, necesitamos cambiar la propiedad de 'flex-base' en lugar del 'ancho' (por defecto) keyWidth: 'flex-base', }, id: 'conmutador de panel', EL: '.panel__switcher', botones: [ { id: 'capas de exhibición', ACTIVO: Cierto, etiqueta: 'Layers', Orden: 'Mostrar capas', Una vez activada, desactiva la posibilidad de desactivarlo Activable: falso, }, { id: 'estilo de espectáculo', ACTIVO: Cierto, etiqueta: 'Styles', Orden: 'Estilos de Exhibición', Activable: falso, }, { id: 'Rasgos de exhibición', ACTIVO: Cierto, etiqueta: 'Rasgos', Orden: 'Mostrar Rasgos', Activable: falso, } ], id: 'panel-dispositivos', EL: '.panel__devices', botones: [{ id: 'dispositivo-escritorio', etiqueta: 'D', comando: 'establecer-dispositivo-escritorio', ACTIVO: Cierto, Activable: falso, }, { id: 'dispositivo-móvil', etiqueta: 'M', comando: 'establecer-dispositivo-móvil', Activable: falso, }], }] }, traitManager: { append To: '.rasgos-contenedor', }, El Selector Manager permite asignar clases y diferentes estados (por ejemplo: hundir) en los componentes. Generalmente, se usa junto con Style Manager Pero no es obligatorio selectorManager: { appendTo: '.estilos-contenedor' }, StyleManager: { append To: '.estilos-contenedor', Sectores: [{ nombre: 'Dimension', abierto: falso, Uso de propiedades integradas buildProps: ['ancho', 'min-height', 'acolchado'], Usa 'propiedades' para definir o anular una sola propiedad Propiedades: [ { Tipo de entrada, opciones: entero | Radio | select | color | deslizador | archivo | compuesto | Stack tipo: 'entero', nombre: 'El ancho', // Etiqueta para la propiedad propiedad: 'width', // propiedad CSS (si buildProps la incluye, se extenderá) unidades: ['px', '%'], // Unidades, disponibles solo para tipos 'enteros' valores por defecto: 'auto', // Valor por defecto min: 0, // Valor mínimo, disponible solo para tipos 'enteros' } ] },{ nombre: 'Extra', abierto: falso, buildProps: ['color de fondo', 'sombra de caja', 'prop personalizado'], Propiedades: [{ ID: 'Atrezzo personalizado', nombre: 'Custom Label', Propiedad: 'tamaño de fuente', tipo: 'select', Valores predeterminados: '32px', Lista de opciones, disponibles solo para tipos 'select' y 'radio' Opciones: [ { valor: '12px', nombre: 'Tiny' }, { valor: '18px', nombre: 'Medium' }, { valor: '32px', nombre: 'Big' }, ], }] }] }, });

editor. Panels.addPanel({ id: 'panel-top', EL: '.panel__top', }); editor. Panels.addPanel({ id: 'Acciones-básicas', EL: '.panel__basic-acciones', botones: [ { Id: 'visibilidad', activo: true, // activo por defecto NombreClase: 'btn-toggle-borders', etiqueta: '<u>B</u>', comando: 'sw-visibility', // Comando incorporado }, { id: 'exportar', NombreClase: 'btn-open-export', etiqueta: 'Exp', comando: 'export-plantilla', contexto: 'export-plantilla', // Para agrupar el contexto de botones del mismo panel }, { id: 'show-json', NombreClase: 'btn-show-json', etiqueta: 'JSON', Contexto: 'show-json', comando(editor) { editor. Modal.setTitle('Components JSON') .setContent('<textarea style="width:100%; height: 250px;"> ${JSON.stringify(editor.getComponents())} ')</textarea> .open(); }, } ], });

Definir comandos editor. Commands.add('show-layers', { getRowEl(editor) { return editor.getContainer().closest('.editor-row'); }, getLayersEl(row) { return row.querySelector('.layers-container') },

run(editor, sendero) { const lmEl = this.getLayersEl(this.getRowEl(editor)); lmEl.style.display = ''; }, stop(editor, remitente) { const lmEl = this.getLayersEl(this.getRowEl(editor)); lmEl.style.display = 'ninguno'; }, }); editor. Commands.add('show-styles', { getRowEl(editor) { return editor.getContainer().closest('.editor-row'); }, getStyleEl(row) { return row.querySelector('.styles-container') },

run(editor, sendero) { const smEl = this.getStyleEl(this.getRowEl(editor)); smEl.style.display = ''; }, stop(editor, remitente) { const smEl = this.getStyleEl(this.getRowEl(editor)); smEl.style.display = 'ninguno'; }, }); editor. Commands.add('show-traits', { getTraitsEl(editor) { const row = editor.getContainer().closest('.editor-row'); return row.querySelector('.traits-container'); }, run(editor, sendero) { this.getTraitsEl(editor).style.display = ''; }, stop(editor, remitente) { this.getTraitsEl(editor).style.display = 'ninguno'; }, }); editor. Commands.add('set-device-desktop', { run: editor => editor.setDevice('Desktop') }); editor. Commands.add('set-device-mobile', { run: editor => editor.setDevice('Mobile') });

Esto es lo que se espera
! [GJS esperado](https://user-images.githubusercontent.com/60381147/179505665-95e222a1-25a8-4e91-9b63-e6bb861f7f42.PNG)  

Y este es el resultado actual
! [resultado GJS](https://user-images.githubusercontent.com/60381147/179505875-0444a4a1-8d9a-43a4-8a2a-d4c827bdad33.PNG)

### Código de conducta

- [X] Acepto seguir el Código de Conducta de este proyecto

Respuestas (3)

artf19 de julio de 2022

@mickeyDominic colocas todos los paneles por defecto bajo el mismo objeto '''js Paneles: { Valores predeterminados: [{ id: 'capas', EL: '.panel__right', // ... id: 'conmutador de panel', EL: '.panel__switcher', }] }

Donde debería haber un objeto por panel
'''js
Paneles: {
  Valores por defecto: [
    {
      id: 'capas',
      EL: '.panel__right',
      // ...
    }, {
      id: 'conmutador de panel',
      EL: '.panel__switcher',
      // ...
    }
  ]
}
mickeyDominic21 de julio de 2022

Gracias, @artf.

Qué tonta :)

ClaudeCode17 de mayo de 2026

Gracias por informar de esto, @mickeyDominic.

Buena pregunta sobre 'panel-dispositivos' y 'panel-switcher' que están en conflicto. El enfoque recomendado con ProseMirror es usar la API orientada a eventos.

Empieza aquí:

  1. Consulta la documentación de GrapesJS de tu módulo específico
  2. Busca el método del oyente de eventos 'on()'
  3. La mayoría de las operaciones se pueden realizar escuchando eventos del editor y de los componentes

Patrones comunes: '''javascript Prestad atención a los cambios editor.on('Change', () => console.log('Something Changed'));

Ciclo de vida de los componentes editor.on('component:mount', (c) => console.log('component ready', c)); editor.on('component:update', (c) => console.log('component updated', c));


**Si sigues atascado:**
- Compartir una reproducción mínima de CodeSandbox
- Incluye lo que ya has probado
- Menciona tu versión GrapesJS
- ¡La comunidad está aquí para ayudar!

Preguntas y respuestas relacionadas

Continúa investigando con debates sobre temas similares.

Plugins de pago que cumplen con este problema

Seleccionado por temas clave y relevancia de etiquetas para ayudarte a enviar más rápido.

Ver todos los plugins

Cargando recomendaciones de plugins de pago...

Opción gratuita

Consulta los plugins de código abierto de GrapesJS en GitHub O haz una búsqueda rápida en nuestro catálogo gratuito.

Explora plugins gratuitos →
Opción premium

Los plugins premium incluyen soporte, actualizaciones regulares y funciones listas para producción — ahorrando días de trabajo de integración.

Explora plugins premium →

Explorar categorías de plugins

Ve directamente a las páginas de categorías de plugins en el marketplace.