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:
Kevin Ross 2015-12-05 14:00:40 -06:00
parent 93f0402161
commit 3a547cd0f3
12 changed files with 340 additions and 190 deletions

View File

@ -103,9 +103,9 @@ module.exports = function (grunt) {
'dist/js/babel/util.js': 'js/src/util.js', 'dist/js/babel/util.js': 'js/src/util.js',
'dist/js/babel/ripples.js': 'js/src/ripples.js', 'dist/js/babel/ripples.js': 'js/src/ripples.js',
'dist/js/babel/autofill.js': 'js/src/autofill.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/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/radio.js': 'js/src/radio.js',
'dist/js/babel/fileInput.js': 'js/src/fileInput.js', 'dist/js/babel/fileInput.js': 'js/src/fileInput.js',
'dist/js/babel/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.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/util.js': 'js/src/util.js',
'docs/dist/js/babel/ripples.js': 'js/src/ripples.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/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/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/radio.js': 'js/src/radio.js',
'docs/dist/js/babel/fileInput.js': 'js/src/fileInput.js', 'docs/dist/js/babel/fileInput.js': 'js/src/fileInput.js',
'docs/dist/js/babel/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.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/util.js': 'js/src/util.js',
'dist/js/umd/ripples.js': 'js/src/ripples.js', 'dist/js/umd/ripples.js': 'js/src/ripples.js',
'dist/js/umd/autofill.js': 'js/src/autofill.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/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/radio.js': 'js/src/radio.js',
'dist/js/umd/fileInput.js': 'js/src/fileInput.js', 'dist/js/umd/fileInput.js': 'js/src/fileInput.js',
'dist/js/umd/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.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/util.js',
'dist/js/babel/ripples.js', 'dist/js/babel/ripples.js',
'dist/js/babel/autofill.js', 'dist/js/babel/autofill.js',
'dist/js/babel/input.js', 'dist/js/babel/textInput.js',
'dist/js/babel/checkbox.js', 'dist/js/babel/checkbox.js',
'dist/js/babel/togglebutton.js', 'dist/js/babel/switch.js',
'dist/js/babel/radio.js', 'dist/js/babel/radio.js',
'dist/js/babel/fileInput.js', 'dist/js/babel/fileInput.js',
'dist/js/babel/bootstrapMaterialDesign.js', 'dist/js/babel/bootstrapMaterialDesign.js',

View File

@ -9,6 +9,7 @@
], ],
"coreJs": [ "coreJs": [
"../dist/js/babel/baseInput.js",
"../dist/js/babel/autofill.js", "../dist/js/babel/autofill.js",
"../dist/js/babel/bootstrapMaterialDesign.js", "../dist/js/babel/bootstrapMaterialDesign.js",
"../dist/js/babel/checkbox.js", "../dist/js/babel/checkbox.js",

164
js/src/baseInput.js Normal file
View 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

View File

@ -8,7 +8,7 @@
* *
* NOTE: If omitting use of this class, please note that the Input component must be * 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, * initialized prior to other decorating components such as radio, checkbox,
* togglebutton, fileInput. * switch, fileInput.
* *
*/ */
const BootstrapMaterialDesign = (($) => { const BootstrapMaterialDesign = (($) => {
@ -42,18 +42,18 @@ const BootstrapMaterialDesign = (($) => {
'.ripple' // generic marker class to add ripple to elements '.ripple' // generic marker class to add ripple to elements
] ]
}, },
input: { textInput: {
selector: [ selector: [
'input.form-control', 'input[type=text]',
'textarea.form-control', 'textarea',
'select.form-control' 'select'
] ]
}, },
checkbox: { checkbox: {
selector: '.checkbox > label > input[type=checkbox]' selector: '.checkbox > label > input[type=checkbox]'
}, },
togglebutton: { switch: {
selector: '.togglebutton > label > input[type=checkbox]' selector: '.switch > label > input[type=checkbox]'
}, },
radio: { radio: {
selector: '.radio > label > input[type=radio]' selector: '.radio > label > input[type=radio]'
@ -68,9 +68,9 @@ const BootstrapMaterialDesign = (($) => {
// create an ordered component list for instantiation // create an ordered component list for instantiation
instantiation: [ instantiation: [
'ripples', 'ripples',
'input', 'textInput',
'checkbox', 'checkbox',
'togglebutton', 'switch',
'radio', 'radio',
'fileInput', 'fileInput',
'autofill' 'autofill'

View File

@ -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' import Util from './util'
// Checkbox decorator, to be called after Input
const Checkbox = (($) => { const Checkbox = (($) => {
/** /**
@ -13,7 +17,11 @@ const Checkbox = (($) => {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = { 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 Definition
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
class Checkbox { class Checkbox extends BaseInput {
constructor(element, config) { constructor(element, config) {
this.$element = $(element) super(element, Default, config)
this.config = $.extend({}, Default, config)
this.$element.after(this.config.template) this.$element.after(this.config.template)
this.$formGroup = Util.findFormGroup(this.$element, false)
this._bindEventListeners()
} }
dispose() { dispose() {
$.removeData(this.$element, DATA_KEY) super.dispose(DATA_KEY)
this.$element = null }
this.$formGroup = null
this.config = null 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 // protected
_bindEventListeners() {
// 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 // checkboxes didn't appear to bubble to the document, so we'll bind these directly
this.$formGroup.find('.checkbox label').hover(() => { this.$formGroup.find('.checkbox label').hover(() => {
Util.addFormGroupFocus(this.$formGroup) Util.addFormGroupFocus(this.$formGroup)
}, () => { }, () => {
Util.removeFormGroupFocus(this.$formGroup) Util.removeFormGroupFocus(this.$formGroup)
}) })
}
addChangeListener() {
this.$element.change(() => { this.$element.change(() => {
this.$element.blur() this.$element.blur()
}) })
} }
// ------------------------------------------------------------------------
// private
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// static // static
static _jQueryInterface(config) { static _jQueryInterface(config) {

View File

@ -16,7 +16,6 @@ const FileInput = (($) => {
const ClassName = { const ClassName = {
IS_FILEINPUT: 'is-fileinput', IS_FILEINPUT: 'is-fileinput',
IS_EMPTY: 'is-empty'
} }
@ -44,6 +43,20 @@ const FileInput = (($) => {
this.config = null 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 // private
_bindEventListeners() { _bindEventListeners() {
@ -63,22 +76,14 @@ const FileInput = (($) => {
}) })
value = value.substring(0, value.length - 2) value = value.substring(0, value.length - 2)
if (value) { if (value) {
this._removeIsEmpty() this.removeIsEmpty()
} else { } else {
this._addIsEmpty() this.addIsEmpty()
} }
this.$formGroup.find('input.form-control[readonly]').val(value) 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
static _jQueryInterface(config) { static _jQueryInterface(config) {

View File

@ -1,19 +1,19 @@
//import Util from './util' //import Util from './util'
// Togglebutton decorator, to be called after Input // Switch decorator, to be called after Input
const Togglebutton = (($) => { const Switch = (($) => {
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Constants * Constants
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'togglebutton' const NAME = 'switch'
const DATA_KEY = `mdb.${NAME}` const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = { const Default = {
template: `<span class='toggle'></span>` template: `<span class='switch-decorator'></span>`
} }
/** /**
@ -21,7 +21,7 @@ const Togglebutton = (($) => {
* Class Definition * Class Definition
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
class Togglebutton { class Switch {
constructor(element, config) { constructor(element, config) {
this.$element = $(element) this.$element = $(element)
@ -47,7 +47,7 @@ const Togglebutton = (($) => {
let data = $element.data(DATA_KEY) let data = $element.data(DATA_KEY)
if (!data) { if (!data) {
data = new Togglebutton(this, config) data = new Switch(this, config)
$element.data(DATA_KEY, data) $element.data(DATA_KEY, data)
} }
}) })
@ -59,15 +59,15 @@ const Togglebutton = (($) => {
* jQuery * jQuery
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
$.fn[NAME] = Togglebutton._jQueryInterface $.fn[NAME] = Switch._jQueryInterface
$.fn[NAME].Constructor = Togglebutton $.fn[NAME].Constructor = Switch
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Togglebutton._jQueryInterface return Switch._jQueryInterface
} }
return Togglebutton return Switch
})(jQuery) })(jQuery)
export default Togglebutton export default Switch

View File

@ -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' import Util from './util'
const Input = (($) => { const TextInput = (($) => {
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Constants * Constants
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'input' const NAME = 'textInput'
const DATA_KEY = `mdb.${NAME}` const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
@ -15,23 +20,15 @@ const Input = (($) => {
convertInputSizeVariations: true, convertInputSizeVariations: true,
template: `<span class='material-input'></span>`, template: `<span class='material-input'></span>`,
formGroup: { formGroup: {
template: `<div class='form-group'></div>` autoCreate: true
} },
requiredClasses: ['form-control'],
invalidComponentMatches: [Checkbox, FileInput, Radio, Switch]
} }
const InputSizeConversions = { const InputSizeConversions = {
'input-lg': 'form-group-lg', 'textInput-lg': 'form-group-lg',
'input-sm': 'form-group-sm' 'textInput-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}` //,
} }
/** /**
@ -39,115 +36,95 @@ const Input = (($) => {
* Class Definition * Class Definition
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
class Input { class TextInput extends BaseInput {
constructor(element, config) { constructor(element, config) {
this.$element = $(element) super(element, Default, config)
this.config = $.extend({}, Default, config)
// Requires form-group standard markup (will add it if necessary)
this.$formGroup = this._findOrCreateFormGroup()
this._convertInputSizeVariations() this._convertInputSizeVariations()
// Initially mark as empty // Initially mark as empty
if (this._isEmpty()) { if (this.isEmpty()) {
this.$formGroup.addClass(ClassName.IS_EMPTY) this.addIsEmpty()
} }
// Add marker div the end of the form-group // Add marker div the end of the form-group
this.$formGroup.append(this.config.template) this.$formGroup.append(this.config.template)
this._bindEventListeners()
} }
dispose() { dispose() {
$.removeData(this.$element, DATA_KEY) super.dispose(DATA_KEY)
this.$element = null }
this.$formGroup = null
this.config = null 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 this.$element
.on('keydown paste', (event) => { .on('keydown paste', (event) => {
if (Util.isChar(event)) { if (Util.isChar(event)) {
this._removeIsEmpty() this.removeIsEmpty()
} }
}) })
.on('keyup change', (event) => { .on('keyup change', (event) => {
let isValid = (typeof this.$element[0].checkValidity === 'undefined' || this.$element[0].checkValidity())
if (this.$element.val() === '' && isValid) { // make sure empty is added back when there is a programmatic value change.
this._addIsEmpty() // 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 { } 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 // Further, even the bind method is being caught, but since we are already calling #checkValidity here, just alter
// the form-group on change. // 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. // 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. // BUT, I've left it here for backwards compatibility.
let isValid = (typeof this.$element[0].checkValidity === 'undefined' || this.$element[0].checkValidity())
if (isValid) { if (isValid) {
this._removeHasError() this.removeHasError()
} else { } else {
this._addHasError() 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) // private
}
_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() { _convertInputSizeVariations() {
if (!this.config.convertInputSizeVariations) { if (!this.config.convertInputSizeVariations) {
return 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) { for (let inputSize in InputSizeConversions) {
if (this.$element.hasClass(inputSize)) { if (this.$element.hasClass(inputSize)) {
this.$element.removeClass(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
static _jQueryInterface(config) { static _jQueryInterface(config) {
@ -173,7 +141,7 @@ const Input = (($) => {
let data = $element.data(DATA_KEY) let data = $element.data(DATA_KEY)
if (!data) { if (!data) {
data = new Input(this, config) data = new TextInput(this, config)
$element.data(DATA_KEY, data) $element.data(DATA_KEY, data)
} }
}) })
@ -185,15 +153,15 @@ const Input = (($) => {
* jQuery * jQuery
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
$.fn[NAME] = Input._jQueryInterface $.fn[NAME] = TextInput._jQueryInterface
$.fn[NAME].Constructor = Input $.fn[NAME].Constructor = TextInput
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Input._jQueryInterface return TextInput._jQueryInterface
} }
return Input return TextInput
})(jQuery) })(jQuery)
export default Input export default TextInput

View File

@ -16,15 +16,6 @@ const Util = (($) => {
transition: 'transitionend' transition: 'transitionend'
} }
const ClassName = {
IS_FOCUSED: 'is-focused',
FORM_GROUP: 'form-group'
}
const Selector = {
FORM_GROUP: `.${ClassName.FORM_GROUP}` //,
}
function transitionEndTest() { function transitionEndTest() {
if (window.QUnit) { if (window.QUnit) {
return false return false
@ -75,23 +66,10 @@ const Util = (($) => {
return false return false
}, },
addFormGroupFocus(formGroup) { assert(test, message) {
formGroup.addClass(ClassName.IS_FOCUSED) if (test) {
}, $.error(message)
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}`)
} }
return fg
} }
} }

View File

@ -9,7 +9,7 @@
@import 'reboot'; @import 'reboot';
@import 'buttons'; @import 'buttons';
@import 'checkboxes'; @import 'checkboxes';
@import 'togglebutton'; @import 'switch';
@import 'radios'; @import 'radios';
@import 'inputs'; @import 'inputs';
@import 'form'; @import 'form';

View File

@ -1,13 +1,13 @@
.togglebutton { .switch {
vertical-align: middle; vertical-align: middle;
&, label, input, .toggle { &, label, input, .switch-decorator {
user-select: none; user-select: none;
} }
label { label {
cursor: pointer; cursor: pointer;
color: $mdb-toggle-label-color; color: $mdb-switch-label-color;
// Hide original checkbox // Hide original checkbox
input[type=checkbox] { input[type=checkbox] {
@ -16,8 +16,8 @@
height: 0; height: 0;
} }
// Switch bg off and disabled // Switch bg off and disabled
.toggle, .switch-decorator,
input[type=checkbox][disabled] + .toggle { input[type=checkbox][disabled] + .switch-decorator {
content: ""; content: "";
display: inline-block; display: inline-block;
width: 30px; width: 30px;
@ -29,7 +29,7 @@
vertical-align: middle; vertical-align: middle;
} }
// Handle off // Handle off
.toggle:after { .switch-decorator:after {
content: ""; content: "";
display: inline-block; display: inline-block;
width: 20px; width: 20px;
@ -45,33 +45,33 @@
input[type=checkbox] { input[type=checkbox] {
// Handle disabled // Handle disabled
&[disabled] { &[disabled] {
& + .toggle:after, & + .switch-decorator:after,
&:checked + .toggle:after { &:checked + .switch-decorator:after {
background-color: #BDBDBD; background-color: #BDBDBD;
} }
} }
& + .toggle:active:after, & + .switch-decorator:active:after,
&[disabled] + .toggle: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); 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 // Ripple off and disabled
&:checked + .toggle:after { &:checked + .switch-decorator:after {
left: 15px; left: 15px;
} }
} }
& label input[type=checkbox]:checked { & label input[type=checkbox]:checked {
& + .toggle { & + .switch-decorator {
background-color: rgba($brand-primary, (50/100)); // Switch bg on background-color: rgba($brand-primary, (50/100)); // Switch bg on
} }
& + .toggle:after { & + .switch-decorator:after {
background-color: $brand-primary; // Handle on 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 box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba($brand-primary, (10/100)); // Ripple on
} }
} }

View File

@ -85,7 +85,7 @@ $mdb-popover-color: #ececec !default;
$mdb-dropdown-font-size: 16px !default; $mdb-dropdown-font-size: 16px !default;
// Toggle // Toggle
$mdb-toggle-label-color: $mdb-checkbox-label-color !default; $mdb-switch-label-color: $mdb-checkbox-label-color !default;
// Radio: // Radio:
$mdb-radio-label-color: $mdb-checkbox-label-color !default; $mdb-radio-label-color: $mdb-checkbox-label-color !default;