@xsolla/xui-button 0.64.0-pr56.1768440195

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.
@@ -0,0 +1,1236 @@
1
+ // src/Button.tsx
2
+ import { useState } from "react";
3
+
4
+ // ../primitives-native/src/Box.tsx
5
+ import {
6
+ View,
7
+ Pressable,
8
+ Image
9
+ } from "react-native";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var Box = ({
12
+ children,
13
+ onPress,
14
+ onLayout,
15
+ onMoveShouldSetResponder,
16
+ onResponderGrant,
17
+ onResponderMove,
18
+ onResponderRelease,
19
+ onResponderTerminate,
20
+ backgroundColor,
21
+ borderColor,
22
+ borderWidth,
23
+ borderBottomWidth,
24
+ borderBottomColor,
25
+ borderTopWidth,
26
+ borderTopColor,
27
+ borderLeftWidth,
28
+ borderLeftColor,
29
+ borderRightWidth,
30
+ borderRightColor,
31
+ borderRadius,
32
+ borderStyle,
33
+ height,
34
+ padding,
35
+ paddingHorizontal,
36
+ paddingVertical,
37
+ margin,
38
+ marginTop,
39
+ marginBottom,
40
+ marginLeft,
41
+ marginRight,
42
+ flexDirection,
43
+ alignItems,
44
+ justifyContent,
45
+ position,
46
+ top,
47
+ bottom,
48
+ left,
49
+ right,
50
+ width,
51
+ flex,
52
+ overflow,
53
+ zIndex,
54
+ hoverStyle,
55
+ pressStyle,
56
+ style,
57
+ "data-testid": dataTestId,
58
+ testID,
59
+ as,
60
+ src,
61
+ alt,
62
+ ...rest
63
+ }) => {
64
+ const getContainerStyle = (pressed) => ({
65
+ backgroundColor: pressed && pressStyle?.backgroundColor ? pressStyle.backgroundColor : backgroundColor,
66
+ borderColor,
67
+ borderWidth,
68
+ borderBottomWidth,
69
+ borderBottomColor,
70
+ borderTopWidth,
71
+ borderTopColor,
72
+ borderLeftWidth,
73
+ borderLeftColor,
74
+ borderRightWidth,
75
+ borderRightColor,
76
+ borderRadius,
77
+ borderStyle,
78
+ overflow,
79
+ zIndex,
80
+ height,
81
+ width,
82
+ padding,
83
+ paddingHorizontal,
84
+ paddingVertical,
85
+ margin,
86
+ marginTop,
87
+ marginBottom,
88
+ marginLeft,
89
+ marginRight,
90
+ flexDirection,
91
+ alignItems,
92
+ justifyContent,
93
+ position,
94
+ top,
95
+ bottom,
96
+ left,
97
+ right,
98
+ flex,
99
+ ...style
100
+ });
101
+ const finalTestID = dataTestId || testID;
102
+ const {
103
+ role,
104
+ tabIndex,
105
+ onKeyDown,
106
+ onKeyUp,
107
+ "aria-label": _ariaLabel,
108
+ "aria-labelledby": _ariaLabelledBy,
109
+ "aria-current": _ariaCurrent,
110
+ "aria-disabled": _ariaDisabled,
111
+ "aria-live": _ariaLive,
112
+ className,
113
+ "data-testid": _dataTestId,
114
+ ...nativeRest
115
+ } = rest;
116
+ if (as === "img" && src) {
117
+ const imageStyle = {
118
+ width,
119
+ height,
120
+ borderRadius,
121
+ position,
122
+ top,
123
+ bottom,
124
+ left,
125
+ right,
126
+ ...style
127
+ };
128
+ return /* @__PURE__ */ jsx(
129
+ Image,
130
+ {
131
+ source: { uri: src },
132
+ style: imageStyle,
133
+ testID: finalTestID,
134
+ resizeMode: "cover",
135
+ ...nativeRest
136
+ }
137
+ );
138
+ }
139
+ if (onPress) {
140
+ return /* @__PURE__ */ jsx(
141
+ Pressable,
142
+ {
143
+ onPress,
144
+ onLayout,
145
+ onMoveShouldSetResponder,
146
+ onResponderGrant,
147
+ onResponderMove,
148
+ onResponderRelease,
149
+ onResponderTerminate,
150
+ style: ({ pressed }) => getContainerStyle(pressed),
151
+ testID: finalTestID,
152
+ ...nativeRest,
153
+ children
154
+ }
155
+ );
156
+ }
157
+ return /* @__PURE__ */ jsx(
158
+ View,
159
+ {
160
+ style: getContainerStyle(),
161
+ testID: finalTestID,
162
+ onLayout,
163
+ onMoveShouldSetResponder,
164
+ onResponderGrant,
165
+ onResponderMove,
166
+ onResponderRelease,
167
+ onResponderTerminate,
168
+ ...nativeRest,
169
+ children
170
+ }
171
+ );
172
+ };
173
+
174
+ // ../primitives-native/src/Text.tsx
175
+ import { Text as RNText } from "react-native";
176
+ import { jsx as jsx2 } from "react/jsx-runtime";
177
+ var roleMap = {
178
+ alert: "alert",
179
+ heading: "header",
180
+ button: "button",
181
+ link: "link",
182
+ text: "text"
183
+ };
184
+ var Text = ({
185
+ children,
186
+ color,
187
+ fontSize,
188
+ fontWeight,
189
+ fontFamily,
190
+ id,
191
+ role,
192
+ ...props
193
+ }) => {
194
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
195
+ if (resolvedFontFamily === "Pilat Wide Bold") {
196
+ resolvedFontFamily = void 0;
197
+ }
198
+ const style = {
199
+ color,
200
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
201
+ fontWeight,
202
+ fontFamily: resolvedFontFamily,
203
+ textDecorationLine: props.textDecoration
204
+ };
205
+ const accessibilityRole = role ? roleMap[role] : void 0;
206
+ return /* @__PURE__ */ jsx2(RNText, { style, testID: id, accessibilityRole, children });
207
+ };
208
+
209
+ // ../primitives-native/src/Spinner.tsx
210
+ import { ActivityIndicator, View as View2 } from "react-native";
211
+ import { jsx as jsx3 } from "react/jsx-runtime";
212
+ var Spinner = ({
213
+ color,
214
+ size,
215
+ role,
216
+ "aria-label": ariaLabel,
217
+ "aria-live": ariaLive,
218
+ "aria-describedby": ariaDescribedBy,
219
+ testID
220
+ }) => {
221
+ return /* @__PURE__ */ jsx3(
222
+ View2,
223
+ {
224
+ accessible: true,
225
+ accessibilityRole: role === "status" ? "none" : void 0,
226
+ accessibilityLabel: ariaLabel,
227
+ accessibilityLiveRegion: ariaLive === "polite" ? "polite" : ariaLive === "assertive" ? "assertive" : "none",
228
+ testID,
229
+ children: /* @__PURE__ */ jsx3(
230
+ ActivityIndicator,
231
+ {
232
+ color,
233
+ size: typeof size === "number" ? size : "small"
234
+ }
235
+ )
236
+ }
237
+ );
238
+ };
239
+ Spinner.displayName = "Spinner";
240
+
241
+ // ../primitives-native/src/Icon.tsx
242
+ import React from "react";
243
+ import { View as View3 } from "react-native";
244
+ import { jsx as jsx4 } from "react/jsx-runtime";
245
+ var Icon = ({ children, color, size }) => {
246
+ const style = {
247
+ width: typeof size === "number" ? size : void 0,
248
+ height: typeof size === "number" ? size : void 0,
249
+ alignItems: "center",
250
+ justifyContent: "center"
251
+ };
252
+ const childrenWithProps = React.Children.map(children, (child) => {
253
+ if (React.isValidElement(child)) {
254
+ return React.cloneElement(child, {
255
+ color: child.props.color || color,
256
+ // Also pass size if child seems to be an icon that needs it
257
+ size: child.props.size || size
258
+ });
259
+ }
260
+ return child;
261
+ });
262
+ return /* @__PURE__ */ jsx4(View3, { style, children: childrenWithProps });
263
+ };
264
+
265
+ // ../primitives-native/src/Divider.tsx
266
+ import { View as View4 } from "react-native";
267
+ import { jsx as jsx5 } from "react/jsx-runtime";
268
+ var Divider = ({
269
+ color,
270
+ height,
271
+ width,
272
+ vertical,
273
+ dashStroke
274
+ }) => {
275
+ const style = {
276
+ backgroundColor: dashStroke ? "transparent" : color || "rgba(255, 255, 255, 0.15)",
277
+ width: vertical ? typeof width === "number" ? width : 1 : "100%",
278
+ height: vertical ? "100%" : typeof height === "number" ? height : 1,
279
+ ...dashStroke && {
280
+ borderStyle: "dashed",
281
+ borderColor: color || "rgba(255, 255, 255, 0.15)",
282
+ borderWidth: 0,
283
+ ...vertical ? { borderLeftWidth: typeof width === "number" ? width : 1 } : { borderTopWidth: typeof height === "number" ? height : 1 }
284
+ }
285
+ };
286
+ return /* @__PURE__ */ jsx5(View4, { style });
287
+ };
288
+
289
+ // ../primitives-native/src/Input.tsx
290
+ import { forwardRef } from "react";
291
+ import { TextInput as RNTextInput } from "react-native";
292
+ import { jsx as jsx6 } from "react/jsx-runtime";
293
+ var keyboardTypeMap = {
294
+ text: "default",
295
+ number: "numeric",
296
+ email: "email-address",
297
+ tel: "phone-pad",
298
+ url: "url",
299
+ decimal: "decimal-pad"
300
+ };
301
+ var inputModeToKeyboardType = {
302
+ none: "default",
303
+ text: "default",
304
+ decimal: "decimal-pad",
305
+ numeric: "number-pad",
306
+ tel: "phone-pad",
307
+ search: "default",
308
+ email: "email-address",
309
+ url: "url"
310
+ };
311
+ var autoCompleteToTextContentType = {
312
+ "one-time-code": "oneTimeCode",
313
+ email: "emailAddress",
314
+ username: "username",
315
+ password: "password",
316
+ "new-password": "newPassword",
317
+ tel: "telephoneNumber",
318
+ "postal-code": "postalCode",
319
+ name: "name"
320
+ };
321
+ var InputPrimitive = forwardRef(
322
+ ({
323
+ value,
324
+ placeholder,
325
+ onChange,
326
+ onChangeText,
327
+ onFocus,
328
+ onBlur,
329
+ onKeyDown,
330
+ disabled,
331
+ secureTextEntry,
332
+ style,
333
+ color,
334
+ fontSize,
335
+ placeholderTextColor,
336
+ maxLength,
337
+ name,
338
+ type,
339
+ inputMode,
340
+ autoComplete,
341
+ id,
342
+ "aria-invalid": ariaInvalid,
343
+ "aria-describedby": ariaDescribedBy,
344
+ "aria-labelledby": ariaLabelledBy,
345
+ "aria-label": ariaLabel,
346
+ "aria-disabled": ariaDisabled,
347
+ "data-testid": dataTestId
348
+ }, ref) => {
349
+ const handleChangeText = (text) => {
350
+ onChangeText?.(text);
351
+ if (onChange) {
352
+ const syntheticEvent = {
353
+ target: { value: text },
354
+ currentTarget: { value: text },
355
+ type: "change",
356
+ nativeEvent: { text },
357
+ preventDefault: () => {
358
+ },
359
+ stopPropagation: () => {
360
+ },
361
+ isTrusted: false
362
+ };
363
+ onChange(syntheticEvent);
364
+ }
365
+ };
366
+ const keyboardType = inputMode ? inputModeToKeyboardType[inputMode] || "default" : type ? keyboardTypeMap[type] || "default" : "default";
367
+ const textContentType = autoComplete ? autoCompleteToTextContentType[autoComplete] : void 0;
368
+ return /* @__PURE__ */ jsx6(
369
+ RNTextInput,
370
+ {
371
+ ref,
372
+ value,
373
+ placeholder,
374
+ onChangeText: handleChangeText,
375
+ onFocus,
376
+ onBlur,
377
+ onKeyPress: (e) => {
378
+ if (onKeyDown) {
379
+ onKeyDown({
380
+ key: e.nativeEvent.key,
381
+ preventDefault: () => {
382
+ }
383
+ });
384
+ }
385
+ },
386
+ editable: !disabled,
387
+ secureTextEntry: secureTextEntry || type === "password",
388
+ keyboardType,
389
+ textContentType,
390
+ style: [
391
+ {
392
+ color,
393
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
394
+ flex: 1,
395
+ padding: 0,
396
+ textAlign: style?.textAlign || "left"
397
+ },
398
+ style
399
+ ],
400
+ placeholderTextColor,
401
+ maxLength,
402
+ testID: dataTestId || id,
403
+ accessibilityLabel: ariaLabel,
404
+ accessibilityHint: ariaDescribedBy,
405
+ accessibilityState: {
406
+ disabled: disabled || ariaDisabled
407
+ },
408
+ accessible: true
409
+ }
410
+ );
411
+ }
412
+ );
413
+ InputPrimitive.displayName = "InputPrimitive";
414
+
415
+ // ../primitives-native/src/TextArea.tsx
416
+ import { forwardRef as forwardRef2 } from "react";
417
+ import { TextInput as RNTextInput2 } from "react-native";
418
+ import { jsx as jsx7 } from "react/jsx-runtime";
419
+ var TextAreaPrimitive = forwardRef2(
420
+ ({
421
+ value,
422
+ placeholder,
423
+ onChange,
424
+ onChangeText,
425
+ onFocus,
426
+ onBlur,
427
+ onKeyDown,
428
+ disabled,
429
+ style,
430
+ color,
431
+ fontSize,
432
+ placeholderTextColor,
433
+ maxLength,
434
+ rows,
435
+ id,
436
+ "aria-invalid": ariaInvalid,
437
+ "aria-describedby": ariaDescribedBy,
438
+ "aria-labelledby": ariaLabelledBy,
439
+ "aria-label": ariaLabel,
440
+ "aria-disabled": ariaDisabled,
441
+ "data-testid": dataTestId
442
+ }, ref) => {
443
+ const handleChangeText = (text) => {
444
+ onChangeText?.(text);
445
+ if (onChange) {
446
+ const syntheticEvent = {
447
+ target: { value: text },
448
+ currentTarget: { value: text },
449
+ type: "change",
450
+ nativeEvent: { text },
451
+ preventDefault: () => {
452
+ },
453
+ stopPropagation: () => {
454
+ },
455
+ isTrusted: false
456
+ };
457
+ onChange(syntheticEvent);
458
+ }
459
+ };
460
+ return /* @__PURE__ */ jsx7(
461
+ RNTextInput2,
462
+ {
463
+ ref,
464
+ value,
465
+ placeholder,
466
+ onChangeText: handleChangeText,
467
+ onFocus,
468
+ onBlur,
469
+ onKeyPress: (e) => {
470
+ if (onKeyDown) {
471
+ onKeyDown({
472
+ key: e.nativeEvent.key,
473
+ preventDefault: () => {
474
+ }
475
+ });
476
+ }
477
+ },
478
+ editable: !disabled,
479
+ multiline: true,
480
+ numberOfLines: rows || 4,
481
+ style: [
482
+ {
483
+ color,
484
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
485
+ flex: 1,
486
+ padding: 0,
487
+ textAlignVertical: "top",
488
+ textAlign: style?.textAlign || "left"
489
+ },
490
+ style
491
+ ],
492
+ placeholderTextColor,
493
+ maxLength,
494
+ testID: dataTestId || id,
495
+ accessibilityLabel: ariaLabel,
496
+ accessibilityHint: ariaDescribedBy,
497
+ accessibilityState: {
498
+ disabled: disabled || ariaDisabled
499
+ },
500
+ accessible: true
501
+ }
502
+ );
503
+ }
504
+ );
505
+ TextAreaPrimitive.displayName = "TextAreaPrimitive";
506
+
507
+ // src/Button.tsx
508
+ import { useDesignSystem } from "@xsolla/xui-core";
509
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
510
+ var Button = ({
511
+ variant = "primary",
512
+ tone = "brand",
513
+ size = "m",
514
+ disabled = false,
515
+ loading = false,
516
+ children,
517
+ onPress,
518
+ iconLeft,
519
+ iconRight,
520
+ "aria-label": ariaLabel,
521
+ "aria-describedby": ariaDescribedBy,
522
+ "aria-expanded": ariaExpanded,
523
+ "aria-haspopup": ariaHasPopup,
524
+ "aria-pressed": ariaPressed,
525
+ "aria-controls": ariaControls,
526
+ testID,
527
+ id,
528
+ type = "button",
529
+ fullWidth = false
530
+ }) => {
531
+ const { theme } = useDesignSystem();
532
+ const [isKeyboardPressed, setIsKeyboardPressed] = useState(false);
533
+ const isDisabled = disabled || loading;
534
+ const sizeStyles = theme.sizing.button(size);
535
+ const variantStyles = theme?.colors?.control?.[tone]?.[variant] || theme?.colors?.control?.brand?.primary || {
536
+ bg: "transparent",
537
+ text: { primary: "#000" }
538
+ };
539
+ const handlePress = () => {
540
+ if (!isDisabled && onPress) {
541
+ onPress();
542
+ }
543
+ };
544
+ const handleKeyDown = (e) => {
545
+ if (isDisabled) return;
546
+ if (e.key === "Enter" || e.key === " ") {
547
+ e.preventDefault();
548
+ setIsKeyboardPressed(true);
549
+ }
550
+ };
551
+ const handleKeyUp = (e) => {
552
+ if (isDisabled) return;
553
+ if (e.key === "Enter" || e.key === " ") {
554
+ e.preventDefault();
555
+ setIsKeyboardPressed(false);
556
+ if (onPress) {
557
+ onPress();
558
+ }
559
+ }
560
+ };
561
+ const styles = variantStyles;
562
+ let backgroundColor = styles.bg;
563
+ if (disabled) {
564
+ backgroundColor = styles.bgDisable || styles.bg;
565
+ } else if (isKeyboardPressed) {
566
+ backgroundColor = styles.bgPress || styles.bg;
567
+ }
568
+ const borderColor = disabled ? styles.borderDisable || styles.border : styles.border;
569
+ const textColor = disabled ? styles.text?.disable || styles.text?.primary : styles.text?.primary;
570
+ const isDarkText = textColor === "#000000" || textColor === "black" || textColor.startsWith("rgba(0, 0, 0");
571
+ const dividerColor = isDarkText ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.2)";
572
+ const computedAriaLabel = ariaLabel || (typeof children === "string" ? children : void 0);
573
+ return /* @__PURE__ */ jsxs(
574
+ Box,
575
+ {
576
+ as: "button",
577
+ type,
578
+ id,
579
+ onPress: handlePress,
580
+ onKeyDown: handleKeyDown,
581
+ onKeyUp: handleKeyUp,
582
+ disabled: isDisabled,
583
+ "aria-label": computedAriaLabel,
584
+ "aria-disabled": isDisabled || void 0,
585
+ "aria-busy": loading || void 0,
586
+ "aria-describedby": ariaDescribedBy,
587
+ "aria-expanded": ariaExpanded,
588
+ "aria-haspopup": ariaHasPopup,
589
+ "aria-pressed": ariaPressed,
590
+ "aria-controls": ariaControls,
591
+ testID,
592
+ backgroundColor,
593
+ borderColor,
594
+ borderWidth: borderColor !== "transparent" && borderColor !== "rgba(255, 255, 255, 0)" ? 1 : 0,
595
+ borderRadius: theme.radius.button,
596
+ height: sizeStyles.height,
597
+ width: fullWidth ? "100%" : void 0,
598
+ padding: 0,
599
+ flexDirection: "row",
600
+ alignItems: "center",
601
+ justifyContent: "center",
602
+ position: "relative",
603
+ cursor: disabled ? "not-allowed" : loading ? "wait" : "pointer",
604
+ opacity: disabled ? 0.6 : 1,
605
+ hoverStyle: !isDisabled ? {
606
+ backgroundColor: variantStyles?.bgHover
607
+ } : void 0,
608
+ pressStyle: !isDisabled ? {
609
+ backgroundColor: variantStyles?.bgPress
610
+ } : void 0,
611
+ focusStyle: {
612
+ outlineColor: theme.colors.border.brand,
613
+ outlineWidth: 2,
614
+ outlineOffset: 2,
615
+ outlineStyle: "solid"
616
+ },
617
+ children: [
618
+ !loading && iconLeft && /* @__PURE__ */ jsxs(
619
+ Box,
620
+ {
621
+ height: "100%",
622
+ flexDirection: "row",
623
+ alignItems: "center",
624
+ justifyContent: "center",
625
+ "aria-hidden": true,
626
+ children: [
627
+ /* @__PURE__ */ jsx8(
628
+ Box,
629
+ {
630
+ alignItems: "center",
631
+ justifyContent: "center",
632
+ paddingHorizontal: sizeStyles.iconPadding,
633
+ children: /* @__PURE__ */ jsx8(Icon, { size: sizeStyles.iconSize, color: textColor, children: iconLeft })
634
+ }
635
+ ),
636
+ /* @__PURE__ */ jsx8(Divider, { vertical: true, color: dividerColor, height: "100%" })
637
+ ]
638
+ }
639
+ ),
640
+ /* @__PURE__ */ jsx8(
641
+ Box,
642
+ {
643
+ flex: fullWidth ? 1 : void 0,
644
+ flexDirection: "row",
645
+ alignItems: "center",
646
+ justifyContent: "center",
647
+ paddingHorizontal: loading ? sizeStyles.loadingPadding : sizeStyles.padding,
648
+ height: "100%",
649
+ children: loading ? /* @__PURE__ */ jsx8(
650
+ Spinner,
651
+ {
652
+ color: textColor,
653
+ size: sizeStyles.spinnerSize,
654
+ "aria-hidden": true
655
+ }
656
+ ) : /* @__PURE__ */ jsx8(
657
+ Text,
658
+ {
659
+ color: textColor,
660
+ fontSize: sizeStyles.fontSize,
661
+ fontWeight: "500",
662
+ "aria-hidden": computedAriaLabel ? true : void 0,
663
+ children
664
+ }
665
+ )
666
+ }
667
+ ),
668
+ !loading && iconRight && /* @__PURE__ */ jsxs(
669
+ Box,
670
+ {
671
+ height: "100%",
672
+ flexDirection: "row",
673
+ alignItems: "center",
674
+ justifyContent: "center",
675
+ "aria-hidden": true,
676
+ children: [
677
+ /* @__PURE__ */ jsx8(Divider, { vertical: true, color: dividerColor, height: "100%" }),
678
+ /* @__PURE__ */ jsx8(
679
+ Box,
680
+ {
681
+ alignItems: "center",
682
+ justifyContent: "center",
683
+ paddingHorizontal: sizeStyles.iconPadding,
684
+ children: /* @__PURE__ */ jsx8(Icon, { size: sizeStyles.iconSize, color: textColor, children: iconRight })
685
+ }
686
+ )
687
+ ]
688
+ }
689
+ )
690
+ ]
691
+ }
692
+ );
693
+ };
694
+ Button.displayName = "Button";
695
+
696
+ // src/IconButton.tsx
697
+ import { useState as useState2 } from "react";
698
+ import { useDesignSystem as useDesignSystem2 } from "@xsolla/xui-core";
699
+ import { jsx as jsx9 } from "react/jsx-runtime";
700
+ var IconButton = ({
701
+ variant = "primary",
702
+ tone = "brand",
703
+ size = "m",
704
+ disabled = false,
705
+ loading = false,
706
+ icon,
707
+ onPress,
708
+ "aria-label": ariaLabel,
709
+ "aria-describedby": ariaDescribedBy,
710
+ "aria-expanded": ariaExpanded,
711
+ "aria-haspopup": ariaHasPopup,
712
+ "aria-pressed": ariaPressed,
713
+ "aria-controls": ariaControls,
714
+ testID,
715
+ id,
716
+ type = "button"
717
+ }) => {
718
+ const { theme } = useDesignSystem2();
719
+ const [isKeyboardPressed, setIsKeyboardPressed] = useState2(false);
720
+ const isDisabled = disabled || loading;
721
+ const sizeStyles = theme.sizing.button(size);
722
+ const variantStyles = theme?.colors?.control?.[tone]?.[variant] || theme?.colors?.control?.brand?.primary || {
723
+ bg: "transparent",
724
+ text: { primary: "#000" }
725
+ };
726
+ const handlePress = () => {
727
+ if (!isDisabled && onPress) {
728
+ onPress();
729
+ }
730
+ };
731
+ const handleKeyDown = (e) => {
732
+ if (isDisabled) return;
733
+ if (e.key === "Enter" || e.key === " ") {
734
+ e.preventDefault();
735
+ setIsKeyboardPressed(true);
736
+ }
737
+ };
738
+ const handleKeyUp = (e) => {
739
+ if (isDisabled) return;
740
+ if (e.key === "Enter" || e.key === " ") {
741
+ e.preventDefault();
742
+ setIsKeyboardPressed(false);
743
+ if (onPress) {
744
+ onPress();
745
+ }
746
+ }
747
+ };
748
+ const styles = variantStyles;
749
+ let backgroundColor = styles.bg;
750
+ if (disabled) {
751
+ backgroundColor = styles.bgDisable || styles.bg;
752
+ } else if (isKeyboardPressed) {
753
+ backgroundColor = styles.bgPress || styles.bg;
754
+ }
755
+ const borderColor = disabled ? styles.borderDisable || styles.border : styles.border;
756
+ const textColor = disabled ? styles.text?.disable || styles.text?.primary : styles.text?.primary;
757
+ return /* @__PURE__ */ jsx9(
758
+ Box,
759
+ {
760
+ as: "button",
761
+ type,
762
+ id,
763
+ onPress: handlePress,
764
+ onKeyDown: handleKeyDown,
765
+ onKeyUp: handleKeyUp,
766
+ disabled: isDisabled,
767
+ "aria-label": ariaLabel,
768
+ "aria-disabled": isDisabled || void 0,
769
+ "aria-busy": loading || void 0,
770
+ "aria-describedby": ariaDescribedBy,
771
+ "aria-expanded": ariaExpanded,
772
+ "aria-haspopup": ariaHasPopup,
773
+ "aria-pressed": ariaPressed,
774
+ "aria-controls": ariaControls,
775
+ testID,
776
+ backgroundColor,
777
+ borderColor,
778
+ borderWidth: borderColor !== "transparent" && borderColor !== "rgba(255, 255, 255, 0)" ? 1 : 0,
779
+ borderRadius: theme.radius.button,
780
+ height: sizeStyles.height,
781
+ width: sizeStyles.height,
782
+ padding: 0,
783
+ flexDirection: "row",
784
+ alignItems: "center",
785
+ justifyContent: "center",
786
+ position: "relative",
787
+ cursor: disabled ? "not-allowed" : loading ? "wait" : "pointer",
788
+ opacity: disabled ? 0.6 : 1,
789
+ hoverStyle: !isDisabled ? {
790
+ backgroundColor: styles.bgHover
791
+ } : void 0,
792
+ pressStyle: !isDisabled ? {
793
+ backgroundColor: styles.bgPress
794
+ } : void 0,
795
+ focusStyle: {
796
+ outlineColor: theme.colors.border.brand,
797
+ outlineWidth: 2,
798
+ outlineOffset: 2,
799
+ outlineStyle: "solid"
800
+ },
801
+ children: loading ? /* @__PURE__ */ jsx9(
802
+ Spinner,
803
+ {
804
+ color: textColor,
805
+ size: sizeStyles.spinnerSize,
806
+ "aria-hidden": true
807
+ }
808
+ ) : /* @__PURE__ */ jsx9(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: icon })
809
+ }
810
+ );
811
+ };
812
+ IconButton.displayName = "IconButton";
813
+
814
+ // src/FlexButton.tsx
815
+ import { useRef, useState as useState3 } from "react";
816
+ import { useDesignSystem as useDesignSystem3 } from "@xsolla/xui-core";
817
+ import { Fragment, jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
818
+ var ICON_SIZES = {
819
+ xs: 12,
820
+ s: 14,
821
+ m: 16,
822
+ l: 18,
823
+ xl: 20
824
+ };
825
+ var SPINNER_SIZES = {
826
+ xs: 12,
827
+ s: 14,
828
+ m: 16,
829
+ l: 18,
830
+ xl: 20
831
+ };
832
+ var LINE_HEIGHTS = {
833
+ xs: "14px",
834
+ s: "16px",
835
+ m: "18px",
836
+ l: "20px",
837
+ xl: "22px"
838
+ };
839
+ var FONT_SIZES = {
840
+ xs: 12,
841
+ s: 14,
842
+ m: 14,
843
+ l: 16,
844
+ xl: 18
845
+ };
846
+ var BORDER_RADIUS = {
847
+ xl: 4,
848
+ l: 4,
849
+ m: 2,
850
+ s: 2,
851
+ xs: 2
852
+ };
853
+ var FlexButton = ({
854
+ children,
855
+ variant = "brand",
856
+ size = "m",
857
+ background = false,
858
+ disabled = false,
859
+ loading = false,
860
+ iconLeft,
861
+ iconRight,
862
+ onPress,
863
+ onClick,
864
+ className,
865
+ type = "button",
866
+ "aria-label": ariaLabel,
867
+ "aria-describedby": ariaDescribedBy,
868
+ "aria-expanded": ariaExpanded,
869
+ "aria-haspopup": ariaHasPopup,
870
+ "aria-pressed": ariaPressed,
871
+ "aria-controls": ariaControls,
872
+ testID,
873
+ tabIndex = 0,
874
+ ...buttonProps
875
+ }) => {
876
+ const { theme } = useDesignSystem3();
877
+ const [state, setState] = useState3("default");
878
+ const [isFocused, setIsFocused] = useState3(false);
879
+ const isMouseOverRef = useRef(false);
880
+ const isDisabled = disabled || loading;
881
+ const getVariantColors = (currentState) => {
882
+ if (isDisabled) {
883
+ return {
884
+ bg: background ? theme.colors.overlay.mono : "transparent",
885
+ text: theme.colors.control.text.disable,
886
+ border: void 0
887
+ };
888
+ }
889
+ const effectiveBackground = loading ? false : background;
890
+ switch (variant) {
891
+ case "brand":
892
+ if (effectiveBackground) {
893
+ return {
894
+ bg: currentState === "press" ? theme.colors.control.brand.primary.bgPress : currentState === "hover" ? theme.colors.control.brand.primary.bgHover : theme.colors.background.brand.primary,
895
+ text: theme.colors.content.on.brand,
896
+ border: void 0
897
+ };
898
+ }
899
+ return {
900
+ bg: currentState === "press" ? theme.colors.background.brand.primary : currentState === "hover" ? theme.colors.overlay.brand : "transparent",
901
+ text: currentState === "press" ? theme.colors.content.on.brand : theme.colors.content.brand.primary,
902
+ border: void 0
903
+ };
904
+ case "primary":
905
+ if (effectiveBackground) {
906
+ return {
907
+ bg: theme.colors.background.primary,
908
+ text: theme.colors.content.primary,
909
+ border: currentState === "press" ? theme.colors.border.primary : void 0
910
+ };
911
+ }
912
+ return {
913
+ bg: currentState === "press" || currentState === "hover" ? theme.colors.overlay.mono : "transparent",
914
+ text: theme.colors.content.primary,
915
+ border: currentState === "press" ? theme.colors.border.primary : void 0
916
+ };
917
+ case "secondary":
918
+ if (effectiveBackground) {
919
+ return {
920
+ bg: currentState === "press" ? theme.colors.control.mono.secondary.bgPress : currentState === "hover" ? theme.colors.control.mono.secondary.bgHover : theme.colors.background.secondary,
921
+ text: theme.colors.content.secondary,
922
+ border: void 0
923
+ };
924
+ }
925
+ return {
926
+ bg: currentState === "press" || currentState === "hover" ? theme.colors.overlay.mono : "transparent",
927
+ text: currentState === "press" ? theme.colors.content.primary : currentState === "hover" ? theme.colors.content.secondary : theme.colors.content.secondary,
928
+ border: void 0
929
+ };
930
+ case "tertiary":
931
+ if (effectiveBackground) {
932
+ return {
933
+ bg: currentState === "press" ? theme.colors.control.mono.secondary.bgPress : currentState === "hover" ? theme.colors.control.mono.secondary.bgHover : theme.colors.background.secondary,
934
+ text: theme.colors.content.tertiary,
935
+ border: void 0
936
+ };
937
+ }
938
+ return {
939
+ bg: currentState === "press" || currentState === "hover" ? theme.colors.overlay.mono : "transparent",
940
+ text: currentState === "press" ? theme.colors.content.secondary : currentState === "hover" ? theme.colors.content.tertiary : theme.colors.content.tertiary,
941
+ border: void 0
942
+ };
943
+ case "brandExtra":
944
+ if (effectiveBackground) {
945
+ return {
946
+ bg: currentState === "press" ? theme.colors.control.brandExtra.primary.bgPress : currentState === "hover" ? theme.colors.control.brandExtra.primary.bgHover : theme.colors.background.brandExtra.primary,
947
+ text: theme.colors.content.on.brandExtra,
948
+ border: void 0
949
+ };
950
+ }
951
+ return {
952
+ bg: currentState === "press" ? theme.colors.background.brandExtra.primary : currentState === "hover" ? theme.colors.overlay.brandExtra : "transparent",
953
+ text: currentState === "press" ? theme.colors.content.on.brandExtra : theme.colors.content.brandExtra.secondary,
954
+ border: void 0
955
+ };
956
+ case "inverse":
957
+ if (effectiveBackground) {
958
+ return {
959
+ bg: currentState === "press" ? theme.colors.control.mono.primary.bgPress : currentState === "hover" ? theme.colors.control.mono.primary.bgHover : theme.colors.background.inverse,
960
+ text: theme.colors.content.inverse,
961
+ border: void 0
962
+ };
963
+ }
964
+ return {
965
+ bg: currentState === "press" || currentState === "hover" ? theme.colors.overlay.mono : "transparent",
966
+ text: theme.colors.content.inverse,
967
+ border: void 0
968
+ };
969
+ default:
970
+ return {
971
+ bg: "transparent",
972
+ text: theme.colors.content.primary,
973
+ border: void 0
974
+ };
975
+ }
976
+ };
977
+ const getFocusRingColor = () => {
978
+ switch (variant) {
979
+ case "brand":
980
+ return theme.colors.overlay.brand;
981
+ case "brandExtra":
982
+ return theme.colors.overlay.brandExtra;
983
+ case "inverse":
984
+ return "rgba(255, 255, 255, 0.3)";
985
+ default:
986
+ return theme.colors.overlay.mono;
987
+ }
988
+ };
989
+ const getSpinnerColor = () => {
990
+ switch (variant) {
991
+ case "brand":
992
+ return theme.colors.content.brand.primary;
993
+ case "primary":
994
+ return theme.colors.content.primary;
995
+ case "secondary":
996
+ return theme.colors.content.secondary;
997
+ case "tertiary":
998
+ return theme.colors.content.tertiary;
999
+ case "brandExtra":
1000
+ return theme.colors.content.brandExtra.secondary;
1001
+ case "inverse":
1002
+ return theme.colors.content.inverse;
1003
+ default:
1004
+ return theme.colors.content.brand.primary;
1005
+ }
1006
+ };
1007
+ const colors = getVariantColors(state);
1008
+ const focusRingColor = getFocusRingColor();
1009
+ const spinnerColor = getSpinnerColor();
1010
+ const iconSize = ICON_SIZES[size];
1011
+ const spinnerSize = SPINNER_SIZES[size];
1012
+ const fontSize = FONT_SIZES[size];
1013
+ const borderRadius = BORDER_RADIUS[size];
1014
+ const lineHeight = LINE_HEIGHTS[size];
1015
+ const handleMouseEnter = () => {
1016
+ if (!isDisabled) {
1017
+ isMouseOverRef.current = true;
1018
+ setState("hover");
1019
+ }
1020
+ };
1021
+ const handleMouseLeave = () => {
1022
+ if (!isDisabled) {
1023
+ isMouseOverRef.current = false;
1024
+ setState("default");
1025
+ }
1026
+ };
1027
+ const handleMouseDown = () => {
1028
+ if (!isDisabled) {
1029
+ setState("press");
1030
+ }
1031
+ };
1032
+ const handleMouseUp = () => {
1033
+ if (!isDisabled) {
1034
+ setState(isMouseOverRef.current ? "hover" : "default");
1035
+ }
1036
+ };
1037
+ const handleFocus = () => {
1038
+ if (!isDisabled) {
1039
+ setIsFocused(true);
1040
+ }
1041
+ };
1042
+ const handleBlur = () => {
1043
+ setIsFocused(false);
1044
+ };
1045
+ const handleClick = (event) => {
1046
+ if (isDisabled) return;
1047
+ if (onPress) {
1048
+ onPress();
1049
+ }
1050
+ if (onClick) {
1051
+ onClick(event);
1052
+ }
1053
+ };
1054
+ const handleKeyDown = (event) => {
1055
+ if (isDisabled) return;
1056
+ if (event.key === "Enter" || event.key === " ") {
1057
+ event.preventDefault();
1058
+ setState("press");
1059
+ }
1060
+ };
1061
+ const handleKeyUp = (event) => {
1062
+ if (isDisabled) return;
1063
+ if (event.key === "Enter" || event.key === " ") {
1064
+ event.preventDefault();
1065
+ setState(isMouseOverRef.current ? "hover" : "default");
1066
+ if (onPress) {
1067
+ onPress();
1068
+ }
1069
+ }
1070
+ };
1071
+ const borderShadow = colors.border ? `inset 0 0 0 1px ${colors.border}` : void 0;
1072
+ const focusShadow = isFocused && !isDisabled ? `0 0 0 2px ${focusRingColor}` : void 0;
1073
+ const boxShadows = [];
1074
+ if (borderShadow) boxShadows.push(borderShadow);
1075
+ if (focusShadow) boxShadows.push(focusShadow);
1076
+ const combinedBoxShadow = boxShadows.length > 0 ? boxShadows.join(", ") : "none";
1077
+ const buttonStyle = {
1078
+ display: "inline-flex",
1079
+ alignItems: "center",
1080
+ justifyContent: "center",
1081
+ gap: "2px",
1082
+ padding: "4px",
1083
+ backgroundColor: loading ? "transparent" : colors.bg,
1084
+ color: colors.text,
1085
+ border: "none",
1086
+ borderWidth: "0px",
1087
+ borderRadius: `${borderRadius}px`,
1088
+ cursor: isDisabled ? "not-allowed" : "pointer",
1089
+ fontSize: `${fontSize}px`,
1090
+ fontWeight: 500,
1091
+ lineHeight,
1092
+ fontFamily: "inherit",
1093
+ transition: "background-color 100ms ease-in-out, color 100ms ease-in-out, box-shadow 100ms ease-in-out",
1094
+ outline: "none",
1095
+ boxShadow: combinedBoxShadow,
1096
+ opacity: isDisabled && !loading ? 0.6 : 1
1097
+ };
1098
+ const contentStyle = {
1099
+ display: "flex",
1100
+ alignItems: "center",
1101
+ justifyContent: "center",
1102
+ gap: "2px"
1103
+ };
1104
+ const spinnerStyle = {
1105
+ display: "flex",
1106
+ alignItems: "center",
1107
+ justifyContent: "center",
1108
+ backgroundColor: "transparent",
1109
+ height: lineHeight
1110
+ };
1111
+ const computedAriaLabel = ariaLabel || (typeof children === "string" ? children : void 0);
1112
+ return /* @__PURE__ */ jsx10(
1113
+ "button",
1114
+ {
1115
+ ...buttonProps,
1116
+ type,
1117
+ className,
1118
+ disabled: isDisabled,
1119
+ onClick: handleClick,
1120
+ onMouseEnter: handleMouseEnter,
1121
+ onMouseLeave: handleMouseLeave,
1122
+ onMouseDown: handleMouseDown,
1123
+ onMouseUp: handleMouseUp,
1124
+ onKeyDown: handleKeyDown,
1125
+ onKeyUp: handleKeyUp,
1126
+ onFocus: handleFocus,
1127
+ onBlur: handleBlur,
1128
+ "aria-label": computedAriaLabel,
1129
+ "aria-busy": loading || void 0,
1130
+ "aria-disabled": isDisabled || void 0,
1131
+ "aria-describedby": ariaDescribedBy,
1132
+ "aria-expanded": ariaExpanded,
1133
+ "aria-haspopup": ariaHasPopup,
1134
+ "aria-pressed": ariaPressed,
1135
+ "aria-controls": ariaControls,
1136
+ tabIndex,
1137
+ style: buttonStyle,
1138
+ "data-testid": testID || "flex-button",
1139
+ children: /* @__PURE__ */ jsx10("span", { style: contentStyle, children: loading ? /* @__PURE__ */ jsx10("span", { style: spinnerStyle, children: /* @__PURE__ */ jsx10(Spinner, { size: spinnerSize, color: spinnerColor }) }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1140
+ iconLeft && /* @__PURE__ */ jsx10(Icon, { size: iconSize, color: colors.text, children: iconLeft }),
1141
+ /* @__PURE__ */ jsx10("span", { children }),
1142
+ iconRight && /* @__PURE__ */ jsx10(Icon, { size: iconSize, color: colors.text, children: iconRight })
1143
+ ] }) })
1144
+ }
1145
+ );
1146
+ };
1147
+ FlexButton.displayName = "FlexButton";
1148
+
1149
+ // src/ButtonGroup.tsx
1150
+ import { useDesignSystem as useDesignSystem4 } from "@xsolla/xui-core";
1151
+ import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
1152
+ var ButtonGroup = ({
1153
+ orientation = "horizontal",
1154
+ size = "m",
1155
+ children,
1156
+ description,
1157
+ error,
1158
+ gap,
1159
+ "aria-label": ariaLabel,
1160
+ "aria-labelledby": ariaLabelledBy,
1161
+ "aria-describedby": ariaDescribedBy,
1162
+ id,
1163
+ testID
1164
+ }) => {
1165
+ const { theme } = useDesignSystem4();
1166
+ const verticalGapMap = {
1167
+ xl: 16,
1168
+ l: 16,
1169
+ m: 12,
1170
+ s: 8,
1171
+ xs: 4
1172
+ };
1173
+ const horizontalGapMap = {
1174
+ xl: 16,
1175
+ l: 16,
1176
+ m: 16,
1177
+ s: 12,
1178
+ xs: 12
1179
+ };
1180
+ const computedGap = gap ?? (orientation === "vertical" ? verticalGapMap[size] : horizontalGapMap[size]);
1181
+ const descriptionId = id ? `${id}-description` : void 0;
1182
+ const errorId = id ? `${id}-error` : void 0;
1183
+ const computedAriaDescribedBy = [
1184
+ ariaDescribedBy,
1185
+ error && errorId ? errorId : void 0,
1186
+ description && !error && descriptionId ? descriptionId : void 0
1187
+ ].filter(Boolean).join(" ") || void 0;
1188
+ return /* @__PURE__ */ jsxs3(Box, { flexDirection: "column", width: "100%", gap: 8, children: [
1189
+ /* @__PURE__ */ jsx11(
1190
+ Box,
1191
+ {
1192
+ role: "group",
1193
+ "aria-label": ariaLabel,
1194
+ "aria-labelledby": ariaLabelledBy,
1195
+ "aria-describedby": computedAriaDescribedBy,
1196
+ id,
1197
+ testID,
1198
+ flexDirection: orientation === "horizontal" ? "row" : "column",
1199
+ alignItems: "stretch",
1200
+ gap: computedGap,
1201
+ width: "100%",
1202
+ children
1203
+ }
1204
+ ),
1205
+ error && /* @__PURE__ */ jsx11(Box, { marginTop: 4, children: /* @__PURE__ */ jsx11(
1206
+ Text,
1207
+ {
1208
+ id: errorId,
1209
+ role: "alert",
1210
+ "aria-live": "assertive",
1211
+ color: theme.colors.content.alert.primary,
1212
+ fontSize: 14,
1213
+ fontWeight: "400",
1214
+ children: error
1215
+ }
1216
+ ) }),
1217
+ description && !error && /* @__PURE__ */ jsx11(Box, { marginTop: 4, children: /* @__PURE__ */ jsx11(
1218
+ Text,
1219
+ {
1220
+ id: descriptionId,
1221
+ color: theme.colors.content.tertiary,
1222
+ fontSize: 14,
1223
+ fontWeight: "400",
1224
+ children: description
1225
+ }
1226
+ ) })
1227
+ ] });
1228
+ };
1229
+ ButtonGroup.displayName = "ButtonGroup";
1230
+ export {
1231
+ Button,
1232
+ ButtonGroup,
1233
+ FlexButton,
1234
+ IconButton
1235
+ };
1236
+ //# sourceMappingURL=index.mjs.map