Skip to content

Commit

Permalink
Allow exporting and importing configs
Browse files Browse the repository at this point in the history
  • Loading branch information
mgmeyers committed Aug 6, 2021
1 parent c39fbbf commit fbfed7f
Show file tree
Hide file tree
Showing 6 changed files with 579 additions and 88 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-style-settings",
"name": "Style Settings",
"version": "0.2.7",
"version": "0.3.0",
"minAppVersion": "0.11.5",
"description": "Offers controls for adjusting theme, plugin, and snippet CSS variables.",
"author": "mgmeyers",
Expand Down
244 changes: 229 additions & 15 deletions src/SettingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ import {
AltFormatList,
} from "./settingHandlers";
import chroma from "chroma-js";
import {
App,
ButtonComponent,
Modal,
Setting,
TextAreaComponent,
} from "obsidian";

type VariableKV = Array<{ key: string; value: string }>;

Expand Down Expand Up @@ -252,35 +259,35 @@ export class CSSSettingsManager {
}

initClasses() {
Object.keys(this.config).forEach(section => {
Object.keys(this.config).forEach((section) => {
const config = this.config[section];

Object.keys(config).forEach(settingId => {
Object.keys(config).forEach((settingId) => {
const setting = config[settingId];

if (setting.type === 'class-toggle') {
if (setting.type === "class-toggle") {
if (this.getSetting(section, settingId)) {
document.body.classList.add(setting.id);
}
}
})
})
});
});
}

removeClasses() {
Object.keys(this.config).forEach(section => {
Object.keys(this.config).forEach((section) => {
const config = this.config[section];

Object.keys(config).forEach(settingId => {
Object.keys(config).forEach((settingId) => {
const setting = config[settingId];

if (setting.type === 'class-toggle') {
if (setting.type === "class-toggle") {
if (this.getSetting(section, settingId)) {
document.body.classList.remove(setting.id);
}
}
})
})
});
});
}

setCSSVariables() {
Expand Down Expand Up @@ -344,15 +351,32 @@ export class CSSSettingsManager {
return this.settings[`${sectionId}@@${settingId}`];
}

getSettings(sectionId: string, ids: string[]) {
return ids.reduce<Record<string, SettingValue>>((settings, id) => {
const fullId = `${sectionId}@@${id}`;

if (this.settings[fullId]) {
settings[fullId] = this.settings[fullId];
}

return settings;
}, {});
}

setSetting(sectionId: string, settingId: string, value: SettingValue) {
this.settings[`${sectionId}@@${settingId}`] = value;
this.save();
}

clearSetting(
sectionId: string,
settingId: string
) {
setSettings(settings: Record<string, SettingValue>) {
Object.keys(settings).forEach((id) => {
this.settings[id] = settings[id];
});

return this.save();
}

clearSetting(sectionId: string, settingId: string) {
delete this.settings[`${sectionId}@@${settingId}`];
this.save();
}
Expand All @@ -361,9 +385,199 @@ export class CSSSettingsManager {
Object.keys(this.settings).forEach((key) => {
const [section] = key.split("@@");
if (section === sectionId) {
delete this.settings[key]
delete this.settings[key];
}
});
this.save();
}

export(section: string, config: Record<string, SettingValue>) {
new ExportModal(this.plugin.app, this.plugin, section, config).open();
}

import() {
new ImportModal(this.plugin.app, this.plugin).open();
}
}

export class ExportModal extends Modal {
plugin: CSSSettingsPlugin;
section: string;
config: Record<string, SettingValue>;

constructor(
app: App,
plugin: CSSSettingsPlugin,
section: string,
config: Record<string, SettingValue>
) {
super(app);
this.plugin = plugin;
this.config = config;
this.section = section;
}

onOpen() {
let { contentEl, modalEl } = this;

modalEl.addClass("modal-style-settings");

new Setting(contentEl)
.setName(`Export settings for: ${this.section}`)
.then((setting) => {
const output = JSON.stringify(this.config, null, 2);

// Build a copy to clipboard link
setting.controlEl.createEl(
"a",
{
cls: "style-settings-copy",
text: "Copy to clipboard",
href: "#",
},
(copyButton) => {
new TextAreaComponent(contentEl)
.setValue(output)
.then((textarea) => {
copyButton.addEventListener("click", (e) => {
e.preventDefault();

// Select the textarea contents and copy them to the clipboard
textarea.inputEl.select();
textarea.inputEl.setSelectionRange(0, 99999);
document.execCommand("copy");

copyButton.addClass("success");

setTimeout(() => {
// If the button is still in the dom, remove the success class
if (copyButton.parentNode) {
copyButton.removeClass("success");
}
}, 2000);
});
});
}
);

// Build a download link
setting.controlEl.createEl("a", {
cls: "style-settings-download",
text: "Download",
attr: {
download: "style-settings.json",
href: `data:application/json;charset=utf-8,${encodeURIComponent(
output
)}`,
},
});
});
}

onClose() {
let { contentEl } = this;
contentEl.empty();
}
}

export class ImportModal extends Modal {
plugin: CSSSettingsPlugin;

constructor(app: App, plugin: CSSSettingsPlugin) {
super(app);
this.plugin = plugin;
}

onOpen() {
let { contentEl, modalEl } = this;

modalEl.addClass("modal-style-settings");

new Setting(contentEl)
.setName("Import style setting")
.setDesc("Import an entire or partial configuration. Warning: this may override existing settings");

new Setting(contentEl).then((setting) => {
// Build an error message container
const errorSpan = createSpan({
cls: "style-settings-import-error",
text: "Error importing config",
});

setting.nameEl.appendChild(errorSpan);

// Attempt to parse the imported data and close if successful
const importAndClose = async (str: string) => {
if (str) {
try {
const importedSettings = JSON.parse(str) as Record<
string,
SettingValue
>;

await this.plugin.settingsManager.setSettings(importedSettings);

this.plugin.settingsTab.display();
this.close();
} catch (e) {
errorSpan.addClass("active");
errorSpan.setText(`Error importing style settings: ${e}`);
}
} else {
errorSpan.addClass("active");
errorSpan.setText(`Error importing style settings: config is empty`);
}
};

// Build a file input
setting.controlEl.createEl(
"input",
{
cls: "style-settings-import-input",
attr: {
id: "style-settings-import-input",
name: "style-settings-import-input",
type: "file",
accept: ".json",
},
},
(importInput) => {
// Set up a FileReader so we can parse the file contents
importInput.addEventListener("change", (e) => {
const reader = new FileReader();

reader.onload = async (e: ProgressEvent<FileReader>) => {
await importAndClose(e.target.result.toString().trim());
};

reader.readAsText((e.target as HTMLInputElement).files[0]);
});
}
);

// Build a label we will style as a link
setting.controlEl.createEl("label", {
cls: "style-settings-import-label",
text: "Import from file",
attr: {
for: "style-settings-import-input",
},
});

new TextAreaComponent(contentEl)
.setPlaceholder("Paste config here...")
.then((ta) => {
new ButtonComponent(contentEl)
.setButtonText("Save")
.onClick(async () => {
await importAndClose(ta.getValue().trim());
});
});
});
}

onClose() {
let { contentEl } = this;
contentEl.empty();
}
}
41 changes: 38 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, Plugin, PluginSettingTab } from "obsidian";
import { App, Plugin, PluginSettingTab, Setting } from "obsidian";
import { CSSSettingsManager } from "./SettingsManager";
import {
CleanupFunction,
Expand Down Expand Up @@ -159,8 +159,7 @@ class CSSSettingsTab extends PluginSettingTab {
);
frag.createEl("a", {
text: "Click here for details and examples.",
href:
"https://github.com/mgmeyers/obsidian-style-settings#obsidian-style-settings-plugin",
href: "https://github.com/mgmeyers/obsidian-style-settings#obsidian-style-settings-plugin",
});
})
);
Expand All @@ -179,6 +178,41 @@ class CSSSettingsTab extends PluginSettingTab {
return this.displayEmpty();
}

new Setting(containerEl)
.then((setting) => {
// Build and import link to open the import modal
setting.controlEl.createEl(
"a",
{
cls: "icon-swapper-import",
text: "Import",
href: "#",
},
(el) => {
el.addEventListener("click", (e) => {
e.preventDefault();
this.plugin.settingsManager.import();
});
}
);

// Build and export link to open the export modal
setting.controlEl.createEl(
"a",
{
cls: "icon-swapper-export",
text: "Export",
href: "#",
},
(el) => {
el.addEventListener("click", (e) => {
e.preventDefault();
this.plugin.settingsManager.export("All settings", this.plugin.settingsManager.settings);
});
}
);
})

const cleanupFns: CleanupFunction[] = [];

settings.forEach((s) => {
Expand All @@ -200,6 +234,7 @@ class CSSSettingsTab extends PluginSettingTab {
const cleanup = createSettings({
containerEl,
sectionId: s.id,
sectionName: s.name,
settings: options,
settingsManager: plugin.settingsManager,
});
Expand Down
Loading

0 comments on commit fbfed7f

Please sign in to comment.