diff --git a/manual-multi-page/react/App.tsx b/manual-multi-page/react/App.tsx new file mode 100644 index 0000000..061af2b --- /dev/null +++ b/manual-multi-page/react/App.tsx @@ -0,0 +1,136 @@ +import { useEffect, useRef, useState } from 'react'; +import { + defineComponents, + DocumentReaderService, + DocumentReaderDetailType, + TransactionEvent, + DocumentReaderWebComponent, + ResponseCode, + EventActions, + InternalScenarios, + DocumentReaderResponseType, +} from '@regulaforensics/vp-frontend-document-components'; +import Results from './Results'; +import './styles.css'; + +enum Status { + Start, + Result, + Processing, +} + +const App = () => { + const containerRef = useRef(null); + const elemRef = useRef(null); + const [response, setResponse] = useState(null); + const [status, setStatus] = useState(Status.Start); + const [morePagesAvailable, setMorePagesAvailable] = useState(false); + + const listener = async (data: CustomEvent) => { + const action = data.detail.action; + + if (action === EventActions.PROCESS_FINISHED) { + const status = data.detail.data?.status; + const isFinishStatus = status === ResponseCode.OK || status === ResponseCode.TIMEOUT; + const responseData = data.detail.data; + + if (isFinishStatus && responseData?.response) { + setResponse(responseData.response); + setStatus(Status.Result); + setMorePagesAvailable(!!responseData?.response?.rawResponse.morePagesAvailable); + } + } + if (data.detail?.action === EventActions.CLOSE || data.detail?.action === EventActions.CAMERA_PROCESS_CLOSED) { + setResponse(null); + setMorePagesAvailable(false); + setStatus(Status.Start); + } + }; + + const startDocument = () => { + if (!window.RegulaDocumentSDK) return; + + void window.RegulaDocumentSDK.startNewDocument(); + setStatus(Status.Processing); + }; + + const startNewPage = () => { + if (!window.RegulaDocumentSDK) return; + + void window.RegulaDocumentSDK.startNewPage(); + setStatus(Status.Processing); + }; + + useEffect(() => { + window.RegulaDocumentSDK = new DocumentReaderService(); + window.RegulaDocumentSDK.recognizerProcessParam = { + processParam: { + scenario: InternalScenarios.MrzAndLocate, + multipageProcessing: true, + }, + }; + + defineComponents().then(() => window.RegulaDocumentSDK.initialize()); + // To use the document-reader component on test environments, you have to set the base64 license + // defineComponents().then(() => window.RegulaDocumentSDK.initialize({ license: 'YOUR_BASE64_LICENSE_KEY' })); + + const containerRefCurrent = containerRef.current; + + if (!containerRefCurrent) return; + + containerRefCurrent.addEventListener('document-reader', listener); + + return () => { + window.RegulaDocumentSDK.shutdown(); + containerRefCurrent.removeEventListener('document-reader', listener); + }; + }, []); + + useEffect(() => { + const elementRefCurrent = elemRef.current; + + if (elementRefCurrent && status === Status.Processing) { + elementRefCurrent.settings = { + manualMultipageMode: true + }; + } + }, [status]); + + return ( +
+ {status === Status.Start && ( +
+ +
+ )} + {status === Status.Result && ( + <> +
+ + {morePagesAvailable && ( + + )} +
+ {response && ( +
+ +
+ )} + + )} + {status === Status.Processing && ( +
+ +
+ )} +
+ ); +}; + +export default App; diff --git a/manual-multi-page/react/Results.tsx b/manual-multi-page/react/Results.tsx new file mode 100644 index 0000000..f9a5353 --- /dev/null +++ b/manual-multi-page/react/Results.tsx @@ -0,0 +1,45 @@ +import { DocumentReaderResponseType } from '@regulaforensics/vp-frontend-document-components'; +import { + DocReaderContainer, + Details, + Info, + Graphics, + Rfid, + ResponseViewer, + Logs, + PortraitsComparison, + Tabs, +} from '@regulaforensics/ui-components'; + +const ResultsTab = () => ( + <> +
+ + + + + +); + +type DocumentReaderResult = { + response: DocumentReaderResponseType; +}; + +const Results = ({ response }: DocumentReaderResult) => { + const items = [ + { id: 'Results', label: 'Results', children: }, + { id: 'Response', label: 'Response', children: }, + ]; + + if (response.rawResponse.log) { + items.push({ id: 'Logs', label: 'Logs', children: }); + } + + return ( + + + + ); +}; + +export default Results; diff --git a/manual-multi-page/react/globals.d.ts b/manual-multi-page/react/globals.d.ts new file mode 100644 index 0000000..99b7726 --- /dev/null +++ b/manual-multi-page/react/globals.d.ts @@ -0,0 +1,10 @@ +import { DocumentReaderWebComponent, IDocumentReader } from '@regulaforensics/vp-frontend-document-components'; + +declare global { + namespace React.JSX { + interface IntrinsicElements { + 'document-reader': React.DetailedHTMLProps, + DocumentReaderWebComponent>; + } + } +} diff --git a/manual-multi-page/react/index.html b/manual-multi-page/react/index.html new file mode 100644 index 0000000..492fa32 --- /dev/null +++ b/manual-multi-page/react/index.html @@ -0,0 +1,14 @@ + + + + + + + My app + + +
+ + + diff --git a/manual-multi-page/react/index.tsx b/manual-multi-page/react/index.tsx new file mode 100644 index 0000000..0fe3366 --- /dev/null +++ b/manual-multi-page/react/index.tsx @@ -0,0 +1,7 @@ +import { createRoot } from 'react-dom/client'; +import App from './App'; + +const element = document.getElementById('root'); +const root = createRoot(element as HTMLElement); + +root.render(); diff --git a/manual-multi-page/react/package.json b/manual-multi-page/react/package.json new file mode 100644 index 0000000..c4e58f9 --- /dev/null +++ b/manual-multi-page/react/package.json @@ -0,0 +1,22 @@ +{ + "name": "sample", + "version": "1.0.0", + "type": "module", + "scripts": { + "serve": "vite", + "build": "vite build" + }, + "dependencies": { + "@regulaforensics/ui-components": "nightly", + "@regulaforensics/vp-frontend-document-components": "nightly", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "typescript": "^5.9.3", + "vite": "^7.2.6" + } +} diff --git a/manual-multi-page/react/styles.css b/manual-multi-page/react/styles.css new file mode 100644 index 0000000..95c9403 --- /dev/null +++ b/manual-multi-page/react/styles.css @@ -0,0 +1,39 @@ +body { + background: #f5f5f5; +} + +.results-container { + max-width: 1200px; + margin: 0 auto; +} + +.reader-container { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.buttons { + display: flex; + justify-content: center; + column-gap: 15px; + padding-bottom: 20px; +} + +.button { + padding: 8px 15px; + border: 1px solid black; + background: #fff; + font-family: Arial, sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 135%; + border-radius: 8px; + cursor: pointer; + transition: .1s; +} + +.button:hover { + background: #efefef; +} diff --git a/manual-multi-page/react/tsconfig.json b/manual-multi-page/react/tsconfig.json new file mode 100644 index 0000000..6209ef6 --- /dev/null +++ b/manual-multi-page/react/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + } +} diff --git a/manual-multi-page/react/vite.config.ts b/manual-multi-page/react/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/manual-multi-page/react/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/manual-multi-page/typescript/index.html b/manual-multi-page/typescript/index.html new file mode 100644 index 0000000..6ca766a --- /dev/null +++ b/manual-multi-page/typescript/index.html @@ -0,0 +1,18 @@ + + + + + + + My app + + +
+ + +
+
Press "Start new document" to start scanning document.
+ + + diff --git a/manual-multi-page/typescript/index.ts b/manual-multi-page/typescript/index.ts new file mode 100644 index 0000000..6be0e87 --- /dev/null +++ b/manual-multi-page/typescript/index.ts @@ -0,0 +1,96 @@ +import { + defineComponents, + DocumentReaderService, + InternalScenarios, + DocumentReaderWebComponent, + DocumentReaderDetailType, + TransactionEvent, + EventActions, + ResponseCode, +} from '@regulaforensics/vp-frontend-document-components'; +import './styles.css'; + +const newDocumentButton = document.querySelector('#new-document') as HTMLButtonElement; +const nextPageButton = document.querySelector('#new-page') as HTMLButtonElement; +const info = document.querySelector('.info') as HTMLDivElement; + +window.RegulaDocumentSDK = new DocumentReaderService(); +window.RegulaDocumentSDK.recognizerProcessParam = { + processParam: { + scenario: InternalScenarios.MrzAndLocate, + multipageProcessing: true, + }, +}; +window.RegulaDocumentSDK.imageProcessParam = { + processParam: { + scenario: InternalScenarios.MrzAndLocate, + }, +}; + +defineComponents().then(() => window.RegulaDocumentSDK.initialize()); +// To use the document-reader component on test environments, you have to set the base64 license +// defineComponents().then(() => window.RegulaDocumentSDK.initialize({ license: 'YOUR_BASE64_LICENSE_KEY' })); + +function createDocumentReader() { + const documentReaderElement = document.createElement('document-reader') as DocumentReaderWebComponent; + const container = document.createElement('div'); + documentReaderElement.setAttribute('new-layout', 'true'); + container.setAttribute('class', 'reader-container'); + container.append(documentReaderElement); + + documentReaderElement.settings = { + uploadFileButton: false, + manualMultipageMode: true, + } + + return container; +} + +function documentReaderListener(data: CustomEvent) { + const reader = document.querySelector('.reader-container'); + const action = data.detail.action; + + if (action === EventActions.PROCESS_FINISHED) { + const status = data.detail.data?.status; + const isFinishStatus = status === ResponseCode.OK || status === ResponseCode.TIMEOUT; + const responseData = data.detail.data; + + if (isFinishStatus && responseData?.response) { + const isMorePagesAvailable = !!responseData.response.rawResponse.morePagesAvailable; + + info.textContent = 'Check your result in the console. '; + + if (isMorePagesAvailable) { + nextPageButton.disabled = false; + info.textContent += 'More pages are available, click "Start new page" ' + + 'to continue scanning, or start scanning a new document by clicking "Start new document".'; + } + console.log(responseData.response); + + reader?.remove(); + } + } + if (action === EventActions.CAMERA_PROCESS_CLOSED) reader?.remove(); +} + +function newDocumentButtonListener() { + if (!window.RegulaDocumentSDK) return; + + info.textContent = ''; + nextPageButton.disabled = true; + void window.RegulaDocumentSDK.startNewDocument(); + document.body.append(createDocumentReader()); +} + +function nextPageButtonListener() { + if (!window.RegulaDocumentSDK) return; + + info.textContent = ''; + nextPageButton.disabled = true; + void window.RegulaDocumentSDK.startNewPage(); + document.body.append(createDocumentReader()); +} + +newDocumentButton?.addEventListener('click', newDocumentButtonListener); +nextPageButton?.addEventListener('click', nextPageButtonListener); +document.body.addEventListener('document-reader', documentReaderListener); diff --git a/manual-multi-page/typescript/package.json b/manual-multi-page/typescript/package.json new file mode 100644 index 0000000..f78d9d1 --- /dev/null +++ b/manual-multi-page/typescript/package.json @@ -0,0 +1,16 @@ +{ + "name": "sample", + "version": "1.0.0", + "type": "module", + "scripts": { + "serve": "vite", + "build": "vite build" + }, + "dependencies": { + "@regulaforensics/vp-frontend-document-components": "nightly" + }, + "devDependencies": { + "typescript": "^5.9.3", + "vite": "^7.2.6" + } +} diff --git a/manual-multi-page/typescript/styles.css b/manual-multi-page/typescript/styles.css new file mode 100644 index 0000000..a8bca30 --- /dev/null +++ b/manual-multi-page/typescript/styles.css @@ -0,0 +1,42 @@ +.reader-container { + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.buttons { + display: flex; + justify-content: center; + column-gap: 15px; + padding-bottom: 20px; +} + +.button { + padding: 8px 15px; + border: 1px solid black; + background: #fff; + font-family: Arial, sans-serif; + font-size: 16px; + font-weight: 400; + line-height: 135%; + border-radius: 8px; + cursor: pointer; + transition: .1s; +} + +.button:hover:not(:disabled) { + background: #efefef; +} + +.button:disabled { + cursor: not-allowed; + border: 1px solid lightgrey; +} + +.info { + max-width: 600px; + text-align: center; + line-height: 24px; + margin: 0 auto; +} diff --git a/manual-multi-page/typescript/tsconfig.json b/manual-multi-page/typescript/tsconfig.json new file mode 100644 index 0000000..5dd35f0 --- /dev/null +++ b/manual-multi-page/typescript/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + } +}