├── src ├── index.css ├── react-app-env.d.ts ├── api │ ├── getCompany.tsx │ ├── getCompanyTopics.tsx │ ├── getCompanyPeerMap.tsx │ ├── getCompanyNews.tsx │ ├── getCompanySectorMap.tsx │ ├── getCompanySdgScores.tsx │ ├── getCompanyDataPoints.tsx │ ├── getCompanyKeyInformation.tsx │ ├── getCompanies.tsx │ ├── getCompanyCumulativeScores.tsx │ ├── getCompanyScores.tsx │ ├── httpRequest.tsx │ ├── liveHttpRequest.tsx │ └── getCompanySubTopics.tsx ├── setupTests.ts ├── context │ ├── LoadingContext.tsx │ ├── TopicContext.tsx │ ├── CompanyContext.tsx │ └── ThemeContext.tsx ├── App.test.tsx ├── pages │ └── Reports │ │ ├── Header.tsx │ │ ├── CompanyContext.tsx │ │ ├── components │ │ ├── Ratings.tsx │ │ ├── UNGoals.tsx │ │ ├── Rating.tsx │ │ ├── SectorMap.tsx │ │ ├── SelectCompany.tsx │ │ ├── PeerComparisons.tsx │ │ ├── NewsSentiment.tsx │ │ ├── Summary.tsx │ │ ├── NewsOrder.tsx │ │ └── TopicScores.tsx │ │ ├── Report.tsx │ │ └── index.tsx ├── utils │ ├── Number.tsx │ ├── getCompanyName.tsx │ └── Report.tsx ├── reportWebVitals.ts ├── index.tsx ├── components │ ├── PageLoading.tsx │ ├── d3 │ │ ├── data.ts │ │ ├── DataPoints.tsx │ │ ├── PieChart.tsx │ │ ├── VerticalBarChart.tsx │ │ ├── MultiLineChart.tsx │ │ └── ScatterPlot.tsx │ ├── DarkModeToggle.tsx │ └── ReportTable.tsx ├── App.css ├── App.tsx ├── logo.svg ├── types │ └── Type.tsx └── global.d.ts ├── Procfile ├── public ├── _redirects ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── postcss.config.js ├── tailwind.config.js ├── .env ├── .gitignore ├── tsconfig.json ├── package.json └── README.md /src/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: yarn start -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirannasim/react-d3-typescript-tailwind/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirannasim/react-d3-typescript-tailwind/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirannasim/react-d3-typescript-tailwind/HEAD/public/logo512.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./src/**/*.{js,jsx,ts,tsx}'], 3 | darkMode: 'class', 4 | theme: { 5 | extend: { 6 | width: {}, 7 | }, 8 | }, 9 | plugins: [require('tw-elements/dist/plugin')], 10 | } 11 | -------------------------------------------------------------------------------- /src/api/getCompany.tsx: -------------------------------------------------------------------------------- 1 | import { httpRequest } from './httpRequest' 2 | 3 | export const getCompany = async (company_id: string) => { 4 | return await httpRequest().get( 5 | '/' + company_id + '&api_key=' + process.env.REACT_APP_ESG_API_Key, 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/api/getCompanyTopics.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanyTopics = async (company_id: string) => { 4 | return await liveHttpRequest().get( 5 | '/' + company_id + '/topics?&api_key=' + process.env.REACT_APP_ESG_API_Key, 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /src/api/getCompanyPeerMap.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanyPeerMap = async (company_id: string) => { 4 | return await liveHttpRequest().get( 5 | '/' + company_id + '/peer_map?api_key=' + process.env.REACT_APP_ESG_API_Key, 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /src/context/LoadingContext.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface ILoadingContext { 4 | loading: boolean 5 | setLoading?: (val: boolean) => void 6 | } 7 | 8 | const defaultState = { 9 | loading: true, 10 | } 11 | 12 | export const LoadingContext = React.createContext(defaultState) 13 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/api/getCompanyNews.tsx: -------------------------------------------------------------------------------- 1 | import { httpRequest } from './httpRequest' 2 | import { liveHttpRequest } from './liveHttpRequest' 3 | 4 | export const getCompanyNews = async (company_id: string) => { 5 | return await liveHttpRequest().get( 6 | '/' + company_id + '/news?api_key=' + process.env.REACT_APP_ESG_API_Key, 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /src/api/getCompanySectorMap.tsx: -------------------------------------------------------------------------------- 1 | import { httpRequest } from './httpRequest' 2 | 3 | export const getCompanySectorMap = async (company_id: string) => { 4 | return await httpRequest().get( 5 | '/' + 6 | company_id + 7 | '/sector_map?api_key=' + 8 | process.env.REACT_APP_ESG_API_Key, 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/context/TopicContext.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ITopicContext { 4 | report_topic: string; 5 | setReportTopic?: (val: string) => void; 6 | } 7 | 8 | const defaultState = { 9 | report_topic: "", 10 | }; 11 | 12 | export const TopicContext = React.createContext(defaultState); 13 | -------------------------------------------------------------------------------- /src/api/getCompanySdgScores.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanySdgScores = async (company_id: string) => { 4 | return await liveHttpRequest().get( 5 | '/' + 6 | company_id + 7 | '/sdg_scores?api_key=' + 8 | process.env.REACT_APP_ESG_API_Key, 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/api/getCompanyDataPoints.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanyDataPoints = async (company_id: string) => { 4 | return await liveHttpRequest().get( 5 | '/' + 6 | company_id + 7 | '/score_counts?api_key=' + 8 | process.env.REACT_APP_ESG_API_Key, 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/Reports/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface Props {} 4 | 5 | export const Header: React.FC = () => { 6 | return ( 7 |
8 |

9 | Company ESG Profile 10 |

