@xsolla/xui-game-card 0.151.0 → 0.153.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,21 +2,34 @@ import React from 'react';
2
2
  import { ThemeOverrideProps } from '@xsolla/xui-core';
3
3
 
4
4
  type GameCardSize = "lg" | "md" | "sm";
5
+ type GameCardImageRatio = "16:9" | "1:1" | "2:3" | "3:2" | "4:3" | "9:16" | "custom";
6
+ type GameCardLayout = "horizontal" | "vertical";
7
+ type GameCardPlatform = "mobile" | "desktop";
5
8
  interface GameCardProps extends ThemeOverrideProps {
6
- /** Game artwork image URL */
7
- image: string;
9
+ /** Game artwork image URL. When omitted, a placeholder is rendered. */
10
+ image?: string;
8
11
  /** Game title */
9
12
  title: string;
10
13
  /** Game subtitle (e.g., genre tags) */
11
14
  subtitle?: string;
12
15
  /** Size variant of the card */
13
16
  size?: GameCardSize;
17
+ /** Aspect ratio of the image area (default "16:9") */
18
+ imageRatio?: GameCardImageRatio;
19
+ /** Custom width for the image area when imageRatio is "custom" */
20
+ customImageWidth?: number;
21
+ /** Custom height for the image area when imageRatio is "custom" */
22
+ customImageHeight?: number;
14
23
  /** Custom badge element displayed above tags on the image (top-left corner) */
15
24
  badge?: React.ReactNode;
16
25
  /** Custom content for top-left corner tags */
17
26
  tagsTopLeft?: React.ReactNode;
18
27
  /** Custom content for top-right corner tags */
19
28
  tagsTopRight?: React.ReactNode;
29
+ /** Custom content for bottom-left corner tags */
30
+ tagsBottomLeft?: React.ReactNode;
31
+ /** Custom content for bottom-right corner tags */
32
+ tagsBottomRight?: React.ReactNode;
20
33
  /** Custom trailing content (button, price, etc.) - overrides buttonText if provided */
21
34
  trailing?: React.ReactNode;
22
35
  /** Button label text (used if trailing is not provided) */
@@ -25,6 +38,14 @@ interface GameCardProps extends ThemeOverrideProps {
25
38
  onButtonClick?: () => void;
26
39
  /** Whether the button is disabled (used if trailing is not provided) */
27
40
  buttonDisabled?: boolean;
41
+ /** Footer layout direction (default "horizontal") */
42
+ layout?: GameCardLayout;
43
+ /** Platform variant affecting footer spacing (default "mobile") */
44
+ platform?: GameCardPlatform;
45
+ /** When true, hides the footer (title/subtitle/button area) entirely. Useful for image-only cards like XWP presets. */
46
+ hideFooter?: boolean;
47
+ /** Hover overlay content rendered at the bottom of the image area on hover */
48
+ hoverContent?: React.ReactNode;
28
49
  /** Card click handler */
29
50
  onPress?: () => void;
30
51
  /** Alt text for game image */
@@ -35,4 +56,4 @@ interface GameCardProps extends ThemeOverrideProps {
35
56
 
36
57
  declare const GameCard: React.FC<GameCardProps>;
37
58
 
38
- export { GameCard, type GameCardProps, type GameCardSize };
59
+ export { GameCard, type GameCardImageRatio, type GameCardLayout, type GameCardPlatform, type GameCardProps, type GameCardSize };
package/native/index.d.ts CHANGED
@@ -2,21 +2,34 @@ import React from 'react';
2
2
  import { ThemeOverrideProps } from '@xsolla/xui-core';
3
3
 
4
4
  type GameCardSize = "lg" | "md" | "sm";
5
+ type GameCardImageRatio = "16:9" | "1:1" | "2:3" | "3:2" | "4:3" | "9:16" | "custom";
6
+ type GameCardLayout = "horizontal" | "vertical";
7
+ type GameCardPlatform = "mobile" | "desktop";
5
8
  interface GameCardProps extends ThemeOverrideProps {
6
- /** Game artwork image URL */
7
- image: string;
9
+ /** Game artwork image URL. When omitted, a placeholder is rendered. */
10
+ image?: string;
8
11
  /** Game title */
9
12
  title: string;
10
13
  /** Game subtitle (e.g., genre tags) */
11
14
  subtitle?: string;
12
15
  /** Size variant of the card */
13
16
  size?: GameCardSize;
17
+ /** Aspect ratio of the image area (default "16:9") */
18
+ imageRatio?: GameCardImageRatio;
19
+ /** Custom width for the image area when imageRatio is "custom" */
20
+ customImageWidth?: number;
21
+ /** Custom height for the image area when imageRatio is "custom" */
22
+ customImageHeight?: number;
14
23
  /** Custom badge element displayed above tags on the image (top-left corner) */
15
24
  badge?: React.ReactNode;
16
25
  /** Custom content for top-left corner tags */
17
26
  tagsTopLeft?: React.ReactNode;
18
27
  /** Custom content for top-right corner tags */
19
28
  tagsTopRight?: React.ReactNode;
29
+ /** Custom content for bottom-left corner tags */
30
+ tagsBottomLeft?: React.ReactNode;
31
+ /** Custom content for bottom-right corner tags */
32
+ tagsBottomRight?: React.ReactNode;
20
33
  /** Custom trailing content (button, price, etc.) - overrides buttonText if provided */
21
34
  trailing?: React.ReactNode;
22
35
  /** Button label text (used if trailing is not provided) */
@@ -25,6 +38,14 @@ interface GameCardProps extends ThemeOverrideProps {
25
38
  onButtonClick?: () => void;
26
39
  /** Whether the button is disabled (used if trailing is not provided) */
27
40
  buttonDisabled?: boolean;
41
+ /** Footer layout direction (default "horizontal") */
42
+ layout?: GameCardLayout;
43
+ /** Platform variant affecting footer spacing (default "mobile") */
44
+ platform?: GameCardPlatform;
45
+ /** When true, hides the footer (title/subtitle/button area) entirely. Useful for image-only cards like XWP presets. */
46
+ hideFooter?: boolean;
47
+ /** Hover overlay content rendered at the bottom of the image area on hover */
48
+ hoverContent?: React.ReactNode;
28
49
  /** Card click handler */
29
50
  onPress?: () => void;
30
51
  /** Alt text for game image */
@@ -35,4 +56,4 @@ interface GameCardProps extends ThemeOverrideProps {
35
56
 
36
57
  declare const GameCard: React.FC<GameCardProps>;
37
58
 
38
- export { GameCard, type GameCardProps, type GameCardSize };
59
+ export { GameCard, type GameCardImageRatio, type GameCardLayout, type GameCardPlatform, type GameCardProps, type GameCardSize };
package/native/index.js CHANGED
@@ -34,6 +34,9 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
+ // src/GameCard.tsx
38
+ var import_react2 = require("react");
39
+
37
40
  // ../../foundation/primitives-native/src/Box.tsx
38
41
  var import_react_native = require("react-native");
39
42
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -381,76 +384,77 @@ var isWeb = false;
381
384
  // src/GameCard.tsx
382
385
  var import_xui_core = require("@xsolla/xui-core");
383
386
  var import_jsx_runtime4 = require("react/jsx-runtime");
384
- var getSizeConfig = (size) => {
387
+ var IMAGE_RATIOS = {
388
+ "16:9": 240 / 135,
389
+ "1:1": 1,
390
+ "2:3": 240 / 360,
391
+ "3:2": 240 / 160,
392
+ "4:3": 360 / 270,
393
+ "9:16": 135 / 240
394
+ };
395
+ var BORDER_RADIUS = 8;
396
+ var TAG_INSET = 8;
397
+ var TAG_GAP = 4;
398
+ var getSizeConfig = (size, platform) => {
399
+ const isDesktop = platform === "desktop";
385
400
  switch (size) {
386
401
  case "lg":
387
402
  return {
388
- imageAspectRatio: 366 / 206,
389
- titleFontSize: 16,
390
- titleLineHeight: 20,
391
- subtitleFontSize: 12,
392
- subtitleLineHeight: 18,
393
- padding: 8,
394
- footerPadding: 12,
395
- footerGap: 12,
396
- tagGap: 4,
397
- buttonHeight: 32,
398
- buttonPaddingX: 20,
399
- buttonFontSize: 12,
400
- buttonLineHeight: 14,
401
- borderRadius: 8,
402
- fadeHeight: 64
403
+ titleFontSize: isDesktop ? 16 : 14,
404
+ titleLineHeight: isDesktop ? 20 : 18,
405
+ subtitleFontSize: isDesktop ? 12 : 11,
406
+ subtitleLineHeight: isDesktop ? 18 : 14
403
407
  };
404
408
  case "md":
405
409
  return {
406
- imageAspectRatio: 280 / 158,
407
- titleFontSize: 14,
408
- titleLineHeight: 18,
409
- subtitleFontSize: 11,
410
- subtitleLineHeight: 16,
411
- padding: 6,
412
- footerPadding: 8,
413
- footerGap: 8,
414
- tagGap: 3,
415
- buttonHeight: 28,
416
- buttonPaddingX: 16,
417
- buttonFontSize: 11,
418
- buttonLineHeight: 13,
419
- borderRadius: 8,
420
- fadeHeight: 48
410
+ titleFontSize: isDesktop ? 16 : 14,
411
+ titleLineHeight: isDesktop ? 20 : 18,
412
+ subtitleFontSize: isDesktop ? 12 : 11,
413
+ subtitleLineHeight: isDesktop ? 18 : 14
421
414
  };
422
415
  case "sm":
423
416
  return {
424
- imageAspectRatio: 200 / 113,
425
417
  titleFontSize: 12,
426
418
  titleLineHeight: 16,
427
419
  subtitleFontSize: 10,
428
- subtitleLineHeight: 14,
429
- padding: 4,
430
- footerPadding: 6,
431
- footerGap: 6,
432
- tagGap: 2,
433
- buttonHeight: 24,
434
- buttonPaddingX: 12,
435
- buttonFontSize: 10,
436
- buttonLineHeight: 12,
437
- borderRadius: 8,
438
- fadeHeight: 32
420
+ subtitleLineHeight: 14
439
421
  };
440
422
  }
441
423
  };
424
+ var getFooterPadding = (platform) => platform === "desktop" ? 16 : 12;
425
+ var getFooterGap = () => 12;
426
+ var resolveAspectRatio = (imageRatio, customWidth, customHeight) => {
427
+ if (imageRatio === "custom" && customWidth && customHeight) {
428
+ return customWidth / customHeight;
429
+ }
430
+ return IMAGE_RATIOS[imageRatio] ?? IMAGE_RATIOS["16:9"];
431
+ };
432
+ var ellipsisStyle = isWeb ? {
433
+ overflow: "hidden",
434
+ textOverflow: "ellipsis",
435
+ whiteSpace: "nowrap"
436
+ } : void 0;
442
437
  var GameCard = ({
443
438
  image,
444
439
  title,
445
440
  subtitle,
446
441
  size = "lg",
442
+ imageRatio = "16:9",
443
+ customImageWidth,
444
+ customImageHeight,
447
445
  badge,
448
446
  tagsTopLeft,
449
447
  tagsTopRight,
448
+ tagsBottomLeft,
449
+ tagsBottomRight,
450
450
  trailing,
451
451
  buttonText,
452
452
  onButtonClick,
453
453
  buttonDisabled,
454
+ layout = "horizontal",
455
+ platform = "mobile",
456
+ hideFooter = false,
457
+ hoverContent,
454
458
  onPress,
455
459
  imageAlt,
456
460
  className,
@@ -458,8 +462,247 @@ var GameCard = ({
458
462
  themeProductContext
459
463
  }) => {
460
464
  const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
461
- const config = getSizeConfig(size);
465
+ const config = getSizeConfig(size, platform);
466
+ const footerPadding = getFooterPadding(platform);
467
+ const footerGap = getFooterGap();
468
+ const aspectRatio = resolveAspectRatio(
469
+ imageRatio,
470
+ customImageWidth,
471
+ customImageHeight
472
+ );
473
+ const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
462
474
  const wrapperProps = onPress ? { onPress } : {};
475
+ const hoverHandlers = isWeb && hoverContent ? {
476
+ onMouseEnter: () => setIsHovered(true),
477
+ onMouseLeave: () => setIsHovered(false)
478
+ } : {};
479
+ const isHorizontal = layout === "horizontal";
480
+ const renderPlaceholder = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
481
+ Box,
482
+ {
483
+ width: "100%",
484
+ height: "100%",
485
+ backgroundColor: theme.colors.background.secondary,
486
+ alignItems: "center",
487
+ justifyContent: "center",
488
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
489
+ Box,
490
+ {
491
+ width: imageRatio === "16:9" || imageRatio === "9:16" || imageRatio === "custom" ? 48 : 56,
492
+ height: imageRatio === "16:9" || imageRatio === "9:16" || imageRatio === "custom" ? 48 : 56,
493
+ alignItems: "center",
494
+ justifyContent: "center",
495
+ style: { opacity: 0.4 },
496
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
497
+ Text,
498
+ {
499
+ color: theme.colors.content.tertiary,
500
+ fontSize: 24,
501
+ style: { textAlign: "center" },
502
+ children: "\u{1F5BC}"
503
+ }
504
+ )
505
+ }
506
+ )
507
+ }
508
+ );
509
+ const renderImage = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
510
+ Box,
511
+ {
512
+ position: "relative",
513
+ width: "100%",
514
+ overflow: "hidden",
515
+ style: { aspectRatio },
516
+ ...hoverHandlers,
517
+ children: [
518
+ image ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
519
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
520
+ Box,
521
+ {
522
+ as: "img",
523
+ src: image,
524
+ alt: imageAlt || title,
525
+ position: "absolute",
526
+ top: 0,
527
+ left: 0,
528
+ width: "100%",
529
+ height: "100%",
530
+ style: isWeb ? {
531
+ objectFit: "cover",
532
+ pointerEvents: "none"
533
+ } : void 0
534
+ }
535
+ ),
536
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
537
+ LinearGradient,
538
+ {
539
+ colors: ["transparent", "rgba(0, 0, 0, 0.48)"],
540
+ start: { x: 0, y: 0 },
541
+ end: { x: 0, y: 1 },
542
+ position: "absolute",
543
+ bottom: 0,
544
+ left: 0,
545
+ right: 0,
546
+ height: "100%",
547
+ style: { maxHeight: 206 }
548
+ }
549
+ )
550
+ ] }) : renderPlaceholder(),
551
+ (badge || tagsTopLeft) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
552
+ Box,
553
+ {
554
+ position: "absolute",
555
+ top: TAG_INSET,
556
+ left: TAG_INSET,
557
+ flexDirection: "column",
558
+ gap: TAG_GAP,
559
+ alignItems: "flex-start",
560
+ children: [
561
+ badge,
562
+ tagsTopLeft
563
+ ]
564
+ }
565
+ ),
566
+ tagsTopRight && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
567
+ Box,
568
+ {
569
+ position: "absolute",
570
+ top: TAG_INSET,
571
+ right: TAG_INSET,
572
+ flexDirection: "column",
573
+ gap: TAG_GAP,
574
+ alignItems: "flex-end",
575
+ children: tagsTopRight
576
+ }
577
+ ),
578
+ tagsBottomLeft && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
579
+ Box,
580
+ {
581
+ position: "absolute",
582
+ bottom: TAG_INSET,
583
+ left: TAG_INSET,
584
+ flexDirection: "column",
585
+ gap: TAG_GAP,
586
+ alignItems: "flex-start",
587
+ children: tagsBottomLeft
588
+ }
589
+ ),
590
+ tagsBottomRight && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
591
+ Box,
592
+ {
593
+ position: "absolute",
594
+ bottom: TAG_INSET,
595
+ right: TAG_INSET,
596
+ flexDirection: "column",
597
+ gap: TAG_GAP,
598
+ alignItems: "flex-end",
599
+ children: tagsBottomRight
600
+ }
601
+ ),
602
+ isWeb && hoverContent && isHovered && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
603
+ Box,
604
+ {
605
+ position: "absolute",
606
+ bottom: 0,
607
+ left: 0,
608
+ right: 0,
609
+ height: 72,
610
+ flexDirection: "row",
611
+ alignItems: "center",
612
+ gap: 8,
613
+ padding: 16,
614
+ borderRadius: 4,
615
+ overflow: "hidden",
616
+ style: {
617
+ backdropFilter: "blur(15px)",
618
+ WebkitBackdropFilter: "blur(15px)",
619
+ backgroundColor: theme.colors.layer.float
620
+ },
621
+ children: hoverContent
622
+ }
623
+ )
624
+ ]
625
+ }
626
+ );
627
+ const renderFooter = () => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
628
+ Box,
629
+ {
630
+ backgroundColor: theme.colors.overlay.mono,
631
+ padding: footerPadding,
632
+ flexDirection: isHorizontal ? "row" : "column",
633
+ alignItems: isHorizontal ? "center" : "flex-start",
634
+ justifyContent: isHorizontal ? void 0 : "center",
635
+ gap: footerGap,
636
+ children: [
637
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
638
+ Box,
639
+ {
640
+ flex: isHorizontal ? 1 : void 0,
641
+ width: isHorizontal ? void 0 : "100%",
642
+ flexDirection: "column",
643
+ style: { minWidth: 0 },
644
+ children: [
645
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
646
+ Text,
647
+ {
648
+ color: theme.colors.content.primary,
649
+ fontSize: config.titleFontSize,
650
+ lineHeight: config.titleLineHeight,
651
+ fontWeight: "500",
652
+ numberOfLines: 1,
653
+ style: ellipsisStyle,
654
+ children: title
655
+ }
656
+ ),
657
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
658
+ Text,
659
+ {
660
+ color: theme.colors.content.tertiary,
661
+ fontSize: config.subtitleFontSize,
662
+ lineHeight: config.subtitleLineHeight,
663
+ fontWeight: "400",
664
+ numberOfLines: 1,
665
+ style: ellipsisStyle,
666
+ children: subtitle
667
+ }
668
+ )
669
+ ]
670
+ }
671
+ ),
672
+ trailing,
673
+ !trailing && buttonText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
674
+ Box,
675
+ {
676
+ onPress: buttonDisabled ? void 0 : onButtonClick,
677
+ backgroundColor: theme.colors.control.mono.secondary.bg,
678
+ borderRadius: BORDER_RADIUS,
679
+ height: 40,
680
+ paddingHorizontal: 20,
681
+ alignItems: "center",
682
+ justifyContent: "center",
683
+ style: isWeb ? {
684
+ opacity: buttonDisabled ? 0.4 : 1,
685
+ backdropFilter: "blur(30px)",
686
+ WebkitBackdropFilter: "blur(30px)",
687
+ border: `1px solid ${theme.colors.control.mono.secondary.border}`,
688
+ cursor: buttonDisabled ? "not-allowed" : onButtonClick ? "pointer" : "default"
689
+ } : { opacity: buttonDisabled ? 0.4 : 1 },
690
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
691
+ Text,
692
+ {
693
+ color: theme.colors.control.mono.text.secondary,
694
+ fontSize: 14,
695
+ lineHeight: 18,
696
+ fontWeight: "500",
697
+ style: { letterSpacing: 0.4 },
698
+ children: buttonText
699
+ }
700
+ )
701
+ }
702
+ )
703
+ ]
704
+ }
705
+ );
463
706
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
464
707
  Box,
465
708
  {
@@ -470,170 +713,22 @@ var GameCard = ({
470
713
  children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
471
714
  Box,
472
715
  {
473
- backgroundColor: theme.colors.background.primary,
474
- borderRadius: config.borderRadius,
716
+ borderRadius: BORDER_RADIUS,
475
717
  overflow: "hidden",
476
718
  flexDirection: "column",
477
719
  style: isWeb ? {
478
- boxShadow: theme.shadow.surface,
479
- borderRadius: `${config.borderRadius}px`,
480
- overflow: "hidden"
720
+ borderRadius: `${BORDER_RADIUS}px`,
721
+ overflow: "hidden",
722
+ border: `1px solid ${theme.colors.border.secondary}`
481
723
  } : {
482
- shadowColor: "#070708",
483
- shadowOffset: { width: 0, height: 2 },
484
- shadowOpacity: 0.15,
485
- shadowRadius: 6,
486
- elevation: 4,
487
- borderRadius: config.borderRadius,
488
- overflow: "hidden"
724
+ borderRadius: BORDER_RADIUS,
725
+ overflow: "hidden",
726
+ borderWidth: 1,
727
+ borderColor: theme.colors.border.secondary
489
728
  },
490
729
  children: [
491
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
492
- Box,
493
- {
494
- position: "relative",
495
- width: "100%",
496
- overflow: "hidden",
497
- style: {
498
- aspectRatio: config.imageAspectRatio,
499
- borderTopLeftRadius: config.borderRadius,
500
- borderTopRightRadius: config.borderRadius
501
- },
502
- children: [
503
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
504
- Box,
505
- {
506
- as: "img",
507
- src: image,
508
- alt: imageAlt || title,
509
- position: "absolute",
510
- top: 0,
511
- left: 0,
512
- width: "100%",
513
- height: "100%",
514
- style: isWeb ? { objectFit: "cover" } : void 0
515
- }
516
- ),
517
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
518
- LinearGradient,
519
- {
520
- colors: ["transparent", "rgba(0, 0, 0, 0.48)"],
521
- start: { x: 0, y: 0 },
522
- end: { x: 0, y: 1 },
523
- position: "absolute",
524
- bottom: 0,
525
- left: 0,
526
- right: 0,
527
- height: config.fadeHeight
528
- }
529
- ),
530
- (badge || tagsTopLeft) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
531
- Box,
532
- {
533
- position: "absolute",
534
- top: config.padding,
535
- left: config.padding,
536
- flexDirection: "column",
537
- gap: config.tagGap,
538
- alignItems: "flex-start",
539
- children: [
540
- badge,
541
- tagsTopLeft
542
- ]
543
- }
544
- ),
545
- tagsTopRight && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
546
- Box,
547
- {
548
- position: "absolute",
549
- top: config.padding,
550
- right: config.padding,
551
- flexDirection: "column",
552
- gap: config.tagGap,
553
- alignItems: "flex-end",
554
- children: tagsTopRight
555
- }
556
- )
557
- ]
558
- }
559
- ),
560
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
561
- Box,
562
- {
563
- backgroundColor: theme.colors.overlay.mono,
564
- padding: config.footerPadding,
565
- flexDirection: "row",
566
- alignItems: "center",
567
- gap: config.footerGap,
568
- children: [
569
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box, { flex: 1, flexDirection: "column", style: { minWidth: 0 }, children: [
570
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
571
- Text,
572
- {
573
- color: theme.colors.content.primary,
574
- fontSize: config.titleFontSize,
575
- lineHeight: config.titleLineHeight,
576
- fontWeight: "500",
577
- numberOfLines: 1,
578
- style: isWeb ? {
579
- overflow: "hidden",
580
- textOverflow: "ellipsis",
581
- whiteSpace: "nowrap"
582
- } : void 0,
583
- children: title
584
- }
585
- ),
586
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
587
- Text,
588
- {
589
- color: theme.colors.content.tertiary,
590
- fontSize: config.subtitleFontSize,
591
- lineHeight: config.subtitleLineHeight,
592
- fontWeight: "400",
593
- numberOfLines: 1,
594
- style: isWeb ? {
595
- overflow: "hidden",
596
- textOverflow: "ellipsis",
597
- whiteSpace: "nowrap"
598
- } : void 0,
599
- children: subtitle
600
- }
601
- )
602
- ] }),
603
- trailing,
604
- !trailing && buttonText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
605
- Box,
606
- {
607
- onPress: buttonDisabled ? void 0 : onButtonClick,
608
- backgroundColor: theme.colors.control.mono.secondary.bg,
609
- borderRadius: config.borderRadius,
610
- height: config.buttonHeight,
611
- paddingHorizontal: config.buttonPaddingX,
612
- alignItems: "center",
613
- justifyContent: "center",
614
- opacity: buttonDisabled ? 0.4 : 1,
615
- style: isWeb ? {
616
- backdropFilter: "blur(30px)",
617
- WebkitBackdropFilter: "blur(30px)",
618
- border: `1px solid ${theme.colors.control.mono.secondary.border}`,
619
- cursor: buttonDisabled ? "not-allowed" : onButtonClick ? "pointer" : "default"
620
- } : void 0,
621
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
622
- Text,
623
- {
624
- color: theme.colors.control.mono.text.secondary,
625
- fontSize: config.buttonFontSize,
626
- lineHeight: config.buttonLineHeight,
627
- fontWeight: "500",
628
- letterSpacing: 0.4,
629
- children: buttonText
630
- }
631
- )
632
- }
633
- )
634
- ]
635
- }
636
- )
730
+ renderImage(),
731
+ !hideFooter && renderFooter()
637
732
  ]
638
733
  }
639
734
  )