2015-12-06 02:45:38 +03:00
import Util from './util'
2015-12-05 23:00:40 +03:00
const BaseInput = ( ( $ ) => {
const Default = {
formGroup : {
2015-12-06 06:24:05 +03:00
required : false
2015-12-06 03:55:29 +03:00
} ,
mdbFormGroup : {
2015-12-07 18:04:32 +03:00
template : ` <span class='mdb-form-group'></span> `
2015-12-05 23:00:40 +03:00
} ,
2015-12-06 02:45:38 +03:00
requiredClasses : [ ] ,
invalidComponentMatches : [ ] ,
convertInputSizeVariations : true
2015-12-05 23:00:40 +03:00
}
const ClassName = {
FORM _GROUP : 'form-group' ,
2015-12-06 03:55:29 +03:00
MDB _FORM _GROUP : 'mdb-form-group' ,
2015-12-05 23:00:40 +03:00
HAS _ERROR : 'has-error' ,
2015-12-06 03:55:29 +03:00
IS _EMPTY : 'is-empty' ,
IS _FOCUSED : 'is-focused'
2015-12-05 23:00:40 +03:00
}
const Selector = {
2015-12-06 03:55:29 +03:00
FORM _GROUP : ` . ${ ClassName . FORM _GROUP } ` ,
MDB _FORM _GROUP : ` . ${ ClassName . MDB _FORM _GROUP } `
2015-12-05 23:00:40 +03:00
}
2015-12-06 02:45:38 +03:00
const FormControlSizeConversions = {
2015-12-07 18:04:32 +03:00
'form-control-lg' : 'mdb-form-group-lg' ,
'form-control-sm' : 'mdb-form-group-sm'
2015-12-06 02:45:38 +03:00
}
2015-12-05 23:00:40 +03:00
/ * *
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* Class Definition
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* /
class BaseInput {
2015-12-06 06:24:05 +03:00
/ * *
*
* @ param element
* @ param config
* @ param properties - anything that needs to be set as this [ key ] = value . Works around the need to call ` super ` before using ` this `
* /
2015-12-07 20:40:42 +03:00
constructor ( $element , config , properties = { } ) {
this . $element = $element
2015-12-06 02:45:38 +03:00
this . config = $ . extend ( { } , Default , config )
2015-12-05 23:00:40 +03:00
2015-12-06 06:24:05 +03:00
// set properties for use in the constructor initialization
for ( let key in properties ) {
this [ key ] = properties [ key ]
}
2015-12-05 23:00:40 +03:00
// Enforce no overlap between components to prevent side effects
this . _rejectInvalidComponentMatches ( )
// Enforce expected structure (if any)
this . rejectWithoutRequiredStructure ( )
2015-12-06 00:07:37 +03:00
// Enforce required classes for a consistent rendering
this . _rejectWithoutRequiredClasses ( )
2015-12-07 18:04:32 +03:00
// Resolve the form-group first, it will be used for mdb-form-group if possible
// note: different components have different rules
2015-12-05 23:00:40 +03:00
this . $formGroup = this . findFormGroup ( this . config . formGroup . required )
2015-12-07 18:04:32 +03:00
// Will add mdb-form-group to form-group or create an mdb-form-group
2015-12-07 20:40:42 +03:00
// Performance Note: for those forms that are really performance driven, create the markup with the .mdb-form-group to avoid
// rendering changes once added.
2015-12-07 18:04:32 +03:00
this . $mdbFormGroup = this . resolveMdbFormGroup ( )
// Signal to the mdb-form-group that a form-control-* variation is being used
this . resolveMdbFormGroupSizing ( )
2015-12-06 02:45:38 +03:00
2015-12-05 23:00:40 +03:00
this . addFocusListener ( )
this . addChangeListener ( )
}
dispose ( dataKey ) {
$ . removeData ( this . $element , dataKey )
this . $element = null
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup = null
2015-12-05 23:00:40 +03:00
this . config = null
}
// ------------------------------------------------------------------------
// protected
2015-12-06 00:19:06 +03:00
rejectWithoutRequiredStructure ( ) {
2015-12-05 23:00:40 +03:00
// implement
}
addFocusListener ( ) {
2015-12-06 02:45:38 +03:00
this . $element
. on ( 'focus' , ( ) => {
this . addFormGroupFocus ( )
} )
. on ( 'blur' , ( ) => {
this . removeFormGroupFocus ( )
} )
2015-12-05 23:00:40 +03:00
}
addChangeListener ( ) {
2015-12-06 02:45:38 +03:00
this . $element
. on ( 'keydown paste' , ( event ) => {
if ( Util . isChar ( event ) ) {
this . removeIsEmpty ( )
}
} )
. on ( 'keyup change' , ( event ) => {
// 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')
if ( this . $element . val ( ) ) {
this . addIsEmpty ( )
} else {
this . removeIsEmpty ( )
}
// Validation events do not bubble, so they must be attached directly to the text: 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.
let isValid = ( typeof this . $element [ 0 ] . checkValidity === 'undefined' || this . $element [ 0 ] . checkValidity ( ) )
if ( isValid ) {
this . removeHasError ( )
} else {
this . addHasError ( )
}
} )
2015-12-05 23:00:40 +03:00
}
2015-12-06 00:07:37 +03:00
addFormGroupFocus ( ) {
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup . addClass ( ClassName . IS _FOCUSED )
2015-12-05 23:00:40 +03:00
}
2015-12-06 00:07:37 +03:00
removeFormGroupFocus ( ) {
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup . removeClass ( ClassName . IS _FOCUSED )
2015-12-05 23:00:40 +03:00
}
addHasError ( ) {
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup . addClass ( ClassName . HAS _ERROR )
2015-12-05 23:00:40 +03:00
}
removeHasError ( ) {
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup . removeClass ( ClassName . HAS _ERROR )
2015-12-05 23:00:40 +03:00
}
addIsEmpty ( ) {
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup . addClass ( ClassName . IS _EMPTY )
2015-12-05 23:00:40 +03:00
}
removeIsEmpty ( ) {
2015-12-06 03:55:29 +03:00
this . $mdbFormGroup . removeClass ( ClassName . IS _EMPTY )
2015-12-05 23:00:40 +03:00
}
isEmpty ( ) {
return ( this . $element . val ( ) === null || this . $element . val ( ) === undefined || this . $element . val ( ) === '' )
}
2015-12-07 18:04:32 +03:00
// Will add mdb-form-group to form-group or create a mdb-form-group if necessary
resolveMdbFormGroup ( ) {
let mfg = this . findMdbFormGroup ( false )
if ( mfg === undefined || mfg . length === 0 ) {
if ( this . $formGroup === undefined || this . $formGroup . length === 0 ) {
// If a form-group doesn't exist (not recommended), take a guess and wrap the element (assuming no label).
// note: it's possible to make this smarter, but I need to see valid cases before adding any complexity.
this . outerElement ( ) . wrap ( this . config . mdbFormGroup . template )
} else {
// a form-group does exist, add our marker class to it
this . $formGroup . addClass ( ClassName . MDB _FORM _GROUP )
// OLD: may want to implement this after all, see how the styling turns out, but using an existing form-group is less manipulation of the dom and therefore preferable
// A form-group does exist, so add an mdb-form-group wrapping it's internal contents
//fg.wrapInner(this.config.mdbFormGroup.template)
}
mfg = this . findMdbFormGroup ( true )
2015-12-05 23:00:40 +03:00
}
2015-12-07 18:04:32 +03:00
return mfg
2015-12-05 23:00:40 +03:00
}
// Demarcation element (e.g. first child of a form-group)
// Subclasses such as file inputs may have different structures
2015-12-06 00:19:06 +03:00
outerElement ( ) {
2015-12-05 23:00:40 +03:00
return this . $element
}
2015-12-06 03:55:29 +03:00
// Find mdb-form-group
findMdbFormGroup ( raiseError = true ) {
let mfg = this . $element . closest ( Selector . MDB _FORM _GROUP )
if ( mfg . length === 0 && raiseError ) {
$ . error ( ` Failed to find ${ Selector . MDB _FORM _GROUP } for ${ Util . describe ( this . $element ) } ` )
}
return mfg
}
// Find mdb-form-group
2015-12-05 23:00:40 +03:00
findFormGroup ( raiseError = true ) {
2015-12-06 03:55:29 +03:00
let fg = this . $element . closest ( Selector . FORM _GROUP )
2015-12-05 23:00:40 +03:00
if ( fg . length === 0 && raiseError ) {
2015-12-06 03:55:29 +03:00
$ . error ( ` Failed to find ${ Selector . FORM _GROUP } for ${ Util . describe ( this . $element ) } ` )
2015-12-05 23:00:40 +03:00
}
return fg
}
2015-12-07 18:04:32 +03:00
// Due to the interconnected nature of labels/inputs/help-blocks, signal the mdb-form-group-* size variation based on
// a found form-control-* size
resolveMdbFormGroupSizing ( ) {
if ( ! this . config . convertInputSizeVariations ) {
return
}
// Modification - Change text-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants)
for ( let inputSize in FormControlSizeConversions ) {
if ( this . $element . hasClass ( inputSize ) ) {
//this.$element.removeClass(inputSize)
this . $mdbFormGroup . addClass ( FormControlSizeConversions [ inputSize ] )
}
}
}
2015-12-05 23:00:40 +03:00
// ------------------------------------------------------------------------
// private
2015-12-06 00:19:06 +03:00
_rejectInvalidComponentMatches ( ) {
2015-12-06 02:45:38 +03:00
for ( let otherComponent of this . config . invalidComponentMatches ) {
2015-12-06 00:19:06 +03:00
otherComponent . rejectMatch ( this . constructor . name , this . $element )
2015-12-05 23:00:40 +03:00
}
}
2015-12-06 00:19:06 +03:00
_rejectWithoutRequiredClasses ( ) {
2015-12-06 02:45:38 +03:00
for ( let requiredClass of this . config . requiredClasses ) {
let found = false
// allow one of several classes to be passed in x||y
if ( requiredClass . indexOf ( '||' ) !== - 1 ) {
let oneOf = requiredClass . split ( '||' )
for ( let requiredClass of oneOf ) {
if ( this . $element . hasClass ( requiredClass ) ) {
found = true
break
}
}
} else if ( this . $element . hasClass ( requiredClass ) ) {
found = true
}
// error if not found
if ( ! found ) {
$ . error ( ` ${ this . constructor . name } element: ${ Util . describe ( this . $element ) } requires class: ${ requiredClass } ` )
}
}
}
2015-12-05 23:00:40 +03:00
// ------------------------------------------------------------------------
// static
}
return BaseInput
} ) ( jQuery )
export default BaseInput