mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +00:00
fix: dragging disable hover, wrong evt for edit skin + remove back of skin hover
This commit is contained in:
@@ -148,7 +148,6 @@ import { arrayBufferToBase64 } from '@modrinth/utils'
|
|||||||
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
|
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
add_and_equip_custom_skin,
|
|
||||||
type Cape,
|
type Cape,
|
||||||
determineModelType,
|
determineModelType,
|
||||||
equip_skin,
|
equip_skin,
|
||||||
@@ -440,9 +439,22 @@ async function save() {
|
|||||||
const bytes: Uint8Array = new Uint8Array(await (await fetch(textureUrl)).arrayBuffer())
|
const bytes: Uint8Array = new Uint8Array(await (await fetch(textureUrl)).arrayBuffer())
|
||||||
|
|
||||||
if (mode.value === 'new') {
|
if (mode.value === 'new') {
|
||||||
const addedSkin = await add_and_equip_custom_skin(bytes, variant.value, selectedCape.value)
|
const addedSkin = await save_custom_skin(
|
||||||
|
{
|
||||||
|
texture_key: '',
|
||||||
|
variant: variant.value,
|
||||||
|
cape_id: selectedCape.value?.id,
|
||||||
|
texture: textureUrl,
|
||||||
|
source: 'custom',
|
||||||
|
is_equipped: false,
|
||||||
|
},
|
||||||
|
bytes,
|
||||||
|
variant.value,
|
||||||
|
selectedCape.value,
|
||||||
|
true,
|
||||||
|
)
|
||||||
emit('saved', {
|
emit('saved', {
|
||||||
applied: true,
|
applied: false,
|
||||||
skin: addedSkin,
|
skin: addedSkin,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ const sections = computed<SkinSection[]>(() => [
|
|||||||
const draggableSavedSkins = ref<Skin[]>([])
|
const draggableSavedSkins = ref<Skin[]>([])
|
||||||
const isDraggingSavedSkin = ref(false)
|
const isDraggingSavedSkin = ref(false)
|
||||||
const canReorderSavedSkins = computed(() => draggableSavedSkins.value.length > 1)
|
const canReorderSavedSkins = computed(() => draggableSavedSkins.value.length > 1)
|
||||||
|
const fixedSavedSkins = computed(() =>
|
||||||
|
props.savedSkins.filter((skin) => !canPersistSkinOrder(skin)),
|
||||||
|
)
|
||||||
|
|
||||||
const sectionLayouts = computed(() => {
|
const sectionLayouts = computed(() => {
|
||||||
const layouts: Array<{ section: SkinSection; top: number; height: number; index: number }> = []
|
const layouts: Array<{ section: SkinSection; top: number; height: number; index: number }> = []
|
||||||
@@ -223,7 +226,7 @@ watch(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
draggableSavedSkins.value = [...nextSkins]
|
draggableSavedSkins.value = nextSkins.filter(canPersistSkinOrder)
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
@@ -280,10 +283,18 @@ function savedSkinKey(skin: Skin) {
|
|||||||
return skinKey(skin, 'saved-skin')
|
return skinKey(skin, 'saved-skin')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canPersistSkinOrder(skin: Skin) {
|
||||||
|
return skin.source === 'custom'
|
||||||
|
}
|
||||||
|
|
||||||
function doSkinOrdersMatch(firstSkins: Skin[], secondSkins: Skin[]) {
|
function doSkinOrdersMatch(firstSkins: Skin[], secondSkins: Skin[]) {
|
||||||
|
const persistedSecondSkins = secondSkins.filter(canPersistSkinOrder)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
firstSkins.length === secondSkins.length &&
|
firstSkins.length === persistedSecondSkins.length &&
|
||||||
firstSkins.every((skin, index) => savedSkinKey(skin) === savedSkinKey(secondSkins[index]))
|
firstSkins.every(
|
||||||
|
(skin, index) => savedSkinKey(skin) === savedSkinKey(persistedSecondSkins[index]),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +306,7 @@ function onSavedSkinDragEnd() {
|
|||||||
isDraggingSavedSkin.value = false
|
isDraggingSavedSkin.value = false
|
||||||
|
|
||||||
if (doSkinOrdersMatch(draggableSavedSkins.value, props.savedSkins)) {
|
if (doSkinOrdersMatch(draggableSavedSkins.value, props.savedSkins)) {
|
||||||
draggableSavedSkins.value = [...props.savedSkins]
|
draggableSavedSkins.value = props.savedSkins.filter(canPersistSkinOrder)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,7 +415,7 @@ defineExpose({ getAddSkinButtonElement })
|
|||||||
:list="draggableSavedSkins"
|
:list="draggableSavedSkins"
|
||||||
class="grid w-full grid-cols-3 gap-3 min-[1300px]:grid-cols-4 min-[1750px]:grid-cols-5 min-[2050px]:grid-cols-6"
|
class="grid w-full grid-cols-3 gap-3 min-[1300px]:grid-cols-4 min-[1750px]:grid-cols-5 min-[2050px]:grid-cols-6"
|
||||||
:item-key="savedSkinKey"
|
:item-key="savedSkinKey"
|
||||||
:disabled="!canReorderSavedSkins"
|
:disabled="readOnly || !canReorderSavedSkins"
|
||||||
:animation="250"
|
:animation="250"
|
||||||
:swap-threshold="1"
|
:swap-threshold="1"
|
||||||
:invert-swap="false"
|
:invert-swap="false"
|
||||||
@@ -447,10 +458,50 @@ defineExpose({ getAddSkinButtonElement })
|
|||||||
<SkinButton
|
<SkinButton
|
||||||
class="h-full w-full min-w-0 box-border rounded-[20px]"
|
class="h-full w-full min-w-0 box-border rounded-[20px]"
|
||||||
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
||||||
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
|
|
||||||
:selected="isSkinSelected(skin)"
|
:selected="isSkinSelected(skin)"
|
||||||
:active="isSkinActive(skin)"
|
:active="isSkinActive(skin)"
|
||||||
:disabled="readOnly"
|
:disabled="readOnly"
|
||||||
|
:is-dragging="isDraggingSavedSkin"
|
||||||
|
@select="emit('select', skin)"
|
||||||
|
>
|
||||||
|
<template v-if="!readOnly" #overlay-buttons>
|
||||||
|
<ButtonStyled color="brand">
|
||||||
|
<button
|
||||||
|
:aria-label="formatMessage(messages.editSkinButton)"
|
||||||
|
class="pointer-events-auto"
|
||||||
|
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
|
||||||
|
>
|
||||||
|
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
|
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
|
||||||
|
<button
|
||||||
|
v-tooltip="formatMessage(messages.deleteSkinButton)"
|
||||||
|
:aria-label="formatMessage(messages.deleteSkinButton)"
|
||||||
|
class="!rounded-[100%] pointer-events-auto"
|
||||||
|
@click.stop="emit('delete', skin)"
|
||||||
|
>
|
||||||
|
<TrashIcon />
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
|
</template>
|
||||||
|
</SkinButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div
|
||||||
|
v-for="skin in fixedSavedSkins"
|
||||||
|
:key="savedSkinKey(skin)"
|
||||||
|
class="relative aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
|
||||||
|
>
|
||||||
|
<SkinButton
|
||||||
|
class="h-full w-full min-w-0 box-border rounded-[20px]"
|
||||||
|
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
||||||
|
:selected="isSkinSelected(skin)"
|
||||||
|
:active="isSkinActive(skin)"
|
||||||
|
:disabled="readOnly"
|
||||||
|
:is-dragging="isDraggingSavedSkin"
|
||||||
@select="emit('select', skin)"
|
@select="emit('select', skin)"
|
||||||
>
|
>
|
||||||
<template v-if="!readOnly" #overlay-buttons>
|
<template v-if="!readOnly" #overlay-buttons>
|
||||||
@@ -488,11 +539,11 @@ defineExpose({ getAddSkinButtonElement })
|
|||||||
:key="skinKey(skin, section.key)"
|
:key="skinKey(skin, section.key)"
|
||||||
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
|
class="aspect-[31/40] w-full min-w-0 box-border rounded-[20px]"
|
||||||
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
:forward-image-src="getBakedSkinTextures(skin)?.forwards"
|
||||||
:backward-image-src="getBakedSkinTextures(skin)?.backwards"
|
|
||||||
:selected="isSkinSelected(skin)"
|
:selected="isSkinSelected(skin)"
|
||||||
:active="isSkinActive(skin)"
|
:active="isSkinActive(skin)"
|
||||||
:tooltip="skin.name"
|
:tooltip="skin.name"
|
||||||
:disabled="readOnly"
|
:disabled="readOnly"
|
||||||
|
:is-dragging="isDraggingSavedSkin"
|
||||||
@select="emit('select', skin)"
|
@select="emit('select', skin)"
|
||||||
>
|
>
|
||||||
<template #overlay-buttons>
|
<template #overlay-buttons>
|
||||||
|
|||||||
@@ -15,12 +15,10 @@ import { skinPreviewStorage } from '../storage/skin-preview-storage'
|
|||||||
|
|
||||||
export interface RenderResult {
|
export interface RenderResult {
|
||||||
forwards: string
|
forwards: string
|
||||||
backwards: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RawRenderResult {
|
export interface RawRenderResult {
|
||||||
forwards: Blob
|
forwards: Blob
|
||||||
backwards: Blob
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BatchSkinRenderer {
|
class BatchSkinRenderer {
|
||||||
@@ -92,12 +90,9 @@ class BatchSkinRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frontCameraPos: [number, number, number] = [-1.3, 1, 6.3]
|
const frontCameraPos: [number, number, number] = [-1.3, 1, 6.3]
|
||||||
const backCameraPos: [number, number, number] = [-1.3, 1, -2.5]
|
|
||||||
|
|
||||||
const forwards = await this.renderView(frontCameraPos, lookAtTarget)
|
const forwards = await this.renderView(frontCameraPos, lookAtTarget)
|
||||||
const backwards = await this.renderView(backCameraPos, lookAtTarget)
|
|
||||||
|
|
||||||
return { forwards, backwards }
|
return { forwards }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async renderView(
|
private async renderView(
|
||||||
@@ -407,7 +402,6 @@ async function generateSkinPreviewsForGeneration(
|
|||||||
if (rawCached && !skinBlobUrlMap.has(skinKey)) {
|
if (rawCached && !skinBlobUrlMap.has(skinKey)) {
|
||||||
const cached: RenderResult = {
|
const cached: RenderResult = {
|
||||||
forwards: URL.createObjectURL(rawCached.forwards),
|
forwards: URL.createObjectURL(rawCached.forwards),
|
||||||
backwards: URL.createObjectURL(rawCached.backwards),
|
|
||||||
}
|
}
|
||||||
skinBlobUrlMap.set(skinKey, cached)
|
skinBlobUrlMap.set(skinKey, cached)
|
||||||
}
|
}
|
||||||
@@ -427,7 +421,6 @@ async function generateSkinPreviewsForGeneration(
|
|||||||
if (DEBUG_MODE) {
|
if (DEBUG_MODE) {
|
||||||
const result = skinBlobUrlMap.get(key)!
|
const result = skinBlobUrlMap.get(key)!
|
||||||
URL.revokeObjectURL(result.forwards)
|
URL.revokeObjectURL(result.forwards)
|
||||||
URL.revokeObjectURL(result.backwards)
|
|
||||||
skinBlobUrlMap.delete(key)
|
skinBlobUrlMap.delete(key)
|
||||||
} else continue
|
} else continue
|
||||||
}
|
}
|
||||||
@@ -456,7 +449,6 @@ async function generateSkinPreviewsForGeneration(
|
|||||||
|
|
||||||
const renderResult: RenderResult = {
|
const renderResult: RenderResult = {
|
||||||
forwards: URL.createObjectURL(rawRenderResult.forwards),
|
forwards: URL.createObjectURL(rawRenderResult.forwards),
|
||||||
backwards: URL.createObjectURL(rawRenderResult.backwards),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
skinBlobUrlMap.set(key, renderResult)
|
skinBlobUrlMap.set(key, renderResult)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { RawRenderResult } from '../rendering/batch-skin-renderer'
|
|||||||
|
|
||||||
interface StoredPreview {
|
interface StoredPreview {
|
||||||
forwards: Blob
|
forwards: Blob
|
||||||
backwards: Blob
|
|
||||||
timestamp: number
|
timestamp: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +37,6 @@ export class SkinPreviewStorage {
|
|||||||
|
|
||||||
const storedPreview: StoredPreview = {
|
const storedPreview: StoredPreview = {
|
||||||
forwards: result.forwards,
|
forwards: result.forwards,
|
||||||
backwards: result.backwards,
|
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +65,7 @@ export class SkinPreviewStorage {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve({ forwards: result.forwards, backwards: result.backwards })
|
resolve({ forwards: result.forwards })
|
||||||
}
|
}
|
||||||
request.onerror = () => reject(request.error)
|
request.onerror = () => reject(request.error)
|
||||||
})
|
})
|
||||||
@@ -95,7 +93,7 @@ export class SkinPreviewStorage {
|
|||||||
const result = request.result as StoredPreview | undefined
|
const result = request.result as StoredPreview | undefined
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
results[key] = { forwards: result.forwards, backwards: result.backwards }
|
results[key] = { forwards: result.forwards }
|
||||||
} else {
|
} else {
|
||||||
results[key] = null
|
results[key] = null
|
||||||
}
|
}
|
||||||
@@ -173,7 +171,7 @@ export class SkinPreviewStorage {
|
|||||||
const key = cursor.primaryKey as string
|
const key = cursor.primaryKey as string
|
||||||
const value = cursor.value as StoredPreview
|
const value = cursor.value as StoredPreview
|
||||||
|
|
||||||
const entrySize = value.forwards.size + value.backwards.size
|
const entrySize = value.forwards.size
|
||||||
totalSize += entrySize
|
totalSize += entrySize
|
||||||
count++
|
count++
|
||||||
|
|
||||||
|
|||||||
@@ -584,7 +584,9 @@ async function reorderSavedSkins(orderedSkins: Skin[]) {
|
|||||||
generateSkinPreviews(skins.value, capes.value)
|
generateSkinPreviews(skins.value, capes.value)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await set_custom_skin_order(nextSavedSkins.map((skin) => skin.texture_key))
|
await set_custom_skin_order(
|
||||||
|
nextSavedSkins.filter((skin) => skin.source === 'custom').map((skin) => skin.texture_key),
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
skins.value = previousSkins
|
skins.value = previousSkins
|
||||||
generateSkinPreviews(skins.value, capes.value)
|
generateSkinPreviews(skins.value, capes.value)
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ use crate::{
|
|||||||
state::{
|
state::{
|
||||||
MinecraftCharacterExpressionState, MinecraftProfile,
|
MinecraftCharacterExpressionState, MinecraftProfile,
|
||||||
minecraft_skins::{
|
minecraft_skins::{
|
||||||
CustomMinecraftSkin, CustomMinecraftSkinInsertPosition,
|
CustomMinecraftSkin, CustomMinecraftSkinInsertPosition, mojang_api,
|
||||||
mojang_api,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -610,20 +609,21 @@ async fn add_and_equip_custom_skin_now(
|
|||||||
let equipped_skin = profile.current_skin()?;
|
let equipped_skin = profile.current_skin()?;
|
||||||
let equipped_skin_texture_key = equipped_skin.texture_key();
|
let equipped_skin_texture_key = equipped_skin.texture_key();
|
||||||
let equipped_skin_variant = equipped_skin.variant;
|
let equipped_skin_variant = equipped_skin.variant;
|
||||||
let insert_position =
|
let insert_position = if local_texture_key
|
||||||
if local_texture_key != equipped_skin_texture_key.as_ref() {
|
!= equipped_skin_texture_key.as_ref()
|
||||||
CustomMinecraftSkin::get_by_texture(
|
{
|
||||||
profile.id,
|
CustomMinecraftSkin::get_by_texture(
|
||||||
local_texture_key,
|
profile.id,
|
||||||
&state.pool,
|
local_texture_key,
|
||||||
)
|
&state.pool,
|
||||||
.await?
|
)
|
||||||
.map_or(CustomMinecraftSkinInsertPosition::Top, |skin| {
|
.await?
|
||||||
CustomMinecraftSkinInsertPosition::At(skin.display_order)
|
.map_or(CustomMinecraftSkinInsertPosition::Top, |skin| {
|
||||||
})
|
CustomMinecraftSkinInsertPosition::At(skin.display_order)
|
||||||
} else {
|
})
|
||||||
CustomMinecraftSkinInsertPosition::Top
|
} else {
|
||||||
};
|
CustomMinecraftSkinInsertPosition::Top
|
||||||
|
};
|
||||||
|
|
||||||
let persistence_result = if cape_id.is_none()
|
let persistence_result = if cape_id.is_none()
|
||||||
&& is_bundled_skin(&equipped_skin_texture_key, equipped_skin_variant)
|
&& is_bundled_skin(&equipped_skin_texture_key, equipped_skin_variant)
|
||||||
@@ -762,6 +762,22 @@ async fn persist_equipped_skin(
|
|||||||
let equipped_skin_variant = equipped_skin.variant;
|
let equipped_skin_variant = equipped_skin.variant;
|
||||||
let texture_key_changed =
|
let texture_key_changed =
|
||||||
skin.texture_key.as_ref() != equipped_skin_texture_key.as_ref();
|
skin.texture_key.as_ref() != equipped_skin_texture_key.as_ref();
|
||||||
|
let insert_position = if texture_key_changed {
|
||||||
|
CustomMinecraftSkin::get_by_texture(
|
||||||
|
profile.id,
|
||||||
|
&skin.texture_key,
|
||||||
|
&state.pool,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.map_or(
|
||||||
|
CustomMinecraftSkinInsertPosition::Bottom,
|
||||||
|
|saved_skin| {
|
||||||
|
CustomMinecraftSkinInsertPosition::At(saved_skin.display_order)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
CustomMinecraftSkinInsertPosition::Bottom
|
||||||
|
};
|
||||||
|
|
||||||
if skin.cape_id.is_none()
|
if skin.cape_id.is_none()
|
||||||
&& is_bundled_skin(&equipped_skin_texture_key, equipped_skin_variant)
|
&& is_bundled_skin(&equipped_skin_texture_key, equipped_skin_variant)
|
||||||
@@ -782,7 +798,7 @@ async fn persist_equipped_skin(
|
|||||||
texture_blob,
|
texture_blob,
|
||||||
equipped_skin_variant,
|
equipped_skin_variant,
|
||||||
skin.cape_id,
|
skin.cape_id,
|
||||||
CustomMinecraftSkinInsertPosition::Bottom,
|
insert_position,
|
||||||
&state.pool,
|
&state.pool,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -9,28 +9,27 @@ const emit = defineEmits<{
|
|||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
forwardImageSrc?: string
|
forwardImageSrc?: string
|
||||||
backwardImageSrc?: string
|
|
||||||
selected: boolean
|
selected: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
tooltip?: string
|
tooltip?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
isDragging?: boolean
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
forwardImageSrc: undefined,
|
forwardImageSrc: undefined,
|
||||||
backwardImageSrc: undefined,
|
|
||||||
active: false,
|
active: false,
|
||||||
tooltip: undefined,
|
tooltip: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
isDragging: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const imagesLoaded = ref({
|
const imagesLoaded = ref({
|
||||||
forward: false,
|
forward: false,
|
||||||
backward: false,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function onImageLoad(type: 'forward' | 'backward') {
|
function onImageLoad() {
|
||||||
imagesLoaded.value[type] = true
|
imagesLoaded.value.forward = true
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -39,13 +38,6 @@ watch(
|
|||||||
imagesLoaded.value.forward = false
|
imagesLoaded.value.forward = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.backwardImageSrc,
|
|
||||||
() => {
|
|
||||||
imagesLoaded.value.backward = false
|
|
||||||
},
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -58,6 +50,7 @@ watch(
|
|||||||
{
|
{
|
||||||
'skin-button--with-actions': $slots['overlay-buttons'] && !disabled,
|
'skin-button--with-actions': $slots['overlay-buttons'] && !disabled,
|
||||||
'skin-button--disabled': disabled,
|
'skin-button--disabled': disabled,
|
||||||
|
'skin-button--dragging': isDragging,
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
@@ -81,15 +74,12 @@ watch(
|
|||||||
class="pointer-events-none absolute right-3 top-3 z-20 size-3 rounded-full border-2 border-solid border-surface-3 bg-green"
|
class="pointer-events-none absolute right-3 top-3 z-20 size-3 rounded-full border-2 border-solid border-surface-3 bg-green"
|
||||||
></span>
|
></span>
|
||||||
|
|
||||||
<div
|
<div v-if="!imagesLoaded.forward" class="skeleton-loader h-full w-full">
|
||||||
v-if="!(imagesLoaded.forward && imagesLoaded.backward)"
|
|
||||||
class="skeleton-loader h-full w-full"
|
|
||||||
>
|
|
||||||
<div class="skeleton absolute inset-0 aspect-[5/7]"></div>
|
<div class="skeleton absolute inset-0 aspect-[5/7]"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-show="imagesLoaded.forward && imagesLoaded.backward"
|
v-show="imagesLoaded.forward"
|
||||||
:key="`${selected}-${active}`"
|
:key="`${selected}-${active}`"
|
||||||
:class="[
|
:class="[
|
||||||
'skin-button__image-parent pointer-events-none relative z-0 mb-[1.5px] grid place-items-stretch with-shadow',
|
'skin-button__image-parent pointer-events-none relative z-0 mb-[1.5px] grid place-items-stretch with-shadow',
|
||||||
@@ -100,14 +90,7 @@ watch(
|
|||||||
:src="forwardImageSrc"
|
:src="forwardImageSrc"
|
||||||
class="skin-button__image-facing col-start-1 row-start-1 h-full w-full object-contain"
|
class="skin-button__image-facing col-start-1 row-start-1 h-full w-full object-contain"
|
||||||
height="504"
|
height="504"
|
||||||
@load="onImageLoad('forward')"
|
@load="onImageLoad"
|
||||||
/>
|
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
:src="backwardImageSrc"
|
|
||||||
class="skin-button__image-away col-start-1 row-start-1 h-full w-full object-contain"
|
|
||||||
height="504"
|
|
||||||
@load="onImageLoad('backward')"
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -202,30 +185,17 @@ watch(
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skin-button--dragging {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.skin-button__image-parent {
|
.skin-button__image-parent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 95%;
|
height: 95%;
|
||||||
transform: rotateY(0deg) translateZ(0);
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
transition: transform 500ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
will-change: transform;
|
|
||||||
-webkit-backface-visibility: hidden;
|
|
||||||
backface-visibility: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.skin-button:not(.skin-button--disabled):hover .skin-button__image-parent {
|
.skin-button__image-facing {
|
||||||
transform: rotateY(180deg) translateZ(0);
|
transform: translateZ(0);
|
||||||
}
|
|
||||||
|
|
||||||
.skin-button__image-facing,
|
|
||||||
.skin-button__image-away {
|
|
||||||
-webkit-backface-visibility: hidden;
|
|
||||||
backface-visibility: hidden;
|
|
||||||
transform: translateZ(0.1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.skin-button__image-away {
|
|
||||||
transform: rotateY(180deg) translateZ(0.1px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.with-shadow img {
|
.with-shadow img {
|
||||||
|
|||||||
@@ -21,25 +21,11 @@ const frontImage = `data:image/svg+xml,${encodeURIComponent(`
|
|||||||
</svg>
|
</svg>
|
||||||
)}`)}`
|
)}`)}`
|
||||||
|
|
||||||
const backImage = `data:image/svg+xml,${encodeURIComponent(`
|
|
||||||
<svg width="114" height="176" viewBox="0 0 114 176" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect x="36" y="10" width="42" height="46" fill="#5A3828"/>
|
|
||||||
<rect x="28" y="57" width="58" height="60" fill="#1E7FAC"/>
|
|
||||||
<rect x="18" y="63" width="18" height="54" fill="#2693C7"/>
|
|
||||||
<rect x="78" y="63" width="18" height="54" fill="#19759E"/>
|
|
||||||
<rect x="22" y="117" width="14" height="43" fill="#A96A4D"/>
|
|
||||||
<rect x="78" y="117" width="14" height="43" fill="#A96A4D"/>
|
|
||||||
<rect x="36" y="117" width="18" height="55" fill="#1D334F"/>
|
|
||||||
<rect x="60" y="117" width="18" height="55" fill="#162B45"/>
|
|
||||||
</svg>
|
|
||||||
)}`)}`
|
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Skin/SkinButton',
|
title: 'Skin/SkinButton',
|
||||||
component: SkinButton,
|
component: SkinButton,
|
||||||
args: {
|
args: {
|
||||||
forwardImageSrc: frontImage,
|
forwardImageSrc: frontImage,
|
||||||
backwardImageSrc: backImage,
|
|
||||||
selected: false,
|
selected: false,
|
||||||
active: false,
|
active: false,
|
||||||
tooltip: 'Steve',
|
tooltip: 'Steve',
|
||||||
|
|||||||
Reference in New Issue
Block a user