@transferwise/components 0.0.0-experimental-1f8ed05 → 0.0.0-experimental-d0bd4b4
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/build/index.esm.js +53 -109
- package/build/index.esm.js.map +1 -1
- package/build/index.js +54 -110
- package/build/index.js.map +1 -1
- package/build/types/common/textFormat/formatWithPattern/formatWithPattern.d.ts +1 -1
- package/build/types/common/textFormat/formatWithPattern/formatWithPattern.d.ts.map +1 -1
- package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts +2 -1
- package/build/types/common/textFormat/getCursorPositionAfterKeystroke/getCursorPositionAfterKeystroke.d.ts.map +1 -1
- package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts +5 -1
- package/build/types/common/textFormat/getSymbolsInPatternWithPosition/getSymbolsInPatternWithPosition.d.ts.map +1 -1
- package/build/types/common/textFormat/unformatWithPattern/unformatWithPattern.d.ts +1 -1
- package/build/types/common/textFormat/unformatWithPattern/unformatWithPattern.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +5 -11
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/index.d.ts +2 -1
- package/build/types/inputWithDisplayFormat/index.d.ts.map +1 -1
- package/build/types/inputs/Input.d.ts +8 -6
- package/build/types/inputs/Input.d.ts.map +1 -1
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts +5 -11
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
- package/build/types/textareaWithDisplayFormat/index.d.ts +2 -1
- package/build/types/textareaWithDisplayFormat/index.d.ts.map +1 -1
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts +55 -82
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/build/types/withDisplayFormat/index.d.ts +2 -1
- package/build/types/withDisplayFormat/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/textFormat/formatWithPattern/{formatWithPattern.js → formatWithPattern.ts} +8 -4
- package/src/common/textFormat/getCursorPositionAfterKeystroke/{getCursorPositionAfterKeystroke.js → getCursorPositionAfterKeystroke.ts} +7 -6
- package/src/common/textFormat/getSymbolsInPatternWithPosition/{getSymbolsInPatternWithPosition.js → getSymbolsInPatternWithPosition.ts} +7 -2
- package/src/common/textFormat/unformatWithPattern/{unformatWithPattern.js → unformatWithPattern.ts} +3 -2
- package/src/index.ts +2 -0
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +11 -0
- package/src/inputWithDisplayFormat/index.ts +2 -0
- package/src/inputs/Input.tsx +6 -11
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.spec.js +3 -1
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +32 -0
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +11 -0
- package/src/textareaWithDisplayFormat/index.ts +2 -0
- package/src/withDisplayFormat/{WithDisplayFormat.js → WithDisplayFormat.tsx} +111 -104
- package/src/withDisplayFormat/index.ts +2 -0
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.js +0 -14
- package/src/inputWithDisplayFormat/index.js +0 -1
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.js +0 -14
- package/src/textareaWithDisplayFormat/index.js +0 -1
- package/src/withDisplayFormat/index.js +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Component } from 'react';
|
|
1
|
+
import { Component, KeyboardEvent, ClipboardEvent, ChangeEvent, FocusEvent } from 'react';
|
|
3
2
|
|
|
4
3
|
import { HistoryNavigator } from '../common';
|
|
5
4
|
import {
|
|
@@ -10,14 +9,65 @@ import {
|
|
|
10
9
|
getDistanceToPreviousSymbol,
|
|
11
10
|
getDistanceToNextSymbol,
|
|
12
11
|
} from '../common/textFormat';
|
|
12
|
+
import { InputProps } from '../inputs/Input';
|
|
13
|
+
import { TextAreaProps } from '../inputs/TextArea';
|
|
14
|
+
import { Merge } from '../utils';
|
|
15
|
+
|
|
16
|
+
export type TextInputProps = Merge<
|
|
17
|
+
InputProps,
|
|
18
|
+
{
|
|
19
|
+
value?: string;
|
|
20
|
+
displayPattern: string;
|
|
21
|
+
/**
|
|
22
|
+
* autocomplete hides our form help so we need to disable it when help text
|
|
23
|
+
* is present. Chrome ignores autocomplete=off, the only way to disable it is
|
|
24
|
+
* to provide an 'invalid' value, for which 'disabled' serves.
|
|
25
|
+
*/
|
|
26
|
+
autoComplete?: InputProps['autoComplete'] | 'disabled';
|
|
27
|
+
onChange?: (value: string) => void;
|
|
28
|
+
onBlur?: (value: string) => void;
|
|
29
|
+
onFocus?: (value: string) => void;
|
|
30
|
+
}
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
export type Props = typeof WithDisplayFormat.defaultProps & {
|
|
34
|
+
render: (renderProps: InputProps | TextAreaProps) => JSX.Element;
|
|
35
|
+
} & TextInputProps;
|
|
36
|
+
|
|
37
|
+
export type EventType =
|
|
38
|
+
| 'KeyDown'
|
|
39
|
+
| 'Paste'
|
|
40
|
+
| 'Cut'
|
|
41
|
+
| 'Undo'
|
|
42
|
+
| 'Redo'
|
|
43
|
+
| 'Backspace'
|
|
44
|
+
| 'Delete'
|
|
45
|
+
| null;
|
|
46
|
+
|
|
47
|
+
type State = {
|
|
48
|
+
value: string;
|
|
49
|
+
historyNavigator: HistoryNavigator;
|
|
50
|
+
prevDisplayPattern?: string;
|
|
51
|
+
triggerType?: EventType;
|
|
52
|
+
triggerEvent?: KeyboardEvent<HTMLInputElement> | null;
|
|
53
|
+
pastedLength?: number;
|
|
54
|
+
selectionStart?: HTMLInputElement['selectionStart'];
|
|
55
|
+
selectionEnd?: HTMLInputElement['selectionEnd'];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
class WithDisplayFormat extends Component<Props, State> {
|
|
59
|
+
static defaultProps = {
|
|
60
|
+
autoComplete: 'off',
|
|
61
|
+
displayPattern: '',
|
|
62
|
+
type: 'text',
|
|
63
|
+
value: '',
|
|
64
|
+
};
|
|
13
65
|
|
|
14
|
-
|
|
15
|
-
constructor(props) {
|
|
66
|
+
constructor(props: Props) {
|
|
16
67
|
super(props);
|
|
17
|
-
const
|
|
18
|
-
const unformattedValue = unformatWithPattern(value, displayPattern);
|
|
68
|
+
const unformattedValue = unformatWithPattern(props.value, props.displayPattern);
|
|
19
69
|
this.state = {
|
|
20
|
-
value: formatWithPattern(unformattedValue, displayPattern),
|
|
70
|
+
value: formatWithPattern(unformattedValue, props.displayPattern),
|
|
21
71
|
historyNavigator: new HistoryNavigator(),
|
|
22
72
|
prevDisplayPattern: props.displayPattern,
|
|
23
73
|
triggerType: null,
|
|
@@ -25,12 +75,11 @@ class WithDisplayFormat extends Component {
|
|
|
25
75
|
};
|
|
26
76
|
}
|
|
27
77
|
|
|
28
|
-
static getDerivedStateFromProps(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
78
|
+
static getDerivedStateFromProps(
|
|
79
|
+
{ displayPattern }: Props,
|
|
80
|
+
{ prevDisplayPattern = displayPattern, value, historyNavigator }: State,
|
|
81
|
+
) {
|
|
82
|
+
if (prevDisplayPattern !== displayPattern) {
|
|
34
83
|
const unFormattedValue = unformatWithPattern(value, prevDisplayPattern);
|
|
35
84
|
historyNavigator.reset();
|
|
36
85
|
|
|
@@ -45,32 +94,37 @@ class WithDisplayFormat extends Component {
|
|
|
45
94
|
return null;
|
|
46
95
|
}
|
|
47
96
|
|
|
48
|
-
getUserAction = (unformattedValue) => {
|
|
97
|
+
getUserAction = (unformattedValue: string): EventType => {
|
|
49
98
|
const { triggerEvent, triggerType, value } = this.state;
|
|
50
99
|
const { displayPattern } = this.props;
|
|
51
100
|
|
|
52
|
-
|
|
101
|
+
if (triggerEvent) {
|
|
102
|
+
const charCode = String.fromCharCode(triggerEvent.which).toLowerCase();
|
|
53
103
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
104
|
+
if (triggerType === 'Paste' || triggerType === 'Cut') {
|
|
105
|
+
return triggerType;
|
|
106
|
+
}
|
|
57
107
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
108
|
+
if ((triggerEvent.ctrlKey || triggerEvent.metaKey) && charCode === 'z') {
|
|
109
|
+
return triggerEvent.shiftKey ? 'Redo' : 'Undo';
|
|
110
|
+
}
|
|
111
|
+
// Detect mouse event redo
|
|
112
|
+
if (triggerEvent.ctrlKey && charCode === 'd') {
|
|
113
|
+
return 'Delete';
|
|
114
|
+
}
|
|
65
115
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
// Android Fix.
|
|
117
|
+
if (
|
|
118
|
+
typeof triggerEvent.key === 'undefined' &&
|
|
119
|
+
unformattedValue.length <= unformatWithPattern(value, displayPattern).length
|
|
120
|
+
) {
|
|
69
121
|
return 'Backspace';
|
|
70
122
|
}
|
|
123
|
+
return triggerEvent.key as EventType;
|
|
124
|
+
} else {
|
|
125
|
+
// triggerEvent can be null only in case of "autofilling" (via password manager extension or browser build-in one) events
|
|
126
|
+
return 'Paste';
|
|
71
127
|
}
|
|
72
|
-
|
|
73
|
-
return triggerEvent.key;
|
|
74
128
|
};
|
|
75
129
|
|
|
76
130
|
resetEvent = () => {
|
|
@@ -81,7 +135,7 @@ class WithDisplayFormat extends Component {
|
|
|
81
135
|
});
|
|
82
136
|
};
|
|
83
137
|
|
|
84
|
-
detectUndoRedo = (event) => {
|
|
138
|
+
detectUndoRedo = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
85
139
|
const charCode = String.fromCharCode(event.which).toLowerCase();
|
|
86
140
|
if ((event.ctrlKey || event.metaKey) && charCode === 'z') {
|
|
87
141
|
return event.shiftKey ? 'Redo' : 'Undo';
|
|
@@ -89,9 +143,9 @@ class WithDisplayFormat extends Component {
|
|
|
89
143
|
return null;
|
|
90
144
|
};
|
|
91
145
|
|
|
92
|
-
handleOnKeyDown = (event) => {
|
|
146
|
+
handleOnKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
93
147
|
event.persist();
|
|
94
|
-
const { selectionStart, selectionEnd } = event.target;
|
|
148
|
+
const { selectionStart, selectionEnd } = event.target as HTMLInputElement;
|
|
95
149
|
const { historyNavigator } = this.state;
|
|
96
150
|
const { displayPattern } = this.props;
|
|
97
151
|
|
|
@@ -114,7 +168,7 @@ class WithDisplayFormat extends Component {
|
|
|
114
168
|
}
|
|
115
169
|
};
|
|
116
170
|
|
|
117
|
-
handleOnPaste = (event) => {
|
|
171
|
+
handleOnPaste = (event: ClipboardEvent<HTMLInputElement>) => {
|
|
118
172
|
const { displayPattern } = this.props;
|
|
119
173
|
const pastedLength = unformatWithPattern(
|
|
120
174
|
event.clipboardData.getData('Text'),
|
|
@@ -128,23 +182,19 @@ class WithDisplayFormat extends Component {
|
|
|
128
182
|
this.setState({ triggerType: 'Cut' });
|
|
129
183
|
};
|
|
130
184
|
|
|
131
|
-
isKeyAllowed = (action) => {
|
|
185
|
+
isKeyAllowed = (action: EventType) => {
|
|
132
186
|
const { displayPattern } = this.props;
|
|
133
187
|
const symbolsInPattern = displayPattern.split('').filter((character) => character !== '*');
|
|
134
188
|
|
|
135
|
-
return !symbolsInPattern.includes(action);
|
|
189
|
+
return !symbolsInPattern.includes(action as string);
|
|
136
190
|
};
|
|
137
191
|
|
|
138
|
-
handleOnChange = (event) => {
|
|
139
|
-
const { historyNavigator,
|
|
192
|
+
handleOnChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
193
|
+
const { historyNavigator, triggerType } = this.state;
|
|
140
194
|
const { displayPattern, onChange } = this.props;
|
|
141
195
|
const { value } = event.target;
|
|
142
196
|
let unformattedValue = unformatWithPattern(value, displayPattern);
|
|
143
|
-
const action =
|
|
144
|
-
triggerEvent === null
|
|
145
|
-
? // triggerEvent can be null only in case of "autofilling" (via password manager extension or browser build-in one) events
|
|
146
|
-
'Paste'
|
|
147
|
-
: this.getUserAction(unformattedValue);
|
|
197
|
+
const action = this.getUserAction(unformattedValue);
|
|
148
198
|
if (!this.isKeyAllowed(action) || triggerType === 'Undo' || triggerType === 'Redo') {
|
|
149
199
|
return;
|
|
150
200
|
}
|
|
@@ -158,19 +208,20 @@ class WithDisplayFormat extends Component {
|
|
|
158
208
|
|
|
159
209
|
this.handleCursorPositioning(action);
|
|
160
210
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
211
|
+
this.setState({ value: newFormattedValue }, () => {
|
|
212
|
+
this.resetEvent();
|
|
213
|
+
if (onChange) {
|
|
214
|
+
const broadcastValue = unformatWithPattern(newFormattedValue, displayPattern);
|
|
215
|
+
onChange(broadcastValue);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
164
218
|
};
|
|
165
219
|
|
|
166
|
-
handleOnBlur = (event) => {
|
|
167
|
-
|
|
168
|
-
if (onBlur) {
|
|
169
|
-
onBlur(unformatWithPattern(event.target.value, displayPattern));
|
|
170
|
-
}
|
|
220
|
+
handleOnBlur = (event: FocusEvent<HTMLInputElement>) => {
|
|
221
|
+
this.props.onBlur?.(unformatWithPattern(event.target.value, this.props.displayPattern));
|
|
171
222
|
};
|
|
172
223
|
|
|
173
|
-
handleOnFocus = (event) => {
|
|
224
|
+
handleOnFocus = (event: FocusEvent<HTMLInputElement>) => {
|
|
174
225
|
const { displayPattern, onFocus } = this.props;
|
|
175
226
|
if (onFocus) {
|
|
176
227
|
this.handleOnChange(event);
|
|
@@ -178,13 +229,14 @@ class WithDisplayFormat extends Component {
|
|
|
178
229
|
}
|
|
179
230
|
};
|
|
180
231
|
|
|
181
|
-
handleDelete = (unformattedValue, action) => {
|
|
232
|
+
handleDelete = (unformattedValue: string, action: EventType) => {
|
|
182
233
|
const { displayPattern } = this.props;
|
|
183
234
|
const { selectionStart, selectionEnd } = this.state;
|
|
184
235
|
const newStack = [...unformattedValue];
|
|
185
236
|
if (selectionStart === selectionEnd) {
|
|
186
237
|
let startPosition =
|
|
187
|
-
selectionStart
|
|
238
|
+
(selectionStart as number) -
|
|
239
|
+
getCountOfSymbolsInSelection(0, selectionStart, displayPattern);
|
|
188
240
|
let toDelete = 0;
|
|
189
241
|
|
|
190
242
|
let count = getDistanceToNextSymbol(selectionStart, displayPattern);
|
|
@@ -203,21 +255,21 @@ class WithDisplayFormat extends Component {
|
|
|
203
255
|
return newStack.join('');
|
|
204
256
|
};
|
|
205
257
|
|
|
206
|
-
handleCursorPositioning = (action) => {
|
|
258
|
+
handleCursorPositioning = (action: EventType) => {
|
|
207
259
|
const { displayPattern } = this.props;
|
|
208
260
|
const { triggerEvent, selectionStart, selectionEnd, pastedLength } = this.state;
|
|
209
261
|
|
|
210
262
|
const cursorPosition = getCursorPositionAfterKeystroke(
|
|
211
263
|
action,
|
|
212
|
-
selectionStart,
|
|
213
|
-
selectionEnd,
|
|
264
|
+
selectionStart as number,
|
|
265
|
+
selectionEnd as number,
|
|
214
266
|
displayPattern,
|
|
215
|
-
pastedLength,
|
|
267
|
+
pastedLength as number,
|
|
216
268
|
);
|
|
217
269
|
|
|
218
270
|
setTimeout(() => {
|
|
219
271
|
if (triggerEvent) {
|
|
220
|
-
triggerEvent.target.setSelectionRange(cursorPosition, cursorPosition);
|
|
272
|
+
(triggerEvent.target as HTMLInputElement).setSelectionRange(cursorPosition, cursorPosition);
|
|
221
273
|
}
|
|
222
274
|
this.setState({ selectionStart: cursorPosition, selectionEnd: cursorPosition });
|
|
223
275
|
}, 0);
|
|
@@ -264,49 +316,4 @@ class WithDisplayFormat extends Component {
|
|
|
264
316
|
}
|
|
265
317
|
}
|
|
266
318
|
|
|
267
|
-
WithDisplayFormat.propTypes = {
|
|
268
|
-
/**
|
|
269
|
-
* autocomplete hides our form help so we need to disable it when help text
|
|
270
|
-
* is present. Chrome ignores autocomplete=off, the only way to disable it is
|
|
271
|
-
* to provide an 'invalid' value, for which 'disabled' serves.
|
|
272
|
-
*/
|
|
273
|
-
autoComplete: PropTypes.oneOf(['on', 'off', 'disabled']),
|
|
274
|
-
className: PropTypes.string,
|
|
275
|
-
disabled: PropTypes.bool,
|
|
276
|
-
id: PropTypes.string,
|
|
277
|
-
maxLength: PropTypes.number,
|
|
278
|
-
minLength: PropTypes.number,
|
|
279
|
-
name: PropTypes.string,
|
|
280
|
-
onFocus: PropTypes.func,
|
|
281
|
-
onBlur: PropTypes.func,
|
|
282
|
-
onChange: PropTypes.func.isRequired,
|
|
283
|
-
placeholder: PropTypes.string,
|
|
284
|
-
readOnly: PropTypes.bool,
|
|
285
|
-
render: PropTypes.func.isRequired,
|
|
286
|
-
required: PropTypes.bool,
|
|
287
|
-
displayPattern: PropTypes.string,
|
|
288
|
-
type: PropTypes.string,
|
|
289
|
-
inputMode: PropTypes.string,
|
|
290
|
-
value: PropTypes.string,
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
WithDisplayFormat.defaultProps = {
|
|
294
|
-
autoComplete: 'off',
|
|
295
|
-
className: null,
|
|
296
|
-
disabled: false,
|
|
297
|
-
id: null,
|
|
298
|
-
maxLength: null,
|
|
299
|
-
minLength: null,
|
|
300
|
-
name: null,
|
|
301
|
-
placeholder: null,
|
|
302
|
-
readOnly: false,
|
|
303
|
-
required: false,
|
|
304
|
-
displayPattern: '',
|
|
305
|
-
type: 'text',
|
|
306
|
-
inputMode: null,
|
|
307
|
-
value: '',
|
|
308
|
-
onFocus: null,
|
|
309
|
-
onBlur: null,
|
|
310
|
-
};
|
|
311
|
-
|
|
312
319
|
export default WithDisplayFormat;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types';
|
|
2
|
-
|
|
3
|
-
import WithDisplayFormat from '../withDisplayFormat';
|
|
4
|
-
|
|
5
|
-
const InputWithDisplayFormat = (props) => (
|
|
6
|
-
<WithDisplayFormat {...props} render={(renderProps) => <input {...renderProps} />} />
|
|
7
|
-
);
|
|
8
|
-
|
|
9
|
-
InputWithDisplayFormat.propTypes = {
|
|
10
|
-
displayPattern: PropTypes.string.isRequired,
|
|
11
|
-
onChange: PropTypes.func.isRequired,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default InputWithDisplayFormat;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './InputWithDisplayFormat';
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types';
|
|
2
|
-
|
|
3
|
-
import WithDisplayFormat from '../withDisplayFormat';
|
|
4
|
-
|
|
5
|
-
const TextareaWithDisplayFormat = (props) => (
|
|
6
|
-
<WithDisplayFormat {...props} render={(renderProps) => <textarea {...renderProps} />} />
|
|
7
|
-
);
|
|
8
|
-
|
|
9
|
-
TextareaWithDisplayFormat.propTypes = {
|
|
10
|
-
displayPattern: PropTypes.string.isRequired,
|
|
11
|
-
onChange: PropTypes.func.isRequired,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default TextareaWithDisplayFormat;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './TextareaWithDisplayFormat';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './WithDisplayFormat';
|