// SPDX-License-Identifier: AGPL-3.0-or-later import styles from '@app/features/channel/components/EmojiPicker.module.css'; import { CATEGORY_HEADER_HEIGHT, EMOJI_ROW_HEIGHT, } from '@app/features/channel/components/emoji_picker/EmojiPickerConstants'; import {EmojiRenderer} from '@app/features/channel/components/emoji_picker/EmojiRenderer'; import type {Channel} from '@app/features/channel/models/Channel'; import * as EmojiPickerCommands from '@app/features/emoji/commands/EmojiPickerCommands'; import EmojiPicker from '@app/features/emoji/state/EmojiPicker'; import type {FlatEmoji} from '@app/features/emoji/types/EmojiTypes'; import {GuildIcon} from '@app/features/guild/components/popouts/GuildIcon'; import Guilds from '@app/features/guild/state/Guilds'; import {remFromPx} from '@app/features/theme/layout/RemFromPx'; import {CaretDownIcon, ClockIcon, StarIcon} from '@phosphor-icons/react'; import {observer} from 'mobx-react-lite'; import React from 'react'; export type VirtualRow = | {type: 'header'; category: string; name: string; guildId?: string; index: number} | {type: 'emoji-row'; emojis: Array; index: number; isCustomEmoji?: boolean; guildId?: string}; interface VirtualRowRendererProps { row: VirtualRow; handleHover: (emoji: FlatEmoji | null, row?: number, column?: number) => void; handleSelect: (emoji: FlatEmoji, shiftKey?: boolean) => void; skinTone: string; channel: Channel | null; shouldAnimate: boolean; gridColumns?: number; gridWidth?: number; cellTrackWidth?: number; selectedColumnInRow: number; emojiRowIndex: number; shouldScrollOnSelection?: boolean; emojiRefs: React.MutableRefObject>; } const VirtualRowRenderer: React.FC = React.memo( ({ row, handleHover, handleSelect, skinTone, channel, shouldAnimate, gridColumns = 9, gridWidth, cellTrackWidth, selectedColumnInRow, emojiRowIndex, shouldScrollOnSelection = false, emojiRefs, }) => { if (row.type === 'header') { const isCollapsed = EmojiPicker.isCategoryCollapsed(row.category); const handleToggleCategory = () => { EmojiPickerCommands.toggleCategory(row.category); }; let leadingIcon: React.ReactNode = null; if (row.category === 'favorites') { leadingIcon = ( ); } else if (row.category === 'frequently-used') { leadingIcon = ( ); } else if (row.guildId) { leadingIcon = (
); } return ( ); } const hasFixedTrack = cellTrackWidth != null && cellTrackWidth > 0; return (
{row.emojis.map((emoji, colIndex) => { const isSelected = colIndex === selectedColumnInRow; const shouldHighlight = isSelected; return ( handleHover(e, emojiRowIndex, colIndex)} handleSelect={handleSelect} skinTone={skinTone} channel={channel} shouldAnimate={shouldAnimate} isHighlighted={shouldHighlight} shouldScrollIntoView={isSelected && shouldScrollOnSelection} ref={(node) => { const key = `${emojiRowIndex}-${colIndex}`; if (node) { emojiRefs.current.set(key, node); } else { emojiRefs.current.delete(key); } }} data-flx="channel.emoji-picker.virtual-row.virtual-row-renderer.emoji-renderer" /> ); })}
); }, ); interface VirtualizedRowProps { row: VirtualRow; handleHover: (emoji: FlatEmoji | null, row?: number, column?: number) => void; handleSelect: (emoji: FlatEmoji, shiftKey?: boolean) => void; skinTone: string; channel: Channel | null; allowAnimation: boolean; gridColumns?: number; gridWidth?: number; cellTrackWidth?: number; selectedRow: number; selectedColumn: number; emojiRowIndex: number; shouldScrollOnSelection?: boolean; emojiRefs: React.MutableRefObject>; } export const VirtualizedRow: React.FC = observer( ({ row, handleHover, handleSelect, skinTone, channel, allowAnimation, gridColumns, gridWidth, cellTrackWidth, selectedRow, selectedColumn, emojiRowIndex, shouldScrollOnSelection = false, emojiRefs, }) => { const selectedColumnInRow = emojiRowIndex === selectedRow ? selectedColumn : -1; return ( ); }, );