refactor(core): make lit portal a bit cleaner (#9354)
This commit is contained in:
@@ -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];
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user