From 6f7cbc27b8a8473e2f12e8ed7ccc186e0c3dd1cf Mon Sep 17 00:00:00 2001 From: kyusho Date: Wed, 14 Sep 2022 14:58:00 +0800 Subject: [PATCH 1/9] chore(g_walker): Use "serve" and "concurrently" to decouple and rewrite dev script. --- packages/graphic-walker/package.json | 4 +++- packages/graphic-walker/src/dataSource/config.ts | 16 ++++++++-------- packages/graphic-walker/vite.config.ts | 5 ++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/graphic-walker/package.json b/packages/graphic-walker/package.json index 169f6c5f..c65a5189 100644 --- a/packages/graphic-walker/package.json +++ b/packages/graphic-walker/package.json @@ -2,7 +2,9 @@ "name": "@kanaries/graphic-walker", "version": "0.1.0", "scripts": { - "dev": "vite --host", + "dev:front_end": "vite --host", + "dev:data_service": "npx serve ../rath-client/public -l 8080", + "dev": "concurrently \"npm run dev:front_end\" \"npm run dev:data_service\"", "build": "tsc && vite build", "serve": "vite preview", "type": "tsc src/lib.ts --declaration --emitDeclarationOnly --jsx react --esModuleInterop --outDir dist" diff --git a/packages/graphic-walker/src/dataSource/config.ts b/packages/graphic-walker/src/dataSource/config.ts index d78381d4..41c23070 100644 --- a/packages/graphic-walker/src/dataSource/config.ts +++ b/packages/graphic-walker/src/dataSource/config.ts @@ -9,15 +9,15 @@ export const DemoDataAssets = process.env.NODE_ENV === 'production' ? { KELPER: 'https://chspace.oss-cn-hongkong.aliyuncs.com/api/ds-kelper-service.json', } : { // CARS: "https://chspace.oss-cn-hongkong.aliyuncs.com/api/ds-cars-service.json", - CARS: "http://localhost:8080/datasets/ds-cars-service.json", + CARS: "/datasets/ds-cars-service.json", // STUDENTS: "https://chspace.oss-cn-hongkong.aliyuncs.com/datasets/ds-students-service.json", - STUDENTS: "http://localhost:8080/datasets/ds-students-service.json", - BTC_GOLD: "http://localhost:8080/datasets/ds_btc_gold_service.json", - BIKE_SHARING: 'http://localhost:8080/datasets/ds-bikesharing-service.json', - CAR_SALES: 'http://localhost:8080/datasets/ds-carsales-service.json', - COLLAGE: 'http://localhost:8080/datasets/ds-collage-service.json', - TITANIC: 'http://localhost:8080/datasets/ds-titanic-service.json', - KELPER: 'http://localhost:8080/datasets/ds-kelper-service.json', + STUDENTS: "/datasets/ds-students-service.json", + BTC_GOLD: "/datasets/ds_btc_gold_service.json", + BIKE_SHARING: '/datasets/ds-bikesharing-service.json', + CAR_SALES: '/datasets/ds-carsales-service.json', + COLLAGE: '/datasets/ds-collage-service.json', + TITANIC: '/datasets/ds-titanic-service.json', + KELPER: '/datasets/ds-kelper-service.json', } as const; interface IPublicData { diff --git a/packages/graphic-walker/vite.config.ts b/packages/graphic-walker/vite.config.ts index fc26d62a..99554e5d 100644 --- a/packages/graphic-walker/vite.config.ts +++ b/packages/graphic-walker/vite.config.ts @@ -6,7 +6,10 @@ import typescript from '@rollup/plugin-typescript' // https://vitejs.dev/config/ export default defineConfig({ server: { - port: 2002 + port: 2002, + proxy: { + '/datasets': 'http://localhost:8080', + }, }, plugins: [ // @ts-ignore From 565aaf7dcc99ca55db7bb420e7b922ed41654646 Mon Sep 17 00:00:00 2001 From: kyusho Date: Wed, 14 Sep 2022 18:36:33 +0800 Subject: [PATCH 2/9] feature(g_walker): Implement i18n of DataSource module in graphic_walker. --- packages/graphic-walker/package.json | 3 ++ packages/graphic-walker/src/App.tsx | 37 +++++++++++--- .../graphic-walker/src/components/modal.tsx | 49 +++++++++++++------ .../src/dataSource/dataSelection/csvData.tsx | 16 ++++-- .../src/dataSource/dataSelection/index.tsx | 31 +++++++++--- .../dataSource/dataSelection/publicData.tsx | 9 +++- .../graphic-walker/src/dataSource/index.tsx | 12 +++-- .../graphic-walker/src/locales/en-US.json | 30 ++++++++++++ packages/graphic-walker/src/locales/i18n.ts | 38 ++++++++++++++ .../graphic-walker/src/locales/zh-CN.json | 30 ++++++++++++ yarn.lock | 41 ++++++++++++++++ 11 files changed, 258 insertions(+), 38 deletions(-) create mode 100644 packages/graphic-walker/src/locales/en-US.json create mode 100644 packages/graphic-walker/src/locales/i18n.ts create mode 100644 packages/graphic-walker/src/locales/zh-CN.json diff --git a/packages/graphic-walker/package.json b/packages/graphic-walker/package.json index c65a5189..25873440 100644 --- a/packages/graphic-walker/package.json +++ b/packages/graphic-walker/package.json @@ -29,6 +29,8 @@ "@heroicons/react": "^2.0.8", "@kanaries/web-data-loader": "0.1.5", "autoprefixer": "^10.3.5", + "i18next": "^21.9.1", + "i18next-browser-languagedetector": "^6.1.5", "mobx": "^6.3.3", "mobx-react-lite": "^3.2.1", "postcss": "^8.3.7", @@ -36,6 +38,7 @@ "react": "^17.0.2", "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", + "react-i18next": "^11.18.6", "react-json-view": "^1.21.3", "rxjs": "^7.3.0", "styled-components": "^5.3.0", diff --git a/packages/graphic-walker/src/App.tsx b/packages/graphic-walker/src/App.tsx index 89f95809..cb14be23 100644 --- a/packages/graphic-walker/src/App.tsx +++ b/packages/graphic-walker/src/App.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { Record, IMutField } from './interfaces'; +import { Record, IMutField, IRow } from './interfaces'; import VisualSettings from './visualSettings'; import { Container, NestContainer } from './components/container'; import ClickMenu from './components/clickMenu'; @@ -17,22 +17,44 @@ import { toJS } from 'mobx'; import "tailwindcss/tailwind.css" import './index.css' import { Specification } from 'visual-insights'; -import PureTabs from './components/tabs/pureTab'; +// import PureTabs from './components/tabs/pureTab'; import VisNav from './segments/visNav'; +import { useTranslation } from 'react-i18next'; +import { setLocaleLanguage } from './locales/i18n'; + export interface EditorProps { - dataSource?: Record[]; + dataSource?: IRow[]; rawFields?: IMutField[]; - spec?: Specification + spec?: Specification; + i18nLang?: string; } const App: React.FC = props => { - const { dataSource = [], rawFields = [], spec } = props; + const { dataSource = [], rawFields = [], spec, i18nLang = 'en-US' } = props; const { commonStore, vizStore } = useGlobalStore(); const [insightReady, setInsightReady] = useState(true); const { currentDataset, datasets, vizEmbededMenu } = commonStore; + const { t, i18n } = useTranslation(); + const curLang = i18n.language; + + // useEffect(() => { + // if (i18nLang !== curLang) { + // setLocaleLanguage(i18nLang); + // } + // }, [i18nLang, curLang]); + + // FIXME: + document.body.ondblclick = () => { + if (curLang.startsWith('en')) { + setLocaleLanguage('zh-CN'); + } else { + setLocaleLanguage('en-US'); + } + } + // use as an embeding module, use outside datasource from props. useEffect(() => { if (dataSource.length > 0) { @@ -94,13 +116,14 @@ const App: React.FC = props => { {vizEmbededMenu.show && ( -
{ commonStore.closeEmbededMenu(); commonStore.setShowInsightBoard(true) }} > - 数据解读 + {t('App.labels.data_interpretation')} +
)} diff --git a/packages/graphic-walker/src/components/modal.tsx b/packages/graphic-walker/src/components/modal.tsx index 760de792..7c59ed08 100644 --- a/packages/graphic-walker/src/components/modal.tsx +++ b/packages/graphic-walker/src/components/modal.tsx @@ -1,6 +1,17 @@ import React from 'react'; import styled from 'styled-components'; import { XCircleIcon } from '@heroicons/react/24/outline'; + +const Background = styled.div({ + position: 'fixed', + left: 0, + top: 0, + width: '100vw', + height: '100vh', + backdropFilter: 'blur(2px)', + zIndex: 25535, +}); + const Container = styled.div` width: 880px; max-height: 800px; @@ -25,23 +36,31 @@ const Container = styled.div` z-index: 999; `; interface ModalProps { - onClose?: () => void - title?: string; + onClose?: () => void + title?: string; } const Modal: React.FC = props => { - const { onClose, title } = props; - return ( - -
- {title} - -
-
{props.children}
-
- ) + const { onClose, title } = props; + + return ( + + e.stopPropagation()}> +
+
+ {title} +
+ +
+
{props.children}
+
+
+ ); } export default Modal; diff --git a/packages/graphic-walker/src/dataSource/dataSelection/csvData.tsx b/packages/graphic-walker/src/dataSource/dataSelection/csvData.tsx index 16283aa5..43ed99a3 100644 --- a/packages/graphic-walker/src/dataSource/dataSelection/csvData.tsx +++ b/packages/graphic-walker/src/dataSource/dataSelection/csvData.tsx @@ -5,6 +5,7 @@ import Table from '../table'; import styled from 'styled-components'; import { useGlobalStore } from '../../store'; import { observer } from 'mobx-react-lite'; +import { useTranslation } from 'react-i18next'; const Container = styled.div` overflow-x: auto; @@ -19,7 +20,10 @@ const CSVData: React.FC = props => { const onSubmitData = useCallback(() => { commonStore.commitTempDS(); - }, []) + }, []); + + const { t } = useTranslation('translation', { keyPrefix: 'DataSource.dialog.file' }); + return ( = props => {
- - + {t('dataset_name')} + + { commonStore.updateTempName(e.target.value) diff --git a/packages/graphic-walker/src/dataSource/dataSelection/index.tsx b/packages/graphic-walker/src/dataSource/dataSelection/index.tsx index 125abd07..854d967a 100644 --- a/packages/graphic-walker/src/dataSource/dataSelection/index.tsx +++ b/packages/graphic-walker/src/dataSource/dataSelection/index.tsx @@ -2,30 +2,47 @@ import React from 'react'; import { useState } from 'react'; import CSVData from './csvData'; import PublicData from './publicData'; +import { useTranslation } from 'react-i18next'; interface IDataSelectionProps { } const DataSelection: React.FC = props =>{ - const [sourceType, setSourceType] = useState<'file' | 'public'>('file') + const [sourceType, setSourceType] = useState<'file' | 'public'>('file'); + const { t } = useTranslation('translation', { keyPrefix: 'DataSource' }); + return
-

Data Types

+

+ {t('dialog.data_types')} +


-
{ setSourceType('file'); }} > - Text File Data + {t('dialog.text_file_data')}
-
{ setSourceType('public'); }} > - Public Data + {t('dialog.public_data')}
-

Data Source Type [{sourceType}]

+

+ {t('dialog.data_source_type', { sourceType })} +


{ sourceType === 'file' && diff --git a/packages/graphic-walker/src/dataSource/dataSelection/publicData.tsx b/packages/graphic-walker/src/dataSource/dataSelection/publicData.tsx index af4d17c6..0863d5ae 100644 --- a/packages/graphic-walker/src/dataSource/dataSelection/publicData.tsx +++ b/packages/graphic-walker/src/dataSource/dataSelection/publicData.tsx @@ -3,6 +3,9 @@ import Table from '../table'; import { DemoDataAssets, PUBLIC_DATA_LIST } from '../config' import { useGlobalStore } from '../../store'; import { observer } from 'mobx-react-lite'; +import { useTranslation } from 'react-i18next'; + + interface IPublicDataProps { } @@ -10,6 +13,8 @@ interface IPublicDataProps { const PublicData: React.FC = props => { const { commonStore } = useGlobalStore(); const { tmpDataSource } = commonStore; + const { t } = useTranslation('translation', { keyPrefix: 'DataSource.dialog.public' }); + return
{ @@ -41,7 +46,9 @@ const PublicData: React.FC = props => { + > + {t('submit')} +
diff --git a/packages/graphic-walker/src/dataSource/index.tsx b/packages/graphic-walker/src/dataSource/index.tsx index 5b2050de..0df09eb5 100644 --- a/packages/graphic-walker/src/dataSource/index.tsx +++ b/packages/graphic-walker/src/dataSource/index.tsx @@ -6,6 +6,7 @@ import Modal from '../components/modal'; import DataSelection from './dataSelection'; import { useGlobalStore } from '../store'; import { CheckCircleIcon, ArrowPathIcon } from '@heroicons/react/24/outline'; +import { useTranslation } from 'react-i18next'; interface DSSegmentProps { preWorkDone: boolean; @@ -14,12 +15,15 @@ interface DSSegmentProps { const DataSourceSegment: React.FC = props => { const { preWorkDone } = props; const { commonStore } = useGlobalStore(); + const { t } = useTranslation(); const { currentDataset, datasets, showDSPanel } = commonStore; return {!preWorkDone &&
} - + = props => { onWidthChange(Math.round(Number(e.target.value) ** 2 * 1000)) }} /> - + + {`${t('width')}: ${width}`} +
= props => { onHeightChange(Math.round(Number(e.target.value) ** 2 * 1000)) }} /> - + + {`${t('height')}: ${height}`} +
diff --git a/packages/graphic-walker/src/components/tabs/pureTab.tsx b/packages/graphic-walker/src/components/tabs/pureTab.tsx index 6f323778..7cd8606b 100644 --- a/packages/graphic-walker/src/components/tabs/pureTab.tsx +++ b/packages/graphic-walker/src/components/tabs/pureTab.tsx @@ -1,4 +1,6 @@ import React, { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + function classNames(...classes: string[]) { return classes.filter(Boolean).join(' ') @@ -7,6 +9,7 @@ function classNames(...classes: string[]) { export interface ITabOption { label: string; key: string; + options?: Record; } interface PureTabsProps { tabs: ITabOption[]; @@ -18,18 +21,26 @@ interface PureTabsProps { export default function PureTabs(props: PureTabsProps) { const { tabs, selectedKey, onSelected, allowEdit, onEditLabel } = props; const [editList, setEditList] = useState([]); + const { t } = useTranslation(); + const clearEditStatus = useCallback(() => { setEditList(new Array(tabs.length).fill(false)) - }, [tabs.length]) + }, [tabs.length]); + useEffect(() => { clearEditStatus - }, [clearEditStatus]) + }, [clearEditStatus]); + return ( -
-