diff --git a/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.component.ts b/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.component.ts index b1b1677e35..45f7e91a63 100644 --- a/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.component.ts +++ b/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.component.ts @@ -72,7 +72,7 @@ export class SkyDocsHeadingAnchorComponent implements AfterViewInit, OnDestroy { public readonly strikethrough = input(false, { transform: booleanAttribute }); public ngAfterViewInit(): void { - this.#anchorSvc?.register(this); + this.#anchorSvc?.register(this, this.#elementRef.nativeElement); } public ngOnDestroy(): void { diff --git a/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.service.ts b/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.service.ts index 93a424b785..ab1653f6b1 100644 --- a/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.service.ts +++ b/libs/components/docs-tools/src/lib/modules/heading-anchor/heading-anchor.service.ts @@ -11,43 +11,60 @@ import { SkyDocsHeadingAnchorComponent } from './heading-anchor.component'; @Injectable() export class SkyDocsHeadingAnchorService implements OnDestroy { #anchors: SkyDocsHeadingAnchorComponent[] = []; + #anchorElements = new Map(); readonly #anchorsChange = new BehaviorSubject([]); public readonly anchorsChange = this.#anchorsChange.asObservable(); public ngOnDestroy(): void { this.#anchors = []; + this.#anchorElements.clear(); this.#anchorsChange.complete(); } - public register(anchor: SkyDocsHeadingAnchorComponent): void { + public register( + anchor: SkyDocsHeadingAnchorComponent, + element: Element, + ): void { if (!this.#anchors.includes(anchor)) { this.#anchors.push(anchor); + this.#anchorElements.set(anchor, element); this.#anchorsChange.next(this.#getLinks()); } } public unregister(anchor: SkyDocsHeadingAnchorComponent): void { if (this.#anchors.includes(anchor)) { - this.#anchors.splice(this.#anchors.indexOf(anchor)); + this.#anchors.splice(this.#anchors.indexOf(anchor), 1); + this.#anchorElements.delete(anchor); this.#anchorsChange.next(this.#getLinks()); } } #getLinks(): SkyDocsHeadingAnchorLink[] { - const els = document.querySelectorAll('sky-docs-heading-anchor'); - const anchorsSorted: SkyDocsHeadingAnchorComponent[] = []; - // Since heading anchors can be registered at any point in the lifecycle // of the app, we need to sort the links by their location in the DOM, // rather than the order at which they were registered. - for (const el of els) { - this.#anchors.forEach((anchor) => { - if (anchor.equals(el)) { - anchorsSorted.push(anchor); - } - }); - } + const anchorsSorted = [...this.#anchors].sort((a, b) => { + const elA = this.#anchorElements.get(a); + const elB = this.#anchorElements.get(b); + + if (!elA || !elB) { + return 0; + } + + const position = elA.compareDocumentPosition(elB); + + if (position & Node.DOCUMENT_POSITION_FOLLOWING) { + return -1; + } + + if (position & Node.DOCUMENT_POSITION_PRECEDING) { + return 1; + } + + return 0; + }); return anchorsSorted.map((a) => { return {