From 3f7d7528f5801869df534bc020c1dd430a9b92e0 Mon Sep 17 00:00:00 2001 From: PlayerMiller109 <145541890+PlayerMiller109@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:32:49 +0800 Subject: [PATCH] Add files via upload --- README.md | 18 +++++++++++ main.js | 49 ++++++++++++++++++++++++++++ manifest.json | 10 ++++++ src/modals.js | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ styles.css | 19 +++++++++++ 5 files changed, 185 insertions(+) create mode 100644 README.md create mode 100644 main.js create mode 100644 manifest.json create mode 100644 src/modals.js create mode 100644 styles.css diff --git a/README.md b/README.md new file mode 100644 index 0000000..7acedc1 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +发布于 [Obsidian 中文论坛 t34933](https://forum-zh.obsidian.md/t/topic/34933/1),里面有一些前置的说明。 + +作者本人不是计算机专业的,一定要充分测试之后再使用,更好更安全的方式是参考示例自己写。 + +每个 modal 都有一些套路部分,如图红框所示,剩下部分就和定义一个普通函数没区别了,如图绿框所示,整个 modal 可看作一个异步函数 `rgx_form_modal = async (su)=>`。这样,就很好理解在其他文件引用时应该如何书写。 + + + +例如,图示的 modal 只有 resolve,且 `this.close()` 后的 `this.r()` 中间没有参数,所以没有返回值,在其他文件使用时,格式就是: + +```js +// Suppose you have introduced the modals.js in your plugin and name it "mySimpleApi" +const su = { rgx: '', f: '' } +// other possible expressions... +await this.mySimpleApi.rgx_form_modal(su) +// if no resolve, function stops here. +// other possible expressions... +``` \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..94485ee --- /dev/null +++ b/main.js @@ -0,0 +1,49 @@ +const ob = require('obsidian') +module.exports = class extends ob.Plugin { + mySimpleApi = require(`${app.vault.adapter.basePath}/${app.plugins.manifests['ample-modals'].dir}/src/modals.js`)(app, ob) + // 假设 JS 路径如上, 希望的 Api 名为 mySimpleApi + onload() { + // 这里注册命令以展示 modals + this.addCommand({ + id: 'test-suggester', name: 'Test suggester', + callback: async ()=> { + const r1 = await this.mySimpleApi.suggester(['你能看到的'], ['你能得到的'], '预填值, 可不填') + if (!r1) return; console.log(r1) + const r2 = await this.mySimpleApi.suggester(p=> '选项'+p, [1, 2]); if (!r2) return + console.log(r2) + } + }) + this.addCommand({ + id: 'test-rgx_form_modal', name: 'Test rgx_form_modal', + callback: async ()=> { + const su = { rgx: '/(.*?_)(\\d{4})(\\d{2})(\\d{2})/', f: '`$1$2-$3-$4`' } + await this.mySimpleApi.rgx_form_modal(su) + console.log(su) + } + }) + this.addCommand({ + id: 'test-inputPrompt', name: 'Test inputPrompt', + callback: async ()=> { + const r = await this.mySimpleApi.inputPrompt('我是标题: 请输入', '我是占位符, 可不填', '我是预填值, 可不填') + if (!r) return; console.log(r) + } + }) + this.addCommand({ + id: 'test-yesNoPrompt', name: 'Test yesNoPrompt', + callback: async ()=> { + await this.mySimpleApi.yesNoPrompt('我是标题: 你好吗', '我是描述, 可不填') + ? console.log('你好呀') : console.log('还没好') + } + }) + this.addCommand({ + id: 'test-checkboxPrompt', name: 'Test checkboxPrompt', + callback: async ()=> { + const items = ['选项1', '选项2', '选项3'] + const chosenItems = await this.mySimpleApi.checkboxPrompt(items, ['选项1', '选项2']) + // 第 2 个参数为预勾选选项, 可不填 + console.log(chosenItems) + } + }) + } + onunload() {} +} \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..65b9cdc --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "ample-modals", + "name": "Ample Modals", + "version": "0.0.1", + "minAppVersion": "1.4.4", + "description": "Plugin modals.", + "author": "PlayerMiller109", + "authorUrl": "https://github.com/PlayerMiller109/obsidian-ample-modals", + "isDesktopOnly": false +} \ No newline at end of file diff --git a/src/modals.js b/src/modals.js new file mode 100644 index 0000000..35430c2 --- /dev/null +++ b/src/modals.js @@ -0,0 +1,89 @@ +module.exports = (app, ob)=> new class { + suggester = async (display, items, value = '')=> await new class extends ob.FuzzySuggestModal { + constructor(app) { + super(app); this.promise = new Promise(r=> this.r = r) + this.open(); Object.assign(this.inputEl, {value}) + } + getItemText = (item)=> Array.isArray(display) ? display[items.indexOf(item)] : display(item) + getItems = ()=> items + onChooseItem = (item, evt)=> this.r(item) + }(app).promise + + rgx_form_modal = async (su)=> await new class extends ob.Modal { + constructor(app) { + super(app); this.Setting = ob.Setting + this.promise = new Promise(r=> this.r = r) + this.modalEl.addClass('ample'); this.open() + } + get base() { return new this.Setting(this.contentEl).setClass('full-line-input') } + onOpen() { + this.base + .setName('Rgx').addText(inpu=> inpu.setValue(su.rgx).onChange(value=> su.rgx = value)) + .addButton(btn=> btn.setButtonText('Submit').onClick(()=> { this.close(); this.r() })) + this.base.setName('Form').addText(inpu=> inpu.setValue(su.f).onChange(value=> su.f = value)) + } + onClose() { this.contentEl.empty() } + }(app).promise + + inputPrompt = async (header, placeholder = '', value = '')=> await new class extends ob.Modal { + constructor(app) { + super(app); this.Setting = ob.Setting + this.promise = new Promise((resolve, reject)=> (this.ok = resolve, this.fail = reject)).catch(e=> e) + this.modalEl.addClass('ample'); this.open() + } + onOpen() { + this.setTitle(header) + const area = this.contentEl.createEl('textarea', { cls: 'full-line-input' }) + Object.assign(area, { + placeholder, value, style: 'height: 30px;', + oninput: ()=> { area.style.height = '32px'; area.style.height = `${area.scrollHeight}px` }, + onkeydown: evt=> { + if (evt.key != 'Enter' || evt.shiftKey || evt.ctrlKey) return; evt.preventDefault() + this.close(); this.ok(area.value) + } + }); area.focus(); area.select() + new this.Setting(this.contentEl) + .addButton(btn=> btn.setButtonText('Cancel').onClick(()=> { this.close(); this.fail() })) + .addButton(btn=> btn.setClass('ok-btn').setButtonText('Ok').onClick(()=> { this.close(); this.ok(area.value) })) + } + onClose() { this.contentEl.empty() } + }(app).promise + + yesNoPrompt = async (header, detail)=> await new class extends ob.Modal { + constructor(app) { + super(app); this.Setting = ob.Setting + this.promise = new Promise((resolve, reject)=> (this.ok = resolve, this.fail = reject)).catch(e=> e) + this.modalEl.addClass('ample'); this.open() + } + onOpen() { + this.setTitle(header); this.contentEl.createEl('div', { text: detail }) + new this.Setting(this.contentEl) + .addButton(btn=> btn.setButtonText('No').onClick(()=> { this.close(); this.fail(!1) })) + .addButton(btn=> btn.setWarning().setButtonText('Yes').onClick(()=> { this.close(); this.ok(!0) })) + } + onClose() { this.contentEl.empty() } + }(app).promise + + checkboxPrompt = async (items, chosenItems)=> await new class extends ob.Modal { + constructor(app) { + super(app); this.Setting = ob.Setting + this.promise = new Promise(r=> this.r = r) + this.modalEl.addClass('ample'); this.open() + } + onOpen() { + items.map(item=> new this.Setting(this.contentEl) + .setClass('toggle-box').setName(item) + .addToggle(box=> box + .setValue(chosenItems.includes(item)) + .onChange(flag=> + flag ? chosenItems.push(item) : chosenItems.splice(chosenItems.findIndex(chosen=> chosen == item), 1) + ) + ) + ) + new this.Setting(this.contentEl).addButton(btn=> btn + .setButtonText('Submit').onClick(()=> { this.close(); this.r(chosenItems) }) + ) + } + onClose() { this.contentEl.empty() } + }(app).promise +} \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..b7ea920 --- /dev/null +++ b/styles.css @@ -0,0 +1,19 @@ +@charset "UTF-8"; + +.ample.modal { + & .setting-item {border-top: none;} + + & .full-line-input > .setting-item-info {display: none;} + & .full-line-input > .setting-item-control > input, + textarea.full-line-input { + flex-grow: 1; width: 100%; resize: none; overflow: hidden; + } + + & .ok-btn { + --interactive-normal: var(--interactive-accent); + --interactive-hover: var(--interactive-accent-hover); + } + + & .toggle-box {padding-block: var(--size-4-1);} + & .toggle-box+ .setting-item {padding-bottom: 0;} +} \ No newline at end of file