Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help with Preserving Table Attributes and Styles on Paste in RTE #247

Open
yadprab opened this issue Sep 6, 2024 · 0 comments
Open

Help with Preserving Table Attributes and Styles on Paste in RTE #247

yadprab opened this issue Sep 6, 2024 · 0 comments

Comments

@yadprab
Copy link

yadprab commented Sep 6, 2024

Hi,

I am currently working on implementing the functionality to copy and paste tables into a rich text editor (RTE) while preserving the table's attributes, especially when users paste email signatures or content from Google Sheets. The goal is to maintain the original styles and attributes when the table is pasted.

However, I'm encountering an issue where styles from the pasted content are not preserved when the resizable option is set to true. This results in the original HTML being broken, and the styles from the pasted tables (like those from Sheets) are not maintained.

The following code demonstrates how I'm handling the CustomTableView and CustomTable extensions:

CustomTableView: Handles the parsing and insertion of the colgroup element.
CustomTable Extension: Manages table attributes and includes a conditional for enabling column resizing. The styles seem to break when resizing is enabled, but without it, the styles are preserved.

Since this issue is a go-live blocker for us, I would appreciate any help or guidance on how to preserve the styles while still supporting the resizable functionality.

export const CustomTable = Table.extend({
  addAttributes() {
    return {
      ...this.parent?.(),
      style: {
        default: 'border-collapse: collapse; margin: 0 !important; table-layout: fixed;',
        parseHTML: (element) => element.style?.cssText || '',
        renderHTML: (attributes) => ({ style: attributes.style }),
      },
      colgroup: {
        default: '',
        parseHTML: (element) => {
          const colgroup = element.querySelector('colgroup');

          return colgroup ? colgroup.outerHTML : null;
        },
        renderHTML: (attributes) => {
          return {
            colgroup: attributes.colgroup,
          };
        },
      },
      ispasted: {
        default: false,
        parseHTML: (element) => element.getAttribute('ispasted') === 'true',
      },
    };
  },

  addProseMirrorPlugins() {
    return [
  
      tableEditing({
        allowTableNodeSelection: this.options.allowTableNodeSelection,
      }),
      DefaultPastePlugin(),

      ...(this.options.resizable && this.options.HTMLAttributes.ispated !== true
        ? [
            columnResizing({
              handleWidth: this.options.handleWidth,
              cellMinWidth: this.options.cellMinWidth,
              View: CustomTableView,
              lastColumnResizable: this.options.lastColumnResizable,
            }),
          ]
        : []),
    ];
  },
});

import { Plugin } from 'prosemirror-state';

export function DefaultPastePlugin() {
  return new Plugin({
    props: {
      transformPastedHTML(html, view) {
        const dom = new DOMParser().parseFromString(html, 'text/html');
        const style = dom.querySelector('style');
        dom.body.querySelectorAll('*').forEach((element) => {
          element.setAttribute('isPasted', 'true');
        });
        const isFromExcel = Array.from(dom.querySelectorAll('meta')).some((meta) => {
          return (
            meta.getAttribute('name') === 'Generator' &&
            meta.getAttribute('content')?.includes('Excel')
          );
        });
        if (isFromExcel) {
          return dom.body.innerHTML;
        }

        if (style) {
          const cssRules = Array.from(style.sheet?.cssRules || []);
          cssRules.forEach((rule) => {
            if (rule instanceof CSSStyleRule) {
              const elements = dom.querySelectorAll(rule.selectorText);
              elements.forEach((element) => {
                if (element instanceof HTMLElement) {
                  // Merge existing styles with new styles
                  const existingStyles = element.getAttribute('style') || '';
                  element.setAttribute('style', `${existingStyles} ${rule.style.cssText}`);
                }
              });
            }
          });
          style.remove(); // Remove the <style> tag after applying the styles
        }
    

        return dom.body.innerHTML;
      },
    },
  });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant