@configuratorware/configurator-admingui 1.38.7 → 1.39.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 (57) hide show
  1. package/Components/Form.js +3 -0
  2. package/Components/FormFragments/AttributeValueChip.js +262 -0
  3. package/Components/FormFragments/InputArray.js +6 -4
  4. package/Components/FormFragments/InputWithSource.js +14 -2
  5. package/Components/FormFragments/Select.js +5 -3
  6. package/Components/FormFragments/index.js +9 -0
  7. package/Components/LocalizedPriceTextField.js +6 -4
  8. package/Components/LocalizedPriceValue.js +1 -1
  9. package/Screens/Attribute/Containers/Edit.js +7 -0
  10. package/Screens/Attribute/Reducers/Reducer.js +4 -1
  11. package/Screens/Attribute/Translations.js +6 -2
  12. package/Screens/Client/Containers/Edit.js +4 -0
  13. package/Screens/Client/Reducers/Reducer.js +3 -0
  14. package/Screens/Client/Translations.js +1 -0
  15. package/Screens/CurrentClient/Containers/Edit.js +4 -0
  16. package/Screens/CurrentClient/Reducers/Reducer.js +3 -0
  17. package/Screens/DefaultClient/Containers/Edit.js +4 -0
  18. package/Screens/DefaultClient/Reducers/Reducer.js +3 -0
  19. package/Screens/Designer/SubScreens/Visualization/Containers/Edit/Edit.js +17 -10
  20. package/Screens/ImageGallery/Containers/Edit.js +59 -0
  21. package/Screens/ImageGallery/Reducers/Reducer.js +36 -2
  22. package/Screens/Item/DataStructures/Attributes/Editor.js +3 -4
  23. package/Screens/Item/Translations.js +4 -2
  24. package/Screens/PriceTypes/Containers/Edit.js +36 -10
  25. package/Screens/PriceTypes/Reducers/Reducer.js +2 -2
  26. package/Screens/PriceTypes/Translations.js +14 -0
  27. package/UIComponents/ChipInput.js +29 -3
  28. package/package.json +3 -3
  29. package/scripts/cpPublic.js +32 -0
  30. package/src/Components/Form.js +2 -0
  31. package/src/Components/FormFragments/AttributeValueChip.js +140 -0
  32. package/src/Components/FormFragments/InputArray.js +8 -2
  33. package/src/Components/FormFragments/InputWithSource.js +3 -0
  34. package/src/Components/FormFragments/Select.js +10 -4
  35. package/src/Components/FormFragments/index.js +3 -0
  36. package/src/Components/LocalizedPriceTextField.js +5 -3
  37. package/src/Components/LocalizedPriceValue.js +5 -3
  38. package/src/Screens/Attribute/Containers/Edit.js +7 -0
  39. package/src/Screens/Attribute/Reducers/Reducer.js +1 -0
  40. package/src/Screens/Attribute/Translations.js +4 -0
  41. package/src/Screens/Client/Containers/Edit.js +7 -2
  42. package/src/Screens/Client/Reducers/Reducer.js +1 -0
  43. package/src/Screens/Client/Translations.js +5 -2
  44. package/src/Screens/CurrentClient/Containers/Edit.js +9 -4
  45. package/src/Screens/CurrentClient/Reducers/Reducer.js +1 -0
  46. package/src/Screens/DefaultClient/Containers/Edit.js +6 -1
  47. package/src/Screens/DefaultClient/Reducers/Reducer.js +1 -0
  48. package/src/Screens/Designer/SubScreens/Visualization/Containers/Edit/Edit.js +10 -3
  49. package/src/Screens/ImageGallery/Containers/Edit.js +46 -1
  50. package/src/Screens/ImageGallery/Reducers/Reducer.js +18 -1
  51. package/src/Screens/Item/DataStructures/Attributes/Editor.js +2 -4
  52. package/src/Screens/Item/Translations.js +5 -1
  53. package/src/Screens/PriceTypes/Containers/Edit.js +32 -9
  54. package/src/Screens/PriceTypes/Reducers/Reducer.js +1 -1
  55. package/src/Screens/PriceTypes/Translations.js +14 -0
  56. package/src/UIComponents/ChipInput.js +23 -0
  57. package/scripts/cpFavicon.js +0 -28
@@ -21,6 +21,10 @@ var _DefaultConnectedForm = _interopRequireDefault(require("../../../Components/
21
21
 
22
22
  var _i18n = require("../../../App/i18n");
23
23
 
24
+ var _LocalizedPriceTextField = _interopRequireDefault(require("../../../Components/LocalizedPriceTextField"));
25
+
26
+ var _LocalizedPriceValue = _interopRequireDefault(require("../../../Components/LocalizedPriceValue"));
27
+
24
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
25
29
 
26
30
  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); }
@@ -88,6 +92,61 @@ var formFields = [{
88
92
  warningMessage: (0, _i18n.T)('UploadFailureMessage', {
89
93
  url: "<a href=\"http://svg.enshrined.co.uk/\"\n target=\"_blank\">http://svg.enshrined.co.uk/</a>"
90
94
  })
95
+ }, {
96
+ name: 'prices',
97
+ label: 'Prices',
98
+ type: 'dynSource',
99
+ sourceConfig: {
100
+ text: function text(_ref) {
101
+ var identifier = _ref.identifier,
102
+ currency = _ref.currency;
103
+ return "".concat(identifier, " / ").concat(currency.symbol, " (").concat(currency.iso, ")");
104
+ }
105
+ },
106
+ dynSource: {
107
+ type: 'tabs',
108
+ reducerName: _Actions.IMAGE_GALLERY_REDUCER_NAME,
109
+ dataSource: 'channels'
110
+ },
111
+ tabs: {
112
+ filterValues: function filterValues(channel, value) {
113
+ return +channel.id === +value.channel;
114
+ },
115
+ assignTabValue: function assignTabValue(tabValue, data) {
116
+ return data.channel = tabValue;
117
+ },
118
+ name: 'prices',
119
+ type: 'array',
120
+ layout: 'groups',
121
+ className: 'table',
122
+ array: {
123
+ columns: null,
124
+ disableAdd: function disableAdd(_ref2) {
125
+ var value = _ref2.value;
126
+ // limit to one price item, since "amount from" is not available
127
+ return value.length > 0;
128
+ },
129
+ fields: [{
130
+ name: 'price',
131
+ label: 'price',
132
+ type: _LocalizedPriceTextField["default"],
133
+ renderValue: function renderValue(value) {
134
+ return /*#__PURE__*/_react["default"].createElement(_LocalizedPriceValue["default"], {
135
+ data: value
136
+ });
137
+ }
138
+ }, {
139
+ name: 'priceNet',
140
+ label: 'price Net',
141
+ type: _LocalizedPriceTextField["default"],
142
+ renderValue: function renderValue(value) {
143
+ return /*#__PURE__*/_react["default"].createElement(_LocalizedPriceValue["default"], {
144
+ data: value
145
+ });
146
+ }
147
+ }]
148
+ }
149
+ }
91
150
  }]); //export default generateConnectedEdit(formFields, IMAGE_GALLERY_REDUCER_NAME, setFieldData, postData);
92
151
 
93
152
  var BaseDataForm = (0, _DefaultConnectedForm["default"])(formFields, _Actions.IMAGE_GALLERY_REDUCER_NAME, setFieldData, postData, false, _Actions["default"].customAction);
@@ -23,13 +23,47 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
23
23
 
24
24
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
25
25
 
