browser / src /components /Segmented.vue
lrigamai's picture
Group LoRAs by character, add table view and docked detail panel
45fb44a
Raw History Blame Contribute Delete
1.62 kB
<script setup lang="ts" generic="T extends string">
import Icon, { type IconName } from "./Icon.vue";
/**
* Segmented control — a styled radio group used in place of a native <select>.
* Options with an `icon` show only the icon; `label` becomes its accessible name.
*/
defineProps<{ options: { value: T; label: string; icon?: IconName }[]; label: string }>();
const model = defineModel<T>({ required: true });
</script>
<template>
<div class="seg" role="radiogroup" :aria-label="label">
<button
v-for="o in options"
:key="o.value"
type="button"
role="radio"
:aria-checked="model === o.value"
:class="{ on: model === o.value, icon: o.icon }"
:aria-label="o.icon ? o.label : undefined"
:title="o.icon ? o.label : undefined"
@click="model = o.value"
>
<Icon v-if="o.icon" :name="o.icon" />
<template v-else>{{ o.label }}</template>
</button>
</div>
</template>
<style scoped>
.seg {
display: inline-flex;
padding: 3px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface);
}
button {
height: 32px;
padding: 0 12px;
border: 0;
border-radius: 2px;
background: transparent;
color: var(--text-3);
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
transition: color 0.15s, background 0.15s;
}
button.icon {
display: grid;
place-items: center;
width: 32px;
padding: 0;
font-size: 15px;
}
button:hover {
color: var(--text);
}
button.on {
background: var(--surface-2);
color: var(--accent);
box-shadow: inset 0 0 0 1px var(--line-strong);
}
</style>