DEV Community

Bryan Ollendyke
Bryan Ollendyke

Posted on

2 1

haxHooks(): How elements can supply their own editing experience in HAX now!

HAX the web is a tag and a movement to replace the way WYSIWYG editors of the past worked and replace it with pure design assets that can plug directly into any editing experience. This week, after refactoring our internals the last few weeks, I have added a new construct in our integration arsenal.

What we've had: haxProperties

haxProperties is a static get method that elements wanting to integrate with HAX implement. By having this method and supplying our schema, our editor knows how to build headless forms to manipulate your design asset in context. Example:

  static get haxProperties() {
    return {
      canScale: true,
      canPosition: true,
      canEditSource: false,
      gizmo: {
        title: "Multiple choice",
        description: "Multiple choice self check",
        icon: "hax:multiple-choice",
        color: "purple",
        groups: ["Instructional"],
        handles: [],
        meta: {
          author: "ELMS:LN",
        },
      },
      settings: {
        configure: [
          {
            property: "title",
            title: "Title",
            description: "The title of the element",
            inputMethod: "textfield",
          },
          {
            property: "hideTitle",
            title: "Hide title",
            description: "Whether or not to display the title",
            inputMethod: "boolean",
          },
          {
            property: "question",
            title: "Question",
            description: "Question for users to respond to.",
            inputMethod: "textfield",
          },
          {
            property: "randomize",
            title: "Randomize",
            description: "Randomize the answers dynamically",
            inputMethod: "boolean",
          },
          {
            property: "answers",
            title: "Answer set",
            description: "Answers in a multiple choice",
            inputMethod: "array",
            itemLabel: "label",
            properties: [
              {
                property: "correct",
                title: "Correct",
                description: "If this is correct or not",
                inputMethod: "boolean",
              },
              {
                property: "label",
                title: "Answer",
                description: "Possible answer to the question",
                inputMethod: "textfield",
                required: true,
              },
            ],
          },
          {
            property: "correctText",
            title: "Correct feedback",
            description: "Feedback when they get it right",
            inputMethod: "textfield",
          },
          {
            property: "correctIcon",
            title: "Correct icon",
            description: "Icon to display when correct answer happens",
            inputMethod: "iconpicker",
            options: [
              "icons:trending-flat",
              "icons:launch",
              "icons:pan-tool",
              "icons:link",
              "icons:check",
              "icons:favorite",
              "icons:thumb-up",
              "icons:thumb-down",
              "icons:send",
            ],
          },
          {
            property: "incorrectText",
            title: "Incorrect feedback",
            description: "Feedback when they get it wrong",
            inputMethod: "textfield",
          },
          {
            property: "incorrectIcon",
            title: "Incorrect icon",
            description: "Icon to display when wrong answer happens",
            inputMethod: "iconpicker",
            options: [
              "icons:trending-flat",
              "icons:launch",
              "icons:pan-tool",
              "icons:link",
              "icons:check",
              "icons:favorite",
              "icons:thumb-up",
              "icons:thumb-down",
              "icons:send",
            ],
          },
          {
            property: "quizName",
            title: "Name of the quiz",
            description: "Quiz name passed in",
            inputMethod: "textfield",
          },
        ],
        advanced: [
          {
            property: "checkLabel",
            title: "Check answers label",
            description: "Label for getting solution feedback",
            inputMethod: "textfield",
          },
          {
            property: "resetLabel",
            title: "Reset label",
            description: "label for the reset button",
            inputMethod: "textfield",
          },
        ],
      },
      saveOptions: {
        unsetAttributes: [
          "__utils",
          "displayed-answers",
          "displayedAnswers",
          "colors",
        ],
      },
    };
  }
Enter fullscreen mode Exit fullscreen mode

The above is how we've worked with HAX to date. This is enough to tell HAX that it can be placed in the page, how to present it's forms, and how to clean up data when saved. It's awesome, but inflexible (by design).

Introducing haxHooks

haxHooks is a method implemented on your design asset web components that allows it to deeply tie into HAX and visa versa. They provide the ability to mix state and respond to events in a consistent way across the ecosystem.

Here's an example from another element now implementing haxHooks, this time to interrupt clicking to open a link that makes sense for the design asset but would make the element in-editable when trying to click to highlight it in HAX.

  /**
   * Implements haxHooks to tie into life-cycle if hax exists.
   */
  haxHooks() {
    return {
      editModeChanged: "haxeditModeChanged",
      activeElementChanged: "haxactiveElementChanged",
    };
  }
  /**
   * 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 (val) {
      this._haxstate = val;
    }
  }
  /**
   * Set a flag to test if we should block link clicking on the entire card
   * otherwise when editing in hax you can't actually edit it bc its all clickable.
   * if editMode goes off this helps ensure we also become clickable again
   */
  haxeditModeChanged(val) {
    this._haxstate = val;
  }
  /**
   * special support for HAX since the whole card is selectable
   */
  _clickCard(e) {
    if (this._haxstate) {
      // do not do default
      e.preventDefault();
      e.stopPropagation();
      e.stopImmediatePropagation();
    }
  }
Enter fullscreen mode Exit fullscreen mode

In this code we can see that this._haxstate is set to true (default false) when HAX has said that it is enabled and working on the asset. When saving, we can disable this flag based on editMode changing in HAX.

This video goes deep in the weeds on how this works and what's now possible as a result

Heroku

Deploy with ease. Manage efficiently. Scale faster.

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

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay