UNPKG

generator-bingo-dig-h5

Version:

Bingo-dig-h5 generator by BINGO.DIG

984 lines (852 loc) 35 kB
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['react'], factory); } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require('react')); } else { // Browser globals (root is window) root.Reactable = factory(root.React); } }(this, function (React) { "use strict"; var exports = {}; // Array.prototype.map polyfill - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Polyfill // Production steps of ECMA-262, Edition 5, 15.4.4.19 // Reference: http://es5.github.io/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback, thisArg) { var T, A, k; if (this === null) { throw new TypeError(" this is null or not defined"); } var O = Object(this); var len = O.length >>> 0; if (typeof callback !== "function") { throw new TypeError(callback + " is not a function"); } if (arguments.length > 1) { T = thisArg; } A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); A[k] = mappedValue; } k++; } return A; }; } // Array.prototype.indexOf polyfill for IE8 if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) { from += len; } for (; from < len; from++) { if (from in this && this[from] === elt) { return from; } } return -1; }; } // Array.prototype.find polyfill - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find if (!Array.prototype.find) { Object.defineProperty(Array.prototype, 'find', { enumerable: false, configurable: true, writable: true, value: function(predicate) { if (this === null) { throw new TypeError('Array.prototype.find called on null or undefined'); } if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } var list = Object(this); var length = list.length >>> 0; var thisArg = arguments[1]; var value; for (var i = 0; i < length; i++) { if (i in list) { value = list[i]; if (predicate.call(thisArg, value, i, list)) { return value; } } } return undefined; } }); } if (!Array.isArray) { Array.isArray = function (value) { return Object.prototype.toString.call(value) === '[object Array]'; }; } if (!Object.assign) { Object.defineProperty(Object, "assign", { enumerable: false, configurable: true, writable: true, value: function(target, firstSource) { if (target === undefined || target === null) throw new TypeError("Cannot convert first argument to object"); var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) continue; var keysArray = Object.keys(Object(nextSource)); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) to[nextKey] = nextSource[nextKey]; } } return to; } }); } function Unsafe(content) { this.content = content; } Unsafe.prototype.toString = function() { return this.content; }; function stringable(thing) { return thing !== null && typeof(thing) !== 'undefined' && typeof(thing.toString === 'function'); } // this is a bit hacky - it'd be nice if React exposed an API for this function isReactComponent(thing) { return thing !== null && typeof(thing) === 'object' && typeof(thing.props) !== 'undefined'; } React.Children.children = function(children) { return React.Children.map(children, function(x) { return x; }) || []; }; exports.unsafe = function(str) { return new Unsafe(str); }; exports.Sort = { Numeric: function(a, b) { var valA = parseFloat(a.toString().replace(/,/g,'')); var valB = parseFloat(b.toString().replace(/,/g,'')); // Sort non-numeric values alphabetically at the bottom of the list if (isNaN(valA) && isNaN(valB)) { valA = a; valB = b; } else { if (isNaN(valA)) { return 1; } if (isNaN(valB)) { return -1; } } if (valA < valB) { return -1; } if (valA > valB) { return 1; } return 0; }, NumericInteger: function(a, b) { if (isNaN(a) || isNaN(b)) { return a > b ? 1 : -1; } return a - b; }, Currency: function(a, b) { // Parse out dollar signs, then do a regular numeric sort // TODO: handle non-American currency if (a[0] === '$') { a = a.substring(1); } if (b[0] === '$') { b = b.substring(1); } return exports.Sort.Numeric(a, b); }, Date: function(a, b) { // Note: this function tries to do a standard javascript string -> date conversion // If you need more control over the date string format, consider using a different // date library and writing your own function var valA = Date.parse(a); var valB = Date.parse(b); // Handle non-date values with numeric sort // Sort non-numeric values alphabetically at the bottom of the list if (isNaN(valA) || isNaN(valB)) { return exports.Sort.Numeric(a, b); } if (valA > valB) { return 1; } if (valB > valA) { return -1; } return 0; }, CaseInsensitive: function(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); } }; var Td = exports.Td = React.createClass({ handleClick: function(e){ if (typeof this.props.handleClick === 'function') { return this.props.handleClick(e, this); } }, render: function() { var tdProps = { className: this.props.className, onClick: this.handleClick }; // Attach any properties on the column to this Td object to allow things like custom event handlers if (typeof(this.props.column) === 'object') { for (var key in this.props.column) { if (key !== 'key' && key !== 'name') { tdProps[key] = this.props.column[key]; } } } var data = this.props.data; if (typeof(this.props.children) !== 'undefined') { if (isReactComponent(this.props.children)) { data = this.props.children; } else if ( typeof(this.props.data) === 'undefined' && stringable(this.props.children) ) { data = this.props.children.toString(); } if (this.props.children instanceof Unsafe) { tdProps.dangerouslySetInnerHTML = { __html: this.props.children.toString() }; } else { tdProps.children = data; } } return React.DOM.td(tdProps); } }); var Tr = exports.Tr = React.createClass({ statics: { childNode: Td, dataType: 'object' }, render: function() { var children = toArray(React.Children.children(this.props.children)); var className = this.props.border ? "dig-has-border" : ""; if ( this.props.data && this.props.columns && typeof this.props.columns.map === 'function' ) { if (typeof(children.concat) === 'undefined') { console.log(children); } children = children.concat(this.props.columns.map(function(column, i) { if (this.props.data.hasOwnProperty(column.key)) { var value = this.props.data[column.key]; var props = {}; if ( typeof(value) !== 'undefined' && value !== null && value.__reactableMeta === true ) { props = value.props; value = value.value; } return <Td column={column} key={column.key} {...props} className={className}>{value}</Td>; } else { return <Td column={column} key={column.key} className={className}/>; } }.bind(this))); } // Manually transfer props var props = filterPropsFrom(this.props); return React.DOM.tr(props, children); } }); var Thead = exports.Thead = React.createClass({ getColumns: function() { return React.Children.map(this.props.children, function(th) { if (typeof th.props.children === 'string') { return th.props.children; } else { throw new TypeError('<th> must have a string child'); } }); }, handleClickTh: function (column) { this.props.onSort(column.key); }, render: function() { // Declare the list of Ths var Ths = []; for (var index = 0; index < this.props.columns.length; index++) { var column = this.props.columns[index]; var sortClass = ''; if (this.props.sortableColumns[column.key]) { sortClass += 'reactable-header-sortable '; } if (this.props.sort.column === column.key) { sortClass += 'reactable-header-sort'; if (this.props.sort.direction === 1) { sortClass += '-asc'; } else { sortClass += '-desc'; } } if (this.props.border) { sortClass += " dig-has-border"; }; if (this.props.headerBg) { sortClass += ' dig-has-header-background'; } var style = {}; if (this.props.columnWidth.length > index) { style = {width: this.props.columnWidth[index]}; }; Ths.push( <Th className={sortClass} key={index} onClick={this.handleClickTh.bind(this, column)} style={style}> {column.label} </Th> ); } // Manually transfer props var props = filterPropsFrom(this.props); return ( <thead {...props}> {this.props.filtering === true ? <Filterer colSpan={this.props.columns.length} onFilter={this.props.onFilter} placeholder={this.props.filterPlaceholder} value={this.props.currentFilter} /> : ''} <tr className="reactable-column-header">{Ths}</tr> </thead> ); } }); var Th = exports.Th = React.createClass({ render: function() { var childProps if (this.props.children instanceof Unsafe) { return <th {...filterPropsFrom(this.props)} dangerouslySetInnerHTML={{__html: this.props.children.toString()}}/> } else { return <th {...filterPropsFrom(this.props)}> {this.props.children} </th>; } } }); var FiltererInput = React.createClass({ onChange: function() { this.props.onFilter(this.getDOMNode().value); }, render: function() { return ( <input type="text" className="reactable-filter-input" placeholder={this.props.placeholder} value={this.props.value} onKeyUp={this.onChange} onChange={this.onChange} /> ); } }); var Filterer = React.createClass({ render: function() { if (typeof this.props.colSpan === 'undefined') { throw new TypeError('Must pass a colSpan argument to Filterer'); } return ( <tr className="reactable-filterer"> <td colSpan={this.props.colSpan}> <FiltererInput onFilter={this.props.onFilter} value={this.props.value} placeholder={this.props.placeholder}/> </td> </tr> ); } }); var Paginator = React.createClass({ render: function() { if (typeof this.props.colSpan === 'undefined') { throw new TypeError('Must pass a colSpan argument to Paginator'); } if (typeof this.props.numPages === 'undefined') { throw new TypeError('Must pass a non-zero numPages argument to Paginator'); } if (typeof this.props.currentPage === 'undefined') { throw new TypeError('Must pass a currentPage argument to Paginator'); } var pageButtons = []; for (var i = 0; i < this.props.numPages; i++) { var pageNum = i; var className = "reactable-page-button"; if (this.props.currentPage === i) { className += " reactable-current-page"; } pageButtons.push( <a className={className} key={i} // create function to get around for-loop closure issue onClick={(function(pageNum) { return function() { this.props.onPageChange(pageNum); }.bind(this); }.bind(this))(i)}>{i + 1}</a> ); } return ( <tbody className="reactable-pagination"> <tr> <td colSpan={this.props.colSpan}> {pageButtons} </td> </tr> </tbody> ); } }); var Table = exports.Table = React.createClass({ // Translate a user defined column array to hold column objects if strings are specified // (e.g. ['column1'] => [{key: 'column1', label: 'column1'}]) translateColumnsArray: function(columns) { return columns.map(function(column, i) { if (typeof(column) === 'string') { return { key: column, label: column }; } else { if (typeof(column.sortable) !== 'undefined') { var sortFunction = column.sortable === true ? 'default' : column.sortable; this._sortable[column.key] = sortFunction; } return column; } }.bind(this)); }, parseChildData: function(props) { var data = []; // Transform any children back to a data array if (typeof(props.children) !== 'undefined') { React.Children.forEach(props.children, function(child) { // TODO: figure out a new way to determine the type of a component /* if (child.type.ConvenienceConstructor !== Tr) { return; // (continue) } */ if (typeof(child.props) !== 'object') { return console.warn('Child passed to <Reactable.Table> was not an object: ' + child.toString()); } var childData = child.props.data || {}; React.Children.forEach(child.props.children, function(descendant) { // TODO /* if (descendant.type.ConvenienceConstructor === Td) { */ if (true) { if (typeof(descendant.props.column) !== 'undefined') { var value; if (typeof(descendant.props.data) !== 'undefined') { value = descendant.props.data; } else if (typeof(descendant.props.children) !== 'undefined') { value = descendant.props.children; } else { console.warn('exports.Td specified without ' + 'a `data` property or children, ' + 'ignoring'); return; } childData[descendant.props.column] = { value: value, props: filterPropsFrom(descendant.props), __reactableMeta: true }; } else { console.warn('exports.Td specified without a ' + '`column` property, ignoring'); } } }); data.push({ data: childData, props: filterPropsFrom(child.props), __reactableMeta: true }); }.bind(this)); } return data; }, initialize: function(props) { this.data = props.data || []; this.data = this.data.concat(this.parseChildData(props)); this.initializeSorts(props); }, initializeSorts: function() { this._sortable = {}; // Transform sortable properties into a more friendly list for (var i in this.props.sortable) { var column = this.props.sortable[i]; var columnName, sortFunction; if (column instanceof Object) { if (typeof(column.column) !== 'undefined') { columnName = column.column; } else { console.warn('Sortable column specified without column name'); return; } if (typeof(column.sortFunction) === 'function') { sortFunction = column.sortFunction; } else { sortFunction = 'default'; } } else { columnName = column; sortFunction = 'default'; } this._sortable[columnName] = sortFunction; } }, getDefaultProps: function() { var defaultProps = { sortBy: false, defaultSort: false, itemsPerPage: 0, headerBg: true, border: true }; return defaultProps; }, getInitialState: function() { var initialState = { currentPage: 0, currentSort: { column: null, direction: 1 }, filter: '' }; // Set the state of the current sort to the default sort if (this.props.sortBy !== false || this.props.defaultSort !== false) { var sortingColumn = this.props.sortBy || this.props.defaultSort; initialState.currentSort = this.getCurrentSort(sortingColumn); } return initialState; }, getCurrentSort: function(column) { var columnName, sortDirection; if (column instanceof Object) { if (typeof(column.column) !== 'undefined') { columnName = column.column; } else { console.warn('Default column specified without column name'); return; } if (typeof(column.direction) !== 'undefined') { if (column.direction === 1 || column.direction === 'asc') { sortDirection = 1; } else if (column.direction === -1 || column.direction === 'desc') { sortDirection = -1; } else { console.warn('Invalid default sort specified. Defaulting to ascending'); sortDirection = 1; } } else { sortDirection = 1; } } else { columnName = column; sortDirection = 1; } return { column: columnName, direction: sortDirection }; }, updateCurrentSort: function(sortBy) { if (sortBy !== false && sortBy.column !== this.state.currentSort.column && sortBy.direction !== this.state.currentSort.direction) { this.setState({ currentSort: this.getCurrentSort(sortBy) }); } }, componentWillMount: function() { this.initialize(this.props); this.sortByCurrentSort(); }, componentWillReceiveProps: function(nextProps) { this.initialize(nextProps); this.updateCurrentSort(nextProps.sortBy); this.sortByCurrentSort(); }, onPageChange: function(page) { this.setState({ currentPage: page }); }, filterBy: function(filter) { this.setState({ filter: filter }); }, applyFilter: function(filter, children) { // Helper function to apply filter text to a list of table rows filter = filter.toLowerCase(); var matchedChildren = []; for (var i = 0; i < children.length; i++) { var data = children[i].props.data; for (var j = 0; j < this.props.filterable.length; j++) { var filterColumn = this.props.filterable[j]; if ( typeof(data[filterColumn]) !== 'undefined' && extractDataFrom(data, filterColumn).toString().toLowerCase().indexOf(filter) > -1 ) { matchedChildren.push(children[i]); break; } } } return matchedChildren; }, sortByCurrentSort: function(){ // Apply a sort function according to the current sort in the state. // This allows us to perform a default sort even on a non sortable column. var currentSort = this.state.currentSort; if (currentSort.column === null) { return; } this.data.sort(function(a, b){ var keyA = extractDataFrom(a, currentSort.column); keyA = (keyA instanceof Unsafe) ? keyA.toString() : keyA || ''; var keyB = extractDataFrom(b, currentSort.column); keyB = (keyB instanceof Unsafe) ? keyB.toString() : keyB || ''; // Default sort if ( typeof(this._sortable[currentSort.column]) === 'undefined' || this._sortable[currentSort.column] === 'default' ) { // Reverse direction if we're doing a reverse sort if (keyA < keyB) { return -1 * currentSort.direction; } if (keyA > keyB) { return 1 * currentSort.direction; } return 0; } else { // Reverse columns if we're doing a reverse sort if (currentSort.direction === 1) { return this._sortable[currentSort.column](keyA, keyB); } else { return this._sortable[currentSort.column](keyB, keyA); } } }.bind(this)); }, onSort: function(column) { // Don't perform sort on unsortable columns if (typeof(this._sortable[column]) === 'undefined') { return; } var currentSort = this.state.currentSort; if (currentSort.column === column) { currentSort.direction *= -1; } else { currentSort.column = column; currentSort.direction = 1; } // Set the current sort and pass it to the sort function this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); }, render: function() { var children = []; var columns; var userColumnsSpecified = false; if ( this.props.children && this.props.children.length > 0 && this.props.children[0].type.ConvenienceConstructor === Thead ) { columns = this.props.children[0].getColumns(); } else { columns = this.props.columns || []; } if (columns.length > 0) { userColumnsSpecified = true; columns = this.translateColumnsArray(columns); } // Build up table rows if (this.data && typeof this.data.map === 'function') { // Build up the columns array children = children.concat(this.data.map(function(rawData, i) { var data = rawData; var props = {}; if (rawData.__reactableMeta === true) { data = rawData.data; props = rawData.props; } // Loop through the keys in each data row and build a td for it for (var k in data) { if (data.hasOwnProperty(k)) { // Update the columns array with the data's keys if columns were not // already specified if (userColumnsSpecified === false) { var column = { key: k, label: k }; // Only add a new column if it doesn't already exist in the columns array if ( columns.find(function(element) { return element.key === column.key; }) === undefined ) { columns.push(column); } } } } return ( <Tr columns={columns} key={i} data={data} {...props} border={this.props.border}/> ); }.bind(this))); } if (this.props.sortable === true) { for (var i = 0; i < columns.length; i++) { this._sortable[columns[i].key] = 'default'; } } // Determine if we render the filter box var filtering = false; if ( this.props.filterable && Array.isArray(this.props.filterable) && this.props.filterable.length > 0 ) { filtering = true; } // Apply filters var filteredChildren = children; if (this.state.filter !== '') { filteredChildren = this.applyFilter(this.state.filter, filteredChildren); } // Determine pagination properties and which columns to display var itemsPerPage = 0; var pagination = false; var numPages; var currentPage = this.state.currentPage; var currentChildren = filteredChildren; if (this.props.itemsPerPage > 0) { itemsPerPage = this.props.itemsPerPage; numPages = Math.ceil(filteredChildren.length / itemsPerPage); if (currentPage > numPages - 1) { currentPage = numPages - 1; } pagination = true; currentChildren = filteredChildren.slice( currentPage * itemsPerPage, (currentPage + 1) * itemsPerPage ); } var columnWidth = this.props.columnWidth || []; var className = this.props.border ? 'dig-has-border' : ''; // Manually transfer props var props = filterPropsFrom(this.props); if (props.className) { className = className + ' ' + props.className; }else{ className += ' dig-table'; } props.className = className; return <table {...props} key="table">{[ (columns && columns.length > 0 ? <Thead columns={columns} filtering={filtering} onFilter={this.filterBy} filterPlaceholder={this.props.filterPlaceholder} currentFilter={this.state.filter} sort={this.state.currentSort} sortableColumns={this._sortable} onSort={this.onSort} columnWidth={columnWidth} border={this.props.border} headerBg={this.props.headerBg} key="thead"/> : null ), <tbody className="reactable-data" key="tbody"> {currentChildren} </tbody>, (pagination === true ? <Paginator colSpan={columns.length} numPages={numPages} currentPage={currentPage} onPageChange={this.onPageChange} key="paginator"/> : null ) ]}</table>; } }); function toArray(obj) { var ret = []; for (var attr in obj) { ret[attr] = obj; } return ret; } function filterPropsFrom(baseProps) { baseProps = baseProps || {}; var props = {}; for (var key in baseProps) { if (!(key in internalProps)) { props[key] = baseProps[key]; } } return props; } function extractDataFrom(key, column) { var value; if ( typeof(key) !== 'undefined' && key !== null && key.__reactableMeta === true ) { value = key.data[column]; } else { value = key[column]; } if ( typeof(value) !== 'undefined' && value !== null && value.__reactableMeta === true ) { value = (typeof(value.props.value) !== 'undefined' && value.props.value !== null) ? value.props.value : value.value; } return (stringable(value) ? value : ''); } var internalProps = { columns: true, sortable: true, filterable: true, sortBy: true, defaultSort: true, itemsPerPage: true, childNode: true, data: true, children: true }; return exports; }));