Skip to content

Commit 861034c

Browse files
TotomIncrudokemper
andauthored
feat(select): add custom #tag slot (#135)
Co-authored-by: Rudo Kemper <[email protected]>
1 parent 7e1a8a5 commit 861034c

File tree

4 files changed

+154
-18
lines changed

4 files changed

+154
-18
lines changed

docs/.vitepress/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ export default defineConfig({
4747
{ text: "Single Select", link: "/demo/single-select" },
4848
{ text: "Multiple Select", link: "/demo/multiple-select" },
4949
{ text: "Custom Option Slot", link: "/demo/custom-option-slot" },
50-
{ text: "Pre-Selected Values (single & multi)", link: "/demo/pre-selected-values" },
50+
{ text: "Custom Tag Slot", link: "/demo/custom-tag-slot" },
51+
{ text: "Pre-Selected Values", link: "/demo/pre-selected-values" },
5152
{ text: "Disabled Options", link: "/demo/disabled-options" },
5253
{ text: "With Menu Header", link: "/demo/with-menu-header" },
5354
{ text: "With Complex Menu Filter", link: "/demo/with-complex-menu-filter.md" },

docs/demo/custom-tag-slot.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
---
2+
title: 'Custom Tag Slot'
3+
---
4+
5+
# Custom Tag Slot
6+
7+
The following example demonstrates how to use the `VueSelect` component with a custom slot `#tag` when using the `isMulti` prop.
8+
9+
::: info
10+
Read more about available [slots here](../slots.md) and the `isMulti` prop [here](../props.md#isMulti).
11+
:::
12+
13+
<script setup>
14+
import { ref } from "vue";
15+
16+
import VueSelect from "../../src";
17+
18+
const selected = ref([]);
19+
</script>
20+
21+
<VueSelect
22+
v-model="selected"
23+
:is-multi="true"
24+
:options="[
25+
{ label: 'Alice', value: 'alice', username: '@alice_user' },
26+
{ label: 'John', value: 'john', username: '@john_user' },
27+
{ label: 'Greg', value: 'greg', username: '@greg_user' },
28+
]"
29+
>
30+
<template #tag="{ option, removeOption }">
31+
<div :class="$style['custom-tag']">
32+
{{ option.username }} <button type="button" @click="removeOption">&times;</button>
33+
</div>
34+
</template>
35+
</VueSelect>
36+
37+
<style module>
38+
.custom-tag {
39+
--vs-multi-value-gap: 4px;
40+
41+
display: flex;
42+
align-items: center;
43+
gap: var(--vs-multi-value-gap);
44+
border-radius: 4px;
45+
border: 1px solid rgba(0, 0, 0, 0.1);
46+
padding: var(--vs-multi-value-padding);
47+
margin: var(--vs-multi-value-margin);
48+
color: var(--vs-multi-value-text-color);
49+
line-height: var(--vs-multi-value-line-height);
50+
background: var(--vs-multi-value-bg);
51+
}
52+
53+
.custom-tag button {
54+
font-size: 1.25rem;
55+
background: none;
56+
}
57+
</style>
58+
59+
## Demo source-code
60+
61+
```vue
62+
<script setup>
63+
import { ref } from "vue";
64+
import VueSelect from "vue3-select-component";
65+
66+
const selected = ref([]);
67+
</script>
68+
69+
<VueSelect
70+
v-model="selected"
71+
:is-multi="true"
72+
:options="[
73+
{ label: 'Alice', value: 'alice', username: '@alice_user' },
74+
{ label: 'John', value: 'john', username: '@john_user' },
75+
{ label: 'Greg', value: 'greg', username: '@greg_user' },
76+
]"
77+
>
78+
<template #tag="{ option, removeOption }">
79+
<div class="custom-tag">
80+
{{ option.username }} <button type="button" @click="removeOption">&times;</button>
81+
</div>
82+
</template>
83+
</VueSelect>
84+
85+
<style lang="css" scoped>
86+
.custom-tag {
87+
--vs-multi-value-gap: 4px;
88+
89+
display: flex;
90+
align-items: center;
91+
gap: var(--vs-multi-value-gap);
92+
border-radius: 4px;
93+
border: 1px solid rgba(0, 0, 0, 0.1);
94+
padding: var(--vs-multi-value-padding);
95+
margin: var(--vs-multi-value-margin);
96+
color: var(--vs-multi-value-text-color);
97+
line-height: var(--vs-multi-value-line-height);
98+
background: var(--vs-multi-value-bg);
99+
}
100+
101+
.custom-tag button {
102+
font-size: 1.25rem;
103+
background: none;
104+
}
105+
</style>
106+
```

docs/slots.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ If you are not familiar with Vue's slots, you can read more about them [here](ht
1414

1515
**Type**: `slotProps: { option: Option }`
1616

17-
Customize the rendered HTML of an option inside the menu. You can use the slot props to retrieve the current menu option that will be rendered.
17+
Customize the rendered template of an option inside the menu. You can use the slot props to retrieve the current menu option that will be rendered.
1818

1919
```vue
2020
<template>
@@ -30,7 +30,7 @@ Customize the rendered HTML of an option inside the menu. You can use the slot p
3030

3131
**Type**: `slotProps: { option: Option }`
3232

33-
Customize the rendered HTML if a selected option (inside the select control). You can use the slot props to retrieve the current selected option.
33+
Customize the rendered template if a selected option (inside the select control). You can use the slot props to retrieve the current selected option.
3434

3535
```vue
3636
<template>
@@ -42,11 +42,31 @@ Customize the rendered HTML if a selected option (inside the select control). Yo
4242
</template>
4343
```
4444

45+
## tag
46+
47+
**Type**: `slotProps: { option: Option, removeOption: () => void }`
48+
49+
When using `isMulti` prop, customize the rendered template of a selected option. You can use the slot props to retrieve the current selected option and a function to remove it.
50+
51+
```vue
52+
<template>
53+
<VueSelect
54+
v-model="option"
55+
:options="options"
56+
:is-multi="true"
57+
>
58+
<template #tag="{ option, removeOption }">
59+
<span>{{ option.label }} <button type="button" @click="removeOption">&times;</button></span>
60+
</template>
61+
</VueSelect>
62+
</template>
63+
```
64+
4565
## menu-header
4666

4767
**Type**: `slotProps: {}`
4868

49-
Customize the rendered HTML for the menu header. This slot is placed **before** the options.
69+
Customize the rendered template for the menu header. This slot is placed **before** the options.
5070

5171
```vue
5272
<template>
@@ -64,7 +84,7 @@ Customize the rendered HTML for the menu header. This slot is placed **before**
6484

6585
**Type**: `slotProps: {}`
6686

67-
Customize the rendered HTML when there are no options matching the search, inside the menu.
87+
Customize the rendered template when there are no options matching the search, inside the menu.
6888

6989
```vue
7090
<template>
@@ -80,7 +100,7 @@ Customize the rendered HTML when there are no options matching the search, insid
80100

81101
**Type**: `slotProps: {}`
82102

83-
Customize the rendered HTML for the dropdown icon. Please note that the slot is placed **inside the button**, so you don't have to deal with attaching event-listeners.
103+
Customize the rendered template for the dropdown icon. Please note that the slot is placed **inside the button**, so you don't have to deal with attaching event-listeners.
84104

85105
```vue
86106
<template>
@@ -96,7 +116,7 @@ Customize the rendered HTML for the dropdown icon. Please note that the slot is
96116

97117
**Type**: `slotProps: {}`
98118

99-
Customize the rendered HTML for the clear icon. Please note that the slot is placed **inside the button**, so you don't have to deal with attaching event-listeners.
119+
Customize the rendered template for the clear icon. Please note that the slot is placed **inside the button**, so you don't have to deal with attaching event-listeners.
100120

101121
```vue
102122
<template>
@@ -112,7 +132,7 @@ Customize the rendered HTML for the clear icon. Please note that the slot is pla
112132

113133
**Type**: `slotProps: {}`
114134

115-
Customize the rendered HTML when the select component is in a loading state. By default, it displays a `<Spinner />` component.
135+
Customize the rendered template when the select component is in a loading state. By default, it displays a `<Spinner />` component.
116136

117137
```vue
118138
<template>

src/Select.vue

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -421,15 +421,24 @@ onBeforeUnmount(() => {
421421
</div>
422422

423423
<template v-if="props.isMulti && selectedOptions.length">
424-
<button
425-
v-for="(option, i) in selectedOptions"
426-
:key="i"
427-
type="button"
428-
class="multi-value"
429-
@click="removeOption(option)"
424+
<template
425+
v-for="selectedOption in selectedOptions"
426+
:key="selectedOption.value"
430427
>
431-
{{ getMultiValueLabel(option) }}<XMarkIcon />
432-
</button>
428+
<slot
429+
name="tag"
430+
:option="selectedOption"
431+
:remove-option="() => removeOption(selectedOption)"
432+
>
433+
<button
434+
type="button"
435+
class="multi-value"
436+
@click="removeOption(selectedOption)"
437+
>
438+
{{ getMultiValueLabel(selectedOption) }}<XMarkIcon />
439+
</button>
440+
</slot>
441+
</template>
433442
</template>
434443

435444
<input
@@ -716,7 +725,7 @@ onBeforeUnmount(() => {
716725
border: 0;
717726
width: var(--vs-icon-size);
718727
height: var(--vs-icon-size);
719-
fill: var(--vs-icon-color);
728+
color: var(--vs-icon-color);
720729
background: none;
721730
outline: none;
722731
cursor: pointer;
@@ -730,7 +739,7 @@ onBeforeUnmount(() => {
730739
border: 0;
731740
width: var(--vs-icon-size);
732741
height: var(--vs-icon-size);
733-
fill: var(--vs-icon-color);
742+
color: var(--vs-icon-color);
734743
background: none;
735744
outline: none;
736745
cursor: pointer;

0 commit comments

Comments
 (0)