refactor(component): lit portal (#9303)

* removes `listeners`
* adds `notify` property into `LitReactPortal`
* adds `rerendering` argument  to element factory

Related to https://github.com/toeverything/AFFiNE/pull/9295#discussion_r1897043399
This commit is contained in:
fundon
2024-12-26 03:08:04 +00:00
parent 7b572bc89e
commit 3064f21454

View File

@@ -11,29 +11,27 @@ type PortalEvent = {
}; };
type PortalListener = (event: PortalEvent) => void; type PortalListener = (event: PortalEvent) => void;
const listeners: Set<PortalListener> = new Set();
export function createLitPortalAnchor(callback: (event: PortalEvent) => void) { export function createLitPortalAnchor(callback: (event: PortalEvent) => void) {
const id = nanoid(); const id = nanoid();
// todo(@Peng): clean up listeners? return html`<lit-react-portal
listeners.add(event => { .notify=${callback}
if (event.target.portalId !== id) { portalId=${id}
return; ></lit-react-portal>`;
}
callback(event);
});
return html`<lit-react-portal portalId=${id}></lit-react-portal>`;
} }
export const LIT_REACT_PORTAL = 'lit-react-portal'; 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;
static override get properties() { static override get properties() {
return { return {
portalId: { type: String }, portalId: { type: String },
notify: { attribute: false },
}; };
} }
@@ -44,13 +42,11 @@ class LitReactPortal extends LitElement {
) { ) {
super.attributeChangedCallback(name, oldVal, newVal); super.attributeChangedCallback(name, oldVal, newVal);
if (name.toLowerCase() === 'portalid') { if (name.toLowerCase() === 'portalid') {
listeners.forEach(l => this.notify({
l({ name: 'willUpdate',
name: 'willUpdate', target: this,
target: this, previousPortalId: oldVal,
previousPortalId: oldVal, });
})
);
} }
} }
@@ -60,22 +56,18 @@ class LitReactPortal extends LitElement {
} }
override connectedCallback() { override connectedCallback() {
listeners.forEach(l => this.notify({
l({ name: 'connectedCallback',
name: 'connectedCallback', target: this,
target: this, });
})
);
} }
override disconnectedCallback() { override disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
listeners.forEach(l => this.notify({
l({ name: 'disconnectedCallback',
name: 'disconnectedCallback', target: this,
target: this, });
})
);
} }
} }
@@ -98,24 +90,54 @@ export const useLitPortalFactory = () => {
return [ return [
useCallback( useCallback(
(elementOrFactory: React.ReactElement | (() => React.ReactElement)) => { (
elementOrFactory: React.ReactElement | (() => React.ReactElement),
rerendering = true
) => {
const element = const element =
typeof elementOrFactory === 'function' typeof elementOrFactory === 'function'
? elementOrFactory() ? elementOrFactory()
: elementOrFactory; : elementOrFactory;
return createLitPortalAnchor(event => { return createLitPortalAnchor(event => {
const portalId = event.target.portalId; 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;
// Ignore first `willUpdate`
if (!prevId) {
return;
}
// No re-rendering allowed
// Used in `pdf embed view` scenario
if (!rerendering) {
return;
}
setPortals(portals => { setPortals(portals => {
const newPortals = portals.filter( const portal = portals.find(p => p.id === prevId);
p => p.id !== event.previousPortalId && p.id !== portalId if (!portal) return portals;
);
if (event.name !== 'disconnectedCallback') { portal.id = id;
newPortals.push({ portal.portal.key = id;
id: portalId, portal.portal.children = element;
portal: ReactDOM.createPortal(element, event.target), return [...portals];
});
}
return newPortals;
}); });
}); });
}, },