mirror of
https://github.com/mdbootstrap/mdb-ui-kit.git
synced 2025-02-02 12:54:13 +03:00
es6 refactoring classes into discrete input classes without overlap, and enforcing classes and structure. TextInput and Checkbox done, more to go.
This commit is contained in:
parent
93f0402161
commit
3a547cd0f3
16
Gruntfile.js
16
Gruntfile.js
|
@ -103,9 +103,9 @@ module.exports = function (grunt) {
|
|||
'dist/js/babel/util.js': 'js/src/util.js',
|
||||
'dist/js/babel/ripples.js': 'js/src/ripples.js',
|
||||
'dist/js/babel/autofill.js': 'js/src/autofill.js',
|
||||
'dist/js/babel/input.js': 'js/src/input.js',
|
||||
'dist/js/babel/input.js': 'js/src/textInput.js',
|
||||
'dist/js/babel/checkbox.js': 'js/src/checkbox.js',
|
||||
'dist/js/babel/togglebutton.js': 'js/src/togglebutton.js',
|
||||
'dist/js/babel/togglebutton.js': 'js/src/switch.js',
|
||||
'dist/js/babel/radio.js': 'js/src/radio.js',
|
||||
'dist/js/babel/fileInput.js': 'js/src/fileInput.js',
|
||||
'dist/js/babel/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.js',
|
||||
|
@ -124,9 +124,9 @@ module.exports = function (grunt) {
|
|||
'docs/dist/js/babel/util.js': 'js/src/util.js',
|
||||
'docs/dist/js/babel/ripples.js': 'js/src/ripples.js',
|
||||
'docs/dist/js/babel/autofill.js': 'js/src/autofill.js',
|
||||
'docs/dist/js/babel/input.js': 'js/src/input.js',
|
||||
'docs/dist/js/babel/input.js': 'js/src/textInput.js',
|
||||
'docs/dist/js/babel/checkbox.js': 'js/src/checkbox.js',
|
||||
'docs/dist/js/babel/togglebutton.js': 'js/src/togglebutton.js',
|
||||
'docs/dist/js/babel/togglebutton.js': 'js/src/switch.js',
|
||||
'docs/dist/js/babel/radio.js': 'js/src/radio.js',
|
||||
'docs/dist/js/babel/fileInput.js': 'js/src/fileInput.js',
|
||||
'docs/dist/js/babel/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.js',
|
||||
|
@ -148,9 +148,9 @@ 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/input.js': 'js/src/textInput.js',
|
||||
'dist/js/umd/checkbox.js': 'js/src/checkbox.js',
|
||||
'dist/js/umd/togglebutton.js': 'js/src/togglebutton.js',
|
||||
'dist/js/umd/togglebutton.js': 'js/src/switch.js',
|
||||
'dist/js/umd/radio.js': 'js/src/radio.js',
|
||||
'dist/js/umd/fileInput.js': 'js/src/fileInput.js',
|
||||
'dist/js/umd/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.js',
|
||||
|
@ -208,9 +208,9 @@ module.exports = function (grunt) {
|
|||
'dist/js/babel/util.js',
|
||||
'dist/js/babel/ripples.js',
|
||||
'dist/js/babel/autofill.js',
|
||||
'dist/js/babel/input.js',
|
||||
'dist/js/babel/textInput.js',
|
||||
'dist/js/babel/checkbox.js',
|
||||
'dist/js/babel/togglebutton.js',
|
||||
'dist/js/babel/switch.js',
|
||||
'dist/js/babel/radio.js',
|
||||
'dist/js/babel/fileInput.js',
|
||||
'dist/js/babel/bootstrapMaterialDesign.js',
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
],
|
||||
|
||||
"coreJs": [
|
||||
"../dist/js/babel/baseInput.js",
|
||||
"../dist/js/babel/autofill.js",
|
||||
"../dist/js/babel/bootstrapMaterialDesign.js",
|
||||
"../dist/js/babel/checkbox.js",
|
||||
|
|
164
js/src/baseInput.js
Normal file
164
js/src/baseInput.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
import Util from './util'
|
||||
|
||||
const BaseInput = (($) => {
|
||||
|
||||
const Default = {
|
||||
formGroup: {
|
||||
template: `<div class='form-group'></div>`,
|
||||
required: true,
|
||||
autoCreate: false
|
||||
},
|
||||
requiredClasses: ['form-control'],
|
||||
invalidComponentMatches: []
|
||||
}
|
||||
|
||||
const ClassName = {
|
||||
FORM_GROUP: 'form-group',
|
||||
HAS_ERROR: 'has-error',
|
||||
IS_EMPTY: 'is-empty'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
FORM_GROUP: `.${ClassName.FORM_GROUP}` //,
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
class BaseInput {
|
||||
|
||||
constructor(element, defaultConfig, config) {
|
||||
this.$element = $(element)
|
||||
this.config = $.extend({}, Default, defaultConfig, config)
|
||||
|
||||
// Enforce no overlap between components to prevent side effects
|
||||
this._rejectInvalidComponentMatches()
|
||||
|
||||
// Enforce required classes for a consistent rendering
|
||||
this._rejectWithoutRequiredClasses()
|
||||
|
||||
// Enforce expected structure (if any)
|
||||
this.rejectWithoutRequiredStructure()
|
||||
|
||||
if(this.config.formGroup.autoCreate) {
|
||||
// Will create form-group if necessary
|
||||
this.autoCreateFormGroup()
|
||||
}
|
||||
|
||||
// different components have different rules, always run this separately
|
||||
this.$formGroup = this.findFormGroup(this.config.formGroup.required)
|
||||
|
||||
this.addFocusListener()
|
||||
this.addChangeListener()
|
||||
}
|
||||
|
||||
dispose(dataKey) {
|
||||
$.removeData(this.$element, dataKey)
|
||||
this.$element = null
|
||||
this.$formGroup = null
|
||||
this.config = null
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// protected
|
||||
|
||||
rejectWithoutRequiredStructure(){
|
||||
// implement
|
||||
}
|
||||
|
||||
addFocusListener() {
|
||||
// implement
|
||||
}
|
||||
|
||||
addChangeListener() {
|
||||
// implement
|
||||
}
|
||||
|
||||
addFormGroupFocus(formGroup) {
|
||||
formGroup.addClass(ClassName.IS_FOCUSED)
|
||||
}
|
||||
|
||||
removeFormGroupFocus(formGroup) {
|
||||
formGroup.removeClass(ClassName.IS_FOCUSED)
|
||||
}
|
||||
|
||||
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() === '')
|
||||
}
|
||||
|
||||
// Find or create a form-group if necessary
|
||||
autoCreateFormGroup() {
|
||||
let fg = this.findFormGroup(false)
|
||||
if (fg === null || fg.length === 0) {
|
||||
this.outerElement().wrap(this.config.formGroup.template)
|
||||
}
|
||||
}
|
||||
|
||||
// Demarcation element (e.g. first child of a form-group)
|
||||
// Subclasses such as file inputs may have different structures
|
||||
outerElement(){
|
||||
return this.$element
|
||||
}
|
||||
|
||||
// Find expected form-group
|
||||
findFormGroup(raiseError = true) {
|
||||
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 && raiseError) {
|
||||
$.error(`Failed to find form-group for ${$element}`)
|
||||
}
|
||||
return fg
|
||||
}
|
||||
|
||||
findOrCreateFormGroup() {
|
||||
let fg = this.$element.closest(Selector.FORM_GROUP) // note that form-group may be grandparent in the case of an baseInput-group
|
||||
if (fg === null || 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
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// private
|
||||
_rejectInvalidComponentMatches(){
|
||||
for(let otherComponent in this.config.invalidComponentMatches){
|
||||
otherComponent.rejectMatch(this.constructor.name, $element)
|
||||
}
|
||||
}
|
||||
|
||||
_rejectWithoutRequiredClasses(){
|
||||
for(let requiredClass in this.config.requiredClasses){
|
||||
if(!$element.hasClass(requiredClass)){
|
||||
$.error(`${this.constructor.name} elements require class: ${requiredClass}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// static
|
||||
|
||||
}
|
||||
|
||||
return BaseInput
|
||||
|
||||
})(jQuery)
|
||||
|
||||
export default BaseInput
|
18
js/src/bootstrapMaterialDesign.js
vendored
18
js/src/bootstrapMaterialDesign.js
vendored
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
* NOTE: If omitting use of this class, please note that the Input component must be
|
||||
* initialized prior to other decorating components such as radio, checkbox,
|
||||
* togglebutton, fileInput.
|
||||
* switch, fileInput.
|
||||
*
|
||||
*/
|
||||
const BootstrapMaterialDesign = (($) => {
|
||||
|
@ -42,18 +42,18 @@ const BootstrapMaterialDesign = (($) => {
|
|||
'.ripple' // generic marker class to add ripple to elements
|
||||
]
|
||||
},
|
||||
input: {
|
||||
textInput: {
|
||||
selector: [
|
||||
'input.form-control',
|
||||
'textarea.form-control',
|
||||
'select.form-control'
|
||||
'input[type=text]',
|
||||
'textarea',
|
||||
'select'
|
||||
]
|
||||
},
|
||||
checkbox: {
|
||||
selector: '.checkbox > label > input[type=checkbox]'
|
||||
},
|
||||
togglebutton: {
|
||||
selector: '.togglebutton > label > input[type=checkbox]'
|
||||
switch: {
|
||||
selector: '.switch > label > input[type=checkbox]'
|
||||
},
|
||||
radio: {
|
||||
selector: '.radio > label > input[type=radio]'
|
||||
|
@ -68,9 +68,9 @@ const BootstrapMaterialDesign = (($) => {
|
|||
// create an ordered component list for instantiation
|
||||
instantiation: [
|
||||
'ripples',
|
||||
'input',
|
||||
'textInput',
|
||||
'checkbox',
|
||||
'togglebutton',
|
||||
'switch',
|
||||
'radio',
|
||||
'fileInput',
|
||||
'autofill'
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import BaseInput from './baseInput'
|
||||
import TextInput from './textInput'
|
||||
import FileInput from './fileInput'
|
||||
import Radio from './radio'
|
||||
import Switch from './switch'
|
||||
import Util from './util'
|
||||
|
||||
// Checkbox decorator, to be called after Input
|
||||
const Checkbox = (($) => {
|
||||
|
||||
/**
|
||||
|
@ -13,7 +17,11 @@ const Checkbox = (($) => {
|
|||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const Default = {
|
||||
template: `<span class='checkbox-material'><span class='check'></span></span>`
|
||||
template: `<span class='checkbox-material'><span class='check'></span></span>`,
|
||||
formGroup: {
|
||||
autoCreate: true
|
||||
},
|
||||
invalidComponentMatches: [TextInput, FileInput, Radio, Switch]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,40 +29,66 @@ const Checkbox = (($) => {
|
|||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
class Checkbox {
|
||||
class Checkbox extends BaseInput {
|
||||
|
||||
constructor(element, config) {
|
||||
this.$element = $(element)
|
||||
this.config = $.extend({}, Default, config)
|
||||
|
||||
super(element, Default, config)
|
||||
this.$element.after(this.config.template)
|
||||
this.$formGroup = Util.findFormGroup(this.$element, false)
|
||||
|
||||
this._bindEventListeners()
|
||||
}
|
||||
|
||||
dispose() {
|
||||
$.removeData(this.$element, DATA_KEY)
|
||||
this.$element = null
|
||||
this.$formGroup = null
|
||||
this.config = null
|
||||
super.dispose(DATA_KEY)
|
||||
}
|
||||
|
||||
static matches($element) {
|
||||
// '.checkbox > label > input[type=checkbox]'
|
||||
if ($element.attr('type') === 'checkbox') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static rejectMatch(component, $element) {
|
||||
Util.assert(this.matches($element), `${component} component is invalid for type='checkbox'.`)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// private
|
||||
_bindEventListeners() {
|
||||
// protected
|
||||
|
||||
// Demarcation element (e.g. first child of a form-group)
|
||||
// Subclasses such as file inputs may have different structures
|
||||
outerElement() {
|
||||
// '.checkbox > label > input[type=checkbox]'
|
||||
return this.$element.parent().parent()
|
||||
}
|
||||
|
||||
rejectWithoutRequiredStructure() {
|
||||
// '.checkbox > label > input[type=checkbox]'
|
||||
Util.assert(this.$element.parent().prop('tagName') === 'label', `${component} parent element should be <label>.`)
|
||||
Util.assert(this.outerElement().hasClass('checkbox'), `${component} grandparent element should have class .checkbox.`)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// protected
|
||||
|
||||
addFocusListener() {
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
|
||||
addChangeListener() {
|
||||
this.$element.change(() => {
|
||||
this.$element.blur()
|
||||
})
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// private
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// static
|
||||
static _jQueryInterface(config) {
|
||||
|
|
|
@ -16,7 +16,6 @@ const FileInput = (($) => {
|
|||
|
||||
const ClassName = {
|
||||
IS_FILEINPUT: 'is-fileinput',
|
||||
IS_EMPTY: 'is-empty'
|
||||
}
|
||||
|
||||
|
||||
|
@ -44,6 +43,20 @@ const FileInput = (($) => {
|
|||
this.config = null
|
||||
}
|
||||
|
||||
static matches($element) {
|
||||
if ($element.attr('type') === 'file') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static rejectMatch(component, $element) {
|
||||
if (this.matches($element)) {
|
||||
let msg = `${component} component is invalid for type='file'.`
|
||||
$.error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// private
|
||||
_bindEventListeners() {
|
||||
|
@ -63,22 +76,14 @@ const FileInput = (($) => {
|
|||
})
|
||||
value = value.substring(0, value.length - 2)
|
||||
if (value) {
|
||||
this._removeIsEmpty()
|
||||
this.removeIsEmpty()
|
||||
} else {
|
||||
this._addIsEmpty()
|
||||
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) {
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
//import Util from './util'
|
||||
|
||||
// Togglebutton decorator, to be called after Input
|
||||
const Togglebutton = (($) => {
|
||||
// Switch decorator, to be called after Input
|
||||
const Switch = (($) => {
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
const NAME = 'togglebutton'
|
||||
const NAME = 'switch'
|
||||
const DATA_KEY = `mdb.${NAME}`
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
const Default = {
|
||||
template: `<span class='toggle'></span>`
|
||||
template: `<span class='switch-decorator'></span>`
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ const Togglebutton = (($) => {
|
|||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
class Togglebutton {
|
||||
class Switch {
|
||||
|
||||
constructor(element, config) {
|
||||
this.$element = $(element)
|
||||
|
@ -47,7 +47,7 @@ const Togglebutton = (($) => {
|
|||
let data = $element.data(DATA_KEY)
|
||||
|
||||
if (!data) {
|
||||
data = new Togglebutton(this, config)
|
||||
data = new Switch(this, config)
|
||||
$element.data(DATA_KEY, data)
|
||||
}
|
||||
})
|
||||
|
@ -59,15 +59,15 @@ const Togglebutton = (($) => {
|
|||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
$.fn[NAME] = Togglebutton._jQueryInterface
|
||||
$.fn[NAME].Constructor = Togglebutton
|
||||
$.fn[NAME] = Switch._jQueryInterface
|
||||
$.fn[NAME].Constructor = Switch
|
||||
$.fn[NAME].noConflict = () => {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Togglebutton._jQueryInterface
|
||||
return Switch._jQueryInterface
|
||||
}
|
||||
|
||||
return Togglebutton
|
||||
return Switch
|
||||
|
||||
})(jQuery)
|
||||
|
||||
export default Togglebutton
|
||||
export default Switch
|
|
@ -1,13 +1,18 @@
|
|||
import BaseInput from './baseInput'
|
||||
import Checkbox from './checkbox'
|
||||
import FileInput from './fileInput'
|
||||
import Radio from './radio'
|
||||
import Switch from './switch'
|
||||
import Util from './util'
|
||||
|
||||
const Input = (($) => {
|
||||
const TextInput = (($) => {
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
const NAME = 'input'
|
||||
const NAME = 'textInput'
|
||||
const DATA_KEY = `mdb.${NAME}`
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
|
||||
|
@ -15,23 +20,15 @@ const Input = (($) => {
|
|||
convertInputSizeVariations: true,
|
||||
template: `<span class='material-input'></span>`,
|
||||
formGroup: {
|
||||
template: `<div class='form-group'></div>`
|
||||
}
|
||||
autoCreate: true
|
||||
},
|
||||
requiredClasses: ['form-control'],
|
||||
invalidComponentMatches: [Checkbox, FileInput, Radio, Switch]
|
||||
}
|
||||
|
||||
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}` //,
|
||||
'textInput-lg': 'form-group-lg',
|
||||
'textInput-sm': 'form-group-sm'
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,115 +36,95 @@ const Input = (($) => {
|
|||
* Class Definition
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
class Input {
|
||||
class TextInput extends BaseInput {
|
||||
|
||||
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()
|
||||
super(element, Default, config)
|
||||
|
||||
this._convertInputSizeVariations()
|
||||
|
||||
// Initially mark as empty
|
||||
if (this._isEmpty()) {
|
||||
this.$formGroup.addClass(ClassName.IS_EMPTY)
|
||||
if (this.isEmpty()) {
|
||||
this.addIsEmpty()
|
||||
}
|
||||
|
||||
// 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
|
||||
super.dispose(DATA_KEY)
|
||||
}
|
||||
|
||||
static matches($element) {
|
||||
if (
|
||||
($element.attr('type') === 'text')
|
||||
|| ($element.prop('tagName') === 'textarea')
|
||||
|| ($element.prop('tagName') === 'select')
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static rejectMatch(component, $element) {
|
||||
Util.assert(this.matches($element), `${component} component is invalid for type='text'.`)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// private
|
||||
// protected
|
||||
|
||||
_bindEventListeners() {
|
||||
addFocusListener() {
|
||||
this.$element
|
||||
.on('focus', () => {
|
||||
this.addFormGroupFocus(this.$formGroup)
|
||||
})
|
||||
.on('blur', () => {
|
||||
this.removeFormGroupFocus(this.$formGroup)
|
||||
})
|
||||
}
|
||||
|
||||
addChangeListener() {
|
||||
this.$element
|
||||
.on('keydown paste', (event) => {
|
||||
if (Util.isChar(event)) {
|
||||
this._removeIsEmpty()
|
||||
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()
|
||||
// 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()
|
||||
this.removeIsEmpty()
|
||||
}
|
||||
|
||||
// Validation events do not bubble, so they must be attached directly to the input: http://jsfiddle.net/PEpRM/1/
|
||||
// Validation events do not bubble, so they must be attached directly to the textInput: 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()
|
||||
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()
|
||||
this.addHasError()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_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() === '')
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
// private
|
||||
|
||||
_convertInputSizeVariations() {
|
||||
if (!this.config.convertInputSizeVariations) {
|
||||
return
|
||||
}
|
||||
|
||||
// Modification - Change input-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants)
|
||||
// Modification - Change textInput-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)
|
||||
|
@ -156,15 +133,6 @@ const Input = (($) => {
|
|||
}
|
||||
}
|
||||
|
||||
_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 === null || 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) {
|
||||
|
@ -173,7 +141,7 @@ const Input = (($) => {
|
|||
let data = $element.data(DATA_KEY)
|
||||
|
||||
if (!data) {
|
||||
data = new Input(this, config)
|
||||
data = new TextInput(this, config)
|
||||
$element.data(DATA_KEY, data)
|
||||
}
|
||||
})
|
||||
|
@ -185,15 +153,15 @@ const Input = (($) => {
|
|||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
$.fn[NAME] = Input._jQueryInterface
|
||||
$.fn[NAME].Constructor = Input
|
||||
$.fn[NAME] = TextInput._jQueryInterface
|
||||
$.fn[NAME].Constructor = TextInput
|
||||
$.fn[NAME].noConflict = () => {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Input._jQueryInterface
|
||||
return TextInput._jQueryInterface
|
||||
}
|
||||
|
||||
return Input
|
||||
return TextInput
|
||||
|
||||
})(jQuery)
|
||||
|
||||
export default Input
|
||||
export default TextInput
|
|
@ -16,15 +16,6 @@ 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
|
||||
|
@ -75,23 +66,10 @@ const Util = (($) => {
|
|||
return false
|
||||
},
|
||||
|
||||
addFormGroupFocus(formGroup) {
|
||||
formGroup.addClass(ClassName.IS_FOCUSED)
|
||||
},
|
||||
|
||||
removeFormGroupFocus(formGroup) {
|
||||
formGroup.removeClass(ClassName.IS_FOCUSED)
|
||||
},
|
||||
|
||||
/**
|
||||
Find expected form-group
|
||||
*/
|
||||
findFormGroup($element, raiseError = true) {
|
||||
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 && raiseError) {
|
||||
$.error(`Failed to find form-group for ${$element}`)
|
||||
assert(test, message) {
|
||||
if (test) {
|
||||
$.error(message)
|
||||
}
|
||||
return fg
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
@import 'reboot';
|
||||
@import 'buttons';
|
||||
@import 'checkboxes';
|
||||
@import 'togglebutton';
|
||||
@import 'switch';
|
||||
@import 'radios';
|
||||
@import 'inputs';
|
||||
@import 'form';
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
|
||||
.togglebutton {
|
||||
.switch {
|
||||
vertical-align: middle;
|
||||
&, label, input, .toggle {
|
||||
&, label, input, .switch-decorator {
|
||||
user-select: none;
|
||||
}
|
||||
label {
|
||||
cursor: pointer;
|
||||
color: $mdb-toggle-label-color;
|
||||
color: $mdb-switch-label-color;
|
||||
|
||||
// Hide original checkbox
|
||||
input[type=checkbox] {
|
||||
|
@ -16,8 +16,8 @@
|
|||
height: 0;
|
||||
}
|
||||
// Switch bg off and disabled
|
||||
.toggle,
|
||||
input[type=checkbox][disabled] + .toggle {
|
||||
.switch-decorator,
|
||||
input[type=checkbox][disabled] + .switch-decorator {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
|
@ -29,7 +29,7 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
// Handle off
|
||||
.toggle:after {
|
||||
.switch-decorator:after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
|
@ -45,33 +45,33 @@
|
|||
input[type=checkbox] {
|
||||
// Handle disabled
|
||||
&[disabled] {
|
||||
& + .toggle:after,
|
||||
&:checked + .toggle:after {
|
||||
& + .switch-decorator:after,
|
||||
&:checked + .switch-decorator:after {
|
||||
background-color: #BDBDBD;
|
||||
}
|
||||
}
|
||||
|
||||
& + .toggle:active:after,
|
||||
&[disabled] + .toggle:active:after {
|
||||
& + .switch-decorator:active:after,
|
||||
&[disabled] + .switch-decorator:active:after {
|
||||
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
// Ripple off and disabled
|
||||
&:checked + .toggle:after {
|
||||
&:checked + .switch-decorator:after {
|
||||
left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
& label input[type=checkbox]:checked {
|
||||
& + .toggle {
|
||||
& + .switch-decorator {
|
||||
background-color: rgba($brand-primary, (50/100)); // Switch bg on
|
||||
}
|
||||
|
||||
& + .toggle:after {
|
||||
& + .switch-decorator:after {
|
||||
background-color: $brand-primary; // Handle on
|
||||
}
|
||||
|
||||
& + .toggle:active:after {
|
||||
& + .switch-decorator:active:after {
|
||||
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba($brand-primary, (10/100)); // Ripple on
|
||||
}
|
||||
}
|
|
@ -85,7 +85,7 @@ $mdb-popover-color: #ececec !default;
|
|||
$mdb-dropdown-font-size: 16px !default;
|
||||
|
||||
// Toggle
|
||||
$mdb-toggle-label-color: $mdb-checkbox-label-color !default;
|
||||
$mdb-switch-label-color: $mdb-checkbox-label-color !default;
|
||||
|
||||
// Radio:
|
||||
$mdb-radio-label-color: $mdb-checkbox-label-color !default;
|
||||
|
|
Loading…
Reference in New Issue
Block a user