/* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable no-underscore-dangle */ import { fromEvent } from 'rxjs'; import { debounceTime, throttleTime } from 'rxjs/operators'; export const objIsEmpty = (obj: Record): boolean => !Object.keys(obj).length; export const watchScroll = ( viewAreaElement: HTMLElement | null, cb: (state: ScrollStateType) => void, ): ScrollStateType => { let rAF: number | null = null; const element = viewAreaElement as HTMLElement; const state = { right: true, down: true, lastX: element.scrollLeft, lastY: element.scrollTop, subscriber: {}, }; const debounceScroll = (): void => { if (rAF) { return; } // schedule an invocation of scroll for next animation frame. rAF = window.requestAnimationFrame(() => { rAF = null; const currentX = element.scrollLeft; const { lastX } = state; if (currentX !== lastX) { state.right = currentX > lastX; } state.lastX = currentX; const currentY = element.scrollTop; const { lastY } = state; if (currentY !== lastY) { state.down = currentY > lastY; } state.lastY = currentY; cb(state); }); }; const subscriber = fromEvent(element, 'scroll') .pipe(throttleTime(100), debounceTime(100)) .subscribe(debounceScroll); state.subscriber = subscriber; return state; }; export const scrollIntoView = ( element: HTMLElement, spot?: { top: number }, skipOverflowHiddenElements = false, ): void => { let parent = element.offsetParent as HTMLElement; let offsetY = element.offsetTop + element.clientTop; if (!parent) { return; // no need to scroll } while ( (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth) || (skipOverflowHiddenElements && getComputedStyle(parent).overflow === 'hidden') ) { if (parent.dataset._scaleY) { offsetY /= parseInt(parent.dataset._scaleY, 10); } offsetY += parent.offsetTop; parent = parent.offsetParent as HTMLElement; if (!parent) { return; // no need to scroll } } if (spot) { if (spot.top !== undefined) { offsetY += spot.top; } } parent.scrollTop = offsetY; }; export const scaleCheck = (scale: number): number => { if (typeof scale === 'number' && scale >= 50 && scale <= 250) { return Math.round(scale * 100) / 10000; } if (scale < 50) { return 0.5; } return 2.5; }; export const downloadFileWithUri = (name: string, uri: string): void => { const ele = document.createElement('a'); ele.download = name; ele.href = uri; ele.target = '_blank'; document.body.appendChild(ele); ele.click(); document.body.removeChild(ele); ele.remove(); }; export const uploadFile = (extension: string): Promise => new Promise((resolve) => { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = extension; fileInput.onchange = (): void => { if (fileInput.files) { const reader = new FileReader(); reader.onload = (): void => { const contents = reader.result as string; resolve(contents); }; if (extension.includes('xfdf')) { reader.readAsText(fileInput.files[0]); } else { reader.readAsDataURL(fileInput.files[0]); } } }; document.body.appendChild(fileInput); fileInput.click(); }); const componentToHex = (c: number): string => { const hex = c.toString(16); return hex.length === 1 ? `0${hex}` : hex; }; export const hexToRgb = ( hex: string, ): { r: number; g: number; b: number } | null => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16), } : null; }; export const rgbToHex = (r: number, g: number, b: number): string => `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`; export const floatToHex = (r: number, g: number, b: number): string => rgbToHex(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)); type ScaleDataType = CoordType & { operator: string; clickX: number; clickY: number; moveX: number; moveY: number; }; export const calcDragAndDropScale = ({ top, left, width = 0, height = 0, operator, clickX, clickY, moveX, moveY, }: ScaleDataType): CoordType => { let scaleCoord = { left, top, width, height, }; switch (operator) { case 'top': scaleCoord = { ...scaleCoord, top: moveY - (clickY - top), height: height + (clickY - moveY), }; break; case 'top-left': scaleCoord = { top: moveY - (clickY - top), left: moveX - (clickX - left), height: height + (clickY - moveY), width: width + (clickX - moveX), }; break; case 'left': scaleCoord = { ...scaleCoord, left: moveX - (clickX - left), width: width + (clickX - moveX), }; break; case 'bottom-left': scaleCoord = { ...scaleCoord, left: moveX - (clickX - left), width: width + (clickX - moveX), height: height + moveY - clickY, }; break; case 'bottom': scaleCoord = { ...scaleCoord, height: height + moveY - clickY, }; break; case 'bottom-right': scaleCoord = { ...scaleCoord, width: width + moveX - clickX, height: height + moveY - clickY, }; break; case 'right': scaleCoord = { ...scaleCoord, width: width + moveX - clickX, }; break; case 'top-right': scaleCoord = { ...scaleCoord, top: moveY - (clickY - top), width: width + moveX - clickX, height: height + (clickY - moveY), }; break; default: break; } return scaleCoord; }; const FRACTIONDIGITS = 2; type NormalizeRoundFunc = (num: number, fractionDigits?: number) => number; export const normalizeRound: NormalizeRoundFunc = (num, fractionDigits) => { const frac = fractionDigits || FRACTIONDIGITS; return Math.round(num * 10 ** frac) / 10 ** frac; }; const printDocument = (documentId: string): void => { const iframe = document.getElementById(documentId) as HTMLIFrameElement; const doc = iframe.contentWindow as { print: () => void }; if (doc && typeof doc.print === 'undefined') { setTimeout(() => { printDocument(documentId); }, 1000); } else { setTimeout(() => { doc.print(); }, 3000); } }; export const printPdf = async (url: string): Promise => { // eslint-disable-next-line global-require const printJS = require('print-js'); const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob); if (!isIE11) { printJS(url); } else { const wrapper = document.getElementById('embed-wrapper') as HTMLElement; wrapper.style.display = 'flex'; const ele = document.createElement('embed'); ele.setAttribute('type', 'application/pdf'); ele.setAttribute('id', 'pdf-embed'); ele.style.width = '70%'; ele.style.height = '70%'; ele.style.position = 'fixed'; ele.style.top = '0'; ele.style.left = '0'; ele.style.bottom = '0'; ele.style.right = '0'; ele.style.margin = 'auto'; ele.setAttribute('src', url); wrapper.appendChild(ele); printDocument('pdf-embed'); } }; export const strip = (number: number): number => { return parseFloat(parseFloat(number.toString()).toPrecision(12)); };