polish: use figma values
This commit is contained in:
committed by
Alejandro González
parent
721577cdb1
commit
cf354d2bbc
@@ -8,7 +8,7 @@ import {
|
||||
Button
|
||||
} from '@modrinth/ui'
|
||||
import type { Ref} from 'vue';
|
||||
import {ref, computed, useTemplateRef, watch, onMounted, onUnmounted, inject} from 'vue'
|
||||
import {ref, computed, useTemplateRef, watch, onMounted, onUnmounted, inject, nextTick} from 'vue'
|
||||
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
|
||||
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
|
||||
import { handleError } from '@/store/notifications'
|
||||
@@ -41,7 +41,6 @@ const loginInProgress = ref(false);
|
||||
const currentUser = ref(undefined)
|
||||
const currentUserId = ref<string | undefined>(undefined)
|
||||
|
||||
// @ts-ignore
|
||||
const username = computed(() => currentUser.value?.profile?.name ?? undefined)
|
||||
const selectedSkin = ref<Skin | null>(null)
|
||||
const defaultCape = ref<Cape>()
|
||||
@@ -64,36 +63,8 @@ const capeTexture = computed(() => currentCape.value?.texture)
|
||||
const skinVariant = computed(() => selectedSkin.value?.variant)
|
||||
const skinNametag = computed(() => settings.value.hide_nametag_skins_page ? undefined : username.value)
|
||||
|
||||
await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
|
||||
let userCheckInterval: number | null = null
|
||||
|
||||
onMounted(() => {
|
||||
// TODO: Surely a better way to do this?
|
||||
userCheckInterval = window.setInterval(checkUserChanges, 250)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (userCheckInterval !== null) {
|
||||
window.clearInterval(userCheckInterval)
|
||||
}
|
||||
})
|
||||
|
||||
async function checkUserChanges() {
|
||||
try {
|
||||
const defaultId = await get_default_user()
|
||||
if (defaultId !== currentUserId.value) {
|
||||
await loadCurrentUser();
|
||||
await loadCapes();
|
||||
await loadSkins();
|
||||
}
|
||||
} catch (error) {
|
||||
if (currentUser.value) {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCapes() {
|
||||
try {
|
||||
capes.value = (await get_available_capes()) ?? []
|
||||
@@ -138,7 +109,6 @@ async function loadCurrentUser() {
|
||||
currentUserId.value = defaultId
|
||||
|
||||
const allAccounts = await users()
|
||||
// @ts-ignore
|
||||
currentUser.value = allAccounts.find((acc) => acc.profile.id === defaultId)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
@@ -168,6 +138,33 @@ watch(
|
||||
() => selectedSkin.value?.cape_id,
|
||||
() => {}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
userCheckInterval = window.setInterval(checkUserChanges, 250);
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (userCheckInterval !== null) {
|
||||
window.clearInterval(userCheckInterval);
|
||||
}
|
||||
})
|
||||
|
||||
async function checkUserChanges() {
|
||||
try {
|
||||
const defaultId = await get_default_user()
|
||||
if (defaultId !== currentUserId.value) {
|
||||
await loadCurrentUser();
|
||||
await loadCapes();
|
||||
await loadSkins();
|
||||
}
|
||||
} catch (error) {
|
||||
if (currentUser.value) {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -179,8 +176,8 @@ watch(
|
||||
/>
|
||||
<SelectCapeModal ref="selectCapeModal" :capes="capes" @select="handleCapeSelected" />
|
||||
|
||||
<div v-if="currentUser" class="p-4 grid grid-cols-[300px_1fr] xl:grid-cols-[3fr_5fr] gap-6">
|
||||
<div class="sticky top-6 self-start p-2">
|
||||
<div v-if="currentUser" class="p-4 skin-layout">
|
||||
<div class="preview-panel">
|
||||
<div class="flex justify-between gap-4">
|
||||
<h1 class="m-0 text-2xl font-bold">Skins</h1>
|
||||
<ButtonStyled :disabled="!!selectedSkin?.cape_id">
|
||||
@@ -206,7 +203,7 @@ watch(
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="h-[80vh] flex items-center justify-center">
|
||||
<div class="preview-container">
|
||||
<SkinPreviewRenderer
|
||||
wide-model-src="/src/assets/models/classic_player.gltf"
|
||||
slim-model-src="/src/assets/models/slim_player.gltf"
|
||||
@@ -220,25 +217,25 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6 add-perspective pt-2">
|
||||
<div class="skins-container">
|
||||
<section class="flex flex-col gap-2 mt-1">
|
||||
<h2 class="text-lg font-bold m-0 text-primary">Saved skins</h2>
|
||||
<div class="flex flex-row flex-wrap gap-2 w-full">
|
||||
<div class="skin-card-grid">
|
||||
<SkinLikeTextButton
|
||||
class="flex-none skin-card"
|
||||
tooltip="Add a skin"
|
||||
@click="editSkinModal?.show"
|
||||
>
|
||||
<template #icon>
|
||||
<PlusIcon />
|
||||
</template>
|
||||
Add a skin
|
||||
class="skin-card"
|
||||
tooltip="Add a skin"
|
||||
@click="editSkinModal?.show"
|
||||
>
|
||||
<template #icon>
|
||||
<PlusIcon class="w-5 h-5 stroke-2" />
|
||||
</template>
|
||||
<span>Add a skin</span>
|
||||
</SkinLikeTextButton>
|
||||
|
||||
<SkinButton
|
||||
v-for="skin in savedSkins"
|
||||
:key="`saved-skin-${skin.texture_key}`"
|
||||
class="flex-none skin-card"
|
||||
class="skin-card"
|
||||
editable
|
||||
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
||||
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
|
||||
@@ -249,13 +246,13 @@ watch(
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="flex flex-col gap-2 mt-1 mr-0">
|
||||
<section class="flex flex-col gap-2 mt-6">
|
||||
<h2 class="text-lg font-bold m-0 text-primary">Default skins</h2>
|
||||
<div class="flex flex-row flex-wrap gap-2 w-full">
|
||||
<div class="skin-card-grid">
|
||||
<SkinButton
|
||||
v-for="skin in defaultSkins"
|
||||
:key="`default-skin-${skin.texture_key}`"
|
||||
class="flex-none skin-card"
|
||||
class="skin-card"
|
||||
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
||||
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
|
||||
:selected="selectedSkin === skin"
|
||||
@@ -291,15 +288,56 @@ watch(
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$skin-card-width: 10.5vw;
|
||||
$skin-card-aspect: 1.1;
|
||||
$skin-card-width: 140px;
|
||||
$skin-card-gap: 4px;
|
||||
|
||||
.skin-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
|
||||
gap: 2.5rem;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-panel {
|
||||
position: sticky;
|
||||
top: 1.5rem;
|
||||
align-self: start;
|
||||
padding: 0.5rem;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
height: 80vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
height: 50vh;
|
||||
}
|
||||
}
|
||||
|
||||
.skins-container {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.skin-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, $skin-card-width);
|
||||
gap: $skin-card-gap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.skin-card {
|
||||
width: $skin-card-width;
|
||||
min-width: $skin-card-width;
|
||||
max-width: $skin-card-width;
|
||||
height: calc(#{$skin-card-width} * #{$skin-card-aspect});
|
||||
min-height: calc(#{$skin-card-width} * #{$skin-card-aspect});
|
||||
max-height: calc(#{$skin-card-width} * #{$skin-card-aspect});
|
||||
aspect-ratio: 1.05;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -41,30 +41,30 @@ function onImageLoad(type: 'forward' | 'backward') {
|
||||
]"
|
||||
>
|
||||
<button
|
||||
class="skin-btn-bg absolute inset-0 rounded-xl cursor-pointer p-0 border-none group-hover:brightness-125"
|
||||
class="skin-btn-bg absolute inset-0 cursor-pointer p-0 border-none group-hover:brightness-125"
|
||||
:class="selected ? 'selected' : ''"
|
||||
@click="emit('select')"
|
||||
></button>
|
||||
|
||||
<div v-if="!(imagesLoaded.forward && imagesLoaded.backward)" class="skeleton-loader w-full h-full rounded-xl">
|
||||
<div class="skeleton absolute inset-0 rounded-xl aspect-[5/7]"></div>
|
||||
<div v-if="!(imagesLoaded.forward && imagesLoaded.backward)" class="skeleton-loader w-full h-full">
|
||||
<div class="skeleton absolute inset-0 aspect-[5/7]"></div>
|
||||
</div>
|
||||
|
||||
<span
|
||||
<span
|
||||
v-show="imagesLoaded.forward && imagesLoaded.backward"
|
||||
:class="['skin-button__image-parent pointer-events-none w-full h-full grid [transform-style:preserve-3d] transition-transform duration-500 group-hover:[transform:rotateY(180deg)] place-items-stretch', selected ? 'with-shadow' : '']"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
:src="forwardImageSrc"
|
||||
class="skin-button__image-facing rounded-xl object-contain w-full h-full [backface-visibility:hidden] col-start-1 row-start-1"
|
||||
class="skin-button__image-facing object-contain w-full h-full [backface-visibility:hidden] col-start-1 row-start-1"
|
||||
height="504"
|
||||
@load="onImageLoad('forward')"
|
||||
/>
|
||||
<img
|
||||
alt=""
|
||||
:src="backwardImageSrc"
|
||||
class="skin-button__image-away rounded-xl object-contain w-full h-full [backface-visibility:hidden] [transform:rotateY(180deg)] col-start-1 row-start-1"
|
||||
class="skin-button__image-away object-contain w-full h-full [backface-visibility:hidden] [transform:rotateY(180deg)] col-start-1 row-start-1"
|
||||
height="504"
|
||||
@load="onImageLoad('backward')"
|
||||
/>
|
||||
@@ -118,8 +118,14 @@ function onImageLoad(type: 'forward' | 'backward') {
|
||||
background: linear-gradient(180deg, #3a3d47 0%, #33363d 100%);
|
||||
}
|
||||
.skin-btn-bg.selected {
|
||||
background: linear-gradient(180deg, #5a5d6b 0%, #424956 100%);
|
||||
background: linear-gradient(157.61deg, var(--color-brand) -76.68%, rgba(27, 217, 106, 0.534) -38.61%, rgba(12, 89, 44, 0.6) 100.4%), #27292F;
|
||||
}
|
||||
|
||||
.skin-btn-bg.selected:hover,
|
||||
.group:hover .skin-btn-bg.selected {
|
||||
filter: brightness(1.15);
|
||||
}
|
||||
|
||||
.with-shadow img {
|
||||
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.4));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Drag to rotate
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="nametag" class="absolute top-[13%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md text-[200%] pointer-events-none z-10 font-minecraft text-white bg-black/60 shadow-md border-solid border-1 border-white/10">
|
||||
<div v-if="nametag" class="absolute top-[13%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md text-[200%] pointer-events-none z-10 font-minecraft text-primary nametag-bg">
|
||||
{{ nametag }}
|
||||
</div>
|
||||
|
||||
@@ -226,7 +226,7 @@ function applyTextureToScene(root: THREE.Object3D | null, tex: THREE.Texture | n
|
||||
|
||||
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
||||
|
||||
materials.forEach((mat, index, array) => {
|
||||
materials.forEach((mat, _index, _array) => {
|
||||
const standardMat = mat as THREE.MeshStandardMaterial
|
||||
standardMat.map = tex
|
||||
standardMat.metalness = 0
|
||||
@@ -248,7 +248,7 @@ function applyCapeTexture(root: THREE.Object3D | null, tex: THREE.Texture | null
|
||||
|
||||
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
||||
|
||||
materials.forEach((mat, index, array) => {
|
||||
materials.forEach((mat, _index, _array) => {
|
||||
const standardMat = mat as THREE.MeshStandardMaterial
|
||||
standardMat.map = tex || transparentTexture
|
||||
standardMat.transparent = true
|
||||
@@ -342,3 +342,10 @@ onBeforeMount(async () => {
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nametag-bg {
|
||||
background: linear-gradient(308.68deg, rgba(0, 0, 0, 0) -52.46%, rgba(100, 100, 100, 0.1) 94.75%), rgba(0, 0, 0, 0.2);
|
||||
box-shadow: inset -0.5px -0.5px 0px rgba(0, 0, 0, 0.25), inset 0.5px 0.5px 0px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user