Spaces:
Running
Running
| class TimelineSidebar extends HTMLElement { | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.render(); | |
| this.setupEventListeners(); | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| position: fixed; | |
| right: 0; | |
| top: 0; | |
| height: 100vh; | |
| width: 300px; | |
| background: #1f2937; | |
| border-left: 1px solid #374151; | |
| z-index: 30; | |
| transform: translateX(100%); | |
| transition: transform 0.3s ease; | |
| overflow-y: auto; | |
| } | |
| :host(.open) { | |
| transform: translateX(0); | |
| } | |
| .header { | |
| padding: 1rem; | |
| border-bottom: 1px solid #374151; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .timeline-container { | |
| padding: 1rem; | |
| } | |
| .timeline-hour { | |
| position: relative; | |
| min-height: 60px; | |
| border-bottom: 1px solid #374151; | |
| padding-left: 50px; | |
| } | |
| .hour-label { | |
| position: absolute; | |
| left: 10px; | |
| top: 5px; | |
| font-size: 12px; | |
| color: #9ca3af; | |
| } | |
| .task-drop-area { | |
| height: 100%; | |
| min-height: 60px; | |
| } | |
| .toggle-btn { | |
| position: fixed; | |
| right: 300px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: #1f2937; | |
| border: 1px solid #374151; | |
| border-right: none; | |
| padding: 1rem 0.5rem; | |
| border-radius: 5px 0 0 5px; | |
| cursor: pointer; | |
| z-index: 30; | |
| } | |
| .task-block { | |
| background: #374151; | |
| border-left: 3px solid #f97316; | |
| padding: 0.5rem; | |
| margin: 0.5rem 0; | |
| border-radius: 4px; | |
| } | |
| </style> | |
| <button class="toggle-btn"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="15 18 9 12 15 6"></polyline> | |
| </svg> | |
| </button> | |
| <div class="header"> | |
| <h3>Timeline</h3> | |
| <button id="close-timeline"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="18" y1="6" x2="6" y2="18"></line> | |
| <line x1="6" y1="6" x2="18" y2="18"></line> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="timeline-container" id="timeline-hours"> | |
| ${Array.from({ length: 24 }, (_, i) => ` | |
| <div class="timeline-hour" data-hour="${i}"> | |
| <span class="hour-label">${i.toString().padStart(2, '0')}:00</span> | |
| <div class="task-drop-area" data-hour="${i}"></div> | |
| </div> | |
| `).join('')} | |
| </div> | |
| `; | |
| } | |
| setupEventListeners() { | |
| const toggleBtn = this.shadowRoot.querySelector('.toggle-btn'); | |
| toggleBtn.addEventListener('click', () => { | |
| this.classList.toggle('open'); | |
| const icon = toggleBtn.querySelector('svg'); | |
| if (this.classList.contains('open')) { | |
| icon.innerHTML = '<polyline points="15 18 9 12 15 6"></polyline>'; | |
| } else { | |
| icon.innerHTML = '<polyline points="9 18 15 12 9 6"></polyline>'; | |
| } | |
| }); | |
| // Setup drag and drop | |
| this.shadowRoot.addEventListener('dragover', (e) => { | |
| if (e.target.classList.contains('task-drop-area')) { | |
| e.preventDefault(); | |
| e.target.style.backgroundColor = '#37415170'; | |
| } | |
| }); | |
| this.shadowRoot.addEventListener('dragleave', (e) => { | |
| if (e.target.classList.contains('task-drop-area')) { | |
| e.target.style.backgroundColor = ''; | |
| } | |
| }); | |
| this.shadowRoot.addEventListener('drop', (e) => { | |
| if (e.target.classList.contains('task-drop-area')) { | |
| e.preventDefault(); | |
| e.target.style.backgroundColor = ''; | |
| const taskId = e.dataTransfer.getData('text/plain'); | |
| const hour = parseInt(e.target.dataset.hour); | |
| this.dispatchEvent(new CustomEvent('task-dropped', { | |
| detail: { taskId, hour } | |
| })); | |
| } | |
| }); | |
| } | |
| } | |
| customElements.define('timeline-sidebar', TimelineSidebar); |