项目源码:github.com/jakobhoeg/sha...

项目图片:

​​

左右头像

一个头像在左边一个头像在右边。

​​

当然可以写两个组件来显示,但是作者的方法是通过判定然后动态显示的。

{/* 通过信息的用户名是否为当前的用户,因为一个头像在左,一个头像在右边,所以通过这个来判断,然后动态显示 */}
<div className="flex gap-3 items-center">
  {message.name === selectedUser.name && (
    <Avatar className="flex justify-center items-center">
      <AvatarImage
        src={message.avatar}
        alt={message.name}
        width={6}
        height={6}
      />
    </Avatar>
  )}
  <span className=" bg-accent p-3 rounded-md max-w-xs">
    {message.message}
  </span>
  {message.name !== selectedUser.name && (
    <Avatar className="flex justify-center items-center">
      <AvatarImage
        src={message.avatar}
        alt={message.name}
        width={6}
        height={6}
      />
    </Avatar>
  )}
</div>

通过message.name === selectedUser.name和message.name !== selectedUser.name来动态的控制是头像+信息​还是信息+头像​。

判断输入信息是否为空

当没有信息输入的时候是这样的


​当输入了信息的时候是这样的
​​

可以看到右边的图标转换了,逻辑就是判断是否有字符串,然后动态显示

{/* 判断message是否为空 */}
        {message.trim() ? (
          <Link
            href="#"
            className={cn(
              buttonVariants({ variant: "ghost", size: "icon" }),
              "h-9 w-9",
              "dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white shrink-0"
            )}
            onClick={handleSend}
          >
            <SendHorizontal size={20} className="text-muted-foreground" />
          </Link>
        ) : (
          <Link
            href="#"
            className={cn(
              buttonVariants({ variant: "ghost", size: "icon" }),
              "h-9 w-9",
              "dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white shrink-0"
            )}
            onClick={handleThumbsUp}
          >
            <ThumbsUp size={20} className="text-muted-foreground" />
          </Link>
        )}

这里通过

message.trim()

方法来判断信息是否为空,比较巧妙,这个方法是对字符串的操作,它的作用是去除字符串两端的空格(whitespace),返回一个新的字符串。

那么这个的思路其实就是trim()去除字符串的空白,如果只有空格的话,去除之后就为空,所以代表的是如果为空的话。

输入框动态变化

​可以看到当输入内容的时候,编辑框动态的发生了变化。

首先其引用了AnimatePresence​以及motion.div​

​AnimatePresence​ 是来自 Framer Motion 库的一个组件,用于处理 React 中元素的进场(enter)和离场(exit)动画。它提供了一个容器,用于管理在组件树中插入或删除的子元素的动画效果。

<motion.div
          key="input"
          className="w-full relative"
          layout
          initial={{ opacity: 0, scale: 1 }}
          animate={{ opacity: 1, scale: 1 }}
          exit={{ opacity: 0, scale: 1 }}
          transition={{
            opacity: { duration: 0.05 },
            layout: {
              type: "spring",
              bounce: 0.15,
            },
          }}
        >
          <Textarea
            autoComplete="off"
            value={message}
            ref={inputRef}
            onKeyDown={handleKeyPress}
            onChange={handleInputChange}
            name="message"
            placeholder="Aa"
            className=" w-full border rounded-full flex items-center h-9 resize-none overflow-hidden bg-background"
          ></Textarea>
          <div className="absolute right-2 bottom-0.5  ">
            <EmojiPicker onChange={(value) => {
              setMessage(message + value)
              if (inputRef.current) {
                inputRef.current.focus();
              }
            }} />
          </div>
        </motion.div>

可以看到Textarea​放在了motion.div​容器中,并且容器还设置了动画的变化规则等信息,Textarea​中定义了变化函数onChange={handleInputChange}​

  const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
    setMessage(event.target.value);
  };
  • ​React.ChangeEvent<HTMLTextAreaElement>​ 表示这个事件对象是 React 中的 ChangeEvent​ 类型,对应于 HTML 中 <textarea>​ 元素的变化事件。HTMLTextAreaElement​ 则是指明了事件目标的类型,即 <textarea>​ 元素。

event.target.value​:

  • ​event​ 是一个事件对象,它包含了有关事件的各种信息。

  • event.target​ 表示触发事件的元素,即引发了变化的输入框元素。

  • event.target.value​ 表示了输入框元素的当前值,即用户输入的文本内容。这是一个常用的方式来获取用户在输入框中输入的内容。

即改变了message的状态,注意这里是setmessage,而不是sendmessage。