funkwhale/front/src/components/ui/Pills.vue

150 lines
3.4 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { computed, ref, watch, watchEffect } from 'vue'
import { color } from '~/composables/color';
import Pill from './Pill.vue'
import Layout from './Layout.vue';
import Spacer from './Spacer.vue';
const props = defineProps<{
icon?: string,
placeholder?: string,
label?: string,
customOptions?: true,
autofocus?: boolean,
}>();
const customOptions = ref<string[]>([]);
const model = defineModel<{
current : string[],
others?: string[]
}>({required:true})
const whenInteractive = (then:() => void) => {
if(!model.value.others) return; then();
}
const selectedLabel = ref("+");
whenInteractive(()=>
watchEffect(() => {
if (!model.value.others) return
const newLabel = selectedLabel.value;
console.log("newLabel", newLabel);
selectedLabel.value = '+'
if (!newLabel || newLabel==='+') return;
if (!model.value.current.includes(newLabel) ){
model.value.current.push(newLabel);
model.value.others = model.value.others.filter(value=>value!==newLabel)
}
})
)
const deselectPill = (value:string) =>
whenInteractive(()=>
model.value =
{...model.value,
current : model.value.current.filter(v=>v!==value),
others : [value, ...(model.value.others || [])]
}
)
</script>
<template>
<Layout stack no-gap label :class="$style.pills" for="dropdown">
<!-- Label -->
<span v-if="$slots['label']" :class="$style.label">
<slot name="label" />
</span>
<span v-if="props.label" :class="$style.label">
{{ props.label }}
</span>
<!-- List of Pills -->
<Layout flex gap-8
v-bind="color({}, ['solid', 'default', 'raised'])()"
:class="$style.list"
>
<Pill outline raised
v-for="value in model.current"
v-if="!model.others"
:class="$style.pill"
>
<span>{{ value }}</span>
</Pill>
<Pill outline raised
no-underline @click="deselectPill(value)"
v-for="value in model.current"
v-if="model.others"
:class="$style.pill"
>
<span :class="$style['pill-content']">{{ value }}</span>   ×
</Pill>
<!-- Add more pills -->
<select
v-if="model.others"
name="dropdown"
v-model="selectedLabel"
:class="$style.dropdown"
@change="e => e.target.value='+'"
>
<option value="+"></option>
<option v-for="value in model.others" :value="value">
{{ value }}
</option>
</select>
</Layout>
</Layout>
</template>
<style module lang="scss">
.pills {
>.label {
margin-top: -18px;
padding-bottom: 8px;
font-size: 14px;
font-weight: 600;
}
>.list {
padding:4px;
border-radius: 24px;
min-height: 48px;
min-width: 160px;
>:is(:hover, :focus-visible) .pill-content {
text-decoration: line-through !important;
}
> .pill {
margin: 4px;
padding: 2px;
}
>.dropdown{
border-radius: 15px;
padding: 2px 11.25px;
flex-grow: 1;
text-align: right;
background: transparent;
appearance: auto;
margin-right: 12px;
// From vitepress default, needed for app
border: 0;
color: inherit;
}
}
&:hover>.list {
box-shadow: inset 0 0 0 4px var(--border-color)
}
:has(>.dropdown:focus) {
box-shadow: inset 0 0 0 4px var(--focus-ring-color)
}
}
</style>