@datarobot/design-system 28.9.2 → 28.10.0

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.
Files changed (61) hide show
  1. package/cjs/floating-panel/constants.d.ts +21 -0
  2. package/cjs/floating-panel/constants.js +94 -0
  3. package/cjs/floating-panel/draggable-area.d.ts +12 -0
  4. package/cjs/floating-panel/draggable-area.js +72 -0
  5. package/cjs/floating-panel/floating-panel-dock-button.d.ts +5 -0
  6. package/cjs/floating-panel/floating-panel-dock-button.js +37 -0
  7. package/cjs/floating-panel/floating-panel-drag-handle.d.ts +5 -0
  8. package/cjs/floating-panel/floating-panel-drag-handle.js +51 -0
  9. package/cjs/floating-panel/floating-panel-header.d.ts +11 -0
  10. package/cjs/floating-panel/floating-panel-header.js +46 -0
  11. package/cjs/floating-panel/floating-panel.d.ts +66 -0
  12. package/cjs/floating-panel/floating-panel.js +140 -0
  13. package/cjs/floating-panel/index.d.ts +4 -0
  14. package/cjs/floating-panel/index.js +19 -0
  15. package/cjs/floating-panel/types.d.ts +21 -0
  16. package/cjs/floating-panel/types.js +5 -0
  17. package/cjs/floating-panel/use-floating-panel-root.d.ts +6 -0
  18. package/cjs/floating-panel/use-floating-panel-root.js +31 -0
  19. package/cjs/floating-panel/use-floating-panel-state.d.ts +27 -0
  20. package/cjs/floating-panel/use-floating-panel-state.js +316 -0
  21. package/cjs/floating-panel/use-floating-panel.d.ts +4 -0
  22. package/cjs/floating-panel/use-floating-panel.js +14 -0
  23. package/cjs/index.d.ts +1 -0
  24. package/cjs/index.js +11 -0
  25. package/cjs/table-react/hooks/useColumns.js +13 -5
  26. package/esm/floating-panel/constants.d.ts +21 -0
  27. package/esm/floating-panel/constants.js +86 -0
  28. package/esm/floating-panel/draggable-area.d.ts +12 -0
  29. package/esm/floating-panel/draggable-area.js +65 -0
  30. package/esm/floating-panel/floating-panel-dock-button.d.ts +5 -0
  31. package/esm/floating-panel/floating-panel-dock-button.js +30 -0
  32. package/esm/floating-panel/floating-panel-drag-handle.d.ts +5 -0
  33. package/esm/floating-panel/floating-panel-drag-handle.js +44 -0
  34. package/esm/floating-panel/floating-panel-header.d.ts +11 -0
  35. package/esm/floating-panel/floating-panel-header.js +39 -0
  36. package/esm/floating-panel/floating-panel.d.ts +66 -0
  37. package/esm/floating-panel/floating-panel.js +132 -0
  38. package/esm/floating-panel/index.d.ts +4 -0
  39. package/esm/floating-panel/index.js +2 -0
  40. package/esm/floating-panel/types.d.ts +21 -0
  41. package/esm/floating-panel/types.js +1 -0
  42. package/esm/floating-panel/use-floating-panel-root.d.ts +6 -0
  43. package/esm/floating-panel/use-floating-panel-root.js +24 -0
  44. package/esm/floating-panel/use-floating-panel-state.d.ts +27 -0
  45. package/esm/floating-panel/use-floating-panel-state.js +310 -0
  46. package/esm/floating-panel/use-floating-panel.d.ts +4 -0
  47. package/esm/floating-panel/use-floating-panel.js +9 -0
  48. package/esm/index.d.ts +1 -0
  49. package/esm/index.js +1 -0
  50. package/esm/table-react/hooks/useColumns.js +13 -5
  51. package/floating-panel/package.json +7 -0
  52. package/js/bundle/bundle.js +1351 -332
  53. package/js/bundle/bundle.min.js +1 -1
  54. package/js/bundle/index.d.ts +94 -1
  55. package/package.json +1 -1
  56. package/styles/index.css +150 -0
  57. package/styles/index.min.css +1 -1
  58. package/styles/themes/alpine-light.css +12 -0
  59. package/styles/themes/alpine-light.min.css +1 -1
  60. package/styles/themes/midnight-gray.css +12 -0
  61. package/styles/themes/midnight-gray.min.css +1 -1
