Skip to content

Commit af5ecc5

Browse files
committed
fix(pat-depends): Fix infinite loop situations.
Fix some situations where infinite loops were created of unnecessary function calls were done: - Do not en/disable already en/disabled inputs. - Do not trigger and pat-depends element if the input is the element itself and not a contained sub-input. - Do not trigger input events on button-like elements.
1 parent 132e42b commit af5ecc5

File tree

3 files changed

+356
-9
lines changed

3 files changed

+356
-9
lines changed

src/pat/depends/depends.js

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ parser.addArgument("transition", "none", ["none", "css", "fade", "slide"]);
1616
parser.addArgument("effect-duration", "fast");
1717
parser.addArgument("effect-easing", "swing");
1818

19+
20+
// A custom input event which differs from the one in `core/events` in that it
21+
// accepts a `detail` object to pass arbitrary information around.
22+
// TODO: The events in `core/events` should be refactored to accept a `detail`
23+
// object.
24+
const input_event = (detail = {}) => {
25+
return new CustomEvent("input", {
26+
bubbles: true,
27+
cancelable: false,
28+
detail: detail,
29+
});
30+
};
31+
32+
1933
class Pattern extends BasePattern {
2034
static name = "depends";
2135
static trigger = ".pat-depends";
@@ -44,7 +58,14 @@ class Pattern extends BasePattern {
4458
input,
4559
"input",
4660
`pat-depends--input--${this.uuid}`,
47-
this.set_state.bind(this)
61+
(e) => {
62+
if (e?.detail?.pattern_uuid === this.uuid) {
63+
// Ignore input events invoked from this pattern
64+
// instance to avoid infinite loops.
65+
return;
66+
}
67+
this.set_state();
68+
}
4869
);
4970

5071
if (input.form) {
@@ -74,15 +95,40 @@ class Pattern extends BasePattern {
7495
enable() {
7596
const inputs = dom.find_inputs(this.el);
7697
for (const input of inputs) {
98+
if (input.disabled === false) {
99+
// Do not re-enable an already enabled input.
100+
continue;
101+
}
102+
103+
// Now, enable the input element.
77104
input.disabled = false;
78-
// Trigger the input after disabling so that any other bound
105+
106+
if (input === this.el) {
107+
// Do not re-trigger this pattern on it's own element to avoid
108+
// infinite loops.
109+
continue;
110+
}
111+
112+
if (dom.is_button(input)) {
113+
// Do not trigger the input event on buttons as they do not
114+
// support it.
115+
continue;
116+
}
117+
118+
// Trigger the input after enabling so that any other bound
79119
// actions can react on that.
80-
input.dispatchEvent(events.input_event());
120+
input.dispatchEvent(input_event({ pattern_uuid: this.uuid }));
81121
}
122+
123+
// Restore the original click behavior for anchor elements.
82124
if (this.el.tagName === "A") {
83125
events.remove_event_listener(this.el, "pat-depends--click");
84126
}
127+
128+
// Remove the disabled class from the element.
85129
this.el.classList.remove("disabled");
130+
131+
// Trigger the pat-update event to notify other patterns about enabling.
86132
this.$el.trigger("pat-update", {
87133
pattern: "depends",
88134
action: "attribute-changed",
@@ -94,17 +140,42 @@ class Pattern extends BasePattern {
94140
disable() {
95141
const inputs = dom.find_inputs(this.el);
96142
for (const input of inputs) {
143+
if (input.disabled === true) {
144+
// Do not re-disable an already disabled input.
145+
continue;
146+
}
147+
148+
// Now, disable the input element.
97149
input.disabled = true;
150+
151+
if (input === this.el) {
152+
// Do not re-trigger this pattern on it's own element to avoid
153+
// infinite loops.
154+
continue;
155+
}
156+
157+
if (dom.is_button(input)) {
158+
// Do not trigger the input event on buttons as they do not
159+
// support it.
160+
continue;
161+
}
162+
98163
// Trigger the input after disabling so that any other bound
99164
// actions can react on that.
100-
input.dispatchEvent(events.input_event());
165+
input.dispatchEvent(input_event({ pattern_uuid: this.uuid }));
101166
}
167+
168+
// Prevent the default click behavior for anchor elements.
102169
if (this.el.tagName === "A") {
103170
events.add_event_listener(this.el, "click", "pat-depends--click", (e) =>
104171
e.preventDefault()
105172
);
106173
}
174+
175+
// Add the disabled class to the element.
107176
this.el.classList.add("disabled");
177+
178+
// Trigger the pat-update event to notify other patterns about disabling.
108179
this.$el.trigger("pat-update", {
109180
pattern: "depends",
110181
action: "attribute-changed",

0 commit comments

Comments
 (0)