fix(forms): close completed drawers and restore portal styling

This commit is contained in:
DeCentN2Madness committed 2026-10-30 14:21:27 -07:00
1 parent e97f0ea9b4
commit 55adb79705
15 files changed
+598 -9

No files matched your search

+13
View File
@@ -1,4 +1,5 @@
import { decryptPortalStream, validatePortalPayload } from '../utils/portal-crypto.mjs';
import { portalLinkPresentation } from '../utils/portal-presentation';
/** Build a passive DOM, never inject executable decrypted HTML. Remote media,
* embedded forms and styles require owner review and are not loaded on unlock. */
@@ -21,9 +22,21 @@ export function safePortalContent(html: string): DocumentFragment {
element.setAttribute('rel', 'noopener noreferrer');
element.setAttribute('referrerpolicy', 'no-referrer');
if (url.origin !== location.origin && url.protocol === 'https:') element.setAttribute('target', '_blank');
const presentation = portalLinkPresentation([...source.classList], source.style.backgroundColor, url.protocol);
if (presentation) element.className = presentation;
}
} catch { /* Invalid destination remains visible text without navigation. */ }
}
// Preserve directory semantics without retaining arbitrary legacy attributes.
if (source.tagName === 'TH' && ['row', 'col', 'rowgroup', 'colgroup'].includes(source.getAttribute('scope') ?? '')) {
element.setAttribute('scope', source.getAttribute('scope')!);
}
if (['TD', 'TH'].includes(source.tagName)) {
for (const attr of ['colspan', 'rowspan']) {
const value = source.getAttribute(attr);
if (value && /^[1-9]\d?$/.test(value)) element.setAttribute(attr, value);
}
}
for (const child of source.childNodes) copy(child, element);
target.appendChild(element);
};
+26 -2
View File
@@ -1,4 +1,5 @@
import { isSuppressed, reachedHalf, suppressionDuration, suppressionKey } from '../utils/drawer-preference';
import { isFormACompletion } from '../utils/jotform-completion';
let disposeAll: (() => void) | undefined;
// Memory preference also covers storage-denied browsing during this document lifetime.
@@ -23,6 +24,8 @@ export function initializeServiceInquiryDrawers() {
.filter((anchor) => anchor.dataset.serviceInquiryTrigger === dialog.dataset.service);
let timer: number | undefined;
let closeTimer: number | undefined;
let completionTimer: number | undefined;
const acknowledgment = dialog.querySelector<HTMLElement>('[data-service-inquiry-completed]');
let firstFrame = 0;
let secondFrame = 0;
let trigger: HTMLElement | null = null;
@@ -47,6 +50,11 @@ export function initializeServiceInquiryDrawers() {
if (document.querySelector('dialog[open]')) return;
autoConsumed = true;
trigger = manual ?? (document.activeElement instanceof HTMLElement ? document.activeElement : null);
if (dialog.dataset.submissionCompleted === 'true') {
dialog.querySelector('[data-jotform-form-a]')?.dispatchEvent(new Event('aia:jotform-reset'));
delete dialog.dataset.submissionCompleted;
if (acknowledgment) acknowledgment.hidden = true;
}
dialog.showModal();
document.documentElement.classList.add('service-inquiry-open');
dialog.querySelector('[data-jotform-form-a]')?.dispatchEvent(new Event('aia:jotform-activate'));
@@ -59,7 +67,8 @@ export function initializeServiceInquiryDrawers() {
const dismiss = () => {
if (!dialog.open || closing) return;
closing = true;
remember();
clearTimeout(completionTimer);
if (dialog.dataset.submissionCompleted !== 'true') remember();
stopAuto();
dialog.classList.remove('is-open');
const finish = () => { dialog.close(); closing = false; };
@@ -74,6 +83,7 @@ export function initializeServiceInquiryDrawers() {
dialog.addEventListener('cancel', (event) => { event.preventDefault(); dismiss(); }, { signal });
dialog.addEventListener('click', (event) => { if (event.target === dialog) dismiss(); }, { signal });
dialog.addEventListener('close', () => {
clearTimeout(completionTimer);
stopAuto();
dialog.classList.remove('is-open');
document.documentElement.classList.remove('service-inquiry-open');
@@ -81,6 +91,20 @@ export function initializeServiceInquiryDrawers() {
trigger = null;
}, { signal });
if (authorized) {
addEventListener('message', (event) => {
const container = dialog.querySelector<HTMLElement>('[data-jotform-form-a]');
const frame = container?.querySelector<HTMLIFrameElement>('[data-jotform-iframe-frame]');
const active = dialog.open && !closing && dialog.dataset.submissionCompleted !== 'true' &&
container?.dataset.formContext === 'consultation' && container.dataset.jotformIframeActivated === 'true';
if (!isFormACompletion(event, frame?.contentWindow, active, frame?.dataset.jotformIframeUrl)) return;
dialog.dataset.submissionCompleted = 'true';
remember();
if (acknowledgment) {
acknowledgment.hidden = false;
acknowledgment.scrollIntoView({ block: 'nearest', behavior: 'instant' });
}
completionTimer = window.setTimeout(dismiss, 5000);
}, { signal });
for (const anchor of triggers) {
anchor.setAttribute('aria-haspopup', 'dialog');
anchor.setAttribute('aria-controls', dialog.id);
@@ -103,7 +127,7 @@ export function initializeServiceInquiryDrawers() {
}
addEventListener('storage', (event) => { if (event.key === suppressionKey && suppressed()) stopAuto(); }, { signal });
disposers.push(() => {
stopAuto(); clearTimeout(closeTimer); cancelAnimationFrame(firstFrame); cancelAnimationFrame(secondFrame);
stopAuto(); clearTimeout(closeTimer); clearTimeout(completionTimer); cancelAnimationFrame(firstFrame); cancelAnimationFrame(secondFrame);
controller.abort(); dialog.classList.remove('is-open');
if (dialog.open) dialog.close();
for (const anchor of triggers) { anchor.removeAttribute('aria-controls'); anchor.removeAttribute('aria-haspopup'); }