@vuu-ui/vuu-layout 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +30 -0
- package/src/Component.css +2 -0
- package/src/Component.tsx +20 -0
- package/src/DraggableLayout.css +18 -0
- package/src/DraggableLayout.tsx +29 -0
- package/src/__tests__/flexbox-utils.spec.js +90 -0
- package/src/action-buttons/action-buttons.css +12 -0
- package/src/action-buttons/action-buttons.tsx +30 -0
- package/src/action-buttons/index.ts +1 -0
- package/src/chest-of-drawers/Chest.css +36 -0
- package/src/chest-of-drawers/Chest.tsx +42 -0
- package/src/chest-of-drawers/Drawer.css +153 -0
- package/src/chest-of-drawers/Drawer.tsx +118 -0
- package/src/chest-of-drawers/index.ts +2 -0
- package/src/common-types.ts +9 -0
- package/src/debug.ts +16 -0
- package/src/dialog/Dialog.css +16 -0
- package/src/dialog/Dialog.tsx +59 -0
- package/src/dialog/index.ts +1 -0
- package/src/drag-drop/BoxModel.ts +546 -0
- package/src/drag-drop/DragState.ts +222 -0
- package/src/drag-drop/Draggable.ts +282 -0
- package/src/drag-drop/DropMenu.css +70 -0
- package/src/drag-drop/DropMenu.tsx +68 -0
- package/src/drag-drop/DropTarget.ts +392 -0
- package/src/drag-drop/DropTargetRenderer.css +40 -0
- package/src/drag-drop/DropTargetRenderer.tsx +284 -0
- package/src/drag-drop/dragDropTypes.ts +49 -0
- package/src/drag-drop/index.ts +4 -0
- package/src/editable-label/EditableLabel.css +28 -0
- package/src/editable-label/EditableLabel.tsx +99 -0
- package/src/editable-label/index.ts +1 -0
- package/src/flexbox/Flexbox.css +45 -0
- package/src/flexbox/Flexbox.tsx +70 -0
- package/src/flexbox/FlexboxLayout.jsx +26 -0
- package/src/flexbox/FluidGrid.css +134 -0
- package/src/flexbox/FluidGrid.tsx +84 -0
- package/src/flexbox/FluidGridLayout.tsx +10 -0
- package/src/flexbox/Splitter.css +140 -0
- package/src/flexbox/Splitter.tsx +135 -0
- package/src/flexbox/flexbox-utils.ts +128 -0
- package/src/flexbox/flexboxTypes.ts +63 -0
- package/src/flexbox/index.ts +4 -0
- package/src/flexbox/useResponsiveSizing.ts +85 -0
- package/src/flexbox/useSplitterResizing.ts +272 -0
- package/src/index.ts +20 -0
- package/src/layout-action.ts +21 -0
- package/src/layout-header/ActionButton.tsx +23 -0
- package/src/layout-header/Header.css +8 -0
- package/src/layout-header/Header.tsx +222 -0
- package/src/layout-header/index.ts +1 -0
- package/src/layout-provider/LayoutProvider.tsx +160 -0
- package/src/layout-provider/LayoutProviderContext.ts +17 -0
- package/src/layout-provider/index.ts +2 -0
- package/src/layout-provider/useLayoutDragDrop.ts +241 -0
- package/src/layout-reducer/flexUtils.ts +281 -0
- package/src/layout-reducer/index.ts +4 -0
- package/src/layout-reducer/insert-layout-element.ts +365 -0
- package/src/layout-reducer/layout-reducer.ts +255 -0
- package/src/layout-reducer/layoutTypes.ts +151 -0
- package/src/layout-reducer/layoutUtils.ts +302 -0
- package/src/layout-reducer/remove-layout-element.ts +240 -0
- package/src/layout-reducer/replace-layout-element.ts +118 -0
- package/src/layout-reducer/resize-flex-children.ts +56 -0
- package/src/layout-reducer/wrap-layout-element.ts +317 -0
- package/src/layout-view/View.css +58 -0
- package/src/layout-view/View.tsx +149 -0
- package/src/layout-view/ViewContext.ts +31 -0
- package/src/layout-view/index.ts +4 -0
- package/src/layout-view/useView.tsx +104 -0
- package/src/layout-view/useViewActionDispatcher.ts +133 -0
- package/src/layout-view/useViewResize.ts +53 -0
- package/src/layout-view/viewTypes.ts +37 -0
- package/src/palette/Palette.css +37 -0
- package/src/palette/Palette.tsx +140 -0
- package/src/palette/PaletteUitk.css +9 -0
- package/src/palette/PaletteUitk.tsx +79 -0
- package/src/palette/index.ts +2 -0
- package/src/placeholder/Placeholder.css +10 -0
- package/src/placeholder/Placeholder.tsx +39 -0
- package/src/placeholder/index.ts +1 -0
- package/src/registry/ComponentRegistry.ts +35 -0
- package/src/registry/index.ts +1 -0
- package/src/responsive/OverflowMenu.css +31 -0
- package/src/responsive/OverflowMenu.jsx +56 -0
- package/src/responsive/breakpoints.ts +48 -0
- package/src/responsive/index.ts +4 -0
- package/src/responsive/measureMinimumNodeSize.ts +23 -0
- package/src/responsive/overflowUtils.js +14 -0
- package/src/responsive/use-breakpoints.ts +100 -0
- package/src/responsive/useOverflowObserver.ts +606 -0
- package/src/responsive/useResizeObserver.ts +154 -0
- package/src/responsive/utils.ts +37 -0
- package/src/stack/Stack.css +39 -0
- package/src/stack/Stack.tsx +160 -0
- package/src/stack/StackLayout.tsx +137 -0
- package/src/stack/index.ts +3 -0
- package/src/stack/stackTypes.ts +19 -0
- package/src/tabs/TabPanel.css +12 -0
- package/src/tabs/TabPanel.tsx +17 -0
- package/src/tabs/index.ts +1 -0
- package/src/tools/config-wrapper/ConfigWrapper.jsx +53 -0
- package/src/tools/config-wrapper/index.js +1 -0
- package/src/tools/devtools-box/layout-configurator.css +112 -0
- package/src/tools/devtools-box/layout-configurator.jsx +369 -0
- package/src/tools/devtools-tree/layout-tree-viewer.css +15 -0
- package/src/tools/devtools-tree/layout-tree-viewer.jsx +36 -0
- package/src/tools/index.js +3 -0
- package/src/use-persistent-state.ts +115 -0
- package/src/utils/componentFromLayout.tsx +30 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/pathUtils.ts +294 -0
- package/src/utils/propUtils.ts +24 -0
- package/src/utils/refUtils.ts +16 -0
- package/src/utils/styleUtils.ts +14 -0
- package/src/utils/typeOf.ts +22 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import React, { ReactElement } from "react";
|
|
2
|
+
import { LayoutModel } from "../layout-reducer";
|
|
3
|
+
import { isContainer } from "../registry/ComponentRegistry";
|
|
4
|
+
import { getProp, getProps } from "./propUtils";
|
|
5
|
+
import { typeOf } from "./typeOf";
|
|
6
|
+
|
|
7
|
+
const removeFinalPathSegment = (path: string) => {
|
|
8
|
+
const pos = path.lastIndexOf(".");
|
|
9
|
+
if (pos === -1) {
|
|
10
|
+
return path;
|
|
11
|
+
} else {
|
|
12
|
+
return path.slice(0, pos);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// TODO isn't this equivalent to containerOf ?
|
|
17
|
+
export function followPathToParent(
|
|
18
|
+
source: ReactElement,
|
|
19
|
+
path: string
|
|
20
|
+
): ReactElement | null {
|
|
21
|
+
const { "data-path": dataPath, path: sourcePath = dataPath } =
|
|
22
|
+
getProps(source);
|
|
23
|
+
|
|
24
|
+
if (path === "0") return null;
|
|
25
|
+
if (path === sourcePath) return null;
|
|
26
|
+
|
|
27
|
+
return followPath(source, removeFinalPathSegment(path), true);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function findTarget(
|
|
31
|
+
source: LayoutModel,
|
|
32
|
+
test: (props: any) => boolean
|
|
33
|
+
): LayoutModel | undefined {
|
|
34
|
+
const { children, ...props } = getProps(source);
|
|
35
|
+
if (test(props)) {
|
|
36
|
+
return source;
|
|
37
|
+
} else if (React.Children.count(children) > 0) {
|
|
38
|
+
const array = React.isValidElement(children) ? [children] : children;
|
|
39
|
+
for (let child of array) {
|
|
40
|
+
const target = findTarget(child, test);
|
|
41
|
+
if (target) {
|
|
42
|
+
return target;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function containerOf(
|
|
49
|
+
source: LayoutModel,
|
|
50
|
+
target: LayoutModel
|
|
51
|
+
): LayoutModel | null {
|
|
52
|
+
if (target === source) {
|
|
53
|
+
return null;
|
|
54
|
+
} else {
|
|
55
|
+
const { path: sourcePath, children } = getProps(source);
|
|
56
|
+
|
|
57
|
+
let { idx, finalStep } = nextStep(sourcePath, getProp(target, "path"));
|
|
58
|
+
if (finalStep) {
|
|
59
|
+
return source;
|
|
60
|
+
} else if (children === undefined || children[idx] === undefined) {
|
|
61
|
+
return null;
|
|
62
|
+
} else {
|
|
63
|
+
return containerOf(children[idx], target);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Do not use React.Children.toArray,
|
|
69
|
+
// it does not preserve keys
|
|
70
|
+
export const getChild = (
|
|
71
|
+
children: ReactElement[],
|
|
72
|
+
idx: number
|
|
73
|
+
): ReactElement | undefined => {
|
|
74
|
+
// idx may be a nu,mber or string
|
|
75
|
+
if (React.isValidElement(children) && idx == 0) {
|
|
76
|
+
return children;
|
|
77
|
+
} else if (Array.isArray(children)) {
|
|
78
|
+
return children[idx];
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Use a path only to identify a component
|
|
83
|
+
export function followPathToComponent(component: ReactElement, path: string) {
|
|
84
|
+
var paths = path.split(".");
|
|
85
|
+
let children = [component];
|
|
86
|
+
|
|
87
|
+
const getChildren = (c: ReactElement) =>
|
|
88
|
+
React.isValidElement(c.props.children)
|
|
89
|
+
? [c.props.children]
|
|
90
|
+
: c.props.children;
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < paths.length; i++) {
|
|
93
|
+
const idx = parseInt(paths[i]);
|
|
94
|
+
const child = children[idx];
|
|
95
|
+
if (i === paths.length - 1) {
|
|
96
|
+
return child;
|
|
97
|
+
} else {
|
|
98
|
+
children = getChildren(child);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function followPath(
|
|
104
|
+
source: LayoutModel,
|
|
105
|
+
path: string
|
|
106
|
+
): LayoutModel | undefined;
|
|
107
|
+
export function followPath(
|
|
108
|
+
source: ReactElement,
|
|
109
|
+
path: string,
|
|
110
|
+
throwIfNotFound: true
|
|
111
|
+
): ReactElement;
|
|
112
|
+
export function followPath(source: any, path: any, throwIfNotFound = false) {
|
|
113
|
+
const { "data-path": dataPath, path: sourcePath = dataPath } =
|
|
114
|
+
getProps(source);
|
|
115
|
+
if (path.indexOf(sourcePath) !== 0) {
|
|
116
|
+
throw Error(
|
|
117
|
+
`pathUtils.followPath path ${path} is not within source path ${sourcePath}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
const route = path.slice(sourcePath.length + 1);
|
|
121
|
+
if (route === "") {
|
|
122
|
+
return source;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let target = source;
|
|
126
|
+
const paths = route.split(".");
|
|
127
|
+
|
|
128
|
+
for (var i = 0; i < paths.length; i++) {
|
|
129
|
+
if (React.Children.count(target.props.children) === 0) {
|
|
130
|
+
const message = `element at 0.${paths
|
|
131
|
+
.slice(0, i)
|
|
132
|
+
.join(".")} has no children, so cannot fulfill rest of path ${paths
|
|
133
|
+
.slice(i)
|
|
134
|
+
.join(".")}`;
|
|
135
|
+
|
|
136
|
+
if (throwIfNotFound) {
|
|
137
|
+
throw Error(message);
|
|
138
|
+
} else {
|
|
139
|
+
console.warn(message);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
target = getChild(target.props.children, parseInt(paths[i]));
|
|
145
|
+
|
|
146
|
+
if (target === undefined) {
|
|
147
|
+
const message = `model at 0.${paths
|
|
148
|
+
.slice(0, i)
|
|
149
|
+
.join(".")} has no children that fulfill next step of path ${paths
|
|
150
|
+
.slice(i)
|
|
151
|
+
.join(".")}`;
|
|
152
|
+
|
|
153
|
+
if (throwIfNotFound) {
|
|
154
|
+
throw Error(message);
|
|
155
|
+
} else {
|
|
156
|
+
console.warn(message);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return target;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function nextLeaf(root: ReactElement, path: string) {
|
|
164
|
+
const parent = followPathToParent(root, path);
|
|
165
|
+
let pathIndices = path.split(".").map((idx) => parseInt(idx, 10));
|
|
166
|
+
if (parent) {
|
|
167
|
+
const lastIdx = pathIndices.pop();
|
|
168
|
+
const { children } = parent.props;
|
|
169
|
+
if (children.length - 1 > lastIdx!) {
|
|
170
|
+
return firstLeaf(children[lastIdx! + 1]);
|
|
171
|
+
} else {
|
|
172
|
+
const parentIdx = pathIndices.pop();
|
|
173
|
+
const nextParent = followPathToParent(root, getProp(parent, "path"));
|
|
174
|
+
if (nextParent && typeof parentIdx === "number") {
|
|
175
|
+
pathIndices = nextParent.props.path
|
|
176
|
+
.split(".")
|
|
177
|
+
.map((idx: string) => parseInt(idx, 10));
|
|
178
|
+
if (nextParent.props.children.length - 1 > parentIdx) {
|
|
179
|
+
const nextStep = nextParent.props.children[parentIdx + 1];
|
|
180
|
+
if (isContainer(typeOf(nextStep) as string)) {
|
|
181
|
+
return firstLeaf(nextStep);
|
|
182
|
+
} else {
|
|
183
|
+
return nextStep;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return firstLeaf(root);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function previousLeaf(root: ReactElement, path: string) {
|
|
194
|
+
let pathIndices = path.split(".").map((idx) => parseInt(idx, 10));
|
|
195
|
+
let lastIdx = pathIndices.pop();
|
|
196
|
+
let parent = followPathToParent(root, path);
|
|
197
|
+
if (parent != null && typeof lastIdx === "number") {
|
|
198
|
+
const { children } = parent.props;
|
|
199
|
+
if (lastIdx > 0) {
|
|
200
|
+
return lastLeaf(children[lastIdx - 1]);
|
|
201
|
+
} else {
|
|
202
|
+
while (pathIndices.length > 1) {
|
|
203
|
+
lastIdx = pathIndices.pop() as number;
|
|
204
|
+
parent = followPathToParent(
|
|
205
|
+
root,
|
|
206
|
+
getProp(parent, "path")
|
|
207
|
+
) as ReactElement;
|
|
208
|
+
// pathIndices = nextParent.props.path
|
|
209
|
+
// .split(".")
|
|
210
|
+
// .map((idx) => parseInt(idx, 10));
|
|
211
|
+
if (lastIdx > 0) {
|
|
212
|
+
const nextStep = parent.props.children[lastIdx - 1];
|
|
213
|
+
if (isContainer(typeOf(nextStep) as string)) {
|
|
214
|
+
return lastLeaf(nextStep);
|
|
215
|
+
} else {
|
|
216
|
+
return nextStep;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return lastLeaf(root);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function firstLeaf(layoutRoot: ReactElement): ReactElement {
|
|
226
|
+
if (isContainer(typeOf(layoutRoot) as string)) {
|
|
227
|
+
const { children } = layoutRoot.props || layoutRoot;
|
|
228
|
+
return firstLeaf(children[0]);
|
|
229
|
+
} else {
|
|
230
|
+
return layoutRoot;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function lastLeaf(root: ReactElement): ReactElement {
|
|
235
|
+
if (isContainer(typeOf(root) as string)) {
|
|
236
|
+
const { children } = root.props || root;
|
|
237
|
+
return lastLeaf(children[children.length - 1]);
|
|
238
|
+
} else {
|
|
239
|
+
return root;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
type NextStepResult = {
|
|
244
|
+
idx: number;
|
|
245
|
+
finalStep: boolean;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export function nextStep(
|
|
249
|
+
pathSoFar: string,
|
|
250
|
+
targetPath: string,
|
|
251
|
+
followPathToEnd = false
|
|
252
|
+
): NextStepResult {
|
|
253
|
+
if (pathSoFar === targetPath) {
|
|
254
|
+
return { idx: -1, finalStep: true };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const pathVisited = `${pathSoFar}.`;
|
|
258
|
+
if (!targetPath.startsWith(pathVisited)) {
|
|
259
|
+
throw Error("pathUtils nextStep has strayed from the path");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const endOfTheLine = followPathToEnd ? 0 : 1;
|
|
263
|
+
// check that pathSoFar startsWith targetPath and if not, throw
|
|
264
|
+
const paths = targetPath
|
|
265
|
+
.replace(pathVisited, "")
|
|
266
|
+
.split(".")
|
|
267
|
+
.map((n) => parseInt(n, 10));
|
|
268
|
+
return { idx: paths[0], finalStep: paths.length === endOfTheLine };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function resetPath(
|
|
272
|
+
model: ReactElement,
|
|
273
|
+
path: string,
|
|
274
|
+
additionalProps?: any
|
|
275
|
+
): ReactElement {
|
|
276
|
+
if (getProp(model, "path") === path) {
|
|
277
|
+
return model;
|
|
278
|
+
}
|
|
279
|
+
const children: ReactElement[] = [];
|
|
280
|
+
// React.Children.map rewrites keys, forEach does not
|
|
281
|
+
React.Children.forEach(model.props.children, (child, i) => {
|
|
282
|
+
if (!getProp(child, "path")) {
|
|
283
|
+
children.push(child);
|
|
284
|
+
} else {
|
|
285
|
+
children.push(resetPath(child, `${path}.${i}`));
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
const pathPropName = model.props["data-path"] ? "data-path" : "path";
|
|
289
|
+
return React.cloneElement(
|
|
290
|
+
model,
|
|
291
|
+
{ [pathPropName]: path, ...additionalProps },
|
|
292
|
+
children
|
|
293
|
+
);
|
|
294
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { LayoutModel } from '../layout-reducer';
|
|
3
|
+
|
|
4
|
+
const NO_PROPS = {};
|
|
5
|
+
export const getProp = (component: LayoutModel, propName: string) => {
|
|
6
|
+
const props = getProps(component);
|
|
7
|
+
return props[propName] ?? props[`data-${propName}`];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getProps = (component?: LayoutModel) => component?.props || component || NO_PROPS;
|
|
11
|
+
|
|
12
|
+
// Used when a container is expected to have a single child
|
|
13
|
+
export const getChildProp = (container: LayoutModel) => {
|
|
14
|
+
const props = getProps(container);
|
|
15
|
+
if (props.children) {
|
|
16
|
+
const {
|
|
17
|
+
children: [target, ...rest]
|
|
18
|
+
} = props;
|
|
19
|
+
if (rest.length > 0) {
|
|
20
|
+
console.warn(`getChild expected a single child, found ${rest.length + 1}`);
|
|
21
|
+
}
|
|
22
|
+
return target as ReactElement;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MutableRefObject } from "react";
|
|
2
|
+
|
|
3
|
+
export function setRef<T>(
|
|
4
|
+
ref:
|
|
5
|
+
| MutableRefObject<T | null>
|
|
6
|
+
| ((instance: T | null) => void)
|
|
7
|
+
| null
|
|
8
|
+
| undefined,
|
|
9
|
+
value: T | null
|
|
10
|
+
): void {
|
|
11
|
+
if (typeof ref === "function") {
|
|
12
|
+
ref(value);
|
|
13
|
+
} else if (ref) {
|
|
14
|
+
ref.current = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CSSProperties } from 'react';
|
|
2
|
+
export type CSSFlexProperties = Pick<CSSProperties, 'flexBasis' | 'flexGrow' | 'flexShrink'>;
|
|
3
|
+
|
|
4
|
+
export const expandFlex = (flex: number | CSSFlexProperties): CSSFlexProperties => {
|
|
5
|
+
if (typeof flex === 'number') {
|
|
6
|
+
return {
|
|
7
|
+
flexBasis: 0,
|
|
8
|
+
flexGrow: 1,
|
|
9
|
+
flexShrink: 1
|
|
10
|
+
};
|
|
11
|
+
} else {
|
|
12
|
+
throw Error(`"no support yet for flex value ${flex}`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { LayoutModel, WithType } from '../layout-reducer';
|
|
3
|
+
|
|
4
|
+
//TODO this should throw if we cannot identify a type
|
|
5
|
+
export function typeOf(element?: LayoutModel | WithType): string | undefined {
|
|
6
|
+
if (element) {
|
|
7
|
+
const type = element.type as any;
|
|
8
|
+
if (typeof type === 'function' || typeof type === 'object') {
|
|
9
|
+
const elementName = type.displayName || type.name || type.type?.name;
|
|
10
|
+
if (typeof elementName === 'string') {
|
|
11
|
+
return elementName;
|
|
12
|
+
}
|
|
13
|
+
} else if (typeof element.type === 'string') {
|
|
14
|
+
return element.type;
|
|
15
|
+
} else if (element.constructor) {
|
|
16
|
+
return (element.constructor as any).displayName as string;
|
|
17
|
+
}
|
|
18
|
+
throw Error(`typeOf unable to determine type of element`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const isTypeOf = (element: ReactElement, type: string) => typeOf(element) === type;
|