1
0
mirror of https://github.com/excalidraw/excalidraw.git synced 2024-11-10 11:35:52 +01:00

feat: render unknown supplied children to UI (#6096)

This commit is contained in:
David Luzar 2023-01-12 15:20:16 +01:00 committed by GitHub
parent 699897f71b
commit 0982da38fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 24 deletions

@ -15,11 +15,7 @@ import {
BinaryFiles, BinaryFiles,
UIChildrenComponents, UIChildrenComponents,
} from "../types"; } from "../types";
import { import { isShallowEqual, muteFSAbortError, getReactChildren } from "../utils";
isShallowEqual,
muteFSAbortError,
ReactChildrenToObject,
} from "../utils";
import { SelectedShapeActions, ShapesSwitcher } from "./Actions"; import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
import CollabButton from "./CollabButton"; import CollabButton from "./CollabButton";
import { ErrorDialog } from "./ErrorDialog"; import { ErrorDialog } from "./ErrorDialog";
@ -111,8 +107,11 @@ const LayerUI = ({
}: LayerUIProps) => { }: LayerUIProps) => {
const device = useDevice(); const device = useDevice();
const childrenComponents = const [childrenComponents, restChildren] =
ReactChildrenToObject<UIChildrenComponents>(children); getReactChildren<UIChildrenComponents>(children, {
Menu: true,
FooterCenter: true,
});
const renderJSONExportDialog = () => { const renderJSONExportDialog = () => {
if (!UIOptions.canvasActions.export) { if (!UIOptions.canvasActions.export) {
@ -390,6 +389,7 @@ const LayerUI = ({
return ( return (
<> <>
{restChildren}
{appState.isLoading && <LoadingMessage delay={250} />} {appState.isLoading && <LoadingMessage delay={250} />}
{appState.errorMessage && ( {appState.errorMessage && (
<ErrorDialog <ErrorDialog

@ -15,6 +15,8 @@ Please add the latest change on the top under the correct section.
### Features ### Features
- Any top-level children passed to the `<Excalidraw/>` component that do not belong to one of the officially supported Excalidraw children components are now rendered directly inside the Excalidraw container (previously, they weren't rendered at all) [#6096](https://github.com/excalidraw/excalidraw/pull/6096).
- Expose component API for the Excalidraw main menu [#6034](https://github.com/excalidraw/excalidraw/pull/6034), You can read more about its usage [here](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#MainMenu) - Expose component API for the Excalidraw main menu [#6034](https://github.com/excalidraw/excalidraw/pull/6034), You can read more about its usage [here](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#MainMenu)
- Render Footer as a component instead of render prop [#5970](https://github.com/excalidraw/excalidraw/pull/5970). You can read more about its usage [here](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#Footer) - Render Footer as a component instead of render prop [#5970](https://github.com/excalidraw/excalidraw/pull/5970). You can read more about its usage [here](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#Footer)

@ -687,27 +687,46 @@ export const queryFocusableElements = (container: HTMLElement | null) => {
: []; : [];
}; };
export const ReactChildrenToObject = < /**
T extends { * Partitions React children into named components and the rest of children.
[k in string]?: *
| React.ReactPortal * Returns known children as a dictionary of react children keyed by their
| React.ReactElement<unknown, string | React.JSXElementConstructor<any>>; * displayName, and the rest children as an array.
*
* NOTE all named react components are included in the dictionary, irrespective
* of the supplied type parameter. This means you may be throwing away
* children that you aren't expecting, but should nonetheless be rendered.
* To guard against this (provided you care about the rest children at all),
* supply a second parameter with an object with keys of the expected children.
*/
export const getReactChildren = <
KnownChildren extends {
[k in string]?: React.ReactNode;
}, },
>( >(
children: React.ReactNode, children: React.ReactNode,
expectedComponents?: Record<keyof KnownChildren, any>,
) => { ) => {
return React.Children.toArray(children).reduce((acc, child) => { const restChildren: React.ReactNode[] = [];
if (
React.isValidElement(child) && const knownChildren = React.Children.toArray(children).reduce(
typeof child.type !== "string" && (acc, child) => {
//@ts-ignore if (
child?.type.displayName React.isValidElement(child) &&
) { (!expectedComponents ||
// @ts-ignore ((child.type as any).displayName as string) in expectedComponents)
acc[child.type.displayName] = child; ) {
} // @ts-ignore
return acc; acc[child.type.displayName] = child;
}, {} as Partial<T>); } else {
restChildren.push(child);
}
return acc;
},
{} as Partial<KnownChildren>,
);
return [knownChildren, restChildren] as const;
}; };
export const isShallowEqual = <T extends Record<string, any>>( export const isShallowEqual = <T extends Record<string, any>>(