akkoma-fe/src/components/emoji_grid/emoji_grid.js

126 lines
3.2 KiB
JavaScript

const EMOJI_SIZE = 32 + 8;
const GROUP_TITLE_HEIGHT = 24;
const BUFFER_SIZE = 3*EMOJI_SIZE;
const EmojiGrid = {
props: {
groups: {
required: true,
type: Array
}
},
data() {
return {
containerWidth: 0,
containerHeight: 0,
scrollPos: 0,
resizeObserver: null,
}
},
mounted() {
const rect = this.$refs.container.getBoundingClientRect();
this.containerWidth = rect.width;
this.containerHeight = rect.height;
this.resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
this.containerWidth = entry.contentRect.width;
this.containerHeight = entry.contentRect.height;
}
})
this.resizeObserver.observe(this.$refs.container);
},
beforeUnmount() {
this.resizeObserver.disconnect();
this.resizeObserver = null;
},
watch: {
groups() {
// Scroll to top when grid content changes
if (this.$refs.container) {
this.$refs.container.scrollTo(0, 0);
}
},
activeGroup(group) {
this.$emit('activeGroup', group);
}
},
methods: {
onScroll() {
this.scrollPos = this.$refs.container.scrollTop
},
onEmoji(emoji) {
this.$emit('emoji', emoji)
},
scrollToItem(itemId) {
const container = this.$refs.container;
if (!container) return;
for (const item of this.itemList) {
if (item.id === itemId) {
container.scrollTo(0, item.position.y);
return;
}
}
}
},
computed: {
// Total height of scroller content
gridHeight() {
if (this.itemList.length == 0) return 0;
const lastItem = this.itemList[this.itemList.length - 1];
return lastItem.position.y + ('title' in lastItem ? GROUP_TITLE_HEIGHT : EMOJI_SIZE)
},
activeGroup() {
const items = this.itemList;
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i];
if ('title' in item && item.position.y <= this.scrollPos) {
return item.id;
}
}
return null;
},
itemList() {
const items = [];
let x = 0, y = 0;
for (const group of this.groups) {
items.push({ position: {x, y}, id: group.id, title: group.text });
if (group.text.length) {
y += GROUP_TITLE_HEIGHT;
}
for (const emoji of group.emojis) {
items.push({ position: {x, y}, id: `${group.id}-${emoji.displayText}`, emoji })
x += EMOJI_SIZE;
if (x + EMOJI_SIZE > this.containerWidth) {
y += EMOJI_SIZE;
x = 0;
}
}
if (x > 0) {
y += EMOJI_SIZE;
x = 0;
}
}
return items;
},
visibleItems() {
const startPos = this.scrollPos - BUFFER_SIZE;
const endPos = this.scrollPos + this.containerHeight + BUFFER_SIZE;
return this.itemList.filter(i => {
return i.position.y >= startPos && i.position.y < endPos;
});
},
scrolledClass() {
if (this.scrollPos <= 5) {
return 'scrolled-top'
} else if (this.scrollPos >= this.gridHeight - this.containerHeight - 5) {
return 'scrolled-bottom'
} else {
return 'scrolled-middle'
}
}
}
}
export default EmojiGrid