@grazziotin/react-components 1.0.3 → 1.0.4
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 +34 -24
- package/dist/150.index.js +1 -0
- package/dist/815.index.js +1 -0
- package/dist/908.index.js +2 -0
- package/dist/908.index.js.LICENSE.txt +9 -0
- package/dist/988.index.js +1 -0
- package/dist/assets/fonts/Montserrat-Black.ttf +0 -0
- package/dist/assets/fonts/Montserrat-BlackItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-Bold.ttf +0 -0
- package/dist/assets/fonts/Montserrat-BoldItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-ExtraBold.ttf +0 -0
- package/dist/assets/fonts/Montserrat-ExtraBoldItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-ExtraLight.ttf +0 -0
- package/dist/assets/fonts/Montserrat-ExtraLightItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-Italic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-Light.ttf +0 -0
- package/dist/assets/fonts/Montserrat-LightItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-Medium.ttf +0 -0
- package/dist/assets/fonts/Montserrat-MediumItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-Regular.ttf +0 -0
- package/dist/assets/fonts/Montserrat-SemiBold.ttf +0 -0
- package/dist/assets/fonts/Montserrat-SemiBoldItalic.ttf +0 -0
- package/dist/assets/fonts/Montserrat-Thin.ttf +0 -0
- package/dist/assets/fonts/Montserrat-ThinItalic.ttf +0 -0
- package/dist/components/Inputs/Input/index.d.ts +39 -18
- package/dist/components/Inputs/Input/index.js +122 -28
- package/dist/components/Inputs/Input/utils/input-styled.d.ts +4 -0
- package/dist/components/Inputs/Input/utils/input-styled.js +63 -0
- package/dist/components/Inputs/Input/utils/interface.d.ts +21 -0
- package/dist/components/Inputs/Input/utils/interface.js +1 -0
- package/dist/components/Inputs/Input/utils/mask-custom.d.ts +4 -0
- package/dist/components/Inputs/Input/utils/mask-custom.js +30 -0
- package/dist/components/Inputs/input-select/index.d.ts +42 -0
- package/dist/components/Inputs/input-select/index.js +84 -0
- package/dist/components/Inputs/input-select/utils/interface.d.ts +43 -0
- package/dist/components/Inputs/input-select/utils/interface.js +1 -0
- package/dist/components/Loader/index.d.ts +5 -0
- package/dist/components/Loader/index.js +7 -0
- package/dist/components/barcode/BarcodeScanner.d.ts +26 -0
- package/dist/components/barcode/barcode-scanner.d.ts +26 -0
- package/dist/components/barcode/barcode-scanner.js +433 -0
- package/dist/components/barcode/index.d.ts +36 -0
- package/dist/components/barcode/index.js +65 -0
- package/dist/components/barcode/utils/constants.d.ts +139 -0
- package/dist/components/barcode/utils/constants.js +224 -0
- package/dist/components/barcode/utils/interface.d.ts +50 -0
- package/dist/components/barcode/utils/interface.js +1 -0
- package/dist/components/button/button-excel/index.d.ts +32 -0
- package/dist/components/button/button-excel/index.js +143 -0
- package/dist/components/button/index.d.ts +21 -0
- package/dist/components/button/index.js +101 -0
- package/dist/components/card/index.d.ts +21 -0
- package/dist/components/card/index.js +48 -0
- package/dist/components/datatable/index.d.ts +8 -0
- package/dist/components/datatable/index.js +103 -0
- package/dist/components/datatable/utils/constants.d.ts +8 -0
- package/dist/components/datatable/utils/constants.js +42 -0
- package/dist/components/filtro/components/card.d.ts +24 -0
- package/dist/components/filtro/components/card.js +49 -0
- package/dist/components/filtro/components/filtros.d.ts +19 -0
- package/dist/components/filtro/components/filtros.js +77 -0
- package/dist/components/filtro/index.d.ts +28 -0
- package/dist/components/filtro/index.js +38 -0
- package/dist/components/filtro/utils/interface.d.ts +105 -0
- package/dist/components/filtro/utils/interface.js +1 -0
- package/dist/components/icons/index.d.ts +21 -0
- package/dist/components/icons/index.js +39 -0
- package/dist/components/tabs/index.d.ts +24 -0
- package/dist/components/tabs/index.js +39 -0
- package/dist/functions/mascaras/index.js +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +1 -1
- package/dist/theme.css +1039 -98
- package/package.json +24 -14
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
38
|
+
import { TIMEOUTS, VALIDACAO, getQuaggaConfig, verificarSuporteCamera, solicitarPermissaoCamera, } from './utils/constants';
|
|
39
|
+
import Button from 'src/components/button';
|
|
40
|
+
import CustomLoaders from 'src/components/loader';
|
|
41
|
+
import { useEffect, useState, useRef, useCallback } from 'react';
|
|
42
|
+
/**
|
|
43
|
+
* Componente scanner de código de barras que utiliza a biblioteca Quagga.js
|
|
44
|
+
* para capturar códigos de barras através da câmera do dispositivo.
|
|
45
|
+
*
|
|
46
|
+
* @component
|
|
47
|
+
* @param props - Propriedades do componente
|
|
48
|
+
* @param props.setCode - Função callback para definir o código lido
|
|
49
|
+
* @param props.open - Estado que controla se o scanner está aberto/ativo
|
|
50
|
+
* @param props.setOpen - Função para controlar o estado de abertura do scanner
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* const [code, setCode] = useState('')
|
|
55
|
+
* const [scannerOpen, setScannerOpen] = useState(false)
|
|
56
|
+
*
|
|
57
|
+
* return (
|
|
58
|
+
* <BarcodeScanner
|
|
59
|
+
* setCode={setCode}
|
|
60
|
+
* open={scannerOpen}
|
|
61
|
+
* setOpen={setScannerOpen}
|
|
62
|
+
* />
|
|
63
|
+
* )
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function BarcodeScanner(_a) {
|
|
67
|
+
var _this = this;
|
|
68
|
+
var setCode = _a.setCode, open = _a.open, setOpen = _a.setOpen;
|
|
69
|
+
var _b = useState(true), loading = _b[0], setLoading = _b[1];
|
|
70
|
+
var _c = useState(null), error = _c[0], setError = _c[1];
|
|
71
|
+
var _d = useState(''), lastCode = _d[0], setLastCode = _d[1];
|
|
72
|
+
var scannerRef = useRef(null);
|
|
73
|
+
var isInitialized = useRef(false);
|
|
74
|
+
var isInitializing = useRef(false);
|
|
75
|
+
var detectionTimeout = useRef();
|
|
76
|
+
var consecutiveReads = useRef(new Map());
|
|
77
|
+
var initTimeout = useRef();
|
|
78
|
+
var quaggaRef = useRef(null);
|
|
79
|
+
var getQuagga = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
80
|
+
var module_1, quagga, err_1;
|
|
81
|
+
var _a;
|
|
82
|
+
return __generator(this, function (_b) {
|
|
83
|
+
switch (_b.label) {
|
|
84
|
+
case 0:
|
|
85
|
+
if (quaggaRef.current)
|
|
86
|
+
return [2 /*return*/, quaggaRef.current];
|
|
87
|
+
_b.label = 1;
|
|
88
|
+
case 1:
|
|
89
|
+
_b.trys.push([1, 3, , 4]);
|
|
90
|
+
return [4 /*yield*/, import('quagga')];
|
|
91
|
+
case 2:
|
|
92
|
+
module_1 = _b.sent();
|
|
93
|
+
quagga = ((_a = module_1.default) !== null && _a !== void 0 ? _a : module_1);
|
|
94
|
+
quaggaRef.current = quagga;
|
|
95
|
+
return [2 /*return*/, quagga];
|
|
96
|
+
case 3:
|
|
97
|
+
err_1 = _b.sent();
|
|
98
|
+
console.error('Erro ao carregar biblioteca Quagga:', err_1);
|
|
99
|
+
setError('Biblioteca de scanner não disponível neste ambiente');
|
|
100
|
+
setLoading(false);
|
|
101
|
+
return [2 /*return*/, null];
|
|
102
|
+
case 4: return [2 /*return*/];
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}); }, []);
|
|
106
|
+
/**
|
|
107
|
+
* Verifica se o dispositivo suporta câmera
|
|
108
|
+
*
|
|
109
|
+
* @async
|
|
110
|
+
* @function checkCameraSupport
|
|
111
|
+
* @returns {Promise<void>} Promise que resolve quando a verificação é concluída
|
|
112
|
+
*/
|
|
113
|
+
var checkCameraSupport = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
114
|
+
return __generator(this, function (_a) {
|
|
115
|
+
switch (_a.label) {
|
|
116
|
+
case 0: return [4 /*yield*/, verificarSuporteCamera()];
|
|
117
|
+
case 1:
|
|
118
|
+
_a.sent();
|
|
119
|
+
return [2 /*return*/];
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}); }, []);
|
|
123
|
+
/**
|
|
124
|
+
* Solicita permissão para uso da câmera
|
|
125
|
+
*
|
|
126
|
+
* @async
|
|
127
|
+
* @function requestCameraPermission
|
|
128
|
+
* @returns {Promise<void>} Promise que resolve quando a permissão é concedida ou rejeitada
|
|
129
|
+
*/
|
|
130
|
+
var requestCameraPermission = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
131
|
+
return __generator(this, function (_a) {
|
|
132
|
+
switch (_a.label) {
|
|
133
|
+
case 0: return [4 /*yield*/, solicitarPermissaoCamera()];
|
|
134
|
+
case 1:
|
|
135
|
+
_a.sent();
|
|
136
|
+
return [2 /*return*/];
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}); }, []);
|
|
140
|
+
/**
|
|
141
|
+
* Valida se o código de barras lido é válido
|
|
142
|
+
*
|
|
143
|
+
* @function validateBarcode
|
|
144
|
+
* @param {string} code - Código de barras para validar
|
|
145
|
+
* @returns {boolean} true se o código é válido, false caso contrário
|
|
146
|
+
*
|
|
147
|
+
* @description
|
|
148
|
+
* Verifica se o código:
|
|
149
|
+
* - Não está vazio
|
|
150
|
+
* - Tem tamanho mínimo adequado
|
|
151
|
+
* - Não possui caracteres repetidos em excesso
|
|
152
|
+
*/
|
|
153
|
+
var validateBarcode = function (code) {
|
|
154
|
+
var cleanCode = code.trim();
|
|
155
|
+
if (!cleanCode || cleanCode.length < VALIDACAO.TAMANHO_MINIMO_CODIGO)
|
|
156
|
+
return false;
|
|
157
|
+
if (VALIDACAO.REGEX_CARACTERES_REPETIDOS.test(cleanCode))
|
|
158
|
+
return false;
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Limpa e para o scanner Quagga, liberando recursos
|
|
163
|
+
*
|
|
164
|
+
* @function cleanupQuagga
|
|
165
|
+
* @returns {void}
|
|
166
|
+
*
|
|
167
|
+
* @description
|
|
168
|
+
* - Remove listeners de detecção
|
|
169
|
+
* - Para o scanner
|
|
170
|
+
* - Reseta estados de inicialização
|
|
171
|
+
* - Limpa contadores de leituras consecutivas
|
|
172
|
+
*/
|
|
173
|
+
var cleanupQuagga = useCallback(function () {
|
|
174
|
+
try {
|
|
175
|
+
var quagga = quaggaRef.current;
|
|
176
|
+
if (isInitialized.current && quagga) {
|
|
177
|
+
quagga.offDetected();
|
|
178
|
+
quagga.stop();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
console.error('Erro ao limpar scanner:', err);
|
|
183
|
+
}
|
|
184
|
+
isInitialized.current = false;
|
|
185
|
+
isInitializing.current = false;
|
|
186
|
+
consecutiveReads.current.clear();
|
|
187
|
+
}, []);
|
|
188
|
+
/**
|
|
189
|
+
* Confirma a leitura de um código após múltiplas detecções consecutivas
|
|
190
|
+
*
|
|
191
|
+
* @function confirmReading
|
|
192
|
+
* @param {string} code - Código detectado para confirmar
|
|
193
|
+
* @returns {void}
|
|
194
|
+
*
|
|
195
|
+
* @description
|
|
196
|
+
* Implementa um sistema de confirmação que requer múltiplas leituras
|
|
197
|
+
* consecutivas do mesmo código antes de considerá-lo válido.
|
|
198
|
+
* Isso reduz falsos positivos e melhora a precisão.
|
|
199
|
+
*/
|
|
200
|
+
var confirmReading = useCallback(function (code) {
|
|
201
|
+
var count = consecutiveReads.current.get(code) || 0;
|
|
202
|
+
consecutiveReads.current.set(code, count + 1);
|
|
203
|
+
var newCount = consecutiveReads.current.get(code);
|
|
204
|
+
setLastCode(code);
|
|
205
|
+
for (var _i = 0, _a = Array.from(consecutiveReads.current.entries()); _i < _a.length; _i++) {
|
|
206
|
+
var _b = _a[_i], key = _b[0], value = _b[1];
|
|
207
|
+
if (key !== code && value < TIMEOUTS.CONFIRMACAO_THRESHOLD) {
|
|
208
|
+
consecutiveReads.current.delete(key);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (newCount >= TIMEOUTS.CONFIRMACAO_THRESHOLD) {
|
|
212
|
+
cleanupQuagga();
|
|
213
|
+
consecutiveReads.current.clear();
|
|
214
|
+
setTimeout(function () {
|
|
215
|
+
setCode(code);
|
|
216
|
+
setOpen(false);
|
|
217
|
+
}, TIMEOUTS.FECHAMENTO_MODAL);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
setTimeout(function () {
|
|
221
|
+
if (consecutiveReads.current.get(code) === newCount) {
|
|
222
|
+
consecutiveReads.current.delete(code);
|
|
223
|
+
if (lastCode === code)
|
|
224
|
+
setLastCode('');
|
|
225
|
+
}
|
|
226
|
+
}, TIMEOUTS.LIMPEZA_CONTADOR);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
230
|
+
[cleanupQuagga, setCode, setOpen]);
|
|
231
|
+
/**
|
|
232
|
+
* Manipula a detecção de códigos de barras pelo Quagga
|
|
233
|
+
*
|
|
234
|
+
* @function handleQuaggaDetected
|
|
235
|
+
* @param {QuaggaResult} result - Resultado da detecção do Quagga
|
|
236
|
+
* @returns {void}
|
|
237
|
+
*
|
|
238
|
+
* @description
|
|
239
|
+
* Processa os resultados da detecção:
|
|
240
|
+
* - Extrai o código e formato
|
|
241
|
+
* - Valida o código detectado
|
|
242
|
+
* - Inicia o processo de confirmação
|
|
243
|
+
*/
|
|
244
|
+
var handleQuaggaDetected = useCallback(function (result) {
|
|
245
|
+
var _a;
|
|
246
|
+
try {
|
|
247
|
+
if (!((_a = result === null || result === void 0 ? void 0 : result.codeResult) === null || _a === void 0 ? void 0 : _a.code))
|
|
248
|
+
return;
|
|
249
|
+
var code = result.codeResult.code.trim();
|
|
250
|
+
if (!validateBarcode(code))
|
|
251
|
+
return;
|
|
252
|
+
confirmReading(code);
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
console.error('Erro ao processar detecção:', err);
|
|
256
|
+
}
|
|
257
|
+
}, [confirmReading]);
|
|
258
|
+
/**
|
|
259
|
+
* Inicializa o scanner Quagga com configurações apropriadas
|
|
260
|
+
*
|
|
261
|
+
* @async
|
|
262
|
+
* @function initializeScanner
|
|
263
|
+
* @returns {Promise<void>} Promise que resolve quando a inicialização é concluída
|
|
264
|
+
*
|
|
265
|
+
* @description
|
|
266
|
+
* Processo de inicialização:
|
|
267
|
+
* 1. Verifica suporte à câmera
|
|
268
|
+
* 2. Solicita permissões
|
|
269
|
+
* 3. Configura o Quagga com base no dispositivo
|
|
270
|
+
* 4. Inicia o scanner
|
|
271
|
+
* 5. Configura listeners de detecção
|
|
272
|
+
*/
|
|
273
|
+
var initializeScanner = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
274
|
+
var err_2, isMobile, config, quagga;
|
|
275
|
+
return __generator(this, function (_a) {
|
|
276
|
+
switch (_a.label) {
|
|
277
|
+
case 0:
|
|
278
|
+
if (!scannerRef.current || isInitialized.current || isInitializing.current)
|
|
279
|
+
return [2 /*return*/];
|
|
280
|
+
isInitializing.current = true;
|
|
281
|
+
_a.label = 1;
|
|
282
|
+
case 1:
|
|
283
|
+
_a.trys.push([1, 4, , 5]);
|
|
284
|
+
return [4 /*yield*/, checkCameraSupport()];
|
|
285
|
+
case 2:
|
|
286
|
+
_a.sent();
|
|
287
|
+
return [4 /*yield*/, requestCameraPermission()];
|
|
288
|
+
case 3:
|
|
289
|
+
_a.sent();
|
|
290
|
+
return [3 /*break*/, 5];
|
|
291
|
+
case 4:
|
|
292
|
+
err_2 = _a.sent();
|
|
293
|
+
isInitializing.current = false;
|
|
294
|
+
setError(err_2.message);
|
|
295
|
+
setLoading(false);
|
|
296
|
+
return [2 /*return*/];
|
|
297
|
+
case 5:
|
|
298
|
+
isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
299
|
+
config = getQuaggaConfig(scannerRef.current, isMobile);
|
|
300
|
+
return [4 /*yield*/, getQuagga()];
|
|
301
|
+
case 6:
|
|
302
|
+
quagga = _a.sent();
|
|
303
|
+
if (!quagga)
|
|
304
|
+
return [2 /*return*/];
|
|
305
|
+
quagga.init(config, function (err) {
|
|
306
|
+
if (err) {
|
|
307
|
+
var message = typeof err === 'object' && err !== null && 'message' in err
|
|
308
|
+
? String(err.message)
|
|
309
|
+
: JSON.stringify(err);
|
|
310
|
+
console.error('Erro na inicialização do Quagga:', err);
|
|
311
|
+
setError("Erro ao inicializar scanner: ".concat(message));
|
|
312
|
+
setLoading(false);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
quagga.start();
|
|
317
|
+
isInitialized.current = true;
|
|
318
|
+
isInitializing.current = false;
|
|
319
|
+
setError(null);
|
|
320
|
+
consecutiveReads.current.clear();
|
|
321
|
+
quagga.onDetected(handleQuaggaDetected);
|
|
322
|
+
setTimeout(function () { return setLoading(false); }, TIMEOUTS.LOADING_DELAY);
|
|
323
|
+
}
|
|
324
|
+
catch (error_) {
|
|
325
|
+
console.error('Erro ao iniciar Quagga:', error_);
|
|
326
|
+
setError('Erro ao iniciar scanner');
|
|
327
|
+
setLoading(false);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
return [2 /*return*/];
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}); }, [
|
|
334
|
+
checkCameraSupport,
|
|
335
|
+
requestCameraPermission,
|
|
336
|
+
handleQuaggaDetected,
|
|
337
|
+
getQuagga,
|
|
338
|
+
]);
|
|
339
|
+
/**
|
|
340
|
+
* Effect principal que gerencia o ciclo de vida do scanner
|
|
341
|
+
*
|
|
342
|
+
* @description
|
|
343
|
+
* - Inicializa o scanner quando `open` se torna true
|
|
344
|
+
* - Limpa recursos quando `open` se torna false
|
|
345
|
+
* - Gerencia timeouts de inicialização
|
|
346
|
+
*/
|
|
347
|
+
useEffect(function () {
|
|
348
|
+
var currentTimeout = detectionTimeout.current;
|
|
349
|
+
if (initTimeout.current)
|
|
350
|
+
clearTimeout(initTimeout.current);
|
|
351
|
+
if (open && !isInitialized.current && !isInitializing.current) {
|
|
352
|
+
setLoading(true);
|
|
353
|
+
setError(null);
|
|
354
|
+
setLastCode('');
|
|
355
|
+
consecutiveReads.current.clear();
|
|
356
|
+
initTimeout.current = setTimeout(function () {
|
|
357
|
+
initializeScanner();
|
|
358
|
+
}, TIMEOUTS.INICIALIZACAO);
|
|
359
|
+
return function () {
|
|
360
|
+
if (initTimeout.current)
|
|
361
|
+
clearTimeout(initTimeout.current);
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
else if (!open && isInitialized.current) {
|
|
365
|
+
isInitialized.current = false;
|
|
366
|
+
isInitializing.current = false;
|
|
367
|
+
cleanupQuagga();
|
|
368
|
+
if (currentTimeout)
|
|
369
|
+
clearTimeout(currentTimeout);
|
|
370
|
+
if (initTimeout.current)
|
|
371
|
+
clearTimeout(initTimeout.current);
|
|
372
|
+
}
|
|
373
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
374
|
+
}, [open]);
|
|
375
|
+
/**
|
|
376
|
+
* Effect de limpeza executado na desmontagem do componente
|
|
377
|
+
*
|
|
378
|
+
* @description
|
|
379
|
+
* Garante que todos os recursos sejam liberados quando o componente
|
|
380
|
+
* é desmontado, evitando vazamentos de memória.
|
|
381
|
+
*/
|
|
382
|
+
useEffect(function () {
|
|
383
|
+
var currentTimeout = detectionTimeout.current;
|
|
384
|
+
return function () {
|
|
385
|
+
cleanupQuagga();
|
|
386
|
+
if (currentTimeout)
|
|
387
|
+
clearTimeout(currentTimeout);
|
|
388
|
+
};
|
|
389
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
390
|
+
}, []);
|
|
391
|
+
/**
|
|
392
|
+
* Renderização condicional quando há erro no scanner
|
|
393
|
+
*
|
|
394
|
+
* @description
|
|
395
|
+
* Exibe uma interface de erro completa com:
|
|
396
|
+
* - Mensagem de erro principal
|
|
397
|
+
* - Informações de debug (se disponíveis)
|
|
398
|
+
* - Alertas sobre contexto de segurança
|
|
399
|
+
* - Instruções para resolução de problemas
|
|
400
|
+
* - Botão para tentar novamente
|
|
401
|
+
*
|
|
402
|
+
* Trata especificamente:
|
|
403
|
+
* - Problemas de HTTPS/contexto seguro
|
|
404
|
+
* - Permissões de câmera
|
|
405
|
+
* - Acesso externo vs local
|
|
406
|
+
*/
|
|
407
|
+
if (error) {
|
|
408
|
+
return (_jsx("div", { className: "overflow-hidden", children: _jsxs("div", { className: "p-2 text-center", children: [_jsx("p", { className: "mb-2 font-semibold", children: "\u26A0\uFE0F Erro no scanner" }), _jsx("p", { className: "text-xs text-gray-600 mb-4", children: error }), error.toLowerCase().includes('permissão') && (_jsxs("div", { className: "bg-light-blue-50 border border-blue-200 rounded p-3 mb-4 text-left", children: [_jsx("p", { className: "text-xs font-semibold mb-2", children: "\uD83D\uDD12 Permiss\u00F5es da c\u00E2mera:" }), _jsxs("ul", { className: "text-xs text-blue-700 list-disc list-inside space-y-1", children: [_jsx("li", { children: "Clique no \u00EDcone de cadeado na barra de endere\u00E7o" }), _jsx("li", { children: "Encontre \"C\u00E2mera\" nas configura\u00E7\u00F5es" }), _jsx("li", { children: "Mude para \"Permitir\"" }), _jsx("li", { children: "Recarregue a p\u00E1gina" })] })] })), _jsx(Button, { size: "small", variant: "text", className: "mt-2", onClick: function () {
|
|
409
|
+
setError(null);
|
|
410
|
+
setLoading(true);
|
|
411
|
+
setTimeout(initializeScanner, TIMEOUTS.TENTAR_NOVAMENTE);
|
|
412
|
+
}, children: "Tentar novamente" })] }) }));
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Renderização principal do scanner quando não há erro
|
|
416
|
+
*
|
|
417
|
+
* @description
|
|
418
|
+
* Estrutura principal do componente:
|
|
419
|
+
* 1. Container do scanner (div com ref) - onde o Quagga renderiza o vídeo
|
|
420
|
+
* 2. Loader customizado - exibido durante inicialização
|
|
421
|
+
* 3. Dicas de uso - instruções para melhor leitura
|
|
422
|
+
*
|
|
423
|
+
* O container do scanner:
|
|
424
|
+
* - Tem altura dinâmica baseada no estado de loading
|
|
425
|
+
* - Fica oculto durante carregamento
|
|
426
|
+
* - Possui bordas estilizadas para indicar área de captura
|
|
427
|
+
*/
|
|
428
|
+
return (_jsxs("div", { children: [_jsx("div", { ref: scannerRef, id: "barcode-scanner", className: "mb-2 relative overflow-hidden border-2 border-dashed border-blue-300 rounded-lg", style: {
|
|
429
|
+
maxHeight: loading ? '0px' : '400px',
|
|
430
|
+
minHeight: loading ? '0px' : '300px',
|
|
431
|
+
visibility: loading ? 'hidden' : 'visible',
|
|
432
|
+
} }), _jsx(CustomLoaders, { open: loading }), !loading && (_jsx("div", { className: "text-center text-sm text-gray-600 mt-2", children: _jsxs("div", { className: "mt-3 text-xs text-gray-500", children: [_jsx("p", { className: "mb-1", children: "\uD83D\uDCA1 Dicas para melhor leitura:" }), _jsxs("ul", { className: "text-left space-y-1", children: [_jsx("li", { children: "\u2022 Mantenha o c\u00F3digo de barras bem iluminado" }), _jsx("li", { children: "\u2022 Aproxime ou afaste at\u00E9 focar bem" }), _jsx("li", { children: "\u2022 Mantenha o celular est\u00E1vel" }), _jsx("li", { children: "\u2022 Certifique-se que o c\u00F3digo n\u00E3o est\u00E1 danificado" })] })] }) }))] }));
|
|
433
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BarcodeDialogProps } from './utils/interface';
|
|
3
|
+
/**
|
|
4
|
+
* Componente modal para leitura de código de barras usando câmera
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Este componente exibe um dialog modal que permite a leitura de códigos de barras
|
|
8
|
+
* através da câmera do dispositivo. A funcionalidade só está disponível em
|
|
9
|
+
* contextos seguros (HTTPS) ou em desenvolvimento local.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const [isOpen, setIsOpen] = useState(false)
|
|
14
|
+
* const [barcode, setBarcode] = useState('')
|
|
15
|
+
*
|
|
16
|
+
* return (
|
|
17
|
+
* <BarcodeDialog
|
|
18
|
+
* open={isOpen}
|
|
19
|
+
* setOpen={setIsOpen}
|
|
20
|
+
* setCode={setBarcode}
|
|
21
|
+
* />
|
|
22
|
+
* )
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param {BarcodeDialogProps} props - As propriedades do componente
|
|
26
|
+
* @param {boolean} props.open - Estado que controla se o dialog está visível
|
|
27
|
+
* @param {function} props.setOpen - Função para alterar a visibilidade do dialog
|
|
28
|
+
* @param {function} props.setCode - Callback executado quando um código é lido
|
|
29
|
+
*
|
|
30
|
+
* @returns {React.ReactElement} Elemento React do dialog de leitura de barcode
|
|
31
|
+
*
|
|
32
|
+
* @version 1.2.0
|
|
33
|
+
* @author Flaviasoz
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
*/
|
|
36
|
+
export declare function BarcodeDialog({ open, setOpen, setCode, }: Readonly<BarcodeDialogProps>): React.ReactElement;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import Dialog from 'src/components/dialog';
|
|
3
|
+
import { BarcodeScanner } from './barcode-scanner';
|
|
4
|
+
import { useCallback, useMemo } from 'react';
|
|
5
|
+
import { contextoSeguro, MENSAGENS, style } from './utils/constants';
|
|
6
|
+
/**
|
|
7
|
+
* Componente modal para leitura de código de barras usando câmera
|
|
8
|
+
*
|
|
9
|
+
* @description
|
|
10
|
+
* Este componente exibe um dialog modal que permite a leitura de códigos de barras
|
|
11
|
+
* através da câmera do dispositivo. A funcionalidade só está disponível em
|
|
12
|
+
* contextos seguros (HTTPS) ou em desenvolvimento local.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const [isOpen, setIsOpen] = useState(false)
|
|
17
|
+
* const [barcode, setBarcode] = useState('')
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <BarcodeDialog
|
|
21
|
+
* open={isOpen}
|
|
22
|
+
* setOpen={setIsOpen}
|
|
23
|
+
* setCode={setBarcode}
|
|
24
|
+
* />
|
|
25
|
+
* )
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @param {BarcodeDialogProps} props - As propriedades do componente
|
|
29
|
+
* @param {boolean} props.open - Estado que controla se o dialog está visível
|
|
30
|
+
* @param {function} props.setOpen - Função para alterar a visibilidade do dialog
|
|
31
|
+
* @param {function} props.setCode - Callback executado quando um código é lido
|
|
32
|
+
*
|
|
33
|
+
* @returns {React.ReactElement} Elemento React do dialog de leitura de barcode
|
|
34
|
+
*
|
|
35
|
+
* @version 1.2.0
|
|
36
|
+
* @author Flaviasoz
|
|
37
|
+
* @since 1.0.0
|
|
38
|
+
*/
|
|
39
|
+
export function BarcodeDialog(_a) {
|
|
40
|
+
var open = _a.open, setOpen = _a.setOpen, setCode = _a.setCode;
|
|
41
|
+
/**
|
|
42
|
+
* Manipula o fechamento do dialog
|
|
43
|
+
* @function handleClose
|
|
44
|
+
* @description Fecha o dialog definindo o estado 'open' como false
|
|
45
|
+
*/
|
|
46
|
+
var handleClose = useCallback(function () {
|
|
47
|
+
setOpen(false);
|
|
48
|
+
}, [setOpen]);
|
|
49
|
+
/**
|
|
50
|
+
* Verifica se o contexto é seguro para usar a câmera
|
|
51
|
+
* @description Memoriza o resultado da verificação de segurança para evitar re-cálculos
|
|
52
|
+
*/
|
|
53
|
+
var isContextSecure = useMemo(function () { return contextoSeguro(); }, []);
|
|
54
|
+
/**
|
|
55
|
+
* Componente de conteúdo do dialog baseado no contexto de segurança
|
|
56
|
+
* @description Renderiza o scanner ou mensagem de erro baseado na segurança do contexto
|
|
57
|
+
*/
|
|
58
|
+
var dialogContent = useMemo(function () {
|
|
59
|
+
if (!isContextSecure) {
|
|
60
|
+
return (_jsx("div", { role: "alert", "aria-live": "polite", className: "text-xs", style: style, children: MENSAGENS.HTTPS_REQUERIDO }));
|
|
61
|
+
}
|
|
62
|
+
return _jsx(BarcodeScanner, { setCode: setCode, open: open, setOpen: setOpen });
|
|
63
|
+
}, [isContextSecure, setCode, open, setOpen]);
|
|
64
|
+
return (_jsx(Dialog, { open: open, maxWidth: "xs", onClose: handleClose, "aria-labelledby": "barcode-dialog-title", "aria-describedby": "barcode-dialog-description", children: _jsx("div", { id: "barcode-dialog-description", children: dialogContent }) }));
|
|
65
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { CSSProperties } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Mensagens de erro e informação para o componente
|
|
4
|
+
*/
|
|
5
|
+
export declare const MENSAGENS: {
|
|
6
|
+
HTTPS_REQUERIDO: string;
|
|
7
|
+
TITULO: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Mapa de mensagens de erro para diferentes tipos de erro de câmera
|
|
11
|
+
*/
|
|
12
|
+
export declare const MENSAGENS_ERRO_CAMERA: Map<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Configurações de timeout e delays
|
|
15
|
+
*/
|
|
16
|
+
export declare const TIMEOUTS: {
|
|
17
|
+
CONFIRMACAO_THRESHOLD: number;
|
|
18
|
+
LIMPEZA_CONTADOR: number;
|
|
19
|
+
INICIALIZACAO: number;
|
|
20
|
+
LOADING_DELAY: number;
|
|
21
|
+
FECHAMENTO_MODAL: number;
|
|
22
|
+
TENTAR_NOVAMENTE: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Configurações de validação
|
|
26
|
+
*/
|
|
27
|
+
export declare const VALIDACAO: {
|
|
28
|
+
TAMANHO_MINIMO_CODIGO: number;
|
|
29
|
+
REGEX_CARACTERES_REPETIDOS: RegExp;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Configurações da câmera
|
|
33
|
+
*/
|
|
34
|
+
export declare const CONFIG_CAMERA: {
|
|
35
|
+
CONSTRAINTS: {
|
|
36
|
+
WIDTH: {
|
|
37
|
+
min: number;
|
|
38
|
+
ideal: number;
|
|
39
|
+
max: number;
|
|
40
|
+
};
|
|
41
|
+
HEIGHT: {
|
|
42
|
+
min: number;
|
|
43
|
+
ideal: number;
|
|
44
|
+
max: number;
|
|
45
|
+
};
|
|
46
|
+
ASPECT_RATIO: {
|
|
47
|
+
ideal: number;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
AREA_SCANNER: {
|
|
51
|
+
top: string;
|
|
52
|
+
right: string;
|
|
53
|
+
left: string;
|
|
54
|
+
bottom: string;
|
|
55
|
+
};
|
|
56
|
+
WORKERS: {
|
|
57
|
+
MOBILE: number;
|
|
58
|
+
DESKTOP: number;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Readers suportados pelo Quagga
|
|
63
|
+
*/
|
|
64
|
+
export declare const READERS_SUPORTADOS: string[];
|
|
65
|
+
/**
|
|
66
|
+
* Configuração padrão do Quagga
|
|
67
|
+
*/
|
|
68
|
+
export declare const getQuaggaConfig: (target: HTMLElement, isMobile: boolean) => {
|
|
69
|
+
inputStream: {
|
|
70
|
+
name: string;
|
|
71
|
+
type: string;
|
|
72
|
+
constraints: {
|
|
73
|
+
width: {
|
|
74
|
+
min: number;
|
|
75
|
+
ideal: number;
|
|
76
|
+
max: number;
|
|
77
|
+
};
|
|
78
|
+
height: {
|
|
79
|
+
min: number;
|
|
80
|
+
ideal: number;
|
|
81
|
+
max: number;
|
|
82
|
+
};
|
|
83
|
+
facingMode: string;
|
|
84
|
+
aspectRatio: {
|
|
85
|
+
ideal: number;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
target: HTMLElement;
|
|
89
|
+
area: {
|
|
90
|
+
top: string;
|
|
91
|
+
right: string;
|
|
92
|
+
left: string;
|
|
93
|
+
bottom: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
locator: {
|
|
97
|
+
halfSample: boolean;
|
|
98
|
+
patchSize: string;
|
|
99
|
+
debug: {
|
|
100
|
+
showCanvas: boolean;
|
|
101
|
+
showPatches: boolean;
|
|
102
|
+
showFoundPatches: boolean;
|
|
103
|
+
showSkeleton: boolean;
|
|
104
|
+
showLabels: boolean;
|
|
105
|
+
showPatchLabels: boolean;
|
|
106
|
+
showRemainingPatchLabels: boolean;
|
|
107
|
+
boxFromPatches: {
|
|
108
|
+
showTransformed: boolean;
|
|
109
|
+
showTransformedBox: boolean;
|
|
110
|
+
showBB: boolean;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
numOfWorkers: number;
|
|
115
|
+
decoder: {
|
|
116
|
+
readers: string[];
|
|
117
|
+
multiple: boolean;
|
|
118
|
+
};
|
|
119
|
+
locate: boolean;
|
|
120
|
+
debug: boolean;
|
|
121
|
+
frequency: number;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Verifica se o protocolo atual é HTTPS ou se está em desenvolvimento local
|
|
125
|
+
* @returns {boolean} True se for HTTPS ou localhost, false caso contrário
|
|
126
|
+
*/
|
|
127
|
+
export declare const contextoSeguro: () => boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Verifica se o navegador suporta acesso à câmera
|
|
130
|
+
* @throws {Error} Quando o navegador não suporta câmera ou não está em contexto seguro
|
|
131
|
+
*/
|
|
132
|
+
export declare const verificarSuporteCamera: () => Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Solicita permissão para acessar a câmera
|
|
135
|
+
* @returns {Promise<boolean>} True se a permissão foi concedida
|
|
136
|
+
* @throws {Error} Quando a permissão é negada ou há erro no acesso
|
|
137
|
+
*/
|
|
138
|
+
export declare const solicitarPermissaoCamera: () => Promise<boolean>;
|
|
139
|
+
export declare const style: CSSProperties;
|