teableio/teable

View on GitHub
apps/nextjs-app/src/features/app/blocks/table-list/TableListItem.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import { Table2 } from '@teable/icons';
import type { Table } from '@teable/sdk/model';
import { Button, cn } from '@teable/ui-lib/shadcn';
import { Input } from '@teable/ui-lib/shadcn/ui/input';
import { useRouter } from 'next/router';
import { useEffect, useRef, useState } from 'react';
import { Emoji } from '../../components/emoji/Emoji';
import { EmojiPicker } from '../../components/emoji/EmojiPicker';
import { TableOperation } from './TableOperation';

interface IProps {
  table: Table;
  isActive: boolean;
  isDragging?: boolean;
  className?: string;
}

export const TableListItem: React.FC<IProps> = ({ table, isActive, className, isDragging }) => {
  const [isEditing, setIsEditing] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);
  const router = useRouter();
  const { baseId } = router.query;
  const viewId = router.query.viewId;

  const navigateHandler = () => {
    router.push(
      {
        pathname: '/base/[baseId]/[tableId]/[viewId]',
        query: {
          tableId: table.id,
          viewId: table.defaultViewId,
          baseId: baseId as string,
        },
      },
      undefined,
      { shallow: Boolean(table.defaultViewId) && Boolean(viewId) }
    );
  };

  useEffect(() => {
    if (isEditing) {
      setTimeout(() => inputRef.current?.focus());
    }
  }, [isEditing]);

  return (
    <>
      <Button
        variant={'ghost'}
        size={'xs'}
        asChild
        className={cn(
          'my-[2px] w-full px-2 justify-start text-sm font-normal gap-2 group bg-popover',
          className,
          {
            'bg-secondary/90': isActive,
          }
        )}
        onClick={navigateHandler}
      >
        <div>
          {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
          <div onClick={(e) => e.stopPropagation()}>
            <EmojiPicker
              className="flex size-5 items-center justify-center hover:bg-muted-foreground/60"
              onChange={(icon: string) => table.updateIcon(icon)}
              disabled={!table.permission?.['table|update']}
            >
              {table.icon ? (
                <Emoji emoji={table.icon} size={'1rem'} />
              ) : (
                <Table2 className="size-4 shrink-0" />
              )}
            </EmojiPicker>
          </div>
          <p
            className="grow truncate"
            onDoubleClick={() => {
              table.permission?.['table|update'] && setIsEditing(true);
            }}
          >
            {' ' + table.name}
          </p>
          {!isDragging && (
            <TableOperation
              table={table}
              className="size-4 shrink-0 sm:opacity-0 sm:group-hover:opacity-100"
              onRename={() => setIsEditing(true)}
            />
          )}
        </div>
      </Button>
      {isEditing && (
        <Input
          ref={inputRef}
          type="text"
          placeholder="name"
          defaultValue={table.name}
          style={{
            boxShadow: 'none',
          }}
          className="round-none absolute left-0 top-0 size-full cursor-text bg-background px-4 outline-none"
          onBlur={(e) => {
            if (e.target.value && e.target.value !== table.name) {
              table.updateName(e.target.value);
            }
            setIsEditing(false);
          }}
          onKeyDown={(e) => {
            if (e.key === 'Enter') {
              if (e.currentTarget.value && e.currentTarget.value !== table.name) {
                table.updateName(e.currentTarget.value);
              }
              setIsEditing(false);
            }
          }}
          onMouseDown={(e) => {
            e.stopPropagation();
          }}
        />
      )}
    </>
  );
};