Skip to content

Instantly share code, notes, and snippets.

@Fevol
Last active December 23, 2022 23:28
Show Gist options
  • Save Fevol/7327e41efbb1a30d5d9bb2c1e01c940c to your computer and use it in GitHub Desktop.
Save Fevol/7327e41efbb1a30d5d9bb2c1e01c940c to your computer and use it in GitHub Desktop.
Obsidian - Add Button List Component
// Copy this entire file into your source directory, you can adapt it if you want
// This code is written in a way that makes use of the same interface as all other Obsidian settings components
import {setIcon, Setting, ValueComponent} from "obsidian";
declare module "obsidian" {
interface Setting {
addButtonList(cb: (component: ButtonListComponent) => any): this;
}
}
class ButtonListComponent extends ValueComponent<string[]> {
/**
* Element that contains the list of selected values
* @public
*/
buttonListEl: HTMLElement;
/**
* Element that contains available options
* @public
*/
selectEl: HTMLSelectElement;
/**
* Each option may only be selected once
* @private
*/
private uniqueSelection: boolean = true;
/**
* Selected options will be hidden from the dropdown
* @private
*/
private hideSelected: boolean = false;
/**
* Maintain order of options
* @private
*/
private orderedSelection: boolean = true;
/**
* User-selected values
* @private
*/
private selectedValues: string[] = [];
/**
* Options that are available to select
* @private
*/
private options: Record<string, string> = {};
/**
* Onchange callback
* @private
*/
private callback: (value: string[]) => any = () => {
};
/**
* Is disabled
* @private
*/
disabled: boolean = false;
constructor(containerEl: HTMLElement) {
super();
this.buttonListEl = containerEl.createEl('div', {cls: 'setting-command-hotkeys'});
this.selectEl = containerEl.createEl('select', {cls: 'dropdown'});
this.selectEl.createEl('option', {value: '', text: '+'});
this.selectEl.addEventListener('change', (e) => {
const value = this.selectEl.value;
if (value) {
if ((this.uniqueSelection && !this.selectedValues.includes(value)) || !this.uniqueSelection) {
this.selectedValues.push(value);
if (this.orderedSelection)
this.selectedValues.sort((a, b) => Object.keys(this.options).indexOf(a) - Object.keys(this.options).indexOf(b));
this.render();
this.callback(this.selectedValues);
}
this.selectEl.value = '';
}
});
}
/**
* Render the component
* @private
*/
private render() {
this.buttonListEl.empty();
for (const value of this.selectedValues) {
const buttonEl = this.buttonListEl.createEl('span', {cls: 'setting-hotkey', text: this.options[value]});
if (!this.disabled) {
const closeBtn = buttonEl.createEl('span', {cls: 'setting-hotkey-icon'});
setIcon(closeBtn, 'cross');
closeBtn.addEventListener('click', () => {
this.selectedValues = this.selectedValues.filter((v) => v !== value);
this.render();
this.callback(this.selectedValues);
});
}
}
if (this.hideSelected) {
// Remove all options and only add the ones that are not selected
this.selectEl.empty();
this.selectEl.createEl('option', {value: '', text: '+'});
for (const value in this.options)
if (!this.hideSelected || !this.selectedValues.includes(value))
this.selectEl.createEl('option', {value: value, text: this.options[value]});
}
}
setDisabled(disabled: boolean): this {
this.selectEl.disabled = disabled;
this.disabled = disabled;
this.render();
return this;
}
addOption(value: string, text: string): this {
this.selectEl.createEl('option', {value: value, text: text});
this.options[value] = text;
this.render();
return this;
}
addOptions(options: { value: string, text: string }[]): this {
for (const {value, text} of options) {
this.selectEl.createEl('option', {value: value, text: text});
this.options[value] = text;
}
this.render();
return this;
}
getValue(): string[] {
return this.selectedValues;
}
setValue(value: string[]): this {
this.selectedValues = value;
this.render();
return this;
}
/**
* Hide selected options from the dropdown
* @param setting - default: false
*/
setHideSelected(setting: boolean = false) {
this.hideSelected = setting;
// If the buttonlist's setting can be changed on the fly, re-rendering is necessary
// this.render();
// Remove all options and only add the ones that are not selected
this.selectEl.empty();
this.selectEl.createEl('option', {value: '', text: '+'});
for (const value in this.options)
if (!this.hideSelected || !this.selectedValues.includes(value))
this.selectEl.createEl('option', {value: value, text: this.options[value]});
return this;
}
/**
* Do not allow an option to be selected more than once
* @param setting - default: true
*/
setUniqueSelection(setting: boolean = true): this {
this.uniqueSelection = setting;
// If the buttonlist's setting can be changed on the fly, re-rendering is necessary
// this.selectedValues = this.selectedValues
// .filter((value, index, self) => self.indexOf(value) === index);
// this.render();
return this;
}
/**
* Set selection to be ordered according to the order of the options
* @param setting - default: true
*/
setOrderedSelection(setting: boolean = true): this {
this.orderedSelection = setting;
// If the buttonlist's setting can be changed on the fly, re-rendering is necessary
// this.render();
// Sort the selected values according to the order of the options
this.selectedValues = this.selectedValues
.sort((a, b) => Object.keys(this.options).indexOf(a) - Object.keys(this.options).indexOf(b));
return this;
}
onChange(callback: (value: string[]) => any): this {
this.callback = callback;
return this;
};
}
Setting.prototype.addButtonList = function (cb) {
const component = new ButtonListComponent(this.controlEl);
return this.components.push(component), cb(component), this;
}
// !!! Either import this file, or put the code above in the same file as your settings !!!
import './buttonlist';
// Example usage
export class SampleSettingTab extends PluginSettingTab {
display(): void {
const {containerEl} = this;
let settingel = new Setting(containerEl)
.setName('Buttonlist')
.setDesc('This is a buttonlist component')
.addButtonList((component) => {
component
.addOptions([
{value: '1', text: 'Option 1'},
{value: '2', text: 'Option 2'},
{value: '3', text: 'Option 3'},
{value: '4', text: 'Option 4'},
{value: '5', text: 'Option 5'},
])
.setValue(['1', '3'])
// .setUniqueSelection(true)
// .setHideSelected(false)
// .setOrderedSelection(true)
.onChange(async (value) => {
console.log(value);
});
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment