npm package: @gcsc/common-compo
Description: 공통 컴포넌트 패키지
Author: Jeongin Choi
Latest version: 0.4.4

# Vue 3 + TypeScript + Vite 공통 컴포넌트 패키지

### 1. BaseList 컴포넌트
#### 1-1) 요약
```
BaseList는 세 가지 모드(기본, 드래그 가능, 트리 구조)를 지원하는 다목적 리스트 컴포넌트입니다. 체크박스 기능, 드래그 앤 드롭, 트리 구조의 계층형 데이터를 표시할 수 있으며, 각 요소를 슬롯을 통해 커스터마이징할 수 있습니다.
```
#### 1-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| items | ListItem[] | ✓ | - | 리스트에 표시할 아이템 배열 |
| draggable | boolean | - | false | 드래그 앤 드롭 기능 활성화 |
| tree | boolean | - | false | 트리 구조 활성화 |
| checkable | boolean | - | false | 체크박스 기능 활성화 |

#### 1-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| header-checkbox-content | - | 전체 선택 헤더 커스터마이징 |
| item-checkbox | { item, checked } | 아이템 체크박스 커스터마이징 |
| item-image | { item } | 아이템 이미지 커스터마이징 |
| item-header | { item } | 아이템 헤더 영역 커스터마이징 |
| item-title | { item } | 아이템 제목 커스터마이징 |
| item-time | { item } | 아이템 시간 커스터마이징 |
| item-preview | { item } | 아이템 미리보기 커스터마이징 |
| tree-toggle | { item } | 트리 토글 버튼 커스터마이징 |
| child-item-checkbox | { item, checked } | 자식 아이템 체크박스 커스터마이징 |
| child-item-image | { item } | 자식 아이템 이미지 커스터마이징 |
| child-item-header | { item } | 자식 아이템 헤더 커스터마이징 |
| child-item-title | { item } | 자식 아이템 제목 커스터마이징 |
| child-item-time | { item } | 자식 아이템 시간 커스터마이징 |
| child-item-preview | { item } | 자식 아이템 미리보기 커스터마이징 |

#### 1-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| itemClick | (item: ListItem) | 아이템 클릭 시 발생 |
| check | (checkedItems: number[]) | 체크박스 상태 변경 시 발생 |
| checkAll | (checked: boolean) | 전체 선택/해제 시 발생 |
| update:items | (items: ListItem[]) | 드래그로 순서 변경 시 발생 |
| update:modelValue | (value: number[]) | 선택된 아이템 ID 배열 업데이트 |

#### 1-5) 타입 정의
```typescript
interface ListItem {
  id: number;
  imageUrl: string;
  title: string;
  preview: string;
  time: string | Date;
  checked?: boolean;
  children?: ListItem[];
  isOpen?: boolean;
}

interface BaseListProps {
  modelValue?: number[];
  items: ListItem[];
  draggable?: boolean;
  tree?: boolean;
  checkable?: boolean;
}

type BaseListEmits = {
  (e: 'itemClick', item: ListItem): void;
  (e: 'update:items', items: ListItem[]): void;
  (e: 'update:modelValue', value: number[]): void;
  (e: 'check', checkedItems: number[]): void;
  (e: 'checkAll', checked: boolean): void;
}
```

#### 1-6) 사용 예시
##### 1-6-1) 기본 리스트
```vue
<template>
  <BaseList 
    :items="items"
    :checkable="true"
    @item-click="handleItemClick"
  >
    <template #item-title="{ item }">
      <div class="custom-title">{{ item.title }}</div>
    </template>
  </BaseList>
</template>
```
##### 1-6-2) 드래거블 리스트
```vue
<template>
  <BaseList 
    :items="items"
    :draggable="true"
    @update:items="handleReorder"
  >
    <template #item-preview="{ item }">
      <div class="preview-with-badge">
        <span class="badge">{{ item.category }}</span>
        <p>{{ item.preview }}</p>
      </div>
    </template>
  </BaseList>
</template>
```
##### 1-6-3) 트리 리스트
```vue
<template>
  <BaseList 
    :items="treeItems"
    :tree="true"
    :checkable="true"
    @check="handleCheck"
  >
    <template #tree-toggle="{ item }">
      <i :class="['tree-icon', item.isOpen ? 'open' : 'closed']" />
    </template>
    
    <template #child-item-title="{ item }">
      <span class="child-title">{{ item.title }}</span>
    </template>
  </BaseList>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { ListItem } from '@/components/BaseList.vue'

// 리스트 아이템 데이터
const items = ref<ListItem[]>([
  {
    id: 1,
    imageUrl: 'https://example.com/image1.jpg',
    title: '첫 번째 아이템',
    preview: '이것은 첫 번째 아이템의 미리보기입니다.',
    time: new Date(),
    checked: false
  },
  {
    id: 2,
    imageUrl: 'https://example.com/image2.jpg',
    title: '두 번째 아이템',
    preview: '이것은 두 번째 아이템의 미리보기입니다.',
    time: new Date(),
    checked: false
  },
    
...(생략)
</script>
```

### 2. BaseTable 컴포넌트
#### 2-1) 요약
```
BaseTable은 정렬 기능을 지원하는 기본 테이블 컴포넌트입니다. 컬럼별 정렬, 행 클릭 이벤트, 슬롯을 통한 커스텀 셀 렌더링을 지원하며, 데이터가 없을 경우 안내 메시지를 표시합니다.
```
#### 2-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| columns | TableColumn[] | ✓ | - | 테이블 컬럼 설정 배열 |
| data | TableItem[] | ✓ | - | 테이블에 표시할 데이터 배열 |

