Document emoji picker (#4338)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
14
shared/editor/lib/emoji.ts
Normal file
14
shared/editor/lib/emoji.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import data, { type EmojiMartData } from "@emoji-mart/data";
|
||||
import snakeCase from "lodash/snakeCase";
|
||||
|
||||
/**
|
||||
* A map of emoji shortcode to emoji character. The shortcode is snake cased
|
||||
* for backwards compatibility with those already encoded into documents.
|
||||
*/
|
||||
export const nameToEmoji = Object.values((data as EmojiMartData).emojis).reduce(
|
||||
(acc, emoji) => {
|
||||
acc[snakeCase(emoji.id)] = emoji.skins[0].native;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
@@ -1,4 +1,3 @@
|
||||
import nameToEmoji from "gemoji/name-to-emoji.json";
|
||||
import Token from "markdown-it/lib/token";
|
||||
import {
|
||||
NodeSpec,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
import { Command, TextSelection } from "prosemirror-state";
|
||||
import { Primitive } from "utility-types";
|
||||
import Suggestion from "../extensions/Suggestion";
|
||||
import { nameToEmoji } from "../lib/emoji";
|
||||
import { MarkdownSerializerState } from "../lib/markdown/serializer";
|
||||
import { SuggestionsMenuType } from "../plugins/Suggestions";
|
||||
import emojiRule from "../rules/emoji";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import nameToEmoji from "gemoji/name-to-emoji.json";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import emojiPlugin from "markdown-it-emoji";
|
||||
import { nameToEmoji } from "../lib/emoji";
|
||||
|
||||
export default function emoji(md: MarkdownIt) {
|
||||
// Ideally this would be an empty object, but due to a bug in markdown-it-emoji
|
||||
|
||||
@@ -168,6 +168,8 @@
|
||||
"Currently editing": "Currently editing",
|
||||
"Currently viewing": "Currently viewing",
|
||||
"Viewed {{ timeAgo }} ago": "Viewed {{ timeAgo }} ago",
|
||||
"Emoji Picker": "Emoji Picker",
|
||||
"Remove": "Remove",
|
||||
"Module failed to load": "Module failed to load",
|
||||
"Loading Failed": "Loading Failed",
|
||||
"Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.": "Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.",
|
||||
@@ -242,6 +244,8 @@
|
||||
"{{ releasesBehind }} versions behind_plural": "{{ releasesBehind }} versions behind",
|
||||
"Return to App": "Back to App",
|
||||
"Installation": "Installation",
|
||||
"Unstar document": "Unstar document",
|
||||
"Star document": "Star document",
|
||||
"No results": "No results",
|
||||
"Previous page": "Previous page",
|
||||
"Next page": "Next page",
|
||||
@@ -358,7 +362,6 @@
|
||||
"Show path to document": "Show path to document",
|
||||
"Path to document": "Path to document",
|
||||
"Group member options": "Group member options",
|
||||
"Remove": "Remove",
|
||||
"Export collection": "Export collection",
|
||||
"Sort in sidebar": "Sort in sidebar",
|
||||
"Alphabetical sort": "Alphabetical sort",
|
||||
|
||||
@@ -37,3 +37,20 @@ export const hideScrollbars = () => `
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Mixin on any component with relative positioning to add additional hidden clickable/hoverable area
|
||||
*
|
||||
* @param pixels
|
||||
* @returns
|
||||
*/
|
||||
export const extraArea = (pixels: number): string => `
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: -${pixels}px;
|
||||
right: -${pixels}px;
|
||||
left: -${pixels}px;
|
||||
bottom: -${pixels}px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -159,6 +159,7 @@ export type NavigationNode = {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
emoji?: string;
|
||||
children: NavigationNode[];
|
||||
isDraft?: boolean;
|
||||
collectionId?: string;
|
||||
|
||||
1
shared/typings/gemoji.d.ts
vendored
1
shared/typings/gemoji.d.ts
vendored
@@ -1 +0,0 @@
|
||||
declare module "gemoji";
|
||||
@@ -1,5 +1,5 @@
|
||||
import md5 from "crypto-js/md5";
|
||||
import { darken } from "polished";
|
||||
import { darken, parseToRgb } from "polished";
|
||||
import theme from "../styles/theme";
|
||||
|
||||
export const palette = [
|
||||
@@ -26,3 +26,12 @@ export const stringToColor = (input: string) => {
|
||||
const inputAsNumber = parseInt(md5(input).toString(), 16);
|
||||
return palette[inputAsNumber % palette.length];
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a color to string of RGB values separated by commas
|
||||
*
|
||||
* @param color - A color string
|
||||
* @returns A string of RGB values separated by commas
|
||||
*/
|
||||
export const toRGB = (color: string) =>
|
||||
Object.values(parseToRgb(color)).join(", ");
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function parseTitle(text = "") {
|
||||
// find and extract first emoji
|
||||
const matches = regex.exec(title);
|
||||
const firstEmoji = matches ? matches[0] : null;
|
||||
const startsWithEmoji = firstEmoji && title.startsWith(`${firstEmoji} `);
|
||||
const startsWithEmoji = firstEmoji && title.startsWith(firstEmoji);
|
||||
const emoji = startsWithEmoji ? firstEmoji : undefined;
|
||||
|
||||
// title with first leading emoji stripped
|
||||
|
||||
Reference in New Issue
Block a user