This commit is contained in:
parent
777f31761c
commit
eaca7fc192
20 changed files with 469 additions and 1718 deletions
7
src/const.ts
Normal file
7
src/const.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export const SVG_MIME = "image/svg+xml";
|
||||
export const JSON_MIME = "application/json";
|
||||
export const DATA_PATH = "/data/assets";
|
||||
export const STORAGE_PATH = "/data/storage/petal/siyuan-jsdraw-plugin";
|
||||
export const TOOLBAR_PATH = STORAGE_PATH + "/toolbar.json";
|
||||
export const CONFIG_PATH = STORAGE_PATH + "/conf.json";
|
||||
export const EMBED_PATH = "/plugins/siyuan-jsdraw-plugin/webapp/?id=";
|
75
src/editorTab.ts
Normal file
75
src/editorTab.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import {ITabModel, openTab, Plugin} from "siyuan"
|
||||
import Editor, {BaseWidget, EditorEventType} from "js-draw";
|
||||
import { MaterialIconProvider } from '@js-draw/material-icons';
|
||||
import 'js-draw/styles';
|
||||
import {getFile, saveFile} from "@/file";
|
||||
import {JSON_MIME, SVG_MIME, TOOLBAR_PATH} from "@/const";
|
||||
import {idToPath} from "@/helper";
|
||||
|
||||
export function openEditorTab(p: Plugin, fileID: string) {
|
||||
openTab({
|
||||
app: p.app,
|
||||
custom: {
|
||||
title: 'Drawing',
|
||||
icon: 'iconDraw',
|
||||
id: "siyuan-jsdraw-pluginwhiteboard",
|
||||
data: { id: fileID }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function saveCallback(editor: Editor, fileID: string, saveButton: BaseWidget) {
|
||||
const svgElem = editor.toSVG();
|
||||
try {
|
||||
saveFile(idToPath(fileID), SVG_MIME, svgElem.outerHTML);
|
||||
saveButton.setDisabled(true);
|
||||
setTimeout(() => { // @todo improve save button feedback
|
||||
saveButton.setDisabled(false);
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
alert("Error saving drawing! Enter developer mode to find the error, and a copy of the current status.");
|
||||
console.error(error);
|
||||
console.log("Couldn't save SVG: ", svgElem.outerHTML)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function createEditor(i: ITabModel) {
|
||||
|
||||
const fileID = i.data.id;
|
||||
if(fileID == null) {
|
||||
alert("File ID missing - couldn't open file.")
|
||||
return;
|
||||
}
|
||||
|
||||
const editor = new Editor(i.element, {
|
||||
iconProvider: new MaterialIconProvider(),
|
||||
});
|
||||
|
||||
const toolbar = editor.addToolbar();
|
||||
|
||||
// restore toolbar state
|
||||
getFile(TOOLBAR_PATH).then(toolbarState => {
|
||||
if(toolbarState!= null) {
|
||||
toolbar.deserializeState(toolbarState)
|
||||
}
|
||||
});
|
||||
// restore drawing
|
||||
getFile(idToPath(fileID)).then(svg => {
|
||||
if(svg != null) {
|
||||
editor.loadFromSVG(svg);
|
||||
}
|
||||
});
|
||||
|
||||
// save logic
|
||||
const saveButton = toolbar.addSaveButton(() => saveCallback(editor, fileID, saveButton));
|
||||
|
||||
// save toolbar config on tool change (toolbar state is not saved in SVGs!)
|
||||
editor.notifier.on(EditorEventType.ToolUpdated, () => {
|
||||
saveFile(TOOLBAR_PATH, JSON_MIME, toolbar.serializeState());
|
||||
});
|
||||
|
||||
editor.dispatch(editor.setBackgroundStyle({ autoresize: true }), false);
|
||||
editor.getRootElement().style.height = '100%';
|
||||
|
||||
}
|
37
src/file.ts
Normal file
37
src/file.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import {getFileBlob, putFile} from "@/api";
|
||||
|
||||
function toFile(title: string, content: string, mimeType: string){
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
return new File([blob], title, { type: mimeType });
|
||||
}
|
||||
|
||||
export function saveFile(path: string, mimeType: string, content: string) {
|
||||
|
||||
const file = toFile(path.split('/').pop(), content, mimeType);
|
||||
|
||||
try {
|
||||
putFile(path, false, file);
|
||||
} catch (error) {
|
||||
console.error("Error saving file:", error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export async function getFile(path: string) {
|
||||
|
||||
const blob = await getFileBlob(path);
|
||||
const jsonText = await blob.text();
|
||||
|
||||
// if we got a 404 api response, we will return null
|
||||
try {
|
||||
const res = JSON.parse(jsonText);
|
||||
if(res.code == 404) {
|
||||
return null;
|
||||
}
|
||||
}catch {}
|
||||
|
||||
// js-draw expects a string!
|
||||
return jsonText;
|
||||
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
<!--
|
||||
Copyright (c) 2024 by frostime. All Rights Reserved.
|
||||
Author : frostime
|
||||
Date : 2023-11-19 12:30:45
|
||||
FilePath : /src/hello.svelte
|
||||
LastEditTime : 2024-10-16 14:37:50
|
||||
Description :
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { version, sql as query } from "@/api";
|
||||
import { showMessage, fetchPost, Protyle } from "siyuan";
|
||||
|
||||
export let app;
|
||||
|
||||
let time: string = "";
|
||||
let ver: string = "";
|
||||
|
||||
let divProtyle: HTMLDivElement;
|
||||
let protyle: any;
|
||||
let blockID: string = '';
|
||||
|
||||
onMount(async () => {
|
||||
ver = await version();
|
||||
fetchPost("/api/system/currentTime", {}, (response) => {
|
||||
time = new Date(response.data).toString();
|
||||
});
|
||||
protyle = await initProtyle();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
showMessage("Hello panel closed");
|
||||
protyle.destroy();
|
||||
});
|
||||
|
||||
async function initProtyle() {
|
||||
let sql = "SELECT * FROM blocks ORDER BY RANDOM () LIMIT 1;";
|
||||
let blocks: Block[] = await query(sql);
|
||||
blockID = blocks[0].id;
|
||||
return new Protyle(app, divProtyle, {
|
||||
blockId: blockID
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="b3-dialog__content">
|
||||
<div>appId:</div>
|
||||
<div class="fn__hr"></div>
|
||||
<div class="plugin-sample__time">${app?.appId}</div>
|
||||
<div class="fn__hr"></div>
|
||||
<div class="fn__hr"></div>
|
||||
<div>API demo:</div>
|
||||
<div class="fn__hr" />
|
||||
<div class="plugin-sample__time">
|
||||
System current time: <span id="time">{time}</span>
|
||||
</div>
|
||||
<div class="fn__hr" />
|
||||
<div class="fn__hr" />
|
||||
<div>Protyle demo: id = {blockID}</div>
|
||||
<div class="fn__hr" />
|
||||
<div id="protyle" style="height: 360px;" bind:this={divProtyle}/>
|
||||
</div>
|
||||
|
57
src/helper.ts
Normal file
57
src/helper.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { Plugin } from 'siyuan';
|
||||
import {DATA_PATH, EMBED_PATH} from "@/const";
|
||||
|
||||
const drawIcon: string = `
|
||||
<symbol id="iconDraw" viewBox="0 0 28 28">
|
||||
<path clip-rule="evenodd" d="M26.4097 9.61208C27.196 8.8358 27.1969 7.57578 26.4117 6.79842L21.1441 1.58305C20.3597 0.806412 19.0875 0.805538 18.302 1.5811L3.55214 16.1442C3.15754 16.5338 2.87982 17.024 2.74985 17.5603L1.05726 24.5451C0.697341 26.0304 2.09375 27.3461 3.57566 26.918L10.3372 24.9646C10.8224 24.8244 11.2642 24.5658 11.622 24.2125L26.4097 9.61208ZM20.4642 12.6725L10.2019 22.8047C10.0827 22.9225 9.9354 23.0087 9.77366 23.0554L4.17079 24.6741C3.65448 24.8232 3.16963 24.359 3.2962 23.8367L4.70476 18.024C4.74809 17.8453 4.84066 17.6819 4.97219 17.552L15.195 7.45865L20.4642 12.6725ZM21.8871 11.2676L16.618 6.05372L19.0185 3.68356C19.4084 3.29865 20.0354 3.29908 20.4247 3.68454L24.271 7.49266C24.6666 7.88436 24.6661 8.52374 24.27 8.91488L21.8871 11.2676Z" fill-rule="evenodd"/>
|
||||
</symbol>
|
||||
`;
|
||||
|
||||
export function loadIcons(p: Plugin) {
|
||||
const icons = drawIcon;
|
||||
p.addIcons(icons);
|
||||
}
|
||||
|
||||
export function getMenuHTML(icon: string, text: string): string {
|
||||
return `
|
||||
<div class="b3-list-item__first">
|
||||
<svg class="b3-list-item__graphic">
|
||||
<use xlink:href="#${icon}"></use>
|
||||
</svg>
|
||||
<span class="b3-list-item__text">${text}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
export function generateSiyuanId() {
|
||||
const now = new Date();
|
||||
|
||||
const year = now.getFullYear().toString();
|
||||
const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = now.getSeconds().toString().padStart(2, '0');
|
||||
|
||||
const timestamp = `${year}${month}${day}${hours}${minutes}${seconds}`;
|
||||
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz';
|
||||
let random = '';
|
||||
for (let i = 0; i < 7; i++) {
|
||||
random += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||
}
|
||||
|
||||
return `${timestamp}-${random}`;
|
||||
}
|
||||
|
||||
export function idToPath(id: string) {
|
||||
return DATA_PATH + '/' + id + '.svg';
|
||||
}
|
||||
|
||||
// [Edit](siyuan://plugins/siyuan-jsdraw-pluginwhiteboard/?icon=iconDraw&title=Drawing&data={"id":"${id}"})
|
||||
// 
|
||||
export function getPreviewHTML(id: string): string {
|
||||
return `
|
||||
<iframe src="${EMBED_PATH + id}&antiCache=0"></iframe>
|
||||
`
|
||||
}
|
945
src/index.ts
945
src/index.ts
|
@ -1,943 +1,44 @@
|
|||
import {
|
||||
Plugin,
|
||||
showMessage,
|
||||
confirm,
|
||||
Dialog,
|
||||
Menu,
|
||||
openTab,
|
||||
adaptHotkey,
|
||||
getFrontend,
|
||||
getBackend,
|
||||
IModel,
|
||||
Protyle,
|
||||
openWindow,
|
||||
IOperation,
|
||||
Constants,
|
||||
openMobileFileById,
|
||||
lockScreen,
|
||||
ICard,
|
||||
ICardData
|
||||
} from "siyuan";
|
||||
import "@/index.scss";
|
||||
import {Plugin, Protyle} from 'siyuan';
|
||||
import {getPreviewHTML, loadIcons, getMenuHTML, generateSiyuanId} from "@/helper";
|
||||
import {createEditor, openEditorTab} from "@/editorTab";
|
||||
|
||||
import HelloExample from "@/hello.svelte";
|
||||
import SettingExample from "@/setting-example.svelte";
|
||||
export default class DrawJSPlugin extends Plugin {
|
||||
onload() {
|
||||
|
||||
import { SettingUtils } from "./libs/setting-utils";
|
||||
import { svelteDialog } from "./libs/dialog";
|
||||
|
||||
const STORAGE_NAME = "menu-config";
|
||||
const TAB_TYPE = "custom_tab";
|
||||
const DOCK_TYPE = "dock_tab";
|
||||
|
||||
export default class PluginSample extends Plugin {
|
||||
|
||||
customTab: () => IModel;
|
||||
private isMobile: boolean;
|
||||
private blockIconEventBindThis = this.blockIconEvent.bind(this);
|
||||
private settingUtils: SettingUtils;
|
||||
|
||||
async onload() {
|
||||
this.data[STORAGE_NAME] = { readonlyText: "Readonly" };
|
||||
|
||||
console.log("loading plugin-sample", this.i18n);
|
||||
|
||||
const frontEnd = getFrontend();
|
||||
this.isMobile = frontEnd === "mobile" || frontEnd === "browser-mobile";
|
||||
// 图标的制作参见帮助文档
|
||||
this.addIcons(`<symbol id="iconFace" viewBox="0 0 32 32">
|
||||
<path d="M13.667 17.333c0 0.92-0.747 1.667-1.667 1.667s-1.667-0.747-1.667-1.667 0.747-1.667 1.667-1.667 1.667 0.747 1.667 1.667zM20 15.667c-0.92 0-1.667 0.747-1.667 1.667s0.747 1.667 1.667 1.667 1.667-0.747 1.667-1.667-0.747-1.667-1.667-1.667zM29.333 16c0 7.36-5.973 13.333-13.333 13.333s-13.333-5.973-13.333-13.333 5.973-13.333 13.333-13.333 13.333 5.973 13.333 13.333zM14.213 5.493c1.867 3.093 5.253 5.173 9.12 5.173 0.613 0 1.213-0.067 1.787-0.16-1.867-3.093-5.253-5.173-9.12-5.173-0.613 0-1.213 0.067-1.787 0.16zM5.893 12.627c2.28-1.293 4.040-3.4 4.88-5.92-2.28 1.293-4.040 3.4-4.88 5.92zM26.667 16c0-1.040-0.16-2.040-0.44-2.987-0.933 0.2-1.893 0.32-2.893 0.32-4.173 0-7.893-1.92-10.347-4.92-1.4 3.413-4.187 6.093-7.653 7.4 0.013 0.053 0 0.12 0 0.187 0 5.88 4.787 10.667 10.667 10.667s10.667-4.787 10.667-10.667z"></path>
|
||||
</symbol>
|
||||
<symbol id="iconSaving" viewBox="0 0 32 32">
|
||||
<path d="M20 13.333c0-0.733 0.6-1.333 1.333-1.333s1.333 0.6 1.333 1.333c0 0.733-0.6 1.333-1.333 1.333s-1.333-0.6-1.333-1.333zM10.667 12h6.667v-2.667h-6.667v2.667zM29.333 10v9.293l-3.76 1.253-2.24 7.453h-7.333v-2.667h-2.667v2.667h-7.333c0 0-3.333-11.28-3.333-15.333s3.28-7.333 7.333-7.333h6.667c1.213-1.613 3.147-2.667 5.333-2.667 1.107 0 2 0.893 2 2 0 0.28-0.053 0.533-0.16 0.773-0.187 0.453-0.347 0.973-0.427 1.533l3.027 3.027h2.893zM26.667 12.667h-1.333l-4.667-4.667c0-0.867 0.12-1.72 0.347-2.547-1.293 0.333-2.347 1.293-2.787 2.547h-8.227c-2.573 0-4.667 2.093-4.667 4.667 0 2.507 1.627 8.867 2.68 12.667h2.653v-2.667h8v2.667h2.68l2.067-6.867 3.253-1.093v-4.707z"></path>
|
||||
</symbol>`);
|
||||
|
||||
const topBarElement = this.addTopBar({
|
||||
icon: "iconFace",
|
||||
title: this.i18n.addTopBarIcon,
|
||||
position: "right",
|
||||
callback: () => {
|
||||
if (this.isMobile) {
|
||||
this.addMenu();
|
||||
} else {
|
||||
let rect = topBarElement.getBoundingClientRect();
|
||||
// 如果被隐藏,则使用更多按钮
|
||||
if (rect.width === 0) {
|
||||
rect = document.querySelector("#barMore").getBoundingClientRect();
|
||||
}
|
||||
if (rect.width === 0) {
|
||||
rect = document.querySelector("#barPlugins").getBoundingClientRect();
|
||||
}
|
||||
this.addMenu(rect);
|
||||
}
|
||||
loadIcons(this);
|
||||
//const id = Math.random().toString(36).substring(7);
|
||||
this.addTab({
|
||||
'type': "whiteboard",
|
||||
init() {
|
||||
createEditor(this);
|
||||
}
|
||||
});
|
||||
|
||||
const statusIconTemp = document.createElement("template");
|
||||
statusIconTemp.innerHTML = `<div class="toolbar__item ariaLabel" aria-label="Remove plugin-sample Data">
|
||||
<svg>
|
||||
<use xlink:href="#iconTrashcan"></use>
|
||||
</svg>
|
||||
</div>`;
|
||||
statusIconTemp.content.firstElementChild.addEventListener("click", () => {
|
||||
confirm("⚠️", this.i18n.confirmRemove.replace("${name}", this.name), () => {
|
||||
this.removeData(STORAGE_NAME).then(() => {
|
||||
this.data[STORAGE_NAME] = { readonlyText: "Readonly" };
|
||||
showMessage(`[${this.name}]: ${this.i18n.removedData}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
this.addStatusBar({
|
||||
element: statusIconTemp.content.firstElementChild as HTMLElement,
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
langKey: "showDialog",
|
||||
hotkey: "⇧⌘O",
|
||||
callback: () => {
|
||||
this.showDialog();
|
||||
},
|
||||
fileTreeCallback: (file: any) => {
|
||||
console.log(file, "fileTreeCallback");
|
||||
},
|
||||
editorCallback: (protyle: any) => {
|
||||
console.log(protyle, "editorCallback");
|
||||
},
|
||||
dockCallback: (element: HTMLElement) => {
|
||||
console.log(element, "dockCallback");
|
||||
},
|
||||
});
|
||||
this.addCommand({
|
||||
langKey: "getTab",
|
||||
hotkey: "⇧⌘M",
|
||||
globalCallback: () => {
|
||||
console.log(this.getOpenedTab());
|
||||
},
|
||||
});
|
||||
|
||||
this.addDock({
|
||||
config: {
|
||||
position: "LeftBottom",
|
||||
size: { width: 200, height: 0 },
|
||||
icon: "iconSaving",
|
||||
title: "Custom Dock",
|
||||
hotkey: "⌥⌘W",
|
||||
},
|
||||
data: {
|
||||
text: "This is my custom dock"
|
||||
},
|
||||
type: DOCK_TYPE,
|
||||
resize() {
|
||||
console.log(DOCK_TYPE + " resize");
|
||||
},
|
||||
update() {
|
||||
console.log(DOCK_TYPE + " update");
|
||||
},
|
||||
init: (dock) => {
|
||||
if (this.isMobile) {
|
||||
dock.element.innerHTML = `<div class="toolbar toolbar--border toolbar--dark">
|
||||
<svg class="toolbar__icon"><use xlink:href="#iconEmoji"></use></svg>
|
||||
<div class="toolbar__text">Custom Dock</div>
|
||||
</div>
|
||||
<div class="fn__flex-1 plugin-sample__custom-dock">
|
||||
${dock.data.text}
|
||||
</div>
|
||||
</div>`;
|
||||
} else {
|
||||
dock.element.innerHTML = `<div class="fn__flex-1 fn__flex-column">
|
||||
<div class="block__icons">
|
||||
<div class="block__logo">
|
||||
<svg class="block__logoicon"><use xlink:href="#iconEmoji"></use></svg>
|
||||
Custom Dock
|
||||
</div>
|
||||
<span class="fn__flex-1 fn__space"></span>
|
||||
<span data-type="min" class="block__icon b3-tooltips b3-tooltips__sw" aria-label="Min ${adaptHotkey("⌘W")}"><svg class="block__logoicon"><use xlink:href="#iconMin"></use></svg></span>
|
||||
</div>
|
||||
<div class="fn__flex-1 plugin-sample__custom-dock">
|
||||
${dock.data.text}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
console.log("destroy dock:", DOCK_TYPE);
|
||||
}
|
||||
});
|
||||
|
||||
this.settingUtils = new SettingUtils({
|
||||
plugin: this, name: STORAGE_NAME
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Input",
|
||||
value: "",
|
||||
type: "textinput",
|
||||
title: "Readonly text",
|
||||
description: "Input description",
|
||||
action: {
|
||||
// Called when focus is lost and content changes
|
||||
callback: () => {
|
||||
// Return data and save it in real time
|
||||
let value = this.settingUtils.takeAndSave("Input");
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "InputArea",
|
||||
value: "",
|
||||
type: "textarea",
|
||||
title: "Readonly text",
|
||||
description: "Input description",
|
||||
// Called when focus is lost and content changes
|
||||
action: {
|
||||
callback: () => {
|
||||
// Read data in real time
|
||||
let value = this.settingUtils.take("InputArea");
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Check",
|
||||
value: true,
|
||||
type: "checkbox",
|
||||
title: "Checkbox text",
|
||||
description: "Check description",
|
||||
action: {
|
||||
callback: () => {
|
||||
// Return data and save it in real time
|
||||
let value = !this.settingUtils.get("Check");
|
||||
this.settingUtils.set("Check", value);
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Select",
|
||||
value: 1,
|
||||
type: "select",
|
||||
title: "Select",
|
||||
description: "Select description",
|
||||
options: {
|
||||
1: "Option 1",
|
||||
2: "Option 2"
|
||||
},
|
||||
action: {
|
||||
callback: () => {
|
||||
// Read data in real time
|
||||
let value = this.settingUtils.take("Select");
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Slider",
|
||||
value: 50,
|
||||
type: "slider",
|
||||
title: "Slider text",
|
||||
description: "Slider description",
|
||||
direction: "column",
|
||||
slider: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
},
|
||||
action:{
|
||||
callback: () => {
|
||||
// Read data in real time
|
||||
let value = this.settingUtils.take("Slider");
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Btn",
|
||||
value: "",
|
||||
type: "button",
|
||||
title: "Button",
|
||||
description: "Button description",
|
||||
button: {
|
||||
label: "Button",
|
||||
callback: () => {
|
||||
showMessage("Button clicked");
|
||||
}
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Custom Element",
|
||||
value: "",
|
||||
type: "custom",
|
||||
direction: "row",
|
||||
title: "Custom Element",
|
||||
description: "Custom Element description",
|
||||
//Any custom element must offer the following methods
|
||||
createElement: (currentVal: any) => {
|
||||
let div = document.createElement('div');
|
||||
div.style.border = "1px solid var(--b3-theme-primary)";
|
||||
div.contentEditable = "true";
|
||||
div.textContent = currentVal;
|
||||
return div;
|
||||
},
|
||||
getEleVal: (ele: HTMLElement) => {
|
||||
return ele.textContent;
|
||||
},
|
||||
setEleVal: (ele: HTMLElement, val: any) => {
|
||||
ele.textContent = val;
|
||||
}
|
||||
});
|
||||
this.settingUtils.addItem({
|
||||
key: "Hint",
|
||||
value: "",
|
||||
type: "hint",
|
||||
title: this.i18n.hintTitle,
|
||||
description: this.i18n.hintDesc,
|
||||
});
|
||||
|
||||
try {
|
||||
this.settingUtils.load();
|
||||
} catch (error) {
|
||||
console.error("Error loading settings storage, probably empty config json:", error);
|
||||
}
|
||||
|
||||
|
||||
this.protyleSlash = [{
|
||||
filter: ["insert emoji 😊", "插入表情 😊", "crbqwx"],
|
||||
html: `<div class="b3-list-item__first"><span class="b3-list-item__text">${this.i18n.insertEmoji}</span><span class="b3-list-item__meta">😊</span></div>`,
|
||||
id: "insertEmoji",
|
||||
callback(protyle: Protyle) {
|
||||
protyle.insert("😊");
|
||||
id: "insert-drawing",
|
||||
filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"],
|
||||
html: getMenuHTML("iconDraw", this.i18n.insertDrawing),
|
||||
callback: (protyle: Protyle) => {
|
||||
const uid = generateSiyuanId();
|
||||
protyle.insert(getPreviewHTML(uid), true, false);
|
||||
openEditorTab(this, uid);
|
||||
}
|
||||
}];
|
||||
|
||||
this.protyleOptions = {
|
||||
toolbar: ["block-ref",
|
||||
"a",
|
||||
"|",
|
||||
"text",
|
||||
"strong",
|
||||
"em",
|
||||
"u",
|
||||
"s",
|
||||
"mark",
|
||||
"sup",
|
||||
"sub",
|
||||
"clear",
|
||||
"|",
|
||||
"code",
|
||||
"kbd",
|
||||
"tag",
|
||||
"inline-math",
|
||||
"inline-memo",
|
||||
"|",
|
||||
{
|
||||
name: "insert-smail-emoji",
|
||||
icon: "iconEmoji",
|
||||
hotkey: "⇧⌘I",
|
||||
tipPosition: "n",
|
||||
tip: this.i18n.insertEmoji,
|
||||
click(protyle: Protyle) {
|
||||
protyle.insert("😊");
|
||||
}
|
||||
}],
|
||||
};
|
||||
|
||||
console.log(this.i18n.helloPlugin);
|
||||
}
|
||||
|
||||
onLayoutReady() {
|
||||
// this.loadData(STORAGE_NAME);
|
||||
this.settingUtils.load();
|
||||
console.log(`frontend: ${getFrontend()}; backend: ${getBackend()}`);
|
||||
|
||||
console.log(
|
||||
"Official settings value calling example:\n" +
|
||||
this.settingUtils.get("InputArea") + "\n" +
|
||||
this.settingUtils.get("Slider") + "\n" +
|
||||
this.settingUtils.get("Select") + "\n"
|
||||
);
|
||||
|
||||
let tabDiv = document.createElement("div");
|
||||
new HelloExample({
|
||||
target: tabDiv,
|
||||
props: {
|
||||
app: this.app,
|
||||
}
|
||||
});
|
||||
this.customTab = this.addTab({
|
||||
type: TAB_TYPE,
|
||||
init() {
|
||||
this.element.appendChild(tabDiv);
|
||||
console.log(this.element);
|
||||
},
|
||||
beforeDestroy() {
|
||||
console.log("before destroy tab:", TAB_TYPE);
|
||||
},
|
||||
destroy() {
|
||||
console.log("destroy tab:", TAB_TYPE);
|
||||
}
|
||||
});
|
||||
// This function is automatically called when the layout is loaded.
|
||||
}
|
||||
|
||||
async onunload() {
|
||||
console.log(this.i18n.byePlugin);
|
||||
showMessage("Goodbye SiYuan Plugin");
|
||||
console.log("onunload");
|
||||
onunload() {
|
||||
// This function is automatically called when the plugin is disabled.
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
console.log("uninstall");
|
||||
// This function is automatically called when the plugin is uninstalled.
|
||||
}
|
||||
|
||||
async updateCards(options: ICardData) {
|
||||
options.cards.sort((a: ICard, b: ICard) => {
|
||||
if (a.blockID < b.blockID) {
|
||||
return -1;
|
||||
}
|
||||
if (a.blockID > b.blockID) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom setting pannel provided by svelte
|
||||
*/
|
||||
openDIYSetting(): void {
|
||||
let dialog = new Dialog({
|
||||
title: "SettingPannel",
|
||||
content: `<div id="SettingPanel" style="height: 100%;"></div>`,
|
||||
width: "800px",
|
||||
destroyCallback: (options) => {
|
||||
console.log("destroyCallback", options);
|
||||
//You'd better destroy the component when the dialog is closed
|
||||
pannel.$destroy();
|
||||
}
|
||||
});
|
||||
let pannel = new SettingExample({
|
||||
target: dialog.element.querySelector("#SettingPanel"),
|
||||
});
|
||||
}
|
||||
|
||||
private eventBusPaste(event: any) {
|
||||
// 如果需异步处理请调用 preventDefault, 否则会进行默认处理
|
||||
event.preventDefault();
|
||||
// 如果使用了 preventDefault,必须调用 resolve,否则程序会卡死
|
||||
event.detail.resolve({
|
||||
textPlain: event.detail.textPlain.trim(),
|
||||
});
|
||||
}
|
||||
|
||||
private eventBusLog({ detail }: any) {
|
||||
console.log(detail);
|
||||
}
|
||||
|
||||
private blockIconEvent({ detail }: any) {
|
||||
detail.menu.addItem({
|
||||
iconHTML: "",
|
||||
label: this.i18n.removeSpace,
|
||||
click: () => {
|
||||
const doOperations: IOperation[] = [];
|
||||
detail.blockElements.forEach((item: HTMLElement) => {
|
||||
const editElement = item.querySelector('[contenteditable="true"]');
|
||||
if (editElement) {
|
||||
editElement.textContent = editElement.textContent.replace(/ /g, "");
|
||||
doOperations.push({
|
||||
id: item.dataset.nodeId,
|
||||
data: item.outerHTML,
|
||||
action: "update"
|
||||
});
|
||||
}
|
||||
});
|
||||
detail.protyle.getInstance().transaction(doOperations);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private showDialog() {
|
||||
// let dialog = new Dialog({
|
||||
// title: `SiYuan ${Constants.SIYUAN_VERSION}`,
|
||||
// content: `<div id="helloPanel" class="b3-dialog__content"></div>`,
|
||||
// width: this.isMobile ? "92vw" : "720px",
|
||||
// destroyCallback() {
|
||||
// // hello.$destroy();
|
||||
// },
|
||||
// });
|
||||
// new HelloExample({
|
||||
// target: dialog.element.querySelector("#helloPanel"),
|
||||
// props: {
|
||||
// app: this.app,
|
||||
// }
|
||||
// });
|
||||
svelteDialog({
|
||||
title: `SiYuan ${Constants.SIYUAN_VERSION}`,
|
||||
width: this.isMobile ? "92vw" : "720px",
|
||||
constructor: (container: HTMLElement) => {
|
||||
return new HelloExample({
|
||||
target: container,
|
||||
props: {
|
||||
app: this.app,
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private addMenu(rect?: DOMRect) {
|
||||
const menu = new Menu("topBarSample", () => {
|
||||
console.log(this.i18n.byeMenu);
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconInfo",
|
||||
label: "Dialog(open help first)",
|
||||
accelerator: this.commands[0].customHotkey,
|
||||
click: () => {
|
||||
this.showDialog();
|
||||
}
|
||||
});
|
||||
if (!this.isMobile) {
|
||||
menu.addItem({
|
||||
icon: "iconFace",
|
||||
label: "Open Custom Tab",
|
||||
click: () => {
|
||||
const tab = openTab({
|
||||
app: this.app,
|
||||
custom: {
|
||||
icon: "iconFace",
|
||||
title: "Custom Tab",
|
||||
data: {
|
||||
text: "This is my custom tab",
|
||||
},
|
||||
id: this.name + TAB_TYPE
|
||||
},
|
||||
});
|
||||
console.log(tab);
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconImage",
|
||||
label: "Open Asset Tab(open help first)",
|
||||
click: () => {
|
||||
const tab = openTab({
|
||||
app: this.app,
|
||||
asset: {
|
||||
path: "assets/paragraph-20210512165953-ag1nib4.svg"
|
||||
}
|
||||
});
|
||||
console.log(tab);
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconFile",
|
||||
label: "Open Doc Tab(open help first)",
|
||||
click: async () => {
|
||||
const tab = await openTab({
|
||||
app: this.app,
|
||||
doc: {
|
||||
id: "20200812220555-lj3enxa",
|
||||
}
|
||||
});
|
||||
console.log(tab);
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconSearch",
|
||||
label: "Open Search Tab",
|
||||
click: () => {
|
||||
const tab = openTab({
|
||||
app: this.app,
|
||||
search: {
|
||||
k: "SiYuan"
|
||||
}
|
||||
});
|
||||
console.log(tab);
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconRiffCard",
|
||||
label: "Open Card Tab",
|
||||
click: () => {
|
||||
const tab = openTab({
|
||||
app: this.app,
|
||||
card: {
|
||||
type: "all"
|
||||
}
|
||||
});
|
||||
console.log(tab);
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconLayout",
|
||||
label: "Open Float Layer(open help first)",
|
||||
click: () => {
|
||||
this.addFloatLayer({
|
||||
ids: ["20210428212840-8rqwn5o", "20201225220955-l154bn4"],
|
||||
defIds: ["20230415111858-vgohvf3", "20200813131152-0wk5akh"],
|
||||
x: window.innerWidth - 768 - 120,
|
||||
y: 32
|
||||
});
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconOpenWindow",
|
||||
label: "Open Doc Window(open help first)",
|
||||
click: () => {
|
||||
openWindow({
|
||||
doc: {id: "20200812220555-lj3enxa"}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
menu.addItem({
|
||||
icon: "iconFile",
|
||||
label: "Open Doc(open help first)",
|
||||
click: () => {
|
||||
openMobileFileById(this.app, "20200812220555-lj3enxa");
|
||||
}
|
||||
});
|
||||
}
|
||||
menu.addItem({
|
||||
icon: "iconLock",
|
||||
label: "Lockscreen",
|
||||
click: () => {
|
||||
lockScreen(this.app);
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconScrollHoriz",
|
||||
label: "Event Bus",
|
||||
type: "submenu",
|
||||
submenu: [{
|
||||
icon: "iconSelect",
|
||||
label: "On ws-main",
|
||||
click: () => {
|
||||
this.eventBus.on("ws-main", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off ws-main",
|
||||
click: () => {
|
||||
this.eventBus.off("ws-main", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On click-blockicon",
|
||||
click: () => {
|
||||
this.eventBus.on("click-blockicon", this.blockIconEventBindThis);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off click-blockicon",
|
||||
click: () => {
|
||||
this.eventBus.off("click-blockicon", this.blockIconEventBindThis);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On click-pdf",
|
||||
click: () => {
|
||||
this.eventBus.on("click-pdf", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off click-pdf",
|
||||
click: () => {
|
||||
this.eventBus.off("click-pdf", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On click-editorcontent",
|
||||
click: () => {
|
||||
this.eventBus.on("click-editorcontent", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off click-editorcontent",
|
||||
click: () => {
|
||||
this.eventBus.off("click-editorcontent", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On click-editortitleicon",
|
||||
click: () => {
|
||||
this.eventBus.on("click-editortitleicon", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off click-editortitleicon",
|
||||
click: () => {
|
||||
this.eventBus.off("click-editortitleicon", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On click-flashcard-action",
|
||||
click: () => {
|
||||
this.eventBus.on("click-flashcard-action", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off click-flashcard-action",
|
||||
click: () => {
|
||||
this.eventBus.off("click-flashcard-action", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-noneditableblock",
|
||||
click: () => {
|
||||
this.eventBus.on("open-noneditableblock", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-noneditableblock",
|
||||
click: () => {
|
||||
this.eventBus.off("open-noneditableblock", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On loaded-protyle-static",
|
||||
click: () => {
|
||||
this.eventBus.on("loaded-protyle-static", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off loaded-protyle-static",
|
||||
click: () => {
|
||||
this.eventBus.off("loaded-protyle-static", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On loaded-protyle-dynamic",
|
||||
click: () => {
|
||||
this.eventBus.on("loaded-protyle-dynamic", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off loaded-protyle-dynamic",
|
||||
click: () => {
|
||||
this.eventBus.off("loaded-protyle-dynamic", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On switch-protyle",
|
||||
click: () => {
|
||||
this.eventBus.on("switch-protyle", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off switch-protyle",
|
||||
click: () => {
|
||||
this.eventBus.off("switch-protyle", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On destroy-protyle",
|
||||
click: () => {
|
||||
this.eventBus.on("destroy-protyle", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off destroy-protyle",
|
||||
click: () => {
|
||||
this.eventBus.off("destroy-protyle", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-doctree",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-doctree", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-doctree",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-doctree", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-blockref",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-blockref", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-blockref",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-blockref", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-fileannotationref",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-fileannotationref", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-fileannotationref",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-fileannotationref", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-tag",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-tag", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-tag",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-tag", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-link",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-link", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-link",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-link", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-image",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-image", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-image",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-image", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-av",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-av", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-av",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-av", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-content",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-content", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-content",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-content", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-breadcrumbmore",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-breadcrumbmore", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-breadcrumbmore",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-breadcrumbmore", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-menu-inbox",
|
||||
click: () => {
|
||||
this.eventBus.on("open-menu-inbox", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-menu-inbox",
|
||||
click: () => {
|
||||
this.eventBus.off("open-menu-inbox", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On input-search",
|
||||
click: () => {
|
||||
this.eventBus.on("input-search", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off input-search",
|
||||
click: () => {
|
||||
this.eventBus.off("input-search", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On paste",
|
||||
click: () => {
|
||||
this.eventBus.on("paste", this.eventBusPaste);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off paste",
|
||||
click: () => {
|
||||
this.eventBus.off("paste", this.eventBusPaste);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-siyuan-url-plugin",
|
||||
click: () => {
|
||||
this.eventBus.on("open-siyuan-url-plugin", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-siyuan-url-plugin",
|
||||
click: () => {
|
||||
this.eventBus.off("open-siyuan-url-plugin", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconSelect",
|
||||
label: "On open-siyuan-url-block",
|
||||
click: () => {
|
||||
this.eventBus.on("open-siyuan-url-block", this.eventBusLog);
|
||||
}
|
||||
}, {
|
||||
icon: "iconClose",
|
||||
label: "Off open-siyuan-url-block",
|
||||
click: () => {
|
||||
this.eventBus.off("open-siyuan-url-block", this.eventBusLog);
|
||||
}
|
||||
}]
|
||||
});
|
||||
menu.addSeparator();
|
||||
menu.addItem({
|
||||
icon: "iconSettings",
|
||||
label: "Official Setting Dialog",
|
||||
click: () => {
|
||||
this.openSetting();
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconSettings",
|
||||
label: "A custom setting dialog (by svelte)",
|
||||
click: () => {
|
||||
this.openDIYSetting();
|
||||
}
|
||||
});
|
||||
menu.addItem({
|
||||
icon: "iconSparkles",
|
||||
label: this.data[STORAGE_NAME].readonlyText || "Readonly",
|
||||
type: "readonly",
|
||||
});
|
||||
if (this.isMobile) {
|
||||
menu.fullscreen();
|
||||
} else {
|
||||
menu.open({
|
||||
x: rect.right,
|
||||
y: rect.bottom,
|
||||
isLeft: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { showMessage } from "siyuan";
|
||||
import SettingPanel from "./libs/components/setting-panel.svelte";
|
||||
|
||||
let groups: string[] = ["🌈 Group 1", "✨ Group 2"];
|
||||
let focusGroup = groups[0];
|
||||
|
||||
const group1Items: ISettingItem[] = [
|
||||
{
|
||||
type: 'checkbox',
|
||||
title: 'checkbox',
|
||||
description: 'checkbox',
|
||||
key: 'a',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
type: 'textinput',
|
||||
title: 'text',
|
||||
description: 'This is a text',
|
||||
key: 'b',
|
||||
value: 'This is a text',
|
||||
placeholder: 'placeholder'
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
title: 'textarea',
|
||||
description: 'This is a textarea',
|
||||
key: 'b2',
|
||||
value: 'This is a textarea',
|
||||
placeholder: 'placeholder',
|
||||
direction: 'row'
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
title: 'select',
|
||||
description: 'select',
|
||||
key: 'c',
|
||||
value: 'x',
|
||||
options: {
|
||||
x: 'x',
|
||||
y: 'y',
|
||||
z: 'z'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const group2Items: ISettingItem[] = [
|
||||
{
|
||||
type: 'button',
|
||||
title: 'button',
|
||||
description: 'This is a button',
|
||||
key: 'e',
|
||||
value: 'Click Button',
|
||||
button: {
|
||||
label: 'Click Me',
|
||||
callback: () => {
|
||||
showMessage('Hello, world!');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'slider',
|
||||
title: 'slider',
|
||||
description: 'slider',
|
||||
key: 'd',
|
||||
value: 50,
|
||||
slider: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
/********** Events **********/
|
||||
interface ChangeEvent {
|
||||
group: string;
|
||||
key: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
const onChanged = ({ detail }: CustomEvent<ChangeEvent>) => {
|
||||
if (detail.group === groups[0]) {
|
||||
// setting.set(detail.key, detail.value);
|
||||
//Please add your code here
|
||||
//Udpate the plugins setting data, don't forget to call plugin.save() for data persistence
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="fn__flex-1 fn__flex config__panel">
|
||||
<ul class="b3-tab-bar b3-list b3-list--background">
|
||||
{#each groups as group}
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<li
|
||||
data-name="editor"
|
||||
class:b3-list-item--focus={group === focusGroup}
|
||||
class="b3-list-item"
|
||||
on:click={() => {
|
||||
focusGroup = group;
|
||||
}}
|
||||
on:keydown={() => {}}
|
||||
>
|
||||
<span class="b3-list-item__text">{group}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="config__tab-wrap">
|
||||
<SettingPanel
|
||||
group={groups[0]}
|
||||
settingItems={group1Items}
|
||||
display={focusGroup === groups[0]}
|
||||
on:changed={onChanged}
|
||||
on:click={({ detail }) => { console.debug("Click:", detail.key); }}
|
||||
>
|
||||
<div class="fn__flex b3-label">
|
||||
💡 This is our default settings.
|
||||
</div>
|
||||
</SettingPanel>
|
||||
<SettingPanel
|
||||
group={groups[1]}
|
||||
settingItems={group2Items}
|
||||
display={focusGroup === groups[1]}
|
||||
on:changed={onChanged}
|
||||
on:click={({ detail }) => { console.debug("Click:", detail.key); }}
|
||||
>
|
||||
</SettingPanel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.config__panel {
|
||||
height: 100%;
|
||||
}
|
||||
.config__panel > ul > li {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue