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 ?? '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}
-
- ) : (
-
-
-
- )
- )
- : searchResult.map((emoji) =>
- 'unicode' in emoji ? null : (
-
-
-
- )
- )}
+ {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) => (
-
-
-
+ mx={mx}
+ useAuthentication={useAuthentication}
+ image={image}
+ />
))}
))}
@@ -434,18 +326,10 @@ export const StickerGroups = memo(
.map((image) => (
-
-
+ 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 (
+
+
+
+ );
+}
+
+type StickerItemProps = {
+ mx: MatrixClient;
+ useAuthentication?: boolean;
+ image: PackImageReader;
+};
+
+export function StickerItem({ mx, useAuthentication, image }: StickerItemProps) {
+ return (
+
+
+
+ );
+}
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 ? (
+
+ ) : (
+
+ )}
+
+ );
+}
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',
+ },
+]);