Spaces:
Running
Running
| class ParkingGrid extends HTMLElement { | |
| static get observedAttributes() { | |
| return ['slots', 'slots-data']; | |
| } | |
| constructor() { | |
| super(); | |
| this.slots = 20; | |
| this.slotsData = Array(this.slots).fill(null); | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (name === 'slots') { | |
| this.slots = parseInt(newValue) || 20; | |
| this.render(); | |
| } | |
| if (name === 'slots-data') { | |
| try { | |
| this.slotsData = JSON.parse(newValue) || Array(this.slots).fill(null); | |
| this.render(); | |
| } catch (e) { | |
| console.error('Error parsing slots data', e); | |
| } | |
| } | |
| } | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.render(); | |
| } | |
| render() { | |
| const slotSize = this.slots <= 30 ? 'min-w-[100px]' : | |
| this.slots <= 50 ? 'min-w-[80px]' : 'min-w-[60px]'; | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| .parking-slot { | |
| transition: all 0.2s ease; | |
| } | |
| .occupied { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .occupied:after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: linear-gradient(135deg, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.2) 100%); | |
| } | |
| .slot-number { | |
| position: absolute; | |
| top: 4px; | |
| left: 4px; | |
| background: rgba(255,255,255,0.9); | |
| border-radius: 4px; | |
| padding: 0 4px; | |
| font-size: 10px; | |
| font-weight: bold; | |
| } | |
| .plate-number { | |
| position: absolute; | |
| bottom: 4px; | |
| left: 0; | |
| right: 0; | |
| text-align: center; | |
| font-size: 10px; | |
| font-weight: bold; | |
| color: white; | |
| text-shadow: 0 1px 2px rgba(0,0,0,0.5); | |
| } | |
| </style> | |
| <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3"> | |
| ${Array.from({ length: this.slots }, (_, i) => { | |
| const slotData = this.slotsData[i]; | |
| let bgColor = 'bg-gray-100'; | |
| let borderColor = 'border-gray-200'; | |
| let textColor = 'text-gray-800'; | |
| if (slotData) { | |
| switch(slotData.type) { | |
| case 'monthly-fixed': | |
| bgColor = 'bg-green-100'; | |
| borderColor = 'border-green-300'; | |
| textColor = 'text-green-800'; | |
| break; | |
| case 'monthly-rotating': | |
| bgColor = 'bg-yellow-100'; | |
| borderColor = 'border-yellow-300'; | |
| textColor = 'text-yellow-800'; | |
| break; | |
| case 'hourly': | |
| bgColor = 'bg-red-100'; | |
| borderColor = 'border-red-300'; | |
| textColor = 'text-red-800'; | |
| break; | |
| } | |
| } | |
| return ` | |
| <div class="parking-slot ${slotData ? 'occupied' : ''} ${bgColor} ${borderColor} border-2 rounded-md h-20 ${slotSize} relative cursor-pointer"> | |
| <span class="slot-number ${textColor}">${i + 1}</span> | |
| ${slotData ? ` | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <i data-feather="${slotData.keyDeposit ? 'key' : 'car'}" class="h-6 w-6 ${textColor}"></i> | |
| </div> | |
| <span class="plate-number">${slotData.plateNumber}</span> | |
| ` : ` | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <i data-feather="circle" class="h-6 w-6 text-gray-300"></i> | |
| </div> | |
| `} | |
| </div> | |
| `; | |
| }).join('')} | |
| </div> | |
| `; | |
| // Reemplazar íconos feather | |
| const featherIcons = this.shadowRoot.querySelectorAll('[data-feather]'); | |
| featherIcons.forEach(icon => { | |
| feather.replace(icon); | |
| }); | |
| } | |
| } | |
| customElements.define('parking-grid', ParkingGrid); |