Skip to content

Commit

Permalink
feat(select): add custom #tag slot (#135)
Browse files Browse the repository at this point in the history
Co-authored-by: Rudo Kemper <rtakemper@gmail.com>
  • Loading branch information
TotomInc and rudokemper authored Nov 17, 2024
1 parent 7e1a8a5 commit 861034c
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 18 deletions.
3 changes: 2 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export default defineConfig({
{ text: "Single Select", link: "/demo/single-select" },
{ text: "Multiple Select", link: "/demo/multiple-select" },
{ text: "Custom Option Slot", link: "/demo/custom-option-slot" },
{ text: "Pre-Selected Values (single & multi)", link: "/demo/pre-selected-values" },
{ text: "Custom Tag Slot", link: "/demo/custom-tag-slot" },
{ text: "Pre-Selected Values", link: "/demo/pre-selected-values" },
{ text: "Disabled Options", link: "/demo/disabled-options" },
{ text: "With Menu Header", link: "/demo/with-menu-header" },
{ text: "With Complex Menu Filter", link: "/demo/with-complex-menu-filter.md" },
Expand Down
106 changes: 106 additions & 0 deletions docs/demo/custom-tag-slot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: 'Custom Tag Slot'
---

# Custom Tag Slot

The following example demonstrates how to use the `VueSelect` component with a custom slot `#tag` when using the `isMulti` prop.

::: info
Read more about available [slots here](../slots.md) and the `isMulti` prop [here](../props.md#isMulti).
:::

<script setup>
import { ref } from "vue";

import VueSelect from "../../src";

const selected = ref([]);
</script>

<VueSelect
v-model="selected"
:is-multi="true"
:options="[
{ label: 'Alice', value: 'alice', username: '@alice_user' },
{ label: 'John', value: 'john', username: '@john_user' },
{ label: 'Greg', value: 'greg', username: '@greg_user' },
]"
>
<template #tag="{ option, removeOption }">
<div :class="$style['custom-tag']">
{{ option.username }} <button type="button" @click="removeOption">&times;</button>
</div>
</template>
</VueSelect>
<style module>
.custom-tag {
--vs-multi-value-gap: 4px;

display: flex;
align-items: center;
gap: var(--vs-multi-value-gap);
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.1);
padding: var(--vs-multi-value-padding);
margin: var(--vs-multi-value-margin);
color: var(--vs-multi-value-text-color);
line-height: var(--vs-multi-value-line-height);
background: var(--vs-multi-value-bg);
}

.custom-tag button {
font-size: 1.25rem;
background: none;
}
</style>

## Demo source-code

```vue
<script setup>
import { ref } from "vue";
import VueSelect from "vue3-select-component";
const selected = ref([]);
</script>
<VueSelect
v-model="selected"
:is-multi="true"
:options="[
{ label: 'Alice', value: 'alice', username: '@alice_user' },
{ label: 'John', value: 'john', username: '@john_user' },
{ label: 'Greg', value: 'greg', username: '@greg_user' },
]"
>
<template #tag="{ option, removeOption }">
<div class="custom-tag">
{{ option.username }} <button type="button" @click="removeOption">&times;</button>
</div>
</template>
</VueSelect>
<style lang="css" scoped>
.custom-tag {
--vs-multi-value-gap: 4px;
display: flex;
align-items: center;
gap: var(--vs-multi-value-gap);
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.1);
padding: var(--vs-multi-value-padding);
margin: var(--vs-multi-value-margin);
color: var(--vs-multi-value-text-color);
line-height: var(--vs-multi-value-line-height);
background: var(--vs-multi-value-bg);
}
.custom-tag button {
font-size: 1.25rem;
background: none;
}
</style>
```
34 changes: 27 additions & 7 deletions docs/slots.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ If you are not familiar with Vue's slots, you can read more about them [here](ht

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

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.
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.

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

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

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

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

## tag

**Type**: `slotProps: { option: Option, removeOption: () => void }`

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.

```vue
<template>
<VueSelect
v-model="option"
:options="options"
:is-multi="true"
>
<template #tag="{ option, removeOption }">
<span>{{ option.label }} <button type="button" @click="removeOption">&times;</button></span>
</template>
</VueSelect>
</template>
```

## menu-header

**Type**: `slotProps: {}`

Customize the rendered HTML for the menu header. This slot is placed **before** the options.
Customize the rendered template for the menu header. This slot is placed **before** the options.

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

**Type**: `slotProps: {}`

Customize the rendered HTML when there are no options matching the search, inside the menu.
Customize the rendered template when there are no options matching the search, inside the menu.

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

**Type**: `slotProps: {}`

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.
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.

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

**Type**: `slotProps: {}`

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.
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.

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

**Type**: `slotProps: {}`

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

```vue
<template>
Expand Down
29 changes: 19 additions & 10 deletions src/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -421,15 +421,24 @@ onBeforeUnmount(() => {
</div>
<template v-if="props.isMulti && selectedOptions.length">
<button
v-for="(option, i) in selectedOptions"
:key="i"
type="button"
class="multi-value"
@click="removeOption(option)"
<template
v-for="selectedOption in selectedOptions"
:key="selectedOption.value"
>
{{ getMultiValueLabel(option) }}<XMarkIcon />
</button>
<slot
name="tag"
:option="selectedOption"
:remove-option="() => removeOption(selectedOption)"
>
<button
type="button"
class="multi-value"
@click="removeOption(selectedOption)"
>
{{ getMultiValueLabel(selectedOption) }}<XMarkIcon />
</button>
</slot>
</template>
</template>
<input
Expand Down Expand Up @@ -716,7 +725,7 @@ onBeforeUnmount(() => {
border: 0;
width: var(--vs-icon-size);
height: var(--vs-icon-size);
fill: var(--vs-icon-color);
color: var(--vs-icon-color);
background: none;
outline: none;
cursor: pointer;
Expand All @@ -730,7 +739,7 @@ onBeforeUnmount(() => {
border: 0;
width: var(--vs-icon-size);
height: var(--vs-icon-size);
fill: var(--vs-icon-color);
color: var(--vs-icon-color);
background: none;
outline: none;
cursor: pointer;
Expand Down

0 comments on commit 861034c

Please sign in to comment.