@inseefr/lunatic 0.3.1-experimental → 0.3.5-experimental
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +218 -259
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/component-wrapper/controls/validators/datepicker.js +25 -14
- package/src/components/component-wrapper/missing/component.js +37 -17
- package/src/components/datepicker/component.js +8 -12
- package/src/components/declarations/wrappers/input-declarations-wrapper.js +31 -9
- package/src/components/dropdown/commons/components/dropdown.js +21 -0
- package/src/components/dropdown/dropdown-edit/dropdown-edit.js +4 -1
- package/src/components/dropdown/dropdown-simple/dropdown.js +3 -1
- package/src/components/input/input-number.js +2 -1
- package/src/components/loop-constructor/block/index.js +1 -1
- package/src/components/loop-constructor/index.js +1 -1
- package/src/components/loop-constructor/roster/index.js +1 -1
- package/src/components/loop-constructor/wrapper/body-component.js +3 -0
- package/src/components/loop-constructor/wrapper/build-components.js +33 -33
- package/src/components/loop-constructor/wrapper/index.js +1 -1
- package/src/components/suggester/components/panel/option-container.js +1 -1
- package/src/components/suggester/components/suggester-content.js +42 -42
- package/src/components/suggester/components/suggester.js +43 -3
- package/src/components/suggester/idb-suggester.js +7 -1
- package/src/components/suggester/lunatic-suggester.js +1 -0
- package/src/components/suggester/suggester-wrapper.js +9 -3
- package/src/components/table/table.js +3 -1
- package/src/stories/loop-constructor/README.md +27 -27
- package/src/stories/loop-constructor/data-input-forced.json +64 -64
- package/src/stories/loop-constructor/data-input.json +100 -100
- package/src/stories/loop-constructor/data-loop-forced.json +66 -66
- package/src/stories/loop-constructor/data-loop-static-forced.json +66 -66
- package/src/stories/loop-constructor/data-loop-static.json +81 -81
- package/src/stories/loop-constructor/data-loop.json +81 -81
- package/src/stories/loop-constructor/data-roster-forced.json +68 -68
- package/src/stories/loop-constructor/data-roster.json +83 -83
- package/src/stories/loop-constructor/loop-constructor.stories.js +180 -180
- package/src/stories/questionnaire/arithmetic-management.json +47 -0
- package/src/stories/questionnaire/logement-queen.json +23389 -22705
- package/src/stories/questionnaire/logement-s2.json +46027 -44536
- package/src/stories/questionnaire/questionnaire.stories.js +12 -12
- package/src/stories/suggester/data.json +4 -1
- package/src/stories/suggester/suggester-workers.stories.js +4 -1
- package/src/stories/utils/orchestrator-split.js +119 -0
- package/src/stories/utils/orchestrator.js +4 -2
- package/src/tests/utils/to-expose/handler/results/res-input-edited.json +158 -158
- package/src/tests/utils/to-expose/state/state.spec.js +59 -59
- package/src/utils/lib/index.js +1 -0
- package/src/utils/lib/pagination/navigation/shared.js +5 -5
- package/src/utils/lib/splitting.js +142 -0
- package/src/utils/suggester-workers/commons-tokenizer/create-entity-tokenizer.js +4 -2
- package/src/utils/suggester-workers/commons-tokenizer/filters/{filter-accents-to-lower.js → filter-accents.js} +2 -2
- package/src/utils/suggester-workers/commons-tokenizer/filters/{filter-accents-to-lower.spec.js → filter-accents.spec.js} +1 -1
- package/src/utils/suggester-workers/commons-tokenizer/filters/filter-synonyms.js +27 -1
- package/src/utils/suggester-workers/commons-tokenizer/filters/filter-to-lower.js +10 -0
- package/src/utils/suggester-workers/commons-tokenizer/filters/filter-to-lower.spec.js +12 -0
- package/src/utils/suggester-workers/commons-tokenizer/index.js +1 -1
- package/src/utils/to-expose/handler.js +47 -28
- package/src/utils/to-expose/hooks/filter-components.js +106 -106
- package/src/utils/to-expose/hooks/index.js +2 -1
- package/src/utils/to-expose/hooks/lunatic-split.js +421 -0
- package/src/utils/to-expose/hooks/lunatic.js +23 -5
- package/src/utils/to-expose/index.js +11 -11
- package/src/utils/to-expose/state.js +23 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inseefr/lunatic",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5-experimental",
|
|
4
4
|
"workersVersion": "0.2.1-experimental",
|
|
5
5
|
"description": "Library of questionnaire components",
|
|
6
6
|
"repository": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"library"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@inseefr/trevas": "^0.1.
|
|
53
|
+
"@inseefr/trevas": "^0.1.12",
|
|
54
54
|
"date-fns": "^2.25.0",
|
|
55
55
|
"lodash.camelcase": "^4.3.0",
|
|
56
56
|
"lodash.debounce": "^4.0.8",
|
|
@@ -13,21 +13,32 @@ const getMessage = (min, max, value) => {
|
|
|
13
13
|
if (!value) {
|
|
14
14
|
return undefined;
|
|
15
15
|
}
|
|
16
|
-
const dateFormat = 'dd
|
|
16
|
+
const dateFormat = 'dd-MM-yyyy';
|
|
17
17
|
const date = new Date(value);
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
if (isDef(min) && isDef(max)) {
|
|
19
|
+
const minDate = new Date(min);
|
|
20
|
+
const maxDate = new Date(max);
|
|
21
|
+
if (date < minDate || date > maxDate) {
|
|
22
|
+
const minDateAsString = minDate ? format(minDate, dateFormat) : '';
|
|
23
|
+
const maxDateAsString = maxDate ? format(maxDate, dateFormat) : '';
|
|
24
|
+
return `La date doit être comprise entre le ${minDateAsString} et le ${maxDateAsString}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (isDef(min)) {
|
|
28
|
+
const minDate = new Date(min);
|
|
29
|
+
if (compareAsc(date, minDate) < 0) {
|
|
30
|
+
const minDateAsString = minDate ? format(minDate, dateFormat) : '';
|
|
31
|
+
return `La date doit être supérieure au ${minDateAsString}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (isDef(max)) {
|
|
35
|
+
const maxDate = new Date(max);
|
|
36
|
+
if (compareAsc(date, maxDate) > 0) {
|
|
37
|
+
const maxDateAsString = maxDate ? format(maxDate, dateFormat) : '';
|
|
38
|
+
return `La date doit être inférieure au ${maxDateAsString}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
30
41
|
return undefined;
|
|
31
42
|
};
|
|
32
43
|
|
|
33
|
-
const isDef = (d) => d;
|
|
44
|
+
const isDef = (d) => !isNaN(Date.parse(d));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import KeyboardEventHandler from 'react-keyboard-event-handler';
|
|
3
3
|
import Button from '../../button';
|
|
4
4
|
import * as U from '../../../utils/lib';
|
|
@@ -21,10 +21,33 @@ const Missing = ({ Component, props }) => {
|
|
|
21
21
|
bindings,
|
|
22
22
|
shortcut,
|
|
23
23
|
componentType,
|
|
24
|
-
|
|
24
|
+
paginatedLoop,
|
|
25
25
|
} = props;
|
|
26
26
|
|
|
27
|
+
const missingResponseName = U.getResponseName(missingResponse);
|
|
27
28
|
const buttonState = U.getResponseByPreference(preferences)(missingResponse);
|
|
29
|
+
const [oldMissingValue] = useState(() => buttonState);
|
|
30
|
+
|
|
31
|
+
const [bindingsForMissingStrategy, setBindingsForMissingStrategy] =
|
|
32
|
+
useState(null);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sources split: use MissingStragy only if missingResponse has been updated
|
|
36
|
+
* Ensures that missingResponse is persisted when the source has to be changed
|
|
37
|
+
*/
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const isSameValue = buttonState === oldMissingValue;
|
|
40
|
+
if (bindingsForMissingStrategy && !isSameValue) {
|
|
41
|
+
if (U.isFunction(missingStrategy))
|
|
42
|
+
missingStrategy(bindingsForMissingStrategy);
|
|
43
|
+
setBindingsForMissingStrategy(null);
|
|
44
|
+
}
|
|
45
|
+
}, [
|
|
46
|
+
bindingsForMissingStrategy,
|
|
47
|
+
missingStrategy,
|
|
48
|
+
buttonState,
|
|
49
|
+
oldMissingValue,
|
|
50
|
+
]);
|
|
28
51
|
|
|
29
52
|
useEffect(() => {
|
|
30
53
|
if (
|
|
@@ -36,7 +59,7 @@ const Missing = ({ Component, props }) => {
|
|
|
36
59
|
components,
|
|
37
60
|
})
|
|
38
61
|
) {
|
|
39
|
-
handleChange({ [
|
|
62
|
+
handleChange({ [missingResponseName]: null });
|
|
40
63
|
}
|
|
41
64
|
}, [
|
|
42
65
|
buttonState,
|
|
@@ -46,7 +69,7 @@ const Missing = ({ Component, props }) => {
|
|
|
46
69
|
responses,
|
|
47
70
|
cells,
|
|
48
71
|
components,
|
|
49
|
-
|
|
72
|
+
missingResponseName,
|
|
50
73
|
]);
|
|
51
74
|
|
|
52
75
|
const getVarsToClean = () =>
|
|
@@ -62,10 +85,9 @@ const Missing = ({ Component, props }) => {
|
|
|
62
85
|
if (!isSameValue) {
|
|
63
86
|
const toClean = getVarsToClean();
|
|
64
87
|
if (Object.keys(toClean)) {
|
|
65
|
-
const {
|
|
88
|
+
const { currentPage } = props;
|
|
66
89
|
const currentIterationIndex = getCurrentIterationIndex({
|
|
67
90
|
currentPage,
|
|
68
|
-
missingLoopIteration,
|
|
69
91
|
});
|
|
70
92
|
handleChange(toClean);
|
|
71
93
|
if (U.isFunction(missingStrategy)) {
|
|
@@ -81,20 +103,17 @@ const Missing = ({ Component, props }) => {
|
|
|
81
103
|
fullBindings,
|
|
82
104
|
toHandle,
|
|
83
105
|
});
|
|
84
|
-
|
|
106
|
+
setBindingsForMissingStrategy(missingBindings);
|
|
85
107
|
}
|
|
86
108
|
} else {
|
|
87
|
-
if (U.isFunction(missingStrategy))
|
|
109
|
+
if (U.isFunction(missingStrategy))
|
|
110
|
+
setBindingsForMissingStrategy(bindings);
|
|
88
111
|
}
|
|
89
|
-
handleChange({ [
|
|
112
|
+
handleChange({ [missingResponseName]: value });
|
|
90
113
|
}
|
|
91
114
|
};
|
|
92
115
|
|
|
93
|
-
if (
|
|
94
|
-
componentType === 'Loop' ||
|
|
95
|
-
missingLoopIteration ||
|
|
96
|
-
missingLoopIteration === 0
|
|
97
|
-
)
|
|
116
|
+
if ((componentType === 'Loop' && paginatedLoop) || !missingResponse)
|
|
98
117
|
return <Component {...props} />;
|
|
99
118
|
|
|
100
119
|
return (
|
|
@@ -111,6 +130,7 @@ const Missing = ({ Component, props }) => {
|
|
|
111
130
|
<Button
|
|
112
131
|
label="dont-know-button"
|
|
113
132
|
value={dontKnowButton}
|
|
133
|
+
disabled={!missingResponseName || missingResponseName?.length === 0}
|
|
114
134
|
onClick={onClick(U.DK)}
|
|
115
135
|
/>
|
|
116
136
|
</span>
|
|
@@ -122,11 +142,13 @@ const Missing = ({ Component, props }) => {
|
|
|
122
142
|
<Button
|
|
123
143
|
label="refused-button"
|
|
124
144
|
value={refusedButton}
|
|
145
|
+
disabled={!missingResponseName || missingResponseName?.length === 0}
|
|
125
146
|
onClick={onClick(U.RF)}
|
|
126
147
|
/>
|
|
127
148
|
</span>
|
|
128
149
|
</div>
|
|
129
150
|
{shortcut &&
|
|
151
|
+
missingResponseName?.length > 0 &&
|
|
130
152
|
missingShortcut &&
|
|
131
153
|
missingShortcut.dontKnow &&
|
|
132
154
|
missingShortcut.refused && (
|
|
@@ -147,11 +169,9 @@ const Missing = ({ Component, props }) => {
|
|
|
147
169
|
export default Missing;
|
|
148
170
|
|
|
149
171
|
// TODO: make it recursive for Loop into Loop
|
|
150
|
-
const getCurrentIterationIndex = ({ currentPage
|
|
172
|
+
const getCurrentIterationIndex = ({ currentPage }) => {
|
|
151
173
|
const { currentIteration } = U.splitPage(currentPage, 1);
|
|
152
174
|
if (currentIteration) return currentIteration - 1;
|
|
153
|
-
if (missingLoopIteration || missingLoopIteration === 0)
|
|
154
|
-
return missingLoopIteration;
|
|
155
175
|
return null;
|
|
156
176
|
};
|
|
157
177
|
|
|
@@ -6,18 +6,14 @@ import { areEqual } from '../../utils/lib';
|
|
|
6
6
|
import { getTypeControls } from '../component-wrapper/controls/validators';
|
|
7
7
|
import './datepicker.scss';
|
|
8
8
|
|
|
9
|
-
const Datepicker = (props) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
max={max || '1979-12-31'}
|
|
18
|
-
/>
|
|
19
|
-
);
|
|
20
|
-
};
|
|
9
|
+
const Datepicker = (props) => (
|
|
10
|
+
<InputDeclarationsWrapper
|
|
11
|
+
type="date"
|
|
12
|
+
roleType="datepicker"
|
|
13
|
+
{...props}
|
|
14
|
+
validators={[getTypeControls]}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
21
17
|
|
|
22
18
|
Datepicker.defaultProps = {
|
|
23
19
|
validators: [],
|
|
@@ -172,13 +172,25 @@ const InputDeclarationsWrapper = ({
|
|
|
172
172
|
aria-required={mandatory}
|
|
173
173
|
onChange={(e) => {
|
|
174
174
|
const v = e.target.value;
|
|
175
|
+
const valueToFire = v === '' ? null : v;
|
|
176
|
+
const valueToFireForArrows =
|
|
177
|
+
Number.parseFloat(v).toFixed(decimals);
|
|
175
178
|
if (
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
decimals &&
|
|
180
|
+
v !== '' &&
|
|
181
|
+
!new RegExp(`^[0-9]+(.[0-9]{1,${decimals}})?$`).test(
|
|
182
|
+
valueToFireForArrows
|
|
183
|
+
)
|
|
178
184
|
) {
|
|
179
|
-
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
} else if (
|
|
187
|
+
(([null, ''].includes(v) && value.length > 0) ||
|
|
188
|
+
([null, ''].includes(value) && v.length > 0)) &&
|
|
189
|
+
componentType !== 'Datepicker'
|
|
190
|
+
) {
|
|
191
|
+
setValue(valueToFire);
|
|
180
192
|
handleChange({
|
|
181
|
-
[U.getResponseName(response)]:
|
|
193
|
+
[U.getResponseName(response)]: valueToFire,
|
|
182
194
|
});
|
|
183
195
|
} else if (
|
|
184
196
|
// Chrome
|
|
@@ -186,11 +198,16 @@ const InputDeclarationsWrapper = ({
|
|
|
186
198
|
'Event' &&
|
|
187
199
|
roleType !== 'datepicker') ||
|
|
188
200
|
// FF hack: impossible to handle arrow events
|
|
189
|
-
(Math.abs(v - value)
|
|
201
|
+
(Math.abs(v - value).toFixed(decimals) !==
|
|
202
|
+
Number.parseFloat(`${Math.pow(10, -decimals)}`).toFixed(
|
|
203
|
+
decimals
|
|
204
|
+
) &&
|
|
205
|
+
!Number.parseInt(v, 10) &&
|
|
206
|
+
isInputNumber)
|
|
190
207
|
) {
|
|
191
|
-
setValue(
|
|
208
|
+
setValue(valueToFireForArrows);
|
|
192
209
|
handleChange({
|
|
193
|
-
[U.getResponseName(response)]:
|
|
210
|
+
[U.getResponseName(response)]: valueToFireForArrows,
|
|
194
211
|
});
|
|
195
212
|
} else {
|
|
196
213
|
if (isInputNumber) {
|
|
@@ -204,12 +221,17 @@ const InputDeclarationsWrapper = ({
|
|
|
204
221
|
return null;
|
|
205
222
|
} else validate(v);
|
|
206
223
|
}
|
|
207
|
-
if (management) setValue(
|
|
208
|
-
else setValue(
|
|
224
|
+
if (management) setValue(valueToFire);
|
|
225
|
+
else setValue(valueToFire);
|
|
209
226
|
}
|
|
210
227
|
}}
|
|
211
228
|
onBlur={handleChangeOnBlur}
|
|
212
229
|
onFocus={handleFocusIn}
|
|
230
|
+
onKeyPress={(event) => {
|
|
231
|
+
if (decimals === 0 && !/[0-9]/.test(event.key)) {
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
}
|
|
234
|
+
}}
|
|
213
235
|
/>
|
|
214
236
|
{isInputNumber && unit && unitPosition === 'AFTER' && (
|
|
215
237
|
<span className="unit">{unit}</span>
|
|
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import classnames from 'classnames';
|
|
4
4
|
import Label from './label';
|
|
5
5
|
import * as U from '../../../../utils/lib';
|
|
6
|
+
import * as C from '../../../../constants';
|
|
6
7
|
import * as CLEAN from '../cleaner-callbacks';
|
|
7
8
|
import * as actions from '../actions';
|
|
8
9
|
import DropdownField from './dropdown-field';
|
|
@@ -65,9 +66,19 @@ function Dropdown({
|
|
|
65
66
|
state,
|
|
66
67
|
dispatch,
|
|
67
68
|
refs,
|
|
69
|
+
logFunction,
|
|
68
70
|
}) {
|
|
69
71
|
const { visible, focused, id, disabled } = state;
|
|
70
72
|
|
|
73
|
+
const createEventFocus = (focusIn = true) =>
|
|
74
|
+
U.createObjectEvent(
|
|
75
|
+
id,
|
|
76
|
+
C.INPUT_CATEGORY,
|
|
77
|
+
focusIn ? C.EVENT_FOCUS_IN : C.EVENT_FOCUS_OUT,
|
|
78
|
+
U.getResponseName(response),
|
|
79
|
+
valueFromProps
|
|
80
|
+
);
|
|
81
|
+
|
|
71
82
|
CLEAN.add(id, function () {
|
|
72
83
|
dispatch(actions.hidePanel());
|
|
73
84
|
dispatch(actions.setFocused(false));
|
|
@@ -130,6 +141,16 @@ function Dropdown({
|
|
|
130
141
|
[state, dispatch, onSelect]
|
|
131
142
|
);
|
|
132
143
|
|
|
144
|
+
// log info when focus change
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (id && focused && U.isFunction(logFunction))
|
|
147
|
+
logFunction(createEventFocus());
|
|
148
|
+
if (id && !focused && U.isFunction(logFunction))
|
|
149
|
+
logFunction(createEventFocus(false));
|
|
150
|
+
|
|
151
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
152
|
+
}, [focused, id]);
|
|
153
|
+
|
|
133
154
|
return (
|
|
134
155
|
<DropdownContainer
|
|
135
156
|
className={classnames(className, U.getLabelPositionClass(labelPosition), {
|
|
@@ -37,6 +37,7 @@ const createOnSelect = (_, dispatch, onSelect) => (option) => {
|
|
|
37
37
|
* @param {props}
|
|
38
38
|
*/
|
|
39
39
|
function Dropdown({
|
|
40
|
+
id: initId,
|
|
40
41
|
widthAuto,
|
|
41
42
|
options = [],
|
|
42
43
|
onSelect,
|
|
@@ -52,10 +53,11 @@ function Dropdown({
|
|
|
52
53
|
disabled,
|
|
53
54
|
focused: initFocused,
|
|
54
55
|
DeclarationAfterLabel,
|
|
56
|
+
logFunction,
|
|
55
57
|
}) {
|
|
56
58
|
const [state, dispatch] = useReducer(reducer, {
|
|
57
59
|
...initial,
|
|
58
|
-
id: `dropdown-${new Date().getMilliseconds()}`,
|
|
60
|
+
id: `dropdown-${initId || new Date().getMilliseconds()}`,
|
|
59
61
|
disabled,
|
|
60
62
|
focused: initFocused,
|
|
61
63
|
});
|
|
@@ -98,6 +100,7 @@ function Dropdown({
|
|
|
98
100
|
value={valueFromProps}
|
|
99
101
|
zIndex={zIndex}
|
|
100
102
|
management={management}
|
|
103
|
+
logFunction={logFunction}
|
|
101
104
|
>
|
|
102
105
|
<DeclarationAfterLabel />
|
|
103
106
|
<span
|
|
@@ -26,11 +26,12 @@ const Dropdown = ({
|
|
|
26
26
|
className,
|
|
27
27
|
zIndex,
|
|
28
28
|
DeclarationAfterLabel,
|
|
29
|
+
logFunction,
|
|
29
30
|
}) => {
|
|
30
31
|
const containerEl = useRef();
|
|
31
32
|
const [state, dispatch] = useReducer(reducer, {
|
|
32
33
|
...initial,
|
|
33
|
-
id: `dropdown-${initId
|
|
34
|
+
id: `dropdown-${initId || new Date().getMilliseconds()}`,
|
|
34
35
|
disabled,
|
|
35
36
|
focused: initFocused,
|
|
36
37
|
});
|
|
@@ -59,6 +60,7 @@ const Dropdown = ({
|
|
|
59
60
|
onSelect={onSelect_}
|
|
60
61
|
value={value}
|
|
61
62
|
zIndex={zIndex}
|
|
63
|
+
logFunction={logFunction}
|
|
62
64
|
>
|
|
63
65
|
<DeclarationAfterLabel />
|
|
64
66
|
<span className={classnames('lunatic-dropdown-input', { focused })}>
|
|
@@ -6,11 +6,12 @@ import { areEqual } from '../../utils/lib';
|
|
|
6
6
|
import { getTypeControls } from '../component-wrapper/controls/validators';
|
|
7
7
|
import './input.scss';
|
|
8
8
|
|
|
9
|
-
const InputNumber = ({ numberAsTextfield, ...props }) => (
|
|
9
|
+
const InputNumber = ({ numberAsTextfield, decimals, ...props }) => (
|
|
10
10
|
<InputDeclarationsWrapper
|
|
11
11
|
type={numberAsTextfield ? 'text' : 'number'}
|
|
12
12
|
roleType="input"
|
|
13
13
|
{...props}
|
|
14
|
+
decimals={decimals || 0}
|
|
14
15
|
isInputNumber
|
|
15
16
|
numberAsTextfield
|
|
16
17
|
validators={[getTypeControls]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from './component';
|
|
1
|
+
export { default } from './component';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as RosterForLoop } from './roster';
|
|
1
|
+
export { default as RosterForLoop } from './roster';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from './component';
|
|
1
|
+
export { default } from './component';
|
|
@@ -100,11 +100,14 @@ const BodyComponent = ({
|
|
|
100
100
|
const Component = lunatic[componentType];
|
|
101
101
|
const localBindings =
|
|
102
102
|
U.buildBindingsForDeeperComponents(i)(bindings);
|
|
103
|
+
// ensure to have only N-1 missingResponse
|
|
104
|
+
const { missingResponse } = componentProps;
|
|
103
105
|
return (
|
|
104
106
|
<div className="block-component" key={`${id}-row-${i}`}>
|
|
105
107
|
<Component
|
|
106
108
|
{...otherProps}
|
|
107
109
|
{...componentProps}
|
|
110
|
+
missingResponse={missingResponse}
|
|
108
111
|
id={`${id}-row-${i}`}
|
|
109
112
|
label={label}
|
|
110
113
|
handleChange={(up) => {
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import * as C from '../../../constants';
|
|
2
|
-
|
|
3
|
-
export const buildContentForLoopConstructor = ({ components, headers }) => {
|
|
4
|
-
// Start hack to find interation number
|
|
5
|
-
// Refactor if we have to handle complex components (vector, matrix)
|
|
6
|
-
const iterations =
|
|
7
|
-
components.find((c) => c.response).response.values[C.COLLECTED].length || 1;
|
|
8
|
-
// End
|
|
9
|
-
const initialArray = [...Array(iterations).keys()];
|
|
10
|
-
const uiComponents = components.map((comp) => {
|
|
11
|
-
const { response, ...other } = comp;
|
|
12
|
-
if (!response) return initialArray.map(() => comp);
|
|
13
|
-
// Handle reponses & cells components ?
|
|
14
|
-
const { name, values } = response;
|
|
15
|
-
return initialArray.map((rowNumber) => {
|
|
16
|
-
const newValues = Object.entries(values).reduce(
|
|
17
|
-
(acc, [key, value]) => ({
|
|
18
|
-
...acc,
|
|
19
|
-
[key]: value ? value[rowNumber] : null,
|
|
20
|
-
}),
|
|
21
|
-
{}
|
|
22
|
-
);
|
|
23
|
-
return {
|
|
24
|
-
...other,
|
|
25
|
-
response: { name, values: newValues },
|
|
26
|
-
rowNumber,
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
}, []);
|
|
30
|
-
const transpose = (m) => m[0].map((_, i) => m.map((x) => x[i]));
|
|
31
|
-
const rows = transpose(uiComponents);
|
|
32
|
-
return headers ? [headers, ...rows] : rows;
|
|
33
|
-
};
|
|
1
|
+
import * as C from '../../../constants';
|
|
2
|
+
|
|
3
|
+
export const buildContentForLoopConstructor = ({ components, headers }) => {
|
|
4
|
+
// Start hack to find interation number
|
|
5
|
+
// Refactor if we have to handle complex components (vector, matrix)
|
|
6
|
+
const iterations =
|
|
7
|
+
components.find((c) => c.response).response.values[C.COLLECTED].length || 1;
|
|
8
|
+
// End
|
|
9
|
+
const initialArray = [...Array(iterations).keys()];
|
|
10
|
+
const uiComponents = components.map((comp) => {
|
|
11
|
+
const { response, ...other } = comp;
|
|
12
|
+
if (!response) return initialArray.map(() => comp);
|
|
13
|
+
// Handle reponses & cells components ?
|
|
14
|
+
const { name, values } = response;
|
|
15
|
+
return initialArray.map((rowNumber) => {
|
|
16
|
+
const newValues = Object.entries(values).reduce(
|
|
17
|
+
(acc, [key, value]) => ({
|
|
18
|
+
...acc,
|
|
19
|
+
[key]: value ? value[rowNumber] : null,
|
|
20
|
+
}),
|
|
21
|
+
{}
|
|
22
|
+
);
|
|
23
|
+
return {
|
|
24
|
+
...other,
|
|
25
|
+
response: { name, values: newValues },
|
|
26
|
+
rowNumber,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}, []);
|
|
30
|
+
const transpose = (m) => m[0].map((_, i) => m.map((x) => x[i]));
|
|
31
|
+
const rows = transpose(uiComponents);
|
|
32
|
+
return headers ? [headers, ...rows] : rows;
|
|
33
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from './component';
|
|
1
|
+
export { default } from './component';
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import React, { useRef, useCallback } from 'react';
|
|
2
|
-
import classnames from 'classnames';
|
|
3
|
-
import useDocumentAddEventListener from '../../../utils/to-expose/hooks/use-document-add-event-listener';
|
|
4
|
-
|
|
5
|
-
function SuggesterContent({
|
|
6
|
-
children,
|
|
7
|
-
id,
|
|
8
|
-
focused,
|
|
9
|
-
onFocus,
|
|
10
|
-
onBlur,
|
|
11
|
-
onKeyDown,
|
|
12
|
-
}) {
|
|
13
|
-
const ref = useRef();
|
|
14
|
-
const onClick = useCallback(
|
|
15
|
-
function (e) {
|
|
16
|
-
const { current } = ref;
|
|
17
|
-
if (!current.contains(e.target)) {
|
|
18
|
-
onBlur();
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
[ref, onBlur]
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
useDocumentAddEventListener('mousedown', onClick);
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div
|
|
28
|
-
className={classnames('lunatic-suggester', {
|
|
29
|
-
focused,
|
|
30
|
-
})}
|
|
31
|
-
onFocus={onFocus}
|
|
32
|
-
onKeyDown={onKeyDown}
|
|
33
|
-
ref={ref}
|
|
34
|
-
>
|
|
35
|
-
<div className={classnames('lunatic-suggester-content', { focused })}>
|
|
36
|
-
{children}
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export default SuggesterContent;
|
|
1
|
+
import React, { useRef, useCallback } from 'react';
|
|
2
|
+
import classnames from 'classnames';
|
|
3
|
+
import useDocumentAddEventListener from '../../../utils/to-expose/hooks/use-document-add-event-listener';
|
|
4
|
+
|
|
5
|
+
function SuggesterContent({
|
|
6
|
+
children,
|
|
7
|
+
id,
|
|
8
|
+
focused,
|
|
9
|
+
onFocus,
|
|
10
|
+
onBlur,
|
|
11
|
+
onKeyDown,
|
|
12
|
+
}) {
|
|
13
|
+
const ref = useRef();
|
|
14
|
+
const onClick = useCallback(
|
|
15
|
+
function (e) {
|
|
16
|
+
const { current } = ref;
|
|
17
|
+
if (!current.contains(e.target) && focused) {
|
|
18
|
+
onBlur();
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
[ref, focused, onBlur]
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
useDocumentAddEventListener('mousedown', onClick);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
className={classnames('lunatic-suggester', {
|
|
29
|
+
focused,
|
|
30
|
+
})}
|
|
31
|
+
onFocus={onFocus}
|
|
32
|
+
onKeyDown={onKeyDown}
|
|
33
|
+
ref={ref}
|
|
34
|
+
>
|
|
35
|
+
<div className={classnames('lunatic-suggester-content', { focused })}>
|
|
36
|
+
{children}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default SuggesterContent;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useContext,
|
|
4
|
+
useRef,
|
|
5
|
+
useMemo,
|
|
6
|
+
useEffect,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
2
9
|
import classnames from 'classnames';
|
|
3
10
|
import { actions, SuggesterContext } from '../state-management';
|
|
4
11
|
import SuggesterContent from './suggester-content';
|
|
@@ -6,6 +13,8 @@ import Selection from './selection';
|
|
|
6
13
|
import Panel from './panel';
|
|
7
14
|
import createOnKeyDownCallback from './create-on-keydown-callback';
|
|
8
15
|
import Delete from './selection/delete';
|
|
16
|
+
import * as C from '../../../constants';
|
|
17
|
+
import * as U from '../../../utils/lib';
|
|
9
18
|
import './suggester.scss';
|
|
10
19
|
|
|
11
20
|
function Suggester({
|
|
@@ -16,23 +25,54 @@ function Suggester({
|
|
|
16
25
|
labelRenderer,
|
|
17
26
|
onSelect,
|
|
18
27
|
value,
|
|
28
|
+
focused: initFocused,
|
|
29
|
+
response,
|
|
30
|
+
logFunction,
|
|
19
31
|
}) {
|
|
20
32
|
const inputEl = useRef();
|
|
21
33
|
const [state, dispatch] = useContext(SuggesterContext);
|
|
22
34
|
const { focused, id, messageError, search, disabled } = state;
|
|
23
35
|
|
|
36
|
+
const [init, setInit] = useState(false);
|
|
37
|
+
|
|
38
|
+
const createEventFocus = (focusIn = true) =>
|
|
39
|
+
U.createObjectEvent(
|
|
40
|
+
`suggester-${id}`,
|
|
41
|
+
C.INPUT_CATEGORY,
|
|
42
|
+
focusIn ? C.EVENT_FOCUS_IN : C.EVENT_FOCUS_OUT,
|
|
43
|
+
U.getResponseName(response),
|
|
44
|
+
value
|
|
45
|
+
);
|
|
46
|
+
|
|
24
47
|
const onFocus = useCallback(
|
|
25
48
|
function () {
|
|
26
|
-
if (!disabled) {
|
|
49
|
+
if (!focused && !disabled) {
|
|
27
50
|
if (inputEl.current !== document.activeElement) {
|
|
28
51
|
}
|
|
29
52
|
inputEl.current.focus();
|
|
30
53
|
dispatch(actions.onFocus());
|
|
31
54
|
}
|
|
32
55
|
},
|
|
33
|
-
[dispatch,
|
|
56
|
+
[disabled, dispatch, focused]
|
|
34
57
|
);
|
|
35
58
|
|
|
59
|
+
// Handle focused props of Component
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!init && id) {
|
|
62
|
+
if (initFocused && !focused) onFocus();
|
|
63
|
+
setInit(true);
|
|
64
|
+
}
|
|
65
|
+
}, [focused, init, initFocused, onFocus, id]);
|
|
66
|
+
|
|
67
|
+
// log info when focus change
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (init && id && focused && U.isFunction(logFunction))
|
|
70
|
+
logFunction(createEventFocus());
|
|
71
|
+
if (init && id && !focused && U.isFunction(logFunction))
|
|
72
|
+
logFunction(createEventFocus(false));
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
+
}, [focused, id, init]);
|
|
75
|
+
|
|
36
76
|
const onDelete = useCallback(
|
|
37
77
|
function () {
|
|
38
78
|
dispatch(actions.onDeleteSearch());
|