From 7ff18a3ed3af417f66940b4248a2ca723af56139 Mon Sep 17 00:00:00 2001 From: Ajay Bura <32841439+ajbura@users.noreply.github.com> Date: Sat, 13 Sep 2025 14:25:01 +0530 Subject: [PATCH] extract component from emoji/sticker item and sidebar buttons --- .../components/emoji-board/EmojiBoard.css.tsx | 45 +--- src/app/components/emoji-board/EmojiBoard.tsx | 240 +++++------------- .../emoji-board/components/Item.tsx | 89 +++++++ .../emoji-board/components/Sidebar.tsx | 68 ++++- .../emoji-board/components/index.tsx | 1 + .../emoji-board/components/styles.css.ts | 51 +++- 6 files changed, 262 insertions(+), 232 deletions(-) create mode 100644 src/app/components/emoji-board/components/Item.tsx diff --git a/src/app/components/emoji-board/EmojiBoard.css.tsx b/src/app/components/emoji-board/EmojiBoard.css.tsx index 3411dc78..271e3fb8 100644 --- a/src/app/components/emoji-board/EmojiBoard.css.tsx +++ b/src/app/components/emoji-board/EmojiBoard.css.tsx @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; -import { DefaultReset, FocusOutline, color, config, toRem } from 'folds'; +import { DefaultReset, color, config, toRem } from 'folds'; export const Base = style({ maxWidth: toRem(432), @@ -46,46 +46,3 @@ export const EmojiGroupContent = style([ padding: `0 ${config.space.S200}`, }, ]); - -export const EmojiItem = style([ - DefaultReset, - FocusOutline, - { - width: toRem(48), - height: toRem(48), - fontSize: toRem(32), - lineHeight: toRem(32), - borderRadius: config.radii.R400, - cursor: 'pointer', - - ':hover': { - backgroundColor: color.Surface.ContainerHover, - }, - }, -]); - -export const StickerItem = style([ - EmojiItem, - { - width: toRem(112), - height: toRem(112), - }, -]); - -export const CustomEmojiImg = style([ - DefaultReset, - { - width: toRem(32), - height: toRem(32), - objectFit: 'contain', - }, -]); - -export const StickerImg = style([ - DefaultReset, - { - width: toRem(96), - height: toRem(96), - objectFit: 'contain', - }, -]); diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx index 8044f217..0419c798 100644 --- a/src/app/components/emoji-board/EmojiBoard.tsx +++ b/src/app/components/emoji-board/EmojiBoard.tsx @@ -10,7 +10,7 @@ import React, { useMemo, useRef, } from 'react'; -import { Box, Icon, Icons, Line, Scroll, Text, as, toRem } from 'folds'; +import { Box, Icons, Line, Scroll, Text, as } from 'folds'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; import classNames from 'classnames'; @@ -39,12 +39,16 @@ import { EmojiBoardTabs, SidebarStack, SidebarDivider, - SidebarBtn, Sidebar, NoStickerPacks, createPreviewDataAtom, Preview, PreviewData, + EmojiItem, + StickerItem, + CustomEmojiItem, + ImageGroupIcon, + GroupIcon, } from './components'; import { EmojiBoardTab, EmojiItemInfo, EmojiType } from './types'; @@ -125,81 +129,18 @@ export const EmojiGroup = as< )); -export function EmojiItem({ - label, - type, - data, - shortcode, - children, -}: { - label: string; - type: EmojiType; - data: string; - shortcode: string; - children: ReactNode; -}) { - return ( - - {children} - - ); -} - -export function StickerItem({ - label, - type, - data, - shortcode, - children, -}: { - label: string; - type: EmojiType; - data: string; - shortcode: string; - children: ReactNode; -}) { - return ( - - {children} - - ); -} - function RecentEmojiSidebarStack({ onItemClick }: { onItemClick: (id: string) => void }) { const activeGroupId = useAtomValue(activeGroupIdAtom); return ( - onItemClick(RECENT_GROUP_ID)} - > - - + icon={Icons.RecentClock} + onClick={onItemClick} + /> ); } @@ -224,27 +165,19 @@ function ImagePackSidebarStack({ {packs.map((pack) => { let label = pack.meta.name; if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name; + + const url = + mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) || pack.meta.avatar; + return ( - - {label - + label={label ?? 'Unknown Pack'} + url={url} + onClick={onItemClick} + /> ); })} @@ -267,15 +200,14 @@ function NativeEmojiSidebarStack({ {groups.map((group) => ( - - - + icon={icons[group.id]} + onClick={onItemClick} + /> ))} ); @@ -293,15 +225,7 @@ export function RecentEmojiGroup({ return ( {recentEmojis.map((emoji) => ( - - {emoji.unicode} - + ))} ); @@ -324,53 +248,29 @@ export function SearchEmojiGroup({ }) { return ( - {tab === EmojiBoardTab.Emoji - ? searchResult.map((emoji) => - 'unicode' in emoji ? ( - - {emoji.unicode} - - ) : ( - - {emoji.body - - ) - ) - : searchResult.map((emoji) => - 'unicode' in emoji ? null : ( - - {emoji.body - - ) - )} + {searchResult.map((emoji) => { + if ('unicode' in emoji) { + return ; + } + if (tab === EmojiBoardTab.Sticker) { + return ( + + ); + } + return ( + + ); + })} ); } @@ -392,20 +292,12 @@ export const CustomEmojiGroups = memo( .getImages(ImageUsage.Emoticon) .sort((a, b) => a.shortcode.localeCompare(b.shortcode)) .map((image) => ( - - {image.body - + mx={mx} + useAuthentication={useAuthentication} + image={image} + /> ))} ))} @@ -434,18 +326,10 @@ export const StickerGroups = memo( .map((image) => ( - {image.body - + mx={mx} + useAuthentication={useAuthentication} + image={image} + /> ))} )) @@ -458,15 +342,7 @@ export const NativeEmojiGroups = memo( {groups.map((emojiGroup) => ( {emojiGroup.emojis.map((emoji) => ( - - {emoji.unicode} - + ))} ))} diff --git a/src/app/components/emoji-board/components/Item.tsx b/src/app/components/emoji-board/components/Item.tsx new file mode 100644 index 00000000..d033132d --- /dev/null +++ b/src/app/components/emoji-board/components/Item.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { Box } from 'folds'; +import { MatrixClient } from 'matrix-js-sdk'; +import { EmojiType } from '../types'; +import * as css from './styles.css'; +import { PackImageReader } from '../../../plugins/custom-emoji'; +import { IEmoji } from '../../../plugins/emoji'; +import { mxcUrlToHttp } from '../../../utils/matrix'; + +type EmojiItemProps = { + emoji: IEmoji; +}; +export function EmojiItem({ emoji }: EmojiItemProps) { + return ( + + {emoji.unicode} + + ); +} + +type CustomEmojiItemProps = { + mx: MatrixClient; + useAuthentication?: boolean; + image: PackImageReader; +}; +export function CustomEmojiItem({ mx, useAuthentication, image }: CustomEmojiItemProps) { + return ( + + {image.body + + ); +} + +type StickerItemProps = { + mx: MatrixClient; + useAuthentication?: boolean; + image: PackImageReader; +}; + +export function StickerItem({ mx, useAuthentication, image }: StickerItemProps) { + return ( + + {image.body + + ); +} diff --git a/src/app/components/emoji-board/components/Sidebar.tsx b/src/app/components/emoji-board/components/Sidebar.tsx index 2dee0b0a..970c4f83 100644 --- a/src/app/components/emoji-board/components/Sidebar.tsx +++ b/src/app/components/emoji-board/components/Sidebar.tsx @@ -1,5 +1,17 @@ import React, { ReactNode } from 'react'; -import { Box, Scroll, Line, as, TooltipProvider, Tooltip, Text, IconButton } from 'folds'; +import { + Box, + Scroll, + Line, + as, + TooltipProvider, + Tooltip, + Text, + IconButton, + Icon, + IconSrc, + Icons, +} from 'folds'; import classNames from 'classnames'; import * as css from './styles.css'; @@ -31,17 +43,17 @@ export function SidebarDivider() { return ; } -export function SidebarBtn({ +function SidebarBtn({ active, label, id, - onItemClick, + onClick, children, }: { active?: boolean; label: string; id: T; - onItemClick: (id: T) => void; + onClick: (id: T) => void; children: ReactNode; }) { return ( @@ -59,7 +71,7 @@ export function SidebarBtn({ aria-pressed={active} aria-labelledby={`SidebarStackItem-${id}-label`} ref={ref} - onClick={() => onItemClick(id)} + onClick={() => onClick(id)} size="400" radii="300" variant="Surface" @@ -70,3 +82,49 @@ export function SidebarBtn({ ); } + +type GroupIconProps = { + active: boolean; + id: T; + label: string; + icon: IconSrc; + onClick: (id: T) => void; +}; +export function GroupIcon({ + active, + id, + label, + icon, + onClick, +}: GroupIconProps) { + return ( + + + + ); +} + +type ImageGroupIconProps = { + active: boolean; + id: T; + label: string; + url?: string; + onClick: (id: T) => void; +}; +export function ImageGroupIcon({ + active, + id, + label, + url, + onClick, +}: ImageGroupIconProps) { + return ( + + {url ? ( + + ) : ( + {label} + )} + + ); +} diff --git a/src/app/components/emoji-board/components/index.tsx b/src/app/components/emoji-board/components/index.tsx index 88ecbf23..4aeae217 100644 --- a/src/app/components/emoji-board/components/index.tsx +++ b/src/app/components/emoji-board/components/index.tsx @@ -3,3 +3,4 @@ export * from './Tabs'; export * from './Sidebar'; export * from './NoStickerPacks'; export * from './Preview'; +export * from './Item'; diff --git a/src/app/components/emoji-board/components/styles.css.ts b/src/app/components/emoji-board/components/styles.css.ts index 50d6ec44..7c9de007 100644 --- a/src/app/components/emoji-board/components/styles.css.ts +++ b/src/app/components/emoji-board/components/styles.css.ts @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; -import { toRem, color, config, DefaultReset } from 'folds'; +import { toRem, color, config, DefaultReset, FocusOutline } from 'folds'; export const Sidebar = style({ width: toRem(54), @@ -21,6 +21,12 @@ export const SidebarDivider = style({ width: toRem(18), }); +export const SidebarBtnImg = style({ + width: toRem(24), + height: toRem(24), + objectFit: 'contain', +}); + export const Preview = style({ padding: config.space.S200, margin: config.space.S300, @@ -49,3 +55,46 @@ export const PreviewImg = style([ objectFit: 'contain', }, ]); + +export const EmojiItem = style([ + DefaultReset, + FocusOutline, + { + width: toRem(48), + height: toRem(48), + fontSize: toRem(32), + lineHeight: toRem(32), + borderRadius: config.radii.R400, + cursor: 'pointer', + + ':hover': { + backgroundColor: color.Surface.ContainerHover, + }, + }, +]); + +export const StickerItem = style([ + EmojiItem, + { + width: toRem(112), + height: toRem(112), + }, +]); + +export const CustomEmojiImg = style([ + DefaultReset, + { + width: toRem(32), + height: toRem(32), + objectFit: 'contain', + }, +]); + +export const StickerImg = style([ + DefaultReset, + { + width: toRem(96), + height: toRem(96), + objectFit: 'contain', + }, +]);