@panneau/core 4.1.1 → 5.0.0-alpha.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/README.md +83 -0
- package/dist/definition/abilities.d.ts +21 -0
- package/dist/definition/abilities.d.ts.map +1 -0
- package/dist/definition/abilities.js +47 -0
- package/dist/definition/abilities.js.map +1 -0
- package/dist/definition/form.d.ts +31 -0
- package/dist/definition/form.d.ts.map +1 -0
- package/dist/definition/form.js +54 -0
- package/dist/definition/form.js.map +1 -0
- package/dist/definition/list.d.ts +44 -0
- package/dist/definition/list.d.ts.map +1 -0
- package/dist/definition/list.js +70 -0
- package/dist/definition/list.js.map +1 -0
- package/dist/definition/resource.d.ts +21 -0
- package/dist/definition/resource.d.ts.map +1 -0
- package/dist/definition/resource.js +24 -0
- package/dist/definition/resource.js.map +1 -0
- package/dist/form/form.d.ts +148 -0
- package/dist/form/form.d.ts.map +1 -0
- package/dist/form/form.js +239 -0
- package/dist/form/form.js.map +1 -0
- package/dist/form/messages.d.ts +7 -0
- package/dist/form/messages.d.ts.map +1 -0
- package/dist/form/messages.js +25 -0
- package/dist/form/messages.js.map +1 -0
- package/dist/form/problem.d.ts +16 -0
- package/dist/form/problem.d.ts.map +1 -0
- package/dist/form/problem.js +34 -0
- package/dist/form/problem.js.map +1 -0
- package/dist/form/selectors.d.ts +16 -0
- package/dist/form/selectors.d.ts.map +1 -0
- package/dist/form/selectors.js +27 -0
- package/dist/form/selectors.js.map +1 -0
- package/dist/form/store.d.ts +19 -0
- package/dist/form/store.d.ts.map +1 -0
- package/dist/form/store.js +29 -0
- package/dist/form/store.js.map +1 -0
- package/dist/index.d.ts +17 -692
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -536
- package/dist/list/list.d.ts +68 -0
- package/dist/list/list.d.ts.map +1 -0
- package/dist/list/list.js +191 -0
- package/dist/list/list.js.map +1 -0
- package/dist/list/router.d.ts +26 -0
- package/dist/list/router.d.ts.map +1 -0
- package/dist/list/router.js +54 -0
- package/dist/list/router.js.map +1 -0
- package/dist/plugins.d.ts +73 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +54 -0
- package/dist/plugins.js.map +1 -0
- package/dist/react/provider.d.ts +38 -0
- package/dist/react/provider.d.ts.map +1 -0
- package/dist/react/provider.js +63 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/useForm.d.ts +26 -0
- package/dist/react/useForm.d.ts.map +1 -0
- package/dist/react/useForm.js +115 -0
- package/dist/react/useForm.js.map +1 -0
- package/dist/react/useList.d.ts +11 -0
- package/dist/react/useList.d.ts.map +1 -0
- package/dist/react/useList.js +47 -0
- package/dist/react/useList.js.map +1 -0
- package/dist/values.d.ts +12 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +44 -0
- package/dist/values.js.map +1 -0
- package/package.json +19 -81
- package/dist/contexts.d.ts +0 -667
- package/dist/contexts.js +0 -1772
- package/dist/hooks.d.ts +0 -512
- package/dist/hooks.js +0 -2146
- package/dist/utils.d.ts +0 -457
- package/dist/utils.js +0 -557
package/dist/utils.js
DELETED
|
@@ -1,557 +0,0 @@
|
|
|
1
|
-
import isNumber from 'lodash-es/isNumber';
|
|
2
|
-
import isObject from 'lodash-es/isObject';
|
|
3
|
-
import { useEffect, isValidElement } from 'react';
|
|
4
|
-
import { isValidElementType } from 'react-is';
|
|
5
|
-
import isString from 'lodash-es/isString';
|
|
6
|
-
import { pascalCase, kebabCase, snakeCase } from 'change-case';
|
|
7
|
-
export { kebabCase as dashCase, pascalCase, snakeCase } from 'change-case';
|
|
8
|
-
import isFunction from 'lodash-es/isFunction';
|
|
9
|
-
import isArray from 'lodash-es/isArray';
|
|
10
|
-
import get from 'lodash-es/get';
|
|
11
|
-
import capitalize from 'lodash-es/capitalize';
|
|
12
|
-
import EventEmitter from 'wolfy87-eventemitter';
|
|
13
|
-
import slugify from 'slugify';
|
|
14
|
-
|
|
15
|
-
function checkClickable(el, options = {}, parentDistance = 1) {
|
|
16
|
-
const {
|
|
17
|
-
maxParentDistance = 5,
|
|
18
|
-
tags = ['BUTTON', 'A', 'INPUT', 'I', 'TEXTAREA']
|
|
19
|
-
} = options || {};
|
|
20
|
-
const {
|
|
21
|
-
tagName = null,
|
|
22
|
-
parentNode = null
|
|
23
|
-
} = el || {};
|
|
24
|
-
if (tagName === 'BODY') {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Check if video is suspended
|
|
29
|
-
// if (
|
|
30
|
-
// tagName === 'VIDEO' &&
|
|
31
|
-
// typeof dataset.isSuspended !== 'undefined' &&
|
|
32
|
-
// (dataset.isSuspended === 'true' || dataset.isSuspended === true)
|
|
33
|
-
// ) {
|
|
34
|
-
// return true;
|
|
35
|
-
// }
|
|
36
|
-
|
|
37
|
-
if (tagName && tags.map(it => it.toLowerCase()).indexOf(tagName.toLowerCase()) !== -1) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
if (parentDistance < maxParentDistance) {
|
|
41
|
-
return checkClickable(parentNode, options, parentDistance + 1);
|
|
42
|
-
}
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Convert a camelCase style property (ex: backgroundColor) to its CSS kebab-case
|
|
47
|
-
// name (ex: background-color). Custom properties (--foo) are kept as is.
|
|
48
|
-
function toCssProperty(key) {
|
|
49
|
-
if (key.startsWith('--')) {
|
|
50
|
-
return key;
|
|
51
|
-
}
|
|
52
|
-
const property = key.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
|
|
53
|
-
return property.startsWith('ms-') ? `-${property}` : property;
|
|
54
|
-
}
|
|
55
|
-
function convertStyleToString(style) {
|
|
56
|
-
return style !== null ? Object.keys(style).map(key => `${toCssProperty(key)}:${isNumber(style[key]) ? `${style[key]}px` : style[key]};`).join('\n') : '';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function createNullableOnChange(onChange = null) {
|
|
60
|
-
return function nullableOnChange(newValue) {
|
|
61
|
-
let nullableValue = newValue;
|
|
62
|
-
if (isObject(newValue)) {
|
|
63
|
-
const allNull = Object.keys(newValue).reduce((acc, key) => acc && newValue[key] === null, true);
|
|
64
|
-
if (allNull) {
|
|
65
|
-
nullableValue = null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (onChange !== null) {
|
|
69
|
-
onChange(nullableValue);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function createUseEvent(eventsManager) {
|
|
75
|
-
return function useEvent(event, callback, enabled = true) {
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
if (enabled && eventsManager !== null) {
|
|
78
|
-
eventsManager.subscribe(event, callback);
|
|
79
|
-
}
|
|
80
|
-
return () => {
|
|
81
|
-
if (enabled && eventsManager !== null) {
|
|
82
|
-
eventsManager.unsubscribe(event, callback);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}, [eventsManager, event, callback, enabled]);
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/*! clipboard-copy. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
90
|
-
|
|
91
|
-
// @note vendoring this: https://github.com/feross/clipboard-copy/blob/master/index.js
|
|
92
|
-
// we might want to add that to the npm deps and just use it
|
|
93
|
-
|
|
94
|
-
function makeError() {
|
|
95
|
-
return new DOMException('The request is not allowed', 'NotAllowedError');
|
|
96
|
-
}
|
|
97
|
-
async function copyClipboardApi(text) {
|
|
98
|
-
// Use the Async Clipboard API when available. Requires a secure browsing
|
|
99
|
-
// context (i.e. HTTPS)
|
|
100
|
-
if (!navigator.clipboard) {
|
|
101
|
-
throw makeError();
|
|
102
|
-
}
|
|
103
|
-
return navigator.clipboard.writeText(text);
|
|
104
|
-
}
|
|
105
|
-
async function copyExecCommand(text) {
|
|
106
|
-
// Put the text to copy into a <span>
|
|
107
|
-
const span = document.createElement('span');
|
|
108
|
-
span.textContent = text;
|
|
109
|
-
|
|
110
|
-
// Preserve consecutive spaces and newlines
|
|
111
|
-
span.style.whiteSpace = 'pre';
|
|
112
|
-
span.style.webkitUserSelect = 'auto';
|
|
113
|
-
span.style.userSelect = 'all';
|
|
114
|
-
|
|
115
|
-
// Add the <span> to the page
|
|
116
|
-
document.body.appendChild(span);
|
|
117
|
-
|
|
118
|
-
// Make a selection object representing the range of text selected by the user
|
|
119
|
-
const selection = window.getSelection();
|
|
120
|
-
const range = window.document.createRange();
|
|
121
|
-
selection?.removeAllRanges();
|
|
122
|
-
range.selectNode(span);
|
|
123
|
-
selection?.addRange(range);
|
|
124
|
-
|
|
125
|
-
// Copy text to the clipboard
|
|
126
|
-
let success = false;
|
|
127
|
-
try {
|
|
128
|
-
success = window.document.execCommand('copy');
|
|
129
|
-
} finally {
|
|
130
|
-
// Cleanup
|
|
131
|
-
selection?.removeAllRanges();
|
|
132
|
-
window.document.body.removeChild(span);
|
|
133
|
-
}
|
|
134
|
-
if (!success) throw makeError();
|
|
135
|
-
}
|
|
136
|
-
async function copyToClipboard(text) {
|
|
137
|
-
try {
|
|
138
|
-
await copyClipboardApi(text);
|
|
139
|
-
} catch (err) {
|
|
140
|
-
// ...Otherwise, use document.execCommand() fallback
|
|
141
|
-
try {
|
|
142
|
-
await copyExecCommand(text);
|
|
143
|
-
} catch (err2) {
|
|
144
|
-
throw err2 || err || makeError();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function flattenComponents(components, prefix = null) {
|
|
150
|
-
if (components === null) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
return Object.keys(components).reduce((newMap, key) => {
|
|
154
|
-
const path = prefix !== null ? `${prefix}.${key}` : key;
|
|
155
|
-
return isValidElementType(components[key]) ? {
|
|
156
|
-
...newMap,
|
|
157
|
-
[path]: components[key]
|
|
158
|
-
} : {
|
|
159
|
-
...newMap,
|
|
160
|
-
...flattenComponents(components[key], path)
|
|
161
|
-
};
|
|
162
|
-
}, {});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function formatDuration(timeInSeconds) {
|
|
166
|
-
const numberOfSeconds = parseInt(String(timeInSeconds), 10); // don't forget the second param
|
|
167
|
-
let hours = Math.floor(numberOfSeconds / 3600);
|
|
168
|
-
let minutes = Math.floor((numberOfSeconds - hours * 3600) / 60);
|
|
169
|
-
let seconds = numberOfSeconds - hours * 3600 - minutes * 60;
|
|
170
|
-
if (hours < 10) {
|
|
171
|
-
hours = `0${hours}`;
|
|
172
|
-
}
|
|
173
|
-
if (minutes < 10) {
|
|
174
|
-
minutes = `0${minutes}`;
|
|
175
|
-
}
|
|
176
|
-
if (seconds < 10) {
|
|
177
|
-
seconds = `0${seconds}`;
|
|
178
|
-
}
|
|
179
|
-
return `${hours}:${minutes}:${seconds}`;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function getComponent(component) {
|
|
183
|
-
const {
|
|
184
|
-
component: name = null,
|
|
185
|
-
...props
|
|
186
|
-
} = isString(component) ? {
|
|
187
|
-
component
|
|
188
|
-
} : component || {};
|
|
189
|
-
return {
|
|
190
|
-
name,
|
|
191
|
-
props: Object.keys(props).length > 0 ? props : null
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function getColumnFromField(field) {
|
|
196
|
-
if (field === null) {
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
const {
|
|
200
|
-
name,
|
|
201
|
-
components: {
|
|
202
|
-
display = null
|
|
203
|
-
} = {},
|
|
204
|
-
label
|
|
205
|
-
} = field;
|
|
206
|
-
const {
|
|
207
|
-
name: componentName = null,
|
|
208
|
-
props: componentProps = null
|
|
209
|
-
} = getComponent(display);
|
|
210
|
-
return {
|
|
211
|
-
...componentProps,
|
|
212
|
-
id: name,
|
|
213
|
-
label,
|
|
214
|
-
valueKey: name,
|
|
215
|
-
component: componentName,
|
|
216
|
-
field
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
function getColumnsWithFields(resource, columns) {
|
|
220
|
-
const {
|
|
221
|
-
fields = []
|
|
222
|
-
} = resource;
|
|
223
|
-
const newColumns = columns !== null ? columns.map(column => {
|
|
224
|
-
const {
|
|
225
|
-
id: colId = null,
|
|
226
|
-
field: colField = null,
|
|
227
|
-
...otherProps
|
|
228
|
-
} = isString(column) ? {
|
|
229
|
-
field: column
|
|
230
|
-
} : column;
|
|
231
|
-
const fieldName = colField || colId;
|
|
232
|
-
return {
|
|
233
|
-
...(fieldName !== null ? getColumnFromField(fields.find(it => it.name === fieldName) || null) : null),
|
|
234
|
-
...otherProps,
|
|
235
|
-
id: colId || (isString(column) ? column : null) || null
|
|
236
|
-
};
|
|
237
|
-
}).filter(it => it !== null) : [];
|
|
238
|
-
return newColumns;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function getComponentFromName(name = null, components = {}, defaultComponent = null) {
|
|
242
|
-
if (components === null || name === null) {
|
|
243
|
-
return !isString(defaultComponent) ? defaultComponent : null;
|
|
244
|
-
}
|
|
245
|
-
if (isObject(name) || isFunction(name)) {
|
|
246
|
-
return name;
|
|
247
|
-
}
|
|
248
|
-
const pascalName = pascalCase(name);
|
|
249
|
-
const pascalDefaultName = isString(defaultComponent) ? pascalCase(defaultComponent) : null;
|
|
250
|
-
const component = components[pascalName] || components[name] || (isString(pascalDefaultName) ? components[pascalDefaultName] : null) || (isString(defaultComponent) ? components[defaultComponent] : null) || defaultComponent;
|
|
251
|
-
if (!component) {
|
|
252
|
-
console.warn('Could not find component from name', name, pascalName, components);
|
|
253
|
-
}
|
|
254
|
-
return component;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function getDefinitionFromId(id = null, items = [], defaultObject = null) {
|
|
258
|
-
if (items === null || id === null || isArray(items) && items.length === 0) {
|
|
259
|
-
return defaultObject;
|
|
260
|
-
}
|
|
261
|
-
const pascalId = pascalCase(id);
|
|
262
|
-
return items.find(i => i.id === pascalId) || items.find(i => i.id === id) || defaultObject;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Returns a message for each key of the definition that is not supported anymore,
|
|
267
|
-
* with the key to use instead. Without it, these keys would be silently ignored.
|
|
268
|
-
*/
|
|
269
|
-
function getDefinitionWarnings(definition) {
|
|
270
|
-
if (!isObject(definition)) {
|
|
271
|
-
return [];
|
|
272
|
-
}
|
|
273
|
-
const {
|
|
274
|
-
components = null,
|
|
275
|
-
intl = null,
|
|
276
|
-
theme = null,
|
|
277
|
-
resources = []
|
|
278
|
-
} = definition;
|
|
279
|
-
const {
|
|
280
|
-
values = null
|
|
281
|
-
} = intl || {};
|
|
282
|
-
return [...(components !== null ? ['`components` is not supported: give the custom components to the `components` prop of the container.'] : []), ...(values !== null ? ['`intl.values` is not supported: set the values of each resource in `resources[].intl.values`.'] : []), ...Object.keys(theme || {}).filter(key => key !== 'colorScheme').map(key => `\`theme.${key}\` is not supported: only \`theme.colorScheme\` is.`), ...(resources || []).reduce((warnings, resource) => {
|
|
283
|
-
const {
|
|
284
|
-
id,
|
|
285
|
-
components: resourceComponents = null,
|
|
286
|
-
columns = null
|
|
287
|
-
} = resource;
|
|
288
|
-
return [...warnings, ...(resourceComponents !== null ? [`\`resources[${id}].components\` is not supported: use \`index.component\`, \`forms.default.component\` and \`pages\`.`] : []), ...(columns !== null ? [`\`resources[${id}].columns\` is not supported: use \`index.columns\`.`] : [])];
|
|
289
|
-
}, [])];
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function getDisplayName({
|
|
293
|
-
displayName = null,
|
|
294
|
-
name = null
|
|
295
|
-
}) {
|
|
296
|
-
return displayName || name || 'Component';
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function getFieldByName(fields, name) {
|
|
300
|
-
return fields.reduce((foundField, it) => {
|
|
301
|
-
if (foundField !== null) {
|
|
302
|
-
return foundField;
|
|
303
|
-
}
|
|
304
|
-
const {
|
|
305
|
-
name: fieldName = null,
|
|
306
|
-
fields: subFields = []
|
|
307
|
-
} = it;
|
|
308
|
-
if (name !== null && fieldName === name) {
|
|
309
|
-
return it;
|
|
310
|
-
}
|
|
311
|
-
return getFieldByName(subFields, name);
|
|
312
|
-
}, null);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function getFieldFromPath(path, fields, fieldManager) {
|
|
316
|
-
const initialField = {
|
|
317
|
-
fields
|
|
318
|
-
};
|
|
319
|
-
return (isArray(path) ? path : [path]).reduce((foundField, key) => {
|
|
320
|
-
if (foundField === null) {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
const {
|
|
324
|
-
type = null,
|
|
325
|
-
fields: fieldFields = null,
|
|
326
|
-
field = null
|
|
327
|
-
} = foundField;
|
|
328
|
-
const finalType = field !== null ? field.type || type : type;
|
|
329
|
-
const {
|
|
330
|
-
fields: subFields = null,
|
|
331
|
-
settings = null,
|
|
332
|
-
itemsField = null
|
|
333
|
-
} = finalType !== null ? fieldManager.getDefinition(finalType) : foundField;
|
|
334
|
-
if (itemsField !== null && key.match(/^[0-9]+$/)) {
|
|
335
|
-
return {
|
|
336
|
-
...itemsField,
|
|
337
|
-
name: (isArray(path) ? path : [path]).join('/'),
|
|
338
|
-
listItems: true
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
return getFieldByName([...(fieldFields || []), ...(subFields || []), ...(settings || [])], key);
|
|
342
|
-
}, initialField);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function getFileName(url = null) {
|
|
346
|
-
if (url === null || typeof url.match === 'undefined') {
|
|
347
|
-
return null;
|
|
348
|
-
}
|
|
349
|
-
const match = url.match(/([^/]+)(\?.*)?$/);
|
|
350
|
-
return match ? match[1] : url;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function getPathValue(it, path) {
|
|
354
|
-
const value = get(it, path, null);
|
|
355
|
-
return value !== null && isObject(value) ? Object.values(value).reduce((foundValue, val) => foundValue !== null ? foundValue : val, null) : value;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function getSelectOptions(options) {
|
|
359
|
-
return options.map(it => {
|
|
360
|
-
if (isObject(it)) {
|
|
361
|
-
const {
|
|
362
|
-
label = null,
|
|
363
|
-
value
|
|
364
|
-
} = it;
|
|
365
|
-
if (label === null) {
|
|
366
|
-
return {
|
|
367
|
-
value,
|
|
368
|
-
label: capitalize(`${value}`)
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
return {
|
|
372
|
-
value,
|
|
373
|
-
label: /*#__PURE__*/isValidElement(label) ? label : `${label}`
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
return {
|
|
377
|
-
value: it,
|
|
378
|
-
label: capitalize(`${it}`)
|
|
379
|
-
};
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function isMessage(message) {
|
|
384
|
-
return isObject(message) && (typeof message.defaultMessage !== 'undefined' || typeof message.id !== 'undefined');
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Locale loader
|
|
389
|
-
*/
|
|
390
|
-
const loaders = {};
|
|
391
|
-
const events = new EventEmitter();
|
|
392
|
-
function loadPackage(name, loader) {
|
|
393
|
-
if (typeof loaders[name] === 'undefined') {
|
|
394
|
-
loaders[name] = {
|
|
395
|
-
loader: loader().then(response => {
|
|
396
|
-
loaders[name].loader = null;
|
|
397
|
-
loaders[name].loaded = true;
|
|
398
|
-
loaders[name].resource = response;
|
|
399
|
-
events.emit(`loaded:${name}`, response);
|
|
400
|
-
return response;
|
|
401
|
-
}, error => {
|
|
402
|
-
// Remove the failed loader so a later call can retry
|
|
403
|
-
delete loaders[name];
|
|
404
|
-
throw error;
|
|
405
|
-
}),
|
|
406
|
-
loaded: false,
|
|
407
|
-
resource: null
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
const {
|
|
411
|
-
loaded = false,
|
|
412
|
-
resource = null,
|
|
413
|
-
loader: currentLoader = null
|
|
414
|
-
} = loaders[name];
|
|
415
|
-
if (loaded === true) {
|
|
416
|
-
return Promise.resolve(resource);
|
|
417
|
-
}
|
|
418
|
-
return currentLoader;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
function mergeRefs(...refs) {
|
|
422
|
-
return value => {
|
|
423
|
-
refs.forEach(ref => {
|
|
424
|
-
if (typeof ref === 'function') {
|
|
425
|
-
ref(value);
|
|
426
|
-
} else if (ref !== null) {
|
|
427
|
-
ref.current = value;
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// A date without time (2026-10-01), or a date at midnight with or without a timezone
|
|
434
|
-
// (2026-10-01T00:00:00-04:00, 2026-10-01T00:00:00.000000Z, 2026-10-01 00:00:00)
|
|
435
|
-
const DATE_ONLY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})(?:[T ]00:00(?::00(?:\.0+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Parse a date-only value as a local date, ignoring its timezone.
|
|
439
|
-
*
|
|
440
|
-
* A date stored at midnight represents a calendar day, not an instant: converting it to the
|
|
441
|
-
* browser timezone would show the previous day west of the server timezone (ex: a date saved as
|
|
442
|
-
* 2026-10-01 00:00 UTC is 2026-09-30 20:00 in Montreal). Returns null when the value has a time,
|
|
443
|
-
* so the caller can parse it as an instant.
|
|
444
|
-
*/
|
|
445
|
-
function parseDateOnly(value) {
|
|
446
|
-
if (typeof value !== 'string') {
|
|
447
|
-
return null;
|
|
448
|
-
}
|
|
449
|
-
const match = DATE_ONLY_PATTERN.exec(value.trim());
|
|
450
|
-
if (match === null) {
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
const [, year, month, day] = match;
|
|
454
|
-
return new Date(parseInt(year, 10), parseInt(month, 10) - 1, parseInt(day, 10));
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function toggleSelectedItem(items, item, {
|
|
458
|
-
multiple: multipleSelection = false
|
|
459
|
-
} = {}) {
|
|
460
|
-
const {
|
|
461
|
-
id: itemId = null
|
|
462
|
-
} = item || {};
|
|
463
|
-
const currentItem = (items || []).find(({
|
|
464
|
-
id
|
|
465
|
-
}) => id === itemId) || null;
|
|
466
|
-
if (currentItem !== null) {
|
|
467
|
-
return items.filter(({
|
|
468
|
-
id
|
|
469
|
-
}) => id !== itemId);
|
|
470
|
-
}
|
|
471
|
-
return multipleSelection ? [...(items || []), item] : [item];
|
|
472
|
-
}
|
|
473
|
-
function selectItems(items, itemsToSelect) {
|
|
474
|
-
const newItems = itemsToSelect.filter(item => {
|
|
475
|
-
const {
|
|
476
|
-
id: itemId = null
|
|
477
|
-
} = item || {};
|
|
478
|
-
const currentItem = (items || []).find(({
|
|
479
|
-
id
|
|
480
|
-
}) => id === itemId) || null;
|
|
481
|
-
return currentItem === null;
|
|
482
|
-
});
|
|
483
|
-
return newItems.length > 0 ? [...(items || []), ...newItems] : items;
|
|
484
|
-
}
|
|
485
|
-
function unselectItems(items, itemsToUnselect) {
|
|
486
|
-
const newItems = (items || []).filter(item => {
|
|
487
|
-
const {
|
|
488
|
-
id: itemId = null
|
|
489
|
-
} = item || {};
|
|
490
|
-
const currentItem = itemsToUnselect.find(({
|
|
491
|
-
id
|
|
492
|
-
}) => id === itemId) || null;
|
|
493
|
-
return currentItem === null;
|
|
494
|
-
});
|
|
495
|
-
return items !== null && newItems.length !== items.length ? newItems : items;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function setValue(value, keyParts, fieldValue) {
|
|
499
|
-
const key = keyParts.shift();
|
|
500
|
-
if (!key) {
|
|
501
|
-
return value;
|
|
502
|
-
}
|
|
503
|
-
const isArray = key.match(/^[0-9]+$/) !== null;
|
|
504
|
-
if (value !== null || fieldValue !== null) {
|
|
505
|
-
if (isArray) {
|
|
506
|
-
const index = parseInt(key, 10);
|
|
507
|
-
const arrayValue = Array.isArray(value) ? value : [];
|
|
508
|
-
let newArrayValue;
|
|
509
|
-
if (fieldValue !== null) {
|
|
510
|
-
newArrayValue = [...arrayValue];
|
|
511
|
-
// Pad with null so an out-of-range index lands at the right position
|
|
512
|
-
while (newArrayValue.length < index) {
|
|
513
|
-
newArrayValue.push(null);
|
|
514
|
-
}
|
|
515
|
-
newArrayValue[index] = keyParts.length > 0 ? setValue(arrayValue[index] || null, keyParts, fieldValue) : fieldValue;
|
|
516
|
-
} else {
|
|
517
|
-
newArrayValue = [...arrayValue.slice(0, index), ...arrayValue.slice(index + 1)];
|
|
518
|
-
}
|
|
519
|
-
return newArrayValue.length > 0 ? newArrayValue : null;
|
|
520
|
-
}
|
|
521
|
-
const objectValue = value;
|
|
522
|
-
return {
|
|
523
|
-
...objectValue,
|
|
524
|
-
[key]: keyParts.length > 0 ? setValue(objectValue !== null ? objectValue[key] || null : null, keyParts, fieldValue) : fieldValue
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
return null;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function slug(str, separator = null) {
|
|
531
|
-
let toSlug;
|
|
532
|
-
if (separator === '-') {
|
|
533
|
-
toSlug = kebabCase(str);
|
|
534
|
-
} else {
|
|
535
|
-
toSlug = snakeCase(str);
|
|
536
|
-
}
|
|
537
|
-
return slugify(toSlug, {
|
|
538
|
-
lower: true
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function validateFields(fields, value) {
|
|
543
|
-
return fields.reduce((acc, field) => {
|
|
544
|
-
if (acc === true) {
|
|
545
|
-
if (field.type === 'fields' && field.fields) {
|
|
546
|
-
return validateFields(field.fields, value);
|
|
547
|
-
}
|
|
548
|
-
const val = value && field.name ? value[field.name] : null;
|
|
549
|
-
// Only null, undefined and empty strings are considered missing (0 and false are valid)
|
|
550
|
-
const isMissing = val === null || typeof val === 'undefined' || val === '';
|
|
551
|
-
return !(field.required && isMissing);
|
|
552
|
-
}
|
|
553
|
-
return acc;
|
|
554
|
-
}, true);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
export { checkClickable, convertStyleToString, copyToClipboard, createNullableOnChange, createUseEvent, flattenComponents, formatDuration, getColumnsWithFields, getComponent, getComponentFromName, getDefinitionFromId, getDefinitionWarnings, getDisplayName, getFieldByName, getFieldFromPath, getFileName, getPathValue, getSelectOptions, isMessage, loadPackage, mergeRefs, parseDateOnly, selectItems, setValue as setFieldValue, slug, toggleSelectedItem, unselectItems, validateFields };
|