@campxdev/react-native-blueprint 0.1.23 → 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 +77 -129
  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 +71 -157
  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
@@ -1,17 +1,25 @@
1
1
  // @ts-nocheck
2
- import { cva, type VariantProps } from 'class-variance-authority';
3
2
  import * as React from 'react';
4
3
  import {
5
4
  Pressable,
6
- Text as RNText,
7
5
  View,
6
+ Text as RNText,
8
7
  type GestureResponderEvent,
9
- type PressableProps,
10
8
  type StyleProp,
11
9
  type ViewStyle,
10
+ type PressableProps,
12
11
  } from 'react-native';
12
+ import { cssInterop } from 'nativewind';
13
13
  import { SquircleView } from 'react-native-figma-squircle';
14
- import { useTheme } from '../../shared/theme';
14
+ import { cva, type VariantProps } from 'class-variance-authority';
15
+
16
+ import { cn } from '../../../lib/utils';
17
+ import { useThemeColors } from '../../../lib/theme';
18
+
19
+ // NativeWind interop (className -> style)
20
+ cssInterop(Pressable, { className: 'style' });
21
+ cssInterop(View, { className: 'style' });
22
+ cssInterop(RNText, { className: 'style' });
15
23
 
16
24
  /**
17
25
  * ✅ Canonical values (match Figma)
@@ -22,38 +30,31 @@ export const ButtonVariants = {
22
30
  } as const;
23
31
 
24
32
  /**
25
- * Local lightweight placeholder icon
33
+ * Local lightweight placeholder icon (used in demos/tests)
34
+ * ✅ No inline styles (satisfies react-native/no-inline-styles)
26
35
  */
27
36
  function DummyIcon({ testID }: { testID?: string }) {
28
- return (
29
- <View
30
- testID={testID}
31
- style={{
32
- width: 16,
33
- height: 16,
34
- borderRadius: 4,
35
- backgroundColor: '#6a7282',
36
- }}
37
- />
38
- );
37
+ return <View testID={testID} className="w-4 h-4 rounded bg-text-inverse" />;
39
38
  }
40
39
 
41
40
  /**
42
- * Returns squircle fill & stroke colors per variant from theme
41
+ * Returns squircle fill & stroke colors per variant from THEME tokens
43
42
  */
44
- function useVariantSquircleParams(variant: string, colors: any) {
43
+ function useVariantSquircleParams(variant: string) {
44
+ const colors = useThemeColors();
45
+
45
46
  const map: Record<
46
47
  string,
47
48
  { fillColor: string; strokeColor: string; strokeWidth: number }
48
49
  > = {
49
50
  default: {
50
- fillColor: colors.primary,
51
+ fillColor: colors.brandPrimary,
51
52
  strokeColor: 'transparent',
52
53
  strokeWidth: 0,
53
54
  },
54
55
  secondary: {
55
- fillColor: colors.secondary,
56
- strokeColor: colors.primary,
56
+ fillColor: colors.brandSecondary,
57
+ strokeColor: colors.border,
57
58
  strokeWidth: 1,
58
59
  },
59
60
  outline: {
@@ -81,34 +82,37 @@ function useVariantSquircleParams(variant: string, colors: any) {
81
82
  return map[variant] ?? map.default;
82
83
  }
83
84
 
84
- const CORNER_RADIUS = 12;
85
- const CORNER_SMOOTHING = 1;
85
+ const CORNER_RADIUS = 8; // radius-lg
86
+ const CORNER_SMOOTHING = 1; // maximum smoothing
86
87
 
87
88
  const buttonVariants = cva('flex-row items-center justify-center', {
88
89
  variants: {
89
90
  variant: {
90
- default: 'gap-2',
91
- secondary: 'gap-2',
92
- outline: 'gap-2',
93
- ghost: 'gap-2',
94
- destructive: 'gap-2',
91
+ default: 'gap-2 active:opacity-90',
92
+ secondary: 'active:opacity-90',
93
+ outline: 'active:opacity-90',
94
+ ghost: 'active:opacity-90',
95
+ destructive: 'active:opacity-90',
95
96
  link: '',
96
97
  },
97
98
  size: {
99
+ // ✅ Figma-like sizing
98
100
  default: 'py-3 px-5',
99
- lg: 'py-4 px-6',
100
- icon: 'h-10 w-10',
101
+ lg: 'py-5 px-6',
102
+ icon: 'h-10 w-10 p-3 justify-center items-center',
101
103
  },
102
104
  disabled: {
103
105
  false: '',
104
106
  true: 'opacity-50',
105
107
  },
108
+ // Optional icon toggles still affect spacing correctly
106
109
  hasIcons: {
107
110
  false: '',
108
111
  true: 'gap-2',
109
112
  },
110
113
  },
111
114
  compoundVariants: [
115
+ // ✅ Link should not have forced height/padding
112
116
  { variant: 'link', size: 'default', className: 'h-auto p-0' },
113
117
  { variant: 'link', size: 'lg', className: 'h-auto p-0' },
114
118
  { variant: 'link', size: 'icon', className: 'h-auto p-0 w-auto' },
@@ -124,11 +128,11 @@ const buttonVariants = cva('flex-row items-center justify-center', {
124
128
  const buttonTextVariants = cva('font-semibold', {
125
129
  variants: {
126
130
  variant: {
127
- default: 'text-white',
131
+ default: 'text-text-inverse',
128
132
  secondary: 'text-foreground',
129
133
  outline: 'text-foreground',
130
134
  ghost: 'text-foreground',
131
- destructive: 'text-white',
135
+ destructive: 'text-primary-foreground',
132
136
  link: 'text-primary underline',
133
137
  },
134
138
  size: {
@@ -148,11 +152,21 @@ const buttonTextVariants = cva('font-semibold', {
148
152
  },
149
153
  });
150
154
 
155
+ /**
156
+ * ✅ Strict rule: className must NOT be accepted as a prop.
157
+ * So we omit it explicitly from PressableProps.
158
+ */
151
159
  type PressablePropsWithoutClassName = Omit<PressableProps, 'style'> & {
152
160
  className?: never;
153
161
  };
154
162
 
155
- export type ButtonComponentProps = PressablePropsWithoutClassName &
163
+ /**
164
+ * ✅ Backward compatible types:
165
+ * - supports boolean disabled (new)
166
+ * - supports 'True'/'False' and 'true'/'false' and '1'/'0' (old)
167
+ * - supports capitalized variant/size (old)
168
+ */
169
+ export type ButtonProps = PressablePropsWithoutClassName &
156
170
  VariantProps<typeof buttonVariants> & {
157
171
  variant?:
158
172
  | (typeof ButtonVariants.variant)[number]
@@ -192,7 +206,7 @@ function normalizeDisabled(v: any): boolean {
192
206
  return s === 'true' || s === '1';
193
207
  }
194
208
 
195
- export function ButtonComponent(props: ButtonComponentProps) {
209
+ export function Button(props: ButtonProps) {
196
210
  const {
197
211
  variant,
198
212
  size,
@@ -208,16 +222,17 @@ export function ButtonComponent(props: ButtonComponentProps) {
208
222
  ...rest
209
223
  } = props;
210
224
 
211
- const { colors } = useTheme();
212
-
213
225
  const v = normalizeVariant(variant);
214
226
  const s = normalizeSize(size);
215
227
  const d = normalizeDisabled(disabled);
216
228
 
229
+ // ✅ icon button must be square and must NOT show label (prevents long button)
217
230
  const isIconOnly = s === 'icon';
231
+
232
+ // spacing only when icons exist
218
233
  const hasIcons = Boolean(showLeftIcon || showRightIcon);
219
234
 
220
- const squircleParams = useVariantSquircleParams(v, colors);
235
+ const squircleParams = useVariantSquircleParams(v);
221
236
 
222
237
  const handlePress = React.useCallback(
223
238
  (e: GestureResponderEvent) => {
@@ -227,100 +242,9 @@ export function ButtonComponent(props: ButtonComponentProps) {
227
242
  [d, onPress]
228
243
  );
229
244
 
230
- // Get text color based on variant
231
- const getTextColor = () => {
232
- switch (v) {
233
- case 'default':
234
- return colors.primaryForeground;
235
- case 'secondary':
236
- return colors.secondaryForeground;
237
- case 'outline':
238
- return colors.foreground;
239
- case 'ghost':
240
- return colors.foreground;
241
- case 'destructive':
242
- return colors.destructiveForeground;
243
- case 'link':
244
- return colors.primary;
245
- default:
246
- return colors.primaryForeground;
247
- }
248
- };
249
-
250
- // Get size styles
251
- const getSizeStyles = () => {
252
- switch (s) {
253
- case 'lg':
254
- return { height: 52, paddingHorizontal: 16 };
255
- case 'icon':
256
- return { height: 40, width: 40 };
257
- case 'default':
258
- default:
259
- return { height: 48, paddingHorizontal: 16 };
260
- }
261
- };
262
-
263
- const sizeStyles = getSizeStyles();
264
-
265
- if (v === 'link') {
266
- // Link variant doesn't use SquircleView
267
- return (
268
- <Pressable
269
- testID={testID ?? 'button'}
270
- accessibilityRole="button"
271
- accessibilityState={{ disabled: d }}
272
- disabled={d}
273
- onPress={handlePress}
274
- style={[
275
- {
276
- flexDirection: 'row',
277
- alignItems: 'center',
278
- justifyContent: 'center',
279
- opacity: d ? 0.5 : 1,
280
- },
281
- style,
282
- ]}
283
- {...rest}
284
- >
285
- {showLeftIcon ? (
286
- leftIcon ? (
287
- leftIcon
288
- ) : (
289
- <DummyIcon testID="button-left-icon" />
290
- )
291
- ) : null}
292
-
293
- {!isIconOnly ? (
294
- <RNText
295
- testID="button-label"
296
- style={{
297
- color: getTextColor(),
298
- fontSize: s === 'lg' ? 16 : 14,
299
- fontWeight: '600',
300
- textDecorationLine: 'underline',
301
- }}
302
- numberOfLines={1}
303
- ellipsizeMode="tail"
304
- >
305
- {children}
306
- </RNText>
307
- ) : null}
308
-
309
- {showRightIcon ? (
310
- rightIcon ? (
311
- rightIcon
312
- ) : (
313
- <DummyIcon testID="button-right-icon" />
314
- )
315
- ) : null}
316
- </Pressable>
317
- );
318
- }
319
-
320
245
  return (
321
246
  <View style={style}>
322
247
  <SquircleView
323
- style={sizeStyles}
324
248
  squircleParams={{
325
249
  cornerRadius: CORNER_RADIUS,
326
250
  cornerSmoothing: CORNER_SMOOTHING,
@@ -333,32 +257,26 @@ export function ButtonComponent(props: ButtonComponentProps) {
333
257
  accessibilityState={{ disabled: d }}
334
258
  disabled={d}
335
259
  onPress={handlePress}
336
- style={{
337
- flex: 1,
338
- flexDirection: 'row',
339
- alignItems: 'center',
340
- justifyContent: 'center',
341
- gap: hasIcons ? 8 : 0,
342
- opacity: d ? 0.5 : 1,
343
- }}
260
+ className={cn(
261
+ buttonVariants({
262
+ variant: v,
263
+ size: s,
264
+ disabled: d,
265
+ hasIcons,
266
+ })
267
+ )}
344
268
  {...rest}
345
269
  >
346
- {showLeftIcon ? (
347
- leftIcon ? (
348
- leftIcon
349
- ) : (
350
- <DummyIcon testID="button-left-icon" />
351
- )
352
- ) : null}
270
+ {showLeftIcon
271
+ ? leftIcon || <DummyIcon testID="button-left-icon" />
272
+ : null}
353
273
 
354
274
  {!isIconOnly ? (
355
275
  <RNText
356
276
  testID="button-label"
357
- style={{
358
- color: getTextColor(),
359
- fontSize: s === 'lg' ? 16 : 14,
360
- fontWeight: '600',
361
- }}
277
+ className={cn(
278
+ buttonTextVariants({ variant: v, size: s, disabled: d })
279
+ )}
362
280
  numberOfLines={1}
363
281
  ellipsizeMode="tail"
364
282
  >
@@ -366,19 +284,15 @@ export function ButtonComponent(props: ButtonComponentProps) {
366
284
  </RNText>
367
285
  ) : null}
368
286
 
369
- {showRightIcon ? (
370
- rightIcon ? (
371
- rightIcon
372
- ) : (
373
- <DummyIcon testID="button-right-icon" />
374
- )
375
- ) : null}
287
+ {showRightIcon
288
+ ? rightIcon || <DummyIcon testID="button-right-icon" />
289
+ : null}
376
290
  </Pressable>
377
291
  </SquircleView>
378
292
  </View>
379
293
  );
380
294
  }
381
295
 
382
- ButtonComponent.displayName = 'Button';
296
+ Button.displayName = 'Button';
383
297
 
384
- export { ButtonComponent as Button, buttonTextVariants, buttonVariants };
298
+ export { buttonTextVariants, buttonVariants };
@@ -21,9 +21,11 @@ const FIGMA_URL =
21
21
  */
22
22
  figma.connect(AppBar, FIGMA_URL, {
23
23
  props: {
24
- variant: figma.enum('variant', {
25
- default: 'default',
26
- home: 'home',
24
+ type: figma.enum('type', {
25
+ 'context': 'context',
26
+ 'brand': 'brand',
27
+ 'brand+switcher': 'brand+switcher',
28
+ 'context+switcher': 'context+switcher',
27
29
  }),
28
30
  showLeading: figma.boolean('Show Leading'),
29
31
  showTrailing: figma.boolean('Show Trailing'),
@@ -37,11 +39,20 @@ figma.connect(AppBar, FIGMA_URL, {
37
39
  search: 'search',
38
40
  }),
39
41
  }),
40
- logo: figma.instance('Logo'),
42
+ logo: figma.nestedProps('Logo', {
43
+ mode: figma.enum('mode', {
44
+ light: 'light',
45
+ dark: 'dark',
46
+ }),
47
+ brand: figma.enum('brand', {
48
+ campx: 'campx',
49
+ winnify: 'winnify',
50
+ }),
51
+ }),
41
52
  },
42
53
  example: (props) => (
43
54
  <AppBar
44
- variant={props.variant}
55
+ type={props.type}
45
56
  title={props.title}
46
57
  subtitle={props.subtitle}
47
58
  showLeading={props.showLeading}
@@ -49,9 +60,10 @@ figma.connect(AppBar, FIGMA_URL, {
49
60
  showSubtitle={props.showSubtitle}
50
61
  showHeaderBlock={props.showHeaderBlock}
51
62
  headerType={props.headerType.type}
63
+ logoMode={props.logo.mode}
64
+ logoBrand={props.logo.brand}
52
65
  onLeadingPress={() => {}}
53
66
  onTrailingPress={() => {}}
54
67
  />
55
68
  ),
56
69
  });
57
- i;
@@ -30,10 +30,16 @@ const Pressable = RNPressable as any;
30
30
  * --------------------------------------------------- */
31
31
 
32
32
  export type AppBarVariant = 'default' | 'home';
33
+ export type AppBarType =
34
+ | 'context'
35
+ | 'brand'
36
+ | 'brand+switcher'
37
+ | 'context+switcher';
33
38
  export type HeaderBlockType = 'tabs' | 'search';
34
39
 
35
40
  export interface AppBarProps {
36
41
  variant?: AppBarVariant;
42
+ type?: AppBarType;
37
43
 
38
44
  title?: string;
39
45
  subtitle?: string;
@@ -51,9 +57,13 @@ export interface AppBarProps {
51
57
  leadingIcon?: React.ReactNode;
52
58
  trailingIcon?: React.ReactNode;
53
59
 
54
- /** For home variant logo */
60
+ /** For home variant logo or brand type */
55
61
  logoSource?: ImageSourcePropType;
56
62
 
63
+ /** Avatar for switcher types (brand+switcher, context+switcher) */
64
+ switcherAvatar?: React.ReactNode;
65
+ onSwitcherPress?: () => void;
66
+
57
67
  /** Whether to wrap with SafeAreaView for status bar */
58
68
  useSafeArea?: boolean;
59
69
 
@@ -90,6 +100,7 @@ function HeaderBlock({ type }: { type: 'tabs' | 'search' }) {
90
100
  export function AppBar(props: AppBarProps) {
91
101
  const {
92
102
  variant = 'default',
103
+ type = 'context',
93
104
  title = 'Page Title',
94
105
  subtitle = 'This is the page Subtitle',
95
106
 
@@ -107,6 +118,8 @@ export function AppBar(props: AppBarProps) {
107
118
  trailingIcon,
108
119
 
109
120
  logoSource,
121
+ switcherAvatar,
122
+ onSwitcherPress,
110
123
 
111
124
  useSafeArea = true,
112
125
 
@@ -116,6 +129,8 @@ export function AppBar(props: AppBarProps) {
116
129
  const { colorScheme } = useColorScheme();
117
130
  const isDark = colorScheme === 'dark';
118
131
  const isHome = variant === 'home';
132
+ const isBrandType = type === 'brand' || type === 'brand+switcher';
133
+ const hasSwitcher = type === 'brand+switcher' || type === 'context+switcher';
119
134
 
120
135
  const content = (
121
136
  <View
@@ -139,7 +154,22 @@ export function AppBar(props: AppBarProps) {
139
154
  </Pressable>
140
155
  )}
141
156
 
142
- {variant === 'default' && (
157
+ {/* Logo for brand+switcher (on left side) */}
158
+ {type === 'brand+switcher' && (
159
+ <View testID="appbar-logo-left">
160
+ {logoSource ? (
161
+ <Image
162
+ source={logoSource}
163
+ resizeMode="contain"
164
+ className="w-8 h-8"
165
+ />
166
+ ) : (
167
+ <View className="w-8 h-8 bg-surface-subtle rounded-lg" />
168
+ )}
169
+ </View>
170
+ )}
171
+
172
+ {!isBrandType && (
143
173
  <View className="gap-1">
144
174
  <Text
145
175
  testID="appbar-title"
@@ -160,8 +190,8 @@ export function AppBar(props: AppBarProps) {
160
190
  )}
161
191
  </View>
162
192
 
163
- {/* CENTER (Home logo) */}
164
- {isHome && (
193
+ {/* CENTER (Home logo or Brand logo - only for 'brand' type) */}
194
+ {(isHome || type === 'brand') && (
165
195
  <View
166
196
  testID="appbar-logo"
167
197
  className="flex-1 items-center justify-center"
@@ -179,15 +209,30 @@ export function AppBar(props: AppBarProps) {
179
209
  )}
180
210
 
181
211
  {/* RIGHT */}
182
- {showTrailing && (
183
- <Pressable
184
- testID="appbar-trailing"
185
- onPress={onTrailingPress}
186
- accessibilityRole="button"
187
- >
188
- {trailingIcon ?? <View className="w-4 h-4" />}
189
- </Pressable>
190
- )}
212
+ <View className="flex-row items-center gap-3">
213
+ {showTrailing && (
214
+ <Pressable
215
+ testID="appbar-trailing"
216
+ onPress={onTrailingPress}
217
+ accessibilityRole="button"
218
+ >
219
+ {trailingIcon ?? <View className="w-4 h-4" />}
220
+ </Pressable>
221
+ )}
222
+ {hasSwitcher && (
223
+ <Pressable
224
+ testID="appbar-switcher"
225
+ onPress={onSwitcherPress}
226
+ className="flex-row items-center gap-2 px-2 py-1 rounded-full border border-border-default"
227
+ accessibilityRole="button"
228
+ >
229
+ {switcherAvatar ?? (
230
+ <View className="w-8 h-8 bg-surface-subtle rounded-full" />
231
+ )}
232
+ <View className="w-4 h-4" />
233
+ </Pressable>
234
+ )}
235
+ </View>
191
236
  </View>
192
237
 
193
238
  {/* HEADER BLOCK */}
@@ -1,13 +1,13 @@
1
1
  // @ts-nocheck
2
2
  import * as React from 'react';
3
- import { Text, View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { View, Text, type StyleProp, type ViewStyle } from 'react-native';
4
4
 
5
5
  import { Button } from '../../Input/Button/Button';
6
6
  import {
7
- PopoverContainer,
8
- PopoverContent,
9
7
  Popover as PopoverPrimitive,
10
8
  PopoverTrigger,
9
+ PopoverContent,
10
+ PopoverContainer,
11
11
  } from '../../Layout/PopoverContainer/Popover-Container';
12
12
 
13
13
  /**
@@ -30,6 +30,7 @@ export * from '../DataDisplay/Banner/Banner';
30
30
  export * from '../DataDisplay/BannerRow/BannerRow';
31
31
  export * from '../DataDisplay/CalendarItem/CalendarItem';
32
32
  export * from '../DataDisplay/Card/Card';
33
+ export * from '../DataDisplay/ChatBubble/ChatBubble';
33
34
  export * from '../DataDisplay/Chips/Chips';
34
35
  export * from '../DataDisplay/ChipsRow/ChipsRow';
35
36
  export * from '../DataDisplay/DataCard/DataCard';
@@ -37,6 +38,7 @@ export * from '../DataDisplay/Datalist/Datalist';
37
38
  export * from '../DataDisplay/DataListItem/DataListItem';
38
39
  export * from '../DataDisplay/FeedCard/FeedCard';
39
40
  export * from '../DataDisplay/Greeting/Greeting';
41
+ export * from '../DataDisplay/ProfileCard/ProfileCard';
40
42
  export * from '../DataDisplay/MonthCalendar/MonthCalendar';
41
43
  export * from '../DataDisplay/Separator/Separator';
42
44
  export * from '../DataDisplay/Skeleton/Skeleton';
package/src/lib/theme.ts CHANGED
@@ -46,8 +46,8 @@ export const THEME = {
46
46
  accentForeground: 'hsl(0, 0%, 100%)', // #FFFFFF
47
47
 
48
48
  // === 6. BORDERS ===
49
- border: 'hsl(214, 32%, 84%)', // #CBD5E1
50
- input: 'hsl(214, 32%, 84%)', // #CBD5E1
49
+ border: 'hsl(210, 13%, 80%)', // #CBD5E1
50
+ input: 'hsl(210, 13%, 80%)', // #CBD5E1
51
51
  ring: 'hsl(200, 13%, 11%)', // #161A1D - Same as primary
52
52
 
53
53
  // === 7. HIGHLIGHTS ===