neater tabs
continuous-integration/drone Build is passing Details

This commit is contained in:
Hamcha 2023-12-03 17:57:12 +01:00
parent 87f198ae36
commit 6e24ffb499
3 changed files with 81 additions and 32 deletions

2
.swcrc
View File

@ -7,6 +7,6 @@
"decorators": false,
"dynamicImport": false
},
"target": "esnext"
"target": "es2022"
}
}

View File

@ -1,21 +1,77 @@
import "/static/vendor/ace/ace.js";
import { $el } from "/static/vendor/domutil/domutil.js";
class EditorTab extends HTMLElement {
static observedAttributes = ["name", "selected"];
private tabEl: HTMLDivElement;
private buttonsEl: HTMLDivElement;
private root: ShadowRoot;
constructor() {
super();
this.root = this.attachShadow({ mode: "open" });
const style = $el("style");
style.textContent = `
:host {
border: 2px solid var(--table-border-color);
padding: 0.5ch 0.8ch;
background-color: var(--button-bg);
cursor: pointer;
}
:host([selected="true"]) {
background-color: var(--table-border-color);
}
:host([modified="true"]) .tab-name::after {
content: " ᴹ";
}
:host([unsaved="true"]) {
font-style: italic;
}
`;
this.root.append(style);
this.tabEl = $el("div", { className: "tab-name" }, this.getAttribute("name") || $el("i", "unknown"));
this.buttonsEl = $el("div", { className: "buttons" });
this.root.append(this.tabEl, this.buttonsEl);
}
connectedCallback() {
}
attributeChangedCallback(name, _, value) {
switch (name) {
case "name":
this.tabEl.textContent = value;
break;
case "selected":
this.classList.toggle("active", Boolean(value));
break;
}
}
}
customElements.define("file-editor-tab", EditorTab);
class Editor extends HTMLElement {
private editor: AceAjax.Editor;
private tabEl: HTMLDivElement;
private sessions: Record<string, AceAjax.IEditSession>;
private root: ShadowRoot;
private addTabButton: HTMLDivElement;
private addTabButton: EditorTab;
private original: Record<string, string>;
constructor() {
super();
this.sessions = {};
this.original = {};
this.root = this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.addTabButton = $el("div", { className: "tab" }, "+");
this.addTabButton = new EditorTab();
this.addTabButton.setAttribute("name", "+");
this.addTabButton.addEventListener("click", () => this.#createNewTab());
this.tabEl = $el("div", { className: "tab-container" }, this.addTabButton);
this.root.append(this.tabEl);
@ -25,19 +81,10 @@ class Editor extends HTMLElement {
.tab-container {
display: flex;
margin-bottom: -2px;
& .tab {
border: 2px solid var(--table-border-color);
padding: 0.5ch 0.8ch;
background-color: var(--button-bg);
cursor: pointer;
&.active {
background-color: var(--table-border-color);
}
&:not(:first-child) {
margin-left: -2px;
}
}
}
file-editor-tab:not(:first-child) {
margin-left: -2px;
}
`;
const slot = $el("slot", { name: "body" });
@ -65,23 +112,27 @@ class Editor extends HTMLElement {
* @param name File name
* @param content File contents
*/
addFile(name: string, content: string) {
addFile(name: string, content: string, saved = true) {
// Add to session list
this.sessions[name] = ace.createEditSession(content);
this.original[name] = content;
// TODO replace this with auto-detection
this.sessions[name].setMode("ace/mode/nix");
// Create tab and set as active
const tab = this.#addTab(name);
const tab = this.#addTab(name, saved);
tab.click();
this.sessions[name].on("change", () => {
tab.setAttribute("modified", this.sessions[name].getValue() != this.original[name] ? "true" : "false");
});
}
setCurrent(name: string) {
this.editor.setSession(this.sessions[name]);
this.tabEl.querySelectorAll<HTMLDivElement>(".tab").forEach(el => {
if (el.dataset.name === name) el.classList.add("active");
else el.classList.remove("active");
this.tabEl.querySelectorAll<EditorTab>("file-editor-tab").forEach(el => {
el.setAttribute("selected", el.getAttribute("name") == name ? "true" : "false");
});
}
@ -94,20 +145,18 @@ class Editor extends HTMLElement {
* @param name Tab name
* @returns Tab element
*/
#addTab(name: string) {
const tab = $el("div", {
className: "tab",
dataset: { name },
"@click": () => this.setCurrent(name)
}, name);
#addTab(name: string, saved = true) {
const tab = new EditorTab();
tab.setAttribute("name", name);
tab.setAttribute("unsaved", saved ? "false" : "true");
tab.addEventListener("click", () => this.setCurrent(name));
this.tabEl.insertBefore(tab, this.addTabButton);
return tab;
}
#createNewTab() {
this.addFile(`untitled${Object.keys(this.sessions).filter(s => s.startsWith("untitled")).length + 1}`, "");
this.addFile(`untitled${Object.keys(this.sessions).filter(s => s.startsWith("untitled")).length + 1}`, "", false);
}
}
customElements.define("file-editor", Editor);

View File

@ -1,19 +1,19 @@
{
"compilerOptions": {
"target": "esnext",
"target": "ES2022",
"paths": {
"/static/*": [
"./static/*"
],
},
"lib": [
"ESNext",
"ES2022",
"DOM"
],
"rootDir": "./",
"baseUrl": "./",
"strictNullChecks": true,
"declaration": true,
"declaration": true
},
"include": [
"./static/**/*.ts",