staxman-old/static/scripts/components/editor/script.ts

113 lines
2.8 KiB
TypeScript
Raw Normal View History

2023-11-30 00:57:45 +00:00
import "/static/vendor/ace/ace.js";
2023-11-30 09:14:32 +00:00
import { $el } from "/static/vendor/domutil/domutil.js";
2023-11-30 00:57:45 +00:00
class Editor extends HTMLElement {
2023-11-30 09:14:32 +00:00
private editor: AceAjax.Editor;
private tabEl: HTMLDivElement;
2023-12-03 10:36:45 +00:00
private sessions: Record<string, AceAjax.IEditSession>;
2023-11-30 09:14:32 +00:00
private root: ShadowRoot;
2023-11-30 09:29:30 +00:00
private addTabButton: HTMLDivElement;
2023-11-30 00:57:45 +00:00
constructor() {
super();
2023-12-03 10:36:45 +00:00
this.sessions = {};
2023-11-30 09:14:32 +00:00
this.root = this.attachShadow({ mode: "open" });
2023-11-30 00:57:45 +00:00
}
connectedCallback() {
2023-11-30 09:29:30 +00:00
this.addTabButton = $el("div", { className: "tab" }, "+");
2023-12-03 10:36:45 +00:00
this.addTabButton.addEventListener("click", () => this.#createNewTab());
2023-11-30 09:29:30 +00:00
this.tabEl = $el("div", { className: "tab-container" }, this.addTabButton);
2023-11-30 09:14:32 +00:00
this.root.append(this.tabEl);
2023-11-30 00:57:45 +00:00
const style = $el("style");
style.textContent = `
.tab-container {
display: flex;
2023-11-30 09:29:30 +00:00
margin-bottom: -2px;
2023-11-30 00:57:45 +00:00
& .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;
}
}
}
`;
const slot = $el("slot", { name: "body" });
2023-11-30 09:14:32 +00:00
this.root.append(style, slot);
2023-11-30 00:57:45 +00:00
const editorEl = $el("div", { slot: "body" });
this.append(editorEl);
// Create editor
ace.config.set('basePath', '/static/vendor/ace')
this.editor = ace.edit(editorEl);
// todo make this stuff customizable??
this.editor.setTheme("ace/theme/dracula");
this.editor.setOptions({
fontFamily: "Iosevka Web",
fontSize: "12pt",
});
this.editor.getSession().setUseWrapMode(true);
this.editor.setKeyboardHandler("ace/keyboard/vim");
}
/**
* Add a file editing session
2023-11-30 09:14:32 +00:00
* @param name File name
* @param content File contents
2023-11-30 00:57:45 +00:00
*/
2023-11-30 09:14:32 +00:00
addFile(name: string, content: string) {
2023-11-30 00:57:45 +00:00
// Add to session list
2023-12-03 10:36:45 +00:00
this.sessions[name] = ace.createEditSession(content);
2023-11-30 00:57:45 +00:00
// TODO replace this with auto-detection
2023-12-03 10:36:45 +00:00
this.sessions[name].setMode("ace/mode/nix");
2023-11-30 00:57:45 +00:00
// Create tab and set as active
const tab = this.#addTab(name);
tab.click();
}
2023-11-30 09:29:30 +00:00
setCurrent(name: string) {
2023-12-03 10:36:45 +00:00
this.editor.setSession(this.sessions[name]);
2023-11-30 09:29:30 +00:00
this.tabEl.querySelectorAll<HTMLDivElement>(".tab").forEach(el => {
if (el.dataset.name === name) el.classList.add("active");
else el.classList.remove("active");
});
}
2023-12-03 10:36:45 +00:00
files(): [String, String][] {
return Object.entries(this.sessions).map(([name, session]) => [name, session.getValue()]);
}
2023-11-30 00:57:45 +00:00
/**
* Create a new tab
2023-11-30 09:14:32 +00:00
* @param name Tab name
2023-11-30 00:57:45 +00:00
* @returns Tab element
*/
2023-11-30 09:14:32 +00:00
#addTab(name: string) {
2023-11-30 00:57:45 +00:00
const tab = $el("div", {
className: "tab",
2023-11-30 09:29:30 +00:00
dataset: { name },
"@click": () => this.setCurrent(name)
2023-11-30 00:57:45 +00:00
}, name);
2023-11-30 09:29:30 +00:00
this.tabEl.insertBefore(tab, this.addTabButton);
2023-11-30 00:57:45 +00:00
return tab;
}
2023-12-03 10:36:45 +00:00
#createNewTab() {
this.addFile(`untitled${Object.keys(this.sessions).filter(s => s.startsWith("untitled")).length + 1}`, "");
}
2023-11-30 00:57:45 +00:00
}
customElements.define("file-editor", Editor);