UNPKG

@figlinq/react-chart-editor

Version:
306 lines (288 loc) 9.48 kB
import {Component, createRef} from 'react'; import {hot} from 'react-hot-loader/root'; import plotly from 'plotly.js/dist/plotly-with-meta'; import '../src/styles/main.scss'; import AceEditor from 'react-ace'; import Select from 'react-select'; import PlotlyEditor, {Button, DefaultEditor, Panel} from '../src'; import Inspector from 'react-inspector'; import dataSources from './dataSources'; // https://github.com/plotly/react-chart-editor#mapbox-access-tokens import ACCESS_TOKENS from '../accessTokens'; // Have actual buttons outside the component for testing undo/redo const SHOW_EXTERNAL_UNDO_REDO = false; // import {customConfigTest} from '../src/__stories__'; const dataSourceOptions = Object.keys(dataSources).map((name) => ({ value: name, label: name, })); const config = {mapboxAccessToken: ACCESS_TOKENS.MAPBOX, editable: true, displaylogo: false}; // eslint-disable-next-line no-unused-vars const traceTypesConfig = { traces: (_) => [ { value: 'scatter', icon: 'scatter', label: _('Scatter'), }, { value: 'line', label: _('Line'), }, { value: 'area', label: _('Area'), }, { value: 'bar', label: _('Bar'), }, { value: 'histogram', label: _('Histogram'), }, { value: 'table', label: _('Table'), }, { value: 'pie', label: _('Pie'), }, { value: 'box', label: _('Box'), }, { value: 'histogram2d', label: _('Histogram 2D'), }, ], complex: true, }; // eslint-disable-next-line no-unused-vars const chartHelp = { area: { helpDoc: 'https://help.plot.ly/make-an-area-graph/', examplePlot: () => { // eslint-disable-next-line no-console console.log('example bar plot!'); }, }, bar: { helpDoc: 'https://help.plot.ly/stacked-bar-chart/', examplePlot: () => { // eslint-disable-next-line no-console console.log('example bar plot!'); }, }, box: {helpDoc: 'https://help.plot.ly/make-a-box-plot/'}, candlestick: {helpDoc: 'https://help.plot.ly/make-a-candlestick/'}, choropleth: {helpDoc: 'https://help.plot.ly/make-a-choropleth-map/'}, contour: {helpDoc: 'https://help.plot.ly/make-a-contour-plot/'}, heatmap: {helpDoc: 'https://help.plot.ly/make-a-heatmap/'}, histogram2d: {helpDoc: 'https://help.plot.ly/make-a-2d-histogram-heatmap/'}, histogram2dcontour: {helpDoc: 'https://help.plot.ly/make-a-histogram/'}, line: {helpDoc: 'https://help.plot.ly/make-a-line-graph/'}, mesh3d: {helpDoc: null}, ohlc: {helpDoc: 'https://help.plot.ly/make-a-ohlc/'}, pie: {helpDoc: 'https://help.plot.ly/make-a-pie-chart/'}, scatter3d: {helpDoc: 'https://help.plot.ly/make-a-3d-scatter-plot/'}, line3d: {helpDoc: null}, scatter: {helpDoc: 'https://help.plot.ly/how-to-make-a-scatter-plot/'}, scattergeo: {helpDoc: 'https://help.plot.ly/make-scatter-map/'}, scattermapbox: {helpDoc: 'https://help.plot.ly/make-a-mapbox-map/'}, scatterternary: {helpDoc: 'https://help.plot.ly/ternary-scatter-plot/'}, surface: {helpDoc: 'https://help.plot.ly/make-a-3d-surface-plot/'}, table: {helpDoc: null}, timeseries: {helpDoc: 'https://help.plot.ly/range-slider/'}, }; class App extends Component { constructor() { super(); this.state = { data: [], layout: {}, frames: [], currentMockIndex: -1, mocks: [], }; this.loadMock = this.loadMock.bind(this); this.loadJSON = this.loadJSON.bind(this); this.updateState = this.updateState.bind(this); this.PlotlyEditor = createRef(); } UNSAFE_componentWillMount() { // curl https://api.github.com/repos/plotly/plotly.js/contents/test/image/mocks \ // | jq '[.[] | .name ]' > mocks.json fetch('/mocks.json') .then((response) => response.json()) .then((mocks) => this.setState({mocks})); } loadMock(mockIndex) { const mockName = this.state.mocks[mockIndex]; const prefix = mockName[0] === '/' ? '' : 'https://api.github.com/repos/plotly/plotly.js/contents/test/image/mocks/'; fetch(prefix + mockName, { headers: new Headers({Accept: 'application/vnd.github.v3.raw'}), }) .then((response) => response.json()) .then((figure) => { const {data, layout, frames} = figure; this.updateState(data, layout, frames, mockIndex); }); } updateState(data, layout, frames, currentMockIndex) { this.setState({ data, layout, frames, currentMockIndex, full: 'hit refresh', json_error: false, json_string: JSON.stringify({data, layout, frames}, null, 2), }); } loadJSON() { try { const {data, layout, frames} = JSON.parse(this.state.json_string); this.updateState(data, layout, frames); } catch (e) { this.setState({json_error: true}); } } render() { return ( <div className="app"> {SHOW_EXTERNAL_UNDO_REDO && ( <div> <Button label="Undo" onClick={() => { this.PlotlyEditor.current.undo(); }} /> <Button label="Redo" onClick={() => { this.PlotlyEditor.current.redo(); }} /> </div> )} <PlotlyEditor ref={this.PlotlyEditor} data={this.state.data} layout={this.state.layout} frames={this.state.frames} config={config} dataSources={dataSources} dataSourceOptions={dataSourceOptions} plotly={plotly} onUpdate={this.updateState} divId="gd" useResizeHandler debug advancedTraceTypeSelector showFieldTooltips // showUndoRedo // glByDefault // traceTypesConfig={traceTypesConfig} // makeDefaultTrace={() => ({type: 'scattergl', mode: 'markers'})} // fontOptions={[{label:'Arial', value: 'arial'}]} // chartHelp={chartHelp} // customConfig={customConfigTest} > <DefaultEditor // menuPanelOrder={[ // {group: 'Dev', name: 'JSON'}, // {group: 'Dev', name: 'Inspector'}, // {group: 'Structure', name: 'Create'}, // {group: 'Structure', name: 'Subplots'}, // {group: 'Structure', name: 'Transforms'}, // {group: 'Test', name: 'Testing'}, // {group: 'Style', name: 'General'}, // {group: 'Style', name: 'Traces'}, // {group: 'Style', name: 'Axes'}, // {group: 'Style', name: 'Legend'}, // {group: 'Style', name: 'Color Bars'}, // {group: 'Style', name: 'Annotation'}, // {group: 'Style', name: 'Shapes'}, // {group: 'Style', name: 'Images'}, // {group: 'Style', name: 'Sliders'}, // {group: 'Style', name: 'Menus'}, // ]} > <Panel group="Dev" name="JSON"> <div className="mocks"> <Select clearable={false} value={this.state.currentMockIndex} name="mock-dropdown" options={this.state.mocks.map((item, i) => ({ label: item, value: i, }))} searchable={true} searchPromptText="Search for a mock" onChange={(option) => this.loadMock(option.value)} noResultsText={'No Results'} placeholder={'Search for a mock'} /> </div> <button className="devbtn" onClick={this.loadJSON} style={{background: this.state.json_error ? 'pink' : 'white'}} > Save </button> <AceEditor mode="json" theme="textmate" onChange={(json_string) => this.setState({json_string})} value={this.state.json_string} name="UNIQUE_ID_OF_DIV" style={{height: '80vh'}} setOptions={{ showLineNumbers: false, tabSize: 2, }} commands={[ { name: 'save', bindKey: {win: 'Ctrl-s', mac: 'Command-s'}, exec: this.loadJSON, }, ]} editorProps={{$blockScrolling: true}} /> </Panel> <Panel group="Dev" name="Inspector"> <button className="devbtn" onClick={() => { const gd = document.getElementById('gd') || {}; this.setState({ full: { _fullData: gd._fullData || [], _fullLayout: gd._fullLayout || {}, }, }); }} > Refresh </button> <div style={{height: '80vh'}}> <Inspector data={{_full: this.state.full}} expandLevel={2} sortObjectKeys={true} /> </div> </Panel> </DefaultEditor> </PlotlyEditor> </div> ); } } export default hot(App);