this.element.nextElementSibling.querySelector('input,textarea')) inp.focus();
});
return true;
}
@bindable() icon;
}
@autoinject()
@customElement('ui-input-button')
@inlineView(``)
export class UIInputButton {
constructor(public element: Element) { }
@bindable() icon;
}
@autoinject()
@customElement('ui-input-info')
@inlineView(``)
export class UIInputInfo {
constructor(public element: Element) {
if (this.element.hasAttribute('primary')) this.element.classList.add('ui-text-primary');
else if (this.element.hasAttribute('secondary')) this.element.classList.add('ui-text-secondary');
else if (this.element.hasAttribute('dark')) this.element.classList.add('ui-text-dark');
else if (this.element.hasAttribute('light')) this.element.classList.add('ui-text-light');
else if (this.element.hasAttribute('info')) this.element.classList.add('ui-text-info');
else if (this.element.hasAttribute('danger')) this.element.classList.add('ui-text-danger');
else if (this.element.hasAttribute('success')) this.element.classList.add('ui-text-success');
else if (this.element.hasAttribute('warning')) this.element.classList.add('ui-text-warning');
else this.element.classList.add('ui-text-muted');
if (this.element.hasAttribute('center')) this.element.classList.add('ui-text-center');
}
}
@autoinject()
@customElement('ui-display')
@inlineView(``)
export class UIInputDisplay {
constructor(public element: Element) { }
@bindable() value: any = '';
}
@autoinject()
@customElement('ui-input')
@inlineView(`
\${(maxlength-__value.length)}
×
`)
export class UIInput {
constructor(public element: Element) {
if (element.hasAttribute('email')) this.__type = 'email';
else if (element.hasAttribute('url')) this.__type = 'url';
else if (element.hasAttribute('file')) this.__type = 'file';
else if (element.hasAttribute('search')) this.__type = 'search';
else if (element.hasAttribute('number') || element.hasAttribute('number.bind')) this.__type = 'number';
else if (element.hasAttribute('decimal') || element.hasAttribute('decimal.bind')) this.__type = 'number';
else if (element.hasAttribute('password')) this.__type = 'password';
else this.__type = 'text';
if (element.hasAttribute('email')) this.__format = 'email';
else if (element.hasAttribute('url')) this.__format = 'url';
else if (element.hasAttribute('number') || element.hasAttribute('number.bind')) this.__format = 'number';
else if (element.hasAttribute('decimal') || element.hasAttribute('decimal.bind')) this.__format = 'decimal';
else this.__format = 'text';
this.__clear = element.hasAttribute('clear');
this.__counter = element.hasAttribute('charcount');
}
bind() {
this.disabled = isTrue(this.disabled);
this.readonly = isTrue(this.readonly);
if (this.width) this.element['style'].width = this.width;
if (this.value) this.valueChanged(this.value);
if (this.number) this.numberChanged(this.number);
if (this.decimal) this.decimalChanged(this.decimal);
}
__type;
__format;
__counter;
__clear;
__input;
__value = '';
errors = [];
/**
* @property
* @type
*/
@bindable({ defaultBindingMode: bindingMode.twoWay })
value: any = '';
/**
* @property
* @type
*/
@bindable({ defaultBindingMode: bindingMode.twoWay })
number = NaN;
/**
* @property
* @type
*/
@bindable({ defaultBindingMode: bindingMode.twoWay })
decimal = NaN;
@bindable() maxlength = 999;
@bindable() placeholder = '';
@bindable() disabled = false;
@bindable() readonly = false;
@bindable() dir = 'ltr';
@bindable() width;
clear() {
this.__value = this.value = ''; this.__input.focus();
UIEvent.fireEvent('change', this.element, this.value);
}
widthChanged(newValue) {
this.element['style'].width = newValue;
}
busy;
disable(disabled?) {
this.busy = disabled;
}
__ignoreChange = false;
valueChanged(newValue) {
if (this.__ignoreChange) return;
if (this.__format == 'email' || this.__format == 'url') newValue = (newValue || '').toLowerCase();
this.value = this.__value = newValue;
}
numberChanged(newValue) {
if (this.__ignoreChange) return;
this.__value = newValue;
}
decimalChanged(newValue) {
if (this.__ignoreChange) return;
this.__value = newValue;
}
format(evt) {
evt.stopPropagation();
this.__ignoreChange = true;
let start = 0;
try { start = evt.target.selectionStart; } catch (e) { }
if (this.__format == 'email' || this.__format == 'url') this.value = evt.target.value = evt.target.value.toLowerCase();
else if (this.__format == 'number') this.number = evt.target.valueAsNumber || '';
else if (this.__format == 'decimal') this.decimal = evt.target.valueAsNumber || '';
else this.value = evt.target.value;
try { evt.target.setSelectionRange(start, start); } catch (e) { }
UIEvent.queueTask(() => this.__ignoreChange = false);
UIEvent.fireEvent('input', this.element);
}
keyDown(evt) {
evt.stopPropagation();
let code = evt.keyCode || evt.which;
if (evt.ctrlKey || evt.metaKey || evt.altKey || code == 9 || code == 8) return true;
if (code == 13) return UIEvent.fireEvent('enterpressed', this.element);
if (this.__format == 'email') return /[a-zA-Z0-9\@\-\.\_\&\+]/.test(String.fromCharCode(code));
if (this.__format == 'url') return /[a-zA-Z0-9\/\-\.\_\?\#\%\=\;\:\{\[\]\}\&\+]/.test(String.fromCharCode(code));
if (this.__format == 'number') return /[0-9\-]/.test(String.fromCharCode(code));
if (this.__format == 'decimal') {
if (code == 46 && evt.target.value.indexOf('.') >= 0) return false;
return /[0-9\.\-]/.test(String.fromCharCode(code));
}
return true;
}
fireChange(evt) {
evt.stopPropagation();
if (this.__format == 'number') UIEvent.fireEvent('change', this.element, this.number);
else if (this.__format == 'decimal') UIEvent.fireEvent('change', this.element, this.decimal);
else UIEvent.fireEvent('change', this.element, this.value);
}
__focus;
fireBlur() {
this.__focus = false;
UIEvent.fireEvent('blur', this.element);
}
fireFocus() {
this.__focus = true;
UIEvent.fireEvent('focus', this.element);
}
}
@autoinject()
@customElement('ui-textarea')
@inlineView(`
`)
export class UITextarea {
constructor(public element: Element) {
this.__counter = element.hasAttribute('charcount');
this.__clear = element.hasAttribute('clear');
}
bind() {
this.disabled = isTrue(this.disabled);
this.readonly = isTrue(this.readonly);
if (!isEmpty(this.autoComplete)) this.autoCompleteChanged(this.autoComplete);
}
__counter;
__clear;
__input;
errors = [];
/**
* @property
* @type
*/
@bindable({ defaultBindingMode: bindingMode.twoWay })
value = '';
@bindable() rows = 5;
@bindable() dir = 'ltr';
@bindable() maxlength = 10000;
@bindable() placeholder = '';
@bindable() autoComplete = '';
@bindable() disabled = false;
@bindable() readonly = false;
clear() {
this.value = ''; this.__input.focus();
UIEvent.fireEvent('change', this.element, this.value);
}
busy;
disable(disabled?) {
this.busy = disabled;
}
fireChange(evt) {
evt.stopPropagation();
UIEvent.fireEvent('change', this.element, this.value);
}
fireInput(evt) {
evt.stopPropagation();
UIEvent.fireEvent('input', this.element);
}
__focus;
fireBlur() {
this.__focus = this.__showList = false;
UIEvent.fireEvent('blur', this.element);
}
fireFocus() {
this.__focus = true;
UIEvent.fireEvent('focus', this.element);
}
// autoComplete
__list;
__tether;
__hilight;
__listCss = { top: '0px', left: '0px', right: 'auto', width: '200px', 'max-height': '400px' };
__acRegExp;
__showList;
__autoComplete;
attached() {
if (!isEmpty(this.autoComplete)) {
this.__input.onkeyup = (evt) => this.showList(evt);
this.__input.onkeydown = (evt) => this.keyDown(evt);
this.__acRegExp = eval(`/\\b(\\w{1,})$/`);
this.__tether = new Tether({
element: this.__list,
target: this.element,
attachment: 'top left',
targetAttachment: 'top left',
// offset: '0 10px',
constraints: [
{
to: 'scrollParent',
attachment: 'together'
},
{
to: 'window',
attachment: 'together'
}
],
optimizations: {
moveElement: false
}
});
}
}
detached() {
if (this.__tether) this.__tether.destroy();
}
autoCompleteChanged(newValue) {
if (_.isString(newValue)) newValue = newValue.split(',');
this.autoComplete = newValue.sort();
}
showList(evt) {
if (evt.ctrlKey || evt.altKey || evt.metaKey || (evt.keyCode || evt.which) === 0) return true;
let code = (evt.keyCode || evt.which);
if (code == 13) {
return false;
}
let text = this.__input.value.substring(0, this.__input.selectionEnd);
let query = text.match(this.__acRegExp);
this.__showList = false;
if (query !== null) {
var rx = new RegExp(getAscii(query[1]), 'i');
this.__autoComplete = _.filter(this.autoComplete, v => {
let asc = getAscii(v);
return rx.test(asc);
});
let pos = this.getCaretCoordinates();
this.__listCss = Object.assign(this.__listCss, pos);
this.__showList = this.__autoComplete.length > 0;
}
return true;
}
keyDown(evt) {
if (evt.ctrlKey || evt.altKey || evt.metaKey || (evt.keyCode || evt.which) === 0) return true;
let code = (evt.keyCode || evt.which);
if (this.__showList) {
if (code == 13) {
let h = this.__list.querySelector('.ui-list-item.ui-highlight');
if (h !== null)
this.__replace(h.dataset.value);
this.__showList = false;
return false;
}
if (code === 38) {
let h = this.__list.querySelector('.ui-list-item.ui-highlight');
// if found hilight or selected get previous
if (h !== null) h = h.previousElementSibling;
// if no hilight get first
if (h === null) h = this.__list.querySelector('.ui-list-item');
if (h != null) {
if (h !== null) {
if (this.__hilight != null) this.__hilight.classList.remove('ui-highlight');
(this.__hilight = h).classList.add('ui-highlight');
this.__scrollIntoView();
}
}
evt.preventDefault();
return false;
}
else if (code === 40) {
let h = this.__list.querySelector('.ui-list-item.ui-highlight');
// if found hilight or selected get next
if (h !== null) h = h.nextElementSibling;
// if no selected get first
if (h === null) h = this.__list.querySelector('.ui-list-item');
if (h !== null) {
if (this.__hilight != null) this.__hilight.classList.remove('ui-highlight');
(this.__hilight = h).classList.add('ui-highlight');
this.__scrollIntoView();
}
evt.preventDefault();
return false;
}
}
return true;
}
__replace(selected) {
var pre = this.__input.value.substring(0, this.__input.selectionEnd);
var post = this.__input.value.substring(this.__input.selectionEnd);
pre = pre.replace(this.__acRegExp, ' ' + selected + ' ');
this.value = (pre + post);//.replace(/\s{2,}/g, ' ');
this.__input.selectionStart = this.__input.selectionEnd = pre.length;
}
__clicked($event) {
if ($event.button !== 0) return true;
let o: any = getParentByClass($event.target, 'ui-list-item', 'ui-list');
if (o !== null) {
this.__replace(o.dataset.value);
this.__showList = false;
}
}
__scrollIntoView() {
this.__list.scrollTop = (this.__hilight !== null ? this.__hilight.offsetTop - (this.__list.offsetHeight / 2) : 0);
}
// Compute autoComplete
properties = [
'direction', // RTL support
'boxSizing',
'width',
'height',
'overflowX',
'overflowY',
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth',
'borderStyle',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
'fontStyle',
'fontVariant',
'fontWeight',
'fontStretch',
'fontSize',
'fontSizeAdjust',
'lineHeight',
'fontFamily',
'textAlign',
'textTransform',
'textIndent',
'textDecoration',
'letterSpacing',
'wordSpacing',
'tabSize',
'MozTabSize'
];
isBrowser = (typeof window !== 'undefined');
isFirefox = (this.isBrowser && window['mozInnerScreenX'] != null);
getCaretCoordinates() {
let element: any = this.__input;
let position = this.__input.selectionStart;
if (!this.isBrowser) {
throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser');
}
var debug = false;
if (debug) {
var el = document.querySelector('#input-textarea-caret-position-mirror-div');
if (el) {
el.parentNode.removeChild(el);
}
}
// mirrored div
var div = document.createElement('div');
div.id = 'input-textarea-caret-position-mirror-div';
document.body.appendChild(div);
var style = div.style;
var computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
// default textarea styles
style.whiteSpace = 'pre-wrap';
if (element.nodeName !== 'INPUT')
style.wordWrap = 'break-word'; // only for textarea-s
// position off-screen
style.position = 'absolute'; // required to return coordinates properly
if (!debug)
style.visibility = 'hidden'; // not 'display: none' because we want rendering
// transfer the element's properties to the div
_.forEach(this.properties, prop => {
style[prop] = computed[prop];
});
if (this.isFirefox) {
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if (element.scrollHeight > parseInt(computed.height))
style.overflowY = 'scroll';
} else {
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
}
div.textContent = element.value.substring(0, position);
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
if (element.nodeName === 'INPUT')
div.textContent = div.textContent.replace(/\s/g, '\u00a0');
var span = document.createElement('span');
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the created at the caret position.
// for inputs, just '.' would be enough, but why bother?
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
div.appendChild(span);
var coordinates = {
top: (span.offsetTop + parseInt(computed['borderTopWidth']) + 20 - element.scrollTop) + 'px',
left: (span.offsetLeft + parseInt(computed['borderLeftWidth'])) + 'px'
};
if (debug) {
span.style.backgroundColor = '#aaa';
} else {
document.body.removeChild(div);
}
return coordinates;
}
}
@autoinject()
@customElement('ui-phone')
@inlineView(`
\${__prefix}
×
`)
export class UIPhone {
constructor(public element: Element) {
this.__clear = element.hasAttribute('clear');
}
bind() {
this.disabled = isTrue(this.disabled);
this.readonly = isTrue(this.readonly);
this.valueChanged(this.value);
if (this.__national = !isEmpty(this.country)) this.__prefix = '+' + PhoneLib.getDialingCode(this.country); else this.__ctry = PhoneLib.getIso2Code(this.value);
this.__placeholder = PhoneLib.getExample(this.country || 'us', PhoneLib.TYPE.FIXED_LINE_OR_MOBILE, this.__national);
}
__clear;
__input;
__ctry = '';
__value = '';
__prefix = '';
__national = false;
__placeholder = '';
errors = [];
/**
* @property
* @type
*/
@bindable({ defaultBindingMode: bindingMode.twoWay })
value: any = '';
/**
* @property
* @type
*/
@bindable({ defaultBindingMode: bindingMode.twoWay })
phone;
@bindable() country = '';
@bindable() disabled = false;
@bindable() readonly = false;
clear() {
this.__value = this.value = ''; this.__input.focus();
UIEvent.fireEvent('change', this.element, this.value);
}
busy;
disable(disabled?) {
this.busy = disabled;
}
__ignoreChange = false;
valueChanged(newValue) {
if (this.__ignoreChange) return;
if (isEmpty(newValue)) {
this.__value = '';
this.phone = null;
}
else {
this.__value = PhoneLib.formatInput(newValue, this.country);
this.phone = PhoneLib.getNumberInfo(newValue, this.country);
}
}
countryChanged(newValue) {
if (this.__national = !isEmpty(newValue)) this.__prefix = '+' + PhoneLib.getDialingCode(this.country); else this.__ctry = PhoneLib.getIso2Code(this.value);
this.__placeholder = PhoneLib.getExample(this.country || 'us', PhoneLib.TYPE.FIXED_LINE_OR_MOBILE, this.__national);
}
format(evt) {
this.__ignoreChange = true;
let val = evt.target.value;
// let start = evt.target.selectionStart;
if (!this.__national && !(/^\+/.test(val))) val = '+' + val;
if (!this.__national) this.__ctry = PhoneLib.getIso2Code(val);
evt.target.value = PhoneLib.formatInput(val, this.country);
this.value = PhoneLib.format(val, this.country, PhoneLib.FORMAT.FULL);
this.phone = PhoneLib.getNumberInfo(val, this.country);
// try { evt.target.setSelectionRange(start, start); } catch (e) { }
UIEvent.queueTask(() => this.__ignoreChange = false);
}
keyDown(evt) {
evt.stopPropagation();
let code = evt.keyCode || evt.which;
if (evt.ctrlKey || evt.metaKey || evt.altKey || code == 9 || code == 8) return true;
if (code == 13) return UIEvent.fireEvent('enterpressed', this.element);
return /[0-9]/.test(String.fromCharCode(code));
}
fireChange(evt) {
evt.stopPropagation();
UIEvent.fireEvent('change', this.element, { value: this.value, phone: this.phone });
}
__focus;
fireBlur() {
this.__focus = false;
UIEvent.fireEvent('blur', this.element);
}
fireFocus() {
this.__focus = true;
UIEvent.fireEvent('focus', this.element);
}
}