import { FolkShape } from "./folk-shape"; import { css, html } from "./tags"; const styles = css` :host { background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); min-width: 260px; min-height: 180px; } .header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: #059669; color: white; border-radius: 8px 8px 0 0; font-size: 12px; font-weight: 600; cursor: move; } .header-title { display: flex; align-items: center; gap: 6px; } .header-actions button { background: transparent; border: none; color: white; cursor: pointer; padding: 2px 6px; border-radius: 4px; font-size: 14px; } .header-actions button:hover { background: rgba(255, 255, 255, 0.2); } .dest-body { padding: 12px; } .dest-name { font-size: 16px; font-weight: 700; color: #1e293b; margin-bottom: 2px; } .dest-country { font-size: 12px; color: #64748b; margin-bottom: 8px; } .dest-dates { display: flex; gap: 12px; font-size: 11px; color: #475569; margin-bottom: 10px; padding: 6px 8px; background: #f1f5f9; border-radius: 4px; } .dest-dates span { display: flex; align-items: center; gap: 4px; } .dest-dates .label { color: #94a3b8; font-weight: 500; } .dest-notes { font-size: 12px; color: #475569; line-height: 1.5; padding: 8px; background: #fafaf9; border-radius: 4px; border: 1px solid #e2e8f0; min-height: 40px; white-space: pre-wrap; } .dest-notes[contenteditable]:focus { outline: none; border-color: #059669; } .coords { font-size: 10px; color: #94a3b8; margin-top: 6px; } .empty-name { color: #94a3b8; font-style: italic; } `; declare global { interface HTMLElementTagNameMap { "folk-destination": FolkDestination; } } export class FolkDestination extends FolkShape { static override tagName = "folk-destination"; static { const sheet = new CSSStyleSheet(); const parentRules = Array.from(FolkShape.styles.cssRules) .map((r) => r.cssText) .join("\n"); const childRules = Array.from(styles.cssRules) .map((r) => r.cssText) .join("\n"); sheet.replaceSync(`${parentRules}\n${childRules}`); this.styles = sheet; } #destName = ""; #country = ""; #lat: number | null = null; #lng: number | null = null; #arrivalDate = ""; #departureDate = ""; #notes = ""; #nameEl: HTMLElement | null = null; #countryEl: HTMLElement | null = null; #notesEl: HTMLElement | null = null; #datesEl: HTMLElement | null = null; get destName() { return this.#destName; } set destName(v: string) { this.#destName = v; if (this.#nameEl) this.#nameEl.textContent = v || "Unnamed destination"; this.dispatchEvent(new CustomEvent("content-change")); } get country() { return this.#country; } set country(v: string) { this.#country = v; if (this.#countryEl) this.#countryEl.textContent = v; this.dispatchEvent(new CustomEvent("content-change")); } get lat() { return this.#lat; } set lat(v: number | null) { this.#lat = v; } get lng() { return this.#lng; } set lng(v: number | null) { this.#lng = v; } get arrivalDate() { return this.#arrivalDate; } set arrivalDate(v: string) { this.#arrivalDate = v; this.#renderDates(); this.dispatchEvent(new CustomEvent("content-change")); } get departureDate() { return this.#departureDate; } set departureDate(v: string) { this.#departureDate = v; this.#renderDates(); this.dispatchEvent(new CustomEvent("content-change")); } get notes() { return this.#notes; } set notes(v: string) { this.#notes = v; if (this.#notesEl && this.#notesEl.textContent !== v) { this.#notesEl.textContent = v; } this.dispatchEvent(new CustomEvent("content-change")); } override createRenderRoot() { const root = super.createRenderRoot(); const wrapper = document.createElement("div"); wrapper.innerHTML = html`
\uD83D\uDCCD Destination
`; const slot = root.querySelector("slot"); const containerDiv = slot?.parentElement as HTMLElement; if (containerDiv) { containerDiv.replaceWith(wrapper); } this.#nameEl = wrapper.querySelector(".dest-name"); this.#countryEl = wrapper.querySelector(".dest-country"); this.#datesEl = wrapper.querySelector(".dest-dates"); this.#notesEl = wrapper.querySelector(".dest-notes"); if (this.#nameEl) { this.#nameEl.textContent = this.#destName || "Unnamed destination"; if (!this.#destName) this.#nameEl.classList.add("empty-name"); } if (this.#countryEl) this.#countryEl.textContent = this.#country; const closeBtn = wrapper.querySelector(".close-btn") as HTMLButtonElement; closeBtn.addEventListener("click", (e) => { e.stopPropagation(); this.dispatchEvent(new CustomEvent("close")); }); if (this.#notesEl) { this.#notesEl.textContent = this.#notes; this.#notesEl.addEventListener("input", () => { this.#notes = this.#notesEl!.textContent || ""; this.dispatchEvent(new CustomEvent("content-change")); }); this.#notesEl.addEventListener("click", (e) => e.stopPropagation()); } this.#renderDates(); return root; } #renderDates() { if (!this.#datesEl) return; if (!this.#arrivalDate && !this.#departureDate) { this.#datesEl.style.display = "none"; return; } this.#datesEl.style.display = "flex"; let result = ""; if (this.#arrivalDate) { const d = new Date(this.#arrivalDate); result += `Arrive: ${d.toLocaleDateString("en-US", { month: "short", day: "numeric" })}`; } if (this.#departureDate) { const d = new Date(this.#departureDate); result += `Depart: ${d.toLocaleDateString("en-US", { month: "short", day: "numeric" })}`; } this.#datesEl.innerHTML = result; } override toJSON() { return { ...super.toJSON(), type: "folk-destination", destName: this.#destName, country: this.#country, lat: this.#lat, lng: this.#lng, arrivalDate: this.#arrivalDate, departureDate: this.#departureDate, notes: this.#notes, }; } }