@amimpact/willy-utils 4.4.0 → 4.5.1

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.
@@ -1,7 +1,22 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serialize = exports.parse = exports.InvalidDurationError = void 0;
4
- var units = [
1
+ /**
2
+ * https://github.com/MelleB/tinyduration
3
+ * v 3.2.0
4
+ */
5
+ interface DurationValues {
6
+ years?: number;
7
+ months?: number;
8
+ weeks?: number;
9
+ days?: number;
10
+ hours?: number;
11
+ minutes?: number;
12
+ seconds?: number;
13
+ }
14
+
15
+ export type Duration = {
16
+ negative?: boolean;
17
+ } & DurationValues;
18
+
19
+ const units: Array<{ unit: keyof DurationValues; symbol: string }> = [
5
20
  { unit: 'years', symbol: 'Y' },
6
21
  { unit: 'months', symbol: 'M' },
7
22
  { unit: 'weeks', symbol: 'W' },
@@ -10,95 +25,121 @@ var units = [
10
25
  { unit: 'minutes', symbol: 'M' },
11
26
  { unit: 'seconds', symbol: 'S' },
12
27
  ];
28
+
13
29
  // Construction of the duration regex
14
30
  // NOTE: IE11 kan niet overweg met groups in een regex
15
31
  // const r = (name: string, unit: string): string =>
16
32
  // `((?<${name}>-?\\d*[\\.,]?\\d+)${unit})?`;
17
- var r = function (name, unit) { return "((\\d*[\\.,]?\\d+)".concat(unit, ")?"); };
18
- var durationRegex = new RegExp([
19
- 'P',
20
- r('years', 'Y'),
21
- r('months', 'M'),
22
- r('weeks', 'W'),
23
- r('days', 'D'),
24
- '(T',
25
- r('hours', 'H'),
26
- r('minutes', 'M'),
27
- r('seconds', 'S'),
28
- ')?', // end optional time
29
- ].join(''));
30
- function parseNum(s) {
33
+ const r = (name: string, unit: string): string => `((\\d*[\\.,]?\\d+)${unit})?`;
34
+
35
+ const durationRegex = new RegExp(
36
+ [
37
+ 'P',
38
+ r('years', 'Y'),
39
+ r('months', 'M'),
40
+ r('weeks', 'W'),
41
+ r('days', 'D'),
42
+ '(T',
43
+ r('hours', 'H'),
44
+ r('minutes', 'M'),
45
+ r('seconds', 'S'),
46
+ ')?', // end optional time
47
+ ].join(''),
48
+ );
49
+
50
+ function parseNum(s: string): number | undefined {
31
51
  if (s === '' || s === undefined || s === null) {
32
52
  return undefined;
33
53
  }
54
+
34
55
  return parseFloat(s.replace(',', '.'));
35
56
  }
36
- exports.InvalidDurationError = new Error('Invalid duration');
37
- function parse(durationStr) {
38
- var match = durationRegex.exec(durationStr);
57
+
58
+ export const InvalidDurationError = new Error('Invalid duration');
59
+
60
+ export function parse(durationStr: string): Duration {
61
+ const match = durationRegex.exec(durationStr);
62
+
39
63
  if (!match) {
40
- throw exports.InvalidDurationError;
64
+ throw InvalidDurationError;
41
65
  }
42
- var empty = true;
43
- var values = {};
44
- for (var _i = 0, units_1 = units; _i < units_1.length; _i++) {
45
- var _a = units_1[_i], unit = _a.unit, symbol = _a.symbol;
66
+
67
+ let empty = true;
68
+ const values: DurationValues = {};
69
+ for (const { unit, symbol } of units) {
46
70
  // NOTE: IE11 kan niet overweg met groups in een regex
47
71
  // if (match.groups[unit]) {
48
72
  // empty = false;
49
73
  // values[unit] = parseNum(match.groups[unit]);
50
74
  // }
75
+
51
76
  // Bovenstaande manier met named groups geeft een fout in IE11
52
77
  // Dus maar zelf een extra loop typen en checken hoe de object opgebouwd moet worden
53
78
  // Eerste match overslaan
54
- for (var index = 1; index < match.length - 1; index++) {
55
- var str = match[index];
79
+ for (let index = 1; index < match.length - 1; index++) {
80
+ const str = match[index];
81
+
56
82
  if (str) {
57
- var strippedSymbol = str.charAt(str.length - 1);
83
+ const strippedSymbol = str.charAt(str.length - 1);
84
+
58
85
  if (strippedSymbol === symbol) {
59
86
  empty = false;
87
+
60
88
  // 2 x een symbool 'M', deze even handmatig checken
61
- var u = unit;
89
+ let u = unit;
62
90
  if (symbol === 'M') {
63
91
  u = index === 3 ? 'months' : 'minutes';
64
92
  }
93
+
65
94
  values[u] = parseNum(str.substring(0, str.length - 1));
66
95
  }
67
96
  }
68
97
  }
69
98
  }
99
+
70
100
  if (empty) {
71
- throw exports.InvalidDurationError;
101
+ throw InvalidDurationError;
72
102
  }
73
- var duration = values;
103
+
104
+ const duration: Duration = values;
74
105
  // if (match.groups.negative) {
75
106
  // duration.negative = true;
76
107
  // }
108
+
77
109
  return duration;
78
110
  }
79
- exports.parse = parse;
80
- var s = function (number, component) {
111
+
112
+ const s = (
113
+ number: number | undefined,
114
+ component: string,
115
+ ): string | undefined => {
81
116
  if (!number) {
82
117
  return undefined;
83
118
  }
84
- var numberAsString = number.toString();
85
- var exponentIndex = numberAsString.indexOf('e');
119
+
120
+ let numberAsString = number.toString();
121
+ const exponentIndex = numberAsString.indexOf('e');
86
122
  if (exponentIndex > -1) {
87
- var magnitude = parseInt(numberAsString.slice(exponentIndex + 2), 10);
123
+ const magnitude = parseInt(numberAsString.slice(exponentIndex + 2), 10);
88
124
  numberAsString = number.toFixed(magnitude + exponentIndex - 2);
89
125
  }
126
+
90
127
  return numberAsString + component;
91
128
  };
92
- function serialize(duration) {
93
- if (!duration.years &&
129
+
130
+ export function serialize(duration: Duration): string {
131
+ if (
132
+ !duration.years &&
94
133
  !duration.months &&
95
134
  !duration.weeks &&
96
135
  !duration.days &&
97
136
  !duration.hours &&
98
137
  !duration.minutes &&
99
- !duration.seconds) {
138
+ !duration.seconds
139
+ ) {
100
140
  return 'PT0S';
101
141
  }
142
+
102
143
  return [
103
144
  // duration.negative && '-',
104
145
  'P',
@@ -114,4 +155,3 @@ function serialize(duration) {
114
155
  .filter(Boolean)
115
156
  .join('');
116
157
  }
117
- exports.serialize = serialize;
package/src/index.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { createCookie, readCookie, eraseCookie } from './cookie';
2
+ import {
3
+ formatCraftDate,
4
+ formatDateFromNow,
5
+ formatNewDate,
6
+ formatUTCDate,
7
+ parseISODuration,
8
+ } from './date';
9
+ import { convertObjectToFormData } from './form';
10
+ import {
11
+ bytesToSize,
12
+ capitalize,
13
+ checkForScrollbars,
14
+ convertHexToRGBA,
15
+ downloadFile,
16
+ getNestedSet,
17
+ getUrlsFromString,
18
+ isEmpty,
19
+ isHoverableDevice,
20
+ isPlainObject,
21
+ isTouchDevice,
22
+ isValidUrl,
23
+ observeResize,
24
+ wrap,
25
+ } from './misc';
26
+ import { $, $$, getParents } from './selectors';
27
+ import { isCraftActionUrl, isInternalLink, isExternalUrl, isAsset, slugify } from './url';
28
+
29
+ export {
30
+ $,
31
+ $$,
32
+ bytesToSize,
33
+ capitalize,
34
+ checkForScrollbars,
35
+ convertHexToRGBA,
36
+ createCookie,
37
+ downloadFile,
38
+ eraseCookie,
39
+ formatCraftDate,
40
+ convertObjectToFormData,
41
+ formatDateFromNow,
42
+ formatNewDate,
43
+ formatUTCDate,
44
+ getNestedSet,
45
+ getParents,
46
+ getUrlsFromString,
47
+ isAsset,
48
+ isCraftActionUrl,
49
+ isEmpty,
50
+ isExternalUrl,
51
+ isHoverableDevice,
52
+ isInternalLink,
53
+ isPlainObject,
54
+ isTouchDevice,
55
+ isValidUrl,
56
+ observeResize,
57
+ parseISODuration,
58
+ readCookie,
59
+ slugify,
60
+ wrap,
61
+ };
package/src/misc.ts ADDED
@@ -0,0 +1,285 @@
1
+ declare global {
2
+ interface Navigator {
3
+ msSaveBlob?: (blob: any, defaultName?: string) => boolean;
4
+ msSaveOrOpenBlob: (blob: any, defaultName?: string) => boolean;
5
+ }
6
+ }
7
+
8
+ /**
9
+ * Toon mooier bestandsformaat
10
+ * @param {string | number} bytes
11
+ */
12
+ export const bytesToSize = (bytes: number) => {
13
+ if (typeof bytes === 'string') {
14
+ bytes = parseInt(bytes);
15
+ }
16
+
17
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
18
+ if (bytes === 0) return '0 Byte';
19
+
20
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
21
+ return Math.round(bytes / Math.pow(1024, i)) + ' ' + sizes[i];
22
+ };
23
+
24
+ /**
25
+ * Eerste letter van een string een hoofdletter maken
26
+ * @param {string} str
27
+ */
28
+ export const capitalize = (str: string) => {
29
+ return str.charAt(0).toUpperCase() + str.slice(1);
30
+ };
31
+
32
+ /**
33
+ * Check of een HTML element horizontale of verticale scrollbars heeft
34
+ * als dat zo is, dan wordt een class op het element gezet.
35
+ *
36
+ * @param {HTMLElement} el
37
+ * @param {String} dir // vertical of horizontal
38
+ */
39
+ export const checkForScrollbars = (el: HTMLElement, dir: String = 'vertical') => {
40
+ if (!el) {
41
+ return;
42
+ }
43
+
44
+ const direction = dir === 'vertical' ? 'scrollTop' : 'scrollLeft';
45
+
46
+ let hasScrollbar = !!el[direction];
47
+
48
+ if (!hasScrollbar) {
49
+ el[direction] = 1;
50
+ hasScrollbar = !!el[direction];
51
+ el[direction] = 0;
52
+ }
53
+
54
+ if (hasScrollbar) {
55
+ el.classList.add(`has-${dir}-scrollbar`);
56
+ } else {
57
+ el.classList.remove(`has-${dir}-scrollbar`);
58
+ }
59
+ };
60
+
61
+ /**
62
+ * Hex kleurcode converten naar rgba
63
+ *
64
+ * @param {string} hexCode
65
+ * @param {number} opacity
66
+ */
67
+ export const convertHexToRGBA = (hexCode: string, opacity = 1) => {
68
+ let hex = hexCode.replace('#', '');
69
+
70
+ if (hex.length === 3) {
71
+ hex = `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}`;
72
+ }
73
+
74
+ const r = parseInt(hex.substring(0, 2), 16);
75
+ const g = parseInt(hex.substring(2, 4), 16);
76
+ const b = parseInt(hex.substring(4, 6), 16);
77
+
78
+ /* Backward compatibility for whole number based opacity values. */
79
+ if (opacity > 1 && opacity <= 100) {
80
+ opacity = opacity / 100;
81
+ }
82
+
83
+ return `rgba(${r},${g},${b},${opacity})`;
84
+ };
85
+
86
+ /**
87
+ * Downloaden van een bestand
88
+ *
89
+ * @param {any} data
90
+ * @param {string} fileName
91
+ */
92
+ export const downloadFile = (data: any, fileName: string) => {
93
+ if (typeof window.navigator.msSaveBlob !== 'undefined') {
94
+ window.navigator.msSaveOrOpenBlob(new Blob([data]), fileName);
95
+ } else {
96
+ let fileURL;
97
+ if (isValidUrl(data)) {
98
+ fileURL = data;
99
+ } else {
100
+ fileURL = window.URL.createObjectURL(new Blob([data]));
101
+ }
102
+
103
+ const fileLink = document.createElement('a');
104
+ fileLink.href = fileURL;
105
+ fileLink.setAttribute('download', fileName);
106
+ document.body.appendChild(fileLink);
107
+ fileLink.click();
108
+ }
109
+ };
110
+
111
+ /**
112
+ * Flat structure array ombouwen naar een array met children
113
+ * @param {array} entries
114
+ * @param {number} level
115
+ * @param {number | string} left
116
+ * @param {number | string} right
117
+ */
118
+ export const getNestedSet = (entries: any[] = [], level: number = 1, left: any, right: any) => {
119
+ return entries
120
+ .filter((item) => {
121
+ return (
122
+ parseFloat(item.level) === level &&
123
+ ((typeof left === 'undefined' && typeof right === 'undefined') ||
124
+ (parseFloat(item.lft) > left && parseFloat(item.rgt) < right))
125
+ );
126
+ })
127
+ .map((item) => {
128
+ const returnItem = item;
129
+
130
+ returnItem['children'] = getNestedSet(
131
+ entries,
132
+ parseFloat(item.level) + 1,
133
+ parseFloat(item.lft),
134
+ parseFloat(item.rgt),
135
+ );
136
+
137
+ return returnItem;
138
+ });
139
+ };
140
+
141
+ /**
142
+ * Urls uit een string halen
143
+ * @param {string} content - html
144
+ */
145
+ export const getUrlsFromString = (content: string) => {
146
+ const regex =
147
+ /(https?|ftp|file):\/\/([-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?)/gi;
148
+
149
+ const m = content.match(regex);
150
+
151
+ return m || null;
152
+ };
153
+
154
+ /**
155
+ * Bepalen of een waarde leeg is
156
+ *
157
+ * @param {any} value
158
+ * @returns {boolean}
159
+ */
160
+ export const isEmpty = (value): boolean => {
161
+ if (Array.isArray(value)) {
162
+ return !value.length;
163
+ }
164
+
165
+ if (isPlainObject(value)) {
166
+ return !Object.keys(value).length;
167
+ }
168
+
169
+ if (Number.isNaN(value)) {
170
+ return false;
171
+ }
172
+
173
+ return !value;
174
+ };
175
+
176
+ /**
177
+ * Bepalen of jouw device hover ondersteunt
178
+ *
179
+ * @returns {boolean}
180
+ */
181
+ export const isHoverableDevice = (): boolean => {
182
+ return window.matchMedia('(hover: hover) and (pointer: fine)').matches;
183
+ };
184
+
185
+ /**
186
+ * Bekijken of een waarde een object is
187
+ * https://github.com/reduxjs/redux/blob/master/src/utils/isPlainObject.ts
188
+ *
189
+ * @param {any} value - Waarde welke je wilt checken of het een object is
190
+ * @returns {boolean}
191
+ */
192
+ export const isPlainObject = (value: any): boolean => {
193
+ if (typeof value !== 'object' || value === null) {
194
+ return false;
195
+ }
196
+
197
+ let proto = value;
198
+
199
+ while (Object.getPrototypeOf(proto) !== null) {
200
+ proto = Object.getPrototypeOf(proto);
201
+ }
202
+
203
+ return Object.getPrototypeOf(value) === proto;
204
+ };
205
+
206
+ /**
207
+ * Bepalen of je gebruikt maakt van een touchscreen
208
+ * https://gist.github.com/esedic/39a16a7521d42ae205203e3d40dc19f5
209
+ *
210
+ * @returns {boolean}
211
+ */
212
+ export const isTouchDevice = (): boolean => {
213
+ let result = false;
214
+
215
+ if (window.PointerEvent && 'maxTouchPoints' in navigator) {
216
+ // if Pointer Events are supported, just check maxTouchPoints
217
+ if (navigator.maxTouchPoints > 0) {
218
+ result = true;
219
+ }
220
+ } else {
221
+ // no Pointer Events...
222
+ if (window.matchMedia && window.matchMedia('(any-pointer:coarse)').matches) {
223
+ // check for any-pointer:coarse which mostly means touchscreen
224
+ result = true;
225
+ } else if (window.TouchEvent || 'ontouchstart' in window) {
226
+ // last resort - check for exposed touch events API / event handler
227
+ result = true;
228
+ }
229
+ }
230
+
231
+ return result;
232
+ };
233
+
234
+ /**
235
+ * Bekijken of een waarde url is
236
+ *
237
+ * @param {any} urlString - Waarde welke je wilt checken of het een url is
238
+ * @returns {boolean}
239
+ */
240
+ export const isValidUrl = (urlString: any) => {
241
+ if (typeof urlString !== 'string') {
242
+ return false;
243
+ }
244
+
245
+ const urlPattern = new RegExp(
246
+ '^(https?:\\/\\/)?' + // validate protocol
247
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
248
+ '((\\d{1,3}\\.){3}\\d{1,3}))' + // validate OR ip (v4) address
249
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
250
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
251
+ '(\\#[-a-z\\d_]*)?$',
252
+ 'i',
253
+ ); // validate fragment locator
254
+ return !!urlPattern.test(urlString);
255
+ };
256
+
257
+ /**
258
+ * Resize observer; checkt of een HTML element zijn dimensies veranderen
259
+ * en zodra dat gebeurt wordt de callback functie gecalled.
260
+ *
261
+ * @param {HTMLElement} el
262
+ * @param {Function} callback
263
+ */
264
+ export const observeResize = (element: HTMLElement, callback: Function) => {
265
+ const resizeObserver = new ResizeObserver(() => {
266
+ callback(element);
267
+ });
268
+
269
+ if (element) {
270
+ resizeObserver.observe(element);
271
+ }
272
+ };
273
+
274
+ /**
275
+ * Wrap een element in een nieuw element
276
+ *
277
+ * @param {HTMLElement} el
278
+ * @param {HTMLElement} wrapper
279
+ */
280
+ export const wrap = (el: HTMLElement, wrapper: HTMLElement) => {
281
+ if (el.parentNode) {
282
+ el.parentNode.insertBefore(wrapper, el);
283
+ wrapper.appendChild(el);
284
+ }
285
+ };
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getParents = exports.$$ = exports.$ = void 0;
4
1
  /**
5
2
  * Selecteer eerste item wat je tegenkomt
6
3
  *
@@ -8,11 +5,9 @@ exports.getParents = exports.$$ = exports.$ = void 0;
8
5
  * @param {*} [containerElement=document] - Element waarin hij moet selecteren
9
6
  * @returns {(HTMLElement|null)}
10
7
  */
11
- var $ = function (selector, containerElement) {
12
- if (containerElement === void 0) { containerElement = document; }
13
- return containerElement.querySelector(selector);
14
- };
15
- exports.$ = $;
8
+ export const $ = (selector: string, containerElement: any = document): HTMLElement | null =>
9
+ containerElement.querySelector(selector);
10
+
16
11
  /**
17
12
  * Selecteer alle items
18
13
  *
@@ -20,11 +15,9 @@ exports.$ = $;
20
15
  * @param {*} [containerElement=document] - Element waarin hij moet selecteren
21
16
  * @returns {HTMLElement[]}
22
17
  */
23
- var $$ = function (selector, containerElement) {
24
- if (containerElement === void 0) { containerElement = document; }
25
- return Array.prototype.slice.apply(containerElement.querySelectorAll(selector));
26
- };
27
- exports.$$ = $$;
18
+ export const $$ = (selector: string, containerElement: any = document): HTMLElement[] =>
19
+ Array.prototype.slice.apply(containerElement.querySelectorAll(selector));
20
+
28
21
  /**
29
22
  * Haal parents van een element op
30
23
  *
@@ -32,20 +25,22 @@ exports.$$ = $$;
32
25
  * @param {string} [parentSelector] - Een selector waar hij aan moet voldoen.
33
26
  * Als deze niet wordt meegegeven dan geeft hij alle parents terug
34
27
  */
35
- var getParents = function (el, parentSelector) {
36
- if (parentSelector === void 0) { parentSelector = ''; }
37
- var parents = [];
38
- var p = el.parentElement;
28
+ export const getParents = (el: HTMLElement, parentSelector: string = '') => {
29
+ const parents: HTMLElement[] = [];
30
+ let p = el.parentElement;
31
+
39
32
  // Tot de body omhoog bubbelen
40
33
  while (p && p.tagName !== 'BODY') {
41
- var o = p;
34
+ const o = p;
35
+
42
36
  // Als er geen selector is meegegeven, dan het element toevoegen
43
37
  // Of als er een selector is meegegeven en hij matcht, dan het element toevoegen
44
38
  if (parentSelector === '' || o.matches(parentSelector)) {
45
39
  parents.push(o);
46
40
  }
41
+
47
42
  p = o.parentElement;
48
43
  }
44
+
49
45
  return parents;
50
46
  };
51
- exports.getParents = getParents;