mirror of
https://github.com/modrinth/code.git
synced 2026-08-27 18:14:49 +00:00
* feat: refactor button components
* fix: qa
* fix: storybook
* a11y: pass
* fix: build
* fix: rename default ->md
* fix: lint
* docs: buttons.md
* refactor part 1
* refactor part 2
* fix: undo refactor for fresh restart
* refactor
* Revert "refactor"
This reverts commit 96d65902d7.
* refactor: part 1
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: remove text-contrast
* fix: qa
* fix: v-tooltip
* fix: lint
* fix: split broken
* fix: passkey qa
* fix: qa
* fix: qa
* fix: prepr
* fix: splitbutton
* improve button group seam
---------
Signed-off-by: Calum H. <calum@modrinth.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
104 lines
2.2 KiB
Vue
104 lines
2.2 KiB
Vue
<template>
|
|
<div class="chips" role="radiogroup" :aria-label="ariaLabel">
|
|
<Button
|
|
v-for="item in items"
|
|
:key="formatLabel(item)"
|
|
v-tooltip="isDisabled(item) ? disabledTooltip : undefined"
|
|
role="radio"
|
|
:aria-checked="selected === item"
|
|
:disabled="isDisabled(item)"
|
|
class="btn !brightness-100 hover:!brightness-125"
|
|
:class="{
|
|
selected: selected === item,
|
|
capitalize: capitalize,
|
|
'!px-2.5 !py-1.5': size === 'small',
|
|
}"
|
|
@click="toggleItem(item)"
|
|
>
|
|
<CheckIcon
|
|
v-if="selected === item && !hideCheckmarkIcon"
|
|
class="!text-brand"
|
|
aria-hidden="true"
|
|
/>
|
|
<span>{{ formatLabel(item) }}</span>
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts" generic="T">
|
|
import { CheckIcon } from '@modrinth/assets'
|
|
|
|
import Button from './buttons/Button.vue'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
items: T[]
|
|
formatLabel?: (item: T) => string
|
|
neverEmpty?: boolean
|
|
capitalize?: boolean
|
|
size?: 'standard' | 'small'
|
|
ariaLabel?: string
|
|
disabledItems?: T[]
|
|
disabledTooltip?: string
|
|
hideCheckmarkIcon?: boolean
|
|
}>(),
|
|
{
|
|
neverEmpty: true,
|
|
// Intentional any type, as this default should only be used for primitives (string or number)
|
|
formatLabel: (item) => item.toString(),
|
|
capitalize: true,
|
|
size: 'standard',
|
|
},
|
|
)
|
|
|
|
const selected = defineModel<T | null>()
|
|
|
|
// If one always has to be selected, default to the first one
|
|
if (props.items.length > 0 && props.neverEmpty && !selected.value) {
|
|
selected.value = props.items[0]
|
|
}
|
|
|
|
function isDisabled(item: T): boolean {
|
|
return props.disabledItems?.includes(item) ?? false
|
|
}
|
|
|
|
function toggleItem(item: T) {
|
|
if (isDisabled(item)) return
|
|
if (selected.value === item && !props.neverEmpty) {
|
|
selected.value = null
|
|
} else {
|
|
selected.value = item
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.chips {
|
|
display: flex;
|
|
grid-gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
|
|
.btn {
|
|
border: 1px solid transparent;
|
|
&.capitalize {
|
|
text-transform: capitalize;
|
|
}
|
|
|
|
svg {
|
|
width: 1em;
|
|
height: 1em;
|
|
}
|
|
|
|
&:focus-visible {
|
|
outline: 0.25rem solid var(--color-focus-ring);
|
|
}
|
|
}
|
|
|
|
.selected {
|
|
color: var(--color-brand);
|
|
background-color: var(--color-brand-highlight);
|
|
border: 1px solid var(--color-brand);
|
|
}
|
|
}
|
|
</style>
|