From cdb5085f356b0c52e634421a5c190b38429ff11a Mon Sep 17 00:00:00 2001 From: frostime Date: Sat, 16 Sep 2023 18:03:59 +0800 Subject: [PATCH] setting-utils --- src/index.ts | 81 +++++++++++++----- src/libs/index.d.ts | 20 +++++ src/libs/setting-utils.ts | 176 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+), 22 deletions(-) create mode 100644 src/libs/index.d.ts create mode 100644 src/libs/setting-utils.ts diff --git a/src/index.ts b/src/index.ts index 02aa494..b98b48a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,8 @@ import "@/index.scss"; import HelloExample from "@/hello.svelte"; import SettingPannel from "@/libs/setting-panel.svelte"; +import { SettingUtils } from "./libs/setting-utils"; + const STORAGE_NAME = "menu-config"; const TAB_TYPE = "custom_tab"; const DOCK_TYPE = "dock_tab"; @@ -27,6 +29,7 @@ export default class PluginSample extends Plugin { private customTab: () => IModel; private isMobile: boolean; private blockIconEventBindThis = this.blockIconEvent.bind(this); + private settingUtils: SettingUtils; async onload() { this.data[STORAGE_NAME] = { readonlyText: "Readonly" }; @@ -158,32 +161,66 @@ export default class PluginSample extends Plugin { } }); - const textareaElement = document.createElement("textarea"); - this.setting = new Setting({ - confirmCallback: () => { - this.saveData(STORAGE_NAME, { readonlyText: textareaElement.value }); + this.settingUtils = new SettingUtils(this, STORAGE_NAME); + this.settingUtils.addItem({ + key: "Input", + value: "", + type: "textinput", + title: "Readonly text", + description: "Input description", + }); + this.settingUtils.addItem({ + key: "InputArea", + value: "", + type: "textarea", + title: "Readonly text", + description: "Input description", + }); + this.settingUtils.addItem({ + key: "Select", + value: 1, + type: "select", + title: "Readonly text", + description: "Select description", + select: { + options: [ + { + val: 1, + text: "Option 1" + }, + { + val: 2, + text: "Option 2" + } + ] } }); - this.setting.addItem({ - title: "Readonly text", - createActionElement: () => { - textareaElement.className = "b3-text-field fn__block"; - textareaElement.placeholder = "Readonly text in the menu"; - textareaElement.value = this.data[STORAGE_NAME].readonlyText; - return textareaElement; - }, + this.settingUtils.addItem({ + key: "Slider", + value: 50, + type: "slider", + title: "Slider text", + description: "Slider description", + slider: { + min: 0, + max: 100, + step: 1, + } }); - const btnaElement = document.createElement("button"); - btnaElement.className = "b3-button b3-button--outline fn__flex-center fn__size200"; - btnaElement.textContent = "Open"; - btnaElement.addEventListener("click", () => { - window.open("https://github.com/siyuan-note/plugin-sample-vite-svelte"); - }); - this.setting.addItem({ - title: "Open plugin url", - description: "Open plugin url in browser", - actionElement: btnaElement, + this.settingUtils.addItem({ + key: "Btn", + value: "", + type: "button", + title: "Button", + description: "Button description", + button: { + label: "Button", + callback: () => { + showMessage("Button clicked"); + } + } }); + this.settingUtils.load(); this.protyleSlash = [{ filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"], diff --git a/src/libs/index.d.ts b/src/libs/index.d.ts new file mode 100644 index 0000000..b46f655 --- /dev/null +++ b/src/libs/index.d.ts @@ -0,0 +1,20 @@ +type TSettingItemType = "checkbox" | "select" | "textinput" | "textarea" | "slider" | "button"; +interface ISettingItem { + key: string; + value: any; + type: TSettingItemType; + title: string; + description?: string; + slider?: { + min: number; + max: number; + step: number; + }; + select?: { + options: {val: any; text: string}[]; + }; + button?: { + label: string; + callback: () => void; + } +} diff --git a/src/libs/setting-utils.ts b/src/libs/setting-utils.ts new file mode 100644 index 0000000..5c5638b --- /dev/null +++ b/src/libs/setting-utils.ts @@ -0,0 +1,176 @@ +import { Plugin, Setting } from 'siyuan'; + +export class SettingUtils { + plugin: Plugin; + name: string; + file: string; + + settings: Map = new Map(); + elements: Map = new Map(); + + constructor(plugin: Plugin, name?: string, width?: string, height?: string) { + this.name = name ?? 'settings'; + this.plugin = plugin; + this.file = this.name.endsWith('.json') ? this.name : `${this.name}.json`; + this.plugin.setting = new Setting({ + width: width, + height: height, + confirmCallback: () => { + for (let key of this.settings.keys()) { + this.updateValue(key); + } + let data = this.dump(); + this.plugin.data[this.name] = data; + this.save(); + } + }); + } + + async load() { + let data = await this.plugin.loadData(this.file); + if (data) { + for (let [key, item] of this.settings) { + item.value = data?.[key] ?? item.value; + } + } + } + + async save() { + let data = this.dump(); + await this.plugin.saveData(this.file, this.dump()); + return data; + } + + /** + * Get setting item value + * @param key key name + * @returns setting item value + */ + get(key: string) { + return this.settings.get(key)?.value; + } + + /** + * 将设置项目导出为 JSON 对象 + * @returns object + */ + dump(): Object { + let data: any = {}; + for (let [key, item] of this.settings) { + if (item.type === 'button') continue; + data[key] = item.value; + } + return data; + } + + addItem(item: ISettingItem) { + this.settings.set(item.key, item); + let itemElement: HTMLElement; + switch (item.type) { + case 'checkbox': + let element: HTMLInputElement = document.createElement('input'); + element.type = 'checkbox'; + element.checked = item.value; + element.className = "b3-switch fn__flex-center"; + itemElement = element; + break; + case 'select': + let selectElement: HTMLSelectElement = document.createElement('select'); + selectElement.className = "b3-select fn__flex-center fn__size200"; + for (let option of item.select?.options ?? []) { + let optionElement = document.createElement('option'); + optionElement.value = option.val; + optionElement.text = option.text; + selectElement.appendChild(optionElement); + } + selectElement.value = item.value; + itemElement = selectElement; + break; + case 'slider': + let sliderElement: HTMLInputElement = document.createElement('input'); + sliderElement.type = 'range'; + sliderElement.className = 'b3-slider fn__size200'; + sliderElement.ariaLabel = item.value; + sliderElement.min = item.slider?.min.toString() ?? '0'; + sliderElement.max = item.slider?.max.toString() ?? '100'; + sliderElement.step = item.slider?.step.toString() ?? '1'; + sliderElement.value = item.value; + itemElement = sliderElement; + break; + case 'textinput': + let textInputElement: HTMLInputElement = document.createElement('input'); + textInputElement.className = 'b3-text-field fn__flex-center fn__size200'; + textInputElement.value = item.value; + itemElement = textInputElement; + break; + case 'textarea': + let textareaElement: HTMLTextAreaElement = document.createElement('textarea'); + textareaElement.className = "b3-text-field fn__block"; + textareaElement.value = item.value; + itemElement = textareaElement; + break; + case 'button': + let buttonElement: HTMLButtonElement = document.createElement('button'); + buttonElement.className = "b3-button b3-button--outline fn__flex-center fn__size200"; + buttonElement.innerText = item.button?.label ?? 'Button'; + buttonElement.onclick = item.button?.callback ?? (() => {}); + itemElement = buttonElement; + break; + } + this.elements.set(item.key, itemElement); + this.plugin.setting.addItem({ + title: item.title, + description: item?.description, + createActionElement: () => { + let element = this.getElement(item.key); + return element; + } + }) + } + + private getElement(key: string) { + let item = this.settings.get(key); + let element = this.elements.get(key) as any; + switch (item.type) { + case 'checkbox': + element.checked = item.value; + break; + case 'select': + element.value = item.value; + break; + case 'slider': + element.value = item.value; + break; + case 'textinput': + element.value = item.value; + break; + case 'textarea': + element.value = item.value; + break; + } + return element; + } + + private updateValue(key: string) { + let item = this.settings.get(key); + let element = this.elements.get(key) as any; + switch (item.type) { + case 'checkbox': + item.value = element.checked; + break; + case 'select': + item.value = element.value; + break; + case 'slider': + item.value = element.value; + break; + case 'textinput': + item.value = element.value; + break; + case 'textarea': + item.value = element.value; + break; + } + } + +} \ No newline at end of file