diff --git a/packages/aziface/src/module/aziface.ts b/packages/aziface/src/module/aziface.ts index f55f021..7ad1c1c 100644 --- a/packages/aziface/src/module/aziface.ts +++ b/packages/aziface/src/module/aziface.ts @@ -7,7 +7,10 @@ import { FaceTecSDK as FaceTecSDKType } from '../types/FaceTecSDK'; import { SessionError } from '../errors/errors'; import { SessionRequestProcessor } from '../services/request-processor'; import { applyTheme, getBackgroundColor } from '../styles/theme'; -import { getInitializationErrorCauseByCode } from '../utils'; +import { + getInitializationErrorCauseByCode, + applyResponsiveStyles, +} from '../utils'; import { Controller, DisposeCallback, @@ -30,6 +33,8 @@ export class AzifaceController implements Controller { public static baseUrl: string = ''; public static headers: InitializeHeaders = {} as InitializeHeaders; private faceTecSDKInstance: FaceTecSDKInstance | null = null; + private internalID: number | undefined = undefined; + public initialize = ( init: Initialize, callback: InitializeCallback, @@ -200,6 +205,9 @@ export class AzifaceController implements Controller { AzifaceController.baseUrl = ''; AzifaceController.headers = {} as InitializeHeaders; + window.removeEventListener('click', applyResponsiveStyles); + window.clearInterval(this.internalID); + this.withTheme(); }; @@ -216,6 +224,9 @@ export class AzifaceController implements Controller { private onInitializationError = (): void => this.cleanup(); private onComplete = (faceTecSessionStatus: FaceTecSessionStatus): void => { + window.removeEventListener('click', applyResponsiveStyles); + window.clearInterval(this.internalID); + const isError = faceTecSessionStatus !== FaceTecSDK.FaceTecSessionStatus.SessionCompleted; if (isError) { @@ -233,6 +244,18 @@ export class AzifaceController implements Controller { } else { throw new SessionError(MethodError.NotInitialized); } + + window.addEventListener('click', applyResponsiveStyles); + + const windowClickEvent = new MouseEvent('click', { + bubbles: true, + cancelable: true, + view: window, + }); + + this.internalID = window.setInterval(() => { + window.dispatchEvent(windowClickEvent); + }, 250); }; } diff --git a/packages/aziface/src/styles/aziface.css b/packages/aziface/src/styles/aziface.css index 4dc44ab..31ae617 100644 --- a/packages/aziface/src/styles/aziface.css +++ b/packages/aziface/src/styles/aziface.css @@ -1,13 +1,31 @@ -#DOM_FT_PRIMARY_TOPLEVEL_mainContainer - > #DOM_FT_overlayContainer - > #DOM_FT_idScanDesktopSubframeInterfaceContainer - > #DOM_FT_idScanButtonContainer { +#DOM_FT_idScanButtonContainer { border: none !important; } -#DOM_FT_PRIMARY_TOPLEVEL_mainContainer - > #DOM_FT_orientationContainer - > #DOM_FT_orientationContentContainer - > #DOM_FT_orientationImageContainer { +#DOM_FT_orientationImageContainer { margin: 0 auto !important; } + +#DOM_FT_mainInterfaceNonOverlayContainer { + @media (max-width: 768px) { + border: none !important; + } +} + +#DOM_FT_readyScreenHeaderElement { + @media (max-width: 768px) { + font-size: 1rem !important; + } +} + +#DOM_FT_readyScreenSubtextElement { + @media (max-width: 768px) { + font-size: 0.75rem !important; + } +} + +#DOM_FT_yourAppLogoContainer { + @media (max-width: 768px) { + height: calc(100% - 360px) !important; + } +} diff --git a/packages/aziface/src/utils/styles.ts b/packages/aziface/src/utils/styles.ts index e923e4a..29fa60b 100644 --- a/packages/aziface/src/utils/styles.ts +++ b/packages/aziface/src/utils/styles.ts @@ -1,12 +1,169 @@ import { CancelLocation } from '../types/aziface'; import type { FaceTecCancelButtonLocation } from '../types/FaceTecCustomization'; +const PREFIX = 'DOM_FT_'; +const SAFE_MARGIN = 48; const CANCEL_BUTTON_LOCATION = { Disabled: 0 as FaceTecCancelButtonLocation, TopLeft: 1 as FaceTecCancelButtonLocation, TopRight: 2 as FaceTecCancelButtonLocation, } as const; +function getElementById(id: string): HTMLElement | null { + return document.getElementById(id); +} + +function getStyle(id: string): string { + const element = getElementById(id); + return element ? element.getAttribute('style') || '' : ''; +} + +function setStyle(id: string, style: string): void { + const element = getElementById(id); + if (element) element.setAttribute('style', style); +} + +function isMobile(): boolean { + return window.innerWidth <= 768; +} + +function isRequestingPermission(): boolean { + const id = `${PREFIX}cameraPermissionsScreen`; + const currentStyle = getStyle(id); + return currentStyle.includes('display: flex;'); +} + +function adjustFrameContainer(): void { + if (!isMobile()) return; + + const id = `${PREFIX}frameContainer`; + if (!!getElementById(id)) { + const currentStyle = getStyle(id); + const newStyle = 'top: 0px !important; left: 0px !important;'; + const isRequesting = isRequestingPermission(); + const hasStyle = currentStyle.includes(newStyle); + + if (!hasStyle && !isRequesting) { + setStyle(id, `${currentStyle} ${newStyle}`); + } else if (hasStyle && isRequesting) { + const replatedStyle = currentStyle.replace( + newStyle, + 'top: -2px !important; left: -2px !important;', + ); + setStyle(id, replatedStyle); + } + } +} + +function adjustCancelButtonElementBase(id: string, style: string): void { + if (!isMobile() || !getElementById(id)) return; + + if (isRequestingPermission()) { + setStyle(id, style); + } else { + const left = window.innerWidth > 400 ? '20%' : '22%'; + const newStyle = `left: ${left} !important;`; + const currentStyle = getStyle(id); + if (!currentStyle.includes(newStyle)) { + setStyle(id, `${currentStyle} ${newStyle}`); + } + } +} + +function adjustCancelButtonElement(): void { + adjustCancelButtonElementBase( + `${PREFIX}cancelButtonElement`, + 'left: 0px !important; height: 16px; width: 16px; padding: 15px; margin: 0px; opacity: 1; display: block; transition: opacity 500ms;', + ); + adjustCancelButtonElementBase( + `${PREFIX}idScanCancelButtonElement`, + 'height: 16px; width: 16px; padding: 8px; margin: 8px; top: 0px; left: 0px; opacity: 1; display: flex; transition: opacity 2000ms;', + ); +} + +function adjustIdScanMask(): void { + if (!isMobile()) return; + + const startY = '12px'; + const mask = getElementById(`${PREFIX}idScanCaptureFrameMask`); + if (mask) { + const [, rect] = mask.children; + rect.setAttribute('style', ''); + rect.setAttribute('y', startY); + } + + const rectId = `${PREFIX}idScanCaptureFrameRect`; + const rect = getElementById(rectId); + if (rect) { + setStyle(rectId, ''); + rect.setAttribute('y', startY); + } +} + +function adjustOCRFormContainer(): void { + if (!isMobile()) return; + + const id = `${PREFIX}ocrFormContainer`; + const element = getElementById(id); + if (element) { + const [form] = element.children; + + for (const child of form.children) { + if (child.tagName === 'SECTION') { + for (const sectionChild of child.children) { + if (sectionChild.tagName === 'DIV') { + const width = window.innerWidth - SAFE_MARGIN; + const currentStyle = sectionChild.getAttribute('style') || ''; + const newStyle = `margin: 0px auto; display: block; width: ${width}px;`; + if (!currentStyle.includes(newStyle)) { + sectionChild.setAttribute('style', newStyle); + } + } + } + } + } + } +} + +function generateDPath(): string | null { + if (!isMobile()) return null; + + const VIEW_BOX = 640; + const OVAL = 158; + const displacement = OVAL.toFixed(1); + const rx = (79).toFixed(1); + const ry = (116).toFixed(1); + const width = (VIEW_BOX / 2 - OVAL / 2).toFixed(1); + + return `m${width} 180 a${rx} ${ry} 0 1 0 ${displacement} 0 a${rx} ${ry} 0 1 0 -${displacement} 0`; +} + +function removeElementById(id: string): void { + if (!isMobile()) return; + + const element = getElementById(id); + if (element) element.remove(); +} + +function setDPath(id: string): void { + const element = getElementById(id); + const d = generateDPath(); + if (element && d) element.setAttribute('d', d); +} + +export function applyResponsiveStyles(): void { + removeElementById(`${PREFIX}ovalSpinner1`); + removeElementById(`${PREFIX}ovalSpinner2`); + + adjustCancelButtonElement(); + adjustFrameContainer(); + adjustIdScanMask(); + adjustOCRFormContainer(); + + setDPath(`${PREFIX}frameGetReadyOvalPath`); + setDPath(`${PREFIX}frameOvalPath`); +} + export function resolveCancelLocation( location?: CancelLocation, ): FaceTecCancelButtonLocation {