cozy-ui-plus 6.0.0 → 6.1.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 (25) hide show
  1. package/CHANGELOG.md +13 -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 +12 -2
  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 +11 -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,19 @@
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.0](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@6.0.0...cozy-ui-plus@6.1.0) (2026-04-02)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **cozy-ui-plus:** Remove icon wrapper on contact modal if no icon ([8b0ffa9](https://github.com/cozy/cozy-libs/commit/8b0ffa9a55dcc964c558625515e191316f03072a))
11
+
12
+ ### Features
13
+
14
+ - **cozy-ui-plus:** Add `isHalfWidth` prop on fields ([a69e7b7](https://github.com/cozy/cozy-libs/commit/a69e7b7af6e0e7021d8ab5bb68894829b9a75c97))
15
+ - **cozy-ui-plus:** Add prop on Contacts modal to remove field ([d9637ff](https://github.com/cozy/cozy-libs/commit/d9637ff248e3ddc39fe8664af645e96e4e551f2f))
16
+ - **cozy-ui-plus:** Change size of contacts modal fields ([27fdb9a](https://github.com/cozy/cozy-libs/commit/27fdb9a344a0803ca1442b93c9213aedb93ad8a2))
17
+ - **cozy-ui-plus:** Move field remove button next to the input ([0a7c409](https://github.com/cozy/cozy-libs/commit/0a7c409a84dbb5f235586069feaa45454df0fbee))
18
+
6
19
  # [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
20
 
8
21
  ### 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,
@@ -541,7 +541,17 @@ var makeFields = function makeFields(customFields, defaultFields) {
541
541
  customFields.forEach(function (customField) {
542
542
  var defaultField = fields.find(function (field) {
543
543
  return field.name === customField.name;
544
- });
544
+ }); // If isRemoved is true, remove the field from the result
545
+
546
+ if (customField.isRemoved) {
547
+ if (defaultField) {
548
+ fields = fields.filter(function (field) {
549
+ return field.name !== customField.name;
550
+ });
551
+ }
552
+
553
+ return;
554
+ }
545
555
 
546
556
  var _field = defaultField || customField;
547
557
 
@@ -549,7 +559,7 @@ var makeFields = function makeFields(customFields, defaultFields) {
549
559
  Object.assign(_field, customField);
550
560
  }
551
561
 
552
- if (_field.position) {
562
+ if (_field.position !== undefined) {
553
563
  if (defaultField) {
554
564
  var fieldIndex = fields.indexOf(defaultField);
555
565
  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.0",
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": "0ac8873a7ba8909f3b02cb719d9fc05133bf5a1d"
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',
@@ -460,17 +460,26 @@ export const makeFields = (customFields, defaultFields) => {
460
460
  return defaultFields
461
461
  }
462
462
 
463
- const fields = [...defaultFields]
463
+ let fields = [...defaultFields]
464
464
 
465
465
  customFields.forEach(customField => {
466
466
  const defaultField = fields.find(field => field.name === customField.name)
467
+
468
+ // If isRemoved is true, remove the field from the result
469
+ if (customField.isRemoved) {
470
+ if (defaultField) {
471
+ fields = fields.filter(field => field.name !== customField.name)
472
+ }
473
+ return
474
+ }
475
+
467
476
  const _field = defaultField || customField
468
477
 
469
478
  if (defaultField) {
470
479
  Object.assign(_field, customField)
471
480
  }
472
481
 
473
- if (_field.position) {
482
+ if (_field.position !== undefined) {
474
483
  if (defaultField) {
475
484
  const fieldIndex = fields.indexOf(defaultField)
476
485
  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)