datocms-react-ui 2.1.0-alpha.0 → 2.1.0-alpha.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/dist/cjs/HotKey/index.js +105 -0
- package/dist/cjs/HotKey/index.js.map +1 -0
- package/dist/cjs/HotKey/styles.module.css.json +1 -0
- package/dist/cjs/SplitView/SplitViewPane/index.js +14 -0
- package/dist/cjs/SplitView/SplitViewPane/index.js.map +1 -0
- package/dist/cjs/SplitView/SplitViewPane/styles.module.css.json +1 -0
- package/dist/cjs/SplitView/SplitViewSash/index.js +76 -0
- package/dist/cjs/SplitView/SplitViewSash/index.js.map +1 -0
- package/dist/cjs/SplitView/SplitViewSash/styles.module.css.json +1 -0
- package/dist/cjs/SplitView/index.js +206 -0
- package/dist/cjs/SplitView/index.js.map +1 -0
- package/dist/cjs/SplitView/styles.module.css.json +1 -0
- package/dist/cjs/SplitView/types.js +3 -0
- package/dist/cjs/SplitView/types.js.map +1 -0
- package/dist/cjs/Tooltip/Tooltip/index.js +116 -0
- package/dist/cjs/Tooltip/Tooltip/index.js.map +1 -0
- package/dist/cjs/Tooltip/TooltipContent/index.js +147 -0
- package/dist/cjs/Tooltip/TooltipContent/index.js.map +1 -0
- package/dist/cjs/Tooltip/TooltipContent/styles.module.css.json +1 -0
- package/dist/cjs/Tooltip/TooltipDelayGroup/index.js +140 -0
- package/dist/cjs/Tooltip/TooltipDelayGroup/index.js.map +1 -0
- package/dist/cjs/Tooltip/TooltipTrigger/index.js +102 -0
- package/dist/cjs/Tooltip/TooltipTrigger/index.js.map +1 -0
- package/dist/cjs/Tooltip/index.js +12 -0
- package/dist/cjs/Tooltip/index.js.map +1 -0
- package/dist/cjs/Tooltip/utils.js +165 -0
- package/dist/cjs/Tooltip/utils.js.map +1 -0
- package/dist/cjs/VerticalSplit/index.js +337 -0
- package/dist/cjs/VerticalSplit/index.js.map +1 -0
- package/dist/cjs/VerticalSplit/styles.module.css.json +1 -0
- package/dist/cjs/icons.js +19 -1
- package/dist/cjs/icons.js.map +1 -1
- package/dist/cjs/index.js +7 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/HotKey/index.d.ts +70 -0
- package/dist/esm/HotKey/index.js +75 -0
- package/dist/esm/HotKey/index.js.map +1 -0
- package/dist/esm/HotKey/styles.module.css.json +1 -0
- package/dist/esm/SplitView/SplitViewPane/index.d.ts +7 -0
- package/dist/esm/SplitView/SplitViewPane/index.js +7 -0
- package/dist/esm/SplitView/SplitViewPane/index.js.map +1 -0
- package/dist/esm/SplitView/SplitViewPane/styles.module.css.json +1 -0
- package/dist/esm/SplitView/SplitViewSash/index.d.ts +17 -0
- package/dist/esm/SplitView/SplitViewSash/index.js +46 -0
- package/dist/esm/SplitView/SplitViewSash/index.js.map +1 -0
- package/dist/esm/SplitView/SplitViewSash/styles.module.css.json +1 -0
- package/dist/esm/SplitView/index.d.ts +16 -0
- package/dist/esm/SplitView/index.js +176 -0
- package/dist/esm/SplitView/index.js.map +1 -0
- package/dist/esm/SplitView/styles.module.css.json +1 -0
- package/dist/esm/SplitView/types.d.ts +8 -0
- package/dist/esm/SplitView/types.js +2 -0
- package/dist/esm/SplitView/types.js.map +1 -0
- package/dist/esm/Tooltip/Tooltip/index.d.ts +74 -0
- package/dist/esm/Tooltip/Tooltip/index.js +89 -0
- package/dist/esm/Tooltip/Tooltip/index.js.map +1 -0
- package/dist/esm/Tooltip/TooltipContent/index.d.ts +68 -0
- package/dist/esm/Tooltip/TooltipContent/index.js +118 -0
- package/dist/esm/Tooltip/TooltipContent/index.js.map +1 -0
- package/dist/esm/Tooltip/TooltipContent/styles.module.css.json +1 -0
- package/dist/esm/Tooltip/TooltipDelayGroup/index.d.ts +118 -0
- package/dist/esm/Tooltip/TooltipDelayGroup/index.js +113 -0
- package/dist/esm/Tooltip/TooltipDelayGroup/index.js.map +1 -0
- package/dist/esm/Tooltip/TooltipTrigger/index.d.ts +45 -0
- package/dist/esm/Tooltip/TooltipTrigger/index.js +76 -0
- package/dist/esm/Tooltip/TooltipTrigger/index.js.map +1 -0
- package/dist/esm/Tooltip/index.d.ts +8 -0
- package/dist/esm/Tooltip/index.js +5 -0
- package/dist/esm/Tooltip/index.js.map +1 -0
- package/dist/esm/Tooltip/utils.d.ts +166 -0
- package/dist/esm/Tooltip/utils.js +135 -0
- package/dist/esm/Tooltip/utils.js.map +1 -0
- package/dist/esm/VerticalSplit/index.d.ts +238 -0
- package/dist/esm/VerticalSplit/index.js +307 -0
- package/dist/esm/VerticalSplit/index.js.map +1 -0
- package/dist/esm/VerticalSplit/styles.module.css.json +1 -0
- package/dist/esm/icons.d.ts +3 -0
- package/dist/esm/icons.js +15 -0
- package/dist/esm/icons.js.map +1 -1
- package/dist/esm/index.d.ts +7 -3
- package/dist/esm/index.js +7 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/types/HotKey/index.d.ts +70 -0
- package/dist/types/SplitView/SplitViewPane/index.d.ts +7 -0
- package/dist/types/SplitView/SplitViewSash/index.d.ts +17 -0
- package/dist/types/SplitView/index.d.ts +16 -0
- package/dist/types/SplitView/types.d.ts +8 -0
- package/dist/types/Tooltip/Tooltip/index.d.ts +74 -0
- package/dist/types/Tooltip/TooltipContent/index.d.ts +68 -0
- package/dist/types/Tooltip/TooltipDelayGroup/index.d.ts +118 -0
- package/dist/types/Tooltip/TooltipTrigger/index.d.ts +45 -0
- package/dist/types/Tooltip/index.d.ts +8 -0
- package/dist/types/Tooltip/utils.d.ts +166 -0
- package/dist/types/VerticalSplit/index.d.ts +238 -0
- package/dist/types/icons.d.ts +3 -0
- package/dist/types/index.d.ts +7 -3
- package/package.json +4 -3
- package/src/HotKey/index.tsx +95 -0
- package/src/HotKey/styles.module.css +22 -0
- package/src/HotKey/styles.module.css.json +1 -0
- package/src/SplitView/SplitViewPane/index.tsx +19 -0
- package/src/SplitView/SplitViewPane/styles.module.css +6 -0
- package/src/SplitView/SplitViewPane/styles.module.css.json +1 -0
- package/src/SplitView/SplitViewSash/index.tsx +99 -0
- package/src/SplitView/SplitViewSash/styles.module.css +68 -0
- package/src/SplitView/SplitViewSash/styles.module.css.json +1 -0
- package/src/SplitView/index.tsx +256 -0
- package/src/SplitView/styles.module.css +22 -0
- package/src/SplitView/styles.module.css.json +1 -0
- package/src/SplitView/types.ts +9 -0
- package/src/Tooltip/Tooltip/index.tsx +85 -0
- package/src/Tooltip/TooltipContent/index.tsx +145 -0
- package/src/Tooltip/TooltipContent/styles.module.css +10 -0
- package/src/Tooltip/TooltipContent/styles.module.css.json +1 -0
- package/src/Tooltip/TooltipDelayGroup/index.tsx +128 -0
- package/src/Tooltip/TooltipTrigger/index.tsx +71 -0
- package/src/Tooltip/index.ts +8 -0
- package/src/Tooltip/utils.ts +176 -0
- package/src/VerticalSplit/index.tsx +401 -0
- package/src/VerticalSplit/styles.module.css +103 -0
- package/src/VerticalSplit/styles.module.css.json +1 -0
- package/src/global.css +31 -25
- package/src/icons.tsx +60 -0
- package/src/index.ts +7 -3
- package/styles.css +1 -1
- package/types.json +6126 -3451
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useMergeRefs } from '@floating-ui/react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { useTooltipState } from '../utils';
|
|
4
|
+
|
|
5
|
+
export type TooltipTriggerProps = {
|
|
6
|
+
children: React.ReactElement;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* TooltipTrigger wraps the element that triggers the tooltip on hover/focus.
|
|
11
|
+
*
|
|
12
|
+
* The child must be a single React element that accepts ref and event handler props.
|
|
13
|
+
* Common triggers include buttons, icons, or other interactive elements.
|
|
14
|
+
*
|
|
15
|
+
* @example With Button component
|
|
16
|
+
*
|
|
17
|
+
* Wrap a button to show a tooltip when the user hovers over it:
|
|
18
|
+
*
|
|
19
|
+
* ```js
|
|
20
|
+
* <Canvas ctx={ctx}>
|
|
21
|
+
* <Tooltip>
|
|
22
|
+
* <TooltipTrigger>
|
|
23
|
+
* <Button>Hover for info</Button>
|
|
24
|
+
* </TooltipTrigger>
|
|
25
|
+
* <TooltipContent>
|
|
26
|
+
* Additional information appears here
|
|
27
|
+
* </TooltipContent>
|
|
28
|
+
* </Tooltip>
|
|
29
|
+
* </Canvas>;
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example With icon button
|
|
33
|
+
*
|
|
34
|
+
* Use tooltips with icon-only buttons to explain their purpose:
|
|
35
|
+
*
|
|
36
|
+
* ```js
|
|
37
|
+
* <Canvas ctx={ctx}>
|
|
38
|
+
* <Tooltip placement="bottom">
|
|
39
|
+
* <TooltipTrigger>
|
|
40
|
+
* <Button buttonSize="s" leftIcon={<InfoIcon />} />
|
|
41
|
+
* </TooltipTrigger>
|
|
42
|
+
* <TooltipContent>
|
|
43
|
+
* Click for more details
|
|
44
|
+
* </TooltipContent>
|
|
45
|
+
* </Tooltip>
|
|
46
|
+
* </Canvas>;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const TooltipTrigger = React.forwardRef<
|
|
50
|
+
HTMLElement,
|
|
51
|
+
TooltipTriggerProps
|
|
52
|
+
>(function TooltipTrigger({ children, ...props }, propRef) {
|
|
53
|
+
const state = useTooltipState();
|
|
54
|
+
|
|
55
|
+
const childrenRef = (children as any).ref;
|
|
56
|
+
const ref = useMergeRefs([state.refs.setReference, propRef, childrenRef]);
|
|
57
|
+
|
|
58
|
+
if (!React.isValidElement(children)) {
|
|
59
|
+
throw new Error('TooltipTrigger children must be a valid React element');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return React.cloneElement(
|
|
63
|
+
children,
|
|
64
|
+
state.getReferenceProps({
|
|
65
|
+
ref,
|
|
66
|
+
...props,
|
|
67
|
+
...children.props,
|
|
68
|
+
'data-state': state.open ? 'open' : 'closed',
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Tooltip } from './Tooltip';
|
|
2
|
+
export type { TooltipProps } from './Tooltip';
|
|
3
|
+
export { TooltipContent } from './TooltipContent';
|
|
4
|
+
export type { TooltipContentProps } from './TooltipContent';
|
|
5
|
+
export { TooltipDelayGroup } from './TooltipDelayGroup';
|
|
6
|
+
export type { TooltipDelayGroupProps } from './TooltipDelayGroup';
|
|
7
|
+
export { TooltipTrigger } from './TooltipTrigger';
|
|
8
|
+
export type { TooltipTriggerProps } from './TooltipTrigger';
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { Placement } from '@floating-ui/react';
|
|
2
|
+
import {
|
|
3
|
+
autoUpdate,
|
|
4
|
+
flip,
|
|
5
|
+
offset,
|
|
6
|
+
shift,
|
|
7
|
+
useDelayGroupContext,
|
|
8
|
+
useDismiss,
|
|
9
|
+
useFloating,
|
|
10
|
+
useFocus,
|
|
11
|
+
useHover,
|
|
12
|
+
useInteractions,
|
|
13
|
+
useRole,
|
|
14
|
+
} from '@floating-ui/react';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
|
|
17
|
+
// Create a single shared portal root for all tooltips
|
|
18
|
+
let sharedPortalRoot: HTMLDivElement | null = null;
|
|
19
|
+
let portalRefCount = 0;
|
|
20
|
+
|
|
21
|
+
export function getSharedPortalRoot(): HTMLDivElement {
|
|
22
|
+
if (!sharedPortalRoot) {
|
|
23
|
+
sharedPortalRoot = document.createElement('div');
|
|
24
|
+
sharedPortalRoot.style.position = 'relative';
|
|
25
|
+
sharedPortalRoot.style.zIndex = '100000';
|
|
26
|
+
sharedPortalRoot.style.height = '0px';
|
|
27
|
+
|
|
28
|
+
// Insert as the first child of body
|
|
29
|
+
if (document.body.firstChild) {
|
|
30
|
+
document.body.insertBefore(sharedPortalRoot, document.body.firstChild);
|
|
31
|
+
} else {
|
|
32
|
+
document.body.appendChild(sharedPortalRoot);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
portalRefCount++;
|
|
36
|
+
return sharedPortalRoot;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function releaseSharedPortalRoot(): void {
|
|
40
|
+
portalRefCount--;
|
|
41
|
+
if (portalRefCount === 0 && sharedPortalRoot) {
|
|
42
|
+
if (sharedPortalRoot.parentNode) {
|
|
43
|
+
sharedPortalRoot.parentNode.removeChild(sharedPortalRoot);
|
|
44
|
+
}
|
|
45
|
+
sharedPortalRoot = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TooltipOptions {
|
|
50
|
+
/** Whether the tooltip is initially open (uncontrolled mode) */
|
|
51
|
+
initialOpen?: boolean;
|
|
52
|
+
/** Placement of the tooltip relative to its trigger */
|
|
53
|
+
placement?: Placement;
|
|
54
|
+
/** Controlled open state */
|
|
55
|
+
open?: boolean;
|
|
56
|
+
/** Callback when open state changes (controlled mode) */
|
|
57
|
+
onOpenChange?: (open: boolean) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Hook that manages tooltip state and positioning logic.
|
|
62
|
+
*
|
|
63
|
+
* This hook provides the core tooltip behavior including hover/focus detection,
|
|
64
|
+
* positioning, and accessibility features. Use this when you need full control
|
|
65
|
+
* over the tooltip structure.
|
|
66
|
+
*
|
|
67
|
+
* @example Basic tooltip hook usage
|
|
68
|
+
*
|
|
69
|
+
* Build a custom tooltip implementation using the hook directly for maximum flexibility:
|
|
70
|
+
*
|
|
71
|
+
* ```js
|
|
72
|
+
* function MyComponent() {
|
|
73
|
+
* const tooltip = useTooltip({ placement: 'top' });
|
|
74
|
+
*
|
|
75
|
+
* return (
|
|
76
|
+
* <>
|
|
77
|
+
* <button
|
|
78
|
+
* ref={tooltip.refs.setReference}
|
|
79
|
+
* {...tooltip.getReferenceProps()}
|
|
80
|
+
* >
|
|
81
|
+
* Hover me
|
|
82
|
+
* </button>
|
|
83
|
+
*
|
|
84
|
+
* {tooltip.open && (
|
|
85
|
+
* <div
|
|
86
|
+
* ref={tooltip.refs.setFloating}
|
|
87
|
+
* style={tooltip.floatingStyles}
|
|
88
|
+
* {...tooltip.getFloatingProps()}
|
|
89
|
+
* >
|
|
90
|
+
* Tooltip content
|
|
91
|
+
* </div>
|
|
92
|
+
* )}
|
|
93
|
+
* </>
|
|
94
|
+
* );
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example Controlled tooltip
|
|
99
|
+
*
|
|
100
|
+
* Control the tooltip's open state programmatically for click-to-toggle behavior:
|
|
101
|
+
*
|
|
102
|
+
* ```js
|
|
103
|
+
* function ControlledTooltip() {
|
|
104
|
+
* const [open, setOpen] = React.useState(false);
|
|
105
|
+
* const tooltip = useTooltip({ open, onOpenChange: setOpen });
|
|
106
|
+
*
|
|
107
|
+
* return (
|
|
108
|
+
* <Canvas ctx={ctx}>
|
|
109
|
+
* <button onClick={() => setOpen(!open)}>
|
|
110
|
+
* Toggle tooltip
|
|
111
|
+
* </button>
|
|
112
|
+
* </Canvas>
|
|
113
|
+
* );
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function useTooltip({
|
|
118
|
+
initialOpen = false,
|
|
119
|
+
placement = 'top',
|
|
120
|
+
open: controlledOpen,
|
|
121
|
+
onOpenChange: setControlledOpen,
|
|
122
|
+
}: TooltipOptions = {}) {
|
|
123
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
|
|
124
|
+
|
|
125
|
+
const open = controlledOpen ?? uncontrolledOpen;
|
|
126
|
+
const setOpen = setControlledOpen ?? setUncontrolledOpen;
|
|
127
|
+
|
|
128
|
+
const { delay } = useDelayGroupContext();
|
|
129
|
+
|
|
130
|
+
const data = useFloating({
|
|
131
|
+
placement,
|
|
132
|
+
open,
|
|
133
|
+
onOpenChange: setOpen,
|
|
134
|
+
whileElementsMounted: autoUpdate,
|
|
135
|
+
middleware: [offset(5), flip(), shift()],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const context = data.context;
|
|
139
|
+
|
|
140
|
+
const hover = useHover(context, {
|
|
141
|
+
move: false,
|
|
142
|
+
enabled: controlledOpen == null,
|
|
143
|
+
delay,
|
|
144
|
+
});
|
|
145
|
+
const focus = useFocus(context, {
|
|
146
|
+
enabled: controlledOpen == null,
|
|
147
|
+
});
|
|
148
|
+
const dismiss = useDismiss(context);
|
|
149
|
+
const role = useRole(context, { role: 'tooltip' });
|
|
150
|
+
|
|
151
|
+
const interactions = useInteractions([hover, focus, dismiss, role]);
|
|
152
|
+
|
|
153
|
+
return React.useMemo(
|
|
154
|
+
() => ({
|
|
155
|
+
open,
|
|
156
|
+
setOpen,
|
|
157
|
+
...interactions,
|
|
158
|
+
...data,
|
|
159
|
+
}),
|
|
160
|
+
[open, setOpen, interactions, data],
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
type ContextType = ReturnType<typeof useTooltip> | null;
|
|
165
|
+
|
|
166
|
+
export const TooltipContext = React.createContext<ContextType>(null);
|
|
167
|
+
|
|
168
|
+
export const useTooltipState = () => {
|
|
169
|
+
const context = React.useContext(TooltipContext);
|
|
170
|
+
|
|
171
|
+
if (context == null) {
|
|
172
|
+
throw new Error('Tooltip components must be wrapped in <Tooltip />');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return context;
|
|
176
|
+
};
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { SplitView } from '../SplitView';
|
|
4
|
+
import { SplitViewPane } from '../SplitView/SplitViewPane';
|
|
5
|
+
import sashS from '../SplitView/SplitViewSash/styles.module.css.json';
|
|
6
|
+
import { ChevronsLeftIcon, ChevronsRightIcon, SidebarFlipIcon } from '../icons';
|
|
7
|
+
import s from './styles.module.css.json';
|
|
8
|
+
|
|
9
|
+
const initialSidebarWidth = 220;
|
|
10
|
+
|
|
11
|
+
export type VerticalSplitProps = {
|
|
12
|
+
mode?: 'overlay' | 'split';
|
|
13
|
+
minSize?: number | string;
|
|
14
|
+
maxSize?: number | string;
|
|
15
|
+
size?: number | string;
|
|
16
|
+
primaryPane: 'left' | 'right';
|
|
17
|
+
isSecondaryCollapsed?: boolean;
|
|
18
|
+
allowResize?: boolean;
|
|
19
|
+
children: [React.ReactNode, React.ReactNode];
|
|
20
|
+
onDragFinished?: (newSize: number) => void;
|
|
21
|
+
onSecondaryToggle?: (secondaryExpanded: boolean) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type Size = string | number;
|
|
25
|
+
|
|
26
|
+
function calculateSizes({
|
|
27
|
+
size,
|
|
28
|
+
primaryPane,
|
|
29
|
+
isSecondaryCollapsed,
|
|
30
|
+
}: {
|
|
31
|
+
size: number | string;
|
|
32
|
+
primaryPane: 'left' | 'right';
|
|
33
|
+
isSecondaryCollapsed?: boolean;
|
|
34
|
+
}): Size[] {
|
|
35
|
+
const realSize = isSecondaryCollapsed ? 20 : size;
|
|
36
|
+
return primaryPane === 'right' ? [realSize] : ['auto', realSize];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @example Resizable, left primary panel
|
|
41
|
+
*
|
|
42
|
+
* ```js
|
|
43
|
+
* <Canvas ctx={ctx}>
|
|
44
|
+
* <div style={{ height: 500, position: 'relative' }}>
|
|
45
|
+
* <VerticalSplit primaryPane="left" size="25%" minSize={220}>
|
|
46
|
+
* <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
47
|
+
* <Toolbar>
|
|
48
|
+
* <ToolbarStack stackSize="l">
|
|
49
|
+
* <ToolbarTitle>Primary</ToolbarTitle>
|
|
50
|
+
* </ToolbarStack>
|
|
51
|
+
* </Toolbar>
|
|
52
|
+
* <div
|
|
53
|
+
* style={{
|
|
54
|
+
* flex: '1',
|
|
55
|
+
* display: 'flex',
|
|
56
|
+
* justifyContent: 'center',
|
|
57
|
+
* alignItems: 'center',
|
|
58
|
+
* height: '150px',
|
|
59
|
+
* }}
|
|
60
|
+
* >
|
|
61
|
+
* Main content
|
|
62
|
+
* </div>
|
|
63
|
+
* </div>
|
|
64
|
+
* <div style={{ display: 'flex', flexDirection: 'column', height: '100%', borderLeft: '1px solid var(--border-color)' }}>
|
|
65
|
+
* <Toolbar>
|
|
66
|
+
* <ToolbarStack stackSize="l">
|
|
67
|
+
* <ToolbarTitle>Secondary</ToolbarTitle>
|
|
68
|
+
* </ToolbarStack>
|
|
69
|
+
* </Toolbar>
|
|
70
|
+
* <div
|
|
71
|
+
* style={{
|
|
72
|
+
* flex: '1',
|
|
73
|
+
* display: 'flex',
|
|
74
|
+
* justifyContent: 'center',
|
|
75
|
+
* alignItems: 'center',
|
|
76
|
+
* height: '150px',
|
|
77
|
+
* }}
|
|
78
|
+
* >
|
|
79
|
+
* Sidebar
|
|
80
|
+
* </div>
|
|
81
|
+
* </div>
|
|
82
|
+
* </VerticalSplit>
|
|
83
|
+
* </div>
|
|
84
|
+
* </Canvas>;
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example Resizable, right primary panel
|
|
88
|
+
*
|
|
89
|
+
* ```js
|
|
90
|
+
* <Canvas ctx={ctx}>
|
|
91
|
+
* <div style={{ height: 500, position: 'relative' }}>
|
|
92
|
+
* <VerticalSplit primaryPane="right" size="25%" minSize={220}>
|
|
93
|
+
* <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
94
|
+
* <Toolbar>
|
|
95
|
+
* <ToolbarStack stackSize="l">
|
|
96
|
+
* <ToolbarTitle>Secondary</ToolbarTitle>
|
|
97
|
+
* </ToolbarStack>
|
|
98
|
+
* </Toolbar>
|
|
99
|
+
* <div
|
|
100
|
+
* style={{
|
|
101
|
+
* flex: '1',
|
|
102
|
+
* display: 'flex',
|
|
103
|
+
* justifyContent: 'center',
|
|
104
|
+
* alignItems: 'center',
|
|
105
|
+
* height: '150px',
|
|
106
|
+
* }}
|
|
107
|
+
* >
|
|
108
|
+
* Sidebar
|
|
109
|
+
* </div>
|
|
110
|
+
* </div>
|
|
111
|
+
* <div style={{ display: 'flex', flexDirection: 'column', height: '100%', borderLeft: '1px solid var(--border-color)' }}>
|
|
112
|
+
* <Toolbar>
|
|
113
|
+
* <ToolbarStack stackSize="l">
|
|
114
|
+
* <ToolbarTitle>Primary</ToolbarTitle>
|
|
115
|
+
* </ToolbarStack>
|
|
116
|
+
* </Toolbar>
|
|
117
|
+
* <div
|
|
118
|
+
* style={{
|
|
119
|
+
* flex: '1',
|
|
120
|
+
* display: 'flex',
|
|
121
|
+
* justifyContent: 'center',
|
|
122
|
+
* alignItems: 'center',
|
|
123
|
+
* height: '150px',
|
|
124
|
+
* }}
|
|
125
|
+
* >
|
|
126
|
+
* Main content
|
|
127
|
+
* </div>
|
|
128
|
+
* </div>
|
|
129
|
+
* </VerticalSplit>
|
|
130
|
+
* </div>
|
|
131
|
+
* </Canvas>;
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @example Collapsible
|
|
135
|
+
*
|
|
136
|
+
* ```js
|
|
137
|
+
* <Canvas ctx={ctx}>
|
|
138
|
+
* <div style={{ height: 500, position: 'relative' }}>
|
|
139
|
+
* <StateManager initial={true}>
|
|
140
|
+
* {(isCollapsed, setCollapsed) => (
|
|
141
|
+
* <VerticalSplit
|
|
142
|
+
* primaryPane="left"
|
|
143
|
+
* size="25%"
|
|
144
|
+
* minSize={220}
|
|
145
|
+
* isSecondaryCollapsed={isCollapsed}
|
|
146
|
+
* onSecondaryToggle={setCollapsed}
|
|
147
|
+
* >
|
|
148
|
+
* <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
149
|
+
* <Toolbar>
|
|
150
|
+
* <ToolbarStack stackSize="l">
|
|
151
|
+
* <ToolbarTitle>Primary</ToolbarTitle>
|
|
152
|
+
* </ToolbarStack>
|
|
153
|
+
* </Toolbar>
|
|
154
|
+
* <div
|
|
155
|
+
* style={{
|
|
156
|
+
* flex: '1',
|
|
157
|
+
* display: 'flex',
|
|
158
|
+
* justifyContent: 'center',
|
|
159
|
+
* alignItems: 'center',
|
|
160
|
+
* height: '150px',
|
|
161
|
+
* }}
|
|
162
|
+
* >
|
|
163
|
+
* Main content
|
|
164
|
+
* </div>
|
|
165
|
+
* </div>
|
|
166
|
+
* <div
|
|
167
|
+
* style={{
|
|
168
|
+
* display: 'flex',
|
|
169
|
+
* flexDirection: 'column',
|
|
170
|
+
* height: '100%',
|
|
171
|
+
* borderLeft: '1px solid var(--border-color)',
|
|
172
|
+
* }}
|
|
173
|
+
* >
|
|
174
|
+
* <Toolbar>
|
|
175
|
+
* <ToolbarStack stackSize="l">
|
|
176
|
+
* <ToolbarTitle>Secondary</ToolbarTitle>
|
|
177
|
+
* </ToolbarStack>
|
|
178
|
+
* </Toolbar>
|
|
179
|
+
* <div
|
|
180
|
+
* style={{
|
|
181
|
+
* flex: '1',
|
|
182
|
+
* display: 'flex',
|
|
183
|
+
* justifyContent: 'center',
|
|
184
|
+
* alignItems: 'center',
|
|
185
|
+
* height: '150px',
|
|
186
|
+
* }}
|
|
187
|
+
* >
|
|
188
|
+
* Sidebar
|
|
189
|
+
* </div>
|
|
190
|
+
* </div>
|
|
191
|
+
* </VerticalSplit>
|
|
192
|
+
* )}
|
|
193
|
+
* </StateManager>
|
|
194
|
+
* </div>
|
|
195
|
+
* </Canvas>;
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* @example Overlay mode
|
|
199
|
+
*
|
|
200
|
+
* ```js
|
|
201
|
+
* <Canvas ctx={ctx}>
|
|
202
|
+
* <div style={{ height: 500, position: 'relative' }}>
|
|
203
|
+
* <StateManager initial={true}>
|
|
204
|
+
* {(isCollapsed, setCollapsed) => (
|
|
205
|
+
* <VerticalSplit
|
|
206
|
+
* mode="overlay"
|
|
207
|
+
* primaryPane="left"
|
|
208
|
+
* size="25%"
|
|
209
|
+
* minSize={220}
|
|
210
|
+
* isSecondaryCollapsed={isCollapsed}
|
|
211
|
+
* onSecondaryToggle={setCollapsed}
|
|
212
|
+
* >
|
|
213
|
+
* <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
214
|
+
* <Toolbar>
|
|
215
|
+
* <ToolbarStack stackSize="l">
|
|
216
|
+
* <ToolbarTitle>Primary</ToolbarTitle>
|
|
217
|
+
* </ToolbarStack>
|
|
218
|
+
* </Toolbar>
|
|
219
|
+
* <div
|
|
220
|
+
* style={{
|
|
221
|
+
* flex: '1',
|
|
222
|
+
* display: 'flex',
|
|
223
|
+
* justifyContent: 'center',
|
|
224
|
+
* alignItems: 'center',
|
|
225
|
+
* height: '150px',
|
|
226
|
+
* }}
|
|
227
|
+
* >
|
|
228
|
+
* Main content
|
|
229
|
+
* </div>
|
|
230
|
+
* </div>
|
|
231
|
+
* <div
|
|
232
|
+
* style={{
|
|
233
|
+
* display: 'flex',
|
|
234
|
+
* flexDirection: 'column',
|
|
235
|
+
* height: '100%',
|
|
236
|
+
* borderLeft: '1px solid var(--border-color)',
|
|
237
|
+
* }}
|
|
238
|
+
* >
|
|
239
|
+
* <Toolbar>
|
|
240
|
+
* <ToolbarStack stackSize="l">
|
|
241
|
+
* <ToolbarTitle>Secondary</ToolbarTitle>
|
|
242
|
+
* </ToolbarStack>
|
|
243
|
+
* </Toolbar>
|
|
244
|
+
* <div
|
|
245
|
+
* style={{
|
|
246
|
+
* flex: '1',
|
|
247
|
+
* display: 'flex',
|
|
248
|
+
* justifyContent: 'center',
|
|
249
|
+
* alignItems: 'center',
|
|
250
|
+
* height: '150px',
|
|
251
|
+
* }}
|
|
252
|
+
* >
|
|
253
|
+
* Sidebar
|
|
254
|
+
* </div>
|
|
255
|
+
* </div>
|
|
256
|
+
* </VerticalSplit>
|
|
257
|
+
* )}
|
|
258
|
+
* </StateManager>
|
|
259
|
+
* </div>
|
|
260
|
+
* </Canvas>;
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
export function VerticalSplit({
|
|
264
|
+
mode = 'split',
|
|
265
|
+
minSize = 200,
|
|
266
|
+
maxSize,
|
|
267
|
+
size = initialSidebarWidth,
|
|
268
|
+
primaryPane,
|
|
269
|
+
children,
|
|
270
|
+
allowResize = true,
|
|
271
|
+
onDragFinished,
|
|
272
|
+
onSecondaryToggle,
|
|
273
|
+
isSecondaryCollapsed,
|
|
274
|
+
}: VerticalSplitProps) {
|
|
275
|
+
const [sizes, setSizes] = useState<Size[]>(
|
|
276
|
+
calculateSizes({ size, primaryPane, isSecondaryCollapsed }),
|
|
277
|
+
);
|
|
278
|
+
const currentSizes = useRef<Size[]>(sizes);
|
|
279
|
+
|
|
280
|
+
const SashActionIcon = onSecondaryToggle
|
|
281
|
+
? primaryPane === 'left'
|
|
282
|
+
? isSecondaryCollapsed
|
|
283
|
+
? SidebarFlipIcon
|
|
284
|
+
: ChevronsRightIcon
|
|
285
|
+
: isSecondaryCollapsed
|
|
286
|
+
? ChevronsRightIcon
|
|
287
|
+
: ChevronsLeftIcon
|
|
288
|
+
: undefined;
|
|
289
|
+
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
setSizes(calculateSizes({ size, primaryPane, isSecondaryCollapsed }));
|
|
292
|
+
}, [size, primaryPane, isSecondaryCollapsed]);
|
|
293
|
+
|
|
294
|
+
function handleChange(newSizes: Size[]) {
|
|
295
|
+
setSizes(newSizes);
|
|
296
|
+
currentSizes.current = newSizes;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function handleDragEnd() {
|
|
300
|
+
onDragFinished?.(currentSizes.current[0] as number);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function handleSecondaryClick() {
|
|
304
|
+
onSecondaryToggle?.(!isSecondaryCollapsed);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function renderPane(pane: 'left' | 'right') {
|
|
308
|
+
const isSecondaryPane = primaryPane !== pane;
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<SplitViewPane
|
|
312
|
+
{...(isSecondaryPane && !isSecondaryCollapsed
|
|
313
|
+
? { minSize, maxSize }
|
|
314
|
+
: {})}
|
|
315
|
+
>
|
|
316
|
+
{isSecondaryPane && isSecondaryCollapsed ? (
|
|
317
|
+
<div
|
|
318
|
+
className={classNames(
|
|
319
|
+
s.VerticalSplitPane__expand,
|
|
320
|
+
s[`VerticalSplitPane__expand--${pane}`],
|
|
321
|
+
)}
|
|
322
|
+
onClick={handleSecondaryClick}
|
|
323
|
+
/>
|
|
324
|
+
) : (
|
|
325
|
+
children[pane === 'left' ? 0 : 1]
|
|
326
|
+
)}
|
|
327
|
+
</SplitViewPane>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (mode === 'overlay' && !isSecondaryCollapsed && SashActionIcon) {
|
|
332
|
+
const primaryPaneChild = children[primaryPane === 'left' ? 0 : 1];
|
|
333
|
+
const secondaryPaneChild = children[primaryPane === 'left' ? 1 : 0];
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<>
|
|
337
|
+
<div
|
|
338
|
+
className={s.VerticalSplitPaneOverlay}
|
|
339
|
+
onClick={handleSecondaryClick}
|
|
340
|
+
>
|
|
341
|
+
<div
|
|
342
|
+
className={classNames(
|
|
343
|
+
s.VerticalSplitPaneOverlay__secondary,
|
|
344
|
+
s[
|
|
345
|
+
`VerticalSplitPaneOverlay__secondary--${
|
|
346
|
+
primaryPane === 'left' ? 'right' : 'left'
|
|
347
|
+
}`
|
|
348
|
+
],
|
|
349
|
+
)}
|
|
350
|
+
style={{
|
|
351
|
+
width: size,
|
|
352
|
+
maxWidth: maxSize,
|
|
353
|
+
minWidth: minSize,
|
|
354
|
+
}}
|
|
355
|
+
onClick={(e) => {
|
|
356
|
+
e.stopPropagation();
|
|
357
|
+
}}
|
|
358
|
+
>
|
|
359
|
+
{secondaryPaneChild}
|
|
360
|
+
<div className={s.VerticalSplitPaneOverlay__sash}>
|
|
361
|
+
<div
|
|
362
|
+
className={sashS.SplitViewSash__content}
|
|
363
|
+
onClick={handleSecondaryClick}
|
|
364
|
+
>
|
|
365
|
+
<div className={sashS.SplitViewSash__content__button}>
|
|
366
|
+
<SashActionIcon />
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
<div
|
|
373
|
+
className={classNames(
|
|
374
|
+
s.VerticalSplitPaneOverlay__primary,
|
|
375
|
+
s[`VerticalSplitPaneOverlay__primary--${primaryPane}`],
|
|
376
|
+
)}
|
|
377
|
+
>
|
|
378
|
+
{primaryPaneChild}
|
|
379
|
+
</div>
|
|
380
|
+
</>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<SplitView
|
|
386
|
+
allowResize={!isSecondaryCollapsed && allowResize}
|
|
387
|
+
sizes={sizes}
|
|
388
|
+
onChange={handleChange}
|
|
389
|
+
onDragEnd={handleDragEnd}
|
|
390
|
+
sashAction={
|
|
391
|
+
SashActionIcon && {
|
|
392
|
+
icon: <SashActionIcon />,
|
|
393
|
+
onClick: handleSecondaryClick,
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
>
|
|
397
|
+
{renderPane('left')}
|
|
398
|
+
{renderPane('right')}
|
|
399
|
+
</SplitView>
|
|
400
|
+
);
|
|
401
|
+
}
|