Rewild Your Web
web browser dweb
16
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 508943f815334a02aefc47d9da365aba115ab0f5 199 lines 5.2 kB view raw
1// SPDX-License-Identifier: AGPL-3.0-or-later 2 3import { 4 LitElement, 5 html, 6 css, 7} from "//shared.localhost:8888/third_party/lit/lit-all.min.js"; 8 9export class MobileOverview extends LitElement { 10 static properties = { 11 open: { type: Boolean, reflect: true }, 12 tabs: { type: Array }, 13 activeTabId: { type: String }, 14 }; 15 16 static styles = css` 17 @import url(//system.localhost:8888/mobile_overview.css); 18 `; 19 20 constructor() { 21 super(); 22 this.open = false; 23 this.tabs = []; 24 this.activeTabId = null; 25 26 // Touch state for swipe-to-close 27 this.swipeState = null; 28 } 29 30 handleOverlayClick(e) { 31 if (e.target.classList.contains("overlay")) { 32 this.close(); 33 } 34 } 35 36 close() { 37 this.open = false; 38 this.dispatchEvent(new CustomEvent("overview-close", { bubbles: true })); 39 } 40 41 handleTabClick(tab) { 42 this.dispatchEvent( 43 new CustomEvent("tab-select", { 44 bubbles: true, 45 detail: { tabId: tab.id }, 46 }) 47 ); 48 this.close(); 49 } 50 51 handleCloseTab(e, tab) { 52 e.stopPropagation(); 53 54 // Animate the card closing 55 const card = e.currentTarget.closest(".tab-card"); 56 card.classList.add("closing"); 57 58 setTimeout(() => { 59 this.dispatchEvent( 60 new CustomEvent("tab-close", { 61 bubbles: true, 62 detail: { tabId: tab.id }, 63 }) 64 ); 65 }, 300); 66 } 67 68 handleNewTab() { 69 this.dispatchEvent(new CustomEvent("tab-new", { bubbles: true })); 70 this.close(); 71 } 72 73 handleHome() { 74 this.dispatchEvent(new CustomEvent("tab-home", { bubbles: true })); 75 this.close(); 76 } 77 78 handleDone() { 79 this.close(); 80 } 81 82 // Touch handlers for swipe-up-to-close on cards 83 handleTouchStart(e, tab) { 84 const touch = e.touches[0]; 85 this.swipeState = { 86 tab, 87 startY: touch.clientY, 88 currentY: touch.clientY, 89 element: e.currentTarget, 90 }; 91 } 92 93 handleTouchMove(e) { 94 if (!this.swipeState) { 95 return; 96 } 97 98 const touch = e.touches[0]; 99 this.swipeState.currentY = touch.clientY; 100 const deltaY = this.swipeState.currentY - this.swipeState.startY; 101 102 // Only allow upward swipe (close) 103 if (deltaY < 0) { 104 this.swipeState.element.style.transform = `translateY(${deltaY}px)`; 105 this.swipeState.element.style.opacity = Math.max(0, 1 + deltaY / 150); 106 } 107 } 108 109 handleTouchEnd(e) { 110 if (!this.swipeState) { 111 return; 112 } 113 114 const deltaY = this.swipeState.currentY - this.swipeState.startY; 115 const element = this.swipeState.element; 116 const tab = this.swipeState.tab; 117 118 if (deltaY < -80) { 119 // Close threshold reached 120 element.classList.add("closing"); 121 setTimeout(() => { 122 this.dispatchEvent( 123 new CustomEvent("tab-close", { 124 bubbles: true, 125 detail: { tabId: tab.id }, 126 }) 127 ); 128 }, 300); 129 } else { 130 // Snap back 131 element.style.transform = ""; 132 element.style.opacity = ""; 133 } 134 135 this.swipeState = null; 136 } 137 138 render() { 139 let tabText = this.tabs.length > 1 ? `${this.tabs.length} Views` : `1 View`; 140 141 return html` 142 <div class="overlay" @click=${this.handleOverlayClick}></div> 143 <div class="container"> 144 <div class="header"> 145 <span class="header-title">${tabText}</span> 146 <div class="header-actions"> 147 <button class="header-button" @click=${this.handleHome}> 148 <lucide-icon name="house"></lucide-icon> 149 </button> 150 <button class="header-button" @click=${this.handleDone}> 151 <lucide-icon name="check"></lucide-icon> 152 </button> 153 </div> 154 </div> 155 156 <div class="grid"> 157 ${this.tabs.map( 158 (tab) => html` 159 <div 160 class="tab-card ${tab.id === this.activeTabId ? "active" : ""}" 161 @click=${() => this.handleTabClick(tab)} 162 @touchstart=${(e) => this.handleTouchStart(e, tab)} 163 @touchmove=${this.handleTouchMove} 164 @touchend=${this.handleTouchEnd} 165 > 166 ${tab.screenshotUrl 167 ? html`<img 168 class="tab-screenshot" 169 src="${tab.screenshotUrl}" 170 alt="" 171 />` 172 : html`<div class="tab-screenshot-placeholder"> 173 <lucide-icon name="globe"></lucide-icon> 174 </div>`} 175 <div class="tab-info"> 176 <img class="tab-favicon" src="${tab.favicon || ""}" alt="" /> 177 <span class="tab-title">${tab.title || "Untitled"}</span> 178 </div> 179 <button 180 class="close-button" 181 @click=${(e) => this.handleCloseTab(e, tab)} 182 > 183 <lucide-icon name="x"></lucide-icon> 184 </button> 185 </div> 186 ` 187 )} 188 189 <div class="home-card" @click=${this.handleHome}> 190 <lucide-icon name="house"></lucide-icon> 191 <span>Home</span> 192 </div> 193 </div> 194 </div> 195 `; 196 } 197} 198 199customElements.define("mobile-overview", MobileOverview);