skip to Main Content

not sure if I am asking correctly. But I understood that controller is probably what I need. I want to have the functionality of custom plugin number 4 called Dialog – http://suneditor.com/sample/html/customPlugins.html#dialog But I do not want to insert a link but custom element named for example customElement.

I can modify the code provided for the sample plugin to custom HTML element is inserted but then I will loose the context dialog to update, unlink, or remove the element.

I my case I want only the update and remove icons.

enter image description here

Is that possible in Suneditor?

You test with https://jsfiddle.net/radek/qjoevast/12/ the very first line defines what HTML element is going to be inserted.

for var customElement = 'a' I get the context buttons
for var customElement = 'customElement' the correct element is inserted but not context buttons

2

Answers


  1. I have specified the places where you can get the context of update,remove with //$$$$$$$$$$$$$ in js code below,and also the place where you can create the custom element look for it.

    var plugin_customElement = {
      name: "customElement",
    
      display: "dialog",
    
      add: function (core) {
        core.addModule([SUNEDITOR_MODULES.dialog])
    
        const context = core.context
        context.customElement = {
          focusElement: null,
          elementText: null,
          _element: null,
        }
    
        let element_dialog = this.setDialog(core)
        context.customElement.modal = element_dialog
        context.customElement.focusElement = element_dialog.querySelector(
          "._se_customElement_text",
        )
        context.customElement.elementText = element_dialog.querySelector(
          "._se_customElement_text",
        )
    
        let element_controller = this.setController_ElementButton(core)
        context.customElement.elementController = element_controller
        context.customElement._element = null
    
        element_dialog
          .querySelector("form")
          .addEventListener("submit", this.submit.bind(core))
        element_controller.addEventListener(
          "click",
          this.onClick_elementController.bind(core),
        )
    
        context.dialog.modal.appendChild(element_dialog)
        context.element.relative.appendChild(element_controller)
    
        // Attach click event listener to the editor
        core.context.element.wysiwyg.addEventListener(
          "click",
          this.onEditorClick.bind(core),
        )
    
        element_dialog = null
        element_controller = null
      },
    
      setDialog: function (core) {
        const lang = core.lang
        const dialog = core.util.createElement("DIV")
        dialog.className = "se-dialog-content"
        dialog.style.display = "none"
        dialog.innerHTML = `
                <form class="editor_customElement">
                    <div class="se-dialog-header">
                        <button type="button" data-command="close" class="se-btn se-dialog-close" aria-label="Close" title="${lang.dialogBox.close}">
                            ${core.icons.cancel}
                        </button>
                        <span class="se-modal-title">Custom Element</span>
                    </div>
                    <div class="se-dialog-body">
                        <div class="se-dialog-form">
                            <label>Content</label>
                            <input class="se-input-form _se_customElement_text" type="text" />
                        </div>
                    </div>
                    <div class="se-dialog-footer">
                        <button type="submit" class="se-btn-primary" title="${lang.dialogBox.submitButton}">
                            <span>${lang.dialogBox.submitButton}</span>
                        </button>
                    </div>
                </form>`
        return dialog
      },
      onEditorClick: function (event) {
        const clickedElement = event.target
    
        // Check if the clicked element is a target (e.g., custom element)
        if (clickedElement.tagName === "CUSTOM-ELEMENT") {
          this.plugins.customElement.call_controller.call(this, clickedElement)
        } else {
          // Hide controller if the click is outside the custom element
          this.controllersOff()
        }
      },
    
      setController_ElementButton: function (core) {
        const lang = core.lang
        const icons = core.icons
        const controller_btn = core.util.createElement("DIV")
    
        controller_btn.className = "se-controller se-controller-customElement"
        controller_btn.innerHTML = `
                <div class="se-arrow se-arrow-up"></div>
                <div class="element-content">
                    <span><span></span>&nbsp;</span>
                    <div class="se-btn-group">
                        <button type="button" data-command="update" tabindex="-1" class="se-tooltip">
                            ${icons.edit}
                            <span class="se-tooltip-inner"><span class="se-tooltip-text">${lang.controller.edit}</span></span>
                        </button>
                        <button type="button" data-command="delete" tabindex="-1" class="se-tooltip">
                            ${icons.delete}
                            <span class="se-tooltip-inner"><span class="se-tooltip-text">${lang.controller.remove}</span></span>
                        </button>
                    </div>
                </div>`
        return controller_btn
      },
    
      open: function () {
        this.plugins.dialog.open.call(
          this,
          "customElement",
          "customElement" === this.currentControllerName,
        )
      },
    
      submit: function (e) {
        this.showLoading()
        e.preventDefault()
        e.stopPropagation()
    
        const submitAction = function () {
          if (this.context.customElement.focusElement.value.trim().length === 0)
            return false
    
          const contextElement = this.context.customElement
          const content = contextElement.focusElement.value
    
          if (!this.context.dialog.updateModal) {
          //$$$$$$$$$$$$$ Creation
            const customElement = this.util.createElement("custom-element")
            customElement.textContent = content
    
            const selectedFormats = this.getSelectedElements()
            if (selectedFormats.length > 1) {
              const oFormat = this.util.createElement(selectedFormats[0].nodeName)
              oFormat.appendChild(customElement)
              this.insertNode(oFormat)
            } else {
              this.insertNode(customElement)
            }
    
            this.setRange(
              customElement.childNodes[0],
              0,
              customElement.childNodes[0],
              customElement.textContent.length,
            )
          } else {
          //$$$$$$$$$$$$$ Updation
            contextElement._element.textContent = content
            this.setRange(
              contextElement._element.childNodes[0],
              0,
              contextElement._element.childNodes[0],
              contextElement._element.textContent.length,
            )
          }
    
          this.history.push(false)
          contextElement.focusElement.value = ""
        }.bind(this)
    
        try {
          submitAction()
        } finally {
          this.plugins.dialog.close.call(this)
          this.closeLoading()
          this.focus()
        }
    
        return false
      },
    
      active: function (element) {
        if (element && element.tagName === "CUSTOM-ELEMENT") {
          if (
            this.controllerArray.indexOf(
              this.context.customElement.elementController,
            ) < 0
          ) {
            this.plugins.customElement.call_controller.call(this, element)
          }
          return true
        }
        // Hide controller for non-matching elements
        if (
          this.controllerArray.indexOf(
            this.context.customElement.elementController,
          ) > -1
        ) {
          this.controllersOff()
        }
        return false
      },
    
      call_controller: function (selectedElement) {
        this.context.customElement._element = selectedElement
        const controller = this.context.customElement.elementController
        const span = controller.querySelector("span > span")
    
        span.textContent = selectedElement.textContent
    
        // Position the controller near the selected element
        const offset = this.util.getOffset(
          selectedElement,
          this.context.element.wysiwygFrame,
        )
        controller.style.top = `${offset.top + selectedElement.offsetHeight + 10}px`
        controller.style.left = `${offset.left - this.context.element.wysiwygFrame.scrollLeft}px`
    
        controller.style.display = "block"
    
        this.controllersOn(controller, selectedElement, "customElement")
      },
    
      onClick_elementController: function (e) {
        e.stopPropagation()
    
        const command = e.target.getAttribute("data-command")
        if (!command) return
    
        e.preventDefault()
    
        if (/update/.test(command)) {
          const contextElement = this.context.customElement
          contextElement.focusElement.value = contextElement._element.textContent
          this.plugins.dialog.open.call(this, "customElement", true)
        } else {
        //$$$$$$$$$$$$$ Remove
          this.util.removeItem(this.context.customElement._element)
          this.context.customElement._element = null
          this.focus()
          this.history.push(false)
        }
    
        this.controllersOff()
      },
    
      init: function () {
        const contextElement = this.context.customElement
        contextElement.elementController.style.display = "none"
        contextElement._element = null
        contextElement.focusElement.value = ""
      },
    }
    
    // Use the custom plugin
    SUNEDITOR.create(document.getElementById("ex_dialog"), {
      plugins: [plugin_customElement],
      buttonList: [
        [
          {
            name: "customElement",
            dataDisplay: "dialog",
            title: "Custom Element",
            buttonClass: "",
            innerHTML: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2a10 10 0 1 1-10 10 10 10 0 0 1 10-10z" /></svg>'//$$$$$$$$$ Edit if required ,
          },
        ],
      ],
    })
    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>SunEditor Custom Button Example</title>
        <link
          rel="stylesheet"
          href="https://cdn.jsdelivr.net/npm/suneditor/dist/css/suneditor.min.css"
        />
        <script src="https://cdn.jsdelivr.net/npm/suneditor/dist/suneditor.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/suneditor/src/lang/en.js"></script>
    
        <script src="https://cdn.jsdelivr.net/npm/suneditor@latest/src/plugins/modules/dialog.js"></script>
    
        <script src="https://cdn.jsdelivr.net/npm/@joeattardi/[email protected]/dist/index.min.js"></script>
        <style>
          .wrapper {
            z-index: 1000;
          }
        </style>
      </head>
      <body>
        <textarea id="ex_dialog"></textarea>
        <script></script>
      </body>
    </html>

    I have only modified js. The above code is compataible with most type of custom element(like image, a,link,some marquee,etc.), I have demonstrated with <a>.
    The key working is the active: and onEditorClick: methods in above js, which helps to detect and launch the appropriate handles.

    Login or Signup to reply.
  2. To create a controller for a custom HTML element that allows for context buttons to update and delete the element, you can follow these steps. This example assumes you are working within a JavaScript environment, likely in conjunction with a web framework or library that supports custom elements.

    Step 1: Define the Custom Element
    First, you need to define your custom HTML element. This is done using the Web Components API. Here’s an example of how to create a simple custom element:

    class CustomElement extends HTMLElement {
        constructor() {
            super();
            this.attachShadow({ mode: 'open' });
            this.shadowRoot.innerHTML = ``;
        }
    }
    
    customElements.define('custom-element', CustomElement);
    

    Step 2: Create the Controller
    Next, create a controller that will handle the actions for updating and deleting the custom element. This controller will listen for events and manage the state of your custom elements.

    class CustomElementController {
    constructor() {
        this.customElements = [];
        this.init();
    }
    
    init() {
        document.addEventListener('click', (event) => {
            if (event.target.tagName === 'CUSTOM-ELEMENT') {
                this.showController(event.target);
            } else {
                this.hideController();
            }
        });
    
        // Add event listeners for update and delete buttons
        document.getElementById('updateButton').addEventListener('click', () => this.updateElement());
        document.getElementById('deleteButton').addEventListener('click', () => this.deleteElement());
    }
    
    showController(element) {
        // Display context buttons near the clicked element
        const controller = document.getElementById('elementController');
        const rect = element.getBoundingClientRect();
        
        controller.style.top = `${rect.bottom + window.scrollY}px`;
        controller.style.left = `${rect.left + window.scrollX}px`;
        controller.style.display = 'block';
    
        // Store reference to the selected element
        this.selectedElement = element;
    }
    
    hideController() {
        const controller = document.getElementById('elementController');
        controller.style.display = 'none';
    }
    
    updateElement() {
        if (this.selectedElement) {
            const newContent = prompt("Enter new content:", this.selectedElement.textContent);
            if (newContent !== null) {
                this.selectedElement.textContent = newContent;
            }
            this.hideController();
        }
    }
    
    deleteElement() {
        if (this.selectedElement) {
            this.selectedElement.remove();
            this.hideController();
            this.selectedElement = null;
        }
    }
    }
    
    // Initialize the controller
    const customController = new CustomElementController();
    

    Step 3: Create Context Buttons in HTML
    You need to have buttons in your HTML that will serve as the context menu for updating and deleting your custom elements.

    Step 4: Putting It All Together
    Remember to include everything in your main HTML file:

    This method I suggested will help you build a basic structure for managing a custom HTML element with context buttons for updating and deleting it. The CustomElement class defines how your custom elements behave, while CustomElementController manages user interactions with those elements.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search