react-mosaic-component2
Version:
A React Tiling Window Manager
131 lines (130 loc) • 4.55 kB
JavaScript
// src/Mosaic.tsx
import classNames from "classnames";
import { countBy, keys, pickBy } from "lodash-es";
import { HTML5toTouch } from "rdndmb-html5-to-touch";
import React from "react";
import { DndProvider } from "react-dnd";
import { MultiBackend } from "react-dnd-multi-backend";
import { v4 as uuid } from "uuid";
import { MosaicContext } from "./contextTypes.mjs";
import { MosaicRoot } from "./MosaicRoot.mjs";
import { MosaicZeroState } from "./MosaicZeroState.mjs";
import { RootDropTargets } from "./RootDropTargets.mjs";
import { createExpandUpdate, createHideUpdate, createRemoveUpdate, updateTree } from "./util/mosaicUpdates.mjs";
import { getLeaves } from "./util/mosaicUtilities.mjs";
var DEFAULT_EXPAND_PERCENTAGE = 70;
function isUncontrolled(props) {
return props.initialValue != null;
}
var MosaicWithoutDragDropContext = class extends React.PureComponent {
static defaultProps = {
onChange: () => void 0,
zeroStateView: /* @__PURE__ */ React.createElement(MosaicZeroState, null),
className: "mosaic-blueprint-theme",
blueprintNamespace: "bp3"
};
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.mosaicId && prevState.mosaicId !== nextProps.mosaicId && process.env.NODE_ENV !== "production") {
throw new Error("Mosaic does not support updating the mosaicId after instantiation");
}
if (isUncontrolled(nextProps) && nextProps.initialValue !== prevState.lastInitialValue) {
return {
lastInitialValue: nextProps.initialValue,
currentNode: nextProps.initialValue
};
}
return null;
}
state = {
currentNode: null,
lastInitialValue: null,
mosaicId: this.props.mosaicId ?? uuid()
};
render() {
const { className } = this.props;
return /* @__PURE__ */ React.createElement(MosaicContext.Provider, { value: this.childContext }, /* @__PURE__ */ React.createElement("div", { className: classNames(className, "mosaic mosaic-drop-target") }, this.renderTree(), /* @__PURE__ */ React.createElement(RootDropTargets, null)));
}
getRoot() {
if (isUncontrolled(this.props)) {
return this.state.currentNode;
} else {
return this.props.value;
}
}
updateRoot = (updates, suppressOnRelease = false) => {
const currentNode = this.getRoot() || {};
this.replaceRoot(updateTree(currentNode, updates), suppressOnRelease);
};
replaceRoot = (currentNode, suppressOnRelease = false) => {
this.props.onChange(currentNode);
if (!suppressOnRelease && this.props.onRelease) {
this.props.onRelease(currentNode);
}
if (isUncontrolled(this.props)) {
this.setState({ currentNode });
}
};
actions = {
updateTree: this.updateRoot,
remove: (path) => {
if (path.length === 0) {
this.replaceRoot(null);
} else {
this.updateRoot([createRemoveUpdate(this.getRoot(), path)]);
}
},
expand: (path, percentage = DEFAULT_EXPAND_PERCENTAGE) => this.updateRoot([createExpandUpdate(path, percentage)]),
getRoot: () => this.getRoot(),
hide: (path) => this.updateRoot([createHideUpdate(path)]),
replaceWith: (path, newNode) => this.updateRoot([
{
path,
spec: {
$set: newNode
}
}
])
};
childContext = {
mosaicActions: this.actions,
mosaicId: this.state.mosaicId,
blueprintNamespace: this.props.blueprintNamespace
};
renderTree() {
const root = this.getRoot();
this.validateTree(root);
if (root === null || root === void 0) {
return this.props.zeroStateView;
} else {
const { renderTile, resize } = this.props;
return /* @__PURE__ */ React.createElement(MosaicRoot, { root, renderTile, resize });
}
}
validateTree(node) {
if (process.env.NODE_ENV !== "production") {
const duplicates = keys(pickBy(countBy(getLeaves(node)), (n) => n > 1));
if (duplicates.length > 0) {
throw new Error(
`Duplicate IDs [${duplicates.join(", ")}] detected. Mosaic does not support leaves with the same ID`
);
}
}
}
};
var Mosaic = class extends React.PureComponent {
render() {
return /* @__PURE__ */ React.createElement(
DndProvider,
{
backend: MultiBackend,
options: HTML5toTouch,
...this.props.dragAndDropManager && { manager: this.props.dragAndDropManager }
},
/* @__PURE__ */ React.createElement(MosaicWithoutDragDropContext, { ...this.props })
);
}
};
export {
Mosaic,
MosaicWithoutDragDropContext
};