# VChatCloud React UI Kit

[![NPM Version](https://img.shields.io/npm/v/%40vchatcloud%2Freact-ui-kit)](https://www.npmjs.com/package/@vchatcloud/react-ui-kit)

VChatCloud React UI Kit은 UI 컴포넌트를 제공하여 VChatCloud 채팅 서비스를 React에 통합하기 쉽게 만들어줍니다.

## 목차

- [VChatCloud React UI Kit](#vchatcloud-react-ui-kit)
  - [목차](#목차)
  - [설치](#설치)
  - [사용법](#사용법)
    - [코드 예시](#코드-예시)
  - [컴포넌트 설명](#컴포넌트-설명)
    - [VChatCloudApp](#vchatcloudapp)
  - [주의사항](#주의사항)
  - [커스터마이징](#커스터마이징)
    - [컴포넌트 변경](#컴포넌트-변경)
    - [색상 변경](#색상-변경)
  - [추가 정보](#추가-정보)

## 설치

해당 라이브러리는 react 18버전을 지원합니다.

```bash
npm install react@~18 react-dom@~18 @vchatcloud/react-ui-kit
# or
# yarn add react@~18 react-dom@~18 @vchatcloud/react-ui-kit
# or
# pnpm add react@~18 react-dom@~18 @vchatcloud/react-ui-kit
```

## 사용법

우선 사용하려는 프로젝트의 `index.html`에 해당 내용을 작성합니다.

```html
<head>
  <!-- ... -->
  <!-- 아래 내용을 추가합니다. -->
  <script src="https://www.vchatcloud.com/lib/e7lib-latest.min.js"></script>
  <script src="https://www.vchatcloud.com/lib/vchatcloud-last.min.js"></script>
</head>
```

그 후 프로젝트에서 UI Kit을 사용하려면, 필요한 컴포넌트를 import하여 사용합니다.
`VChatCloudApp` 컴포넌트를 사용하여 VChatCloud를 설정하고 실행할 수 있습니다.

### 코드 예시

```tsx
import { VChatCloudApp, ChannelUserGrade } from "@vchatcloud/react-ui-kit";
import { FC, useEffect, useRef } from "react";

const App: FC = () => {
  const [roomId, clientKey, nickName, grade, userInfo] = [
    "YOUR_ROOM_ID",
    // clientKey는 원하는 값을 사용하면 되지만, 사용자끼리 중복되지 않도록 주의해주세요.
    // 같은 clientKey로 로그인 시 기존에 로그인 된 사용자는 접속이 종료됩니다.
    // 또는 `setRandomClientKey`함수를 불러와 사용할 수 있습니다.
    "YOUR_CLIENT_KEY",
    "YOUR_NICKNAME",
    "user",
    { profile: 1 },
  ];
  const privateContainer = useRef<HTMLDivElement>(null);

  // private container 사용 시
  useEffect(() => {
    if (!privateContainer.current) return;
    privateContainer.current.style.display = "none";

    const observer = new MutationObserver(() => {
      if (!privateContainer.current) return;

      privateContainer.current.style.display =
        privateContainer.current.children.length === 0 ? "none" : "block";
    });
    observer.observe(privateContainer.current, { childList: true });

    return () => observer.disconnect();
  }, []);

  return (
    <>
      <div className="app" style={{ width: "100%", height: "100vh" }}>
        <VChatCloudApp
          clientKey={clientKey}
          email="YOUR_EMAIL" // VChatCloud CMS계정 ID
          grade={grade}
          nickName={nickName}
          privateContainer=".private-container" // querySelector 파라미터 또는 HTMLElement
          roomId={roomId}
          sessionType="parameter"
          userInfo={userInfo}
          // logoUrl="LOGO_IMG_URL"
          // company="YOUR_COMPANY_NAME"
        />
      </div>
      {/* 비밀채팅용 wrapper */}
      <div
        className="app private-container"
        ref={privateContainer}
        style={{ width: "100%", height: "100vh" }}
      />
    </>
  );
};

export default App;
```

## 컴포넌트 설명

### VChatCloudApp

`VChatCloudApp` 컴포넌트는 VChatCloud 서비스를 설정하고 실행하는 메인 컴포넌트입니다. 주요 props는 다음과 같습니다:

- `clientKey` : 사용자 고유의 클라이언트 키.
- `email` : VChatCloud CMS 계정 ID.
- `grade` : 사용자 등급 (`user`, `userManager` 등).
- `nickName` : 사용자 닉네임.
- `roomId` : 채팅방 ID.
- `privateContainer` : 비밀 채팅을 위한 HTML 컨테이너 (`querySelector 파라미터` 또는 `HTMLElement`).
- `sessionType` : 세션 타입
  - `login` : 로그인 화면을 사용해 접속자가 원하는 닉네임과 프로필을 설정할 수 있습니다.
  - `parameter` : 파라미터로 사용자의 정보를 불러와 사용합니다. 개발자가 지정한 정보를 사용하게 됩니다.
  - `private` : 파라미터와 동일하게 사용자의 정보를 불러와 사용하지만, 비밀채팅을 이용할 때 설정합니다.
- `userInfo` : 추가 사용자 정보 (객체 형태). `userInfo.profile`에 숫자를 지정하면 기본 제공하는 프로필 이미지를 사용할 수 있습니다. _(1 ~ 48까지 제공)_
- `logoUrl` : 로그인 화면에서 표시할 로고 이미지를 변경할 수 있습니다.
- `company` : 로그인 화면에서 표시할 푸터의 회사 정보를 변경할 수 있습니다.

## 주의사항

- **`StrictMode` 비활성화 필요** : 개발서버로 실행 시 `StrictMode`를 비활성화 하여야 오류가 발생하지 않습니다.
- **중복된 `clientKey` 주의** : 같은 clientKey로 로그인 시 기존에 로그인 된 사용자가 접속이 종료됩니다. `setRandomClientKey` 함수를 사용하여 랜덤 clientKey를 생성할 수 있습니다.

## 커스터마이징

### 컴포넌트 변경

`VChatCloudApp`을 사용하는 대신 `VChatCloudProvider`를 사용하실 수 있습니다. `VChatCloudProvider`내부에 원하는 컴포넌트를 사용하실 수 있으며, `useVChatCloud`훅을 불러와 필요한 데이터에 접근할 수 있습니다. 단 `useVChatCloud`훅을 사용할 경우 반드시 `VChatCloudProvider` 컴포넌트 내부에서 사용하여야 합니다.

```tsx
import { VChatCloudApp, useVChatCloud } from "@vchatcloud/react-ui-kit";
import { useCallback } from "react";

const SendButton = () => {
  const { channel } = useVChatCloud();

  const send = useCallback(() => {
    channel?.sendMessage({ message: "Hello VChatCloud!" });
  }, [channel]);

  return <button onClick={send}>전송</button>;
};

const App = () => {
  return (
    <VChatCloudProvider
      clientKey={clientKey}
      company={company}
      email={email}
      grade={grade}
      logoUrl={logoUrl}
      nickName={nickName}
      privateContainer={privateContainer}
      roomId={roomId}
      sessionType={sessionType}
      url={url}
      userInfo={userInfo}
      onDisconnect={onDisconnect}
    >
      {/* 원하는 컴포넌트로 재정의하세요. */}
      <SendButton />
    </VChatCloudProvider>
  );
};

export default App;
```

### 색상 변경

css 변수를 사용하여 원하는 부분을 덮어 써 색상을 재 지정할 수 있습니다.  
목록은 아래에 있습니다.

<details>

```css
/* 일반, 라이트 모드 */
:root,
:root .light {
  --button-primary: #0a0a6b;
  --button-secondary: #fff;
  --image-invert: none;
  --scrollbar-bg: #cfcfcf;

  --login-bg: #c9c9f2;
  --login-desc: #0033ab;
  --login-footer: #0a0a6b4c;

  --profile-shadow: 1px 1.7px 7px 0 rgba(201, 201, 201, 0.3);
  --profile-border: #f2f2f2;
  --profile-bg: #fff;
  --profile-button-bg: #aaa;
  --profile-img-border: #eaeaea;
  --profile-input-border: #dddddd;
  --profile-input-color: #000;
  --profile-input-focus-border: #0033ab;

  --notify-toast-box-shadow: 1.5px 2.6px 5px 0 rgba(168, 168, 168, 0.3);
  --notify-toast-bg-color: #363940;
  --notify-toast-text-color: #fff;

  --drawer-color-bg: #fff;
  --drawer-color-text: #333;
  --drawer-color-border: #ddd;
  --drawer-color-shadow: rgba(168, 168, 168, 0.3);
  --drawer-color-drawer-border: #f2f2f2;

  --file-drawer-border-color: #ddd;
  --file-drawer-header-title-color: #333;
  --file-drawer-tab-color: #666;
  --file-drawer-tab-focus-color: #000;
  --file-drawer-tab-focus-border-color: #000;
  --file-drawer-select-count-color: #666;
  --file-drawer-save-color: #000;

  --preview-item-checked-border-color: #4160b9;

  --file-item-preview-bg: #fff;
  --file-item-preview-border: #f2f2f2;
  --file-item-preview-text: #000;
  --file-item-preview-sub-text: #999;

  --translate-modal-shadow: rgba(168, 168, 168, 0.3);
  --translate-modal-popup-bg: #fff;
  --translate-modal-border-color: #f2f2f2;
  --translate-modal-title-color: #333;
  --translate-modal-translate-bg-color: #ddd;
  --translate-modal-translate-text-color: #000;

  --modal-dim: rgba(0, 0, 0, 0.7);
  --modal-shadow: rgba(168, 168, 168, 0.3);
  --modal-bg: #fff;
  --modal-border: #f2f2f2;
  --modal-title: #333;
  --modal-content: #444;

  --invite-popup-input-bg: #fff;
  --invite-popup-input-border: #ddd;
  --invite-popup-input-color: #666;

  --secret-bg-color: #ddd;
  --secret-text-color: #000;

  --checkbox-border-color: #ddd;
  --checkbox-bg-color: #fff;

  --user-item-border-color: #f2f2f2;
  --user-item-nickname-color: #333;
  --user-item-translate-color: #999;

  --toggle-bar-color: #ddd;
  --toggle-bar-on-color: #c9ddff;
  --toggle-dot-color: #999;
  --toggle-dot-on-color: #2a61be;
  --toggle-label-color: #666;

  --whisper-modal-input-text-color: #000;
  --whisper-modal-input-bg-color: #fff;
  --whisper-modal-input-border-color: #ddd;

  --user-popup-shadow: rgba(168, 168, 168, 0.3);
  --user-popup-border: #f2f2f2;
  --user-popup-bg: #fff;
  --user-popup-text: #000;
  --user-popup-focus-bg: #0a0a6b;
  --user-popup-focus-text: #fff;
  --user-popup-opacity: 1;
  --user-popup-focus-opacity: 1;

  --chatting-field-bg: #fff;
  --chatting-field-nickname: #0033ab;
  --chatting-field-input-text: #000;
  --chatting-field-input-placeholder: #888;
  --chatting-field-border-color: #ddd;
  --chatting-field-input-size-color: #999;

  --emoticon-field-border-color: #ddd;
  --emoticon-field-hover-bg-color: rgba(0, 0, 0, 0.1);
  --emoticon-field-list-bg-color: #f2f2f2;
  --emoticon-field-list-opacity: 1;
  --emoticon-field-focus-border-color: #ddd;
  --emoticon-field-focus-box-shadow: 1px 1.7px 1px 0 rgba(207, 207, 207, 0.3);
  --emoticon-field-focus-bg-color: #fff;

  --scroll-down-bg: #fff;
  --scroll-down-text: #333;
  --scroll-down-border: #ddd;
  --scroll-down-shadow: rgba(105, 105, 105, 0.25);

  --chatting-header-bg: #f2f2f2;
  --chatting-header-border: #f2f2f2;
  --chatting-header-text: #000;
  --chatting-header-user-count: #999;

  --chatting-body-bg: #fff;

  --notice-item-bg: #333;
  --notice-item-content: #fff;
  --notice-item-shadow: rgba(157, 173, 184, 0.3);

  --chat-base-border: #f2f2f2;
  --chat-base-nickname: #666;
  --chat-base-time: #999;

  --text-chat-item-message: #333;

  --whisper-item-from: #e1dffc;
  --whisper-item-nickname-from: #0a0a6b;
  --whisper-item-to: #f2f2f2;
  --whisper-item-nickname-to: #0a0a6b;
  --whisper-item-nickname-where: #666;
  --whisper-item-message: #010000;
  --whisper-item-time: #999;
  --whisper-item-shadow: rgba(157, 173, 184, 0.3);

  --join-item-message: #0033ab;

  --exit-item-message: #ff5a5a;

  --alert-item-message: #ff7800;

  --open-graph-item-border: #ddd;
  --open-graph-item-shadow: rgba(157, 173, 184, 0.3);
  --open-graph-item-background: #fff;
  --open-graph-item-title: #333;
  --open-graph-item-desc: #666;

  --radio-group-border: #ddd;
  --radio-group-checked: #000;
  --radio-group-radio-bg: #fff;
  --radio-group-text: #000;
}

/* 다크 모드 */
:root .dark {
  --button-primary: #fff;
  --button-secondary: #000;
  --image-invert: invert(100%);
  --scrollbar-bg: #444;

  --login-bg: #292929;
  --login-desc: #ddd;
  --login-footer: #aaa;

  --profile-shadow: 1.5px 2.6px 5px 0 rgba(0, 0, 0, 0.3);
  --profile-border: #444;
  --profile-bg: #313131;
  --profile-button-bg: #aaa;
  --profile-img-border: #444;
  --profile-input-border: #444;
  --profile-input-color: #fff;
  --profile-input-focus-border: #fff;

  --notify-toast-box-shadow: 1.5px 2.6px 5px 0 rgba(0, 0, 0, 0.3);
  --notify-toast-bg-color: #333;
  --notify-toast-text-color: #fff;

  --drawer-color-bg: #333;
  --drawer-color-text: #fff;
  --drawer-color-border: #444;
  --drawer-color-shadow: rgba(0, 0, 0, 0.3);
  --drawer-color-drawer-border: #444;

  --file-drawer-border-color: #444;
  --file-drawer-header-title-color: #fff;
  --file-drawer-tab-color: #ddd;
  --file-drawer-tab-focus-color: #fff;
  --file-drawer-tab-focus-border-color: #fff;
  --file-drawer-select-count-color: #fff;
  --file-drawer-save-color: #fff;

  --preview-item-checked-border-color: #0ed893;

  --file-item-preview-bg: #fff;
  --file-item-preview-border: #f2f2f2;
  --file-item-preview-text: #000;
  --file-item-preview-sub-text: #999;

  --translate-modal-shadow: rgba(0, 0, 0, 0.3);
  --translate-modal-popup-bg: #313131;
  --translate-modal-border-color: #444;
  --translate-modal-title-color: #fff;
  --translate-modal-translate-bg-color: #222;
  --translate-modal-translate-text-color: #fff;

  --modal-dim: rgba(0, 0, 0, 0.7);
  --modal-shadow: rgba(0, 0, 0, 0.3);
  --modal-bg: #313131;
  --modal-border: #444;
  --modal-title: #fff;
  --modal-content: #fff;
  --modal-button-primary: #fff;

  --invite-popup-input-bg: #000;
  --invite-popup-input-border: #444;
  --invite-popup-input-color: #aaa;

  --secret-bg-color: #222;
  --secret-text-color: #fff;

  --checkbox-border-color: #ddd;
  --checkbox-bg-color: #fff;

  --user-item-border-color: #444;
  --user-item-nickname-color: #fff;
  --user-item-translate-color: #aaa;

  --toggle-bar-color: #666;
  --toggle-bar-on-color: #fff;
  --toggle-dot-color: #999;
  --toggle-dot-on-color: #0ed893;
  --toggle-label-color: #ddd;

  --whisper-modal-input-text-color: #fff;
  --whisper-modal-input-bg-color: #191919;
  --whisper-modal-input-border-color: #444;

  --user-popup-shadow: rgba(0, 0, 0, 0.3);
  --user-popup-border: #444;
  --user-popup-bg: #313131;
  --user-popup-text: #fff;
  --user-popup-focus-bg: #313131;
  --user-popup-focus-text: #fff;
  --user-popup-opacity: 0.7;
  --user-popup-focus-opacity: 1;

  --chatting-field-bg: #292929;
  --chatting-field-nickname: #aaa;
  --chatting-field-input-text: #fff;
  --chatting-field-input-placeholder: #aaa;
  --chatting-field-border-color: #444;
  --chatting-field-input-size-color: #ddd;

  --emoticon-field-border-color: #444;
  --emoticon-field-hover-bg-color: rgba(255, 255, 255, 0.1);
  --emoticon-field-list-bg-color: #353535;
  --emoticon-field-list-opacity: 0.5;
  --emoticon-field-focus-border-color: #353535;
  --emoticon-field-focus-box-shadow: 1px 1.7px 1px 0 rgba(0, 0, 0, 0.3);
  --emoticon-field-focus-bg-color: #292929;

  --scroll-down-bg: #353535;
  --scroll-down-text: #aaa;
  --scroll-down-border: #353535;
  --scroll-down-shadow: rgba(0, 0, 0, 0.25);

  --chatting-header-bg: #191919;
  --chatting-header-border: #444;
  --chatting-header-text: #fff;
  --chatting-header-user-count: #ddd;

  --chatting-body-bg: #292929;

  --notice-item-bg: #111;
  --notice-item-content: #fff;
  --notice-item-shadow: rgba(0, 0, 0, 0.3);

  --chat-base-border: #444;
  --chat-base-nickname: #aaa;
  --chat-base-time: #aaa;

  --text-chat-item-message: #fff;

  --whisper-item-from: #384150;
  --whisper-item-nickname-from: #dfb84e;
  --whisper-item-to: #353535;
  --whisper-item-nickname-to: #00ffc0;
  --whisper-item-nickname-where: #aaa;
  --whisper-item-message: #fff;
  --whisper-item-time: #aaa;
  --whisper-item-shadow: rgba(0, 0, 0, 0.3);

  --join-item-message: #00ffc0;

  --exit-item-message: #ff3a54;

  --alert-item-message: #fff000;

  --open-graph-item-border: #444;
  --open-graph-item-shadow: rgba(0, 0, 0, 0.3);
  --open-graph-item-background: #fff;
  --open-graph-item-title: #333;
  --open-graph-item-desc: #666;

  --radio-group-border: #ddd;
  --radio-group-checked: #000;
  --radio-group-radio-bg: #fff;
  --radio-group-text: #fff;
}
```

</details>

아래와 같이 원하는 변수를 재 지정하여 덮어쓸 수 있습니다.

```css
:root,
:root .light {
  --button-primary: skyblue;
}

/* 다크모드 색상 */
:root .dark {
  --button-primary: beige;
}
```

## 추가 정보

더 많은 정보와 자세한 사용법은 [공식 문서 링크](https://www.vchatcloud.com/doc/react)를 참조하세요.
