orcs-design-system 3.3.66 → 3.3.68
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/es/components/Avatar/Avatar.stories.js +37 -0
- package/es/components/Avatar/CroppedImage.js +188 -0
- package/es/components/Avatar/index.js +49 -2
- package/es/components/SideNavV2/SideNav.js +5 -1
- package/es/components/SideNavV2/__tests__/resize.test.js +65 -0
- package/es/components/SideNavV2/components/ExpandedPanel.js +33 -4
- package/es/components/SideNavV2/hooks/useResize.js +6 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import Flex from "../Flex";
|
|
|
4
4
|
import Box from "../Box";
|
|
5
5
|
import Spacer from "../Spacer";
|
|
6
6
|
import StyledLink from "../StyledLink";
|
|
7
|
+
import Header from "../Typography";
|
|
7
8
|
import Avatar from ".";
|
|
8
9
|
import { action } from "@storybook/addon-actions";
|
|
9
10
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
@@ -436,6 +437,37 @@ export const headerAvatarVariants = () => {
|
|
|
436
437
|
});
|
|
437
438
|
};
|
|
438
439
|
headerAvatarVariants.storyName = "Header Avatar Variants";
|
|
440
|
+
export const withCropData = () => /*#__PURE__*/_jsxs(Flex, {
|
|
441
|
+
gap: "xxl",
|
|
442
|
+
p: "r",
|
|
443
|
+
flexWrap: "wrap",
|
|
444
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
445
|
+
children: [/*#__PURE__*/_jsx(Header.H4, {
|
|
446
|
+
children: "Without Crop Data"
|
|
447
|
+
}), /*#__PURE__*/_jsx(Avatar, {
|
|
448
|
+
sizing: "large",
|
|
449
|
+
shape: "square",
|
|
450
|
+
image: "https://images.unsplash.com/photo-1533473359331-0135ef1b58bf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxNjAxODB8MHwxfHNlYXJjaHwzfHxjYXJ8ZW58MHwwfHx8MTc2MDMwNzg1MHww&ixlib=rb-4.1.0&q=80&w=1440&h=1440",
|
|
451
|
+
imageAlt: "Avatar without crop"
|
|
452
|
+
})]
|
|
453
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
454
|
+
children: [/*#__PURE__*/_jsx(Header.H4, {
|
|
455
|
+
children: "With Crop Data"
|
|
456
|
+
}), /*#__PURE__*/_jsx(Avatar, {
|
|
457
|
+
sizing: "large",
|
|
458
|
+
shape: "square",
|
|
459
|
+
image: "https://images.unsplash.com/photo-1533473359331-0135ef1b58bf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxNjAxODB8MHwxfHNlYXJjaHwzfHxjYXJ8ZW58MHwwfHx8MTc2MDMwNzg1MHww&ixlib=rb-4.1.0&q=80&w=1440&h=1440",
|
|
460
|
+
imageAlt: "Avatar with crop",
|
|
461
|
+
croppedAreaPercent: {
|
|
462
|
+
x: 49,
|
|
463
|
+
y: 39,
|
|
464
|
+
width: 33,
|
|
465
|
+
height: 50
|
|
466
|
+
}
|
|
467
|
+
})]
|
|
468
|
+
})]
|
|
469
|
+
});
|
|
470
|
+
withCropData.storyName = "With Crop Data";
|
|
439
471
|
defaultAvatar.__docgenInfo = {
|
|
440
472
|
"description": "",
|
|
441
473
|
"methods": [],
|
|
@@ -505,4 +537,9 @@ headerAvatarVariants.__docgenInfo = {
|
|
|
505
537
|
"description": "",
|
|
506
538
|
"methods": [],
|
|
507
539
|
"displayName": "headerAvatarVariants"
|
|
540
|
+
};
|
|
541
|
+
withCropData.__docgenInfo = {
|
|
542
|
+
"description": "",
|
|
543
|
+
"methods": [],
|
|
544
|
+
"displayName": "withCropData"
|
|
508
545
|
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import styled from "styled-components";
|
|
4
|
+
import { variant } from "styled-system";
|
|
5
|
+
import css from "@styled-system/css";
|
|
6
|
+
import { themeGet } from "@styled-system/theme-get";
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const CroppedImageContainer = /*#__PURE__*/styled.div.withConfig({
|
|
9
|
+
displayName: "CroppedImageContainer",
|
|
10
|
+
componentId: "sc-1bfrurv-0"
|
|
11
|
+
})(["position:relative;width:100%;height:100%;overflow:hidden;"]);
|
|
12
|
+
const CroppedImageInner = /*#__PURE__*/styled.div.withConfig({
|
|
13
|
+
displayName: "CroppedImageInner",
|
|
14
|
+
componentId: "sc-1bfrurv-1"
|
|
15
|
+
})(["padding-bottom:100%;position:relative;width:100%;"]);
|
|
16
|
+
const CroppedImageElement = /*#__PURE__*/styled.img.withConfig({
|
|
17
|
+
displayName: "CroppedImageElement",
|
|
18
|
+
componentId: "sc-1bfrurv-2"
|
|
19
|
+
})(["position:absolute;top:0;left:0;transform-origin:top left;height:auto;width:calc(100% + 0.5px);"]);
|
|
20
|
+
const resolveCustomSize = (customSize, props) => {
|
|
21
|
+
if (!customSize) return null;
|
|
22
|
+
if (customSize.includes("px") || customSize.includes("rem") || customSize.includes("em") || customSize.includes("%")) {
|
|
23
|
+
return customSize;
|
|
24
|
+
} else {
|
|
25
|
+
return themeGet(`avatarScale.${customSize}`)(props) || customSize;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const CroppedImage = _ref => {
|
|
29
|
+
let {
|
|
30
|
+
src,
|
|
31
|
+
alt,
|
|
32
|
+
croppedAreaPercent,
|
|
33
|
+
className,
|
|
34
|
+
style
|
|
35
|
+
} = _ref;
|
|
36
|
+
const scale = 100 / (croppedAreaPercent.width || 1);
|
|
37
|
+
const transform = {
|
|
38
|
+
x: `${-(croppedAreaPercent.x ?? 0) * scale}%`,
|
|
39
|
+
y: `${-(croppedAreaPercent.y ?? 0) * scale}%`,
|
|
40
|
+
scale
|
|
41
|
+
};
|
|
42
|
+
const imageStyle = {
|
|
43
|
+
transform: `translate3d(${transform.x}, ${transform.y}, 0) scale3d(${transform.scale}, ${transform.scale}, 1)`,
|
|
44
|
+
width: "calc(100% + 0.5px)",
|
|
45
|
+
height: "auto"
|
|
46
|
+
};
|
|
47
|
+
return /*#__PURE__*/_jsx(CroppedImageContainer, {
|
|
48
|
+
className: className,
|
|
49
|
+
style: style,
|
|
50
|
+
children: /*#__PURE__*/_jsx(CroppedImageInner, {
|
|
51
|
+
children: /*#__PURE__*/_jsx(CroppedImageElement, {
|
|
52
|
+
src: src,
|
|
53
|
+
alt: alt,
|
|
54
|
+
style: imageStyle
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
CroppedImage.propTypes = {
|
|
60
|
+
src: PropTypes.string.isRequired,
|
|
61
|
+
alt: PropTypes.string,
|
|
62
|
+
croppedAreaPercent: PropTypes.shape({
|
|
63
|
+
x: PropTypes.number.isRequired,
|
|
64
|
+
y: PropTypes.number.isRequired,
|
|
65
|
+
width: PropTypes.number.isRequired,
|
|
66
|
+
height: PropTypes.number.isRequired
|
|
67
|
+
}).isRequired,
|
|
68
|
+
className: PropTypes.string,
|
|
69
|
+
style: PropTypes.object
|
|
70
|
+
};
|
|
71
|
+
export const CroppedImageWrapper = /*#__PURE__*/styled(CroppedImage).withConfig({
|
|
72
|
+
displayName: "CroppedImageWrapper",
|
|
73
|
+
componentId: "sc-1bfrurv-3"
|
|
74
|
+
})(props => css({
|
|
75
|
+
width: resolveCustomSize(props.customSize, props) || themeGet("avatarScale.avatarDefault")(props),
|
|
76
|
+
height: resolveCustomSize(props.customSize, props) || themeGet("avatarScale.avatarDefault")(props),
|
|
77
|
+
flex: "0 0 auto",
|
|
78
|
+
backgroundColor: themeGet("colors.greyLighter")(props),
|
|
79
|
+
border: "0",
|
|
80
|
+
display: "block",
|
|
81
|
+
borderRadius: "50%",
|
|
82
|
+
overflow: "hidden"
|
|
83
|
+
}), props => variant({
|
|
84
|
+
prop: "sizing",
|
|
85
|
+
variants: {
|
|
86
|
+
small: {
|
|
87
|
+
width: themeGet("avatarScale.avatarSmall")(props),
|
|
88
|
+
height: themeGet("avatarScale.avatarSmall")(props)
|
|
89
|
+
},
|
|
90
|
+
large: {
|
|
91
|
+
width: themeGet("avatarScale.avatarLarge")(props),
|
|
92
|
+
height: themeGet("avatarScale.avatarLarge")(props)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}), props => variant({
|
|
96
|
+
prop: "shape",
|
|
97
|
+
variants: {
|
|
98
|
+
square: {
|
|
99
|
+
borderRadius: themeGet("radii.2")(props)
|
|
100
|
+
},
|
|
101
|
+
hexagon: {
|
|
102
|
+
borderRadius: "0",
|
|
103
|
+
clipPath: "polygon(50% 0, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%)"
|
|
104
|
+
},
|
|
105
|
+
tag: {
|
|
106
|
+
borderRadius: "0",
|
|
107
|
+
clipPath: "polygon(6% 0, 95% 0, 95% 65%, 50% 100%, 6% 65%)"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
export const HeaderAvatarCroppedImage = /*#__PURE__*/styled(CroppedImage).withConfig({
|
|
112
|
+
displayName: "HeaderAvatarCroppedImage",
|
|
113
|
+
componentId: "sc-1bfrurv-4"
|
|
114
|
+
})(_ref2 => {
|
|
115
|
+
let {
|
|
116
|
+
$size,
|
|
117
|
+
$shape
|
|
118
|
+
} = _ref2;
|
|
119
|
+
return {
|
|
120
|
+
width: $size,
|
|
121
|
+
height: $size,
|
|
122
|
+
transition: "width 200ms ease-in-out, height 200ms ease-in-out",
|
|
123
|
+
borderRadius: $shape === "circle" || !$shape ? "50%" : $shape === "square" ? "6px" : "0",
|
|
124
|
+
overflow: "hidden",
|
|
125
|
+
display: "block"
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
CroppedImage.__docgenInfo = {
|
|
129
|
+
"description": "",
|
|
130
|
+
"methods": [],
|
|
131
|
+
"displayName": "CroppedImage",
|
|
132
|
+
"props": {
|
|
133
|
+
"src": {
|
|
134
|
+
"description": "",
|
|
135
|
+
"type": {
|
|
136
|
+
"name": "string"
|
|
137
|
+
},
|
|
138
|
+
"required": true
|
|
139
|
+
},
|
|
140
|
+
"alt": {
|
|
141
|
+
"description": "",
|
|
142
|
+
"type": {
|
|
143
|
+
"name": "string"
|
|
144
|
+
},
|
|
145
|
+
"required": false
|
|
146
|
+
},
|
|
147
|
+
"croppedAreaPercent": {
|
|
148
|
+
"description": "",
|
|
149
|
+
"type": {
|
|
150
|
+
"name": "shape",
|
|
151
|
+
"value": {
|
|
152
|
+
"x": {
|
|
153
|
+
"name": "number",
|
|
154
|
+
"required": true
|
|
155
|
+
},
|
|
156
|
+
"y": {
|
|
157
|
+
"name": "number",
|
|
158
|
+
"required": true
|
|
159
|
+
},
|
|
160
|
+
"width": {
|
|
161
|
+
"name": "number",
|
|
162
|
+
"required": true
|
|
163
|
+
},
|
|
164
|
+
"height": {
|
|
165
|
+
"name": "number",
|
|
166
|
+
"required": true
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"required": true
|
|
171
|
+
},
|
|
172
|
+
"className": {
|
|
173
|
+
"description": "",
|
|
174
|
+
"type": {
|
|
175
|
+
"name": "string"
|
|
176
|
+
},
|
|
177
|
+
"required": false
|
|
178
|
+
},
|
|
179
|
+
"style": {
|
|
180
|
+
"description": "",
|
|
181
|
+
"type": {
|
|
182
|
+
"name": "object"
|
|
183
|
+
},
|
|
184
|
+
"required": false
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
export default CroppedImage;
|
|
@@ -6,6 +6,7 @@ import Header, { Text } from "../Typography";
|
|
|
6
6
|
import Icon from "../Icon";
|
|
7
7
|
import Popover from "../Popover";
|
|
8
8
|
import StatusDot from "../StatusDot";
|
|
9
|
+
import { CroppedImageWrapper, HeaderAvatarCroppedImage } from "./CroppedImage";
|
|
9
10
|
import css from "@styled-system/css";
|
|
10
11
|
import { themeGet } from "@styled-system/theme-get";
|
|
11
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -454,6 +455,7 @@ const Avatar = _ref0 => {
|
|
|
454
455
|
let {
|
|
455
456
|
sizing,
|
|
456
457
|
image,
|
|
458
|
+
croppedAreaPercent,
|
|
457
459
|
gradient,
|
|
458
460
|
fallbackGradient,
|
|
459
461
|
imageAlt,
|
|
@@ -500,7 +502,13 @@ const Avatar = _ref0 => {
|
|
|
500
502
|
theme
|
|
501
503
|
}) || customSize;
|
|
502
504
|
}
|
|
503
|
-
const imgContent = image ? /*#__PURE__*/_jsx(
|
|
505
|
+
const imgContent = image ? croppedAreaPercent ? /*#__PURE__*/_jsx(HeaderAvatarCroppedImage, {
|
|
506
|
+
src: image,
|
|
507
|
+
alt: alt,
|
|
508
|
+
croppedAreaPercent: croppedAreaPercent,
|
|
509
|
+
$size: size,
|
|
510
|
+
$shape: shape
|
|
511
|
+
}) : /*#__PURE__*/_jsx(HeaderAvatarImage, {
|
|
504
512
|
src: image,
|
|
505
513
|
alt: alt,
|
|
506
514
|
$size: size,
|
|
@@ -573,7 +581,14 @@ const Avatar = _ref0 => {
|
|
|
573
581
|
...props,
|
|
574
582
|
type: type,
|
|
575
583
|
sizing: sizing,
|
|
576
|
-
children: [image ? /*#__PURE__*/_jsx(
|
|
584
|
+
children: [image ? croppedAreaPercent ? /*#__PURE__*/_jsx(CroppedImageWrapper, {
|
|
585
|
+
src: image,
|
|
586
|
+
alt: alt,
|
|
587
|
+
croppedAreaPercent: croppedAreaPercent,
|
|
588
|
+
sizing: sizing,
|
|
589
|
+
shape: shape,
|
|
590
|
+
customSize: customSize
|
|
591
|
+
}) : /*#__PURE__*/_jsx(Image, {
|
|
577
592
|
src: image,
|
|
578
593
|
sizing: sizing,
|
|
579
594
|
shape: shape,
|
|
@@ -647,6 +662,13 @@ Avatar.propTypes = {
|
|
|
647
662
|
type: PropTypes.oneOf(["inverted", "default"]),
|
|
648
663
|
/** Specifies a source path for an image */
|
|
649
664
|
image: PropTypes.string,
|
|
665
|
+
/** Specifies crop data for the image with x, y, width, and height properties */
|
|
666
|
+
croppedAreaPercent: PropTypes.shape({
|
|
667
|
+
x: PropTypes.number.isRequired,
|
|
668
|
+
y: PropTypes.number.isRequired,
|
|
669
|
+
width: PropTypes.number.isRequired,
|
|
670
|
+
height: PropTypes.number.isRequired
|
|
671
|
+
}),
|
|
650
672
|
/** Specifies the gradient for the avatar background */
|
|
651
673
|
gradient: PropTypes.string,
|
|
652
674
|
/** Specifies the fallback gradient when no image is provided */
|
|
@@ -821,6 +843,31 @@ Avatar.__docgenInfo = {
|
|
|
821
843
|
},
|
|
822
844
|
"required": false
|
|
823
845
|
},
|
|
846
|
+
"croppedAreaPercent": {
|
|
847
|
+
"description": "Specifies crop data for the image with x, y, width, and height properties",
|
|
848
|
+
"type": {
|
|
849
|
+
"name": "shape",
|
|
850
|
+
"value": {
|
|
851
|
+
"x": {
|
|
852
|
+
"name": "number",
|
|
853
|
+
"required": true
|
|
854
|
+
},
|
|
855
|
+
"y": {
|
|
856
|
+
"name": "number",
|
|
857
|
+
"required": true
|
|
858
|
+
},
|
|
859
|
+
"width": {
|
|
860
|
+
"name": "number",
|
|
861
|
+
"required": true
|
|
862
|
+
},
|
|
863
|
+
"height": {
|
|
864
|
+
"name": "number",
|
|
865
|
+
"required": true
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
"required": false
|
|
870
|
+
},
|
|
824
871
|
"gradient": {
|
|
825
872
|
"description": "Specifies the gradient for the avatar background",
|
|
826
873
|
"type": {
|
|
@@ -73,6 +73,8 @@ const SideNavV2 = _ref => {
|
|
|
73
73
|
// Memoize expensive calculations to prevent unnecessary re-renders
|
|
74
74
|
const currentItem = useMemo(() => expandedItem !== null ? items[expandedItem] : null, [expandedItem, items]);
|
|
75
75
|
const {
|
|
76
|
+
isResizing,
|
|
77
|
+
hasResized,
|
|
76
78
|
handleResizeStart
|
|
77
79
|
} = useResize(expandedRef, isSmallScreen, expandedItem, handleWidthChange, currentItem);
|
|
78
80
|
|
|
@@ -147,7 +149,9 @@ const SideNavV2 = _ref => {
|
|
|
147
149
|
isSmallScreen: isSmallScreen,
|
|
148
150
|
expandedRef: expandedRef,
|
|
149
151
|
onResizeStart: handleResizeStart,
|
|
150
|
-
onItemClick: handleItemClick
|
|
152
|
+
onItemClick: handleItemClick,
|
|
153
|
+
isResizing: isResizing,
|
|
154
|
+
hasResized: hasResized
|
|
151
155
|
}, item.name);
|
|
152
156
|
})]
|
|
153
157
|
});
|
|
@@ -31,6 +31,9 @@ const TestComponent = _ref => {
|
|
|
31
31
|
}), /*#__PURE__*/_jsx("div", {
|
|
32
32
|
"data-testid": "resize-status",
|
|
33
33
|
children: resizeHandlers.isResizing ? "resizing" : "not-resizing"
|
|
34
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
35
|
+
"data-testid": "has-resized-status",
|
|
36
|
+
children: resizeHandlers.hasResized ? "has-resized" : "not-resized"
|
|
34
37
|
})]
|
|
35
38
|
});
|
|
36
39
|
};
|
|
@@ -145,4 +148,66 @@ describe("useResize hook", () => {
|
|
|
145
148
|
});
|
|
146
149
|
expect(mockExpandedRef.current.style.height).toBe("250px");
|
|
147
150
|
});
|
|
151
|
+
it("should track isResizing state correctly during resize operations", () => {
|
|
152
|
+
const {
|
|
153
|
+
getByTestId
|
|
154
|
+
} = render(/*#__PURE__*/_jsx(TestComponent, {
|
|
155
|
+
expandedRef: mockExpandedRef,
|
|
156
|
+
isSmallScreen: false,
|
|
157
|
+
expandedItem: 0,
|
|
158
|
+
onWidthChange: mockOnWidthChange,
|
|
159
|
+
currentItem: 1
|
|
160
|
+
}));
|
|
161
|
+
|
|
162
|
+
// Initially not resizing
|
|
163
|
+
expect(getByTestId("resize-status")).toHaveTextContent("not-resizing");
|
|
164
|
+
|
|
165
|
+
// Start resizing
|
|
166
|
+
const resizeHandle = getByTestId("resize-handle");
|
|
167
|
+
fireEvent.mouseDown(resizeHandle);
|
|
168
|
+
|
|
169
|
+
// Should be resizing now
|
|
170
|
+
expect(getByTestId("resize-status")).toHaveTextContent("resizing");
|
|
171
|
+
|
|
172
|
+
// End resize
|
|
173
|
+
fireEvent.mouseUp(document);
|
|
174
|
+
|
|
175
|
+
// Should not be resizing anymore
|
|
176
|
+
expect(getByTestId("resize-status")).toHaveTextContent("not-resizing");
|
|
177
|
+
});
|
|
178
|
+
it("should track hasResized state correctly when resize movement occurs", () => {
|
|
179
|
+
const {
|
|
180
|
+
getByTestId
|
|
181
|
+
} = render(/*#__PURE__*/_jsx(TestComponent, {
|
|
182
|
+
expandedRef: mockExpandedRef,
|
|
183
|
+
isSmallScreen: false,
|
|
184
|
+
expandedItem: 0,
|
|
185
|
+
onWidthChange: mockOnWidthChange,
|
|
186
|
+
currentItem: 1
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
// Initially not resized
|
|
190
|
+
expect(getByTestId("has-resized-status")).toHaveTextContent("not-resized");
|
|
191
|
+
|
|
192
|
+
// Start resizing
|
|
193
|
+
const resizeHandle = getByTestId("resize-handle");
|
|
194
|
+
fireEvent.mouseDown(resizeHandle);
|
|
195
|
+
|
|
196
|
+
// Still not resized (no movement yet)
|
|
197
|
+
expect(getByTestId("has-resized-status")).toHaveTextContent("not-resized");
|
|
198
|
+
|
|
199
|
+
// Simulate mouse move (actual resize)
|
|
200
|
+
fireEvent.mouseMove(document, {
|
|
201
|
+
clientX: 600
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Should be marked as resized
|
|
205
|
+
expect(getByTestId("has-resized-status")).toHaveTextContent("has-resized");
|
|
206
|
+
|
|
207
|
+
// End resize
|
|
208
|
+
fireEvent.mouseUp(document);
|
|
209
|
+
|
|
210
|
+
// Should still be marked as resized
|
|
211
|
+
expect(getByTestId("has-resized-status")).toHaveTextContent("has-resized");
|
|
212
|
+
});
|
|
148
213
|
});
|
|
@@ -21,6 +21,8 @@ import Icon from "../../Icon";
|
|
|
21
21
|
* @param {React.RefObject} props.expandedRef - Ref for the expanded panel
|
|
22
22
|
* @param {Function} props.onResizeStart - Resize start handler
|
|
23
23
|
* @param {Function} props.onItemClick - Item click handler
|
|
24
|
+
* @param {boolean} props.isResizing - Whether a resize operation is currently active
|
|
25
|
+
* @param {boolean} props.hasResized - Whether any resize movement has occurred
|
|
24
26
|
* @returns {JSX.Element} Rendered expanded panel or null if not applicable
|
|
25
27
|
*/
|
|
26
28
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -33,7 +35,9 @@ const ExpandedPanel = _ref => {
|
|
|
33
35
|
isSmallScreen,
|
|
34
36
|
expandedRef,
|
|
35
37
|
onResizeStart,
|
|
36
|
-
onItemClick
|
|
38
|
+
onItemClick,
|
|
39
|
+
isResizing,
|
|
40
|
+
hasResized
|
|
37
41
|
} = _ref;
|
|
38
42
|
const panelRef = useRef(null);
|
|
39
43
|
const isHoveringInPanelRef = useRef(false);
|
|
@@ -61,7 +65,7 @@ const ExpandedPanel = _ref => {
|
|
|
61
65
|
return /*#__PURE__*/_jsxs(Box, {
|
|
62
66
|
position: "relative",
|
|
63
67
|
height: "100%",
|
|
64
|
-
zIndex: "
|
|
68
|
+
zIndex: "1",
|
|
65
69
|
children: [/*#__PURE__*/_jsxs(SideNavExpanded, {
|
|
66
70
|
ref: el => {
|
|
67
71
|
expandedRef.current = el;
|
|
@@ -78,6 +82,15 @@ const ExpandedPanel = _ref => {
|
|
|
78
82
|
}), item.component()]
|
|
79
83
|
}), /*#__PURE__*/_jsx(ResizeHandle, {
|
|
80
84
|
onMouseDown: onResizeStart,
|
|
85
|
+
onClick: e => {
|
|
86
|
+
// Prevent click from firing if we're currently resizing or if we've resized
|
|
87
|
+
if (isResizing || hasResized) {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
onItemClick(item, true);
|
|
93
|
+
},
|
|
81
94
|
children: /*#__PURE__*/_jsxs(ToggleIcon, {
|
|
82
95
|
className: "resize-icon",
|
|
83
96
|
children: [/*#__PURE__*/_jsx(Icon, {
|
|
@@ -107,10 +120,12 @@ ExpandedPanel.propTypes = {
|
|
|
107
120
|
isSmallScreen: PropTypes.bool,
|
|
108
121
|
expandedRef: PropTypes.object,
|
|
109
122
|
onResizeStart: PropTypes.func.isRequired,
|
|
110
|
-
onItemClick: PropTypes.func.isRequired
|
|
123
|
+
onItemClick: PropTypes.func.isRequired,
|
|
124
|
+
isResizing: PropTypes.bool,
|
|
125
|
+
hasResized: PropTypes.bool
|
|
111
126
|
};
|
|
112
127
|
ExpandedPanel.__docgenInfo = {
|
|
113
|
-
"description": "ExpandedPanel - A resizable panel component for expanded navigation items\n\nRenders an expandable panel that can be resized by the user. Supports both\ndesktop (horizontal resize) and mobile (vertical resize) orientations.\nHover behavior is handled at the parent component level.\n\n@param {Object} props - Component props\n@param {Object} props.item - Navigation item data\n@param {number} [props.expandedItem] - Currently expanded item index\n@param {boolean} props.isExpanded - Whether the navigation is expanded\n@param {number} [props.expandedWidth] - Width of the expanded panel (desktop)\n@param {boolean} props.isSmallScreen - Whether currently on a small screen\n@param {React.RefObject} props.expandedRef - Ref for the expanded panel\n@param {Function} props.onResizeStart - Resize start handler\n@param {Function} props.onItemClick - Item click handler\n@returns {JSX.Element} Rendered expanded panel or null if not applicable",
|
|
128
|
+
"description": "ExpandedPanel - A resizable panel component for expanded navigation items\n\nRenders an expandable panel that can be resized by the user. Supports both\ndesktop (horizontal resize) and mobile (vertical resize) orientations.\nHover behavior is handled at the parent component level.\n\n@param {Object} props - Component props\n@param {Object} props.item - Navigation item data\n@param {number} [props.expandedItem] - Currently expanded item index\n@param {boolean} props.isExpanded - Whether the navigation is expanded\n@param {number} [props.expandedWidth] - Width of the expanded panel (desktop)\n@param {boolean} props.isSmallScreen - Whether currently on a small screen\n@param {React.RefObject} props.expandedRef - Ref for the expanded panel\n@param {Function} props.onResizeStart - Resize start handler\n@param {Function} props.onItemClick - Item click handler\n@param {boolean} props.isResizing - Whether a resize operation is currently active\n@param {boolean} props.hasResized - Whether any resize movement has occurred\n@returns {JSX.Element} Rendered expanded panel or null if not applicable",
|
|
114
129
|
"methods": [],
|
|
115
130
|
"displayName": "ExpandedPanel",
|
|
116
131
|
"props": {
|
|
@@ -191,6 +206,20 @@ ExpandedPanel.__docgenInfo = {
|
|
|
191
206
|
"name": "func"
|
|
192
207
|
},
|
|
193
208
|
"required": true
|
|
209
|
+
},
|
|
210
|
+
"isResizing": {
|
|
211
|
+
"description": "",
|
|
212
|
+
"type": {
|
|
213
|
+
"name": "bool"
|
|
214
|
+
},
|
|
215
|
+
"required": false
|
|
216
|
+
},
|
|
217
|
+
"hasResized": {
|
|
218
|
+
"description": "",
|
|
219
|
+
"type": {
|
|
220
|
+
"name": "bool"
|
|
221
|
+
},
|
|
222
|
+
"required": false
|
|
194
223
|
}
|
|
195
224
|
}
|
|
196
225
|
};
|
|
@@ -16,11 +16,13 @@ import { calculateDesktopWidth, calculateMobileHeight, applyResizeCursor, remove
|
|
|
16
16
|
*/
|
|
17
17
|
const useResize = (expandedRef, isSmallScreen, expandedItem, onWidthChange, currentItem) => {
|
|
18
18
|
const [isResizing, setIsResizing] = useState(false);
|
|
19
|
+
const [hasResized, setHasResized] = useState(false);
|
|
19
20
|
const [resizeStartY, setResizeStartY] = useState(0);
|
|
20
21
|
const [resizeStartHeight, setResizeStartHeight] = useState(0);
|
|
21
22
|
const handleResizeStart = useCallback(e => {
|
|
22
23
|
e.preventDefault();
|
|
23
24
|
setIsResizing(true);
|
|
25
|
+
setHasResized(false); // Reset resize flag
|
|
24
26
|
applyResizeCursor(isSmallScreen);
|
|
25
27
|
if (isSmallScreen && expandedRef.current) {
|
|
26
28
|
setResizeStartY(e.clientY);
|
|
@@ -31,6 +33,9 @@ const useResize = (expandedRef, isSmallScreen, expandedItem, onWidthChange, curr
|
|
|
31
33
|
if (!isResizing || !expandedRef.current) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
36
|
+
|
|
37
|
+
// Mark that we've actually resized
|
|
38
|
+
setHasResized(true);
|
|
34
39
|
if (isSmallScreen) {
|
|
35
40
|
// Vertical resizing for small screens
|
|
36
41
|
const newHeight = calculateMobileHeight(e.clientY, resizeStartY, resizeStartHeight, currentItem);
|
|
@@ -84,6 +89,7 @@ const useResize = (expandedRef, isSmallScreen, expandedItem, onWidthChange, curr
|
|
|
84
89
|
}, [isSmallScreen, expandedRef]);
|
|
85
90
|
return {
|
|
86
91
|
isResizing,
|
|
92
|
+
hasResized,
|
|
87
93
|
handleResizeStart,
|
|
88
94
|
handleResizeMove,
|
|
89
95
|
handleResizeEnd
|