@@ -0,0 +1,310 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useFocusTrap } from '../hooks';
3
+ import { useFloatingPanelRoot } from './use-floating-panel-root';
4
+ import { MOVE_KEYS, MOVE_KEYS_VALUES, HIGHLIGHTED_CLASSNAME, resizeHandlesMap } from './constants';
5
+ const step = 80;
6
+ const dockAreaWidth = 26;
7
+ const delta = {
8
+ [MOVE_KEYS.UP]: {
9
+ dx: 0,
10
+ dy: -step
11
+ },
12
+ [MOVE_KEYS.RIGHT]: {
13
+ dx: step,
14
+ dy: 0
15
+ },
16
+ [MOVE_KEYS.DOWN]: {
17
+ dx: 0,
18
+ dy: step
19
+ },
20
+ [MOVE_KEYS.LEFT]: {
21
+ dx: -step,
22
+ dy: 0
23
+ }
24
+ };
25
+ export function useFloatingPanelState({
26
+ minWidth,
27
+ minHeight,
28
+ maxWidth,
29
+ maxHeight,
30
+ isDocked: isDockedExternal,
31
+ setIsDocked: setIsDockedExternal,
32
+ initialState,
33
+ containerElement,
34
+ pageContentElement
35
+ }) {
36
+ const [isDocked, setIsDocked] = useState(isDockedExternal);
37
+ const [isMoving, setIsMoving] = useState(false);
38
+ const animationRef = useRef(null);
39
+ const headerRef = useRef(null);
40
+ const headerHeightRef = useRef(36);
41
+ const panelRef = useRef(null);
42
+ const dragStartPositionsRef = useRef();
43
+ const isDockHighlightedRef = useRef(false);
44
+ const containerRef = useRef(containerElement);
45
+ const lastPositionRef = useRef({
46
+ top: 0,
47
+ left: 0,
48
+ width: 0,
49
+ height: 0
50
+ });
51
+ const [state, setState] = useState(initialState ? {
52
+ top: initialState.top || lastPositionRef.current.top,
53
+ left: initialState.left || lastPositionRef.current.left,
54
+ width: between(initialState.width || lastPositionRef.current.width, minWidth, maxWidth),
55
+ height: between(initialState.height || lastPositionRef.current.height, minHeight, maxHeight)
56
+ } : {
57
+ top: 0,
58
+ left: 0,
59
+ width: minWidth,
60
+ height: minHeight
61
+ });
62
+ lastPositionRef.current = state;
63
+ containerRef.current = containerElement;
64
+ const style = useMemo(() => isDocked ? {} : {
65
+ transform: `translate3d(${state.left}px, ${state.top}px, 0px)`,
66
+ width: `${state.width}px`,
67
+ height: `${state.height}px`
68
+ }, [state, isDocked]);
69
+ const onDrag = useCallback(event => {
70
+ if (animationRef.current) {
71
+ return;
72
+ }
73
+ animationRef.current = requestAnimationFrame(() => {
74
+ const start = dragStartPositionsRef.current;
75
+ const maxLeft = window.innerWidth - start.width * 0.25;
76
+ const maxTop = window.innerHeight - headerHeightRef.current;
77
+ const nextLeft = event.clientX - (start.mouseX - start.left);
78
+ const nextTop = event.clientY - (start.mouseY - start.top);
79
+ setState(state => {
80
+ const nextState = {
81
+ ...state,
82
+ top: between(nextTop, 0, maxTop),
83
+ left: between(nextLeft, 0, maxLeft)
84
+ };
85
+ const nextIsDockHighlighted = isInDockArea(nextState);
86
+ // DOM manipulation, not state
87
+ if (nextIsDockHighlighted !== isDockHighlightedRef.current) {
88
+ toggleDockHighlight(nextIsDockHighlighted, containerRef.current);
89
+ isDockHighlightedRef.current = nextIsDockHighlighted;
90
+ }
91
+ return nextState;
92
+ });
93
+ animationRef.current = null;
94
+ });
95
+ }, []);
96
+ const onMovementStart = useCallback(event => {
97
+ const element = panelRef.current;
98
+ const header = headerRef.current;
99
+ if (!element || !header) {
100
+ return;
101
+ }
102
+ headerHeightRef.current = header.getBoundingClientRect().height;
103
+ const mouseX = event.pageX;
104
+ const mouseY = event.pageY;
105
+ dragStartPositionsRef.current = {
106
+ ...getPanelStateFromElement(element),
107
+ mouseX,
108
+ mouseY
109
+ };
110
+ lastPositionRef.current = dragStartPositionsRef.current;
111
+ setIsMoving(true);
112
+ }, []);
113
+ const onDragEnd = useCallback(() => {
114
+ if (isDockHighlightedRef.current) {
115
+ const position = dragStartPositionsRef.current;
116
+ setIsDocked(true);
117
+ setIsDockedExternal(true);
118
+ setState(state => {
119
+ const nextState = {
120
+ ...state,
121
+ left: window.innerWidth - position.width - dockAreaWidth * 2
122
+ };
123
+ lastPositionRef.current = nextState;
124
+ return nextState;
125
+ });
126
+ isDockHighlightedRef.current = false;
127
+ toggleDockHighlight(false, containerRef.current);
128
+ }
129
+ setIsMoving(false);
130
+ }, []);
131
+ const onResize = useCallback(e => {
132
+ if (animationRef.current) {
133
+ return;
134
+ }
135
+ animationRef.current = requestAnimationFrame(() => {
136
+ const start = dragStartPositionsRef.current;
137
+ const statePatch = {};
138
+ const resize = resizeHandlesMap.get(e.areaId);
139
+ if (resize?.right) {
140
+ const width = between(start.width + (e.pageX - start.mouseX), minWidth, maxWidth);
141
+ if (width !== start.width) {
142
+ statePatch.width = width;
143
+ }
144
+ }
145
+ if (resize?.bottom) {
146
+ const height = between(start.height + (e.pageY - start.mouseY), minHeight, maxHeight);
147
+ if (height !== start.height) {
148
+ statePatch.height = height;
149
+ }
150
+ }
151
+ if (resize?.top) {
152
+ const height = start.height - (e.pageY - start.mouseY);
153
+ const nextTop = start.top + (e.pageY - start.mouseY);
154
+ if (height > minHeight && height <= maxHeight && nextTop > 0) {
155
+ statePatch.height = height;
156
+ statePatch.top = nextTop;
157
+ }
158
+ }
159
+ if (resize?.left) {
160
+ const width = start.width - (e.pageX - start.mouseX);
161
+ if (width > minWidth && width <= maxWidth) {
162
+ statePatch.width = width;
163
+ statePatch.left = start.left + (e.pageX - start.mouseX);
164
+ }
165
+ }
166
+ if (Object.keys(statePatch).length) {
167
+ setState(state => ({
168
+ ...state,
169
+ ...statePatch
170
+ }));
171
+ }
172
+ animationRef.current = null;
173
+ });
174
+ }, [minWidth, minHeight, maxWidth, maxHeight]);
175
+ const onResizeEnd = useCallback(() => {
176
+ setIsMoving(false);
177
+ }, []);
178
+ const setIsDockedHandler = useCallback(docked => {
179
+ const element = panelRef.current;
180
+ if (!element || docked === isDocked) {
181
+ return;
182
+ }
183
+ if (docked) {
184
+ setIsDocked(true);
185
+ } else {
186
+ setIsDocked(false);
187
+ }
188
+ if (isDockedExternal !== docked) {
189
+ setIsDockedExternal(docked);
190
+ }
191
+ }, [isDockedExternal, isDocked, setIsDockedExternal]);
192
+ const onMoveWithArrows = useCallback(event => {
193
+ if (!MOVE_KEYS_VALUES.includes(event.key)) {
194
+ return;
195
+ }
196
+ const element = panelRef.current;
197
+ if (!element) {
198
+ return;
199
+ }
200
+ const dx = delta[event.key].dx;
201
+ const dy = delta[event.key].dy;
202
+ const currentState = getPanelStateFromElement(element);
203
+ const maxLeft = window.innerWidth - currentState.width * 0.25;
204
+ const maxTop = window.innerHeight - headerHeightRef.current;
205
+ const nextLeft = currentState.left + dx;
206
+ const nextTop = currentState.top + dy;
207
+ let shouldDockInsteadOfMove = false;
208
+ const nextState = {
209
+ ...state,
210
+ top: between(nextTop, 0, maxTop),
211
+ left: between(nextLeft, 0, maxLeft)
212
+ };
213
+ shouldDockInsteadOfMove = isInDockArea(nextState) !== isDocked;
214
+ if (shouldDockInsteadOfMove) {
215
+ setIsDocked(true);
216
+ setIsDockedExternal(true);
217
+ } else {
218
+ setState(nextState);
219
+ }
220
+ }, [isDocked, state, setIsDockedExternal]);
221
+ const getPosition = useCallback(() => {
222
+ return lastPositionRef.current;
223
+ }, []);
224
+ useEffect(() => {
225
+ if (isDockedExternal !== isDocked) {
226
+ setIsDockedHandler(isDockedExternal);
227
+ }
228
+ }, [isDockedExternal]);
229
+
230
+ // update position on window resize
231
+ useEffect(() => {
232
+ const header = headerRef.current;
233
+ headerHeightRef.current = header.getBoundingClientRect().height;
234
+ const onResizeHandler = () => {
235
+ const start = lastPositionRef.current;
236
+ const maxLeft = window.innerWidth - start.width;
237
+ const maxTop = window.innerHeight - headerHeightRef.current;
238
+ if (start.left > maxLeft || start.top > maxTop) {
239
+ setState(state => ({
240
+ ...state,
241
+ top: between(start.top, 0, maxTop),
242
+ left: between(start.left, 0, maxLeft)
243
+ }));
244
+ }
245
+ };
246
+ window.addEventListener('resize', onResizeHandler);
247
+ return () => {
248
+ window.removeEventListener('resize', onResizeHandler);
249
+ if (animationRef.current) {
250
+ cancelAnimationFrame(animationRef.current);
251
+ animationRef.current = null;
252
+ }
253
+ };
254
+ }, []);
255
+ useFocusTrap(panelRef);
256
+ // update style according to content and header height
257
+ useFloatingPanelRoot({
258
+ containerElement,
259
+ pageContentElement
260
+ });
261
+ const context = useMemo(() => ({
262
+ onDrag,
263
+ onDragStart: onMovementStart,
264
+ onDragEnd,
265
+ onMoveWithArrows,
266
+ isDocked,
267
+ setIsDocked: setIsDockedHandler,
268
+ getPosition,
269
+ isMoving
270
+ }), [isDocked, setIsDockedHandler, onDrag, onDragEnd, onMovementStart, onMoveWithArrows, getPosition, isMoving]);
271
+ const api = {
272
+ style,
273
+ panelRef,
274
+ headerRef,
275
+ onResize,
276
+ onMovementStart,
277
+ onResizeEnd,
278
+ setState,
279
+ context,
280
+ isDocked,
281
+ setIsDocked: setIsDockedHandler,
282
+ getPosition
283
+ };
284
+ return useMemo(() => api, Object.values(api));
285
+ }
286
+ function getPanelStateFromElement(element) {
287
+ const bbox = element.getBoundingClientRect();
288
+ return {
289
+ width: bbox.width,
290
+ height: bbox.height,
291
+ left: bbox.x,
292
+ top: bbox.y
293
+ };
294
+ }
295
+ function isInDockArea(panelPosition) {
296
+ return panelPosition.left + panelPosition.width > window.innerWidth - dockAreaWidth;
297
+ }
298
+ function toggleDockHighlight(isDockHighlighted, element) {
299
+ if (!element) {
300
+ return;
301
+ }
302
+ if (isDockHighlighted) {
303
+ element.classList.add(HIGHLIGHTED_CLASSNAME);
304
+ } else {
305
+ element.classList.remove(HIGHLIGHTED_CLASSNAME);
306
+ }
307
+ }
308
+ function between(value, min, max) {
309
+ return Math.min(Math.max(value, min), max);
310
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Use inside the content component for FloatingPanel in order to get access to context
3
+ */
4
+ export declare function useFloatingPanel(): import("./types").FloatingPanelContextType;
@@ -0,0 +1,9 @@
1
+ import { useContext } from 'react';
2
+ import { FloatingPanelContext } from './constants';
3
+
4
+ /**
5
+ * Use inside the content component for FloatingPanel in order to get access to context
6
+ */
7
+ export function useFloatingPanel() {
8
+ return useContext(FloatingPanelContext);
9
+ }
package/esm/index.d.ts CHANGED
@@ -39,6 +39,7 @@ export * from './file-upload';
39
39
  export * from './file-uploader';
40
40
  export * from './files-upload-area';
41
41
  export * from './filtering-panel';
42
+ export * from './floating-panel';
42
43
  export * from './font-awesome-icon';
43
44
  export * from './form-field';
44
45
  export * from './full-screen-drawer';
package/esm/index.js CHANGED
@@ -39,6 +39,7 @@ export * from './file-upload';
39
39
  export * from './file-uploader';
40
40
  export * from './files-upload-area';
41
41
  export * from './filtering-panel';
42
+ export * from './floating-panel';
42
43
  export * from './font-awesome-icon';
43
44
  export * from './form-field';
44
45
  export * from './full-screen-drawer';
@@ -7,14 +7,21 @@ import { jsx as _jsx } from "react/jsx-runtime";
7
7
  function calculateDelta(columns, maxWidth, padding = 0, columnSizing = {}, visibility = {}) {
8
8
  // we are interested only in visible columns
9
9
  const visibleColumns = columns.filter(({
10
- accessorKey
11
- }) => visibility[accessorKey] !== false);
10
+ accessorKey,
11
+ id
12
+ }) => {
13
+ // visibility state uses column ids for setting visibility, and for most cases it's the same as accessorKey
14
+ // but for the case when either 1) user provided an explicit id to the columnDef or 2) accessorKey is a complex get object path like `<object_name>.<object_property_name>`
15
+ // accessorKey would be different from id. Thus we should add a check on columnDef.id here
16
+ const visibilityValue = visibility[id] ?? visibility[accessorKey];
17
+ return visibilityValue !== false;
18
+ });
12
19
  // we need to know columns that do have maxSize set, as it will influence how free space is distributed
