Inserting non editable Value in the editor with custom attributes like id and custom styling

1 Answer 77 Views
AJAX and Web 2.0 Announcements Designs, skins, themes Forum suggestions Fun Getting started with ASP.NET Interesting resources Job opportunities for RadControls experts Let's talk about telerik (the good and the bad) Miscellaneous My glory with telerik controls Telerik Trainer Telerik TV
Rahul
Top achievements
Rank 1
Rahul asked on 03 Jan 2024, 02:46 PM
Here's my current code, where on the button clicks I'm opening a popup in which the user can type anything and insert it into the cursor position, it's working fine.

Problem:- When I save my text like this <p>I’m looking for the parsing to keep my deeply nested div structure but the deepest div allows inline content. For some reason, a new div gets injected though. Which I’m later removing. I’d rather not have that hack though. My name is&nbsp;<span contenteditable="false" class="uneditable" style="user-select: none; opacity: 0.5; background-color: red; padding: 2px;" id="12312381829919">rahul</span>.</p>
where rahul is a custom value inserted using a popup with properties like id & style 

but when I parse this value onMount from API, the editor doesn't keep my style and id properties and when inspected the text in the editor looks like 

<p>I’m looking for the parsing to keep my deeply nested div structure but the deepest div allows inline content. For some reason, a new div gets injected though, which I’m later removing. I’d rather not have that hack though. My name is&nbsp;<span contenteditable="false" class="uneditable" >rahul</span>.</p>

which Is without ID and style tag 

here's my code for reference 

import React, { useRef, useState } from "react";
import { Editor, EditorTools, EditorUtils, ProseMirror } from "@progress/kendo-react-editor";
import "@progress/kendo-theme-default/dist/all.css";
import { Button } from "@progress/kendo-react-buttons";
import { Dialog, DialogActionsBar } from "@progress/kendo-react-dialogs";
import { Input } from "components/atoms/input/Input";

