├── postcss.config.js
├── dist
├── app.bundle.css.map
├── resources
│ ├── favicon.ico
│ └── logo.svg
├── index.html
└── app.bundle.css
├── src
├── index.js
├── style
│ ├── index.js
│ ├── font.css
│ ├── reset.css
│ ├── codepen-export.svg
│ └── app.scss
└── app
│ ├── component
│ ├── index.js
│ ├── gui
│ │ ├── input
│ │ │ ├── index.jsx
│ │ │ ├── radio.jsx
│ │ │ ├── Code.jsx
│ │ │ ├── checkbox.jsx
│ │ │ ├── text.jsx
│ │ │ ├── collection.js
│ │ │ └── number.jsx
│ │ ├── icon
│ │ │ ├── modify.jsx
│ │ │ └── Clear.jsx
│ │ ├── index.jsx
│ │ ├── member.jsx
│ │ └── property.jsx
│ ├── chart
│ │ ├── billboard
│ │ │ ├── util.js
│ │ │ ├── proptypes.js
│ │ │ └── index.jsx
│ │ └── index.jsx
│ ├── tabs
│ │ └── index.jsx
│ ├── guide
│ │ ├── index.jsx
│ │ └── card.jsx
│ ├── command
│ │ ├── exportcode.js
│ │ ├── index.jsx
│ │ └── codemirror.js
│ └── data
│ │ └── index.jsx
│ ├── configure
│ ├── resources
│ │ ├── template.js
│ │ └── preset.js
│ └── index.js
│ ├── index.jsx
│ ├── reducers
│ ├── logic
│ │ ├── convert.js
│ │ └── index.js
│ └── index.js
│ ├── actions
│ └── index.js
│ └── util
│ └── index.js
├── .gitignore
├── webpack.config.js
├── .eslintrc
├── LICENSE
├── README.md
├── package.json
├── NOTICE
└── webpack.production.config.js
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
--------------------------------------------------------------------------------
/dist/app.bundle.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"app.bundle.css","sourceRoot":""}
--------------------------------------------------------------------------------
/dist/resources/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naver/billboard.js-playground/master/dist/resources/favicon.ico
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | require("codemirror/mode/javascript/javascript");
2 | require("./style");
3 | require("./app/index.jsx");
4 |
--------------------------------------------------------------------------------
/src/style/index.js:
--------------------------------------------------------------------------------
1 | require("./reset.css");
2 | require("./font.css");
3 | require("codemirror/lib/codemirror.css");
4 | require("billboard.js/dist/billboard.css");
5 | require("./app.scss");
6 |
--------------------------------------------------------------------------------
/src/app/component/index.js:
--------------------------------------------------------------------------------
1 | import { Chart } from "./chart";
2 | import { GUI } from "./gui";
3 | import { TabViews } from "./tabs";
4 | import { Guide } from "./guide";
5 |
6 | export {
7 | Chart, GUI, TabViews, Guide
8 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # npm modules
2 | components
3 | build
4 | .sass-cache
5 | npm-debug.log
6 | npm-debug.log*
7 |
8 | # coverage report
9 | coverage/
10 | .DS_Store
11 | #dist/
12 | doc/
13 | node_modules
14 | .idea/
15 | *.log
16 |
--------------------------------------------------------------------------------
/src/app/configure/resources/template.js:
--------------------------------------------------------------------------------
1 | const line1 = {
2 | data: {
3 | columns: [
4 | ["data1", 30, 200, 100, 400, 150],
5 | ["data2", 50, 20, 10, 40, 15],
6 | ["data3", 50, 20, 10, 40, 15],
7 | ["data4", 30, 200, 130, 400, 30],
8 | ["data5", 50, 20, 10, 40, 415]
9 | ]
10 | }
11 | };
12 |
13 | export {
14 | line1
15 | };
16 | export default line1;
17 |
--------------------------------------------------------------------------------
/src/app/component/gui/input/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import ConnectedCheckbox from "./checkbox";
4 | import ConnectedNumber from "./number";
5 | import ConnectedText from "./text";
6 | import ConnectedCollection from "./collection";
7 | import ConnectedCode from "./code";
8 |
9 | export {
10 | ConnectedCollection,
11 | ConnectedCheckbox,
12 | ConnectedNumber,
13 | ConnectedText,
14 | ConnectedCode
15 | };
16 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const portscanner = require("portscanner");
2 | const productconfig = require("./webpack.production.config");
3 |
4 | const config = productconfig;
5 |
6 | const host = process.env.HOST || "localhost";
7 | const port = process.env.PORT || 3000;
8 |
9 | const portPromise = new Promise((resolve) => {
10 | portscanner.findAPortNotInUse(port, port + 10, host, (error, psport) => {
11 | config.devServer.port = psport; // Status is 'open' if currently in use or 'closed' if available
12 | config.devServer.host = host;
13 | resolve(config);
14 | });
15 | });
16 |
17 | module.exports = portPromise;
18 |
19 |
--------------------------------------------------------------------------------
/src/app/component/chart/billboard/util.js:
--------------------------------------------------------------------------------
1 | // http://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge
2 | const deepCopy = (target, ...sources) => {
3 | const isObject = (item) =>{
4 | return (item && typeof item === 'object' && !Array.isArray(item));
5 | };
6 |
7 | if (!sources.length) return target;
8 | const source = sources.shift();
9 |
10 | if (isObject(target) && isObject(source)) {
11 | for (const key in source) {
12 | if (isObject(source[key])) {
13 | if (!target[key]) Object.assign(target, { [key]: {} });
14 | deepCopy(target[key], source[key]);
15 | } else {
16 | Object.assign(target, { [key]: source[key] });
17 | }
18 | }
19 | }
20 |
21 | return deepCopy(target, ...sources);
22 | };
23 |
24 | const UTIL = {
25 | deepCopy: deepCopy
26 | };
27 |
28 | export {
29 | deepCopy
30 | };
31 | export default UTIL;
--------------------------------------------------------------------------------
/src/app/component/chart/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { connect } from "react-redux";
3 | import CONFIGURE_PROPTYPES from "./billboard/proptypes";
4 | import { Billboard } from "./billboard/index";
5 |
6 | class RawChart extends React.Component {
7 | render() {
8 | const chartConfigure = this.props.chartConfigure;
9 |
10 | return (
);
18 | }
19 | }
20 |
21 | RawChart.propTypes = {
22 | chartConfigure: PropTypes.shape(CONFIGURE_PROPTYPES).isRequired
23 | };
24 |
25 | const mapStateToProps = state => ({
26 | chartConfigure: state.command.original
27 | });
28 |
29 | const Chart = connect(mapStateToProps)(RawChart);
30 |
31 | export {
32 | Chart,
33 | CONFIGURE_PROPTYPES
34 | };
35 | export default Chart;
36 |
37 |
--------------------------------------------------------------------------------
/src/style/font.css:
--------------------------------------------------------------------------------
1 |
2 | /* fallback */
3 | @font-face {
4 | font-family: 'Material Icons';
5 | font-style: normal;
6 | font-weight: 400;
7 | /*src: url(MaterialIcons-Regular.woff2) format('woff2'),*/
8 | src: url(https://fonts.gstatic.com/s/materialicons/v29/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2) format('woff2');
9 | }
10 | @import url('https://fonts.googleapis.com/css?family=Bungee');
11 | @import url('https://fonts.googleapis.com/css?family=Roboto:400,500');
12 |
13 | .material-icons {
14 | font-family: 'Material Icons';
15 | font-weight: normal;
16 | font-style: normal;
17 | font-size: 24px;
18 | line-height: 1;
19 | letter-spacing: normal;
20 | text-transform: none;
21 | display: inline-block;
22 | white-space: nowrap;
23 | word-wrap: normal;
24 | direction: ltr;
25 | -webkit-font-feature-settings: 'liga';
26 | -webkit-font-smoothing: antialiased;
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/src/app/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { Provider } from "react-redux";
4 | import { createStore } from "redux";
5 | import { Chart } from "./component/chart";
6 | import { GUI } from "./component/gui";
7 | import { TabViews } from "./component/tabs";
8 | import { Guide } from "./component/guide";
9 | import playgroundApp from "./reducers";
10 |
11 | const store = createStore(playgroundApp);
12 | const root = document.querySelector("#app");
13 |
14 | ReactDOM.render(
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 | ,
29 | root
30 | );
31 |
32 | document.body.className = "";
33 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | // .eslintrc 파일
2 | {
3 | "env": {
4 | "browser": true,
5 | "node": true
6 | },
7 | "extends": "airbnb",
8 | "parserOptions" : {
9 | "sourceType": "module",
10 | "ecmaFeatures": {
11 | "jsx": true
12 | }
13 | },
14 | "rules": {
15 | "indent": ["error",
16 | "tab",
17 | {
18 | "SwitchCase": 1,
19 | "MemberExpression": 1
20 | }
21 | ],
22 | "padded-blocks": ["error", "never"],
23 | "no-tabs": "off",
24 | "react/jsx-indent": ["error",
25 | "tab"
26 | ],
27 | "react/jsx-indent-props": ["error", "tab"],
28 | "quotes": ["error", "double", {
29 | "avoidEscape": true,
30 | "allowTemplateLiterals": true
31 | }],
32 | "jsx-closing-bracket-location" : [1, {"selfClosing": "props-aligned", "nonEmpty": "after-props"}],
33 | "react/forbid-prop-types": ["error", { "forbid": [] }],
34 | "react/prefer-stateless-function": [0, { "ignorePureComponents": true }],
35 | "comma-dangle" : ["error", {
36 | "functions": "ignore"
37 | }]
38 | }
39 | }
--------------------------------------------------------------------------------
/src/app/component/gui/icon/modify.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import FontIcon from 'material-ui/FontIcon';
4 | import { grey500 } from 'material-ui/styles/colors';
5 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
6 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
7 |
8 | class EditIcon extends React.Component {
9 | render (){
10 | return
11 | mode edit
18 | ;
19 | }
20 |
21 | onClickDelete(e) {
22 | //this.props.onClickDelete(e);
23 | }
24 | }
25 |
26 | const mapDispatchToProps = (dispatch, ownProps) => {
27 | return {
28 | //onChangeActive: e => dispatch(changeGuiActivate(ownProps.name.replace(/\:/g, "."), e.target.checked)),
29 | };
30 | };
31 |
32 | const Modify = connect(
33 | null, mapDispatchToProps
34 | )(EditIcon);
35 |
36 | export default Modify;
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 NAVER Corp.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/src/app/component/chart/billboard/proptypes.js:
--------------------------------------------------------------------------------
1 | import {
2 | PropTypes
3 | } from "react";
4 |
5 | const CONFIGURE_PROPTYPES = {
6 | area: PropTypes.object,
7 | axis: PropTypes.object,
8 | bar: PropTypes.object,
9 | bindto: PropTypes.string,
10 | color: PropTypes.object,
11 | data: PropTypes.object.isRequired,
12 | donut: PropTypes.object,
13 | gauge: PropTypes.object,
14 | grid: PropTypes.object,
15 | interaction: PropTypes.object,
16 | legend: PropTypes.object,
17 | line: PropTypes.object,
18 | oninit: PropTypes.func,
19 | onout: PropTypes.func,
20 | onover: PropTypes.func,
21 | onrendered: PropTypes.func,
22 | onresize: PropTypes.func,
23 | onresized: PropTypes.func,
24 | padding: PropTypes.object,
25 | pie: PropTypes.object,
26 | point: PropTypes.object,
27 | regions: PropTypes.array,
28 | resize: PropTypes.object,
29 | size: PropTypes.object,
30 | spline: PropTypes.object,
31 | subchart: PropTypes.object,
32 | svg: PropTypes.object,
33 | title: PropTypes.object,
34 | tooltip: PropTypes.object,
35 | transition: PropTypes.object,
36 | zoom: PropTypes.object,
37 | forceUpdate: PropTypes.bool // custom
38 | };
39 |
40 | export default CONFIGURE_PROPTYPES;
41 |
--------------------------------------------------------------------------------
/src/app/component/gui/icon/Clear.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import FontIcon from 'material-ui/FontIcon';
4 | import {
5 | deepOrange300,
6 | grey500
7 | } from 'material-ui/styles/colors';
8 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
9 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
10 | import {
11 | resetGui
12 | } from "../../../actions";
13 |
14 |
15 | class DeleteIcon extends React.Component {
16 | render (){
17 | const color = this.props.activated ? deepOrange300 : grey500;
18 | return
19 |
20 | this.onClickDelete(e)}
22 | className="material-icons"
23 | color={color}>remove_circle
24 |
25 | ;
26 | }
27 |
28 | onClickDelete(e) {
29 | this.props.onClickDelete(e);
30 | }
31 | }
32 |
33 | const mapDispatchToProps = (dispatch, ownProps) => {
34 | return {
35 | onChangeActive: e => dispatch(changeGuiActivate(ownProps.name.replace(/\:/g, "."), e.target.checked)),
36 | onClickDelete: () => dispatch(resetGui(ownProps.name.replace(/\:/g, "."), {
37 | root: ownProps.rootMemberName
38 | }))
39 | };
40 | };
41 |
42 | const Clear = connect(
43 | null, mapDispatchToProps
44 | )(DeleteIcon);
45 |
46 | export default Clear;
47 |
--------------------------------------------------------------------------------
/src/app/component/tabs/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Tabs, Tab} from 'material-ui/Tabs';
3 | import { Command } from "../command";
4 | import { Data } from "../data";
5 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
6 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
7 | import {grey100, grey400, deepOrange300} from "material-ui/styles/colors";
8 |
9 | class TabViews extends React.Component {
10 | componentWillMount(props) {
11 | this.setState({
12 | target: "command",
13 | });
14 | }
15 |
16 | render(){
17 | return (
18 |
25 | this.onChangeTab(e)}>
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
);
40 | }
41 |
42 | onChangeTab(target) {
43 | this.setState({
44 | target
45 | });
46 | }
47 | }
48 |
49 | export {
50 | TabViews
51 | }
52 | export default TabViews;
53 |
--------------------------------------------------------------------------------
/src/app/component/guide/index.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Drawer from "material-ui/Drawer";
3 | import getMuiTheme from "material-ui/styles/getMuiTheme";
4 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
5 | import { connect } from "react-redux";
6 | import { DocumentCard } from "./card";
7 |
8 | class DockedGuide extends React.Component {
9 | constructor() {
10 | super();
11 |
12 | this.state = {
13 | open: false
14 | };
15 | }
16 |
17 | componentDidMount() {
18 | this.setState(this.props);
19 | }
20 |
21 | componentWillReceiveProps(nextProps) {
22 | this.setState(nextProps);
23 | }
24 |
25 | shouldComponentUpdate(nextProps, nextState) {
26 | if (this.state.open !== nextState.open) {
27 | this.setState({
28 | open: nextState.open
29 | });
30 | return true;
31 | } else {
32 | return false;
33 | }
34 | }
35 |
36 | render() {
37 | return (
38 |
39 | { this.setState({ open }); }}
47 | width={600}
48 | openSecondary={true}
49 | open={this.state.open}
50 | >
51 |
52 |
53 | );
54 | }
55 | }
56 |
57 | const mapStateToProps = state => ({
58 | open: state.guide.open,
59 | style: state.guide.style,
60 | });
61 |
62 | const Guide = connect(
63 | mapStateToProps, null
64 | )(DockedGuide);
65 |
66 |
67 | export {
68 | Guide
69 | }
70 | export default Guide;
71 |
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # billboard.js playground
2 |
3 | - Create chart easy with GUI & Command & Table UI 🎨 .
4 |
5 | ### Features
6 | - You can handle most configures supported in billboard.js [billboard.js API](https://naver.github.io/billboard.js/release/latest/doc/)
7 | - directly access to description, default value, example etc.
8 | - export to code pen and share
9 |
10 | 
11 |
12 |
13 |
14 |
15 | ### comming soon..
16 | > import data file .csv, .json ...
17 | > color, data type template
18 |
19 | ### LICENSE
20 |
21 | ```
22 | Copyright (c) 2017 NAVER Corp.
23 |
24 | Permission is hereby granted, free of charge, to any person obtaining a copy of
25 | this software and associated documentation files (the "Software"), to deal in
26 | the Software without restriction, including without limitation the rights to
27 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
28 | the Software, and to permit persons to whom the Software is furnished to do so,
29 | subject to the following conditions:
30 |
31 | The above copyright notice and this permission notice shall be included in all
32 | copies or substantial portions of the Software.
33 |
34 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
36 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
37 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
38 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
39 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 | ```
41 |
--------------------------------------------------------------------------------
/src/app/component/gui/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import * as _ from "lodash";
3 | import { connect } from "react-redux";
4 | import Property from "./property";
5 | import Member from "./member";
6 | import Subheader from 'material-ui/Subheader';
7 | import Divider from 'material-ui/Divider';
8 | import {List, ListItem} from 'material-ui/List';
9 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
10 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
11 | import {yellow200, deepOrange400,deepOrange500, deepOrange700, grey900, grey50, grey200, yellow500, red600, deepOrange600, greenA200} from 'material-ui/styles/colors';
12 |
13 | class Control extends React.Component {
14 | getMember() {
15 | const hasProperty = [];
16 | const noneProperty = [];
17 | const lastUpdateRoot = this.props.lastUpdateRoot;
18 |
19 | _.map(this.props, (option) => {
20 | if (option.kind === "member") {
21 | const properties = option.properties;
22 |
23 | if (properties) {
24 |
25 | hasProperty.push( );
26 |
27 | } else {
28 | if(option.attributes.name !== "regions"){
29 | noneProperty.push( );
30 | }
31 | }
32 | }
33 | });
34 |
35 | return _.map(noneProperty.concat(hasProperty), (pp) => (pp));
36 | }
37 |
38 | render() {
39 | return (
40 |
41 | {this.getMember()}
42 |
43 |
);
44 | }
45 | }
46 |
47 | const mapStateToProps = (state) => {
48 | return state.gui;
49 | };
50 |
51 | const GUI = connect(mapStateToProps)(Control);
52 |
53 | export {
54 | GUI,
55 | };
56 | export default GUI;
57 |
--------------------------------------------------------------------------------
/src/app/component/gui/input/radio.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { connect } from "react-redux";
3 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
4 | import {RadioButton, RadioButtonGroup} from 'material-ui/RadioButton';
5 | import { grey400, lightBlue300} from 'material-ui/styles/colors';
6 | import { updateGui, resetGui } from "../../../actions";
7 |
8 | const iconSelect = {
9 | fill: lightBlue300,
10 | };
11 |
12 | const icon = {
13 | fill: grey400
14 | };
15 |
16 | const labelSelect = {
17 | fontSize : "14px"
18 | }
19 |
20 | const label = {
21 | fontSize : "14px",
22 | color: grey400
23 | }
24 |
25 | class InputRadio extends React.Component {
26 | render() {
27 | const { valueoptions, name, value, onChangeRadio } = this.props;
28 |
29 | return
30 |
31 | {_.map(valueoptions, (v, i) => {
32 | const iconStyle = value === v ? iconSelect : icon;
33 | const labelStyle = value === v ? labelSelect : label;
34 |
35 | return ( );
42 | })}
43 |
44 | ;
45 | }
46 | }
47 |
48 | const mapDispatchToProps = (dispatch, ownProps) => ({
49 | onChangeRadio: (e, value) => {
50 | if (value === "") {
51 | dispatch(resetGui(ownProps.name.replace(/\:/g, "."), {
52 | root: ownProps.rootMemberName
53 | }));
54 | } else {
55 | (dispatch(updateGui(ownProps.name.replace(/\:/g, "."), {
56 | value: value,
57 | root: ownProps.rootMemberName
58 | })));
59 | }
60 | }
61 | });
62 |
63 |
64 | const ConnectedRadio = connect(
65 | null, mapDispatchToProps
66 | )(InputRadio);
67 |
68 | export default ConnectedRadio;
69 |
--------------------------------------------------------------------------------
/src/app/component/gui/input/Code.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import TextField from 'material-ui/TextField';
3 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
4 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
5 | import IconButton from 'material-ui/IconButton';
6 |
7 | import { connect } from "react-redux";
8 | import {
9 | updateCodeInput
10 | } from "../../../actions";
11 | import {grey900, grey400, lightBlue300, grey800} from 'material-ui/styles/colors';
12 |
13 | class CodeInput extends React.Component {
14 | render() {
15 | let { value } = this.props;
16 | value += "";
17 |
18 | return
19 |
20 |
32 | edit}
40 | />
41 |
42 |
43 | ;
44 | }
45 | }
46 |
47 | const mapDispatchToProps = (dispatch, ownProps) => ({
48 | onClickCodeEdit: (e, value) => {
49 | dispatch(updateCodeInput(ownProps.name.replace(/\:/g, "."), {
50 | root: ownProps.rootMemberName || ownProps.name
51 | }));
52 | }
53 | });
54 |
55 | const ConnectedCode = connect(
56 | null, mapDispatchToProps
57 | )(CodeInput);
58 |
59 | export default ConnectedCode;
60 |
--------------------------------------------------------------------------------
/src/app/component/gui/input/checkbox.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { connect } from "react-redux";
3 | import getMuiTheme from "material-ui/styles/getMuiTheme";
4 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
5 |
6 | import Toggle from "material-ui/Toggle";
7 | import * as color from "material-ui/styles/colors";
8 | import { updateGui } from "../../../actions";
9 |
10 | const styles = {
11 | block: {
12 | maxWidth: 250,
13 | },
14 | toggle: {
15 | marginBottom: 16,
16 | },
17 | thumbSwitched: {
18 | backgroundColor: color.lightBlue100,
19 | },
20 | trackSwitched: {
21 | backgroundColor: color.lightBlue300,
22 | },
23 | labelStyle: {
24 | color: 'red',
25 | },
26 | };
27 |
28 | class InputCheckbox extends React.Component {
29 | render() {
30 | const checked = this.props.value ? true : false;
31 | const labelStyle = this.props.activated ? {
32 | color: color.grey900,
33 | fontSize: "14px"
34 | } : {
35 | color: color.grey400,
36 | fontSize: "14px"
37 | };
38 |
39 | return (
40 |
41 |
42 |
51 |
52 |
53 | );
54 | }
55 | }
56 |
57 | const mapDispatchToProps = (dispatch, ownProps) => ({
58 | onChange: (e, checked) => {
59 | dispatch(updateGui(ownProps.name.replace(/\:/g, "."), {
60 | value: checked,
61 | root: ownProps.rootMemberName
62 | }));
63 | }
64 | });
65 |
66 | const ConnectedCheckbox = connect(
67 | null, mapDispatchToProps
68 | )(InputCheckbox);
69 |
70 | export default ConnectedCheckbox;
71 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | billboard.js playground
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
--------------------------------------------------------------------------------
/src/app/component/command/exportcode.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { connect } from "react-redux";
3 | const beautify = require('js-beautify').js_beautify;
4 | import IconButton from 'material-ui/IconButton';
5 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
6 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
7 | const URL = {
8 | d3v4 : "https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.js",
9 | bbjs : "https://cdnjs.cloudflare.com/ajax/libs/billboard.js/1.0.1/billboard.js",
10 | bbcss : "https://cdnjs.cloudflare.com/ajax/libs/billboard.js/1.0.1/billboard.css",
11 | };
12 |
13 | const html = `
`;
14 |
15 | const css_external = `${URL.bbcss}`;
16 | const js_external = `${URL.d3v4};${URL.bbjs}`
17 |
18 | class CodePenExportCode extends React.Component {
19 | render() {
20 | const textConfig = beautify(this.props.text);
21 | const js = `bb.generate(${textConfig})`;
22 | const value = {
23 | title: "bb playground",
24 | css_external,
25 | js_external,
26 | html,
27 | js
28 | };
29 |
30 | return ();
52 | }
53 |
54 | onClickSubmit() {
55 | this.form.submit();
56 | }
57 | }
58 |
59 |
60 | CodePenExportCode.propTypes = {
61 | text: PropTypes.string.isRequired
62 | };
63 |
64 | const mapStateToProps = state => ({
65 | text: state.command.text
66 | });
67 |
68 | const ExportCode = connect(mapStateToProps, null)(CodePenExportCode);
69 |
70 | export default ExportCode;
71 |
--------------------------------------------------------------------------------
/src/style/reset.css:
--------------------------------------------------------------------------------
1 | /*
2 | html5doctor.com Reset Stylesheet
3 | v1.6.1
4 | Last Updated: 2010-09-17
5 | Author: Richard Clark - http://richclarkdesign.com
6 | Twitter: @rich_clark
7 | License : http://html5doctor.com/html-5-reset-stylesheet/
8 | */
9 |
10 | html, body, div, span, object, iframe,
11 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
12 | abbr, address, cite, code,
13 | del, dfn, em, img, ins, kbd, q, samp,
14 | small, strong, sub, sup, var,
15 | b, i,
16 | dl, dt, dd, ol, ul, li,
17 | fieldset, form, label, legend,
18 | table, caption, tbody, tfoot, thead, tr, th, td,
19 | article, aside, canvas, details, figcaption, figure,
20 | footer, header, hgroup, menu, nav, section, summary,
21 | time, mark, audio, video {
22 | margin:0;
23 | padding:0;
24 | border:0;
25 | outline:0;
26 | font-size:100%;
27 | vertical-align:baseline;
28 | background:transparent;
29 | }
30 |
31 | body {
32 | line-height:1;
33 | }
34 |
35 | article,aside,details,figcaption,figure,
36 | footer,header,hgroup,menu,nav,section {
37 | display:block;
38 | }
39 |
40 | nav ul {
41 | list-style:none;
42 | }
43 |
44 | blockquote, q {
45 | quotes:none;
46 | }
47 |
48 | blockquote:before, blockquote:after,
49 | q:before, q:after {
50 | content:'';
51 | content:none;
52 | }
53 |
54 | a {
55 | margin:0;
56 | padding:0;
57 | font-size:100%;
58 | vertical-align:baseline;
59 | background:transparent;
60 | }
61 |
62 | /* change colours to suit your needs */
63 | ins {
64 | background-color:#ff9;
65 | color:#000;
66 | text-decoration:none;
67 | }
68 |
69 | /* change colours to suit your needs */
70 | mark {
71 | background-color:#ff9;
72 | color:#000;
73 | font-style:italic;
74 | font-weight:bold;
75 | }
76 |
77 | del {
78 | text-decoration: line-through;
79 | }
80 |
81 | abbr[title], dfn[title] {
82 | border-bottom:1px dotted;
83 | cursor:help;
84 | }
85 |
86 | table {
87 | border-collapse:collapse;
88 | border-spacing:0;
89 | }
90 |
91 | /* change border colour to suit your needs */
92 | hr {
93 | display:block;
94 | height:1px;
95 | border:0;
96 | border-top:1px solid #cccccc;
97 | margin:1em 0;
98 | padding:0;
99 | }
100 |
101 | input, select {
102 | vertical-align:middle;
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/src/app/component/gui/input/text.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import TextField from 'material-ui/TextField';
3 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
4 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
5 | import { connect } from "react-redux";
6 | import { updateGui, resetGui } from "../../../actions";
7 | import { grey900, grey400, lightBlue300} from 'material-ui/styles/colors';
8 | import ConnectedRadio from "./radio";
9 |
10 | const underlineFocusStyle = {
11 | borderColor: lightBlue300
12 | };
13 |
14 | const textFieldStyle = {
15 | width: "100%",
16 | display: "inline-block"
17 | };
18 |
19 | class InputText extends React.Component {
20 | getTextToRadio() {
21 | return ( );
22 | }
23 |
24 | getDefaultTextArea(options) {
25 | return (
26 | this.props.onChangeText(e, v)}
29 | underlineFocusStyle={underlineFocusStyle}
30 | style={textFieldStyle}
31 | fullWidth={true}
32 | {...options} />
33 | );
34 | }
35 |
36 | render() {
37 | const { value, valueoptions } = this.props;
38 | let returnValue;
39 |
40 | if (valueoptions) {
41 | returnValue = this.getTextToRadio();
42 | } else if (value === undefined || value === "undefined") {
43 | returnValue = this.getDefaultTextArea({
44 | hintText: "undefined",
45 | value: ""
46 | });
47 | } else {
48 | returnValue = this.getDefaultTextArea({
49 | inputStyle: {
50 | color: this.props.activated ? grey900 : grey400
51 | },
52 | hintText: "undefined",
53 | value
54 | });
55 | }
56 |
57 | return returnValue;
58 | }
59 | }
60 |
61 | InputText.propTypes = {
62 | onChangeText: PropTypes.func,
63 | value: PropTypes.string,
64 | valueoptions: PropTypes.array
65 | };
66 |
67 |
68 | const mapDispatchToProps = (dispatch, ownProps) => ({
69 | onChangeText: (e, value) => {
70 | if (value === "") {
71 | dispatch(resetGui(ownProps.name.replace(/\:/g, "."), {
72 | root: ownProps.rootMemberName
73 | }));
74 | } else {
75 | (dispatch(updateGui(ownProps.name.replace(/\:/g, "."), {
76 | value,
77 | root: ownProps.rootMemberName
78 | })));
79 | }
80 | }
81 | });
82 |
83 |
84 | const ConnectedText = connect(
85 | null, mapDispatchToProps
86 | )(InputText);
87 |
88 | export default ConnectedText;
89 |
--------------------------------------------------------------------------------
/src/app/component/gui/member.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { ListItem } from "material-ui/List";
3 | import getMuiTheme from "material-ui/styles/getMuiTheme";
4 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
5 | import { deepOrange300 } from "material-ui/styles/colors";
6 | import * as _ from "lodash";
7 | import Property from "./property";
8 |
9 | class Member extends React.Component {
10 | componentWillMount() {
11 | this.setState(this.props);
12 | }
13 |
14 | componentWillReceiveProps(nextProps) {
15 | this.setState(nextProps);
16 | }
17 |
18 | shouldComponentUpdate(nextProps) {
19 | const roots = nextProps.lastUpdateRoot || [];
20 |
21 | if (roots.indexOf(this.props.attributes.name) > -1) {
22 | return true;
23 | }
24 | return false;
25 | }
26 |
27 | child(properties) {
28 | let items = [];
29 |
30 | _.map(properties, (option) => {
31 | if (option.properties) {
32 | items = items.concat(this.child(option.properties));
33 | } else if (option.attributes.type) {
34 | const name = option.attributes.type.names;
35 |
36 | if (name.indexOf("Array") > -1) {
37 | // items.push( );
43 | } else if (name.indexOf("Object") > -1) {
44 | // console.log(option.attributes.name);
45 | // console.log(option.attributes.defaultvalue);
46 | // console.log(option.attributes.examples);
47 | } else {
48 | items.push( );
54 | }
55 | }
56 | });
57 |
58 | return items;
59 | }
60 |
61 | render() {
62 | const member = this.props.attributes;
63 | const properties = this.props.properties;
64 |
65 | return (
66 |
81 | );
82 | }
83 | }
84 |
85 | export default Member;
86 |
--------------------------------------------------------------------------------
/src/app/component/gui/input/collection.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import * as _ from "lodash";
3 | import TextField from 'material-ui/TextField';
4 | import getMuiTheme from 'material-ui/styles/getMuiTheme';
5 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
6 | import IconButton from 'material-ui/IconButton';
7 |
8 | import { connect } from "react-redux";
9 | import {
10 | updateGui, resetGui
11 | } from "../../../actions";
12 |
13 | class ArrayInput extends React.Component {
14 | render() {
15 | const { value } = this.props;
16 | let returnValue;
17 | const edit = add
18 |
19 | if(Array.isArray(value)){
20 | returnValue = (
21 |
22 |
23 | {_.map(value, (v, i) => {
24 | return
25 | {i}
26 |
31 |
32 | })}
33 |
38 |
39 | );
40 | } else {
41 | returnValue = (
42 |
43 |
54 |
59 |
60 | );
61 | }
62 |
63 | return returnValue;
64 | }
65 | }
66 |
67 | const mapDispatchToProps = (dispatch, ownProps) => ({
68 | onChange: (e, value) => {
69 | if(value === ""){
70 | dispatch(resetGui(ownProps.name.replace(/\:/g, "."), {
71 | root: ownProps.rootMemberName
72 | }));
73 | } else {
74 | (dispatch(updateGui(ownProps.name.replace(/\:/g, "."), {
75 | value: value,
76 | root: ownProps.rootMemberName
77 | })));
78 | }
79 | }
80 | });
81 |
82 | const ConnectedCollection = connect(
83 | null, mapDispatchToProps
84 | )(ArrayInput);
85 |
86 | export default ConnectedCollection;
87 |
--------------------------------------------------------------------------------
/src/app/reducers/logic/convert.js:
--------------------------------------------------------------------------------
1 | const isUndefined = v => typeof v === "undefined";
2 | const findValueInJson = (object, path) => {
3 | if (object[path] !== undefined) {
4 | return object[path];
5 | }
6 |
7 | const convertedPath = path.replace(/\[(\w+)\]/g, ".$1"); // convert indexes to properties (replace [] with .)
8 | const pathArray = convertedPath.replace(/^\./, "").split("."); // strip a leading dot
9 | let target = object;
10 |
11 | for (const k of pathArray) {
12 | if (k in target) {
13 | target = target[k];
14 | } else {
15 | target = undefined;
16 | break;
17 | }
18 | }
19 | return target;
20 | };
21 |
22 |
23 | export const convertColumnsToData = (columns) => {
24 | const newRows = [];
25 | let i;
26 | let j;
27 | let key;
28 |
29 | for (i = 0; i < columns.length; i++) {
30 | key = columns[i][0];
31 | for (j = 1; j < columns[i].length; j++) {
32 | if (isUndefined(newRows[j - 1])) {
33 | newRows[j - 1] = {};
34 | }
35 | if (isUndefined(columns[i][j])) {
36 | throw new Error(`Source data is missing a component at (${i}, ${j})!`);
37 | }
38 | newRows[j - 1][key] = columns[i][j];
39 | }
40 | }
41 | return newRows;
42 | }
43 |
44 | export const convertJsonToData = (json, keys) => {
45 | const newRows = [];
46 | let targetKeys;
47 | let data;
48 |
49 | if (keys) { // when keys specified, json would be an array that includes objects
50 | if (keys.x) {
51 | targetKeys = keys.value.concat(keys.x);
52 | this.config.data_x = keys.x;
53 | } else {
54 | targetKeys = keys.value;
55 | }
56 | newRows.push(targetKeys);
57 |
58 | json.forEach(o => {
59 | const newRow = [];
60 | let v;
61 |
62 | for (const key of targetKeys) {
63 | // convert undefined to null because undefined data will be removed in convertDataToTargets()
64 | v = findValueInJson(o, key);
65 | if (isUndefined(v)) {
66 | v = null;
67 | }
68 | newRow.push(v);
69 | }
70 | newRows.push(newRow);
71 | });
72 | data = convertRowsToData(newRows);
73 | } else {
74 | Object.keys(json).forEach(key => {
75 | const tmp = json[key].concat();
76 |
77 | tmp.unshift(key);
78 | newRows.push(tmp);
79 | });
80 | data = convertColumnsToData(newRows);
81 | }
82 | return data;
83 | };
84 |
85 | export const convertRowsToData = (rows) => {
86 | const keys = rows[0];
87 | const newRows = [];
88 | let newRow = {};
89 | let i;
90 | let j;
91 |
92 | for (i = 1; i < rows.length; i++) {
93 | newRow = {};
94 | for (j = 0; j < rows[i].length; j++) {
95 | if (isUndefined(rows[i][j])) {
96 | throw new Error(`Source data is missing a component at (${i}, ${j})!`);
97 | }
98 | newRow[keys[j]] = rows[i][j];
99 | }
100 | newRows.push(newRow);
101 | }
102 |
103 | return newRows;
104 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "billboardjs-playground",
3 | "version": "0.7.0",
4 | "description": "playground for billboard.js",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "npm run extract:document && webpack-dev-server --open",
8 | "build": "npm run extract:document && webpack --env production --config ./webpack.production.config.js",
9 | "extract:document": "echo module.exports = > src/app/configure/resources/document.js && jsdoc2md --json node_modules/billboard.js/src/config/Options.js >> src/app/configure/resources/document.js"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/naver/billboard.js-playground"
14 | },
15 | "keywords": [
16 | "billboard.js",
17 | "chart",
18 | "generator",
19 | "playground"
20 | ],
21 | "author": "joohwee.kim",
22 | "license": "MIT",
23 | "dependencies": {
24 | "billboard.js": "^1.2.0",
25 | "classnames": "^2.2.5",
26 | "codemirror": "^5.30.0",
27 | "create-react-class": "^15.6.2",
28 | "js-beautify": "^1.6.14",
29 | "lodash": "^4.17.4",
30 | "material-ui": "^0.19.1",
31 | "react": "^16.0.0",
32 | "react-dom": "^16.0.0",
33 | "react-redux": "^5.0.6",
34 | "react-watcher": "0.0.1",
35 | "redux": "^3.7.2"
36 | },
37 | "devDependencies": {
38 | "babel-core": "^6.24.1",
39 | "babel-loader": "^6.4.1",
40 | "babel-plugin-add-module-exports": "^0.2.1",
41 | "babel-preset-es2015": "^6.24.1",
42 | "babel-preset-react": "^6.24.1",
43 | "clean-webpack-plugin": "^0.1.16",
44 | "css-loader": "^0.28.7",
45 | "eslint": "^3.19.0",
46 | "eslint-config-naver": "^1.0.0",
47 | "eslint-loader": "^1.7.1",
48 | "eslint-plugin-import": "^2.2.0",
49 | "eslint-plugin-jsx-a11y": "^5.0.1",
50 | "eslint-plugin-react": "^7.4.0",
51 | "extract-text-webpack-plugin": "^2.1.2",
52 | "file-loader": "^0.11.2",
53 | "jsdoc-to-markdown": "^3.0.3",
54 | "node-sass": "^4.5.2",
55 | "optimize-css-assets-webpack-plugin": "^1.3.0",
56 | "portscanner": "^2.1.1",
57 | "postcss": "^6.0.11",
58 | "postcss-cssnext": "^3.0.2",
59 | "postcss-load-config": "^1.2.0",
60 | "postcss-loader": "^2.0.6",
61 | "react-billboardjs": "git://github.com/kjhwee91/react-billboardjs.git",
62 | "react-hot-loader": "^3.0.0-beta.7",
63 | "sass-loader": "^6.0.6",
64 | "scss-loader": "0.0.1",
65 | "string-replace-webpack-plugin": "0.1.3",
66 | "style-loader": "^0.18.2",
67 | "svg-inline-loader": "^0.8.0",
68 | "svg-url-loader": "^2.1.1",
69 | "uglify-js": "^2.8.22",
70 | "uglifyjs-webpack-plugin": "^0.4.1",
71 | "url-loader": "^0.5.9",
72 | "webpack": "^2.6.0",
73 | "webpack-cleanup-plugin": "^0.5.1",
74 | "webpack-dev-server": "^2.4.2",
75 | "webpack-merge": "^4.1.0",
76 | "write-file-webpack-plugin": "^4.0.0"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/app/component/command/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import CodeMirror from "./codemirror";
3 | import { connect } from "react-redux";
4 | import { updateCommand, reflectCommandToDatatable } from "../../actions";
5 | import ExportCode from "./exportcode";
6 | var beautify_js = require('js-beautify').js_beautify;
7 |
8 | class Controller extends React.Component {
9 | constructor() {
10 | super();
11 |
12 | this.state = {
13 | original: {},
14 | error: false,
15 | text: ""
16 | };
17 |
18 | this.onChangeText = this.onChangeText.bind(this);
19 | }
20 |
21 | componentWillMount() {
22 | this.setState({
23 | fromData: this.props.fromData,
24 | original: this.props.original,
25 | text: this.props.text,
26 | focus: this.props.focus
27 | });
28 | }
29 |
30 | componentWillReceiveProps(nextProps) {
31 | this.setState({
32 | fromData: nextProps.fromData,
33 | original: nextProps.original,
34 | focus: nextProps.focus,
35 | text: nextProps.text
36 | });
37 | }
38 |
39 | render() {
40 | const value = `${this.state.text}`;
41 | const options = {
42 | lineNumbers: true,
43 | mode: "javascript"
44 | };
45 |
46 | const newLine = beautify_js(value);
47 | const className = "textConfigure" + " " + (this.state.error ? "err" : "");
48 |
49 | return (
50 |
51 | this.onChangeText(v)} options={options} />
52 |
);
53 | }
54 |
55 | getParsed(value) {
56 | let parsed;
57 | try {
58 | eval(`parsed = ${value}`);
59 | } catch (e) {
60 | parsed = null;
61 | }
62 |
63 | return parsed;
64 | }
65 |
66 | onChangeText(value) {
67 | let parsed = this.getParsed(value);
68 |
69 | if(parsed === null){
70 | this.setState({
71 | error: true,
72 | text: value
73 | });
74 | } else {
75 | this.setState({
76 | error: false,
77 | text: value,
78 | });
79 | this.props.reflectCode({ value: parsed });
80 | this.props.onChange({
81 | prev: this.state.original,
82 | value: parsed
83 | });
84 | }
85 | }
86 | }
87 |
88 | Controller.propTypes = {
89 | text: PropTypes.string.isRequired,
90 | onChange: PropTypes.func.isRequired
91 | };
92 |
93 | const mapDispatchToProps = dispatch => ({
94 | reflectCode: (value) => {
95 | dispatch(reflectCommandToDatatable(value));
96 | },
97 | onChange: value => {
98 | dispatch(updateCommand(value))
99 | },
100 | });
101 |
102 | const mapStateToProps = state => ({
103 | original: state.command.original,
104 | text: state.command.text,
105 | focus: state.command.focus
106 | });
107 |
108 | const Command = connect(mapStateToProps, mapDispatchToProps)(Controller);
109 |
110 | export {
111 | Command
112 | };
113 | export default Command;
114 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | billboard.js playground
2 | Copyright 2017 NAVER Corp.
3 |
4 | This project contains subcomponents with separate copyright notices and license terms.
5 | Your use of the source code for these subcomponents is subject to the terms and conditions of the following licenses.
6 |
7 | =====
8 |
9 | JedWatson/react-codemirror from https://github.com/JedWatson/react-codemirror
10 |
11 | =====
12 |
13 | The MIT License (MIT)
14 |
15 | Copyright (c) 2016 Jed Watson
16 |
17 | Permission is hereby granted, free of charge, to any person obtaining a copy
18 | of this software and associated documentation files (the "Software"), to deal
19 | in the Software without restriction, including without limitation the rights
20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 | copies of the Software, and to permit persons to whom the Software is
22 | furnished to do so, subject to the following conditions:
23 |
24 | The above copyright notice and this permission notice shall be included in all
25 | copies or substantial portions of the Software.
26 |
27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 | SOFTWARE.
34 |
35 | =====
36 |
37 | richclark/HTML5resetCSS from http://html5doctor.com/html-5-reset-stylesheet/
38 |
39 | =====
40 |
41 | Permission is hereby granted, free of charge, to any person obtaining a copy of any version of this file on Google Code or this file on Github (the “Software”), to deal in the Software under the terms of the CC0 Public Domain Dedication, or,alternatively, to deal in the Software under the terms of the MIT License.
42 |
43 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
44 |
45 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
46 |
47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/src/app/component/gui/input/number.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { connect } from "react-redux";
3 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
4 | import getMuiTheme from "material-ui/styles/getMuiTheme";
5 | import Slider from "material-ui/Slider";
6 | import * as color from "material-ui/styles/colors";
7 |
8 | import {
9 | updateGui, resetGui
10 | } from "../../../actions";
11 | import {
12 | deepCopy
13 | } from "../../../util";
14 |
15 |
16 | const SliderStyleTheme = {
17 | trackColor: color.grey400,
18 | trackColorSelected: color.grey400,
19 | selectionColor: color.lightBlue300,
20 | handleColorZero: color.grey400,
21 | handleFillColor: color.grey100,
22 | rippleColor: color.lightBlue100
23 | };
24 |
25 | const SliderStyle = {
26 | padding: 0,
27 | margin: 0
28 | };
29 | const Style = {
30 | width: "60%",
31 | display: "inline-block"
32 | };
33 | const ValueTextStyle = {
34 | width: "40%",
35 | display: "inline-block"
36 | };
37 |
38 | class InputNumber extends React.Component {
39 | render() {
40 | const props = deepCopy({}, this.props);
41 | const textStyle = deepCopy({}, ValueTextStyle);
42 |
43 | props.min = props.min || 0;
44 | props.max = props.max || 100;
45 | textStyle.color = props.activated ? color.grey900 : color.grey400;
46 |
47 | if (props.docid && isNaN(props.value)) {
48 | props.value = props.defaultvalue || undefined;
49 |
50 | return (
51 |
52 |
63 |
64 | undefined
65 |
);
66 | }
67 | props.value *= 1;
68 |
69 | return (
70 |
71 |
82 |
83 | {props.value}
84 |
);
85 | }
86 | }
87 |
88 | const mapDispatchToProps = (dispatch, ownProps) => ({
89 | onChange: (e, value) => {
90 | const name = ownProps.name.replace(/\:/g, ".");
91 | if (value === "") {
92 | dispatch(resetGui(name, {
93 | root: ownProps.rootMemberName
94 | }));
95 | } else {
96 | dispatch(updateGui(name, {
97 | root: ownProps.rootMemberName,
98 | value: value * 1
99 | }));
100 | }
101 | }
102 | });
103 |
104 | const ConnectedNumber = connect(
105 | null, mapDispatchToProps
106 | )(InputNumber);
107 |
108 | export default ConnectedNumber;
109 |
--------------------------------------------------------------------------------
/src/app/component/chart/billboard/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import connect from "react-watcher";
3 | import * as bb from "billboard.js";
4 | import CONFIGURE_PROPTYPES from "./proptypes";
5 | import { deepCopy } from "./util";
6 |
7 | class Billboard extends React.Component {
8 | componentWillMount() {
9 | this.instance = null;
10 | this.updated = false;
11 | this.setState(this.props);
12 | }
13 |
14 | componentDidMount() {
15 | this.watchConfig();
16 | this.generateChart();
17 | }
18 |
19 | componentDidUpdate() {
20 | this.updated = false;
21 | this.generateChart();
22 | }
23 |
24 | componentWillReceiveProps(props) {
25 | this.setState(props);
26 | }
27 |
28 | shouldComponentUpdate() {
29 | const willRender = !this.updated;
30 |
31 | if (willRender) {
32 | this.destroy();
33 | }
34 | return willRender;
35 | }
36 |
37 | componentWillUnmount() {
38 | this.destroy();
39 | }
40 |
41 | watchConfig() {
42 | const { watch } = this.state;
43 |
44 | //watch("axis.y.labels", data => this.instance.axis.label({ y: data }));
45 | //watch("axis.x.categories", data => this.instance.categories(data));
46 | //watch("legend.show", data => this.instance.legend.show(data));
47 | //watch("legend.hide", data => this.instance.legend.hide(data));
48 | //watch("regions", data => this.instance.regions(data));
49 | //watch("size.width", data => this.instance.resize({ width: data }));
50 | //watch("size.height", data => this.instance.resize({ height: data }));
51 | //watch("grid.x.lines", data => this.instance.xgrids(data));
52 | //watch("grid.y.lines", data => this.instance.ygrids(data));
53 | //watch("zoom.enable", data => this.instance.zoom.enable(data));
54 | //watch("zoom.domain", (data) => {
55 | // if (data === null) {
56 | // this.instance.unzoom();
57 | // } else {
58 | // this.instance.zoom();
59 | // }
60 | //});
61 | //watch("data.columns", (data) => {
62 | // this.instance.load({ columns: data });
63 | //});
64 |
65 | //Object.keys(CONFIGURE_PROPTYPES).forEach((key) => {
66 | // watch(key, (data) => {
67 | // console.log(data);
68 | // });
69 | //});
70 | }
71 |
72 | getDispatchedValue(namespace, props) {
73 | const keys = namespace.split(".");
74 | let newProps = props;
75 |
76 | keys.every(key => {
77 | newProps = newProps[key];
78 | if(newProps === undefined){
79 | return false;
80 | }
81 | });
82 |
83 | return newProps;
84 | }
85 |
86 | destroy() {
87 | this.instance && this.instance.destroy();
88 | this.instance = null;
89 | }
90 |
91 | generateChart(mountNode = this.wrapper, config = this.props) {
92 | // using react node
93 |
94 | const newConfig = deepCopy({}, config);
95 | newConfig.bindto = mountNode;
96 |
97 | this.instance = bb.generate(newConfig);
98 | window.chart = this.instance;
99 | }
100 |
101 | render() {
102 | return ( { this.wrapper = d; }}
105 | >
);
106 | }
107 | }
108 |
109 | Billboard.propTypes = CONFIGURE_PROPTYPES;
110 |
111 | export {
112 | Billboard
113 | };
114 | Billboard = connect(Billboard);
115 | export default Billboard;
116 |
--------------------------------------------------------------------------------
/webpack.production.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const path = require("path");
3 | const ExtractTextPlugin = require("extract-text-webpack-plugin");
4 | const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
5 |
6 | const config = {
7 | context: `${__dirname}/`,
8 | entry: {
9 | app: path.resolve(__dirname, "./src/index.js")
10 | },
11 | output: {
12 | path: path.join(__dirname, "/dist"), // 파일이 저장될 경로
13 | filename: "[name].bundle.js",
14 | publicPath: "/dist/", // 서버상의 경로 (ex. express.static)
15 | libraryTarget: "umd"
16 | },
17 | module: {
18 | rules: [{
19 | test: [/(\.js)$/, /(\.jsx)$/],
20 | exclude: /(node_modules)/,
21 | include: path.join(__dirname, "src"),
22 | loader: "babel-loader",
23 | query: {
24 | presets: ["es2015", "react"]
25 | }
26 | }, {
27 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
28 | loader: "url-loader?name=[name].[ext]&limit=10000&minetype=application/font-woff"
29 | }, {
30 | test: /\.svg$/,
31 | loader: "svg-url-loader?name=[name].[ext]"
32 | }, {
33 | test: /\.scss$/,
34 | use: ExtractTextPlugin.extract({
35 | use: [{
36 | loader: "css-loader"
37 | }, {
38 | loader: "sass-loader"
39 | }],
40 | fallback: "style-loader"
41 | })
42 | }, {
43 | test: /\.css$/,
44 | loader: ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" })
45 | }]
46 | },
47 | resolve: {
48 | modules: [
49 | "node_modules",
50 | path.resolve("./src")
51 | ],
52 | extensions: [".js", ".jsx"]
53 | },
54 | externals: {
55 | "prop-types": {
56 | commonjs2: "prop-types",
57 | commonjs: "prop-types",
58 | amd: "prop-types",
59 | umd: "prop-types",
60 | root: "PropTypes"
61 | },
62 | lodash: {
63 | commonjs2: "lodash",
64 | commonjs: "lodash",
65 | amd: "lodash",
66 | umd: "lodash",
67 | root: "_"
68 | },
69 | d3: {
70 | root: "d3",
71 | commonjs2: "d3",
72 | commonjs: "d3",
73 | amd: "d3",
74 | umd: "d3"
75 | },
76 | "billboard.js": {
77 | root: "bb",
78 | commonjs2: "billboard.js",
79 | commonjs: "billboard.js",
80 | amd: "billboard.js",
81 | umd: "billboard.js"
82 | },
83 | react: {
84 | root: "React",
85 | commonjs2: "react",
86 | commonjs: "react",
87 | amd: "react",
88 | umd: "react"
89 | },
90 | redux: {
91 | root: "Redux",
92 | commonjs2: "redux",
93 | commonjs: "redux",
94 | amd: "redux",
95 | umd: "redux"
96 | },
97 | "react-redux": {
98 | root: "ReactRedux",
99 | commonjs2: "react-redux",
100 | commonjs: "react-redux",
101 | amd: "react-redux",
102 | umd: "react-redux"
103 | },
104 | "react-dom": {
105 | root: "ReactDOM",
106 | commonjs2: "react-dom",
107 | commonjs: "react-dom",
108 | amd: "react-dom",
109 | umd: "react-dom"
110 | }
111 | },
112 | devtool: "cheap-eval-source-map",
113 | devServer: {
114 | contentBase: `${__dirname}/`,
115 | hot: true,
116 | inline: true
117 | },
118 | plugins: [
119 | new webpack.ProvidePlugin({
120 | d3: "d3",
121 | $: "jquery",
122 | jQuery: "jquery"
123 | }),
124 | new ExtractTextPlugin("app.bundle.css"),
125 | new webpack.HotModuleReplacementPlugin(),
126 | new UglifyJsPlugin({
127 | drop_console: true,
128 | sourceMap: true
129 | })
130 | ]
131 | };
132 |
133 | module.exports = config;
134 |
--------------------------------------------------------------------------------
/src/app/actions/index.js:
--------------------------------------------------------------------------------
1 | export const UPDATE_GUI = "UPDATE_GUI";
2 | export const UPDATE_COMMAND = "UPDATE_COMMAND";
3 | export const RESET_GUI = "RESET_GUI";
4 | export const CHANGE_GUI_ACTIVATE = "CHANGE_GUI_ACTIVATE";
5 | export const UPDATE_DATA = "UPDATE_DATA";
6 | export const UPDATE_CODE_INPUT = "UPDATE_CODE_INPUT";
7 | export const RECENT_CONFIGURE = "RECENT_CONFIGURE";
8 | export const ADD_KEY_FROM_TABLE = "ADD_KEY_FROM_TABLE";
9 | export const ADD_VALUE_FROM_TABLE = "ADD_VALUE_FROM_TABLE";
10 | export const REMOVE_VALUE_FROM_TABLE = "REMOVE_VALUE_FROM_TABLE";
11 | export const REMOVE_KEY_FROM_TABLE = "REMOVE_KEY_FROM_DATA";
12 | export const REFLECTED_DATA = "REFLECTED_DATA";
13 | export const UPDATE_TABLE_KEY = "UPDATE_TABLE_KEY";
14 | export const UPDATE_TABLE_VALUE = "UPDATE_TABLE_VALUE";
15 | export const REFLECT_CODE_TO_DATATABLE = "REFLECT_CODE_TO_DATATABLE";
16 | export const UPDATE_CONFIGURE_INFO = "UPDATE_CONFIGURE_INFO";
17 | export const SHOW_GUIDE_CARD = "SHOW_GUIDE_CARD";
18 | export const HIDE_GUIDE_CARD = "HIDE_GUIDE_CARD";
19 |
20 | export const hideGuideCard = () => {
21 | return {
22 | type: HIDE_GUIDE_CARD
23 | }
24 |
25 | };
26 | export const showGuideCard = (style) => {
27 | return {
28 | type: SHOW_GUIDE_CARD,
29 | style,
30 | }
31 | }
32 |
33 | export const updateConfigureInfo = (name) => {
34 | return {
35 | type: UPDATE_CONFIGURE_INFO,
36 | name
37 | }
38 | }
39 |
40 |
41 | export const updateHeader = (updateData) => {
42 | return {
43 | type: UPDATE_TABLE_KEY,
44 | data: updateData
45 | };
46 | };
47 |
48 | export const updateCell = (updateData) => {
49 | return {
50 | type: UPDATE_TABLE_VALUE,
51 | data: updateData
52 | };
53 | };
54 |
55 | export const reflectedDataToCommand = (latestData) => {
56 | return {
57 | type: REFLECTED_DATA,
58 | data: latestData
59 | }
60 | }
61 |
62 | export const removeValueFromTable = (index) => {
63 | return {
64 | type: REMOVE_VALUE_FROM_TABLE,
65 | data: index
66 | };
67 | };
68 |
69 | export const removeKeyFromTable = (key) => {
70 | return {
71 | type: REMOVE_KEY_FROM_TABLE,
72 | data: key
73 | };
74 | };
75 |
76 | export const addValueToData = (newData) => {
77 | return {
78 | type: ADD_VALUE_FROM_TABLE,
79 | data: newData
80 | };
81 | }
82 | export const addKeyToData = (newData) => {
83 | return {
84 | type: ADD_KEY_FROM_TABLE,
85 | data: newData
86 | };
87 | };
88 |
89 | export const updateCodeInput = (name, state) => {
90 | return {
91 | type: UPDATE_CODE_INPUT,
92 | name,
93 | };
94 | };
95 |
96 | export const updateData = (name, value, info) => {
97 | return {
98 | type: UPDATE_DATA,
99 | name,
100 | value,
101 | info
102 | };
103 | };
104 | export const recentConfigureUpdate = (configure) => ({
105 | type: RECENT_CONFIGURE,
106 | configure
107 | })
108 |
109 | export const changeGuiActivate = (name, value) => ({
110 | type: CHANGE_GUI_ACTIVATE,
111 | value: value.value,
112 | root: value.root,
113 | name
114 | });
115 |
116 |
117 | export const resetGui = (name, updated) => {
118 | return {
119 | type: RESET_GUI,
120 | updated,
121 | name
122 | };
123 | };
124 | export const updateGui = (name, updated) => ({
125 | type: UPDATE_GUI,
126 | name,
127 | updated
128 | });
129 |
130 | export const updateCommand = updated => ({
131 | type: UPDATE_COMMAND,
132 | updated
133 | });
134 |
135 | export const reflectCommandToDatatable = (updated) => {
136 | return {
137 | type: REFLECT_CODE_TO_DATATABLE,
138 | updated
139 | };
140 | };
141 |
--------------------------------------------------------------------------------
/src/app/configure/index.js:
--------------------------------------------------------------------------------
1 | import * as rawDocument from "./resources/document";
2 | import * as presetDocument from "./resources/preset";
3 | import { deepCopy, typeValid } from "../util";
4 | import * as template from "./resources/template";
5 | const keysFromDocument = [];
6 |
7 | const getDefaultAttributes = (name) => {
8 | return {
9 | description: "",
10 | optional: true,
11 | activated: false,
12 | name,
13 | value: undefined,
14 | defaultvalue: undefined,
15 | type: {
16 | names: ["Object"]
17 | },
18 | };
19 | };
20 |
21 | const fillDefaultAttributes = (target, root) => {
22 | target.attributes = target.attributes || getDefaultAttributes(root);
23 | if(target.properties){
24 | _.each(target.properties, (obj, key) => {
25 | target.properties[key] = fillDefaultAttributes(obj, `${root}.${key}`);
26 | });
27 | }
28 | return target;
29 | };
30 |
31 | const memberFlatten = (member) => {
32 | let newArray = [member];
33 | if (member.properties) {
34 | _.each(member.properties, (item) => {
35 | if(item.properties){
36 | newArray = newArray.concat(memberFlatten(item));
37 | } else {
38 | const value = typeValid(item.type.names.map(type => (type.toLowerCase())), item.defaultvalue);
39 | newArray.push({
40 | type: item.type,
41 | defaultvalue: value,
42 | value: value,
43 | name: item.name,
44 | description: item.description,
45 | optional: item.optional,
46 | activated: false,
47 | examples: item.examples,
48 | docid: item.name,
49 | });
50 | }
51 | });
52 | }
53 |
54 | return newArray;
55 | };
56 |
57 |
58 | const documentToObject = (defaultDocumentOption) => {
59 | let fullProperties = [];
60 |
61 | _.each(defaultDocumentOption, ({ id, examples, name, type, kind, defaultvalue, description, properties, optional }) => {
62 | if (kind === "member") {
63 | if (name.indexOf("․") > -1) {
64 | const target = {
65 | type: type,
66 | defaultvalue: defaultvalue,
67 | value: defaultvalue,
68 | docid: name,
69 | name: name.replace(/\․/g, "."),
70 | description: description,
71 | optional: optional,
72 | activated: false,
73 | examples,
74 | };
75 |
76 | fullProperties = fullProperties.concat([target]);
77 | } else {
78 | const ps = memberFlatten({
79 | value: defaultvalue,
80 | defaultvalue,
81 | properties,
82 | type,
83 | name,
84 | description,
85 | kind,
86 | activated: false,
87 | examples,
88 | docid: name,
89 | });
90 | fullProperties = fullProperties.concat(ps);
91 | }
92 | }
93 | });
94 |
95 | let member = {};
96 |
97 | _.each(fullProperties, (p) => {
98 | const keys = p.name.split(".");
99 | let copy = {
100 | attributes: p
101 | };
102 | _.each(keys.reverse(), (key) => {
103 | const newCopy = {
104 | properties: {}
105 | };
106 |
107 | newCopy.properties[key] = copy;
108 | copy = newCopy;
109 | });
110 | member = deepCopy(member, copy);
111 | keysFromDocument.push(p.name);
112 | });
113 |
114 | const newObj = {};
115 | _.each(Object.keys(member.properties), (memberKey) => {
116 | let target = member.properties[memberKey];
117 | target.kind = "member";
118 | target = fillDefaultAttributes(target, memberKey);
119 | newObj[memberKey] = deepCopy({}, target, presetDocument[memberKey]);
120 | });
121 |
122 | return newObj;
123 | };
124 |
125 | const getFormattedDocument = () => {
126 | return documentToObject(rawDocument);
127 | };
128 |
129 | const formattedDocument = getFormattedDocument();
130 | const initConfigure = template.line1;
131 |
132 | export {
133 | rawDocument as documentFromJSDoc,
134 | presetDocument as formattedCustomDocumnet,
135 | formattedDocument,
136 | initConfigure
137 | };
138 |
--------------------------------------------------------------------------------
/src/app/util/index.js:
--------------------------------------------------------------------------------
1 | export const namespaceToObject = (ns, lastValue, obj = {}) => {
2 | const keys = ns.split(".");
3 | obj[keys.pop()] = lastValue;
4 |
5 | keys.reverse().forEach(key => {
6 | const newObj = {};
7 | newObj[key] = obj;
8 | obj = newObj;
9 | });
10 |
11 | return obj;
12 | };
13 |
14 | export const deepCopy = (target, ...sources) => {
15 | const isObject = (item) =>{
16 | return (item && typeof item === "object" && !Array.isArray(item));
17 | };
18 |
19 | if (!sources.length) return target;
20 | const source = sources.shift();
21 |
22 | if (isObject(target) && isObject(source)) {
23 | for (const key in source) {
24 | if (isObject(source[key])) {
25 | if (!target[key]) Object.assign(target, { [key]: {} });
26 | deepCopy(target[key], source[key]);
27 | } else {
28 | Object.assign(target, { [key]: source[key] });
29 | }
30 | }
31 | }
32 |
33 | return deepCopy(target, ...sources);
34 | };
35 |
36 | export const stringToFunction = (str) =>{
37 | let code = str;
38 | eval(`code = ${code}`);
39 | return code;
40 | }
41 |
42 | export const objectFlatten = (obj) => {
43 | var flattenObject = function(ob) {
44 | var toReturn = {};
45 |
46 | for (var i in ob) {
47 | if (!ob.hasOwnProperty(i)) continue;
48 |
49 | if ((typeof ob[i]) == 'object' && !Array.isArray(ob[i])) {
50 | var flatObject = flattenObject(ob[i]);
51 | for (var x in flatObject) {
52 | if (!flatObject.hasOwnProperty(x)) continue;
53 |
54 | toReturn[i + '.' + x] = flatObject[x];
55 | }
56 | } else {
57 | toReturn[i] = ob[i];
58 | }
59 | }
60 | return toReturn;
61 | };
62 |
63 |
64 | return flattenObject(obj);
65 | };
66 |
67 |
68 | export const hasProperty = (name) => {
69 | const keys = name.split(".");
70 | let configure = initDocumentConfigure;
71 | let attr = {};
72 |
73 | _.each(keys, (key) => {
74 | if(configure !== undefined){
75 | const cof = configure[key] || {};
76 |
77 | attr = cof.attributes;
78 | configure = cof.properties;
79 | } else {
80 | attr = undefined;
81 | }
82 | });
83 |
84 | return !!attr;
85 | };
86 |
87 |
88 | export const objectToKeys = (obj, root = "") => {
89 | const keys = _.keys(obj);
90 | const arr = [];
91 |
92 | if(keys.length > 0){
93 | _.each(keys, key => {
94 | const parent = root == "" ? key : root + "." + key;
95 | if(hasProperty(parent)){
96 | arr.push(objectToKeys(obj[key], parent));
97 | } else {
98 | arr.indexOf(root) < 0 && arr.push(root);
99 | }
100 | });
101 | } else {
102 | arr.push(root);
103 | }
104 |
105 | return _.flatten(arr);
106 | };
107 |
108 |
109 | export const strinifyContainsFunction = (obj) => {
110 | function replacer(key, value) {
111 | if(typeof value == "function"){
112 | return `___codestart${value}codeend___`;
113 | }
114 | return value;
115 | }
116 | var text = JSON.stringify(obj, replacer);
117 | text = text.replace(/\"\_\_\_codestart/g, "").replace(/codeend\_\_\_\"/g, "");
118 |
119 | return text;
120 | }
121 |
122 |
123 | export const typeValid = (types, value) => {
124 | if(value === undefined || value === "undefined"){
125 | return undefined;
126 | }
127 |
128 | if(types.indexOf("number") > -1) {
129 | if(isNaN(value)){
130 | //console.log("TODO => " + value);
131 | return value;
132 | } else {
133 | return value*1;
134 | }
135 | }
136 |
137 | if(types.indexOf("array") > -1){
138 | const array = JSON.parse(value+="");
139 | return array;
140 | }
141 |
142 | if(types.indexOf("function") > -1){
143 | return value;
144 | }
145 |
146 | if(types.indexOf("boolean") > -1){
147 | return value;
148 | }
149 |
150 | if(types.indexOf("string") > -1){
151 | return value;
152 | }
153 |
154 | if(types.indexOf("object") > -1){
155 | return value;
156 | }
157 |
158 | return value;
159 |
160 | };
--------------------------------------------------------------------------------
/src/app/component/gui/property.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from "react";
2 | import { connect } from "react-redux";
3 | import getMuiTheme from "material-ui/styles/getMuiTheme";
4 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
5 | import { ListItem } from "material-ui/List";
6 | import FontIcon from "material-ui/FontIcon";
7 | import Clear from "./icon/clear";
8 | import {
9 | ConnectedCollection,
10 | ConnectedCheckbox,
11 | ConnectedNumber,
12 | ConnectedText,
13 | ConnectedCode
14 | } from "./input/index";
15 | import {
16 | resetGui, updateConfigureInfo, showGuideCard
17 | } from "../../actions";
18 | import * as color from "material-ui/styles/colors";
19 |
20 |
21 | class InputProperty extends React.Component {
22 | componentWillMount() {
23 | this.setState(this.props);
24 | }
25 |
26 | componentWillReceiveProps(nextProps) {
27 | this.setState(nextProps);
28 | }
29 |
30 | getName() {
31 | return (
36 |
37 |
{this.state.name}
38 | this.onClickTitle()} className="material-icons property_info">info_outline
39 |
40 |
{this.state.type.names.join(", ")}
41 |
42 |
);
43 | }
44 |
45 | getInput(type) {
46 | return (
51 |
52 | {InputProperty.getInputType(type, this.state)}
53 |
54 |
);
55 | }
56 |
57 | onClickTitle() {
58 | this.props.onClickTitle(this.state.name);
59 | }
60 |
61 | getClear() {
62 | return ();
77 | }
78 |
79 | render() {
80 | const option = this.state;
81 | const type = option.type ? option.type.names.join(", ") : "";
82 |
83 | const title = this.getName();
84 | const input = this.getInput(type);
85 | const clear = this.getClear();
86 | const className = `property ${(option.activated ? " activated" : "")}`;
87 |
88 | if(this.state.hidegui){
89 | return ;
90 | };
91 |
92 |
93 | return (
94 |
100 | );
101 | }
102 |
103 | onClickDelete(e) {
104 | this.props.onClickDelete(e);
105 | }
106 |
107 | static getInputType(type, option) {
108 |
109 | switch (type.toLocaleLowerCase()) {
110 | case "boolean" :
111 | return ;
112 | case "string" :
113 | return ;
114 | case "number" :
115 | return ;
116 | case "array" :
117 | return ""; // ;
118 | case "function" :
119 | return ;
120 | default :
121 | return "";
122 | }
123 | }
124 | }
125 |
126 | const mapDispatchToProps = (dispatch, ownProps) => ({
127 | onClickTitle : (name) => {
128 | dispatch(updateConfigureInfo(name));
129 | dispatch(showGuideCard(name));
130 | },
131 | onClickDelete: () => {
132 | dispatch(resetGui(ownProps.name.replace(/\:/g, "."), {
133 | root: ownProps.rootMemberName
134 | }))
135 | }
136 | });
137 |
138 | const Property = connect(
139 | null, mapDispatchToProps
140 | )(InputProperty);
141 |
142 | export default Property;
143 |
--------------------------------------------------------------------------------
/src/style/codepen-export.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/app/component/guide/card.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Card, CardActions, CardHeader, CardMedia, CardTitle, CardText} from 'material-ui/Card';
3 | import FlatButton from 'material-ui/FlatButton';
4 | import Toggle from 'material-ui/Toggle';
5 | import Chip from 'material-ui/Chip';
6 | import FontIcon from 'material-ui/FontIcon';
7 | import { connect } from "react-redux";
8 | import {
9 | showGuideCard, resetGui, updateConfigureInfo, hideGuideCard
10 | } from "../../actions";
11 | import CodeMirror from "../command/codemirror";
12 | import * as color from 'material-ui/styles/colors';
13 |
14 | const styles = {
15 | chip: {
16 | display: "inline-block",
17 | paddingBottom: "8px",
18 | paddingRight: "8px"
19 | },
20 | wrapper: {
21 | display: "flex",
22 | flexWrap: "wrap"
23 | },
24 |
25 | cardHeader: {
26 | color: color.grey800,
27 | padding: 0
28 | },
29 |
30 | cardText: {
31 | padding: "0",
32 | margin: "0 16px",
33 | marginBottom: "20px",
34 | borderTop : "1px solid #E0E0E0"
35 | }
36 | };
37 |
38 | class PropertyCard extends React.Component {
39 |
40 | componentDidMount() {
41 | PR && PR.prettyPrint();
42 | }
43 |
44 | componentDidUpdate() {
45 | if(this.pp){
46 | this.pp.className = "prettyprint";
47 | PR && PR.prettyPrint();
48 | }
49 | }
50 |
51 | onClickOpenDoc() {
52 | const url = `https://naver.github.io/billboard.js/release/latest/doc/Options.html#.${this.props.attributes.docid}`;
53 |
54 | window.open(url);
55 | }
56 |
57 | getActiveOption() {
58 | const closeIcon = ( this.props.onHideCard()} className="material-icons">close );
59 | const chip = this.props.chip;
60 | return (
61 |
66 | {chip.map(({name, optional}) => {
67 | if(optional){
68 | return
69 | this.props.onClickChip(name)} onRequestDelete={() => this.props.onClickDelete(name)}>{name}
70 |
71 | } else {
72 | return
73 | this.props.onClickChip(name)}>{name}
74 |
75 | }
76 |
77 | })}
78 |
79 |
84 | {closeIcon}
85 |
86 | );
87 | }
88 |
89 | getExample() {
90 | const example = this.props.attributes.examples;
91 |
92 | return example ? (
93 | example
94 | {this.pp = p;}} className="prettyprint">{example}
95 | ) : "";
96 | }
97 |
98 | getDescription() {
99 | return (
100 | description
101 | {this.props.attributes.description}
102 | );
103 | }
104 |
105 | render() {
106 | const openDocIcon = ( this.onClickOpenDoc()} className="material-icons">open_in_new );
107 | const attr = this.props.attributes;
108 | const name = attr.name;
109 | const type = attr.type.names.join(" | ");
110 | const defaultvalue = attr.defaultvalue;
111 |
112 | return (
113 | { this.wrapper = d; }}>
114 |
115 | {this.getActiveOption()}
116 |
122 |
123 |
124 | {this.getDescription()}
125 | {this.getExample()}
126 |
127 |
128 | );
129 | }
130 |
131 |
132 | }
133 |
134 | const mapStateToProps = state => ({
135 | chip: state.command.chip,
136 | name: state.guide.name,
137 | attributes: state.guide.attributes,
138 | });
139 |
140 | const mapDispatchToProps = (dispatch, ownProps) => ({
141 | onHideCard: () => {
142 | dispatch(hideGuideCard());
143 | },
144 | onClickChip : (name) => {
145 | dispatch(updateConfigureInfo(name));
146 | },
147 | onClickDelete: (name) => {
148 | dispatch(resetGui(name));
149 | },
150 | onUpdateCard : (heightStyle) => {
151 | dispatch(showGuideCard(heightStyle));
152 | }
153 | });
154 |
155 | const DocumentCard = connect(
156 | mapStateToProps, mapDispatchToProps
157 | )(PropertyCard);
158 |
159 |
160 | export {
161 | DocumentCard
162 | }
163 | export default DocumentCard;
164 |
165 |
166 |
--------------------------------------------------------------------------------
/src/app/component/command/codemirror.js:
--------------------------------------------------------------------------------
1 | /**
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2016 Jed Watson
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | **/
24 |
25 | 'use strict';
26 |
27 | var React = require('react');
28 | var ReactDOM = require('react-dom');
29 | var PropTypes = require('prop-types');
30 | var className = require('classnames');
31 | var debounce = require('lodash').debounce;
32 | var isEqual = require('lodash').isEqual;
33 | var createReactClass = require('create-react-class');
34 |
35 | function normalizeLineEndings(str) {
36 | if (!str) return str;
37 | return str.replace(/\r\n|\r/g, '\n');
38 | }
39 |
40 | var CodeMirror = createReactClass({
41 | propTypes: {
42 | autoFocus: PropTypes.bool,
43 | className: PropTypes.any,
44 | codeMirrorInstance: PropTypes.func,
45 | defaultValue: PropTypes.string,
46 | name: PropTypes.string,
47 | onChange: PropTypes.func,
48 | onCursorActivity: PropTypes.func,
49 | onFocusChange: PropTypes.func,
50 | onScroll: PropTypes.func,
51 | options: PropTypes.object,
52 | path: PropTypes.string,
53 | value: PropTypes.string,
54 | preserveScrollPosition: PropTypes.bool
55 | },
56 | getDefaultProps: function getDefaultProps() {
57 | return {
58 | preserveScrollPosition: false
59 | };
60 | },
61 | getCodeMirrorInstance: function getCodeMirrorInstance() {
62 | return this.props.codeMirrorInstance || require('codemirror');
63 | },
64 | getInitialState: function getInitialState() {
65 | return {
66 | isFocused: false
67 | };
68 | },
69 | componentWillMount: function componentWillMount() {
70 | this.componentWillReceiveProps = debounce(this.componentWillReceiveProps, 0);
71 | if (this.props.path) {
72 | console.error('Warning: react-codemirror: the `path` prop has been changed to `name`');
73 | }
74 | },
75 | componentDidMount: function componentDidMount() {
76 | var codeMirrorInstance = this.getCodeMirrorInstance();
77 | this.codeMirror = codeMirrorInstance.fromTextArea(this.textareaNode, this.props.options);
78 |
79 | this.codeMirror.on('change', this.codemirrorValueChanged);
80 | this.codeMirror.on('cursorActivity', this.cursorActivity);
81 | this.codeMirror.on('focus', this.focusChanged.bind(this, true));
82 | this.codeMirror.on('blur', this.focusChanged.bind(this, false));
83 | this.codeMirror.on('scroll', this.scrollChanged);
84 | this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
85 | },
86 | componentWillUnmount: function componentWillUnmount() {
87 | // is there a lighter-weight way to remove the cm instance?
88 | if (this.codeMirror) {
89 | this.codeMirror.toTextArea();
90 | }
91 | },
92 | componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
93 | if (this.codeMirror && nextProps.value !== undefined && nextProps.value !== this.props.value && normalizeLineEndings(this.codeMirror.getValue()) !== normalizeLineEndings(nextProps.value)) {
94 | if (this.props.preserveScrollPosition) {
95 | var prevScrollPosition = this.codeMirror.getScrollInfo();
96 | this.codeMirror.setValue(nextProps.value);
97 | this.codeMirror.scrollTo(prevScrollPosition.left, prevScrollPosition.top);
98 | } else {
99 | this.codeMirror.setValue(nextProps.value);
100 | }
101 | } else {
102 | if(this.state.origin === "setValue" || this.state.isFocused === false){
103 | this.codeMirror.setValue(nextProps.value);
104 | }
105 | }
106 | if (typeof nextProps.options === 'object') {
107 | for (var optionName in nextProps.options) {
108 | if (nextProps.options.hasOwnProperty(optionName)) {
109 | this.setOptionIfChanged(optionName, nextProps.options[optionName]);
110 | }
111 | }
112 | }
113 | },
114 | setOptionIfChanged: function setOptionIfChanged(optionName, newValue) {
115 | var oldValue = this.codeMirror.getOption(optionName);
116 | if (!isEqual(oldValue, newValue)) {
117 | this.codeMirror.setOption(optionName, newValue);
118 | }
119 | },
120 | getCodeMirror: function getCodeMirror() {
121 | return this.codeMirror;
122 | },
123 | focus: function focus() {
124 | if (this.codeMirror) {
125 | this.codeMirror.focus();
126 | }
127 | },
128 | focusChanged: function focusChanged(focused) {
129 | this.setState({
130 | isFocused: focused
131 | });
132 | this.props.onFocusChange && this.props.onFocusChange(focused);
133 | },
134 | cursorActivity: function cursorActivity(cm) {
135 | this.props.onCursorActivity && this.props.onCursorActivity(cm);
136 | },
137 | scrollChanged: function scrollChanged(cm) {
138 | this.props.onScroll && this.props.onScroll(cm.getScrollInfo());
139 | },
140 | codemirrorValueChanged: function codemirrorValueChanged(doc, change) {
141 | this.setState({
142 | origin: change.origin
143 | });
144 | if (this.props.onChange && change.origin !== 'setValue') {
145 | this.props.onChange(doc.getValue(), change);
146 | }
147 | },
148 | render: function render() {
149 | var _this = this;
150 |
151 | var editorClassName = className('ReactCodeMirror', this.state.isFocused ? 'ReactCodeMirror--focused' : null, this.props.className);
152 | return React.createElement(
153 | 'div',
154 | { className: editorClassName },
155 | React.createElement('textarea', {
156 | ref: function (ref) {
157 | return _this.textareaNode = ref;
158 | },
159 | name: this.props.name || this.props.path,
160 | value: this.props.value,
161 | onChange: this.props.onChange,
162 | autoComplete: 'off',
163 | autoFocus: this.props.autoFocus
164 | })
165 | );
166 | }
167 | });
168 |
169 | module.exports = CodeMirror;
170 |
--------------------------------------------------------------------------------
/src/app/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 | import {
3 | namespaceToObject,
4 | deepCopy,
5 | objectFlatten
6 | } from "../util";
7 |
8 | import {
9 | formattedDocument as initDocument,
10 | initConfigure
11 | } from "../configure";
12 |
13 | import {
14 | HIDE_GUIDE_CARD,
15 | SHOW_GUIDE_CARD,
16 | UPDATE_GUI,
17 | RESET_GUI,
18 | REFLECTED_DATA,
19 | UPDATE_CODE_INPUT,
20 | UPDATE_COMMAND,
21 | UPDATE_CONFIGURE_INFO,
22 | UPDATE_TABLE_KEY,
23 | UPDATE_TABLE_VALUE,
24 | REMOVE_VALUE_FROM_TABLE,
25 | REMOVE_KEY_FROM_TABLE,
26 | ADD_VALUE_FROM_TABLE,
27 | ADD_KEY_FROM_TABLE
28 | } from "../actions";
29 |
30 | import {
31 | getDefaultValue,
32 | updateGuiState,
33 | changeDefaultGuiState,
34 | updateGuiActivate,
35 | resetDataState,
36 | getChip,
37 | removeValueToDataState,
38 | removeKeyToDataState,
39 | addValueToDataTable,
40 | addKeyToDataTable,
41 | updateDataKeyState,
42 | updateDataValueState,
43 | updateCommandByGUI,
44 | getRemovedToDefault,
45 | updateCommandData,
46 | updateCommandCode,
47 | resetCommandState,
48 | updateCommandState,
49 | deleteTargetCommandState,
50 | getAttributesFromDocument,
51 | convertData
52 | } from "./logic";
53 |
54 |
55 | /** state variable **/
56 | let guiState = initDocument;
57 | let dataState = convertData(initConfigure.data);
58 | let commandState = {
59 | original: initConfigure,
60 | text: JSON.stringify(initConfigure),
61 | chip: getChip(Object.keys(objectFlatten(initConfigure)))
62 | };
63 | let guideState = {
64 | open: false,
65 | name: null,
66 | attributes: getAttributesFromDocument("bindto")
67 | };
68 |
69 |
70 | /** gui action handler **/
71 | const gui = (state = guiState, action) => {
72 | let returnState = {};
73 |
74 | switch (action.type) {
75 | case UPDATE_GUI : {
76 | const latest = namespaceToObject(action.name, action.updated.value);
77 |
78 | returnState = updateGuiState(state, latest);
79 |
80 | returnState.lastUpdateRoot = [action.name.split(".")[0]];
81 | returnState.lastUpdate = (new Date()).getTime();
82 |
83 | break;
84 | }
85 | case RESET_GUI : {
86 | const value = getDefaultValue(action.name);
87 | const latest = namespaceToObject(action.name, value);
88 |
89 | returnState = updateGuiState(state, latest);
90 |
91 | returnState.lastUpdateRoot = [action.name.split(".")[0]];
92 | returnState.lastUpdate = (new Date()).getTime();
93 |
94 | break;
95 | }
96 |
97 | case UPDATE_CODE_INPUT : {
98 | returnState = updateGuiActivate(state, action.name);
99 |
100 | returnState.lastUpdateRoot = [action.name.split(".")[0]];
101 | returnState.lastUpdate = (new Date()).getTime();
102 |
103 | break;
104 | }
105 |
106 | case UPDATE_COMMAND : {
107 | const prev = action.updated.prev;
108 | const current = action.updated.value;
109 | const resets = getRemovedToDefault(prev, current);
110 |
111 | returnState = updateGuiState(state, current);
112 | returnState = updateGuiState(returnState, resets);
113 |
114 | returnState.lastUpdateRoot = Object.keys(current).concat(Object.keys(resets));
115 | returnState.lastUpdate = (new Date()).getTime();
116 |
117 | break;
118 | }
119 | default :
120 | returnState = state;
121 | }
122 |
123 | guiState = returnState;
124 | return returnState;
125 | };
126 |
127 | const command = (state = commandState, action) => {
128 | let returnState;
129 |
130 | switch (action.type) {
131 | case UPDATE_GUI : {
132 | returnState = updateCommandByGUI(state, action.name, action.updated.value);
133 | returnState.chip = getChip(Object.keys(objectFlatten(returnState.original)));
134 | returnState.lastUpdate = new Date();
135 | break;
136 | }
137 |
138 | case RESET_GUI : {
139 | returnState = deleteTargetCommandState(state, action.name);
140 | returnState.chip = getChip(Object.keys(objectFlatten(returnState.original)));
141 | returnState.lastUpdate = new Date();
142 | break;
143 | }
144 |
145 | case UPDATE_CODE_INPUT : {
146 | returnState = updateCommandCode(state, action.name);
147 | returnState.chip = getChip(Object.keys(objectFlatten(returnState.original)));
148 | returnState.lastUpdate = new Date();
149 | break;
150 | }
151 |
152 | case UPDATE_COMMAND : {
153 | returnState = resetCommandState(action.updated.value);
154 | returnState.chip = getChip(Object.keys(objectFlatten(returnState.original)));
155 | returnState.lastUpdate = new Date();
156 | break;
157 | }
158 |
159 | case REFLECTED_DATA : {
160 | const lastedData = deepCopy({}, action.data);
161 | returnState = updateCommandData(state, lastedData);
162 | returnState.chip = getChip(Object.keys(objectFlatten(returnState.original)));
163 | returnState.fromData = true;
164 | returnState.lastUpdate = new Date()
165 | break;
166 | }
167 |
168 | default :
169 | returnState = state;
170 | }
171 |
172 |
173 | commandState = returnState;
174 |
175 | return returnState;
176 | };
177 |
178 |
179 | const data = (state = dataState, action) => {
180 | let returnState;
181 |
182 | switch (action.type) {
183 | case UPDATE_COMMAND : {
184 | const newData = convertData(action.updated.value.data);
185 | returnState = resetDataState(state, newData);
186 | returnState.lastUpdate = new Date();
187 |
188 | break;
189 | }
190 | case UPDATE_TABLE_VALUE : {
191 | returnState = updateDataValueState(state, action.data);
192 | returnState.lastUpdate = new Date();
193 |
194 | break;
195 | }
196 | case UPDATE_TABLE_KEY : {
197 | returnState = updateDataKeyState(state, action.data);
198 | returnState.lastUpdate = new Date();
199 |
200 | break;
201 | }
202 | case REMOVE_VALUE_FROM_TABLE : {
203 | returnState = removeValueToDataState(state, action.data);
204 | returnState.lastUpdate = new Date();
205 |
206 | break;
207 | }
208 | case REMOVE_KEY_FROM_TABLE : {
209 | returnState = removeKeyToDataState(state, action.data);
210 | returnState.lastUpdate = new Date();
211 |
212 | break;
213 | }
214 | case ADD_VALUE_FROM_TABLE : {
215 | returnState = addValueToDataTable(state, action.data);
216 | returnState.lastUpdate = new Date();
217 |
218 | break;
219 | }
220 | case ADD_KEY_FROM_TABLE : {
221 | returnState = addKeyToDataTable(state, action.data);
222 | returnState.lastUpdate = new Date();
223 |
224 | break;
225 | }
226 |
227 | default :
228 | returnState = state;
229 | }
230 |
231 | dataState = returnState;
232 |
233 | return returnState;
234 | };
235 |
236 | const guide = (state = guideState, action) => {
237 | let returnState = {};
238 |
239 | switch (action.type) {
240 | case UPDATE_CONFIGURE_INFO : {
241 | const name = action.name;
242 | const attributes = getAttributesFromDocument(name);
243 | returnState = {
244 | name,
245 | attributes
246 | };
247 | returnState.lastUpdate = (new Date()).getTime();
248 | break;
249 | }
250 | case HIDE_GUIDE_CARD : {
251 | returnState.open = false;
252 | returnState.lastUpdate = (new Date()).getTime();
253 | break;
254 | }
255 | case SHOW_GUIDE_CARD : {
256 | returnState.open = true;
257 | returnState.lastUpdate = (new Date()).getTime();
258 | break;
259 | }
260 | default :
261 | returnState = state;
262 | }
263 |
264 | returnState = deepCopy({}, guideState, returnState);
265 | guideState = returnState;
266 |
267 | return returnState;
268 | };
269 |
270 | const playgroundApp = combineReducers({
271 | gui, command, data, guide
272 | });
273 |
274 | export default playgroundApp;
275 |
--------------------------------------------------------------------------------
/src/app/component/data/index.jsx:
--------------------------------------------------------------------------------
1 | import TextField from "material-ui/TextField";
2 | import React, { PropTypes } from "react";
3 | import FontIcon from "material-ui/FontIcon";
4 | import { connect } from "react-redux";
5 | import {
6 | Table,
7 | TableBody,
8 | TableHeader,
9 | TableHeaderColumn,
10 | TableRow,
11 | TableRowColumn
12 | } from "material-ui/Table";
13 | import * as _ from "lodash";
14 | import getMuiTheme from "material-ui/styles/getMuiTheme";
15 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
16 | import {
17 | addKeyToData,
18 | addValueToData,
19 | removeValueFromTable,
20 | removeKeyFromTable,
21 | reflectedDataToCommand,
22 | updateHeader,
23 | updateCell
24 | } from "../../actions";
25 |
26 | const cellStyle = {
27 | width: "50px",
28 | padding: "0 5"
29 | };
30 |
31 | const tableProps = {
32 | bodyStyle: {
33 | overflowX: "initial",
34 | overflowY: "initial"
35 | },
36 | selectable: false,
37 | multiSelectable: false
38 | };
39 |
40 | const tableHeaderProps = {
41 | displaySelectAll: false,
42 | adjustForCheckbox: false
43 | };
44 |
45 | const textFieldProps = {
46 | fullWidth: true,
47 | underlineStyle: {
48 | borderColor: "transparent"
49 | },
50 | underlineFocusStyle: {
51 | borderColor: "transparent"
52 | },
53 | inputStyle: {
54 | textAlign: "center"
55 | }
56 | };
57 |
58 | class DataTableController extends React.Component {
59 | componentDidUpdate() {
60 | this.props.reflectedData(this.props.data);
61 | }
62 |
63 | onClickDelete(deleteType, targetInfo) {
64 | if (deleteType === "column") {
65 | this.props.deleteKey(targetInfo);
66 | }
67 |
68 | if (deleteType === "row") {
69 | this.props.deleteData(targetInfo);
70 | }
71 | }
72 |
73 | onClickAdd(addType) {
74 | const headers = this.props.data.header;
75 | const rows = this.props.data.body.concat([]);
76 | let data = {};
77 |
78 | if (addType === "column") {
79 | const newLength = rows.length;
80 | const newKey = `data${headers.length + 1}`;
81 | const value = Array(...Array(newLength)).map(() => parseInt((Math.random() * 100), 10));
82 | data = {
83 | name: newKey,
84 | value
85 | };
86 | this.props.addKey(data);
87 | } else if (addType === "row") {
88 | headers.forEach((header) => {
89 | data[header] = parseInt((Math.random() * 100), 10);
90 | });
91 | this.props.addData(data);
92 | }
93 | }
94 |
95 | getTextField(value, rowIndex, columnIndex) {
96 | const header = this.props.data.header[columnIndex - 1];
97 | return ( { this.props.onChangeData(v, rowIndex, header); }}
101 | {...textFieldProps}
102 | />);
103 | }
104 |
105 | getRemoveButton(deleteType, targetInfo) {
106 | return (
107 | this.onClickDelete(deleteType, targetInfo)} >
110 | remove_circle
111 |
112 |
);
113 | }
114 |
115 | getAddButton(addType) {
116 | return (
117 | this.onClickAdd(addType)} className="material-icons">
118 | add_circle
119 |
120 |
);
121 | }
122 |
123 | getHeaderTextField(headerKey, columnIndex) {
124 | const removeButton = this.getRemoveButton("column", headerKey);
125 | return (
126 | {removeButton}
127 | { this.props.onChangeHeader(value, columnIndex - 1); }}
131 | {...textFieldProps}
132 | />
133 |
);
134 | }
135 |
136 | getHeader(keys) {
137 | const headers = keys.concat([]);
138 | headers.unshift(""); // for delete button column
139 | headers.push(""); // for add button column
140 |
141 | const tc = _.map(headers, (headerKey, columnIndex) => {
142 | let inner;
143 | if (columnIndex === 0) {
144 | inner = "";
145 | } else if (columnIndex < headers.length - 1) {
146 | inner = this.getHeaderTextField(headerKey, columnIndex);
147 | } else {
148 | inner = this.getAddButton("column");
149 | }
150 | return ({inner} );
151 | });
152 |
153 | return (
154 |
155 | {tc}
156 |
157 | );
158 | }
159 |
160 | getBody(tableData) {
161 | return (
162 | {tableData.map((row, rowIndex) => (
163 | {this.getBodyRow(row, rowIndex)}
164 | ))}
165 |
166 |
167 | {this.getAddButton("row")}
168 |
169 |
170 | );
171 | }
172 |
173 | getBodyRow(row, rowIndex) {
174 | const values = this.props.data.header.map(header => row[header]);
175 | values.unshift("");
176 | values.push("");
177 |
178 | return _.map(values, (value, columnIndex) => {
179 | let inner;
180 | if (columnIndex === 0) {
181 | inner = this.getRemoveButton("row", rowIndex);
182 | } else if (columnIndex < values.length - 1) {
183 | inner = this.getTextField(value, rowIndex, columnIndex);
184 | } else {
185 | inner = "";
186 | }
187 | return ({inner} );
188 | });
189 | }
190 |
191 | render() {
192 | const data = this.props.data;
193 | const header = this.getHeader(data.header);
194 | const body = this.getBody(data.body);
195 |
196 | return (
197 |
198 |
199 | {header}
200 | {body}
201 |
202 |
203 |
);
204 | }
205 | }
206 |
207 |
208 | const mapDispatchToProps = dispatch => ({
209 | onChangeHeader: (value, columnIndex) => {
210 | dispatch(updateHeader({
211 | value,
212 | column: columnIndex
213 | }));
214 | },
215 | onChangeData: (value, rowIndex, header) => {
216 | dispatch(updateCell({
217 | value,
218 | header,
219 | row: rowIndex
220 | }));
221 | },
222 | reflectedData: (latest) => {
223 | dispatch(reflectedDataToCommand(latest));
224 | },
225 | deleteData: (rowIndex) => {
226 | dispatch(removeValueFromTable(rowIndex));
227 | },
228 | deleteKey: (columnKey) => {
229 | dispatch(removeKeyFromTable(columnKey));
230 | },
231 | addKey: (newDataObject) => {
232 | dispatch(addKeyToData(newDataObject));
233 | },
234 | addData: (newDataObject) => {
235 | dispatch(addValueToData(newDataObject));
236 | }
237 | });
238 |
239 | DataTableController.defaultProps = {
240 | data: {
241 | header: [],
242 | body: []
243 | }
244 | };
245 |
246 | DataTableController.propTypes = {
247 | onChangeHeader: PropTypes.func.isRequired,
248 | onChangeData: PropTypes.func.isRequired,
249 | addKey: PropTypes.func.isRequired,
250 | addData: PropTypes.func.isRequired,
251 | deleteKey: PropTypes.func.isRequired,
252 | deleteData: PropTypes.func.isRequired,
253 | reflectedData: PropTypes.func.isRequired,
254 | data: PropTypes.shape({
255 | header: PropTypes.arrayOf(PropTypes.string),
256 | body: PropTypes.arrayOf(PropTypes.object)
257 | })
258 | };
259 |
260 | const mapStateToProps = state => ({
261 | data: state.data
262 | });
263 |
264 | const Data = connect(mapStateToProps, mapDispatchToProps)(DataTableController);
265 |
266 | export {
267 | Data
268 | };
269 | export default Data;
270 |
--------------------------------------------------------------------------------
/src/style/app.scss:
--------------------------------------------------------------------------------
1 | /** app.scss **/
2 | html {
3 | padding : 10px;
4 | }
5 |
6 | #app {
7 | border: 1px solid #d7d7d7;
8 | position: relative;
9 | height: 100%;
10 | box-sizing: border-box;
11 | }
12 |
13 | #wrapper {
14 | height: 100%;
15 | }
16 |
17 |
18 | .ReactCodeMirror {
19 | width: 100%;
20 | height: auto;
21 | position : absolute;
22 | }
23 |
24 | .guide_card .ReactCodeMirror{
25 | width: auto;
26 | height: auto;
27 | position: relative;
28 | }
29 |
30 | .muidocs-icon-custom-github:before {
31 | content: "github";
32 | }
33 |
34 |
35 | .CodeMirror {
36 | width: 100%;
37 | height: 100%;
38 | }
39 |
40 | .type {
41 | color: #009688;
42 | }
43 | .err {
44 | background: #ffd9d9;
45 | }
46 |
47 | .bbtitle {
48 | width : 60%;
49 | height : 10%;
50 | margin : 0;
51 | left : 0;
52 | text-align: center;
53 | font-family: 'Roboto', sans-serif;
54 | font-size: 25px;
55 | }
56 |
57 | .bbtitle .logo {
58 | display: inline-block;
59 | width: 145px;
60 | height: 33px;
61 | margin-left : 10px;
62 | background-repeat :no-repeat;
63 | position: relative;
64 | top: 9px;
65 | box-sizing: content-box;
66 | background-color: #fff;
67 | border-style: none;
68 | }
69 |
70 | .chart {
71 | width : 60%;
72 | height : 40%;
73 | margin : 0;
74 | left : 0;
75 | }
76 |
77 | .tabviews {
78 | width : 60%;
79 | height : 50%;
80 | margin : 0;
81 | left : 0;
82 | }
83 |
84 | .textConfigure, .data-table {
85 | width : 100%;
86 | }
87 |
88 | .data-table .remove-data {
89 | text-align: center;
90 | opacity:0;
91 | }
92 |
93 | .data-table .add-data {
94 | position: relative;
95 | text-align: center;
96 | }
97 |
98 | .data-table .add-data.column{
99 | top: 33px;
100 | }
101 |
102 | .data-table .add-data .row{
103 | top: 3px;
104 | }
105 |
106 | .data-table .header-text:hover .remove-data{
107 | opacity:1;
108 | }
109 | .data-table .table-row:hover .remove-data {
110 | opacity:1;
111 | }
112 |
113 | .layer_wrapper {
114 | margin: 10px;
115 | }
116 |
117 | .bb {
118 | height : 100%;
119 | }
120 |
121 | .inputConfigure {
122 | width : 40%;
123 | left : 60%;
124 | position : absolute;
125 | height : 100%;
126 | top : 0;
127 | border-left: 1px solid #d7d7d7;
128 | box-sizing: border-box;
129 | }
130 | .scroll {
131 | width : 100%;
132 | height : 100%;
133 | overflow-y: scroll;
134 | }
135 |
136 | .property_clear {
137 | opacity:0;
138 | }
139 |
140 | .property.activated .property_clear{
141 | opacity: 1;
142 | }
143 |
144 | .property_content {
145 | display : inline-block;
146 | }
147 |
148 |
149 | .property:hover .edit_function {
150 | opacity: 1;
151 | }
152 |
153 |
154 | .property.updated {
155 | color : red;
156 | }
157 |
158 | .property.deactivated {
159 | opacity: 0.5;
160 | }
161 |
162 | .activate_check {
163 | //opacity:0;
164 | }
165 |
166 | .property.deactivated .activate_check{
167 | opacity: 0;
168 | }
169 |
170 | .property:hover .activate_check{
171 | opacity:1;
172 | }
173 |
174 | .react-toolbox_checkbox {
175 | display: inline-block;
176 | }
177 |
178 | .property_name {
179 | color : #424242;
180 | font-weight : 500;
181 | }
182 |
183 | .property .material-icons {
184 | font-size:20px;
185 | padding-left:10px;
186 | }
187 |
188 | .property .property_info {
189 | top: 6px;
190 | padding-left: 12px;
191 | }
192 |
193 | .property .property_info, .property .edit_code_property {
194 | opacity:0;
195 | }
196 |
197 | .property:hover .property_info, .property:hover .edit_code_property {
198 | opacity:1;
199 | }
200 |
201 | .property_type {
202 | font-size: 14px;
203 | line-height: 16px;
204 | height: 16px;
205 | margin: 0;
206 | margin-top: 4px;
207 | color: rgba(0, 0, 0, 0.54);
208 | overflow: hidden;
209 | text-overflow: ellipsis;
210 | white-space: nowrap;
211 | }
212 |
213 | .member svg {
214 | color : #ff8a65 !important;
215 | }
216 |
217 | .number_guide {
218 | font-size: 14px;
219 | line-height: 24px;
220 | font-family: Roboto, sans-serif;
221 | text-align : center;
222 |
223 | }
224 |
225 | .codepen-export {
226 | position: absolute;
227 | right : 60px;
228 | z-index: 1000;
229 | }
230 | .codepen-export input[type="submit"] {
231 | border : none;
232 | width: 60px;
233 | height : 40px;
234 | background: none;
235 | display: inline-block;
236 | }
237 | .codepen-export .button {
238 | background-size: contain !important;
239 | border : none ;
240 | width: 60px !important;
241 | height : 40px !important;
242 | background-image : url(codepen-export.svg) !important;
243 | }
244 | .drawer_close {
245 | transform : translate(-10px, 200px) !important;
246 |
247 | }
248 | .drawer_open {
249 | transform : translate(-10px, -10px) !important;
250 | }
251 |
252 | .table-row:hover .table-row-column{
253 | opacity:1;
254 | }
255 | .table-row-column {
256 | opacity:0;
257 | }
258 |
259 | /** app.css **/
260 |
261 | .guide_card .example {
262 | padding: 10px !important;
263 | background: #F5F5F5;
264 | font-size: 13px;
265 | }
266 |
267 | .guide_card {
268 | }
269 |
270 | .guide_card .text_title {
271 | //color : #424242;
272 | font-weight: 500;
273 | margin-bottom: 9px;
274 | margin-top: 22px;
275 | font-family: 'Roboto', sans-serif;
276 | }
277 | .text_content {
278 | //color : #424242;
279 | font-weight: 400;
280 | font-family: 'Roboto', sans-serif;
281 | }
282 |
283 |
284 | /*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
285 | .prettyprint {
286 | font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace;
287 | border: 0 !important;
288 | }
289 |
290 | .pln {
291 | color: #333;
292 | }
293 |
294 | /* Specify class=linenums on a pre to get line numbering */
295 | ol.linenums {
296 | margin-top: 0;
297 | margin-bottom: 0;
298 | color: #cccccc;
299 | }
300 |
301 | li.L0,
302 | li.L1,
303 | li.L2,
304 | li.L3,
305 | li.L4,
306 | li.L5,
307 | li.L6,
308 | li.L7,
309 | li.L8,
310 | li.L9 {
311 | padding-left: 1em;
312 | background-color: #fff;
313 | list-style-type: decimal;
314 | }
315 |
316 | @media screen {
317 | /* string content */
318 |
319 | .str {
320 | color: #d14;
321 | }
322 |
323 | /* keyword */
324 |
325 | .kwd {
326 | color: #333;
327 | }
328 |
329 | /* comment */
330 |
331 | .com {
332 | color: #998;
333 | }
334 |
335 | /* type name */
336 |
337 | .typ {
338 | color: #458;
339 | }
340 |
341 | /* literal value */
342 |
343 | .lit {
344 | color: #458;
345 | }
346 |
347 | /* punctuation */
348 |
349 | .pun {
350 | color: #333;
351 | }
352 |
353 | /* lisp open bracket */
354 |
355 | .opn {
356 | color: #333;
357 | }
358 |
359 | /* lisp close bracket */
360 |
361 | .clo {
362 | color: #333;
363 | }
364 |
365 | /* markup tag name */
366 |
367 | .tag {
368 | color: #000080;
369 | }
370 |
371 | /* markup attribute name */
372 |
373 | .atn {
374 | color: #008080;
375 | }
376 |
377 | /* markup attribute value */
378 |
379 | .atv {
380 | color: #d14;
381 | }
382 |
383 | /* declaration */
384 |
385 | .dec {
386 | color: #333;
387 | }
388 |
389 | /* variable name */
390 |
391 | .var {
392 | color: #008080;
393 | }
394 |
395 | /* function name */
396 |
397 | .fun {
398 | color: #900;
399 | }
400 | }
--------------------------------------------------------------------------------
/dist/resources/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/app/configure/resources/preset.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | regions: {
3 | attributes: {
4 | customkeys: [{
5 | name: "axis",
6 | type: "string"
7 | }, {
8 | name: "start",
9 | type: "number"
10 | }, {
11 | name: "end",
12 | type: "number"
13 | }, {
14 | name: "class",
15 | type: "string"
16 | }]
17 | }
18 | },
19 | size: {
20 | properties: {
21 | width: {
22 | attributes: {
23 | min: 0,
24 | max: 1000,
25 | step: 1
26 | }
27 | },
28 | height: {
29 | attributes: {
30 | min: 0,
31 | max: 1000,
32 | step: 1
33 | }
34 | }
35 | }
36 | },
37 | padding: {
38 | properties: {
39 | top: {
40 | attributes: {
41 | min: -1000,
42 | max: 1000,
43 | step: 1
44 | }
45 | },
46 | right: {
47 | attributes: {
48 | min: -1000,
49 | max: 1000,
50 | step: 1
51 | }
52 | },
53 | bottom: {
54 | attributes: {
55 | min: -1000,
56 | max: 1000,
57 | step: 1
58 | }
59 | },
60 | left: {
61 | attributes: {
62 | min: -1000,
63 | max: 1000,
64 | step: 1
65 | }
66 | }
67 | }
68 | },
69 | transition: {
70 | properties: {
71 | duration: {
72 | attributes: {
73 | min: 0,
74 | max: 1000,
75 | step: 1
76 | }
77 | }
78 | }
79 | },
80 | data: {
81 | properties: {
82 | columns: {
83 | attributes: {
84 | hidegui: true
85 | }
86 | },
87 | rows: {
88 | attributes: {
89 | hidegui: true
90 | }
91 | },
92 | json: {
93 | attributes: {
94 | hidegui: true
95 | }
96 | },
97 | url: {
98 | attributes: {
99 | hidegui: true
100 | }
101 | },
102 | type: {
103 | attributes: {
104 | valueoptions: ["line", "spline", "step", "area", "area-spline", "area-step", "bar", "bubble", "scatter", "pie", "donut", "gauge"]
105 | }
106 | }
107 | }
108 | },
109 | axis: {
110 | properties: {
111 | y: {
112 | properties: {
113 | tick: {
114 | properties: {
115 | count: {
116 | attributes: {
117 | min: -500,
118 | max: 500,
119 | step: 1
120 | }
121 | }
122 | }
123 | },
124 | center: {
125 | attributes: {
126 | min: -500,
127 | max: 500,
128 | step: 1
129 | }
130 | },
131 | max: {
132 | attributes: {
133 | min: -500,
134 | max: 500,
135 | step: 1
136 | }
137 | },
138 | min: {
139 | attributes: {
140 | min: -500,
141 | max: 500,
142 | step: 1
143 | }
144 | }
145 | }
146 | },
147 | y2: {
148 | properties: {
149 | tick: {
150 | properties: {
151 | count: {
152 | attributes: {
153 | min: -500,
154 | max: 500,
155 | step: 1
156 | }
157 | }
158 | }
159 | },
160 | center: {
161 | attributes: {
162 | min: -500,
163 | max: 500,
164 | step: 1
165 | }
166 | },
167 | max: {
168 | attributes: {
169 | min: -500,
170 | max: 500,
171 | step: 1
172 | }
173 | },
174 | min: {
175 | attributes: {
176 | min: -500,
177 | max: 500,
178 | step: 1
179 | }
180 | }
181 | }
182 | },
183 | x: {
184 | properties: {
185 | height: {
186 | attributes: {
187 | min: 0,
188 | max: 500,
189 | step: 1
190 | }
191 | },
192 | tick: {
193 | properties: {
194 | width: {
195 | attributes: {
196 | min: 0,
197 | max: 100,
198 | step: 1
199 | }
200 | },
201 | rotate: {
202 | attributes: {
203 | min: -100,
204 | max: 100,
205 | step: 1
206 | }
207 | },
208 | count: {
209 | attributes: {
210 | min: 0,
211 | max: 100,
212 | step: 1
213 | }
214 | },
215 | culling: {
216 | properties: {
217 | max: {
218 | attributes: {
219 | min: 0,
220 | max: 100,
221 | step: 1
222 | }
223 | }
224 | }
225 | }
226 | }
227 | },
228 | type: {
229 | attributes: {
230 | valueoptions: ["timeseries", "category", "indexed"]
231 | }
232 | },
233 | max: {
234 | attributes: {
235 | min: -500,
236 | max: 500,
237 | step: 1
238 | }
239 | },
240 | min: {
241 | attributes: {
242 | min: -500,
243 | max: 500,
244 | step: 1
245 | }
246 | }
247 | }
248 | }
249 | }
250 | },
251 | bubble: {
252 | properties: {
253 | maxR: {
254 | attributes: {
255 | min: 0,
256 | max: 100,
257 | step: 1
258 | }
259 | }
260 | }
261 | },
262 | point: {
263 | properties: {
264 | select: {
265 | properties: {
266 | r: {
267 | attributes: {
268 | custominput: true
269 | }
270 | }
271 | }
272 | },
273 | focus: {
274 | properties: {
275 | expand: {
276 | properties: {
277 | r: {
278 | attributes: {
279 | custominput: true
280 | }
281 | }
282 | }
283 | }
284 | }
285 | },
286 | r: {
287 | attributes: {
288 | min: 0,
289 | max: 50,
290 | step: 0.1
291 | }
292 | }
293 | }
294 | },
295 | gauge: {
296 | properties: {
297 | startingAngle: {
298 | attributes: {
299 | custominput: true
300 | }
301 | },
302 | max: {
303 | attributes: {
304 | min: 0,
305 | max: 1000,
306 | step: 1
307 | }
308 | },
309 | expand: {
310 | properties: {
311 | duration: {
312 | attributes: {
313 | min: 0,
314 | max: 100,
315 | step: 1
316 | }
317 | }
318 | }
319 | }
320 | }
321 | },
322 | legend: {
323 | properties: {
324 | inset: {
325 | properties: {
326 | anchor: {
327 | attributes: {
328 | name: "legend.inset.anchor",
329 | valueoptions: ["top-left", "top-right", "bottom-left", "bottom-right"],
330 | type: {
331 | names: ["String"]
332 | },
333 | optional: true,
334 | value: "top-left",
335 | defaultvalue: "top-left"
336 | }
337 | },
338 | x: {
339 | attributes: {
340 | name: "legend.inset.x",
341 | type: {
342 | names: ["Number"]
343 | },
344 | optional: true,
345 | value: 10,
346 | defaultvalue: 10,
347 | min: 0,
348 | max: 100,
349 | step: 1
350 | }
351 | },
352 | y: {
353 | attributes: {
354 | name: "legend.inset.y",
355 | type: {
356 | names: ["Number"]
357 | },
358 | optional: true,
359 | value: 0,
360 | defaultvalue: 0,
361 | min: 0,
362 | max: 100,
363 | step: 1
364 | }
365 | },
366 | step: {
367 | attributes: {
368 | name: "legend.inset.step",
369 | type: {
370 | names: ["Number"]
371 | },
372 | optional: true,
373 | value: "undefined",
374 | defaultvalue: "undefined",
375 | min: 0,
376 | max: 100,
377 | step: 1
378 | }
379 | }
380 | }
381 | },
382 | position: {
383 | attributes: {
384 | valueoptions: ["bottom", "right", "inset"]
385 | }
386 | }
387 | }
388 | },
389 |
390 | grid: {
391 | properties: {
392 | y: {
393 | properties: {
394 | ticks: {
395 | attributes: {
396 | min: 0,
397 | max: 100,
398 | step: 1
399 | }
400 | }
401 | }
402 | }
403 | }
404 | },
405 | color: {
406 | properties: {
407 | threshold: {
408 | properties: {
409 | max: {
410 | attributes: {
411 | min: 0,
412 | max: 300,
413 | step: 1
414 | }
415 | }
416 | }
417 | }
418 | }
419 | },
420 | title: {
421 | properties: {
422 | position: {
423 | attributes: {
424 | valueoptions: ["top-center", "top-right", "top-left"]
425 | }
426 | }
427 | }
428 | }
429 | };
430 |
--------------------------------------------------------------------------------
/src/app/reducers/logic/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | namespaceToObject,
3 | deepCopy,
4 | strinifyContainsFunction,
5 | stringToFunction,
6 | objectFlatten
7 | } from "../../util";
8 | import {
9 | formattedDocument as initDocument
10 | } from "../../configure";
11 | import {
12 | convertColumnsToData
13 | } from "./convert";
14 |
15 | const dataToFormatedData = (format, data) => {
16 | if (format === "columns") {
17 | const mapper = {};
18 | const columns = [];
19 |
20 | data.header.forEach(function(key, index){
21 | mapper[key] = index;
22 | columns.push([key]);
23 | });
24 |
25 | data.body.forEach(function(data, index){
26 | _.each(data, (value, key) => {
27 | columns[mapper[key]][index+1] = value;
28 | });
29 | });
30 |
31 | return columns;
32 | }
33 | };
34 |
35 | export const convertData = (data) => {
36 | let converted;
37 | if (data.columns) {
38 | converted = convertColumnsToData(data.columns);
39 | } else if (data.rows) {
40 | converted = convertRowsToData(data.rows);
41 | } else if (data.json){
42 | converted = convertJsonToData(data.json, {
43 | value: _.keys(data.json[0])
44 | });
45 | }
46 | return {
47 | header: _.keys(converted[0]),
48 | body: converted
49 | };
50 | };
51 |
52 |
53 |
54 | export const resetDataState = (state, newState) => {
55 | return deepCopy({}, newState);
56 | };
57 |
58 | export const removeValueToDataState = (state, newData) => {
59 | const newState = deepCopy({}, state);
60 | newState.body.splice(newData, 1);
61 |
62 | return newState;
63 | };
64 |
65 | export const removeKeyToDataState = (state, newData) => {
66 | const newState = deepCopy({}, state);
67 |
68 | newState.body.forEach((target) => {
69 | delete target[newData];
70 | });
71 | newState.header = _.remove(newState.header, function(name) {
72 | return name !== newData;
73 | });
74 |
75 | return newState;
76 | };
77 |
78 |
79 | export const addValueToDataTable = (state, newData) => {
80 | const newState = deepCopy({}, state);
81 | newState.body.push(newData);
82 |
83 | return newState;
84 | };
85 |
86 | export const addKeyToDataTable = (state, newData) => {
87 | const newState = deepCopy({}, state);
88 |
89 | newState.header.push(newData.name);
90 | newData.value.forEach((value, index) => {
91 | newState.body[index][newData.name] = value;
92 | });
93 | return newState;
94 | };
95 |
96 | export const updateDataKeyState = (state, { value, column }) => {
97 | const newState = deepCopy({}, state);
98 | const prevHeader = state.header[column];
99 | const newBody = newState.body.map((data) => {
100 | data[value] = data[prevHeader];
101 | delete data[prevHeader];
102 | return data;
103 | });
104 | newState.header[column] = value;
105 | newState.body = newBody;
106 |
107 | return newState;
108 | };
109 |
110 | export const updateDataValueState = (state, {row, header, value}) => {
111 | const newState = deepCopy({}, state);
112 | const newValue = !isNaN(value) && value !== "" ? Number(value) : value;
113 |
114 | newState.body[row][header] = newValue;
115 |
116 | return newState;
117 | };
118 |
119 |
120 |
121 |
122 | export const updateCommandData = (state, lastest) => {
123 | // columns, json, rows
124 | const formatList = ["columns", "json", "rows"];
125 | const newState = deepCopy({}, state);
126 | const format = Object.keys(newState.original.data).filter((key) => {
127 | return formatList.indexOf(key) > -1;
128 | })[0];
129 |
130 | newState.original.data[format] = dataToFormatedData(format, lastest);
131 |
132 | const text = strinifyContainsFunction(newState.original);
133 |
134 |
135 | return {
136 | original: newState.original,
137 | text
138 | };
139 | };
140 |
141 |
142 |
143 | export const updateCommandCode = (state, name) => {
144 | const textFunc = getDefaultValue(name);
145 | const objFunc = stringToFunction(textFunc);
146 | const updated = namespaceToObject(name, objFunc);
147 |
148 | const original = deepCopy({}, state.original, updated);
149 | const text = strinifyContainsFunction(original);
150 |
151 | return {
152 | original, text
153 | };
154 | };
155 |
156 |
157 | export const resetCommandState = (updated) => {
158 | const original = deepCopy({}, updated);
159 | const text = strinifyContainsFunction(original);
160 |
161 | return {
162 | original, text
163 | };
164 | };
165 |
166 | export const updateCommandState = (state, updated) => {
167 | const original = deepCopy({}, state.original, updated);
168 | const text = strinifyContainsFunction(original);
169 |
170 | return {
171 | original, text
172 | };
173 | };
174 |
175 | export const deleteTargetCommandState = (state, namespace) => {
176 | const original = deepCopy({}, state.original);
177 | const deleteTarget = deleteTargetKey(original, namespace);
178 | const text = strinifyContainsFunction(deleteTarget);
179 |
180 | return {
181 | original: deepCopy({}, deleteTarget),
182 | text: text
183 | };
184 | };
185 |
186 | export const updateCommandByGUI = (state, name, value) => {
187 | const latest = namespaceToObject(name, value);
188 | const defaultvalue = getDefaultValue(name);
189 |
190 | return (value === defaultvalue) ? deleteTargetCommandState(state, name) :
191 | updateCommandState(state, latest);
192 | }
193 |
194 |
195 | export const updateGuiState = (state, updated) => {
196 | const newObj = changeMemberProperty(state, deepCopy({}, updated));
197 | return deepCopy({}, newObj);
198 | };
199 |
200 |
201 | export const changeDefaultGuiState = (state, keys) => {
202 | let newObj = {};
203 | _.each(keys, (name) => {
204 | const newValue = getDefaultValue(name);
205 | const obj = namespaceToObject(name, newValue);
206 | newObj = deepCopy(newObj, obj);
207 | });
208 |
209 | const newState = changeMemberProperty(state, deepCopy({}, newObj));
210 | return deepCopy({}, newState);
211 | };
212 |
213 |
214 | export const updateGuiActivate = (state, name) => {
215 | const path = `${name.replace(/\./g, ".properties.")}.attributes.activated`;
216 | _.update(state, path, () => {
217 | return true;
218 | });
219 | return deepCopy({}, state);
220 | };
221 |
222 |
223 |
224 | const typeValid = (types, value) => {
225 | if(value === undefined || value === "undefined"){
226 | return undefined;
227 | }
228 |
229 | if(types.indexOf("number") > -1) {
230 | if(isNaN(value)){
231 | //console.log("TODO => " + value);
232 | return value;
233 | } else {
234 | return value*1;
235 | }
236 | }
237 |
238 | if(types.indexOf("array") > -1){
239 | const array = JSON.parse(value+="");
240 | return array;
241 | }
242 |
243 | if(types.indexOf("function") > -1){
244 | return value;
245 | }
246 |
247 | if(types.indexOf("boolean") > -1){
248 | return value;
249 | }
250 |
251 | if(types.indexOf("string") > -1){
252 | return value;
253 | }
254 |
255 | if(types.indexOf("object") > -1){
256 | return value;
257 | }
258 |
259 | return value;
260 |
261 | };
262 |
263 | const memberFlatten = (member) => {
264 | let newArray = [member];
265 | if (member.properties) {
266 | _.each(member.properties, (item) => {
267 | if(item.properties){
268 | newArray = newArray.concat(memberFlatten(item));
269 | } else {
270 | const value = typeValid(item.type.names.map(type => (type.toLowerCase())), item.defaultvalue);
271 | newArray.push({
272 | type: item.type,
273 | defaultvalue: value,
274 | value: value,
275 | name: item.name,
276 | description: item.description,
277 | optional: item.optional,
278 | activated: false,
279 | examples: item.examples,
280 | docid: item.name,
281 | });
282 | }
283 | });
284 | }
285 |
286 | return newArray;
287 | };
288 |
289 |
290 |
291 | export const getValueFromDocument = (doc, namespace, targetAttribute) => {
292 | const path = namespace.replace(/\./g, ".properties.");
293 | const attirbutes = _.get(doc, path).attributes;
294 |
295 | return attirbutes[targetAttribute];
296 | };
297 |
298 | export const getRemovedToDefault = (prev, cur) => {
299 | const pkeys = Object.keys(objectFlatten(prev));
300 | const ckeys = Object.keys(objectFlatten(cur));
301 | const nkeys = _.difference(pkeys, ckeys);
302 |
303 | let newObj = {};
304 | _.each(nkeys, (name) => {
305 | const newValue = getDefaultValue(name);
306 | const obj = namespaceToObject(name, newValue);
307 | newObj = deepCopy(newObj, obj);
308 | });
309 |
310 | return newObj;
311 | };
312 |
313 | export const getDefaultValue = (namespace) => {
314 | const document = initDocument;
315 | const path = namespace.replace(/\./g, ".properties.");
316 | const attirbutes = _.get(document, path).attributes;
317 |
318 | return attirbutes.defaultvalue;
319 | };
320 |
321 | const fillDefaultAttributes = (target, root) => {
322 | target.attributes = target.attributes || getDefaultAttributes(root);
323 | if(target.properties){
324 | _.each(target.properties, (obj, key) => {
325 | target.properties[key] = fillDefaultAttributes(obj, `${root}.${key}`);
326 | });
327 | }
328 | return target;
329 | };
330 |
331 | const getDefaultAttributes = (name) => {
332 | return {
333 | description: "",
334 | optional: true,
335 | activated: false,
336 | name,
337 | value: undefined,
338 | defaultvalue: undefined,
339 | type: {
340 | names: ["Object"]
341 | },
342 | };
343 | };
344 |
345 |
346 | export const changeMemberActivate = (original, name, value) => {
347 | const fkeys = [name];
348 |
349 | _.each(fkeys, (keyPath) => {
350 | const targetPath = keyPath.replace(/\./g, ".properties.") + ".attributes";
351 | const type = _.get(original, targetPath + ".type");
352 |
353 | _.update(original, targetPath + ".activated", () => {
354 | return value;
355 | });
356 | });
357 |
358 | return original;
359 | };
360 |
361 |
362 | const typeConverting = (types, value) => {
363 | return value;
364 | };
365 |
366 | const isSameValue = (a, b) => {
367 | return a+"" == b+"";
368 | };
369 |
370 | export const changeMemberProperty = (document, latest) => {
371 | const flatten = objectFlatten(latest);
372 | const keys = Object.keys(flatten);
373 |
374 | _.each(keys, (keyPath) => {
375 | const path = `${keyPath.replace(/\./g, ".properties.")}.attributes`;
376 | const attributes = _.get(document, path);
377 | const latestValue = flatten[keyPath];
378 | const isUpdate = !isSameValue(latestValue, attributes.defaultvalue);
379 |
380 | _.update(document, `${path}.value`, () => {
381 | return typeConverting(attributes.type.names, latestValue);
382 | });
383 |
384 | _.update(document, `${path}.activated`, () => {
385 | return isUpdate;
386 | });
387 | });
388 |
389 | return document;
390 |
391 | //_.each(fkeys, (keyPath) => {
392 | // const targetPath = keyPath.replace(/\./g, ".properties.") + ".attributes";
393 | // const type = _.get(original, targetPath + ".type");
394 | // let value = _.get(object, keyPath);
395 | // let changed = _.get(original, targetPath + ".defaultvalue") !== value;
396 | //
397 | // _.update(original, targetPath + ".activated", () => {
398 | // return changed;
399 | // });
400 | //
401 | // if(type && type.names && type.names[0] && type.names[0].toLowerCase() === "number" && value !== undefined){
402 | // value = value * 1;
403 | // }
404 | //
405 | // if(typeof value === "function") {
406 | // value = value.toString();
407 | // }
408 | //
409 | // _.update(original, targetPath + ".value", () => {
410 | // return value;
411 | // });
412 | //});
413 | //return original;
414 | };
415 |
416 | export const deleteTargetKey = (original, targetkey) => {
417 | const keys = targetkey.split(".").reverse();
418 | let path = targetkey.replace("." + keys[0], "");
419 | let prevPath = path;
420 |
421 | _.unset(original, targetkey);
422 |
423 | _.each(keys.slice(1), (key) => {
424 | const target = _.get(original, path);
425 | if(target && Object.keys(target).length < 1){
426 | _.unset(original, path);
427 | }
428 | prevPath = path;
429 | path = path.replace("." + key, "");
430 | });
431 |
432 | return original;
433 | };
434 | export const getAttributesFromDocument = (name) => {
435 | const init = initDocument;
436 | const path = name.replace(/\./g, ".properties.") + ".attributes";
437 | return _.get(init, path);
438 | };
439 |
440 |
441 | const canOptional = (keys, name) => {
442 | const datas = ["data.columns", "data.rows", "data.json", "data.url"];
443 | if(datas.indexOf(name) < 0){
444 | return true;
445 | }
446 | let flag = 0;
447 | _.each(keys, (key) => {
448 | datas.indexOf(key) > -1 ? flag++ : flag;
449 | });
450 |
451 | };
452 |
453 | const dataSetConfig = ["data.columns", "data.rows", "data.json", "data.url"];
454 | const hasOneMoreDataset = (keys) => {
455 | return (dataSetConfig.length - _.difference(dataSetConfig, keys).length > 1);
456 | };
457 |
458 | export const getChip = (keys) => {
459 | const collection = [];
460 | const hasMoreDataSet = hasOneMoreDataset(keys);
461 |
462 | _.each(keys, (name) => {
463 | const target = getAttributesFromDocument(name);
464 | if(dataSetConfig.indexOf(name) > -1 && !hasMoreDataSet){
465 | collection.push({
466 | name,
467 | optional: false
468 | });
469 | } else {
470 | collection.push({
471 | name,
472 | optional: target.optional === undefined ? true : target.optional,
473 | });
474 | }
475 |
476 | });
477 |
478 | return collection;
479 | };
480 |
--------------------------------------------------------------------------------
/dist/app.bundle.css:
--------------------------------------------------------------------------------
1 | @import url(https://fonts.googleapis.com/css?family=Bungee);@import url(https://fonts.googleapis.com/css?family=Roboto:400,500);/*
2 | html5doctor.com Reset Stylesheet
3 | v1.6.1
4 | Last Updated: 2010-09-17
5 | Author: Richard Clark - http://richclarkdesign.com
6 | Twitter: @rich_clark
7 | License : http://html5doctor.com/html-5-reset-stylesheet/
8 | */
9 |
10 | html, body, div, span, object, iframe,
11 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
12 | abbr, address, cite, code,
13 | del, dfn, em, img, ins, kbd, q, samp,
14 | small, strong, sub, sup, var,
15 | b, i,
16 | dl, dt, dd, ol, ul, li,
17 | fieldset, form, label, legend,
18 | table, caption, tbody, tfoot, thead, tr, th, td,
19 | article, aside, canvas, details, figcaption, figure,
20 | footer, header, hgroup, menu, nav, section, summary,
21 | time, mark, audio, video {
22 | margin:0;
23 | padding:0;
24 | border:0;
25 | outline:0;
26 | font-size:100%;
27 | vertical-align:baseline;
28 | background:transparent;
29 | }
30 |
31 | body {
32 | line-height:1;
33 | }
34 |
35 | article,aside,details,figcaption,figure,
36 | footer,header,hgroup,menu,nav,section {
37 | display:block;
38 | }
39 |
40 | nav ul {
41 | list-style:none;
42 | }
43 |
44 | blockquote, q {
45 | quotes:none;
46 | }
47 |
48 | blockquote:before, blockquote:after,
49 | q:before, q:after {
50 | content:'';
51 | content:none;
52 | }
53 |
54 | a {
55 | margin:0;
56 | padding:0;
57 | font-size:100%;
58 | vertical-align:baseline;
59 | background:transparent;
60 | }
61 |
62 | /* change colours to suit your needs */
63 | ins {
64 | background-color:#ff9;
65 | color:#000;
66 | text-decoration:none;
67 | }
68 |
69 | /* change colours to suit your needs */
70 | mark {
71 | background-color:#ff9;
72 | color:#000;
73 | font-style:italic;
74 | font-weight:bold;
75 | }
76 |
77 | del {
78 | text-decoration: line-through;
79 | }
80 |
81 | abbr[title], dfn[title] {
82 | border-bottom:1px dotted;
83 | cursor:help;
84 | }
85 |
86 | table {
87 | border-collapse:collapse;
88 | border-spacing:0;
89 | }
90 |
91 | /* change border colour to suit your needs */
92 | hr {
93 | display:block;
94 | height:1px;
95 | border:0;
96 | border-top:1px solid #cccccc;
97 | margin:1em 0;
98 | padding:0;
99 | }
100 |
101 | input, select {
102 | vertical-align:middle;
103 | }
104 |
105 |
106 | /* fallback */
107 | @font-face {
108 | font-family: 'Material Icons';
109 | font-style: normal;
110 | font-weight: 400;
111 | /*src: url(MaterialIcons-Regular.woff2) format('woff2'),*/
112 | src: url(https://fonts.gstatic.com/s/materialicons/v29/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2) format('woff2');
113 | }
114 |
115 | .material-icons {
116 | font-family: 'Material Icons';
117 | font-weight: normal;
118 | font-style: normal;
119 | font-size: 24px;
120 | line-height: 1;
121 | letter-spacing: normal;
122 | text-transform: none;
123 | display: inline-block;
124 | white-space: nowrap;
125 | word-wrap: normal;
126 | direction: ltr;
127 | -webkit-font-feature-settings: 'liga';
128 | -webkit-font-smoothing: antialiased;
129 | }
130 |
131 | /* BASICS */
132 |
133 | .CodeMirror {
134 | /* Set height, width, borders, and global font properties here */
135 | font-family: monospace;
136 | height: 300px;
137 | color: black;
138 | direction: ltr;
139 | }
140 |
141 | /* PADDING */
142 |
143 | .CodeMirror-lines {
144 | padding: 4px 0; /* Vertical padding around content */
145 | }
146 | .CodeMirror pre {
147 | padding: 0 4px; /* Horizontal padding of content */
148 | }
149 |
150 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
151 | background-color: white; /* The little square between H and V scrollbars */
152 | }
153 |
154 | /* GUTTER */
155 |
156 | .CodeMirror-gutters {
157 | border-right: 1px solid #ddd;
158 | background-color: #f7f7f7;
159 | white-space: nowrap;
160 | }
161 | .CodeMirror-linenumbers {}
162 | .CodeMirror-linenumber {
163 | padding: 0 3px 0 5px;
164 | min-width: 20px;
165 | text-align: right;
166 | color: #999;
167 | white-space: nowrap;
168 | }
169 |
170 | .CodeMirror-guttermarker { color: black; }
171 | .CodeMirror-guttermarker-subtle { color: #999; }
172 |
173 | /* CURSOR */
174 |
175 | .CodeMirror-cursor {
176 | border-left: 1px solid black;
177 | border-right: none;
178 | width: 0;
179 | }
180 | /* Shown when moving in bi-directional text */
181 | .CodeMirror div.CodeMirror-secondarycursor {
182 | border-left: 1px solid silver;
183 | }
184 | .cm-fat-cursor .CodeMirror-cursor {
185 | width: auto;
186 | border: 0 !important;
187 | background: #7e7;
188 | }
189 | .cm-fat-cursor div.CodeMirror-cursors {
190 | z-index: 1;
191 | }
192 | .cm-fat-cursor-mark {
193 | background-color: rgba(20, 255, 20, 0.5);
194 | -webkit-animation: blink 1.06s steps(1) infinite;
195 | -moz-animation: blink 1.06s steps(1) infinite;
196 | animation: blink 1.06s steps(1) infinite;
197 | }
198 | .cm-animate-fat-cursor {
199 | width: auto;
200 | border: 0;
201 | -webkit-animation: blink 1.06s steps(1) infinite;
202 | -moz-animation: blink 1.06s steps(1) infinite;
203 | animation: blink 1.06s steps(1) infinite;
204 | background-color: #7e7;
205 | }
206 | @-moz-keyframes blink {
207 | 0% {}
208 | 50% { background-color: transparent; }
209 | 100% {}
210 | }
211 | @-webkit-keyframes blink {
212 | 0% {}
213 | 50% { background-color: transparent; }
214 | 100% {}
215 | }
216 | @keyframes blink {
217 | 0% {}
218 | 50% { background-color: transparent; }
219 | 100% {}
220 | }
221 |
222 | /* Can style cursor different in overwrite (non-insert) mode */
223 | .CodeMirror-overwrite .CodeMirror-cursor {}
224 |
225 | .cm-tab { display: inline-block; text-decoration: inherit; }
226 |
227 | .CodeMirror-rulers {
228 | position: absolute;
229 | left: 0; right: 0; top: -50px; bottom: -20px;
230 | overflow: hidden;
231 | }
232 | .CodeMirror-ruler {
233 | border-left: 1px solid #ccc;
234 | top: 0; bottom: 0;
235 | position: absolute;
236 | }
237 |
238 | /* DEFAULT THEME */
239 |
240 | .cm-s-default .cm-header {color: blue;}
241 | .cm-s-default .cm-quote {color: #090;}
242 | .cm-negative {color: #d44;}
243 | .cm-positive {color: #292;}
244 | .cm-header, .cm-strong {font-weight: bold;}
245 | .cm-em {font-style: italic;}
246 | .cm-link {text-decoration: underline;}
247 | .cm-strikethrough {text-decoration: line-through;}
248 |
249 | .cm-s-default .cm-keyword {color: #708;}
250 | .cm-s-default .cm-atom {color: #219;}
251 | .cm-s-default .cm-number {color: #164;}
252 | .cm-s-default .cm-def {color: #00f;}
253 | .cm-s-default .cm-variable,
254 | .cm-s-default .cm-punctuation,
255 | .cm-s-default .cm-property,
256 | .cm-s-default .cm-operator {}
257 | .cm-s-default .cm-variable-2 {color: #05a;}
258 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
259 | .cm-s-default .cm-comment {color: #a50;}
260 | .cm-s-default .cm-string {color: #a11;}
261 | .cm-s-default .cm-string-2 {color: #f50;}
262 | .cm-s-default .cm-meta {color: #555;}
263 | .cm-s-default .cm-qualifier {color: #555;}
264 | .cm-s-default .cm-builtin {color: #30a;}
265 | .cm-s-default .cm-bracket {color: #997;}
266 | .cm-s-default .cm-tag {color: #170;}
267 | .cm-s-default .cm-attribute {color: #00c;}
268 | .cm-s-default .cm-hr {color: #999;}
269 | .cm-s-default .cm-link {color: #00c;}
270 |
271 | .cm-s-default .cm-error {color: #f00;}
272 | .cm-invalidchar {color: #f00;}
273 |
274 | .CodeMirror-composing { border-bottom: 2px solid; }
275 |
276 | /* Default styles for common addons */
277 |
278 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
279 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
280 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
281 | .CodeMirror-activeline-background {background: #e8f2ff;}
282 |
283 | /* STOP */
284 |
285 | /* The rest of this file contains styles related to the mechanics of
286 | the editor. You probably shouldn't touch them. */
287 |
288 | .CodeMirror {
289 | position: relative;
290 | overflow: hidden;
291 | background: white;
292 | }
293 |
294 | .CodeMirror-scroll {
295 | overflow: scroll !important; /* Things will break if this is overridden */
296 | /* 30px is the magic margin used to hide the element's real scrollbars */
297 | /* See overflow: hidden in .CodeMirror */
298 | margin-bottom: -30px; margin-right: -30px;
299 | padding-bottom: 30px;
300 | height: 100%;
301 | outline: none; /* Prevent dragging from highlighting the element */
302 | position: relative;
303 | }
304 | .CodeMirror-sizer {
305 | position: relative;
306 | border-right: 30px solid transparent;
307 | }
308 |
309 | /* The fake, visible scrollbars. Used to force redraw during scrolling
310 | before actual scrolling happens, thus preventing shaking and
311 | flickering artifacts. */
312 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
313 | position: absolute;
314 | z-index: 6;
315 | display: none;
316 | }
317 | .CodeMirror-vscrollbar {
318 | right: 0; top: 0;
319 | overflow-x: hidden;
320 | overflow-y: scroll;
321 | }
322 | .CodeMirror-hscrollbar {
323 | bottom: 0; left: 0;
324 | overflow-y: hidden;
325 | overflow-x: scroll;
326 | }
327 | .CodeMirror-scrollbar-filler {
328 | right: 0; bottom: 0;
329 | }
330 | .CodeMirror-gutter-filler {
331 | left: 0; bottom: 0;
332 | }
333 |
334 | .CodeMirror-gutters {
335 | position: absolute; left: 0; top: 0;
336 | min-height: 100%;
337 | z-index: 3;
338 | }
339 | .CodeMirror-gutter {
340 | white-space: normal;
341 | height: 100%;
342 | display: inline-block;
343 | vertical-align: top;
344 | margin-bottom: -30px;
345 | }
346 | .CodeMirror-gutter-wrapper {
347 | position: absolute;
348 | z-index: 4;
349 | background: none !important;
350 | border: none !important;
351 | }
352 | .CodeMirror-gutter-background {
353 | position: absolute;
354 | top: 0; bottom: 0;
355 | z-index: 4;
356 | }
357 | .CodeMirror-gutter-elt {
358 | position: absolute;
359 | cursor: default;
360 | z-index: 4;
361 | }
362 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
363 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
364 |
365 | .CodeMirror-lines {
366 | cursor: text;
367 | min-height: 1px; /* prevents collapsing before first draw */
368 | }
369 | .CodeMirror pre {
370 | /* Reset some styles that the rest of the page might have set */
371 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
372 | border-width: 0;
373 | background: transparent;
374 | font-family: inherit;
375 | font-size: inherit;
376 | margin: 0;
377 | white-space: pre;
378 | word-wrap: normal;
379 | line-height: inherit;
380 | color: inherit;
381 | z-index: 2;
382 | position: relative;
383 | overflow: visible;
384 | -webkit-tap-highlight-color: transparent;
385 | -webkit-font-variant-ligatures: contextual;
386 | font-variant-ligatures: contextual;
387 | }
388 | .CodeMirror-wrap pre {
389 | word-wrap: break-word;
390 | white-space: pre-wrap;
391 | word-break: normal;
392 | }
393 |
394 | .CodeMirror-linebackground {
395 | position: absolute;
396 | left: 0; right: 0; top: 0; bottom: 0;
397 | z-index: 0;
398 | }
399 |
400 | .CodeMirror-linewidget {
401 | position: relative;
402 | z-index: 2;
403 | padding: 0.1px; /* Force widget margins to stay inside of the container */
404 | }
405 |
406 | .CodeMirror-widget {}
407 |
408 | .CodeMirror-rtl pre { direction: rtl; }
409 |
410 | .CodeMirror-code {
411 | outline: none;
412 | }
413 |
414 | /* Force content-box sizing for the elements where we expect it */
415 | .CodeMirror-scroll,
416 | .CodeMirror-sizer,
417 | .CodeMirror-gutter,
418 | .CodeMirror-gutters,
419 | .CodeMirror-linenumber {
420 | -moz-box-sizing: content-box;
421 | box-sizing: content-box;
422 | }
423 |
424 | .CodeMirror-measure {
425 | position: absolute;
426 | width: 100%;
427 | height: 0;
428 | overflow: hidden;
429 | visibility: hidden;
430 | }
431 |
432 | .CodeMirror-cursor {
433 | position: absolute;
434 | pointer-events: none;
435 | }
436 | .CodeMirror-measure pre { position: static; }
437 |
438 | div.CodeMirror-cursors {
439 | visibility: hidden;
440 | position: relative;
441 | z-index: 3;
442 | }
443 | div.CodeMirror-dragcursors {
444 | visibility: visible;
445 | }
446 |
447 | .CodeMirror-focused div.CodeMirror-cursors {
448 | visibility: visible;
449 | }
450 |
451 | .CodeMirror-selected { background: #d9d9d9; }
452 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
453 | .CodeMirror-crosshair { cursor: crosshair; }
454 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
455 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
456 |
457 | .cm-searching {
458 | background-color: #ffa;
459 | background-color: rgba(255, 255, 0, .4);
460 | }
461 |
462 | /* Used to force a border model for a node */
463 | .cm-force-border { padding-right: .1px; }
464 |
465 | @media print {
466 | /* Hide the cursor when printing */
467 | .CodeMirror div.CodeMirror-cursors {
468 | visibility: hidden;
469 | }
470 | }
471 |
472 | /* See issue #2901 */
473 | .cm-tab-wrap-hack:after { content: ''; }
474 |
475 | /* Help users use markselection to safely style text background */
476 | span.CodeMirror-selectedtext { background: none; }
477 | /*!
478 | * Copyright (c) 2017 NAVER Corp.
479 | * billboard.js project is licensed under the MIT license
480 | *
481 | * billboard.js, JavaScript chart library
482 | * http://naver.github.io/billboard.js/
483 | *
484 | * @version 1.2.0
485 | */
486 | /*-- Chart --*/
487 | .bb svg {
488 | font: 10px sans-serif;
489 | -webkit-tap-highlight-color: transparent; }
490 |
491 | .bb path, .bb line {
492 | fill: none;
493 | stroke: #000; }
494 |
495 | .bb text {
496 | -webkit-user-select: none;
497 | -moz-user-select: none;
498 | user-select: none; }
499 |
500 | .bb-legend-item-tile,
501 | .bb-xgrid-focus,
502 | .bb-ygrid,
503 | .bb-event-rect,
504 | .bb-bars path {
505 | shape-rendering: crispEdges; }
506 |
507 | .bb-chart-arc path {
508 | stroke: #fff; }
509 |
510 | .bb-chart-arc text {
511 | fill: #fff;
512 | font-size: 13px; }
513 |
514 | /*-- Axis --*/
515 | /*-- Grid --*/
516 | .bb-grid line {
517 | stroke: #aaa; }
518 |
519 | .bb-grid text {
520 | fill: #aaa; }
521 |
522 | .bb-xgrid, .bb-ygrid {
523 | stroke-dasharray: 3 3; }
524 |
525 | /*-- Text on Chart --*/
526 | .bb-text.bb-empty {
527 | fill: #808080;
528 | font-size: 2em; }
529 |
530 | /*-- Line --*/
531 | .bb-line {
532 | stroke-width: 1px; }
533 |
534 | /*-- Point --*/
535 | .bb-circle._expanded_ {
536 | stroke-width: 1px;
537 | stroke: white; }
538 |
539 | .bb-selected-circle {
540 | fill: white;
541 | stroke-width: 2px; }
542 |
543 | /*-- Bar --*/
544 | .bb-bar {
545 | stroke-width: 0; }
546 | .bb-bar._expanded_ {
547 | fill-opacity: 0.75; }
548 |
549 | /*-- Focus --*/
550 | .bb-target.bb-focused {
551 | opacity: 1; }
552 |
553 | .bb-target.bb-focused path.bb-line, .bb-target.bb-focused path.bb-step {
554 | stroke-width: 2px; }
555 |
556 | .bb-target.bb-defocused {
557 | opacity: 0.3 !important; }
558 |
559 | /*-- Region --*/
560 | .bb-region {
561 | fill: steelblue;
562 | fill-opacity: .1; }
563 |
564 | /*-- Brush --*/
565 | .bb-brush .extent {
566 | fill-opacity: .1; }
567 |
568 | /*-- Select - Drag --*/
569 | /*-- Legend --*/
570 | .bb-legend-item {
571 | font-size: 12px; }
572 |
573 | .bb-legend-item-hidden {
574 | opacity: 0.15; }
575 |
576 | .bb-legend-background {
577 | opacity: 0.75;
578 | fill: white;
579 | stroke: lightgray;
580 | stroke-width: 1; }
581 |
582 | /*-- Title --*/
583 | .bb-title {
584 | font: 14px sans-serif; }
585 |
586 | /*-- Tooltip --*/
587 | .bb-tooltip-container {
588 | z-index: 10; }
589 |
590 | .bb-tooltip {
591 | border-collapse: collapse;
592 | border-spacing: 0;
593 | background-color: #fff;
594 | empty-cells: show;
595 | opacity: 0.9;
596 | -webkit-box-shadow: 7px 7px 12px -9px #777777;
597 | -moz-box-shadow: 7px 7px 12px -9px #777777;
598 | box-shadow: 7px 7px 12px -9px #777777; }
599 | .bb-tooltip tr {
600 | border: 1px solid #CCC; }
601 | .bb-tooltip th {
602 | background-color: #aaa;
603 | font-size: 14px;
604 | padding: 2px 5px;
605 | text-align: left;
606 | color: #FFF; }
607 | .bb-tooltip td {
608 | font-size: 13px;
609 | padding: 3px 6px;
610 | background-color: #fff;
611 | border-left: 1px dotted #999; }
612 | .bb-tooltip td > span, .bb-tooltip td > svg {
613 | display: inline-block;
614 | width: 10px;
615 | height: 10px;
616 | margin-right: 6px; }
617 | .bb-tooltip td.value {
618 | text-align: right; }
619 |
620 | /*-- Area --*/
621 | .bb-area {
622 | stroke-width: 0;
623 | opacity: 0.2; }
624 |
625 | /*-- Arc --*/
626 | .bb-chart-arcs-title {
627 | dominant-baseline: middle;
628 | font-size: 1.3em; }
629 |
630 | .bb-chart-arcs .bb-chart-arcs-background {
631 | fill: #e0e0e0;
632 | stroke: none; }
633 |
634 | .bb-chart-arcs .bb-chart-arcs-gauge-unit {
635 | fill: #000;
636 | font-size: 16px; }
637 |
638 | .bb-chart-arcs .bb-chart-arcs-gauge-max {
639 | fill: #777; }
640 |
641 | .bb-chart-arcs .bb-chart-arcs-gauge-min {
642 | fill: #777; }
643 |
644 | .bb-chart-arc .bb-gauge-value {
645 | fill: #000; }/** app.scss **/
646 | html {
647 | padding: 10px; }
648 |
649 | #app {
650 | border: 1px solid #d7d7d7;
651 | position: relative;
652 | height: 100%;
653 | box-sizing: border-box; }
654 |
655 | #wrapper {
656 | height: 100%; }
657 |
658 | .ReactCodeMirror {
659 | width: 100%;
660 | height: auto;
661 | position: absolute; }
662 |
663 | .guide_card .ReactCodeMirror {
664 | width: auto;
665 | height: auto;
666 | position: relative; }
667 |
668 | .muidocs-icon-custom-github:before {
669 | content: "github"; }
670 |
671 | .CodeMirror {
672 | width: 100%;
673 | height: 100%; }
674 |
675 | .type {
676 | color: #009688; }
677 |
678 | .err {
679 | background: #ffd9d9; }
680 |
681 | .bbtitle {
682 | width: 60%;
683 | height: 10%;
684 | margin: 0;
685 | left: 0;
686 | text-align: center;
687 | font-family: 'Roboto', sans-serif;
688 | font-size: 25px; }
689 |
690 | .bbtitle .logo {
691 | display: inline-block;
692 | width: 145px;
693 | height: 33px;
694 | margin-left: 10px;
695 | background-repeat: no-repeat;
696 | position: relative;
697 | top: 9px;
698 | box-sizing: content-box;
699 | background-color: #fff;
700 | border-style: none; }
701 |
702 | .chart {
703 | width: 60%;
704 | height: 40%;
705 | margin: 0;
706 | left: 0; }
707 |
708 | .tabviews {
709 | width: 60%;
710 | height: 50%;
711 | margin: 0;
712 | left: 0; }
713 |
714 | .textConfigure, .data-table {
715 | width: 100%; }
716 |
717 | .data-table .remove-data {
718 | text-align: center;
719 | opacity: 0; }
720 |
721 | .data-table .add-data {
722 | position: relative;
723 | text-align: center; }
724 |
725 | .data-table .add-data.column {
726 | top: 33px; }
727 |
728 | .data-table .add-data .row {
729 | top: 3px; }
730 |
731 | .data-table .header-text:hover .remove-data {
732 | opacity: 1; }
733 |
734 | .data-table .table-row:hover .remove-data {
735 | opacity: 1; }
736 |
737 | .layer_wrapper {
738 | margin: 10px; }
739 |
740 | .bb {
741 | height: 100%; }
742 |
743 | .inputConfigure {
744 | width: 40%;
745 | left: 60%;
746 | position: absolute;
747 | height: 100%;
748 | top: 0;
749 | border-left: 1px solid #d7d7d7;
750 | box-sizing: border-box; }
751 |
752 | .scroll {
753 | width: 100%;
754 | height: 100%;
755 | overflow-y: scroll; }
756 |
757 | .property_clear {
758 | opacity: 0; }
759 |
760 | .property.activated .property_clear {
761 | opacity: 1; }
762 |
763 | .property_content {
764 | display: inline-block; }
765 |
766 | .property:hover .edit_function {
767 | opacity: 1; }
768 |
769 | .property.updated {
770 | color: red; }
771 |
772 | .property.deactivated {
773 | opacity: 0.5; }
774 |
775 | .property.deactivated .activate_check {
776 | opacity: 0; }
777 |
778 | .property:hover .activate_check {
779 | opacity: 1; }
780 |
781 | .react-toolbox_checkbox {
782 | display: inline-block; }
783 |
784 | .property_name {
785 | color: #424242;
786 | font-weight: 500; }
787 |
788 | .property .material-icons {
789 | font-size: 20px;
790 | padding-left: 10px; }
791 |
792 | .property .property_info {
793 | top: 6px;
794 | padding-left: 12px; }
795 |
796 | .property .property_info, .property .edit_code_property {
797 | opacity: 0; }
798 |
799 | .property:hover .property_info, .property:hover .edit_code_property {
800 | opacity: 1; }
801 |
802 | .property_type {
803 | font-size: 14px;
804 | line-height: 16px;
805 | height: 16px;
806 | margin: 0;
807 | margin-top: 4px;
808 | color: rgba(0, 0, 0, 0.54);
809 | overflow: hidden;
810 | text-overflow: ellipsis;
811 | white-space: nowrap; }
812 |
813 | .member svg {
814 | color: #ff8a65 !important; }
815 |
816 | .number_guide {
817 | font-size: 14px;
818 | line-height: 24px;
819 | font-family: Roboto, sans-serif;
820 | text-align: center; }
821 |
822 | .codepen-export {
823 | position: absolute;
824 | right: 60px;
825 | z-index: 1000; }
826 |
827 | .codepen-export input[type="submit"] {
828 | border: none;
829 | width: 60px;
830 | height: 40px;
831 | background: none;
832 | display: inline-block; }
833 |
834 | .codepen-export .button {
835 | background-size: contain !important;
836 | border: none;
837 | width: 60px !important;
838 | height: 40px !important;
839 | background-image: url("data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E %3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='179.851px' height='120.095px' viewBox='0 0 179.851 120.095' enable-background='new 0 0 179.851 120.095' xml:space='preserve'%3E %3Cg%3E %3Cg%3E %3Crect x='104.055' y='54.547' fill='%23EFEFEF' width='48.38' height='11'/%3E %3C/g%3E %3Cg%3E %3Cg%3E %3Cpath fill='%23EFEFEF' d='M149.336,60.047c-8.046-8.046-11.881-23.957-12.196-34.996c11.031,14.821,25.367,27.747,42.711,34.996 c-17.344,6.941-31.048,20.649-42.711,34.996C138.087,83.223,140.975,68.875,149.336,60.047z'/%3E %3C/g%3E %3C/g%3E %3C/g%3E %3Cg%3E %3Cpolygon fill='%23EFEFEF' points='29.301,54.708 29.301,65.388 37.284,60.048 '/%3E %3Cpolygon fill='%23EFEFEF' points='56.856,46.956 56.856,32.073 31.855,48.739 43.023,56.21 '/%3E %3Cpolygon fill='%23EFEFEF' points='88.239,48.739 63.238,32.073 63.238,46.956 77.072,56.21 '/%3E %3Cpolygon fill='%23EFEFEF' points='31.855,71.356 56.856,88.022 56.856,73.14 43.023,63.888 '/%3E %3Cpolygon fill='%23EFEFEF' points='63.238,73.14 63.238,88.022 88.239,71.356 77.072,63.888 '/%3E %3Cpolygon fill='%23EFEFEF' points='60.047,52.499 48.762,60.048 60.047,67.597 71.333,60.048 '/%3E %3Cpath fill='%23EFEFEF' d='M60.047,0C26.885,0,0,26.884,0,60.048s26.885,60.047,60.047,60.047c33.164,0,60.048-26.883,60.048-60.047 S93.211,0,60.047,0z M97.175,71.36c0,0.141-0.01,0.279-0.028,0.418c-0.007,0.045-0.018,0.092-0.025,0.137 c-0.017,0.09-0.032,0.18-0.056,0.268c-0.014,0.053-0.033,0.104-0.05,0.154c-0.025,0.078-0.051,0.156-0.082,0.234 c-0.021,0.053-0.048,0.104-0.071,0.154c-0.034,0.072-0.069,0.143-0.108,0.213c-0.028,0.049-0.061,0.098-0.091,0.146 c-0.043,0.066-0.087,0.131-0.135,0.193c-0.035,0.049-0.071,0.094-0.11,0.139c-0.05,0.059-0.103,0.117-0.158,0.172 c-0.042,0.043-0.083,0.086-0.127,0.125c-0.058,0.053-0.119,0.104-0.182,0.152c-0.047,0.037-0.094,0.074-0.144,0.109 c-0.019,0.012-0.035,0.027-0.054,0.039L61.817,96.64c-0.536,0.357-1.152,0.537-1.771,0.537c-0.616,0-1.233-0.18-1.77-0.537 L24.34,74.015c-0.018-0.012-0.034-0.027-0.052-0.039c-0.05-0.035-0.098-0.072-0.145-0.109c-0.062-0.049-0.123-0.1-0.181-0.152 c-0.044-0.039-0.086-0.082-0.128-0.125c-0.056-0.055-0.107-0.113-0.159-0.172c-0.037-0.045-0.074-0.09-0.109-0.139 c-0.047-0.062-0.092-0.127-0.134-0.193c-0.032-0.049-0.062-0.098-0.092-0.146c-0.039-0.07-0.074-0.141-0.108-0.213 c-0.024-0.051-0.049-0.102-0.071-0.154c-0.031-0.078-0.058-0.156-0.082-0.234c-0.018-0.051-0.035-0.102-0.05-0.154 c-0.023-0.088-0.039-0.178-0.056-0.268c-0.008-0.045-0.02-0.092-0.025-0.137c-0.019-0.139-0.029-0.277-0.029-0.418V48.735 c0-0.141,0.011-0.279,0.029-0.416c0.006-0.047,0.018-0.092,0.025-0.139c0.017-0.09,0.032-0.18,0.056-0.268 c0.015-0.053,0.032-0.104,0.05-0.154c0.024-0.078,0.051-0.156,0.082-0.232c0.022-0.053,0.047-0.105,0.071-0.156 c0.034-0.072,0.069-0.143,0.108-0.211c0.029-0.051,0.06-0.1,0.092-0.148c0.042-0.066,0.087-0.131,0.134-0.193 c0.035-0.047,0.072-0.094,0.109-0.139c0.052-0.059,0.104-0.117,0.159-0.172c0.042-0.043,0.084-0.086,0.128-0.125 c0.058-0.053,0.118-0.104,0.181-0.152c0.047-0.037,0.095-0.074,0.145-0.109c0.018-0.012,0.034-0.027,0.052-0.039l33.938-22.625 c1.072-0.715,2.468-0.715,3.54,0l33.937,22.625c0.019,0.012,0.035,0.027,0.054,0.039c0.05,0.035,0.097,0.072,0.144,0.109 c0.062,0.049,0.124,0.1,0.182,0.152c0.044,0.039,0.085,0.082,0.127,0.125c0.056,0.055,0.108,0.113,0.158,0.172 c0.039,0.045,0.075,0.092,0.11,0.139c0.048,0.062,0.092,0.127,0.135,0.193c0.03,0.049,0.062,0.098,0.091,0.148 c0.039,0.068,0.074,0.139,0.108,0.211c0.023,0.051,0.05,0.104,0.071,0.156c0.031,0.076,0.057,0.154,0.082,0.232 c0.017,0.051,0.036,0.102,0.05,0.154c0.023,0.088,0.039,0.178,0.056,0.268c0.008,0.047,0.019,0.092,0.025,0.139 c0.019,0.137,0.028,0.275,0.028,0.416V71.36z'/%3E %3Cpolygon fill='%23EFEFEF' points='90.794,65.388 90.794,54.708 82.812,60.048 '/%3E %3C/g%3E %3C/svg%3E") !important; }
840 |
841 | .drawer_close {
842 | transform: translate(-10px, 200px) !important; }
843 |
844 | .drawer_open {
845 | transform: translate(-10px, -10px) !important; }
846 |
847 | .table-row:hover .table-row-column {
848 | opacity: 1; }
849 |
850 | .table-row-column {
851 | opacity: 0; }
852 |
853 | /** app.css **/
854 | .guide_card .example {
855 | padding: 10px !important;
856 | background: #F5F5F5;
857 | font-size: 13px; }
858 |
859 | .guide_card .text_title {
860 | font-weight: 500;
861 | margin-bottom: 9px;
862 | margin-top: 22px;
863 | font-family: 'Roboto', sans-serif; }
864 |
865 | .text_content {
866 | font-weight: 400;
867 | font-family: 'Roboto', sans-serif; }
868 |
869 | /*! Color themes for Google Code Prettify | MIT License | github.com/jmblog/color-themes-for-google-code-prettify */
870 | .prettyprint {
871 | font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace;
872 | border: 0 !important; }
873 |
874 | .pln {
875 | color: #333; }
876 |
877 | /* Specify class=linenums on a pre to get line numbering */
878 | ol.linenums {
879 | margin-top: 0;
880 | margin-bottom: 0;
881 | color: #cccccc; }
882 |
883 | li.L0,
884 | li.L1,
885 | li.L2,
886 | li.L3,
887 | li.L4,
888 | li.L5,
889 | li.L6,
890 | li.L7,
891 | li.L8,
892 | li.L9 {
893 | padding-left: 1em;
894 | background-color: #fff;
895 | list-style-type: decimal; }
896 |
897 | @media screen {
898 | /* string content */
899 | .str {
900 | color: #d14; }
901 | /* keyword */
902 | .kwd {
903 | color: #333; }
904 | /* comment */
905 | .com {
906 | color: #998; }
907 | /* type name */
908 | .typ {
909 | color: #458; }
910 | /* literal value */
911 | .lit {
912 | color: #458; }
913 | /* punctuation */
914 | .pun {
915 | color: #333; }
916 | /* lisp open bracket */
917 | .opn {
918 | color: #333; }
919 | /* lisp close bracket */
920 | .clo {
921 | color: #333; }
922 | /* markup tag name */
923 | .tag {
924 | color: #000080; }
925 | /* markup attribute name */
926 | .atn {
927 | color: #008080; }
928 | /* markup attribute value */
929 | .atv {
930 | color: #d14; }
931 | /* declaration */
932 | .dec {
933 | color: #333; }
934 | /* variable name */
935 | .var {
936 | color: #008080; }
937 | /* function name */
938 | .fun {
939 | color: #900; } }
940 |
--------------------------------------------------------------------------------