add line numbers to code editor

this is the sort of fancy stuff we can do now
This commit is contained in:
liquidex 2024-09-05 22:05:15 +02:00
parent 09a81ec032
commit 92e3b8fcb7
3 changed files with 95 additions and 10 deletions

View file

@ -11,6 +11,9 @@
--color-shaded-background: rgba(0, 0, 0, 5%); --color-shaded-background: rgba(0, 0, 0, 5%);
--dialog-backdrop: rgba(255, 255, 255, 0.5); --dialog-backdrop: rgba(255, 255, 255, 0.5);
--font-body: "Atkinson Hyperlegible", sans-serif;
--font-monospace: "Iosevka Hyperlegible", monospace;
} }
/* Reset */ /* Reset */
@ -24,7 +27,7 @@ body {
/* Fonts */ /* Fonts */
:root { :root {
font-family: "Atkinson Hyperlegible", sans-serif; font-family: var(--font-body);
} }
button, textarea, input { button, textarea, input {
@ -33,7 +36,7 @@ button, textarea, input {
} }
pre, code, textarea { pre, code, textarea {
font-family: "Iosevka Hyperlegible", monospace; font-family: var(--font-monospace);
line-height: 1.5; line-height: 1.5;
} }

View file

@ -6,6 +6,9 @@ export class CodeEditor extends HTMLElement {
connectedCallback() { connectedCallback() {
this.indentWidth = 2; this.indentWidth = 2;
this.layerGutter = this.appendChild(document.createElement("pre"));
this.layerGutter.classList.add("layer", "layer-gutter");
this.textArea = this.appendChild(document.createElement("textarea")); this.textArea = this.appendChild(document.createElement("textarea"));
this.textArea.spellcheck = false; this.textArea.spellcheck = false;
this.textArea.rows = 1; this.textArea.rows = 1;
@ -29,13 +32,17 @@ export class CodeEditor extends HTMLElement {
this.#textAreaAutoSizingBehaviour(); this.#textAreaAutoSizingBehaviour();
this.#keyShortcutBehaviours(); this.#keyShortcutBehaviours();
this.#renderLayers();
} }
get code() { get code() {
return this.textArea.value; return this.textArea.value;
} }
#sendCodeChanged() { #codeChanged() {
this.#resizeTextArea();
this.#renderLayers();
this.dispatchEvent( this.dispatchEvent(
Object.assign(new Event(".codeChanged"), { Object.assign(new Event(".codeChanged"), {
newCode: this.code, newCode: this.code,
@ -45,14 +52,14 @@ export class CodeEditor extends HTMLElement {
setCode(value) { setCode(value) {
this.textArea.value = value; this.textArea.value = value;
this.#resizeTextArea(); this.#codeChanged();
this.#sendCodeChanged();
} }
// Resizing the text area
#textAreaAutoSizingBehaviour() { #textAreaAutoSizingBehaviour() {
this.textArea.addEventListener("input", () => { this.textArea.addEventListener("input", () => {
this.#resizeTextArea(); this.#codeChanged();
this.#sendCodeChanged();
}); });
this.#resizeTextArea(); this.#resizeTextArea();
document.fonts.addEventListener("loadingdone", () => this.#resizeTextArea()); document.fonts.addEventListener("loadingdone", () => this.#resizeTextArea());
@ -84,6 +91,24 @@ export class CodeEditor extends HTMLElement {
this.textArea.style.height = `${this.textArea.scrollHeight}px`; this.textArea.style.height = `${this.textArea.scrollHeight}px`;
} }
// Layers
#renderLayers() {
this.#renderGutter();
}
#renderGutter() {
this.layerGutter.replaceChildren();
for (let line of this.code.split("\n")) {
let lineElement = this.layerGutter.appendChild(document.createElement("span"));
lineElement.classList.add("line");
lineElement.textContent = line;
}
}
// Text editing
#keyShortcutBehaviours() { #keyShortcutBehaviours() {
this.textArea.addEventListener("keydown", (event) => { this.textArea.addEventListener("keydown", (event) => {
let keyComponents = []; let keyComponents = [];

View file

@ -169,15 +169,72 @@ rkgk-reticle-cursor {
/* Code editor */ /* Code editor */
rkgk-code-editor { rkgk-code-editor {
--gutter-width: 2.5em;
display: block;
position: relative;
width: 100%;
&>.layer {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
margin: 0;
pointer-events: none;
display: flex;
flex-direction: column;
&>.line {
white-space: pre-wrap;
}
}
&>.layer-gutter {
user-select: none;
counter-reset: line;
color: transparent;
&>.line {
display: flex;
flex-direction: row;
counter-increment: line;
&::before {
display: block;
width: var(--gutter-width);
flex-shrink: 0;
padding-right: 0.5em;
box-sizing: border-box;
content: counter(line);
text-align: right;
color: var(--color-text);
opacity: 40%;
}
}
}
&>textarea { &>textarea {
display: block; display: block;
width: 100%; width: calc(100% - var(--gutter-width));
margin: 0; margin: 0;
margin-left: var(--gutter-width);
padding: 0;
box-sizing: border-box;
overflow: hidden;
resize: none; resize: none;
white-space: pre-wrap; white-space: pre-wrap;
border: none; border: none;
overflow: hidden;
box-sizing: border-box;
} }
} }