@ngrdt/utils 0.0.5 → 0.0.6

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.
Files changed (58) hide show
  1. package/.swcrc +29 -0
  2. package/eslint.config.js +22 -0
  3. package/jest.config.ts +30 -0
  4. package/package.json +7 -5
  5. package/project.json +29 -0
  6. package/rollup.config.js +20 -0
  7. package/src/index.ts +17 -0
  8. package/src/lib/__test__/array.utils.spec.ts +13 -0
  9. package/src/lib/__test__/css.utils.spec.ts +27 -0
  10. package/src/lib/__test__/date-format.spec.ts +71 -0
  11. package/src/lib/__test__/date.utils.spec.ts +72 -0
  12. package/src/lib/__test__/file-zip.utils.spec.ts +14 -0
  13. package/src/lib/__test__/object.utils.spec.ts +23 -0
  14. package/src/lib/__test__/random.utils.spec.ts +7 -0
  15. package/src/lib/__test__/string.utils.spec.ts +17 -0
  16. package/src/lib/types/aria.ts +334 -0
  17. package/src/lib/types/encodings.ts +273 -0
  18. package/src/lib/types/keyboard.ts +29 -0
  19. package/src/lib/types/mime-types.ts +119 -0
  20. package/src/lib/{router.utils.d.ts → types/router.ts} +1 -1
  21. package/src/lib/types/type.ts +43 -0
  22. package/src/lib/utils/array.ts +30 -0
  23. package/src/lib/utils/css.ts +69 -0
  24. package/src/lib/utils/date-format.ts +580 -0
  25. package/src/lib/utils/date.ts +363 -0
  26. package/src/lib/utils/file.ts +258 -0
  27. package/src/lib/utils/html.ts +26 -0
  28. package/src/lib/utils/model.ts +73 -0
  29. package/src/lib/utils/names.ts +73 -0
  30. package/src/lib/utils/object.ts +58 -0
  31. package/src/lib/utils/random.ts +9 -0
  32. package/src/lib/utils/rxjs.ts +30 -0
  33. package/src/lib/utils/string.ts +123 -0
  34. package/tsconfig.json +22 -0
  35. package/tsconfig.lib.json +11 -0
  36. package/tsconfig.spec.json +14 -0
  37. package/index.cjs.d.ts +0 -1
  38. package/index.cjs.js +0 -1
  39. package/index.esm.d.ts +0 -1
  40. package/index.esm.js +0 -1
  41. package/src/index.d.ts +0 -17
  42. package/src/lib/array.utils.d.ts +0 -6
  43. package/src/lib/color.utils.d.ts +0 -5
  44. package/src/lib/css.utils.d.ts +0 -14
  45. package/src/lib/date-format.d.ts +0 -77
  46. package/src/lib/date.utils.d.ts +0 -60
  47. package/src/lib/encodings.d.ts +0 -44
  48. package/src/lib/file.utils.d.ts +0 -69
  49. package/src/lib/html.utils.d.ts +0 -4
  50. package/src/lib/keyboard.utils.d.ts +0 -36
  51. package/src/lib/mime-types.d.ts +0 -109
  52. package/src/lib/model.utils.d.ts +0 -22
  53. package/src/lib/names.d.ts +0 -19
  54. package/src/lib/object.utils.d.ts +0 -5
  55. package/src/lib/random.utils.d.ts +0 -4
  56. package/src/lib/rxjs.utils.d.ts +0 -6
  57. package/src/lib/string.utils.d.ts +0 -22
  58. package/src/lib/type.utils.d.ts +0 -13
