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
3 changes: 1 addition & 2 deletions packages/esl-website/src/autofocus/autofocus-mixin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {ESLMixinElement} from '@exadel/esl/modules/esl-mixin-element/core';
import {attr, listen, memoize} from '@exadel/esl/modules/esl-utils/decorators';
import {getKeyboardFocusableElements} from '@exadel/esl/modules/esl-utils/dom';
import {ESLTraversingQuery} from '@exadel/esl/modules/esl-traversing-query/core';
import type {ESLToggleable} from '@exadel/esl/modules/esl-toggleable/core';

export class ESLDemoAutofocus extends ESLMixinElement {
Expand All @@ -13,7 +12,7 @@ export class ESLDemoAutofocus extends ESLMixinElement {

@memoize()
get $focusable(): HTMLElement | undefined {
if (this.target) return ESLTraversingQuery.first(this.target, this.$host) as HTMLElement | undefined;
if (this.target) return this.$$find(this.target) as HTMLElement | undefined;
return getKeyboardFocusableElements(this.$host)[0] as HTMLElement | undefined;
}

Expand Down
5 changes: 2 additions & 3 deletions packages/esl-website/src/esl-popup/esl-d-popup-game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {ESLBaseElement} from '@exadel/esl/modules/esl-base-element/core';
import {ESLPopup} from '@exadel/esl/modules/esl-popup/core';
import {attr, boolAttr, listen, memoize} from '@exadel/esl/modules/esl-utils/decorators';
import {ESLResizeObserverTarget} from '@exadel/esl/modules/esl-utils/dom';
import {ESLTraversingQuery} from '@exadel/esl/modules/esl-traversing-query/core';

import type {Point} from '@exadel/esl/modules/esl-utils/dom';

Expand All @@ -18,8 +17,8 @@ export class ESLDemoPopupGame extends ESLBaseElement {
@boolAttr() public dragging = false;

@memoize()
get $trigger(): HTMLElement | undefined {
if (this.trigger) return ESLTraversingQuery.first(this.trigger, this) as HTMLElement | undefined;
get $trigger(): HTMLElement | null {
return this.trigger ? this.$$find(this.trigger) as HTMLElement | null : null;
}

protected override connectedCallback(): void {
Expand Down
3 changes: 1 addition & 2 deletions packages/esl-website/src/navigation/header/header-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {prop, attr, boolAttr, listen} from '@exadel/esl/modules/esl-utils/decora
import {CSSClassUtils} from '@exadel/esl/modules/esl-utils/dom/class';
import {afterNextRender} from '@exadel/esl/modules/esl-utils/async/raf';
import {parseNumber} from '@exadel/esl/modules/esl-utils/misc/format';
import {ESLTraversingQuery} from '@exadel/esl/modules/esl-traversing-query/core';
import {ESLToggleable} from '@exadel/esl/modules/esl-toggleable/core';
import {requestGss} from '../../search/search-script';

Expand All @@ -27,7 +26,7 @@ export class ESLDemoSearchBox extends ESLToggleable {
private showSearchElements(params: ESLToggleableActionParams): void {
afterNextRender(() => super.onShow(params));
if (this.autofocus) {
const $focusEl = ESLTraversingQuery.first(this.firstFocusable, this) as HTMLElement;
const $focusEl = this.$$find(this.firstFocusable) as HTMLElement;
$focusEl && window.setTimeout(() => $focusEl.focus(), parseNumber(this.postClsDelay));
}

Expand Down
5 changes: 2 additions & 3 deletions packages/esl/src/esl-alert/core/esl-alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {attr, jsonAttr, prop, listen} from '../../esl-utils/decorators';
import {isMatches} from '../../esl-utils/dom/traversing';
import {ESLToggleable} from '../../esl-toggleable/core';
import {CSSClassUtils} from '../../esl-utils/dom/class';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';

import type {ESLToggleableActionParams, ESLToggleableRequestDetails} from '../../esl-toggleable/core';

Expand Down Expand Up @@ -74,7 +73,7 @@ export class ESLAlert extends ESLToggleable {
protected override attributeChangedCallback(attrName: string, oldVal: string, newVal: string): void {
if (!this.connected) return;
if (attrName === 'target') {
this.$target = ESLTraversingQuery.first(this.target) as EventTarget;
this.$target = this.$$find(newVal) as EventTarget;
}
}

Expand All @@ -86,7 +85,7 @@ export class ESLAlert extends ESLToggleable {
this.innerHTML = '';
this.appendChild(this.$content);
if (this.target) {
this.$target = ESLTraversingQuery.first(this.target, this) as EventTarget;
this.$target = this.$$find(this.target) as EventTarget;
}
}

Expand Down
4 changes: 1 addition & 3 deletions packages/esl/src/esl-animate/core/esl-animate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {ExportNs} from '../../esl-utils/environment/export-ns';
import {ready, memoize, attr, boolAttr} from '../../esl-utils/decorators';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';
import {parseNumber} from '../../esl-utils/misc/format';
import {ESLBaseElement} from '../../esl-base-element/core';

Expand Down Expand Up @@ -61,7 +60,7 @@ export class ESLAnimate extends ESLBaseElement {
/** Elements-targets found by target query */
@memoize()
public get $targets(): HTMLElement[] {
return ESLTraversingQuery.all(this.target, this) as HTMLElement[];
return this.$$findAll(this.target) as HTMLElement[];
}

protected override attributeChangedCallback(): void {
Expand Down Expand Up @@ -104,4 +103,3 @@ declare global {
'esl-animate': ESLAnimate;
}
}

4 changes: 4 additions & 0 deletions packages/esl/src/esl-base-element/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Attributes:
- `$$attr` - checks or changes element attributes
- `$$fire` - dispatches event

- `$$find` - finds the first element that matches the passed selector using **ESLTraversingQuery** (extended selector syntax)
- `$$findAll` - finds all elements that match the passed selector using **ESLTraversingQuery** (extended selector syntax)
- Note: empty selector (`''`) returns the current base element; relative selectors like `::closest(...)`, `::parent`, `::find(...)` are supported. See `esl-traversing-query` module docs.

- `$$on` - subscribes to the event manually or subscribes decorated method
- `$$off` - unsubscribes from the event manually or unsubscribes decorated method

Expand Down
10 changes: 10 additions & 0 deletions packages/esl/src/esl-base-element/core/esl-base-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {setAttr} from '../../esl-utils/dom/attr';
import {prop} from '../../esl-utils/decorators';
import {ESLEventUtils} from '../../esl-utils/dom/events';
import {CSSClassUtils} from '../../esl-utils/dom/class';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';

import type {
DelegatedEvent,
Expand Down Expand Up @@ -112,6 +113,15 @@ export abstract class ESLBaseElement extends HTMLElement implements ESLBaseCompo
return prevValue;
}

/** Resolves the first element matching the specified traversing query relative to the current element. Uses ESLTraversingQuery to find the element. */
public $$find(selector: string): Element | null {
return ESLTraversingQuery.first(selector, this);
}
/** Resolves all elements matching the specified traversing query relative to the current element. Uses ESLTraversingQuery to find the elements. */
public $$findAll(selector: string): Element[] {
return ESLTraversingQuery.all(selector, this);
}

/**
* Dispatches component custom event.
* @param eventName - event name
Expand Down
26 changes: 26 additions & 0 deletions packages/esl/src/esl-base-element/test/element.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {ESLBaseElement} from '../core';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';

describe('ESLBaseElement', () => {
class TestElement extends ESLBaseElement {
Expand Down Expand Up @@ -124,5 +125,30 @@ describe('ESLBaseElement', () => {
}, {once: true});
el.$$fire('testevent');
}));

describe('$$find/$$findAll delegate to ESLTraversingQuery', () => {
const $child1 = document.createElement('div');
const $child2 = document.createElement('span');

beforeEach(() => {
el.replaceChildren($child1, $child2);
});

test('$$find delegates to ESLTraversingQuery.first with base = element', () => {
const spy = vi.spyOn(ESLTraversingQuery, 'first').mockReturnValue($child1);
const result = el.$$find('::child');
expect(spy).toHaveBeenCalledWith('::child', el);
expect(result).toBe($child1);
spy.mockRestore();
});

test('$$findAll delegates to ESLTraversingQuery.all with base = element', () => {
const spy = vi.spyOn(ESLTraversingQuery, 'all').mockReturnValue([$child1, $child2]);
const result = el.$$findAll('::child');
expect(spy).toHaveBeenCalledWith('::child', el);
expect(result).toEqual([$child1, $child2]);
spy.mockRestore();
});
});
});
});
3 changes: 1 addition & 2 deletions packages/esl/src/esl-carousel/core/esl-carousel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {microtask} from '../../esl-utils/async';
import {parseBoolean, parseTime, sequentialUID, toCamelCase} from '../../esl-utils/misc';

import {CSSClassUtils} from '../../esl-utils/dom/class';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';
import {ESLMediaRuleList} from '../../esl-media-query/core';
import {ESLResizeObserverTarget} from '../../esl-event-listener/core';

Expand Down Expand Up @@ -248,7 +247,7 @@ export class ESLCarousel extends ESLBaseElement {
*/
@memoize()
public get $container(): Element | null {
return ESLTraversingQuery.first(this.container, this) as HTMLElement;
return this.$$find(this.container) as HTMLElement;
}

/** @returns carousel slides area */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {listen, memoize} from '../../../esl-utils/decorators';
import {parseTime} from '../../../esl-utils/misc/format';
import {CSSClassUtils} from '../../../esl-utils/dom/class';
import {ESLMediaRuleList} from '../../../esl-media-query/core';
import {ESLTraversingQuery} from '../../../esl-traversing-query/core';
import {ESLIntersectionTarget, ESLIntersectionEvent} from '../../../esl-event-listener/core';

import {ESLCarouselPlugin} from '../esl-carousel.plugin';
Expand Down Expand Up @@ -103,25 +102,25 @@ export class ESLCarouselAutoplayMixin extends ESLCarouselPlugin<ESLCarouselAutop
@memoize()
public get $controls(): HTMLElement[] {
const sel = this.config.control;
return sel ? ESLTraversingQuery.all(sel, this.$host) as HTMLElement[] : [];
return sel ? this.$$findAll(sel) as HTMLElement[] : [];
}

/** Interaction scope elements (memoized) */
@memoize()
public get $interactionScope(): HTMLElement[] {
const sel = this.config.interactionScope;
return sel ? ESLTraversingQuery.all(sel, this.$host) as HTMLElement[] : [this.$host];
}

/** True if any scope element is hovered */
public get hovered(): boolean {
return this.$interactionScope.some(($el) => $el.matches('*:hover'));
return sel ? this.$$findAll(sel) as HTMLElement[] : [this.$host];
}

/** True if active slide contains any blocking items */
public get hasActiveBlockingItems(): boolean {
const {blockerSelector} = this.config;
return !!blockerSelector && !!ESLTraversingQuery.first(blockerSelector, this.$host);
return !!blockerSelector && !!this.$$find(blockerSelector);
}

/** True if any scope element is hovered */
public get hovered(): boolean {
return this.$interactionScope.some(($el) => $el.matches('*:hover'));
}

/** True if keyboard-visible focus is within scope */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {format} from '../../../esl-utils/misc/format';
import {ARROW_LEFT, ARROW_RIGHT} from '../../../esl-utils/dom/keys';
import {attr, listen, memoize, prop, ready} from '../../../esl-utils/decorators';
import {ESLBaseElement} from '../../../esl-base-element/core';
import {ESLTraversingQuery} from '../../../esl-traversing-query/core';

import {indexToGroup} from '../../core/esl-carousel.utils';
import {ESLCarouselChangeEvent, ESLCarouselSlideEvent} from '../../core/esl-carousel.events';
Expand Down Expand Up @@ -122,7 +121,7 @@ export class ESLCarouselNavDots extends ESLBaseElement {
/** @returns ESLCarousel instance; based on {@link carousel} attribute */
@memoize()
public get $carousel(): ESLCarousel | null {
return ESLTraversingQuery.first(this.carousel, this) as ESLCarousel;
return this.$$find(this.carousel) as ESLCarousel | null;
}

/** @returns accessible target ID */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {ExportNs} from '../../../esl-utils/environment/export-ns';
import {format} from '../../../esl-utils/misc/format';
import {attr, memoize, listen} from '../../../esl-utils/decorators';
import {ESLBaseElement} from '../../../esl-base-element/core';
import {ESLTraversingQuery} from '../../../esl-traversing-query/core';

import {indexToGroup} from '../../core/esl-carousel.utils';
import {ESLCarouselChangeEvent, ESLCarouselSlideEvent} from '../../core/esl-carousel.events';
Expand Down Expand Up @@ -36,7 +35,7 @@ export class ESLCarouselInfo extends ESLBaseElement {
/** Returns ESLCarousel instance based on `target` attr */
@memoize()
public get $carousel(): ESLCarousel | null {
return ESLTraversingQuery.first(this.carousel, this) as ESLCarousel;
return this.$$find(this.carousel) as ESLCarousel;
}

public override connectedCallback(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {ExportNs} from '../../../esl-utils/environment/export-ns';
import {attr, listen, memoize, ready} from '../../../esl-utils/decorators';
import {ESLMixinElement} from '../../../esl-mixin-element/core';
import {ESLTraversingQuery} from '../../../esl-traversing-query/core';

import {ESLCarouselChangeEvent, ESLCarouselMoveEvent, ESLCarouselSlideEvent} from '../../core/esl-carousel.events';

Expand Down Expand Up @@ -44,7 +43,7 @@ export class ESLCarouselNavMixin extends ESLMixinElement {
/** @returns ESLCarousel instance; based on {@link carousel} attribute */
@memoize()
public get $carousel(): ESLCarousel | null {
return ESLTraversingQuery.first(this.carousel, this.$host) as ESLCarousel;
return this.$$find(this.carousel) as ESLCarousel;
}

/** @returns accessible target ID */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {ExportNs} from '../../../esl-utils/environment/export-ns';
import {listen, memoize} from '../../../esl-utils/decorators';
import {ESLTraversingQuery} from '../../../esl-traversing-query/core';

import {ESLCarousel} from '../../core/esl-carousel';
import {ESLCarouselPlugin} from '../esl-carousel.plugin';
Expand Down Expand Up @@ -35,7 +34,7 @@ export class ESLCarouselRelateToMixin extends ESLCarouselPlugin<ESLCarouselRelat
public get $target(): ESLCarousel | null {
const {target} = this.config;
if (!target || target === 'none') return null;
const $target = ESLTraversingQuery.first(target, this.$host);
const $target = this.$$find(target);
// Prevent cyclic reference - target should not be the host itself
if (!($target instanceof ESLCarousel) || $target === this.$host) return null;
return $target;
Expand Down Expand Up @@ -73,4 +72,3 @@ declare global {
RelateTo: typeof ESLCarouselRelateToMixin;
}
}

3 changes: 1 addition & 2 deletions packages/esl/src/esl-footnotes/core/esl-footnotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {ExportNs} from '../../esl-utils/environment/export-ns';
import {bind, memoize, attr, listen, prop} from '../../esl-utils/decorators';
import {debounce} from '../../esl-utils/async/debounce';
import {ESLBaseElement} from '../../esl-base-element/core';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';
import {ESLEventUtils} from '../../esl-utils/dom/events';
import {ENTER, SPACE} from '../../esl-utils/dom/keys';
import {sequentialUID} from '../../esl-utils/misc/uid';
Expand Down Expand Up @@ -35,7 +34,7 @@ export class ESLFootnotes extends ESLBaseElement {
/** Scope element */
@memoize()
protected get scopeEl(): HTMLElement {
return ESLTraversingQuery.first(this.scopeTarget, this) as HTMLElement;
return this.$$find(this.scopeTarget) as HTMLElement;
}

/** Notes that are allowed to be processed by footnotes */
Expand Down
3 changes: 1 addition & 2 deletions packages/esl/src/esl-footnotes/core/esl-note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {ESLTooltip} from '../../esl-tooltip/core';
import {promisifyTimeout, repeatSequence} from '../../esl-utils/async';
import {scrollIntoView} from '../../esl-utils/dom/scroll';
import {ESLMediaQuery} from '../../esl-media-query/core';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';

import type {ESLToggleable, ESLToggleableActionParams} from '../../esl-toggleable/core/esl-toggleable';
import type {ESLFootnotes} from './esl-footnotes';
Expand Down Expand Up @@ -202,7 +201,7 @@ export class ESLNote extends ESLBaseTrigger {
/** Merge params to pass to the toggleable */
protected override mergeToggleableParams(this: ESLNote, ...params: ESLTooltipActionParams[]): ESLTooltipActionParams {
const container = this.getClosestRelatedAttr('container') || this.container;
const containerEl = container ? ESLTraversingQuery.first(container, this) as HTMLElement : undefined;
const containerEl = container ? this.$$find(container) as HTMLElement : undefined;
return super.mergeToggleableParams({
initiator: 'note',
activator: this,
Expand Down
3 changes: 1 addition & 2 deletions packages/esl/src/esl-image/core/esl-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {bind, prop, attr, boolAttr} from '../../esl-utils/decorators';
import {CSSClassUtils} from '../../esl-utils/dom/class';
import {ESLBaseElement} from '../../esl-base-element/core';
import {ESLMediaRuleList} from '../../esl-media-query/core';
import {ESLTraversingQuery} from '../../esl-traversing-query/core/esl-traversing-query';

import {getIObserver} from './esl-image-iobserver';
import {EMPTY_IMAGE, STRATEGIES, isEmptyImage} from './esl-image-strategies';
Expand Down Expand Up @@ -299,7 +298,7 @@ export class ESLImage extends ESLBaseElement {
const cls = this.containerClass || (this.constructor as typeof ESLImage).DEFAULT_CONTAINER_CLS;
const state = isLoadState(this.containerClassState) && this[this.containerClassState];

const targetEl = ESLTraversingQuery.first(this.containerClassTarget, this) as HTMLElement;
const targetEl = this.$$find(this.containerClassTarget) as HTMLElement;
targetEl && CSSClassUtils.toggle(targetEl, cls, state);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {attr, boolAttr, listen, memoize, prop} from '../../esl-utils/decorators';
import {ESLTraversingQuery} from '../../esl-traversing-query/core';
import {ESLMixinElement} from '../../esl-mixin-element/core';

/**
Expand All @@ -24,11 +23,11 @@ export class ESLLineClampToggler extends ESLMixinElement {
/** @returns the target element to control */
@memoize()
public get $target(): HTMLElement {
return ESLTraversingQuery.first(this.target, this.$host) as HTMLElement;
return this.$$find(this.target) as HTMLElement;
}

protected get isTargetActive(): boolean {
return !!this.$target.hasAttribute(this.ALT_ACTIVE_ATTRIBUTE);
return this.$target.hasAttribute(this.ALT_ACTIVE_ATTRIBUTE);
}

protected override connectedCallback(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {ESLMixinElement} from '../../esl-mixin-element/core';
import {ESLTraversingQuery} from '../../esl-traversing-query/core/esl-traversing-query';
import {listen, memoize} from '../../esl-utils/decorators';
import {parseObjectSafe} from '../../esl-utils/misc/format';
import {ExportNs} from '../../esl-utils/environment/export-ns';
Expand Down Expand Up @@ -34,7 +33,7 @@ export class ESLMediaControlMixin extends ESLMixinElement {

public get $target(): ESLMedia | null {
if (!this.config.target) return null;
return ESLTraversingQuery.first(this.config.target, this.$host) as ESLMedia | null;
return this.$$find(this.config.target) as ESLMedia | null;
}

protected override attributeChangedCallback(): void {
Expand Down
Loading
Loading