PageSourceSearch

https://sorocaba.sp.gov.br/wp-content/themes/temaPortal2026/dist/componenteBottomSheet.js?ver=1786620596

js sorocaba.sp.gov.br collected 2026-10-01 11:43:32 UTC 18,637 bytes, 418 lines download raw bytes

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">&times;</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.