@xsolla/xui-context-menu 0.64.0-pr81.1768677662

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/web/index.mjs ADDED
@@ -0,0 +1,1854 @@
1
+ // src/ContextMenu.tsx
2
+ import React10, {
3
+ forwardRef as forwardRef9,
4
+ useState,
5
+ useRef as useRef5,
6
+ useEffect as useEffect5,
7
+ useCallback,
8
+ useMemo
9
+ } from "react";
10
+
11
+ // ../primitives-web/src/Box.tsx
12
+ import React from "react";
13
+ import styled from "styled-components";
14
+ import { jsx } from "react/jsx-runtime";
15
+ var StyledBox = styled.div`
16
+ display: flex;
17
+ box-sizing: border-box;
18
+ background-color: ${(props) => props.backgroundColor || "transparent"};
19
+ border-color: ${(props) => props.borderColor || "transparent"};
20
+ border-width: ${(props) => typeof props.borderWidth === "number" ? `${props.borderWidth}px` : props.borderWidth || 0};
21
+
22
+ ${(props) => props.borderBottomWidth !== void 0 && `
23
+ border-bottom-width: ${typeof props.borderBottomWidth === "number" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};
24
+ border-bottom-color: ${props.borderBottomColor || props.borderColor || "transparent"};
25
+ border-bottom-style: solid;
26
+ `}
27
+ ${(props) => props.borderTopWidth !== void 0 && `
28
+ border-top-width: ${typeof props.borderTopWidth === "number" ? `${props.borderTopWidth}px` : props.borderTopWidth};
29
+ border-top-color: ${props.borderTopColor || props.borderColor || "transparent"};
30
+ border-top-style: solid;
31
+ `}
32
+ ${(props) => props.borderLeftWidth !== void 0 && `
33
+ border-left-width: ${typeof props.borderLeftWidth === "number" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};
34
+ border-left-color: ${props.borderLeftColor || props.borderColor || "transparent"};
35
+ border-left-style: solid;
36
+ `}
37
+ ${(props) => props.borderRightWidth !== void 0 && `
38
+ border-right-width: ${typeof props.borderRightWidth === "number" ? `${props.borderRightWidth}px` : props.borderRightWidth};
39
+ border-right-color: ${props.borderRightColor || props.borderColor || "transparent"};
40
+ border-right-style: solid;
41
+ `}
42
+
43
+ border-style: ${(props) => props.borderStyle || (props.borderWidth || props.borderBottomWidth || props.borderTopWidth || props.borderLeftWidth || props.borderRightWidth ? "solid" : "none")};
44
+ border-radius: ${(props) => typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius || 0};
45
+ height: ${(props) => typeof props.height === "number" ? `${props.height}px` : props.height || "auto"};
46
+ width: ${(props) => typeof props.width === "number" ? `${props.width}px` : props.width || "auto"};
47
+ min-width: ${(props) => typeof props.minWidth === "number" ? `${props.minWidth}px` : props.minWidth || "auto"};
48
+ min-height: ${(props) => typeof props.minHeight === "number" ? `${props.minHeight}px` : props.minHeight || "auto"};
49
+
50
+ padding: ${(props) => typeof props.padding === "number" ? `${props.padding}px` : props.padding || 0};
51
+ ${(props) => props.paddingHorizontal && `
52
+ padding-left: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
53
+ padding-right: ${typeof props.paddingHorizontal === "number" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};
54
+ `}
55
+ ${(props) => props.paddingVertical && `
56
+ padding-top: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
57
+ padding-bottom: ${typeof props.paddingVertical === "number" ? `${props.paddingVertical}px` : props.paddingVertical};
58
+ `}
59
+ ${(props) => props.paddingTop !== void 0 && `padding-top: ${typeof props.paddingTop === "number" ? `${props.paddingTop}px` : props.paddingTop};`}
60
+ ${(props) => props.paddingBottom !== void 0 && `padding-bottom: ${typeof props.paddingBottom === "number" ? `${props.paddingBottom}px` : props.paddingBottom};`}
61
+ ${(props) => props.paddingLeft !== void 0 && `padding-left: ${typeof props.paddingLeft === "number" ? `${props.paddingLeft}px` : props.paddingLeft};`}
62
+ ${(props) => props.paddingRight !== void 0 && `padding-right: ${typeof props.paddingRight === "number" ? `${props.paddingRight}px` : props.paddingRight};`}
63
+
64
+ margin: ${(props) => typeof props.margin === "number" ? `${props.margin}px` : props.margin || 0};
65
+ ${(props) => props.marginTop !== void 0 && `margin-top: ${typeof props.marginTop === "number" ? `${props.marginTop}px` : props.marginTop};`}
66
+ ${(props) => props.marginBottom !== void 0 && `margin-bottom: ${typeof props.marginBottom === "number" ? `${props.marginBottom}px` : props.marginBottom};`}
67
+ ${(props) => props.marginLeft !== void 0 && `margin-left: ${typeof props.marginLeft === "number" ? `${props.marginLeft}px` : props.marginLeft};`}
68
+ ${(props) => props.marginRight !== void 0 && `margin-right: ${typeof props.marginRight === "number" ? `${props.marginRight}px` : props.marginRight};`}
69
+
70
+ flex-direction: ${(props) => props.flexDirection || "column"};
71
+ flex-wrap: ${(props) => props.flexWrap || "nowrap"};
72
+ align-items: ${(props) => props.alignItems || "stretch"};
73
+ justify-content: ${(props) => props.justifyContent || "flex-start"};
74
+ cursor: ${(props) => props.cursor ? props.cursor : props.onClick || props.onPress ? "pointer" : "inherit"};
75
+ position: ${(props) => props.position || "static"};
76
+ top: ${(props) => typeof props.top === "number" ? `${props.top}px` : props.top};
77
+ bottom: ${(props) => typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom};
78
+ left: ${(props) => typeof props.left === "number" ? `${props.left}px` : props.left};
79
+ right: ${(props) => typeof props.right === "number" ? `${props.right}px` : props.right};
80
+ flex: ${(props) => props.flex};
81
+ flex-shrink: ${(props) => props.flexShrink ?? 1};
82
+ gap: ${(props) => typeof props.gap === "number" ? `${props.gap}px` : props.gap || 0};
83
+ align-self: ${(props) => props.alignSelf || "auto"};
84
+ overflow: ${(props) => props.overflow || "visible"};
85
+ overflow-x: ${(props) => props.overflowX || "visible"};
86
+ overflow-y: ${(props) => props.overflowY || "visible"};
87
+ z-index: ${(props) => props.zIndex};
88
+ opacity: ${(props) => props.disabled ? 0.5 : 1};
89
+ pointer-events: ${(props) => props.disabled ? "none" : "auto"};
90
+
91
+ &:hover {
92
+ ${(props) => props.hoverStyle?.backgroundColor && `background-color: ${props.hoverStyle.backgroundColor};`}
93
+ ${(props) => props.hoverStyle?.borderColor && `border-color: ${props.hoverStyle.borderColor};`}
94
+ }
95
+
96
+ &:active {
97
+ ${(props) => props.pressStyle?.backgroundColor && `background-color: ${props.pressStyle.backgroundColor};`}
98
+ }
99
+ `;
100
+ var Box = React.forwardRef(
101
+ ({
102
+ children,
103
+ onPress,
104
+ onKeyDown,
105
+ onKeyUp,
106
+ role,
107
+ "aria-label": ariaLabel,
108
+ "aria-labelledby": ariaLabelledBy,
109
+ "aria-current": ariaCurrent,
110
+ "aria-disabled": ariaDisabled,
111
+ "aria-live": ariaLive,
112
+ "aria-busy": ariaBusy,
113
+ "aria-describedby": ariaDescribedBy,
114
+ "aria-expanded": ariaExpanded,
115
+ "aria-haspopup": ariaHasPopup,
116
+ "aria-pressed": ariaPressed,
117
+ "aria-controls": ariaControls,
118
+ tabIndex,
119
+ as,
120
+ src,
121
+ alt,
122
+ type,
123
+ disabled,
124
+ id,
125
+ ...props
126
+ }, ref) => {
127
+ if (as === "img" && src) {
128
+ return /* @__PURE__ */ jsx(
129
+ "img",
130
+ {
131
+ src,
132
+ alt: alt || "",
133
+ style: {
134
+ display: "block",
135
+ objectFit: "cover",
136
+ width: typeof props.width === "number" ? `${props.width}px` : props.width,
137
+ height: typeof props.height === "number" ? `${props.height}px` : props.height,
138
+ borderRadius: typeof props.borderRadius === "number" ? `${props.borderRadius}px` : props.borderRadius,
139
+ position: props.position,
140
+ top: typeof props.top === "number" ? `${props.top}px` : props.top,
141
+ left: typeof props.left === "number" ? `${props.left}px` : props.left,
142
+ right: typeof props.right === "number" ? `${props.right}px` : props.right,
143
+ bottom: typeof props.bottom === "number" ? `${props.bottom}px` : props.bottom
144
+ }
145
+ }
146
+ );
147
+ }
148
+ return /* @__PURE__ */ jsx(
149
+ StyledBox,
150
+ {
151
+ ref,
152
+ as,
153
+ id,
154
+ type: as === "button" ? type || "button" : void 0,
155
+ disabled: as === "button" ? disabled : void 0,
156
+ onClick: onPress,
157
+ onKeyDown,
158
+ onKeyUp,
159
+ role,
160
+ "aria-label": ariaLabel,
161
+ "aria-labelledby": ariaLabelledBy,
162
+ "aria-current": ariaCurrent,
163
+ "aria-disabled": ariaDisabled,
164
+ "aria-busy": ariaBusy,
165
+ "aria-describedby": ariaDescribedBy,
166
+ "aria-expanded": ariaExpanded,
167
+ "aria-haspopup": ariaHasPopup,
168
+ "aria-pressed": ariaPressed,
169
+ "aria-controls": ariaControls,
170
+ "aria-live": ariaLive,
171
+ tabIndex: tabIndex !== void 0 ? tabIndex : void 0,
172
+ ...props,
173
+ children
174
+ }
175
+ );
176
+ }
177
+ );
178
+ Box.displayName = "Box";
179
+
180
+ // ../primitives-web/src/Text.tsx
181
+ import styled2 from "styled-components";
182
+ import { jsx as jsx2 } from "react/jsx-runtime";
183
+ var StyledText = styled2.span`
184
+ color: ${(props) => props.color || "inherit"};
185
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
186
+ font-weight: ${(props) => props.fontWeight || "normal"};
187
+ font-family: ${(props) => props.fontFamily || '"Pilat Wide Bold", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important'};
188
+ line-height: ${(props) => typeof props.lineHeight === "number" ? `${props.lineHeight}px` : props.lineHeight || "inherit"};
189
+ white-space: ${(props) => props.whiteSpace || "normal"};
190
+ text-align: ${(props) => props.textAlign || "inherit"};
191
+ text-decoration: ${(props) => props.textDecoration || "none"};
192
+ `;
193
+ var Text = ({
194
+ style,
195
+ className,
196
+ id,
197
+ role,
198
+ ...props
199
+ }) => {
200
+ return /* @__PURE__ */ jsx2(
201
+ StyledText,
202
+ {
203
+ ...props,
204
+ style,
205
+ className,
206
+ id,
207
+ role
208
+ }
209
+ );
210
+ };
211
+
212
+ // ../primitives-web/src/Spinner.tsx
213
+ import styled3, { keyframes } from "styled-components";
214
+ import { jsx as jsx3 } from "react/jsx-runtime";
215
+ var rotate = keyframes`
216
+ from {
217
+ transform: rotate(0deg);
218
+ }
219
+ to {
220
+ transform: rotate(360deg);
221
+ }
222
+ `;
223
+ var StyledSpinner = styled3.div`
224
+ width: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
225
+ height: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
226
+ border: ${(props) => props.strokeWidth || 2}px solid
227
+ ${(props) => props.color || "currentColor"};
228
+ border-bottom-color: transparent;
229
+ border-radius: 50%;
230
+ display: inline-block;
231
+ box-sizing: border-box;
232
+ animation: ${rotate} 1s linear infinite;
233
+ `;
234
+ var Spinner = ({
235
+ role = "status",
236
+ "aria-label": ariaLabel,
237
+ "aria-live": ariaLive = "polite",
238
+ "aria-describedby": ariaDescribedBy,
239
+ testID,
240
+ ...props
241
+ }) => {
242
+ return /* @__PURE__ */ jsx3(
243
+ StyledSpinner,
244
+ {
245
+ role,
246
+ "aria-label": ariaLabel,
247
+ "aria-live": ariaLive,
248
+ "aria-describedby": ariaDescribedBy,
249
+ "data-testid": testID,
250
+ ...props
251
+ }
252
+ );
253
+ };
254
+ Spinner.displayName = "Spinner";
255
+
256
+ // ../primitives-web/src/Icon.tsx
257
+ import styled4 from "styled-components";
258
+ import { jsx as jsx4 } from "react/jsx-runtime";
259
+ var StyledIcon = styled4.div`
260
+ display: flex;
261
+ align-items: center;
262
+ justify-content: center;
263
+ width: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
264
+ height: ${(props) => typeof props.size === "number" ? `${props.size}px` : props.size || "24px"};
265
+ color: ${(props) => props.color || "currentColor"};
266
+
267
+ svg {
268
+ width: 100%;
269
+ height: 100%;
270
+ fill: none;
271
+ stroke: currentColor;
272
+ }
273
+ `;
274
+ var Icon = ({ children, ...props }) => {
275
+ return /* @__PURE__ */ jsx4(StyledIcon, { ...props, children });
276
+ };
277
+
278
+ // ../primitives-web/src/Divider.tsx
279
+ import styled5 from "styled-components";
280
+ import { jsx as jsx5 } from "react/jsx-runtime";
281
+ var StyledDivider = styled5.div`
282
+ background-color: ${(props) => props.dashStroke ? "transparent" : props.color || "rgba(255, 255, 255, 0.15)"};
283
+ width: ${(props) => props.vertical ? typeof props.width === "number" ? `${props.width}px` : props.width || "1px" : "100%"};
284
+ height: ${(props) => props.vertical ? "100%" : typeof props.height === "number" ? `${props.height}px` : props.height || "1px"};
285
+
286
+ ${(props) => props.dashStroke && `
287
+ border-style: dashed;
288
+ border-color: ${props.color || "rgba(255, 255, 255, 0.15)"};
289
+ border-width: 0;
290
+ ${props.vertical ? `
291
+ border-left-width: ${typeof props.width === "number" ? `${props.width}px` : props.width || "1px"};
292
+ height: 100%;
293
+ ` : `
294
+ border-top-width: ${typeof props.height === "number" ? `${props.height}px` : props.height || "1px"};
295
+ width: 100%;
296
+ `}
297
+ `}
298
+ `;
299
+
300
+ // ../primitives-web/src/Input.tsx
301
+ import { forwardRef } from "react";
302
+ import styled6 from "styled-components";
303
+ import { jsx as jsx6 } from "react/jsx-runtime";
304
+ var StyledInput = styled6.input`
305
+ background: transparent;
306
+ border: none;
307
+ outline: none;
308
+ width: 100%;
309
+ height: 100%;
310
+ padding: 0;
311
+ margin: 0;
312
+ color: ${(props) => props.color || "inherit"};
313
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
314
+ font-family: inherit;
315
+ text-align: inherit;
316
+
317
+ &::placeholder {
318
+ color: ${(props) => props.placeholderTextColor || "rgba(255, 255, 255, 0.5)"};
319
+ }
320
+
321
+ &:disabled {
322
+ cursor: not-allowed;
323
+ }
324
+ `;
325
+ var InputPrimitive = forwardRef(
326
+ ({
327
+ value,
328
+ placeholder,
329
+ onChange,
330
+ onChangeText,
331
+ onFocus,
332
+ onBlur,
333
+ onKeyDown,
334
+ disabled,
335
+ secureTextEntry,
336
+ style,
337
+ color,
338
+ fontSize,
339
+ placeholderTextColor,
340
+ maxLength,
341
+ name,
342
+ type,
343
+ inputMode,
344
+ autoComplete,
345
+ id,
346
+ "aria-invalid": ariaInvalid,
347
+ "aria-describedby": ariaDescribedBy,
348
+ "aria-labelledby": ariaLabelledBy,
349
+ "aria-label": ariaLabel,
350
+ "aria-disabled": ariaDisabled,
351
+ "data-testid": dataTestId,
352
+ ...rest
353
+ }, ref) => {
354
+ const handleChange = (e) => {
355
+ if (onChange) {
356
+ onChange(e);
357
+ }
358
+ if (onChangeText) {
359
+ onChangeText(e.target.value);
360
+ }
361
+ };
362
+ const inputValue = value !== void 0 ? value : "";
363
+ return /* @__PURE__ */ jsx6(
364
+ StyledInput,
365
+ {
366
+ ref,
367
+ id,
368
+ value: inputValue,
369
+ name,
370
+ placeholder,
371
+ onChange: handleChange,
372
+ onFocus,
373
+ onBlur,
374
+ onKeyDown,
375
+ disabled,
376
+ type: secureTextEntry ? "password" : type || "text",
377
+ inputMode,
378
+ autoComplete,
379
+ style,
380
+ color,
381
+ fontSize,
382
+ placeholderTextColor,
383
+ maxLength,
384
+ "aria-invalid": ariaInvalid,
385
+ "aria-describedby": ariaDescribedBy,
386
+ "aria-labelledby": ariaLabelledBy,
387
+ "aria-label": ariaLabel,
388
+ "aria-disabled": ariaDisabled,
389
+ "data-testid": dataTestId,
390
+ ...rest
391
+ }
392
+ );
393
+ }
394
+ );
395
+ InputPrimitive.displayName = "InputPrimitive";
396
+
397
+ // ../primitives-web/src/TextArea.tsx
398
+ import { forwardRef as forwardRef2 } from "react";
399
+ import styled7 from "styled-components";
400
+ import { jsx as jsx7 } from "react/jsx-runtime";
401
+ var StyledTextArea = styled7.textarea`
402
+ background: transparent;
403
+ border: none;
404
+ outline: none;
405
+ width: 100%;
406
+ height: 100%;
407
+ padding: 0;
408
+ margin: 0;
409
+ color: ${(props) => props.color || "inherit"};
410
+ font-size: ${(props) => typeof props.fontSize === "number" ? `${props.fontSize}px` : props.fontSize || "inherit"};
411
+ font-family: inherit;
412
+ text-align: inherit;
413
+ resize: none;
414
+
415
+ &::placeholder {
416
+ color: ${(props) => props.placeholderTextColor || "rgba(255, 255, 255, 0.5)"};
417
+ }
418
+
419
+ &:disabled {
420
+ cursor: not-allowed;
421
+ }
422
+ `;
423
+ var TextAreaPrimitive = forwardRef2(
424
+ ({
425
+ value,
426
+ placeholder,
427
+ onChangeText,
428
+ onFocus,
429
+ onBlur,
430
+ onKeyDown,
431
+ disabled,
432
+ style,
433
+ color,
434
+ fontSize,
435
+ placeholderTextColor,
436
+ maxLength,
437
+ rows
438
+ }, ref) => {
439
+ return /* @__PURE__ */ jsx7(
440
+ StyledTextArea,
441
+ {
442
+ ref,
443
+ value,
444
+ placeholder,
445
+ onChange: (e) => onChangeText?.(e.target.value),
446
+ onFocus,
447
+ onBlur,
448
+ onKeyDown,
449
+ disabled,
450
+ style,
451
+ color,
452
+ fontSize,
453
+ placeholderTextColor,
454
+ maxLength,
455
+ rows
456
+ }
457
+ );
458
+ }
459
+ );
460
+ TextAreaPrimitive.displayName = "TextAreaPrimitive";
461
+
462
+ // src/ContextMenu.tsx
463
+ import { useDesignSystem as useDesignSystem7 } from "@xsolla/xui-core";
464
+ import { Spinner as Spinner2 } from "@xsolla/xui-spinner";
465
+
466
+ // src/ContextMenuContext.tsx
467
+ import { createContext, useContext } from "react";
468
+ var ContextMenuContext = createContext(void 0);
469
+ var useContextMenu = () => {
470
+ const context = useContext(ContextMenuContext);
471
+ return context;
472
+ };
473
+ var useContextMenuRequired = () => {
474
+ const context = useContext(ContextMenuContext);
475
+ if (!context) {
476
+ throw new Error(
477
+ "useContextMenuRequired must be used within a ContextMenu component"
478
+ );
479
+ }
480
+ return context;
481
+ };
482
+
483
+ // src/ContextMenuItem.tsx
484
+ import { forwardRef as forwardRef3, useEffect, useRef } from "react";
485
+ import { useDesignSystem } from "@xsolla/xui-core";
486
+ import { ArrowRight } from "@xsolla/xui-icons";
487
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
488
+ var ContextMenuItem = forwardRef3(
489
+ ({
490
+ children,
491
+ description,
492
+ icon,
493
+ trailing,
494
+ shortcut,
495
+ selected,
496
+ disabled,
497
+ active,
498
+ size: propSize,
499
+ hasSubmenu,
500
+ onPress,
501
+ "data-testid": testId = "context-menu-item"
502
+ }, ref) => {
503
+ const { theme } = useDesignSystem();
504
+ const context = useContextMenu();
505
+ const size = propSize || context?.size || "m";
506
+ const sizeStyles = theme.sizing.contextMenu(size);
507
+ const itemRef = useRef(null);
508
+ const brandColors = theme.colors.control.brand.primary;
509
+ const contentColors = theme.colors.content;
510
+ useEffect(() => {
511
+ if (context && !disabled) {
512
+ const id = typeof children === "string" ? children : String(Math.random());
513
+ context.registerItem(id);
514
+ return () => context.unregisterItem(id);
515
+ }
516
+ }, [context, disabled, children]);
517
+ const handlePress = () => {
518
+ if (disabled) return;
519
+ onPress?.();
520
+ if (context) {
521
+ context.onItemSelect({
522
+ id: typeof children === "string" ? children : "",
523
+ label: typeof children === "string" ? children : "",
524
+ variant: "default",
525
+ selected
526
+ });
527
+ }
528
+ };
529
+ const handleKeyDown = (event) => {
530
+ if (disabled) return;
531
+ if (event.key === "Enter" || event.key === " ") {
532
+ event.preventDefault();
533
+ handlePress();
534
+ }
535
+ };
536
+ const getBackgroundColor = () => {
537
+ if (selected) {
538
+ return brandColors.bg;
539
+ }
540
+ if (active) {
541
+ return theme.colors.control.input.bgHover;
542
+ }
543
+ return "transparent";
544
+ };
545
+ const getTextColor = () => {
546
+ if (disabled) {
547
+ return theme.colors.control.input.textDisable;
548
+ }
549
+ if (selected) {
550
+ return contentColors.on.brand;
551
+ }
552
+ return theme.colors.content.primary;
553
+ };
554
+ const getIconColor = () => {
555
+ if (disabled) {
556
+ return theme.colors.content.tertiary;
557
+ }
558
+ if (selected) {
559
+ return contentColors.on.brand;
560
+ }
561
+ return theme.colors.content.secondary;
562
+ };
563
+ return /* @__PURE__ */ jsxs(
564
+ Box,
565
+ {
566
+ ref: ref || itemRef,
567
+ role: "menuitem",
568
+ "aria-disabled": disabled || void 0,
569
+ "aria-selected": selected || void 0,
570
+ tabIndex: disabled ? -1 : 0,
571
+ flexDirection: "row",
572
+ alignItems: description ? "flex-start" : "center",
573
+ gap: sizeStyles.gap,
574
+ paddingHorizontal: sizeStyles.itemPaddingHorizontal,
575
+ paddingVertical: sizeStyles.itemPaddingVertical,
576
+ backgroundColor: getBackgroundColor(),
577
+ hoverStyle: !disabled && !selected ? {
578
+ backgroundColor: theme.colors.control.input.bgHover
579
+ } : void 0,
580
+ pressStyle: !disabled ? {
581
+ backgroundColor: selected ? brandColors.bgPress : theme.colors.control.input.bgDisable
582
+ } : void 0,
583
+ onPress: handlePress,
584
+ onKeyDown: handleKeyDown,
585
+ style: {
586
+ cursor: disabled ? "not-allowed" : "pointer",
587
+ opacity: disabled ? 0.5 : 1,
588
+ outline: "none"
589
+ },
590
+ "data-testid": testId,
591
+ children: [
592
+ icon && /* @__PURE__ */ jsx8(
593
+ Box,
594
+ {
595
+ width: sizeStyles.iconSize,
596
+ marginTop: description ? 2 : 0,
597
+ alignItems: "center",
598
+ justifyContent: "center",
599
+ children: /* @__PURE__ */ jsx8(Icon, { size: sizeStyles.iconSize, color: getIconColor(), children: icon })
600
+ }
601
+ ),
602
+ /* @__PURE__ */ jsxs(Box, { flex: 1, flexDirection: "column", gap: 2, children: [
603
+ /* @__PURE__ */ jsx8(
604
+ Text,
605
+ {
606
+ color: getTextColor(),
607
+ fontSize: sizeStyles.fontSize,
608
+ fontWeight: "400",
609
+ lineHeight: sizeStyles.fontSize + 2,
610
+ children
611
+ }
612
+ ),
613
+ description && /* @__PURE__ */ jsx8(
614
+ Text,
615
+ {
616
+ color: selected ? contentColors.on.brand : theme.colors.content.tertiary,
617
+ fontSize: sizeStyles.descriptionFontSize,
618
+ lineHeight: sizeStyles.descriptionFontSize + 2,
619
+ children: description
620
+ }
621
+ )
622
+ ] }),
623
+ shortcut && /* @__PURE__ */ jsx8(
624
+ Text,
625
+ {
626
+ color: selected ? contentColors.on.brand : theme.colors.content.tertiary,
627
+ fontSize: sizeStyles.descriptionFontSize,
628
+ children: shortcut
629
+ }
630
+ ),
631
+ trailing,
632
+ hasSubmenu && /* @__PURE__ */ jsx8(
633
+ Icon,
634
+ {
635
+ size: sizeStyles.iconSize - 4,
636
+ color: selected ? contentColors.on.brand : theme.colors.content.secondary,
637
+ children: /* @__PURE__ */ jsx8(ArrowRight, {})
638
+ }
639
+ )
640
+ ]
641
+ }
642
+ );
643
+ }
644
+ );
645
+ ContextMenuItem.displayName = "ContextMenuItem";
646
+
647
+ // src/ContextMenuCheckboxItem.tsx
648
+ import { forwardRef as forwardRef4, useEffect as useEffect2, useRef as useRef2 } from "react";
649
+ import { useDesignSystem as useDesignSystem2 } from "@xsolla/xui-core";
650
+ import { Check, Minus } from "@xsolla/xui-icons";
651
+ import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
652
+ var checkboxSizeMap = {
653
+ s: 14,
654
+ m: 16,
655
+ l: 18,
656
+ xl: 20
657
+ };
658
+ var checkIconSizeMap = {
659
+ s: 10,
660
+ m: 12,
661
+ l: 14,
662
+ xl: 16
663
+ };
664
+ var CheckboxIndicator = ({
665
+ checked,
666
+ indeterminate,
667
+ size,
668
+ disabled
669
+ }) => {
670
+ const { theme } = useDesignSystem2();
671
+ const brandColors = theme.colors.control.brand.primary;
672
+ const faintColors = theme.colors.control.faint;
673
+ const contentColors = theme.colors.content;
674
+ const boxSize = checkboxSizeMap[size];
675
+ const iconSize = checkIconSizeMap[size];
676
+ const isActive = checked || indeterminate;
677
+ const getBgColor = () => {
678
+ if (disabled) {
679
+ return brandColors.bgDisable;
680
+ }
681
+ if (isActive) {
682
+ return brandColors.bg;
683
+ }
684
+ return faintColors.bg;
685
+ };
686
+ const getBorderColor = () => {
687
+ if (isActive && !disabled) {
688
+ return brandColors.bg;
689
+ }
690
+ return faintColors.border;
691
+ };
692
+ return /* @__PURE__ */ jsx9(
693
+ Box,
694
+ {
695
+ width: boxSize,
696
+ height: boxSize,
697
+ borderRadius: 4,
698
+ borderWidth: isActive ? 0 : 1,
699
+ borderColor: getBorderColor(),
700
+ backgroundColor: getBgColor(),
701
+ alignItems: "center",
702
+ justifyContent: "center",
703
+ children: indeterminate ? /* @__PURE__ */ jsx9(
704
+ Icon,
705
+ {
706
+ size: iconSize,
707
+ color: disabled ? contentColors.tertiary : contentColors.on.brand,
708
+ children: /* @__PURE__ */ jsx9(Minus, {})
709
+ }
710
+ ) : checked ? /* @__PURE__ */ jsx9(
711
+ Icon,
712
+ {
713
+ size: iconSize,
714
+ color: disabled ? contentColors.tertiary : contentColors.on.brand,
715
+ children: /* @__PURE__ */ jsx9(Check, {})
716
+ }
717
+ ) : null
718
+ }
719
+ );
720
+ };
721
+ var ContextMenuCheckboxItem = forwardRef4(
722
+ ({
723
+ children,
724
+ description,
725
+ leadingContent,
726
+ trailing,
727
+ checked = false,
728
+ indeterminate = false,
729
+ disabled,
730
+ size: propSize,
731
+ onCheckedChange,
732
+ onPress,
733
+ "data-testid": testId = "context-menu-checkbox-item"
734
+ }, ref) => {
735
+ const { theme } = useDesignSystem2();
736
+ const context = useContextMenu();
737
+ const size = propSize || context?.size || "m";
738
+ const sizeStyles = theme.sizing.contextMenu(size);
739
+ const itemRef = useRef2(null);
740
+ const contentColors = theme.colors.content;
741
+ useEffect2(() => {
742
+ if (context && !disabled) {
743
+ const id = typeof children === "string" ? children : String(Math.random());
744
+ context.registerItem(id);
745
+ return () => context.unregisterItem(id);
746
+ }
747
+ }, [context, disabled, children]);
748
+ const handlePress = () => {
749
+ if (disabled) return;
750
+ const newChecked = !checked;
751
+ onCheckedChange?.(newChecked);
752
+ onPress?.();
753
+ if (context) {
754
+ context.onItemSelect({
755
+ id: typeof children === "string" ? children : "",
756
+ label: typeof children === "string" ? children : "",
757
+ variant: "checkbox",
758
+ checked: newChecked
759
+ });
760
+ }
761
+ };
762
+ const handleKeyDown = (event) => {
763
+ if (disabled) return;
764
+ if (event.key === "Enter" || event.key === " ") {
765
+ event.preventDefault();
766
+ handlePress();
767
+ }
768
+ };
769
+ const getTextColor = () => {
770
+ if (disabled) {
771
+ return theme.colors.control.input.textDisable;
772
+ }
773
+ return theme.colors.control.input.text;
774
+ };
775
+ return /* @__PURE__ */ jsxs2(
776
+ Box,
777
+ {
778
+ ref: ref || itemRef,
779
+ role: "menuitemcheckbox",
780
+ "aria-checked": indeterminate ? "mixed" : checked,
781
+ "aria-disabled": disabled || void 0,
782
+ tabIndex: disabled ? -1 : 0,
783
+ flexDirection: "row",
784
+ alignItems: description ? "flex-start" : "center",
785
+ gap: sizeStyles.gap,
786
+ paddingHorizontal: sizeStyles.itemPaddingHorizontal,
787
+ paddingVertical: sizeStyles.itemPaddingVertical,
788
+ backgroundColor: "transparent",
789
+ hoverStyle: !disabled ? {
790
+ backgroundColor: theme.colors.control.input.bgHover
791
+ } : void 0,
792
+ pressStyle: !disabled ? {
793
+ backgroundColor: theme.colors.control.input.bgDisable
794
+ } : void 0,
795
+ onPress: handlePress,
796
+ onKeyDown: handleKeyDown,
797
+ style: {
798
+ cursor: disabled ? "not-allowed" : "pointer",
799
+ opacity: disabled ? 0.5 : 1,
800
+ outline: "none"
801
+ },
802
+ "data-testid": testId,
803
+ children: [
804
+ /* @__PURE__ */ jsx9(
805
+ Box,
806
+ {
807
+ marginTop: description ? 2 : 0,
808
+ alignItems: "center",
809
+ justifyContent: "center",
810
+ children: /* @__PURE__ */ jsx9(
811
+ CheckboxIndicator,
812
+ {
813
+ checked,
814
+ indeterminate,
815
+ size,
816
+ disabled
817
+ }
818
+ )
819
+ }
820
+ ),
821
+ leadingContent && /* @__PURE__ */ jsx9(Box, { padding: 5, alignItems: "center", justifyContent: "center", children: leadingContent }),
822
+ /* @__PURE__ */ jsxs2(Box, { flex: 1, flexDirection: "column", gap: 2, children: [
823
+ /* @__PURE__ */ jsx9(
824
+ Text,
825
+ {
826
+ color: getTextColor(),
827
+ fontSize: sizeStyles.fontSize,
828
+ fontWeight: "400",
829
+ lineHeight: sizeStyles.fontSize + 2,
830
+ children
831
+ }
832
+ ),
833
+ description && /* @__PURE__ */ jsx9(
834
+ Text,
835
+ {
836
+ color: theme.colors.content.tertiary,
837
+ fontSize: sizeStyles.descriptionFontSize,
838
+ lineHeight: sizeStyles.descriptionFontSize + 2,
839
+ children: description
840
+ }
841
+ )
842
+ ] }),
843
+ trailing
844
+ ]
845
+ }
846
+ );
847
+ }
848
+ );
849
+ ContextMenuCheckboxItem.displayName = "ContextMenuCheckboxItem";
850
+
851
+ // src/ContextMenuRadioGroup.tsx
852
+ import { createContext as createContext2, useContext as useContext2, forwardRef as forwardRef5 } from "react";
853
+ import { jsx as jsx10 } from "react/jsx-runtime";
854
+ var RadioGroupContext = createContext2(null);
855
+ var useRadioGroup = () => useContext2(RadioGroupContext);
856
+ var ContextMenuRadioGroup = forwardRef5(
857
+ ({
858
+ children,
859
+ value,
860
+ onValueChange,
861
+ "data-testid": testId = "context-menu-radio-group"
862
+ }, ref) => {
863
+ return /* @__PURE__ */ jsx10(RadioGroupContext.Provider, { value: { value, onValueChange }, children: /* @__PURE__ */ jsx10(Box, { ref, role: "group", flexDirection: "column", "data-testid": testId, children }) });
864
+ }
865
+ );
866
+ ContextMenuRadioGroup.displayName = "ContextMenuRadioGroup";
867
+
868
+ // src/ContextMenuRadioItem.tsx
869
+ import { forwardRef as forwardRef6, useEffect as useEffect3, useRef as useRef3 } from "react";
870
+ import { useDesignSystem as useDesignSystem3 } from "@xsolla/xui-core";
871
+ import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
872
+ var radioSizeMap = {
873
+ s: 14,
874
+ m: 16,
875
+ l: 18,
876
+ xl: 20
877
+ };
878
+ var radioDotSizeMap = {
879
+ s: 6,
880
+ m: 8,
881
+ l: 8,
882
+ xl: 10
883
+ };
884
+ var RadioIndicator = ({
885
+ checked,
886
+ size,
887
+ disabled
888
+ }) => {
889
+ const { theme } = useDesignSystem3();
890
+ const brandColors = theme.colors.control.brand.primary;
891
+ const faintColors = theme.colors.control.faint;
892
+ const contentColors = theme.colors.content;
893
+ const outerSize = radioSizeMap[size];
894
+ const dotSize = radioDotSizeMap[size];
895
+ const getBgColor = () => {
896
+ if (disabled) {
897
+ return brandColors.bgDisable;
898
+ }
899
+ if (checked) {
900
+ return brandColors.bg;
901
+ }
902
+ return faintColors.bg;
903
+ };
904
+ const getBorderColor = () => {
905
+ if (checked && !disabled) {
906
+ return brandColors.bg;
907
+ }
908
+ return faintColors.border;
909
+ };
910
+ return /* @__PURE__ */ jsx11(
911
+ Box,
912
+ {
913
+ width: outerSize,
914
+ height: outerSize,
915
+ borderRadius: outerSize / 2,
916
+ borderWidth: checked ? 0 : 1,
917
+ borderColor: getBorderColor(),
918
+ backgroundColor: getBgColor(),
919
+ alignItems: "center",
920
+ justifyContent: "center",
921
+ children: checked && /* @__PURE__ */ jsx11(
922
+ Box,
923
+ {
924
+ width: dotSize,
925
+ height: dotSize,
926
+ borderRadius: dotSize / 2,
927
+ backgroundColor: disabled ? contentColors.tertiary : contentColors.on.brand
928
+ }
929
+ )
930
+ }
931
+ );
932
+ };
933
+ var ContextMenuRadioItem = forwardRef6(
934
+ ({
935
+ children,
936
+ description,
937
+ value,
938
+ disabled,
939
+ size: propSize,
940
+ onPress,
941
+ "data-testid": testId = "context-menu-radio-item"
942
+ }, ref) => {
943
+ const { theme } = useDesignSystem3();
944
+ const context = useContextMenu();
945
+ const radioGroup = useRadioGroup();
946
+ const size = propSize || context?.size || "m";
947
+ const sizeStyles = theme.sizing.contextMenu(size);
948
+ const itemRef = useRef3(null);
949
+ const checked = radioGroup?.value === value;
950
+ useEffect3(() => {
951
+ if (context && !disabled) {
952
+ const id = typeof children === "string" ? children : String(Math.random());
953
+ context.registerItem(id);
954
+ return () => context.unregisterItem(id);
955
+ }
956
+ }, [context, disabled, children]);
957
+ const handlePress = () => {
958
+ if (disabled) return;
959
+ radioGroup?.onValueChange(value);
960
+ onPress?.();
961
+ if (context) {
962
+ context.onItemSelect({
963
+ id: typeof children === "string" ? children : "",
964
+ label: typeof children === "string" ? children : "",
965
+ variant: "radio",
966
+ checked: true
967
+ });
968
+ }
969
+ };
970
+ const handleKeyDown = (event) => {
971
+ if (disabled) return;
972
+ if (event.key === "Enter" || event.key === " ") {
973
+ event.preventDefault();
974
+ handlePress();
975
+ }
976
+ };
977
+ const getTextColor = () => {
978
+ if (disabled) {
979
+ return theme.colors.control.input.textDisable;
980
+ }
981
+ return theme.colors.control.input.text;
982
+ };
983
+ return /* @__PURE__ */ jsxs3(
984
+ Box,
985
+ {
986
+ ref: ref || itemRef,
987
+ role: "menuitemradio",
988
+ "aria-checked": checked,
989
+ "aria-disabled": disabled || void 0,
990
+ tabIndex: disabled ? -1 : 0,
991
+ flexDirection: "row",
992
+ alignItems: description ? "flex-start" : "center",
993
+ gap: sizeStyles.gap,
994
+ paddingHorizontal: sizeStyles.itemPaddingHorizontal,
995
+ paddingVertical: sizeStyles.itemPaddingVertical,
996
+ backgroundColor: "transparent",
997
+ hoverStyle: !disabled ? {
998
+ backgroundColor: theme.colors.control.input.bgHover
999
+ } : void 0,
1000
+ pressStyle: !disabled ? {
1001
+ backgroundColor: theme.colors.control.input.bgDisable
1002
+ } : void 0,
1003
+ onPress: handlePress,
1004
+ onKeyDown: handleKeyDown,
1005
+ style: {
1006
+ cursor: disabled ? "not-allowed" : "pointer",
1007
+ opacity: disabled ? 0.5 : 1,
1008
+ outline: "none"
1009
+ },
1010
+ "data-testid": testId,
1011
+ children: [
1012
+ /* @__PURE__ */ jsx11(
1013
+ Box,
1014
+ {
1015
+ marginTop: description ? 2 : 0,
1016
+ alignItems: "center",
1017
+ justifyContent: "center",
1018
+ children: /* @__PURE__ */ jsx11(RadioIndicator, { checked, size, disabled })
1019
+ }
1020
+ ),
1021
+ /* @__PURE__ */ jsxs3(Box, { flex: 1, flexDirection: "column", gap: 2, children: [
1022
+ /* @__PURE__ */ jsx11(
1023
+ Text,
1024
+ {
1025
+ color: getTextColor(),
1026
+ fontSize: sizeStyles.fontSize,
1027
+ fontWeight: "400",
1028
+ lineHeight: sizeStyles.fontSize + 2,
1029
+ children
1030
+ }
1031
+ ),
1032
+ description && /* @__PURE__ */ jsx11(
1033
+ Text,
1034
+ {
1035
+ color: theme.colors.content.tertiary,
1036
+ fontSize: sizeStyles.descriptionFontSize,
1037
+ lineHeight: sizeStyles.descriptionFontSize + 2,
1038
+ children: description
1039
+ }
1040
+ )
1041
+ ] })
1042
+ ]
1043
+ }
1044
+ );
1045
+ }
1046
+ );
1047
+ ContextMenuRadioItem.displayName = "ContextMenuRadioItem";
1048
+
1049
+ // src/ContextMenuGroup.tsx
1050
+ import { forwardRef as forwardRef7 } from "react";
1051
+ import { useDesignSystem as useDesignSystem4 } from "@xsolla/xui-core";
1052
+ import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
1053
+ var ContextMenuGroup = forwardRef7(
1054
+ ({
1055
+ label,
1056
+ description,
1057
+ children,
1058
+ size: propSize,
1059
+ "data-testid": testId = "context-menu-group"
1060
+ }, ref) => {
1061
+ const { theme } = useDesignSystem4();
1062
+ const context = useContextMenu();
1063
+ const size = propSize || context?.size || "m";
1064
+ const sizeStyles = theme.sizing.contextMenu(size);
1065
+ return /* @__PURE__ */ jsxs4(Box, { ref, role: "group", "aria-label": label, "data-testid": testId, children: [
1066
+ label && /* @__PURE__ */ jsxs4(
1067
+ Box,
1068
+ {
1069
+ paddingHorizontal: sizeStyles.itemPaddingHorizontal,
1070
+ paddingVertical: sizeStyles.itemPaddingVertical,
1071
+ children: [
1072
+ /* @__PURE__ */ jsx12(
1073
+ Text,
1074
+ {
1075
+ color: theme.colors.content.secondary,
1076
+ fontSize: sizeStyles.descriptionFontSize,
1077
+ fontWeight: "600",
1078
+ style: { textTransform: "uppercase", letterSpacing: 0.5 },
1079
+ children: label
1080
+ }
1081
+ ),
1082
+ description && /* @__PURE__ */ jsx12(
1083
+ Text,
1084
+ {
1085
+ color: theme.colors.content.tertiary,
1086
+ fontSize: sizeStyles.descriptionFontSize - 2,
1087
+ marginTop: 2,
1088
+ children: description
1089
+ }
1090
+ )
1091
+ ]
1092
+ }
1093
+ ),
1094
+ children
1095
+ ] });
1096
+ }
1097
+ );
1098
+ ContextMenuGroup.displayName = "ContextMenuGroup";
1099
+
1100
+ // src/ContextMenuSeparator.tsx
1101
+ import { useDesignSystem as useDesignSystem5 } from "@xsolla/xui-core";
1102
+ import { jsx as jsx13 } from "react/jsx-runtime";
1103
+ var ContextMenuSeparator = ({
1104
+ size: propSize,
1105
+ "data-testid": testId = "context-menu-separator"
1106
+ }) => {
1107
+ const { theme } = useDesignSystem5();
1108
+ const context = useContextMenu();
1109
+ const size = propSize || context?.size || "m";
1110
+ const sizeStyles = theme.sizing.contextMenu(size);
1111
+ return /* @__PURE__ */ jsx13(
1112
+ Box,
1113
+ {
1114
+ role: "separator",
1115
+ marginVertical: sizeStyles.paddingVertical,
1116
+ marginHorizontal: sizeStyles.itemPaddingHorizontal,
1117
+ height: 1,
1118
+ backgroundColor: theme.colors.border.secondary,
1119
+ "data-testid": testId
1120
+ }
1121
+ );
1122
+ };
1123
+ ContextMenuSeparator.displayName = "ContextMenuSeparator";
1124
+
1125
+ // src/ContextMenuSearch.tsx
1126
+ import React9, { forwardRef as forwardRef8, useRef as useRef4, useEffect as useEffect4 } from "react";
1127
+ import { useDesignSystem as useDesignSystem6 } from "@xsolla/xui-core";
1128
+
1129
+ // ../icons-base/dist/web/index.mjs
1130
+ import styled8 from "styled-components";
1131
+ import { jsx as jsx14 } from "react/jsx-runtime";
1132
+ import { jsx as jsx22 } from "react/jsx-runtime";
1133
+ import { jsx as jsx32 } from "react/jsx-runtime";
1134
+ import { jsx as jsx42 } from "react/jsx-runtime";
1135
+ import { jsx as jsx52 } from "react/jsx-runtime";
1136
+ import { jsx as jsx62 } from "react/jsx-runtime";
1137
+ import { jsx as jsx72 } from "react/jsx-runtime";
1138
+ import { jsx as jsx82 } from "react/jsx-runtime";
1139
+ import { jsx as jsx92 } from "react/jsx-runtime";
1140
+ import { jsx as jsx102 } from "react/jsx-runtime";
1141
+ import { jsx as jsx112 } from "react/jsx-runtime";
1142
+ import { jsx as jsx122 } from "react/jsx-runtime";
1143
+ import { jsx as jsx132 } from "react/jsx-runtime";
1144
+ import { jsx as jsx142 } from "react/jsx-runtime";
1145
+ import { jsx as jsx15 } from "react/jsx-runtime";
1146
+ import { jsx as jsx16 } from "react/jsx-runtime";
1147
+ import { jsx as jsx17 } from "react/jsx-runtime";
1148
+ import { jsx as jsx18 } from "react/jsx-runtime";
1149
+ import { jsx as jsx19 } from "react/jsx-runtime";
1150
+ import { jsx as jsx20 } from "react/jsx-runtime";
1151
+ import { jsx as jsx21 } from "react/jsx-runtime";
1152
+ import { jsx as jsx222 } from "react/jsx-runtime";
1153
+ import { jsx as jsx23 } from "react/jsx-runtime";
1154
+ import { jsx as jsx24 } from "react/jsx-runtime";
1155
+ import { jsx as jsx25 } from "react/jsx-runtime";
1156
+ import { jsx as jsx26 } from "react/jsx-runtime";
1157
+ import { jsx as jsx27 } from "react/jsx-runtime";
1158
+ import { jsx as jsx28 } from "react/jsx-runtime";
1159
+ import { jsx as jsx29 } from "react/jsx-runtime";
1160
+ import { jsx as jsx30 } from "react/jsx-runtime";
1161
+ import { jsx as jsx31 } from "react/jsx-runtime";
1162
+ import { jsx as jsx322 } from "react/jsx-runtime";
1163
+ import { jsx as jsx33 } from "react/jsx-runtime";
1164
+ import { jsx as jsx34 } from "react/jsx-runtime";
1165
+ import { jsx as jsx35 } from "react/jsx-runtime";
1166
+ import { jsx as jsx36 } from "react/jsx-runtime";
1167
+ import { jsx as jsx37 } from "react/jsx-runtime";
1168
+ import { jsx as jsx38 } from "react/jsx-runtime";
1169
+ import { jsx as jsx39 } from "react/jsx-runtime";
1170
+ import { jsx as jsx40 } from "react/jsx-runtime";
1171
+ import { jsx as jsx41 } from "react/jsx-runtime";
1172
+ import { jsx as jsx422 } from "react/jsx-runtime";
1173
+ import { jsx as jsx43 } from "react/jsx-runtime";
1174
+ import { jsx as jsx44 } from "react/jsx-runtime";
1175
+ import { jsx as jsx45 } from "react/jsx-runtime";
1176
+ import { jsx as jsx46 } from "react/jsx-runtime";
1177
+ import { jsx as jsx47 } from "react/jsx-runtime";
1178
+ import { jsx as jsx48 } from "react/jsx-runtime";
1179
+ import { jsx as jsx49 } from "react/jsx-runtime";
1180
+ import { jsx as jsx50 } from "react/jsx-runtime";
1181
+ import { jsx as jsx51 } from "react/jsx-runtime";
1182
+ import { jsx as jsx522 } from "react/jsx-runtime";
1183
+ import { jsx as jsx53 } from "react/jsx-runtime";
1184
+ import { jsx as jsx54 } from "react/jsx-runtime";
1185
+ import { jsx as jsx55 } from "react/jsx-runtime";
1186
+ import { jsx as jsx56 } from "react/jsx-runtime";
1187
+ import { jsx as jsx57 } from "react/jsx-runtime";
1188
+ import { jsx as jsx58 } from "react/jsx-runtime";
1189
+ import { jsx as jsx59 } from "react/jsx-runtime";
1190
+ import { jsx as jsx60 } from "react/jsx-runtime";
1191
+ import { jsx as jsx61 } from "react/jsx-runtime";
1192
+ import { jsx as jsx622 } from "react/jsx-runtime";
1193
+ import { jsx as jsx63 } from "react/jsx-runtime";
1194
+ import { jsx as jsx64 } from "react/jsx-runtime";
1195
+ import { jsx as jsx65 } from "react/jsx-runtime";
1196
+ import { jsx as jsx66 } from "react/jsx-runtime";
1197
+ import { jsx as jsx67 } from "react/jsx-runtime";
1198
+ import { jsx as jsx68 } from "react/jsx-runtime";
1199
+ import { jsx as jsx69 } from "react/jsx-runtime";
1200
+ import { jsx as jsx70 } from "react/jsx-runtime";
1201
+ import { jsx as jsx71 } from "react/jsx-runtime";
1202
+ import { jsx as jsx722 } from "react/jsx-runtime";
1203
+ import { jsx as jsx73 } from "react/jsx-runtime";
1204
+ import { jsx as jsx74 } from "react/jsx-runtime";
1205
+ import { jsx as jsx75 } from "react/jsx-runtime";
1206
+ import { jsx as jsx76 } from "react/jsx-runtime";
1207
+ import { jsx as jsx77 } from "react/jsx-runtime";
1208
+ import { jsx as jsx78 } from "react/jsx-runtime";
1209
+ import { jsx as jsx79 } from "react/jsx-runtime";
1210
+ import { jsx as jsx80 } from "react/jsx-runtime";
1211
+ import { jsx as jsx81 } from "react/jsx-runtime";
1212
+ import { jsx as jsx822 } from "react/jsx-runtime";
1213
+ import { jsx as jsx83 } from "react/jsx-runtime";
1214
+ import { jsx as jsx84 } from "react/jsx-runtime";
1215
+ import { jsx as jsx85 } from "react/jsx-runtime";
1216
+ import { jsx as jsx86 } from "react/jsx-runtime";
1217
+ import { jsx as jsx87 } from "react/jsx-runtime";
1218
+ import { jsx as jsx88 } from "react/jsx-runtime";
1219
+ import { jsx as jsx89 } from "react/jsx-runtime";
1220
+ import { jsx as jsx90 } from "react/jsx-runtime";
1221
+ import { jsx as jsx91 } from "react/jsx-runtime";
1222
+ import { jsx as jsx922 } from "react/jsx-runtime";
1223
+ import { jsx as jsx93 } from "react/jsx-runtime";
1224
+ import { jsx as jsx94 } from "react/jsx-runtime";
1225
+ import { jsx as jsx95 } from "react/jsx-runtime";
1226
+ import { jsx as jsx96 } from "react/jsx-runtime";
1227
+ import { jsx as jsx97 } from "react/jsx-runtime";
1228
+ import { jsx as jsx98 } from "react/jsx-runtime";
1229
+ import { jsx as jsx99 } from "react/jsx-runtime";
1230
+ import { jsx as jsx100 } from "react/jsx-runtime";
1231
+ import { jsx as jsx101 } from "react/jsx-runtime";
1232
+ import { jsx as jsx1022 } from "react/jsx-runtime";
1233
+ import { jsx as jsx103 } from "react/jsx-runtime";
1234
+ import { jsx as jsx104 } from "react/jsx-runtime";
1235
+ import { jsx as jsx105 } from "react/jsx-runtime";
1236
+ import { jsx as jsx106 } from "react/jsx-runtime";
1237
+ import { jsx as jsx107 } from "react/jsx-runtime";
1238
+ import { jsx as jsx108 } from "react/jsx-runtime";
1239
+ import { jsx as jsx109 } from "react/jsx-runtime";
1240
+ import { jsx as jsx110 } from "react/jsx-runtime";
1241
+ import { jsx as jsx111 } from "react/jsx-runtime";
1242
+ import { jsx as jsx1122 } from "react/jsx-runtime";
1243
+ import { jsx as jsx113 } from "react/jsx-runtime";
1244
+ import { jsx as jsx114 } from "react/jsx-runtime";
1245
+ var StyledIcon2 = styled8.div`
1246
+ display: inline-flex;
1247
+ align-items: center;
1248
+ justify-content: center;
1249
+ width: ${(props) => props.$size}px;
1250
+ height: ${(props) => props.$size}px;
1251
+ color: ${(props) => props.$color};
1252
+
1253
+ svg {
1254
+ width: 100%;
1255
+ height: 100%;
1256
+ }
1257
+ `;
1258
+ var BaseIcon = ({
1259
+ variant = "line",
1260
+ size = 24,
1261
+ color = "currentColor",
1262
+ solidContent: solidContent114,
1263
+ lineContent: lineContent114,
1264
+ className,
1265
+ style,
1266
+ "data-testid": testId,
1267
+ "aria-label": ariaLabel,
1268
+ "aria-hidden": ariaHidden
1269
+ }) => {
1270
+ const svgContent = variant === "line" ? lineContent114 : solidContent114;
1271
+ return /* @__PURE__ */ jsx14(
1272
+ StyledIcon2,
1273
+ {
1274
+ $size: size,
1275
+ $color: color,
1276
+ className,
1277
+ style,
1278
+ "data-testid": testId,
1279
+ role: ariaLabel ? "img" : void 0,
1280
+ "aria-label": ariaLabel,
1281
+ "aria-hidden": ariaHidden != null ? ariaHidden : ariaLabel ? void 0 : true,
1282
+ dangerouslySetInnerHTML: { __html: svgContent }
1283
+ }
1284
+ );
1285
+ };
1286
+ var solidContent100 = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><g id="icon_search--line"><path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M10 2C14.4183 2 18 5.58172 18 10C18 11.8484 17.3711 13.5488 16.3184 14.9033L21.957 20.543L20.543 21.957L14.9033 16.3174C13.5487 17.3703 11.8486 18 10 18C5.58172 18 2 14.4183 2 10C2 5.58172 5.58172 2 10 2ZM10 4C6.68629 4 4 6.68629 4 10C4 13.3137 6.68629 16 10 16C13.3137 16 16 13.3137 16 10C16 6.68629 13.3137 4 10 4Z" style="fill: currentColor"/></g></svg>`;
1287
+ var lineContent100 = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><g id="icon_search--line"><path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M10 2C14.4183 2 18 5.58172 18 10C18 11.8484 17.3711 13.5488 16.3184 14.9033L21.957 20.543L20.543 21.957L14.9033 16.3174C13.5487 17.3703 11.8486 18 10 18C5.58172 18 2 14.4183 2 10C2 5.58172 5.58172 2 10 2ZM10 4C6.68629 4 4 6.68629 4 10C4 13.3137 6.68629 16 10 16C13.3137 16 16 13.3137 16 10C16 6.68629 13.3137 4 10 4Z" style="fill: currentColor"/></g></svg>`;
1288
+ var Search = (props) => /* @__PURE__ */ jsx101(BaseIcon, { ...props, solidContent: solidContent100, lineContent: lineContent100 });
1289
+
1290
+ // src/ContextMenuSearch.tsx
1291
+ import { Divider } from "@xsolla/xui-divider";
1292
+ import { jsx as jsx115, jsxs as jsxs5 } from "react/jsx-runtime";
1293
+ var ContextMenuSearch = forwardRef8(
1294
+ ({
1295
+ value,
1296
+ onChange,
1297
+ onValueChange,
1298
+ placeholder = "Search",
1299
+ autoFocus = true,
1300
+ size: propSize,
1301
+ "data-testid": testId = "context-menu-search"
1302
+ }, ref) => {
1303
+ const { theme } = useDesignSystem6();
1304
+ const context = useContextMenu();
1305
+ const size = propSize || context?.size || "m";
1306
+ const sizeStyles = theme.sizing.contextMenu(size);
1307
+ const inputRef = useRef4(null);
1308
+ const inputColors = theme.colors.control.input;
1309
+ React9.useImperativeHandle(
1310
+ ref,
1311
+ () => inputRef.current,
1312
+ []
1313
+ );
1314
+ useEffect4(() => {
1315
+ if (autoFocus && inputRef.current) {
1316
+ setTimeout(() => {
1317
+ inputRef.current?.focus();
1318
+ }, 0);
1319
+ }
1320
+ }, [autoFocus]);
1321
+ const handleChange = (e) => {
1322
+ onChange?.(e);
1323
+ onValueChange?.(e.target.value);
1324
+ };
1325
+ const handleKeyDown = (e) => {
1326
+ e.stopPropagation();
1327
+ if (e.key === "Escape") {
1328
+ context?.closeMenu();
1329
+ }
1330
+ };
1331
+ return /* @__PURE__ */ jsxs5(
1332
+ Box,
1333
+ {
1334
+ flexDirection: "column",
1335
+ gap: sizeStyles.searchGap,
1336
+ paddingHorizontal: sizeStyles.searchPaddingHorizontal,
1337
+ paddingVertical: sizeStyles.searchPaddingVertical,
1338
+ "data-testid": testId,
1339
+ children: [
1340
+ /* @__PURE__ */ jsxs5(Box, { flexDirection: "row", alignItems: "center", gap: 8, children: [
1341
+ /* @__PURE__ */ jsx115(
1342
+ Icon,
1343
+ {
1344
+ size: sizeStyles.searchIconSize,
1345
+ color: inputColors.placeholder,
1346
+ children: /* @__PURE__ */ jsx115(Search, {})
1347
+ }
1348
+ ),
1349
+ /* @__PURE__ */ jsx115(Box, { flex: 1, children: /* @__PURE__ */ jsx115(
1350
+ InputPrimitive,
1351
+ {
1352
+ ref: inputRef,
1353
+ value,
1354
+ onChange: handleChange,
1355
+ onKeyDown: handleKeyDown,
1356
+ placeholder,
1357
+ fontSize: sizeStyles.searchFontSize,
1358
+ color: inputColors.text,
1359
+ placeholderTextColor: inputColors.placeholder,
1360
+ "aria-label": placeholder,
1361
+ "data-testid": `${testId}__input`,
1362
+ style: {
1363
+ lineHeight: `${sizeStyles.searchLineHeight}px`
1364
+ }
1365
+ }
1366
+ ) })
1367
+ ] }),
1368
+ /* @__PURE__ */ jsx115(Divider, {})
1369
+ ]
1370
+ }
1371
+ );
1372
+ }
1373
+ );
1374
+ ContextMenuSearch.displayName = "ContextMenuSearch";
1375
+
1376
+ // src/ContextMenu.tsx
1377
+ import { jsx as jsx116, jsxs as jsxs6 } from "react/jsx-runtime";
1378
+ var ContextMenuRoot = forwardRef9(
1379
+ ({
1380
+ children,
1381
+ list,
1382
+ groups,
1383
+ size = "m",
1384
+ isOpen: propIsOpen,
1385
+ onOpenChange,
1386
+ position,
1387
+ trigger,
1388
+ width,
1389
+ maxHeight = 300,
1390
+ onSelect,
1391
+ onCheckedChange,
1392
+ closeOnSelect = true,
1393
+ isLoading,
1394
+ "aria-label": ariaLabel,
1395
+ "data-testid": testId = "context-menu"
1396
+ }, ref) => {
1397
+ const { theme } = useDesignSystem7();
1398
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
1399
+ const [activeIndex, setActiveIndex] = useState(-1);
1400
+ const containerRef = useRef5(null);
1401
+ const menuRef = useRef5(null);
1402
+ const itemsRef = useRef5([]);
1403
+ const isOpen = propIsOpen !== void 0 ? propIsOpen : internalIsOpen;
1404
+ const sizeStyles = theme.sizing.contextMenu(size);
1405
+ const closeMenu = useCallback(() => {
1406
+ if (propIsOpen === void 0) {
1407
+ setInternalIsOpen(false);
1408
+ }
1409
+ onOpenChange?.(false);
1410
+ setActiveIndex(-1);
1411
+ }, [propIsOpen, onOpenChange]);
1412
+ const toggleMenu = useCallback(() => {
1413
+ const nextOpen = !isOpen;
1414
+ if (propIsOpen === void 0) {
1415
+ setInternalIsOpen(nextOpen);
1416
+ }
1417
+ onOpenChange?.(nextOpen);
1418
+ if (!nextOpen) {
1419
+ setActiveIndex(-1);
1420
+ }
1421
+ }, [isOpen, propIsOpen, onOpenChange]);
1422
+ useEffect5(() => {
1423
+ const handleClickOutside = (event) => {
1424
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1425
+ closeMenu();
1426
+ }
1427
+ };
1428
+ if (isOpen) {
1429
+ document.addEventListener("mousedown", handleClickOutside);
1430
+ }
1431
+ return () => {
1432
+ document.removeEventListener("mousedown", handleClickOutside);
1433
+ };
1434
+ }, [isOpen, closeMenu]);
1435
+ useEffect5(() => {
1436
+ const handleEscape = (event) => {
1437
+ if (event.key === "Escape") {
1438
+ closeMenu();
1439
+ }
1440
+ };
1441
+ if (isOpen) {
1442
+ document.addEventListener("keydown", handleEscape);
1443
+ }
1444
+ return () => {
1445
+ document.removeEventListener("keydown", handleEscape);
1446
+ };
1447
+ }, [isOpen, closeMenu]);
1448
+ useEffect5(() => {
1449
+ if (isOpen && menuRef.current) {
1450
+ menuRef.current.focus();
1451
+ }
1452
+ }, [isOpen]);
1453
+ useEffect5(() => {
1454
+ if (!isOpen) {
1455
+ itemsRef.current = [];
1456
+ }
1457
+ }, [isOpen]);
1458
+ const registerItem = useCallback((id) => {
1459
+ const index = itemsRef.current.indexOf(id);
1460
+ if (index === -1) {
1461
+ itemsRef.current.push(id);
1462
+ return itemsRef.current.length - 1;
1463
+ }
1464
+ return index;
1465
+ }, []);
1466
+ const unregisterItem = useCallback((id) => {
1467
+ const index = itemsRef.current.indexOf(id);
1468
+ if (index !== -1) {
1469
+ itemsRef.current.splice(index, 1);
1470
+ }
1471
+ }, []);
1472
+ const onItemSelect = useCallback(
1473
+ (item) => {
1474
+ onSelect?.(item);
1475
+ if (item.variant === "checkbox") {
1476
+ onCheckedChange?.(item.id, !item.checked);
1477
+ } else if (item.variant === "radio") {
1478
+ onCheckedChange?.(item.id, true);
1479
+ if (closeOnSelect) {
1480
+ closeMenu();
1481
+ }
1482
+ } else if (closeOnSelect) {
1483
+ closeMenu();
1484
+ }
1485
+ },
1486
+ [onSelect, onCheckedChange, closeOnSelect, closeMenu]
1487
+ );
1488
+ const handleKeyDown = useCallback(
1489
+ (event) => {
1490
+ const itemCount = itemsRef.current.length;
1491
+ if (itemCount === 0) return;
1492
+ switch (event.key) {
1493
+ case "ArrowDown":
1494
+ event.preventDefault();
1495
+ setActiveIndex((prev) => prev < itemCount - 1 ? prev + 1 : 0);
1496
+ break;
1497
+ case "ArrowUp":
1498
+ event.preventDefault();
1499
+ setActiveIndex((prev) => prev > 0 ? prev - 1 : itemCount - 1);
1500
+ break;
1501
+ case "Home":
1502
+ event.preventDefault();
1503
+ setActiveIndex(0);
1504
+ break;
1505
+ case "End":
1506
+ event.preventDefault();
1507
+ setActiveIndex(itemCount - 1);
1508
+ break;
1509
+ case "Enter":
1510
+ case " ":
1511
+ event.preventDefault();
1512
+ break;
1513
+ case "Tab":
1514
+ closeMenu();
1515
+ break;
1516
+ }
1517
+ },
1518
+ [closeMenu]
1519
+ );
1520
+ const contextValue = useMemo(
1521
+ () => ({
1522
+ size,
1523
+ closeMenu,
1524
+ onItemSelect,
1525
+ activeIndex,
1526
+ setActiveIndex,
1527
+ registerItem,
1528
+ unregisterItem
1529
+ }),
1530
+ [size, closeMenu, onItemSelect, activeIndex, registerItem, unregisterItem]
1531
+ );
1532
+ const getPositionStyles = () => {
1533
+ if (position) {
1534
+ return {
1535
+ position: "fixed",
1536
+ left: position.x,
1537
+ top: position.y
1538
+ };
1539
+ }
1540
+ return {
1541
+ position: "absolute",
1542
+ top: "100%",
1543
+ left: 0,
1544
+ marginTop: 4
1545
+ };
1546
+ };
1547
+ const renderItems = (itemsData) => {
1548
+ return itemsData.map((item, index) => {
1549
+ const commonProps = {
1550
+ key: item.id || index,
1551
+ disabled: item.disabled,
1552
+ description: item.description,
1553
+ "data-testid": `context-menu-item-${item.id || index}`
1554
+ };
1555
+ if (item.variant === "checkbox") {
1556
+ return /* @__PURE__ */ jsx116(
1557
+ ContextMenuCheckboxItem,
1558
+ {
1559
+ ...commonProps,
1560
+ checked: item.checked,
1561
+ onCheckedChange: (checked) => onCheckedChange?.(item.id, checked),
1562
+ onPress: item.onPress,
1563
+ children: item.label
1564
+ }
1565
+ );
1566
+ }
1567
+ if (item.variant === "radio") {
1568
+ return /* @__PURE__ */ jsx116(
1569
+ ContextMenuRadioItem,
1570
+ {
1571
+ ...commonProps,
1572
+ value: item.id,
1573
+ onPress: item.onPress,
1574
+ children: item.label
1575
+ }
1576
+ );
1577
+ }
1578
+ return /* @__PURE__ */ jsx116(
1579
+ ContextMenuItem,
1580
+ {
1581
+ ...commonProps,
1582
+ icon: item.icon,
1583
+ selected: item.selected,
1584
+ shortcut: item.trailing?.type === "shortcut" ? item.trailing.content : void 0,
1585
+ trailing: item.trailing?.type !== "shortcut" && item.trailing?.type !== "none" ? item.trailing?.content : void 0,
1586
+ hasSubmenu: item.children && item.children.length > 0,
1587
+ onPress: item.onPress,
1588
+ active: activeIndex === index,
1589
+ children: item.label
1590
+ }
1591
+ );
1592
+ });
1593
+ };
1594
+ const renderGroups = (groupsData) => {
1595
+ return groupsData.map((group, groupIndex) => /* @__PURE__ */ jsxs6(React10.Fragment, { children: [
1596
+ groupIndex > 0 && /* @__PURE__ */ jsx116(ContextMenuSeparator, {}),
1597
+ /* @__PURE__ */ jsx116(
1598
+ ContextMenuGroup,
1599
+ {
1600
+ label: group.label,
1601
+ description: group.description,
1602
+ "data-testid": `context-menu-group-${group.id || groupIndex}`,
1603
+ children: renderItems(group.items)
1604
+ }
1605
+ )
1606
+ ] }, group.id || groupIndex));
1607
+ };
1608
+ const renderContent = () => {
1609
+ if (isLoading) {
1610
+ const brandColor = theme.colors.control.brand.primary.bg;
1611
+ return /* @__PURE__ */ jsx116(
1612
+ Box,
1613
+ {
1614
+ padding: 16,
1615
+ alignItems: "center",
1616
+ justifyContent: "center",
1617
+ minHeight: 60,
1618
+ children: /* @__PURE__ */ jsx116(Spinner2, { size: "m", color: brandColor })
1619
+ }
1620
+ );
1621
+ }
1622
+ if (children) {
1623
+ return children;
1624
+ }
1625
+ if (groups && groups.length > 0) {
1626
+ return renderGroups(groups);
1627
+ }
1628
+ if (list && list.length > 0) {
1629
+ return renderItems(list);
1630
+ }
1631
+ return null;
1632
+ };
1633
+ return /* @__PURE__ */ jsx116(ContextMenuContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs6(
1634
+ "div",
1635
+ {
1636
+ ref: containerRef,
1637
+ style: {
1638
+ position: position ? "static" : "relative",
1639
+ width: trigger ? "fit-content" : void 0,
1640
+ display: "inline-block"
1641
+ },
1642
+ "data-testid": testId,
1643
+ children: [
1644
+ trigger && /* @__PURE__ */ jsx116(
1645
+ "div",
1646
+ {
1647
+ onClick: toggleMenu,
1648
+ style: { cursor: "pointer" },
1649
+ "data-testid": `${testId}-trigger`,
1650
+ children: trigger
1651
+ }
1652
+ ),
1653
+ isOpen && /* @__PURE__ */ jsx116(
1654
+ Box,
1655
+ {
1656
+ ref: (node) => {
1657
+ menuRef.current = node;
1658
+ if (typeof ref === "function") {
1659
+ ref(node);
1660
+ } else if (ref) {
1661
+ ref.current = node;
1662
+ }
1663
+ },
1664
+ role: "menu",
1665
+ "aria-label": ariaLabel,
1666
+ backgroundColor: theme.colors.background.secondary,
1667
+ borderColor: theme.colors.border.secondary,
1668
+ borderWidth: 1,
1669
+ borderRadius: theme.radius.button,
1670
+ paddingVertical: sizeStyles.paddingVertical,
1671
+ width,
1672
+ minWidth: sizeStyles.minWidth,
1673
+ style: {
1674
+ ...getPositionStyles(),
1675
+ zIndex: 1e3,
1676
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
1677
+ maxHeight,
1678
+ overflowY: "auto",
1679
+ outline: "none"
1680
+ },
1681
+ tabIndex: -1,
1682
+ onKeyDown: handleKeyDown,
1683
+ "data-testid": `${testId}-content`,
1684
+ children: renderContent()
1685
+ }
1686
+ )
1687
+ ]
1688
+ }
1689
+ ) });
1690
+ }
1691
+ );
1692
+ ContextMenuRoot.displayName = "ContextMenu";
1693
+ var ContextMenu = Object.assign(ContextMenuRoot, {
1694
+ Item: ContextMenuItem,
1695
+ CheckboxItem: ContextMenuCheckboxItem,
1696
+ RadioGroup: ContextMenuRadioGroup,
1697
+ RadioItem: ContextMenuRadioItem,
1698
+ Group: ContextMenuGroup,
1699
+ Separator: ContextMenuSeparator,
1700
+ Search: ContextMenuSearch
1701
+ });
1702
+
1703
+ // src/hooks/useContextMenuPosition.ts
1704
+ import { useState as useState2, useEffect as useEffect6 } from "react";
1705
+ var useContextMenuPosition = ({
1706
+ position,
1707
+ menuRef,
1708
+ isOpen,
1709
+ offset = 8
1710
+ }) => {
1711
+ const [adjustedPosition, setAdjustedPosition] = useState2();
1712
+ useEffect6(() => {
1713
+ if (!isOpen || !position || !menuRef.current) {
1714
+ setAdjustedPosition(void 0);
1715
+ return;
1716
+ }
1717
+ const rafId = requestAnimationFrame(() => {
1718
+ const menu = menuRef.current;
1719
+ if (!menu) return;
1720
+ const rect = menu.getBoundingClientRect();
1721
+ const viewportWidth = window.innerWidth;
1722
+ const viewportHeight = window.innerHeight;
1723
+ let { x, y } = position;
1724
+ let transformOriginX = "left";
1725
+ let transformOriginY = "top";
1726
+ if (x + rect.width > viewportWidth - offset) {
1727
+ x = Math.max(offset, viewportWidth - rect.width - offset);
1728
+ transformOriginX = "right";
1729
+ }
1730
+ if (x < offset) {
1731
+ x = offset;
1732
+ transformOriginX = "left";
1733
+ }
1734
+ if (y + rect.height > viewportHeight - offset) {
1735
+ const aboveY = position.y - rect.height;
1736
+ if (aboveY >= offset) {
1737
+ y = aboveY;
1738
+ transformOriginY = "bottom";
1739
+ } else {
1740
+ y = Math.max(offset, viewportHeight - rect.height - offset);
1741
+ transformOriginY = "bottom";
1742
+ }
1743
+ }
1744
+ if (y < offset) {
1745
+ y = offset;
1746
+ transformOriginY = "top";
1747
+ }
1748
+ setAdjustedPosition({
1749
+ x,
1750
+ y,
1751
+ transformOrigin: `${transformOriginY} ${transformOriginX}`
1752
+ });
1753
+ });
1754
+ return () => cancelAnimationFrame(rafId);
1755
+ }, [isOpen, position, menuRef, offset]);
1756
+ return adjustedPosition;
1757
+ };
1758
+
1759
+ // src/hooks/useKeyboardNavigation.ts
1760
+ import { useCallback as useCallback2, useRef as useRef6, useEffect as useEffect7 } from "react";
1761
+ var useKeyboardNavigation = ({
1762
+ isOpen,
1763
+ itemCount,
1764
+ activeIndex,
1765
+ setActiveIndex,
1766
+ onSelect,
1767
+ onClose,
1768
+ loop = true
1769
+ }) => {
1770
+ const typeaheadBuffer = useRef6("");
1771
+ const typeaheadTimeout = useRef6(null);
1772
+ useEffect7(() => {
1773
+ if (!isOpen) {
1774
+ typeaheadBuffer.current = "";
1775
+ if (typeaheadTimeout.current) {
1776
+ clearTimeout(typeaheadTimeout.current);
1777
+ }
1778
+ }
1779
+ }, [isOpen]);
1780
+ const handleKeyDown = useCallback2(
1781
+ (event) => {
1782
+ if (!isOpen || itemCount === 0) return;
1783
+ switch (event.key) {
1784
+ case "ArrowDown":
1785
+ event.preventDefault();
1786
+ if (loop) {
1787
+ setActiveIndex(activeIndex < itemCount - 1 ? activeIndex + 1 : 0);
1788
+ } else {
1789
+ setActiveIndex(Math.min(activeIndex + 1, itemCount - 1));
1790
+ }
1791
+ break;
1792
+ case "ArrowUp":
1793
+ event.preventDefault();
1794
+ if (loop) {
1795
+ setActiveIndex(activeIndex > 0 ? activeIndex - 1 : itemCount - 1);
1796
+ } else {
1797
+ setActiveIndex(Math.max(activeIndex - 1, 0));
1798
+ }
1799
+ break;
1800
+ case "Home":
1801
+ event.preventDefault();
1802
+ setActiveIndex(0);
1803
+ break;
1804
+ case "End":
1805
+ event.preventDefault();
1806
+ setActiveIndex(itemCount - 1);
1807
+ break;
1808
+ case "Enter":
1809
+ case " ":
1810
+ event.preventDefault();
1811
+ if (activeIndex >= 0 && activeIndex < itemCount) {
1812
+ onSelect(activeIndex);
1813
+ }
1814
+ break;
1815
+ case "Escape":
1816
+ event.preventDefault();
1817
+ onClose();
1818
+ break;
1819
+ case "Tab":
1820
+ onClose();
1821
+ break;
1822
+ default:
1823
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey) {
1824
+ typeaheadBuffer.current += event.key.toLowerCase();
1825
+ if (typeaheadTimeout.current) {
1826
+ clearTimeout(typeaheadTimeout.current);
1827
+ }
1828
+ typeaheadTimeout.current = setTimeout(() => {
1829
+ typeaheadBuffer.current = "";
1830
+ }, 500);
1831
+ }
1832
+ break;
1833
+ }
1834
+ },
1835
+ [isOpen, itemCount, activeIndex, setActiveIndex, onSelect, onClose, loop]
1836
+ );
1837
+ return { handleKeyDown };
1838
+ };
1839
+ export {
1840
+ ContextMenu,
1841
+ ContextMenuCheckboxItem,
1842
+ ContextMenuGroup,
1843
+ ContextMenuItem,
1844
+ ContextMenuRadioGroup,
1845
+ ContextMenuRadioItem,
1846
+ ContextMenuSearch,
1847
+ ContextMenuSeparator,
1848
+ useContextMenu,
1849
+ useContextMenuPosition,
1850
+ useContextMenuRequired,
1851
+ useKeyboardNavigation,
1852
+ useRadioGroup
1853
+ };
1854
+ //# sourceMappingURL=index.mjs.map