@vaadin/field-base 22.0.0-alpha1 → 22.0.0-alpha10

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 (67) hide show
  1. package/index.d.ts +10 -9
  2. package/index.js +10 -9
  3. package/package.json +25 -19
  4. package/src/aria-label-controller.d.ts +11 -0
  5. package/src/aria-label-controller.js +58 -0
  6. package/src/checked-mixin.d.ts +26 -0
  7. package/src/checked-mixin.js +54 -0
  8. package/src/delegate-focus-mixin.d.ts +7 -4
  9. package/src/delegate-focus-mixin.js +154 -72
  10. package/src/delegate-state-mixin.d.ts +23 -0
  11. package/src/delegate-state-mixin.js +125 -0
  12. package/src/field-mixin.d.ts +39 -0
  13. package/src/field-mixin.js +317 -0
  14. package/src/input-constraints-mixin.d.ts +28 -0
  15. package/src/input-constraints-mixin.js +126 -0
  16. package/src/input-control-mixin.d.ts +52 -0
  17. package/src/input-control-mixin.js +170 -0
  18. package/src/input-controller.d.ts +11 -0
  19. package/src/input-controller.js +35 -0
  20. package/src/input-field-mixin.d.ts +2 -22
  21. package/src/input-field-mixin.js +117 -168
  22. package/src/input-mixin.d.ts +22 -6
  23. package/src/input-mixin.js +161 -51
  24. package/src/label-mixin.d.ts +2 -2
  25. package/src/label-mixin.js +74 -60
  26. package/src/pattern-mixin.d.ts +32 -0
  27. package/src/pattern-mixin.js +72 -0
  28. package/src/shadow-focus-mixin.d.ts +21 -0
  29. package/src/shadow-focus-mixin.js +87 -0
  30. package/src/slot-controller.d.ts +8 -0
  31. package/src/slot-controller.js +36 -0
  32. package/src/slot-label-mixin.d.ts +20 -0
  33. package/src/slot-label-mixin.js +38 -0
  34. package/src/slot-styles-mixin.d.ts +24 -0
  35. package/src/slot-styles-mixin.js +76 -0
  36. package/src/slot-target-mixin.d.ts +32 -0
  37. package/src/slot-target-mixin.js +110 -0
  38. package/src/styles/clear-button-styles.d.ts +8 -0
  39. package/src/styles/clear-button-styles.js +21 -0
  40. package/src/styles/field-shared-styles.d.ts +8 -0
  41. package/src/styles/field-shared-styles.js +29 -0
  42. package/src/styles/input-field-container-styles.d.ts +8 -0
  43. package/src/styles/input-field-container-styles.js +16 -0
  44. package/src/styles/input-field-shared-styles.d.ts +8 -0
  45. package/src/styles/input-field-shared-styles.js +10 -0
  46. package/src/text-area-controller.d.ts +11 -0
  47. package/src/text-area-controller.js +38 -0
  48. package/src/validate-mixin.d.ts +1 -9
  49. package/src/validate-mixin.js +43 -118
  50. package/src/clear-button-mixin.d.ts +0 -32
  51. package/src/clear-button-mixin.js +0 -87
  52. package/src/disabled-mixin.d.ts +0 -23
  53. package/src/disabled-mixin.js +0 -48
  54. package/src/field-aria-mixin.d.ts +0 -24
  55. package/src/field-aria-mixin.js +0 -61
  56. package/src/focus-mixin.d.ts +0 -33
  57. package/src/focus-mixin.js +0 -104
  58. package/src/helper-text-mixin.d.ts +0 -24
  59. package/src/helper-text-mixin.js +0 -109
  60. package/src/input-aria-mixin.d.ts +0 -20
  61. package/src/input-aria-mixin.js +0 -69
  62. package/src/input-props-mixin.d.ts +0 -40
  63. package/src/input-props-mixin.js +0 -106
  64. package/src/slot-mixin.d.ts +0 -23
  65. package/src/slot-mixin.js +0 -55
  66. package/src/text-field-mixin.d.ts +0 -47
  67. package/src/text-field-mixin.js +0 -125
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
7
+ import { DelegateFocusMixin } from './delegate-focus-mixin.js';
8
+ import { FieldMixin } from './field-mixin.js';
9
+ import { InputConstraintsMixin } from './input-constraints-mixin.js';
10
+
11
+ /**
12
+ * A mixin to provide shared logic for the editable form input controls.
13
+ *
14
+ * @polymerMixin
15
+ * @mixes DelegateFocusMixin
16
+ * @mixes FieldMixin
17
+ * @mixes InputConstraintsMixin
18
+ * @mixes KeyboardMixin
19
+ */
20
+ export const InputControlMixin = (superclass) =>
21
+ class InputControlMixinClass extends DelegateFocusMixin(
22
+ InputConstraintsMixin(FieldMixin(KeyboardMixin(superclass)))
23
+ ) {
24
+ static get properties() {
25
+ return {
26
+ /**
27
+ * If true, the input text gets fully selected when the field is focused using click or touch / tap.
28
+ */
29
+ autoselect: {
30
+ type: Boolean,
31
+ value: false
32
+ },
33
+
34
+ /**
35
+ * Set to true to display the clear icon which clears the input.
36
+ * @attr {boolean} clear-button-visible
37
+ */
38
+ clearButtonVisible: {
39
+ type: Boolean,
40
+ reflectToAttribute: true,
41
+ value: false
42
+ },
43
+
44
+ /**
45
+ * The name of this field.
46
+ */
47
+ name: {
48
+ type: String,
49
+ reflectToAttribute: true
50
+ },
51
+
52
+ /**
53
+ * A hint to the user of what can be entered in the field.
54
+ */
55
+ placeholder: {
56
+ type: String,
57
+ reflectToAttribute: true
58
+ },
59
+
60
+ /**
61
+ * When present, it specifies that the field is read-only.
62
+ */
63
+ readonly: {
64
+ type: Boolean,
65
+ value: false,
66
+ reflectToAttribute: true
67
+ },
68
+
69
+ /**
70
+ * The text usually displayed in a tooltip popup when the mouse is over the field.
71
+ */
72
+ title: {
73
+ type: String,
74
+ reflectToAttribute: true
75
+ }
76
+ };
77
+ }
78
+
79
+ static get delegateAttrs() {
80
+ return [...super.delegateAttrs, 'name', 'type', 'placeholder', 'readonly', 'invalid', 'title'];
81
+ }
82
+
83
+ /**
84
+ * Any element extending this mixin is required to implement this getter.
85
+ * It returns the reference to the clear button element.
86
+ * @protected
87
+ * @return {Element | null | undefined}
88
+ */
89
+ get clearElement() {
90
+ console.warn(`Please implement the 'clearElement' property in <${this.localName}>`);
91
+ return null;
92
+ }
93
+
94
+ /** @protected */
95
+ ready() {
96
+ super.ready();
97
+
98
+ if (this.clearElement) {
99
+ this.clearElement.addEventListener('click', (e) => this._onClearButtonClick(e));
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param {Event} event
105
+ * @protected
106
+ */
107
+ _onClearButtonClick(event) {
108
+ event.preventDefault();
109
+ this.inputElement.focus();
110
+ this.clear();
111
+ this.inputElement.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
112
+ this.inputElement.dispatchEvent(new Event('change', { bubbles: true }));
113
+ }
114
+
115
+ /**
116
+ * Override an event listener from `DelegateFocusMixin`.
117
+ * @param {FocusEvent} event
118
+ * @protected
119
+ * @override
120
+ */
121
+ _onFocus(event) {
122
+ super._onFocus(event);
123
+
124
+ if (this.autoselect && this.inputElement) {
125
+ this.inputElement.select();
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Override an event listener inherited from `KeydownMixin` to clear on Esc.
131
+ * Components that extend this mixin can prevent this behavior by overriding
132
+ * this method without calling `super._onKeyDown` to provide custom logic.
133
+ * @param {KeyboardEvent} event
134
+ * @protected
135
+ * @override
136
+ */
137
+ _onKeyDown(event) {
138
+ super._onKeyDown(event);
139
+
140
+ if (event.key === 'Escape' && this.clearButtonVisible) {
141
+ const dispatchChange = !!this.value;
142
+ this.clear();
143
+ dispatchChange && this.inputElement.dispatchEvent(new Event('change', { bubbles: true }));
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Override an event listener inherited from `InputMixin`
149
+ * to capture native `change` event and make sure that
150
+ * a new one is dispatched after validation runs.
151
+ * @param {Event} event
152
+ * @protected
153
+ * @override
154
+ */
155
+ _onChange(event) {
156
+ event.stopPropagation();
157
+
158
+ this.validate();
159
+
160
+ this.dispatchEvent(
161
+ new CustomEvent('change', {
162
+ detail: {
163
+ sourceEvent: event
164
+ },
165
+ bubbles: event.bubbles,
166
+ cancelable: event.cancelable
167
+ })
168
+ );
169
+ }
170
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { SlotController } from './slot-controller.js';
7
+
8
+ /**
9
+ * A controller to create and initialize slotted `<input>` element.
10
+ */
11
+ export class InputController implements SlotController {}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { SlotController } from './slot-controller.js';
7
+
8
+ /**
9
+ * A controller to create and initialize slotted `<input>` element.
10
+ */
11
+ export class InputController extends SlotController {
12
+ constructor(host, callback) {
13
+ super(host, [
14
+ 'input',
15
+ () => document.createElement('input'),
16
+ (host, node) => {
17
+ if (host.value) {
18
+ node.setAttribute('value', host.value);
19
+ }
20
+ if (host.type) {
21
+ node.setAttribute('type', host.type);
22
+ }
23
+
24
+ // Ensure every instance has unique ID
25
+ const uniqueId = (InputController._uniqueInputId = 1 + InputController._uniqueInputId || 0);
26
+ host._inputId = `${host.localName}-${uniqueId}`;
27
+ node.id = host._inputId;
28
+
29
+ if (typeof callback == 'function') {
30
+ callback(node);
31
+ }
32
+ }
33
+ ]);
34
+ }
35
+ }
@@ -3,10 +3,7 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { ClearButtonMixin } from './clear-button-mixin.js';
7
- import { DelegateFocusMixin } from './delegate-focus-mixin.js';
8
- import { FieldAriaMixin } from './field-aria-mixin.js';
9
- import { InputPropsMixin } from './input-props-mixin.js';
6
+ import { InputControlMixin } from './input-control-mixin.js';
10
7
 
11
8
  /**
12
9
  * A mixin to provide logic for vaadin-text-field and related components.
@@ -17,9 +14,7 @@ interface InputFieldMixinConstructor {
17
14
  new (...args: any[]): InputFieldMixin;
18
15
  }
19
16
 
20
- interface InputFieldMixin extends ClearButtonMixin, DelegateFocusMixin, FieldAriaMixin, InputPropsMixin {
21
- readonly inputElement: HTMLElement | undefined;
22
-
17
+ interface InputFieldMixin extends InputControlMixin {
23
18
  /**
24
19
  * Whether the value of the control can be automatically completed by the browser.
25
20
  * List of available options at:
@@ -46,21 +41,6 @@ interface InputFieldMixin extends ClearButtonMixin, DelegateFocusMixin, FieldAri
46
41
  * none: No capitalization.
47
42
  */
48
43
  autocapitalize: 'on' | 'off' | 'none' | 'characters' | 'words' | 'sentences' | undefined;
49
-
50
- /**
51
- * Specify that the value should be automatically selected when the field gains focus.
52
- */
53
- autoselect: boolean;
54
-
55
- /**
56
- * The value of the field.
57
- */
58
- value: string;
59
-
60
- /**
61
- * Returns true if the current input value satisfies all constraints (if any).
62
- */
63
- checkValidity(): boolean;
64
44
  }
65
45
 
66
46
  export { InputFieldMixin, InputFieldMixinConstructor };
@@ -3,16 +3,16 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
7
- import { animationFrame } from '@polymer/polymer/lib/utils/async.js';
8
- import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
9
- import { ClearButtonMixin } from './clear-button-mixin.js';
10
- import { DelegateFocusMixin } from './delegate-focus-mixin.js';
11
- import { FieldAriaMixin } from './field-aria-mixin.js';
12
- import { InputPropsMixin } from './input-props-mixin.js';
6
+ import { InputControlMixin } from './input-control-mixin.js';
13
7
 
14
- const InputFieldMixinImplementation = (superclass) =>
15
- class InputFieldMixinClass extends ClearButtonMixin(FieldAriaMixin(InputPropsMixin(DelegateFocusMixin(superclass)))) {
8
+ /**
9
+ * A mixin to provide logic for vaadin-text-field and related components.
10
+ *
11
+ * @polymerMixin
12
+ * @mixes InputControlMixin
13
+ */
14
+ export const InputFieldMixin = (superclass) =>
15
+ class InputFieldMixinClass extends InputControlMixin(superclass) {
16
16
  static get properties() {
17
17
  return {
18
18
  /**
@@ -49,112 +49,56 @@ const InputFieldMixinImplementation = (superclass) =>
49
49
  },
50
50
 
51
51
  /**
52
- * Specify that the value should be automatically selected when the field gains focus.
52
+ * A pattern matched against individual characters the user inputs.
53
+ * When set, the field will prevent:
54
+ * - `keyDown` events if the entered key doesn't match `/^_enabledCharPattern$/`
55
+ * - `paste` events if the pasted text doesn't match `/^_enabledCharPattern*$/`
56
+ * - `drop` events if the dropped text doesn't match `/^_enabledCharPattern*$/`
57
+ *
58
+ * For example, to enable entering only numbers and minus signs,
59
+ * `_enabledCharPattern = "[\\d-]"`
60
+ * @protected
53
61
  */
54
- autoselect: {
55
- type: Boolean,
56
- value: false
57
- },
58
-
59
- /**
60
- * The value of the field.
61
- */
62
- value: {
62
+ _enabledCharPattern: {
63
63
  type: String,
64
- value: '',
65
- observer: '_valueChanged',
66
- notify: true
64
+ observer: '_enabledCharPatternChanged'
67
65
  }
68
66
  };
69
67
  }
70
68
 
71
- static get hostProps() {
72
- return [...super.hostProps, 'autocapitalize', 'autocomplete', 'autocorrect'];
73
- }
74
-
75
- static get observers() {
76
- return ['__observeOffsetHeight(errorMessage, invalid, label, helperText)'];
77
- }
78
-
79
- /**
80
- * Element used by `FieldAriaMixin` to set ARIA attributes.
81
- * @protected
82
- */
83
- get _ariaTarget() {
84
- return this._inputNode;
85
- }
86
-
87
- /**
88
- * Element used by `DelegatesFocusMixin` to handle focus.
89
- * @return {!HTMLInputElement}
90
- */
91
- get focusElement() {
92
- return this._inputNode;
69
+ static get delegateAttrs() {
70
+ return [...super.delegateAttrs, 'autocapitalize', 'autocomplete', 'autocorrect'];
93
71
  }
94
72
 
95
73
  constructor() {
96
74
  super();
97
75
 
98
- this._boundOnInput = this._onInput.bind(this);
99
- this._boundOnBlur = this._onBlur.bind(this);
100
- this._boundOnFocus = this._onFocus.bind(this);
76
+ this._boundOnPaste = this._onPaste.bind(this);
77
+ this._boundOnDrop = this._onDrop.bind(this);
78
+ this._boundOnBeforeInput = this._onBeforeInput.bind(this);
101
79
  }
102
80
 
103
- /** @protected */
104
- connectedCallback() {
105
- super.connectedCallback();
106
-
107
- if (this._inputNode) {
108
- this._addInputListeners(this._inputNode);
81
+ /**
82
+ * @param {HTMLElement} input
83
+ * @protected
84
+ * @override
85
+ */
86
+ _inputElementChanged(input) {
87
+ super._inputElementChanged(input);
109
88
 
89
+ if (input) {
110
90
  // Discard value set on the custom slotted input.
111
- if (this._inputNode.value !== this.value) {
91
+ if (input.value && input.value !== this.value) {
112
92
  console.warn(`Please define value on the <${this.localName}> component!`);
113
- this._inputNode.value = '';
93
+ input.value = '';
114
94
  }
115
95
 
116
96
  if (this.value) {
117
- this._inputNode.value = this.value;
118
- this.validate();
97
+ input.value = this.value;
119
98
  }
120
99
  }
121
100
  }
122
101
 
123
- /** @protected */
124
- disconnectedCallback() {
125
- super.disconnectedCallback();
126
-
127
- if (this._inputNode) {
128
- this._removeInputListeners(this._inputNode);
129
- }
130
- }
131
-
132
- /** @protected */
133
- ready() {
134
- super.ready();
135
-
136
- // Lumo theme defines a max-height transition for the "error-message"
137
- // part on invalid state change.
138
- const errorPart = this.shadowRoot.querySelector('[part="error-message"]');
139
- if (errorPart) {
140
- errorPart.addEventListener('transitionend', () => {
141
- this.__observeOffsetHeight();
142
- });
143
- }
144
- }
145
-
146
- /**
147
- * Returns true if the current input value satisfies all constraints (if any).
148
- * @return {boolean}
149
- */
150
- checkValidity() {
151
- if (this.required) {
152
- return this._inputNode ? this._inputNode.checkValidity() : undefined;
153
- } else {
154
- return !this.invalid;
155
- }
156
- }
157
-
158
102
  // Workaround for https://github.com/Polymer/polymer/issues/5259
159
103
  get __data() {
160
104
  return this.__dataValue || {};
@@ -165,117 +109,122 @@ const InputFieldMixinImplementation = (superclass) =>
165
109
  }
166
110
 
167
111
  /**
168
- * @param {HTMLElement} node
112
+ * Override an event listener from `DelegateFocusMixin`.
113
+ * @param {FocusEvent} event
169
114
  * @protected
115
+ * @override
170
116
  */
171
- _addInputListeners(node) {
172
- node.addEventListener('input', this._boundOnInput);
173
- node.addEventListener('blur', this._boundOnBlur);
174
- node.addEventListener('focus', this._boundOnFocus);
117
+ _onBlur(event) {
118
+ super._onBlur(event);
119
+
120
+ this.validate();
175
121
  }
176
122
 
177
123
  /**
178
- * @param {HTMLElement} node
124
+ * Override a method from `InputMixin` to validate the field
125
+ * when a new value is set programmatically.
126
+ * @param {string} value
179
127
  * @protected
128
+ * @override
180
129
  */
181
- _removeInputListeners(node) {
182
- node.removeEventListener('input', this._boundOnInput);
183
- node.removeEventListener('blur', this._boundOnBlur);
184
- node.removeEventListener('focus', this._boundOnFocus);
185
- }
130
+ _forwardInputValue(value) {
131
+ super._forwardInputValue(value);
186
132
 
187
- /** @private */
188
- _onFocus() {
189
- if (this.autoselect && this._inputNode) {
190
- this._inputNode.select();
133
+ if (this.invalid) {
134
+ this.validate();
191
135
  }
192
136
  }
193
137
 
194
- /** @private */
195
- _onBlur() {
196
- this.validate();
138
+ /**
139
+ * Override a method from `InputMixin`.
140
+ * @param {!HTMLElement} input
141
+ * @protected
142
+ * @override
143
+ */
144
+ _addInputListeners(input) {
145
+ super._addInputListeners(input);
146
+
147
+ input.addEventListener('paste', this._boundOnPaste);
148
+ input.addEventListener('drop', this._boundOnDrop);
149
+ input.addEventListener('beforeinput', this._boundOnBeforeInput);
197
150
  }
198
151
 
199
152
  /**
200
- * @param {Event} event
153
+ * Override a method from `InputMixin`.
154
+ * @param {!HTMLElement} input
201
155
  * @protected
156
+ * @override
202
157
  */
203
- _onInput(event) {
204
- // Ignore manual clear button events
205
- this.__userInput = event.isTrusted;
206
- this.value = event.target.value;
207
- this.__userInput = false;
158
+ _removeInputListeners(input) {
159
+ super._removeInputListeners(input);
160
+
161
+ input.removeEventListener('paste', this._boundOnPaste);
162
+ input.removeEventListener('drop', this._boundOnDrop);
163
+ input.removeEventListener('beforeinput', this._boundOnBeforeInput);
208
164
  }
209
165
 
210
166
  /**
211
- * Dispatch an event if a specific size measurement property has changed.
212
- * Supporting multiple properties here is needed for `vaadin-text-area`.
167
+ * Override an event listener from `ClearButtonMixin`
168
+ * to avoid adding a separate listener.
169
+ * @param {!KeyboardEvent} event
213
170
  * @protected
171
+ * @override
214
172
  */
215
- _dispatchIronResizeEventIfNeeded(prop, value) {
216
- const oldSize = '__old' + prop;
217
- if (this[oldSize] !== undefined && this[oldSize] !== value) {
218
- this.dispatchEvent(new CustomEvent('iron-resize', { bubbles: true, composed: true }));
173
+ _onKeyDown(event) {
174
+ if (this._enabledCharPattern && !this.__shouldAcceptKey(event)) {
175
+ event.preventDefault();
219
176
  }
220
177
 
221
- this[oldSize] = value;
178
+ super._onKeyDown(event);
222
179
  }
223
180
 
224
181
  /** @private */
225
- __observeOffsetHeight() {
226
- this.__observeOffsetHeightDebouncer = Debouncer.debounce(
227
- this.__observeOffsetHeightDebouncer,
228
- animationFrame,
229
- () => {
230
- this._dispatchIronResizeEventIfNeeded('Height', this.offsetHeight);
231
- }
182
+ __shouldAcceptKey(event) {
183
+ return (
184
+ event.metaKey ||
185
+ event.ctrlKey ||
186
+ !event.key || // allow typing anything if event.key is not supported
187
+ event.key.length !== 1 || // allow "Backspace", "ArrowLeft" etc.
188
+ this.__enabledCharRegExp.test(event.key)
232
189
  );
233
190
  }
234
191
 
235
- /**
236
- * @param {unknown} newVal
237
- * @param {unknown} oldVal
238
- * @protected
239
- */
240
- _valueChanged(newVal, oldVal) {
241
- // Setting initial value to empty string, skip validation
242
- if (newVal === '' && oldVal === undefined) {
243
- return;
244
- }
245
-
246
- if (newVal !== '' && newVal != null) {
247
- this.setAttribute('has-value', '');
248
- } else {
249
- this.removeAttribute('has-value');
250
- }
251
-
252
- // Value is set before an element is connected to the DOM:
253
- // this case is handled separately in `connectedCallback`.
254
- if (!this._inputNode) {
255
- return;
192
+ /** @private */
193
+ _onPaste(e) {
194
+ if (this._enabledCharPattern) {
195
+ const pastedText = (e.clipboardData || window.clipboardData).getData('text');
196
+ if (!this.__enabledTextRegExp.test(pastedText)) {
197
+ e.preventDefault();
198
+ }
256
199
  }
200
+ }
257
201
 
258
- // Value is set by the user, no need to sync it back to input.
259
- // Also no need to validate, as we call `validate` on blur.
260
- if (this.__userInput) {
261
- return;
202
+ /** @private */
203
+ _onDrop(e) {
204
+ if (this._enabledCharPattern) {
205
+ const draggedText = e.dataTransfer.getData('text');
206
+ if (!this.__enabledTextRegExp.test(draggedText)) {
207
+ e.preventDefault();
208
+ }
262
209
  }
210
+ }
263
211
 
264
- // Setting a value programmatically, sync it to input element.
265
- if (newVal != undefined) {
266
- this._inputNode.value = newVal;
267
- } else {
268
- this.clear();
212
+ /** @private */
213
+ _onBeforeInput(e) {
214
+ // The `beforeinput` event covers all the cases for `_enabledCharPattern`: keyboard, pasting and dropping,
215
+ // but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
216
+ // because it seems to be the only way to detect and prevent specific keys on mobile devices.
217
+ // See https://github.com/vaadin/vaadin-text-field/issues/429
218
+ if (this._enabledCharPattern && e.data && !this.__enabledTextRegExp.test(e.data)) {
219
+ e.preventDefault();
269
220
  }
221
+ }
270
222
 
271
- // Validate the field after a new value is set programmatically.
272
- if (this.invalid) {
273
- this.validate();
223
+ /** @private */
224
+ _enabledCharPatternChanged(charPattern) {
225
+ if (charPattern) {
226
+ this.__enabledCharRegExp = new RegExp('^' + charPattern + '$');
227
+ this.__enabledTextRegExp = new RegExp('^' + charPattern + '*$');
274
228
  }
275
229
  }
276
230
  };
277
-
278
- /**
279
- * A mixin to provide logic for vaadin-text-field and related components.
280
- */
281
- export const InputFieldMixin = dedupingMixin(InputFieldMixinImplementation);
@@ -3,10 +3,10 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { SlotMixin } from './slot-mixin.js';
7
6
 
8
7
  /**
9
- * A mixin to add `<input>` element to the corresponding named slot.
8
+ * A mixin to store the reference to an input element
9
+ * and add input and change event listeners to it.
10
10
  */
11
11
  declare function InputMixin<T extends new (...args: any[]) => {}>(base: T): T & InputMixinConstructor;
12
12
 
@@ -14,11 +14,27 @@ interface InputMixinConstructor {
14
14
  new (...args: any[]): InputMixin;
15
15
  }
16
16
 
17
- interface InputMixin extends SlotMixin {
17
+ interface InputMixin {
18
18
  /**
19
- * String used to define input type.
19
+ * A reference to the input element controlled by the mixin.
20
+ * Any component implementing this mixin is expected to provide it
21
+ * by using `this._setInputElement(input)` Polymer API.
22
+ *
23
+ * A typical case is using `InputController` that does this automatically.
24
+ * However, the input element does not have to always be native <input>:
25
+ * as an example, <vaadin-combo-box-light> accepts other components.
20
26
  */
21
- readonly type: string;
27
+ readonly inputElement: HTMLInputElement;
28
+
29
+ /**
30
+ * The value of the field.
31
+ */
32
+ value: string;
33
+
34
+ /**
35
+ * Clear the value of the field.
36
+ */
37
+ clear(): void;
22
38
  }
23
39
 
24
- export { InputMixinConstructor, InputMixin };
40
+ export { InputMixin, InputMixinConstructor };