// Заміна вмісту таблиці стилів на надані CSS-правила. const CARD_STYLES_CSS = ` :host { display: inline-block; box-sizing: border-box; width: 300px; height: 200px; } @media (max-width: 2300px) { :host { width: calc((100% / 5) - 40px); } } @media (max-width: 1960px) { :host { width: calc((100% / 4) - 40px); } } @media (max-width: 1640px) { :host { width: calc((100% / 3) - 40px); } } @media (max-width: 1280px) { :host { width: calc((100% / 2) - 40px); } } @media (max-width: 650px) { :host { width: 100%; } } .card { position: relative; width: 100%; height: 200px; background-color: var(--ColorThemes2, #525151); overflow: hidden; cursor: pointer; border-radius: calc(var(--border-radius, 15px) - 5px); } @media(hover: hover) { .card:hover { opacity: 0.8; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); } } img { width: 100%; height: 100%; object-fit: cover; position: relative; z-index: 1; filter: blur(3px); border-radius: calc(var(--border-radius, 15px) - 5px); } .contents { position: absolute; top: 0; left: 0; z-index: 2; background: rgb(64 64 64 / 0.7); width: 100%; height: 100%; display: flex; flex-direction: column; align-items: stretch; justify-content: space-between; border-radius: calc(var(--border-radius, 15px) - 5px); } .address { margin: 10px; height: 35px; display: flex; background: var(--ColorThemes0, #1c1c19); align-items: center; justify-content: center; font-size: var(--FontSize3, 14px); color: var(--ColorThemes3, #f3f3f3); border-radius: calc(var(--border-radius, 15px) - 5px - 4px); position: relative; overflow: hidden; font-weight: 400; } /* Стилі для режиму 'sheep' */ .sheep { margin: 10px; max-height: 50px; border-radius: calc(var(--border-radius, 15px) - 5px - 4px); padding: 10px 0; margin-top: 10px; display: flex; background: var(--PrimaryColor, #cb9e44); align-items: center; flex-direction: column; justify-content: space-around; } .sheep span { color: var(--PrimaryColorText, #2e2e2e); font-size: var(--FontSize3, 14px); font-weight: 400; opacity: 0.8; } .sheep p { color: var(--PrimaryColorText, #2e2e2e); font-size: var(--FontSize4, 15px); font-weight: 400; margin: 5px 0 0 0; } /* Стилі для режиму 'info' (прогресс) */ .info { margin: 10px; } .info > div { position: relative; background-color: var(--ColorThemes0, #1c1c19);; border-radius: calc(var(--border-radius, 15px) - 5px - 4px); overflow: hidden; padding: 5px 10px; display: flex; justify-content: space-between; align-items: center; min-height: 25px; } .info span { z-index: 2; font-size: var(--FontSize3, 14px); color: var(--ColorThemes3, #f3f3f3); } .info p { z-index: 2; margin: 0; font-weight: 500; font-size: var(--FontSize3, 14px); color: var(--ColorThemes3, #f3f3f3); } .progress { position: absolute; top: 0; left: 0; height: 100%; background-color: var(--PrimaryColor, #cb9e44); transition: width 0.3s ease; } a { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10; border-radius: calc(var(--border-radius, 15px) - 5px); } `; // Створення об'єкта CSSStyleSheet (якщо підтримується) let appTerritoryCardStyles = null; if (typeof CSSStyleSheet !== 'undefined' && CSSStyleSheet.prototype.replaceSync) { appTerritoryCardStyles = new CSSStyleSheet(); // (2) Визначення об'єкта тут appTerritoryCardStyles.replaceSync(CARD_STYLES_CSS); } /** * Веб-компонент AppTerritoryCard. * Відображає картку території з фоновим зображенням та різними режимами відображення. */ class AppTerritoryCard extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); // Додаємо стилі в конструкторі, якщо це adoptable if (this.shadowRoot.adoptedStyleSheets) { this.shadowRoot.adoptedStyleSheets = [appTerritoryCardStyles]; } else { // FALLBACK для старих браузерів (наприклад, iOS < 16.4) const style = document.createElement('style'); style.textContent = CARD_STYLES_CSS; this.shadowRoot.appendChild(style); } } // Вказуємо, які атрибути ми хочемо відстежувати static get observedAttributes() { return ['image', 'address', 'sheep', 'link', 'atWork', 'quantity', 'overdue']; } // Геттери та Сеттери для атрибутів // Вони спрощують роботу з атрибутами як з властивостями DOM-елемента get image() { return this.getAttribute('image'); } set image(newValue) { if (newValue === null) { this.removeAttribute('image'); } else { this.setAttribute('image', newValue); } } get address() { return this.getAttribute('address'); } set address(newValue) { if (newValue === null) { this.removeAttribute('address'); } else { this.setAttribute('address', newValue); } } /** * Атрибут 'sheep' може приймати три стани: * 1. null / відсутній: відключення блоку sheep та info * 2. порожній рядок ('') / присутній без значення: Режим "Територія не опрацьовується" * 3. рядок зі значенням: Режим "Територію опрацьовує: [значення]" */ get sheep() { return this.getAttribute('sheep'); } set sheep(newValue) { if (newValue === null) { this.removeAttribute('sheep'); } else { this.setAttribute('sheep', newValue); } } get link() { return this.getAttribute('link'); } set link(newValue) { if (newValue === null) { this.removeAttribute('link'); } else { this.setAttribute('link', newValue); } } get atWork() { return this.getAttribute('atWork'); } set atWork(newValue) { if (newValue === null) { this.removeAttribute('atWork'); } else { // Приводимо до рядка, оскільки атрибути завжди є рядками this.setAttribute('atWork', String(newValue)); } } get quantity() { return this.getAttribute('quantity'); } set quantity(newValue) { if (newValue === null) { this.removeAttribute('quantity'); } else { // Приводимо до рядка this.setAttribute('quantity', String(newValue)); } } get overdue() { return this.getAttribute('address'); } set overdue(newValue) { if (newValue === null) { this.removeAttribute('overdue'); } else { this.setAttribute('overdue', newValue); } } /** * connectedCallback викликається, коли елемент додається в DOM. * Тут ми викликаємо початкове рендеринг. */ connectedCallback() { this.render(); } /** * attributeChangedCallback викликається при зміні одного зі спостережуваних атрибутів. */ attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { this.render(); // Перерендеринг при зміні атрибута } } /** * Логіка рендерингу (відображення) вмісту компонента. */ render() { const image = this.getAttribute('image') || ''; const address = this.getAttribute('address') || ''; const sheep = this.getAttribute('sheep'); const link = this.getAttribute('link') || '#'; const atWork = this.getAttribute('atWork'); const quantity = this.getAttribute('quantity'); const overdue = this.getAttribute('overdue') == 'true' ? true : false; this.shadowRoot.innerHTML = ``; // Додаємо стилі для старих браузерів if (!this.shadowRoot.adoptedStyleSheets) { const style = document.createElement('style'); style.textContent = CARD_STYLES_CSS; this.shadowRoot.appendChild(style); } let contentHTML = ''; // Перевіряємо, чи має бути увімкнений режим прогресу ('info'): // обидва атрибути 'atWork' та 'quantity' присутні і є коректними числами. const isProgressMode = atWork !== null && quantity !== null && !isNaN(parseInt(atWork)) && !isNaN(parseInt(quantity)); if (isProgressMode) { // Режим прогресу (вільні під'їзди) const atWorkNum = parseInt(atWork); const quantityNum = parseInt(quantity); const free = quantityNum - atWorkNum; // Обчислення відсотка прогресу. Уникнення ділення на нуль. const progressPercent = quantityNum > 0 ? (atWorkNum / quantityNum) * 100 : 100; contentHTML = `
${free} / ${quantityNum}
${sheep}