I would like to render content in a new window. This is the component where I am doing it:
export const NewWindowPortal = ({ children }: { children: (props) => ReactNode }) => {
const ref = useRef(null);
const containerEl = useMemo(() => document.createElement("div"), []);
useEffect(() => {
const externalWindow = window.open("", "", "width=600,height=400,left=200,top=200");
externalWindow?.document?.body?.appendChild(containerEl);
}, []);
return (
<>
<BodyShort>This is a new window</BodyShort>
{createPortal(children({ ref }), containerEl)}
</>
);
};
And this is where I am calling this component:
{openInNewWindow && (
<NewWindowPortal>
{(props) => (
<CustomQuillEditor
ref={props.ref}
resize={resize}
readOnly={lesemodus || readOnly}
defaultValue={reformatText(value)}
onTextChange={onTextChange}
/>
)}
</NewWindowPortal>
)}
I can see that the new window opens, but nothing is rendered in it, it just an empty document. I see that if I just send a div as a child:
{openInNewWindow && <NewWindowPortal>{(props) => <div>{props.text}</div>}</NewWindowPortal>}
And change the NewWindowPortal
to render it:
const text = "test";
const containerEl = useMemo(() => document.createElement("div"), []);
useEffect(() => {
const externalWindow = window.open("", "", "width=600,height=400,left=200,top=200");
externalWindow?.document?.body?.appendChild(containerEl);
}, []);
return (
<>
<BodyShort>This is a new window</BodyShort>
{createPortal(children({ text }), containerEl)}
</>
);
Then I get children rendered in NewWindowPortal
. So, the issue is obviously in CustomQuillEditor
:
export const CustomQuillEditor = ({ readOnly, defaultValue, onTextChange, ref, resize }: EditorProps) => {
const containerRef = useRef(null);
useEffect(() => {
const container = containerRef.current;
const editorContainer = container.appendChild(container.ownerDocument.createElement("div"));
const quill = new Quill(editorContainer, {
theme: "snow",
readOnly,
modules: {
history: {},
toolbar: readOnly
? false
: {
container: [
["bold", "italic", "underline", { header: 3 }],
// [{ 'color': "red" }, { 'background': "yellow" }]
],
},
clipboard: {
allowed: {
tags: ["strong", "h3", "h4", "em", "p", "br", "span", "u"],
// attributes: ['href', 'rel', 'target', 'class', "style"]
attributes: [],
},
customButtons: [],
keepSelection: true,
substituteBlockElements: true,
magicPasteLinks: false,
removeConsecutiveSubstitutionTags: false,
},
},
});
ref.current = quill;
if (defaultValue) {
const delta = quill.clipboard.convert({ html: defaultValue });
quill.setContents(delta, "silent");
quill.history.clear();
}
quill.on(Quill.events.TEXT_CHANGE, () => {
if (quill.getLength() <= 1) {
onTextChange("");
} else {
onTextChange(quill.getSemanticHTML().replaceAll("<p></p>", "<p><br/></p>"));
}
});
return () => {
ref.current = null;
container.innerHTML = "";
};
}, [ref]);
return (
<div
spellCheck={false}
className={`ql-top-container ${readOnly ? "readonly" : ""} ${resize ? "resizable" : ""}`}
ref={containerRef}
></div>
);
};
How can I fix this to have CustomQuillEditor
rendered as well?
I would like to render content in a new window. This is the component where I am doing it:
export const NewWindowPortal = ({ children }: { children: (props) => ReactNode }) => {
const ref = useRef(null);
const containerEl = useMemo(() => document.createElement("div"), []);
useEffect(() => {
const externalWindow = window.open("", "", "width=600,height=400,left=200,top=200");
externalWindow?.document?.body?.appendChild(containerEl);
}, []);
return (
<>
<BodyShort>This is a new window</BodyShort>
{createPortal(children({ ref }), containerEl)}
</>
);
};
And this is where I am calling this component:
{openInNewWindow && (
<NewWindowPortal>
{(props) => (
<CustomQuillEditor
ref={props.ref}
resize={resize}
readOnly={lesemodus || readOnly}
defaultValue={reformatText(value)}
onTextChange={onTextChange}
/>
)}
</NewWindowPortal>
)}
I can see that the new window opens, but nothing is rendered in it, it just an empty document. I see that if I just send a div as a child:
{openInNewWindow && <NewWindowPortal>{(props) => <div>{props.text}</div>}</NewWindowPortal>}
And change the NewWindowPortal
to render it:
const text = "test";
const containerEl = useMemo(() => document.createElement("div"), []);
useEffect(() => {
const externalWindow = window.open("", "", "width=600,height=400,left=200,top=200");
externalWindow?.document?.body?.appendChild(containerEl);
}, []);
return (
<>
<BodyShort>This is a new window</BodyShort>
{createPortal(children({ text }), containerEl)}
</>
);
Then I get children rendered in NewWindowPortal
. So, the issue is obviously in CustomQuillEditor
:
export const CustomQuillEditor = ({ readOnly, defaultValue, onTextChange, ref, resize }: EditorProps) => {
const containerRef = useRef(null);
useEffect(() => {
const container = containerRef.current;
const editorContainer = container.appendChild(container.ownerDocument.createElement("div"));
const quill = new Quill(editorContainer, {
theme: "snow",
readOnly,
modules: {
history: {},
toolbar: readOnly
? false
: {
container: [
["bold", "italic", "underline", { header: 3 }],
// [{ 'color': "red" }, { 'background': "yellow" }]
],
},
clipboard: {
allowed: {
tags: ["strong", "h3", "h4", "em", "p", "br", "span", "u"],
// attributes: ['href', 'rel', 'target', 'class', "style"]
attributes: [],
},
customButtons: [],
keepSelection: true,
substituteBlockElements: true,
magicPasteLinks: false,
removeConsecutiveSubstitutionTags: false,
},
},
});
ref.current = quill;
if (defaultValue) {
const delta = quill.clipboard.convert({ html: defaultValue });
quill.setContents(delta, "silent");
quill.history.clear();
}
quill.on(Quill.events.TEXT_CHANGE, () => {
if (quill.getLength() <= 1) {
onTextChange("");
} else {
onTextChange(quill.getSemanticHTML().replaceAll("<p></p>", "<p><br/></p>"));
}
});
return () => {
ref.current = null;
container.innerHTML = "";
};
}, [ref]);
return (
<div
spellCheck={false}
className={`ql-top-container ${readOnly ? "readonly" : ""} ${resize ? "resizable" : ""}`}
ref={containerRef}
></div>
);
};
How can I fix this to have CustomQuillEditor
rendered as well?
Pass the ownerDocument
to the child component, and use that ownerDocument
when creating quill elements.
Change NewWindowPortal
to pass ownerDocument
to the child component:
return createPortal(children({ ownerDocument: externalWindowRef.current?.document }), containerEl);
Update CustomQuillEditor
to use ownerDocument
when creating elements:
const editorContainer = ownerDocument.createElement("div");
containerRef.current.appendChild(editorContainer);
This will initializes quill in the right context and editor will render properly in the new window.