{"id":16200845,"url":"https://github.com/observedobserver/pivot-chart","last_synced_at":"2025-03-16T10:32:51.847Z","repository":{"id":41788617,"uuid":"147664443","full_name":"ObservedObserver/pivot-chart","owner":"ObservedObserver","description":"light and fast implementation of web pivot table / pivot chart components.","archived":false,"fork":false,"pushed_at":"2024-03-28T18:38:00.000Z","size":1692,"stargazers_count":99,"open_issues_count":5,"forks_count":25,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-27T07:51:12.779Z","etag":null,"topics":["business-intelligence","chart","cube","data-exploration","data-visualization","eda","excel","olap","pivot-chart","pivot-table","pivot-tables","react","tableau","typescript","visualization"],"latest_commit_sha":null,"homepage":"https://pivot-chart.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ObservedObserver.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-09-06T11:37:12.000Z","updated_at":"2024-12-19T04:49:42.000Z","dependencies_parsed_at":"2024-02-05T03:45:43.363Z","dependency_job_id":"cdac5cca-2e95-402b-ad55-239ebf518b38","html_url":"https://github.com/ObservedObserver/pivot-chart","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ObservedObserver%2Fpivot-chart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ObservedObserver%2Fpivot-chart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ObservedObserver%2Fpivot-chart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ObservedObserver%2Fpivot-chart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ObservedObserver","download_url":"https://codeload.github.com/ObservedObserver/pivot-chart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243814907,"owners_count":20352037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["business-intelligence","chart","cube","data-exploration","data-visualization","eda","excel","olap","pivot-chart","pivot-table","pivot-tables","react","tableau","typescript","visualization"],"created_at":"2024-10-10T09:34:30.335Z","updated_at":"2025-03-16T10:32:51.398Z","avatar_url":"https://github.com/ObservedObserver.png","language":"TypeScript","readme":"\u003cimg src=\"https://ch-resources.oss-cn-shanghai.aliyuncs.com/images/lang-icons/icon128px.png\" width=\"22px\" /\u003e English | [简体中文](./README.zh-CN.md)\n\n# Pivot-Chart\n![](https://img.shields.io/npm/v/pivot-chart)\n![](https://img.shields.io/github/license/observedobserver/pivot-chart)\n![](https://img.shields.io/github/issues-pr/observedobserver/pivot-chart)\n![](https://img.shields.io/github/actions/workflow/status/observedobserver/pivot-chart/build.yml)\n\nPivot-Chart is a comprehensive data visualization tool that enhances pivot tables with visualizations and charts, offering a more intuitive and informative way to analyze your data. With Pivot-Chart, you can elevate your data analysis and take a new approach to exploring your data. Unlike other pivot-table + vis solution, pivot chart retains nest multi-level aggregation feature of pivot table, which allows you to compare between different aggregation levels and make it easier to drill down and roll up.\n\nIntegrating Pivot-Chart into your data applications is easy, simply import the npm package. \n\n\u003e While this is a proof-of-concept project, your feedback and suggestions are valued and appreciated.\n\n`pivot-chart` also provide with basic pivot table components for you to build your web apps, you can regard pivot table as a member in the subset of pivot charts.\n\n## Demo\n\n[Online Demo](https://pivot-chart.vercel.app/)\n\n## Features\n\n| feature | demo(gif) |\n| - | - |\n| basic expandable nest/cross table | ![basic expandable nest/cross table.gif](https://ch-resources.oss-cn-shanghai.aliyuncs.com/images/pivot-chart/pivot-table-basic.gif) |\n| custom aggregator of measures | ![ustom aggregator of measures.gif](https://ch-resources.oss-cn-shanghai.aliyuncs.com/images/pivot-chart/pivot-table-aggregator.gif) |\n| different visualization type | ![different visualization type.gif](https://ch-resources.oss-cn-shanghai.aliyuncs.com/images/pivot-chart/pivot-chart-light.gif)\u003cbr /\u003e \u003cimg width=\"100%\" src=\"https://ch-resources.oss-cn-shanghai.aliyuncs.com/images/pivot-chart/pivot-chart-static-bar.jpg\" /\u003e \u003cbr /\u003e \u003cimg width=\"100%\" src=\"https://ch-open-sharing.oss-us-west-1.aliyuncs.com/pivot-chart-scatter.png\" /\u003e |\n\n## Usage\n\n\u003cbr /\u003einstall npm package.\u003cbr /\u003e\n\n```bash\nnpm i --save pivot-chart\n\n# or\n\nyarn add pivot-chart\n```\n\n\u003cbr /\u003ebasic usage.\u003cbr /\u003e\n\n```javascript\nimport { PivotChart } from 'pivot-chart';\n\nfunction App () {\n  return \u003cPivotChart\n    visType={visType}\n    dataSource={data}\n    rows={rows}\n    columns={columns}\n    measures={measures} \n    /\u003e\n}\n```\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003edemo above can be run locally with\u003cbr /\u003e\n\n```bash\n# init development environment\nyarn workspace react-pivot-table initenv\n# start dev server\nyarn workspace react-pivot-table dev\n```\n\n\n\u003ca name=\"API\"\u003e\u003c/a\u003e\n## API\n\n\n\u003ca name=\"Types\"\u003e\u003c/a\u003e\n### Types\n| Type | Desc |\n| --- | --- |\n| Field | \u003cbr /\u003e- `id` \u003cstring\u003e\u003cbr /\u003e- `name` \u003cstring\u003e\u003cbr /\u003e- `aggName` \u003cstring\u003e aggregator's name.\u003cbr /\u003e- `cmp` \u003c(a: any, b: any) =\u003e number\u003e\u003cbr /\u003e |\n| Measure | extends Field\u003cbr /\u003e- `aggregator` aggregator function.\u003cbr /\u003e- `minWidth` \u003cnumber\u003e\u003cbr /\u003e- `formatter` \u003cvalue: number\u003e =\u003e number | string | ReactNode\u003cbr /\u003e |\n| VisType | currently support `number` , `bar` , `line` , `scatter` . |\n| Record | a plain javascript object |\n| DataSource | `Record[]` , Array of Record. |\n| QueryNode | \u003cbr /\u003e- `dimCode` \u003cstring\u003e\u003cbr /\u003e- `dimValue` \u003cstring\u003e\u003cbr /\u003e |\n| QueryPath | \u003cQueryNode[]\u003e.\u003cbr /\u003eexample: `[{dimCode: 'Sex', dimValue: 'male'}, {dimCode: 'Age', dimValue:'*'}]`   |\n\n\n\u003cbr /\u003e\n\n\u003ca name=\"jzdLj\"\u003e\u003c/a\u003e\n### common\ncommon interface are those used in both async pivot table and sync pivot table.\n\n- **rows**: `Field[]` dimensions in row. required\n- **columns**: `Field[]` dimensions in column.required\n- **measures**: `Measure[]` measures or indicators.required\n- **visType**: `VisType` mark type in cell. `number` as default(which is a common pivot table).optional\n- **defaultExpandedDepth**. default expanded level in row or column nest tree.optional\n  - defaultExpandedDepth.rowDepth: `number` \n  - defaultExpandedDepth.columnDepth: `number` \n- **showAggregatedNode**: `{row: boolean; column: boolean}` whether display aggregated node for each group. optional\n\u003ca name=\"f7jCH\"\u003e\u003c/a\u003e\n#### SyncPivotTable\n\n- **dataSource**: `Record[]` . Array of record(normaly plain object). (json style data array). required\n\n\n\u003cbr /\u003e\nexample:\n\n```javascript\nimport React, { useEffect, useState, useMemo } from 'react';\nimport ReactDOM from 'react-dom';\nimport { getTitanicData } from './mock';\nimport { ToolBar, PivotChart, DragableFields, Aggregators, DataSource, VisType, DraggableFieldState } from '../src/index';\n\nconst { dataSource, dimensions, measures } = getTitanicData();\nconst fields = dimensions.concat(measures).map(f =\u003e ({ id: f, name: f }));\n\nconst initDraggableState: DraggableFieldState = {\n  fields: [],\n  rows: [],\n  columns: [],\n  measures: []\n};\n\nfunction App () {\n  const [data, setData] = useState\u003cDataSource\u003e([]);\n  const [fstate, setFstate] = useState\u003cDraggableFieldState\u003e(initDraggableState)\n  const [visType, setVisType] = useState\u003cVisType\u003e('number');\n  useEffect(() =\u003e {\n    setData(dataSource);\n  }, [])\n  const measures = useMemo(() =\u003e fstate['measures'].map(f =\u003e ({\n    ...f,\n    aggregator: Aggregators[(f.aggName || 'sum') as keyof typeof Aggregators]\n  })), [fstate['measures']]);\n  return \u003cdiv\u003e\n    \u003cDragableFields onStateChange={(state) =\u003e {setFstate(state)}} fields={fields} /\u003e\n    \u003cToolBar visType={visType} onVisTypeChange={(type) =\u003e { setVisType(type) }} /\u003e\n    \u003cPivotChart visType={visType} dataSource={data} rows={fstate['rows']} columns={fstate['columns']} measures={measures} /\u003e\n  \u003c/div\u003e\n}\n\nReactDOM.render(\u003cApp /\u003e, document.getElementById('root'))\n```\n\n\u003ca name=\"8ppwU\"\u003e\u003c/a\u003e\n\n#### AsyncPivotTable (WIP)\n\n- **cubeQuery**: `(path: QueryPath, measures: string[]) =\u003e Promise;` . A function handle for cube query. path is the groupby dimension path(made of a series of dimension and its value). measures are the fields needed to be aggregated. required\n- **branchFilter**: bad api, not recommanded to use it. a fake filter whihc only control display of node and not influence the aggregated result of parent node. optional\n- **dimensionCompare**: `(a: string, b: string) =\u003e number` .compare function for sort of dimension node. optional\n\n\n\n```javascript\nfunction AsyncApp () {\n  \n  const [data, setData] = useState\u003cDataSource\u003e([]);\n  const [fields, setFields] = useState\u003cField[]\u003e([]);\n  const [fstate, setFstate] = useState\u003cDraggableFieldState\u003e(initDraggableState)\n  const [visType, setVisType] = useState\u003cVisType\u003e('number');\n  useEffect(() =\u003e {\n    const { dataSource, dimensions, measures } = getTitanicData();\n    setData(dataSource);\n    const fs: Field[] = [...dimensions, ...measures].map((f: string) =\u003e ({ id: f, name: f }));\n    setFields(fs);\n  }, [])\n  const measures = useMemo(() =\u003e fstate['measures'].map(f =\u003e ({\n    ...f,\n    aggregator: Aggregators[(f.aggName || 'sum') as keyof typeof Aggregators],\n    minWidth: 100,\n    formatter: f.id === 'Survived' \u0026\u0026 ((val: any) =\u003e `${val} *`)\n  })), [fstate['measures']]);\n  const cubeQuery = useCallback(async (path: QueryPath, meas: string[]) =\u003e {\n    const ops = measures.filter(m =\u003e meas.includes(m.id)).map(m =\u003e m.aggName || 'sum');\n    return TitanicCubeService(path.map(p =\u003e p.dimCode), meas, ops);\n  }, [measures])\n  return \u003cdiv\u003e\n    \u003cDragableFields onStateChange={(state) =\u003e {setFstate(state)}} fields={fields} /\u003e\n    \u003cToolBar visType={visType} onVisTypeChange={(type) =\u003e { setVisType(type) }} /\u003e\n    \u003cAsyncPivotChart\n      visType={visType}\n      rows={fstate['rows']}\n      columns={fstate['columns']}\n      async\n      defaultExpandedDepth={{\n        rowDepth: 20,\n        columnDepth: 20\n      }}\n      cubeQuery={cubeQuery}\n      measures={measures} /\u003e\n  \u003c/div\u003e\n}\n```\n\n\n\u003ca name=\"yi5rr\"\u003e\u003c/a\u003e\n### Theme\n\n- Theme.registerTheme(theme: ThemeConfig)\n- `ThemeConfig` \n\n\n\n```typescript\ninterface ThemeConfig {\n  root?: {\n    display?: boolean,\n    label?: string\n  },\n  summary?: {\n    label?: string\n  },\n  table?: {\n    thead?: {\n      backgroundColor?: string;\n      color?: string;\n    }\n    borderColor?: string;\n    color?: string;\n  }\n}\n// default config\nconst THEME_CONFIG: ThemeConfig = {\n  root: {\n    display: true,\n    label: 'All'\n  },\n  summary: {\n    label: '(total)'\n  },\n  table: {\n    thead: {\n      backgroundColor: '#E9EDF2',\n      color: '#5A6C84'\n    },\n    borderColor: '#DFE3E8',\n    color: '#333333'\n  }\n};\n```\n\n\n```javascript\nimport React, { useEffect, useState, useMemo } from 'react';\nimport ReactDOM from 'react-dom';\nimport { getTitanicData } from './mock';\nimport { ToolBar, PivotChart, DragableFields, Aggregators, DataSource, VisType, DraggableFieldState, Theme } from '../src/index';\n\nconst { dataSource, dimensions, measures } = getTitanicData();\nconst fields = dimensions.concat(measures).map(f =\u003e ({ id: f, name: f }));\n\nconst initDraggableState: DraggableFieldState = {\n  fields: [],\n  rows: [],\n  columns: [],\n  measures: []\n};\n\nTheme.registerTheme({\n  root: {\n    display: true,\n    label: 'root'\n  },\n  summary: {\n    label: '(total)'\n  }\n})\n\nfunction App () {\n  const [data, setData] = useState\u003cDataSource\u003e([]);\n  const [fstate, setFstate] = useState\u003cDraggableFieldState\u003e(initDraggableState)\n  const [visType, setVisType] = useState\u003cVisType\u003e('number');\n  useEffect(() =\u003e {\n    setData(dataSource);\n  }, [])\n  const measures = useMemo(() =\u003e fstate['measures'].map(f =\u003e ({\n    ...f,\n    aggregator: Aggregators[(f.aggName || 'sum') as keyof typeof Aggregators]\n  })), [fstate['measures']]);\n  return \u003cdiv\u003e\n    \u003cDragableFields onStateChange={(state) =\u003e {setFstate(state)}} fields={fields} /\u003e\n    \u003cToolBar visType={visType} onVisTypeChange={(type) =\u003e { setVisType(type) }} /\u003e\n    \u003cPivotChart visType={visType} dataSource={data} rows={fstate['rows']} columns={fstate['columns']} measures={measures} /\u003e\n  \u003c/div\u003e\n}\n\nReactDOM.render(\u003cApp /\u003e, document.getElementById('root'))\n```\n\n\u003ca name=\"OPzwK\"\u003e\u003c/a\u003e\n### Common Question\n\u003e SyncPivotChart vs. AsyncPivotChart ?\n\nSync Pivot Chart does all cube computaion in frontend.(In future, it may do those work in webworker and it will seems to be async). \n\u003cbr /\u003e\nAsync Pivot Chart can use cube query from server or customed implementation(either on server or browser, async or sync), but developer need to figure out how to speed up those by themsleves.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobservedobserver%2Fpivot-chart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobservedobserver%2Fpivot-chart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobservedobserver%2Fpivot-chart/lists"}