discretely separated all form input types into discrete es6 classes to allow for easy configuration/enforcement of markup/classes/structure

This commit is contained in:
Kevin Ross 2015-12-05 17:45:38 -06:00
parent 6f0e41a486
commit 108da48a0b
17 changed files with 426 additions and 261 deletions

View File

@ -105,11 +105,13 @@ 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/textInput.js': 'js/src/textInput.js',
'dist/js/babel/text.js': 'js/src/text.js',
'dist/js/babel/textarea.js': 'js/src/textarea.js',
'dist/js/babel/select.js': 'js/src/select.js',
'dist/js/babel/checkbox.js': 'js/src/checkbox.js',
'dist/js/babel/switch.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/fileInput.js': 'js/src/file.js',
'dist/js/babel/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.js',
}
},
@ -128,11 +130,13 @@ 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/textInput.js': 'js/src/textInput.js',
'docs/dist/js/babel/text.js': 'js/src/text.js',
'docs/dist/js/babel/textarea.js': 'js/src/textarea.js',
'docs/dist/js/babel/select.js': 'js/src/select.js',
'docs/dist/js/babel/checkbox.js': 'js/src/checkbox.js',
'docs/dist/js/babel/switch.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/fileInput.js': 'js/src/file.js',
'docs/dist/js/babel/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.js',
}
},
@ -154,11 +158,13 @@ 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/textInput.js': 'js/src/textInput.js',
'dist/js/umd/text.js': 'js/src/text.js',
'dist/js/umd/textarea.js': 'js/src/textarea.js',
'dist/js/umd/select.js': 'js/src/select.js',
'dist/js/umd/checkbox.js': 'js/src/checkbox.js',
'dist/js/umd/switch.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/fileInput.js': 'js/src/file.js',
'dist/js/umd/bootstrapMaterialDesign.js': 'js/src/bootstrapMaterialDesign.js',
}
}
@ -216,11 +222,13 @@ module.exports = function (grunt) {
'dist/js/babel/util.js',
'dist/js/babel/ripples.js',
'dist/js/babel/autofill.js',
'dist/js/babel/textInput.js',
'dist/js/babel/text.js',
'dist/js/babel/textarea.js',
'dist/js/babel/select.js',
'dist/js/babel/checkbox.js',
'dist/js/babel/switch.js',
'dist/js/babel/radio.js',
'dist/js/babel/fileInput.js',
'dist/js/babel/file.js',
'dist/js/babel/bootstrapMaterialDesign.js',
],
dest: 'dist/js/<%= pkg.name %>.js'

View File

@ -15,7 +15,9 @@
"../dist/js/babel/bootstrapMaterialDesign.js",
"../dist/js/babel/checkbox.js",
"../dist/js/babel/fileInput.js",
"../dist/js/babel/textInput.js",
"../dist/js/babel/text.js",
"../dist/js/babel/textarea.js",
"../dist/js/babel/select.js",
"../dist/js/babel/radio.js",
"../dist/js/babel/ripples.js",
"../dist/js/babel/switch.js",

View File

