Skip to content

Commit

Permalink
feat(docs-infra): put the editor back in angular/angular (angular#53540)
Browse files Browse the repository at this point in the history
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
josephperrott authored and something43234523454325 committed Dec 12, 2023
1 parent 63fd649 commit d9348be
Show file tree
Hide file tree
Showing 69 changed files with 6,347 additions and 17 deletions.
105 changes: 105 additions & 0 deletions adev/src/app/editor/alert-manager.service.ts
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 adev/src/app/editor/code-editor/code-editor.component.html
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 adev/src/app/editor/code-editor/code-editor.component.scss
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);
}
}
}
Loading

0 comments on commit d9348be

Please sign in to comment.