Socket
Socket
Sign inDemoInstall

@vaadin/field-base

Package Overview
Dependencies
Maintainers
19
Versions
378
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vaadin/field-base - npm Package Compare versions

Comparing version 22.0.0-alpha1 to 22.0.0-alpha10

src/aria-label-controller.d.ts

19

index.d.ts

@@ -1,14 +0,15 @@

export { ClearButtonMixin } from './src/clear-button-mixin.js';
export { AriaLabelController } from './src/aria-label-controller.js';
export { CheckedMixin } from './src/checked-mixin.js';
export { DelegateFocusMixin } from './src/delegate-focus-mixin.js';
export { DisabledMixin } from './src/disabled-mixin.js';
export { FieldAriaMixin } from './src/field-aria-mixin.js';
export { FocusMixin } from './src/focus-mixin.js';
export { HelperTextMixin } from './src/helper-text-mixin.js';
export { InputAriaMixin } from './src/input-aria-mixin.js';
export { DelegateStateMixin } from './src/delegate-state-mixin.js';
export { FieldMixin } from './src/field-mixin.js';
export { InputController } from './src/input-controller.js';
export { InputControlMixin } from './src/input-control-mixin.js';
export { InputFieldMixin } from './src/input-field-mixin.js';
export { InputMixin } from './src/input-mixin.js';
export { InputPropsMixin } from './src/input-props-mixin.js';
export { LabelMixin } from './src/label-mixin.js';
export { SlotMixin } from './src/slot-mixin.js';
export { TextFieldMixin } from './src/text-field-mixin.js';
export { PatternMixin } from './src/pattern-mixin.js';
export { ShadowFocusMixin } from './src/shadow-focus-mixin.js';
export { SlotStylesMixin } from './src/slot-styles-mixin.js';
export { TextAreaController } from './src/text-area-controller.js';
export { ValidateMixin } from './src/validate-mixin.js';

@@ -1,14 +0,15 @@

export { ClearButtonMixin } from './src/clear-button-mixin.js';
export { AriaLabelController } from './src/aria-label-controller.js';
export { CheckedMixin } from './src/checked-mixin.js';
export { DelegateFocusMixin } from './src/delegate-focus-mixin.js';
export { DisabledMixin } from './src/disabled-mixin.js';
export { FieldAriaMixin } from './src/field-aria-mixin.js';
export { FocusMixin } from './src/focus-mixin.js';
export { HelperTextMixin } from './src/helper-text-mixin.js';
export { InputAriaMixin } from './src/input-aria-mixin.js';
export { DelegateStateMixin } from './src/delegate-state-mixin.js';
export { FieldMixin } from './src/field-mixin.js';
export { InputController } from './src/input-controller.js';
export { InputControlMixin } from './src/input-control-mixin.js';
export { InputFieldMixin } from './src/input-field-mixin.js';
export { InputMixin } from './src/input-mixin.js';
export { InputPropsMixin } from './src/input-props-mixin.js';
export { LabelMixin } from './src/label-mixin.js';
export { SlotMixin } from './src/slot-mixin.js';
export { TextFieldMixin } from './src/text-field-mixin.js';
export { PatternMixin } from './src/pattern-mixin.js';
export { ShadowFocusMixin } from './src/shadow-focus-mixin.js';
export { SlotStylesMixin } from './src/slot-styles-mixin.js';
export { TextAreaController } from './src/text-area-controller.js';
export { ValidateMixin } from './src/validate-mixin.js';
{
"name": "@vaadin/field-base",
"version": "22.0.0-alpha1",
"version": "22.0.0-alpha10",
"publishConfig": {
"access": "public"
},
"description": "Vaadin field base mixins",
"main": "index.js",
"module": "index.js",
"repository": "vaadin/web-components",
"keywords": [
"Vaadin",
"web-components",
"web-component"
],
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/vaadin/web-components.git",
"directory": "packages/field-base"
},
"author": "Vaadin Ltd",
"license": "Apache-2.0",
"homepage": "https://vaadin.com/components",
"bugs": {
"url": "https://github.com/vaadin/web-components"
},
"homepage": "https://vaadin.com/components",
"main": "index.js",
"module": "index.js",
"files": [
"index.d.ts",
"index.js",
"index.d.ts",
"src"
],
"keywords": [
"Vaadin",
"web-components",
"web-component"
],
"dependencies": {
"@polymer/polymer": "^3.0.0"
"@polymer/polymer": "^3.0.0",
"@vaadin/component-base": "22.0.0-alpha10",
"lit": "^2.0.0"
},
"devDependencies": {
"@esm-bundle/chai": "^4.1.5",
"@vaadin/testing-helpers": "^0.2.1",
"@esm-bundle/chai": "^4.3.4",
"@vaadin/testing-helpers": "^0.3.0",
"sinon": "^9.2.1"
},
"publishConfig": {
"access": "public"
},
"gitHead": "c9694d6549bff1f7fffb9ece26178e57fc228a51"
"gitHead": "6d3055383b9c3c8306ea34c6f2e2087e63c49348"
}

