You've already forked AstralRinth
forked from didirus/AstralRinth
Medal promo v2 (#4220)
* Revert "Revert "feat: medal promotion on servers page (#4117)"" This reverts commit2e6cff7efc. * Revert "Revert "update changelog"" This reverts commitb2ff2d8737. * Revert "Revert "turn off medal promo"" This reverts commiteaa4b44a16. * Revert "Revert "Revert "turn off medal promo""" This reverts commit76d0ef03e7. * Revert "Revert "fix medal thing showing up for everyone"" This reverts commitee8c47adcb. * New medal colors * Update medal server listings * Upgrade modal enhancements & more medal consistency * undo app promo changes * Only apply medal promo with flag on * remove unneessary files * lint * disable medal flag
This commit is contained in:
@@ -3,7 +3,7 @@ import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
color?: 'standard' | 'brand' | 'red' | 'orange' | 'green' | 'blue' | 'purple'
|
||||
color?: 'standard' | 'brand' | 'red' | 'orange' | 'green' | 'blue' | 'purple' | 'medal-promo'
|
||||
size?: 'standard' | 'large' | 'small'
|
||||
circular?: boolean
|
||||
type?: 'standard' | 'outlined' | 'transparent' | 'highlight' | 'highlight-colored-text'
|
||||
@@ -34,6 +34,7 @@ const highlightedColorVar = computed(() => {
|
||||
return 'var(--color-orange-highlight)'
|
||||
case 'green':
|
||||
return 'var(--color-green-highlight)'
|
||||
case 'medal-promo':
|
||||
case 'blue':
|
||||
return 'var(--color-blue-highlight)'
|
||||
case 'purple':
|
||||
@@ -58,6 +59,8 @@ const colorVar = computed(() => {
|
||||
return 'var(--color-blue)'
|
||||
case 'purple':
|
||||
return 'var(--color-purple)'
|
||||
case 'medal-promo':
|
||||
return 'var(--medal-promotion-text-orange)'
|
||||
case 'standard':
|
||||
default:
|
||||
return null
|
||||
|
||||
128
packages/ui/src/components/base/OptionGroup.vue
Normal file
128
packages/ui/src/components/base/OptionGroup.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<nav
|
||||
ref="scrollContainer"
|
||||
class="card-shadow experimental-styles-within relative flex w-fit overflow-x-auto rounded-full bg-bg-raised p-1 text-sm font-bold"
|
||||
>
|
||||
<button
|
||||
v-for="(option, index) in options"
|
||||
:key="`option-group-${index}`"
|
||||
ref="optionButtons"
|
||||
class="button-animation z-[1] flex flex-row items-center gap-2 rounded-full bg-transparent px-4 py-2 font-semibold"
|
||||
:class="{
|
||||
'text-button-textSelected': modelValue === option,
|
||||
'text-primary': modelValue !== option,
|
||||
}"
|
||||
@click="setOption(option)"
|
||||
>
|
||||
<slot :option="option" :selected="modelValue === option" />
|
||||
</button>
|
||||
<div
|
||||
class="navtabs-transition pointer-events-none absolute h-[calc(100%-0.5rem)] overflow-hidden rounded-full bg-button-bgSelected p-1"
|
||||
:style="{
|
||||
left: sliderLeftPx,
|
||||
top: sliderTopPx,
|
||||
right: sliderRightPx,
|
||||
bottom: sliderBottomPx,
|
||||
opacity: initialized ? 1 : 0,
|
||||
}"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" generic="T">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
|
||||
const modelValue = defineModel<T>({ required: true })
|
||||
|
||||
const props = defineProps<{
|
||||
options: T[]
|
||||
}>()
|
||||
|
||||
const scrollContainer = ref<HTMLElement | null>(null)
|
||||
|
||||
const sliderLeft = ref(4)
|
||||
const sliderTop = ref(4)
|
||||
const sliderRight = ref(4)
|
||||
const sliderBottom = ref(4)
|
||||
|
||||
const sliderLeftPx = computed(() => `${sliderLeft.value}px`)
|
||||
const sliderTopPx = computed(() => `${sliderTop.value}px`)
|
||||
const sliderRightPx = computed(() => `${sliderRight.value}px`)
|
||||
const sliderBottomPx = computed(() => `${sliderBottom.value}px`)
|
||||
|
||||
const optionButtons = ref()
|
||||
|
||||
const initialized = ref(false)
|
||||
|
||||
function setOption(option: T) {
|
||||
modelValue.value = option
|
||||
}
|
||||
|
||||
watch(modelValue, () => {
|
||||
startAnimation(props.options.indexOf(modelValue.value))
|
||||
})
|
||||
|
||||
function startAnimation(index: number) {
|
||||
const el = optionButtons.value[index]
|
||||
|
||||
if (!el || !el.offsetParent) return
|
||||
|
||||
const newValues = {
|
||||
left: el.offsetLeft,
|
||||
top: el.offsetTop,
|
||||
right: el.offsetParent.offsetWidth - el.offsetLeft - el.offsetWidth,
|
||||
bottom: el.offsetParent.offsetHeight - el.offsetTop - el.offsetHeight,
|
||||
}
|
||||
|
||||
if (sliderLeft.value === 4 && sliderRight.value === 4) {
|
||||
sliderLeft.value = newValues.left
|
||||
sliderRight.value = newValues.right
|
||||
sliderTop.value = newValues.top
|
||||
sliderBottom.value = newValues.bottom
|
||||
} else {
|
||||
const delay = 200
|
||||
|
||||
if (newValues.left < sliderLeft.value) {
|
||||
sliderLeft.value = newValues.left
|
||||
setTimeout(() => {
|
||||
sliderRight.value = newValues.right
|
||||
}, delay)
|
||||
} else {
|
||||
sliderRight.value = newValues.right
|
||||
setTimeout(() => {
|
||||
sliderLeft.value = newValues.left
|
||||
}, delay)
|
||||
}
|
||||
|
||||
if (newValues.top < sliderTop.value) {
|
||||
sliderTop.value = newValues.top
|
||||
setTimeout(() => {
|
||||
sliderBottom.value = newValues.bottom
|
||||
}, delay)
|
||||
} else {
|
||||
sliderBottom.value = newValues.bottom
|
||||
setTimeout(() => {
|
||||
sliderTop.value = newValues.top
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
initialized.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startAnimation(props.options.indexOf(modelValue.value))
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.navtabs-transition {
|
||||
transition:
|
||||
all 150ms cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 250ms cubic-bezier(0.5, 0, 0.2, 1) 50ms;
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user