decap-cms-core 3.8.1 → 3.10.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.
Files changed (37) hide show
  1. package/dist/decap-cms-core.js +23 -23
  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/Entries/Pagination.js +132 -0
  9. package/dist/esm/components/Editor/EditorNotesPane/AddNoteForm.js +111 -0
  10. package/dist/esm/components/Editor/EditorNotesPane/EditorNotesPane.js +138 -0
  11. package/dist/esm/components/Editor/EditorNotesPane/NoteItem.js +247 -0
  12. package/dist/esm/components/Editor/EditorNotesPane/NotesList.js +54 -0
  13. package/dist/esm/components/UI/ErrorBoundary.js +6 -9
  14. package/dist/esm/constants/configSchema.js +41 -23
  15. package/dist/esm/lib/entryCache.js +145 -0
  16. package/dist/esm/lib/entryHelpers.js +102 -0
  17. package/dist/esm/lib/formatters.js +2 -1
  18. package/dist/esm/lib/immutableHelpers.js +21 -0
  19. package/dist/esm/lib/indexFileHelper.js +36 -0
  20. package/dist/esm/lib/pagination.js +68 -0
  21. package/dist/esm/reducers/collections.js +54 -5
  22. package/dist/esm/reducers/entries.js +9 -4
  23. package/index.d.ts +10 -2
  24. package/package.json +2 -3
  25. package/src/actions/__tests__/config.spec.js +4 -4
  26. package/src/actions/config.ts +22 -0
  27. package/src/actions/entries.ts +11 -1
  28. package/src/components/UI/ErrorBoundary.js +1 -2
  29. package/src/constants/__tests__/configSchema.spec.js +84 -0
  30. package/src/constants/configSchema.js +34 -1
  31. package/src/lib/__tests__/formatters.spec.js +30 -2
  32. package/src/lib/formatters.ts +6 -1
  33. package/src/reducers/__tests__/collections.spec.js +39 -0
  34. package/src/reducers/__tests__/entries.spec.js +3 -3
  35. package/src/reducers/collections.ts +52 -5
  36. package/src/reducers/entries.ts +13 -5
  37. package/src/types/redux.ts +11 -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
 
@@ -532,8 +540,6 @@ export function selectIsFetching(state: Entries, collection: string) {
532
540
  return state.getIn(['pages', collection, 'isFetching'], false);
533
541
  }
534
542
 
535
- const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES';
536
-
537
543
  function getFileField(collectionFiles: CollectionFiles, slug: string | undefined) {
538
544
  const file = collectionFiles.find(f => f?.get('name') === slug);
539
545
  return file;
@@ -752,7 +758,7 @@ export function selectMediaFolder(
752
758
  const entryPath = entryMap?.get('path');
753
759
  mediaFolder = entryPath
754
760
  ? join(dirname(entryPath), folder)
755
- : join(collection!.get('folder') as string, DRAFT_MEDIA_FILES);
761
+ : (collection!.get('folder') as string);
756
762
  }
757
763
  }
758
764
 
@@ -787,12 +793,14 @@ export function selectMediaFilePublicPath(
787
793
  }
788
794
 
789
795
  const name = 'public_folder';
790
- let publicFolder = config[name]!;
796
+ let publicFolder = normalizeDoubleSlashes(config[name]!);
791
797
 
792
798
  const customFolder = hasCustomFolder(name, collection, entryMap?.get('slug'), field);
793
799
 
794
800
  if (customFolder) {
795
- publicFolder = evaluateFolder(name, config, collection!, entryMap, field);
801
+ publicFolder = normalizeDoubleSlashes(
802
+ evaluateFolder(name, config, collection!, entryMap, field),
803
+ );
796
804
  }
797
805
 
798
806
  if (isAbsolutePath(publicFolder)) {
@@ -148,6 +148,8 @@ export interface CmsFieldList {
148
148
  default?: unknown;
149
149
 
150
150
  allow_add?: boolean;
151
+ allow_remove?: boolean;
152
+ allow_reorder?: boolean;
151
153
  collapsed?: boolean;
152
154
  summary?: string;
153
155
  minimize_collapsed?: boolean;
@@ -307,6 +309,12 @@ export interface ViewGroup {
307
309
  id: string;
308
310
  }
309
311
 
312
+ export interface SortableField {
313
+ field: string;
314
+ label?: string;
315
+ default_sort?: boolean | 'asc' | 'desc';
316
+ }
317
+
310
318
  export interface CmsCollection {
311
319
  name: string;
312
320
  label: string;
@@ -346,7 +354,7 @@ export interface CmsCollection {
346
354
  path?: string;
347
355
  media_folder?: string;
348
356
  public_folder?: string;
349
- sortable_fields?: string[];
357
+ sortable_fields?: (string | SortableField)[];
350
358
  view_filters?: ViewFilter[];
351
359
  view_groups?: ViewGroup[];
352
360
  i18n?: boolean | CmsI18nConfig;
@@ -354,7 +362,7 @@ export interface CmsCollection {
354
362
  /**
355
363
  * @deprecated Use sortable_fields instead
356
364
  */
357
- sortableFields?: string[];
365
+ sortableFields?: (string | SortableField)[];
358
366
  }
359
367
 
360
368
  export interface CmsBackend {
@@ -633,7 +641,7 @@ type CollectionObject = {
633
641
  slug?: string;
634
642
  label_singular?: string;
635
643
  label: string;
636
- sortable_fields: List<string>;
644
+ sortable_fields: List<StaticallyTypedRecord<SortableField>>;
637
645
  view_filters: List<StaticallyTypedRecord<ViewFilter>>;
638
646
  view_groups: List<StaticallyTypedRecord<ViewGroup>>;
639
647
  nested?: Nested;