@getuserfeedback/react-native 3.0.49 → 3.0.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/dist/index.js CHANGED
@@ -650,8 +650,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
650
650
  (previousSheetHostLayoutUpdate === null || previousSheetHostLayoutUpdate === void 0 ? void 0 : previousSheetHostLayoutUpdate.transition.kind) === "timed" &&
651
651
  previousSheetHostLayoutUpdate.size.height ===
652
652
  sheetHostLayoutPlan.size.height
653
- ? previousSheetHostLayoutUpdate
654
- : sheetHostLayoutPlan.update;
653
+ ? Object.assign(Object.assign({}, previousSheetHostLayoutUpdate), { size: sheetHostLayoutPlan.size }) : sheetHostLayoutPlan.update;
655
654
  break;
656
655
  }
657
656
  case "timed":
@@ -699,6 +698,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
699
698
  nativeView: NativeView,
700
699
  onRequestDismiss: handleWidgetHostDismiss,
701
700
  sheetHostLayoutUpdate,
701
+ transitionSequence: transitionPolicySequence,
702
702
  }, widgetHost));
703
703
  }
704
704
  export function useGetUserFeedbackNative() {
@@ -1,3 +1,4 @@
1
+ import type { CoreCommandEnvelope } from "@getuserfeedback/protocol";
1
2
  import { type ReactElement } from "react";
2
3
  import { type CoreWebViewHostProps } from "./core-webview-host.js";
3
4
  import { type NativeCoreCommandChannel } from "./native-core-command-channel.js";
@@ -6,10 +7,17 @@ export type NativeRuntimeRootProps = {
6
7
  coreUrl: string;
7
8
  coreWebViewComponent?: CoreWebViewHostProps["webViewComponent"];
8
9
  coreWebViewProps?: CoreWebViewHostProps["webViewProps"];
10
+ instanceRegistrationEnvelope?: NativeRuntimeInstanceRegistrationEnvelope;
9
11
  onCommandChannelChange?: (channel: NativeCoreCommandChannel | null) => void;
10
12
  onError?: (error: Error) => void;
11
13
  onInvalidMessage?: (error: Error, value: unknown) => void;
12
14
  viewWebViewComponent?: NativeViewHostProjectionProps["webViewComponent"];
13
15
  viewWebViewProps?: NativeViewHostProjectionProps["webViewProps"];
14
16
  };
15
- export declare function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandChannelChange, onError, onInvalidMessage, viewWebViewComponent, viewWebViewProps, }: NativeRuntimeRootProps): ReactElement;
17
+ type NativeRuntimeInstanceRegistrationEnvelope = Omit<CoreCommandEnvelope, "command"> & {
18
+ command: Extract<CoreCommandEnvelope["command"], {
19
+ kind: "init";
20
+ }>;
21
+ };
22
+ export declare function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, instanceRegistrationEnvelope, onCommandChannelChange, onError, onInvalidMessage, viewWebViewComponent, viewWebViewProps, }: NativeRuntimeRootProps): ReactElement;
23
+ export {};
@@ -3,43 +3,51 @@ import { CoreWebViewHost, } from "./core-webview-host.js";
3
3
  import { createNativeCoreCommandChannel, } from "./native-core-command-channel.js";
4
4
  import { NativeViewHostProjection, } from "./native-view-host-projection.js";
5
5
  import { createNativeViewRecordController, } from "./native-view-record-controller.js";
