import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { useTranslation } from 'react-i18next'; import { downloadBlob } from '../utils/save'; import GwFile from './dataSelection/gwFile'; import DataSelection from './dataSelection'; import DropdownSelect from '../components/dropdownSelect'; import { IUIThemeConfig, IComputationFunction, IDarkMode, IDataSourceEventType, IDataSourceProvider, IMutField, IThemeKey } from '../interfaces'; import { syncProviderSpecs } from './syncSpecs'; import { ShadowDom } from '../shadow-dom'; import { CommonStore } from '../store/commonStore'; import { VizSpecStore } from '../store/visualSpecStore'; import { useCurrentMediaTheme } from '../utils/media'; import { GWGlobalConfig } from '../vis/theme'; import { composeContext } from '../utils/context'; import { portalContainerContext, themeContext, vegaThemeContext } from '../store/theme'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; interface DSSegmentProps { commonStore: CommonStore; dataSources: { name: string; id: string }[]; selectedId: string; onSelectId: (value: string) => void; onSave?: () => Promise; onLoad?: (file: File) => void; } const DataSourceSegment: React.FC = observer((props) => { const { commonStore, dataSources, onSelectId, selectedId, onLoad, onSave } = props; const gwFileRef = useRef(null); const { t } = useTranslation(); const { showDSPanel } = commonStore; return (
{props.onLoad && } {/* */}
({ label: d.name, value: d.id }))} selectedKey={selectedId} onSelect={onSelectId} placeholder={t('DataSource.labels.cur_dataset')} />
{onSave && ( )} {props.onLoad && ( )} { commonStore.setShowDSPanel(false); }} open={showDSPanel} > {t('DataSource.dialog.create_data_source')}
); }); function once any>(register: (x: T) => () => void, cb: T) { const disposer = { current: () => {} }; const newCB = (...args: Parameters) => { const result = cb(...args); disposer.current(); return result; }; disposer.current = register(newCB as T); } const DataSourceThemeContext = composeContext({ themeContext, vegaThemeContext, portalContainerContext }); export function DataSourceSegmentComponent(props: { provider: IDataSourceProvider; displayOffset?: number; /** @deprecated renamed to appearence */ dark?: IDarkMode; appearance?: IDarkMode; /** @deprecated use vizThemeConfig instead */ themeKey?: IThemeKey; /** @deprecated use vizThemeConfig instead */ themeConfig?: GWGlobalConfig; vizThemeConfig?: IThemeKey | GWGlobalConfig; /** @deprecated renamed to uiTheme */ colorConfig?: IUIThemeConfig; uiTheme?: IUIThemeConfig; children: (props: { meta: IMutField[]; onMetaChange: (fid: string, meta: Partial) => void; computation: IComputationFunction; storeRef: React.RefObject; datasetName: string; syncSpecs: () => void; }) => React.ReactNode; }) { const [selectedId, setSelectedId] = useState(''); const [datasetList, setDatasetList] = useState<{ name: string; id: string }[]>([]); useEffect(() => { props.provider.getDataSourceList().then(setDatasetList); return props.provider.registerCallback((e) => { if (e & IDataSourceEventType.updateList) { props.provider.getDataSourceList().then(setDatasetList); } }); }, [props.provider]); const dataset = useMemo(() => datasetList.find((x) => x.id === selectedId), [datasetList, selectedId]); const [computationID, refreshComputation] = useReducer((x: number) => x + 1, 0); const [meta, setMeta] = useState([]); const vizSpecStoreRef = useRef(null); useEffect(() => { if (dataset) { const { provider } = props; provider.getMeta(dataset.id).then(setMeta); syncProviderSpecs(provider, dataset.id, vizSpecStoreRef); const disposer = provider.registerCallback((e, datasetId) => { if (dataset.id === datasetId) { if (e & IDataSourceEventType.updateData) { refreshComputation(); } if (e & IDataSourceEventType.updateMeta) { provider.getMeta(datasetId).then(setMeta); } if (e & IDataSourceEventType.updateSpec) { syncProviderSpecs(provider, datasetId, vizSpecStoreRef); } } }); return () => { disposer(); const data = vizSpecStoreRef.current?.exportAllCharts(); data && provider.saveSpecs(dataset.id, JSON.stringify(data)); }; } }, [dataset, props.provider]); const computation = useMemo( () => async (payload) => { return selectedId ? props.provider.queryData(payload, [selectedId]) : []; }, [computationID, props.provider, selectedId] ); const onMetaChange = useCallback( (fid: string, meta: Partial) => { setMeta((x) => { const result = x.map((f) => (f.fid === fid ? { ...f, ...meta } : f)); props.provider.setMeta(selectedId, result); return result; }); }, [props.provider, selectedId] ); const commonStore = useMemo(() => new CommonStore(props.provider, setSelectedId, { displayOffset: props.displayOffset }), [props.provider]); useEffect(() => { commonStore.setDisplayOffset(props.displayOffset); }, [props.displayOffset, commonStore]); const onLoad = useMemo(() => { const importFile = props.provider.onImportFile; if (importFile) { return (file: File) => { importFile(file); once(props.provider.registerCallback, (e) => { if (e & IDataSourceEventType.updateList) { props.provider.getDataSourceList().then(([first]) => setSelectedId(first.id)); } }); }; } }, [props.provider]); const onSave = useMemo(() => { const exportFile = props.provider.onExportFile; const saveSpecs = props.provider.saveSpecs; if (exportFile) { return async () => { const data = vizSpecStoreRef.current?.exportAllCharts(); if (data) { await saveSpecs(selectedId, JSON.stringify(data)); } return exportFile(); }; } }, [selectedId, props.provider]); const syncSpecs = useCallback(() => { const data = vizSpecStoreRef.current?.exportAllCharts(); if (data) { props.provider.saveSpecs(selectedId, JSON.stringify(data)); } }, [selectedId, props.provider]); const darkMode = useCurrentMediaTheme(props.appearance ?? props.dark); const [currentTheme, setCurrentTheme] = useState( (props.vizThemeConfig ?? props.themeConfig ?? props.themeKey) as IThemeKey | GWGlobalConfig ); const [portal, setPortal] = useState(null); return ( <>
); } export default DataSourceSegment;