@asteby/metacore-runtime-react 29.2.9 → 29.2.10
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/CHANGELOG.md +6 -0
- package/dist/barcode-scanner.d.ts +12 -1
- package/dist/barcode-scanner.d.ts.map +1 -1
- package/dist/barcode-scanner.js +6 -2
- package/package.json +5 -5
- package/src/barcode-scanner.tsx +35 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 29.2.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fd81213: BarcodeScanner: prop `feedback` para mostrar un chip de confirmación (producto agregado / sin coincidencia) **sobre** la cámara — los toasts de Sonner no se ven encima del overlay opaco.
|
|
8
|
+
|
|
3
9
|
## 29.2.9
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -12,6 +12,11 @@ export declare function isCameraScanSupported(): boolean;
|
|
|
12
12
|
* dispositivo no permite audio. Exportado para reutilizarlo con lectores físicos.
|
|
13
13
|
*/
|
|
14
14
|
export declare function useScanBeep(): () => void;
|
|
15
|
+
/** Chip superpuesto sobre la cámara (éxito / error del padre tras resolver el código). */
|
|
16
|
+
export type BarcodeScannerFeedback = {
|
|
17
|
+
message: string;
|
|
18
|
+
tone?: 'success' | 'error' | 'neutral';
|
|
19
|
+
};
|
|
15
20
|
export interface BarcodeScannerProps {
|
|
16
21
|
open: boolean;
|
|
17
22
|
onClose: () => void;
|
|
@@ -35,12 +40,18 @@ export interface BarcodeScannerProps {
|
|
|
35
40
|
* modal, sin taparse con el chrome del host. `fixed` cubre todo el viewport.
|
|
36
41
|
*/
|
|
37
42
|
position?: 'absolute' | 'fixed';
|
|
43
|
+
/**
|
|
44
|
+
* Feedback del padre (ej. "Harina × 2" / "Sin coincidencia") pintado ENCIMA
|
|
45
|
+
* del video. Los toasts de Sonner del host quedan detrás de este overlay
|
|
46
|
+
* opaco en la práctica — este chip es la confirmación visible al escanear.
|
|
47
|
+
*/
|
|
48
|
+
feedback?: BarcodeScannerFeedback | null;
|
|
38
49
|
}
|
|
39
50
|
/**
|
|
40
51
|
* Overlay de cámara a pantalla del contenedor con retícula de puntería. Montalo
|
|
41
52
|
* condicionalmente (`open`) dentro de un ancestro `relative` cuando uses el
|
|
42
53
|
* posicionamiento `absolute` por defecto.
|
|
43
54
|
*/
|
|
44
|
-
export declare function BarcodeScanner({ open, onClose, onDetected, continuous, beepOnScan, title, hint, position, }: BarcodeScannerProps): React.JSX.Element | null;
|
|
55
|
+
export declare function BarcodeScanner({ open, onClose, onDetected, continuous, beepOnScan, title, hint, position, feedback, }: BarcodeScannerProps): React.JSX.Element | null;
|
|
45
56
|
export default BarcodeScanner;
|
|
46
57
|
//# sourceMappingURL=barcode-scanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"barcode-scanner.d.ts","sourceRoot":"","sources":["../src/barcode-scanner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAOtF;AAoDD,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,UASlC,CAAA;AAQD,uFAAuF;AACvF,wBAAgB,qBAAqB,IAAI,OAAO,CAM/C;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,eAyB1B;AAED,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,8EAA8E;IAC9E,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"barcode-scanner.d.ts","sourceRoot":"","sources":["../src/barcode-scanner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAOtF;AAoDD,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,UASlC,CAAA;AAQD,uFAAuF;AACvF,wBAAgB,qBAAqB,IAAI,OAAO,CAM/C;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,eAyB1B;AAED,0FAA0F;AAC1F,MAAM,MAAM,sBAAsB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAA;CACzC,CAAA;AAED,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,8EAA8E;IAC9E,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAA;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC3B,IAAI,EACJ,OAAO,EACP,UAAU,EACV,UAAiB,EACjB,UAAiB,EACjB,KAAyB,EACzB,IAAmC,EACnC,QAAqB,EACrB,QAAe,GAClB,EAAE,mBAAmB,4BAsNrB;AAED,eAAe,cAAc,CAAA"}
|
package/dist/barcode-scanner.js
CHANGED
|
@@ -93,7 +93,7 @@ export function useScanBeep() {
|
|
|
93
93
|
* condicionalmente (`open`) dentro de un ancestro `relative` cuando uses el
|
|
94
94
|
* posicionamiento `absolute` por defecto.
|
|
95
95
|
*/
|
|
96
|
-
export function BarcodeScanner({ open, onClose, onDetected, continuous = true, beepOnScan = true, title = 'Escanear código', hint = 'Apuntá al código de barras', position = 'absolute', }) {
|
|
96
|
+
export function BarcodeScanner({ open, onClose, onDetected, continuous = true, beepOnScan = true, title = 'Escanear código', hint = 'Apuntá al código de barras', position = 'absolute', feedback = null, }) {
|
|
97
97
|
const videoRef = React.useRef(null);
|
|
98
98
|
const streamRef = React.useRef(null);
|
|
99
99
|
const rafRef = React.useRef(null);
|
|
@@ -207,7 +207,11 @@ export function BarcodeScanner({ open, onClose, onDetected, continuous = true, b
|
|
|
207
207
|
const posClass = position === 'fixed' ? 'fixed' : 'absolute';
|
|
208
208
|
return (_jsxs("div", { className: `${posClass} inset-0 z-[100] flex flex-col bg-black`, children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3 text-white", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(ScanLine, { className: "size-5" }), title] }), _jsxs("div", { className: "flex items-center gap-1", children: [torchAvailable && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: torchOn
|
|
209
209
|
? 'bg-amber-400/20 text-amber-300 hover:bg-amber-400/30 hover:text-amber-200'
|
|
210
|
-
: 'text-white hover:bg-white/10 hover:text-white', onClick: () => void toggleTorch(), "aria-label": torchOn ? 'Apagar linterna' : 'Encender linterna', "aria-pressed": torchOn, title: torchOn ? 'Apagar linterna' : 'Encender linterna', children: torchOn ? (_jsx(Flashlight, { className: "size-5" })) : (_jsx(FlashlightOff, { className: "size-5" })) })), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "text-white hover:bg-white/10 hover:text-white", onClick: onClose, "aria-label": "Cerrar esc\u00E1ner", children: _jsx(X, { className: "size-5" }) })] })] }), _jsx("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden", children: error ? (_jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3 px-6 text-center text-white", children: [_jsx(Camera, { className: "size-10 opacity-70" }), _jsx("p", { className: "text-sm leading-relaxed", children: error }), _jsx(Button, { variant: "secondary", onClick: onClose, children: "Entendido" })] })) : (_jsxs(_Fragment, { children: [_jsx("video", { ref: videoRef, className: "h-full w-full object-cover", muted: true, playsInline: true }), _jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "relative h-40 w-72 max-w-[80%] rounded-xl border-2 border-white/80 shadow-[0_0_0_9999px_rgba(0,0,0,0.35)]", children: _jsx("span", { className: "bg-primary absolute inset-x-0 top-1/2 h-0.5 -translate-y-1/2 animate-pulse" }) }) }), _jsx("
|
|
210
|
+
: 'text-white hover:bg-white/10 hover:text-white', onClick: () => void toggleTorch(), "aria-label": torchOn ? 'Apagar linterna' : 'Encender linterna', "aria-pressed": torchOn, title: torchOn ? 'Apagar linterna' : 'Encender linterna', children: torchOn ? (_jsx(Flashlight, { className: "size-5" })) : (_jsx(FlashlightOff, { className: "size-5" })) })), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "text-white hover:bg-white/10 hover:text-white", onClick: onClose, "aria-label": "Cerrar esc\u00E1ner", children: _jsx(X, { className: "size-5" }) })] })] }), _jsx("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden", children: error ? (_jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3 px-6 text-center text-white", children: [_jsx(Camera, { className: "size-10 opacity-70" }), _jsx("p", { className: "text-sm leading-relaxed", children: error }), _jsx(Button, { variant: "secondary", onClick: onClose, children: "Entendido" })] })) : (_jsxs(_Fragment, { children: [_jsx("video", { ref: videoRef, className: "h-full w-full object-cover", muted: true, playsInline: true }), _jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: "relative h-40 w-72 max-w-[80%] rounded-xl border-2 border-white/80 shadow-[0_0_0_9999px_rgba(0,0,0,0.35)]", children: _jsx("span", { className: "bg-primary absolute inset-x-0 top-1/2 h-0.5 -translate-y-1/2 animate-pulse" }) }) }), feedback?.message ? (_jsx("div", { className: "pointer-events-none absolute inset-x-0 top-4 z-10 flex justify-center px-4", "data-testid": "barcode-scanner-feedback", role: "status", "aria-live": "polite", children: _jsx("div", { className: feedback.tone === 'error'
|
|
211
|
+
? 'max-w-[90%] rounded-full bg-red-500 px-4 py-2.5 text-center text-sm font-semibold text-white shadow-lg'
|
|
212
|
+
: feedback.tone === 'neutral'
|
|
213
|
+
? 'max-w-[90%] rounded-full bg-white/90 px-4 py-2.5 text-center text-sm font-semibold text-black shadow-lg'
|
|
214
|
+
: 'max-w-[90%] rounded-full bg-emerald-500 px-4 py-2.5 text-center text-sm font-semibold text-white shadow-lg', children: feedback.message }) })) : null, _jsx("p", { className: "absolute bottom-8 left-0 right-0 px-4 text-center text-sm text-white/90", children: !ready
|
|
211
215
|
? 'Abriendo cámara…'
|
|
212
216
|
: torchAvailable
|
|
213
217
|
? `${hint} · tocá la linterna si está oscuro`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "29.2.
|
|
3
|
+
"version": "29.2.10",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"react-i18next": ">=13",
|
|
38
38
|
"sonner": ">=1.7",
|
|
39
39
|
"zustand": ">=5",
|
|
40
|
-
"@asteby/metacore-
|
|
41
|
-
"@asteby/metacore-
|
|
40
|
+
"@asteby/metacore-sdk": "^3.4.0",
|
|
41
|
+
"@asteby/metacore-ui": "^2.13.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@tanstack/react-router": {
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"typescript": "^6.0.0",
|
|
68
68
|
"vitest": "^4.0.0",
|
|
69
69
|
"zustand": "^5.0.0",
|
|
70
|
-
"@asteby/metacore-
|
|
71
|
-
"@asteby/metacore-
|
|
70
|
+
"@asteby/metacore-sdk": "3.4.0",
|
|
71
|
+
"@asteby/metacore-ui": "2.13.0"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsc -p tsconfig.json",
|
package/src/barcode-scanner.tsx
CHANGED
|
@@ -126,6 +126,12 @@ export function useScanBeep() {
|
|
|
126
126
|
}, [])
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/** Chip superpuesto sobre la cámara (éxito / error del padre tras resolver el código). */
|
|
130
|
+
export type BarcodeScannerFeedback = {
|
|
131
|
+
message: string
|
|
132
|
+
tone?: 'success' | 'error' | 'neutral'
|
|
133
|
+
}
|
|
134
|
+
|
|
129
135
|
export interface BarcodeScannerProps {
|
|
130
136
|
open: boolean
|
|
131
137
|
onClose: () => void
|
|
@@ -149,6 +155,12 @@ export interface BarcodeScannerProps {
|
|
|
149
155
|
* modal, sin taparse con el chrome del host. `fixed` cubre todo el viewport.
|
|
150
156
|
*/
|
|
151
157
|
position?: 'absolute' | 'fixed'
|
|
158
|
+
/**
|
|
159
|
+
* Feedback del padre (ej. "Harina × 2" / "Sin coincidencia") pintado ENCIMA
|
|
160
|
+
* del video. Los toasts de Sonner del host quedan detrás de este overlay
|
|
161
|
+
* opaco en la práctica — este chip es la confirmación visible al escanear.
|
|
162
|
+
*/
|
|
163
|
+
feedback?: BarcodeScannerFeedback | null
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
/**
|
|
@@ -165,6 +177,7 @@ export function BarcodeScanner({
|
|
|
165
177
|
title = 'Escanear código',
|
|
166
178
|
hint = 'Apuntá al código de barras',
|
|
167
179
|
position = 'absolute',
|
|
180
|
+
feedback = null,
|
|
168
181
|
}: BarcodeScannerProps) {
|
|
169
182
|
const videoRef = React.useRef<HTMLVideoElement | null>(null)
|
|
170
183
|
const streamRef = React.useRef<MediaStream | null>(null)
|
|
@@ -345,6 +358,28 @@ export function BarcodeScanner({
|
|
|
345
358
|
<span className="bg-primary absolute inset-x-0 top-1/2 h-0.5 -translate-y-1/2 animate-pulse" />
|
|
346
359
|
</div>
|
|
347
360
|
</div>
|
|
361
|
+
{/* Confirmación del padre ENCIMA del video (Sonner queda
|
|
362
|
+
detrás de este overlay opaco al escanear). */}
|
|
363
|
+
{feedback?.message ? (
|
|
364
|
+
<div
|
|
365
|
+
className="pointer-events-none absolute inset-x-0 top-4 z-10 flex justify-center px-4"
|
|
366
|
+
data-testid="barcode-scanner-feedback"
|
|
367
|
+
role="status"
|
|
368
|
+
aria-live="polite"
|
|
369
|
+
>
|
|
370
|
+
<div
|
|
371
|
+
className={
|
|
372
|
+
feedback.tone === 'error'
|
|
373
|
+
? 'max-w-[90%] rounded-full bg-red-500 px-4 py-2.5 text-center text-sm font-semibold text-white shadow-lg'
|
|
374
|
+
: feedback.tone === 'neutral'
|
|
375
|
+
? 'max-w-[90%] rounded-full bg-white/90 px-4 py-2.5 text-center text-sm font-semibold text-black shadow-lg'
|
|
376
|
+
: 'max-w-[90%] rounded-full bg-emerald-500 px-4 py-2.5 text-center text-sm font-semibold text-white shadow-lg'
|
|
377
|
+
}
|
|
378
|
+
>
|
|
379
|
+
{feedback.message}
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
) : null}
|
|
348
383
|
<p className="absolute bottom-8 left-0 right-0 px-4 text-center text-sm text-white/90">
|
|
349
384
|
{!ready
|
|
350
385
|
? 'Abriendo cámara…'
|