feat: 新建Excel

This commit is contained in:
leijun 2023-08-21 17:08:47 +08:00
parent 2c3dd57786
commit 176131376a
11 changed files with 534 additions and 108 deletions

View file

@ -2,7 +2,7 @@ import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { lessLoader } from "esbuild-plugin-less";
import svg from 'esbuild-plugin-svg';
import { svgo } from "@hyrious/esbuild-plugin-svgo";
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
@ -40,7 +40,8 @@ const context = await esbuild.context({
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
plugins: [lessLoader(), svg()],
plugins: [lessLoader(), svgo()],
loader: { '.svg': 'dataurl' },
});
if (prod) {

File diff suppressed because one or more lines are too long

135
main.ts
View file

@ -1,46 +1,139 @@
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, WorkspaceLeaf } from 'obsidian';
import { TFile, Plugin, WorkspaceLeaf, normalizePath } from "obsidian";
import { ExcelSettings, DEFAULT_SETTINGS } from "./src/utils/Settings";
import {
emulateCTRLClickForLinks,
linkClickModifierType,
PaneTarget,
} from "./src/utils/ModifierkeyHelper";
import { ExcelView, VIEW_TYPE_EXCEL } from "./src/ExcelView";
import {
checkAndCreateFolder,
getNewUniqueFilepath,
getExcelFilename,
} from "./src/utils/FileUtils";
import { ExcelView, VIEW_TYPE_EXCEL } from './src/ExcelView';
import { getNewOrAdjacentLeaf } from "./src/utils/ObsidianUtils";
declare const PLUGIN_VERSION: string;
export default class ExcelPlugin extends Plugin {
public settings: ExcelSettings;
async onload() {
this.registerView(VIEW_TYPE_EXCEL, (leaf: WorkspaceLeaf) => new ExcelView(leaf, this));
this.registerView(
VIEW_TYPE_EXCEL,
(leaf: WorkspaceLeaf) => new ExcelView(leaf, this)
);
this.registerExtensions(["xlsx"], VIEW_TYPE_EXCEL);
// This creates an icon in the left ribbon.
const ribbonIconEl = this.addRibbonIcon('table', 'Excel', (evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice('This is a notice!');
});
const ribbonIconEl = this.addRibbonIcon(
"table",
"Excel",
(e: MouseEvent) => {
// Called when the user clicks the icon.
this.createAndOpenExcel(
getExcelFilename(this.settings)
);
}
);
// Perform additional things with the ribbon
ribbonIconEl.addClass('my-plugin-ribbon-class');
ribbonIconEl.addClass("my-plugin-ribbon-class");
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status Bar Text');
statusBarItemEl.setText("Status Bar Text");
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
// Using this function will automatically remove the event listener when this plugin is disabled.
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
console.log('click', evt);
this.registerDomEvent(document, "click", (evt: MouseEvent) => {
console.log("click", evt);
});
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
this.registerInterval(
window.setInterval(() => console.log("setInterval"), 5 * 60 * 1000)
);
}
onunload() {
onunload() {}
async loadSettings() {
this.settings = Object.assign(
{},
DEFAULT_SETTINGS,
await this.loadData()
);
}
// async loadSettings() {
// this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
// }
async saveSettings() {
await this.saveData(this.settings);
}
// async saveSettings() {
// await this.saveData(this.settings);
// }
public async createExcel(
filename: string,
foldername?: string,
initData?: string
): Promise<TFile> {
// const folderpath = normalizePath(
// foldername ? foldername : "Excel"
// );
// await checkAndCreateFolder(folderpath); //create folder if it does not exist
const fname = normalizePath(filename);
const file = await this.app.vault.create(fname, initData ?? "{}");
return file;
}
public async createAndOpenExcel(
filename: string,
foldername?: string,
initData?: string
): Promise<string> {
const file = await this.createExcel(filename, foldername, initData);
this.openExcel(file, "new-pane", true, undefined);
return file.path;
}
public openExcel(
excelFile: TFile,
location: PaneTarget,
active: boolean = false,
subpath?: string
) {
if (location === "md-properties") {
location = "new-tab";
}
var leaf: WorkspaceLeaf;
if (location === "popout-window") {
leaf = app.workspace.openPopoutLeaf();
}
if (location === "new-tab") {
leaf = app.workspace.getLeaf("tab");
}
if (!leaf) {
leaf = this.app.workspace.getLeaf(false);
if (
leaf.view.getViewType() !== "empty" &&
location === "new-pane"
) {
leaf = app.workspace.getMostRecentLeaf();
}
}
leaf.openFile(
excelFile,
!subpath || subpath === ""
? { active }
: { active, eState: { subpath } }
).then(() => {});
}
public isExcelFile(f: TFile) {
if (!f) return false;
if (f.extension === "xlsx") {
return true;
}
return false
}
}

141
package-lock.json generated
View file

@ -158,6 +158,15 @@
"dev": true,
"optional": true
},
"@hyrious/esbuild-plugin-svgo": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@hyrious/esbuild-plugin-svgo/-/esbuild-plugin-svgo-0.2.0.tgz",
"integrity": "sha512-wg7OTGS/eOsV/+EXsFnnfyhdjGMsLtMlgbB0fdV8fM/5BZQ0VwJ561+IwzX9xEcTrFe8UBIUAH4xb4Xf+0j+dg==",
"dev": true,
"requires": {
"svgo": "^3.0.2"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -446,26 +455,26 @@
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
},
"css-select": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
"integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dev": true,
"requires": {
"boolbase": "^1.0.0",
"css-what": "^6.0.1",
"domhandler": "^4.3.1",
"domutils": "^2.8.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
}
},
"css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"requires": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
"mdn-data": "2.0.30",
"source-map-js": "^1.0.1"
}
},
"css-what": {
@ -475,12 +484,30 @@
"dev": true
},
"csso": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
"integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
"dev": true,
"requires": {
"css-tree": "^1.1.2"
"css-tree": "~2.2.0"
},
"dependencies": {
"css-tree": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
"dev": true,
"requires": {
"mdn-data": "2.0.28",
"source-map-js": "^1.0.1"
}
},
"mdn-data": {
"version": "2.0.28",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
"dev": true
}
}
},
"debug": {
@ -502,14 +529,14 @@
}
},
"dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
}
},
"domelementtype": {
@ -519,23 +546,23 @@
"dev": true
},
"domhandler": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dev": true,
"requires": {
"domelementtype": "^2.2.0"
"domelementtype": "^2.3.0"
}
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
"dev": true,
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
}
},
"encoding": {
@ -557,9 +584,9 @@
}
},
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true
},
"errno": {
@ -612,15 +639,6 @@
"less": "^4.2.0"
}
},
"esbuild-plugin-svg": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/esbuild-plugin-svg/-/esbuild-plugin-svg-0.1.0.tgz",
"integrity": "sha512-/9ZhvIpl+Ovl6glVK3BedvIwrOwSQnECw4Fy6ZwysWib3Ns7UkX6WNGjMOWtvQ1Cnm0uc7sptiKGm0BthKCAJA==",
"dev": true,
"requires": {
"svgo": "^2.2.2"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -853,11 +871,6 @@
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true
},
"jquery": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz",
"integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ=="
},
"less": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
@ -911,9 +924,9 @@
}
},
"mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
},
"merge2": {
@ -1211,12 +1224,13 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
"dev": true,
"optional": true
},
"stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
},
"string-width": {
@ -1257,18 +1271,17 @@
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
},
"svgo": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
"integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz",
"integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==",
"dev": true,
"requires": {
"@trysound/sax": "0.2.0",
"commander": "^7.2.0",
"css-select": "^4.1.3",
"css-tree": "^1.1.3",
"csso": "^4.2.0",
"picocolors": "^1.0.0",
"stable": "^0.1.8"
"css-select": "^5.1.0",
"css-tree": "^2.2.1",
"csso": "^5.0.5",
"picocolors": "^1.0.0"
}
},
"through": {

View file

@ -12,13 +12,13 @@
"author": "",
"license": "MIT",
"devDependencies": {
"@hyrious/esbuild-plugin-svgo": "^0.2.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"esbuild-plugin-less": "^1.2.4",
"esbuild-plugin-svg": "^0.1.0",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"

View file

@ -30,22 +30,22 @@ export class ExcelView extends TextFileView {
},
});
console.log(this.ownerWindow.document.documentElement.clientWidth);
const jsonData = JSON.parse(this.data || "{}") || {}
const jsonData = JSON.parse(this.data || "{}") || {};
//@ts-ignore
this.sheet = new Spreadsheet("#x-spreadsheet", {
view: {
height: () => this.contentEl.clientHeight,
width: () => this.contentEl.clientWidth,
},
},
})
.loadData(jsonData) // load data
.change(data => {
// save data to db
this.data = JSON.stringify(data);
});
.loadData(jsonData) // load data
.change((data) => {
// save data to db
this.data = JSON.stringify(data);
});
this.sheet.validate()
this.sheet.validate();
}
onload(): void {

124
src/utils/FileUtils.ts Normal file
View file

@ -0,0 +1,124 @@
import { normalizePath, Notice, TAbstractFile, TFile, TFolder, Vault } from "obsidian";
import { ExcelSettings } from "./Settings";
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
* @param filepath
*/
export function splitFolderAndFilename(filepath: string): {
folderpath: string;
filename: string;
basename: string;
} {
const lastIndex = filepath.lastIndexOf("/");
const filename = lastIndex == -1 ? filepath : filepath.substring(lastIndex + 1);
return {
folderpath: normalizePath(filepath.substring(0, lastIndex)),
filename,
basename: filename.replace(/\.[^/.]+$/, ""),
};
}
/**
* Download data as file from Obsidian, to store on local device
* @param encoding
* @param data
* @param filename
*/
export const download = (encoding: string, data: any, filename: string) => {
const element = document.createElement("a");
element.setAttribute("href", (encoding ? `${encoding},` : "") + data);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/**
* Generates the image filename based on the excalidraw filename
* @param excalidrawPath - Full filepath of ExclidrawFile
* @param newExtension - extension of IMG file in ".extension" format
* @returns
*/
/*export function getIMGPathFromExcalidrawFile(
excalidrawPath: string,
newExtension: string,
): string {
const isLegacyFile: boolean = excalidrawPath.endsWith(".excalidraw");
const replaceExtension: string = isLegacyFile ? ".excalidraw" : ".md";
return (
excalidrawPath.substring(0, excalidrawPath.lastIndexOf(replaceExtension)) +
newExtension
);
}*/
/**
* Generates the image filename based on the excalidraw filename
* @param path - path to the excalidraw file
* @param extension - extension without the preceeding "."
* @returns
*/
export function getIMGFilename(path: string, extension: string): string {
return `${path.substring(0, path.lastIndexOf("."))}.${extension}`;
}
/**
* Create new file, if file already exists find first unique filename by adding a number to the end of the filename
* @param filename
* @param folderpath
* @returns
*/
export function getNewUniqueFilepath(
vault: Vault,
filename: string,
folderpath: string,
): string {
let fname = normalizePath(`${folderpath}/${filename}`);
let file: TAbstractFile = vault.getAbstractFileByPath(fname);
let i = 0;
const extension = filename.endsWith(".xlsx.md")
? ".xlsx.md"
: filename.slice(filename.lastIndexOf("."));
while (file) {
fname = normalizePath(
`${folderpath}/${filename.slice(
0,
filename.lastIndexOf(extension),
)}_${i}${extension}`,
);
i++;
file = vault.getAbstractFileByPath(fname);
}
return fname;
}
export function getExcelFilename(settings: ExcelSettings): string {
return (
"Excel " +
window.moment().format('YYYY-MM-DD HH.mm.ss') +
".xlsx"
);
}
/**
* Open or create a folderpath if it does not exist
* @param folderpath
*/
export async function checkAndCreateFolder(folderpath: string) {
const vault = app.vault;
folderpath = normalizePath(folderpath);
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/658
//@ts-ignore
const folder = vault.getAbstractFileByPathInsensitive(folderpath);
if (folder && folder instanceof TFolder) {
return;
}
if (folder && folder instanceof TFile) {
new Notice(`The folder cannot be created because it already exists as a file: ${folderpath}.`)
}
await vault.createFolder(folderpath);
}

View file

@ -0,0 +1,53 @@
export type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
export type ModifierKeys = {shiftKey:boolean, ctrlKey: boolean, metaKey: boolean, altKey: boolean};
export type KeyEvent = PointerEvent | MouseEvent | KeyboardEvent | ModifierKeys;
export type DeviceType = {
isDesktop: boolean,
isPhone: boolean,
isTablet: boolean,
isMobile: boolean,
isLinux: boolean,
isMacOS: boolean,
isWindows: boolean,
isIOS: boolean,
isAndroid: boolean
};
export const DEVICE: DeviceType = {
isDesktop: !document.body.hasClass("is-tablet") && !document.body.hasClass("is-mobile"),
isPhone: document.body.hasClass("is-phone"),
isTablet: document.body.hasClass("is-tablet"),
isMobile: document.body.hasClass("is-mobile"), //running Obsidian Mobile, need to also check isTablet
isLinux: document.body.hasClass("mod-linux") && ! document.body.hasClass("is-android"),
isMacOS: document.body.hasClass("mod-macos") && ! document.body.hasClass("is-ios"),
isWindows: document.body.hasClass("mod-windows"),
isIOS: document.body.hasClass("is-ios"),
isAndroid: document.body.hasClass("is-android")
};
export const isCTRL = (e:KeyEvent) => DEVICE.isIOS || DEVICE.isMacOS ? e.metaKey : e.ctrlKey;
export const isALT = (e:KeyEvent) => e.altKey;
export const isMETA = (e:KeyEvent) => DEVICE.isIOS || DEVICE.isMacOS ? e.ctrlKey : e.metaKey;
export const isSHIFT = (e:KeyEvent) => e.shiftKey;
export const mdPropModifier = (ev: KeyEvent): boolean => !isSHIFT(ev) && isCTRL(ev) && !isALT(ev) && isMETA(ev);
export const emulateCTRLClickForLinks = (e: KeyEvent) => {
return {
shiftKey: e.shiftKey,
ctrlKey: e.ctrlKey || !(DEVICE.isIOS || DEVICE.isMacOS),
metaKey: e.metaKey || (DEVICE.isIOS || DEVICE.isMacOS),
altKey: e.altKey
}
}
export const linkClickModifierType = (ev: KeyEvent):PaneTarget => {
if(isCTRL(ev) && !isALT(ev) && isSHIFT(ev) && !isMETA(ev)) return "active-pane";
if(isCTRL(ev) && !isALT(ev) && !isSHIFT(ev) && !isMETA(ev)) return "new-tab";
if(isCTRL(ev) && isALT(ev) && !isSHIFT(ev) && !isMETA(ev)) return "new-pane";
if(DEVICE.isDesktop && isCTRL(ev) && isALT(ev) && isSHIFT(ev) && !isMETA(ev) ) return "popout-window";
if(isCTRL(ev) && isALT(ev) && isSHIFT(ev) && !isMETA(ev)) return "new-tab";
if(mdPropModifier(ev)) return "md-properties";
return "active-pane";
}

126
src/utils/ObsidianUtils.ts Normal file
View file

@ -0,0 +1,126 @@
import ExcelPlugin from "main";
import { WorkspaceLeaf } from "obsidian";
const getLeafLoc = (
leaf: WorkspaceLeaf
): ["main" | "popout" | "left" | "right" | "hover", any] => {
//@ts-ignore
const leafId = leaf.id;
const layout = app.workspace.getLayout();
const getLeaves = (l: any) =>
l.children
.filter((c: any) => c.type !== "leaf")
.map((c: any) => getLeaves(c))
.flat()
.concat(
l.children
.filter((c: any) => c.type === "leaf")
.map((c: any) => c.id)
);
const mainLeavesIds = getLeaves(layout.main);
return [
layout.main && mainLeavesIds.contains(leafId)
? "main"
: layout.floating && getLeaves(layout.floating).contains(leafId)
? "popout"
: layout.left && getLeaves(layout.left).contains(leafId)
? "left"
: layout.right && getLeaves(layout.right).contains(leafId)
? "right"
: "hover",
mainLeavesIds,
];
};
/*
| Setting | Originating Leaf |
| | Main Workspace | Hover Editor | Popout Window |
| ----------------------- | -------------------------------- | -------------------------------------- | -------------------------------- |
| InMain && InAdjacent | 1.1 Reuse Leaf in Main Workspace | 1.1 Reuse Leaf in Main Workspace | 1.1 Reuse Leaf in Main Workspace |
| InMain && !InAdjacent | 1.2 New Leaf in Main Workspace | 1.2 New Leaf in Main Workspace | 1.2 New Leaf in Main Workspace |
| !InMain && InAdjacent | 1.1 Reuse Leaf in Main Workspace | 3 Reuse Leaf in Current Hover Editor | 4 Reuse Leaf in Current Popout |
| !InMain && !InAdjacent | 1.2 New Leaf in Main Workspace | 2 New Leaf in Current Hover Editor | 2 New Leaf in Current Popout |
*/
export const getNewOrAdjacentLeaf = (
plugin: ExcelPlugin,
leaf: WorkspaceLeaf
): WorkspaceLeaf | null => {
const [leafLoc, mainLeavesIds] = getLeafLoc(leaf);
const getMostRecentOrAvailableLeafInMainWorkspace = (
inDifferentTabGroup?: boolean
): WorkspaceLeaf => {
let mainLeaf = app.workspace.getMostRecentLeaf();
if (
mainLeaf &&
mainLeaf !== leaf &&
mainLeaf.view?.containerEl.ownerDocument === document
) {
//Found a leaf in the main workspace that is not the originating leaf
return mainLeaf;
}
//Iterate all leaves in the main workspace and find the first one that is not the originating leaf
mainLeaf = null;
mainLeavesIds.forEach((id: any) => {
const l = app.workspace.getLeafById(id);
if (
mainLeaf ||
!l.view?.navigation ||
leaf === l ||
//@ts-ignore
(inDifferentTabGroup && l?.parent === leaf?.parent)
)
return;
mainLeaf = l;
});
return mainLeaf;
};
//1 - In Main Workspace
if (["main", "left", "right"].contains(leafLoc)
) {
//1.2 - Reuse leaf if it is adjacent
const ml = getMostRecentOrAvailableLeafInMainWorkspace(true);
return ml ?? app.workspace.createLeafBySplit(leaf); //app.workspace.getLeaf(true);
}
//3
if (leafLoc === "hover") {
const leaves = new Set<WorkspaceLeaf>();
app.workspace.iterateAllLeaves((l) => {
//@ts-ignore
if (
l !== leaf &&
leaf.containerEl.parentElement === l.containerEl.parentElement
)
leaves.add(l);
});
if (leaves.size === 0) {
return plugin.app.workspace.createLeafBySplit(leaf);
}
return Array.from(leaves)[0];
}
//4
if (leafLoc === "popout") {
const popoutLeaves = new Set<WorkspaceLeaf>();
app.workspace.iterateAllLeaves((l) => {
if (
l !== leaf &&
l.view.navigation &&
l.view.containerEl.ownerDocument ===
leaf.view.containerEl.ownerDocument
) {
popoutLeaves.add(l);
}
});
if (popoutLeaves.size === 0) {
return app.workspace.createLeafBySplit(leaf);
}
return Array.from(popoutLeaves)[0];
}
return plugin.app.workspace.createLeafBySplit(leaf);
};

15
src/utils/Settings.ts Normal file
View file

@ -0,0 +1,15 @@
export interface ExcelSettings {
folder: string;
excelFilenamePrefix: String,
excelEmbedPrefixWithFilename: true,
excelFilnameEmbedPostfix: String,
excelFilenameDateTime: String,
}
export const DEFAULT_SETTINGS: ExcelSettings = {
folder: "Excel",
excelFilenamePrefix: "Excel ",
excelEmbedPrefixWithFilename: true,
excelFilnameEmbedPostfix: " ",
excelFilenameDateTime: "YYYY-MM-DD HH.mm.ss",
};

File diff suppressed because one or more lines are too long