Mercury/ui/src/views/admin/MethodSelect.vue
Matthew L McPeak 7e9bd0b55d
Some checks failed
ci / test (push) Failing after 8s
ci / build-ui (push) Successful in 12s
ci / publish (push) Has been skipped
Inital Commit
2026-06-19 06:02:08 -04:00

122 lines
2.8 KiB
Vue

<template>
<div ref="triggerRef">
<button
type="button"
class="nych-select ms-trigger"
@click="toggleOpen"
>
<span
class="nych-select-label"
:data-p="modelValue.length === 0 ? 'placeholder' : undefined"
>
{{ modelValue.length === 0 ? placeholder : modelValue.join(', ') }}
</span>
<span class="nych-select-dropdown">
<svg
class="nych-select-dropdownIcon ms-chevron"
:class="{ 'ms-chevron-open': isOpen }"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9" />
</svg>
</span>
</button>
<Teleport to="body">
<template v-if="isOpen">
<div class="ms-backdrop" @click="close" />
<div class="nych-select-overlay ms-panel" :style="overlayStyle">
<ul class="nych-select-list">
<li
v-for="opt in options"
:key="opt"
class="nych-select-option"
:data-p-selected="modelValue.includes(opt) ? 'true' : undefined"
@click="toggleOption(opt)"
>
{{ opt }}
</li>
</ul>
</div>
</template>
</Teleport>
</div>
</template>
<script setup lang="ts">
import { ref, onUnmounted } from 'vue'
const props = withDefaults(defineProps<{
modelValue: string[]
options: string[]
placeholder?: string
}>(), { placeholder: 'Select…' })
const emit = defineEmits<{ 'update:modelValue': [string[]] }>()
const triggerRef = ref<HTMLElement>()
const isOpen = ref(false)
const overlayStyle = ref<Record<string, string>>({})
function open() {
const rect = triggerRef.value!.getBoundingClientRect()
overlayStyle.value = {
position: 'fixed',
top: `${rect.bottom + 4}px`,
left: `${rect.left}px`,
width: `${rect.width}px`,
}
isOpen.value = true
window.addEventListener('scroll', close, { capture: true, once: true })
}
function close() {
isOpen.value = false
}
function toggleOpen() {
isOpen.value ? close() : open()
}
function toggleOption(opt: string) {
const cur = props.modelValue
emit('update:modelValue', cur.includes(opt) ? cur.filter(x => x !== opt) : [...cur, opt])
}
onUnmounted(close)
</script>
<style>
/* Must be global — teleported content renders outside this component's scoped tree */
.ms-trigger {
display: flex;
width: 100%;
min-width: unset;
text-align: left;
font-family: var(--font-sans);
font-size: 14px;
cursor: pointer;
}
.ms-backdrop {
position: fixed;
inset: 0;
z-index: 9998;
}
.ms-panel {
z-index: 9999;
}
.ms-chevron {
transition: transform 0.15s ease;
}
.ms-chevron-open {
transform: rotate(180deg);
}
</style>