/* Copyright 2024 New Vector Ltd. Copyright 2020 The Matrix.org Foundation C.I.C. Copyright 2019 Tulir Asokan SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React, { RefObject } from "react"; import { DATA_BY_CATEGORY, Emoji as IEmoji } from "@matrix-org/emojibase-bindings"; import { CATEGORY_HEADER_HEIGHT, EMOJI_HEIGHT, EMOJIS_PER_ROW } from "./EmojiPicker"; import LazyRenderList from "../elements/LazyRenderList"; import Emoji from "./Emoji"; import { ButtonEvent } from "../elements/AccessibleButton"; const OVERFLOW_ROWS = 3; export type CategoryKey = keyof typeof DATA_BY_CATEGORY | "recent"; export interface ICategory { id: CategoryKey; name: string; enabled: boolean; visible: boolean; ref: RefObject; } interface IProps { id: string; name: string; emojis: IEmoji[]; selectedEmojis?: Set; heightBefore: number; viewportHeight: number; scrollTop: number; onClick(ev: ButtonEvent, emoji: IEmoji): void; onMouseEnter(emoji: IEmoji): void; onMouseLeave(emoji: IEmoji): void; isEmojiDisabled?: (unicode: string) => boolean; } function hexEncode(str: string): string { let hex: string; let i: number; let result = ""; for (i = 0; i < str.length; i++) { hex = str.charCodeAt(i).toString(16); result += ("000" + hex).slice(-4); } return result; } class Category extends React.PureComponent { private renderEmojiRow = (rowIndex: number): JSX.Element => { const { onClick, onMouseEnter, onMouseLeave, selectedEmojis, emojis } = this.props; const emojisForRow = emojis.slice(rowIndex * 8, (rowIndex + 1) * 8); return (
{emojisForRow.map((emoji) => ( ))}
); }; public render(): React.ReactNode { const { emojis, name, heightBefore, viewportHeight, scrollTop } = this.props; if (!emojis || emojis.length === 0) { return null; } const rows = new Array(Math.ceil(emojis.length / EMOJIS_PER_ROW)); for (let counter = 0; counter < rows.length; ++counter) { rows[counter] = counter; } const viewportTop = scrollTop; const viewportBottom = viewportTop + viewportHeight; const listTop = heightBefore + CATEGORY_HEADER_HEIGHT; const listBottom = listTop + rows.length * EMOJI_HEIGHT; const top = Math.max(viewportTop, listTop); const bottom = Math.min(viewportBottom, listBottom); // the viewport height and scrollTop passed to the LazyRenderList // is capped at the intersection with the real viewport, so lists // out of view are passed height 0, so they won't render any items. const localHeight = Math.max(0, bottom - top); const localScrollTop = Math.max(0, scrollTop - listTop); return (

{name}

); } } export default Category;