@contentful/field-editor-shared 1.2.0 → 1.3.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.
Files changed (50) hide show
  1. package/dist/cjs/CharCounter.js +75 -0
  2. package/dist/cjs/CharValidation.js +60 -0
  3. package/dist/cjs/FieldConnector.js +167 -0
  4. package/dist/cjs/FieldConnector.test.js +81 -0
  5. package/dist/cjs/ModalDialogLauncher.js +132 -0
  6. package/dist/cjs/PredefinedValuesError.js +57 -0
  7. package/dist/cjs/index.js +155 -0
  8. package/dist/cjs/types.js +4 -0
  9. package/dist/cjs/typesEntity.js +31 -0
  10. package/dist/cjs/utils/constraints.js +63 -0
  11. package/dist/cjs/utils/entityHelpers.js +214 -0
  12. package/dist/cjs/utils/isValidImage.js +26 -0
  13. package/dist/cjs/utils/shortenStorageUnit.js +55 -0
  14. package/dist/esm/CharCounter.js +21 -0
  15. package/dist/esm/CharValidation.js +11 -0
  16. package/dist/esm/FieldConnector.js +113 -0
  17. package/dist/esm/FieldConnector.test.js +33 -0
  18. package/dist/esm/ModalDialogLauncher.js +67 -0
  19. package/dist/esm/PredefinedValuesError.js +8 -0
  20. package/dist/esm/index.js +14 -0
  21. package/dist/esm/types.js +1 -0
  22. package/dist/esm/typesEntity.js +1 -0
  23. package/dist/esm/utils/constraints.js +40 -0
  24. package/dist/esm/utils/entityHelpers.js +170 -0
  25. package/dist/esm/utils/isValidImage.js +16 -0
  26. package/dist/esm/utils/shortenStorageUnit.js +37 -0
  27. package/dist/types/CharCounter.d.ts +7 -0
  28. package/dist/{CharValidation.d.ts → types/CharValidation.d.ts} +7 -7
  29. package/dist/{FieldConnector.d.ts → types/FieldConnector.d.ts} +48 -48
  30. package/dist/types/FieldConnector.test.d.ts +1 -0
  31. package/dist/{ModalDialogLauncher.d.ts → types/ModalDialogLauncher.d.ts} +13 -13
  32. package/dist/types/PredefinedValuesError.d.ts +2 -0
  33. package/dist/{index.d.ts → types/index.d.ts} +15 -15
  34. package/dist/{types.d.ts → types/types.d.ts} +11 -11
  35. package/dist/{typesEntity.d.ts → types/typesEntity.d.ts} +9 -9
  36. package/dist/{utils → types/utils}/constraints.d.ts +3 -3
  37. package/dist/{utils → types/utils}/entityHelpers.d.ts +68 -68
  38. package/dist/{utils → types/utils}/isValidImage.d.ts +5 -5
  39. package/dist/{utils → types/utils}/shortenStorageUnit.d.ts +17 -17
  40. package/package.json +26 -12
  41. package/CHANGELOG.md +0 -196
  42. package/dist/CharCounter.d.ts +0 -7
  43. package/dist/PredefinedValuesError.d.ts +0 -2
  44. package/dist/field-editor-shared.cjs.development.js +0 -634
  45. package/dist/field-editor-shared.cjs.development.js.map +0 -1
  46. package/dist/field-editor-shared.cjs.production.min.js +0 -2
  47. package/dist/field-editor-shared.cjs.production.min.js.map +0 -1
  48. package/dist/field-editor-shared.esm.js +0 -619
  49. package/dist/field-editor-shared.esm.js.map +0 -1
  50. package/dist/index.js +0 -8