#### 2-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| [column.key] | { row: TableItem } | 각 컬럼별 커스텀 셀 렌더링 |

#### 2-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| row-click | (row: TableItem) | 행 클릭 시 발생 |
| sort | (sortStates: SortState[]) | 정렬 상태 변경 시 발생 |

#### 2-5) 타입 정의
```typescript
interface TableColumn {
  key: string;
  label: string;
  sortable?: boolean;
}

interface TableItem {
  [key: string]: any;
}

interface SortState {
  column: string;
  direction: 'asc' | 'desc';
}

interface TableProps {
  columns: TableColumn[];
  data: TableItem[];
}

type TableEmits = {
  (e: 'row-click', row: TableItem): void;
  (e: 'sort', sortStates: SortState[]): void;
}
```
#### 2-6) 사용 예시
```vue
<template>
  <BaseTable
      :columns="columns"
      :data="tableData"
      @row-click="handleRowClick"
      @sort="handleSort"
  >
    <!-- 이름 컬럼 커스터마이징 -->
    <template #name="{ row }">
      <div class="user-name">
        <img :src="row.avatar" :alt="row.name" class="avatar">
        <span>{{ row.name }}</span>
      </div>
    </template>

    <!-- 상태 컬럼 커스터마이징 -->
    <template #status="{ row }">
      <span :class="['status-badge', row.status]">
        {{ row.status }}
      </span>
    </template>
  </BaseTable>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { TableColumn, TableItem, SortState } from '@gcsc/common-compo'

// 컬럼 정의
const columns: TableColumn[] = [
  { key: 'name', label: '이름', sortable: true },
  { key: 'email', label: '이메일', sortable: true },
  { key: 'status', label: '상태', sortable: true },
  { key: 'createdAt', label: '생성일', sortable: true }
]

// 테이블 데이터
const tableData = ref<TableItem[]>([
  {
    name: '홍길동',
    email: 'hong@example.com',
    status: 'active',
    createdAt: '2024-03-20',
    avatar: 'https://example.com/avatar1.jpg'
  },
  // ... 더 많은 데이터
])

...(생략)
</script>
```
### 3. BaseCalendar 컴포넌트
#### 3-1) 요약
```
BaseCalendar는 날짜와 시간을 선택할 수 있는 캘린더 컴포넌트입니다. 연도, 월, 일 선택 기능을 제공하며, 시간 선택 옵션도 포함되어 있습니다. 다양한 뷰(연도, 월, 일)를 지원하고 날짜 범위 선택이 가능합니다.
```

#### 3-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| modelValue | DateRange | - | { start: null, end: null } | 선택된 날짜 범위 |
| currentDate | Dayjs | - | dayjs() | 현재 날짜 |
| startDate | string \| null | - | null | 시작 날짜 |
| endDate | string \| null | - | null | 종료 날짜 |
| selectedInput | 'start' \| 'end' \| null | - | null | 선택된 입력 필드 |
| enableTimeSelection | boolean | - | false | 시간 선택 활성화 |
| minDate | string \| undefined | - | undefined | 최소 선택 가능 날짜 |
| maxDate | string \| undefined | - | undefined | 최대 선택 가능 날짜 |
| disabledDates | string[] | - | [] | 비활성화된 날짜 배열 |
| firstDayOfWeek | number | - | 0 | 주의 첫 번째 요일 |

#### 3-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| close-button | - | 닫기 버튼 커스터마이징 |
| time-selection | { startDate, endDate, startTime, endTime, updateStartTime, updateEndTime } | 시간 선택 영역 커스터마이징 |
| footer | { onRefresh, onCancel, onApply } | 푸터 영역 커스터마이징 |

#### 3-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| select | (range: DateRange) | 날짜/시간 선택 시 발생 |
| apply | (range: DateRange) | 선택 적용 시 발생 |
| cancel | - | 취소 시 발생 |

#### 3-5) 타입 정의
```typescript
interface DateRange {
  start: string | null;
  end: string | null;
}

interface TimeValue {
  hours: number;
  minutes: number;
}

type CalendarView = 'calendar' | 'month' | 'year';

interface CalendarProps {
  modelValue?: DateRange;
  currentDate?: Dayjs;
  startDate?: string | null;
  endDate?: string | null;
  selectedInput?: 'start' | 'end' | null;
  enableTimeSelection?: boolean;
  minDate?: string;
  maxDate?: string;
  disabledDates?: string[];
  firstDayOfWeek?: number;
}
```

#### 3-6) 사용 예시
```vue
<template>
  <BaseCalendar
    v-model="dateRange"
    :enable-time-selection="true"
    @select="handleDateSelect"
    @apply="handleApply"
    @cancel="handleCancel"
  >
    <!-- 닫기 버튼 커스터마이징 -->
    <template #close-button>
      <button class="close-btn">×</button>
    </template>

    <!-- 시간 선택 영역 커스터마이징 -->
    <template #time-selection="{ startTime, endTime, updateStartTime, updateEndTime }">
      <div class="custom-time-selector">
        <TimeSelector
          :start-time="startTime"
          :end-time="endTime"
          @update:start-time="updateStartTime"
          @update:end-time="updateEndTime"
        />
      </div>
    </template>

    <!-- 푸터 영역 커스터마이징 -->
    <template #footer="{ onApply, onCancel }">
      <div class="custom-footer">
        <button @click="onCancel">취소</button>
        <button @click="onApply">확인</button>
      </div>
    </template>
  </BaseCalendar>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { DateRange } from '@gcsc/common-compo'

const dateRange = ref<DateRange>({
  start: null,
  end: null
})

...(생략)
</script>
```

