@vaadin/field-base 23.1.1 → 23.2.0-alpha1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/field-base",
3
- "version": "23.1.1",
3
+ "version": "23.2.0-alpha1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,7 +32,7 @@
32
32
  "dependencies": {
33
33
  "@open-wc/dedupe-mixin": "^1.3.0",
34
34
  "@polymer/polymer": "^3.0.0",
35
- "@vaadin/component-base": "^23.1.1",
35
+ "@vaadin/component-base": "23.2.0-alpha1",
36
36
  "lit": "^2.0.0"
37
37
  },
38
38
  "devDependencies": {
@@ -40,5 +40,5 @@
40
40
  "@vaadin/testing-helpers": "^0.3.2",
41
41
  "sinon": "^13.0.2"
42
42
  },
43
- "gitHead": "390458d6519433a2dd502cef90da48e84573a275"
43
+ "gitHead": "f226a2976c270d3d53c824f6e0a740a5d3382d91"
44
44
  }
@@ -89,10 +89,6 @@ export const FieldMixin = (superclass) =>
89
89
  this._helperController = new HelperController(this);
90
90
  this._errorController = new ErrorController(this);
91
91
 
92
- this.addController(this._fieldAriaController);
93
- this.addController(this._helperController);
94
- this.addController(this._errorController);
95
-
96
92
  this._labelController.addEventListener('label-changed', (event) => {
97
93
  const { hasLabel, node } = event.detail;
98
94
  this.__labelChanged(hasLabel, node);
@@ -104,6 +100,15 @@ export const FieldMixin = (superclass) =>
104
100
  });
105
101
  }
106
102
 
103
+ /** @protected */
104
+ ready() {
105
+ super.ready();
106
+
107
+ this.addController(this._fieldAriaController);
108
+ this.addController(this._helperController);
109
+ this.addController(this._errorController);
110
+ }
111
+
107
112
  /** @private */
