rnnovaui 0.3.1 → 0.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnnovaui",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "react-native": "src/index.js",
@@ -74,17 +74,33 @@ function UICardComponent({
74
74
 
75
75
  imageHeight = 190,
76
76
  imageResizeMode = "cover",
77
-
78
77
  imageStyle,
79
78
 
79
+ /*
80
+ * Example:
81
+ *
82
+ * badges={[
83
+ * {
84
+ * id: 'offer',
85
+ * label: '20% OFF',
86
+ * position: 'topLeft',
87
+ * variant: 'danger',
88
+ * },
89
+ *
90
+ * {
91
+ * id: 'rating',
92
+ * label: '★ 4.8',
93
+ * position: 'topRight',
94
+ * variant: 'warning',
95
+ * }
96
+ * ]}
97
+ */
80
98
  badges = [],
81
99
 
82
100
  badgeGap = 8,
83
101
 
84
102
  badgeStyle,
85
-
86
103
  badgeTextStyle,
87
-
88
104
  badgeContainerStyle,
89
105
 
90
106
  variant = "default",
@@ -96,19 +112,16 @@ function UICardComponent({
96
112
  gap = 12,
97
113
 
98
114
  style,
99
-
100
115
  headerStyle,
101
116
  contentStyle,
102
117
  footerStyle,
103
118
 
104
119
  imageOverlay = null,
105
-
106
120
  imageOverlayStyle,
107
121
 
108
122
  pressable = false,
109
123
 
110
124
  disabled = false,
111
-
112
125
  loading = false,
113
126
 
114
127
  onPress,
@@ -141,9 +154,9 @@ function UICardComponent({
141
154
 
142
155
  const safeVariant = CARD_VARIANTS[variant] ? variant : CARD_VARIANTS.default;
143
156
 
144
- const resolvedRadius = CARD_RADIUS[radius] ? radius : CARD_RADIUS.lg;
157
+ const safeRadius = CARD_RADIUS[radius] ? radius : CARD_RADIUS.lg;
145
158
 
146
- const radiusValue = getRadiusValue(resolvedRadius, theme);
159
+ const radiusValue = getRadiusValue(safeRadius, theme);
147
160
 
148
161
  const cardColors = useMemo(
149
162
  () => getCardColors(safeVariant, colors),
@@ -269,13 +282,17 @@ function UICardComponent({
269
282
  [cardColors, radiusValue, isDisabled, scale, style],
270
283
  );
271
284
 
272
- const renderImage = Boolean(image || imageSource);
285
+ const hasImage = Boolean(image || imageSource);
273
286
 
274
- const renderBody = Boolean(header || content || children || footer);
287
+ const hasBody = Boolean(header || content || children || footer);
275
288
 
276
289
  const cardContent = (
277
290
  <>
278
- {renderImage ? (
291
+ {/* ------------------------------------------ */}
292
+ {/* IMAGE */}
293
+ {/* ------------------------------------------ */}
294
+
295
+ {hasImage ? (
279
296
  <View
280
297
  style={[
281
298
  styles.imageSection,
@@ -289,7 +306,9 @@ function UICardComponent({
289
306
  },
290
307
  ]}
291
308
  >
292
- {image || (
309
+ {image ? (
310
+ image
311
+ ) : (
293
312
  <Image
294
313
  source={imageSource}
295
314
  resizeMode={imageResizeMode}
@@ -297,6 +316,10 @@ function UICardComponent({
297
316
  />
298
317
  )}
299
318
 
319
+ {/* -------------------------------------- */}
320
+ {/* IMAGE OVERLAY */}
321
+ {/* -------------------------------------- */}
322
+
300
323
  {imageOverlay ? (
301
324
  <View
302
325
  pointerEvents="none"
@@ -306,18 +329,18 @@ function UICardComponent({
306
329
  </View>
307
330
  ) : null}
308
331
 
309
- {badges.length > 0 ? (
332
+ {/* -------------------------------------- */}
333
+ {/* BADGES */}
334
+ {/* -------------------------------------- */}
335
+
336
+ {Array.isArray(badges) && badges.length > 0 ? (
310
337
  <View pointerEvents="box-none" style={styles.badgeLayer}>
311
338
  {badges.map((badge, index) => (
312
339
  <CardBadge
313
- key={
314
- badge.id ||
315
- badge.key ||
316
- `${badge.position || "topLeft"}-${index}`
317
- }
340
+ key={badge?.id || badge?.key || `badge-${index}`}
318
341
  badge={badge}
319
342
  index={index}
320
- gap={badgeGap}
343
+ badgeGap={badgeGap}
321
344
  defaultStyle={badgeStyle}
322
345
  defaultTextStyle={badgeTextStyle}
323
346
  defaultContainerStyle={badgeContainerStyle}
@@ -329,7 +352,11 @@ function UICardComponent({
329
352
  </View>
330
353
  ) : null}
331
354
 
332
- {renderBody ? (
355
+ {/* ------------------------------------------ */}
356
+ {/* BODY */}
357
+ {/* ------------------------------------------ */}
358
+
359
+ {hasBody ? (
333
360
  <View
334
361
  style={[
335
362
  styles.body,
@@ -340,41 +367,42 @@ function UICardComponent({
340
367
  },
341
368
  ]}
342
369
  >
370
+ {/* HEADER */}
371
+
343
372
  {header ? (
344
373
  <View style={[styles.header, headerStyle]}>{header}</View>
345
374
  ) : null}
346
375
 
376
+ {/* CONTENT */}
377
+
347
378
  {content ? (
348
379
  <View style={[styles.content, contentStyle]}>{content}</View>
349
380
  ) : null}
350
381
 
351
- {children ? (
352
- <View
353
- style={[
354
- styles.content,
355
-
356
- contentStyle,
382
+ {/* CHILDREN */}
357
383
 
358
- {
359
- marginTop: header || content ? 0 : undefined,
360
- },
361
- ]}
362
- >
363
- {children}
364
- </View>
384
+ {children ? (
385
+ <View style={[styles.content, contentStyle]}>{children}</View>
365
386
  ) : null}
366
387
 
388
+ {/* FOOTER */}
389
+
367
390
  {footer ? (
368
391
  <View style={[styles.footer, footerStyle]}>{footer}</View>
369
392
  ) : null}
370
393
  </View>
371
394
  ) : null}
372
395
 
396
+ {/* ------------------------------------------ */}
397
+ {/* LOADING */}
398
+ {/* ------------------------------------------ */}
399
+
373
400
  {loading ? (
374
401
  <View
375
402
  pointerEvents="none"
376
403
  style={[
377
404
  styles.loadingOverlay,
405
+
378
406
  {
379
407
  borderRadius: radiusValue,
380
408
  },
@@ -389,6 +417,10 @@ function UICardComponent({
389
417
  </>
390
418
  );
391
419
 
420
+ /*
421
+ * Non-pressable card
422
+ */
423
+
392
424
  if (!pressable) {
393
425
  return (
394
426
  <Animated.View
@@ -402,6 +434,10 @@ function UICardComponent({
402
434
  );
403
435
  }
404
436
 
437
+ /*
438
+ * Pressable card
439
+ */
440
+
405
441
  return (
406
442
  <Animated.View {...props} testID={testID} style={cardStyle}>
407
443
  <Pressable
@@ -426,28 +462,34 @@ function UICardComponent({
426
462
  );
427
463
  }
428
464
 
429
- /*
430
- * --------------------------------------------------
431
- * CARD BADGE
432
- * --------------------------------------------------
433
- */
465
+ /* ================================================= */
466
+ /* CARD BADGE */
467
+ /* ================================================= */
434
468
 
435
469
  const CardBadge = memo(function CardBadge({
436
470
  badge,
437
471
  index,
438
- gap,
472
+ badgeGap,
473
+
439
474
  defaultStyle,
440
475
  defaultTextStyle,
441
476
  defaultContainerStyle,
477
+
442
478
  colors,
443
479
  }) {
444
- const position = BADGE_POSITIONS[badge.position] ? badge.position : "topLeft";
480
+ if (!badge) {
481
+ return null;
482
+ }
483
+
484
+ const position = BADGE_POSITIONS[badge.position]
485
+ ? badge.position
486
+ : BADGE_POSITIONS.topLeft;
445
487
 
446
488
  const badgeColors = getBadgeColors(badge, colors);
447
489
 
448
- const positionStyle = getBadgePosition(position, index, gap);
490
+ const positionStyle = getBadgePosition(position, index, badgeGap);
449
491
 
450
- const badgeContent = badge.content ?? badge.label ?? badge.text ?? "";
492
+ const label = badge.label ?? badge.text ?? badge.content ?? "";
451
493
 
452
494
  return (
453
495
  <View
@@ -458,11 +500,13 @@ const CardBadge = memo(function CardBadge({
458
500
  positionStyle,
459
501
 
460
502
  {
461
- backgroundColor: badgeColors.background,
503
+ backgroundColor: badge.backgroundColor || badgeColors.background,
504
+
505
+ borderColor: badge.borderColor || badgeColors.border,
462
506
 
463
- borderColor: badgeColors.border,
507
+ borderWidth: badge.borderWidth ?? badgeColors.borderWidth,
464
508
 
465
- borderWidth: badgeColors.borderWidth,
509
+ borderRadius: badge.borderRadius ?? 999,
466
510
  },
467
511
 
468
512
  defaultStyle,
@@ -472,16 +516,29 @@ const CardBadge = memo(function CardBadge({
472
516
  defaultContainerStyle,
473
517
  ]}
474
518
  >
475
- {badge.icon ? <View style={styles.badgeIcon}>{badge.icon}</View> : null}
519
+ {badge.icon ? (
520
+ <View
521
+ style={[
522
+ styles.badgeIcon,
476
523
 
477
- {badgeContent ? (
524
+ {
525
+ marginRight: label ? 5 : 0,
526
+ },
527
+ ]}
528
+ >
529
+ {badge.icon}
530
+ </View>
531
+ ) : null}
532
+
533
+ {label !== "" ? (
478
534
  <Text
479
535
  numberOfLines={1}
536
+ ellipsizeMode="tail"
480
537
  style={[
481
538
  styles.badgeText,
482
539
 
483
540
  {
484
- color: badgeColors.text,
541
+ color: badge.textColor || badgeColors.text,
485
542
 
486
543
  fontSize: badge.fontSize || 12,
487
544
 
@@ -495,7 +552,7 @@ const CardBadge = memo(function CardBadge({
495
552
  badge.textStyle,
496
553
  ]}
497
554
  >
498
- {badgeContent}
555
+ {label}
499
556
  </Text>
500
557
  ) : null}
501
558
  </View>
@@ -504,135 +561,249 @@ const CardBadge = memo(function CardBadge({
504
561
 
505
562
  CardBadge.displayName = "CardBadge";
506
563
 
507
- /*
508
- * --------------------------------------------------
509
- * CARD COLORS
510
- * --------------------------------------------------
511
- */
564
+ /* ================================================= */
565
+ /* BADGE POSITIONING */
566
+ /* ================================================= */
512
567
 
513
- function getCardColors(variant, colors) {
514
- const background = colors.card || colors.surface || FALLBACK_COLORS.card;
568
+ function getBadgePosition(position, index, gap) {
569
+ /*
570
+ * IMPORTANT:
571
+ *
572
+ * Each position has a fixed anchor.
573
+ *
574
+ * index only creates an offset for
575
+ * multiple badges using THE SAME
576
+ * position.
577
+ */
578
+
579
+ const offset = index * (32 + gap);
515
580
 
516
- const border = colors.border || FALLBACK_COLORS.border;
581
+ switch (position) {
582
+ /* -------------------------------------------- */
583
+ /* TOP LEFT */
584
+ /* -------------------------------------------- */
517
585
 
518
- switch (variant) {
519
- case CARD_VARIANTS.elevated:
586
+ case BADGE_POSITIONS.topLeft:
520
587
  return {
521
- background,
588
+ top: 12,
522
589
 
523
- border: "transparent",
590
+ left: 12 + offset,
591
+ };
524
592
 
525
- borderWidth: 0,
593
+ /* -------------------------------------------- */
594
+ /* TOP CENTER */
595
+ /* -------------------------------------------- */
526
596
 
527
- shadow: styles.elevated,
597
+ case BADGE_POSITIONS.topCenter:
598
+ return {
599
+ top: 12,
600
+
601
+ left: "50%",
602
+
603
+ transform: [
604
+ {
605
+ translateX: -50,
606
+ },
607
+ ],
528
608
  };
529
609
 
530
- case CARD_VARIANTS.outlined:
610
+ /* -------------------------------------------- */
611
+ /* TOP RIGHT */
612
+ /* -------------------------------------------- */
613
+
614
+ case BADGE_POSITIONS.topRight:
531
615
  return {
532
- background,
616
+ top: 12,
533
617
 
534
- border,
618
+ right: 12 + offset,
619
+ };
535
620
 
536
- borderWidth: 1,
621
+ /* -------------------------------------------- */
622
+ /* CENTER LEFT */
623
+ /* -------------------------------------------- */
537
624
 
538
- shadow: styles.noShadow,
625
+ case BADGE_POSITIONS.centerLeft:
626
+ return {
627
+ left: 12,
628
+
629
+ top: "50%",
630
+
631
+ transform: [
632
+ {
633
+ translateY: -50,
634
+ },
635
+ ],
539
636
  };
540
637
 
541
- case CARD_VARIANTS.filled:
638
+ /* -------------------------------------------- */
639
+ /* CENTER */
640
+ /* -------------------------------------------- */
641
+
642
+ case BADGE_POSITIONS.center:
542
643
  return {
543
- background: colors.surface || FALLBACK_COLORS.surface,
644
+ left: "50%",
544
645
 
545
- border: "transparent",
646
+ top: "50%",
546
647
 
547
- borderWidth: 0,
648
+ transform: [
649
+ {
650
+ translateX: -50,
651
+ },
548
652
 
549
- shadow: styles.noShadow,
653
+ {
654
+ translateY: -50,
655
+ },
656
+ ],
550
657
  };
551
658
 
552
- case CARD_VARIANTS.default:
553
- default:
659
+ /* -------------------------------------------- */
660
+ /* CENTER RIGHT */
661
+ /* -------------------------------------------- */
662
+
663
+ case BADGE_POSITIONS.centerRight:
554
664
  return {
555
- background,
665
+ right: 12,
556
666
 
557
- border: "transparent",
667
+ top: "50%",
558
668
 
559
- borderWidth: 0,
669
+ transform: [
670
+ {
671
+ translateY: -50,
672
+ },
673
+ ],
674
+ };
560
675
 
561
- shadow: styles.noShadow,
676
+ /* -------------------------------------------- */
677
+ /* BOTTOM LEFT */
678
+ /* -------------------------------------------- */
679
+
680
+ case BADGE_POSITIONS.bottomLeft:
681
+ return {
682
+ bottom: 12,
683
+
684
+ left: 12 + offset,
685
+ };
686
+
687
+ /* -------------------------------------------- */
688
+ /* BOTTOM CENTER */
689
+ /* -------------------------------------------- */
690
+
691
+ case BADGE_POSITIONS.bottomCenter:
692
+ return {
693
+ bottom: 12,
694
+
695
+ left: "50%",
696
+
697
+ transform: [
698
+ {
699
+ translateX: -50,
700
+ },
701
+ ],
702
+ };
703
+
704
+ /* -------------------------------------------- */
705
+ /* BOTTOM RIGHT */
706
+ /* -------------------------------------------- */
707
+
708
+ case BADGE_POSITIONS.bottomRight:
709
+ return {
710
+ bottom: 12,
711
+
712
+ right: 12 + offset,
713
+ };
714
+
715
+ default:
716
+ return {
717
+ top: 12,
718
+
719
+ left: 12,
562
720
  };
563
721
  }
564
722
  }
565
723
 
566
- /*
567
- * --------------------------------------------------
568
- * BADGE COLORS
569
- * --------------------------------------------------
570
- */
724
+ /* ================================================= */
725
+ /* BADGE COLORS */
726
+ /* ================================================= */
571
727
 
572
728
  function getBadgeColors(badge, colors) {
573
- const type = badge.variant || badge.type || "primary";
729
+ const variant = badge.variant || badge.type || "primary";
574
730
 
575
- switch (type) {
731
+ switch (variant) {
576
732
  case "success":
577
733
  return {
578
- background:
579
- badge.backgroundColor || colors.success || FALLBACK_COLORS.success,
734
+ background: colors.success || FALLBACK_COLORS.success,
580
735
 
581
- border: badge.borderColor || colors.success || FALLBACK_COLORS.success,
736
+ border: colors.success || FALLBACK_COLORS.success,
582
737
 
583
- borderWidth: badge.borderWidth ?? 0,
738
+ borderWidth: 0,
584
739
 
585
- text: badge.textColor || colors.onSuccess || FALLBACK_COLORS.white,
740
+ text: colors.onSuccess || FALLBACK_COLORS.white,
586
741
  };
587
742
 
588
743
  case "danger":
589
744
  return {
590
- background:
591
- badge.backgroundColor || colors.danger || FALLBACK_COLORS.danger,
745
+ background: colors.danger || FALLBACK_COLORS.danger,
592
746
 
593
- border: badge.borderColor || colors.danger || FALLBACK_COLORS.danger,
747
+ border: colors.danger || FALLBACK_COLORS.danger,
594
748
 
595
- borderWidth: badge.borderWidth ?? 0,
749
+ borderWidth: 0,
596
750
 
597
- text: badge.textColor || colors.onDanger || FALLBACK_COLORS.white,
751
+ text: colors.onDanger || FALLBACK_COLORS.white,
598
752
  };
599
753
 
600
754
  case "warning":
601
755
  return {
602
- background:
603
- badge.backgroundColor || colors.warning || FALLBACK_COLORS.warning,
756
+ background: colors.warning || FALLBACK_COLORS.warning,
604
757
 
605
- border: badge.borderColor || colors.warning || FALLBACK_COLORS.warning,
758
+ border: colors.warning || FALLBACK_COLORS.warning,
606
759
 
607
- borderWidth: badge.borderWidth ?? 0,
760
+ borderWidth: 0,
608
761
 
609
- text: badge.textColor || colors.onWarning || FALLBACK_COLORS.white,
762
+ text: colors.onWarning || FALLBACK_COLORS.white,
610
763
  };
611
764
 
612
765
  case "info":
613
766
  return {
614
- background:
615
- badge.backgroundColor || colors.info || FALLBACK_COLORS.info,
767
+ background: colors.info || FALLBACK_COLORS.info,
616
768
 
617
- border: badge.borderColor || colors.info || FALLBACK_COLORS.info,
769
+ border: colors.info || FALLBACK_COLORS.info,
618
770
 
619
- borderWidth: badge.borderWidth ?? 0,
771
+ borderWidth: 0,
620
772
 
621
- text: badge.textColor || colors.onInfo || FALLBACK_COLORS.white,
773
+ text: colors.onInfo || FALLBACK_COLORS.white,
622
774
  };
623
775
 
624
776
  case "soft":
625
777
  return {
626
- background:
627
- badge.backgroundColor ||
628
- colors.primarySoft ||
629
- FALLBACK_COLORS.primarySoft,
778
+ background: colors.primarySoft || FALLBACK_COLORS.primarySoft,
630
779
 
631
- border: badge.borderColor || "transparent",
780
+ border: "transparent",
632
781
 
633
- borderWidth: badge.borderWidth ?? 0,
782
+ borderWidth: 0,
783
+
784
+ text: colors.primary || FALLBACK_COLORS.primary,
785
+ };
786
+
787
+ case "secondary":
788
+ return {
789
+ background: colors.secondary || "#7CFF32",
790
+
791
+ border: colors.secondary || "#7CFF32",
792
+
793
+ borderWidth: 0,
794
+
795
+ text: colors.onSecondary || FALLBACK_COLORS.black,
796
+ };
797
+
798
+ case "neutral":
799
+ return {
800
+ background: colors.surface || FALLBACK_COLORS.surface,
801
+
802
+ border: colors.border || FALLBACK_COLORS.border,
634
803
 
635
- text: badge.textColor || colors.primary || FALLBACK_COLORS.primary,
804
+ borderWidth: 1,
805
+
806
+ text: colors.textSecondary || FALLBACK_COLORS.textSecondary,
636
807
  };
637
808
 
638
809
  case "custom":
@@ -649,121 +820,77 @@ function getBadgeColors(badge, colors) {
649
820
  case "primary":
650
821
  default:
651
822
  return {
652
- background:
653
- badge.backgroundColor || colors.primary || FALLBACK_COLORS.primary,
823
+ background: colors.primary || FALLBACK_COLORS.primary,
654
824
 
655
- border: badge.borderColor || colors.primary || FALLBACK_COLORS.primary,
825
+ border: colors.primary || FALLBACK_COLORS.primary,
656
826
 
657
- borderWidth: badge.borderWidth ?? 0,
827
+ borderWidth: 0,
658
828
 
659
- text: badge.textColor || colors.onPrimary || FALLBACK_COLORS.white,
829
+ text: colors.onPrimary || FALLBACK_COLORS.white,
660
830
  };
661
831
  }
662
832
  }
663
833
 
664
- /*
665
- * --------------------------------------------------
666
- * BADGE POSITION
667
- * --------------------------------------------------
668
- */
834
+ /* ================================================= */
835
+ /* CARD COLORS */
836
+ /* ================================================= */
669
837
 
670
- function getBadgePosition(position, index, gap) {
671
- const offset = index * (28 + gap);
672
-
673
- switch (position) {
674
- case BADGE_POSITIONS.topCenter:
675
- return {
676
- top: 12 + offset,
838
+ function getCardColors(variant, colors) {
839
+ const background = colors.card || colors.surface || FALLBACK_COLORS.card;
677
840
 
678
- alignSelf: "center",
679
- };
841
+ const border = colors.border || FALLBACK_COLORS.border;
680
842
 
681
- case BADGE_POSITIONS.topRight:
843
+ switch (variant) {
844
+ case CARD_VARIANTS.elevated:
682
845
  return {
683
- top: 12 + offset,
684
-
685
- right: 12,
686
- };
846
+ background,
687
847
 
688
- case BADGE_POSITIONS.centerLeft:
689
- return {
690
- left: 12 + offset,
848
+ border: "transparent",
691
849
 
692
- top: "50%",
850
+ borderWidth: 0,
693
851
 
694
- transform: [
695
- {
696
- translateY: -12,
697
- },
698
- ],
852
+ shadow: styles.elevated,
699
853
  };
700
854
 
701
- case BADGE_POSITIONS.center:
855
+ case CARD_VARIANTS.outlined:
702
856
  return {
703
- top: "50%",
704
-
705
- left: "50%",
706
-
707
- transform: [
708
- {
709
- translateX: -50,
710
- },
711
-
712
- {
713
- translateY: -12,
714
- },
715
- ],
716
- };
857
+ background,
717
858
 
718
- case BADGE_POSITIONS.centerRight:
719
- return {
720
- right: 12 + offset,
859
+ border,
721
860
 
722
- top: "50%",
861
+ borderWidth: 1,
723
862
 
724
- transform: [
725
- {
726
- translateY: -12,
727
- },
728
- ],
863
+ shadow: styles.noShadow,
729
864
  };
730
865
 
731
- case BADGE_POSITIONS.bottomLeft:
866
+ case CARD_VARIANTS.filled:
732
867
  return {
733
- bottom: 12,
868
+ background: colors.surface || FALLBACK_COLORS.surface,
734
869
 
735
- left: 12 + offset,
736
- };
870
+ border: "transparent",
737
871
 
738
- case BADGE_POSITIONS.bottomCenter:
739
- return {
740
- bottom: 12 + offset,
872
+ borderWidth: 0,
741
873
 
742
- alignSelf: "center",
874
+ shadow: styles.noShadow,
743
875
  };
744
876
 
745
- case BADGE_POSITIONS.bottomRight:
877
+ case CARD_VARIANTS.default:
878
+ default:
746
879
  return {
747
- bottom: 12,
880
+ background,
748
881
 
749
- right: 12 + offset,
750
- };
882
+ border: "transparent",
751
883
 
752
- case BADGE_POSITIONS.topLeft:
753
- default:
754
- return {
755
- top: 12 + offset,
884
+ borderWidth: 0,
756
885
 
757
- left: 12,
886
+ shadow: styles.noShadow,
758
887
  };
759
888
  }
760
889
  }
761
890
 
762
- /*
763
- * --------------------------------------------------
764
- * RADIUS
765
- * --------------------------------------------------
766
- */
891
+ /* ================================================= */
892
+ /* RADIUS */
893
+ /* ================================================= */
767
894
 
768
895
  function getRadiusValue(radius, theme) {
769
896
  const themeRadius = theme?.radius || {};
@@ -787,6 +914,10 @@ function getRadiusValue(radius, theme) {
787
914
  }
788
915
  }
789
916
 
917
+ /* ================================================= */
918
+ /* STYLES */
919
+ /* ================================================= */
920
+
790
921
  const styles = StyleSheet.create({
791
922
  card: {
792
923
  width: "100%",
@@ -817,39 +948,46 @@ const styles = StyleSheet.create({
817
948
  imageOverlay: {
818
949
  ...StyleSheet.absoluteFillObject,
819
950
 
820
- justifyContent: "center",
821
-
822
- alignItems: "center",
951
+ zIndex: 5,
823
952
  },
824
953
 
954
+ /*
955
+ * This is the key fix.
956
+ *
957
+ * The badge layer fills the complete
958
+ * image section and does not participate
959
+ * in normal layout.
960
+ */
825
961
  badgeLayer: {
826
962
  ...StyleSheet.absoluteFillObject,
827
963
 
828
- pointerEvents: "box-none",
964
+ zIndex: 20,
965
+
966
+ elevation: 20,
829
967
  },
830
968
 
831
969
  badge: {
832
970
  position: "absolute",
833
971
 
834
- minHeight: 26,
972
+ minHeight: 28,
835
973
 
836
974
  paddingHorizontal: 10,
837
975
 
838
976
  paddingVertical: 5,
839
977
 
840
- borderRadius: 999,
841
-
842
978
  flexDirection: "row",
843
979
 
844
980
  alignItems: "center",
845
981
 
846
982
  justifyContent: "center",
847
983
 
848
- maxWidth: "80%",
984
+ maxWidth: "75%",
985
+
986
+ zIndex: 30,
849
987
 
850
- zIndex: 10,
988
+ elevation: 30,
851
989
 
852
- elevation: 3,
990
+ overflow: "hidden",
853
991
  },
854
992
 
855
993
  badgeText: {
@@ -861,11 +999,11 @@ const styles = StyleSheet.create({
861
999
  },
862
1000
 
863
1001
  badgeIcon: {
864
- marginRight: 5,
865
-
866
1002
  alignItems: "center",
867
1003
 
868
1004
  justifyContent: "center",
1005
+
1006
+ flexShrink: 0,
869
1007
  },
870
1008
 
871
1009
  body: {
@@ -901,6 +1039,7 @@ const styles = StyleSheet.create({
901
1039
 
902
1040
  shadowOffset: {
903
1041
  width: 0,
1042
+
904
1043
  height: 4,
905
1044
  },
906
1045
 
@@ -0,0 +1,401 @@
1
+ import React, { memo, useMemo } from "react";
2
+
3
+ import { StyleSheet, Text, View } from "react-native";
4
+
5
+ import { useUITheme } from "../../theme";
6
+
7
+ const DIVIDER_ORIENTATIONS = {
8
+ horizontal: "horizontal",
9
+ vertical: "vertical",
10
+ };
11
+
12
+ const DIVIDER_VARIANTS = {
13
+ solid: "solid",
14
+ dashed: "dashed",
15
+ };
16
+
17
+ const DIVIDER_TEXT_POSITIONS = {
18
+ left: "left",
19
+ center: "center",
20
+ right: "right",
21
+ };
22
+
23
+ const DIVIDER_ENDS = {
24
+ rounded: "rounded",
25
+ sharp: "sharp",
26
+ leftSharp: "leftSharp",
27
+ rightSharp: "rightSharp",
28
+ };
29
+
30
+ function UIDividerComponent({
31
+ orientation = "horizontal",
32
+
33
+ variant = "solid",
34
+
35
+ thickness = 1,
36
+
37
+ color,
38
+
39
+ length,
40
+
41
+ spacing = 0,
42
+
43
+ text = null,
44
+
45
+ textPosition = "center",
46
+
47
+ textStyle,
48
+
49
+ textContainerStyle,
50
+
51
+ lineStyle,
52
+
53
+ gap = 12,
54
+
55
+ ends = "rounded",
56
+
57
+ style,
58
+
59
+ testID,
60
+
61
+ ...props
62
+ }) {
63
+ const { theme } = useUITheme();
64
+
65
+ const colors = theme?.colors || {};
66
+
67
+ const safeOrientation = DIVIDER_ORIENTATIONS[orientation]
68
+ ? orientation
69
+ : DIVIDER_ORIENTATIONS.horizontal;
70
+
71
+ const safeVariant = DIVIDER_VARIANTS[variant]
72
+ ? variant
73
+ : DIVIDER_VARIANTS.solid;
74
+
75
+ const safeTextPosition = DIVIDER_TEXT_POSITIONS[textPosition]
76
+ ? textPosition
77
+ : DIVIDER_TEXT_POSITIONS.center;
78
+
79
+ const safeEnds = DIVIDER_ENDS[ends] ? ends : DIVIDER_ENDS.rounded;
80
+
81
+ const resolvedColor = color || colors.divider || colors.border || "#E5E5E5";
82
+
83
+ const resolvedTextColor =
84
+ colors.textSecondary || colors.textMuted || "#737373";
85
+
86
+ const isHorizontal = safeOrientation === DIVIDER_ORIENTATIONS.horizontal;
87
+
88
+ const hasText = isHorizontal && text !== null && text !== undefined;
89
+
90
+ const lineBaseStyle = useMemo(
91
+ () => ({
92
+ backgroundColor:
93
+ safeVariant === DIVIDER_VARIANTS.solid ? resolvedColor : "transparent",
94
+
95
+ borderColor: resolvedColor,
96
+
97
+ borderStyle: safeVariant === DIVIDER_VARIANTS.dashed ? "dashed" : "solid",
98
+
99
+ borderWidth: safeVariant === DIVIDER_VARIANTS.dashed ? thickness : 0,
100
+
101
+ borderRadius: getEndRadius(safeEnds, thickness),
102
+ }),
103
+ [safeVariant, resolvedColor, thickness, safeEnds],
104
+ );
105
+
106
+ /*
107
+ * ------------------------------------------
108
+ * VERTICAL DIVIDER
109
+ * ------------------------------------------
110
+ */
111
+
112
+ if (!isHorizontal) {
113
+ return (
114
+ <View
115
+ {...props}
116
+ testID={testID}
117
+ pointerEvents="none"
118
+ style={[
119
+ styles.vertical,
120
+
121
+ {
122
+ width: thickness,
123
+
124
+ height: length || "100%",
125
+
126
+ marginHorizontal: spacing,
127
+
128
+ backgroundColor:
129
+ safeVariant === DIVIDER_VARIANTS.solid
130
+ ? resolvedColor
131
+ : "transparent",
132
+
133
+ borderColor: resolvedColor,
134
+
135
+ borderStyle:
136
+ safeVariant === DIVIDER_VARIANTS.dashed ? "dashed" : "solid",
137
+
138
+ borderWidth:
139
+ safeVariant === DIVIDER_VARIANTS.dashed ? thickness : 0,
140
+
141
+ borderRadius: getEndRadius(safeEnds, thickness),
142
+ },
143
+
144
+ style,
145
+ ]}
146
+ />
147
+ );
148
+ }
149
+
150
+ /*
151
+ * ------------------------------------------
152
+ * HORIZONTAL WITHOUT TEXT
153
+ * ------------------------------------------
154
+ */
155
+
156
+ if (!hasText) {
157
+ return (
158
+ <View
159
+ {...props}
160
+ testID={testID}
161
+ pointerEvents="none"
162
+ style={[
163
+ styles.horizontal,
164
+
165
+ {
166
+ width: length || "100%",
167
+
168
+ height: thickness,
169
+
170
+ marginVertical: spacing,
171
+ },
172
+
173
+ lineBaseStyle,
174
+
175
+ lineStyle,
176
+
177
+ style,
178
+ ]}
179
+ />
180
+ );
181
+ }
182
+
183
+ /*
184
+ * ------------------------------------------
185
+ * HORIZONTAL WITH TEXT
186
+ * ------------------------------------------
187
+ */
188
+
189
+ return (
190
+ <View
191
+ {...props}
192
+ testID={testID}
193
+ pointerEvents="none"
194
+ style={[
195
+ styles.textDivider,
196
+
197
+ {
198
+ width: length || "100%",
199
+
200
+ marginVertical: spacing,
201
+ },
202
+
203
+ style,
204
+ ]}
205
+ >
206
+ {safeTextPosition === DIVIDER_TEXT_POSITIONS.left ? (
207
+ <>
208
+ <View style={[styles.line, lineBaseStyle, lineStyle]} />
209
+
210
+ <View
211
+ style={[
212
+ styles.textContainer,
213
+
214
+ {
215
+ marginLeft: gap,
216
+ },
217
+
218
+ textContainerStyle,
219
+ ]}
220
+ >
221
+ <Text
222
+ style={[
223
+ styles.text,
224
+
225
+ {
226
+ color: resolvedTextColor,
227
+ },
228
+
229
+ textStyle,
230
+ ]}
231
+ >
232
+ {text}
233
+ </Text>
234
+ </View>
235
+ </>
236
+ ) : null}
237
+
238
+ {safeTextPosition === DIVIDER_TEXT_POSITIONS.center ? (
239
+ <>
240
+ <View style={[styles.line, lineBaseStyle, lineStyle]} />
241
+
242
+ <View
243
+ style={[
244
+ styles.textContainer,
245
+
246
+ {
247
+ marginHorizontal: gap,
248
+ },
249
+
250
+ textContainerStyle,
251
+ ]}
252
+ >
253
+ <Text
254
+ style={[
255
+ styles.text,
256
+
257
+ {
258
+ color: resolvedTextColor,
259
+ },
260
+
261
+ textStyle,
262
+ ]}
263
+ >
264
+ {text}
265
+ </Text>
266
+ </View>
267
+
268
+ <View style={[styles.line, lineBaseStyle, lineStyle]} />
269
+ </>
270
+ ) : null}
271
+
272
+ {safeTextPosition === DIVIDER_TEXT_POSITIONS.right ? (
273
+ <>
274
+ <View
275
+ style={[
276
+ styles.textContainer,
277
+
278
+ {
279
+ marginRight: gap,
280
+ },
281
+
282
+ textContainerStyle,
283
+ ]}
284
+ >
285
+ <Text
286
+ style={[
287
+ styles.text,
288
+
289
+ {
290
+ color: resolvedTextColor,
291
+ },
292
+
293
+ textStyle,
294
+ ]}
295
+ >
296
+ {text}
297
+ </Text>
298
+ </View>
299
+
300
+ <View style={[styles.line, lineBaseStyle, lineStyle]} />
301
+ </>
302
+ ) : null}
303
+ </View>
304
+ );
305
+ }
306
+
307
+ /*
308
+ * ------------------------------------------
309
+ * END RADIUS
310
+ * ------------------------------------------
311
+ */
312
+
313
+ function getEndRadius(ends, thickness) {
314
+ switch (ends) {
315
+ case DIVIDER_ENDS.sharp:
316
+ return 0;
317
+
318
+ case DIVIDER_ENDS.leftSharp:
319
+ return {
320
+ borderTopLeftRadius: 0,
321
+ borderBottomLeftRadius: 0,
322
+
323
+ borderTopRightRadius: thickness,
324
+
325
+ borderBottomRightRadius: thickness,
326
+ };
327
+
328
+ case DIVIDER_ENDS.rightSharp:
329
+ return {
330
+ borderTopLeftRadius: thickness,
331
+
332
+ borderBottomLeftRadius: thickness,
333
+
334
+ borderTopRightRadius: 0,
335
+ borderBottomRightRadius: 0,
336
+ };
337
+
338
+ case DIVIDER_ENDS.rounded:
339
+ default:
340
+ return thickness / 2;
341
+ }
342
+ }
343
+
344
+ const styles = StyleSheet.create({
345
+ horizontal: {
346
+ flexShrink: 0,
347
+ },
348
+
349
+ vertical: {
350
+ flexShrink: 0,
351
+ },
352
+
353
+ textDivider: {
354
+ flexDirection: "row",
355
+
356
+ alignItems: "center",
357
+
358
+ flexShrink: 0,
359
+ },
360
+
361
+ line: {
362
+ flex: 1,
363
+
364
+ minWidth: 0,
365
+
366
+ height: 1,
367
+ },
368
+
369
+ textContainer: {
370
+ flexShrink: 0,
371
+
372
+ alignItems: "center",
373
+
374
+ justifyContent: "center",
375
+ },
376
+
377
+ text: {
378
+ fontSize: 13,
379
+
380
+ lineHeight: 18,
381
+
382
+ fontWeight: "500",
383
+
384
+ includeFontPadding: false,
385
+
386
+ textAlign: "center",
387
+ },
388
+ });
389
+
390
+ export const UIDivider = memo(UIDividerComponent);
391
+
392
+ UIDivider.displayName = "UIDivider";
393
+
394
+ export {
395
+ DIVIDER_ORIENTATIONS as UIDividerOrientations,
396
+ DIVIDER_VARIANTS as UIDividerVariants,
397
+ DIVIDER_TEXT_POSITIONS as UIDividerTextPositions,
398
+ DIVIDER_ENDS as UIDividerEnds,
399
+ };
400
+
401
+ export default UIDivider;
@@ -0,0 +1,7 @@
1
+ export {
2
+ UIDivider,
3
+ UIDividerOrientations,
4
+ UIDividerVariants,
5
+ UIDividerTextPositions,
6
+ UIDividerEnds,
7
+ } from "./UIDivider";
@@ -12,3 +12,10 @@ export {
12
12
  UICardRadius,
13
13
  UICardBadgePositions,
14
14
  } from "./Card";
15
+ export {
16
+ UIDivider,
17
+ UIDividerOrientations,
18
+ UIDividerVariants,
19
+ UIDividerTextPositions,
20
+ UIDividerEnds,
21
+ } from "./Divider";
package/src/index.js CHANGED
@@ -28,4 +28,9 @@ export {
28
28
  UICardVariants,
29
29
  UICardRadius,
30
30
  UICardBadgePositions,
31
+ UIDivider,
32
+ UIDividerOrientations,
33
+ UIDividerVariants,
34
+ UIDividerTextPositions,
35
+ UIDividerEnds,
31
36
  } from "./components";