GrapesJS/grapesjs

BUG: When you delete a component, duplicate classes between components will be deleted.

ihatov08 opened this issue · 3 comments

GrapesJS version

  • I confirm to use the latest version of GrapesJS

What browser are you using?

Chrome 126.0.6478.127(Windows)

Reproducible demo link

https://jsfiddle.net/t06s4oLb/8/

Describe the bug

How to reproduce the bug?

  1. Add section1 to canvas
  2. Add section2 to canvas under section1
  3. remove section2 from canvas.
  4. The style of section1 will be broken.
Video_2024_07_04-5.mp4

What is the expected behavior?

I would like it to not be deleted even if separate components have the same class.

What is the current behavior?
The reason is that both section1 and section2 have .flex classes, and if you delete section2 from canvas, the .flex css will be deleted from gjs-css-rules.

Code of Conduct

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

As a temporary workaround, styles for each component are prefixed with id.

import { html as hero1html, css as hero1css } from './data/hero-1';
import { html as content1html, css as content1css } from './data/content-1';
const sources = [
  {
    id: 'hero-1',
    name: 'Hero 1',
    category: 'Hero',
    html: hero1html,
    css: hero1css,
    class: 'bg-gray-100 p-5',
  },
  {
    id: 'content-1',
    name: 'Content 1',
    category: 'Content',
    html: content1html,
    css: content1css,
    class: 'p-5',
  }
]

const escapeTailwindClasses = (str, id) => {
  return str.replace(/(\.[a-z0-9_-]+)(:\w+)/gi, (match, p1, p2) => {
    return `.${id}${p1.replace('.', '')}\\${p2}`;
  });
};

export const importBlocks = (editor, options = {}) => {
  const domc = editor.DomComponents;
  const blockManager = editor.BlockManager;
  sources.forEach(source => {
    const prefix = source.id + '-';
    const prefixedHtml = source.html.replace(/class="([^"]*)"/g, (match, className) => {
      return `class="${className.split(' ').map(cn => prefix + cn).join(' ')}"`;
    });

    const prefixedCss = source.css.replace(/\.([a-z0-9_-]+)(?=\s*[{,])/gi, (match, className) => {
      return '.' + prefix + className;
    });

    console.log(escapeTailwindClasses(prefixedCss, prefix))
    const prefixedClasses = source.class.split(' ').map(cn => prefix + cn).join(' ');
    domc.addType(source.id, {
      model: {
        defaults: {
          attributes: { class: prefixedClasses },
          components: prefixedHtml,
          styles: escapeTailwindClasses(prefixedCss, prefix),
        },
      },
    });
    blockManager.add(source.id, {
      category: source.category,
      id: source.id,
      label: source.name,
      content: {
        type: source.id,
      },
    });
  });
}
artf commented

You should avoid putting shared styles in component-related ones.
Check this discussion on how to prevent this: #5968

@artf
thank you for your reply.
I would like to confirm #5968