cozy-ui-plus 6.0.0 → 6.1.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 (25) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/Contacts/AddModal/ContactForm/FieldInput.js +49 -20
  3. package/dist/Contacts/AddModal/ContactForm/FieldInputArray.js +5 -16
  4. package/dist/Contacts/AddModal/ContactForm/FieldInputLayout.js +2 -2
  5. package/dist/Contacts/AddModal/ContactForm/FieldInputWrapper.js +3 -1
  6. package/dist/Contacts/AddModal/ContactForm/RemoveButton.js +34 -0
  7. package/dist/Contacts/AddModal/ContactForm/fieldsConfig.js +3 -1
  8. package/dist/Contacts/AddModal/ContactForm/helpers.js +14 -3
  9. package/dist/Contacts/AddModal/ContactForm/index.js +14 -3
  10. package/dist/Field/index.js +1 -5
  11. package/dist/src/Contacts/AddModal/ContactForm/FieldInput.d.ts +3 -1
  12. package/dist/src/Contacts/AddModal/ContactForm/FieldInputWrapper.d.ts +2 -1
  13. package/dist/src/Contacts/AddModal/ContactForm/RemoveButton.d.ts +4 -0
  14. package/dist/stylesheet.css +0 -25
  15. package/package.json +2 -2
  16. package/src/Contacts/AddModal/ContactForm/FieldInput.jsx +29 -9
  17. package/src/Contacts/AddModal/ContactForm/FieldInputArray.jsx +3 -14
  18. package/src/Contacts/AddModal/ContactForm/FieldInputLayout.jsx +5 -3
  19. package/src/Contacts/AddModal/ContactForm/FieldInputWrapper.jsx +3 -1
  20. package/src/Contacts/AddModal/ContactForm/RemoveButton.jsx +18 -0
  21. package/src/Contacts/AddModal/ContactForm/fieldsConfig.jsx +3 -1
  22. package/src/Contacts/AddModal/ContactForm/helpers.js +13 -2
  23. package/src/Contacts/AddModal/ContactForm/helpers.spec.js +62 -0
  24. package/src/Contacts/AddModal/ContactForm/index.jsx +27 -14
  25. package/src/Contacts/AddModal/ContactForm/styles.styl +0 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [6.1.1](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@6.1.0...cozy-ui-plus@6.1.1) (2026-04-02)
