@retor/react-native 0.3.2 → 0.3.6

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.d.mts CHANGED
@@ -165,16 +165,26 @@ declare const Viewer: React.ForwardRefExoticComponent<ViewerProps & React.RefAtt
165
165
 
166
166
  interface HudProps {
167
167
  children: React.ReactNode;
168
+ /**
169
+ * Maximum top inset for all sheets (in pixels). The sheets won't expand
170
+ * past `screenHeight - topInset`. Use this to leave room for your app's
171
+ * floating header buttons. Defaults to 0 (sheets can extend to the very top).
172
+ *
173
+ * Per-sheet `topInset` props override this default.
174
+ */
175
+ topInset?: number;
168
176
  }
169
177
  /**
170
178
  * Sets up the bottom-sheet provider tree for the SDK's UI components
171
179
  * (`<ProjectSheet>`, `<LineDetailSheet>`, `<AddNoteSheet>`).
172
180
  *
173
- * Place inside `<Viewer>` so the sheets can read scene state from the bridge:
181
+ * Place inside `<Viewer>` so the sheets can read scene state from the bridge.
182
+ * Pass `topInset` to set a global ceiling so the sheets won't overlap your
183
+ * app's floating header buttons.
174
184
  *
175
185
  * ```tsx
176
186
  * <Viewer projectId="...">
177
- * <Hud>
187
+ * <Hud topInset={160}>
178
188
  * <ProjectSheet />
179
189
  * <LineDetailSheet />
180
190
  * <AddNoteSheet />
@@ -182,11 +192,16 @@ interface HudProps {
182
192
  * </Viewer>
183
193
  * ```
184
194
  */
185
- declare function Hud({ children }: HudProps): React.JSX.Element;
195
+ declare function Hud({ children, topInset }: HudProps): React.JSX.Element;
186
196
 