export default function KendoEditor({ type, updateData, initalVal }) {
  const [isDialogVisible, setDialogVisible] = useState(false);
  const [customData, setCustomData] = useState("");
  const [value, setValue] = useState("test");
  var initialValue =
    type === "header" || type === "footer" ? initalVal?.data?.content : initalVal?.content;
  const editorRef = useRef(null);
  const {
    Bold,
    Italic,
    Underline,
    Strikethrough,
    Subscript,
    Superscript,
    ForeColor,
    BackColor,
    CleanFormatting,
    AlignLeft,
    AlignCenter,
    AlignRight,
    AlignJustify,
    Indent,
    Outdent,
    OrderedList,
    UnorderedList,
    NumberedList,
    BulletedList,
    Undo,
    Redo,
    FontSize,
    FontName,
    FormatBlock,
    Link,
    Unlink,
    InsertImage,
    ViewHtml,
    InsertTable,
    InsertFile,
    SelectAll,
    Print,
    Pdf,
    TableProperties,
    TableCellProperties,
    AddRowBefore,
    AddRowAfter,
    AddColumnBefore,
    AddColumnAfter,
    DeleteRow,
    DeleteColumn,
    DeleteTable,
    MergeCells,
    SplitCell,
  } = EditorTools;
  const { Schema, EditorView, EditorState } = ProseMirror;
  const nonEditable = {
    name: "nonEditable",
    inline: true,
    group: "inline",
    content: "inline+",
    marks: "",
    attrs: {
      contenteditable: { default: null },
      class: { default: null },
      style: { default: null },
      id: { default: null },
    },
    atom: true,
    parseDOM: [{ tag: "span.uneditable", priority: 51 }],
    toDOM: (node) => [
      "span",
      {
        contenteditable: false,
        class: "uneditable",
        style:
          "user-select: none; opacity: 0.5; background-color: red;text-weight:600;padding:2px;",
        id: node.attrs.id,
      },
      0,
    ],
  };

  const toggleSidebar = (type, props) => {
    return (
      <Button
        onClick={() => {
          setDialogVisible(true);
          //  setActiveProperties(type);
        }}
        onMouseDown={(e) => e.preventDefault()}
        onPointerDown={(e) => e.preventDefault()}
        title="Insert Custom Data"
        imageUrl="https://demos.telerik.com/kendo-ui/content/shared/icons/sports/snowboarding.png"
        imageAlt="KendoReact Buttons Snowboarding icon"
      />
    );
  };

  const handleInsertCustomData = () => {
    const { view } = editorRef.current;
    const schema = view.state.schema;

    // get the new node from the schema
    const nodeType = schema.nodes.nonEditable;

    // create a new node with the custom data
    const node = nodeType.createAndFill(
      {
        class: "uneditable", // Keep the existing classes
        style: "user-select: none; opacity: 0.5; background-color: red;", // Add red background color
        id: "nernksf", // Add unique ID using new Date()
      },
      schema.text(customData)
    );

    // Insert the new node
    EditorUtils.insertNode(view, node);
    view.focus();

    // Close the dialog
    setDialogVisible(false);
    setCustomData("");
  };
  console.log(initialValue);
  const onMount = (event) => {
    const { viewProps } = event;
    const { plugins, schema } = viewProps.state;

    let nodes = schema.spec.nodes.addToEnd("nonEditable", nonEditable);

    const mySchema = new Schema({ nodes: nodes, marks: schema.spec.marks });

    console.log(type, initialValue);
    const doc = EditorUtils.createDocument(mySchema, initialValue ? initialValue : "");

    return new EditorView(
      { mount: event.dom },
      {
        ...event.viewProps,
        state: EditorState.create({ doc, plugins }),
      }
    );
  };
  //console.log(value);
  const handleEditorChange = (event) => {
    const data = event?.html;
    //console.log(data);
    setValue(data);
    if (type === "header" || type === "footer") {
      updateData({ visible: initalVal?.visible, data: { ...initalVal?.data, data } });
    } else {
      updateData({ ...initalVal, data });
    }
  };
  return (
    <div>
      <Editor
        ref={editorRef}
        onMount={onMount}
        onChange={handleEditorChange}
        tools={[
          [Bold, Italic, Underline, Strikethrough],
          [Subscript, Superscript],
          ForeColor,
          BackColor,
          [CleanFormatting],
          [AlignLeft, AlignCenter, AlignRight, AlignJustify],
          [Indent, Outdent],
          [OrderedList, UnorderedList],
          [NumberedList, BulletedList],
          FontSize,
          FontName,
          FormatBlock,
          [SelectAll],
          [Undo, Redo],
          [Link, Unlink, InsertImage, ViewHtml],
          [InsertTable, TableProperties, TableCellProperties],
          [AddRowBefore, AddRowAfter, AddColumnBefore, AddColumnAfter],
          [DeleteRow, DeleteColumn, DeleteTable],
          [MergeCells, SplitCell],
          [
            (props) => toggleSidebar("dataSourceProperties", props),
            (props) => toggleSidebar("formulaProperties", props),
            (props) => toggleSidebar("letterProperties", props),
            (props) => toggleSidebar("conditionBuilder", props),
          ],
        ]}
        contentStyle={{
          height: 320,
        }}
        //value={initialValue}
      />
      {isDialogVisible && (
        <Dialog
          title="Insert Custom Data"
          onClose={() => setDialogVisible(false)}
          visible={isDialogVisible}
        >
          <div>
            <Input
              type="text"
              value={customData}
              onChange={(e) => setCustomData(e.target.value)}
              label="Enter name"
            />

            <DialogActionsBar>
              <button
                className="k-button k-button-md k-rounded-md k-button-solid k-button-solid-base"
                onClick={() => setDialogVisible(false)}
              >
                No
              </button>
              <button
                className="k-button k-button-md k-rounded-md k-button-solid k-button-solid-base"
                onClick={handleInsertCustomData}
              >
                Yes
              </button>
            </DialogActionsBar>
          </div>
        </Dialog>
      )}
    </div>
  );
}

1 Answer, 1 is accepted

Sort by
0
Konstantin Dikov
Telerik team
answered on 08 Jan 2024, 09:14 AM | edited on 08 Jan 2024, 09:22 AM

Hello Rahul,

In order to enable custom attributes for the Editor nodes you can follow the approach in the following example:

More information on this matter can be found in the following help article:

Hope this helps.

 

Regards,
Konstantin Dikov
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages! Or perhaps, if you are new to our Kendo family, check out our getting started resources!

Tags
AJAX and Web 2.0 Announcements Designs, skins, themes Forum suggestions Fun Getting started with ASP.NET Interesting resources Job opportunities for RadControls experts Let's talk about telerik (the good and the bad) Miscellaneous My glory with telerik controls Telerik Trainer Telerik TV
Asked by
Rahul
Top achievements
Rank 1
Answers by
Konstantin Dikov
Telerik team
Share this question
or