├── .babelrc ├── .codecov.yml ├── .github ├── FUNDING.yml └── config.yml ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __mocks__ ├── browserMock.js ├── fileMock.js ├── request.js └── styleMock.js ├── __tests__ └── components │ ├── Exceptions.test.js │ ├── Helpers.test.js │ ├── form │ ├── Button.test.js │ ├── CSVLink.test.js │ ├── Editor.test.js │ ├── __snapshots__ │ │ ├── Button.test.js.snap │ │ ├── CSVLink.test.js.snap │ │ └── Editor.test.js.snap │ └── fields │ │ ├── CheckRadio.test.js │ │ ├── Select.test.js │ │ ├── TextArea.test.js │ │ ├── TextEditor.test.js │ │ └── __snapshots__ │ │ ├── CheckRadio.test.js.snap │ │ ├── Select.test.js.snap │ │ ├── TextArea.test.js.snap │ │ └── TextEditor.test.js.snap │ ├── plugins │ ├── PieChart.test.js │ ├── ProgressBar.test.js │ ├── Trendy.test.js │ └── __snapshots__ │ │ ├── PieChart.test.js.snap │ │ ├── ProgressBar.test.js.snap │ │ └── Trendy.test.js.snap │ ├── table │ ├── Column.test.js │ ├── Header.test.js │ ├── Row.test.js │ └── __snapshots__ │ │ ├── Column.test.js.snap │ │ ├── Header.test.js.snap │ │ └── Row.test.js.snap │ └── tools │ ├── PagesSelector.test.js │ ├── Pagination.test.js │ ├── Search.test.js │ ├── Tools.test.js │ └── __snapshots__ │ ├── PagesSelector.test.js.snap │ ├── Pagination.test.js.snap │ ├── Search.test.js.snap │ └── Tools.test.js.snap ├── _config.yml ├── bower.json ├── build └── index.js ├── coverage ├── clover.xml ├── coverage-final.json └── lcov.info ├── helpers ├── .DS_Store ├── loaders │ ├── babel.js │ ├── css.js │ ├── fonts.js │ ├── images.js │ ├── json.js │ └── scss.js ├── plugins │ ├── css.js │ ├── dedupe.js │ ├── hot.js │ ├── html.js │ ├── injectVars.js │ ├── moment.js │ ├── noErrors.js │ ├── uglify.js │ └── vars.js ├── postcss │ └── index.js └── template.ejs ├── index.html ├── local_data.js ├── main.css ├── main.js ├── main.prod.js ├── package-lock.json ├── package.json ├── screens ├── GT.gif ├── GT_pie_plugin.png ├── GT_plugins_for_table.png ├── GT_progress_bar_plugin.png ├── GT_react_select_plugin.png ├── GT_rte_plugin.png ├── GT_slider_range_plugins.png ├── GT_trend_plugin.png ├── GigaTables_basic_view.png ├── GigaTables_cell_edit.png ├── GigaTables_create_item.png ├── GigaTables_delete_items.png ├── GigaTables_edit_item.png ├── GigaTables_form_styling.png ├── GigaTables_pagination_with_sort.png └── paypal.png ├── src ├── .DS_Store ├── Reactables.jsx ├── components │ ├── CommonConstants.js │ ├── EditorConstants.js │ ├── Exceptions.js │ ├── Helpers.js │ ├── Lang.js │ ├── Main.js │ ├── form │ │ ├── Button.js │ │ ├── CSVLink.js │ │ ├── Editor.js │ │ ├── EditorButton.js │ │ ├── FormField.js │ │ ├── HOCButton.js │ │ ├── HOCEditorButton.js │ │ └── fields │ │ │ ├── CheckRadio.js │ │ │ ├── File.js │ │ │ ├── HOCCheckRadio.js │ │ │ ├── HOCInput.js │ │ │ ├── HOCSelect.js │ │ │ ├── HOCTextArea.js │ │ │ ├── HTML5Input.js │ │ │ ├── Input.js │ │ │ ├── ReactSelect.js │ │ │ ├── Select.js │ │ │ ├── TextArea.js │ │ │ └── TextEditor.js │ ├── plugins │ │ ├── Pie.js │ │ ├── ProgressBar.js │ │ └── Trendy.js │ ├── table │ │ ├── Column.js │ │ ├── Footer.js │ │ ├── Header.js │ │ ├── Row.js │ │ ├── TBody.js │ │ ├── TFoot.js │ │ └── THead.js │ ├── theme │ │ └── material-ui │ │ │ ├── MButton.js │ │ │ ├── MCheckBox.js │ │ │ ├── MEditorButton.js │ │ │ ├── MInput.js │ │ │ ├── MPagesSelector.js │ │ │ ├── MRadio.js │ │ │ ├── MSearch.js │ │ │ ├── MSelect.js │ │ │ └── MTextArea.js │ └── tools │ │ ├── HOCPagesSelector.js │ │ ├── HOCSearch.js │ │ ├── PagesSelector.js │ │ ├── Pagination.js │ │ ├── Search.js │ │ └── Tools.js ├── css │ ├── editor.css │ ├── editor.js │ ├── styles.css │ └── styles.js └── images │ ├── sort_asc.png │ ├── sort_asc_disabled.png │ ├── sort_both.png │ ├── sort_desc.png │ └── sort_desc_disabled.png ├── webpack.config.js └── webpack.config.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react", 5 | "stage-0", 6 | "babel-preset-react" 7 | ], 8 | "plugins": [ 9 | [ 10 | "transform-class-properties", 11 | { 12 | "spec": true 13 | } 14 | ] 15 | ], 16 | "env": { 17 | "test": { 18 | "plugins": [ 19 | "transform-es2015-modules-commonjs" 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "70...100" 8 | 9 | status: 10 | project: yes 11 | patch: yes 12 | changes: no 13 | 14 | parsers: 15 | gcov: 16 | branch_detection: 17 | conditional: yes 18 | loop: yes 19 | method: no 20 | macro: no 21 | 22 | comment: 23 | layout: "header, diff" 24 | behavior: default 25 | require_changes: no -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | # github: [arthurkushman] 4 | liberapay: arthurkushman 5 | issuehunt: arthurkushman 6 | patreon: user?u=26175584 7 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | 2 | # Comment to be posted on first time issues. 3 | newIssueWelcomeComment: | 4 | 👋 Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include particular steps to reproduce it. 5 | 6 | # Comment to be posted on PRs from first time contributors. 7 | newPRWelcomeComment: | 8 | 💖 Thanks for opening this pull request! 💖 9 | 10 | # Comment to be posted on pull requests merged by a first time user. 11 | firstPRMergeComment: > 12 | Congrats on merging your first pull request and thank you for your contribution! 🎉🎉🎉 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log.* 4 | jest/* 5 | *.log* 6 | .idea/* 7 | __tests__/components/__snapshots__/* 8 | coverage/lcov-report/* 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at arthurkushman@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to gigatables-react 2 | ======================== 3 | 4 | :wave: We are glad that you want to support our project. 5 | 6 | Please, follow next rules to add new pull request: 7 | 8 | * Your code MUST follow PSR recommendations 9 | * All your code MUST be covered by unit tests 10 | * All methods MUST be documented by PHPDocs 11 | * Adding documentation of new functionality to [README.md](https://github.com/GigaTables/reactables/blob/master/README.md) greets 12 | 13 | You may always add your improvement ideas or bug reports to [Issues](https://github.com/GigaTables/reactables/issues) 14 | 15 | Thank you for contribution 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 GigaTables 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__mocks__/browserMock.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(document, 'CSVLink', { 2 | // value: document.createElement('a'), 3 | createElement: (val) => { 4 | return document.createElement(val) 5 | } 6 | }); 7 | 8 | const constantDate = new Date('2018-03-23T04:41:20') 9 | 10 | Date = class extends Date { 11 | constructor() { 12 | super(); 13 | return constantDate 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /__mocks__/request.js: -------------------------------------------------------------------------------- 1 | const rows = {"rows":[ 2 | {"GT_RowId":123,"title":"Test Bar 123st row","id":123,"desc":"Lorem Ipsum is simply dummy Bar 7196 text of the printing and typesetting","info":"some info some info some info some info","date":"13:23:43 02:04:2017","field1":123,"field2":1357,"field3":12468}, 3 | {"GT_RowId":66,"title":"Test Foo 66st row","id":66,"desc":"Lorem Ipsum is simply dummy Foo 9608 text of the printing and typesetting","info":"some info some info some info some info","date":"13:24:40 02:04:2017","field1":0,"field2":1300,"field3":12411}, 4 | {"GT_RowId":105,"title":"Test Bar 105st row","id":105,"desc":"Lorem Ipsum is simply dummy Bar 7258 text of the printing and typesetting","info":"some info some info some info some info","date":"13:24:01 02:04:2017","field1":null,"field2":1339,"field3":12450} 5 | ]}; 6 | 7 | export default function fetch(url) { 8 | return new Promise((resolve, reject) => { 9 | process.nextTick( 10 | () => resolve(rows) 11 | ); 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /__tests__/components/Exceptions.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { DataException, EditorException } from '../../src/components/Exceptions.js' 3 | 4 | it('run exceptions correctly', () => { 5 | expect(() => { 6 | throw new DataException() 7 | }).toThrow() 8 | expect(() => { 9 | throw new EditorException() 10 | }).toThrow() 11 | }) -------------------------------------------------------------------------------- /__tests__/components/Helpers.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { t } from '../../src/components/Helpers' 3 | 4 | describe('to translate string correctly', () => { 5 | test('has changed to Foo baz baz', () => { 6 | expect(t('Foo {{bar}} baz', { 'bar': 'baz' })).toBe('Foo baz baz') 7 | let innerObj = Object.create({name: 'inherited'}); 8 | t('Foo {{bar}} baz', innerObj) 9 | }) 10 | }) -------------------------------------------------------------------------------- /__tests__/components/form/Button.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | import Button from '../../../src/components/form/Button.js' 4 | import {shallow, configure} from 'enzyme'; 5 | import Adapter from 'enzyme-adapter-react-16'; 6 | configure({adapter: new Adapter()}); 7 | 8 | it('renders Button correctly', () => { 9 | const tree = renderer.create( 10 | 17 | ).toJSON(); 18 | expect(tree).toMatchSnapshot(); 19 | 20 | const obj = shallow( 21 | 28 | ); 29 | 30 | obj.instance().shouldComponentUpdate({ 31 | active: true, 32 | action: "edit" 33 | }); 34 | 35 | obj.instance().onClick({ 36 | target: { 37 | dataset: 'title' 38 | } 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /__tests__/components/form/CSVLink.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import renderer from 'react-test-renderer' 3 | import CSVLink from '../../../src/components/form/CSVLink.js' 4 | import { shallow, configure } from 'enzyme' 5 | import Adapter from 'enzyme-adapter-react-16' 6 | 7 | configure({ adapter: new Adapter() }) 8 | 9 | it('renders CSVLink correctly', () => { 10 | const tree = renderer.create( 11 | 31 | ).toJSON() 32 | expect(tree).toMatchSnapshot() 33 | }) 34 | 35 | const obj = shallow( 36 | 56 | ) 57 | 58 | global.URL = { 59 | createObjectURL: (url) => { 60 | } 61 | } 62 | // todo: this mock is not working 63 | // global.navigator = { 64 | // msSaveBlob: true 65 | // } 66 | // Object.defineProperty(window.navigator, 'msSaveBlob', {value: (k, v) => {}}); 67 | // Object.defineProperty(window.navigator, "msSaveBlob", (function(_value){ 68 | // return { 69 | // get: function _get() { 70 | // return _value; 71 | // }, 72 | // set: function _set(v) { 73 | // _value = v; 74 | // } 75 | // }; 76 | // })(window.navigator.msSaveBlob)); 77 | 78 | obj.instance().onClick([ 79 | { 80 | 'GT_RowId': 1059, 81 | 'title': 'Test 1059st row', 82 | 'id': 1059, 83 | 'desc': 'Lorem Ipsum is simply dummy 1447 text of the printing and typesetting', 84 | 'info': 'some info some info some info some info', 85 | 'date': '15:28:06 01:05:2018', 86 | 'field1': 29, 87 | 'field2': 2293, 88 | 'field3': 13404 89 | } 90 | ]) 91 | obj.instance().onClick({}) 92 | obj.instance().objectToCSVRow( 93 | { 94 | 'GT_RowId': 1059, 95 | 'title': 'Test 1059st row', 96 | 'id': 1059, 97 | 'desc': 'Lorem Ipsum is simply dummy 1447 text of the printing and typesetting', 98 | 'info': 'some info some info some info some info', 99 | 'date': '15:28:06 01:05:2018', 100 | 'field1': 29, 101 | 'field2': 2293, 102 | 'field3': 13404 103 | } 104 | ) -------------------------------------------------------------------------------- /__tests__/components/form/__snapshots__/Button.test.js.snap: -------------------------------------------------------------------------------- 1 | exports[`test renders Button correctly 1`] = ` 2 |
6 |
9 | 11 | New 12 | 13 |
14 |
16 |
17 | `; 18 | -------------------------------------------------------------------------------- /__tests__/components/form/__snapshots__/CSVLink.test.js.snap: -------------------------------------------------------------------------------- 1 | exports[`test renders CSVLink correctly 1`] = ` 2 |
5 |
7 | 8 |
9 |
11 |
12 | `; 13 | -------------------------------------------------------------------------------- /__tests__/components/form/fields/CheckRadio.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import renderer from 'react-test-renderer' 3 | import CheckRadio from '../../../../src/components/form/fields/CheckRadio.js' 4 | 5 | it('renders Select correctly', () => { 6 | const tree = renderer.create( 7 | {}} 10 | id={'title'} 11 | type={'select'} 12 | name={'title'} 13 | label={'Label'} 14 | value={"1"} 15 | objectValues={[ { 'key1': "1" }, { 'key2': 'val2' } ]} 16 | /> 17 | ).toJSON() 18 | expect(tree).toMatchSnapshot() 19 | let innerObj = Object.create({name: 'inherited'}); 20 | const tree2 = renderer.create( 21 | {}} 24 | id={'title'} 25 | type={'select'} 26 | name={'title'} 27 | label={'Label'} 28 | value={1} 29 | objectValues={[innerObj]} 30 | /> 31 | ).toJSON() 32 | expect(tree2).toMatchSnapshot() 33 | const tree3 = renderer.create( 34 | {}} 37 | id={'title'} 38 | type={'select'} 39 | name={'title'} 40 | label={'Label'} 41 | value={1} 42 | objectValues={innerObj} 43 | /> 44 | ).toJSON() 45 | expect(tree3).toMatchSnapshot() 46 | }); -------------------------------------------------------------------------------- /__tests__/components/form/fields/Select.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import renderer from 'react-test-renderer' 3 | import Select from '../../../../src/components/form/fields/Select.js' 4 | 5 | it('renders Select correctly', () => { 6 | const tree = renderer.create( 7 | {}} 24 | id={'title'} 25 | type={'select'} 26 | name={'title'} 27 | label={'Label'} 28 | value={1} 29 | objectValues={[innerObj]} 30 | /> 31 | ).toJSON() 32 | expect(tree2).toMatchSnapshot() 33 | const tree3 = renderer.create( 34 | 26 | ) 27 | } 28 | } 29 | 30 | TextArea.propTypes = { 31 | onChange: PropTypes.func.isRequired, 32 | id: PropTypes.string.isRequired, 33 | name: PropTypes.string.isRequired, 34 | isMultiple: PropTypes.bool, 35 | onFocus: PropTypes.func, 36 | attributes: PropTypes.object 37 | } 38 | 39 | export default TextArea -------------------------------------------------------------------------------- /src/components/form/fields/TextEditor.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import RichTextEditor from 'react-rte' 3 | 4 | class TextEditor extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { 8 | value: RichTextEditor.createEmptyValue() 9 | } 10 | } 11 | 12 | onChange = (value) => { 13 | this.setState({value}) 14 | if (this.props.onChangeHtml) { 15 | // Send the changes up to the parent component as an HTML string. 16 | // This is here to demonstrate using `.toString()` but in a real app it 17 | // would be better to avoid generating a string on each change. 18 | this.props.onChangeHtml(this, 19 | value.toString('html') 20 | ) 21 | } 22 | } 23 | 24 | render() { 25 | const { 26 | name, 27 | onFocus, 28 | multiple 29 | } = this.props 30 | return ( 31 | 39 | ) 40 | } 41 | } 42 | 43 | export default TextEditor -------------------------------------------------------------------------------- /src/components/plugins/Pie.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import PieChart from 'react-minimal-pie-chart' 4 | 5 | class Pie extends Component { 6 | render () { 7 | const { 8 | data, 9 | pluginProps 10 | } = this.props; 11 | 12 | return ( 13 | 14 | ) 15 | } 16 | } 17 | 18 | Pie.propTypes = { 19 | data: PropTypes.array.isRequired 20 | }; 21 | 22 | export default Pie -------------------------------------------------------------------------------- /src/components/plugins/ProgressBar.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames/bind'; 4 | import '../../css/styles.css'; 5 | 6 | const CommonConstants = require('../CommonConstants'); 7 | 8 | class ProgressBar extends Component { 9 | render() { 10 | const { 11 | percent, 12 | height, 13 | showPercent, 14 | } = this.props; 15 | 16 | let barClasses = classNames({ 17 | progress_bar: true, 18 | progress_bar_color_red: (percent > 0 && percent < 20), 19 | progress_bar_color_orange: (percent >= 20 && percent < 40), 20 | progress_bar_color_yellow: (percent >= 40 && percent < 60), 21 | progress_bar_color_light_green: (percent >= 60 && percent < 80), 22 | progress_bar_color_green: (percent >= 80), 23 | progress_bar_none: (percent === null || percent === CommonConstants.UNDEFINED), 24 | }); 25 | let st = { 26 | height: (typeof height === CommonConstants.UNDEFINED) ? 10 : height, 27 | width: ((percent > 100) ? 100 : percent) + '%', 28 | }; 29 | let percentStr = (((percent === null) ? 0 : ((percent > 100) ? 100 : percent)) + '%'); 30 | return ( 31 |
32 |
33 |
34 |
35 | {(showPercent === true) ? percentStr : ' '} 36 |
37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | 44 | ProgressBar.defaultProps = { 45 | height: 10, 46 | percent: 0, 47 | showPercent: true, 48 | }; 49 | 50 | ProgressBar.propTypes = { 51 | percent: PropTypes.any, 52 | height: PropTypes.number, 53 | }; 54 | 55 | export default ProgressBar -------------------------------------------------------------------------------- /src/components/plugins/Trendy.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import Trend from 'react-trend' 4 | 5 | class Trendy extends Component { 6 | render () { 7 | const { 8 | data, 9 | pluginProps 10 | } = this.props 11 | return ( 12 | 13 | ) 14 | } 15 | } 16 | 17 | Trendy.propTypes = { 18 | data: PropTypes.array.isRequired 19 | } 20 | 21 | export default Trendy -------------------------------------------------------------------------------- /src/components/table/Column.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import classNames from 'classnames/bind' 4 | import ProgressBar from '../plugins/ProgressBar' 5 | import Pie from '../plugins/Pie' 6 | import Trendy from '../plugins/Trendy' 7 | 8 | const CommonConstants = require('../CommonConstants') 9 | const EditorConstants = require('../EditorConstants') 10 | 11 | class Column extends Component { 12 | shouldComponentUpdate(nextProps) { 13 | const { 14 | gteRowId, 15 | count, 16 | selectedRows, 17 | dataIndex, 18 | editableCells, 19 | children 20 | } = this.props 21 | return editableCells === true 22 | || gteRowId !== nextProps.gteRowId 23 | || count !== nextProps.count 24 | || selectedRows.length !== nextProps.selectedRows.length 25 | || dataIndex !== nextProps.dataIndex 26 | || children !== nextProps.children // iff content of a column has been changed - re-render 27 | } 28 | 29 | constructor(props) { 30 | super(props) 31 | this.state = { 32 | dataIndices: {}, 33 | cellValue: props.children 34 | } 35 | this.cell = props.cell 36 | } 37 | 38 | componentDidUpdate() { 39 | if (typeof this.dataIn !== CommonConstants.UNDEFINED && this.dataIn !== null) { 40 | this.dataIn.focus() 41 | } 42 | } 43 | 44 | changeCell(e) { 45 | this.setState({ 46 | dataIndices: Object.assign({}, this.state.dataIndices, { 47 | [e.target.dataset.index]: e.target.value 48 | }), 49 | cellValue: e.target.value 50 | }) 51 | } 52 | 53 | btnClickedEnter(e) { 54 | e.persist() // this is to avoid null values in this.props.editorUpdate(e, dataResp) call 55 | const {editorUpdate, editor} = this.props 56 | const {dataIndices} = this.state 57 | let ajaxUrl = editor.ajax 58 | let dataResp = dataIndices 59 | if (e.keyCode === CommonConstants.ENTER_KEY) { 60 | // fill-in id 61 | let payload = Object.assign({}, dataIndices, { 62 | ['id']: parseInt(e.target.dataset.realid) 63 | }) 64 | fetch(ajaxUrl, { 65 | method: EditorConstants.HTTP_METHOD_PUT, 66 | body: JSON.stringify(payload) 67 | }).then(response => response.json()).then((data) => { 68 | editorUpdate(e, dataResp) 69 | }) 70 | // close cell 71 | this.cell = 0 72 | } 73 | } 74 | 75 | getColumn() { 76 | const { 77 | gteRowId, 78 | count, 79 | selectedRows, 80 | dataIndex, 81 | cell, // string uid of this cell - 00, 01, 11, 12 82 | editableCells, // bool if cells are editable 83 | editedCell, // uid of edited 84 | editCell, // function 85 | editRow, // function 86 | minRow, 87 | maxRow, 88 | footer 89 | } = this.props 90 | const { 91 | cellValue 92 | } = this.state 93 | 94 | if (editableCells === true && dataIndex === EditorConstants.EDITABLE_CELLS_INDEX) { 95 | let cellClasses = classNames({ 96 | normal_checkbox: true, 97 | select_checkbox: (selectedRows.indexOf(count) !== -1) 98 | }) 99 | return ( 100 | 109 |
119 | 120 | ) 121 | } 122 | let cellContent = this.getCellContent() 123 | let tdClasses = classNames({ 124 | td_footer_cell: footer 125 | }) 126 | return ( 127 | 137 | {(editableCells === true && editedCell === this.cell) ? { 139 | this.dataIn = input 140 | }} 141 | id="edit_cell" 142 | type={EditorConstants.TYPE_TEXT} 143 | value={cellValue} 144 | data-realid={gteRowId} 145 | data-index={dataIndex} 146 | data-cell={cell} 147 | data-action="edit" 148 | data-rowid={count} 149 | onClick={editCell} 150 | onKeyUp={this.btnClickedEnter.bind(this)} 151 | onChange={(e) => { 152 | this.changeCell(e) 153 | }}/> : cellContent} 154 | 155 | ) 156 | } 157 | 158 | getCellContent() { 159 | const { 160 | children, 161 | plugins 162 | } = this.props 163 | if (typeof plugins !== CommonConstants.UNDEFINED && plugins !== false) { 164 | switch (plugins[CommonConstants.PLUGINS]) { 165 | case CommonConstants.PLUGINS_PROGRESS_BAR: 166 | return 171 | case CommonConstants.PLUGINS_PIE: 172 | return 173 | case CommonConstants.PLUGINS_TRENDY: 174 | return 175 | } 176 | } 177 | 178 | return children 179 | } 180 | 181 | render() { 182 | return this.getColumn() 183 | } 184 | } 185 | 186 | Column.propTypes = { 187 | editableCells: PropTypes.bool, 188 | gteRowId: PropTypes.number.isRequired, 189 | count: PropTypes.number.isRequired, 190 | selectedRows: PropTypes.array, 191 | dataIndex: PropTypes.string, 192 | editor: PropTypes.object, 193 | editorUpdate: PropTypes.func 194 | } 195 | 196 | Column.defaultProps = { 197 | footer: false 198 | } 199 | 200 | export default Column 201 | -------------------------------------------------------------------------------- /src/components/table/Footer.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames/bind'; 4 | 5 | class Footer extends Component { 6 | shouldComponentUpdate() { 7 | return false; 8 | } 9 | 10 | render() { 11 | const { 12 | children, 13 | gteRowId, 14 | } = this.props; 15 | 16 | let rowClasses = classNames({ 17 | footer: true, 18 | }); 19 | return ( 20 | {children} 24 | ) 25 | } 26 | } 27 | 28 | Footer.defaultProps = { 29 | gteRowId: -1, 30 | }; 31 | 32 | Footer.propTypes = { 33 | gteRowId: PropTypes.number.isRequired, 34 | }; 35 | 36 | export default Footer 37 | -------------------------------------------------------------------------------- /src/components/table/Header.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames/bind'; 4 | import styles from '../../css/styles.css'; 5 | 6 | const CommonConstants = require('../CommonConstants'); 7 | 8 | class Header extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.isDiscreteSearch = false; 12 | } 13 | 14 | shouldComponentUpdate(nextProps) { 15 | return this.props.sortId !== nextProps.sortId 16 | || this.props.columns[this.props.sortId] !== nextProps.columns[this.props.sortId] 17 | || this.props.sortDirection !== nextProps.sortDirection 18 | || this.props.columnsSearch !== nextProps.columnsSearch; 19 | } 20 | 21 | getHeaderContent() { 22 | const { 23 | children, 24 | columns, 25 | data, 26 | sortId, 27 | doDiscreteSearch, 28 | columnsSearch, 29 | discreteFocus, 30 | discreteBlur 31 | } = this.props; 32 | 33 | if (this.isDiscreteSearch === true) { 34 | let val = '', placeholder = data; 35 | if (typeof columnsSearch[data] !== CommonConstants.UNDEFINED) { 36 | val = columnsSearch[data]; 37 | } 38 | if (typeof columns[sortId][CommonConstants.DISCRETE_SEARCH_VALUE] !== CommonConstants.UNDEFINED 39 | && typeof columns[sortId][CommonConstants.DISCRETE_SEARCH_VALUE] === CommonConstants.FUNCTION) { // custom column 40 | placeholder = columns[sortId].discreteSearchValue(data); 41 | } 42 | return ( 43 |
44 | 53 |
54 | ); 55 | } 56 | return
{children}
; 57 | } 58 | 59 | getHeader() { 60 | const { 61 | sortDirection, 62 | gteSort, 63 | sortId, 64 | updateSort, 65 | columns, 66 | } = this.props; 67 | 68 | if (typeof columns[sortId].discreteSearch !== CommonConstants.UNDEFINED 69 | && columns[sortId].discreteSearch === true) { 70 | this.isDiscreteSearch = true; 71 | } 72 | 73 | let sorting = gteSort === CommonConstants.SORTABLE, 74 | // 0 - default data-direction, 1 - asc, -1 - desc 75 | desc = (sortDirection === -1) ? true : false, 76 | asc = (sortDirection === 1) ? true : false; 77 | let thClasses = classNames({ 78 | // gt_head_tr_th: true, 79 | sorting: sorting ? true : false, 80 | sorting_desc: desc, 81 | sorting_asc: asc 82 | }); 83 | return ( 84 | 87 | {this.getHeaderContent()} 88 | 89 | ) 90 | } 91 | 92 | render() { 93 | return this.getHeader(); 94 | } 95 | } 96 | 97 | Header.propTypes = { 98 | sortId: PropTypes.string, 99 | columns: PropTypes.array, 100 | sortDirection: PropTypes.number, 101 | columnsSearch: PropTypes.object, 102 | }; 103 | 104 | export default Header 105 | -------------------------------------------------------------------------------- /src/components/table/Row.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames/bind'; 4 | 5 | class Row extends Component { 6 | shouldComponentUpdate(nextProps) { 7 | const { 8 | gteRowId, 9 | count, 10 | selectedRows, 11 | minRow, 12 | maxRow, 13 | editableCells, 14 | } = this.props; 15 | 16 | return editableCells === true 17 | || gteRowId !== nextProps.gteRowId 18 | || selectedRows.length !== nextProps.selectedRows.length 19 | || selectedRows.indexOf(count) !== nextProps.selectedRows.indexOf(count) // on multiple merged rows selection Shift+Clk 20 | || selectedRows.indexOf(count) !== -1 // on multiple splitted rows selection Ctrl+Clk 21 | || minRow !== nextProps.minRow 22 | || maxRow !== nextProps.maxRow 23 | || selectedRows.length > 0; 24 | } 25 | 26 | render() { 27 | const { 28 | count, 29 | selectedRows, 30 | children, 31 | gteRowId, 32 | minRow, 33 | maxRow, 34 | clickedRow, 35 | editableCells, 36 | } = this.props; 37 | 38 | let rowClasses = classNames({ 39 | even: (count % 2 === 0), 40 | odd: (count % 2 !== 0), 41 | active: (selectedRows.indexOf(count) !== -1) 42 | }); 43 | 44 | return ( 45 | {children} 54 | ) 55 | } 56 | } 57 | 58 | Row.propTypes = { 59 | gteRowId: PropTypes.number.isRequired, 60 | count: PropTypes.number.isRequired, 61 | minRow: PropTypes.number, 62 | maxRow: PropTypes.number, 63 | selectedRows: PropTypes.array 64 | }; 65 | 66 | export default Row 67 | -------------------------------------------------------------------------------- /src/components/table/TBody.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import styles from '../../css/styles.css'; 3 | import onClickOutside from 'react-onclickoutside' 4 | 5 | class TBody extends Component { 6 | /** 7 | * @uses handleClickOutside 8 | */ 9 | handleClickOutside() { 10 | const { 11 | struct, 12 | rerenderTable, 13 | } = this.props; 14 | if (struct.editableCells === true) { 15 | rerenderTable(); 16 | } 17 | } 18 | 19 | render() { 20 | const {children} = this.props; 21 | return ( 22 | 23 | {children} 24 | 25 | ); 26 | } 27 | } 28 | 29 | export default onClickOutside(TBody) -------------------------------------------------------------------------------- /src/components/table/TFoot.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import styles from '../../css/styles.css'; 3 | 4 | class TFoot extends Component { 5 | render() { 6 | const {children} = this.props; 7 | return ( 8 | 9 | 10 | {children} 11 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | export default TFoot -------------------------------------------------------------------------------- /src/components/table/THead.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import styles from '../../css/styles.css'; 3 | 4 | class THead extends Component { 5 | render() { 6 | const {children} = this.props; 7 | return ( 8 | 9 | 10 | {children} 11 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | export default THead -------------------------------------------------------------------------------- /src/components/theme/material-ui/MButton.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import Button from '@material-ui/core/Button' 4 | 5 | class MButton extends Component { 6 | 7 | onClick(action, event) { 8 | const {showPopup} = this.props; 9 | return showPopup(event, action) 10 | } 11 | 12 | shouldComponentUpdate(nextProps) { 13 | const {active, action} = this.props; 14 | 15 | return active !== nextProps.active 16 | || action !== nextProps.action; 17 | } 18 | 19 | render() { 20 | const {action, children, active} = this.props; 21 | 22 | return ( 23 |
25 | 28 |
29 | ) 30 | } 31 | } 32 | 33 | MButton.propTypes = { 34 | action: PropTypes.string, 35 | active: PropTypes.bool, 36 | showPopup: PropTypes.func 37 | }; 38 | 39 | export default MButton 40 | -------------------------------------------------------------------------------- /src/components/theme/material-ui/MCheckBox.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import PropTypes from 'prop-types' 3 | import Checkbox from '@material-ui/core/Checkbox' 4 | import FormControlLabel from '@material-ui/core/FormControlLabel' 5 | 6 | class MCheckBox extends Component { 7 | 8 | constructor(props) { 9 | super(props); 10 | this.state = {}; 11 | 12 | this.presetDefaults(props); 13 | } 14 | 15 | presetDefaults(props) { 16 | const { 17 | value, 18 | objectValues, 19 | } = props; 20 | 21 | let values = objectValues; 22 | let options = [], val = '', 23 | // fixme: regexp to remove ex: [3] etc 24 | id = name.replace('[]', ''); 25 | 26 | for (let k in values) { 27 | if (values.hasOwnProperty(k)) { 28 | for (let key in values[k]) { 29 | if (values[k].hasOwnProperty(key)) { 30 | val = values[k][key].trim(); 31 | this.state[key] = val === value 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | handleChange(e) { 39 | this.setState({ 40 | [e.target.value]: e.target.checked 41 | }); 42 | 43 | this.props.onChange(e); 44 | } 45 | 46 | render() { 47 | const { 48 | attributes, 49 | name, 50 | // onChange, 51 | objectValues, 52 | type, 53 | } = this.props; 54 | 55 | let values = objectValues; 56 | let options = [], val = '', 57 | // fixme: regexp to remove ex: [3] etc 58 | id = name.replace('[]', ''); 59 | 60 | for (let k in values) { 61 | if (values.hasOwnProperty(k)) { 62 | for (let key in values[k]) { 63 | if (values[k].hasOwnProperty(key)) { 64 | val = values[k][key].trim(); 65 | options[k] = 66 | { 73 | this.handleChange(e) 74 | }} 75 | name={name} 76 | value={key} 77 | type={type} 78 | color="primary" 79 | /> 80 | } 81 | label={val} 82 | labelPlacement="end" 83 | />; 84 | } 85 | } 86 | } 87 | } 88 | 89 | return options 90 | } 91 | 92 | } 93 | 94 | MCheckBox.propTypes = { 95 | onChange: PropTypes.func.isRequired, 96 | id: PropTypes.string.isRequired, 97 | name: PropTypes.string.isRequired, 98 | type: PropTypes.string.isRequired, 99 | attributes: PropTypes.object, 100 | }; 101 | 102 | export default MCheckBox -------------------------------------------------------------------------------- /src/components/theme/material-ui/MEditorButton.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import Button from '@material-ui/core/Button' 3 | 4 | const styles = { 5 | container: { 6 | paddingRight: 15, 7 | paddingLeft: 15, 8 | marginBottom: -5, 9 | }, 10 | btn: { 11 | float: "right", 12 | padding: "5px 15px", 13 | } 14 | }; 15 | 16 | class MEditorButton extends Component { 17 | render() { 18 | const { 19 | action, 20 | btnClicked, 21 | children 22 | } = this.props; 23 | 24 | return ( 25 |
26 | 35 |
36 | ) 37 | } 38 | } 39 | 40 | export default MEditorButton -------------------------------------------------------------------------------- /src/components/theme/material-ui/MInput.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import classNames from 'classnames/bind' 4 | import TextField from '@material-ui/core/TextField' 5 | 6 | const classes = theme => ({ 7 | textField: { 8 | marginLeft: theme.spacing.unit, 9 | marginRight: theme.spacing.unit, 10 | width: 200, 11 | float: right, 12 | marginTop: -5, 13 | }, 14 | dense: { 15 | marginTop: -5, 16 | }, 17 | }); 18 | 19 | class MInput extends Component { 20 | render() { 21 | const { 22 | attributes, 23 | id, 24 | type, 25 | name, 26 | value, 27 | isMultiple, 28 | onFocus, 29 | onChange 30 | } = this.props; 31 | 32 | return ( 33 | 47 | ) 48 | } 49 | } 50 | 51 | MInput.propTypes = { 52 | onChange: PropTypes.func.isRequired, 53 | id: PropTypes.string.isRequired, 54 | name: PropTypes.string.isRequired, 55 | type: PropTypes.string.isRequired, 56 | isMultiple: PropTypes.bool, 57 | onFocus: PropTypes.func, 58 | attributes: PropTypes.object 59 | }; 60 | 61 | export default MInput -------------------------------------------------------------------------------- /src/components/theme/material-ui/MPagesSelector.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {withStyles} from '@material-ui/core/styles'; 4 | import Select from '@material-ui/core/Select'; 5 | import styles from '../../../css/styles.css'; 6 | 7 | const Lang = require('../../Lang'); 8 | 9 | class MPagesSelector extends Component { 10 | shouldComponentUpdate(nextProps) { 11 | return this.props.perPageRows !== nextProps.perPageRows 12 | || this.props.perPage !== nextProps.perPage; 13 | } 14 | 15 | render() { 16 | const { 17 | lang, 18 | perPageRows, 19 | perPage, 20 | updatePerPage 21 | } = this.props; 22 | 23 | let language = Lang[lang]; 24 | 25 | return ( 26 |
27 | {language.show}  28 | 29 | 43 | 44 |  {language.entries} 45 |
46 | ) 47 | } 48 | } 49 | 50 | MPagesSelector.propTypes = { 51 | lang: PropTypes.string, 52 | perPage: PropTypes.number, 53 | perPageRows: PropTypes.array, 54 | updatePerPage: PropTypes.func 55 | }; 56 | 57 | export default withStyles(styles)(MPagesSelector); 58 | -------------------------------------------------------------------------------- /src/components/theme/material-ui/MRadio.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import RadioGroup from '@material-ui/core/RadioGroup' 4 | import FormControlLabel from '@material-ui/core/FormControlLabel' 5 | import Radio from '@material-ui/core/Radio' 6 | 7 | const classes = theme => ({ 8 | root: { 9 | display: 'flex' 10 | }, 11 | formControl: { 12 | margin: theme.spacing.unit * 3 13 | }, 14 | group: { 15 | margin: `${theme.spacing.unit}px 0` 16 | } 17 | }) 18 | 19 | class MRadio extends Component { 20 | render() { 21 | const { 22 | attributes, 23 | type, 24 | name, 25 | onChange, 26 | value, // fieldValue 27 | objectValues, 28 | label 29 | } = this.props 30 | 31 | let values = objectValues 32 | let options = [], val = '', 33 | // fixme: regexp to remove ex: [3] etc 34 | id = name.replace('[]', '') 35 | 36 | for (let k in values) { 37 | if (values.hasOwnProperty(k)) { 38 | for (let key in values[k]) { 39 | if (values[k].hasOwnProperty(key)) { 40 | val = values[k][key].trim() 41 | options[k] = 42 | } 50 | labelPlacement="end" 51 | data-value={val.toLowerCase()} 52 | value={key} 53 | label={val} 54 | /> 55 | } 56 | } 57 | } 58 | } 59 | 60 | return 67 | {options} 68 | 69 | } 70 | 71 | } 72 | 73 | MRadio.propTypes = { 74 | onChange: PropTypes.func.isRequired, 75 | id: PropTypes.string.isRequired, 76 | name: PropTypes.string.isRequired, 77 | type: PropTypes.string.isRequired, 78 | attributes: PropTypes.object, 79 | value: PropTypes.string 80 | } 81 | 82 | export default MRadio -------------------------------------------------------------------------------- /src/components/theme/material-ui/MSearch.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styles from '../../../css/styles.css'; 4 | import TextField from '@material-ui/core/TextField'; 5 | import classNames from 'classnames/bind'; 6 | 7 | const Lang = require('../../Lang'); 8 | 9 | const classes = theme => ({ 10 | container: { 11 | display: 'flex', 12 | flexWrap: 'wrap', 13 | }, 14 | textField: { 15 | marginLeft: theme.spacing.unit, 16 | marginRight: theme.spacing.unit, 17 | width: 200, 18 | float: right, 19 | marginTop: -5, 20 | }, 21 | dense: { 22 | marginTop: -5, 23 | }, 24 | menu: { 25 | width: 200, 26 | }, 27 | }); 28 | 29 | class MSearch extends Component { 30 | constructor(props) 31 | { 32 | super(props); 33 | this.state = { 34 | search: '' 35 | } 36 | } 37 | 38 | shouldComponentUpdate(nextProps) { 39 | return this.props.search !== nextProps.search; 40 | } 41 | 42 | handleChange(name, event) { 43 | const {doSearch} = this.props; 44 | 45 | doSearch(event); 46 | this.setState({ 47 | [name]: event.target.value, 48 | }); 49 | }; 50 | 51 | render() 52 | { 53 | const { 54 | lang, 55 | search, 56 | searchBlur, 57 | searchFocus, 58 | } = this.props; 59 | 60 | const language = Lang[lang]; 61 | 62 | return
63 | {this.handleChange('search', e)}} 67 | onFocus={searchFocus} 68 | onBlur={searchBlur} 69 | id="standard-search" 70 | label={language.search} 71 | className={classNames(classes.textField, classes.dense)} 72 | margin="dense" 73 | name="search" 74 | /> 75 |
 
76 |
; 77 | } 78 | } 79 | 80 | MSearch.propTypes = { 81 | lang: PropTypes.string.isRequired, 82 | doSearch: PropTypes.func.isRequired, 83 | search: PropTypes.string, 84 | }; 85 | 86 | export default MSearch 87 | -------------------------------------------------------------------------------- /src/components/theme/material-ui/MSelect.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react' 2 | import PropTypes from 'prop-types' 3 | import Select from '@material-ui/core/Select' 4 | 5 | const CommonConstants = require('../../CommonConstants'); 6 | const EditorConstants = require('../../EditorConstants'); 7 | 8 | class MSelect extends Component { 9 | render() { 10 | const { 11 | attributes, 12 | id, 13 | name, 14 | value, 15 | onChange, 16 | objectValues 17 | } = this.props; 18 | 19 | let values = objectValues; 20 | let options = [], val = ''; 21 | let optionAttributes = []; 22 | 23 | for (let k in values) { 24 | if (values.hasOwnProperty(k)) { 25 | for (let key in values[k]) { 26 | if (values[k].hasOwnProperty(key)) { 27 | if (key !== EditorConstants.ATTRIBUTES) { 28 | 29 | if (typeof values[k][EditorConstants.ATTRIBUTES] !== CommonConstants.UNDEFINED) { 30 | optionAttributes = values[k][EditorConstants.ATTRIBUTES]; 31 | } 32 | 33 | val = values[k][key].trim(); 34 | options[k] = 35 | optionAttributes = []; 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | return ( 53 | ) 54 | } 55 | } 56 | 57 | MSelect.propTypes = { 58 | onChange: PropTypes.func.isRequired, 59 | id: PropTypes.string.isRequired, 60 | name: PropTypes.string.isRequired, 61 | attributes: PropTypes.object, 62 | value: PropTypes.string 63 | }; 64 | 65 | export default MSelect -------------------------------------------------------------------------------- /src/components/theme/material-ui/MTextArea.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import TextField from '@material-ui/core/TextField' 4 | 5 | class MTextArea extends Component { 6 | 7 | render() { 8 | const { 9 | attributes, 10 | id, 11 | name, 12 | value, 13 | onFocus, 14 | onChange, 15 | isMultiple 16 | } = this.props 17 | 18 | return ( 19 | 33 | ) 34 | } 35 | } 36 | 37 | MTextArea.propTypes = { 38 | onChange: PropTypes.func.isRequired, 39 | id: PropTypes.string.isRequired, 40 | name: PropTypes.string.isRequired, 41 | isMultiple: PropTypes.bool, 42 | onFocus: PropTypes.func, 43 | attributes: PropTypes.object 44 | } 45 | 46 | export default MTextArea -------------------------------------------------------------------------------- /src/components/tools/HOCPagesSelector.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import MPagesSelector from "../theme/material-ui/MPagesSelector"; 3 | import PagesSelector from "./PagesSelector"; 4 | 5 | const CommonConstants = require('../CommonConstants'); 6 | 7 | class HOCPagesSelector extends Component { 8 | render() { 9 | const { 10 | lang, 11 | perPageRows, 12 | perPage, 13 | updatePerPage, 14 | theme, 15 | defaultPerPage 16 | } = this.props; 17 | 18 | return (theme === CommonConstants.THEME_MATERIAL_UI) ? : 29 | } 30 | } 31 | 32 | export default HOCPagesSelector -------------------------------------------------------------------------------- /src/components/tools/HOCSearch.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Search from "./Search"; 4 | import MSearch from "../theme/material-ui/MSearch"; 5 | 6 | const CommonConstants = require('../CommonConstants'); 7 | 8 | class HOCSearch extends Component { 9 | shouldComponentUpdate(nextProps) { 10 | return this.props.search !== nextProps.search; 11 | } 12 | 13 | render() 14 | { 15 | const { 16 | lang, 17 | doSearch, 18 | search, 19 | theme, 20 | searchBlur, 21 | searchFocus, 22 | } = this.props; 23 | 24 | return (theme === CommonConstants.THEME_MATERIAL_UI) ? 25 | : 37 | } 38 | } 39 | 40 | Search.propTypes = { 41 | lang: PropTypes.string.isRequired, 42 | doSearch: PropTypes.func.isRequired, 43 | search: PropTypes.string, 44 | }; 45 | 46 | export default HOCSearch 47 | -------------------------------------------------------------------------------- /src/components/tools/PagesSelector.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styles from '../../css/styles.css'; 4 | 5 | const Lang = require('../Lang'); 6 | 7 | class PagesSelector extends Component { 8 | shouldComponentUpdate(nextProps) { 9 | return this.props.perPageRows !== nextProps.perPageRows 10 | || this.props.perPage !== nextProps.perPage; 11 | } 12 | 13 | render() 14 | { 15 | const { 16 | lang, 17 | perPageRows, 18 | perPage, 19 | updatePerPage 20 | } = this.props; 21 | let language = Lang[lang]; 22 | return ( 23 |
24 | {language.show}  25 | 26 | 34 | 35 |  {language.entries} 36 |
37 | ) 38 | } 39 | } 40 | 41 | PagesSelector.propTypes = { 42 | lang: PropTypes.string, 43 | perPage: PropTypes.number, 44 | perPageRows: PropTypes.array, 45 | updatePerPage: PropTypes.func 46 | }; 47 | 48 | export default PagesSelector 49 | -------------------------------------------------------------------------------- /src/components/tools/Pagination.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames/bind'; 4 | import styles from '../../css/styles.css'; 5 | 6 | const CommonConstants = require('../CommonConstants'); 7 | const Lang = require('../Lang'); 8 | 9 | class Pagination extends Component { 10 | shouldComponentUpdate(nextProps) 11 | { 12 | return this.props.countRows !== nextProps.countRows 13 | || this.props.page !== nextProps.page 14 | || this.props.perPage !== nextProps.perPage 15 | || this.props.fromRow !== nextProps.fromRow; 16 | } 17 | 18 | render() 19 | { 20 | const { 21 | fromRow, 22 | countRows, 23 | page, 24 | perPage, 25 | } = this.props; 26 | let lang = Lang[this.props.lang]; 27 | let prevClasses = classNames({ 28 | gt_page: true, 29 | prev: true 30 | }); 31 | let nextClasses = classNames({ 32 | gt_page: true, 33 | next: true 34 | }); 35 | 36 | let from = parseInt(fromRow); 37 | let pages = Math.ceil(countRows / perPage), 38 | selectedPage = from / perPage + 1, 39 | tail = parseInt(pages) - CommonConstants.MORE_PAGES, 40 | prevFrom = 0, nextFrom = 0; 41 | 42 | let pagesContent = []; 43 | for (let p = 0;p < pages;++p) { 44 | let currentPage = p + 1, prevPage = p - 1, nextPage = p + 1; 45 | let pageClasses = classNames({ 46 | gt_page: true, 47 | selected: (currentPage === page) ? true : false 48 | }); 49 | if (p > CommonConstants.MORE_PAGES) { 50 | if (selectedPage < CommonConstants.MORE_PAGES) { // head 51 | pagesContent[p] =
...
{pages}
; 53 | break; 54 | } else if (selectedPage >= CommonConstants.MORE_PAGES && selectedPage <= pages - CommonConstants.MORE_PAGES) { //middle 55 | prevPage = selectedPage - 1; 56 | nextPage = selectedPage + 1; 57 | prevFrom = (selectedPage - 2) * perPage; 58 | nextFrom = (selectedPage) * perPage; 59 | 60 | let midClasses = classNames({ 61 | gt_page: true, 62 | selected: (selectedPage === page) ? true : false 63 | }); 64 | pagesContent[p] =
1
65 |
...
66 |
{selectedPage - 1}
67 |
{selectedPage}
68 |
{selectedPage + 1}
69 |
...
70 |
{pages}
; 71 | break; 72 | } else if (selectedPage > tail) { // tail 73 | let innerPages = []; 74 | for (let i = tail - 1; i < pages; ++i) { 75 | let from = i * perPage; 76 | let prevPage = i - 1, nextPage = i + 1; 77 | if (selectedPage === nextPage) { 78 | prevFrom = prevPage * perPage; 79 | if (prevPage < 0) { 80 | prevFrom = (pages - 1) * perPage; 81 | } 82 | nextFrom = nextPage * perPage; 83 | if (nextPage === pages) { 84 | nextFrom = 0; 85 | } 86 | } 87 | pageClasses = classNames({ 88 | gt_page: true, 89 | selected: ((i + 1) === page) ? true : false 90 | }); 91 | innerPages[i] =
{(i + 1)}
; 92 | } 93 | pagesContent[p] =
1
...
{innerPages}
; 95 | break; 96 | } 97 | } else { 98 | if (selectedPage < CommonConstants.MORE_PAGES || ((selectedPage >= CommonConstants.MORE_PAGES) && tail === 1)) { 99 | pagesContent[p] =
{currentPage}
; 101 | } 102 | } 103 | } 104 | 105 | if (pages > 0 && pagesContent.length === 0) { // bug-fix with CommonConstants.MORE_PAGES === pages 106 | for (let p = 0;p < pages;++p) { 107 | let currentPage = p + 1, prevPage = p - 1, nextPage = p + 1; 108 | let pageClasses = classNames({ 109 | gt_page: true, 110 | selected: (currentPage === page) ? true : false 111 | }); 112 | pagesContent[p] =
{currentPage}
; 114 | } 115 | } 116 | 117 | let prev = (page === 1) ? perPage * (pages - 1) : perPage * (page - 2), 118 | next = (page === pages) ? 0 : perPage * (page); 119 | 120 | let showFrom = countRows === 0 ? 0 : (from + 1); 121 | let showTo = page * perPage; 122 | let description = lang.showing + ' ' + showFrom + ' ' 123 | + lang.to + ' ' + (showTo > countRows ? countRows : showTo) + ' ' + lang.of + ' ' + countRows + ' ' + lang.entries + '.'; 124 | if (countRows === 0) { 125 | description = lang.no_entries; 126 | } 127 | return ( 128 |
129 |
{description}
130 |
131 |
132 |
{lang.prev}
133 | {pagesContent} 134 |
{lang.next}
135 |
136 |
137 |
138 |
139 | ) 140 | } 141 | } 142 | 143 | Pagination.propTypes = { 144 | page: PropTypes.number, 145 | perPage: PropTypes.number, 146 | countRows: PropTypes.number, 147 | fromRow: PropTypes.number 148 | }; 149 | 150 | export default Pagination 151 | -------------------------------------------------------------------------------- /src/components/tools/Search.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styles from '../../css/styles.css'; 4 | 5 | const Lang = require('../Lang'); 6 | 7 | class Search extends Component { 8 | shouldComponentUpdate(nextProps) { 9 | return this.props.search !== nextProps.search; 10 | } 11 | 12 | render() 13 | { 14 | const { 15 | lang, 16 | doSearch, 17 | search, 18 | searchBlur, 19 | searchFocus, 20 | } = this.props; 21 | 22 | const language = Lang[lang]; 23 | return ( 24 |
25 | 36 |
37 |
38 | ) 39 | } 40 | } 41 | 42 | Search.propTypes = { 43 | lang: PropTypes.string.isRequired, 44 | doSearch: PropTypes.func.isRequired, 45 | search: PropTypes.string, 46 | }; 47 | 48 | export default Search 49 | -------------------------------------------------------------------------------- /src/components/tools/Tools.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Button from '../form/Button'; 4 | import CSVLink from "../form/CSVLink"; 5 | import styles from '../../css/styles.css'; 6 | import HOCButton from '../form/HOCButton' 7 | import HOCPagesSelector from "./HOCPagesSelector"; 8 | import HOCSearch from "./HOCSearch"; 9 | 10 | let CommonConstants = require('../CommonConstants'); 11 | let EditorConstants = require('../EditorConstants'); 12 | let Lang = require('../Lang'); 13 | 14 | class Tools extends Component { 15 | getPagesSelection() { 16 | const { 17 | struct, 18 | display, 19 | defaultPerPage, 20 | perPage, 21 | updatePerPage, 22 | perPageRows, 23 | lang, 24 | tableOpts 25 | } = this.props; 26 | 27 | if (struct.rowsSelector.indexOf(display) === -1) { 28 | return ''; 29 | } 30 | 31 | return () 38 | } 39 | 40 | getSearch() { 41 | const { 42 | struct, 43 | display, 44 | search, 45 | doSearch, 46 | lang, 47 | tableOpts, 48 | searchBlur, 49 | searchFocus 50 | } = this.props; 51 | 52 | if (struct.search.indexOf(display) === -1) { 53 | return ''; 54 | } 55 | 56 | return () 63 | } 64 | 65 | getButtons() { 66 | const { 67 | selectedRows, 68 | showPopup, 69 | lang, 70 | tableOpts, 71 | display, 72 | jsonData, 73 | struct, 74 | } = this.props; 75 | 76 | let language = Lang[lang]; 77 | let buttons = []; 78 | if (typeof tableOpts.buttons !== CommonConstants.UNDEFINED 79 | && tableOpts.buttons.length > 0 80 | && tableOpts.buttonsPosition.indexOf(display) !== -1) { 81 | 82 | tableOpts.buttons.map((btn, i) => { 83 | switch (btn[EditorConstants.EXTENDED]) { 84 | case EditorConstants.EDITOR_CSV: 85 | if (jsonData !== CommonConstants.UNDEFINED 86 | && typeof struct.download !== CommonConstants.UNDEFINED && struct.download.csv === true) { 87 | buttons[ i ] = {language.editor_csv}; 91 | } 92 | break; 93 | case EditorConstants.EDITOR_RELOAD: 94 | buttons[i] = ; 102 | break; 103 | case EditorConstants.EDITOR_CREATE: 104 | buttons[i] = {language.editor_create}; 113 | break; 114 | case EditorConstants.EDITOR_EDIT: 115 | buttons[i] = = 1)} 117 | action={EditorConstants.ACTION_EDIT} 118 | showPopup={showPopup} 119 | key={i} 120 | theme={tableOpts.theme} 121 | incr={i} 122 | selectedRows={selectedRows} 123 | >{language.editor_edit}; 124 | break; 125 | case EditorConstants.EDITOR_REMOVE: 126 | buttons[i] = {language.editor_remove}; 135 | break; 136 | } 137 | }); 138 | } 139 | return buttons; 140 | } 141 | 142 | render() { 143 | const { 144 | isData, 145 | } = this.props; 146 | 147 | return ( 148 |
149 | {(isData) ? '' : this.getButtons()} 150 | {this.getPagesSelection()} 151 | {this.getSearch()} 152 |
153 |
154 | ) 155 | } 156 | } 157 | 158 | Tools.propTypes = { 159 | updatePerPage: PropTypes.func.isRequired, 160 | perPageRows: PropTypes.array, 161 | doSearch: PropTypes.func, 162 | tableOpts: PropTypes.object, 163 | showPopup: PropTypes.func, 164 | defaultPerPage: PropTypes.number, 165 | perPage: PropTypes.number, 166 | search: PropTypes.string, 167 | selectedRows: PropTypes.array, 168 | lang: PropTypes.string, 169 | }; 170 | 171 | export default Tools 172 | -------------------------------------------------------------------------------- /src/css/styles.css: -------------------------------------------------------------------------------- 1 | .gt_container { 2 | margin: 0 auto; 3 | font: normal 14px Tahoma; 4 | min-width: 650px; 5 | } 6 | 7 | table.gigatable { 8 | border-spacing: 0; 9 | /*box-sizing: content-box;*/ 10 | font: normal 12px Tahoma; 11 | border-collapse: collapse; 12 | margin: 0 auto; 13 | table-layout: fixed; 14 | min-width: 550px; 15 | } 16 | 17 | tbody { 18 | color: #333; 19 | } 20 | 21 | tbody tr { 22 | cursor: default; 23 | border-collapse: collapse; 24 | border-top: 1px solid #ddd; 25 | } 26 | 27 | :global tbody tr.active { 28 | /*background-color:#08c;*/ 29 | /*background-color:#08c !important;*/ 30 | background-color: #25adf1 !important; 31 | } 32 | 33 | .active { 34 | background-color: #25adf1; 35 | } 36 | 37 | :global tbody tr.active:hover { 38 | background-color: #1098dc !important; 39 | } 40 | 41 | tbody tr.even { 42 | background-color: #fff; 43 | } 44 | 45 | tbody tr.odd { 46 | background-color: #f9f9f9; 47 | } 48 | 49 | :global .even { 50 | background-color: #fff; 51 | } 52 | 53 | :global .odd { 54 | background-color: #f9f9f9; 55 | } 56 | 57 | td { 58 | word-wrap: break-word; 59 | min-width: 50px; 60 | min-height: 20px; 61 | cursor: default; 62 | box-sizing: content-box; 63 | } 64 | 65 | .gt_head { 66 | border: 0; 67 | font-size: 14px; 68 | font-weight: bold; 69 | /*border-bottom: 1px solid #333;*/ 70 | } 71 | 72 | .gt_head_tr { 73 | border-bottom: 1px solid #333; 74 | } 75 | 76 | /*:global .gt_head_tr_th { 77 | padding:0 0 10px 0; 78 | }*/ 79 | .c_pointer { 80 | cursor: pointer; 81 | } 82 | 83 | .c_default { 84 | cursor: pointer 85 | } 86 | 87 | .gt_th_box { 88 | padding: 0 20px 0 0; 89 | } 90 | 91 | :global .gt_head_tools { 92 | margin: 15px 0; 93 | } 94 | 95 | .gt_foot_tools { 96 | margin: 15px 0; 97 | } 98 | 99 | .gt_body { 100 | border: solid 1px #efefef; 101 | font-size: 12px; 102 | } 103 | 104 | .gt_foot { 105 | border: 0; 106 | font-size: 14px; 107 | font-weight: bold; 108 | } 109 | 110 | .gt_foot tr { 111 | border-top: 1px solid #333; 112 | } 113 | 114 | /*.gt_foot tr th { 115 | padding:0 0 0 0; 116 | }*/ 117 | .gt_loader { 118 | text-align: center; 119 | } 120 | 121 | .gt_body tr td { 122 | padding: 8px 10px; 123 | border: 0; 124 | } 125 | 126 | .gt_body tr:hover { 127 | background-color: #f5f5f5; 128 | } 129 | 130 | .gt_body tr:focus { 131 | background-color: #08c; 132 | } 133 | 134 | .gt_main_search { 135 | float: right; 136 | } 137 | 138 | .gt_main_search input { 139 | padding: 3px 5px; 140 | color: #666; 141 | } 142 | 143 | .gt_rows_selector { 144 | float: left; 145 | } 146 | 147 | .clear { 148 | clear: both; 149 | } 150 | 151 | /* pagination */ 152 | .gt_pagination { 153 | margin: 15px 0; 154 | } 155 | 156 | .gt_pgn_ttl { 157 | float: left; 158 | margin: 8px 0; 159 | } 160 | 161 | .gt_pgn_pages { 162 | float: right; 163 | -moz-user-select: -moz-none; 164 | -khtml-user-select: none; 165 | -webkit-user-select: none; 166 | -ms-user-select: none; 167 | user-select: none; 168 | } 169 | 170 | :global .gt_page { 171 | cursor: pointer; 172 | background-color: #fff; 173 | border: 1px solid #ddd; 174 | color: #337ab7; 175 | float: left; 176 | line-height: 1.42857; 177 | margin-left: -1px; 178 | padding: 6px 12px; 179 | position: relative; 180 | text-decoration: none; 181 | } 182 | 183 | :global .gt_page_dots { 184 | cursor: not-allowed; 185 | background-color: #fff; 186 | border: 1px solid #ddd; 187 | color: #333; 188 | float: left; 189 | line-height: 1.42857; 190 | margin-left: -1px; 191 | padding: 6px 12px; 192 | position: relative; 193 | text-decoration: none; 194 | } 195 | 196 | :global .gt_page:hover { 197 | background-color: #eee; 198 | border-color: #ddd; 199 | color: #23527c; 200 | } 201 | 202 | :global .gt_page.prev { 203 | border-radius: 3px 0 0 3px; 204 | } 205 | 206 | :global .gt_page.next { 207 | border-radius: 0 3px 3px 0; 208 | } 209 | 210 | .gt_select { 211 | border: 1px solid #ddd; 212 | border-radius: 3px; 213 | font-size: 12px; 214 | height: 25px; 215 | line-height: 1.5; 216 | padding: 5px 10px; 217 | } 218 | 219 | .gt_select:focus { 220 | border: 1px solid #66afe9; 221 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6); 222 | outline: 0 none; 223 | } 224 | 225 | .gt_search { 226 | border: 1px solid #ddd; 227 | border-radius: 3px; 228 | font-size: 12px; 229 | height: 20px; 230 | line-height: 1.5; 231 | padding: 5px 10px; 232 | } 233 | 234 | .gt_search:focus { 235 | border: 1px solid #66afe9; 236 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6); 237 | outline: 0 none; 238 | } 239 | 240 | :global .selected { 241 | background-color: #337ab7; 242 | color: #fff; 243 | } 244 | 245 | :global .selected:hover { 246 | background-color: #337ab7; 247 | color: #fff; 248 | } 249 | 250 | :global .sorting { 251 | background: url('../images/sort_both.png') right center no-repeat; 252 | /* width:19px; 253 | height:19px;*/ 254 | } 255 | 256 | :global .sorting_asc { 257 | background: url('../images/sort_asc.png') right 16px no-repeat; 258 | } 259 | 260 | :global .sorting_desc { 261 | background: url('../images/sort_desc.png') right 16px no-repeat; 262 | } 263 | 264 | :global .unselectable { 265 | -moz-user-select: -moz-none; 266 | -moz-user-select: none; 267 | -o-user-select: none; 268 | -khtml-user-select: none; 269 | -webkit-user-select: none; 270 | -ms-user-select: none; 271 | user-select: none; 272 | } 273 | 274 | :global .normal_checkbox { 275 | border: 1px solid black !important; 276 | border-radius: 3px; 277 | content: " "; 278 | margin-left: 12px; 279 | margin-top: -20px; 280 | text-align: center; 281 | box-sizing: border-box; 282 | display: block; 283 | width: 13px; 284 | height: 12px; 285 | top: 1.2em; 286 | position: relative; 287 | } 288 | 289 | :global .select_checkbox::after { 290 | content: "\2713"; 291 | text-shadow: 1px 1px #25adf1, -1px -1px #25adf1, 1px -1px #25adf1, -1px 1px #25adf1; 292 | margin-left: 1px; 293 | /*margin-top: -11px;*/ 294 | } 295 | 296 | :global .progress_bar_container { 297 | border-radius: 4px; 298 | box-shadow: inset 0 0.5em 0.5em rgba(0, 0, 0, 0.05); 299 | margin: 10px; 300 | min-width: 100px; 301 | overflow: hidden; 302 | position: relative; 303 | transform: translateZ(0); 304 | } 305 | 306 | :global 307 | 308 | @keyframes slideInFromLeft { 309 | 0% { 310 | transform: translateX(-100%); 311 | } 312 | 100% { 313 | transform: translateX(0); 314 | } 315 | } 316 | 317 | :global .progress_bar { 318 | animation: 1s ease-out 0s 1 slideInFromLeft; 319 | } 320 | 321 | :global .progress_bar_percent { 322 | position: absolute; 323 | left: 50%; 324 | top: 25%; 325 | } 326 | 327 | :global .progress_bar_percent_child { 328 | position: relative; 329 | left: -50%; 330 | top: -25%; 331 | font: normal 10px Tahoma; 332 | text-align: center; 333 | } 334 | 335 | :global .progress_bar_none { 336 | /*border: 0;*/ 337 | } 338 | 339 | :global .progress_bar_color_red { 340 | background-color: #f35001; 341 | } 342 | 343 | :global .progress_bar_color_orange { 344 | background-color: #feae17; 345 | } 346 | 347 | :global .progress_bar_color_yellow { 348 | background-color: #fee71b; 349 | } 350 | 351 | :global .progress_bar_color_light_green { 352 | background-color: #abcb42; 353 | } 354 | 355 | :global .progress_bar_color_green { 356 | background-color: #5c7943; 357 | color: #fff; 358 | } 359 | 360 | :global .footer { 361 | background-color: #fff; 362 | border-radius: 5px; 363 | border: 1px solid #333; 364 | } 365 | :global .td_footer_cell { 366 | border-right: 1px dotted #333 !important; 367 | } -------------------------------------------------------------------------------- /src/css/styles.js: -------------------------------------------------------------------------------- 1 | import React, {StyleSheet, Dimensions, PixelRatio} from "react-native"; 2 | const {width, height, scale} = Dimensions.get("window"), 3 | vw = width / 100, 4 | vh = height / 100, 5 | vmin = Math.min(vw, vh), 6 | vmax = Math.max(vw, vh); 7 | 8 | export default StyleSheet.create({ 9 | "gt_container": { 10 | "marginTop": 0, 11 | "marginRight": "auto", 12 | "marginBottom": 0, 13 | "marginLeft": "auto", 14 | "font": "normal 14px Tahoma", 15 | "minWidth": 650 16 | }, 17 | "tablegigatable": { 18 | "borderSpacing": 0, 19 | "font": "normal 12px Tahoma", 20 | "borderCollapse": "collapse", 21 | "marginTop": 0, 22 | "marginRight": "auto", 23 | "marginBottom": 0, 24 | "marginLeft": "auto", 25 | "tableLayout": "fixed", 26 | "minWidth": 550 27 | }, 28 | "tbody": { 29 | "color": "#333" 30 | }, 31 | "tbody tr": { 32 | "cursor": "default", 33 | "borderCollapse": "collapse", 34 | "borderTop": "1px solid #ddd" 35 | }, 36 | ":global tbody tractive": { 37 | "backgroundColor": "#25adf1 !important" 38 | }, 39 | ":global active": { 40 | "backgroundColor": "#25adf1 !important" 41 | }, 42 | ":global tbody tractive:hover": { 43 | "backgroundColor": "#1098dc !important" 44 | }, 45 | "tbody treven": { 46 | "backgroundColor": "#fff" 47 | }, 48 | "tbody trodd": { 49 | "backgroundColor": "#f9f9f9" 50 | }, 51 | ":global even": { 52 | "backgroundColor": "#fff" 53 | }, 54 | ":global odd": { 55 | "backgroundColor": "#f9f9f9" 56 | }, 57 | "td": { 58 | "wordWrap": "break-word", 59 | "minWidth": 50, 60 | "minHeight": 20, 61 | "cursor": "default", 62 | "boxSizing": "content-box" 63 | }, 64 | "gt_head": { 65 | "border": 0, 66 | "fontSize": 14, 67 | "fontWeight": "bold" 68 | }, 69 | "gt_head_tr": { 70 | "borderBottom": "1px solid #333" 71 | }, 72 | "c_pointer": { 73 | "cursor": "pointer" 74 | }, 75 | "c_default": { 76 | "cursor": "pointer" 77 | }, 78 | "gt_th_box": { 79 | "paddingTop": 0, 80 | "paddingRight": 20, 81 | "paddingBottom": 0, 82 | "paddingLeft": 0 83 | }, 84 | ":global gt_head_tools": { 85 | "marginTop": 15, 86 | "marginRight": 0, 87 | "marginBottom": 15, 88 | "marginLeft": 0 89 | }, 90 | "gt_foot_tools": { 91 | "marginTop": 15, 92 | "marginRight": 0, 93 | "marginBottom": 15, 94 | "marginLeft": 0 95 | }, 96 | "gt_body": { 97 | "border": "solid 1px #efefef", 98 | "fontSize": 12 99 | }, 100 | "gt_foot": { 101 | "border": 0, 102 | "fontSize": 14, 103 | "fontWeight": "bold" 104 | }, 105 | "gt_foot tr": { 106 | "borderTop": "1px solid #333" 107 | }, 108 | "gt_loader": { 109 | "textAlign": "center" 110 | }, 111 | "gt_body tr td": { 112 | "paddingTop": 8, 113 | "paddingRight": 10, 114 | "paddingBottom": 8, 115 | "paddingLeft": 10, 116 | "border": 0 117 | }, 118 | "gt_body tr:hover": { 119 | "backgroundColor": "#f5f5f5" 120 | }, 121 | "gt_body tr:focus": { 122 | "backgroundColor": "#08c" 123 | }, 124 | "gt_main_search": { 125 | "float": "right" 126 | }, 127 | "gt_main_search input": { 128 | "paddingTop": 3, 129 | "paddingRight": 5, 130 | "paddingBottom": 3, 131 | "paddingLeft": 5, 132 | "color": "#666" 133 | }, 134 | "gt_rows_selector": { 135 | "float": "left" 136 | }, 137 | "clear": { 138 | "clear": "both" 139 | }, 140 | "gt_pagination": { 141 | "marginTop": 15, 142 | "marginRight": 0, 143 | "marginBottom": 15, 144 | "marginLeft": 0 145 | }, 146 | "gt_pgn_ttl": { 147 | "float": "left", 148 | "marginTop": 8, 149 | "marginRight": 0, 150 | "marginBottom": 8, 151 | "marginLeft": 0 152 | }, 153 | "gt_pgn_pages": { 154 | "float": "right", 155 | "MozUserSelect": "-moz-none", 156 | "KhtmlUserSelect": "none", 157 | "WebkitUserSelect": "none", 158 | "MsUserSelect": "none", 159 | "userSelect": "none" 160 | }, 161 | ":global gt_page": { 162 | "cursor": "pointer", 163 | "backgroundColor": "#fff", 164 | "border": "1px solid #ddd", 165 | "color": "#337ab7", 166 | "float": "left", 167 | "lineHeight": 1.42857, 168 | "marginLeft": -1, 169 | "paddingTop": 6, 170 | "paddingRight": 12, 171 | "paddingBottom": 6, 172 | "paddingLeft": 12, 173 | "position": "relative", 174 | "textDecoration": "none" 175 | }, 176 | ":global gt_page_dots": { 177 | "cursor": "not-allowed", 178 | "backgroundColor": "#fff", 179 | "border": "1px solid #ddd", 180 | "color": "#333", 181 | "float": "left", 182 | "lineHeight": 1.42857, 183 | "marginLeft": -1, 184 | "paddingTop": 6, 185 | "paddingRight": 12, 186 | "paddingBottom": 6, 187 | "paddingLeft": 12, 188 | "position": "relative", 189 | "textDecoration": "none" 190 | }, 191 | ":global gt_page:hover": { 192 | "backgroundColor": "#eee", 193 | "borderColor": "#ddd", 194 | "color": "#23527c" 195 | }, 196 | ":global gt_pageprev": { 197 | "borderRadius": "3px 0 0 3px" 198 | }, 199 | ":global gt_pagenext": { 200 | "borderRadius": "0 3px 3px 0" 201 | }, 202 | "gt_select": { 203 | "border": "1px solid #ddd", 204 | "borderRadius": 3, 205 | "fontSize": 12, 206 | "height": 25, 207 | "lineHeight": 1.5, 208 | "paddingTop": 5, 209 | "paddingRight": 10, 210 | "paddingBottom": 5, 211 | "paddingLeft": 10 212 | }, 213 | "gt_select:focus": { 214 | "border": "1px solid #66afe9", 215 | "boxShadow": "0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6)", 216 | "outline": "0 none" 217 | }, 218 | "gt_search": { 219 | "border": "1px solid #ddd", 220 | "borderRadius": 3, 221 | "fontSize": 12, 222 | "height": 20, 223 | "lineHeight": 1.5, 224 | "paddingTop": 5, 225 | "paddingRight": 10, 226 | "paddingBottom": 5, 227 | "paddingLeft": 10 228 | }, 229 | "gt_search:focus": { 230 | "border": "1px solid #66afe9", 231 | "boxShadow": "0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6)", 232 | "outline": "0 none" 233 | }, 234 | ":global selected": { 235 | "backgroundColor": "#337ab7", 236 | "color": "#fff" 237 | }, 238 | ":global selected:hover": { 239 | "backgroundColor": "#337ab7", 240 | "color": "#fff" 241 | }, 242 | ":global sorting": { 243 | "background": "url('../images/sort_both.png') right center no-repeat" 244 | }, 245 | ":global sorting_asc": { 246 | "background": "url('../images/sort_asc.png') right top no-repeat" 247 | }, 248 | ":global sorting_desc": { 249 | "background": "url('../images/sort_desc.png') right top no-repeat" 250 | }, 251 | ":global unselectable": { 252 | "MozUserSelect": "none", 253 | "OUserSelect": "none", 254 | "KhtmlUserSelect": "none", 255 | "WebkitUserSelect": "none", 256 | "MsUserSelect": "none", 257 | "userSelect": "none" 258 | }, 259 | ":global normal_checkbox": { 260 | "border": "1px solid black !important", 261 | "borderRadius": 3, 262 | "content": " ", 263 | "marginLeft": 12, 264 | "marginTop": -20, 265 | "textAlign": "center", 266 | "boxSizing": "border-box", 267 | "display": "block", 268 | "width": 13, 269 | "height": 12, 270 | "top": 1.2, 271 | "position": "relative" 272 | }, 273 | ":global select_checkbox::after": { 274 | "content": "\\2713", 275 | "textShadow": "1px 1px #25adf1, -1px -1px #25adf1, 1px -1px #25adf1, -1px 1px #25adf1", 276 | "marginLeft": 1 277 | } 278 | }); -------------------------------------------------------------------------------- /src/images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GigaTables/reactables/64b60235b0006e392ee5a42f619723fd28981c70/src/images/sort_asc.png -------------------------------------------------------------------------------- /src/images/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GigaTables/reactables/64b60235b0006e392ee5a42f619723fd28981c70/src/images/sort_asc_disabled.png -------------------------------------------------------------------------------- /src/images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GigaTables/reactables/64b60235b0006e392ee5a42f619723fd28981c70/src/images/sort_both.png -------------------------------------------------------------------------------- /src/images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GigaTables/reactables/64b60235b0006e392ee5a42f619723fd28981c70/src/images/sort_desc.png -------------------------------------------------------------------------------- /src/images/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GigaTables/reactables/64b60235b0006e392ee5a42f619723fd28981c70/src/images/sort_desc_disabled.png -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const config = { 4 | mode: 'development', 5 | entry: './main.js', 6 | output: { 7 | path: path.normalize(__dirname + '/build'), 8 | publicPath: '', 9 | filename: 'index.js', 10 | library: '[name]', 11 | chunkFilename: '[name].[chunkhash].js' 12 | }, 13 | devServer: { 14 | inline: true, 15 | port: 8888 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.jsx?$/, 21 | exclude: /node_modules/, 22 | loader: 'babel-loader', 23 | query: { 24 | presets: ['es2015', 'react', 'stage-0'] // stage-2 = transform-object-rest-spread etc 25 | } 26 | }, 27 | { 28 | test: /\.css$/, 29 | loader: 'style-loader!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' 30 | }, 31 | {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} 32 | ] 33 | }, 34 | externals: { 35 | 'react/addons': true 36 | }, 37 | node: { 38 | console: false, 39 | fs: 'empty', 40 | net: 'empty', 41 | tls: 'empty' 42 | } 43 | }; 44 | module.exports = config; -------------------------------------------------------------------------------- /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const package = require('./package.json'); 2 | const path = require('path'); 3 | 4 | const config = { 5 | mode: 'production', 6 | entry: './main.prod.js', 7 | output: { 8 | path: path.normalize(__dirname + '/build'), 9 | publicPath: '', 10 | filename: 'index.js', 11 | library: '[name]', 12 | chunkFilename: '[name].[chunkhash].js', 13 | libraryTarget: 'umd', 14 | umdNamedDefine: true 15 | }, 16 | devServer: { 17 | inline: true, 18 | port: 8888 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.jsx?$/, 24 | exclude: /node_modules/, 25 | loader: 'babel-loader', 26 | query: { 27 | presets: ['es2015', 'react', 'stage-0'] // stage-2 = transform-object-rest-spread etc 28 | } 29 | }, 30 | { 31 | test: /\.css$/, 32 | loader: 'style-loader!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' 33 | }, 34 | {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} 35 | ] 36 | }, 37 | plugins: [] 38 | .concat(require('./helpers/plugins/injectVars')({ 39 | bundleName: package.name, 40 | bundleVersion: package.version, 41 | bundleDescription: package.description, 42 | bundleAuthor: package.author, 43 | isPlatform: false, 44 | })), 45 | externals: [{ 46 | 'react': 'umd react', 47 | 'react-dom': 'umd react-dom', 48 | 'prop-types': 'umd prop-types', 49 | 'classnames': 'umd classnames', 50 | 'css-loader': 'umd css-loader', 51 | 'file-loader': 'umd file-loader', 52 | 'style-loader': 'umd style-loader', 53 | 'url-loader': 'umd url-loader', 54 | 'resolve-url': 'umd resolve-url', 55 | 'expect': 'umd expect', 56 | 'react-rte': 'umd react-rte', 57 | 'lodash': 'umd lodash', 58 | 'hoek': 'umd hoek', 59 | 'superagent': 'umd superagent', 60 | 'react-minimal-pie-chart': 'umd react-minimal-pie-chart', 61 | 'react-trend': 'umd react-trend', 62 | 'react-select': 'umd react-select', 63 | '@material-ui/core/Select': 'umd @material-ui/core/Select' 64 | }, /@material-ui\/core\/*./,] 65 | }; 66 | module.exports = config; 67 | --------------------------------------------------------------------------------