1
0
mirror of https://github.com/excalidraw/excalidraw.git synced 2024-11-02 03:25:53 +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,
UIChildrenComponents,
} from "../types";
import {
isShallowEqual,
muteFSAbortError,
ReactChildrenToObject,
} from "../utils";
import { isShallowEqual, muteFSAbortError, getReactChildren } from "../utils";
import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
import CollabButton from "./CollabButton";
import { ErrorDialog } from "./ErrorDialog";
@ -111,8 +107,11 @@ const LayerUI = ({
}: LayerUIProps) => {
const device = useDevice();
const childrenComponents =
ReactChildrenToObject<UIChildrenComponents>(children);
const [childrenComponents, restChildren] =
getReactChildren<UIChildrenComponents>(children, {
Menu: true,
FooterCenter: true,
});
const renderJSONExportDialog = () => {
if (!UIOptions.canvasActions.export) {
@ -390,6 +389,7 @@ const LayerUI = ({
return (
<>
{restChildren}
{appState.isLoading && <LoadingMessage delay={250} />}
{appState.errorMessage && (
<ErrorDialog

@ -15,6 +15,8 @@ Please add the latest change on the top under the correct section.
### 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)
- 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 {
[k in string]?:
| React.ReactPortal
| React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
/**
* Partitions React children into named components and the rest of children.
*
* Returns known children as a dictionary of react children keyed by their
* 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,
expectedComponents?: Record<keyof KnownChildren, any>,
) => {
return React.Children.toArray(children).reduce((acc, child) => {
if (
React.isValidElement(child) &&
typeof child.type !== "string" &&
//@ts-ignore
child?.type.displayName
) {
// @ts-ignore
acc[child.type.displayName] = child;
}
return acc;
}, {} as Partial<T>);
const restChildren: React.ReactNode[] = [];
const knownChildren = React.Children.toArray(children).reduce(
(acc, child) => {
if (
React.isValidElement(child) &&
(!expectedComponents ||
((child.type as any).displayName as string) in expectedComponents)
) {
// @ts-ignore
acc[child.type.displayName] = child;
} else {
restChildren.push(child);
}
return acc;
},
{} as Partial<KnownChildren>,
);
return [knownChildren, restChildren] as const;
};
export const isShallowEqual = <T extends Record<string, any>>(