ENH - refine CityMarkt glass interface

This commit is contained in:
smx.pusha
2026-08-08 21:49:38 +02:00
parent 3cc8292748
commit 1124f04d76
5 changed files with 1456 additions and 182 deletions
@@ -65,4 +65,7 @@ const formattedAmount = computed(() =>
<style scoped>
.citymarkt-offer{width:92%;padding:11px;border:1px solid #ffc92842;border-radius:14px;align-self:flex-start;background:linear-gradient(145deg,#332d19,var(--panel));box-shadow:0 7px 18px #0003}.citymarkt-offer--own{align-self:flex-end}.citymarkt-offer header{display:flex;align-items:center;gap:8px}.citymarkt-offer header>span{width:32px;height:32px;flex:none;border-radius:10px;display:grid;place-items:center;background:var(--yellow);color:#171816}.citymarkt-offer header>div{min-width:0;flex:1}.citymarkt-offer header small,.citymarkt-offer header strong{display:block}.citymarkt-offer header small{color:var(--muted);font-size:9px;font-weight:800;letter-spacing:.02em;text-transform:uppercase}.citymarkt-offer header strong{margin-top:1px;font-size:18px}.citymarkt-offer header i{padding:5px 7px;border-radius:7px;background:#ffc92817;color:var(--yellow);font-size:8px;font-style:normal;font-weight:900;text-transform:uppercase}.citymarkt-offer>p{margin:7px 0 0;color:var(--muted);font-size:9px;line-height:1.35}.citymarkt-offer--accepted{border-color:#54d68173;background:linear-gradient(145deg,#193526,var(--panel))}.citymarkt-offer--accepted header>span{background:#54d681}.citymarkt-offer--accepted header i{background:#54d6811c;color:#67e494}.citymarkt-offer--rejected,.citymarkt-offer--countered{border-color:#ffffff14;filter:saturate(.65)}.citymarkt-offer--rejected header>span,.citymarkt-offer--countered header>span{background:#555750;color:#ddd}.citymarkt-offer--rejected header i,.citymarkt-offer--countered header i{background:#ffffff0c;color:var(--muted)}.citymarkt-offer__actions{margin-top:10px;display:grid;grid-template-columns:1fr 1fr;gap:6px}.citymarkt-offer__actions button{min-height:35px;padding:7px 6px;border:1px solid #ffffff12;border-radius:10px;display:flex;align-items:center;justify-content:center;gap:4px;background:#ffffff09;color:inherit;font-size:9.5px;font-weight:850;line-height:1.1}.citymarkt-offer__actions button.accept{border:0;background:#54d681;color:#102319}.citymarkt-offer__actions button.reject{grid-column:1/-1;color:#ff8078}:global(.citymarkt--light) .citymarkt-offer{background:linear-gradient(145deg,#fff8d9,#fff);box-shadow:0 7px 18px #0001}:global(.citymarkt--light) .citymarkt-offer--accepted{background:linear-gradient(145deg,#e6faed,#fff)}
.citymarkt-offer{padding:12px}.citymarkt-offer header>span{width:35px;height:35px}.citymarkt-offer header small{font-size:10.5px}.citymarkt-offer header strong{font-size:20px}.citymarkt-offer header i{padding:5px 8px;font-size:9.5px}.citymarkt-offer>p{margin-top:8px;font-size:11.5px}.citymarkt-offer__actions{margin-top:11px;gap:7px}.citymarkt-offer__actions button{min-height:40px;padding:8px;font-size:12px;font-weight:850;gap:5px}.citymarkt-offer__actions button svg{width:15px;height:15px}
.citymarkt-offer:not(.citymarkt-offer--accepted) {
border-color: transparent;
}
</style>
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { Check, ChevronDown } from 'lucide-vue-next'
import { kGlass } from 'konsta/vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
type SelectOption = {
@@ -20,7 +21,9 @@ const root = ref<HTMLElement | null>(null)
const isOpen = ref(false)
const highlightedIndex = ref(0)
const selectedLabel = computed(
() => props.options.find((option) => option.value === props.modelValue)?.label ?? '',
() =>
props.options.find((option) => option.value === props.modelValue)?.label ??
'',
)
function open(): void {
@@ -44,7 +47,8 @@ function handleKeydown(event: KeyboardEvent): void {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
if (!isOpen.value) open()
else select(props.options[highlightedIndex.value]?.value ?? props.modelValue)
else
select(props.options[highlightedIndex.value]?.value ?? props.modelValue)
return
}
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return
@@ -52,7 +56,8 @@ function handleKeydown(event: KeyboardEvent): void {
if (!isOpen.value) open()
const direction = event.key === 'ArrowDown' ? 1 : -1
highlightedIndex.value =
(highlightedIndex.value + direction + props.options.length) % props.options.length
(highlightedIndex.value + direction + props.options.length) %
props.options.length
}
function handleOutsidePointer(event: PointerEvent): void {
@@ -60,12 +65,15 @@ function handleOutsidePointer(event: PointerEvent): void {
}
onMounted(() => window.addEventListener('pointerdown', handleOutsidePointer))
onUnmounted(() => window.removeEventListener('pointerdown', handleOutsidePointer))
onUnmounted(() =>
window.removeEventListener('pointerdown', handleOutsidePointer),
)
</script>
<template>
<div ref="root" class="citymarkt-select" @keydown="handleKeydown">
<button
<k-glass
component="button"
class="citymarkt-select__trigger"
type="button"
aria-haspopup="listbox"
@@ -74,7 +82,7 @@ onUnmounted(() => window.removeEventListener('pointerdown', handleOutsidePointer
>
<span>{{ selectedLabel }}</span>
<ChevronDown :size="14" :class="{ open: isOpen }" />
</button>
</k-glass>
<Transition name="citymarkt-select">
<div v-if="isOpen" class="citymarkt-select__menu" role="listbox">
@@ -100,7 +108,105 @@ onUnmounted(() => window.removeEventListener('pointerdown', handleOutsidePointer
</template>
<style scoped>
.citymarkt-select{position:relative;min-width:0}.citymarkt-select__trigger{width:100%;height:36px;padding:0 10px;border:1px solid #ffffff0d;border-radius:10px;display:flex;align-items:center;justify-content:space-between;gap:6px;background:var(--panel);color:inherit;font-size:10px;text-align:left}.citymarkt-select__trigger span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.citymarkt-select__trigger svg{flex:none;color:var(--yellow);transition:transform .18s ease}.citymarkt-select__trigger svg.open{transform:rotate(180deg)}.citymarkt-select__menu{position:absolute;z-index:12;top:calc(100% + 5px);right:0;left:0;max-height:176px;padding:4px;border:1px solid #ffffff16;border-radius:11px;overflow-y:auto;background:#292a27;box-shadow:0 12px 28px #0009;scrollbar-width:none}:global(.citymarkt--light) .citymarkt-select__menu{border-color:#00000014;background:#fff;box-shadow:0 12px 28px #0003}.citymarkt-select__menu button{width:100%;min-height:31px;padding:6px 7px;border:0;border-radius:8px;display:flex;align-items:center;justify-content:space-between;gap:5px;background:none;color:var(--muted);font-size:10px;text-align:left}.citymarkt-select__menu button.highlighted{background:#ffffff0b;color:inherit}:global(.citymarkt--light) .citymarkt-select__menu button.highlighted{background:#0000000b}.citymarkt-select__menu button.selected{color:var(--yellow);font-weight:800}.citymarkt-select-enter-active,.citymarkt-select-leave-active{transition:opacity .15s ease,transform .15s ease}.citymarkt-select-enter-from,.citymarkt-select-leave-to{opacity:0;transform:translateY(-4px) scale(.98)}
.citymarkt-select__trigger{height:40px;font-size:13px}
.citymarkt-select__menu button{min-height:36px;padding:7px 8px;font-size:12px}
.citymarkt-select {
position: relative;
min-width: 0;
}
.citymarkt-select__trigger {
width: 100%;
height: 36px;
padding: 0 10px;
border: 0;
border-radius: 9999px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
color: inherit;
font-size: 10px;
text-align: left;
}
.citymarkt-select__trigger span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.citymarkt-select__trigger svg {
flex: none;
color: var(--yellow);
transition: transform 0.18s ease;
}
.citymarkt-select__trigger svg.open {
transform: rotate(180deg);
}
.citymarkt-select__menu {
position: absolute;
z-index: 12;
top: calc(100% + 5px);
right: 0;
left: 0;
max-height: 176px;
padding: 4px;
border: 1px solid #ffffff14;
border-radius: 11px;
overflow-y: auto;
background: #292a27;
box-shadow: 0 12px 28px #0009;
scrollbar-width: none;
}
:global(.citymarkt--light) .citymarkt-select__menu {
border-color: #00000012;
background: #fff;
box-shadow: 0 12px 28px #0003;
}
.citymarkt-select__menu button {
width: 100%;
min-height: 31px;
padding: 6px 7px;
border: 0;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 5px;
background: none;
color: var(--muted);
font-size: 10px;
text-align: left;
}
.citymarkt-select__menu button.highlighted {
background: #ffffff0b;
color: inherit;
}
:global(.citymarkt--light) .citymarkt-select__menu button.highlighted {
background: #0000000b;
}
.citymarkt-select__menu button.selected {
background: #ffffff14;
color: inherit;
font-weight: 800;
}
:global(.citymarkt--light) .citymarkt-select__menu button.selected {
background: #0000000d;
}
.citymarkt-select-enter-active,
.citymarkt-select-leave-active {
transition:
opacity 0.15s ease,
transform 0.15s ease;
}
.citymarkt-select-enter-from,
.citymarkt-select-leave-to {
opacity: 0;
transform: translateY(-4px) scale(0.98);
}
.citymarkt-select__trigger {
height: 40px;
font-size: 13px;
}
.citymarkt-select__menu button {
min-height: 36px;
padding: 7px 8px;
font-size: 12px;
}
</style>
+1 -1
View File
@@ -496,7 +496,7 @@ const defaultLocales: LocaleTree = {
reportReasons: { prohibited: 'Prohibited item', fraud: 'Suspected fraud', spam: 'Spam', offensive: 'Offensive content', other: 'Other' },
searchPlaceholder: 'What are you looking for?', allCategories: 'All categories', allDistricts: 'All districts',
sortNewest: 'Newest first', sortPriceAsc: 'Lowest price', sortPriceDesc: 'Highest price',
freshOffers: 'Fresh offers', offers: 'offers', noListings: 'No offers found',
freshOffers: 'Fresh offers', offers: 'offers', compactView: 'Compact view', largeView: 'Large view', noListings: 'No offers found',
noListingsBody: 'Try another search or category.', noDistrict: 'No district',
free: 'Free', negotiablePrice: '${price} negotiable', money: '${price}',
hoursAgo: '{count}h ago', daysAgo: '{count}d ago', photos: 'photos', activeListings: 'active listings',
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -354,7 +354,7 @@ Locales["en"] = {
reportReasons = { prohibited = "Prohibited item", fraud = "Suspected fraud", spam = "Spam", offensive = "Offensive content", other = "Other" },
searchPlaceholder = "What are you looking for?", allCategories = "All categories", allDistricts = "All districts",
sortNewest = "Newest first", sortPriceAsc = "Lowest price", sortPriceDesc = "Highest price",
freshOffers = "Fresh offers", offers = "offers", noListings = "No offers found",
freshOffers = "Fresh offers", offers = "offers", compactView = "Compact view", largeView = "Large view", noListings = "No offers found",
noListingsBody = "Try another search or category.", noDistrict = "No district",
free = "Free", negotiablePrice = "${price} negotiable", money = "${price}",
hoursAgo = "{count}h ago", daysAgo = "{count}d ago", photos = "photos", activeListings = "active listings",