@@ -1,634 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
-
7
- var React = _interopDefault(require('react'));
8
- var isEqual = _interopDefault(require('lodash/isEqual'));
9
- var throttle = _interopDefault(require('lodash/throttle'));
10
- var f36Note = require('@contentful/f36-note');
11
- var tokens = _interopDefault(require('@contentful/f36-tokens'));
12
- var emotion = require('emotion');
13
- var ReactDOM = _interopDefault(require('react-dom'));
14
- var f36Components = require('@contentful/f36-components');
15
- var isNumber = _interopDefault(require('lodash/isNumber'));
16
- var get = _interopDefault(require('lodash/get'));
17
- var isObject = _interopDefault(require('lodash/isObject'));
18
- var isString = _interopDefault(require('lodash/isString'));
19
-
20
- class FieldConnector extends React.Component {
21
- constructor(props) {
22
- super(props);
23
- this.unsubscribeErrors = null;
24
- this.unsubscribeDisabled = null;
25
- this.unsubscribeValue = null;
26
-
27
- this.setValue = async value => {
28
- if (this.props.isEmptyValue(value ?? null)) {
29
- this.setState({
30
- value: undefined
31
- });
32
- } else {
33
- this.setState({
34
- value
35
- });
36
- }
37
-
38
- await this.triggerSetValueCallbacks(value);
39
- };
40
-
41
- this.triggerSetValueCallbacks = throttle(value => {
42
- return new Promise((resolve, reject) => {
43
- if (this.props.isEmptyValue(value ?? null)) {
44
- this.props.field.removeValue().then(resolve).catch(reject);
45
- } else {
46
- this.props.field.setValue(value).then(resolve).catch(reject);
47
- }
48
- });
49
- }, this.props.throttle, {
50
- leading: this.props.throttle === 0
51
- });
52
- const initialValue = props.field.getValue();
53
- this.state = {
54
- isLocalValueChange: false,
55
- externalReset: 0,
56
- value: initialValue,
57
- lastRemoteValue: initialValue,
58
- disabled: props.isInitiallyDisabled ?? false,
59
- errors: []
60
- };
61
- }
62
-
63
- componentDidMount() {
64
- const {
65
- field
66
- } = this.props;
67
- this.unsubscribeErrors = field.onSchemaErrorsChanged(errors => {
68
- this.setState({
69
- errors: errors || []
70
- });
71
- });
72
- this.unsubscribeDisabled = field.onIsDisabledChanged(disabled => {
73
- this.setState({
74
- disabled
75
- });
76
- });
77
- this.unsubscribeValue = field.onValueChanged(value => {
78
- this.setState(currentState => {
79
- const isLocalValueChange = this.props.isEqualValues(value, currentState.value);
80
- const lastRemoteValue = isLocalValueChange ? currentState.lastRemoteValue : value;
81
- const externalReset = currentState.externalReset + (isLocalValueChange ? 0 : 1);
82
- return {
83
- value,
84
- lastRemoteValue,
85
- isLocalValueChange,
86
- externalReset
87
- };
88
- });
89
- });
90
- }
91
-
92
- componentWillUnmount() {
93
- if (typeof this.unsubscribeErrors === 'function') {
94
- this.unsubscribeErrors();
95
- }
96
-
97
- if (typeof this.unsubscribeDisabled === 'function') {
98
- this.unsubscribeDisabled();
99
- }
100
-
101
- if (typeof this.unsubscribeValue === 'function') {
102
- this.unsubscribeValue();
103
- }
104
- }
105
-
106
- render() {
107
- return this.props.children({ ...this.state,
108
- setValue: this.setValue,
109
- disabled: this.props.isDisabled || this.state.disabled
110
- });
111
- }
112
-
113
- }
114
- FieldConnector.defaultProps = {
115
- children: () => {
116
- return null;
117
- },
118
- // eslint-disable-next-line -- TODO: describe this disable
119
- isEmptyValue: value => {
120
- return value === null || value === '';
121
- },
122
- // eslint-disable-next-line -- TODO: describe this disable
123
- isEqualValues: (value1, value2) => {
124
- return isEqual(value1, value2);
125
- },
126
- throttle: 300
127
- };
128
-
129
- function PredefinedValuesError() {
130
- return React.createElement(f36Note.Note, {
131
- variant: "warning",
132
- testId: "predefined-values-warning"
133
- }, "The widget failed to initialize. You can fix the problem by providing predefined values under the validations tab in the field settings.");
134
- }
135
-
136
- const styles = {
137
- invalid: /*#__PURE__*/emotion.css({
138
- color: tokens.red600
139
- })
140
- };
141
- function CharCounter(props) {
142
- let count = 0;
143
-
144
- if (props.value) {
145
- count = props.value.length;
146
- }
147
-
148
- const valid = count === 0 || props.checkConstraint(count);
149
- return React.createElement("span", {
150
- "data-status-code": valid ? null : 'invalid-size',
151
- className: emotion.cx({
152
- [styles.invalid]: !valid
153
- })
154
- }, count, " characters");
155
- }
156
-
157
- function CharValidation(props) {
158
- const {
159
- constraints
160
- } = props;
161
-
162
- if (constraints.type === 'max') {
163
- return React.createElement("span", null, "Maximum ", constraints.max, " characters");
164
- } else if (constraints.type === 'min') {
165
- return React.createElement("span", null, "Requires at least ", constraints.min, " characters");
166
- } else {
167
- return React.createElement("span", null, "Requires between ", constraints.min, " and ", constraints.max, " characters");
168
- }
169
- }
170
-
171
- /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-use-before-define */
172
- function open(componentRenderer) {
173
- let rootDom = null;
174
-
175
- const getRoot = () => {
176
- if (rootDom === null) {
177
- rootDom = document.createElement('div');
178
- rootDom.setAttribute('id', 'field-editor-modal-root');
179
- document.body.appendChild(rootDom);
180
- }
181
-
182
- return rootDom;
183
- };
184
-
185
- return new Promise(resolve => {
186
- let currentConfig = {
187
- onClose,
188
- isShown: true
189
- };
190
-
191
- function render({
192
- onClose,
193
- isShown
194
- }) {
195
- ReactDOM.render(componentRenderer({
196
- onClose,
197
- isShown
198
- }), getRoot());
199
- }
200
-
201
- function onClose(...args) {
202
- currentConfig = { ...currentConfig,
203
- isShown: false
204
- };
205
- render(currentConfig); // eslint-disable-next-line -- TODO: describe this disable @typescript-eslint/ban-ts-comment
206
- // @ts-ignore
207
-
208
- resolve(...args);
209
- getRoot().remove();
210
- }
211
-
212
- render(currentConfig);
213
- });
214
- }
215
- function openDialog(options, Component) {
216
- const key = Date.now();
217
- const size = isNumber(options.width) ? `${options.width}px` : options.width;
218
- return open(({
219
- isShown,
220
- onClose
221
- }) => {
222
- const onCloseHandler = () => onClose();
223
-
224
- return React.createElement(f36Components.Modal, {
225
- key: key,
226
- shouldCloseOnOverlayClick: options.shouldCloseOnOverlayClick || false,
227
- shouldCloseOnEscapePress: options.shouldCloseOnEscapePress || false,
228
- allowHeightOverflow: options.allowHeightOverflow || false,
229
- position: options.position || 'center',
230
- isShown: isShown,
231
- onClose: onCloseHandler,
232
- size: size || '700px'
233
- }, () => React.createElement(React.Fragment, null, options.title && React.createElement(f36Components.ModalHeader, {
234
- testId: "dialog-title",
235
- title: options.title,
236
- onClose: onCloseHandler
237
- }), React.createElement("div", {
238
- style: {
239
- minHeight: options.minHeight || 'auto'
240
- }
241
- }, React.createElement(Component, {
242
- onClose: onClose
243
- }))));
244
- });
245
- }
246
- var ModalDialogLauncher = {
247
- openDialog
248
- };
249
-
250
- var ModalDialogLauncher$1 = {
251
- __proto__: null,
252
- open: open,
253
- openDialog: openDialog,
254
- 'default': ModalDialogLauncher
255
- };
256
-
257
- function titleOrDefault(title, defaultTitle) {
258
- if (!isString(title)) {
259
- return defaultTitle;
260
- }
261
-
262
- if (title) {
263
- const trimmedTitle = title.trim();
264
-
265
- if (trimmedTitle.length === 0) {
266
- return defaultTitle;
267
- }
268
-
269
- return trimmedTitle;
270
- }
271
-
272
- return defaultTitle;
273
- }
274
-
275
- function getFieldValue({
276
- /**
277
- * Expects an entity fetched with a flag Skip-Transformation: true
278
- */
279
- entity,
280
- fieldId,
281
- localeCode,
282
- defaultLocaleCode
283
- }) {
284
- const values = get(entity, ['fields', fieldId]);
285
-
286
- if (!isObject(values)) {
287
- return;
288
- }
289
-
290
- const firstLocaleCode = Object.keys(values)[0];
291
- return values[localeCode] || values[defaultLocaleCode] || values[firstLocaleCode];
292
- }
293
- function getAssetTitle({
294
- asset,
295
- localeCode,
296
- defaultLocaleCode,
297
- defaultTitle
298
- }) {
299
- var _asset$fields;
300
-
301
- const title = getFieldValue({
302
- entity: {
303
- fields: {
304
- title: (_asset$fields = asset.fields) == null ? void 0 : _asset$fields.title
305
- }
306
- },
307
- fieldId: 'title',
308
- localeCode,
309
- defaultLocaleCode
310
- });
311
- return titleOrDefault(title, defaultTitle);
312
- }
313
- /**
314
- * Returns true if field is an Asset
315
- *
316
- * @param field
317
- * @returns {boolean}
318
- */
319
-
320
- const isAssetField = field => field.type === 'Link' && field.linkType === 'Asset';
321
- /**
322
- * Returns true if field is a Title
323
- */
324
-
325
- function isDisplayField({
326
- field,
327
- contentType
328
- }) {
329
- return field.id === contentType.displayField;
330
- }
331
- /**
332
- * Returns true if field is a short Description
333
- */
334
-
335
- function isDescriptionField({
336
- field,
337
- contentType
338
- }) {
339
- const isTextField = field => ['Symbol', 'Text'].includes(field.type);
340
-
341
- const isMaybeSlugField = field => /\bslug\b/.test(field.name);
342
-
343
- return isTextField(field) && !isDisplayField({
344
- field,
345
- contentType
346
- }) && !isMaybeSlugField(field);
347
- }
348
- function getEntityDescription({
349
- entity,
350
- contentType,
351
- localeCode,
352
- defaultLocaleCode
353
- }) {
354
- if (!contentType) {
355
- return '';
356
- }
357
-
358
- const descriptionField = contentType.fields.find(field => isDescriptionField({
359
- field,
360
- contentType
361
- }));
362
-
363
- if (!descriptionField) {
364
- return '';
365
- }
366
-
367
- return getFieldValue({
368
- entity,
369
- fieldId: descriptionField.id,
370
- localeCode,
371
- defaultLocaleCode
372
- }) || '';
373
- }
374
- function getEntryTitle({
375
- entry,
376
- contentType,
377
- localeCode,
378
- defaultLocaleCode,
379
- defaultTitle
380
- }) {
381
- let title;
382
-
383
- if (!contentType) {
384
- return defaultTitle;
385
- }
386
-
387
- const displayField = contentType.displayField;
388
-
389
- if (!displayField) {
390
- return defaultTitle;
391
- }
392
-
393
- const displayFieldInfo = contentType.fields.find(field => field.id === displayField);
394
-
395
- if (!displayFieldInfo) {
396
- return defaultTitle;
397
- } // when localization for a field is "turned off",
398
- // we don't clean up the "old" localized data, so it is still in the response.
399
- // Therefore, we're checking if displayField is localizable.
400
-
401
-
402
- if (displayFieldInfo.localized) {
403
- title = getFieldValue({
404
- entity: entry,
405
- fieldId: displayField,
406
- localeCode,
407
- defaultLocaleCode
408
- });
409
-
410
- if (!title) {
411
- // Older content types may return id/apiName, but some entry lookup paths do not fetch raw data
412
- // In order to still return a title in this case, look for displayField as apiName in content type,
413
- // ...but still look for displayField as a field in the entry
414
- title = getFieldValue({
415
- entity: entry,
416
- fieldId: displayFieldInfo.id,
417
- localeCode,
418
- defaultLocaleCode
419
- });
420
- }
421
- } else {
422
- title = getFieldValue({
423
- entity: entry,
424
- fieldId: displayField,
425
- defaultLocaleCode,
426
- localeCode: ''
427
- });
428
-
429
- if (!title) {
430
- title = getFieldValue({
431
- entity: entry,
432
- fieldId: displayFieldInfo.id,
433
- defaultLocaleCode,
434
- localeCode: ''
435
- });
436
- }
437
- }
438
-
439
- return titleOrDefault(title, defaultTitle);
440
- }
441
- function getEntryStatus(sys) {
442
- if (!sys || sys.type !== 'Entry' && sys.type !== 'Asset') {
443
- throw new TypeError('Invalid entity metadata object');
444
- }
445
-
446
- if (sys.deletedVersion) {
447
- return 'deleted';
448
- } else if (sys.archivedVersion) {
449
- return 'archived';
450
- } else if (sys.publishedVersion) {
451
- if (sys.version > sys.publishedVersion + 1) {
452
- return 'changed';
453
- } else {
454
- return 'published';
455
- }
456
- } else {
457
- return 'draft';
458
- }
459
- }
460
- /**
461
- * Gets a promise resolving with a localized asset image field representing a
462
- * given entities file. The promise may resolve with null.
463
- */
464
-
465
- const getEntryImage = async ({
466
- entry,
467
- contentType,
468
- localeCode
469
- }, getAsset) => {
470
- if (!contentType) {
471
- return null;
472
- }
473
-
474
- const assetLink = contentType.fields.find(isAssetField);
475
-
476
- if (!assetLink) {
477
- return null;
478
- }
479
-
480
- const assetId = get(entry.fields, [assetLink.id, localeCode, 'sys', 'id']);
481
-
482
- if (!assetId) {
483
- return null;
484
- }
485
-
486
- try {
487
- const asset = await getAsset(assetId);
488
- const file = get(asset, ['fields', 'file', localeCode]);
489
- const isImage = Boolean(get(file, ['details', 'image'], false));
490
- return isImage ? file : null;
491
- } catch (e) {
492
- return null;
493
- }
494
- };
495
-
496
- var entityHelpers = {
497
- __proto__: null,
498
- getFieldValue: getFieldValue,
499
- getAssetTitle: getAssetTitle,
500
- isAssetField: isAssetField,
501
- isDisplayField: isDisplayField,
502
- isDescriptionField: isDescriptionField,
503
- getEntityDescription: getEntityDescription,
504
- getEntryTitle: getEntryTitle,
505
- getEntryStatus: getEntryStatus,
506
- getEntryImage: getEntryImage
507
- };
508
-
509
- /* eslint-disable @typescript-eslint/no-explicit-any */
510
- function fromFieldValidations(validations = [], fieldType) {
511
- const sizeValidation = validations.find(v => 'size' in v);
512
- const size = sizeValidation && sizeValidation.size || {};
513
- const min = size.min;
514
- const max = size.max;
515
-
516
- if (isNumber(min) && isNumber(max)) {
517
- return {
518
- type: 'min-max',
519
- min,
520
- max
521
- };
522
- } else if (isNumber(min)) {
523
- return {
524
- type: 'min',
525
- min
526
- };
527
- } else if (isNumber(max)) {
528
- return {
529
- type: 'max',
530
- max
531
- };
532
- } else {
533
- return {
534
- type: 'max',
535
- max: fieldType === 'Symbol' ? 256 : 50000
536
- };
537
- }
538
- }
539
- function makeChecker(constraint) {
540
- return function checkConstraint(length) {
541
- if (constraint.type === 'max') {
542
- return length <= constraint.max;
543
- } else if (constraint.type === 'min') {
544
- return length >= constraint.min;
545
- } else {
546
- return length >= constraint.min && length <= constraint.max;
547
- }
548
- };
549
- }
550
-
551
- var constraints = {
552
- __proto__: null,
553
- fromFieldValidations: fromFieldValidations,
554
- makeChecker: makeChecker
555
- };
556
-
557
- /**
558
- * Checks whether the passed content type matches one of our valid MIME types
559
- */
560
- function isValidImage(file) {
561
- const validMimeTypes = ['image/avif', 'image/bmp', 'image/x-windows-bmp', 'image/gif', 'image/webp', // This is not a valid MIME type but we supported it in the past.
562
- 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/x-jps', 'image/png', 'image/svg+xml'];
563
- return validMimeTypes.includes(file.contentType);
564
- }
565
-
566
- /**
567
- * Transforms a number into a localized string (en-US)
568
- * toLocaleString(1000); // "1,000"
569
- * @param {Number} number
570
- */
571
- function toLocaleString(number) {
572
- return number.toLocaleString('en-US');
573
- }
574
-
575
- function formatFloat(value, localize) {
576
- return localize ? toLocaleString(value) : value.toFixed(2) // remove floating point if not necessary
577
- .replace(/\.(0)*$|0*$/, '');
578
- }
579
- /**
580
- * Make a storage unit number more readable by making them smaller
581
- * shortenStorageUnit(1000, 'GB'); // "1 TB"
582
- * shortenStorageUnit(0.001, 'TB'); // "1 GB"
583
- * @param value
584
- * @param uom Unit of measure
585
- * @returns
586
- */
587
-
588
-
589
- function shortenStorageUnit(value, uom) {
590
- if (value <= 0) {
591
- return '0 B';
592
- }
593
-
594
- const units = ['PB', 'TB', 'GB', 'MB', 'KB', 'B'];
595
-
596
- const getBigger = unit => units[units.indexOf(unit) - 1];
597
-
598
- const getSmaller = unit => units[units.indexOf(unit) + 1];
599
-
600
- const isBiggestUnit = unit => units.indexOf(unit) === 0;
601
-
602
- const isSmallestUnit = unit => units.indexOf(unit) === units.length - 1;
603
-
604
- const reduce = (number, unit) => {
605
- if (number < 0.99 && !isSmallestUnit(unit)) {
606
- return reduce(number * 1000, getSmaller(unit));
607
- } else if (number >= 1000 && !isBiggestUnit(unit)) {
608
- return reduce(number / 1000, getBigger(unit));
609
- } else {
610
- return {
611
- number,
612
- unit
613
- };
614
- }
615
- };
616
-
617
- const {
618
- number,
619
- unit
620
- } = reduce(value, uom);
621
- return `${formatFloat(number, false)} ${unit}`;
622
- }
623
-
624
- exports.CharCounter = CharCounter;
625
- exports.CharValidation = CharValidation;
626
- exports.ConstraintsUtils = constraints;
627
- exports.FieldConnector = FieldConnector;
628
- exports.ModalDialogLauncher = ModalDialogLauncher$1;
629
- exports.PredefinedValuesError = PredefinedValuesError;
630
- exports.entityHelpers = entityHelpers;
631
- exports.isValidImage = isValidImage;
632
- exports.shortenStorageUnit = shortenStorageUnit;
633
- exports.toLocaleString = toLocaleString;
634
- //# sourceMappingURL=field-editor-shared.cjs.development.js.map