import { t, i18n } from '../i18n.js';
import { escapeHTML, formatPrice } from '../utils/helpers.js';
class ListingCard extends HTMLElement {
static get observedAttributes() {
return ['listing-id', 'title', 'price', 'currency', 'location', 'image'];
}
constructor() {
super();
this.isFavorite = false;
}
connectedCallback() {
this.loadFavoriteState();
this.render();
this.setupEventListeners();
}
attributeChangedCallback() {
if (this.isConnected) {
this.render();
this.setupEventListeners();
}
}
loadFavoriteState() {
const id = this.getAttribute('listing-id');
if (id) {
const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
this.isFavorite = favorites.includes(id);
}
}
saveFavoriteState() {
const id = this.getAttribute('listing-id');
if (!id) return;
let favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
if (this.isFavorite) {
if (!favorites.includes(id)) favorites.push(id);
} else {
favorites = favorites.filter(f => f !== id);
}
localStorage.setItem('favorites', JSON.stringify(favorites));
}
render() {
const id = this.getAttribute('listing-id') || '';
const title = this.getAttribute('title') || t('home.placeholderTitle');
const price = this.getAttribute('price');
const currency = this.getAttribute('currency') || 'EUR';
const location = this.getAttribute('location') || t('home.placeholderLocation');
const image = this.getAttribute('image');
const priceDisplay = price ? formatPrice(parseFloat(price), currency) : '–';
const favoriteLabel = this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite');
this.innerHTML = /* html */ `
${escapeHTML(title)}
${priceDisplay}
${escapeHTML(location)}
`;
}
setupEventListeners() {
const btn = this.querySelector('.favorite-btn');
btn?.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
this.toggleFavorite();
});
}
toggleFavorite() {
this.isFavorite = !this.isFavorite;
this.saveFavoriteState();
const btn = this.querySelector('.favorite-btn');
btn?.classList.toggle('active', this.isFavorite);
btn?.setAttribute('aria-pressed', this.isFavorite);
btn?.setAttribute('aria-label', this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite'));
btn?.classList.add('animate__animated', 'animate__heartBeat');
btn?.addEventListener('animationend', () => {
btn?.classList.remove('animate__animated', 'animate__heartBeat');
}, { once: true });
this.dispatchEvent(new CustomEvent('favorite-toggle', {
bubbles: true,
detail: { id: this.getAttribute('listing-id'), isFavorite: this.isFavorite }
}));
}
}
customElements.define('listing-card', ListingCard);
const style = document.createElement('style');
style.textContent = /* css */ `
listing-card {
display: block;
position: relative;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
overflow: hidden;
transition: box-shadow var(--transition-fast);
}
listing-card:hover {
box-shadow: var(--shadow-md);
}
listing-card .listing-link {
display: block;
text-decoration: none;
color: inherit;
}
listing-card .listing-image {
aspect-ratio: 1;
background: var(--color-bg-tertiary);
background-size: cover;
background-position: center;
}
listing-card .listing-info {
padding: var(--space-sm);
}
listing-card .listing-title {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
margin: 0 0 var(--space-xs);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
listing-card .listing-price {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-bold);
color: var(--color-primary);
margin: 0;
}
listing-card .listing-location {
font-size: var(--font-size-xs);
color: var(--color-text-muted);
margin: 0;
}
listing-card .favorite-btn {
position: absolute;
top: var(--space-sm);
right: var(--space-sm);
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-bg);
border: none;
border-radius: var(--radius-full);
cursor: pointer;
box-shadow: var(--shadow-sm);
transition: all var(--transition-fast);
z-index: 1;
}
listing-card .favorite-btn:hover {
transform: scale(1.1);
}
listing-card .favorite-btn .heart-icon {
color: var(--color-text-muted);
transition: all var(--transition-fast);
}
listing-card .favorite-btn.active .heart-icon {
fill: var(--color-error);
stroke: var(--color-error);
color: var(--color-error);
}
listing-card .favorite-btn:hover .heart-icon {
color: var(--color-error);
}
`;
document.head.appendChild(style);