utility.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /* eslint-disable no-param-reassign */
  2. /* eslint-disable @typescript-eslint/no-var-requires */
  3. /* eslint-disable no-underscore-dangle */
  4. import { fromEvent } from 'rxjs';
  5. import { debounceTime, throttleTime } from 'rxjs/operators';
  6. export const objIsEmpty = (obj: Record<string, any>): boolean =>
  7. !Object.keys(obj).length;
  8. export const watchScroll = (
  9. viewAreaElement: HTMLElement | null,
  10. cb: (state: ScrollStateType) => void
  11. ): ScrollStateType => {
  12. let rAF: number | null = null;
  13. const element = viewAreaElement as HTMLElement;
  14. const state = {
  15. right: true,
  16. down: true,
  17. lastX: element.scrollLeft,
  18. lastY: element.scrollTop,
  19. subscriber: {},
  20. };
  21. const debounceScroll = (): void => {
  22. if (rAF) {
  23. return;
  24. }
  25. // schedule an invocation of scroll for next animation frame.
  26. rAF = window.requestAnimationFrame(() => {
  27. rAF = null;
  28. const currentX = element.scrollLeft;
  29. const { lastX } = state;
  30. if (currentX !== lastX) {
  31. state.right = currentX > lastX;
  32. }
  33. state.lastX = currentX;
  34. const currentY = element.scrollTop;
  35. const { lastY } = state;
  36. if (currentY !== lastY) {
  37. state.down = currentY > lastY;
  38. }
  39. state.lastY = currentY;
  40. cb(state);
  41. });
  42. };
  43. const subscriber = fromEvent(element, 'scroll')
  44. .pipe(throttleTime(100), debounceTime(100))
  45. .subscribe(debounceScroll);
  46. state.subscriber = subscriber;
  47. return state;
  48. };
  49. export const scrollIntoView = (
  50. element: HTMLElement,
  51. spot?: { top: number },
  52. skipOverflowHiddenElements = false
  53. ): void => {
  54. let parent = element.offsetParent as HTMLElement;
  55. let offsetY = element.offsetTop + element.clientTop;
  56. if (!parent) {
  57. return; // no need to scroll
  58. }
  59. while (
  60. (parent.clientHeight === parent.scrollHeight &&
  61. parent.clientWidth === parent.scrollWidth) ||
  62. (skipOverflowHiddenElements &&
  63. getComputedStyle(parent).overflow === 'hidden')
  64. ) {
  65. if (parent.dataset._scaleY) {
  66. offsetY /= parseInt(parent.dataset._scaleY, 10);
  67. }
  68. offsetY += parent.offsetTop;
  69. parent = parent.offsetParent as HTMLElement;
  70. if (!parent) {
  71. return; // no need to scroll
  72. }
  73. }
  74. if (spot) {
  75. if (spot.top !== undefined) {
  76. offsetY += spot.top;
  77. }
  78. }
  79. parent.scrollTop = offsetY;
  80. };
  81. export const scaleCheck = (scale: number): number => {
  82. if (typeof scale === 'number' && scale >= 50 && scale <= 250) {
  83. return Math.round(scale * 100) / 10000;
  84. }
  85. if (scale < 50) {
  86. return 0.5;
  87. }
  88. return 2.5;
  89. };
  90. export const downloadFileWithUri = (name: string, uri: string): void => {
  91. const ele = document.createElement('a');
  92. ele.download = name;
  93. ele.href = uri;
  94. ele.target = '_blank';
  95. document.body.appendChild(ele);
  96. ele.click();
  97. document.body.removeChild(ele);
  98. ele.remove();
  99. };
  100. export const uploadFile = (extension: string): Promise<any> =>
  101. new Promise(resolve => {
  102. const fileInput = document.createElement('input');
  103. fileInput.type = 'file';
  104. fileInput.accept = extension;
  105. fileInput.onchange = (): void => {
  106. if (fileInput.files) {
  107. const reader = new FileReader();
  108. reader.onload = (): void => {
  109. const contents = reader.result;
  110. resolve(contents);
  111. };
  112. if (extension.includes('xfdf')) {
  113. reader.readAsText(fileInput.files[0]);
  114. } else {
  115. reader.readAsDataURL(fileInput.files[0]);
  116. }
  117. }
  118. };
  119. document.body.appendChild(fileInput);
  120. fileInput.click();
  121. });
  122. const componentToHex = (c: number): string => {
  123. const hex = c.toString(16);
  124. return hex.length === 1 ? `0${hex}` : hex;
  125. };
  126. export const hexToRgb = (hex: string): any => {
  127. const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  128. return result
  129. ? {
  130. r: parseInt(result[1], 16),
  131. g: parseInt(result[2], 16),
  132. b: parseInt(result[3], 16),
  133. }
  134. : null;
  135. };
  136. export const rgbToHex = (r: number, g: number, b: number): string =>
  137. `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
  138. export const floatToHex = (r: number, g: number, b: number): string =>
  139. rgbToHex(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));
  140. type ScaleDataType = CoordType & {
  141. operator: string;
  142. clickX: number;
  143. clickY: number;
  144. moveX: number;
  145. moveY: number;
  146. };
  147. export const calcDragAndDropScale = ({
  148. top,
  149. left,
  150. width = 0,
  151. height = 0,
  152. operator,
  153. clickX,
  154. clickY,
  155. moveX,
  156. moveY,
  157. }: ScaleDataType): CoordType => {
  158. let scaleCoord = {
  159. left,
  160. top,
  161. width,
  162. height,
  163. };
  164. switch (operator) {
  165. case 'top':
  166. scaleCoord = {
  167. ...scaleCoord,
  168. top: moveY - (clickY - top),
  169. height: height + (clickY - moveY),
  170. };
  171. break;
  172. case 'top-left':
  173. scaleCoord = {
  174. top: moveY - (clickY - top),
  175. left: moveX - (clickX - left),
  176. height: height + (clickY - moveY),
  177. width: width + (clickX - moveX),
  178. };
  179. break;
  180. case 'left':
  181. scaleCoord = {
  182. ...scaleCoord,
  183. left: moveX - (clickX - left),
  184. width: width + (clickX - moveX),
  185. };
  186. break;
  187. case 'bottom-left':
  188. scaleCoord = {
  189. ...scaleCoord,
  190. left: moveX - (clickX - left),
  191. width: width + (clickX - moveX),
  192. height: height + moveY - clickY,
  193. };
  194. break;
  195. case 'bottom':
  196. scaleCoord = {
  197. ...scaleCoord,
  198. height: height + moveY - clickY,
  199. };
  200. break;
  201. case 'bottom-right':
  202. scaleCoord = {
  203. ...scaleCoord,
  204. width: width + moveX - clickX,
  205. height: height + moveY - clickY,
  206. };
  207. break;
  208. case 'right':
  209. scaleCoord = {
  210. ...scaleCoord,
  211. width: width + moveX - clickX,
  212. };
  213. break;
  214. case 'top-right':
  215. scaleCoord = {
  216. ...scaleCoord,
  217. top: moveY - (clickY - top),
  218. width: width + moveX - clickX,
  219. height: height + (clickY - moveY),
  220. };
  221. break;
  222. default:
  223. break;
  224. }
  225. return scaleCoord;
  226. };
  227. const FRACTIONDIGITS = 2;
  228. type NormalizeRoundFunc = (num: number, fractionDigits?: number) => number;
  229. export const normalizeRound: NormalizeRoundFunc = (num, fractionDigits) => {
  230. const frac = fractionDigits || FRACTIONDIGITS;
  231. return Math.round(num * 10 ** frac) / 10 ** frac;
  232. };
  233. const printDocument = (documentId: string): void => {
  234. const doc = document.getElementById(documentId) as HTMLElement;
  235. // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  236. // @ts-ignore
  237. if (typeof doc.print === 'undefined') {
  238. setTimeout(() => {
  239. printDocument(documentId);
  240. }, 1000);
  241. } else {
  242. setTimeout(() => {
  243. // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  244. // @ts-ignore
  245. doc.print();
  246. }, 3000);
  247. }
  248. };
  249. export const printPdf = async (url: string) => {
  250. // eslint-disable-next-line global-require
  251. const printJS = require('print-js');
  252. const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob);
  253. if (!isIE11) {
  254. printJS(url);
  255. } else {
  256. const wrapper = document.getElementById('embed-wrapper') as HTMLElement;
  257. wrapper.style.display = 'flex';
  258. const ele = document.createElement('embed');
  259. ele.setAttribute('type', 'application/pdf');
  260. ele.setAttribute('id', 'pdf-embed');
  261. ele.style.width = '70%';
  262. ele.style.height = '70%';
  263. ele.style.position = 'fixed';
  264. ele.style.top = '0';
  265. ele.style.left = '0';
  266. ele.style.bottom = '0';
  267. ele.style.right = '0';
  268. ele.style.margin = 'auto';
  269. ele.setAttribute('src', url);
  270. wrapper.appendChild(ele);
  271. printDocument('pdf-embed');
  272. }
  273. };
  274. export const strip = (number: number): number => {
  275. return parseFloat(parseFloat(number.toString()).toPrecision(12));
  276. };