Skip to content

Commit

Permalink
Merge pull request #15 from OfficerHalf/manage-modal
Browse files Browse the repository at this point in the history
Manage modal
  • Loading branch information
nathonius committed Nov 13, 2021
2 parents 9416579 + 6a200f5 commit 831f6de
Show file tree
Hide file tree
Showing 13 changed files with 431 additions and 104 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# 1.4.0

## Features

- Revamp settings. Now you can update a path with a new scope, new folder, and add or remove classes.
- Configured paths can be re-ordered by dragging.
- Use icons to show the scope of a path.

## Fixes

- Ensure the plugin works for both linked and unlinked panes.

# 1.3.0

## Features
Expand Down
25 changes: 24 additions & 1 deletion esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import esbuild from 'esbuild';
import process from 'process';
import { join } from 'path';
import { copyFileSync } from 'fs';
import { sassPlugin } from 'esbuild-sass-plugin';

const banner = `/*
Expand All @@ -9,6 +11,22 @@ if you want to view the source visit the plugins github repository
`;

const prod = process.argv[2] === 'production';
const vaultPath = process.argv[3];

function devCopy(error) {
if (vaultPath && !error) {
try {
const copyPath = join(vaultPath, '.obsidian/plugins/auto-class');
process.stdout.write(`Copying to ${copyPath}...`);
copyFileSync('main.js', join(vaultPath, '.obsidian/plugins/auto-class/main.js'));
copyFileSync('manifest.json', join(vaultPath, '.obsidian/plugins/auto-class/manifest.json'));
copyFileSync('styles.css', join(vaultPath, '.obsidian/plugins/auto-class/styles.css'));
process.stdout.write('Done.\n');
} catch {
process.stdout.write('\n****COULD NOT COPY FILES****\n');
}
}
}

esbuild
.build({
Expand All @@ -21,12 +39,17 @@ esbuild
format: 'cjs',
outdir: '.',
outbase: 'src',
watch: !prod,
watch: !prod ? { onRebuild: devCopy } : false,
target: 'es2016',
logLevel: 'info',
sourcemap: prod ? false : 'inline',
minify: prod,
treeShaking: true,
plugins: [sassPlugin({ cache: !prod })]
})
.then(() => {
if (!prod) {
devCopy(false);
}
})
.catch(() => process.exit(1));
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "auto-class",
"name": "Auto Class",
"version": "1.3.0",
"version": "1.4.0",
"minAppVersion": "0.12.12",
"description": "Automatically apply CSS classes to the markdown view based on a note's path.",
"author": "OfficerHalf",
Expand Down
30 changes: 28 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "auto-class",
"version": "1.3.0",
"version": "1.4.0",
"description": "Automatically apply CSS classes to the markdown view based on a note's path.",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"dev": "node esbuild.config.mjs development",
"build": "node esbuild.config.mjs production",
"prepare": "husky install"
},
Expand All @@ -20,6 +20,7 @@
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.1",
"@types/sortablejs": "^1.10.7",
"@typescript-eslint/eslint-plugin": "^5.2.0",
"@typescript-eslint/parser": "^5.2.0",
"esbuild": "0.13.8",
Expand All @@ -37,5 +38,8 @@
"lint-staged": {
"*.ts": "eslint --cache --fix",
"*.{ts,scss}": "prettier --write"
},
"dependencies": {
"sortablejs": "^1.14.0"
}
}
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { AutoClassPluginSettings } from './interfaces';

export const DEFAULT_SETTINGS: AutoClassPluginSettings = {
paths: [{ path: 'Example Path/Subfolder/', classes: ['example-class'], scope: ClassPathScope.Preview }],
version: '1.3.0'
version: '1.4.0'
};
File renamed without changes.
143 changes: 143 additions & 0 deletions src/modal/manage-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { Modal, setIcon, TFolder } from 'obsidian';
import { ClassPathScope } from 'src/enum';
import { ClassPath } from 'src/interfaces';
import { AutoClassPlugin } from '../plugin';
import { FolderSuggestModal } from './folder-suggest';

export class ManagePathModal extends Modal {
readonly folderSuggestModal = new FolderSuggestModal(this.app);
classPath: ClassPath | null = null;
updatedClassPath: ClassPath | null = null;
save: (original: ClassPath, updated: ClassPath) => Promise<void>;

constructor(private readonly plugin: AutoClassPlugin) {
super(plugin.app);
this.modalEl.addClass('auto-class-manage-path__modal');
}

onOpen(): void {
if (this.classPath) {
// Make a copy of the original setting
this.updatedClassPath = { ...this.classPath, classes: [...this.classPath.classes] };
this.display();
}
}

display(): void {
this.contentEl.empty();
this.titleEl.setText('Edit path');

// Render path field
const pathInputContainer = this.contentEl.createDiv('auto-class-manage-path__input-container');
pathInputContainer.createEl('label', {
text: 'Target folder',
attr: { for: 'auto-class-manage-path__path-input' }
});
const pathInputWrapper = pathInputContainer.createDiv('auto-class-manage-path__path-input-wrapper');
const pathButton = pathInputWrapper.createEl('button', { attr: { type: 'button', 'aria-label': 'Select folder' } });
setIcon(pathButton, 'folder');
const folders: TFolder[] = this.app.vault.getAllLoadedFiles().filter((f) => f instanceof TFolder) as TFolder[];
pathButton.addEventListener('click', () => {
this.folderSuggestModal.selectedFolder = null;
this.folderSuggestModal.items = folders;
this.folderSuggestModal.callback = (folder: TFolder) => {
pathInput.value = folder.path;
};
this.folderSuggestModal.open();
});
const pathInput = pathInputWrapper.createEl('input', {
attr: { placeholder: 'Folder', type: 'text', id: 'auto-class-manage-path__path-input' }
});
pathInput.value = this.updatedClassPath.path;

// Render scope dropdown
const scopeDropdownContainer = this.contentEl.createDiv('auto-class-manage-path__input-container');
scopeDropdownContainer.createEl('label', {
text: 'Class scope',
attr: { for: 'auto-class-manage-path__scope-input' }
});
const scopeSelect = scopeDropdownContainer.createEl('select', {
cls: 'dropdown',
attr: { id: 'auto-class-manage-path__scope-input' }
});
const previewOption = scopeSelect.createEl('option', {
text: ClassPathScope.Preview,
attr: { value: ClassPathScope.Preview }
});
if (this.updatedClassPath.scope === ClassPathScope.Preview) {
previewOption.selected = true;
}
const editOption = scopeSelect.createEl('option', {
text: ClassPathScope.Edit,
attr: { value: ClassPathScope.Edit }
});
if (this.updatedClassPath.scope === ClassPathScope.Edit) {
editOption.selected = true;
}
const bothOption = scopeSelect.createEl('option', {
text: ClassPathScope.Both,
attr: { value: ClassPathScope.Both }
});
if (this.updatedClassPath.scope === ClassPathScope.Both) {
bothOption.selected = true;
}
scopeSelect.addEventListener('change', (event: Event) => {
this.updatedClassPath.scope = (event.target as HTMLSelectElement).value as ClassPathScope;
});

// Render class input
const classInputContainer = this.contentEl.createDiv('auto-class-manage-path__input-container');
classInputContainer.createEl('label', {
text: 'New class(es)',
attr: { for: 'auto-class-manage-path__class-input' }
});
const classInputWrapper = classInputContainer.createDiv('auto-class-manage-path__class-input-wrapper');
const classInput = classInputWrapper.createEl('input', {
attr: { placeholder: 'class1, class2', type: 'text', id: 'auto-class-manage-path__class-input' }
});
const addClassesButton = classInputWrapper.createEl('button', { text: 'Add' });
addClassesButton.addEventListener('click', () => {
if (classInput.value) {
this.addClasses(classInput.value);
}
});

// Render classes
const classListContainer = this.contentEl.createDiv('auto-class-manage-path__class-list-container');
classListContainer.createEl('h3', { text: 'Classes' });
const classList = classListContainer.createEl('ul', { cls: 'auto-class-manage-path__class-list' });
for (let i = 0; i < this.updatedClassPath.classes.length; i++) {
const classname = this.updatedClassPath.classes[i];
const listItem = classList.createEl('li', { cls: 'auto-class-manage-path__class-list-item' });
listItem.createSpan({ text: classname });
const deleteButton = listItem.createEl('span', {
cls: 'auto-class-manage-path__class-list-control',
attr: { 'aria-label': 'Remove Class' }
});
setIcon(deleteButton, 'trash');
deleteButton.addEventListener('click', () => {
this.updatedClassPath.classes.splice(i, 1);
this.display();
});
}

this.contentEl.createEl('hr');

// Render controls
const controlsContainer = this.contentEl.createDiv('auto-class-manage-path__controls');
const saveButton = controlsContainer.createEl('button', { cls: 'mod-cta', text: 'Save', attr: { type: 'button' } });
saveButton.addEventListener('click', async () => {
await this.save(this.classPath, this.updatedClassPath);
this.close();
});
const cancelButton = controlsContainer.createEl('button', { text: 'Cancel', attr: { type: 'button' } });
cancelButton.addEventListener('click', () => {
this.close();
});
}

addClasses(classes: string): void {
this.updatedClassPath.classes = [...this.plugin.getClassList(classes), ...this.updatedClassPath.classes];
this.display();
}
}
6 changes: 5 additions & 1 deletion src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,23 @@ export class AutoClassPlugin extends Plugin {
* any linked panes.
*/
handleLayoutChange(): void {
console.log('HEY');
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (activeView) {
console.log(activeView);
// Get any linked views
let activeViews: MarkdownView[] = [activeView];
const leafGroup = this.app.workspace.getGroupLeaves((activeView.leaf as any).group);
if (leafGroup) {
if (leafGroup && leafGroup.length > 0) {
activeViews = leafGroup
.map((leaf) => leaf.view)
.filter((view) => view instanceof MarkdownView) as MarkdownView[];
}
console.log(activeViews);

// Remove and apply classes for each applicable view
activeViews.forEach((view) => {
console.log(view);
this.removePreviousClasses(view);
let matches: ClassPath[] = [];
let container: Element;
Expand Down
Loading

0 comments on commit 831f6de

Please sign in to comment.