@alikhalilll/ui 1.0.0 → 1.1.1

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.
Files changed (43) hide show
  1. package/README.md +155 -187
  2. package/dist/chunks/ADrawerContent.vue_vue_type_script_setup_true_lang-BivIZvV1.mjs +108 -0
  3. package/dist/chunks/ADrawerContent.vue_vue_type_script_setup_true_lang-BivIZvV1.mjs.map +1 -0
  4. package/dist/chunks/AInput.vue_vue_type_script_setup_true_lang-BJUP7ePq.mjs +81 -0
  5. package/dist/chunks/AInput.vue_vue_type_script_setup_true_lang-BJUP7ePq.mjs.map +1 -0
  6. package/dist/chunks/APopoverContent.vue_vue_type_script_setup_true_lang-o1XhV9DM.mjs +132 -0
  7. package/dist/chunks/APopoverContent.vue_vue_type_script_setup_true_lang-o1XhV9DM.mjs.map +1 -0
  8. package/dist/chunks/AResponsivePopoverContent.vue_vue_type_script_setup_true_lang-BYEb5UBL.mjs +92 -0
  9. package/dist/chunks/AResponsivePopoverContent.vue_vue_type_script_setup_true_lang-BYEb5UBL.mjs.map +1 -0
  10. package/dist/chunks/ATellInput.vue_vue_type_script_setup_true_lang-Clc-B8SW.mjs +1353 -0
  11. package/dist/chunks/ATellInput.vue_vue_type_script_setup_true_lang-Clc-B8SW.mjs.map +1 -0
  12. package/dist/chunks/cn-B6yFEsav.mjs +9 -0
  13. package/dist/chunks/cn-B6yFEsav.mjs.map +1 -0
  14. package/dist/chunks/sizes-B_9MfLkz.mjs +34 -0
  15. package/dist/chunks/sizes-B_9MfLkz.mjs.map +1 -0
  16. package/dist/drawer.d.ts +116 -0
  17. package/dist/drawer.mjs +8 -0
  18. package/dist/drawer.mjs.map +1 -0
  19. package/dist/index.d.ts +84 -57
  20. package/dist/index.mjs +35 -1516
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/input.d.ts +65 -0
  23. package/dist/input.mjs +5 -0
  24. package/dist/input.mjs.map +1 -0
  25. package/dist/popover.d.ts +117 -0
  26. package/dist/popover.mjs +8 -0
  27. package/dist/popover.mjs.map +1 -0
  28. package/dist/responsive-popover.d.ts +124 -0
  29. package/dist/responsive-popover.mjs +7 -0
  30. package/dist/responsive-popover.mjs.map +1 -0
  31. package/dist/tell-input.d.ts +637 -0
  32. package/dist/tell-input.mjs +13 -0
  33. package/dist/tell-input.mjs.map +1 -0
  34. package/dist/utils.d.ts +29 -0
  35. package/dist/utils.mjs +12 -0
  36. package/dist/utils.mjs.map +1 -0
  37. package/entries/drawer/README.md +39 -0
  38. package/entries/input/README.md +42 -0
  39. package/entries/popover/README.md +37 -0
  40. package/entries/responsive-popover/README.md +47 -0
  41. package/entries/tell-input/README.md +52 -0
  42. package/package.json +30 -4
  43. /package/dist/{styles.css → assets/styles.css} +0 -0
package/README.md CHANGED
@@ -1,13 +1,29 @@
1
1
  # @alikhalilll/ui
2
2
 
3
- Headless, [shadcn-vue](https://www.shadcn-vue.com/) style component library for Vue 3 (and Nuxt). Built on [reka-ui](https://reka-ui.com) and [vaul-vue](https://github.com/unovue/vaul-vue), fully typed, scalable, with sensible defaults you can override on every level.
3
+ [![npm](https://img.shields.io/npm/v/@alikhalilll/ui.svg?color=444)](https://www.npmjs.com/package/@alikhalilll/ui)
4
+ [![downloads](https://img.shields.io/npm/dm/@alikhalilll/ui.svg?color=444)](https://www.npmjs.com/package/@alikhalilll/ui)
5
+ [![size](https://img.shields.io/bundlephobia/minzip/@alikhalilll/ui?label=minzip&color=444)](https://bundlephobia.com/package/@alikhalilll/ui)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-444.svg)](./LICENSE)
4
7
 
5
- The first component shipped is **`ATellInput`**a phone-number input that:
8
+ Headless, shadcn-vue style Vue 3 component library every component lives behind its own subpath import so consumers only ship the components they actually use. Built on [reka-ui](https://reka-ui.com) and [vaul-vue](https://github.com/unovue/vaul-vue), themed via prefixed CSS variables, fully typed.
6
9
 
7
- - Detects the user's country automatically (IP geolocation → timezone → `navigator.language` → fallback).
8
- - Validates and formats input via [`libphonenumber-js`](https://www.npmjs.com/package/libphonenumber-js).
9
- - Renders a **popover on desktop, vaul-vue drawer on mobile** for the country picker.
10
- - Exposes every sub-primitive so you can compose your own variant if the default doesn't fit.
10
+ 📚 **Live docs:** <https://alikhalilll.github.io/ali-nuxt-toolkit/ui>
11
+ 📦 **npm:** <https://www.npmjs.com/package/@alikhalilll/ui>
12
+ 🐙 **Source:** <https://github.com/alikhalilll/ali-nuxt-toolkit/tree/master/packages/ui>
13
+
14
+ ---
15
+
16
+ ## Components
17
+
18
+ | Component | Subpath | Per-component README | Live docs |
19
+ | -------------------- | ------------------------------------ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
20
+ | `ATellInput` | `@alikhalilll/ui/tell-input` | [./entries/tell-input/README.md](./entries/tell-input/README.md) | [/ui/tell-input](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/tell-input) |
21
+ | `AInput` | `@alikhalilll/ui/input` | [./entries/input/README.md](./entries/input/README.md) | [/ui/input](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/input) |
22
+ | `APopover` | `@alikhalilll/ui/popover` | [./entries/popover/README.md](./entries/popover/README.md) | [/ui/popover](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/popover) |
23
+ | `ADrawer` | `@alikhalilll/ui/drawer` | [./entries/drawer/README.md](./entries/drawer/README.md) | [/ui/drawer](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/drawer) |
24
+ | `AResponsivePopover` | `@alikhalilll/ui/responsive-popover` | [./entries/responsive-popover/README.md](./entries/responsive-popover/README.md) | [/ui/responsive-popover](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/responsive-popover) |
25
+
26
+ Each README ships inside the npm tarball at `node_modules/@alikhalilll/ui/entries/<name>/README.md` and is rendered on the file browser at <https://www.npmjs.com/package/@alikhalilll/ui?activeTab=code>.
11
27
 
12
28
  ## Install
13
29
 
@@ -15,113 +31,164 @@ The first component shipped is **`ATellInput`** — a phone-number input that:
15
31
  pnpm add @alikhalilll/ui
16
32
  ```
17
33
 
18
- Peer dependency: `vue ^3.5.0`. The library bundles `reka-ui`, `vaul-vue`, `libphonenumber-js`, `lucide-vue-next`, `@vueuse/core`, `class-variance-authority`, `clsx`, and `tailwind-merge`.
34
+ Peer dependency: `vue ^3.5.0`. Bundled deps: `reka-ui`, `vaul-vue`, `libphonenumber-js`, `lucide-vue-next`, `@vueuse/core`, `class-variance-authority`, `clsx`, `tailwind-merge`.
35
+
36
+ ### Subpath imports (recommended)
37
+
38
+ ```ts
39
+ // Only the tell-input chunk ships into the bundle.
40
+ import { ATellInput } from '@alikhalilll/ui/tell-input';
41
+
42
+ // Main entry — bundlers still tree-shake unused exports via `sideEffects`.
43
+ import { ATellInput, APopover } from '@alikhalilll/ui';
44
+ ```
45
+
46
+ Available subpaths: `/tell-input`, `/input`, `/popover`, `/drawer`, `/responsive-popover`, `/utils`.
19
47
 
20
48
  ## Setup
21
49
 
22
- The components are styled with Tailwind utility classes (`bg-popover`, `text-destructive`, etc.) that resolve to CSS variables shipped in `@alikhalilll/ui/styles.css`. You need to:
50
+ Components style themselves with Tailwind utility classes (`bg-popover`, `text-muted-foreground`, ) resolving to CSS variables shipped at `@alikhalilll/ui/styles.css`. Three steps:
23
51
 
24
- 1. **Import the variables** somewhere global:
52
+ ### 1. Import the tokens
25
53
 
26
- ```ts
27
- // In a Nuxt config or your main entry
28
- import '@alikhalilll/ui/styles.css';
29
- ```
54
+ **Nuxt 3 / 4:**
30
55
 
31
- 2. **Expose the variables to Tailwind**. For Tailwind v4 add an `@theme inline` block to your global stylesheet:
56
+ ```ts
57
+ // nuxt.config.ts
58
+ export default defineNuxtConfig({
59
+ css: ['@alikhalilll/ui/styles.css'],
60
+ });
61
+ ```
32
62
 
33
- ```css
34
- @import 'tailwindcss';
35
- @import '@alikhalilll/ui/styles.css';
63
+ **Vue + Vite (no Nuxt):**
36
64
 
37
- @theme inline {
38
- --color-background: hsl(var(--ak-ui-background));
39
- --color-foreground: hsl(var(--ak-ui-foreground));
40
- --color-popover: hsl(var(--ak-ui-popover));
41
- --color-popover-foreground: hsl(var(--ak-ui-popover-foreground));
42
- --color-primary: hsl(var(--ak-ui-primary));
43
- --color-primary-foreground: hsl(var(--ak-ui-primary-foreground));
44
- --color-muted: hsl(var(--ak-ui-muted));
45
- --color-muted-foreground: hsl(var(--ak-ui-muted-foreground));
46
- --color-accent: hsl(var(--ak-ui-accent));
47
- --color-accent-foreground: hsl(var(--ak-ui-accent-foreground));
48
- --color-destructive: hsl(var(--ak-ui-destructive));
49
- --color-destructive-foreground: hsl(var(--ak-ui-destructive-foreground));
50
- --color-border: hsl(var(--ak-ui-border));
51
- --color-input: hsl(var(--ak-ui-input));
52
- --color-ring: hsl(var(--ak-ui-ring));
53
- }
54
- ```
65
+ ```ts
66
+ // main.ts
67
+ import { createApp } from 'vue';
68
+ import App from './App.vue';
69
+ import '@alikhalilll/ui/styles.css';
70
+
71
+ createApp(App).mount('#app');
72
+ ```
55
73
 
56
- For Tailwind v3, add the same colors to `theme.extend.colors` in `tailwind.config.ts`.
74
+ Every variable is prefixed `--ak-ui-` guaranteed not to collide with your own CSS.
75
+
76
+ ### 2. Map to Tailwind v4
77
+
78
+ ```css
79
+ @import 'tailwindcss';
80
+ @import '@alikhalilll/ui/styles.css';
81
+ @source '../node_modules/@alikhalilll/ui/dist/index.mjs';
82
+
83
+ @theme inline {
84
+ --color-background: hsl(var(--ak-ui-background));
85
+ --color-foreground: hsl(var(--ak-ui-foreground));
86
+ --color-popover: hsl(var(--ak-ui-popover));
87
+ --color-popover-foreground: hsl(var(--ak-ui-popover-foreground));
88
+ --color-muted: hsl(var(--ak-ui-muted));
89
+ --color-muted-foreground: hsl(var(--ak-ui-muted-foreground));
90
+ --color-accent: hsl(var(--ak-ui-accent));
91
+ --color-accent-foreground: hsl(var(--ak-ui-accent-foreground));
92
+ --color-destructive: hsl(var(--ak-ui-destructive));
93
+ --color-destructive-foreground: hsl(var(--ak-ui-destructive-foreground));
94
+ --color-border: hsl(var(--ak-ui-border));
95
+ --color-input: hsl(var(--ak-ui-input));
96
+ --color-ring: hsl(var(--ak-ui-ring));
97
+ }
98
+ ```
57
99
 
58
- 3. **Toggle dark mode** by adding `.dark` to a parent element (typically `<html>`).
100
+ `@source` tells Tailwind v4 to scan the lib's compiled templates — it skips `node_modules` by default. Inside a pnpm workspace, point at the source so HMR works:
59
101
 
60
- The lib ships **both** `:root, .light { … }` and `.dark { … }` blocks. You can lock the theme:
102
+ ```css
103
+ @source '../../packages/ui/index.ts';
104
+ @source '../../packages/ui/entries/**/*.{vue,ts}';
105
+ @source '../../packages/ui/utils/**/*.ts';
106
+ ```
61
107
 
62
- ```ts
63
- // Locked dark
64
- export default defineNuxtConfig({
65
- app: { head: { htmlAttrs: { class: 'dark' } } },
66
- });
67
- ```
108
+ ### 3. Dark mode
68
109
 
69
- Or run a tri-state Light / Dark / System switcher that follows `prefers-color-scheme`. The docs site under [`apps/docs/composables/useColorMode.ts`](https://github.com/alikhalilll/ali-nuxt-toolkit/blob/master/apps/docs/composables/useColorMode.ts) ships a complete working pattern (persisted preference, OS-change listener, pre-paint inline script to avoid flash of wrong theme) that you can copy as-is.
110
+ The lib ships both `.light` and `.dark` blocks. Toggle the class on `<html>` portaled popovers and drawers inherit via the cascade.
70
111
 
71
- ## Usage
112
+ **Nuxt 3 / 4 — locked dark:**
113
+
114
+ ```ts
115
+ // nuxt.config.ts
116
+ export default defineNuxtConfig({
117
+ app: { head: { htmlAttrs: { class: 'dark' } } },
118
+ });
119
+ ```
120
+
121
+ **Vue + Vite — locked dark:**
122
+
123
+ ```html
124
+ <!-- index.html -->
125
+ <html class="dark">
126
+ ...
127
+ </html>
128
+ ```
129
+
130
+ For Light / Dark / System (persisted, OS-aware, no flash of wrong theme), see [`apps/docs/composables/useColorMode.ts`](https://github.com/alikhalilll/ali-nuxt-toolkit/blob/master/apps/docs/composables/useColorMode.ts) — the pattern is framework-agnostic.
131
+
132
+ ## Quick start
72
133
 
73
134
  ```vue
74
135
  <script setup lang="ts">
75
136
  import { ref } from 'vue';
76
- import { ATellInput } from '@alikhalilll/ui';
137
+ import { ATellInput } from '@alikhalilll/ui/tell-input';
77
138
 
78
139
  const phone = ref('');
79
- const country = ref('');
140
+ const country = ref<number | null>(null); // dial number, e.g. 20 for Egypt
80
141
  </script>
81
142
 
82
143
  <template>
83
- <ATellInput
84
- v-model:phone="phone"
85
- v-model:country="country"
86
- default-country="SA"
87
- show-validation
88
- />
144
+ <ATellInput v-model:phone="phone" v-model:country="country" show-validation />
89
145
  </template>
90
146
  ```
91
147
 
92
- ### Props
93
-
94
- | Prop | Type | Default | Description |
95
- | ------------------- | ------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------ |
96
- | `v-model:phone` | `string` | `''` | Digits-only national number (no leading `+` or dial code). |
97
- | `v-model:country` | `string` | `''` | ISO 3166-1 alpha-2 code, e.g. `"EG"`. |
98
- | `placeholder` | `string` | `'Phone number'` | Falls back to the country's example number when empty. |
99
- | `disabled` | `boolean` | `false` | |
100
- | `loading` | `boolean` | `false` | Disables interaction. |
101
- | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Controls input height (32 / 36 / 40 px). |
102
- | `allowedDialCodes` | `string[]` | _all_ | Whitelist of dial-digit codes (no `+`). Countries outside the list are shown but disabled. |
103
- | `showValidation` | `boolean` | `false` | Renders an error message below the input when invalid. |
104
- | `detectCountry` | `'auto' \| 'locale' \| 'none'` | `'auto'` | Country auto-detect strategy. |
105
- | `defaultCountry` | `string` | `'US'` | Fallback ISO2 when detection fails or is disabled. |
106
- | `ipEndpoint` | `string` | `'https://ipapi.co/json/'` | Override the geolocation endpoint. Must return JSON with `country_code` or `country`. |
107
- | `searchPlaceholder` | `string` | `'Search by country or code…'` | Country picker search input placeholder. |
108
- | `emptyText` | `string` | `'No countries found.'` | Shown when the search yields no results. |
109
- | `loadingText` | `string` | `'Loading…'` | Shown while the country list is loading. |
110
- | `errorMessages` | `Partial<Record<PhoneValidationReason, string>>` | English defaults | Override the validation error labels. |
111
- | `class` | `string \| any[] \| Record<string, boolean>` | — | Merged into the outer wrapper via `tailwind-merge`. |
112
-
113
- ### Exposed (via template ref)
148
+ The picker stays hidden until the user types or pastes something that matches a known dial code, then slides in pre-filled and `phone` is normalised to the national significant number (`01066105963` → `1066105963`, `+447911123456` → `7911123456`).
149
+
150
+ Full props, slots, theming recipes, and live demos → [tell-input docs](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/tell-input) or [./entries/tell-input/README.md](./entries/tell-input/README.md).
151
+
152
+ ## Nuxt integration
153
+
154
+ `@alikhalilll/ui` is a plain Vue 3 library works in Nuxt 3 / 4 without a module wrapper. Full guide (auto-imports, SSR behaviour, source globs) on the [docs site](https://alikhalilll.github.io/ali-nuxt-toolkit/ui#nuxt-integration).
114
155
 
115
156
  ```ts
116
- const ref = ref<InstanceType<typeof ATellInput>>();
117
- ref.value.validation; // PhoneValidationResult — computed, reactive
118
- ref.value.required; // PhoneRequiredInfo | null — example, length range, format hint
119
- ref.value.selectedDialCode; // '+20' | null
157
+ // nuxt.config.ts optional auto-import
158
+ export default defineNuxtConfig({
159
+ components: [{ path: '@alikhalilll/ui', pathPrefix: false, global: true }],
160
+ });
120
161
  ```
121
162
 
122
- ## Compose your own component
163
+ ## Theming
123
164
 
124
- Every primitive, composable, and CVA helper is re-exported from the package root, so you can build a custom variant without forking the library.
165
+ Override any `--ak-ui-*` variable globally, per wrapper, or inline:
166
+
167
+ ```css
168
+ .tenant-acme {
169
+ --ak-ui-popover: 220 70% 8%;
170
+ --ak-ui-accent: 220 50% 30%;
171
+ --ak-ui-ring: 220 100% 65%;
172
+ }
173
+ ```
174
+
175
+ Values are HSL **triplets** — no `hsl(…)` wrapper. Full token list + recipes (brand-only, day/night, multi-tenant, server-driven, state-specific) → [theming guide](https://alikhalilll.github.io/ali-nuxt-toolkit/ui#theming).
176
+
177
+ ## Size scale
178
+
179
+ | Token | Height | Tailwind |
180
+ | ----- | ------------------- | ---------- |
181
+ | `xs` | 28 px | `h-7` |
182
+ | `sm` | 36 px | `h-9` |
183
+ | `md` | **43 px (default)** | `h-[43px]` |
184
+ | `lg` | 52 px | `h-[52px]` |
185
+ | `xl` | 60 px | `h-[60px]` |
186
+
187
+ `controlHeight`, `controlPaddingX`, `controlTextSize`, `controlHeightPx`, `SIZES`, `DEFAULT_SIZE`, and the `Size` type are exported so you can build size-aware components in lockstep with the library.
188
+
189
+ ## Compose your own variant
190
+
191
+ Every primitive, composable, and helper is re-exported — fork-free composition:
125
192
 
126
193
  ```ts
127
194
  import {
@@ -149,102 +216,13 @@ import {
149
216
  ACountryFlag,
150
217
  aTellInputVariants,
151
218
  DEFAULT_ERROR_MESSAGES,
219
+ defaultFlagUrl,
152
220
 
153
- // Utilities
221
+ // Helpers
154
222
  cn,
155
223
  } from '@alikhalilll/ui';
156
-
157
- // Build your own tel input from the same composables
158
- const { validate, getCountries, searchCountries } = usePhoneValidation();
159
- const detected = await detectCountry({ strategy: 'auto' });
160
224
  ```
161
225
 
162
- Or pull the country select on its own (any list-of-countries UI):
163
-
164
- ```vue
165
- <ACountrySelect v-model:selected="iso2" />
166
- ```
167
-
168
- Or build any responsive popover (popover-on-desktop, drawer-on-mobile):
169
-
170
- ```vue
171
- <AResponsivePopover v-model:open="open">
172
- <AResponsivePopoverTrigger as-child>
173
- <button>Open</button>
174
- </AResponsivePopoverTrigger>
175
- <AResponsivePopoverContent>
176
- <p>Content</p>
177
- </AResponsivePopoverContent>
178
- </AResponsivePopover>
179
- ```
180
-
181
- ## Full customization
182
-
183
- `ATellInput` exposes a deep customization surface across three vectors. Every override is opt-in — defaults are sensible, so a vanilla `<ATellInput />` works out of the box.
184
-
185
- **Slots (replace any rendered region):**
186
-
187
- ```
188
- prefix · suffix · trigger · chevron · flag · search · search-icon ·
189
- loading · empty · group-header · item · item-check · valid-icon ·
190
- error-icon · hint · error
191
- ```
192
-
193
- **Data-override props:**
194
-
195
- ```ts
196
- flagUrl?: (iso2, width) => string // swap flagcdn.com for any source
197
- countries?: CountryOption[] // bypass REST Countries; ship your own list
198
- searcher?: (query, country) => boolean // custom search (fuzzy/starts-with/locale-aware)
199
- detector?: (opts) => Promise<string | null> // custom country detection (e.g. server-driven)
200
- errorMessages?: Partial<Record<PhoneValidationReason, string>> // i18n
201
- kbdOpen?: string | null // override the '⌘K' hint
202
- kbdClose?: string | null // override the 'Esc' hint
203
- ```
204
-
205
- **Class-override props (every region):** `class`, `fieldClass`, `inputClass`, `contentClass`, `popoverClass`, `drawerClass`, `hintClass`, `errorClass`. All merged via `tailwind-merge`, so you only specify the bits you want to change.
206
-
207
- **Example — fully bespoke trigger, custom country list, custom searcher, custom detector:**
208
-
209
- ```vue
210
- <script setup lang="ts">
211
- import { ATellInput, defaultFlagUrl, type CountryOption } from '@alikhalilll/ui';
212
-
213
- const countries: CountryOption[] = [
214
- /* … your curated list … */
215
- ];
216
-
217
- const flagUrl = (iso: string) => `/flags/${iso.toLowerCase()}.svg`;
218
- const searcher = (q: string, c: CountryOption) =>
219
- c.raw_data.name.toLowerCase().startsWith(q.toLowerCase());
220
-
221
- async function detector() {
222
- // Pretend your backend tells you the user's country from the request
223
- const { country } = await $fetch('/api/locale');
224
- return country;
225
- }
226
- </script>
227
-
228
- <template>
229
- <ATellInput
230
- v-model:phone="phone"
231
- v-model:country="country"
232
- :countries="countries"
233
- :flag-url="flagUrl"
234
- :searcher="searcher"
235
- :detector="detector"
236
- >
237
- <template #trigger="{ selectedCountry, open }">
238
- <button :data-open="open">
239
- {{ selectedCountry?.raw_data.iso2 ?? '??' }}
240
- </button>
241
- </template>
242
- </ATellInput>
243
- </template>
244
- ```
245
-
246
- `AInput` also exposes `#prefix` and `#suffix` slots — when either is filled the component switches to a wrapped layout so the bordered field carries the focus ring while the slot content sits inside the border. See the [docs site](https://alikhalilll.github.io/ali-nuxt-toolkit/ui/input) for the full prop tables and a live demo gallery.
247
-
248
226
  ## Exported types
249
227
 
250
228
  ```ts
@@ -265,21 +243,11 @@ import type {
265
243
  } from '@alikhalilll/ui';
266
244
  ```
267
245
 
268
- The `defaultFlagUrl(iso2, width)` builder is also exported — handy for composing a custom builder on top of the default:
269
-
270
- ```ts
271
- import { defaultFlagUrl } from '@alikhalilll/ui';
272
- const hiRes = (iso: string) => defaultFlagUrl(iso, 80);
273
- ```
274
-
275
- ## Country detection chain
276
-
277
- 1. **IP geolocation** — fetch `ipEndpoint` (default `https://ipapi.co/json/`), aborted after `timeoutMs` (default 2 s). Result cached in `sessionStorage` so re-mounts skip the network call.
278
- 2. **Timezone** — `Intl.DateTimeFormat().resolvedOptions().timeZone` against a built-in IANA-zone-to-ISO2 map (~70 zones, covers most populated cities).
279
- 3. **`navigator.language`** — extracts the region from tags like `en-US`, `ar-EG`, `pt-BR`.
280
- 4. **`defaultCountry`** — final fallback (`'US'` if you don't set one).
246
+ ## Notes
281
247
 
282
- Pass `detect-country="locale"` to skip the IP step (no network), or `detect-country="none"` to use `defaultCountry` immediately.
248
+ - **Country detection** runs only in `onMounted` the input renders immediately with `defaultCountry` (or empty); the detected ISO2 patches in on hydration.
249
+ - The default tel-input behaviour is **picker hidden until detected** (`detect-from-input`). Pass `:detect-from-input="false"` to opt out, or `default-country="20"` / `default-country="EG"` to pre-fill the picker at mount.
250
+ - Import `@alikhalilll/ui/styles.css` **before** your overrides so your overrides win the cascade.
283
251
 
284
252
  ## License
285
253
 
@@ -0,0 +1,108 @@
1
+ import { defineComponent as d, openBlock as c, createBlock as p, unref as e, mergeProps as i, withCtx as l, renderSlot as m, createVNode as y, createElementVNode as b } from "vue";
2
+ import { DrawerRoot as h, DrawerTrigger as x, DrawerOverlay as O, DrawerPortal as D, DrawerContent as v } from "vaul-vue";
3
+ import { useForwardPropsEmits as B, useForwardProps as w } from "reka-ui";
4
+ import { reactiveOmit as _ } from "@vueuse/core";
5
+ import { c as g } from "./cn-B6yFEsav.mjs";
6
+ const F = /* @__PURE__ */ d({
7
+ __name: "ADrawer",
8
+ props: {
9
+ activeSnapPoint: {},
10
+ closeThreshold: {},
11
+ shouldScaleBackground: { type: Boolean, default: !0 },
12
+ setBackgroundColorOnScale: { type: Boolean },
13
+ scrollLockTimeout: {},
14
+ fixed: { type: Boolean },
15
+ dismissible: { type: Boolean },
16
+ modal: { type: Boolean },
17
+ open: { type: Boolean },
18
+ defaultOpen: { type: Boolean },
19
+ nested: { type: Boolean },
20
+ direction: {},
21
+ noBodyStyles: { type: Boolean },
22
+ handleOnly: { type: Boolean },
23
+ preventScrollRestoration: { type: Boolean },
24
+ snapPoints: {},
25
+ fadeFromIndex: {}
26
+ },
27
+ emits: ["drag", "release", "close", "update:open", "update:activeSnapPoint", "animationEnd"],
28
+ setup(t, { emit: o }) {
29
+ const s = B(t, o);
30
+ return (n, u) => (c(), p(e(h), i({ "data-slot": "drawer" }, e(s)), {
31
+ default: l(() => [
32
+ m(n.$slots, "default")
33
+ ]),
34
+ _: 3
35
+ }, 16));
36
+ }
37
+ }), E = /* @__PURE__ */ d({
38
+ __name: "ADrawerTrigger",
39
+ props: {
40
+ asChild: { type: Boolean },
41
+ as: {}
42
+ },
43
+ setup(t) {
44
+ const a = w(t);
45
+ return (r, s) => (c(), p(e(x), i({ "data-slot": "drawer-trigger" }, e(a)), {
46
+ default: l(() => [
47
+ m(r.$slots, "default")
48
+ ]),
49
+ _: 3
50
+ }, 16));
51
+ }
52
+ }), A = /* @__PURE__ */ d({
53
+ __name: "ADrawerOverlay",
54
+ props: {
55
+ forceMount: { type: Boolean },
56
+ asChild: { type: Boolean },
57
+ as: {},
58
+ class: { type: [Boolean, null, String, Object, Array] }
59
+ },
60
+ setup(t) {
61
+ const o = t, a = _(o, "class"), r = w(a);
62
+ return (s, n) => (c(), p(e(O), i({ "data-slot": "drawer-overlay" }, e(r), {
63
+ class: e(g)(
64
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-40 bg-black/60 backdrop-blur-sm",
65
+ o.class
66
+ )
67
+ }), null, 16, ["class"]));
68
+ }
69
+ }), T = /* @__PURE__ */ d({
70
+ inheritAttrs: !1,
71
+ __name: "ADrawerContent",
72
+ props: {
73
+ forceMount: { type: Boolean },
74
+ disableOutsidePointerEvents: { type: Boolean },
75
+ asChild: { type: Boolean },
76
+ as: {},
77
+ class: { type: [Boolean, null, String, Object, Array] }
78
+ },
79
+ emits: ["escapeKeyDown", "pointerDownOutside", "focusOutside", "interactOutside", "openAutoFocus", "closeAutoFocus"],
80
+ setup(t, { emit: o }) {
81
+ const a = t, r = o, s = _(a, "class"), n = B(s, r);
82
+ return (u, f) => (c(), p(e(D), null, {
83
+ default: l(() => [
84
+ y(A),
85
+ y(e(v), i({ "data-slot": "drawer-content" }, { ...u.$attrs, ...e(n) }, {
86
+ class: e(g)(
87
+ "bg-background fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border outline-none",
88
+ a.class
89
+ )
90
+ }), {
91
+ default: l(() => [
92
+ f[0] || (f[0] = b("div", { class: "bg-muted mx-auto mt-4 h-2 w-[100px] rounded-full" }, null, -1)),
93
+ m(u.$slots, "default")
94
+ ]),
95
+ _: 3
96
+ }, 16, ["class"])
97
+ ]),
98
+ _: 3
99
+ }));
100
+ }
101
+ });
102
+ export {
103
+ F as _,
104
+ T as a,
105
+ A as b,
106
+ E as c
107
+ };
108
+ //# sourceMappingURL=ADrawerContent.vue_vue_type_script_setup_true_lang-BivIZvV1.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ADrawerContent.vue_vue_type_script_setup_true_lang-BivIZvV1.mjs","sources":["../../entries/drawer/components/ADrawer.vue","../../entries/drawer/components/ADrawerTrigger.vue","../../entries/drawer/components/ADrawerOverlay.vue","../../entries/drawer/components/ADrawerContent.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DrawerRoot, type DrawerRootEmits, type DrawerRootProps } from 'vaul-vue';\nimport { useForwardPropsEmits } from 'reka-ui';\n\nconst props = withDefaults(defineProps<DrawerRootProps>(), {\n shouldScaleBackground: true,\n});\nconst emits = defineEmits<DrawerRootEmits>();\nconst forwarded = useForwardPropsEmits(props, emits);\n</script>\n\n<template>\n <DrawerRoot data-slot=\"drawer\" v-bind=\"forwarded\">\n <slot />\n </DrawerRoot>\n</template>\n","<script setup lang=\"ts\">\nimport { DrawerTrigger, type DrawerTriggerProps } from 'vaul-vue';\nimport { useForwardProps } from 'reka-ui';\n\nconst props = defineProps<DrawerTriggerProps>();\nconst forwarded = useForwardProps(props);\n</script>\n\n<template>\n <DrawerTrigger data-slot=\"drawer-trigger\" v-bind=\"forwarded\">\n <slot />\n </DrawerTrigger>\n</template>\n","<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue';\nimport { DrawerOverlay } from 'vaul-vue';\nimport type { DialogOverlayProps } from 'reka-ui';\nimport { reactiveOmit } from '@vueuse/core';\nimport { useForwardProps } from 'reka-ui';\nimport { cn } from '@/utils';\n\nconst props = defineProps<DialogOverlayProps & { class?: HTMLAttributes['class'] }>();\nconst delegated = reactiveOmit(props, 'class');\nconst forwarded = useForwardProps(delegated);\n</script>\n\n<template>\n <DrawerOverlay\n data-slot=\"drawer-overlay\"\n v-bind=\"forwarded\"\n :class=\"\n cn(\n 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-40 bg-black/60 backdrop-blur-sm',\n props.class\n )\n \"\n />\n</template>\n","<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue';\nimport { DrawerContent, DrawerPortal } from 'vaul-vue';\nimport type { DialogContentEmits, DialogContentProps } from 'reka-ui';\nimport { reactiveOmit } from '@vueuse/core';\nimport { useForwardPropsEmits } from 'reka-ui';\nimport { cn } from '@/utils';\nimport ADrawerOverlay from './ADrawerOverlay.vue';\n\ndefineOptions({ inheritAttrs: false });\n\nconst props = defineProps<DialogContentProps & { class?: HTMLAttributes['class'] }>();\nconst emits = defineEmits<DialogContentEmits>();\nconst delegated = reactiveOmit(props, 'class');\nconst forwarded = useForwardPropsEmits(delegated, emits);\n</script>\n\n<template>\n <DrawerPortal>\n <ADrawerOverlay />\n <DrawerContent\n data-slot=\"drawer-content\"\n v-bind=\"{ ...$attrs, ...forwarded }\"\n :class=\"\n cn(\n 'bg-background fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border outline-none',\n props.class\n )\n \"\n >\n <div class=\"bg-muted mx-auto mt-4 h-2 w-[100px] rounded-full\" />\n <slot />\n </DrawerContent>\n </DrawerPortal>\n</template>\n"],"names":["forwarded","useForwardPropsEmits","__props","__emit","_openBlock","_createBlock","_unref","_mergeProps","_renderSlot","_ctx","useForwardProps","props","delegated","reactiveOmit","cn","emits","DrawerPortal","_createVNode","ADrawerOverlay","DrawerContent","$attrs","_createElementVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,UAAMA,IAAYC,EAJJC,GAGAC,CACqC;sBAIjDC,EAAA,GAAAC,EAEaC,MAFbC,EAEa,EAFD,aAAU,SAAA,GAAiBD,EAAAN,CAAA,CAAS,GAAA;AAAA,iBAC9C,MAAQ;AAAA,QAARQ,EAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;;;;;;;;ACRZ,UAAMT,IAAYU,EADJR,CACyB;sBAIrCE,EAAA,GAAAC,EAEgBC,MAFhBC,EAEgB,EAFD,aAAU,iBAAA,GAAyBD,EAAAN,CAAA,CAAS,GAAA;AAAA,iBACzD,MAAQ;AAAA,QAARQ,EAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;;;;;;;;;;ACFZ,UAAME,IAAQT,GACRU,IAAYC,EAAaF,GAAO,OAAO,GACvCX,IAAYU,EAAgBE,CAAS;sBAIzCR,EAAA,GAAAC,EASEC,MATFC,EASE,EARA,aAAU,iBAAA,GACFD,EAAAN,CAAA,GAAS;AAAA,MAChB,OAAcM,EAAAQ,CAAA;AAAA;QAA+LH,EAAM;AAAA,MAAA;AAAA;;;;;;;;;;;;;;ACNxN,UAAMA,IAAQT,GACRa,IAAQZ,GACRS,IAAYC,EAAaF,GAAO,OAAO,GACvCX,IAAYC,EAAqBW,GAAWG,CAAK;2BAIrDV,EAeeC,EAAAU,CAAA,GAAA,MAAA;AAAA,iBAdb,MAAkB;AAAA,QAAlBC,EAAkBC,CAAA;AAAA,QAClBD,EAYgBX,EAAAa,CAAA,GAZhBZ,EAYgB,EAXd,aAAU,iBAAA,GAAgB,EAAA,GACba,EAAAA,QAAM,GAAKd,EAAAN,CAAA,KAAS;AAAA,UAChC,OAAgBM,EAAAQ,CAAA;AAAA;YAAuIH,EAAM;AAAA,UAAA;AAAA;qBAO9J,MAAgE;AAAA,4BAAhEU,EAAgE,OAAA,EAA3D,OAAM,mDAAA,GAAkD,MAAA,EAAA;AAAA,YAC7Db,EAAQC,EAAA,QAAA,SAAA;AAAA,UAAA;;;;;;;;"}
@@ -0,0 +1,81 @@
1
+ import { defineComponent as S, useSlots as A, computed as l, openBlock as i, createElementBlock as r, normalizeClass as o, unref as t, renderSlot as x, createCommentVNode as v, withDirectives as y, createElementVNode as B, isRef as z, vModelText as h } from "vue";
2
+ import { useVModel as _ } from "@vueuse/core";
3
+ import { c as n } from "./cn-B6yFEsav.mjs";
4
+ import { D as j, c as E, b as O, d as T } from "./sizes-B_9MfLkz.mjs";
5
+ const D = ["data-size"], P = ["data-size"], N = /* @__PURE__ */ S({
6
+ __name: "AInput",
7
+ props: {
8
+ defaultValue: {},
9
+ modelValue: {},
10
+ class: { type: [Boolean, null, String, Object, Array] },
11
+ inputClass: { type: [Boolean, null, String, Object, Array] },
12
+ prefixClass: { type: [Boolean, null, String, Object, Array] },
13
+ suffixClass: { type: [Boolean, null, String, Object, Array] },
14
+ size: { default: j }
15
+ },
16
+ emits: ["update:modelValue"],
17
+ setup(k, { emit: V }) {
18
+ const e = k, w = V, d = A(), f = l(() => !!d.prefix), c = l(() => !!d.suffix), C = l(() => f.value || c.value), s = _(e, "modelValue", w, {
19
+ passive: !0,
20
+ defaultValue: e.defaultValue
21
+ }), p = l(() => E[e.size]), m = l(() => O[e.size]), g = l(() => T[e.size]);
22
+ return (b, a) => C.value ? (i(), r("div", {
23
+ key: 0,
24
+ "data-size": e.size,
25
+ class: o(
26
+ t(n)(
27
+ "border-input bg-background ring-offset-background focus-within:ring-ring inline-flex w-full items-center rounded-md border shadow-sm transition-colors focus-within:ring-1",
28
+ p.value,
29
+ m.value,
30
+ g.value,
31
+ e.class
32
+ )
33
+ )
34
+ }, [
35
+ f.value ? (i(), r("span", {
36
+ key: 0,
37
+ class: o(t(n)("text-muted-foreground flex shrink-0 items-center pr-2", e.prefixClass))
38
+ }, [
39
+ x(b.$slots, "prefix")
40
+ ], 2)) : v("", !0),
41
+ y(B("input", {
42
+ "onUpdate:modelValue": a[0] || (a[0] = (u) => z(s) ? s.value = u : null),
43
+ "data-slot": "input",
44
+ class: o(
45
+ t(n)(
46
+ "placeholder:text-muted-foreground h-full min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-50",
47
+ e.inputClass
48
+ )
49
+ )
50
+ }, null, 2), [
51
+ [h, t(s)]
52
+ ]),
53
+ c.value ? (i(), r("span", {
54
+ key: 1,
55
+ class: o(t(n)("text-muted-foreground flex shrink-0 items-center pl-2", e.suffixClass))
56
+ }, [
57
+ x(b.$slots, "suffix")
58
+ ], 2)) : v("", !0)
59
+ ], 10, D)) : y((i(), r("input", {
60
+ key: 1,
61
+ "onUpdate:modelValue": a[1] || (a[1] = (u) => z(s) ? s.value = u : null),
62
+ "data-slot": "input",
63
+ "data-size": e.size,
64
+ class: o(
65
+ t(n)(
66
+ "border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border py-1 shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
67
+ p.value,
68
+ m.value,
69
+ g.value,
70
+ e.class
71
+ )
72
+ )
73
+ }, null, 10, P)), [
74
+ [h, t(s)]
75
+ ]);
76
+ }
77
+ });
78
+ export {
79
+ N as _
80
+ };
81
+ //# sourceMappingURL=AInput.vue_vue_type_script_setup_true_lang-BJUP7ePq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AInput.vue_vue_type_script_setup_true_lang-BJUP7ePq.mjs","sources":["../../entries/input/components/AInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue';\nimport { computed, useSlots } from 'vue';\nimport { useVModel } from '@vueuse/core';\nimport { cn } from '@/utils';\nimport { controlHeight, controlPaddingX, controlTextSize, DEFAULT_SIZE, type Size } from '@/utils';\n\nconst props = withDefaults(\n defineProps<{\n defaultValue?: string | number;\n modelValue?: string | number;\n class?: HTMLAttributes['class'];\n /** Classes for the inner &lt;input&gt; element (useful when prefix/suffix are present). */\n inputClass?: HTMLAttributes['class'];\n /** Classes for the prefix wrapper. */\n prefixClass?: HTMLAttributes['class'];\n /** Classes for the suffix wrapper. */\n suffixClass?: HTMLAttributes['class'];\n size?: Size;\n }>(),\n { size: DEFAULT_SIZE }\n);\n\nconst emits = defineEmits<{\n (e: 'update:modelValue', payload: string | number): void;\n}>();\n\ndefineSlots<{\n /** Content rendered inside the input's border, left of the field. */\n prefix?: () => unknown;\n /** Content rendered inside the input's border, right of the field. */\n suffix?: () => unknown;\n}>();\n\nconst slots = useSlots();\nconst hasPrefix = computed(() => !!slots.prefix);\nconst hasSuffix = computed(() => !!slots.suffix);\nconst hasAdornment = computed(() => hasPrefix.value || hasSuffix.value);\n\nconst modelValue = useVModel(props, 'modelValue', emits, {\n passive: true,\n defaultValue: props.defaultValue,\n});\n\nconst sizeHeight = computed(() => controlHeight[props.size]);\nconst sizePaddingX = computed(() => controlPaddingX[props.size]);\nconst sizeText = computed(() => controlTextSize[props.size]);\n</script>\n\n<template>\n <!--\n When prefix or suffix slots are filled we render a wrapper that owns the border,\n background and focus ring — so the visible \"input\" is the whole bar, not just the\n native element. Otherwise we render the plain native input directly so consumers\n can use AInput as a drop-in for <input>.\n -->\n <div\n v-if=\"hasAdornment\"\n :data-size=\"props.size\"\n :class=\"\n cn(\n 'border-input bg-background ring-offset-background focus-within:ring-ring inline-flex w-full items-center rounded-md border shadow-sm transition-colors focus-within:ring-1',\n sizeHeight,\n sizePaddingX,\n sizeText,\n props.class\n )\n \"\n >\n <span\n v-if=\"hasPrefix\"\n :class=\"cn('text-muted-foreground flex shrink-0 items-center pr-2', props.prefixClass)\"\n >\n <slot name=\"prefix\" />\n </span>\n\n <input\n v-model=\"modelValue\"\n data-slot=\"input\"\n :class=\"\n cn(\n 'placeholder:text-muted-foreground h-full min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-50',\n props.inputClass\n )\n \"\n />\n\n <span\n v-if=\"hasSuffix\"\n :class=\"cn('text-muted-foreground flex shrink-0 items-center pl-2', props.suffixClass)\"\n >\n <slot name=\"suffix\" />\n </span>\n </div>\n\n <input\n v-else\n v-model=\"modelValue\"\n data-slot=\"input\"\n :data-size=\"props.size\"\n :class=\"\n cn(\n 'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border py-1 shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n sizeHeight,\n sizePaddingX,\n sizeText,\n props.class\n )\n \"\n />\n</template>\n"],"names":["props","__props","emits","__emit","slots","useSlots","hasPrefix","computed","hasSuffix","hasAdornment","modelValue","useVModel","sizeHeight","controlHeight","sizePaddingX","controlPaddingX","sizeText","controlTextSize","_createElementBlock","_normalizeClass","_unref","cn","_renderSlot","_ctx","_createElementVNode","$event"],"mappings":";;;;;;;;;;;;;;;;;AAOA,UAAMA,IAAQC,GAgBRC,IAAQC,GAWRC,IAAQC,EAAA,GACRC,IAAYC,EAAS,MAAM,CAAC,CAACH,EAAM,MAAM,GACzCI,IAAYD,EAAS,MAAM,CAAC,CAACH,EAAM,MAAM,GACzCK,IAAeF,EAAS,MAAMD,EAAU,SAASE,EAAU,KAAK,GAEhEE,IAAaC,EAAUX,GAAO,cAAcE,GAAO;AAAA,MACvD,SAAS;AAAA,MACT,cAAcF,EAAM;AAAA,IAAA,CACrB,GAEKY,IAAaL,EAAS,MAAMM,EAAcb,EAAM,IAAI,CAAC,GACrDc,IAAeP,EAAS,MAAMQ,EAAgBf,EAAM,IAAI,CAAC,GACzDgB,IAAWT,EAAS,MAAMU,EAAgBjB,EAAM,IAAI,CAAC;qBAWjDS,EAAA,cADRS,EAqCM,OAAA;AAAA;MAnCH,aAAWlB,EAAM;AAAA,MACjB,OAAKmB;AAAAA,QAASC,EAAAC,CAAA;AAAA;UAAkMT,EAAA;AAAA,UAAoBE,EAAA;AAAA,UAAsBE,EAAA;AAAA,UAAkBhB,EAAM;AAAA,QAAA;AAAA;;MAW3QM,EAAA,cADRY,EAKO,QAAA;AAAA;QAHJ,OAAKC,EAAEC,EAAAC,CAAA,EAAE,yDAA0DrB,EAAM,WAAW,CAAA;AAAA,MAAA;QAErFsB,EAAsBC,EAAA,QAAA,QAAA;AAAA,MAAA;QAGxBC,EASE,SAAA;AAAA,6DARSd,EAAU,QAAAe,IAAA;AAAA,QACnB,aAAU;AAAA,QACT,OAAKN;AAAAA,UAAWC,EAAAC,CAAA;AAAA;YAA+JrB,EAAM;AAAA,UAAA;AAAA;;YAF7KoB,EAAAV,CAAA,CAAU;AAAA,MAAA;MAWbF,EAAA,cADRU,EAKO,QAAA;AAAA;QAHJ,OAAKC,EAAEC,EAAAC,CAAA,EAAE,yDAA0DrB,EAAM,WAAW,CAAA;AAAA,MAAA;QAErFsB,EAAsBC,EAAA,QAAA,QAAA;AAAA,MAAA;yBAI1BL,EAcE,SAAA;AAAA;2DAZSR,EAAU,QAAAe,IAAA;AAAA,MACnB,aAAU;AAAA,MACT,aAAWzB,EAAM;AAAA,MACjB,OAAKmB;AAAAA,QAASC,EAAAC,CAAA;AAAA;UAAkWT,EAAA;AAAA,UAAoBE,EAAA;AAAA,UAAsBE,EAAA;AAAA,UAAkBhB,EAAM;AAAA,QAAA;AAAA;;UAH1aoB,EAAAV,CAAA,CAAU;AAAA,IAAA;;;"}