@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.
- package/README.md +94 -5
- 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 +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 +10 -10
- package/dist/runtime/components/Button.vue.d.ts +1 -1
- package/dist/runtime/components/Card.vue +3 -3
- package/dist/runtime/components/Chip.d.vue.ts +1 -1
- package/dist/runtime/components/Chip.vue +8 -8
- 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
|
@@ -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` | `
|
|
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
|
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;
|
|
@@ -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-
|
|
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-
|
|
111
|
-
ios: "bg-gray-200/60 dark:bg-
|
|
112
|
-
primary: "bg-gray-200/60 dark:bg-
|
|
113
|
-
danger: "bg-gray-200/60 dark:bg-
|
|
114
|
-
success: "bg-gray-200/60 dark:bg-
|
|
115
|
-
warning: "bg-gray-200/60 dark:bg-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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-
|
|
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-
|
|
74
|
-
ios: "bg-gray-200/60 dark:bg-
|
|
75
|
-
primary: "bg-gray-200/60 dark:bg-
|
|
76
|
-
danger: "bg-gray-200/60 dark:bg-
|
|
77
|
-
success: "bg-gray-200/60 dark:bg-
|
|
78
|
-
warning: "bg-gray-200/60 dark:bg-
|
|
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-
|
|
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
|
|
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;
|