@proulr/sdui-components 0.1.15 → 0.1.17

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": "@proulr/sdui-components",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "main": "./src/renderer/index.ts",
5
5
  "types": "./src/renderer/index.ts",
6
6
  "exports": {
@@ -13,6 +13,7 @@ function layoutBlock(type: string, props: LayoutProps): BdcElement {
13
13
 
14
14
  type ChipProps = LayoutProps & {
15
15
  label: string;
16
+ glyph?: string;
16
17
  actionId?: string;
17
18
  actionParams?: Record<string, string>;
18
19
  active?: boolean;
@@ -0,0 +1,68 @@
1
+ import type { BdcComponent, BdcElement } from '../jsx-runtime';
2
+
3
+ type LayoutProps = {
4
+ id?: string;
5
+ gap?: number | string;
6
+ padding?: number | string;
7
+ style?: Record<string, unknown>;
8
+ children?: BdcElement[];
9
+ };
10
+
11
+ function layoutBlock(type: string, props: LayoutProps): BdcElement {
12
+ return { type, props: { ...props }, children: props.children ?? [] };
13
+ }
14
+
15
+ type SegmentedTabsProps = LayoutProps & {
16
+ segmentsRef?: string;
17
+ activeRef?: string;
18
+ actionPrefix?: string;
19
+ };
20
+
21
+ export const SegmentedTabs: BdcComponent = (props) =>
22
+ layoutBlock('segmented_tabs', props as SegmentedTabsProps);
23
+
24
+ type EmptyStateProps = LayoutProps & {
25
+ title?: string;
26
+ titleRef?: string;
27
+ hint?: string;
28
+ hintRef?: string;
29
+ };
30
+
31
+ export const EmptyState: BdcComponent = (props) => layoutBlock('empty_state', props as EmptyStateProps);
32
+
33
+ type InboxPersonRowProps = LayoutProps & {
34
+ variant: 'visit' | 'flash' | 'chat';
35
+ rowRef: string;
36
+ actionId?: string;
37
+ actionParams?: Record<string, string>;
38
+ };
39
+
40
+ export const InboxPersonRow: BdcComponent = (props) =>
41
+ layoutBlock('inbox_person_row', props as InboxPersonRowProps);
42
+
43
+ type InboxSearchFieldProps = LayoutProps & {
44
+ placeholder?: string;
45
+ layout?: 'wide_only' | 'always';
46
+ segment?: string;
47
+ };
48
+
49
+ export const InboxSearchField: BdcComponent = (props) =>
50
+ layoutBlock('inbox_search_field', props as InboxSearchFieldProps);
51
+
52
+ type InboxCapabilitiesProps = {
53
+ id?: string;
54
+ dataRef?: string;
55
+ };
56
+
57
+ function capabilityBlock(type: string, props: InboxCapabilitiesProps): BdcElement {
58
+ return { type, props: { ...props }, children: [] };
59
+ }
60
+
61
+ export const ChatInboxCapabilities: BdcComponent = (props) =>
62
+ capabilityBlock('chat_inbox_capabilities', props as InboxCapabilitiesProps);
63
+
64
+ export const FlashInboxCapabilities: BdcComponent = (props) =>
65
+ capabilityBlock('flash_inbox_capabilities', props as InboxCapabilitiesProps);
66
+
67
+ export const ProfileVisitsCapabilities: BdcComponent = (props) =>
68
+ capabilityBlock('profile_visits_capabilities', props as InboxCapabilitiesProps);
@@ -18,6 +18,7 @@ function nativeBlock(type: string, props: NativeBlockProps): BdcElement {
18
18
 
19
19
  export const FlashInboxList: BdcComponent = (props) => nativeBlock('flash_inbox_list', props);
20
20
  export const ChatInboxList: BdcComponent = (props) => nativeBlock('chat_inbox_list', props);
21
+ export const ProfileVisitsList: BdcComponent = (props) => nativeBlock('profile_visits_list', props);
21
22
  export const ChatThread: BdcComponent = (props) => nativeBlock('chat_thread', props);
22
23
  export const SocialPostFeed: BdcComponent = (props) => nativeBlock('social_post_feed', props);
23
24
  export const ThoughtFeed: BdcComponent = (props) => nativeBlock('thought_feed', props);
package/src/bdc/index.ts CHANGED
@@ -16,6 +16,15 @@ export {
16
16
  screenProps,
17
17
  } from './components/ui';
18
18
  export { Chip, ChipRow, DiscoverGrid, DiscoverMap, DiscoverProfileCard } from './components/discover';
19
+ export {
20
+ ChatInboxCapabilities,
21
+ EmptyState,
22
+ FlashInboxCapabilities,
23
+ InboxPersonRow,
24
+ InboxSearchField,
25
+ ProfileVisitsCapabilities,
26
+ SegmentedTabs,
27
+ } from './components/inbox';
19
28
  export { TextField } from './components/forms';
20
29
  export { ProfileCompletionBanner, StoryRail } from './components/social';
21
30
  export {
@@ -24,6 +33,7 @@ export {
24
33
  ChatInboxList,
25
34
  ChatThread,
26
35
  FlashInboxList,
36
+ ProfileVisitsList,
27
37
  NewPostComposer,
28
38
  NewStoryComposer,
29
39
  PostComments,
@@ -263,6 +263,7 @@ export function SduiDiscoverGridBlock({ block, data, onAction }: SduiBlockRender
263
263
 
264
264
  export function SduiChipBlock({ block, onAction }: SduiBlockRenderProps) {
265
265
  const label = readStringProp(block, 'label') ?? '';
266
+ const glyph = readStringProp(block, 'glyph');
266
267
  const actionId = readStringProp(block, 'action_id');
267
268
  const actionParams = readStringRecordProp(block, 'action_params');
268
269
  const active = readBooleanProp(block, 'active');
@@ -275,6 +276,11 @@ export function SduiChipBlock({ block, onAction }: SduiBlockRenderProps) {
275
276
  onPress={() => actionId && onAction(actionId, actionParams)}
276
277
  disabled={!actionId}
277
278
  >
279
+ {glyph ? (
280
+ <Text style={[styles.chipLabel, active && styles.chipLabelActive]} accessibilityElementsHidden>
281
+ {glyph}{' '}
282
+ </Text>
283
+ ) : null}
278
284
  <Text style={[styles.chipLabel, active && styles.chipLabelActive]}>{label}</Text>
279
285
  </Pressable>
280
286
  );
@@ -289,7 +295,9 @@ export function SduiChipRowBlock({ block, children }: SduiBlockRenderProps) {
289
295
  <ScrollView
290
296
  horizontal
291
297
  showsHorizontalScrollIndicator={false}
298
+ style={styles.chipRowScroll}
292
299
  contentContainerStyle={[styles.chipRow, isHeader && styles.chipRowOnHeader]}
300
+ keyboardShouldPersistTaps="handled"
293
301
  >
294
302
  {children}
295
303
  </ScrollView>
@@ -433,17 +441,27 @@ const styles = StyleSheet.create({
433
441
  fontWeight: '600',
434
442
  fontFamily: fonts.semibold,
435
443
  },
444
+ chipRowScroll: {
445
+ flexGrow: 0,
446
+ flexShrink: 0,
447
+ alignSelf: 'stretch',
448
+ },
436
449
  chipRow: {
437
450
  flexDirection: 'row',
438
- gap: spacing.sm,
451
+ alignItems: 'center',
452
+ gap: spacing.xs,
439
453
  paddingHorizontal: spacing.sm,
454
+ paddingVertical: 0,
440
455
  },
441
456
  chipRowOnHeader: {
442
- paddingHorizontal: 0,
457
+ paddingHorizontal: spacing.sm,
443
458
  },
444
459
  chip: {
445
- paddingHorizontal: spacing.md,
446
- paddingVertical: spacing.sm,
460
+ flexDirection: 'row',
461
+ alignItems: 'center',
462
+ alignSelf: 'center',
463
+ paddingHorizontal: spacing.sm + 2,
464
+ paddingVertical: spacing.xs,
447
465
  borderRadius: m3Shape.cornerFull,
448
466
  backgroundColor: m3Colors.surfaceContainerHigh,
449
467
  borderWidth: 1,
@@ -459,8 +477,8 @@ const styles = StyleSheet.create({
459
477
  },
460
478
  chipLabel: {
461
479
  color: m3Colors.onSurface,
462
- fontSize: 13,
463
- fontFamily: fonts.medium,
480
+ fontSize: 12,
481
+ fontFamily: fonts.semibold,
464
482
  },
465
483
  chipLabelActive: {
466
484
  color: m3Colors.onPrimaryContainer,
@@ -0,0 +1,292 @@
1
+ import React from 'react';
2
+ import { Pressable, StyleSheet, Text, View } from 'react-native';
3
+ import type { SduiBlock } from '@proulr/sdui-contract';
4
+ import { Card } from '../ui/Card';
5
+ import { fonts, m3Colors, m3Shape, spacing } from '../theme/proulr-theme';
6
+ import { resolveDataRef, resolveDataRefString } from '../lib/resolve-data-ref';
7
+ import { useSduiHybridRenderer } from '../sdui-hybrid-renderer-context';
8
+ import type { SduiBlockRenderProps } from './SduiPrimitiveBlocks';
9
+
10
+ function readStringProp(block: SduiBlock, key: string): string | undefined {
11
+ const value = block.props[key];
12
+ return typeof value === 'string' ? value : undefined;
13
+ }
14
+
15
+ function readStringRecordProp(block: SduiBlock, key: string): Record<string, string> | undefined {
16
+ const value = block.props[key];
17
+ if (!value || typeof value !== 'object') return undefined;
18
+ const entries = Object.entries(value as Record<string, unknown>).filter(
19
+ (entry): entry is [string, string] => typeof entry[1] === 'string',
20
+ );
21
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
22
+ }
23
+
24
+ type HubSegmentWire = { id: string; label: string; glyph?: string };
25
+
26
+ function readHubSegments(data: Record<string, unknown>, ref?: string): HubSegmentWire[] {
27
+ const hub = resolveDataRef(data, ref?.split('.')[0] ?? 'hub');
28
+ if (!hub || typeof hub !== 'object') return [];
29
+ const segments = (hub as Record<string, unknown>).segments;
30
+ if (!Array.isArray(segments)) return [];
31
+ return segments.filter(
32
+ (item): item is HubSegmentWire =>
33
+ Boolean(item && typeof item === 'object' && typeof (item as HubSegmentWire).id === 'string'),
34
+ );
35
+ }
36
+
37
+ function readActiveSegment(data: Record<string, unknown>, ref?: string): string {
38
+ const value = resolveDataRefString(data, ref ?? 'hub.active_segment');
39
+ return value || 'chats';
40
+ }
41
+
42
+ export function SduiSegmentedTabsBlock({ block, data, onAction }: SduiBlockRenderProps) {
43
+ const hybrid = useSduiHybridRenderer();
44
+ if (hybrid.renderSegmentedTabs) {
45
+ return (
46
+ <>
47
+ {hybrid.renderSegmentedTabs({
48
+ block,
49
+ data,
50
+ onAction,
51
+ segmentsRef: readStringProp(block, 'segments_ref'),
52
+ activeRef: readStringProp(block, 'active_ref'),
53
+ actionPrefix: readStringProp(block, 'action_prefix') ?? 'select_segment_',
54
+ })}
55
+ </>
56
+ );
57
+ }
58
+
59
+ const segmentsRef = readStringProp(block, 'segments_ref') ?? 'hub.segments';
60
+ const activeRef = readStringProp(block, 'active_ref') ?? 'hub.active_segment';
61
+ const actionPrefix = readStringProp(block, 'action_prefix') ?? 'select_segment_';
62
+ const segments = readHubSegments(data, segmentsRef);
63
+ const active = readActiveSegment(data, activeRef);
64
+
65
+ return (
66
+ <View style={styles.segmentTrack} accessibilityRole="tablist">
67
+ {segments.map((segment) => {
68
+ const selected = segment.id === active;
69
+ return (
70
+ <Pressable
71
+ key={segment.id}
72
+ style={[styles.segment, selected && styles.segmentActive]}
73
+ onPress={() => onAction(`${actionPrefix}${segment.id}`)}
74
+ accessibilityRole="tab"
75
+ accessibilityState={{ selected }}
76
+ >
77
+ {segment.glyph ? <Text style={styles.segmentGlyph}>{segment.glyph}</Text> : null}
78
+ <Text style={[styles.segmentLabel, selected && styles.segmentLabelActive]}>
79
+ {segment.label}
80
+ </Text>
81
+ </Pressable>
82
+ );
83
+ })}
84
+ </View>
85
+ );
86
+ }
87
+
88
+ export function SduiEmptyStateBlock({ block, data }: SduiBlockRenderProps) {
89
+ const hybrid = useSduiHybridRenderer();
90
+ const title =
91
+ readStringProp(block, 'title') || resolveDataRefString(data, readStringProp(block, 'title_ref'));
92
+ const hint =
93
+ readStringProp(block, 'hint') || resolveDataRefString(data, readStringProp(block, 'hint_ref'));
94
+
95
+ if (hybrid.renderEmptyState) {
96
+ return <>{hybrid.renderEmptyState({ title, hint })}</>;
97
+ }
98
+
99
+ return (
100
+ <Card style={styles.emptyCard}>
101
+ <Text style={styles.emptyTitle}>{title}</Text>
102
+ {hint ? <Text style={styles.emptyHint}>{hint}</Text> : null}
103
+ </Card>
104
+ );
105
+ }
106
+
107
+ export function SduiInboxPersonRowBlock({ block, data, onAction }: SduiBlockRenderProps) {
108
+ const hybrid = useSduiHybridRenderer();
109
+ const variant = readStringProp(block, 'variant');
110
+ const rowRef = readStringProp(block, 'row_ref');
111
+ const actionId = readStringProp(block, 'action_id');
112
+ const actionParams = readStringRecordProp(block, 'action_params');
113
+
114
+ if (
115
+ hybrid.renderInboxPersonRow &&
116
+ variant &&
117
+ rowRef &&
118
+ (variant === 'visit' || variant === 'flash' || variant === 'chat')
119
+ ) {
120
+ return (
121
+ <>
122
+ {hybrid.renderInboxPersonRow({
123
+ block,
124
+ data,
125
+ onAction,
126
+ variant,
127
+ rowRef,
128
+ actionId,
129
+ actionParams,
130
+ })}
131
+ </>
132
+ );
133
+ }
134
+
135
+ const row = rowRef ? resolveDataRef(data, rowRef) : null;
136
+ if (!row || typeof row !== 'object') return null;
137
+ const record = row as Record<string, unknown>;
138
+ const title =
139
+ resolveDataRefString(data, `${rowRef}.viewer_name`) ||
140
+ resolveDataRefString(data, `${rowRef}.sender_name`) ||
141
+ resolveDataRefString(data, `${rowRef}.peer_display_name`) ||
142
+ 'Alguém';
143
+
144
+ return (
145
+ <Pressable
146
+ onPress={() => actionId && onAction(actionId, actionParams)}
147
+ disabled={!actionId}
148
+ >
149
+ <Card style={styles.rowCard}>
150
+ <Text style={styles.rowTitle}>{title}</Text>
151
+ {typeof record.viewed_at === 'string' ? (
152
+ <Text style={styles.rowMeta}>Visualizou seu perfil</Text>
153
+ ) : null}
154
+ </Card>
155
+ </Pressable>
156
+ );
157
+ }
158
+
159
+ export function SduiInboxSearchFieldBlock({ block, data, onAction }: SduiBlockRenderProps) {
160
+ const hybrid = useSduiHybridRenderer();
161
+ const placeholder = readStringProp(block, 'placeholder') ?? 'Buscar…';
162
+ const layout = readStringProp(block, 'layout') ?? 'wide_only';
163
+ const segment = readStringProp(block, 'segment') ?? 'chats';
164
+ const activeSegment = readActiveSegment(data, 'hub.active_segment');
165
+
166
+ if (segment && activeSegment !== segment) return null;
167
+
168
+ if (hybrid.renderInboxSearchField) {
169
+ return (
170
+ <>
171
+ {hybrid.renderInboxSearchField({
172
+ block,
173
+ data,
174
+ onAction,
175
+ placeholder,
176
+ layout,
177
+ segment,
178
+ })}
179
+ </>
180
+ );
181
+ }
182
+
183
+ return null;
184
+ }
185
+
186
+ function CapabilitiesShell({
187
+ block,
188
+ data,
189
+ onAction,
190
+ rendererKey,
191
+ }: SduiBlockRenderProps & {
192
+ rendererKey:
193
+ | 'renderChatInboxCapabilities'
194
+ | 'renderFlashInboxCapabilities'
195
+ | 'renderProfileVisitsCapabilities';
196
+ }) {
197
+ const hybrid = useSduiHybridRenderer();
198
+ const renderer = hybrid[rendererKey];
199
+ if (typeof renderer !== 'function') return null;
200
+ return (
201
+ <>
202
+ {renderer({
203
+ block,
204
+ data,
205
+ onAction,
206
+ dataRef: readStringProp(block, 'data_ref'),
207
+ })}
208
+ </>
209
+ );
210
+ }
211
+
212
+ export function SduiChatInboxCapabilitiesBlock(props: SduiBlockRenderProps) {
213
+ return <CapabilitiesShell {...props} rendererKey="renderChatInboxCapabilities" />;
214
+ }
215
+
216
+ export function SduiFlashInboxCapabilitiesBlock(props: SduiBlockRenderProps) {
217
+ return <CapabilitiesShell {...props} rendererKey="renderFlashInboxCapabilities" />;
218
+ }
219
+
220
+ export function SduiProfileVisitsCapabilitiesBlock(props: SduiBlockRenderProps) {
221
+ return <CapabilitiesShell {...props} rendererKey="renderProfileVisitsCapabilities" />;
222
+ }
223
+
224
+ const styles = StyleSheet.create({
225
+ segmentTrack: {
226
+ flexDirection: 'row',
227
+ flexGrow: 0,
228
+ flexShrink: 0,
229
+ alignSelf: 'stretch',
230
+ gap: spacing.sm,
231
+ backgroundColor: m3Colors.surfaceContainerHigh,
232
+ borderRadius: m3Shape.cornerFull,
233
+ padding: spacing.xs,
234
+ marginHorizontal: spacing.sm,
235
+ },
236
+ segment: {
237
+ flex: 1,
238
+ flexDirection: 'row',
239
+ alignItems: 'center',
240
+ justifyContent: 'center',
241
+ gap: spacing.xs,
242
+ paddingVertical: spacing.sm,
243
+ paddingHorizontal: spacing.sm,
244
+ borderRadius: m3Shape.cornerFull,
245
+ minHeight: 40,
246
+ },
247
+ segmentActive: {
248
+ backgroundColor: m3Colors.primaryContainer,
249
+ },
250
+ segmentGlyph: {
251
+ fontSize: 14,
252
+ },
253
+ segmentLabel: {
254
+ color: m3Colors.onSurfaceVariant,
255
+ fontFamily: fonts.semibold,
256
+ fontSize: 14,
257
+ },
258
+ segmentLabelActive: {
259
+ color: m3Colors.onPrimaryContainer,
260
+ },
261
+ emptyCard: {
262
+ alignItems: 'center',
263
+ gap: spacing.sm,
264
+ paddingVertical: spacing.xxl,
265
+ marginHorizontal: spacing.sm,
266
+ },
267
+ emptyTitle: {
268
+ color: m3Colors.onSurfaceVariant,
269
+ fontSize: 15,
270
+ fontFamily: fonts.medium,
271
+ textAlign: 'center',
272
+ },
273
+ emptyHint: {
274
+ color: m3Colors.onSurfaceVariant,
275
+ fontSize: 13,
276
+ textAlign: 'center',
277
+ },
278
+ rowCard: {
279
+ padding: spacing.md,
280
+ marginHorizontal: spacing.sm,
281
+ },
282
+ rowTitle: {
283
+ color: m3Colors.onSurface,
284
+ fontSize: 16,
285
+ fontFamily: fonts.semibold,
286
+ },
287
+ rowMeta: {
288
+ color: m3Colors.onSurfaceVariant,
289
+ fontSize: 12,
290
+ marginTop: spacing.xs,
291
+ },
292
+ });
@@ -20,6 +20,7 @@ function NativeBlockShell({
20
20
  rendererKey:
21
21
  | 'renderFlashInboxList'
22
22
  | 'renderChatInboxList'
23
+ | 'renderProfileVisitsList'
23
24
  | 'renderChatThread'
24
25
  | 'renderSocialPostFeed'
25
26
  | 'renderThoughtFeed'
@@ -75,6 +76,10 @@ export function SduiChatInboxListBlock(props: SduiBlockRenderProps) {
75
76
  return <NativeBlockShell {...props} rendererKey="renderChatInboxList" fallbackLabel="Chat" />;
76
77
  }
77
78
 
79
+ export function SduiProfileVisitsListBlock(props: SduiBlockRenderProps) {
80
+ return <NativeBlockShell {...props} rendererKey="renderProfileVisitsList" fallbackLabel="Visitas" />;
81
+ }
82
+
78
83
  export function SduiChatThreadBlock(props: SduiBlockRenderProps) {
79
84
  return <NativeBlockShell {...props} rendererKey="renderChatThread" fallbackLabel="Conversa" />;
80
85
  }
@@ -29,6 +29,15 @@ import {
29
29
  SduiProfileCompletionBannerBlock,
30
30
  SduiStoryRailBlock,
31
31
  } from './blocks/SduiSocialBlocks';
32
+ import {
33
+ SduiChatInboxCapabilitiesBlock,
34
+ SduiEmptyStateBlock,
35
+ SduiFlashInboxCapabilitiesBlock,
36
+ SduiInboxPersonRowBlock,
37
+ SduiInboxSearchFieldBlock,
38
+ SduiProfileVisitsCapabilitiesBlock,
39
+ SduiSegmentedTabsBlock,
40
+ } from './blocks/SduiInboxBlocks';
32
41
  import { SduiNativeAdBlock } from './blocks/SduiNativeAdBlock';
33
42
  import {
34
43
  SduiAlbumDetailBlock,
@@ -36,6 +45,7 @@ import {
36
45
  SduiChatInboxListBlock,
37
46
  SduiChatThreadBlock,
38
47
  SduiFlashInboxListBlock,
48
+ SduiProfileVisitsListBlock,
39
49
  SduiNewPostComposerBlock,
40
50
  SduiNewStoryComposerBlock,
41
51
  SduiPostCommentsBlock,
@@ -70,6 +80,13 @@ const REGISTRY: Record<string, SduiBlockComponent> = {
70
80
  toggle_row: SduiToggleRowBlock,
71
81
  chip: SduiChipBlock,
72
82
  chip_row: SduiChipRowBlock,
83
+ segmented_tabs: SduiSegmentedTabsBlock,
84
+ empty_state: SduiEmptyStateBlock,
85
+ inbox_person_row: SduiInboxPersonRowBlock,
86
+ inbox_search_field: SduiInboxSearchFieldBlock,
87
+ chat_inbox_capabilities: SduiChatInboxCapabilitiesBlock,
88
+ flash_inbox_capabilities: SduiFlashInboxCapabilitiesBlock,
89
+ profile_visits_capabilities: SduiProfileVisitsCapabilitiesBlock,
73
90
  discover_grid: SduiDiscoverGridBlock,
74
91
  discover_profile_card: SduiDiscoverProfileCardBlock,
75
92
  discover_map: SduiDiscoverMapBlock,
@@ -78,6 +95,7 @@ const REGISTRY: Record<string, SduiBlockComponent> = {
78
95
  profile_completion_banner: SduiProfileCompletionBannerBlock,
79
96
  flash_inbox_list: SduiFlashInboxListBlock,
80
97
  chat_inbox_list: SduiChatInboxListBlock,
98
+ profile_visits_list: SduiProfileVisitsListBlock,
81
99
  chat_thread: SduiChatThreadBlock,
82
100
  social_post_feed: SduiSocialPostFeedBlock,
83
101
  thought_feed: SduiThoughtFeedBlock,
@@ -56,6 +56,7 @@ export type SduiHybridRenderers = {
56
56
  onDiscoverMapCenterChange?: (coords: { lat: number; lng: number }) => void;
57
57
  renderFlashInboxList?: (props: SduiNativeBlockRenderProps) => ReactNode;
58
58
  renderChatInboxList?: (props: SduiNativeBlockRenderProps) => ReactNode;
59
+ renderProfileVisitsList?: (props: SduiNativeBlockRenderProps) => ReactNode;
59
60
  renderChatThread?: (props: SduiNativeBlockRenderProps) => ReactNode;
60
61
  renderSocialPostFeed?: (props: SduiNativeBlockRenderProps) => ReactNode;
61
62
  renderThoughtFeed?: (props: SduiNativeBlockRenderProps) => ReactNode;
@@ -72,6 +73,35 @@ export type SduiHybridRenderers = {
72
73
  renderSocialProfile?: (props: SduiNativeBlockRenderProps) => ReactNode;
73
74
  renderSettingsHome?: (props: SduiNativeBlockRenderProps) => ReactNode;
74
75
  renderDiscoverBrowseLocation?: (props: SduiNativeBlockRenderProps) => ReactNode;
76
+ renderSegmentedTabs?: (props: {
77
+ block: SduiBlock;
78
+ data: Record<string, unknown>;
79
+ onAction: (actionId: string, vars?: Record<string, string>) => void;
80
+ segmentsRef?: string;
81
+ activeRef?: string;
82
+ actionPrefix?: string;
83
+ }) => ReactNode;
84
+ renderEmptyState?: (props: { title: string; hint?: string }) => ReactNode;
85
+ renderInboxPersonRow?: (props: {
86
+ block: SduiBlock;
87
+ data: Record<string, unknown>;
88
+ onAction: (actionId: string, vars?: Record<string, string>) => void;
89
+ variant: 'visit' | 'flash' | 'chat';
90
+ rowRef: string;
91
+ actionId?: string;
92
+ actionParams?: Record<string, string>;
93
+ }) => ReactNode;
94
+ renderInboxSearchField?: (props: {
95
+ block: SduiBlock;
96
+ data: Record<string, unknown>;
97
+ onAction: (actionId: string, vars?: Record<string, string>) => void;
98
+ placeholder: string;
99
+ layout: string;
100
+ segment: string;
101
+ }) => ReactNode;
102
+ renderChatInboxCapabilities?: (props: SduiNativeBlockRenderProps) => ReactNode;
103
+ renderFlashInboxCapabilities?: (props: SduiNativeBlockRenderProps) => ReactNode;
104
+ renderProfileVisitsCapabilities?: (props: SduiNativeBlockRenderProps) => ReactNode;
75
105
  };
76
106
 
77
107
  const SduiHybridRendererContext = createContext<SduiHybridRenderers>({});