forked from angular/angular
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(docs-infra): put the editor back in angular/angular (angular#53540)
Puts the editor back in angular/angular until we are able to set up a package containing a worker as needed for this project PR Close angular#53540
- Loading branch information
1 parent
63fd649
commit d9348be
Showing
69 changed files
with
6,347 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/*! | ||
* @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} from '@angular/core'; | ||
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'; | ||
|
||
export const MAX_RECOMMENDED_WEBCONTAINERS_INSTANCES = 3; | ||
export const WEBCONTAINERS_COUNTER_KEY = 'numberOfWebcontainers'; | ||
|
||
export enum AlertReason { | ||
OUT_OF_MEMORY, | ||
MOBILE, | ||
} | ||
|
||
@Injectable({providedIn: 'root'}) | ||
export class AlertManager { | ||
private readonly localStorage = inject(LOCAL_STORAGE); | ||
private readonly window = inject(WINDOW); | ||
private snackBar = inject(MatSnackBar); | ||
|
||
init(): void { | ||
this.listenToLocalStorageValuesChange(); | ||
|
||
this.increaseInstancesCounter(); | ||
|
||
this.decreaseInstancesCounterOnPageClose(); | ||
|
||
this.checkDevice(); | ||
} | ||
|
||
private listenToLocalStorageValuesChange(): void { | ||
this.window.addEventListener('storage', () => { | ||
const countOfRunningInstances = this.getStoredCountOfWebcontainerInstances(); | ||
|
||
this.validateRunningInstances(countOfRunningInstances); | ||
}); | ||
} | ||
|
||
// Increase count of the running instances of the webcontainers when user will boot the webcontainer | ||
private increaseInstancesCounter(): void { | ||
const countOfRunningInstances = this.getStoredCountOfWebcontainerInstances() + 1; | ||
|
||
this.localStorage?.setItem(WEBCONTAINERS_COUNTER_KEY, countOfRunningInstances.toString()); | ||
this.validateRunningInstances(countOfRunningInstances); | ||
} | ||
|
||
// Decrease count of running instances of the webcontainers when user close the app. | ||
private decreaseInstancesCounterOnPageClose(): void { | ||
this.window.addEventListener('beforeunload', () => { | ||
const countOfRunningInstances = this.getStoredCountOfWebcontainerInstances() - 1; | ||
|
||
this.localStorage?.setItem(WEBCONTAINERS_COUNTER_KEY, countOfRunningInstances.toString()); | ||
this.validateRunningInstances(countOfRunningInstances); | ||
}); | ||
} | ||
|
||
private getStoredCountOfWebcontainerInstances(): number { | ||
const countStoredInLocalStorage = this.localStorage?.getItem(WEBCONTAINERS_COUNTER_KEY); | ||
|
||
if (!countStoredInLocalStorage || Number.isNaN(countStoredInLocalStorage)) { | ||
return 0; | ||
} | ||
|
||
return Number(countStoredInLocalStorage); | ||
} | ||
|
||
private validateRunningInstances(countOfRunningInstances: number): void { | ||
if (countOfRunningInstances > MAX_RECOMMENDED_WEBCONTAINERS_INSTANCES) { | ||
this.openSnackBar(AlertReason.OUT_OF_MEMORY); | ||
} | ||
} | ||
|
||
private checkDevice() { | ||
if (isMobile) { | ||
this.openSnackBar(AlertReason.MOBILE); | ||
} | ||
} | ||
|
||
private openSnackBar(reason: AlertReason) { | ||
let message = ''; | ||
switch (reason) { | ||
case AlertReason.OUT_OF_MEMORY: | ||
message = `Your browser is currently limiting the memory available to run the Angular Tutorials or Playground. If you have multiple tabs open with Tutorials or Playground, please close some of them and refresh this page.`; | ||
break; | ||
case AlertReason.MOBILE: | ||
message = `You are running the embedded editor in a mobile device, this may result in an Out of memory error.`; | ||
break; | ||
} | ||
|
||
this.snackBar.openFromComponent(ErrorSnackBar, { | ||
panelClass: 'adev-invert-mode', | ||
data: { | ||
message, | ||
actionText: 'I understand', | ||
} satisfies ErrorSnackBarData, | ||
}); | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
adev/src/app/editor/code-editor/code-editor.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<!-- Code Editor Tabs --> | ||
<div class="adev-code-editor-tabs"> | ||
<div class="adev-tabs-and-plus"> | ||
<mat-tab-group animationDuration="0ms" mat-stretch-tabs="false"> | ||
<!-- | ||
Hint: we would like to keep only one instance of #codeEditorWrapper, | ||
that's why we're not placing this element as content of mat-tab, just to | ||
not call another time init method from CodeMirrorEditor service. | ||
--> | ||
@for (file of files(); track file) { | ||
<mat-tab #tab> | ||
<ng-template mat-tab-label> | ||
{{ file.filename.replace('src/', '') }} | ||
@if (tab.isActive && canDeleteFile(file.filename)) { | ||
<button | ||
class="adev-delete-file" | ||
aria-label="Delete file" | ||
(click)="deleteFile(file.filename)" | ||
> | ||
<docs-icon>delete</docs-icon> | ||
</button> | ||
} | ||
</ng-template> | ||
</mat-tab> | ||
} @if (isCreatingFile()) { | ||
<mat-tab> | ||
<ng-template mat-tab-label> | ||
<form (submit)="createFile($event)"> | ||
<input | ||
name="new-file" | ||
class="adev-new-file-input" | ||
#createFileInput | ||
(keydown)="$event.stopPropagation()" | ||
/> | ||
</form> | ||
</ng-template> | ||
</mat-tab> | ||
} | ||
</mat-tab-group> | ||
|
||
<button class="adev-add-file" (click)="onAddButtonClick()" aria-label="Add a new file"> | ||
<docs-icon>add</docs-icon> | ||
</button> | ||
</div> | ||
|
||
<button | ||
class="adev-editor-download-button" | ||
type="button" | ||
(click)="downloadCurrentCodeEditorState()" | ||
aria-label="Download current code in editor" | ||
> | ||
<docs-icon>download</docs-icon> | ||
</button> | ||
</div> | ||
<!-- Code Editor --> | ||
<div #codeEditorWrapper class="adev-code-editor-wrapper"></div> | ||
|
||
@if (displayErrorsBox()) { | ||
<div class="adev-inline-errors-box"> | ||
<button type="button" (click)="closeErrorsBox()"> | ||
<docs-icon class="docs-icon_high-contrast">close</docs-icon> | ||
</button> | ||
<ul> | ||
@for (error of errors(); track error) { | ||
<li>(line: {{ error.lineNumber }}:{{ error.characterPosition }}) {{ error.message }}</li> | ||
} | ||
</ul> | ||
</div> | ||
} |
185 changes: 185 additions & 0 deletions
185
adev/src/app/editor/code-editor/code-editor.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
:host { | ||
// backgrounds | ||
--code-editor-selection-background: color-mix( | ||
in srgb, | ||
var(--selection-background) 5%, | ||
var(--octonary-contrast) | ||
); | ||
--code-editor-focused-selection-background: color-mix( | ||
in srgb, | ||
var(--selection-background) 12%, | ||
var(--octonary-contrast) | ||
); | ||
|
||
// text base colors | ||
--code-editor-text-base-color: var(--primary-contrast); | ||
|
||
// tooltips | ||
--code-editor-tooltip-background: color-mix( | ||
in srgb, | ||
var(--bright-blue), | ||
var(--page-background) 90% | ||
); | ||
--code-editor-tooltip-color: var(--primary-contrast); | ||
--code-editor-tooltip-border: 1px solid | ||
color-mix(in srgb, var(--bright-blue), var(--page-background) 70%); | ||
--code-editor-tooltip-border-radius: 0.25rem; | ||
|
||
// autocomplete | ||
--code-editor-autocomplete-item-background: var(--senary-contrast); | ||
--code-editor-autocomplete-item-color: var(--primary-contrast); | ||
|
||
// cursor | ||
--code-name: var(--primary-contrast); | ||
--code-editor-cursor-color: var(--code-name); | ||
--code-variable-name: var(--bright-blue); | ||
--code-property-name: var(--code-name); | ||
--code-definition-keyword: var(--electric-violet); | ||
|
||
// comments | ||
--code-comment: var(--electric-violet); | ||
--code-line-comment: var(--symbolic-gray); | ||
--code-block-comment: var(--symbolic-brown); | ||
--code-doc-comment: var(--code-comment); | ||
|
||
// keywords | ||
--code-keyword: var(--electric-violet); | ||
--code-modifier: var(--code-keyword); | ||
--code-operator-keyword: var(--code-keyword); | ||
--code-control-keyword: var(--code-keyword); | ||
--code-module-keyword: var(--code-keyword); | ||
|
||
// structural | ||
--code-brace: var(--vivid-pink); | ||
|
||
// Misc | ||
--code-bool: var(--bright-blue); | ||
--code-string: var(--orange-red); | ||
--code-regexp: var(--orange-red); | ||
|
||
--code-tags: var(--bright-blue); | ||
--code-component: var(--primary-contrast); | ||
--code-type-name: var(--vivid-pink); | ||
--code-self: var(--orange-red); | ||
|
||
position: relative; | ||
} | ||
|
||
.adev-code-editor-tabs { | ||
display: flex; | ||
background: var(--octonary-contrast); | ||
border-block-end: 1px solid var(--senary-contrast); | ||
transition: background 0.3s ease, border 0.3s ease; | ||
} | ||
|
||
.adev-tabs-and-plus { | ||
display: flex; | ||
width: calc(100% - 2.875rem); | ||
min-height: 48px; | ||
} | ||
|
||
.adev-add-file, | ||
.adev-delete-file { | ||
docs-icon { | ||
color: var(--gray-400); | ||
transition: color 0.3s ease; | ||
font-size: 1.2rem; | ||
} | ||
&:hover { | ||
docs-icon { | ||
color: var(--primary-contrast); | ||
} | ||
} | ||
} | ||
|
||
.adev-delete-file { | ||
padding-inline-start: 0.1rem; | ||
padding-inline-end: 0; | ||
margin-block-start: 0.2rem; | ||
docs-icon { | ||
font-size: 1rem; | ||
} | ||
} | ||
|
||
.adev-new-file-input { | ||
color: var(--primary-contrast); | ||
border: none; | ||
border-radius: 0; | ||
border-block-end: 1px solid var(--senary-contrast); | ||
background: transparent; | ||
outline: none; | ||
transition: color 0.3s ease; | ||
&:focus { | ||
background: transparent; | ||
border-block-end: 1px solid var(--primary-contrast); | ||
} | ||
} | ||
|
||
.adev-code-editor-wrapper { | ||
// adjust height for terminal tabs | ||
// so that scroll bar & content do not render under tabs | ||
height: calc(100% - 49px); | ||
|
||
&-hidden { | ||
display: none; | ||
} | ||
} | ||
|
||
.adev-inline-errors-box { | ||
position: absolute; | ||
bottom: 0.5rem; | ||
left: 0.5rem; | ||
right: 0.5rem; | ||
padding: 0.25rem; | ||
background-color: color-mix(in srgb, var(--bright-blue), var(--page-background) 90%); | ||
border: 1px solid color-mix(in srgb, var(--bright-blue), var(--page-background) 70%); | ||
border-radius: 0.25rem; | ||
|
||
button { | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
padding: 0.25rem; | ||
|
||
docs-icon { | ||
font-size: 1.25rem; | ||
color: var(--quaternary-contrast); | ||
transition: color 0.3s ease; | ||
} | ||
|
||
&:hover { | ||
docs-icon { | ||
color: var(--primary-contrast); | ||
} | ||
} | ||
} | ||
|
||
ul { | ||
padding: 0; | ||
margin: 0; | ||
margin-inline: 1.25rem; | ||
color: var(--tertiary-contrast); | ||
max-height: 200px; | ||
overflow: auto; | ||
} | ||
} | ||
|
||
.adev-editor-download-button { | ||
padding: 0; | ||
width: 2.875rem; | ||
border-radius: 0 0.19rem 0 0; | ||
background: var(--octonary-contrast); | ||
border-inline-start: 1px solid var(--senary-contrast); | ||
|
||
docs-icon { | ||
color: var(--gray-400); | ||
transition: color 0.3s ease; | ||
font-size: 1.3rem; | ||
} | ||
|
||
&:hover { | ||
docs-icon { | ||
color: var(--primary-contrast); | ||
} | ||
} | ||
} |
Oops, something went wrong.