decap-cms-core 3.9.0 → 3.10.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 (43) hide show
  1. package/dist/decap-cms-core.js +24 -24
  2. package/dist/decap-cms-core.js.LICENSE.txt +10 -0
  3. package/dist/decap-cms-core.js.map +1 -1
  4. package/dist/esm/actions/config.js +19 -0
  5. package/dist/esm/actions/entries.js +10 -1
  6. package/dist/esm/bootstrap.js +2 -2
  7. package/dist/esm/components/App/StatusBar.js +41 -0
  8. package/dist/esm/components/Collection/CollectionControls.js +3 -2
  9. package/dist/esm/components/Collection/ViewStyleControl.js +6 -3
  10. package/dist/esm/components/Editor/EditorControlPane/EditorControl.js +8 -5
  11. package/dist/esm/components/Editor/EditorNotesPane/AddNoteForm.js +17 -14
  12. package/dist/esm/components/Editor/EditorNotesPane/EditorNotesPane.js +75 -15
  13. package/dist/esm/components/Editor/EditorNotesPane/NoteItem.js +21 -24
  14. package/dist/esm/components/Editor/EditorNotesPane/NotesList.js +4 -4
  15. package/dist/esm/components/MediaLibrary/MediaLibraryHeader.js +5 -3
  16. package/dist/esm/components/MediaLibrary/MediaLibraryTop.js +5 -4
  17. package/dist/esm/components/UI/ErrorBoundary.js +6 -9
  18. package/dist/esm/components/UI/SettingsDropdown.js +9 -7
  19. package/dist/esm/constants/configSchema.js +41 -23
  20. package/dist/esm/lib/formatters.js +2 -1
  21. package/dist/esm/lib/indexFileHelper.js +36 -0
  22. package/dist/esm/reducers/collections.js +54 -5
  23. package/dist/esm/reducers/entries.js +8 -2
  24. package/index.d.ts +8 -2
  25. package/package.json +2 -3
  26. package/src/actions/__tests__/config.spec.js +4 -4
  27. package/src/actions/config.ts +22 -0
  28. package/src/actions/entries.ts +11 -1
  29. package/src/components/Collection/CollectionControls.js +1 -1
  30. package/src/components/Collection/ViewStyleControl.js +3 -1
  31. package/src/components/Editor/EditorControlPane/EditorControl.js +1 -0
  32. package/src/components/MediaLibrary/MediaLibraryHeader.js +2 -2
  33. package/src/components/MediaLibrary/MediaLibraryTop.js +1 -0
  34. package/src/components/UI/ErrorBoundary.js +1 -2
  35. package/src/components/UI/SettingsDropdown.js +1 -1
  36. package/src/constants/__tests__/configSchema.spec.js +84 -0
  37. package/src/constants/configSchema.js +34 -1
  38. package/src/lib/__tests__/formatters.spec.js +30 -2
  39. package/src/lib/formatters.ts +6 -1
  40. package/src/reducers/__tests__/collections.spec.js +39 -0
  41. package/src/reducers/collections.ts +52 -5
  42. package/src/reducers/entries.ts +12 -2
  43. package/src/types/redux.ts +9 -3
@@ -412,23 +412,46 @@ export function selectDefaultSortableFields(
412
412
  defaultSortable = [COMMIT_DATE, ...defaultSortable];
413
413
  }
414
414
 
415
- return defaultSortable as string[];
415
+ // Return as objects with field property
416
+ return defaultSortable.map(field => ({ field })) as {
417
+ field: string;
418
+ label?: string;
419
+ default_sort?: boolean | 'asc' | 'desc';
420
+ }[];
416
421
  }
417
422
 
