|
1 | | -import {SearchEngine} from '@coveo/headless'; |
2 | | -import {FunctionalComponent, h} from '@stencil/core'; |
| 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'; |
3 | 6 |
|
4 | 7 | const documentIdentifierInIframe = 'CoveoDocIdentifier'; |
5 | 8 |
|
@@ -44,62 +47,85 @@ const warnAboutLimitedUsageQuickview = (logger?: SearchEngine['logger']) => { |
44 | 47 | ); |
45 | 48 | }; |
46 | 49 |
|
47 | | -/** |
48 | | - * @deprecated should only be used for Stencil components. |
49 | | - */ |
50 | | -export const QuickviewIframe: FunctionalComponent<{ |
| 50 | +export interface QuickviewIframeProps { |
51 | 51 | title: string; |
52 | 52 | content?: string; |
53 | 53 | onSetIframeRef: (ref: HTMLIFrameElement) => void; |
54 | 54 | uniqueIdentifier?: string; |
55 | 55 | sandbox?: string; |
56 | 56 | src?: string; |
57 | 57 | logger?: SearchEngine['logger']; |
58 | | -}> = ({title, onSetIframeRef, uniqueIdentifier, content, sandbox, src, logger}) => { |
| 58 | +} |
| 59 | + |
| 60 | +export const renderQuickviewIframe: FunctionalComponent< |
| 61 | + QuickviewIframeProps |
| 62 | +> = ({props}) => { |
| 63 | + const { |
| 64 | + title, |
| 65 | + onSetIframeRef, |
| 66 | + uniqueIdentifier, |
| 67 | + content, |
| 68 | + sandbox, |
| 69 | + src, |
| 70 | + logger, |
| 71 | + } = props; |
| 72 | + |
59 | 73 | // When a document is written with document.open/document.write/document.close |
60 | 74 | // it is not synchronous and the content of the iframe is only available to be queried at the end of the current call stack. |
61 | 75 | // This add a "wait" (setTimeout 0) before calling the `onSetIframeRef` from the parent modal quickview |
62 | 76 | const waitForIframeContentToBeWritten = () => { |
63 | 77 | return new Promise((resolve) => setTimeout(resolve)); |
64 | 78 | }; |
65 | 79 |
|
66 | | - return ( |
67 | | - <iframe |
68 | | - title={title} |
69 | | - src="about:blank" |
70 | | - class="h-full w-full" |
71 | | - sandbox={sandbox} |
72 | | - ref={async (el) => { |
73 | | - const iframeRef = el as HTMLIFrameElement; |
74 | | - |
75 | | - if (!uniqueIdentifier || !content) { |
76 | | - return; |
77 | | - } |
| 80 | + const handleRef = (el: Element | undefined) => { |
| 81 | + if (!el) return; |
78 | 82 |
|
79 | | - const documentWriter = iframeRef.contentDocument; |
80 | | - if (!documentWriter) { |
81 | | - if (src) { |
82 | | - warnAboutLimitedUsageQuickview(logger); |
83 | | - iframeRef.src = src; |
84 | | - } |
| 83 | + const iframeRef = el as HTMLIFrameElement; |
85 | 84 |
|
86 | | - return; |
87 | | - } |
88 | | - if ( |
89 | | - currentResultAlreadyWrittenToDocument( |
90 | | - documentWriter, |
91 | | - uniqueIdentifier |
92 | | - ) |
93 | | - ) { |
94 | | - return; |
| 85 | + if (!uniqueIdentifier || !content) { |
| 86 | + return; |
| 87 | + } |
| 88 | + |
| 89 | + // Wait for iframe to be ready before accessing contentDocument |
| 90 | + const writeContentWhenReady = async () => { |
| 91 | + // In some environments, contentDocument might not be immediately available |
| 92 | + // Wait a tick to ensure iframe is ready |
| 93 | + await waitForIframeContentToBeWritten(); |
| 94 | + |
| 95 | + const documentWriter = iframeRef.contentDocument; |
| 96 | + if (!documentWriter) { |
| 97 | + if (src) { |
| 98 | + warnAboutLimitedUsageQuickview(logger); |
| 99 | + iframeRef.src = src; |
95 | 100 | } |
96 | 101 |
|
97 | | - writeDocument(documentWriter, content); |
98 | | - ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier); |
| 102 | + return; |
| 103 | + } |
| 104 | + if ( |
| 105 | + currentResultAlreadyWrittenToDocument(documentWriter, uniqueIdentifier) |
| 106 | + ) { |
| 107 | + return; |
| 108 | + } |
99 | 109 |
|
100 | | - await waitForIframeContentToBeWritten(); |
101 | | - onSetIframeRef(iframeRef); |
102 | | - }} |
103 | | - ></iframe> |
104 | | - ); |
| 110 | + writeDocument(documentWriter, content); |
| 111 | + ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier); |
| 112 | + |
| 113 | + await waitForIframeContentToBeWritten(); |
| 114 | + onSetIframeRef(iframeRef); |
| 115 | + }; |
| 116 | + |
| 117 | + // Execute async operation without blocking |
| 118 | + writeContentWhenReady(); |
| 119 | + }; |
| 120 | + |
| 121 | + return html`<iframe |
| 122 | + title=${title} |
| 123 | + src="about:blank" |
| 124 | + class="h-full w-full" |
| 125 | + sandbox=${ |
| 126 | + // biome-ignore lint/suspicious/noExplicitAny: ifDefined requires 'any' for optional HTML attributes |
| 127 | + ifDefined(sandbox) as any |
| 128 | + } |
| 129 | + ${ref(handleRef)} |
| 130 | + ></iframe>`; |
105 | 131 | }; |
0 commit comments