UNPKG

nest-parrot

Version:
349 lines (345 loc) 10.7 kB
/** * Created by brad.wu on 8/18/2015. * depends cell components which will be renderred in cell. * * the following settings are shared with all form cell components * layout: { * dataId: string, * comp: { * paintRequired: boolean, * labelDirection: string, * labelWidth: number * }, * css: { * cell: string, * label: string * } * } */ (function (window, $, React, ReactDOM, $pt) { var NFormCell = React.createClass($pt.defineCellComponent({ displayName: 'NFormCell', keepRender: true, statics: { REQUIRED_ICON: 'asterisk', TOOLTIP_ICON: 'question-circle', LABEL_WIDTH: 4, __componentRenderer: {}, registerComponentRenderer: function (type, func) { $pt.LayoutHelper.registerComponentRenderer(type, func); }, getComponentRenderer: function (type) { return $pt.LayoutHelper.getComponentRenderer(type); } }, getDefaultProps: function () { return { defaultOptions: { // paintRequired: true }, direction: 'vertical' }; }, beforeWillUpdate: function (nextProps) { this.destroyPopover(); this.removeRequiredDependencyMonitor(); }, beforeDidUpdate: function (prevProps, prevState) { this.renderPopover(); this.addRequiredDependencyMonitor(); }, beforeDidMount: function () { this.renderPopover(); this.addRequiredDependencyMonitor(); }, beforeWillUnmount: function () { this.destroyPopover(); this.removeRequiredDependencyMonitor(); }, destroyPopover: function () { var comp = this.refs.comp; if (comp != null) { $(ReactDOM.findDOMNode(comp)).popover("destroy"); } var tooltip = this.refs.tooltip; if (tooltip != null) { $(ReactDOM.findDOMNode(tooltip)).popover('destroy'); } }, /** * render error popover */ renderPopover: function () { var tooltip = this.getComponentOption('tooltip'); if (tooltip != null) { if (typeof tooltip === 'string') { tooltip = { text: tooltip }; } var tooltipPopover = { title: tooltip.title, content: tooltip.text, placement: tooltip.position ? tooltip.position: 'top', trigger: 'hover', container: 'body', html: true, animation: false }; $(ReactDOM.findDOMNode(this.refs.tooltip)).popover(tooltipPopover); } if ($pt.ComponentConstants.ERROR_POPOVER && this.getLayout().getComponentType().popover !== false && this.getModel().hasError(this.getDataId())) { var messages = this.getModel().getError(this.getDataId()); var _this = this; var popover = { placement: 'top', trigger: 'hover', html: true, content: messages.map(function (msg) { return "<span style='display:block'>" + msg.format([_this.getLayout().getLabel(_this)]) + "</span>"; }), container: 'body', // false is very import, since when destroy popover, // the really destroy will be invoked by some delay, // and before really destory invoked, // the new popover is bind by componentDidUpdate method. // and finally new popover will be destroyed. animation: false, template: '<div class="popover form-cell-error" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' }; var comp = this.refs.comp; if (comp != null) { var dom = $(ReactDOM.findDOMNode(comp)) dom.popover(popover); if (dom.has($(':focus')).length != 0) { dom.popover('show'); } } } }, /** * render input component * @param componentDefinition */ renderInputComponent: function (componentDefinition) { // always pass form model to component, // since maybe getModel() returns inner model which defined with comp: {model: another} var direction = this.props.direction ? this.props.direction : 'vertical'; if (componentDefinition.render) { // user defined component return componentDefinition.render.call(this, this.getFormModel(), this.getLayout(), direction, this.isViewMode()); } // pre-defined components var type = componentDefinition.type; if (!type) { type = "text"; } var innerComponent = $pt.LayoutHelper.getComponentRenderer(type).call(this, this.getFormModel(), this.getLayout(), direction, this.isViewMode()); return (<div ref="comp"> {innerComponent} </div>); }, isRequiredSignPaint: function() { if (this.isViewMode()) { return false; } // calculate the 'paintRequired' attribute var requiredPaint = this.getComponentOption("paintRequired"); if (requiredPaint == null) { // not given, calculate 'required' rules requiredPaint = this.getModel().isRequired(this.getDataId()); return requiredPaint ? true : this.isRequiredSignNeeded(); } else if (typeof requiredPaint === 'boolean') { // boolean type, return directly return requiredPaint; } else if (typeof requiredPaint === 'function') { requiredPaint = requiredPaint.call(this); if (typeof requiredPaint === 'boolean') { // boolean type, return directly return requiredPaint; } } // calculate from model validator return this.getModel().isRequired(this.getDataId(), requiredPaint); }, /** * render label * @returns {XML} */ renderLabel: function () { var labelIcon = this.getComponentOption('labelIcon'); var iconLabel = labelIcon ? <span className={'label-icon fa fa-fw fa-' + labelIcon} /> : null; var requireIconCSS = { fa: true, 'fa-fw': true, required: true }; requireIconCSS['fa-' + NFormCell.REQUIRED_ICON] = true; var requiredLabel = this.isRequiredSignPaint() ? (<span className={$pt.LayoutHelper.classSet(requireIconCSS)}/>) : null; var tooltip = this.getComponentOption('tooltip'); var tooltipIcon = null; var tooltipCSS = { fa: true, 'fa-fw': true, 'n-form-cell-tooltip': true }; if (tooltip != null) { tooltipCSS['fa-' + NFormCell.TOOLTIP_ICON] = true; tooltipIcon = <span className={$pt.LayoutHelper.classSet(tooltipCSS)} ref='tooltip'/>; } return (<span className={this.getLayout().getLabelCSS()} onClick={this.onLabelClicked} ref="label"> {iconLabel} {this.getLayout().getLabel(this)} {tooltipIcon} {requiredLabel} {this.getComponentOption('customLabelAddon')} </span>); }, /** * render * @returns {XML} */ render: function () { if (!this.isVisible()) { return (<div className={this.getCSSClassName() + ' n-form-cell-invisible'}/>); } else { var css = this.getCSSClassName(); if (this.getModel().hasError(this.getDataId()) && this.getLayout().getComponentType().renderError !== false) { css += " has-error"; } if (!this.isEnabled()) { css += ' n-form-cell-disabled'; } // read component definition var type = this.getLayout().getComponentType(); if (type.label === false) { return (<div className={css} ref='div'> {this.renderInputComponent(type)} </div>); } else { var labelDirection = this.getComponentOption("labelDirection"); if (labelDirection == null) { labelDirection = this.props.direction ? this.props.direction : 'vertical'; } if (labelDirection != 'vertical') { return (<div className={css + ' horizontal-label'} ref='div'> <div className='row'> <div className={this.getHorizontalLabelCSS()}> {this.renderLabel()} </div> <div className={this.getHorizontalComponentCSS()}> {this.renderInputComponent(type)} </div> </div> </div>); } else { return (<div className={css + ' vertical-label'} ref='div'> {this.renderLabel()} {this.renderInputComponent(type)} </div>); } } } }, /** * on model change * @param evt */ onModelChanged: function (evt) { var delay = this.getValidationOption('delay', this.getLayout().getComponentType().delay); if (delay != null && delay > 0) { if (this.state.delayedValidation) { window.clearTimeout(this.state.delayedValidation); } this.state.delayedValidation = window.setTimeout(function() { this.validate(); }.bind(this), delay); } else { // no delay for validation this.validate(); } }, /** * on label clicked */ onLabelClicked: function () { $(ReactDOM.findDOMNode(this.refs.comp)).focus(); }, /** * get css class * @returns {string} */ getCSSClassName: function () { var width = this.getLayout().getWidth(); var css = { 'n-form-cell': true }; if (typeof width === 'number' || typeof width === 'string') { css['col-sm-' + width] = true; css['col-md-' + width] = true; css['col-lg-' + width] = true; } else { Object.keys(width).forEach(function(key) { css['col-' + key + '-' + width[key]] = true; }); if (typeof width.sm === 'undefined') { css['col-sm-' + width.width] = true; } if (typeof width.md === 'undefined') { css['col-md-' + width.width] = true; } if (typeof width.lg === 'undefined') { css['col-lg-' + width.width] = true; } // css['col-sm-' + (width.sm ? width.sm : width.width)] = true; // css['col-md-' + (width.md ? width.md : width.width)] = true; // css['col-lg-' + (width.lg ? width.lg : width.width)] = true; } return this.getLayout().getCellCSS($pt.LayoutHelper.classSet(css)); }, /** * get label css when horizontal direction * @returns {string} */ getHorizontalLabelCSS: function () { var width = this.getHorizontalLabelWidth(); return "col-sm-" + width + " col-md-" + width + " col-lg-" + width; }, /** * get component css when horizontal direction * @returns {string} */ getHorizontalComponentCSS: function () { var width = 12 - this.getHorizontalLabelWidth(); return "col-sm-" + width + " col-md-" + width + " col-lg-" + width; }, getHorizontalLabelWidth: function () { var width = this.getComponentOption('labelWidth'); return width ? width : NFormCell.LABEL_WIDTH; }, /** * register to component central */ registerToComponentCentral: function() { var id = this.getComponentCentralId(); if (id) { $pt.LayoutHelper.registerComponent(id + '@cell', this); } }, /** * unregsiter from component central */ unregisterFromComponentCentral: function() { var id = this.getComponentCentralId(); if (id) { $pt.LayoutHelper.unregisterComponent(id + '@cell', this); } } })); $pt.Components.NFormCell = NFormCell; }(window, jQuery, React, ReactDOM, $pt));