### 4. InputCalendar 컴포넌트
#### 4-1) 요약
```
InputCalendar는 날짜 범위를 선택할 수 있는 입력 필드와 캘린더 팝업을 제공하는 컴포넌트입니다. 시작일과 종료일을 선택할 수 있으며, 시간 선택 옵션도 지원합니다. 두 개의 연속된 캘린더를 통해 직관적인 날짜 범위 선택이 가능합니다.
```

#### 4-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| modelValue | DateRange | ✓ | - | 선택된 날짜 범위 |
| enableTimeSelection | boolean | - | false | 시간 선택 기능 활성화 |
| placeholder | string | - | - | 입력 필드 플레이스홀더 |

#### 4-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| close-button | { onCancel } | 닫기 버튼 커스터마이징 |
| footer | { onRefresh, onCancel, onApply } | 푸터 영역 커스터마이징 |

#### 4-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:modelValue | (value: DateRange) | 날짜 범위 변경 시 발생 |
| cancel | - | 캘린더 닫기 시 발생 |

#### 4-5) 타입 정의
```typescript
interface DateRange {
  start: string | null;
  end: string | null;
}

interface DateTime {
  date: string;
  fullInput: string;
  time: {
    hours: number;
    minutes: number;
  };
}

interface InputCalendarProps {
  modelValue: DateRange;
  enableTimeSelection?: boolean;
  placeholder?: string;
}

interface DateInfo {
  date: string;
  day: number;
  currentMonth: boolean;
}
```

#### 4-6) 사용 예시
```vue
<template>
  <InputCalendar
    v-model="dateRange"
    :enable-time-selection="true"
    placeholder="YYYY-MM-DD HH:mm"
    @cancel="handleCancel"
  >
    <!-- 닫기 버튼 커스터마이징 -->
    <template #close-button="{ onCancel }">
      <button class="close-btn" @click="onCancel">×</button>
    </template>

    <!-- 푸터 커스터마이징 -->
    <template #footer="{ onRefresh, onCancel, onApply }">
      <div class="calendar-footer">
        <button @click="onRefresh">초기화</button>
        <button @click="onCancel">취소</button>
        <button @click="onApply">확인</button>
      </div>
    </template>
  </InputCalendar>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { DateRange } from '@gcsc/common-compo'

const dateRange = ref<DateRange>({
  start: null,
  end: null
})

...(생략)
</script>
```

### 5. BaseCheckbox 컴포넌트
#### 5-1) 요약
```
BaseCheckbox는 단일 체크박스와 다중 선택(체크박스/라디오) 기능을 제공하는 컴포넌트입니다. 전체 선택 기능과 개별 항목 선택이 가능하며, 비활성화 상태도 지원합니다. 체크박스와 라디오 버튼 두 가지 타입을 제공합니다.
```

#### 5-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| modelValue | boolean \| (string \| number)[] \| string \| number | ✓ | false | 선택된 값 |
| items | CheckboxItem[] | - | [] | 선택 항목 배열 |
| label | string | - | '' | 단일 체크박스 라벨 |
| type | 'checkbox' \| 'radio' | - | 'checkbox' | 체크박스 타입 |
| name | string | - | '' | 입력 필드 이름 |
| disabled | boolean | - | false | 비활성화 여부 |

#### 5-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| default | - | 단일 체크박스 라벨 커스터마이징 |
| select-all | { allChecked, indeterminate, toggleAll } | 전체 선택 영역 커스터마이징 |

#### 5-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:modelValue | (value: boolean \| (string \| number)[] \| string \| number) | 값 변경 시 발생 |
| check | (value: boolean \| (string \| number)[] \| string \| number) | 항목 선택 시 발생 |
| checkAll | (checked: boolean) | 전체 선택/해제 시 발생 |

#### 5-5) 타입 정의
```typescript
interface CheckboxItem {
  id: number;
  label: string;
}

interface CheckboxProps {
  modelValue?: boolean | (string | number)[] | string | number;
  items?: CheckboxItem[];
  label?: string;
  type?: 'checkbox' | 'radio';
  name?: string;
  disabled?: boolean;
}

type CheckboxEmits = {
  (e: 'update:modelValue', value: boolean | (string | number)[] | string | number): void;
  (e: 'check', value: boolean | (string | number)[] | string | number): void;
  (e: 'checkAll', checked: boolean): void;
}
```

#### 5-6) 사용 예시
```vue
<template>
  <!-- 단일 체크박스 -->
  <BaseCheckbox
    v-model="isChecked"
    label="동의합니다"
    @check="handleCheck"
  />

  <!-- 다중 체크박스 -->
  <BaseCheckbox
    v-model="selectedItems"
    :items="checkboxItems"
    type="checkbox"
    @check="handleItemsCheck"
    @checkAll="handleCheckAll"
  >
    <!-- 전체 선택 커스터마이징 -->
    <template #select-all="{ allChecked, indeterminate, toggleAll }">
      <div class="select-all-wrapper">
        <label class="checkbox-item">
          <input
            type="checkbox"
            :checked="allChecked"
            :indeterminate="indeterminate"
            @change="toggleAll"
          />
          <span class="item-label">전체 선택</span>
        </label>
      </div>
    </template>
  </BaseCheckbox>

  <!-- 라디오 버튼 -->
  <BaseCheckbox
    v-model="selectedOption"
    :items="radioItems"
    type="radio"
    name="options"
    @check="handleOptionSelect"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { CheckboxItem } from '@gcsc/common-compo'

// 단일 체크박스
const isChecked = ref(false)
const handleCheck = (checked: boolean) => {
console.log('체크 상태:', checked)
}

// 다중 체크박스
const checkboxItems: CheckboxItem[] = [
{ id: 1, label: '옵션 1' },
{ id: 2, label: '옵션 2' },
{ id: 3, label: '옵션 3' }
]
const selectedItems = ref<(string | number)[]>([])

const handleItemsCheck = (items: (string | number)[]) => {
console.log('선택된 항목:', items)
}

const handleCheckAll = (checked: boolean) => {
console.log('전체 선택:', checked)
}

// 라디오 버튼
const radioItems: CheckboxItem[] = [
{ id: 1, label: '라디오 1' },
{ id: 2, label: '라디오 2' },
{ id: 3, label: '라디오 3' }
]
const selectedOption = ref<number | null>(null)

const handleOptionSelect = (option: number) => {
console.log('선택된 옵션:', option)
}
</script>
```

### 6. ListCheckbox 컴포넌트
#### 6-1) 요약
```
ListCheckbox는 리스트 형태의 항목들에 대한 체크박스 기능을 제공하는 컴포넌트입니다. 전체 선택/해제 기능과 개별 항목 선택이 가능하며, 선택된 항목 수를 표시합니다. 리스트의 헤더에 위치하여 일괄적인 항목 선택을 관리합니다.
```

#### 6-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| items | ListItem[] | ✓ | [] | 체크박스 항목 배열 |
| modelValue | number[] | ✓ | [] | 선택된 항목 ID 배열 |

#### 6-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| header-checkbox | { allChecked, indeterminate, toggleAll } | 헤더 체크박스 영역 커스터마이징 |
| header-checkbox-content | - | 헤더 체크박스 내용 커스터마이징 |

#### 6-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:modelValue | (value: number[]) | 선택된 항목 배열 변경 시 발생 |
| check | (checkedIds: number[]) | 항목 선택/해제 시 발생 |
| checkAll | (checked: boolean) | 전체 선택/해제 시 발생 |

#### 6-5) 타입 정의
```typescript
interface ListItem {
  id: number;
  label: string;
}

interface ListCheckboxProps {
  items: ListItem[];
  modelValue: number[];
}

type BaseListEmits = {
  (e: 'update:modelValue', value: number[]): void;
  (e: 'check', checkedIds: number[]): void;
  (e: 'checkAll', checked: boolean): void;
}
```

#### 6-6) 사용예시
```vue
<template>
  <ListCheckbox
    v-model="selectedItems"
    :items="listItems"
    @check="handleCheck"
    @checkAll="handleCheckAll"
  >
    <!-- 헤더 체크박스 커스터마이징 -->
    <template #header-checkbox="{ allChecked, indeterminate, toggleAll }">
      <div class="custom-header-checkbox">
        <label>
          <input
            type="checkbox"
            :checked="allChecked"
            :indeterminate="indeterminate"
            @change="toggleAll"
          />
          <slot name="header-checkbox-content">
            <span class="selection-count">
              선택된 항목: {{ selectedItems.length }}/{{ listItems.length }}
            </span>
          </slot>
        </label>
      </div>
    </template>
  </ListCheckbox>

  <!-- 리스트 항목들 -->
  <div class="list-items">
    <div
      v-for="item in listItems"
      :key="item.id"
      class="list-item"
      @click="toggleItem(item.id, $event)"
    >
      <input
        type="checkbox"
        :checked="isChecked(item.id)"
        @change="e => e.stopPropagation()"
      />
      <span>{{ item.label }}</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { ListItem } from '@gcsc/common-compo'

// 리스트 항목 데이터
const listItems: ListItem[] = [
  { id: 1, label: '항목 1' },
  { id: 2, label: '항목 2' },
  { id: 3, label: '항목 3' },
  { id: 4, label: '항목 4' }
]

const selectedItems = ref<number[]>([])

// 이벤트 핸들러
const handleCheck = (checkedIds: number[]) => {
  console.log('선택된 항목:', checkedIds)
}

const handleCheckAll = (checked: boolean) => {
  console.log('전체 선택 상태:', checked)
}

// 항목 토글 함수
const toggleItem = (itemId: number, event: Event) => {
  event.stopPropagation()
  const index = selectedItems.value.indexOf(itemId)
  if (index === -1) {
    selectedItems.value.push(itemId)
  } else {
    selectedItems.value.splice(index, 1)
  }
}
</script>
```

### 7. BaseButton 컴포넌트
#### 7-1) 요약
```
BaseButton은 다양한 스타일과 기능을 제공하는 기본 버튼 컴포넌트입니다. 다양한 변형(variant)을 지원하며, 비활성화 상태와 타입(button, submit, reset)을 설정할 수 있습니다. 클릭과 제출 이벤트를 처리할 수 있습니다.
```

#### 7-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| variant | 'primary' \| 'secondary' \| 'outline' \| 'text' | ✓ | - | 버튼 스타일 변형 |
| disabled | boolean | - | false | 비활성화 여부 |
| type | 'button' \| 'submit' \| 'reset' | - | 'button' | 버튼 타입 |

#### 7-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| default | - | 버튼 내용 커스터마이징 |

#### 7-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| click | (event: MouseEvent) | 클릭 시 발생 |
| submit | (event: Event) | 제출 시 발생 |

#### 7-5) 타입 정의
```typescript
type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text';
type ButtonType = 'button' | 'submit' | 'reset';

interface ButtonProps {
  variant: ButtonVariant;
  disabled?: boolean;
  type?: ButtonType;
}

type ButtonEmits = {
  (e: 'click', event: MouseEvent): void;
  (e: 'submit', event: Event): void;
}
```

#### 7-6) 사용 예시
```vue
<template>
  <div class="button-examples">
    <!-- 기본 버튼 -->
    <BaseButton
      variant="primary"
      @click="handleClick"
    >
      기본 버튼
    </BaseButton>

    <!-- 제출 버튼 -->
    <BaseButton
      variant="primary"
      type="submit"
      @submit="handleSubmit"
    >
      제출
    </BaseButton>

    <!-- 비활성화된 버튼 -->
    <BaseButton
      variant="secondary"
      :disabled="true"
    >
      비활성화
    </BaseButton>

    <!-- 아이콘이 있는 버튼 -->
    <BaseButton
      variant="outline"
      @click="handleIconClick"
    >
      <i class="icon">+</i>
      아이콘 버튼
    </BaseButton>

    <!-- 텍스트 버튼 -->
    <BaseButton
      variant="text"
      @click="handleTextClick"
    >
      텍스트 버튼
    </BaseButton>
  </div>
</template>

<script setup lang="ts">
const handleClick = (event: MouseEvent) => {
  console.log('버튼 클릭:', event)
}

const handleSubmit = (event: Event) => {
  console.log('폼 제출:', event)
}

const handleIconClick = () => {
  console.log('아이콘 버튼 클릭')
}

const handleTextClick = () => {
  console.log('텍스트 버튼 클릭')
}
</script>

```

### 8. BaseInput 컴포넌트
#### 8-1) 요약
```
BaseInput은 다양한 스타일과 기능을 제공하는 기본 입력 필드 컴포넌트입니다. 다양한 변형(variant)을 지원하며, 비활성화 상태와 필수 입력 여부를 설정할 수 있습니다. v-model을 통한 양방향 바인딩을 지원합니다.
```

#### 8-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| modelValue | string | ✓ | - | 입력 필드 값 |
| variant | 'default' \| 'primary' \| 'error' | - | 'default' | 입력 필드 스타일 변형 |
| type | 'text' \| 'password' \| 'email' \| 'number' | - | 'text' | 입력 필드 타입 |
| disabled | boolean | - | false | 비활성화 여부 |
| placeholder | string | - | - | 플레이스홀더 텍스트 |
| required | boolean | - | false | 필수 입력 여부 |

#### 8-3) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:modelValue | (value: string) | 입력값 변경 시 발생 |

#### 8-4) 타입 정의
```typescript
type InputVariant = 'default' | 'primary' | 'error';
type InputType = 'text' | 'password' | 'email' | 'number';

interface InputProps {
  modelValue: string;
  variant?: InputVariant;
  type?: InputType;
  disabled?: boolean;
  placeholder?: string;
  required?: boolean;
}

type InputEmits = {
  (e: 'update:modelValue', value: string): void;
}
```

#### 8-5) 사용 예시
```vue
<template>
  <div class="input-examples">
    <!-- 기본 입력 필드 -->
    <BaseInput
      v-model="textValue"
      placeholder="기본 입력"
    />

    <!-- 비밀번호 입력 필드 -->
    <BaseInput
      v-model="passwordValue"
      type="password"
      placeholder="비밀번호 입력"
    />

    <!-- 이메일 입력 필드 -->
    <BaseInput
      v-model="emailValue"
      type="email"
      placeholder="이메일 입력"
      required
    />

    <!-- 숫자 입력 필드 -->
    <BaseInput
      v-model="numberValue"
      type="number"
      placeholder="숫자 입력"
    />

    <!-- 에러 상태 입력 필드 -->
    <BaseInput
      v-model="errorValue"
      variant="error"
      placeholder="에러 상태"
    />

    <!-- 비활성화된 입력 필드 -->
    <BaseInput
      v-model="disabledValue"
      disabled
      placeholder="비활성화"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const textValue = ref('')
const passwordValue = ref('')
const emailValue = ref('')
const numberValue = ref('')
const errorValue = ref('')
const disabledValue = ref('비활성화된 값')
</script>
```

### 9. BaseSelect 컴포넌트
#### 9-1) 요약
```
BaseSelect는 드롭다운 선택 필드를 제공하는 컴포넌트입니다. 옵션 배열을 통한 동적 렌더링과 사용자 정의 옵션을 위한 슬롯을 지원합니다. 플레이스홀더, 비활성화 상태, 필수 입력 여부를 설정할 수 있으며, 다양한 스타일 변형을 제공합니다.
```

#### 9-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| modelValue | string | ✓ | - | 선택된 값 |
| options | SelectOption[] | ✓ | - | 선택 옵션 배열 |
| variant | 'default' \| 'primary' \| 'error' | - | 'default' | 선택 필드 스타일 변형 |
| withAccompany | boolean | - | false | 동반 요소 표시 여부 |
| disabled | boolean | - | false | 비활성화 여부 |
| required | boolean | - | false | 필수 선택 여부 |
| placeholder | string | - | - | 플레이스홀더 텍스트 |

#### 9-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| default | - | 사용자 정의 옵션을 위한 슬롯 |

#### 9-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:modelValue | (value: string) | 선택값 변경 시 발생 |

#### 9-5) 타입 정의
```typescript
type SelectVariant = 'default' | 'primary' | 'error';

interface SelectOption {
  value: string;
  label: string;
  disabled?: boolean;
}

interface SelectProps {
  modelValue: string;
  options: SelectOption[];
  variant?: SelectVariant;
  withAccompany?: boolean;
  disabled?: boolean;
  required?: boolean;
  placeholder?: string;
}

type SelectEmits = {
  (e: 'update:modelValue', value: string): void;
}
```

#### 9-6) 사용 예시
```vue
<template>
  <div class="select-examples">
    <!-- 기본 선택 필드 -->
    <BaseSelect
      v-model="selectedValue"
      :options="options"
      placeholder="선택하세요"
    />

    <!-- 동반 요소가 있는 선택 필드 -->
    <BaseSelect
      v-model="accompanyValue"
      :options="options"
      with-accompany
      placeholder="동반 요소 선택"
    >
      <template #accompany>
        <span class="accompany-text">동반</span>
      </template>
    </BaseSelect>

    <!-- 에러 상태 선택 필드 -->
    <BaseSelect
      v-model="errorValue"
      :options="options"
      variant="error"
      placeholder="에러 상태"
    />

    <!-- 비활성화된 선택 필드 -->
    <BaseSelect
      v-model="disabledValue"
      :options="options"
      disabled
      placeholder="비활성화"
    />

    <!-- 사용자 정의 옵션이 있는 선택 필드 -->
    <BaseSelect
      v-model="customValue"
      :options="options"
      placeholder="사용자 정의 옵션"
    >
      <option value="custom1">사용자 정의 옵션 1</option>
      <option value="custom2">사용자 정의 옵션 2</option>
    </BaseSelect>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { Option } from '@gcsc/common-compo'

const options: Option[] = [
  { value: 'option1', label: '옵션 1' },
  { value: 'option2', label: '옵션 2' },
  { value: 'option3', label: '옵션 3', disabled: true },
  { value: 'option4', label: '옵션 4' }
]

const selectedValue = ref('')
const accompanyValue = ref('')
const errorValue = ref('')
const disabledValue = ref('option1')
const customValue = ref('')
</script>
```

### 10. BaseDialog 컴포넌트
#### 10-1) 요약
```
BaseDialog은 드래그 가능한 모달 다이얼로그를 제공하는 컴포넌트입니다. 오버레이, 헤더, 컨텐츠, 푸터 영역을 포함하며, 커스터마이징 가능한 슬롯을 제공합니다. 확인/취소 버튼을 기본으로 제공하며, 오버레이 클릭 시 닫기 옵션을 지원합니다.
```

#### 10-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| modelValue | boolean | ✓ | - | 모달 표시 여부 |
| title | string | - | '알림' | 모달 제목 |
| content | string | - | '' | 모달 내용 |
| showConfirm | boolean | - | true | 확인 버튼 표시 여부 |
| showCancel | boolean | - | true | 취소 버튼 표시 여부 |
| confirmText | string | - | '확인' | 확인 버튼 텍스트 |
| cancelText | string | - | '취소' | 취소 버튼 텍스트 |
| closeOnOverlayClick | boolean | - | true | 오버레이 클릭 시 닫기 여부 |

#### 10-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| title | - | 제목 영역 커스터마이징 |
| content | - | 내용 영역 커스터마이징 |
| footer | { close } | 푸터 영역 커스터마이징 |
| buttons | { close } | 버튼 영역 커스터마이징 |

#### 10-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:modelValue | (value: boolean) | 모달 표시 상태 변경 시 발생 |
| confirm | - | 확인 버튼 클릭 시 발생 |
| cancel | - | 취소 버튼 클릭 시 발생 |

#### 10-5) 타입 정의
```typescript
interface DialogProps {
  modelValue: boolean;
  title?: string;
  content?: string;
  showConfirm?: boolean;
  showCancel?: boolean;
  confirmText?: string;
  cancelText?: string;
  closeOnOverlayClick?: boolean;
}

type DilogEmits = {
  (e: 'update:modelValue', value: boolean): void;
  (e: 'confirm'): void;
  (e: 'cancel'): void;
}
```

#### 10-6) 사용 예시
```vue
<template>
  <BaseDialog
    v-model="showDialog"
    title="사용자 정보"
    @confirm="handleConfirm"
    @cancel="handleCancel"
  >
    <!-- 제목 커스터마이징 -->
    <template #title>
      <h2 class="custom-title">사용자 정보 수정</h2>
    </template>

    <!-- 내용 커스터마이징 -->
    <template #content>
      <div class="user-form">
        <div class="form-group">
          <label>이름</label>
          <input v-model="userName" type="text" />
        </div>
        <div class="form-group">
          <label>이메일</label>
          <input v-model="userEmail" type="email" />
        </div>
      </div>
    </template>

    <!-- 버튼 커스터마이징 -->
    <template #buttons="{ close }">
      <button class="custom-button save" @click="handleSave">저장</button>
      <button class="custom-button cancel" @click="close">닫기</button>
    </template>
  </BaseDialog>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const showDialog = ref(false)
const userName = ref('')
const userEmail = ref('')

const handleConfirm = () => {
  console.log('확인 버튼 클릭')
}

const handleCancel = () => {
  console.log('취소 버튼 클릭')
}

const handleSave = () => {
  console.log('저장 버튼 클릭', {
    name: userName.value,
    email: userEmail.value
  })
}
</script>
```

