diff options
| author | schneefux <schneefux+commit@schneefux.xyz> | 2017-03-22 19:19:26 +0100 |
|---|---|---|
| committer | schneefux <schneefux+commit@schneefux.xyz> | 2017-03-22 19:19:26 +0100 |
| commit | d42c8b528eb595101954f478e0024e3f079f6fbf (patch) | |
| tree | 61a528cf94f1f6941f4683ae8736a4a0135c035e /templates/assets/bootstrap-material-design/js/baseInput.js | |
| download | minionlivesmatter-d42c8b528eb595101954f478e0024e3f079f6fbf.tar.gz minionlivesmatter-d42c8b528eb595101954f478e0024e3f079f6fbf.zip | |
first implementation of fun stats site
Diffstat (limited to 'templates/assets/bootstrap-material-design/js/baseInput.js')
| -rw-r--r-- | templates/assets/bootstrap-material-design/js/baseInput.js | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/templates/assets/bootstrap-material-design/js/baseInput.js b/templates/assets/bootstrap-material-design/js/baseInput.js new file mode 100644 index 0000000..5396ccb --- /dev/null +++ b/templates/assets/bootstrap-material-design/js/baseInput.js @@ -0,0 +1,321 @@ +import Base from './base' +import Util from './util' + +const BaseInput = (($) => { + + const ClassName = { + 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' + } + + const Selector = { + FORM_GROUP: `.${ClassName.FORM_GROUP}`, + 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 + } + + const Default = { + validate: false, + formGroup: { + required: false + }, + bmdFormGroup: { + template: `<span class='${ClassName.BMD_FORM_GROUP}'></span>`, + 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, + + // Prioritized find order for resolving the label to be used as an bmd-label if not specified in the markup + // - a function(thisComponent); or + // - a string selector used like $bmdFormGroup.find(selector) + // + // Note this only runs if $bmdFormGroup.find(Selector.BMD_LABEL_WILDCARD) fails to find a label (as authored in the markup) + // + selectors: [ + `.form-control-label`, // in the case of horizontal or inline forms, this will be marked + `> label` // usual case for text inputs, first child. Deeper would find toggle labels so don't do that. + ], + className: ClassName.BMD_LABEL_STATIC + }, + requiredClasses: [], + invalidComponentMatches: [], + convertInputSizeVariations: true + } + + const FormControlSizeMarkers = { + 'form-control-lg': 'bmd-form-group-lg', + 'form-control-sm': 'bmd-form-group-sm' + } + + /** + * ------------------------------------------------------------------------ + * 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 = {}) { + super($element, $.extend(true, {}, Default, config), properties) + + // Enforce no overlap between components to prevent side effects + this._rejectInvalidComponentMatches() + + // Enforce expected structure (if any) + this.rejectWithoutRequiredStructure() + + // Enforce required classes for a consistent rendering + this._rejectWithoutRequiredClasses() + + // Resolve the form-group first, it will be used for bmd-form-group if possible + // note: different components have different rules + this.$formGroup = this.findFormGroup(this.config.formGroup.required) + + // 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 + // rendering changes once added. + this.$bmdFormGroup = this.resolveMdbFormGroup() + + // Resolve and mark the bmdLabel if necessary as defined by the config + this.$bmdLabel = this.resolveMdbLabel() + + // Signal to the bmd-form-group that a form-control-* variation is being used + this.resolveMdbFormGroupSizing() + + this.addFocusListener() + this.addChangeListener() + + if(this.$element.val() != ''){ + this.addIsFilled() + } + } + + dispose(dataKey) { + super.dispose(dataKey) + this.$bmdFormGroup = null + this.$formGroup = null + } + + // ------------------------------------------------------------------------ + // protected + + rejectWithoutRequiredStructure() { + // implement + } + + addFocusListener() { + this.$element + .on('focus', () => { + this.addFormGroupFocus() + }) + .on('blur', () => { + this.removeFormGroupFocus() + }) + } + + addChangeListener() { + this.$element + .on('keydown paste', (event) => { + if (Util.isChar(event)) { + this.addIsFilled() + } + }) + .on('keyup change', () => { + + // 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()) { + this.removeIsFilled() + } else { + this.addIsFilled() + } + + if (this.config.validate) { + // Validation events do not bubble, so they must be attached directly to the text: http://jsfiddle.net/PEpRM/1/ + // Further, even the bind method is being caught, but since we are already calling #checkValidity here, just alter + // the form-group on change. + // + // NOTE: I'm not sure we should be intervening regarding validation, this seems better as a README and snippet of code. + // BUT, I've left it here for backwards compatibility. + let isValid = (typeof this.$element[0].checkValidity === 'undefined' || this.$element[0].checkValidity()) + if (isValid) { + this.removeHasDanger() + } else { + this.addHasDanger() + } + } + }) + } + + addHasDanger() { + this.$bmdFormGroup.addClass(ClassName.HAS_DANGER) + } + + removeHasDanger() { + this.$bmdFormGroup.removeClass(ClassName.HAS_DANGER) + } + + isEmpty() { + return (this.$element.val() === null || this.$element.val() === undefined || this.$element.val() === '') + } + + // Will add bmd-form-group to form-group or create a bmd-form-group if necessary + resolveMdbFormGroup() { + let mfg = this.findMdbFormGroup(false) + if (mfg === undefined || mfg.length === 0) { + if (this.config.bmdFormGroup.create && (this.$formGroup === undefined || this.$formGroup.length === 0)) { + // If a form-group doesn't exist (not recommended), take a guess and wrap the element (assuming no label). + // note: it's possible to make this smarter, but I need to see valid cases before adding any complexity. + + // this may be an input-group, wrap that instead + if(this.outerElement().parent().hasClass(ClassName.INPUT_GROUP)){ + this.outerElement().parent().wrap(this.config.bmdFormGroup.template) + } + else{ + this.outerElement().wrap(this.config.bmdFormGroup.template) + } + } else { + // a form-group does exist, add our marker class to it + this.$formGroup.addClass(ClassName.BMD_FORM_GROUP) + + // OLD: may want to implement this after all, see how the styling turns out, but using an existing form-group is less manipulation of the dom and therefore preferable + // A form-group does exist, so add an bmd-form-group wrapping it's internal contents + //fg.wrapInner(this.config.bmdFormGroup.template) + } + + mfg = this.findMdbFormGroup(this.config.bmdFormGroup.required) + } + + return mfg + } + + // Demarcation element (e.g. first child of a form-group) + // Subclasses such as file inputs may have different structures + outerElement() { + return this.$element + } + + // Will add bmd-label to bmd-form-group if not already specified + resolveMdbLabel() { + + let label = this.$bmdFormGroup.find(Selector.BMD_LABEL_WILDCARD) + if (label === undefined || label.length === 0) { + // we need to find it based on the configured selectors + label = this.findMdbLabel(this.config.label.required) + + if (label === undefined || label.length === 0) { + // no label found, and finder did not require one + } else { + // a candidate label was found, add the configured default class name + label.addClass(this.config.label.className) + } + } + + return label + } + + // Find bmd-label variant based on the config selectors + findMdbLabel(raiseError = true) { + let label = null + + // use the specified selector order + for (let selector of this.config.label.selectors) { + if ($.isFunction(selector)) { + label = selector(this) + } else { + label = this.$bmdFormGroup.find(selector) + } + + if (label !== undefined && label.length > 0) { + break + } + } + + if (label.length === 0 && raiseError) { + $.error(`Failed to find ${Selector.BMD_LABEL_WILDCARD} within form-group for ${Util.describe(this.$element)}`) + } + return label + } + + // Find bmd-form-group + findFormGroup(raiseError = true) { + let fg = this.$element.closest(Selector.FORM_GROUP) + if (fg.length === 0 && raiseError) { + $.error(`Failed to find ${Selector.FORM_GROUP} for ${Util.describe(this.$element)}`) + } + return fg + } + + // Due to the interconnected nature of labels/inputs/help-blocks, signal the bmd-form-group-* size variation based on + // a found form-control-* size + resolveMdbFormGroupSizing() { + if (!this.config.convertInputSizeVariations) { + return + } + + // Modification - Change text-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants) + for (let inputSize in FormControlSizeMarkers) { + if (this.$element.hasClass(inputSize)) { + //this.$element.removeClass(inputSize) + this.$bmdFormGroup.addClass(FormControlSizeMarkers[inputSize]) + } + } + } + + // ------------------------------------------------------------------------ + // private + _rejectInvalidComponentMatches() { + for (let otherComponent of this.config.invalidComponentMatches) { + otherComponent.rejectMatch(this.constructor.name, this.$element) + } + } + + _rejectWithoutRequiredClasses() { + 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}`) + } + } + } + + // ------------------------------------------------------------------------ + // static + + } + + return BaseInput + +})(jQuery) + +export default BaseInput |
