@proulr/sdui-components 0.1.54 → 0.1.56

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.54",
3
+ "version": "0.1.56",
4
4
  "type": "module",
5
5
  "main": "./src/renderer/index.ts",
6
6
  "types": "./src/renderer/index.ts",
@@ -29,6 +29,7 @@ import {
29
29
  DiscoverBrowseLocation,
30
30
  NewPostComposer,
31
31
  NewStoryComposer,
32
+ StoryViewersPanel,
32
33
  PostComments,
33
34
  ProfileOnboarding,
34
35
  ProfileReport,
@@ -130,6 +131,7 @@ describe('bdc discover/forms/social/inbox/native', () => {
130
131
  AlbumDetail,
131
132
  NewPostComposer,
132
133
  NewStoryComposer,
134
+ StoryViewersPanel,
133
135
  PostComments,
134
136
  SocialProfile,
135
137
  SettingsHome,
@@ -30,6 +30,7 @@ export const AlbumGrid: BdcComponent = (props) => nativeBlock('album_grid', prop
30
30
  export const AlbumDetail: BdcComponent = (props) => nativeBlock('album_detail', props);
31
31
  export const NewPostComposer: BdcComponent = (props) => nativeBlock('new_post_composer', props);
32
32
  export const NewStoryComposer: BdcComponent = (props) => nativeBlock('new_story_composer', props);
33
+ export const StoryViewersPanel: BdcComponent = (props) => nativeBlock('story_viewers_panel', props);
33
34
  export const PostComments: BdcComponent = (props) => nativeBlock('post_comments', props);
34
35
  export const SocialProfile: BdcComponent = (props) => nativeBlock('social_profile', props);
35
36
  export const SettingsHome: BdcComponent = (props) => nativeBlock('settings_home', props);
package/src/bdc/index.ts CHANGED
@@ -38,6 +38,7 @@ export {
38
38
  ChatThread,
39
39
  NewPostComposer,
40
40
  NewStoryComposer,
41
+ StoryViewersPanel,
41
42
  PostComments,
42
43
  ProfileOnboarding,
43
44
  ProfileReport,
@@ -30,6 +30,7 @@ function NativeBlockShell({
30
30
  | 'renderAlbumDetail'
31
31
  | 'renderNewPostComposer'
32
32
  | 'renderNewStoryComposer'
33
+ | 'renderStoryViewersPanel'
33
34
  | 'renderPostComments'
34
35
  | 'renderSocialProfile'
35
36
  | 'renderSettingsHome'
@@ -118,6 +119,12 @@ export function SduiNewStoryComposerBlock(props: SduiBlockRenderProps) {
118
119
  return <NativeBlockShell {...props} rendererKey="renderNewStoryComposer" fallbackLabel="Novo story" />;
119
120
  }
120
121
 
122
+ export function SduiStoryViewersPanelBlock(props: SduiBlockRenderProps) {
123
+ return (
124
+ <NativeBlockShell {...props} rendererKey="renderStoryViewersPanel" fallbackLabel="Story viewers" />
125
+ );
126
+ }
127
+
121
128
  export function SduiPostCommentsBlock(props: SduiBlockRenderProps) {
122
129
  return <NativeBlockShell {...props} rendererKey="renderPostComments" fallbackLabel="Comentários" />;
123
130
  }
@@ -50,6 +50,7 @@ import {
50
50
  SduiChatThreadBlock,
51
51
  SduiNewPostComposerBlock,
52
52
  SduiNewStoryComposerBlock,
53
+ SduiStoryViewersPanelBlock,
53
54
  SduiPostCommentsBlock,
54
55
  SduiProfileOnboardingBlock,
55
56
  SduiProfileSelfBlock,
@@ -117,6 +118,7 @@ const REGISTRY: Record<string, SduiBlockComponent> = {
117
118
  album_detail: SduiAlbumDetailBlock,
118
119
  new_post_composer: SduiNewPostComposerBlock,
119
120
  new_story_composer: SduiNewStoryComposerBlock,
121
+ story_viewers_panel: SduiStoryViewersPanelBlock,
120
122
  post_comments: SduiPostCommentsBlock,
121
123
  social_profile: SduiSocialProfileBlock,
122
124
  settings_home: SduiSettingsHomeBlock,
@@ -66,6 +66,7 @@ export type SduiHybridRenderers = {
66
66
  renderAlbumDetail?: (props: SduiNativeBlockRenderProps) => ReactNode;
67
67
  renderNewPostComposer?: (props: SduiNativeBlockRenderProps) => ReactNode;
68
68
  renderNewStoryComposer?: (props: SduiNativeBlockRenderProps) => ReactNode;
69
+ renderStoryViewersPanel?: (props: SduiNativeBlockRenderProps) => ReactNode;
69
70
  renderPostComments?: (props: SduiNativeBlockRenderProps) => ReactNode;
70
71
  renderSocialProfile?: (props: SduiNativeBlockRenderProps) => ReactNode;
71
72
  renderSettingsHome?: (props: SduiNativeBlockRenderProps) => ReactNode;
@@ -19,6 +19,8 @@ import { ChromeGlassLayer } from './ChromeGlass';
19
19
  import { colors, fonts, m3Colors, m3Shape, spacing } from '../theme/proulr-theme';
20
20
 
21
21
  const ACTION_BUTTON_SIZE = 48;
22
+ /** Filled action circle when nested inside the pill (Instagram-style composer). */
23
+ const ACTION_BUTTON_SIZE_INSET = 36;
22
24
  const FONT_SIZE = 16;
23
25
  const LINE_HEIGHT = 25;
24
26
  const MAX_LINES_DEFAULT = 4;
@@ -216,6 +218,8 @@ export function ComposePillTextField({
216
218
  );
217
219
  }
218
220
 
221
+ export type ComposePillEndActionPlacement = 'outside' | 'inside';
222
+
219
223
  export type ComposePillInputProps = {
220
224
  value: string;
221
225
  onChangeText: (text: string) => void;
@@ -229,6 +233,11 @@ export type ComposePillInputProps = {
229
233
  leading?: ReactNode;
230
234
  trailing?: ReactNode;
231
235
  endAction?: ReactNode;
236
+ /**
237
+ * `outside` — send/mic circle beside the pill (legacy / story reply).
238
+ * `inside` — action nested in the pill (Instagram-style chat composer).
239
+ */
240
+ endActionPlacement?: ComposePillEndActionPlacement;
232
241
  replyPreview?: ComposePillReplyPreview | null;
233
242
  style?: StyleProp<ViewStyle>;
234
243
  transparentWrap?: boolean;
@@ -345,15 +354,23 @@ export function ComposePillActionButton({
345
354
  children,
346
355
  onPress,
347
356
  disabled,
357
+ inset = false,
348
358
  }: {
349
359
  label: string;
350
360
  children: ReactNode;
351
361
  onPress?: () => void;
352
362
  disabled?: boolean;
363
+ /** Smaller filled circle for use inside the composer pill. */
364
+ inset?: boolean;
353
365
  }) {
366
+ const size = inset ? ACTION_BUTTON_SIZE_INSET : ACTION_BUTTON_SIZE;
354
367
  return (
355
368
  <Pressable
356
- style={[styles.actionButton, disabled && styles.actionButtonDisabled]}
369
+ style={[
370
+ styles.actionButton,
371
+ { width: size, height: size, borderRadius: size / 2 },
372
+ disabled && styles.actionButtonDisabled,
373
+ ]}
357
374
  onPress={onPress}
358
375
  disabled={disabled || !onPress}
359
376
  accessibilityRole="button"
@@ -377,6 +394,7 @@ export function ComposePillInput({
377
394
  leading,
378
395
  trailing,
379
396
  endAction,
397
+ endActionPlacement = 'outside',
380
398
  replyPreview,
381
399
  style,
382
400
  transparentWrap = false,
@@ -387,6 +405,7 @@ export function ComposePillInput({
387
405
  const layout = useComposePillLineLayout(value, { maxLines });
388
406
  const { isMultiline, pillHeight, resetLineCount } = layout;
389
407
  const variantColors = composePillVariantColors(variant);
408
+ const endActionInside = endActionPlacement === 'inside' && !bare;
390
409
 
391
410
  const handleChange = useCallback(
392
411
  (text: string) => {
@@ -415,6 +434,14 @@ export function ComposePillInput({
415
434
  />
416
435
  );
417
436
 
437
+ const trailingSlot =
438
+ trailing || (endActionInside && endAction) ? (
439
+ <View style={[styles.trailingIcons, isMultiline && styles.trailingIconsExpanded]}>
440
+ {trailing}
441
+ {endActionInside ? endAction : null}
442
+ </View>
443
+ ) : null;
444
+
418
445
  return (
419
446
  <View
420
447
  style={[
@@ -446,20 +473,17 @@ export function ComposePillInput({
446
473
  borderWidth: variant === 'overlay' || variant === 'chrome' ? 1 : 0,
447
474
  },
448
475
  variantColors.useChromeGlass && styles.inputPillGlass,
476
+ endActionInside && styles.inputPillWithInsetAction,
449
477
  ]}
450
478
  >
451
479
  {variantColors.useChromeGlass ? <ChromeGlassLayer /> : null}
452
480
  {leading}
453
481
  {textField}
454
- {trailing ? (
455
- <View style={[styles.trailingIcons, isMultiline && styles.trailingIconsExpanded]}>
456
- {trailing}
457
- </View>
458
- ) : null}
482
+ {trailingSlot}
459
483
  </View>
460
484
  )}
461
485
 
462
- {endAction}
486
+ {!endActionInside ? endAction : null}
463
487
  </View>
464
488
  </View>
465
489
  );
@@ -532,6 +556,11 @@ const styles = StyleSheet.create({
532
556
  paddingRight: spacing.xs,
533
557
  overflow: 'hidden',
534
558
  },
559
+ inputPillWithInsetAction: {
560
+ paddingLeft: spacing.xs,
561
+ paddingRight: spacing.xs,
562
+ gap: spacing.xs,
563
+ },
535
564
  inputPillGlass: {},
536
565
  inputPillSingle: {
537
566
  borderRadius: m3Shape.cornerFull,
@@ -573,12 +602,10 @@ const styles = StyleSheet.create({
573
602
  paddingBottom: spacing.xs,
574
603
  },
575
604
  actionButton: {
576
- width: ACTION_BUTTON_SIZE,
577
- height: ACTION_BUTTON_SIZE,
578
- borderRadius: ACTION_BUTTON_SIZE / 2,
579
605
  backgroundColor: m3Colors.primary,
580
606
  alignItems: 'center',
581
607
  justifyContent: 'center',
608
+ zIndex: 1,
582
609
  },
583
610
  actionButtonDisabled: {
584
611
  opacity: 0.45,
@@ -586,3 +613,4 @@ const styles = StyleSheet.create({
586
613
  });
587
614
 
588
615
  export const COMPOSE_PILL_ACTION_BUTTON_SIZE = ACTION_BUTTON_SIZE;
616
+ export const COMPOSE_PILL_ACTION_BUTTON_SIZE_INSET = ACTION_BUTTON_SIZE_INSET;
@@ -116,9 +116,11 @@ export {
116
116
  ComposePillReplyBar,
117
117
  ComposePillTextField,
118
118
  COMPOSE_PILL_ACTION_BUTTON_SIZE,
119
+ COMPOSE_PILL_ACTION_BUTTON_SIZE_INSET,
119
120
  countComposePillLines,
120
121
  pillHeightForLineCount,
121
122
  useComposePillLineLayout,
123
+ type ComposePillEndActionPlacement,
122
124
  type ComposePillInputProps,
123
125
  type ComposePillLineLayout,
124
126
  type ComposePillLineLayoutState,
@@ -12,23 +12,29 @@ export function StorySurfacePicker({
12
12
  visible,
13
13
  value,
14
14
  options,
15
+ title,
16
+ subtitle,
17
+ closeLabel,
15
18
  onClose,
16
19
  onSelect,
17
20
  }: {
18
21
  visible: boolean;
19
22
  value: string;
20
23
  options: StorySurfaceOption[];
24
+ title: string;
25
+ subtitle: string;
26
+ closeLabel: string;
21
27
  onClose: () => void;
22
28
  onSelect: (value: string) => void;
23
29
  }) {
24
30
  return (
25
31
  <Modal visible={visible} transparent animationType="slide" onRequestClose={onClose}>
26
32
  <View style={styles.backdrop}>
27
- <Pressable style={styles.backdropPress} onPress={onClose} accessibilityLabel="Fechar" />
33
+ <Pressable style={styles.backdropPress} onPress={onClose} accessibilityLabel={closeLabel} />
28
34
  <View style={styles.sheet}>
29
35
  <View style={styles.handle} />
30
- <Text style={styles.title}>Onde publicar</Text>
31
- <Text style={styles.subtitle}>Escolha em qual superfície o story vai aparecer.</Text>
36
+ <Text style={styles.title}>{title}</Text>
37
+ <Text style={styles.subtitle}>{subtitle}</Text>
32
38
 
33
39
  {options.map((option) => {
34
40
  const selected = option.value === value;