26
- var initialState = _objectSpread(_objectSpread({}, (0, _Reducer.getDefaultEntityState)(_objectSpread({
26
+ var initialState = _objectSpread(_objectSpread({}, (0, _Reducer.getDefaultEntityState)(_objectSpread(_objectSpread({
27
27
  id: null,
28
28
  imageUrl: '',
29
29
  printUrl: '',
30
30
  thumbUrl: '',
31
31
  texts: []
32
- }, _Reducer2["default"]), null, _Actions.IMAGE_GALLERY_DATA_KEY)), {}, {
32
+ }, _Reducer2["default"]), {}, {
33
+ prices: {
34
+ value: [],
35
+ schema: {
36
+ id: {
37
+ value: null
38
+ },
39
+ price: {
40
+ value: null,
41
+ constraints: {
42
+ presence: true,
43
+ decimalPoint: true
44
+ }
45
+ },
46
+ priceNet: {
47
+ value: 0,
48
+ constraints: {
49
+ presence: true
50
+ }
51
+ },
52
+ channel: {
53
+ value: null,
54
+ constraints: {
55
+ presence: true
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }), {
61
+ dependencies: [{
62
+ name: 'channels',
63
+ dataKey: 'channels',
64
+ source: 'channels'
65
+ }]
66
+ }, _Actions.IMAGE_GALLERY_DATA_KEY)), {}, {
33
67
  imageFile: null,
34
68
  printFile: null,
35
69
  uploadedImages: null,
@@ -66,7 +66,7 @@ var _default = [{
66
66
  data: translations,
67
67
  property: "translation",
68
68
  fallback: translated_value
69
- }), /*#__PURE__*/_react["default"].createElement("span", null, " (", value, ")"));
69
+ }));
70
70
  }));
71
71
  }
72
72
  }],
