diff --git a/packages/presentation/src/components/markup/NodeContent.svelte b/packages/presentation/src/components/markup/NodeContent.svelte
index ca46d66037..4cb53b3628 100644
--- a/packages/presentation/src/components/markup/NodeContent.svelte
+++ b/packages/presentation/src/components/markup/NodeContent.svelte
@@ -80,7 +80,15 @@
{:else if node.type === MarkupNodeType.text}
{node.text}
{:else if node.type === MarkupNodeType.emoji}
- {node.attrs?.emoji}
+
+ {#if node.attrs?.kind === 'custom'}
+ {@const src = toString(attrs.url)}
+ {@const alt = toString(attrs.emoji)}
+
+ {:else}
+ {node.attrs?.emoji}
+ {/if}
+
{:else if node.type === MarkupNodeType.paragraph}
{#if nodes.length > 0}
diff --git a/packages/text/src/nodes/emoji.ts b/packages/text/src/nodes/emoji.ts
index 2e11bc4464..6a6c062aee 100644
--- a/packages/text/src/nodes/emoji.ts
+++ b/packages/text/src/nodes/emoji.ts
@@ -18,13 +18,15 @@ import { Node, mergeAttributes } from '@tiptap/core'
declare module '@tiptap/core' {
interface Commands {
emoji: {
- insertEmoji: (emoji: string) => ReturnType
+ insertEmoji: (emoji: string, kind: 'unicode' | 'custom', url?: string) => ReturnType
}
}
}
export interface EmojiNodeOptions {
emoji: string
+ kind: 'unicode' | 'custom'
+ url?: string
}
export const EmojiNode = Node.create({
@@ -38,6 +40,12 @@ export const EmojiNode = Node.create({
return {
emoji: {
default: ''
+ },
+ kind: {
+ default: 'unicode'
+ },
+ url: {
+ default: null
}
}
},
@@ -45,11 +53,11 @@ export const EmojiNode = Node.create({
addCommands () {
return {
insertEmoji:
- (emoji: string) =>
+ (emoji: string, kind: 'unicode' | 'custom', url?: string) =>
({ commands }) => {
return commands.insertContent({
type: this.name,
- attrs: { emoji }
+ attrs: { emoji, kind, url }
})
}
}
@@ -64,6 +72,28 @@ export const EmojiNode = Node.create({
},
renderHTML ({ node, HTMLAttributes }) {
+ if (node.attrs.kind === 'custom') {
+ return [
+ 'span',
+ mergeAttributes(
+ {
+ 'data-type': this.name,
+ class: 'emoji'
+ },
+ HTMLAttributes
+ ),
+ [
+ 'img',
+ mergeAttributes(
+ {
+ 'data-type': this.name,
+ src: node.attrs.url,
+ alt: node.attrs.emoji
+ }
+ )
+ ]
+ ]
+ }
return [
'span',
mergeAttributes(
diff --git a/packages/theme/styles/common.scss b/packages/theme/styles/common.scss
index 7757fccba5..610ee03613 100644
--- a/packages/theme/styles/common.scss
+++ b/packages/theme/styles/common.scss
@@ -1,14 +1,14 @@
//
// Copyright © 2021 Anticrm Platform Contributors.
-//
+//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//
+//
// See the License for the specific language governing permissions and
// limitations under the License.
//
@@ -108,7 +108,7 @@
justify-content: space-between;
align-items: center;
background-color: var(--theme-navpanel-color);
-
+
&.vertical {
flex-direction: column;
min-width: var(--app-panel-width);
@@ -132,7 +132,7 @@
display: flex;
height: 100%;
min-height: 0;
-
+
&.header { background-color: var(--theme-comp-header-color); }
&.filled { background-color: var(--theme-bg-color); }
&.filledNav { background-color: var(--theme-navpanel-color) !important; }
@@ -144,13 +144,13 @@
min-width: 12.5rem;
max-width: 22.5rem;
width: 17.5rem;
-
+
&:not(.second) { background-color: var(--theme-navpanel-color); }
&.second.float {
background-color: var(--theme-navpanel-color);
filter: drop-shadow(2px 0 5px rgba(0, 0, 0, .2));
z-index: 460;
-
+
&:not(.inner) {
position: fixed;
top: calc(var(--status-bar-height) + 3.5rem + 1px);
@@ -301,7 +301,7 @@
width: 1rem;
height: 1rem;
border-radius: .25rem;
-
+
&.arrow {
color: var(--theme-trans-color);
@@ -446,7 +446,7 @@
width: 15rem;
padding-right: 1rem;
color: var(--theme-caption-color);
-
+
&.withDesciption {
display: flex;
flex-direction: column;
@@ -592,7 +592,7 @@
height: 2.5rem;
min-height: 2.5rem;
border-bottom: 1px solid var(--theme-divider-color);
-
+
&.high {
padding-right: 1rem;
height: 3.5rem;
@@ -613,7 +613,7 @@
min-width: 0;
font-size: 1rem;
color: var(--caption-color);
-
+
&:not(.short) { flex-grow: 1; }
}
&__header {
@@ -632,7 +632,7 @@
}
&__counter {
color: var(--theme-darker-color);
- }
+ }
&__tag {
display: flex;
align-items: center;
@@ -662,7 +662,7 @@
display: flex;
min-width: 0;
min-height: 0;
-
+
&:not(.none-appearance) {
justify-content: center;
align-items: center;
@@ -818,7 +818,7 @@
}
&:hover .caption.hasAttachments { margin-bottom: .5rem; }
}
-
+
&:first-child {
border-top-left-radius: .75rem;
border-top-right-radius: .75rem;
@@ -854,3 +854,13 @@
padding-top: 0;
.message.outcoming { border-radius: 0.75rem 0.125rem 0.125rem 0.75rem; }
}
+
+.emoji > img {
+ display: inline-block;
+ vertical-align: sub;
+ height: 1.3em;
+ width: auto;
+ margin: 0 0.05em 0 0.1em;
+ padding: 0;
+ line-height: 1;
+}
diff --git a/packages/ui-next/src/components/TextInput.svelte b/packages/ui-next/src/components/TextInput.svelte
index 7b4dfebf72..fab2e436d4 100644
--- a/packages/ui-next/src/components/TextInput.svelte
+++ b/packages/ui-next/src/components/TextInput.svelte
@@ -77,8 +77,8 @@
insertText: (text) => {
editor?.insertText(text)
},
- insertEmoji: (emoji) => {
- editor?.insertEmoji(emoji)
+ insertEmoji: (text: string, url?: string) => {
+ editor?.insertEmoji(text, url)
},
insertMarkup: (markup) => {
editor?.insertMarkup(markup)
diff --git a/packages/ui/src/components/emoji/EmojiButton.svelte b/packages/ui/src/components/emoji/EmojiButton.svelte
index daf35fb068..ba4e90b8f8 100644
--- a/packages/ui/src/components/emoji/EmojiButton.svelte
+++ b/packages/ui/src/components/emoji/EmojiButton.svelte
@@ -41,7 +41,11 @@
dispatch('select', displayedEmoji)
}}
>
- {isCustomEmoji(displayedEmoji) ? displayedEmoji.shortcode : displayedEmoji.emoji}
+ {#if isCustomEmoji(displayedEmoji)}
+
+ {:else}
+ {displayedEmoji.emoji}
+ {/if}
{/if}
@@ -74,6 +78,10 @@
transform: translateY(1%);
pointer-events: none;
}
+ span > img {
+ height: 1em;
+ width: auto;
+ }
&:enabled:hover {
background-color: var(--theme-popup-hover);
}
diff --git a/packages/ui/src/components/emoji/EmojiPopup.svelte b/packages/ui/src/components/emoji/EmojiPopup.svelte
index 26a7dba808..b5995324cc 100644
--- a/packages/ui/src/components/emoji/EmojiPopup.svelte
+++ b/packages/ui/src/components/emoji/EmojiPopup.svelte
@@ -102,9 +102,8 @@
selected = isCustomEmoji(emoji) ? emoji.shortcode : emoji.emoji
addFrequentlyEmojis(emoji)
dispatch('close', {
- // TODO: send ExtendedEmoji
- emoji: selected
- // codes: emoji.hexcode.split('-').map((hc) => parseInt(hc, 16))
+ text: selected,
+ url: isCustomEmoji(emoji) ? emoji.url : undefined
})
}
diff --git a/packages/ui/src/components/emoji/utils.ts b/packages/ui/src/components/emoji/utils.ts
index 0b393d470d..eb2b16f99b 100644
--- a/packages/ui/src/components/emoji/utils.ts
+++ b/packages/ui/src/components/emoji/utils.ts
@@ -69,6 +69,13 @@ export async function loadEmojis (lang?: string): Promise {
export async function updateEmojis (lang?: string): Promise {
const emojis = await loadEmojis(lang)
+ emojis.push({
+ shortcode: 'huly',
+ label: 'huly',
+ url: 'https://fonts.gstatic.com/s/e/notoemoji/latest/1f979/512.gif',
+ tags: ['huly'],
+ key: 'flags'
+ })
emojiStore.set(emojis)
}
@@ -86,13 +93,15 @@ export function getEmojiByEmoticon (emoticon: string | undefined): string | unde
export function getUnicodeEmojiByShortCode (shortcode: string | undefined, skinTone?: number): Emoji | undefined {
const emoji = getEmojiByShortCode(shortcode, skinTone)
if (emoji === undefined || isCustomEmoji(emoji)) return undefined
+ return emoji
}
export function getEmojiByShortCode (shortcode: string | undefined, skinTone?: number): ExtendedEmoji | undefined {
if (shortcode === undefined) return undefined
+ const pureShortcode = shortcode.replaceAll(':', '')
return findEmoji((e) => {
- if (isCustomEmoji(e)) return e.shortcode === shortcode
- return e.shortcodes?.includes(shortcode.replaceAll(':', ''))
+ if (isCustomEmoji(e)) return e.shortcode === pureShortcode
+ return e.shortcodes?.includes(pureShortcode)
}, skinTone)
}
diff --git a/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte b/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte
index 2576c0e3e7..daa9b0930e 100644
--- a/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte
+++ b/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte
@@ -178,8 +178,8 @@
insertText: (text) => {
editor?.commands.insertContent(text)
},
- insertEmoji: (emoji) => {
- editor?.commands.insertEmoji(emoji)
+ insertEmoji: (text: string, url?: string) => {
+ editor?.commands.insertEmoji(text, url === undefined ? 'unicode' : 'custom')
},
insertMarkup: (markup) => {
editor?.commands.insertContent(markupToJSON(markup))
diff --git a/plugins/text-editor-resources/src/components/ReferenceInput.svelte b/plugins/text-editor-resources/src/components/ReferenceInput.svelte
index e73bd8003e..1a73097f58 100644
--- a/plugins/text-editor-resources/src/components/ReferenceInput.svelte
+++ b/plugins/text-editor-resources/src/components/ReferenceInput.svelte
@@ -84,8 +84,8 @@
insertText: (text) => {
editor?.insertText(text)
},
- insertEmoji: (emoji) => {
- editor?.insertEmoji(emoji)
+ insertEmoji: (text: string, url?: string) => {
+ editor?.insertEmoji(text, url)
},
insertMarkup: (markup) => {
editor?.insertMarkup(markup)
diff --git a/plugins/text-editor-resources/src/components/StyledTextEditor.svelte b/plugins/text-editor-resources/src/components/StyledTextEditor.svelte
index 7831e59cc2..e413d40dac 100644
--- a/plugins/text-editor-resources/src/components/StyledTextEditor.svelte
+++ b/plugins/text-editor-resources/src/components/StyledTextEditor.svelte
@@ -79,8 +79,8 @@
insertText: (text) => {
editor?.insertText(text)
},
- insertEmoji: (emoji) => {
- editor?.insertEmoji(emoji)
+ insertEmoji: (text: string, url?: string) => {
+ editor?.insertEmoji(text, url)
},
insertMarkup: (markup) => {
editor?.insertMarkup(markup)
diff --git a/plugins/text-editor-resources/src/components/TextEditor.svelte b/plugins/text-editor-resources/src/components/TextEditor.svelte
index 9e8984851b..d1ec7c4ef8 100644
--- a/plugins/text-editor-resources/src/components/TextEditor.svelte
+++ b/plugins/text-editor-resources/src/components/TextEditor.svelte
@@ -96,8 +96,8 @@
return editor
}
- export function insertEmoji (emoji: string): void {
- editor?.commands.insertEmoji(emoji)
+ export function insertEmoji (text: string, url?: string): void {
+ editor?.commands.insertEmoji(text, url === undefined ? 'unicode' : 'custom', url)
}
export function insertText (text: string): void {
diff --git a/plugins/text-editor-resources/src/components/editor/actions.ts b/plugins/text-editor-resources/src/components/editor/actions.ts
index e76b17e1da..fc8819dc73 100644
--- a/plugins/text-editor-resources/src/components/editor/actions.ts
+++ b/plugins/text-editor-resources/src/components/editor/actions.ts
@@ -27,7 +27,7 @@ export const defaultRefActions: RefAction[] = [
if (emoji === null || emoji === undefined) {
return
}
- editorHandler.insertEmoji(emoji.emoji)
+ editorHandler.insertEmoji(emoji.text, emoji.url)
editorHandler.focus()
},
() => {}
diff --git a/plugins/text-editor-resources/src/components/extension/emoji.ts b/plugins/text-editor-resources/src/components/extension/emoji.ts
index eeba837094..977279696f 100644
--- a/plugins/text-editor-resources/src/components/extension/emoji.ts
+++ b/plugins/text-editor-resources/src/components/extension/emoji.ts
@@ -37,16 +37,28 @@ function handleEmoji (
}
const emoji = getEmojiFunction(match.pop())
if (emoji === undefined) return
- // TODO: add custom emoji
- const unicodeEmoji = typeof emoji === 'string' ? emoji : (isCustomEmoji(emoji) ? emoji.shortcode : emoji.emoji)
- commands.insertContentAt(range, [
- {
- type: 'emoji',
- attrs: {
- emoji: unicodeEmoji
+ if (typeof emoji === 'string' || !isCustomEmoji(emoji)) {
+ commands.insertContentAt(range, [
+ {
+ type: 'emoji',
+ attrs: {
+ emoji: typeof emoji === 'string' ? emoji : emoji.emoji,
+ kind: 'unicode'
+ }
}
- }
- ])
+ ])
+ } else {
+ commands.insertContentAt(range, [
+ {
+ type: 'emoji',
+ attrs: {
+ emoji: emoji.shortcode,
+ kind: 'custom',
+ url: emoji.url
+ }
+ }
+ ])
+ }
}
export const EmojiExtension = EmojiNode.extend({
diff --git a/plugins/text-editor/src/types.ts b/plugins/text-editor/src/types.ts
index 43074d6298..292345ff3b 100644
--- a/plugins/text-editor/src/types.ts
+++ b/plugins/text-editor/src/types.ts
@@ -16,7 +16,7 @@ export type CollaboratorType = 'local' | 'cloud'
*/
export interface TextEditorHandler {
insertText: (html: string) => void
- insertEmoji: (emoji: string) => void
+ insertEmoji: (text: string, url?: string) => void
insertMarkup: (markup: Markup) => void
insertTemplate: (name: string, markup: string) => void
insertTable: (options: { rows?: number, cols?: number, withHeaderRow?: boolean }) => void