2015-12-06 02:45:38 +03:00
import Util from './util'
2015-12-05 23:00:40 +03:00
const BaseInput = ( ( $ ) => {
2015-12-11 20:07:03 +03:00
const ClassName = {
FORM _GROUP : 'form-group' ,
MDB _FORM _GROUP : 'mdb-form-group' ,
MDB _LABEL : 'mdb-label' ,
2015-12-12 00:13:33 +03:00
MDB _LABEL _STATIC : 'mdb-label-static' ,
2015-12-11 20:07:03 +03:00
MDB _LABEL _PLACEHOLDER : 'mdb-label-placeholder' ,
MDB _LABEL _FLOATING : 'mdb-label-floating' ,
HAS _DANGER : 'has-danger' ,
2015-12-12 00:13:33 +03:00
IS _FILLED : 'is-filled' ,
2015-12-11 20:07:03 +03:00
IS _FOCUSED : 'is-focused'
}
const Selector = {
FORM _GROUP : ` . ${ ClassName . FORM _GROUP } ` ,
MDB _FORM _GROUP : ` . ${ ClassName . MDB _FORM _GROUP } ` ,
MDB _LABEL _WILDCARD : ` label[class^=' ${ ClassName . MDB _LABEL } '], label[class*=' ${ ClassName . MDB _LABEL } '] ` // match any label variant if specified
}
2015-12-05 23:00:40 +03:00
const Default = {
2015-12-08 03:07:21 +03:00
validate : false ,
2015-12-05 23:00:40 +03:00
formGroup : {
2015-12-06 06:24:05 +03:00
required : false
2015-12-06 03:55:29 +03:00
} ,
mdbFormGroup : {
2015-12-14 23:19:17 +03:00
template : ` <span class='mdb-form-group'></span> ` ,
2015-12-15 01:42:22 +03:00
create : true , // create a wrapper if form-group not found
required : true // not recommended to turn this off, only used for inline components
2015-12-05 23:00:40 +03:00
} ,
2015-12-11 20:07:03 +03:00
mdbLabel : {
required : false ,
// Prioritized find order for resolving the label to be used as an mdb-label if not specified in the markup
// - a function(thisComponent); or
// - a string selector used like $mdbFormGroup.find(selector)
//
// Note this only runs if $mdbFormGroup.find(Selector.MDB_LABEL_WILDCARD) fails to find a label (as authored in the markup)
//
selectors : [
` .form-control-label ` , // in the case of horizontal or inline forms, this will be marked
2015-12-11 20:32:00 +03:00
` > label ` // usual case for text inputs, first child. Deeper would find toggle labels so don't do that.
2015-12-11 20:07:03 +03:00
] ,
2015-12-12 00:13:33 +03:00
className : ClassName . MDB _LABEL _STATIC
2015-12-11 20:07:03 +03:00
} ,
2015-12-06 02:45:38 +03:00
requiredClasses : [ ] ,
invalidComponentMatches : [ ] ,
convertInputSizeVariations : true
2015-12-05 23:00:40 +03:00
}
2015-12-11 20:07:03 +03:00
const FormControlSizeMarkers = {
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-11 20:07:03 +03:00
this . config = $ . extend ( true , { } , 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-15 01:42:22 +03:00
this . $mdbFormGroup = this . resolveMdbFormGroup ( )
2015-12-07 18:04:32 +03:00
2015-12-11 20:07:03 +03:00
// Resolve and mark the mdbLabel if necessary as defined by the config
this . $mdbLabel = this . resolveMdbLabel ( )
2015-12-07 18:04:32 +03:00
// 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 ) ) {
2015-12-12 00:13:33 +03:00
this . addIsFilled ( )
2015-12-06 02:45:38 +03:00
}
} )
. 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')
2015-12-12 00:13:33 +03:00
if ( this . isEmpty ( ) ) {
this . removeIsFilled ( )
2015-12-06 02:45:38 +03:00
} else {
2015-12-12 00:13:33 +03:00
this . addIsFilled ( )
2015-12-06 02:45:38 +03:00
}
2015-12-08 03:07:21 +03:00
if ( this . config . validate ) {
// 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 ) {
2015-12-09 04:09:56 +03:00
this . removeHasDanger ( )
2015-12-08 03:07:21 +03:00
} else {
2015-12-09 04:09:56 +03:00
this . addHasDanger ( )
2015-12-08 03:07:21 +03:00
}
2015-12-06 02:45:38 +03:00
}
} )
2015-12-05 23:00:40 +03:00
}
2015-12-06 00:07:37 +03:00
addFormGroupFocus ( ) {
2015-12-09 22:13:55 +03:00
if ( ! this . $element . prop ( 'disabled' ) ) {
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
}
2015-12-09 04:09:56 +03:00
addHasDanger ( ) {
this . $mdbFormGroup . addClass ( ClassName . HAS _DANGER )
2015-12-05 23:00:40 +03:00
}
2015-12-09 04:09:56 +03:00
removeHasDanger ( ) {
this . $mdbFormGroup . removeClass ( ClassName . HAS _DANGER )
2015-12-05 23:00:40 +03:00
}
2015-12-12 00:13:33 +03:00
removeIsFilled ( ) {
this . $mdbFormGroup . removeClass ( ClassName . IS _FILLED )
2015-12-05 23:00:40 +03:00
}
2015-12-12 00:13:33 +03:00
addIsFilled ( ) {
this . $mdbFormGroup . addClass ( ClassName . IS _FILLED )
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 ) {
2015-12-15 01:42:22 +03:00
if ( this . config . mdbFormGroup . create && ( this . $formGroup === undefined || this . $formGroup . length === 0 ) ) {
2015-12-07 18:04:32 +03:00
// 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)
}
2015-12-15 01:42:22 +03:00
mfg = this . findMdbFormGroup ( this . config . mdbFormGroup . required )
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-11 20:07:03 +03:00
// Will add mdb-label to mdb-form-group if not already specified
resolveMdbLabel ( ) {
let label = this . $mdbFormGroup . find ( Selector . MDB _LABEL _WILDCARD )
if ( label === undefined || label . length === 0 ) {
// we need to find it based on the configured selectors
label = this . findMdbLabel ( this . config . mdbLabel . required )
if ( label === undefined || label . length === 0 ) {
// no label found, and finder did not require one
} else {
// a candidate label was found, add the configured default class name
label . addClass ( this . config . mdbLabel . className )
}
}
return label
}
// Find mdb-label variant based on the config selectors
findMdbLabel ( raiseError = true ) {
let label = null
// use the specified selector order
for ( let selector of this . config . mdbLabel . selectors ) {
if ( $ . isFunction ( selector ) ) {
label = selector ( this )
} else {
label = this . $mdbFormGroup . find ( selector )
}
2015-12-14 23:19:17 +03:00
if ( label !== undefined && label . length > 0 ) {
2015-12-11 20:07:03 +03:00
break ;
}
}
if ( label . length === 0 && raiseError ) {
$ . error ( ` Failed to find ${ Selector . MDB _LABEL _WILDCARD } within form-group for ${ Util . describe ( this . $element ) } ` )
}
return label
}
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)
2015-12-11 20:07:03 +03:00
for ( let inputSize in FormControlSizeMarkers ) {
2015-12-07 18:04:32 +03:00
if ( this . $element . hasClass ( inputSize ) ) {
//this.$element.removeClass(inputSize)
2015-12-11 20:07:03 +03:00
this . $mdbFormGroup . addClass ( FormControlSizeMarkers [ inputSize ] )
2015-12-07 18:04:32 +03:00
}
}
}
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