DEV Community

Bryan Ollendyke
Bryan Ollendyke

Posted on

1 1

haxHooks: webcomponents that ship their own editing UX

This time I had an idea. What if instead of a new tag bringing in a totally different experience, I leveraged haxHooks to allow for modification of the element in context via contenteditable.

How this works in the video in task-list:

  • listen for activeElement state change in hax
  • set internal editMode state to match
  • on enable, set a mutation observer and apply contenteditable to internal shadowRoot elements
  • make changes by typing
  • on activeElement disable state, turn off content editable, remove mutationObserver, and look at the shadowRoot and update internal state values to match.

Here's the relevant pieces of code from the video:

render() {
    return html`
      <div id="wrapper">
        <h3 property="oer:name" ?contenteditable="${this.editMode}">${this.name}</h3>
        <ol ?contenteditable="${this.editMode}">
          ${this.tasks.map(
            (task) => html`
              <li>
                ${task.link
                  ? html`
                      <a href="${task.link}" property="oer:task">${task.name}</a>
                    `
                  : html` <span property="oer:task">${task.name}</span> `}
              </li>
            `
          )}
        </ol>
      </div>
    `;
  }
  /**
   * Implements haxHooks to tie into life-cycle if hax exists.
   */
  haxHooks() {
    return {
      activeElementChanged: "haxactiveElementChanged",
      inlineContextMenu: "haxinlineContextMenu",
    };
  }
  /**
   * double-check that we are set to inactivate click handlers
   * this is for when activated in a duplicate / adding new content state
   */
  haxactiveElementChanged(el, val) {
    if (this.__thereAreChanges) {
      this.alignState();
    }
    this.editMode = val;
  }
  haxinlineContextMenu(ceMenu) {
    ceMenu.ceButtons = [
      {
        icon: "icons:add",
        callback: "haxClickInlineAdd",
        label: "Add task",
      },
      {
        icon: "icons:remove",
        callback: "haxClickInlineRemove",
        label: "Remove task",
      },
    ];
  }
  haxClickInlineAdd(e) {
    let d = this.tasks;
    d.push({ name: "Do this" });
    this.tasks = [...d];
    return true;
  }
  haxClickInlineRemove(e) {
    if (this.tasks.length > 0) {
      let d = this.tasks;
      d.pop();
      this.tasks = [...d];
      return true;
    }
  }
  static get tag() {
    return "task-list";
  }
  alignState() {
    this.name = this.shadowRoot.querySelector('h3').innerText;
    this.__thereAreChanges = false;
  }
  updated(changedProperties) {
    changedProperties.forEach((oldValue, propName) => {
      if (propName === 'editMode') {
        if (!this[propName]) {
          if (this._observer) {
            this._observer.disconnect();
          }
        }
        else {
          this._observer = new MutationObserver((mutations) => {
            this.__thereAreChanges = true;
          });
          this._observer.observe(this.shadowRoot.querySelector('#wrapper'), {
            childList: true,
            subtree: true,
            characterData: true,
          });
        }
      }
    });
  }
Enter fullscreen mode Exit fullscreen mode

Next I'm going to use this state change to update the array of values in the task list, a more complex testing and iteration activity than I wanted to do via live coding :)

code repo: https://github.com/elmsln/lrnwebcomponents/blob/master/elements/task-list/src/task-list.js
will be in the next release of task-list on npm later this month

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay