From 4c9d41bcb0c06fb44f0da9d5e2ea54e4984bda7e Mon Sep 17 00:00:00 2001 From: Christophe Henry Date: Thu, 18 Jun 2026 12:58:08 +0200 Subject: [PATCH] Allow to use scoped (':scope' and '&') selectors relative to controller's element in outlets selectors --- src/core/outlet_observer.ts | 4 +++- src/core/outlet_set.ts | 4 +++- src/mutation-observers/selector_observer.ts | 6 ++---- src/tests/controllers/outlet_controller.ts | 2 +- src/tests/modules/core/outlet_tests.ts | 14 +++++++++++--- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/core/outlet_observer.ts b/src/core/outlet_observer.ts index b0956f47..5de9fca2 100644 --- a/src/core/outlet_observer.ts +++ b/src/core/outlet_observer.ts @@ -95,7 +95,9 @@ export class OutletObserver implements AttributeObserverDelegate, SelectorObserv const hasOutletController = element.matches(`[${this.schema.controllerAttribute}~=${outletName}]`) if (selector) { - return hasOutlet && hasOutletController && element.matches(selector) + const matches = + element.matches(selector) || Array.from(this.context.element.querySelectorAll(selector)).includes(element) + return hasOutlet && hasOutletController && matches } else { return false } diff --git a/src/core/outlet_set.ts b/src/core/outlet_set.ts index b8ca77b4..f0135742 100644 --- a/src/core/outlet_set.ts +++ b/src/core/outlet_set.ts @@ -66,6 +66,8 @@ export class OutletSet { private matchesElement(element: Element, selector: string, outletName: string): boolean { const controllerAttribute = element.getAttribute(this.scope.schema.controllerAttribute) || "" - return element.matches(selector) && controllerAttribute.split(" ").includes(outletName) + const matches = + element.matches(selector) || Array.from(this.controllerElement.querySelectorAll(selector)).includes(element) + return matches && controllerAttribute.split(" ").includes(outletName) } } diff --git a/src/mutation-observers/selector_observer.ts b/src/mutation-observers/selector_observer.ts index 6321de64..33651fd9 100644 --- a/src/mutation-observers/selector_observer.ts +++ b/src/mutation-observers/selector_observer.ts @@ -61,13 +61,11 @@ export class SelectorObserver implements ElementObserverDelegate { const { selector } = this if (selector) { - const matches = element.matches(selector) - if (this.delegate.selectorMatchElement) { - return matches && this.delegate.selectorMatchElement(element, this.details) + return this.delegate.selectorMatchElement(element, this.details) } - return matches + return element.matches(selector) } else { return false } diff --git a/src/tests/controllers/outlet_controller.ts b/src/tests/controllers/outlet_controller.ts index b382f80c..e7cb6546 100644 --- a/src/tests/controllers/outlet_controller.ts +++ b/src/tests/controllers/outlet_controller.ts @@ -12,7 +12,7 @@ class BaseOutletController extends Controller { export class OutletController extends BaseOutletController { static classes = ["connected", "disconnected"] - static outlets = ["beta", "gamma", "delta", "omega", "namespaced--epsilon"] + static outlets = ["beta", "gamma", "delta", "omega", "namespaced--epsilon", "zeta"] static values = { alphaOutletConnectedCallCount: Number, diff --git a/src/tests/modules/core/outlet_tests.ts b/src/tests/modules/core/outlet_tests.ts index fb87ba12..e85bc978 100644 --- a/src/tests/modules/core/outlet_tests.ts +++ b/src/tests/modules/core/outlet_tests.ts @@ -12,6 +12,8 @@ export default class OutletTests extends ControllerTestCase(OutletController) {
+ +
+
@@ -36,8 +40,9 @@ export default class OutletTests extends ControllerTestCase(OutletController) {
` + get identifiers() { - return ["test", "alpha", "beta", "gamma", "delta", "omega", "namespaced--epsilon"] + return ["test", "alpha", "beta", "gamma", "delta", "omega", "namespaced--epsilon", "zeta"] } "test OutletSet#find"() { @@ -45,6 +50,7 @@ export default class OutletTests extends ControllerTestCase(OutletController) { this.assert.equal(this.controller.outlets.find("beta"), this.findElement("#beta1")) this.assert.equal(this.controller.outlets.find("delta"), this.findElement("#delta1")) this.assert.equal(this.controller.outlets.find("namespaced--epsilon"), this.findElement("#epsilon1")) + this.assert.equal(this.controller.outlets.find("zeta"), this.findElement("#inner-zeta")) } "test OutletSet#findAll"() { @@ -54,12 +60,13 @@ export default class OutletTests extends ControllerTestCase(OutletController) { this.controller.outlets.findAll("namespaced--epsilon"), this.findElements("#epsilon1", "#epsilon2") ) + this.assert.deepEqual(this.controller.outlets.findAll("zeta"), this.findElements("#inner-zeta")) } "test OutletSet#findAll with multiple arguments"() { this.assert.deepEqual( - this.controller.outlets.findAll("alpha", "beta", "namespaced--epsilon"), - this.findElements("#alpha1", "#alpha2", "#beta1", "#beta2", "#epsilon1", "#epsilon2") + this.controller.outlets.findAll("alpha", "beta", "namespaced--epsilon", "zeta"), + this.findElements("#alpha1", "#alpha2", "#beta1", "#beta2", "#epsilon1", "#epsilon2", "#inner-zeta") ) } @@ -70,6 +77,7 @@ export default class OutletTests extends ControllerTestCase(OutletController) { this.assert.equal(this.controller.outlets.has("delta"), true) this.assert.equal(this.controller.outlets.has("omega"), false) this.assert.equal(this.controller.outlets.has("namespaced--epsilon"), true) + this.assert.equal(this.controller.outlets.has("zeta"), true) } "test OutletSet#has when attribute gets added later"() {