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:
@@ -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;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user