418
423
  export function selectSortableFields(collection: Collection, t: (key: string) => string) {
419
424
  const fields = collection
420
425
  .get('sortable_fields')
421
426
  .toArray()
422
- .map(key => {
427
+ .map(sortableField => {
428
+ // Extract the field name and custom label from the sortable field object
429
+ const key = sortableField.get('field');
430
+ const customLabel = sortableField.get('label');
431
+
423
432
  if (key === COMMIT_DATE) {
424
- return { key, field: { name: key, label: t('collection.defaultFields.updatedOn.label') } };
433
+ const label = customLabel || t('collection.defaultFields.updatedOn.label');
434
+ return { key, field: { name: key, label } };
425
435
  }
426
436
  const field = selectField(collection, key);
427
437
  if (key === COMMIT_AUTHOR && !field) {
428
- return { key, field: { name: key, label: t('collection.defaultFields.author.label') } };
438
+ const label = customLabel || t('collection.defaultFields.author.label');
439
+ return { key, field: { name: key, label } };
440
+ }
441
+
442
+ let fieldObj: Record<string, unknown> | undefined = field?.toJS();
443
+
444
+ // If custom label is provided, override the field's label
445
+ if (fieldObj && customLabel) {
446
+ fieldObj = { ...fieldObj, label: customLabel };
429
447
  }
430
448
 
431
- return { key, field: field?.toJS() };
449
+ // If no label exists at all, use the field name
450
+ if (fieldObj && !fieldObj.label) {
451
+ fieldObj = { ...fieldObj, label: (fieldObj.name as string) || key };
452
+ }
453
+
454
+ return { key, field: fieldObj };
432
455
  })
433
456
  .filter(item => !!item.field)
434
457
  .map(item => ({ ...item.field, key: item.key }));
@@ -436,6 +459,30 @@ export function selectSortableFields(collection: Collection, t: (key: string) =>
436
459
  return fields;
437
460
  }
438
461
 
462
+ export function selectDefaultSortField(collection: Collection) {
463
+ const sortableFields = collection.get('sortable_fields').toArray();
464
+ const defaultField = sortableFields.find(field => field.get('default_sort') !== undefined);
465
+
466
+ if (!defaultField) {
467
+ return null;
468
+ }
469
+
470
+ const fieldName = defaultField.get('field');
471
+ const defaultSortValue = defaultField.get('default_sort');
472
+
473
+ // Determine direction based on default_sort value
474
+ let direction;
475
+ if (defaultSortValue === true || defaultSortValue === 'asc') {
476
+ direction = 'asc';
477
+ } else if (defaultSortValue === 'desc') {
478
+ direction = 'desc';
479
+ } else {
480
+ direction = 'asc'; // fallback
481
+ }
482
+
483
+ return { field: fieldName, direction };
484
+ }
485
+
439
486
  export function selectSortDataPath(collection: Collection, key: string) {
440
487
  if (key === COMMIT_DATE) {
441
488
  return 'updatedOn';
@@ -78,6 +78,14 @@ let slug: string;
78
78
 
79
79
  const storageSortKey = 'decap-cms.entries.sort';
80
80
  const viewStyleKey = 'decap-cms.entries.viewStyle';
81
+
82
+ function normalizeDoubleSlashes(path: string) {
83
+ if (!path) {
84
+ return path;
85
+ }
86
+
87
+ return path.replace(/([^:]\/)\/+/g, '$1');
88
+ }
81
89
  type StorageSortObject = SortObject & { index: number };
82
90
  type StorageSort = { [collection: string]: { [key: string]: StorageSortObject } };
83
91
 
@@ -785,12 +793,14 @@ export function selectMediaFilePublicPath(
785
793
  }
786
794
 
787
795
  const name = 'public_folder';
788
- let publicFolder = config[name]!;
796
+ let publicFolder = normalizeDoubleSlashes(config[name]!);
789
797
 
790
798
  const customFolder = hasCustomFolder(name, collection, entryMap?.get('slug'), field);
791
799
 
792
800
  if (customFolder) {
793
- publicFolder = evaluateFolder(name, config, collection!, entryMap, field);
801
+ publicFolder = normalizeDoubleSlashes(
802
+ evaluateFolder(name, config, collection!, entryMap, field),
803
+ );
794
804
  }
795
805
 
796
806
  if (isAbsolutePath(publicFolder)) {
@@ -309,6 +309,12 @@ export interface ViewGroup {
309
309
  id: string;
310
310
  }
311
311
 
312
+ export interface SortableField {
313
+ field: string;
314
+ label?: string;
315
+ default_sort?: boolean | 'asc' | 'desc';
316
+ }
317
+
312
318
  export interface CmsCollection {
313
319
  name: string;
314
320
  label: string;
@@ -348,7 +354,7 @@ export interface CmsCollection {
348
354
  path?: string;
349
355
  media_folder?: string;
350
356
  public_folder?: string;
351
- sortable_fields?: string[];
357
+ sortable_fields?: (string | SortableField)[];
352
358
  view_filters?: ViewFilter[];
353
359
  view_groups?: ViewGroup[];
354
360
  i18n?: boolean | CmsI18nConfig;
@@ -356,7 +362,7 @@ export interface CmsCollection {
356
362
  /**
357
363
  * @deprecated Use sortable_fields instead
358
364
  */
359
- sortableFields?: string[];
365
+ sortableFields?: (string | SortableField)[];
360
366
  }
361
367
 
362
368
  export interface CmsBackend {
@@ -635,7 +641,7 @@ type CollectionObject = {
635
641
  slug?: string;
636
642
  label_singular?: string;
637
643
  label: string;
638
- sortable_fields: List<string>;
644
+ sortable_fields: List<StaticallyTypedRecord<SortableField>>;
639
645
  view_filters: List<StaticallyTypedRecord<ViewFilter>>;
640
646
  view_groups: List<StaticallyTypedRecord<ViewGroup>>;
641
647
  nested?: Nested;