# 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();
});
}
}
```