JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour

File "font-selector.tsx"

Full Path: /home/markqprx/iniasli.pro/client/ui/font-selector/font-selector.tsx
File size: 3.64 KB
MIME-type: text/plain
Charset: utf-8

import {Trans} from '@common/i18n/trans';
import React from 'react';
import {ButtonBase} from '@common/ui/buttons/button-base';
import clsx from 'clsx';
import {IllustratedMessage} from '@common/ui/images/illustrated-message';
import fontImage from './font.svg';
import {SvgImage} from '@common/ui/images/svg-image/svg-image';
import {FontSelectorFilters} from '@common/ui/font-selector/font-selector-filters';
import {
  FontSelectorState,
  UseFontSelectorProps,
  useFontSelectorState,
} from '@common/ui/font-selector/font-selector-state';
import {FontSelectorPagination} from '@common/ui/font-selector/font-selector-pagination';
import {FontConfig} from '@common/http/value-lists';
import {Skeleton} from '@common/ui/skeleton/skeleton';
import {AnimatePresence, m} from 'framer-motion';
import {opacityAnimation} from '@common/ui/animation/opacity-animation';

interface FontSelectorProps extends UseFontSelectorProps {
  className?: string;
}
export function FontSelector(props: FontSelectorProps) {
  const state = useFontSelectorState(props);
  return (
    <div className={props.className}>
      <FontSelectorFilters state={state} />
      <AnimatePresence initial={false} mode="wait">
        <FontList state={state} />
      </AnimatePresence>
      <FontSelectorPagination state={state} />
    </div>
  );
}

interface FontListProps {
  state: FontSelectorState;
}
function FontList({state}: FontListProps) {
  const {isLoading, fonts} = state;

  const gridClassName =
    'grid gap-24 grid-cols-[repeat(auto-fill,minmax(90px,1fr))] items-start';

  if (isLoading) {
    return <FontListSkeleton className={gridClassName} />;
  }

  if (!fonts?.length) {
    return (
      <IllustratedMessage
        className="mt-60"
        size="sm"
        image={<SvgImage src={fontImage} />}
        title={<Trans message="No matching fonts" />}
        description={
          <Trans message="Try another search query or different category" />
        }
      />
    );
  }

  return (
    <m.div key="font-list" {...opacityAnimation} className={gridClassName}>
      {fonts?.map(font => (
        <FontButton key={font.family} font={font} state={state} />
      ))}
    </m.div>
  );
}

interface FontButtonProps {
  font: FontConfig;
  state: FontSelectorState;
}
function FontButton({font, state: {value, onChange}}: FontButtonProps) {
  const isActive = value?.family === font.family;
  const displayName = font.family.split(',')[0].replace(/"/g, '');

  return (
    <ButtonBase
      key={font.family}
      display="block"
      onClick={() => {
        onChange(font);
      }}
    >
      <span
        className={clsx(
          'flex aspect-square items-center justify-center rounded-panel border text-4xl transition-bg-color hover:bg-hover md:text-5xl',
          isActive && 'ring-2 ring-primary ring-offset-2',
        )}
      >
        <span style={{fontFamily: font.family}}>Aa</span>
      </span>
      <span
        className={clsx(
          'mt-6 block overflow-hidden overflow-ellipsis whitespace-nowrap text-sm',
          isActive && 'text-primary',
        )}
      >
        {font.label ? <Trans {...font.label} /> : displayName}
      </span>
    </ButtonBase>
  );
}

interface FontListSkeletonProps {
  className: string;
}
function FontListSkeleton({className}: FontListSkeletonProps) {
  const items = Array.from(Array(20).keys());
  return (
    <m.div key="font-list-skeleton" {...opacityAnimation} className={className}>
      {items.map(index => (
        <div key={index}>
          <div className="aspect-square">
            <Skeleton display="block" variant="rect" />
          </div>
          <Skeleton className="mt-6 text-sm" />
        </div>
      ))}
    </m.div>
  );
}