# Component/Modal/BasicModal

> Props: component-modal-basicmodal.props.txt

## Examples


### Auto Focus Button

푸터 영역의 autoFocusButton 적용 기본 버튼을 변경할 수 있습니다.

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label',
    autoFocusButton: 'confirm'
  },
  parameters: {
    docs: {
      description: {
        story: '푸터 영역의 autoFocusButton 적용 기본 버튼을 변경할 수 있습니다.'
      }
    }
  }
}
```

### Auto Max Height

콘텐츠 영역이 길어질 경우 상하좌우 안전여백내에서 maxHeight값이 자동 적용됩니다.

```tsx
{
  parameters: {
    docs: {
      description: {
        story: '콘텐츠 영역이 길어질 경우 상하좌우 안전여백내에서 maxHeight값이 자동 적용됩니다.'
      }
    }
  },
  render: () => {
    const [show, setShow] = useState(false);
    const handleOpen = () => setShow(true);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={handleOpen}>open</Button>
        <Modal opened={show} onConfirm={handleClose} onCancel={handleClose} title='Title' confirmText='Confirm' cancelText='Cancel'>
          <Stack direction='column' gap={16} style={{
          height: '2000px'
        }}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Auto Max Height Long Content...</BaseText>
          </Stack>
        </Modal>
      </>;
  }
}
```

### Base

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  }
}
```

### Controlled Cancel Event

취소사유(cancelClick | escapeKeyPress | overlayClick) 별로 특정 로직을 넣을 수 있습니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal opened={show} title='Title' confirmText='Label' cancelText='Label' onCancel={(reason: ModalCancelReason) => {
        console.log('reason is', reason);
        switch (reason) {
          case 'cancelClick':
            handleClose();
            break;
          case 'escapeKeyPress':
            // custom logic...
            handleClose();
            break;
          case 'overlayClick':
            // custom logic...
            handleClose();
        }
      }} onConfirm={handleClose}>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '취소사유(cancelClick | escapeKeyPress | overlayClick) 별로 특정 로직을 넣을 수 있습니다.'
      }
    }
  }
}
```

### Custom Content

콘텐츠 영역을 커스텀할 수 있습니다. 기본 콘텐츠 영역은 간격을 가지고 있기 때문에 레이아웃 스타일을 커스텀할 경우 spaceProps를 이용, contentProps로 전달시 해당 스타일을 우선순위로 가집니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal opened={show} onConfirm={handleClose} onCancel={handleClose} title='Title' confirmText='Label' cancelText='Label' contentProps={{
        p: 0
      }}>
          <Stack direction='column' style={{
          backgroundColor: colors.gray10,
          height: '200px',
          justifyContent: 'center'
        }} align='center'>
            <BaseText kind='Body_13_Regular'>Custom Modal Content - SpaceProps</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '콘텐츠 영역을 커스텀할 수 있습니다. 기본 콘텐츠 영역은 간격을 가지고 있기 때문에 레이아웃 스타일을 커스텀할 경우 spaceProps를 이용, contentProps로 전달시 해당 스타일을 우선순위로 가집니다.'
      }
    }
  }
}
```

### Custom Footer

푸터 영역을 커스텀할 수 있습니다. 커스텀 사용시 dense 속성은 적용되지 않습니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal opened={show} title='Title' confirmText='Label' cancelText='Label' onCancel={handleClose} footer={<div>
              <Divider />
              <Stack direction='row' align='center' p={24}>
                <Badge color='pink' size='large'>
                  Footer Addon 1
                </Badge>
                <Stack direction='row' ml='auto'>
                  <Button size='large' kind='primary' onClick={handleClose}>
                    Custom Confirm Text
                  </Button>
                </Stack>
              </Stack>
            </div>}>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '푸터 영역을 커스텀할 수 있습니다. 커스텀 사용시 dense 속성은 적용되지 않습니다.'
      }
    }
  }
}
```

### Custom Footer Full Screen

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal opened={show} title='Title' confirmText='Label' cancelText='Label' fullScreen onCancel={handleClose} footer={<div>
              <Divider />
              <Stack direction='row' align='center' p={24}>
                <Badge color='pink' size='large'>
                  Footer Addon 1
                </Badge>
                <Stack direction='row' ml='auto'>
                  <Button size='large' kind='primary' onClick={handleClose}>
                    Confirm
                  </Button>
                </Stack>
              </Stack>
            </div>}>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Custom Footer + FullScreen</BaseText>
            <div style={{
            margin: '20px 0 0',
            height: '1200px',
            backgroundColor: colors.gray10
          }}>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Long Long Content
              </BaseText>
            </div>
          </Stack>
        </Modal>
      </>;
  }
}
```

### Custom Header

헤더 영역을 커스텀할 수 있습니다. 커스텀 사용시 기본 Header(title, backButton, closeButton)는 미노출됩니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal opened={show} confirmText='Label' cancelText='Label' onCancel={handleClose} onConfirm={handleClose} header={<div>
              <Stack p={24} justify='space-between'>
                <BaseText kind='Heading_17_Bold' color={semantic_colors.content.primary}>
                  Custom Header Title
                </BaseText>
                <Stack gap={8} align='center'>
                  <Badge color='blue' size='large'>
                    Status
                  </Badge>
                  <IconX size={20} style={{
              cursor: 'pointer'
            }} onClick={handleClose} />
                </Stack>
              </Stack>
              <Divider />
            </div>}>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '헤더 영역을 커스텀할 수 있습니다. 커스텀 사용시 기본 Header(title, backButton, closeButton)는 미노출됩니다.'
      }
    }
  }
}
```

### Custom Max Height

콘텐츠 영역 maxHeight을 커스텀할 수 있습니다. 지정하지 않을 경우 안전여백내에서 계산된 maxHeight값이 적용됩니다.

```tsx
{
  parameters: {
    docs: {
      description: {
        story: '콘텐츠 영역 maxHeight을 커스텀할 수 있습니다. 지정하지 않을 경우 안전여백내에서 계산된 maxHeight값이 적용됩니다.'
      }
    }
  },
  render: () => {
    const [show, setShow] = useState(false);
    const handleOpen = () => setShow(true);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={handleOpen}>open</Button>
        <Modal opened={show} onConfirm={handleClose} onCancel={handleClose} title='Title' confirmText='Confirm' cancelText='Cancel' contentProps={{
        maxHeight: 500
      }}>
          <Stack direction='column' gap={16} style={{
          height: '2000px'
        }}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>
              Custom Max Height - 500
              <br />
              Long Content...
            </BaseText>
          </Stack>
        </Modal>
      </>;
  }
}
```

### Dense

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label',
    dense: true,
    width: 340
  }
}
```

### Disabled Overlay Escape

오버레이 클릭, esc 키이벤트로 인한 cancel 처리를 중지할 수 있습니다.

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label',
    canClickOutside: false,
    canCloseEscapeKey: false
  },
  parameters: {
    docs: {
      description: {
        story: '오버레이 클릭, esc 키이벤트로 인한 cancel 처리를 중지할 수 있습니다.'
      }
    }
  }
}
```

### Fill

안전여백내에서 너비 100% 적용됩니다.

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label',
    fill: true
  },
  parameters: {
    docs: {
      description: {
        story: '안전여백내에서 너비 100% 적용됩니다.'
      }
    }
  }
}
```

### Fill Button

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    confirmButtonProps: {
      fill: true
    },
    confirmText: 'Label',
    cancelText: 'Label',
    cancelButtonProps: {
      fill: true
    }
  }
}
```

### Fit Content

contentProps를 display: flex로 지정하면 하위 내용 컴포넌트를 모달 높이에 맞출 수 있습니다.

```tsx
{
  parameters: {
    docs: {
      description: {
        story: 'contentProps를 display: flex로 지정하면 하위 내용 컴포넌트를 모달 높이에 맞출 수 있습니다.'
      }
    }
  },
  render: () => {
    const [show, setShow] = useState(false);
    const handleOpen = () => setShow(true);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={handleOpen}>open</Button>
        <Modal opened={show} onConfirm={handleClose} onCancel={handleClose} title='Title' confirmText='Confirm' cancelText='Cancel' contentProps={{
        scrollable: true,
        style: {
          display: 'flex',
          flexDirection: 'column'
        }
      }}>
          <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
          <div style={{
          flex: '0 1 auto',
          background: '#faa',
          overflow: 'auto'
        }}>
            <div style={{
            height: '5000px'
          }}>Content</div>
          </div>
        </Modal>
      </>;
  }
}
```

### Footer Extra

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    footerExtra: <Stack width='100%' gap={4} align='center'>
        <IconCircleWarning color={semantic_colors.state.negative} />
        <BaseText kind='Body_13_Medium' color={semantic_colors.state.negative}>
          조회 결과 내역이 모두 제외 처리됩니다. 제외하지 않을 대상은 ‘목록에서 빼기’를 이용해주세요.
        </BaseText>
      </Stack>,
    width: 900,
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  }
}
```

### Footer Extra Full Screen

전체화면을 사용할 수 있습니다. 헤더/푸터 영역이 자동 고정됩니다.

```tsx
{
  render: args => {
    const [show, setShow] = useState(false);
    const [selectValue, setSelectValue] = useState<string | number>();
    const options = [{
      label: 'option 1',
      value: 'value-1'
    }, {
      label: 'option 2',
      value: 'value-2'
    }, {
      label: 'option 3',
      value: 'value-3'
    }, {
      label: 'option 4',
      value: 'value-4'
    }, {
      label: 'option 5',
      value: 'value-5'
    }];
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal title='Title' dense fullScreen confirmText='Label' confirmButtonProps={{
        fill: true
      }} opened={show} onConfirm={() => setShow(false)} onCancel={() => setShow(false)} autoFocusButton='confirm' {...args}>
          <>
            <Stack direction='column' gap={8} mb={24}>
              <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                Text Example1
              </BaseText>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Description Text Example1
              </BaseText>
            </Stack>
            <Input size='large' placeholder='placeholder' />
            <Divider spacing={40} />
            <Stack direction='column' gap={8} mb={24}>
              <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                Text Example2
              </BaseText>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Description Text Example2
              </BaseText>
            </Stack>
            <Stack direction='column' gap={16}>
              <FormField label='label'>
                <Input size='large' placeholder='placeholder' />
              </FormField>
              <FormField label='label'>
                <Dropdown size='large' placeholder='placeholder' value={selectValue} options={options} onChange={setSelectValue} />
              </FormField>
            </Stack>
            <div style={{
            margin: '20px 0 0',
            height: '1200px',
            backgroundColor: colors.gray10
          }}>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Long Long Content
              </BaseText>
            </div>
          </>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '전체화면을 사용할 수 있습니다. 헤더/푸터 영역이 자동 고정됩니다.'
      }
    }
  },
  args: {
    footerExtra: <Stack width='100%' gap={4} align='center'>
        <IconCircleWarning color={semantic_colors.state.negative} />
        <BaseText kind='Body_13_Medium' color={semantic_colors.state.negative}>
          조회 결과 내역이 모두 제외 처리됩니다. 제외하지 않을 대상은 ‘목록에서 빼기’를 이용해주세요.
        </BaseText>
      </Stack>
  }
}
```

### Full Screen

전체화면을 사용할 수 있습니다. 헤더/푸터 영역이 자동 고정됩니다.

```tsx
{
  render: args => {
    const [show, setShow] = useState(false);
    const [selectValue, setSelectValue] = useState<string | number>();
    const options = [{
      label: 'option 1',
      value: 'value-1'
    }, {
      label: 'option 2',
      value: 'value-2'
    }, {
      label: 'option 3',
      value: 'value-3'
    }, {
      label: 'option 4',
      value: 'value-4'
    }, {
      label: 'option 5',
      value: 'value-5'
    }];
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal title='Title' dense fullScreen confirmText='Label' confirmButtonProps={{
        fill: true
      }} opened={show} onConfirm={() => setShow(false)} onCancel={() => setShow(false)} autoFocusButton='confirm' {...args}>
          <>
            <Stack direction='column' gap={8} mb={24}>
              <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                Text Example1
              </BaseText>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Description Text Example1
              </BaseText>
            </Stack>
            <Input size='large' placeholder='placeholder' />
            <Divider spacing={40} />
            <Stack direction='column' gap={8} mb={24}>
              <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                Text Example2
              </BaseText>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Description Text Example2
              </BaseText>
            </Stack>
            <Stack direction='column' gap={16}>
              <FormField label='label'>
                <Input size='large' placeholder='placeholder' />
              </FormField>
              <FormField label='label'>
                <Dropdown size='large' placeholder='placeholder' value={selectValue} options={options} onChange={setSelectValue} />
              </FormField>
            </Stack>
            <div style={{
            margin: '20px 0 0',
            height: '1200px',
            backgroundColor: colors.gray10
          }}>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Long Long Content
              </BaseText>
            </div>
          </>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '전체화면을 사용할 수 있습니다. 헤더/푸터 영역이 자동 고정됩니다.'
      }
    }
  }
}
```

### Nesting Child Modal

자식 요소로써 중첩 모달을 호출 할 수 있습니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const [showSecond, setShowSecond] = useState(false);
    const handleOpenFirstModal = () => setShow(true);
    const handleCloseFirsModal = () => setShow(false);
    const handleOpenSecondModal = () => setShowSecond(true);
    const handleCloseSecondModal = () => setShowSecond(false);
    return <>
        <Button onClick={handleOpenFirstModal}>open</Button>
        <Modal opened={show} onConfirm={handleOpenSecondModal} onCancel={handleCloseFirsModal} title='Title' confirmText='Open Second Modal' cancelText='Cancel'>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
            <Modal width={300} opened={showSecond} onConfirm={handleCloseSecondModal} onCancel={handleCloseSecondModal} title='Second Modal Title' confirmText='Confirm' cancelText='Cancel'>
              <Stack direction='column' gap={16}>
                <BaseText kind='Heading_20_SemiBold'>Second Modal Heading</BaseText>
                <BaseText kind='Body_13_Regular'>Second Modal Body</BaseText>
              </Stack>
            </Modal>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '자식 요소로써 중첩 모달을 호출 할 수 있습니다.'
      }
    }
  }
}
```

### Nesting Confirm

다른 타입의 모달을 중첩으로 호출할 수 있습니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const handleOpen = () => setShow(true);
    const handleClose = () => setShow(false);
    const openConfirm = () => {
      return Confirm({
        title: 'Confirm Title',
        confirmText: 'Confirm',
        cancelText: 'Cancel',
        onConfirm: () => {
          console.log('onConfirm');
          handleClose();
        },
        onCancel: () => {
          console.log('onCancel');
        },
        onExited: () => {
          console.log('onExited');
        }
      });
    };
    return <>
        <Button onClick={handleOpen}>open</Button>
        <Modal opened={show} onConfirm={handleClose} onCancel={openConfirm} title='Title' confirmText='Confirm' cancelText='Open Confirm Modal'>
          <Stack direction='column' gap={16}>
            <Button kind='outlined_negative' onClick={() => {
            Alert({
              kind: 'info',
              title: 'Heading',
              text: 'Body',
              confirmText: 'Label',
              onConfirm: () => {
                console.log('onConfirm');
              },
              onExited: () => {
                console.log('onExited');
              }
            });
          }}>
              show Alert
            </Button>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '다른 타입의 모달을 중첩으로 호출할 수 있습니다.'
      }
    }
  }
}
```

### Nesting Floating Elements

중첩된 플로팅 요소 테스트입니다.

```tsx
{
  render: () => {
    const [modalShow, setModalShow] = useState(false);
    const [bottomSheetShow, setBottomSheetShow] = useState(false);
    const [nestedModalShow, setNestedModalShow] = useState(false);
    const [nestedChildModalShow, setNestedChildModalShow] = useState(false);
    const [selectValue, setSelectValue] = useState<string | number>();
    const [dates, setDates] = useState<Dates>();
    const options = [{
      label: 'option 1',
      value: 'value-1'
    }, {
      label: 'option 2',
      value: 'value-2'
    }, {
      label: 'option 3',
      value: 'value-3'
    }, {
      label: 'option 4',
      value: 'value-4'
    }, {
      label: 'option 5',
      value: 'value-5'
    }];
    return <>
        <Button onClick={() => setModalShow(true)}>open</Button>
        <Modal title='Title' dense confirmText='Label' confirmButtonProps={{
        fill: true
      }} opened={modalShow} onConfirm={() => setModalShow(false)} onCancel={() => setModalShow(false)} autoFocusButton='confirm'>
          <>
            <Stack direction='column' gap={8} mb={24}>
              <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                Text Example1
              </BaseText>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Description Text Example1
              </BaseText>
            </Stack>
            <Input size='large' placeholder='placeholder' />
            <Divider spacing={40} />
            <Stack direction='column' gap={8} mb={24}>
              <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                Text Example2
              </BaseText>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Description Text Example2
              </BaseText>
            </Stack>
            <Stack direction='column' gap={16}>
              <FormField label='label'>
                <Input size='large' placeholder='placeholder' />
              </FormField>
              <FormField label='label'>
                <Dropdown size='large' placeholder='placeholder' value={selectValue} options={options} onChange={setSelectValue} />
              </FormField>
              <Button size='large' kind='black' onClick={() => setBottomSheetShow(true)}>
                Open BottomSheet
              </Button>
              <Button size='large' kind='outlined_black' onClick={() => setNestedModalShow(true)}>
                Open Nested Modal
              </Button>
              <Button size='large' kind='outlined_black' onClick={() => setNestedChildModalShow(true)}>
                Open Nested Child Modal
              </Button>
              <Modal title='Nested Child Modal' dense confirmText='Label' confirmButtonProps={{
              fill: true
            }} opened={nestedChildModalShow} onConfirm={() => setNestedChildModalShow(false)} onCancel={() => setNestedChildModalShow(false)} autoFocusButton='confirm'>
                <Stack direction='column' gap={8} mb={24}>
                  <BaseText kind='Heading_18_Bold' color={semantic_colors.content.primary}>
                    Text Example1
                  </BaseText>
                  <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                    Description Text Example1
                  </BaseText>
                  <DateRangePickerOld dates={dates} onChange={(from?: Date, end?: Date) => setDates({
                  from,
                  to: end
                })} showTimePicker showRemoveButton />
                </Stack>
              </Modal>
            </Stack>
            <div style={{
            margin: '20px 0 0',
            height: '1200px',
            backgroundColor: colors.gray10
          }}>
              <BaseText kind='Body_13_Regular' color={semantic_colors.content.secondary}>
                Long Long Content
              </BaseText>
            </div>
          </>
        </Modal>
        <BottomSheet opened={bottomSheetShow} title='Heading' subTitle='SubHeading' onClose={() => setBottomSheetShow(false)}>
          <>
            <Stack direction='row' mt={16} mb={16}>
              <BaseText kind='Body_12_Regular' color={semantic_colors.content.secondary}>
                Body Content
              </BaseText>
            </Stack>
            <Stack direction='column' gap={40}>
              <Input size='large' placeholder='placeholder' />
              <Button kind='primary' size='large'>
                Label
              </Button>
            </Stack>
            <Stack direction='column' mt={16} p={20} height={1500} style={{
            backgroundColor: colors.gray50
          }}>
              long content...
            </Stack>
          </>
        </BottomSheet>
        <Modal title='Nested Sibling Modal' dense confirmText='Label' confirmButtonProps={{
        fill: true
      }} opened={nestedModalShow} onConfirm={() =
// ... (truncated)
```

### Nesting Mixed

다른 타입의 모달을 중첩으로 호출할 수 있습니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const order = useRef(0);
    const handleOpen = () => setShow(true);
    const handleClose = () => setShow(false);
    const openConfirm = (times: number = 1) => {
      const id = order.current++;
      return Confirm({
        title: `Confirm Title (ID: ${id})`,
        text: new Array(times).fill(0).map((_, index) => index).join('\n'),
        confirmText: 'Confirm',
        cancelText: 'Cancel',
        onConfirm: () => {
          console.log('Confirm onConfirm', id);
        },
        onCancel: () => {
          console.log('Confirm onCancel', id);
        },
        onExited: () => {
          console.log('Confirm onExited', id);
        }
      });
    };
    const openAlert = (times: number = 1) => {
      const id = order.current++;
      return Alert({
        title: `Alert Title (ID: ${id})`,
        text: new Array(times).fill(0).map((_, index) => index).join('\n'),
        confirmText: 'Close',
        onConfirm: () => {
          console.log('Alert onConfirm', id);
        },
        onExited: () => {
          console.log('Alert onExited', id);
        }
      });
    };
    return <>
        <Button onClick={async () => {
        openConfirm(10);
        setTimeout(() => {
          openAlert(8);
        }, 100);
        setTimeout(() => {
          openConfirm(6);
        }, 200);
        setTimeout(() => {
          openAlert(4);
        }, 300);
        setTimeout(() => {
          handleOpen();
        }, 400);
      }}>
          open
        </Button>
        <Modal opened={show} onConfirm={() => {
        console.log('Modal onConfirm');
        handleClose();
      }} onCancel={() => {
        console.log('Modal onCancel');
        handleClose();
      }} onExited={() => {
        console.log('Modal onExited');
      }} title='Title' confirmText='Confirm' cancelText='Cancel'>
          <Stack direction='column' gap={16}>
            <Button kind='outlined_negative' onClick={() => {
            Alert({
              kind: 'warning',
              title: 'Heading',
              text: 'Body',
              confirmText: 'Label',
              onConfirm: () => {
                console.log('Alert onConfirm');
              },
              onExited: () => {
                console.log('Alert onExited');
              }
            });
          }}>
              show Alert
            </Button>
            <Button kind='outlined_primary' onClick={() => {
            Confirm({
              kind: 'success',
              title: 'Heading',
              text: 'Body',
              confirmText: 'Label',
              onConfirm: () => {
                console.log('Confirm onConfirm');
              },
              onExited: () => {
                console.log('Confirm onExited');
              }
            });
          }}>
              show Confirm
            </Button>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '다른 타입의 모달을 중첩으로 호출할 수 있습니다.'
      }
    }
  }
}
```

### Nesting Modal

중첩 모달을 호출 할 수 있습니다.

```tsx
{
  render: () => {
    const [show, setShow] = useState(false);
    const [showSecond, setShowSecond] = useState(false);
    const handleOpenFirstModal = () => setShow(true);
    const handleCloseFirsModal = () => setShow(false);
    const handleOpenSecondModal = () => setShowSecond(true);
    const handleCloseSecondModal = () => setShowSecond(false);
    return <>
        <Button onClick={handleOpenFirstModal}>open</Button>
        <Modal opened={show} onConfirm={handleOpenSecondModal} onCancel={handleCloseFirsModal} title='Title' confirmText='Open Second Modal' cancelText='Cancel'>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
        <Modal width={300} opened={showSecond} onConfirm={handleCloseSecondModal} onCancel={handleCloseSecondModal} title='Second Modal Title' confirmText='Confirm' cancelText='Cancel'>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Second Modal Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Second Modal Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: '중첩 모달을 호출 할 수 있습니다.'
      }
    }
  }
}
```

### Scrollable

콘텐츠 영역 maxHeight 지정하지 않거나 비동기적인 가변 높이를 가질 경우 contentProps scrollable 속성을 통해 overflow auto 여부를 지정할 수 있습니다.

```tsx
{
  parameters: {
    docs: {
      description: {
        story: '콘텐츠 영역 maxHeight 지정하지 않거나 비동기적인 가변 높이를 가질 경우 contentProps scrollable 속성을 통해 overflow auto 여부를 지정할 수 있습니다.'
      }
    }
  },
  render: () => {
    const [show, setShow] = useState(false);
    const handleOpen = () => setShow(true);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={handleOpen}>open</Button>
        <Modal opened={show} onConfirm={handleClose} onCancel={handleClose} title='Title' confirmText='Confirm' cancelText='Cancel' contentProps={{
        scrollable: true
      }}>
          <Stack direction='column' gap={16} style={{
          height: '2000px'
        }}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>
              contentProps.scrollable = true
              <br />
              content child 영역에 overflow auto 여부를 지정할 수 있습니다.
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
              Long Content...
              <br />
            </BaseText>
          </Stack>
        </Modal>
      </>;
  }
}
```

### With Back Button

```tsx
{
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label'
  },
  render: args => {
    const [show, setShow] = useState(false);
    const spacing_value = args.dense ? 8 : 16;
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...args} opened={show} onCancel={(reason: ModalCancelReason) => {
        console.log('cancel reason is', reason);
        handleClose();
      }} onConfirm={handleClose} onExited={() => console.log('onExited')}>
          <Stack direction='column' gap={spacing_value}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Body</BaseText>
          </Stack>
        </Modal>
      </>;
  },
  args: {
    title: 'Title',
    confirmText: 'Label',
    cancelText: 'Label',
    backButton: true,
    onClickBackButton: () => alert('onClickBackButton')
  }
}
```

### Without Footer

confirmText, cancelText, footer를 모두 전달하지 않은 경우 콘텐츠 영역 하단 border-radius가 적용되는 케이스입니다.

```tsx
{
  args: {
    title: 'Title',
    confirmText: '',
    cancelText: '',
    contentProps: {
      p: 0
    }
  },
  render: props => {
    const [show, setShow] = useState(false);
    const handleClose = () => setShow(false);
    return <>
        <Button onClick={() => setShow(true)}>open</Button>
        <Modal {...props} opened={show} onCancel={handleClose}>
          <Stack direction='column' gap={16}>
            <BaseText kind='Heading_20_SemiBold'>Heading</BaseText>
            <BaseText kind='Body_13_Regular'>Footer가 없는 BasicModal + Long Content 케이스</BaseText>
            <div style={{
            height: '1200px',
            backgroundColor: colors.zigzag_pink100,
            border: '2px solid red'
          }}>
              <BaseText kind='Body_13_Regular'>
                Long Content
                <br />
                Long Content
                <br />
                Long Content
                <br />
                Long Content
                <br />
                Long Content
                <br />
                Long Content
                <br />
                Long Content
                <br />
                Long Content
                <br />
                Long Content
              </BaseText>
            </div>
          </Stack>
        </Modal>
      </>;
  },
  parameters: {
    docs: {
      description: {
        story: 'confirmText, cancelText, footer를 모두 전달하지 않은 경우 콘텐츠 영역 하단 border-radius가 적용되는 케이스입니다.'
      }
    }
  }
}
```