@octane-xplat/camera 0.9.0 → 0.11.0
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 +31 -13
- package/dist/native/CameraView.android.js +8 -7
- package/dist/native/CameraView.ios.js +8 -7
- package/dist/native/octane-client-build.json +1 -1
- package/dist/web/CameraView.web.tsrx.js +18 -14
- package/dist/web/octane-client-build.json +1 -1
- package/package.json +2 -2
- package/src/CameraView.android.tsrx +5 -4
- package/src/CameraView.ios.tsrx +5 -4
- package/src/CameraView.web.tsrx +6 -3
- package/src/CameraView.windows.ts +5 -0
- package/src/props.ts +3 -1
- package/types/props.d.ts +2 -1
package/README.md
CHANGED
|
@@ -1,31 +1,49 @@
|
|
|
1
1
|
# `@octane-xplat/camera`
|
|
2
2
|
|
|
3
|
-
Live camera preview for Octane xplat apps: `getUserMedia` video on web,
|
|
4
|
-
`AVCaptureSession` on iOS, CameraX on Android.
|
|
5
|
-
|
|
6
3
|
```sh
|
|
7
4
|
pnpm add @octane-xplat/camera
|
|
8
5
|
```
|
|
9
6
|
|
|
7
|
+
Live camera preview for Octane xplat apps: `getUserMedia` video on web,
|
|
8
|
+
`AVCaptureSession` on iOS, CameraX on Android.
|
|
9
|
+
|
|
10
10
|
```tsx
|
|
11
11
|
import { CameraView } from '@octane-xplat/camera'
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
export function Preview() {
|
|
14
|
+
return (
|
|
15
|
+
<CameraView
|
|
16
|
+
facing="back"
|
|
17
|
+
active
|
|
18
|
+
onReady={() => console.log('Preview running')}
|
|
19
|
+
onError={(error) => console.log(error.message)}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
20
23
|
```
|
|
21
24
|
|
|
22
25
|
This is the preview surface only — for one-shot still capture and library
|
|
23
26
|
picking use [`@octane-xplat/media`](../media/README.md). Permissions are
|
|
24
27
|
requested when the session starts; `onError` is how a denial shows up.
|
|
25
|
-
`
|
|
28
|
+
`ref` hands you the platform surface (`HTMLVideoElement` on web) for
|
|
26
29
|
controls the shared props don't cover, and the `ios`/`android`/`web`
|
|
27
30
|
escape props apply after the shared props.
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
```tsx
|
|
33
|
+
// Preview.web.tsx — browser properties belong in this platform file.
|
|
34
|
+
import { CameraView } from '@octane-xplat/camera'
|
|
35
|
+
|
|
36
|
+
export function Preview() {
|
|
37
|
+
return (
|
|
38
|
+
<CameraView
|
|
39
|
+
web={{ playsInline: true }}
|
|
40
|
+
ref={(handle) => console.log(handle.native)}
|
|
41
|
+
onError={(error) => console.log(error.message)}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Guide: [Building screens](../../docs/app/primitives.md) (leaf components);
|
|
48
|
+
component index: [`docs/app/components.md`](../../docs/app/components.md).
|
|
31
49
|
Exercised by [`CameraDemo`](../demos/src/CameraDemo.tsrx).
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { cameraHardwarePresent, ensureCameraPermission } from "./camera-permission.mobile.js";
|
|
2
2
|
import { applyEscapeProps, layoutChildProps, nativeAccessibilityRole, nativeAccessibilityState } from "./layout-child.js";
|
|
3
3
|
import { cx } from "./cx.mobile.js";
|
|
4
|
-
import { defineUniversalComponent, hookSlots, registerElement, universalPlan, universalProps, universalValue, useCallback, useEffect, useRef } from "@nativescript-community/octane";
|
|
4
|
+
import { defineUniversalComponent, hookSlots, registerElement, universalPlan, universalProps, universalValue, useCallback, useEffect, useImperativeHandle, useRef } from "@nativescript-community/octane";
|
|
5
5
|
import { Application, View } from "@nativescript/core";
|
|
6
6
|
//#region src/CameraView.android.tsrx
|
|
7
|
-
var _hs$ = /* @__PURE__ */ hookSlots(
|
|
7
|
+
var _hs$ = /* @__PURE__ */ hookSlots(5);
|
|
8
8
|
var _h$0 = /* @__PURE__ */ Symbol(_hs$);
|
|
9
9
|
var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
|
|
10
10
|
var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
|
|
11
11
|
var _h$3 = /* @__PURE__ */ Symbol(_hs$ + 3);
|
|
12
|
+
var _h$4 = /* @__PURE__ */ Symbol(_hs$ + 4);
|
|
12
13
|
var __octaneUniversalPlan0 = universalPlan("nativescript", {
|
|
13
14
|
"kind": "host",
|
|
14
15
|
"type": "gridlayout",
|
|
@@ -34,9 +35,10 @@ var CameraView = defineUniversalComponent("nativescript", function CameraView(pr
|
|
|
34
35
|
const host = useRef(null, _h$0);
|
|
35
36
|
const active = props.active !== false;
|
|
36
37
|
const facing = props.facing ?? "back";
|
|
38
|
+
useImperativeHandle(props.ref, () => ({ native: host.current.android }), [], _h$1);
|
|
37
39
|
const bindHost = useCallback((view) => {
|
|
38
40
|
host.current = view;
|
|
39
|
-
}, [], _h$
|
|
41
|
+
}, [], _h$2);
|
|
40
42
|
useEffect(() => {
|
|
41
43
|
const nativeView = host.current?.android;
|
|
42
44
|
if (!nativeView) return;
|
|
@@ -87,13 +89,12 @@ var CameraView = defineUniversalComponent("nativescript", function CameraView(pr
|
|
|
87
89
|
provider.unbind(preview);
|
|
88
90
|
} catch {}
|
|
89
91
|
};
|
|
90
|
-
}, [active, facing], _h$
|
|
92
|
+
}, [active, facing], _h$3);
|
|
91
93
|
useEffect(() => {
|
|
92
94
|
const view = host.current;
|
|
93
95
|
if (!view) return;
|
|
94
96
|
applyEscapeProps(view.android, props);
|
|
95
|
-
|
|
96
|
-
}, [props.bind, props.android], _h$3);
|
|
97
|
+
}, [props.ref, props.android], _h$4);
|
|
97
98
|
return universalValue(__octaneUniversalPlan0, [universalProps([
|
|
98
99
|
[
|
|
99
100
|
"set",
|
|
@@ -113,7 +114,7 @@ var CameraView = defineUniversalComponent("nativescript", function CameraView(pr
|
|
|
113
114
|
[
|
|
114
115
|
"set",
|
|
115
116
|
"className",
|
|
116
|
-
cx("vx-
|
|
117
|
+
cx("vx-layers", "vx-cameraview", props.className)
|
|
117
118
|
],
|
|
118
119
|
[
|
|
119
120
|
"set",
|
|
@@ -2,13 +2,14 @@ import { cameraHardwarePresent, ensureCameraPermission } from "./camera-permissi
|
|
|
2
2
|
import { startCameraPreview } from "./camera-session.ios.js";
|
|
3
3
|
import { applyEscapeProps, layoutChildProps, nativeAccessibilityRole, nativeAccessibilityState } from "./layout-child.js";
|
|
4
4
|
import { cx } from "./cx.mobile.js";
|
|
5
|
-
import { defineUniversalComponent, hookSlots, universalPlan, universalProps, universalValue, useCallback, useEffect, useRef } from "@nativescript-community/octane";
|
|
5
|
+
import { defineUniversalComponent, hookSlots, universalPlan, universalProps, universalValue, useCallback, useEffect, useImperativeHandle, useRef } from "@nativescript-community/octane";
|
|
6
6
|
//#region src/CameraView.ios.tsrx
|
|
7
|
-
var _hs$ = /* @__PURE__ */ hookSlots(
|
|
7
|
+
var _hs$ = /* @__PURE__ */ hookSlots(5);
|
|
8
8
|
var _h$0 = /* @__PURE__ */ Symbol(_hs$);
|
|
9
9
|
var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
|
|
10
10
|
var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
|
|
11
11
|
var _h$3 = /* @__PURE__ */ Symbol(_hs$ + 3);
|
|
12
|
+
var _h$4 = /* @__PURE__ */ Symbol(_hs$ + 4);
|
|
12
13
|
var __octaneUniversalPlan0 = universalPlan("nativescript", {
|
|
13
14
|
"kind": "host",
|
|
14
15
|
"type": "gridlayout",
|
|
@@ -18,9 +19,10 @@ var CameraView = defineUniversalComponent("nativescript", function CameraView(pr
|
|
|
18
19
|
const host = useRef(null, _h$0);
|
|
19
20
|
const active = props.active !== false;
|
|
20
21
|
const facing = props.facing ?? "back";
|
|
22
|
+
useImperativeHandle(props.ref, () => ({ native: host.current.ios ?? host.current.nativeViewProtected }), [], _h$1);
|
|
21
23
|
const bindHost = useCallback((view) => {
|
|
22
24
|
host.current = view;
|
|
23
|
-
}, [], _h$
|
|
25
|
+
}, [], _h$2);
|
|
24
26
|
useEffect(() => {
|
|
25
27
|
const view = host.current;
|
|
26
28
|
if (!view || !active) return;
|
|
@@ -47,13 +49,12 @@ var CameraView = defineUniversalComponent("nativescript", function CameraView(pr
|
|
|
47
49
|
disposed = true;
|
|
48
50
|
stopPreview?.();
|
|
49
51
|
};
|
|
50
|
-
}, [active, facing], _h$
|
|
52
|
+
}, [active, facing], _h$3);
|
|
51
53
|
useEffect(() => {
|
|
52
54
|
const view = host.current;
|
|
53
55
|
if (!view) return;
|
|
54
56
|
applyEscapeProps(view.ios ?? view.nativeViewProtected, props);
|
|
55
|
-
|
|
56
|
-
}, [props.bind, props.ios], _h$3);
|
|
57
|
+
}, [props.ref, props.ios], _h$4);
|
|
57
58
|
return universalValue(__octaneUniversalPlan0, [universalProps([
|
|
58
59
|
[
|
|
59
60
|
"set",
|
|
@@ -78,7 +79,7 @@ var CameraView = defineUniversalComponent("nativescript", function CameraView(pr
|
|
|
78
79
|
[
|
|
79
80
|
"set",
|
|
80
81
|
"className",
|
|
81
|
-
cx("vx-
|
|
82
|
+
cx("vx-layers", "vx-cameraview", props.className)
|
|
82
83
|
],
|
|
83
84
|
[
|
|
84
85
|
"set",
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { applyEscapeProps, layoutChildProps } from "./layout-child.web.js";
|
|
2
|
-
import { __s, bag15, bag5, child, clone, delegateEvents, evt2e, evt2u, hookSlots, ifBlock, markDangerouslySetInnerHTMLChildren, queueRefAttach, queueRefDetach, setStyleProperty, snapshotSpread, template, useEffect, useRef } from "octane";
|
|
2
|
+
import { __s, bag15, bag5, child, clone, delegateEvents, evt2e, evt2u, hookSlots, ifBlock, markDangerouslySetInnerHTMLChildren, queueRefAttach, queueRefDetach, setStyleProperty, snapshotSpread, template, useEffect, useImperativeHandle, useRef } from "octane";
|
|
3
3
|
import { bindSignalHostPropSources, enableSignalBindings, replaceRef, setAttributeIfChanged } from "octane/internal/client";
|
|
4
4
|
//#region src/CameraView.web.tsrx
|
|
5
5
|
enableSignalBindings(1, true);
|
|
6
6
|
delegateEvents(["playing"]);
|
|
7
7
|
var _t$0 = /* @__PURE__ */ template("<video autoplay=\"\" playsinline=\"\" style=\"display: block; width: 100%; height: 100%; object-fit: cover;\"></video>");
|
|
8
8
|
var _t$1 = /* @__PURE__ */ template("<div><!></div>");
|
|
9
|
-
var _hs$ = /* @__PURE__ */ hookSlots(
|
|
9
|
+
var _hs$ = /* @__PURE__ */ hookSlots(5);
|
|
10
10
|
var __event = (__eventArg, readyFired, props) => {
|
|
11
11
|
if (readyFired.current) return;
|
|
12
12
|
readyFired.current = true;
|
|
@@ -19,18 +19,19 @@ var _rm$1 = [
|
|
|
19
19
|
];
|
|
20
20
|
function __then$0(__props, __s, __extra) {
|
|
21
21
|
const facing = __extra[0];
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
22
|
+
const hostRef = __extra[1];
|
|
23
|
+
const props = __extra[2];
|
|
24
|
+
const readyFired = __extra[3];
|
|
25
|
+
const video = __extra[4];
|
|
25
26
|
let _b = __s.slots[0];
|
|
26
27
|
if (_b === void 0) {
|
|
27
28
|
let _m0, _m1, _m4;
|
|
28
29
|
const _root = clone(_t$0, void 0, "||");
|
|
29
30
|
queueRefAttach(__s, _m1 = (element) => {
|
|
31
|
+
hostRef.current = element;
|
|
30
32
|
video.current = element;
|
|
31
33
|
if (!element) return;
|
|
32
34
|
applyEscapeProps(element, props);
|
|
33
|
-
props.bind?.({ native: element });
|
|
34
35
|
}, _m0 = _root);
|
|
35
36
|
(__s.cleanups ??= []).push(() => _b && queueRefDetach(_b.b, _b.a));
|
|
36
37
|
_m4 = evt2e(_root, "$$playing", __event, readyFired, props);
|
|
@@ -39,10 +40,10 @@ function __then$0(__props, __s, __extra) {
|
|
|
39
40
|
} else {
|
|
40
41
|
{
|
|
41
42
|
const _r = (element) => {
|
|
43
|
+
hostRef.current = element;
|
|
42
44
|
video.current = element;
|
|
43
45
|
if (!element) return;
|
|
44
46
|
applyEscapeProps(element, props);
|
|
45
|
-
props.bind?.({ native: element });
|
|
46
47
|
};
|
|
47
48
|
if (_r !== _b.b) _b.b = replaceRef(__s, _b.b, _r, _b.a);
|
|
48
49
|
}
|
|
@@ -63,10 +64,11 @@ var _rm$3 = [
|
|
|
63
64
|
"m"
|
|
64
65
|
];
|
|
65
66
|
var CameraView = /* @__PURE__ */ __s(function CameraView(props, __s, __extra) {
|
|
66
|
-
const
|
|
67
|
+
const hostRef = useRef(null, _hs$ + 0);
|
|
68
|
+
const video = useRef(null, _hs$ + 1);
|
|
67
69
|
const facing = props.facing ?? "back";
|
|
68
70
|
const active = props.active !== false;
|
|
69
|
-
const readyFired = useRef(false, _hs$ +
|
|
71
|
+
const readyFired = useRef(false, _hs$ + 2);
|
|
70
72
|
useEffect(() => {
|
|
71
73
|
const element = video.current;
|
|
72
74
|
if (!element || !active) return;
|
|
@@ -97,14 +99,15 @@ var CameraView = /* @__PURE__ */ __s(function CameraView(props, __s, __extra) {
|
|
|
97
99
|
element.srcObject = null;
|
|
98
100
|
if (stream) for (const track of stream.getTracks()) track.stop();
|
|
99
101
|
};
|
|
100
|
-
}, [active, facing], _hs$ +
|
|
102
|
+
}, [active, facing], _hs$ + 3);
|
|
103
|
+
useImperativeHandle(props.ref, () => ({ native: hostRef.current }), void 0, _hs$ + 4);
|
|
101
104
|
let _b = __s.slots[0];
|
|
102
105
|
if (_b === void 0) {
|
|
103
106
|
let _m0, _m1, _m2, _m3, _m4, _m5, _m6, _m7, _m8, _m9, _m10, _m11, _m12, _m13, _m14;
|
|
104
107
|
const _root = clone(_t$1);
|
|
105
108
|
_m0 = props.id;
|
|
106
109
|
_m1 = [
|
|
107
|
-
"vx-
|
|
110
|
+
"vx-layers",
|
|
108
111
|
"vx-cameraview",
|
|
109
112
|
props.className
|
|
110
113
|
];
|
|
@@ -181,7 +184,7 @@ var CameraView = /* @__PURE__ */ __s(function CameraView(props, __s, __extra) {
|
|
|
181
184
|
"aria-live",
|
|
182
185
|
_m11
|
|
183
186
|
]
|
|
184
|
-
], "b:
|
|
187
|
+
], "b:25f1aaa9", true);
|
|
185
188
|
markDangerouslySetInnerHTMLChildren(_root);
|
|
186
189
|
_m14 = child(_root);
|
|
187
190
|
_b = bag15(__s, _root, _m0, _m1, _m2, _m3, _m4, _m5, _m6, _m7, _m8, _m9, _m10, _m11, _m12, _m13, _m14);
|
|
@@ -193,7 +196,7 @@ var CameraView = /* @__PURE__ */ __s(function CameraView(props, __s, __extra) {
|
|
|
193
196
|
}
|
|
194
197
|
{
|
|
195
198
|
const _v = [
|
|
196
|
-
"vx-
|
|
199
|
+
"vx-layers",
|
|
197
200
|
"vx-cameraview",
|
|
198
201
|
props.className
|
|
199
202
|
];
|
|
@@ -298,12 +301,13 @@ var CameraView = /* @__PURE__ */ __s(function CameraView(props, __s, __extra) {
|
|
|
298
301
|
"aria-live",
|
|
299
302
|
_b.l
|
|
300
303
|
]
|
|
301
|
-
], "b:
|
|
304
|
+
], "b:25f1aaa9", true);
|
|
302
305
|
}
|
|
303
306
|
{
|
|
304
307
|
const __ifCondition = active;
|
|
305
308
|
ifBlock(__s, 1, _b.m, __ifCondition, __then$0, null, _b.o, __ifCondition ? [
|
|
306
309
|
facing,
|
|
310
|
+
hostRef,
|
|
307
311
|
props,
|
|
308
312
|
readyFired,
|
|
309
313
|
video
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octane-xplat/camera",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Live camera preview for Octane xplat — web getUserMedia + native capture (AVCapture / CameraX)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"android",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@nativescript/core": "9.1.2",
|
|
46
46
|
"@octanejs/vite-plugin": "0.1.61",
|
|
47
47
|
"octane": "0.6.3",
|
|
48
|
-
"tsrx-typegen": "0.2.
|
|
48
|
+
"tsrx-typegen": "0.2.2",
|
|
49
49
|
"typescript": "5.9.3",
|
|
50
50
|
"vite": "8.3.0"
|
|
51
51
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsxImportSource @nativescript-community/octane */
|
|
2
|
-
import { useCallback, useEffect, useRef } from 'octane'
|
|
2
|
+
import { useImperativeHandle, useCallback, useEffect, useRef } from 'octane'
|
|
3
3
|
import { Application, View } from '@nativescript/core'
|
|
4
4
|
import { registerElement } from '@nativescript-community/octane'
|
|
5
5
|
import type { Attributes } from '@nativescript-community/octane/intrinsics'
|
|
@@ -28,6 +28,8 @@ export function CameraView(props: CameraViewProps) @{
|
|
|
28
28
|
const host = useRef<any>(null)
|
|
29
29
|
const active = props.active !== false
|
|
30
30
|
const facing = props.facing ?? 'back'
|
|
31
|
+
useImperativeHandle(props.ref, () => ({ native: host.current.android }), []);
|
|
32
|
+
|
|
31
33
|
const bindHost = useCallback((view: any) => {
|
|
32
34
|
host.current = view
|
|
33
35
|
}, [])
|
|
@@ -102,14 +104,13 @@ export function CameraView(props: CameraViewProps) @{
|
|
|
102
104
|
const view = host.current
|
|
103
105
|
if (!view) return
|
|
104
106
|
applyEscapeProps(view.android, props)
|
|
105
|
-
|
|
106
|
-
}, [props.bind, props.android])
|
|
107
|
+
}, [props.ref, props.android])
|
|
107
108
|
|
|
108
109
|
<gridlayout
|
|
109
110
|
id={props.id}
|
|
110
111
|
rows="*"
|
|
111
112
|
columns="*"
|
|
112
|
-
className={cx('vx-
|
|
113
|
+
className={cx('vx-layers', 'vx-cameraview', props.className)}
|
|
113
114
|
style={{ backgroundColor: '#000', ...props.style }}
|
|
114
115
|
{...layoutChildProps(props)}
|
|
115
116
|
accessible={props.accessible}
|
package/src/CameraView.ios.tsrx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsxImportSource @nativescript-community/octane */
|
|
2
|
-
import { useCallback, useEffect, useRef } from 'octane'
|
|
2
|
+
import { useImperativeHandle, useCallback, useEffect, useRef } from 'octane'
|
|
3
3
|
import type { CameraViewProps } from './props'
|
|
4
4
|
import { cameraHardwarePresent, ensureCameraPermission } from './camera-permission.mobile'
|
|
5
5
|
import { startCameraPreview } from './camera-session.ios'
|
|
@@ -11,6 +11,8 @@ export function CameraView(props: CameraViewProps) @{
|
|
|
11
11
|
const host = useRef<any>(null)
|
|
12
12
|
const active = props.active !== false
|
|
13
13
|
const facing = props.facing ?? 'back'
|
|
14
|
+
useImperativeHandle(props.ref, () => ({ native: host.current.ios ?? host.current.nativeViewProtected }), []);
|
|
15
|
+
|
|
14
16
|
const bindHost = useCallback((view: any) => {
|
|
15
17
|
host.current = view
|
|
16
18
|
}, [])
|
|
@@ -51,15 +53,14 @@ export function CameraView(props: CameraViewProps) @{
|
|
|
51
53
|
const view = host.current
|
|
52
54
|
if (!view) return
|
|
53
55
|
applyEscapeProps(view.ios ?? view.nativeViewProtected, props)
|
|
54
|
-
|
|
55
|
-
}, [props.bind, props.ios])
|
|
56
|
+
}, [props.ref, props.ios])
|
|
56
57
|
|
|
57
58
|
<gridlayout
|
|
58
59
|
ref={bindHost}
|
|
59
60
|
id={props.id}
|
|
60
61
|
rows="*"
|
|
61
62
|
columns="*"
|
|
62
|
-
className={cx('vx-
|
|
63
|
+
className={cx('vx-layers', 'vx-cameraview', props.className)}
|
|
63
64
|
style={{ backgroundColor: '#000', ...props.style }}
|
|
64
65
|
{...layoutChildProps(props)}
|
|
65
66
|
accessible={props.accessible}
|
package/src/CameraView.web.tsrx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'octane'
|
|
1
|
+
import { useImperativeHandle, useEffect, useRef } from 'octane'
|
|
2
2
|
import type { CameraViewProps } from './props'
|
|
3
3
|
import { applyEscapeProps, layoutChildProps } from './layout-child.web'
|
|
4
4
|
|
|
5
5
|
/** Live web preview backed by getUserMedia. */
|
|
6
6
|
export function CameraView(props: CameraViewProps) @{
|
|
7
|
+
const hostRef = useRef<any>(null);
|
|
7
8
|
const video = useRef<HTMLVideoElement | null>(null)
|
|
8
9
|
const facing = props.facing ?? 'back'
|
|
9
10
|
const active = props.active !== false
|
|
@@ -52,9 +53,11 @@ export function CameraView(props: CameraViewProps) @{
|
|
|
52
53
|
}
|
|
53
54
|
}, [active, facing])
|
|
54
55
|
|
|
56
|
+
useImperativeHandle(props.ref, () => ({ native: hostRef.current }), undefined);
|
|
57
|
+
|
|
55
58
|
<div
|
|
56
59
|
id={props.id}
|
|
57
|
-
className={['vx-
|
|
60
|
+
className={['vx-layers', 'vx-cameraview', props.className]}
|
|
58
61
|
{...layoutChildProps(props, {
|
|
59
62
|
position: 'relative',
|
|
60
63
|
overflow: 'hidden',
|
|
@@ -74,10 +77,10 @@ export function CameraView(props: CameraViewProps) @{
|
|
|
74
77
|
@if (active) {
|
|
75
78
|
<video
|
|
76
79
|
ref={(element: HTMLVideoElement | null) => {
|
|
80
|
+
hostRef.current = element;
|
|
77
81
|
video.current = element
|
|
78
82
|
if (!element) return
|
|
79
83
|
applyEscapeProps(element, props)
|
|
80
|
-
props.bind?.({ native: element })
|
|
81
84
|
}}
|
|
82
85
|
muted={true}
|
|
83
86
|
autoplay={true}
|
package/src/props.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Octane } from 'octane/jsx-runtime'
|
|
2
|
+
|
|
1
3
|
export type CameraAccessibilityRole =
|
|
2
4
|
| 'button'
|
|
3
5
|
| 'link'
|
|
@@ -54,7 +56,7 @@ export interface CameraViewProps {
|
|
|
54
56
|
onReady?: () => void
|
|
55
57
|
onError?: (error: { message?: string }) => void
|
|
56
58
|
/** Native preview surface for platform-specific controls. */
|
|
57
|
-
|
|
59
|
+
ref?: Octane.Ref<CameraViewHandle>
|
|
58
60
|
/** Platform-specific properties applied to the preview surface. */
|
|
59
61
|
ios?: Record<string, any>
|
|
60
62
|
android?: Record<string, any>
|
package/types/props.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Octane } from 'octane/jsx-runtime';
|
|
1
2
|
export type CameraAccessibilityRole = 'button' | 'link' | 'search' | 'image' | 'heading' | 'adjustable' | 'summary' | 'text' | 'none' | 'progressbar' | 'checkbox' | 'switch' | 'radio' | 'spinbutton' | 'tab';
|
|
2
3
|
export interface CameraViewHandle {
|
|
3
4
|
/** The native preview surface (`HTMLVideoElement` on web). */
|
|
@@ -39,7 +40,7 @@ export interface CameraViewProps {
|
|
|
39
40
|
message?: string;
|
|
40
41
|
}) => void;
|
|
41
42
|
/** Native preview surface for platform-specific controls. */
|
|
42
|
-
|
|
43
|
+
ref?: Octane.Ref<CameraViewHandle>;
|
|
43
44
|
/** Platform-specific properties applied to the preview surface. */
|
|
44
45
|
ios?: Record<string, any>;
|
|
45
46
|
android?: Record<string, any>;
|