Skip to content

Commit

Permalink
feat: add functionality of setting color of an individual icon (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianWoelki committed Jan 6, 2024
1 parent 5e42df6 commit 88e16f7
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 12 deletions.
4 changes: 3 additions & 1 deletion src/lib/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ const addAll = (
if (titleEl.children.length === 2 || titleEl.children.length === 1) {
// Gets the icon name directly or from the inheritance folder.
const iconName = typeof value === 'string' ? value : value.iconName;
const iconColor =
typeof value === 'string' ? undefined : value.iconColor;
if (iconName) {
// Removes a possible existing icon.
const existingIcon = titleEl.querySelector('.iconize-icon');
Expand All @@ -231,7 +233,7 @@ const addAll = (
IconCache.getInstance().set(dataPath, {
iconNameWithPrefix: iconName,
});
dom.setIconForNode(plugin, iconName, iconNode);
dom.setIconForNode(plugin, iconName, iconNode, iconColor);

titleEl.insertBefore(iconNode, titleInnerEl);
}
Expand Down
15 changes: 14 additions & 1 deletion src/lib/inheritance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ const getFolders = (
plugin: IconFolderPlugin,
): Record<string, FolderIconObject> => {
return Object.entries(plugin.getData())
.filter(([k, v]) => k !== 'settings' && typeof v === 'object')
.filter(
([k, v]) =>
k !== 'settings' &&
typeof v === 'object' &&
(v as FolderIconObject).inheritanceIcon,
)
.reduce<Record<string, FolderIconObject>>((prev, [path, value]) => {
prev[path] = value as FolderIconObject;
return prev;
Expand Down Expand Up @@ -57,6 +62,10 @@ const add = (
return;
}

if (!(folder as FolderIconObject).inheritanceIcon) {
return;
}

// A inner function that helps to add the inheritance icon to the DOM.
const addIcon = (fileItem: FileItem): void => {
const titleEl = getFileItemTitleEl(fileItem);
Expand Down Expand Up @@ -133,6 +142,10 @@ const remove = (
return;
}

if (!(folder as FolderIconObject).inheritanceIcon) {
return;
}

// Gets all files that include the folder path of the currently opened vault.
const files = getFiles(plugin, folderPath);

Expand Down
7 changes: 7 additions & 0 deletions src/lib/util/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,18 @@ const getIconFromElement = (element: HTMLElement): string | undefined => {
return existingIcon;
};

const getIconNodeFromPath = (path: string): HTMLElement | undefined => {
return document
.querySelector(`[data-path="${path}"]`)
?.querySelector('[data-icon]');
};

export default {
setIconForNode,
createIconNode,
doesElementHasIconNode,
getIconFromElement,
getIconNodeFromPath,
removeIconInNode,
removeIconInPath,
};
94 changes: 87 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ import { buildIconPlugin } from './editor/live-preview';
import { PositionField, buildPositionField } from './editor/live-preview/state';
import { calculateInlineTitleSize } from './lib/util/text';
import { processMarkdown } from './editor/markdown-processor';
import ChangeColorModal from './ui/change-color-modal';

export interface FolderIconObject {
iconName: string | null;
inheritanceIcon: string;
inheritanceIcon?: string;
iconColor?: string;
}

export default class IconFolderPlugin extends Plugin {
Expand Down Expand Up @@ -135,6 +137,15 @@ export default class IconFolderPlugin extends Plugin {
});
};

const changeColorOfIcon = (item: MenuItem) => {
item.setTitle('Change color of icon');
item.setIcon('palette');
item.onClick(() => {
const modal = new ChangeColorModal(this.app, this, file.path);
modal.open();
});
};

menu.addItem(addIconMenuItem);

const filePathData = this.getData()[file.path];
Expand All @@ -147,12 +158,20 @@ export default class IconFolderPlugin extends Plugin {
filePathData &&
(typeof filePathData === 'string' || inheritanceFolderHasIcon)
) {
const icon =
typeof filePathData === 'string'
? filePathData
: (filePathData as FolderIconObject).iconName;
if (!emoji.isEmoji(icon)) {
menu.addItem(changeColorOfIcon);
}

menu.addItem(removeIconMenuItem);
}

const inheritIcon = (item: MenuItem) => {
const iconData = this.data[file.path] as FolderIconObject | string;
if (typeof iconData === 'object') {
if (typeof iconData === 'object' && iconData.inheritanceIcon) {
item.setTitle('Remove inherit icon');
item.onClick(() => {
inheritance.remove(this, file.path, {
Expand Down Expand Up @@ -280,6 +299,9 @@ export default class IconFolderPlugin extends Plugin {
const folderPath = inheritance.getFolderPathByFilePath(this, file.path);
const folderInheritance = inheritance.getByPath(this, file.path);
const iconName = folderInheritance.inheritanceIcon;
if (!iconName) {
return;
}
didUpdate = true;
inheritance.add(this, folderPath, iconName, {
file,
Expand Down Expand Up @@ -406,6 +428,11 @@ export default class IconFolderPlugin extends Plugin {
const folderInheritance = inheritance.getByPath(this, file.path);
const iconName = folderInheritance.inheritanceIcon;
dom.removeIconInPath(file.path);

if (!iconName) {
return;
}

inheritance.add(this, folderPath, iconName, {
file,
onAdd: (file) => {
Expand Down Expand Up @@ -486,6 +513,10 @@ export default class IconFolderPlugin extends Plugin {

inheritanceFolders.forEach(
([path, obj]: [string, FolderIconObject]) => {
if (!obj.inheritanceIcon) {
return;
}

inheritance.add(this, path, obj.inheritanceIcon, {
file,
onAdd: (file) => {
Expand Down Expand Up @@ -747,8 +778,10 @@ export default class IconFolderPlugin extends Plugin {
if (icon === null && currentValue && typeof currentValue === 'object') {
const folderObject = currentValue as FolderIconObject;

if (folderObject.iconName) {
if (folderObject.iconName && !folderObject.iconColor) {
this.data[folderPath] = getNormalizedName(folderObject.iconName);
} else if (folderObject.iconName) {
(currentValue as FolderIconObject).inheritanceIcon = null;
} else {
delete this.data[folderPath];
}
Expand Down Expand Up @@ -806,6 +839,48 @@ export default class IconFolderPlugin extends Plugin {
this.saveIconFolderData();
}

addIconColor(path: string, iconColor: string): void {
const pathData = this.getData()[path];

if (typeof pathData === 'string') {
this.getData()[path] = {
iconName: pathData,
iconColor,
};
} else {
(pathData as FolderIconObject).iconColor = iconColor;
}

this.saveIconFolderData();
}

getIconColor(path: string): string | undefined {
const pathData = this.getData()[path];

if (typeof pathData === 'string') {
return undefined;
}

return (pathData as FolderIconObject).iconColor;
}

removeIconColor(path: string): void {
const pathData = this.getData()[path];

if (typeof pathData === 'string') {
return;
}

const currentValue = pathData as FolderIconObject;
if (!currentValue.inheritanceIcon) {
this.data[path] = currentValue.iconName;
} else {
delete currentValue.iconColor;
}

this.saveIconFolderData();
}

removeFolderIcon(path: string): void {
if (!this.data[path]) {
return;
Expand All @@ -816,10 +891,15 @@ export default class IconFolderPlugin extends Plugin {

if (typeof this.data[path] === 'object') {
const currentValue = this.data[path] as FolderIconObject;
this.data[path] = {
...currentValue,
iconName: null,
};
if (!currentValue.inheritanceIcon) {
delete this.data[path];
} else {
delete currentValue.iconColor;
this.data[path] = {
...currentValue,
iconName: null,
};
}
} else {
delete this.data[path];
}
Expand Down
2 changes: 1 addition & 1 deletion src/settings/ui/customIconRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export default class CustomIconRuleSetting extends IconFolderSetting {
// Create modal and its children elements.
const modal = new Modal(this.plugin.app);
modal.contentEl.style.display = 'block';
modal.modalEl.classList.add('iconize-custom-rule-modal');
modal.modalEl.classList.add('iconize-custom-modal');
modal.titleEl.setText('Edit custom rule');

// Create the input for the rule.
Expand Down
4 changes: 4 additions & 0 deletions src/settings/ui/emojiStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export default class EmojiStyleSetting extends IconFolderSetting {
const inheritanceData = this.plugin.getData()[
path
] as FolderIconObject;
if (!inheritanceData.inheritanceIcon) {
continue;
}

iconName = inheritanceData.iconName;

// Handle updating the emoji style for the inheritance icon.
Expand Down
4 changes: 2 additions & 2 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@
}

/* Custom rule modal. */
.iconize-custom-rule-modal .modal-content {
.iconize-custom-modal .modal-content {
display: flex;
align-items: center;
justify-content: center;
}

.iconize-custom-rule-modal .modal-content input {
.iconize-custom-modal .modal-content input {
width: 100%;
margin-right: 0.5rem;
}
80 changes: 80 additions & 0 deletions src/ui/change-color-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { App, Modal, ColorComponent, ButtonComponent, Notice } from 'obsidian';
import IconFolderPlugin from '@app/main';
import svg from '@app/lib/util/svg';
import dom from '@app/lib/util/dom';

export default class ChangeColorModal extends Modal {
private plugin: IconFolderPlugin;
private path: string;

private usedColor?: string;

constructor(app: App, plugin: IconFolderPlugin, path: string) {
super(app);
this.plugin = plugin;
this.path = path;

this.usedColor = this.plugin.getIconColor(this.path);

this.contentEl.style.display = 'block';
this.modalEl.classList.add('iconize-custom-modal');
this.titleEl.setText('Change color');

const description = this.contentEl.createEl('p', {
text: 'Select a color for this icon',
cls: 'setting-item-description',
});
description.style.marginBottom = 'var(--size-2-2)';
const colorContainer = this.contentEl.createDiv();
colorContainer.style.display = 'flex';
colorContainer.style.alignItems = 'center';
colorContainer.style.justifyContent = 'space-between';
const colorPicker = new ColorComponent(colorContainer)
.setValue(this.usedColor ?? '#000000')
.onChange((value) => {
this.usedColor = value;
});
const defaultColorButton = new ButtonComponent(colorContainer);
defaultColorButton.setTooltip('Set color to the default one');
defaultColorButton.setButtonText('Reset');
defaultColorButton.onClick(() => {
colorPicker.setValue('#000000');
this.usedColor = undefined;
});

// Save button.
const button = new ButtonComponent(this.contentEl);
button.buttonEl.style.marginTop = 'var(--size-4-4)';
button.buttonEl.style.float = 'right';
button.setButtonText('Save Changes');
button.onClick(async () => {
new Notice('Color of icon changed.');

if (this.usedColor) {
this.plugin.addIconColor(this.path, this.usedColor);
} else {
this.plugin.removeIconColor(this.path);
}

// Refresh the DOM.
const iconNode = dom.getIconNodeFromPath(this.path);
iconNode.style.color = this.usedColor ?? null;
const colorizedInnerHtml = svg.colorize(
iconNode.innerHTML,
this.usedColor,
);
iconNode.innerHTML = colorizedInnerHtml;

this.close();
});
}

onOpen() {
super.onOpen();
}

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

0 comments on commit 88e16f7

Please sign in to comment.