code-editor
Changes
codeeditor-app/app.html 20(+17 -3)
codeeditor-app/app.less 31(+30 -1)
codeeditor-app/boot.ts 6(+6 -0)
codeeditor-app/static/index.html 2(+1 -1)
outputframe-app/boot.ts 10(+8 -2)
shared/_base.ts 16(+16 -0)
shared/create-script.ts 14(+14 -0)
shared/message-listener.ts 2(+1 -1)
Details
codeeditor-app/app.html 20(+17 -3)
diff --git a/codeeditor-app/app.html b/codeeditor-app/app.html
index e022ccc..0febe63 100644
--- a/codeeditor-app/app.html
+++ b/codeeditor-app/app.html
@@ -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>
codeeditor-app/app.less 31(+30 -1)
diff --git a/codeeditor-app/app.less b/codeeditor-app/app.less
index af2d476..99cc228 100644
--- a/codeeditor-app/app.less
+++ b/codeeditor-app/app.less
@@ -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;
}
-}
\ No newline at end of file
+
+ .code-editor-containers {
+ background: @editor-bg-dark;
+ }
+}
codeeditor-app/boot.ts 6(+6 -0)
diff --git a/codeeditor-app/boot.ts b/codeeditor-app/boot.ts
index 2d4578c..0e4eb2b 100644
--- a/codeeditor-app/boot.ts
+++ b/codeeditor-app/boot.ts
@@ -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;
\ No newline at end of file
diff --git a/codeeditor-app/elements/code-editor/code-editor.ts b/codeeditor-app/elements/code-editor/code-editor.ts
index 0dbffc3..25f231b 100644
--- a/codeeditor-app/elements/code-editor/code-editor.ts
+++ b/codeeditor-app/elements/code-editor/code-editor.ts
@@ -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 = {
diff --git a/codeeditor-app/elements/code-editor/injects/editor-init.css b/codeeditor-app/elements/code-editor/injects/editor-init.css
new file mode 100644
index 0000000..47e1dc8
--- /dev/null
+++ b/codeeditor-app/elements/code-editor/injects/editor-init.css
@@ -0,0 +1,2 @@
+body {
+}
\ No newline at end of file
diff --git a/codeeditor-app/elements/code-editor/injects/editor-init.html b/codeeditor-app/elements/code-editor/injects/editor-init.html
new file mode 100644
index 0000000..094418b
--- /dev/null
+++ b/codeeditor-app/elements/code-editor/injects/editor-init.html
@@ -0,0 +1,2 @@
+<div>
+</div>
\ No newline at end of file
diff --git a/codeeditor-app/elements/code-editor/injects/editor-init.js b/codeeditor-app/elements/code-editor/injects/editor-init.js
index 2e07dda..a74da39 100644
--- a/codeeditor-app/elements/code-editor/injects/editor-init.js
+++ b/codeeditor-app/elements/code-editor/injects/editor-init.js
@@ -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);
\ No newline at end of file
+
+//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");
\ No newline at end of file
diff --git a/codeeditor-app/elements/code-editor-container/code-editor-container.less b/codeeditor-app/elements/code-editor-container/code-editor-container.less
new file mode 100644
index 0000000..0b1e01b
--- /dev/null
+++ b/codeeditor-app/elements/code-editor-container/code-editor-container.less
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/codeeditor-app/elements/code-editor-container/code-editor-container.ts b/codeeditor-app/elements/code-editor-container/code-editor-container.ts
new file mode 100644
index 0000000..2796983
--- /dev/null
+++ b/codeeditor-app/elements/code-editor-container/code-editor-container.ts
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts b/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts
index dc6a479..d886910 100644
--- a/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts
+++ b/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts
@@ -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");
diff --git a/codeeditor-app/elements/output-frame/output-frame.ts b/codeeditor-app/elements/output-frame/output-frame.ts
index 6580019..ff2215e 100644
--- a/codeeditor-app/elements/output-frame/output-frame.ts
+++ b/codeeditor-app/elements/output-frame/output-frame.ts
@@ -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";
- }
+ this.reset(() => {
+ if (this.Iframe)
+ this.Iframe.src = GetOutputFrameUrl() + "execution-time-error.html";
+ });
}
- preventInfiniteLoop(value: string) {
- return value.replace("while (true)", "while (false)");
- }
+ 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]);
+ }
- 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");
- }
- });
+ if (this.scripts["javascript"])
+ this.doPostMessage("javascript", this.scripts["javascript"]);
});
}
- onIframeLoaded(fn: Function) {
- if (this.Iframe) {
- this.Iframe.onload = () => {
- fn();
+ 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 {
- this.waitFor(this.Iframe, () => {
- this.onIframeLoaded(fn);
- });
- }
+ else {
+ var bubbles = GetNotificationBubbles();
+ bubbles.add(language + " executed in: " + executeTimeInMs + "ms", "info");
+ }
+ } : undefined);
}
}
\ No newline at end of file
diff --git a/codeeditor-app/elements/resize-handle/resize-handle.less b/codeeditor-app/elements/resize-handle/resize-handle.less
index 650c2ed..361f49f 100644
--- a/codeeditor-app/elements/resize-handle/resize-handle.less
+++ b/codeeditor-app/elements/resize-handle/resize-handle.less
@@ -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[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: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 {
diff --git a/codeeditor-app/elements/resize-handle/resize-handle.ts b/codeeditor-app/elements/resize-handle/resize-handle.ts
index 65ddf91..89f7a3b 100644
--- a/codeeditor-app/elements/resize-handle/resize-handle.ts
+++ b/codeeditor-app/elements/resize-handle/resize-handle.ts
@@ -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";
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.setOverlay({
+ x: evt.touches[0].pageX,
+ y: evt.touches[0].pageY
+ });
});
+ }
- window.addEventListener("touchmove", (evt: TouchEvent) => {
- if (this.isDowned) {
- this.setPosition(evt.touches[0].pageX);
- }
- });
+ setSize(x: number, y: number) {
+ var t = this.getTarget();
- window.addEventListener("touchend", (evt: TouchEvent) => {
- if (this.isDowned) {
- this.getIframe().style.display = "block";
- this.isDowned = false;
+ 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";
+ }
+ }
}
- setPosition(x: number) {
- var t = this.getTarget();
+ getTarget() {
+ var targetAttr = this.getAttribute("target");
+ if (targetAttr) {
+ var target = <HTMLElement>document.querySelector(targetAttr);
- t.style.minWidth = x + "px";
- t.style.maxWidth = x + "px";
+ if (target) {
+ return target;
+ }
+ }
+
+ return this.parentElement;
}
- getTarget() {
- return <CodeEditor>this.find("code-editor");
+ 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");
+ }
}
\ No newline at end of file
diff --git a/codeeditor-app/elements/window-control/window-control connector.ts b/codeeditor-app/elements/window-control/window-control connector.ts
new file mode 100644
index 0000000..e846390
--- /dev/null
+++ b/codeeditor-app/elements/window-control/window-control connector.ts
@@ -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;
+ }
+
+}
\ No newline at end of file
diff --git a/codeeditor-app/elements/window-control/window-control.ts b/codeeditor-app/elements/window-control/window-control.ts
new file mode 100644
index 0000000..f1474d9
--- /dev/null
+++ b/codeeditor-app/elements/window-control/window-control.ts
@@ -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();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/codeeditor-app/elements/window-control/window-control-connector.less b/codeeditor-app/elements/window-control/window-control-connector.less
new file mode 100644
index 0000000..bde357a
--- /dev/null
+++ b/codeeditor-app/elements/window-control/window-control-connector.less
@@ -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;
+ }
+}
\ No newline at end of file
codeeditor-app/static/index.html 2(+1 -1)
diff --git a/codeeditor-app/static/index.html b/codeeditor-app/static/index.html
index 19e3113..722a5dd 100644
--- a/codeeditor-app/static/index.html
+++ b/codeeditor-app/static/index.html
@@ -12,7 +12,7 @@
<base href="/" />
</head>
-<body>
+<body class="animate">
<script type="text/javascript" src="/main.bundle.js?v=1"></script>
</body>
outputframe-app/boot.ts 10(+8 -2)
diff --git a/outputframe-app/boot.ts b/outputframe-app/boot.ts
index d5fc47a..d5cb429 100644
--- a/outputframe-app/boot.ts
+++ b/outputframe-app/boot.ts
@@ -2,7 +2,7 @@ import './app.less';
import bootHtml from './app.html';
import { watchConsole } from './injects/watch-console';
import { setOnErrorListener } from './injects/on-error';
-import { createScript } from '../shared/create-script';
+import { createCss, createHtml, createScript } from '../shared/create-script';
import { messageListener } from '../shared/message-listener';
import { postMessage, postResponseMessage } from '../shared/post-message';
import { RenderCanvas } from './elements/render-canvas/render-canvas';
@@ -17,10 +17,16 @@ watchConsole((type, value) => {
postMessage(parent, type, value);
});
messageListener((type, value, id) => {
- if (type == "script") {
+ if (type == "javascript") {
createScript(value);
postResponseMessage(parent, id, value);
}
+ else if (type == "html") {
+ createHtml(value);
+ }
+ else if (type == "css") {
+ createCss(value);
+ }
});
setOnErrorListener((type, value) => {
postMessage(parent, type, value);
diff --git a/outputframe-app/elements/render-canvas/render-canvas.ts b/outputframe-app/elements/render-canvas/render-canvas.ts
index ea062bf..77e94a3 100644
--- a/outputframe-app/elements/render-canvas/render-canvas.ts
+++ b/outputframe-app/elements/render-canvas/render-canvas.ts
@@ -11,10 +11,10 @@ export class RenderCanvas extends BaseElement {
width: 400,
height: 400
}
- public drawCount: number = 0;
- public drawQueue: { points: { x: number, y: number }[], color?: string }[] = [];
+ private drawCount: number = 0;
+ private drawQueue: { points: { x: number, y: number }[], color?: string, connected?: boolean }[] = [];
- public prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
+ private prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
private theme: any;
@@ -44,10 +44,12 @@ export class RenderCanvas extends BaseElement {
this.canvas.style.height = smallest + "px";
this.canvas.setAttribute("width", smallest.toString());
this.canvas.setAttribute("height", smallest.toString());
+
+ this.draw();
}
}
- drawPoly(points: { x: number, y: number }[], color?: string | CanvasGradient) {
+ private drawPoly(points: { x: number, y: number }[], color?: string | CanvasGradient) {
if (this.ctx && this.canvas) {
this.canvas.style.display = "block";
if (color instanceof CanvasGradient) {
@@ -77,7 +79,7 @@ export class RenderCanvas extends BaseElement {
}
}
- centerOf(points: { x: number, y: number }[]) {
+ private centerOf(points: { x: number, y: number }[]) {
var x = 0,
y = 0,
i,
@@ -99,7 +101,7 @@ export class RenderCanvas extends BaseElement {
return { x: x / f, y: y / f };
};
- areaOf(points: { x: number, y: number }[]) {
+ private areaOf(points: { x: number, y: number }[]) {
var area = 0,
i,
j,
@@ -117,50 +119,42 @@ export class RenderCanvas extends BaseElement {
return area;
};
- angleBetweenPoints(point1: { x: number, y: number }, point2: { x: number, y: number }) {
- // angle in radians
- var angleRadians = Math.atan2(point2.y - point1.y, point2.x - point1.x);
-
- // angle in degrees
+ private angleBetweenPoints(point1: { x: number, y: number }, point2: { x: number, y: number }) {
return Math.atan2(point2.y - point1.y, point2.x - point1.x) * 180 / Math.PI;
}
- executeDrawQueue() {
- let prevShape: any | null = null;
+ draw() {
+ let prevConnectedShape: any | null = null;
for (let i = 0; i < this.drawQueue.length; i++) {
const shape = this.drawQueue[i];
- if (prevShape != null && this.ctx) {
- var prevCenter = this.centerOf(prevShape.points);
+ if (shape.connected && prevConnectedShape != null && this.ctx) {
+ var prevCenter = this.centerOf(prevConnectedShape.points);
var thisCenter = this.centerOf(shape.points);
- var middle = {
- x: (prevCenter.x + thisCenter.x) / 2,
- y: (prevCenter.y + thisCenter.y) / 2
- };
-
var grd = this.ctx.createLinearGradient(this.unitsToPx(prevCenter.x), this.unitsToPx(prevCenter.y), this.unitsToPx(thisCenter.x), this.unitsToPx(thisCenter.y));
- grd.addColorStop(0, prevShape?.color ?? this.getColor());
+ grd.addColorStop(0, prevConnectedShape?.color ?? this.getColor());
grd.addColorStop(1, shape?.color ?? this.getColor());
this.drawPoly([prevCenter, thisCenter], grd);
-
- //this.drawPoly([prevCenter, middle], prevShape.color);
- //this.drawPoly([middle, thisCenter], shape.color);
}
- prevShape = shape;
+ if (shape.connected) {
+ prevConnectedShape = shape;
+ }
}
for (let i = 0; i < this.drawQueue.length; i++) {
const shape = this.drawQueue[i];
this.drawPoly(shape.points, shape.color);
var thisCenter = this.centerOf(shape.points);
- this.drawText(thisCenter.x, thisCenter.y, (i + 1).toString(), this.unitsToPx((shape.points[1].x - shape.points[0].x) * 0.6), shape.color);
+ if (shape.connected) {
+ this.drawText(thisCenter.x, thisCenter.y, (i + 1).toString(), this.unitsToPx((shape.points[1].x - shape.points[0].x) * 0.6), shape.color);
+ }
}
}
- drawText(x: number, y: number, text: string, size: number = 20, color?: string) {
+ private drawText(x: number, y: number, text: string, size: number = 20, color?: string) {
if (this.ctx && this.canvas) {
this.ctx.moveTo(x, y);
this.ctx.fillStyle = this.getColor(color);
@@ -171,27 +165,23 @@ export class RenderCanvas extends BaseElement {
}
}
- getColor(color?: string) {
+ private getColor(color?: string) {
return color ? color : (this.prefersDarkScheme.matches ? this.theme["@draw-color-dark"] : this.theme["@draw-color-white"]);
}
- getBackgroundColor() {
+ private getBackgroundColor() {
return this.prefersDarkScheme.matches ? this.theme["@median-bg-dark"] : this.theme["@median-bg-white"];
}
- drawRect(x: number, y: number, width: number, height: number, color?: string) {
- this.drawPoly([{ x: x, y: y }, { x: x + width, y: y }, { x: x + width, y: y + height }, { x: x, y: y + height }], color);
- }
-
- queuePoly(points: { x: number, y: number }[], color?: string) {
+ poly(points: { x: number, y: number }[], color?: string) {
this.drawQueue.push({ points: points, color: color });
}
- queueRect(x: number, y: number, width: number, height: number, color?: string) {
- this.drawQueue.push({ points: [{ x: x, y: y }, { x: x + width, y: y }, { x: x + width, y: y + height }, { x: x, y: y + height }], color: color });
+ rect(x: number, y: number, width: number, height: number, color?: string, connected?: boolean) {
+ this.drawQueue.push({ points: [{ x: x, y: y }, { x: x + width, y: y }, { x: x + width, y: y + height }, { x: x, y: y + height }], color: color, connected: connected });
}
- unitsToPx(unitNumber: number) {
+ private unitsToPx(unitNumber: number) {
var newPPU = Math.min(this.width, this.height) / this.units.width;
return Math.floor(unitNumber * newPPU);
}
diff --git a/outputframe-app/injects/dom-helpers.ts b/outputframe-app/injects/dom-helpers.ts
index b94bf72..f1b1b07 100644
--- a/outputframe-app/injects/dom-helpers.ts
+++ b/outputframe-app/injects/dom-helpers.ts
@@ -6,12 +6,12 @@ export function clear() {
export function rect(x: number, y: number, width: number, height: number, color?: string): void {
var renderCanvas = <RenderCanvas>document.getElementsByTagName("render-canvas")[0];
- renderCanvas.drawRect(x, y, width, height, color);
+ renderCanvas.rect(x, y, width, height, color, false);
}
export function box(x: number, y: number, width: number, height: number, color?: string): void {
var renderCanvas = <RenderCanvas>document.getElementsByTagName("render-canvas")[0];
- renderCanvas.queueRect(x, y, width, height, color);
+ renderCanvas.rect(x, y, width, height, color, true);
}
export function getWidth(): number {
@@ -35,7 +35,7 @@ export function getRandom(min: number, max: number): number {
export function executeDrawQueue(): void {
var renderCanvas = <RenderCanvas>document.getElementsByTagName("render-canvas")[0];
- renderCanvas.executeDrawQueue();
+ renderCanvas.draw();
}
export function getRandomColor(): string {
shared/_base.ts 16(+16 -0)
diff --git a/shared/_base.ts b/shared/_base.ts
index c90c3e5..82bb140 100644
--- a/shared/_base.ts
+++ b/shared/_base.ts
@@ -67,6 +67,22 @@ export class BaseElement extends HTMLElement {
return null;
}
+
+ findVisible(tagName: string) {
+ var result = [];
+
+ var elements = document.getElementsByTagName(tagName);
+
+ for (let i = 0; i < elements.length; i++) {
+ const element = <HTMLElement>elements[i];
+
+ if (element.style.display !== "none") {
+ result.push(element);
+ }
+ }
+
+ return result;
+ }
hasChild(element: Element, startsWidth: boolean = false): boolean {
var ele = this;
shared/create-script.ts 14(+14 -0)
diff --git a/shared/create-script.ts b/shared/create-script.ts
index a75b403..2119c3f 100644
--- a/shared/create-script.ts
+++ b/shared/create-script.ts
@@ -8,4 +8,18 @@ export function createScript(value: string) {
document.body.appendChild(script);
executeDrawQueue();
+}
+
+export function createHtml(value: string) {
+ var div: HTMLDivElement = document.createElement("div");
+ div.innerHTML = value;
+
+ document.body.appendChild(div);
+}
+
+export function createCss(value: string) {
+ var style: HTMLStyleElement = document.createElement("style");
+ style.innerHTML = value;
+
+ document.body.appendChild(style);
}
\ No newline at end of file
shared/message-listener.ts 2(+1 -1)
diff --git a/shared/message-listener.ts b/shared/message-listener.ts
index 75c3a29..58c0f72 100644
--- a/shared/message-listener.ts
+++ b/shared/message-listener.ts
@@ -1,4 +1,4 @@
-export function messageListener(fn: (type: 'script' | 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'time' | 'response', value: string, id: number) => void): Function {
+export function messageListener(fn: (type: 'script' | 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'time' | 'response' | string, value: string, id: number) => void): Function {
var evt = function(e: any) {
if (!e.data || e.data === '' || e.data.type === 'webpackOk') {