Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] duckdb plugin #2798

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ npm-debug.log
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# Local Netlify folder
.netlify
meta.json
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/keplergl-duckdb-plugin"]
path = src/keplergl-duckdb-plugin
url = git@github.com:foursquare/keplergl-duckdb-plugin.git
43 changes: 39 additions & 4 deletions examples/demo-app/esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,22 @@ const RESOLVE_LOCAL_ALIASES = {
// kepler.gl and loaders.gl need to use same apache-arrow
'apache-arrow': `${NODE_MODULES_DIR}/apache-arrow`,
// all react-ai-assist needs to be resolved from samenode_modules
'react-ai-assist': `${NODE_MODULES_DIR}/react-ai-assist`
'react-ai-assist': `${NODE_MODULES_DIR}/react-ai-assist`,
// add alias to serve from keplergl-duckdb-plugin submodule
'keplergl-duckdb-plugin': `${SRC_DIR}/keplergl-duckdb-plugin/src`
};

const config = {
platform: 'browser',
format: 'iife',
logLevel: 'info',
loader: {'.js': 'jsx', '.css': 'css'},
loader: {
'.js': 'jsx',
'.css': 'css',
'.ttf': 'file',
'.woff': 'file',
'.woff2': 'file'
},
entryPoints: ['src/main.js'],
outfile: 'dist/bundle.js',
bundle: true,
Expand Down Expand Up @@ -188,9 +196,36 @@ function openURL(url) {
await esbuild
.build({
...config,

minify: true,
sourcemap: false
sourcemap: false,
// Add alias resolution for build
alias: {
...RESOLVE_LOCAL_ALIASES,
'keplergl-duckdb-plugin': `${SRC_DIR}/keplergl-duckdb-plugin/src`
},
// Add these production optimizations
define: {
...config.define,
'process.env.NODE_ENV': '"production"'
},
drop: ['console', 'debugger'],
treeShaking: true,
metafile: true,
// Optionally generate a bundle analysis
plugins: [
...config.plugins,
{
name: 'bundle-analyzer',
setup(build) {
build.onEnd(result => {
if (result.metafile) {
// Write bundle analysis to disk
fs.writeFileSync('meta.json', JSON.stringify(result.metafile));
}
});
}
}
]
})
.catch(e => {
console.error(e);
Expand Down
4 changes: 4 additions & 0 deletions examples/demo-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"scripts": {
"start": "node esbuild.config.mjs --start",
"build": "node esbuild.config.mjs --build",
"deploy": "yarn build && netlify deploy -d dist",
"deploy:prod": "yarn build && netlify deploy -d dist --prod",
"start:local": "NODE_ENV=local node esbuild.config.mjs --start",
"start:local-ai": "NODE_ENV=local node esbuild.config.mjs --start --env.ai",
"start:local-deck": "NODE_ENV=local node esbuild.config.mjs --start --env.deck",
Expand Down Expand Up @@ -39,6 +42,7 @@
"react-intl": "^6.3.0",
"react-markdown": "^6.0.3",
"react-redux": "^8.0.5",
"react-resizable-panels": "^2.1.7",
"react-router": "3.2.5",
"react-router-redux": "^4.0.8",
"react-virtualized": "^9.21.0",
Expand Down
99 changes: 65 additions & 34 deletions examples/demo-app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import {
setStartScreenCapture,
setScreenCaptured
} from '@kepler.gl/ai-assistant';
import {theme} from '@kepler.gl/styles';
import {panelBorderColor, theme} from '@kepler.gl/styles';
import {useSelector} from 'react-redux';
import {ParsedConfig} from '@kepler.gl/types';
import {SqlPanel} from 'keplergl-duckdb-plugin';
import Banner from './components/banner';
import Announcement, {FormLink} from './components/announcement';
import {replaceLoadDataModal} from './factories/load-data-modal';
Expand All @@ -31,8 +34,15 @@ import {
onLoadCloudMapSuccess
} from './actions';

import {loadCloudMap, addDataToMap, replaceDataInMap} from '@kepler.gl/actions';
import {
loadCloudMap,
addDataToMap,
replaceDataInMap,
toggleMapControl,
toggleModal
} from '@kepler.gl/actions';
import {CLOUD_PROVIDERS} from './cloud-providers';
import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels';

const KeplerGl = require('@kepler.gl/components').injectComponents([
replaceLoadDataModal(),
Expand All @@ -53,6 +63,7 @@ import sampleIconCsv from './data/sample-icon-csv';
import sampleGpsData from './data/sample-gps-data';
import sampleRowData, {config as rowDataConfig} from './data/sample-row-data';
import {processCsvData, processGeojson, processRowObject} from '@kepler.gl/processors';

/* eslint-enable no-unused-vars */

const BannerHeight = 48;
Expand Down Expand Up @@ -96,14 +107,28 @@ const CONTAINER_STYLE = {
left: 0,
top: 0,
display: 'flex',
flexDirection: 'row'
flexDirection: 'column',
backgroundColor: '#333'
};

const StyledResizeHandle = styled(PanelResizeHandle)`
background-color: ${panelBorderColor};
&:hover {
background-color: #555;
}
width: 100%;
height: 5px;
cursor: row-resize;
`;

const App = props => {
const [showBanner, toggleShowBanner] = useState(false);
const {params: {id, provider} = {}, location: {query = {}} = {}} = props;
const dispatch = useDispatch();

const isSqlPanelOpen = useSelector(
state => state?.demo?.keplerGl?.map?.uiState.mapControls.sqlPanel.active
);
const prevQueryRef = useRef<number>(null);

useEffect(() => {
Expand Down Expand Up @@ -138,6 +163,11 @@ const App = props => {
dispatch(loadRemoteMap({dataUrl: query.mapUrl}));
}

if (query.sql) {
dispatch(toggleMapControl('sqlPanel', 0));
dispatch(toggleModal(null));
}

// delay zs to show the banner
// if (!window.localStorage.getItem(BannerKey)) {
// window.setTimeout(_showBanner, 3000);
Expand Down Expand Up @@ -317,7 +347,7 @@ const App = props => {
options: {
keepExistingConfig: true
},
config: sampleGeojsonConfig
config: sampleGeojsonConfig as ParsedConfig
})
);
}, [dispatch]);
Expand Down Expand Up @@ -376,7 +406,7 @@ const App = props => {
data: processCsvData(sampleS2Data)
}
],
config: s2MapConfig,
config: s2MapConfig as ParsedConfig,
options: {
keepExistingConfig: true
}
Expand Down Expand Up @@ -443,14 +473,7 @@ const App = props => {

return (
<ThemeProvider theme={theme}>
<GlobalStyle
// this is to apply the same modal style as kepler.gl core
// because styled-components doesn't always return a node
// https://github.com/styled-components/styled-components/issues/617
// ref={node => {
// node ? (this.root = node) : null;
// }}
>
<GlobalStyle>
<ScreenshotWrapper
startScreenCapture={props.demo.aiAssistant.screenshotToAsk.startScreenCapture}
setScreenCaptured={_setScreenCaptured}
Expand All @@ -460,27 +483,35 @@ const App = props => {
<Announcement onDisable={_disableBanner} />
</Banner>
<div style={CONTAINER_STYLE}>
<div style={{flexGrow: 1}}>
<AutoSizer>
{({height, width}) => (
<KeplerGl
mapboxApiAccessToken={CLOUD_PROVIDERS_CONFIGURATION.MAPBOX_TOKEN}
id="map"
/*
* Specify path to keplerGl state, because it is not mount at the root
*/
getState={keplerGlGetState}
width={width}
height={height}
cloudProviders={CLOUD_PROVIDERS}
localeMessages={combinedMessages}
onExportToCloudSuccess={onExportFileSuccess}
onLoadCloudMapSuccess={onLoadCloudMapSuccess}
featureFlags={DEFAULT_FEATURE_FLAGS}
/>
)}
</AutoSizer>
</div>
<PanelGroup direction="vertical">
<Panel defaultSize={isSqlPanelOpen ? 60 : 100}>
<AutoSizer>
{({height, width}) => (
<KeplerGl
mapboxApiAccessToken={CLOUD_PROVIDERS_CONFIGURATION.MAPBOX_TOKEN}
id="map"
getState={keplerGlGetState}
width={width}
height={height}
cloudProviders={CLOUD_PROVIDERS}
localeMessages={combinedMessages}
onExportToCloudSuccess={onExportFileSuccess}
onLoadCloudMapSuccess={onLoadCloudMapSuccess}
featureFlags={DEFAULT_FEATURE_FLAGS}
/>
)}
</AutoSizer>
</Panel>

{isSqlPanelOpen && (
<>
<StyledResizeHandle />
<Panel defaultSize={40} minSize={20}>
<SqlPanel initialSql={query.sql || ''} />
</Panel>
</>
)}
</PanelGroup>
</div>
</ScreenshotWrapper>
</GlobalStyle>
Expand Down
59 changes: 59 additions & 0 deletions examples/demo-app/src/components/map-control/sql-panel-control.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import React, {useCallback, ComponentType} from 'react';

import {MapControls} from '@kepler.gl/types';

import {MapControlButton, MapControlTooltipFactory} from '@kepler.gl/components';

interface EffectControlIcons {
effectsIcon: ComponentType<any>;
}

export type SqlPanelControlProps = {
mapControls: MapControls;
onToggleMapControl: (control: string) => void;
actionIcons: EffectControlIcons;
};

SqlPanelControlFactory.deps = [MapControlTooltipFactory];

export default function SqlPanelControlFactory(
MapControlTooltip: ReturnType<typeof MapControlTooltipFactory>
): React.FC<SqlPanelControlProps> {
const SqlPanelControl = ({mapControls, onToggleMapControl}) => {
const onClick = useCallback(
event => {
event.preventDefault();
onToggleMapControl('sqlPanel');
},
[onToggleMapControl]
);

// ! fix here!

const showControl = mapControls?.effect?.show;
if (!showControl) {
return null;
}

const active = mapControls?.effect?.active;
return (
<MapControlTooltip
id="show-sql-panel"
message={active ? 'tooltip.hideSQLPanel' : 'tooltip.showSQLPanel'}
>
<MapControlButton
className="map-control-button toggle-sql-panel"
onClick={onClick}
active={active}
>
SQL
</MapControlButton>
</MapControlTooltip>
);
};

return SqlPanelControl;
}
4 changes: 3 additions & 1 deletion examples/demo-app/src/constants/localization.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const en = {
'* CORS policy must be defined on your custom url domain in order to be accessible. For more info ',
'loadRemoteMap.clickHere':
'<a rel="noopener noreferrer" target="_blank" href="{corsLink}">click here</a>',
'loadRemoteMap.fetch': 'Fetch'
'loadRemoteMap.fetch': 'Fetch',
'tooltip.hideSQLPanel': 'Hide SQL Panel',
'tooltip.showSQLPanel': 'Show SQL Panel'
};

export const messages = {
Expand Down
9 changes: 7 additions & 2 deletions examples/demo-app/src/factories/map-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {
withState,
MapControlFactory,
EffectControlFactory,
EffectManagerFactory
EffectManagerFactory,
MapControlButton
} from '@kepler.gl/components';
import {AiAssistantControlFactory, AiAssistantManagerFactory} from '@kepler.gl/ai-assistant';
import {SampleMapPanel} from '../components/map-control/map-control';
import SqlPanelControlFactory from '../components/map-control/sql-panel-control';

const StyledMapControlPanel = styled.div`
position: relative;
Expand Down Expand Up @@ -57,20 +59,23 @@ CustomMapControlFactory.deps = [
EffectManagerFactory,
AiAssistantControlFactory,
AiAssistantManagerFactory,
SqlPanelControlFactory,
...MapControlFactory.deps
];
function CustomMapControlFactory(
EffectControl,
EffectManager,
AiAssistantControl,
AiAssistantManager,
SqlPanelControl,
...deps
) {
const MapControl = MapControlFactory(...deps);
const actionComponents = [
...(MapControl.defaultActionComponents ?? []),
EffectControl,
AiAssistantControl
AiAssistantControl,
SqlPanelControl
];

const CustomMapControl = props => {
Expand Down
11 changes: 11 additions & 0 deletions examples/demo-app/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
import {CLOUD_PROVIDERS_CONFIGURATION} from '../constants/default-settings';
import {generateHashId} from '../utils/strings';

const {DEFAULT_MAP_CONTROLS} = uiStateUpdaters;
console.log(DEFAULT_MAP_CONTROLS);
// INITIAL_APP_STATE
const initialAppState = {
appName: 'example',
Expand Down Expand Up @@ -70,6 +72,15 @@ const demoReducer = combineReducers({
...DEFAULT_EXPORT_MAP[[EXPORT_MAP_FORMATS.HTML]],
exportMapboxAccessToken: CLOUD_PROVIDERS_CONFIGURATION.EXPORT_MAPBOX_TOKEN
}
},
mapControls: {
...DEFAULT_MAP_CONTROLS,
sqlPanel: {
active: false,
activeMapIndex: 0,
disableClose: false,
show: true
}
}
},
visState: {
Expand Down
Loading
Loading