@retor/react-native 0.3.1 → 0.3.2
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/README.md +4 -2
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +44 -43
- package/dist/index.mjs +46 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,14 +6,16 @@ Embed Retor 3D experiences in a React Native / Expo app — with composable bott
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Expo
|
|
9
|
-
npx expo install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg
|
|
9
|
+
npx expo install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg expo-blur
|
|
10
10
|
npm install @gorhom/bottom-sheet lucide-react-native @retor/react-native
|
|
11
11
|
|
|
12
12
|
# bare React Native
|
|
13
|
-
npm install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg @gorhom/bottom-sheet lucide-react-native @retor/react-native
|
|
13
|
+
npm install react-native-webview react-native-gesture-handler react-native-reanimated react-native-svg @gorhom/bottom-sheet lucide-react-native expo-blur @retor/react-native
|
|
14
14
|
cd ios && pod install
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
> `expo-blur` is optional — it's used for the default blurred sheet background. Skip it if you don't want blur and pass a custom `backgroundComponent` to any sheet.
|
|
18
|
+
|
|
17
19
|
You also need to wrap your app root in a `GestureHandlerRootView` (per `@gorhom/bottom-sheet` requirements):
|
|
18
20
|
|
|
19
21
|
```tsx
|
package/dist/index.d.mts
CHANGED
|
@@ -220,6 +220,12 @@ interface LineTagListProps {
|
|
|
220
220
|
/** Optional header rendered above the list (used internally for the default header). */
|
|
221
221
|
listHeader?: React.ReactNode;
|
|
222
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Renders the tags of the active line as a vertical list inside a
|
|
225
|
+
* `BottomSheetScrollView`. Tracks each item's Y position via `onLayout`
|
|
226
|
+
* and scrolls the closest tag to the top of the visible area as the
|
|
227
|
+
* camera scrolls.
|
|
228
|
+
*/
|
|
223
229
|
declare function LineTagList({ children, listHeader }: LineTagListProps): React.JSX.Element | null;
|
|
224
230
|
|
|
225
231
|
interface AddNoteSheetProps {
|
package/dist/index.d.ts
CHANGED
|
@@ -220,6 +220,12 @@ interface LineTagListProps {
|
|
|
220
220
|
/** Optional header rendered above the list (used internally for the default header). */
|
|
221
221
|
listHeader?: React.ReactNode;
|
|
222
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Renders the tags of the active line as a vertical list inside a
|
|
225
|
+
* `BottomSheetScrollView`. Tracks each item's Y position via `onLayout`
|
|
226
|
+
* and scrolls the closest tag to the top of the visible area as the
|
|
227
|
+
* camera scrolls.
|
|
228
|
+
*/
|
|
223
229
|
declare function LineTagList({ children, listHeader }: LineTagListProps): React.JSX.Element | null;
|
|
224
230
|
|
|
225
231
|
interface AddNoteSheetProps {
|
package/dist/index.js
CHANGED
|
@@ -217,14 +217,10 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
217
217
|
openLine: (lineId) => send("open-line", { lineId }),
|
|
218
218
|
exitLine: () => send("exit-line"),
|
|
219
219
|
scrollToTag: (tagId) => send("scroll-to-tag", { tagId }),
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
setAutoplay: (playing) => {
|
|
225
|
-
setIsPlaying(playing);
|
|
226
|
-
send("set-autoplay", { playing });
|
|
227
|
-
}
|
|
220
|
+
// Bridge owns autoplay state — it'll emit `autoplay-state` back, which
|
|
221
|
+
// we use to update isPlaying. No optimistic local update.
|
|
222
|
+
toggleAutoplay: () => send("toggle-autoplay"),
|
|
223
|
+
setAutoplay: (playing) => send("set-autoplay", { playing })
|
|
228
224
|
}),
|
|
229
225
|
[send]
|
|
230
226
|
);
|
|
@@ -299,6 +295,11 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
|
|
|
299
295
|
onLineProgress?.(payload);
|
|
300
296
|
break;
|
|
301
297
|
}
|
|
298
|
+
case "autoplay-state": {
|
|
299
|
+
const payload = data.payload;
|
|
300
|
+
setIsPlaying(!!payload.playing);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
302
303
|
default:
|
|
303
304
|
onMessage?.(data.type, data.payload);
|
|
304
305
|
}
|
|
@@ -379,11 +380,15 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
|
|
|
379
380
|
(0, import_react5.useEffect)(() => {
|
|
380
381
|
if (activeLineId || isAddNoteOpen) {
|
|
381
382
|
sheetRef.current?.dismiss();
|
|
382
|
-
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const t = setTimeout(() => {
|
|
383
386
|
sheetRef.current?.present();
|
|
387
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
384
388
|
setMinimized(false);
|
|
385
|
-
}
|
|
386
|
-
|
|
389
|
+
}, 80);
|
|
390
|
+
return () => clearTimeout(t);
|
|
391
|
+
}, [activeLineId, isAddNoteOpen, snapPointsArr.length]);
|
|
387
392
|
const handleSheetChange = (index) => {
|
|
388
393
|
setMinimized(index === 0);
|
|
389
394
|
};
|
|
@@ -498,13 +503,17 @@ function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }
|
|
|
498
503
|
const snapPointsArr = (0, import_react6.useMemo)(() => snapPoints, [snapPoints]);
|
|
499
504
|
const [minimized, setMinimized] = (0, import_react6.useState)(false);
|
|
500
505
|
(0, import_react6.useEffect)(() => {
|
|
501
|
-
if (activeLine
|
|
502
|
-
sheetRef.current?.present();
|
|
503
|
-
setMinimized(false);
|
|
504
|
-
} else {
|
|
506
|
+
if (!activeLine || isAddNoteOpen) {
|
|
505
507
|
sheetRef.current?.dismiss();
|
|
508
|
+
return;
|
|
506
509
|
}
|
|
507
|
-
|
|
510
|
+
const t = setTimeout(() => {
|
|
511
|
+
sheetRef.current?.present();
|
|
512
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
513
|
+
setMinimized(false);
|
|
514
|
+
}, 80);
|
|
515
|
+
return () => clearTimeout(t);
|
|
516
|
+
}, [activeLine, isAddNoteOpen, snapPointsArr.length]);
|
|
508
517
|
const handleSheetChange = (index) => {
|
|
509
518
|
setMinimized(index === 0);
|
|
510
519
|
};
|
|
@@ -574,45 +583,37 @@ function AutoplayButton() {
|
|
|
574
583
|
}
|
|
575
584
|
function LineTagList({ children, listHeader }) {
|
|
576
585
|
const { activeLine, closestTagId } = useRetorBridge();
|
|
577
|
-
const
|
|
586
|
+
const scrollRef = (0, import_react6.useRef)(null);
|
|
587
|
+
const offsetsRef = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
578
588
|
const tags = (0, import_react6.useMemo)(
|
|
579
589
|
() => (activeLine?.tags ?? []).filter((t) => t.name && t.name.trim().length > 0),
|
|
580
590
|
[activeLine]
|
|
581
591
|
);
|
|
592
|
+
(0, import_react6.useEffect)(() => {
|
|
593
|
+
offsetsRef.current = /* @__PURE__ */ new Map();
|
|
594
|
+
}, [activeLine?._id]);
|
|
582
595
|
(0, import_react6.useEffect)(() => {
|
|
583
596
|
if (!closestTagId) return;
|
|
584
|
-
const index = tags.findIndex((t2) => t2._id === closestTagId);
|
|
585
|
-
if (index < 0) return;
|
|
586
597
|
const t = setTimeout(() => {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
598
|
+
const y = offsetsRef.current.get(closestTagId);
|
|
599
|
+
if (y != null) {
|
|
600
|
+
scrollRef.current?.scrollTo({ y, animated: true });
|
|
590
601
|
}
|
|
591
|
-
},
|
|
602
|
+
}, 60);
|
|
592
603
|
return () => clearTimeout(t);
|
|
593
|
-
}, [closestTagId
|
|
604
|
+
}, [closestTagId]);
|
|
594
605
|
if (!activeLine) return null;
|
|
606
|
+
const handleItemLayout = (id, e) => {
|
|
607
|
+
offsetsRef.current.set(id, e.nativeEvent.layout.y);
|
|
608
|
+
};
|
|
595
609
|
return /* @__PURE__ */ import_react6.default.createElement(
|
|
596
|
-
import_bottom_sheet3.
|
|
610
|
+
import_bottom_sheet3.BottomSheetScrollView,
|
|
597
611
|
{
|
|
598
|
-
ref:
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
contentContainerStyle: styles3.list,
|
|
604
|
-
removeClippedSubviews: false,
|
|
605
|
-
onScrollToIndexFailed: (info) => {
|
|
606
|
-
const offset = info.averageItemLength * info.index;
|
|
607
|
-
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
608
|
-
setTimeout(() => {
|
|
609
|
-
try {
|
|
610
|
-
listRef.current?.scrollToIndex({ index: info.index, animated: true, viewPosition: 0 });
|
|
611
|
-
} catch {
|
|
612
|
-
}
|
|
613
|
-
}, 100);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
612
|
+
ref: scrollRef,
|
|
613
|
+
contentContainerStyle: styles3.list
|
|
614
|
+
},
|
|
615
|
+
listHeader,
|
|
616
|
+
tags.map((tag) => /* @__PURE__ */ import_react6.default.createElement(import_react_native5.View, { key: tag._id, onLayout: (e) => handleItemLayout(tag._id, e) }, children(tag, tag._id === closestTagId)))
|
|
616
617
|
);
|
|
617
618
|
}
|
|
618
619
|
function DefaultLineTagList({ listHeader }) {
|
package/dist/index.mjs
CHANGED
|
@@ -173,14 +173,10 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
173
173
|
openLine: (lineId) => send("open-line", { lineId }),
|
|
174
174
|
exitLine: () => send("exit-line"),
|
|
175
175
|
scrollToTag: (tagId) => send("scroll-to-tag", { tagId }),
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
setAutoplay: (playing) => {
|
|
181
|
-
setIsPlaying(playing);
|
|
182
|
-
send("set-autoplay", { playing });
|
|
183
|
-
}
|
|
176
|
+
// Bridge owns autoplay state — it'll emit `autoplay-state` back, which
|
|
177
|
+
// we use to update isPlaying. No optimistic local update.
|
|
178
|
+
toggleAutoplay: () => send("toggle-autoplay"),
|
|
179
|
+
setAutoplay: (playing) => send("set-autoplay", { playing })
|
|
184
180
|
}),
|
|
185
181
|
[send]
|
|
186
182
|
);
|
|
@@ -255,6 +251,11 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
|
|
|
255
251
|
onLineProgress?.(payload);
|
|
256
252
|
break;
|
|
257
253
|
}
|
|
254
|
+
case "autoplay-state": {
|
|
255
|
+
const payload = data.payload;
|
|
256
|
+
setIsPlaying(!!payload.playing);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
258
259
|
default:
|
|
259
260
|
onMessage?.(data.type, data.payload);
|
|
260
261
|
}
|
|
@@ -335,11 +336,15 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
|
|
|
335
336
|
useEffect2(() => {
|
|
336
337
|
if (activeLineId || isAddNoteOpen) {
|
|
337
338
|
sheetRef.current?.dismiss();
|
|
338
|
-
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const t = setTimeout(() => {
|
|
339
342
|
sheetRef.current?.present();
|
|
343
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
340
344
|
setMinimized(false);
|
|
341
|
-
}
|
|
342
|
-
|
|
345
|
+
}, 80);
|
|
346
|
+
return () => clearTimeout(t);
|
|
347
|
+
}, [activeLineId, isAddNoteOpen, snapPointsArr.length]);
|
|
343
348
|
const handleSheetChange = (index) => {
|
|
344
349
|
setMinimized(index === 0);
|
|
345
350
|
};
|
|
@@ -447,9 +452,9 @@ import React6, { useCallback as useCallback2, useEffect as useEffect3, useMemo a
|
|
|
447
452
|
import { Pressable as Pressable2, StyleSheet as StyleSheet5, Text as Text2, View as View5 } from "react-native";
|
|
448
453
|
import {
|
|
449
454
|
BottomSheetBackdrop as BottomSheetBackdrop2,
|
|
450
|
-
BottomSheetFlatList,
|
|
451
455
|
BottomSheetFooter,
|
|
452
|
-
BottomSheetModal as BottomSheetModal2
|
|
456
|
+
BottomSheetModal as BottomSheetModal2,
|
|
457
|
+
BottomSheetScrollView
|
|
453
458
|
} from "@gorhom/bottom-sheet";
|
|
454
459
|
import Svg, { Circle } from "react-native-svg";
|
|
455
460
|
import { ArrowDown as ArrowDown2, ArrowUp as ArrowUp2, Pause, Play, Plus } from "lucide-react-native";
|
|
@@ -459,13 +464,17 @@ function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }
|
|
|
459
464
|
const snapPointsArr = useMemo4(() => snapPoints, [snapPoints]);
|
|
460
465
|
const [minimized, setMinimized] = useState3(false);
|
|
461
466
|
useEffect3(() => {
|
|
462
|
-
if (activeLine
|
|
463
|
-
sheetRef.current?.present();
|
|
464
|
-
setMinimized(false);
|
|
465
|
-
} else {
|
|
467
|
+
if (!activeLine || isAddNoteOpen) {
|
|
466
468
|
sheetRef.current?.dismiss();
|
|
469
|
+
return;
|
|
467
470
|
}
|
|
468
|
-
|
|
471
|
+
const t = setTimeout(() => {
|
|
472
|
+
sheetRef.current?.present();
|
|
473
|
+
sheetRef.current?.snapToIndex(snapPointsArr.length - 1);
|
|
474
|
+
setMinimized(false);
|
|
475
|
+
}, 80);
|
|
476
|
+
return () => clearTimeout(t);
|
|
477
|
+
}, [activeLine, isAddNoteOpen, snapPointsArr.length]);
|
|
469
478
|
const handleSheetChange = (index) => {
|
|
470
479
|
setMinimized(index === 0);
|
|
471
480
|
};
|
|
@@ -535,45 +544,37 @@ function AutoplayButton() {
|
|
|
535
544
|
}
|
|
536
545
|
function LineTagList({ children, listHeader }) {
|
|
537
546
|
const { activeLine, closestTagId } = useRetorBridge();
|
|
538
|
-
const
|
|
547
|
+
const scrollRef = useRef3(null);
|
|
548
|
+
const offsetsRef = useRef3(/* @__PURE__ */ new Map());
|
|
539
549
|
const tags = useMemo4(
|
|
540
550
|
() => (activeLine?.tags ?? []).filter((t) => t.name && t.name.trim().length > 0),
|
|
541
551
|
[activeLine]
|
|
542
552
|
);
|
|
553
|
+
useEffect3(() => {
|
|
554
|
+
offsetsRef.current = /* @__PURE__ */ new Map();
|
|
555
|
+
}, [activeLine?._id]);
|
|
543
556
|
useEffect3(() => {
|
|
544
557
|
if (!closestTagId) return;
|
|
545
|
-
const index = tags.findIndex((t2) => t2._id === closestTagId);
|
|
546
|
-
if (index < 0) return;
|
|
547
558
|
const t = setTimeout(() => {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
559
|
+
const y = offsetsRef.current.get(closestTagId);
|
|
560
|
+
if (y != null) {
|
|
561
|
+
scrollRef.current?.scrollTo({ y, animated: true });
|
|
551
562
|
}
|
|
552
|
-
},
|
|
563
|
+
}, 60);
|
|
553
564
|
return () => clearTimeout(t);
|
|
554
|
-
}, [closestTagId
|
|
565
|
+
}, [closestTagId]);
|
|
555
566
|
if (!activeLine) return null;
|
|
567
|
+
const handleItemLayout = (id, e) => {
|
|
568
|
+
offsetsRef.current.set(id, e.nativeEvent.layout.y);
|
|
569
|
+
};
|
|
556
570
|
return /* @__PURE__ */ React6.createElement(
|
|
557
|
-
|
|
571
|
+
BottomSheetScrollView,
|
|
558
572
|
{
|
|
559
|
-
ref:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
contentContainerStyle: styles3.list,
|
|
565
|
-
removeClippedSubviews: false,
|
|
566
|
-
onScrollToIndexFailed: (info) => {
|
|
567
|
-
const offset = info.averageItemLength * info.index;
|
|
568
|
-
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
569
|
-
setTimeout(() => {
|
|
570
|
-
try {
|
|
571
|
-
listRef.current?.scrollToIndex({ index: info.index, animated: true, viewPosition: 0 });
|
|
572
|
-
} catch {
|
|
573
|
-
}
|
|
574
|
-
}, 100);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
573
|
+
ref: scrollRef,
|
|
574
|
+
contentContainerStyle: styles3.list
|
|
575
|
+
},
|
|
576
|
+
listHeader,
|
|
577
|
+
tags.map((tag) => /* @__PURE__ */ React6.createElement(View5, { key: tag._id, onLayout: (e) => handleItemLayout(tag._id, e) }, children(tag, tag._id === closestTagId)))
|
|
577
578
|
);
|
|
578
579
|
}
|
|
579
580
|
function DefaultLineTagList({ listHeader }) {
|