# Extend Obsidian Classes in CustomJS ## Metadata **Status**:: #x **Zettel**:: #zettel/literature **Created**:: [[2023-08-12]] **Topic**:: [[♯ Obsidian]] **Parent**:: [[Obsidian CustomJS Plugin]], [[JavaScript]] ## Synopsis Use the syntax `Field = class extends customJS.obsidian.ParentClass {}` ```javascript class Input { PromptModal = class extends customJS.obsidian.Modal { title = "Input"; value = ""; submitted = false; placeholder = "Type text here"; constructor(opts = {}) { super(app); Object.assign(this, opts); } onOpen() { const { TextComponent } = customJS.obsidian; const { titleEl, contentEl, title } = this; titleEl.setText(title); const textInput = new TextComponent(contentEl); textInput.inputEl.style.width = "100%"; textInput.setPlaceholder(this.placeholder); textInput.setValue(this.value); textInput.onChange((value) => (this.value = value)); textInput.inputEl.addEventListener("keydown", (event) => this.enterCallback(event) ); } onClose() { if (this.resolve) { this.resolve(this.submitted ? this.value : null); } } enterCallback(event) { if (event.key === "Enter") { this.submitted = true; event.preventDefault(); this.close(); } } }; SuggestModal = class extends customJS.obsidian.FuzzySuggestModal { items = []; constructor(opts = {}) { super(app); Object.assign(this, opts); } getItems() { return this.items; } getItemText(item) { if (typeof item === "object") { return item.title; } return item; } onChooseItem(item) { if (this.resolve) { this.resolve(item); this.resolve = null; } } onClose() { // ensure onChooseItem is called setTimeout(() => this.onChooseItem(null), 0); } }; async prompt(opts = {}) { const { PromptModal } = this; return new Promise((resolve) => { const modal = new PromptModal({ …opts, resolve, }); modal.open(); }); } async suggester(items, opts = {}) { const { SuggestModal } = this; return new Promise((resolve) => { const modal = new SuggestModal({ …opts, items, resolve, }); modal.open(); }); } } ```