diff --git a/adev/shared-docs/components/table-of-contents/table-of-contents.component.html b/adev/shared-docs/components/table-of-contents/table-of-contents.component.html index f5b514649f1..e791dd207dd 100644 --- a/adev/shared-docs/components/table-of-contents/table-of-contents.component.html +++ b/adev/shared-docs/components/table-of-contents/table-of-contents.component.html @@ -6,26 +6,26 @@
ng {commandName(entry, command)}
- {entry.argumentsLabel ? : <>>}
- {entry.hasOptions ? : <>>}
+ {entry.argumentsLabel ? (
+
+ ) : (
+ <>>
+ )}
+ {entry.hasOptions ? (
+
+ ) : (
+ <>>
+ )}
This command has the following sub-commands
+This command has the following sub-commands
-
) : (
- (.*?)<\/code>/g, '$1') // remove
- .replace(/(.*?)<\/strong>/g, '$1') // remove
- .replace(/(.*?)<\/em>/g, '$1') // remove
- .replace(/\s|\//g, '-') // remove spaces and slashes
- .replace(/gt;|lt;/g, '') // remove escaped < and >
- .replace(/\d+;/g, '') // remove HTML entities
- .replace(/[^0-9a-zA-Z\-]/g, ''); // only keep letters, digits & dashes
-};
diff --git a/adev/shared-docs/pipeline/guides/extensions/docs-workflow/docs-step.ts b/adev/shared-docs/pipeline/guides/extensions/docs-workflow/docs-step.ts
index 781fe7550df..36b1fcab641 100644
--- a/adev/shared-docs/pipeline/guides/extensions/docs-workflow/docs-step.ts
+++ b/adev/shared-docs/pipeline/guides/extensions/docs-workflow/docs-step.ts
@@ -7,7 +7,7 @@
*/
import {Token, Tokens, RendererThis, TokenizerThis} from 'marked';
-import {formatHeading, headingRender} from '../../tranformations/heading';
+import {formatHeading} from '../../tranformations/heading';
interface DocsStepToken extends Tokens.Generic {
type: 'docs-step';
diff --git a/adev/shared-docs/services/table-of-contents-loader.service.ts b/adev/shared-docs/services/table-of-contents-loader.service.ts
index 40b76424ff6..281cc1c5c6f 100644
--- a/adev/shared-docs/services/table-of-contents-loader.service.ts
+++ b/adev/shared-docs/services/table-of-contents-loader.service.ts
@@ -49,9 +49,7 @@ export class TableOfContentsLoader {
const updatedTopValues = new Map();
for (const heading of headings) {
- const parentTop = heading.parentElement?.offsetTop ?? 0;
- const top = Math.floor(parentTop + heading.offsetTop - this.toleranceThreshold);
- updatedTopValues.set(heading.id, top);
+ updatedTopValues.set(heading.id, this.calculateTop(heading));
}
this.tableOfContentItems.update((oldItems) => {
diff --git a/adev/shared-docs/styles/_reference.scss b/adev/shared-docs/styles/_reference.scss
new file mode 100644
index 00000000000..ca5360bd574
--- /dev/null
+++ b/adev/shared-docs/styles/_reference.scss
@@ -0,0 +1,410 @@
+@use './anchor' as anchor;
+
+/* Common styles for the API & CLI references */
+@mixin reference-common() {
+ .docs-code {
+ pre {
+ margin-block: 0;
+ }
+ }
+
+ .docs-reference-header {
+ // deprecated markers beside header
+ & ~ .docs-deprecated {
+ margin-block-start: 0.5rem;
+ }
+
+ & > p {
+ color: var(--secondary-contrast);
+ margin-block-start: 0;
+ margin-block-end: 1.5rem;
+ }
+
+ .docs-reference-title {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ padding-block-end: 0;
+ gap: 0.5rem;
+
+ > div {
+ margin-block: 0.67em;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem;
+
+ h1 {
+ margin-block: 0;
+ }
+ }
+
+ a {
+ fill: var(--quinary-contrast);
+ transition: fill 0.3s ease;
+
+ &:hover {
+ fill: var(--primary-contrast);
+ }
+ }
+ }
+
+ .docs-reference-category {
+ color: var(--gray-400);
+ font-size: 0.875rem;
+ font-weight: 500;
+ line-height: 1.4rem;
+ letter-spacing: -0.00875rem;
+ }
+
+ .docs-code {
+ margin-block-end: 1.5rem;
+ }
+ }
+
+ .docs-reference-section-heading {
+ padding-block-start: 3rem;
+
+ a {
+ @include anchor.docs-anchor();
+ color: inherit;
+ }
+ }
+
+ .docs-reference-members {
+ box-sizing: border-box;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+
+ &:not(:first-child) {
+ margin-top: 1rem;
+ }
+
+ .docs-reference-member-card {
+ border: 1px solid var(--senary-contrast);
+ border-radius: 0.25rem;
+ position: relative;
+ transition: border 0.3s ease;
+ pointer-events: none;
+
+ &::before {
+ content: '';
+ inset: -1px;
+ position: absolute;
+ background: transparent;
+ border-radius: 0.35rem;
+ z-index: 0;
+ }
+
+ &:focus {
+ box-shadow: 10px 4px 40px 0 rgba(0, 0, 0, 0.01);
+
+ &::before {
+ background: var(--red-to-pink-to-purple-horizontal-gradient);
+ }
+ }
+
+ > p {
+ padding-inline: 1.25rem;
+ margin-block-end: 0;
+ }
+
+ a {
+ pointer-events: initial;
+ }
+
+ .docs-reference-card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ border-radius: 0.25rem 0.25rem 0 0;
+ background-color: var(--octonary-contrast);
+ position: relative;
+ z-index: 10;
+ padding: 0.7rem 1rem;
+ cursor: pointer;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ transition:
+ background-color 0.3s ease,
+ border 0.3s ease;
+
+ &:focus {
+ outline: none;
+ }
+
+ &:has(+ .docs-reference-card-body:empty) {
+ border-radius: 0.25rem;
+ }
+
+ code {
+ font-size: 0.875rem;
+
+ &:has(pre) {
+ padding: 0;
+ }
+
+ &:not(pre *) {
+ padding: 0 0.3rem;
+ }
+ }
+
+ pre {
+ margin: 0;
+
+ /* Do we have a better alternative ? */
+ overflow: auto;
+ }
+
+ h3 {
+ display: inline-block;
+ font-family: var(--code-font);
+ font-size: 1rem;
+ letter-spacing: -0.025rem;
+ margin: 0;
+ }
+
+ span {
+ font-size: 0.875rem;
+ }
+ }
+
+ .docs-reference-card-body {
+ padding: 0.25rem 1.25rem;
+ background: var(--septenary-contrast);
+ transition: background-color 0.3s ease;
+ color: var(--quaternary-contrast);
+ border-radius: 0 0 0.25rem 0.25rem;
+ position: relative;
+ z-index: 10;
+
+ &:empty {
+ display: none;
+ }
+
+ &:first-child {
+ border-radius: 0.25rem;
+ }
+
+ hr {
+ margin-block: 2rem;
+ }
+
+ .docs-code {
+ margin-block-end: 1rem;
+ }
+
+ .docs-deprecation-message {
+ border-block-end: 1px solid var(--senary-contrast);
+
+ .docs-deprecated {
+ color: var(--page-background);
+ background-color: var(--quaternary-contrast);
+ width: max-content;
+ border-radius: 0.25rem;
+ padding: 0.1rem 0.25rem;
+ margin-block-start: 1rem;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* API reference styles */
+@mixin api-reference {
+ // API section styles
+ .docs-reference-api-section {
+ .docs-code {
+ box-sizing: border-box;
+ width: 100%;
+ overflow: hidden;
+ padding: 0;
+
+ button {
+ transition: background-color 0.3s ease;
+
+ &.shiki-ln-line-highlighted {
+ background-color: var(--senary-contrast);
+ }
+
+ &:hover {
+ background-color: var(--septenary-contrast);
+ }
+
+ &:focus {
+ background-color: var(--senary-contrast);
+
+ span {
+ background-color: inherit;
+ }
+ }
+ }
+
+ // Hide copy source code button
+ button[docs-copy-source-code] {
+ display: none;
+ }
+ }
+
+ code {
+ margin-block: 0;
+ }
+
+ pre {
+ white-space: pre;
+ overflow-x: auto;
+ margin: 0;
+ }
+ }
+
+ // "API member card"-specific styles
+ .docs-reference-member-card {
+ .docs-reference-card-item {
+ // When it's not the only card ...
+ &:has(~ .docs-reference-card-item) {
+ border: 1px solid var(--senary-contrast);
+ margin-block: 1rem;
+ border-radius: 0.25rem;
+ padding-inline: 1rem;
+ }
+
+ // & the last card
+ &:last-child:not(:first-of-type) {
+ border: 1px solid var(--senary-contrast);
+ margin-block: 1rem;
+ border-radius: 0.25rem;
+ padding-inline: 1rem;
+ }
+
+ span {
+ display: inline-block;
+ font-size: 0.875rem;
+ }
+
+ code {
+ font-size: 0.875rem;
+ }
+
+ .docs-function-definition:has(*) {
+ border-block-end: 1px solid var(--senary-contrast);
+ }
+
+ .docs-param-group {
+ margin-block-start: 1rem;
+
+ // If it's the only param group...
+ &:not(:has(~ .docs-param-group)) {
+ margin-block: 1rem;
+ }
+
+ .docs-param-name {
+ color: var(--vivid-pink);
+ font-family: var(--code-font);
+ margin-inline-end: 0.25rem;
+
+ &::after {
+ content: ':';
+ }
+ }
+
+ .docs-parameter-description {
+ p:first-child {
+ margin-block-start: 0;
+ }
+ }
+ }
+
+ .docs-param-keyword {
+ color: var(--primary-contrast);
+ font-family: var(--code-font);
+ margin-inline-end: 0.5rem;
+ }
+
+ .docs-return-type {
+ padding-block: 1rem;
+
+ // & does not follow a function definition
+ &:not(.docs-function-definition + .docs-return-type) {
+ border-block-start: 1px solid var(--senary-contrast);
+ }
+ }
+ }
+ }
+}
+
+/* CLI reference styles */
+@mixin cli-reference {
+ // CLI TOC
+ .docs-reference-cli-toc {
+ margin-bottom: 1rem;
+
+ .shiki-ln-line-argument,
+ .shiki-ln-line-option {
+ padding: 0.1rem 0.2rem 0.2rem;
+ margin-inline: 0.1rem;
+ color: var(--quaternary-contrast);
+ background: transparent;
+ border-radius: 0.25rem;
+ position: relative;
+ transition:
+ color 0.3s ease,
+ background 0.3s ease,
+ border 0.3s ease;
+
+ &:hover {
+ color: var(--primary-contrast);
+ background: var(--septenary-contrast);
+ }
+
+ &.shiki-ln-line-highlighted {
+ color: var(--primary-contrast);
+ background: var(--senary-contrast);
+ }
+ }
+
+ .shiki-ln-line-argument {
+ margin-inline-start: 0.2rem;
+ }
+ }
+
+ .docs-reference-members {
+ .docs-reference-section-heading {
+ margin: 0;
+ }
+
+ // "CLI member card"-specific styles
+ .docs-reference-member-card {
+ .docs-ref-content {
+ padding: 1rem 0;
+
+ &:not(:first-child) {
+ border-block-start: 1px solid var(--senary-contrast);
+ }
+
+ .docs-reference-type-and-default {
+ width: 4.375rem;
+ flex-shrink: 0;
+
+ span {
+ display: block;
+ font-size: 0.875rem;
+ margin-block-end: 0.2rem;
+ white-space: nowrap;
+
+ &:not(:first-child) {
+ margin-block-start: 1rem;
+ }
+ }
+
+ code {
+ font-size: 0.775rem;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.html b/adev/src/app/features/references/api-items-section/api-items-section.component.html
index 45c2a24f5b8..52578f6aecb 100644
--- a/adev/src/app/features/references/api-items-section/api-items-section.component.html
+++ b/adev/src/app/features/references/api-items-section/api-items-section.component.html
@@ -29,7 +29,7 @@
{{ apiItem.title }}
@if (apiItem.isDeprecated) {
- <!>
+ <!>
}
}
diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.scss b/adev/src/app/features/references/api-items-section/api-items-section.component.scss
index 6790c0c65f4..a13c428ce45 100644
--- a/adev/src/app/features/references/api-items-section/api-items-section.component.scss
+++ b/adev/src/app/features/references/api-items-section/api-items-section.component.scss
@@ -79,7 +79,7 @@
gap: 1em;
}
-.docs-deprecated {
+.adev-deprecated {
font-family: var(--code-font);
background-color: var(--senary-contrast);
color: var(--tertiary-contrast);
diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts b/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts
index 7a79182ecfc..ddb4a9fba95 100644
--- a/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts
+++ b/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts
@@ -10,7 +10,6 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
import ApiItemsSection from './api-items-section.component';
import {ApiItemsGroup} from '../interfaces/api-items-group';
-import {ApiReferenceManager} from '../api-reference-list/api-reference-manager.service';
import {ApiItemType} from '../interfaces/api-item-type';
import {provideRouter} from '@angular/router';
import {By} from '@angular/platform-browser';
@@ -60,7 +59,7 @@ describe('ApiItemsSection', () => {
fixture.detectChanges();
const deprecatedApiIcons = fixture.debugElement.queryAll(
- By.css('.adev-api-items-section-grid li .docs-deprecated'),
+ By.css('.adev-api-items-section-grid li .adev-deprecated'),
);
const deprecatedApiTitle = deprecatedApiIcons[0].parent?.query(By.css('.adev-item-title'));
diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html
index d61fcf0aa8c..8c5e47f8de6 100644
--- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html
+++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html
@@ -1,30 +1,9 @@
-
-
-
-
- @for (tab of tabs(); track tab.url) {
-
-
-
-
-
- }
-
-
-
-@if (isApiTabActive()) {
+@if (docContent(); as docContent) {
-
+ [docContent]="docContent.contents"
+ [hasToc]="true"
+ (contentLoaded)="onContentLoaded()"
+ />
}
Jump to details
diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss
index a2ac3ac7802..3d475c60b4f 100644
--- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss
+++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss
@@ -1,11 +1,23 @@
@use '@angular/docs/styles/media-queries' as mq;
+@use '@angular/docs/styles/reference' as ref;
:host {
- display: flex;
- gap: 1rem;
+ display: block;
width: 100%;
+ max-width: var(--page-width);
+ padding: var(--layout-padding) 0 1rem var(--layout-padding);
box-sizing: border-box;
- flex-direction: column;
+
+ @include mq.for-desktop-down {
+ padding: var(--layout-padding);
+ max-width: none;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background-color: var(--septenary-contrast);
+ border-radius: 10px;
+ transition: background-color 0.3s ease;
+ }
h1 {
font-size: 1.5rem;
@@ -27,391 +39,7 @@
}
}
-// stylelint-disable-next-line
::ng-deep {
- .adev-header-and-tabs {
- padding: var(--layout-padding) 0 1rem var(--layout-padding);
- box-sizing: border-box;
- width: 100%;
- max-width: var(--page-width);
-
- @include mq.for-desktop-down {
- padding: var(--layout-padding);
- max-width: none;
- }
-
- &::-webkit-scrollbar-thumb {
- background-color: var(--septenary-contrast);
- border-radius: 10px;
- transition: background-color 0.3s ease;
- }
- }
-
- .docs-code {
- pre {
- margin-block: 0;
- }
- }
-
- .docs-reference-header {
- > p {
- color: var(--secondary-contrast);
- margin-block-start: 0;
- margin-block-end: 1.5rem;
- }
-
- .docs-code {
- margin-block-end: 1.5rem;
- }
- }
-
- .adev-reference-tab-body {
- margin-block-start: 1.5rem;
- docs-viewer > div {
- :first-child {
- margin-top: 0;
- }
- }
- }
-
- .docs-reference-api-tab {
- display: flex;
- gap: 1.81rem;
- align-items: flex-start;
- margin-bottom: 1px;
-
- @include mq.for-desktop-down {
- flex-direction: column;
- }
-
- & > .docs-code {
- box-sizing: border-box;
- width: 100%;
- overflow: hidden;
- padding: 0;
-
- @include mq.for-desktop-down {
- width: 100%;
- position: static;
- }
-
- button {
- transition: background-color 0.3s ease;
-
- &.shiki-ln-line-highlighted {
- background-color: var(--senary-contrast);
- }
- &:hover {
- background-color: var(--septenary-contrast);
- }
- &:focus {
- background-color: var(--senary-contrast);
- }
- }
-
- // Hide copy source code button
- button[docs-copy-source-code] {
- display: none;
- }
- }
-
- code {
- margin-block: 0;
- }
-
- pre {
- white-space: pre;
- overflow-x: auto;
- margin: 0;
- }
- }
-
- .docs-reference-cli-toc {
- margin-bottom: 1rem;
- }
-
- .adev-reference-tab {
- min-width: 50ch;
- margin-block-start: 2.5rem;
- }
-
- .docs-reference-members-container {
- width: 40%;
- box-sizing: border-box;
- width: 100%;
- max-width: var(--page-width);
- padding: 0 0 1rem var(--layout-padding);
-
- @include mq.for-desktop-down {
- padding: var(--layout-padding);
- padding-top: 0;
- max-width: none;
- }
- }
-
- // Sidebar
- .docs-reference-members {
- display: flex;
- flex-direction: column;
- gap: 20px;
-
- @include mq.for-desktop-down {
- width: 100%;
- }
- }
-
- .docs-reference-title {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: space-between;
- padding-block-end: 0;
- gap: 0.5rem;
-
- > div {
- margin-block: 0.67em;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 0.5rem;
-
- h1 {
- margin-block: 0;
- }
- }
-
- a {
- fill: var(--quinary-contrast);
- transition: fill 0.3s ease;
-
- &:hover {
- fill: var(--primary-contrast);
- }
- }
- }
-
- .adev-reference-labels {
- display: flex;
- gap: 0.5rem;
- }
-
- .docs-reference-category {
- color: var(--gray-400);
- font-size: 0.875rem;
- font-weight: 500;
- line-height: 1.4rem;
- letter-spacing: -0.00875rem;
- }
-
- .docs-reference-card-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 0.5rem;
- flex-wrap: wrap;
-
- padding: 0.7rem 1rem;
-
- code:not(pre *) {
- padding: 0 0.3rem;
- }
- }
-
- .docs-reference-member-card {
- border: 1px solid var(--senary-contrast);
- border-radius: 0.25rem;
- position: relative;
- transition: border 0.3s ease;
-
- &::before {
- content: '';
- inset: -1px;
- position: absolute;
- background: transparent;
- border-radius: 0.35rem;
- z-index: 0;
- }
-
- &:focus {
- box-shadow: 10px 4px 40px 0 rgba(0, 0, 0, 0.01);
-
- &::before {
- background: var(--red-to-pink-to-purple-horizontal-gradient);
- }
- }
-
- header {
- display: flex;
- flex-direction: column;
- border-radius: 0.25rem 0.25rem 0 0;
- background-color: var(--octonary-contrast);
- position: relative;
- z-index: 10;
- cursor: pointer;
- transition:
- background-color 0.3s ease,
- border 0.3s ease;
-
- & > code {
- max-width: 100%;
- }
-
- code:has(pre) {
- padding: 0;
- }
-
- pre {
- margin: 0;
-
- /* Do we have a better alternative ? */
- overflow: auto;
- }
- }
-
- .docs-reference-card-header {
- h3 {
- display: inline-block;
- font-family: var(--code-font);
- font-size: 1rem;
- letter-spacing: -0.025rem;
- margin: 0;
- max-width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- code,
- span {
- font-size: 0.875rem;
- }
- }
-
- > p {
- padding-inline: 1.25rem;
- margin-block-end: 0;
- }
- }
-
- .docs-reference-card-body {
- padding: 0.25rem 1.25rem;
- background: var(--septenary-contrast);
- transition: background-color 0.3s ease;
- color: var(--quaternary-contrast);
- border-radius: 0 0 0.25rem 0.25rem;
- position: relative;
- z-index: 10;
- hr {
- margin-block: 2rem;
- }
- .docs-code {
- margin-block-end: 1rem;
- }
-
- &:empty {
- display: none;
- }
- }
-
- // when it's not the only card...
- .docs-reference-card-item:has(~ .docs-reference-card-item) {
- border: 1px solid var(--senary-contrast);
- margin-block: 1rem;
- border-radius: 0.25rem;
- padding-inline: 1rem;
- }
- // & the last card
- .docs-reference-card-item:last-child {
- &:not(:first-of-type) {
- border: 1px solid var(--senary-contrast);
- margin-block: 1rem;
- border-radius: 0.25rem;
- padding-inline: 1rem;
- }
- }
-
- .docs-reference-card-item {
- span {
- display: inline-block;
- font-size: 0.875rem;
- }
- code {
- font-size: 0.875rem;
- }
- }
-
- .docs-function-definition {
- &:has(*) {
- border-block-end: 1px solid var(--senary-contrast);
- }
- }
-
- .docs-deprecation-message {
- border-block-end: 1px solid var(--senary-contrast);
- }
-
- .docs-param-group {
- margin-block-start: 1rem;
- }
-
- // If it's the only param group...
- .docs-param-group:not(:has(~ .docs-param-group)) {
- margin-block: 1rem;
- }
-
- .docs-return-type {
- padding-block: 1rem;
-
- // & does not follow a function definition
- &:not(.docs-function-definition + .docs-return-type) {
- border-block-start: 1px solid var(--senary-contrast);
- }
- }
-
- .docs-param-keyword {
- color: var(--primary-contrast);
- font-family: var(--code-font);
- margin-inline-end: 0.5rem;
- }
-
- .docs-param-name {
- color: var(--vivid-pink);
- font-family: var(--code-font);
- margin-inline-end: 0.25rem;
- &::after {
- content: ':';
- }
- }
-
- .docs-deprecated {
- color: var(--page-background);
- background-color: var(--quaternary-contrast);
- width: max-content;
- border-radius: 0.25rem;
- padding: 0.1rem 0.25rem;
- margin-block-start: 1rem;
- }
-
- // deprecated markers beside header
- .docs-reference-header ~ .docs-deprecated {
- margin-block-start: 0.5rem;
- }
-
- .docs-parameter-description {
- p:first-child {
- margin-block-start: 0;
- }
- }
-
- .docs-ref-content {
- padding: 1rem 0;
-
- &:not(:first-child) {
- border-block-start: 1px solid var(--senary-contrast);
- }
-
- .docs-param-keyword {
- display: block;
- margin: 0 0 0.5rem 0;
- }
- }
+ @include ref.reference-common();
+ @include ref.api-reference();
}
diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts
index 7bc5a6a40bc..18c9830c022 100644
--- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts
+++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts
@@ -6,37 +6,29 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {HarnessLoader} from '@angular/cdk/testing';
-import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
-import {TestBed} from '@angular/core/testing';
-import {MatTabGroupHarness} from '@angular/material/tabs/testing';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
import {provideNoopAnimations} from '@angular/platform-browser/animations';
import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
-import {signal} from '@angular/core';
import {provideRouter, withComponentInputBinding} from '@angular/router';
import {RouterTestingHarness} from '@angular/router/testing';
import ApiReferenceDetailsPage from './api-reference-details-page.component';
-import {By} from '@angular/platform-browser';
describe('ApiReferenceDetailsPage', () => {
let component: ApiReferenceDetailsPage;
- let loader: HarnessLoader;
- let harness: RouterTestingHarness;
+ let fixture: ComponentFixture;
let fakeApiReferenceScrollHandler = {
setupListeners: () => {},
- membersMarginTopInPx: signal(10),
- updateMembersMarginTop: () => {},
};
- const SAMPLE_CONTENT_WITH_TABS = `
-
-
-
-
-
-`;
+ const SAMPLE_CONTENT_WITH_SECTIONS = `
+ API
+
+ Description
+ Examples
+ Usage Notes
+ `;
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -51,7 +43,7 @@ describe('ApiReferenceDetailsPage', () => {
data: {
'docContent': {
id: 'id',
- contents: SAMPLE_CONTENT_WITH_TABS,
+ contents: SAMPLE_CONTENT_WITH_SECTIONS,
},
},
},
@@ -61,10 +53,9 @@ describe('ApiReferenceDetailsPage', () => {
],
});
TestBed.overrideProvider(ReferenceScrollHandler, {useValue: fakeApiReferenceScrollHandler});
- harness = await RouterTestingHarness.create();
- const {fixture} = harness;
+ const harness = await RouterTestingHarness.create();
+ fixture = harness.fixture;
component = await harness.navigateByUrl('/', ApiReferenceDetailsPage);
- loader = TestbedHarnessEnvironment.loader(fixture);
fixture.detectChanges();
});
@@ -72,39 +63,10 @@ describe('ApiReferenceDetailsPage', () => {
expect(component).toBeTruthy();
});
- it('should render tabs for all elements with tab attribute', async () => {
- const matTabGroup = await loader.getHarness(MatTabGroupHarness);
+ it('should load the doc content', () => {
+ expect(component.docContent()?.contents).toBeTruthy();
- const tabs = await matTabGroup.getTabs();
-
- expect(tabs.length).toBe(4);
- });
-
- it('should display members cards when API tab is active', async () => {
- const matTabGroup = await loader.getHarness(MatTabGroupHarness);
- const tabs = await matTabGroup.getTabs();
-
- let membersCard = harness.fixture.debugElement.query(
- By.css('.docs-reference-members-container'),
- );
- expect(membersCard).toBeTruthy();
-
- await matTabGroup.selectTab({label: await tabs[1].getLabel()});
-
- membersCard = harness.fixture.debugElement.query(By.css('.docs-reference-members-container'));
- expect(membersCard).toBeFalsy();
-
- await matTabGroup.selectTab({label: await tabs[0].getLabel()});
-
- membersCard = harness.fixture.debugElement.query(By.css('.docs-reference-members-container'));
- expect(membersCard).toBeTruthy();
- });
-
- it('should setup scroll listeners when API members are loaded', () => {
- const setupListenersSpy = spyOn(fakeApiReferenceScrollHandler, 'setupListeners');
-
- component.membersCardsLoaded();
-
- expect(setupListenersSpy).toHaveBeenCalled();
+ const docsViewer = fixture.nativeElement.querySelector('docs-viewer');
+ expect(docsViewer).toBeTruthy();
});
});
diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts
index 6cd36d3d1a1..e37635f6290 100644
--- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts
+++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts
@@ -6,103 +6,50 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {ChangeDetectionStrategy, Component, inject, input, computed} from '@angular/core';
-import {DOCUMENT} from '@angular/common';
-import {MatTabsModule} from '@angular/material/tabs';
+import {ChangeDetectionStrategy, Component, inject, input} from '@angular/core';
import {DocContent, DocViewer} from '@angular/docs';
-import {ActivatedRoute, Router} from '@angular/router';
-import {ApiItemType} from './../interfaces/api-item-type';
+import {ActivatedRoute} from '@angular/router';
+import {DOCUMENT} from '@angular/common';
import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
-import {
- API_REFERENCE_DETAILS_PAGE_HEADER_CLASS_NAME,
- API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME,
- API_REFERENCE_TAB_ATTRIBUTE,
- API_REFERENCE_TAB_API_LABEL,
- API_TAB_CLASS_NAME,
- API_REFERENCE_TAB_URL_ATTRIBUTE,
-} from '../constants/api-reference-prerender.constants';
+import {API_SECTION_CLASS_NAME} from '../constants/api-reference-prerender.constants';
@Component({
selector: 'adev-reference-page',
- imports: [DocViewer, MatTabsModule],
+ standalone: true,
+ imports: [DocViewer],
templateUrl: './api-reference-details-page.component.html',
styleUrls: ['./api-reference-details-page.component.scss'],
providers: [ReferenceScrollHandler],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export default class ApiReferenceDetailsPage {
- private readonly activatedRoute = inject(ActivatedRoute);
+ private readonly referenceScrollHandler = inject(ReferenceScrollHandler);
+ private readonly route = inject(ActivatedRoute);
private readonly document = inject(DOCUMENT);
- private readonly router = inject(Router);
- private readonly scrollHandler = inject(ReferenceScrollHandler);
docContent = input();
- tab = input();
- // aliases
- ApiItemType = ApiItemType;
-
- // computed state
- parsedDocContent = computed(() => {
- // TODO: pull this logic outside of a computed where it can be tested etc.
- const docContent = this.docContent();
-
- if (docContent === undefined) {
- return {
- header: undefined,
- members: undefined,
- tabs: [],
- };
- }
-
- const element = this.document.createElement('div');
- element.innerHTML = docContent.contents;
-
- // Get the innerHTML of the header element from received document.
- const header = element.querySelector(API_REFERENCE_DETAILS_PAGE_HEADER_CLASS_NAME);
- // Get the innerHTML of the card elements from received document.
- const members = element.querySelector(API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME);
-
- // Get the tab elements from received document.
- // We're expecting that tab element will contain `tab` attribute.
- const tabs = Array.from(element.querySelectorAll(`[${API_REFERENCE_TAB_ATTRIBUTE}]`)).map(
- (tab) => ({
- url: tab.getAttribute(API_REFERENCE_TAB_URL_ATTRIBUTE)!,
- title: tab.getAttribute(API_REFERENCE_TAB_ATTRIBUTE)!,
- content: tab.innerHTML,
- }),
- );
-
- element.remove();
-
- return {
- header: header?.innerHTML,
- members: members?.innerHTML,
- tabs,
- };
- });
-
- tabs = () => this.parsedDocContent().tabs;
-
- selectedTabIndex = computed(() => {
- const existingTabIdx = this.tabs().findIndex((tab) => tab.url === this.tab());
- return Math.max(existingTabIdx, 0);
- });
-
- isApiTabActive = computed(() => {
- const activeTabTitle = this.tabs()[this.selectedTabIndex()]?.title;
- return activeTabTitle === API_REFERENCE_TAB_API_LABEL || activeTabTitle === 'CLI';
- });
-
- membersCardsLoaded(): void {
- this.scrollHandler.setupListeners(API_TAB_CLASS_NAME);
+ onContentLoaded() {
+ this.referenceScrollHandler.setupListeners(API_SECTION_CLASS_NAME);
+ this.scrollToSectionLegacy();
}
- tabChange(tabIndex: number) {
- this.router.navigate([], {
- relativeTo: this.activatedRoute,
- queryParams: {tab: this.tabs()[tabIndex].url},
- queryParamsHandling: 'merge',
- });
+ /** Handle legacy URLs with a `tab` query param from the old tab layout */
+ private scrollToSectionLegacy() {
+ const params = this.route.snapshot.queryParams;
+ const tab = params['tab'] as string | undefined;
+
+ if (tab) {
+ const section = this.document.getElementById(tab);
+
+ if (section) {
+ // `scrollIntoView` is ignored even, if the element exists.
+ // It seems that it's related to: https://issues.chromium.org/issues/40715316
+ // Hence, the usage of `setTimeout`.
+ setTimeout(() => {
+ section.scrollIntoView({behavior: 'smooth'});
+ }, 100);
+ }
+ }
}
}
diff --git a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.html b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.html
index 4345ce9f1e8..b9ba87546e1 100644
--- a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.html
+++ b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.html
@@ -1,11 +1,5 @@
-
-
-
-
-
+@if (docContent(); as docContent) {
+
+}
Jump to details
diff --git a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss
index e1d1fcc38e8..9d356ce0c80 100644
--- a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss
+++ b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss
@@ -1,123 +1,21 @@
@use '@angular/docs/styles/media-queries' as mq;
-// Note: cli-reference-details-page is receiving page styles
-// from api-reference-details-page.component.scss
+@use '@angular/docs/styles/reference' as ref;
+
+:host {
+ display: block;
+ width: 100%;
+ max-width: var(--page-width);
+ padding: var(--layout-padding) 0 1rem var(--layout-padding);
+ box-sizing: border-box;
+
+ @include mq.for-desktop-down {
+ padding: var(--layout-padding);
+ max-width: none;
+ }
+}
// stylelint-disable-next-line
::ng-deep {
- .adev-ref-content {
- display: flex;
- padding-block: 1rem;
- gap: 1rem;
- &:not(:last-of-type) {
- border-block-end: 1px solid var(--senary-contrast);
- }
- }
-
- .adev-header-and-tabs {
- &.adev-cli-content {
- width: 100%;
- max-width: var(--page-width);
-
- @include mq.for-desktop-down {
- max-width: none;
- }
- }
- }
-
- .adev-cli-members-container {
- padding: 0 0 var(--layout-padding) var(--layout-padding);
- padding-bottom: 1rem;
- box-sizing: border-box;
- max-width: var(--page-width);
-
- @include mq.for-desktop-down {
- width: 100%;
- padding: var(--layout-padding);
- padding-top: 0;
- max-width: none;
- }
- }
-
- .adev-ref-option-and-description {
- flex-grow: 1;
- max-width: calc(100% - 80px);
- p {
- margin-block-end: 0;
- }
- }
-
- .docs-reference-type-and-default {
- width: 4.375rem;
- flex-shrink: 0;
- span {
- display: block;
- font-size: 0.875rem;
- margin-block-end: 0.2rem;
- white-space: nowrap;
-
- &:not(:first-child) {
- margin-block-start: 1rem;
- }
- }
-
- code {
- font-size: 0.775rem;
- }
- }
-
- .adev-reference-cli-toc {
- border: 1px solid var(--senary-contrast);
- border-radius: 0.3rem;
- position: relative;
- transition: border 0.3s ease;
-
- &::before {
- content: '';
- inset: -1px;
- position: absolute;
- background: transparent;
- border-radius: 0.35rem;
- z-index: 0;
- }
-
- &:has(.shiki-ln-line-highlighted) {
- &::before {
- background: var(--red-to-pink-to-purple-horizontal-gradient);
- }
- }
-
- pre {
- border-radius: 0.25rem;
- position: relative;
- z-index: 100;
- background: var(--octonary-contrast);
- }
- }
-
- .shiki-ln-line-argument,
- .shiki-ln-line-option {
- padding: 0.1rem 0.2rem 0.2rem;
- margin-inline: 0.1rem;
- color: var(--quaternary-contrast);
- background: transparent;
- border-radius: 0.25rem;
- position: relative;
- transition:
- color 0.3s ease,
- background 0.3s ease,
- border 0.3s ease;
-
- &:hover {
- color: var(--primary-contrast);
- background: var(--septenary-contrast);
- }
-
- &.shiki-ln-line-highlighted {
- color: var(--primary-contrast);
- background: var(--senary-contrast);
- }
- }
- .shiki-ln-line-argument {
- margin-inline-start: 0.2rem;
- }
+ @include ref.reference-common();
+ @include ref.cli-reference();
}
diff --git a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.spec.ts b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.spec.ts
index a0e7a100511..fc7e14c9438 100644
--- a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.spec.ts
+++ b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.spec.ts
@@ -6,23 +6,20 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {TestBed} from '@angular/core/testing';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
import CliReferenceDetailsPage from './cli-reference-details-page.component';
-import {RouterTestingHarness, RouterTestingModule} from '@angular/router/testing';
-import {signal} from '@angular/core';
+import {RouterTestingHarness} from '@angular/router/testing';
import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
-import {provideRouter} from '@angular/router';
-import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
+import {provideRouter, withComponentInputBinding} from '@angular/router';
+import {provideNoopAnimations} from '@angular/platform-browser/animations';
describe('CliReferenceDetailsPage', () => {
let component: CliReferenceDetailsPage;
- let harness: RouterTestingHarness;
+ let fixture: ComponentFixture;
let fakeApiReferenceScrollHandler = {
setupListeners: () => {},
- membersMarginTopInPx: signal(0),
- updateMembersMarginTop: () => {},
};
const SAMPLE_CONTENT = `
@@ -34,33 +31,42 @@ describe('CliReferenceDetailsPage', () => {
beforeEach(async () => {
TestBed.configureTestingModule({
- imports: [CliReferenceDetailsPage, RouterTestingModule],
+ imports: [CliReferenceDetailsPage],
providers: [
- provideRouter([
- {
- path: '**',
- component: CliReferenceDetailsPage,
- data: {
- 'docContent': {
- id: 'id',
- contents: SAMPLE_CONTENT,
+ provideNoopAnimations(),
+ provideRouter(
+ [
+ {
+ path: '**',
+ component: CliReferenceDetailsPage,
+ data: {
+ 'docContent': {
+ id: 'id',
+ contents: SAMPLE_CONTENT,
+ },
},
},
- },
- ]),
+ ],
+ withComponentInputBinding(),
+ ),
],
});
TestBed.overrideProvider(ReferenceScrollHandler, {useValue: fakeApiReferenceScrollHandler});
- harness = await RouterTestingHarness.create();
- const {fixture} = harness;
+ const harness = await RouterTestingHarness.create();
+ fixture = harness.fixture;
component = await harness.navigateByUrl('/', CliReferenceDetailsPage);
- TestbedHarnessEnvironment.loader(fixture);
fixture.detectChanges();
});
- it('should set content on init', () => {
- expect(component.mainContentInnerHtml()).toBe('First column content');
- expect(component.cardsInnerHtml()).toBe('Members content');
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should load the doc content', () => {
+ expect(component.docContent()?.contents).toBeTruthy();
+
+ const docsViewer = fixture.nativeElement.querySelector('docs-viewer');
+ expect(docsViewer).toBeTruthy();
});
});
diff --git a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts
index 75414c36164..ebbadfe7b36 100644
--- a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts
+++ b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts
@@ -6,81 +6,16 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {DOCUMENT} from '@angular/common';
-import {
- ChangeDetectionStrategy,
- Component,
- DestroyRef,
- OnInit,
- inject,
- signal,
-} from '@angular/core';
-import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
+import {ChangeDetectionStrategy, Component, input} from '@angular/core';
import {DocContent, DocViewer} from '@angular/docs';
-import {ActivatedRoute} from '@angular/router';
-import {map} from 'rxjs/operators';
-import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
-import {API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME} from '../constants/api-reference-prerender.constants';
-
-export const CLI_MAIN_CONTENT_SELECTOR = '.docs-reference-cli-content';
-export const CLI_TOC = '.adev-reference-cli-toc';
@Component({
selector: 'adev-cli-reference-page',
imports: [DocViewer],
templateUrl: './cli-reference-details-page.component.html',
- styleUrls: [
- './cli-reference-details-page.component.scss',
- '../api-reference-details-page/api-reference-details-page.component.scss',
- ],
- providers: [ReferenceScrollHandler],
+ styleUrls: ['./cli-reference-details-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export default class CliReferenceDetailsPage implements OnInit {
- private readonly activatedRoute = inject(ActivatedRoute);
- private readonly destroyRef = inject(DestroyRef);
- private readonly document = inject(DOCUMENT);
- private readonly scrollHandler = inject(ReferenceScrollHandler);
-
- cardsInnerHtml = signal('');
- mainContentInnerHtml = signal('');
-
- ngOnInit(): void {
- this.setPageContent();
- }
-
- contentLoaded(): void {
- this.scrollHandler.setupListeners(CLI_TOC);
- }
-
- // Fetch the content for CLI Reference page based on the active route.
- private setPageContent(): void {
- this.activatedRoute.data
- .pipe(
- map((data) => data['docContent']),
- takeUntilDestroyed(this.destroyRef),
- )
- .subscribe((doc: DocContent | undefined) => {
- this.setContentForPageSections(doc);
- });
- }
-
- private setContentForPageSections(doc: DocContent | undefined) {
- const element = this.document.createElement('div');
- element.innerHTML = doc?.contents!;
-
- // Get the innerHTML of the main content from received document.
- const mainContent = element.querySelector(CLI_MAIN_CONTENT_SELECTOR);
- if (mainContent) {
- this.mainContentInnerHtml.set(mainContent.innerHTML);
- }
-
- // Get the innerHTML of the cards from received document.
- const cards = element.querySelector(API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME);
- if (cards) {
- this.cardsInnerHtml.set(cards.innerHTML);
- }
-
- element.remove();
- }
+export default class CliReferenceDetailsPage {
+ docContent = input();
}
diff --git a/adev/src/app/features/references/constants/api-reference-prerender.constants.ts b/adev/src/app/features/references/constants/api-reference-prerender.constants.ts
index f0a9dd731e2..84cc97814b9 100644
--- a/adev/src/app/features/references/constants/api-reference-prerender.constants.ts
+++ b/adev/src/app/features/references/constants/api-reference-prerender.constants.ts
@@ -6,10 +6,5 @@
* found in the LICENSE file at https://angular.dev/license
*/
-export const API_REFERENCE_DETAILS_PAGE_HEADER_CLASS_NAME = '.docs-reference-header';
-export const API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME = '.docs-reference-members-container';
-export const API_REFERENCE_TAB_ATTRIBUTE = 'data-tab';
-export const API_REFERENCE_TAB_URL_ATTRIBUTE = 'data-tab-url';
-export const API_REFERENCE_TAB_API_LABEL = 'API';
-export const API_TAB_CLASS_NAME = '.docs-reference-api-tab';
+export const API_SECTION_CLASS_NAME = 'docs-reference-api-section';
export const MEMBER_ID_ATTRIBUTE = 'member-id';
diff --git a/adev/src/app/features/references/services/reference-scroll-handler.service.ts b/adev/src/app/features/references/services/reference-scroll-handler.service.ts
index b479de08d43..f8df1204726 100644
--- a/adev/src/app/features/references/services/reference-scroll-handler.service.ts
+++ b/adev/src/app/features/references/services/reference-scroll-handler.service.ts
@@ -7,55 +7,29 @@
*/
import {DOCUMENT, isPlatformBrowser} from '@angular/common';
-import {DestroyRef, Injectable, Injector, PLATFORM_ID, inject} from '@angular/core';
+import {DestroyRef, Injectable, PLATFORM_ID, inject} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {fromEvent} from 'rxjs';
import {MEMBER_ID_ATTRIBUTE} from '../constants/api-reference-prerender.constants';
-import {WINDOW} from '@angular/docs';
import {Router} from '@angular/router';
-import {AppScroller} from '../../../app-scroller';
-
-// Adds some space/margin between the top of the target element and the top of viewport.
-const SCROLL_MARGIN_TOP = 100;
@Injectable()
export class ReferenceScrollHandler {
private readonly destroyRef = inject(DestroyRef);
private readonly document = inject(DOCUMENT);
- private readonly injector = inject(Injector);
- private readonly window = inject(WINDOW);
private readonly router = inject(Router);
- private readonly appScroller = inject(AppScroller);
private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
- setupListeners(tocSelector: string): void {
+ setupListeners(tocClass: string): void {
if (!this.isBrowser) {
return;
}
- this.setupCodeToCListeners(tocSelector);
- this.setupFragmentChangeListener();
+ this.setupCodeToCListeners(tocClass);
}
- private setupFragmentChangeListener() {
- this.router.routerState.root.fragment
- .pipe(takeUntilDestroyed(this.destroyRef))
- .subscribe((fragment) => {
- // If there is no fragment or the scroll event has a position (traversing through history),
- // allow the scroller to handle scrolling instead of going to the fragment
- if (!fragment || this.appScroller.lastScrollEvent?.position) {
- this.appScroller.scroll(this.injector);
- return;
- }
-
- const card = this.document.getElementById(fragment) as HTMLDivElement | null;
- card?.focus();
- this.scrollToCard(card);
- });
- }
-
- private setupCodeToCListeners(tocSelector: string): void {
- const tocContainer = this.document.querySelector(tocSelector);
+ private setupCodeToCListeners(tocClass: string): void {
+ const tocContainer = this.document.querySelector(`.${tocClass}`);
if (!tocContainer) {
return;
@@ -82,21 +56,6 @@ export class ReferenceScrollHandler {
});
}
- private scrollToCard(card: HTMLDivElement | null): void {
- if (!card) {
- return;
- }
-
- if (card !== document.activeElement) {
- (document.activeElement).blur();
- }
-
- this.window.scrollTo({
- top: card!.offsetTop - SCROLL_MARGIN_TOP,
- behavior: 'smooth',
- });
- }
-
private getMemberId(lineButton: HTMLButtonElement | null): string | undefined {
if (!lineButton) {
return undefined;