108
113
  __helperChanged(hasHelper, helperNode) {
109
114
  if (hasHelper) {
@@ -36,6 +36,19 @@ export declare function InputControlMixin<T extends Constructor<HTMLElement>>(
36
36
  Constructor<ValidateMixinClass>;
37
37
 
38
38
  export declare class InputControlMixinClass {
39
+ /**
40
+ * A pattern matched against individual characters the user inputs.
41
+ *
42
+ * When set, the field will prevent:
43
+ * - `keydown` events if the entered key doesn't match `/^allowedCharPattern$/`
44
+ * - `paste` events if the pasted text doesn't match `/^allowedCharPattern*$/`
45
+ * - `drop` events if the dropped text doesn't match `/^allowedCharPattern*$/`
46
+ *
47
+ * For example, to allow entering only numbers and minus signs, use:
48
+ * `allowedCharPattern = "[\\d-]"`
49
+ */
50
+ allowedCharPattern: string;
51
+
39
52
  /**
40
53
  * If true, the input text gets fully selected when the field is focused using click or touch / tap.
41
54
  */
@@ -3,6 +3,8 @@
3
3
  * Copyright (c) 2021 - 2022 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { timeOut } from '@vaadin/component-base/src/async.js';
7
+ import { Debouncer } from '@vaadin/component-base/src/debounce.js';
6
8
  import { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
7
9
  import { DelegateFocusMixin } from './delegate-focus-mixin.js';
8
10
  import { FieldMixin } from './field-mixin.js';
@@ -23,6 +25,22 @@ export const InputControlMixin = (superclass) =>
23
25
  ) {
24
26
  static get properties() {
25
27
  return {
28
+ /**
29
+ * A pattern matched against individual characters the user inputs.
30
+ *
31
+ * When set, the field will prevent:
32
+ * - `keydown` events if the entered key doesn't match `/^allowedCharPattern$/`
33
+ * - `paste` events if the pasted text doesn't match `/^allowedCharPattern*$/`
34
+ * - `drop` events if the dropped text doesn't match `/^allowedCharPattern*$/`
35
+ *
36
+ * For example, to allow entering only numbers and minus signs, use:
37
+ * `allowedCharPattern = "[\\d-]"`
38
+ */
39
+ allowedCharPattern: {
40
+ type: String,
41
+ observer: '_allowedCharPatternChanged',
42
+ },
43
+
26
44
  /**
27
45
  * If true, the input text gets fully selected when the field is focused using click or touch / tap.
28
46
  */
@@ -80,6 +98,14 @@ export const InputControlMixin = (superclass) =>
80
98
  return [...super.delegateAttrs, 'name', 'type', 'placeholder', 'readonly', 'invalid', 'title'];
81
99
  }
82
100
 
101
+ constructor() {
102
+ super();
103
+
104
+ this._boundOnPaste = this._onPaste.bind(this);
105
+ this._boundOnDrop = this._onDrop.bind(this);
106
+ this._boundOnBeforeInput = this._onBeforeInput.bind(this);
107
+ }
108
+
83
109
  /**
84
110
  * Any element extending this mixin is required to implement this getter.
85
111
  * It returns the reference to the clear button element.
@@ -172,6 +198,115 @@ export const InputControlMixin = (superclass) =>
172
198
  this.inputElement.dispatchEvent(new Event('change', { bubbles: true }));
173
199
  }
174
200
 
201
+ /**
202
+ * Override a method from `InputMixin`.
203
+ * @param {!HTMLElement} input
204
+ * @protected
205
+ * @override
206
+ */
207
+ _addInputListeners(input) {
208
+ super._addInputListeners(input);
209
+
210
+ input.addEventListener('paste', this._boundOnPaste);
211
+ input.addEventListener('drop', this._boundOnDrop);
212
+ input.addEventListener('beforeinput', this._boundOnBeforeInput);
213
+ }
214
+
215
+ /**
216
+ * Override a method from `InputMixin`.
217
+ * @param {!HTMLElement} input
218
+ * @protected
219
+ * @override
220
+ */
221
+ _removeInputListeners(input) {
222
+ super._removeInputListeners(input);
223
+
224
+ input.removeEventListener('paste', this._boundOnPaste);
225
+ input.removeEventListener('drop', this._boundOnDrop);
226
+ input.removeEventListener('beforeinput', this._boundOnBeforeInput);
227
+ }
228
+
229
+ /**
230
+ * Override an event listener from `KeyboardMixin`.
231
+ * @param {!KeyboardEvent} event
232
+ * @protected
233
+ * @override
234
+ */
235
+ _onKeyDown(event) {
236
+ super._onKeyDown(event);
237
+
238
+ if (this.allowedCharPattern && !this.__shouldAcceptKey(event)) {
239
+ event.preventDefault();
240
+ this._markInputPrevented();
241
+ }
242
+ }
243
+
244
+ /** @protected */
245
+ _markInputPrevented() {
246
+ // Add input-prevented attribute for 200ms
247
+ this.setAttribute('input-prevented', '');
248
+ this._preventInputDebouncer = Debouncer.debounce(this._preventInputDebouncer, timeOut.after(200), () => {
249
+ this.removeAttribute('input-prevented');
250
+ });
251
+ }
252
+
253
+ /** @private */
254
+ __shouldAcceptKey(event) {
255
+ return (
256
+ event.metaKey ||
257
+ event.ctrlKey ||
258
+ !event.key || // Allow typing anything if event.key is not supported
259
+ event.key.length !== 1 || // Allow "Backspace", "ArrowLeft" etc.
260
+ this.__allowedCharRegExp.test(event.key)
261
+ );
262
+ }
263
+
264
+ /** @private */
265
+ _onPaste(e) {
266
+ if (this.allowedCharPattern) {
267
+ const pastedText = e.clipboardData.getData('text');
268
+ if (!this.__allowedTextRegExp.test(pastedText)) {
269
+ e.preventDefault();
270
+ this._markInputPrevented();
271
+ }
272
+ }
273
+ }
274
+
275
+ /** @private */
276
+ _onDrop(e) {
277
+ if (this.allowedCharPattern) {
278
+ const draggedText = e.dataTransfer.getData('text');
279
+ if (!this.__allowedTextRegExp.test(draggedText)) {
280
+ e.preventDefault();
281
+ this._markInputPrevented();
282
+ }
283
+ }
284
+ }
285
+
286
+ /** @private */
287
+ _onBeforeInput(e) {
288
+ // The `beforeinput` event covers all the cases for `allowedCharPattern`: keyboard, pasting and dropping,
289
+ // but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
290
+ // because it seems to be the only way to detect and prevent specific keys on mobile devices.
291
+ // See https://github.com/vaadin/vaadin-text-field/issues/429
292
+ if (this.allowedCharPattern && e.data && !this.__allowedTextRegExp.test(e.data)) {
293
+ e.preventDefault();
294
+ this._markInputPrevented();
295
+ }
296
+ }
297
+
298
+ /** @private */
299
+ _allowedCharPatternChanged(charPattern) {
300
+ if (charPattern) {
301
+ try {
302
+ this.__allowedCharRegExp = new RegExp(`^${charPattern}$`);
303
+ this.__allowedTextRegExp = new RegExp(`^${charPattern}*$`);
304
+ } catch (e) {
305
+ console.error(e);
306
+ }
307
+ }
308
+ }
309
+
175
310
  /**
176
311
  * Fired when the user commits a value change.
177
312
  *
@@ -23,9 +23,7 @@ export class InputController extends SlotController {
23
23
  }
24
24
 
25
25
  // Ensure every instance has unique ID
26
- const uniqueId = (InputController._uniqueInputId = 1 + InputController._uniqueInputId || 0);
27
- host._inputId = `${host.localName}-${uniqueId}`;
28
- node.id = host._inputId;
26
+ node.id = this.defaultId;
29
27
 
30
28
  if (typeof callback === 'function') {
31
29
  callback(node);
@@ -47,22 +47,6 @@ export const InputFieldMixin = (superclass) =>
47
47
  autocapitalize: {
48
48
  type: String,
49
49
  },
50
-
51
- /**
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
61
- */
62
- _enabledCharPattern: {
63
- type: String,
64
- observer: '_enabledCharPatternChanged',
65
- },
66
50
  };
67
51
  }
68
52
 
@@ -70,14 +54,6 @@ export const InputFieldMixin = (superclass) =>
70
54
  return [...super.delegateAttrs, 'autocapitalize', 'autocomplete', 'autocorrect'];
71
55
  }
72
56
 
73
- constructor() {
74
- super();
75
-
76
- this._boundOnPaste = this._onPaste.bind(this);
77
- this._boundOnDrop = this._onDrop.bind(this);
78
- this._boundOnBeforeInput = this._onBeforeInput.bind(this);
79
- }
80
-
81
57
  /**
82
58
  * @param {HTMLElement} input
83
59
  * @protected
@@ -149,97 +125,4 @@ export const InputFieldMixin = (superclass) =>
149
125
  this.validate();
150
126
  }
151
127
  }
152
-
153
- /**
154
- * Override a method from `InputMixin`.
155
- * @param {!HTMLElement} input
156
- * @protected
157
- * @override
158
- */
159
- _addInputListeners(input) {
160
- super._addInputListeners(input);
161
-
162
- input.addEventListener('paste', this._boundOnPaste);
163
- input.addEventListener('drop', this._boundOnDrop);
164
- input.addEventListener('beforeinput', this._boundOnBeforeInput);
165
- }
166
-
167
- /**
168
- * Override a method from `InputMixin`.
169
- * @param {!HTMLElement} input
170
- * @protected
171
- * @override
172
- */
173
- _removeInputListeners(input) {
174
- super._removeInputListeners(input);
175
-
176
- input.removeEventListener('paste', this._boundOnPaste);
177
- input.removeEventListener('drop', this._boundOnDrop);
178
- input.removeEventListener('beforeinput', this._boundOnBeforeInput);
179
- }
180
-
181
- /**
182
- * Override an event listener from `InputControlMixin`
183
- * to avoid adding a separate listener.
184
- * @param {!KeyboardEvent} event
185
- * @protected
186
- * @override
187
- */
188
- _onKeyDown(event) {
189
- if (this._enabledCharPattern && !this.__shouldAcceptKey(event)) {
190
- event.preventDefault();
191
- }
192
-
193
- super._onKeyDown(event);
194
- }
195
-
196
- /** @private */
197
- __shouldAcceptKey(event) {
198
- return (
199
- event.metaKey ||
200
- event.ctrlKey ||
201
- !event.key || // Allow typing anything if event.key is not supported
202
- event.key.length !== 1 || // Allow "Backspace", "ArrowLeft" etc.
203
- this.__enabledCharRegExp.test(event.key)
204
- );
205
- }
206
-
207
- /** @private */
208
- _onPaste(e) {
209
- if (this._enabledCharPattern) {
210
- const pastedText = (e.clipboardData || window.clipboardData).getData('text');
211
- if (!this.__enabledTextRegExp.test(pastedText)) {
212
- e.preventDefault();
213
- }
214
- }
215
- }
216
-
217
- /** @private */
218
- _onDrop(e) {
219
- if (this._enabledCharPattern) {
220
- const draggedText = e.dataTransfer.getData('text');
221
- if (!this.__enabledTextRegExp.test(draggedText)) {
222
- e.preventDefault();
223
- }
224
- }
225
- }
226
-
227
- /** @private */
228
- _onBeforeInput(e) {
229
- // The `beforeinput` event covers all the cases for `_enabledCharPattern`: keyboard, pasting and dropping,
230
- // but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
231
- // because it seems to be the only way to detect and prevent specific keys on mobile devices.
232
- // See https://github.com/vaadin/vaadin-text-field/issues/429
233
- if (this._enabledCharPattern && e.data && !this.__enabledTextRegExp.test(e.data)) {
234
- e.preventDefault();
235
- }
236
- }
237
-
238
- /** @private */
239
- _enabledCharPatternChanged(charPattern) {
240
- if (charPattern) {
241
- this.__enabledCharRegExp = new RegExp(`^${charPattern}$`);
242
- this.__enabledTextRegExp = new RegExp(`^${charPattern}*$`);
243
- }
244
- }
245
128
  };
@@ -43,6 +43,12 @@ export const LabelMixin = dedupingMixin(
43
43
  super();
44
44
 
45
45
  this._labelController = new LabelController(this);
46
+ }
47
+
48
+ /** @protected */
49
+ ready() {
50
+ super.ready();
51
+
46
52
  this.addController(this._labelController);
47
53
  }
48
54
 
@@ -34,6 +34,7 @@ export declare class PatternMixinClass {
34
34
  * When set to true, user is prevented from typing a value that
35
35
  * conflicts with the given `pattern`.
36
36
  * @attr {boolean} prevent-invalid-input
37
+ * @deprecated Please use `allowedCharPattern` instead.
37
38
  */
38
39
  preventInvalidInput: boolean | null | undefined;
39
40
  }
@@ -29,9 +29,11 @@ export const PatternMixin = (superclass) =>
29
29
  * When set to true, user is prevented from typing a value that
30
30
  * conflicts with the given `pattern`.
31
31
  * @attr {boolean} prevent-invalid-input
32
+ * @deprecated Please use `allowedCharPattern` instead.
32
33
  */
33
34
  preventInvalidInput: {
34
35
  type: Boolean,
36
+ observer: '_preventInvalidInputChanged',
35
37
  },
36
38
  };
37
39
  }
