Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/core/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ const is_input = (el) => {
return re_input.test(el.nodeName);
};

/**
* Test, if a element is a button-like input type.
*
* @param {Node} el - The DOM node to test.
* @returns {Boolean} - True if the element is a input-type element.
*/
const is_button = (el) => {
return el.matches(`
button,
input[type=image],
input[type=button],
input[type=reset],
input[type=submit]
`);
};



/**
* Return all direct parents of ``el`` matching ``selector``.
* This matches against all parents but not the element itself.
Expand Down Expand Up @@ -613,6 +631,7 @@ const dom = {
acquire_attribute: acquire_attribute,
is_visible: is_visible,
is_input: is_input,
is_button: is_button,
create_from_string: create_from_string,
get_css_value: get_css_value,
find_scroll_container: find_scroll_container,
Expand Down
41 changes: 41 additions & 0 deletions src/core/dom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,47 @@ describe("core.dom tests", () => {
});
});

describe("is_button", () => {
it("checks, if an element is a button-like element or not.", (done) => {

const button = document.createElement("button");
const button_button = document.createElement("button");
button_button.setAttribute("type", "button");
const button_submit = document.createElement("button");
button_submit.setAttribute("type", "submit");

const input_button = document.createElement("input");
input_button.setAttribute("type", "button");
const input_submit = document.createElement("input");
input_submit.setAttribute("type", "submit");
const input_reset = document.createElement("input");
input_reset.setAttribute("type", "reset");
const input_image = document.createElement("input");
input_image.setAttribute("type", "image");

expect(dom.is_button(button)).toBe(true);
expect(dom.is_button(button_button)).toBe(true);
expect(dom.is_button(button_submit)).toBe(true);
expect(dom.is_button(input_button)).toBe(true);
expect(dom.is_button(input_image)).toBe(true);
expect(dom.is_button(input_reset)).toBe(true);
expect(dom.is_button(input_submit)).toBe(true);

const input_text = document.createElement("input");
input_text.setAttribute("type", "text");

expect(dom.is_button(input_text)).toBe(false);
expect(dom.is_button(document.createElement("input"))).toBe(false);
expect(dom.is_button(document.createElement("select"))).toBe(false);
expect(dom.is_button(document.createElement("textarea"))).toBe(false);
expect(dom.is_button(document.createElement("form"))).toBe(false);
expect(dom.is_button(document.createElement("div"))).toBe(false);

done();
});
});


describe("create_from_string", () => {
it("Creates a DOM element from a string", (done) => {
const res = dom.create_from_string(`
Expand Down
79 changes: 75 additions & 4 deletions src/pat/depends/depends.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ parser.addArgument("transition", "none", ["none", "css", "fade", "slide"]);
parser.addArgument("effect-duration", "fast");
parser.addArgument("effect-easing", "swing");


// A custom input event which differs from the one in `core/events` in that it
// accepts a `detail` object to pass arbitrary information around.
// TODO: The events in `core/events` should be refactored to accept a `detail`
// object.
const input_event = (detail = {}) => {
return new CustomEvent("input", {
bubbles: true,
cancelable: false,
detail: detail,
});
};


class Pattern extends BasePattern {
static name = "depends";
static trigger = ".pat-depends";
Expand Down Expand Up @@ -44,7 +58,14 @@ class Pattern extends BasePattern {
input,
"input",
`pat-depends--input--${this.uuid}`,
this.set_state.bind(this)
(e) => {
if (e?.detail?.pattern_uuid === this.uuid) {
// Ignore input events invoked from this pattern
// instance to avoid infinite loops.
return;
}
this.set_state();
}
);

if (input.form) {
Expand Down Expand Up @@ -74,15 +95,40 @@ class Pattern extends BasePattern {
enable() {
const inputs = dom.find_inputs(this.el);
for (const input of inputs) {
if (input.disabled === false) {
// Do not re-enable an already enabled input.
continue;
}

// Now, enable the input element.
input.disabled = false;
// Trigger the input after disabling so that any other bound

if (input === this.el) {
// Do not re-trigger this pattern on it's own element to avoid
// infinite loops.
continue;
}

if (dom.is_button(input)) {
// Do not trigger the input event on buttons as they do not
// support it.
continue;
}

// Trigger the input after enabling so that any other bound
// actions can react on that.
input.dispatchEvent(events.input_event());
input.dispatchEvent(input_event({ pattern_uuid: this.uuid }));
}

// Restore the original click behavior for anchor elements.
if (this.el.tagName === "A") {
events.remove_event_listener(this.el, "pat-depends--click");
}

// Remove the disabled class from the element.
this.el.classList.remove("disabled");

// Trigger the pat-update event to notify other patterns about enabling.
this.$el.trigger("pat-update", {
pattern: "depends",
action: "attribute-changed",
Expand All @@ -94,17 +140,42 @@ class Pattern extends BasePattern {
disable() {
const inputs = dom.find_inputs(this.el);
for (const input of inputs) {
if (input.disabled === true) {
// Do not re-disable an already disabled input.
continue;
}

// Now, disable the input element.
input.disabled = true;

if (input === this.el) {
// Do not re-trigger this pattern on it's own element to avoid
// infinite loops.
continue;
}

if (dom.is_button(input)) {
// Do not trigger the input event on buttons as they do not
// support it.
continue;
}

// Trigger the input after disabling so that any other bound
// actions can react on that.
input.dispatchEvent(events.input_event());
input.dispatchEvent(input_event({ pattern_uuid: this.uuid }));
}

// Prevent the default click behavior for anchor elements.
if (this.el.tagName === "A") {
events.add_event_listener(this.el, "click", "pat-depends--click", (e) =>
e.preventDefault()
);
}

// Add the disabled class to the element.
this.el.classList.add("disabled");

// Trigger the pat-update event to notify other patterns about disabling.
this.$el.trigger("pat-update", {
pattern: "depends",
action: "attribute-changed",
Expand Down
Loading