馃 distributed transcription service thistle.dunkirk.sh
1import { css, html, LitElement } from "lit"; 2import { customElement, property, state } from "lit/decorators.js"; 3 4export interface MeetingTime { 5 day: string; 6 startTime: string; 7 endTime: string; 8 label: string; 9} 10 11interface DayState { 12 day: string; 13 shortName: string; 14 selected: boolean; 15} 16 17@customElement("meeting-time-picker") 18export class MeetingTimePicker extends LitElement { 19 @property({ type: Array }) value: MeetingTime[] = []; 20 21 @state() private days: DayState[] = [ 22 { day: "Monday", shortName: "Mon", selected: false }, 23 { day: "Tuesday", shortName: "Tue", selected: false }, 24 { day: "Wednesday", shortName: "Wed", selected: false }, 25 { day: "Thursday", shortName: "Thu", selected: false }, 26 { day: "Friday", shortName: "Fri", selected: false }, 27 { day: "Saturday", shortName: "Sat", selected: false }, 28 { day: "Sunday", shortName: "Sun", selected: false }, 29 ]; 30 31 static override styles = css` 32 :host { 33 display: block; 34 } 35 36 .day-selector { 37 display: flex; 38 gap: 0.5rem; 39 flex-wrap: wrap; 40 } 41 42 .day-button { 43 flex: 1; 44 min-width: 3.5rem; 45 padding: 0.75rem 0.5rem; 46 background: var(--background); 47 border: 2px solid var(--secondary); 48 border-radius: 6px; 49 font-size: 0.875rem; 50 font-weight: 500; 51 cursor: pointer; 52 transition: all 0.2s; 53 font-family: inherit; 54 color: var(--text); 55 } 56 57 .day-button:hover { 58 border-color: var(--primary); 59 } 60 61 .day-button.selected { 62 background: var(--primary); 63 border-color: var(--primary); 64 color: white; 65 } 66 67 .helper-text { 68 margin-top: 0.5rem; 69 font-size: 0.75rem; 70 color: var(--paynes-gray); 71 } 72 `; 73 74 override connectedCallback() { 75 super.connectedCallback(); 76 this.loadFromValue(); 77 } 78 79 override updated(changedProperties: Map<string, unknown>) { 80 if (changedProperties.has("value")) { 81 this.loadFromValue(); 82 } 83 } 84 85 private loadFromValue() { 86 // Always reset all days first 87 this.days = this.days.map((d) => ({ ...d, selected: false })); 88 89 // If no value, we're done 90 if (!this.value || this.value.length === 0) return; 91 92 // Load from value 93 for (const meeting of this.value) { 94 const dayIndex = this.days.findIndex((d) => d.day === meeting.day); 95 if (dayIndex !== -1) { 96 this.days = this.days.map((d, i) => 97 i === dayIndex ? { ...d, selected: true } : d, 98 ); 99 } 100 } 101 } 102 103 private toggleDay(index: number) { 104 this.days = this.days.map((d, i) => 105 i === index ? { ...d, selected: !d.selected } : d, 106 ); 107 this.dispatchChange(); 108 } 109 110 private dispatchChange() { 111 const selectedDays = this.days 112 .filter((d) => d.selected) 113 .map((d) => ({ 114 day: d.day, 115 startTime: "", 116 endTime: "", 117 label: d.day, 118 })); 119 120 this.dispatchEvent( 121 new CustomEvent("change", { 122 detail: selectedDays, 123 bubbles: true, 124 composed: true, 125 }), 126 ); 127 } 128 129 override render() { 130 return html` 131 <div class="day-selector"> 132 ${this.days.map( 133 (day, index) => html` 134 <button 135 type="button" 136 class="day-button ${day.selected ? "selected" : ""}" 137 @click=${() => this.toggleDay(index)} 138 > 139 ${day.shortName} 140 </button> 141 `, 142 )} 143 </div> 144 145 <div class="helper-text"> 146 Click on days to select when this class meets 147 </div> 148 `; 149 } 150}