187
197
  interface ProjectSheetProps {
188
- /** Snap points. Defaults to ["35%", "85%"]. */
198
+ /** Snap points. Defaults to ["35%", "75%"]. */
189
199
  snapPoints?: (string | number)[];
200
+ /**
201
+ * Top inset in pixels — the sheet won't expand past `screenHeight - topInset`.
202
+ * Overrides the default set on `<Hud>`.
203
+ */
204
+ topInset?: number;
190
205
  /** Override the default header (project name + description + arrow button). */
191
206
  renderHeader?: (project: {
192
207
  name?: string;
@@ -198,7 +213,7 @@ interface ProjectSheetProps {
198
213
  /**
199
214
  * Browse-mode bottom sheet — auto-presents whenever no line is open.
200
215
  */
201
- declare function ProjectSheet({ snapPoints, renderHeader, children }: ProjectSheetProps): React.JSX.Element;
216
+ declare function ProjectSheet({ snapPoints, topInset, renderHeader, children }: ProjectSheetProps): React.JSX.Element;
202
217
  interface LinesCarouselProps {
203
218
  children: (line: RetorLine, index: number) => React.ReactNode;
204
219
  gap?: number;
@@ -207,14 +222,19 @@ interface LinesCarouselProps {
207
222
  declare function LinesCarousel({ children, gap, paddingHorizontal }: LinesCarouselProps): React.JSX.Element | null;
208
223
 
209
224
  interface LineDetailSheetProps {
210
- /** Snap points. Defaults to ["35%", "85%"]. */
225
+ /** Snap points. Defaults to ["35%", "75%"]. */
211
226
  snapPoints?: (string | number)[];
227
+ /**
228
+ * Top inset in pixels — the sheet won't expand past `screenHeight - topInset`.
229
+ * Overrides the default set on `<Hud>`.
230
+ */
231
+ topInset?: number;
212
232
  /** Override the header. */
213
233
  renderHeader?: (line: RetorLine) => React.ReactNode;
214
234
  /** Custom content (typically a `<LineTagList>`). */
215
235
  children?: React.ReactNode;
216
236
  }
217
- declare function LineDetailSheet({ snapPoints, renderHeader, children }: LineDetailSheetProps): React.JSX.Element;
237
+ declare function LineDetailSheet({ snapPoints, topInset, renderHeader, children }: LineDetailSheetProps): React.JSX.Element;
218
238
  interface LineTagListProps {
219
239
  children: (tag: RetorTag, isActive: boolean) => React.ReactNode;
220
240
  /** Optional header rendered above the list (used internally for the default header). */
@@ -231,6 +251,11 @@ declare function LineTagList({ children, listHeader }: LineTagListProps): React.
231
251
  interface AddNoteSheetProps {
232
252
  /** Snap points. Defaults to ["50%"]. */
233
253
  snapPoints?: (string | number)[];
254
+ /**
255
+ * Top inset in pixels — the sheet won't expand past `screenHeight - topInset`.
256
+ * Overrides the default set on `<Hud>`.
257
+ */
258
+ topInset?: number;
234
259
  /** Max characters allowed. Defaults to 280. */
235
260
  maxLength?: number;
236
261
  /** Placeholder text. */
@@ -251,7 +276,7 @@ interface AddNoteFormApi {
251
276
  * Sheet for adding a note. Auto-presents when `useAddNote().open()` is called.
252
277
  * On submit, fires `onNoteSubmit` on the parent `<Viewer>` with the text + position.
253
278
  */
254
- declare function AddNoteSheet({ snapPoints, maxLength, placeholder, renderForm, }: AddNoteSheetProps): React.JSX.Element;
279
+ declare function AddNoteSheet({ snapPoints, topInset, maxLength, placeholder, renderForm, }: AddNoteSheetProps): React.JSX.Element;
255
280
 
256
281
  interface CoverPhotoProps {
257
282
  projectId: string;
package/dist/index.d.ts CHANGED
@@ -165,16 +165,26 @@ declare const Viewer: React.ForwardRefExoticComponent<ViewerProps & React.RefAtt
165
165
 
166
166
  interface HudProps {
167
167
  children: React.ReactNode;
168
+ /**
169
+ * Maximum top inset for all sheets (in pixels). The sheets won't expand
170
+ * past `screenHeight - topInset`. Use this to leave room for your app's
171
+ * floating header buttons. Defaults to 0 (sheets can extend to the very top).
172
+ *
173
+ * Per-sheet `topInset` props override this default.
174
+ */
175
+ topInset?: number;
168
176
  }
169
177
  /**
170
178
  * Sets up the bottom-sheet provider tree for the SDK's UI components
171
179
  * (`<ProjectSheet>`, `<LineDetailSheet>`, `<AddNoteSheet>`).
172
180
  *
173
- * Place inside `<Viewer>` so the sheets can read scene state from the bridge:
181
+ * Place inside `<Viewer>` so the sheets can read scene state from the bridge.
182
+ * Pass `topInset` to set a global ceiling so the sheets won't overlap your
183
+ * app's floating header buttons.
174
184
  *
175
185
  * ```tsx
176
186
  * <Viewer projectId="...">
177
- * <Hud>
187
+ * <Hud topInset={160}>
178
188
  * <ProjectSheet />
179
189
  * <LineDetailSheet />
180
190
  * <AddNoteSheet />
@@ -182,11 +192,16 @@ interface HudProps {
182
192
  * </Viewer>
183
193
  * ```
184
194
  */
185
- declare function Hud({ children }: HudProps): React.JSX.Element;
195
+ declare function Hud({ children, topInset }: HudProps): React.JSX.Element;
186
196
 
187
197
  interface ProjectSheetProps {
188
- /** Snap points. Defaults to ["35%", "85%"]. */
198
+ /** Snap points. Defaults to ["35%", "75%"]. */
189
199
  snapPoints?: (string | number)[];
200
+ /**
201
+ * Top inset in pixels — the sheet won't expand past `screenHeight - topInset`.
202
+ * Overrides the default set on `<Hud>`.
203
+ */
204
+ topInset?: number;
190
205
  /** Override the default header (project name + description + arrow button). */
191
206
  renderHeader?: (project: {
192
207
  name?: string;
@@ -198,7 +213,7 @@ interface ProjectSheetProps {
198
213
  /**
199
214
  * Browse-mode bottom sheet — auto-presents whenever no line is open.
200
215
  */
201
- declare function ProjectSheet({ snapPoints, renderHeader, children }: ProjectSheetProps): React.JSX.Element;
216
+ declare function ProjectSheet({ snapPoints, topInset, renderHeader, children }: ProjectSheetProps): React.JSX.Element;
202
217
  interface LinesCarouselProps {
203
218
  children: (line: RetorLine, index: number) => React.ReactNode;
204
219
  gap?: number;
@@ -207,14 +222,19 @@ interface LinesCarouselProps {
207
222
  declare function LinesCarousel({ children, gap, paddingHorizontal }: LinesCarouselProps): React.JSX.Element | null;
208
223
 
209
224
  interface LineDetailSheetProps {
210
- /** Snap points. Defaults to ["35%", "85%"]. */
225
+ /** Snap points. Defaults to ["35%", "75%"]. */
211
226
  snapPoints?: (string | number)[];
227
+ /**
228
+ * Top inset in pixels — the sheet won't expand past `screenHeight - topInset`.
229
+ * Overrides the default set on `<Hud>`.
230
+ */
231
+ topInset?: number;
212
232
  /** Override the header. */
213
233
  renderHeader?: (line: RetorLine) => React.ReactNode;
214
234
  /** Custom content (typically a `<LineTagList>`). */
215
235
  children?: React.ReactNode;
216
236
  }
217
- declare function LineDetailSheet({ snapPoints, renderHeader, children }: LineDetailSheetProps): React.JSX.Element;
237
+ declare function LineDetailSheet({ snapPoints, topInset, renderHeader, children }: LineDetailSheetProps): React.JSX.Element;
218
238
  interface LineTagListProps {
219
239
  children: (tag: RetorTag, isActive: boolean) => React.ReactNode;
220
240
  /** Optional header rendered above the list (used internally for the default header). */
@@ -231,6 +251,11 @@ declare function LineTagList({ children, listHeader }: LineTagListProps): React.
231
251
  interface AddNoteSheetProps {
232
252
  /** Snap points. Defaults to ["50%"]. */
233
253
  snapPoints?: (string | number)[];
254
+ /**
255
+ * Top inset in pixels — the sheet won't expand past `screenHeight - topInset`.
256
+ * Overrides the default set on `<Hud>`.
257
+ */
258
+ topInset?: number;
234
259
  /** Max characters allowed. Defaults to 280. */
235
260
  maxLength?: number;
236
261
  /** Placeholder text. */
@@ -251,7 +276,7 @@ interface AddNoteFormApi {
251
276
  * Sheet for adding a note. Auto-presents when `useAddNote().open()` is called.
252
277
  * On submit, fires `onNoteSubmit` on the parent `<Viewer>` with the text + position.
253
278
  */
254
- declare function AddNoteSheet({ snapPoints, maxLength, placeholder, renderForm, }: AddNoteSheetProps): React.JSX.Element;
279
+ declare function AddNoteSheet({ snapPoints, topInset, maxLength, placeholder, renderForm, }: AddNoteSheetProps): React.JSX.Element;
255
280
 
256
281
  interface CoverPhotoProps {
257
282
  projectId: string;
package/dist/index.js CHANGED
@@ -232,30 +232,47 @@ var Viewer = (0, import_react2.forwardRef)(function Viewer2({ projectId, id = "d
232
232
  if (!notes || !readyRef.current) return;
233
233
  send("set-notes", { notes });
234
234
  }, [notes, send]);
235
+ const closestTagIdRef = (0, import_react2.useRef)(closestTagId);
236
+ const addNoteTagIdRef = (0, import_react2.useRef)(addNoteTagId);
237
+ const activeLineIdRef = (0, import_react2.useRef)(activeLineId);
238
+ const linesRef = (0, import_react2.useRef)(lines);
239
+ const onNoteSubmitRef = (0, import_react2.useRef)(onNoteSubmit);
240
+ (0, import_react2.useEffect)(() => {
241
+ closestTagIdRef.current = closestTagId;
242
+ }, [closestTagId]);
243
+ (0, import_react2.useEffect)(() => {
244
+ addNoteTagIdRef.current = addNoteTagId;
245
+ }, [addNoteTagId]);
246
+ (0, import_react2.useEffect)(() => {
247
+ activeLineIdRef.current = activeLineId;
248
+ }, [activeLineId]);
249
+ (0, import_react2.useEffect)(() => {
250
+ linesRef.current = lines;
251
+ }, [lines]);
252
+ (0, import_react2.useEffect)(() => {
253
+ onNoteSubmitRef.current = onNoteSubmit;
254
+ }, [onNoteSubmit]);
235
255
  const openAddNote = (0, import_react2.useCallback)((tagId) => {
236
- setAddNoteTagId(tagId ?? closestTagId ?? null);
256
+ setAddNoteTagId(tagId ?? closestTagIdRef.current ?? null);
237
257
  setIsAddNoteOpen(true);
238
- }, [closestTagId]);
258
+ }, []);
239
259
  const closeAddNote = (0, import_react2.useCallback)(() => {
240
260
  setIsAddNoteOpen(false);
241
261
  }, []);
242
- const submitNote = (0, import_react2.useCallback)(
243
- (text, isPrivate = true) => {
244
- const tagId = addNoteTagId;
245
- const lineId = activeLineId;
246
- const tag = lines.find((l) => l._id === lineId)?.tags.find((t) => t._id === tagId);
247
- const payload = {
248
- text,
249
- isPrivate,
250
- tagId,
251
- lineId,
252
- position: tag?.position ?? null
253
- };
254
- onNoteSubmit?.(payload);
255
- setIsAddNoteOpen(false);
256
- },
257
- [addNoteTagId, activeLineId, lines, onNoteSubmit]
258
- );
262
+ const submitNote = (0, import_react2.useCallback)((text, isPrivate = true) => {
263
+ const tagId = addNoteTagIdRef.current;
264
+ const lineId = activeLineIdRef.current;
265
+ const tag = linesRef.current.find((l) => l._id === lineId)?.tags.find((t) => t._id === tagId);
266
+ const payload = {
267
+ text,
268
+ isPrivate,
269
+ tagId,
270
+ lineId,
271
+ position: tag?.position ?? null
272
+ };
273
+ onNoteSubmitRef.current?.(payload);
274
+ setIsAddNoteOpen(false);
275
+ }, []);
259
276
  const handleMessage = (0, import_react2.useCallback)(
260
277
  (event) => {
261
278
  let data = null;
@@ -353,8 +370,12 @@ var styles = import_react_native.StyleSheet.create({
353
370
  var import_react3 = __toESM(require("react"));
354
371
  var import_react_native2 = require("react-native");
355
372
  var import_bottom_sheet = require("@gorhom/bottom-sheet");
356
- function Hud({ children }) {
357
- return /* @__PURE__ */ import_react3.default.createElement(import_react_native2.View, { pointerEvents: "box-none", style: import_react_native2.StyleSheet.absoluteFill }, /* @__PURE__ */ import_react3.default.createElement(import_bottom_sheet.BottomSheetModalProvider, null, children));
373
+ var HudContext = (0, import_react3.createContext)({ topInset: 0 });
374
+ function useHudContext() {
375
+ return (0, import_react3.useContext)(HudContext);
376
+ }
377
+ function Hud({ children, topInset = 0 }) {
378
+ return /* @__PURE__ */ import_react3.default.createElement(HudContext.Provider, { value: { topInset } }, /* @__PURE__ */ import_react3.default.createElement(import_react_native2.View, { pointerEvents: "box-none", style: import_react_native2.StyleSheet.absoluteFill }, /* @__PURE__ */ import_react3.default.createElement(import_bottom_sheet.BottomSheetModalProvider, null, children)));
358
379
  }
359
380
 
360
381
  // src/ProjectSheet.tsx
@@ -372,8 +393,20 @@ function BlurBackground({ style }) {
372
393
  }
373
394
 
374
395
  // src/ProjectSheet.tsx
375
- function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
396
+ var renderBackdrop = (props) => /* @__PURE__ */ import_react5.default.createElement(
397
+ import_bottom_sheet2.BottomSheetBackdrop,
398
+ {
399
+ ...props,
400
+ appearsOnIndex: 1,
401
+ disappearsOnIndex: 0,
402
+ opacity: 0.4,
403
+ pressBehavior: "collapse"
404
+ }
405
+ );
406
+ function ProjectSheet({ snapPoints = ["35%", "75%"], topInset, renderHeader, children }) {
376
407
  const { project, activeLineId, isAddNoteOpen } = useRetorBridge();
408
+ const { topInset: hudTopInset } = useHudContext();
409
+ const effectiveTopInset = topInset ?? hudTopInset;
377
410
  const sheetRef = (0, import_react5.useRef)(null);
378
411
  const snapPointsArr = (0, import_react5.useMemo)(() => snapPoints, [snapPoints]);
379
412
  const [minimized, setMinimized] = (0, import_react5.useState)(false);
@@ -406,19 +439,12 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
406
439
  snapPoints: snapPointsArr,
407
440
  enablePanDownToClose: false,
408
441
  enableDismissOnClose: false,
442
+ enableOverDrag: false,
409
443
  onChange: handleSheetChange,
410
- backdropComponent: (props) => /* @__PURE__ */ import_react5.default.createElement(
411
- import_bottom_sheet2.BottomSheetBackdrop,
412
- {
413
- ...props,
414
- appearsOnIndex: 1,
415
- disappearsOnIndex: 0,
416
- opacity: 0.4,
417
- pressBehavior: "collapse"
418
- }
419
- ),
444
+ backdropComponent: renderBackdrop,
420
445
  handleIndicatorStyle: styles2.handle,
421
- backgroundComponent: BlurBackground
446
+ backgroundComponent: BlurBackground,
447
+ topInset: effectiveTopInset
422
448
  },
423
449
  /* @__PURE__ */ import_react5.default.createElement(import_bottom_sheet2.BottomSheetView, { style: styles2.content }, renderHeader ? renderHeader({ name: project?.name, description: project?.description }) : /* @__PURE__ */ import_react5.default.createElement(import_react_native4.View, { style: styles2.header }, /* @__PURE__ */ import_react5.default.createElement(import_react_native4.View, { style: { flex: 1, minWidth: 0 } }, project?.name && /* @__PURE__ */ import_react5.default.createElement(import_react_native4.Text, { style: styles2.title, numberOfLines: 1 }, project.name), project?.description && /* @__PURE__ */ import_react5.default.createElement(import_react_native4.Text, { style: styles2.subtitle, numberOfLines: 4 }, project.description)), /* @__PURE__ */ import_react5.default.createElement(import_react_native4.Pressable, { style: styles2.iconBtn, onPress: toggleMinimize }, minimized ? /* @__PURE__ */ import_react5.default.createElement(import_lucide_react_native.ArrowUp, { size: 14, color: "rgba(255,255,255,0.6)" }) : /* @__PURE__ */ import_react5.default.createElement(import_lucide_react_native.ArrowDown, { size: 14, color: "rgba(255,255,255,0.6)" }))), children ?? /* @__PURE__ */ import_react5.default.createElement(DefaultLinesCarousel, null))
424
450
  );
@@ -497,8 +523,20 @@ var import_react_native5 = require("react-native");
497
523
  var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
498
524
  var import_react_native_svg = __toESM(require("react-native-svg"));
499
525
  var import_lucide_react_native2 = require("lucide-react-native");
500
- function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
526
+ var renderBackdrop2 = (props) => /* @__PURE__ */ import_react6.default.createElement(
527
+ import_bottom_sheet3.BottomSheetBackdrop,
528
+ {
529
+ ...props,
530
+ appearsOnIndex: 1,
531
+ disappearsOnIndex: 0,
532
+ opacity: 0.4,
533
+ pressBehavior: "collapse"
534
+ }
535
+ );
536
+ function LineDetailSheet({ snapPoints = ["35%", "75%"], topInset, renderHeader, children }) {
501
537
  const { activeLine, isAddNoteOpen, controls } = useRetorBridge();
538
+ const { topInset: hudTopInset } = useHudContext();
539
+ const effectiveTopInset = topInset ?? hudTopInset;
502
540
  const sheetRef = (0, import_react6.useRef)(null);
503
541
  const snapPointsArr = (0, import_react6.useMemo)(() => snapPoints, [snapPoints]);
504
542
  const [minimized, setMinimized] = (0, import_react6.useState)(false);
@@ -536,20 +574,13 @@ function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }
536
574
  snapPoints: snapPointsArr,
537
575
  enablePanDownToClose: false,
538
576
  enableDismissOnClose: false,
577
+ enableOverDrag: false,
539
578
  onChange: handleSheetChange,
540
579
  footerComponent: renderFooter,
541
- backdropComponent: (props) => /* @__PURE__ */ import_react6.default.createElement(
542
- import_bottom_sheet3.BottomSheetBackdrop,
543
- {
544
- ...props,
545
- appearsOnIndex: 1,
546
- disappearsOnIndex: 0,
547
- opacity: 0.4,
548
- pressBehavior: "collapse"
549
- }
550
- ),
580
+ backdropComponent: renderBackdrop2,
551
581
  handleIndicatorStyle: styles3.handle,
552
- backgroundComponent: BlurBackground
582
+ backgroundComponent: BlurBackground,
583
+ topInset: effectiveTopInset
553
584
  },
554
585
  activeLine && (children ?? /* @__PURE__ */ import_react6.default.createElement(DefaultLineTagList, { listHeader: header }))
555
586
  );
@@ -594,13 +625,18 @@ function LineTagList({ children, listHeader }) {
594
625
  }, [activeLine?._id]);
595
626
  (0, import_react6.useEffect)(() => {
596
627
  if (!closestTagId) return;
597
- const t = setTimeout(() => {
628
+ let attempts = 0;
629
+ const tick = () => {
598
630
  const y = offsetsRef.current.get(closestTagId);
599
631
  if (y != null) {
600
- scrollRef.current?.scrollTo({ y, animated: true });
632
+ scrollRef.current?.scrollTo?.({ y, animated: true });
633
+ return;
601
634
  }
602
- }, 60);
603
- return () => clearTimeout(t);
635
+ attempts += 1;
636
+ if (attempts < 10) raf = requestAnimationFrame(tick);
637
+ };
638
+ let raf = requestAnimationFrame(tick);
639
+ return () => cancelAnimationFrame(raf);
604
640
  }, [closestTagId]);
605
641
  if (!activeLine) return null;
606
642
  const handleItemLayout = (id, e) => {
@@ -665,10 +701,11 @@ var styles3 = import_react_native5.StyleSheet.create({
665
701
  justifyContent: "center",
666
702
  position: "relative"
667
703
  },
668
- list: { paddingHorizontal: 16, paddingBottom: 96, gap: 4 },
704
+ list: { paddingBottom: 96, gap: 4 },
669
705
  tagItem: {
670
706
  flexDirection: "row",
671
707
  alignItems: "center",
708
+ marginHorizontal: 16,
672
709
  paddingHorizontal: 12,
673
710
  paddingVertical: 12,
674
711
  borderRadius: 12,
@@ -707,13 +744,25 @@ var import_react7 = __toESM(require("react"));
707
744
  var import_react_native6 = require("react-native");
708
745
  var import_bottom_sheet4 = require("@gorhom/bottom-sheet");
709
746
  var import_lucide_react_native3 = require("lucide-react-native");
747
+ var renderBackdrop3 = (props) => /* @__PURE__ */ import_react7.default.createElement(
748
+ import_bottom_sheet4.BottomSheetBackdrop,
749
+ {
750
+ ...props,
751
+ appearsOnIndex: 0,
752
+ disappearsOnIndex: -1,
753
+ opacity: 0.6
754
+ }
755
+ );
710
756
  function AddNoteSheet({
711
757
  snapPoints = ["50%"],
758
+ topInset,
712
759
  maxLength = 280,
713
760
  placeholder = "Write a note...",
714
761
  renderForm
715
762
  }) {
716
763
  const { isAddNoteOpen, addNoteTagId, activeLine, closeAddNote, submitNote } = useRetorBridge();
764
+ const { topInset: hudTopInset } = useHudContext();
765
+ const effectiveTopInset = topInset ?? hudTopInset;
717
766
  const sheetRef = (0, import_react7.useRef)(null);
718
767
  const snapPointsArr = (0, import_react7.useMemo)(() => snapPoints, [snapPoints]);
719
768
  const [text, setText] = (0, import_react7.useState)("");
@@ -753,18 +802,12 @@ function AddNoteSheet({
753
802
  ref: sheetRef,
754
803
  snapPoints: snapPointsArr,
755
804
  enablePanDownToClose: true,
805
+ enableOverDrag: false,
756
806
  onDismiss: closeAddNote,
757
- backdropComponent: (props) => /* @__PURE__ */ import_react7.default.createElement(
758
- import_bottom_sheet4.BottomSheetBackdrop,
759
- {
760
- ...props,
761
- appearsOnIndex: 0,
762
- disappearsOnIndex: -1,
763
- opacity: 0.6
764
- }
765
- ),
807
+ backdropComponent: renderBackdrop3,
766
808
  handleIndicatorStyle: styles4.handle,
767
- backgroundComponent: BlurBackground
809
+ backgroundComponent: BlurBackground,
810
+ topInset: effectiveTopInset
768
811
  },
769
812
  /* @__PURE__ */ import_react7.default.createElement(import_bottom_sheet4.BottomSheetView, { style: styles4.content }, renderForm ? renderForm(formApi) : /* @__PURE__ */ import_react7.default.createElement(import_react_native6.View, { style: styles4.form }, /* @__PURE__ */ import_react7.default.createElement(import_react_native6.View, { style: styles4.headerRow }, /* @__PURE__ */ import_react7.default.createElement(import_react_native6.View, { style: { flex: 1 } }, activeLine && /* @__PURE__ */ import_react7.default.createElement(import_react_native6.Text, { style: styles4.title, numberOfLines: 1 }, activeLine.name), tag && /* @__PURE__ */ import_react7.default.createElement(import_react_native6.Text, { style: styles4.subtitle, numberOfLines: 1 }, tag.name)), /* @__PURE__ */ import_react7.default.createElement(import_react_native6.Pressable, { onPress: closeAddNote, style: styles4.closeBtn }, /* @__PURE__ */ import_react7.default.createElement(import_lucide_react_native3.X, { size: 14, color: "rgba(255,255,255,0.6)" }))), /* @__PURE__ */ import_react7.default.createElement(
770
813
  import_bottom_sheet4.BottomSheetTextInput,
package/dist/index.mjs CHANGED
@@ -188,30 +188,47 @@ var Viewer = forwardRef(function Viewer2({ projectId, id = "default", baseUrl =
188
188
  if (!notes || !readyRef.current) return;
189
189
  send("set-notes", { notes });
190
190
  }, [notes, send]);
191
+ const closestTagIdRef = useRef(closestTagId);
192
+ const addNoteTagIdRef = useRef(addNoteTagId);
193
+ const activeLineIdRef = useRef(activeLineId);
194
+ const linesRef = useRef(lines);
195
+ const onNoteSubmitRef = useRef(onNoteSubmit);
196
+ useEffect(() => {
197
+ closestTagIdRef.current = closestTagId;
198
+ }, [closestTagId]);
199
+ useEffect(() => {
200
+ addNoteTagIdRef.current = addNoteTagId;
201
+ }, [addNoteTagId]);
202
+ useEffect(() => {
203
+ activeLineIdRef.current = activeLineId;
204
+ }, [activeLineId]);
205
+ useEffect(() => {
206
+ linesRef.current = lines;
207
+ }, [lines]);
208
+ useEffect(() => {
209
+ onNoteSubmitRef.current = onNoteSubmit;
210
+ }, [onNoteSubmit]);
191
211
  const openAddNote = useCallback((tagId) => {
192
- setAddNoteTagId(tagId ?? closestTagId ?? null);
212
+ setAddNoteTagId(tagId ?? closestTagIdRef.current ?? null);
193
213
  setIsAddNoteOpen(true);
194
- }, [closestTagId]);
214
+ }, []);
195
215
  const closeAddNote = useCallback(() => {
196
216
  setIsAddNoteOpen(false);
197
217
  }, []);
198
- const submitNote = useCallback(
199
- (text, isPrivate = true) => {
200
- const tagId = addNoteTagId;
201
- const lineId = activeLineId;
202
- const tag = lines.find((l) => l._id === lineId)?.tags.find((t) => t._id === tagId);
203
- const payload = {
204
- text,
205
- isPrivate,
206
- tagId,
207
- lineId,
208
- position: tag?.position ?? null
209
- };
210
- onNoteSubmit?.(payload);
211
- setIsAddNoteOpen(false);
212
- },
213
- [addNoteTagId, activeLineId, lines, onNoteSubmit]
214
- );
218
+ const submitNote = useCallback((text, isPrivate = true) => {
219
+ const tagId = addNoteTagIdRef.current;
220
+ const lineId = activeLineIdRef.current;
221
+ const tag = linesRef.current.find((l) => l._id === lineId)?.tags.find((t) => t._id === tagId);
222
+ const payload = {
223
+ text,
224
+ isPrivate,
225
+ tagId,
226
+ lineId,
227
+ position: tag?.position ?? null
228
+ };
229
+ onNoteSubmitRef.current?.(payload);
230
+ setIsAddNoteOpen(false);
231
+ }, []);
215
232
  const handleMessage = useCallback(
216
233
  (event) => {
217
234
  let data = null;
@@ -306,11 +323,15 @@ var styles = StyleSheet.create({
306
323
  });
307
324
 
308
325
  // src/Hud.tsx
309
- import React3 from "react";
326
+ import React3, { createContext as createContext2, useContext as useContext2 } from "react";
310
327
  import { StyleSheet as StyleSheet2, View as View2 } from "react-native";
311
328
  import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
312
- function Hud({ children }) {
313
- return /* @__PURE__ */ React3.createElement(View2, { pointerEvents: "box-none", style: StyleSheet2.absoluteFill }, /* @__PURE__ */ React3.createElement(BottomSheetModalProvider, null, children));
329
+ var HudContext = createContext2({ topInset: 0 });
330
+ function useHudContext() {
331
+ return useContext2(HudContext);
332
+ }
333
+ function Hud({ children, topInset = 0 }) {
334
+ return /* @__PURE__ */ React3.createElement(HudContext.Provider, { value: { topInset } }, /* @__PURE__ */ React3.createElement(View2, { pointerEvents: "box-none", style: StyleSheet2.absoluteFill }, /* @__PURE__ */ React3.createElement(BottomSheetModalProvider, null, children)));
314
335
  }
315
336
 
316
337
  // src/ProjectSheet.tsx
@@ -328,8 +349,20 @@ function BlurBackground({ style }) {
328
349
  }
329
350
 
330
351
  // src/ProjectSheet.tsx
331
- function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
352
+ var renderBackdrop = (props) => /* @__PURE__ */ React5.createElement(
353
+ BottomSheetBackdrop,
354
+ {
355
+ ...props,
356
+ appearsOnIndex: 1,
357
+ disappearsOnIndex: 0,
358
+ opacity: 0.4,
359
+ pressBehavior: "collapse"
360
+ }
361
+ );
362
+ function ProjectSheet({ snapPoints = ["35%", "75%"], topInset, renderHeader, children }) {
332
363
  const { project, activeLineId, isAddNoteOpen } = useRetorBridge();
364
+ const { topInset: hudTopInset } = useHudContext();
365
+ const effectiveTopInset = topInset ?? hudTopInset;
333
366
  const sheetRef = useRef2(null);
334
367
  const snapPointsArr = useMemo3(() => snapPoints, [snapPoints]);
335
368
  const [minimized, setMinimized] = useState2(false);
@@ -362,19 +395,12 @@ function ProjectSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
362
395
  snapPoints: snapPointsArr,
363
396
  enablePanDownToClose: false,
364
397
  enableDismissOnClose: false,
398
+ enableOverDrag: false,
365
399
  onChange: handleSheetChange,
366
- backdropComponent: (props) => /* @__PURE__ */ React5.createElement(
367
- BottomSheetBackdrop,
368
- {
369
- ...props,
370
- appearsOnIndex: 1,
371
- disappearsOnIndex: 0,
372
- opacity: 0.4,
373
- pressBehavior: "collapse"
374
- }
375
- ),
400
+ backdropComponent: renderBackdrop,
376
401
  handleIndicatorStyle: styles2.handle,
377
- backgroundComponent: BlurBackground
402
+ backgroundComponent: BlurBackground,
403
+ topInset: effectiveTopInset
378
404
  },
379
405
  /* @__PURE__ */ React5.createElement(BottomSheetView, { style: styles2.content }, renderHeader ? renderHeader({ name: project?.name, description: project?.description }) : /* @__PURE__ */ React5.createElement(View4, { style: styles2.header }, /* @__PURE__ */ React5.createElement(View4, { style: { flex: 1, minWidth: 0 } }, project?.name && /* @__PURE__ */ React5.createElement(Text, { style: styles2.title, numberOfLines: 1 }, project.name), project?.description && /* @__PURE__ */ React5.createElement(Text, { style: styles2.subtitle, numberOfLines: 4 }, project.description)), /* @__PURE__ */ React5.createElement(Pressable, { style: styles2.iconBtn, onPress: toggleMinimize }, minimized ? /* @__PURE__ */ React5.createElement(ArrowUp, { size: 14, color: "rgba(255,255,255,0.6)" }) : /* @__PURE__ */ React5.createElement(ArrowDown, { size: 14, color: "rgba(255,255,255,0.6)" }))), children ?? /* @__PURE__ */ React5.createElement(DefaultLinesCarousel, null))
380
406
  );
@@ -458,8 +484,20 @@ import {
458
484
  } from "@gorhom/bottom-sheet";
459
485
  import Svg, { Circle } from "react-native-svg";
460
486
  import { ArrowDown as ArrowDown2, ArrowUp as ArrowUp2, Pause, Play, Plus } from "lucide-react-native";
461
- function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }) {
487
+ var renderBackdrop2 = (props) => /* @__PURE__ */ React6.createElement(
488
+ BottomSheetBackdrop2,
489
+ {
490
+ ...props,
491
+ appearsOnIndex: 1,
492
+ disappearsOnIndex: 0,
493
+ opacity: 0.4,
494
+ pressBehavior: "collapse"
495
+ }
496
+ );
497
+ function LineDetailSheet({ snapPoints = ["35%", "75%"], topInset, renderHeader, children }) {
462
498
  const { activeLine, isAddNoteOpen, controls } = useRetorBridge();
499
+ const { topInset: hudTopInset } = useHudContext();
500
+ const effectiveTopInset = topInset ?? hudTopInset;
463
501
  const sheetRef = useRef3(null);
464
502
  const snapPointsArr = useMemo4(() => snapPoints, [snapPoints]);
465
503
  const [minimized, setMinimized] = useState3(false);
@@ -497,20 +535,13 @@ function LineDetailSheet({ snapPoints = ["35%", "85%"], renderHeader, children }
497
535
  snapPoints: snapPointsArr,
498
536
  enablePanDownToClose: false,
499
537
  enableDismissOnClose: false,
538
+ enableOverDrag: false,
500
539
  onChange: handleSheetChange,
501
540
  footerComponent: renderFooter,
502
- backdropComponent: (props) => /* @__PURE__ */ React6.createElement(
503
- BottomSheetBackdrop2,
504
- {
505
- ...props,
506
- appearsOnIndex: 1,
507
- disappearsOnIndex: 0,
508
- opacity: 0.4,
509
- pressBehavior: "collapse"
510
- }
511
- ),
541
+ backdropComponent: renderBackdrop2,
512
542
  handleIndicatorStyle: styles3.handle,
513
- backgroundComponent: BlurBackground
543
+ backgroundComponent: BlurBackground,
544
+ topInset: effectiveTopInset
514
545
  },
515
546
  activeLine && (children ?? /* @__PURE__ */ React6.createElement(DefaultLineTagList, { listHeader: header }))
516
547
  );
@@ -555,13 +586,18 @@ function LineTagList({ children, listHeader }) {
555
586
  }, [activeLine?._id]);
556
587
  useEffect3(() => {
557
588
  if (!closestTagId) return;
558
- const t = setTimeout(() => {
589
+ let attempts = 0;
590
+ const tick = () => {
559
591
  const y = offsetsRef.current.get(closestTagId);
560
592
  if (y != null) {
561
- scrollRef.current?.scrollTo({ y, animated: true });
593
+ scrollRef.current?.scrollTo?.({ y, animated: true });
594
+ return;
562
595
  }
563
- }, 60);
564
- return () => clearTimeout(t);
596
+ attempts += 1;
597
+ if (attempts < 10) raf = requestAnimationFrame(tick);
598
+ };
599
+ let raf = requestAnimationFrame(tick);
600
+ return () => cancelAnimationFrame(raf);
565
601
  }, [closestTagId]);
566
602
  if (!activeLine) return null;
567
603
  const handleItemLayout = (id, e) => {
@@ -626,10 +662,11 @@ var styles3 = StyleSheet5.create({
626
662
  justifyContent: "center",
627
663
  position: "relative"
628
664
  },
629
- list: { paddingHorizontal: 16, paddingBottom: 96, gap: 4 },
665
+ list: { paddingBottom: 96, gap: 4 },
630
666
  tagItem: {
631
667
  flexDirection: "row",
632
668
  alignItems: "center",
669
+ marginHorizontal: 16,
633
670
  paddingHorizontal: 12,
634
671
  paddingVertical: 12,
635
672
  borderRadius: 12,
@@ -673,13 +710,25 @@ import {
673
710
  BottomSheetView as BottomSheetView2
674
711
  } from "@gorhom/bottom-sheet";
675
712
  import { ArrowUp as ArrowUp3, X } from "lucide-react-native";
713
+ var renderBackdrop3 = (props) => /* @__PURE__ */ React7.createElement(
714
+ BottomSheetBackdrop3,
715
+ {
716
+ ...props,
717
+ appearsOnIndex: 0,
718
+ disappearsOnIndex: -1,
719
+ opacity: 0.6
720
+ }
721
+ );
676
722
  function AddNoteSheet({
677
723
  snapPoints = ["50%"],
724
+ topInset,
678
725
  maxLength = 280,
679
726
  placeholder = "Write a note...",
680
727
  renderForm
681
728
  }) {
682
729
  const { isAddNoteOpen, addNoteTagId, activeLine, closeAddNote, submitNote } = useRetorBridge();
730
+ const { topInset: hudTopInset } = useHudContext();
731
+ const effectiveTopInset = topInset ?? hudTopInset;
683
732
  const sheetRef = useRef4(null);
684
733
  const snapPointsArr = useMemo5(() => snapPoints, [snapPoints]);
685
734
  const [text, setText] = useState4("");
@@ -719,18 +768,12 @@ function AddNoteSheet({
719
768
  ref: sheetRef,
720
769
  snapPoints: snapPointsArr,
721
770
  enablePanDownToClose: true,
771
+ enableOverDrag: false,
722
772
  onDismiss: closeAddNote,
723
- backdropComponent: (props) => /* @__PURE__ */ React7.createElement(
724
- BottomSheetBackdrop3,
725
- {
726
- ...props,
727
- appearsOnIndex: 0,
728
- disappearsOnIndex: -1,
729
- opacity: 0.6
730
- }
731
- ),
773
+ backdropComponent: renderBackdrop3,
732
774
  handleIndicatorStyle: styles4.handle,
733
- backgroundComponent: BlurBackground
775
+ backgroundComponent: BlurBackground,
776
+ topInset: effectiveTopInset
734
777
  },
735
778
  /* @__PURE__ */ React7.createElement(BottomSheetView2, { style: styles4.content }, renderForm ? renderForm(formApi) : /* @__PURE__ */ React7.createElement(View6, { style: styles4.form }, /* @__PURE__ */ React7.createElement(View6, { style: styles4.headerRow }, /* @__PURE__ */ React7.createElement(View6, { style: { flex: 1 } }, activeLine && /* @__PURE__ */ React7.createElement(Text3, { style: styles4.title, numberOfLines: 1 }, activeLine.name), tag && /* @__PURE__ */ React7.createElement(Text3, { style: styles4.subtitle, numberOfLines: 1 }, tag.name)), /* @__PURE__ */ React7.createElement(Pressable3, { onPress: closeAddNote, style: styles4.closeBtn }, /* @__PURE__ */ React7.createElement(X, { size: 14, color: "rgba(255,255,255,0.6)" }))), /* @__PURE__ */ React7.createElement(
736
779
  BottomSheetTextInput,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retor/react-native",
3
- "version": "0.3.2",
3
+ "version": "0.3.6",
4
4
  "description": "React Native SDK for embedding Retor 3D experiences",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",