decap-cms-widget-list 3.2.2 → 3.3.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/dist/esm/index.js CHANGED
@@ -1,30 +1,23 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = exports.DecapCmsWidgetList = void 0;
7
- var _decapCmsWidgetObject = _interopRequireDefault(require("decap-cms-widget-object"));
8
- var _ListControl = _interopRequireDefault(require("./ListControl"));
9
- var _schema = _interopRequireDefault(require("./schema"));
10
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
1
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
12
2
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
13
3
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
14
4
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
15
5
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
16
- const previewComponent = _decapCmsWidgetObject.default.previewComponent;
6
+ import DecapCmsWidgetObject from 'decap-cms-widget-object';
7
+ import controlComponent from './ListControl';
8
+ import schema from './schema';
9
+ const previewComponent = DecapCmsWidgetObject.previewComponent;
17
10
  function Widget(opts = {}) {
18
11
  return _objectSpread({
19
12
  name: 'list',
20
- controlComponent: _ListControl.default,
13
+ controlComponent,
21
14
  previewComponent,
22
- schema: _schema.default
15
+ schema
23
16
  }, opts);
24
17
  }
25
- const DecapCmsWidgetList = exports.DecapCmsWidgetList = {
18
+ export const DecapCmsWidgetList = {
26
19
  Widget,
27
- controlComponent: _ListControl.default,
20
+ controlComponent,
28
21
  previewComponent
29
22
  };
30
- var _default = exports.default = DecapCmsWidgetList;
23
+ export default DecapCmsWidgetList;
@@ -1,10 +1,4 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _default = exports.default = {
1
+ export default {
8
2
  properties: {
9
3
  allow_add: {
10
4
  type: 'boolean'
@@ -1,23 +1,13 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.TYPE_KEY = exports.TYPES_KEY = exports.DEFAULT_TYPE_KEY = void 0;
7
- exports.getErrorMessageForTypedFieldAndValue = getErrorMessageForTypedFieldAndValue;
8
- exports.getTypedFieldForValue = getTypedFieldForValue;
9
- exports.resolveFieldKeyType = resolveFieldKeyType;
10
- exports.resolveFunctionForTypedField = resolveFunctionForTypedField;
11
- const TYPES_KEY = exports.TYPES_KEY = 'types';
12
- const TYPE_KEY = exports.TYPE_KEY = 'typeKey';
13
- const DEFAULT_TYPE_KEY = exports.DEFAULT_TYPE_KEY = 'type';
14
- function getTypedFieldForValue(field, value) {
1
+ export const TYPES_KEY = 'types';
2
+ export const TYPE_KEY = 'typeKey';
3
+ export const DEFAULT_TYPE_KEY = 'type';
4
+ export function getTypedFieldForValue(field, value) {
15
5
  const typeKey = resolveFieldKeyType(field);
16
6
  const types = field.get(TYPES_KEY);
17
7
  const valueType = value.get(typeKey);
18
8
  return types.find(type => type.get('name') === valueType);
19
9
  }
20
- function resolveFunctionForTypedField(field) {
10
+ export function resolveFunctionForTypedField(field) {
21
11
  const typeKey = resolveFieldKeyType(field);
22
12
  const types = field.get(TYPES_KEY);
23
13
  return value => {
@@ -25,10 +15,10 @@ function resolveFunctionForTypedField(field) {
25
15
  return types.find(type => type.get('name') === valueType);
26
16
  };
27
17
  }
28
- function resolveFieldKeyType(field) {
18
+ export function resolveFieldKeyType(field) {
29
19
  return field.get(TYPE_KEY, DEFAULT_TYPE_KEY);
30
20
  }
31
- function getErrorMessageForTypedFieldAndValue(field, value) {
21
+ export function getErrorMessageForTypedFieldAndValue(field, value) {
32
22
  const keyType = resolveFieldKeyType(field);
33
23
  const type = value.get(keyType);
34
24
  let errorMessage;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "decap-cms-widget-list",
3
3
  "description": "Widget for editing lists in Decap CMS.",
4
- "version": "3.2.2",
4
+ "version": "3.3.0",
5
5
  "homepage": "https://www.decapcms.org/docs/widgets/#list",
6
6
  "repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-cms-widget-list",
7
7
  "bugs": "https://github.com/decaporg/decap-cms/issues",
@@ -37,5 +37,5 @@
37
37
  "react": "^18.2.0",
38
38
  "react-immutable-proptypes": "^2.1.0"
39
39
  },
40
- "gitHead": "64d91b8bb3d0a93dd36c53800cdac4ba2e435000"
40
+ "gitHead": "e2165450e5963b2a8d76d4cf331319bf91f05d45"
41
41
  }
@@ -171,7 +171,7 @@ function LabelComponent({ field, isActive, hasErrors, uniqueFieldId, isFieldOpti
171
171
  }
172
172
 
173
173
  export default class ListControl extends React.Component {
174
- validations = [];
174
+ childRefs = {};
175
175
 
176
176
  static propTypes = {
177
177
  metadata: ImmutablePropTypes.map,
@@ -365,16 +365,18 @@ export default class ListControl extends React.Component {
365
365
  processControlRef = ref => {
366
366
  if (!ref) return;
367
367
  const {
368
- validate,
369
368
  props: { validationKey: key },
370
369
  } = ref;
371
- this.validations.push({ key, validate });
370
+ this.childRefs[key] = ref;
371
+ this.props.controlRef?.(this);
372
372
  };
373
373
 
374
374
  validate = () => {
375
- if (this.getValueType()) {
376
- this.validations.forEach(item => {
377
- item.validate();
375
+ // First validate child widgets if this is a complex list
376
+ const hasChildWidgets = this.getValueType() && Object.keys(this.childRefs).length > 0;
377
+ if (hasChildWidgets) {
378
+ Object.values(this.childRefs).forEach(widget => {
379
+ widget?.validate?.();
378
380
  });
379
381
  } else {
380
382
  this.props.validate();
@@ -431,7 +433,17 @@ export default class ListControl extends React.Component {
431
433
  handleRemove = (index, event) => {
432
434
  event.preventDefault();
433
435
  const { itemsCollapsed } = this.state;
434
- const { value, metadata, onChange, field, clearFieldErrors } = this.props;
436
+ const {
437
+ value,
438
+ metadata,
439
+ onChange,
440
+ field,
441
+ clearFieldErrors,
442
+ onValidateObject,
443
+ forID,
444
+ fieldsErrors,
445
+ } = this.props;
446
+
435
447
  const collectionName = field.get('name');
436
448
  const isSingleField = this.getValueType() === valueTypes.SINGLE;
437
449
 
@@ -441,17 +453,41 @@ export default class ListControl extends React.Component {
441
453
  ? { [collectionName]: metadata.removeIn(metadataRemovePath) }
442
454
  : metadata;
443
455
 
456
+ // Get the key of the item being removed
457
+ const removedKey = this.state.keys[index];
458
+
459
+ // Update state while preserving keys for remaining items
460
+ const newKeys = [...this.state.keys];
461
+ newKeys.splice(index, 1);
444
462
  itemsCollapsed.splice(index, 1);
445
- // clear validations
446
- this.validations = [];
447
463
 
448
464
  this.setState({
449
465
  itemsCollapsed: [...itemsCollapsed],
450
- keys: Array.from({ length: value.size - 1 }, () => uuid()),
466
+ keys: newKeys,
451
467
  });
452
468
 
453
- onChange(value.remove(index), parsedMetadata);
454
- clearFieldErrors();
469
+ // Clear the ref for the removed item
470
+ delete this.childRefs[removedKey];
471
+
472
+ const newValue = value.delete(index);
473
+
474
+ // Clear errors for the removed item and its children
475
+ if (fieldsErrors) {
476
+ Object.entries(fieldsErrors.toJS()).forEach(([fieldId, errors]) => {
477
+ if (errors.some(err => err.parentIds?.includes(removedKey))) {
478
+ clearFieldErrors(fieldId);
479
+ }
480
+ });
481
+ }
482
+
483
+ // If list is empty, mark it as valid
484
+ if (newValue.size === 0) {
485
+ clearFieldErrors(forID);
486
+ onValidateObject(forID, []);
487
+ }
488
+
489
+ // Update the value last to ensure all error states are cleared
490
+ onChange(newValue, parsedMetadata);
455
491
  };
456
492
 
457
493
  handleItemCollapseToggle = (index, event) => {
@@ -527,7 +563,7 @@ export default class ListControl extends React.Component {
527
563
  }
528
564
 
529
565
  onSortEnd = ({ oldIndex, newIndex }) => {
530
- const { value, clearFieldErrors } = this.props;
566
+ const { value } = this.props;
531
567
  const { itemsCollapsed, keys } = this.state;
532
568
 
533
569
  // Update value
@@ -541,18 +577,13 @@ export default class ListControl extends React.Component {
541
577
  const updatedItemsCollapsed = [...itemsCollapsed];
542
578
  updatedItemsCollapsed.splice(newIndex, 0, collapsed);
543
579
 
544
- // Reset item to ensure updated state
545
- const updatedKeys = keys.map((key, keyIndex) => {
546
- if (keyIndex === oldIndex || keyIndex === newIndex) {
547
- return uuid();
548
- }
549
- return key;
550
- });
551
- this.setState({ itemsCollapsed: updatedItemsCollapsed, keys: updatedKeys });
580
+ // Move keys to maintain relationships
581
+ const movedKey = keys[oldIndex];
582
+ const updatedKeys = [...keys];
583
+ updatedKeys.splice(oldIndex, 1);
584
+ updatedKeys.splice(newIndex, 0, movedKey);
552
585
 
553
- //clear error fields and remove old validations
554
- clearFieldErrors();
555
- this.validations = this.validations.filter(item => updatedKeys.includes(item.key));
586
+ this.setState({ itemsCollapsed: updatedItemsCollapsed, keys: updatedKeys });
556
587
  };
557
588
 
558
589
  hasError = index => {
@@ -564,6 +595,34 @@ export default class ListControl extends React.Component {
564
595
  }
565
596
  };
566
597
 
598
+ focus(path) {
599
+ const [index, ...remainingPath] = path.split('.');
600
+
601
+ if (this.state.listCollapsed || this.state.itemsCollapsed[index]) {
602
+ const newItemsCollapsed = [...this.state.itemsCollapsed];
603
+ newItemsCollapsed[index] = false;
604
+ this.setState(
605
+ {
606
+ listCollapsed: false,
607
+ itemsCollapsed: newItemsCollapsed,
608
+ },
609
+ () => {
610
+ const key = this.state.keys[index];
611
+ const control = this.childRefs[key];
612
+ if (control?.focus) {
613
+ control.focus(remainingPath.join('.'));
614
+ }
615
+ },
616
+ );
617
+ } else {
618
+ const key = this.state.keys[index];
619
+ const control = this.childRefs[key];
620
+ if (control?.focus) {
621
+ control.focus(remainingPath.join('.'));
622
+ }
623
+ }
624
+ }
625
+
567
626
  // eslint-disable-next-line react/display-name
568
627
  renderItem = (item, index) => {
569
628
  const {
@@ -724,13 +724,12 @@ exports[`ListControl should remove from list when remove button is clicked 2`] =
724
724
  <div>
725
725
  <div
726
726
  class="emotion-15 emotion-16"
727
- style="transition: transform 0ms linear;"
728
727
  >
729
728
  <mock-list-item-top-bar
730
729
  classname="css-1e1b0lr-StyledListItemTopBar e11zrb3c1"
731
730
  collapsed="true"
732
- data-testid="styled-list-item-top-bar-2"
733
- id="2"
731
+ data-testid="styled-list-item-top-bar-1"
732
+ id="1"
734
733
  >
735
734
  <button>
736
735
  Remove
@@ -744,12 +743,12 @@ exports[`ListControl should remove from list when remove button is clicked 2`] =
744
743
  <mock-object-control
745
744
  classnamewrapper="classNameWrapper css-1pznrxi"
746
745
  collapsed="true"
747
- data-testid="object-control-2"
746
+ data-testid="object-control-1"
748
747
  field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"minimize_collapsed\\": true, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
749
748
  fieldserrors="Map {}"
750
749
  forlist="true"
751
- parentids="forID,2"
752
- validationkey="2"
750
+ parentids="forID,1"
751
+ validationkey="1"
753
752
  value="Map { \\"string\\": \\"item 2\\" }"
754
753
  />
755
754
  </div>