diff --git a/examples/demo-app/esbuild.config.mjs b/examples/demo-app/esbuild.config.mjs index c9dcc1ad26..106bca7dfd 100644 --- a/examples/demo-app/esbuild.config.mjs +++ b/examples/demo-app/esbuild.config.mjs @@ -3,6 +3,7 @@ import esbuild from 'esbuild'; import {replace} from 'esbuild-plugin-replace'; +import copy from 'esbuild-plugin-copy'; import {dotenvRun} from '@dotenv-run/esbuild'; import process from 'node:process'; @@ -62,6 +63,44 @@ const getProductionReactAliases = nodeModulesDir => ({ 'react-dom/client': `${nodeModulesDir}/react-dom/cjs/react-dom-client.production.js` }); +// Workspace packages to load from source when NODE_ENV=local. +// Must include package subpaths (e.g. duckdb/components) — the monorepo +// tsconfig only maps `@kepler.gl/*` (one segment), so subpath imports would +// otherwise resolve to demo-app/node_modules and create a second utils singleton. +const KEPLER_WORKSPACE_PACKAGES = [ + 'actions', + 'ai-assistant', + 'cloud-providers', + 'common-utils', + 'components', + 'constants', + 'deckgl-arrow-layers', + 'deckgl-layers', + 'duckdb', + 'effects', + 'layers', + 'localization', + 'processors', + 'reducers', + 'schemas', + 'styles', + 'table', + 'tasks', + 'utils' +]; + +const getKeplerLocalAliases = () => { + const aliases = {}; + for (const pkg of KEPLER_WORKSPACE_PACKAGES) { + aliases[`@kepler.gl/${pkg}`] = join(SRC_DIR, pkg, 'src'); + } + aliases['@kepler.gl/duckdb/components'] = join(SRC_DIR, 'duckdb/src/components'); + aliases['@kepler.gl/duckdb/table'] = join(SRC_DIR, 'duckdb/src/table'); + return aliases; +}; + +const isLocalEnv = process.env.NODE_ENV === 'local'; + // Env variables required for demo app const requiredEnvVariables = [ 'MapboxAccessToken', @@ -95,6 +134,10 @@ const config = { logOverride: { 'unsupported-jsx-comment': 'silent' }, + // Use demo-app tsconfig (no path mappings). Walking up to the monorepo + // tsconfig would rewrite `@kepler.gl/pkg` → `src/pkg/src` but not subpaths + // like `@kepler.gl/duckdb/components`, splitting the applicationConfig singleton. + tsconfig: join(__dirname, 'tsconfig.json'), inject: ['src/react19-shim.js'], loader: { '.js': 'jsx', @@ -146,7 +189,34 @@ const config = { return result; }); } - } + }, + // Force a single @kepler.gl/* copy from demo-app/node_modules (published + // packages on Netlify/production). Local monorepo source is handled via + // getKeplerLocalAliases() instead — do not resolve from the repo root + // node_modules here (workspace dist/ is often missing on CI). + { + name: 'dedupe-kepler', + setup(build) { + if (isLocalEnv) return; + build.onResolve({filter: /^@kepler\.gl\//}, async args => { + if (args.pluginData?.deduped) return; + const result = await build.resolve(args.path, { + resolveDir: __dirname, + kind: 'import-statement', + pluginData: {deduped: true} + }); + return result; + }); + } + }, + // copy files to dist + copy({ + resolveFrom: 'cwd', + assets: { + from: ['./src/static/_redirects'], + to: ['./dist/'] + } + }) ] }; @@ -309,7 +379,8 @@ function openURL(url) { sourcemap: false, // Add alias resolution for build alias: { - ...getThirdPartyLibraryAliases(true) + ...getThirdPartyLibraryAliases(true), + ...(isLocalEnv ? getKeplerLocalAliases() : {}) }, // Add these production optimizations define: { @@ -348,7 +419,7 @@ function openURL(url) { if (args.includes('--start')) { const isLocal = process.env.NODE_ENV === 'local'; const baseAliases = isLocal - ? localAliases + ? {...localAliases, ...getKeplerLocalAliases()} : getThirdPartyLibraryAliases(false); const nodeModulesDir = isLocal ? NODE_MODULES_DIR : BASE_NODE_MODULES_DIR; // Skip dedupe-webgl when a local deck.gl source override is active so that diff --git a/examples/demo-app/package.json b/examples/demo-app/package.json index 248b373652..73a8a871e8 100644 --- a/examples/demo-app/package.json +++ b/examples/demo-app/package.json @@ -63,6 +63,7 @@ "packageManager": "yarn@4.4.0", "devDependencies": { "@dotenv-run/esbuild": "^1.5.0", + "esbuild-plugin-copy": "^2.1.1", "esbuild-plugin-replace": "^1.4.0" } } diff --git a/examples/demo-app/src/actions.js b/examples/demo-app/src/actions.js index 628e0ff274..d80566bff0 100644 --- a/examples/demo-app/src/actions.js +++ b/examples/demo-app/src/actions.js @@ -298,6 +298,15 @@ export function loadSampleConfigurations(sampleMapId = null) { } }) .then(samples => { + if (samples.length > 10) { + // For DuckDB preview: move tiled examples to the top + let removed = samples.splice(4, 2); + samples.splice(1, 0, ...removed); + + // For DuckDB preview: filter out the example with trips as 4th timecoordinate is dropped in GEOMETRY + samples = samples.filter(s => s.id !== 'world_flights'); + } + dispatch(loadMapSampleFile(samples)); // Load the specified map const map = sampleMapId && samples.find(s => s.id === sampleMapId); diff --git a/examples/demo-app/src/app.tsx b/examples/demo-app/src/app.tsx index 6a61abdced..18e421513f 100644 --- a/examples/demo-app/src/app.tsx +++ b/examples/demo-app/src/app.tsx @@ -20,6 +20,7 @@ import { import {panelBorderColor, theme} from '@kepler.gl/styles'; import {ParsedConfig} from '@kepler.gl/types'; import {getApplicationConfig} from '@kepler.gl/utils'; +import {injectComponents} from '@kepler.gl/components'; import {SqlPanel} from '@kepler.gl/duckdb/components'; import Banner from './components/banner'; import Announcement, {FormLink} from './components/announcement'; @@ -46,7 +47,7 @@ import { import {CLOUD_PROVIDERS} from './cloud-providers'; import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels'; -const KeplerGl = require('@kepler.gl/components').injectComponents([ +const KeplerGl = injectComponents([ replaceLoadDataModal(), replaceMapControl(), replacePanelHeader() diff --git a/examples/demo-app/src/cloud-providers/index.js b/examples/demo-app/src/cloud-providers/index.js index c1ea2e3dc6..05d284e250 100644 --- a/examples/demo-app/src/cloud-providers/index.js +++ b/examples/demo-app/src/cloud-providers/index.js @@ -3,20 +3,20 @@ import {CLOUD_PROVIDERS_CONFIGURATION} from '../constants/default-settings'; -import DropboxProvider from './dropbox/dropbox-provider'; -import CartoProvider from './carto/carto-provider'; +// import DropboxProvider from './dropbox/dropbox-provider'; +// import CartoProvider from './carto/carto-provider'; import FoursquareProvider from './foursquare/foursquare-provider'; const { - DROPBOX_CLIENT_ID, - CARTO_CLIENT_ID, + // DROPBOX_CLIENT_ID, + // CARTO_CLIENT_ID, FOURSQUARE_CLIENT_ID, FOURSQUARE_DOMAIN, FOURSQUARE_API_URL, FOURSQUARE_USER_MAPS_URL } = CLOUD_PROVIDERS_CONFIGURATION; -const DROPBOX_CLIENT_NAME = 'Kepler.gl Demo App'; +// const DROPBOX_CLIENT_NAME = 'Kepler.gl Demo App'; export const DEFAULT_CLOUD_PROVIDER = 'dropbox'; @@ -26,9 +26,11 @@ export const CLOUD_PROVIDERS = [ authDomain: FOURSQUARE_DOMAIN, apiURL: FOURSQUARE_API_URL, userMapsURL: FOURSQUARE_USER_MAPS_URL - }), - new DropboxProvider(DROPBOX_CLIENT_ID, DROPBOX_CLIENT_NAME), - new CartoProvider(CARTO_CLIENT_ID) + }) + // TODO DuckDB preview only + // Disable Dropbox and Carto providers in DuckDb-preview as the domain isn't whitelisted for them. + // new DropboxProvider(DROPBOX_CLIENT_ID, DROPBOX_CLIENT_NAME), + // new CartoProvider(CARTO_CLIENT_ID) ]; export function getCloudProvider(providerName) { diff --git a/examples/demo-app/src/components/load-data-modal/sample-data-viewer.js b/examples/demo-app/src/components/load-data-modal/sample-data-viewer.js index 37280fc2db..97a26b04f9 100644 --- a/examples/demo-app/src/components/load-data-modal/sample-data-viewer.js +++ b/examples/demo-app/src/components/load-data-modal/sample-data-viewer.js @@ -5,7 +5,7 @@ import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import {format} from 'd3-format'; -import {LoadingDialog} from '@kepler.gl/components'; +import {LoadingDialog, Icons} from '@kepler.gl/components'; import {FormattedMessage} from 'react-intl'; const numFormat = format(','); @@ -72,6 +72,24 @@ const StyledError = styled.div` margin-bottom: 16px; `; +const AddDataHelper = styled.div` + margin-left: auto; + margin-right: auto; + margin-top: -25px; + margin-bottom: 6px; + cursor: pointer; + + color: ${props => props.theme.subtextColorLT}; + &:hover { + color: ${props => props.theme.textColorLT}; + } +`; + +const StyledAddIcon = styled(Icons.Add)` + display: inline; + margin-top: -3px; +`; + const SampleMap = ({id, sample, onClick}) => (
@@ -98,7 +116,8 @@ const SampleMapGallery = ({ error, isMapLoading, locale, - loadSampleConfigurations + loadSampleConfigurations, + enableLoadDataModal }) => { useEffect(() => { if (!sampleMaps.length) { @@ -107,27 +126,34 @@ const SampleMapGallery = ({ }, [sampleMaps, loadSampleConfigurations]); return ( -
- {error ? ( - {error.message} - ) : isMapLoading ? ( - - ) : ( - - {sampleMaps - .filter(sp => sp.visible) - .map(sp => ( - onLoadSample(sp)} - locale={locale} - /> - ))} - - )} -
+ <> + {!error && !isMapLoading ? ( + + Add data + + ) : null} +
+ {error ? ( + {error.message} + ) : isMapLoading ? ( + + ) : ( + + {sampleMaps + .filter(sp => sp.visible) + .map(sp => ( + onLoadSample(sp)} + locale={locale} + /> + ))} + + )} +
+ ); }; diff --git a/examples/demo-app/src/components/load-data-modal/sample-maps-tab.js b/examples/demo-app/src/components/load-data-modal/sample-maps-tab.js index 0692e7c49f..eedd1a31f6 100644 --- a/examples/demo-app/src/components/load-data-modal/sample-maps-tab.js +++ b/examples/demo-app/src/components/load-data-modal/sample-maps-tab.js @@ -2,6 +2,7 @@ // Copyright contributors to the kepler.gl project import React from 'react'; +import classnames from 'classnames'; import styled from 'styled-components'; import {Icons} from '@kepler.gl/components'; import {media} from '@kepler.gl/styles'; @@ -25,10 +26,13 @@ const StyledMapIcon = styled.div` const StyledTrySampleData = styled.div` display: flex; - margin-bottom: 12px; + margin-bottom: 0px; + margin-left: 84px; + padding-bottom: 9px; flex-grow: 1; justify-content: flex-end; color: ${props => props.theme.subtextColorLT}; + border-bottom: 3px solid transparent; .demo-map-title { margin-left: 16px; @@ -37,6 +41,12 @@ const StyledTrySampleData = styled.div` justify-content: flex-end; } + .load-data-modal__tab__item.active { + color: ${props => props.theme.textColorLT}; + border-bottom: 3px solid ${props => props.theme.textColorLT}; + font-weight: 500; + } + .demo-map-label { font-size: 11px; @@ -70,9 +80,9 @@ const StyledTrySampleData = styled.div` } `; -const SampleMapsTab = ({onClick}) => { +const SampleMapsTab = ({onClick, className}) => { return ( - +
diff --git a/examples/demo-app/src/reducers/index.js b/examples/demo-app/src/reducers/index.js index 9f275583f9..fe364fc4a0 100644 --- a/examples/demo-app/src/reducers/index.js +++ b/examples/demo-app/src/reducers/index.js @@ -10,12 +10,8 @@ import {processGeojson, processRowObject, processArrowTable} from '@kepler.gl/pr import keplerGlReducer, {combinedUpdaters, uiStateUpdaters} from '@kepler.gl/reducers'; import KeplerGlSchema from '@kepler.gl/schemas'; import {KeplerTable} from '@kepler.gl/table'; -import {getApplicationConfig} from '@kepler.gl/utils'; - -// import {getApplicationConfig, initApplicationConfig} from '@kepler.gl/utils'; -// import keplerGlDuckdbPlugin, {KeplerGlDuckDbTable, DuckDBWasmAdapter} from '@kepler.gl/duckdb'; - -import {initApplicationConfig} from '@kepler.gl/utils'; +import {getApplicationConfig, initApplicationConfig} from '@kepler.gl/utils'; +import keplerGlDuckdbPlugin, {KeplerGlDuckDbTable, DuckDBWasmAdapter} from '@kepler.gl/duckdb'; import { INIT, @@ -31,7 +27,7 @@ import {CLOUD_PROVIDERS_CONFIGURATION} from '../constants/default-settings'; import {generateHashId} from '../utils/strings'; // initialize kepler demo-app with DuckDB plugin -/* + initApplicationConfig({ // Custom UI for DuckDB plugins: [keplerGlDuckdbPlugin], @@ -46,9 +42,9 @@ initApplicationConfig({ } }), // progressive loading is sync, doesn't wait properly for a dataset to be created in DuckDB - useArrowProgressiveLoading: false + useArrowProgressiveLoading: false, + showReleaseBanner: false }); -*/ // Example: Register custom icons for the icon layer. // These will be merged with the default icons fetched from CDN. diff --git a/examples/demo-app/src/static/_redirects b/examples/demo-app/src/static/_redirects new file mode 100644 index 0000000000..952ebf0ce0 --- /dev/null +++ b/examples/demo-app/src/static/_redirects @@ -0,0 +1 @@ +/* / 200 diff --git a/examples/demo-app/tsconfig.json b/examples/demo-app/tsconfig.json new file mode 100644 index 0000000000..02473f16d0 --- /dev/null +++ b/examples/demo-app/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es2020", + "jsx": "react", + "module": "esnext", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "baseUrl": "." + }, + "include": ["src"] +} diff --git a/examples/demo-app/yarn.lock b/examples/demo-app/yarn.lock index 6716d3198f..3b4f5ac8b4 100644 --- a/examples/demo-app/yarn.lock +++ b/examples/demo-app/yarn.lock @@ -10824,6 +10824,13 @@ __metadata: languageName: node linkType: hard +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962 + languageName: node + linkType: hard + "arraybuffer.prototype.slice@npm:^1.0.3": version: 1.0.3 resolution: "arraybuffer.prototype.slice@npm:1.0.3" @@ -11228,7 +11235,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.6.0": +"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -12115,6 +12122,15 @@ __metadata: languageName: node linkType: hard +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: "npm:^4.0.0" + checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c + languageName: node + linkType: hard + "dlv@npm:^1.1.3": version: 1.1.3 resolution: "dlv@npm:1.1.3" @@ -12416,6 +12432,20 @@ __metadata: languageName: node linkType: hard +"esbuild-plugin-copy@npm:^2.1.1": + version: 2.1.1 + resolution: "esbuild-plugin-copy@npm:2.1.1" + dependencies: + chalk: "npm:^4.1.2" + chokidar: "npm:^3.5.3" + fs-extra: "npm:^10.0.1" + globby: "npm:^11.0.3" + peerDependencies: + esbuild: ">= 0.14.0" + checksum: 10c0/ae0cb7cbac7328de54cbdfbfc9d47a600a16ee7fff20d8b77afaa8c2999921fff21506db3be2c20e05fd76448f098ae76d1aaae4ccfa73293247a07931c9c3e2 + languageName: node + linkType: hard + "esbuild-plugin-replace@npm:^1.4.0": version: 1.4.0 resolution: "esbuild-plugin-replace@npm:1.4.0" @@ -12617,7 +12647,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -12878,6 +12908,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^10.0.1": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/5f579466e7109719d162a9249abbeffe7f426eb133ea486e020b89bc6d67a741134076bf439983f2eb79276ceaf6bd7b7c1e43c3fd67fe889863e69072fb0a5e + languageName: node + linkType: hard + "fs-minipass@npm:^3.0.0": version: 3.0.3 resolution: "fs-minipass@npm:3.0.3" @@ -13123,6 +13164,20 @@ __metadata: languageName: node linkType: hard +"globby@npm:^11.0.3": + version: 11.1.0 + resolution: "globby@npm:11.1.0" + dependencies: + array-union: "npm:^2.1.0" + dir-glob: "npm:^3.0.1" + fast-glob: "npm:^3.2.9" + ignore: "npm:^5.2.0" + merge2: "npm:^1.4.1" + slash: "npm:^3.0.0" + checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -13158,7 +13213,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -13423,6 +13478,13 @@ __metadata: languageName: node linkType: hard +"ignore@npm:^5.2.0": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 + languageName: node + linkType: hard + "image-size@npm:^0.7.4": version: 0.7.5 resolution: "image-size@npm:0.7.5" @@ -14075,6 +14137,19 @@ __metadata: languageName: node linkType: hard +"jsonfile@npm:^6.0.1": + version: 6.2.1 + resolution: "jsonfile@npm:6.2.1" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/e1abf000ecee9942d4d028a8e02dc752617face227d72afd1cfb2187e2433079e625bf82b807a313689db71b6472c6b2b389a2340d2798737b1199a39631c28a + languageName: node + linkType: hard + "jstat@npm:^1.9.6": version: 1.9.6 resolution: "jstat@npm:1.9.6" @@ -14782,7 +14857,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0": +"merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb @@ -15845,6 +15920,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c + languageName: node + linkType: hard + "pbf@npm:^3.1.0, pbf@npm:^3.2.1, pbf@npm:^3.3.0": version: 3.3.0 resolution: "pbf@npm:3.3.0" @@ -17127,6 +17209,7 @@ __metadata: d3-format: "npm:^2.0.0" dropbox: "npm:^4.0.12" esbuild: "npm:^0.28.1" + esbuild-plugin-copy: "npm:^2.1.1" esbuild-plugin-replace: "npm:^1.4.0" keymirror: "npm:^0.1.1" markdown-to-jsx: "npm:^7.7.6" @@ -17381,6 +17464,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -18405,6 +18495,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + "update-input-width@npm:^2.0.0": version: 2.0.0 resolution: "update-input-width@npm:2.0.0" diff --git a/src/components/src/modals/load-data-modal.tsx b/src/components/src/modals/load-data-modal.tsx index 4ba1234171..2fa8cedd36 100644 --- a/src/components/src/modals/load-data-modal.tsx +++ b/src/components/src/modals/load-data-modal.tsx @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // Copyright contributors to the kepler.gl project -import React, {useState} from 'react'; +import React, {useState, useCallback} from 'react'; import styled from 'styled-components'; import get from 'lodash/get'; import {IntlShape, useIntl} from 'react-intl'; @@ -14,6 +14,7 @@ import LoadingDialog from './loading-dialog'; import {LOADING_METHODS} from '@kepler.gl/constants'; import {FileLoading, FileLoadingProgress, LoadFiles} from '@kepler.gl/types'; +import {isTest} from '@kepler.gl/utils'; const StyledLoadDataModal = styled.div.attrs({ className: 'load-data-modal' @@ -28,12 +29,18 @@ const noop = () => { return; }; const getDefaultMethod = (methods: T[] = []) => - Array.isArray(methods) ? get(methods, [0]) : null; + // Try sample data modal by default for DuckDB preview + // Note: In tests only 3 loading methods are available + Array.isArray(methods) ? get(methods, [isTest() && methods.length > 4 ? 4 : 0]) : null; export interface LoadingMethod { id: string; label: string; elementType: React.ComponentType; - tabElementType?: React.ComponentType<{onClick: React.MouseEventHandler; intl: IntlShape}>; + tabElementType?: React.ComponentType<{ + onClick: React.MouseEventHandler; + intl: IntlShape; + className?: string; + }>; } type LoadDataModalProps = { @@ -99,17 +106,27 @@ export function LoadDataModalFactory( loadingMethods = defaultLoadingMethods, isCloudMapLoading, ...restProps - }) => { + }: LoadDataModalProps) => { const intl = useIntl(); + + // const {loadingMethods, isCloudMapLoading} = props; + const [currentMethod, toggleMethod] = useState(getDefaultMethod(loadingMethods)); + + const loadDataMethod = loadingMethods.find(ldm => ldm.id === 'upload'); + const enableLoadDataModal = useCallback(() => { + if (loadDataMethod) { + toggleMethod(loadDataMethod); + } + }, [loadDataMethod, toggleMethod]); + const currentModalProps = { ...restProps, onFileUpload, onTilesetAdded, fileLoading, - isCloudMapLoading + isCloudMapLoading, + enableLoadDataModal }; - // const {loadingMethods, isCloudMapLoading} = props; - const [currentMethod, toggleMethod] = useState(getDefaultMethod(loadingMethods)); const ElementType = currentMethod?.elementType; diff --git a/src/components/src/modals/modal-tabs.tsx b/src/components/src/modals/modal-tabs.tsx index 61c3f342bf..a437f6b858 100644 --- a/src/components/src/modals/modal-tabs.tsx +++ b/src/components/src/modals/modal-tabs.tsx @@ -79,7 +79,13 @@ export const ModalTabItem: React.FC = ({ const intl = useIntl(); return method.tabElementType ? ( - + ) : (