<style>
:host {
/* Values overriden in Javascript from constants.js. */
--emoji-group-button-size: 0;
--emoji-picker-height: 0;
--emoji-picker-side-padding: 0;
--emoji-picker-top-padding: 0;
--emoji-picker-width: 0;
--emoji-size: 0;
--emoji-spacing: 0;
--emoji-per-row: 0;
/* Values set in css only. */
--emoji-group-clear-recents-icon-size: 16px;
--emoji-group-heading-padding-bottom: 6px;
--emoji-group-heading-padding-top: 10px;
--emoji-group-heading-size: 32px;
--emoji-group-tabs-left: 0;
--emoji-hover-background: var(--cros-ripple-color);
--emoji-picker-bottom-padding: 14px;
--emoji-picker-last-emoji-left: calc(var(--emoji-picker-side-padding)
+ var(--emoji-size) * (var(--emoji-per-row) - 1)
+ var(--emoji-spacing) * (var(--emoji-per-row) - 1));
--emoji-picker-group-button-padding: 8px;
--emoji-picker-group-button-border-radius: 4px;
--emoji-picker-group-button-icon-size: 24px;
--emoji-picker-group-button-height: 48px;
--emoji-picker-group-highlight-bar-width: 24px;
--emoji-picker-subcategory-bar-inline-margin: 18px;
--emoji-picker-tabs-vertical-padding: 0px;
--emoji-tooltip-delay-in: 500ms;
--emoji-tooltip-delay-out: 500ms;
/**
* Scemantic color css variables
*/
--emoji-picker-category-header-color: var(--cros-sys-on_surface_variant);
--emoji-picker-container-color: var(--cros-sys-base_elevated);
--emoji-picker-focus-ring-color: var(--cros-sys-focus_ring);
--emoji-picker-icon-button-icon-color: var(--cros-sys-on_surface);
--emoji-picker-icon-toggle-unselected-color: var(--cros-sys-on_surface);
--emoji-picker-icon-toggle-selected-color: var(--cros-sys-primary);
--emoji-picker-icon-toggle-selected-container-color: var(--cros-sys-app_base_shaded);
--emoji-picker-illustration-text-color: var(--cros-sys-on_surface_variant);
--emoji-picker-illustration-link-color: var(--cros-sys-primary);
--emoji-picker-nudge-background-color: var(--cros-sys-primary);
--emoji-picker-nudge-icon-color: var(--cros-sys-on_primary);
--emoji-picker-search-field-clear-icon-color: var(--cros-sys-secondary);
--emoji-picker-search-field-placeholder-color: var(--cros-sys-secondary);
--emoji-picker-search-field-search-icon-color: var(--cros-sys-secondary);
--emoji-picker-search-field-container-color: var(--cros-sys-input_field_on_base);
--emoji-picker-symbol-color: var(--cros-sys-on_surface);
--emoji-picker-tab-selected-color: var(--cros-sys-primary);
--emoji-picker-tab-unselected-color: var(--cros-sys-on_surface_variant);
/* Actual properties of the emoji picker */
background-color: var(--emoji-picker-container-color);
display: flex;
flex-direction: column;
font: var(--cros-body-2-font);
height: calc(var(--emoji-picker-height) - var(--emoji-picker-top-padding));
margin-top: 0;
padding-top: var(--emoji-picker-top-padding);
width: var(--emoji-picker-width);
}
.search-side-padding,
.side-padding {
flex-grow: 0;
flex-shrink: 0;
max-height: 100%;
overflow-y: scroll;
}
.search-side-padding {
padding-inline-end: var(--emoji-picker-search-side-padding);
padding-inline-start: var(--emoji-picker-search-side-padding);
}
.side-padding {
padding-inline-end: var(--emoji-picker-side-padding);
padding-inline-start: var(--emoji-picker-side-padding);
}
#search-container,
#list-container {
display: flex;
flex-direction: column;
overflow-y: clip;
min-height: 0;
}
#list-container {
margin-top: -6px;
}
#search-container {
flex-grow: 0;
}
#tabs {
display: grid;
grid-gap: var(--emoji-spacing);
/**
* The number of repetition here should be aligned with the maximum number
* of paginations. Theoretically, we can put MAX_INT here but 100 seems big
* enough for our case for now.
*
* TODO(b/295426497): Find a better way rather than having a magic number.
*/
grid-template-columns: repeat(100,1fr);
left: var(--emoji-group-tabs-left);
overflow-x: hidden;
padding-top: var(--emoji-picker-tabs-vertical-padding, 6px);
padding-bottom: var(--emoji-picker-tabs-vertical-padding, 6px);
scroll-behavior: smooth;
scroll-padding: var(--emoji-size);
scroll-snap-type: x mandatory;
width: 100%;
}
#tabs::-webkit-scrollbar {
display: none;
}
#tabs::-webkit-scrollbar {
display: none;
}
#groups {
flex-grow: 1;
flex-shrink: 1;
overflow-y: scroll;
padding-bottom: var(--emoji-picker-bottom-padding);
}
.chevron {
--cr-icon-button-fill-color: var(--emoji-picker-icon-button-icon-color);
--cr-icon-button-focus-outline-color: var(--emoji-picker-focus-ring-color);
--cr-icon-button-icon-size: 20px;
--cr-icon-button-size: 28px;
background-color: var(--emoji-picker-container-color);
border-radius: 0;
margin: 0;
padding: 0;
position: absolute;
width: var(--emoji-size);
z-index: 2;
}
#right-chevron {
left: var(--emoji-picker-last-emoji-left);
/* Icons may become visible to the right of this during scrolling without
* the additional padding.
*/
padding-inline-end: var(--emoji-picker-side-padding);
}
#left-chevron {
display: none;
left: var(--emoji-picker-side-padding);
}
.divider {
border-top: 1px solid var(--cros-separator-color);
}
.sr-only {
color: transparent;
position: absolute;
z-index: -2;
}
.hidden {
display: none;
}
/**
* The highlight bar should be on-top of the tab buttons.
*/
#bar-container {
margin-top: -4px;
}
#bar-container > #bar {
height: 4px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
#bar {
background-color: var(--emoji-picker-tab-selected-color);
border-top-left-radius: 2px;
border-top-right-radius: 2px;
/* This gets made visible after we determine the correct location by loading
* history.
*/
display: none;
flex-shrink: 0;
height: 2px;
/* Left needed so that transition end fires when it changes for the first
* time.
*/
left: 0;
padding-inline-end: calc((var(--emoji-size)
- var(--emoji-picker-group-highlight-bar-width)) / 2);
padding-inline-start: calc((var(--emoji-size)
- var(--emoji-picker-group-highlight-bar-width)) / 2);
position: relative;
width: var(--emoji-picker-group-highlight-bar-width);
}
.fake {
flex-shrink: 0;
width: var(--emoji-size);
}
/* Invisible dummy tab used to dynamically calculate width of GIF tabs */
#dummyTab {
border: 0;
height: 1em;
line-height: 0;
margin: 0;
padding: 0;
position: absolute;
visibility: hidden;
}
.sr-only {
user-select: none;
}
.pagination {
align-items: center;
display: flex;
width: var(--emoji-picker-width);
}
:host(:not([text-subcategory-bar-enabled])) #tabs {
grid-gap: var(--emoji-group-spacing);
}
:host(:not([text-subcategory-bar-enabled])) #left-chevron {
display: none;
}
:host(:not([text-subcategory-bar-enabled])) #right-chevron {
display: none;
}
:host([text-subcategory-bar-enabled]) .chevron {
--cr-icon-button-size: var(--emoji-size);
width: var(--emoji-size);
}
:host([text-subcategory-bar-enabled]) #left-chevron {
left: var(--emoji-picker-subcategory-bar-inline-margin);
margin-inline-end: var(--tab-button-margin);
border-radius: var(--emoji-picker-group-button-border-radius, 0px);
}
:host([text-subcategory-bar-enabled]) #right-chevron {
left: calc(var(--emoji-picker-width) - var(--emoji-size)
- var(--emoji-picker-subcategory-bar-inline-margin));
padding-inline-end: 0;
--cr-icon-button-fill-color: var(--emoji-picker-icon-button-icon-color);
border-radius: var(--emoji-picker-group-button-border-radius, 0px);
}
/* Center vertically the history icon and text buttons.*/
:host([text-subcategory-bar-enabled]) #tabs {
align-items: center;
grid-gap: unset;
}
:host([text-subcategory-bar-enabled]) #bar {
margin-inline: 9px;
padding-inline: 0;
}
:host(:not([text-subcategory-bar-enabled])) #bar {
/*
* Introduce some margin to align the highlight bar vertically centered with
* the emoji button.
*/
margin-inline: 4px;
padding-inline: 0;
}
.category-gap {
height: 16px;
}
#list-container.error-only {
flex: 1;
}
#list-container.error-only>:not(#groups,#dummyTab) {
display: none;
}
#list-container.error-only>#groups {
display: block;
flex-grow: 0;
margin: auto;
}
#list-container.error-only>#groups> :not(emoji-error) {
display: none;
}
#list-container.error-only emoji-error {
display: block;
}
#list-container.no-gif emoji-group[category="gif"] {
display: none;
}
</style>
<div class="sr-only" aria-live="polite">
Insert emoji by activating them. Close with escape.
Some emoji have variants which can be opened with context menu.
Use the emoji group buttons or headings to jump to emoji groups.
</div>
<div id="message" class="sr-only" aria-live="polite"></div>
<emoji-search
class="search-side-padding"
id="search-container"
use-mojo-search="[[useMojoSearch]]"
categories-data="[[categoriesData]]"
lazy-indexing="[[searchLazyIndexing]]"
on-scroll="onSearchScroll"
category-metadata="[[getCategoryMetadata(gifSupport, category)]]"
gif-support$="[[gifSupport]]"
seal-support$="[[sealSupport]]"
close-gif-nudge-overlay="[[closeGifNudgeOverlay]]"
use-grouped-preference="[[variantGroupingSupport]]"
global-tone="[[globalTone]]"
global-gender="[[globalGender]]">
</emoji-search>
<div id="list-container" class$="[[computeListContainerClass(category, status)]]">
<div class="sr-only" role="heading" aria-level="1">Emoji Group Buttons</div>
<div class="side-padding">
<div id="tabs" on-scroll="onGroupsScroll">
<cr-icon-button id="left-chevron" class="chevron"
aria-label$="[[getLeftChevronAriaLabel(gifSupport)]]"
on-click="onLeftChevronClick"
iron-icon="emoji_picker:keyboard_arrow_left">
</cr-icon-button>
<template is="dom-if" if="[[!textSubcategoryBarEnabled]]">
<template is="dom-repeat" items="[[emojiGroupTabs]]">
<emoji-group-button data-group$="[[item.groupId]]"
group-id="[[item.groupId]]"
active="[[item.active]]"
disabled="[[item.disabled]]"
icon="[[item.icon]]" name="[[item.name]]"
on-focus="preventV2TabScrolling"
custom-tab-index="[[getTabIndex(item.pagination,pagination)]]">>
</emoji-group-button>
</template>
<!--Fake group button to increase maximum scrolling, need an icon to
render as invisible-->
<div class="fake"
tabindex="-1">
</div>
<div class="fake"
tabindex="-1">
</div>
<div class="fake"
tabindex="-1">
</div>
<div class="fake"
tabindex="-1">
</div>
<div class="fake"
tabindex="-1">
</div>
<div class="fake" id="RightChevronScrollTarget"
tabindex="-1">
</div>
</template>
<template is="dom-if" if="[[textSubcategoryBarEnabled]]">
<div class="pagination">
<!-- Render history tab. -->
<emoji-group-button data-group$="[[emojiGroupTabs.0.groupId]]"
group-id="[[emojiGroupTabs.0.groupId]]"
active="[[emojiGroupTabs.0.active]]"
disabled="[[emojiGroupTabs.0.disabled]]"
icon="[[emojiGroupTabs.0.icon]]"
name="[[emojiGroupTabs.0.name]]"
class="tab"
on-focus="preventV2TabScrolling"
custom-tab-index="[[getTabIndex(item.pagination,pagination)]]">
</emoji-group-button>
<!-- Render non history tab. -->
<!-- If text-group-button is changed, update dummyTab below. -->
<template is="dom-repeat"
items="[[emojiGroupTabs]]"
filter="[[filterGroupTabByPagination(1)]]">
<text-group-button data-group$="[[item.groupId]]"
group-id="[[item.groupId]]"
active="[[item.active]]"
disabled="[[item.disabled]]"
name="[[item.name]]"
class="tab"
on-focus="preventV2TabScrolling"
custom-tab-index=
"[[getTabIndex(item.pagination,pagination)]]">
</text-group-button>
</template>
</div>
<template is="dom-repeat" as="pageNumber" filter="isNotFirstPage"
items="[[getPaginationArray(emojiGroupTabs)]]">
<div class="pagination">
<template is="dom-repeat"
items="[[emojiGroupTabs]]"
filter="[[filterGroupTabByPagination(pageNumber)]]">
<text-group-button data-group$="[[item.groupId]]"
group-id="[[item.groupId]]"
active="[[item.active]]"
disabled="[[item.disabled]]"
name="[[item.name]]"
class="tab"
on-focus="preventV2TabScrolling"
custom-tab-index=
"[[getTabIndex(item.pagination,pagination)]]">
</text-group-button>
</template>
</div>
</template>
</template>
<cr-icon-button id="right-chevron" class="chevron"
aria-label$="[[getRightChevronAriaLabel(gifSupport)]]"
on-click="onRightChevronClick"
on-keydown="onRightChevronKeyDown"
iron-icon="emoji_picker:keyboard_arrow_right">
</cr-icon-button>
</div>
</div>
<div class="sr-only" role="heading" aria-level="1">Emoji Groups</div>
<div class="side-padding" id="bar-container">
<div id="bar"
on-transitionend="onBarTransitionEnd"
on-transitionstart="onBarTransitionStart"></div>
</div>
<div class="divider"></div>
<div class="side-padding" id="groups" on-scroll="onEmojiScroll">
<template is="dom-repeat" items="[[categoriesGroupElements]]">
<div data-group$="[[item.groupId]]">
<emoji-group data="[[item.emoji]]"
gif-support$="[[gifSupport]]"
group="[[item.name]]"
global-tone="[[globalTone]]"
global-gender="[[globalGender]]"
preferred="[[item.preferences]]"
clearable$="[[item.isHistory]]"
use-grouped-preference="[[shouldUseGroupedPreference(item.isHistory)]]"
category$="[[item.category]]"
class$="[[getEmojiGroupClassNames(item.emoji,item,category,activeInfiniteGroupId)]]">
</emoji-group>
</div>
</template>
<template is="dom-if" if="[[isGifInErrorState(status)]]">
<emoji-error
status="[[status]]"
error-message="[[errorMessage]]">
</emoji-error>
</template>
</div>
<!-- Render invisible dummy tab to temporarily calculate width of a tab. -->
<div id="dummyTab">
<text-group-button data-group$="[[dummyTab]]"
group-id="[[dummyTab.groupId]]"
active="[[dummyTab.active]]"
disabled="[[dummyTab.disabled]]"
name="[[dummyTab.name]]"
class="tab"
on-focus="preventV2TabScrolling"
custom-tab-index=
"[[getTabIndex(dummyTab.pagination,pagination)]]">
</text-group-button>
</div>
</div>
<template is="dom-if" if="[[showGifNudgeOverlay]]">
<emoji-gif-nudge-overlay close-overlay="[[closeGifNudgeOverlay]]">
</template>