Skip to content

Commit

Permalink
Merge 3899380 into fbe70fc
Browse files Browse the repository at this point in the history
  • Loading branch information
yuriy-fix committed Dec 11, 2018
2 parents fbe70fc + 3899380 commit 7cce2b1
Show file tree
Hide file tree
Showing 33 changed files with 1,592 additions and 910 deletions.
4 changes: 4 additions & 0 deletions src/vaadin-password-field.html
Expand Up @@ -141,6 +141,10 @@
}

_onChange(e) {
const slotted = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`);
if (slotted) {
e.stopPropagation();
}
if (this._passwordVisibilityChanging) {
this._cachedChangeEvent = e;
} else {
Expand Down
27 changes: 7 additions & 20 deletions src/vaadin-text-area.html
Expand Up @@ -56,26 +56,9 @@

<slot name="prefix"></slot>

<textarea part="value"
autocomplete$="[[autocomplete]]"
autocorrect$="[[autocorrect]]"
autocapitalize$="[[autocapitalize]]"
autofocus$="[[autofocus]]"
disabled$="[[disabled]]"
maxlength$="[[maxlength]]"
minlength$="[[minlength]]"
placeholder$="[[placeholder]]"
readonly$="[[readonly]]"
aria-readonly$="[[readonly]]"
required$="[[required]]"
aria-required$="[[required]]"
value="{{value::input}}"
on-blur="validate"
on-input="_onInput"
on-change="_onChange"
aria-describedby$="[[_getActiveErrorId(invalid, errorMessage, _errorId)]]"
aria-labelledby$="[[_getActiveLabelId(label, _labelId)]]"
aria-invalid$="[[invalid]]"></textarea>
<slot name="textarea">
<textarea part="value"></textarea>
</slot>

<div part="clear-button" id="clearButton" role="button" aria-label="Clear"></div>
<slot name="suffix"></slot>
Expand Down Expand Up @@ -173,6 +156,10 @@
}
}

get _slottedTagName() {
return 'textarea';
}

_textAreaValueChanged(value) {
this._updateHeight();
}
Expand Down
216 changes: 186 additions & 30 deletions src/vaadin-text-field-mixin.html
Expand Up @@ -47,7 +47,9 @@
}

/* Reset the native input styles */
[part="value"] {
[part="value"],
[part="input-field"] ::slotted(input),
[part="input-field"] ::slotted(textarea) {
-webkit-appearance: none;
-moz-appearance: none;
outline: none;
Expand All @@ -69,8 +71,10 @@
flex: none;
}

/* Slotted by vaadin-dropdown-menu-text-field */
[part="value"],
[part="input-field"] ::slotted(input),
[part="input-field"] ::slotted(textarea),
/* Slotted by vaadin-select-text-field */
[part="input-field"] ::slotted([part="value"]) {
flex: auto;
white-space: nowrap;
Expand All @@ -79,7 +83,12 @@
height: 100%;
}

[part="value"]::-ms-clear {
[part="input-field"] ::slotted(textarea) {
resize: none;
}

[part="value"]::-ms-clear,
[part="input-field"] ::slotted(input)::-ms-clear {
display: none;
}

Expand All @@ -100,6 +109,18 @@
*/
window.Vaadin = window.Vaadin || {};

const HOST_PROPS = {
default: ['list', 'autofocus', 'pattern', 'autocapitalize', 'autocorrect', 'maxlength',
'minlength', 'name', 'placeholder', 'autocomplete', 'title'],
accessible: ['disabled', 'readonly', 'required', 'invalid']
};

const PROP_TYPE = {
DEFAULT: 'default',
ACCESSIBLE: 'accessible'
};


/**
* @polymerMixin
* @memberof Vaadin
Expand Down Expand Up @@ -259,20 +280,38 @@
}

static get observers() {
return ['_stateChanged(disabled, readonly, clearButtonVisible, hasValue)'];
return ['_stateChanged(disabled, readonly, clearButtonVisible, hasValue)',
'_hostPropsChanged(' + HOST_PROPS.default.join(', ') + ')',
'_hostAccessiblePropsChanged(' + HOST_PROPS.accessible.join(', ') + ')',
'_getActiveErrorId(invalid, errorMessage, _errorId)',
'_getActiveLabelId(label, _labelId)'];
}

get focusElement() {
return this.root && this.root.querySelector('[part=value]');
if (!this.shadowRoot) {
return;
}
const slotted = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`);
if (slotted) {
return slotted;
}
return this.shadowRoot.querySelector('[part="value"]');
}

