added html and css editors
This commit is contained in:
+17
-3
@@ -1,11 +1,25 @@
|
||||
<div class="flex-vert">
|
||||
<div class="flex-shrink section top">
|
||||
code editor
|
||||
<h1>code editor</h1>
|
||||
<window-control>
|
||||
<window-control-connector target="code-editor-container[language='Javascript']" class="active">Javascript</window-control-connector>
|
||||
<window-control-connector target="code-editor-container[language='HTML']">HTML</window-control-connector>
|
||||
<window-control-connector target="code-editor-container[language='CSS']">CSS</window-control-connector>
|
||||
</window-control>
|
||||
</div>
|
||||
<div class="flex-horiz">
|
||||
<code-editor style="min-width: 50%; max-width: 50%;"></code-editor>
|
||||
<div class="code-editor-containers resize-target flex-vert" style="min-width: 50%; max-width: 50%;">
|
||||
<code-editor-container language="Javascript" dir="vertical">
|
||||
</code-editor-container>
|
||||
|
||||
<code-editor-container language="HTML" dir="vertical" style="display: none;">
|
||||
</code-editor-container>
|
||||
|
||||
<code-editor-container language="CSS" dir="vertical" style="display: none;">
|
||||
</code-editor-container>
|
||||
</div>
|
||||
<div>
|
||||
<resize-handle></resize-handle>
|
||||
<resize-handle target=".resize-target" dir="horizontal"></resize-handle>
|
||||
<output-frame></output-frame>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+30
-1
@@ -8,10 +8,12 @@ html, body {
|
||||
background: @median-bg-white;
|
||||
}
|
||||
|
||||
|
||||
.flex-horiz {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flex-vert {
|
||||
@@ -28,6 +30,10 @@ html, body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.animate .flex-horiz > *, .animate .flex-vert > * {
|
||||
transition: all 280ms ease-in-out;
|
||||
}
|
||||
|
||||
.flex-horiz .flex-shrink, .flex-vert .flex-shrink {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 1;
|
||||
@@ -60,6 +66,16 @@ body {
|
||||
|
||||
.section.top {
|
||||
border-bottom: 1px solid @primary-border-color-white;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.section.top h1 {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
font-family: @primary-font-family;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.section.bottom {
|
||||
@@ -69,6 +85,15 @@ body {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.code-editor-containers {
|
||||
background: @editor-bg-white;
|
||||
}
|
||||
|
||||
.code-editor-containers.force-close {
|
||||
min-width: 0 !important;
|
||||
max-width: 0 !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: @median-bg-dark;
|
||||
@@ -86,4 +111,8 @@ body {
|
||||
.section.bottom {
|
||||
border-color: @primary-bg-color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.code-editor-containers {
|
||||
background: @editor-bg-dark;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import { OutputFrame } from './elements/output-frame/output-frame';
|
||||
import { NotificationBubbles } from './elements/notification-bubbles/notification-bubbles';
|
||||
import { NotificationBubble } from './elements/notification-bubbles/notification-bubble';
|
||||
import { ResizeHandle } from './elements/resize-handle/resize-handle';
|
||||
import { CodeEditorContainer } from './elements/code-editor-container/code-editor-container';
|
||||
import { WindowControl } from './elements/window-control/window-control';
|
||||
import { WindowControlConnector } from './elements/window-control/window-control connector';
|
||||
|
||||
window.customElements.define('app-root', AppRoot);
|
||||
window.customElements.define('code-editor', CodeEditor);
|
||||
@@ -13,6 +16,9 @@ window.customElements.define('output-frame', OutputFrame);
|
||||
window.customElements.define('notification-bubbles', NotificationBubbles);
|
||||
window.customElements.define('notification-bubble', NotificationBubble);
|
||||
window.customElements.define('resize-handle', ResizeHandle);
|
||||
window.customElements.define('code-editor-container', CodeEditorContainer);
|
||||
window.customElements.define('window-control', WindowControl);
|
||||
window.customElements.define('window-control-connector', WindowControlConnector);
|
||||
|
||||
|
||||
document.body.innerHTML += bootHtml;
|
||||
@@ -0,0 +1,26 @@
|
||||
@import url('../../../shared/theme.less');
|
||||
|
||||
div > code-editor-container.active:last-child {
|
||||
min-height: auto !important;
|
||||
max-height: none !important;
|
||||
min-width: auto !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.code-editor-header {
|
||||
padding: 10px 18px;
|
||||
background: lighten(@editor-bg-white, 1%);
|
||||
color: @primary-text-color-white;
|
||||
font-size: 13px;
|
||||
border-style: solid none;
|
||||
border-color: @primary-bg-color-white;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.code-editor-header {
|
||||
background: lighten(@editor-bg-dark, 1%);
|
||||
color: @primary-text-color-dark;
|
||||
border-color: @primary-bg-color-dark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { BaseElement } from "../../../shared/_base";
|
||||
import { CodeEditor } from "../code-editor/code-editor";
|
||||
import { ResizeHandle } from "../resize-handle/resize-handle";
|
||||
import './code-editor-container.less';
|
||||
|
||||
export class CodeEditorContainer extends BaseElement {
|
||||
onInit(): void {
|
||||
var language = this.getAttribute("language");
|
||||
this.classList.add("flex-vert");
|
||||
if (language) {
|
||||
this.setHeader(language);
|
||||
this.createCodeEditor(language, this.getAttribute("dir")?.toLowerCase().indexOf("v") === 0 ? 'vertical' : 'horizontal');
|
||||
}
|
||||
}
|
||||
|
||||
setHeader(title: string) {
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("code-editor-header");
|
||||
div.classList.add("flex-shrink");
|
||||
div.innerText = title;
|
||||
|
||||
this.appendChild(div);
|
||||
}
|
||||
|
||||
createCodeEditor(language: string, expandDir: 'vertical' | 'horizontal') {
|
||||
var codeEditor = <CodeEditor>document.createElement("code-editor");
|
||||
codeEditor.setAttribute("language", language);
|
||||
var resizeHandle = <ResizeHandle>document.createElement("resize-handle");
|
||||
resizeHandle.setAttribute("dir", expandDir);
|
||||
|
||||
this.appendChild(codeEditor);
|
||||
this.appendChild(resizeHandle);
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,29 @@ import { BaseElement } from "../../../shared/_base";
|
||||
import './code-editor.less';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import { OutputFrame } from "../output-frame/output-frame";
|
||||
import initScript from '!!raw-loader!./injects/editor-init.js';
|
||||
import initJs from '!!raw-loader!./injects/editor-init.js';
|
||||
import initHtml from '!!raw-loader!./injects/editor-init.html';
|
||||
import initCss from '!!raw-loader!./injects/editor-init.css';
|
||||
import spiralBoxesSolutionScript from '!!raw-loader!./injects/spiral-boxes-solution.js';
|
||||
import { debounceManager } from "../../../shared/ensure-debounce";
|
||||
import declarations from '!!raw-loader!./injects/declarations.d.ts';
|
||||
|
||||
export class CodeEditor extends BaseElement {
|
||||
|
||||
public input: string = initScript;
|
||||
public input: string = "";
|
||||
public language: string = "";
|
||||
|
||||
onInit(): void {
|
||||
|
||||
this.initWorkers();
|
||||
this.language = this.getAttribute("language")?.toLocaleLowerCase() ?? "javascript";
|
||||
this.input = this.setInitInput(this.language);
|
||||
|
||||
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
var editor = monaco.editor.create(this, {
|
||||
value: this.input,
|
||||
language: 'javascript',
|
||||
language: this.language,
|
||||
automaticLayout: true,
|
||||
contextmenu: false,
|
||||
minimap: {
|
||||
@@ -29,12 +34,45 @@ export class CodeEditor extends BaseElement {
|
||||
theme: prefersDarkScheme.matches ? 'vs-dark' : 'vs-light'
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
var debounce = debounceManager(1000);
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
this.input = editor.getValue();
|
||||
debounce.ensureDebounce(() => {
|
||||
this.outputFrame.setScript(this.language, this.input);
|
||||
});
|
||||
});
|
||||
|
||||
this.waitFor(this.outputFrame, () => {
|
||||
this.outputFrame.setScript(this.language, this.input);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
setInitInput(language: string | null) {
|
||||
if (language) {
|
||||
if (language.toLowerCase() === "javascript") {
|
||||
this.initJs();
|
||||
return initJs;
|
||||
}
|
||||
else if (language.toLowerCase() === 'html') {
|
||||
return initHtml;
|
||||
}
|
||||
else if (language.toLowerCase() === 'css') {
|
||||
return initCss;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
initJs() {
|
||||
monaco.languages.typescript.javascriptDefaults.addExtraLib(declarations, 'ts:filename/declarations.d.ts');
|
||||
|
||||
monaco.languages.registerCompletionItemProvider("javascript", {
|
||||
triggerCharacters: ["."],
|
||||
provideCompletionItems: (model, position, context, token) => (
|
||||
{
|
||||
provideCompletionItems: (model, position, context, token) => {
|
||||
return {
|
||||
suggestions: [
|
||||
{
|
||||
label: 'for',
|
||||
@@ -60,24 +98,11 @@ export class CodeEditor extends BaseElement {
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var debounce = debounceManager(1000);
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
this.input = editor.getValue();
|
||||
debounce.ensureDebounce(() => {
|
||||
this.outputFrame.setScript(this.input);
|
||||
});
|
||||
});
|
||||
|
||||
this.waitFor(this.outputFrame, () => {
|
||||
this.outputFrame.setScript(this.input);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
initWorkers() {
|
||||
// @ts-ignore
|
||||
self.MonacoEnvironment = {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
body {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<div>
|
||||
</div>
|
||||
@@ -1,4 +1,17 @@
|
||||
//create a box
|
||||
box(10, 10, 20, 20);
|
||||
|
||||
//use random:
|
||||
box(50, rand(100, 200), 20, 20);
|
||||
|
||||
//use random color:
|
||||
box(160, 50, 20, 20, randomColor());
|
||||
box(center(), center(), 20, 20);
|
||||
|
||||
//center method for center of canvas:
|
||||
box(center() - 10, center() - 10, 20, 20);
|
||||
|
||||
//width and height gives canvas size:
|
||||
box(width() - 20, height() - 20, 20, 20);
|
||||
|
||||
//rect will not be connected to boxes:
|
||||
rect(300, 200, 40, 40, "green");
|
||||
@@ -4,7 +4,7 @@ import { NotificationBubble } from "./notification-bubble";
|
||||
import './notification-bubbles.less';
|
||||
|
||||
export class NotificationBubbles extends BaseElement {
|
||||
add(text: string, type: 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'script' | 'time' | 'response') {
|
||||
add(text: string, type: 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'script' | 'time' | 'response' | string) {
|
||||
|
||||
if (text && type) {
|
||||
var bubble = <NotificationBubble>document.createElement("notification-bubble");
|
||||
|
||||
@@ -6,12 +6,14 @@ import { postMessage } from "../../../shared/post-message";
|
||||
|
||||
export class OutputFrame extends BaseElement {
|
||||
public Iframe: HTMLIFrameElement | null = null;
|
||||
public CurrentIframeScriptRemoveResponseListener: Function | null = null;
|
||||
private CurrentIframeScriptRemoveResponseListener: Function | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
private scripts: any = {};
|
||||
|
||||
onInit(): void {
|
||||
this.reset();
|
||||
}
|
||||
@@ -19,7 +21,7 @@ export class OutputFrame extends BaseElement {
|
||||
onUpdate(): void {
|
||||
}
|
||||
|
||||
reset(resetNotificationBubbles: boolean = true) {
|
||||
reset(onload: ((ev: Event) => void) | null = null, resetNotificationBubbles: boolean = true) {
|
||||
if (this.CurrentIframeScriptRemoveResponseListener != null) {
|
||||
this.CurrentIframeScriptRemoveResponseListener();
|
||||
this.CurrentIframeScriptRemoveResponseListener = null;
|
||||
@@ -32,6 +34,13 @@ export class OutputFrame extends BaseElement {
|
||||
this.Iframe = document.createElement("iframe");
|
||||
this.Iframe.setAttribute("sandbox", "allow-pointer-lock allow-same-origin allow-scripts");
|
||||
this.Iframe.src = GetOutputFrameUrl();
|
||||
|
||||
if (onload) {
|
||||
this.Iframe.onload = ((ev) => {
|
||||
onload(ev);
|
||||
});
|
||||
}
|
||||
|
||||
this.appendChild(this.Iframe);
|
||||
|
||||
if (resetNotificationBubbles) {
|
||||
@@ -43,41 +52,34 @@ export class OutputFrame extends BaseElement {
|
||||
}
|
||||
|
||||
setError() {
|
||||
this.reset();
|
||||
if (this.Iframe) {
|
||||
this.Iframe.src = GetOutputFrameUrl() + "execution-time-error.html";
|
||||
}
|
||||
}
|
||||
|
||||
preventInfiniteLoop(value: string) {
|
||||
return value.replace("while (true)", "while (false)");
|
||||
}
|
||||
|
||||
setScript(value: string) {
|
||||
this.reset();
|
||||
this.onIframeLoaded(() => {
|
||||
this.CurrentIframeScriptRemoveResponseListener = postMessage(this.Iframe?.contentWindow, "script", value, (executeTimeInMs, exceedTimeInMs) => {
|
||||
if (executeTimeInMs === -1) {
|
||||
this.setError();
|
||||
}
|
||||
else {
|
||||
var bubbles = GetNotificationBubbles();
|
||||
bubbles.add("code executed in: " + executeTimeInMs + "ms", "info");
|
||||
}
|
||||
});
|
||||
this.reset(() => {
|
||||
if (this.Iframe)
|
||||
this.Iframe.src = GetOutputFrameUrl() + "execution-time-error.html";
|
||||
});
|
||||
}
|
||||
|
||||
onIframeLoaded(fn: Function) {
|
||||
if (this.Iframe) {
|
||||
this.Iframe.onload = () => {
|
||||
fn();
|
||||
setScript(language: string, value: string) {
|
||||
this.scripts[language] = value;
|
||||
this.reset(() => {
|
||||
for (const lang in this.scripts) {
|
||||
if (lang != "javascript")
|
||||
this.doPostMessage(lang, this.scripts[lang]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.waitFor(this.Iframe, () => {
|
||||
this.onIframeLoaded(fn);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.scripts["javascript"])
|
||||
this.doPostMessage("javascript", this.scripts["javascript"]);
|
||||
});
|
||||
}
|
||||
|
||||
doPostMessage(language: string, value: string) {
|
||||
this.CurrentIframeScriptRemoveResponseListener = postMessage(this.Iframe?.contentWindow, language, this.scripts[language], language == 'javascript' ? (executeTimeInMs, exceedTimeInMs) => {
|
||||
if (executeTimeInMs === -1) {
|
||||
this.setError();
|
||||
}
|
||||
else {
|
||||
var bubbles = GetNotificationBubbles();
|
||||
bubbles.add(language + " executed in: " + executeTimeInMs + "ms", "info");
|
||||
}
|
||||
} : undefined);
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,42 @@
|
||||
|
||||
resize-handle {
|
||||
background: transparent;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 8px;
|
||||
position: absolute !important;
|
||||
z-index: 9;
|
||||
cursor: ew-resize;
|
||||
transition: 1s ease-in;
|
||||
flex: none !important;
|
||||
}
|
||||
|
||||
resize-handle:hover {
|
||||
resize-handle[dir="horizontal"] {
|
||||
cursor: ew-resize;
|
||||
width: 8px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
resize-handle[dir="vertical"] {
|
||||
cursor: ns-resize;
|
||||
height: 8px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
resize-handle:hover, resize-handle.active {
|
||||
background: @primary-interaction-highlight-white;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
#fixed-resize-overlay {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
resize-handle:hover {
|
||||
|
||||
@@ -4,7 +4,6 @@ import './resize-handle.less';
|
||||
|
||||
export class ResizeHandle extends BaseElement {
|
||||
|
||||
public isDowned = false;
|
||||
|
||||
onInit(): void {
|
||||
this.setDragEvents();
|
||||
@@ -12,54 +11,120 @@ export class ResizeHandle extends BaseElement {
|
||||
|
||||
setDragEvents() {
|
||||
this.addEventListener("mousedown", (evt: MouseEvent) => {
|
||||
this.isDowned = true;
|
||||
this.getIframe().style.display = "none";
|
||||
});
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.setOverlay({
|
||||
x: evt.pageX,
|
||||
y: evt.pageY
|
||||
});
|
||||
|
||||
window.addEventListener("mousemove", (evt: MouseEvent) => {
|
||||
if (this.isDowned) {
|
||||
this.setPosition(evt.pageX);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("mouseup", (evt: MouseEvent) => {
|
||||
if (this.isDowned) {
|
||||
this.getIframe().style.display = "block";
|
||||
this.isDowned = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.addEventListener("touchstart", (evt: TouchEvent) => {
|
||||
this.isDowned = true;
|
||||
this.getIframe().style.display = "none";
|
||||
});
|
||||
|
||||
window.addEventListener("touchmove", (evt: TouchEvent) => {
|
||||
if (this.isDowned) {
|
||||
this.setPosition(evt.touches[0].pageX);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("touchend", (evt: TouchEvent) => {
|
||||
if (this.isDowned) {
|
||||
this.getIframe().style.display = "block";
|
||||
this.isDowned = false;
|
||||
}
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.setOverlay({
|
||||
x: evt.touches[0].pageX,
|
||||
y: evt.touches[0].pageY
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setPosition(x: number) {
|
||||
setSize(x: number, y: number) {
|
||||
var t = this.getTarget();
|
||||
|
||||
t.style.minWidth = x + "px";
|
||||
t.style.maxWidth = x + "px";
|
||||
if (t) {
|
||||
if (this.directionIsHorizontal()) {
|
||||
t.style.minWidth = x + "px";
|
||||
t.style.maxWidth = x + "px";
|
||||
}
|
||||
else {
|
||||
t.style.minHeight = y + "px";
|
||||
t.style.maxHeight = y + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTarget() {
|
||||
return <CodeEditor>this.find("code-editor");
|
||||
var targetAttr = this.getAttribute("target");
|
||||
if (targetAttr) {
|
||||
var target = <HTMLElement>document.querySelector(targetAttr);
|
||||
|
||||
if (target) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return this.parentElement;
|
||||
}
|
||||
|
||||
directionIsHorizontal() {
|
||||
var attr = this.getAttribute("dir");
|
||||
|
||||
if (attr) {
|
||||
if (attr.toLowerCase().indexOf('v') === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getIframe() {
|
||||
return <HTMLIFrameElement>this.find("iframe");
|
||||
}
|
||||
|
||||
setOverlay(start: { x: number, y: number }) {
|
||||
var overlay = document.createElement("div");
|
||||
overlay.setAttribute("id", "fixed-resize-overlay");
|
||||
|
||||
var targetStartSize = { width: 0, height: 0 };
|
||||
|
||||
var target = this.getTarget();
|
||||
if (target) {
|
||||
var bounds = target.getBoundingClientRect();
|
||||
|
||||
targetStartSize = {
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
};
|
||||
}
|
||||
|
||||
overlay.addEventListener("mousemove", (evt: MouseEvent) => {
|
||||
this.getIframe().style.pointerEvents = "none";
|
||||
this.classList.add("active");
|
||||
this.setSize(targetStartSize.width + (evt.pageX - start.x), targetStartSize.height + (evt.pageY - start.y));
|
||||
});
|
||||
|
||||
overlay.addEventListener("mouseup", (evt: MouseEvent) => {
|
||||
this.getIframe().style.pointerEvents = "all";
|
||||
this.classList.remove("active");
|
||||
this.removeOverlay();
|
||||
});
|
||||
|
||||
overlay.addEventListener("touchmove", (evt: TouchEvent) => {
|
||||
this.getIframe().style.pointerEvents = "none";
|
||||
this.classList.add("active");
|
||||
this.setSize(targetStartSize.width + (start.x - evt.touches[0].pageX), targetStartSize.height + (start.y - evt.touches[0].pageY));
|
||||
});
|
||||
|
||||
overlay.addEventListener("touchend", (evt: TouchEvent) => {
|
||||
this.getIframe().style.pointerEvents = "all";
|
||||
this.classList.remove("active");
|
||||
this.removeOverlay();
|
||||
});
|
||||
|
||||
document.body.classList.remove("animate");
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
removeOverlay() {
|
||||
var overlay = document.getElementById("fixed-resize-overlay");
|
||||
|
||||
if (overlay) {
|
||||
overlay.parentElement?.removeChild(overlay);
|
||||
}
|
||||
|
||||
document.body.classList.add("animate");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { BaseElement } from "../../../shared/_base";
|
||||
import { CodeEditorContainer } from "../code-editor-container/code-editor-container";
|
||||
import './window-control-connector.less';
|
||||
|
||||
export class WindowControlConnector extends BaseElement {
|
||||
|
||||
onInit(): void {
|
||||
this.addEventListener("click", () => {
|
||||
this.toggle();
|
||||
})
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this.classList.contains("active")) {
|
||||
this.deactivate();
|
||||
}
|
||||
else {
|
||||
this.activate();
|
||||
}
|
||||
}
|
||||
|
||||
activate() {
|
||||
this.classList.add("active");
|
||||
var target = this.getTarget();
|
||||
|
||||
if (target) {
|
||||
target.classList.add("active");
|
||||
target.style.display = "block";
|
||||
}
|
||||
|
||||
this.distributeEvenly();
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.classList.remove("active");
|
||||
var target = this.getTarget();
|
||||
|
||||
if (target) {
|
||||
target.classList.remove("active");
|
||||
target.style.display = "none";
|
||||
}
|
||||
|
||||
this.distributeEvenly();
|
||||
}
|
||||
|
||||
private distributeEvenly() {
|
||||
var elements = <CodeEditorContainer[]>this.findVisible("code-editor-container");
|
||||
var containers = document.getElementsByClassName("code-editor-containers")[0];
|
||||
|
||||
if (elements.length === 0) {
|
||||
containers.classList.add("force-close");
|
||||
}
|
||||
else {
|
||||
containers.classList.remove("force-close");
|
||||
var size = (100 / elements.length) + "%";
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i];
|
||||
|
||||
element.style.minHeight = size;
|
||||
element.style.maxHeight = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getTarget() {
|
||||
var attr = this.getAttribute("target");
|
||||
if (attr) {
|
||||
return <HTMLElement>document.querySelector(attr);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
@import url('../../../shared/theme.less');
|
||||
|
||||
window-control {
|
||||
display: inline-block;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
window-control-connector {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
background: lighten(@editor-bg-white, 1%);
|
||||
padding: 6px;
|
||||
padding-right: 16px;
|
||||
transition: all 200ms;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
window-control-connector::after {
|
||||
position: absolute;
|
||||
content: "❌";
|
||||
font-size: 8px;
|
||||
transform: rotate(45deg);
|
||||
filter: grayscale();
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
transition: all 200ms;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
window-control-connector.active {
|
||||
transform: translateY(7px);
|
||||
}
|
||||
|
||||
window-control-connector.active::after {
|
||||
transform: rotate(0);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
window-control-connector {
|
||||
background: lighten(@editor-bg-dark, 1%);
|
||||
color: @primary-text-color-dark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BaseElement } from "../../../shared/_base";
|
||||
import { WindowControlConnector } from "./window-control connector";
|
||||
|
||||
export class WindowControl extends BaseElement {
|
||||
|
||||
setActive(element: WindowControlConnector) {
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
const child = <WindowControlConnector>this.children[i];
|
||||
|
||||
if (element == child) {
|
||||
child.activate();
|
||||
}
|
||||
else {
|
||||
child.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<base href="/" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="animate">
|
||||
<script type="text/javascript" src="/main.bundle.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user