Skip to content

Commit

Permalink
Merge pull request #69 from Kanaries/dev
Browse files Browse the repository at this point in the history
feat: graphic walker better renderer
  • Loading branch information
ObservedObserver authored Sep 25, 2021
2 parents 91b5dc6 + 36894bc commit 0cccbce
Show file tree
Hide file tree
Showing 23 changed files with 614 additions and 290 deletions.
2 changes: 1 addition & 1 deletion packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"singleQuote": true
},
"dependencies": {
"@kanaries/graphic-walker": "^0.0.1",
"@kanaries/web-data-loader": "0.1.0",
"@tableau/tableau-ui": "^3.0.0",
"@testing-library/jest-dom": "^5.11.8",
Expand All @@ -24,7 +25,6 @@
"cube-core": "^2.13.0",
"datalib": "^1.9.2",
"fuse.js": "^3.4.6",
"graphic-walker": "^0.0.1",
"immer": "^4.0.1",
"mobx": "^6.3.2",
"mobx-react-lite": "^3.2.0",
Expand Down
9 changes: 4 additions & 5 deletions packages/frontend/src/pages/visualInterface/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from 'react';
import React, { useMemo } from 'react';
import { observer } from 'mobx-react-lite';
import { GraphicWalker } from 'graphic-walker';
import { GraphicWalker } from '@kanaries/graphic-walker';
import { useGlobalStore } from '../../store';
import { useMemo } from 'react';
import { IMutField } from 'graphic-walker/dist/interfaces';
import 'graphic-walker/dist/style.css';
import { IMutField } from '@kanaries/graphic-walker/dist/interfaces';
import '@kanaries/graphic-walker/dist/style.css';

const VisualInterface: React.FC = props => {
const { dataSourceStore } = useGlobalStore();
Expand Down
20 changes: 15 additions & 5 deletions packages/graphic-walker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ Main features:
+ A Data Explainer which explain some why some patterns occur / what may cause them.

## Usage

### use as an embedding module
```bash
cd graphic-waler
yarn install
npm run build
yarn add @kanaries/graphic-walker

# or

npm i --save @kanaries/graphic-walker
```

In your app:
```typescript
import { GraphicWalker } from 'graphic-walker';
import { GraphicWalker } from '@kanaries/graphic-walker';
import 'graphic-walker/dist/style.css'

const YourEmbeddingTableauStyleApp: React.FC = props => {
Expand All @@ -30,4 +34,10 @@ const YourEmbeddingTableauStyleApp: React.FC = props => {
}

export default YourEmbeddingTableauStyleApp;
```
```

### try local
```bash
# packages/graphic-walker
npm run dev
```
3 changes: 2 additions & 1 deletion packages/graphic-walker/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "graphic-walker",
"name": "@kanaries/graphic-walker",
"version": "0.0.1",
"scripts": {
"dev": "vite",
Expand Down Expand Up @@ -29,6 +29,7 @@
"react-beautiful-dnd": "^13.1.0",
"react-dom": "^17.0.2",
"react-json-view": "^1.21.3",
"rxjs": "^7.3.0",
"styled-components": "^5.3.0",
"tailwindcss": "^2.2.15",
"vega": "^5.20.2",
Expand Down
138 changes: 69 additions & 69 deletions packages/graphic-walker/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,82 +17,82 @@ import "tailwindcss/tailwind.css"
import './index.css'

export interface EditorProps {
dataSource?: Record[];
rawFields?: IMutField[];
dataSource?: Record[];
rawFields?: IMutField[];
}

const App: React.FC<EditorProps> = props => {
const { dataSource = [], rawFields = [] } = props;
const { commonStore } = useGlobalStore();
const [insightReady, setInsightReady] = useState<boolean>(true);
const { dataSource = [], rawFields = [] } = props;
const { commonStore } = useGlobalStore();
const [insightReady, setInsightReady] = useState<boolean>(true);

const { currentDataset, datasets, vizEmbededMenu } = commonStore;
const { currentDataset, datasets, vizEmbededMenu } = commonStore;

// use as an embeding module, use outside datasource from props.
useEffect(() => {
if (dataSource.length > 0) {
commonStore.addAndUseDS({
name: 'context dataset',
dataSource: dataSource,
rawFields
})
}
}, [dataSource, rawFields])
// use as an embeding module, use outside datasource from props.
useEffect(() => {
if (dataSource.length > 0) {
commonStore.addAndUseDS({
name: 'context dataset',
dataSource: dataSource,
rawFields
})
}
}, [dataSource, rawFields])

// do preparation analysis work when using a new dataset
useEffect(() => {
const ds = currentDataset;
if (ds && ds.dataSource.length > 0 && ds.rawFields.length > 0) {
setInsightReady(false)
preAnalysis({
dataSource: ds.dataSource,
fields: toJS(ds.rawFields)
}).then(() => {
setInsightReady(true);
})
}
return () => {
destroyWorker();
}
}, [currentDataset]);
// do preparation analysis work when using a new dataset
useEffect(() => {
const ds = currentDataset;
if (ds && ds.dataSource.length > 0 && ds.rawFields.length > 0) {
setInsightReady(false)
preAnalysis({
dataSource: ds.dataSource,
fields: toJS(ds.rawFields)
}).then(() => {
setInsightReady(true);
})
}
return () => {
destroyWorker();
}
}, [currentDataset]);

return (
<div className="App">
<DataSourceSegment preWorkDone={insightReady} />
<VisualSettings />
<Container>
<div className="grid grid-cols-6">
<div className="col-span-1">
<DatasetFields />
</div>
<div className="col-span-1">
<AestheticFields />
</div>
<div className="col-span-4">
<div>
<PosFields />
</div>
<NestContainer style={{ minHeight: '600px', overflow: 'auto' }}>
{datasets.length > 0 && <ReactiveRenderer />}
<InsightBoard />
{vizEmbededMenu.show && (
<ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
<div
onClick={() => {
commonStore.closeEmbededMenu();
commonStore.setShowInsightBoard(true)
}}
>
深度解读
</div>
</ClickMenu>
)}
</NestContainer>
</div>
</div>
</Container>
</div>
)
return (
<div className="App">
<DataSourceSegment preWorkDone={insightReady} />
<VisualSettings />
<Container>
<div className="grid grid-cols-6">
<div className="col-span-1">
<DatasetFields />
</div>
<div className="col-span-1">
<AestheticFields />
</div>
<div className="col-span-4">
<div>
<PosFields />
</div>
<NestContainer style={{ minHeight: '600px', overflow: 'auto' }}>
{datasets.length > 0 && <ReactiveRenderer />}
<InsightBoard />
{vizEmbededMenu.show && (
<ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
<div
onClick={() => {
commonStore.closeEmbededMenu();
commonStore.setShowInsightBoard(true)
}}
>
深度解读
</div>
</ClickMenu>
)}
</NestContainer>
</div>
</div>
</Container>
</div>
)
}

export default observer(App);
7 changes: 2 additions & 5 deletions packages/graphic-walker/src/Fields/AestheticFields.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import React from 'react';
import {
DragDropContext,
Droppable,
Draggable,
DropResult,
ResponderProvided,
DraggableLocation,
} from "react-beautiful-dnd";
import { AGGREGATOR_LIST, DRAGGABLE_STATE_KEYS } from './fieldsContext';
import { AestheticFieldContainer, FieldsContainer, Pill } from './components'
Expand All @@ -25,8 +21,9 @@ const AestheticFields: React.FC = props => {
{...provided.droppableProps}
ref={provided.innerRef}
>
{provided.placeholder}
{draggableFieldState[dkey.id].map((f, index) => (
<Draggable key={f.id} draggableId={f.id} index={index}>
<Draggable key={`${dkey.id}-${f.id}`} draggableId={`${dkey.id}-${f.id}`} index={index}>
{(provided, snapshot) => {
return (
<Pill
Expand Down
101 changes: 63 additions & 38 deletions packages/graphic-walker/src/Fields/DatasetFields.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,82 @@
import React from 'react';
import { FieldListContainer, FieldsContainer, Pill } from "./components";
import { NestContainer } from '../components/container'
import { observer } from 'mobx-react-lite';
import {
DragDropContext,
Droppable,
Draggable,
DropResult,
ResponderProvided,
DraggableLocation,
} from "react-beautiful-dnd";

import { AGGREGATOR_LIST, DRAGGABLE_STATE_KEYS } from './fieldsContext';
import { DraggableFieldState } from './fieldsContext';
import { useGlobalStore } from '../store';
import DataTypeIcon from '../components/dataTypeIcon';
import { IViewField } from '../interfaces';

const FIELDS_KEY: keyof DraggableFieldState = 'fields';

const rowsAndCols = DRAGGABLE_STATE_KEYS.filter(f => f.id === 'fields');
const DatasetFields: React.FC = props => {
const { vizStore } = useGlobalStore();
const { draggableFieldState } = vizStore;
const { fields } = draggableFieldState;

const dimensions: IViewField[] = []; // = draggableFieldState[FIELDS_KEY].filter(f => f.type === 'D');
const measures: IViewField[] = []; // = draggableFieldState[FIELDS_KEY].filter(f => f.type === 'M');
const dimOriginIndices: number[] = [];
const meaOriginIndices: number[] = [];

for (let i = 0; i < fields.length; i++) {
if (fields[i].type === 'D') {
dimensions.push(fields[i]);
dimOriginIndices.push(i)
} else {
measures.push(fields[i])
meaOriginIndices.push(i);
}
}

return <NestContainer style={{ minHeight: '680px', overflowY: 'auto' }}>
<h4 className="text-xs mb-2">字段列表</h4>
{
rowsAndCols.map(dkey => <div key={dkey.id}>
<Droppable droppableId={dkey.id} direction="vertical">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{draggableFieldState[dkey.id].map((f, index) => (
<Draggable key={f.id} draggableId={f.id} index={index}>
{(provided, snapshot) => {
return (
<div
className="pt-0.5 pb-0.5 pl-2 pr-2 m-1 text-xs hover:bg-blue-100 rounded-full"
ref={provided.innerRef}
// type={f.type}
// colType={f.type === 'D' ? 'discrete' : 'continuous'}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{f.name}&nbsp;
</div>
);
}}
</Draggable>
))}
</div>
)}
</Droppable>
</div>)
}
<Droppable droppableId={FIELDS_KEY} direction="vertical">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
>
{provided.placeholder}
{dimensions.map((f, index) => (
<Draggable key={f.id} draggableId={f.id} index={dimOriginIndices[index]}>
{(provided, snapshot) => {
return (
<div
className="pt-0.5 pb-0.5 pl-2 pr-2 m-1 text-xs hover:bg-blue-100 rounded-full"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<DataTypeIcon dataType="string" /> {f.name}&nbsp;
</div>
);
}}
</Draggable>
))}
{measures.map((f, index) => (
<Draggable key={f.id} draggableId={f.id} index={meaOriginIndices[index]}>
{(provided, snapshot) => {
return (
<div
className="pt-0.5 pb-0.5 pl-2 pr-2 m-1 text-xs hover:bg-blue-100 rounded-full"
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<DataTypeIcon dataType="number" /> {f.name}&nbsp;
</div>
);
}}
</Draggable>
))}
</div>
)}
</Droppable>
</NestContainer>
}

Expand Down
Loading

0 comments on commit 0cccbce

Please sign in to comment.