6
- // TODO(architecture): Route protocol-owned init/bootstrap commands through the private native runtime channel without moving provider ownership.
7
- // [from=private-native-command-bootstrap-composition] [to=protocol-owned-native-command-bootstrap-routing] [scope=slice] [principle=preserve-provider-runtime] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=70]
8
- export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandChannelChange, onError, onInvalidMessage, viewWebViewComponent, viewWebViewProps, }) {
6
+ // TODO(architecture): Compose provider-owned startup state with the private native runtime without changing the default loader-backed path.
7
+ // [from=protocol-owned-native-command-bootstrap-routing] [to=provider-owned-native-registration-composition] [scope=slice] [principle=preserve-provider-runtime] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=80]
8
+ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, instanceRegistrationEnvelope, onCommandChannelChange, onError, onInvalidMessage, viewWebViewComponent, viewWebViewProps, }) {
9
+ const [coreHostRevision, setCoreHostRevision] = useState(0);
9
10
  const [session, setSession] = useState(null);
10
11
  const sessionRef = useRef(null);
11
12
  const nextSessionKeyRef = useRef(0);
13
+ const registrationRetryAttemptedRef = useRef(false);
12
14
  const onErrorRef = useRef(onError);
13
15
  onErrorRef.current = onError;
14
16
  const onCommandChannelChangeRef = useRef(onCommandChannelChange);
15
17
  onCommandChannelChangeRef.current = onCommandChannelChange;
16
18
  const onInvalidMessageRef = useRef(onInvalidMessage);
17
19
  onInvalidMessageRef.current = onInvalidMessage;
18
- const notifyCommandChannelObserver = useCallback((observer, channel) => {
20
+ const instanceRegistrationEnvelopeRef = useRef(instanceRegistrationEnvelope);
21
+ instanceRegistrationEnvelopeRef.current = instanceRegistrationEnvelope;
22
+ const reportError = useCallback((error) => {
19
23
  var _a;
24
+ try {
25
+ (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, error instanceof Error ? error : new Error(String(error)));
26
+ }
27
+ catch (_b) {
28
+ // Error observers cannot change native runtime settlement.
29
+ }
30
+ }, []);
31
+ const notifyCommandChannelObserver = useCallback((observer, channel) => {
20
32
  try {
21
33
  observer === null || observer === void 0 ? void 0 : observer(channel);
22
34
  }
23
35
  catch (error) {
24
- try {
25
- (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, error instanceof Error ? error : new Error(String(error)));
26
- }
27
- catch (_b) {
28
- // Observer failures must not interrupt connection settlement.
29
- }
36
+ reportError(error);
30
37
  }
31
- }, []);
38
+ }, [reportError]);
32
39
  const handleConnected = useCallback((coreConnection) => {
33
40
  const previousSession = sessionRef.current;
34
41
  if (previousSession) {
35
42
  sessionRef.current = null;
36
43
  previousSession.commandChannelOwner.dispose();
37
44
  previousSession.controller.dispose();
45
+ setSession(null);
38
46
  notifyCommandChannelObserver(previousSession.observer, null);
39
47
  }
40
48
  const controller = createNativeViewRecordController({
41
49
  coreConnection,
42
- onError: (error) => { var _a; return (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, error); },
50
+ onError: reportError,
43
51
  });
44
52
  const commandChannelOwner = createNativeCoreCommandChannel({
45
53
  coreConnection,
@@ -51,11 +59,28 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
51
59
  key: nextSessionKeyRef.current + 1,
52
60
  observer: onCommandChannelChangeRef.current,
53
61
  };
62
+ const registrationEnvelope = instanceRegistrationEnvelopeRef.current;
63
+ if (registrationEnvelope) {
64
+ try {
65
+ commandChannelOwner.channel.post(registrationEnvelope);
66
+ }
67
+ catch (error) {
68
+ commandChannelOwner.dispose();
69
+ controller.dispose();
70
+ if (!registrationRetryAttemptedRef.current) {
71
+ registrationRetryAttemptedRef.current = true;
72
+ setCoreHostRevision((revision) => revision + 1);
73
+ }
74
+ reportError(error);
75
+ return;
76
+ }
77
+ }
78
+ registrationRetryAttemptedRef.current = false;
54
79
  nextSessionKeyRef.current = nextSession.key;
55
80
  sessionRef.current = nextSession;
56
81
  setSession(nextSession);
57
82
  notifyCommandChannelObserver(nextSession.observer, commandChannelOwner.channel);
58
- }, [notifyCommandChannelObserver]);
83
+ }, [notifyCommandChannelObserver, reportError]);
59
84
  const handleDisconnected = useCallback(() => {
60
85
  const currentSession = sessionRef.current;
61
86
  if (!currentSession) {
@@ -88,6 +113,7 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
88
113
  }, [notifyCommandChannelObserver]);
89
114
  return createElement(Fragment, null, createElement(CoreWebViewHost, {
90
115
  coreUrl,
116
+ key: `${coreUrl}:${coreHostRevision}`,
91
117
  onConnected: handleConnected,
92
118
  onDisconnected: handleDisconnected,
93
119
  onInvalidMessage,
@@ -0,0 +1,11 @@
1
+ import type { WidgetFrameResolvedBorderRadius } from "@getuserfeedback/protocol/host";
2
+ type ProviderWidgetFrameSize = {
3
+ height: number;
4
+ width: number;
5
+ };
6
+ export declare function safeParseProviderWidgetCanonicalTopRadii(value: string): {
7
+ topLeft: number;
8
+ topRight: number;
9
+ } | undefined;
10
+ export declare function resolveProviderWidgetCanonicalBorderRadius(value: string, frameSize: ProviderWidgetFrameSize): WidgetFrameResolvedBorderRadius | undefined;
11
+ export {};
@@ -0,0 +1,115 @@
1
+ const CANONICAL_PX_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d+)?px$/;
2
+ const CANONICAL_RADIUS_COMPONENT_PATTERN = /^((?:\d+(?:\.\d+)?|\.\d+))(px|%)$/;
3
+ const safeParseCanonicalPx = (value) => {
4
+ if (!CANONICAL_PX_PATTERN.test(value)) {
5
+ return undefined;
6
+ }
7
+ const parsed = Number.parseFloat(value);
8
+ return Number.isFinite(parsed) ? parsed : undefined;
9
+ };
10
+ export function safeParseProviderWidgetCanonicalTopRadii(value) {
11
+ if (value.includes("/")) {
12
+ return undefined;
13
+ }
14
+ const radii = value.split(/\s+/).map(safeParseCanonicalPx);
15
+ if (radii.length === 0 ||
16
+ radii.length > 4 ||
17
+ radii.some((radius) => radius === undefined)) {
18
+ return undefined;
19
+ }
20
+ const [topLeft, topRight = topLeft] = radii;
21
+ return topLeft === undefined || topRight === undefined
22
+ ? undefined
23
+ : { topLeft, topRight };
24
+ }
25
+ const parseCanonicalRadiusComponent = (value) => {
26
+ var _a;
27
+ const match = CANONICAL_RADIUS_COMPONENT_PATTERN.exec(value);
28
+ if (match === null) {
29
+ return undefined;
30
+ }
31
+ const parsed = Number.parseFloat((_a = match[1]) !== null && _a !== void 0 ? _a : "");
32
+ const unit = match[2];
33
+ return Number.isFinite(parsed) && (unit === "px" || unit === "%")
34
+ ? { unit, value: parsed }
35
+ : undefined;
36
+ };
37
+ const expandRadiusComponents = (values) => {
38
+ const [topLeft, topRight, bottomRight, bottomLeft] = values;
39
+ if (topLeft === undefined || values.length > 4) {
40
+ return undefined;
41
+ }
42
+ if (values.length === 1) {
43
+ return [topLeft, topLeft, topLeft, topLeft];
44
+ }
45
+ if (topRight === undefined) {
46
+ return undefined;
47
+ }
48
+ if (values.length === 2) {
49
+ return [topLeft, topRight, topLeft, topRight];
50
+ }
51
+ if (bottomRight === undefined) {
52
+ return undefined;
53
+ }
54
+ if (values.length === 3) {
55
+ return [topLeft, topRight, bottomRight, topRight];
56
+ }
57
+ return bottomLeft === undefined
58
+ ? undefined
59
+ : [topLeft, topRight, bottomRight, bottomLeft];
60
+ };
61
+ const parseCanonicalRadiusComponents = (value) => {
62
+ const components = value
63
+ .trim()
64
+ .split(/\s+/)
65
+ .map(parseCanonicalRadiusComponent);
66
+ return components.some((component) => component === undefined)
67
+ ? undefined
68
+ : expandRadiusComponents(components);
69
+ };
70
+ const resolveCanonicalRadiusComponent = (component, basis) => component.unit === "%" ? (component.value / 100) * basis : component.value;
71
+ const toRadiusOverlapRatio = (limit, first, second) => {
72
+ const max = Math.max(first, second);
73
+ return max === 0 ? 1 : limit / max / (first / max + second / max);
74
+ };
75
+ export function resolveProviderWidgetCanonicalBorderRadius(value, frameSize) {
76
+ var _a, _b, _c;
77
+ if (!Number.isFinite(frameSize.width) ||
78
+ frameSize.width < 0 ||
79
+ !Number.isFinite(frameSize.height) ||
80
+ frameSize.height < 0) {
81
+ return undefined;
82
+ }
83
+ const axes = value.split("/");
84
+ if (axes.length > 2) {
85
+ return undefined;
86
+ }
87
+ const horizontal = parseCanonicalRadiusComponents((_a = axes[0]) !== null && _a !== void 0 ? _a : "");
88
+ const vertical = parseCanonicalRadiusComponents((_c = (_b = axes[1]) !== null && _b !== void 0 ? _b : axes[0]) !== null && _c !== void 0 ? _c : "");
89
+ if (horizontal === undefined || vertical === undefined) {
90
+ return undefined;
91
+ }
92
+ const resolveCorner = (index) => ({
93
+ horizontalPx: resolveCanonicalRadiusComponent(horizontal[index], frameSize.width),
94
+ verticalPx: resolveCanonicalRadiusComponent(vertical[index], frameSize.height),
95
+ });
96
+ const topLeft = resolveCorner(0);
97
+ const topRight = resolveCorner(1);
98
+ const bottomRight = resolveCorner(2);
99
+ const bottomLeft = resolveCorner(3);
100
+ if ([topLeft, topRight, bottomRight, bottomLeft].some((corner) => !Number.isFinite(corner.horizontalPx) ||
101
+ !Number.isFinite(corner.verticalPx))) {
102
+ return undefined;
103
+ }
104
+ const overlapScale = Math.min(1, toRadiusOverlapRatio(frameSize.width, topLeft.horizontalPx, topRight.horizontalPx), toRadiusOverlapRatio(frameSize.width, bottomLeft.horizontalPx, bottomRight.horizontalPx), toRadiusOverlapRatio(frameSize.height, topLeft.verticalPx, bottomLeft.verticalPx), toRadiusOverlapRatio(frameSize.height, topRight.verticalPx, bottomRight.verticalPx));
105
+ const scaleCorner = (corner) => ({
106
+ horizontalPx: corner.horizontalPx * overlapScale,
107
+ verticalPx: corner.verticalPx * overlapScale,
108
+ });
109
+ return {
110
+ topLeft: scaleCorner(topLeft),
111
+ topRight: scaleCorner(topRight),
112
+ bottomRight: scaleCorner(bottomRight),
113
+ bottomLeft: scaleCorner(bottomLeft),
114
+ };
115
+ }
@@ -1,18 +1,46 @@
1
- import type { WidgetFrameAppearance, WidgetFrameResolvedBorder } from "@getuserfeedback/protocol/host";
1
+ import type { WidgetFrameAppearance, WidgetFrameResolvedBorder, WidgetFrameResolvedBoxShadowLayer } from "@getuserfeedback/protocol/host";
2
2
  export type ProviderWidgetFrameBorderStyle = {
3
3
  borderColor: string;
4
4
  borderStyle: "dashed" | "dotted" | "solid";
5
5
  borderWidth: number;
6
6
  };
7
+ type ProviderWidgetFrameBoxShadowValue = {
8
+ blurRadius: number;
9
+ color: string;
10
+ inset: boolean;
11
+ offsetX: number;
12
+ offsetY: number;
13
+ spreadDistance: number;
14
+ };
15
+ type ProviderWidgetFrameLegacyShadowStyle = {
16
+ elevation: number;
17
+ shadowColor: string;
18
+ shadowOffset: {
19
+ height: number;
20
+ width: number;
21
+ };
22
+ shadowOpacity: number;
23
+ shadowRadius: number;
24
+ };
25
+ export type ProviderWidgetFrameShadowProjection = {
26
+ kind: "clear";
27
+ } | {
28
+ kind: "legacy";
29
+ } | {
30
+ boxShadow: ProviderWidgetFrameBoxShadowValue[];
31
+ fallbackStyle: ProviderWidgetFrameLegacyShadowStyle;
32
+ kind: "resolved";
33
+ };
7
34
  export type ProviderWidgetFrameAppearanceProjection = {
8
35
  borderStyle?: ProviderWidgetFrameBorderStyle;
9
- shadowPolicy: "clear" | "legacy";
36
+ shadow: ProviderWidgetFrameShadowProjection;
10
37
  topLeftRadius: number;
11
38
  topRightRadius: number;
12
39
  };
13
- export declare function safeParseProviderWidgetCanonicalTopRadii(value: string): {
14
- topLeft: number;
15
- topRight: number;
16
- } | undefined;
40
+ export declare function toProviderWidgetFrameShadow(layers?: WidgetFrameResolvedBoxShadowLayer[]): ProviderWidgetFrameShadowProjection;
17
41
  export declare function toProviderWidgetFrameBorderStyle(border?: WidgetFrameResolvedBorder): ProviderWidgetFrameBorderStyle;
18
- export declare function toProviderWidgetFrameAppearance(appearance?: WidgetFrameAppearance): ProviderWidgetFrameAppearanceProjection;
42
+ export declare function toProviderWidgetFrameAppearance(appearance?: WidgetFrameAppearance, nativeSheetSize?: {
43
+ height: number;
44
+ width: number;
45
+ }): ProviderWidgetFrameAppearanceProjection;
46
+ export {};
@@ -1,32 +1,55 @@
1
+ import { resolveProviderWidgetCanonicalBorderRadius, safeParseProviderWidgetCanonicalTopRadii, } from "./provider-widget-border-radius.js";
1
2
  const LEGACY_WIDGET_SHEET_RADIUS = 24;
2
- const CANONICAL_PX_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d+)?px$/;
3
3
  const CLEARED_WIDGET_FRAME_BORDER_STYLE = {
4
4
  borderColor: "transparent",
5
5
  borderStyle: "solid",
6
6
  borderWidth: 0,
7
7
  };
8
- const safeParseCanonicalPx = (value) => {
9
- if (!CANONICAL_PX_PATTERN.test(value)) {
10
- return undefined;
11
- }
12
- const parsed = Number.parseFloat(value);
13
- return Number.isFinite(parsed) ? parsed : undefined;
8
+ const CLEARED_WIDGET_FRAME_SHADOW_STYLE = {
9
+ elevation: 0,
10
+ shadowColor: "transparent",
11
+ shadowOffset: { height: 0, width: 0 },
12
+ shadowOpacity: 0,
13
+ shadowRadius: 0,
14
14
  };
15
- export function safeParseProviderWidgetCanonicalTopRadii(value) {
16
- if (value.includes("/")) {
17
- return undefined;
15
+ const toProviderWidgetFrameColor = (color) => `rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha})`;
16
+ const toProviderWidgetFrameBoxShadow = (layers) => layers.map((layer) => ({
17
+ blurRadius: layer.blurRadiusPx,
18
+ color: toProviderWidgetFrameColor(layer.color),
19
+ inset: layer.inset,
20
+ offsetX: layer.offsetXPx,
21
+ offsetY: layer.offsetYPx,
22
+ spreadDistance: layer.spreadRadiusPx,
23
+ }));
24
+ const toProviderWidgetFrameLegacyShadow = (layers) => {
25
+ const layer = layers.find((candidate) => !candidate.inset && candidate.color.alpha > 0);
26
+ if (layer === undefined) {
27
+ return Object.assign({}, CLEARED_WIDGET_FRAME_SHADOW_STYLE);
18
28
  }
19
- const radii = value.split(/\s+/).map(safeParseCanonicalPx);
20
- if (radii.length === 0 ||
21
- radii.length > 4 ||
22
- radii.some((radius) => radius === undefined)) {
23
- return undefined;
29
+ return {
30
+ elevation: Math.max(1, Math.ceil(layer.blurRadiusPx + Math.max(0, layer.spreadRadiusPx))),
31
+ shadowColor: `rgb(${layer.color.red}, ${layer.color.green}, ${layer.color.blue})`,
32
+ shadowOffset: { height: layer.offsetYPx, width: layer.offsetXPx },
33
+ shadowOpacity: layer.color.alpha,
34
+ shadowRadius: layer.blurRadiusPx,
35
+ };
36
+ };
37
+ export function toProviderWidgetFrameShadow(layers) {
38
+ if (layers === undefined) {
39
+ return { kind: "clear" };
24
40
  }
25
- const [topLeft, topRight = topLeft] = radii;
26
- return topLeft === undefined || topRight === undefined
27
- ? undefined
28
- : { topLeft, topRight };
41
+ return {
42
+ boxShadow: toProviderWidgetFrameBoxShadow(layers),
43
+ fallbackStyle: toProviderWidgetFrameLegacyShadow(layers),
44
+ kind: "resolved",
45
+ };
29
46
  }
47
+ const toProviderWidgetConservativeResolvedTopRadii = (radius) => ({
48
+ // React Native exposes one scalar per corner, so use a deliberately smaller
49
+ // circle that does not exceed the authored ellipse on either axis.
50
+ topLeft: Math.min(radius.topLeft.horizontalPx, radius.topLeft.verticalPx),
51
+ topRight: Math.min(radius.topRight.horizontalPx, radius.topRight.verticalPx),
52
+ });
30
53
  export function toProviderWidgetFrameBorderStyle(border) {
31
54
  if (border === undefined ||
32
55
  border.widthPx === 0 ||
@@ -43,11 +66,11 @@ export function toProviderWidgetFrameBorderStyle(border) {
43
66
  borderWidth: border.widthPx,
44
67
  };
45
68
  }
46
- export function toProviderWidgetFrameAppearance(appearance) {
69
+ export function toProviderWidgetFrameAppearance(appearance, nativeSheetSize) {
47
70
  var _a, _b;
48
71
  if (appearance === undefined) {
49
72
  return {
50
- shadowPolicy: "legacy",
73
+ shadow: { kind: "legacy" },
51
74
  topLeftRadius: LEGACY_WIDGET_SHEET_RADIUS,
52
75
  topRightRadius: LEGACY_WIDGET_SHEET_RADIUS,
53
76
  };
@@ -55,15 +78,24 @@ export function toProviderWidgetFrameAppearance(appearance) {
55
78
  if (appearance.kind === "none") {
56
79
  return {
57
80
  borderStyle: toProviderWidgetFrameBorderStyle(),
58
- shadowPolicy: "clear",
81
+ shadow: { kind: "clear" },
59
82
  topLeftRadius: 0,
60
83
  topRightRadius: 0,
61
84
  };
62
85
  }
63
- const radius = safeParseProviderWidgetCanonicalTopRadii(appearance.borderRadius);
86
+ // Percentage radii depend on the host-owned sheet box, which can differ from
87
+ // the view's reported plan. Browser-only expressions keep the structured fallback.
88
+ const nativeResolvedRadius = nativeSheetSize === undefined || !appearance.borderRadius.includes("%")
89
+ ? undefined
90
+ : resolveProviderWidgetCanonicalBorderRadius(appearance.borderRadius, nativeSheetSize);
91
+ const radius = nativeResolvedRadius !== undefined
92
+ ? toProviderWidgetConservativeResolvedTopRadii(nativeResolvedRadius)
93
+ : appearance.resolvedBorderRadius === undefined
94
+ ? safeParseProviderWidgetCanonicalTopRadii(appearance.borderRadius)
95
+ : toProviderWidgetConservativeResolvedTopRadii(appearance.resolvedBorderRadius);
64
96
  return {
65
97
  borderStyle: toProviderWidgetFrameBorderStyle(appearance.resolvedBorder),
66
- shadowPolicy: "clear",
98
+ shadow: toProviderWidgetFrameShadow(appearance.resolvedBoxShadow),
67
99
  topLeftRadius: (_a = radius === null || radius === void 0 ? void 0 : radius.topLeft) !== null && _a !== void 0 ? _a : 0,
68
100
  topRightRadius: (_b = radius === null || radius === void 0 ? void 0 : radius.topRight) !== null && _b !== void 0 ? _b : 0,
69
101
  };
@@ -2,6 +2,19 @@ import { type WidgetFrameAppearance, type WidgetLayoutComparableSizeUpdate, type
2
2
  import { type ComponentType, type ReactElement, type ReactNode } from "react";
3
3
  import type { WidgetHostProps } from "./widget-host.js";
4
4
  type NativeViewComponent = ComponentType<Record<string, unknown>>;
5
+ type NativeReactVersion = {
6
+ major?: unknown;
7
+ minor?: unknown;
8
+ };
9
+ export type ProviderWidgetNativeBoxShadowSupport = "all" | "none" | "outset";
10
+ export declare function toProviderWidgetNativeBoxShadowSupport(input: {
11
+ hasFabricUIManager: boolean;
12
+ platform: {
13
+ os?: unknown;
14
+ version?: unknown;
15
+ };
16
+ reactNativeVersion?: NativeReactVersion;
17
+ }): ProviderWidgetNativeBoxShadowSupport;
5
18
  export declare function toProviderWidgetSheetHostViewport(viewport: WidgetHostProps["hostViewport"], flowSize?: {
6
19
  height?: number;
7
20
  }): WidgetHostProps["hostViewport"];
@@ -28,17 +41,19 @@ export declare function toProviderWidgetSheetSurfaceStyles(input: {
28
41
  frameAppearance?: WidgetFrameAppearance;
29
42
  isVisible: boolean;
30
43
  sheetHostLayoutUpdate: WidgetLayoutComparableSizeUpdate | undefined;
44
+ nativeBoxShadowSupport?: ProviderWidgetNativeBoxShadowSupport;
31
45
  }): {
32
- contentStyle: unknown;
33
- frameStyle: unknown;
34
- sheetStyle: unknown;
46
+ contentStyle: Record<string, unknown>;
47
+ frameStyle: Record<string, unknown>;
48
+ sheetStyle: Record<string, unknown>;
35
49
  };
36
- export declare function ProviderWidgetHostOverlay({ children, frameAppearance, isVisible, nativeView, onRequestDismiss, sheetHostLayoutUpdate, }: {
50
+ export declare function ProviderWidgetHostOverlay({ children, frameAppearance, isVisible, nativeView, onRequestDismiss, sheetHostLayoutUpdate, transitionSequence, }: {
37
51
  children?: ReactNode;
38
52
  frameAppearance?: WidgetFrameAppearance;
39
53
  isVisible: boolean;
40
54
  nativeView: NativeViewComponent;
41
55
  onRequestDismiss: () => void;
42
56
  sheetHostLayoutUpdate: WidgetLayoutComparableSizeUpdate | undefined;
57
+ transitionSequence?: number;
43
58
  }): ReactElement;
44
59
  export {};
@@ -134,6 +134,60 @@ function resolveNativeAnimationRuntime() {
134
134
  return null;
135
135
  }
136
136
  }
137
+ export function toProviderWidgetNativeBoxShadowSupport(input) {
138
+ var _a, _b;
139
+ if (!input.hasFabricUIManager) {
140
+ return "none";
141
+ }
142
+ const major = (_a = input.reactNativeVersion) === null || _a === void 0 ? void 0 : _a.major;
143
+ const minor = (_b = input.reactNativeVersion) === null || _b === void 0 ? void 0 : _b.minor;
144
+ const supportsBoxShadow = typeof major === "number" &&
145
+ Number.isInteger(major) &&
146
+ (major > 0 ||
147
+ (major === 0 &&
148
+ typeof minor === "number" &&
149
+ Number.isInteger(minor) &&
150
+ minor >= 76));
151
+ if (!supportsBoxShadow) {
152
+ return "none";
153
+ }
154
+ if (input.platform.os === "ios") {
155
+ return "all";
156
+ }
157
+ if (input.platform.os !== "android") {
158
+ return "none";
159
+ }
160
+ const androidApiLevel = input.platform.version;
161
+ if (typeof androidApiLevel !== "number" ||
162
+ !Number.isInteger(androidApiLevel)) {
163
+ return "none";
164
+ }
165
+ return androidApiLevel >= 29
166
+ ? "all"
167
+ : androidApiLevel >= 28
168
+ ? "outset"
169
+ : "none";
170
+ }
171
+ function resolveNativeBoxShadowSupport() {
172
+ var _a, _b, _c, _d;
173
+ try {
174
+ const reactNativeModule = require("react-native");
175
+ // React Native exposes renderer capability to JavaScript process-wide.
176
+ // There is no stable public API for querying the renderer of this View.
177
+ const fabricUIManager = Reflect.get(globalThis, "nativeFabricUIManager");
178
+ return toProviderWidgetNativeBoxShadowSupport({
179
+ hasFabricUIManager: fabricUIManager !== undefined && fabricUIManager !== null,
180
+ platform: {
181
+ os: (_a = reactNativeModule.Platform) === null || _a === void 0 ? void 0 : _a.OS,
182
+ version: (_b = reactNativeModule.Platform) === null || _b === void 0 ? void 0 : _b.Version,
183
+ },
184
+ reactNativeVersion: (_d = (_c = reactNativeModule.Platform) === null || _c === void 0 ? void 0 : _c.constants) === null || _d === void 0 ? void 0 : _d.reactNativeVersion,
185
+ });
186
+ }
187
+ catch (_e) {
188
+ return "none";
189
+ }
190
+ }
137
191
  function toResponderPageY(event) {
138
192
  var _a, _b, _c;
139
193
  const nativeEvent = event === null || event === void 0 ? void 0 : event.nativeEvent;
@@ -212,100 +266,184 @@ export function toProviderWidgetHostStyle({ isVisible, webViewStyle, }) {
212
266
  export function toProviderWidgetSheetSurfaceStyles(input) {
213
267
  var _a;
214
268
  const sheetHostViewport = (_a = input.sheetHostLayoutUpdate) === null || _a === void 0 ? void 0 : _a.size;
215
- const appearance = toProviderWidgetFrameAppearance(input.frameAppearance);
269
+ const appearance = toProviderWidgetFrameAppearance(input.frameAppearance, sheetHostViewport);
216
270
  const radiusStyle = {
217
271
  borderTopLeftRadius: appearance.topLeftRadius,
218
272
  borderTopRightRadius: appearance.topRightRadius,
219
273
  };
220
- const shadowStyle = appearance.shadowPolicy === "legacy"
274
+ const nativeBoxShadow = appearance.shadow.kind !== "resolved" ||
275
+ input.nativeBoxShadowSupport === undefined ||
276
+ input.nativeBoxShadowSupport === "none"
277
+ ? undefined
278
+ : input.nativeBoxShadowSupport === "all"
279
+ ? appearance.shadow.boxShadow
280
+ : appearance.shadow.boxShadow.filter((shadow) => !shadow.inset);
281
+ const usesNativeBoxShadow = nativeBoxShadow !== undefined;
282
+ const sheetShadowStyle = appearance.shadow.kind === "legacy"
221
283
  ? WIDGET_SHEET_LEGACY_FRAME_STYLE
222
- : WIDGET_SHEET_CLEARED_FRAME_STYLE;
284
+ : appearance.shadow.kind === "resolved" && !usesNativeBoxShadow
285
+ ? appearance.shadow.fallbackStyle
286
+ : WIDGET_SHEET_CLEARED_FRAME_STYLE;
287
+ const frameShadowStyle = nativeBoxShadow === undefined ? {} : { boxShadow: nativeBoxShadow };
223
288
  return {
224
289
  contentStyle: Object.assign(Object.assign({}, WIDGET_SHEET_CONTENT_BASE_STYLE), radiusStyle),
225
290
  frameStyle: input.isVisible && sheetHostViewport !== undefined
226
- ? Object.assign(Object.assign(Object.assign({}, WIDGET_SHEET_FRAME_OVERLAY_STYLE), radiusStyle), appearance.borderStyle) : HIDDEN_WIDGET_HOST_STYLE,
291
+ ? Object.assign(Object.assign(Object.assign(Object.assign({}, WIDGET_SHEET_FRAME_OVERLAY_STYLE), radiusStyle), appearance.borderStyle), frameShadowStyle) : HIDDEN_WIDGET_HOST_STYLE,
227
292
  sheetStyle: input.isVisible && sheetHostViewport !== undefined
228
- ? Object.assign(Object.assign(Object.assign(Object.assign({}, WIDGET_SHEET_LAYOUT_STYLE), radiusStyle), shadowStyle), { height: sheetHostViewport.height }) : HIDDEN_WIDGET_HOST_STYLE,
293
+ ? Object.assign(Object.assign(Object.assign(Object.assign({}, WIDGET_SHEET_LAYOUT_STYLE), radiusStyle), sheetShadowStyle), { height: sheetHostViewport.height }) : HIDDEN_WIDGET_HOST_STYLE,
229
294
  };
230
295
  }
231
296
  function useProviderWidgetSheetPresentation(input) {
232
- var _a, _b, _c, _d;
297
+ var _a, _b, _c;
233
298
  const animationRuntime = useMemo(resolveNativeAnimationRuntime, []);
234
- const animatedHeightRef = useRef(null);
235
- const animationRef = useRef(null);
236
- const previousHeightRef = useRef(undefined);
237
- const animatedSheetView = useMemo(() => animationRuntime
299
+ const animatedValuesRef = useRef(null);
300
+ const animationRef = useRef([]);
301
+ const consumedTransitionSequenceRef = useRef(undefined);
302
+ const previousFrameRef = useRef(undefined);
303
+ const animatedSurfaceView = useMemo(() => animationRuntime
238
304
  ? animationRuntime.Animated.createAnimatedComponent(input.nativeView)
239
305
  : input.nativeView, [animationRuntime, input.nativeView]);
240
306
  const sheetHostViewport = (_a = input.sheetHostLayoutUpdate) === null || _a === void 0 ? void 0 : _a.size;
241
307
  const height = sheetHostViewport === null || sheetHostViewport === void 0 ? void 0 : sheetHostViewport.height;
308
+ const topLeftRadius = input.surfaceStyles.sheetStyle.borderTopLeftRadius;
309
+ const topRightRadius = input.surfaceStyles.sheetStyle.borderTopRightRadius;
310
+ const frame = useMemo(() => height !== undefined &&
311
+ typeof topLeftRadius === "number" &&
312
+ typeof topRightRadius === "number"
313
+ ? { height, topLeftRadius, topRightRadius }
314
+ : undefined, [height, topLeftRadius, topRightRadius]);
242
315
  const shouldUseAnimatedStyle = animationRuntime !== null &&
243
316
  input.isVisible &&
244
- height !== undefined &&
317
+ frame !== undefined &&
318
+ previousFrameRef.current !== undefined &&
245
319
  ((_b = input.sheetHostLayoutUpdate) === null || _b === void 0 ? void 0 : _b.transition.kind) === "timed";
246
- const sheetView = animationRuntime ? animatedSheetView : input.nativeView;
247
- if (animationRuntime &&
248
- height !== undefined &&
249
- animatedHeightRef.current === null) {
250
- animatedHeightRef.current = new animationRuntime.Animated.Value(height);
320
+ const surfaceView = animationRuntime ? animatedSurfaceView : input.nativeView;
321
+ if (animationRuntime && frame && animatedValuesRef.current === null) {
322
+ animatedValuesRef.current = {
323
+ height: new animationRuntime.Animated.Value(frame.height),
324
+ topLeftRadius: new animationRuntime.Animated.Value(frame.topLeftRadius),
325
+ topRightRadius: new animationRuntime.Animated.Value(frame.topRightRadius),
326
+ };
251
327
  }
252
328
  useEffect(() => {
253
- var _a, _b, _c, _d, _e, _f;
254
- if (!animationRuntime || height === undefined) {
255
- (_b = (_a = animationRef.current) === null || _a === void 0 ? void 0 : _a.stop) === null || _b === void 0 ? void 0 : _b.call(_a);
256
- animationRef.current = null;
257
- previousHeightRef.current = height;
329
+ var _a, _b, _c;
330
+ const stopAnimations = (property) => {
331
+ var _a, _b;
332
+ const retained = [];
333
+ for (const animation of animationRef.current) {
334
+ if (property === undefined || animation.property === property) {
335
+ (_b = (_a = animation.handle).stop) === null || _b === void 0 ? void 0 : _b.call(_a);
336
+ }
337
+ else {
338
+ retained.push(animation);
339
+ }
340
+ }
341
+ animationRef.current = retained;
342
+ };
343
+ if (!animationRuntime || !frame || !input.isVisible) {
344
+ stopAnimations();
345
+ previousFrameRef.current = undefined;
258
346
  return;
259
347
  }
260
- const animatedHeight = animatedHeightRef.current;
261
- if (!animatedHeight) {
348
+ const animatedValues = animatedValuesRef.current;
349
+ if (!animatedValues) {
262
350
  return;
263
351
  }
264
- const previousHeight = previousHeightRef.current;
265
- previousHeightRef.current = height;
266
- const transition = (_c = input.sheetHostLayoutUpdate) === null || _c === void 0 ? void 0 : _c.transition;
267
- const activeAnimation = animationRef.current;
268
- (_d = activeAnimation === null || activeAnimation === void 0 ? void 0 : activeAnimation.stop) === null || _d === void 0 ? void 0 : _d.call(activeAnimation);
269
- animationRef.current = null;
270
- if (previousHeight === undefined || (transition === null || transition === void 0 ? void 0 : transition.kind) !== "timed") {
271
- animatedHeight.setValue(height);
352
+ const previousFrame = previousFrameRef.current;
353
+ previousFrameRef.current = frame;
354
+ const transition = (_a = input.sheetHostLayoutUpdate) === null || _a === void 0 ? void 0 : _a.transition;
355
+ const hasNewTransitionPolicy = input.transitionSequence !== undefined &&
356
+ consumedTransitionSequenceRef.current !== input.transitionSequence;
357
+ const shouldAnimate = (transition === null || transition === void 0 ? void 0 : transition.kind) === "timed" && hasNewTransitionPolicy;
358
+ if (hasNewTransitionPolicy) {
359
+ consumedTransitionSequenceRef.current = input.transitionSequence;
360
+ }
361
+ if (previousFrame === undefined || (transition === null || transition === void 0 ? void 0 : transition.kind) === "instant") {
362
+ stopAnimations();
363
+ animatedValues.height.setValue(frame.height);
364
+ animatedValues.topLeftRadius.setValue(frame.topLeftRadius);
365
+ animatedValues.topRightRadius.setValue(frame.topRightRadius);
272
366
  return;
273
367
  }
274
- if (previousHeight === height && !activeAnimation) {
275
- animatedHeight.setValue(height);
368
+ const values = {
369
+ height: animatedValues.height,
370
+ topLeftRadius: animatedValues.topLeftRadius,
371
+ topRightRadius: animatedValues.topRightRadius,
372
+ };
373
+ if (!shouldAnimate) {
374
+ for (const property of [
375
+ "height",
376
+ "topLeftRadius",
377
+ "topRightRadius",
378
+ ]) {
379
+ if (previousFrame[property] !== frame[property]) {
380
+ stopAnimations(property);
381
+ values[property].setValue(frame[property]);
382
+ }
383
+ }
276
384
  return;
277
385
  }
278
386
  const easing = transition.easing.kind === "cubic-bezier"
279
- ? (_f = (_e = animationRuntime.Easing) === null || _e === void 0 ? void 0 : _e.bezier) === null || _f === void 0 ? void 0 : _f.call(_e, ...transition.easing.points)
387
+ ? (_c = (_b = animationRuntime.Easing) === null || _b === void 0 ? void 0 : _b.bezier) === null || _c === void 0 ? void 0 : _c.call(_b, ...transition.easing.points)
280
388
  : undefined;
281
- const animation = animationRuntime.Animated.timing(animatedHeight, {
282
- toValue: height,
283
- duration: transition.durationMs,
284
- easing,
285
- useNativeDriver: false,
389
+ const animate = (property, value, toValue) => ({
390
+ handle: animationRuntime.Animated.timing(value, {
391
+ toValue,
392
+ duration: transition.durationMs,
393
+ easing,
394
+ useNativeDriver: false,
395
+ }),
396
+ property,
286
397
  });
287
- animationRef.current = animation;
288
- animation.start();
289
- return () => {
290
- var _a;
291
- (_a = animation.stop) === null || _a === void 0 ? void 0 : _a.call(animation);
292
- if (animationRef.current === animation) {
293
- animationRef.current = null;
398
+ const activeProperties = new Set(animationRef.current.map((animation) => animation.property));
399
+ const animations = [];
400
+ for (const property of [
401
+ "height",
402
+ "topLeftRadius",
403
+ "topRightRadius",
404
+ ]) {
405
+ if (previousFrame[property] !== frame[property] ||
406
+ activeProperties.has(property)) {
407
+ stopAnimations(property);
408
+ animations.push(animate(property, values[property], frame[property]));
294
409
  }
295
- };
296
- }, [animationRuntime, height, (_c = input.sheetHostLayoutUpdate) === null || _c === void 0 ? void 0 : _c.transition]);
297
- if (!shouldUseAnimatedStyle) {
298
- return {
299
- sheetView,
300
- style: input.staticStyle,
301
- };
410
+ }
411
+ animationRef.current.push(...animations);
412
+ for (const animation of animations) {
413
+ animation.handle.start(() => {
414
+ animationRef.current = animationRef.current.filter((candidate) => candidate !== animation);
415
+ });
416
+ }
417
+ }, [
418
+ animationRuntime,
419
+ frame,
420
+ input.isVisible,
421
+ (_c = input.sheetHostLayoutUpdate) === null || _c === void 0 ? void 0 : _c.transition,
422
+ input.transitionSequence,
423
+ ]);
424
+ useEffect(() => () => {
425
+ var _a, _b;
426
+ for (const animation of animationRef.current) {
427
+ (_b = (_a = animation.handle).stop) === null || _b === void 0 ? void 0 : _b.call(_a);
428
+ }
429
+ animationRef.current = [];
430
+ }, []);
431
+ const animatedValues = animatedValuesRef.current;
432
+ if (!shouldUseAnimatedStyle || animatedValues === null) {
433
+ return Object.assign(Object.assign({}, input.surfaceStyles), { surfaceView });
302
434
  }
435
+ const animatedRadiusStyle = {
436
+ borderTopLeftRadius: animatedValues.topLeftRadius,
437
+ borderTopRightRadius: animatedValues.topRightRadius,
438
+ };
303
439
  return {
304
- sheetView,
305
- style: Object.assign(Object.assign({}, input.staticStyle), { height: (_d = animatedHeightRef.current) !== null && _d !== void 0 ? _d : height }),
440
+ contentStyle: Object.assign(Object.assign({}, input.surfaceStyles.contentStyle), animatedRadiusStyle),
441
+ frameStyle: Object.assign(Object.assign({}, input.surfaceStyles.frameStyle), animatedRadiusStyle),
442
+ sheetStyle: Object.assign(Object.assign(Object.assign({}, input.surfaceStyles.sheetStyle), animatedRadiusStyle), { height: animatedValues.height }),
443
+ surfaceView,
306
444
  };
307
445
  }
308
- export function ProviderWidgetHostOverlay({ children, frameAppearance, isVisible, nativeView, onRequestDismiss, sheetHostLayoutUpdate, }) {
446
+ export function ProviderWidgetHostOverlay({ children, frameAppearance, isVisible, nativeView, onRequestDismiss, sheetHostLayoutUpdate, transitionSequence, }) {
309
447
  const dragStartYRef = useRef(undefined);
310
448
  const handleDragGrant = useCallback((event) => {
311
449
  dragStartYRef.current = toResponderPageY(event);
@@ -359,16 +497,19 @@ export function ProviderWidgetHostOverlay({ children, frameAppearance, isVisible
359
497
  const sheetHostViewport = sheetHostLayoutUpdate === null || sheetHostLayoutUpdate === void 0 ? void 0 : sheetHostLayoutUpdate.size;
360
498
  const isSheetVisible = isVisible && sheetHostViewport !== undefined;
361
499
  const pointerEvents = isSheetVisible ? "auto" : "none";
500
+ const nativeBoxShadowSupport = useMemo(resolveNativeBoxShadowSupport, []);
362
501
  const surfaceStyles = toProviderWidgetSheetSurfaceStyles({
363
502
  frameAppearance,
364
503
  isVisible,
365
504
  sheetHostLayoutUpdate,
505
+ nativeBoxShadowSupport,
366
506
  });
367
507
  const sheetPresentation = useProviderWidgetSheetPresentation({
368
508
  isVisible,
369
509
  nativeView,
370
510
  sheetHostLayoutUpdate,
371
- staticStyle: surfaceStyles.sheetStyle,
511
+ surfaceStyles,
512
+ transitionSequence,
372
513
  });
373
514
  return createElement(nativeView, {
374
515
  pointerEvents,
@@ -382,22 +523,22 @@ export function ProviderWidgetHostOverlay({ children, frameAppearance, isVisible
382
523
  ? WIDGET_SHEET_BACKDROP_STYLE
383
524
  : HIDDEN_WIDGET_HOST_STYLE,
384
525
  testID: "gx-widget-host-backdrop",
385
- }), createElement(sheetPresentation.sheetView, {
526
+ }), createElement(sheetPresentation.surfaceView, {
386
527
  pointerEvents,
387
- style: sheetPresentation.style,
528
+ style: sheetPresentation.sheetStyle,
388
529
  testID: "gx-widget-host-sheet",
389
- }, createElement(nativeView, {
530
+ }, createElement(sheetPresentation.surfaceView, {
390
531
  pointerEvents,
391
- style: surfaceStyles.contentStyle,
532
+ style: sheetPresentation.contentStyle,
392
533
  testID: "gx-widget-host-sheet-content",
393
534
  }, children, isSheetVisible
394
535
  ? createElement(nativeView, Object.assign(Object.assign({}, dragHandleGestureProps), { style: WIDGET_SHEET_DRAG_HANDLE_TOUCH_STYLE, testID: "gx-widget-host-drag-handle" }), createElement(nativeView, {
395
536
  style: WIDGET_SHEET_DRAG_HANDLE_INDICATOR_STYLE,
396
537
  testID: "gx-widget-host-drag-handle-indicator",
397
538
  }))
398
- : null), createElement(nativeView, {
539
+ : null), createElement(sheetPresentation.surfaceView, {
399
540
  pointerEvents: "none",
400
- style: surfaceStyles.frameStyle,
541
+ style: sheetPresentation.frameStyle,
401
542
  testID: "gx-widget-host-sheet-frame",
402
543
  })));
403
544
  }
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@ const reactNativeSdkVersion = typeof __GX_REACT_NATIVE_SDK_VERSION__ === "string
3
3
  : "";
4
4
  // Build scripts patch this fallback to the package version for published artifacts.
5
5
  // Source-linked workspace usage keeps the local fallback.
6
- export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.49";
6
+ export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.56";
@@ -23,6 +23,8 @@ const buildViewHostBootstrapScript = () => {
23
23
  const bootstrapProtocolVersion = serializeForJavaScript(VIEW_HOST_BOOTSTRAP_PROTOCOL_VERSION);
24
24
  const bootstrapCapabilities = serializeForJavaScript([
25
25
  VIEW_HOST_BOOTSTRAP_CAPABILITIES.RESOLVED_FRAME_BORDER,
26
+ VIEW_HOST_BOOTSTRAP_CAPABILITIES.RESOLVED_FRAME_BORDER_RADIUS,
27
+ VIEW_HOST_BOOTSTRAP_CAPABILITIES.RESOLVED_FRAME_BOX_SHADOW,
26
28
  ]);
27
29
  const bootstrapReadyEventName = serializeForJavaScript(VIEW_HOST_BOOTSTRAP_READY_EVENT_NAME);
28
30
  const inactiveConnectionMessage = serializeForJavaScript(VIEW_WEBVIEW_BRIDGE_CONFIG.inactiveConnectionMessage);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/react-native",
3
- "version": "3.0.49",
3
+ "version": "3.0.56",
4
4
  "description": "getuserfeedback React Native SDK",
5
5
  "keywords": [
6
6
  "getuserfeedback",
@@ -44,8 +44,8 @@
44
44
  "lint": "biome check ."
45
45
  },
46
46
  "dependencies": {
47
- "@getuserfeedback/protocol": "^3.13.0",
48
- "@getuserfeedback/sdk": "^0.10.14",
47
+ "@getuserfeedback/protocol": "^3.15.0",
48
+ "@getuserfeedback/sdk": "^0.10.18",
49
49
  "robot3": "^1.2.0"
50
50
  },
51
51
  "peerDependencies": {