@@ -68,4 +70,13 @@ export const PatternMixin = (superclass) =>
68
70
 
69
71
  super._onInput(event);
70
72
  }
73
+
74
+ /** @private */
75
+ _preventInvalidInputChanged(preventInvalidInput) {
76
+ if (preventInvalidInput) {
77
+ console.warn(
78
+ `WARNING: Since Vaadin 23.2, "preventInvalidInput" is deprecated. Please use "allowedCharPattern" instead.`,
79
+ );
80
+ }
81
+ }
71
82
  };
@@ -41,7 +41,11 @@ export class SlotTargetController {
41
41
 
42
42
  // Ensure the content is up to date when host is connected
43
43
  // to handle e.g. mutating text content while disconnected.
44
- this.__checkAndCopyNodesToSlotTarget();
44
+ // Note, `hostConnected()` is called twice if the controller
45
+ // is initialized in `ready()` when using `ControllerMixin`.
46
+ if (!this.__copying) {
47
+ this.__checkAndCopyNodesToSlotTarget();
48
+ }
45
49
  }
46
50
 
47
51
  /**
@@ -25,10 +25,7 @@ export class TextAreaController extends SlotController {
25
25
  node.setAttribute('name', name);
26
26
  }
27
27
 
28
- // Ensure every instance has unique ID
29
- const uniqueId = (TextAreaController._uniqueTextAreaId = 1 + TextAreaController._uniqueTextAreaId || 0);
30
- host._textareaId = `${host.localName}-${uniqueId}`;
31
- node.id = host._textareaId;
28
+ node.id = this.defaultId;
32
29
 
33
30
  if (typeof callback === 'function') {
34
31
  callback(node);