mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-09-13 22:32:26 +03:00
extract component from emoji/sticker item and sidebar buttons
This commit is contained in:
parent
f674482911
commit
7ff18a3ed3
6 changed files with 262 additions and 232 deletions
|
@ -1,5 +1,5 @@
|
||||||
import { style } from '@vanilla-extract/css';
|
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({
|
export const Base = style({
|
||||||
maxWidth: toRem(432),
|
maxWidth: toRem(432),
|
||||||
|
@ -46,46 +46,3 @@ export const EmojiGroupContent = style([
|
||||||
padding: `0 ${config.space.S200}`,
|
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',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import React, {
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} 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 FocusTrap from 'focus-trap-react';
|
||||||
import { isKeyHotkey } from 'is-hotkey';
|
import { isKeyHotkey } from 'is-hotkey';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -39,12 +39,16 @@ import {
|
||||||
EmojiBoardTabs,
|
EmojiBoardTabs,
|
||||||
SidebarStack,
|
SidebarStack,
|
||||||
SidebarDivider,
|
SidebarDivider,
|
||||||
SidebarBtn,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
NoStickerPacks,
|
NoStickerPacks,
|
||||||
createPreviewDataAtom,
|
createPreviewDataAtom,
|
||||||
Preview,
|
Preview,
|
||||||
PreviewData,
|
PreviewData,
|
||||||
|
EmojiItem,
|
||||||
|
StickerItem,
|
||||||
|
CustomEmojiItem,
|
||||||
|
ImageGroupIcon,
|
||||||
|
GroupIcon,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { EmojiBoardTab, EmojiItemInfo, EmojiType } from './types';
|
import { EmojiBoardTab, EmojiItemInfo, EmojiType } from './types';
|
||||||
|
|
||||||
|
@ -125,81 +129,18 @@ export const EmojiGroup = as<
|
||||||
</Box>
|
</Box>
|
||||||
));
|
));
|
||||||
|
|
||||||
export function EmojiItem({
|
|
||||||
label,
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
shortcode,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
label: string;
|
|
||||||
type: EmojiType;
|
|
||||||
data: string;
|
|
||||||
shortcode: string;
|
|
||||||
children: ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
as="button"
|
|
||||||
className={css.EmojiItem}
|
|
||||||
type="button"
|
|
||||||
alignItems="Center"
|
|
||||||
justifyContent="Center"
|
|
||||||
title={label}
|
|
||||||
aria-label={`${label} emoji`}
|
|
||||||
data-emoji-type={type}
|
|
||||||
data-emoji-data={data}
|
|
||||||
data-emoji-shortcode={shortcode}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function StickerItem({
|
|
||||||
label,
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
shortcode,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
label: string;
|
|
||||||
type: EmojiType;
|
|
||||||
data: string;
|
|
||||||
shortcode: string;
|
|
||||||
children: ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
as="button"
|
|
||||||
className={css.StickerItem}
|
|
||||||
type="button"
|
|
||||||
alignItems="Center"
|
|
||||||
justifyContent="Center"
|
|
||||||
title={label}
|
|
||||||
aria-label={`${label} sticker`}
|
|
||||||
data-emoji-type={type}
|
|
||||||
data-emoji-data={data}
|
|
||||||
data-emoji-shortcode={shortcode}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RecentEmojiSidebarStack({ onItemClick }: { onItemClick: (id: string) => void }) {
|
function RecentEmojiSidebarStack({ onItemClick }: { onItemClick: (id: string) => void }) {
|
||||||
const activeGroupId = useAtomValue(activeGroupIdAtom);
|
const activeGroupId = useAtomValue(activeGroupIdAtom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarStack>
|
<SidebarStack>
|
||||||
<SidebarBtn
|
<GroupIcon
|
||||||
active={activeGroupId === RECENT_GROUP_ID}
|
active={activeGroupId === RECENT_GROUP_ID}
|
||||||
id={RECENT_GROUP_ID}
|
id={RECENT_GROUP_ID}
|
||||||
label="Recent"
|
label="Recent"
|
||||||
onItemClick={() => onItemClick(RECENT_GROUP_ID)}
|
icon={Icons.RecentClock}
|
||||||
>
|
onClick={onItemClick}
|
||||||
<Icon src={Icons.RecentClock} filled={activeGroupId === RECENT_GROUP_ID} />
|
/>
|
||||||
</SidebarBtn>
|
|
||||||
</SidebarStack>
|
</SidebarStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -224,27 +165,19 @@ function ImagePackSidebarStack({
|
||||||
{packs.map((pack) => {
|
{packs.map((pack) => {
|
||||||
let label = pack.meta.name;
|
let label = pack.meta.name;
|
||||||
if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.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 (
|
return (
|
||||||
<SidebarBtn
|
<ImageGroupIcon
|
||||||
active={activeGroupId === pack.id}
|
|
||||||
key={pack.id}
|
key={pack.id}
|
||||||
|
active={activeGroupId === pack.id}
|
||||||
id={pack.id}
|
id={pack.id}
|
||||||
label={label || 'Unknown Pack'}
|
label={label ?? 'Unknown Pack'}
|
||||||
onItemClick={onItemClick}
|
url={url}
|
||||||
>
|
onClick={onItemClick}
|
||||||
<img
|
|
||||||
style={{
|
|
||||||
width: toRem(24),
|
|
||||||
height: toRem(24),
|
|
||||||
objectFit: 'contain',
|
|
||||||
}}
|
|
||||||
src={
|
|
||||||
mxcUrlToHttp(mx, pack.getAvatarUrl(usage) ?? '', useAuthentication) ||
|
|
||||||
pack.meta.avatar
|
|
||||||
}
|
|
||||||
alt={label || 'Unknown Pack'}
|
|
||||||
/>
|
/>
|
||||||
</SidebarBtn>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</SidebarStack>
|
</SidebarStack>
|
||||||
|
@ -267,15 +200,14 @@ function NativeEmojiSidebarStack({
|
||||||
<SidebarStack className={css.NativeEmojiSidebarStack}>
|
<SidebarStack className={css.NativeEmojiSidebarStack}>
|
||||||
<SidebarDivider />
|
<SidebarDivider />
|
||||||
{groups.map((group) => (
|
{groups.map((group) => (
|
||||||
<SidebarBtn
|
<GroupIcon
|
||||||
key={group.id}
|
key={group.id}
|
||||||
active={activeGroupId === group.id}
|
active={activeGroupId === group.id}
|
||||||
id={group.id}
|
id={group.id}
|
||||||
label={labels[group.id]}
|
label={labels[group.id]}
|
||||||
onItemClick={onItemClick}
|
icon={icons[group.id]}
|
||||||
>
|
onClick={onItemClick}
|
||||||
<Icon src={icons[group.id]} filled={activeGroupId === group.id} />
|
/>
|
||||||
</SidebarBtn>
|
|
||||||
))}
|
))}
|
||||||
</SidebarStack>
|
</SidebarStack>
|
||||||
);
|
);
|
||||||
|
@ -293,15 +225,7 @@ export function RecentEmojiGroup({
|
||||||
return (
|
return (
|
||||||
<EmojiGroup key={id} id={id} label={label}>
|
<EmojiGroup key={id} id={id} label={label}>
|
||||||
{recentEmojis.map((emoji) => (
|
{recentEmojis.map((emoji) => (
|
||||||
<EmojiItem
|
<EmojiItem key={emoji.shortcode} emoji={emoji} />
|
||||||
key={emoji.unicode}
|
|
||||||
label={emoji.label}
|
|
||||||
type={EmojiType.Emoji}
|
|
||||||
data={emoji.unicode}
|
|
||||||
shortcode={emoji.shortcode}
|
|
||||||
>
|
|
||||||
{emoji.unicode}
|
|
||||||
</EmojiItem>
|
|
||||||
))}
|
))}
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
);
|
);
|
||||||
|
@ -324,53 +248,29 @@ export function SearchEmojiGroup({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<EmojiGroup key={id} id={id} label={label}>
|
<EmojiGroup key={id} id={id} label={label}>
|
||||||
{tab === EmojiBoardTab.Emoji
|
{searchResult.map((emoji) => {
|
||||||
? searchResult.map((emoji) =>
|
if ('unicode' in emoji) {
|
||||||
'unicode' in emoji ? (
|
return <EmojiItem key={emoji.unicode} emoji={emoji} />;
|
||||||
<EmojiItem
|
}
|
||||||
key={emoji.unicode}
|
if (tab === EmojiBoardTab.Sticker) {
|
||||||
label={emoji.label}
|
return (
|
||||||
type={EmojiType.Emoji}
|
|
||||||
data={emoji.unicode}
|
|
||||||
shortcode={emoji.shortcode}
|
|
||||||
>
|
|
||||||
{emoji.unicode}
|
|
||||||
</EmojiItem>
|
|
||||||
) : (
|
|
||||||
<EmojiItem
|
|
||||||
key={emoji.shortcode}
|
|
||||||
label={emoji.body || emoji.shortcode}
|
|
||||||
type={EmojiType.CustomEmoji}
|
|
||||||
data={emoji.url}
|
|
||||||
shortcode={emoji.shortcode}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
loading="lazy"
|
|
||||||
className={css.CustomEmojiImg}
|
|
||||||
alt={emoji.body || emoji.shortcode}
|
|
||||||
src={mxcUrlToHttp(mx, emoji.url, useAuthentication) ?? emoji.url}
|
|
||||||
/>
|
|
||||||
</EmojiItem>
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: searchResult.map((emoji) =>
|
|
||||||
'unicode' in emoji ? null : (
|
|
||||||
<StickerItem
|
<StickerItem
|
||||||
key={emoji.shortcode}
|
key={emoji.shortcode}
|
||||||
label={emoji.body || emoji.shortcode}
|
mx={mx}
|
||||||
type={EmojiType.Sticker}
|
useAuthentication={useAuthentication}
|
||||||
data={emoji.url}
|
image={emoji}
|
||||||
shortcode={emoji.shortcode}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
loading="lazy"
|
|
||||||
className={css.StickerImg}
|
|
||||||
alt={emoji.body || emoji.shortcode}
|
|
||||||
src={mxcUrlToHttp(mx, emoji.url, useAuthentication) ?? emoji.url}
|
|
||||||
/>
|
/>
|
||||||
</StickerItem>
|
);
|
||||||
)
|
}
|
||||||
)}
|
return (
|
||||||
|
<CustomEmojiItem
|
||||||
|
key={emoji.shortcode}
|
||||||
|
mx={mx}
|
||||||
|
useAuthentication={useAuthentication}
|
||||||
|
image={emoji}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -392,20 +292,12 @@ export const CustomEmojiGroups = memo(
|
||||||
.getImages(ImageUsage.Emoticon)
|
.getImages(ImageUsage.Emoticon)
|
||||||
.sort((a, b) => a.shortcode.localeCompare(b.shortcode))
|
.sort((a, b) => a.shortcode.localeCompare(b.shortcode))
|
||||||
.map((image) => (
|
.map((image) => (
|
||||||
<EmojiItem
|
<CustomEmojiItem
|
||||||
key={image.shortcode}
|
key={image.shortcode}
|
||||||
label={image.body || image.shortcode}
|
mx={mx}
|
||||||
type={EmojiType.CustomEmoji}
|
useAuthentication={useAuthentication}
|
||||||
data={image.url}
|
image={image}
|
||||||
shortcode={image.shortcode}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
loading="lazy"
|
|
||||||
className={css.CustomEmojiImg}
|
|
||||||
alt={image.body || image.shortcode}
|
|
||||||
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
|
|
||||||
/>
|
/>
|
||||||
</EmojiItem>
|
|
||||||
))}
|
))}
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
))}
|
))}
|
||||||
|
@ -434,18 +326,10 @@ export const StickerGroups = memo(
|
||||||
.map((image) => (
|
.map((image) => (
|
||||||
<StickerItem
|
<StickerItem
|
||||||
key={image.shortcode}
|
key={image.shortcode}
|
||||||
label={image.body || image.shortcode}
|
mx={mx}
|
||||||
type={EmojiType.Sticker}
|
useAuthentication={useAuthentication}
|
||||||
data={image.url}
|
image={image}
|
||||||
shortcode={image.shortcode}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
loading="lazy"
|
|
||||||
className={css.StickerImg}
|
|
||||||
alt={image.body || image.shortcode}
|
|
||||||
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
|
|
||||||
/>
|
/>
|
||||||
</StickerItem>
|
|
||||||
))}
|
))}
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
))
|
))
|
||||||
|
@ -458,15 +342,7 @@ export const NativeEmojiGroups = memo(
|
||||||
{groups.map((emojiGroup) => (
|
{groups.map((emojiGroup) => (
|
||||||
<EmojiGroup key={emojiGroup.id} id={emojiGroup.id} label={labels[emojiGroup.id]}>
|
<EmojiGroup key={emojiGroup.id} id={emojiGroup.id} label={labels[emojiGroup.id]}>
|
||||||
{emojiGroup.emojis.map((emoji) => (
|
{emojiGroup.emojis.map((emoji) => (
|
||||||
<EmojiItem
|
<EmojiItem key={emoji.unicode} emoji={emoji} />
|
||||||
key={emoji.unicode}
|
|
||||||
label={emoji.label}
|
|
||||||
type={EmojiType.Emoji}
|
|
||||||
data={emoji.unicode}
|
|
||||||
shortcode={emoji.shortcode}
|
|
||||||
>
|
|
||||||
{emoji.unicode}
|
|
||||||
</EmojiItem>
|
|
||||||
))}
|
))}
|
||||||
</EmojiGroup>
|
</EmojiGroup>
|
||||||
))}
|
))}
|
||||||
|
|
89
src/app/components/emoji-board/components/Item.tsx
Normal file
89
src/app/components/emoji-board/components/Item.tsx
Normal file
|
@ -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 (
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
alignItems="Center"
|
||||||
|
justifyContent="Center"
|
||||||
|
className={css.EmojiItem}
|
||||||
|
title={emoji.label}
|
||||||
|
aria-label={`${emoji.label} emoji`}
|
||||||
|
data-emoji-type={EmojiType.Emoji}
|
||||||
|
data-emoji-data={emoji.unicode}
|
||||||
|
data-emoji-shortcode={emoji.shortcode}
|
||||||
|
>
|
||||||
|
{emoji.unicode}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomEmojiItemProps = {
|
||||||
|
mx: MatrixClient;
|
||||||
|
useAuthentication?: boolean;
|
||||||
|
image: PackImageReader;
|
||||||
|
};
|
||||||
|
export function CustomEmojiItem({ mx, useAuthentication, image }: CustomEmojiItemProps) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
alignItems="Center"
|
||||||
|
justifyContent="Center"
|
||||||
|
className={css.EmojiItem}
|
||||||
|
title={image.body || image.shortcode}
|
||||||
|
aria-label={`${image.body || image.shortcode} emoji`}
|
||||||
|
data-emoji-type={EmojiType.CustomEmoji}
|
||||||
|
data-emoji-data={image.url}
|
||||||
|
data-emoji-shortcode={image.shortcode}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
loading="lazy"
|
||||||
|
className={css.CustomEmojiImg}
|
||||||
|
alt={image.body || image.shortcode}
|
||||||
|
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type StickerItemProps = {
|
||||||
|
mx: MatrixClient;
|
||||||
|
useAuthentication?: boolean;
|
||||||
|
image: PackImageReader;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function StickerItem({ mx, useAuthentication, image }: StickerItemProps) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
alignItems="Center"
|
||||||
|
justifyContent="Center"
|
||||||
|
className={css.StickerItem}
|
||||||
|
title={image.body || image.shortcode}
|
||||||
|
aria-label={`${image.body || image.shortcode} emoji`}
|
||||||
|
data-emoji-type={EmojiType.Sticker}
|
||||||
|
data-emoji-data={image.url}
|
||||||
|
data-emoji-shortcode={image.shortcode}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
loading="lazy"
|
||||||
|
className={css.StickerImg}
|
||||||
|
alt={image.body || image.shortcode}
|
||||||
|
src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,5 +1,17 @@
|
||||||
import React, { ReactNode } from 'react';
|
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 classNames from 'classnames';
|
||||||
import * as css from './styles.css';
|
import * as css from './styles.css';
|
||||||
|
|
||||||
|
@ -31,17 +43,17 @@ export function SidebarDivider() {
|
||||||
return <Line className={css.SidebarDivider} size="300" variant="Surface" />;
|
return <Line className={css.SidebarDivider} size="300" variant="Surface" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidebarBtn<T extends string>({
|
function SidebarBtn<T extends string>({
|
||||||
active,
|
active,
|
||||||
label,
|
label,
|
||||||
id,
|
id,
|
||||||
onItemClick,
|
onClick,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
id: T;
|
id: T;
|
||||||
onItemClick: (id: T) => void;
|
onClick: (id: T) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
@ -59,7 +71,7 @@ export function SidebarBtn<T extends string>({
|
||||||
aria-pressed={active}
|
aria-pressed={active}
|
||||||
aria-labelledby={`SidebarStackItem-${id}-label`}
|
aria-labelledby={`SidebarStackItem-${id}-label`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={() => onItemClick(id)}
|
onClick={() => onClick(id)}
|
||||||
size="400"
|
size="400"
|
||||||
radii="300"
|
radii="300"
|
||||||
variant="Surface"
|
variant="Surface"
|
||||||
|
@ -70,3 +82,49 @@ export function SidebarBtn<T extends string>({
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GroupIconProps<T extends string> = {
|
||||||
|
active: boolean;
|
||||||
|
id: T;
|
||||||
|
label: string;
|
||||||
|
icon: IconSrc;
|
||||||
|
onClick: (id: T) => void;
|
||||||
|
};
|
||||||
|
export function GroupIcon<T extends string>({
|
||||||
|
active,
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
onClick,
|
||||||
|
}: GroupIconProps<T>) {
|
||||||
|
return (
|
||||||
|
<SidebarBtn active={active} id={id} label={label} onClick={onClick}>
|
||||||
|
<Icon src={icon} filled={active} />
|
||||||
|
</SidebarBtn>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageGroupIconProps<T extends string> = {
|
||||||
|
active: boolean;
|
||||||
|
id: T;
|
||||||
|
label: string;
|
||||||
|
url?: string;
|
||||||
|
onClick: (id: T) => void;
|
||||||
|
};
|
||||||
|
export function ImageGroupIcon<T extends string>({
|
||||||
|
active,
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
url,
|
||||||
|
onClick,
|
||||||
|
}: ImageGroupIconProps<T>) {
|
||||||
|
return (
|
||||||
|
<SidebarBtn active={active} id={id} label={label} onClick={onClick}>
|
||||||
|
{url ? (
|
||||||
|
<Icon src={Icons.Photo} filled={active} />
|
||||||
|
) : (
|
||||||
|
<img className={css.SidebarBtnImg} src={url} alt={label} />
|
||||||
|
)}
|
||||||
|
</SidebarBtn>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -3,3 +3,4 @@ export * from './Tabs';
|
||||||
export * from './Sidebar';
|
export * from './Sidebar';
|
||||||
export * from './NoStickerPacks';
|
export * from './NoStickerPacks';
|
||||||
export * from './Preview';
|
export * from './Preview';
|
||||||
|
export * from './Item';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { style } from '@vanilla-extract/css';
|
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({
|
export const Sidebar = style({
|
||||||
width: toRem(54),
|
width: toRem(54),
|
||||||
|
@ -21,6 +21,12 @@ export const SidebarDivider = style({
|
||||||
width: toRem(18),
|
width: toRem(18),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const SidebarBtnImg = style({
|
||||||
|
width: toRem(24),
|
||||||
|
height: toRem(24),
|
||||||
|
objectFit: 'contain',
|
||||||
|
});
|
||||||
|
|
||||||
export const Preview = style({
|
export const Preview = style({
|
||||||
padding: config.space.S200,
|
padding: config.space.S200,
|
||||||
margin: config.space.S300,
|
margin: config.space.S300,
|
||||||
|
@ -49,3 +55,46 @@ export const PreviewImg = style([
|
||||||
objectFit: 'contain',
|
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',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue