@smurfox/proxy-ui 0.3.2 → 0.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 +88 -3
- package/dist/module.json +1 -1
- package/dist/runtime/components/Autocomplete.d.vue.ts +0 -0
- package/dist/runtime/components/Autocomplete.vue +270 -0
- package/dist/runtime/components/Autocomplete.vue.d.ts +0 -0
- package/dist/runtime/components/Avatar.d.vue.ts +1 -1
- package/dist/runtime/components/Avatar.vue.d.ts +1 -1
- package/dist/runtime/components/Button.d.vue.ts +1 -1
- package/dist/runtime/components/Button.vue.d.ts +1 -1
- package/dist/runtime/components/Chip.d.vue.ts +1 -1
- package/dist/runtime/components/Chip.vue.d.ts +1 -1
- package/dist/runtime/components/Dropdown.d.vue.ts +11 -13
- package/dist/runtime/components/Dropdown.vue +65 -19
- package/dist/runtime/components/Dropdown.vue.d.ts +11 -13
- package/dist/runtime/components/Input.d.vue.ts +5 -3
- package/dist/runtime/components/Input.vue +9 -1
- package/dist/runtime/components/Input.vue.d.ts +5 -3
- package/dist/runtime/components/Lottie.d.vue.ts +1 -1
- package/dist/runtime/components/Lottie.vue.d.ts +1 -1
- package/dist/runtime/components/Select.d.vue.ts +0 -37
- package/dist/runtime/components/Select.vue +15 -5
- package/dist/runtime/components/Select.vue.d.ts +0 -37
- package/dist/runtime/components/Tabs.d.vue.ts +1 -1
- package/dist/runtime/components/Tabs.vue.d.ts +1 -1
- package/dist/runtime/components/TextArea.d.vue.ts +3 -3
- package/dist/runtime/components/TextArea.vue.d.ts +3 -3
- package/dist/runtime/composables/popoverGroup.d.ts +5 -0
- package/dist/runtime/composables/popoverGroup.js +12 -0
- package/dist/runtime/types/index.d.ts +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,6 +164,7 @@ A flexible input component with validation and state management.
|
|
|
164
164
|
| `required` | `boolean` | `false` | Shows a red asterisk on the label. |
|
|
165
165
|
| `error` | `string` | — | Error message to display. Changes styling to danger. |
|
|
166
166
|
| `disabled` | `boolean` | `false` | Disables the input. |
|
|
167
|
+
| `focus` | `boolean` | `false` | Selects the whole text on focus (useful for pre-filled fields the user is likely to replace). |
|
|
167
168
|
|
|
168
169
|
**Slots**
|
|
169
170
|
|
|
@@ -178,6 +179,9 @@ A flexible input component with validation and state management.
|
|
|
178
179
|
<!-- Basic -->
|
|
179
180
|
<PUInput label="Email" type="email" placeholder="you@example.com" />
|
|
180
181
|
|
|
182
|
+
<!-- Select all on focus (great for pre-filled codes the user wants to replace) -->
|
|
183
|
+
<PUInput v-model="orderCode" label="Order code" focus />
|
|
184
|
+
|
|
181
185
|
<!-- With helper text -->
|
|
182
186
|
<PUInput label="Password" type="password" description="At least 8 characters" />
|
|
183
187
|
|
|
@@ -260,7 +264,7 @@ A multi-line text input. Shares the look and feel of `PUInput`, with extra props
|
|
|
260
264
|
|
|
261
265
|
### PUSelect
|
|
262
266
|
|
|
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`.
|
|
267
|
+
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
268
|
|
|
265
269
|
```vue
|
|
266
270
|
<PUSelect
|
|
@@ -320,6 +324,85 @@ A custom select with an animated dropdown panel teleported to `body`. Dark-mode
|
|
|
320
324
|
|
|
321
325
|
---
|
|
322
326
|
|
|
327
|
+
### PUAutocomplete
|
|
328
|
+
|
|
329
|
+
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.
|
|
330
|
+
|
|
331
|
+
```vue
|
|
332
|
+
<PUAutocomplete
|
|
333
|
+
v-model="framework"
|
|
334
|
+
label="Framework"
|
|
335
|
+
placeholder="Search a framework"
|
|
336
|
+
:options="[
|
|
337
|
+
{ label: 'Nuxt', value: 'nuxt' },
|
|
338
|
+
{ label: 'Vue', value: 'vue' },
|
|
339
|
+
{ label: 'React', value: 'react' },
|
|
340
|
+
]"
|
|
341
|
+
/>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
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).
|
|
345
|
+
|
|
346
|
+
**Props**
|
|
347
|
+
|
|
348
|
+
| Prop | Type | Default | Description |
|
|
349
|
+
| ------------- | ----------------------------------------------------------- | ------------------------- | ---------------------------------------------------- |
|
|
350
|
+
| `modelValue` | `string \| number \| null` | `null` | Selected value (v-model). |
|
|
351
|
+
| `options` | `{ label: string, value: string \| number }[]` | `[]` | Items shown in the dropdown. |
|
|
352
|
+
| `label` | `string` | — | Label displayed above the input. |
|
|
353
|
+
| `labelClass` | `string` | `'text-sm font-semibold'` | Custom classes for the label. |
|
|
354
|
+
| `placeholder` | `string` | `'Search...'` | Placeholder text shown when the input is empty. |
|
|
355
|
+
| `description` | `string` | — | Helper text displayed below. |
|
|
356
|
+
| `rounded` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| 'full'` | `'xl'` | Border radius. |
|
|
357
|
+
| `variant` | `'default' \| 'secondary'` | `'default'` | Visual style. |
|
|
358
|
+
| `required` | `boolean` | `false` | Shows a red asterisk on the label. |
|
|
359
|
+
| `error` | `string` | `''` | Error message to display. Changes styling to danger. |
|
|
360
|
+
| `disabled` | `boolean` | `false` | Disables the input. |
|
|
361
|
+
|
|
362
|
+
**Events**
|
|
363
|
+
|
|
364
|
+
| Event | Payload | Description |
|
|
365
|
+
| ------------------- | -------------------------------- | ------------------------------------------------------------------------ |
|
|
366
|
+
| `update:modelValue` | `string \| number \| null` | Emitted when an option is picked, or `null` when the clear button is used. |
|
|
367
|
+
| `change` | `string \| number \| null` | Emitted alongside `update:modelValue`. |
|
|
368
|
+
| `search` | `string` | Emitted on every keystroke with the current input text. Useful for remote search. |
|
|
369
|
+
|
|
370
|
+
**Examples**
|
|
371
|
+
|
|
372
|
+
```vue
|
|
373
|
+
<!-- Basic -->
|
|
374
|
+
<PUAutocomplete v-model="country" :options="countries" label="Country" />
|
|
375
|
+
|
|
376
|
+
<!-- Secondary variant + required -->
|
|
377
|
+
<PUAutocomplete
|
|
378
|
+
v-model="city"
|
|
379
|
+
variant="secondary"
|
|
380
|
+
:options="cities"
|
|
381
|
+
label="City"
|
|
382
|
+
required
|
|
383
|
+
/>
|
|
384
|
+
|
|
385
|
+
<!-- With error -->
|
|
386
|
+
<PUAutocomplete
|
|
387
|
+
v-model="status"
|
|
388
|
+
:options="statuses"
|
|
389
|
+
label="Status"
|
|
390
|
+
error="Pick a status"
|
|
391
|
+
required
|
|
392
|
+
/>
|
|
393
|
+
|
|
394
|
+
<!-- Remote search via the `search` event -->
|
|
395
|
+
<PUAutocomplete
|
|
396
|
+
v-model="user"
|
|
397
|
+
:options="remoteResults"
|
|
398
|
+
label="User"
|
|
399
|
+
placeholder="Type to search users"
|
|
400
|
+
@search="onSearch"
|
|
401
|
+
/>
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
323
406
|
### PUCard
|
|
324
407
|
|
|
325
408
|
A flexible card component with customizable styling and borders.
|
|
@@ -569,7 +652,7 @@ const activeTab = ref("dashboard");
|
|
|
569
652
|
|
|
570
653
|
### PUDropdown
|
|
571
654
|
|
|
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.
|
|
655
|
+
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
656
|
|
|
574
657
|
```vue
|
|
575
658
|
<PUDropdown>
|
|
@@ -782,10 +865,12 @@ import type {
|
|
|
782
865
|
TableItem,
|
|
783
866
|
TableRounded,
|
|
784
867
|
TableItemsSize,
|
|
868
|
+
AutocompleteOption,
|
|
869
|
+
AutocompleteProps,
|
|
785
870
|
} from "@smurfox/proxy-ui";
|
|
786
871
|
```
|
|
787
872
|
|
|
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.
|
|
873
|
+
> `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
874
|
|
|
790
875
|
---
|
|
791
876
|
|
package/dist/module.json
CHANGED
|
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;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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
|
|
6
|
+
<div
|
|
7
|
+
ref="activatorRef"
|
|
8
|
+
@click="toggle"
|
|
9
|
+
>
|
|
7
10
|
<slot name="activator" />
|
|
8
11
|
</div>
|
|
9
12
|
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
if (isOpen.value) {
|
|
195
|
+
close();
|
|
196
|
+
return;
|
|
192
197
|
}
|
|
193
|
-
|
|
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,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;
|