mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 14:30:29 +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 { 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',
 | 
			
		||||
  },
 | 
			
		||||
]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<
 | 
			
		|||
  </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 }) {
 | 
			
		||||
  const activeGroupId = useAtomValue(activeGroupIdAtom);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SidebarStack>
 | 
			
		||||
      <SidebarBtn
 | 
			
		||||
      <GroupIcon
 | 
			
		||||
        active={activeGroupId === RECENT_GROUP_ID}
 | 
			
		||||
        id={RECENT_GROUP_ID}
 | 
			
		||||
        label="Recent"
 | 
			
		||||
        onItemClick={() => onItemClick(RECENT_GROUP_ID)}
 | 
			
		||||
      >
 | 
			
		||||
        <Icon src={Icons.RecentClock} filled={activeGroupId === RECENT_GROUP_ID} />
 | 
			
		||||
      </SidebarBtn>
 | 
			
		||||
        icon={Icons.RecentClock}
 | 
			
		||||
        onClick={onItemClick}
 | 
			
		||||
      />
 | 
			
		||||
    </SidebarStack>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
          <SidebarBtn
 | 
			
		||||
            active={activeGroupId === pack.id}
 | 
			
		||||
          <ImageGroupIcon
 | 
			
		||||
            key={pack.id}
 | 
			
		||||
            active={activeGroupId === pack.id}
 | 
			
		||||
            id={pack.id}
 | 
			
		||||
            label={label || 'Unknown Pack'}
 | 
			
		||||
            onItemClick={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>
 | 
			
		||||
            label={label ?? 'Unknown Pack'}
 | 
			
		||||
            url={url}
 | 
			
		||||
            onClick={onItemClick}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </SidebarStack>
 | 
			
		||||
| 
						 | 
				
			
			@ -267,15 +200,14 @@ function NativeEmojiSidebarStack({
 | 
			
		|||
    <SidebarStack className={css.NativeEmojiSidebarStack}>
 | 
			
		||||
      <SidebarDivider />
 | 
			
		||||
      {groups.map((group) => (
 | 
			
		||||
        <SidebarBtn
 | 
			
		||||
        <GroupIcon
 | 
			
		||||
          key={group.id}
 | 
			
		||||
          active={activeGroupId === group.id}
 | 
			
		||||
          id={group.id}
 | 
			
		||||
          label={labels[group.id]}
 | 
			
		||||
          onItemClick={onItemClick}
 | 
			
		||||
        >
 | 
			
		||||
          <Icon src={icons[group.id]} filled={activeGroupId === group.id} />
 | 
			
		||||
        </SidebarBtn>
 | 
			
		||||
          icon={icons[group.id]}
 | 
			
		||||
          onClick={onItemClick}
 | 
			
		||||
        />
 | 
			
		||||
      ))}
 | 
			
		||||
    </SidebarStack>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			@ -293,15 +225,7 @@ export function RecentEmojiGroup({
 | 
			
		|||
  return (
 | 
			
		||||
    <EmojiGroup key={id} id={id} label={label}>
 | 
			
		||||
      {recentEmojis.map((emoji) => (
 | 
			
		||||
        <EmojiItem
 | 
			
		||||
          key={emoji.unicode}
 | 
			
		||||
          label={emoji.label}
 | 
			
		||||
          type={EmojiType.Emoji}
 | 
			
		||||
          data={emoji.unicode}
 | 
			
		||||
          shortcode={emoji.shortcode}
 | 
			
		||||
        >
 | 
			
		||||
          {emoji.unicode}
 | 
			
		||||
        </EmojiItem>
 | 
			
		||||
        <EmojiItem key={emoji.shortcode} emoji={emoji} />
 | 
			
		||||
      ))}
 | 
			
		||||
    </EmojiGroup>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			@ -324,53 +248,29 @@ export function SearchEmojiGroup({
 | 
			
		|||
}) {
 | 
			
		||||
  return (
 | 
			
		||||
    <EmojiGroup key={id} id={id} label={label}>
 | 
			
		||||
      {tab === EmojiBoardTab.Emoji
 | 
			
		||||
        ? searchResult.map((emoji) =>
 | 
			
		||||
            'unicode' in emoji ? (
 | 
			
		||||
              <EmojiItem
 | 
			
		||||
                key={emoji.unicode}
 | 
			
		||||
                label={emoji.label}
 | 
			
		||||
                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
 | 
			
		||||
                key={emoji.shortcode}
 | 
			
		||||
                label={emoji.body || emoji.shortcode}
 | 
			
		||||
                type={EmojiType.Sticker}
 | 
			
		||||
                data={emoji.url}
 | 
			
		||||
                shortcode={emoji.shortcode}
 | 
			
		||||
              >
 | 
			
		||||
                <img
 | 
			
		||||
                  loading="lazy"
 | 
			
		||||
                  className={css.StickerImg}
 | 
			
		||||
                  alt={emoji.body || emoji.shortcode}
 | 
			
		||||
                  src={mxcUrlToHttp(mx, emoji.url, useAuthentication) ?? emoji.url}
 | 
			
		||||
                />
 | 
			
		||||
              </StickerItem>
 | 
			
		||||
            )
 | 
			
		||||
          )}
 | 
			
		||||
      {searchResult.map((emoji) => {
 | 
			
		||||
        if ('unicode' in emoji) {
 | 
			
		||||
          return <EmojiItem key={emoji.unicode} emoji={emoji} />;
 | 
			
		||||
        }
 | 
			
		||||
        if (tab === EmojiBoardTab.Sticker) {
 | 
			
		||||
          return (
 | 
			
		||||
            <StickerItem
 | 
			
		||||
              key={emoji.shortcode}
 | 
			
		||||
              mx={mx}
 | 
			
		||||
              useAuthentication={useAuthentication}
 | 
			
		||||
              image={emoji}
 | 
			
		||||
            />
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
        return (
 | 
			
		||||
          <CustomEmojiItem
 | 
			
		||||
            key={emoji.shortcode}
 | 
			
		||||
            mx={mx}
 | 
			
		||||
            useAuthentication={useAuthentication}
 | 
			
		||||
            image={emoji}
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </EmojiGroup>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -392,20 +292,12 @@ export const CustomEmojiGroups = memo(
 | 
			
		|||
            .getImages(ImageUsage.Emoticon)
 | 
			
		||||
            .sort((a, b) => a.shortcode.localeCompare(b.shortcode))
 | 
			
		||||
            .map((image) => (
 | 
			
		||||
              <EmojiItem
 | 
			
		||||
              <CustomEmojiItem
 | 
			
		||||
                key={image.shortcode}
 | 
			
		||||
                label={image.body || image.shortcode}
 | 
			
		||||
                type={EmojiType.CustomEmoji}
 | 
			
		||||
                data={image.url}
 | 
			
		||||
                shortcode={image.shortcode}
 | 
			
		||||
              >
 | 
			
		||||
                <img
 | 
			
		||||
                  loading="lazy"
 | 
			
		||||
                  className={css.CustomEmojiImg}
 | 
			
		||||
                  alt={image.body || image.shortcode}
 | 
			
		||||
                  src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
 | 
			
		||||
                />
 | 
			
		||||
              </EmojiItem>
 | 
			
		||||
                mx={mx}
 | 
			
		||||
                useAuthentication={useAuthentication}
 | 
			
		||||
                image={image}
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
        </EmojiGroup>
 | 
			
		||||
      ))}
 | 
			
		||||
| 
						 | 
				
			
			@ -434,18 +326,10 @@ export const StickerGroups = memo(
 | 
			
		|||
            .map((image) => (
 | 
			
		||||
              <StickerItem
 | 
			
		||||
                key={image.shortcode}
 | 
			
		||||
                label={image.body || image.shortcode}
 | 
			
		||||
                type={EmojiType.Sticker}
 | 
			
		||||
                data={image.url}
 | 
			
		||||
                shortcode={image.shortcode}
 | 
			
		||||
              >
 | 
			
		||||
                <img
 | 
			
		||||
                  loading="lazy"
 | 
			
		||||
                  className={css.StickerImg}
 | 
			
		||||
                  alt={image.body || image.shortcode}
 | 
			
		||||
                  src={mxcUrlToHttp(mx, image.url, useAuthentication) ?? image.url}
 | 
			
		||||
                />
 | 
			
		||||
              </StickerItem>
 | 
			
		||||
                mx={mx}
 | 
			
		||||
                useAuthentication={useAuthentication}
 | 
			
		||||
                image={image}
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
        </EmojiGroup>
 | 
			
		||||
      ))
 | 
			
		||||
| 
						 | 
				
			
			@ -458,15 +342,7 @@ export const NativeEmojiGroups = memo(
 | 
			
		|||
      {groups.map((emojiGroup) => (
 | 
			
		||||
        <EmojiGroup key={emojiGroup.id} id={emojiGroup.id} label={labels[emojiGroup.id]}>
 | 
			
		||||
          {emojiGroup.emojis.map((emoji) => (
 | 
			
		||||
            <EmojiItem
 | 
			
		||||
              key={emoji.unicode}
 | 
			
		||||
              label={emoji.label}
 | 
			
		||||
              type={EmojiType.Emoji}
 | 
			
		||||
              data={emoji.unicode}
 | 
			
		||||
              shortcode={emoji.shortcode}
 | 
			
		||||
            >
 | 
			
		||||
              {emoji.unicode}
 | 
			
		||||
            </EmojiItem>
 | 
			
		||||
            <EmojiItem key={emoji.unicode} emoji={emoji} />
 | 
			
		||||
          ))}
 | 
			
		||||
        </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 { 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 <Line className={css.SidebarDivider} size="300" variant="Surface" />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function SidebarBtn<T extends string>({
 | 
			
		||||
function SidebarBtn<T extends string>({
 | 
			
		||||
  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<T extends string>({
 | 
			
		|||
          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<T extends string>({
 | 
			
		|||
    </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 './NoStickerPacks';
 | 
			
		||||
export * from './Preview';
 | 
			
		||||
export * from './Item';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
  },
 | 
			
		||||
]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue