@bestow-ui/react 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,35 +3,36 @@
3
3
  [![npm](https://img.shields.io/npm/v/@bestow-ui/react)](https://www.npmjs.com/package/@bestow-ui/react)
4
4
  [![license](https://img.shields.io/npm/l/@bestow-ui/react)](https://github.com/Dev-BestOW/BestOW-UI/blob/main/LICENSE)
5
5
 
6
- **A React design system for building Korean services — with accessibility you can verify.**
7
- Built on [vanilla-extract](https://vanilla-extract.style) and [Radix UI](https://www.radix-ui.com).
6
+ **한국 서비스를 만들 때 바로 쓰는, 접근성이 검증된 React 디자인 시스템이에요.**
7
+ [vanilla-extract](https://vanilla-extract.style) 와 [Radix UI](https://www.radix-ui.com) 위에 만들었어요.
8
8
 
9
- - **Korean service kit** — terms agreement (agree-to-all), SMS verification code (one-time-code autofill, countdown), phone / business registration
10
- number (checksum) / amount (Korean reading like “12만 5,000원”), address (Kakao postcode), and a date picker (Sunday-first, holidays).
11
- - **Accessibility, tested** — every story is rendered in a real browser and checked with axe in both light and dark themes on CI.
12
- Item-by-item notes for KWCAG 2.2 (the Korean web accessibility standard), plus a log of real issues the checks caught.
13
- - **One brand color → accessible theme** — brand colors keep 4.5:1 text contrast (including hover) and a 3:1 focus ring, light and dark.
14
- Try the [Theme Builder](https://dev-bestow.github.io/BestOW-UI/?path=/story/foundations-theme-builder--builder).
15
- - **Zero-runtime CSS & RSC-friendly** — works with the Next.js App Router; tree-shakable (a single `Button` is about 1.5 kB, minified + brotli).
16
- - **Korean by default, any language** — default UI strings are Korean; switch them all with `LocaleProvider` (English included).
9
+ > A React design system for Korean services, with accessibility verified in CI. Docs are in Korean.
17
10
 
18
- 📖 **Docs & live examples:** [Storybook](https://dev-bestow.github.io/BestOW-UI/) · [Next.js example app](https://dev-bestow.github.io/BestOW-UI/playground/)
11
+ - **한국 서비스 키트** — 약관 동의(전체 동의), 인증번호(문자 자동 채우기·남은 시간), 전화번호·사업자등록번호(검증)·
12
+ 금액("12만 5,000원" 읽기)·주소(카카오 우편번호)·날짜(일요일 시작·공휴일)·기간(빠른 선택) 입력.
13
+ - **검사로 증명하는 접근성** — 모든 스토리를 실제 브라우저(Chromium·WebKit·Firefox)에서 라이트·다크 두 테마로 axe 검사해요.
14
+ 고대비 모드·320px 리플로·기본 글꼴 2배도 CI 로 확인하고, [KWCAG 2.2 항목별 대응 근거](https://dev-bestow.github.io/BestOW-UI/?path=/docs/accessibility-kwcag-2-2-%EB%8C%80%EC%9D%91--docs)를 문서로 정리했어요.
15
+ - **대표색 하나로 테마** — 브랜드 색을 넣어도 글자 대비 4.5:1(hover 포함)과 포커스 링 3:1 을 라이트·다크 모두 지켜요.
16
+ [테마 빌더](https://dev-bestow.github.io/BestOW-UI/?path=/story/foundations-theme-builder--builder)에서 바로 해 볼 수 있어요.
17
+ - **가볍고 서버 컴포넌트 친화적** — 런타임 CSS 0, Next.js App Router 지원, 쓰는 컴포넌트만 번들에 들어가요
18
+ (Button 하나 약 1.5 kB, 모든 컴포넌트 CSS 를 합쳐도 약 9 kB — minify + brotli).
19
+ - **기본 문구는 한국어** — 닫기·결과 없음·달력 등 기본 문구가 한국어이고, `LocaleProvider` 로 한 번에 바꿀 수 있어요(영어 묶음 포함).
19
20
 
20
- > 🇰🇷 한국어 안내는 [아래](#한국어)에 있어요.
21
+ 📖 **문서와 실제 예제:** [Storybook](https://dev-bestow.github.io/BestOW-UI/) · [Next.js 예제 앱](https://dev-bestow.github.io/BestOW-UI/playground/)
21
22
 
22
- ## Install
23
+ ## 설치
23
24
 
24
25
  ```bash
25
26
  npm install @bestow-ui/react
26
27
  # pnpm add @bestow-ui/react / yarn add @bestow-ui/react
27
28
  ```
28
29
 
29
- Requires React 18 or later. [`@bestow-ui/icons`](https://www.npmjs.com/package/@bestow-ui/icons) and [`@bestow-ui/tokens`](https://www.npmjs.com/package/@bestow-ui/tokens) are installed as dependencies.
30
+ React 18 이상이 필요해요. [`@bestow-ui/icons`](https://www.npmjs.com/package/@bestow-ui/icons) 와 [`@bestow-ui/tokens`](https://www.npmjs.com/package/@bestow-ui/tokens) 는 함께 설치돼요.
30
31
 
31
- ## Quick start
32
+ ## 시작하기
32
33
 
33
34
  ```tsx
34
- import '@bestow-ui/react/styles.css'; // once, at your app entry
35
+ import '@bestow-ui/react/styles.css'; // 앱 진입점에서 한 번
35
36
  import { ThemeProvider, ToastProvider, Button, useToast } from '@bestow-ui/react';
36
37
 
37
38
  export function App() {
@@ -46,15 +47,18 @@ export function App() {
46
47
 
47
48
  function SaveButton() {
48
49
  const { toast } = useToast();
49
- return <Button onClick={() => toast({ title: 'Saved', color: 'success' })}>Save</Button>;
50
+ return <Button onClick={() => toast({ title: '저장했어요', color: 'success' })}>저장</Button>;
50
51
  }
51
52
  ```
52
53
 
53
- With the Next.js App Router, put the providers in a `'use client'` file and wrap them in `app/layout.tsx`. Components can be rendered from Server Components — display components without state (`Typography`, `Stack`, `Card`, `Badge`, `Divider`, `Progress`, `Skeleton`) render as Server Components themselves (no client JS) — and plain functions and values (`createTheme`, `createIntentColors`, `vars`, `formatKoreanAmount`, …) can be called there directly. See the [Introduction](https://dev-bestow.github.io/BestOW-UI/) page for the full setup.
54
+ Next.js App Router 에서는 Provider 를 `'use client'` 파일로 분리해 `app/layout.tsx` 에서 감싸면 돼요. 컴포넌트는 서버 컴포넌트에서도 바로 쓸 수 있어요.
55
+ 상태가 없는 표시용 컴포넌트(`Typography`, `Stack`, `Card`, `Badge`, `Divider`, `Progress`, `Skeleton`, `DescriptionList`, `VisuallyHidden`)는
56
+ 서버 컴포넌트로 렌더링되고(클라이언트 JS 없음), `createTheme`·`createIntentColors`·`vars`·`formatKoreanAmount` 같은 함수·값도 서버에서 그대로 불러요.
57
+ 자세한 설정은 [Storybook 시작하기](https://dev-bestow.github.io/BestOW-UI/)에 있어요.
54
58
 
55
- Upgrading from 0.x? See the [migration guide](https://github.com/Dev-BestOW/BestOW-UI/blob/main/docs/MIGRATION-1.0.md).
59
+ 0.x 에서 올리시나요? [마이그레이션 가이드](https://github.com/Dev-BestOW/BestOW-UI/blob/main/docs/MIGRATION-1.0.md)를 보세요.
56
60
 
57
- ## Dark mode
61
+ ## 다크 모드
58
62
 
59
63
  ```tsx
60
64
  const [mode, setMode] = useState<ThemeName>('light');
@@ -62,22 +66,28 @@ const [mode, setMode] = useState<ThemeName>('light');
62
66
  <ThemeProvider theme={mode}>…</ThemeProvider>;
63
67
  ```
64
68
 
65
- Portal content (Modal, Tooltip, Select, Toast, …) follows the theme automatically.
69
+ Modal·Tooltip·Select·Toast 처럼 portal 로 띄우는 요소도 테마를 자동으로 따라가요.
66
70
 
67
- ## Brand themes
71
+ ## 브랜드 테마
68
72
 
69
73
  ```tsx
70
74
  import { ThemeProvider, createTheme, createIntentColors } from '@bestow-ui/react';
71
75
 
72
76
  const brand = createTheme(
73
- createIntentColors('primary', '#7c3aed'), // light + dark color sets, WCAG 4.5:1 contrast guaranteed
77
+ createIntentColors('primary', '#7c3aed'), // 라이트·다크 색 세트, 글자 대비 4.5:1 보장
74
78
  { common: { radius: { md: '6px' } } },
75
79
  );
76
80
 
77
81
  <ThemeProvider theme={mode} overrides={brand}>…</ThemeProvider>;
78
82
  ```
79
83
 
80
- All tokens are plain CSS variables with stable names, so you can also override them in CSS:
84
+ 입력 칸 테두리를 배경과 3:1 이상으로 진하게 하려면 `strongInputBorders` 를 더해요(2.0 에서 기본값이 될 예정).
85
+
86
+ ```tsx
87
+ createTheme(createIntentColors('primary', '#7c3aed'), strongInputBorders);
88
+ ```
89
+
90
+ 모든 토큰은 이름이 고정된 CSS 변수라 CSS 로도 덮어쓸 수 있어요.
81
91
 
82
92
  ```css
83
93
  :root {
@@ -85,9 +95,9 @@ All tokens are plain CSS variables with stable names, so you can also override t
85
95
  }
86
96
  ```
87
97
 
88
- ## Locale
98
+ ## 문구 바꾸기
89
99
 
90
- Default UI strings (close buttons, "no results", calendar labels, …) are Korean. Switch them all with `LocaleProvider`:
100
+ 기본 문구(닫기 버튼, "결과가 없어요", 달력 이름 등)는 한국어예요. `LocaleProvider` 로 한 번에 바꿔요.
91
101
 
92
102
  ```tsx
93
103
  import { LocaleProvider, en } from '@bestow-ui/react';
@@ -95,56 +105,42 @@ import { LocaleProvider, en } from '@bestow-ui/react';
95
105
  <LocaleProvider messages={en}>…</LocaleProvider>
96
106
  ```
97
107
 
98
- ## Components
108
+ ## 폼 라이브러리
99
109
 
100
- | Category | Components |
101
- |---|---|
102
- | Layout | `Stack`, `Divider`, `Card` |
103
- | Typography | `Typography` |
104
- | Actions | `Button`, `IconButton`, `Link` |
105
- | Forms | `Input`, `TextField`, `Textarea`, `Select`, `Combobox`, `NumberInput`, `Checkbox`, `RadioGroup`, `Switch`, `Slider` |
106
- | Feedback | `Alert`, `Progress`, `ToastProvider` + `useToast`, `Spinner`, `Skeleton`, `Badge` |
107
- | Overlays | `Modal`, `Drawer`, `Popover`, `Tooltip`, `DropdownMenu` |
108
- | Navigation | `Tabs`, `SegmentedControl`, `Accordion`, `Pagination`, `Breadcrumb`, `SkipLink` |
109
- | Display | `Avatar`, `Table` |
110
- | Korean kit | `AgreementGroup` (약관 동의), `VerificationCodeInput` (인증번호), `PhoneNumberInput`, `BusinessNumberInput`, `AmountInput`, `AddressInput`, `DatePicker`, `Calendar` (+ `formatPhoneNumber`, `formatBusinessNumber`, `isValidBusinessNumber`, `formatKoreanAmount`, `formatKoreanDate`, `parseISODate`, `toISODate`) |
111
- | Locale | `LocaleProvider`, `en` (English messages) |
112
- | Theming | `ThemeProvider`, `createTheme`, `createIntentColors`, `vars`, `useTheme`, `usePortalProps` |
113
-
114
- ## Links
115
-
116
- - [Storybook](https://dev-bestow.github.io/BestOW-UI/) — docs, design tokens, all components
117
- - [GitHub](https://github.com/Dev-BestOW/BestOW-UI) — source, [issues](https://github.com/Dev-BestOW/BestOW-UI/issues), [contributing guide](https://github.com/Dev-BestOW/BestOW-UI/blob/main/CONTRIBUTING.md)
118
- - [Changelog](https://github.com/Dev-BestOW/BestOW-UI/blob/main/packages/react/CHANGELOG.md) · [Upgrading from 0.x (MIGRATION-1.0, Korean)](https://github.com/Dev-BestOW/BestOW-UI/blob/main/docs/MIGRATION-1.0.md)
119
-
120
- ## License
110
+ react-hook-form 과 함께 쓸 수 있어요. 입력창은 `register` 를 그대로 펼치고, 값을 고르는 컴포넌트는 `Controller` 로 연결해요.
111
+ 제출에 실패하면 `ErrorSummary` 가 포커스를 받아 고칠 곳을 알려요. 자세한 방법은 [화면 패턴](https://dev-bestow.github.io/BestOW-UI/?path=/docs/guides-%ED%99%94%EB%A9%B4-%ED%8C%A8%ED%84%B4--docs) 가이드에 있어요.
121
112
 
122
- [MIT](https://github.com/Dev-BestOW/BestOW-UI/blob/main/LICENSE) © Dev-BestOW
113
+ ## 컴포넌트
123
114
 
124
- ---
115
+ | 분류 | 컴포넌트 |
116
+ |---|---|
117
+ | 레이아웃 | `Stack`, `Divider`, `Card`, `VisuallyHidden` |
118
+ | 글자 | `Typography` |
119
+ | 동작 | `Button`, `IconButton`, `Link` |
120
+ | 폼 | `Input`, `TextField`, `Textarea`, `Select`, `Combobox`, `NumberInput`, `FileUpload`, `Checkbox`, `RadioGroup`, `Switch`, `Slider` |
121
+ | 피드백 | `Alert`, `ErrorSummary`, `Progress`, `ToastProvider` + `useToast`, `Spinner`, `Skeleton`, `Badge` |
122
+ | 오버레이 | `Modal`, `Drawer`, `Popover`, `Tooltip`, `DropdownMenu` |
123
+ | 탐색 | `Tabs`, `SegmentedControl`, `Accordion`, `Pagination`, `Breadcrumb`, `SkipLink`, `Stepper` |
124
+ | 표시 | `Avatar`, `Table`(+ `TableEmpty`, `TableSkeleton`, `useTableSelection`), `DescriptionList` |
125
+ | 한국 서비스 키트 | `AgreementGroup`(약관 동의), `VerificationCodeInput`(인증번호), `PhoneNumberInput`, `BusinessNumberInput`, `AmountInput`, `AddressInput`, `DatePicker`, `DateRangePicker`(+ `dateRangePresets`), `Calendar` (+ `formatPhoneNumber`, `formatBusinessNumber`, `isValidBusinessNumber`, `formatKoreanAmount`, `formatKoreanDate`, `parseISODate`, `toISODate`) |
126
+ | 문구 | `LocaleProvider`, `en`(영어 문구 묶음) |
127
+ | 테마 | `ThemeProvider`, `createTheme`, `createIntentColors`, `strongInputBorders`, `vars`, `useTheme`, `usePortalProps` |
128
+ | 도움 함수 | `focusField` |
125
129
 
126
- ## 한국어
130
+ 어떤 컴포넌트를 골라야 할지는 [언제 무엇을 쓰나](https://dev-bestow.github.io/BestOW-UI/?path=/docs/guides-%EC%96%B8%EC%A0%9C-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%93%B0%EB%82%98--docs) 가이드를 보세요.
127
131
 
128
- **한국 서비스를 만들 때 바로 쓰는, 접근성이 검증된 React 디자인 시스템**이에요.
132
+ ## 지원 범위
129
133
 
130
- - **한국 서비스 키트** — 약관 동의, 인증번호(문자 자동 채우기·남은 시간), 전화번호·사업자등록번호·금액·주소·날짜 입력.
131
- - **검사로 증명하는 접근성** — 모든 스토리를 라이트·다크에서 axe 로 검사(CI), [KWCAG 2.2 항목별 대응 근거](https://dev-bestow.github.io/BestOW-UI/?path=/docs/accessibility-kwcag-2-2-%EB%8C%80%EC%9D%91--docs).
132
- - **대표색 하나로 테마** — 브랜드 색을 넣어도 대비 기준을 자동으로 지켜요. [테마 빌더](https://dev-bestow.github.io/BestOW-UI/?path=/story/foundations-theme-builder--builder)
133
- - **가벼움** — 런타임 CSS 0, 서버 컴포넌트 지원, 쓰는 컴포넌트만 번들에.
134
- - **다국어** — 기본 문구는 한국어, `LocaleProvider` 로 한 번에 바꿔요.
134
+ Chrome·Edge·Firefox·Safari 최근 2개 메이저 버전(모바일 포함), React 18·19, Next.js App Router 를 CI 로 확인해요.
135
+ 버전 정책(semver·deprecated 절차)과 배포 주기는 [SUPPORT.md](https://github.com/Dev-BestOW/BestOW-UI/blob/main/SUPPORT.md),
136
+ 보안 문제는 공개 이슈 대신 [SECURITY.md](https://github.com/Dev-BestOW/BestOW-UI/blob/main/SECURITY.md) 의 방법으로 알려 주세요.
135
137
 
136
- ```bash
137
- npm install @bestow-ui/react
138
- ```
138
+ ## 링크
139
139
 
140
- ```tsx
141
- import '@bestow-ui/react/styles.css';
142
- import { ThemeProvider, Button } from '@bestow-ui/react';
140
+ - [Storybook](https://dev-bestow.github.io/BestOW-UI/) — 문서, 디자인 토큰, 모든 컴포넌트
141
+ - [GitHub](https://github.com/Dev-BestOW/BestOW-UI) — 소스, [이슈](https://github.com/Dev-BestOW/BestOW-UI/issues), [기여 방법](https://github.com/Dev-BestOW/BestOW-UI/blob/main/CONTRIBUTING.md)
142
+ - [변경 내역](https://github.com/Dev-BestOW/BestOW-UI/blob/main/packages/react/CHANGELOG.md) · [0.x 에서 올리기 (MIGRATION-1.0)](https://github.com/Dev-BestOW/BestOW-UI/blob/main/docs/MIGRATION-1.0.md)
143
143
 
144
- <ThemeProvider theme="light">
145
- <Button>확인</Button>
146
- </ThemeProvider>;
147
- ```
144
+ ## 라이선스
148
145
 
149
- 설치·설정·다크 모드·브랜드 테마·아이콘 사용법은 [Storybook 시작하기](https://dev-bestow.github.io/BestOW-UI/) 에 자세히 정리돼 있어요.
150
- 버그 제보·제안은 [GitHub Issues](https://github.com/Dev-BestOW/BestOW-UI/issues), 기여 방법은 [CONTRIBUTING.md](https://github.com/Dev-BestOW/BestOW-UI/blob/main/CONTRIBUTING.md) 를 봐 주세요.
146
+ [MIT](https://github.com/Dev-BestOW/BestOW-UI/blob/main/LICENSE) © Dev-BestOW
@@ -1,6 +1,6 @@
1
1
  // src/theme/contract.css.ts
2
2
  var themeClass = { light: "tkagtb0", dark: "tkagtb1" };
3
- var vars = { space: { "0": "var(--bestow-space-0)", "1": "var(--bestow-space-1)", "2": "var(--bestow-space-2)", "3": "var(--bestow-space-3)", "4": "var(--bestow-space-4)", "5": "var(--bestow-space-5)", "6": "var(--bestow-space-6)", "8": "var(--bestow-space-8)", "10": "var(--bestow-space-10)", "12": "var(--bestow-space-12)", "16": "var(--bestow-space-16)" }, radius: { none: "var(--bestow-radius-none)", sm: "var(--bestow-radius-sm)", md: "var(--bestow-radius-md)", lg: "var(--bestow-radius-lg)", xl: "var(--bestow-radius-xl)", full: "var(--bestow-radius-full)" }, font: { family: { sans: "var(--bestow-font-family-sans)", mono: "var(--bestow-font-family-mono)" }, size: { xs: "var(--bestow-font-size-xs)", sm: "var(--bestow-font-size-sm)", md: "var(--bestow-font-size-md)", lg: "var(--bestow-font-size-lg)", xl: "var(--bestow-font-size-xl)", "2xl": "var(--bestow-font-size-2xl)", "3xl": "var(--bestow-font-size-3xl)", "4xl": "var(--bestow-font-size-4xl)" }, weight: { regular: "var(--bestow-font-weight-regular)", medium: "var(--bestow-font-weight-medium)", semibold: "var(--bestow-font-weight-semibold)", bold: "var(--bestow-font-weight-bold)" }, lineHeight: { tight: "var(--bestow-font-line-height-tight)", normal: "var(--bestow-font-line-height-normal)", relaxed: "var(--bestow-font-line-height-relaxed)" } }, duration: { fast: "var(--bestow-duration-fast)", normal: "var(--bestow-duration-normal)", slow: "var(--bestow-duration-slow)" }, zIndex: { dropdown: "var(--bestow-z-index-dropdown)", overlay: "var(--bestow-z-index-overlay)", modal: "var(--bestow-z-index-modal)", toast: "var(--bestow-z-index-toast)", tooltip: "var(--bestow-z-index-tooltip)" }, color: { bg: { "default": "var(--bestow-color-bg-default)", subtle: "var(--bestow-color-bg-subtle)", muted: "var(--bestow-color-bg-muted)", inverse: "var(--bestow-color-bg-inverse)", overlay: "var(--bestow-color-bg-overlay)" }, fg: { "default": "var(--bestow-color-fg-default)", muted: "var(--bestow-color-fg-muted)", subtle: "var(--bestow-color-fg-subtle)", inverse: "var(--bestow-color-fg-inverse)" }, border: { "default": "var(--bestow-color-border-default)", strong: "var(--bestow-color-border-strong)", focus: "var(--bestow-color-border-focus)" }, primary: { solid: "var(--bestow-color-primary-solid)", solidHover: "var(--bestow-color-primary-solid-hover)", contrast: "var(--bestow-color-primary-contrast)", subtle: "var(--bestow-color-primary-subtle)", subtleHover: "var(--bestow-color-primary-subtle-hover)", text: "var(--bestow-color-primary-text)" }, neutral: { solid: "var(--bestow-color-neutral-solid)", solidHover: "var(--bestow-color-neutral-solid-hover)", contrast: "var(--bestow-color-neutral-contrast)", subtle: "var(--bestow-color-neutral-subtle)", subtleHover: "var(--bestow-color-neutral-subtle-hover)", text: "var(--bestow-color-neutral-text)" }, danger: { solid: "var(--bestow-color-danger-solid)", solidHover: "var(--bestow-color-danger-solid-hover)", contrast: "var(--bestow-color-danger-contrast)", subtle: "var(--bestow-color-danger-subtle)", subtleHover: "var(--bestow-color-danger-subtle-hover)", text: "var(--bestow-color-danger-text)" }, success: { solid: "var(--bestow-color-success-solid)", solidHover: "var(--bestow-color-success-solid-hover)", contrast: "var(--bestow-color-success-contrast)", subtle: "var(--bestow-color-success-subtle)", subtleHover: "var(--bestow-color-success-subtle-hover)", text: "var(--bestow-color-success-text)" }, warning: { solid: "var(--bestow-color-warning-solid)", solidHover: "var(--bestow-color-warning-solid-hover)", contrast: "var(--bestow-color-warning-contrast)", subtle: "var(--bestow-color-warning-subtle)", subtleHover: "var(--bestow-color-warning-subtle-hover)", text: "var(--bestow-color-warning-text)" } }, shadow: { sm: "var(--bestow-shadow-sm)", md: "var(--bestow-shadow-md)", lg: "var(--bestow-shadow-lg)" } };
3
+ var vars = { space: { "0": "var(--bestow-space-0)", "1": "var(--bestow-space-1)", "2": "var(--bestow-space-2)", "3": "var(--bestow-space-3)", "4": "var(--bestow-space-4)", "5": "var(--bestow-space-5)", "6": "var(--bestow-space-6)", "8": "var(--bestow-space-8)", "10": "var(--bestow-space-10)", "12": "var(--bestow-space-12)", "16": "var(--bestow-space-16)" }, radius: { none: "var(--bestow-radius-none)", sm: "var(--bestow-radius-sm)", md: "var(--bestow-radius-md)", lg: "var(--bestow-radius-lg)", xl: "var(--bestow-radius-xl)", full: "var(--bestow-radius-full)" }, font: { family: { sans: "var(--bestow-font-family-sans)", mono: "var(--bestow-font-family-mono)" }, size: { xs: "var(--bestow-font-size-xs)", sm: "var(--bestow-font-size-sm)", md: "var(--bestow-font-size-md)", lg: "var(--bestow-font-size-lg)", xl: "var(--bestow-font-size-xl)", "2xl": "var(--bestow-font-size-2xl)", "3xl": "var(--bestow-font-size-3xl)", "4xl": "var(--bestow-font-size-4xl)" }, weight: { regular: "var(--bestow-font-weight-regular)", medium: "var(--bestow-font-weight-medium)", semibold: "var(--bestow-font-weight-semibold)", bold: "var(--bestow-font-weight-bold)" }, lineHeight: { tight: "var(--bestow-font-line-height-tight)", normal: "var(--bestow-font-line-height-normal)", relaxed: "var(--bestow-font-line-height-relaxed)" } }, duration: { fast: "var(--bestow-duration-fast)", normal: "var(--bestow-duration-normal)", slow: "var(--bestow-duration-slow)" }, zIndex: { overlay: "var(--bestow-z-index-overlay)", modal: "var(--bestow-z-index-modal)", dropdown: "var(--bestow-z-index-dropdown)", toast: "var(--bestow-z-index-toast)", tooltip: "var(--bestow-z-index-tooltip)" }, color: { bg: { "default": "var(--bestow-color-bg-default)", subtle: "var(--bestow-color-bg-subtle)", muted: "var(--bestow-color-bg-muted)", inverse: "var(--bestow-color-bg-inverse)", overlay: "var(--bestow-color-bg-overlay)" }, fg: { "default": "var(--bestow-color-fg-default)", muted: "var(--bestow-color-fg-muted)", subtle: "var(--bestow-color-fg-subtle)", inverse: "var(--bestow-color-fg-inverse)" }, border: { "default": "var(--bestow-color-border-default)", strong: "var(--bestow-color-border-strong)", focus: "var(--bestow-color-border-focus)", input: "var(--bestow-color-border-input)", inputHover: "var(--bestow-color-border-input-hover)" }, primary: { solid: "var(--bestow-color-primary-solid)", solidHover: "var(--bestow-color-primary-solid-hover)", contrast: "var(--bestow-color-primary-contrast)", subtle: "var(--bestow-color-primary-subtle)", subtleHover: "var(--bestow-color-primary-subtle-hover)", text: "var(--bestow-color-primary-text)" }, neutral: { solid: "var(--bestow-color-neutral-solid)", solidHover: "var(--bestow-color-neutral-solid-hover)", contrast: "var(--bestow-color-neutral-contrast)", subtle: "var(--bestow-color-neutral-subtle)", subtleHover: "var(--bestow-color-neutral-subtle-hover)", text: "var(--bestow-color-neutral-text)" }, danger: { solid: "var(--bestow-color-danger-solid)", solidHover: "var(--bestow-color-danger-solid-hover)", contrast: "var(--bestow-color-danger-contrast)", subtle: "var(--bestow-color-danger-subtle)", subtleHover: "var(--bestow-color-danger-subtle-hover)", text: "var(--bestow-color-danger-text)" }, success: { solid: "var(--bestow-color-success-solid)", solidHover: "var(--bestow-color-success-solid-hover)", contrast: "var(--bestow-color-success-contrast)", subtle: "var(--bestow-color-success-subtle)", subtleHover: "var(--bestow-color-success-subtle-hover)", text: "var(--bestow-color-success-text)" }, warning: { solid: "var(--bestow-color-warning-solid)", solidHover: "var(--bestow-color-warning-solid-hover)", contrast: "var(--bestow-color-warning-contrast)", subtle: "var(--bestow-color-warning-subtle)", subtleHover: "var(--bestow-color-warning-subtle-hover)", text: "var(--bestow-color-warning-text)" } }, shadow: { sm: "var(--bestow-shadow-sm)", md: "var(--bestow-shadow-md)", lg: "var(--bestow-shadow-lg)" } };
4
4
 
5
5
  // src/theme/createTheme.ts
6
6
  import { assignInlineVars } from "@vanilla-extract/dynamic";
@@ -185,11 +185,96 @@ function formatKoreanDate(date, withWeekday = true) {
185
185
  return withWeekday ? `${base} ${KOREAN_WEEKDAYS[date.getDay()]}\uC694\uC77C` : base;
186
186
  }
187
187
 
188
+ // src/components/DateRangePicker/presets.ts
189
+ var dateRangePresets = [
190
+ { label: "\uC624\uB298", days: 0 },
191
+ { label: "1\uC8FC\uC77C", days: 7 },
192
+ { label: "1\uAC1C\uC6D4", months: 1 },
193
+ { label: "3\uAC1C\uC6D4", months: 3 }
194
+ ];
195
+ function presetRange(preset, today) {
196
+ const end = parseISODate(today);
197
+ if (!end) return { start: "", end: "" };
198
+ const start = preset.months !== void 0 ? addMonths(end, -preset.months) : addDays(end, -(preset.days ?? 0));
199
+ return { start: toISODate(start), end: today };
200
+ }
201
+
202
+ // src/utils/focusField.ts
203
+ var FOCUSABLE = 'input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])';
204
+ function isUsable(element) {
205
+ return element instanceof HTMLElement && element.matches(FOCUSABLE) && element.tabIndex >= 0 && !element.closest('[aria-hidden="true"], [inert], [hidden]');
206
+ }
207
+ function firstUsableIn(container) {
208
+ if (!container) return null;
209
+ const checked = Array.from(container.querySelectorAll(':checked, [aria-checked="true"]')).find(isUsable);
210
+ return checked ?? Array.from(container.querySelectorAll(FOCUSABLE)).find(isUsable) ?? null;
211
+ }
212
+ function focusField(target) {
213
+ const element = typeof target === "string" ? document.getElementById(target) : target ?? null;
214
+ if (!element) return false;
215
+ const destination = (isUsable(element) ? element : null) ?? firstUsableIn(element) ?? firstUsableIn(element.closest('[role="group"], [role="radiogroup"], fieldset'));
216
+ if (!destination) return false;
217
+ destination.focus();
218
+ return document.activeElement === destination;
219
+ }
220
+
221
+ // src/components/Skeleton/Skeleton.tsx
222
+ import { assignInlineVars as assignInlineVars2 } from "@vanilla-extract/dynamic";
223
+ import { forwardRef } from "react";
224
+
188
225
  // src/utils/cx.ts
189
226
  function cx(...classNames) {
190
227
  return classNames.filter(Boolean).join(" ");
191
228
  }
192
229
 
230
+ // src/components/Skeleton/Skeleton.css.ts
231
+ import { createRuntimeFn as _7a468 } from "@vanilla-extract/recipes/createRuntimeFn";
232
+ var height = "var(--_1s8datd1)";
233
+ var lines = "_1s8datd6";
234
+ var skeleton = /* @__PURE__ */ _7a468({ defaultClassName: "_1s8datd2", variantClassNames: { variant: { text: "_1s8datd3", rect: "_1s8datd4", circle: "_1s8datd5" } }, defaultVariants: { variant: "text" }, compoundVariants: [] });
235
+ var width = "var(--_1s8datd0)";
236
+
237
+ // src/components/Skeleton/Skeleton.tsx
238
+ import { jsx } from "react/jsx-runtime";
239
+ var toCss = (value) => typeof value === "number" ? `${value}px` : value;
240
+ var Skeleton = /* @__PURE__ */ forwardRef(function Skeleton2({ variant = "text", width: width2, height: height2, lines: lines2 = 1, className, style, ...rest }, ref) {
241
+ const w = toCss(width2 ?? (variant === "circle" ? 40 : "100%"));
242
+ const h = toCss(height2 ?? (variant === "circle" ? w : variant === "rect" ? 100 : "1em"));
243
+ if (variant === "text" && lines2 > 1) {
244
+ return /* @__PURE__ */ jsx(
245
+ "span",
246
+ {
247
+ ref,
248
+ "aria-hidden": true,
249
+ className: cx(lines, className),
250
+ style: { ...assignInlineVars2({ [width]: w }), ...style },
251
+ ...rest,
252
+ children: Array.from({ length: lines2 }, (_, i) => /* @__PURE__ */ jsx(
253
+ "span",
254
+ {
255
+ className: skeleton({ variant }),
256
+ style: assignInlineVars2({ [width]: i === lines2 - 1 ? "60%" : "100%", [height]: h })
257
+ },
258
+ i
259
+ ))
260
+ }
261
+ );
262
+ }
263
+ return /* @__PURE__ */ jsx(
264
+ "span",
265
+ {
266
+ ref,
267
+ "aria-hidden": true,
268
+ className: cx(skeleton({ variant }), className),
269
+ style: { ...assignInlineVars2({ [width]: w, [height]: h }), ...style },
270
+ ...rest
271
+ }
272
+ );
273
+ });
274
+
275
+ // src/styles/visuallyHidden.css.ts
276
+ var visuallyHidden = "_1kdtbw10";
277
+
193
278
  export {
194
279
  cx,
195
280
  themeClass,
@@ -197,6 +282,7 @@ export {
197
282
  createTheme,
198
283
  mergeThemes,
199
284
  themeToStyle,
285
+ visuallyHidden,
200
286
  composeExtraAddress,
201
287
  kakaoPostcodeProvider,
202
288
  phoneNumberMaxDigits,
@@ -213,6 +299,10 @@ export {
213
299
  compareDays,
214
300
  getCalendarWeeks,
215
301
  KOREAN_WEEKDAYS,
216
- formatKoreanDate
302
+ formatKoreanDate,
303
+ dateRangePresets,
304
+ presetRange,
305
+ focusField,
306
+ Skeleton
217
307
  };
218
- //# sourceMappingURL=chunk-LHT64JDG.js.map
308
+ //# sourceMappingURL=chunk-TIFI6ZQU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theme/contract.css.ts","../src/theme/createTheme.ts","../src/components/AddressInput/addressProviders.ts","../src/utils/koreanFormat.ts","../src/utils/date.ts","../src/components/DateRangePicker/presets.ts","../src/utils/focusField.ts","../src/components/Skeleton/Skeleton.tsx","../src/utils/cx.ts","../src/components/Skeleton/Skeleton.css.ts","../src/styles/visuallyHidden.css.ts"],"sourcesContent":["import 'src/theme/contract.css.ts.vanilla.css?source=#H4sIAAAAAAAAA-2XTW_jNhCG7_kVA-fSAJIhUh-2tKcuii3QIotiA_ROSaQjrCQapLRxttj_XlCSZVseKkb30BwUODaseTnkvPwwn0RJ2cA_dwCum3LdyBdX71nGXS8B78P1Y5JAsD8gAZrAFg34CRCKRoIESIRGwgSoh0aiBCg-gG0CPt4P8RII8HSEJhDg4yZRAtG0K8XyotVuLWs-9WcI6eraoSFU5dceDaFyh7g0xA4l4tMQE21ZJhDHcTyJC1k3rmBVUb66mtU6gdVfije8zpnK4W-mCpaWfOXA6akDLtvvS-7qV93wyoGPZVF_fWTZU_f9k6wbB_qY2xYOrH41anj6DX6XzXORwWcuVw6sPstGwhOrNfz5ZeWA6d3VXBXCOsBK1jKB1R-8-ahYUWt4lLVJ1RZdqJsPB54-mcfuF75rS6YceOR1KR0YFdfpdfGduwedgLfehIpXFoWZMm-9nZOYqSP2cDd9a0JnMnSzuJ5T0F4yo_B7xexIA6Ohto5eeLF7blzVe2i2hWcVVTwv2iqBcEajeVWksswTiGZUvWKDKcqi5u5zL2vMe-_SvLCWqmK9WfNCxUt24Gbu1ptLad4q1hSydgXTpk_qVRoXHDujnlWiS_liTp-p4Ltb1Dk_uPIbVyV7TYD4Ew-OikrmXUE-weO5kvtcvtRGQnFJI_tKplN6isuyKfYJkOmEZrKUyk13bs4Fa8smgXvR_eEq3aZNyY1oK2LBcFHVNsb3e0GEL0JcU9TfuNImEyU0pDGuGs1Tu5T94jkwvNZB-IC0EOdlWPOK0wiDOPTCDa4ZS42iiG8iXHQqw-6aVDlXZwPLOaccyzcodaNkvUvgnuVpmOZ2oZBZqxO499NICCzhXhUVU6-ulmVhyqVhGPD0LaH7bGxP4J7kAc23M_JM1o3qVp69_DH10U7ORXCDchxFnvJI8Bl9ww_N3Ghr3jaKlaMLfuCzANsFF8Kxf-s6OspvcWFMPe4f2964VI6j4DHP-FzmwQVrcTmrd2ZxDSbkGY0otmbOdWPvaUwyktnVt1hwTDw6wAUV9C3hOARhds2MfKjfOlLdZhnXejSAhFvPxzbXhfC0F6Io9IMZ-S0WjKlHDzyRi7ms072QiYxj59VRf9wLtuJemKqLeje6IMKYe9iJcCE89R9vNh62ao7yMxes-2ZMPbogRIoeS5fKs6Ug_Axz4agfXIhp4HmXJ4d-Zrn5MLc-IPsD0P3h6sfFix6wRuYe6Jn7fXdbv261RVuZ66HXt_CxZoQ-fLj7cbduvrJdk3oLji04tuDYgmMLji04tuDYgmMLji04tuDYgmMLjv0fOEYWHFtwbMGxBccWHFtwrMcFEpEtwX4dz3FsDqKOsGO9Gl7g2BzYWXEsehvHrHnPcMxKRuc4ZqUicRtVTnHMyoD_FcciL_bQSt8jjnUzGcYOEEIcoEHU3UkibDpxPkMT0NkEx-uZnwYC83MKbG-yz6VPb6LVLZfUKbB1ZdIwdOD0dn3nm0M4PAMJ5jIMTlkrem9Q19foxw5E2_7ftphQyMOa40vpEvpExkKG-fMeoa8r0g8cIPHGgTiwe4RDINYeN2kChduIC4bN909CoUhTQTGXfgYK-6UQhA6QcGsOF7tNOCWiCXCfJtgostwPLhfJTdgYzlCjN7SzHiPOHFrimXuynM9MusR2-jR-_PgXHH1O55cpAAA';\nexport var themeClass = {light:'tkagtb0',dark:'tkagtb1'};\nexport var vars = {space:{'0':'var(--bestow-space-0)','1':'var(--bestow-space-1)','2':'var(--bestow-space-2)','3':'var(--bestow-space-3)','4':'var(--bestow-space-4)','5':'var(--bestow-space-5)','6':'var(--bestow-space-6)','8':'var(--bestow-space-8)','10':'var(--bestow-space-10)','12':'var(--bestow-space-12)','16':'var(--bestow-space-16)'},radius:{none:'var(--bestow-radius-none)',sm:'var(--bestow-radius-sm)',md:'var(--bestow-radius-md)',lg:'var(--bestow-radius-lg)',xl:'var(--bestow-radius-xl)',full:'var(--bestow-radius-full)'},font:{family:{sans:'var(--bestow-font-family-sans)',mono:'var(--bestow-font-family-mono)'},size:{xs:'var(--bestow-font-size-xs)',sm:'var(--bestow-font-size-sm)',md:'var(--bestow-font-size-md)',lg:'var(--bestow-font-size-lg)',xl:'var(--bestow-font-size-xl)','2xl':'var(--bestow-font-size-2xl)','3xl':'var(--bestow-font-size-3xl)','4xl':'var(--bestow-font-size-4xl)'},weight:{regular:'var(--bestow-font-weight-regular)',medium:'var(--bestow-font-weight-medium)',semibold:'var(--bestow-font-weight-semibold)',bold:'var(--bestow-font-weight-bold)'},lineHeight:{tight:'var(--bestow-font-line-height-tight)',normal:'var(--bestow-font-line-height-normal)',relaxed:'var(--bestow-font-line-height-relaxed)'}},duration:{fast:'var(--bestow-duration-fast)',normal:'var(--bestow-duration-normal)',slow:'var(--bestow-duration-slow)'},zIndex:{overlay:'var(--bestow-z-index-overlay)',modal:'var(--bestow-z-index-modal)',dropdown:'var(--bestow-z-index-dropdown)',toast:'var(--bestow-z-index-toast)',tooltip:'var(--bestow-z-index-tooltip)'},color:{bg:{'default':'var(--bestow-color-bg-default)',subtle:'var(--bestow-color-bg-subtle)',muted:'var(--bestow-color-bg-muted)',inverse:'var(--bestow-color-bg-inverse)',overlay:'var(--bestow-color-bg-overlay)'},fg:{'default':'var(--bestow-color-fg-default)',muted:'var(--bestow-color-fg-muted)',subtle:'var(--bestow-color-fg-subtle)',inverse:'var(--bestow-color-fg-inverse)'},border:{'default':'var(--bestow-color-border-default)',strong:'var(--bestow-color-border-strong)',focus:'var(--bestow-color-border-focus)',input:'var(--bestow-color-border-input)',inputHover:'var(--bestow-color-border-input-hover)'},primary:{solid:'var(--bestow-color-primary-solid)',solidHover:'var(--bestow-color-primary-solid-hover)',contrast:'var(--bestow-color-primary-contrast)',subtle:'var(--bestow-color-primary-subtle)',subtleHover:'var(--bestow-color-primary-subtle-hover)',text:'var(--bestow-color-primary-text)'},neutral:{solid:'var(--bestow-color-neutral-solid)',solidHover:'var(--bestow-color-neutral-solid-hover)',contrast:'var(--bestow-color-neutral-contrast)',subtle:'var(--bestow-color-neutral-subtle)',subtleHover:'var(--bestow-color-neutral-subtle-hover)',text:'var(--bestow-color-neutral-text)'},danger:{solid:'var(--bestow-color-danger-solid)',solidHover:'var(--bestow-color-danger-solid-hover)',contrast:'var(--bestow-color-danger-contrast)',subtle:'var(--bestow-color-danger-subtle)',subtleHover:'var(--bestow-color-danger-subtle-hover)',text:'var(--bestow-color-danger-text)'},success:{solid:'var(--bestow-color-success-solid)',solidHover:'var(--bestow-color-success-solid-hover)',contrast:'var(--bestow-color-success-contrast)',subtle:'var(--bestow-color-success-subtle)',subtleHover:'var(--bestow-color-success-subtle-hover)',text:'var(--bestow-color-success-text)'},warning:{solid:'var(--bestow-color-warning-solid)',solidHover:'var(--bestow-color-warning-solid-hover)',contrast:'var(--bestow-color-warning-contrast)',subtle:'var(--bestow-color-warning-subtle)',subtleHover:'var(--bestow-color-warning-subtle-hover)',text:'var(--bestow-color-warning-text)'}},shadow:{sm:'var(--bestow-shadow-sm)',md:'var(--bestow-shadow-md)',lg:'var(--bestow-shadow-lg)'}};","import { assignInlineVars } from '@vanilla-extract/dynamic';\nimport { vars, type ThemeName, type ThemeTokens } from './contract.css';\n\nexport type DeepPartial<T> = { [K in keyof T]?: T[K] extends string ? string : DeepPartial<T[K]> };\n\nexport type ThemeOverrides = DeepPartial<ThemeTokens>;\n\n/** createTheme 에 넘기는 입력. common 은 라이트·다크 공통, light/dark 는 모드별 값 (common 보다 우선). */\nexport interface ThemeInput {\n common?: ThemeOverrides;\n light?: ThemeOverrides;\n dark?: ThemeOverrides;\n}\n\n/** createTheme 의 결과. ThemeProvider 의 overrides 로 넘긴다. */\nexport interface BestowTheme {\n readonly light: ThemeOverrides;\n readonly dark: ThemeOverrides;\n}\n\ntype Tree = { [key: string]: string | Tree };\n\nfunction deepMerge(target: Tree, source: Tree | undefined): Tree {\n if (!source) return target;\n const out: Tree = { ...target };\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) continue;\n const prev = out[key];\n out[key] = typeof value === 'object' && typeof prev === 'object' ? deepMerge(prev, value) : value;\n }\n return out;\n}\n\n/**\n * 브랜드 테마를 만든다. 여러 입력을 넘기면 순서대로 깊게 병합한다 (뒤가 우선).\n *\n * @example\n * const brand = createTheme(\n * createIntentColors('primary', '#7c3aed'),\n * { common: { radius: { md: '6px' }, font: { family: { sans: 'Inter, sans-serif' } } } },\n * );\n * <ThemeProvider theme=\"dark\" overrides={brand}>…</ThemeProvider>\n */\nexport function createTheme(...inputs: ThemeInput[]): BestowTheme {\n let light: Tree = {};\n let dark: Tree = {};\n for (const input of inputs) {\n light = deepMerge(deepMerge(light, input.common as Tree), input.light as Tree);\n dark = deepMerge(deepMerge(dark, input.common as Tree), input.dark as Tree);\n }\n return Object.freeze({ light, dark }) as BestowTheme;\n}\n\n/** 두 테마를 병합한다 (b 가 우선). 중첩 ThemeProvider 가 바깥 테마를 이어받을 때 쓴다. */\nexport function mergeThemes(a: BestowTheme | undefined, b: BestowTheme | undefined): BestowTheme | undefined {\n if (!a) return b;\n if (!b) return a;\n return createTheme(a, b);\n}\n\n/** 부분 토큰을 { 'var(--bestow-…)': 값 } 으로 펼친다. 알 수 없는 키는 무시한다. */\nfunction flatten(partial: Tree, contract: Tree, out: Record<string, string>) {\n for (const [key, value] of Object.entries(partial)) {\n const target = contract[key];\n if (target === undefined) continue;\n if (typeof target === 'string' && typeof value === 'string') out[target] = value;\n else if (typeof target === 'object' && typeof value === 'object') flatten(value, target, out);\n }\n return out;\n}\n\n/** 현재 모드의 오버라이드를 인라인 style 로 바꾼다. */\nexport function themeToStyle(theme: BestowTheme | undefined, mode: ThemeName): Record<string, string> | undefined {\n if (!theme) return undefined;\n const map = flatten(theme[mode] as Tree, vars as unknown as Tree, {});\n return Object.keys(map).length ? assignInlineVars(map) : undefined;\n}\n","/** 주소 검색 결과 */\nexport interface AddressSearchResult {\n /** 우편번호 (5자리 국가기초구역번호) */\n zonecode: string;\n /** 기본 주소 (도로명 또는 지번) */\n address: string;\n /** 참고 항목 (도로명 주소일 때 법정동·건물명, 예: \" (역삼동, 래미안아파트)\") */\n extraAddress?: string;\n}\n\n/**\n * 주소 검색 공급자. container 안에 검색 화면을 그리고, 사용자가 고르면 onSelect 를 부른다.\n * 정리 함수를 돌려주면 검색 창이 닫힐 때 부른다. 다른 주소 서비스(행정안전부 도로명주소 API 등)로 바꿔 끼울 수 있다.\n */\nexport type AddressProvider = (\n container: HTMLElement,\n onSelect: (result: AddressSearchResult) => void,\n) => void | (() => void) | Promise<void | (() => void)>;\n\n// 카카오 우편번호 서비스 (https://postcode.map.kakao.com/guide)\nconst KAKAO_POSTCODE_SRC = 'https://t1.kakaocdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js';\n\ninterface KakaoPostcodeData {\n zonecode: string;\n userSelectedType: 'R' | 'J';\n roadAddress: string;\n jibunAddress: string;\n autoJibunAddress?: string;\n bname: string;\n buildingName: string;\n apartment: 'Y' | 'N';\n}\n\ntype PostcodeConstructor = new (options: {\n oncomplete: (data: KakaoPostcodeData) => void;\n width?: string | number;\n height?: string | number;\n}) => { embed: (element: HTMLElement) => void };\n\ninterface PostcodeWindow {\n kakao?: { Postcode?: PostcodeConstructor };\n daum?: { Postcode?: PostcodeConstructor };\n}\n\nlet loading: Promise<PostcodeConstructor> | null = null;\n\nfunction loadKakaoPostcode(): Promise<PostcodeConstructor> {\n const w = window as unknown as PostcodeWindow;\n const existing = w.kakao?.Postcode ?? w.daum?.Postcode;\n if (existing) return Promise.resolve(existing);\n loading ??= new Promise<PostcodeConstructor>((resolve, reject) => {\n const script = document.createElement('script');\n script.src = KAKAO_POSTCODE_SRC;\n script.async = true;\n script.onload = () => {\n const ctor = w.kakao?.Postcode ?? w.daum?.Postcode;\n if (ctor) resolve(ctor);\n else reject(new Error('우편번호 서비스를 찾지 못했어요'));\n };\n script.onerror = () => {\n loading = null; // 다음에 다시 시도할 수 있게\n reject(new Error('우편번호 서비스를 불러오지 못했어요'));\n };\n document.head.appendChild(script);\n });\n return loading;\n}\n\n/** 카카오 가이드의 방식으로 도로명 주소의 참고 항목(법정동, 공동주택 건물명)을 만든다 */\nexport function composeExtraAddress(\n data: Pick<KakaoPostcodeData, 'userSelectedType' | 'bname' | 'buildingName' | 'apartment'>,\n) {\n if (data.userSelectedType !== 'R') return '';\n const parts: string[] = [];\n // 법정동명은 \"동·로·가\" 로 끝날 때만 (법정리는 제외)\n if (data.bname && /[동로가]$/.test(data.bname)) parts.push(data.bname);\n if (data.buildingName && data.apartment === 'Y') parts.push(data.buildingName);\n return parts.length ? ` (${parts.join(', ')})` : '';\n}\n\n/**\n * 기본 공급자: 카카오(다음) 우편번호 서비스. 처음 쓸 때 스크립트를 불러오므로 코어 번들 크기에 영향이 없다.\n * 사용 조건은 서비스 가이드를 따른다.\n */\nexport const kakaoPostcodeProvider: AddressProvider = async (container, onSelect) => {\n const Postcode = await loadKakaoPostcode();\n new Postcode({\n width: '100%',\n height: '100%',\n oncomplete: (data) => {\n const address =\n data.userSelectedType === 'R' ? data.roadAddress : data.jibunAddress || data.autoJibunAddress || '';\n onSelect({ zonecode: data.zonecode, address, extraAddress: composeExtraAddress(data) });\n },\n }).embed(container);\n};\n","// 한국 서비스에서 자주 쓰는 서식·검증 함수. 컴포넌트와 함께 공개 API 로 export 한다.\n\nconst onlyDigits = (input: string) => input.replace(/\\D/g, '');\n\n/** 전화번호 최대 자릿수: 02 는 10, 15xx·16xx·18xx 대표번호는 8, 나머지(010, 031, 070 …)는 11 */\nexport function phoneNumberMaxDigits(digits: string): number {\n if (digits.startsWith('02')) return 10;\n if (/^1[568]/.test(digits)) return 8;\n return 11;\n}\n\n/**\n * 숫자만 있는 전화번호에 하이픈을 넣는다. 입력 중인 값도 자연스럽게 나눈다.\n * @example formatPhoneNumber('01012345678') // '010-1234-5678'\n * @example formatPhoneNumber('021234567') // '02-123-4567'\n * @example formatPhoneNumber('15881234') // '1588-1234'\n */\nexport function formatPhoneNumber(input: string): string {\n const d = onlyDigits(input).slice(0, phoneNumberMaxDigits(onlyDigits(input)));\n if (d.startsWith('02')) {\n if (d.length <= 2) return d;\n if (d.length <= 5) return `${d.slice(0, 2)}-${d.slice(2)}`;\n if (d.length <= 9) return `${d.slice(0, 2)}-${d.slice(2, 5)}-${d.slice(5)}`;\n return `${d.slice(0, 2)}-${d.slice(2, 6)}-${d.slice(6)}`;\n }\n if (/^1[568]/.test(d)) {\n return d.length <= 4 ? d : `${d.slice(0, 4)}-${d.slice(4)}`;\n }\n if (d.length <= 3) return d;\n if (d.length <= 6) return `${d.slice(0, 3)}-${d.slice(3)}`;\n if (d.length <= 10) return `${d.slice(0, 3)}-${d.slice(3, 6)}-${d.slice(6)}`;\n return `${d.slice(0, 3)}-${d.slice(3, 7)}-${d.slice(7)}`;\n}\n\n/**\n * 사업자등록번호(10자리)에 하이픈을 넣는다.\n * @example formatBusinessNumber('1234567890') // '123-45-67890'\n */\nexport function formatBusinessNumber(input: string): string {\n const d = onlyDigits(input).slice(0, 10);\n if (d.length <= 3) return d;\n if (d.length <= 5) return `${d.slice(0, 3)}-${d.slice(3)}`;\n return `${d.slice(0, 3)}-${d.slice(3, 5)}-${d.slice(5)}`;\n}\n\n/**\n * 사업자등록번호 검증 공식(가중치 1,3,7,1,3,7,1,3,5)으로 확인한다. 실제로 등록된 번호인지는 국세청 조회가 필요하다.\n * @example isValidBusinessNumber('124-81-00998') // true\n */\nexport function isValidBusinessNumber(input: string): boolean {\n const d = onlyDigits(input);\n if (d.length !== 10) return false;\n const weights = [1, 3, 7, 1, 3, 7, 1, 3, 5];\n const digits = [...d].map(Number);\n let sum = weights.reduce((acc, w, i) => acc + w * digits[i]!, 0);\n sum += Math.floor((digits[8]! * 5) / 10);\n return (10 - (sum % 10)) % 10 === digits[9];\n}\n\n/**\n * 금액을 만·억·조 단위로 읽기 쉽게 쓴다.\n * @example formatKoreanAmount(125000) // '12만 5,000원'\n * @example formatKoreanAmount(150000000) // '1억 5,000만원'\n * @example formatKoreanAmount(0) // '0원'\n */\nexport function formatKoreanAmount(amount: number, unit = '원'): string {\n const n = Math.floor(Math.abs(amount));\n if (n === 0) return `0${unit}`;\n const units = [\n { value: 1e12, label: '조' },\n { value: 1e8, label: '억' },\n { value: 1e4, label: '만' },\n { value: 1, label: '' },\n ];\n let rest = n;\n const parts: string[] = [];\n for (const u of units) {\n const q = Math.floor(rest / u.value);\n rest %= u.value;\n if (q > 0) parts.push(`${q.toLocaleString('ko-KR')}${u.label}`);\n }\n return `${amount < 0 ? '-' : ''}${parts.join(' ')}${unit}`;\n}\n","// 날짜 계산 유틸. 값은 시간대 문제가 없도록 'YYYY-MM-DD' 문자열로 다루고, 계산할 때만 로컬 Date 로 바꾼다.\n\nconst pad = (n: number) => String(n).padStart(2, '0');\n\n/** Date → 'YYYY-MM-DD' (로컬 기준) */\nexport function toISODate(date: Date): string {\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n}\n\n/** 'YYYY-MM-DD' → Date (로컬 자정). 형식이 틀리거나 없는 날짜(2월 30일 등)면 null */\nexport function parseISODate(value: string | null | undefined): Date | null {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value ?? '');\n if (!m) return null;\n const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];\n const date = new Date(y, mo - 1, d);\n return date.getFullYear() === y && date.getMonth() === mo - 1 && date.getDate() === d ? date : null;\n}\n\nexport function addDays(date: Date, days: number): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);\n}\n\n/** 달을 옮긴다. 옮긴 달에 같은 날이 없으면 그 달의 마지막 날 (1월 31일 + 1달 → 2월 28/29일) */\nexport function addMonths(date: Date, months: number): Date {\n const target = new Date(date.getFullYear(), date.getMonth() + months, 1);\n const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();\n return new Date(target.getFullYear(), target.getMonth(), Math.min(date.getDate(), lastDay));\n}\n\nexport function isSameDay(a: Date | null, b: Date | null): boolean {\n return !!a && !!b && toISODate(a) === toISODate(b);\n}\n\nexport function isSameMonth(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n}\n\n/** 날짜만 비교 (-1, 0, 1) */\nexport function compareDays(a: Date, b: Date): number {\n const x = toISODate(a);\n const y = toISODate(b);\n return x < y ? -1 : x > y ? 1 : 0;\n}\n\n/** 일요일부터 시작하는 달력의 주 목록 (앞뒤 달 날짜 포함, 항상 6주) */\nexport function getCalendarWeeks(year: number, month: number): Date[][] {\n const first = new Date(year, month, 1);\n const start = addDays(first, -first.getDay());\n return Array.from({ length: 6 }, (_, week) => Array.from({ length: 7 }, (_, day) => addDays(start, week * 7 + day)));\n}\n\nexport const KOREAN_WEEKDAYS = ['일', '월', '화', '수', '목', '금', '토'] as const;\n\n/**\n * 한국어 날짜. 스크린리더용 날짜 이름 등에 쓴다.\n * @example formatKoreanDate(new Date(2026, 9, 15)) // '2026년 10월 15일 목요일'\n */\nexport function formatKoreanDate(date: Date, withWeekday = true): string {\n const base = `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일`;\n return withWeekday ? `${base} ${KOREAN_WEEKDAYS[date.getDay()]}요일` : base;\n}\n","import { addDays, addMonths, parseISODate, toISODate } from '../../utils/date';\n\nexport interface DateRange {\n /** 시작일 'YYYY-MM-DD' (없으면 '') */\n start: string;\n /** 종료일 'YYYY-MM-DD' (없으면 '') */\n end: string;\n}\n\n/** 빠른 선택 — 오늘에서 거슬러 올라간 기간. `days: 0` 은 오늘 하루 */\nexport interface DateRangePreset {\n label: string;\n days?: number;\n months?: number;\n}\n\n/** 조회 화면에서 흔한 빠른 선택(오늘·1주일·1개월·3개월). 그대로 넘기거나 골라 쓴다 */\nexport const dateRangePresets: DateRangePreset[] = [\n { label: '오늘', days: 0 },\n { label: '1주일', days: 7 },\n { label: '1개월', months: 1 },\n { label: '3개월', months: 3 },\n];\n\n/** 빠른 선택이 가리키는 기간 (오늘 'YYYY-MM-DD' 기준) */\nexport function presetRange(preset: DateRangePreset, today: string): DateRange {\n const end = parseISODate(today);\n if (!end) return { start: '', end: '' };\n const start = preset.months !== undefined ? addMonths(end, -preset.months) : addDays(end, -(preset.days ?? 0));\n return { start: toISODate(start), end: today };\n}\n","const FOCUSABLE =\n 'input:not([type=\"hidden\"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), a[href], [tabindex]:not([tabindex=\"-1\"])';\n\n/** 화면에 보이고 보조기술에서 숨기지 않은, Tab 으로 갈 수 있는 요소 */\nfunction isUsable(element: Element): element is HTMLElement {\n return (\n element instanceof HTMLElement &&\n element.matches(FOCUSABLE) &&\n element.tabIndex >= 0 &&\n !element.closest('[aria-hidden=\"true\"], [inert], [hidden]')\n );\n}\n\nfunction firstUsableIn(container: Element | null): HTMLElement | null {\n if (!container) return null;\n // 라디오·세그먼트처럼 하나를 고르는 묶음은 지금 선택된 항목으로 간다\n const checked = Array.from(container.querySelectorAll(':checked, [aria-checked=\"true\"]')).find(isUsable);\n return checked ?? Array.from(container.querySelectorAll(FOCUSABLE)).find(isUsable) ?? null;\n}\n\n/**\n * 폼 칸으로 포커스를 옮긴다(오류 요약의 링크, 제출 실패 뒤 등). `target` 은 칸의 id 나 요소.\n * id 가 포커스할 수 없는 요소를 가리켜도 알맞은 곳을 찾는다 — RadioGroup·AgreementGroup 처럼 묶음이면 선택된(없으면 첫) 항목,\n * FileUpload 처럼 숨긴 input 이면 같은 묶음(role=\"group\")의 버튼. 옮겼으면 true.\n */\nexport function focusField(target: string | HTMLElement | null | undefined): boolean {\n const element = typeof target === 'string' ? document.getElementById(target) : (target ?? null);\n if (!element) return false;\n const destination =\n (isUsable(element) ? element : null) ??\n firstUsableIn(element) ??\n firstUsableIn(element.closest('[role=\"group\"], [role=\"radiogroup\"], fieldset'));\n if (!destination) return false;\n destination.focus();\n return document.activeElement === destination;\n}\n","import { assignInlineVars } from '@vanilla-extract/dynamic';\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cx } from '../../utils/cx';\nimport * as styles from './Skeleton.css';\n\nexport interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {\n /** 모양. text 는 글자 높이(1em), circle 은 width 를 지름으로 쓴다 */\n variant?: 'text' | 'rect' | 'circle';\n /** 너비. 숫자는 px (기본: text·rect 100%, circle 40px) */\n width?: number | string;\n /** 높이. 숫자는 px (기본: text 1em, rect 100px, circle 은 width 와 같음) */\n height?: number | string;\n /** text 에서 여러 줄을 그린다. 마지막 줄은 60% 너비 */\n lines?: number;\n}\n\nconst toCss = (value: number | string) => (typeof value === 'number' ? `${value}px` : value);\n\n/**\n * 콘텐츠를 불러오는 동안 자리를 표시한다. 장식용이라 스크린리더에서 숨긴다 —\n * 로딩 중인 영역(감싸는 요소)에 `aria-busy=\"true\"` 를 붙여 상태를 알린다.\n */\nexport const Skeleton = /* @__PURE__ */ forwardRef<HTMLSpanElement, SkeletonProps>(function Skeleton(\n { variant = 'text', width, height, lines = 1, className, style, ...rest },\n ref,\n) {\n const w = toCss(width ?? (variant === 'circle' ? 40 : '100%'));\n const h = toCss(height ?? (variant === 'circle' ? w : variant === 'rect' ? 100 : '1em'));\n\n if (variant === 'text' && lines > 1) {\n return (\n <span\n ref={ref}\n aria-hidden\n className={cx(styles.lines, className)}\n style={{ ...assignInlineVars({ [styles.width]: w }), ...style }}\n {...rest}\n >\n {Array.from({ length: lines }, (_, i) => (\n <span\n key={i}\n className={styles.skeleton({ variant })}\n style={assignInlineVars({ [styles.width]: i === lines - 1 ? '60%' : '100%', [styles.height]: h })}\n />\n ))}\n </span>\n );\n }\n\n return (\n <span\n ref={ref}\n aria-hidden\n className={cx(styles.skeleton({ variant }), className)}\n style={{ ...assignInlineVars({ [styles.width]: w, [styles.height]: h }), ...style }}\n {...rest}\n />\n );\n});\n","export function cx(...classNames: Array<string | false | null | undefined>): string {\n return classNames.filter(Boolean).join(' ');\n}\n","import 'src/styles/animations.css.ts.vanilla.css?source=#H4sIAAAAAAAAA51T3WqDMBS-9ynOjaBsGYlV27qbPsbuRhqjDdVEYjZahu8-plspGjPtXeB8-f4453Dm10LTmrfwTkrdHLcYw5cHUGhV9w8A1VAmzDUD_OoBdB6AUeMRGUadd5gSkllCMk-IHYSR2yGA0VS2hdJ1NjwraniAEuw_A4p3fggtoxUP8Ms-Df_J5CZL8I2MhA7HG3cFq0VmG3sku9VxvKLjB9tc0FsydmFL9xYwWrGAYOzDE0RxcwmnPuwfsUs8dYhrZX56xTkvnVq_uE16h7SKbcdiR8rOpVYfMkeNaoURSmbQZ5yeoRWL7sFWzd1Y88RFeTK2Q_-bfFIdIKRpLi6IMqZ0LpRETEnDpUEDyhVzPye5gthiy5WSLtuhvq6lexMlN2z3DVsBqiZDBQAA';\nimport 'src/theme/contract.css.ts.vanilla.css?source=#H4sIAAAAAAAAA-2XTW_jNhCG7_kVA-fSAJIhUh-2tKcuii3QIotiA_ROSaQjrCQapLRxttj_XlCSZVseKkb30BwUODaseTnkvPwwn0RJ2cA_dwCum3LdyBdX71nGXS8B78P1Y5JAsD8gAZrAFg34CRCKRoIESIRGwgSoh0aiBCg-gG0CPt4P8RII8HSEJhDg4yZRAtG0K8XyotVuLWs-9WcI6eraoSFU5dceDaFyh7g0xA4l4tMQE21ZJhDHcTyJC1k3rmBVUb66mtU6gdVfije8zpnK4W-mCpaWfOXA6akDLtvvS-7qV93wyoGPZVF_fWTZU_f9k6wbB_qY2xYOrH41anj6DX6XzXORwWcuVw6sPstGwhOrNfz5ZeWA6d3VXBXCOsBK1jKB1R-8-ahYUWt4lLVJ1RZdqJsPB54-mcfuF75rS6YceOR1KR0YFdfpdfGduwedgLfehIpXFoWZMm-9nZOYqSP2cDd9a0JnMnSzuJ5T0F4yo_B7xexIA6Ohto5eeLF7blzVe2i2hWcVVTwv2iqBcEajeVWksswTiGZUvWKDKcqi5u5zL2vMe-_SvLCWqmK9WfNCxUt24Gbu1ptLad4q1hSydgXTpk_qVRoXHDujnlWiS_liTp-p4Ltb1Dk_uPIbVyV7TYD4Ew-OikrmXUE-weO5kvtcvtRGQnFJI_tKplN6isuyKfYJkOmEZrKUyk13bs4Fa8smgXvR_eEq3aZNyY1oK2LBcFHVNsb3e0GEL0JcU9TfuNImEyU0pDGuGs1Tu5T94jkwvNZB-IC0EOdlWPOK0wiDOPTCDa4ZS42iiG8iXHQqw-6aVDlXZwPLOaccyzcodaNkvUvgnuVpmOZ2oZBZqxO499NICCzhXhUVU6-ulmVhyqVhGPD0LaH7bGxP4J7kAc23M_JM1o3qVp69_DH10U7ORXCDchxFnvJI8Bl9ww_N3Ghr3jaKlaMLfuCzANsFF8Kxf-s6OspvcWFMPe4f2964VI6j4DHP-FzmwQVrcTmrd2ZxDSbkGY0otmbOdWPvaUwyktnVt1hwTDw6wAUV9C3hOARhds2MfKjfOlLdZhnXejSAhFvPxzbXhfC0F6Io9IMZ-S0WjKlHDzyRi7ms072QiYxj59VRf9wLtuJemKqLeje6IMKYe9iJcCE89R9vNh62ao7yMxes-2ZMPbogRIoeS5fKs6Ug_Axz4agfXIhp4HmXJ4d-Zrn5MLc-IPsD0P3h6sfFix6wRuYe6Jn7fXdbv261RVuZ66HXt_CxZoQ-fLj7cbduvrJdk3oLji04tuDYgmMLji04tuDYgmMLji04tuDYgmMLjv0fOEYWHFtwbMGxBccWHFtwrMcFEpEtwX4dz3FsDqKOsGO9Gl7g2BzYWXEsehvHrHnPcMxKRuc4ZqUicRtVTnHMyoD_FcciL_bQSt8jjnUzGcYOEEIcoEHU3UkibDpxPkMT0NkEx-uZnwYC83MKbG-yz6VPb6LVLZfUKbB1ZdIwdOD0dn3nm0M4PAMJ5jIMTlkrem9Q19foxw5E2_7ftphQyMOa40vpEvpExkKG-fMeoa8r0g8cIPHGgTiwe4RDINYeN2kChduIC4bN909CoUhTQTGXfgYK-6UQhA6QcGsOF7tNOCWiCXCfJtgostwPLhfJTdgYzlCjN7SzHiPOHFrimXuynM9MusR2-jR-_PgXHH1O55cpAAA';\nimport 'src/components/Skeleton/Skeleton.css.ts.vanilla.css?source=Ll8xczhkYXRkMiB7CiAgZGlzcGxheTogYmxvY2s7CiAgZmxleC1zaHJpbms6IDA7CiAgd2lkdGg6IHZhcigtLV8xczhkYXRkMCk7CiAgaGVpZ2h0OiB2YXIoLS1fMXM4ZGF0ZDEpOwogIGJhY2tncm91bmQtY29sb3I6IHZhcigtLWJlc3Rvdy1jb2xvci1uZXV0cmFsLXN1YnRsZSk7CiAgYmFja2dyb3VuZC1pbWFnZTogbGluZWFyLWdyYWRpZW50KDkwZGVnLCB0cmFuc3BhcmVudCAyNSUsIHZhcigtLWJlc3Rvdy1jb2xvci1uZXV0cmFsLXN1YnRsZS1ob3ZlcikgNTAlLCB0cmFuc3BhcmVudCA3NSUpOwogIGJhY2tncm91bmQtc2l6ZTogMjAwJSAxMDAlOwogIGFuaW1hdGlvbjogXzFncnBiNzA3IDEuNnMgZWFzZS1pbi1vdXQgaW5maW5pdGU7Cn0KLl8xczhkYXRkMyB7CiAgYm9yZGVyLXJhZGl1czogdmFyKC0tYmVzdG93LXJhZGl1cy1zbSk7Cn0KLl8xczhkYXRkNCB7CiAgYm9yZGVyLXJhZGl1czogdmFyKC0tYmVzdG93LXJhZGl1cy1tZCk7Cn0KLl8xczhkYXRkNSB7CiAgYm9yZGVyLXJhZGl1czogdmFyKC0tYmVzdG93LXJhZGl1cy1mdWxsKTsKfQouXzFzOGRhdGQ2IHsKICBkaXNwbGF5OiBmbGV4OwogIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgZ2FwOiB2YXIoLS1iZXN0b3ctc3BhY2UtMik7CiAgd2lkdGg6IHZhcigtLV8xczhkYXRkMCk7Cn0KQG1lZGlhIChwcmVmZXJzLXJlZHVjZWQtbW90aW9uOiByZWR1Y2UpIHsKICAuXzFzOGRhdGQyIHsKICAgIGFuaW1hdGlvbjogbm9uZTsKICAgIGJhY2tncm91bmQtaW1hZ2U6IG5vbmU7CiAgfQp9';\nimport { createRuntimeFn as _7a468 } from '@vanilla-extract/recipes/createRuntimeFn';\nexport var height = 'var(--_1s8datd1)';\nexport var lines = '_1s8datd6';\nexport var skeleton = /* @__PURE__ */ _7a468({defaultClassName:'_1s8datd2',variantClassNames:{variant:{text:'_1s8datd3',rect:'_1s8datd4',circle:'_1s8datd5'}},defaultVariants:{variant:'text'},compoundVariants:[]});\nexport var width = 'var(--_1s8datd0)';","import 'src/styles/visuallyHidden.css.ts.vanilla.css?source=Ll8xa2R0YncxMCB7CiAgcG9zaXRpb246IGFic29sdXRlOwogIHdpZHRoOiAxcHg7CiAgaGVpZ2h0OiAxcHg7CiAgcGFkZGluZzogMDsKICBtYXJnaW46IC0xcHg7CiAgb3ZlcmZsb3c6IGhpZGRlbjsKICBjbGlwOiByZWN0KDAsIDAsIDAsIDApOwogIHdoaXRlLXNwYWNlOiBub3dyYXA7CiAgYm9yZGVyOiAwOwp9';\nexport var visuallyHidden = '_1kdtbw10';"],"mappings":";AACO,IAAI,aAAa,EAAC,OAAM,WAAU,MAAK,UAAS;AAChD,IAAI,OAAO,EAAC,OAAM,EAAC,KAAI,yBAAwB,KAAI,yBAAwB,KAAI,yBAAwB,KAAI,yBAAwB,KAAI,yBAAwB,KAAI,yBAAwB,KAAI,yBAAwB,KAAI,yBAAwB,MAAK,0BAAyB,MAAK,0BAAyB,MAAK,yBAAwB,GAAE,QAAO,EAAC,MAAK,6BAA4B,IAAG,2BAA0B,IAAG,2BAA0B,IAAG,2BAA0B,IAAG,2BAA0B,MAAK,4BAA2B,GAAE,MAAK,EAAC,QAAO,EAAC,MAAK,kCAAiC,MAAK,iCAAgC,GAAE,MAAK,EAAC,IAAG,8BAA6B,IAAG,8BAA6B,IAAG,8BAA6B,IAAG,8BAA6B,IAAG,8BAA6B,OAAM,+BAA8B,OAAM,+BAA8B,OAAM,8BAA6B,GAAE,QAAO,EAAC,SAAQ,qCAAoC,QAAO,oCAAmC,UAAS,sCAAqC,MAAK,iCAAgC,GAAE,YAAW,EAAC,OAAM,wCAAuC,QAAO,yCAAwC,SAAQ,yCAAwC,EAAC,GAAE,UAAS,EAAC,MAAK,+BAA8B,QAAO,iCAAgC,MAAK,8BAA6B,GAAE,QAAO,EAAC,SAAQ,iCAAgC,OAAM,+BAA8B,UAAS,kCAAiC,OAAM,+BAA8B,SAAQ,gCAA+B,GAAE,OAAM,EAAC,IAAG,EAAC,WAAU,kCAAiC,QAAO,iCAAgC,OAAM,gCAA+B,SAAQ,kCAAiC,SAAQ,iCAAgC,GAAE,IAAG,EAAC,WAAU,kCAAiC,OAAM,gCAA+B,QAAO,iCAAgC,SAAQ,iCAAgC,GAAE,QAAO,EAAC,WAAU,sCAAqC,QAAO,qCAAoC,OAAM,oCAAmC,OAAM,oCAAmC,YAAW,yCAAwC,GAAE,SAAQ,EAAC,OAAM,qCAAoC,YAAW,2CAA0C,UAAS,wCAAuC,QAAO,sCAAqC,aAAY,4CAA2C,MAAK,mCAAkC,GAAE,SAAQ,EAAC,OAAM,qCAAoC,YAAW,2CAA0C,UAAS,wCAAuC,QAAO,sCAAqC,aAAY,4CAA2C,MAAK,mCAAkC,GAAE,QAAO,EAAC,OAAM,oCAAmC,YAAW,0CAAyC,UAAS,uCAAsC,QAAO,qCAAoC,aAAY,2CAA0C,MAAK,kCAAiC,GAAE,SAAQ,EAAC,OAAM,qCAAoC,YAAW,2CAA0C,UAAS,wCAAuC,QAAO,sCAAqC,aAAY,4CAA2C,MAAK,mCAAkC,GAAE,SAAQ,EAAC,OAAM,qCAAoC,YAAW,2CAA0C,UAAS,wCAAuC,QAAO,sCAAqC,aAAY,4CAA2C,MAAK,mCAAkC,EAAC,GAAE,QAAO,EAAC,IAAG,2BAA0B,IAAG,2BAA0B,IAAG,0BAAyB,EAAC;;;ACFnoH,SAAS,wBAAwB;AAsBjC,SAAS,UAAU,QAAc,QAAgC;AAC/D,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,MAAY,EAAE,GAAG,OAAO;AAC9B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,OAAW;AACzB,UAAM,OAAO,IAAI,GAAG;AACpB,QAAI,GAAG,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,WAAW,UAAU,MAAM,KAAK,IAAI;AAAA,EAC9F;AACA,SAAO;AACT;AAYO,SAAS,eAAe,QAAmC;AAChE,MAAI,QAAc,CAAC;AACnB,MAAI,OAAa,CAAC;AAClB,aAAW,SAAS,QAAQ;AAC1B,YAAQ,UAAU,UAAU,OAAO,MAAM,MAAc,GAAG,MAAM,KAAa;AAC7E,WAAO,UAAU,UAAU,MAAM,MAAM,MAAc,GAAG,MAAM,IAAY;AAAA,EAC5E;AACA,SAAO,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AACtC;AAGO,SAAS,YAAY,GAA4B,GAAqD;AAC3G,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,YAAY,GAAG,CAAC;AACzB;AAGA,SAAS,QAAQ,SAAe,UAAgB,KAA6B;AAC3E,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,UAAM,SAAS,SAAS,GAAG;AAC3B,QAAI,WAAW,OAAW;AAC1B,QAAI,OAAO,WAAW,YAAY,OAAO,UAAU,SAAU,KAAI,MAAM,IAAI;AAAA,aAClE,OAAO,WAAW,YAAY,OAAO,UAAU,SAAU,SAAQ,OAAO,QAAQ,GAAG;AAAA,EAC9F;AACA,SAAO;AACT;AAGO,SAAS,aAAa,OAAgC,MAAqD;AAChH,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,QAAQ,MAAM,IAAI,GAAW,MAAyB,CAAC,CAAC;AACpE,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,iBAAiB,GAAG,IAAI;AAC3D;;;ACxDA,IAAM,qBAAqB;AAwB3B,IAAI,UAA+C;AAEnD,SAAS,oBAAkD;AACzD,QAAM,IAAI;AACV,QAAM,WAAW,EAAE,OAAO,YAAY,EAAE,MAAM;AAC9C,MAAI,SAAU,QAAO,QAAQ,QAAQ,QAAQ;AAC7C,wBAAY,IAAI,QAA6B,CAAC,SAAS,WAAW;AAChE,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,MAAM;AACb,WAAO,QAAQ;AACf,WAAO,SAAS,MAAM;AACpB,YAAM,OAAO,EAAE,OAAO,YAAY,EAAE,MAAM;AAC1C,UAAI,KAAM,SAAQ,IAAI;AAAA,UACjB,QAAO,IAAI,MAAM,yFAAmB,CAAC;AAAA,IAC5C;AACA,WAAO,UAAU,MAAM;AACrB,gBAAU;AACV,aAAO,IAAI,MAAM,qGAAqB,CAAC;AAAA,IACzC;AACA,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACD,SAAO;AACT;AAGO,SAAS,oBACd,MACA;AACA,MAAI,KAAK,qBAAqB,IAAK,QAAO;AAC1C,QAAM,QAAkB,CAAC;AAEzB,MAAI,KAAK,SAAS,SAAS,KAAK,KAAK,KAAK,EAAG,OAAM,KAAK,KAAK,KAAK;AAClE,MAAI,KAAK,gBAAgB,KAAK,cAAc,IAAK,OAAM,KAAK,KAAK,YAAY;AAC7E,SAAO,MAAM,SAAS,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM;AACnD;AAMO,IAAM,wBAAyC,OAAO,WAAW,aAAa;AACnF,QAAM,WAAW,MAAM,kBAAkB;AACzC,MAAI,SAAS;AAAA,IACX,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,YAAY,CAAC,SAAS;AACpB,YAAM,UACJ,KAAK,qBAAqB,MAAM,KAAK,cAAc,KAAK,gBAAgB,KAAK,oBAAoB;AACnG,eAAS,EAAE,UAAU,KAAK,UAAU,SAAS,cAAc,oBAAoB,IAAI,EAAE,CAAC;AAAA,IACxF;AAAA,EACF,CAAC,EAAE,MAAM,SAAS;AACpB;;;AC7FA,IAAM,aAAa,CAAC,UAAkB,MAAM,QAAQ,OAAO,EAAE;AAGtD,SAAS,qBAAqB,QAAwB;AAC3D,MAAI,OAAO,WAAW,IAAI,EAAG,QAAO;AACpC,MAAI,UAAU,KAAK,MAAM,EAAG,QAAO;AACnC,SAAO;AACT;AAQO,SAAS,kBAAkB,OAAuB;AACvD,QAAM,IAAI,WAAW,KAAK,EAAE,MAAM,GAAG,qBAAqB,WAAW,KAAK,CAAC,CAAC;AAC5E,MAAI,EAAE,WAAW,IAAI,GAAG;AACtB,QAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,QAAI,EAAE,UAAU,EAAG,QAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxD,QAAI,EAAE,UAAU,EAAG,QAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACzE,WAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAAA,EACxD;AACA,MAAI,UAAU,KAAK,CAAC,GAAG;AACrB,WAAO,EAAE,UAAU,IAAI,IAAI,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAAA,EAC3D;AACA,MAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,MAAI,EAAE,UAAU,EAAG,QAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxD,MAAI,EAAE,UAAU,GAAI,QAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1E,SAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxD;AAMO,SAAS,qBAAqB,OAAuB;AAC1D,QAAM,IAAI,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE;AACvC,MAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,MAAI,EAAE,UAAU,EAAG,QAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxD,SAAO,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxD;AAMO,SAAS,sBAAsB,OAAwB;AAC5D,QAAM,IAAI,WAAW,KAAK;AAC1B,MAAI,EAAE,WAAW,GAAI,QAAO;AAC5B,QAAM,UAAU,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAC1C,QAAM,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,MAAM;AAChC,MAAI,MAAM,QAAQ,OAAO,CAAC,KAAK,GAAG,MAAM,MAAM,IAAI,OAAO,CAAC,GAAI,CAAC;AAC/D,SAAO,KAAK,MAAO,OAAO,CAAC,IAAK,IAAK,EAAE;AACvC,UAAQ,KAAM,MAAM,MAAO,OAAO,OAAO,CAAC;AAC5C;AAQO,SAAS,mBAAmB,QAAgB,OAAO,UAAa;AACrE,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC;AACrC,MAAI,MAAM,EAAG,QAAO,IAAI,IAAI;AAC5B,QAAM,QAAQ;AAAA,IACZ,EAAE,OAAO,MAAM,OAAO,SAAI;AAAA,IAC1B,EAAE,OAAO,KAAK,OAAO,SAAI;AAAA,IACzB,EAAE,OAAO,KAAK,OAAO,SAAI;AAAA,IACzB,EAAE,OAAO,GAAG,OAAO,GAAG;AAAA,EACxB;AACA,MAAI,OAAO;AACX,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,OAAO;AACrB,UAAM,IAAI,KAAK,MAAM,OAAO,EAAE,KAAK;AACnC,YAAQ,EAAE;AACV,QAAI,IAAI,EAAG,OAAM,KAAK,GAAG,EAAE,eAAe,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE;AAAA,EAChE;AACA,SAAO,GAAG,SAAS,IAAI,MAAM,EAAE,GAAG,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI;AAC1D;;;AChFA,IAAM,MAAM,CAAC,MAAc,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AAG7C,SAAS,UAAU,MAAoB;AAC5C,SAAO,GAAG,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC;AACjF;AAGO,SAAS,aAAa,OAA+C;AAC1E,QAAM,IAAI,4BAA4B,KAAK,SAAS,EAAE;AACtD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;AAC5D,QAAM,OAAO,IAAI,KAAK,GAAG,KAAK,GAAG,CAAC;AAClC,SAAO,KAAK,YAAY,MAAM,KAAK,KAAK,SAAS,MAAM,KAAK,KAAK,KAAK,QAAQ,MAAM,IAAI,OAAO;AACjG;AAEO,SAAS,QAAQ,MAAY,MAAoB;AACtD,SAAO,IAAI,KAAK,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,KAAK,QAAQ,IAAI,IAAI;AAC5E;AAGO,SAAS,UAAU,MAAY,QAAsB;AAC1D,QAAM,SAAS,IAAI,KAAK,KAAK,YAAY,GAAG,KAAK,SAAS,IAAI,QAAQ,CAAC;AACvE,QAAM,UAAU,IAAI,KAAK,OAAO,YAAY,GAAG,OAAO,SAAS,IAAI,GAAG,CAAC,EAAE,QAAQ;AACjF,SAAO,IAAI,KAAK,OAAO,YAAY,GAAG,OAAO,SAAS,GAAG,KAAK,IAAI,KAAK,QAAQ,GAAG,OAAO,CAAC;AAC5F;AAEO,SAAS,UAAU,GAAgB,GAAyB;AACjE,SAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,UAAU,CAAC,MAAM,UAAU,CAAC;AACnD;AAEO,SAAS,YAAY,GAAS,GAAkB;AACrD,SAAO,EAAE,YAAY,MAAM,EAAE,YAAY,KAAK,EAAE,SAAS,MAAM,EAAE,SAAS;AAC5E;AAGO,SAAS,YAAY,GAAS,GAAiB;AACpD,QAAM,IAAI,UAAU,CAAC;AACrB,QAAM,IAAI,UAAU,CAAC;AACrB,SAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI;AAClC;AAGO,SAAS,iBAAiB,MAAc,OAAyB;AACtE,QAAM,QAAQ,IAAI,KAAK,MAAM,OAAO,CAAC;AACrC,QAAM,QAAQ,QAAQ,OAAO,CAAC,MAAM,OAAO,CAAC;AAC5C,SAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,SAAS,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAACA,IAAG,QAAQ,QAAQ,OAAO,OAAO,IAAI,GAAG,CAAC,CAAC;AACrH;AAEO,IAAM,kBAAkB,CAAC,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,QAAG;AAM1D,SAAS,iBAAiB,MAAY,cAAc,MAAc;AACvE,QAAM,OAAO,GAAG,KAAK,YAAY,CAAC,UAAK,KAAK,SAAS,IAAI,CAAC,UAAK,KAAK,QAAQ,CAAC;AAC7E,SAAO,cAAc,GAAG,IAAI,IAAI,gBAAgB,KAAK,OAAO,CAAC,CAAC,iBAAO;AACvE;;;AC3CO,IAAM,mBAAsC;AAAA,EACjD,EAAE,OAAO,gBAAM,MAAM,EAAE;AAAA,EACvB,EAAE,OAAO,iBAAO,MAAM,EAAE;AAAA,EACxB,EAAE,OAAO,iBAAO,QAAQ,EAAE;AAAA,EAC1B,EAAE,OAAO,iBAAO,QAAQ,EAAE;AAC5B;AAGO,SAAS,YAAY,QAAyB,OAA0B;AAC7E,QAAM,MAAM,aAAa,KAAK;AAC9B,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,IAAI,KAAK,GAAG;AACtC,QAAM,QAAQ,OAAO,WAAW,SAAY,UAAU,KAAK,CAAC,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,OAAO,QAAQ,EAAE;AAC7G,SAAO,EAAE,OAAO,UAAU,KAAK,GAAG,KAAK,MAAM;AAC/C;;;AC9BA,IAAM,YACJ;AAGF,SAAS,SAAS,SAA0C;AAC1D,SACE,mBAAmB,eACnB,QAAQ,QAAQ,SAAS,KACzB,QAAQ,YAAY,KACpB,CAAC,QAAQ,QAAQ,yCAAyC;AAE9D;AAEA,SAAS,cAAc,WAA+C;AACpE,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,UAAU,MAAM,KAAK,UAAU,iBAAiB,iCAAiC,CAAC,EAAE,KAAK,QAAQ;AACvG,SAAO,WAAW,MAAM,KAAK,UAAU,iBAAiB,SAAS,CAAC,EAAE,KAAK,QAAQ,KAAK;AACxF;AAOO,SAAS,WAAW,QAA0D;AACnF,QAAM,UAAU,OAAO,WAAW,WAAW,SAAS,eAAe,MAAM,IAAK,UAAU;AAC1F,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,eACH,SAAS,OAAO,IAAI,UAAU,SAC/B,cAAc,OAAO,KACrB,cAAc,QAAQ,QAAQ,+CAA+C,CAAC;AAChF,MAAI,CAAC,YAAa,QAAO;AACzB,cAAY,MAAM;AAClB,SAAO,SAAS,kBAAkB;AACpC;;;ACnCA,SAAS,oBAAAC,yBAAwB;AACjC,SAAS,kBAAuC;;;ACDzC,SAAS,MAAM,YAA8D;AAClF,SAAO,WAAW,OAAO,OAAO,EAAE,KAAK,GAAG;AAC5C;;;ACCA,SAAS,mBAAmB,cAAc;AACnC,IAAI,SAAS;AACb,IAAI,QAAQ;AACZ,IAAI,WAA2B,uBAAO,EAAC,kBAAiB,aAAY,mBAAkB,EAAC,SAAQ,EAAC,MAAK,aAAY,MAAK,aAAY,QAAO,YAAW,EAAC,GAAE,iBAAgB,EAAC,SAAQ,OAAM,GAAE,kBAAiB,CAAC,EAAC,CAAC;AAC5M,IAAI,QAAQ;;;AFgCT;AAvBV,IAAM,QAAQ,CAAC,UAA4B,OAAO,UAAU,WAAW,GAAG,KAAK,OAAO;AAM/E,IAAM,WAA2B,2BAA2C,SAASC,UAC1F,EAAE,UAAU,QAAQ,OAAAC,QAAO,QAAAC,SAAQ,OAAAC,SAAQ,GAAG,WAAW,OAAO,GAAG,KAAK,GACxE,KACA;AACA,QAAM,IAAI,MAAMF,WAAU,YAAY,WAAW,KAAK,OAAO;AAC7D,QAAM,IAAI,MAAMC,YAAW,YAAY,WAAW,IAAI,YAAY,SAAS,MAAM,MAAM;AAEvF,MAAI,YAAY,UAAUC,SAAQ,GAAG;AACnC,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,eAAW;AAAA,QACX,WAAW,GAAU,OAAO,SAAS;AAAA,QACrC,OAAO,EAAE,GAAGC,kBAAiB,EAAE,CAAQ,KAAK,GAAG,EAAE,CAAC,GAAG,GAAG,MAAM;AAAA,QAC7D,GAAG;AAAA,QAEH,gBAAM,KAAK,EAAE,QAAQD,OAAM,GAAG,CAAC,GAAG,MACjC;AAAA,UAAC;AAAA;AAAA,YAEC,WAAkB,SAAS,EAAE,QAAQ,CAAC;AAAA,YACtC,OAAOC,kBAAiB,EAAE,CAAQ,KAAK,GAAG,MAAMD,SAAQ,IAAI,QAAQ,QAAQ,CAAQ,MAAM,GAAG,EAAE,CAAC;AAAA;AAAA,UAF3F;AAAA,QAGP,CACD;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,eAAW;AAAA,MACX,WAAW,GAAU,SAAS,EAAE,QAAQ,CAAC,GAAG,SAAS;AAAA,MACrD,OAAO,EAAE,GAAGC,kBAAiB,EAAE,CAAQ,KAAK,GAAG,GAAG,CAAQ,MAAM,GAAG,EAAE,CAAC,GAAG,GAAG,MAAM;AAAA,MACjF,GAAG;AAAA;AAAA,EACN;AAEJ,CAAC;;;AGzDM,IAAI,iBAAiB;","names":["_","assignInlineVars","Skeleton","width","height","lines","assignInlineVars"]}