@ -1,5 +1,3 @@
//import Util from './util'
const Autofill = (($) => {
/**

View File

@ -1,13 +1,16 @@
import Util from './util'
const BaseInput = (($) => {
const Default = {
formGroup: {
template: `<div class='form-group'></div>`,
required: true,
autoCreate: false
autoCreate: true
},
requiredClasses: ['form-control'],
invalidComponentMatches: []
requiredClasses: [],
invalidComponentMatches: [],
convertInputSizeVariations: true
}
const ClassName = {
@ -20,6 +23,11 @@ const BaseInput = (($) => {
FORM_GROUP: `.${ClassName.FORM_GROUP}` //,
}
const FormControlSizeConversions = {
'form-control-lg': 'form-group-lg',
'form-control-sm': 'form-group-sm'
}
/**
* ------------------------------------------------------------------------
* Class Definition
@ -27,9 +35,9 @@ const BaseInput = (($) => {
*/
class BaseInput {
constructor(element, defaultConfig, config) {
constructor(element, config) {
this.$element = $(element)
this.config = $.extend({}, Default, defaultConfig, config)
this.config = $.extend({}, Default, config)
// Enforce no overlap between components to prevent side effects
this._rejectInvalidComponentMatches()
@ -48,6 +56,8 @@ const BaseInput = (($) => {
// different components have different rules, always run this separately
this.$formGroup = this.findFormGroup(this.config.formGroup.required)
this._convertFormControlSizeVariations()
this.addFocusListener()
this.addChangeListener()
}
@ -67,11 +77,45 @@ const BaseInput = (($) => {
}
addFocusListener() {
// implement
this.$element
.on('focus', () => {
this.addFormGroupFocus()
})
.on('blur', () => {
this.removeFormGroupFocus()
})
}
addChangeListener() {
// implement
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()
}
})
}
addFormGroupFocus() {
@ -120,16 +164,7 @@ const BaseInput = (($) => {
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 ${this.$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)
$.error(`Failed to find form-group for ${Util.describe(this.$element)}`)
}
return fg
}
@ -137,15 +172,45 @@ const BaseInput = (($) => {
// ------------------------------------------------------------------------
// private
_rejectInvalidComponentMatches() {
for (let otherComponent in this.config.invalidComponentMatches) {
for (let otherComponent of this.config.invalidComponentMatches) {
otherComponent.rejectMatch(this.constructor.name, this.$element)
}
}
_rejectWithoutRequiredClasses() {
for (let requiredClass in this.config.requiredClasses) {
if (!this.$element.hasClass(requiredClass)) {
$.error(`${this.constructor.name} elements require class: ${requiredClass}`)
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}`)
}
}
}
_convertFormControlSizeVariations() {
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.$formGroup.addClass(FormControlSizeConversions[inputSize])
}
}
}

View File

@ -1,8 +1,4 @@
import BaseInput from './baseInput'
//import TextInput from './textInput'
//import FileInput from './fileInput'
//import Radio from './radio'
//import Switch from './switch'
import Util from './util'
const BaseToggle = (($) => {
@ -13,9 +9,6 @@ const BaseToggle = (($) => {
* ------------------------------------------------------------------------
*/
const Default = {
formGroup: {
autoCreate: true
}
}
const Selector = {
@ -30,7 +23,7 @@ const BaseToggle = (($) => {
class BaseToggle extends BaseInput {
constructor(element, config, inputType, outerClass) {
super(element, Default, config)
super(element, $.extend({}, Default, config))
this.$element.after(this.config.template)
// '.checkbox|switch|radio > label > input[type=checkbox|radio]'
// '.${this.outerClass} > label > input[type=${this.inputType}]'

View File

@ -1,15 +1,8 @@
//import Util from './util'
/**
* $.bootstrapMaterialDesign(config) is a macro class to configure the components generally
* used in Material Design for Bootstrap. You may pass overrides to the configurations
* which will be passed into each component, or you may omit use of this class and
* configure each component separately.
*
* 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,
* switch, fileInput.
*
*/
const BootstrapMaterialDesign = (($) => {
@ -42,12 +35,14 @@ const BootstrapMaterialDesign = (($) => {
'.ripple' // generic marker class to add ripple to elements
]
},
textInput: {
selector: [
'input[type=text]',
'textarea',
'select'
]
text: {
selector: ['input[type=text]']
},
textarea: {
selector: ['textarea']
},
select: {
selector: ['select']
},
checkbox: {
selector: '.checkbox > label > input[type=checkbox]'
@ -58,7 +53,7 @@ const BootstrapMaterialDesign = (($) => {
radio: {
selector: '.radio > label > input[type=radio]'
},
fileInput: {
file: {
selector: 'input[type=file]'
},
autofill: {
@ -68,11 +63,13 @@ const BootstrapMaterialDesign = (($) => {
// create an ordered component list for instantiation
instantiation: [
'ripples',
'textInput',
'checkbox',
'switch',
'file',
'radio',
'fileInput',
'switch',
'text',
'textarea',
'select',
'autofill'
]
}

View File

@ -1,7 +1,9 @@
import BaseToggle from './baseToggle'
import TextInput from './textInput'
import FileInput from './fileInput'
import Text from './text'
import File from './file'
import Radio from './radio'
import Textarea from './textare'
import Select from './select'
import Util from './util'
const Checkbox = (($) => {
@ -16,8 +18,7 @@ const Checkbox = (($) => {
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
template: `<span class='checkbox-decorator'><span class='check'></span></span>`,
invalidComponentMatches: [FileInput, Radio, TextInput]
template: `<span class='checkbox-decorator'><span class='check'></span></span>`
}
/**
@ -28,7 +29,9 @@ const Checkbox = (($) => {
class Checkbox extends BaseToggle {
constructor(element, config, inputType = NAME, outerClass = NAME) {
super(element, $.extend({}, Default, config), inputType, outerClass)
super(element, $.extend({
invalidComponentMatches: [File, Radio, Text, Textarea, Select]
}, Default, config), inputType, outerClass)
}
dispose() {

View File

@ -2,30 +2,24 @@ import BaseInput from './baseInput'
import Checkbox from './checkbox'
import Radio from './radio'
import Switch from './switch'
import TextInput from './textInput'
import Text from './text'
import Textarea from './textare'
import Select from './select'
import Util from './util'
// FileInput decorator, to be called after Input
const FileInput = (($) => {
const File = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'fileInput'
const NAME = 'file'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
formGroup: {
autoCreate: true
},
invalidComponentMatches: [Checkbox, Radio, Switch, TextInput]
}
const ClassName = {
IS_FILEINPUT: 'is-fileinput'
IS_FILE: 'is-file'
}
const Selector = {
@ -37,12 +31,12 @@ const FileInput = (($) => {
* Class Definition
* ------------------------------------------------------------------------
*/
class FileInput extends BaseInput {
class File extends BaseInput {
constructor(element, config) {
super(element, Default, config)
super(element, $.extend({invalidComponentMatches: [Checkbox, Radio, Text, Textarea, Select, Switch]}, config))
this.$formGroup.addClass(ClassName.IS_FILEINPUT)
this.$formGroup.addClass(ClassName.IS_FILE)
}
dispose() {
@ -105,7 +99,7 @@ const FileInput = (($) => {
let data = $element.data(DATA_KEY)
if (!data) {
data = new FileInput(this, config)
data = new File(this, config)
$element.data(DATA_KEY, data)
}
})
@ -117,15 +111,15 @@ const FileInput = (($) => {
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = FileInput._jQueryInterface
$.fn[NAME].Constructor = FileInput
$.fn[NAME] = File._jQueryInterface
$.fn[NAME].Constructor = File
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return FileInput._jQueryInterface
return File._jQueryInterface
}
return FileInput
return File
})(jQuery)
export default FileInput
export default File

View File

@ -1,11 +1,10 @@
import BaseToggle from './baseToggle'
import TextInput from './textInput'
import FileInput from './fileInput'
import Text from './text'
import File from './file'
import Checkbox from './checkbox'
import Switch from './switch'
import Util from './util'
// Radio decorator, to be called after Input
const Radio = (($) => {
/**
@ -18,8 +17,7 @@ const Radio = (($) => {
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
template: `<span class='radio-decorator'></span><span class='check'></span>`,
invalidComponentMatches: [Checkbox, FileInput, Switch, TextInput]
template: `<span class='radio-decorator'></span><span class='check'></span>`
}
/**
@ -30,7 +28,9 @@ const Radio = (($) => {
class Radio extends BaseToggle {
constructor(element, config) {
super(element, $.extend({}, Default, config), NAME, NAME)
super(element, $.extend({
invalidComponentMatches: [Checkbox, File, Switch, Text]
}, Default, config), NAME, NAME)
}
dispose() {

87
js/src/select.js Normal file
View File

@ -0,0 +1,87 @@
import Checkbox from './checkbox'
import File from './file'
import Radio from './radio'
import Switch from './switch'
import Text from './text'
import Textarea from './textare'
import Util from './util'
const Select = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'select'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
requiredClasses: ['form-control||c-select']
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Select extends Text {
constructor(element, config) {
super(element, $.extend({invalidComponentMatches: [Checkbox, File, Radio, Switch, Text, Textarea]}, Default, config))
}
dispose() {
super.dispose(DATA_KEY)
}
static matches($element) {
if ($element.prop('tagName') === 'select') {
return true
}
return false
}
static rejectMatch(component, $element) {
Util.assert(this.matches($element), `${component} component is invalid for <select>.`)
}
// ------------------------------------------------------------------------
// protected
// ------------------------------------------------------------------------
// private
// ------------------------------------------------------------------------
// static
static _jQueryInterface(config) {
return this.each(function () {
let $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Select(this, config)
$element.data(DATA_KEY, data)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Select._jQueryInterface
$.fn[NAME].Constructor = Select
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Select._jQueryInterface
}
return Select
})(jQuery)
export default Select

View File

@ -1,6 +1,5 @@
import Checkbox from './checkbox'
// Switch decorator, to be called after Input
const Switch = (($) => {
/**

97
js/src/text.js Normal file
View File

@ -0,0 +1,97 @@
import BaseInput from './baseInput'
import Checkbox from './checkbox'
import File from './file'
import Radio from './radio'
import Switch from './switch'
import Textarea from './textare'
import Select from './select'
import Util from './util'
const Text = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'text'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
template: `<span class='text-input-decorator'></span>`,
requiredClasses: ['form-control']
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Text extends BaseInput {
constructor(element, config) {
super(element, $.extend({invalidComponentMatches: [Checkbox, File, Radio, Select, Switch, Textarea]}, Default, config))
// Initially mark as empty
if (this.isEmpty()) {
this.addIsEmpty()
}
// Add marker div the end of the form-group
this.$formGroup.append(this.config.template)
}
dispose(dataKey = DATA_KEY) {
super.dispose(dataKey)
}
static matches($element) {
if ($element.attr('type') === 'text') {
return true
}
return false
}
static rejectMatch(component, $element) {
Util.assert(this.matches($element), `${component} component is invalid for type='text'.`)
}
// ------------------------------------------------------------------------
// protected
// ------------------------------------------------------------------------
// private
// ------------------------------------------------------------------------
// static
static _jQueryInterface(config) {
return this.each(function () {
let $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Text(this, config)
$element.data(DATA_KEY, data)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Text._jQueryInterface
$.fn[NAME].Constructor = Text
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Text._jQueryInterface
}
return Text
})(jQuery)
export default Text

View File

@ -1,167 +0,0 @@
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 TextInput = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'textInput'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {
convertInputSizeVariations: true,
template: `<span class='material-input'></span>`,
formGroup: {
autoCreate: true
},
requiredClasses: ['form-control'],
invalidComponentMatches: [Checkbox, FileInput, Radio, Switch]
}
const InputSizeConversions = {
'textInput-lg': 'form-group-lg',
'textInput-sm': 'form-group-sm'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class TextInput extends BaseInput {
constructor(element, config) {
super(element, Default, config)
this._convertInputSizeVariations()
// Initially mark as empty
if (this.isEmpty()) {
this.addIsEmpty()
}
// Add marker div the end of the form-group
this.$formGroup.append(this.config.template)
}
dispose() {
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'.`)
}
// ------------------------------------------------------------------------
// protected
addFocusListener() {
this.$element
.on('focus', () => {
this.addFormGroupFocus()
})
.on('blur', () => {
this.removeFormGroupFocus()
})
}
addChangeListener() {
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 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()
} else {
this.addHasError()
}
})
}
// ------------------------------------------------------------------------
// private
_convertInputSizeVariations() {
if (!this.config.convertInputSizeVariations) {
return
}
// 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)
this.$formGroup.addClass(InputSizeConversions[inputSize])
}
}
}
// ------------------------------------------------------------------------
// static
static _jQueryInterface(config) {
return this.each(function () {
let $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new TextInput(this, config)
$element.data(DATA_KEY, data)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = TextInput._jQueryInterface
$.fn[NAME].Constructor = TextInput
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return TextInput._jQueryInterface
}
return TextInput
})(jQuery)
export default TextInput

85
js/src/textarea.js Normal file
View File

@ -0,0 +1,85 @@
import Checkbox from './checkbox'
import File from './file'
import Radio from './radio'
import Switch from './switch'
import Text from './text'
import Select from './select'
import Util from './util'
const Textarea = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'textarea'
const DATA_KEY = `mdb.${NAME}`
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Default = {}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Textarea extends Text {
constructor(element, config) {
super(element, $.extend({invalidComponentMatches: [Checkbox, File, Radio, Text, Select, Switch]}, Default, config))
}
dispose() {
super.dispose(DATA_KEY)
}
static matches($element) {
if ($element.prop('tagName') === 'textarea') {
return true
}
return false
}
static rejectMatch(component, $element) {
Util.assert(this.matches($element), `${component} component is invalid for <textarea>.`)
}
// ------------------------------------------------------------------------
// protected
// ------------------------------------------------------------------------
// private
// ------------------------------------------------------------------------
// static
static _jQueryInterface(config) {
return this.each(function () {
let $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Textarea(this, config)
$element.data(DATA_KEY, data)
}
})
}
}
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Textarea._jQueryInterface
$.fn[NAME].Constructor = Textarea
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Textarea._jQueryInterface
}
return Textarea
})(jQuery)
export default Textarea

View File

@ -70,6 +70,10 @@ const Util = (($) => {
if (test) {
$.error(message)
}
},
describe($element) {
return `${$element[0].outerHTML.split('>')[0]}>`
}
}

View File

@ -220,7 +220,7 @@
box-shadow: none;
transition-duration: 0.3s;
.material-input:after {
.text-input-decorator:after {
background-color: $brand-primary;
}
}
@ -259,7 +259,7 @@
select {
appearance: none; // Fix for OS X
& ~ .material-input:after {
& ~ .text-input-decorator:after {
display: none;
}
}

View File

@ -159,8 +159,8 @@
// margin: 0;
// padding: 0;
//
// .material-input:before,
// &.is-focused .material-input:after {
// .text-input-decorator:before,
// &.is-focused .text-input-decorator:after {
// background-color: inherit;
// }
// }