|
| 1 | +import type {SearchEngine} from '@coveo/headless'; |
| 2 | +import {html} from 'lit'; |
| 3 | +import {ifDefined} from 'lit/directives/if-defined.js'; |
| 4 | +import {ref} from 'lit/directives/ref.js'; |
| 5 | +import type {FunctionalComponent} from '@/src/utils/functional-component-utils'; |
1 | 6 | import { eventPromise } from '@/src/utils/event-utils'; |
2 | | -import {SearchEngine} from '@coveo/headless'; |
3 | | -import {FunctionalComponent, h} from '@stencil/core'; |
4 | 7 |
|
5 | 8 | const documentIdentifierInIframe = 'CoveoDocIdentifier'; |
6 | 9 |
|
@@ -65,64 +68,83 @@ const iframeConnectedEvent = async (iframe: HTMLIFrameElement, logger?: SearchEn |
65 | 68 | }); |
66 | 69 | }; |
67 | 70 |
|
68 | | -/** |
69 | | - * @deprecated should only be used for Stencil components. |
70 | | - */ |
71 | | -export const QuickviewIframe: FunctionalComponent<{ |
| 71 | +export interface QuickviewIframeProps { |
72 | 72 | title: string; |
73 | 73 | content?: string; |
74 | 74 | onSetIframeRef: (ref: HTMLIFrameElement) => void; |
75 | 75 | uniqueIdentifier?: string; |
76 | 76 | sandbox?: string; |
77 | 77 | src?: string; |
78 | 78 | logger?: SearchEngine['logger']; |
79 | | -}> = ({title, onSetIframeRef, uniqueIdentifier, content, sandbox, src, logger}) => { |
| 79 | +} |
| 80 | + |
| 81 | +export const renderQuickviewIframe: FunctionalComponent< |
| 82 | + QuickviewIframeProps |
| 83 | +> = ({props}) => { |
| 84 | + const { |
| 85 | + title, |
| 86 | + onSetIframeRef, |
| 87 | + uniqueIdentifier, |
| 88 | + content, |
| 89 | + sandbox, |
| 90 | + src, |
| 91 | + logger, |
| 92 | + } = props; |
| 93 | + |
80 | 94 | // When a document is written with document.open/document.write/document.close |
81 | 95 | // it is not synchronous and the content of the iframe is only available to be queried at the end of the current call stack. |
82 | 96 | // This add a "wait" (setTimeout 0) before calling the `onSetIframeRef` from the parent modal quickview |
83 | 97 | const waitForIframeContentToBeWritten = () => { |
84 | 98 | return new Promise((resolve) => setTimeout(resolve)); |
85 | 99 | }; |
86 | 100 |
|
87 | | - return ( |
88 | | - <iframe |
89 | | - title={title} |
90 | | - src="about:blank" |
91 | | - class="h-full w-full" |
92 | | - sandbox={sandbox} |
93 | | - ref={async (el) => { |
94 | | - const iframeRef = el as HTMLIFrameElement; |
95 | | - |
96 | | - if (!uniqueIdentifier || !content) { |
97 | | - return; |
98 | | - } |
| 101 | + const handleRef = (el: Element | undefined) => { |
| 102 | + if (!el) return; |
99 | 103 |
|
100 | | - await iframeConnectedEvent(iframeRef, logger); |
| 104 | + const iframeRef = el as HTMLIFrameElement; |
101 | 105 |
|
102 | | - const documentWriter = iframeRef.contentDocument; |
103 | | - if (!documentWriter) { |
104 | | - if (src) { |
105 | | - warnAboutLimitedUsageQuickview(logger); |
106 | | - iframeRef.src = src; |
107 | | - } |
| 106 | + if (!uniqueIdentifier || !content) { |
| 107 | + return; |
| 108 | + } |
108 | 109 |
|
109 | | - return; |
110 | | - } |
111 | | - if ( |
112 | | - currentResultAlreadyWrittenToDocument( |
113 | | - documentWriter, |
114 | | - uniqueIdentifier |
115 | | - ) |
116 | | - ) { |
117 | | - return; |
| 110 | + // Wait for iframe to be ready before accessing contentDocument |
| 111 | + const writeContentWhenReady = async () => { |
| 112 | + await iframeConnectedEvent(iframeRef, logger); |
| 113 | + |
| 114 | + const documentWriter = iframeRef.contentDocument; |
| 115 | + if (!documentWriter) { |
| 116 | + if (src) { |
| 117 | + warnAboutLimitedUsageQuickview(logger); |
| 118 | + iframeRef.src = src; |
118 | 119 | } |
119 | 120 |
|
120 | | - writeDocument(documentWriter, content); |
121 | | - ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier); |
| 121 | + return; |
| 122 | + } |
| 123 | + if ( |
| 124 | + currentResultAlreadyWrittenToDocument(documentWriter, uniqueIdentifier) |
| 125 | + ) { |
| 126 | + return; |
| 127 | + } |
122 | 128 |
|
123 | | - await waitForIframeContentToBeWritten(); |
124 | | - onSetIframeRef(iframeRef); |
125 | | - }} |
126 | | - ></iframe> |
127 | | - ); |
| 129 | + writeDocument(documentWriter, content); |
| 130 | + ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier); |
| 131 | + |
| 132 | + await waitForIframeContentToBeWritten(); |
| 133 | + onSetIframeRef(iframeRef); |
| 134 | + }; |
| 135 | + |
| 136 | + // Execute async operation without blocking |
| 137 | + writeContentWhenReady(); |
| 138 | + }; |
| 139 | + |
| 140 | + return html`<iframe |
| 141 | + title=${title} |
| 142 | + src="about:blank" |
| 143 | + class="h-full w-full" |
| 144 | + sandbox=${ |
| 145 | + // biome-ignore lint/suspicious/noExplicitAny: ifDefined requires 'any' for optional HTML attributes |
| 146 | + ifDefined(sandbox) as any |
| 147 | + } |
| 148 | + ${ref(handleRef)} |
| 149 | + ></iframe>`; |
128 | 150 | }; |
0 commit comments