pacocoursey / cmdk

Fast, unstyled command menu React component.
https://cmdk.paco.me
MIT License
9.04k stars 259 forks source link

Flash of empty component when closing popover with animation #219

Open arekrgw opened 4 months ago

arekrgw commented 4 months ago

https://github.com/pacocoursey/cmdk/assets/34128433/89dc400b-d964-4a88-93ff-680764ed6427

Hi! In above video you can see a flash of empty message content while closing the popover and when the loading component is present.

I've used Radix Popover here in combination with cmdk, similar as you can see in Shadcn/ui Combobox example.

I'm not 100% sure this is a bug, because I think it is connected with how radix handles css animations.

Are there any workarounds about it, like manually show Empty message somehow?

Below is a code used in the example

export function AppsFilter() {
  const { data, isLoading } = useApplicationsQuery();
  const [selectedApps, setSelectedApps] = useState<Application[]>([]);
  const [open, setOpen] = useState(false);

  return (
    <Popover.Root onOpenChange={setOpen} open={open}>
      <Popover.Trigger asChild>
        <Combobox.ValueDisplay className="shrink-0 md:flex-[1]" open={open}>
          {selectedApps.length === 0
            ? 'Applications'
            : selectedApps.map((app) => (
                <Badge asChild className="pl-[3px]" key={app.id}>
                  <button
                    onClick={(e) => {
                      e.stopPropagation();
                      setSelectedApps((prev) => {
                        return prev.filter((a) => app.id !== a.id);
                      });
                      if (selectedApps.length === 1 && selectedApps[0].id === app.id) setOpen(true);
                    }}
                    type="button"
                  >
                    <AppIcon alt={app.name} size="sm" />
                    <X size="1em" />
                  </button>
                </Badge>
              ))}
        </Combobox.ValueDisplay>
      </Popover.Trigger>
      <Popover.Content>
        <Command.Root>
          <Command.Input />
          <Command.List>
            <Command.Empty>No applications found 🫤</Command.Empty>
            {isLoading && <Command.Loading>Getting apps…</Command.Loading>}
            {data?.map((app) => (
              <Command.Item
                isSelected={!!selectedApps.find((a) => a.id === app.id)}
                key={app.id}
                onSelect={(v) => {
                  console.log(v);
                  setSelectedApps((prev) => {
                    if (prev.find((a) => a.id === app.id)) {
                      return prev.filter((a) => a.id !== app.id);
                    }
                    return [...prev, app];
                  });
                }}
                value={app.name}
              >
                {app.name}
              </Command.Item>
            ))}
          </Command.List>
        </Command.Root>
      </Popover.Content>
    </Popover.Root>
  );
}
export const PopoverContent = forwardRef<
  ElementRef<typeof PopoverPrimitive.Content>,
  ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = 'start', sideOffset = 4, ...props }, ref) => (
  <PopoverPrimitive.Portal>
    <PopoverPrimitive.Content
      align={align}
      className={cn(
        'ui-z-tooltip ui-w-[var(--radix-popper-anchor-width)] ui-overflow-hidden ui-duration-150',
        'data-[state=open]:ui-animate-in data-[state=closed]:ui-animate-out data-[state=closed]:ui-fade-out data-[state=open]:ui-fade-in',
        cardStyles,
        className
      )}
      ref={ref}
      sideOffset={sideOffset}
      {...props}
    />
  </PopoverPrimitive.Portal>
));

PopoverContent.displayName = PopoverPrimitive.Content.displayName;