7
+
8
+ ### Bug Fixes
9
+
10
+ - Now getRelatedContactRelationships handle null arg ([d3677ba](https://github.com/cozy/cozy-libs/commit/d3677ba6cfd496db48ebe00fac401746fb6680c9))
11
+
12
+ # [6.1.0](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@6.0.0...cozy-ui-plus@6.1.0) (2026-04-02)
13
+
14
+ ### Bug Fixes
15
+
16
+ - **cozy-ui-plus:** Remove icon wrapper on contact modal if no icon ([8b0ffa9](https://github.com/cozy/cozy-libs/commit/8b0ffa9a55dcc964c558625515e191316f03072a))
17
+
18
+ ### Features
19
+
20
+ - **cozy-ui-plus:** Add `isHalfWidth` prop on fields ([a69e7b7](https://github.com/cozy/cozy-libs/commit/a69e7b7af6e0e7021d8ab5bb68894829b9a75c97))
21
+ - **cozy-ui-plus:** Add prop on Contacts modal to remove field ([d9637ff](https://github.com/cozy/cozy-libs/commit/d9637ff248e3ddc39fe8664af645e96e4e551f2f))
22
+ - **cozy-ui-plus:** Change size of contacts modal fields ([27fdb9a](https://github.com/cozy/cozy-libs/commit/27fdb9a344a0803ca1442b93c9213aedb93ad8a2))
23
+ - **cozy-ui-plus:** Move field remove button next to the input ([0a7c409](https://github.com/cozy/cozy-libs/commit/0a7c409a84dbb5f235586069feaa45454df0fbee))
24
+
6
25
  # [6.0.0](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@5.3.1...cozy-ui-plus@6.0.0) (2026-03-24)
7
26
 
8
27
  ### Features
@@ -21,6 +21,8 @@ var _react = _interopRequireWildcard(require("react"));
21
21
 
22
22
  var _reactFinalForm = require("react-final-form");
23
23
 
24
+ var _Breakpoints = require("cozy-ui/transpiled/react/providers/Breakpoints");
25
+
24
26
  var _twakeI18n = require("twake-i18n");
25
27
 
26
28
  var _FieldInputWrapper = _interopRequireDefault(require("./FieldInputWrapper"));
@@ -29,35 +31,55 @@ var _HasValueCondition = _interopRequireDefault(require("./HasValueCondition"));
29
31
 
30
32
  var _RelatedContactList = require("./RelatedContactList");
31
33
 
34
+ var _RemoveButton = _interopRequireDefault(require("./RemoveButton"));
35
+
32
36
  var _locales = require("./locales");
33
37
 
34
38
  var _ContactAddressDialog = _interopRequireDefault(require("../ContactAddressDialog"));
35
39
 
36
40
  var _types = require("../types");
37
41
 
38
- var _excluded = ["subFields"];
42
+ var _excluded = ["showRemove", "onRemove"],
43
+ _excluded2 = ["subFields"];
39
44
 
40
45
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
41
46
 
42
47
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
43
48
 
44
- var styles = {
45
- "contact-form-field__wrapper": "styles__contact-form-field__wrapper___13Usq"
49
+ var FieldAndRemove = function FieldAndRemove(_ref) {
50
+ var showRemove = _ref.showRemove,
51
+ onRemove = _ref.onRemove,
52
+ props = (0, _objectWithoutProperties2.default)(_ref, _excluded);
53
+
54
+ var _useBreakpoints = (0, _Breakpoints.useBreakpoints)(),
55
+ isMobile = _useBreakpoints.isMobile;
56
+
57
+ if (isMobile) {
58
+ return /*#__PURE__*/_react.default.createElement("div", {
59
+ className: "u-flex u-flex-items-center u-w-100"
60
+ }, /*#__PURE__*/_react.default.createElement(_reactFinalForm.Field, props), showRemove && /*#__PURE__*/_react.default.createElement(_RemoveButton.default, {
61
+ onRemove: onRemove
62
+ }));
63
+ }
64
+
65
+ return /*#__PURE__*/_react.default.createElement(_reactFinalForm.Field, props);
46
66
  };
47
67
 
48
- var FieldInput = function FieldInput(_ref) {
49
- var name = _ref.name,
50
- labelProps = _ref.labelProps,
51
- className = _ref.className,
52
- _ref$attributes = _ref.attributes,
53
- subFields = _ref$attributes.subFields,
54
- restAttributes = (0, _objectWithoutProperties2.default)(_ref$attributes, _excluded),
55
- contacts = _ref.contacts,
56
- contact = _ref.contact,
57
- error = _ref.error,
58
- helperText = _ref.helperText,
59
- label = _ref.label,
60
- isInvisible = _ref.isInvisible;
68
+ var FieldInput = function FieldInput(_ref2) {
69
+ var name = _ref2.name,
70
+ labelProps = _ref2.labelProps,
71
+ className = _ref2.className,
72
+ _ref2$attributes = _ref2.attributes,
73
+ subFields = _ref2$attributes.subFields,
74
+ restAttributes = (0, _objectWithoutProperties2.default)(_ref2$attributes, _excluded2),
75
+ contacts = _ref2.contacts,
76
+ contact = _ref2.contact,
77
+ error = _ref2.error,
78
+ onRemove = _ref2.onRemove,
79
+ showRemove = _ref2.showRemove,
80
+ helperText = _ref2.helperText,
81
+ label = _ref2.label,
82
+ isInvisible = _ref2.isInvisible;
61
83
 
62
84
  var _useState = (0, _react.useState)((0, _uniqueId.default)('field_')),
63
85
  _useState2 = (0, _slicedToArray2.default)(_useState, 1),
@@ -84,6 +106,9 @@ var FieldInput = function FieldInput(_ref) {
84
106
  var _useI18n = (0, _twakeI18n.useI18n)(),
85
107
  t = _useI18n.t;
86
108
 
109
+ var _useBreakpoints2 = (0, _Breakpoints.useBreakpoints)(),
110
+ isMobile = _useBreakpoints2.isMobile;
111
+
87
112
  var handleClick = function handleClick() {
88
113
  if (name.includes('address')) {
89
114
  setIsAddressDialogOpen(true);
@@ -99,11 +124,11 @@ var FieldInput = function FieldInput(_ref) {
99
124
  };
100
125
 
101
126
  return /*#__PURE__*/_react.default.createElement("div", {
102
- className: (0, _classnames.default)(className, styles['contact-form-field__wrapper'], 'u-flex-column-s', {
127
+ className: (0, _classnames.default)(className, 'u-flex-column-s u-flex-items-center u-w-100', {
103
128
  'u-flex': !isInvisible,
104
129
  'u-dn': isInvisible
105
130
  })
106
- }, /*#__PURE__*/_react.default.createElement(_reactFinalForm.Field, {
131
+ }, /*#__PURE__*/_react.default.createElement(FieldAndRemove, {
107
132
  error: error,
108
133
  helperText: helperText,
109
134
  label: label,
@@ -112,6 +137,8 @@ var FieldInput = function FieldInput(_ref) {
112
137
  name: name,
113
138
  contact: contact,
114
139
  component: _FieldInputWrapper.default,
140
+ showRemove: showRemove,
141
+ onRemove: onRemove,
115
142
  onFocus: onFocus,
116
143
  onClick: handleClick
117
144
  }), isAddressDialogOpen && /*#__PURE__*/_react.default.createElement(_ContactAddressDialog.default, {
@@ -130,7 +157,7 @@ var FieldInput = function FieldInput(_ref) {
130
157
  name: name,
131
158
  otherCondition: hasBeenFocused
132
159
  }, /*#__PURE__*/_react.default.createElement("div", {
133
- className: "u-mt-half-s u-ml-half u-ml-0-s u-flex-shrink-0 u-w-auto u-miw-4"
160
+ className: "u-mt-half-s u-ml-half u-ml-0-s u-flex-shrink-0 u-w-100-s u-w-auto u-miw-4"
134
161
  }, /*#__PURE__*/_react.default.createElement(_reactFinalForm.Field, {
135
162
  attributes: labelProps,
136
163
  name: "".concat(name, "Label"),
@@ -138,7 +165,9 @@ var FieldInput = function FieldInput(_ref) {
138
165
  contact: contact,
139
166
  component: _FieldInputWrapper.default,
140
167
  onFocus: onFocus
141
- }))));
168
+ }))), showRemove && !isMobile && /*#__PURE__*/_react.default.createElement(_RemoveButton.default, {
169
+ onRemove: onRemove
170
+ }));
142
171
  };
143
172
 
144
173
  FieldInput.propTypes = {
@@ -19,14 +19,8 @@ var _Buttons = _interopRequireDefault(require("cozy-ui/transpiled/react/Buttons"
19
19
 
20
20
  var _Icon = _interopRequireDefault(require("cozy-ui/transpiled/react/Icon"));
21
21
 
22
- var _IconButton = _interopRequireDefault(require("cozy-ui/transpiled/react/IconButton"));
23
-
24
- var _CrossCircle = _interopRequireDefault(require("cozy-ui/transpiled/react/Icons/CrossCircle"));
25
-
26
22
  var _Plus = _interopRequireDefault(require("cozy-ui/transpiled/react/Icons/Plus"));
27
23
 
28
- var _ListItemIcon = _interopRequireDefault(require("cozy-ui/transpiled/react/ListItemIcon"));
29
-
30
24
  var _twakeI18n = require("twake-i18n");
31
25
 
32
26
  var _FieldInput = _interopRequireDefault(require("./FieldInput"));
@@ -76,21 +70,16 @@ var FieldInputArray = function FieldInputArray(_ref) {
76
70
  contacts: contacts,
77
71
  contact: contact,
78
72
  error: isError,
73
+ index: index,
74
+ showRemove: showRemove,
79
75
  helperText: isError ? errors[inputName] : null,
80
76
  name: inputName,
81
77
  label: t("Contacts.AddModal.ContactForm.fields.".concat(name)),
82
- labelProps: label
83
- }), showRemove && /*#__PURE__*/_react.default.createElement(_ListItemIcon.default, {
84
- className: "u-ml-half"
85
- }, /*#__PURE__*/_react.default.createElement(_IconButton.default, {
86
- "aria-label": "delete",
87
- size: "medium",
88
- onClick: function onClick() {
78
+ labelProps: label,
79
+ onRemove: function onRemove() {
89
80
  return (0, _helpers.removeField)(fields, index);
90
81
  }
91
- }, /*#__PURE__*/_react.default.createElement(_Icon.default, {
92
- icon: _CrossCircle.default
93
- }))));
82
+ }));
94
83
  }), /*#__PURE__*/_react.default.createElement(_Buttons.default, {
95
84
  variant: "text",
96
85
  startIcon: /*#__PURE__*/_react.default.createElement(_Icon.default, {
@@ -57,9 +57,9 @@ var FieldInputLayout = function FieldInputLayout(_ref) {
57
57
  'u-flex': !isSecondary || showSecondaryFields,
58
58
  'u-dn': isSecondary && !showSecondaryFields
59
59
  })
60
- }, /*#__PURE__*/_react.default.createElement("div", {
60
+ }, icon && /*#__PURE__*/_react.default.createElement("div", {
61
61
  className: "u-w-2-half"
62
- }, icon && /*#__PURE__*/_react.default.createElement(_Icon.default, {
62
+ }, /*#__PURE__*/_react.default.createElement(_Icon.default, {
63
63
  icon: icon,
64
64
  color: "var(--iconTextColor)"
65
65
  })), /*#__PURE__*/_react.default.createElement("div", {
@@ -21,7 +21,7 @@ var _TextFieldSelect = _interopRequireDefault(require("./TextFieldSelect"));
21
21
 
22
22
  var _types = require("../types");
23
23
 
24
- var _excluded = ["component", "layout", "icon", "isSecondary", "validate"],
24
+ var _excluded = ["component", "layout", "isHalfWidth", "icon", "isSecondary", "validate"],
25
25
  _excluded2 = ["input", "attributes", "variant", "fullWidth"];
26
26
 
27
27
  // component used to flatten props to ensure compatibility
@@ -31,6 +31,7 @@ var FieldInputWrapper = function FieldInputWrapper(_ref) {
31
31
  _ref$attributes = _ref.attributes,
32
32
  component = _ref$attributes.component,
33
33
  layout = _ref$attributes.layout,
34
+ isHalfWidth = _ref$attributes.isHalfWidth,
34
35
  icon = _ref$attributes.icon,
35
36
  isSecondary = _ref$attributes.isSecondary,
36
37
  validate = _ref$attributes.validate,
@@ -42,6 +43,7 @@ var FieldInputWrapper = function FieldInputWrapper(_ref) {
42
43
  return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({}, input, restAttributes, props, {
43
44
  variant: variant,
44
45
  fullWidth: fullWidth,
46
+ size: "small",
45
47
  minRows: "2"
46
48
  }));
47
49
  };
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = void 0;
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _Icon = _interopRequireDefault(require("cozy-ui/transpiled/react/Icon"));
13
+
14
+ var _IconButton = _interopRequireDefault(require("cozy-ui/transpiled/react/IconButton"));
15
+
16
+ var _CrossCircle = _interopRequireDefault(require("cozy-ui/transpiled/react/Icons/CrossCircle"));
17
+
18
+ var _ListItemIcon = _interopRequireDefault(require("cozy-ui/transpiled/react/ListItemIcon"));
19
+
20
+ var RemoveButton = function RemoveButton(_ref) {
21
+ var onRemove = _ref.onRemove;
22
+ return /*#__PURE__*/_react.default.createElement(_ListItemIcon.default, {
23
+ className: "u-ml-half"
24
+ }, /*#__PURE__*/_react.default.createElement(_IconButton.default, {
25
+ "aria-label": "delete",
26
+ size: "medium",
27
+ onClick: onRemove
28
+ }, /*#__PURE__*/_react.default.createElement(_Icon.default, {
29
+ icon: _CrossCircle.default
30
+ })));
31
+ };
32
+
33
+ var _default = RemoveButton;
34
+ exports.default = _default;
@@ -56,6 +56,7 @@ var fields = [{
56
56
  icon: null,
57
57
  type: 'text',
58
58
  layout: 'accordion',
59
+ isHalfWidth: true,
59
60
  subFields: [{
60
61
  name: 'additionalName',
61
62
  icon: null,
@@ -68,7 +69,8 @@ var fields = [{
68
69
  }, {
69
70
  name: 'familyName',
70
71
  icon: null,
71
- type: 'text'
72
+ type: 'text',
73
+ isHalfWidth: true
72
74
  }, {
73
75
  name: 'company',
74
76
  icon: _Company.default,
@@ -256,7 +256,8 @@ var createAddress = function createAddress(_ref2) {
256
256
  exports.createAddress = createAddress;
257
257
 
258
258
  var getRelatedContactRelationships = function getRelatedContactRelationships(relatedContact) {
259
- // Tips filter Boolean to remove undefined value from array when relatedContact is empty (see contactToFormValues)
259
+ if (!relatedContact) return {}; // Tips filter Boolean to remove undefined value from array when relatedContact is empty (see contactToFormValues)
260
+
260
261
  var data = relatedContact.filter(Boolean).reduce(function (acc, curr) {
261
262
  var relationType = curr.relatedContactLabel ? JSON.parse(curr.relatedContactLabel).type : 'related';
262
263
  var existingIndex = acc.findIndex(function (item) {
@@ -541,7 +542,17 @@ var makeFields = function makeFields(customFields, defaultFields) {
541
542
  customFields.forEach(function (customField) {
542
543
  var defaultField = fields.find(function (field) {
543
544
  return field.name === customField.name;
544
- });
545
+ }); // If isRemoved is true, remove the field from the result
546
+
547
+ if (customField.isRemoved) {
548
+ if (defaultField) {
549
+ fields = fields.filter(function (field) {
550
+ return field.name !== customField.name;
551
+ });
552
+ }
553
+
554
+ return;
555
+ }
545
556
 
546
557
  var _field = defaultField || customField;
547
558
 
@@ -549,7 +560,7 @@ var makeFields = function makeFields(customFields, defaultFields) {
549
560
  Object.assign(_field, customField);
550
561
  }
551
562
 
552
- if (_field.position) {
563
+ if (_field.position !== undefined) {
553
564
  if (defaultField) {
554
565
  var fieldIndex = fields.indexOf(defaultField);
555
566
  fields.splice(fieldIndex, 1);
@@ -21,8 +21,12 @@ var _reactFinalForm = require("react-final-form");
21
21
 
22
22
  var _HasMany = require("cozy-client/dist/associations/HasMany");
23
23
 
24
+ var _Box = _interopRequireDefault(require("cozy-ui/transpiled/react/Box"));
25
+
24
26
  var _Buttons = _interopRequireDefault(require("cozy-ui/transpiled/react/Buttons"));
25
27
 
28
+ var _Breakpoints = require("cozy-ui/transpiled/react/providers/Breakpoints");
29
+
26
30
  var _twakeI18n = require("twake-i18n");
27
31
 
28
32
  var _FieldInputLayout = _interopRequireDefault(require("./FieldInputLayout"));
@@ -91,6 +95,9 @@ var ContactForm = function ContactForm(_ref) {
91
95
  makeCustomFieldsFormValues = customFieldsProps.makeCustomFieldsFormValues,
92
96
  makeCustomContactValues = customFieldsProps.makeCustomContactValues;
93
97
 
98
+ var _useBreakpoints = (0, _Breakpoints.useBreakpoints)(),
99
+ isMobile = _useBreakpoints.isMobile;
100
+
94
101
  var _fields = (0, _helpers.makeFields)(fields, _fieldsConfig.fields);
95
102
 
96
103
  var hasSecondaryFields = _fields.some(function (el) {
@@ -131,9 +138,13 @@ var ContactForm = function ContactForm(_ref) {
131
138
  role: "form",
132
139
  onSubmit: handleSubmit,
133
140
  className: "u-flex u-flex-column"
141
+ }, /*#__PURE__*/_react.default.createElement("div", {
142
+ className: "u-flex u-flex-wrap u-flex-justify-between"
134
143
  }, _fields.map(function (attributes, index) {
135
- return /*#__PURE__*/_react.default.createElement(_FieldInputLayout.default, {
144
+ return /*#__PURE__*/_react.default.createElement(_Box.default, {
136
145
  key: index,
146
+ width: attributes.isHalfWidth && !isMobile ? 'calc(50% - 0.5rem)' : '100%'
147
+ }, /*#__PURE__*/_react.default.createElement(_FieldInputLayout.default, {
137
148
  attributes: attributes,
138
149
  contacts: contacts,
139
150
  contact: contact,
@@ -143,8 +154,8 @@ var ContactForm = function ContactForm(_ref) {
143
154
  submitFailed: submitFailed,
144
155
  errors: errors
145
156
  }
146
- });
147
- }), hasSecondaryFields && !showSecondaryFields && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Buttons.default, {
157
+ }));
158
+ })), hasSecondaryFields && !showSecondaryFields && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Buttons.default, {
148
159
  className: "u-db u-ml-2 u-mt-1",
149
160
  variant: "text",
150
161
  label: t('Contacts.AddModal.ContactForm.other-fields'),
@@ -46,11 +46,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
46
46
 
47
47
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
48
48
 
49
- var labelStyles = {
50
- "c-label": "styles__c-label___24c92",
51
- "is-error": "styles__is-error___1gUPb",
52
- "c-label--block": "styles__c-label--block___1Zska"
53
- };
49
+ var labelStyles = {};
54
50
  var styles = {
55
51
  "o-field": "styles__o-field___2B6P3",
56
52
  "o-field--inline": "styles__o-field--inline___3D2bq",
@@ -1,5 +1,5 @@
1
1
  export default FieldInput;
2
- declare function FieldInput({ name, labelProps, className, attributes: { subFields, ...restAttributes }, contacts, contact, error, helperText, label, isInvisible }: {
2
+ declare function FieldInput({ name, labelProps, className, attributes: { subFields, ...restAttributes }, contacts, contact, error, onRemove, showRemove, helperText, label, isInvisible }: {
3
3
  name: any;
4
4
  labelProps: any;
5
5
  className: any;
@@ -10,6 +10,8 @@ declare function FieldInput({ name, labelProps, className, attributes: { subFiel
10
10
  contacts: any;
11
11
  contact: any;
12
12
  error: any;
13
+ onRemove: any;
14
+ showRemove: any;
13
15
  helperText: any;
14
16
  label: any;
15
17
  isInvisible: any;
@@ -1,11 +1,12 @@
1
1
  export default FieldInputWrapper;
2
- declare function FieldInputWrapper({ input, attributes: { component, layout, icon, isSecondary, validate, ...restAttributes }, variant, fullWidth, ...props }: {
2
+ declare function FieldInputWrapper({ input, attributes: { component, layout, isHalfWidth, icon, isSecondary, validate, ...restAttributes }, variant, fullWidth, ...props }: {
3
3
  [x: string]: any;
4
4
  input: any;
5
5
  attributes: {
6
6
  [x: string]: any;
7
7
  component: any;
8
8
  layout: any;
9
+ isHalfWidth: any;
9
10
  icon: any;
10
11
  isSecondary: any;
11
12
  validate: any;
@@ -0,0 +1,4 @@
1
+ export default RemoveButton;
2
+ declare function RemoveButton({ onRemove }: {
3
+ onRemove: any;
4
+ }): JSX.Element;
@@ -220,11 +220,6 @@
220
220
  }
221
221
  @media (max-width: 48rem) {
222
222
  }
223
- /* imported from styles.styl */
224
-
225
- .styles__contact-form-field__wrapper___13Usq {
226
- width: calc(100% - 3rem);
227
- }
228
223
  /* imported from styles.styl */
229
224
 
230
225
  .styles__container___w8t8I {
@@ -343,26 +338,6 @@ it overrides nothing so there's no need for
343
338
  }
344
339
  /* imported from styles.styl */
345
340
 
346
- .styles__c-label___24c92 {
347
- text-transform: uppercase;
348
- color: var(--secondaryTextColor);
349
- font-size: 0.813rem;
350
- font-weight: bold;
351
- line-height: 1rem;
352
- }
353
- .styles__c-label___24c92.styles__is-error___1gUPb {
354
- color: var(--errorColor);
355
- }
356
- .styles__c-label--block___1Zska {
357
- display: block;
358
- padding: 0.5rem 0;
359
- }
360
- @media (max-width: 48rem) {
361
- }
362
- @media (max-width: 48rem) {
363
- }
364
- /* imported from styles.styl */
365
-
366
341
  .styles__o-field___2B6P3 {
367
342
  position: relative;
368
343
  display: flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui-plus",
3
- "version": "6.0.0",
3
+ "version": "6.1.1",
4
4
  "description": "Cozy-ui-plus is an extension of cozy-ui and provides components based on cozy-client",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -73,5 +73,5 @@
73
73
  "react-dom": "^16 || ^17 || ^18",
74
74
  "twake-i18n": ">=0.3.0"
75
75
  },
76
- "gitHead": "4c152ef7f2acafb450df245f5ce58638cbdb6be3"
76
+ "gitHead": "aee7d46723561ad42f2cdc4fe96036ffe92adcdd"
77
77
  }
@@ -4,16 +4,32 @@ import PropTypes from 'prop-types'
4
4
  import React, { useState } from 'react'
5
5
  import { Field } from 'react-final-form'
6
6
 
7
+ import { useBreakpoints } from 'cozy-ui/transpiled/react/providers/Breakpoints'
7
8
  import { useI18n, useExtendI18n } from 'twake-i18n'
8
9
 
9
10
  import FieldInputWrapper from './FieldInputWrapper'
10
11
  import HasValueCondition from './HasValueCondition'
11
12
  import { RelatedContactList } from './RelatedContactList'
13
+ import RemoveButton from './RemoveButton'
12
14
  import { locales } from './locales'
13
- import styles from './styles.styl'
14
15
  import ContactAddressDialog from '../ContactAddressDialog'
15
16
  import { fieldInputAttributesTypes, labelPropTypes } from '../types'
16
17
 
18
+ const FieldAndRemove = ({ showRemove, onRemove, ...props }) => {
19
+ const { isMobile } = useBreakpoints()
20
+
21
+ if (isMobile) {
22
+ return (
23
+ <div className="u-flex u-flex-items-center u-w-100">
24
+ <Field {...props} />
25
+ {showRemove && <RemoveButton onRemove={onRemove} />}
26
+ </div>
27
+ )
28
+ }
29
+
30
+ return <Field {...props} />
31
+ }
32
+
17
33
  const FieldInput = ({
18
34
  name,
19
35
  labelProps,
@@ -22,6 +38,8 @@ const FieldInput = ({
22
38
  contacts,
23
39
  contact,
24
40
  error,
41
+ onRemove,
42
+ showRemove,
25
43
  helperText,
26
44
  label,
27
45
  isInvisible
@@ -33,6 +51,7 @@ const FieldInput = ({
33
51
  useState(false)
34
52
  useExtendI18n(locales)
35
53
  const { t } = useI18n()
54
+ const { isMobile } = useBreakpoints()
36
55
 
37
56
  const handleClick = () => {
38
57
  if (name.includes('address')) {
@@ -50,14 +69,12 @@ const FieldInput = ({
50
69
 
51
70
  return (
52
71
  <div
53
- className={cx(
54
- className,
55
- styles['contact-form-field__wrapper'],
56
- 'u-flex-column-s',
57
- { 'u-flex': !isInvisible, 'u-dn': isInvisible }
58
- )}
72
+ className={cx(className, 'u-flex-column-s u-flex-items-center u-w-100', {
73
+ 'u-flex': !isInvisible,
74
+ 'u-dn': isInvisible
75
+ })}
59
76
  >
60
- <Field
77
+ <FieldAndRemove
61
78
  error={error}
62
79
  helperText={helperText}
63
80
  label={label}
@@ -66,6 +83,8 @@ const FieldInput = ({
66
83
  name={name}
67
84
  contact={contact}
68
85
  component={FieldInputWrapper}
86
+ showRemove={showRemove}
87
+ onRemove={onRemove}
69
88
  onFocus={onFocus}
70
89
  onClick={handleClick}
71
90
  />
@@ -85,7 +104,7 @@ const FieldInput = ({
85
104
  )}
86
105
  {labelProps && (
87
106
  <HasValueCondition name={name} otherCondition={hasBeenFocused}>
88
- <div className="u-mt-half-s u-ml-half u-ml-0-s u-flex-shrink-0 u-w-auto u-miw-4">
107
+ <div className="u-mt-half-s u-ml-half u-ml-0-s u-flex-shrink-0 u-w-100-s u-w-auto u-miw-4">
89
108
  <Field
90
109
  attributes={labelProps}
91
110
  name={`${name}Label`}
@@ -97,6 +116,7 @@ const FieldInput = ({
97
116
  </div>
98
117
  </HasValueCondition>
99
118
  )}
119
+ {showRemove && !isMobile && <RemoveButton onRemove={onRemove} />}
100
120
  </div>
101
121
  )
102
122
  }
@@ -4,10 +4,7 @@ import { FieldArray } from 'react-final-form-arrays'
4
4
 
5
5
  import Button from 'cozy-ui/transpiled/react/Buttons'
6
6
  import Icon from 'cozy-ui/transpiled/react/Icon'
7
- import IconButton from 'cozy-ui/transpiled/react/IconButton'
8
- import CrossCircleIcon from 'cozy-ui/transpiled/react/Icons/CrossCircle'
9
7
  import PlusIcon from 'cozy-ui/transpiled/react/Icons/Plus'
10
- import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
11
8
  import { useI18n, useExtendI18n } from 'twake-i18n'
12
9
 
13
10
  import FieldInput from './FieldInput'
@@ -53,22 +50,14 @@ const FieldInputArray = ({
53
50
  contacts={contacts}
54
51
  contact={contact}
55
52
  error={isError}
53
+ index={index}
54
+ showRemove={showRemove}
56
55
  helperText={isError ? errors[inputName] : null}
57
56
  name={inputName}
58
57
  label={t(`Contacts.AddModal.ContactForm.fields.${name}`)}
59
58
  labelProps={label}
59
+ onRemove={() => removeField(fields, index)}
60
60
  />
61
- {showRemove && (
62
- <ListItemIcon className="u-ml-half">
63
- <IconButton
64
- aria-label="delete"
65
- size="medium"
66
- onClick={() => removeField(fields, index)}
67
- >
68
- <Icon icon={CrossCircleIcon} />
69
- </IconButton>
70
- </ListItemIcon>
71
- )}
72
61
  </div>
73
62
  )
74
63
  })}
@@ -35,9 +35,11 @@ const FieldInputLayout = ({
35
35
  'u-dn': isSecondary && !showSecondaryFields
36
36
  })}
37
37
  >
38
- <div className="u-w-2-half">
39
- {icon && <Icon icon={icon} color="var(--iconTextColor)" />}
40
- </div>
38
+ {icon && (
39
+ <div className="u-w-2-half">
40
+ <Icon icon={icon} color="var(--iconTextColor)" />
41
+ </div>
42
+ )}
41
43
  <div className="u-w-100">
42
44
  {layout === 'array' ? (
43
45
  <FieldInputArray
@@ -13,11 +13,12 @@ const FieldInputWrapper = ({
13
13
  attributes: {
14
14
  component,
15
15
  layout,
16
+ isHalfWidth,
16
17
  icon,
17
18
  isSecondary,
18
19
  validate,
19
20
  ...restAttributes
20
- }, // ⚠️ `layout` `icon` `isSecondary` `validate` are removed from attributes to avoid DOM propagration, only used for business rules
21
+ }, // ⚠️ `layout` `isHalfWidth` `icon` `isSecondary` `validate` are removed from attributes to avoid DOM propagration, only used for business rules
21
22
  variant,
22
23
  fullWidth,
23
24
  ...props
@@ -37,6 +38,7 @@ const FieldInputWrapper = ({
37
38
  {...props}
38
39
  variant={variant}
39
40
  fullWidth={fullWidth}
41
+ size="small"
40
42
  minRows="2"
41
43
  />
42
44
  )
@@ -0,0 +1,18 @@
1
+ import React from 'react'
2
+
3
+ import Icon from 'cozy-ui/transpiled/react/Icon'
4
+ import IconButton from 'cozy-ui/transpiled/react/IconButton'
5
+ import CrossCircleIcon from 'cozy-ui/transpiled/react/Icons/CrossCircle'
6
+ import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
7
+
8
+ const RemoveButton = ({ onRemove }) => {
9
+ return (
10
+ <ListItemIcon className="u-ml-half">
11
+ <IconButton aria-label="delete" size="medium" onClick={onRemove}>
12
+ <Icon icon={CrossCircleIcon} />
13
+ </IconButton>
14
+ </ListItemIcon>
15
+ )
16
+ }
17
+
18
+ export default RemoveButton
@@ -40,6 +40,7 @@ export const fields = [
40
40
  icon: null,
41
41
  type: 'text',
42
42
  layout: 'accordion',
43
+ isHalfWidth: true,
43
44
  subFields: [
44
45
  {
45
46
  name: 'additionalName',
@@ -56,7 +57,8 @@ export const fields = [
56
57
  {
57
58
  name: 'familyName',
58
59
  icon: null,
59
- type: 'text'
60
+ type: 'text',
61
+ isHalfWidth: true
60
62
  },
61
63
  {
62
64
  name: 'company',
@@ -210,6 +210,8 @@ export const createAddress = ({ address, oldContact, t }) => {
210
210
  * @returns {Record<string, { data: { _id: string, _type: string }[] }>} - The related contacts relationships
211
211
  */
212
212
  export const getRelatedContactRelationships = relatedContact => {
213
+ if (!relatedContact) return {}
214
+
213
215
  // Tips filter Boolean to remove undefined value from array when relatedContact is empty (see contactToFormValues)
214
216
  const data = relatedContact.filter(Boolean).reduce((acc, curr) => {
215
217
  const relationType = curr.relatedContactLabel
@@ -460,17 +462,26 @@ export const makeFields = (customFields, defaultFields) => {
460
462
  return defaultFields
461
463
  }
462
464
 
463
- const fields = [...defaultFields]
465
+ let fields = [...defaultFields]
464
466
 
465
467
  customFields.forEach(customField => {
466
468
  const defaultField = fields.find(field => field.name === customField.name)
469
+
470
+ // If isRemoved is true, remove the field from the result
471
+ if (customField.isRemoved) {
472
+ if (defaultField) {
473
+ fields = fields.filter(field => field.name !== customField.name)
474
+ }
475
+ return
476
+ }
477
+
467
478
  const _field = defaultField || customField
468
479
 
469
480
  if (defaultField) {
470
481
  Object.assign(_field, customField)
471
482
  }
472
483
 
473
- if (_field.position) {
484
+ if (_field.position !== undefined) {
474
485
  if (defaultField) {
475
486
  const fieldIndex = fields.indexOf(defaultField)
476
487
  fields.splice(fieldIndex, 1)
@@ -441,6 +441,68 @@ describe('makeFields', () => {
441
441
  }
442
442
  ])
443
443
  })
444
+
445
+ it('should remove default field when custom field has isRemoved set to true', () => {
446
+ const defaultFields = [
447
+ { name: 'firstname' },
448
+ { name: 'lastname' },
449
+ { name: 'email' }
450
+ ]
451
+ const customFields = [{ name: 'lastname', isRemoved: true }]
452
+
453
+ const res = makeFields(customFields, defaultFields)
454
+
455
+ expect(res).toStrictEqual([{ name: 'firstname' }, { name: 'email' }])
456
+ })
457
+
458
+ it('should remove multiple default fields when multiple custom fields have isRemoved set to true', () => {
459
+ const defaultFields = [
460
+ { name: 'firstname' },
461
+ { name: 'lastname' },
462
+ { name: 'email' },
463
+ { name: 'phone' }
464
+ ]
465
+ const customFields = [
466
+ { name: 'lastname', isRemoved: true },
467
+ { name: 'phone', isRemoved: true }
468
+ ]
469
+
470
+ const res = makeFields(customFields, defaultFields)
471
+
472
+ expect(res).toStrictEqual([{ name: 'firstname' }, { name: 'email' }])
473
+ })
474
+
475
+ it('should ignore custom fields with isRemoved but no matching default field', () => {
476
+ const defaultFields = [{ name: 'firstname' }, { name: 'lastname' }]
477
+ const customFields = [{ name: 'nonexistent', isRemoved: true }]
478
+
479
+ const res = makeFields(customFields, defaultFields)
480
+
481
+ expect(res).toStrictEqual(defaultFields)
482
+ })
483
+
484
+ it('should handle mixed custom fields with isRemoved, position, and property overrides', () => {
485
+ const defaultFields = [
486
+ { name: 'firstname', type: 'text' },
487
+ { name: 'lastname', type: 'text' },
488
+ { name: 'email', type: 'email' },
489
+ { name: 'phone', type: 'tel' }
490
+ ]
491
+ const customFields = [
492
+ { name: 'lastname', isRemoved: true },
493
+ { name: 'email', isSecondary: true, position: 0 },
494
+ { name: 'middlename', position: 1 }
495
+ ]
496
+
497
+ const res = makeFields(customFields, defaultFields)
498
+
499
+ expect(res).toStrictEqual([
500
+ { name: 'email', type: 'email', isSecondary: true, position: 0 },
501
+ { name: 'middlename', position: 1 },
502
+ { name: 'firstname', type: 'text' },
503
+ { name: 'phone', type: 'tel' }
504
+ ])
505
+ })
444
506
  })
445
507
 
446
508
  describe('hasNoValues', () => {
@@ -3,7 +3,9 @@ import React, { useState } from 'react'
3
3
  import { Form } from 'react-final-form'
4
4
 
5
5
  import { getHasManyItems } from 'cozy-client/dist/associations/HasMany'
6
+ import Box from 'cozy-ui/transpiled/react/Box'
6
7
  import Button from 'cozy-ui/transpiled/react/Buttons'
8
+ import { useBreakpoints } from 'cozy-ui/transpiled/react/providers/Breakpoints'
7
9
  import { useI18n, useExtendI18n } from 'twake-i18n'
8
10
 
9
11
  import FieldInputLayout from './FieldInputLayout'
@@ -49,6 +51,7 @@ const ContactForm = ({ contacts, contact, customFieldsProps, onSubmit }) => {
49
51
  const { t } = useI18n()
50
52
  const { fields, makeCustomFieldsFormValues, makeCustomContactValues } =
51
53
  customFieldsProps
54
+ const { isMobile } = useBreakpoints()
52
55
 
53
56
  const _fields = makeFields(fields, defaultFields)
54
57
  const hasSecondaryFields = _fields.some(el => el.isSecondary)
@@ -90,20 +93,30 @@ const ContactForm = ({ contacts, contact, customFieldsProps, onSubmit }) => {
90
93
  onSubmit={handleSubmit}
91
94
  className="u-flex u-flex-column"
92
95
  >
93
- {_fields.map((attributes, index) => (
94
- <FieldInputLayout
95
- key={index}
96
- attributes={attributes}
97
- contacts={contacts}
98
- contact={contact}
99
- showSecondaryFields={showSecondaryFields}
100
- formProps={{
101
- valid,
102
- submitFailed,
103
- errors
104
- }}
105
- />
106
- ))}
96
+ <div className="u-flex u-flex-wrap u-flex-justify-between">
97
+ {_fields.map((attributes, index) => (
98
+ <Box
99
+ key={index}
100
+ width={
101
+ attributes.isHalfWidth && !isMobile
102
+ ? 'calc(50% - 0.5rem)'
103
+ : '100%'
104
+ }
105
+ >
106
+ <FieldInputLayout
107
+ attributes={attributes}
108
+ contacts={contacts}
109
+ contact={contact}
110
+ showSecondaryFields={showSecondaryFields}
111
+ formProps={{
112
+ valid,
113
+ submitFailed,
114
+ errors
115
+ }}
116
+ />
117
+ </Box>
118
+ ))}
119
+ </div>
107
120
  {hasSecondaryFields && !showSecondaryFields && (
108
121
  <div>
109
122
  <Button
@@ -1,2 +0,0 @@
1
- .contact-form-field__wrapper
2
- width calc(100% - 3rem)