@agimon-ai/po-native-components 0.0.0-alpha.67 → 0.0.0-alpha.69
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.
|
@@ -15,6 +15,8 @@ export interface AnnotationMark {
|
|
|
15
15
|
x: number;
|
|
16
16
|
y: number;
|
|
17
17
|
}[];
|
|
18
|
+
/** A translucent, thick rose stroke over the content. */
|
|
19
|
+
highlight?: boolean;
|
|
18
20
|
}
|
|
19
21
|
export interface AnnotationSurfaceProps {
|
|
20
22
|
/** The content being marked up: an image, a page, a block of text. */
|
|
@@ -36,6 +38,16 @@ export interface AnnotationSurfaceProps {
|
|
|
36
38
|
accessibilityLabel: string;
|
|
37
39
|
/** Image surfaces can pinch and move with two fingers on both native platforms. */
|
|
38
40
|
zoomable?: boolean;
|
|
41
|
+
/** Explicit one-finger pan instead of placing or drawing marks. */
|
|
42
|
+
navigation?: boolean;
|
|
43
|
+
/** Controlled transform. Origins are unscaled RN layout positions, not raster pixels. */
|
|
44
|
+
viewport?: {
|
|
45
|
+
zoom: number;
|
|
46
|
+
originX: number;
|
|
47
|
+
originY: number;
|
|
48
|
+
};
|
|
49
|
+
/** Render the in-progress stroke as a highlight. */
|
|
50
|
+
drawingHighlight?: boolean;
|
|
39
51
|
/** Viewport dimensions and source-layout-to-viewport transform, all in RN layout units, not raster pixels. */
|
|
40
52
|
onViewportChange?: (viewport: {
|
|
41
53
|
width: number;
|
|
@@ -52,6 +64,6 @@ export interface AnnotationSurfaceProps {
|
|
|
52
64
|
* content, so text and controls inside stay readable, and activating it places a
|
|
53
65
|
* note at its centre.
|
|
54
66
|
*/
|
|
55
|
-
export declare function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable, onViewportChange, style }: AnnotationSurfaceProps): import("react").JSX.Element;
|
|
67
|
+
export declare function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable, onViewportChange, navigation, viewport: controlledViewport, drawingHighlight, style }: AnnotationSurfaceProps): import("react").JSX.Element;
|
|
56
68
|
//#endregion
|
|
57
69
|
//# sourceMappingURL=AnnotationSurface.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnnotationSurface.d.mts","names":[],"sources":["../../src/components/AnnotationSurface.tsx"],"mappings":";;;;iBAQiB;EACf;EACA;EACA;;EAEA;;EAEA;;EAEA;IAAoB;IAAW;;;
|
|
1
|
+
{"version":3,"file":"AnnotationSurface.d.mts","names":[],"sources":["../../src/components/AnnotationSurface.tsx"],"mappings":";;;;iBAQiB;EACf;EACA;EACA;;EAEA;;EAEA;;EAEA;IAAoB;IAAW;;;EAE/B;;iBAGe;;EAEf,UAAU;EACV,gBAAgB;EAChB;;EAEA,WAAW;IAAS;IAAW;;;EAE/B,UAAU;IAAU;IAAW;;EAC/B,gBAAgB;;EAEhB;;EAEA;;EAEA;;EAEA;IAAa;IAAc;IAAiB;;;EAE5C;;EAEA,oBAAoB;IAClB;IACA;IACA;IACA;IACA;;EAEF,QAAQ,UAAU;;;;;;;;wBAqDJ,oBACd,UACA,OACA,YACA,SACA,QACA,cACA,oBACA,UACA,kBACA,YACA,UAAU,oBACV,kBACA,SACC,yCAAsB,IAAA"}
|
|
@@ -3,14 +3,15 @@ import { selectionTick } from "../lib/haptics.mjs";
|
|
|
3
3
|
import { View } from "react-native-unistyles/components/native/View";
|
|
4
4
|
import { Text } from "react-native-unistyles/components/native/Text";
|
|
5
5
|
import { StyleSheet } from "react-native-unistyles";
|
|
6
|
-
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Pressable } from "react-native-unistyles/components/native/Pressable";
|
|
9
9
|
|
|
10
10
|
//#region src/components/AnnotationSurface.tsx
|
|
11
|
-
const clamp = (value) => Math.min(1, Math.max(0, value));
|
|
11
|
+
const clamp = (value) => Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0;
|
|
12
12
|
const MAX_POINTS = 128;
|
|
13
|
-
function Stroke({ points, width, height }) {
|
|
13
|
+
function Stroke({ points, width, height, highlight = false }) {
|
|
14
|
+
const thickness = highlight ? 16 : 3;
|
|
14
15
|
const bounded = points.slice(0, MAX_POINTS);
|
|
15
16
|
return bounded.map((point, index) => {
|
|
16
17
|
const previous = bounded[index - 1];
|
|
@@ -25,12 +26,16 @@ function Stroke({ points, width, height }) {
|
|
|
25
26
|
const dx = clamp(point.x) * width - x;
|
|
26
27
|
const dy = clamp(point.y) * height - y;
|
|
27
28
|
const length = Math.hypot(dx, dy);
|
|
28
|
-
return /* @__PURE__ */ jsx(View, { style: [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
return /* @__PURE__ */ jsx(View, { style: [
|
|
30
|
+
styles.segment,
|
|
31
|
+
highlight && styles.highlight,
|
|
32
|
+
{
|
|
33
|
+
left: x + dx / 2 - length / 2,
|
|
34
|
+
top: y + dy / 2 - thickness / 2,
|
|
35
|
+
width: length,
|
|
36
|
+
transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }]
|
|
37
|
+
}
|
|
38
|
+
] }, index);
|
|
34
39
|
});
|
|
35
40
|
}
|
|
36
41
|
/**
|
|
@@ -39,7 +44,7 @@ function Stroke({ points, width, height }) {
|
|
|
39
44
|
* content, so text and controls inside stay readable, and activating it places a
|
|
40
45
|
* note at its centre.
|
|
41
46
|
*/
|
|
42
|
-
function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable = false, onViewportChange, style }) {
|
|
47
|
+
function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSelectMark, accessibilityLabel, zoomable = false, onViewportChange, navigation = false, viewport: controlledViewport, drawingHighlight = false, style }) {
|
|
43
48
|
const [size, setSize] = useState({
|
|
44
49
|
width: 0,
|
|
45
50
|
height: 0
|
|
@@ -52,6 +57,8 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
52
57
|
const transform = useRef(viewport);
|
|
53
58
|
const pinch = useRef(null);
|
|
54
59
|
const suppressTap = useRef(false);
|
|
60
|
+
const pressedPoint = useRef(void 0);
|
|
61
|
+
const navigating = useRef(false);
|
|
55
62
|
const reportViewport = (next, dimensions = size) => {
|
|
56
63
|
onViewportChange?.({
|
|
57
64
|
...dimensions,
|
|
@@ -60,26 +67,42 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
60
67
|
originY: dimensions.height * (1 - next.scale) / 2 + next.y
|
|
61
68
|
});
|
|
62
69
|
};
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
const boundViewport = (request, dimensions = size) => {
|
|
71
|
+
const scale = zoomable && Number.isFinite(request.zoom) ? Math.max(1, Math.min(4, request.zoom)) : 1;
|
|
72
|
+
const maxX = dimensions.width * (scale - 1) / 2;
|
|
73
|
+
const maxY = dimensions.height * (scale - 1) / 2;
|
|
74
|
+
return {
|
|
75
|
+
scale,
|
|
76
|
+
x: Math.max(-maxX, Math.min(maxX, (Number.isFinite(request.originX) ? request.originX : 0) + maxX)),
|
|
77
|
+
y: Math.max(-maxY, Math.min(maxY, (Number.isFinite(request.originY) ? request.originY : 0) + maxY))
|
|
69
78
|
};
|
|
79
|
+
};
|
|
80
|
+
const resetViewport = (dimensions = size) => {
|
|
81
|
+
pinch.current = null;
|
|
82
|
+
transform.current = boundViewport(controlledViewport ?? {
|
|
83
|
+
zoom: 1,
|
|
84
|
+
originX: 0,
|
|
85
|
+
originY: 0
|
|
86
|
+
}, dimensions);
|
|
70
87
|
setViewport(transform.current);
|
|
71
|
-
reportViewport(transform.current);
|
|
88
|
+
reportViewport(transform.current, dimensions);
|
|
72
89
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
90
|
+
useLayoutEffect(() => {
|
|
91
|
+
const next = boundViewport(controlledViewport ?? {
|
|
92
|
+
zoom: 1,
|
|
93
|
+
originX: 0,
|
|
94
|
+
originY: 0
|
|
95
|
+
});
|
|
96
|
+
if (next.scale !== transform.current.scale || Math.abs(next.x - transform.current.x) > 1e-6 || Math.abs(next.y - transform.current.y) > 1e-6) resetViewport();
|
|
78
97
|
}, [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
zoomable,
|
|
99
|
+
controlledViewport?.zoom,
|
|
100
|
+
controlledViewport?.originX,
|
|
101
|
+
controlledViewport?.originY
|
|
82
102
|
]);
|
|
103
|
+
useLayoutEffect(() => {
|
|
104
|
+
reportViewport(transform.current);
|
|
105
|
+
}, [size, onViewportChange]);
|
|
83
106
|
const sourcePoint = (x, y) => {
|
|
84
107
|
const current = transform.current;
|
|
85
108
|
return {
|
|
@@ -88,14 +111,18 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
88
111
|
};
|
|
89
112
|
};
|
|
90
113
|
const moveViewport = (event) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
114
|
+
const { touches } = event.nativeEvent;
|
|
115
|
+
const [first, second] = touches;
|
|
116
|
+
const count = touches.length;
|
|
117
|
+
if (!zoomable || !first || count !== 2 && !(navigation && count === 1) || size.width <= 0 || size.height <= 0) {
|
|
93
118
|
pinch.current = null;
|
|
94
119
|
return;
|
|
95
120
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
121
|
+
cancel();
|
|
122
|
+
suppressTap.current = true;
|
|
123
|
+
const x = second ? (first.pageX + second.pageX) / 2 : first.pageX;
|
|
124
|
+
const y = second ? (first.pageY + second.pageY) / 2 : first.pageY;
|
|
125
|
+
const distance = second ? Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY) : 1;
|
|
99
126
|
if (![
|
|
100
127
|
x,
|
|
101
128
|
y,
|
|
@@ -104,8 +131,13 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
104
131
|
pinch.current = null;
|
|
105
132
|
return;
|
|
106
133
|
}
|
|
107
|
-
if (!pinch.current) {
|
|
134
|
+
if (!pinch.current || pinch.current.count !== count) {
|
|
135
|
+
const focalX = Number.isFinite(first.locationX) ? first.locationX + x - first.pageX : x;
|
|
136
|
+
const focalY = Number.isFinite(first.locationY) ? first.locationY + y - first.pageY : y;
|
|
108
137
|
pinch.current = {
|
|
138
|
+
count,
|
|
139
|
+
focalX,
|
|
140
|
+
focalY,
|
|
109
141
|
x,
|
|
110
142
|
y,
|
|
111
143
|
distance,
|
|
@@ -117,12 +149,13 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
117
149
|
}
|
|
118
150
|
const start = pinch.current;
|
|
119
151
|
const scale = Math.max(1, Math.min(4, start.scale * distance / start.distance));
|
|
152
|
+
const ratio = scale / start.scale;
|
|
120
153
|
const maxX = size.width * (scale - 1) / 2;
|
|
121
154
|
const maxY = size.height * (scale - 1) / 2;
|
|
122
155
|
transform.current = {
|
|
123
156
|
scale,
|
|
124
|
-
x: Math.max(-maxX, Math.min(maxX, start.offsetX + x - start.x)),
|
|
125
|
-
y: Math.max(-maxY, Math.min(maxY, start.offsetY + y - start.y))
|
|
157
|
+
x: Math.max(-maxX, Math.min(maxX, start.offsetX * ratio + (start.focalX - size.width / 2) * (1 - ratio) + x - start.x)),
|
|
158
|
+
y: Math.max(-maxY, Math.min(maxY, start.offsetY * ratio + (start.focalY - size.height / 2) * (1 - ratio) + y - start.y))
|
|
126
159
|
};
|
|
127
160
|
reportViewport(transform.current);
|
|
128
161
|
setViewport(transform.current);
|
|
@@ -134,6 +167,7 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
134
167
|
const drawing = onDraw !== void 0;
|
|
135
168
|
const cancel = () => {
|
|
136
169
|
stroke.current = null;
|
|
170
|
+
pressedPoint.current = void 0;
|
|
137
171
|
setLive([]);
|
|
138
172
|
};
|
|
139
173
|
useEffect(() => {
|
|
@@ -142,7 +176,14 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
142
176
|
return () => {
|
|
143
177
|
stroke.current = null;
|
|
144
178
|
};
|
|
145
|
-
}, [
|
|
179
|
+
}, [
|
|
180
|
+
drawing,
|
|
181
|
+
navigation,
|
|
182
|
+
drawingHighlight,
|
|
183
|
+
controlledViewport?.zoom,
|
|
184
|
+
controlledViewport?.originX,
|
|
185
|
+
controlledViewport?.originY
|
|
186
|
+
]);
|
|
146
187
|
const append = (event) => {
|
|
147
188
|
if (!stroke.current || !draw.current) return cancel();
|
|
148
189
|
const { locationX, locationY, touches } = event.nativeEvent;
|
|
@@ -160,14 +201,15 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
160
201
|
setLive([...stroke.current]);
|
|
161
202
|
};
|
|
162
203
|
const place = (event) => {
|
|
163
|
-
if (!onPlace || onDraw || suppressTap.current) return;
|
|
204
|
+
if (!onPlace || onDraw || navigation || suppressTap.current) return;
|
|
164
205
|
const { locationX, locationY } = event.nativeEvent;
|
|
165
|
-
const
|
|
166
|
-
selectionTick();
|
|
167
|
-
onPlace(known ? sourcePoint(locationX, locationY) : {
|
|
206
|
+
const point = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY) ? sourcePoint(locationX, locationY) : pressedPoint.current ?? {
|
|
168
207
|
x: .5,
|
|
169
208
|
y: .5
|
|
170
|
-
}
|
|
209
|
+
};
|
|
210
|
+
pressedPoint.current = void 0;
|
|
211
|
+
selectionTick();
|
|
212
|
+
onPlace(point);
|
|
171
213
|
};
|
|
172
214
|
return /* @__PURE__ */ jsxs(View, {
|
|
173
215
|
style: [
|
|
@@ -175,30 +217,41 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
175
217
|
zoomable && { overflow: "hidden" },
|
|
176
218
|
style
|
|
177
219
|
],
|
|
178
|
-
onStartShouldSetResponderCapture: (event) => zoomable && event.nativeEvent.touches.length > 1,
|
|
179
|
-
onMoveShouldSetResponderCapture: (event) => zoomable && event.nativeEvent.touches.length > 1,
|
|
220
|
+
onStartShouldSetResponderCapture: (event) => zoomable && (navigation || event.nativeEvent.touches.length > 1),
|
|
221
|
+
onMoveShouldSetResponderCapture: (event) => zoomable && (navigation || event.nativeEvent.touches.length > 1),
|
|
180
222
|
onResponderGrant: zoomable ? (event) => {
|
|
181
223
|
cancel();
|
|
224
|
+
navigating.current = true;
|
|
182
225
|
suppressTap.current = true;
|
|
183
226
|
moveViewport(event);
|
|
184
227
|
} : void 0,
|
|
228
|
+
onResponderStart: zoomable ? (event) => {
|
|
229
|
+
cancel();
|
|
230
|
+
moveViewport(event);
|
|
231
|
+
} : void 0,
|
|
232
|
+
onResponderEnd: zoomable ? (event) => {
|
|
233
|
+
cancel();
|
|
234
|
+
moveViewport(event);
|
|
235
|
+
} : void 0,
|
|
185
236
|
onResponderMove: zoomable ? moveViewport : void 0,
|
|
186
237
|
onResponderRelease: zoomable ? () => {
|
|
238
|
+
navigating.current = false;
|
|
187
239
|
pinch.current = null;
|
|
188
240
|
} : void 0,
|
|
189
241
|
onResponderTerminationRequest: () => true,
|
|
190
242
|
onResponderTerminate: zoomable ? () => {
|
|
243
|
+
navigating.current = false;
|
|
191
244
|
pinch.current = null;
|
|
192
245
|
cancel();
|
|
193
246
|
} : void 0,
|
|
194
247
|
onLayout: (event) => {
|
|
195
248
|
cancel();
|
|
196
|
-
|
|
249
|
+
const { width, height } = event.nativeEvent.layout;
|
|
197
250
|
const dimensions = {
|
|
198
|
-
width:
|
|
199
|
-
height:
|
|
251
|
+
width: Number.isFinite(width) ? Math.max(0, width) : 0,
|
|
252
|
+
height: Number.isFinite(height) ? Math.max(0, height) : 0
|
|
200
253
|
};
|
|
201
|
-
|
|
254
|
+
resetViewport(dimensions);
|
|
202
255
|
setSize(dimensions);
|
|
203
256
|
},
|
|
204
257
|
children: [
|
|
@@ -223,9 +276,13 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
223
276
|
}),
|
|
224
277
|
/* @__PURE__ */ jsx(Pressable, {
|
|
225
278
|
accessible: false,
|
|
226
|
-
disabled: !onPlace || !!onDraw,
|
|
227
|
-
onPressIn: () => {
|
|
228
|
-
|
|
279
|
+
disabled: !onPlace || !!onDraw || navigation,
|
|
280
|
+
onPressIn: (event) => {
|
|
281
|
+
pressedPoint.current = void 0;
|
|
282
|
+
if (navigating.current) return;
|
|
283
|
+
suppressTap.current = false;
|
|
284
|
+
const { locationX, locationY } = event.nativeEvent;
|
|
285
|
+
if (size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY)) pressedPoint.current = sourcePoint(locationX, locationY);
|
|
229
286
|
},
|
|
230
287
|
onPress: place,
|
|
231
288
|
children: /* @__PURE__ */ jsxs(View, {
|
|
@@ -242,16 +299,18 @@ function AnnotationSurface({ children, marks, selectedId, onPlace, onDraw, onSel
|
|
|
242
299
|
children: [marks.map((mark) => mark.points && /* @__PURE__ */ jsx(Stroke, {
|
|
243
300
|
points: mark.points,
|
|
244
301
|
width: size.width,
|
|
245
|
-
height: size.height
|
|
302
|
+
height: size.height,
|
|
303
|
+
highlight: mark.highlight
|
|
246
304
|
}, mark.id)), onDraw && /* @__PURE__ */ jsx(Stroke, {
|
|
247
305
|
points: live,
|
|
248
306
|
width: size.width,
|
|
249
|
-
height: size.height
|
|
307
|
+
height: size.height,
|
|
308
|
+
highlight: drawingHighlight
|
|
250
309
|
})]
|
|
251
310
|
})]
|
|
252
311
|
})
|
|
253
312
|
}),
|
|
254
|
-
onDraw && /* @__PURE__ */ jsx(View, {
|
|
313
|
+
onDraw && !navigation && /* @__PURE__ */ jsx(View, {
|
|
255
314
|
accessible: false,
|
|
256
315
|
collapsable: false,
|
|
257
316
|
style: styles.overlay,
|
|
@@ -320,6 +379,11 @@ const styles = StyleSheet.create((theme) => ({
|
|
|
320
379
|
backgroundColor: theme.colors.magenta,
|
|
321
380
|
uni__dependencies: [0]
|
|
322
381
|
},
|
|
382
|
+
highlight: {
|
|
383
|
+
height: 16,
|
|
384
|
+
borderRadius: 8,
|
|
385
|
+
opacity: .3
|
|
386
|
+
},
|
|
323
387
|
hit: {
|
|
324
388
|
position: "absolute",
|
|
325
389
|
width: HIT,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnnotationSurface.mjs","names":["ReactNode","useEffect","useRef","useState","GestureResponderEvent","StyleProp","ViewStyle","StyleSheet","fontOf","selectionTick","AnnotationMark","id","x","y","label","description","points","AnnotationSurfaceProps","children","marks","selectedId","onPlace","point","onDraw","onSelectMark","accessibilityLabel","zoomable","onViewportChange","viewport","width","height","zoom","originX","originY","style","clamp","value","Math","min","max","MAX_POINTS","Point","Stroke","bounded","slice","map","index","previous","every","Number","isFinite","dx","dy","length","hypot","styles","segment","left","top","transform","rotate","atan2","AnnotationSurface","size","setSize","setViewport","scale","pinch","distance","offsetX","offsetY","suppressTap","reportViewport","next","dimensions","resetViewport","current","sourcePoint","moveViewport","event","first","second","nativeEvent","touches","pageX","pageY","start","maxX","maxY","live","setLive","stroke","draw","drawing","undefined","cancel","append","locationX","locationY","last","push","place","known","frame","overflow","layout","overlay","name","actionName","translateX","translateY","mark","some","selected","hit","pin","pinSelected","labelSelected","HIT","PIN","create","theme","position","alignSelf","right","bottom","borderRadius","backgroundColor","colors","magenta","uni__dependencies","marginLeft","marginTop","alignItems","justifyContent","borderWidth","borderColor","bg","color","fontFamily","sans","fontSize","sm","fontWeight","semibold"],"sources":["../../src/components/AnnotationSurface.tsx"],"sourcesContent":["import { type ReactNode, useEffect, useRef, useState } from 'react';\nimport { type GestureResponderEvent, Pressable, type StyleProp, Text, View, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { selectionTick } from '../lib/haptics';\n\n/** One note pinned to the content, with an optional stroke, in fractions (0 to 1) of the surface's width and height. */\nexport interface AnnotationMark {\n id: string;\n x: number;\n y: number;\n /** The short text drawn on the pin, such as \"1\". */\n label: string;\n /** What a screen reader announces for the pin, such as the note text. */\n description?: string;\n /** Optional normalized freehand stroke. At most 128 finite points are rendered. */\n points?: readonly { x: number; y: number }[];\n}\n\nexport interface AnnotationSurfaceProps {\n /** The content being marked up: an image, a page, a block of text. */\n children: ReactNode;\n marks: readonly AnnotationMark[];\n selectedId?: string;\n /** A tap places a note there. Without onPlace or onDraw the surface is read-only. */\n onPlace?: (point: { x: number; y: number }) => void;\n /** Enables drawing instead of tap placement. Receives 2 to 128 finite [0,1] points only on a completed stroke. */\n onDraw?: (points: { x: number; y: number }[]) => void;\n onSelectMark?: (id: string) => void;\n /** What the content is, such as \"hero.png\". */\n accessibilityLabel: string;\n /** Image surfaces can pinch and move with two fingers on both native platforms. */\n zoomable?: boolean;\n /** Viewport dimensions and source-layout-to-viewport transform, all in RN layout units, not raster pixels. */\n onViewportChange?: (viewport: {\n width: number;\n height: number;\n zoom: number;\n originX: number;\n originY: number;\n }) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst clamp = (value: number) => Math.min(1, Math.max(0, value));\nconst MAX_POINTS = 128;\ntype Point = { x: number; y: number };\n\nfunction Stroke({ points, width, height }: { points: readonly Point[]; width: number; height: number }) {\n // Slice before processing so even externally supplied strokes have bounded work.\n const bounded = points.slice(0, MAX_POINTS);\n return bounded.map((point, index) => {\n const previous = bounded[index - 1];\n if (!previous || ![point.x, point.y, previous.x, previous.y].every(Number.isFinite)) return null;\n const x = clamp(previous.x) * width;\n const y = clamp(previous.y) * height;\n const dx = clamp(point.x) * width - x;\n const dy = clamp(point.y) * height - y;\n const length = Math.hypot(dx, dy);\n return (\n <View\n key={index}\n style={[\n styles.segment,\n {\n left: x + dx / 2 - length / 2,\n top: y + dy / 2 - 1.5,\n width: length,\n transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }],\n },\n ]}\n />\n );\n });\n}\n\n/**\n * Tap to pin a note on whatever it wraps. Each pin is its own 44pt button, so a\n * screen reader can reach every note; the surface's own element sits behind the\n * content, so text and controls inside stay readable, and activating it places a\n * note at its centre.\n */\nexport function AnnotationSurface({\n children,\n marks,\n selectedId,\n onPlace,\n onDraw,\n onSelectMark,\n accessibilityLabel,\n zoomable = false,\n onViewportChange,\n style,\n}: AnnotationSurfaceProps) {\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [viewport, setViewport] = useState({ scale: 1, x: 0, y: 0 });\n const transform = useRef(viewport);\n const pinch = useRef<{\n distance: number;\n x: number;\n y: number;\n scale: number;\n offsetX: number;\n offsetY: number;\n } | null>(null);\n const suppressTap = useRef(false);\n const reportViewport = (next: typeof viewport, dimensions = size) => {\n onViewportChange?.({\n ...dimensions,\n zoom: next.scale,\n originX: (dimensions.width * (1 - next.scale)) / 2 + next.x,\n originY: (dimensions.height * (1 - next.scale)) / 2 + next.y,\n });\n };\n const resetViewport = () => {\n pinch.current = null;\n transform.current = { scale: 1, x: 0, y: 0 };\n setViewport(transform.current);\n reportViewport(transform.current);\n };\n useEffect(() => {\n resetViewport();\n }, [zoomable]);\n useEffect(() => {\n reportViewport(viewport);\n }, [viewport, size, onViewportChange]);\n const sourcePoint = (x: number, y: number) => {\n const current = transform.current;\n return {\n x: clamp((x - size.width / 2 - current.x) / current.scale / size.width + 0.5),\n y: clamp((y - size.height / 2 - current.y) / current.scale / size.height + 0.5),\n };\n };\n const moveViewport = (event: GestureResponderEvent) => {\n const [first, second] = event.nativeEvent.touches;\n if (!zoomable || !first || !second || size.width <= 0 || size.height <= 0) {\n pinch.current = null;\n return;\n }\n const x = (first.pageX + second.pageX) / 2;\n const y = (first.pageY + second.pageY) / 2;\n const distance = Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY);\n if (![x, y, distance].every(Number.isFinite) || distance <= 0) {\n pinch.current = null;\n return;\n }\n if (!pinch.current) {\n pinch.current = {\n x,\n y,\n distance,\n scale: transform.current.scale,\n offsetX: transform.current.x,\n offsetY: transform.current.y,\n };\n return;\n }\n const start = pinch.current;\n const scale = Math.max(1, Math.min(4, (start.scale * distance) / start.distance));\n const maxX = (size.width * (scale - 1)) / 2;\n const maxY = (size.height * (scale - 1)) / 2;\n transform.current = {\n scale,\n x: Math.max(-maxX, Math.min(maxX, start.offsetX + x - start.x)),\n y: Math.max(-maxY, Math.min(maxY, start.offsetY + y - start.y)),\n };\n // Publish synchronously so capture can reject a gesture even before React renders it.\n reportViewport(transform.current);\n setViewport(transform.current);\n };\n const [live, setLive] = useState<readonly Point[]>([]);\n const stroke = useRef<Point[] | null>(null);\n // Callers pass inline callbacks, so a new identity is not a new tool: only switching drawing off or on cancels.\n const draw = useRef(onDraw);\n draw.current = onDraw;\n const drawing = onDraw !== undefined;\n const cancel = () => {\n stroke.current = null;\n setLive([]);\n };\n useEffect(() => {\n stroke.current = null;\n setLive([]);\n return () => {\n stroke.current = null;\n };\n }, [drawing]);\n const append = (event: GestureResponderEvent) => {\n if (!stroke.current || !draw.current) return cancel();\n const { locationX, locationY, touches } = event.nativeEvent;\n if (\n touches.length > 1 ||\n ![locationX, locationY, size.width, size.height].every(Number.isFinite) ||\n size.width <= 0 ||\n size.height <= 0\n )\n return cancel();\n const point = sourcePoint(locationX, locationY);\n const last = stroke.current[stroke.current.length - 1];\n if (last?.x === point.x && last.y === point.y) return;\n // Keep the start and a bounded prefix; update the last point once full.\n if (stroke.current.length < MAX_POINTS) stroke.current.push(point);\n else stroke.current[MAX_POINTS - 1] = point;\n setLive([...stroke.current]);\n };\n const place = (event: GestureResponderEvent) => {\n if (!onPlace || onDraw || suppressTap.current) return;\n const { locationX, locationY } = event.nativeEvent;\n const known = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY);\n selectionTick();\n onPlace(known ? sourcePoint(locationX, locationY) : { x: 0.5, y: 0.5 });\n };\n return (\n <View\n style={[styles.frame, zoomable && { overflow: 'hidden' }, style]}\n onStartShouldSetResponderCapture={(event) => zoomable && event.nativeEvent.touches.length > 1}\n onMoveShouldSetResponderCapture={(event) => zoomable && event.nativeEvent.touches.length > 1}\n onResponderGrant={\n zoomable\n ? (event) => {\n cancel();\n suppressTap.current = true;\n moveViewport(event);\n }\n : undefined\n }\n onResponderMove={zoomable ? moveViewport : undefined}\n onResponderRelease={\n zoomable\n ? () => {\n pinch.current = null;\n }\n : undefined\n }\n onResponderTerminationRequest={() => true}\n onResponderTerminate={\n zoomable\n ? () => {\n pinch.current = null;\n cancel();\n }\n : undefined\n }\n onLayout={(event) => {\n cancel();\n resetViewport();\n const dimensions = { width: event.nativeEvent.layout.width, height: event.nativeEvent.layout.height };\n reportViewport(transform.current, dimensions);\n setSize(dimensions);\n }}\n >\n <View\n pointerEvents=\"none\"\n style={styles.overlay}\n accessible\n accessibilityRole={onPlace && !onDraw ? 'button' : 'image'}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={\n onPlace && !onDraw\n ? 'Double tap to add a note here. Use the Note on whole image action to mark the whole image.'\n : undefined\n }\n accessibilityActions={\n onPlace\n ? [...(onDraw ? [] : [{ name: 'activate' }]), { name: 'noteWholeImage', label: 'Note on whole image' }]\n : undefined\n }\n onAccessibilityAction={(event) => {\n const { actionName } = event.nativeEvent;\n if (actionName === 'noteWholeImage' || (actionName === 'activate' && !onDraw)) onPlace?.({ x: 0.5, y: 0.5 });\n }}\n />\n <Pressable\n accessible={false}\n disabled={!onPlace || !!onDraw}\n onPressIn={() => {\n if (!pinch.current) suppressTap.current = false;\n }}\n onPress={place}\n >\n {/* Zoomed content is only a picture, so every touch maps through the frame. At 1x it keeps its own controls, such as PDF page buttons. */}\n <View\n pointerEvents={zoomable && viewport.scale > 1 ? 'none' : 'auto'}\n style={\n zoomable\n ? { transform: [{ translateX: viewport.x }, { translateY: viewport.y }, { scale: viewport.scale }] }\n : undefined\n }\n >\n {children}\n <View pointerEvents=\"none\" accessible={false} style={styles.overlay}>\n {marks.map(\n (mark) =>\n mark.points && <Stroke key={mark.id} points={mark.points} width={size.width} height={size.height} />,\n )}\n {onDraw && <Stroke points={live} width={size.width} height={size.height} />}\n </View>\n </View>\n </Pressable>\n {onDraw && (\n <View\n accessible={false}\n // Only JS responder props, which Fabric may flatten out of the native tree; keep it hit-testable.\n collapsable={false}\n style={styles.overlay}\n onStartShouldSetResponder={() => true}\n onResponderGrant={(event) => {\n stroke.current = [];\n append(event);\n }}\n onResponderStart={(event) => {\n if (event.nativeEvent.touches.length > 1) cancel();\n }}\n onResponderMove={append}\n onResponderRelease={(event) => {\n append(event);\n const points = stroke.current;\n cancel();\n if (\n points &&\n points.length >= 2 &&\n points.some((point) => point.x !== points[0].x || point.y !== points[0].y)\n ) {\n draw.current?.(points);\n }\n }}\n onResponderTerminationRequest={() => true}\n onResponderTerminate={cancel}\n />\n )}\n {marks.map((mark) => {\n const selected = mark.id === selectedId;\n return (\n <Pressable\n key={mark.id}\n accessibilityRole=\"button\"\n accessibilityLabel={`Note ${mark.label}${mark.description ? `: ${mark.description}` : ''}`}\n accessibilityState={{ selected }}\n onPress={() => onSelectMark?.(mark.id)}\n style={[\n styles.hit,\n zoomable\n ? {\n left: size.width / 2 + (clamp(mark.x) - 0.5) * size.width * viewport.scale + viewport.x,\n top: size.height / 2 + (clamp(mark.y) - 0.5) * size.height * viewport.scale + viewport.y,\n }\n : { left: `${clamp(mark.x) * 100}%`, top: `${clamp(mark.y) * 100}%` },\n ]}\n >\n <View style={[styles.pin, selected && styles.pinSelected]}>\n <Text style={[styles.label, selected && styles.labelSelected]}>{mark.label}</Text>\n </View>\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nconst HIT = 44;\nconst PIN = 28;\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { position: 'relative', alignSelf: 'stretch' },\n overlay: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 },\n segment: { position: 'absolute', height: 3, borderRadius: 1.5, backgroundColor: theme.colors.magenta },\n hit: {\n position: 'absolute',\n width: HIT,\n height: HIT,\n marginLeft: -HIT / 2,\n marginTop: -HIT / 2,\n alignItems: 'center',\n justifyContent: 'center',\n },\n pin: {\n width: PIN,\n height: PIN,\n borderRadius: PIN / 2,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderColor: theme.colors.magenta,\n backgroundColor: theme.colors.bg,\n },\n pinSelected: { backgroundColor: theme.colors.magenta },\n label: {\n color: theme.colors.magenta,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n fontWeight: theme.fontWeight.semibold,\n },\n labelSelected: { color: theme.colors.bg },\n}));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnnotationSurface.mjs","names":["ReactNode","useEffect","useLayoutEffect","useRef","useState","GestureResponderEvent","StyleProp","ViewStyle","StyleSheet","fontOf","selectionTick","AnnotationMark","id","x","y","label","description","points","highlight","AnnotationSurfaceProps","children","marks","selectedId","onPlace","point","onDraw","onSelectMark","accessibilityLabel","zoomable","navigation","viewport","zoom","originX","originY","drawingHighlight","onViewportChange","width","height","style","clamp","value","Number","isFinite","Math","min","max","MAX_POINTS","Point","Stroke","thickness","bounded","slice","map","index","previous","every","dx","dy","length","hypot","styles","segment","left","top","transform","rotate","atan2","AnnotationSurface","controlledViewport","size","setSize","setViewport","scale","pinch","count","focalX","focalY","distance","offsetX","offsetY","suppressTap","pressedPoint","undefined","navigating","reportViewport","next","dimensions","boundViewport","request","NonNullable","maxX","maxY","resetViewport","current","abs","sourcePoint","moveViewport","event","touches","nativeEvent","first","second","cancel","pageX","pageY","locationX","locationY","start","ratio","live","setLive","stroke","draw","drawing","append","last","push","place","known","frame","overflow","layout","overlay","name","actionName","translateX","translateY","mark","some","selected","hit","pin","pinSelected","labelSelected","HIT","PIN","create","theme","position","alignSelf","right","bottom","borderRadius","backgroundColor","colors","magenta","uni__dependencies","opacity","marginLeft","marginTop","alignItems","justifyContent","borderWidth","borderColor","bg","color","fontFamily","sans","fontSize","sm","fontWeight","semibold"],"sources":["../../src/components/AnnotationSurface.tsx"],"sourcesContent":["import { type ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { type GestureResponderEvent, Pressable, type StyleProp, Text, View, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { selectionTick } from '../lib/haptics';\n\n/** One note pinned to the content, with an optional stroke, in fractions (0 to 1) of the surface's width and height. */\nexport interface AnnotationMark {\n id: string;\n x: number;\n y: number;\n /** The short text drawn on the pin, such as \"1\". */\n label: string;\n /** What a screen reader announces for the pin, such as the note text. */\n description?: string;\n /** Optional normalized freehand stroke. At most 128 finite points are rendered. */\n points?: readonly { x: number; y: number }[];\n /** A translucent, thick rose stroke over the content. */\n highlight?: boolean;\n}\n\nexport interface AnnotationSurfaceProps {\n /** The content being marked up: an image, a page, a block of text. */\n children: ReactNode;\n marks: readonly AnnotationMark[];\n selectedId?: string;\n /** A tap places a note there. Without onPlace or onDraw the surface is read-only. */\n onPlace?: (point: { x: number; y: number }) => void;\n /** Enables drawing instead of tap placement. Receives 2 to 128 finite [0,1] points only on a completed stroke. */\n onDraw?: (points: { x: number; y: number }[]) => void;\n onSelectMark?: (id: string) => void;\n /** What the content is, such as \"hero.png\". */\n accessibilityLabel: string;\n /** Image surfaces can pinch and move with two fingers on both native platforms. */\n zoomable?: boolean;\n /** Explicit one-finger pan instead of placing or drawing marks. */\n navigation?: boolean;\n /** Controlled transform. Origins are unscaled RN layout positions, not raster pixels. */\n viewport?: { zoom: number; originX: number; originY: number };\n /** Render the in-progress stroke as a highlight. */\n drawingHighlight?: boolean;\n /** Viewport dimensions and source-layout-to-viewport transform, all in RN layout units, not raster pixels. */\n onViewportChange?: (viewport: {\n width: number;\n height: number;\n zoom: number;\n originX: number;\n originY: number;\n }) => void;\n style?: StyleProp<ViewStyle>;\n}\n\nconst clamp = (value: number) => (Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0);\nconst MAX_POINTS = 128;\ntype Point = { x: number; y: number };\n\nfunction Stroke({\n points,\n width,\n height,\n highlight = false,\n}: {\n points: readonly Point[];\n width: number;\n height: number;\n highlight?: boolean;\n}) {\n const thickness = highlight ? 16 : 3;\n // Slice before processing so even externally supplied strokes have bounded work.\n const bounded = points.slice(0, MAX_POINTS);\n return bounded.map((point, index) => {\n const previous = bounded[index - 1];\n if (!previous || ![point.x, point.y, previous.x, previous.y].every(Number.isFinite)) return null;\n const x = clamp(previous.x) * width;\n const y = clamp(previous.y) * height;\n const dx = clamp(point.x) * width - x;\n const dy = clamp(point.y) * height - y;\n const length = Math.hypot(dx, dy);\n return (\n <View\n key={index}\n style={[\n styles.segment,\n highlight && styles.highlight,\n {\n left: x + dx / 2 - length / 2,\n top: y + dy / 2 - thickness / 2,\n width: length,\n transform: [{ rotate: `${Math.atan2(dy, dx)}rad` }],\n },\n ]}\n />\n );\n });\n}\n\n/**\n * Tap to pin a note on whatever it wraps. Each pin is its own 44pt button, so a\n * screen reader can reach every note; the surface's own element sits behind the\n * content, so text and controls inside stay readable, and activating it places a\n * note at its centre.\n */\nexport function AnnotationSurface({\n children,\n marks,\n selectedId,\n onPlace,\n onDraw,\n onSelectMark,\n accessibilityLabel,\n zoomable = false,\n onViewportChange,\n navigation = false,\n viewport: controlledViewport,\n drawingHighlight = false,\n style,\n}: AnnotationSurfaceProps) {\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [viewport, setViewport] = useState({ scale: 1, x: 0, y: 0 });\n const transform = useRef(viewport);\n const pinch = useRef<{\n count: number;\n focalX: number;\n focalY: number;\n distance: number;\n x: number;\n y: number;\n scale: number;\n offsetX: number;\n offsetY: number;\n } | null>(null);\n const suppressTap = useRef(false);\n const pressedPoint = useRef<Point | undefined>(undefined);\n const navigating = useRef(false);\n const reportViewport = (next: typeof viewport, dimensions = size) => {\n onViewportChange?.({\n ...dimensions,\n zoom: next.scale,\n originX: (dimensions.width * (1 - next.scale)) / 2 + next.x,\n originY: (dimensions.height * (1 - next.scale)) / 2 + next.y,\n });\n };\n const boundViewport = (request: NonNullable<AnnotationSurfaceProps['viewport']>, dimensions = size) => {\n const scale = zoomable && Number.isFinite(request.zoom) ? Math.max(1, Math.min(4, request.zoom)) : 1;\n const maxX = (dimensions.width * (scale - 1)) / 2;\n const maxY = (dimensions.height * (scale - 1)) / 2;\n return {\n scale,\n x: Math.max(-maxX, Math.min(maxX, (Number.isFinite(request.originX) ? request.originX : 0) + maxX)),\n y: Math.max(-maxY, Math.min(maxY, (Number.isFinite(request.originY) ? request.originY : 0) + maxY)),\n };\n };\n const resetViewport = (dimensions = size) => {\n pinch.current = null;\n transform.current = boundViewport(controlledViewport ?? { zoom: 1, originX: 0, originY: 0 }, dimensions);\n setViewport(transform.current);\n reportViewport(transform.current, dimensions);\n };\n // Layout effects publish programmatic zoom/Fit/focus before a parent's capture effect.\n useLayoutEffect(() => {\n const next = boundViewport(controlledViewport ?? { zoom: 1, originX: 0, originY: 0 });\n // A controlled parent's gesture echo is not a new navigation command.\n if (\n next.scale !== transform.current.scale ||\n Math.abs(next.x - transform.current.x) > 1e-6 ||\n Math.abs(next.y - transform.current.y) > 1e-6\n )\n resetViewport();\n }, [zoomable, controlledViewport?.zoom, controlledViewport?.originX, controlledViewport?.originY]);\n useLayoutEffect(() => {\n reportViewport(transform.current);\n }, [size, onViewportChange]);\n const sourcePoint = (x: number, y: number) => {\n const current = transform.current;\n return {\n x: clamp((x - size.width / 2 - current.x) / current.scale / size.width + 0.5),\n y: clamp((y - size.height / 2 - current.y) / current.scale / size.height + 0.5),\n };\n };\n const moveViewport = (event: GestureResponderEvent) => {\n const { touches } = event.nativeEvent;\n const [first, second] = touches;\n const count = touches.length;\n if (!zoomable || !first || (count !== 2 && !(navigation && count === 1)) || size.width <= 0 || size.height <= 0) {\n pinch.current = null;\n return;\n }\n cancel();\n suppressTap.current = true;\n const x = second ? (first.pageX + second.pageX) / 2 : first.pageX;\n const y = second ? (first.pageY + second.pageY) / 2 : first.pageY;\n const distance = second ? Math.hypot(first.pageX - second.pageX, first.pageY - second.pageY) : 1;\n if (![x, y, distance].every(Number.isFinite) || distance <= 0) {\n pinch.current = null;\n return;\n }\n if (!pinch.current || pinch.current.count !== count) {\n // Touch locations are frame-local; page positions are used only for movement deltas.\n const focalX = Number.isFinite(first.locationX) ? first.locationX + x - first.pageX : x;\n const focalY = Number.isFinite(first.locationY) ? first.locationY + y - first.pageY : y;\n pinch.current = {\n count,\n focalX,\n focalY,\n x,\n y,\n distance,\n scale: transform.current.scale,\n offsetX: transform.current.x,\n offsetY: transform.current.y,\n };\n return;\n }\n const start = pinch.current;\n const scale = Math.max(1, Math.min(4, (start.scale * distance) / start.distance));\n const ratio = scale / start.scale;\n const maxX = (size.width * (scale - 1)) / 2;\n const maxY = (size.height * (scale - 1)) / 2;\n transform.current = {\n scale,\n x: Math.max(\n -maxX,\n Math.min(maxX, start.offsetX * ratio + (start.focalX - size.width / 2) * (1 - ratio) + x - start.x),\n ),\n y: Math.max(\n -maxY,\n Math.min(maxY, start.offsetY * ratio + (start.focalY - size.height / 2) * (1 - ratio) + y - start.y),\n ),\n };\n // Publish synchronously so capture can reject a gesture even before React renders it.\n reportViewport(transform.current);\n setViewport(transform.current);\n };\n const [live, setLive] = useState<readonly Point[]>([]);\n const stroke = useRef<Point[] | null>(null);\n // Callers pass inline callbacks, so a new identity is not a new tool: only switching drawing off or on cancels.\n const draw = useRef(onDraw);\n draw.current = onDraw;\n const drawing = onDraw !== undefined;\n const cancel = () => {\n stroke.current = null;\n pressedPoint.current = undefined;\n setLive([]);\n };\n useEffect(() => {\n stroke.current = null;\n setLive([]);\n return () => {\n stroke.current = null;\n };\n }, [\n drawing,\n navigation,\n drawingHighlight,\n controlledViewport?.zoom,\n controlledViewport?.originX,\n controlledViewport?.originY,\n ]);\n const append = (event: GestureResponderEvent) => {\n if (!stroke.current || !draw.current) return cancel();\n const { locationX, locationY, touches } = event.nativeEvent;\n if (\n touches.length > 1 ||\n ![locationX, locationY, size.width, size.height].every(Number.isFinite) ||\n size.width <= 0 ||\n size.height <= 0\n )\n return cancel();\n const point = sourcePoint(locationX, locationY);\n const last = stroke.current[stroke.current.length - 1];\n if (last?.x === point.x && last.y === point.y) return;\n // Keep the start and a bounded prefix; update the last point once full.\n if (stroke.current.length < MAX_POINTS) stroke.current.push(point);\n else stroke.current[MAX_POINTS - 1] = point;\n setLive([...stroke.current]);\n };\n const place = (event: GestureResponderEvent) => {\n if (!onPlace || onDraw || navigation || suppressTap.current) return;\n const { locationX, locationY } = event.nativeEvent;\n const known = size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY);\n const point = known ? sourcePoint(locationX, locationY) : (pressedPoint.current ?? { x: 0.5, y: 0.5 });\n pressedPoint.current = undefined;\n selectionTick();\n onPlace(point);\n };\n return (\n <View\n style={[styles.frame, zoomable && { overflow: 'hidden' }, style]}\n onStartShouldSetResponderCapture={(event) => zoomable && (navigation || event.nativeEvent.touches.length > 1)}\n onMoveShouldSetResponderCapture={(event) => zoomable && (navigation || event.nativeEvent.touches.length > 1)}\n onResponderGrant={\n zoomable\n ? (event) => {\n cancel();\n navigating.current = true;\n suppressTap.current = true;\n moveViewport(event);\n }\n : undefined\n }\n onResponderStart={\n zoomable\n ? (event) => {\n cancel();\n moveViewport(event);\n }\n : undefined\n }\n onResponderEnd={\n zoomable\n ? (event) => {\n cancel();\n moveViewport(event);\n }\n : undefined\n }\n onResponderMove={zoomable ? moveViewport : undefined}\n onResponderRelease={\n zoomable\n ? () => {\n navigating.current = false;\n pinch.current = null;\n }\n : undefined\n }\n onResponderTerminationRequest={() => true}\n onResponderTerminate={\n zoomable\n ? () => {\n navigating.current = false;\n pinch.current = null;\n cancel();\n }\n : undefined\n }\n onLayout={(event) => {\n cancel();\n const { width, height } = event.nativeEvent.layout;\n const dimensions = {\n width: Number.isFinite(width) ? Math.max(0, width) : 0,\n height: Number.isFinite(height) ? Math.max(0, height) : 0,\n };\n resetViewport(dimensions);\n setSize(dimensions);\n }}\n >\n <View\n pointerEvents=\"none\"\n style={styles.overlay}\n accessible\n accessibilityRole={onPlace && !onDraw ? 'button' : 'image'}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={\n onPlace && !onDraw\n ? 'Double tap to add a note here. Use the Note on whole image action to mark the whole image.'\n : undefined\n }\n accessibilityActions={\n onPlace\n ? [...(onDraw ? [] : [{ name: 'activate' }]), { name: 'noteWholeImage', label: 'Note on whole image' }]\n : undefined\n }\n onAccessibilityAction={(event) => {\n const { actionName } = event.nativeEvent;\n if (actionName === 'noteWholeImage' || (actionName === 'activate' && !onDraw)) onPlace?.({ x: 0.5, y: 0.5 });\n }}\n />\n <Pressable\n accessible={false}\n disabled={!onPlace || !!onDraw || navigation}\n onPressIn={(event) => {\n pressedPoint.current = undefined;\n if (navigating.current) return;\n suppressTap.current = false;\n // react-native-web onPress is a MouseEvent; its responder start retains the native location.\n const { locationX, locationY } = event.nativeEvent;\n if (size.width > 0 && size.height > 0 && Number.isFinite(locationX) && Number.isFinite(locationY))\n pressedPoint.current = sourcePoint(locationX, locationY);\n }}\n onPress={place}\n >\n {/* Zoomed content is only a picture, so every touch maps through the frame. At 1x it keeps its own controls, such as PDF page buttons. */}\n <View\n pointerEvents={zoomable && viewport.scale > 1 ? 'none' : 'auto'}\n style={\n zoomable\n ? { transform: [{ translateX: viewport.x }, { translateY: viewport.y }, { scale: viewport.scale }] }\n : undefined\n }\n >\n {children}\n <View pointerEvents=\"none\" accessible={false} style={styles.overlay}>\n {marks.map(\n (mark) =>\n mark.points && (\n <Stroke\n key={mark.id}\n points={mark.points}\n width={size.width}\n height={size.height}\n highlight={mark.highlight}\n />\n ),\n )}\n {onDraw && <Stroke points={live} width={size.width} height={size.height} highlight={drawingHighlight} />}\n </View>\n </View>\n </Pressable>\n {onDraw && !navigation && (\n <View\n accessible={false}\n // Only JS responder props, which Fabric may flatten out of the native tree; keep it hit-testable.\n collapsable={false}\n style={styles.overlay}\n onStartShouldSetResponder={() => true}\n onResponderGrant={(event) => {\n stroke.current = [];\n append(event);\n }}\n onResponderStart={(event) => {\n if (event.nativeEvent.touches.length > 1) cancel();\n }}\n onResponderMove={append}\n onResponderRelease={(event) => {\n append(event);\n const points = stroke.current;\n cancel();\n if (\n points &&\n points.length >= 2 &&\n points.some((point) => point.x !== points[0].x || point.y !== points[0].y)\n ) {\n draw.current?.(points);\n }\n }}\n onResponderTerminationRequest={() => true}\n onResponderTerminate={cancel}\n />\n )}\n {marks.map((mark) => {\n const selected = mark.id === selectedId;\n return (\n <Pressable\n key={mark.id}\n accessibilityRole=\"button\"\n accessibilityLabel={`Note ${mark.label}${mark.description ? `: ${mark.description}` : ''}`}\n accessibilityState={{ selected }}\n onPress={() => onSelectMark?.(mark.id)}\n style={[\n styles.hit,\n zoomable\n ? {\n left: size.width / 2 + (clamp(mark.x) - 0.5) * size.width * viewport.scale + viewport.x,\n top: size.height / 2 + (clamp(mark.y) - 0.5) * size.height * viewport.scale + viewport.y,\n }\n : { left: `${clamp(mark.x) * 100}%`, top: `${clamp(mark.y) * 100}%` },\n ]}\n >\n <View style={[styles.pin, selected && styles.pinSelected]}>\n <Text style={[styles.label, selected && styles.labelSelected]}>{mark.label}</Text>\n </View>\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nconst HIT = 44;\nconst PIN = 28;\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { position: 'relative', alignSelf: 'stretch' },\n overlay: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0 },\n segment: { position: 'absolute', height: 3, borderRadius: 1.5, backgroundColor: theme.colors.magenta },\n highlight: { height: 16, borderRadius: 8, opacity: 0.3 },\n hit: {\n position: 'absolute',\n width: HIT,\n height: HIT,\n marginLeft: -HIT / 2,\n marginTop: -HIT / 2,\n alignItems: 'center',\n justifyContent: 'center',\n },\n pin: {\n width: PIN,\n height: PIN,\n borderRadius: PIN / 2,\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderColor: theme.colors.magenta,\n backgroundColor: theme.colors.bg,\n },\n pinSelected: { backgroundColor: theme.colors.magenta },\n label: {\n color: theme.colors.magenta,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n fontWeight: theme.fontWeight.semibold,\n },\n labelSelected: { color: theme.colors.bg },\n}));\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-native-components",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.69",
|
|
4
4
|
"description": "Shared React Native components for Po and its native plugins: the web component library's look and API on native parts, tuned to the Po palette.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
48
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.69"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@agimon-ai/po-build": "0.0.0-alpha.
|
|
52
|
-
"@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.
|
|
51
|
+
"@agimon-ai/po-build": "0.0.0-alpha.69",
|
|
52
|
+
"@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.69",
|
|
53
53
|
"@babel/core": "7.29.7",
|
|
54
54
|
"@expo/vector-icons": "15.1.1",
|
|
55
55
|
"@fontsource-variable/ibm-plex-sans": "5.3.0",
|