mirror of
				https://litchi.icu/ngc2207/judge4c-demo.git
				synced 2025-11-04 12:14:39 +00:00 
			
		
		
		
	feat: 添加shadcn-chat
This commit is contained in:
		
							parent
							
								
									778342b40b
								
							
						
					
					
						commit
						f2373ba8b0
					
				
							
								
								
									
										202
									
								
								src/components/ui/chat/chat-bubble.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								src/components/ui/chat/chat-bubble.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
import { cva, type VariantProps } from "class-variance-authority";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
 | 
			
		||||
import MessageLoading from "./message-loading";
 | 
			
		||||
import { Button, ButtonProps } from "../button";
 | 
			
		||||
 | 
			
		||||
// ChatBubble
 | 
			
		||||
const chatBubbleVariant = cva(
 | 
			
		||||
  "flex gap-2 max-w-[60%] items-end relative group",
 | 
			
		||||
  {
 | 
			
		||||
    variants: {
 | 
			
		||||
      variant: {
 | 
			
		||||
        received: "self-start",
 | 
			
		||||
        sent: "self-end flex-row-reverse",
 | 
			
		||||
      },
 | 
			
		||||
      layout: {
 | 
			
		||||
        default: "",
 | 
			
		||||
        ai: "max-w-full w-full items-center",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    defaultVariants: {
 | 
			
		||||
      variant: "received",
 | 
			
		||||
      layout: "default",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
interface ChatBubbleProps
 | 
			
		||||
  extends React.HTMLAttributes<HTMLDivElement>,
 | 
			
		||||
    VariantProps<typeof chatBubbleVariant> {}
 | 
			
		||||
 | 
			
		||||
const ChatBubble = React.forwardRef<HTMLDivElement, ChatBubbleProps>(
 | 
			
		||||
  ({ className, variant, layout, children, ...props }, ref) => (
 | 
			
		||||
    <div
 | 
			
		||||
      className={cn(
 | 
			
		||||
        chatBubbleVariant({ variant, layout, className }),
 | 
			
		||||
        "relative group",
 | 
			
		||||
      )}
 | 
			
		||||
      ref={ref}
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      {React.Children.map(children, (child) =>
 | 
			
		||||
        React.isValidElement(child) && typeof child.type !== "string"
 | 
			
		||||
          ? React.cloneElement(child, {
 | 
			
		||||
              variant,
 | 
			
		||||
              layout,
 | 
			
		||||
            } as React.ComponentProps<typeof child.type>)
 | 
			
		||||
          : child,
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
ChatBubble.displayName = "ChatBubble";
 | 
			
		||||
 | 
			
		||||
// ChatBubbleAvatar
 | 
			
		||||
interface ChatBubbleAvatarProps {
 | 
			
		||||
  src?: string;
 | 
			
		||||
  fallback?: string;
 | 
			
		||||
  className?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChatBubbleAvatar: React.FC<ChatBubbleAvatarProps> = ({
 | 
			
		||||
  src,
 | 
			
		||||
  fallback,
 | 
			
		||||
  className,
 | 
			
		||||
}) => (
 | 
			
		||||
  <Avatar className={className}>
 | 
			
		||||
    <AvatarImage src={src} alt="Avatar" />
 | 
			
		||||
    <AvatarFallback>{fallback}</AvatarFallback>
 | 
			
		||||
  </Avatar>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// ChatBubbleMessage
 | 
			
		||||
const chatBubbleMessageVariants = cva("p-4", {
 | 
			
		||||
  variants: {
 | 
			
		||||
    variant: {
 | 
			
		||||
      received:
 | 
			
		||||
        "bg-secondary text-secondary-foreground rounded-r-lg rounded-tl-lg",
 | 
			
		||||
      sent: "bg-primary text-primary-foreground rounded-l-lg rounded-tr-lg",
 | 
			
		||||
    },
 | 
			
		||||
    layout: {
 | 
			
		||||
      default: "",
 | 
			
		||||
      ai: "border-t w-full rounded-none bg-transparent",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  defaultVariants: {
 | 
			
		||||
    variant: "received",
 | 
			
		||||
    layout: "default",
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface ChatBubbleMessageProps
 | 
			
		||||
  extends React.HTMLAttributes<HTMLDivElement>,
 | 
			
		||||
    VariantProps<typeof chatBubbleMessageVariants> {
 | 
			
		||||
  isLoading?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChatBubbleMessage = React.forwardRef<
 | 
			
		||||
  HTMLDivElement,
 | 
			
		||||
  ChatBubbleMessageProps
 | 
			
		||||
>(
 | 
			
		||||
  (
 | 
			
		||||
    { className, variant, layout, isLoading = false, children, ...props },
 | 
			
		||||
    ref,
 | 
			
		||||
  ) => (
 | 
			
		||||
    <div
 | 
			
		||||
      className={cn(
 | 
			
		||||
        chatBubbleMessageVariants({ variant, layout, className }),
 | 
			
		||||
        "break-words max-w-full whitespace-pre-wrap",
 | 
			
		||||
      )}
 | 
			
		||||
      ref={ref}
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      {isLoading ? (
 | 
			
		||||
        <div className="flex items-center space-x-2">
 | 
			
		||||
          <MessageLoading />
 | 
			
		||||
        </div>
 | 
			
		||||
      ) : (
 | 
			
		||||
        children
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
ChatBubbleMessage.displayName = "ChatBubbleMessage";
 | 
			
		||||
 | 
			
		||||
// ChatBubbleTimestamp
 | 
			
		||||
interface ChatBubbleTimestampProps
 | 
			
		||||
  extends React.HTMLAttributes<HTMLDivElement> {
 | 
			
		||||
  timestamp: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChatBubbleTimestamp: React.FC<ChatBubbleTimestampProps> = ({
 | 
			
		||||
  timestamp,
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}) => (
 | 
			
		||||
  <div className={cn("text-xs mt-2 text-right", className)} {...props}>
 | 
			
		||||
    {timestamp}
 | 
			
		||||
  </div>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// ChatBubbleAction
 | 
			
		||||
type ChatBubbleActionProps = ButtonProps & {
 | 
			
		||||
  icon: React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ChatBubbleAction: React.FC<ChatBubbleActionProps> = ({
 | 
			
		||||
  icon,
 | 
			
		||||
  onClick,
 | 
			
		||||
  className,
 | 
			
		||||
  variant = "ghost",
 | 
			
		||||
  size = "icon",
 | 
			
		||||
  ...props
 | 
			
		||||
}) => (
 | 
			
		||||
  <Button
 | 
			
		||||
    variant={variant}
 | 
			
		||||
    size={size}
 | 
			
		||||
    className={className}
 | 
			
		||||
    onClick={onClick}
 | 
			
		||||
    {...props}
 | 
			
		||||
  >
 | 
			
		||||
    {icon}
 | 
			
		||||
  </Button>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
interface ChatBubbleActionWrapperProps
 | 
			
		||||
  extends React.HTMLAttributes<HTMLDivElement> {
 | 
			
		||||
  variant?: "sent" | "received";
 | 
			
		||||
  className?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ChatBubbleActionWrapper = React.forwardRef<
 | 
			
		||||
  HTMLDivElement,
 | 
			
		||||
  ChatBubbleActionWrapperProps
 | 
			
		||||
>(({ variant, className, children, ...props }, ref) => (
 | 
			
		||||
  <div
 | 
			
		||||
    ref={ref}
 | 
			
		||||
    className={cn(
 | 
			
		||||
      "absolute top-1/2 -translate-y-1/2 flex opacity-0 group-hover:opacity-100 transition-opacity duration-200",
 | 
			
		||||
      variant === "sent"
 | 
			
		||||
        ? "-left-1 -translate-x-full flex-row-reverse"
 | 
			
		||||
        : "-right-1 translate-x-full",
 | 
			
		||||
      className,
 | 
			
		||||
    )}
 | 
			
		||||
    {...props}
 | 
			
		||||
  >
 | 
			
		||||
    {children}
 | 
			
		||||
  </div>
 | 
			
		||||
));
 | 
			
		||||
ChatBubbleActionWrapper.displayName = "ChatBubbleActionWrapper";
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ChatBubble,
 | 
			
		||||
  ChatBubbleAvatar,
 | 
			
		||||
  ChatBubbleMessage,
 | 
			
		||||
  ChatBubbleTimestamp,
 | 
			
		||||
  chatBubbleVariant,
 | 
			
		||||
  chatBubbleMessageVariants,
 | 
			
		||||
  ChatBubbleAction,
 | 
			
		||||
  ChatBubbleActionWrapper,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										23
									
								
								src/components/ui/chat/chat-input.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/components/ui/chat/chat-input.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
import { Textarea } from "@/components/ui/textarea";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
 | 
			
		||||
interface ChatInputProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>{}
 | 
			
		||||
 | 
			
		||||
const ChatInput = React.forwardRef<HTMLTextAreaElement, ChatInputProps>(
 | 
			
		||||
  ({ className, ...props }, ref) => (
 | 
			
		||||
    <Textarea
 | 
			
		||||
      autoComplete="off"
 | 
			
		||||
      ref={ref}
 | 
			
		||||
      name="message"
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "max-h-12 px-4 py-3 bg-background text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 w-full rounded-md flex items-center h-16 resize-none",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
      {...props}
 | 
			
		||||
    />
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
ChatInput.displayName = "ChatInput";
 | 
			
		||||
 | 
			
		||||
export { ChatInput };
 | 
			
		||||
							
								
								
									
										23
									
								
								src/components/ui/chat/chat-message-list.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/components/ui/chat/chat-message-list.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
 | 
			
		||||
interface ChatMessageListProps extends React.HTMLAttributes<HTMLDivElement> {}
 | 
			
		||||
 | 
			
		||||
const ChatMessageList = React.forwardRef<HTMLDivElement, ChatMessageListProps>(
 | 
			
		||||
  ({ className, children, ...props }, ref) => (
 | 
			
		||||
    <div
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "flex flex-col w-full h-full p-4 gap-6 overflow-y-auto",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
      ref={ref}
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
    </div>
 | 
			
		||||
  ),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
ChatMessageList.displayName = "ChatMessageList";
 | 
			
		||||
 | 
			
		||||
export { ChatMessageList };
 | 
			
		||||
							
								
								
									
										153
									
								
								src/components/ui/chat/expandable-chat.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/components/ui/chat/expandable-chat.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,153 @@
 | 
			
		||||
"use client";
 | 
			
		||||
 | 
			
		||||
import React, { useRef, useState } from "react";
 | 
			
		||||
import { X, MessageCircle } from "lucide-react";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
import { Button } from "@/components/ui/button";
 | 
			
		||||
 | 
			
		||||
export type ChatPosition = "bottom-right" | "bottom-left";
 | 
			
		||||
export type ChatSize = "sm" | "md" | "lg" | "xl" | "full";
 | 
			
		||||
 | 
			
		||||
const chatConfig = {
 | 
			
		||||
  dimensions: {
 | 
			
		||||
    sm: "sm:max-w-sm sm:max-h-[500px]",
 | 
			
		||||
    md: "sm:max-w-md sm:max-h-[600px]",
 | 
			
		||||
    lg: "sm:max-w-lg sm:max-h-[700px]",
 | 
			
		||||
    xl: "sm:max-w-xl sm:max-h-[800px]",
 | 
			
		||||
    full: "sm:w-full sm:h-full",
 | 
			
		||||
  },
 | 
			
		||||
  positions: {
 | 
			
		||||
    "bottom-right": "bottom-5 right-5",
 | 
			
		||||
    "bottom-left": "bottom-5 left-5",
 | 
			
		||||
  },
 | 
			
		||||
  chatPositions: {
 | 
			
		||||
    "bottom-right": "sm:bottom-[calc(100%+10px)] sm:right-0",
 | 
			
		||||
    "bottom-left": "sm:bottom-[calc(100%+10px)] sm:left-0",
 | 
			
		||||
  },
 | 
			
		||||
  states: {
 | 
			
		||||
    open: "pointer-events-auto opacity-100 visible scale-100 translate-y-0",
 | 
			
		||||
    closed:
 | 
			
		||||
      "pointer-events-none opacity-0 invisible scale-100 sm:translate-y-5",
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface ExpandableChatProps extends React.HTMLAttributes<HTMLDivElement> {
 | 
			
		||||
  position?: ChatPosition;
 | 
			
		||||
  size?: ChatSize;
 | 
			
		||||
  icon?: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ExpandableChat: React.FC<ExpandableChatProps> = ({
 | 
			
		||||
  className,
 | 
			
		||||
  position = "bottom-right",
 | 
			
		||||
  size = "md",
 | 
			
		||||
  icon,
 | 
			
		||||
  children,
 | 
			
		||||
  ...props
 | 
			
		||||
}) => {
 | 
			
		||||
  const [isOpen, setIsOpen] = useState(false);
 | 
			
		||||
  const chatRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
 | 
			
		||||
  const toggleChat = () => setIsOpen(!isOpen);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={cn(`fixed ${chatConfig.positions[position]} z-50`, className)}
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      <div
 | 
			
		||||
        ref={chatRef}
 | 
			
		||||
        className={cn(
 | 
			
		||||
          "flex flex-col bg-background border sm:rounded-lg shadow-md overflow-hidden transition-all duration-250 ease-out sm:absolute sm:w-[90vw] sm:h-[80vh] fixed inset-0 w-full h-full sm:inset-auto",
 | 
			
		||||
          chatConfig.chatPositions[position],
 | 
			
		||||
          chatConfig.dimensions[size],
 | 
			
		||||
          isOpen ? chatConfig.states.open : chatConfig.states.closed,
 | 
			
		||||
          className,
 | 
			
		||||
        )}
 | 
			
		||||
      >
 | 
			
		||||
        {children}
 | 
			
		||||
        <Button
 | 
			
		||||
          variant="ghost"
 | 
			
		||||
          size="icon"
 | 
			
		||||
          className="absolute top-2 right-2 sm:hidden"
 | 
			
		||||
          onClick={toggleChat}
 | 
			
		||||
        >
 | 
			
		||||
          <X className="h-4 w-4" />
 | 
			
		||||
        </Button>
 | 
			
		||||
      </div>
 | 
			
		||||
      <ExpandableChatToggle
 | 
			
		||||
        icon={icon}
 | 
			
		||||
        isOpen={isOpen}
 | 
			
		||||
        toggleChat={toggleChat}
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ExpandableChat.displayName = "ExpandableChat";
 | 
			
		||||
 | 
			
		||||
const ExpandableChatHeader: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}) => (
 | 
			
		||||
  <div
 | 
			
		||||
    className={cn("flex items-center justify-between p-4 border-b", className)}
 | 
			
		||||
    {...props}
 | 
			
		||||
  />
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
ExpandableChatHeader.displayName = "ExpandableChatHeader";
 | 
			
		||||
 | 
			
		||||
const ExpandableChatBody: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}) => <div className={cn("flex-grow overflow-y-auto", className)} {...props} />;
 | 
			
		||||
 | 
			
		||||
ExpandableChatBody.displayName = "ExpandableChatBody";
 | 
			
		||||
 | 
			
		||||
const ExpandableChatFooter: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}) => <div className={cn("border-t p-4", className)} {...props} />;
 | 
			
		||||
 | 
			
		||||
ExpandableChatFooter.displayName = "ExpandableChatFooter";
 | 
			
		||||
 | 
			
		||||
interface ExpandableChatToggleProps
 | 
			
		||||
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
 | 
			
		||||
  icon?: React.ReactNode;
 | 
			
		||||
  isOpen: boolean;
 | 
			
		||||
  toggleChat: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ExpandableChatToggle: React.FC<ExpandableChatToggleProps> = ({
 | 
			
		||||
  className,
 | 
			
		||||
  icon,
 | 
			
		||||
  isOpen,
 | 
			
		||||
  toggleChat,
 | 
			
		||||
  ...props
 | 
			
		||||
}) => (
 | 
			
		||||
  <Button
 | 
			
		||||
    variant="default"
 | 
			
		||||
    onClick={toggleChat}
 | 
			
		||||
    className={cn(
 | 
			
		||||
      "w-14 h-14 rounded-full shadow-md flex items-center justify-center hover:shadow-lg hover:shadow-black/30 transition-all duration-300",
 | 
			
		||||
      className,
 | 
			
		||||
    )}
 | 
			
		||||
    {...props}
 | 
			
		||||
  >
 | 
			
		||||
    {isOpen ? (
 | 
			
		||||
      <X className="h-6 w-6" />
 | 
			
		||||
    ) : (
 | 
			
		||||
      icon || <MessageCircle className="h-6 w-6" />
 | 
			
		||||
    )}
 | 
			
		||||
  </Button>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
ExpandableChatToggle.displayName = "ExpandableChatToggle";
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ExpandableChat,
 | 
			
		||||
  ExpandableChatHeader,
 | 
			
		||||
  ExpandableChatBody,
 | 
			
		||||
  ExpandableChatFooter,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										45
									
								
								src/components/ui/chat/message-loading.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/components/ui/chat/message-loading.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
// @hidden
 | 
			
		||||
export default function MessageLoading() {
 | 
			
		||||
  return (
 | 
			
		||||
    <svg
 | 
			
		||||
      width="24"
 | 
			
		||||
      height="24"
 | 
			
		||||
      viewBox="0 0 24 24"
 | 
			
		||||
      xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
      className="text-foreground"
 | 
			
		||||
    >
 | 
			
		||||
      <circle cx="4" cy="12" r="2" fill="currentColor">
 | 
			
		||||
        <animate
 | 
			
		||||
          id="spinner_qFRN"
 | 
			
		||||
          begin="0;spinner_OcgL.end+0.25s"
 | 
			
		||||
          attributeName="cy"
 | 
			
		||||
          calcMode="spline"
 | 
			
		||||
          dur="0.6s"
 | 
			
		||||
          values="12;6;12"
 | 
			
		||||
          keySplines=".33,.66,.66,1;.33,0,.66,.33"
 | 
			
		||||
        />
 | 
			
		||||
      </circle>
 | 
			
		||||
      <circle cx="12" cy="12" r="2" fill="currentColor">
 | 
			
		||||
        <animate
 | 
			
		||||
          begin="spinner_qFRN.begin+0.1s"
 | 
			
		||||
          attributeName="cy"
 | 
			
		||||
          calcMode="spline"
 | 
			
		||||
          dur="0.6s"
 | 
			
		||||
          values="12;6;12"
 | 
			
		||||
          keySplines=".33,.66,.66,1;.33,0,.66,.33"
 | 
			
		||||
        />
 | 
			
		||||
      </circle>
 | 
			
		||||
      <circle cx="20" cy="12" r="2" fill="currentColor">
 | 
			
		||||
        <animate
 | 
			
		||||
          id="spinner_OcgL"
 | 
			
		||||
          begin="spinner_qFRN.begin+0.2s"
 | 
			
		||||
          attributeName="cy"
 | 
			
		||||
          calcMode="spline"
 | 
			
		||||
          dur="0.6s"
 | 
			
		||||
          values="12;6;12"
 | 
			
		||||
          keySplines=".33,.66,.66,1;.33,0,.66,.33"
 | 
			
		||||
        />
 | 
			
		||||
      </circle>
 | 
			
		||||
    </svg>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user