refactor(core): make lit portal a bit cleaner (#9354)

This commit is contained in:
pengx17
2024-12-27 08:34:04 +00:00
parent 67be1fb47f
commit 153c1b2c55
3 changed files with 41 additions and 53 deletions

View File

@@ -7,12 +7,11 @@ import ReactDOM from 'react-dom';
type PortalEvent = { type PortalEvent = {
name: 'connectedCallback' | 'disconnectedCallback' | 'willUpdate'; name: 'connectedCallback' | 'disconnectedCallback' | 'willUpdate';
target: LitReactPortal; target: LitReactPortal;
previousPortalId?: string;
}; };
type PortalListener = (event: PortalEvent) => void; type PortalListener = (event: PortalEvent) => void;
export function createLitPortalAnchor(callback: (event: PortalEvent) => void) { function createLitPortalAnchor(callback: (event: PortalEvent) => void) {
return html`<lit-react-portal return html`<lit-react-portal
.notify=${callback} .notify=${callback}
portalId=${nanoid()} portalId=${nanoid()}
@@ -24,7 +23,6 @@ export const LIT_REACT_PORTAL = 'lit-react-portal';
@customElement(LIT_REACT_PORTAL) @customElement(LIT_REACT_PORTAL)
class LitReactPortal extends LitElement { class LitReactPortal extends LitElement {
portalId!: string; portalId!: string;
notify!: PortalListener; notify!: PortalListener;
static override get properties() { static override get properties() {
@@ -34,6 +32,14 @@ class LitReactPortal extends LitElement {
}; };
} }
override connectedCallback() {
super.connectedCallback();
this.notify({
name: 'connectedCallback',
target: this,
});
}
override attributeChangedCallback( override attributeChangedCallback(
name: string, name: string,
oldVal: string, oldVal: string,
@@ -44,7 +50,6 @@ class LitReactPortal extends LitElement {
this.notify({ this.notify({
name: 'willUpdate', name: 'willUpdate',
target: this, target: this,
previousPortalId: oldVal,
}); });
} }
} }
@@ -54,13 +59,6 @@ class LitReactPortal extends LitElement {
return this; return this;
} }
override connectedCallback() {
this.notify({
name: 'connectedCallback',
target: this,
});
}
override disconnectedCallback() { override disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
this.notify({ this.notify({
@@ -81,6 +79,7 @@ export type ElementOrFactory = React.ReactElement | (() => React.ReactElement);
type LitPortal = { type LitPortal = {
id: string; id: string;
portal: React.ReactPortal; portal: React.ReactPortal;
litElement: LitReactPortal;
}; };
// returns a factory function that renders a given element to a lit template // returns a factory function that renders a given element to a lit template
@@ -98,48 +97,37 @@ export const useLitPortalFactory = () => {
? elementOrFactory() ? elementOrFactory()
: elementOrFactory; : elementOrFactory;
return createLitPortalAnchor(event => { return createLitPortalAnchor(event => {
const { name, target } = event;
const id = target.portalId;
if (name === 'connectedCallback') {
setPortals(portals => [
...portals,
{
id,
portal: ReactDOM.createPortal(element, target, id),
},
]);
return;
}
if (name === 'disconnectedCallback') {
setPortals(portals => portals.filter(p => p.id !== id));
return;
}
const prevId = event.previousPortalId;
// Ignores first `willUpdate`
if (!prevId) {
return;
}
setPortals(portals => { setPortals(portals => {
const portal = portals.find(p => p.id === prevId); const { name, target } = event;
if (!portal) return [...portals]; const id = target.portalId;
let newPortals = portals;
// Updates `ID` const updatePortals = () => {
// Used for portal queries in `disconnectedCallback` let oldPortalIndex = portals.findIndex(
portal.id = id; p => p.litElement === target
);
// Re-rendering oldPortalIndex =
// true: `inline link` oldPortalIndex === -1 ? portals.length : oldPortalIndex;
// false: `pdf embed view` newPortals = portals.toSpliced(oldPortalIndex, 1, {
if (rerendering) { id,
portal.portal = ReactDOM.createPortal(element, target, id); portal: ReactDOM.createPortal(element, target),
litElement: target,
});
};
switch (name) {
case 'connectedCallback':
updatePortals();
break;
case 'disconnectedCallback':
newPortals = portals.filter(p => p.litElement.isConnected);
break;
case 'willUpdate':
if (!target.isConnected || !rerendering) {
break;
}
updatePortals();
break;
} }
return newPortals;
return [...portals];
}); });
}); });
}, },

View File

@@ -357,7 +357,7 @@ export const BiDirectionalLinkPanel = () => {
{ {
<> <>
{portals.map(p => ( {portals.map(p => (
<Fragment key={p.portal.key}>{p.portal}</Fragment> <Fragment key={p.id}>{p.portal}</Fragment>
))} ))}
</> </>
} }

View File

@@ -194,7 +194,7 @@ const usePatchSpecs = (shared: boolean, mode: DocMode) => {
() => ( () => (
<> <>
{portals.map(p => ( {portals.map(p => (
<Fragment key={p.portal.key}>{p.portal}</Fragment> <Fragment key={p.id}>{p.portal}</Fragment>
))} ))}
</> </>
), ),