@@ -6,4 +6,4 @@ /**

*/
import { FocusMixin } from './focus-mixin.js';
import { DisabledMixin } from './disabled-mixin.js';
import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';

@@ -26,4 +26,7 @@ /**

/**
* Any element extending this mixin is required to implement this getter.
* It returns the actual focusable element in the component.
* A reference to the focusable element controlled by the mixin.
* It can be an input, textarea, button or any element with tabindex > -1.
*
* Any component implementing this mixin is expected to provide it
* by using `this._setFocusElement(input)` Polymer API.
*/

@@ -30,0 +33,0 @@ readonly focusElement: Element | null | undefined;

@@ -7,92 +7,174 @@ /**

import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
import { FocusMixin } from './focus-mixin.js';
import { DisabledMixin } from './disabled-mixin.js';
import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
import { FocusMixin } from '@vaadin/component-base/src/focus-mixin.js';
const DelegateFocusMixinImplementation = (superclass) =>
class DelegateFocusMixinClass extends FocusMixin(DisabledMixin(superclass)) {
static get properties() {
return {
/**
* Specify that this control should have input focus when the page loads.
*/
autofocus: {
type: Boolean
/**
* A mixin to forward focus to an element in the light DOM.
*
* @polymerMixin
* @mixes DisabledMixin
* @mixes FocusMixin
*/
export const DelegateFocusMixin = dedupingMixin(
(superclass) =>
class DelegateFocusMixinClass extends FocusMixin(DisabledMixin(superclass)) {
static get properties() {
return {
/**
* Specify that this control should have input focus when the page loads.
*/
autofocus: {
type: Boolean
},
/**
* A reference to the focusable element controlled by the mixin.
* It can be an input, textarea, button or any element with tabindex > -1.
*
* Any component implementing this mixin is expected to provide it
* by using `this._setFocusElement(input)` Polymer API.
*
* @protected
* @type {!HTMLElement}
*/
focusElement: {
type: Object,
readOnly: true,
observer: '_focusElementChanged'
}
};
}
constructor() {
super();
this._boundOnBlur = this._onBlur.bind(this);
this._boundOnFocus = this._onFocus.bind(this);
}
/** @protected */
ready() {
super.ready();
if (this.autofocus && !this.disabled) {
requestAnimationFrame(() => {
this.focus();
this.setAttribute('focus-ring', '');
});
}
};
}
}
/**
* Any element extending this mixin is required to implement this getter.
* It returns the actual focusable element in the component.
* @return {Element | null | undefined}
*/
get focusElement() {
console.warn(`Please implement the 'focusElement' property in <${this.localName}>`);
return null;
}
/**
* @protected
* @override
*/
focus() {
if (!this.focusElement || this.disabled) {
return;
}
/** @protected */
ready() {
super.ready();
this.focusElement.focus();
this._setFocused(true);
}
if (this.autofocus && !this.disabled) {
requestAnimationFrame(() => {
this.focus();
this.setAttribute('focus-ring', '');
});
/**
* @protected
* @override
*/
blur() {
if (!this.focusElement) {
return;
}
this.focusElement.blur();
this._setFocused(false);
}
}
focus() {
if (!this.focusElement || this.disabled) {
return;
/**
* @protected
* @override
*/
click() {
if (this.focusElement && !this.disabled) {
this.focusElement.click();
}
}
this.focusElement.focus();
this._setFocused(true);
}
/** @protected */
_focusElementChanged(element, oldElement) {
if (element) {
element.disabled = this.disabled;
this._addFocusListeners(element);
} else if (oldElement) {
this._removeFocusListeners(oldElement);
}
}
blur() {
if (!this.focusElement) {
return;
/**
* @param {HTMLElement} element
* @protected
*/
_addFocusListeners(element) {
element.addEventListener('blur', this._boundOnBlur);
element.addEventListener('focus', this._boundOnFocus);
}
this.focusElement.blur();
this._setFocused(false);
}
click() {
if (this.focusElement && !this.disabled) {
this.focusElement.click();
/**
* @param {HTMLElement} element
* @protected
*/
_removeFocusListeners(element) {
element.removeEventListener('blur', this._boundOnBlur);
element.removeEventListener('focus', this._boundOnFocus);
}
}
/**
* @param {Event} event
* @return {boolean}
* @protected
*/
_shouldSetFocus(event) {
return event.target === this.focusElement;
}
/**
* Focus event does not bubble, so we dispatch it manually
* on the host element to support adding focus listeners
* when the focusable element is placed in light DOM.
* @param {FocusEvent} event
* @protected
*/
_onFocus(event) {
event.stopPropagation();
this.dispatchEvent(new Event('focus'));
}
/**
* @param {boolean} disabled
* @protected
*/
_disabledChanged(disabled) {
super._disabledChanged(disabled);
/**
* Blur event does not bubble, so we dispatch it manually
* on the host element to support adding blur listeners
* when the focusable element is placed in light DOM.
* @param {FocusEvent} event
* @protected
*/
_onBlur(event) {
event.stopPropagation();
this.dispatchEvent(new Event('blur'));
}
if (this.focusElement) {
this.focusElement.disabled = disabled;
/**
* @param {Event} event
* @return {boolean}
* @protected
* @override
*/
_shouldSetFocus(event) {
return event.target === this.focusElement;
}
if (disabled) {
this.blur();
/**
* @param {boolean} disabled
* @protected
*/
_disabledChanged(disabled) {
super._disabledChanged(disabled);
if (this.focusElement) {
this.focusElement.disabled = disabled;
}
if (disabled) {
this.blur();
}
}
}
};
/**
* A mixin to forward focus to an element in the light DOM.
*/
export const DelegateFocusMixin = dedupingMixin(DelegateFocusMixinImplementation);
);

@@ -6,6 +6,3 @@ /**

*/
import { ClearButtonMixin } from './clear-button-mixin.js';
import { DelegateFocusMixin } from './delegate-focus-mixin.js';
import { FieldAriaMixin } from './field-aria-mixin.js';
import { InputPropsMixin } from './input-props-mixin.js';
import { InputControlMixin } from './input-control-mixin.js';

@@ -21,5 +18,3 @@ /**

interface InputFieldMixin extends ClearButtonMixin, DelegateFocusMixin, FieldAriaMixin, InputPropsMixin {
readonly inputElement: HTMLElement | undefined;
interface InputFieldMixin extends InputControlMixin {
/**

@@ -51,19 +46,4 @@ * Whether the value of the control can be automatically completed by the browser.

autocapitalize: 'on' | 'off' | 'none' | 'characters' | 'words' | 'sentences' | undefined;
/**
* Specify that the value should be automatically selected when the field gains focus.
*/
autoselect: boolean;
/**
* The value of the field.
*/
value: string;
/**
* Returns true if the current input value satisfies all constraints (if any).
*/
checkValidity(): boolean;
}
export { InputFieldMixin, InputFieldMixinConstructor };

@@ -6,12 +6,12 @@ /**

*/
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
import { animationFrame } from '@polymer/polymer/lib/utils/async.js';
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
import { ClearButtonMixin } from './clear-button-mixin.js';
import { DelegateFocusMixin } from './delegate-focus-mixin.js';
import { FieldAriaMixin } from './field-aria-mixin.js';
import { InputPropsMixin } from './input-props-mixin.js';
import { InputControlMixin } from './input-control-mixin.js';
const InputFieldMixinImplementation = (superclass) =>
class InputFieldMixinClass extends ClearButtonMixin(FieldAriaMixin(InputPropsMixin(DelegateFocusMixin(superclass)))) {
/**
* A mixin to provide logic for vaadin-text-field and related components.
*
* @polymerMixin
* @mixes InputControlMixin
*/
export const InputFieldMixin = (superclass) =>
class InputFieldMixinClass extends InputControlMixin(superclass) {
static get properties() {

@@ -53,17 +53,15 @@ return {

/**
* Specify that the value should be automatically selected when the field gains focus.
* A pattern matched against individual characters the user inputs.
* When set, the field will prevent:
* - `keyDown` events if the entered key doesn't match `/^_enabledCharPattern$/`
* - `paste` events if the pasted text doesn't match `/^_enabledCharPattern*$/`
* - `drop` events if the dropped text doesn't match `/^_enabledCharPattern*$/`
*
* For example, to enable entering only numbers and minus signs,
* `_enabledCharPattern = "[\\d-]"`
* @protected
*/
autoselect: {
type: Boolean,
value: false
},
/**
* The value of the field.
*/
value: {
_enabledCharPattern: {
type: String,
value: '',
observer: '_valueChanged',
notify: true
observer: '_enabledCharPatternChanged'
}

@@ -73,50 +71,31 @@ };

static get hostProps() {
return [...super.hostProps, 'autocapitalize', 'autocomplete', 'autocorrect'];
static get delegateAttrs() {
return [...super.delegateAttrs, 'autocapitalize', 'autocomplete', 'autocorrect'];
}
static get observers() {
return ['__observeOffsetHeight(errorMessage, invalid, label, helperText)'];
constructor() {
super();
this._boundOnPaste = this._onPaste.bind(this);
this._boundOnDrop = this._onDrop.bind(this);
this._boundOnBeforeInput = this._onBeforeInput.bind(this);
}
/**
* Element used by `FieldAriaMixin` to set ARIA attributes.
* @param {HTMLElement} input
* @protected
* @override
*/
get _ariaTarget() {
return this._inputNode;
}
_inputElementChanged(input) {
super._inputElementChanged(input);
/**
* Element used by `DelegatesFocusMixin` to handle focus.
* @return {!HTMLInputElement}
*/
get focusElement() {
return this._inputNode;
}
constructor() {
super();
this._boundOnInput = this._onInput.bind(this);
this._boundOnBlur = this._onBlur.bind(this);
this._boundOnFocus = this._onFocus.bind(this);
}
/** @protected */
connectedCallback() {
super.connectedCallback();
if (this._inputNode) {
this._addInputListeners(this._inputNode);
if (input) {
// Discard value set on the custom slotted input.
if (this._inputNode.value !== this.value) {
if (input.value && input.value !== this.value) {
console.warn(`Please define value on the <${this.localName}> component!`);
this._inputNode.value = '';
input.value = '';
}
if (this.value) {
this._inputNode.value = this.value;
this.validate();
input.value = this.value;
}

@@ -126,37 +105,2 @@ }

/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
if (this._inputNode) {
this._removeInputListeners(this._inputNode);
}
}
/** @protected */
ready() {
super.ready();
// Lumo theme defines a max-height transition for the "error-message"
// part on invalid state change.
const errorPart = this.shadowRoot.querySelector('[part="error-message"]');
if (errorPart) {
errorPart.addEventListener('transitionend', () => {
this.__observeOffsetHeight();
});
}
}
/**
* Returns true if the current input value satisfies all constraints (if any).
* @return {boolean}
*/
checkValidity() {
if (this.required) {
return this._inputNode ? this._inputNode.checkValidity() : undefined;
} else {
return !this.invalid;
}
}
// Workaround for https://github.com/Polymer/polymer/issues/5259

@@ -172,115 +116,120 @@ get __data() {

/**
* @param {HTMLElement} node
* Override an event listener from `DelegateFocusMixin`.
* @param {FocusEvent} event
* @protected
* @override
*/
_addInputListeners(node) {
node.addEventListener('input', this._boundOnInput);
node.addEventListener('blur', this._boundOnBlur);
node.addEventListener('focus', this._boundOnFocus);
_onBlur(event) {
super._onBlur(event);
this.validate();
}
/**
* @param {HTMLElement} node
* Override a method from `InputMixin` to validate the field
* when a new value is set programmatically.
* @param {string} value
* @protected
* @override
*/
_removeInputListeners(node) {
node.removeEventListener('input', this._boundOnInput);
node.removeEventListener('blur', this._boundOnBlur);
node.removeEventListener('focus', this._boundOnFocus);
}
_forwardInputValue(value) {
super._forwardInputValue(value);
/** @private */
_onFocus() {
if (this.autoselect && this._inputNode) {
this._inputNode.select();
if (this.invalid) {
this.validate();
}
}
/** @private */
_onBlur() {
this.validate();
/**
* Override a method from `InputMixin`.
* @param {!HTMLElement} input
* @protected
* @override
*/
_addInputListeners(input) {
super._addInputListeners(input);
input.addEventListener('paste', this._boundOnPaste);
input.addEventListener('drop', this._boundOnDrop);
input.addEventListener('beforeinput', this._boundOnBeforeInput);
}
/**
* @param {Event} event
* Override a method from `InputMixin`.
* @param {!HTMLElement} input
* @protected
* @override
*/
_onInput(event) {
// Ignore manual clear button events
this.__userInput = event.isTrusted;
this.value = event.target.value;
this.__userInput = false;
_removeInputListeners(input) {
super._removeInputListeners(input);
input.removeEventListener('paste', this._boundOnPaste);
input.removeEventListener('drop', this._boundOnDrop);
input.removeEventListener('beforeinput', this._boundOnBeforeInput);
}
/**
* Dispatch an event if a specific size measurement property has changed.
* Supporting multiple properties here is needed for `vaadin-text-area`.
* Override an event listener from `ClearButtonMixin`
* to avoid adding a separate listener.
* @param {!KeyboardEvent} event
* @protected
* @override
*/
_dispatchIronResizeEventIfNeeded(prop, value) {
const oldSize = '__old' + prop;
if (this[oldSize] !== undefined && this[oldSize] !== value) {
this.dispatchEvent(new CustomEvent('iron-resize', { bubbles: true, composed: true }));
_onKeyDown(event) {
if (this._enabledCharPattern && !this.__shouldAcceptKey(event)) {
event.preventDefault();
}
this[oldSize] = value;
super._onKeyDown(event);
}
/** @private */
__observeOffsetHeight() {
this.__observeOffsetHeightDebouncer = Debouncer.debounce(
this.__observeOffsetHeightDebouncer,
animationFrame,
() => {
this._dispatchIronResizeEventIfNeeded('Height', this.offsetHeight);
}
__shouldAcceptKey(event) {
return (
event.metaKey ||
event.ctrlKey ||
!event.key || // allow typing anything if event.key is not supported
event.key.length !== 1 || // allow "Backspace", "ArrowLeft" etc.
this.__enabledCharRegExp.test(event.key)
);
}
/**
* @param {unknown} newVal
* @param {unknown} oldVal
* @protected
*/
_valueChanged(newVal, oldVal) {
// Setting initial value to empty string, skip validation
if (newVal === '' && oldVal === undefined) {
return;
/** @private */
_onPaste(e) {
if (this._enabledCharPattern) {
const pastedText = (e.clipboardData || window.clipboardData).getData('text');
if (!this.__enabledTextRegExp.test(pastedText)) {
e.preventDefault();
}
}
}
if (newVal !== '' && newVal != null) {
this.setAttribute('has-value', '');
} else {
this.removeAttribute('has-value');
/** @private */
_onDrop(e) {
if (this._enabledCharPattern) {
const draggedText = e.dataTransfer.getData('text');
if (!this.__enabledTextRegExp.test(draggedText)) {
e.preventDefault();
}
}
}
// Value is set before an element is connected to the DOM:
// this case is handled separately in `connectedCallback`.
if (!this._inputNode) {
return;
/** @private */
_onBeforeInput(e) {
// The `beforeinput` event covers all the cases for `_enabledCharPattern`: keyboard, pasting and dropping,
// but it is still experimental technology so we can't rely on it. It's used here just as an additional check,
// because it seems to be the only way to detect and prevent specific keys on mobile devices.
// See https://github.com/vaadin/vaadin-text-field/issues/429
if (this._enabledCharPattern && e.data && !this.__enabledTextRegExp.test(e.data)) {
e.preventDefault();
}
}
// Value is set by the user, no need to sync it back to input.
// Also no need to validate, as we call `validate` on blur.
if (this.__userInput) {
return;
/** @private */
_enabledCharPatternChanged(charPattern) {
if (charPattern) {
this.__enabledCharRegExp = new RegExp('^' + charPattern + '$');
this.__enabledTextRegExp = new RegExp('^' + charPattern + '*$');
}
// Setting a value programmatically, sync it to input element.
if (newVal != undefined) {
this._inputNode.value = newVal;
} else {
this.clear();
}
// Validate the field after a new value is set programmatically.
if (this.invalid) {
this.validate();
}
}
};
/**
* A mixin to provide logic for vaadin-text-field and related components.
*/
export const InputFieldMixin = dedupingMixin(InputFieldMixinImplementation);

@@ -6,6 +6,6 @@ /**

*/
import { SlotMixin } from './slot-mixin.js';
/**
* A mixin to add `<input>` element to the corresponding named slot.
* A mixin to store the reference to an input element
* and add input and change event listeners to it.
*/

@@ -18,9 +18,25 @@ declare function InputMixin<T extends new (...args: any[]) => {}>(base: T): T & InputMixinConstructor;

interface InputMixin extends SlotMixin {
interface InputMixin {
/**
* String used to define input type.
* A reference to the input element controlled by the mixin.
* Any component implementing this mixin is expected to provide it
* by using `this._setInputElement(input)` Polymer API.
*
* A typical case is using `InputController` that does this automatically.
* However, the input element does not have to always be native <input>:
* as an example, <vaadin-combo-box-light> accepts other components.
*/
readonly type: string;
readonly inputElement: HTMLInputElement;
/**
* The value of the field.
*/
value: string;
/**
* Clear the value of the field.
*/
clear(): void;
}
export { InputMixinConstructor, InputMixin };
export { InputMixin, InputMixinConstructor };

@@ -7,65 +7,175 @@ /**

import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
import { SlotMixin } from './slot-mixin.js';
const InputMixinImplementation = (superclass) =>
class InputMixinClass extends SlotMixin(superclass) {
static get properties() {
/**
* A mixin to store the reference to an input element
* and add input and change event listeners to it.
*
* @polymerMixin
*/
export const InputMixin = dedupingMixin(
(superclass) =>
class InputMixinClass extends superclass {
static get properties() {
return {
/**
* A reference to the input element controlled by the mixin.
* Any component implementing this mixin is expected to provide it
* by using `this._setInputElement(input)` Polymer API.
*
* A typical case is using `InputController` that does this automatically.
* However, the input element does not have to always be native <input>:
* as an example, <vaadin-combo-box-light> accepts other components.
*
* @protected
* @type {!HTMLElement}
*/
inputElement: {
type: Object,
readOnly: true,
observer: '_inputElementChanged'
},
/**
* String used to define input type.
* @protected
*/
type: {
type: String,
readOnly: true
},
/**
* The value of the field.
*/
value: {
type: String,
value: '',
observer: '_valueChanged',
notify: true
}
};
}
constructor() {
super();
this._boundOnInput = this._onInput.bind(this);
this._boundOnChange = this._onChange.bind(this);
}
/**
* String used to define input type.
* Clear the value of the field.
*/
return {
type: {
type: String,
readOnly: true
clear() {
this.value = '';
}
/**
* Add event listeners to the input element instance.
* Override this method to add custom listeners.
* @param {!HTMLElement} input
*/
_addInputListeners(input) {
input.addEventListener('input', this._boundOnInput);
input.addEventListener('change', this._boundOnChange);
}
/**
* Remove event listeners from the input element instance.
* @param {!HTMLElement} input
*/
_removeInputListeners(input) {
input.removeEventListener('input', this._boundOnInput);
input.removeEventListener('change', this._boundOnChange);
}
/**
* A method to forward the value property set on the field
* programmatically back to the input element value.
* Override this method to perform additional checks,
* for example to skip this in certain conditions.
* @param {string} value
* @protected
* @override
*/
_forwardInputValue(value) {
// Value might be set before an input element is initialized.
// This case should be handled separately by a component that
// implements this mixin, for example in `connectedCallback`.
if (!this.inputElement) {
return;
}
};
}
get slots() {
return {
...super.slots,
input: () => {
const native = document.createElement('input');
const value = this.getAttribute('value');
if (value) {
native.setAttribute('value', value);
}
const name = this.getAttribute('name');
if (name) {
native.setAttribute('name', name);
}
if (this.type) {
native.setAttribute('type', this.type);
}
return native;
if (value != undefined) {
this.inputElement.value = value;
} else {
this.inputElement.value = '';
}
};
}
}
/** @protected */
get _inputNode() {
return this._getDirectSlotChild('input');
}
/** @protected */
_inputElementChanged(input, oldInput) {
if (input) {
this._addInputListeners(input);
} else if (oldInput) {
this._removeInputListeners(oldInput);
}
}
constructor() {
super();
/**
* An input event listener used to update the field value.
* Override this method with an actual implementation.
* @param {Event} _event
* @protected
* @override
*/
_onInput(event) {
// Ignore fake input events e.g. used by clear button.
this.__userInput = event.isTrusted;
this.value = event.target.value;
this.__userInput = false;
}
// Ensure every instance has unique ID
const uniqueId = (InputMixinClass._uniqueId = 1 + InputMixinClass._uniqueId || 0);
this._inputId = `${this.localName}-${uniqueId}`;
}
/**
* A change event listener.
* Override this method with an actual implementation.
* @param {Event} _event
* @protected
* @override
*/
_onChange(_event) {}
/** @protected */
connectedCallback() {
super.connectedCallback();
/**
* Toggle the has-value attribute based on the value property.
* @param {boolean} hasValue
* @protected
*/
_toggleHasValue(hasValue) {
this.toggleAttribute('has-value', hasValue);
}
if (this._inputNode) {
this._inputNode.id = this._inputId;
/**
* Observer called when a value property changes.
* @param {string | undefined} newVal
* @param {string | undefined} oldVal
* @protected
* @override
*/
_valueChanged(newVal, oldVal) {
this._toggleHasValue(newVal !== '' && newVal != null);
// Setting initial value to empty string, do nothing.
if (newVal === '' && oldVal === undefined) {
return;
}
// Value is set by the user, no need to sync it back to input.
if (this.__userInput) {
return;
}
// Setting a value programmatically, sync it to input element.
this._forwardInputValue(newVal);
}
}
};
/**
* A mixin to add `<input>` element to the corresponding named slot.
*/
export const InputMixin = dedupingMixin(InputMixinImplementation);
);

@@ -6,3 +6,3 @@ /**

*/
import { SlotMixin } from './slot-mixin.js';
import { SlotMixin } from '@vaadin/component-base/src/slot-mixin.js';

@@ -22,5 +22,5 @@ /**

*/
label: string;
label: string | null | undefined;
}
export { LabelMixinConstructor, LabelMixin };

@@ -7,75 +7,89 @@ /**

import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
import { SlotMixin } from './slot-mixin.js';
import { SlotMixin } from '@vaadin/component-base/src/slot-mixin.js';
const LabelMixinImplementation = (superclass) =>
class LabelMixinClass extends SlotMixin(superclass) {
static get properties() {
return {
/**
* The label text for the input node.
* When no light dom defined via [slot=label], this value will be used.
*/
label: {
type: String,
observer: '_labelChanged'
}
};
}
/**
* A mixin to provide label via corresponding property or named slot.
*
* @polymerMixin
* @mixes SlotMixin
*/
export const LabelMixin = dedupingMixin(
(superclass) =>
class LabelMixinClass extends SlotMixin(superclass) {
static get properties() {
return {
/**
* The label text for the input node.
* When no light dom defined via [slot=label], this value will be used.
*/
label: {
type: String,
observer: '_labelChanged'
}
};
}
get slots() {
return {
...super.slots,
label: () => {
const label = document.createElement('label');
label.textContent = this.label;
return label;
}
};
}
/** @protected */
get slots() {
return {
...super.slots,
label: () => {
const label = document.createElement('label');
label.textContent = this.label;
return label;
}
};
}
/** @protected */
get _labelNode() {
return this._getDirectSlotChild('label');
}
/** @protected */
get _labelNode() {
return this._getDirectSlotChild('label');
}
constructor() {
super();
constructor() {
super();
// Ensure every instance has unique ID
const uniqueId = (LabelMixinClass._uniqueId = 1 + LabelMixinClass._uniqueId || 0);
this._labelId = `label-${this.localName}-${uniqueId}`;
}
// Ensure every instance has unique ID
const uniqueId = (LabelMixinClass._uniqueLabelId = 1 + LabelMixinClass._uniqueLabelId || 0);
this._labelId = `label-${this.localName}-${uniqueId}`;
/** @protected */
connectedCallback() {
super.connectedCallback();
/**
* @type {MutationObserver}
* @private
*/
this.__labelNodeObserver = new MutationObserver(() => {
this._toggleHasLabelAttribute();
});
}
if (this._labelNode) {
this._labelNode.id = this._labelId;
/** @protected */
ready() {
super.ready();
this._applyCustomLabel();
if (this._labelNode) {
this._labelNode.id = this._labelId;
this._toggleHasLabelAttribute();
this.__labelNodeObserver.observe(this._labelNode, { childList: true });
}
}
}
/** @protected */
_applyCustomLabel() {
const label = this._labelNode.textContent;
if (label !== this.label) {
this.label = label;
/** @protected */
_labelChanged(label) {
if (this._labelNode) {
this._labelNode.textContent = label;
this._toggleHasLabelAttribute();
}
}
}
/** @protected */
_labelChanged(label) {
if (this._labelNode) {
this._labelNode.textContent = label;
/** @protected */
_toggleHasLabelAttribute() {
if (this._labelNode) {
const hasLabel = this._labelNode.children.length > 0 || this._labelNode.textContent.trim() !== '';
this.toggleAttribute('has-label', hasLabel);
this.dispatchEvent(new CustomEvent('has-label-changed', { detail: { value: hasLabel } }));
}
}
this.toggleAttribute('has-label', Boolean(label));
}
};
/**
* A mixin to provide label via corresponding property or named slot.
*/
export const LabelMixin = dedupingMixin(LabelMixinImplementation);
);

@@ -6,3 +6,2 @@ /**

*/
import { SlotMixin } from './slot-mixin.js';

@@ -18,3 +17,3 @@ /**

interface ValidateMixin extends SlotMixin {
interface ValidateMixin {
/**

@@ -31,9 +30,2 @@ * Set to true when the field is invalid.

/**
* Error to show when the field is invalid.
*
* @attr {string} error-message
*/
errorMessage: string;
/**
* Returns true if field is valid, and sets `invalid` based on the field validity.

@@ -40,0 +32,0 @@ */

@@ -7,126 +7,51 @@ /**

import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
import { SlotMixin } from './slot-mixin.js';
const ValidateMixinImplementation = (superclass) =>
class ValidateMixinClass extends SlotMixin(superclass) {
static get properties() {
return {
/**
* Set to true when the field is invalid.
*/
invalid: {
type: Boolean,
reflectToAttribute: true,
notify: true,
value: false
},
/**
* A mixin to provide required state and validation logic.
*
* @polymerMixin
*/
export const ValidateMixin = dedupingMixin(
(superclass) =>
class ValidateMixinClass extends superclass {
static get properties() {
return {
/**
* Set to true when the field is invalid.
*/
invalid: {
type: Boolean,
reflectToAttribute: true,
notify: true,
value: false
},
/**
* Specifies that the user must fill in a value.
*/
required: {
type: Boolean,
reflectToAttribute: true
},
/**
* Error to show when the field is invalid.
*
* @attr {string} error-message
*/
errorMessage: {
type: String
}
};
}
get slots() {
return {
...super.slots,
'error-message': () => {
const error = document.createElement('div');
error.textContent = this.errorMessage;
error.setAttribute('aria-live', 'assertive');
return error;
}
};
}
static get observers() {
return ['_updateErrorMessage(invalid, errorMessage)'];
}
/** @protected */
get _errorNode() {
return this._getDirectSlotChild('error-message');
}
constructor() {
super();
// Ensure every instance has unique ID
const uniqueId = (ValidateMixinClass._uniqueId = 1 + ValidateMixinClass._uniqueId || 0);
this._errorId = `error-${this.localName}-${uniqueId}`;
}
/** @protected */
connectedCallback() {
super.connectedCallback();
if (this._errorNode) {
this._errorNode.id = this._errorId;
this._applyCustomError();
/**
* Specifies that the user must fill in a value.
*/
required: {
type: Boolean,
reflectToAttribute: true
}
};
}
}
/**
* Returns true if field is valid, and sets `invalid` based on the field validity.
*
* @return {boolean} True if the value is valid.
*/
validate() {
return !(this.invalid = !this.checkValidity());
}
/**
* Returns true if the field value satisfies all constraints (if any).
*
* @return {boolean}
*/
checkValidity() {
return !this.required || !!this.value;
}
/** @protected */
_applyCustomError() {
const error = this.__errorMessage;
if (error && error !== this.errorMessage) {
this.errorMessage = error;
delete this.__errorMessage;
/**
* Returns true if field is valid, and sets `invalid` based on the field validity.
*
* @return {boolean} True if the value is valid.
*/
validate() {
return !(this.invalid = !this.checkValidity());
}
}
/**
* @param {boolean} invalid
* @protected
*/
_updateErrorMessage(invalid, errorMessage) {
if (!this._errorNode) {
return;
/**
* Returns true if the field value satisfies all constraints (if any).
*
* @return {boolean}
*/
checkValidity() {
return !this.required || !!this.value;
}
// save the custom error message content
if (this._errorNode.textContent && !errorMessage) {
this.__errorMessage = this._errorNode.textContent.trim();
}
const hasError = Boolean(invalid && errorMessage);
this._errorNode.textContent = hasError ? errorMessage : '';
this.toggleAttribute('has-error-message', hasError);
}
};
/**
* A mixin to provide required state and validation logic.
*/
export const ValidateMixin = dedupingMixin(ValidateMixinImplementation);
);
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc