@deck.gl/react
Version:
React Components for deck.gl
88 lines • 3.55 kB
JavaScript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import * as React from 'react';
import { createElement } from 'react';
import { inheritsFrom } from "./inherits-from.js";
import { Layer, View } from '@deck.gl/core';
import { isComponent } from "./evaluate-children.js";
// recursively wrap render callbacks in `View`
function wrapInView(node) {
if (typeof node === 'function') {
// React.Children does not traverse functions.
// All render callbacks must be protected under a <View>
// @ts-expect-error View is not a ReactJSXElement constructor. Only used as a temporary wrapper and will be removed in extractJSXLayers
return createElement(View, {}, node);
}
if (Array.isArray(node)) {
return node.map(wrapInView);
}
if (isComponent(node)) {
if (node.type === React.Fragment) {
return wrapInView(node.props.children);
}
if (inheritsFrom(node.type, View)) {
return node;
}
}
return node;
}
// extracts any deck.gl layers masquerading as react elements from props.children
export default function extractJSXLayers({ children, layers = [], views = null }) {
const reactChildren = []; // extract real react elements (i.e. not deck.gl layers)
const jsxLayers = []; // extracted layer from react children, will add to deck.gl layer array
const jsxViews = {};
// React.children
React.Children.forEach(wrapInView(children), reactElement => {
if (isComponent(reactElement)) {
// For some reason Children.forEach doesn't filter out `null`s
const ElementType = reactElement.type;
if (inheritsFrom(ElementType, Layer)) {
const layer = createLayer(ElementType, reactElement.props);
jsxLayers.push(layer);
}
else {
reactChildren.push(reactElement);
}
// empty id => default view
if (inheritsFrom(ElementType, View) && ElementType !== View && reactElement.props.id) {
// @ts-ignore Cannot instantiate an abstract class (View)
const view = new ElementType(reactElement.props);
jsxViews[view.id] = view;
}
}
else if (reactElement) {
reactChildren.push(reactElement);
}
});
// Avoid modifying views if no JSX views were found
if (Object.keys(jsxViews).length > 0) {
// If a view is specified in both views prop and JSX, use the one in views
if (Array.isArray(views)) {
views.forEach(view => {
jsxViews[view.id] = view;
});
}
else if (views) {
jsxViews[views.id] = views;
}
views = Object.values(jsxViews);
}
// Avoid modifying layers array if no JSX layers were found
layers = jsxLayers.length > 0 ? [...jsxLayers, ...layers] : layers;
return { layers, children: reactChildren, views };
}
function createLayer(LayerType, reactProps) {
const props = {};
// Layer.defaultProps is treated as ReactElement.defaultProps and merged into react props
// Remove them
const defaultProps = LayerType.defaultProps || {};
for (const key in reactProps) {
if (defaultProps[key] !== reactProps[key]) {
props[key] = reactProps[key];
}
}
// @ts-ignore Cannot instantiate an abstract class (Layer)
return new LayerType(props);
}
//# sourceMappingURL=extract-jsx-layers.js.map