11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/api/getCompanyKeyInformation.tsx: -------------------------------------------------------------------------------- 1 | import { httpRequest } from './httpRequest' 2 | import { liveHttpRequest } from './liveHttpRequest' 3 | 4 | export const getCompanyKeyInformation = async (company_id: string) => { 5 | return await liveHttpRequest().get( 6 | '/' + 7 | company_id + 8 | '/key_information?api_key=' + 9 | process.env.REACT_APP_ESG_API_Key, 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /src/api/getCompanies.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanies = async (company_type: string, restrict: boolean) => { 4 | return await liveHttpRequest().get( 5 | '/' + 6 | '?company_type=' + 7 | company_type + 8 | '&restrict=' + 9 | restrict + 10 | '&api_key=' + 11 | process.env.REACT_APP_ESG_API_Key, 12 | ) 13 | } -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_LIVE_ESG_API_URL=https://corsproblemsolve.herokuapp.com/http://52.70.86.213:8000/api/v1.0/esg/companies/ 2 | #REACT_APP_LIVE_ESG_API_URL=https://corsproblemsolve.herokuapp.com/http://52.70.86.213:8000/api/v1.0/esg/companies/ 3 | REACT_APP_ESG_API_Key=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJndWVzdCIsImtleV9uYW1lIjoidGVzdCIsImV4cCI6MTY0OTU5MDE3Ni4zNDQxMDd9.lODjOPMZNdvH3QLHzriflLi0hQt39s4KjHd9LwIeuvg 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/context/CompanyContext.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ICompanyContext { 4 | company_id: string; 5 | company_name: string; 6 | setCompanyId?: (val: string) => void; 7 | setCompanyName?: (val: string) => void; 8 | } 9 | 10 | const defaultState = { 11 | company_id: "", 12 | company_name: "", 13 | }; 14 | 15 | export const CompanyContext = 16 | React.createContext(defaultState); 17 | -------------------------------------------------------------------------------- /src/utils/Number.tsx: -------------------------------------------------------------------------------- 1 | export const getMaxNumberUnit = (value: number) => { 2 | let str = Math.ceil(value).toString(); 3 | let digits = str.length; 4 | let unit = str.substring(0, 1); 5 | if (unit && parseInt(unit) < 5) { 6 | unit = "5"; 7 | } else { 8 | unit = "1"; 9 | digits++; 10 | } 11 | 12 | for (let i = 0; i < digits - 1; i++) { 13 | unit += "0"; 14 | } 15 | return parseInt(unit); 16 | }; 17 | -------------------------------------------------------------------------------- /src/pages/Reports/CompanyContext.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ICompanyContext { 4 | company_id: string; 5 | company_name: string; 6 | setCompanyId?: (val: string) => void; 7 | setCompanyName?: (val: string) => void; 8 | } 9 | 10 | const defaultState = { 11 | company_id: "", 12 | company_name: "", 13 | }; 14 | 15 | export const CompanyContext = 16 | React.createContext(defaultState); 17 | -------------------------------------------------------------------------------- /src/api/getCompanyCumulativeScores.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanyCumulativeScores = async ( 4 | company_id: string, 5 | topic: string, 6 | ) => { 7 | return await liveHttpRequest().get( 8 | '/' + 9 | company_id + 10 | '/cumulative_scores' + 11 | '?topics=' + 12 | topic + 13 | '&api_key=' + 14 | process.env.REACT_APP_ESG_API_Key, 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/getCompanyName.tsx: -------------------------------------------------------------------------------- 1 | import { TypeCompany } from "../types/Type"; 2 | 3 | export const getCompanyName = ( 4 | company_id: string, 5 | companies: TypeCompany[] 6 | ) => { 7 | var ticker_name = ""; 8 | 9 | for (let i = 0; i < companies.length; i++) { 10 | if (companies[i].ticker_id === company_id.replace("%20", " ")) { 11 | ticker_name = companies[i].ticker_name; 12 | break; 13 | } 14 | } 15 | return ticker_name; 16 | }; 17 | -------------------------------------------------------------------------------- /src/api/getCompanyScores.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanyScores = async ( 4 | company_id: string, 5 | topics: string, 6 | sub_topics: string, 7 | ) => { 8 | return await liveHttpRequest().get( 9 | '/' + 10 | company_id + 11 | '/scores/?topics=' + 12 | topics + 13 | '&subtopics=' + 14 | sub_topics + 15 | '&api_key=' + 16 | process.env.REACT_APP_ESG_API_Key, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/api/httpRequest.tsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const axiosInstance = axios.create({ 4 | headers: { 5 | 'X-Requested-With': 'XMLHttpRequest', 6 | Authorization: 7 | 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJndWVzdCIsImtleV9uYW1lIjoiaXZhbiIsImV4cCI6MTY0NTY4NzYzMy4zNDcwNzN9.k1lfcAyTUBuKp-WdjZoWQuxJpmX7Bdr3QBsTH6cCJMY', 8 | }, 9 | baseURL: process.env.REACT_APP_ESG_API_URL, 10 | }) 11 | 12 | export function httpRequest() { 13 | return axiosInstance 14 | } 15 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/api/liveHttpRequest.tsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const axiosInstance = axios.create({ 4 | headers: { 5 | 'X-Requested-With': 'XMLHttpRequest', 6 | Authorization: 7 | 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJndWVzdCIsImtleV9uYW1lIjoidGVzdCIsImV4cCI6MTY0OTU5MDE3Ni4zNDQxMDd9.lODjOPMZNdvH3QLHzriflLi0hQt39s4KjHd9LwIeuvg', 8 | }, 9 | baseURL: process.env.REACT_APP_LIVE_ESG_API_URL, 10 | }) 11 | 12 | export function liveHttpRequest() { 13 | return axiosInstance 14 | } 15 | -------------------------------------------------------------------------------- /src/api/getCompanySubTopics.tsx: -------------------------------------------------------------------------------- 1 | import { liveHttpRequest } from './liveHttpRequest' 2 | 3 | export const getCompanySubTopics = async (company_id: string) => { 4 | return await liveHttpRequest().get( 5 | '/' + 6 | company_id + 7 | '/subtopic_performance?api_key=' + 8 | process.env.REACT_APP_ESG_API_Key, 9 | ) 10 | } 11 | // export const getCompanySubTopics = async ( 12 | // company_id: string, 13 | // topic_name: string 14 | // ) => { 15 | // return await httpRequest().get( 16 | // "/" + company_id + "/topics/" + topic_name + "/subtopics" 17 | // ); 18 | // }; 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"] 20 | } 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import reportWebVitals from './reportWebVitals' 6 | import { ThemeProvider } from './context/ThemeContext' 7 | 8 | ReactDOM.render( 9 | 10 | {/* */} 11 | 12 | {/* */} 13 | , 14 | document.getElementById('root'), 15 | ) 16 | 17 | // If you want to start measuring performance in your app, pass a function 18 | // to log results (for example: reportWebVitals(console.log)) 19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 20 | reportWebVitals() 21 | -------------------------------------------------------------------------------- /src/components/PageLoading.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const PageLoading: React.FC = () => { 4 | return ( 5 |
6 |
7 |
8 | 9 |
10 |
14 | Loading... 15 |
16 |
17 |
18 |
19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/d3/data.ts: -------------------------------------------------------------------------------- 1 | export const wbData = { 2 | data_type: 'peer map', 3 | data: [ 4 | { 5 | ticker_name: 'apple inc', 6 | ticker_market_cap: 1971700000000, 7 | signals_average: -0.5, 8 | signals_count: 8551, 9 | }, 10 | { 11 | ticker_name: 'tata consultancy svcs ltd', 12 | ticker_market_cap: 133855000000, 13 | signals_average: 0.827, 14 | signals_count: 2081, 15 | }, 16 | { 17 | ticker_name: 'infosys ltd', 18 | ticker_market_cap: 62566227968, 19 | signals_average: 0.147, 20 | signals_count: 1953, 21 | }, 22 | { 23 | ticker_name: 'intl business machines corp', 24 | ticker_market_cap: 105065000000, 25 | signals_average: 0.412, 26 | signals_count: 3480, 27 | }, 28 | { 29 | ticker_name: 'accenture plc', 30 | ticker_market_cap: 151468000000, 31 | signals_average: 0.857, 32 | signals_count: 1596, 33 | }, 34 | ], 35 | } 36 | -------------------------------------------------------------------------------- /src/components/DarkModeToggle.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useContext } from "react"; 2 | import { ThemeContext } from "../context/ThemeContext"; 3 | import { SunIcon, MoonIcon } from "@heroicons/react/solid"; 4 | 5 | type Props = { 6 | className?: string; 7 | }; 8 | 9 | export const DarkModeToggle: React.VFC = memo(({ className }) => { 10 | const { theme, setTheme } = useContext(ThemeContext); 11 | 12 | return ( 13 |
14 | 24 |
25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer components { 6 | body { 7 | @apply transition duration-150 ease-in-out; 8 | } 9 | html.light body { 10 | background: #f5f1f0; 11 | } 12 | 13 | html.dark body { 14 | @apply bg-gray-900; 15 | } 16 | 17 | .vertical-bar:hover .bar { 18 | @apply stroke-1 stroke-black dark:stroke-white; 19 | } 20 | 21 | .vertical-bar:hover .active { 22 | @apply stroke-2; 23 | } 24 | 25 | .subtitle { 26 | @apply text-sm dark:text-white; 27 | } 28 | } 29 | 30 | .text-xxs { 31 | font-size: 0.7rem; 32 | line-height: 0.9rem; 33 | } 34 | 35 | .App { 36 | font-family: sans-serif; 37 | text-align: center; 38 | } 39 | svg { 40 | font-family: Sans-Serif, Arial; 41 | } 42 | .line { 43 | stroke-width: 2; 44 | fill: none; 45 | } 46 | 47 | .axis path { 48 | stroke: black; 49 | } 50 | 51 | .text { 52 | font-size: 1rem; 53 | } 54 | 55 | .title-text { 56 | font-size: 12px; 57 | } 58 | 59 | .w-8-12 { 60 | width: 66.66%; 61 | } 62 | 63 | .w-2-12 { 64 | width: 16.66%; 65 | } 66 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 3 | // import { Route, useRouteMatch, Switch } from 'react-router-dom'; 4 | 5 | import "./App.css"; 6 | import { DarkModeToggle } from "./components/DarkModeToggle"; 7 | import { Reports } from "./pages/Reports"; 8 | import { LoadingContext } from "./context/LoadingContext"; 9 | 10 | function App() { 11 | const [loading, setLoading] = React.useState(false); 12 | const loading_context = React.useMemo( 13 | () => ({ 14 | loading, 15 | setLoading, 16 | }), 17 | [loading] 18 | ); 19 | 20 | return ( 21 | 22 | 23 | {/* */} 24 | {/* 25 | 26 | */} 27 | 28 | 29 | } /> 30 | } /> 31 | 32 | 33 | 34 | ); 35 | } 36 | 37 | export default App; 38 | -------------------------------------------------------------------------------- /src/utils/Report.tsx: -------------------------------------------------------------------------------- 1 | export const calcScore = (average: number) => { 2 | return (100 * (average + 1)) / 2; 3 | }; 4 | 5 | export const getScoreColor = (score: number): string => { 6 | if (score >= 0 && score < 5) return "orange-900"; 7 | if (score >= 5 && score < 10) return "orange-800"; 8 | if (score >= 10 && score < 15) return "orange-700"; 9 | if (score >= 15 && score < 20) return "orange-600"; 10 | if (score >= 20 && score < 25) return "orange-500"; 11 | if (score >= 25 && score < 30) return "orange-400"; 12 | if (score >= 30 && score < 35) return "orange-300"; 13 | if (score >= 35 && score < 40) return "orange-200"; 14 | if (score >= 40 && score < 45) return "orange-100"; 15 | if (score >= 45 && score < 50) return "orange-50"; 16 | if (score >= 50 && score < 55) return "sky-50"; 17 | if (score >= 55 && score < 60) return "sky-100"; 18 | if (score >= 60 && score < 65) return "sky-200"; 19 | if (score >= 65 && score < 70) return "sky-300"; 20 | if (score >= 70 && score < 75) return "sky-400"; 21 | if (score >= 75 && score < 80) return "sky-500"; 22 | if (score >= 80 && score < 85) return "sky-600"; 23 | if (score >= 85 && score < 90) return "sky-700"; 24 | if (score >= 90 && score < 95) return "sky-800"; 25 | if (score >= 95 && score <= 100) return "sky-900"; 26 | return ""; 27 | }; 28 | -------------------------------------------------------------------------------- /src/pages/Reports/components/Ratings.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { getCompanyTopics } from '../../../api/getCompanyTopics' 3 | import { Rating } from './Rating' 4 | import { PageLoading } from '../../../components/PageLoading' 5 | 6 | interface Props { 7 | company_id: string 8 | } 9 | 10 | export const Ratings: React.FC = ({ company_id }) => { 11 | const [data, setData] = useState(null) 12 | const [loading, setLoading] = useState(true) 13 | 14 | useEffect(() => { 15 | setLoading(true) 16 | getCompanyTopics(company_id) 17 | .then((response) => { 18 | setData(response.data) 19 | }) 20 | .finally(() => { 21 | setLoading(false) 22 | }) 23 | }, [company_id]) 24 | 25 | const topics = data ? (data as string[]) : [] 26 | 27 | return loading ? ( 28 | 29 | ) : ( 30 | // loading... 31 |
32 |

ESG Ratings

33 | {!!topics.length && ( 34 |
35 |
36 |
37 | {topics.map((topic, index) => ( 38 | 39 | ))} 40 |
41 |
42 | )} 43 |
44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /src/pages/Reports/components/UNGoals.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { PieChart } from '../../../components/d3/PieChart' 3 | import { getCompanySdgScores } from '../../../api/getCompanySdgScores' 4 | import { PageLoading } from '../../../components/PageLoading' 5 | 6 | interface Props { 7 | company_id: string 8 | } 9 | 10 | export const UNGoals: React.FC = ({ company_id }) => { 11 | const [loading, setLoading] = useState(true) 12 | const [data, setData] = useState(0) 13 | 14 | useEffect(() => { 15 | getCompanySdgScores(company_id) 16 | .then((response) => { 17 | const sdg_score: number = response?.data?.data[0]?.positive_sdgs || 0 18 | setData(sdg_score / 17) 19 | }) 20 | .finally(() => { 21 | setLoading(false) 22 | }) 23 | }, [company_id]) 24 | 25 | return loading ? ( 26 | 27 | ) : ( 28 | // loading... 29 |
30 |

UN Sustainability Goals

31 |
32 |
33 |
34 | 41 |
42 |
43 |
44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /src/context/ThemeContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState, useEffect, ReactNode } from "react"; 2 | 3 | type ThemeName = "light" | "dark" | string; 4 | type ThemeContextType = { 5 | theme: ThemeName; 6 | setTheme: (name: ThemeName) => void; 7 | }; 8 | type ThemeProps = { 9 | initialTheme?: ThemeName; 10 | children: ReactNode; 11 | }; 12 | 13 | const getInitialTheme = () => { 14 | if (typeof window !== "undefined" && window.localStorage) { 15 | const storedPrefs = window.localStorage.getItem("color-theme"); 16 | if (typeof storedPrefs === "string") { 17 | return storedPrefs; 18 | } 19 | 20 | const userMedia = window.matchMedia("(prefers-color-scheme:dark)"); 21 | if (userMedia.matches) { 22 | return "dark"; 23 | } 24 | } 25 | // returning default theme here 26 | return "light"; 27 | }; 28 | 29 | export const ThemeContext = createContext( 30 | {} as ThemeContextType 31 | ); 32 | 33 | export const ThemeProvider = ({ initialTheme, children }: ThemeProps) => { 34 | const [theme, setTheme] = useState(getInitialTheme); 35 | 36 | const rawSetTheme = (theme: ThemeName) => { 37 | //Updated rawSetTheme to theme above// 38 | const root = window.document.documentElement; 39 | const isDark = theme === "dark"; 40 | 41 | root.classList.remove(isDark ? "light" : "dark"); 42 | root.classList.add(theme); 43 | 44 | localStorage.setItem("color-theme", theme); 45 | }; 46 | 47 | if (initialTheme) { 48 | rawSetTheme(initialTheme); 49 | } 50 | 51 | useEffect(() => { 52 | rawSetTheme(theme); 53 | }, [theme]); 54 | 55 | return ( 56 | 57 | {children} 58 | 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /src/pages/Reports/components/Rating.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext } from "react"; 2 | import { getCompanyScores } from "../../../api/getCompanyScores"; 3 | import { PieChart } from "../../../components/d3/PieChart"; 4 | import { TopicContext } from "../../../context/TopicContext"; 5 | import { TypeCompanyScore, TypeCompanyScores } from "../../../types/Type"; 6 | import { calcScore } from "../../../utils/Report"; 7 | import { PageLoading } from "../../../components/PageLoading"; 8 | 9 | interface Props { 10 | company_id: string; 11 | topic: string; 12 | } 13 | 14 | export const Rating: React.FC = ({ company_id, topic }) => { 15 | const [data, setData] = useState(null); 16 | const [score, setScore] = useState(0); 17 | const [loading, setLoading] = useState(true); 18 | 19 | useEffect(() => { 20 | setLoading(true); 21 | getCompanyScores(company_id, topic, "") 22 | .then((response) => { 23 | setData(response.data); 24 | }) 25 | .finally(() => { 26 | setLoading(false); 27 | }); 28 | return () => { 29 | setData(null); // This worked for me 30 | }; 31 | }, [company_id]); 32 | 33 | useEffect(() => { 34 | if (data) { 35 | setScore(Math.round(calcScore(data?.data[0]?.score)) / 100); 36 | } 37 | }, [data, company_id]); 38 | const { report_topic, setReportTopic } = useContext(TopicContext); 39 | 40 | return loading ? ( 41 | 42 | ) : ( 43 |
44 | 53 |
54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/pages/Reports/components/SectorMap.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useContext, useState, useRef } from "react"; 2 | import { ScatterPlot } from "../../../components/d3/ScatterPlot"; 3 | import { CompanyContext } from "../../../context/CompanyContext"; 4 | import { getCompanyPeerMap } from "../../../api/getCompanyPeerMap"; 5 | import { TypePeerMap } from "../../../types/Type"; 6 | import { PageLoading } from "../../../components/PageLoading"; 7 | import { TypeCompany } from "../../../types/Type"; 8 | 9 | interface Props { 10 | companies: TypeCompany[] | []; 11 | } 12 | export const SectorMap: React.FC = ({ companies }) => { 13 | const { company_id } = useContext(CompanyContext); 14 | const [loading, setLoading] = useState(true); 15 | 16 | const ref = useRef(null); 17 | const [width, setWidth] = useState(0); 18 | const [data, setData] = useState([]); 19 | 20 | useEffect(() => { 21 | setLoading(true); 22 | getCompanyPeerMap(company_id) 23 | .then((response) => { 24 | setData(response.data.data); 25 | }) 26 | .finally(() => { 27 | setLoading(false); 28 | }); 29 | }, [company_id]); 30 | 31 | const getListSize = () => { 32 | const newWidth = ref.current?.clientWidth || 0; 33 | setWidth(newWidth); 34 | }; 35 | 36 | useEffect(() => { 37 | setWidth(ref.current?.offsetWidth || 0); 38 | window.addEventListener("resize", getListSize); 39 | }, []); 40 | 41 | return ( 42 |
43 |

Sector Map

44 | {loading ? ( 45 | 46 | ) : ( 47 | data.length && ( 48 | 53 | ) 54 | )} 55 |
56 | ); 57 | }; 58 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Company Ethical Screening 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@heroicons/react": "^1.0.5", 7 | "@testing-library/jest-dom": "^5.14.1", 8 | "@testing-library/react": "^12.0.0", 9 | "@testing-library/user-event": "^13.2.1", 10 | "@types/d3": "^7.1.0", 11 | "@types/jest": "^27.0.1", 12 | "@types/node": "^16.7.13", 13 | "@types/react": "^17.0.20", 14 | "@types/react-dom": "^17.0.9", 15 | "@visx/axis": "^2.6.0", 16 | "@visx/event": "^2.6.0", 17 | "@visx/grid": "^2.6.0", 18 | "@visx/legend": "^2.2.2", 19 | "@visx/responsive": "^2.8.0", 20 | "@visx/scale": "^2.2.2", 21 | "@visx/tooltip": "^2.8.0", 22 | "@visx/voronoi": "^2.1.2", 23 | "axios": "^0.25.0", 24 | "cors": "^2.8.5", 25 | "d3": "^7.3.0", 26 | "d3-scale-chromatic": "^3.0.0", 27 | "react": "^17.0.2", 28 | "react-dom": "^17.0.2", 29 | "react-move": "^6.5.0", 30 | "react-query": "3.34.12", 31 | "react-router-dom": "^6.2.2", 32 | "react-scripts": "5.0.0", 33 | "react-select": "^5.2.2", 34 | "react-table": "^7.7.0", 35 | "react-tailwind-table": "^1.1.1", 36 | "react-tooltip": "^4.2.21", 37 | "tailwindcss": "^3.0.18", 38 | "tw-elements": "1.0.0-alpha9", 39 | "typescript": "^4.4.2", 40 | "web-vitals": "^2.1.0" 41 | }, 42 | "scripts": { 43 | "start": "react-scripts start", 44 | "build": "react-scripts build", 45 | "test": "react-scripts test", 46 | "eject": "react-scripts eject" 47 | }, 48 | "eslintConfig": { 49 | "extends": [ 50 | "react-app", 51 | "react-app/jest" 52 | ] 53 | }, 54 | "browserslist": { 55 | "production": [ 56 | ">0.2%", 57 | "not dead", 58 | "not op_mini all" 59 | ], 60 | "development": [ 61 | "last 1 chrome version", 62 | "last 1 firefox version", 63 | "last 1 safari version" 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /src/pages/Reports/Report.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PeerComparisons } from "./components/PeerComparisons"; 3 | import { NewsSentiment } from "./components/NewsSentiment"; 4 | import { Ratings } from "./components/Ratings"; 5 | import { UNGoals } from "./components/UNGoals"; 6 | import { Summary } from "./components/Summary"; 7 | import { SectorMap } from "./components/SectorMap"; 8 | import { TopicScores } from "./components/TopicScores"; 9 | import { NewsOrder } from "./components/NewsOrder"; 10 | import { TypeCompany } from "../../types/Type"; 11 | 12 | interface Props { 13 | company_id: string; 14 | company_name: string; 15 | companies: TypeCompany[] | []; 16 | } 17 | 18 | export const Report: React.FC = ({ 19 | company_id, 20 | company_name, 21 | companies, 22 | }) => { 23 | return ( 24 | <> 25 |
26 |
27 | 28 |
29 |
30 |
31 |
32 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 | 53 |
54 |
55 | 59 |
60 |
61 |
62 |
63 |
64 | 65 |
66 | 67 | ); 68 | }; 69 | -------------------------------------------------------------------------------- /src/pages/Reports/components/SelectCompany.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react"; 2 | import Select from "react-select"; 3 | import { useQuery } from "react-query"; 4 | import { getCompanies } from "../../../api/getCompanies"; 5 | import { CompanyContext } from "../../../context/CompanyContext"; 6 | import { LoadingContext } from "../../../context/LoadingContext"; 7 | import { TypeCompany } from "../../../types/Type"; 8 | import { useNavigate } from "react-router-dom"; 9 | 10 | interface Props { 11 | companies: TypeCompany[] | []; 12 | setCompanies: (value: TypeCompany[] | []) => void; 13 | } 14 | 15 | interface SelectedOption { 16 | value: string; 17 | label: string; 18 | } 19 | 20 | export const SelectCompany: React.FC = ({ companies, setCompanies }) => { 21 | const { isLoading, error, data } = useQuery(["companies"], () => 22 | getCompanies("public", true) 23 | ); 24 | 25 | const { setLoading } = useContext(LoadingContext); 26 | const { setCompanyId, setCompanyName } = useContext(CompanyContext); 27 | const navigate = useNavigate(); 28 | useEffect(() => { 29 | setLoading && setLoading(isLoading); 30 | }, [isLoading, setLoading]); 31 | 32 | // const companies = data?.data ? (data.data as TypeCompany[]) : []; 33 | useEffect(() => { 34 | setCompanies(data?.data ? (data.data as TypeCompany[]) : []); 35 | }, [data]); 36 | 37 | if (isLoading || error) { 38 | return <>; 39 | } 40 | const changeCompany = (value: SelectedOption) => { 41 | navigate("/" + value.value); 42 | setCompanyId && setCompanyId(value.value); 43 | setCompanyName && setCompanyName(value.label); 44 | }; 45 | 46 | return ( 47 |
48 | 54 | 66 |
67 | ); 68 | }; 69 | -------------------------------------------------------------------------------- /src/pages/Reports/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useMemo, useState } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | import { QueryClient, QueryClientProvider } from "react-query"; 4 | import { PageLoading } from "../../components/PageLoading"; 5 | import { LoadingContext } from "../../context/LoadingContext"; 6 | import { Header } from "./Header"; 7 | import { SelectCompany } from "./components/SelectCompany"; 8 | import { Report } from "./Report"; 9 | import { CompanyContext } from "../../context/CompanyContext"; 10 | import { TopicContext } from "../../context/TopicContext"; 11 | import { TypeCompany } from "../../types/Type"; 12 | 13 | const queryClient = new QueryClient({ 14 | defaultOptions: { 15 | queries: { 16 | refetchOnWindowFocus: false, 17 | }, 18 | }, 19 | }); 20 | 21 | interface ReportsProps {} 22 | 23 | export const Reports: React.FC = () => { 24 | const location = useLocation(); 25 | 26 | const ticker_id = location.pathname.substring(1); 27 | const { loading } = useContext(LoadingContext); 28 | const [companies, setCompanies] = useState([]); 29 | const [company_id, setCompanyId] = useState(ticker_id ? ticker_id : ""); 30 | const [company_name, setCompanyName] = useState(""); 31 | const company_context = useMemo( 32 | () => ({ 33 | company_id, 34 | company_name, 35 | setCompanyId, 36 | setCompanyName, 37 | }), 38 | [company_id, company_name] 39 | ); 40 | const [report_topic, setReportTopic] = useState(""); 41 | const topic_context = useMemo( 42 | () => ({ 43 | report_topic, 44 | setReportTopic, 45 | }), 46 | [report_topic] 47 | ); 48 | 49 | return ( 50 | 51 |
52 |
53 | 54 | 55 | {loading ? : <>} 56 | 57 | {company_id ? ( 58 | 63 | ) : ( 64 | <> 65 | )} 66 | 67 | 68 |
69 |
70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/Reports/components/PeerComparisons.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext } from "react"; 2 | import { getCompanyPeerMap } from "../../../api/getCompanyPeerMap"; 3 | import { VerticalBarChart } from "../../../components/d3/VerticalBarChart"; 4 | import { TypeCompany, TypePeerMaps } from "../../../types/Type"; 5 | import { getMaxNumberUnit } from "../../../utils/Number"; 6 | import { calcScore } from "../../../utils/Report"; 7 | import { TopicContext } from "../../../context/TopicContext"; 8 | import { PageLoading } from "../../../components/PageLoading"; 9 | import { getCompanyName } from "../../../utils/getCompanyName"; 10 | 11 | interface Props { 12 | company_id: string; 13 | company_name: string; 14 | companies: TypeCompany[] | []; 15 | } 16 | 17 | export const PeerComparisons: React.FC = ({ 18 | company_id, 19 | company_name, 20 | companies, 21 | }) => { 22 | const [data, setData] = useState(null); 23 | const [loading, setLoading] = useState(true); 24 | 25 | useEffect(() => { 26 | setLoading(true); 27 | getCompanyPeerMap(company_id) 28 | .then((response) => { 29 | setData(response.data); 30 | }) 31 | .finally(() => { 32 | setLoading(false); 33 | }); 34 | }, [company_id]); 35 | 36 | const { report_topic } = useContext(TopicContext); 37 | const peer_map = data ? (data as TypePeerMaps).data : []; 38 | const ticker_name = getCompanyName(company_id, companies); 39 | 40 | const chart_data = peer_map.length 41 | ? peer_map.map((d) => ({ 42 | value: calcScore(d.signals_average), 43 | barColor: d.ticker_name !== ticker_name ? "#4e79a7" : "#f28e2b", 44 | bgColor: "transparent", 45 | label: d.ticker_name, 46 | })) 47 | : []; 48 | const max = getMaxNumberUnit( 49 | calcScore( 50 | peer_map.length 51 | ? peer_map.reduce((prev, current) => { 52 | if (prev.signals_average < current.signals_average) return current; 53 | return prev; 54 | }).signals_average 55 | : 0 56 | ) 57 | ); 58 | 59 | return loading ? ( 60 | 61 | ) : ( 62 | // loading... 63 |
64 |

Peer Comparisons

65 |
66 | 75 |
76 |
77 | ); 78 | }; 79 | -------------------------------------------------------------------------------- /src/types/Type.tsx: -------------------------------------------------------------------------------- 1 | export interface TypeCompany { 2 | ticker_id: string 3 | ticker_name: string 4 | industry_group: string 5 | } 6 | 7 | export interface TypePeerMap { 8 | ticker_name: string 9 | ticker_market_cap: number 10 | signals_average: number 11 | signals_count: number 12 | } 13 | 14 | export interface TypePeerMaps { 15 | data: TypePeerMap[] 16 | data_type: string 17 | } 18 | 19 | export interface TypeSectorPerformance { 20 | topic: string 21 | subtopic: string 22 | company_sentiment: number 23 | nearest_neighbours_sentiment: number 24 | } 25 | 26 | export interface TypeSectorPerformances { 27 | data: TypeSectorPerformance[] 28 | data_type: string 29 | } 30 | 31 | export interface TypeCompanySummary { 32 | data: [TypePeerMaps, TypeSectorPerformances] 33 | data_type: string 34 | ticker: string 35 | } 36 | 37 | export interface TypeCompanyScore { 38 | ticker_id: string 39 | ticker_name: string 40 | environmental_score: number 41 | governance_score: number 42 | social_score: number 43 | } 44 | 45 | export interface TypeCompanyScores { 46 | data_type: string 47 | data: TypeCompanyScore[] 48 | } 49 | 50 | export interface TypeSubTopicScore { 51 | sub_topic: string 52 | score: number 53 | } 54 | 55 | export interface TypeTopicScores { 56 | topic: string 57 | scores: TypeSubTopicScore[] 58 | } 59 | 60 | export interface TypeTopicData { 61 | ticker_id: string 62 | day: string 63 | score: number 64 | cumulative_score: number 65 | } 66 | 67 | export interface TypeFormatedTopicData { 68 | ticker_id: string 69 | day: Date 70 | score: number 71 | cumulative_score: number 72 | } 73 | 74 | export interface TypeNews { 75 | topic: string 76 | topic_datas: TypeTopicData[] 77 | } 78 | 79 | export interface TypeNewsData { 80 | headline: string 81 | sentence: string 82 | datetime: string 83 | link: string 84 | source: Number 85 | } 86 | 87 | export interface TypeNews { 88 | data_type: string 89 | data: TypeNewsData[] 90 | } 91 | 92 | export interface TypeDataPoint { 93 | topic: string 94 | value_type: string 95 | value: Number 96 | } 97 | 98 | export interface TypeKeyInformation { 99 | name: string 100 | industry: string 101 | country: string 102 | market_cap: string 103 | institutional_investors: Number 104 | latest_emissions: string 105 | sustainability_report: string 106 | charities_supported: Number 107 | disclosure_score: Number 108 | } 109 | 110 | export interface TypeDataDpData { 111 | topic: string 112 | value_type: string 113 | value: number 114 | id: string 115 | } 116 | -------------------------------------------------------------------------------- /src/components/d3/DataPoints.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useMemo } from 'react' 2 | import * as d3 from 'd3' 3 | import Tooltip from 'react-tooltip' 4 | import { CompanyContext } from '../../context/CompanyContext' 5 | import { TypeDataDpData } from '../../types/Type' 6 | 7 | interface Props { 8 | data: TypeDataDpData[] | any 9 | width: number 10 | height: number 11 | } 12 | 13 | export const DataPoints: React.FC = ({ data, width, height }) => { 14 | const { company_id } = useContext(CompanyContext) 15 | 16 | useEffect(() => { 17 | Tooltip.rebuild() 18 | }, [company_id]) 19 | 20 | const valueFunction = (d: TypeDataDpData): number => d.value 21 | 22 | const maxValue = useMemo(() => d3.max(data, valueFunction) || 0, [data]) 23 | 24 | const yScale = d3 25 | .scaleBand() 26 | .paddingInner(0.3) 27 | .domain(data.map((d: TypeDataDpData) => d.id)) 28 | .range([0, height]) 29 | const widthScale = d3.scaleLinear().domain([0, maxValue]).range([0, width]) 30 | 31 | return ( 32 | <> 33 |

Data Points

34 | 35 |
36 |
37 |

Environment

38 |

Governance

39 |

Social

40 |
41 | 42 | {data.map((d: TypeDataDpData) => ( 43 | 44 | 55 | 62 | {d.value_type} 63 | 64 | 65 | ))} 66 | 67 | {data.map((d: TypeDataDpData) => ( 68 | 69 |
Topic: {d.topic}
70 |
{`${d.value_type}: ${d.value}`}
71 |
72 | ))} 73 |
74 |
75 | Useful for Green Washing Analysis 76 |
77 | 78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /src/pages/Reports/components/NewsSentiment.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useRef } from 'react' 2 | import { getCompanyCumulativeScores } from '../../../api/getCompanyCumulativeScores' 3 | import { MultiLineChart } from '../../../components/d3/MultiLineChart' 4 | import { TypeNews } from '../../../types/Type' 5 | import { PageLoading } from '../../../components/PageLoading' 6 | 7 | interface Props { 8 | company_id: string 9 | } 10 | 11 | export const NewsSentiment: React.FC = ({ company_id }) => { 12 | const [data, setData] = useState([]) 13 | const ref = useRef(null) 14 | const [wrapWidth, setWrapWidth] = useState(0) 15 | const [loading, setLoading] = useState(true) 16 | 17 | const getListSize = () => { 18 | const newWidth = ref.current?.clientWidth || 0 19 | setWrapWidth(newWidth) 20 | } 21 | 22 | useEffect(() => { 23 | setWrapWidth(ref.current?.offsetWidth || 0) 24 | window.addEventListener('resize', getListSize) 25 | }, []) 26 | 27 | useEffect(() => { 28 | setLoading(true) 29 | const sum_data: any[] = [] 30 | getCompanyCumulativeScores(company_id, 'environmental') 31 | .then((response) => { 32 | response?.data?.data[0]?.topic_datas.length && 33 | sum_data.push(response.data.data[0]) 34 | }) 35 | .finally(() => { 36 | getCompanyCumulativeScores(company_id, 'governance') 37 | .then((response) => { 38 | response?.data?.data[0]?.topic_datas.length && 39 | sum_data.push(response.data.data[0]) 40 | }) 41 | .finally(() => { 42 | getCompanyCumulativeScores(company_id, 'social') 43 | .then((response) => { 44 | response?.data?.data[0]?.topic_datas.length && 45 | sum_data.push(response.data.data[0]) 46 | }) 47 | .finally(() => { 48 | setLoading(false) 49 | setData(sum_data) 50 | }) 51 | }) 52 | }) 53 | }, [company_id]) 54 | return ( 55 |
56 |
57 |

Last 12 Months

58 | {data.length ? ( 59 | 60 | ) : ( 61 | <> 62 | )} 63 |
64 |
65 |
66 | Topic 67 |
68 | 69 | Environmental 70 |
71 |
72 | 73 | Governance 74 |
75 |
76 | 77 | Social 78 |
79 |
80 |
81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /src/components/ReportTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Column, useSortBy, useTable } from 'react-table' 3 | import { SortAscendingIcon, SortDescendingIcon } from '@heroicons/react/solid' 4 | import ToolTip from 'react-tooltip' 5 | 6 | type Props = { 7 | data: Array 8 | columns: Array 9 | } 10 | 11 | function useInstance(instance: { allColumns: any }) { 12 | const { allColumns } = instance 13 | 14 | let rowSpanHeaders: any[] = [] 15 | 16 | allColumns.forEach((column: { id: any; enableRowSpan: any }, i: any) => { 17 | const { id, enableRowSpan } = column 18 | 19 | if (enableRowSpan !== undefined) { 20 | rowSpanHeaders = [ 21 | ...rowSpanHeaders, 22 | { id, topCellValue: null, topCellIndex: 0 }, 23 | ] 24 | } 25 | }) 26 | 27 | Object.assign(instance, { rowSpanHeaders }) 28 | } 29 | const initialState = { hiddenColumns: ['headline'] } 30 | 31 | export const ReportTable: React.FC = ({ columns, data }) => { 32 | const { 33 | getTableProps, 34 | getTableBodyProps, 35 | headerGroups, 36 | rows, 37 | prepareRow, 38 | rowSpanHeaders, 39 | } = useTable({ columns, data, initialState }, useSortBy, (hooks) => { 40 | hooks.useInstance.push(useInstance) 41 | }) 42 | 43 | return ( 44 | 45 | 46 | {headerGroups.map((headerGroup) => ( 47 | 48 | {headerGroup.headers.map((column) => ( 49 | 65 | ))} 66 | 67 | ))} 68 | 69 | 70 | {rows.map((row, i) => { 71 | prepareRow(row) 72 | 73 | for (let j = 0; j < row.allCells.length; j++) { 74 | let cell = row.allCells[j] 75 | let rowSpanHeader = rowSpanHeaders.find( 76 | (x: { id: string }) => x.id === cell.column.id, 77 | ) 78 | 79 | if (rowSpanHeader !== undefined) { 80 | if ( 81 | rowSpanHeader.topCellValue === null || 82 | rowSpanHeader.topCellValue !== cell.value 83 | ) { 84 | cell.isRowSpanned = false 85 | rowSpanHeader.topCellValue = cell.value 86 | rowSpanHeader.topCellIndex = i 87 | cell.rowSpan = 1 88 | } else { 89 | rows[rowSpanHeader.topCellIndex].allCells[j].rowSpan++ 90 | cell.isRowSpanned = true 91 | } 92 | } 93 | } 94 | return null 95 | })} 96 | {rows.map((row) => { 97 | return ( 98 | 99 | {row.cells.map((cell) => { 100 | if (cell.isRowSpanned) return null 101 | else 102 | return ( 103 | 124 | ) 125 | })} 126 | 127 | ) 128 | })} 129 | 130 |
55 | {column.render('Header')} 56 | 57 | {column.isSorted && column.isSortedDesc && ( 58 | 59 | )} 60 | {column.isSorted && !column.isSortedDesc && ( 61 | 62 | )} 63 | 64 |
113 | {cell.render('Cell')} 114 | 120 |
Headline
121 | {cell.row.values?.headline} 122 |
123 |
131 | ) 132 | } 133 | -------------------------------------------------------------------------------- /src/pages/Reports/components/Summary.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, useEffect, useRef } from "react"; 2 | 3 | import { CompanyContext } from "../../../context/CompanyContext"; 4 | import { getCompanyDataPoints } from "../../../api/getCompanyDataPoints"; 5 | import { getCompanyKeyInformation } from "../../../api/getCompanyKeyInformation"; 6 | import { DataPoints } from "../../../components/d3/DataPoints"; 7 | import { TypeCompany, TypeKeyInformation } from "../../../types/Type"; 8 | import { TypeDataDpData } from "../../../types/Type"; 9 | import { PageLoading } from "../../../components/PageLoading"; 10 | import _ from "lodash"; 11 | import { getCompanyName } from "../../../utils/getCompanyName"; 12 | 13 | interface Props { 14 | companies: TypeCompany[] | []; 15 | } 16 | 17 | export const Summary: React.FC = ({ companies }) => { 18 | const { company_id, company_name } = useContext(CompanyContext); 19 | const [dataKeyInfo, setDataKeyInfo] = useState(); 20 | const [dataDp, setDataDp] = useState(); 21 | const [loadingKey, setLoadingKey] = useState(true); 22 | const [loadingPoint, setLoadingPoint] = useState(true); 23 | 24 | useEffect(() => { 25 | setLoadingKey(true); 26 | setLoadingPoint(true); 27 | getCompanyKeyInformation(company_id) 28 | .then((response) => { 29 | const res: TypeKeyInformation = response.data.data[0]; 30 | setDataKeyInfo(res); 31 | }) 32 | .finally(() => { 33 | setLoadingKey(false); 34 | }); 35 | 36 | getCompanyDataPoints(company_id) 37 | .then((response) => { 38 | const res = response.data.data[0]; 39 | const res1 = [ 40 | { 41 | topic: "Environmental", 42 | value_type: "-", 43 | value: res.negative?.environmental || 0, 44 | id: "Environmental-" + res.negative?.environmental, 45 | }, 46 | { 47 | topic: "Environmental", 48 | value_type: "+", 49 | value: res.positive?.environmental || 0, 50 | id: "Environmental+" + res.positive?.environmental, 51 | }, 52 | { 53 | topic: "Governance", 54 | value_type: "-", 55 | value: res.negative?.governance || 0, 56 | id: "Governance-" + res.negative?.governance, 57 | }, 58 | { 59 | topic: "Governance", 60 | value_type: "+", 61 | value: res.positive?.governance || 0, 62 | id: "Governance+" + res.positive?.governance, 63 | }, 64 | { 65 | topic: "Social", 66 | value_type: "-", 67 | value: res.negative?.social || 0, 68 | id: "Social-" + res.negative?.social, 69 | }, 70 | { 71 | topic: "Social", 72 | value_type: "+", 73 | value: res.positive?.social || 0, 74 | id: "Social+" + res.positive?.social, 75 | }, 76 | ]; 77 | setDataDp(res1); 78 | }) 79 | .finally(() => { 80 | setLoadingPoint(false); 81 | }); 82 | }, [company_id]); 83 | 84 | return loadingKey || loadingPoint ? ( 85 | 86 | ) : ( 87 |
88 |

89 | {getCompanyName(company_id, companies)} 90 |

91 |
92 |
Sector
93 |
94 | {dataKeyInfo?.industry} 95 |
96 |
Market Cap
97 |
98 | {dataKeyInfo?.market_cap} 99 |
100 |
Investors
101 |
102 | {dataKeyInfo?.institutional_investors} 103 |
104 |
105 | {!_.isEmpty(dataDp) ? ( 106 |
107 | 108 |
109 | ) : ( 110 |
111 |

Data Points

112 |
113 | )} 114 |
115 |

Disclosures Scores

116 |
CSR Liink
117 | 126 |
Disclosure Score
127 |
128 | {dataKeyInfo?.disclosure_score || "0"} 129 |
130 |
Carbon Emissions(mt)
131 |
132 | {dataKeyInfo?.latest_emissions || "null"} 133 |
134 |
Charity Supported
135 |
136 | {dataKeyInfo?.charities_supported || "0"} 137 |
138 |
139 |
140 | ); 141 | }; 142 | -------------------------------------------------------------------------------- /src/pages/Reports/components/NewsOrder.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext, useRef } from 'react' 2 | import { getCompanyNews } from '../../../api/getCompanyNews' 3 | import { Column } from 'react-table' 4 | import ToolTip from 'react-tooltip' 5 | import { TypeNewsData } from '../../../types/Type' 6 | import { ReportTable } from '../../../components/ReportTable' 7 | import { TopicContext } from '../../../context/TopicContext' 8 | import { CompanyContext } from '../../../context/CompanyContext' 9 | import * as d3 from 'd3' 10 | import { PageLoading } from '../../../components/PageLoading' 11 | 12 | export const NewsOrder: React.FC = () => { 13 | const { company_id } = useContext(CompanyContext) 14 | 15 | const [data, setData] = useState() 16 | const { report_topic, setReportTopic } = useContext(TopicContext) 17 | const ref = useRef(null) 18 | const [loading, setLoading] = useState(true) 19 | 20 | useEffect(() => { 21 | ToolTip.rebuild() 22 | setLoading(true) 23 | getCompanyNews(company_id) 24 | .then((response) => { 25 | const res: TypeNewsData[] = response.data.data 26 | setData( 27 | res 28 | .sort((a, b) => (a.datetime < b.datetime ? -1 : 1)) 29 | .map((value, index, arr) => { 30 | if (index < arr.length / 2) 31 | return { 32 | ...value, 33 | data_type: 'above' + index, 34 | } 35 | else 36 | return { 37 | ...value, 38 | data_type: 'below' + index, 39 | } 40 | }), 41 | ) 42 | }) 43 | .catch(() => { 44 | setData(null) 45 | }) 46 | .finally(() => { 47 | setLoading(false) 48 | }) 49 | }, [company_id]) 50 | const columns: Array = React.useMemo( 51 | () => [ 52 | { 53 | Header: 'Sentence', 54 | accessor: 'sentence', 55 | width: '8-12', 56 | Cell: ({ cell: { value } }) => { 57 | return ( 58 |
66 | {/* {value.length > 120 ? value.slice(0, 120) + '...' : value} */} 67 | {value} 68 |
69 | ) 70 | }, 71 | }, 72 | { 73 | Header: 'Link', 74 | accessor: 'link', 75 | width: '2-12', 76 | Cell: ({ cell: { value } }) => ( 77 | 83 | {value} 84 | 85 | ), 86 | }, 87 | { 88 | Header: 'Data Time', 89 | accessor: 'datetime', 90 | width: '2', 91 | Cell: ({ cell: { value } }) => ( 92 |
95 | {d3.timeFormat('%Y-%m-%d')(new Date(value))} 96 |
97 | ), 98 | }, 99 | { 100 | Header: '', 101 | accessor: 'data_type', 102 | show: true, 103 | Cell: ({ cell: { value } }) => { 104 | return ( 105 |
108 |
116 |
117 | ) 118 | }, 119 | }, 120 | { 121 | Header: 'Headline', 122 | accessor: 'headline', 123 | Cell: ({ cell: { value } }) => ( 124 |
127 | {value} 128 |
129 | ), 130 | }, 131 | // { 132 | // Header: '', 133 | // accessor: 'data_type', 134 | // show: true, 135 | // Cell: ({ cell: { value } }) => { 136 | // return ( 137 | //
140 | //
151 | //
152 | // ) 153 | // }, 154 | // }, 155 | ], 156 | [company_id], 157 | ) 158 | 159 | const handleClickOutside = (e: MouseEvent) => { 160 | ref && 161 | ref.current && 162 | !ref.current.contains(e.target as Node) && 163 | typeof setReportTopic === 'function' && 164 | setReportTopic('') 165 | } 166 | 167 | useEffect(() => { 168 | document.addEventListener('mousedown', handleClickOutside) 169 | return () => { 170 | document.removeEventListener('mousedown', handleClickOutside) 171 | } 172 | }, []) 173 | 174 | return loading ? ( 175 | 176 | ) : ( 177 | // loading... 178 |
179 |

Top / Worst News

180 |
181 | {data?.length ? : ''} 182 |
183 |
184 | ) 185 | } 186 | -------------------------------------------------------------------------------- /src/components/d3/PieChart.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from 'react' 2 | import * as d3 from 'd3' 3 | 4 | interface Props { 5 | prefix?: string 6 | radius?: number 7 | scale?: number 8 | max?: number 9 | border_width?: number 10 | label?: string 11 | color?: string 12 | type?: string 13 | active_type?: string 14 | setActiveType?: (val: string) => void 15 | } 16 | 17 | export const PieChart: React.FC = ({ 18 | prefix = '', 19 | label = '', 20 | scale = 0, 21 | max = 100, 22 | radius = 65, 23 | border_width = 15, 24 | color = '#000', 25 | type = '', 26 | active_type = '', 27 | setActiveType, 28 | }) => { 29 | const width = radius * 2 + 10 30 | const height = width 31 | const ref = useRef(null) 32 | const [current, setCurrent] = useState('') 33 | 34 | const progressArc = (radius: number, start: number, end: number) => 35 | d3.arc()({ 36 | innerRadius: 0, 37 | outerRadius: radius, 38 | startAngle: 2 * Math.PI * start, 39 | endAngle: 2 * Math.PI * end, 40 | }) || undefined 41 | 42 | const showArc = (e: React.MouseEvent) => { 43 | let cur = e.currentTarget.id.replace(prefix + '_arc_', '') 44 | current === cur ? setCurrent('') : setCurrent(cur) 45 | } 46 | 47 | const hideArc = (e: React.MouseEvent) => { 48 | setCurrent('') 49 | } 50 | 51 | const showLine = (e: React.MouseEvent) => { 52 | let line_id: string = e.currentTarget.id.replace('arc_', 'line_') 53 | let element = document.getElementById(line_id) 54 | element && (element.style.opacity = '1') 55 | document 56 | .getElementById(prefix + '_use_line') 57 | ?.setAttribute('xlink:href', '#' + line_id) 58 | } 59 | 60 | const hideLine = (e: React.MouseEvent) => { 61 | let line_id: string = e.currentTarget.id.replace('arc_', 'line_') 62 | let element = document.getElementById(line_id) 63 | element && (element.style.opacity = '0') 64 | document 65 | .getElementById(prefix + '_use_line') 66 | ?.setAttribute('xlink:href', '') 67 | } 68 | 69 | const arcClass = (sel: string) => 70 | 'transition duration-300 ease-in-out ' + 71 | (active_type !== '' && active_type !== type 72 | ? 'opacity-30' 73 | : current === sel 74 | ? 'stroke-2 stroke-black dark:stroke-white' 75 | : current !== '' 76 | ? 'opacity-30' 77 | : '') 78 | 79 | let text_class = 80 | 'transition duration-300 ease-in-out ' + 81 | (active_type !== '' && active_type !== type 82 | ? 'opacity-30' 83 | : current === 'circle' || current === '' 84 | ? '' 85 | : 'opacity-0') 86 | let line_class = 'stroke stroke-black dark:stroke-white opacity-0' 87 | 88 | const handleClickOutside = (e: MouseEvent) => { 89 | ref && 90 | ref.current && 91 | (ref.current.contains(e.target as Node) 92 | ? typeof setActiveType === 'function' && setActiveType(type) 93 | : setCurrent('')) 94 | } 95 | 96 | useEffect(() => { 97 | if (typeof setActiveType === 'function') { 98 | if (active_type === type && current === '') { 99 | setActiveType('') 100 | } 101 | if (active_type !== type && current !== '') { 102 | setActiveType(type) 103 | } 104 | } 105 | }, [current]) 106 | 107 | useEffect(() => { 108 | document.addEventListener('mousedown', handleClickOutside) 109 | return () => { 110 | document.removeEventListener('mousedown', handleClickOutside) 111 | } 112 | }, []) 113 | 114 | return ( 115 | <> 116 | 122 | 123 | 127 | 136 | 146 | 154 | 162 | 163 | {label} 164 | 165 | 166 | 167 | {Math.round(scale * max)} 168 | {' '} 169 | / {max} 170 | 171 | 172 | 178 | 184 | 190 | 197 | 198 | 199 | 200 | 201 | ) 202 | } 203 | -------------------------------------------------------------------------------- /src/components/d3/VerticalBarChart.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo, useEffect, useState } from 'react' 2 | import * as d3 from 'd3' 3 | import { easeQuadInOut } from 'd3-ease' 4 | import { 5 | MenuAlt1Icon, 6 | SortAscendingIcon, 7 | SortDescendingIcon, 8 | } from '@heroicons/react/solid' 9 | import { NodeGroup } from 'react-move' 10 | 11 | interface ChartData { 12 | value: number 13 | barColor: string 14 | bgColor: string 15 | label: string 16 | } 17 | 18 | interface Props { 19 | width?: number 20 | height?: number 21 | x_min?: number 22 | x_max?: number 23 | y_min?: number 24 | y_max?: number 25 | max?: number 26 | padding_inner?: number 27 | data: ChartData[] 28 | caption?: string 29 | sortable?: boolean 30 | refresh?: boolean 31 | } 32 | 33 | export const VerticalBarChart: React.FC = ({ 34 | x_min = 0, 35 | x_max = 1, 36 | y_min = 0, 37 | y_max = 1, 38 | width = 200, 39 | height = 150, 40 | max = 100, 41 | padding_inner = 0.4, 42 | data, 43 | caption = '', 44 | sortable = false, 45 | refresh = false, 46 | }) => { 47 | const [activeLabel, setActiveLabel] = useState('') 48 | const handleClick = (label: string) => { 49 | activeLabel === label ? setActiveLabel('') : setActiveLabel(label) 50 | } 51 | 52 | const [chart_data, setChartData] = useState(data) 53 | const [sort, setSort] = useState(0) // 0: default, 1: desc, 2: asc 54 | const handleSort = () => { 55 | setSort((sort + 1) % 3) 56 | } 57 | 58 | const sortStyle = { 59 | marginTop: '-0.2rem', 60 | marginLeft: '-0.2rem', 61 | transform: 'scaleX(0.8) scaleY(0.77)', 62 | } 63 | 64 | useMemo(() => { 65 | const org_data = [...data].sort((a, b) => { 66 | if (sort === 1) { 67 | return b.value - a.value 68 | } 69 | if (sort === 2) { 70 | return a.value - b.value 71 | } 72 | return 0 73 | }) 74 | setChartData(org_data) 75 | }, [data, sort]) 76 | 77 | useEffect(() => { 78 | if (refresh === true) { 79 | setActiveLabel('') 80 | } 81 | }, [refresh]) 82 | 83 | const yScale = d3 84 | .scaleBand() 85 | .paddingInner(padding_inner) 86 | .domain(chart_data.map((d) => d.label)) 87 | .range([y_min * height, y_max * height]) 88 | 89 | const xScale = d3 90 | .scaleLinear() 91 | .domain([x_min * max, x_max * max]) 92 | .range([x_min * width, x_max * width]) 93 | 94 | return ( 95 | <> 96 | {!!chart_data.length && ( 97 | <> 98 |
99 | 100 | d.label} 103 | start={(d, i) => { 104 | return { opacity: 1e-6, y: height / 2 } 105 | }} 106 | enter={(d) => { 107 | return { 108 | opacity: [0.7], 109 | y: [yScale(d.label)], 110 | timing: { duration: 750, ease: easeQuadInOut }, 111 | } 112 | }} 113 | update={(d, i) => ({ 114 | opacity: [0.7], 115 | y: [yScale(d.label)], 116 | timing: { 117 | duration: 750, 118 | //delay: i * 50, 119 | ease: easeQuadInOut, 120 | }, 121 | })} 122 | leave={() => ({ 123 | opacity: [1e-6], 124 | y: [yScale.range()[1]], 125 | timing: { duration: 750, ease: easeQuadInOut }, 126 | })} 127 | > 128 | {(nodes) => ( 129 | <> 130 | {nodes.map(({ key, data, state: { y, opacity } }) => ( 131 | { 140 | handleClick(data.label) 141 | }} 142 | > 143 | 148 | 158 | 165 | {data.label} 166 | 167 | 168 | ))}{' '} 169 | 170 | )} 171 | 172 | 173 |
174 |
175 | {x_min * max} 176 | {Math.round(((x_min + x_max) * max) / 2)} 177 | {x_max * max} 178 |
179 | {!!caption && ( 180 |
181 | {caption} 182 | {!!sortable && ( 183 |
187 | {sort === 0 ? ( 188 | 189 | ) : sort === 1 ? ( 190 | 191 | ) : ( 192 | 193 | )} 194 |
195 | )} 196 |
197 | )} 198 | 199 | )} 200 | 201 | ) 202 | } 203 | -------------------------------------------------------------------------------- /src/components/d3/MultiLineChart.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import * as d3 from 'd3' 3 | import { 4 | TypeNews, 5 | TypeTopicData, 6 | TypeFormatedTopicData, 7 | } from '../../types/Type' 8 | 9 | interface props { 10 | data: TypeNews[] | any 11 | wrap_width: number 12 | } 13 | 14 | export const MultiLineChart: React.FC = ({ data, wrap_width }) => { 15 | useEffect(() => { 16 | data.length && renderChart() 17 | }, [data]) 18 | 19 | const renderChart = () => { 20 | const width = wrap_width 21 | const height = 200 22 | const margin = 50 23 | 24 | const lineOpacity = '1' 25 | const lineOpacityHover = '0.85' 26 | const otherLinesOpacityHover = '0.1' 27 | const lineStroke = '0.2rem' 28 | const lineStrokeHover = '2.5px' 29 | 30 | /* Add SVG */ 31 | d3.selectAll('.new-sentiment').remove() 32 | const svg = d3 33 | .select('#chart') 34 | .append('svg') 35 | .attr('class', 'new-sentiment') 36 | .attr('width', width + margin + 'px') 37 | .attr('height', height + margin + 'px') 38 | .append('g') 39 | .attr('transform', `translate(${margin}, 0)`) 40 | 41 | /* Format Data */ 42 | const formatedData = data.map((d: any) => { 43 | return { 44 | ...d, 45 | topic_datas: d.topic_datas.map((topic: TypeTopicData) => { 46 | return { ...topic, day: new Date(topic.day) } 47 | }), 48 | } 49 | }) 50 | 51 | //Accessors 52 | const x = (d: TypeFormatedTopicData): Date => d.day 53 | const y = (d: TypeFormatedTopicData): number => d.cumulative_score 54 | 55 | const x_min = d3.min([ 56 | (formatedData[0]?.topic_datas 57 | ? d3.min(formatedData[0]?.topic_datas, x) 58 | : 0) as number, 59 | (formatedData[1]?.topic_datas 60 | ? d3.min(formatedData[1]?.topic_datas, x) 61 | : 0) as number, 62 | (formatedData[2]?.topic_datas 63 | ? d3.min(formatedData[2]?.topic_datas, x) 64 | : 0) as number, 65 | ]) 66 | 67 | const x_max = d3.max([ 68 | (formatedData[0]?.topic_datas 69 | ? d3.max(formatedData[0]?.topic_datas, x) 70 | : 0) as number, 71 | (formatedData[1]?.topic_datas 72 | ? d3.max(formatedData[1]?.topic_datas, x) 73 | : 0) as number, 74 | (formatedData[2]?.topic_datas 75 | ? d3.max(formatedData[2]?.topic_datas, x) 76 | : 0) as number, 77 | ]) 78 | const y_min = d3.min([ 79 | (formatedData[0]?.topic_datas 80 | ? d3.min(formatedData[0]?.topic_datas, y) 81 | : 0) as number, 82 | (formatedData[1]?.topic_datas 83 | ? d3.min(formatedData[1]?.topic_datas, y) 84 | : 0) as number, 85 | (formatedData[2]?.topic_datas 86 | ? d3.min(formatedData[2]?.topic_datas, y) 87 | : 0) as number, 88 | ]) 89 | 90 | const y_max = d3.max([ 91 | (formatedData[0]?.topic_datas 92 | ? d3.max(formatedData[0]?.topic_datas, y) 93 | : 0) as number, 94 | (formatedData[1]?.topic_datas 95 | ? d3.max(formatedData[1]?.topic_datas, y) 96 | : 0) as number, 97 | (formatedData[2]?.topic_datas 98 | ? d3.max(formatedData[2]?.topic_datas, y) 99 | : 0) as number, 100 | ]) 101 | /* Scale */ 102 | // const xScale = d3 103 | // .scaleLinear() 104 | // .domain(d3.extent(formatedData[0].topic_datas, x) as any) 105 | // .range([0, width - margin]) 106 | const xScale = d3 107 | .scaleLinear() 108 | .domain([x_min, x_max] as any) 109 | .range([0, width - margin]) 110 | 111 | const yScale = d3 112 | .scaleLinear() 113 | .domain([y_min, y_max] as any) 114 | .range([height, margin]) 115 | 116 | const color = d3.scaleOrdinal(d3.schemeCategory10) 117 | /* Add Axis into SVG */ 118 | const xAxis = d3 119 | .axisBottom(xScale) 120 | .ticks(3) 121 | .tickFormat((d: any) => `${d3.timeFormat('%B %d, %y')(d)}`) 122 | 123 | const yAxis = d3.axisLeft(yScale).ticks(3) 124 | 125 | svg 126 | .append('g') 127 | .attr('class', 'x axis dark:text-gray-300') 128 | .attr('transform', `translate(0, ${height + 10})`) 129 | .attr('stroke-width', 0) 130 | .style('font-size', '0.8rem') 131 | .call(xAxis) 132 | 133 | svg 134 | .append('g') 135 | .attr('class', 'y axis dark:text-gray-300') 136 | .attr('stroke-width', 0) 137 | .call(yAxis) 138 | .append('text') 139 | .attr('transform', 'rotate(-90)') 140 | .attr('class', 'dark:fill-gray-300') 141 | .attr('y', 0 - margin) 142 | .attr('x', 0 - height / 2) 143 | .attr('fill', '#000') 144 | .attr('dy', '1em') 145 | .style('text-anchor', 'middle') 146 | .text('New Sentiment') 147 | .style('font-size', '0.8rem') 148 | 149 | var div = d3 150 | .select('body') 151 | .append('div') 152 | .attr('class', 'tooltip') 153 | .style('opacity', 0) 154 | /* Add line into SVG */ 155 | const line = d3 156 | .line() 157 | .x((d: any) => xScale(d.day)) 158 | .y((d: any) => yScale(d.cumulative_score)) 159 | 160 | let lines = svg.append('g').attr('class', 'lines') 161 | 162 | lines 163 | .selectAll('.line-group') 164 | .data(formatedData) 165 | .enter() 166 | .append('g') 167 | .attr('class', 'line-group') 168 | .on('mouseover', (event, d: any) => { 169 | svg 170 | .append('text') 171 | .attr('class', 'title-text dark:fill-gray-300') 172 | .text(d.topic) 173 | .attr('text-anchor', 'middle') 174 | .attr('x', (width - margin) / 2) 175 | .attr('y', 30) 176 | }) 177 | .on('mouseout', function (d) { 178 | svg.select('.title-text').remove() 179 | }) 180 | .append('path') 181 | .attr('class', 'line') 182 | .attr('d', (d: any) => line(d.topic_datas)) 183 | .style('stroke', (d: any, i) => { 184 | switch (d.topic) { 185 | case 'environmental': 186 | return 'rgb(89, 161, 79)' 187 | case 'governance': 188 | return 'rgb(176, 122, 161)' 189 | case 'social': 190 | return 'rgb(78, 121, 167)' 191 | } 192 | return 'rgb(89, 161, 79)' 193 | }) 194 | .style('stroke-width', '0.2rem') 195 | .style('opacity', lineOpacity) 196 | .on('mouseover', function (d: any) { 197 | d3.selectAll('.line').style('opacity', otherLinesOpacityHover) 198 | d3.select(this) 199 | .style('opacity', lineOpacityHover) 200 | .style('stroke-width', lineStrokeHover) 201 | .style('cursor', 'pointer') 202 | }) 203 | .on('mouseout', function (d) { 204 | d3.selectAll('.line').style('opacity', lineOpacity) 205 | d3.select(this) 206 | .style('stroke-width', lineStroke) 207 | .style('cursor', 'none') 208 | }) 209 | } 210 | return
211 | } 212 | -------------------------------------------------------------------------------- /src/pages/Reports/components/TopicScores.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useContext, useRef } from 'react' 2 | import { getCompanyScores } from '../../../api/getCompanyScores' 3 | import { getCompanySubTopics } from '../../../api/getCompanySubTopics' 4 | import { getCompanyTopics } from '../../../api/getCompanyTopics' 5 | import { Column } from 'react-table' 6 | import { TypeTopicScores } from '../../../types/Type' 7 | import { ReportTable } from '../../../components/ReportTable' 8 | import { calcScore, getScoreColor } from '../../../utils/Report' 9 | import { TopicContext } from '../../../context/TopicContext' 10 | import { PageLoading } from '../../../components/PageLoading' 11 | 12 | type responseType = { 13 | topic: string 14 | subtopic: string 15 | company_sentiment: number 16 | } 17 | 18 | interface Props { 19 | company_id: string 20 | company_name: string 21 | } 22 | 23 | export const TopicScores: React.FC = ({ company_id, company_name }) => { 24 | const [loading, setLoading] = useState(true) 25 | const [data, setData] = useState([]) 26 | const [topics, setTopics] = useState([]) 27 | const [sub_topics, setSubTopics] = useState([[]]) 28 | const [scores, setScores] = useState([[]]) 29 | const [cells, setCells] = useState([]) 30 | const { report_topic, setReportTopic } = useContext(TopicContext) 31 | const ref = useRef(null) 32 | 33 | useEffect(() => { 34 | setLoading(true) 35 | getCompanySubTopics(company_id) 36 | .then((response) => { 37 | const res: responseType[] = response.data.data 38 | res.sort((prev, next) => (prev.topic < next.topic ? -1 : 1)) 39 | 40 | setData( 41 | res.map((item) => ({ 42 | ...item, 43 | company_sentiment: Math.round( 44 | calcScore(item.company_sentiment), 45 | ).toString(), 46 | })), 47 | ) 48 | }) 49 | .finally(() => { 50 | setLoading(false) 51 | }) 52 | }, [company_id]) 53 | 54 | const selectTopic = (e: React.MouseEvent) => { 55 | let topic = e.currentTarget.innerText.toLowerCase() 56 | typeof setReportTopic === 'function' && 57 | setReportTopic(report_topic === topic ? '' : topic) 58 | } 59 | 60 | const columns: Array = React.useMemo( 61 | () => [ 62 | { 63 | Header: 'Topic', 64 | accessor: 'topic', 65 | enableRowSpan: true, 66 | Cell: ({ cell: { value } }) => { 67 | return ( 68 |
76 | {value} 77 |
78 | ) 79 | }, 80 | }, 81 | { 82 | Header: 'Subtopic', 83 | accessor: 'subtopic', 84 | Cell: ({ cell: { value } }) => ( 85 |
97 | {value.replace(/%S%(.*?)(%E%|$)/g, '')} 98 |
99 | ), 100 | }, 101 | { 102 | Header: '', 103 | accessor: 'company_sentiment', 104 | Cell: ({ cell: { value } }) => ( 105 |
117 |
126 |
127 | ), 128 | }, 129 | ], 130 | [report_topic], 131 | ) 132 | 133 | const handleClickOutside = (e: MouseEvent) => { 134 | ref && 135 | ref.current && 136 | !ref.current.contains(e.target as Node) && 137 | typeof setReportTopic === 'function' && 138 | setReportTopic('') 139 | } 140 | 141 | useEffect(() => { 142 | document.addEventListener('mousedown', handleClickOutside) 143 | return () => { 144 | document.removeEventListener('mousedown', handleClickOutside) 145 | } 146 | }, []) 147 | 148 | return loading ? ( 149 | 150 | ) : ( 151 | // loading... 152 |
153 |

{company_name}

154 |
155 | {data.length ? : ''} 156 |
157 |
Score Colors
158 |
159 |
0.0
160 |
161 |
162 |
163 |
164 |
165 |
100.0
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | ) 193 | } 194 | -------------------------------------------------------------------------------- /src/components/d3/ScatterPlot.tsx: -------------------------------------------------------------------------------- 1 | import { wbData } from "./data"; 2 | import { scaleLinear, scaleLog, scaleSqrt, scaleOrdinal } from "@visx/scale"; 3 | import { extent, format, max, min } from "d3"; 4 | import * as d3 from "d3"; 5 | import { Circle } from "@visx/shape"; 6 | import { Text } from "@visx/text"; 7 | import { Group } from "@visx/group"; 8 | import { Axis, AxisLeft } from "@visx/axis"; 9 | import { GridColumns } from "@visx/grid"; 10 | import { useTooltip, TooltipWithBounds, defaultStyles } from "@visx/tooltip"; 11 | import { useEffect, useContext, useRef, useMemo, useCallback } from "react"; 12 | import { localPoint } from "@visx/event"; 13 | import { voronoi } from "@visx/voronoi"; 14 | import { CompanyContext } from "../../context/CompanyContext"; 15 | import { getCompanyName } from "../../utils/getCompanyName"; 16 | import { TypeCompany, TypePeerMap } from "../../types/Type"; 17 | 18 | interface Props { 19 | wrap_width: number; 20 | data: TypePeerMap[]; 21 | companies: TypeCompany[] | []; 22 | } 23 | 24 | export const ScatterPlot: React.FC = ({ 25 | wrap_width, 26 | data, 27 | companies, 28 | }) => { 29 | // let data = wbData.data 30 | const { company_id, company_name } = useContext(CompanyContext); 31 | 32 | const width = wrap_width * 0.95; 33 | const height = 360; 34 | const margin = { top: 30, left: 60, right: 40, bottom: 50 }; 35 | const innerWidth = 36 | width - margin.left - margin.right > 0 37 | ? width - margin.left - margin.right 38 | : 0; 39 | const innerHeight = height - margin.top - margin.bottom; 40 | 41 | const x = (d: any) => d.signals_count; 42 | const y = (d: any) => d.signals_average; 43 | const radius = (d: any) => d.ticker_market_cap; 44 | const color = (d: any) => d.ticker_name; 45 | 46 | const xScale = scaleLinear({ 47 | range: [margin.left, innerWidth + margin.left], 48 | domain: extent(data, x) as any, 49 | }); 50 | 51 | const yScale = scaleLinear({ 52 | range: [innerHeight + margin.top, margin.top], 53 | domain: [min(data, y), max(data, y)] as any, 54 | nice: true, 55 | }); 56 | 57 | const rScale = scaleSqrt({ 58 | range: [3, 30], 59 | domain: extent(data, radius) as any, 60 | }); 61 | 62 | const { 63 | showTooltip, 64 | hideTooltip, 65 | tooltipData, 66 | tooltipOpen, 67 | tooltipTop = 0, 68 | tooltipLeft = 0, 69 | } = useTooltip(); 70 | 71 | const voronoiLayout = useMemo( 72 | () => 73 | voronoi({ 74 | x: (d) => xScale(x(d)) ?? 0, 75 | y: (d) => yScale(y(d)) ?? 0, 76 | width, 77 | height, 78 | })(data), 79 | [data, width, height, xScale, yScale] 80 | ); 81 | 82 | let tooltipTimeout: number; 83 | const svgRef = useRef(null); 84 | 85 | const handleMouseMove = useCallback( 86 | (event) => { 87 | if (tooltipTimeout) clearTimeout(tooltipTimeout); 88 | if (!svgRef.current) return; 89 | 90 | // find the nearest polygon to the current mouse position 91 | const point = localPoint(svgRef.current, event); 92 | if (!point) return; 93 | const neighborRadius = 100; 94 | const closest = voronoiLayout.find(point.x, point.y, neighborRadius); 95 | if (closest) { 96 | showTooltip({ 97 | tooltipLeft: xScale(x(closest.data)), 98 | tooltipTop: yScale(y(closest.data)), 99 | tooltipData: closest.data as TypePeerMap, 100 | }); 101 | } 102 | }, 103 | [xScale, yScale, showTooltip, voronoiLayout] 104 | ); 105 | 106 | const handleMouseLeave = useCallback(() => { 107 | tooltipTimeout = window.setTimeout(() => { 108 | hideTooltip(); 109 | }, 500); 110 | }, [hideTooltip]); 111 | 112 | // Sort the data 113 | const sortedData = useMemo( 114 | () => data.sort((a, b) => b.ticker_market_cap - a.ticker_market_cap), 115 | [data] 116 | ); 117 | 118 | const ticker_name = getCompanyName(company_id, companies); 119 | 120 | return ( 121 | <> 122 | 128 | 139 | ({ 151 | fill: "inherit", 152 | fontSize: "0.6rem", 153 | textAnchor: "end", 154 | verticalAnchor: "middle", 155 | })} 156 | /> 157 | ({ 168 | fill: "inherit", 169 | fontSize: "0.6rem", 170 | textAnchor: "end", 171 | verticalAnchor: "middle", 172 | })} 173 | /> 174 | 182 | 183 | {sortedData.map((point, i) => ( 184 | 193 | innerWidth 194 | ? xScale(x(point)) - 195 | rScale(radius(point)) - 196 | 5 - 197 | color(point).length * 6 198 | : xScale(x(point)) + rScale(radius(point)) + 5 199 | } 200 | y={yScale(y(point))} 201 | fill="black" 202 | fontSize="0.7rem" 203 | > 204 | {color(point)} 205 | 206 | ))} 207 | 208 | 209 | {sortedData.map((point, i) => ( 210 | 225 | ))} 226 | 227 | 228 | {tooltipOpen && tooltipData && tooltipLeft != null && tooltipTop != null && ( 229 | 234 |

241 | {color(tooltipData)} 242 |

243 |
250 |
Signals Count
251 |
{`${format("2~s")( 252 | x(tooltipData) 253 | )}`}
254 |
Signals Average
255 |
256 | {/* {Math.round(y(tooltipData))} */} 257 | {y(tooltipData)} 258 |
259 |
Ticker Market Cap
260 |
{`${Math.round( 261 | radius(tooltipData) 262 | )}`}
263 |
264 |
265 | )} 266 | 267 | ); 268 | }; 269 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-table" { 2 | // Type definitions for react-table 7.7 3 | // Project: https://github.com/tannerlinsley/react-table 4 | // Definitions by: Guy Gascoigne-Piggford , 5 | // Michael Stramel 6 | // Rohit Garg 7 | // Jason Clark 8 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 9 | // TypeScript Version: 3.5 10 | // reflects react-table@7.7.0 11 | 12 | // tslint:disable:no-empty-interface 13 | // no-empty-interface is disabled to allow easy extension with declaration merging 14 | 15 | // tslint:disable:no-unnecessary-generics 16 | // no-unnecessary-generics is disabled because many of these definitions are either used in a generic 17 | // context or the signatures are required to match for declaration merging 18 | 19 | // The changelog for the important changes is located in the Readme.md 20 | 21 | import { 22 | ChangeEvent, 23 | ComponentType, 24 | CSSProperties, 25 | DependencyList, 26 | EffectCallback, 27 | MouseEvent, 28 | ReactElement, 29 | ReactFragment, 30 | ReactNode, 31 | ReactText, 32 | } from "react"; 33 | 34 | export {}; 35 | 36 | /** 37 | * The empty definitions of below provides a base definition for the parts used by useTable, that can then be extended in the users code. 38 | * 39 | * @example 40 | * export interface TableOptions 41 | * extends 42 | * UseExpandedOptions, 43 | * UseFiltersOptions {} 44 | * see https://gist.github.com/ggascoigne/646e14c9d54258e40588a13aabf0102d for more details 45 | */ 46 | export interface TableOptions extends UseTableOptions {} 47 | 48 | export interface TableInstance 49 | extends Omit, "columns" | "pageCount">, 50 | UseTableInstanceProps {} 51 | 52 | export interface TableState { 53 | hiddenColumns?: Array> | undefined; 54 | } 55 | 56 | export interface Hooks extends UseTableHooks {} 57 | 58 | export interface Cell 59 | extends UseTableCellProps {} 60 | 61 | export interface ColumnInterface 62 | extends UseTableColumnOptions {} 63 | 64 | export interface ColumnInterfaceBasedOnValue { 65 | Cell?: Renderer> | undefined; 66 | } 67 | 68 | export interface ColumnGroupInterface { 69 | columns: Array>; 70 | } 71 | 72 | export type ColumnGroup = ColumnInterface & 73 | ColumnGroupInterface & 74 | ( 75 | | { Header: string } 76 | | ({ id: IdType } & { 77 | Header: Renderer>; 78 | }) 79 | ) & { accessor?: Accessor | undefined }; // Not used, but needed for backwards compatibility 80 | 81 | type ValueOf = T[keyof T]; 82 | 83 | // The accessors like `foo.bar` are not supported, use functions instead 84 | export type ColumnWithStrictAccessor = 85 | ColumnInterface & 86 | ValueOf<{ 87 | [K in keyof D]: { 88 | accessor: K; 89 | } & ColumnInterfaceBasedOnValue; 90 | }>; 91 | 92 | export type ColumnWithLooseAccessor = 93 | ColumnInterface & 94 | ColumnInterfaceBasedOnValue & 95 | ( 96 | | { Header: string } 97 | | { id: IdType } 98 | | { accessor: keyof D extends never ? IdType : never } 99 | ) & { 100 | accessor?: 101 | | (keyof D extends never ? IdType | Accessor : Accessor) 102 | | undefined; 103 | }; 104 | 105 | export type Column = 106 | | ColumnGroup 107 | | ColumnWithLooseAccessor 108 | | ColumnWithStrictAccessor; 109 | 110 | export interface ColumnInstance 111 | extends Omit, "id">, 112 | ColumnInterfaceBasedOnValue, 113 | UseTableColumnProps {} 114 | 115 | export interface HeaderGroup 116 | extends ColumnInstance, 117 | UseTableHeaderGroupProps { 118 | [x: string]: any; 119 | } 120 | 121 | export interface Row extends UseTableRowProps {} 122 | 123 | export interface TableCommonProps { 124 | style?: CSSProperties | undefined; 125 | className?: string | undefined; 126 | role?: string | undefined; 127 | } 128 | 129 | export interface TableProps extends TableCommonProps {} 130 | 131 | export interface TableBodyProps extends TableCommonProps {} 132 | 133 | export interface TableKeyedProps extends TableCommonProps { 134 | key: React.Key; 135 | } 136 | 137 | export interface TableHeaderGroupProps extends TableKeyedProps {} 138 | 139 | export interface TableFooterGroupProps extends TableKeyedProps {} 140 | 141 | export interface TableHeaderProps extends TableKeyedProps {} 142 | 143 | export interface TableFooterProps extends TableKeyedProps {} 144 | 145 | export interface TableRowProps extends TableKeyedProps {} 146 | 147 | export interface TableCellProps extends TableKeyedProps {} 148 | 149 | export interface TableToggleCommonProps extends TableCommonProps { 150 | onChange?: ((e: ChangeEvent) => void) | undefined; 151 | checked?: boolean | undefined; 152 | title?: string | undefined; 153 | indeterminate?: boolean | undefined; 154 | } 155 | 156 | export interface MetaBase { 157 | instance: TableInstance; 158 | userProps: any; 159 | } 160 | 161 | // inspired by ExtendState in https://github.com/reduxjs/redux/blob/master/src/types/store.ts 162 | export type Meta> = [ 163 | Extension 164 | ] extends [never] 165 | ? M 166 | : M & Extension; 167 | 168 | //#region useTable 169 | export function useTable( 170 | options: TableOptions, 171 | ...plugins: Array> 172 | ): TableInstance; 173 | 174 | /** 175 | * NOTE: To use custom options, use "Interface Merging" to add the custom options 176 | */ 177 | export type UseTableOptions = { 178 | columns: ReadonlyArray>; 179 | data: readonly D[]; 180 | } & Partial<{ 181 | initialState: Partial>; 182 | stateReducer: ( 183 | newState: TableState, 184 | action: ActionType, 185 | previousState: TableState, 186 | instance?: TableInstance 187 | ) => TableState; 188 | useControlledState: (state: TableState, meta: Meta) => TableState; 189 | defaultColumn: Partial>; 190 | getSubRows: (originalRow: D, relativeIndex: number) => D[]; 191 | getRowId: ( 192 | originalRow: D, 193 | relativeIndex: number, 194 | parent?: Row 195 | ) => string; 196 | autoResetHiddenColumns: boolean; 197 | }>; 198 | 199 | export type PropGetter< 200 | D extends object, 201 | Props, 202 | T extends object = never, 203 | P = Partial 204 | > = ((props: P, meta: Meta) => P | P[]) | P | P[]; 205 | 206 | export type TablePropGetter = PropGetter; 207 | 208 | export type TableBodyPropGetter = PropGetter< 209 | D, 210 | TableBodyProps 211 | >; 212 | 213 | export type HeaderPropGetter = PropGetter< 214 | D, 215 | TableHeaderProps, 216 | { column: HeaderGroup } 217 | >; 218 | 219 | export type FooterGroupPropGetter = PropGetter< 220 | D, 221 | TableFooterGroupProps, 222 | { column: HeaderGroup } 223 | >; 224 | 225 | export type HeaderGroupPropGetter = PropGetter< 226 | D, 227 | TableHeaderGroupProps, 228 | { column: HeaderGroup } 229 | >; 230 | 231 | export type FooterPropGetter = PropGetter< 232 | D, 233 | TableFooterProps, 234 | { column: HeaderGroup } 235 | >; 236 | 237 | export type RowPropGetter = PropGetter< 238 | D, 239 | TableRowProps, 240 | { row: Row } 241 | >; 242 | 243 | export type CellPropGetter = PropGetter< 244 | D, 245 | TableCellProps, 246 | { cell: Cell } 247 | >; 248 | 249 | export interface ReducerTableState 250 | extends TableState, 251 | Record {} 252 | 253 | export interface UseTableHooks extends Record { 254 | useOptions: Array< 255 | (options: TableOptions, args: TableOptions) => TableOptions 256 | >; 257 | stateReducers: Array< 258 | ( 259 | newState: TableState, 260 | action: ActionType, 261 | previousState?: TableState, 262 | instance?: TableInstance 263 | ) => ReducerTableState | undefined 264 | >; 265 | columns: Array< 266 | (columns: Array>, meta: Meta) => Array> 267 | >; 268 | columnsDeps: Array<(deps: any[], meta: Meta) => any[]>; 269 | allColumns: Array< 270 | (allColumns: Array>, meta: Meta) => Array> 271 | >; 272 | allColumnsDeps: Array<(deps: any[], meta: Meta) => any[]>; 273 | visibleColumns: Array< 274 | (allColumns: Array>, meta: Meta) => Array> 275 | >; 276 | visibleColumnsDeps: Array<(deps: any[], meta: Meta) => any[]>; 277 | headerGroups: Array< 278 | ( 279 | allColumns: Array>, 280 | meta: Meta 281 | ) => Array> 282 | >; 283 | headerGroupsDeps: Array<(deps: any[], meta: Meta) => any[]>; 284 | useInstanceBeforeDimensions: Array<(instance: TableInstance) => void>; 285 | useInstance: Array<(instance: TableInstance) => void>; 286 | prepareRow: Array<(row: Row, meta: Meta) => void>; 287 | useControlledState: Array< 288 | (state: TableState, meta: Meta) => TableState 289 | >; 290 | 291 | getTableProps: Array>; 292 | getTableBodyProps: Array>; 293 | getHeaderGroupProps: Array>; 294 | getFooterGroupProps: Array>; 295 | getHeaderProps: Array>; 296 | getFooterProps: Array>; 297 | getRowProps: Array>; 298 | getCellProps: Array>; 299 | useFinalInstance: Array<(instance: TableInstance) => void>; 300 | } 301 | 302 | export interface UseTableColumnOptions { 303 | id?: IdType | undefined; 304 | Header?: Renderer> | undefined; 305 | Footer?: Renderer> | undefined; 306 | width?: number | string | undefined; 307 | minWidth?: number | undefined; 308 | maxWidth?: number | undefined; 309 | } 310 | 311 | type UpdateHiddenColumns = ( 312 | oldHidden: Array> 313 | ) => Array>; 314 | 315 | export interface TableToggleHideAllColumnProps 316 | extends TableToggleCommonProps {} 317 | 318 | export interface UseTableInstanceProps { 319 | state: TableState; 320 | plugins: Array>; 321 | dispatch: TableDispatch; 322 | columns: Array>; 323 | allColumns: Array>; 324 | visibleColumns: Array>; 325 | headerGroups: Array>; 326 | footerGroups: Array>; 327 | headers: Array>; 328 | flatHeaders: Array>; 329 | rows: Array>; 330 | rowsById: Record>; 331 | getTableProps: (propGetter?: TablePropGetter) => TableProps; 332 | getTableBodyProps: (propGetter?: TableBodyPropGetter) => TableBodyProps; 333 | prepareRow: (row: Row) => void; 334 | flatRows: Array>; 335 | totalColumnsWidth: number; 336 | allColumnsHidden: boolean; 337 | toggleHideColumn: (columnId: IdType, value?: boolean) => void; 338 | setHiddenColumns: ( 339 | param: Array> | UpdateHiddenColumns 340 | ) => void; 341 | toggleHideAllColumns: (value?: boolean) => void; 342 | getToggleHideAllColumnsProps: ( 343 | props?: Partial 344 | ) => TableToggleHideAllColumnProps; 345 | getHooks: () => Hooks; 346 | rowSpanHeaders: any; 347 | } 348 | 349 | export interface UseTableHeaderGroupProps { 350 | headers: Array>; 351 | getHeaderGroupProps: ( 352 | propGetter?: HeaderGroupPropGetter 353 | ) => TableHeaderProps; 354 | getFooterGroupProps: ( 355 | propGetter?: FooterGroupPropGetter 356 | ) => TableFooterProps; 357 | totalHeaderCount: number; // not documented 358 | } 359 | 360 | export interface UseTableColumnProps { 361 | id: IdType; 362 | columns?: Array> | undefined; 363 | isVisible: boolean; 364 | render: (type: "Header" | "Footer" | string, props?: object) => ReactNode; 365 | totalLeft: number; 366 | totalWidth: number; 367 | getHeaderProps: (propGetter?: HeaderPropGetter) => TableHeaderProps; 368 | getFooterProps: (propGetter?: FooterPropGetter) => TableFooterProps; 369 | toggleHidden: (value?: boolean) => void; 370 | parent?: ColumnInstance | undefined; // not documented 371 | getToggleHiddenProps: (userProps?: any) => any; 372 | depth: number; // not documented 373 | placeholderOf?: ColumnInstance | undefined; 374 | } 375 | 376 | export interface UseTableRowProps { 377 | cells: Array>; 378 | allCells: Array>; 379 | values: Record, CellValue>; 380 | getRowProps: (propGetter?: RowPropGetter) => TableRowProps; 381 | index: number; 382 | original: D; 383 | id: string; 384 | subRows: Array>; 385 | } 386 | 387 | export interface UseTableCellProps { 388 | column: ColumnInstance; 389 | row: Row; 390 | value: CellValue; 391 | isRowSpanned?: boolean; 392 | rowSpan: number; 393 | getCellProps: (propGetter?: CellPropGetter) => TableCellProps; 394 | render: (type: "Cell" | string, userProps?: object) => ReactNode; 395 | } 396 | 397 | export type HeaderProps = TableInstance & { 398 | column: ColumnInstance; 399 | }; 400 | 401 | export type FooterProps = TableInstance & {}; 402 | 403 | export type CellProps = TableInstance & { 404 | column: ColumnInstance; 405 | row: Row; 406 | cell: Cell; 407 | value: CellValue; 408 | }; 409 | 410 | export type Accessor = ( 411 | originalRow: D, 412 | index: number, 413 | sub: { 414 | subRows: D[]; 415 | depth: number; 416 | data: D[]; 417 | } 418 | ) => CellValue; 419 | 420 | //#endregion 421 | 422 | // Plugins 423 | 424 | //#region useAbsoluteLayout 425 | export function useAbsoluteLayout( 426 | hooks: Hooks 427 | ): void; 428 | 429 | export namespace useAbsoluteLayout { 430 | const pluginName = "useAbsoluteLayout"; 431 | } 432 | //#endregion 433 | 434 | //#region useBlockLayout 435 | export function useBlockLayout(hooks: Hooks): void; 436 | 437 | export namespace useBlockLayout { 438 | const pluginName = "useBlockLayout"; 439 | } 440 | //#endregion 441 | 442 | //#region useColumnOrder 443 | export function useColumnOrder(hooks: Hooks): void; 444 | 445 | export namespace useColumnOrder { 446 | const pluginName = "useColumnOrder"; 447 | } 448 | 449 | export interface UseColumnOrderState { 450 | columnOrder: Array>; 451 | } 452 | 453 | export interface UseColumnOrderInstanceProps { 454 | setColumnOrder: ( 455 | updater: 456 | | ((columnOrder: Array>) => Array>) 457 | | Array> 458 | ) => void; 459 | } 460 | 461 | //#endregion 462 | 463 | //#region useExpanded 464 | export function useExpanded(hooks: Hooks): void; 465 | 466 | export namespace useExpanded { 467 | const pluginName = "useExpanded"; 468 | } 469 | 470 | export interface TableExpandedToggleProps extends TableKeyedProps {} 471 | 472 | export type UseExpandedOptions = Partial<{ 473 | manualExpandedKey: IdType; 474 | paginateExpandedRows: boolean; 475 | expandSubRows: boolean; 476 | autoResetExpanded?: boolean | undefined; 477 | }>; 478 | 479 | export interface UseExpandedHooks { 480 | getToggleRowsExpandedProps: Array>; 481 | getToggleAllRowsExpandedProps: Array>; 482 | } 483 | 484 | export interface UseExpandedState { 485 | expanded: Record, boolean>; 486 | } 487 | 488 | export interface UseExpandedInstanceProps { 489 | preExpandedRows: Array>; 490 | expandedRows: Array>; 491 | rows: Array>; 492 | expandedDepth: number; 493 | isAllRowsExpanded: boolean; 494 | toggleRowExpanded: (id: Array>, value?: boolean) => void; 495 | toggleAllRowsExpanded: (value?: boolean) => void; 496 | } 497 | 498 | export interface UseExpandedRowProps { 499 | isExpanded: boolean; 500 | canExpand: boolean; 501 | subRows: Array>; 502 | toggleRowExpanded: (value?: boolean) => void; 503 | getToggleRowExpandedProps: ( 504 | props?: Partial 505 | ) => TableExpandedToggleProps; 506 | depth: number; 507 | } 508 | 509 | //#endregion 510 | 511 | //#region useFilters 512 | export function useFilters(hooks: Hooks): void; 513 | 514 | export namespace useFilters { 515 | const pluginName = "useFilters"; 516 | } 517 | 518 | export type UseFiltersOptions = Partial<{ 519 | manualFilters: boolean; 520 | disableFilters: boolean; 521 | defaultCanFilter: boolean; 522 | filterTypes: FilterTypes; 523 | autoResetFilters?: boolean | undefined; 524 | }>; 525 | 526 | export interface UseFiltersState { 527 | filters: Filters; 528 | } 529 | 530 | export type UseFiltersColumnOptions = Partial<{ 531 | Filter: Renderer>; 532 | disableFilters: boolean; 533 | defaultCanFilter: boolean; 534 | filter: FilterType | DefaultFilterTypes | string; 535 | }>; 536 | 537 | export interface UseFiltersInstanceProps { 538 | preFilteredRows: Array>; 539 | preFilteredFlatRows: Array>; 540 | preFilteredRowsById: Record>; 541 | filteredRows: Array>; 542 | filteredFlatRows: Array>; 543 | filteredRowsById: Record>; 544 | rows: Array>; 545 | flatRows: Array>; 546 | rowsById: Record>; 547 | setFilter: ( 548 | columnId: IdType, 549 | updater: ((filterValue: FilterValue) => FilterValue) | FilterValue 550 | ) => void; 551 | setAllFilters: ( 552 | updater: Filters | ((filters: Filters) => Filters) 553 | ) => void; 554 | } 555 | 556 | export interface UseFiltersColumnProps { 557 | canFilter: boolean; 558 | setFilter: ( 559 | updater: ((filterValue: FilterValue) => FilterValue) | FilterValue 560 | ) => void; 561 | filterValue: FilterValue; 562 | preFilteredRows: Array>; 563 | filteredRows: Array>; 564 | } 565 | 566 | export type FilterProps = HeaderProps; 567 | export type FilterValue = any; 568 | export type Filters = Array<{ 569 | id: IdType; 570 | value: FilterValue; 571 | }>; 572 | export type FilterTypes = Record>; 573 | 574 | export type DefaultFilterTypes = 575 | | "text" 576 | | "exactText" 577 | | "exactTextCase" 578 | | "includes" 579 | | "includesAll" 580 | | "exact" 581 | | "equals" 582 | | "between"; 583 | 584 | export interface FilterType { 585 | ( 586 | rows: Array>, 587 | columnIds: Array>, 588 | filterValue: FilterValue 589 | ): Array>; 590 | 591 | autoRemove?: ((filterValue: FilterValue) => boolean) | undefined; 592 | } 593 | 594 | //#endregion 595 | 596 | //#region useFlexLayout 597 | export function useFlexLayout(hooks: Hooks): void; 598 | 599 | export namespace useFlexLayout { 600 | const pluginName = "useFlexLayout"; 601 | } 602 | //#endregion 603 | 604 | //#region useGridLayout 605 | export function useGridLayout(hooks: Hooks): void; 606 | 607 | export namespace useGridLayout { 608 | const pluginName = "useGridLayout"; 609 | } 610 | //#endregion 611 | 612 | //#region useGlobalFilter 613 | export function useGlobalFilter(hooks: Hooks): void; 614 | 615 | export namespace useGlobalFilter { 616 | const pluginName = "useGlobalFilter"; 617 | } 618 | 619 | export type UseGlobalFiltersOptions = Partial<{ 620 | globalFilter: 621 | | (( 622 | rows: Array>, 623 | columnIds: Array>, 624 | filterValue: any 625 | ) => Array>) 626 | | string; 627 | manualGlobalFilter: boolean; 628 | filterTypes: FilterTypes; 629 | autoResetGlobalFilter?: boolean | undefined; 630 | disableGlobalFilter?: boolean | undefined; 631 | }>; 632 | 633 | export interface UseGlobalFiltersState { 634 | globalFilter: any; 635 | } 636 | 637 | export type UseGlobalFiltersColumnOptions = Partial<{ 638 | disableGlobalFilter?: boolean | undefined; 639 | }>; 640 | 641 | export interface UseGlobalFiltersInstanceProps { 642 | preGlobalFilteredRows: Array>; 643 | preGlobalFilteredFlatRows: Array>; 644 | preGlobalFilteredRowsById: Record>; 645 | globalFilteredRows: Array>; 646 | globalFilteredFlatRows: Array>; 647 | globalFilteredRowsById: Record>; 648 | rows: Array>; 649 | flatRows: Array>; 650 | rowsById: Record>; 651 | setGlobalFilter: (filterValue: FilterValue) => void; 652 | } 653 | 654 | //#endregion 655 | 656 | //#region useGroupBy 657 | export function useGroupBy(hooks: Hooks): void; 658 | 659 | export namespace useGroupBy { 660 | const pluginName = "useGroupBy"; 661 | } 662 | 663 | export interface TableGroupByToggleProps { 664 | title?: string | undefined; 665 | style?: CSSProperties | undefined; 666 | onClick?: ((e: MouseEvent) => void) | undefined; 667 | } 668 | 669 | export type UseGroupByOptions = Partial<{ 670 | manualGroupBy: boolean; 671 | disableGroupBy: boolean; 672 | defaultCanGroupBy: boolean; 673 | aggregations: Record>; 674 | groupByFn: ( 675 | rows: Array>, 676 | columnId: IdType 677 | ) => Record>>; 678 | autoResetGroupBy?: boolean | undefined; 679 | }>; 680 | 681 | export interface UseGroupByHooks { 682 | getGroupByToggleProps: Array>; 683 | } 684 | 685 | export interface UseGroupByState { 686 | groupBy: Array>; 687 | } 688 | 689 | export type UseGroupByColumnOptions = Partial<{ 690 | aggregate: Aggregator; 691 | Aggregated: Renderer>; 692 | disableGroupBy: boolean; 693 | defaultCanGroupBy: boolean; 694 | }>; 695 | 696 | export interface UseGroupByInstanceProps { 697 | preGroupedRows: Array>; 698 | preGroupedFlatRows: Array>; 699 | preGroupedRowsById: Record>; 700 | groupedRows: Array>; 701 | groupedFlatRows: Array>; 702 | groupedRowsById: Record>; 703 | onlyGroupedFlatRows: Array>; 704 | onlyGroupedRowsById: Record>; 705 | nonGroupedFlatRows: Array>; 706 | nonGroupedRowsById: Record>; 707 | rows: Array>; 708 | flatRows: Array>; 709 | rowsById: Record>; 710 | toggleGroupBy: (columnId: IdType, value?: boolean) => void; 711 | } 712 | 713 | export interface UseGroupByColumnProps { 714 | canGroupBy: boolean; 715 | isGrouped: boolean; 716 | groupedIndex: number; 717 | toggleGroupBy: () => void; 718 | getGroupByToggleProps: ( 719 | props?: Partial 720 | ) => TableGroupByToggleProps; 721 | } 722 | 723 | export interface UseGroupByRowProps { 724 | isGrouped: boolean; 725 | groupByID: IdType; 726 | groupByVal: string; 727 | values: Record, AggregatedValue>; 728 | subRows: Array>; 729 | leafRows: Array>; 730 | depth: number; 731 | id: string; 732 | index: number; 733 | } 734 | 735 | export interface UseGroupByCellProps { 736 | isGrouped: boolean; 737 | isPlaceholder: boolean; 738 | isAggregated: boolean; 739 | } 740 | 741 | export type DefaultAggregators = 742 | | "sum" 743 | | "average" 744 | | "median" 745 | | "uniqueCount" 746 | | "count"; 747 | 748 | export type AggregatorFn = ( 749 | columnValues: CellValue[], 750 | rows: Array>, 751 | isAggregated: boolean 752 | ) => AggregatedValue; 753 | export type Aggregator = 754 | | AggregatorFn 755 | | DefaultAggregators 756 | | string; 757 | export type AggregatedValue = any; 758 | //#endregion 759 | 760 | //#region usePagination 761 | export function usePagination(hooks: Hooks): void; 762 | 763 | export namespace usePagination { 764 | const pluginName = "usePagination"; 765 | } 766 | 767 | export type UsePaginationOptions = Partial<{ 768 | pageCount: number; 769 | manualPagination: boolean; 770 | autoResetPage?: boolean | undefined; 771 | paginateExpandedRows: boolean; 772 | }>; 773 | 774 | export interface UsePaginationState { 775 | pageSize: number; 776 | pageIndex: number; 777 | } 778 | 779 | export interface UsePaginationInstanceProps { 780 | page: Array>; 781 | pageCount: number; 782 | pageOptions: number[]; 783 | canPreviousPage: boolean; 784 | canNextPage: boolean; 785 | gotoPage: (updater: ((pageIndex: number) => number) | number) => void; 786 | previousPage: () => void; 787 | nextPage: () => void; 788 | setPageSize: (pageSize: number) => void; 789 | } 790 | 791 | //#endregion 792 | 793 | //#region useResizeColumns 794 | export function useResizeColumns( 795 | hooks: Hooks 796 | ): void; 797 | 798 | export namespace useResizeColumns { 799 | const pluginName = "useResizeColumns"; 800 | } 801 | 802 | export interface UseResizeColumnsOptions { 803 | disableResizing?: boolean | undefined; 804 | autoResetResize?: boolean | undefined; 805 | } 806 | 807 | export interface UseResizeColumnsState { 808 | columnResizing: { 809 | startX?: number | undefined; 810 | columnWidth: number; 811 | headerIdWidths: Record; 812 | columnWidths: any; 813 | isResizingColumn?: string | undefined; 814 | }; 815 | } 816 | 817 | export interface UseResizeColumnsColumnOptions { 818 | disableResizing?: boolean | undefined; 819 | } 820 | 821 | export interface TableResizerProps {} 822 | 823 | export interface UseResizeColumnsColumnProps { 824 | getResizerProps: (props?: Partial) => TableResizerProps; 825 | canResize: boolean; 826 | isResizing: boolean; 827 | } 828 | 829 | //#endregion 830 | 831 | //#region useRowSelect 832 | export function useRowSelect(hooks: Hooks): void; 833 | 834 | export namespace useRowSelect { 835 | const pluginName = "useRowSelect"; 836 | } 837 | 838 | export interface TableToggleAllRowsSelectedProps 839 | extends TableToggleCommonProps {} 840 | 841 | export interface TableToggleRowsSelectedProps 842 | extends TableToggleCommonProps {} 843 | 844 | export type UseRowSelectOptions = Partial<{ 845 | manualRowSelectedKey: IdType; 846 | autoResetSelectedRows: boolean; 847 | selectSubRows: boolean; 848 | }>; 849 | 850 | export interface UseRowSelectHooks { 851 | getToggleRowSelectedProps: Array< 852 | PropGetter 853 | >; 854 | getToggleAllRowsSelectedProps: Array< 855 | PropGetter 856 | >; 857 | getToggleAllPageRowsSelectedProps: Array< 858 | PropGetter 859 | >; 860 | } 861 | 862 | export interface UseRowSelectState { 863 | selectedRowIds: Record, boolean>; 864 | } 865 | 866 | export interface UseRowSelectInstanceProps { 867 | toggleRowSelected: (rowId: IdType, set?: boolean) => void; 868 | toggleAllRowsSelected: (value?: boolean) => void; 869 | getToggleAllRowsSelectedProps: ( 870 | props?: Partial 871 | ) => TableToggleAllRowsSelectedProps; 872 | getToggleAllPageRowsSelectedProps: ( 873 | props?: Partial 874 | ) => TableToggleAllRowsSelectedProps; 875 | isAllRowsSelected: boolean; 876 | selectedFlatRows: Array>; 877 | } 878 | 879 | export interface UseRowSelectRowProps { 880 | isSelected: boolean; 881 | isSomeSelected: boolean; 882 | toggleRowSelected: (set?: boolean) => void; 883 | getToggleRowSelectedProps: ( 884 | props?: Partial 885 | ) => TableToggleRowsSelectedProps; 886 | } 887 | 888 | //#endregion 889 | 890 | //#region useRowState 891 | export function useRowState(hooks: Hooks): void; 892 | 893 | export namespace useRowState { 894 | const pluginName = "useRowState"; 895 | } 896 | 897 | export type UseRowStateOptions = Partial<{ 898 | initialRowStateAccessor: (row: Row) => UseRowStateLocalState; 899 | getResetRowStateDeps: (instance: TableInstance) => any[]; 900 | autoResetRowState?: boolean | undefined; 901 | }>; 902 | 903 | export interface UseRowStateState { 904 | rowState: Record }>; 905 | } 906 | 907 | export interface UseRowStateInstanceProps { 908 | setRowState: (rowPath: string[], updater: UseRowUpdater) => void; 909 | setCellState: ( 910 | rowPath: string[], 911 | columnId: IdType, 912 | updater: UseRowUpdater 913 | ) => void; 914 | } 915 | 916 | export interface UseRowStateRowProps { 917 | state: UseRowStateLocalState; 918 | setState: (updater: UseRowUpdater) => void; 919 | } 920 | 921 | export interface UseRowStateCellProps { 922 | state: UseRowStateLocalState; 923 | setState: (updater: UseRowUpdater) => void; 924 | } 925 | 926 | export type UseRowUpdater = T | ((prev: T) => T); 927 | export type UseRowStateLocalState = Record< 928 | IdType, 929 | T 930 | >; 931 | //#endregion 932 | 933 | //#region useSortBy 934 | export function useSortBy(hooks: Hooks): void; 935 | 936 | export namespace useSortBy { 937 | const pluginName = "useSortBy"; 938 | } 939 | 940 | export interface TableSortByToggleProps { 941 | title?: string | undefined; 942 | style?: CSSProperties | undefined; 943 | onClick?: ((e: MouseEvent) => void) | undefined; 944 | } 945 | 946 | export type UseSortByOptions = Partial<{ 947 | manualSortBy: boolean; 948 | disableSortBy: boolean; 949 | defaultCanSort: boolean; 950 | disableMultiSort: boolean; 951 | isMultiSortEvent: (e: MouseEvent) => boolean; 952 | maxMultiSortColCount: number; 953 | disableSortRemove: boolean; 954 | disabledMultiRemove: boolean; 955 | orderByFn: ( 956 | rows: Array>, 957 | sortFns: Array>, 958 | directions: boolean[] 959 | ) => Array>; 960 | sortTypes: Record>; 961 | autoResetSortBy?: boolean | undefined; 962 | }>; 963 | 964 | export interface UseSortByHooks { 965 | getSortByToggleProps: Array>; 966 | } 967 | 968 | export interface UseSortByState { 969 | sortBy: Array>; 970 | } 971 | 972 | export type UseSortByColumnOptions = Partial<{ 973 | defaultCanSort: boolean; 974 | disableSortBy: boolean; 975 | sortDescFirst: boolean; 976 | sortInverted: boolean; 977 | sortType: SortByFn | DefaultSortTypes | string; 978 | }>; 979 | 980 | export interface UseSortByInstanceProps { 981 | rows: Array>; 982 | preSortedRows: Array>; 983 | setSortBy: (sortBy: Array>) => void; 984 | toggleSortBy: ( 985 | columnId: IdType, 986 | descending?: boolean, 987 | isMulti?: boolean 988 | ) => void; 989 | } 990 | 991 | export interface UseSortByColumnProps { 992 | canSort: boolean; 993 | toggleSortBy: (descending?: boolean, multi?: boolean) => void; 994 | getSortByToggleProps: ( 995 | props?: Partial 996 | ) => TableSortByToggleProps; 997 | clearSortBy: () => void; 998 | isSorted: boolean; 999 | sortedIndex: number; 1000 | isSortedDesc: boolean | undefined; 1001 | } 1002 | 1003 | export type SortByFn = ( 1004 | rowA: Row, 1005 | rowB: Row, 1006 | columnId: IdType, 1007 | desc?: boolean 1008 | ) => number; 1009 | 1010 | export type DefaultSortTypes = 1011 | | "alphanumeric" 1012 | | "datetime" 1013 | | "basic" 1014 | | "string" 1015 | | "number"; 1016 | 1017 | export interface SortingRule { 1018 | id: IdType; 1019 | desc?: boolean | undefined; 1020 | } 1021 | 1022 | //#endregion 1023 | 1024 | // Additional API 1025 | export const actions: Record; 1026 | export type ActionType = { type: string } & Record; 1027 | export const defaultColumn: Partial & Record; 1028 | 1029 | // Helpers 1030 | export type StringKey = Extract; 1031 | export type IdType = StringKey | string; 1032 | export type CellValue = V; 1033 | 1034 | export type Renderer = 1035 | | ComponentType 1036 | | ReactElement 1037 | | ReactText 1038 | | ReactFragment; 1039 | 1040 | export interface PluginHook { 1041 | (hooks: Hooks): void; 1042 | pluginName?: string | undefined; 1043 | } 1044 | 1045 | export type TableDispatch = (action: A) => void; 1046 | 1047 | // utils 1048 | export function defaultOrderByFn( 1049 | arr: Array>, 1050 | funcs: Array>, 1051 | dirs: boolean[] 1052 | ): Array>; 1053 | 1054 | export function defaultGroupByFn( 1055 | rows: Array>, 1056 | columnId: IdType 1057 | ): Record>>; 1058 | 1059 | export function makePropGetter(hooks: Hooks, ...meta: any[]): any; 1060 | 1061 | export function reduceHooks( 1062 | hooks: Hooks, 1063 | initial: T, 1064 | ...args: any[] 1065 | ): T; 1066 | 1067 | export function loopHooks(hooks: Hooks, ...args: any[]): void; 1068 | 1069 | export function ensurePluginOrder( 1070 | plugins: Array>, 1071 | befores: string[], 1072 | pluginName: string 1073 | ): void; 1074 | 1075 | export function functionalUpdate( 1076 | updater: any, 1077 | old: Partial> 1078 | ): Partial>; 1079 | 1080 | export function useGetLatest(obj: T): () => T; 1081 | 1082 | export function safeUseLayoutEffect( 1083 | effect: EffectCallback, 1084 | deps?: DependencyList 1085 | ): void; 1086 | 1087 | export function useMountedLayoutEffect( 1088 | effect: EffectCallback, 1089 | deps?: DependencyList 1090 | ): void; 1091 | 1092 | export function useAsyncDebounce any>( 1093 | defaultFn: F, 1094 | defaultWait?: number 1095 | ): F; 1096 | 1097 | export function makeRenderer( 1098 | instance: TableInstance, 1099 | column: ColumnInstance, 1100 | meta?: any 1101 | ): ReactElement; 1102 | } 1103 | --------------------------------------------------------------------------------