@campxdev/react-native-blueprint 0.1.20 → 0.1.24

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.
Files changed (126) hide show
  1. package/global.css +3 -3
  2. package/lib/global.css +1 -1
  3. package/lib/module/assets/icons/Image.png +0 -0
  4. package/lib/module/components/DataDisplay/Avatar/Avatar.js +1 -1
  5. package/lib/module/components/DataDisplay/Avatar/Avatar.js.map +1 -1
  6. package/lib/module/components/DataDisplay/Banner/Banner.figma.js +17 -3
  7. package/lib/module/components/DataDisplay/Banner/Banner.figma.js.map +1 -1
  8. package/lib/module/components/DataDisplay/Banner/Banner.js +138 -34
  9. package/lib/module/components/DataDisplay/Banner/Banner.js.map +1 -1
  10. package/lib/module/components/DataDisplay/CalendarItem/CalendarItem.js +2 -2
  11. package/lib/module/components/DataDisplay/CalendarItem/CalendarItem.js.map +1 -1
  12. package/lib/module/components/DataDisplay/Card/Card.figma.js +11 -4
  13. package/lib/module/components/DataDisplay/Card/Card.figma.js.map +1 -1
  14. package/lib/module/components/DataDisplay/Card/Card.js +119 -65
  15. package/lib/module/components/DataDisplay/Card/Card.js.map +1 -1
  16. package/lib/module/components/DataDisplay/ChatBubble/ChatBubble.figma.js +54 -0
  17. package/lib/module/components/DataDisplay/ChatBubble/ChatBubble.figma.js.map +1 -0
  18. package/lib/module/components/DataDisplay/ChatBubble/ChatBubble.js +318 -0
  19. package/lib/module/components/DataDisplay/ChatBubble/ChatBubble.js.map +1 -0
  20. package/lib/module/components/DataDisplay/ChatBubble/index.js +4 -0
  21. package/lib/module/components/DataDisplay/ChatBubble/index.js.map +1 -0
  22. package/lib/module/components/DataDisplay/FeedCard/AttachmentDetails.js +69 -0
  23. package/lib/module/components/DataDisplay/FeedCard/AttachmentDetails.js.map +1 -0
  24. package/lib/module/components/DataDisplay/FeedCard/FeedCard.figma.js +19 -17
  25. package/lib/module/components/DataDisplay/FeedCard/FeedCard.figma.js.map +1 -1
  26. package/lib/module/components/DataDisplay/FeedCard/FeedCard.js +30 -19
  27. package/lib/module/components/DataDisplay/FeedCard/FeedCard.js.map +1 -1
  28. package/lib/module/components/DataDisplay/Greeting/Greeting.figma.js +5 -5
  29. package/lib/module/components/DataDisplay/Greeting/Greeting.figma.js.map +1 -1
  30. package/lib/module/components/DataDisplay/Greeting/Greeting.js +46 -70
  31. package/lib/module/components/DataDisplay/Greeting/Greeting.js.map +1 -1
  32. package/lib/module/components/DataDisplay/ProfileCard/ProfileCard.figma.js +16 -0
  33. package/lib/module/components/DataDisplay/ProfileCard/ProfileCard.figma.js.map +1 -0
  34. package/lib/module/components/DataDisplay/ProfileCard/ProfileCard.js +111 -0
  35. package/lib/module/components/DataDisplay/ProfileCard/ProfileCard.js.map +1 -0
  36. package/lib/module/components/DataDisplay/ProfileCard/index.js +4 -0
  37. package/lib/module/components/DataDisplay/ProfileCard/index.js.map +1 -0
  38. package/lib/module/components/Input/Button/Button.js +11 -9
  39. package/lib/module/components/Input/Button/Button.js.map +1 -1
  40. package/lib/module/components/Navigation/Appbar/AppBar.figma.js +18 -6
  41. package/lib/module/components/Navigation/Appbar/AppBar.figma.js.map +1 -1
  42. package/lib/module/components/Navigation/Appbar/AppBar.js +36 -9
  43. package/lib/module/components/Navigation/Appbar/AppBar.js.map +1 -1
  44. package/lib/module/components/Navigation/Popover/Popover.js +1 -1
  45. package/lib/module/components/Navigation/Popover/Popover.js.map +1 -1
  46. package/lib/module/components/ui/index.js +2 -0
  47. package/lib/module/components/ui/index.js.map +1 -1
  48. package/lib/module/lib/theme.js +2 -2
  49. package/lib/module/patterns/pattern-components/AccountPattern/AccountPattern.figma.js +196 -0
  50. package/lib/module/patterns/pattern-components/AccountPattern/AccountPattern.figma.js.map +1 -0
  51. package/lib/module/patterns/pattern-components/AccountPattern/AccountPattern.js +255 -0
  52. package/lib/module/patterns/pattern-components/AccountPattern/AccountPattern.js.map +1 -0
  53. package/lib/module/patterns/pattern-components/AccountPattern/index.js +4 -0
  54. package/lib/module/patterns/pattern-components/AccountPattern/index.js.map +1 -0
  55. package/lib/module/patterns/pattern-components/CalendarPattern/CalendarPattern.figma.js +1 -1
  56. package/lib/module/patterns/pattern-components/CardListPattern/CardListPattern.js +2 -4
  57. package/lib/module/patterns/pattern-components/CardListPattern/CardListPattern.js.map +1 -1
  58. package/lib/module/patterns/pattern-components/FeedPattern/FeedPattern.figma.js +144 -0
  59. package/lib/module/patterns/pattern-components/FeedPattern/FeedPattern.figma.js.map +1 -0
  60. package/lib/module/patterns/pattern-components/FeedPattern/FeedPattern.js +213 -0
  61. package/lib/module/patterns/pattern-components/FeedPattern/FeedPattern.js.map +1 -0
  62. package/lib/module/patterns/pattern-components/FeedPattern/index.js +4 -0
  63. package/lib/module/patterns/pattern-components/FeedPattern/index.js.map +1 -0
  64. package/lib/module/patterns/pattern-components/HomeFacultyPattern/HomeFacultyPattern.figma.js +70 -0
  65. package/lib/module/patterns/pattern-components/HomeFacultyPattern/HomeFacultyPattern.figma.js.map +1 -0
  66. package/lib/module/patterns/pattern-components/HomeFacultyPattern/HomeFacultyPattern.js +260 -0
  67. package/lib/module/patterns/pattern-components/HomeFacultyPattern/HomeFacultyPattern.js.map +1 -0
  68. package/lib/module/patterns/pattern-components/HomeFacultyPattern/index.js +4 -0
  69. package/lib/module/patterns/pattern-components/HomeFacultyPattern/index.js.map +1 -0
  70. package/lib/module/patterns/pattern-components/HomeParentPattern/HomeParentPattern.figma.js +82 -0
  71. package/lib/module/patterns/pattern-components/HomeParentPattern/HomeParentPattern.figma.js.map +1 -0
  72. package/lib/module/patterns/pattern-components/HomeParentPattern/HomeParentPattern.js +256 -0
  73. package/lib/module/patterns/pattern-components/HomeParentPattern/HomeParentPattern.js.map +1 -0
  74. package/lib/module/patterns/pattern-components/HomeParentPattern/index.js +4 -0
  75. package/lib/module/patterns/pattern-components/HomeParentPattern/index.js.map +1 -0
  76. package/lib/module/patterns/pattern-components/HomeStudentPattern/HomeStudentPattern.figma.js +73 -0
  77. package/lib/module/patterns/pattern-components/HomeStudentPattern/HomeStudentPattern.figma.js.map +1 -0
  78. package/lib/module/patterns/pattern-components/HomeStudentPattern/HomeStudentPattern.js +283 -0
  79. package/lib/module/patterns/pattern-components/HomeStudentPattern/HomeStudentPattern.js.map +1 -0
  80. package/lib/module/patterns/pattern-components/HomeStudentPattern/index.js +4 -0
  81. package/lib/module/patterns/pattern-components/HomeStudentPattern/index.js.map +1 -0
  82. package/lib/module/patterns/pattern-components/index.js +5 -0
  83. package/lib/module/patterns/pattern-components/index.js.map +1 -1
  84. package/package.json +27 -1
  85. package/src/assets/icons/Image.png +0 -0
  86. package/src/components/DataDisplay/Avatar/Avatar.tsx +24 -21
  87. package/src/components/DataDisplay/Banner/Banner.figma.tsx +18 -3
  88. package/src/components/DataDisplay/Banner/Banner.tsx +153 -26
  89. package/src/components/DataDisplay/CalendarItem/CalendarItem.tsx +2 -2
  90. package/src/components/DataDisplay/Card/Card.figma.tsx +7 -3
  91. package/src/components/DataDisplay/Card/Card.tsx +152 -101
  92. package/src/components/DataDisplay/ChatBubble/ChatBubble.figma.tsx +54 -0
  93. package/src/components/DataDisplay/ChatBubble/ChatBubble.tsx +404 -0
  94. package/src/components/DataDisplay/ChatBubble/index.ts +8 -0
  95. package/src/components/DataDisplay/FeedCard/AttachmentDetails.tsx +96 -0
  96. package/src/components/DataDisplay/FeedCard/FeedCard.figma.tsx +17 -15
  97. package/src/components/DataDisplay/FeedCard/FeedCard.tsx +66 -35
  98. package/src/components/DataDisplay/Greeting/Greeting.figma.tsx +5 -5
  99. package/src/components/DataDisplay/Greeting/Greeting.tsx +58 -96
  100. package/src/components/DataDisplay/ProfileCard/ProfileCard.figma.tsx +17 -0
  101. package/src/components/DataDisplay/ProfileCard/ProfileCard.tsx +173 -0
  102. package/src/components/DataDisplay/ProfileCard/index.ts +1 -0
  103. package/src/components/Input/Button/Button.tsx +18 -10
  104. package/src/components/Navigation/Appbar/AppBar.figma.tsx +18 -6
  105. package/src/components/Navigation/Appbar/AppBar.tsx +58 -13
  106. package/src/components/Navigation/Popover/Popover.tsx +3 -3
  107. package/src/components/ui/index.ts +2 -0
  108. package/src/lib/theme.ts +2 -2
  109. package/src/patterns/pattern-components/AccountPattern/AccountPattern.figma.tsx +193 -0
  110. package/src/patterns/pattern-components/AccountPattern/AccountPattern.tsx +301 -0
  111. package/src/patterns/pattern-components/AccountPattern/index.ts +1 -0
  112. package/src/patterns/pattern-components/CalendarPattern/CalendarPattern.figma.tsx +1 -1
  113. package/src/patterns/pattern-components/CardListPattern/CardListPattern.tsx +4 -9
  114. package/src/patterns/pattern-components/FeedPattern/FeedPattern.figma.tsx +146 -0
  115. package/src/patterns/pattern-components/FeedPattern/FeedPattern.tsx +264 -0
  116. package/src/patterns/pattern-components/FeedPattern/index.ts +2 -0
  117. package/src/patterns/pattern-components/HomeFacultyPattern/HomeFacultyPattern.figma.tsx +66 -0
  118. package/src/patterns/pattern-components/HomeFacultyPattern/HomeFacultyPattern.tsx +326 -0
  119. package/src/patterns/pattern-components/HomeFacultyPattern/index.ts +2 -0
  120. package/src/patterns/pattern-components/HomeParentPattern/HomeParentPattern.figma.tsx +75 -0
  121. package/src/patterns/pattern-components/HomeParentPattern/HomeParentPattern.tsx +328 -0
  122. package/src/patterns/pattern-components/HomeParentPattern/index.ts +2 -0
  123. package/src/patterns/pattern-components/HomeStudentPattern/HomeStudentPattern.figma.tsx +66 -0
  124. package/src/patterns/pattern-components/HomeStudentPattern/HomeStudentPattern.tsx +355 -0
  125. package/src/patterns/pattern-components/HomeStudentPattern/index.ts +2 -0
  126. package/src/patterns/pattern-components/index.ts +5 -0