get _slottedTagName() {
return 'input';
}

_onInput(e) {
if (this.preventInvalidInput) {
const input = this.focusElement;
if (input.value.length > 0 && !this.checkValidity()) {
input.value = this.value || '';
return;
}
}
this.__userInput = true;
this.value = e.target.value;
}

// NOTE(yuriy): Workaround needed for IE11 and Edge for proper displaying
Expand Down Expand Up @@ -311,14 +350,25 @@
if (newVal === '' && oldVal === undefined) {
return;
}
if (this.invalid) {
this.validate();
}

if (newVal !== '' && newVal != null) {
this.hasValue = true;
} else {
this.hasValue = false;
}

if (this.__userInput) {
this.__userInput = false;
return;
} else if (newVal !== undefined) {
this.focusElement.value = newVal;
} else {
this.value = '';
}

if (this.invalid) {
this.validate();
}
}

_labelChanged(label) {
Expand All @@ -329,6 +379,74 @@
}
}

_onSlotChange() {
const slotted = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`);

if (this.value) {
this.focusElement.value = this.value;
this.validate();
}

if (slotted && !this._slottedInput) {
this._validateSlottedValue(slotted);
this._addInputListeners(slotted);
this._addIEListeners(slotted);
this._slottedInput = slotted;
} else if (!this._slottedInput) {
this._removeInputListeners(this._slottedInput);
this._removeIEListeners(this._slottedInput);
this._slottedInput = undefined;
}

Object.keys(PROP_TYPE).map(key => PROP_TYPE[key]).forEach(type =>
this._propagateHostAttributes(HOST_PROPS[type].map(attr => this[attr]), type));
}

_hostPropsChanged(...attributesValues) {
this._propagateHostAttributes(attributesValues, PROP_TYPE.DEFAULT);
}

_hostAccessiblePropsChanged(...attributesValues) {
this._propagateHostAttributes(attributesValues, PROP_TYPE.ACCESSIBLE);
}

_validateSlottedValue(slotted) {
if (slotted.value !== this.value) {
console.warn('Please define value on the vaadin-text-field component!');
slotted.value = '';
}
}

_propagateHostAttributes(attributesValues, type) {
const input = this.focusElement;
const attributeNames = HOST_PROPS[type];

if (type === 'accessible') {
attributeNames.forEach((attr, index) => {
this._setOrToggleAttribute(attr, attributesValues[index], input);
this._setOrToggleAttribute(`aria-${attr}`, attributesValues[index], input);
});
} else {
attributeNames.forEach((attr, index) => {
this._setOrToggleAttribute(attr, attributesValues[index], input);
});
}
}

_setOrToggleAttribute(name, value, node) {
if (!name || !node) {
return;
}

if (node.hasAttribute(name) === !value) {
if (value) {
node.setAttribute(name, (typeof value === 'boolean') ? '' : value);
} else {
node.removeAttribute(name);
}
}
}

/**
* Returns true if the current input value satisfies all constraints (if any)
* @returns {boolean}
Expand All @@ -341,9 +459,37 @@
}
}

_addInputListeners(node) {
node.addEventListener('input', this._boundOnInput);
node.addEventListener('change', this._boundOnChange);
node.addEventListener('blur', this._boundOnBlur);
}

_removeInputListeners(node) {
node.removeEventListener('input', this._boundOnInput);
node.removeEventListener('change', this._boundOnChange);
node.removeEventListener('blur', this._boundOnBlur);
}

ready() {
super.ready();

this._boundOnInput = this._onInput.bind(this);
this._boundOnChange = this._onChange.bind(this);
this._boundOnBlur = this.validate.bind(this);

const defaultInput = this.shadowRoot.querySelector('[part="value"]');
this._slottedInput = this.querySelector(`${this._slottedTagName}[slot="${this._slottedTagName}"]`);
this._addInputListeners(defaultInput);
this._addIEListeners(defaultInput);
if (this._slottedInput) {
this._addIEListeners(this._slottedInput);
this._addInputListeners(this._slottedInput);
}

this.shadowRoot.querySelector('[name="input"], [name="textarea"]')
.addEventListener('slotchange', this._onSlotChange.bind(this));

if (!(window.ShadyCSS && window.ShadyCSS.nativeCss)) {
this.updateStyles();
}
Expand All @@ -355,11 +501,6 @@
var uniqueId = Vaadin.TextFieldMixin._uniqueId = 1 + Vaadin.TextFieldMixin._uniqueId || 0;
this._errorId = `${this.constructor.is}-error-${uniqueId}`;
this._labelId = `${this.constructor.is}-label-${uniqueId}`;

/* istanbul ignore if */
if (navigator.userAgent.match(/Trident/)) {
this._addIEListeners();
}
}

/**
Expand All @@ -381,7 +522,7 @@
this.focusElement.focus();
this.clear();
this._valueClearing = false;
this.focusElement.dispatchEvent(new Event('change', {bubbles: true}));
this.focusElement.dispatchEvent(new Event('change', {bubbles: !this._slottedInput}));
}

_onKeyDown(e) {
Expand All @@ -390,31 +531,46 @@
}
}

_addIEListeners() {
// IE11 dispatches `input` event in following cases:
// - focus or blur, when placeholder attribute is set
// - placeholder attribute value changed
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101220/
const prevent = e => {
e.stopImmediatePropagation();
this.focusElement.removeEventListener('input', prevent);
};
const shouldPreventInput = () => this.placeholder && this.focusElement.addEventListener('input', prevent);
this.focusElement.addEventListener('focusin', shouldPreventInput);
this.focusElement.addEventListener('focusout', shouldPreventInput);
this._createPropertyObserver('placeholder', shouldPreventInput);
_addIEListeners(node) {
/* istanbul ignore if */
if (navigator.userAgent.match(/Trident/)) {
// IE11 dispatches `input` event in following cases:
// - focus or blur, when placeholder attribute is set
// - placeholder attribute value changed
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101220/
this._prevent = e => {
e.stopImmediatePropagation();
this.focusElement.removeEventListener('input', this._prevent);
};
this._shouldPreventInput = () => this.placeholder && node.addEventListener('input', this._prevent);
node.addEventListener('focusin', this._shouldPreventInput);
node.addEventListener('focusout', this._shouldPreventInput);
this._createPropertyObserver('placeholder', this._shouldPreventInput);
}
}

_removeIEListeners(node) {
/* istanbul ignore if */
if (navigator.userAgent.match(/Trident/)) {
node.removeEventListener('focusin', this._shouldPreventInput);
node.removeEventListener('focusout', this._shouldPreventInput);
}
}

_getActiveErrorId(invalid, errorMessage, errorId) {
return errorMessage && invalid ? errorId : undefined;
this._setOrToggleAttribute('aria-describedby',
(errorMessage && invalid ? errorId : undefined),
this.focusElement);
}

_getActiveLabelId(label, labelId) {
return label ? labelId : undefined;
this._setOrToggleAttribute('aria-labelledby',
(label ? labelId : undefined),
this.focusElement);
}

_getErrorMessageAriaHidden(invalid, errorMessage, errorId) {
return (!this._getActiveErrorId(invalid, errorMessage, errorId)).toString();
return (!(errorMessage && invalid ? errorId : undefined)).toString();
}

/**
Expand Down

0 comments on commit 7cce2b1

Please sign in to comment.