1"use strict"; 2const BOTTOM_SHEET_ANIMATION = { 3 snap: true, 4 durationMs: 500, 5 curve: '0.32,0.72,0,1', 6 flick: 700 7}; 8const PANEL_ANIMATION_MS = BOTTOM_SHEET_ANIMATION.durationMs; 9const SWIPE_CLOSE_THRESHOLD = 96; 10const SWIPE_FLICK_THRESHOLD = BOTTOM_SHEET_ANIMATION.flick; 11const BOTTOM_SHEET_CLASSES = { 12 root: 'portal-bottom-sheet hidden fixed inset-0 z-[55] flex items-end justify-center p-4 pb-[calc(1rem+env(safe-area-inset-bottom,0px))]', 13 backdrop: 'portal-bottom-sheet__backdrop absolute inset-0', 14 panel: 'portal-bottom-sheet__panel relative z-10 flex w-full max-w-lg max-h-[min(88dvh,40rem)] flex-col overflow-hidden rounded-[2rem] border', 15 handleWrap: 'flex shrink-0 touch-none select-none justify-center pt-3 pb-1', 16 handle: 'h-1 w-10 rounded-full bg-borda-base', 17 header: 'portal-bottom-sheet__header hidden shrink-0 touch-none select-none items-center justify-between gap-3 px-5 pb-2 sm:p-6', 18 title: 'portal-bottom-sheet__title m-0 min-w-0 flex-1 text-lg font-bold leading-snug text-texto-base', 19 closeButton: 'portal-bottom-sheet__close inline-flex min-h-9 min-w-9 shrink-0 items-center justify-center rounded-full bg-transparent p-0 text-xl leading-none text-texto-suave transition-all duration-150 hover:bg-marca-suave hover:text-marca-suave-texto focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foco focus-visible:ring-offset-2 focus-visible:ring-offset-fundo-base', 20 body: 'portal-bottom-sheet__body min-h-0 flex-1 overflow-y-auto px-5 pb-5' 21}; 22(function () { 23 class PortalBottomSheetManager { 24 constructor() { 25 this.root = null; 26 this.panel = null; 27 this.backdrop = null; 28 this.header = null; 29 this.titleEl = null; 30 this.closeButton = null; 31 this.contentEl = null; 32 this.state = 'closed'; 33 this.currentId = null; 34 this.dismissible = true; 35 this.closeOnEscape = true; 36 this.swipeToDismiss = true; 37 this.desktopLayout = 'side-panel'; 38 this.onOpenCallback = null; 39 this.onCloseCallback = null; 40 this.focusBeforeOpen = null; 41 this.closeTimerId = null; 42 this.dragState = null; 43 this.idCounter = 0; 44 this.boundKeydown = (event) => this.handleKeydown(event); 45 this.boundBackdropClick = (event) => this.handleBackdropClick(event); 46 this.boundBackdropWheel = (event) => this.handleBackdropWheel(event); 47 this.boundCloseClick = (event) => this.handleCloseClick(event); 48 this.boundPointerDown = (event) => this.handlePointerDown(event); 49 this.boundPointerMove = (event) => this.handlePointerMove(event); 50 this.boundPointerUp = (event) => this.handlePointerUp(event); 51 this.boundPointerCancel = (event) => this.handlePointerCancel(event); 52 } 53 init() { 54 if (this.root) { 55 return; 56 } 57 this.root = document.getElementById('portal-bottom-sheet'); 58 if (!this.root) { 59 this.root = this.createRoot(); 60 document.body.appendChild(this.root); 61 } 62 this.panel = this.root.querySelector('[data-role="bottom-sheet-panel"]'); 63 this.backdrop = this.root.querySelector('.portal-bottom-sheet__backdrop'); 64 this.header = this.root.querySelector('[data-role="bottom-sheet-header"]'); 65 this.titleEl = this.root.querySelector('[data-role="bottom-sheet-title"]'); 66 this.closeButton = this.root.querySelector('[data-role="bottom-sheet-close"]'); 67 this.contentEl = this.root.querySelector('[data-role="bottom-sheet-content"]'); 68 if (!this.panel || 69 !this.backdrop || 70 !this.header || 71 !this.titleEl || 72 !this.closeButton || 73 !this.contentEl) { 74 return; 75 } 76 this.applyAnimationSettings(); 77 this.backdrop.addEventListener('click', this.boundBackdropClick); 78 this.backdrop.addEventListener('wheel', this.boundBackdropWheel, { passive: false }); 79 this.closeButton.addEventListener('click', this.boundCloseClick); 80 this.panel.addEventListener('pointerdown', this.boundPointerDown, { passive: false }); 81 this.panel.addEventListener('pointermove', this.boundPointerMove, { passive: false }); 82 this.panel.addEventListener('pointerup', this.boundPointerUp); 83 this.panel.addEventListener('pointercancel', this.boundPointerCancel); 84 } 85 open(options) { 86 var _a, _b, _c, _d, _e; 87 this.init(); 88 if (!this.root || !this.panel || !this.contentEl || !this.titleEl || !this.header) { 89 return ''; 90 } 91 if (this.state === 'open' || this.state === 'opening') { 92 this.closeInternal({ immediate: true }); 93 } 94 const id = (_a = options.id) !== null && _a !== void 0 ? _a : this.generateId(); 95 this.currentId = id; 96 this.dismissible = options.dismissible !== false; 97 this.closeOnEscape = options.closeOnEscape !== false; 98 this.swipeToDismiss = options.swipeToDismiss !== false; 99 this.desktopLayout = (_b = options.desktopLayout) !== null && _b !== void 0 ? _b : 'side-panel'; 100 this.onOpenCallback = (_c = options.onOpen) !== null && _c !== void 0 ? _c : null; 101 this.onCloseCallback = (_d = options.onClose) !== null && _d !== void 0 ? _d : null; 102 this.applyDesktopLayout(); 103 this.applyTitle(options.title, options.ariaLabel); 104 this.setContent((_e = options.content) !== null && _e !== void 0 ? _e : ''); 105 this.focusBeforeOpen = 106 document.activeElement instanceof HTMLElement ? document.activeElement : null; 107 this.clearCloseTimer(); 108 this.resetPanelOffset(); 109 this.root.classList.remove('hidden'); 110 this.root.setAttribute('aria-hidden', 'false'); 111 this.setState('opening'); 112 window.requestAnimationFrame(() => { 113 window.requestAnimationFrame(() => { 114 var _a; 115 this.setState('open'); 116 (_a = this.onOpenCallback) === null || _a === void 0 ? void 0 : _a.call(this); 117 this.focusPanel(); 118 }); 119 }); 120 document.addEventListener('keydown', this.boundKeydown); 121 return id; 122 } 123 close() { 124 this.closeInternal(); 125 } 126 isOpen() { 127 return this.state === 'open' || this.state === 'opening'; 128 } 129 getId() { 130 return this.currentId; 131 } 132 setContent(content) { 133 if (!this.contentEl) { 134 return; 135 } 136 this.contentEl.replaceChildren(); 137 if (typeof content === 'string') { 138 this.contentEl.innerHTML = content; 139 return; 140 } 141 this.contentEl.appendChild(content); 142 } 143 closeInternal(options) { 144 if (!this.root || this.state === 'closed' || this.state === 'closing') { 145 return; 146 } 147 const immediate = (options === null || options === void 0 ? void 0 : options.immediate) === true; 148 document.removeEventListener('keydown', this.boundKeydown); 149 this.resetPanelOffset(); 150 this.dragState = null; 151 if (immediate) { 152 this.finishClose(); 153 return; 154 } 155 this.setState('closing'); 156 this.clearCloseTimer(); 157 this.closeTimerId = window.setTimeout(() => { 158 this.finishClose(); 159 }
159, PANEL_ANIMATION_MS); 160 } 161 finishClose() { 162 if (!this.root) { 163 return; 164 } 165 this.clearCloseTimer(); 166 this.setState('closed'); 167 this.root.classList.add('hidden'); 168 this.root.setAttribute('aria-hidden', 'true'); 169 this.root.setAttribute('data-desktop-layout', 'side-panel'); 170 const callback = this.onCloseCallback; 171 this.currentId = null; 172 this.onOpenCallback = null; 173 this.onCloseCallback = null; 174 callback === null || callback === void 0 ? void 0 : callback(); 175 if (this.focusBeforeOpen && document.contains(this.focusBeforeOpen)) { 176 this.focusBeforeOpen.focus(); 177 } 178 this.focusBeforeOpen = null; 179 } 180 applyTitle(title, ariaLabel) { 181 var _a; 182 if (!this.header || !this.titleEl || !this.panel || !this.closeButton) { 183 return; 184 } 185 const label = ((_a = title !== null && title !== void 0 ? title : ariaLabel) !== null && _a !== void 0 ? _a : '').trim(); 186 if (label === '' && !this.dismissible) { 187 this.header.classList.add('hidden'); 188 this.header.classList.remove('flex'); 189 this.closeButton.hidden = true; 190 this.titleEl.textContent = ''; 191 this.titleEl.removeAttribute('aria-hidden'); 192 if (ariaLabel) { 193 this.panel.setAttribute('aria-label', ariaLabel); 194 this.panel.removeAttribute('aria-labelledby'); 195 } 196 else { 197 this.panel.removeAttribute('aria-label'); 198 this.panel.removeAttribute('aria-labelledby'); 199 } 200 return; 201 } 202 this.header.classList.remove('hidden'); 203 this.header.classList.add('flex'); 204 this.closeButton.hidden = !this.dismissible; 205 this.titleEl.textContent = title !== null && title !== void 0 ? title : label; 206 if (label === '') { 207 this.titleEl.setAttribute('aria-hidden', 'true'); 208 this.panel.setAttribute('aria-label', ariaLabel || 'Painel'); 209 this.panel.removeAttribute('aria-labelledby'); 210 return; 211 } 212 this.titleEl.removeAttribute('aria-hidden'); 213 this.panel.setAttribute('aria-labelledby', 'portal-bottom-sheet-title'); 214 this.panel.removeAttribute('aria-label'); 215 } 216 setState(state) { 217 var _a; 218 this.state = state; 219 (_a = this.root) === null || _a === void 0 ? void 0 : _a.setAttribute('data-state', state); 220 document.documentElement.removeAttribute('data-portal-bottom-sheet'); 221 } 222 applyAnimationSettings() { 223 const rootStyle = document.documentElement.style; 224 rootStyle.setProperty('--portal-bottom-sheet-duration', `${PANEL_ANIMATION_MS}ms`); 225 rootStyle.setProperty('--portal-bottom-sheet-curve', `cubic-bezier(${BOTTOM_SHEET_ANIMATION.curve})`); 226 } 227 applyDesktopLayout() { 228 var _a; 229 (_a = this.root) === null || _a === void 0 ? void 0 : _a.setAttribute('data-desktop-layout', this.desktopLayout); 230 } 231 handleKeydown(event) { 232 if (!this.closeOnEscape || event.key !== 'Escape') { 233 return; 234 } 235 event.preventDefault(); 236 this.closeInternal(); 237 } 238 handleCloseClick(event) { 239 if (!this.dismissible) { 240 return; 241 } 242 event.preventDefault(); 243 this.closeInternal(); 244 } 245 handleBackdropClick(event) { 246 if (!this.dismissible || event.target !== this.backdrop) { 247 return; 248 } 249 this.closeInternal(); 250 } 251 handleBackdropWheel(event) { 252 if (!this.isDesktopSidePanelActive()) { 253 return; 254 } 255 event.preventDefault(); 256 window.scrollBy({ 257 left: event.deltaX, 258 top: event.deltaY, 259 behavior: 'auto' 260 }); 261 } 262 handlePointerDown(event) { 263 var _a, _b, _c; 264 if (!this.swipeToDismiss || 265 this.state !== 'open' || 266 event.button !== 0 || 267 this.isDesktopSidePanelActive()) { 268 return; 269 } 270 const handle = (_a = this.panel) === null || _a === void 0 ? void 0 : _a.querySelector('[data-role="bottom-sheet-handle"]'); 271 const target = event.target; 272 if (!(target instanceof Element)) { 273 return; 274 } 275 const onHandle = (handle === null || handle === void 0 ? void 0 : handle.contains(target)) === true; 276 const onPanelTop = this.isPointerInTopDragZone(event.clientY); 277 if (this.isInteractiveDragTarget(target)) { 278 return; 279 } 280 if (!onHandle && !onPanelTop) { 281 return; 282 } 283 event.preventDefault(); 284 this.dragState = { 285 pointerId: event.pointerId, 286 startY: event.clientY, 287 startTime: performance.now(), 288 currentOffset: 0 289 }; 290 (_b = this.panel) === null || _b === void 0 ? void 0 : _b.setPointerCapture(event.pointerId); 291 (_c = this.panel) === null || _c === void 0 ? void 0 : _c.classList.add('portal-bottom-sheet__panel--dragging'); 292 } 293 handlePointerMove(event) { 294 if (!this.dragState || event.pointerId !== this.dragState.pointerId || !this.panel) { 295 return; 296 } 297 const deltaY = Math.max(0, event.clientY - this.dragState.startY); 298 this.dragState.currentOffset = deltaY; 299 event.preventDefault(); 300 this.panel.style.transform = `translateY(${deltaY}px)`; 301 } 302 handlePointerUp(event) { 303 if (!this.dragState || event.pointerId !== this.dragState.pointerId) { 304 return; 305 } 306 this.releaseDrag(event); 307 } 308 handlePointerCancel(event) { 309 if (!this.dragState || event.pointerId !== this.dragState.pointerId) { 310 return; 311 } 312 this.releaseDrag(event, true); 313 } 314 releaseDrag(event, cancel = false) { 315 if (!this.dragState || !this.panel) { 316 return; 317 } 318 const { startTime, currentOffset, pointerId } = this.dragState;
319 const elapsed = Math.max(performance.now() - startTime, 1); 320 const velocityPxPerSecond = (currentOffset / elapsed) * 1000; 321 this.panel.releasePointerCapture(pointerId); 322 this.panel.classList.remove('portal-bottom-sheet__panel--dragging'); 323 this.dragState = null; 324 if (!cancel && 325 this.dismissible && 326 (currentOffset >= SWIPE_CLOSE_THRESHOLD || 327 velocityPxPerSecond >= SWIPE_FLICK_THRESHOLD)) { 328 this.closeInternal(); 329 return; 330 } 331 this.resetPanelOffset(); 332 } 333 isPointerInTopDragZone(clientY) { 334 if (!this.panel) { 335 return false; 336 } 337 const rect = this.panel.getBoundingClientRect(); 338 return clientY <= rect.top + 56; 339 } 340 isInteractiveDragTarget(target) { 341 return target.closest('button, a, input, select, textarea, summary, [role="button"], [data-role="bottom-sheet-close"]') !== null; 342 } 343 resetPanelOffset() { 344 if (!this.panel) { 345 return; 346 } 347 this.panel.style.transform = ''; 348 } 349 isDesktopSidePanelActive() { 350 return (this.desktopLayout === 'side-panel' && 351 window.matchMedia('(min-width: 640px)').matches); 352 } 353 focusPanel() { 354 if (!this.panel) { 355 return; 356 } 357 const focusable = this.panel.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); 358 if (focusable) { 359 focusable.focus(); 360 return; 361 } 362 this.panel.setAttribute('tabindex', '-1'); 363 this.panel.focus(); 364 } 365 clearCloseTimer() { 366 if (this.closeTimerId !== null) { 367 window.clearTimeout(this.closeTimerId); 368 this.closeTimerId = null; 369 } 370 } 371 generateId() { 372 this.idCounter += 1; 373 return `bottom-sheet-${Date.now()}-${this.idCounter}`; 374 } 375 createRoot() { 376 const root = document.createElement('div'); 377 root.id = 'portal-bottom-sheet'; 378 root.className = BOTTOM_SHEET_CLASSES.root; 379 root.setAttribute('data-state', 'closed'); 380 root.setAttribute('aria-hidden', 'true'); 381 root.innerHTML = ` 382 <div class="${BOTTOM_SHEET_CLASSES.backdrop}" data-portal-bottom-sheet-close="true" aria-hidden="true"></div> 383 <div 384 class="${BOTTOM_SHEET_CLASSES.panel}" 385 role="dialog" 386 aria-modal="true" 387 aria-labelledby="portal-bottom-sheet-title" 388 data-role="bottom-sheet-panel" 389 > 390 <div class="${BOTTOM_SHEET_CLASSES.handleWrap}" data-role="bottom-sheet-handle" aria-hidden="true"> 391 <span class="${BOTTOM_SHEET_CLASSES.handle}"></span> 392 </div> 393 <header class="${BOTTOM_SHEET_CLASSES.header}" data-role="bottom-sheet-header"> 394 <h2 id="portal-bottom-sheet-title" class="${BOTTOM_SHEET_CLASSES.title}" data-role="bottom-sheet-title"></h2> 395 <button type="button" class="${BOTTOM_SHEET_CLASSES.closeButton}" data-role="bottom-sheet-close" aria-label="Fechar">×</button> 396 </header> 397 <div class="${BOTTOM_SHEET_CLASSES.body}" data-role="bottom-sheet-content"></div> 398 </div> 399 `; 400 return root; 401 } 402 } 403 const manager = new PortalBottomSheetManager(); 404 const api = { 405 open: (options) => manager.open(options), 406 close: () => manager.close(), 407 isOpen: () => manager.isOpen(), 408 getId: () => manager.getId(), 409 setContent: (content) => manager.setContent(content) 410 }; 411 window.PortalBottomSheet = api; 412 if (document.readyState === 'loading') { 413 document.addEventListener('DOMContentLoaded', () => manager.init()); 414 } 415 else { 416 manager.init(); 417 } 418})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.