@amimpact/willy-utils 4.13.0 → 4.15.0
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.
- package/package.json +1 -1
- package/src/date.ts +42 -1
- package/src/form.ts +1 -14
- package/src/index.ts +4 -2
- package/src/misc.ts +72 -12
package/package.json
CHANGED
package/src/date.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
subDays,
|
|
8
8
|
} from 'date-fns';
|
|
9
9
|
import { nl } from 'date-fns/locale';
|
|
10
|
-
import { TZDate } from '@date-fns/tz';
|
|
10
|
+
import { TZDate, tzOffset } from '@date-fns/tz';
|
|
11
11
|
import { parse as parseISO } from './includes/tinyduration';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -293,6 +293,47 @@ export const formatNewDate = (dateObject: CraftDate) => {
|
|
|
293
293
|
return new Date(parsed);
|
|
294
294
|
};
|
|
295
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Een datum met tijdzone zoals teruggegeven door externe APIs (bijv. Microsoft Graph).
|
|
298
|
+
*/
|
|
299
|
+
export interface DateTimeWithZone {
|
|
300
|
+
dateTime: string;
|
|
301
|
+
timeZone: string;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Parseert een DateTimeWithZone object naar een TZDate in de lokale tijdzone van de browser.
|
|
306
|
+
*
|
|
307
|
+
* TZDate gebruikt de timeZone parameter alleen voor de getters, niet voor het parsen
|
|
308
|
+
* van de string. Daardoor wordt een string zonder timezone-aanduiding altijd als lokale
|
|
309
|
+
* tijd geïnterpreteerd. Om de string correct te interpreteren in de opgegeven tijdzone:
|
|
310
|
+
* 1. Parseer de string naïef als UTC (via 'Z' suffix) als referentiedatum
|
|
311
|
+
* 2. Bepaal de UTC-offset van de opgegeven tijdzone op dat moment via tzOffset
|
|
312
|
+
* 3. Corrigeer de timestamp: UTC = lokale tijd - offset
|
|
313
|
+
* 4. Maak een TZDate in de lokale tijdzone van de browser
|
|
314
|
+
*
|
|
315
|
+
* Retourneert null als de dateTime string ongeldig is of de timeZone onbekend is.
|
|
316
|
+
*
|
|
317
|
+
* @param {DateTimeWithZone} dateTimeWithZone - Object met dateTime string en timeZone
|
|
318
|
+
* @returns {TZDate | null}
|
|
319
|
+
*/
|
|
320
|
+
export const parseDateTimeWithZone = (dateTimeWithZone: DateTimeWithZone): TZDate | null => {
|
|
321
|
+
try {
|
|
322
|
+
const localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
323
|
+
const naiveUtc = new Date(dateTimeWithZone.dateTime + 'Z');
|
|
324
|
+
|
|
325
|
+
if (isNaN(naiveUtc.getTime())) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const offsetMinutes = tzOffset(dateTimeWithZone.timeZone, naiveUtc);
|
|
330
|
+
const utcMs = naiveUtc.getTime() - offsetMinutes * 60 * 1000;
|
|
331
|
+
return new TZDate(utcMs, localTimeZone);
|
|
332
|
+
} catch {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
296
337
|
/**
|
|
297
338
|
* Convert datum
|
|
298
339
|
* @param {Date} date
|
package/src/form.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verwijdert de Froala footer uit de HTML string
|
|
3
|
-
* @param {string} htmlString
|
|
4
|
-
* @return {string} HTML string zonder de Froala footer
|
|
5
|
-
*/
|
|
6
|
-
const removeFroalaFooter = (htmlString) => {
|
|
7
|
-
if (typeof htmlString !== 'string') {
|
|
8
|
-
return htmlString;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
return htmlString.replace(/<p[^>]*data-f-id="pbf"[^>]*>[\s\S]*?<\/p>/, '');
|
|
12
|
-
};
|
|
13
|
-
|
|
14
1
|
/**
|
|
15
2
|
* Creeert een FormData object vanuit een object
|
|
16
3
|
* https://gist.github.com/ghinda/8442a57f22099bdb2e34#gistcomment-2719686
|
|
@@ -76,7 +63,7 @@ export const convertObjectToFormData = (
|
|
|
76
63
|
const val = model[propertyName];
|
|
77
64
|
const formDataValue = typeof val === 'boolean' ? (val ? '1' : '0') : val;
|
|
78
65
|
|
|
79
|
-
formData.append(formKey,
|
|
66
|
+
formData.append(formKey, formDataValue.toString());
|
|
80
67
|
}
|
|
81
68
|
}
|
|
82
69
|
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
formatSecondsToHumanReadableTime,
|
|
7
7
|
formatUTCDate,
|
|
8
8
|
parseISODuration,
|
|
9
|
+
parseDateTimeWithZone,
|
|
9
10
|
} from './date';
|
|
10
11
|
import { convertObjectToFormData } from './form';
|
|
11
12
|
import {
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
checkForScrollbars,
|
|
15
16
|
convertHexToRGBA,
|
|
16
17
|
downloadFile,
|
|
17
|
-
|
|
18
|
+
getTree,
|
|
18
19
|
getUrlsFromString,
|
|
19
20
|
isEmpty,
|
|
20
21
|
isHoverableDevice,
|
|
@@ -50,7 +51,7 @@ export {
|
|
|
50
51
|
formatNewDate,
|
|
51
52
|
formatSecondsToHumanReadableTime,
|
|
52
53
|
formatUTCDate,
|
|
53
|
-
|
|
54
|
+
getTree,
|
|
54
55
|
getParents,
|
|
55
56
|
getUrlsFromString,
|
|
56
57
|
isAsset,
|
|
@@ -65,6 +66,7 @@ export {
|
|
|
65
66
|
isValidEmail,
|
|
66
67
|
observeResize,
|
|
67
68
|
parseISODuration,
|
|
69
|
+
parseDateTimeWithZone,
|
|
68
70
|
readCookie,
|
|
69
71
|
slugify,
|
|
70
72
|
wrap,
|
package/src/misc.ts
CHANGED
|
@@ -105,17 +105,37 @@ export const downloadFile = (data: any, fileName: string) => {
|
|
|
105
105
|
fileLink.setAttribute('download', fileName);
|
|
106
106
|
document.body.appendChild(fileLink);
|
|
107
107
|
fileLink.click();
|
|
108
|
+
// direct opruimen
|
|
109
|
+
document.body.removeChild(fileLink);
|
|
110
|
+
if (!isValidUrl(data)) {
|
|
111
|
+
// ook de object URL vrijgeven
|
|
112
|
+
window.URL.revokeObjectURL(fileURL);
|
|
113
|
+
}
|
|
108
114
|
}
|
|
109
115
|
};
|
|
110
116
|
|
|
111
117
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* @param {
|
|
115
|
-
* @
|
|
116
|
-
* @param {number | string} right
|
|
118
|
+
* Detecteert of items een nested-set structuur (lft/rgt) hebben
|
|
119
|
+
*
|
|
120
|
+
* @param {unknown[]} entries
|
|
121
|
+
* @returns {boolean}
|
|
117
122
|
*/
|
|
118
|
-
|
|
123
|
+
const hasNestedSetFields = (entries: unknown[]): boolean => {
|
|
124
|
+
if (entries.length === 0) return false;
|
|
125
|
+
const first = entries[0] as Record<string, unknown>;
|
|
126
|
+
return 'lft' in first && 'rgt' in first;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Bouwt een boom op basis van nested-set velden (lft, rgt, level)
|
|
131
|
+
*
|
|
132
|
+
* @param {any[]} entries - Platte lijst met lft, rgt en level properties
|
|
133
|
+
* @param {number} level - Huidig level om te filteren
|
|
134
|
+
* @param {number | string} left - Linker grens
|
|
135
|
+
* @param {number | string} right - Rechter grens
|
|
136
|
+
* @returns {any[]}
|
|
137
|
+
*/
|
|
138
|
+
const buildNestedSetTree = (entries: any[] = [], level: number = 1, left?: any, right?: any): any[] => {
|
|
119
139
|
return entries
|
|
120
140
|
.filter((item) => {
|
|
121
141
|
return (
|
|
@@ -127,7 +147,7 @@ export const getNestedSet = (entries: any[] = [], level: number = 1, left: any,
|
|
|
127
147
|
.map((item) => {
|
|
128
148
|
const returnItem = item;
|
|
129
149
|
|
|
130
|
-
returnItem['children'] =
|
|
150
|
+
returnItem['children'] = buildNestedSetTree(
|
|
131
151
|
entries,
|
|
132
152
|
parseFloat(item.level) + 1,
|
|
133
153
|
parseFloat(item.lft),
|
|
@@ -138,6 +158,40 @@ export const getNestedSet = (entries: any[] = [], level: number = 1, left: any,
|
|
|
138
158
|
});
|
|
139
159
|
};
|
|
140
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Bouwt een boom op basis van parentId velden (id, parentId)
|
|
163
|
+
*
|
|
164
|
+
* @param {any[]} entries - Platte lijst met id en parentId properties
|
|
165
|
+
* @param {string | number | null} parent - parentId van de root
|
|
166
|
+
* @returns {any[] | undefined}
|
|
167
|
+
*/
|
|
168
|
+
const buildParentIdTree = (entries: any[] = [], parent: string | number | null): any[] | undefined => {
|
|
169
|
+
const t: Record<string, any> = {};
|
|
170
|
+
entries.forEach((o) =>
|
|
171
|
+
((t[o.parentId] ??= {}).children ??= []).push(Object.assign((t[o.id] ??= {}), o)),
|
|
172
|
+
);
|
|
173
|
+
return t[`${parent}`]?.children;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Platte array omzetten naar een boom met children.
|
|
178
|
+
* Detecteert automatisch of de data nested-set velden (lft/rgt) bevat
|
|
179
|
+
* of parentId-gebaseerd is en kiest het juiste algoritme.
|
|
180
|
+
*
|
|
181
|
+
* @param {any[]} entries - Platte lijst van items
|
|
182
|
+
* @param {string | number | null} parent - parentId van de root (alleen gebruikt bij parentId-modus)
|
|
183
|
+
* @returns {any[]}
|
|
184
|
+
*/
|
|
185
|
+
export const getTree = (entries: any[] = [], parent: string | number | null = null): any[] => {
|
|
186
|
+
if (entries.length === 0) return [];
|
|
187
|
+
|
|
188
|
+
if (hasNestedSetFields(entries)) {
|
|
189
|
+
return buildNestedSetTree(entries);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return buildParentIdTree(entries, parent) || [];
|
|
193
|
+
};
|
|
194
|
+
|
|
141
195
|
/**
|
|
142
196
|
* Urls uit een string halen
|
|
143
197
|
* @param {string} content - html
|
|
@@ -257,18 +311,24 @@ export const isValidUrl = (urlString: any) => {
|
|
|
257
311
|
/**
|
|
258
312
|
* Resize observer; checkt of een HTML element zijn dimensies veranderen
|
|
259
313
|
* en zodra dat gebeurt wordt de callback functie gecalled.
|
|
314
|
+
* Retourneert de observer zodat callers deze kunnen disconnecten bij cleanup.
|
|
260
315
|
*
|
|
261
|
-
* @param {HTMLElement}
|
|
316
|
+
* @param {HTMLElement} element
|
|
262
317
|
* @param {Function} callback
|
|
318
|
+
* @returns {ResizeObserver | null} - De observer instantie, of null als het element niet bestaat
|
|
263
319
|
*/
|
|
264
|
-
export const observeResize = (element: HTMLElement, callback: Function) => {
|
|
320
|
+
export const observeResize = (element: HTMLElement, callback: Function): ResizeObserver | null => {
|
|
321
|
+
if (!element) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
265
325
|
const resizeObserver = new ResizeObserver(() => {
|
|
266
326
|
callback(element);
|
|
267
327
|
});
|
|
268
328
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
329
|
+
resizeObserver.observe(element);
|
|
330
|
+
|
|
331
|
+
return resizeObserver;
|
|
272
332
|
};
|
|
273
333
|
|
|
274
334
|
/**
|