@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,580 @@
1
+ import { RdtDateUtils } from './date';
2
+ import { RdtHTMLUtils } from './html';
3
+ import { RdtStringUtils } from './string';
4
+
5
+ interface RdtNumericNode {
6
+ type: RdtFormatType.Day | RdtFormatType.Month | RdtFormatType.Year;
7
+ maxLength: number;
8
+ minLength: number;
9
+ }
10
+
11
+ interface RdtSeparatorNode {
12
+ type: RdtFormatType.Constant;
13
+ value: string;
14
+ }
15
+
16
+ type RdtFormatNode = RdtSeparatorNode | RdtNumericNode;
17
+
18
+ enum RdtFormatType {
19
+ Day = 'day',
20
+ Month = 'month',
21
+ Year = 'year',
22
+ Constant = 'constant',
23
+ }
24
+
25
+ export const PRIMENG_DATE_FORMAT_META: Record<string, RdtNumericNode> = {
26
+ d: {
27
+ type: RdtFormatType.Day,
28
+ minLength: 1,
29
+ maxLength: 2,
30
+ },
31
+ dd: {
32
+ type: RdtFormatType.Day,
33
+ minLength: 2,
34
+ maxLength: 2,
35
+ },
36
+ m: {
37
+ type: RdtFormatType.Month,
38
+ minLength: 1,
39
+ maxLength: 2,
40
+ },
41
+ mm: {
42
+ type: RdtFormatType.Month,
43
+ minLength: 2,
44
+ maxLength: 2,
45
+ },
46
+ y: {
47
+ type: RdtFormatType.Year,
48
+ minLength: 2,
49
+ maxLength: 2,
50
+ },
51
+ yy: {
52
+ type: RdtFormatType.Year,
53
+ minLength: 4,
54
+ maxLength: 4,
55
+ },
56
+ };
57
+
58
+ export interface RdtDateInputConfig {
59
+ format: string;
60
+ yearInputMode: RdtYearInputMode;
61
+ twoDigitYearInputStrategy: RdtTwoDigitYearInputConversionStrategy;
62
+ leadingZeroMode: RdtLeadingZeroInputMode;
63
+ symbolMeta: Record<string, RdtNumericNode>;
64
+ insertMode: RdtInsertMode;
65
+ }
66
+
67
+ export enum RdtInsertMode {
68
+ Overwrite,
69
+ Shift,
70
+ }
71
+
72
+ export enum RdtPasteMode {
73
+ Replace,
74
+ Insert,
75
+ }
76
+
77
+ export enum RdtYearInputMode {
78
+ FourDigit = 0b01,
79
+ TwoDigit = 0b10,
80
+ Both = 0b11,
81
+ }
82
+
83
+ export enum RdtLeadingZeroInputMode {
84
+ NoLeadingZero = 0b01,
85
+ LeadingZero = 0b10,
86
+ Both = 0b11,
87
+ }
88
+
89
+ export type RdtTwoDigitYearInputConversionStrategy = 'past' | 'future';
90
+
91
+ function getDigitCount(value: number) {
92
+ return (Math.log10(value) + 1) | 0;
93
+ }
94
+
95
+ export class RdtDateParser {
96
+ private cfg: RdtDateInputConfig;
97
+ private _value: Date | null;
98
+ private _inputValue: string;
99
+ private _parsedFormat: RdtFormatNode[];
100
+ private deleting: boolean;
101
+ private caretPosition: number;
102
+
103
+ constructor(cfg: RdtDateInputConfig) {
104
+ this.cfg = cfg;
105
+ this._inputValue = '';
106
+ this._value = null;
107
+ this._parsedFormat = this.parseFormat(cfg);
108
+ this.deleting = false;
109
+ this.caretPosition = 0;
110
+ }
111
+
112
+ set leadingZeroMode(mode: RdtLeadingZeroInputMode) {
113
+ this.cfg.leadingZeroMode = mode;
114
+ this._parsedFormat = this.parseFormat(this.cfg);
115
+ }
116
+ get leadingZeroMode() {
117
+ return this.cfg.leadingZeroMode;
118
+ }
119
+
120
+ set yearInputMode(mode: RdtYearInputMode) {
121
+ this.cfg.yearInputMode = mode;
122
+ this._parsedFormat = this.parseFormat(this.cfg);
123
+ }
124
+ get yearInputMode() {
125
+ return this.cfg.yearInputMode;
126
+ }
127
+
128
+ set twoDigitYearInputStrategy(
129
+ strategy: RdtTwoDigitYearInputConversionStrategy
130
+ ) {
131
+ this.cfg.twoDigitYearInputStrategy = strategy;
132
+ this._parsedFormat = this.parseFormat(this.cfg);
133
+ }
134
+ get twoDigitYearInputStrategy() {
135
+ return this.cfg.twoDigitYearInputStrategy;
136
+ }
137
+
138
+ set insertMode(mode: RdtInsertMode) {
139
+ this.cfg.insertMode = mode;
140
+ this._parsedFormat = this.parseFormat(this.cfg);
141
+ }
142
+ get insertMode() {
143
+ return this.cfg.insertMode;
144
+ }
145
+
146
+ set format(format: string) {
147
+ this.cfg.format = format;
148
+ this._parsedFormat = this.parseFormat(this.cfg);
149
+ }
150
+
151
+ get format() {
152
+ return this.cfg.format;
153
+ }
154
+
155
+ onKeyDown(event: KeyboardEvent) {
156
+ if (event.key === 'Backspace') {
157
+ this.deleting = true;
158
+ }
159
+ const target = event.target as HTMLInputElement;
160
+ this.caretPosition = target.selectionStart ?? target.value.length - 1;
161
+ }
162
+
163
+ onKeyUp(event: KeyboardEvent) {
164
+ if (event.key === 'Backspace') {
165
+ this.deleting = false;
166
+ }
167
+ }
168
+
169
+ onInput(event: InputEvent) {
170
+ const target = event.target as HTMLInputElement;
171
+ const value = target.value;
172
+ const caretPosition = target.selectionStart ?? value.length - 1;
173
+ const res = this.parse(value, caretPosition);
174
+ target.value = res.prettyInput;
175
+ RdtHTMLUtils.setCaretPosition(target, res.caret);
176
+ this._value = res.date;
177
+ this._inputValue = res.prettyInput;
178
+ this.caretPosition = res.caret;
179
+ }
180
+
181
+ get value() {
182
+ return this._value;
183
+ }
184
+
185
+ set value(d: Date | null) {
186
+ if (!(d instanceof Date)) {
187
+ this._value = null;
188
+ this._inputValue = '';
189
+ return;
190
+ }
191
+ let result = '';
192
+
193
+ this._parsedFormat.forEach((node) => {
194
+ switch (node.type) {
195
+ case RdtFormatType.Constant:
196
+ result += node.value;
197
+ break;
198
+ case RdtFormatType.Day:
199
+ result += `${d.getDate()}`.padStart(node.minLength, '0');
200
+ break;
201
+ case RdtFormatType.Month:
202
+ result += `${d.getMonth() + 1}`.padStart(node.minLength, '0');
203
+ break;
204
+ case RdtFormatType.Year:
205
+ result += `${d.getFullYear() % 10 ** node.maxLength}`.padStart(
206
+ node.minLength,
207
+ '0'
208
+ );
209
+ break;
210
+ }
211
+ });
212
+ this._value = d;
213
+ this._inputValue = result;
214
+ }
215
+
216
+ set inputValue(value: string) {
217
+ this._inputValue = value;
218
+ }
219
+
220
+ get inputValue() {
221
+ return this._inputValue;
222
+ }
223
+
224
+ parse(
225
+ input: string,
226
+ caretPosition = this.caretPosition,
227
+ deleting = this.deleting
228
+ ) {
229
+ let rest = input;
230
+ let day: number | null = null,
231
+ month: number | null = null,
232
+ year: number | null = null;
233
+
234
+ let output = '';
235
+ let targetCaret = caretPosition;
236
+ let allComplete = true;
237
+ let ambiguous = false;
238
+
239
+ for (let i = 0; i < this._parsedFormat.length; i++) {
240
+ if (rest.length === 0) {
241
+ break;
242
+ }
243
+ const node = this._parsedFormat[i];
244
+
245
+ const firstNumber = rest.search(/\d/);
246
+
247
+ if (node.type === RdtFormatType.Constant) {
248
+ if (firstNumber > 0) {
249
+ const value = deleting
250
+ ? rest.substring(0, node.value.length)
251
+ : rest.substring(0, firstNumber);
252
+ // In case we are deleting from separator, delete previous number as well
253
+ if (deleting && value !== node.value) {
254
+ if (rest.length === input.length) {
255
+ continue;
256
+ } else {
257
+ targetCaret -= node.value.length - value.length;
258
+ }
259
+ }
260
+ rest = rest.substring(firstNumber);
261
+ }
262
+ output += node.value;
263
+ } else {
264
+ if (firstNumber < 0) {
265
+ break;
266
+ }
267
+
268
+ if (targetCaret > output.length) {
269
+ allComplete = true;
270
+ }
271
+
272
+ if (node.type === RdtFormatType.Year) {
273
+ if (
274
+ this.cfg.yearInputMode & RdtYearInputMode.FourDigit &&
275
+ (rest.match(/^\d{3,4}/) ||
276
+ (rest.match(/^\d{2}$/) && i !== this._parsedFormat.length - 1))
277
+ ) {
278
+ if (
279
+ !deleting &&
280
+ this.cfg.insertMode === RdtInsertMode.Overwrite &&
281
+ rest.match(/^\d{5}/) &&
282
+ caretPosition < rest.length &&
283
+ caretPosition - output.length <= 5 &&
284
+ RdtStringUtils.isNumericCharacter(rest[caretPosition])
285
+ ) {
286
+ rest =
287
+ rest.substring(0, caretPosition) +
288
+ rest.substring(caretPosition + 1);
289
+ }
290
+ if (rest.match(/^\d{4}/)) {
291
+ const strVal = rest.substring(0, 4);
292
+ year = parseInt(strVal);
293
+ rest = rest.substring(4);
294
+ output += strVal;
295
+ if (!deleting) {
296
+ ({ rest, targetCaret } = this.useUpNumbersUntilNextSeparator(
297
+ rest,
298
+ targetCaret,
299
+ true,
300
+ deleting
301
+ ));
302
+ }
303
+ } else if (rest.match(/^\d{3}/)) {
304
+ output += rest.substring(0, 3);
305
+ rest = rest.substring(3);
306
+ if (output.length <= targetCaret) {
307
+ allComplete = false;
308
+ }
309
+ } else {
310
+ output = rest.substring(0, 2);
311
+ rest = rest.substring(2);
312
+ if (output.length <= targetCaret) {
313
+ allComplete = false;
314
+ }
315
+ }
316
+ } else if (
317
+ this.cfg.yearInputMode & RdtYearInputMode.TwoDigit &&
318
+ rest.match(/^\d{2}/) &&
319
+ (this.containsNonNumericCharacters(rest.substring(2)) ||
320
+ i === this._parsedFormat.length - 1)
321
+ ) {
322
+ const strVal = rest.substring(0, 2);
323
+ const y2 = parseInt(strVal);
324
+ if (this.cfg.twoDigitYearInputStrategy === 'past') {
325
+ year = RdtDateUtils.doubleDigitYearToPast(y2);
326
+ } else {
327
+ year = RdtDateUtils.doubleDigitYearToFuture(y2);
328
+ }
329
+ rest = rest.substring(2);
330
+ if (!deleting) {
331
+ ({ rest, targetCaret } = this.useUpNumbersUntilNextSeparator(
332
+ rest,
333
+ targetCaret,
334
+ true,
335
+ deleting
336
+ ));
337
+ }
338
+ output += strVal;
339
+ if (
340
+ this.cfg.yearInputMode & RdtYearInputMode.FourDigit &&
341
+ i === this._parsedFormat.length - 1
342
+ ) {
343
+ ambiguous = true;
344
+ }
345
+ } else if (rest.match(/^\d/)) {
346
+ output += rest[0];
347
+ rest = rest.substring(1);
348
+ if (output.length <= targetCaret) {
349
+ allComplete = false;
350
+ }
351
+ }
352
+ } else if (node.type === RdtFormatType.Month) {
353
+ const val = this.readNumber(
354
+ 1,
355
+ 12,
356
+ output,
357
+ rest,
358
+ targetCaret,
359
+ deleting
360
+ );
361
+ output = val.output;
362
+ rest = val.rest;
363
+ targetCaret = val.targetCaret;
364
+ if (val.value !== null) {
365
+ month = val.value;
366
+ // Add separator in case it's complete
367
+ if (val.complete && !deleting) {
368
+ ({ rest, targetCaret } = this.useUpNumbersUntilNextSeparator(
369
+ rest,
370
+ targetCaret,
371
+ true,
372
+ deleting
373
+ ));
374
+ }
375
+ if (!val.complete && output.length <= targetCaret) {
376
+ allComplete = false;
377
+ }
378
+ }
379
+ } else if (node.type === RdtFormatType.Day) {
380
+ const val = this.readNumber(
381
+ 1,
382
+ 31,
383
+ output,
384
+ rest,
385
+ targetCaret,
386
+ deleting
387
+ );
388
+ output = val.output;
389
+ rest = val.rest;
390
+ targetCaret = val.targetCaret;
391
+ if (val.value !== null) {
392
+ day = val.value;
393
+ // Add separator in case it's complete
394
+ if (val.complete && !deleting) {
395
+ ({ rest, targetCaret } = this.useUpNumbersUntilNextSeparator(
396
+ rest,
397
+ targetCaret,
398
+ true,
399
+ deleting
400
+ ));
401
+ }
402
+ if (!val.complete && output.length <= targetCaret) {
403
+ allComplete = false;
404
+ }
405
+ }
406
+ }
407
+ }
408
+ }
409
+
410
+ let date: Date | null = null;
411
+ if (year !== null && month !== null && day !== null) {
412
+ date = new Date(year, month - 1, day);
413
+ if (date.getMonth() !== month - 1) {
414
+ date = null;
415
+ }
416
+ }
417
+
418
+ targetCaret = Math.min(targetCaret, output.length + 1);
419
+
420
+ if (!deleting) {
421
+ if (caretPosition === input.length) {
422
+ targetCaret = output.length;
423
+ } else if (allComplete) {
424
+ while (
425
+ targetCaret > 0 &&
426
+ output.length > targetCaret &&
427
+ !RdtStringUtils.isNumericCharacter(output[targetCaret])
428
+ ) {
429
+ targetCaret++;
430
+ }
431
+ }
432
+ } else if (!date) {
433
+ output = input;
434
+ targetCaret = caretPosition;
435
+ }
436
+
437
+ return {
438
+ prettyInput: output,
439
+ date: date,
440
+ complete: date !== null,
441
+ caret: targetCaret,
442
+ ambiguous: ambiguous || date === null,
443
+ };
444
+ }
445
+
446
+ private readNumber(
447
+ min: number,
448
+ max: number,
449
+ output: string,
450
+ rest: string,
451
+ targetCaret: number,
452
+ deleting: boolean
453
+ ) {
454
+ const maxDigits = getDigitCount(max);
455
+
456
+ let value: number | null = null;
457
+ let strVal = '';
458
+ let complete = false;
459
+ if (!(this.cfg.leadingZeroMode & RdtLeadingZeroInputMode.NoLeadingZero)) {
460
+ const regex = new RegExp(`^\\d{${maxDigits}}`);
461
+ if (rest.match(regex)) {
462
+ strVal = rest.substring(0, maxDigits);
463
+ output += strVal;
464
+ rest = rest.substring(maxDigits);
465
+ value = parseInt(strVal);
466
+ }
467
+ } else {
468
+ let digits = rest.match(/^\d+/)?.[0] ?? '';
469
+
470
+ if (digits.length > 0) {
471
+ digits = digits.slice(0, maxDigits);
472
+ value = parseInt(digits);
473
+ if (value < min) {
474
+ value = null;
475
+ let nines = '9';
476
+ while (
477
+ digits.length + 1 > maxDigits ||
478
+ parseInt(digits + nines) < min
479
+ ) {
480
+ digits = digits.slice(0, -1);
481
+ nines += '9';
482
+ }
483
+ } else {
484
+ while (value > max && digits.length > 0) {
485
+ digits = digits.slice(0, -1);
486
+ value = parseInt(digits);
487
+ }
488
+ if (digits.length === 0) {
489
+ value = null;
490
+ }
491
+ }
492
+ if (
493
+ digits[0] === '0' &&
494
+ min !== 0 &&
495
+ !(this.cfg.leadingZeroMode & RdtLeadingZeroInputMode.LeadingZero)
496
+ ) {
497
+ value = null;
498
+ digits = '';
499
+ }
500
+ }
501
+
502
+ output += digits;
503
+ rest = rest.substring(digits.length);
504
+ strVal = digits;
505
+ }
506
+
507
+ if (value !== null && value >= min && value <= max) {
508
+ complete = strVal.length === maxDigits || value * 10 > max;
509
+ ({ rest, targetCaret } = this.useUpNumbersUntilNextSeparator(
510
+ rest,
511
+ targetCaret,
512
+ complete && !deleting,
513
+ deleting
514
+ ));
515
+ return { value, complete, output, rest, targetCaret };
516
+ } else {
517
+ return { value: null, complete: false, output, rest, targetCaret };
518
+ }
519
+ }
520
+
521
+ private parseFormat(cfg: RdtDateInputConfig) {
522
+ const keys = Object.keys(cfg.symbolMeta);
523
+ keys.sort((a, b) => b.length - a.length);
524
+
525
+ let rest = cfg.format;
526
+ const result: RdtFormatNode[] = [];
527
+
528
+ while (rest.length > 0) {
529
+ const indices = keys.map((key) => rest.indexOf(key));
530
+ const minI = indices.indexOf(Math.min(...indices.filter((i) => i >= 0)));
531
+ const minIndex = indices[minI];
532
+ const minKey = keys[minI];
533
+
534
+ if (minIndex > 0) {
535
+ result.push({
536
+ type: RdtFormatType.Constant,
537
+ value: rest.substring(0, minIndex),
538
+ });
539
+ rest = rest.substring(minIndex);
540
+ } else if (minIndex < 0) {
541
+ result.push({
542
+ type: RdtFormatType.Constant,
543
+ value: rest,
544
+ });
545
+ rest = '';
546
+ } else {
547
+ result.push(cfg.symbolMeta[minKey]);
548
+ rest = rest.substring(minIndex + minKey.length);
549
+ }
550
+ }
551
+
552
+ return result;
553
+ }
554
+
555
+ private useUpNumbersUntilNextSeparator(
556
+ input: string,
557
+ targetCaret: number,
558
+ insertSeparator: boolean,
559
+ deleting: boolean
560
+ ) {
561
+ let i = 0;
562
+ while (i < input.length && RdtStringUtils.isNumericCharacter(input[i])) {
563
+ i++;
564
+ }
565
+ if (i !== input.length) {
566
+ return {
567
+ rest: input.substring(i),
568
+ targetCaret: targetCaret - (deleting ? i : 0),
569
+ };
570
+ } else if (insertSeparator) {
571
+ return { rest: ' ' + input, targetCaret: targetCaret };
572
+ } else {
573
+ return { rest: input, targetCaret };
574
+ }
575
+ }
576
+
577
+ private containsNonNumericCharacters(input: string) {
578
+ return input.search(/\D/) >= 0;
579
+ }
580
+ }