@dialpad/dialtone-vue
Version:
Vue component library for Dialpad's design system Dialtone
452 lines (451 loc) • 17.6 kB
JavaScript
import { emojisGrouped } from "@dialpad/dialtone-emojis";
import { CDN_URL, EMOJIS_PER_ROW } from "../emoji_picker_constants.js";
import normalizeComponent from "../../../_virtual/_plugin-vue2_normalizer.js";
const _sfc_main = {
name: "EmojiSelector",
props: {
emojiFilter: {
type: String,
default: ""
},
skinTone: {
type: String,
required: true
},
tabSetLabels: {
type: Array,
required: true
},
selectedTabset: {
type: Object,
required: true
},
searchResultsLabel: {
type: String,
required: true
},
searchNoResultsLabel: {
type: String,
required: true
},
recentlyUsedEmojis: {
type: Array,
default: () => []
}
},
data() {
return {
tabLabelsRefs: [],
emojiRefs: [],
emojiFilteredRefs: [],
isFiltering: false,
hoverFirstEmoji: true,
fixedLabel: "",
filteredEmojis: [],
TABS_DATA: ["Recently used", "People", "Nature", "Food", "Activity", "Travel", "Objects", "Symbols", "Flags"],
tabLabelObserver: null
};
},
computed: {
/* eslint-disable-next-line complexity */
currentEmojis() {
return [
...this.emojis[`People${this.skinTone}`] || [],
...this.emojis.Nature || [],
...this.emojis.Food || [],
...this.emojis[`Activity${this.skinTone}`] || [],
...this.emojis.Travel || [],
...this.emojis[`Objects${this.skinTone}`] || [],
...this.emojis.Symbols || [],
...this.emojis.Flags || []
];
},
emojis() {
return emojisGrouped;
},
CDN_URL() {
return CDN_URL;
},
tabLabels() {
return this.recentlyUsedEmojis.length ? this.tabSetLabels.map((label) => ({ label })) : this.tabSetLabels.slice(1).map((label) => ({ label }));
},
tabs() {
return this.recentlyUsedEmojis.length ? this.TABS_DATA : this.TABS_DATA.slice(1);
}
},
watch: {
currentEmojis: {
handler() {
this.searchByNameAndKeywords();
},
immediate: true
},
recentlyUsedEmojis: {
handler(newValue) {
this.emojis["Recently used"] = newValue;
},
immediate: true
},
emojiFilter: {
handler(newFilter) {
this.resetScroll();
if (newFilter) {
this.isFiltering = true;
} else {
this.isFiltering = false;
this.$emit("highlighted-emoji", null);
}
this.debouncedSearch();
}
},
selectedTabset: {
handler(newValue) {
this.scrollToTab(newValue.tabId);
},
deep: true
}
},
created() {
this.debouncedSearch = this.debounce(this.searchByNameAndKeywords, 300);
},
mounted() {
this.$nextTick(() => {
this.setupEmojiRefs();
this.setupFilteredRefs();
this.setupTabLabelRefs();
this.setTabLabelObserver();
});
},
beforeDestroy() {
if (this.tabLabelObserver) {
this.tabLabelObserver.disconnect();
}
},
methods: {
setupTabLabelRefs() {
var _a;
(_a = this.tabSetLabels) == null ? void 0 : _a.forEach((_, index) => {
const refKey = `tabLabelRef-${index}`;
if (this.$refs[refKey]) {
this.$set(this.tabLabelsRefs, index, { ref: this.$refs[refKey] });
}
});
},
setupFilteredRefs() {
this.emojiFilteredRefs = [];
this.filteredEmojis.forEach((emoji, index) => {
const refKey = `filteredEmoji-${index}`;
if (this.$refs[refKey]) {
this.setFilteredRef(this.$refs[refKey], index);
}
});
},
setupEmojiRefs() {
for (let i = 0; i < this.tabs.length; i++) {
const refKey = `emojiRef-${i}`;
if (this.$refs[refKey]) {
this.$refs[refKey].forEach((el, indexEmoji) => {
if (el) {
this.setEmojiRef(el, i, indexEmoji);
}
});
}
}
},
searchByNameAndKeywords() {
const searchStr = this.emojiFilter.toLowerCase();
this.filteredEmojis = this.currentEmojis.filter(function(obj) {
const nameIncludesSearchStr = obj.name.toLowerCase().includes(searchStr);
const keywordsIncludeSearchStr = obj.keywords.some(function(keyword) {
return keyword.toLowerCase().includes(searchStr);
});
return nameIncludesSearchStr || keywordsIncludeSearchStr;
});
this.$nextTick(function() {
if (searchStr) {
this.hoverEmoji(this.filteredEmojis[0], true);
this.setupFilteredRefs();
}
});
},
debounce: function(fn, delay) {
if (delay === void 0) {
delay = 300;
}
let timeout;
return function() {
const args = [];
let len = arguments.length;
while (len--) args[len] = arguments[len];
clearTimeout(timeout);
timeout = setTimeout(function() {
fn.apply(void 0, args);
}, delay);
};
},
getImgSrc: function(emoji) {
return this.CDN_URL + emoji + ".png";
},
handleImageError: function(event) {
event.target.parentNode.style.display = "none";
},
scrollToTab: function(tabIndex, focusFirstEmoji) {
const vm = this;
if (focusFirstEmoji === void 0) {
focusFirstEmoji = true;
}
const tabElement = vm.tabLabelsRefs[tabIndex - 1].ref[0];
vm.$nextTick(function() {
const container = vm.$refs.listRef;
const offsetTop = tabIndex === 1 ? 0 : tabElement.offsetTop - 15;
container.scrollTop = offsetTop;
if (focusFirstEmoji) {
vm.focusEmoji(tabIndex - 1, 0);
}
});
},
resetScroll: function() {
const container = this.$refs.listRef;
container.scrollTop = 0;
},
focusEmojiSelector: function() {
this.focusEmoji(0, 0);
},
hoverEmoji(emoji, isFirst) {
if (isFirst === void 0) {
isFirst = false;
}
this.hoverFirstEmoji = isFirst;
this.$emit("highlighted-emoji", emoji);
},
setEmojiRef: function(el, indexTab, indexEmoji) {
if (!this.emojiRefs[indexTab]) {
this.$set(this.emojiRefs, indexTab, []);
}
this.$set(this.emojiRefs[indexTab], indexEmoji, el);
},
setFilteredRef: function(el, index) {
this.$set(this.emojiFilteredRefs, index, el);
},
focusEmoji: function(indexTab, indexEmoji) {
var _a;
const emojiRef = this.isFiltering ? (_a = this.emojiFilteredRefs[indexEmoji]) == null ? void 0 : _a[0] : this.emojiRefs[indexTab] && this.emojiRefs[indexTab][indexEmoji];
if (emojiRef) {
emojiRef.focus();
return true;
}
return false;
},
// eslint-disable-next-line complexity
handleKeyDown: function(event, indexTab, indexEmoji, emoji) {
var _a, _b;
event.preventDefault();
if (event.key === "ArrowUp") {
const position = indexEmoji % EMOJIS_PER_ROW;
if (indexTab === 0) {
const numberOfMissingEmojis = EMOJIS_PER_ROW - this.emojiRefs[this.emojiRefs.length - 1].length % EMOJIS_PER_ROW;
const emojiToJump = this.emojiRefs[this.emojiRefs.length - 1].length + numberOfMissingEmojis - (EMOJIS_PER_ROW - position);
if (!this.focusEmoji(this.emojiRefs.length - 1, emojiToJump)) {
this.focusEmoji(this.emojiRefs.length - 1, this.emojiRefs[this.emojiRefs.length - 1].length - 1);
}
return;
}
if (!this.focusEmoji(indexTab, indexEmoji - EMOJIS_PER_ROW)) {
const previousTab = indexTab - 1 < 0 ? 0 : indexTab - 1;
const emojisInPreviousTab = this.emojiRefs[previousTab].length;
const lastEmojiPosition = emojisInPreviousTab - emojisInPreviousTab % EMOJIS_PER_ROW + position;
if (!this.focusEmoji(previousTab, lastEmojiPosition)) {
this.focusEmoji(indexTab - 1, this.emojiRefs[indexTab - 1].length - 1);
}
}
}
if (event.key === "ArrowDown") {
if (!this.focusEmoji(indexTab, indexEmoji + EMOJIS_PER_ROW)) {
const position = indexEmoji % EMOJIS_PER_ROW;
if ((_b = (_a = this.emojiRefs) == null ? void 0 : _a[indexTab]) == null ? void 0 : _b[indexEmoji + (EMOJIS_PER_ROW - position)]) {
this.focusEmoji(indexTab, this.emojiRefs[indexTab].length - 1);
} else {
if (!this.focusEmoji(indexTab + 1, position)) {
if (!this.focusEmoji(0, position)) {
this.focusEmoji(0, this.emojiRefs[0].length - 1);
}
}
}
}
}
if (event.key === "ArrowLeft") {
this.handleHorizontalNavigation("left", indexTab, indexEmoji);
}
if (event.key === "ArrowRight") {
this.handleHorizontalNavigation("right", indexTab, indexEmoji);
}
if (event.key === "Tab" && !event.shiftKey) {
if (this.focusEmoji(indexTab + 1, 0)) {
this.scrollToTab(indexTab + 1 + 1, false);
} else {
this.$emit("focus-skin-selector");
}
}
if (event.key === "Tab" && event.shiftKey) {
if (this.focusEmoji(indexTab, 0) && indexTab > 0) {
this.scrollToTab(indexTab, true);
} else {
this.scrollToTab(1, false);
this.$emit("focus-search-input");
}
}
if (event.key === "Enter") {
this.handleEmojiSelection(emoji, event);
}
},
/* eslint-disable-next-line complexity */
handleHorizontalNavigation: function(direction, indexTab, indexEmoji) {
if (this.isFiltering) {
if (direction === "left") {
this.handleArrowLeftFiltered(indexTab, indexEmoji);
} else if (direction === "right") {
this.handleArrowRightFiltered(indexTab, indexEmoji);
}
} else {
if (direction === "left") {
this.handleArrowLeft(indexTab, indexEmoji);
} else if (direction === "right") {
this.handleArrowRight(indexTab, indexEmoji);
}
}
},
handleArrowLeftFiltered: function(indexTab, indexEmoji) {
if (!this.focusEmoji(0, indexEmoji - 1)) {
this.focusEmoji(0, this.emojiFilteredRefs.length - 1);
}
},
handleArrowRightFiltered: function(indexTab, indexEmoji) {
if (!this.focusEmoji(0, indexEmoji + 1)) {
this.focusEmoji(0, 0);
}
},
handleArrowLeft: function(indexTab, indexEmoji) {
if (!this.focusEmoji(indexTab, indexEmoji - 1)) {
if (this.emojiRefs[indexTab - 1]) {
this.focusEmoji(indexTab - 1, this.emojiRefs[indexTab - 1].length - 1);
} else {
this.focusEmoji(this.emojiRefs.length - 1, this.emojiRefs[this.emojiRefs.length - 1].length - 1);
}
}
},
handleArrowRight: function(indexTab, indexEmoji) {
if (!this.focusEmoji(indexTab, indexEmoji + 1)) {
if (!this.focusEmoji(indexTab + 1, 0)) {
this.focusEmoji(0, 0);
}
}
},
handleEmojiSelection(emoji, event) {
this.$emit("selected-emoji", { ...emoji, shift_key: event.shiftKey });
},
/* eslint-disable-next-line complexity */
handleKeyDownFilteredEmojis(event, indexEmoji, emoji) {
var _a;
event.preventDefault();
this.hoverFirstEmoji = false;
if (event.key === "ArrowUp") {
const position = indexEmoji % EMOJIS_PER_ROW;
if (!this.focusEmoji(0, indexEmoji - EMOJIS_PER_ROW)) {
const lastEmojiPosition = this.emojiFilteredRefs.length - this.emojiFilteredRefs.length % EMOJIS_PER_ROW + position;
this.focusEmoji(0, lastEmojiPosition);
if (!this.focusEmoji(0, lastEmojiPosition)) {
this.focusEmoji(0, this.emojiFilteredRefs.length - 1);
}
}
}
if (event.key === "ArrowDown") {
if (!this.focusEmoji(0, indexEmoji + EMOJIS_PER_ROW)) {
const position = indexEmoji % EMOJIS_PER_ROW;
if ((_a = this.emojiFilteredRefs) == null ? void 0 : _a[indexEmoji + (EMOJIS_PER_ROW - position)]) {
this.focusEmoji(0, this.emojiFilteredRefs.length - 1);
} else {
this.focusEmoji(0, position);
}
}
}
if (event.key === "ArrowLeft") {
this.handleHorizontalNavigation("left", 0, indexEmoji);
}
if (event.key === "ArrowRight") {
this.handleHorizontalNavigation("right", 0, indexEmoji);
}
if (event.key === "Tab") {
this.$emit("focus-skin-selector");
}
if (event.key === "Enter") {
this.handleEmojiSelection(emoji, event);
}
},
setTabLabelObserver() {
this.tabLabelObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
var _a, _b, _c, _d, _e, _f;
const { target } = entry;
const index = parseInt(target.dataset.index);
if (entry.isIntersecting && target.offsetTop <= ((_a = this.$refs.tabCategoryRef) == null ? void 0 : _a.offsetTop) + 50) {
this.fixedLabel = ((_b = this.tabLabels[index - 1]) == null ? void 0 : _b.label) ?? ((_c = this.tabLabels[0]) == null ? void 0 : _c.label);
this.$emit("scroll-into-tab", index - 1);
} else if (entry.boundingClientRect.bottom <= ((_d = this.$refs.tabCategoryRef) == null ? void 0 : _d.getBoundingClientRect().bottom)) {
this.$emit("scroll-into-tab", index);
this.fixedLabel = (_e = this.tabLabels[index]) == null ? void 0 : _e.label;
} else if (index === 1) {
this.$emit("scroll-into-tab", index);
this.fixedLabel = (_f = this.tabLabels[0]) == null ? void 0 : _f.label;
}
});
});
this.tabLabelObserver.observe(this.$refs.tabCategoryRef);
Array.from(this.$refs.listRef.children).forEach((child, index) => {
this.tabLabelObserver.observe(child);
child.dataset.index = index;
});
},
focusLastEmoji() {
this.scrollToTab(this.tabs.length, true);
}
}
};
var _sfc_render = function render() {
var _vm = this, _c = _vm._self._c;
return _c("div", { staticClass: "d-emoji-picker__selector" }, [_c("div", { ref: "listRef", staticClass: "d-emoji-picker__list", attrs: { "id": "d-emoji-picker-list" } }, [_vm.emojiFilter ? _c("p", { staticClass: "d-emoji-picker__search-label d-emoji-picker__alignment" }, [_vm._v(" " + _vm._s(_vm.filteredEmojis.length > 0 ? _vm.searchResultsLabel : _vm.searchNoResultsLabel) + " ")]) : _c("div", { ref: "tabCategoryRef", staticClass: "d-emoji-picker__category d-emoji-picker__alignment" }, [_c("p", [_vm._v(" " + _vm._s(_vm.fixedLabel) + " ")])]), _vm._l(_vm.tabLabels, function(tabLabel, indexTab) {
return _c("div", { directives: [{ name: "show", rawName: "v-show", value: !_vm.emojiFilter, expression: "!emojiFilter" }], key: indexTab, ref: `tabLabelRef-${indexTab}`, refInFor: true, staticClass: "d-emoji-picker__alignment" }, [indexTab ? _c("p", [_vm._v(" " + _vm._s(tabLabel.label) + " ")]) : _vm._e(), _c("div", { staticClass: "d-emoji-picker__tab" }, _vm._l(_vm.emojis[_vm.tabs[indexTab] + _vm.skinTone] ? _vm.emojis[_vm.tabs[indexTab] + _vm.skinTone] : _vm.emojis[_vm.tabs[indexTab]], function(emoji, indexEmoji) {
return _c("button", { key: emoji.shortname, ref: `emojiRef-${indexTab}`, refInFor: true, attrs: { "type": "button", "aria-label": emoji.name }, on: { "click": (event) => _vm.handleEmojiSelection(emoji, event), "focusin": function($event) {
return _vm.$emit("highlighted-emoji", emoji);
}, "focusout": function($event) {
return _vm.$emit("highlighted-emoji", null);
}, "mouseover": function($event) {
return _vm.$emit("highlighted-emoji", emoji);
}, "mouseleave": function($event) {
return _vm.$emit("highlighted-emoji", null);
}, "keydown": (event) => _vm.handleKeyDown(event, indexTab, indexEmoji, emoji) } }, [_c("img", { staticClass: "d-icon d-icon--size-500", attrs: { "alt": emoji.name, "aria-label": emoji.name, "title": emoji.name, "src": _vm.getImgSrc(emoji.unicode_character) }, on: { "error": _vm.handleImageError } })]);
}), 0)]);
}), _vm.emojiFilter ? _c("div", { staticClass: "d-emoji-picker__alignment" }, [_c("div", { staticClass: "d-emoji-picker__tab", attrs: { "data-qa": "filtered-emojis" } }, _vm._l(_vm.filteredEmojis, function(emoji, index) {
return _c("button", { key: emoji.shortname, ref: `filteredEmoji-${index}`, refInFor: true, class: {
"hover-emoji": index === 0 && _vm.hoverFirstEmoji
}, attrs: { "type": "button", "aria-label": emoji.name }, on: { "click": (event) => _vm.handleEmojiSelection(emoji, event), "focusin": function($event) {
return _vm.$emit("highlighted-emoji", emoji);
}, "focusout": function($event) {
return _vm.$emit("highlighted-emoji", null);
}, "mouseover": function($event) {
return _vm.hoverEmoji(emoji);
}, "mouseleave": function($event) {
return _vm.hoverEmoji(null);
}, "keydown": (event) => _vm.handleKeyDownFilteredEmojis(event, index, emoji) } }, [_c("img", { staticClass: "d-icon d-icon--size-500", attrs: { "alt": emoji.name, "aria-label": emoji.name, "title": emoji.name, "src": `${_vm.CDN_URL + emoji.unicode_character}.png` } })]);
}), 0)]) : _vm._e()], 2)]);
};
var _sfc_staticRenderFns = [];
var __component__ = /* @__PURE__ */ normalizeComponent(
_sfc_main,
_sfc_render,
_sfc_staticRenderFns
);
const EmojiSelector = __component__.exports;
export {
EmojiSelector as default
};
//# sourceMappingURL=emoji_selector.vue.js.map