12245759ac
CI / test (push) Has been cancelled
Web app: new entities (Image, RenderedAsset, SharedImage, Token, DeviceImageHistory), enums, repositories, controllers, message handlers, migrations, tests, frontend upload/library/sticker UI, Vue components. Firmware: EPD background screen binaries + gen scripts, setup_bg header. Infra: ddev config, test bundle, gitignore coverage dir. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
116 lines
3.0 KiB
Vue
116 lines
3.0 KiB
Vue
<template>
|
|
<BaseBottomSheet :model-value="modelValue" label="Add sticker" @update:model-value="$emit('update:modelValue', $event)">
|
|
<div class="sticker-tray">
|
|
<div class="sticker-tray__cats" role="tablist">
|
|
<button
|
|
v-for="cat in STICKER_CATEGORIES"
|
|
:key="cat.id"
|
|
type="button"
|
|
role="tab"
|
|
:class="['sticker-tray__cat', { 'sticker-tray__cat--active': activeCategory === cat.id }]"
|
|
@click="activeCategory = cat.id"
|
|
>{{ cat.label }}</button>
|
|
</div>
|
|
<div class="sticker-tray__grid" role="tabpanel">
|
|
<button
|
|
v-for="s in visibleStickers"
|
|
:key="s.id"
|
|
type="button"
|
|
class="sticker-tray__item"
|
|
:aria-label="s.label"
|
|
@click="$emit('pick', s.id)"
|
|
>
|
|
<span class="sticker-tray__emoji" aria-hidden="true">{{ s.emoji }}</span>
|
|
<span class="sticker-tray__label">{{ s.label }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</BaseBottomSheet>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import BaseBottomSheet from '@/components/BaseBottomSheet.vue'
|
|
import { STICKERS, STICKER_CATEGORIES } from '@/assets/stickers/index'
|
|
import type { StickerCategory } from '@/assets/stickers/index'
|
|
|
|
defineProps<{ modelValue: boolean }>()
|
|
defineEmits<{
|
|
(e: 'update:modelValue', v: boolean): void
|
|
(e: 'pick', stickerId: string): void
|
|
}>()
|
|
|
|
const activeCategory = ref<StickerCategory>('seasonal')
|
|
|
|
const visibleStickers = computed(() =>
|
|
STICKERS.filter(s => s.category === activeCategory.value)
|
|
)
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.sticker-tray {
|
|
&__cats {
|
|
display: flex;
|
|
gap: var(--space-2);
|
|
overflow-x: auto;
|
|
padding-bottom: var(--space-3);
|
|
scrollbar-width: none;
|
|
&::-webkit-scrollbar { display: none; }
|
|
}
|
|
|
|
&__cat {
|
|
padding: 6px 14px;
|
|
border-radius: 999px;
|
|
border: 1.5px solid var(--color-border);
|
|
font-size: var(--text-sm);
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
background: transparent;
|
|
color: var(--color-text-muted);
|
|
transition: all var(--duration-fast);
|
|
|
|
&--active {
|
|
background: var(--color-primary);
|
|
border-color: var(--color-primary);
|
|
color: var(--color-primary-fg);
|
|
}
|
|
}
|
|
|
|
&__grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
gap: var(--space-2);
|
|
}
|
|
|
|
&__item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: var(--space-2) var(--space-1);
|
|
border-radius: var(--radius-sm);
|
|
border: none;
|
|
background: transparent;
|
|
cursor: pointer;
|
|
transition: background var(--duration-fast);
|
|
|
|
&:active { background: var(--color-surface-2); }
|
|
}
|
|
|
|
&__emoji {
|
|
font-size: 36px;
|
|
line-height: 1;
|
|
font-family: "Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif;
|
|
}
|
|
|
|
&__label {
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
color: var(--color-text-muted);
|
|
text-align: center;
|
|
line-height: 1.2;
|
|
}
|
|
}
|
|
</style>
|