### 11. BasePagination 컴포넌트
#### 11-1) 요약
```
BasePagination은 페이지 네비게이션을 제공하는 컴포넌트입니다. 첫 페이지, 이전 페이지, 페이지 번호, 다음 페이지, 마지막 페이지 버튼을 포함하며, 최대 표시 페이지 수를 설정할 수 있습니다. 현재 페이지 그룹에 따라 페이지 번호를 동적으로 표시합니다.
```

#### 11-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| currentPage | number | ✓ | - | 현재 페이지 번호 |
| totalPages | number | ✓ | - | 전체 페이지 수 |
| maxDisplayPages | number | - | 5 | 한 번에 표시할 최대 페이지 수 |

#### 11-3) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| update:currentPage | (page: number) | 페이지 변경 시 발생 |

#### 11-4) 타입 정의
```typescript
interface PaginationProps {
  currentPage: number;
  totalPages: number;
  maxDisplayPages?: number;
}
```

#### 11-5) 사용 예시
```vue
<template>
  <div class="pagination-example">
    <!-- 기본 페이지네이션 -->
    <BasePagination
      v-model:current-page="currentPage"
      :total-pages="totalPages"
      @update:current-page="handlePageChange"
    />

    <!-- 커스텀 스타일 페이지네이션 -->
    <BasePagination
      v-model:current-page="customPage"
      :total-pages="totalPages"
      :max-display-pages="7"
      class="custom-pagination"
      @update:current-page="handleCustomPageChange"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const currentPage = ref(1)
const customPage = ref(1)
const totalPages = 20

const handlePageChange = (page: number) => {
  console.log('페이지 변경:', page)
}

const handleCustomPageChange = (page: number) => {
  console.log('커스텀 페이지 변경:', page)
}
</script>
```

### 12. BaseLoading 컴포넌트
#### 12-1) 요약
```
BaseLoading은 세 가지 다른 스타일의 로딩 인디케이터를 제공하는 컴포넌트입니다. 스피너, 바, 점 애니메이션 중 하나를 선택하여 사용할 수 있으며, 오버레이 옵션과 크기 조절이 가능합니다. 각 로딩 타입은 상황에 맞게 커스터마이징할 수 있습니다.
```

#### 12-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| size | 'small' \| 'medium' \| 'large' | - | 'medium' | 로딩 인디케이터 크기 |
| overlay | boolean | - | false | 오버레이 표시 여부 |
| spinner | boolean | - | false | 스피너 로딩 표시 여부 |
| bar | boolean | - | false | 바 로딩 표시 여부 |
| dots | boolean | - | false | 점 로딩 표시 여부 |

#### 12-3) 타입 정의
```typescript
type LoadingSize = 'small' | 'medium' | 'large';

interface LoadingProps {
  size?: LoadingSize;
  overlay?: boolean;
  spinner?: boolean;
  bar?: boolean;
  dots?: boolean;
}
```

#### 12-4) 사용 예시
```vue
<template>
  <div class="loading-examples">
    <!-- 스피너 로딩 -->
    <BaseLoading
      spinner
      size="medium"
    />

    <!-- 바 로딩 -->
    <BaseLoading
      bar
      size="large"
    />

    <!-- 점 로딩 -->
    <BaseLoading
      dots
      size="small"
    />

    <!-- 오버레이가 있는 로딩 -->
    <BaseLoading
      spinner
      overlay
      size="large"
    />
  </div>
</template>

<script setup lang="ts">
// 컴포넌트 import
</script>
```

### 13. BaseToast 컴포넌트
#### 13-1) 요약
```
BaseToast는 알림 메시지를 표시하는 컴포넌트입니다. 성공, 에러, 경고, 정보 등 다양한 타입의 토스트 메시지를 지원하며, 위치와 지속 시간을 설정할 수 있습니다. 아이콘, 메시지, 닫기 버튼을 커스터마이징할 수 있는 슬롯을 제공합니다.
```

#### 13-2) Props
| Prop 이름 | 타입 | 필수 | 기본값 | 설명 |
|-----------|------|------|--------|------|
| id | string \| number | ✓ | - | 토스트 고유 식별자 |
| message | string | ✓ | - | 토스트 메시지 |
| type | 'success' \| 'error' \| 'warning' \| 'info' | - | 'info' | 토스트 타입 |
| duration | number \| null | - | null | 표시 지속 시간 (ms) |
| position | 'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left' | - | 'top-right' | 표시 위치 |

#### 13-3) Slots
| Slot 이름 | Props | 설명 |
|-----------|-------|------|
| icon | - | 아이콘 영역 커스터마이징 |
| message | - | 메시지 영역 커스터마이징 |
| close-button | - | 닫기 버튼 영역 커스터마이징 |
| default | - | 전체 토스트 영역 커스터마이징 |

#### 13-4) Events
| 이벤트 이름 | 파라미터 | 설명 |
|------------|----------|------|
| close | (id: string \| number) | 토스트 닫힐 때 발생 |

