2017-08-11 17:59:31 +03:00
import Base from "./base" ;
import Util from "./util" ;
2016-01-26 21:12:48 +03:00
2017-08-11 17:59:31 +03:00
const BaseInput = ( $ => {
2016-01-26 21:12:48 +03:00
const ClassName = {
2017-08-11 17:59:31 +03:00
FORM _GROUP : "form-group" ,
BMD _FORM _GROUP : "bmd-form-group" ,
BMD _LABEL : "bmd-label" ,
BMD _LABEL _STATIC : "bmd-label-static" ,
BMD _LABEL _PLACEHOLDER : "bmd-label-placeholder" ,
BMD _LABEL _FLOATING : "bmd-label-floating" ,
HAS _DANGER : "has-danger" ,
IS _FILLED : "is-filled" ,
IS _FOCUSED : "is-focused" ,
INPUT _GROUP : "input-group"
} ;
2016-01-26 21:12:48 +03:00
const Selector = {
FORM _GROUP : ` . ${ ClassName . FORM _GROUP } ` ,
2016-03-28 19:13:18 +03:00
BMD _FORM _GROUP : ` . ${ ClassName . BMD _FORM _GROUP } ` ,
BMD _LABEL _WILDCARD : ` label[class^=' ${ ClassName . BMD _LABEL } '], label[class*=' ${ ClassName . BMD _LABEL } '] ` // match any label variant if specified
2017-08-11 17:59:31 +03:00
} ;
2016-01-26 21:12:48 +03:00
const Default = {
validate : false ,
formGroup : {
required : false
} ,
2016-03-28 23:18:19 +03:00
bmdFormGroup : {
2016-03-28 19:13:18 +03:00
template : ` <span class=' ${ ClassName . BMD _FORM _GROUP } '></span> ` ,
2016-01-26 21:12:48 +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
} ,
label : {
required : false ,
2016-03-21 17:56:51 +03:00
// Prioritized find order for resolving the label to be used as an bmd-label if not specified in the markup
2016-01-26 21:12:48 +03:00
// - a function(thisComponent); or
2016-03-28 23:18:19 +03:00
// - a string selector used like $bmdFormGroup.find(selector)
2016-01-26 21:12:48 +03:00
//
2016-03-28 23:18:19 +03:00
// Note this only runs if $bmdFormGroup.find(Selector.BMD_LABEL_WILDCARD) fails to find a label (as authored in the markup)
2016-01-26 21:12:48 +03:00
//
selectors : [
` .form-control-label ` , // in the case of horizontal or inline forms, this will be marked
` > label ` // usual case for text inputs, first child. Deeper would find toggle labels so don't do that.
] ,
2016-03-28 19:13:18 +03:00
className : ClassName . BMD _LABEL _STATIC
2016-01-26 21:12:48 +03:00
} ,
requiredClasses : [ ] ,
invalidComponentMatches : [ ] ,
convertInputSizeVariations : true
2017-08-11 17:59:31 +03:00
} ;
2016-01-26 21:12:48 +03:00
const FormControlSizeMarkers = {
2017-08-11 17:59:31 +03:00
"form-control-lg" : "bmd-form-group-lg" ,
"form-control-sm" : "bmd-form-group-sm"
} ;
2016-01-26 21:12:48 +03:00
/ * *
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* Class Definition
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
* /
class BaseInput extends Base {
/ * *
*
* @ 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 `
* /
constructor ( $element , config , properties = { } ) {
2017-08-11 17:59:31 +03:00
super ( $element , $ . extend ( true , { } , Default , config ) , properties ) ;
2016-01-26 21:12:48 +03:00
// Enforce no overlap between components to prevent side effects
2017-08-11 17:59:31 +03:00
this . _rejectInvalidComponentMatches ( ) ;
2016-01-26 21:12:48 +03:00
// Enforce expected structure (if any)
2017-08-11 17:59:31 +03:00
this . rejectWithoutRequiredStructure ( ) ;
2016-01-26 21:12:48 +03:00
// Enforce required classes for a consistent rendering
2017-08-11 17:59:31 +03:00
this . _rejectWithoutRequiredClasses ( ) ;
2016-01-26 21:12:48 +03:00
2016-03-21 17:56:51 +03:00
// Resolve the form-group first, it will be used for bmd-form-group if possible
2016-01-26 21:12:48 +03:00
// note: different components have different rules
2017-08-11 17:59:31 +03:00
this . $formGroup = this . findFormGroup ( this . config . formGroup . required ) ;
2016-01-26 21:12:48 +03:00
2016-03-21 17:56:51 +03:00
// Will add bmd-form-group to form-group or create an bmd-form-group
// Performance Note: for those forms that are really performance driven, create the markup with the .bmd-form-group to avoid
2016-01-26 21:12:48 +03:00
// rendering changes once added.
2017-08-11 17:59:31 +03:00
this . $bmdFormGroup = this . resolveMdbFormGroup ( ) ;
2016-01-26 21:12:48 +03:00
2016-03-28 23:18:19 +03:00
// Resolve and mark the bmdLabel if necessary as defined by the config
2017-08-11 17:59:31 +03:00
this . $bmdLabel = this . resolveMdbLabel ( ) ;
2016-01-26 21:12:48 +03:00
2016-03-21 17:56:51 +03:00
// Signal to the bmd-form-group that a form-control-* variation is being used
2017-08-11 17:59:31 +03:00
this . resolveMdbFormGroupSizing ( ) ;
2016-01-26 21:12:48 +03:00
2017-08-11 17:59:31 +03:00
this . addFocusListener ( ) ;
this . addChangeListener ( ) ;
2016-05-04 04:24:13 +03:00
2017-08-11 17:59:31 +03:00
if ( this . $element . val ( ) != "" ) {
this . addIsFilled ( ) ;
2016-05-04 04:24:13 +03:00
}
2016-01-26 21:12:48 +03:00
}
dispose ( dataKey ) {
2017-08-11 17:59:31 +03:00
super . dispose ( dataKey ) ;
this . $bmdFormGroup = null ;
this . $formGroup = null ;
2016-01-26 21:12:48 +03:00
}
// ------------------------------------------------------------------------
// protected
rejectWithoutRequiredStructure ( ) {
// implement
}
addFocusListener ( ) {
this . $element
2017-08-11 17:59:31 +03:00
. on ( "focus" , ( ) => {
this . addFormGroupFocus ( ) ;
2016-01-26 21:12:48 +03:00
} )
2017-08-11 17:59:31 +03:00
. on ( "blur" , ( ) => {
this . removeFormGroupFocus ( ) ;
} ) ;
2016-01-26 21:12:48 +03:00
}
addChangeListener ( ) {
this . $element
2017-08-11 17:59:31 +03:00
. on ( "keydown paste" , event => {
2016-01-26 21:12:48 +03:00
if ( Util . isChar ( event ) ) {
2017-08-11 17:59:31 +03:00
this . addIsFilled ( ) ;
2016-01-26 21:12:48 +03:00
}
} )
2017-08-11 17:59:31 +03:00
. on ( "keyup change" , ( ) => {
2016-01-26 21:12:48 +03:00
// 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 . isEmpty ( ) ) {
2017-08-11 17:59:31 +03:00
this . removeIsFilled ( ) ;
2016-01-26 21:12:48 +03:00
} else {
2017-08-11 17:59:31 +03:00
this . addIsFilled ( ) ;
2016-01-26 21:12:48 +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.
2017-08-11 17:59:31 +03:00
let isValid =
typeof this . $element [ 0 ] . checkValidity === "undefined" ||
this . $element [ 0 ] . checkValidity ( ) ;
2016-01-26 21:12:48 +03:00
if ( isValid ) {
2017-08-11 17:59:31 +03:00
this . removeHasDanger ( ) ;
2016-01-26 21:12:48 +03:00
} else {
2017-08-11 17:59:31 +03:00
this . addHasDanger ( ) ;
2016-01-26 21:12:48 +03:00
}
}
2017-08-11 17:59:31 +03:00
} ) ;
2016-01-26 21:12:48 +03:00
}
addHasDanger ( ) {
2017-08-11 17:59:31 +03:00
this . $bmdFormGroup . addClass ( ClassName . HAS _DANGER ) ;
2016-01-26 21:12:48 +03:00
}
removeHasDanger ( ) {
2017-08-11 17:59:31 +03:00
this . $bmdFormGroup . removeClass ( ClassName . HAS _DANGER ) ;
2016-01-26 21:12:48 +03:00
}
isEmpty ( ) {
2017-08-11 17:59:31 +03:00
return (
this . $element . val ( ) === null ||
this . $element . val ( ) === undefined ||
this . $element . val ( ) === ""
) ;
2016-01-26 21:12:48 +03:00
}
2016-03-21 17:56:51 +03:00
// Will add bmd-form-group to form-group or create a bmd-form-group if necessary
2016-01-26 21:12:48 +03:00
resolveMdbFormGroup ( ) {
2017-08-11 17:59:31 +03:00
let mfg = this . findMdbFormGroup ( false ) ;
2016-01-26 21:12:48 +03:00
if ( mfg === undefined || mfg . length === 0 ) {
2017-08-11 17:59:31 +03:00
if (
this . config . bmdFormGroup . create &&
( this . $formGroup === undefined || this . $formGroup . length === 0 )
) {
2016-01-26 21:12:48 +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.
2016-04-30 00:03:40 +03:00
// this may be an input-group, wrap that instead
2017-08-11 17:59:31 +03:00
if ( this . outerElement ( ) . parent ( ) . hasClass ( ClassName . INPUT _GROUP ) ) {
this . outerElement ( )
. parent ( )
. wrap ( this . config . bmdFormGroup . template ) ;
} else {
this . outerElement ( ) . wrap ( this . config . bmdFormGroup . template ) ;
2016-04-30 00:03:40 +03:00
}
2016-01-26 21:12:48 +03:00
} else {
// a form-group does exist, add our marker class to it
2017-08-11 17:59:31 +03:00
this . $formGroup . addClass ( ClassName . BMD _FORM _GROUP ) ;
2016-01-26 21:12:48 +03:00
// 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
2016-03-21 17:56:51 +03:00
// A form-group does exist, so add an bmd-form-group wrapping it's internal contents
2016-03-28 23:18:19 +03:00
//fg.wrapInner(this.config.bmdFormGroup.template)
2016-01-26 21:12:48 +03:00
}
2017-08-11 17:59:31 +03:00
mfg = this . findMdbFormGroup ( this . config . bmdFormGroup . required ) ;
2016-01-26 21:12:48 +03:00
}
2017-08-11 17:59:31 +03:00
return mfg ;
2016-01-26 21:12:48 +03:00
}
// Demarcation element (e.g. first child of a form-group)
// Subclasses such as file inputs may have different structures
outerElement ( ) {
2017-08-11 17:59:31 +03:00
return this . $element ;
2016-01-26 21:12:48 +03:00
}
2016-03-21 17:56:51 +03:00
// Will add bmd-label to bmd-form-group if not already specified
2016-01-26 21:12:48 +03:00
resolveMdbLabel ( ) {
2017-08-11 17:59:31 +03:00
let label = this . $bmdFormGroup . find ( Selector . BMD _LABEL _WILDCARD ) ;
2016-01-26 21:12:48 +03:00
if ( label === undefined || label . length === 0 ) {
// we need to find it based on the configured selectors
2017-08-11 17:59:31 +03:00
label = this . findMdbLabel ( this . config . label . required ) ;
2016-01-26 21:12:48 +03:00
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
2017-08-11 17:59:31 +03:00
label . addClass ( this . config . label . className ) ;
2016-01-26 21:12:48 +03:00
}
}
2017-08-11 17:59:31 +03:00
return label ;
2016-01-26 21:12:48 +03:00
}
2016-03-21 17:56:51 +03:00
// Find bmd-label variant based on the config selectors
2016-01-26 21:12:48 +03:00
findMdbLabel ( raiseError = true ) {
2017-08-11 17:59:31 +03:00
let label = null ;
2016-01-26 21:12:48 +03:00
// use the specified selector order
for ( let selector of this . config . label . selectors ) {
if ( $ . isFunction ( selector ) ) {
2017-08-11 17:59:31 +03:00
label = selector ( this ) ;
2016-01-26 21:12:48 +03:00
} else {
2017-08-11 17:59:31 +03:00
label = this . $bmdFormGroup . find ( selector ) ;
2016-01-26 21:12:48 +03:00
}
if ( label !== undefined && label . length > 0 ) {
2017-08-11 17:59:31 +03:00
break ;
2016-01-26 21:12:48 +03:00
}
}
if ( label . length === 0 && raiseError ) {
2017-08-11 17:59:31 +03:00
$ . error (
` Failed to find ${ Selector . BMD _LABEL _WILDCARD } within form-group for ${ Util . describe (
this . $element
) } `
) ;
2016-01-26 21:12:48 +03:00
}
2017-08-11 17:59:31 +03:00
return label ;
2016-01-26 21:12:48 +03:00
}
2016-03-21 17:56:51 +03:00
// Find bmd-form-group
2016-01-26 21:12:48 +03:00
findFormGroup ( raiseError = true ) {
2017-08-11 17:59:31 +03:00
let fg = this . $element . closest ( Selector . FORM _GROUP ) ;
2016-01-26 21:12:48 +03:00
if ( fg . length === 0 && raiseError ) {
2017-08-11 17:59:31 +03:00
$ . error (
` Failed to find ${ Selector . FORM _GROUP } for ${ Util . describe (
this . $element
) } `
) ;
2016-01-26 21:12:48 +03:00
}
2017-08-11 17:59:31 +03:00
return fg ;
2016-01-26 21:12:48 +03:00
}
2016-03-21 17:56:51 +03:00
// Due to the interconnected nature of labels/inputs/help-blocks, signal the bmd-form-group-* size variation based on
2016-01-26 21:12:48 +03:00
// a found form-control-* size
resolveMdbFormGroupSizing ( ) {
if ( ! this . config . convertInputSizeVariations ) {
2017-08-11 17:59:31 +03:00
return ;
2016-01-26 21:12:48 +03:00
}
// Modification - Change text-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants)
for ( let inputSize in FormControlSizeMarkers ) {
if ( this . $element . hasClass ( inputSize ) ) {
//this.$element.removeClass(inputSize)
2017-08-11 17:59:31 +03:00
this . $bmdFormGroup . addClass ( FormControlSizeMarkers [ inputSize ] ) ;
2016-01-26 21:12:48 +03:00
}
}
}
// ------------------------------------------------------------------------
// private
_rejectInvalidComponentMatches ( ) {
for ( let otherComponent of this . config . invalidComponentMatches ) {
2017-08-11 17:59:31 +03:00
otherComponent . rejectMatch ( this . constructor . name , this . $element ) ;
2016-01-26 21:12:48 +03:00
}
}
_rejectWithoutRequiredClasses ( ) {
for ( let requiredClass of this . config . requiredClasses ) {
2017-08-11 17:59:31 +03:00
let found = false ;
2016-01-26 21:12:48 +03:00
// allow one of several classes to be passed in x||y
2017-08-11 17:59:31 +03:00
if ( requiredClass . indexOf ( "||" ) !== - 1 ) {
let oneOf = requiredClass . split ( "||" ) ;
2016-01-26 21:12:48 +03:00
for ( let requiredClass of oneOf ) {
if ( this . $element . hasClass ( requiredClass ) ) {
2017-08-11 17:59:31 +03:00
found = true ;
break ;
2016-01-26 21:12:48 +03:00
}
}
} else if ( this . $element . hasClass ( requiredClass ) ) {
2017-08-11 17:59:31 +03:00
found = true ;
2016-01-26 21:12:48 +03:00
}
}
}
// ------------------------------------------------------------------------
// static
}
2017-08-11 17:59:31 +03:00
return BaseInput ;
} ) ( jQuery ) ;
2016-01-26 21:12:48 +03:00
2017-08-11 17:59:31 +03:00
export default BaseInput ;