Skip to content

Commit

Permalink
feat: add first functionality for twemoji (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianWoelki committed Dec 23, 2021
1 parent 453dc1c commit 90f9dbe
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 29 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -47,6 +47,7 @@
"rollup": "^2.32.1",
"rollup-plugin-babel": "^4.4.0",
"tslib": "^2.2.0",
"twemoji": "^13.1.0",
"typescript": "^4.2.4"
}
}
28 changes: 25 additions & 3 deletions src/iconsPickerModal.ts
@@ -1,6 +1,7 @@
import twemoji from 'twemoji';
import { App, FuzzyMatch, FuzzySuggestModal } from 'obsidian';
import IconFolderPlugin from './main';
import { addToDOM, getEnabledIcons, getIcon } from './util';
import { addToDOM, getEnabledIcons, getIcon, isEmoji } from './util';

export interface Icon {
name: string;
Expand All @@ -15,6 +16,23 @@ export default class IconsPickerModal extends FuzzySuggestModal<any> {
super(app);
this.plugin = plugin;
this.path = path;

this.inputEl.addEventListener('input', (e) => {
const inputVal = (e.target as HTMLInputElement).value;
if (isEmoji(inputVal)) {
this.resultContainerEl.querySelector('.suggestion-empty').remove();

const suggestionItem = this.resultContainerEl.createDiv();
suggestionItem.className = 'suggestion-item';
suggestionItem.textContent = 'Use twemoji Emoji';
suggestionItem.addEventListener('click', () => {
const codepoint = twemoji.convert.toCodePoint(inputVal);
this.onChooseItem(codepoint);
this.close();
});
this.resultContainerEl.appendChild(suggestionItem);
}
});
}

onOpen() {
Expand Down Expand Up @@ -42,8 +60,12 @@ export default class IconsPickerModal extends FuzzySuggestModal<any> {
return iconKeys;
}

onChooseItem(item: Icon): void {
addToDOM(this.plugin, this.path, item.name);
onChooseItem(item: Icon | string): void {
if (typeof item === 'object') {
addToDOM(this.plugin, this.path, item.name);
} else {
addToDOM(this.plugin, this.path, item);
}
this.plugin.addFolderIcon(this.path, item);
}

Expand Down
14 changes: 7 additions & 7 deletions src/main.ts
Expand Up @@ -69,7 +69,7 @@ export default class IconFolderPlugin extends Plugin {
const modal = new IconsPickerModal(this.app, this, file.path);
modal.open();
// manipulate `onChooseItem` method to get custom functioanlity for inheriting icons
modal.onChooseItem = (icon: Icon) => {
modal.onChooseItem = (icon: Icon | string) => {
this.saveInheritanceData(file.path, icon);
addInheritanceForFolder(this, file.path);
};
Expand Down Expand Up @@ -124,7 +124,7 @@ export default class IconFolderPlugin extends Plugin {
});
}

private saveInheritanceData(folderPath: string, icon: Icon | null): void {
private saveInheritanceData(folderPath: string, icon: Icon | string | null): void {
const currentValue = this.data[folderPath];
// if icon is null, it will remove the inheritance icon from the data
if (icon === null && currentValue && typeof currentValue === 'object') {
Expand All @@ -144,20 +144,20 @@ export default class IconFolderPlugin extends Plugin {
if (typeof currentValue === 'string') {
this.data[folderPath] = {
iconName: currentValue as string,
inheritanceIcon: icon.name,
inheritanceIcon: typeof icon === 'object' ? icon.name : icon,
};
}
// check if it has already a inheritance icon
else if (folderPath !== 'settings') {
this.data[folderPath] = {
...(currentValue as FolderIconObject),
inheritanceIcon: icon.name,
inheritanceIcon: typeof icon === 'object' ? icon.name : icon,
};
}
} else {
this.data[folderPath] = {
iconName: null,
inheritanceIcon: icon.name,
inheritanceIcon: typeof icon === 'object' ? icon.name : icon,
};
}
}
Expand Down Expand Up @@ -197,8 +197,8 @@ export default class IconFolderPlugin extends Plugin {
this.saveIconFolderData();
}

addFolderIcon(path: string, icon: Icon): void {
this.data[path] = icon.name;
addFolderIcon(path: string, icon: Icon | string): void {
this.data[path] = typeof icon === 'object' ? icon.name : icon;
this.saveIconFolderData();
}

Expand Down
67 changes: 48 additions & 19 deletions src/util.ts
@@ -1,3 +1,4 @@
import twemoji from 'twemoji';
import * as remixicons from '../remixicons';
import * as faLine from '../fontawesome/index-line';
import * as faFill from '../fontawesome/index-fill';
Expand Down Expand Up @@ -71,11 +72,11 @@ export const getEnabledIcons = (plugin: IconFolderPlugin): string[] => {
*
* @public
* @param {string} name - The icon name.
* @returns {string} The correct transformed svg.
* @returns {string | null} The transformed svg or null if it cannot find any iconpack.
*/
export const getIcon = (name: string): string => {
export const getIcon = (name: string): string | null => {
const prefix = name.substr(0, 2);
let iconSvg: string = '';
let iconSvg: string = null;
if (prefix === 'Fa') {
if (name.toLowerCase().substr(name.length - 4) === 'line') {
iconSvg = faLine[name.substr(2)];
Expand All @@ -86,8 +87,6 @@ export const getIcon = (name: string): string => {
}
} else if (prefix === 'Ri') {
iconSvg = remixicons[name.substr(2)];
} else {
iconSvg = remixicons[name.substr(2)];
}

return iconSvg;
Expand All @@ -101,21 +100,18 @@ export const getIcon = (name: string): string => {
*
* @public
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} iconSvg - The to be styled icon svg.
* @param {string} icon - The to be styled icon.
* @param {HTMLElement} el - The element that will include the padding from the user settings.
* @returns {string} The svg with the customized css settings.
*/
export const customizeIconStyle = (plugin: IconFolderPlugin, iconSvg: string, el: HTMLElement): string => {
export const customizeIconStyle = (plugin: IconFolderPlugin, icon: string, el: HTMLElement): string => {
// Allow custom font size
const sizeRe = new RegExp(/width="\d+" height="\d+"/g);
iconSvg = iconSvg.replace(
sizeRe,
`width="${plugin.getSettings().fontSize}" height="${plugin.getSettings().fontSize}"`,
);
icon = icon.replace(sizeRe, `width="${plugin.getSettings().fontSize}" height="${plugin.getSettings().fontSize}"`);

// Allow custom icon color
const colorRe = new RegExp(/fill="(\w|#)+"/g);
iconSvg = iconSvg.replace(colorRe, `fill="${plugin.getSettings().iconColor ?? 'currentColor'}"`);
icon = icon.replace(colorRe, `fill="${plugin.getSettings().iconColor ?? 'currentColor'}"`);

// Change padding of icon
if (plugin.getSettings().extraPadding) {
Expand All @@ -124,7 +120,7 @@ export const customizeIconStyle = (plugin: IconFolderPlugin, iconSvg: string, el
}px ${plugin.getSettings().extraPadding.bottom ?? 2}px ${plugin.getSettings().extraPadding.left ?? 2}px`;
}

return iconSvg;
return icon;
};

/**
Expand Down Expand Up @@ -168,7 +164,8 @@ export const addIconsToDOM = (
if (iconName) {
const iconNode = titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
iconNode.innerHTML = customizeIconStyle(plugin, getIcon(iconName), iconNode);

insertIconToNode(plugin, iconName, iconNode);

titleEl.insertBefore(iconNode, titleInnerEl);
}
Expand All @@ -181,7 +178,8 @@ export const addIconsToDOM = (
const inheritanceFileItem = fileExplorer.view.fileItems[f.path];
const iconNode = inheritanceFileItem.titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
iconNode.innerHTML = customizeIconStyle(plugin, getIcon(inheritanceIconName), iconNode);

insertIconToNode(plugin, inheritanceIconName, iconNode);

inheritanceFileItem.titleEl.insertBefore(iconNode, inheritanceFileItem.titleInnerEl);
}
Expand Down Expand Up @@ -210,7 +208,8 @@ export const addInheritanceIconToFile = (
if (fileItem) {
const iconNode = fileItem.titleEl.createDiv();
iconNode.classList.add('obsidian-icon-folder-icon');
iconNode.innerHTML = customizeIconStyle(plugin, getIcon(iconName), iconNode);

insertIconToNode(plugin, iconName, iconNode);

fileItem.titleEl.insertBefore(iconNode, fileItem.titleInnerEl);
}
Expand Down Expand Up @@ -268,9 +267,9 @@ export const removeFromDOM = (path: string): void => {
* @public
* @param {IconFolderPlugin} plugin - The main plugin.
* @param {string} path - The path in the DOM where the icon will be added.
* @param {string} iconId - The icon id that will be added to the DOM.
* @param {string} icon - The icon that will be added to the DOM - can be an icon id or codepoint for twemoji.
*/
export const addToDOM = (plugin: IconFolderPlugin, path: string, iconId: string): void => {
export const addToDOM = (plugin: IconFolderPlugin, path: string, icon: string): void => {
if (plugin.getData()[path]) {
removeFromDOM(path);
}
Expand Down Expand Up @@ -299,11 +298,29 @@ export const addToDOM = (plugin: IconFolderPlugin, path: string, iconId: string)

const iconNode = document.createElement('div');
iconNode.classList.add('obsidian-icon-folder-icon');
iconNode.innerHTML = customizeIconStyle(plugin, getIcon(iconId), iconNode);

insertIconToNode(plugin, icon, iconNode);

node.insertBefore(iconNode, titleNode);
};

const insertIconToNode = (plugin: IconFolderPlugin, icon: string, node: HTMLElement): void => {
const possibleIcon = getIcon(icon);
if (possibleIcon) {
node.innerHTML = customizeIconStyle(plugin, possibleIcon, node);
} else {
const emoji = twemoji.parse(twemoji.convert.fromCodePoint(icon), {
folder: 'svg',
ext: '.svg',
attributes: () => ({
width: '16px',
height: '16px',
}),
});
node.innerHTML = customizeIconStyle(plugin, emoji, node);
}
};

/**
* This function will add inheritance functionality to a specific folder.
* It will add the inheritance icon to all child files.
Expand Down Expand Up @@ -351,3 +368,15 @@ export const removeInheritanceForFolder = (plugin: IconFolderPlugin, folderPath:
}
});
};

export const isEmoji = (str: string): boolean => {
const ranges = [
'(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c[\ude32-\ude3a]|[\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])', // U+1F680 to U+1F6FF
];

if (str.match(ranges.join('|'))) {
return true;
} else {
return false;
}
};
45 changes: 45 additions & 0 deletions yarn.lock
Expand Up @@ -1094,6 +1094,15 @@ fs-extra@^10.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"

fs-extra@^8.0.1:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"

fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
Expand Down Expand Up @@ -1421,6 +1430,22 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=

jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"

jsonfile@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922"
integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==
dependencies:
universalify "^0.1.2"
optionalDependencies:
graceful-fs "^4.1.6"

jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
Expand Down Expand Up @@ -2268,6 +2293,21 @@ tsutils@^3.21.0:
dependencies:
tslib "^1.8.1"

twemoji-parser@13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.1.0.tgz#65e7e449c59258791b22ac0b37077349127e3ea4"
integrity sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg==

twemoji@^13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-13.1.0.tgz#65bb71e966dae56f0d42c30176f04cbdae109913"
integrity sha512-e3fZRl2S9UQQdBFLYXtTBT6o4vidJMnpWUAhJA+yLGR+kaUTZAt3PixC0cGvvxWSuq2MSz/o0rJraOXrWw/4Ew==
dependencies:
fs-extra "^8.0.1"
jsonfile "^5.0.0"
twemoji-parser "13.1.0"
universalify "^0.1.2"

type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
Expand Down Expand Up @@ -2310,6 +2350,11 @@ uglify-js@^3.1.4:
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.3.tgz#c0f25dfea1e8e5323eccf59610be08b6043c15cf"
integrity sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==

universalify@^0.1.0, universalify@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==

universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
Expand Down

0 comments on commit 90f9dbe

Please sign in to comment.