@xsolla/xui-context-menu 0.172.2 → 0.173.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/native/index.d.mts +79 -75
- package/native/index.d.ts +79 -75
- package/native/index.js +796 -478
- package/native/index.js.map +1 -1
- package/native/index.mjs +807 -481
- package/native/index.mjs.map +1 -1
- package/package.json +10 -10
- package/web/index.d.mts +79 -75
- package/web/index.d.ts +79 -75
- package/web/index.js +896 -501
- package/web/index.js.map +1 -1
- package/web/index.mjs +889 -490
- package/web/index.mjs.map +1 -1
package/web/index.mjs
CHANGED
|
@@ -1,18 +1,269 @@
|
|
|
1
1
|
// src/ContextMenu.tsx
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
useMemo,
|
|
6
|
-
useRef,
|
|
7
|
-
useState as useState3,
|
|
2
|
+
import {
|
|
3
|
+
cloneElement,
|
|
4
|
+
forwardRef,
|
|
8
5
|
isValidElement,
|
|
9
|
-
|
|
6
|
+
useCallback as useCallback3,
|
|
7
|
+
useEffect as useEffect4,
|
|
8
|
+
useLayoutEffect as useLayoutEffect3,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef as useRef2,
|
|
11
|
+
useState as useState4
|
|
10
12
|
} from "react";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
|
|
14
|
+
// ../../foundation/primitives-web/src/Box.tsx
|
|
15
|
+
import React2 from "react";
|
|
16
|
+
import styled from "styled-components";
|
|
17
|
+
|
|
18
|
+
// ../../foundation/primitives-web/src/filterDOMProps.ts
|
|
19
|
+
import React from "react";
|
|
20
|
+
|
|
21
|
+
// ../../../node_modules/@emotion/memoize/dist/memoize.esm.js
|
|
22
|
+
function memoize(fn) {
|
|
23
|
+
var cache = {};
|
|
24
|
+
return function(arg) {
|
|
25
|
+
if (cache[arg] === void 0) cache[arg] = fn(arg);
|
|
26
|
+
return cache[arg];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
var memoize_esm_default = memoize;
|
|
30
|
+
|
|
31
|
+
// ../../../node_modules/@emotion/is-prop-valid/dist/is-prop-valid.esm.js
|
|
32
|
+
var reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|inert|itemProp|itemScope|itemType|itemID|itemRef|on|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/;
|
|
33
|
+
var index = memoize_esm_default(
|
|
34
|
+
function(prop) {
|
|
35
|
+
return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111 && prop.charCodeAt(1) === 110 && prop.charCodeAt(2) < 91;
|
|
36
|
+
}
|
|
37
|
+
/* Z+1 */
|
|
38
|
+
);
|
|
39
|
+
var is_prop_valid_esm_default = index;
|
|
40
|
+
|
|
41
|
+
// ../../foundation/primitives-web/src/filterDOMProps.ts
|
|
42
|
+
var ADDITIONAL_BLOCKED_PROPS = /* @__PURE__ */ new Set([
|
|
43
|
+
// RN-only event handlers (pass isPropValid's on* pattern)
|
|
44
|
+
"onPress",
|
|
45
|
+
"onChangeText",
|
|
46
|
+
"onLayout",
|
|
47
|
+
"onMoveShouldSetResponder",
|
|
48
|
+
"onResponderGrant",
|
|
49
|
+
"onResponderMove",
|
|
50
|
+
"onResponderRelease",
|
|
51
|
+
"onResponderTerminate",
|
|
52
|
+
// SVG attributes that pass isPropValid
|
|
53
|
+
"strokeWidth",
|
|
54
|
+
// CSS properties that pass isPropValid but are used as component props
|
|
55
|
+
"overflow",
|
|
56
|
+
"cursor",
|
|
57
|
+
"fontSize",
|
|
58
|
+
"fontWeight",
|
|
59
|
+
"fontFamily",
|
|
60
|
+
"textDecoration"
|
|
61
|
+
]);
|
|
62
|
+
function shouldForwardProp(key) {
|
|
63
|
+
if (ADDITIONAL_BLOCKED_PROPS.has(key)) return false;
|
|
64
|
+
return is_prop_valid_esm_default(key);
|
|
65
|
+
}
|
|
66
|
+
function createFilteredElement(defaultTag) {
|
|
67
|
+
const Component = React.forwardRef(
|
|
68
|
+
({ children, elementType, ...props }, ref) => {
|
|
69
|
+
const Tag = elementType || defaultTag;
|
|
70
|
+
const htmlProps = {};
|
|
71
|
+
for (const key of Object.keys(props)) {
|
|
72
|
+
if (shouldForwardProp(key)) {
|
|
73
|
+
htmlProps[key] = props[key];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return React.createElement(
|
|
77
|
+
Tag,
|
|
78
|
+
{ ref, ...htmlProps },
|
|
79
|
+
children
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
Component.displayName = `Filtered(${defaultTag})`;
|
|
84
|
+
return Component;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ../../foundation/primitives-web/src/Box.tsx
|
|
88
|
+
import { jsx } from "react/jsx-runtime";
|
|
89
|
+
var FilteredDiv = createFilteredElement("div");
|
|
90
|
+
var StyledBox = styled(FilteredDiv)`
|
|
91
|
+
display: flex;
|
|
92
|
+
box-sizing: border-box;
|
|
93
|
+
background-color: ${(props) => props.backgroundColor || "transparent"};
|
|
94
|
+
border-color: ${(props) => props.borderColor || "transparent"};
|
|
95
|
+
border-width: ${(props) => typeof props.borderWidth === "number" ? `${props.borderWidth}px` : props.borderWidth || 0};
|
|
96
|
+
|
|
97
|
+
${(props) => props.borderBottomWidth !== void 0 && `
|
|
98
|
+
border-bottom-width: ${typeof props.borderBottomWidth === "number" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};
|
|
99
|
+
border-bottom-color: ${props.borderBottomColor || props.borderColor || "transparent"};
|
|
100
|
+
border-bottom-style: solid;
|
|
101
|
+
`}
|
|
102
|
+
${(props) => props.borderTopWidth !== void 0 && `
|
|
103
|
+
border-top-width: ${typeof props.borderTopWidth === "number" ? `${props.borderTopWidth}px` : props.borderTopWidth};
|
|
104
|
+
border-top-color: ${props.borderTopColor || props.borderColor || "transparent"};
|
|
105
|
+
border-top-style: solid;
|
|
106
|
+
`}
|
|
107
|
+
${(props) => props.borderLeftWidth !== void 0 && `
|
|
108
|
+
border-left-width: ${typeof props.borderLeftWidth === "number" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};
|
|
109
|
+
border-left-color: ${props.borderLeftColor || props.borderColor || "transparent"};
|
|
110
|
+
border-left-style: solid;
|
|
111
|
+
`}
|
|
112
|
+
${(props) => props.borderRightWidth !== void 0 && `
|
|
113
|
+
border-right-width: ${typeof props.borderRightWidth === "number" ? `${props.borderRightWidth}px` : props.borderRightWidth};
|
|
114
|
+
border-right-color: ${props.borderRightColor || props.borderColor || "transparent"};
|
|
115
|
+
border-right-style: solid;
|
|
116
|
+
`}
|
|
117
|
+
|
|
118
|
+
border-style: ${(props) => props.borderStyle || (props.borderWidth || props.borderBottomWidth || props.borderTopWidth || props.borderLeftWidth || props.borderRightWidth ? "solid" : "none")};
|
|
119
|
+
border-radius: ${(props) => typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius || 0};
|
|
120
|
+
height: ${(props) => typeof props.height === "number" ? `${props.height}px` : props.height || "auto"};
|
|
121
|
+
width: ${(props) => typeof props.width === "number" ? `${props.width}px` : props.width || "auto"};
|
|
122
|
+
min-width: ${(props) => typeof props.minWidth === "number" ? `${props.minWidth}px` : props.minWidth || "auto"};
|
|
123
|
+
min-height: ${(props) => typeof props.minHeight === "number" ? `${props.minHeight}px` : props.minHeight || "auto"};
|
|
124
|
+
max-width: ${(props) => typeof props.maxWidth === "number" ? `${props.maxWidth}px` : props.maxWidth || "none"};
|
|
125
|
+
max-height: ${(props) => typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight || "none"};
|
|
126
|
+
|
|
127
|
+
padding: ${(props) => typeof props.padding === "number" ? `${props.padding}px` : props.padding || 0};
|
|
128
|
+
${(props) => props.paddingHorizontal && `
|
|
129
|
+
padding-left: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
|
|
130
|
+
padding-right: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
|
|
131
|
+
`}
|
|
132
|
+
${(props) => props.paddingVertical && `
|
|
133
|
+
padding-top: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
|
|
134
|
+
padding-bottom: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
|
|
135
|
+
`}
|
|
136
|
+
${(props) => props.paddingTop !== void 0 && `padding-top: ${typeof props.paddingTop === "number" ? `${props.paddingTop}px` : props.paddingTop};`}
|
|
137
|
+
${(props) => props.paddingBottom !== void 0 && `padding-bottom: ${typeof props.paddingBottom === "number" ? `${props.paddingBottom}px` : props.paddingBottom};`}
|
|
138
|
+
${(props) => props.paddingLeft !== void 0 && `padding-left: ${typeof props.paddingLeft === "number" ? `${props.paddingLeft}px` : props.paddingLeft};`}
|
|
139
|
+
${(props) => props.paddingRight !== void 0 && `padding-right: ${typeof props.paddingRight === "number" ? `${props.paddingRight}px` : props.paddingRight};`}
|
|
140
|
+
|
|
141
|
+
margin: ${(props) => typeof props.margin === "number" ? `${props.margin}px` : props.margin || 0};
|
|
142
|
+
${(props) => props.marginTop !== void 0 && `margin-top: ${typeof props.marginTop === "number" ? `${props.marginTop}px` : props.marginTop};`}
|
|
143
|
+
${(props) => props.marginBottom !== void 0 && `margin-bottom: ${typeof props.marginBottom === "number" ? `${props.marginBottom}px` : props.marginBottom};`}
|
|
144
|
+
${(props) => props.marginLeft !== void 0 && `margin-left: ${typeof props.marginLeft === "number" ? `${props.marginLeft}px` : props.marginLeft};`}
|
|
145
|
+
${(props) => props.marginRight !== void 0 && `margin-right: ${typeof props.marginRight === "number" ? `${props.marginRight}px` : props.marginRight};`}
|
|
146
|
+
|
|
147
|
+
flex-direction: ${(props) => props.flexDirection || "column"};
|
|
148
|
+
flex-wrap: ${(props) => props.flexWrap || "nowrap"};
|
|
149
|
+
align-items: ${(props) => props.alignItems || "stretch"};
|
|
150
|
+
justify-content: ${(props) => props.justifyContent || "flex-start"};
|
|
151
|
+
cursor: ${(props) => props.cursor ? props.cursor : props.onClick || props.onPress ? "pointer" : "inherit"};
|
|
152
|
+
position: ${(props) => props.position || "static"};
|
|
153
|
+
top: ${(props) => typeof props.top === "number" ? `${props.top}px` : props.top};
|
|
154
|
+
bottom: ${(props) => typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom};
|
|
155
|
+
left: ${(props) => typeof props.left === "number" ? `${props.left}px` : props.left};
|
|
156
|
+
right: ${(props) => typeof props.right === "number" ? `${props.right}px` : props.right};
|
|
157
|
+
flex: ${(props) => props.flex};
|
|
158
|
+
flex-shrink: ${(props) => props.flexShrink ?? 1};
|
|
159
|
+
gap: ${(props) => typeof props.gap === "number" ? `${props.gap}px` : props.gap || 0};
|
|
160
|
+
align-self: ${(props) => props.alignSelf || "auto"};
|
|
161
|
+
overflow: ${(props) => props.overflow || "visible"};
|
|
162
|
+
overflow-x: ${(props) => props.overflowX || "visible"};
|
|
163
|
+
overflow-y: ${(props) => props.overflowY || "visible"};
|
|
164
|
+
z-index: ${(props) => props.zIndex};
|
|
165
|
+
opacity: ${(props) => props.disabled ? 0.5 : 1};
|
|
166
|
+
pointer-events: ${(props) => props.disabled ? "none" : "auto"};
|
|
167
|
+
|
|
168
|
+
&:hover {
|
|
169
|
+
${(props) => props.hoverStyle?.backgroundColor && `background-color: ${props.hoverStyle.backgroundColor};`}
|
|
170
|
+
${(props) => props.hoverStyle?.borderColor && `border-color: ${props.hoverStyle.borderColor};`}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
&:active {
|
|
174
|
+
${(props) => props.pressStyle?.backgroundColor && `background-color: ${props.pressStyle.backgroundColor};`}
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
var Box = React2.forwardRef(
|
|
178
|
+
({
|
|
179
|
+
children,
|
|
180
|
+
onPress,
|
|
181
|
+
onKeyDown,
|
|
182
|
+
onKeyUp,
|
|
183
|
+
role,
|
|
184
|
+
"aria-label": ariaLabel,
|
|
185
|
+
"aria-labelledby": ariaLabelledBy,
|
|
186
|
+
"aria-current": ariaCurrent,
|
|
187
|
+
"aria-disabled": ariaDisabled,
|
|
188
|
+
"aria-live": ariaLive,
|
|
189
|
+
"aria-busy": ariaBusy,
|
|
190
|
+
"aria-describedby": ariaDescribedBy,
|
|
191
|
+
"aria-expanded": ariaExpanded,
|
|
192
|
+
"aria-haspopup": ariaHasPopup,
|
|
193
|
+
"aria-pressed": ariaPressed,
|
|
194
|
+
"aria-controls": ariaControls,
|
|
195
|
+
tabIndex,
|
|
196
|
+
as,
|
|
197
|
+
src,
|
|
198
|
+
alt,
|
|
199
|
+
onError,
|
|
200
|
+
onLoad,
|
|
201
|
+
type,
|
|
202
|
+
disabled,
|
|
203
|
+
id,
|
|
204
|
+
testID,
|
|
205
|
+
"data-testid": dataTestId,
|
|
206
|
+
...props
|
|
207
|
+
}, ref) => {
|
|
208
|
+
if (as === "img" && src) {
|
|
209
|
+
return /* @__PURE__ */ jsx(
|
|
210
|
+
"img",
|
|
211
|
+
{
|
|
212
|
+
src,
|
|
213
|
+
alt: alt || "",
|
|
214
|
+
onError,
|
|
215
|
+
onLoad,
|
|
216
|
+
style: {
|
|
217
|
+
display: "block",
|
|
218
|
+
objectFit: "cover",
|
|
219
|
+
width: typeof props.width === "number" ? `${props.width}px` : props.width,
|
|
220
|
+
height: typeof props.height === "number" ? `${props.height}px` : props.height,
|
|
221
|
+
borderRadius: typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius,
|
|
222
|
+
position: props.position,
|
|
223
|
+
top: typeof props.top === "number" ? `${props.top}px` : props.top,
|
|
224
|
+
left: typeof props.left === "number" ? `${props.left}px` : props.left,
|
|
225
|
+
right: typeof props.right === "number" ? `${props.right}px` : props.right,
|
|
226
|
+
bottom: typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom,
|
|
227
|
+
...props.style
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return /* @__PURE__ */ jsx(
|
|
233
|
+
StyledBox,
|
|
234
|
+
{
|
|
235
|
+
ref,
|
|
236
|
+
elementType: as,
|
|
237
|
+
id,
|
|
238
|
+
type: as === "button" ? type || "button" : void 0,
|
|
239
|
+
disabled: as === "button" ? disabled : void 0,
|
|
240
|
+
onClick: onPress,
|
|
241
|
+
onKeyDown,
|
|
242
|
+
onKeyUp,
|
|
243
|
+
role,
|
|
244
|
+
"aria-label": ariaLabel,
|
|
245
|
+
"aria-labelledby": ariaLabelledBy,
|
|
246
|
+
"aria-current": ariaCurrent,
|
|
247
|
+
"aria-disabled": ariaDisabled,
|
|
248
|
+
"aria-busy": ariaBusy,
|
|
249
|
+
"aria-describedby": ariaDescribedBy,
|
|
250
|
+
"aria-expanded": ariaExpanded,
|
|
251
|
+
"aria-haspopup": ariaHasPopup,
|
|
252
|
+
"aria-pressed": ariaPressed,
|
|
253
|
+
"aria-controls": ariaControls,
|
|
254
|
+
"aria-live": ariaLive,
|
|
255
|
+
tabIndex: tabIndex !== void 0 ? tabIndex : void 0,
|
|
256
|
+
"data-testid": dataTestId || testID,
|
|
257
|
+
...props,
|
|
258
|
+
children
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
Box.displayName = "Box";
|
|
264
|
+
|
|
265
|
+
// src/ContextMenu.tsx
|
|
266
|
+
import { useDesignSystem as useDesignSystem2, useId as useId2 } from "@xsolla/xui-core";
|
|
16
267
|
import { Spinner } from "@xsolla/xui-spinner";
|
|
17
268
|
|
|
18
269
|
// src/ContextMenuContext.tsx
|
|
@@ -33,7 +284,7 @@ var useContextMenuRequired = () => {
|
|
|
33
284
|
};
|
|
34
285
|
|
|
35
286
|
// src/ContextMenuItem.tsx
|
|
36
|
-
import
|
|
287
|
+
import React3, { useEffect, useLayoutEffect, useState } from "react";
|
|
37
288
|
import { createPortal } from "react-dom";
|
|
38
289
|
import {
|
|
39
290
|
useResolvedTheme,
|
|
@@ -43,7 +294,7 @@ import { Typography } from "@xsolla/xui-typography";
|
|
|
43
294
|
import { Checkbox } from "@xsolla/xui-checkbox";
|
|
44
295
|
import { Radio } from "@xsolla/xui-radio";
|
|
45
296
|
import { Search } from "@xsolla/xui-icons-base";
|
|
46
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
297
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
47
298
|
var sizeToVariants = {
|
|
48
299
|
xl: { label: "bodyLg", description: "bodyLg", headingAccent: "bodyLgAccent" },
|
|
49
300
|
lg: { label: "bodyLg", description: "bodyMd", headingAccent: "bodyMdAccent" },
|
|
@@ -53,18 +304,51 @@ var sizeToVariants = {
|
|
|
53
304
|
var sizeLabelOverride = {
|
|
54
305
|
xl: { fontSize: 20, lineHeight: "26px" }
|
|
55
306
|
};
|
|
307
|
+
var SUBMENU_GAP = 8;
|
|
308
|
+
var SUBMENU_VIEWPORT_PADDING = 8;
|
|
309
|
+
var clipsOverflow = (style) => {
|
|
310
|
+
return /(auto|scroll|hidden|clip)/.test(
|
|
311
|
+
`${style.overflow}${style.overflowX}${style.overflowY}`
|
|
312
|
+
);
|
|
313
|
+
};
|
|
314
|
+
var getSubmenuBoundary = (node) => {
|
|
315
|
+
const viewport = {
|
|
316
|
+
top: SUBMENU_VIEWPORT_PADDING,
|
|
317
|
+
right: window.innerWidth - SUBMENU_VIEWPORT_PADDING,
|
|
318
|
+
bottom: window.innerHeight - SUBMENU_VIEWPORT_PADDING,
|
|
319
|
+
left: SUBMENU_VIEWPORT_PADDING
|
|
320
|
+
};
|
|
321
|
+
let current = node.parentElement;
|
|
322
|
+
while (current && current !== document.body) {
|
|
323
|
+
const style = window.getComputedStyle(current);
|
|
324
|
+
if (clipsOverflow(style) && current.getAttribute("role") !== "menu") {
|
|
325
|
+
const rect = current.getBoundingClientRect();
|
|
326
|
+
return {
|
|
327
|
+
top: Math.max(viewport.top, rect.top + SUBMENU_VIEWPORT_PADDING),
|
|
328
|
+
right: Math.min(viewport.right, rect.right - SUBMENU_VIEWPORT_PADDING),
|
|
329
|
+
bottom: Math.min(
|
|
330
|
+
viewport.bottom,
|
|
331
|
+
rect.bottom - SUBMENU_VIEWPORT_PADDING
|
|
332
|
+
),
|
|
333
|
+
left: Math.max(viewport.left, rect.left + SUBMENU_VIEWPORT_PADDING)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
current = current.parentElement;
|
|
337
|
+
}
|
|
338
|
+
return viewport;
|
|
339
|
+
};
|
|
56
340
|
var ContextMenuItem = (props) => {
|
|
57
|
-
if (props.type === "option") return /* @__PURE__ */
|
|
58
|
-
if (props.type === "heading") return /* @__PURE__ */
|
|
59
|
-
if (props.type === "divider") return /* @__PURE__ */
|
|
60
|
-
if (props.type === "search") return /* @__PURE__ */
|
|
341
|
+
if (props.type === "option") return /* @__PURE__ */ jsx2(OptionCell, { ...props });
|
|
342
|
+
if (props.type === "heading") return /* @__PURE__ */ jsx2(HeadingCell, { ...props });
|
|
343
|
+
if (props.type === "divider") return /* @__PURE__ */ jsx2(DividerCell, { ...props });
|
|
344
|
+
if (props.type === "search") return /* @__PURE__ */ jsx2(SearchCell, { ...props });
|
|
61
345
|
return null;
|
|
62
346
|
};
|
|
63
347
|
ContextMenuItem.displayName = "ContextMenuItem";
|
|
64
348
|
var SubmenuChevron = ({
|
|
65
349
|
color,
|
|
66
350
|
size
|
|
67
|
-
}) => /* @__PURE__ */
|
|
351
|
+
}) => /* @__PURE__ */ jsx2(
|
|
68
352
|
"span",
|
|
69
353
|
{
|
|
70
354
|
"data-testid": "ctxmenu-submenu-chevron",
|
|
@@ -76,7 +360,7 @@ var SubmenuChevron = ({
|
|
|
76
360
|
width: size,
|
|
77
361
|
height: size
|
|
78
362
|
},
|
|
79
|
-
children: /* @__PURE__ */
|
|
363
|
+
children: /* @__PURE__ */ jsx2(
|
|
80
364
|
"svg",
|
|
81
365
|
{
|
|
82
366
|
width: size,
|
|
@@ -84,7 +368,7 @@ var SubmenuChevron = ({
|
|
|
84
368
|
viewBox: "0 0 24 24",
|
|
85
369
|
fill: "none",
|
|
86
370
|
xmlns: "http://www.w3.org/2000/svg",
|
|
87
|
-
children: /* @__PURE__ */
|
|
371
|
+
children: /* @__PURE__ */ jsx2(
|
|
88
372
|
"path",
|
|
89
373
|
{
|
|
90
374
|
d: "M17.0605 11.6464C17.2558 11.8417 17.2558 12.1583 17.0605 12.3536L9.70703 19.707L8.29297 18.293L14.5859 12L8.29297 5.70703L9.70703 4.29297L17.0605 11.6464Z",
|
|
@@ -106,6 +390,7 @@ var OptionCell = ({
|
|
|
106
390
|
leadingIcon,
|
|
107
391
|
status,
|
|
108
392
|
iconWrapper,
|
|
393
|
+
slot,
|
|
109
394
|
slotContent,
|
|
110
395
|
value,
|
|
111
396
|
hint,
|
|
@@ -114,12 +399,17 @@ var OptionCell = ({
|
|
|
114
399
|
hasSubmenu,
|
|
115
400
|
submenu,
|
|
116
401
|
onSelect,
|
|
402
|
+
onCheckedChange,
|
|
117
403
|
testID,
|
|
118
404
|
themeMode,
|
|
119
405
|
themeProductContext,
|
|
120
406
|
"data-testid": testId
|
|
121
407
|
}) => {
|
|
122
|
-
const { theme } = useResolvedTheme({
|
|
408
|
+
const { theme: rawTheme } = useResolvedTheme({
|
|
409
|
+
themeMode,
|
|
410
|
+
themeProductContext
|
|
411
|
+
});
|
|
412
|
+
const theme = rawTheme;
|
|
123
413
|
const ctx = useContextMenu();
|
|
124
414
|
const size = propSize ?? ctx?.size ?? "md";
|
|
125
415
|
const sizing = theme.sizing.contextMenu(size);
|
|
@@ -131,9 +421,9 @@ var OptionCell = ({
|
|
|
131
421
|
const [isHovered, setIsHovered] = useState(false);
|
|
132
422
|
const [submenuOpen, setSubmenuOpen] = useState(false);
|
|
133
423
|
const [submenuPos, setSubmenuPos] = useState(null);
|
|
134
|
-
const optionRef =
|
|
135
|
-
const submenuWrapperRef =
|
|
136
|
-
const closeTimerRef =
|
|
424
|
+
const optionRef = React3.useRef(null);
|
|
425
|
+
const submenuWrapperRef = React3.useRef(null);
|
|
426
|
+
const closeTimerRef = React3.useRef(
|
|
137
427
|
null
|
|
138
428
|
);
|
|
139
429
|
const cancelClose = () => {
|
|
@@ -176,7 +466,32 @@ var OptionCell = ({
|
|
|
176
466
|
const node = optionRef.current;
|
|
177
467
|
if (!node) return;
|
|
178
468
|
const rect = node.getBoundingClientRect();
|
|
179
|
-
|
|
469
|
+
const submenuRect = submenuWrapperRef.current?.getBoundingClientRect();
|
|
470
|
+
const submenuWidth = submenuRect?.width ?? 0;
|
|
471
|
+
const submenuHeight = submenuRect?.height ?? 0;
|
|
472
|
+
const boundary = getSubmenuBoundary(node);
|
|
473
|
+
const rightSideLeft = rect.right + SUBMENU_GAP;
|
|
474
|
+
const leftSideLeft = rect.left - SUBMENU_GAP - submenuWidth;
|
|
475
|
+
const opensLeft = submenuWidth > 0 && rightSideLeft + submenuWidth > boundary.right;
|
|
476
|
+
let left = opensLeft ? leftSideLeft : rightSideLeft;
|
|
477
|
+
if (submenuWidth > 0) {
|
|
478
|
+
if (opensLeft) {
|
|
479
|
+
left = Math.max(SUBMENU_VIEWPORT_PADDING, left);
|
|
480
|
+
} else {
|
|
481
|
+
left = Math.min(
|
|
482
|
+
Math.max(boundary.left, left),
|
|
483
|
+
Math.max(boundary.left, boundary.right - submenuWidth)
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
let top = rect.top;
|
|
488
|
+
if (submenuHeight > 0 && top + submenuHeight > boundary.bottom) {
|
|
489
|
+
top = boundary.bottom - submenuHeight;
|
|
490
|
+
}
|
|
491
|
+
top = Math.max(boundary.top, top);
|
|
492
|
+
setSubmenuPos(
|
|
493
|
+
(prev) => prev?.top === top && prev.left === left ? prev : { top, left }
|
|
494
|
+
);
|
|
180
495
|
};
|
|
181
496
|
update();
|
|
182
497
|
window.addEventListener("scroll", update, true);
|
|
@@ -185,8 +500,8 @@ var OptionCell = ({
|
|
|
185
500
|
window.removeEventListener("scroll", update, true);
|
|
186
501
|
window.removeEventListener("resize", update);
|
|
187
502
|
};
|
|
188
|
-
}
|
|
189
|
-
const onSelectRef =
|
|
503
|
+
});
|
|
504
|
+
const onSelectRef = React3.useRef(onSelect);
|
|
190
505
|
onSelectRef.current = onSelect;
|
|
191
506
|
useEffect(() => {
|
|
192
507
|
if (!registerCell || !unregisterCell) return;
|
|
@@ -204,12 +519,12 @@ var OptionCell = ({
|
|
|
204
519
|
onSelect: () => onSelectRef.current?.()
|
|
205
520
|
});
|
|
206
521
|
}, [registerCell, id, disabled]);
|
|
207
|
-
const
|
|
208
|
-
const isActive = ctx ?
|
|
522
|
+
const index2 = getCellIndex ? getCellIndex(id) : -1;
|
|
523
|
+
const isActive = ctx ? index2 >= 0 && ctx.activeIndex === index2 : false;
|
|
209
524
|
const inHoverState = isActive || !ctx && isHovered || hasSubmenu && submenuOpen;
|
|
210
525
|
const handleEnter = () => {
|
|
211
526
|
if (disabled) return;
|
|
212
|
-
if (ctx &&
|
|
527
|
+
if (ctx && index2 >= 0) ctx.setActiveIndex(index2);
|
|
213
528
|
if (!ctx) setIsHovered(true);
|
|
214
529
|
if (hasSubmenu) setSubmenuOpen(true);
|
|
215
530
|
};
|
|
@@ -219,7 +534,7 @@ var OptionCell = ({
|
|
|
219
534
|
};
|
|
220
535
|
const labelColor = disabled ? theme.colors.control.input.textDisable : destructive ? theme.colors.content.alert.primary : theme.colors.content.primary;
|
|
221
536
|
const bg = inHoverState ? theme.colors.control.input.bgHover : "transparent";
|
|
222
|
-
const role = !hasSubmenu && checked !== void 0 ? "menuitemcheckbox" : "menuitem";
|
|
537
|
+
const role = !hasSubmenu && checked !== void 0 ? leadingControl === "radio" ? "menuitemradio" : "menuitemcheckbox" : "menuitem";
|
|
223
538
|
const ariaChecked = !hasSubmenu && checked !== void 0 ? checked ? "true" : "false" : void 0;
|
|
224
539
|
const handleClick = () => {
|
|
225
540
|
if (disabled) return;
|
|
@@ -227,6 +542,9 @@ var OptionCell = ({
|
|
|
227
542
|
setSubmenuOpen(true);
|
|
228
543
|
return;
|
|
229
544
|
}
|
|
545
|
+
if (checked !== void 0) {
|
|
546
|
+
onCheckedChange?.(!checked);
|
|
547
|
+
}
|
|
230
548
|
onSelect?.();
|
|
231
549
|
};
|
|
232
550
|
const closeSubmenuAndFocus = () => {
|
|
@@ -256,6 +574,9 @@ var OptionCell = ({
|
|
|
256
574
|
}
|
|
257
575
|
if (e.key === "Enter" || e.key === " ") {
|
|
258
576
|
e.preventDefault();
|
|
577
|
+
if (checked !== void 0) {
|
|
578
|
+
onCheckedChange?.(!checked);
|
|
579
|
+
}
|
|
259
580
|
onSelect?.();
|
|
260
581
|
}
|
|
261
582
|
};
|
|
@@ -299,13 +620,13 @@ var OptionCell = ({
|
|
|
299
620
|
outline: "none"
|
|
300
621
|
},
|
|
301
622
|
children: [
|
|
302
|
-
leadingControl === "checkbox" && /* @__PURE__ */
|
|
623
|
+
leadingControl === "checkbox" && /* @__PURE__ */ jsx2(
|
|
303
624
|
"span",
|
|
304
625
|
{
|
|
305
626
|
"data-testid": "ctxmenu-leading-checkbox",
|
|
306
627
|
"aria-hidden": "true",
|
|
307
628
|
style: { pointerEvents: "none", display: "inline-flex" },
|
|
308
|
-
children: /* @__PURE__ */
|
|
629
|
+
children: /* @__PURE__ */ jsx2(
|
|
309
630
|
Checkbox,
|
|
310
631
|
{
|
|
311
632
|
size,
|
|
@@ -317,13 +638,13 @@ var OptionCell = ({
|
|
|
317
638
|
)
|
|
318
639
|
}
|
|
319
640
|
),
|
|
320
|
-
leadingControl === "radio" && /* @__PURE__ */
|
|
641
|
+
leadingControl === "radio" && /* @__PURE__ */ jsx2(
|
|
321
642
|
"span",
|
|
322
643
|
{
|
|
323
644
|
"data-testid": "ctxmenu-leading-radio",
|
|
324
645
|
"aria-hidden": "true",
|
|
325
646
|
style: { pointerEvents: "none", display: "inline-flex" },
|
|
326
|
-
children: /* @__PURE__ */
|
|
647
|
+
children: /* @__PURE__ */ jsx2(
|
|
327
648
|
Radio,
|
|
328
649
|
{
|
|
329
650
|
size,
|
|
@@ -338,6 +659,7 @@ var OptionCell = ({
|
|
|
338
659
|
leadingIcon,
|
|
339
660
|
status,
|
|
340
661
|
iconWrapper,
|
|
662
|
+
slot,
|
|
341
663
|
slotContent,
|
|
342
664
|
/* @__PURE__ */ jsxs(
|
|
343
665
|
"span",
|
|
@@ -350,7 +672,7 @@ var OptionCell = ({
|
|
|
350
672
|
minWidth: 0
|
|
351
673
|
},
|
|
352
674
|
children: [
|
|
353
|
-
/* @__PURE__ */
|
|
675
|
+
/* @__PURE__ */ jsx2(
|
|
354
676
|
Typography,
|
|
355
677
|
{
|
|
356
678
|
variant: variants.label,
|
|
@@ -363,7 +685,7 @@ var OptionCell = ({
|
|
|
363
685
|
children: label
|
|
364
686
|
}
|
|
365
687
|
),
|
|
366
|
-
description !== void 0 && /* @__PURE__ */
|
|
688
|
+
description !== void 0 && /* @__PURE__ */ jsx2(
|
|
367
689
|
Typography,
|
|
368
690
|
{
|
|
369
691
|
variant: variants.description,
|
|
@@ -383,7 +705,7 @@ var OptionCell = ({
|
|
|
383
705
|
alignItems: "flex-end"
|
|
384
706
|
},
|
|
385
707
|
children: [
|
|
386
|
-
value !== void 0 && /* @__PURE__ */
|
|
708
|
+
value !== void 0 && /* @__PURE__ */ jsx2(
|
|
387
709
|
Typography,
|
|
388
710
|
{
|
|
389
711
|
variant: variants.label,
|
|
@@ -392,7 +714,7 @@ var OptionCell = ({
|
|
|
392
714
|
children: value
|
|
393
715
|
}
|
|
394
716
|
),
|
|
395
|
-
hint !== void 0 && /* @__PURE__ */
|
|
717
|
+
hint !== void 0 && /* @__PURE__ */ jsx2(
|
|
396
718
|
Typography,
|
|
397
719
|
{
|
|
398
720
|
variant: variants.description,
|
|
@@ -403,7 +725,7 @@ var OptionCell = ({
|
|
|
403
725
|
]
|
|
404
726
|
}
|
|
405
727
|
),
|
|
406
|
-
keyboardShortcut && /* @__PURE__ */
|
|
728
|
+
keyboardShortcut && /* @__PURE__ */ jsx2(
|
|
407
729
|
Typography,
|
|
408
730
|
{
|
|
409
731
|
as: "kbd",
|
|
@@ -412,7 +734,7 @@ var OptionCell = ({
|
|
|
412
734
|
children: keyboardShortcut
|
|
413
735
|
}
|
|
414
736
|
),
|
|
415
|
-
hasSubmenu && /* @__PURE__ */
|
|
737
|
+
hasSubmenu && /* @__PURE__ */ jsx2(
|
|
416
738
|
SubmenuChevron,
|
|
417
739
|
{
|
|
418
740
|
color: theme.colors.content.tertiary,
|
|
@@ -421,7 +743,7 @@ var OptionCell = ({
|
|
|
421
743
|
),
|
|
422
744
|
trailingIcon,
|
|
423
745
|
hasSubmenu && submenuOpen && submenu && submenuPos && typeof document !== "undefined" && createPortal(
|
|
424
|
-
/* @__PURE__ */
|
|
746
|
+
/* @__PURE__ */ jsx2(
|
|
425
747
|
"div",
|
|
426
748
|
{
|
|
427
749
|
ref: submenuWrapperRef,
|
|
@@ -452,7 +774,11 @@ var HeadingCell = ({
|
|
|
452
774
|
themeProductContext,
|
|
453
775
|
"data-testid": testId
|
|
454
776
|
}) => {
|
|
455
|
-
const { theme } = useResolvedTheme({
|
|
777
|
+
const { theme: rawTheme } = useResolvedTheme({
|
|
778
|
+
themeMode,
|
|
779
|
+
themeProductContext
|
|
780
|
+
});
|
|
781
|
+
const theme = rawTheme;
|
|
456
782
|
const ctx = useContextMenu();
|
|
457
783
|
const size = propSize ?? ctx?.size ?? "md";
|
|
458
784
|
const sizing = theme.sizing.contextMenu(size);
|
|
@@ -480,7 +806,7 @@ var HeadingCell = ({
|
|
|
480
806
|
paddingBottom: sizing.itemPaddingVertical
|
|
481
807
|
},
|
|
482
808
|
children: [
|
|
483
|
-
/* @__PURE__ */
|
|
809
|
+
/* @__PURE__ */ jsx2(
|
|
484
810
|
Typography,
|
|
485
811
|
{
|
|
486
812
|
variant: variants.headingAccent,
|
|
@@ -489,7 +815,7 @@ var HeadingCell = ({
|
|
|
489
815
|
children: label
|
|
490
816
|
}
|
|
491
817
|
),
|
|
492
|
-
description !== void 0 && /* @__PURE__ */
|
|
818
|
+
description !== void 0 && /* @__PURE__ */ jsx2(
|
|
493
819
|
Typography,
|
|
494
820
|
{
|
|
495
821
|
variant: variants.description,
|
|
@@ -504,6 +830,7 @@ var HeadingCell = ({
|
|
|
504
830
|
var SearchCell = ({
|
|
505
831
|
size: propSize,
|
|
506
832
|
value,
|
|
833
|
+
onChange,
|
|
507
834
|
onValueChange,
|
|
508
835
|
placeholder = "Search",
|
|
509
836
|
autoFocus,
|
|
@@ -513,7 +840,11 @@ var SearchCell = ({
|
|
|
513
840
|
themeMode,
|
|
514
841
|
themeProductContext
|
|
515
842
|
}) => {
|
|
516
|
-
const { theme } = useResolvedTheme({
|
|
843
|
+
const { theme: rawTheme } = useResolvedTheme({
|
|
844
|
+
themeMode,
|
|
845
|
+
themeProductContext
|
|
846
|
+
});
|
|
847
|
+
const theme = rawTheme;
|
|
517
848
|
const ctx = useContextMenu();
|
|
518
849
|
const size = propSize ?? ctx?.size ?? "md";
|
|
519
850
|
const sizing = theme.sizing.contextMenu(size);
|
|
@@ -525,7 +856,7 @@ var SearchCell = ({
|
|
|
525
856
|
registerCell(id, { type: "search" });
|
|
526
857
|
return () => unregisterCell(id);
|
|
527
858
|
}, [registerCell, unregisterCell, id]);
|
|
528
|
-
return /* @__PURE__ */
|
|
859
|
+
return /* @__PURE__ */ jsx2(
|
|
529
860
|
"div",
|
|
530
861
|
{
|
|
531
862
|
style: {
|
|
@@ -546,7 +877,7 @@ var SearchCell = ({
|
|
|
546
877
|
borderBottom: `1px solid ${theme.colors.border.secondary}`
|
|
547
878
|
},
|
|
548
879
|
children: [
|
|
549
|
-
/* @__PURE__ */
|
|
880
|
+
/* @__PURE__ */ jsx2(
|
|
550
881
|
Search,
|
|
551
882
|
{
|
|
552
883
|
variant: "line",
|
|
@@ -555,16 +886,19 @@ var SearchCell = ({
|
|
|
555
886
|
"aria-hidden": true
|
|
556
887
|
}
|
|
557
888
|
),
|
|
558
|
-
/* @__PURE__ */
|
|
889
|
+
/* @__PURE__ */ jsx2(
|
|
559
890
|
"input",
|
|
560
891
|
{
|
|
561
892
|
type: "search",
|
|
562
893
|
role: "searchbox",
|
|
563
894
|
"aria-label": ariaLabel,
|
|
564
895
|
placeholder,
|
|
565
|
-
value,
|
|
896
|
+
value: value ?? "",
|
|
566
897
|
autoFocus,
|
|
567
|
-
onChange: (e) =>
|
|
898
|
+
onChange: (e) => {
|
|
899
|
+
onChange?.(e);
|
|
900
|
+
onValueChange?.(e.target.value);
|
|
901
|
+
},
|
|
568
902
|
"data-testid": testId || testID,
|
|
569
903
|
style: {
|
|
570
904
|
flex: 1,
|
|
@@ -587,7 +921,11 @@ var SearchCell = ({
|
|
|
587
921
|
);
|
|
588
922
|
};
|
|
589
923
|
var DividerCell = ({ themeMode, themeProductContext, "data-testid": testId }) => {
|
|
590
|
-
const { theme } = useResolvedTheme({
|
|
924
|
+
const { theme: rawTheme } = useResolvedTheme({
|
|
925
|
+
themeMode,
|
|
926
|
+
themeProductContext
|
|
927
|
+
});
|
|
928
|
+
const theme = rawTheme;
|
|
591
929
|
const ctx = useContextMenu();
|
|
592
930
|
const id = useId();
|
|
593
931
|
const registerCell = ctx?.registerCell;
|
|
@@ -597,7 +935,7 @@ var DividerCell = ({ themeMode, themeProductContext, "data-testid": testId }) =>
|
|
|
597
935
|
registerCell(id, { type: "divider" });
|
|
598
936
|
return () => unregisterCell(id);
|
|
599
937
|
}, [registerCell, unregisterCell, id]);
|
|
600
|
-
return /* @__PURE__ */
|
|
938
|
+
return /* @__PURE__ */ jsx2(
|
|
601
939
|
"div",
|
|
602
940
|
{
|
|
603
941
|
role: "separator",
|
|
@@ -611,8 +949,156 @@ var DividerCell = ({ themeMode, themeProductContext, "data-testid": testId }) =>
|
|
|
611
949
|
);
|
|
612
950
|
};
|
|
613
951
|
|
|
952
|
+
// src/ContextMenuSubmenu.tsx
|
|
953
|
+
import {
|
|
954
|
+
useState as useState2,
|
|
955
|
+
useRef,
|
|
956
|
+
useEffect as useEffect2,
|
|
957
|
+
useLayoutEffect as useLayoutEffect2,
|
|
958
|
+
useCallback
|
|
959
|
+
} from "react";
|
|
960
|
+
import { useDesignSystem } from "@xsolla/xui-core";
|
|
961
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
962
|
+
var SUBMENU_GAP2 = 4;
|
|
963
|
+
var OPEN_DELAY_MS = 200;
|
|
964
|
+
var CLOSE_GRACE_MS = 100;
|
|
965
|
+
var ContextMenuSubmenu = ({
|
|
966
|
+
label,
|
|
967
|
+
icon,
|
|
968
|
+
disabled,
|
|
969
|
+
children,
|
|
970
|
+
size: propSize,
|
|
971
|
+
"data-testid": testId = "context-menu-submenu"
|
|
972
|
+
}) => {
|
|
973
|
+
const { theme } = useDesignSystem();
|
|
974
|
+
const xuiTheme = theme;
|
|
975
|
+
const context = useContextMenu();
|
|
976
|
+
const size = propSize || context?.size || "md";
|
|
977
|
+
const sizeStyles = xuiTheme.sizing.contextMenu(size);
|
|
978
|
+
const borderRadius = xuiTheme.shape?.contextMenu?.[size]?.borderRadius ?? xuiTheme.radius?.button ?? 8;
|
|
979
|
+
const [isOpen, setIsOpen] = useState2(false);
|
|
980
|
+
const [visible, setVisible] = useState2(false);
|
|
981
|
+
const [openLeft, setOpenLeft] = useState2(false);
|
|
982
|
+
const [topOffset, setTopOffset] = useState2(0);
|
|
983
|
+
const triggerRef = useRef(null);
|
|
984
|
+
const submenuRef = useRef(null);
|
|
985
|
+
const openTimerRef = useRef(null);
|
|
986
|
+
const closeTimerRef = useRef(null);
|
|
987
|
+
const clearTimers = useCallback(() => {
|
|
988
|
+
if (openTimerRef.current) clearTimeout(openTimerRef.current);
|
|
989
|
+
if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
|
|
990
|
+
}, []);
|
|
991
|
+
const calculatePlacement = useCallback(() => {
|
|
992
|
+
if (!triggerRef.current) return;
|
|
993
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
994
|
+
const estimatedWidth = sizeStyles.minWidth + 32;
|
|
995
|
+
const wouldOverflowRight = triggerRect.right + estimatedWidth + SUBMENU_GAP2 > window.innerWidth - 8;
|
|
996
|
+
setOpenLeft(wouldOverflowRight);
|
|
997
|
+
setTopOffset(0);
|
|
998
|
+
}, [sizeStyles.minWidth]);
|
|
999
|
+
useLayoutEffect2(() => {
|
|
1000
|
+
if (!isOpen || !submenuRef.current || !triggerRef.current) return;
|
|
1001
|
+
const submenuRect = submenuRef.current.getBoundingClientRect();
|
|
1002
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
1003
|
+
const wouldOverflowRight = triggerRect.right + submenuRect.width + SUBMENU_GAP2 > window.innerWidth - 8;
|
|
1004
|
+
setOpenLeft(wouldOverflowRight);
|
|
1005
|
+
const overflowBottom = triggerRect.top + submenuRect.height - (window.innerHeight - 8);
|
|
1006
|
+
if (overflowBottom > 0) {
|
|
1007
|
+
setTopOffset(-Math.min(overflowBottom, triggerRect.top - 8));
|
|
1008
|
+
} else {
|
|
1009
|
+
setTopOffset(0);
|
|
1010
|
+
}
|
|
1011
|
+
}, [isOpen]);
|
|
1012
|
+
useEffect2(() => {
|
|
1013
|
+
if (!isOpen) {
|
|
1014
|
+
setVisible(false);
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
const raf = requestAnimationFrame(() => setVisible(true));
|
|
1018
|
+
return () => cancelAnimationFrame(raf);
|
|
1019
|
+
}, [isOpen]);
|
|
1020
|
+
useEffect2(() => () => clearTimers(), [clearTimers]);
|
|
1021
|
+
const handleTriggerEnter = () => {
|
|
1022
|
+
if (disabled) return;
|
|
1023
|
+
clearTimers();
|
|
1024
|
+
openTimerRef.current = setTimeout(() => {
|
|
1025
|
+
calculatePlacement();
|
|
1026
|
+
setIsOpen(true);
|
|
1027
|
+
}, OPEN_DELAY_MS);
|
|
1028
|
+
};
|
|
1029
|
+
const handleTriggerLeave = () => {
|
|
1030
|
+
clearTimers();
|
|
1031
|
+
closeTimerRef.current = setTimeout(() => setIsOpen(false), CLOSE_GRACE_MS);
|
|
1032
|
+
};
|
|
1033
|
+
const handleSubmenuEnter = () => clearTimers();
|
|
1034
|
+
const handleSubmenuLeave = () => {
|
|
1035
|
+
clearTimers();
|
|
1036
|
+
closeTimerRef.current = setTimeout(() => setIsOpen(false), CLOSE_GRACE_MS);
|
|
1037
|
+
};
|
|
1038
|
+
const submenuPositionStyle = openLeft ? { right: `calc(100% + ${SUBMENU_GAP2}px)` } : { left: `calc(100% + ${SUBMENU_GAP2}px)` };
|
|
1039
|
+
return /* @__PURE__ */ jsxs2(
|
|
1040
|
+
"div",
|
|
1041
|
+
{
|
|
1042
|
+
ref: triggerRef,
|
|
1043
|
+
style: { position: "relative" },
|
|
1044
|
+
onMouseEnter: handleTriggerEnter,
|
|
1045
|
+
onMouseLeave: handleTriggerLeave,
|
|
1046
|
+
"data-testid": testId,
|
|
1047
|
+
children: [
|
|
1048
|
+
/* @__PURE__ */ jsx3(
|
|
1049
|
+
ContextMenuItem,
|
|
1050
|
+
{
|
|
1051
|
+
type: "option",
|
|
1052
|
+
label,
|
|
1053
|
+
leadingIcon: icon,
|
|
1054
|
+
disabled,
|
|
1055
|
+
hasSubmenu: true,
|
|
1056
|
+
size,
|
|
1057
|
+
"data-testid": `${testId}-trigger`
|
|
1058
|
+
}
|
|
1059
|
+
),
|
|
1060
|
+
isOpen && /* @__PURE__ */ jsx3(
|
|
1061
|
+
"div",
|
|
1062
|
+
{
|
|
1063
|
+
ref: submenuRef,
|
|
1064
|
+
onMouseEnter: handleSubmenuEnter,
|
|
1065
|
+
onMouseLeave: handleSubmenuLeave,
|
|
1066
|
+
style: {
|
|
1067
|
+
position: "absolute",
|
|
1068
|
+
top: topOffset,
|
|
1069
|
+
...submenuPositionStyle,
|
|
1070
|
+
zIndex: 1001,
|
|
1071
|
+
opacity: visible ? 1 : 0,
|
|
1072
|
+
transform: visible ? "translateX(0)" : openLeft ? "translateX(4px)" : "translateX(-4px)",
|
|
1073
|
+
transition: "opacity 100ms ease, transform 100ms ease"
|
|
1074
|
+
},
|
|
1075
|
+
"data-testid": `${testId}-content`,
|
|
1076
|
+
children: /* @__PURE__ */ jsx3(
|
|
1077
|
+
Box,
|
|
1078
|
+
{
|
|
1079
|
+
role: "menu",
|
|
1080
|
+
backgroundColor: xuiTheme.colors.background.secondary,
|
|
1081
|
+
borderColor: xuiTheme.colors.border.secondary,
|
|
1082
|
+
borderWidth: 1,
|
|
1083
|
+
borderRadius,
|
|
1084
|
+
paddingVertical: sizeStyles.paddingVertical,
|
|
1085
|
+
style: {
|
|
1086
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
1087
|
+
minWidth: sizeStyles.minWidth
|
|
1088
|
+
},
|
|
1089
|
+
children
|
|
1090
|
+
}
|
|
1091
|
+
)
|
|
1092
|
+
}
|
|
1093
|
+
)
|
|
1094
|
+
]
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
1097
|
+
};
|
|
1098
|
+
ContextMenuSubmenu.displayName = "ContextMenuSubmenu";
|
|
1099
|
+
|
|
614
1100
|
// src/hooks/useContextMenuPosition.ts
|
|
615
|
-
import { useEffect as
|
|
1101
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
616
1102
|
var splitPlacement = (placement) => {
|
|
617
1103
|
const [vertical, horizontal] = placement.split("-");
|
|
618
1104
|
return { vertical, horizontal };
|
|
@@ -625,8 +1111,8 @@ var useContextMenuPosition = ({
|
|
|
625
1111
|
placement = "bottom-start",
|
|
626
1112
|
offset = 4
|
|
627
1113
|
}) => {
|
|
628
|
-
const [resolved, setResolved] =
|
|
629
|
-
|
|
1114
|
+
const [resolved, setResolved] = useState3();
|
|
1115
|
+
useEffect3(() => {
|
|
630
1116
|
if (!isOpen) {
|
|
631
1117
|
setResolved(void 0);
|
|
632
1118
|
return;
|
|
@@ -673,30 +1159,19 @@ var useContextMenuPosition = ({
|
|
|
673
1159
|
(prev) => prev && prev.top === next.top && prev.left === next.left && prev.placement === next.placement ? prev : next
|
|
674
1160
|
);
|
|
675
1161
|
};
|
|
676
|
-
|
|
1162
|
+
const rafId = window.requestAnimationFrame(compute);
|
|
677
1163
|
const onResize = () => compute();
|
|
678
|
-
let scrollRafPending = false;
|
|
679
|
-
const onScroll = () => {
|
|
680
|
-
if (scrollRafPending) return;
|
|
681
|
-
scrollRafPending = true;
|
|
682
|
-
rafId = window.requestAnimationFrame(() => {
|
|
683
|
-
scrollRafPending = false;
|
|
684
|
-
compute();
|
|
685
|
-
});
|
|
686
|
-
};
|
|
687
1164
|
window.addEventListener("resize", onResize);
|
|
688
|
-
window.addEventListener("scroll", onScroll, true);
|
|
689
1165
|
return () => {
|
|
690
1166
|
window.cancelAnimationFrame(rafId);
|
|
691
1167
|
window.removeEventListener("resize", onResize);
|
|
692
|
-
window.removeEventListener("scroll", onScroll, true);
|
|
693
1168
|
};
|
|
694
1169
|
}, [isOpen, placement, offset, triggerRef, panelRef]);
|
|
695
1170
|
return resolved;
|
|
696
1171
|
};
|
|
697
1172
|
|
|
698
1173
|
// src/hooks/useKeyboardNavigation.ts
|
|
699
|
-
import { useCallback } from "react";
|
|
1174
|
+
import { useCallback as useCallback2 } from "react";
|
|
700
1175
|
var isNavigableOption = (meta) => meta.type === "option" && !meta.disabled;
|
|
701
1176
|
var isTextInputTarget = (target) => {
|
|
702
1177
|
if (!(target instanceof HTMLElement)) return false;
|
|
@@ -711,19 +1186,19 @@ var useKeyboardNavigation = ({
|
|
|
711
1186
|
onClose,
|
|
712
1187
|
triggerRef
|
|
713
1188
|
}) => {
|
|
714
|
-
const findFirstOption =
|
|
1189
|
+
const findFirstOption = useCallback2(() => {
|
|
715
1190
|
for (let i = 0; i < cells.length; i += 1) {
|
|
716
1191
|
if (isNavigableOption(cells[i].meta)) return i;
|
|
717
1192
|
}
|
|
718
1193
|
return -1;
|
|
719
1194
|
}, [cells]);
|
|
720
|
-
const findLastOption =
|
|
1195
|
+
const findLastOption = useCallback2(() => {
|
|
721
1196
|
for (let i = cells.length - 1; i >= 0; i -= 1) {
|
|
722
1197
|
if (isNavigableOption(cells[i].meta)) return i;
|
|
723
1198
|
}
|
|
724
1199
|
return -1;
|
|
725
1200
|
}, [cells]);
|
|
726
|
-
const findNextOption =
|
|
1201
|
+
const findNextOption = useCallback2(
|
|
727
1202
|
(from) => {
|
|
728
1203
|
const len = cells.length;
|
|
729
1204
|
if (len === 0) return -1;
|
|
@@ -735,7 +1210,7 @@ var useKeyboardNavigation = ({
|
|
|
735
1210
|
},
|
|
736
1211
|
[cells]
|
|
737
1212
|
);
|
|
738
|
-
const findPrevOption =
|
|
1213
|
+
const findPrevOption = useCallback2(
|
|
739
1214
|
(from) => {
|
|
740
1215
|
const len = cells.length;
|
|
741
1216
|
if (len === 0) return -1;
|
|
@@ -747,7 +1222,7 @@ var useKeyboardNavigation = ({
|
|
|
747
1222
|
},
|
|
748
1223
|
[cells]
|
|
749
1224
|
);
|
|
750
|
-
const handleKeyDown =
|
|
1225
|
+
const handleKeyDown = useCallback2(
|
|
751
1226
|
(event) => {
|
|
752
1227
|
if (!isOpen) return;
|
|
753
1228
|
switch (event.key) {
|
|
@@ -817,458 +1292,382 @@ var useKeyboardNavigation = ({
|
|
|
817
1292
|
};
|
|
818
1293
|
|
|
819
1294
|
// src/ContextMenu.tsx
|
|
820
|
-
import {
|
|
1295
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1296
|
+
var DEFAULT_EMPTY_MESSAGE = "No results";
|
|
821
1297
|
var SEARCH_DEBOUNCE_MS = 200;
|
|
822
|
-
var
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
"
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1298
|
+
var textFromNode = (node) => {
|
|
1299
|
+
if (node === null || node === void 0 || typeof node === "boolean")
|
|
1300
|
+
return "";
|
|
1301
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
1302
|
+
if (Array.isArray(node)) return node.map(textFromNode).join(" ");
|
|
1303
|
+
if (isValidElement(node)) return textFromNode(node.props.children);
|
|
1304
|
+
return "";
|
|
1305
|
+
};
|
|
1306
|
+
var filterItems = (items, query) => {
|
|
1307
|
+
const normalized = query.trim().toLowerCase();
|
|
1308
|
+
if (!normalized) return [...items];
|
|
1309
|
+
const result = [];
|
|
1310
|
+
let pendingStructural = [];
|
|
1311
|
+
for (const item of items) {
|
|
1312
|
+
if (item.type === "option") {
|
|
1313
|
+
const label = textFromNode(item.label).toLowerCase();
|
|
1314
|
+
if (label.includes(normalized)) {
|
|
1315
|
+
result.push(...pendingStructural, item);
|
|
1316
|
+
pendingStructural = [];
|
|
1317
|
+
}
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
if (item.type === "heading") {
|
|
1321
|
+
pendingStructural = [item];
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
if (item.type === "divider") {
|
|
1325
|
+
if (result.length > 0) pendingStructural.push(item);
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
835
1328
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1329
|
+
return result;
|
|
1330
|
+
};
|
|
1331
|
+
var isOption = (item) => item.type === "option";
|
|
1332
|
+
var ContextMenuRoot = forwardRef(
|
|
1333
|
+
({
|
|
841
1334
|
children,
|
|
1335
|
+
type = "list",
|
|
1336
|
+
items = [],
|
|
842
1337
|
size = "md",
|
|
843
1338
|
searchable,
|
|
844
1339
|
loading,
|
|
845
|
-
|
|
1340
|
+
isLoading,
|
|
1341
|
+
emptyMessage = DEFAULT_EMPTY_MESSAGE,
|
|
846
1342
|
empty,
|
|
847
|
-
|
|
848
|
-
isOpen,
|
|
1343
|
+
isOpen: propIsOpen,
|
|
849
1344
|
onOpenChange,
|
|
850
|
-
|
|
851
|
-
width,
|
|
852
|
-
maxHeight,
|
|
1345
|
+
trigger,
|
|
853
1346
|
placement = "bottom-start",
|
|
1347
|
+
position,
|
|
1348
|
+
width,
|
|
1349
|
+
maxHeight = 300,
|
|
854
1350
|
onSelect,
|
|
1351
|
+
closeOnSelect,
|
|
855
1352
|
"aria-label": ariaLabel,
|
|
856
|
-
"data-testid": testId,
|
|
857
1353
|
testID,
|
|
1354
|
+
"data-testid": dataTestId,
|
|
858
1355
|
themeMode,
|
|
859
|
-
themeProductContext
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
(
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
1356
|
+
themeProductContext,
|
|
1357
|
+
style
|
|
1358
|
+
}, ref) => {
|
|
1359
|
+
const { theme } = useDesignSystem2();
|
|
1360
|
+
const xuiTheme = theme;
|
|
1361
|
+
const menuId = useId2();
|
|
1362
|
+
const [internalIsOpen, setInternalIsOpen] = useState4(false);
|
|
1363
|
+
const [activeIndex, setActiveIndex] = useState4(-1);
|
|
1364
|
+
const [cellsVersion, setCellsVersion] = useState4(0);
|
|
1365
|
+
const [query, setQuery] = useState4("");
|
|
1366
|
+
const [debouncedQuery, setDebouncedQuery] = useState4("");
|
|
1367
|
+
const containerRef = useRef2(null);
|
|
1368
|
+
const triggerRef = useRef2(null);
|
|
1369
|
+
const panelRef = useRef2(null);
|
|
1370
|
+
const cellsRef = useRef2([]);
|
|
1371
|
+
const isOpen = propIsOpen !== void 0 ? propIsOpen : internalIsOpen;
|
|
1372
|
+
const sizeStyles = xuiTheme.sizing.contextMenu(size);
|
|
1373
|
+
const borderRadius = xuiTheme.shape?.contextMenu?.[size]?.borderRadius ?? xuiTheme.radius?.button ?? 8;
|
|
1374
|
+
const shouldCloseOnSelect = closeOnSelect ?? (type === "checkbox" ? false : true);
|
|
1375
|
+
const positioned = useContextMenuPosition({
|
|
1376
|
+
triggerRef,
|
|
1377
|
+
panelRef,
|
|
1378
|
+
isOpen: isOpen && !!trigger && !position,
|
|
1379
|
+
placement
|
|
1380
|
+
});
|
|
1381
|
+
const setOpen = useCallback3(
|
|
1382
|
+
(nextOpen) => {
|
|
1383
|
+
if (propIsOpen === void 0) setInternalIsOpen(nextOpen);
|
|
1384
|
+
onOpenChange?.(nextOpen);
|
|
1385
|
+
if (!nextOpen) setActiveIndex(-1);
|
|
1386
|
+
},
|
|
1387
|
+
[propIsOpen, onOpenChange]
|
|
1388
|
+
);
|
|
1389
|
+
const closeMenu = useCallback3(() => {
|
|
1390
|
+
setOpen(false);
|
|
1391
|
+
}, [setOpen]);
|
|
1392
|
+
const toggleMenu = useCallback3(() => {
|
|
1393
|
+
setOpen(!isOpen);
|
|
1394
|
+
}, [isOpen, setOpen]);
|
|
1395
|
+
const registerCell = useCallback3((id, meta) => {
|
|
1396
|
+
const existingIndex = cellsRef.current.findIndex(
|
|
1397
|
+
(cell) => cell.id === id
|
|
1398
|
+
);
|
|
1399
|
+
if (existingIndex >= 0) {
|
|
1400
|
+
cellsRef.current[existingIndex] = { id, meta };
|
|
1401
|
+
setCellsVersion((version) => version + 1);
|
|
1402
|
+
return existingIndex;
|
|
1403
|
+
}
|
|
897
1404
|
cellsRef.current.push({ id, meta });
|
|
898
|
-
setCellsVersion((
|
|
1405
|
+
setCellsVersion((version) => version + 1);
|
|
899
1406
|
return cellsRef.current.length - 1;
|
|
900
|
-
}
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const getCellIndex = useCallback2(
|
|
916
|
-
(id) => cellsRef.current.findIndex((c) => c.id === id),
|
|
917
|
-
[]
|
|
918
|
-
);
|
|
919
|
-
const ctx = useMemo(
|
|
920
|
-
() => ({
|
|
921
|
-
size,
|
|
922
|
-
menuId,
|
|
923
|
-
closeMenu,
|
|
924
|
-
registerCell,
|
|
925
|
-
unregisterCell,
|
|
926
|
-
getCellIndex,
|
|
927
|
-
cellsVersion,
|
|
1407
|
+
}, []);
|
|
1408
|
+
const unregisterCell = useCallback3((id) => {
|
|
1409
|
+
const index2 = cellsRef.current.findIndex((cell) => cell.id === id);
|
|
1410
|
+
if (index2 >= 0) {
|
|
1411
|
+
cellsRef.current.splice(index2, 1);
|
|
1412
|
+
setCellsVersion((version) => version + 1);
|
|
1413
|
+
}
|
|
1414
|
+
}, []);
|
|
1415
|
+
const getCellIndex = useCallback3((id) => {
|
|
1416
|
+
return cellsRef.current.findIndex((cell) => cell.id === id);
|
|
1417
|
+
}, []);
|
|
1418
|
+
const cells = useMemo(() => [...cellsRef.current], [cellsVersion]);
|
|
1419
|
+
const keyboard = useKeyboardNavigation({
|
|
1420
|
+
isOpen,
|
|
1421
|
+
cells,
|
|
928
1422
|
activeIndex,
|
|
929
1423
|
setActiveIndex,
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
}),
|
|
933
|
-
[
|
|
934
|
-
size,
|
|
935
|
-
menuId,
|
|
936
|
-
closeMenu,
|
|
937
|
-
registerCell,
|
|
938
|
-
unregisterCell,
|
|
939
|
-
getCellIndex,
|
|
940
|
-
cellsVersion,
|
|
941
|
-
activeIndex,
|
|
942
|
-
query
|
|
943
|
-
]
|
|
944
|
-
);
|
|
945
|
-
const triggerNode = useMemo(() => {
|
|
946
|
-
if (!trigger) return null;
|
|
947
|
-
const inner = isValidElement(trigger) ? cloneElement(
|
|
948
|
-
trigger,
|
|
949
|
-
{
|
|
950
|
-
"aria-haspopup": "menu",
|
|
951
|
-
"aria-expanded": open ? "true" : "false"
|
|
952
|
-
}
|
|
953
|
-
) : trigger;
|
|
954
|
-
return /* @__PURE__ */ jsx2(
|
|
955
|
-
"span",
|
|
956
|
-
{
|
|
957
|
-
ref: (node) => {
|
|
958
|
-
if (!node) {
|
|
959
|
-
triggerRef.current = null;
|
|
960
|
-
return;
|
|
961
|
-
}
|
|
962
|
-
const focusable = node.querySelector(
|
|
963
|
-
"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
|
|
964
|
-
);
|
|
965
|
-
triggerRef.current = focusable ?? node;
|
|
966
|
-
},
|
|
967
|
-
onClick: () => setOpen(!open),
|
|
968
|
-
style: { display: "inline-flex" },
|
|
969
|
-
children: inner
|
|
970
|
-
}
|
|
971
|
-
);
|
|
972
|
-
}, [trigger, open, setOpen]);
|
|
973
|
-
const usePortal = !!trigger && typeof document !== "undefined";
|
|
974
|
-
const position = useContextMenuPosition({
|
|
975
|
-
triggerRef,
|
|
976
|
-
panelRef,
|
|
977
|
-
isOpen: open && usePortal,
|
|
978
|
-
placement
|
|
979
|
-
});
|
|
980
|
-
const cellsForNav = useMemo(
|
|
981
|
-
() => cellsRef.current.map((c) => ({ id: c.id, meta: c.meta })),
|
|
982
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
983
|
-
[cellsVersion]
|
|
984
|
-
);
|
|
985
|
-
const { handleKeyDown } = useKeyboardNavigation({
|
|
986
|
-
isOpen: open,
|
|
987
|
-
cells: cellsForNav,
|
|
988
|
-
activeIndex,
|
|
989
|
-
setActiveIndex,
|
|
990
|
-
onClose: closeMenu,
|
|
991
|
-
triggerRef
|
|
992
|
-
});
|
|
993
|
-
const sizingFn = theme.sizing.contextMenu;
|
|
994
|
-
const sizing = sizingFn ? sizingFn(size) : {};
|
|
995
|
-
const radiusObj = theme.radius;
|
|
996
|
-
const radiusVal = sizing.borderRadius ?? radiusObj?.contextMenu ?? 8;
|
|
997
|
-
const shadowObj = theme.shadow;
|
|
998
|
-
const shadowVal = shadowObj?.popover ?? "";
|
|
999
|
-
const panelPaddingVertical = sizing.paddingVertical ?? 8;
|
|
1000
|
-
const glassBackground = theme.colors.layer?.float ?? theme.colors.background.primary;
|
|
1001
|
-
const panelStyle = {
|
|
1002
|
-
background: glassBackground,
|
|
1003
|
-
backdropFilter: "blur(12px)",
|
|
1004
|
-
WebkitBackdropFilter: "blur(12px)",
|
|
1005
|
-
border: `1px solid ${theme.colors.border.secondary}`,
|
|
1006
|
-
borderRadius: radiusVal,
|
|
1007
|
-
boxShadow: shadowVal,
|
|
1008
|
-
width: width ?? sizing.panelWidth,
|
|
1009
|
-
maxHeight,
|
|
1010
|
-
overflow: "hidden",
|
|
1011
|
-
display: open ? "flex" : "none",
|
|
1012
|
-
flexDirection: "column",
|
|
1013
|
-
outline: "none",
|
|
1014
|
-
fontFamily: theme.fonts.body,
|
|
1015
|
-
paddingTop: panelPaddingVertical,
|
|
1016
|
-
paddingBottom: panelPaddingVertical
|
|
1017
|
-
};
|
|
1018
|
-
if (usePortal) {
|
|
1019
|
-
panelStyle.position = "fixed";
|
|
1020
|
-
panelStyle.top = position?.top ?? 0;
|
|
1021
|
-
panelStyle.left = position?.left ?? 0;
|
|
1022
|
-
}
|
|
1023
|
-
const filteredItems = useMemo(() => {
|
|
1024
|
-
if (!items) return void 0;
|
|
1025
|
-
if (!searchable || !debouncedQuery) return items.slice();
|
|
1026
|
-
const q = debouncedQuery.toLowerCase();
|
|
1027
|
-
const matchedFlags = items.map((item) => {
|
|
1028
|
-
if (item.type === "option") {
|
|
1029
|
-
return String(item.label ?? "").toLowerCase().includes(q);
|
|
1030
|
-
}
|
|
1031
|
-
return false;
|
|
1424
|
+
onClose: closeMenu,
|
|
1425
|
+
triggerRef
|
|
1032
1426
|
});
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (item.type === "heading") {
|
|
1040
|
-
pendingHeading = { item, idx: i };
|
|
1041
|
-
groupHasOption = false;
|
|
1042
|
-
} else if (item.type === "divider") {
|
|
1043
|
-
if (lastEmittedWasContent) {
|
|
1044
|
-
result.push(item);
|
|
1045
|
-
lastEmittedWasContent = false;
|
|
1046
|
-
}
|
|
1047
|
-
pendingHeading = null;
|
|
1048
|
-
groupHasOption = false;
|
|
1049
|
-
} else if (item.type === "option") {
|
|
1050
|
-
if (matchedFlags[i]) {
|
|
1051
|
-
if (pendingHeading) {
|
|
1052
|
-
result.push(pendingHeading.item);
|
|
1053
|
-
pendingHeading = null;
|
|
1054
|
-
}
|
|
1055
|
-
result.push(item);
|
|
1056
|
-
lastEmittedWasContent = true;
|
|
1057
|
-
groupHasOption = true;
|
|
1058
|
-
}
|
|
1427
|
+
useEffect4(() => {
|
|
1428
|
+
if (!isOpen) {
|
|
1429
|
+
cellsRef.current = [];
|
|
1430
|
+
setCellsVersion((version) => version + 1);
|
|
1431
|
+
setQuery("");
|
|
1432
|
+
setDebouncedQuery("");
|
|
1059
1433
|
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
return result;
|
|
1066
|
-
}, [items, searchable, debouncedQuery]);
|
|
1067
|
-
const effectiveCloseOnSelect = closeOnSelect !== void 0 ? closeOnSelect : type !== "checkbox";
|
|
1068
|
-
const renderPresetItem = (item, key) => {
|
|
1069
|
-
if (item.type === "heading") {
|
|
1070
|
-
return /* @__PURE__ */ jsx2(
|
|
1071
|
-
ContextMenuItem,
|
|
1072
|
-
{
|
|
1073
|
-
...item,
|
|
1074
|
-
size: item.size ?? size
|
|
1075
|
-
},
|
|
1076
|
-
`h-${key}`
|
|
1434
|
+
}, [isOpen]);
|
|
1435
|
+
useEffect4(() => {
|
|
1436
|
+
const timer = setTimeout(
|
|
1437
|
+
() => setDebouncedQuery(query),
|
|
1438
|
+
SEARCH_DEBOUNCE_MS
|
|
1077
1439
|
);
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1440
|
+
return () => clearTimeout(timer);
|
|
1441
|
+
}, [query]);
|
|
1442
|
+
useEffect4(() => {
|
|
1443
|
+
if (!isOpen || !trigger) return;
|
|
1444
|
+
const onMouseDown = (event) => {
|
|
1445
|
+
const target = event.target;
|
|
1446
|
+
if (!target || containerRef.current?.contains(target)) return;
|
|
1447
|
+
closeMenu();
|
|
1448
|
+
};
|
|
1449
|
+
const onScroll = () => closeMenu();
|
|
1450
|
+
document.addEventListener("mousedown", onMouseDown);
|
|
1451
|
+
window.addEventListener("scroll", onScroll);
|
|
1452
|
+
return () => {
|
|
1453
|
+
document.removeEventListener("mousedown", onMouseDown);
|
|
1454
|
+
window.removeEventListener("scroll", onScroll);
|
|
1455
|
+
};
|
|
1456
|
+
}, [isOpen, trigger, closeMenu]);
|
|
1457
|
+
useLayoutEffect3(() => {
|
|
1458
|
+
if (!isOpen || !panelRef.current) return;
|
|
1459
|
+
const searchbox = panelRef.current.querySelector('[role="searchbox"]');
|
|
1460
|
+
const firstOption = panelRef.current.querySelector(
|
|
1461
|
+
'[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]'
|
|
1462
|
+
);
|
|
1463
|
+
(searchbox ?? firstOption)?.focus();
|
|
1464
|
+
}, [isOpen]);
|
|
1465
|
+
useLayoutEffect3(() => {
|
|
1466
|
+
if (!isOpen || !panelRef.current) return;
|
|
1467
|
+
const activeElement = document.activeElement;
|
|
1468
|
+
if (activeElement && panelRef.current.contains(activeElement)) return;
|
|
1469
|
+
panelRef.current.focus();
|
|
1470
|
+
}, [isOpen, cellsVersion]);
|
|
1471
|
+
useEffect4(() => {
|
|
1472
|
+
if (isOpen) return;
|
|
1473
|
+
triggerRef.current?.focus();
|
|
1474
|
+
}, [isOpen]);
|
|
1475
|
+
const contextValue = useMemo(
|
|
1476
|
+
() => ({
|
|
1477
|
+
size,
|
|
1478
|
+
menuId,
|
|
1479
|
+
closeMenu,
|
|
1480
|
+
activeIndex,
|
|
1481
|
+
setActiveIndex,
|
|
1482
|
+
registerCell,
|
|
1483
|
+
unregisterCell,
|
|
1484
|
+
getCellIndex,
|
|
1485
|
+
cellsVersion,
|
|
1486
|
+
query,
|
|
1487
|
+
setQuery
|
|
1488
|
+
}),
|
|
1489
|
+
[
|
|
1490
|
+
size,
|
|
1491
|
+
menuId,
|
|
1492
|
+
closeMenu,
|
|
1493
|
+
activeIndex,
|
|
1494
|
+
registerCell,
|
|
1495
|
+
unregisterCell,
|
|
1496
|
+
getCellIndex,
|
|
1497
|
+
cellsVersion,
|
|
1498
|
+
query
|
|
1499
|
+
]
|
|
1500
|
+
);
|
|
1501
|
+
const renderPresetItem = (item, index2) => {
|
|
1502
|
+
if (!isOption(item)) {
|
|
1503
|
+
return /* @__PURE__ */ jsx4(
|
|
1504
|
+
ContextMenuItem,
|
|
1505
|
+
{
|
|
1506
|
+
...item,
|
|
1507
|
+
themeMode,
|
|
1508
|
+
themeProductContext
|
|
1509
|
+
},
|
|
1510
|
+
`context-menu-item-${index2}`
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
const leadingControl = item.leadingControl ?? (type === "checkbox" ? "checkbox" : type === "radio" ? "radio" : void 0);
|
|
1514
|
+
return /* @__PURE__ */ jsx4(
|
|
1081
1515
|
ContextMenuItem,
|
|
1082
1516
|
{
|
|
1083
1517
|
...item,
|
|
1084
|
-
|
|
1518
|
+
leadingControl,
|
|
1519
|
+
themeMode,
|
|
1520
|
+
themeProductContext,
|
|
1521
|
+
onSelect: () => {
|
|
1522
|
+
item.onSelect?.();
|
|
1523
|
+
onSelect?.(item);
|
|
1524
|
+
if (shouldCloseOnSelect) closeMenu();
|
|
1525
|
+
}
|
|
1085
1526
|
},
|
|
1086
|
-
`
|
|
1527
|
+
`context-menu-item-${index2}`
|
|
1087
1528
|
);
|
|
1088
|
-
}
|
|
1089
|
-
const composed = composeItemForPreset(type, item);
|
|
1090
|
-
const originalSelect = composed.onSelect;
|
|
1091
|
-
const wrappedSelect = () => {
|
|
1092
|
-
originalSelect?.();
|
|
1093
|
-
onSelect?.(item);
|
|
1094
|
-
if (effectiveCloseOnSelect) closeMenu();
|
|
1095
1529
|
};
|
|
1096
|
-
|
|
1097
|
-
|
|
1530
|
+
const renderedItems = searchable ? filterItems(items, debouncedQuery) : [...items];
|
|
1531
|
+
const renderContent = () => {
|
|
1532
|
+
if (loading || isLoading || type === "loading") {
|
|
1533
|
+
const brandColor = xuiTheme.colors.control.brand.primary.bg;
|
|
1534
|
+
return /* @__PURE__ */ jsx4(
|
|
1535
|
+
Box,
|
|
1536
|
+
{
|
|
1537
|
+
padding: 16,
|
|
1538
|
+
alignItems: "center",
|
|
1539
|
+
justifyContent: "center",
|
|
1540
|
+
minHeight: 60,
|
|
1541
|
+
children: /* @__PURE__ */ jsx4(Spinner, { size: "md", color: brandColor })
|
|
1542
|
+
}
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
if (children) return children;
|
|
1546
|
+
const content = renderedItems.map(renderPresetItem);
|
|
1547
|
+
if (searchable) {
|
|
1548
|
+
content.unshift(
|
|
1549
|
+
/* @__PURE__ */ jsx4(
|
|
1550
|
+
"div",
|
|
1551
|
+
{
|
|
1552
|
+
"data-sticky": "top",
|
|
1553
|
+
style: {
|
|
1554
|
+
position: "sticky",
|
|
1555
|
+
top: 0,
|
|
1556
|
+
zIndex: 1,
|
|
1557
|
+
backgroundColor: xuiTheme.colors.background.secondary
|
|
1558
|
+
},
|
|
1559
|
+
children: /* @__PURE__ */ jsx4(
|
|
1560
|
+
ContextMenuItem,
|
|
1561
|
+
{
|
|
1562
|
+
type: "search",
|
|
1563
|
+
value: query,
|
|
1564
|
+
onValueChange: setQuery,
|
|
1565
|
+
placeholder: "Search",
|
|
1566
|
+
autoFocus: true,
|
|
1567
|
+
themeMode,
|
|
1568
|
+
themeProductContext
|
|
1569
|
+
}
|
|
1570
|
+
)
|
|
1571
|
+
},
|
|
1572
|
+
"context-menu-search"
|
|
1573
|
+
)
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
if (content.length > (searchable ? 1 : 0)) return content;
|
|
1577
|
+
return empty ?? /* @__PURE__ */ jsx4("div", { style: { padding: 16 }, children: emptyMessage });
|
|
1578
|
+
};
|
|
1579
|
+
const assignPanelRef = (node) => {
|
|
1580
|
+
panelRef.current = node;
|
|
1581
|
+
if (typeof ref === "function") ref(node);
|
|
1582
|
+
else if (ref) ref.current = node;
|
|
1583
|
+
};
|
|
1584
|
+
const assignTriggerRef = (node) => {
|
|
1585
|
+
triggerRef.current = node;
|
|
1586
|
+
};
|
|
1587
|
+
const triggerNode = trigger && isValidElement(trigger) ? cloneElement(trigger, {
|
|
1588
|
+
ref: assignTriggerRef,
|
|
1589
|
+
"aria-haspopup": "menu",
|
|
1590
|
+
"aria-expanded": isOpen ? "true" : "false",
|
|
1591
|
+
onClick: (event) => {
|
|
1592
|
+
trigger.props.onClick?.(event);
|
|
1593
|
+
if (!event.defaultPrevented) toggleMenu();
|
|
1594
|
+
}
|
|
1595
|
+
}) : trigger ? /* @__PURE__ */ jsx4(
|
|
1596
|
+
"span",
|
|
1098
1597
|
{
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1598
|
+
ref: assignTriggerRef,
|
|
1599
|
+
role: "button",
|
|
1600
|
+
tabIndex: 0,
|
|
1601
|
+
"aria-haspopup": "menu",
|
|
1602
|
+
"aria-expanded": isOpen ? "true" : "false",
|
|
1603
|
+
onClick: toggleMenu,
|
|
1604
|
+
children: trigger
|
|
1605
|
+
}
|
|
1606
|
+
) : null;
|
|
1607
|
+
const positionStyle = position ? {
|
|
1608
|
+
position: "fixed",
|
|
1609
|
+
left: position.x,
|
|
1610
|
+
top: position.y
|
|
1611
|
+
} : trigger ? {
|
|
1612
|
+
position: "fixed",
|
|
1613
|
+
left: positioned?.left ?? 0,
|
|
1614
|
+
top: positioned?.top ?? 0
|
|
1615
|
+
} : void 0;
|
|
1616
|
+
return /* @__PURE__ */ jsx4(ContextMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs3(
|
|
1112
1617
|
"div",
|
|
1113
1618
|
{
|
|
1619
|
+
ref: containerRef,
|
|
1114
1620
|
style: {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
justifyContent: "center",
|
|
1118
|
-
padding: 16
|
|
1621
|
+
position: trigger || position ? "relative" : void 0,
|
|
1622
|
+
display: trigger ? "inline-block" : void 0
|
|
1119
1623
|
},
|
|
1120
|
-
children:
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
bodyContent = empty ?? /* @__PURE__ */ jsx2(EmptyMessage, { color: theme.colors.content.tertiary, children: emptyMessage ?? "No results" });
|
|
1154
|
-
}
|
|
1155
|
-
const hasStickySearch = !!searchNode;
|
|
1156
|
-
const prevOpenRef = useRef(false);
|
|
1157
|
-
const skipFocusRestoreRef = useRef(false);
|
|
1158
|
-
useEffect3(() => {
|
|
1159
|
-
const wasOpen = prevOpenRef.current;
|
|
1160
|
-
prevOpenRef.current = open;
|
|
1161
|
-
if (!wasOpen && open) {
|
|
1162
|
-
const timer = setTimeout(() => {
|
|
1163
|
-
const panel2 = panelRef.current;
|
|
1164
|
-
if (!panel2) return;
|
|
1165
|
-
const search = panel2.querySelector("[role='searchbox']");
|
|
1166
|
-
if (search) {
|
|
1167
|
-
search.focus();
|
|
1168
|
-
return;
|
|
1169
|
-
}
|
|
1170
|
-
const firstOption = panel2.querySelector(
|
|
1171
|
-
"[role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio']"
|
|
1172
|
-
);
|
|
1173
|
-
if (firstOption) {
|
|
1174
|
-
firstOption.focus();
|
|
1175
|
-
setActiveIndex(-1);
|
|
1176
|
-
} else {
|
|
1177
|
-
panel2.focus();
|
|
1178
|
-
}
|
|
1179
|
-
}, 0);
|
|
1180
|
-
return () => clearTimeout(timer);
|
|
1181
|
-
}
|
|
1182
|
-
if (wasOpen && !open) {
|
|
1183
|
-
if (!skipFocusRestoreRef.current) {
|
|
1184
|
-
triggerRef.current?.focus();
|
|
1185
|
-
}
|
|
1186
|
-
skipFocusRestoreRef.current = false;
|
|
1187
|
-
}
|
|
1188
|
-
}, [open]);
|
|
1189
|
-
useEffect3(() => {
|
|
1190
|
-
if (!open || !usePortal || typeof document === "undefined") return;
|
|
1191
|
-
const handlePointerDown = (event) => {
|
|
1192
|
-
const target = event.target;
|
|
1193
|
-
if (!target) return;
|
|
1194
|
-
if (panelRef.current?.contains(target)) return;
|
|
1195
|
-
if (triggerRef.current?.contains(target)) return;
|
|
1196
|
-
if (target instanceof Element) {
|
|
1197
|
-
const portals = document.querySelectorAll(
|
|
1198
|
-
"[data-xui-context-menu-portal]"
|
|
1199
|
-
);
|
|
1200
|
-
for (let i = 0; i < portals.length; i += 1) {
|
|
1201
|
-
const portal = portals[i];
|
|
1202
|
-
if (portal.getAttribute("data-xui-context-menu-portal") === menuId && portal.contains(target)) {
|
|
1203
|
-
return;
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1624
|
+
children: [
|
|
1625
|
+
triggerNode,
|
|
1626
|
+
isOpen && /* @__PURE__ */ jsx4(
|
|
1627
|
+
Box,
|
|
1628
|
+
{
|
|
1629
|
+
ref: assignPanelRef,
|
|
1630
|
+
role: "menu",
|
|
1631
|
+
"aria-label": ariaLabel,
|
|
1632
|
+
"data-testid": dataTestId ?? testID ?? "context-menu",
|
|
1633
|
+
"data-placement": positioned?.placement ?? placement,
|
|
1634
|
+
backgroundColor: xuiTheme.colors.background.secondary,
|
|
1635
|
+
borderColor: xuiTheme.colors.border.secondary,
|
|
1636
|
+
borderWidth: 1,
|
|
1637
|
+
borderRadius,
|
|
1638
|
+
paddingVertical: sizeStyles.paddingVertical,
|
|
1639
|
+
width,
|
|
1640
|
+
minWidth: sizeStyles.minWidth,
|
|
1641
|
+
tabIndex: -1,
|
|
1642
|
+
onKeyDown: keyboard.handleKeyDown,
|
|
1643
|
+
onMouseLeave: () => setActiveIndex(-1),
|
|
1644
|
+
style: {
|
|
1645
|
+
...positionStyle,
|
|
1646
|
+
...style,
|
|
1647
|
+
zIndex: 1e3,
|
|
1648
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
1649
|
+
maxHeight,
|
|
1650
|
+
overflowY: "auto",
|
|
1651
|
+
outline: "none"
|
|
1652
|
+
},
|
|
1653
|
+
children: renderContent()
|
|
1654
|
+
}
|
|
1655
|
+
)
|
|
1656
|
+
]
|
|
1206
1657
|
}
|
|
1207
|
-
|
|
1208
|
-
closeMenu();
|
|
1209
|
-
};
|
|
1210
|
-
document.addEventListener("mousedown", handlePointerDown);
|
|
1211
|
-
return () => document.removeEventListener("mousedown", handlePointerDown);
|
|
1212
|
-
}, [open, usePortal, closeMenu, menuId]);
|
|
1213
|
-
const resolvedPlacement = position?.placement ?? placement;
|
|
1214
|
-
const scrollContainerStyle = {
|
|
1215
|
-
overflowY: "auto",
|
|
1216
|
-
flex: 1,
|
|
1217
|
-
minHeight: 0
|
|
1218
|
-
};
|
|
1219
|
-
const stickyHeaderStyle = {
|
|
1220
|
-
position: "sticky",
|
|
1221
|
-
top: 0,
|
|
1222
|
-
zIndex: 1,
|
|
1223
|
-
// Match the glass panel so options scrolling underneath blur instead of
|
|
1224
|
-
// showing through the translucent header.
|
|
1225
|
-
background: glassBackground,
|
|
1226
|
-
backdropFilter: "blur(12px)",
|
|
1227
|
-
WebkitBackdropFilter: "blur(12px)"
|
|
1228
|
-
};
|
|
1229
|
-
const panel = open ? /* @__PURE__ */ jsx2(ContextMenuContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs2(
|
|
1230
|
-
"div",
|
|
1231
|
-
{
|
|
1232
|
-
ref: panelRef,
|
|
1233
|
-
role: "menu",
|
|
1234
|
-
"aria-label": ariaLabel,
|
|
1235
|
-
"data-testid": testId || testID,
|
|
1236
|
-
"data-placement": usePortal ? resolvedPlacement : void 0,
|
|
1237
|
-
tabIndex: -1,
|
|
1238
|
-
onKeyDown: handleKeyDown,
|
|
1239
|
-
onMouseLeave: () => setActiveIndex(-1),
|
|
1240
|
-
style: panelStyle,
|
|
1241
|
-
children: [
|
|
1242
|
-
hasStickySearch && /* @__PURE__ */ jsx2("div", { "data-sticky": "top", style: stickyHeaderStyle, children: searchNode }),
|
|
1243
|
-
/* @__PURE__ */ jsx2("div", { style: scrollContainerStyle, children: bodyContent })
|
|
1244
|
-
]
|
|
1245
|
-
}
|
|
1246
|
-
) }) : null;
|
|
1247
|
-
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1248
|
-
triggerNode,
|
|
1249
|
-
usePortal ? panel && createPortal2(panel, document.body) : panel
|
|
1250
|
-
] });
|
|
1251
|
-
};
|
|
1252
|
-
ContextMenu.displayName = "ContextMenu";
|
|
1253
|
-
function composeItemForPreset(type, item) {
|
|
1254
|
-
switch (type) {
|
|
1255
|
-
case "checkbox":
|
|
1256
|
-
return { ...item, leadingControl: "checkbox" };
|
|
1257
|
-
case "radio":
|
|
1258
|
-
return { ...item, leadingControl: "radio" };
|
|
1259
|
-
case "list":
|
|
1260
|
-
case "phone":
|
|
1261
|
-
case "status":
|
|
1262
|
-
case "brandLogo":
|
|
1263
|
-
case "avatar":
|
|
1264
|
-
default:
|
|
1265
|
-
return { ...item };
|
|
1658
|
+
) });
|
|
1266
1659
|
}
|
|
1267
|
-
|
|
1660
|
+
);
|
|
1661
|
+
ContextMenuRoot.displayName = "ContextMenu";
|
|
1662
|
+
var ContextMenu = Object.assign(ContextMenuRoot, {
|
|
1663
|
+
Item: ContextMenuItem,
|
|
1664
|
+
Submenu: ContextMenuSubmenu
|
|
1665
|
+
});
|
|
1268
1666
|
export {
|
|
1269
1667
|
ContextMenu,
|
|
1270
1668
|
ContextMenuContext,
|
|
1271
1669
|
ContextMenuItem,
|
|
1670
|
+
ContextMenuSubmenu,
|
|
1272
1671
|
useContextMenu,
|
|
1273
1672
|
useContextMenuPosition,
|
|
1274
1673
|
useContextMenuRequired,
|