@@ -0,0 +1,363 @@
1
+ import { Nullable } from '../types/type';
2
+
3
+ export class RdtDateUtils {
4
+ static readonly MS = 1;
5
+ static readonly SECOND = 1000 * RdtDateUtils.MS;
6
+ static readonly MINUTE = 60 * RdtDateUtils.SECOND;
7
+ static readonly HOUR = 60 * RdtDateUtils.MINUTE;
8
+ static readonly DAY = 24 * RdtDateUtils.HOUR;
9
+
10
+ /**
11
+ * Returns number of days between two dates.
12
+ * Returns zero in case dates are the same. Ignores time.
13
+ */
14
+ static getDays(a: Date | string | number, b: Date | string | number) {
15
+ return (
16
+ RdtDateUtils.getDayOffset(new Date(a)) -
17
+ RdtDateUtils.getDayOffset(new Date(b))
18
+ );
19
+ }
20
+
21
+ static checkDate(year: number, month: number, day: number) {
22
+ const d = new Date(year, month - 1, day);
23
+ return (
24
+ d.getFullYear() === year &&
25
+ d.getMonth() + 1 === month &&
26
+ d.getDate() === day
27
+ );
28
+ }
29
+
30
+ static isValidDate(value: any): value is string | number | Date {
31
+ if (value == null) {
32
+ return false;
33
+ }
34
+ if (value instanceof Date) {
35
+ return !isNaN(value.getTime());
36
+ }
37
+ return !isNaN(new Date(value).getTime());
38
+ }
39
+
40
+ static startOfDay(input: Date | string) {
41
+ const date = new Date(input);
42
+ date.setHours(0, 0, 0, 0);
43
+ return date;
44
+ }
45
+
46
+ static endOfDay(input: Date | string) {
47
+ const date = new Date(input);
48
+ date.setHours(23, 59, 59);
49
+ return date;
50
+ }
51
+
52
+ static startOfMonth(input: Date | string) {
53
+ const date = new Date(input);
54
+ date.setHours(0, 0, 0, 0);
55
+ date.setDate(1);
56
+ return date;
57
+ }
58
+
59
+ static endOfMonth(input: Date | string) {
60
+ const date = new Date(input);
61
+ date.setHours(23, 59, 59);
62
+ date.setDate(0);
63
+ return date;
64
+ }
65
+
66
+ static startOfYear(year: number) {
67
+ return new Date(year, Month.January, 1);
68
+ }
69
+
70
+ static endOfYear(year: number) {
71
+ return new Date(year, Month.December, 31);
72
+ }
73
+
74
+ static beginningOfNextMonth() {
75
+ const today = new Date();
76
+ if (today.getMonth() === 11) {
77
+ return new Date(today.getFullYear() + 1, 0, 1);
78
+ } else {
79
+ return new Date(today.getFullYear(), today.getMonth() + 1, 1);
80
+ }
81
+ }
82
+
83
+ static endOfNextMonth() {
84
+ const today = new Date();
85
+ if (today.getMonth() === 10) {
86
+ const firstDayOfInTwoMonths = new Date(today.getFullYear() + 1, 0, 1);
87
+ return this.minusOneDay(firstDayOfInTwoMonths);
88
+ } else if (today.getMonth() === 11) {
89
+ const firstDayOfInTwoMonths = new Date(today.getFullYear() + 1, 1, 1);
90
+ return this.minusOneDay(firstDayOfInTwoMonths);
91
+ } else {
92
+ const firstDayOfInTwoMonths = new Date(
93
+ today.getFullYear(),
94
+ today.getMonth() + 2,
95
+ 1
96
+ );
97
+ return this.minusOneDay(firstDayOfInTwoMonths);
98
+ }
99
+ }
100
+
101
+ static beginningOfInThreeMonths() {
102
+ const today = new Date();
103
+ if (today.getMonth() === 10) {
104
+ return new Date(today.getFullYear() + 1, 1, 1);
105
+ } else if (today.getMonth() === 11) {
106
+ return new Date(today.getFullYear() + 1, 2, 1);
107
+ } else {
108
+ return new Date(today.getFullYear(), today.getMonth() + 3, 1);
109
+ }
110
+ }
111
+
112
+ static endOfInNextThreeMonths() {
113
+ const today = new Date();
114
+ if (today.getMonth() === 9) {
115
+ const firstDayOfInFourMonths = new Date(today.getFullYear() + 1, 1, 1);
116
+ return this.minusOneDay(firstDayOfInFourMonths);
117
+ } else if (today.getMonth() === 10) {
118
+ const firstDayOfInFourMonths = new Date(today.getFullYear() + 1, 2, 1);
119
+ return this.minusOneDay(firstDayOfInFourMonths);
120
+ } else if (today.getMonth() === 11) {
121
+ const firstDayOfInFourMonths = new Date(today.getFullYear() + 1, 3, 1);
122
+ return this.minusOneDay(firstDayOfInFourMonths);
123
+ } else {
124
+ const firstDayOfInFourMonths = new Date(
125
+ today.getFullYear(),
126
+ today.getMonth() + 4,
127
+ 1
128
+ );
129
+ return this.minusOneDay(firstDayOfInFourMonths);
130
+ }
131
+ }
132
+
133
+ static minusOneDay(date: Date) {
134
+ return new Date(date.getTime() - RdtDateUtils.DAY);
135
+ }
136
+
137
+ static toISOLocal(d: Date) {
138
+ const year = d.getFullYear();
139
+ const month = `${d.getMonth() + 1}`.padStart(2, '0');
140
+ const day = `${d.getDate()}`.padStart(2, '0');
141
+
142
+ const hours = `${d.getHours()}`.padStart(2, '0');
143
+ const minutes = `${d.getMinutes()}`.padStart(2, '0');
144
+ const seconds = `${d.getSeconds()}`.padStart(2, '0');
145
+
146
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
147
+ }
148
+
149
+ static formatDate(date: any) {
150
+ if (date === null || date === undefined || date === '') {
151
+ return '';
152
+ }
153
+
154
+ const d = date instanceof Date ? date : new Date(date);
155
+
156
+ if (RdtDateUtils.isValidDate(d)) {
157
+ const year = d.getFullYear();
158
+ const month = `${d.getMonth() + 1}`.padStart(2, '0');
159
+ const day = `${d.getDate()}`.padStart(2, '0');
160
+
161
+ return `${day}.${month}.${year}`;
162
+ } else {
163
+ return '';
164
+ }
165
+ }
166
+
167
+ static timeToDate(time: string) {
168
+ const date = new Date(0);
169
+ if (RdtDateUtils.hhMmSsMsRegex.test(time)) {
170
+ const matches = time.match(RdtDateUtils.hhMmSsMsRegex);
171
+ if (matches) {
172
+ const h = parseInt(matches[1]);
173
+ const m = parseInt(matches[2]);
174
+ const s = parseInt(matches[3]);
175
+ const ms = parseInt(matches[4]);
176
+ date.setHours(h, m, s, ms);
177
+ return date;
178
+ }
179
+ } else if (RdtDateUtils.hhMmSsRegex.test(time)) {
180
+ const matches = time.match(RdtDateUtils.hhMmSsRegex);
181
+ if (matches) {
182
+ const h = parseInt(matches[1]);
183
+ const m = parseInt(matches[2]);
184
+ const s = parseInt(matches[3]);
185
+ date.setHours(h, m, s);
186
+ return date;
187
+ }
188
+ } else if (RdtDateUtils.hhMmRegex.test(time)) {
189
+ const matches = time.match(RdtDateUtils.hhMmRegex);
190
+ if (matches) {
191
+ const h = parseInt(matches[1]);
192
+ const m = parseInt(matches[2]);
193
+ date.setHours(h, m);
194
+ return date;
195
+ }
196
+ }
197
+ return null;
198
+ }
199
+
200
+ static setTime(date: Date, time: string) {
201
+ const newDate = new Date(date);
202
+ const timeDate = RdtDateUtils.timeToDate(time);
203
+ if (timeDate) {
204
+ newDate.setHours(
205
+ timeDate.getHours(),
206
+ timeDate.getMinutes(),
207
+ timeDate.getSeconds(),
208
+ timeDate.getMilliseconds()
209
+ );
210
+ }
211
+ return newDate;
212
+ }
213
+
214
+ static getTime(d: Date, showSeconds = true) {
215
+ const hh = d.getHours().toString().padStart(2, '0');
216
+ const mm = d.getMinutes().toString().padStart(2, '0');
217
+
218
+ if (showSeconds) {
219
+ const ss = d.getSeconds().toString().padStart(2, '0');
220
+ return `${hh}:${mm}:${ss}`;
221
+ } else {
222
+ return `${hh}:${mm}`;
223
+ }
224
+ }
225
+
226
+ static parseToDate(input: Nullable<string | Date>): Date | null {
227
+ if (input instanceof Date) {
228
+ return RdtDateUtils.isValidDate(input) ? input : null;
229
+ }
230
+ if (typeof input === 'string') {
231
+ const date = new Date(input);
232
+ if (RdtDateUtils.isValidDate(date)) {
233
+ return date;
234
+ }
235
+ }
236
+ return null;
237
+ }
238
+
239
+ static parse(input: Nullable<string | Date>): number | null {
240
+ if (input instanceof Date) {
241
+ return RdtDateUtils.isValidDate(input) ? input.getTime() : null;
242
+ }
243
+ if (typeof input === 'string') {
244
+ const date = new Date(input);
245
+ if (RdtDateUtils.isValidDate(date)) {
246
+ return date.getTime();
247
+ }
248
+ }
249
+ return null;
250
+ }
251
+
252
+ static isEqual(d1: Date | null, d2: Date | null) {
253
+ if (d1 === d2) {
254
+ return true;
255
+ }
256
+ if (!d1 || !d2) {
257
+ return false;
258
+ }
259
+ return d1.getTime() === d2.getTime();
260
+ }
261
+
262
+ static isLeapYear(year: number) {
263
+ return new Date(year, 1, 29).getDate() === 29;
264
+ }
265
+
266
+ static getDaysInYear(year: number) {
267
+ return RdtDateUtils.isLeapYear(year) ? 366 : 365;
268
+ }
269
+
270
+ private static hhMmRegex =
271
+ /^\s*([0-9]|0[0-9]|1[0-9]|2[0-3])\s*\W+\s*([0-5]?[0-9])\s*$/;
272
+ private static hhMmSsRegex =
273
+ /^\s*([0-9]|0[0-9]|1[0-9]|2[0-3])\s*\W+\s*([0-5]?[0-9])\s*\W+\s*([0-5]?\d)\s*$/;
274
+ private static hhMmSsMsRegex =
275
+ /^\s*([0-9]|0[0-9]|1[0-9]|2[0-3])\s*\W+\s*([0-5]?[0-9])\s*\W+\s*([0-5]?\d)\s*[\W|\s]\s*(\d{1,3})\s*$/;
276
+
277
+ private static daysUpToMonth = [
278
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
279
+ ];
280
+ private static daysUpToMonthLeapYear = [
281
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
282
+ ];
283
+
284
+ private static getDayOffset(date: Date) {
285
+ const year = date.getFullYear() - 1;
286
+ const month = date.getMonth() + 1,
287
+ day = date.getDate();
288
+ const numOfLeapsYear =
289
+ Math.trunc(year / 4) - Math.trunc(year / 100) + Math.trunc(year / 400);
290
+
291
+ if (RdtDateUtils.isLeapYear(year + 1)) {
292
+ return (
293
+ year * 365 +
294
+ numOfLeapsYear +
295
+ RdtDateUtils.daysUpToMonthLeapYear[month - 1] +
296
+ day -
297
+ 1
298
+ );
299
+ } else {
300
+ return (
301
+ year * 365 +
302
+ numOfLeapsYear +
303
+ RdtDateUtils.daysUpToMonth[month - 1] +
304
+ day -
305
+ 1
306
+ );
307
+ }
308
+ }
309
+
310
+ static doubleDigitYearToPast(year2: number) {
311
+ // If current year is 2024 and input is 24 or less, then result is 2024
312
+ // If input is 25 or greater, then result is 1925-1999
313
+ const currentYear = RdtDateUtils.getCurrentYear();
314
+ const current2 = currentYear % 100;
315
+ if (year2 <= current2) {
316
+ return year2 + currentYear - current2;
317
+ } else {
318
+ return year2 + currentYear - current2 - 100;
319
+ }
320
+ }
321
+
322
+ static doubleDigitYearToFuture(year2: number) {
323
+ // If current year is 2024 and input is 24 or less, then result is 2124
324
+ // If input is 25 or greater, then result is 2025-2099
325
+ const currentYear = RdtDateUtils.getCurrentYear();
326
+ const current2 = currentYear % 100;
327
+ if (year2 >= current2) {
328
+ return year2 + currentYear - current2;
329
+ } else {
330
+ return year2 + currentYear - current2 + 100;
331
+ }
332
+ }
333
+
334
+ static getCurrentYear() {
335
+ return new Date().getFullYear();
336
+ }
337
+
338
+ static calculateAge(birthDateStr: string, referenceDateStr: string): number {
339
+ const birthDate = new Date(birthDateStr);
340
+ const referenceDate = new Date(referenceDateStr);
341
+ let age = referenceDate.getFullYear() - birthDate.getFullYear();
342
+ const monthDifference = referenceDate.getMonth() - birthDate.getMonth();
343
+ const dayDifference = referenceDate.getDate() - birthDate.getDate();
344
+ if (monthDifference < 0 || (monthDifference === 0 && dayDifference < 0)) {
345
+ age--;
346
+ }
347
+ return age;
348
+ }
349
+ }
350
+ export enum Month {
351
+ January = 0,
352
+ February = 1,
353
+ March = 2,
354
+ April = 3,
355
+ May = 4,
356
+ June = 5,
357
+ July = 6,
358
+ August = 7,
359
+ September = 8,
360
+ October = 9,
361
+ November = 10,
362
+ December = 11,
363
+ }
@@ -0,0 +1,258 @@
1
+ import { HttpResponse } from '@angular/common/http';
2
+ import { RdtEncoding } from '../types/encodings';
3
+ import {
4
+ EXTENSION_BY_MIME_TYPE,
5
+ MIME_TYPE_BY_EXTENSION,
6
+ RdtMimeType,
7
+ } from '../types/mime-types';
8
+ import { Nullable } from '../types/type';
9
+
10
+ export enum RdtMsOfficeAction {
11
+ Edit = 'ofe|u|',
12
+ View = 'ofv|u|',
13
+ }
14
+
15
+ export enum RdtMsOfficeApp {
16
+ Word = 'ms-word',
17
+ Excel = 'ms-excel',
18
+ PowerPoint = 'ms-powerpoint',
19
+ }
20
+
21
+ export interface RdtMsOfficeConfig {
22
+ mimeType?: RdtMimeType;
23
+ app?: RdtMsOfficeApp;
24
+ action?: RdtMsOfficeAction;
25
+ }
26
+
27
+ export interface RdtDocumentFile<T = string> {
28
+ fileName: string;
29
+ content?: T;
30
+ url?: string;
31
+ mimeType?: RdtMimeType;
32
+ size?: number;
33
+ modified?: Date;
34
+ originalFile?: File;
35
+ }
36
+
37
+ export interface DokumentProtokol {
38
+ DokumentProtokol: {
39
+ filename: string;
40
+ content_data: string;
41
+ guid: string;
42
+ code: string;
43
+ };
44
+ }
45
+
46
+ export const Rdt_DEFAULT_MIME_TYPE = RdtMimeType.BIN;
47
+ export const Rdt_DEFAULT_MS_OFFICE_ACTION = RdtMsOfficeAction.View;
48
+
49
+ export enum FileSizeUnit {
50
+ B = 'B',
51
+ KB = 'KB',
52
+ MB = 'MB',
53
+ GB = 'GB',
54
+ TB = 'TB',
55
+ PB = 'PB',
56
+ EB = 'EB',
57
+ ZB = 'ZB',
58
+ YB = 'YB',
59
+ }
60
+
61
+ export type RdtFileSize = `${number} ${FileSizeUnit}`;
62
+
63
+ const SORTED_FILE_SIZE_UNITS = [
64
+ FileSizeUnit.B,
65
+ FileSizeUnit.KB,
66
+ FileSizeUnit.MB,
67
+ FileSizeUnit.GB,
68
+ FileSizeUnit.TB,
69
+ FileSizeUnit.PB,
70
+ FileSizeUnit.EB,
71
+ FileSizeUnit.ZB,
72
+ FileSizeUnit.YB,
73
+ ] as const;
74
+
75
+ export class FileUtils {
76
+ static getMsOfficeLink(url: string, config: RdtMsOfficeConfig) {
77
+ const app =
78
+ config.app ?? FileUtils.getMsOfficeAppByMimeType(config.mimeType);
79
+ if (app) {
80
+ const action = config.action ?? Rdt_DEFAULT_MS_OFFICE_ACTION;
81
+ return `${app}:${action}${url}`;
82
+ } else {
83
+ return url;
84
+ }
85
+ }
86
+
87
+ static openFileFromRemoteUrl(url: string, config: RdtMsOfficeConfig) {
88
+ window.open(FileUtils.getMsOfficeLink(url, config));
89
+ }
90
+
91
+ static openFileInBrowser(stringData: string, mimeType?: RdtMimeType) {
92
+ const link = FileUtils.getBase64Link(stringData, mimeType);
93
+ window.open(link, '_blank');
94
+ }
95
+
96
+ static fileAsArrayBuffer(file: Blob): Promise<ArrayBuffer> {
97
+ return new Promise((resolve, reject) => {
98
+ if (!file) {
99
+ reject(null);
100
+ }
101
+ const reader = new FileReader();
102
+ reader.onload = () => resolve(reader.result as ArrayBuffer);
103
+ reader.onerror = reject;
104
+ reader.readAsArrayBuffer(file);
105
+ });
106
+ }
107
+
108
+ static fileAsText(file: Blob, encoding?: RdtEncoding): Promise<string> {
109
+ return new Promise((resolve, reject) => {
110
+ if (!file) {
111
+ reject(null);
112
+ }
113
+ const reader = new FileReader();
114
+ reader.onload = () => resolve(reader.result as string);
115
+ reader.onerror = reject;
116
+ reader.readAsText(file, encoding);
117
+ });
118
+ }
119
+
120
+ static downloadFileFromData(
121
+ stringData: string,
122
+ filename: string,
123
+ mimeType?: RdtMimeType
124
+ ): void {
125
+ mimeType =
126
+ mimeType ??
127
+ FileUtils.getMimeTypeFromFileName(filename) ??
128
+ FileUtils.getMimeTypeFromBase64(stringData);
129
+
130
+ const url = FileUtils.getBase64Link(stringData, mimeType);
131
+
132
+ this.downloadFileFromRemoteUrl(url, filename, mimeType);
133
+ }
134
+
135
+ static downloadFileFromRemoteUrl(
136
+ url: string,
137
+ filename: string,
138
+ mimeType?: RdtMimeType
139
+ ): void {
140
+ mimeType = mimeType ?? FileUtils.getMimeTypeFromFileName(filename);
141
+
142
+ const link = document.createElement('a');
143
+ link.href = url;
144
+ link.download = FileUtils.getFileName(filename, mimeType);
145
+ link.click();
146
+ }
147
+
148
+ static getMimeTypeFromBase64(base64Data: string) {
149
+ return base64Data.match(/data:(.*?);base64/)?.[1] as
150
+ | RdtMimeType
151
+ | undefined;
152
+ }
153
+
154
+ static getMimeTypeFromFileName(fileName: Nullable<string>): RdtMimeType {
155
+ const ext = fileName?.split('.').pop();
156
+ // @ts-ignore
157
+ return MIME_TYPE_BY_EXTENSION[ext] ?? Rdt_DEFAULT_MIME_TYPE;
158
+ }
159
+
160
+ static getFileName(filename: string, mimeType?: RdtMimeType) {
161
+ if (mimeType) {
162
+ const ext = EXTENSION_BY_MIME_TYPE[mimeType];
163
+ if (filename.endsWith(`.${ext}`)) {
164
+ return filename;
165
+ } else {
166
+ return `${filename}.${ext}`;
167
+ }
168
+ } else {
169
+ return filename;
170
+ }
171
+ }
172
+
173
+ static getBase64Link(base64Data: string, mimeType?: RdtMimeType) {
174
+ if (base64Data.startsWith('data:')) {
175
+ return base64Data;
176
+ } else if (mimeType) {
177
+ return `data:${mimeType};base64,${base64Data}`;
178
+ } else {
179
+ console.error('Missing mime type for base64 data.');
180
+ return '';
181
+ }
182
+ }
183
+
184
+ static getMimeTypeFromResponse(resp: HttpResponse<Blob>) {
185
+ return resp.body?.type as RdtMimeType;
186
+ }
187
+
188
+ static getFileNameFromResponse(resp: HttpResponse<Blob>): string {
189
+ return (
190
+ resp.headers
191
+ .get('content-disposition')
192
+ ?.split('filename=')?.[1]
193
+ ?.split(';')?.[0] ?? ''
194
+ );
195
+ }
196
+
197
+ static async blobToDataUrl(file: Blob): Promise<string> {
198
+ return new Promise((resolve, reject) => {
199
+ if (!file) {
200
+ reject(null);
201
+ }
202
+ const reader = new FileReader();
203
+ reader.onload = () => resolve(reader.result as string);
204
+ reader.onerror = reject;
205
+ reader.readAsDataURL(file);
206
+ });
207
+ }
208
+
209
+ static convertFromBytes(bytes: number, decimals = 2) {
210
+ if (bytes === 0) {
211
+ return '0 ' + FileSizeUnit.B;
212
+ }
213
+ const k = 1024;
214
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
215
+ return (
216
+ parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) +
217
+ ' ' +
218
+ SORTED_FILE_SIZE_UNITS[i]
219
+ );
220
+ }
221
+
222
+ static convertToBytes(amount: number, unit: FileSizeUnit) {
223
+ const k = 1024;
224
+ const i = SORTED_FILE_SIZE_UNITS.indexOf(unit);
225
+ return amount * Math.pow(k, i);
226
+ }
227
+
228
+ static fileSizeToBytes(fileSize: RdtFileSize) {
229
+ const [amountStr, unitStr] = fileSize.split(' ');
230
+ const amount = parseFloat(amountStr);
231
+ const unit = unitStr as FileSizeUnit;
232
+ if (isNaN(amount) || SORTED_FILE_SIZE_UNITS.indexOf(unit) === -1) {
233
+ return null;
234
+ }
235
+ return FileUtils.convertToBytes(amount, unit);
236
+ }
237
+
238
+ static getMsOfficeAppByMimeType(
239
+ mimeType: RdtMimeType | undefined
240
+ ): RdtMsOfficeApp | null {
241
+ switch (mimeType) {
242
+ case RdtMimeType.DOC:
243
+ case RdtMimeType.DOCX:
244
+ case RdtMimeType.ODT:
245
+ return RdtMsOfficeApp.Word;
246
+ case RdtMimeType.XLS:
247
+ case RdtMimeType.XLSX:
248
+ case RdtMimeType.ODS:
249
+ return RdtMsOfficeApp.Excel;
250
+ case RdtMimeType.PPT:
251
+ case RdtMimeType.PPTX:
252
+ case RdtMimeType.ODP:
253
+ return RdtMsOfficeApp.PowerPoint;
254
+ default:
255
+ return null;
256
+ }
257
+ }
258
+ }
@@ -0,0 +1,26 @@
1
+ export class RdtHTMLUtils {
2
+ static scrollIntoViewHorizontallyWithinParent(element: HTMLElement) {
3
+ const container = element.parentElement;
4
+ if (!container) {
5
+ return;
6
+ }
7
+ const elOffset = element.offsetLeft;
8
+ const elWidth = element.offsetWidth;
9
+ const containerWidth = container.offsetWidth;
10
+ const currentScroll = container.scrollLeft;
11
+
12
+ const alignLeft = elOffset;
13
+ const alignRight = elOffset - containerWidth + elWidth;
14
+
15
+ if (currentScroll > alignLeft) {
16
+ container.scrollLeft = alignLeft;
17
+ } else if (currentScroll < alignRight) {
18
+ container.scrollLeft = alignRight;
19
+ }
20
+ }
21
+
22
+ static setCaretPosition(element: HTMLInputElement, index: number) {
23
+ element.focus();
24
+ element.setSelectionRange(index, index);
25
+ }
26
+ }
@@ -0,0 +1,73 @@
1
+ import { names } from './names';
2
+
3
+ const BOOLEAN_KEYWORDS = ['je', 'lze'];
4
+ const ID_KEYWORD = 'id';
5
+
6
+ export class RdtModelUtils {
7
+ static inferPropertyInfo(
8
+ properties: { key: string; type: string }[],
9
+ interfaceName: string
10
+ ) {
11
+ // Lowercase words that interface name consists of
12
+ const words = names(interfaceName).fileName.split('-');
13
+ const wordMap: { [key: string]: number } = {};
14
+ words.forEach((w) => (wordMap[w] = 1));
15
+ let maxMatches = 0;
16
+ // Primary key is property that shares the most words with interface name
17
+ // while containing the word 'id'.
18
+ // If more such properties exist, the one with shortest name is selected.
19
+ let primaryKey = '';
20
+
21
+ properties.forEach(({ key }) => {
22
+ const propertyNameWords = names(key).fileName.split('-');
23
+ let matches = 0;
24
+ propertyNameWords.forEach((w) => (matches += wordMap[w] ?? 0));
25
+ const isId = propertyNameWords.includes(ID_KEYWORD);
26
+
27
+ if (matches > maxMatches && isId) {
28
+ maxMatches = matches;
29
+ primaryKey = key;
30
+ } else if (matches === maxMatches && isId) {
31
+ if (key.length < primaryKey.length) {
32
+ primaryKey = key;
33
+ }
34
+ }
35
+ });
36
+
37
+ const res: RdtModelProperty[] = properties.map((pair) => {
38
+ const propertyNameWords = names(pair.key).fileName.split('-');
39
+ const isId = propertyNameWords.includes(ID_KEYWORD);
40
+ return {
41
+ key: pair.key,
42
+ type: pair.type as any,
43
+ required: true,
44
+ formControlType: pair.type === 'date' ? 'string' : (pair.type as any),
45
+ primaryKey: pair.key === primaryKey,
46
+ // Any other property that contains ID, but is not primary key is foreign key.
47
+ // Foreign keys are to be ignored.
48
+ foreignKey: isId && pair.key !== primaryKey,
49
+ // If the property starts with the word 'je' and it's numeric,
50
+ // then it's probably boolean.
51
+ probablyBoolean:
52
+ BOOLEAN_KEYWORDS.includes(propertyNameWords[0]) &&
53
+ pair.type === 'number',
54
+ };
55
+ });
56
+
57
+ return res;
58
+ }
59
+
60
+ static names(name: string) {
61
+ return names(name);
62
+ }
63
+ }
64
+
65
+ export interface RdtModelProperty {
66
+ key: string;
67
+ type: 'string' | 'number' | 'date' | 'boolean' | 'array' | 'object' | 'any';
68
+ formControlType: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'any';
69
+ required: boolean;
70
+ foreignKey: boolean;
71
+ primaryKey: boolean;
72
+ probablyBoolean: boolean;
73
+ }