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: 200px; } .header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: #7c3aed; 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); } .progress-info { padding: 8px 12px; font-size: 11px; color: #64748b; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e2e8f0; } .progress-bar { width: 80px; height: 6px; background: #e2e8f0; border-radius: 3px; overflow: hidden; } .progress-fill { height: 100%; background: #7c3aed; border-radius: 3px; transition: width 0.3s ease; } .packing-list { padding: 8px 12px; max-height: 300px; overflow-y: auto; } .category-label { font-size: 10px; font-weight: 600; color: #7c3aed; text-transform: uppercase; letter-spacing: 0.5px; padding: 6px 0 3px; } .item { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12px; cursor: pointer; } .item:hover { background: #f8fafc; border-radius: 4px; padding: 4px 6px; margin: 0 -6px; } .checkbox { width: 16px; height: 16px; border: 2px solid #cbd5e1; border-radius: 4px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; } .checkbox.checked { background: #7c3aed; border-color: #7c3aed; } .checkbox.checked::after { content: "\u2713"; color: white; font-size: 10px; font-weight: 700; } .item-name { color: #1e293b; } .item-name.packed { text-decoration: line-through; color: #94a3b8; } .item-qty { font-size: 10px; color: #94a3b8; margin-left: auto; } .add-form { padding: 8px 12px; border-top: 1px solid #e2e8f0; } .add-form input { width: 100%; padding: 6px 8px; border: 1px solid #e2e8f0; border-radius: 4px; font-size: 12px; outline: none; } .add-form input:focus { border-color: #7c3aed; } `; export interface PackingEntry { id: string; name: string; category?: string; packed: boolean; quantity: number; } declare global { interface HTMLElementTagNameMap { "folk-packing-list": FolkPackingList; } } export class FolkPackingList extends FolkShape { static override tagName = "folk-packing-list"; 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; } #items: PackingEntry[] = []; #listEl: HTMLElement | null = null; #progressTextEl: HTMLElement | null = null; #progressFillEl: HTMLElement | null = null; get items() { return this.#items; } set items(v: PackingEntry[]) { this.#items = v; this.#render(); this.dispatchEvent(new CustomEvent("content-change")); } get packedCount() { return this.#items.filter((i) => i.packed).length; } addItem(item: PackingEntry) { this.#items.push(item); this.#render(); this.dispatchEvent(new CustomEvent("content-change")); } toggleItem(id: string) { const item = this.#items.find((i) => i.id === id); if (item) { item.packed = !item.packed; this.#render(); this.dispatchEvent(new CustomEvent("content-change")); } } override createRenderRoot() { const root = super.createRenderRoot(); const wrapper = document.createElement("div"); wrapper.innerHTML = html`