refactor(editor): improve implementation of lit adapter (#12101)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Improved mobile experience by disabling certain toolbars and slash menu features on mobile devices.
  - Introduced new modular extension classes for editor and view customization, enabling more flexible configuration of themes, AI features, and editor enhancements.
  - Added clipboard adapter configurations for a wide range of data types, improving clipboard compatibility.
  - Added a new theme extension specifically for preview scenarios.
  - Provided new hooks for block scope management in document modules.

- **Refactor**
  - Streamlined editor extension setup, consolidating options and reducing complexity for better maintainability.
  - Reorganized mobile-specific extension exports for clearer usage.
  - Refined React-to-Lit rendering API by introducing a typed alias and updating related function signatures.
  - Simplified extension registration by splitting monolithic view extension into separate common and editor view extensions.

- **Bug Fixes**
  - Corrected naming inconsistencies in internal effect tracking.

- **Chores**
  - Updated type exports and documentation comments for improved clarity and consistency.
  - Removed unused or redundant exports and functions to clean up the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Saul-Mirone
2025-05-01 14:29:11 +00:00
parent e0308c5815
commit 41d404f7f8
26 changed files with 638 additions and 575 deletions

View File

@@ -1,5 +1,6 @@
export {
type ElementOrFactory,
type ReactToLit,
useLitPortal,
useLitPortalFactory,
} from './lit-portal';

View File

@@ -1,4 +1,4 @@
import { html, LitElement } from 'lit';
import { html, LitElement, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import { nanoid } from 'nanoid';
import { useCallback, useMemo, useState } from 'react';
@@ -82,59 +82,60 @@ type LitPortal = {
litElement: LitReactPortal;
};
export type ReactToLit = (
elementOrFactory: ElementOrFactory,
rerendering?: boolean
) => TemplateResult;
// returns a factory function that renders a given element to a lit template
export const useLitPortalFactory = () => {
const [portals, setPortals] = useState<LitPortal[]>([]);
return [
useCallback(
(
elementOrFactory: React.ReactElement | (() => React.ReactElement),
rerendering = true
) => {
const element =
typeof elementOrFactory === 'function'
? elementOrFactory()
: elementOrFactory;
return createLitPortalAnchor(event => {
setPortals(portals => {
const { name, target } = event;
const id = target.portalId;
let newPortals = portals;
const updatePortals = () => {
let oldPortalIndex = portals.findIndex(
p => p.litElement === target
);
oldPortalIndex =
oldPortalIndex === -1 ? portals.length : oldPortalIndex;
newPortals = portals.toSpliced(oldPortalIndex, 1, {
id,
portal: ReactDOM.createPortal(element, target),
litElement: target,
});
};
switch (name) {
case 'connectedCallback':
updatePortals();
const reactToLit: ReactToLit = useCallback(
(elementOrFactory, rerendering) => {
const element =
typeof elementOrFactory === 'function'
? elementOrFactory()
: elementOrFactory;
return createLitPortalAnchor(event => {
setPortals(portals => {
const { name, target } = event;
const id = target.portalId;
let newPortals = portals;
const updatePortals = () => {
let oldPortalIndex = portals.findIndex(
p => p.litElement === target
);
oldPortalIndex =
oldPortalIndex === -1 ? portals.length : oldPortalIndex;
newPortals = portals.toSpliced(oldPortalIndex, 1, {
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;
case 'disconnectedCallback':
newPortals = portals.filter(p => p.litElement.isConnected);
break;
case 'willUpdate':
if (!target.isConnected || !rerendering) {
break;
}
updatePortals();
break;
}
return newPortals;
});
}
updatePortals();
break;
}
return newPortals;
});
},
[setPortals]
),
portals,
] as const;
});
},
[]
);
return [reactToLit, portals] as const;
};
// render a react element to a lit template