@@ -99,20 +99,19 @@ var _default = [{
99
99
  name: 'values',
100
100
  label: 'Values',
101
101
  type: 'dynSource',
102
- placeholderFocused: 'item.attributes.searchOrAddAttributeValue',
103
102
  sourceConfig: {
104
103
  value: 'value',
105
104
  text: 'label'
106
105
  },
107
106
  dynSource: {
108
- type: 'chip',
107
+ type: 'attributeValueChip',
109
108
  listKey: 'attributevalues',
110
109
  url: 'attributevalues',
111
110
  autoLoad: false,
112
111
  maxSearchResults: 25,
113
112
  mapItems: function mapItems(item) {
114
113
  return _objectSpread(_objectSpread({}, item), {}, {
115
- label: "".concat(item.translated_value, " (").concat(item.value, ")")
114
+ label: item.value === item.translated_value ? item.translated_value : "".concat(item.translated_value, " (").concat(item.value, ")")
116
115
  });
117
116
  }
118
117
  },
@@ -37,7 +37,8 @@ require("../../App/i18n").use({
37
37
  attributes: {
38
38
  addButtonLabel: 'Add attribute',
39
39
  searchOrAddAttribute: 'Search for an existing attribute or add a new one by clicking on +',
40
- searchOrAddAttributeValue: 'Search for a existing value or add a new one by clicking on +'
40
+ searchOrAddAttributeValue: 'Search for an existing value or add a new one by clicking on +',
41
+ searchOrAddNumbericAttributeValue: 'Search for an existing value or add a new with enter'
41
42
  },
42
43
  headline: 'This product is defined as follows:',
43
44
  editorPopup: {
@@ -118,7 +119,8 @@ require("../../App/i18n").use({
118
119
  attributes: {
119
120
  addButtonLabel: 'Attribut hinzufügen',
120
121
  searchOrAddAttribute: 'Suche ein vorhandenes Attribut oder lege ein neues mit dem + an',
121
- searchOrAddAttributeValue: 'Suche einen Attributwert oder lege einen neuen mit dem + an'
122
+ searchOrAddAttributeValue: 'Suche einen Attributwert oder lege einen neuen mit dem + an',
123
+ searchOrAddNumbericAttributeValue: 'Suche einen Attributwert oder lege einen neuen mit der Eingabetaste an'
122
124
  },
123
125
  headline: 'Dieses Produkt ist folgendermaßen definiert:',
124
126
  copyCreatorItemDialogTitle: 'Gewähltes Produkt kopieren',
@@ -7,7 +7,9 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports["default"] = void 0;
9
9
 
10
- var _Actions = _interopRequireWildcard(require("../Reducers/Actions"));
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _i18n = require("../../../App/i18n");
11
13
 
12
14
  var _DefaultConnectedForm = _interopRequireDefault(require("../../../Components/DefaultConnectedForm"));
13
15
 
@@ -15,14 +17,14 @@ var _LocalizedPriceTextField = require("../../../Components/LocalizedPriceTextFi
15
17
 
16
18
  var _LocalizedPriceValue = _interopRequireDefault(require("../../../Components/LocalizedPriceValue"));
17
19
 
18
- var _react = _interopRequireDefault(require("react"));
19
-
20
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
20
+ var _Actions = _interopRequireWildcard(require("../Reducers/Actions"));
21
21
 
22
22
  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); }
23
23
 
24
24
  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; }
25
25
 
26
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
27
+
26
28
  var setFieldData = _Actions["default"].setFieldData,
27
29
  postData = _Actions["default"].postData;
28
30
  var formFields = [{
@@ -42,9 +44,33 @@ var formFields = [{
42
44
  label: 'selectable By User',
43
45
  type: 'toggle'
44
46
  }, {
45
- name: 'imageDataDependent',
46
- label: 'image Data Dependent',
47
- type: 'toggle'
47
+ name: 'imageDependent',
48
+ label: 'imageDependent.label',
49
+ type: 'select',
50
+ sourceConfig: {
51
+ text: function text(_ref) {
52
+ var label = _ref.label;
53
+ return (0, _i18n.T)(label);
54
+ },
55
+ value: 'value'
56
+ },
57
+ source: [{
58
+ value: null,
59
+ label: 'imageDependent.no',
60
+ id: 'print'
61
+ }, {
62
+ value: 'image_data_dependent',
63
+ label: 'imageDependent.imageDataDependent',
64
+ id: 'image_data_dependent'
65
+ }, {
66
+ value: 'user_image_dependent',
67
+ label: 'imageDependent.userImageDependent',
68
+ id: 'user_image_dependent'
69
+ }, {
70
+ value: 'gallery_image_dependent',
71
+ label: 'imageDependent.galleryImageDependent',
72
+ id: 'gallery_image_dependent'
73
+ }]
48
74
  }, {
49
75
  name: 'texts',
50
76
  label: 'Title',
@@ -71,9 +97,9 @@ var formFields = [{
71
97
  },
72
98
  type: 'dynSource',
73
99
  sourceConfig: {
74
- text: function text(_ref) {
75
- var identifier = _ref.identifier,
76
- currency = _ref.currency;
100
+ text: function text(_ref2) {
101
+ var identifier = _ref2.identifier,
102
+ currency = _ref2.currency;
77
103
  return "".concat(identifier, " / ").concat(currency.symbol, " (").concat(currency.iso, ")");
78
104
  }
79
105
  },
@@ -34,8 +34,8 @@ var initialState = _objectSpread({}, (0, _Reducer.getDefaultEntityState)({
34
34
  selectableByUser: {
35
35
  value: true
36
36
  },
37
- imageDataDependent: {
38
- value: true
37
+ imageDependent: {
38
+ value: null
39
39
  },
40
40
  texts: [],
41
41
  prices: {
@@ -15,6 +15,13 @@ require("../../App/i18n").use({
15
15
  prices: {
16
16
  addButtonLabel: 'Add Price'
17
17
  }
18
+ },
19
+ imageDependent: {
20
+ label: 'Depending on the image data',
21
+ no: 'No',
22
+ imageDataDependent: 'Image data dependent (uploaded + images from image gallery)',
23
+ userImageDependent: 'Uploaded Image Data Dependent',
24
+ galleryImageDependent: 'Image gallery Data Dependent'
18
25
  }
19
26
  },
20
27
  de: {
@@ -31,6 +38,13 @@ require("../../App/i18n").use({
31
38
  prices: {
32
39
  addButtonLabel: 'Preis hinzufügen'
33
40
  }
41
+ },
42
+ imageDependent: {
43
+ label: 'Abhängig von den Bilddaten',
44
+ no: 'Nein',
45
+ imageDataDependent: 'Abhängig von Bilddaten (hochgeladene + Bildern aus der Bildergalerie)',
46
+ userImageDependent: 'Abhängig von hochgeladenen Bildern',
47
+ galleryImageDependent: 'Abhängig von Bildern aus der Bildergalerie'
34
48
  }
35
49
  }
36
50
  }, true);
@@ -82,7 +82,18 @@ var styles = function styles(theme) {
82
82
  margin: "".concat(theme.spacing.unit / 2, "px ").concat(theme.spacing.unit / 4, "px")
83
83
  },
84
84
  inputRoot: {
85
- flexWrap: 'wrap'
85
+ flexWrap: 'wrap',
86
+ '& input[type=number]': {
87
+ '-moz-appearance': 'textfield'
88
+ },
89
+ '& input[type=number]::-webkit-outer-spin-button': {
90
+ '-webkit-appearance': 'none',
91
+ margin: 0
92
+ },
93
+ '& input[type=number]::-webkit-inner-spin-button': {
94
+ '-webkit-appearance': 'none',
95
+ margin: 0
96
+ }
86
97
  },
87
98
  inputInput: {
88
99
  flexGrow: 1
@@ -199,7 +210,10 @@ var ChipInput = /*#__PURE__*/function (_React$Component) {
199
210
  dataSourceConfig = _this$props.dataSourceConfig,
200
211
  onFocus = _this$props.onFocus,
201
212
  placeholder = _this$props.placeholder,
202
- filter = _this$props.filter;
213
+ filter = _this$props.filter,
214
+ onInputEnterKey = _this$props.onInputEnterKey,
215
+ resetOnInputEnterKey = _this$props.resetOnInputEnterKey,
216
+ textFieldType = _this$props.textFieldType;
203
217
  var _this$state = this.state,
204
218
  selectedItem = _this$state.selectedItem,
205
219
  inputValue = _this$state.inputValue;
@@ -239,7 +253,19 @@ var ChipInput = /*#__PURE__*/function (_React$Component) {
239
253
  onBlur: _this2.onBlur,
240
254
  onFocus: onFocus
241
255
  }),
242
- classes: classes
256
+ classes: classes,
257
+ type: textFieldType,
258
+ onKeyDown: function onKeyDown(evt) {
259
+ if (!highlightedIndex && onInputEnterKey && evt.keyCode === 13) {
260
+ onInputEnterKey(evt);
261
+
262
+ if (resetOnInputEnterKey) {
263
+ _this2.setState({
264
+ inputValue: ''
265
+ });
266
+ }
267
+ }
268
+ }
243
269
  }), isOpen ? /*#__PURE__*/_react["default"].createElement(_Paper["default"], {
244
270
  className: classes.paper,
245
271
  square: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configuratorware/configurator-admingui",
3
- "version": "1.38.7",
3
+ "version": "1.39.1",
4
4
  "license": "UNLICENSED",
5
5
  "private": false,
6
6
  "dependencies": {
@@ -29,7 +29,7 @@
29
29
  "react-redux-i18n": "^1.9.3",
30
30
  "react-router": "^3.2.6",
31
31
  "react-sortable-hoc": "^1.11.0",
32
- "redhotmagma-visualization": "1.38.7",
32
+ "redhotmagma-visualization": "1.39.1",
33
33
  "redux": "^4.1.0",
34
34
  "redux-logger": "^3.0.6",
35
35
  "redux-persist": "^5.10.0",
@@ -58,7 +58,7 @@
58
58
  "url-loader": "^1.1.2"
59
59
  },
60
60
  "scripts": {
61
- "copy-public": "node ./scripts/cpFavicon.js",
61
+ "copy-public": "node ./scripts/cpPublic.js",
62
62
  "install": "npm run copy-public"
63
63
  },
64
64
  "readme": "# configurator-admingui\n\n## Setup for development\n1. Follow the \"preparation\", \"install\" and \"start development\" steps in frontend/README.md\n1. Open the core app in the browser: http://localhost:3000\n\n## Ready to build the artifacts\n- run `pnpm build` from project scope (does not build workspace dependencies automatically)\n- or run `pnpm build` from workspace root\n"
@@ -0,0 +1,32 @@
1
+ /* eslint no-console: 0 */
2
+
3
+ /**
4
+ * This script is made to copy the content of the public folder
5
+ * into the same destination in the client projects
6
+ *
7
+ * Runs only when installing this package as a dependency in client projects
8
+ *
9
+ * usage: node ./scripts/cpPublic.js
10
+ *
11
+ * @author boda@redhotmagma.de
12
+ */
13
+
14
+ const path = require('path');
15
+
16
+ const { cpDirDeep, testMatchers } = require('@configuratorware/scripts/utils/fileSystem');
17
+ const { isDependency } = require('@configuratorware/scripts/utils/process');
18
+
19
+ // keep existing files listed below
20
+ const test = testMatchers.keepIfExists(/favicon\.ico$/);
21
+
22
+ if (isDependency()) {
23
+ console.log(
24
+ '\x1b[37m\x1b[42m%s\x1b[0m',
25
+ 'Copying public content into the public directory of the client project'
26
+ );
27
+
28
+ const src = path.resolve(__dirname, '../public');
29
+ const dst = path.resolve(__dirname, '../../../../public');
30
+
31
+ cpDirDeep(src, dst, { testFn: test });
32
+ }
@@ -77,6 +77,8 @@ const getInputComponentByType = type => {
77
77
  return MarkDown;
78
78
  case 'hinttext':
79
79
  return HintText;
80
+ case 'attributeValueChip':
81
+ return FF.AttributeValueChip;
80
82
  default:
81
83
  return null;
82
84
  }
@@ -0,0 +1,140 @@
1
+ import React from 'react';
2
+ import { cloneDeep, debounce, find, differenceBy, get, isString } from 'lodash';
3
+
4
+ import UIChipInput, { propTypes } from '../../UIComponents/ChipInput';
5
+ import { Filters } from '../../UIComponents/AutoComplete';
6
+ import { withAddButton } from './InlineAddButton';
7
+ import { t } from '../../App/i18n';
8
+
9
+ class AttributeValueChip extends React.Component {
10
+
11
+ static propTypes = {
12
+ sourceConfig: propTypes.dataSourceConfig,
13
+ };
14
+
15
+ state = {
16
+ placeholder: '',
17
+ };
18
+
19
+ isNumericAttributeType = () => {
20
+ const type = get(this.props, 'schema._data.attributedatatype.value');
21
+ return type === 'integer' || type === 'number';
22
+ }
23
+
24
+ loadInitialSource() {
25
+ const { initialSourceSize, loadSource } = this.props;
26
+ if (initialSourceSize) {
27
+ loadSource({ limit: initialSourceSize });
28
+ }
29
+ }
30
+
31
+ loadSource(query) {
32
+ const { autoLoad, loadSource } = this.props;
33
+ if (isString(query) && autoLoad === false) {
34
+ // IMPORTANT: the filter value should be passed in the "query" get param
35
+ loadSource({ query });
36
+ }
37
+ }
38
+
39
+ loadSourceDebounced = debounce(this.loadSource, 300);
40
+
41
+ onUpdateInput = query => {
42
+ this.loadSourceDebounced(query);
43
+ };
44
+
45
+ onRequestAdd = chip => {
46
+ const { name, source, onChange, sourceConfig, single, value } = this.props;
47
+ const items = _.cloneDeep(value);
48
+
49
+ // if it already in the values just return
50
+ if (find(items, { [sourceConfig.value]: chip[sourceConfig.value] })) {
51
+ return;
52
+ }
53
+
54
+ let item = find(source, { [sourceConfig.value]: chip[sourceConfig.value] });
55
+ if (!item) {
56
+ // if not found, use the first which is not in the list
57
+ const diff = differenceBy(source, items, sourceConfig.value);
58
+ if (diff.length !== 0) {
59
+ item = diff[0];
60
+ } else {
61
+ return;
62
+ }
63
+ }
64
+ if (single) {
65
+ items.splice(0, items.length);
66
+ }
67
+ items.push(item);
68
+ onChange(name, items);
69
+ };
70
+
71
+ onRequestDelete = (text, idx) => {
72
+ const { name, onChange, value } = this.props;
73
+ const items = cloneDeep(value);
74
+ if (idx > -1 && idx < items.length) {
75
+ items.splice(idx, 1);
76
+ onChange(name, items);
77
+ }
78
+ };
79
+
80
+ /**
81
+ * Called by the InlineAddButton, when the data is ready
82
+ * @param chip
83
+ */
84
+ triggerChange(chip) {
85
+ // no additional checks needed, just append the new value to the list
86
+ const { name, onChange, sourceConfig, value } = this.props;
87
+ const items = _.cloneDeep(value);
88
+ if (find(items, { [sourceConfig.value]: chip[sourceConfig.value] })) {
89
+ return;
90
+ }
91
+ items.push(chip);
92
+ onChange(name, items);
93
+ }
94
+
95
+ onFocus = evt => {
96
+ this.loadInitialSource();
97
+ const placeholder = t(this.isNumericAttributeType() ? 'item.attributes.searchOrAddNumbericAttributeValue' : 'item.attributes.searchOrAddAttributeValue');
98
+ this.setState({ placeholder });
99
+ const { onFocus } = this.props;
100
+ onFocus && onFocus(evt);
101
+ };
102
+
103
+ onBlur = evt => {
104
+ const { onBlur } = this.props;
105
+ this.setState({ placeholder: null});
106
+ onBlur && onBlur(evt);
107
+ };
108
+
109
+ onInputEnterKey = evt => {
110
+ this.triggerChange({id: null, value: evt.target.value, translated_value: evt.target.value});
111
+ }
112
+
113
+ render() {
114
+ const { value, label, source, sourceConfig, openOnFocus, autoLoad } = this.props;
115
+ const { placeholder } = this.state;
116
+ const { onRequestAdd, onRequestDelete, onUpdateInput, onBlur, onFocus, onInputEnterKey } = this;
117
+
118
+ return (
119
+ <UIChipInput
120
+ floatingLabelText={label}
121
+ onBlur={onBlur}
122
+ onFocus={onFocus}
123
+ dataSource={source}
124
+ onInputValueChange={onUpdateInput}
125
+ dataSourceConfig={sourceConfig}
126
+ value={value}
127
+ handleChange={onRequestAdd}
128
+ onRequestDelete={onRequestDelete}
129
+ openOnFocus={openOnFocus}
130
+ filter={autoLoad === false ? Filters.defaultFilter : Filters.fuzzyFilter}
131
+ onInputEnterKey={onInputEnterKey}
132
+ resetOnInputEnterKey={true}
133
+ textFieldType={'number'}
134
+ placeholder={placeholder}
135
+ />
136
+ );
137
+ }
138
+ }
139
+
140
+ export default withAddButton(AttributeValueChip);
@@ -140,7 +140,11 @@ class InputArray extends Component {
140
140
  }
141
141
  const { schema } = this.props;
142
142
  schema.reset({ active: this });
143
- this.setState({ selectedRow: false, showEditor: true, newItem: !this.state.newItem });
143
+ this.setState(state => ({
144
+ selectedRow: false,
145
+ showEditor: !this.isAddingDisabled(),
146
+ newItem: !state.newItem,
147
+ }));
144
148
  };
145
149
 
146
150
  isAddingDisabled() {
@@ -204,7 +208,9 @@ class InputArray extends Component {
204
208
  {this.state.showEditor &&
205
209
  (showPopup ? (
206
210
  <Dialog open={true}>
207
- <div className="form">{fields.map((field, idx) => this.props.renderWrappedInput(field, idx))}</div>
211
+ <div className="form">
212
+ {fields.map((field, idx) => this.props.renderWrappedInput(field, idx))}
213
+ </div>
208
214
  <div className="overlay-actions">
209
215
  {newItem && (
210
216
  <RaisedButton
@@ -44,3 +44,6 @@ export const sourceConfigPropType = PropTypes.shape({
44
44
  text: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
45
45
  value: PropTypes.string.isRequired,
46
46
  });
47
+
48
+ export const valueToNull = value => (value === 'null' ? null : value);
49
+ export const nullToValue = value => (value === null ? 'null' : value);
@@ -4,10 +4,15 @@ import find from 'lodash/find';
4
4
  import SelectField from '../../UIComponents/SelectField';
5
5
  import MenuItem from '../../UIComponents/MenuItem';
6
6
 
7
- import { getInputValueByConfig, getInputItemParamsByConfig } from './InputWithSource';
7
+ import {
8
+ getInputValueByConfig,
9
+ getInputItemParamsByConfig,
10
+ valueToNull,
11
+ nullToValue,
12
+ } from './InputWithSource';
8
13
 
9
14
  const select = props => {
10
- const value = getInputValueByConfig(props);
15
+ const value = nullToValue(getInputValueByConfig(props));
11
16
  return (
12
17
  <SelectField
13
18
  style={{ width: '100%' }}
@@ -19,7 +24,7 @@ const select = props => {
19
24
  const selectedObj = find(props.source, { [key]: event.target.value });
20
25
  props.onChange(props.name, selectedObj);
21
26
  } else {
22
- props.onChange(props.name, event.target.value);
27
+ props.onChange(props.name, valueToNull(event.target.value));
23
28
  }
24
29
  }}
25
30
  onFocus={() => props.source.length === 0 && this.props.loadSource && this.props.loadSource()}
@@ -32,8 +37,9 @@ const select = props => {
32
37
  >
33
38
  {props.source.map((item, idx) => {
34
39
  const { value, text } = getInputItemParamsByConfig(item, props.sourceConfig);
40
+ const _value = nullToValue(value);
35
41
  return (
36
- <MenuItem value={value} key={idx}>
42
+ <MenuItem value={_value} key={idx}>
37
43
  {text}
38
44
  </MenuItem>
39
45
  );
@@ -26,10 +26,13 @@ import Value from './Value';
26
26
  export { Value };
27
27
  import HintText from './HintText';
28
28
  export { HintText };
29
+ import AttributeValueChip from "./AttributeValueChip";
30
+ export { AttributeValueChip };
29
31
 
30
32
  export default {
31
33
  Text,
32
34
  AutoComplete,
35
+ AttributeValueChip,
33
36
  Select,
34
37
  Checkbox,
35
38
  RadioGroup,
@@ -33,7 +33,7 @@ export const LocalizedPriceTextField = ({
33
33
 
34
34
  const onChange = (name, value) => {
35
35
  // replace occurrences of (localized) decimal separator
36
- let localizedValue = value.replace(new RegExp(altSeparator, 'g'), defaultSeparator);
36
+ let localizedValue = `${value}`.replace(new RegExp(altSeparator, 'g'), defaultSeparator);
37
37
 
38
38
  // remove all letters etc.
39
39
  localizedValue = localizedValue.replace(new RegExp(`[^0-9\\${defaultSeparator}]`, 'g'), '');
@@ -48,9 +48,9 @@ export const LocalizedPriceTextField = ({
48
48
 
49
49
  const displayValue =
50
50
  localeSeparator === altSeparator
51
- ? (value || '').replace(defaultSeparator, localeSeparator)
51
+ ? `${value || ''}`.replace(defaultSeparator, localeSeparator)
52
52
  : localeSeparator === defaultSeparator
53
- ? (value || '').replace(altSeparator, localeSeparator)
53
+ ? `${value || ''}`.replace(altSeparator, localeSeparator)
54
54
  : value;
55
55
 
56
56
  return (
@@ -70,3 +70,5 @@ export const LocalizedPriceTextField = ({
70
70
  />
71
71
  );
72
72
  };
73
+
74
+ export default LocalizedPriceTextField;