@vuu-ui/vuu-layout 2.1.19-beta.1 → 2.1.19-beta.2
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/package.json +12 -13
- package/src/Component.js +14 -0
- package/src/LayoutContainer.css.js +24 -0
- package/src/LayoutContainer.js +26 -0
- package/src/debug.js +16 -0
- package/src/dock-layout/DockLayout.css.js +36 -0
- package/src/dock-layout/DockLayout.js +43 -0
- package/src/dock-layout/Drawer.css.js +150 -0
- package/src/dock-layout/Drawer.js +82 -0
- package/src/dock-layout/index.js +2 -0
- package/src/drag-drop/BoxModel.js +337 -0
- package/src/drag-drop/DragState.js +119 -0
- package/src/drag-drop/Draggable.js +140 -0
- package/src/drag-drop/DropTarget.js +286 -0
- package/src/drag-drop/DropTargetRenderer.js +275 -0
- package/src/drag-drop/dragDropTypes.js +0 -0
- package/src/drag-drop/index.js +3 -0
- package/src/flexbox/Flexbox.css.js +45 -0
- package/src/flexbox/Flexbox.js +45 -0
- package/src/flexbox/FlexboxLayout.js +27 -0
- package/src/flexbox/Splitter.css.js +57 -0
- package/src/flexbox/Splitter.js +112 -0
- package/src/flexbox/flexbox-utils.js +92 -0
- package/src/flexbox/flexboxTypes.js +0 -0
- package/src/flexbox/index.js +2 -0
- package/src/flexbox/useSplitterResizing.js +181 -0
- package/src/index.js +20 -0
- package/src/layout-action.js +20 -0
- package/src/layout-header/ActionButton.js +15 -0
- package/src/layout-header/Header.css.js +32 -0
- package/src/layout-header/Header.js +99 -0
- package/src/layout-header/index.js +1 -0
- package/src/layout-header/useHeader.js +76 -0
- package/src/layout-provider/LayoutProvider.js +242 -0
- package/src/layout-provider/LayoutProviderContext.js +13 -0
- package/src/layout-provider/index.js +2 -0
- package/src/layout-provider/useLayoutDragDrop.js +147 -0
- package/src/layout-reducer/flexUtils.js +186 -0
- package/src/layout-reducer/index.js +4 -0
- package/src/layout-reducer/insert-layout-element.js +179 -0
- package/src/layout-reducer/layout-reducer.js +121 -0
- package/src/layout-reducer/layoutTypes.js +34 -0
- package/src/layout-reducer/layoutUtils.js +178 -0
- package/src/layout-reducer/move-layout-element.js +18 -0
- package/src/layout-reducer/remove-layout-element.js +161 -0
- package/src/layout-reducer/replace-layout-element.js +70 -0
- package/src/layout-reducer/resize-flex-children.js +48 -0
- package/src/layout-reducer/wrap-layout-element.js +134 -0
- package/src/layout-view/View.css.js +76 -0
- package/src/layout-view/View.js +116 -0
- package/src/layout-view/useView.js +69 -0
- package/src/layout-view/useViewBroadcastChannel.js +29 -0
- package/src/layout-view/useViewResize.js +27 -0
- package/src/layout-view/viewTypes.js +0 -0
- package/src/layout-view-actions/ViewContext.js +11 -0
- package/src/layout-view-actions/useViewActionDispatcher.js +116 -0
- package/src/layout-view-actions/useViewContributions.js +31 -0
- package/src/palette/Palette.css.js +31 -0
- package/src/palette/Palette.js +85 -0
- package/src/palette/index.js +1 -0
- package/src/placeholder/LayoutStartPanel.css.js +35 -0
- package/src/placeholder/LayoutStartPanel.js +57 -0
- package/src/placeholder/Placeholder.css.js +20 -0
- package/src/placeholder/Placeholder.js +33 -0
- package/src/placeholder/index.js +1 -0
- package/src/responsive/index.js +2 -0
- package/src/responsive/measureMinimumNodeSize.js +24 -0
- package/src/responsive/useResizeObserver.js +109 -0
- package/src/responsive/utils.js +27 -0
- package/src/stack/Stack.css.js +44 -0
- package/src/stack/Stack.js +98 -0
- package/src/stack/StackLayout.js +116 -0
- package/src/stack/index.js +3 -0
- package/src/stack/stackTypes.js +0 -0
- package/src/tabs/TabPanel.css.js +17 -0
- package/src/tabs/TabPanel.js +11 -0
- package/src/tabs/index.js +1 -0
- package/src/tools/config-wrapper/ConfigWrapper.js +55 -0
- package/src/tools/config-wrapper/index.js +1 -0
- package/src/tools/devtools-box/layout-configurator.css.js +113 -0
- package/src/tools/devtools-tree/layout-tree-viewer.css.js +17 -0
- package/src/tools/index.js +3 -0
- package/src/use-persistent-state.js +43 -0
- package/src/utils/index.js +5 -0
- package/src/utils/pathUtils.js +212 -0
- package/src/utils/propUtils.js +15 -0
- package/src/utils/refUtils.js +5 -0
- package/src/utils/styleUtils.js +9 -0
- package/src/utils/typeOf.js +15 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
const isMessageForSelf = (message, id, path)=>{
|
|
3
|
+
if (id && message.targetId === id) return true;
|
|
4
|
+
if (message.path && path?.startsWith(message.path)) return true;
|
|
5
|
+
return false;
|
|
6
|
+
};
|
|
7
|
+
const useViewBroadcastChannel = (id, path, onMessageReceived)=>{
|
|
8
|
+
const broadcastChannelRef = useRef(void 0);
|
|
9
|
+
useEffect(()=>{
|
|
10
|
+
const broadcastChannel = new BroadcastChannel("vuu");
|
|
11
|
+
broadcastChannel.onmessage = (evt)=>{
|
|
12
|
+
if (isMessageForSelf(evt.data, id, path)) onMessageReceived?.(evt.data);
|
|
13
|
+
};
|
|
14
|
+
broadcastChannelRef.current = broadcastChannel;
|
|
15
|
+
return ()=>{
|
|
16
|
+
broadcastChannel.close();
|
|
17
|
+
broadcastChannelRef.current = void 0;
|
|
18
|
+
};
|
|
19
|
+
}, [
|
|
20
|
+
id,
|
|
21
|
+
onMessageReceived,
|
|
22
|
+
path
|
|
23
|
+
]);
|
|
24
|
+
const sendMessage = useCallback((message)=>{
|
|
25
|
+
broadcastChannelRef.current?.postMessage(message);
|
|
26
|
+
}, []);
|
|
27
|
+
return sendMessage;
|
|
28
|
+
};
|
|
29
|
+
export { useViewBroadcastChannel };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { WidthHeight, useResizeObserver } from "../responsive/index.js";
|
|
2
|
+
import { useCallback, useRef } from "react";
|
|
3
|
+
const NO_MEASUREMENT = [];
|
|
4
|
+
const useViewResize = ({ mainRef, resize = "responsive", rootRef })=>{
|
|
5
|
+
const deferResize = "defer" === resize;
|
|
6
|
+
const mainSize = useRef({});
|
|
7
|
+
const resizeHandle = useRef(void 0);
|
|
8
|
+
const setMainSize = useCallback(()=>{
|
|
9
|
+
if (mainRef.current) {
|
|
10
|
+
mainRef.current.style.height = mainSize.current.height + "px";
|
|
11
|
+
mainRef.current.style.width = mainSize.current.width + "px";
|
|
12
|
+
}
|
|
13
|
+
resizeHandle.current = void 0;
|
|
14
|
+
}, [
|
|
15
|
+
mainRef
|
|
16
|
+
]);
|
|
17
|
+
const onResize = useCallback(({ height, width })=>{
|
|
18
|
+
mainSize.current.height = height;
|
|
19
|
+
mainSize.current.width = width;
|
|
20
|
+
if (null !== resizeHandle.current) clearTimeout(resizeHandle.current);
|
|
21
|
+
resizeHandle.current = window.setTimeout(setMainSize, 40);
|
|
22
|
+
}, [
|
|
23
|
+
setMainSize
|
|
24
|
+
]);
|
|
25
|
+
useResizeObserver(rootRef, deferResize ? WidthHeight : NO_MEASUREMENT, onResize, deferResize);
|
|
26
|
+
};
|
|
27
|
+
export { useViewResize };
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import react, { useContext } from "react";
|
|
2
|
+
const NO_CONTEXT = {
|
|
3
|
+
dispatch: null
|
|
4
|
+
};
|
|
5
|
+
const ViewContext = react.createContext(NO_CONTEXT);
|
|
6
|
+
const useViewDispatch = ()=>{
|
|
7
|
+
const context = useContext(ViewContext);
|
|
8
|
+
return context?.dispatch ?? null;
|
|
9
|
+
};
|
|
10
|
+
const useViewContext = ()=>useContext(ViewContext);
|
|
11
|
+
export { ViewContext, useViewContext, useViewDispatch };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useSessionDataSource } from "@vuu-ui/vuu-data-react";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { useLayoutProviderDispatch } from "../layout-provider/LayoutProvider.js";
|
|
4
|
+
import { useViewBroadcastChannel } from "../layout-view/useViewBroadcastChannel.js";
|
|
5
|
+
import { usePersistentState } from "../use-persistent-state.js";
|
|
6
|
+
import { useViewContributions } from "./useViewContributions.js";
|
|
7
|
+
const useViewActionDispatcher = (id, rootRef, viewPath, dropTargets)=>{
|
|
8
|
+
const { purgeState } = usePersistentState();
|
|
9
|
+
const { clearDataSource: clearDataSourceFromSessionState } = useSessionDataSource();
|
|
10
|
+
const { clearContributions, contributions, updateContributions } = useViewContributions({
|
|
11
|
+
sessionKey: id
|
|
12
|
+
});
|
|
13
|
+
const dispatchLayoutAction = useLayoutProviderDispatch();
|
|
14
|
+
const unsubscribeAndClearState = useCallback(()=>{
|
|
15
|
+
clearDataSourceFromSessionState(id, true);
|
|
16
|
+
purgeState(id);
|
|
17
|
+
}, [
|
|
18
|
+
clearDataSourceFromSessionState,
|
|
19
|
+
id,
|
|
20
|
+
purgeState
|
|
21
|
+
]);
|
|
22
|
+
const handleRemove = useCallback(()=>{
|
|
23
|
+
unsubscribeAndClearState();
|
|
24
|
+
dispatchLayoutAction({
|
|
25
|
+
type: "remove",
|
|
26
|
+
path: viewPath
|
|
27
|
+
});
|
|
28
|
+
}, [
|
|
29
|
+
unsubscribeAndClearState,
|
|
30
|
+
dispatchLayoutAction,
|
|
31
|
+
viewPath
|
|
32
|
+
]);
|
|
33
|
+
const handleMouseDown = useCallback(async (evt, index, preDragActivity)=>{
|
|
34
|
+
evt.stopPropagation();
|
|
35
|
+
const dragRect = rootRef.current?.getBoundingClientRect();
|
|
36
|
+
return new Promise((resolve, reject)=>{
|
|
37
|
+
dispatchLayoutAction({
|
|
38
|
+
type: "drag-start",
|
|
39
|
+
evt,
|
|
40
|
+
path: void 0 === index ? viewPath : `${viewPath}.${index}`,
|
|
41
|
+
dragRect,
|
|
42
|
+
preDragActivity,
|
|
43
|
+
dropTargets,
|
|
44
|
+
resolveDragStart: resolve,
|
|
45
|
+
rejectDragStart: reject
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}, [
|
|
49
|
+
rootRef,
|
|
50
|
+
dispatchLayoutAction,
|
|
51
|
+
viewPath,
|
|
52
|
+
dropTargets
|
|
53
|
+
]);
|
|
54
|
+
const handleMessageReceived = useCallback((message)=>{
|
|
55
|
+
switch(message.type){
|
|
56
|
+
case "highlight-on":
|
|
57
|
+
rootRef?.current?.classList.add("vuuHighlighted");
|
|
58
|
+
break;
|
|
59
|
+
case "highlight-off":
|
|
60
|
+
rootRef?.current?.classList.remove("vuuHighlighted");
|
|
61
|
+
break;
|
|
62
|
+
case "layout-closed":
|
|
63
|
+
unsubscribeAndClearState();
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
console.log(`received ${message.type} message`);
|
|
67
|
+
}
|
|
68
|
+
}, [
|
|
69
|
+
rootRef,
|
|
70
|
+
unsubscribeAndClearState
|
|
71
|
+
]);
|
|
72
|
+
const sendMessage = useViewBroadcastChannel(id, viewPath, handleMessageReceived);
|
|
73
|
+
const dispatchAction = useCallback(async (action, evt)=>{
|
|
74
|
+
const { type } = action;
|
|
75
|
+
switch(type){
|
|
76
|
+
case "collapse":
|
|
77
|
+
case "expand":
|
|
78
|
+
return dispatchLayoutAction({
|
|
79
|
+
type,
|
|
80
|
+
path: action.path ?? viewPath
|
|
81
|
+
});
|
|
82
|
+
case "remove":
|
|
83
|
+
return handleRemove();
|
|
84
|
+
case "mousedown":
|
|
85
|
+
return handleMouseDown(evt, action.index, action.preDragActivity);
|
|
86
|
+
case "add-toolbar-contribution":
|
|
87
|
+
return updateContributions(action.location, action.content);
|
|
88
|
+
case "remove-toolbar-contribution":
|
|
89
|
+
return clearContributions();
|
|
90
|
+
case "query":
|
|
91
|
+
return dispatchLayoutAction({
|
|
92
|
+
type,
|
|
93
|
+
path: action.path,
|
|
94
|
+
query: action.query
|
|
95
|
+
});
|
|
96
|
+
case "broadcast-message":
|
|
97
|
+
sendMessage(action.message);
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}, [
|
|
103
|
+
dispatchLayoutAction,
|
|
104
|
+
viewPath,
|
|
105
|
+
handleRemove,
|
|
106
|
+
handleMouseDown,
|
|
107
|
+
updateContributions,
|
|
108
|
+
clearContributions,
|
|
109
|
+
sendMessage
|
|
110
|
+
]);
|
|
111
|
+
return [
|
|
112
|
+
dispatchAction,
|
|
113
|
+
contributions
|
|
114
|
+
];
|
|
115
|
+
};
|
|
116
|
+
export { useViewActionDispatcher };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
const sessionState = new Map();
|
|
3
|
+
const EMPTY_ARRAY = [];
|
|
4
|
+
const useViewContributions = ({ sessionKey })=>{
|
|
5
|
+
const [contributions, setContributions] = useState(EMPTY_ARRAY);
|
|
6
|
+
const updateContributions = useCallback((location, content)=>{
|
|
7
|
+
const updatedContributions = contributions.concat([
|
|
8
|
+
{
|
|
9
|
+
location,
|
|
10
|
+
content
|
|
11
|
+
}
|
|
12
|
+
]);
|
|
13
|
+
sessionState.set(sessionKey, updatedContributions);
|
|
14
|
+
setContributions(updatedContributions);
|
|
15
|
+
}, [
|
|
16
|
+
contributions,
|
|
17
|
+
sessionKey
|
|
18
|
+
]);
|
|
19
|
+
const clearContributions = useCallback(()=>{
|
|
20
|
+
sessionState.delete(sessionKey);
|
|
21
|
+
setContributions([]);
|
|
22
|
+
}, [
|
|
23
|
+
sessionKey
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
clearContributions,
|
|
27
|
+
contributions,
|
|
28
|
+
updateContributions
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export { useViewContributions };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const css = `
|
|
2
|
+
.vuuPalette {
|
|
3
|
+
--vuuList-borderStyle: none;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.vuuPalette-horizontal {
|
|
7
|
+
align-items: center;
|
|
8
|
+
display: flex;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.vuuPaletteItem {
|
|
12
|
+
--vuu-icon-color: var(--salt-content-secondary-foreground);
|
|
13
|
+
--list-item-text-padding: 0 0 0 calc(var(--salt-size-unit) * 3);
|
|
14
|
+
gap: var(--salt-spacing-200);
|
|
15
|
+
padding-left: 12px;
|
|
16
|
+
font-size: 12px;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.vuuSimpleDraggableWrapper > .vuuPaletteItem {
|
|
21
|
+
--vuu-icon-color: var(--salt-selectable-foreground);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.salt-theme .vuuPaletteItem {
|
|
25
|
+
font-size: 11px;
|
|
26
|
+
font-weight: normal;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
`;
|
|
31
|
+
export default css;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ListBox, Option } from "@salt-ds/core";
|
|
3
|
+
import { queryClosest, registerComponent, uuid } from "@vuu-ui/vuu-utils";
|
|
4
|
+
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
5
|
+
import { useWindow } from "@salt-ds/window";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { cloneElement, memo, useCallback } from "react";
|
|
8
|
+
import { useLayoutProviderDispatch } from "../layout-provider/index.js";
|
|
9
|
+
import { View } from "../layout-view/View.js";
|
|
10
|
+
import Palette from "./Palette.css";
|
|
11
|
+
const classBase = "vuuPalette";
|
|
12
|
+
const clonePaletteItem = (paletteItem)=>{
|
|
13
|
+
const dolly = paletteItem.cloneNode(true);
|
|
14
|
+
dolly.id = "";
|
|
15
|
+
delete dolly.dataset.idx;
|
|
16
|
+
return dolly;
|
|
17
|
+
};
|
|
18
|
+
const wrapInView = (component, viewProps)=>{
|
|
19
|
+
const id = uuid();
|
|
20
|
+
return /*#__PURE__*/ jsx(View, {
|
|
21
|
+
id: id,
|
|
22
|
+
...viewProps,
|
|
23
|
+
children: component
|
|
24
|
+
}, id);
|
|
25
|
+
};
|
|
26
|
+
const PaletteItem = /*#__PURE__*/ memo(({ className, component, idx, key, value, ViewProps, ...props })=>{
|
|
27
|
+
const targetWindow = useWindow();
|
|
28
|
+
useComponentCssInjection({
|
|
29
|
+
testId: "vuu-palette",
|
|
30
|
+
css: Palette,
|
|
31
|
+
window: targetWindow
|
|
32
|
+
});
|
|
33
|
+
const dispatch = useLayoutProviderDispatch();
|
|
34
|
+
const handleMouseDown = useCallback((e)=>{
|
|
35
|
+
const el = queryClosest(e.target, ".vuuPaletteItem", true);
|
|
36
|
+
const { height, left, top, width } = el.getBoundingClientRect();
|
|
37
|
+
dispatch({
|
|
38
|
+
dragRect: {
|
|
39
|
+
left,
|
|
40
|
+
top,
|
|
41
|
+
right: left + width,
|
|
42
|
+
bottom: top + 150,
|
|
43
|
+
width,
|
|
44
|
+
height
|
|
45
|
+
},
|
|
46
|
+
dragElement: clonePaletteItem(el),
|
|
47
|
+
evt: e.nativeEvent,
|
|
48
|
+
instructions: {
|
|
49
|
+
DoNotRemove: true,
|
|
50
|
+
DoNotTransform: true,
|
|
51
|
+
DriftHomeIfNoDropTarget: true,
|
|
52
|
+
RemoveDraggableOnDragEnd: true,
|
|
53
|
+
dragThreshold: 10
|
|
54
|
+
},
|
|
55
|
+
path: "*",
|
|
56
|
+
payload: wrapInView(component, ViewProps),
|
|
57
|
+
type: "drag-start"
|
|
58
|
+
});
|
|
59
|
+
}, [
|
|
60
|
+
ViewProps,
|
|
61
|
+
component,
|
|
62
|
+
dispatch
|
|
63
|
+
]);
|
|
64
|
+
return /*#__PURE__*/ jsx(Option, {
|
|
65
|
+
className: clsx("vuuPaletteItem", className),
|
|
66
|
+
"data-draggable": true,
|
|
67
|
+
"data-index": idx,
|
|
68
|
+
onMouseDown: handleMouseDown,
|
|
69
|
+
value: value,
|
|
70
|
+
...props
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
PaletteItem.displayName = "PaletteItem";
|
|
74
|
+
const Palette_Palette = ({ ListBoxProps, ViewProps, children, className, orientation = "horizontal", ...props })=>/*#__PURE__*/ jsx(ListBox, {
|
|
75
|
+
...ListBoxProps,
|
|
76
|
+
...props,
|
|
77
|
+
className: clsx(classBase, className, `${classBase}-${orientation}`),
|
|
78
|
+
selected: [],
|
|
79
|
+
children: children.map((child, idx)=>child.type === PaletteItem ? /*#__PURE__*/ cloneElement(child, {
|
|
80
|
+
idx,
|
|
81
|
+
key: idx
|
|
82
|
+
}) : child)
|
|
83
|
+
});
|
|
84
|
+
registerComponent("Palette", Palette_Palette, "view");
|
|
85
|
+
export { PaletteItem, Palette_Palette as Palette };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Palette.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const css = `
|
|
2
|
+
.vuuLayoutStartPanel {
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 12px;
|
|
6
|
+
font-weight: 700;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex: none !important;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.vuuLayoutStartPanel-title {
|
|
12
|
+
color: var(--vuu-color-gray-80);
|
|
13
|
+
text-align: center;
|
|
14
|
+
font-size: 28px;
|
|
15
|
+
line-height: 1.5;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.vuuLayoutStartPanel-text {
|
|
19
|
+
color: var(--vuu-color-gray-50);
|
|
20
|
+
max-width: 460px;
|
|
21
|
+
font-size: 18px;
|
|
22
|
+
line-height: 1.5;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.vuuLayoutStartPanel-addButton {
|
|
26
|
+
--vuu-icon-size: 20px;
|
|
27
|
+
--saltButton-height: 56px;
|
|
28
|
+
--saltButton-width: 56px;
|
|
29
|
+
box-shadow: 0 4px 4px #00000040;
|
|
30
|
+
--saltButton-borderRadius: 28px !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
`;
|
|
35
|
+
export default css;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { IconButton } from "@vuu-ui/vuu-ui-controls";
|
|
3
|
+
import { VuuShellLocation } from "@vuu-ui/vuu-utils";
|
|
4
|
+
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
5
|
+
import { useWindow } from "@salt-ds/window";
|
|
6
|
+
import { useMemo, useState } from "react";
|
|
7
|
+
import { useViewContext } from "../layout-view-actions/ViewContext.js";
|
|
8
|
+
import LayoutStartPanel from "./LayoutStartPanel.css";
|
|
9
|
+
const classBase = "vuuLayoutStartPanel";
|
|
10
|
+
const LayoutStartPanel_LayoutStartPanel = (htmlAttributes)=>{
|
|
11
|
+
const targetWindow = useWindow();
|
|
12
|
+
useComponentCssInjection({
|
|
13
|
+
testId: "vuu-layout-start-panel",
|
|
14
|
+
css: LayoutStartPanel,
|
|
15
|
+
window: targetWindow
|
|
16
|
+
});
|
|
17
|
+
const { dispatch, path } = useViewContext();
|
|
18
|
+
const [displayState, setDisplayState] = useState();
|
|
19
|
+
useMemo(()=>{
|
|
20
|
+
dispatch?.({
|
|
21
|
+
type: "query",
|
|
22
|
+
path,
|
|
23
|
+
query: "PARENT_CONTAINER"
|
|
24
|
+
}).then((response)=>{
|
|
25
|
+
response?.parentContainerId === VuuShellLocation.Workspace ? setDisplayState("initial") : setDisplayState("nested");
|
|
26
|
+
});
|
|
27
|
+
}, [
|
|
28
|
+
dispatch,
|
|
29
|
+
path
|
|
30
|
+
]);
|
|
31
|
+
if (void 0 === displayState) return null;
|
|
32
|
+
const showInitialState = "initial" === displayState;
|
|
33
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
34
|
+
...htmlAttributes,
|
|
35
|
+
className: classBase,
|
|
36
|
+
children: [
|
|
37
|
+
showInitialState ? /*#__PURE__*/ jsxs(Fragment, {
|
|
38
|
+
children: [
|
|
39
|
+
/*#__PURE__*/ jsx("div", {
|
|
40
|
+
className: `${classBase}-title`,
|
|
41
|
+
children: "Start by adding a table"
|
|
42
|
+
}),
|
|
43
|
+
/*#__PURE__*/ jsx("div", {
|
|
44
|
+
className: `${classBase}-text`,
|
|
45
|
+
children: "To add a table, drag any of the Vuu Tables to this area or click the button below"
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
}) : null,
|
|
49
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
50
|
+
className: `${classBase}-addButton`,
|
|
51
|
+
icon: "add",
|
|
52
|
+
variant: "cta"
|
|
53
|
+
})
|
|
54
|
+
]
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
export { LayoutStartPanel_LayoutStartPanel as LayoutStartPanel };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const css = `
|
|
2
|
+
.vuuPlaceholder {
|
|
3
|
+
--vuuView-justify: center;
|
|
4
|
+
flex: 1 1 0;
|
|
5
|
+
align-items: center;
|
|
6
|
+
display: flex;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.vuuPlaceholder-nested {
|
|
10
|
+
background-color: red;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.vuuPlaceholder-shim {
|
|
14
|
+
flex-grow: 0;
|
|
15
|
+
flex-shrink: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
`;
|
|
20
|
+
export default css;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
4
|
+
import { useWindow } from "@salt-ds/window";
|
|
5
|
+
import { registerComponent } from "@vuu-ui/vuu-utils";
|
|
6
|
+
import { LayoutStartPanel } from "./LayoutStartPanel.js";
|
|
7
|
+
import { View } from "../layout-view/View.js";
|
|
8
|
+
import Placeholder from "./Placeholder.css";
|
|
9
|
+
const classBase = "vuuPlaceholder";
|
|
10
|
+
const PlaceholderCore = ({ showStartMenu = true })=>/*#__PURE__*/ jsx(Fragment, {
|
|
11
|
+
children: showStartMenu ? /*#__PURE__*/ jsx(LayoutStartPanel, {}) : null
|
|
12
|
+
});
|
|
13
|
+
const Placeholder_Placeholder = ({ className: classNameProp, showStartMenu, ...viewProps })=>{
|
|
14
|
+
const targetWindow = useWindow();
|
|
15
|
+
useComponentCssInjection({
|
|
16
|
+
testId: "vuu-placeholder",
|
|
17
|
+
css: Placeholder,
|
|
18
|
+
window: targetWindow
|
|
19
|
+
});
|
|
20
|
+
const className = clsx(classBase, classNameProp);
|
|
21
|
+
return /*#__PURE__*/ jsx(View, {
|
|
22
|
+
...viewProps,
|
|
23
|
+
className: className,
|
|
24
|
+
"data-placeholder": true,
|
|
25
|
+
resizeable: true,
|
|
26
|
+
children: /*#__PURE__*/ jsx(PlaceholderCore, {
|
|
27
|
+
showStartMenu: showStartMenu
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
Placeholder_Placeholder.displayName = "Placeholder";
|
|
32
|
+
registerComponent("Placeholder", Placeholder_Placeholder, "component");
|
|
33
|
+
export { Placeholder_Placeholder as Placeholder };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Placeholder.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const LEFT_RIGHT = [
|
|
2
|
+
'left',
|
|
3
|
+
'right'
|
|
4
|
+
];
|
|
5
|
+
const TOP_BOTTOM = [
|
|
6
|
+
'top',
|
|
7
|
+
'bottom'
|
|
8
|
+
];
|
|
9
|
+
function measureMinimumNodeSize(node, dimension = 'width') {
|
|
10
|
+
const { [dimension]: size } = node.getBoundingClientRect();
|
|
11
|
+
const { padRight = false, padLeft = false } = node.dataset;
|
|
12
|
+
const style = getComputedStyle(node);
|
|
13
|
+
const [start, end] = 'width' === dimension ? LEFT_RIGHT : TOP_BOTTOM;
|
|
14
|
+
const marginStart = padLeft ? 0 : parseInt(style.getPropertyValue(`margin-${start}`), 10);
|
|
15
|
+
const marginEnd = padRight ? 0 : parseInt(style.getPropertyValue(`margin-${end}`), 10);
|
|
16
|
+
let minWidth = size;
|
|
17
|
+
const flexShrink = parseInt(style.getPropertyValue('flex-shrink'), 10);
|
|
18
|
+
if (flexShrink > 0) {
|
|
19
|
+
const flexBasis = parseInt(style.getPropertyValue('flex-basis'), 10);
|
|
20
|
+
if (!isNaN(flexBasis)) minWidth = flexBasis;
|
|
21
|
+
}
|
|
22
|
+
return marginStart + minWidth + marginEnd;
|
|
23
|
+
}
|
|
24
|
+
export { measureMinimumNodeSize };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
2
|
+
const WidthHeight = [
|
|
3
|
+
"height",
|
|
4
|
+
"width"
|
|
5
|
+
];
|
|
6
|
+
const HeightOnly = [
|
|
7
|
+
"height"
|
|
8
|
+
];
|
|
9
|
+
const WidthOnly = [
|
|
10
|
+
"width"
|
|
11
|
+
];
|
|
12
|
+
const observedMap = new WeakMap();
|
|
13
|
+
const getTargetSize = (element, contentRect, dimension)=>{
|
|
14
|
+
switch(dimension){
|
|
15
|
+
case "height":
|
|
16
|
+
return contentRect.height;
|
|
17
|
+
case "scrollHeight":
|
|
18
|
+
return element.scrollHeight;
|
|
19
|
+
case "scrollWidth":
|
|
20
|
+
return element.scrollWidth;
|
|
21
|
+
case "width":
|
|
22
|
+
return contentRect.width;
|
|
23
|
+
default:
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const resizeObserver = new ResizeObserver((entries)=>{
|
|
28
|
+
for (const entry of entries){
|
|
29
|
+
const { target, contentRect } = entry;
|
|
30
|
+
const observedTarget = observedMap.get(target);
|
|
31
|
+
if (observedTarget) {
|
|
32
|
+
const { onResize, measurements } = observedTarget;
|
|
33
|
+
let sizeChanged = false;
|
|
34
|
+
for (const [dimension, size] of Object.entries(measurements)){
|
|
35
|
+
const newSize = getTargetSize(target, contentRect, dimension);
|
|
36
|
+
if (newSize !== size) {
|
|
37
|
+
sizeChanged = true;
|
|
38
|
+
measurements[dimension] = newSize;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (sizeChanged) onResize && onResize(measurements);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
function useResizeObserver(ref, dimensions, onResize, reportInitialSize = false) {
|
|
46
|
+
const dimensionsRef = useRef(dimensions);
|
|
47
|
+
const measure = useCallback((target)=>{
|
|
48
|
+
const rect = target.getBoundingClientRect();
|
|
49
|
+
return dimensionsRef.current.reduce((map, dim)=>{
|
|
50
|
+
map[dim] = getTargetSize(target, rect, dim);
|
|
51
|
+
return map;
|
|
52
|
+
}, {});
|
|
53
|
+
}, []);
|
|
54
|
+
useLayoutEffect(()=>{
|
|
55
|
+
const target = ref.current;
|
|
56
|
+
let cleanedUp = false;
|
|
57
|
+
async function registerObserver() {
|
|
58
|
+
observedMap.set(target, {
|
|
59
|
+
measurements: {}
|
|
60
|
+
});
|
|
61
|
+
cleanedUp = false;
|
|
62
|
+
const { fonts } = document;
|
|
63
|
+
if (fonts) await fonts.ready;
|
|
64
|
+
if (!cleanedUp) {
|
|
65
|
+
const observedTarget = observedMap.get(target);
|
|
66
|
+
if (observedTarget) {
|
|
67
|
+
const measurements = measure(target);
|
|
68
|
+
observedTarget.measurements = measurements;
|
|
69
|
+
resizeObserver.observe(target);
|
|
70
|
+
if (reportInitialSize) onResize(measurements);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (target) {
|
|
75
|
+
if (observedMap.has(target)) throw Error("useResizeObserver attemping to observe same element twice");
|
|
76
|
+
registerObserver();
|
|
77
|
+
}
|
|
78
|
+
return ()=>{
|
|
79
|
+
if (target && observedMap.has(target)) {
|
|
80
|
+
resizeObserver.unobserve(target);
|
|
81
|
+
observedMap.delete(target);
|
|
82
|
+
cleanedUp = true;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}, [
|
|
86
|
+
ref,
|
|
87
|
+
measure,
|
|
88
|
+
reportInitialSize,
|
|
89
|
+
onResize
|
|
90
|
+
]);
|
|
91
|
+
useLayoutEffect(()=>{
|
|
92
|
+
const target = ref.current;
|
|
93
|
+
const record = observedMap.get(target);
|
|
94
|
+
if (record) {
|
|
95
|
+
if (dimensionsRef.current !== dimensions) {
|
|
96
|
+
dimensionsRef.current = dimensions;
|
|
97
|
+
const measurements = measure(target);
|
|
98
|
+
record.measurements = measurements;
|
|
99
|
+
}
|
|
100
|
+
record.onResize = onResize;
|
|
101
|
+
}
|
|
102
|
+
}, [
|
|
103
|
+
dimensions,
|
|
104
|
+
measure,
|
|
105
|
+
ref,
|
|
106
|
+
onResize
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
export { HeightOnly, WidthHeight, WidthOnly, resizeObserver, useResizeObserver };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const COLLAPSIBLE = 'data-collapsible';
|
|
2
|
+
const RESPONSIVE_ATTRIBUTE = {
|
|
3
|
+
[COLLAPSIBLE]: true,
|
|
4
|
+
'data-pad-start': true,
|
|
5
|
+
'data-pad-end': true
|
|
6
|
+
};
|
|
7
|
+
const isResponsiveAttribute = (propName)=>RESPONSIVE_ATTRIBUTE[propName] ?? false;
|
|
8
|
+
const isCollapsible = (propName)=>propName === COLLAPSIBLE;
|
|
9
|
+
const COLLAPSIBLE_VALUE = {
|
|
10
|
+
dynamic: 'dynamic',
|
|
11
|
+
instant: 'instant',
|
|
12
|
+
true: 'instant'
|
|
13
|
+
};
|
|
14
|
+
const collapsibleValue = (value)=>COLLAPSIBLE_VALUE[value] ?? 'none';
|
|
15
|
+
const extractResponsiveProps = (props)=>Object.keys(props).reduce((result, propName)=>{
|
|
16
|
+
const [toolbarProps, rest] = result;
|
|
17
|
+
if (isResponsiveAttribute(propName)) {
|
|
18
|
+
const value = isCollapsible(propName) ? collapsibleValue(props[propName]) : props[propName];
|
|
19
|
+
toolbarProps[propName] = value;
|
|
20
|
+
rest[propName] = void 0;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}, [
|
|
24
|
+
{},
|
|
25
|
+
{}
|
|
26
|
+
]);
|
|
27
|
+
export { extractResponsiveProps, isResponsiveAttribute };
|