#### 13-5) 타입 정의
```typescript
type ToastType = 'success' | 'error' | 'warning' | 'info';
type ToastPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';

interface ToastProps {
  id: string | number;
  message: string;
  type?: ToastType;
  duration?: number | null;
  position?: ToastPosition;
}

type ToastEmits = {
  (e: 'close', id: string | number): void;
}
```

#### 13-6) 사용 예시
```vue
<template>
  <div class="toast-examples">
    <!-- 기본 토스트 -->
    <BaseToast
      id="1"
      message="기본 알림 메시지입니다."
      type="info"
    />

    <!-- 성공 토스트 -->
    <BaseToast
      id="2"
      message="성공적으로 저장되었습니다."
      type="success"
      duration="3000"
    />

    <!-- 에러 토스트 -->
    <BaseToast
      id="3"
      message="오류가 발생했습니다."
      type="error"
      position="top-left"
    >
      <!-- 커스텀 아이콘 -->
      <template #icon>
        <span class="custom-icon">⚠️</span>
      </template>
    </BaseToast>

    <!-- 경고 토스트 -->
    <BaseToast
      id="4"
      message="주의하세요!"
      type="warning"
      position="bottom-right"
    >
      <!-- 커스텀 닫기 버튼 -->
      <template #close-button>
        <button class="custom-close" @click="handleClose">닫기</button>
      </template>
    </BaseToast>

    <!-- 완전 커스텀 토스트 -->
    <BaseToast
      id="5"
      message="커스텀 토스트"
      type="info"
      position="bottom-left"
    >
      <div class="custom-toast">
        <div class="custom-content">
          <span class="custom-icon">🎉</span>
          <span class="custom-message">커스텀 메시지</span>
        </div>
        <button class="custom-close" @click="handleCustomClose">×</button>
      </div>
    </BaseToast>
  </div>
</template>

<script setup lang="ts">
const handleClose = () => {
  console.log('토스트 닫기')
}

const handleCustomClose = () => {
  console.log('커스텀 토스트 닫기')
}
</script>
```

### 14. BaseModal 컴포넌트
#### 14-1) 요약
```
BaseModal은 드래그 가능한 모달 창으로, 커스터마이징 가능한 헤더/컨텐츠/푸터, 스크롤바 표시 옵션, 닫기 버튼 표시 옵션을 제공하며 TypeScript를 지원합니다.
```

#### 14-2) Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| draggable | boolean | true | 모달의 드래그 가능 여부 |
| header | ModalHeader | { showCloseButton: true } | 헤더 관련 설정 |
| content | ModalContent | { showScrollbar: true } | 컨텐츠 관련 설정 |
| footer | ModalFooter | { showFooter: false } | 푸터 관련 설정 |


#### 14-3) Slots
| 이름 | 설명 |
|------|------|
| default | 기본 컨텐츠 영역 |
| header | 커스텀 헤더 영역 (header.customHeader가 true일 때) |
| content | 커스텀 컨텐츠 영역 (content.customContent가 true일 때) |
| footer | 기본 푸터 영역 |
| custom-footer | 커스텀 푸터 영역 (footer.customFooter가 true일 때) |

#### 14-4) Events
| 이벤트 | 설명 |
|--------|------|
| close | 모달이 닫힐 때 발생하는 이벤트 |

#### 14-5) 타입 정의
```typescript
// ModalHeader 타입
interface ModalHeader {
  title?: string;           // 헤더 제목
  showCloseButton?: boolean; // 닫기 버튼 표시 여부
  customHeader?: boolean;    // 커스텀 헤더 사용 여부
}

// ModalContent 타입
interface ModalContent {
    customContent?: boolean;   // 커스텀 컨텐츠 사용 여부
    showScrollbar?: boolean;   // 스크롤바 표시 여부
}

// ModalFooter 타입
interface ModalFooter {
    showFooter?: boolean;      // 푸터 표시 여부
    customFooter?: boolean;    // 커스텀 푸터 사용 여부
}
```

#### 14-6) 사용 예시
##### (1) 기본 사용

```vue

<template>
  <BaseModal
      :header="{ title: '기본 모달' }"
      :content="{ showScrollbar: true }"
      @close="handleClose"
  >
    <p>모달 내용</p>
  </BaseModal>
</template>

<script setup lang="ts">
import {ref} from "vue";

const isModalOpen = ref(false);
const handleClose = () => {
  isModalOpen.value = false;
};
</script>
```

##### (2) 커스텀 헤더 사용
```vue
<template>
  <BaseModal
    :header="{ customHeader: true }"
    @close="handleClose"
  >
    <template #header>
      <div class="custom-header">
        <h2>커스텀 헤더</h2>
      </div>
    </template>
    
    <p>모달 내용</p>
  </BaseModal>
</template>

<script setup lang="ts">
import {ref} from "vue";

const isModalOpen = ref(false);
const handleClose = () => {
  isModalOpen.value = false;
};
</script>
```

##### (3) 커스텀 푸터 사용

```vue

<template>
  <BaseModal
      :header="{ title: '푸터 예시' }"
      :footer="{ showFooter: true, customFooter: true }"
      @close="handleClose"
  >
    <p>모달 내용</p>

    <template #custom-footer>
      <div class="custom-footer">
        <button @click="handleSave">저장</button>
        <button @click="handleClose">취소</button>
      </div>
    </template>
  </BaseModal>
</template>

<script setup lang="ts">
import {ref} from "vue";

const isModalOpen = ref(false);
const handleClose = () => {
  isModalOpen.value = false;
};

const handleSave = () => {
  // 저장 처리
};
</script>
```