converted inputs to es6 classes

This commit is contained in:
Kevin Ross 2015-12-01 18:28:36 -06:00
parent b034b426bc
commit 68391413ab
8 changed files with 537 additions and 171 deletions

View File

@ -103,11 +103,11 @@ module.exports = function (grunt) {
'js/dist/util.js' : 'js/src/util.js',
'js/dist/ripples.js' : 'js/src/ripples.js',
'js/dist/autofill.js' : 'js/src/autofill.js',
'js/dist/input.js' : 'js/src/input.js',
'js/dist/checkbox.js' : 'js/src/checkbox.js',
'js/dist/togglebutton.js' : 'js/src/togglebutton.js',
'js/dist/radio.js' : 'js/src/radio.js',
//'js/dist/dropdown.js' : 'js/src/dropdown.js',
//'js/dist/modal.js' : 'js/src/modal.js',
'js/dist/fileinput.js' : 'js/src/fileinput.js',
//'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
//'js/dist/tab.js' : 'js/src/tab.js',
//'js/dist/tooltip.js' : 'js/src/tooltip.js',
@ -130,11 +130,11 @@ module.exports = function (grunt) {
'dist/js/umd/util.js' : 'js/src/util.js',
'dist/js/umd/ripples.js' : 'js/src/ripples.js',
'dist/js/umd/autofill.js' : 'js/src/autofill.js',
'dist/js/umd/input.js' : 'js/src/input.js',
'dist/js/umd/checkbox.js' : 'js/src/checkbox.js',
'dist/js/umd/togglebutton.js' : 'js/src/togglebutton.js',
'dist/js/umd/radio.js' : 'js/src/radio.js',
//'dist/js/umd/collapse.js' : 'js/src/collapse.js',
//'dist/js/umd/dropdown.js' : 'js/src/dropdown.js',
'dist/js/umd/fileinput.js' : 'js/src/fileinput.js',
//'dist/js/umd/modal.js' : 'js/src/modal.js',
//'dist/js/umd/scrollspy.js' : 'js/src/scrollspy.js',
//'dist/js/umd/tab.js' : 'js/src/tab.js',
@ -193,11 +193,11 @@ module.exports = function (grunt) {
'js/src/util.js',
'js/src/ripples.js',
'js/src/autofill.js',
'js/src/input.js',
'js/src/checkbox.js',
'js/src/togglebutton.js',
'js/src/radio.js',
//'js/src/dropdown.js',
//'js/src/modal.js',
'js/src/fileinput.js',
//'js/src/scrollspy.js',
//'js/src/tab.js',
//'js/src/tooltip.js',

View File

@ -1,5 +1,6 @@
//import Util from './util'
import Util from './util'
// Checkbox decorator, to be called after Input
const Checkbox = (($) => {
/**
@ -27,17 +28,32 @@ const Checkbox = (($) => {
this.config = $.extend({}, Default, config)
this.element.after(this.config.template)
this.formGroup = Util.findFormGroup(this.element)
this._bindEventListeners()
}
dispose() {
$.removeData(this.element, DATA_KEY)
this.element = null
this.formGroup = null
this.config = null
}
// ------------------------------------------------------------------------
// private
_bindEventListeners() {
// checkboxes didn't appear to bubble to the document, so we'll bind these directly
this.formGroup.find('.checkbox label').hover(() => {
Util.addFormGroupFocus(this.formGroup)
}, () => {
Util.removeFormGroupFocus(this.formGroup)
})
this.element.change(() => {
this.element.blur()
})
}
// ------------------------------------------------------------------------
// static

112
js/src/fileinput.js Normal file
View File

@ -0,0 +1,112 @@
import Util from './util'
// FileInput decorator, to be called after Input
const FileInput = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'fileInput'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {}
const ClassName = {
IS_FILEINPUT: 'is-fileinput',
IS_EMPTY: 'is-empty'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class FileInput {
constructor(element, config) {
this.element = element
this.config = $.extend({}, Default, config)
this.formGroup = Util.findFormGroup(this.element)
this.formGroup.addClass(ClassName.IS_FILEINPUT)
this._bindEventListeners()
}
dispose() {
$.removeData(this.element, DATA_KEY)
this.element = null
this.formGroup = null
this.config = null
}
// ------------------------------------------------------------------------
// private
_bindEventListeners() {
this.formGroup.on('focus', () => {
Util.addFormGroupFocus(this.formGroup)
})
.on('blur', () => {
Util.removeFormGroupFocus(this.formGroup)
})
// set the fileinput readonly field with the name of the file
this.element.on('change', () => {
let value = ''
$.each(this.element.files, (i, file) => {
value += `${file.name} , `
})
value = value.substring(0, value.length - 2)
if (value) {
this._removeIsEmpty()
} else {
this._addIsEmpty()
}
this.formGroup.find('input.form-control[readonly]').val(value)
})
}
_addIsEmpty() {
this.formGroup.addClass(ClassName.IS_EMPTY)
}
_removeIsEmpty() {
this.formGroup.removeClass(ClassName.IS_EMPTY)
}
// ------------------------------------------------------------------------
// static
static _jQueryInterface(config) {
return this.each(function () {
let $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new FileInput(this, config)
$element.data(DATA_KEY, data)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = FileInput._jQueryInterface
$.fn[NAME].Constructor = FileInput
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return FileInput._jQueryInterface
}
return FileInput
})(jQuery)
export default FileInput

199
js/src/input.js Normal file
View File

@ -0,0 +1,199 @@
import Util from './util'
const Input = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'input'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
convertInputSizeVariations: true,
template: `<span class='material-input'></span>`,
formGroup: {
template: `"<div class='form-group'></div>`
}
}
const InputSizeConversions = {
"input-lg": "form-group-lg",
"input-sm": "form-group-sm"
}
const ClassName = {
IS_EMPTY: 'is-empty',
FORM_GROUP: 'form-group',
HAS_ERROR: 'has-error'
}
const Selector = {
FORM_GROUP: `.${ClassName.FORM_GROUP}` //,
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Input {
constructor(element, config) {
this.element = element
this.config = $.extend({}, Default, config)
// Requires form-group standard markup (will add it if necessary)
this.formGroup = this._findOrCreateFormGroup()
this._convertInputSizeVariations()
// Initially mark as empty
if (this._isEmpty()) {
this.formGroup.addClass(ClassName.IS_EMPTY)
}
// Add marker div the end of the form-group
this.formGroup.append(this.config.template)
this._bindEventListeners()
}
dispose() {
$.removeData(this.element, DATA_KEY)
this.element = null
this.formGroup = null
this.config = null
}
// ------------------------------------------------------------------------
// private
_bindEventListeners() {
this.element
.on('keydown paste', (event) => {
if (Util.isChar(event)) {
this._removeIsEmpty()
}
})
.on('keyup change', (event) => {
let isValid = (typeof this.element[0].checkValidity === "undefined" || this.element[0].checkValidity())
if (this.element.val() === "" && isValid) {
this._addIsEmpty()
} else {
this._removeIsEmpty()
}
// Validation events do not bubble, so they must be attached directly to the input: http://jsfiddle.net/PEpRM/1/
// Further, even the bind method is being caught, but since we are already calling #checkValidity here, just alter
// the form-group on change.
//
// NOTE: I'm not sure we should be intervening regarding validation, this seems better as a README and snippet of code.
// BUT, I've left it here for backwards compatibility.
if (isValid) {
this._removeHasError()
} else {
this._addHasError()
}
})
.on('focus', () => {
Util.addFormGroupFocus(this.formGroup)
})
.on('blur', () => {
Util.removeFormGroupFocus(this.formGroup)
})
// make sure empty is added back when there is a programmatic value change.
// NOTE: programmatic changing of value using $.val() must trigger the change event i.e. $.val('x').trigger('change')
.on('change', () => {
if (this.element.attr('type') === 'file') {
return
}
let value = this.element.val()
if (value) {
this._removeIsEmpty()
} else {
this._addIsEmpty()
}
})
}
_addHasError() {
this.formGroup.addClass(ClassName.HAS_ERROR)
}
_removeHasError() {
this.formGroup.removeClass(ClassName.HAS_ERROR)
}
_addIsEmpty() {
this.formGroup.addClass(ClassName.IS_EMPTY)
}
_removeIsEmpty() {
this.formGroup.removeClass(ClassName.IS_EMPTY)
}
_isEmpty() {
return (this.element.val() === null || this.element.val() === undefined || this.element.val() === "")
}
_convertInputSizeVariations() {
if (!this.config.convertInputSizeVariations) {
return
}
// Modification - Change input-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants)
for (let inputSize in InputSizeConversions) {
if (this.element.hasClass(inputSize)) {
this.element.removeClass(inputSize)
this.formGroup.addClass(InputSizeConversions[inputSize])
}
}
}
_findOrCreateFormGroup() {
let fg = this.element.closest(Selector.FORM_GROUP) // note that form-group may be grandparent in the case of an input-group
if (fg.length === 0) {
this.element.wrap(this.config.formGroup.template)
fg = this.element.closest(Selector.FORM_GROUP) // find node after attached (otherwise additional attachments don't work)
}
return fg
}
// ------------------------------------------------------------------------
// static
static _jQueryInterface(config) {
return this.each(function () {
let $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Input(this, config)
$element.data(DATA_KEY, data)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Input._jQueryInterface
$.fn[NAME].Constructor = Input
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Input._jQueryInterface
}
return Input
})(jQuery)
export default Input

View File

@ -2,15 +2,15 @@
(function($) {
// Selector to select only not already processed elements
$.expr[":"].notmdproc = function(obj){
if ($(obj).data("mdproc")) {
return false;
} else {
return true;
}
};
//function _isChar(evt) {
//$.expr[":"].notmdproc = function(obj){
// if ($(obj).data("mdproc")) {
// return false;
// } else {
// return true;
// }
//};
//
//function isChar(evt) {
// if (typeof evt.which == "undefined") {
// return true;
// } else if (typeof evt.which == "number" && evt.which > 0) {
@ -18,14 +18,14 @@
// }
// return false;
//}
function _addFormGroupFocus(element){
$(element).closest(".form-group").addClass("is-focused");
}
function _removeFormGroupFocus(element){
$(element).closest(".form-group").removeClass("is-focused"); // remove class from form-group
}
//
//function addFormGroupFocus(formGroup){
// formGroup.addClass("is-focused");
//}
//
//function removeFormGroupFocus(formGroup){
// formGroup.removeClass("is-focused"); // remove class from form-group
//}
$.material = {
"options": {
@ -50,7 +50,8 @@
"inputElements": "input.form-control, textarea.form-control, select.form-control",
"checkboxElements": ".checkbox > label > input[type=checkbox]",
"togglebuttonElements": ".togglebutton > label > input[type=checkbox]",
"radioElements": ".radio > label > input[type=radio]"
"radioElements": ".radio > label > input[type=radio]" ,
"fileInputElements": 'input[type=file]'
},
//"checkbox": function(selector) {
// // Add fake-checkbox to material checkboxes
@ -72,133 +73,133 @@
// .data("mdproc", true)
// .after("<span class='circle'></span><span class='check'></span>");
//},
"input": function(selector) {
$((selector) ? selector : this.options.inputElements)
.filter(":notmdproc")
.data("mdproc", true)
.each( function() {
var $input = $(this);
// Requires form-group standard markup (will add it if necessary)
var $formGroup = $input.closest(".form-group"); // note that form-group may be grandparent in the case of an input-group
if($formGroup.length === 0){
$input.wrap("<div class='form-group'></div>");
$formGroup = $input.closest(".form-group"); // find node after attached (otherwise additional attachments don't work)
}
// Modification - Change input-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants)
var legacySizes = {
"input-lg": "form-group-lg",
"input-sm": "form-group-sm"
};
$.each( legacySizes, function( legacySize, standardSize ) {
if ($input.hasClass(legacySize)) {
$input.removeClass(legacySize);
$formGroup.addClass(standardSize);
}
}); // TODO: determine if we want to keep meddling this much.
// Set as empty if is empty (damn I must improve this...)
if ($input.val() === null || $input.val() == "undefined" || $input.val() === "") {
$formGroup.addClass("is-empty");
}
// Add at the end of the form-group
$formGroup.append("<span class='material-input'></span>");
// Support for file input
if ($formGroup.find("input[type=file]").length > 0) {
$formGroup.addClass("is-fileinput");
}
});
},
"attachInputEventHandlers": function() {
// checkboxes didn't appear to bubble to the document, so we'll bind these directly
$(".form-group .checkbox label").hover(function() {
_addFormGroupFocus(this);
}, function() {
_removeFormGroupFocus(this);
});
$(document)
.on("change", ".checkbox input[type=checkbox]", function() { $(this).blur(); })
.on("keydown paste", ".form-control", function(e) {
if(_isChar(e)) {
$(this).closest(".form-group").removeClass("is-empty");
}
})
.on("keyup change", ".form-control", function() {
var $input = $(this);
var $formGroup = $input.closest(".form-group");
var isValid = (typeof $input[0].checkValidity === "undefined" || $input[0].checkValidity());
if ($input.val() === "" && isValid) {
$formGroup.addClass("is-empty");
}
else {
$formGroup.removeClass("is-empty");
}
// Validation events do not bubble, so they must be attached directly to the input: http://jsfiddle.net/PEpRM/1/
// Further, even the bind method is being caught, but since we are already calling #checkValidity here, just alter
// the form-group on change.
//
// NOTE: I'm not sure we should be intervening regarding validation, this seems better as a README and snippet of code.
// BUT, I've left it here for backwards compatibility.
if(isValid){
$formGroup.removeClass("has-error");
}
else{
$formGroup.addClass("has-error");
}
})
.on("focus", ".form-control, .form-group.is-fileinput", function() {
_addFormGroupFocus(this);
})
.on("blur", ".form-control, .form-group.is-fileinput", function() {
_removeFormGroupFocus(this);
})
// make sure empty is added back when there is a programmatic value change.
// NOTE: programmatic changing of value using $.val() must trigger the change event i.e. $.val('x').trigger('change')
.on("change", ".form-group input", function() {
var $input = $(this);
if($input.attr("type") == "file") {
return;
}
var $formGroup = $input.closest(".form-group");
var value = $input.val();
if (value) {
$formGroup.removeClass("is-empty");
} else {
$formGroup.addClass("is-empty");
}
})
// set the fileinput readonly field with the name of the file
.on("change", ".form-group.is-fileinput input[type='file']", function() {
var $input = $(this);
var $formGroup = $input.closest(".form-group");
var value = "";
$.each(this.files, function(i, file) {
value += file.name + ", ";
});
value = value.substring(0, value.length - 2);
if (value) {
$formGroup.removeClass("is-empty");
} else {
$formGroup.addClass("is-empty");
}
$formGroup.find("input.form-control[readonly]").val(value);
});
},
"ripples": function(selector) {
$((selector) ? selector : this.options.withRipples).ripples();
},
//"autofill": function() {
//"input": function(selector) {
// $((selector) ? selector : this.options.inputElements)
// .filter(":notmdproc")
// .data("mdproc", true)
// .each( () => {
// var $input = $(this);
//
// // Requires form-group standard markup (will add it if necessary)
// var $formGroup = $input.closest(".form-group"); // note that form-group may be grandparent in the case of an input-group
// if($formGroup.length === 0){
// $input.wrap("<div class='form-group'></div>");
// $formGroup = $input.closest(".form-group"); // find node after attached (otherwise additional attachments don't work)
// }
//
// // Modification - Change input-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants)
// var legacySizes = {
// "input-lg": "form-group-lg",
// "input-sm": "form-group-sm"
// };
// $.each( legacySizes, function( legacySize, standardSize ) {
// if ($input.hasClass(legacySize)) {
// $input.removeClass(legacySize);
// $formGroup.addClass(standardSize);
// }
// }); // TODO: determine if we want to keep meddling this much.
//
// // Set as empty if is empty (damn I must improve this...)
// if ($input.val() === null || $input.val() == "undefined" || $input.val() === "") {
// $formGroup.addClass("is-empty");
// }
//
// // Add at the end of the form-group
// $formGroup.append("<span class='material-input'></span>");
//
// // Support for file input
// if ($formGroup.find("input[type=file]").length > 0) {
// $formGroup.addClass("is-fileinput");
// }
// });
//},
//"attachInputEventHandlers": () => {
//
//// checkboxes didn't appear to bubble to the document, so we'll bind these directly
//$(".form-group .checkbox label").hover(() => {
// Util.addFormGroupFocus(this);
//}, () => {
// Util.removeFormGroupFocus(this);
//});
//
//$(document)
//.on("change", ".checkbox input[type=checkbox]", () => { $(this).blur(); })
//.on("keydown paste", ".form-control", function(e) {
// if(Util.isChar(e)) {
// $(this).closest(".form-group").removeClass("is-empty");
// }
//})
//.on("keyup change", ".form-control", () => {
// var $input = $(this);
// var $formGroup = $input.closest(".form-group");
// var isValid = (typeof $input[0].checkValidity === "undefined" || $input[0].checkValidity());
//
// if ($input.val() === "" && isValid) {
// $formGroup.addClass("is-empty");
// }
// else {
// $formGroup.removeClass("is-empty");
// }
//
// // Validation events do not bubble, so they must be attached directly to the input: http://jsfiddle.net/PEpRM/1/
// // Further, even the bind method is being caught, but since we are already calling #checkValidity here, just alter
// // the form-group on change.
// //
// // NOTE: I'm not sure we should be intervening regarding validation, this seems better as a README and snippet of code.
// // BUT, I've left it here for backwards compatibility.
// if(isValid){
// $formGroup.removeClass("has-error");
// }
// else{
// $formGroup.addClass("has-error");
// }
//})
//.on("focus", ".form-control, .form-group.is-fileinput", () => {
// Util.addFormGroupFocus(this.formGroup);
//})
//.on("blur", ".form-control, .form-group.is-fileinput", () => {
// Util.removeFormGroupFocus(this.formGroup);
//})
//// make sure empty is added back when there is a programmatic value change.
//// NOTE: programmatic changing of value using $.val() must trigger the change event i.e. $.val('x').trigger('change')
//.on("change", ".form-group input", () => {
// var $input = $(this);
// if($input.attr("type") == "file") {
// return;
// }
//
// var $formGroup = $input.closest(".form-group");
// var value = $input.val();
// if (value) {
// $formGroup.removeClass("is-empty");
// } else {
// $formGroup.addClass("is-empty");
// }
//})
//// set the fileinput readonly field with the name of the file
//.on("change", ".form-group.is-fileinput input[type='file']", () => {
// var $input = $(this);
// var $formGroup = $input.closest(".form-group");
// var value = "";
// $.each(this.files, function(i, file) {
// value += file.name + ", ";
// });
// value = value.substring(0, value.length - 2);
// if (value) {
// $formGroup.removeClass("is-empty");
// } else {
// $formGroup.addClass("is-empty");
// }
// $formGroup.find("input.form-control[readonly]").val(value);
//});
//},
//"ripples": function(selector) {
// $((selector) ? selector : this.options.withRipples).ripples();
//},
//"autofill": () => {
// // This part of code will detect autofill when the page is loading (username and password inputs for example)
// var loading = setInterval(function() {
// $("input[type!=checkbox]").each(function() {
// var loading = setInterval(() => {
// $("input[type!=checkbox]").each(() => {
// var $this = $(this);
// if ($this.val() && $this.val() !== $this.attr("value")) {
// $this.triggerStart("change");
@ -207,18 +208,18 @@
// }, 100);
//
// // After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them
// setTimeout(function() {
// setTimeout(() => {
// clearInterval(loading);
// }, 10000);
//},
//"attachAutofillEventHandlers": function() {
//"attachAutofillEventHandlers": () => {
// // Listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus)
// var focused;
// $(document)
// .on("focus", "input", function() {
// .on("focus", "input", () => {
// var $inputs = $(this).parents("form").find("input").not("[type=file]");
// focused = setInterval(function() {
// $inputs.each(function() {
// focused = setInterval(() => {
// $inputs.each(() => {
// var $this = $(this);
// if ($this.val() !== $this.attr("value")) {
// $this.triggerStart("change");
@ -226,19 +227,22 @@
// });
// }, 100);
// })
// .on("blur", ".form-group input", function() {
// .on("blur", ".form-group input", () => {
// clearInterval(focused);
// });
//},
"init": function() {
"init": () => {
var $document = $(document);
if ($.fn.ripples && this.options.ripples) {
this.ripples();
//this.ripples();
$(this.options.withRipples).ripples()
}
if (this.options.input) {
this.input();
this.attachInputEventHandlers();
$(this.options.inputElements).input()
}
if (this.options.checkbox) {
//this.checkbox();
@ -255,36 +259,41 @@
if (this.options.autofill) {
//this.autofill();
//this.attachAutofillEventHandlers();
new Autofill()
new Autofill() // FIXME: if this is the best way to invoke, perhaps it shouldn't be a jquery fn as well?
}
$(this.options.fileInputElements).fileInput()
if (document.arrive && this.options.arrive) {
if ($.fn.ripples && this.options.ripples) {
$document.arrive(this.options.withRipples, function() {
$.material.ripples($(this));
$document.arrive(this.options.withRipples, () => {
$(this).ripples()
});
}
if (this.options.input) {
$document.arrive(this.options.inputElements, function() {
$.material.input($(this));
$document.arrive(this.options.inputElements, () => {
$(this).input()
});
}
if (this.options.checkbox) {
$document.arrive(this.options.checkboxElements, function() {
$document.arrive(this.options.checkboxElements, () => {
$(this).checkbox();
});
}
if (this.options.radio) {
$document.arrive(this.options.radioElements, function() {
$document.arrive(this.options.radioElements, () => {
$(this).radio();
});
}
if (this.options.togglebutton) {
$document.arrive(this.options.togglebuttonElements, function() {
$document.arrive(this.options.togglebuttonElements, () => {
$(this).togglebutton();
});
}
$document.arrive(this.options.fileInputElements, () => {
$(this).fileInput();
});
}
}
};

View File

@ -1,5 +1,6 @@
//import Util from './util'
// Radio decorator, to be called after Input
const Radio = (($) => {
/**

View File

@ -1,5 +1,6 @@
//import Util from './util'
// Togglebutton decorator, to be called after Input
const Togglebutton = (($) => {
/**

View File

@ -16,6 +16,15 @@ const Util = (($) => {
transition: 'transitionend'
}
const ClassName = {
IS_FOCUSED: 'is-focused',
FORM_GROUP: 'form-group'
}
const Selector = {
FORM_GROUP: `.${ClassName.FORM_GROUP}` //,
}
function transitionEndTest() {
if (window.QUnit) {
return false
@ -57,13 +66,32 @@ const Util = (($) => {
return transitionEndSelector
},
isChar(evt) {
if (typeof evt.which === "undefined") {
isChar(event) {
if (typeof event.which === "undefined") {
return true
} else if (typeof evt.which === "number" && evt.which > 0) {
return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which !== 8 && evt.which !== 9
} else if (typeof event.which === "number" && event.which > 0) {
return !event.ctrlKey && !event.metaKey && !event.altKey && event.which !== 8 && event.which !== 9
}
return false
},
addFormGroupFocus(formGroup) {
formGroup.addClass(ClassName.IS_FOCUSED)
},
removeFormGroupFocus(formGroup) {
formGroup.removeClass(ClassName.IS_FOCUSED)
},
/**
Find expected form-group
*/
findFormGroup(element) {
let fg = element.closest(Selector.FORM_GROUP) // note that form-group may be grandparent in the case of an input-group
if (fg.length === 0) {
$.error(`Failed to find form-group for ${element}`)
}
return fg
}
}