@@ -15,11 +15,10 @@ import { Button } from '../../Input/Button/Button';
15
15
  import { Badge } from '../Badge/Badge';
16
16
  import { Text } from '../../Input/Text/Text';
17
17
  import { Avatar } from '../Avatar/Avatar';
18
- import { AspectRatio } from '../../Layout/AspectRatio/Aspect-Ratio';
18
+ import { AttachmentDetails } from './AttachmentDetails';
19
19
 
20
- // Default placeholder image
21
- const DEFAULT_MEDIA_IMAGE =
22
- 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe3e?w=400&h=224&fit=crop';
20
+ // Gradient image asset (same as Card demo)
21
+ const GRADIENT_IMAGE = require('../../../assets/icons/Image.png');
23
22
 
24
23
  // NativeWind interop (className -> style)
25
24
  const View = cssInterop(RNView, { className: 'style' });
@@ -36,23 +35,26 @@ export const FeedCardVariants = {
36
35
 
37
36
  type FeedCardType = (typeof FeedCardVariants.type)[number];
38
37
 
39
- const rootVariants = cva('w-full overflow-hidden rounded-3xl', {
40
- variants: {
41
- type: {
42
- post: 'bg-surface-default',
43
- announcement: 'bg-surface-default border border-border-default',
38
+ const rootVariants = cva(
39
+ 'w-full overflow-hidden rounded-lg bg-surface-default',
40
+ {
41
+ variants: {
42
+ type: {
43
+ post: '',
44
+ announcement: 'border border-border-default',
45
+ },
44
46
  },
45
- },
46
- defaultVariants: {
47
- type: 'post',
48
- },
49
- });
47
+ defaultVariants: {
48
+ type: 'post',
49
+ },
50
+ }
51
+ );
50
52
 
51
- const metaRowVariants = cva('w-full flex-row p-4', {
53
+ const metaRowVariants = cva('w-full flex-row p-4 items-center', {
52
54
  variants: {
53
55
  type: {
54
- post: 'items-center gap-2',
55
- announcement: 'items-center justify-between',
56
+ post: 'gap-2',
57
+ announcement: 'justify-between gap-2',
56
58
  },
57
59
  },
58
60
  defaultVariants: {
@@ -60,9 +62,9 @@ const metaRowVariants = cva('w-full flex-row p-4', {
60
62
  },
61
63
  });
62
64
 
63
- const mediaWrapVariants = cva('w-full overflow-hidden px-4 pb-4');
65
+ const mediaWrapVariants = cva('w-full overflow-hidden px-4 pb-0 pt-0');
64
66
 
65
- const contentWrapVariants = cva('w-full px-4 pb-4 flex-col');
67
+ const contentWrapVariants = cva('w-full px-4 pb-4 flex-col gap-4');
66
68
 
67
69
  const headerVariants = cva('w-full gap-1 flex-col');
68
70
 
@@ -111,6 +113,15 @@ export type FeedCardProps = {
111
113
  /** Optional image/media */
112
114
  image?: ImageSourcePropType;
113
115
 
116
+ /** Media type variant */
117
+ mediaType?: 'image' | 'file';
118
+
119
+ /** File attachment details (for file media type) */
120
+ fileName?: string;
121
+ filePages?: number;
122
+ fileSizeMB?: number;
123
+ fileType?: string;
124
+
114
125
  /** Card type variant */
115
126
  type?: FeedCardType | 'Post' | 'Announcement';
116
127
 
@@ -120,6 +131,7 @@ export type FeedCardProps = {
120
131
  showMedia?: boolean;
121
132
  showPostContent?: boolean;
122
133
  showSubtitle?: boolean;
134
+ showBody?: boolean;
123
135
  showBadges?: boolean;
124
136
  showFooterActions?: boolean;
125
137
  showSecondaryButton?: boolean;
@@ -155,6 +167,11 @@ export function FeedCard(props: FeedCardProps) {
155
167
  subtitle = 'Subtitle of the card goes here',
156
168
  postContent = 'The card may contain body content which can be truncated to 1-2 lines. Swap Content in Props.',
157
169
  image,
170
+ mediaType = 'image',
171
+ fileName = 'File Name',
172
+ filePages = 27,
173
+ fileSizeMB = 3,
174
+ fileType = 'PDF',
158
175
 
159
176
  type = 'post',
160
177
 
@@ -163,6 +180,7 @@ export function FeedCard(props: FeedCardProps) {
163
180
  showMedia = true,
164
181
  showPostContent = true,
165
182
  showSubtitle = true,
183
+ showBody = true,
166
184
  showBadges = true,
167
185
  showFooterActions = true,
168
186
  showSecondaryButton = true,
@@ -243,16 +261,26 @@ export function FeedCard(props: FeedCardProps) {
243
261
  {/* Media Section */}
244
262
  {showMedia && (
245
263
  <View testID="feed-card-media-wrap" className={cn(mediaWrapVariants())}>
246
- <AspectRatio variant="16:9">
247
- <View className="w-full h-full bg-surface-subtle rounded-3xl overflow-hidden">
248
- <Image
249
- testID="feed-card-media"
250
- source={image || { uri: DEFAULT_MEDIA_IMAGE }}
251
- resizeMode="cover"
252
- className="w-full h-full"
264
+ <View className="w-full pb-4">
265
+ {mediaType === 'image' ? (
266
+ <View className="w-full aspect-[16/9] bg-surface-subtle rounded-xl overflow-hidden">
267
+ <Image
268
+ testID="feed-card-media"
269
+ source={image || GRADIENT_IMAGE}
270
+ resizeMode="cover"
271
+ className="w-full h-full"
272
+ />
273
+ </View>
274
+ ) : (
275
+ <AttachmentDetails
276
+ testID="feed-card-attachment"
277
+ fileName={fileName}
278
+ pages={filePages}
279
+ sizeMB={fileSizeMB}
280
+ fileType={fileType}
253
281
  />
254
- </View>
255
- </AspectRatio>
282
+ )}
283
+ </View>
256
284
  </View>
257
285
  )}
258
286
 
@@ -265,6 +293,7 @@ export function FeedCard(props: FeedCardProps) {
265
293
  <RNText
266
294
  testID="feed-card-title"
267
295
  numberOfLines={1}
296
+ ellipsizeMode="tail"
268
297
  className={cn(titleVariants())}
269
298
  >
270
299
  {postTitle}
@@ -283,13 +312,15 @@ export function FeedCard(props: FeedCardProps) {
283
312
  )}
284
313
 
285
314
  {/* Body */}
286
- <RNText
287
- testID="feed-card-body"
288
- numberOfLines={2}
289
- className={cn(bodyVariants())}
290
- >
291
- {postContent}
292
- </RNText>
315
+ {showBody && (
316
+ <RNText
317
+ testID="feed-card-body"
318
+ numberOfLines={2}
319
+ className={cn(bodyVariants())}
320
+ >
321
+ {postContent}
322
+ </RNText>
323
+ )}
293
324
  </View>
294
325
  )}
295
326
 
@@ -6,11 +6,11 @@ const FIGMA_URL =
6
6
 
7
7
  figma.connect(Greeting, FIGMA_URL, {
8
8
  props: {
9
- ctaLayout: figma.enum('CTA Layout', {
10
- 'icon': 'icon',
11
- 'none': 'none',
12
- 'floating': 'floating',
13
- 'ai summary': 'ai summary',
9
+ ctaLayout: figma.enum('ctaLayout', {
10
+ icon: 'icon',
11
+ row: 'row',
12
+ none: 'none',
13
+ floating: 'floating',
14
14
  }),
15
15
  showNextUp: figma.boolean('Show NextUp'),
16
16
  },
@@ -16,10 +16,6 @@ cssInterop(LinearGradient, { className: 'style' });
16
16
  const View = RNView as React.ComponentType<
17
17
  React.ComponentProps<typeof RNView> & { className?: string }
18
18
  >;
19
- const Pressable = RNPressable as React.ComponentType<
20
- React.ComponentProps<typeof RNPressable> & { className?: string }
21
- >;
22
-
23
19
  /**
24
20
  * Props for the Greeting component
25
21
  */
@@ -31,9 +27,7 @@ export interface GreetingProps {
31
27
  /** Subtitle/secondary text below greeting */
32
28
  subtitle?: string;
33
29
  /** CTA (Call-to-Action) layout style */
34
- ctaLayout?: 'icon' | 'none' | 'floating' | 'ai summary';
35
- /** Whether to show the schedule/calendar button - deprecated, use ctaLayout instead */
36
- showScheduleButton?: boolean;
30
+ ctaLayout?: 'icon' | 'none' | 'floating' | 'row';
37
31
  /** Callback when schedule button is pressed */
38
32
  onSchedulePress?: () => void;
39
33
  /** Whether to show the "Next up" section */
@@ -52,12 +46,16 @@ export interface GreetingProps {
52
46
  * Greeting - A personalized greeting card with schedule information
53
47
  *
54
48
  * Displays a personalized greeting with user name, class information, and upcoming
55
- * class details. Features a prominent purple schedule button with calendar icon,
56
- * "Next up" section with course name and time, and flexible visibility controls.
49
+ * class details. Supports multiple CTA layouts: icon button, row button, floating button,
50
+ * or none. Features "Next up" section with course name and time.
57
51
  *
58
- * Design from Figma: node-id=448-8146
52
+ * Design from Figma: node-id=495-8995
59
53
  * - Header: "Hey {name}, {greeting}" + Subtitle
60
- * - Schedule Button: Purple (#573dab) with calendar icon and shadow
54
+ * - CTA Layouts:
55
+ * - icon: Calendar icon button in header
56
+ * - row: Full-width button below content
57
+ * - floating: Full-width button at bottom with separate styling
58
+ * - none: No CTA button
61
59
  * - Next Up: Course name + divider dot + time
62
60
  *
63
61
  * @component
@@ -70,6 +68,7 @@ export interface GreetingProps {
70
68
  * nextUpTitle="Digital Logic Design"
71
69
  * nextUpTime="10:00 AM"
72
70
  * showNextUp={true}
71
+ * ctaLayout="icon"
73
72
  * onSchedulePress={() => navigate('Schedule')}
74
73
  * />
75
74
  * ```
@@ -79,7 +78,6 @@ export function Greeting({
79
78
  greetingText = 'Good Morning',
80
79
  subtitle = 'You have 3 Classes today',
81
80
  ctaLayout = 'icon',
82
- showScheduleButton,
83
81
  onSchedulePress,
84
82
  showNextUp = true,
85
83
  nextUpTitle = 'Digital Logic Design',
@@ -87,49 +85,31 @@ export function Greeting({
87
85
  className,
88
86
  testID,
89
87
  }: GreetingProps) {
90
- // Handle backwards compatibility: showScheduleButton prop takes precedence if explicitly set
91
- const effectiveCtaLayout =
92
- showScheduleButton !== undefined
93
- ? showScheduleButton
94
- ? 'icon'
95
- : 'none'
96
- : ctaLayout;
97
-
98
- const isAiSummary = effectiveCtaLayout === 'ai summary';
99
- const isFloating = effectiveCtaLayout === 'floating';
100
- const isIconOrFloatingOrNoneOrAiSummary = [
101
- 'icon',
102
- 'floating',
103
- 'none',
104
- 'ai summary',
105
- ].includes(effectiveCtaLayout);
106
- const isNone = effectiveCtaLayout === 'none';
88
+ const isFloating = ctaLayout === 'floating';
89
+ const isRow = ctaLayout === 'row';
90
+ const isIcon = ctaLayout === 'icon';
107
91
 
108
92
  return (
109
93
  <View
110
94
  testID={testID}
111
95
  className={cn(
112
- 'w-full max-w-[400px] flex-col items-start justify-end bg-surface-default',
96
+ 'w-full max-w-[400px] flex-col items-start justify-end bg-surface-default rounded-xl border border-border-default',
113
97
  // Base styling based on ctaLayout
114
- isAiSummary
115
- ? 'gap-4 rounded-3xl border border-border-default px-0 pt-4 pb-0 overflow-hidden'
116
- : isFloating
117
- ? 'gap-4 rounded-3xl border border-border-default px-0 pt-4 pb-0 overflow-hidden'
118
- : isNone
119
- ? 'gap-4 rounded-3xl border border-border-default px-0 py-4'
120
- : 'gap-4 rounded-3xl border border-border-default px-0 py-4',
98
+ isFloating
99
+ ? 'gap-5 px-0 py-5'
100
+ : isRow
101
+ ? 'gap-4 px-0 pt-4 pb-0'
102
+ : 'gap-4 px-0 py-3',
121
103
  className
122
104
  )}
123
105
  >
124
- {/* Header Section: Greeting + Schedule Button */}
106
+ {/* Header Section: Greeting + Optional Calendar Icon */}
125
107
  <View
126
108
  className={cn(
127
- 'w-full flex-row items-center',
128
- isFloating || isAiSummary
129
- ? 'justify-between px-3'
130
- : isNone
131
- ? 'px-3'
132
- : 'px-3'
109
+ 'w-full flex-row',
110
+ isFloating
111
+ ? 'justify-between px-3 items-start'
112
+ : 'justify-between px-3 items-center'
133
113
  )}
134
114
  >
135
115
  {/* Header Content: Title and Subtitle */}
@@ -142,32 +122,36 @@ export function Greeting({
142
122
  </Text>
143
123
  </View>
144
124
 
145
- {/* Schedule Button with Calendar Icon - Icon Layout */}
146
- {effectiveCtaLayout === 'icon' && (
147
- <Pressable
125
+ {/* Schedule Button - Icon Layout (top-right in icon layout) */}
126
+ {isIcon && (
127
+ <Button
128
+ variant="default"
129
+ size="icon"
130
+ showLeftIcon
131
+ leftIcon={<Icon as={Calendar} size={16} color="white" />}
148
132
  onPress={onSchedulePress}
149
- hitSlop={12}
150
- className="ml-3 flex-row items-center justify-center rounded-lg p-3 bg-highlight-purple"
151
- >
152
- <Icon as={Calendar} size={16} color="white" />
153
- </Pressable>
133
+ style={{ marginLeft: 12 }}
134
+ />
154
135
  )}
155
136
 
156
- {/* Calendar Icon for AI Summary and Floating layouts */}
157
- {isAiSummary && (
158
- <Pressable
137
+ {/* Schedule Button - Floating Layout (top-right in floating layout) */}
138
+ {isFloating && (
139
+ <Button
140
+ variant="default"
141
+ size="default"
159
142
  onPress={onSchedulePress}
160
- hitSlop={12}
161
- className="ml-3 flex-row items-center justify-center rounded-lg p-3 bg-highlight-purple"
143
+ style={{ marginLeft: 12 }}
162
144
  >
163
- <Icon as={Calendar} size={16} color="white" />
164
- </Pressable>
145
+ My Schedule
146
+ </Button>
165
147
  )}
166
148
  </View>
167
149
 
168
- {/* Next Up Section - For icon, none, floating, and ai summary layouts */}
169
- {isIconOrFloatingOrNoneOrAiSummary && showNextUp && (
170
- <View className="w-full flex-col gap-1 px-3">
150
+ {/* Next Up Section - For icon, row, none, and floating layouts */}
151
+ {showNextUp && (
152
+ <View
153
+ className={cn('w-full flex-col gap-1', isFloating ? 'px-3' : 'px-3')}
154
+ >
171
155
  <Text className="font-medium text-xs text-text-secondary">
172
156
  Next up
173
157
  </Text>
@@ -185,44 +169,22 @@ export function Greeting({
185
169
  </View>
186
170
  )}
187
171
 
188
- {/* AI Summary Button - Purple to Red Gradient - Full Width */}
189
- {isAiSummary && (
190
- <LinearGradient
191
- colors={['#573dab', '#f2353c']}
192
- start={{ x: 0, y: 0 }}
193
- end={{ x: 1, y: 0 }}
194
- className="w-full items-center justify-center"
195
- style={{
196
- paddingVertical: 8,
197
- borderBottomLeftRadius: 20,
198
- borderBottomRightRadius: 20,
199
- }}
200
- >
201
- <Pressable
202
- onPress={onSchedulePress}
203
- className="w-full items-center justify-center"
204
- style={{ paddingVertical: 0 }}
205
- >
206
- <Text className="font-semibold text-sm text-white">
207
- AI Summary for Today
208
- </Text>
209
- </Pressable>
210
- </LinearGradient>
211
- )}
172
+ {/* Divider and Button Container for Row Layout */}
173
+ {isRow && (
174
+ <View className="w-full flex-col">
175
+ {/* Divider Line */}
176
+ <View className="w-full border-t border-border-default" />
212
177
 
213
- {/* Floating Button - Full width at bottom - Card color (white) */}
214
- {isFloating && (
215
- <View
216
- className="w-full bg-surface-default"
217
- style={{ borderBottomLeftRadius: 20, borderBottomRightRadius: 20 }}
218
- >
178
+ {/* Row Button - Full width ghost button */}
219
179
  <Button
220
- variant="outline"
180
+ variant="ghost"
221
181
  size="default"
222
- showRightIcon
223
- rightIcon={Calendar}
182
+ showLeftIcon
183
+ leftIcon={<Icon as={Calendar} size={16} />}
224
184
  onPress={onSchedulePress}
225
- style={{ width: '100%' }}
185
+ style={{
186
+ width: '100%',
187
+ }}
226
188
  >
227
189
  View Schedule
228
190
  </Button>
@@ -0,0 +1,17 @@
1
+ import figma from '@figma/code-connect';
2
+ import { ProfileCard } from './ProfileCard';
3
+
4
+ const FIGMA_URL =
5
+ 'https://www.figma.com/design/66WaqopqU3WXgwVtyQuTUf/React-Native-Blueprint-Library?node-id=637-2039';
6
+
7
+ figma.connect(ProfileCard, FIGMA_URL, {
8
+ example: () => (
9
+ <ProfileCard
10
+ userName="Marshall Mathers"
11
+ userID="3472732323"
12
+ avatarInitials="MM"
13
+ avatarType="image"
14
+ onPress={() => console.log('Profile card pressed')}
15
+ />
16
+ ),
17
+ });
@@ -0,0 +1,173 @@
1
+ // @ts-nocheck
2
+ import {
3
+ View as RNView,
4
+ Text as RNTextBase,
5
+ Pressable as RNPressable,
6
+ Image as RNImage,
7
+ type StyleProp,
8
+ type ViewStyle,
9
+ } from 'react-native';
10
+ import { cssInterop } from 'nativewind';
11
+ import { cva, type VariantProps } from 'class-variance-authority';
12
+ import { ChevronRight } from 'lucide-react-native';
13
+
14
+ import { cn } from '../../../lib/utils';
15
+
16
+ // NativeWind interop (className -> style)
17
+ const View = cssInterop(RNView, { className: 'style' });
18
+ const RNText = cssInterop(RNTextBase, { className: 'style' });
19
+ const Pressable = cssInterop(RNPressable, { className: 'style' });
20
+
21
+ /* -----------------------------------------------------
22
+ * Variants
23
+ * --------------------------------------------------- */
24
+
25
+ export const ProfileCardVariants = {} as const;
26
+
27
+ const rootVariants = cva(
28
+ 'w-full rounded-[20px] overflow-hidden flex-row items-center justify-between gap-3 px-3 py-3 bg-highlight-highlight-purple',
29
+ {
30
+ variants: {},
31
+ defaultVariants: {},
32
+ }
33
+ );
34
+
35
+ const profileCardStyle = (style?: any) => ({
36
+ ...style,
37
+ });
38
+
39
+ const contentContainerVariants = cva('flex-1 flex-row items-center gap-3');
40
+
41
+ const infoColumnVariants = cva('flex-1 flex-col gap-1 justify-center');
42
+
43
+ const nameTextVariants = cva(
44
+ "font-['Geist:SemiBold',sans-serif] font-semibold text-base text-white"
45
+ );
46
+
47
+ const idTextVariants = cva(
48
+ "font-['Geist:Medium',sans-serif] font-medium text-xs text-white opacity-70"
49
+ );
50
+
51
+ const trailingIconVariants = cva('items-center justify-center rounded-lg p-2');
52
+
53
+ /* -----------------------------------------------------
54
+ * Props
55
+ * --------------------------------------------------- */
56
+
57
+ export type ProfileCardProps = VariantProps<typeof rootVariants> & {
58
+ /** User name */
59
+ userName?: string;
60
+
61
+ /** User ID or identifier */
62
+ userID?: string;
63
+
64
+ /** Avatar initials */
65
+ avatarInitials?: string;
66
+
67
+ /** Avatar image source */
68
+ avatarSource?: any;
69
+
70
+ /** Avatar type */
71
+ avatarType?: 'initials' | 'image' | 'icon';
72
+
73
+ /** Callback when profile card is pressed */
74
+ onPress?: () => void;
75
+
76
+ /** Used to override the default root style. */
77
+ style?: StyleProp<ViewStyle>;
78
+
79
+ /** Used to locate this view in end-to-end tests. */
80
+ testID?: string;
81
+
82
+ /** Strictly block passing className */
83
+ className?: never;
84
+ };
85
+
86
+ /* -----------------------------------------------------
87
+ * Component
88
+ * --------------------------------------------------- */
89
+
90
+ export function ProfileCard(props: ProfileCardProps) {
91
+ const {
92
+ userName = 'Marshall Mathers',
93
+ userID = '3472732323',
94
+ avatarInitials = 'MM',
95
+ avatarSource,
96
+ avatarType = 'initials',
97
+ onPress,
98
+ style,
99
+ testID,
100
+ } = props;
101
+
102
+ return (
103
+ <Pressable
104
+ testID={testID ?? 'profile-card'}
105
+ onPress={onPress}
106
+ className={cn(rootVariants())}
107
+ style={profileCardStyle(style)}
108
+ hitSlop={8}
109
+ >
110
+ <View
111
+ testID="profile-card-content"
112
+ className={cn(contentContainerVariants())}
113
+ >
114
+ {/* Avatar - Square */}
115
+ <View
116
+ testID="profile-card-avatar-wrapper"
117
+ className="rounded-[12px] overflow-hidden items-center justify-center bg-surface-subtle"
118
+ style={{
119
+ width: '80',
120
+ height: '80',
121
+ }}
122
+ >
123
+ {avatarType === 'image' && avatarSource ? (
124
+ <RNImage
125
+ testID="profile-card-avatar-image"
126
+ source={avatarSource}
127
+ className="w-full h-full"
128
+ />
129
+ ) : (
130
+ <RNText
131
+ testID="profile-card-avatar-initials"
132
+ className="font-semibold text-xl text-muted-foreground"
133
+ >
134
+ {avatarInitials}
135
+ </RNText>
136
+ )}
137
+ </View>
138
+
139
+ {/* User Info */}
140
+ <View testID="profile-card-info" className={cn(infoColumnVariants())}>
141
+ <RNText
142
+ testID="profile-card-name"
143
+ numberOfLines={1}
144
+ className={cn(nameTextVariants())}
145
+ >
146
+ {userName}
147
+ </RNText>
148
+
149
+ <RNText
150
+ testID="profile-card-id"
151
+ numberOfLines={1}
152
+ className={cn(idTextVariants())}
153
+ >
154
+ {userID}
155
+ </RNText>
156
+ </View>
157
+ </View>
158
+
159
+ {/* Trailing Icon */}
160
+ <View
161
+ testID="profile-card-trailing"
162
+ className={cn(trailingIconVariants())}
163
+ >
164
+ <ChevronRight size={16} color="white" />
165
+ </View>
166
+ </Pressable>
167
+ );
168
+ }
169
+
170
+ ProfileCard.displayName = 'ProfileCard';
171
+
172
+ export { rootVariants };
173
+ export type { ProfileCardProps };
@@ -0,0 +1 @@
1
+ export * from './ProfileCard';