```ts
import Pills from "~/components/ui/Pills.vue"
```
# Pills
Show a dense list of pills representing tags, categories or options.
Users can select a subset of given options and create new ones.
The model you provide will be mutated by this component:
- `currents`: these items are currently selected
- `others`: these items are currently not selected (but can be selected by the user). This prop is optional. By adding it, you allow users to change the selection.
Each item has a `label` of type `string` as well as a `type` of either:
- `custom`: the user can edit its label or
- `preset`: the user cannot edit its label
```ts
type Item = { type: 'custom' | 'preset', label: string }
type Model = {
currents: Item[],
others?: Item[],
}
```
## No pills
```ts
const nullModel = ref({
currents: []
}) satisfies Model;
```
```vue-html
```
## Static list of pills
```ts
const staticModel = ref({
currents: [
{ label: "#noise", type: 'preset' },
{ label: "#fieldRecording", type: 'preset' },
{ label: "#experiment", type: 'preset' }
]
} satisfies Model);
```
```vue-html
```
## Let users add, remove and edit custom pills
By adding `custom` options, you make the `Pills` instance interactive. Use [reactive](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive-variables-with-ref) methods [such as `computed(...)`](https://vuejs.org/guide/essentials/computed.html) and `watch(...)` to bind the model.
Note that this component will automatically add an empty pill to the end of the model because it made the implementation more straightforward. Use `filter(({ label }) => label !== '') to ignore it when reading the model.
### Minimal example
```ts
const simpleCustomModel = ref({
currents: [],
others: []
})
```
```vue-html
```
### Complex example
```ts
const customModel = ref({
...staticModel,
others: [
{ label: "#MyTag1", type: 'custom' },
{ label: "#MyTag2", type: 'custom' }
]
} satisfies Model);
```
```vue-html
```
## Bind data with an external sink
In the following example, `others` are shared among two `Pills` lists.
```ts
const sharedOthers = ref(customModel.value.others)
const currentA = ref([{ label: 'A', type: 'preset' }])
const currentB = ref([])
const updateSharedOthers = (others: Item[]) => {
sharedOthers.value
= unionBy(sharedOthers.value, others, 'label')
.filter(item =>
[...currentA.value, ...currentB.value].every(({ label }) =>
item.label !== label
))
}
```
Shared among A and B:
{{ (sharedOthers || []).map(({ label }) => label).join(', ') }}
## Bind data with an external source
You can use the same pattern to influence the model from an outside source:
```ts
const tags = ref(['1', '2'])
const sharedTags = ref(['3'])
const setTags = (v: string[]) => {
sharedTags.value
= [...tags.value, ...sharedTags.value].filter(tag => !(v.includes(tag)))
tags.value
= v
}
```