13
20
  const columnsWithMaxSize = visibleColumns.filter(column => !!column.maxSize);
14
21
 
15
22
  // calculation of all current columns width
16
23
  const currentWidth = visibleColumns.reduce((acc, current) => {
17
- const resizeValue = columnSizing[current.accessorKey];
24
+ const resizeValue = columnSizing[current.id] || columnSizing[current.accessorKey];
18
25
  const minWidth = current.minSize || COLUMN_SIZE.MIN_WIDTH;
19
26
  const resizeWidth = resizeValue ? Math.max(resizeValue, minWidth) : 0;
20
27
  const columnSize = Math.max(current.size || COLUMN_SIZE.WIDTH, minWidth);
@@ -32,8 +39,9 @@ function calculateDelta(columns, maxWidth, padding = 0, columnSizing = {}, visib
32
39
  // whose size was altered manually by drag-n-drop in UI)
33
40
  const notSettedColumns = visibleColumns.filter(({
34
41
  size,
35
- accessorKey
36
- }) => !size && !columnSizing[accessorKey]);
42
+ accessorKey,
43
+ id
44
+ }) => !size && !(columnSizing[id] || columnSizing[accessorKey]));
37
45
  const evenDistribution = delta / notSettedColumns.length;
38
46
 
39
47
  // if there is no max size on any columns - distribute free space evenly across columns
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "@datarobot/design-system/floating-panel",
3
+ "private": true,
4
+ "main": "../cjs/floating-panel",
5
+ "module": "../esm/floating-panel",
6
+ "types": "../esm/index.d.ts"
7
+ }