You've already forked AstralRinth
8371ff641a
* fix: previous period data was included in the table * fix: revenue displaying stale data when viewing it from different metric and grouped by 6 hour or 1 hour * fix: remove staletime on analytics query so switching tabs does not refersh query * feat: add monetization alert * fix-small: missing space in tooltip * fix: incorrect y-axis formatting for trailing decimal 0s * fix: switching tabs resets table series selection due to other refetches * fix: always show month first in chart tooltip * fix: change all time start date to be project published date * fix: increase length on project name column * fix: unknown download source data points not showing for download source breakdown * fix: double unknown for loader * fix: no data on country labeling incorrectly as "Unknown" instead of "Other" * fix: date picker number inputs showing arrows * fix: stat card showing enormous percentage for prev period by switching it to absolute value difference after 1000% * fix: decimal values for playtime being rounded badly, resulting in 0.04 becoming 0.0 * fix: chips having stroke * refactor: pnpm prepr * fix: spacing in annoucement link * fix: legend scroll shadow on top of event tooltip
100 lines
2.1 KiB
Vue
100 lines
2.1 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" />
|
|
<span>{{ formatLabel(item) }}</span>
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts" generic="T">
|
|
import { CheckIcon } from '@modrinth/assets'
|
|
|
|
import Button from './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>
|