diff --git a/adev/angular.json b/adev/angular.json index 2b35f6f1c46..37dd4969837 100644 --- a/adev/angular.json +++ b/adev/angular.json @@ -105,36 +105,6 @@ } } } - }, - "shared": { - "projectType": "library", - "root": "projects/shared", - "sourceRoot": "projects/shared/src", - "prefix": "lib", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "project": "projects/shared/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "projects/shared/tsconfig.lib.prod.json" - }, - "development": { - "tsConfig": "projects/shared/tsconfig.lib.json" - } - }, - "defaultConfiguration": "production" - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "tsConfig": "projects/shared/tsconfig.spec.json", - "polyfills": ["zone.js", "zone.js/testing"] - } - } - } } }, "cli": { diff --git a/adev/prerender/checks/check-links.ts b/adev/prerender/checks/check-links.ts index bea0f00a0f9..426486ca98e 100644 --- a/adev/prerender/checks/check-links.ts +++ b/adev/prerender/checks/check-links.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ /* tslint:disable:no-console */ -import {NavigationItem} from '@angular/docs-shared'; +import {NavigationItem} from '@angular/docs'; import {readFileSync} from 'fs'; import {glob} from 'glob'; import {join} from 'path'; diff --git a/adev/prerender/tutorials/generate-tutorials-routes.ts b/adev/prerender/tutorials/generate-tutorials-routes.ts index e9106d6aef8..6f896e69ed8 100644 --- a/adev/prerender/tutorials/generate-tutorials-routes.ts +++ b/adev/prerender/tutorials/generate-tutorials-routes.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ -import {NavigationItem} from '@angular/docs-shared'; +import {NavigationItem} from '@angular/docs'; import {PagePrefix} from '../../src/app/core/enums/pages'; diff --git a/adev/prerender/tutorials/tutorials-types.ts b/adev/prerender/tutorials/tutorials-types.ts index 01a92b71747..81b82026f46 100644 --- a/adev/prerender/tutorials/tutorials-types.ts +++ b/adev/prerender/tutorials/tutorials-types.ts @@ -8,7 +8,7 @@ import type {FileSystemTree} from '@webcontainer/api'; import type {TutorialNavigationItemWithStep} from './generate-tutorials-routes'; -import {NavigationItem} from '@angular/docs-shared'; +import {NavigationItem} from '@angular/docs'; import {TutorialType} from './utils/web-constants'; /** diff --git a/adev/shared/ng-package.json b/adev/shared/ng-package.json deleted file mode 100644 index 1aed3740f57..00000000000 --- a/adev/shared/ng-package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/shared", - "lib": { - "entryFile": "src/public-api.ts" - } -} diff --git a/adev/shared/package.json b/adev/shared/package.json deleted file mode 100644 index ae5206413fb..00000000000 --- a/adev/shared/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "shared", - "version": "0.0.1", - "peerDependencies": { - "@angular/common": "^16.0.1", - "@angular/core": "^16.0.1" - }, - "dependencies": { - "tslib": "^2.3.0" - }, - "sideEffects": false -} diff --git a/adev/shared/src/lib/components/algolia-icon/algolia-icon.component.html b/adev/shared/src/lib/components/algolia-icon/algolia-icon.component.html deleted file mode 100644 index fc7b5fc15f0..00000000000 --- a/adev/shared/src/lib/components/algolia-icon/algolia-icon.component.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/adev/shared/src/lib/components/algolia-icon/algolia-icon.component.ts b/adev/shared/src/lib/components/algolia-icon/algolia-icon.component.ts deleted file mode 100644 index b0b469691ef..00000000000 --- a/adev/shared/src/lib/components/algolia-icon/algolia-icon.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -@Component({ - selector: 'docs-algolia-icon', - standalone: true, - imports: [], - templateUrl: './algolia-icon.component.html', -}) -export class AlgoliaIcon {} diff --git a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.html b/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.html deleted file mode 100644 index 5cf1378ed22..00000000000 --- a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.html +++ /dev/null @@ -1,13 +0,0 @@ -@for (breadcrumb of breadcrumbItems(); track breadcrumb) { -
- @if (breadcrumb.path) { - @if (breadcrumb.isExternal) { - {{ breadcrumb.label }} - } @else { - {{ breadcrumb.label }} - } - } @else { - {{ breadcrumb.label }} - } -
-} diff --git a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.scss b/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.scss deleted file mode 100644 index fc5e6889d6b..00000000000 --- a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.scss +++ /dev/null @@ -1,25 +0,0 @@ -:host { - display: flex; - align-items: center; - padding-block-end: 1.5rem; -} - -.docs-breadcrumb { - span { - color: var(--quaternary-contrast); - font-size: 0.875rem; - display: flex; - align-items: center; - } - - &:not(:last-child) { - span { - &::after { - content: 'chevron_right'; - font-family: var(--icons); - margin-inline: 0.5rem; - color: var(--quinary-contrast); - } - } - } -} diff --git a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.spec.ts b/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.spec.ts deleted file mode 100644 index efbffe9177c..00000000000 --- a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {Breadcrumb} from './breadcrumb.component'; - -describe('Breadcrumb', () => { - let component: Breadcrumb; - let fixture: ComponentFixture; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [Breadcrumb], - }); - fixture = TestBed.createComponent(Breadcrumb); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.ts b/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.ts deleted file mode 100644 index 6e4272d7412..00000000000 --- a/adev/shared/src/lib/components/breadcrumb/breadcrumb.component.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ChangeDetectionStrategy, Component, OnInit, inject, signal} from '@angular/core'; -import {NavigationState} from '../../services'; -import {NavigationItem} from '../../interfaces'; -import {NgFor, NgIf} from '@angular/common'; -import {RouterLink} from '@angular/router'; - -@Component({ - selector: 'docs-breadcrumb', - standalone: true, - imports: [NgIf, NgFor, RouterLink], - templateUrl: './breadcrumb.component.html', - styleUrls: ['./breadcrumb.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class Breadcrumb implements OnInit { - private readonly navigationState = inject(NavigationState); - - breadcrumbItems = signal([]); - - ngOnInit(): void { - this.setBreadcrumbItemsBasedOnNavigationStructure(); - } - - private setBreadcrumbItemsBasedOnNavigationStructure(): void { - let breadcrumbs: NavigationItem[] = []; - - const traverse = (node: NavigationItem | null) => { - if (!node) { - return; - } - - if (node.parent) { - breadcrumbs = [node.parent, ...breadcrumbs]; - traverse(node.parent); - } - }; - - traverse(this.navigationState.activeNavigationItem()); - - this.breadcrumbItems.set(breadcrumbs); - } -} diff --git a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.html b/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.html deleted file mode 100644 index ca0af01fd6e..00000000000 --- a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.html +++ /dev/null @@ -1,22 +0,0 @@ -@if (!hasAccepted()) { -
-

This site uses cookies from Google to deliver its services and to analyze traffic.

- -
- - - - -
-
-} diff --git a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.scss b/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.scss deleted file mode 100644 index 65fab67d4e7..00000000000 --- a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.scss +++ /dev/null @@ -1,40 +0,0 @@ -:host { - position: fixed; - bottom: 0.5rem; - right: 0.5rem; - z-index: var(--z-index-cookie-consent); - opacity: 0; - visibility: hidden; - animation: 1s linear forwards 0.5s fadeIn; -} - -.docs-cookies-popup { - padding: 1rem; - background-color: var(--page-background); - border: 1px solid var(--senary-contrast); - border-radius: 0.25rem; - font-size: 0.875rem; - max-width: 265px; - transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease; - box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); - - > div { - display: flex; - gap: 0.5rem; - align-items: center; - width: 100%; - margin-block-start: 1rem; - } - - p { - margin-block: 0; - color: var(--primary-contrast); - } -} - -@keyframes fadeIn { - 100% { - opacity: 100%; - visibility: visible; - } -} diff --git a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.spec.ts b/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.spec.ts deleted file mode 100644 index 70e78b1930b..00000000000 --- a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {CookiePopup, STORAGE_KEY} from './cookie-popup.component'; -import {LOCAL_STORAGE} from '../../providers'; -import {MockLocalStorage} from '../../utils/test.utils'; - -describe('CookiePopup', () => { - let fixture: ComponentFixture; - let mockLocalStorage = new MockLocalStorage(); - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [CookiePopup], - providers: [ - { - provide: LOCAL_STORAGE, - useValue: mockLocalStorage, - }, - ], - }); - }); - - it('should make the popup visible by default', () => { - initComponent(false); - - expect(getCookiesPopup()).not.toBeNull(); - }); - - it('should hide the cookies popup if the user has already accepted cookies', () => { - initComponent(true); - - expect(getCookiesPopup()).toBeNull(); - }); - - it('should hide the cookies popup', () => { - initComponent(false); - - accept(); - - fixture.detectChanges(); - - expect(getCookiesPopup()).toBeNull(); - }); - - it('should store the user confirmation', () => { - initComponent(false); - - expect(mockLocalStorage.getItem(STORAGE_KEY)).toBeNull(); - - accept(); - - expect(mockLocalStorage.getItem(STORAGE_KEY)).toBe('true'); - }); - - // Helpers - function getCookiesPopup() { - return (fixture.nativeElement as HTMLElement).querySelector('.docs-cookies-popup'); - } - - function accept() { - (fixture.nativeElement as HTMLElement) - .querySelector('button[text="Ok, Got it"]') - ?.click(); - } - - function initComponent(cookiesAccepted: boolean) { - mockLocalStorage.setItem(STORAGE_KEY, cookiesAccepted ? 'true' : null); - fixture = TestBed.createComponent(CookiePopup); - - fixture.detectChanges(); - } -}); diff --git a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.ts b/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.ts deleted file mode 100644 index d83482c109d..00000000000 --- a/adev/shared/src/lib/components/cookie-popup/cookie-popup.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core'; -import {NgIf} from '@angular/common'; -import {LOCAL_STORAGE} from '../../providers'; - -export const STORAGE_KEY = 'docs-accepts-cookies'; - -@Component({ - selector: 'docs-cookie-popup', - standalone: true, - imports: [NgIf], - templateUrl: './cookie-popup.component.html', - styleUrls: ['./cookie-popup.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class CookiePopup { - private readonly localStorage = inject(LOCAL_STORAGE); - - /** Whether the user has accepted the cookie disclaimer. */ - hasAccepted = signal(false); - - constructor() { - // Needs to be in a try/catch, because some browsers will - // throw when using `localStorage` in private mode. - try { - this.hasAccepted.set(this.localStorage?.getItem(STORAGE_KEY) === 'true'); - } catch { - this.hasAccepted.set(false); - } - } - - /** Accepts the cookie disclaimer. */ - protected accept(): void { - try { - this.localStorage?.setItem(STORAGE_KEY, 'true'); - } catch {} - - this.hasAccepted.set(true); - } -} diff --git a/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.html b/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.html deleted file mode 100644 index a0ef47cc928..00000000000 --- a/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - -check diff --git a/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.spec.ts b/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.spec.ts deleted file mode 100644 index 425995f858c..00000000000 --- a/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; - -import { - CONFIRMATION_DISPLAY_TIME_MS, - CopySourceCodeButton, -} from './copy-source-code-button.component'; -import {Component, Input} from '@angular/core'; -import {By} from '@angular/platform-browser'; -import {Clipboard} from '@angular/cdk/clipboard'; - -const SUCCESSFULLY_COPY_CLASS_NAME = 'docs-copy-source-code-button-success'; -const FAILED_COPY_CLASS_NAME = 'docs-copy-source-code-button-failed'; - -describe('CopySourceCodeButton', () => { - let component: CodeSnippetWrapper; - let fixture: ComponentFixture; - let copySpy: jasmine.Spy<(text: string) => boolean>; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [CodeSnippetWrapper], - }); - fixture = TestBed.createComponent(CodeSnippetWrapper); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - beforeEach(() => { - const clipboardService = TestBed.inject(Clipboard); - copySpy = spyOn(clipboardService, 'copy'); - }); - - it('should call clipboard service when clicked on copy source code', () => { - const expectedCodeToBeCopied = 'npm install -g @angular/cli'; - component.code = expectedCodeToBeCopied; - - fixture.detectChanges(); - - const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement; - button.click(); - - expect(copySpy.calls.argsFor(0)[0].trim()).toBe(expectedCodeToBeCopied); - }); - - it('should not copy lines marked as deleted when code snippet contains diff', () => { - const codeInHtmlFormat = ` - -
<div *ngFor="let product of products">
-
<div *ngFor="let product of products()">
-
- `; - const expectedCodeToBeCopied = `
`; - component.code = codeInHtmlFormat; - - fixture.detectChanges(); - - const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement; - button.click(); - - expect(copySpy.calls.argsFor(0)[0].trim()).toBe(expectedCodeToBeCopied); - }); - - it(`should set ${SUCCESSFULLY_COPY_CLASS_NAME} for ${CONFIRMATION_DISPLAY_TIME_MS} ms when copy was executed properly`, fakeAsync(() => { - component.code = 'example'; - - fixture.detectChanges(); - - const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement; - button.click(); - fixture.detectChanges(); - - expect(button).toHaveClass(SUCCESSFULLY_COPY_CLASS_NAME); - - tick(CONFIRMATION_DISPLAY_TIME_MS); - fixture.detectChanges(); - - expect(button).not.toHaveClass(SUCCESSFULLY_COPY_CLASS_NAME); - })); - - it(`should set ${FAILED_COPY_CLASS_NAME} for ${CONFIRMATION_DISPLAY_TIME_MS} ms when copy failed`, fakeAsync(() => { - component.code = 'example'; - copySpy.and.throwError('Fake copy error'); - - fixture.detectChanges(); - - const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement; - button.click(); - - fixture.detectChanges(); - - expect(button).toHaveClass(FAILED_COPY_CLASS_NAME); - - tick(CONFIRMATION_DISPLAY_TIME_MS); - fixture.detectChanges(); - - expect(button).not.toHaveClass(FAILED_COPY_CLASS_NAME); - })); -}); - -@Component({ - template: ` -
-      
-    
- - `, - imports: [CopySourceCodeButton], - standalone: true, -}) -class CodeSnippetWrapper { - @Input({required: true}) code!: string; -} diff --git a/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.ts b/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.ts deleted file mode 100644 index 402245d2fbb..00000000000 --- a/adev/shared/src/lib/components/copy-source-code-button/copy-source-code-button.component.ts +++ /dev/null @@ -1,89 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - WritableSignal, - inject, - signal, -} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {Clipboard} from '@angular/cdk/clipboard'; -import {IconComponent} from '../icon/icon.component'; - -export const REMOVED_LINE_CLASS_NAME = '.hljs-ln-line.remove'; -export const CONFIRMATION_DISPLAY_TIME_MS = 2000; - -@Component({ - selector: 'button[docs-copy-source-code]', - standalone: true, - imports: [CommonModule, IconComponent], - templateUrl: './copy-source-code-button.component.html', - host: { - 'type': 'button', - 'aria-label': 'Copy example source to clipboard', - 'title': 'Copy example source', - '(click)': 'copySourceCode()', - '[class.docs-copy-source-code-button-success]': 'showCopySuccess()', - '[class.docs-copy-source-code-button-failed]': 'showCopyFailure()', - }, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class CopySourceCodeButton { - private readonly changeDetector = inject(ChangeDetectorRef); - private readonly clipboard = inject(Clipboard); - private readonly elementRef = inject(ElementRef); - - protected readonly showCopySuccess = signal(false); - protected readonly showCopyFailure = signal(false); - - copySourceCode(): void { - try { - const codeElement = this.elementRef.nativeElement.parentElement.querySelector( - 'code', - ) as HTMLElement; - const sourceCode = this.getSourceCode(codeElement); - this.clipboard.copy(sourceCode); - this.showResult(this.showCopySuccess); - } catch { - this.showResult(this.showCopyFailure); - } - } - - private getSourceCode(codeElement: HTMLElement): string { - this.showCopySuccess.set(false); - this.showCopyFailure.set(false); - - const removedLines: NodeList = codeElement.querySelectorAll(REMOVED_LINE_CLASS_NAME); - - if (removedLines.length) { - // Get only those lines which are not marked as removed - const formattedText = Array.from(codeElement.querySelectorAll('.hljs-ln-line:not(.remove)')) - .map((line) => (line as HTMLDivElement).innerText) - .join('\n'); - - return formattedText.trim(); - } else { - const text: string = codeElement.innerText || ''; - return text.replaceAll(`\n\n\n`, ``).trim(); - } - } - - private showResult(messageState: WritableSignal) { - messageState.set(true); - - setTimeout(() => { - messageState.set(false); - // It's required for code snippets embedded in the ExampleViewer. - this.changeDetector.markForCheck(); - }, CONFIRMATION_DISPLAY_TIME_MS); - } -} diff --git a/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.scss b/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.scss deleted file mode 100644 index b5b034cc957..00000000000 --- a/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.scss +++ /dev/null @@ -1,145 +0,0 @@ -@use '../../styles/links' as links; - -:host { - --translate-y: clamp(5px, 0.25em, 7px); -} - -.docs-viewer { - display: flex; - flex-direction: column; - padding: var(--layout-padding); - max-width: var(--page-width); - width: 100%; - box-sizing: border-box; - - @media only screen and (max-width: 1430px) { - container: docs-content / inline-size; - } - - // If rendered on the docs page, accommodate width for TOC - adev-docs & { - @media only screen and (min-width: 1430px) and (max-width: 1550px) { - width: calc(100% - 195px - var(--layout-padding)); - max-width: var(--page-width); - } - } - - pre { - margin-block: 0; - padding-block: 0.75rem; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - .docs-anchor { - margin-block-start: 2.5rem; - display: inline-block; - color: inherit; - - &::after { - content: '\e157'; // codepoint for "link" - font-family: 'Material Symbols Outlined'; - - opacity: 0; - margin-left: 8px; - vertical-align: middle; - - color: var(--quaternary-contrast); - font-size: clamp(18px, 1.25em, 30px); - transition: opacity 0.3s ease; - } - &:hover { - &::after { - opacity: 1; - } - } - } - } - - h1 { - font-size: 2.5rem; - margin-block-end: 0; - } - - h2 { - font-size: 2rem; - margin-block-end: 0.5rem; - } - - h3 { - font-size: 1.5rem; - margin-block-end: 0.5rem; - } - - h4 { - font-size: 1.25rem; - margin-block-end: 0.5rem; - } - - h5 { - font-size: 1rem; - margin-block-end: 0; - } - - h6 { - font-size: 0.875rem; - margin-block-end: 0; - } - - > :last-child { - margin-block-end: 0; - } - - a:not(.docs-github-links):not(.docs-card):not(.docs-pill):not(.docs-example-github-link) { - &[href^='http:'], - &[href^='https:'] { - @include links.external-link-with-icon(); - } - } - - &-scroll-margin-large { - h2, - h3 { - scroll-margin: 5em; - } - } -} - -.docs-header { - margin-block-end: 1rem; - & > p:first-child { - color: var(--quaternary-contrast); - font-weight: 500; - margin: 0; - } -} - -.docs-page-title { - display: flex; - justify-content: space-between; - - h1 { - margin-block: 0; - font-size: 2.25rem; - } - - a { - color: var(--primary-contrast); - height: fit-content; - - docs-icon { - color: var(--gray-400); - transition: color 0.3s ease; - } - - &:hover { - docs-icon { - color: var(--primary-contrast); - } - } - } -} diff --git a/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.spec.ts b/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.spec.ts deleted file mode 100644 index aca1b580b21..00000000000 --- a/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.spec.ts +++ /dev/null @@ -1,104 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {By} from '@angular/platform-browser'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {RouterTestingModule} from '@angular/router/testing'; -import {DocContent, ExampleViewerContentLoader} from '../../interfaces'; -import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../providers'; -import {CodeExampleViewMode, ExampleViewer} from '../example-viewer/example-viewer.component'; -import {DocViewer} from './docs-viewer.component'; - -describe('DocViewer', () => { - let fixture: ComponentFixture; - let exampleContentSpy: jasmine.SpyObj; - - const sampleDocContentWithExampleViewerPlaceholders: DocContent = { - id: 'id', - contents: `
-
A styled code example
-
-      
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectorRef, Component, inject, signal} from '@angular/core';
import {Component, signal} from '@angular/core';
import {CommonModule} from '@angular/common';
@Component({
selector: 'hello-world',
standalone: true,
imports: [CommonModule],
templateUrl: './hello-world.html',
styleUrls: ['./hello-world.css'],
})
export default class HelloWorldComponent {
world = 'World';
world = 'World!!!';
count = signal(0);
changeDetector = inject(ChangeDetectorRef);
increase(): void {
this.count.update((previous) => {
return previous + 1;
});
this.changeDetector.detectChanges();
}
}
-
-
`, - }; - - const sampleDocContentWithExpandedExampleViewerPlaceholders: DocContent = { - id: 'id', - contents: `
-
-
-        
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectorRef, Component, inject, signal} from '@angular/core';
import {Component, signal} from '@angular/core';
import {CommonModule} from '@angular/common';
@Component({
selector: 'hello-world',
standalone: true,
imports: [CommonModule],
templateUrl: './hello-world.html',
styleUrls: ['./hello-world.css'],
})
export default class HelloWorldComponent {
world = 'World';
world = 'World!!!';
count = signal(0);
changeDetector = inject(ChangeDetectorRef);
increase(): void {
this.count.update((previous) => {
return previous + 1;
});
this.changeDetector.detectChanges();
}
}
-
-
-
-
-        
<h2>Hello {{ world }}</h2>
<button (click)="increase()">Increase</button>
<p>Counter: {{ count() }}</p>
-
-
-
`, - }; - - beforeEach(() => { - exampleContentSpy = jasmine.createSpyObj('ExampleContentLoader', ['getCodeExampleData']); - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DocViewer, NoopAnimationsModule, RouterTestingModule], - providers: [{provide: EXAMPLE_VIEWER_CONTENT_LOADER, useValue: exampleContentSpy}], - }).compileComponents(); - - fixture = TestBed.createComponent(DocViewer); - fixture.detectChanges(); - }); - - it('should load doc into innerHTML', () => { - const fixture = TestBed.createComponent(DocViewer); - fixture.componentRef.setInput('docContent', 'hello world'); - fixture.detectChanges(); - - expect(fixture.nativeElement.innerHTML).toBe('hello world'); - }); - - it('should instantiate example viewer in snippet view mode', async () => { - const fixture = TestBed.createComponent(DocViewer); - fixture.componentRef.setInput( - 'docContent', - sampleDocContentWithExampleViewerPlaceholders.contents, - ); - fixture.detectChanges(); - await fixture.whenStable(); - - const exampleViewer = fixture.debugElement.query(By.directive(ExampleViewer)); - - expect(exampleViewer).not.toBeNull(); - expect(exampleViewer.componentInstance.view()).toBe(CodeExampleViewMode.SNIPPET); - }); - - it('should display example viewer in multi file mode when user clicks expand', async () => { - const fixture = TestBed.createComponent(DocViewer); - fixture.componentRef.setInput( - 'docContent', - sampleDocContentWithExpandedExampleViewerPlaceholders.contents, - ); - fixture.detectChanges(); - await fixture.whenStable(); - - const exampleViewer = fixture.debugElement.query(By.directive(ExampleViewer)); - const expandButton = fixture.debugElement.query( - By.css('button[aria-label="Expand code example"]'), - ); - expandButton.nativeElement.click(); - - expect(exampleViewer).not.toBeNull(); - expect(exampleViewer.componentInstance.view()).toBe(CodeExampleViewMode.MULTI_FILE); - expect(exampleViewer.componentInstance.tabs().length).toBe(2); - }); -}); diff --git a/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.ts b/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.ts deleted file mode 100644 index 3d1c977bc6c..00000000000 --- a/adev/shared/src/lib/components/docs-viewer/docs-viewer.component.ts +++ /dev/null @@ -1,332 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {CommonModule, DOCUMENT, isPlatformBrowser, Location} from '@angular/common'; -import { - ApplicationRef, - ChangeDetectionStrategy, - Component, - ComponentRef, - createComponent, - DestroyRef, - ElementRef, - EnvironmentInjector, - inject, - Injector, - Input, - OnChanges, - PLATFORM_ID, - SimpleChanges, - Type, - ViewContainerRef, - ViewEncapsulation, - ɵInitialRenderPendingTasks as PendingRenderTasks, - EventEmitter, - Output, -} from '@angular/core'; -import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; -import { - handleHrefClickEventWithRouter, - IconComponent, - NavigationState, - Snippet, - TableOfContents, - TOC_SKIP_CONTENT_MARKER, -} from '@angular/docs-shared'; -import {Router} from '@angular/router'; -import {fromEvent} from 'rxjs'; - -import {Breadcrumb} from '../breadcrumb/breadcrumb.component'; -import {CopySourceCodeButton} from '../copy-source-code-button/copy-source-code-button.component'; -import {ExampleViewer} from '../example-viewer/example-viewer.component'; - -/// - -const TOC_HOST_ELEMENT_NAME = 'docs-table-of-contents'; -export const ASSETS_EXAMPLES_PATH = 'assets/content/examples'; -export const DOCS_VIEWER_SELECTOR = 'docs-viewer'; -export const DOCS_CODE_SELECTOR = '.docs-code'; -export const DOCS_CODE_MUTLIFILE_SELECTOR = '.docs-code-multifile'; -// TODO: Update the branch/sha -export const GITHUB_CONTENT_URL = - 'https://github.com/angular/angular/blob/main/adev/src/content/examples/'; - -@Component({ - selector: DOCS_VIEWER_SELECTOR, - standalone: true, - imports: [CommonModule], - template: '', - styleUrls: ['docs-viewer.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, - host: { - '[class.docs-animate-content]': 'animateContent', - }, -}) -export class DocViewer implements OnChanges { - @Input() docContent?: string; - @Input() hasToc = false; - @Output() contentLoaded = new EventEmitter(); - - private readonly destroyRef = inject(DestroyRef); - private readonly document = inject(DOCUMENT); - private readonly elementRef = inject(ElementRef); - private readonly location = inject(Location); - private readonly navigationState = inject(NavigationState); - private readonly platformId = inject(PLATFORM_ID); - private readonly router = inject(Router); - private readonly viewContainer = inject(ViewContainerRef); - private readonly environmentInjector = inject(EnvironmentInjector); - private readonly injector = inject(Injector); - private readonly appRef = inject(ApplicationRef); - - // tslint:disable-next-line:no-unused-variable - private animateContent = false; - private readonly pendingRenderTasks = inject(PendingRenderTasks); - - private countOfExamples = 0; - - async ngOnChanges(changes: SimpleChanges): Promise { - const taskId = this.pendingRenderTasks.add(); - if ('docContent' in changes) { - await this.renderContentsAndRunClientSetup(this.docContent!); - } - this.pendingRenderTasks.remove(taskId); - } - - async renderContentsAndRunClientSetup(content?: string): Promise { - const isBrowser = isPlatformBrowser(this.platformId); - const contentContainer = this.elementRef.nativeElement; - - if (content) { - if (isBrowser && !(this.document as any).startViewTransition) { - // Apply a special class to the host node to trigger animation. - // Note: when a page is hydrated, the `content` would be empty, - // so we don't trigger an animation to avoid a content flickering - // visual effect. In addition, if the browser supports view transitions (startViewTransition is present), the animation is handled by the native View Transition API so it does not need to be done here. - this.animateContent = true; - } - - contentContainer.innerHTML = content; - } - - if (isBrowser) { - // First we setup event listeners on the HTML we just loaded. - // We want to do this before things like the example viewers are loaded. - this.setupAnchorListeners(contentContainer); - // Rewrite relative anchors (hrefs starting with `#`) because relative hrefs are relative to the base URL, which is '/' - this.rewriteRelativeAnchors(contentContainer); - // In case when content contains placeholders for executable examples, create ExampleViewer components. - await this.loadExamples(); - // In case when content contains static code snippets, then create buttons - // responsible for copy source code. - this.loadCopySourceCodeButtons(); - } - - // Display Breadcrumb component if the `` element exists - this.loadBreadcrumb(contentContainer); - - // Display Icon component if the `` element exists - this.loadIcons(contentContainer); - - // Render ToC - this.renderTableOfContents(contentContainer); - - this.contentLoaded.next(); - } - - /** - * Load ExampleViewer component when: - * - exists docs-code-multifile element with multiple files OR - * - exists docs-code element with single file AND - * - 'preview' attribute was provided OR - * - 'visibleLines' attribute was provided - */ - private async loadExamples(): Promise { - const multifileCodeExamples = ( - Array.from(this.elementRef.nativeElement.querySelectorAll(DOCS_CODE_MUTLIFILE_SELECTOR)) - ); - - for (let placeholder of multifileCodeExamples) { - const path = placeholder.getAttribute('path')!; - const snippets = this.getCodeSnippetsFromMultifileWrapper(placeholder); - await this.renderExampleViewerComponents(placeholder, snippets, path); - } - - const docsCodeElements = this.elementRef.nativeElement.querySelectorAll(DOCS_CODE_SELECTOR); - - for (const placeholder of docsCodeElements) { - const snippet = this.getStandaloneCodeSnippet(placeholder); - if (snippet) { - await this.renderExampleViewerComponents(placeholder, [snippet], snippet.name); - } - } - } - - private renderTableOfContents(element: HTMLElement): void { - if (!this.hasToc) { - return; - } - - const firstHeading = element.querySelector('h2,h3[id]'); - if (!firstHeading) { - return; - } - - // Since the content of the main area is dynamically created and there is - // no host element for a ToC component, we create it manually. - let tocHostElement: HTMLElement | null = element.querySelector(TOC_HOST_ELEMENT_NAME); - if (!tocHostElement) { - tocHostElement = this.document.createElement(TOC_HOST_ELEMENT_NAME); - tocHostElement.setAttribute(TOC_SKIP_CONTENT_MARKER, 'true'); - firstHeading?.parentNode?.insertBefore(tocHostElement, firstHeading); - } - - this.renderComponent(TableOfContents, tocHostElement, {contentSourceElement: element}); - } - - private async renderExampleViewerComponents( - placeholder: HTMLElement, - snippets: Snippet[], - path: string, - ): Promise { - const preview = Boolean(placeholder.getAttribute('preview')); - const title = placeholder.getAttribute('header') ?? undefined; - const firstCodeSnippetTitle = - snippets.length > 0 ? snippets[0].title ?? snippets[0].name : undefined; - const exampleRef = this.viewContainer.createComponent(ExampleViewer); - - this.countOfExamples++; - exampleRef.instance.metadata = { - title: title ?? firstCodeSnippetTitle, - path, - files: snippets, - preview, - id: this.countOfExamples, - }; - - exampleRef.instance.githubUrl = `${GITHUB_CONTENT_URL}/${snippets[0].name}`; - exampleRef.instance.stackblitzUrl = `${ASSETS_EXAMPLES_PATH}/${snippets[0].name}.html`; - - placeholder.parentElement!.replaceChild(exampleRef.location.nativeElement, placeholder); - - await exampleRef.instance.renderExample(); - } - - private getCodeSnippetsFromMultifileWrapper(element: HTMLElement): Snippet[] { - const tabs = Array.from(element.querySelectorAll(DOCS_CODE_SELECTOR)); - - return tabs.map((tab) => ({ - name: tab.getAttribute('path') ?? tab.getAttribute('header') ?? '', - content: tab.innerHTML, - visibleLinesRange: tab.getAttribute('visibleLines') ?? undefined, - })); - } - - private getStandaloneCodeSnippet(element: HTMLElement): Snippet | null { - const visibleLines = element.getAttribute('visibleLines') ?? undefined; - const preview = element.getAttribute('preview'); - - if (!visibleLines && !preview) { - return null; - } - - const content = element.querySelector('pre')!; - const path = element.getAttribute('path')!; - const title = element.getAttribute('header') ?? undefined; - - return { - title, - name: path, - content: content?.outerHTML, - visibleLinesRange: visibleLines, - }; - } - - // If the content contains static code snippets, we should add buttons to copy - // the code - private loadCopySourceCodeButtons(): void { - const staticCodeSnippets = ( - Array.from(this.elementRef.nativeElement.querySelectorAll('.docs-code:not([mermaid])')) - ); - - for (let codeSnippet of staticCodeSnippets) { - const copySourceCodeButton = this.viewContainer.createComponent(CopySourceCodeButton); - codeSnippet.appendChild(copySourceCodeButton.location.nativeElement); - } - } - - private loadBreadcrumb(element: HTMLElement): void { - const breadcrumbPlaceholder = element.querySelector('docs-breadcrumb') as HTMLElement; - const activeNavigationItem = this.navigationState.activeNavigationItem(); - - if (breadcrumbPlaceholder && !!activeNavigationItem?.parent) { - this.renderComponent(Breadcrumb, breadcrumbPlaceholder); - } - } - - private loadIcons(element: HTMLElement): void { - element.querySelectorAll('docs-icon').forEach((iconsPlaceholder) => { - this.renderComponent(IconComponent, iconsPlaceholder as HTMLElement); - }); - } - - /** - * Helper method to render a component dynamically in a context of this class. - */ - private renderComponent( - type: Type, - hostElement: HTMLElement, - inputs?: {[key: string]: unknown}, - ): ComponentRef { - const componentRef = createComponent(type, { - hostElement, - elementInjector: this.injector, - environmentInjector: this.environmentInjector, - }); - - if (inputs) { - for (const [name, value] of Object.entries(inputs)) { - componentRef.setInput(name, value); - } - } - - // Trigger change detection after setting inputs. - componentRef.changeDetectorRef.detectChanges(); - - // Attach a view to the ApplicationRef for change detection - // purposes and for hydration serialization to pick it up - // during SSG. - this.appRef.attachView(componentRef.hostView); - - return componentRef; - } - - private setupAnchorListeners(element: HTMLElement): void { - element.querySelectorAll(`a[href]`).forEach((anchor) => { - // Get the target element's ID from the href attribute - const url = new URL((anchor as HTMLAnchorElement).href); - const isExternalLink = url.origin !== this.document.location.origin; - if (isExternalLink) { - return; - } - fromEvent(anchor, 'click') - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((e) => { - handleHrefClickEventWithRouter(e, this.router); - }); - }); - } - - private rewriteRelativeAnchors(element: HTMLElement) { - for (const anchor of Array.from(element.querySelectorAll(`a[href^="#"]:not(a[download])`))) { - const url = new URL((anchor as HTMLAnchorElement).href); - (anchor as HTMLAnchorElement).href = this.location.path() + url.hash; - } - } -} diff --git a/adev/shared/src/lib/components/example-viewer/example-viewer.component.html b/adev/shared/src/lib/components/example-viewer/example-viewer.component.html deleted file mode 100644 index be364bf503e..00000000000 --- a/adev/shared/src/lib/components/example-viewer/example-viewer.component.html +++ /dev/null @@ -1,155 +0,0 @@ -
-
- @if (view() === CodeExampleViewMode.SNIPPET) { - {{ exampleMetadata()?.title }} - } - - @if (view() === CodeExampleViewMode.MULTI_FILE) { - - @for (tab of tabs(); track tab) { - - } - - } - -
- - - @if (expandable()) { - - } -
-
- -
- - -
- - @if (exampleComponent) { -
- -
- } - - - @if (exampleComponent) { - @if (githubUrl) { - - - - } - @if (stackblitzUrl) { - - - - } - } - -
diff --git a/adev/shared/src/lib/components/example-viewer/example-viewer.component.scss b/adev/shared/src/lib/components/example-viewer/example-viewer.component.scss deleted file mode 100644 index 1df3ddbfab6..00000000000 --- a/adev/shared/src/lib/components/example-viewer/example-viewer.component.scss +++ /dev/null @@ -1,133 +0,0 @@ -:host { - .docs-example-viewer-preview { - .adev-dark-mode & { - background: var(--gray-100); - } - @media screen and (prefers-color-scheme: dark) { - background: var(--gray-100); - } - .adev-light-mode & { - background: var(--page-background); - } - } -} - -.docs-example-viewer { - border: 1px solid var(--senary-contrast); - border-radius: 0.25rem; - overflow: hidden; -} - -// Example viewer header -.docs-example-viewer-actions { - background: var(--subtle-purple); - display: flex; - justify-content: space-between; - align-items: center; - gap: 0.5rem; - border-bottom: 1px solid var(--senary-contrast); - transition: background 0.3s ease, border-color 0.3s ease; - padding-inline-end: 0.65rem; - font-family: var(--inter-tight-font); - - mat-tab-group { - max-width: calc(100% - 140px); - } - - span:first-of-type { - background-image: var(--purple-to-blue-horizontal-gradient); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - - padding: 0.7rem 1.1rem; - font-size: 0.875rem; - font-style: normal; - font-weight: 400; - line-height: 1.4rem; - letter-spacing: -0.00875rem; - margin: 0; - word-wrap: break-word; - width: fit-content; - } - - .docs-example-viewer-icons { - display: flex; - gap: 0.75rem; - - svg { - fill: var(--gray-400); - } - } - - a, - button { - padding: 0; - margin: 0; - cursor: pointer; - height: 24px; - width: 24px; - path { - transition: fill 0.3s ease; - } - - &:hover { - svg { - fill: var(--tertiary-contrast); - } - } - } -} - -// Example viewer code -.docs-example-viewer-code-wrapper { - position: relative; - font-size: 0.875rem; - // TODO: only show this if there is a preview - // border-block-end: 1px solid var(--senary-contrast); - transition: border-color 0.3s ease; - container: viewerblock / inline-size; - background-color: var(--octonary-contrast); - - button[docs-copy-source-code] { - top: 0.31rem; - @container viewerblock (min-width: 400px) { - background-color: transparent; - border: 1px solid transparent; - } - } -} - -// stylelint-disable-next-line -::ng-deep { - .docs-example-viewer-preview { - // stylelint-disable-next-line - all: initial; - display: block; - padding: 1rem; - border-block-start: 1px solid var(--senary-contrast); - - *, - code::before, - code, - pre, - a, - i, - p, - h1, - h2, - h3, - h4, - h5, - h6, - ol, - ul, - li, - hr, - input, - select, - table { - all: revert; - } - } -} diff --git a/adev/shared/src/lib/components/example-viewer/example-viewer.component.spec.ts b/adev/shared/src/lib/components/example-viewer/example-viewer.component.spec.ts deleted file mode 100644 index fbe959414d8..00000000000 --- a/adev/shared/src/lib/components/example-viewer/example-viewer.component.spec.ts +++ /dev/null @@ -1,228 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; -import {ExampleViewer} from './example-viewer.component'; -import {DocsContentLoader, ExampleMetadata, ExampleViewerContentLoader} from '../../interfaces'; -import {DOCS_CONTENT_LOADER, EXAMPLE_VIEWER_CONTENT_LOADER} from '../../providers'; -import {Component} from '@angular/core'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {HarnessLoader} from '@angular/cdk/testing'; -import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {Clipboard} from '@angular/cdk/clipboard'; -import {By} from '@angular/platform-browser'; -import {MatTabGroupHarness} from '@angular/material/tabs/testing'; -import {CopySourceCodeButton} from '../copy-source-code-button/copy-source-code-button.component'; -import {ActivatedRoute} from '@angular/router'; - -describe('ExampleViewer', () => { - let component: ExampleViewer; - let fixture: ComponentFixture; - let loader: HarnessLoader; - let exampleContentSpy: jasmine.SpyObj; - let contentServiceSpy: jasmine.SpyObj; - - beforeEach(() => { - exampleContentSpy = jasmine.createSpyObj('ExampleContentLoader', ['loadPreview']); - contentServiceSpy = jasmine.createSpyObj('ContentLoader', ['getContent']); - contentServiceSpy.getContent.and.returnValue(Promise.resolve(undefined)); - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ExampleViewer, NoopAnimationsModule], - providers: [ - {provide: EXAMPLE_VIEWER_CONTENT_LOADER, useValue: exampleContentSpy}, - {provide: DOCS_CONTENT_LOADER, useValue: contentServiceSpy}, - {provide: ActivatedRoute, useValue: {snapshot: {fragment: 'fragment'}}}, - ], - }).compileComponents(); - fixture = TestBed.createComponent(ExampleViewer); - component = fixture.componentInstance; - loader = TestbedHarnessEnvironment.loader(fixture); - fixture.detectChanges(); - }); - - it('should set file extensions as tab names when all files have different extension', waitForAsync(async () => { - component.metadata = getMetadata({ - files: [ - {name: 'file.ts', content: ''}, - {name: 'file.html', content: ''}, - {name: 'file.css', content: ''}, - ], - }); - - await component.renderExample(); - - expect(component.tabs()!.length).toBe(3); - expect(component.tabs()![0].name).toBe('TS'); - expect(component.tabs()![1].name).toBe('HTML'); - expect(component.tabs()![2].name).toBe('CSS'); - })); - - it('should generate correct code content for multi file mode when it is expanded', waitForAsync(async () => { - component.metadata = getMetadata({ - files: [ - {name: 'file.ts', content: 'typescript file'}, - {name: 'file.html', content: 'html file'}, - {name: 'file.css', content: 'css file'}, - ], - }); - - await component.renderExample(); - - expect(component.tabs()!.length).toBe(3); - expect(component.tabs()![0].code).toBe('typescript file'); - expect(component.tabs()![1].code).toBe('html file'); - expect(component.tabs()![2].code).toBe('css file'); - })); - - it('should set file names as tab names when there is at least one duplication', async () => { - component.metadata = getMetadata({ - files: [ - {name: 'example.ts', content: 'typescript file'}, - {name: 'example.html', content: 'html file'}, - {name: 'another-example.ts', content: 'css file'}, - ], - }); - - await component.renderExample(); - expect(component.tabs()!.length).toBe(3); - expect(component.tabs()![0].name).toBe('example.ts'); - expect(component.tabs()![1].name).toBe('example.html'); - expect(component.tabs()![2].name).toBe('another-example.ts'); - }); - - it('should expandable be false when none of the example files have defined visibleLinesRange ', waitForAsync(async () => { - component.metadata = getMetadata(); - await component.renderExample(); - expect(component.expandable()).toBeFalse(); - })); - - it('should expandable be true when at least one example file has defined visibleLinesRange ', waitForAsync(async () => { - component.metadata = getMetadata({ - files: [ - {name: 'example.ts', content: 'typescript file'}, - { - name: 'example.html', - content: 'html file', - visibleLinesRange: '[1, 2]', - }, - {name: 'another-example.ts', content: 'css file'}, - ], - }); - await component.renderExample(); - expect(component.expandable()).toBeTrue(); - })); - - it('should set exampleComponent when metadata contains path and preview is true', waitForAsync(async () => { - exampleContentSpy.loadPreview.and.resolveTo(ExampleComponent); - component.metadata = getMetadata({ - path: 'example.ts', - preview: true, - }); - await component.renderExample(); - expect(component.exampleComponent).toBe(ExampleComponent); - })); - - it('should display GitHub button when githubUrl is provided and there is preview', waitForAsync(async () => { - exampleContentSpy.loadPreview.and.resolveTo(ExampleComponent); - component.metadata = getMetadata({ - path: 'example.ts', - preview: true, - }); - component.githubUrl = 'https://github.com/'; - await component.renderExample(); - const githubButton = fixture.debugElement.query( - By.css('a[aria-label="Open example on GitHub"]'), - ); - expect(githubButton).toBeTruthy(); - expect(githubButton.nativeElement.href).toBe(component.githubUrl); - })); - - it('should display StackBlitz button when stackblitzUrl is provided and there is preview', waitForAsync(async () => { - exampleContentSpy.loadPreview.and.resolveTo(ExampleComponent); - component.metadata = getMetadata({ - path: 'example.ts', - preview: true, - }); - component.stackblitzUrl = 'https://stackblitz.com/'; - await component.renderExample(); - const stackblitzButton = fixture.debugElement.query( - By.css('a[aria-label="Edit this example in StackBlitz"]'), - ); - expect(stackblitzButton).toBeTruthy(); - expect(stackblitzButton.nativeElement.href).toBe(component.stackblitzUrl); - })); - - it('should set expanded flag in metadata after toggleExampleVisibility', waitForAsync(async () => { - component.metadata = getMetadata(); - await component.renderExample(); - component.toggleExampleVisibility(); - expect(component.expanded()).toBeTrue(); - const tabGroup = await loader.getHarness(MatTabGroupHarness); - const tab = await tabGroup.getSelectedTab(); - expect(await tab.getLabel()).toBe('TS'); - component.toggleExampleVisibility(); - expect(component.expanded()).toBeFalse(); - })); - - it('should call clipboard service when clicked on copy source code', waitForAsync(async () => { - const expectedCodeSnippetContent = 'typescript code'; - component.metadata = getMetadata({ - files: [ - { - name: 'example.ts', - content: `
${expectedCodeSnippetContent}
`, - }, - {name: 'example.css', content: ''}, - ], - }); - const clipboardService = TestBed.inject(Clipboard); - const spy = spyOn(clipboardService, 'copy'); - - await component.renderExample(); - const button = fixture.debugElement.query(By.directive(CopySourceCodeButton)).nativeElement; - button.click(); - - expect(spy.calls.argsFor(0)[0].trim()).toBe(expectedCodeSnippetContent); - })); - - it('should call clipboard service when clicked on copy example link', waitForAsync(async () => { - component.metadata = getMetadata(); - component.expanded.set(true); - fixture.detectChanges(); - - const clipboardService = TestBed.inject(Clipboard); - const spy = spyOn(clipboardService, 'copy'); - await component.renderExample(); - const button = fixture.debugElement.query( - By.css('button.docs-example-copy-link'), - ).nativeElement; - button.click(); - expect(spy.calls.argsFor(0)[0].trim()).toBe(`http://localhost:9876/context.html#example-1`); - })); -}); - -const getMetadata = (value: Partial = {}): ExampleMetadata => { - return { - id: 1, - files: [ - {name: 'example.ts', content: ''}, - {name: 'example.css', content: ''}, - ], - preview: false, - ...value, - }; -}; - -@Component({ - template: '', - standalone: true, -}) -class ExampleComponent {} diff --git a/adev/shared/src/lib/components/example-viewer/example-viewer.component.ts b/adev/shared/src/lib/components/example-viewer/example-viewer.component.ts deleted file mode 100644 index 69a762cf5c0..00000000000 --- a/adev/shared/src/lib/components/example-viewer/example-viewer.component.ts +++ /dev/null @@ -1,237 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - ChangeDetectionStrategy, - Component, - DestroyRef, - Input, - Type, - computed, - inject, - ChangeDetectorRef, - ViewChild, - signal, - ElementRef, - forwardRef, -} from '@angular/core'; -import {CommonModule, DOCUMENT} from '@angular/common'; -import {MatTabGroup, MatTabsModule} from '@angular/material/tabs'; -import {Clipboard} from '@angular/cdk/clipboard'; -import {CopySourceCodeButton} from '../copy-source-code-button/copy-source-code-button.component'; -import {ExampleMetadata, Snippet} from '../../interfaces'; -import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../providers'; -import {DocViewer} from '../docs-viewer/docs-viewer.component'; -import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; - -export enum CodeExampleViewMode { - SNIPPET = 'snippet', - MULTI_FILE = 'multi', -} - -export const CODE_LINE_NUMBER_CLASS_NAME = 'hljs-ln-number'; -export const CODE_LINE_CLASS_NAME = 'hljs-ln-line'; -export const GAP_CODE_LINE_CLASS_NAME = 'gap'; -export const HIDDEN_CLASS_NAME = 'hidden'; - -@Component({ - selector: 'docs-example-viewer', - standalone: true, - imports: [CommonModule, forwardRef(() => DocViewer), CopySourceCodeButton, MatTabsModule], - templateUrl: './example-viewer.component.html', - styleUrls: ['./example-viewer.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ExampleViewer { - // TODO: replace by signal-based input when it'll be available - @Input({required: true}) set metadata(value: ExampleMetadata) { - this.exampleMetadata.set(value); - } - - @Input() githubUrl: string | null = null; - @Input() stackblitzUrl: string | null = null; - @ViewChild('codeTabs') matTabGroup?: MatTabGroup; - - private readonly changeDetector = inject(ChangeDetectorRef); - private readonly clipboard = inject(Clipboard); - private readonly destroyRef = inject(DestroyRef); - private readonly document = inject(DOCUMENT); - private readonly elementRef = inject(ElementRef); - private readonly exampleViewerContentLoader = inject(EXAMPLE_VIEWER_CONTENT_LOADER); - - private readonly shouldDisplayFullName = computed(() => { - const fileExtensions = - this.exampleMetadata()?.files.map((file) => this.getFileExtension(file.name)) ?? []; - - // Display full file names only when exist files with the same extension - return new Set(fileExtensions).size !== fileExtensions.length; - }); - - CodeExampleViewMode = CodeExampleViewMode; - exampleComponent?: Type; - - expanded = signal(false); - exampleMetadata = signal(null); - snippetCode = signal(undefined); - tabs = computed(() => - this.exampleMetadata()?.files.map((file) => ({ - name: - file.title ?? (this.shouldDisplayFullName() ? file.name : this.getFileExtension(file.name)), - code: file.content, - })), - ); - view = computed(() => - this.exampleMetadata()?.files.length === 1 - ? CodeExampleViewMode.SNIPPET - : CodeExampleViewMode.MULTI_FILE, - ); - expandable = computed(() => - this.exampleMetadata()?.files.some((file) => !!file.visibleLinesRange), - ); - - async renderExample(): Promise { - // Lazy load live example component - if (this.exampleMetadata()?.path && this.exampleMetadata()?.preview) { - this.exampleComponent = await this.exampleViewerContentLoader.loadPreview( - this.exampleMetadata()?.path!, - ); - } - - this.snippetCode.set(this.exampleMetadata()?.files[0]); - - this.changeDetector.detectChanges(); - - this.setCodeLinesVisibility(); - - this.elementRef.nativeElement.setAttribute( - 'id', - `example-${this.exampleMetadata()?.id.toString()!}`, - ); - - this.matTabGroup?.realignInkBar(); - - this.listenToMatTabIndexChange(); - } - - toggleExampleVisibility(): void { - this.expanded.update((expanded) => !expanded); - - this.setCodeLinesVisibility(); - } - - copyLink(): void { - // Reconstruct the URL using `origin + pathname` so we drop any pre-existing hash. - const fullUrl = location.origin + location.pathname + '#example-' + this.exampleMetadata()?.id; - this.clipboard.copy(fullUrl); - } - - private listenToMatTabIndexChange(): void { - this.matTabGroup?.realignInkBar(); - this.matTabGroup?.selectedIndexChange - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((index) => { - this.snippetCode.set(this.exampleMetadata()?.files[index]); - this.setCodeLinesVisibility(); - }); - } - - private getFileExtension(name: string): string { - const segments = name.split('.'); - return segments.length ? segments[segments.length - 1].toLocaleUpperCase() : ''; - } - - private setCodeLinesVisibility(): void { - this.expanded() - ? this.handleExpandedStateForCodeBlock() - : this.handleCollapsedStateForCodeBlock(); - } - - private handleExpandedStateForCodeBlock(): void { - const lines = ( - Array.from( - this.elementRef.nativeElement.querySelectorAll( - `.${CODE_LINE_CLASS_NAME}.${HIDDEN_CLASS_NAME}`, - ), - ) - ); - - const lineNumbers = ( - Array.from( - this.elementRef.nativeElement.querySelectorAll( - `.${CODE_LINE_NUMBER_CLASS_NAME}.${HIDDEN_CLASS_NAME}`, - ), - ) - ); - - const gapLines = ( - Array.from( - this.elementRef.nativeElement.querySelectorAll( - `.${CODE_LINE_CLASS_NAME}.${GAP_CODE_LINE_CLASS_NAME}`, - ), - ) - ); - - for (const line of lines) { - line.classList.remove(HIDDEN_CLASS_NAME); - } - - for (const lineNumber of lineNumbers) { - lineNumber.classList.remove(HIDDEN_CLASS_NAME); - } - - for (const expandLine of gapLines) { - expandLine.remove(); - } - } - - private handleCollapsedStateForCodeBlock(): void { - const visibleLinesRange = this.snippetCode()?.visibleLinesRange; - - if (!visibleLinesRange) { - return; - } - - const linesToDisplay = (visibleLinesRange?.split(',') ?? []).map((line) => Number(line)); - const lines = ( - Array.from(this.elementRef.nativeElement.querySelectorAll(`.${CODE_LINE_CLASS_NAME}`)) - ); - const lineNumbers = ( - Array.from(this.elementRef.nativeElement.querySelectorAll(`.${CODE_LINE_NUMBER_CLASS_NAME}`)) - ); - const appendGapBefore = []; - - for (const [index, line] of lines.entries()) { - if (!linesToDisplay.includes(index)) { - line.classList.add(HIDDEN_CLASS_NAME); - } else if (!linesToDisplay.includes(index - 1)) { - appendGapBefore.push(line); - } - } - - for (const [index, lineNumber] of lineNumbers.entries()) { - if (!linesToDisplay.includes(index)) { - lineNumber.classList.add(HIDDEN_CLASS_NAME); - } - } - - // Create gap line between visible ranges. For example we would like to display 10-16 and 20-29 lines. - // We should display separator, gap between those two scopes. - // TODO: we could replace div it with the component, and allow to expand code block after click. - for (const [index, element] of appendGapBefore.entries()) { - if (index === 0) { - continue; - } - - const separator = this.document.createElement('div'); - separator.textContent = `...`; - separator.classList.add(CODE_LINE_CLASS_NAME); - separator.classList.add(GAP_CODE_LINE_CLASS_NAME); - element.parentNode?.insertBefore(separator, element); - } - } -} diff --git a/adev/shared/src/lib/components/icon/icon.component.html b/adev/shared/src/lib/components/icon/icon.component.html deleted file mode 100644 index 6dbc7430638..00000000000 --- a/adev/shared/src/lib/components/icon/icon.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/adev/shared/src/lib/components/icon/icon.component.scss b/adev/shared/src/lib/components/icon/icon.component.scss deleted file mode 100644 index a5337d3d774..00000000000 --- a/adev/shared/src/lib/components/icon/icon.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.docs-icon_high-contrast { - color: var(--primary-contrast); -} diff --git a/adev/shared/src/lib/components/icon/icon.component.ts b/adev/shared/src/lib/components/icon/icon.component.ts deleted file mode 100644 index ed38bf5c3f6..00000000000 --- a/adev/shared/src/lib/components/icon/icon.component.ts +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DOCUMENT} from '@angular/common'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - afterNextRender, - inject, -} from '@angular/core'; - -@Component({ - selector: 'docs-icon', - standalone: true, - templateUrl: './icon.component.html', - styleUrl: './icon.component.scss', - host: { - '[class]': 'MATERIAL_SYMBOLS_OUTLINED', - '[style.font-size.px]': 'fontSize', - 'aria-hidden': 'true', - }, - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class IconComponent { - private readonly cdRef = inject(ChangeDetectorRef); - - get fontSize(): number | null { - return IconComponent.isFontLoaded ? null : 0; - } - - protected readonly MATERIAL_SYMBOLS_OUTLINED = 'material-symbols-outlined'; - - private static isFontLoaded: boolean = false; - /** Share the same promise across different instances of the component */ - private static whenFontLoad?: Promise | undefined; - - constructor() { - if (IconComponent.isFontLoaded) { - return; - } - - const document = inject(DOCUMENT); - afterNextRender(async () => { - IconComponent.whenFontLoad ??= document.fonts.load('normal 1px "Material Symbols Outlined"'); - await IconComponent.whenFontLoad; - IconComponent.isFontLoaded = true; - - // We need to ensure CD is triggered on the component when the font is loaded - this.cdRef.markForCheck(); - }); - } -} diff --git a/adev/shared/src/lib/components/index.ts b/adev/shared/src/lib/components/index.ts deleted file mode 100644 index 9503c1d86b2..00000000000 --- a/adev/shared/src/lib/components/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './cookie-popup/cookie-popup.component'; -export * from './docs-viewer/docs-viewer.component'; -export * from './navigation-list/navigation-list.component'; -export * from './select/select.component'; -export * from './slide-toggle/slide-toggle.component'; -export * from './table-of-contents/table-of-contents.component'; -export * from './text-field/text-field.component'; -export * from './icon/icon.component'; -export * from './search-dialog/search-dialog.component'; diff --git a/adev/shared/src/lib/components/navigation-list/navigation-list.component.html b/adev/shared/src/lib/components/navigation-list/navigation-list.component.html deleted file mode 100644 index e54810698bf..00000000000 --- a/adev/shared/src/lib/components/navigation-list/navigation-list.component.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - - diff --git a/adev/shared/src/lib/components/navigation-list/navigation-list.component.scss b/adev/shared/src/lib/components/navigation-list/navigation-list.component.scss deleted file mode 100644 index ab1805c521a..00000000000 --- a/adev/shared/src/lib/components/navigation-list/navigation-list.component.scss +++ /dev/null @@ -1,148 +0,0 @@ -@use '../../../../../shared/src/lib/styles/media-queries' as mq; - -:host { - display: flex; - min-width: var(--secondary-nav-width); - list-style: none; - overflow-y: auto; - overflow-x: hidden; - height: 100vh; - padding: 0; - margin: 0; - padding-block: 1.5rem; - font-size: 0.875rem; - box-sizing: border-box; - - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - cursor: pointer; - } - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--septenary-contrast); - @include mq.for-tablet-landscape-down { - background-color: var(--quinary-contrast); - } - - border-radius: 10px; - transition: background-color 0.3s ease; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quinary-contrast); - } - - .adev-nav-secondary & { - padding-block: 2rem; - } - - > .adev-faceted-list { - border: 0; - } - - .docs-navigation-link-hidden { - display: none; - } - - .adev-nav-item-has-icon { - &::after { - // FIXME: for some reason this disappears when transformed - content: 'chevron_right'; - font-size: 1.25rem; - font-family: var(--icons); - } - } -} - -.adev-secondary-nav-header { - padding-block: 1.25rem; - font-weight: 500; -} - -.adev-secondary-nav-button { - width: 15rem; - display: flex; - justify-content: space-between; - align-items: center; - border: none; - padding-block: 1.25rem; - padding-inline-start: 0; - color: var(--primary-contrast); - font-size: 0.875rem; - font-family: var(--inter-font); - line-height: 160%; - letter-spacing: -0.00875rem; - transition: color 0.3s ease, background 0.3s ease; - text-align: left; // forces left alignment of text in button - - &.adev-secondary-nav-button-active { - // font gradient - background-image: var(--pink-to-purple-vertical-gradient); - &::before { - opacity: 1; - transform: scaleY(1); - background: var(--pink-to-purple-vertical-gradient); - } - &:hover { - &::before { - opacity: 1; - transform: scaleY(1.1); - } - } - } -} - -.adev-expanded-button { - justify-content: start; - gap: 0.5rem; -} - -a, -.adev-not-expanded-button { - display: flex; - justify-content: space-between; - align-items: center; - font-weight: 500; - line-height: 1.4rem; - letter-spacing: -0.00875rem; - padding: 0.5rem; - padding-inline-start: 1rem; - text-align: left; -} - -// Add padding-bottom to last item in the list -.docs-navigation-list { - width: 100%; - - li:last-of-type { - ul:last-of-type { - li:last-of-type { - padding-block-end: 1rem; - } - } - } - &:first-child { - margin-inline-start: 1rem; - } -} - -.adev-external-link { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - gap: 0.5rem; - &::after { - content: 'open_in_new'; - font-family: var(--icons); - font-size: 1.1rem; - color: var(--quinary-contrast); - transition: color 0.3s ease; - margin-inline-end: 0.4rem; - } -} diff --git a/adev/shared/src/lib/components/navigation-list/navigation-list.component.spec.ts b/adev/shared/src/lib/components/navigation-list/navigation-list.component.spec.ts deleted file mode 100644 index 18e9f07b3f1..00000000000 --- a/adev/shared/src/lib/components/navigation-list/navigation-list.component.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {NavigationList} from './navigation-list.component'; -import {By} from '@angular/platform-browser'; -import {NavigationItem} from '../../interfaces'; -import {RouterTestingModule} from '@angular/router/testing'; -import {signal} from '@angular/core'; -import {NavigationState} from '@angular/docs-shared'; - -const navigationItems: NavigationItem[] = [ - { - label: 'Introduction', - path: 'guide', - }, - { - label: 'Getting Started', - children: [ - {label: 'What is Angular?', path: 'guide/what-is-angular'}, - {label: 'Setup', path: 'guide/setup'}, - ], - }, -]; - -describe('NavigationList', () => { - let component: NavigationList; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [NavigationList, RouterTestingModule], - providers: [{provide: NavigationState, useClass: FakeNavigationListState}], - }).compileComponents(); - fixture = TestBed.createComponent(NavigationList); - component = fixture.componentInstance; - }); - - it('should display provided navigation structure', () => { - component.navigationItems = [...navigationItems]; - fixture.detectChanges(true); - - const links = fixture.debugElement.queryAll(By.css('a')); - - expect(links.length).toBe(3); - }); -}); - -class FakeNavigationListState { - isOpened = signal(true); - activeNavigationItem = signal(navigationItems.at(1)); -} diff --git a/adev/shared/src/lib/components/navigation-list/navigation-list.component.ts b/adev/shared/src/lib/components/navigation-list/navigation-list.component.ts deleted file mode 100644 index 3f3657d9bd0..00000000000 --- a/adev/shared/src/lib/components/navigation-list/navigation-list.component.ts +++ /dev/null @@ -1,60 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, - inject, -} from '@angular/core'; -import {NavigationItem} from '../../interfaces'; -import {NavigationState} from '../../services'; -import {RouterLink, RouterLinkActive} from '@angular/router'; -import {CommonModule} from '@angular/common'; -import {IconComponent} from '../icon/icon.component'; -import {IsActiveNavigationItem} from '../../pipes/is-active-navigation-item.pipe'; - -@Component({ - selector: 'docs-navigation-list', - standalone: true, - imports: [CommonModule, RouterLink, RouterLinkActive, IconComponent, IsActiveNavigationItem], - templateUrl: './navigation-list.component.html', - styleUrls: ['./navigation-list.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class NavigationList { - @Input({required: true}) navigationItems: NavigationItem[] = []; - @Input() displayItemsToLevel: number = 2; - @Input() collapsableLevel: number | undefined = undefined; - @Input() expandableLevel: number = 2; - @Input() isDropdownView = false; - - @Output() linkClicked = new EventEmitter(); - - private readonly navigationState = inject(NavigationState); - - expandedItems = this.navigationState.expandedItems; - activeItem = this.navigationState.activeNavigationItem; - - toggle(item: NavigationItem): void { - if ( - item.level === 1 && - item.level !== this.expandableLevel && - item.level !== this.collapsableLevel - ) { - return; - } - this.navigationState.toggleItem(item); - } - - emitClickOnLink(): void { - this.linkClicked.emit(); - } -} diff --git a/adev/shared/src/lib/components/search-dialog/search-dialog.component.html b/adev/shared/src/lib/components/search-dialog/search-dialog.component.html deleted file mode 100644 index 84025ad419d..00000000000 --- a/adev/shared/src/lib/components/search-dialog/search-dialog.component.html +++ /dev/null @@ -1,79 +0,0 @@ - -
- - - @if (searchResults() && searchResults()!.length > 0) { - - } @else { -
- @if (searchResults() === undefined) { -
- Start typing to see results -
- } @else if (searchResults()?.length === 0) { -
- No results found -
- } -
- } - -
- Search by - -
-
-
diff --git a/adev/shared/src/lib/components/search-dialog/search-dialog.component.scss b/adev/shared/src/lib/components/search-dialog/search-dialog.component.scss deleted file mode 100644 index 9f5ac8827b8..00000000000 --- a/adev/shared/src/lib/components/search-dialog/search-dialog.component.scss +++ /dev/null @@ -1,144 +0,0 @@ -dialog { - background-color: transparent; - border: none; - padding-block-end: 3rem; - - &::backdrop { - backdrop-filter: blur(5px); - } -} - -.adev-search-container { - width: 500px; - max-width: 90vw; - background-color: var(--page-background); - border: 1px solid var(--senary-contrast); - border-radius: 0.25rem; - box-sizing: border-box; - - .adev-search-input { - border-radius: 0.25rem 0.25rem 0 0; - border: none; - border-block-end: 1px solid var(--senary-contrast); - height: 2.6875rem; // 43px; - padding-inline-start: 1rem; - position: relative; - - &::after { - content: 'Esc'; - position: absolute; - right: 1rem; - color: var(--gray-400); - font-size: 0.875rem; - } - } - - ul { - max-height: 260px; - overflow-y: auto; - list-style-type: none; - padding-inline: 0; - padding-block-start: 1rem; - margin: 0; - - li { - border-inline-start: 2px solid var(--senary-contrast); - margin-inline-start: 1rem; - padding-inline-end: 1rem; - padding-block: 0.25rem; - - a { - color: var(--secondary-contrast); - display: flex; - justify-content: space-between; - gap: 0.5rem; - - .adev-search-result-icon { - i { - display: flex; - align-items: center; - font-size: 1.2rem; - } - } - } - - &.active { - background-color: var(--septenary-contrast); // stylelint-disable-line - } - - &:hover, - &.active { - background-color: var(--octonary-contrast); // stylelint-disable-line - border-inline-start: 2px solid var(--primary-contrast); - a { - span:not(.adev-result-page-title), - .adev-search-results__type { - color: var(--primary-contrast); - i { - color: var(--primary-contrast); - } - } - } - } - } - - .adev-search-result-icon, - .adev-search-results__type, - .adev-result-page-title { - color: var(--quaternary-contrast); - display: inline-block; - font-size: 0.875rem; - transition: color 0.3s ease; - padding: 0.75rem; - padding-inline-end: 0; - } - - .adev-search-results__lvl2 { - display: inline-block; - margin-inline-start: 2rem; - padding-block-start: 0; - } - - .adev-search-results__lvl3 { - margin-inline-start: 2rem; - padding-block-start: 0; - } - } - - .adev-result-page-title { - font-size: 0.875rem; - font-weight: 400; - } -} - -.adev-search-results__no-results { - padding: 0.75rem; - color: var(--gray-400); -} - -.adev-result-icon-and-type { - display: flex; - - .adev-search-results__type { - padding-inline-start: 0; - } -} - -.adev-algolia { - display: flex; - align-items: center; - justify-content: end; - color: var(--gray-400); - padding: 1rem; - font-size: 0.75rem; - font-weight: 500; - gap: 0.25rem; - background-color: var(--page-background); - border-radius: 0 0 0.25rem 0.25rem; - - docs-algolia-icon { - margin-block-start: 0.12rem; - margin-inline-start: 0.15rem; - width: 4rem; - } -} diff --git a/adev/shared/src/lib/components/search-dialog/search-dialog.component.spec.ts b/adev/shared/src/lib/components/search-dialog/search-dialog.component.spec.ts deleted file mode 100644 index 7b9ad261f82..00000000000 --- a/adev/shared/src/lib/components/search-dialog/search-dialog.component.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {SearchDialog} from './search-dialog.component'; -import {WINDOW} from '../../providers'; -import {Search} from '../../services'; -import {signal} from '@angular/core'; -import {FakeEventTarget} from '../../utils/test.utils'; - -describe('SearchDialog', () => { - let component: SearchDialog; - let fixture: ComponentFixture; - - const fakeSearch = { - keyDown: signal(null), - searchQuery: signal(''), - searchResults: signal([]), - }; - const fakeWindow = new FakeEventTarget(); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [SearchDialog], - providers: [ - { - provide: Search, - useValue: fakeSearch, - }, - { - provide: WINDOW, - useValue: fakeWindow, - }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(SearchDialog); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/adev/shared/src/lib/components/search-dialog/search-dialog.component.ts b/adev/shared/src/lib/components/search-dialog/search-dialog.component.ts deleted file mode 100644 index 5015422fc6d..00000000000 --- a/adev/shared/src/lib/components/search-dialog/search-dialog.component.ts +++ /dev/null @@ -1,139 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - AfterViewInit, - Component, - DestroyRef, - ElementRef, - EventEmitter, - NgZone, - OnDestroy, - OnInit, - Output, - QueryList, - ViewChild, - ViewChildren, - inject, -} from '@angular/core'; - -import {ClickOutside, Search, TextField, WINDOW} from '@angular/docs-shared'; -import {FormsModule} from '@angular/forms'; -import {ActiveDescendantKeyManager} from '@angular/cdk/a11y'; -import {SearchItem} from '../../directives/search-item/search-item.directive'; -import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; -import {Router, RouterLink} from '@angular/router'; -import {filter, fromEvent} from 'rxjs'; -import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component'; -import {RelativeLink} from '../../pipes/relative-link.pipe'; - -@Component({ - selector: 'docs-search-dialog', - standalone: true, - imports: [ - ClickOutside, - TextField, - FormsModule, - SearchItem, - AlgoliaIcon, - RelativeLink, - RouterLink, - ], - templateUrl: './search-dialog.component.html', - styleUrls: ['./search-dialog.component.scss'], -}) -export class SearchDialog implements OnInit, AfterViewInit, OnDestroy { - @Output() onClose = new EventEmitter(); - @ViewChild('searchDialog') dialog?: ElementRef; - @ViewChildren(SearchItem) items?: QueryList; - - private readonly destroyRef = inject(DestroyRef); - private readonly ngZone = inject(NgZone); - private readonly search = inject(Search); - private readonly relativeLink = new RelativeLink(); - private readonly router = inject(Router); - private readonly window = inject(WINDOW); - - private keyManager?: ActiveDescendantKeyManager; - - searchQuery = this.search.searchQuery; - searchResults = this.search.searchResults; - - ngOnInit(): void { - this.ngZone.runOutsideAngular(() => { - fromEvent(this.window, 'keydown') - .pipe( - filter((_) => !!this.keyManager), - takeUntilDestroyed(this.destroyRef), - ) - .subscribe((event) => { - // When user presses Enter we can navigate to currently selected item in the search result list. - if (event.key === 'Enter') { - this.navigateToTheActiveItem(); - } else { - this.ngZone.run(() => { - this.keyManager?.onKeydown(event); - }); - } - }); - }); - } - - ngAfterViewInit() { - this.dialog?.nativeElement.showModal(); - - if (!this.items) { - return; - } - - this.keyManager = new ActiveDescendantKeyManager(this.items).withWrap(); - this.keyManager?.setFirstItemActive(); - - this.updateActiveItemWhenResultsChanged(); - this.scrollToActiveItem(); - } - - ngOnDestroy(): void { - this.keyManager?.destroy(); - } - - closeSearchDialog() { - this.dialog?.nativeElement.close(); - this.onClose.next(); - } - - updateSearchQuery(query: string) { - this.search.updateSearchQuery(query); - } - - private updateActiveItemWhenResultsChanged(): void { - this.items?.changes.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { - // Change detection should be run before execute `setFirstItemActive`. - Promise.resolve().then(() => { - this.keyManager?.setFirstItemActive(); - }); - }); - } - - private navigateToTheActiveItem(): void { - const activeItemLink: string | undefined = this.keyManager?.activeItem?.item?.url; - - if (!activeItemLink) { - return; - } - - this.router.navigateByUrl(this.relativeLink.transform(activeItemLink)); - this.onClose.next(); - } - - private scrollToActiveItem(): void { - this.keyManager?.change.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { - this.keyManager?.activeItem?.scrollIntoView(); - }); - } -} diff --git a/adev/shared/src/lib/components/select/select.component.html b/adev/shared/src/lib/components/select/select.component.html deleted file mode 100644 index 872a145a3d5..00000000000 --- a/adev/shared/src/lib/components/select/select.component.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/adev/shared/src/lib/components/select/select.component.scss b/adev/shared/src/lib/components/select/select.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/adev/shared/src/lib/components/select/select.component.spec.ts b/adev/shared/src/lib/components/select/select.component.spec.ts deleted file mode 100644 index 1433caacef1..00000000000 --- a/adev/shared/src/lib/components/select/select.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {Select} from './select.component'; - -describe('Select', () => { - let component: Select; - let fixture: ComponentFixture - -
- diff --git a/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.scss b/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.scss deleted file mode 100644 index 06eccab25aa..00000000000 --- a/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.scss +++ /dev/null @@ -1,78 +0,0 @@ -:host, -label { - display: inline-flex; - gap: 0.5em; - align-items: center; -} - -.adev-label { - font-size: 0.875rem; - font-style: normal; - font-weight: 500; - line-height: 160%; // 1.4rem - letter-spacing: -0.00875rem; - color: var(--quaternary-contrast); -} - -.adev-toggle { - position: relative; - display: inline-block; - - width: 3rem; - height: 1.5rem; - border: 1px solid var(--senary-contrast); - border-radius: 34px; - - input { - opacity: 0; - width: 0; - height: 0; - } -} - -.adev-slider { - position: absolute; - cursor: pointer; - border-radius: 34px; - inset: 0; - background-color: var(--septenary-contrast); - transition: background-color 0.3s ease, border-color 0.3s ease; - - // background - &::before { - content: ''; - position: absolute; - inset: 0; - border-radius: 34px; - background: var(--pink-to-purple-horizontal-gradient); - opacity: 0; - transition: opacity 0.3s ease; - } - - // toggle knob - &::after { - position: absolute; - content: ''; - height: 1.25rem; - width: 1.25rem; - left: 0.125rem; - bottom: 0.125rem; - background-color: var(--page-background); - transition: transform 0.3s ease, background-color 0.3s ease; - border-radius: 50%; - } -} - -input { - &:checked + .adev-slider { - // background - &::before { - opacity: 1; - } - - // toggle knob - &::after { - transform: translateX(1.5rem); - } - } -} diff --git a/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.spec.ts b/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.spec.ts deleted file mode 100644 index 2a2d15f3eed..00000000000 --- a/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {SlideToggle} from './slide-toggle.component'; - -describe('SlideToggle', () => { - let component: SlideToggle; - let fixture: ComponentFixture; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [SlideToggle], - }); - fixture = TestBed.createComponent(SlideToggle); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should toggle the value when clicked', () => { - expect(component['checked']()).toBeFalse(); - - const buttonElement = fixture.nativeElement.querySelector('input'); - buttonElement.click(); - - expect(component['checked']()).toBeTrue(); - }); - - it('should call onChange and onTouched when toggled', () => { - const onChangeSpy = jasmine.createSpy('onChangeSpy'); - const onTouchedSpy = jasmine.createSpy('onTouchedSpy'); - component.registerOnChange(onChangeSpy); - component.registerOnTouched(onTouchedSpy); - - component.toggle(); - - expect(onChangeSpy).toHaveBeenCalled(); - expect(onChangeSpy).toHaveBeenCalledWith(true); - expect(onTouchedSpy).toHaveBeenCalled(); - }); - - it('should set active class for button when is checked', () => { - component.writeValue(true); - fixture.detectChanges(); - const buttonElement: HTMLButtonElement = fixture.nativeElement.querySelector('input'); - expect(buttonElement.classList.contains('adev-toggle-active')).toBeTrue(); - - component.writeValue(false); - fixture.detectChanges(); - expect(buttonElement.classList.contains('adev-toggle-active')).toBeFalse(); - }); -}); diff --git a/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.ts b/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.ts deleted file mode 100644 index 671426c0d40..00000000000 --- a/adev/shared/src/lib/components/slide-toggle/slide-toggle.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, Input, forwardRef, signal} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; - -@Component({ - selector: 'docs-slide-toggle', - standalone: true, - imports: [CommonModule], - templateUrl: './slide-toggle.component.html', - styleUrls: ['./slide-toggle.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SlideToggle), - multi: true, - }, - ], -}) -export class SlideToggle implements ControlValueAccessor { - @Input({required: true}) buttonId!: string; - @Input({required: true}) label!: string; - @Input() disabled = false; - - // Implemented as part of ControlValueAccessor. - private onChange: (value: boolean) => void = (_: boolean) => {}; - private onTouched: () => void = () => {}; - - protected readonly checked = signal(false); - - // Implemented as part of ControlValueAccessor. - writeValue(value: boolean): void { - this.checked.set(value); - } - - // Implemented as part of ControlValueAccessor. - registerOnChange(fn: any): void { - this.onChange = fn; - } - - // Implemented as part of ControlValueAccessor. - registerOnTouched(fn: any): void { - this.onTouched = fn; - } - - // Implemented as part of ControlValueAccessor. - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - } - - // Toggles the checked state of the slide-toggle. - toggle(): void { - if (this.disabled) { - return; - } - - this.checked.update((checked) => !checked); - this.onChange(this.checked()); - this.onTouched(); - } -} diff --git a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.html b/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.html deleted file mode 100644 index e401c1d7635..00000000000 --- a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.html +++ /dev/null @@ -1,31 +0,0 @@ - diff --git a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.scss b/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.scss deleted file mode 100644 index 906a39d7c13..00000000000 --- a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.scss +++ /dev/null @@ -1,91 +0,0 @@ -:host { - display: flex; - flex-direction: column; - position: fixed; - right: 16px; - top: 0; - height: fit-content; - width: 14rem; - padding-inline: 1rem; - max-height: 100vh; - overflow-y: scroll; - - aside { - margin-bottom: 2rem; - } - - @media only screen and (max-width: 1430px) { - position: relative; - right: 0; - max-height: min-content; - width: 100%; - } - - .docs-title { - font-size: 1.25rem; - margin-block-start: var(--layout-padding); - } - - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - cursor: pointer; - } - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--septenary-contrast); - border-radius: 10px; - transition: background-color 0.3s ease; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quinary-contrast); - } - - .adev-faceted-list-item { - font-size: 0.875rem; - - a { - display: block; // to prevent overflow from the li parent - padding: 0.5rem 0.5rem 0.5rem 1rem; - font-weight: 500; - } - - &.docs-toc-item-h3 a { - padding-inline-start: 2rem; - } - } -} - -button { - background: transparent; - border: none; - font-size: 0.875rem; - font-family: var(--inter-font); - display: flex; - align-items: center; - margin: 0.5rem 0; - color: var(--tertiary-contrast); - transition: color 0.3s ease; - cursor: pointer; - - docs-icon { - margin-inline-end: 0.35rem; - opacity: 0.6; - transition: opacity 0.3s ease; - } - - &:hover { - docs-icon { - opacity: 1; - } - } - - @media only screen and (max-width: 1430px) { - display: none; - } -} diff --git a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.spec.ts b/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.spec.ts deleted file mode 100644 index ee8f40c9cde..00000000000 --- a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {TableOfContents} from './table-of-contents.component'; -import {RouterTestingModule} from '@angular/router/testing'; -import {TableOfContentsItem, TableOfContentsLevel} from '../../interfaces'; -import {TableOfContentsLoader} from '../../services/table-of-contents-loader.service'; -import {TableOfContentsScrollSpy} from '../../services/table-of-contents-scroll-spy.service'; -import {WINDOW} from '../../providers'; - -describe('TableOfContents', () => { - let component: TableOfContents; - let fixture: ComponentFixture; - let tableOfContentsLoaderSpy: jasmine.SpyObj; - let scrollSpy: jasmine.SpyObj; - const items: TableOfContentsItem[] = [ - { - title: 'Heading 2', - top: 0, - id: 'item-heading-2', - level: TableOfContentsLevel.H2, - }, - { - title: 'First Heading 3', - top: 100, - id: 'first-item-heading-3', - level: TableOfContentsLevel.H3, - }, - { - title: 'Second Heading 3', - top: 200, - id: 'second-item-heading-3', - level: TableOfContentsLevel.H3, - }, - ]; - const fakeWindow = { - addEventListener: () => {}, - removeEventListener: () => {}, - }; - - beforeEach(async () => { - scrollSpy = jasmine.createSpyObj('TableOfContentsScrollSpy', [ - 'startListeningToScroll', - 'activeItemId', - 'scrollbarThumbOnTop', - ]); - scrollSpy.startListeningToScroll.and.returnValue(); - scrollSpy.activeItemId.and.returnValue(items[0].id); - scrollSpy.scrollbarThumbOnTop.and.returnValue(false); - tableOfContentsLoaderSpy = jasmine.createSpyObj( - 'TableOfContentsLoader', - ['buildTableOfContent'], - ); - tableOfContentsLoaderSpy.buildTableOfContent.and.returnValue(); - tableOfContentsLoaderSpy.tableOfContentItems = items; - - await TestBed.configureTestingModule({ - imports: [TableOfContents, RouterTestingModule], - providers: [ - { - provide: WINDOW, - useValue: fakeWindow, - }, - ], - }).compileComponents(); - TestBed.overrideProvider(TableOfContentsLoader, { - useValue: tableOfContentsLoaderSpy, - }); - TestBed.overrideProvider(TableOfContentsScrollSpy, { - useValue: scrollSpy, - }); - fixture = TestBed.createComponent(TableOfContents); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should call scrollToTop when user click on Back to the top button', () => { - const spy = spyOn(component, 'scrollToTop'); - - fixture.detectChanges(); - - const button: HTMLButtonElement = fixture.nativeElement.querySelector('button'); - button.click(); - - expect(spy).toHaveBeenCalledOnceWith(); - }); - - it('should render items when tableOfContentItems has value', () => { - fixture.detectChanges(); - - const renderedItems = fixture.nativeElement.querySelectorAll('li'); - - expect(renderedItems.length).toBe(3); - expect(component.tableOfContentItems().length).toBe(3); - }); - - it('should append level class to element', () => { - fixture.detectChanges(); - - const h2Items = fixture.nativeElement.querySelectorAll('li.docs-toc-item-h2'); - const h3Items = fixture.nativeElement.querySelectorAll('li.docs-toc-item-h3'); - - expect(h2Items.length).toBe(1); - expect(h3Items.length).toBe(2); - }); - - it('should append active class when item is active', () => { - fixture.detectChanges(); - - const activeItem = fixture.nativeElement.querySelector('.adev-faceted-list-item-active'); - - expect(activeItem).toBeTruthy(); - }); -}); diff --git a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.ts b/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.ts deleted file mode 100644 index acfc63e719c..00000000000 --- a/adev/shared/src/lib/components/table-of-contents/table-of-contents.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgFor, NgIf} from '@angular/common'; -import {Component, Input, computed, inject} from '@angular/core'; -import {RouterLink} from '@angular/router'; -import {TableOfContentsLevel} from '../../interfaces'; -import {TableOfContentsLoader} from '../../services/table-of-contents-loader.service'; -import {TableOfContentsScrollSpy} from '../../services/table-of-contents-scroll-spy.service'; -import {IconComponent} from '../icon/icon.component'; - -@Component({ - selector: 'docs-table-of-contents', - standalone: true, - providers: [TableOfContentsLoader, TableOfContentsScrollSpy], - templateUrl: './table-of-contents.component.html', - styleUrls: ['./table-of-contents.component.scss'], - imports: [NgIf, NgFor, RouterLink, IconComponent], -}) -export class TableOfContents { - // Element that contains the content from which the Table of Contents is built - @Input({required: true}) contentSourceElement!: HTMLElement; - - private readonly scrollSpy = inject(TableOfContentsScrollSpy); - private readonly tableOfContentsLoader = inject(TableOfContentsLoader); - - activeItemId = this.scrollSpy.activeItemId; - shouldDisplayScrollToTop = computed(() => !this.scrollSpy.scrollbarThumbOnTop()); - TableOfContentsLevel = TableOfContentsLevel; - - tableOfContentItems() { - return this.tableOfContentsLoader.tableOfContentItems; - } - - ngAfterViewInit() { - this.tableOfContentsLoader.buildTableOfContent(this.contentSourceElement); - this.scrollSpy.startListeningToScroll(this.contentSourceElement); - } - - scrollToTop(): void { - this.scrollSpy.scrollToTop(); - } -} diff --git a/adev/shared/src/lib/components/text-field/text-field.component.html b/adev/shared/src/lib/components/text-field/text-field.component.html deleted file mode 100644 index 67423ee4230..00000000000 --- a/adev/shared/src/lib/components/text-field/text-field.component.html +++ /dev/null @@ -1,12 +0,0 @@ -@if (!hideIcon) { -search -} - diff --git a/adev/shared/src/lib/components/text-field/text-field.component.scss b/adev/shared/src/lib/components/text-field/text-field.component.scss deleted file mode 100644 index a466719c80d..00000000000 --- a/adev/shared/src/lib/components/text-field/text-field.component.scss +++ /dev/null @@ -1,9 +0,0 @@ -// search field -.adev-text-field { - font-size: 1.125rem; -} - -// filter field on api reference list -docs-icon + .adev-text-field { - font-size: 1rem; -} diff --git a/adev/shared/src/lib/components/text-field/text-field.component.spec.ts b/adev/shared/src/lib/components/text-field/text-field.component.spec.ts deleted file mode 100644 index 52166579b6e..00000000000 --- a/adev/shared/src/lib/components/text-field/text-field.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -import {TextField} from './text-field.component'; - -describe('TextField', () => { - let component: TextField; - let fixture: ComponentFixture; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [TextField], - }); - fixture = TestBed.createComponent(TextField); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/adev/shared/src/lib/components/text-field/text-field.component.ts b/adev/shared/src/lib/components/text-field/text-field.component.ts deleted file mode 100644 index fbf75d81c62..00000000000 --- a/adev/shared/src/lib/components/text-field/text-field.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - Component, - ElementRef, - Input, - ViewChild, - afterNextRender, - forwardRef, - signal, -} from '@angular/core'; -import {CommonModule} from '@angular/common'; -import {ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR} from '@angular/forms'; -import {IconComponent} from '../icon/icon.component'; - -@Component({ - selector: 'docs-text-field', - standalone: true, - imports: [CommonModule, FormsModule, IconComponent], - templateUrl: './text-field.component.html', - styleUrls: ['./text-field.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => TextField), - multi: true, - }, - ], - host: { - class: 'adev-form-element', - }, -}) -export class TextField implements ControlValueAccessor { - @ViewChild('inputRef') private input?: ElementRef; - - @Input() name: string | null = null; - @Input() placeholder: string | null = null; - @Input() disabled = false; - @Input() hideIcon = false; - @Input() autofocus = false; - - // Implemented as part of ControlValueAccessor. - private onChange: (value: string) => void = (_: string) => {}; - private onTouched: () => void = () => {}; - - protected readonly value = signal(null); - - constructor() { - afterNextRender(() => { - if (this.autofocus) { - this.input?.nativeElement.focus(); - } - }); - } - - // Implemented as part of ControlValueAccessor. - writeValue(value: string): void { - this.value.set(value); - } - - // Implemented as part of ControlValueAccessor. - registerOnChange(fn: any): void { - this.onChange = fn; - } - - // Implemented as part of ControlValueAccessor. - registerOnTouched(fn: any): void { - this.onTouched = fn; - } - - // Implemented as part of ControlValueAccessor. - setDisabledState?(isDisabled: boolean): void { - this.disabled = isDisabled; - } - - setValue(value: string): void { - if (this.disabled) { - return; - } - - this.value.set(value); - this.onChange(value); - this.onTouched(); - } -} diff --git a/adev/shared/src/lib/constants/delay.ts b/adev/shared/src/lib/constants/delay.ts deleted file mode 100644 index fd62285861c..00000000000 --- a/adev/shared/src/lib/constants/delay.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// Used for both the table of contents and the home animation -export const RESIZE_EVENT_DELAY = 500; - -// Used for the home animation -export const WEBGL_LOADED_DELAY = 250; diff --git a/adev/shared/src/lib/constants/index.ts b/adev/shared/src/lib/constants/index.ts deleted file mode 100644 index 092d17d72e1..00000000000 --- a/adev/shared/src/lib/constants/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './delay'; diff --git a/adev/shared/src/lib/directives/click-outside/click-outside.directive.ts b/adev/shared/src/lib/directives/click-outside/click-outside.directive.ts deleted file mode 100644 index 11ab10a738a..00000000000 --- a/adev/shared/src/lib/directives/click-outside/click-outside.directive.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DOCUMENT} from '@angular/common'; -import {Directive, ElementRef, EventEmitter, Input, Output, inject} from '@angular/core'; - -@Directive({ - selector: '[docsClickOutside]', - standalone: true, - host: { - '(document:click)': 'onClick($event)', - }, -}) -export class ClickOutside { - @Input('docsClickOutsideIgnore') public ignoredElementsIds: string[] = []; - @Output('docsClickOutside') public clickOutside = new EventEmitter(); - - private readonly document = inject(DOCUMENT); - private readonly elementRef = inject(ElementRef); - - onClick($event: PointerEvent): void { - if ( - !this.elementRef.nativeElement.contains($event.target) && - !this.wasClickedOnIgnoredElement($event) - ) { - this.clickOutside.emit(); - } - } - - private wasClickedOnIgnoredElement($event: PointerEvent): boolean { - if (this.ignoredElementsIds.length === 0) { - return false; - } - - return this.ignoredElementsIds.some((elementId) => { - const element = this.document.getElementById(elementId); - const target = $event.target as Node; - const contains = element?.contains(target); - return contains; - }); - } -} diff --git a/adev/shared/src/lib/directives/external-link/external-link.directive.ts b/adev/shared/src/lib/directives/external-link/external-link.directive.ts deleted file mode 100644 index f3a3c003cbc..00000000000 --- a/adev/shared/src/lib/directives/external-link/external-link.directive.ts +++ /dev/null @@ -1,44 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {isPlatformBrowser} from '@angular/common'; -import {Directive, ElementRef, OnInit, PLATFORM_ID, inject} from '@angular/core'; -import {WINDOW, isExternalLink} from '@angular/docs-shared'; - -/** - * The directive will set target of anchor elements to '_blank' for the external links. - * We can opt-out this behavior by adding `noBlankForExternalLink` attribute to anchor element. - */ -@Directive({ - selector: 'a[href]:not([noBlankForExternalLink])', - host: { - '[attr.target]': 'target', - }, - standalone: true, -}) -export class ExternalLink implements OnInit { - private readonly anchor: ElementRef = inject(ElementRef); - private readonly platformId = inject(PLATFORM_ID); - private readonly window = inject(WINDOW); - - target?: '_blank' | '_self' | '_parent' | '_top' | ''; - - ngOnInit(): void { - this.setAnchorTarget(); - } - - private setAnchorTarget(): void { - if (!isPlatformBrowser(this.platformId)) { - return; - } - - if (isExternalLink(this.anchor.nativeElement.href, this.window.location.origin)) { - this.target = '_blank'; - } - } -} diff --git a/adev/shared/src/lib/directives/index.ts b/adev/shared/src/lib/directives/index.ts deleted file mode 100644 index 1f636703bca..00000000000 --- a/adev/shared/src/lib/directives/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './click-outside/click-outside.directive'; -export * from './external-link/external-link.directive'; diff --git a/adev/shared/src/lib/directives/search-item/search-item.directive.ts b/adev/shared/src/lib/directives/search-item/search-item.directive.ts deleted file mode 100644 index 1ed77739473..00000000000 --- a/adev/shared/src/lib/directives/search-item/search-item.directive.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Directive, ElementRef, Input, inject} from '@angular/core'; -import {Highlightable} from '@angular/cdk/a11y'; -import {SearchResult} from '../../interfaces/search-results'; - -@Directive({ - selector: '[docsSearchItem]', - standalone: true, - host: { - '[class.active]': 'isActive', - }, -}) -export class SearchItem implements Highlightable { - @Input() item?: SearchResult; - @Input() disabled = false; - - private readonly elementRef = inject(ElementRef); - - private _isActive = false; - - protected get isActive() { - return this._isActive; - } - - setActiveStyles(): void { - this._isActive = true; - } - - setInactiveStyles(): void { - this._isActive = false; - } - - getLabel(): string { - if (!this.item?.hierarchy) { - return ''; - } - const {hierarchy} = this.item; - return `${hierarchy.lvl0}${hierarchy.lvl1}${hierarchy.lvl2}`; - } - - scrollIntoView(): void { - this.elementRef?.nativeElement.scrollIntoView({block: 'nearest'}); - } -} diff --git a/adev/shared/src/lib/icons/chevron.svg b/adev/shared/src/lib/icons/chevron.svg deleted file mode 100644 index ef2a71d4b79..00000000000 --- a/adev/shared/src/lib/icons/chevron.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/adev/shared/src/lib/icons/github.svg b/adev/shared/src/lib/icons/github.svg deleted file mode 100644 index fe1edac2eae..00000000000 --- a/adev/shared/src/lib/icons/github.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/adev/shared/src/lib/icons/twitter.svg b/adev/shared/src/lib/icons/twitter.svg deleted file mode 100644 index 0cda36e3da2..00000000000 --- a/adev/shared/src/lib/icons/twitter.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/adev/shared/src/lib/icons/youtube.svg b/adev/shared/src/lib/icons/youtube.svg deleted file mode 100644 index ac27400116c..00000000000 --- a/adev/shared/src/lib/icons/youtube.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/adev/shared/src/lib/interfaces/algolia-config.ts b/adev/shared/src/lib/interfaces/algolia-config.ts deleted file mode 100644 index 341f2c9263e..00000000000 --- a/adev/shared/src/lib/interfaces/algolia-config.ts +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export interface AlgoliaConfig { - apiKey: string; - appId: string; - indexName: string; -} diff --git a/adev/shared/src/lib/interfaces/code-example.ts b/adev/shared/src/lib/interfaces/code-example.ts deleted file mode 100644 index 5efe4435190..00000000000 --- a/adev/shared/src/lib/interfaces/code-example.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Type} from '@angular/core'; - -/** - * Map of the examples, values are functions which returns the promise of the component type, which will be displayed as preview in the ExampleViewer component - */ -export interface CodeExamplesMap { - [id: string]: () => Promise>; -} - -export interface Snippet { - /** Title of the code snippet */ - title?: string; - /** Name of the file. */ - name: string; - /** Content of code snippet */ - content: string; - /** Text in following format `start-end`. Start and end are numbers, based on them provided range of lines will be displayed in collapsed mode */ - visibleLinesRange?: string; -} - -export interface ExampleMetadata { - /** Numeric id of example, used to generate unique link to the example */ - id: number; - /** Title of the example. */ - title?: string; - /** Path to the preview component */ - path?: string; - /** List of files which are part of the example. */ - files: Snippet[]; - /** True when ExampleViewer should have preview */ - preview: boolean; -} diff --git a/adev/shared/src/lib/interfaces/doc-content.ts b/adev/shared/src/lib/interfaces/doc-content.ts deleted file mode 100644 index cb379b5aca0..00000000000 --- a/adev/shared/src/lib/interfaces/doc-content.ts +++ /dev/null @@ -1,17 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/** Represents a documentation page data. */ -export interface DocContent { - /** The unique identifier for this document. */ - id: string; - /** The HTML to display in the doc viewer. */ - contents: string; - /** The unique title for this document page. */ - title?: string; -} diff --git a/adev/shared/src/lib/interfaces/docs-content-loader.ts b/adev/shared/src/lib/interfaces/docs-content-loader.ts deleted file mode 100644 index 6f4ac99ab64..00000000000 --- a/adev/shared/src/lib/interfaces/docs-content-loader.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DocContent} from './doc-content'; - -/** The service responsible for fetching static content for docs pages */ -export interface DocsContentLoader { - getContent(path: string): Promise; -} diff --git a/adev/shared/src/lib/interfaces/environment.ts b/adev/shared/src/lib/interfaces/environment.ts deleted file mode 100644 index 969078e3301..00000000000 --- a/adev/shared/src/lib/interfaces/environment.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {AlgoliaConfig} from './algolia-config'; - -export interface Environment { - production: boolean; - algolia: AlgoliaConfig; - googleAnalyticsId: string; -} diff --git a/adev/shared/src/lib/interfaces/example-viewer-content-loader.ts b/adev/shared/src/lib/interfaces/example-viewer-content-loader.ts deleted file mode 100644 index 393274647c9..00000000000 --- a/adev/shared/src/lib/interfaces/example-viewer-content-loader.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Type} from '@angular/core'; - -/** The service responsible for fetching the type of Component to display in the preview */ -export interface ExampleViewerContentLoader { - /** Returns type of Component to display in the preview */ - loadPreview(id: string): Promise>; -} diff --git a/adev/shared/src/lib/interfaces/index.ts b/adev/shared/src/lib/interfaces/index.ts deleted file mode 100644 index 37decbe5e9c..00000000000 --- a/adev/shared/src/lib/interfaces/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './code-example'; -export * from './doc-content'; -export * from './docs-content-loader'; -export * from './example-viewer-content-loader'; -export * from './environment'; -export * from './navigation-item'; -export * from './table-of-contents-item'; diff --git a/adev/shared/src/lib/interfaces/navigation-item.ts b/adev/shared/src/lib/interfaces/navigation-item.ts deleted file mode 100644 index 0dfdd177e61..00000000000 --- a/adev/shared/src/lib/interfaces/navigation-item.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export interface NavigationItem { - label?: string; - path?: string; - children?: NavigationItem[]; - isExternal?: boolean; - isExpanded?: boolean; - level?: number; - parent?: NavigationItem; - contentPath?: string; -} diff --git a/adev/shared/src/lib/interfaces/search-results.ts b/adev/shared/src/lib/interfaces/search-results.ts deleted file mode 100644 index a5beb920919..00000000000 --- a/adev/shared/src/lib/interfaces/search-results.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* The interface represents Algolia search result item. */ -export interface SearchResult { - /* The url link to the search result page */ - url?: string; - /* The hierarchy of the item */ - hierarchy?: Hierarchy; - /* The unique id of the search result item */ - objectID: string; -} - -/* The hierarchy of the item */ -export interface Hierarchy { - /* It's kind of the page i.e `Docs`, `Tutorials`, `Reference` etc. */ - lvl0: string | null; - /* Typicaly it's the content of H1 of the page */ - lvl1: string | null; - /* Typicaly it's the content of H2 of the page */ - lvl2: string | null; - lvl3: string | null; - lvl4: string | null; - lvl5: string | null; - lvl6: string | null; -} diff --git a/adev/shared/src/lib/interfaces/table-of-contents-item.ts b/adev/shared/src/lib/interfaces/table-of-contents-item.ts deleted file mode 100644 index 11bf1f256dc..00000000000 --- a/adev/shared/src/lib/interfaces/table-of-contents-item.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export enum TableOfContentsLevel { - H2 = 'h2', - H3 = 'h3', -} - -/** Represents a table of content item. */ -export interface TableOfContentsItem { - /** The url fragment of specific section */ - id: string; - /** The level of the item. */ - level: TableOfContentsLevel; - /** The unique title for this document page. */ - title: string; - /** The top offset px of the heading */ - top: number; -} diff --git a/adev/shared/src/lib/pipes/is-active-navigation-item.pipe.ts b/adev/shared/src/lib/pipes/is-active-navigation-item.pipe.ts deleted file mode 100644 index 3f6d72df99c..00000000000 --- a/adev/shared/src/lib/pipes/is-active-navigation-item.pipe.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Pipe, PipeTransform} from '@angular/core'; -import {NavigationItem} from '../interfaces'; - -@Pipe({ - name: 'isActiveNavigationItem', - standalone: true, -}) -export class IsActiveNavigationItem implements PipeTransform { - // Check whether provided item: `itemToCheck` should be marked as active, based on `activeItem`. - // In addition to `activeItem`, we should mark all its parents, grandparents, etc. as active. - transform(itemToCheck: NavigationItem, activeItem: NavigationItem | null): boolean { - let node = activeItem?.parent; - - while (node) { - if (node === itemToCheck) { - return true; - } - - node = node.parent; - } - - return false; - } -} diff --git a/adev/shared/src/lib/pipes/relative-link.pipe.ts b/adev/shared/src/lib/pipes/relative-link.pipe.ts deleted file mode 100644 index 052585ee5d8..00000000000 --- a/adev/shared/src/lib/pipes/relative-link.pipe.ts +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Pipe, PipeTransform} from '@angular/core'; -import {normalizePath, removeTrailingSlash} from '../utils'; - -@Pipe({ - name: 'relativeLink', - standalone: true, -}) -export class RelativeLink implements PipeTransform { - transform(absoluteUrl: string, result: 'relative' | 'pathname' | 'hash' = 'relative'): string { - const url = new URL(normalizePath(absoluteUrl)); - - if (result === 'hash') { - return url.hash?.substring(1) ?? ''; - } - if (result === 'pathname') { - return `${removeTrailingSlash(normalizePath(url.pathname))}`; - } - return `${removeTrailingSlash(normalizePath(url.pathname))}${url.hash ?? ''}`; - } -} diff --git a/adev/shared/src/lib/providers/docs-content-loader.ts b/adev/shared/src/lib/providers/docs-content-loader.ts deleted file mode 100644 index ba6a44f7b61..00000000000 --- a/adev/shared/src/lib/providers/docs-content-loader.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {InjectionToken, inject} from '@angular/core'; -import {DocsContentLoader} from '../interfaces/docs-content-loader'; -import {ResolveFn} from '@angular/router'; -import {DocContent} from '../interfaces'; - -export const DOCS_CONTENT_LOADER = new InjectionToken('DOCS_CONTENT_LOADER'); - -export function contentResolver(contentPath: string): ResolveFn { - return () => inject(DOCS_CONTENT_LOADER).getContent(contentPath); -} diff --git a/adev/shared/src/lib/providers/environment.ts b/adev/shared/src/lib/providers/environment.ts deleted file mode 100644 index d4babd1ee57..00000000000 --- a/adev/shared/src/lib/providers/environment.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {InjectionToken} from '@angular/core'; -import {Environment} from '../interfaces'; - -export const ENVIRONMENT = new InjectionToken('ENVIRONMENT'); diff --git a/adev/shared/src/lib/providers/example-viewer-content-loader.ts b/adev/shared/src/lib/providers/example-viewer-content-loader.ts deleted file mode 100644 index e9b9c693176..00000000000 --- a/adev/shared/src/lib/providers/example-viewer-content-loader.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {InjectionToken} from '@angular/core'; -import {ExampleViewerContentLoader} from '../interfaces/example-viewer-content-loader'; - -export const EXAMPLE_VIEWER_CONTENT_LOADER = new InjectionToken( - 'EXAMPLE_VIEWER_CONTENT_LOADER', -); diff --git a/adev/shared/src/lib/providers/index.ts b/adev/shared/src/lib/providers/index.ts deleted file mode 100644 index a9b4a001685..00000000000 --- a/adev/shared/src/lib/providers/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './docs-content-loader'; -export * from './environment'; -export * from './example-viewer-content-loader'; -export * from './is-search-dialog-open'; -export * from './local-storage'; -export * from './previews-components'; -export * from './window'; diff --git a/adev/shared/src/lib/providers/is-search-dialog-open.ts b/adev/shared/src/lib/providers/is-search-dialog-open.ts deleted file mode 100644 index ccf2ff1a6aa..00000000000 --- a/adev/shared/src/lib/providers/is-search-dialog-open.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {InjectionToken, signal} from '@angular/core'; - -export const IS_SEARCH_DIALOG_OPEN = new InjectionToken('', { - providedIn: 'root', - factory: () => signal(false), -}); diff --git a/adev/shared/src/lib/providers/local-storage.ts b/adev/shared/src/lib/providers/local-storage.ts deleted file mode 100644 index c5ac69e5cd3..00000000000 --- a/adev/shared/src/lib/providers/local-storage.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {isPlatformBrowser} from '@angular/common'; -import {InjectionToken, PLATFORM_ID, inject} from '@angular/core'; - -export const LOCAL_STORAGE = new InjectionToken('LOCAL_STORAGE', { - providedIn: 'root', - factory: () => getStorage(inject(PLATFORM_ID)), -}); - -const getStorage = (platformId: Object): Storage | null => { - // Prerendering: localStorage is undefined for prerender build - return isPlatformBrowser(platformId) ? new LocalStorage() : null; -}; - -/** - * LocalStorage is wrapper class for localStorage, operations can fail due to various reasons, - * such as browser restrictions or storage limits being exceeded. A wrapper is providing error handling. - */ -class LocalStorage implements Storage { - get length(): number { - try { - return localStorage.length; - } catch { - return 0; - } - } - - clear(): void { - try { - localStorage.clear(); - } catch {} - } - - getItem(key: string): string | null { - try { - return localStorage.getItem(key); - } catch { - return null; - } - } - - key(index: number): string | null { - try { - return localStorage.key(index); - } catch { - return null; - } - } - - removeItem(key: string): void { - try { - localStorage.removeItem(key); - } catch {} - } - - setItem(key: string, value: string): void { - try { - localStorage.setItem(key, value); - } catch {} - } -} diff --git a/adev/shared/src/lib/providers/previews-components.ts b/adev/shared/src/lib/providers/previews-components.ts deleted file mode 100644 index a36ee38363b..00000000000 --- a/adev/shared/src/lib/providers/previews-components.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {InjectionToken} from '@angular/core'; -import {CodeExamplesMap} from '../interfaces/code-example'; - -export const PREVIEWS_COMPONENTS = new InjectionToken('PREVIEWS_COMPONENTS'); diff --git a/adev/shared/src/lib/providers/window.ts b/adev/shared/src/lib/providers/window.ts deleted file mode 100644 index 155bca31ce2..00000000000 --- a/adev/shared/src/lib/providers/window.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {InjectionToken} from '@angular/core'; - -// Providing window using injection token could increase testability and portability (i.e SSR don't have a real browser environment). -export const WINDOW = new InjectionToken('WINDOW'); - -// The project uses prerendering, to resolve issue: 'window is not defined', we should get window from DOCUMENT. -// As it is recommended here: https://github.com/angular/universal/blob/main/docs/gotchas.md#strategy-1-injection -export function windowProvider(document: Document) { - return document.defaultView; -} diff --git a/adev/shared/src/lib/services/index.ts b/adev/shared/src/lib/services/index.ts deleted file mode 100644 index 635b90f7afd..00000000000 --- a/adev/shared/src/lib/services/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './navigation-state.service'; -export {TOC_SKIP_CONTENT_MARKER} from './table-of-contents-loader.service'; -export * from './search.service'; diff --git a/adev/shared/src/lib/services/navigation-state.service.ts b/adev/shared/src/lib/services/navigation-state.service.ts deleted file mode 100644 index 5f45cc58e06..00000000000 --- a/adev/shared/src/lib/services/navigation-state.service.ts +++ /dev/null @@ -1,115 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Injectable, inject, signal} from '@angular/core'; -import {NavigationItem} from '../interfaces'; -import {Router} from '@angular/router'; - -@Injectable({ - providedIn: 'root', -}) -export class NavigationState { - private readonly router = inject(Router); - - private readonly _activeNavigationItem = signal(null); - private readonly _expandedItems = signal([]); - private readonly _isMobileNavVisible = signal(false); - - primaryActiveRouteItem = signal(null); - activeNavigationItem = this._activeNavigationItem.asReadonly(); - expandedItems = this._expandedItems.asReadonly(); - isMobileNavVisible = this._isMobileNavVisible.asReadonly(); - - async toggleItem(item: NavigationItem): Promise { - if (!item.children) { - return; - } - - if (item.isExpanded) { - this.collapse(item); - } else if (item.children && item.children.length > 0 && item.children[0].path) { - // It resolves false, when the user has displayed the page, then changed the slide to a secondary navigation component - // and wants to reopen the slide, where the first item is the currently displayed page - const navigationSucceeds = await this.navigateToFirstPageOfTheCategory(item.children[0].path); - - if (!navigationSucceeds) { - this.expand(item); - } - } - } - - cleanExpandedState(): void { - this._expandedItems.set([]); - } - - expandItemHierarchy( - item: NavigationItem, - shouldExpand: (item: NavigationItem) => boolean, - skipExpandPredicateFn?: (item: NavigationItem) => boolean, - ): void { - if (skipExpandPredicateFn && skipExpandPredicateFn(item)) { - // When `skipExpandPredicateFn` returns `true` then we should trigger `cleanExpandedState` - // to be sure that first navigation slide will be displayed. - this.cleanExpandedState(); - return; - } - // Returns item when parent node was already expanded - const parentItem = this._expandedItems().find( - (expandedItem) => - item.parent?.label === expandedItem.label && item.parent?.path === expandedItem.path, - ); - - if (parentItem) { - // If the parent item is expanded, then we should display all expanded items up to the active item level. - // This provides us with an appropriate list of expanded elements also when the user navigates using browser buttons. - this._expandedItems.update((expandedItems) => - expandedItems.filter( - (item) => - item.level !== undefined && - parentItem.level !== undefined && - item.level <= parentItem.level, - ), - ); - } else { - let itemsToExpand: NavigationItem[] = []; - - let node = item.parent; - - while (node && shouldExpand(node)) { - itemsToExpand.push({...node, isExpanded: true}); - node = node.parent; - } - - this._expandedItems.set(itemsToExpand.reverse()); - } - } - - setActiveNavigationItem(item: NavigationItem | null): void { - this._activeNavigationItem.set(item); - } - - setMobileNavigationListVisibility(isVisible: boolean): void { - this._isMobileNavVisible.set(isVisible); - } - - private expand(item: NavigationItem): void { - // Add item to the expanded items list - this._expandedItems.update((expandedItems) => { - return [...(expandedItems ?? []), {...item, isExpanded: true}]; - }); - } - - private collapse(item: NavigationItem): void { - item.isExpanded = false; - this._expandedItems.update((expandedItems) => expandedItems.slice(0, -1)); - } - - private async navigateToFirstPageOfTheCategory(path: string): Promise { - return this.router.navigateByUrl(path); - } -} diff --git a/adev/shared/src/lib/services/search.service.ts b/adev/shared/src/lib/services/search.service.ts deleted file mode 100644 index e31787b9a97..00000000000 --- a/adev/shared/src/lib/services/search.service.ts +++ /dev/null @@ -1,85 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Injectable, afterNextRender, inject, signal} from '@angular/core'; -import {ENVIRONMENT} from '../providers'; -import {SearchResult} from '../interfaces/search-results'; -import {toObservable} from '@angular/core/rxjs-interop'; -import {debounceTime, filter, from, of, switchMap} from 'rxjs'; -import algoliasearch from 'algoliasearch/lite'; -import {NavigationEnd, Router} from '@angular/router'; - -export const SEARCH_DELAY = 200; -// Maximum number of facet values to return for each facet during a regular search. -export const MAX_VALUE_PER_FACET = 5; - -@Injectable({ - providedIn: 'root', -}) -export class Search { - private readonly _searchQuery = signal(''); - private readonly _searchResults = signal(undefined); - - private readonly router = inject(Router); - private readonly config = inject(ENVIRONMENT); - private readonly client = algoliasearch(this.config.algolia.appId, this.config.algolia.apiKey); - private readonly index = this.client.initIndex(this.config.algolia.indexName); - - searchQuery = this._searchQuery.asReadonly(); - searchResults = this._searchResults.asReadonly(); - - searchResults$ = toObservable(this.searchQuery).pipe( - debounceTime(SEARCH_DELAY), - switchMap((query) => { - return !!query - ? from( - this.index.search(query, { - maxValuesPerFacet: MAX_VALUE_PER_FACET, - }), - ) - : of(undefined); - }), - ); - - constructor() { - afterNextRender(() => { - this.listenToSearchResults(); - this.resetSearchQueryOnNavigationEnd(); - }); - } - - updateSearchQuery(query: string): void { - this._searchQuery.set(query); - } - - private listenToSearchResults(): void { - this.searchResults$.subscribe((response) => { - this._searchResults.set( - response ? this.getUniqueSearchResultItems(response.hits) : undefined, - ); - }); - } - - private getUniqueSearchResultItems(items: SearchResult[]): SearchResult[] { - const uniqueUrls = new Set(); - - return items.filter((item) => { - if (item.url && !uniqueUrls.has(item.url)) { - uniqueUrls.add(item.url); - return true; - } - return false; - }); - } - - private resetSearchQueryOnNavigationEnd(): void { - this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { - this.updateSearchQuery(''); - }); - } -} diff --git a/adev/shared/src/lib/services/table-of-contents-loader.service.spec.ts b/adev/shared/src/lib/services/table-of-contents-loader.service.spec.ts deleted file mode 100644 index 49f07853294..00000000000 --- a/adev/shared/src/lib/services/table-of-contents-loader.service.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {TestBed} from '@angular/core/testing'; - -import {TableOfContentsLoader} from './table-of-contents-loader.service'; - -describe('TableOfContentsLoader', () => { - let service: TableOfContentsLoader; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [TableOfContentsLoader], - }); - service = TestBed.inject(TableOfContentsLoader); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - it('should create empty table of content list when there is no headings in content', () => { - const element = createElement('element-without-headings'); - - service.buildTableOfContent(element); - - expect(service.tableOfContentItems).toEqual([]); - }); - - it('should create empty table of content list when there is only h1 elements', () => { - const element = createElement('element-with-only-h1'); - - service.buildTableOfContent(element); - - expect(service.tableOfContentItems).toEqual([]); - }); - - it('should create table of content list with h2 and h3 when h2 and h3 headings exists', () => { - const element = createElement('element-with-h1-h2-h3-h4'); - - service.buildTableOfContent(element); - - expect(service.tableOfContentItems.length).toEqual(5); - expect(service.tableOfContentItems[0].id).toBe('item-2'); - expect(service.tableOfContentItems[1].id).toBe('item-3'); - expect(service.tableOfContentItems[2].id).toBe('item-5'); - expect(service.tableOfContentItems[3].id).toBe('item-6'); - expect(service.tableOfContentItems[4].id).toBe('item-7'); - - expect(service.tableOfContentItems[0].level).toBe('h2'); - expect(service.tableOfContentItems[1].level).toBe('h3'); - expect(service.tableOfContentItems[2].level).toBe('h3'); - expect(service.tableOfContentItems[3].level).toBe('h2'); - expect(service.tableOfContentItems[4].level).toBe('h3'); - }); -}); - -function createElement(id: string): HTMLElement { - const div = document.createElement('div'); - div.innerHTML = fakeElementHtml[id]; - return div; -} - -const fakeElementHtml: Record = { - 'element-without-headings': `
content
`, - 'element-with-only-h1': `

heading

`, - 'element-with-h1-h2-h3-h4': `
-

H1

-

H2 - first link

-

H3 - first

-

H4

-

H3 - second

-

H2 - second

-

H3 - third

-
- `, -}; diff --git a/adev/shared/src/lib/services/table-of-contents-loader.service.ts b/adev/shared/src/lib/services/table-of-contents-loader.service.ts deleted file mode 100644 index ad7d88efcb8..00000000000 --- a/adev/shared/src/lib/services/table-of-contents-loader.service.ts +++ /dev/null @@ -1,87 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DOCUMENT, isPlatformBrowser} from '@angular/common'; -import {inject, Injectable, PLATFORM_ID} from '@angular/core'; - -import {TableOfContentsItem, TableOfContentsLevel} from '../interfaces/table-of-contents-item'; - -/** - * Name of an attribute that is set on an element that should be - * excluded from the `TableOfContentsLoader` lookup. This is needed - * to exempt SSR'ed content of the `TableOfContents` component from - * being inspected and accidentally pulling more content into ToC. - */ -export const TOC_SKIP_CONTENT_MARKER = 'toc-skip-content'; - -@Injectable() -export class TableOfContentsLoader { - // There are some cases when default browser anchor scrolls a little above the - // heading In that cases wrong item was selected. The value found by trial and - // error. - readonly toleranceThreshold = 40; - - tableOfContentItems: TableOfContentsItem[] = []; - - private readonly document = inject(DOCUMENT); - private readonly platformId = inject(PLATFORM_ID); - - buildTableOfContent(docElement: Element): void { - const headings = this.getHeadings(docElement); - const tocList: TableOfContentsItem[] = headings.map((heading) => ({ - id: heading.id, - level: heading.tagName.toLowerCase() as TableOfContentsLevel, - title: this.getHeadingTitle(heading), - top: this.calculateTop(heading), - })); - - this.tableOfContentItems = tocList; - } - - // Update top value of heading, it should be executed after window resize - updateHeadingsTopValue(element: HTMLElement): void { - const headings = this.getHeadings(element); - 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); - } - - this.tableOfContentItems.forEach((item) => { - item.top = updatedTopValues.get(item.id) ?? 0; - }); - } - - private getHeadingTitle(heading: HTMLHeadingElement): string { - const div: HTMLDivElement = this.document.createElement('div'); - div.innerHTML = heading.innerHTML; - - return (div.textContent || '').trim(); - } - - // Get all headings (h2 and h3) with ids, which are not children of the - // docs-example-viewer component. - private getHeadings(element: Element): HTMLHeadingElement[] { - return Array.from( - element.querySelectorAll( - `h2[id]:not(docs-example-viewer h2):not([${TOC_SKIP_CONTENT_MARKER}]),` + - `h3[id]:not(docs-example-viewer h3):not([${TOC_SKIP_CONTENT_MARKER}])`, - ), - ); - } - - private calculateTop(heading: HTMLHeadingElement): number { - if (!isPlatformBrowser(this.platformId)) return 0; - return ( - Math.floor(heading.offsetTop > 0 ? heading.offsetTop : heading.getClientRects()[0]?.top) - - this.toleranceThreshold - ); - } -} diff --git a/adev/shared/src/lib/services/table-of-contents-scroll-spy.service.spec.ts b/adev/shared/src/lib/services/table-of-contents-scroll-spy.service.spec.ts deleted file mode 100644 index b5af50cccff..00000000000 --- a/adev/shared/src/lib/services/table-of-contents-scroll-spy.service.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {TestBed, discardPeriodicTasks, fakeAsync, tick} from '@angular/core/testing'; - -import {WINDOW} from '../providers'; - -import {TableOfContentsLoader} from './table-of-contents-loader.service'; -import {SCROLL_EVENT_DELAY, TableOfContentsScrollSpy} from './table-of-contents-scroll-spy.service'; -import {DOCUMENT} from '@angular/common'; - -describe('TableOfContentsScrollSpy', () => { - let service: TableOfContentsScrollSpy; - let tableOfContentsLoaderSpy: jasmine.SpyObj; - const fakeWindow = { - addEventListener: () => {}, - removeEventListener: () => {}, - }; - - beforeEach(() => { - tableOfContentsLoaderSpy = jasmine.createSpyObj( - 'TableOfContentsLoader', - ['tableOfContentItems', 'updateHeadingsTopValue'], - ); - TestBed.configureTestingModule({ - providers: [ - TableOfContentsScrollSpy, - { - provide: WINDOW, - useValue: fakeWindow, - }, - { - provide: TableOfContentsLoader, - useValue: tableOfContentsLoaderSpy, - }, - ], - }); - service = TestBed.inject(TableOfContentsScrollSpy); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - it('should activeItemId be null by default', () => { - expect(service.activeItemId()).toBeNull(); - }); - - it(`should only fire setActiveItemId every ${SCROLL_EVENT_DELAY}ms when scrolling`, fakeAsync(() => { - const doc = TestBed.inject(DOCUMENT); - const scrollableContainer = doc; - const setActiveItemIdSpy = spyOn(service as any, 'setActiveItemId'); - - service.startListeningToScroll(doc.querySelector('fake-selector')); - - scrollableContainer.dispatchEvent(new Event('scroll')); - tick(SCROLL_EVENT_DELAY - 2); - - expect(setActiveItemIdSpy).not.toHaveBeenCalled(); - - scrollableContainer.dispatchEvent(new Event('scroll')); - tick(1); - - expect(setActiveItemIdSpy).not.toHaveBeenCalled(); - - scrollableContainer.dispatchEvent(new Event('scroll')); - tick(1); - - expect(setActiveItemIdSpy).toHaveBeenCalled(); - - discardPeriodicTasks(); - })); -}); diff --git a/adev/shared/src/lib/services/table-of-contents-scroll-spy.service.ts b/adev/shared/src/lib/services/table-of-contents-scroll-spy.service.ts deleted file mode 100644 index 7cd2409fe2c..00000000000 --- a/adev/shared/src/lib/services/table-of-contents-scroll-spy.service.ts +++ /dev/null @@ -1,167 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DOCUMENT, ViewportScroller} from '@angular/common'; -import { - DestroyRef, - EnvironmentInjector, - Injectable, - afterNextRender, - inject, - signal, - NgZone, -} from '@angular/core'; -import {RESIZE_EVENT_DELAY} from '@angular/docs-shared'; -import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; -import {auditTime, debounceTime, fromEvent, startWith} from 'rxjs'; -import {WINDOW} from '../providers'; -import {shouldReduceMotion} from '../utils'; -import {TableOfContentsLoader} from './table-of-contents-loader.service'; - -export const SCROLL_EVENT_DELAY = 20; -export const SCROLL_FINISH_DELAY = SCROLL_EVENT_DELAY * 2; - -@Injectable() -// The service is responsible for listening for scrolling and resizing, -// thanks to which it sets the active item in the Table of contents -export class TableOfContentsScrollSpy { - private readonly destroyRef = inject(DestroyRef); - private readonly tableOfContentsLoader = inject(TableOfContentsLoader); - private readonly document = inject(DOCUMENT); - private readonly window = inject(WINDOW); - private readonly ngZone = inject(NgZone); - private readonly viewportScroller = inject(ViewportScroller); - private readonly injector = inject(EnvironmentInjector); - private contentSourceElement: HTMLElement | null = null; - private lastContentWidth = 0; - - activeItemId = signal(null); - scrollbarThumbOnTop = signal(true); - - startListeningToScroll(contentSourceElement: HTMLElement | null): void { - this.contentSourceElement = contentSourceElement; - this.lastContentWidth = this.getContentWidth(); - - this.setScrollEventHandlers(); - this.setResizeEventHandlers(); - } - - scrollToTop(): void { - this.viewportScroller.scrollToPosition([0, 0]); - } - - scrollToSection(id: string): void { - if (shouldReduceMotion()) { - this.offsetToSection(id); - } else { - const section = this.document.getElementById(id); - section?.scrollIntoView({behavior: 'smooth', block: 'start'}); - // We don't want to set the active item here, it would mess up the animation - // The scroll event handler will handle it for us - } - } - - offsetToSection(id: string): void { - const section = this.document.getElementById(id); - section?.scrollIntoView({block: 'start'}); - // Here we need to set the active item manually because scroll events might not be fired - this.activeItemId.set(id); - } - - // After window resize, we should update top value of each table content item - private setResizeEventHandlers() { - fromEvent(this.window, 'resize') - .pipe(debounceTime(RESIZE_EVENT_DELAY), takeUntilDestroyed(this.destroyRef), startWith()) - .subscribe(() => { - this.ngZone.run(() => this.updateHeadingsTopAfterResize()); - }); - - // We need to observe the height of the docs-viewer because it may change after the - // assets (fonts, images) are loaded. They can (and will) change the y-position of the headings. - const docsViewer = this.document.querySelector('docs-viewer'); - if (docsViewer) { - afterNextRender( - () => { - const resizeObserver = new ResizeObserver(() => this.updateHeadingsTopAfterResize()); - resizeObserver.observe(docsViewer); - this.destroyRef.onDestroy(() => resizeObserver.disconnect()); - }, - {injector: this.injector}, - ); - } - } - - private updateHeadingsTopAfterResize(): void { - this.lastContentWidth = this.getContentWidth(); - - const contentElement = this.contentSourceElement; - if (contentElement) { - this.tableOfContentsLoader.updateHeadingsTopValue(contentElement); - this.setActiveItemId(); - } - } - - private setScrollEventHandlers(): void { - const scroll$ = fromEvent(this.document, 'scroll').pipe( - auditTime(SCROLL_EVENT_DELAY), - takeUntilDestroyed(this.destroyRef), - ); - - this.ngZone.runOutsideAngular(() => { - scroll$.subscribe(() => this.setActiveItemId()); - }); - } - - private setActiveItemId(): void { - const tableOfContentItems = this.tableOfContentsLoader.tableOfContentItems; - - if (tableOfContentItems.length === 0) return; - - // Resize could emit scroll event, in that case we could stop setting active item until resize will be finished - if (this.lastContentWidth !== this.getContentWidth()) { - return; - } - - const scrollOffset = this.getScrollOffset(); - if (scrollOffset === null) return; - - for (const [i, currentLink] of tableOfContentItems.entries()) { - const nextLink = tableOfContentItems[i + 1]; - - // A link is considered active if the page is scrolled past the - // anchor without also being scrolled passed the next link. - const isActive = - scrollOffset >= currentLink.top && (!nextLink || nextLink.top >= scrollOffset); - - // When active item was changed then trigger change detection - if (isActive && this.activeItemId() !== currentLink.id) { - this.ngZone.run(() => this.activeItemId.set(currentLink.id)); - return; - } - } - - if (scrollOffset < tableOfContentItems[0].top && this.activeItemId() !== null) { - this.ngZone.run(() => this.activeItemId.set(null)); - } - - const scrollOffsetZero = scrollOffset === 0; - if (scrollOffsetZero !== this.scrollbarThumbOnTop()) { - // we want to trigger change detection only when the value changes - this.ngZone.run(() => this.scrollbarThumbOnTop.set(scrollOffsetZero)); - } - } - - // Gets the scroll offset of the scroll container - private getScrollOffset(): number { - return this.window.scrollY; - } - - private getContentWidth(): number { - return this.document.body.clientWidth || Number.MAX_SAFE_INTEGER; - } -} diff --git a/adev/shared/src/lib/styles/_api-item-label.scss b/adev/shared/src/lib/styles/_api-item-label.scss deleted file mode 100644 index b1a3faf1fe1..00000000000 --- a/adev/shared/src/lib/styles/_api-item-label.scss +++ /dev/null @@ -1,87 +0,0 @@ -@mixin api-item-label() { - .adev-api-item-label { - --label-theme: var(--symbolic-purple); - - display: flex; - justify-content: center; - align-items: center; - font-weight: 500; - color: var(--label-theme); - background: color-mix(in srgb, var(--label-theme) 10%, white); - border-radius: 0.25rem; - transition: color 0.3s ease, background-color 0.3s ease; - text-transform: capitalize; - - &[data-mode='short'] { - height: 22px; - width: 22px; - } - - &[data-mode='full'] { - font-size: 0.75rem; - padding: 0.25rem 0.5rem; - } - - @media screen and (prefers-color-scheme: dark) { - background: color-mix(in srgb, var(--label-theme) 17%, #272727); - } - - .adev-dark-mode & { - background: color-mix(in srgb, var(--label-theme) 17%, #272727); - } - - .adev-light-mode & { - background: color-mix(in srgb, var(--label-theme) 10%, white); - } - - &[data-type='undecorated_class'], - &[data-type='class'] { - --label-theme: var(--symbolic-purple); - } - - &[data-type='constant'], - &[data-type='const'] { - --label-theme: var(--symbolic-gray); - } - - &[data-type='decorator'] { - --label-theme: var(--symbolic-blue); - } - - &[data-type='directive'] { - --label-theme: var(--symbolic-pink); - } - - &[data-type='element'] { - --label-theme: var(--symbolic-orange); - } - - &[data-type='enum'] { - --label-theme: var(--symbolic-yellow); - } - - &[data-type='function'] { - --label-theme: var(--symbolic-green); - } - - &[data-type='interface'] { - --label-theme: var(--symbolic-cyan); - } - - &[data-type='pipe'] { - --label-theme: var(--symbolic-teal); - } - - &[data-type='ng_module'] { - --label-theme: var(--symbolic-brown); - } - - &[data-type='type_alias'] { - --label-theme: var(--symbolic-lime); - } - - &[data-type='block'] { - --label-theme: var(--vivid-pink); - } - } -} diff --git a/adev/shared/src/lib/styles/_button.scss b/adev/shared/src/lib/styles/_button.scss deleted file mode 100644 index 889cc1f3fe7..00000000000 --- a/adev/shared/src/lib/styles/_button.scss +++ /dev/null @@ -1,148 +0,0 @@ -@mixin button() { - button { - font-family: var(--inter-font); - background: transparent; - -webkit-appearance: none; - border: 0; - font-weight: 600; - - // Remove excess padding and border in Firefox 4+ - &::-moz-focus-inner { - border: 0; - padding: 0; - } - - &:disabled { - cursor: not-allowed; - } - } - - @property --angle { - syntax: ''; - initial-value: 90deg; - inherits: false; - } - - @keyframes spin-gradient { - 0% { - --angle: 90deg; - } - 100% { - --angle: 450deg; - } - } - - .adev-primary-btn { - cursor: pointer; - border: none; - outline: none; - position: relative; - border-radius: 0.25rem; - padding: 0.75rem 1.5rem; - width: max-content; - color: transparent; - - // border gradient / background - --angle: 90deg; - background: linear-gradient( - var(--angle), - var(--orange-red) 0%, - var(--vivid-pink) 50%, - var(--electric-violet) 100% - ); - - docs-icon { - z-index: var(--z-index-content); - position: relative; - } - - // text & radial gradient - &::before { - content: attr(text); - position: absolute; - inset: 1px; - background: var(--page-bg-radial-gradient); - border-radius: 0.2rem; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.3s ease, background 0.3s ease; - color: var(--primary-contrast); - } - - // solid color negative space - CSS transition supported - &::after { - content: attr(text); - position: absolute; - inset: 1px; - background: var(--page-background); - border-radius: 0.2rem; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.3s ease, background 0.3s ease; - color: var(--primary-contrast); - } - - &:hover { - animation: spin-gradient 4s linear infinite forwards; - &::before { - background-color: var(--page-background); - background: var(--soft-pink-radial-gradient); - opacity: 0.9; - } - &::after { - opacity: 0; - } - } - - &:active { - &::before { - opacity: 0.8; - } - } - - &:disabled { - //gradient stroke - background: var(--quinary-contrast); - color: var(--quinary-contrast); - - &::before { - background-color: var(--page-background); - background: var(--page-bg-radial-gradient); - opacity: 1; - } - - docs-icon { - color: var(--quinary-contrast); - } - } - - docs-icon { - z-index: var(--z-index-icon); - color: var(--primary-contrast); - } - } - - .adev-secondary-btn { - border: 1px solid var(--senary-contrast); - background: var(--page-background); - padding: 0.75rem 1.5rem; - border-radius: 0.25rem; - color: var(--primary-contrast); - transition: background 0.3s ease; - - docs-icon { - color: var(--quaternary-contrast); - transition: color 0.3s ease; - } - - &:hover { - background: var(--septenary-contrast); - - docs-icon { - color: var(--primary-contrast); - } - } - } -} diff --git a/adev/shared/src/lib/styles/_colors.scss b/adev/shared/src/lib/styles/_colors.scss deleted file mode 100644 index a33ba4084c1..00000000000 --- a/adev/shared/src/lib/styles/_colors.scss +++ /dev/null @@ -1,297 +0,0 @@ -// Colors -// Using OKLCH color space for better color reproduction on P3 displays, -// as well as better human-readability -// --> https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch - -@mixin root-definitions() { - // PRIMITIVES - // Colors - --bright-blue: oklch(51.01% 0.274 263.83); // #0546ff - --indigo-blue: oklch(51.64% 0.229 281.65); // #5c44e4 - --electric-violet: oklch(53.18% 0.28 296.97); // #8514f5 - --french-violet: oklch(47.66% 0.246 305.88); // #8001c6 - --vivid-pink: oklch(69.02% 0.277 332.77); // #f637e3 - --hot-pink: oklch(59.91% 0.239 8.14); // #e90464 - --hot-red: oklch(61.42% 0.238 15.34); // #f11653 - --orange-red: oklch(63.32% 0.24 31.68); // #fa2c04 - --super-green: oklch(79.12% 0.257 155.13); // #00c572 // Used for success, merge additions, etc. - - // subtle-purple is used for inline-code bg, docs-card hover bg & docs-code header bg - --subtle-purple: color-mix(in srgb, var(--bright-blue) 5%, white 10%); - --light-blue: color-mix(in srgb, var(--bright-blue), white 50%); - --light-violet: color-mix(in srgb, var(--electric-violet), white 65%); - --light-orange: color-mix(in srgb, var(--orange-red), white 50%); - --light-pink: color-mix(in srgb, var(--vivid-pink) 10%, white 80%); - - // SYMBOLIC COLORS - // Used for Type Labels - --symbolic-purple: oklch(42.86% 0.29 266.4); //#1801ea - --symbolic-gray: oklch(66.98% 0 0); // #959595 - --symbolic-blue: oklch(42.45% 0.223 263.38); // #0037c5; - --symbolic-pink: oklch(63.67% 0.254 13.47); // #ff025c - --symbolic-orange: oklch(64.73% 0.23769984683784018 33.18328352127882); // #fe3700 - --symbolic-yellow: oklch(78.09% 0.163 65.69); // #fd9f28 - --symbolic-green: oklch(67.83% 0.229 142.73); // #00b80a - --symbolic-cyan: oklch(67.05% 0.1205924489987394 181.34025902203868); // #00ad9a - --symbolic-magenta: oklch(51.74% 0.25453048882711515 315.26261625862725); // #9c00c8 - --symbolic-teal: oklch(57.59% 0.083 230.58); // #3f82a1 - --symbolic-brown: oklch(49.06% 0.128 46.41); // #994411 - --symbolic-lime: oklch(70.33% 0.2078857836035299 135.66843631046476); // #5dba00 - - // Grays - --gray-1000: oklch(16.93% 0.004 285.95); // #0f0f11 - --gray-900: oklch(19.37% 0.006 300.98); // #151417 - --gray-800: oklch(25.16% 0.008 308.11); // #232125 - --gray-700: oklch(36.98% 0.014 302.71); // #413e46 - --gray-600: oklch(44% 0.019 306.08); // #55505b - --gray-500: oklch(54.84% 0.023 304.99); // #746e7c - --gray-400: oklch(70.9% 0.015 304.04); // #a39fa9 - --gray-300: oklch(84.01% 0.009 308.34); // #ccc9cf - --gray-200: oklch(91.75% 0.004 301.42); // #e4e3e6 - --gray-100: oklch(97.12% 0.002 325.59); // #f6f5f6 - --gray-50: oklch(98.81% 0 0); // #fbfbfb - - // GRADIENTS - --red-to-pink-horizontal-gradient: linear-gradient( - 90deg, - var(--hot-pink) 11.42%, - var(--hot-red) 34.83%, - var(--vivid-pink) 60.69% - ); - - --red-to-pink-to-purple-horizontal-gradient: linear-gradient( - 90deg, - var(--orange-red) 0%, - var(--vivid-pink) 50%, - var(--electric-violet) 100% - ); - - --pink-to-highlight-to-purple-to-blue-horizontal-gradient: linear-gradient( - 140deg, - var(--vivid-pink) 0%, - var(--vivid-pink) 15%, - color-mix(in srgb, var(--vivid-pink), var(--electric-violet) 50%) 25%, - color-mix(in srgb, var(--vivid-pink), var(--electric-violet) 10%) 35%, - color-mix(in srgb, var(--vivid-pink), var(--orange-red) 50%) 42%, - color-mix(in srgb, var(--vivid-pink), var(--orange-red) 50%) 44%, - color-mix(in srgb, var(--vivid-pink), var(--page-background) 70%) 47%, - var(--electric-violet) 48%, - var(--bright-blue) 60% - ); - - --purple-to-blue-horizontal-gradient: linear-gradient( - 90deg, - var(--electric-violet) 0%, - var(--bright-blue) 100% - ); - --purple-to-blue-vertical-gradient: linear-gradient( - 0deg, - var(--electric-violet) 0%, - var(--bright-blue) 100% - ); - - --red-to-orange-horizontal-gradient: linear-gradient( - 90deg, - var(--hot-pink) 0%, - var(--orange-red) 100% - ); - --red-to-orange-vertical-gradient: linear-gradient( - 0deg, - var(--hot-pink) 0%, - var(--orange-red) 100% - ); - - --pink-to-purple-horizontal-gradient: linear-gradient( - 90deg, - var(--vivid-pink) 0%, - var(--electric-violet) 100% - ); - --pink-to-purple-vertical-gradient: linear-gradient( - 0deg, - var(--electric-violet) 0%, - var(--vivid-pink) 100% - ); - - --purple-to-light-purple-vertical-gradient: linear-gradient( - 0deg, - var(--french-violet) 0%, - var(--light-violet) 100% - ); - - --green-to-cyan-vertical-gradient: linear-gradient( - 0deg, - var(--symbolic-cyan) 0%, - var(--super-green) 100% - ); - - --blue-to-teal-vertical-gradient: linear-gradient( - 0deg, - var(--bright-blue) 0%, - var(--light-blue) 100% - ); - - --blue-to-cyan-vertical-gradient: linear-gradient( - 0deg, - var(--bright-blue) 0%, - var(--symbolic-cyan) 100% - ); - - --black-to-gray-vertical-gradient: linear-gradient( - 0deg, - var(--primary-contrast) 0%, - var(--gray-400) 100% - ); - - --red-to-pink-vertical-gradient: linear-gradient(0deg, var(--hot-red) 0%, var(--vivid-pink) 100%); - --orange-to-pink-vertical-gradient: linear-gradient( - 0deg, - var(--vivid-pink) 0%, - var(--light-orange) 100% - ); - - // Radial Gradients - --page-bg-radial-gradient: radial-gradient(circle, white 0%, white 100%); - --soft-pink-radial-gradient: radial-gradient( - circle at center bottom, - var(--light-pink) 0%, - white 80% - ); - - // ABSTRACTIONS light - dark - // --full-contrast: black - white - // --primary-constrast: gray-900 - gray-100 - // --secondary-contrast: gray-800 - gray-300 - // --tertiary-contrast: gray-700 - gray-300 - // --quaternary-contrast: gray-500 - gray-400 - // --quinary-contrast: gray-300 - gray-500 - // --senary-contrast: gray-200 - gray-700 - // --septenary-contrast: gray-100 - gray-800 - // --octonary-contrast: gray-50 - gray-900 - // --page-background white - gray-1000 - - // LIGHT MODE is default - // contrast - light mode - --full-contrast: black; - --primary-contrast: var(--gray-900); - --secondary-contrast: var(--gray-800); - --tertiary-contrast: var(--gray-700); - --quaternary-contrast: var(--gray-500); - --quinary-contrast: var(--gray-300); - --senary-contrast: var(--gray-200); - --septenary-contrast: var(--gray-100); - --octonary-contrast: var(--gray-50); - --page-background: white; - - // Home page - // for the "unfilled" portion of the word that hasn't - // been highlighted by the gradient - --gray-unfilled: var(--gray-400); - // TODO: convert oklch to hex at build time - --webgl-page-background: #ffffff; - --webgl-gray-unfilled: #a39fa9; -} - -@mixin dark-mode-definitions() { - // Contrasts - --full-contrast: white; - --primary-contrast: var(--gray-50); - --secondary-contrast: var(--gray-300); - --tertiary-contrast: var(--gray-300); - --quaternary-contrast: var(--gray-400); - --quinary-contrast: var(--gray-500); - --senary-contrast: var(--gray-700); - --septenary-contrast: var(--gray-800); - --octonary-contrast: var(--gray-900); - --page-background: var(--gray-1000); - - --bright-blue: color-mix(in srgb, oklch(51.01% 0.274 263.83), var(--full-contrast) 60%); - --indigo-blue: color-mix(in srgb, oklch(51.64% 0.229 281.65), var(--full-contrast) 70%); - --electric-violet: color-mix(in srgb, oklch(53.18% 0.28 296.97), var(--full-contrast) 70%); - --french-violet: color-mix(in srgb, oklch(47.66% 0.246 305.88), var(--full-contrast) 70%); - --vivid-pink: color-mix(in srgb, oklch(69.02% 0.277 332.77), var(--full-contrast) 70%); - --hot-pink: color-mix(in srgb, oklch(59.91% 0.239 8.14), var(--full-contrast) 70%); - --hot-red: color-mix(in srgb, oklch(61.42% 0.238 15.34), var(--full-contrast) 70%); - --orange-red: color-mix(in srgb, oklch(63.32% 0.24 31.68), var(--full-contrast) 60%); - --super-green: color-mix(in srgb, oklch(79.12% 0.257 155.13), var(--full-contrast) 70%); - - --light-pink: color-mix(in srgb, var(--vivid-pink) 5%, var(--page-background) 75%); - - --symbolic-purple: color-mix(in srgb, oklch(42.86% 0.29 266.4), var(--full-contrast) 65%); - --symbolic-gray: color-mix(in srgb, oklch(66.98% 0 0), var(--full-contrast) 65%); - --symbolic-blue: color-mix(in srgb, oklch(42.45% 0.223 263.38), var(--full-contrast) 65%); - --symbolic-pink: color-mix(in srgb, oklch(63.67% 0.254 13.47), var(--full-contrast) 65%); - --symbolic-orange: color-mix( - in srgb, - oklch(64.73% 0.23769984683784018 33.18328352127882), - var(--full-contrast) 65% - ); - --symbolic-yellow: color-mix(in srgb, oklch(78.09% 0.163 65.69), var(--full-contrast) 65%); - --symbolic-green: color-mix(in srgb, oklch(67.83% 0.229 142.73), var(--full-contrast) 65%); - --symbolic-cyan: color-mix( - in srgb, - oklch(67.05% 0.1205924489987394 181.34025902203868), - var(--full-contrast) 65% - ); - --symbolic-magenta: color-mix( - in srgb, - oklch(51.74% 0.25453048882711515 315.26261625862725), - var(--full-contrast) 65% - ); - --symbolic-teal: color-mix(in srgb, oklch(57.59% 0.083 230.58), var(--full-contrast) 65%); - --symbolic-brown: color-mix(in srgb, oklch(49.06% 0.128 46.41), var(--full-contrast) 65%); - --symbolic-lime: color-mix( - in srgb, - oklch(70.33% 0.2078857836035299 135.66843631046476), - var(--full-contrast) 65% - ); - - --page-bg-radial-gradient: radial-gradient(circle, black 0%, black 100%); - --soft-pink-radial-gradient: radial-gradient( - circle at center bottom, - var(--light-pink) 0%, - color-mix(in srgb, black, transparent 15%) 80% - ); - - // Home page - dark mode - --gray-unfilled: var(--gray-700); - // TODO: convert oklch to hex at build time - --webgl-page-background: #0f0f11; - --webgl-gray-unfilled: #413e46; - - .adev-toggle { - input { - &:checked + .adev-slider { - background: var(--pink-to-purple-horizontal-gradient) !important; - } - } - } -} - -@mixin mdc-definitions() { - --mdc-snackbar-container-shape: 0.25rem; - --mdc-snackbar-container-color: var(--page-background); - --mdc-snackbar-supporting-text-color: var(--primary-contrast); -} - -// LIGHT MODE (Explicit) -.adev-light-mode { - background-color: #ffffff; - @include root-definitions(); - @include mdc-definitions(); - .adev-invert-mode { - @include dark-mode-definitions(); - @include mdc-definitions(); - } -} - -// DARK MODE (Explicit) -.adev-dark-mode { - background-color: oklch(16.93% 0.004 285.95); - @include root-definitions(); - @include dark-mode-definitions(); - @include mdc-definitions(); - .adev-invert-mode { - @include root-definitions(); - @include mdc-definitions(); - } -} diff --git a/adev/shared/src/lib/styles/_faceted-list.scss b/adev/shared/src/lib/styles/_faceted-list.scss deleted file mode 100644 index 90af142d4d2..00000000000 --- a/adev/shared/src/lib/styles/_faceted-list.scss +++ /dev/null @@ -1,56 +0,0 @@ -@mixin faceted-list() { - .adev-faceted-list { - --faceted-list-border-width: 2px; - list-style: none; - padding: 0; - margin: 0; - border-inline-start: calc(var(--faceted-list-border-width) - 1px) solid var(--senary-contrast); - } - .adev-faceted-list-item { - a, - button:not(.adev-expanded-button) { - position: relative; - background-color: var(--quaternary-contrast); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - transition: background-color 0.3s ease; - line-height: 1.1rem; - - &::before { - content: ''; - position: absolute; - top: 0; - left: calc(var(--faceted-list-border-width) * -1); - width: var(--faceted-list-border-width); - height: 100%; - background: var(--primary-contrast); - opacity: 0; - transform: scaleY(0.7); - transition: transform 0.3s ease, opacity 0.3s ease; - } - &:hover { - background-color: var(--primary-contrast); - &::before { - opacity: 0.3; - } - } - &.adev-faceted-list-item-active { - // font gradient - background-image: var(--pink-to-purple-vertical-gradient); - &::before { - opacity: 1; - transform: scaleY(1); - background: var(--pink-to-purple-vertical-gradient); - } - &:hover { - &::before { - opacity: 1; - transform: scaleY(1.1); - } - } - } - } - } - // a or button -} diff --git a/adev/shared/src/lib/styles/_kbd.scss b/adev/shared/src/lib/styles/_kbd.scss deleted file mode 100644 index 61677756ae6..00000000000 --- a/adev/shared/src/lib/styles/_kbd.scss +++ /dev/null @@ -1,28 +0,0 @@ -@mixin kbd() { - kbd { - position: relative; - color: var(---tertiary-contrast); - border: 1px solid var(--quinary-contrast); - - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px var(--octonary-contrast) inset; - // NOTE: This line (in addition to others) prevents proper contrast checking in Lighthouse - text-shadow: 0 1px 0 var(--octonary-contrast); - border-radius: 3px; - display: inline-block; - font-family: sans-serif; - line-height: 1.5; - margin: 0 0.1em; - padding: 1px 0.4em; - min-width: 14px; - min-height: 20px; - vertical-align: middle; - text-align: center; - - @media (prefers-reduced-motion: no-preference) { - &:hover { - box-shadow: 0 0.5px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px var(--octonary-contrast) inset; - top: 1px; - } - } - } -} diff --git a/adev/shared/src/lib/styles/_links.scss b/adev/shared/src/lib/styles/_links.scss deleted file mode 100644 index 7c463eaf822..00000000000 --- a/adev/shared/src/lib/styles/_links.scss +++ /dev/null @@ -1,9 +0,0 @@ -@mixin external-link-with-icon() { - &::after { - display: inline-block; - content: '\e89e'; // codepoint for "open_in_new" - font-family: 'Material Symbols Outlined'; - margin-left: 0.2rem; - vertical-align: middle; - } -} diff --git a/adev/shared/src/lib/styles/_media-queries.scss b/adev/shared/src/lib/styles/_media-queries.scss deleted file mode 100644 index 8416fb2c8dc..00000000000 --- a/adev/shared/src/lib/styles/_media-queries.scss +++ /dev/null @@ -1,64 +0,0 @@ -$screen-xs: 700px; -$screen-sm: 775px; -$screen-md: 900px; -$screen-lg: 1200px; -$screen-xl: 1800px; - -@mixin for-phone-only { - @media (max-width: $screen-xs) { - @content; - } -} -@mixin for-tablet-portrait-up { - @media (min-width: $screen-xs) { - @content; - } -} - -@mixin for-tablet { - @media (min-width: $screen-xs) and (max-width: $screen-md) { - @content; - } -} - -@mixin for-tablet-up { - @media (min-width: $screen-sm) { - @content; - } -} - -@mixin for-tablet-landscape-up { - @media (min-width: $screen-md) { - @content; - } -} - -@mixin for-desktop-up { - @media (min-width: $screen-lg) { - @content; - } -} - -@mixin for-big-desktop-up { - @media (min-width: $screen-xl) { - @content; - } -} - -@mixin for-desktop-down { - @media (max-width: $screen-lg) { - @content; - } -} - -@mixin for-tablet-landscape-down { - @media (max-width: $screen-md) { - @content; - } -} - -@mixin for-tablet-down { - @media (max-width: $screen-sm) { - @content; - } -} diff --git a/adev/shared/src/lib/styles/_resets.scss b/adev/shared/src/lib/styles/_resets.scss deleted file mode 100644 index f3da241640d..00000000000 --- a/adev/shared/src/lib/styles/_resets.scss +++ /dev/null @@ -1,478 +0,0 @@ -@use './media-queries' as mq; - -@mixin resets() { - :root { - --fallback-font-stack: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', - Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; - --page-width: 80ch; - --layout-padding: 3.12rem; // a common padding value throughout the layout - --primary-nav-width: 110px; - --secondary-nav-width: 16.25rem; - --fixed-content-height: calc(100vh - var(--layout-padding) * 2); - - @include mq.for-tablet-landscape-down { - --layout-padding: 2rem; - } - @include mq.for-phone-only { - --layout-padding: 1rem; - } - } - - html { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - // Define the default font for the document - font-family: var(--inter-font); - font-size: 16px; - background-color: var(--page-background); - color: var(--primary-contrast); - transition: color 0.3s ease, background-color 0.3s ease; - scroll-behavior: smooth; - } - - @media (prefers-reduced-motion) { - html { - scroll-behavior: auto; - } - } - - body { - margin: 0; - overflow-y: auto; - overflow-x: hidden; - } - - html, - body { - // Ensures that these elements extend to the full height of the viewport - height: 100vh; - min-height: 100vh; - - @supports (height: 100svh) { - height: 100svh; - } - } - - button { - cursor: pointer; - } - - img { - width: 100%; - border-radius: 0.25rem; - overflow: hidden; - margin: 1rem 0; - - &[src$='#small'] { - max-width: 250px; - } - - &[src$='#medium'] { - max-width: 450px; - } - } - - abbr[title] { - text-decoration: none; - } - - // Select & Input - .adev-form-element { - display: flex; - align-items: center; - gap: 0.5rem; - border: 1px solid var(--senary-contrast); - border-radius: 0.25rem; - padding: 0.5rem; - background-color: var(--page-background); - transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease; - - docs-icon, - label { - color: var(--quinary-contrast); - transition: color 0.3s ease; - } - - label { - font-size: 0.875rem; - } - - select, - input { - width: 16rem; - -webkit-appearance: none; - display: flex; - flex: 1; - font-size: 0.875rem; - border: none; - outline: none; - height: 100%; - background-color: var(--page-background); - color: var(--tertiary-contrast); - transition: color 0.3s ease, background-color 0.3s ease; - } - - select { - width: 10rem; - background-image: url('../icons/chevron.svg'); - background-size: 0.7rem; - background-repeat: no-repeat; - background-position: right center; - margin-inline-end: 0.3rem; - } - - &:focus-within { - border: 1px solid var(--french-violet); - docs-icon { - color: var(--tertiary-contrast); - } - } - } - - // Progress bar styling - .ng-spinner { - display: none !important; - } - - .ng-progress-bar { - .ng-bar { - background: var(--red-to-pink-to-purple-horizontal-gradient) !important; - } - } - - // Material tab styling - .mat-mdc-tab-header { - --mat-tab-header-disabled-ripple-color: transparent; - --mat-tab-header-pagination-icon-color: var(--secondary-contrast); - --mat-tab-header-inactive-label-text-color: var(--secondary-contrast); - --mat-tab-header-inactive-ripple-color: transparent; - --mat-tab-header-inactive-hover-label-text-color: var(--tertiary-contrast); - --mat-tab-header-inactive-focus-label-text-color: var(--secondary-contrast); - --mat-tab-header-active-label-text-color: var(--primary-contrast); - --mat-tab-header-active-ripple-color: transparent; - --mdc-tab-indicator-active-indicator-color: color-mix(in srgb, var(--bright-blue) 40%, white); - --mat-tab-header-active-focus-label-text-color: var(--primary-contrast); - --mat-tab-header-active-hover-label-text-color: var(--primary-contrast); - --mat-tab-header-active-focus-indicator-color: var(--bright-blue); - --mat-tab-header-active-hover-indicator-color: color-mix( - in srgb, - var(--bright-blue) 40%, - white - ); - - .mdc-tab { - --mat-tab-header-label-text-font: Inter, sans-serif; - --mat-tab-header-label-text-letter-spacing: -0.00875rem; - --mat-tab-header-label-text-size: 0.875rem; - --mat-tab-header-label-text-weight: 500; - } - - .mdc-tab__text-label { - user-select: none; - letter-spacing: -0.00875rem; - } - - .mdc-tab--active { - --mat-tab-header-label-text-weight: 500; - } - } - - // Material tab styling on Reference page - .adev-reference-tabs { - .mat-mdc-tab-labels { - gap: 20px; - border-bottom: 1px solid var(--senary-contrast); - transition: border-color 0.3s ease; - } - .mdc-tab__text-label { - letter-spacing: -0.00875rem; - } - - .mdc-tab { - min-width: min-content !important; - padding-inline: 2px !important; - } - } - - // Tabs on Tutorials page, Tutorials Playground - .adev-tutorial-editor, - .adev-code-editor-tabs, - .adev-editor-tabs { - .mat-mdc-tab-header { - --mdc-tab-indicator-active-indicator-color: transparent; - --mat-tab-header-active-focus-indicator-color: transparent; - --mat-tab-header-active-hover-indicator-color: transparent; - --mat-tab-header-label-text-font: InterTight, sans-serif; - --mat-tab-header-label-text-tracking: -0.00875rem; - --mat-tab-header-label-text-size: 0.8125rem; - - .mat-mdc-tab-labels { - gap: 0; - transition: border-color 0.3s ease; - width: 100%; - } - - .mdc-tab { - padding-inline: 18px !important; - } - - .mdc-tab--active { - border: 0; - border-block-end-width: 2px; - border-style: solid; - border-image: var(--pink-to-purple-horizontal-gradient) 1; - - &.cdk-keyboard-focused { - border-image: var(--blue-to-cyan-vertical-gradient) 1; - } - - &:has(.adev-delete-file) { - padding-inline-start: 18px !important; - padding-inline-end: 0.5px !important; - } - } - } - } - - .adev-editor-tabs { - .mat-mdc-tab-header { - border-block-end: 1px solid var(--senary-contrast); - } - } - - .adev-code-editor-tabs { - .mat-mdc-tab-group { - // adjust width for + (add file) button - max-width: calc(100% - 40px); - } - } - - .adev-editor-tabs, - .adev-code-editor-tabs { - .mat-mdc-tab-header { - background-color: var(--octonary-contrast); - transition: background-color 0.3s ease, border-color 0.3s ease; - } - - .mdc-tab__text-label { - i { - color: var(--bright-blue); - margin-inline-start: 0.5rem; - margin-inline-end: 0.25rem; - font-size: 1.25rem; - } - span { - color: var(--primary-contrast); - } - } - } - - // Tabs inside Example Viewer Header - .docs-example-viewer-actions { - .mat-mdc-tab-labels { - width: 100%; - } - - .mat-mdc-tab-header { - --mdc-tab-indicator-active-indicator-color: transparent; - --mat-tab-header-active-focus-indicator-color: transparent; - --mat-tab-header-active-hover-indicator-color: transparent; - --mat-tab-header-active-focus-label-text-color: transparent; - --mat-tab-header-active-hover-label-text-color: transparent; - --mat-tab-header-active-label-text-color: transparent; - --mat-tab-header-label-text-font: InterTight, sans-serif; - --mat-tab-header-label-text-letter-spacing: -0.00875rem; - --mat-tab-header-label-text-size: 0.8125rem; - - .mat-mdc-tab-labels { - gap: 0; - border-bottom: 0; - } - - .mdc-tab { - padding-inline: 15px !important; - } - - .mdc-tab--active { - border: 0; - border-block-end-width: 2px; - border-style: solid; - border-image: var(--purple-to-blue-horizontal-gradient) 1; - - span { - background-image: var(--purple-to-blue-horizontal-gradient); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - } - } - } - } -} - -// TODO: Find better solution -// Temporary overrides for mat-tab in API Ref -.adev-app-main-content { - .mat-mdc-tab-body-wrapper, - .mat-mdc-tab-body-content, - .mat-mdc-tab-body { - display: contents; - } -} - -.cm-editor, -.ͼ3 .cm-gutters, -.cm-scroller { - background-color: var(--page-background); - transition: background-color 0.3s ease; - font-size: 0.875rem; -} - -.ͼ1.cm-focused { - outline: none; -} - -.ͼ2u { - .cm-line.cm-activeLine, - .cm-activeLineGutter { - background-color: color-mix(in srgb, var(--primary-contrast) 5%, transparent); - transition: background-color 0.3s ease; - } -} - -.ͼ1 .cm-button { - background-image: linear-gradient(var(--octonary-contrast), var(--page-background)); - - &:focus { - background-image: linear-gradient(var(--senary-contrast), var(--page-background)); - } -} - -.cm-scroller, -.xterm-viewport, -.xterm-screen { - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - cursor: pointer; - margin: 2px; - } - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--senary-contrast); - border-radius: 10px; - transition: background-color 0.3s ease; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quaternary-contrast); - } -} - -// Override terminal styles -.xterm { - height: 100%; - width: 100%; -} - -.xterm-viewport { - overflow-y: auto !important; - height: 100% !important; - width: 100% !important; - background-color: var(--octonary-contrast) !important; - transition: background-color 0.3s ease; -} - -.xterm-screen { - padding: 10px; - box-sizing: border-box; - overflow: visible !important; - height: 100% !important; - width: 100% !important; -} - -.xterm-rows { - height: 100% !important; - overflow: visible !important; - color: var(--primary-contrast) !important; - transition: color 0.3s ease; - // It is important to not alter the font-size or the selection would lose in precision - - .xterm-cursor { - &.xterm-cursor-outline { - outline-color: var(--primary-contrast) !important; - } - &.xterm-cursor-block { - background: var(--primary-contrast) !important; - } - } -} - -.xterm-selection { - top: 10px !important; - left: 10px !important; - div { - background-color: transparent !important; - } -} - -.xterm-decoration-top { - background-color: var(--quinary-contrast) !important; -} - -.xterm-fg-11 { - color: var(--electric-violet) !important; -} - -.xterm-fg-4 { - color: var(--bright-blue) !important; -} - -// progress ### -.xterm-fg-15 { - color: var(--secondary-contrast) !important; -} - -.xterm-fg-14 { - color: var(--vivid-pink) !important; -} - -// > in terminal -.xterm-fg-5 { - color: var(--electric-violet) !important; -} - -// error text, warning text -.xterm-fg-9, -.xterm-fg-3 { - color: var(--vivid-pink) !important; -} - -.xterm-fg-10, -.xterm-fg-2 { - color: var(--symbolic-green) !important; -} - -// error bg -.xterm-bg-1 { - background-color: var(--orange-red) !important; -} - -// error text -.xterm-fg-257 { - color: var(--octonary-contrast) !important; -} - -.xterm-fg-8 { - color: var(--tertiary-contrast) !important; -} - -[docs-breadcrumb] { - height: 2.5625rem; -} diff --git a/adev/shared/src/lib/styles/_scroll-track.scss b/adev/shared/src/lib/styles/_scroll-track.scss deleted file mode 100644 index 6d180944be3..00000000000 --- a/adev/shared/src/lib/styles/_scroll-track.scss +++ /dev/null @@ -1,78 +0,0 @@ -@mixin scroll-track { - // used on secondary nav - .adev-scroll-hide { - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - } - &::-webkit-scrollbar { - width: 0; - } - } - - // used for main page scroll - .adev-scroll-track-transparent-large { - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - cursor: pointer; - } - - &::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--quinary-contrast); - border-radius: 10px; - transition: background-color 0.3s ease; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quaternary-contrast); - } - } - - // used on table & secondary navigation - .adev-scroll-track-transparent { - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - cursor: pointer; - } - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--senary-contrast); - border-radius: 10px; - transition: background-color 0.3s ease; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quaternary-contrast); - } - } - - // used on docs-code blocks - .adev-mini-scroll-track { - &::-webkit-scrollbar-track { - background: transparent; - } - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--senary-contrast); - border-radius: 10px; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quinary-contrast); - } - } -} diff --git a/adev/shared/src/lib/styles/_split.scss b/adev/shared/src/lib/styles/_split.scss deleted file mode 100644 index a7fe62b5fa1..00000000000 --- a/adev/shared/src/lib/styles/_split.scss +++ /dev/null @@ -1,27 +0,0 @@ -$gutter-border: 1px solid var(--senary-contrast) !important; - -as-split { - ::ng-deep .as-split-gutter { - flex-basis: 5px !important; - background-color: inherit !important; - position: relative; - } - - &.as-horizontal.adev-editor { - ::ng-deep .as-split-gutter { - border-inline: $gutter-border; - } - } - - &.as-vertical.adev-editor { - ::ng-deep .as-split-gutter { - border-block-start: $gutter-border; - } - } - - &.as-vertical.adev-right-side { - ::ng-deep .as-split-gutter { - border-block-start: $gutter-border; - } - } -} diff --git a/adev/shared/src/lib/styles/_typography.scss b/adev/shared/src/lib/styles/_typography.scss deleted file mode 100644 index 1e0479f4ad1..00000000000 --- a/adev/shared/src/lib/styles/_typography.scss +++ /dev/null @@ -1,110 +0,0 @@ -// Base Typography styles - -@mixin typography() { - :root { - --code-font: 'DM Mono', monospace; - --inter-font: 'Inter', var(--fallback-font-stack); - --inter-tight-font: 'Inter Tight', var(--fallback-font-stack); - --icons: 'Material Symbols Outlined'; - - --selection-background: var(--vivid-pink); - --selection-color: var(--vivid-pink); - } - - :nth-child(6n + 1) { - --selection-color: var(--vivid-pink); - } - :nth-child(6n + 2) { - --selection-background: var(--hot-pink); - --selection-color: var(--hot-pink); - } - :nth-child(6n + 3) { - --selection-background: var(--electric-violet); - --selection-color: var(--electric-violet); - } - :nth-child(6n + 4) { - --selection-background: var(--french-violet); - --selection-color: var(--french-violet); - } - :nth-child(6n + 5) { - --selection-background: var(--indigo-blue); - --selection-color: var(--indigo-blue); - } - :nth-child(6n + 6) { - --selection-background: var(--bright-blue); - --selection-color: var(--bright-blue); - } - - ::selection { - // Added fallback color due to browser idiosyncrasies with color-mix and ::selection - background: color-mix(in srgb, var(--selection-background) 10%, var(--octonary-contrast)); - color: color-mix(in srgb, var(--selection-color) 40%, var(--primary-contrast)); - } - - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: var(--inter-tight-font); - font-weight: 500; - text-wrap: balance; - } - - p { - font-size: 0.875rem; - line-height: 1.4rem; - font-weight: 400; - letter-spacing: -0.00875rem; - } - - p ~ ul, - p ~ ol { - margin-block-start: 0; - } - - ul, - ol { - font-size: 0.875rem; - line-height: 1.4rem; - font-weight: 400; - letter-spacing: -0.01rem; - } - - a { - text-decoration: none; - font-weight: 500; - transition: color 0.3s ease; - } - - p > a, - td > a, - div > a:not(.docs-card), - li:not(.adev-faceted-list *) a { - color: var(--bright-blue); - &:hover { - color: var(--vivid-pink); - } - &:active { - color: var(--hot-red); - } - } - - p > a, - .docs-list a, - .docs-card a { - margin-block: 0; - text-decoration: underline; - } - - hr { - border: 0; - border-block-start-width: 1px; - border-style: solid; - border-color: var(--senary-contrast); - width: 100%; - margin-block: 1rem; - transition: border-color 0.3s ease; - } -} diff --git a/adev/shared/src/lib/styles/_z-index.scss b/adev/shared/src/lib/styles/_z-index.scss deleted file mode 100644 index 8d7bb456638..00000000000 --- a/adev/shared/src/lib/styles/_z-index.scss +++ /dev/null @@ -1,7 +0,0 @@ -:root { - --z-index-mini-menu: 200; - --z-index-nav: 100; - --z-index-cookie-consent: 60; - --z-index-content: 50; - --z-index-icon: 10; -} diff --git a/adev/shared/src/lib/styles/docs/_alert.scss b/adev/shared/src/lib/styles/docs/_alert.scss deleted file mode 100644 index 690ec1183a7..00000000000 --- a/adev/shared/src/lib/styles/docs/_alert.scss +++ /dev/null @@ -1,117 +0,0 @@ -// Alert - -@mixin docs-alert() { - .docs-alert { - // Default theme is purple to blue - --alert-gradient: var(--purple-to-blue-vertical-gradient); - --alert-accent: var(--bright-blue); - border-width: 0; - border-inline-start-width: 3px; - border-style: solid; - background: color-mix(in srgb, var(--alert-accent) 5%, transparent); - color: var(--primary-contrast); - border-image: var(--alert-gradient) 1; - padding: 1.5rem; - font-weight: 400; - transition: color 0.3s ease; - margin-block: 1rem; - position: relative; - - &::before { - font-family: var(--icons); - // content: icon is defined in each docs-alert class below... - position: absolute; - margin-top: -0.05rem; - color: var(--alert-accent); - font-size: 1.3rem; - } - - p { - margin-inline-start: 1.65rem; - } - - .adev-dark-mode & { - background: color-mix(in srgb, var(--alert-accent) 10%, transparent); - } - - .docs-pill-row { - margin-block-end: 0; - } - } - - .docs-viewer .docs-alert p { - margin-block: 0; - } - - .docs-alert-note { - --alert-gradient: var(--blue-to-teal-vertical-gradient); - --alert-accent: var(--bright-blue); - &::before { - content: 'bookmark'; - } - } - - .docs-alert-tip { - --alert-gradient: var(--green-to-cyan-vertical-gradient); - --alert-accent: var(--symbolic-cyan); - &::before { - content: 'star'; - } - } - - .docs-alert-todo { - --alert-gradient: var(--black-to-gray-vertical-gradient); - --alert-accent: var(--quaternary-contrast); - &::before { - content: 'error'; - } - } - - .docs-alert-question { - --alert-gradient: var(--blue-to-cyan-vertical-gradient); - --alert-accent: var(--symbolic-cyan); - &::before { - content: 'help'; - } - } - - .docs-alert-summary { - --alert-gradient: var(--purple-to-light-purple-vertical-gradient); - --alert-accent: var(--electric-violet); - &::before { - content: 'sms'; - } - } - - .docs-alert-tldr { - --alert-gradient: var(--pink-to-purple-vertical-gradient); - --alert-accent: var(--vivid-pink); - &::before { - content: 'speaker_notes'; - } - } - - .docs-alert-critical { - --alert-gradient: var(--red-to-orange-vertical-gradient); - --alert-accent: var(--orange-red); - &::before { - content: 'warning'; - } - } - - .docs-alert-important { - --alert-gradient: var(--red-to-pink-vertical-gradient); - --alert-accent: var(--hot-red); - &::before { - content: 'priority_high'; - } - } - - .docs-alert-helpful { - --alert-gradient: var(--orange-to-pink-vertical-gradient); - --alert-accent: var(--vivid-pink); - &::before { - content: 'check_circle'; - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_callout.scss b/adev/shared/src/lib/styles/docs/_callout.scss deleted file mode 100644 index 91a8e46ba7e..00000000000 --- a/adev/shared/src/lib/styles/docs/_callout.scss +++ /dev/null @@ -1,72 +0,0 @@ -// Callout - -@mixin docs-callout() { - .docs-callout { - // Default theme is purple to blue - --callout-theme: var(--purple-to-blue-horizontal-gradient); - border-width: 0; - border-block-start-width: 2px; - border-block-end-width: 1px; - border-style: solid; - margin-block: 1.5rem; - border-image: var(--callout-theme) 1; - position: relative; - - // Removes bottom line if followed by another callout - // Prevents too many lines/visual noise - &:has(+ .docs-callout) { - border-block-end-width: 0; - } - - &::before { - font-family: var(--icons); - // content: icon is defined in each docs-alert class below... - position: absolute; - right: 0; - margin-top: 1.35rem; - color: var(--alert-accent); - font-size: 1.3rem; - } - - // Callout heading - h2, - h3, - h4, - h5, - h6 { - background-image: var(--callout-theme); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - max-width: fit-content; - } - } - .docs-viewer .docs-callout h3 { - font-size: 0.875rem; - margin-block: 1.6rem; - } - - .docs-callout-helpful { - --callout-theme: var(--purple-to-blue-horizontal-gradient); - &::before { - content: 'check_circle'; - color: var(--bright-blue); - } - } - - .docs-callout-critical { - --callout-theme: var(--red-to-orange-horizontal-gradient); - &::before { - content: 'warning'; - color: var(--orange-red); - } - } - - .docs-callout-important { - --callout-theme: var(--pink-to-purple-horizontal-gradient); - &::before { - content: 'priority_high'; - color: var(--electric-violet); - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_card.scss b/adev/shared/src/lib/styles/docs/_card.scss deleted file mode 100644 index e50c1ff6545..00000000000 --- a/adev/shared/src/lib/styles/docs/_card.scss +++ /dev/null @@ -1,100 +0,0 @@ -// Card Grid - -@mixin docs-card() { - .docs-card-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - @container docs-content (max-width: 450px) { - grid-template-columns: 1fr; - } - grid-gap: 1.25rem; - margin-block: 1rem; - } - - .docs-card { - display: flex; - flex-direction: column; - justify-content: space-between; - color: var(--primary-contrast); - padding: 1.5rem; - border: 1px solid var(--senary-contrast); - border-radius: 0.25rem; - overflow: hidden; - transition: border-color 0.3s ease, background-color 0.3s ease; - - p:first-of-type { - margin-block-start: 1.5rem; - } - - p:last-of-type { - margin-block-end: 1.5rem; - } - - span { - font-size: 0.875rem; - font-weight: 500; - margin-block: 0; - - position: relative; - } - - * + *:not(a):not(code):not(span) { - margin-block: 1.5rem; - } - - &.docs-card-with-svg { - padding: 0; - - .docs-card-text-content { - flex-grow: 1; - margin-block-start: 0; - padding-inline: 1.5rem; - display: flex; - flex-direction: column; - justify-content: space-between; - border-block-start: 1px solid var(--senary-contrast); - h3 { - margin-bottom: 0; - margin-block-start: 1rem; - font-size: 1rem; - } - p { - margin-block-start: 0; - } - } - } - } - - // docs-card with link - a.docs-card { - display: flex; - flex-direction: column; - justify-content: space-between; - - span { - background: var(--pink-to-highlight-to-purple-to-blue-horizontal-gradient); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - font-size: 0.875rem; - margin-block: 0; - - transition: background-position 1.8s ease-out; - background-size: 200% 100%; - background-position: 100% 0%; - position: relative; - } - - &:hover { - span { - background-position: 0% 0%; - } - background: var(--subtle-purple); - } - } - - .docs-viewer .docs-card h3 { - margin-block-start: 0; - font-size: 1rem; - } -} diff --git a/adev/shared/src/lib/styles/docs/_code.scss b/adev/shared/src/lib/styles/docs/_code.scss deleted file mode 100644 index fa773c15e61..00000000000 --- a/adev/shared/src/lib/styles/docs/_code.scss +++ /dev/null @@ -1,443 +0,0 @@ -// TODO: Working on refactoring all code components & syntax highlighting - -/* stylelint-disable */ - -@mixin docs-code-block { - // code across docs, inline, blocks, shell, example viewer, etc. - code { - font-family: var(--code-font); - border-radius: 0.25rem; - font-weight: 400; - - // Inline code only - &:not(pre *) { - position: relative; - padding: 0 0.3rem; - background: var(--red-to-orange-horizontal-gradient); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - max-width: max-content; - width: 100%; - display: inline-block; - - &::before { - content: ''; - position: absolute; - inset: 0; - width: 100%; - height: 100%; - background: var(--subtle-purple); - border-radius: 0.25rem; - } - - a:not(.docs-anchor) > & { - position: relative; - padding: 0 0.3rem; - white-space: nowrap; - background: var(--purple-to-blue-horizontal-gradient); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - max-width: max-content; - - &::before { - content: ''; - position: absolute; - inset: 0; - width: 100%; - height: 100%; - background: var(--subtle-purple); - border-radius: 0.25rem; - transition: background 0.3s ease; - } - - &:hover { - background: var(--vivid-pink); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - max-width: max-content; - } - } - } - - // render inline code emjois without gradient - .docs-emoji { - color: initial; - } - } - - pre { - white-space: pre; - } - - // render inline code emjois without gradient - .docs-emoji { - color: inherit; - } - - // docs-code shell, multifile, mermaid diagrams - .docs-code { - .docs-viewer & { - margin-block: 1rem; - } - display: block; - position: relative; - border: 1px solid var(--senary-contrast); - border-radius: 0.25rem; - background: var(--octonary-contrast); - transition: background 0.3s ease, border-color 0.3s ease; - container: codeblock / inline-size; - - pre { - overflow-x: auto; - } - - code { - display: flex; - flex-direction: column; - font-size: 0.875rem; - counter-reset: line; - } - } - - // shell doesn't have a header, for commands only - .shell { - background-color: var(--quaternary-contrast); - border: 1px solid var(--quinary-contrast); - - pre { - white-space: nowrap; - } - - .hljs-ln-line { - color: var(--page-background); - &::before { - content: '$'; - padding-inline-end: 0.5rem; - } - } - - button[docs-copy-source-code] { - background-color: var(--quaternary-contrast); - border: 1px solid var(--quinary-contrast); - - @container codeblock (min-width: 400px) { - border: 1px solid transparent; - } - - .adev-copy { - path { - fill: var(--quinary-contrast); - } - } - .adev-check { - color: var(--page-background); - } - &:hover { - .adev-copy { - path { - fill: var(--octonary-contrast); - } - } - } - } - } - - // copy code button - button[docs-copy-source-code] { - padding: 0.375rem 0.4rem 0.15rem 0.5rem; - position: absolute; - top: 3.1rem; - right: 0.2rem; - border-radius: 0.25rem; - cursor: pointer; - z-index: var(--z-index-icon); - background-color: var(--octonary-contrast); - border: 1px solid var(--senary-contrast); - transition: background-color 0.3s ease, border-color 0.3s ease; - - @container codeblock (min-width: 400px) { - border: 1px solid transparent; - } - - .docs-icon { - transition: opacity 0.3s ease-out; - } - .adev-copy { - opacity: 1; - path { - transition: fill 0.3s ease; - fill: var(--gray-400); - } - } - .adev-check { - opacity: 0; - color: var(--primary-contrast); - position: absolute; - inset: 0; - top: 0.35rem; - path { - transition: fill 0.3s ease; - } - } - - &.docs-copy-source-code-button-success { - .adev-copy { - opacity: 0; - } - .adev-check { - opacity: 1; - } - } - - &:hover { - .adev-copy { - path { - fill: var(--primary-contrast); - } - } - } - } - - .docs-code .docs-code-header { - position: relative; - h3 { - background-image: var(--purple-to-blue-horizontal-gradient); - background-clip: text; - -webkit-background-clip: text; - color: transparent; - font-size: 0.875rem; - font-style: normal; - font-weight: 400; - line-height: 1.4rem; - letter-spacing: -0.00875rem; - margin: 0; - word-wrap: break-word; - width: fit-content; - } - } - - .docs-code-header { - padding: 0.75rem; - - // docs header background - &::before { - content: ''; - position: absolute; - inset: 0; - width: 100%; - height: 100%; - background: var(--subtle-purple); - border-radius: 0.25rem 0.25rem 0 0; - transition: background 0.3s ease; - } - } - - // Single line docs-code elements, without headers, shell code - .docs-code:not(:has(.docs-code-header)) { - button[docs-copy-source-code] { - top: 0.2rem; - right: 0.2rem; - } - } - - .docs-code[mermaid] { - border: 0; - width: 100%; - background-color: transparent; - } - - // Line numbers styling: Add a grid, if there are line numbers - .docs-code:not([mermaid]), - .docs-example-viewer-code-wrapper { - code:has(.hljs-ln-number) { - display: grid; - grid-template-columns: min-content 1fr; - height: 100%; - } - - pre { - overflow-x: auto; - display: flex; - flex-direction: column; - } - } - - .docs-example-viewer-code-wrapper { - .docs-code-header { - display: none; - } - } - - .hljs-ln-number { - border-inline-end: 1px solid var(--senary-contrast); - padding-inline-start: 0.75rem; - padding-inline-end: 0.5rem; - color: var(--quaternary-contrast); - font-size: 0.875rem; - text-align: right; - } - - .highlighted { - background: color-mix(in srgb, var(--bright-blue) 9%, var(--page-background)); - color: color-mix(in srgb, var(--bright-blue) 60%, var(--full-contrast)); - } - .remove { - background: color-mix(in srgb, var(--orange-red) 10%, var(--page-background)); - color: color-mix(in srgb, var(--hot-red) 80%, var(--full-contrast)); - } - .add { - background: color-mix(in srgb, oklch(68.82% 0.224 155.13) 12%, var(--page-background)); - color: color-mix(in srgb, var(--super-green), var(--full-contrast) 50%); - } - .hidden { - display: none; - } -} - -@mixin docs-syntax-highlighting { - .hljs-ln-line { - min-height: 1.375em; - padding-inline: 1rem; - color: var(--tertiary-contrast); - } - - .hljs-ln-group { - display: flex; - flex-direction: column; - margin: 1rem 0; - } - - .hljs-ln-line-deprecated { - text-decoration: line-through; - } - - .hljs-comment { - color: var(--quaternary-contrast); - } - .hljs-tag { - color: var(--bright-blue); - } - .hljs-operator { - color: var(--senary-contrast); - } - - .hljs-punctuation, - .hljs-subst { - color: var(--hot-red); - } - - .hljs-operator { - opacity: 0.7; - } - .hljs-bullet, - .hljs-deletion, - .hljs-name, - .hljs-selector-tag, - .hljs-template-variable, - .hljs-variable { - color: var(--bright-blue); - } - .hljs-attr, - .hljs-link, - .hljs-literal, - .hljs-number, - .hljs-symbol, - .hljs-variable.constant_ { - color: color-mix(in srgb, var(--vivid-pink) 80%, var(--full-contrast)); - } - .hljs-class .hljs-title, - .hljs-title, - .hljs-title.class_ { - color: color-mix(in srgb, var(--vivid-pink) 80%, var(--full-contrast)); - } - .hljs-strong { - font-weight: 700; - color: #ffcb6b; - } - .hljs-addition, - .hljs-code, - .hljs-string, - .hljs-title .class_ .inherited__ { - color: color-mix(in srgb, var(--orange-red) 80%, var(--full-contrast)); - } - - .hljs-doctag, - .hljs-keyword.hljs-atrule, - .hljs-quote, - .hljs-regexp { - color: var(--quinary-contrast); - } - - .hljs-built_in { - color: var(--bright-blue); - } - - .hljs-attribute, - .hljs-function .hljs-title, - .hljs-section, - .hljs-title.function_, - .ruby, - .hljs-property { - color: var(--hot-pink); - } - .diff .hljs-meta, - .hljs-keyword, - .hljs-template-tag, - .hljs-type { - color: var(--bright-blue); - } - .hljs-emphasis { - color: #c792ea; - font-style: italic; - } - .hljs-meta, - .hljs-meta .hljs-keyword, - .hljs-meta .hljs-string { - color: var(--bright-blue); - } - .hljs-meta .hljs-keyword, - .hljs-meta-keyword { - font-weight: 700; - } - - .gap { - color: var(--quaternary-contrast); - } - - .hljs-constructor { - color: var(--symbolic-cyan); - } - - .hljs-params { - color: var(--bright-blue); - } -} - -@mixin docs-code-editor { - .cm-tooltip-hover { - display: flex; - flex-direction: column-reverse; - padding: 0.75rem; - - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - } - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } - - &::-webkit-scrollbar-thumb { - background-color: var(--senary-contrast); - border-radius: 10px; - } - - &::-webkit-scrollbar-thumb:hover { - background-color: var(--quinary-contrast); - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_decorative-header.scss b/adev/shared/src/lib/styles/docs/_decorative-header.scss deleted file mode 100644 index 863f11c3277..00000000000 --- a/adev/shared/src/lib/styles/docs/_decorative-header.scss +++ /dev/null @@ -1,100 +0,0 @@ -@mixin docs-decorative-header() { - .docs-decorative-header-container { - container: header / inline-size; - } - - .docs-decorative-header { - border-radius: 0.625rem; - background: var(--septenary-contrast); - max-width: var(--page-width); - overflow: hidden; - display: flex; - position: relative; - transition: background 0.3s ease; - - @container header (max-width: 550px) { - flex-direction: column-reverse; - } - - .docs-header-content { - box-sizing: border-box; - padding: 1.5rem; - padding-inline-end: 0; - flex-grow: 1; - - @container header (max-width: 550px) { - width: 100%; - padding-block-end: 1.5rem; - } - - h1, - p, - span { - color: var(--primary-contrast); - transition: color 0.3s ease; - } - - a { - position: absolute; - top: 1.5rem; - right: 1.5rem; - z-index: 20; - i { - color: var(--quaternary-contrast); - } - - &:hover { - i { - color: var(--primary-contrast); - } - } - } - - docs-breadcrumb { - padding-block-end: 1rem; - } - - .docs-breadcrumb { - font-size: 0.875rem; - span { - color: var(--primary-contrast) !important; - } - } - } - - svg { - margin: 0; - margin-block: auto; - padding-inline: 1rem 3.5rem; - padding-block: 2rem; - min-width: 150px; - max-width: 250px; - max-height: 125px; - z-index: 0; - - &.adev-what-is-angular-svg { - max-height: 125px; - min-width: 175px; - padding-block-end: 0.5rem; - } - - &.adev-directives-svg { - max-height: 150px; - } - - &.adev-roadmap-svg { - padding-block-end: 0.5rem; - } - - @container header (max-width: 550px) { - padding: 2rem; - padding-block-end: 0; - padding-inline-start: 1.5rem; - width: fit-content; - min-width: auto; - max-width: 80%; - max-height: 125px; - } - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_icon.scss b/adev/shared/src/lib/styles/docs/_icon.scss deleted file mode 100644 index 06477d299db..00000000000 --- a/adev/shared/src/lib/styles/docs/_icon.scss +++ /dev/null @@ -1,13 +0,0 @@ -// Icon styles, primarily for docs - -@mixin docs-icon { - .docs-icon { - color: var(--quinary-contrast); - font-size: 1.5rem; - transition: color 0.3s ease; - } - - .docs-icon-small { - font-size: 1rem; - } -} diff --git a/adev/shared/src/lib/styles/docs/_pill.scss b/adev/shared/src/lib/styles/docs/_pill.scss deleted file mode 100644 index 9a028293a64..00000000000 --- a/adev/shared/src/lib/styles/docs/_pill.scss +++ /dev/null @@ -1,72 +0,0 @@ -// Pill - -@mixin docs-pill() { - .docs-pill { - display: flex; - align-items: center; - // Default blue - --pill-accent: var(--bright-blue); - background: color-mix(in srgb, var(--pill-accent) 5%, transparent); - // Darken the text a bit for contrast - color: color-mix(in srgb, var(--pill-accent) 70%, var(--full-contrast)); - padding-inline: 0.75rem; - padding-block: 0.375rem; - border-radius: 2.75rem; - border: 0; - transition: background 0.3s ease; - - font-family: var(--inter-font); - font-size: 0.875rem; - font-style: normal; - font-weight: 500; - line-height: 1.4rem; - letter-spacing: -0.00875rem; - - &:hover { - background: color-mix(in srgb, var(--pill-accent) 15%, transparent); - } - - .docs-icon-small { - margin-inline-start: 0.25rem; - } - - .adev-dark-mode & { - // Lighten the text a bit for contrast - color: color-mix(in srgb, var(--pill-accent) 60%, white 70%); - background: color-mix(in srgb, var(--pill-accent) 10%, white 2%); - &:hover { - background: color-mix(in srgb, var(--pill-accent) 20%, white 10%); - } - } - } - - .docs-pill-row { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 0.5rem; - margin-block: 0.75rem; - .docs-pill { - // TODO: This gradient supports longer rows - // we may want to refine it - &:nth-child(6n + 1) { - --pill-accent: var(--hot-red); - } - &:nth-child(6n + 2) { - --pill-accent: var(--hot-pink); - } - &:nth-child(6n + 3) { - --pill-accent: var(--electric-violet); - } - &:nth-child(6n + 4) { - --pill-accent: var(--french-violet); - } - &:nth-child(6n + 5) { - --pill-accent: var(--indigo-blue); - } - &:nth-child(6n + 6) { - --pill-accent: var(--bright-blue); - } - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_steps.scss b/adev/shared/src/lib/styles/docs/_steps.scss deleted file mode 100644 index 3446dc4d24d..00000000000 --- a/adev/shared/src/lib/styles/docs/_steps.scss +++ /dev/null @@ -1,82 +0,0 @@ -// Doc Steps/Ordered Doc section -// Did somebody order a doc? -@use '../../../../../shared/src/lib/styles/media-queries' as mq; - -@mixin docs-steps() { - .docs-steps { - --gutter: 4rem; - margin-top: -1.5rem; // even out docs-anchor margin-block-start - padding-inline-start: var(--gutter); - counter-reset: code-steps-list; - list-style-type: none; - li { - position: relative; - } - } - .docs-steps li h3 { - font-size: 1.75rem; - margin-block-start: 0; - margin-block-end: 0.5rem; - line-height: 2.5rem; - } - .docs-step-number { - counter-increment: code-steps-list; - display: block; - pointer-events: none; - position: absolute; - left: calc(var(--gutter) * -1); - top: 2.7rem; - bottom: 0; - &::before { - display: flex; - align-items: center; - justify-content: center; - width: 2rem; - content: counter(code-steps-list); - border-radius: 50%; - aspect-ratio: 1 / 1; - border: 1px solid transparent; - background-image: linear-gradient(var(--page-background), var(--page-background)), - var(--pink-to-purple-horizontal-gradient); - background-origin: border-box; - background-clip: content-box, border-box; - position: sticky; - top: 2rem; - // adjust for tablet nav bar height - @include mq.for-tablet-landscape-down { - top: calc(1rem + 75px); - } - // adjust for mobile nav bar height - @include mq.for-phone-only { - top: calc(1rem + 55px); - } - } - - .adev-tutorial-content & { - &::before { - // calc(1rem + sticky tutorial nav height) - top: calc(1rem + 120px); - - // adjust for tablet nav bar height - @include mq.for-tablet-landscape-down { - top: calc(1rem + 165px); - } - - // adjust for mobile nav bar height - @include mq.for-phone-only { - top: calc(1rem + 140px); - } - } - } - - .adev-tutorial-content:has(.adev-reveal-answer-button) & { - &::before { - // calc(1rem + sticky tutorial nav height - // + reveal answer button height when on smaller screens) - @container tutorial-content (max-width: 430px) { - top: calc(1rem + 175px); - } - } - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_table.scss b/adev/shared/src/lib/styles/docs/_table.scss deleted file mode 100644 index ff3c033f31f..00000000000 --- a/adev/shared/src/lib/styles/docs/_table.scss +++ /dev/null @@ -1,43 +0,0 @@ -// Table - -@mixin docs-table { - .docs-table { - overflow-x: auto; - - table { - width: 100%; - border-collapse: collapse; - margin-block: 1rem; - font-size: 0.875rem; - line-height: 160%; - letter-spacing: -0.00875rem; - } - - th { - text-align: left; - padding-block: 0.4rem; - padding-inline-end: 1.5rem; - border-block: 1px solid var(--senary-contrast); - font-size: 0.75rem; - font-weight: 600; - } - - tr { - td { - padding-block: 0.85rem; - vertical-align: top; - &:not(:last-child) { - padding-inline-end: 1rem; - } - } - td:first-child { - padding-inline-end: 1.62rem; - vertical-align: top; - min-width: 10ch; - } - &:not(:last-child) { - border-block-end: 1px solid var(--senary-contrast); - } - } - } -} diff --git a/adev/shared/src/lib/styles/docs/_video.scss b/adev/shared/src/lib/styles/docs/_video.scss deleted file mode 100644 index c20a6bb8e2b..00000000000 --- a/adev/shared/src/lib/styles/docs/_video.scss +++ /dev/null @@ -1,11 +0,0 @@ -@mixin docs-video() { - .docs-video-container { - iframe { - border: 0; - width: 100%; - border-radius: 0.25rem; - overflow: hidden; - aspect-ratio: 16 / 9; - } - } -} diff --git a/adev/shared/src/lib/styles/global-styles.scss b/adev/shared/src/lib/styles/global-styles.scss deleted file mode 100644 index 6feb1e62192..00000000000 --- a/adev/shared/src/lib/styles/global-styles.scss +++ /dev/null @@ -1,80 +0,0 @@ -// TODO: Continue organizing and refactoring this file -@use '@angular/material' as mat; - -// Using disable-next-line to avoid stylelint errors - these imports are necessary -// TODO: Is there another way to prevent these linting errors? -// stylelint-disable-next-line @angular/no-unused-import -@use '_colors'; -// stylelint-disable-next-line @angular/no-unused-import -@use '_z-index'; - -// Global -@use 'resets'; -@use 'typography'; -@use 'scroll-track'; -@use 'button'; -@use 'kbd'; -@use 'api-item-label'; -@use 'faceted-list'; -@use 'media-queries' as mq; - -// Docs -@use 'docs/alert'; -@use 'docs/callout'; -@use 'docs/card'; -@use 'docs/code'; -@use 'docs/decorative-header'; -@use 'docs/icon'; -@use 'docs/pill'; -@use 'docs/steps'; -@use 'docs/table'; -@use 'docs/video'; - -// Global -@include resets.resets(); -@include typography.typography(); -@include scroll-track.scroll-track(); -@include button.button(); -@include kbd.kbd(); -@include api-item-label.api-item-label(); -@include faceted-list.faceted-list(); - -@include mq.for-phone-only(); -@include mq.for-tablet-portrait-up(); -@include mq.for-tablet-landscape-up(); -@include mq.for-desktop-up(); -@include mq.for-big-desktop-up(); -@include mq.for-tablet-landscape-down(); - -// temporary just to show different options of code component UI. -$primary: mat.define-palette(mat.$indigo-palette); -$accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); -$theme: mat.define-light-theme( - ( - color: ( - primary: $primary, - accent: $accent, - ), - typography: mat.define-typography-config(), - ) -); - -// Include material core styles. -@include mat.core(); -@include mat.tabs-theme($theme); - -// Include custom docs styles -@include alert.docs-alert(); -@include callout.docs-callout(); -@include card.docs-card(); -@include code.docs-code-block(); -@include code.docs-code-editor(); -@include decorative-header.docs-decorative-header(); -@include icon.docs-icon(); -@include pill.docs-pill(); -@include steps.docs-steps(); -@include code.docs-syntax-highlighting(); -@include table.docs-table(); -@include video.docs-video(); - -// Include custom angular.dev styles diff --git a/adev/shared/src/lib/utils/animations.utils.ts b/adev/shared/src/lib/utils/animations.utils.ts deleted file mode 100644 index 29a0589e9b4..00000000000 --- a/adev/shared/src/lib/utils/animations.utils.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export const shouldReduceMotion = () => - typeof window !== 'undefined' && - window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true; diff --git a/adev/shared/src/lib/utils/device.utils.ts b/adev/shared/src/lib/utils/device.utils.ts deleted file mode 100644 index dfdd2a94c2d..00000000000 --- a/adev/shared/src/lib/utils/device.utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export const isMobile = - typeof window !== 'undefined' && window.navigator.userAgent.toLowerCase().includes('mobi'); - -export const isApple = - typeof window !== 'undefined' && - (/iPad|iPhone/.test(window.navigator.userAgent) || window.navigator.userAgent.includes('Mac')); - -export const isIpad = - typeof window !== 'undefined' && - isApple && - !!window.navigator.maxTouchPoints && - window.navigator.maxTouchPoints > 1; - -export const isIos = (isMobile && isApple) || isIpad; - -export const isFirefox = - typeof window !== 'undefined' && window.navigator.userAgent.includes('Firefox/'); diff --git a/adev/shared/src/lib/utils/filesystem.utils.ts b/adev/shared/src/lib/utils/filesystem.utils.ts deleted file mode 100644 index f8ff03cfcf7..00000000000 --- a/adev/shared/src/lib/utils/filesystem.utils.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {normalizePath} from './navigation.utils'; -import type {FileAndContent} from '../../../../../scripts/tutorials/tutorials-types'; - -interface DirEnt { - name: T; - isFile(): boolean; - isDirectory(): boolean; -} - -interface FileSystemAPI { - readdir( - path: string, - options: { - encoding?: BufferEncoding | null; - withFileTypes: true; - }, - ): Promise[]>; - readFile(path: string, encoding?: string): Promise; -} - -export const checkFilesInDirectory = async ( - dir: string, - fs: FileSystemAPI, - filterFoldersPredicate: (path?: string) => boolean = () => true, - files: FileAndContent[] = [], -) => { - const entries = (await fs.readdir(dir, {withFileTypes: true})) ?? []; - - for (const entry of entries) { - const fullPath = normalizePath(`${dir}/${entry.name}`); - - if (entry.isFile()) { - const content = await fs.readFile(fullPath, 'utf-8'); - files.push({content, path: fullPath}); - } else if (entry.isDirectory() && filterFoldersPredicate(entry.name)) { - await checkFilesInDirectory(fullPath, fs, filterFoldersPredicate, files); - } - } - - return files; -}; diff --git a/adev/shared/src/lib/utils/index.ts b/adev/shared/src/lib/utils/index.ts deleted file mode 100644 index 8790e9a5818..00000000000 --- a/adev/shared/src/lib/utils/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export * from './animations.utils'; -export * from './device.utils'; -export * from './filesystem.utils'; -export * from './navigation.utils'; -export * from './test.utils'; -export * from './url.utils'; -export * from './zip.utils'; diff --git a/adev/shared/src/lib/utils/navigation.utils.ts b/adev/shared/src/lib/utils/navigation.utils.ts deleted file mode 100644 index c6f348caf4a..00000000000 --- a/adev/shared/src/lib/utils/navigation.utils.ts +++ /dev/null @@ -1,164 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {inject} from '@angular/core'; -import {ActivatedRouteSnapshot, Route, Router} from '@angular/router'; -import {NavigationItem} from '../interfaces'; -import {DOCS_CONTENT_LOADER} from '../providers'; - -export const flatNavigationData = (tree: NavigationItem[]) => { - const result: NavigationItem[] = []; - - const traverse = (node: NavigationItem, level: number) => { - node.level = level; - if (node.path) { - result.push(node); - } - if (node.children) { - for (const child of node.children) { - child.parent = node; - traverse(child, level + 1); - } - } - }; - - for (const node of tree) { - traverse(node, 1); - } - - return result; -}; - -export const getNavigationItemsTree = ( - tree: NavigationItem[], - mapFn: (item: NavigationItem) => void, -) => { - const traverse = (node: NavigationItem) => { - mapFn(node); - if (node.children) { - for (const child of node.children) { - traverse(child); - } - } - }; - - for (const node of tree) { - traverse(node); - } - - return tree; -}; - -export const findNavigationItem = ( - items: NavigationItem[], - predicate: (item: NavigationItem) => boolean, -): NavigationItem | null => { - let result: NavigationItem | null = null; - - const traverse = (node: NavigationItem) => { - if (predicate(node)) { - result = node; - } - if (node.children && !result) { - for (const child of node.children) { - traverse(child); - } - } - }; - - for (const node of items) { - traverse(node); - } - - return result; -}; - -export const isExternalLink = (link: string, windowOrigin: string) => - new URL(link).origin !== windowOrigin; - -export const markExternalLinks = (item: NavigationItem, origin: string): void => { - if (item.path) { - try { - item.isExternal = isExternalLink(item.path, origin); - } catch (err) {} - } -}; - -export const mapNavigationItemsToRoutes = ( - navigationItems: NavigationItem[], - additionalRouteProperties: Partial, -): Route[] => - navigationItems - .filter((route): route is NavigationItem & {path: string} => Boolean(route.path)) - .map((navigationItem) => { - const route = { - path: navigationItem.path, - ...additionalRouteProperties, - }; - - route.data = { - ...navigationItem, - ...route.data, - }; - - route.resolve = { - 'docContent': (snapshot: ActivatedRouteSnapshot) => { - return snapshot.data['contentPath'] !== undefined - ? inject(DOCS_CONTENT_LOADER).getContent(snapshot.data['contentPath']) - : undefined; - }, - ...route.resolve, - }; - return route; - }); - -export const normalizePath = (path: string): string => { - if (path[0] === '/') { - return path.substring(1); - } - return path; -}; - -export const getBaseUrlAfterRedirects = (url: string, router: Router): string => { - const route = router.parseUrl(url); - route.fragment = null; - route.queryParams = {}; - return normalizePath(route.toString()); -}; - -export function handleHrefClickEventWithRouter(e: Event, router: Router) { - const pointerEvent = e as PointerEvent; - if ( - pointerEvent.ctrlKey || - pointerEvent.shiftKey || - pointerEvent.altKey || - pointerEvent.metaKey - ) { - return; - } - - const closestAnchor = (e.target as Element).closest('a'); - if (closestAnchor?.target && closestAnchor.target !== 'self') { - return; - } - - const relativeUrl = closestAnchor?.getAttribute?.('href'); - if (relativeUrl) { - e.preventDefault(); - router.navigateByUrl(relativeUrl); - } -} - -export function getActivatedRouteSnapshotFromRouter(router: Router): ActivatedRouteSnapshot { - let route = router.routerState.root.snapshot; - - while (route.firstChild) { - route = route.firstChild; - } - return route; -} diff --git a/adev/shared/src/lib/utils/test-utils.spec.ts b/adev/shared/src/lib/utils/test-utils.spec.ts deleted file mode 100644 index 25b0cba8e71..00000000000 --- a/adev/shared/src/lib/utils/test-utils.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -import {ChangeDetectorRef} from '@angular/core'; -import { - DirEnt, - ErrorListener, - FileSystemAPI, - FileSystemTree, - PortListener, - ServerReadyListener, - Unsubscribe, - WebContainer, - WebContainerProcess, -} from '@webcontainer/api'; - -export class FakeChangeDetectorRef implements ChangeDetectorRef { - markForCheck(): void {} - detach(): void {} - checkNoChanges(): void {} - reattach(): void {} - detectChanges(): void {} -} - -export class FakeWebContainer implements WebContainer { - fakeSpawn: FakeWebContainerProcess | undefined = undefined; - - constructor(fakeOptions?: {spawn: FakeWebContainerProcess}) { - if (fakeOptions?.spawn) this.fakeSpawn = fakeOptions.spawn; - } - - spawn(command: unknown, args?: unknown, options?: unknown): Promise { - if (this.fakeSpawn) return Promise.resolve(this.fakeSpawn); - - const fakeProcess = new FakeWebContainerProcess(); - - return Promise.resolve(fakeProcess); - } - on(event: 'port', listener: PortListener): Unsubscribe; - on(event: 'server-ready', listener: ServerReadyListener): Unsubscribe; - on(event: 'error', listener: ErrorListener): Unsubscribe; - on(event: unknown, listener: unknown): Unsubscribe { - return () => {}; - } - mount( - tree: FileSystemTree, - options?: {mountPoint?: string | undefined} | undefined, - ): Promise { - return Promise.resolve(); - } - get path() { - return '/fake-path'; - } - get workdir() { - return '/fake-workdir'; - } - - teardown() {} - - fs: FakeFileSystemAPI = new FakeFileSystemAPI(); -} - -class FakeFileSystemAPI implements FileSystemAPI { - readdir( - path: string, - options: 'buffer' | {encoding: 'buffer'; withFileTypes?: false | undefined}, - ): Promise; - readdir( - path: string, - options?: - | string - | {encoding?: string | null | undefined; withFileTypes?: false | undefined} - | null - | undefined, - ): Promise; - readdir( - path: string, - options: {encoding: 'buffer'; withFileTypes: true}, - ): Promise[]>; - readdir( - path: string, - options: {encoding?: string | null | undefined; withFileTypes: true}, - ): Promise[]>; - readdir( - path: unknown, - options?: unknown, - ): - | Promise - | Promise - | Promise[]> - | Promise[]> { - return Promise.resolve(['/fake-dirname']); - } - - readFile(path: string, encoding?: null | undefined): Promise; - readFile(path: string, encoding: string): Promise; - readFile(path: unknown, encoding?: unknown): Promise | Promise { - return Promise.resolve('fake file content'); - } - writeFile( - path: string, - data: string | Uint8Array, - options?: string | {encoding?: string | null | undefined} | null | undefined, - ): Promise { - return Promise.resolve(); - } - mkdir(path: string, options?: {recursive?: false | undefined} | undefined): Promise; - mkdir(path: string, options: {recursive: true}): Promise; - mkdir(path: unknown, options?: unknown): Promise | Promise { - return Promise.resolve(); - } - rm( - path: string, - options?: {force?: boolean | undefined; recursive?: boolean | undefined} | undefined, - ): Promise { - return Promise.resolve(); - } -} - -export class FakeWebContainerProcess implements WebContainerProcess { - exit: Promise = Promise.resolve(0); - input: WritableStream = new WritableStream(); - output: ReadableStream = new ReadableStream(); - - kill(): void {} - resize(dimensions: {cols: number; rows: number}): void {} -} diff --git a/adev/shared/src/lib/utils/test.utils.ts b/adev/shared/src/lib/utils/test.utils.ts deleted file mode 100644 index a83cea221bc..00000000000 --- a/adev/shared/src/lib/utils/test.utils.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export class FakeEventTarget implements EventTarget { - listeners: Map = new Map(); - - addEventListener(type: string, listener: EventListenerOrEventListenerObject): void { - const listeners = this.listeners.get(type) || []; - listeners.push(listener); - this.listeners.set(type, listeners); - } - - removeEventListener(type: string, listener: EventListenerOrEventListenerObject): void { - const listeners = this.listeners.get(type); - if (listeners) { - const index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - } - } - - dispatchEvent(event: Event): boolean { - const listeners = this.listeners.get(event.type); - if (listeners) { - for (const listener of listeners) { - if (typeof listener === 'function') { - listener.call(this, event); - } else { - listener.handleEvent(event); - } - } - } - return true; - } -} - -export class MockLocalStorage implements Pick { - private items = new Map(); - - getItem(key: string): string | null { - return this.items.get(key) ?? null; - } - - setItem(key: string, value: string | null): void { - this.items.set(key, value); - } -} diff --git a/adev/shared/src/lib/utils/url.utils.ts b/adev/shared/src/lib/utils/url.utils.ts deleted file mode 100644 index 14bee4d5411..00000000000 --- a/adev/shared/src/lib/utils/url.utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export const removeTrailingSlash = (url: string): string => { - if (url.endsWith('/')) { - return url.slice(0, -1); - } - return url; -}; diff --git a/adev/shared/src/lib/utils/zip.utils.ts b/adev/shared/src/lib/utils/zip.utils.ts deleted file mode 100644 index 5e3c2af7941..00000000000 --- a/adev/shared/src/lib/utils/zip.utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import type {FileAndContent} from '../../../../../scripts/tutorials/tutorials-types'; - -export async function generateZip(files: FileAndContent[]): Promise { - const {default: JSZip} = await import('jszip'); - - const zip = new JSZip(); - - for (const file of files) { - zip.file(file.path, file.content, {binary: true}); - } - - return await zip.generateAsync({type: 'blob'}); -} diff --git a/adev/shared/src/public-api.ts b/adev/shared/src/public-api.ts deleted file mode 100644 index 5fac2f4dba4..00000000000 --- a/adev/shared/src/public-api.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* - * Public API Surface of shared - */ - -export * from './lib/components'; -export * from './lib/constants'; -export * from './lib/directives'; -export * from './lib/interfaces'; -export * from './lib/providers'; -export * from './lib/services'; -export * from './lib/utils'; diff --git a/adev/shared/tsconfig.lib.json b/adev/shared/tsconfig.lib.json deleted file mode 100644 index ea2f1adaeff..00000000000 --- a/adev/shared/tsconfig.lib.json +++ /dev/null @@ -1,12 +0,0 @@ -/* To learn more about this file see: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "../../out-tsc/lib", - "declaration": true, - "declarationMap": true, - "inlineSources": true, - "types": [] - }, - "exclude": ["**/*.spec.ts"] -} diff --git a/adev/shared/tsconfig.lib.prod.json b/adev/shared/tsconfig.lib.prod.json deleted file mode 100644 index c786a5ebed2..00000000000 --- a/adev/shared/tsconfig.lib.prod.json +++ /dev/null @@ -1,10 +0,0 @@ -/* To learn more about this file see: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ -{ - "extends": "./tsconfig.lib.json", - "compilerOptions": { - "declarationMap": false - }, - "angularCompilerOptions": { - "compilationMode": "partial" - } -} diff --git a/adev/shared/tsconfig.spec.json b/adev/shared/tsconfig.spec.json deleted file mode 100644 index fb92161958a..00000000000 --- a/adev/shared/tsconfig.spec.json +++ /dev/null @@ -1,11 +0,0 @@ -/* To learn more about this file see: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "../../out-tsc/spec", - "types": ["jasmine"], - "skipDefaultLibCheck": true, - "lib": ["DOM", "ES2022"] - }, - "include": ["**/*.spec.ts", "**/*.d.ts"] -} diff --git a/adev/src/app/app.component.spec.ts b/adev/src/app/app.component.spec.ts index 24573f0fc52..352571ef2cd 100644 --- a/adev/src/app/app.component.spec.ts +++ b/adev/src/app/app.component.spec.ts @@ -10,7 +10,7 @@ import {TestBed} from '@angular/core/testing'; import {AppComponent} from './app.component'; import {provideRouter} from '@angular/router'; import {routes} from './routes'; -import {Search, WINDOW} from '@angular/docs-shared'; +import {Search, WINDOW} from '@angular/docs'; describe('AppComponent', () => { const fakeSearch = {}; diff --git a/adev/src/app/app.component.ts b/adev/src/app/app.component.ts index 7858b901c8b..09d190b4668 100644 --- a/adev/src/app/app.component.ts +++ b/adev/src/app/app.component.ts @@ -16,7 +16,7 @@ import { IS_SEARCH_DIALOG_OPEN, SearchDialog, WINDOW, -} from '@angular/docs-shared'; +} from '@angular/docs'; import {Footer} from './core/layout/footer/footer.component'; import {Navigation} from './core/layout/navigation/navigation.component'; import {SecondaryNavigation} from './core/layout/secondary-navigation/secondary-navigation.component'; diff --git a/adev/src/app/app.config.ts b/adev/src/app/app.config.ts index 737f1d8f4d3..dec5b8244ad 100644 --- a/adev/src/app/app.config.ts +++ b/adev/src/app/app.config.ts @@ -22,7 +22,7 @@ import { PREVIEWS_COMPONENTS, WINDOW, windowProvider, -} from '@angular/docs-shared'; +} from '@angular/docs'; import {provideClientHydration} from '@angular/platform-browser'; import {provideAnimationsAsync} from '@angular/platform-browser/animations/async'; import { diff --git a/adev/src/app/core/layout/footer/footer.component.spec.ts b/adev/src/app/core/layout/footer/footer.component.spec.ts index 569217deb4f..89883f91510 100644 --- a/adev/src/app/core/layout/footer/footer.component.spec.ts +++ b/adev/src/app/core/layout/footer/footer.component.spec.ts @@ -8,7 +8,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; import {RouterTestingModule} from '@angular/router/testing'; import {Footer} from './footer.component'; diff --git a/adev/src/app/core/layout/footer/footer.component.ts b/adev/src/app/core/layout/footer/footer.component.ts index b83915d7d20..a3a10ee7adb 100644 --- a/adev/src/app/core/layout/footer/footer.component.ts +++ b/adev/src/app/core/layout/footer/footer.component.ts @@ -8,7 +8,7 @@ import {CommonModule} from '@angular/common'; import {ChangeDetectionStrategy, Component} from '@angular/core'; -import {ExternalLink} from '@angular/docs-shared'; +import {ExternalLink} from '@angular/docs'; import {RouterLink} from '@angular/router'; import {GITHUB, X, MEDIUM, YOUTUBE} from './../../constants/links'; diff --git a/adev/src/app/core/layout/navigation/navigation.component.spec.ts b/adev/src/app/core/layout/navigation/navigation.component.spec.ts index aab578f39c2..fac51bb2afa 100644 --- a/adev/src/app/core/layout/navigation/navigation.component.spec.ts +++ b/adev/src/app/core/layout/navigation/navigation.component.spec.ts @@ -16,7 +16,7 @@ import {Theme, ThemeManager} from '../../services/theme-manager.service'; import {Version, signal} from '@angular/core'; import {of} from 'rxjs'; import {VersionManager} from '../../services/version-manager.service'; -import {Search, WINDOW} from '@angular/docs-shared'; +import {Search, WINDOW} from '@angular/docs'; describe('Navigation', () => { let component: Navigation; diff --git a/adev/src/app/core/layout/navigation/navigation.component.ts b/adev/src/app/core/layout/navigation/navigation.component.ts index e3310f49f2a..11553c4f5af 100644 --- a/adev/src/app/core/layout/navigation/navigation.component.ts +++ b/adev/src/app/core/layout/navigation/navigation.component.ts @@ -26,7 +26,7 @@ import { getBaseUrlAfterRedirects, isApple, IS_SEARCH_DIALOG_OPEN, -} from '@angular/docs-shared'; +} from '@angular/docs'; import {NavigationEnd, Router, RouterLink} from '@angular/router'; import {filter, map, startWith} from 'rxjs'; import {DOCS_ROUTES, REFERENCE_ROUTES, TUTORIALS_ROUTES} from '../../../routes'; diff --git a/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.spec.ts b/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.spec.ts index 2b7ca25f177..643090cdc14 100644 --- a/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.spec.ts +++ b/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.spec.ts @@ -9,7 +9,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {SecondaryNavigation} from './secondary-navigation.component'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; describe('SecondaryNavigation', () => { let component: SecondaryNavigation; diff --git a/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.ts b/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.ts index 6b0d22d9221..493983824aa 100644 --- a/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.ts +++ b/adev/src/app/core/layout/secondary-navigation/secondary-navigation.component.ts @@ -27,7 +27,7 @@ import { getNavigationItemsTree, markExternalLinks, shouldReduceMotion, -} from '@angular/docs-shared'; +} from '@angular/docs'; import {distinctUntilChanged, filter, map, skip, startWith} from 'rxjs'; import {SUB_NAVIGATION_DATA} from '../../../sub-navigation-data'; import {PagePrefix} from '../../enums/pages'; diff --git a/adev/src/app/core/services/a-dev-title-strategy.ts b/adev/src/app/core/services/a-dev-title-strategy.ts index 55cf39f5962..75a1daa0e97 100644 --- a/adev/src/app/core/services/a-dev-title-strategy.ts +++ b/adev/src/app/core/services/a-dev-title-strategy.ts @@ -7,7 +7,7 @@ */ import {Injectable} from '@angular/core'; -import {NavigationItem} from '@angular/docs-shared'; +import {NavigationItem} from '@angular/docs'; import {Title} from '@angular/platform-browser'; import {ActivatedRouteSnapshot, RouterStateSnapshot, TitleStrategy} from '@angular/router'; diff --git a/adev/src/app/core/services/content-loader.service.ts b/adev/src/app/core/services/content-loader.service.ts index b8f67268016..0890d9f048f 100644 --- a/adev/src/app/core/services/content-loader.service.ts +++ b/adev/src/app/core/services/content-loader.service.ts @@ -8,7 +8,7 @@ import {HttpClient} from '@angular/common/http'; import {Injectable, inject} from '@angular/core'; -import {DocContent, DocsContentLoader} from '@angular/docs-shared'; +import {DocContent, DocsContentLoader} from '@angular/docs'; import {Router} from '@angular/router'; import {firstValueFrom, map} from 'rxjs'; diff --git a/adev/src/app/core/services/example-content-loader.service.spec.ts b/adev/src/app/core/services/example-content-loader.service.spec.ts index b8a95f8c4ba..47e59d9a84b 100644 --- a/adev/src/app/core/services/example-content-loader.service.spec.ts +++ b/adev/src/app/core/services/example-content-loader.service.spec.ts @@ -9,7 +9,7 @@ import {TestBed} from '@angular/core/testing'; import {ExampleContentLoader} from './example-content-loader.service'; -import {PREVIEWS_COMPONENTS} from '@angular/docs-shared'; +import {PREVIEWS_COMPONENTS} from '@angular/docs'; describe('ExampleContentLoader', () => { let service: ExampleContentLoader; diff --git a/adev/src/app/core/services/example-content-loader.service.ts b/adev/src/app/core/services/example-content-loader.service.ts index 07d0cc5ab9f..2971e49aa7b 100644 --- a/adev/src/app/core/services/example-content-loader.service.ts +++ b/adev/src/app/core/services/example-content-loader.service.ts @@ -7,7 +7,7 @@ */ import {Injectable, Type, inject} from '@angular/core'; -import {PREVIEWS_COMPONENTS} from '@angular/docs-shared'; +import {PREVIEWS_COMPONENTS} from '@angular/docs'; @Injectable() export class ExampleContentLoader { diff --git a/adev/src/app/core/services/theme-manager.service.spec.ts b/adev/src/app/core/services/theme-manager.service.spec.ts index 5f50361d441..2c0d094c3a0 100644 --- a/adev/src/app/core/services/theme-manager.service.spec.ts +++ b/adev/src/app/core/services/theme-manager.service.spec.ts @@ -9,7 +9,7 @@ import {TestBed} from '@angular/core/testing'; import {ThemeManager} from './theme-manager.service'; -import {LOCAL_STORAGE} from '@angular/docs-shared'; +import {LOCAL_STORAGE} from '@angular/docs'; describe('ThemeManager', () => { let service: ThemeManager; diff --git a/adev/src/app/core/services/theme-manager.service.ts b/adev/src/app/core/services/theme-manager.service.ts index 209b205cbc8..8de4bcc3a59 100644 --- a/adev/src/app/core/services/theme-manager.service.ts +++ b/adev/src/app/core/services/theme-manager.service.ts @@ -8,7 +8,7 @@ import {DOCUMENT, isPlatformBrowser} from '@angular/common'; import {Injectable, PLATFORM_ID, inject, signal} from '@angular/core'; -import {LOCAL_STORAGE} from '@angular/docs-shared'; +import {LOCAL_STORAGE} from '@angular/docs'; import {Subject} from 'rxjs'; // Keep these constants in sync with the code in index.html diff --git a/adev/src/app/embedded-editor/alert-manager.service.ts b/adev/src/app/embedded-editor/alert-manager.service.ts index a80f46941b1..68a5833233e 100644 --- a/adev/src/app/embedded-editor/alert-manager.service.ts +++ b/adev/src/app/embedded-editor/alert-manager.service.ts @@ -7,7 +7,7 @@ */ import {Injectable, inject} from '@angular/core'; -import {LOCAL_STORAGE, WINDOW, isMobile} from '@angular/docs-shared'; +import {LOCAL_STORAGE, WINDOW, isMobile} from '@angular/docs'; import {MatSnackBar} from '@angular/material/snack-bar'; import {ErrorSnackBar, ErrorSnackBarData} from '../core/services/errors-handling/error-snack-bar'; diff --git a/adev/src/app/embedded-editor/code-editor/code-editor.component.ts b/adev/src/app/embedded-editor/code-editor/code-editor.component.ts index 7d1090ecd38..074043228b2 100644 --- a/adev/src/app/embedded-editor/code-editor/code-editor.component.ts +++ b/adev/src/app/embedded-editor/code-editor/code-editor.component.ts @@ -28,7 +28,7 @@ import {EmbeddedTutorialManager} from '../embedded-tutorial-manager.service'; import {CodeMirrorEditor} from './code-mirror-editor.service'; import {DiagnosticWithLocation, DiagnosticsState} from './services/diagnostics-state.service'; import {DownloadManager} from '../download-manager.service'; -import {IconComponent} from '@angular/docs-shared'; +import {IconComponent} from '@angular/docs'; export const REQUIRED_FILES = new Set(['src/main.ts', 'src/index.html']); diff --git a/adev/src/app/embedded-editor/download-manager.service.ts b/adev/src/app/embedded-editor/download-manager.service.ts index 7341aa77335..5ad784bdd61 100644 --- a/adev/src/app/embedded-editor/download-manager.service.ts +++ b/adev/src/app/embedded-editor/download-manager.service.ts @@ -8,7 +8,7 @@ import {DOCUMENT, isPlatformBrowser} from '@angular/common'; import {EnvironmentInjector, Injectable, PLATFORM_ID, inject} from '@angular/core'; -import {generateZip} from '@angular/docs-shared'; +import {generateZip} from '@angular/docs'; import {injectAsync} from '../core/services/inject-async'; @Injectable({ diff --git a/adev/src/app/embedded-editor/embedded-editor.component.ts b/adev/src/app/embedded-editor/embedded-editor.component.ts index 9bfd1ce78c1..953decbfad2 100644 --- a/adev/src/app/embedded-editor/embedded-editor.component.ts +++ b/adev/src/app/embedded-editor/embedded-editor.component.ts @@ -22,7 +22,7 @@ import { signal, } from '@angular/core'; import {takeUntilDestroyed, toObservable} from '@angular/core/rxjs-interop'; -import {IconComponent} from '@angular/docs-shared'; +import {IconComponent} from '@angular/docs'; import {MatTabGroup, MatTabsModule} from '@angular/material/tabs'; import {distinctUntilChanged, map} from 'rxjs'; diff --git a/adev/src/app/embedded-editor/node-runtime-sandbox.service.ts b/adev/src/app/embedded-editor/node-runtime-sandbox.service.ts index 18de1443d19..37f8ed6969c 100644 --- a/adev/src/app/embedded-editor/node-runtime-sandbox.service.ts +++ b/adev/src/app/embedded-editor/node-runtime-sandbox.service.ts @@ -7,7 +7,7 @@ */ import {DestroyRef, inject, Injectable, signal} from '@angular/core'; -import {checkFilesInDirectory} from '@angular/docs-shared'; +import {checkFilesInDirectory} from '@angular/docs'; import {FileSystemTree, WebContainer, WebContainerProcess} from '@webcontainer/api'; import {BehaviorSubject, filter, map, Subject} from 'rxjs'; diff --git a/adev/src/app/embedded-editor/node-runtime-state.service.ts b/adev/src/app/embedded-editor/node-runtime-state.service.ts index 720f38e4866..8d928e7d722 100644 --- a/adev/src/app/embedded-editor/node-runtime-state.service.ts +++ b/adev/src/app/embedded-editor/node-runtime-state.service.ts @@ -7,7 +7,7 @@ */ import {Injectable, signal} from '@angular/core'; -import {isFirefox, isIos} from '@angular/docs-shared'; +import {isFirefox, isIos} from '@angular/docs'; import {LoadingStep} from './enums/loading-steps'; import {OUT_OF_MEMORY_MSG} from './node-runtime-sandbox.service'; diff --git a/adev/src/app/embedded-editor/preview/preview-error.component.ts b/adev/src/app/embedded-editor/preview/preview-error.component.ts index 5dcea3551ec..8048f1ca7c8 100644 --- a/adev/src/app/embedded-editor/preview/preview-error.component.ts +++ b/adev/src/app/embedded-editor/preview/preview-error.component.ts @@ -8,7 +8,7 @@ import {NgIf, NgSwitch, NgSwitchCase} from '@angular/common'; import {ChangeDetectionStrategy, Component, inject} from '@angular/core'; -import {isFirefox, isIos} from '@angular/docs-shared'; +import {isFirefox, isIos} from '@angular/docs'; import {ErrorType, NodeRuntimeState} from '../node-runtime-state.service'; diff --git a/adev/src/app/embedded-editor/terminal/interactive-terminal.ts b/adev/src/app/embedded-editor/terminal/interactive-terminal.ts index 3032d58f99b..5575ff3c045 100644 --- a/adev/src/app/embedded-editor/terminal/interactive-terminal.ts +++ b/adev/src/app/embedded-editor/terminal/interactive-terminal.ts @@ -10,7 +10,7 @@ import {inject} from '@angular/core'; import {Subject} from 'rxjs'; import {Terminal} from 'xterm'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; import {CommandValidator} from './command-validator.service'; import {environment} from '../../../environments/environment'; diff --git a/adev/src/app/embedded-editor/terminal/terminal-handler.service.spec.ts b/adev/src/app/embedded-editor/terminal/terminal-handler.service.spec.ts index 101e79c5e12..ba5eb762eb9 100644 --- a/adev/src/app/embedded-editor/terminal/terminal-handler.service.spec.ts +++ b/adev/src/app/embedded-editor/terminal/terminal-handler.service.spec.ts @@ -8,7 +8,7 @@ import {TestBed} from '@angular/core/testing'; import {TerminalHandler} from './terminal-handler.service'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; describe('TerminalHandler', () => { let service: TerminalHandler; diff --git a/adev/src/app/embedded-editor/terminal/terminal.component.spec.ts b/adev/src/app/embedded-editor/terminal/terminal.component.spec.ts index ebd3a88378a..e6d894e85ac 100644 --- a/adev/src/app/embedded-editor/terminal/terminal.component.spec.ts +++ b/adev/src/app/embedded-editor/terminal/terminal.component.spec.ts @@ -11,7 +11,7 @@ import {By} from '@angular/platform-browser'; import {Terminal} from './terminal.component'; import {TerminalHandler, TerminalType} from './terminal-handler.service'; -import {FakeEventTarget, WINDOW} from '@angular/docs-shared'; +import {FakeEventTarget, WINDOW} from '@angular/docs'; describe('Terminal', () => { let component: Terminal; diff --git a/adev/src/app/embedded-editor/terminal/terminal.component.ts b/adev/src/app/embedded-editor/terminal/terminal.component.ts index dc2178d3311..5fc8515229d 100644 --- a/adev/src/app/embedded-editor/terminal/terminal.component.ts +++ b/adev/src/app/embedded-editor/terminal/terminal.component.ts @@ -22,7 +22,7 @@ import {fromEvent} from 'rxjs/internal/observable/fromEvent'; import {debounceTime} from 'rxjs/operators'; import {TerminalHandler, TerminalType} from './terminal-handler.service'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; import {NgIf} from '@angular/common'; @Component({ diff --git a/adev/src/app/features/docs/docs.component.spec.ts b/adev/src/app/features/docs/docs.component.spec.ts index 0803596f049..07d8c023411 100644 --- a/adev/src/app/features/docs/docs.component.spec.ts +++ b/adev/src/app/features/docs/docs.component.spec.ts @@ -10,7 +10,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import DocsComponent from './docs.component'; import {RouterTestingModule} from '@angular/router/testing'; -import {DOCS_CONTENT_LOADER, WINDOW} from '@angular/docs-shared'; +import {DOCS_CONTENT_LOADER, WINDOW} from '@angular/docs'; describe('DocsComponent', () => { let component: DocsComponent; diff --git a/adev/src/app/features/docs/docs.component.ts b/adev/src/app/features/docs/docs.component.ts index f319061524e..289f1c73cbc 100644 --- a/adev/src/app/features/docs/docs.component.ts +++ b/adev/src/app/features/docs/docs.component.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {DocContent, DocViewer} from '@angular/docs-shared'; +import {DocContent, DocViewer} from '@angular/docs'; import {Component, inject} from '@angular/core'; import {toSignal} from '@angular/core/rxjs-interop'; import {ActivatedRoute} from '@angular/router'; diff --git a/adev/src/app/features/home/components/views/lines-view.ts b/adev/src/app/features/home/components/views/lines-view.ts index 605f29a4ba8..36e4139ac9a 100644 --- a/adev/src/app/features/home/components/views/lines-view.ts +++ b/adev/src/app/features/home/components/views/lines-view.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {isMobile} from '@angular/docs-shared'; +import {isMobile} from '@angular/docs'; import type {OGLRenderingContext} from 'ogl'; import {View} from './view'; diff --git a/adev/src/app/features/home/home.component.ts b/adev/src/app/features/home/home.component.ts index 199e4f98650..67ba97190a7 100644 --- a/adev/src/app/features/home/home.component.ts +++ b/adev/src/app/features/home/home.component.ts @@ -20,7 +20,7 @@ import { ViewChild, inject, } from '@angular/core'; -import {WINDOW, shouldReduceMotion, isIos} from '@angular/docs-shared'; +import {WINDOW, shouldReduceMotion, isIos} from '@angular/docs'; import {RouterLink} from '@angular/router'; import {injectAsync} from '../../core/services/inject-async'; diff --git a/adev/src/app/features/home/services/home-animation.service.ts b/adev/src/app/features/home/services/home-animation.service.ts index 7acb4c89a0b..4c4f4d3c507 100644 --- a/adev/src/app/features/home/services/home-animation.service.ts +++ b/adev/src/app/features/home/services/home-animation.service.ts @@ -9,7 +9,7 @@ import {DOCUMENT} from '@angular/common'; import {DestroyRef, Injectable, inject} from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; -import {RESIZE_EVENT_DELAY, WEBGL_LOADED_DELAY, WINDOW} from '@angular/docs-shared'; +import {RESIZE_EVENT_DELAY, WEBGL_LOADED_DELAY, WINDOW} from '@angular/docs'; import {gsap} from 'gsap'; import {ScrollTrigger} from 'gsap/ScrollTrigger'; import {debounceTime, fromEvent} from 'rxjs'; diff --git a/adev/src/app/features/playground/playground.component.ts b/adev/src/app/features/playground/playground.component.ts index ebd20741cdb..4180fdf052f 100644 --- a/adev/src/app/features/playground/playground.component.ts +++ b/adev/src/app/features/playground/playground.component.ts @@ -17,7 +17,7 @@ import { Type, inject, } from '@angular/core'; -import {DocViewer, IconComponent} from '@angular/docs-shared'; +import {DocViewer, IconComponent} from '@angular/docs'; import {RouterLink} from '@angular/router'; import {PlaygroundTemplate} from '../../../../../../scripts/tutorials/tutorials-types'; diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.ts b/adev/src/app/features/references/api-items-section/api-items-section.component.ts index 728e4463a82..1a426263bba 100644 --- a/adev/src/app/features/references/api-items-section/api-items-section.component.ts +++ b/adev/src/app/features/references/api-items-section/api-items-section.component.ts @@ -11,7 +11,7 @@ import {NgFor, NgIf} from '@angular/common'; import {RouterLink} from '@angular/router'; import {ApiItemsGroup} from '../interfaces/api-items-group'; import ApiItemLabel from '../api-item-label/api-item-label.component'; -import {IconComponent} from '@angular/docs-shared'; +import {IconComponent} from '@angular/docs'; @Component({ selector: 'adev-api-items-section', 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 3c2a5ff0b63..6045e8b8fe7 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 @@ -20,7 +20,7 @@ import {DOCUMENT, NgFor, NgIf} from '@angular/common'; import {MatTabGroup, MatTabsModule} from '@angular/material/tabs'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {distinctUntilChanged, map} from 'rxjs'; -import {DocContent, DocViewer} from '@angular/docs-shared'; +import {DocContent, DocViewer} from '@angular/docs'; import {ActivatedRoute, Router, RouterLink} from '@angular/router'; import {ApiItemType} from './../interfaces/api-item-type'; import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service'; diff --git a/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts b/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts index 0f4d1e8fe54..38ac5986102 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts +++ b/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts @@ -9,7 +9,7 @@ import {ChangeDetectionStrategy, Component, computed, inject, signal} from '@angular/core'; import ApiItemsSection from '../api-items-section/api-items-section.component'; import {FormsModule} from '@angular/forms'; -import {SlideToggle, TextField} from '@angular/docs-shared'; +import {SlideToggle, TextField} from '@angular/docs'; import {NgFor, NgIf} from '@angular/common'; import {ApiItemType} from '../interfaces/api-item-type'; import {ApiReferenceManager} from './api-reference-manager.service'; diff --git a/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts b/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts index b0c24512d1f..0f6c3404edf 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts +++ b/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts @@ -9,7 +9,7 @@ import {TestBed} from '@angular/core/testing'; import {ApiReferenceManager} from './api-reference-manager.service'; -import {LOCAL_STORAGE} from '@angular/docs-shared'; +import {LOCAL_STORAGE} from '@angular/docs'; describe('ApiReferenceManager', () => { let service: ApiReferenceManager; 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 348a07c06f6..7275cb838b7 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 @@ -16,7 +16,7 @@ import { signal, } from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; -import {DocContent, DocViewer} from '@angular/docs-shared'; +import {DocContent, DocViewer} from '@angular/docs'; import {ActivatedRoute} from '@angular/router'; import {map} from 'rxjs'; import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service'; diff --git a/adev/src/app/features/references/helpers/manifest.helper.ts b/adev/src/app/features/references/helpers/manifest.helper.ts index 09c675fb350..0e5da595d78 100644 --- a/adev/src/app/features/references/helpers/manifest.helper.ts +++ b/adev/src/app/features/references/helpers/manifest.helper.ts @@ -10,7 +10,7 @@ import {Route} from '@angular/router'; import API_MANIFEST_JSON from '../../../../../src/assets/api/manifest.json'; import {ApiManifest, ApiManifestItem} from '../interfaces/api-manifest'; import {PagePrefix} from '../../../core/enums/pages'; -import {NavigationItem, contentResolver} from '@angular/docs-shared'; +import {NavigationItem, contentResolver} from '@angular/docs'; export const ANGULAR_PACKAGE_PREFIX = '@angular/'; 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 4da8b70456d..ed68f8af8f0 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 @@ -25,7 +25,7 @@ import { API_TAB_ACTIVE_CODE_LINE, MEMBER_ID_ATTRIBUTE, } from '../constants/api-reference-prerender.constants'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; export const SCROLL_EVENT_DELAY = 20; export const SCROLL_THRESHOLD = 20; diff --git a/adev/src/app/features/tutorial/tutorial.component.spec.ts b/adev/src/app/features/tutorial/tutorial.component.spec.ts index 819950325c3..bd348f8a367 100644 --- a/adev/src/app/features/tutorial/tutorial.component.spec.ts +++ b/adev/src/app/features/tutorial/tutorial.component.spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {DOCS_VIEWER_SELECTOR, DocViewer, WINDOW} from '@angular/docs-shared'; +import {DOCS_VIEWER_SELECTOR, DocViewer, WINDOW} from '@angular/docs'; import {Component, Input, signal} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; diff --git a/adev/src/app/features/tutorial/tutorial.component.ts b/adev/src/app/features/tutorial/tutorial.component.ts index 3a8384c1f09..2470414f9b6 100644 --- a/adev/src/app/features/tutorial/tutorial.component.ts +++ b/adev/src/app/features/tutorial/tutorial.component.ts @@ -29,7 +29,7 @@ import { IconComponent, NavigationItem, NavigationList, -} from '@angular/docs-shared'; +} from '@angular/docs'; import {ActivatedRoute, RouterLink} from '@angular/router'; import {filter} from 'rxjs'; diff --git a/adev/src/app/native-scroll.ts b/adev/src/app/native-scroll.ts index 20c637fb5e5..e3525b9a71f 100644 --- a/adev/src/app/native-scroll.ts +++ b/adev/src/app/native-scroll.ts @@ -12,7 +12,7 @@ import { ENVIRONMENT_INITIALIZER, ApplicationRef, } from '@angular/core'; -import {WINDOW} from '@angular/docs-shared'; +import {WINDOW} from '@angular/docs'; import {ɵafterNextNavigation as afterNextNavigation, Router} from '@angular/router'; import {firstValueFrom, filter, take} from 'rxjs'; diff --git a/adev/src/app/routes.ts b/adev/src/app/routes.ts index 4cf9aefada8..e460086bc59 100644 --- a/adev/src/app/routes.ts +++ b/adev/src/app/routes.ts @@ -10,7 +10,7 @@ import { contentResolver, flatNavigationData, mapNavigationItemsToRoutes, -} from '@angular/docs-shared'; +} from '@angular/docs'; import {Route} from '@angular/router'; import {DefaultPage, PagePrefix} from './core/enums/pages'; diff --git a/adev/src/app/sub-navigation-data.ts b/adev/src/app/sub-navigation-data.ts index eddfd56a1c4..5d99c23fbde 100644 --- a/adev/src/app/sub-navigation-data.ts +++ b/adev/src/app/sub-navigation-data.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {NavigationItem} from '@angular/docs-shared'; +import {NavigationItem} from '@angular/docs'; import TUTORIALS_SUB_NAVIGATION_DATA_JSON from '../../src/assets/tutorials/routes/tutorials.json';