@smurfox/proxy-ui 0.3.2 → 0.4.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 (34) hide show
  1. package/README.md +94 -5
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/Autocomplete.d.vue.ts +0 -0
  4. package/dist/runtime/components/Autocomplete.vue +270 -0
  5. package/dist/runtime/components/Autocomplete.vue.d.ts +0 -0
  6. package/dist/runtime/components/Avatar.d.vue.ts +1 -1
  7. package/dist/runtime/components/Avatar.vue +1 -1
  8. package/dist/runtime/components/Avatar.vue.d.ts +1 -1
  9. package/dist/runtime/components/Button.d.vue.ts +1 -1
  10. package/dist/runtime/components/Button.vue +10 -10
  11. package/dist/runtime/components/Button.vue.d.ts +1 -1
  12. package/dist/runtime/components/Card.vue +3 -3
  13. package/dist/runtime/components/Chip.d.vue.ts +1 -1
  14. package/dist/runtime/components/Chip.vue +8 -8
  15. package/dist/runtime/components/Chip.vue.d.ts +1 -1
  16. package/dist/runtime/components/Dropdown.d.vue.ts +11 -13
  17. package/dist/runtime/components/Dropdown.vue +65 -19
  18. package/dist/runtime/components/Dropdown.vue.d.ts +11 -13
  19. package/dist/runtime/components/Input.d.vue.ts +5 -3
  20. package/dist/runtime/components/Input.vue +9 -1
  21. package/dist/runtime/components/Input.vue.d.ts +5 -3
  22. package/dist/runtime/components/Lottie.d.vue.ts +1 -1
  23. package/dist/runtime/components/Lottie.vue.d.ts +1 -1
  24. package/dist/runtime/components/Select.d.vue.ts +0 -37
  25. package/dist/runtime/components/Select.vue +15 -5
  26. package/dist/runtime/components/Select.vue.d.ts +0 -37
  27. package/dist/runtime/components/Tabs.d.vue.ts +1 -1
  28. package/dist/runtime/components/Tabs.vue.d.ts +1 -1
  29. package/dist/runtime/components/TextArea.d.vue.ts +3 -3
  30. package/dist/runtime/components/TextArea.vue.d.ts +3 -3
  31. package/dist/runtime/composables/popoverGroup.d.ts +5 -0
  32. package/dist/runtime/composables/popoverGroup.js +12 -0
  33. package/dist/runtime/types/index.d.ts +17 -0
  34. package/package.json +1 -1
package/README.md CHANGED
@@ -34,12 +34,15 @@ export default defineNuxtConfig({
34
34
  });
35
35
  ```
36
36
 
37
- Add Tailwind to your CSS entry file:
37
+ Add Tailwind to your CSS entry file and register the library as a source so Tailwind picks up the component classes:
38
38
 
39
39
  ```css
40
40
  @import "tailwindcss";
41
+ @source "../node_modules/@smurfox/proxy-ui/dist";
41
42
  ```
42
43
 
44
+ > The `@source` path is relative to the CSS file. If your CSS lives deeper (e.g. `app/assets/css/main.css`), adjust the number of `../` segments so it resolves to your project root's `node_modules`. Without this directive, Tailwind v4 will not scan the library files and variants like `flat`/`outline` will render unstyled.
45
+
43
46
  That's it. All `PU` components are auto-imported and ready to use.
44
47
 
45
48
  ---
@@ -50,6 +53,7 @@ ProxyUI uses CSS variables for colors. Add them to your CSS file to customize:
50
53
 
51
54
  ```css
52
55
  @import "tailwindcss";
56
+ @source "../node_modules/@smurfox/proxy-ui/dist";
53
57
 
54
58
  @theme {
55
59
  --color-primary: #376fff;
@@ -164,6 +168,7 @@ A flexible input component with validation and state management.
164
168
  | `required` | `boolean` | `false` | Shows a red asterisk on the label. |
165
169
  | `error` | `string` | — | Error message to display. Changes styling to danger. |
166
170
  | `disabled` | `boolean` | `false` | Disables the input. |
171
+ | `focus` | `boolean` | `false` | Selects the whole text on focus (useful for pre-filled fields the user is likely to replace). |
167
172
 
168
173
  **Slots**
169
174
 
@@ -178,6 +183,9 @@ A flexible input component with validation and state management.
178
183
  <!-- Basic -->
179
184
  <PUInput label="Email" type="email" placeholder="you@example.com" />
180
185
 
186
+ <!-- Select all on focus (great for pre-filled codes the user wants to replace) -->
187
+ <PUInput v-model="orderCode" label="Order code" focus />
188
+
181
189
  <!-- With helper text -->
182
190
  <PUInput label="Password" type="password" description="At least 8 characters" />
183
191
 
@@ -260,7 +268,7 @@ A multi-line text input. Shares the look and feel of `PUInput`, with extra props
260
268
 
261
269
  ### PUSelect
262
270
 
263
- A custom select with an animated dropdown panel teleported to `body`. Dark-mode aware, supports `v-model`, and emits both `update:modelValue` and `change`.
271
+ A custom select with an animated dropdown panel teleported to `body`. Dark-mode aware, supports `v-model`, and emits both `update:modelValue` and `change`. Only one `PUSelect` can be open at a time across the page — opening a new one automatically closes any other open select.
264
272
 
265
273
  ```vue
266
274
  <PUSelect
@@ -320,6 +328,85 @@ A custom select with an animated dropdown panel teleported to `body`. Dark-mode
320
328
 
321
329
  ---
322
330
 
331
+ ### PUAutocomplete
332
+
333
+ A searchable variant of `PUSelect` — same look and dropdown behavior, but the trigger is a real `<input>` that filters the options as you type. Includes a clear (`×`) button that resets both the search text and the selected value. Like `PUSelect`, the dropdown is teleported to `body` and only one autocomplete can be open at a time.
334
+
335
+ ```vue
336
+ <PUAutocomplete
337
+ v-model="framework"
338
+ label="Framework"
339
+ placeholder="Search a framework"
340
+ :options="[
341
+ { label: 'Nuxt', value: 'nuxt' },
342
+ { label: 'Vue', value: 'vue' },
343
+ { label: 'React', value: 'react' },
344
+ ]"
345
+ />
346
+ ```
347
+
348
+ Filtering is case-insensitive and matches `label`. When the input text matches the currently-selected option's label exactly, the list is shown unfiltered so the user can browse all options again without clearing first. On close without selection, the input reverts to the selected option's label (or empty if nothing was selected).
349
+
350
+ **Props**
351
+
352
+ | Prop | Type | Default | Description |
353
+ | ------------- | ----------------------------------------------------------- | ------------------------- | ---------------------------------------------------- |
354
+ | `modelValue` | `string \| number \| null` | `null` | Selected value (v-model). |
355
+ | `options` | `{ label: string, value: string \| number }[]` | `[]` | Items shown in the dropdown. |
356
+ | `label` | `string` | — | Label displayed above the input. |
357
+ | `labelClass` | `string` | `'text-sm font-semibold'` | Custom classes for the label. |
358
+ | `placeholder` | `string` | `'Search...'` | Placeholder text shown when the input is empty. |
359
+ | `description` | `string` | — | Helper text displayed below. |
360
+ | `rounded` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| 'full'` | `'xl'` | Border radius. |
361
+ | `variant` | `'default' \| 'secondary'` | `'default'` | Visual style. |
362
+ | `required` | `boolean` | `false` | Shows a red asterisk on the label. |
363
+ | `error` | `string` | `''` | Error message to display. Changes styling to danger. |
364
+ | `disabled` | `boolean` | `false` | Disables the input. |
365
+
366
+ **Events**
367
+
368
+ | Event | Payload | Description |
369
+ | ------------------- | -------------------------------- | ------------------------------------------------------------------------ |
370
+ | `update:modelValue` | `string \| number \| null` | Emitted when an option is picked, or `null` when the clear button is used. |
371
+ | `change` | `string \| number \| null` | Emitted alongside `update:modelValue`. |
372
+ | `search` | `string` | Emitted on every keystroke with the current input text. Useful for remote search. |
373
+
374
+ **Examples**
375
+
376
+ ```vue
377
+ <!-- Basic -->
378
+ <PUAutocomplete v-model="country" :options="countries" label="Country" />
379
+
380
+ <!-- Secondary variant + required -->
381
+ <PUAutocomplete
382
+ v-model="city"
383
+ variant="secondary"
384
+ :options="cities"
385
+ label="City"
386
+ required
387
+ />
388
+
389
+ <!-- With error -->
390
+ <PUAutocomplete
391
+ v-model="status"
392
+ :options="statuses"
393
+ label="Status"
394
+ error="Pick a status"
395
+ required
396
+ />
397
+
398
+ <!-- Remote search via the `search` event -->
399
+ <PUAutocomplete
400
+ v-model="user"
401
+ :options="remoteResults"
402
+ label="User"
403
+ placeholder="Type to search users"
404
+ @search="onSearch"
405
+ />
406
+ ```
407
+
408
+ ---
409
+
323
410
  ### PUCard
324
411
 
325
412
  A flexible card component with customizable styling and borders.
@@ -337,7 +424,7 @@ A flexible card component with customizable styling and borders.
337
424
  | ------------- | ---------------------------- | ----------- | --------------------------------- |
338
425
  | `variant` | `'default' \| 'liquidGlass'` | `'default'` | Visual style of the card. |
339
426
  | `customClass` | `string` | — | Custom Tailwind classes to apply. |
340
- | `isBordered` | `boolean` | `false` | Adds a border to the card. |
427
+ | `isBordered` | `boolean` | `true` | Adds a border to the card. |
341
428
 
342
429
  **Examples**
343
430
 
@@ -569,7 +656,7 @@ const activeTab = ref("dashboard");
569
656
 
570
657
  ### PUDropdown
571
658
 
572
- A floating panel anchored to an activator element. Opens on click and closes on outside click. Provides a `closeDropdown` function (via `inject`) so child items can close the panel after acting.
659
+ A floating panel anchored to an activator element. The menu is teleported to `body` and positioned with `position: fixed` relative to the activator, so it escapes any `overflow` ancestor (scroll containers, modals, etc.). The menu re-positions itself on scroll and resize while open. Opens on click and closes on outside click; only one `PUDropdown` can be open at a time across the page. Provides a `closeDropdown` function (via `inject`) so child items can close the panel after acting.
573
660
 
574
661
  ```vue
575
662
  <PUDropdown>
@@ -782,10 +869,12 @@ import type {
782
869
  TableItem,
783
870
  TableRounded,
784
871
  TableItemsSize,
872
+ AutocompleteOption,
873
+ AutocompleteProps,
785
874
  } from "@smurfox/proxy-ui";
786
875
  ```
787
876
 
788
- > `PUTextArea`, `PUSelect`, and `PUDropdown` define their props inline and do not export dedicated `Props` types. They reuse `InputVariant` and `InputRounded` from the same package.
877
+ > `PUTextArea`, `PUSelect`, and `PUDropdown` define their props inline and do not export dedicated `Props` types. They reuse `InputVariant` and `InputRounded` from the same package. `PUAutocomplete` does export `AutocompleteProps` and `AutocompleteOption`.
789
878
 
790
879
  ---
791
880
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "proxy-ui",
3
3
  "configKey": "proxyUI",
4
- "version": "0.3.2",
4
+ "version": "0.4.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
File without changes
@@ -0,0 +1,270 @@
1
+ <template>
2
+ <div class="flex flex-col gap-1">
3
+ <div
4
+ v-if="label"
5
+ class="flex items-start gap-1"
6
+ >
7
+ <label
8
+ class="dark:text-white"
9
+ :class="[labelClass]"
10
+ >{{ label }} </label>
11
+ <span
12
+ v-if="props.required"
13
+ class="text-danger"
14
+ >*</span>
15
+ </div>
16
+
17
+ <div
18
+ ref="selectRef"
19
+ class="relative w-full text-left"
20
+ >
21
+ <input
22
+ ref="inputRef"
23
+ type="text"
24
+ :value="searchQuery"
25
+ :placeholder="props.placeholder"
26
+ :disabled="props.disabled"
27
+ class="w-full p-3 pr-10 text-sm transition-colors"
28
+ :class="[
29
+ roundedVariants[props.rounded],
30
+ props.error ? errorVariants[props.variant] : variants[props.variant],
31
+ props.disabled ? 'opacity-70 cursor-not-allowed' : 'cursor-text'
32
+ ]"
33
+ @input="onInput"
34
+ @focus="open"
35
+ @click.stop="open"
36
+ >
37
+
38
+ <button
39
+ v-if="searchQuery && !props.disabled"
40
+ type="button"
41
+ class="absolute right-3 top-1/2 -translate-y-1/2 flex items-center justify-center text-gray-400 hover:text-gray-600 dark:hover:text-white cursor-pointer"
42
+ @click.stop="clear"
43
+ >
44
+ <Icon
45
+ name="mdi:close-circle"
46
+ size="18"
47
+ />
48
+ </button>
49
+ <Icon
50
+ v-else
51
+ name="mdi:chevron-down"
52
+ class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 transition-transform duration-200 pointer-events-none"
53
+ :class="{ 'rotate-180': isOpen && !props.disabled }"
54
+ />
55
+
56
+ <Teleport
57
+ v-if="isOpen && !props.disabled"
58
+ to="body"
59
+ >
60
+ <AnimatePresence>
61
+ <motion.div
62
+ v-if="isOpen && !props.disabled"
63
+ :initial="{ scale: 0.96, opacity: 0, y: -6 }"
64
+ :animate="{ scale: 1, opacity: 1, y: 0 }"
65
+ :exit="{ scale: 0.96, opacity: 0, y: -6 }"
66
+ class="fixed p-2 max-h-56 overflow-y-auto origin-top border rounded-xl shadow-xl"
67
+ :class="
68
+ isDarkMode ? 'bg-[#212123] border-white/10 text-white' : 'bg-white border-gray-100'
69
+ "
70
+ :style="dropdownStyle"
71
+ @click.stop
72
+ >
73
+ <div
74
+ v-if="filteredOptions.length === 0"
75
+ class="px-4 py-2 text-sm text-center"
76
+ :class="isDarkMode ? 'text-white/60' : 'text-black/50'"
77
+ >
78
+ No available options
79
+ </div>
80
+ <template v-else>
81
+ <button
82
+ v-for="option in filteredOptions"
83
+ :key="String(option.value)"
84
+ type="button"
85
+ class="w-full flex items-center justify-between gap-3 px-3 py-2 mb-1 text-left cursor-pointer rounded-lg transition-colors"
86
+ :class="[
87
+ isDarkMode ? 'hover:bg-white/10' : 'hover:bg-gray-100',
88
+ option.value === props.modelValue ? selectedOptionClass : ''
89
+ ]"
90
+ @click.stop="selectOption(option)"
91
+ >
92
+ <span
93
+ class="text-sm truncate"
94
+ :class="
95
+ option.value === props.modelValue ? 'text-primary' : unselectedOptionClass
96
+ "
97
+ >
98
+ {{ option.label }}
99
+ </span>
100
+ <Icon
101
+ v-if="option.value === props.modelValue"
102
+ name="mdi:check"
103
+ class="text-primary text-sm shrink-0"
104
+ />
105
+ </button>
106
+ </template>
107
+ </motion.div>
108
+ </AnimatePresence>
109
+ </Teleport>
110
+ </div>
111
+
112
+ <p
113
+ v-if="description && !props.error"
114
+ class="text-gray-600 dark:text-white/60 text-xs"
115
+ >
116
+ {{ description }}
117
+ </p>
118
+ <p
119
+ v-if="props.error"
120
+ class="text-danger text-xs mt-1"
121
+ >
122
+ {{ props.error }}
123
+ </p>
124
+ </div>
125
+ </template>
126
+
127
+ <script>
128
+ import { AnimatePresence, motion } from "motion-v";
129
+ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
130
+ import { createPopoverGroup } from "../composables/popoverGroup";
131
+ const popoverGroup = createPopoverGroup();
132
+ </script>
133
+
134
+ <script setup>
135
+ const roundedVariants = {
136
+ "none": "rounded-none",
137
+ "sm": "rounded-sm",
138
+ "md": "rounded-md",
139
+ "lg": "rounded-lg",
140
+ "xl": "rounded-xl",
141
+ "2xl": "rounded-2xl",
142
+ "full": "rounded-full"
143
+ };
144
+ const variants = {
145
+ default: "border border-gray-200 dark:border-white/10 bg-white dark:bg-white/10 enabled:hover:bg-gray-100 dark:enabled:hover:bg-white/20 dark:text-white focus:bg-white dark:focus:bg-white/10 focus:ring-2 focus:ring-primary focus:outline-none",
146
+ secondary: "border border-gray-200 dark:border-white/10 bg-[#EBEBEC] dark:bg-white/20 dark:text-white enabled:hover:bg-[#E0E0E1] dark:enabled:hover:bg-white/30 focus:bg-[#EBEBEC] dark:focus:bg-white/20 focus:ring-2 focus:ring-primary focus:outline-none"
147
+ };
148
+ const errorVariants = {
149
+ default: "border border-danger bg-danger/10 dark:bg-danger/20 text-black dark:text-white enabled:hover:bg-white/20 dark:enabled:hover:bg-white/20 focus:bg-white dark:focus:bg-white/10 focus:ring-2 focus:ring-danger focus:outline-none",
150
+ secondary: "border border-danger bg-danger/22 dark:bg-danger/10 text-black dark:text-white enabled:hover:bg-[#E0E0E1] dark:enabled:hover:bg-white/30 focus:bg-[#EBEBEC] dark:focus:bg-white/20 focus:ring-2 focus:ring-danger focus:outline-none"
151
+ };
152
+ const props = defineProps({
153
+ modelValue: { type: [String, Number, null], required: false, default: null },
154
+ options: { type: Array, required: false, default: () => [] },
155
+ label: { type: String, required: false },
156
+ labelClass: { type: String, required: false, default: "text-sm font-semibold" },
157
+ placeholder: { type: String, required: false, default: "Search..." },
158
+ description: { type: String, required: false },
159
+ rounded: { type: String, required: false, default: "xl" },
160
+ variant: { type: String, required: false, default: "default" },
161
+ required: { type: Boolean, required: false, default: false },
162
+ error: { type: String, required: false, default: "" },
163
+ disabled: { type: Boolean, required: false, default: false }
164
+ });
165
+ const emit = defineEmits(["update:modelValue", "change", "search"]);
166
+ const selectRef = ref(null);
167
+ const inputRef = ref(null);
168
+ const isOpen = ref(false);
169
+ const isDarkMode = ref(false);
170
+ const dropdownPosition = ref({ top: 0, left: 0, width: 0 });
171
+ const searchQuery = ref("");
172
+ const selectedOption = computed(() => {
173
+ return props.options.find((option) => option.value === props.modelValue);
174
+ });
175
+ const filteredOptions = computed(() => {
176
+ const q = searchQuery.value.trim().toLowerCase();
177
+ if (!q) return props.options;
178
+ if (selectedOption.value && searchQuery.value === selectedOption.value.label) {
179
+ return props.options;
180
+ }
181
+ return props.options.filter(
182
+ (option) => option.label.toLowerCase().includes(q)
183
+ );
184
+ });
185
+ const dropdownStyle = computed(() => ({
186
+ top: `${dropdownPosition.value.top}px`,
187
+ left: `${dropdownPosition.value.left}px`,
188
+ width: `${dropdownPosition.value.width}px`,
189
+ zIndex: 9999
190
+ }));
191
+ const selectedOptionClass = computed(() => {
192
+ return isDarkMode.value ? "bg-white/10" : "bg-primary/10";
193
+ });
194
+ const unselectedOptionClass = computed(() => {
195
+ return isDarkMode.value ? "text-white" : "text-black";
196
+ });
197
+ watch(
198
+ () => props.modelValue,
199
+ () => {
200
+ searchQuery.value = selectedOption.value?.label ?? "";
201
+ },
202
+ { immediate: true }
203
+ );
204
+ function syncDarkMode() {
205
+ isDarkMode.value = Boolean(selectRef.value?.closest(".dark"));
206
+ }
207
+ function calculateDropdownPosition() {
208
+ if (!selectRef.value) return;
209
+ syncDarkMode();
210
+ const rect = selectRef.value.getBoundingClientRect();
211
+ dropdownPosition.value = {
212
+ top: rect.bottom + window.scrollY + 8,
213
+ left: rect.left + window.scrollX,
214
+ width: rect.width
215
+ };
216
+ }
217
+ async function open() {
218
+ if (props.disabled || isOpen.value) return;
219
+ popoverGroup.open(close);
220
+ await nextTick();
221
+ calculateDropdownPosition();
222
+ isOpen.value = true;
223
+ }
224
+ function close() {
225
+ if (!isOpen.value) return;
226
+ isOpen.value = false;
227
+ popoverGroup.release(close);
228
+ searchQuery.value = selectedOption.value?.label ?? "";
229
+ }
230
+ function onInput(event) {
231
+ const value = event.target.value;
232
+ searchQuery.value = value;
233
+ emit("search", value);
234
+ if (!isOpen.value) open();
235
+ }
236
+ function selectOption(option) {
237
+ emit("update:modelValue", option.value);
238
+ emit("change", option.value);
239
+ searchQuery.value = option.label;
240
+ isOpen.value = false;
241
+ popoverGroup.release(close);
242
+ }
243
+ function clear() {
244
+ searchQuery.value = "";
245
+ emit("update:modelValue", null);
246
+ emit("change", null);
247
+ emit("search", "");
248
+ inputRef.value?.focus();
249
+ }
250
+ function onClickOutside(event) {
251
+ if (selectRef.value && !selectRef.value.contains(event.target)) {
252
+ close();
253
+ }
254
+ }
255
+ function onScroll() {
256
+ if (isOpen.value) calculateDropdownPosition();
257
+ }
258
+ onMounted(() => {
259
+ syncDarkMode();
260
+ document.addEventListener("click", onClickOutside);
261
+ window.addEventListener("scroll", onScroll, true);
262
+ window.addEventListener("resize", onScroll);
263
+ });
264
+ onUnmounted(() => {
265
+ document.removeEventListener("click", onClickOutside);
266
+ window.removeEventListener("scroll", onScroll, true);
267
+ window.removeEventListener("resize", onScroll);
268
+ popoverGroup.release(close);
269
+ });
270
+ </script>
File without changes
@@ -7,8 +7,8 @@ type __VLS_Props = {
7
7
  rounded?: AvatarRounded;
8
8
  };
9
9
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
- size: AvatarSize;
11
10
  rounded: AvatarRounded;
11
+ size: AvatarSize;
12
12
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="flex items-center justify-center bg-gray-200 dark:bg-white/10"
3
+ class="flex items-center justify-center bg-gray-200 dark:bg-[#242830]"
4
4
  :class="[
5
5
  !hasText ? 'text-black dark:text-white' : '',
6
6
  sizes[props.size],
@@ -7,8 +7,8 @@ type __VLS_Props = {
7
7
  rounded?: AvatarRounded;
8
8
  };
9
9
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
- size: AvatarSize;
11
10
  rounded: AvatarRounded;
11
+ size: AvatarSize;
12
12
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
@@ -24,10 +24,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
24
24
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
25
25
  onClick?: ((event: MouseEvent) => any) | undefined;
26
26
  }>, {
27
- size: ButtonSize;
28
27
  rounded: ButtonRounded;
29
28
  variant: ButtonVariant;
30
29
  color: ButtonColor;
30
+ size: ButtonSize;
31
31
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
32
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
33
33
  declare const _default: typeof __VLS_export;
@@ -99,7 +99,7 @@ const props = defineProps({
99
99
  customClass: { type: String, required: false }
100
100
  });
101
101
  const defaultColorClasses = {
102
- default: "bg-gray-200/80 dark:bg-white/10 text-black dark:text-white hover:bg-gray-300/80 dark:hover:bg-white/20",
102
+ default: "bg-gray-200/80 dark:bg-[#242830] text-black dark:text-white hover:bg-gray-300/80 dark:hover:bg-[#2D323B]",
103
103
  ios: "bg-blue-500 text-white hover:brightness-110",
104
104
  primary: "bg-primary text-white hover:brightness-110",
105
105
  danger: "bg-danger text-black hover:brightness-110",
@@ -107,15 +107,15 @@ const defaultColorClasses = {
107
107
  warning: "bg-warning text-black hover:brightness-110"
108
108
  };
109
109
  const secondaryColorClasses = {
110
- default: "bg-gray-100 dark:bg-white/5 text-black dark:text-white hover:bg-gray-200/80 dark:hover:bg-white/10",
111
- ios: "bg-gray-200/60 dark:bg-white/7 text-blue-500 hover:bg-gray-200 dark:hover:bg-white/10",
112
- primary: "bg-gray-200/60 dark:bg-white/7 text-primary hover:bg-gray-200 dark:hover:bg-white/10",
113
- danger: "bg-gray-200/60 dark:bg-white/7 text-danger hover:bg-gray-200 dark:hover:bg-white/10",
114
- success: "bg-gray-200/60 dark:bg-white/7 text-success hover:bg-gray-200 dark:hover:bg-white/10",
115
- warning: "bg-gray-200/60 dark:bg-white/7 text-warning hover:bg-gray-200 dark:hover:bg-white/10"
110
+ default: "bg-gray-100 dark:bg-[#1A1D23] text-black dark:text-white hover:bg-gray-200/80 dark:hover:bg-[#22262E]",
111
+ ios: "bg-gray-200/60 dark:bg-[#242830] text-blue-500 hover:bg-gray-200 dark:hover:bg-white/10",
112
+ primary: "bg-gray-200/60 dark:bg-[#242830] text-primary hover:bg-gray-200 dark:hover:bg-white/10",
113
+ danger: "bg-gray-200/60 dark:bg-[#242830] text-danger hover:bg-gray-200 dark:hover:bg-white/10",
114
+ success: "bg-gray-200/60 dark:bg-[#242830] text-success hover:bg-gray-200 dark:hover:bg-white/10",
115
+ warning: "bg-gray-200/60 dark:bg-[#242830] text-warning hover:bg-gray-200 dark:hover:bg-white/10"
116
116
  };
117
117
  const outlineColorClasses = {
118
- default: "border border-gray-300 text-black dark:border-white/15 dark:text-white hover:bg-gray-100/80 dark:hover:bg-white/10",
118
+ default: "border border-gray-300 text-black dark:border-[#2D323B] dark:text-white hover:bg-gray-100/80 dark:hover:bg-[#1A1D23]",
119
119
  ios: "border border-blue-500 text-blue-500 hover:bg-blue-500/15 dark:hover:bg-blue-500/25",
120
120
  primary: "border border-primary text-primary hover:bg-primary/15 dark:hover:bg-primary/25",
121
121
  danger: "border border-danger text-danger hover:bg-danger/15 dark:hover:bg-danger/25",
@@ -123,7 +123,7 @@ const outlineColorClasses = {
123
123
  warning: "border border-warning text-warning hover:bg-warning/15 dark:hover:bg-warning/25"
124
124
  };
125
125
  const ghostColorClasses = {
126
- default: "text-black dark:text-white hover:bg-gray-200/60 dark:hover:bg-white/10",
126
+ default: "text-black dark:text-white hover:bg-gray-200/60 dark:hover:bg-[#1F232B]",
127
127
  ios: "text-blue-500 hover:bg-blue-100 dark:hover:bg-blue-500/20",
128
128
  primary: "text-primary hover:bg-primary/20 dark:hover:bg-primary/30",
129
129
  danger: "text-danger hover:bg-danger/20 dark:hover:bg-danger/30",
@@ -131,7 +131,7 @@ const ghostColorClasses = {
131
131
  warning: "text-warning hover:bg-warning/20 dark:hover:bg-warning/30"
132
132
  };
133
133
  const flatColorClasses = {
134
- default: "bg-gray-100 dark:bg-white/5 text-black dark:text-white hover:bg-gray-200/80 dark:hover:bg-white/10",
134
+ default: "bg-gray-100 dark:bg-[#1C1F26] text-black dark:text-white hover:bg-gray-200/80 dark:hover:bg-[#242830]",
135
135
  ios: "bg-blue-500/15 text-blue-500 hover:bg-blue-500/25",
136
136
  primary: "bg-primary/15 text-primary hover:bg-primary/25 dark:hover:bg-primary/30",
137
137
  danger: "bg-danger/15 text-danger hover:bg-danger/25 dark:hover:bg-danger/30",
@@ -24,10 +24,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
24
24
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
25
25
  onClick?: ((event: MouseEvent) => any) | undefined;
26
26
  }>, {
27
- size: ButtonSize;
28
27
  rounded: ButtonRounded;
29
28
  variant: ButtonVariant;
30
29
  color: ButtonColor;
30
+ size: ButtonSize;
31
31
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
32
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
33
33
  declare const _default: typeof __VLS_export;
@@ -1,12 +1,12 @@
1
1
  <template>
2
2
  <div
3
3
  :class="[
4
- !hasBg ? 'bg-white dark:bg-white/5' : '',
4
+ !hasBg ? 'bg-white dark:bg-[#14171C]' : '',
5
5
  !hasPadding ? 'p-4' : '',
6
6
  !hasShadow ? 'pu-shadow-ios' : '',
7
7
  !hasRounded ? 'rounded-2xl' : '',
8
8
  customClass,
9
- isBordered ? 'border border-gray-200 dark:border-white/5' : ''
9
+ isBordered ? 'border border-gray-200 dark:border-[#23272F]' : ''
10
10
  ]"
11
11
  >
12
12
  <slot />
@@ -18,7 +18,7 @@ import { computed } from "vue";
18
18
  const props = defineProps({
19
19
  variant: { type: String, required: false },
20
20
  customClass: { type: String, required: false },
21
- isBordered: { type: Boolean, required: false, default: false }
21
+ isBordered: { type: Boolean, required: false, default: true }
22
22
  });
23
23
  const hasBg = computed(
24
24
  () => props.customClass?.split(" ").some((c) => c.startsWith("bg-"))
@@ -15,10 +15,10 @@ type __VLS_Slots = {} & {
15
15
  default?: (props: typeof __VLS_6) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
18
- size: ChipSize;
19
18
  rounded: ChipRounded;
20
19
  variant: ChipVariant;
21
20
  color: ChipColor;
21
+ size: ChipSize;
22
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
23
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
24
24
  declare const _default: typeof __VLS_export;
@@ -62,7 +62,7 @@ const props = defineProps({
62
62
  iconSize: { type: String, required: false }
63
63
  });
64
64
  const defaultColorClasses = {
65
- default: "bg-gray-200/80 dark:bg-white/10 text-black dark:text-white",
65
+ default: "bg-gray-200/80 dark:bg-[#242830] text-black dark:text-white",
66
66
  ios: "bg-blue-500 text-white",
67
67
  primary: "bg-primary text-white",
68
68
  danger: "bg-danger text-black",
@@ -70,12 +70,12 @@ const defaultColorClasses = {
70
70
  warning: "bg-warning text-black"
71
71
  };
72
72
  const secondaryColorClasses = {
73
- default: "bg-gray-100 dark:bg-white/5 text-black dark:text-white",
74
- ios: "bg-gray-200/60 dark:bg-white/7 text-blue-500",
75
- primary: "bg-gray-200/60 dark:bg-white/7 text-primary",
76
- danger: "bg-gray-200/60 dark:bg-white/7 text-danger",
77
- success: "bg-gray-200/60 dark:bg-white/7 text-success",
78
- warning: "bg-gray-200/60 dark:bg-white/7 text-warning"
73
+ default: "bg-gray-100 dark:bg-[#242830] text-black dark:text-white",
74
+ ios: "bg-gray-200/60 dark:bg-[#242830] text-blue-500",
75
+ primary: "bg-gray-200/60 dark:bg-[#242830] text-primary",
76
+ danger: "bg-gray-200/60 dark:bg-[#242830] text-danger",
77
+ success: "bg-gray-200/60 dark:bg-[#242830] text-success",
78
+ warning: "bg-gray-200/60 dark:bg-[#242830] text-warning"
79
79
  };
80
80
  const outlineColorClasses = {
81
81
  default: "border border-gray-300 text-black dark:border-white/15 dark:text-white",
@@ -86,7 +86,7 @@ const outlineColorClasses = {
86
86
  warning: "border border-warning text-warning"
87
87
  };
88
88
  const flatColorClasses = {
89
- default: "bg-gray-100 dark:bg-white/5 text-black dark:text-white",
89
+ default: "bg-gray-100 dark:bg-[#242830] text-black dark:text-white",
90
90
  ios: "bg-blue-500/15 text-blue-500",
91
91
  primary: "bg-primary/15 text-primary",
92
92
  danger: "bg-danger/15 text-danger",
@@ -15,10 +15,10 @@ type __VLS_Slots = {} & {
15
15
  default?: (props: typeof __VLS_6) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
18
- size: ChipSize;
19
18
  rounded: ChipRounded;
20
19
  variant: ChipVariant;
21
20
  color: ChipColor;
21
+ size: ChipSize;
22
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
23
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
24
24
  declare const _default: typeof __VLS_export;
@@ -1,18 +1,16 @@
1
- type __VLS_Props = {
2
- menuMinWidth?: string;
3
- };
4
- declare var __VLS_1: {}, __VLS_17: {};
5
- type __VLS_Slots = {} & {
6
- activator?: (props: typeof __VLS_1) => any;
7
- } & {
8
- content?: (props: typeof __VLS_17) => any;
9
- };
10
- declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
11
- menuMinWidth: string;
12
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
- declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
14
1
  declare const _default: typeof __VLS_export;
15
2
  export default _default;
3
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
4
+ menuMinWidth?: string;
5
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
6
+ menuMinWidth?: string;
7
+ }> & Readonly<{}>, {
8
+ menuMinWidth: string;
9
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
10
+ activator?: (props: {}) => any;
11
+ } & {
12
+ content?: (props: {}) => any;
13
+ }>;
16
14
  type __VLS_WithSlots<T, S> = T & {
17
15
  new (): {
18
16
  $slots: S;
@@ -3,55 +3,101 @@
3
3
  ref="dropdownRef"
4
4
  class="relative inline-block text-left"
5
5
  >
6
- <div @click="toggle">
6
+ <div
7
+ ref="activatorRef"
8
+ @click="toggle"
9
+ >
7
10
  <slot name="activator" />
8
11
  </div>
9
12
 
10
- <AnimatePresence>
11
- <motion.div
12
- v-if="isOpen"
13
- :initial="{ scale: 0.9, opacity: 0, y: -10 }"
14
- :exit="{ opacity: 0 }"
15
- :animate="{ scale: 1, opacity: 1, y: 0 }"
16
- :class="[
17
- 'absolute right-0 z-50 mt-2 origin-top-right bg-white border border-gray-100 rounded-xl shadow-xl dark:bg-[#18181B] dark:border-black/40',
13
+ <Teleport to="body">
14
+ <AnimatePresence>
15
+ <motion.div
16
+ v-if="isOpen"
17
+ ref="menuRef"
18
+ :initial="{ scale: 0.9, opacity: 0, y: -10 }"
19
+ :exit="{ opacity: 0 }"
20
+ :animate="{ scale: 1, opacity: 1, y: 0 }"
21
+ :style="menuStyle"
22
+ :class="[
23
+ 'fixed z-50 origin-top-right bg-white border border-gray-100 rounded-xl shadow-xl dark:bg-[#18181B] dark:border-black/40',
18
24
  props.menuMinWidth
19
25
  ]"
20
- @click="handleContentClick"
21
- >
22
- <slot name="content" />
23
- </motion.div>
24
- </AnimatePresence>
26
+ @click="handleContentClick"
27
+ >
28
+ <slot name="content" />
29
+ </motion.div>
30
+ </AnimatePresence>
31
+ </Teleport>
25
32
  </div>
26
33
  </template>
27
34
 
28
- <script setup>
35
+ <script>
29
36
  import { AnimatePresence, motion } from "motion-v";
30
- import { ref, onMounted, onUnmounted, provide } from "vue";
37
+ import { ref, reactive, onMounted, onUnmounted, provide, nextTick, watch } from "vue";
38
+ import { createPopoverGroup } from "../composables/popoverGroup";
39
+ const popoverGroup = createPopoverGroup();
40
+ </script>
41
+
42
+ <script setup>
31
43
  const props = defineProps({
32
44
  menuMinWidth: { type: String, required: false, default: "min-w-52" }
33
45
  });
34
46
  const isOpen = ref(false);
35
47
  const dropdownRef = ref(null);
36
- const toggle = () => {
37
- isOpen.value = !isOpen.value;
48
+ const activatorRef = ref(null);
49
+ const menuRef = ref(null);
50
+ const menuStyle = reactive({
51
+ top: "0px",
52
+ right: "0px"
53
+ });
54
+ const updatePosition = () => {
55
+ if (!activatorRef.value) return;
56
+ const rect = activatorRef.value.getBoundingClientRect();
57
+ menuStyle.top = `${rect.bottom + 8}px`;
58
+ menuStyle.right = `${window.innerWidth - rect.right}px`;
38
59
  };
39
60
  const close = () => {
40
61
  isOpen.value = false;
62
+ popoverGroup.release(close);
63
+ };
64
+ const toggle = () => {
65
+ if (isOpen.value) {
66
+ close();
67
+ return;
68
+ }
69
+ popoverGroup.open(close);
70
+ isOpen.value = true;
71
+ nextTick(updatePosition);
41
72
  };
42
73
  provide("closeDropdown", close);
43
74
  const onClickOutside = (event) => {
44
- if (dropdownRef.value && !dropdownRef.value.contains(event.target)) {
75
+ const target = event.target;
76
+ const insideActivator = dropdownRef.value?.contains(target);
77
+ const insideMenu = menuRef.value?.contains(target);
78
+ if (!insideActivator && !insideMenu) {
45
79
  setTimeout(() => close(), 10);
46
80
  }
47
81
  };
48
82
  const handleContentClick = (event) => {
49
83
  event.stopPropagation();
50
84
  };
85
+ watch(isOpen, (open) => {
86
+ if (open) {
87
+ window.addEventListener("scroll", updatePosition, true);
88
+ window.addEventListener("resize", updatePosition);
89
+ } else {
90
+ window.removeEventListener("scroll", updatePosition, true);
91
+ window.removeEventListener("resize", updatePosition);
92
+ }
93
+ });
51
94
  onMounted(() => {
52
95
  document.addEventListener("click", onClickOutside);
53
96
  });
54
97
  onUnmounted(() => {
55
98
  document.removeEventListener("click", onClickOutside);
99
+ window.removeEventListener("scroll", updatePosition, true);
100
+ window.removeEventListener("resize", updatePosition);
101
+ popoverGroup.release(close);
56
102
  });
57
103
  </script>
@@ -1,18 +1,16 @@
1
- type __VLS_Props = {
2
- menuMinWidth?: string;
3
- };
4
- declare var __VLS_1: {}, __VLS_17: {};
5
- type __VLS_Slots = {} & {
6
- activator?: (props: typeof __VLS_1) => any;
7
- } & {
8
- content?: (props: typeof __VLS_17) => any;
9
- };
10
- declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
11
- menuMinWidth: string;
12
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
- declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
14
1
  declare const _default: typeof __VLS_export;
15
2
  export default _default;
3
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
4
+ menuMinWidth?: string;
5
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
6
+ menuMinWidth?: string;
7
+ }> & Readonly<{}>, {
8
+ menuMinWidth: string;
9
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
10
+ activator?: (props: {}) => any;
11
+ } & {
12
+ content?: (props: {}) => any;
13
+ }>;
16
14
  type __VLS_WithSlots<T, S> = T & {
17
15
  new (): {
18
16
  $slots: S;
@@ -11,6 +11,7 @@ type __VLS_Props = {
11
11
  required?: boolean;
12
12
  error?: string;
13
13
  disabled?: boolean;
14
+ focus?: boolean;
14
15
  };
15
16
  declare var __VLS_1: {}, __VLS_3: {};
16
17
  type __VLS_Slots = {} & {
@@ -23,12 +24,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
23
24
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
24
25
  "onUpdate:modelValue"?: ((value: string | number) => any) | undefined;
25
26
  }>, {
27
+ focus: boolean;
28
+ labelClass: string;
26
29
  rounded: InputRounded;
27
- type: string;
28
30
  variant: InputVariant;
29
- disabled: boolean;
30
31
  required: boolean;
31
- labelClass: string;
32
+ disabled: boolean;
33
+ type: string;
32
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
35
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
34
36
  declare const _default: typeof __VLS_export;
@@ -38,6 +38,7 @@
38
38
  @input="
39
39
  emit('update:modelValue', $event.target.value)
40
40
  "
41
+ @focus="handleFocus"
41
42
  >
42
43
  <!-- endContent -->
43
44
  <div
@@ -92,9 +93,16 @@ const props = defineProps({
92
93
  variant: { type: String, required: false, default: "default" },
93
94
  required: { type: Boolean, required: false, default: false },
94
95
  error: { type: String, required: false },
95
- disabled: { type: Boolean, required: false, default: false }
96
+ disabled: { type: Boolean, required: false, default: false },
97
+ focus: { type: Boolean, required: false, default: false }
96
98
  });
97
99
  const emit = defineEmits(["update:modelValue"]);
100
+ const handleFocus = (event) => {
101
+ if (props.focus) {
102
+ const target = event.target;
103
+ target.select();
104
+ }
105
+ };
98
106
  </script>
99
107
 
100
108
  <style scoped>
@@ -11,6 +11,7 @@ type __VLS_Props = {
11
11
  required?: boolean;
12
12
  error?: string;
13
13
  disabled?: boolean;
14
+ focus?: boolean;
14
15
  };
15
16
  declare var __VLS_1: {}, __VLS_3: {};
16
17
  type __VLS_Slots = {} & {
@@ -23,12 +24,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
23
24
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
24
25
  "onUpdate:modelValue"?: ((value: string | number) => any) | undefined;
25
26
  }>, {
27
+ focus: boolean;
28
+ labelClass: string;
26
29
  rounded: InputRounded;
27
- type: string;
28
30
  variant: InputVariant;
29
- disabled: boolean;
30
31
  required: boolean;
31
- labelClass: string;
32
+ disabled: boolean;
33
+ type: string;
32
34
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
33
35
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
34
36
  declare const _default: typeof __VLS_export;
@@ -5,8 +5,8 @@ type __VLS_Props = {
5
5
  width?: number;
6
6
  };
7
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
8
- height: number;
9
8
  width: number;
9
+ height: number;
10
10
  loop: boolean;
11
11
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  declare const _default: typeof __VLS_export;
@@ -5,8 +5,8 @@ type __VLS_Props = {
5
5
  width?: number;
6
6
  };
7
7
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
8
- height: number;
9
8
  width: number;
9
+ height: number;
10
10
  loop: boolean;
11
11
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
12
  declare const _default: typeof __VLS_export;
@@ -1,37 +0,0 @@
1
- import type { InputRounded, InputVariant } from '../types/index.js';
2
- interface SelectOption {
3
- label: string;
4
- value: string | number;
5
- }
6
- type __VLS_Props = {
7
- modelValue?: string | number | null;
8
- options?: SelectOption[];
9
- label?: string;
10
- labelClass?: string;
11
- placeholder?: string;
12
- description?: string;
13
- rounded?: InputRounded;
14
- variant?: InputVariant;
15
- required?: boolean;
16
- error?: string;
17
- disabled?: boolean;
18
- };
19
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
- change: (value: string | number) => any;
21
- "update:modelValue": (value: string | number) => any;
22
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
23
- onChange?: ((value: string | number) => any) | undefined;
24
- "onUpdate:modelValue"?: ((value: string | number) => any) | undefined;
25
- }>, {
26
- rounded: InputRounded;
27
- error: string;
28
- variant: InputVariant;
29
- disabled: boolean;
30
- placeholder: string;
31
- required: boolean;
32
- modelValue: string | number | null;
33
- labelClass: string;
34
- options: SelectOption[];
35
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
- declare const _default: typeof __VLS_export;
37
- export default _default;
@@ -111,9 +111,14 @@
111
111
  </div>
112
112
  </template>
113
113
 
114
- <script setup>
114
+ <script>
115
115
  import { AnimatePresence, motion } from "motion-v";
116
116
  import { computed, nextTick, onMounted, onUnmounted, ref } from "vue";
117
+ import { createPopoverGroup } from "../composables/popoverGroup";
118
+ const popoverGroup = createPopoverGroup();
119
+ </script>
120
+
121
+ <script setup>
117
122
  const roundedVariants = {
118
123
  "none": "rounded-none",
119
124
  "sm": "rounded-sm",
@@ -186,14 +191,18 @@ async function toggle() {
186
191
  if (props.disabled) {
187
192
  return;
188
193
  }
189
- if (!isOpen.value) {
190
- await nextTick();
191
- calculateDropdownPosition();
194
+ if (isOpen.value) {
195
+ close();
196
+ return;
192
197
  }
193
- isOpen.value = !isOpen.value;
198
+ popoverGroup.open(close);
199
+ await nextTick();
200
+ calculateDropdownPosition();
201
+ isOpen.value = true;
194
202
  }
195
203
  function close() {
196
204
  isOpen.value = false;
205
+ popoverGroup.release(close);
197
206
  }
198
207
  function selectOption(option) {
199
208
  emit("update:modelValue", option.value);
@@ -220,5 +229,6 @@ onUnmounted(() => {
220
229
  document.removeEventListener("click", onClickOutside);
221
230
  window.removeEventListener("scroll", onScroll, true);
222
231
  window.removeEventListener("resize", onScroll);
232
+ popoverGroup.release(close);
223
233
  });
224
234
  </script>
@@ -1,37 +0,0 @@
1
- import type { InputRounded, InputVariant } from '../types/index.js';
2
- interface SelectOption {
3
- label: string;
4
- value: string | number;
5
- }
6
- type __VLS_Props = {
7
- modelValue?: string | number | null;
8
- options?: SelectOption[];
9
- label?: string;
10
- labelClass?: string;
11
- placeholder?: string;
12
- description?: string;
13
- rounded?: InputRounded;
14
- variant?: InputVariant;
15
- required?: boolean;
16
- error?: string;
17
- disabled?: boolean;
18
- };
19
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
- change: (value: string | number) => any;
21
- "update:modelValue": (value: string | number) => any;
22
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
23
- onChange?: ((value: string | number) => any) | undefined;
24
- "onUpdate:modelValue"?: ((value: string | number) => any) | undefined;
25
- }>, {
26
- rounded: InputRounded;
27
- error: string;
28
- variant: InputVariant;
29
- disabled: boolean;
30
- placeholder: string;
31
- required: boolean;
32
- modelValue: string | number | null;
33
- labelClass: string;
34
- options: SelectOption[];
35
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
- declare const _default: typeof __VLS_export;
37
- export default _default;
@@ -4,9 +4,9 @@ declare const __VLS_export: import("vue").DefineComponent<TabsProps, {}, {}, {},
4
4
  }, string, import("vue").PublicProps, Readonly<TabsProps> & Readonly<{
5
5
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
6
6
  }>, {
7
+ modelValue: string;
7
8
  rounded: import("../types/index.js").TabsRounded;
8
9
  iconSize: number;
9
- modelValue: string;
10
10
  bgColor: string;
11
11
  btnColor: string;
12
12
  activeTextColor: string;
@@ -4,9 +4,9 @@ declare const __VLS_export: import("vue").DefineComponent<TabsProps, {}, {}, {},
4
4
  }, string, import("vue").PublicProps, Readonly<TabsProps> & Readonly<{
5
5
  "onUpdate:modelValue"?: ((value: string) => any) | undefined;
6
6
  }>, {
7
+ modelValue: string;
7
8
  rounded: import("../types/index.js").TabsRounded;
8
9
  iconSize: number;
9
- modelValue: string;
10
10
  bgColor: string;
11
11
  btnColor: string;
12
12
  activeTextColor: string;
@@ -25,13 +25,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
25
25
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
26
26
  "onUpdate:modelValue"?: ((value: string | number) => any) | undefined;
27
27
  }>, {
28
- rounded: InputRounded;
29
28
  resize: TextAreaResize;
29
+ labelClass: string;
30
+ rounded: InputRounded;
30
31
  variant: InputVariant;
31
- disabled: boolean;
32
32
  required: boolean;
33
+ disabled: boolean;
33
34
  rows: number | string;
34
- labelClass: string;
35
35
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
36
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
37
37
  declare const _default: typeof __VLS_export;
@@ -25,13 +25,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
25
25
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
26
26
  "onUpdate:modelValue"?: ((value: string | number) => any) | undefined;
27
27
  }>, {
28
- rounded: InputRounded;
29
28
  resize: TextAreaResize;
29
+ labelClass: string;
30
+ rounded: InputRounded;
30
31
  variant: InputVariant;
31
- disabled: boolean;
32
32
  required: boolean;
33
+ disabled: boolean;
33
34
  rows: number | string;
34
- labelClass: string;
35
35
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
36
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
37
37
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,5 @@
1
+ export interface PopoverGroup {
2
+ open: (close: () => void) => void;
3
+ release: (close: () => void) => void;
4
+ }
5
+ export declare function createPopoverGroup(): PopoverGroup;
@@ -0,0 +1,12 @@
1
+ export function createPopoverGroup() {
2
+ let activeClose = null;
3
+ return {
4
+ open(close) {
5
+ if (activeClose && activeClose !== close) activeClose();
6
+ activeClose = close;
7
+ },
8
+ release(close) {
9
+ if (activeClose === close) activeClose = null;
10
+ }
11
+ };
12
+ }
@@ -53,6 +53,23 @@ export interface InputProps {
53
53
  error?: string;
54
54
  disabled?: boolean;
55
55
  }
56
+ export interface AutocompleteOption {
57
+ label: string;
58
+ value: string | number;
59
+ }
60
+ export interface AutocompleteProps {
61
+ modelValue?: string | number | null;
62
+ options?: AutocompleteOption[];
63
+ label?: string;
64
+ labelClass?: string;
65
+ placeholder?: string;
66
+ description?: string;
67
+ rounded?: InputRounded;
68
+ variant?: InputVariant;
69
+ required?: boolean;
70
+ error?: string;
71
+ disabled?: boolean;
72
+ }
56
73
  export type TabsRounded = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full';
57
74
  export interface TabItem {
58
75
  label: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smurfox/proxy-ui",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "A UI component library built for Nuxt 4",
5
5
  "repository": {
6
6
  "type": "git",