Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
22b2875b
Commit
22b2875b
authored
Jun 25, 2020
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
emoji-picker: Move picker content into another component
to avoid re-rendering them when non-relevant properties change
parent
bec476b6
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
175 additions
and
130 deletions
+175
-130
spec/emojis.js
spec/emojis.js
+8
-9
src/components/emoji-picker.js
src/components/emoji-picker.js
+132
-90
src/templates/emoji_picker.js
src/templates/emoji_picker.js
+35
-31
No files found.
spec/emojis.js
View file @
22b2875b
...
...
@@ -54,11 +54,11 @@ describe("Emojis", function () {
}
view
.
onKeyDown
(
tab_event
);
await
u
.
waitUntil
(()
=>
u
.
isVisible
(
view
.
el
.
querySelector
(
'
.emoji-picker__lists
'
)));
le
t
picker
=
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.emoji-picker__container
'
));
cons
t
picker
=
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.emoji-picker__container
'
));
const
input
=
picker
.
querySelector
(
'
.emoji-search
'
);
expect
(
input
.
value
).
toBe
(
'
:gri
'
);
await
u
.
waitUntil
(()
=>
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
).
length
===
3
,
1000
);
let
visible_emojis
=
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
);
expect
(
visible_emojis
.
length
).
toBe
(
3
);
expect
(
visible_emojis
[
0
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:grimacing:
'
);
expect
(
visible_emojis
[
1
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:grin:
'
);
expect
(
visible_emojis
[
2
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:grinning:
'
);
...
...
@@ -66,8 +66,8 @@ describe("Emojis", function () {
// Test that TAB autocompletes the to first match
input
.
dispatchEvent
(
new
KeyboardEvent
(
'
keydown
'
,
tab_event
));
await
u
.
waitUntil
(()
=>
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
).
length
===
1
);
visible_emojis
=
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
);
await
u
.
waitUntil
(()
=>
sizzle
(
"
.emojis-lists__container--search .insert-emoji:not('.hidden')
"
,
picker
).
length
===
1
,
1000
);
visible_emojis
=
sizzle
(
"
.emojis-lists__container--search .insert-emoji:not('.hidden')
"
,
picker
);
expect
(
visible_emojis
[
0
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:grimacing:
'
);
expect
(
input
.
value
).
toBe
(
'
:grimacing:
'
);
...
...
@@ -95,8 +95,7 @@ describe("Emojis", function () {
textarea
.
value
=
'
:use
'
;
view
.
onKeyDown
(
tab_event
);
await
u
.
waitUntil
(()
=>
u
.
isVisible
(
view
.
el
.
querySelector
(
'
.emoji-picker__lists
'
)));
picker
=
await
u
.
waitUntil
(()
=>
view
.
el
.
querySelector
(
'
.emoji-picker__container
'
));
expect
(
input
.
value
).
toBe
(
'
:use
'
);
await
u
.
waitUntil
(()
=>
input
.
value
===
'
:use
'
);
visible_emojis
=
sizzle
(
'
.insert-emoji:not(.hidden)
'
,
picker
);
expect
(
visible_emojis
.
length
).
toBe
(
0
);
done
();
...
...
@@ -129,8 +128,8 @@ describe("Emojis", function () {
input
.
dispatchEvent
(
new
KeyboardEvent
(
'
keydown
'
,
event
));
await
u
.
waitUntil
(()
=>
view
.
emoji_picker_view
.
model
.
get
(
'
query
'
)
===
'
smiley
'
,
1000
);
await
u
.
waitUntil
(()
=>
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
).
length
===
2
,
1000
);
let
visible_emojis
=
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
);
expect
(
visible_emojis
.
length
).
toBe
(
2
);
expect
(
visible_emojis
[
0
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:smiley:
'
);
expect
(
visible_emojis
[
1
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:smiley_cat:
'
);
...
...
@@ -144,8 +143,8 @@ describe("Emojis", function () {
input
.
dispatchEvent
(
new
KeyboardEvent
(
'
keydown
'
,
tab_event
));
await
u
.
waitUntil
(()
=>
input
.
value
===
'
:smiley:
'
);
visible_emojis
=
sizzle
(
'
.emojis-lists__container--search .insert-emoji
'
,
picker
);
expect
(
visible_emojis
.
length
).
toBe
(
1
);
await
u
.
waitUntil
(()
=>
sizzle
(
"
.emojis-lists__container--search .insert-emoji:not('.hidden')
"
,
picker
).
length
===
1
);
visible_emojis
=
sizzle
(
"
.emojis-lists__container--search .insert-emoji:not('.hidden')
"
,
picker
);
expect
(
visible_emojis
[
0
].
getAttribute
(
'
data-emoji
'
)).
toBe
(
'
:smiley:
'
);
// Check that ENTER now inserts the match
...
...
src/components/emoji-picker.js
View file @
22b2875b
import
DOMNavigator
from
"
../dom-navigator
"
;
import
sizzle
from
'
sizzle
'
;
import
tpl_emoji_picker
from
"
../templates/emoji_picker.js
"
;
import
{
CustomElement
}
from
'
./element.js
'
;
import
{
_converse
,
converse
}
from
"
@converse/headless/converse-core
"
;
import
{
debounce
,
find
}
from
"
lodash-es
"
;
import
{
html
}
from
"
lit-element
"
;
import
{
tpl_all_emojis
,
tpl_emoji_picker
,
tpl_search_results
}
from
"
../templates/emoji_picker.js
"
;
const
u
=
converse
.
env
.
utils
;
export
class
EmojiPickerContent
extends
CustomElement
{
static
get
properties
()
{
return
{
'
chatview
'
:
{
type
:
Object
},
'
search_results
'
:
{
type
:
Array
},
'
current_skintone
'
:
{
type
:
String
},
'
model
'
:
{
type
:
Object
},
'
query
'
:
{
type
:
String
},
}
}
render
()
{
const
props
=
{
'
current_skintone
'
:
this
.
current_skintone
,
'
insertEmoji
'
:
ev
=>
this
.
insertEmoji
(
ev
),
'
query
'
:
this
.
query
,
'
search_results
'
:
this
.
search_results
,
'
shouldBeHidden
'
:
shortname
=>
this
.
shouldBeHidden
(
shortname
),
}
return
html
`
<div class="emoji-picker__lists">
${
tpl_search_results
(
props
)}
${
tpl_all_emojis
(
props
)}
</div>
`
;
}
firstUpdated
()
{
this
.
initIntersectionObserver
();
}
initIntersectionObserver
()
{
if
(
!
window
.
IntersectionObserver
)
{
return
;
}
if
(
this
.
observer
)
{
this
.
observer
.
disconnect
();
}
else
{
const
options
=
{
root
:
this
.
querySelector
(
'
.emoji-picker__lists
'
),
threshold
:
[
0.1
]
}
const
handler
=
ev
=>
this
.
setCategoryOnVisibilityChange
(
ev
);
this
.
observer
=
new
IntersectionObserver
(
handler
,
options
);
}
sizzle
(
'
.emoji-picker
'
,
this
).
forEach
(
a
=>
this
.
observer
.
observe
(
a
));
}
setCategoryOnVisibilityChange
(
ev
)
{
const
selected
=
this
.
parentElement
.
navigator
.
selected
;
const
intersection_with_selected
=
ev
.
filter
(
i
=>
i
.
target
.
contains
(
selected
)).
pop
();
let
current
;
// Choose the intersection that contains the currently selected
// element, or otherwise the one with the largest ratio.
if
(
intersection_with_selected
)
{
current
=
intersection_with_selected
;
}
else
{
current
=
ev
.
reduce
((
p
,
c
)
=>
c
.
intersectionRatio
>=
(
p
?.
intersectionRatio
||
0
)
?
c
:
p
,
null
);
}
if
(
current
&&
current
.
isIntersecting
)
{
const
category
=
current
.
target
.
getAttribute
(
'
data-category
'
);
if
(
category
!==
this
.
model
.
get
(
'
current_category
'
))
{
this
.
parentElement
.
preserve_scroll
=
true
;
this
.
model
.
save
({
'
current_category
'
:
category
});
}
}
}
insertEmoji
(
ev
)
{
ev
.
preventDefault
();
ev
.
stopPropagation
();
const
target
=
ev
.
target
.
nodeName
===
'
IMG
'
?
ev
.
target
.
parentElement
:
ev
.
target
;
const
replace
=
this
.
model
.
get
(
'
autocompleting
'
);
const
position
=
this
.
model
.
get
(
'
position
'
);
this
.
model
.
set
({
'
autocompleting
'
:
null
,
'
position
'
:
null
,
'
query
'
:
''
});
this
.
chatview
.
insertIntoTextArea
(
target
.
getAttribute
(
'
data-emoji
'
),
replace
,
false
,
position
);
this
.
chatview
.
emoji_dropdown
.
toggle
();
}
shouldBeHidden
(
shortname
)
{
// Helper method for the template which decides whether an
// emoji should be hidden, based on which skin tone is
// currently being applied.
if
(
shortname
.
includes
(
'
_tone
'
))
{
if
(
!
this
.
current_skintone
||
!
shortname
.
includes
(
this
.
current_skintone
))
{
return
true
;
}
}
else
{
if
(
this
.
current_skintone
&&
_converse
.
emojis
.
toned
.
includes
(
shortname
))
{
return
true
;
}
}
if
(
this
.
query
&&
!
_converse
.
FILTER_CONTAINS
(
shortname
,
this
.
query
))
{
return
true
;
}
return
false
;
}
}
export
class
EmojiPicker
extends
CustomElement
{
static
get
properties
()
{
...
...
@@ -22,9 +123,8 @@ export class EmojiPicker extends CustomElement {
constructor
()
{
super
();
this
.
debouncedFilter
=
debounce
(
input
=>
this
.
model
.
set
({
'
query
'
:
input
.
value
}),
500
);
this
.
preserve_scroll
=
false
;
this
.
_search_results
=
[];
this
.
search_results
=
[];
this
.
debouncedFilter
=
debounce
(
input
=>
this
.
model
.
set
({
'
query
'
:
input
.
value
}),
250
);
this
.
onGlobalKeyDown
=
ev
=>
this
.
_onGlobalKeyDown
(
ev
);
const
body
=
document
.
querySelector
(
'
body
'
);
body
.
addEventListener
(
'
keydown
'
,
this
.
onGlobalKeyDown
);
...
...
@@ -32,47 +132,59 @@ export class EmojiPicker extends CustomElement {
render
()
{
return
tpl_emoji_picker
({
'
chatview
'
:
this
.
chatview
,
'
current_category
'
:
this
.
current_category
,
'
current_skintone
'
:
this
.
current_skintone
,
'
model
'
:
this
.
model
,
'
onCategoryPicked
'
:
ev
=>
this
.
chooseCategory
(
ev
),
'
onEmojiPicked
'
:
ev
=>
this
.
insertEmoji
(
ev
),
'
onSearchInputBlurred
'
:
ev
=>
this
.
chatview
.
emitFocused
(
ev
),
'
onSearchInputFocus
'
:
ev
=>
this
.
onSearchInputFocus
(
ev
),
'
onSearchInputKeyDown
'
:
ev
=>
this
.
onKeyDown
(
ev
),
'
onSkintonePicked
'
:
ev
=>
this
.
chooseSkinTone
(
ev
),
'
query
'
:
this
.
query
,
'
search_results
'
:
this
.
search_results
,
'
shouldBeHidden
'
:
shortname
=>
this
.
shouldBeHidden
(
shortname
),
'
transformCategory
'
:
shortname
=>
u
.
shortnamesToEmojis
(
this
.
getTonedShortname
(
shortname
))
'
sn2Emoji
'
:
shortname
=>
u
.
shortnamesToEmojis
(
this
.
getTonedShortname
(
shortname
))
});
}
firstUpdated
()
{
this
.
initArrowNavigation
();
this
.
initIntersectionObserver
();
}
updated
(
changed
)
{
if
(
changed
.
has
(
'
current_category
'
)
&&
!
this
.
preserve_scroll
)
{
this
.
setScrollPosition
();
changed
.
has
(
'
query
'
)
&&
this
.
updateSearchResults
();
changed
.
has
(
'
current_category
'
)
&&
this
.
setScrollPosition
();
}
setScrollPosition
()
{
if
(
this
.
preserve_scroll
)
{
this
.
preserve_scroll
=
false
;
return
;
}
const
el
=
this
.
querySelector
(
'
.emoji-lists__container--browse
'
);
const
heading
=
this
.
querySelector
(
`#emoji-picker-
${
this
.
current_category
}
`
);
if
(
heading
)
{
// +4 due to 2px padding on list elements
el
.
scrollTop
=
heading
.
offsetTop
-
heading
.
offsetHeight
*
3
+
4
;
}
}
get
search_r
esults
()
{
updateSearchR
esults
()
{
const
contains
=
_converse
.
FILTER_CONTAINS
;
if
(
this
.
query
)
{
if
(
this
.
query
===
this
.
old_query
)
{
return
this
.
_
search_results
;
return
this
.
search_results
;
}
else
if
(
this
.
old_query
&&
this
.
query
.
includes
(
this
.
old_query
))
{
this
.
_search_results
=
this
.
_
search_results
.
filter
(
e
=>
contains
(
e
.
sn
,
this
.
query
));
this
.
search_results
=
this
.
search_results
.
filter
(
e
=>
contains
(
e
.
sn
,
this
.
query
));
}
else
{
this
.
_
search_results
=
_converse
.
emojis_list
.
filter
(
e
=>
contains
(
e
.
sn
,
this
.
query
));
this
.
search_results
=
_converse
.
emojis_list
.
filter
(
e
=>
contains
(
e
.
sn
,
this
.
query
));
}
this
.
old_query
=
this
.
query
;
}
else
{
this
.
_search_results
=
[];
}
else
if
(
this
.
search_results
.
length
)
{
// Avoid re-rendering by only setting to new empty array if it wasn't empty before
this
.
search_results
=
[];
}
return
this
.
_
search_results
;
return
this
.
search_results
;
}
disconnectedCallback
()
{
...
...
@@ -96,29 +208,14 @@ export class EmojiPicker extends CustomElement {
}
}
setCategoryForElement
(
el
,
preserve_scroll
=
false
)
{
setCategoryForElement
(
el
)
{
const
old_category
=
this
.
current_category
;
const
category
=
el
.
getAttribute
(
'
data-category
'
)
||
old_category
;
if
(
old_category
!==
category
)
{
this
.
preserve_scroll
=
preserve_scroll
;
this
.
model
.
save
({
'
current_category
'
:
category
});
}
}
setCategoryOnVisibilityChange
(
ev
)
{
const
selected
=
this
.
navigator
.
selected
;
const
intersection_with_selected
=
ev
.
filter
(
i
=>
i
.
target
.
contains
(
selected
)).
pop
();
let
current
;
// Choose the intersection that contains the currently selected
// element, or otherwise the one with the largest ratio.
if
(
intersection_with_selected
)
{
current
=
intersection_with_selected
;
}
else
{
current
=
ev
.
reduce
((
p
,
c
)
=>
c
.
intersectionRatio
>=
(
p
?.
intersectionRatio
||
0
)
?
c
:
p
,
null
);
}
current
&&
current
.
isIntersecting
&&
this
.
setCategoryForElement
(
current
.
target
,
true
);
}
insertIntoTextArea
(
value
)
{
const
replace
=
this
.
model
.
get
(
'
autocompleting
'
);
const
position
=
this
.
model
.
get
(
'
position
'
);
...
...
@@ -152,18 +249,6 @@ export class EmojiPicker extends CustomElement {
!
this
.
navigator
.
enabled
&&
this
.
navigator
.
enable
();
}
insertEmoji
(
ev
)
{
ev
.
preventDefault
();
ev
.
stopPropagation
();
const
target
=
ev
.
target
.
nodeName
===
'
IMG
'
?
ev
.
target
.
parentElement
:
ev
.
target
;
const
replace
=
this
.
model
.
get
(
'
autocompleting
'
);
const
position
=
this
.
model
.
get
(
'
position
'
);
this
.
model
.
set
({
'
autocompleting
'
:
null
,
'
position
'
:
null
});
this
.
chatview
.
insertIntoTextArea
(
target
.
getAttribute
(
'
data-emoji
'
),
replace
,
false
,
position
);
this
.
chatview
.
emoji_dropdown
.
toggle
();
this
.
model
.
set
({
'
query
'
:
''
});
}
onKeyDown
(
ev
)
{
if
(
ev
.
keyCode
===
converse
.
keycodes
.
TAB
)
{
if
(
ev
.
target
.
value
)
{
...
...
@@ -208,25 +293,6 @@ export class EmojiPicker extends CustomElement {
this
.
disableArrowNavigation
();
}
shouldBeHidden
(
shortname
)
{
// Helper method for the template which decides whether an
// emoji should be hidden, based on which skin tone is
// currently being applied.
if
(
shortname
.
includes
(
'
_tone
'
))
{
if
(
!
this
.
current_skintone
||
!
shortname
.
includes
(
this
.
current_skintone
))
{
return
true
;
}
}
else
{
if
(
this
.
current_skintone
&&
_converse
.
emojis
.
toned
.
includes
(
shortname
))
{
return
true
;
}
}
if
(
this
.
query
&&
!
_converse
.
FILTER_CONTAINS
(
shortname
,
this
.
query
))
{
return
true
;
}
return
false
;
}
getTonedShortname
(
shortname
)
{
if
(
_converse
.
emojis
.
toned
.
includes
(
shortname
)
&&
this
.
current_skintone
)
{
return
`
${
shortname
.
slice
(
0
,
shortname
.
length
-
1
)}
_
${
this
.
current_skintone
}
:`
...
...
@@ -234,23 +300,6 @@ export class EmojiPicker extends CustomElement {
return
shortname
;
}
initIntersectionObserver
()
{
if
(
!
window
.
IntersectionObserver
)
{
return
;
}
if
(
this
.
observer
)
{
this
.
observer
.
disconnect
();
}
else
{
const
options
=
{
root
:
this
.
querySelector
(
'
.emoji-picker__lists
'
),
threshold
:
[
0.1
]
}
const
handler
=
ev
=>
this
.
setCategoryOnVisibilityChange
(
ev
);
this
.
observer
=
new
IntersectionObserver
(
handler
,
options
);
}
sizzle
(
'
.emoji-picker
'
,
this
).
forEach
(
a
=>
this
.
observer
.
observe
(
a
));
}
initArrowNavigation
()
{
if
(
!
this
.
navigator
)
{
const
default_selector
=
'
li:not(.hidden):not(.emoji-skintone), .emoji-search
'
;
...
...
@@ -291,15 +340,8 @@ export class EmojiPicker extends CustomElement {
this
.
navigator
.
enable
();
this
.
navigator
.
handleKeydown
(
ev
);
}
setScrollPosition
()
{
const
el
=
this
.
querySelector
(
'
.emoji-lists__container--browse
'
);
const
heading
=
this
.
querySelector
(
`#emoji-picker-
${
this
.
current_category
}
`
);
if
(
heading
)
{
// +4 due to 2px padding on list elements
el
.
scrollTop
=
heading
.
offsetTop
-
heading
.
offsetHeight
*
3
+
4
;
}
}
}
window
.
customElements
.
define
(
'
converse-emoji-picker
'
,
EmojiPicker
);
window
.
customElements
.
define
(
'
converse-emoji-picker-content
'
,
EmojiPickerContent
);
src/templates/emoji_picker.js
View file @
22b2875b
...
...
@@ -18,26 +18,26 @@ const emoji_category = (o) => {
<a class="pick-category"
@click=
${
o
.
onCategoryPicked
}
href="#emoji-picker-
${
o
.
category
}
"
data-category="
${
o
.
category
}
">
${
o
.
transformCategory
(
o
.
emoji_categories
[
o
.
category
])
}
</a>
data-category="
${
o
.
category
}
">
${
o
.
emoji
}
</a>
</li>
`
;
}
const
emoji_picker_header
=
(
o
)
=>
html
`
<ul>
${
Object
.
keys
(
o
.
emoji_categories
).
map
(
category
=>
(
o
.
emoji_categories
[
category
]
?
emoji_category
(
Object
.
assign
({
category
},
o
))
:
''
))
}
</ul>
`
;
const
emoji_picker_header
=
(
o
)
=>
{
const
cats
=
api
.
settings
.
get
(
'
emoji_categories
'
);
const
transform
=
c
=>
cats
[
c
]
?
emoji_category
(
Object
.
assign
({
'
category
'
:
c
,
'
emoji
'
:
o
.
sn2Emoji
(
cats
[
c
])},
o
))
:
''
;
return
html
`<ul>
${
Object
.
keys
(
cats
).
map
(
transform
)
}
</ul>`
;
}
const
emoji_item
=
(
o
)
=>
{
return
html
`
<li class="emoji insert-emoji
${
o
.
shouldBeHidden
(
o
.
emoji
.
sn
)
?
'
hidden
'
:
''
}
" data-emoji="
${
o
.
emoji
.
sn
}
" title="
${
o
.
emoji
.
sn
}
">
<a href="#" @click=
${
o
.
onEmojiPicked
}
data-emoji="
${
o
.
emoji
.
sn
}
">
${
u
.
shortnamesToEmojis
(
o
.
emoji
.
sn
)}
</a>
<a href="#" @click=
${
o
.
insertEmoji
}
data-emoji="
${
o
.
emoji
.
sn
}
">
${
u
.
shortnamesToEmojis
(
o
.
emoji
.
sn
)}
</a>
</li>
`
;
}
const
search_results
=
(
o
)
=>
html
`
export
const
tpl_
search_results
=
(
o
)
=>
html
`
<span ?hidden=
${
!
o
.
query
}
class="emoji-lists__container emojis-lists__container--search">
<a id="emoji-picker-search-results" class="emoji-category__heading">
${
i18n_search_results
}
</a>
<ul class="emoji-picker">
...
...
@@ -46,33 +46,33 @@ const search_results = (o) => html`
</span>
`
;
const
emojis_for_category
=
(
o
)
=>
html
`
<a id="emoji-picker-
${
o
.
category
}
" class="emoji-category__heading" data-category="
${
o
.
category
}
">
${
__
(
api
.
settings
.
get
(
'
emoji_category_labels
'
)[
o
.
category
])
}
</a>
<ul class="emoji-picker" data-category="
${
o
.
category
}
">
${
Object
.
values
(
o
.
emojis_by_category
[
o
.
category
]).
map
(
emoji
=>
emoji_item
(
Object
.
assign
({
emoji
},
o
)))
}
</ul>
`
;
const
emojis_for_category
=
(
o
)
=>
{
const
emojis_by_category
=
_converse
.
emojis
.
json
;
return
html
`
<a id="emoji-picker-
${
o
.
category
}
" class="emoji-category__heading" data-category="
${
o
.
category
}
">
${
__
(
api
.
settings
.
get
(
'
emoji_category_labels
'
)[
o
.
category
])
}
</a>
<ul class="emoji-picker" data-category="
${
o
.
category
}
">
${
Object
.
values
(
emojis_by_category
[
o
.
category
]).
map
(
emoji
=>
emoji_item
(
Object
.
assign
({
emoji
},
o
)))
}
</ul>`
;
}
export
const
tpl_all_emojis
=
(
o
)
=>
{
const
cats
=
api
.
settings
.
get
(
'
emoji_categories
'
);
return
html
`
<span ?hidden=
${
o
.
query
}
class="emoji-lists__container emoji-lists__container--browse">
${
Object
.
keys
(
cats
).
map
(
c
=>
(
cats
[
c
]
?
emojis_for_category
(
Object
.
assign
({
'
category
'
:
c
},
o
))
:
''
))}
</span>`
;
}
const
skintone_emoji
=
(
o
)
=>
{
return
html
`
<li data-skintone="
${
o
.
skintone
}
" class="emoji-skintone
${(
o
.
current_skintone
===
o
.
skintone
)
?
'
picked
'
:
''
}
">
<a class="pick-skintone" href="#" data-skintone="
${
o
.
skintone
}
" @click=
${
o
.
onSkintonePicked
}
>
${
u
.
shortnamesToEmojis
(
'
:
'
+
o
.
skintone
+
'
:
'
)}
</a>
</li>
`
;
</li>`
;
}
const
all_emojis
=
(
o
)
=>
html
`
<span ?hidden=
${
o
.
query
}
class="emoji-lists__container emoji-lists__container--browse">
${
Object
.
keys
(
o
.
emoji_categories
).
map
(
category
=>
(
o
.
emoji_categories
[
category
]
?
emojis_for_category
(
Object
.
assign
({
category
},
o
))
:
''
))}
</span>
`
;
export
default
(
o
)
=>
{
o
.
emoji_categories
=
api
.
settings
.
get
(
'
emoji_categories
'
);
o
.
emojis_by_category
=
_converse
.
emojis
.
json
;
o
.
toned_emojis
=
_converse
.
emojis
.
toned
;
export
const
tpl_emoji_picker
=
(
o
)
=>
{
return
html
`
<div class="emoji-picker__header">
<input class="form-control emoji-search" name="emoji-search" placeholder="
${
i18n_search
}
"
...
...
@@ -82,10 +82,14 @@ export default (o) => {
@focus=
${
o
.
onSearchInputFocus
}
>
${
o
.
query
?
''
:
emoji_picker_header
(
o
)
}
</div>
<div class="emoji-picker__lists">
${
search_results
(
o
)}
${
all_emojis
(
o
)}
</div>
<converse-emoji-picker-content
.chatview=
${
o
.
chatview
}
.model=
${
o
.
model
}
.search_results="
${
o
.
search_results
}
"
current_skintone="
${
o
.
current_skintone
}
"
query="
${
o
.
query
}
"
></converse-emoji-picker-content>
<div class="emoji-skintone-picker">
<label>Skin tone</label>
<ul>
${
skintones
.
map
(
skintone
=>
skintone_emoji
(
Object
.
assign
({
skintone
},
o
)))
}
</ul>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment