Rewild Your Web
18
fork

Configure Feed

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

at main 215 lines 5.0 kB view raw
1// SPDX-License-Identifier: AGPL-3.0-or-later 2 3import { 4 LitElement, 5 html, 6 css, 7} from "beaver://shared/third_party/lit/lit-all.min.js"; 8 9class ContentBlockerPanel extends LitElement { 10 static properties = { 11 open: { type: Boolean, reflect: true }, 12 origin: { type: String }, 13 enabled: { type: Boolean }, 14 blockedCount: { type: Number }, 15 allowedCount: { type: Number }, 16 x: { type: Number }, 17 y: { type: Number }, 18 }; 19 20 static styles = css` 21 :host { 22 display: none; 23 } 24 25 :host([open]) { 26 display: block; 27 position: absolute; 28 inset: 0; 29 z-index: 10000; 30 } 31 32 .backdrop { 33 position: fixed; 34 inset: 0; 35 z-index: -1; 36 } 37 38 .panel { 39 position: absolute; 40 background: var(--bg-menu); 41 border: 1px solid var(--color-border); 42 border-radius: var(--radius-md); 43 padding: var(--spacing-md); 44 min-width: 220px; 45 box-shadow: 0 4px 16px var(--color-shadow-menu); 46 font-family: var(--font-family-base); 47 display: flex; 48 flex-direction: column; 49 gap: var(--spacing-sm); 50 animation: panel-in 0.18s cubic-bezier(0.16, 1, 0.3, 1); 51 } 52 53 @keyframes panel-in { 54 from { 55 opacity: 0; 56 transform: translateY(-4px); 57 } 58 } 59 60 .origin { 61 font-size: var(--font-size-sm); 62 color: var(--color-text-secondary); 63 white-space: nowrap; 64 overflow: hidden; 65 text-overflow: ellipsis; 66 } 67 68 .toggle-row { 69 display: flex; 70 align-items: center; 71 justify-content: space-between; 72 gap: var(--spacing-md); 73 } 74 75 .toggle-label { 76 font-size: var(--font-size-sm); 77 font-weight: var(--font-weight-bold); 78 } 79 80 .toggle-switch { 81 position: relative; 82 width: 2.4em; 83 height: 1.4em; 84 flex-shrink: 0; 85 } 86 87 .toggle-switch input { 88 opacity: 0; 89 width: 0; 90 height: 0; 91 } 92 93 .toggle-slider { 94 position: absolute; 95 inset: 0; 96 background: var(--color-border); 97 border-radius: 0.7em; 98 cursor: pointer; 99 transition: background var(--transition-fast); 100 } 101 102 .toggle-slider::before { 103 content: ""; 104 position: absolute; 105 width: 1em; 106 height: 1em; 107 left: 0.2em; 108 bottom: 0.2em; 109 background: var(--color-text-on-header); 110 border-radius: 50%; 111 transition: transform var(--transition-fast); 112 } 113 114 .toggle-switch input:checked + .toggle-slider { 115 background: var(--color-primary); 116 } 117 118 .toggle-switch input:checked + .toggle-slider::before { 119 transform: translateX(1em); 120 } 121 122 .toggle-switch input:focus-visible + .toggle-slider { 123 outline: 2px solid var(--color-focus-ring); 124 outline-offset: 2px; 125 } 126 127 .stats { 128 font-size: var(--font-size-xs); 129 color: var(--color-text-tertiary); 130 } 131 132 .stats .count { 133 color: var(--color-text-secondary); 134 font-weight: var(--font-weight-bold); 135 } 136 `; 137 138 constructor() { 139 super(); 140 this.open = false; 141 this.origin = ""; 142 this.enabled = true; 143 this.blockedCount = 0; 144 this.allowedCount = 0; 145 this.x = 0; 146 this.y = 0; 147 } 148 149 async handleToggle(e) { 150 const newEnabled = e.target.checked; 151 this.enabled = newEnabled; 152 try { 153 await navigator.embedder.contentBlocker.setOriginEnabled( 154 this.origin, 155 newEnabled, 156 ); 157 this.dispatchEvent( 158 new CustomEvent("blocker-toggled", { 159 bubbles: true, 160 composed: true, 161 detail: { enabled: newEnabled }, 162 }), 163 ); 164 } catch (err) { 165 console.error("[ContentBlockerPanel] setOriginEnabled failed:", err); 166 this.enabled = !newEnabled; 167 } 168 } 169 170 dismiss() { 171 this.open = false; 172 this.dispatchEvent( 173 new CustomEvent("panel-dismiss", { bubbles: true, composed: true }), 174 ); 175 } 176 177 render() { 178 if (!this.open) return html``; 179 180 const total = this.blockedCount + this.allowedCount; 181 const percentage = 182 total > 0 ? Math.round((this.blockedCount / total) * 100) : 0; 183 184 let hostname = this.origin; 185 try { 186 hostname = new URL(this.origin).hostname; 187 } catch {} 188 189 const panelStyle = `right: calc(100% - ${this.x}px); top: ${this.y}px;`; 190 191 return html` 192 <div class="backdrop" @click=${this.dismiss}></div> 193 <div class="panel" style="${panelStyle}"> 194 <div class="origin">${hostname}</div> 195 <div class="toggle-row"> 196 <span class="toggle-label">Content blocking</span> 197 <label class="toggle-switch"> 198 <input 199 type="checkbox" 200 .checked=${this.enabled} 201 @change=${this.handleToggle} 202 /> 203 <span class="toggle-slider"></span> 204 </label> 205 </div> 206 <div class="stats"> 207 <span class="count">${this.blockedCount}</span> blocked / 208 ${total} total (${percentage}%) 209 </div> 210 </div> 211 `; 212 } 213} 214 215customElements.define("content-blocker-panel", ContentBlockerPanel);