mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-03-01 12:44:37 +00:00
248 lines
6.0 KiB
Vue
248 lines
6.0 KiB
Vue
<template>
|
|
<div :class="[badges.length === 2 && 'hc-badges-dual']" class="hc-badges">
|
|
<component
|
|
:is="selectionMode && index > 0 ? 'button' : 'div'"
|
|
class="hc-badge-container"
|
|
v-for="(badge, index) in badges"
|
|
:key="index"
|
|
:class="{
|
|
selectable: selectionMode && index > 0,
|
|
selected: selectedIndex === index,
|
|
dragging: draggingIndex === index,
|
|
'drag-over': dragOverIndex === index,
|
|
}"
|
|
:draggable="isDraggable(index, badge)"
|
|
@click="handleBadgeClick(index)"
|
|
@dragstart="handleDragStart($event, index, badge)"
|
|
@dragend="handleDragEnd"
|
|
@dragover.prevent="handleDragOver(index)"
|
|
@dragleave="handleDragLeave(index)"
|
|
@drop.prevent="handleDrop($event, index)"
|
|
>
|
|
<img :title="badge.description" :src="backendPath(badge.icon)" class="hc-badge" />
|
|
</component>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { backendPath } from '~/helpers/backendPath'
|
|
export default {
|
|
name: 'Badges',
|
|
props: {
|
|
badges: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
selectionMode: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
dragEnabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
selectedIndex: null,
|
|
draggingIndex: null,
|
|
dragOverIndex: null,
|
|
}
|
|
},
|
|
methods: {
|
|
backendPath,
|
|
isDraggable(index, badge) {
|
|
if (!this.dragEnabled || index === 0) return false
|
|
if (badge.isDefault) return false
|
|
return true
|
|
},
|
|
handleBadgeClick(index) {
|
|
if (!this.selectionMode || index === 0) {
|
|
return
|
|
}
|
|
if (this.selectedIndex === index) {
|
|
this.selectedIndex = null
|
|
this.$emit('badge-selected', null)
|
|
return
|
|
}
|
|
|
|
this.selectedIndex = index
|
|
this.$emit('badge-selected', index)
|
|
},
|
|
handleDragStart(event, index, badge) {
|
|
if (!this.isDraggable(index, badge)) {
|
|
event.preventDefault()
|
|
return
|
|
}
|
|
this.draggingIndex = index
|
|
event.dataTransfer.effectAllowed = 'move'
|
|
event.dataTransfer.setData(
|
|
'application/json',
|
|
JSON.stringify({ source: 'hex', index, badge }),
|
|
)
|
|
this.$emit('drag-start', { source: 'hex', index, badge })
|
|
},
|
|
handleDragEnd() {
|
|
this.draggingIndex = null
|
|
this.dragOverIndex = null
|
|
this.$emit('drag-end')
|
|
},
|
|
handleDragOver(index) {
|
|
if (index === 0) return
|
|
this.dragOverIndex = index
|
|
},
|
|
handleDragLeave(index) {
|
|
if (this.dragOverIndex === index) {
|
|
this.dragOverIndex = null
|
|
}
|
|
},
|
|
handleDrop(event, index) {
|
|
if (index === 0) return
|
|
this.dragOverIndex = null
|
|
const badge = this.badges[index]
|
|
let source = null
|
|
try {
|
|
source = JSON.parse(event.dataTransfer.getData('application/json'))
|
|
} catch {
|
|
return
|
|
}
|
|
if (!source) return
|
|
this.$emit('badge-drop', { source, targetIndex: index, targetBadge: badge })
|
|
},
|
|
resetSelection() {
|
|
this.selectedIndex = null
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.hc-badges {
|
|
position: relative;
|
|
|
|
transform: scale(var(--badges-scale, 1));
|
|
|
|
$badge-size-x: 30px;
|
|
$badge-size-y: 26px;
|
|
$main-badge-size-x: 60px;
|
|
$main-badge-size-y: 52px;
|
|
$gap-x: -6px;
|
|
$gap-y: 1px;
|
|
$slot-x: $badge-size-x + $gap-x;
|
|
$slot-y: $badge-size-y + $gap-y;
|
|
$offset-y: calc($badge-size-y / 2) - 2 * $gap-x;
|
|
|
|
width: $main-badge-size-x + 4 * $badge-size-x + 4 * $gap-x;
|
|
height: $offset-y + 3 * $badge-size-y + 4 * $gap-y;
|
|
margin: auto;
|
|
|
|
.hc-badge-container {
|
|
position: absolute;
|
|
width: $badge-size-x;
|
|
height: $badge-size-y;
|
|
|
|
clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
|
|
|
|
&.selectable {
|
|
cursor: pointer;
|
|
|
|
transition: transform 0.1s ease-in;
|
|
|
|
&:hover,
|
|
&:focus-visible {
|
|
transform: scale(1.1);
|
|
outline: none;
|
|
}
|
|
}
|
|
|
|
&.selected {
|
|
filter: drop-shadow(0 0 0 $color-primary);
|
|
img {
|
|
opacity: 0.6;
|
|
}
|
|
}
|
|
|
|
&.dragging {
|
|
opacity: 0.4;
|
|
}
|
|
|
|
&.drag-over {
|
|
filter: drop-shadow(0 0 4px $color-success);
|
|
transform: scale(1.15);
|
|
transition: transform 0.15s ease;
|
|
}
|
|
}
|
|
|
|
.hc-badge {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(1) {
|
|
width: $main-badge-size-x;
|
|
height: $main-badge-size-y;
|
|
top: $offset-y + calc($gap-y / 2) - 1px;
|
|
left: 0;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(1)::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -20px;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 20px;
|
|
background-image: url('/img/badges/stars.svg');
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
background-size: contain;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(2) {
|
|
top: $offset-y + calc(-1 * $gap-y / 2) - 1px;
|
|
left: $main-badge-size-x + $gap-x;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(3) {
|
|
top: $offset-y + $slot-y + calc($gap-y / 2) - 1px;
|
|
left: $main-badge-size-x + $gap-x;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(4) {
|
|
top: $offset-y + calc(-1 * $badge-size-y / 2) - (2 * $gap-y) - 0.5px;
|
|
left: $main-badge-size-x + $gap-x + $slot-x;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(5) {
|
|
top: $offset-y + calc($badge-size-y / 2) - 0.5px;
|
|
left: $main-badge-size-x + $gap-x + $slot-x;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(6) {
|
|
top: $offset-y + (1.5 * $badge-size-y) + (2 * $gap-y) - 0.5px;
|
|
left: $main-badge-size-x + $gap-x + $slot-x;
|
|
}
|
|
|
|
.hc-badge-container:nth-child(7) {
|
|
top: $offset-y + calc(-1 * $gap-y / 2) - 1px;
|
|
left: $main-badge-size-x + $gap-x + (2 * $slot-x);
|
|
}
|
|
|
|
.hc-badge-container:nth-child(8) {
|
|
top: $offset-y + $slot-y + calc($gap-y / 2) - 1px;
|
|
left: $main-badge-size-x + $gap-x + (2 * $slot-x);
|
|
}
|
|
|
|
.hc-badge-container:nth-child(9) {
|
|
top: $offset-y + $slot-y - calc($badge-size-y / 2) - $gap-y - 0.5px;
|
|
left: $main-badge-size-x + $gap-x + (3 * $slot-x);
|
|
}
|
|
|
|
.hc-badge-container:nth-child(10) {
|
|
top: $offset-y + ($badge-size-y * 1.5) + (2 * $gap-y) - 0.5px;
|
|
left: $main-badge-size-x + $gap-x + (3 * $slot-x);
|
|
}
|
|
}
|
|
</style>
|