├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── copilot-instructions.md └── workflows │ └── main.yml ├── .gitignore ├── .storybook ├── main.js ├── preview-head.html ├── preview.js ├── tsconfig.json └── webpack.config.js ├── .yarnclean ├── DONOTUPGRADE.md ├── LICENSE ├── README.md ├── appveyor.yml ├── demo.sh ├── docker └── demo │ ├── Dockerfile │ ├── docker-compose.yml │ └── index.php ├── docs_dev └── content │ ├── .nojekyll │ ├── APICOMPAT.md │ ├── APIDOC.md │ ├── API_BROWSER.md │ ├── API_NPM.md │ ├── APPDEF.md │ ├── COMPONENTS.md │ ├── DEMO.md │ ├── DEVELOPING.md │ ├── GETTING_STARTED.md │ ├── KNOWN_ISSUES.md │ ├── LAYER_STRUCTURE.md │ ├── README.md │ ├── RELEASE_NOTES.md │ ├── RELEASE_PROCESS.md │ ├── TEMPLATES.md │ ├── _coverpage.md │ ├── _sidebar.md │ ├── ajax-viewer.png │ ├── aqua.png │ ├── generic.png │ ├── index.html │ ├── limegold.png │ ├── maroon.png │ ├── mrl_base_url.png │ ├── mrl_template_preview.png │ ├── pwd-add-instance.png │ ├── pwd-landing-page.png │ ├── pwd-port.png │ ├── pwd-success.png │ ├── sidebar.png │ ├── slate.png │ └── turquoise-yellow.png ├── mocks ├── fileMock.js └── styleMock.js ├── package.json ├── src ├── actions │ ├── app.ts │ ├── defs.ts │ ├── flyout.ts │ ├── init-command.ts │ ├── init-generic.ts_ │ ├── init-mapguide.ts │ ├── init.ts │ ├── legend.ts │ ├── map.ts │ ├── modal.ts │ ├── taskpane.ts │ └── template.ts ├── api │ ├── base-layer-set.ts │ ├── bootstrap.ts │ ├── builders │ │ ├── deArrayify.ts │ │ ├── factory.ts │ │ └── mapagent.ts │ ├── client.ts │ ├── common.ts │ ├── composite-selection.ts │ ├── contracts │ │ ├── common.ts │ │ ├── fusion.ts │ │ ├── map-definition.ts │ │ ├── query.ts │ │ ├── runtime-map.ts │ │ ├── tile-set-definition.ts │ │ └── weblayout.ts │ ├── default-commands.ts │ ├── default-components.tsx │ ├── error.ts │ ├── expr-eval-context.ts │ ├── generic-layer-set-group.ts_ │ ├── generic-layer-set.ts │ ├── i18n.ts │ ├── layer-manager.ts │ ├── layer-manager │ │ ├── csv-driver.ts │ │ ├── driver-registry.ts │ │ ├── format-driver.ts │ │ └── parsed-features.ts │ ├── layer-set-contracts.ts │ ├── layer-set-group-base.ts │ ├── layer-set.ts │ ├── lazy.ts │ ├── mapguide-commands.ts │ ├── mapguide-components.tsx │ ├── mg-layer-set-group.ts │ ├── ol-factory.ts │ ├── ol-mapguide-source-factory.ts │ ├── ol-mapguide-source.ts_ │ ├── ol-style-builders.ts │ ├── ol-style-contracts.ts │ ├── ol-style-helpers.ts │ ├── ol-style-map-set.ts │ ├── ol-types.ts │ ├── registry │ │ ├── command-spec.ts │ │ ├── command.ts │ │ ├── component.tsx │ │ ├── external-layer.ts │ │ ├── layout.ts │ │ └── projections.ts │ ├── request-builder.ts │ ├── runtime.ts │ ├── selection-count.ts │ └── session-store.ts ├── components │ ├── about.tsx │ ├── accordion.tsx │ ├── base-layer-switcher.tsx │ ├── color-picker.tsx │ ├── context.tsx │ ├── elements │ │ ├── element-context.tsx │ │ └── providers │ │ │ └── blueprint │ │ │ ├── button.tsx │ │ │ ├── callout.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── drawer.tsx │ │ │ ├── editable-text.tsx │ │ │ ├── file-input.tsx │ │ │ ├── form-group.tsx │ │ │ ├── icon.tsx │ │ │ ├── input-group.tsx │ │ │ ├── menu.tsx │ │ │ ├── non-ideal-state.tsx │ │ │ ├── numeric-input.tsx │ │ │ ├── popover.tsx │ │ │ ├── provider.tsx │ │ │ ├── radio.tsx │ │ │ ├── select.tsx │ │ │ ├── slider.tsx │ │ │ ├── spinner.tsx │ │ │ ├── switch.tsx │ │ │ ├── tab-set.tsx │ │ │ └── utils.ts │ ├── error.tsx │ ├── external-layer-factory.ts │ ├── flyout-region.tsx │ ├── form-frame-shim.tsx │ ├── icon-names.ts │ ├── icon.tsx │ ├── layer-manager │ │ ├── add-layer.tsx │ │ ├── add-wfs-layer.tsx │ │ ├── add-wms-layer.tsx │ │ ├── color-brewer.tsx │ │ ├── common.tsx │ │ ├── legend.ts │ │ ├── manage-layers.tsx │ │ ├── wfs-capabilities-panel.tsx │ │ ├── wfs-capabilities-parser.ts │ │ └── wms-capabilities-panel.tsx │ ├── legend.tsx │ ├── map-load-indicator.tsx │ ├── map-menu.tsx │ ├── map-providers │ │ ├── base.ts │ │ ├── context.tsx │ │ ├── generic.ts_ │ │ └── mapguide.ts │ ├── map-viewer-base.tsx_ │ ├── map-viewer-context.ts_ │ ├── mapguide-debug-context.ts │ ├── modal-dialog.tsx │ ├── mouse-coordinates.tsx │ ├── navigator.tsx │ ├── pbmg.tsx │ ├── scale-display.tsx │ ├── selected-feature-count.tsx │ ├── selection-panel.tsx │ ├── session-expired.tsx │ ├── session-keep-alive.tsx │ ├── task-pane.tsx │ ├── toolbar.tsx │ ├── tooltips │ │ ├── feature.ts │ │ ├── mouse.ts │ │ ├── selected-features.ts │ │ └── utfgrid.ts │ ├── vector-style-editor.tsx │ └── view-size.tsx ├── constants.ts ├── constants │ ├── actions.ts │ └── assets.ts ├── containers │ ├── add-manage-layers.tsx │ ├── app.tsx │ ├── base-layer-switcher.tsx │ ├── coordinate-tracker.tsx │ ├── flyout-region.tsx │ ├── hooks-generic.ts │ ├── hooks-mapguide.ts │ ├── hooks.ts │ ├── init-warning-display.tsx │ ├── legend.tsx │ ├── map-capturer-context.ts │ ├── map-menu.tsx │ ├── map-viewer.tsx_ │ ├── measure-context.ts │ ├── measure.tsx │ ├── modal-launcher.tsx │ ├── mouse-coordinates.tsx │ ├── navigator.tsx │ ├── neo-map-viewer.tsx │ ├── quick-plot.tsx │ ├── scale-display.tsx │ ├── selected-feature-count.tsx │ ├── selection-panel.tsx │ ├── share-link-to-view.tsx │ ├── subscriber.tsx │ ├── task-pane.tsx │ ├── toolbar.tsx │ ├── url-state.ts │ ├── view-size.tsx │ ├── viewer-options.tsx │ └── viewer-shim.tsx ├── entries │ ├── application.tsx │ ├── externs.d.ts │ ├── library-generic.tsx_ │ └── library.tsx ├── layouts │ ├── ajax-viewer.tsx │ ├── aqua.tsx │ ├── generic.tsx │ ├── hooks.ts │ ├── limegold.tsx │ ├── maroon.tsx │ ├── sidebar.tsx │ ├── slate.tsx │ └── turquoise-yellow.tsx ├── reducers │ ├── config.ts │ ├── init-error.ts │ ├── last-action.ts │ ├── map-state.ts │ ├── modal.ts │ ├── mouse.ts │ ├── root.ts │ ├── taskpane.ts │ ├── template.ts │ ├── toolbar.ts │ └── viewer.ts ├── store │ ├── configure-store.ts │ ├── logger.ts │ └── promise-middleware.ts ├── stories │ ├── accordion.stories.tsx │ ├── common.stories.tsx │ ├── data │ │ ├── test-app-def.json │ │ ├── test-runtime-map-melbourne.json │ │ ├── test-runtime-map-redding.json │ │ ├── test-runtime-map-sheboygan.json │ │ └── test-selection-response-sheboygan.json │ ├── elements.stories.tsx │ ├── fake-app.tsx │ ├── map-story-frame.tsx │ ├── map.stories.tsx │ ├── mapguide.stories.tsx │ ├── static │ │ └── server │ │ │ ├── TaskPane.html │ │ │ ├── page-a.html │ │ │ └── page-b.html │ ├── story-bootstrap.tsx │ └── styles │ │ └── accordion-slate.css ├── strings │ ├── en.ts │ └── msgdef.ts ├── styles │ └── index.css └── utils │ ├── array.ts │ ├── assert.ts │ ├── asset.ts │ ├── browser-support.ts │ ├── logger.ts │ ├── menu.ts │ ├── never.ts │ ├── number.ts │ ├── scoped-id.ts │ ├── site-version.ts │ ├── string.ts │ ├── type-guards.ts │ ├── units.tsx │ ├── url.ts │ └── viewer-state.ts ├── stdassets ├── bp-icons.js ├── cursors │ ├── digitizeCircle.cur │ ├── digitizeLine.cur │ ├── digitizeLineString.cur │ ├── digitizePoint.cur │ ├── digitizePolygon.cur │ ├── digitizeRectangle.cur │ ├── grab.cur │ ├── grabbing.cur │ ├── zoomin.cur │ └── zoomout.cur ├── images │ ├── icons │ │ ├── PoweredBy_en.png │ │ ├── about.png │ │ ├── application-browser.png │ │ ├── back.png │ │ ├── buffer.png │ │ ├── control-180.png │ │ ├── control-stop-180.png │ │ ├── control-stop.png │ │ ├── control.png │ │ ├── coordinate-tracker.png │ │ ├── edit-copy.png │ │ ├── edit-cut.png │ │ ├── edit-duplicate.png │ │ ├── edit-paste.png │ │ ├── edit-xml.png │ │ ├── feature-info.png │ │ ├── file-print.png │ │ ├── file-save.png │ │ ├── folder-horizontal-open.png │ │ ├── folder-horizontal.png │ │ ├── forward.png │ │ ├── geolocation.png │ │ ├── globe-share.png │ │ ├── globe_add.png │ │ ├── help.png │ │ ├── iconNavigator.png │ │ ├── icon_error.png │ │ ├── icon_home.png │ │ ├── icon_menuarrow.png │ │ ├── icon_menuarrow_disabled.png │ │ ├── icon_menuarrowup.png │ │ ├── icon_menuarrowup_disabled.png │ │ ├── icon_refreshmap.png │ │ ├── icon_select.png │ │ ├── icon_warning.png │ │ ├── icon_zoomselect.png │ │ ├── info.png │ │ ├── initial-center.png │ │ ├── invoke-script.png │ │ ├── invoke-url.png │ │ ├── layer_add.png │ │ ├── lc_unselect.png │ │ ├── legend-DWF.png │ │ ├── legend-layer.png │ │ ├── legend-map.png │ │ ├── legend-raster.png │ │ ├── legend-theme.png │ │ ├── maptip.png │ │ ├── measure.png │ │ ├── options.png │ │ ├── out-of-range.png │ │ ├── overview-map.png │ │ ├── pan-east.png │ │ ├── pan-north.png │ │ ├── pan-south.png │ │ ├── pan-west.png │ │ ├── pan.png │ │ ├── preview.png │ │ ├── print.png │ │ ├── property.png │ │ ├── query.png │ │ ├── redline.png │ │ ├── search.png │ │ ├── select-centre.png │ │ ├── select-clear.png │ │ ├── select-features.png │ │ ├── select-polygon.png │ │ ├── select-radius.png │ │ ├── select-zoom.png │ │ ├── select.png │ │ ├── theme.png │ │ ├── toggle-expand.png │ │ ├── toggle.png │ │ ├── ui-menu.png │ │ ├── view-back.png │ │ ├── view-forward.png │ │ ├── view-refresh.png │ │ ├── zoom-dynamic.png │ │ ├── zoom-full.png │ │ ├── zoom-in-fixed.png │ │ ├── zoom-in.png │ │ └── zoom-out-fixed.png │ └── res │ │ ├── marker.png │ │ ├── slider.png │ │ ├── sliderscale.png │ │ └── spinner.gif └── sprites │ ├── icons.css │ ├── icons.html │ └── icons.png ├── test-data ├── 631_appdef.ts ├── index.ts ├── mdf_melbourne.ts ├── mdf_redding.ts ├── mdf_sheboygan.ts ├── mdf_world.ts ├── multimap_appdef.ts ├── multimap_statelesss_appdef.ts ├── qmf_sheboygan.ts ├── rtmap_melbourne.ts ├── rtmap_redding.ts ├── rtmap_sheboygan.ts ├── sheboygan_weblayout.ts └── tsd_xyz_sheboygan.ts ├── test ├── actions │ ├── app.spec.ts │ ├── flyout.spec.ts │ ├── init-command.spec.ts │ ├── init.spec.ts │ ├── legend.spec.ts │ ├── map.spec.ts │ ├── modal.spec.ts │ ├── taskpane.spec.ts │ └── template.spec.ts ├── api │ ├── builders │ │ └── de-arrayify.spec.ts │ ├── error.spec.ts │ ├── generic-layer-set.spec.ts │ ├── i18n.spec.ts │ ├── layer-manager │ │ ├── csv-driver.spec.ts │ │ ├── format-driver.spec.ts │ │ └── parsed-features.spec.ts │ ├── lazy.spec.ts │ ├── ol-style-map-set.spec.ts │ └── registry │ │ └── command.spec.ts ├── components │ ├── about.spec.tsx │ ├── accordion.spec.tsx │ ├── base-layer-switcher.spec.tsx │ ├── error.spec.tsx │ ├── layer-manager │ │ ├── color-brewer.spec.tsx │ │ ├── common.spec.tsx │ │ ├── legend.spec.ts │ │ ├── wfs-capabilities-panel.spec.tsx │ │ ├── wfs-capabilities-parser.spec.ts │ │ └── wms-capabilities-panel.spec.tsx │ ├── legend.spec.tsx │ ├── map-load-indicator.spec.tsx │ ├── map-menu.spec.tsx │ ├── menu.spec.tsx │ ├── mouse-coordinates.spec.tsx │ ├── navigator.spec.tsx │ ├── providers │ │ └── mapguide.spec.ts │ ├── scale-display.spec.tsx │ ├── selected-feature-count.spec.tsx │ ├── selection-panel.spec.tsx │ ├── session-expired.spec.tsx │ ├── session-keep-alive.spec.tsx │ ├── toolbar.spec.tsx │ ├── vector-style-editor.spec.tsx │ └── view-size.spec.tsx ├── containers │ ├── view-size.spec.tsx │ └── viewer-options.spec.tsx ├── reducers │ ├── config.spec.ts │ ├── initError.spec.ts │ ├── last-action.spec.ts │ ├── map-state.spec.ts │ ├── modal.spec.ts │ ├── mouse.spec.ts │ ├── root.spec.ts │ ├── taskpane.spec.ts │ ├── template.spec.ts │ ├── toolbar.spec.ts │ └── viewer.spec.ts └── utils │ ├── array.spec.ts │ ├── assert.spec.ts │ ├── asset.spec.ts │ ├── browser-support.spec.ts │ ├── logger.spec.ts │ ├── menu.spec.ts │ ├── never.spec.ts │ ├── number.spec.ts │ ├── scoped-id.spec.ts │ ├── site-version.spec.ts │ ├── string.spec.ts │ ├── type-guards.spec.ts │ ├── units.spec.ts │ ├── url.spec.ts │ ├── version.spec.ts │ └── viewer-state.spec.ts ├── tools └── prepare-package.js ├── tsconfig.base.json ├── tsconfig.dts.json ├── tsconfig.json ├── tsconfig.npm.json ├── tsconfig.npmdoc.json ├── tslint.json ├── typedoc.json ├── viewer ├── appdef.json ├── aqua.html ├── config.json ├── css │ ├── ajax-viewer.css │ ├── aqua.css │ ├── generic.css │ ├── limegold.css │ ├── maroon.css │ ├── sidebar.css │ ├── slate.css │ └── turquoise-yellow.css ├── data │ ├── appdef.cog.json │ ├── appdef.generic.json │ └── appdef.staticimage.json ├── examples │ └── taskpane │ │ ├── drawing.html │ │ ├── marker2.png │ │ ├── subscriberapi.html │ │ └── viewerapi.html ├── generic.html ├── help │ └── index.html ├── index.html ├── index.php ├── limegold.html ├── maroon.html ├── server │ └── TaskPane.html ├── sidebar.html ├── slate.html ├── strings │ ├── de.json │ └── he.json ├── turquoiseyellow.html └── web.config ├── vitest-setup.ts ├── vitest.config.ts ├── webpack.config.js └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: 5 | - bug 6 | 7 | --- 8 | 9 | # IMPORTANT! Please read this first 10 | 11 | Issues in this repository concern only the viewer. **Do not submit bug reports concerning the MapGuide Server, FDO or isses in any other web mapping server or services**. Use the appropriate issue trackers for reporting such issues because this repo is not for that! 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **What is your environment?** 17 | 1. MapGuide Installation 18 | * Version: 19 | * OS: 20 | * WebTier configuration (IIS/Apache): 21 | 2. mapguide-react-layout version: 22 | 23 | **To Reproduce** 24 | Steps to reproduce the behavior: 25 | 1. Go to '...' 26 | 2. Click on '....' 27 | 3. See error 28 | 29 | **Expected behavior** 30 | A clear and concise description of what you expected to happen. 31 | 32 | **Screenshots/Browser DevTools output** 33 | Does your browser devtools (F12) show any console/network errors at the time the problem occurs or is there any visual indicators of the problem? 34 | 35 | Include relevant output/screenshots here -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: 5 | - feature request 6 | 7 | --- 8 | 9 | # IMPORTANT! Please read this first 10 | 11 | Issues in this repository concern only the viewer. **Do not submit feature requests concerning the MapGuide Server, FDO or isses in any other web mapping server or services**. Use the appropriate issue trackers for reporting such issues because this repo is not for that! 12 | 13 | **Is your feature request related to a problem? Please describe.** 14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 15 | 16 | **Describe the solution you'd like** 17 | A clear and concise description of what you want to happen. -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | - name: Install yarn 29 | run: | 30 | curl -o- -L https://yarnpkg.com/install.sh | bash 31 | export PATH="$HOME/.yarn/bin:$PATH" 32 | yarn install 33 | 34 | - name: Audit packages (production packages only) 35 | run: yarn audit --groups "dependencies" 36 | 37 | - name: Lint and run tests 38 | run: yarn run ci:gha 39 | 40 | - name: Coveralls 41 | uses: coverallsapp/github-action@master 42 | with: 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | path-to-lcov: ./coverage-report/lcov.info 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .cache-loader 3 | coverage-report 4 | e2e/screenshots 5 | docs_output 6 | dist 7 | lib 8 | build 9 | coverage 10 | node_modules 11 | typings 12 | yarn-error.log 13 | npm-debug.log 14 | index.d.ts 15 | !src/externs/*.js 16 | viewer/dist 17 | declarations 18 | webconfig.ini 19 | viewer.zip 20 | storybook.zip 21 | apidocs.zip 22 | package 23 | webpack_stats_prod.json 24 | webpack_stats_dev.json 25 | index_frame.html 26 | slate_frame.html 27 | storybook-static 28 | src/index.ts 29 | viewer/ApplicationDefinition.schema.json -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | framework: "@storybook/react-webpack5", 3 | stories: ["../src/stories/*.stories.tsx"], 4 | 5 | core: { 6 | builder: "webpack5", 7 | disableTelemetry: true 8 | }, 9 | 10 | staticDirs: ['../src/stories/static'], 11 | 12 | addons: [ 13 | "@storybook/addon-knobs", 14 | "@storybook/addon-viewport", 15 | "@storybook/addon-links", 16 | "@storybook/addon-actions" 17 | ], 18 | 19 | docs: { 20 | autodocs: true 21 | } 22 | }; -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/.storybook/preview-head.html -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | //import '@storybook/addon-console'; 2 | //import { configure, addParameters } from '@storybook/react'; 3 | //import { INITIAL_VIEWPORTS, DEFAULT_VIEWPORT } from "@storybook/addon-viewport"; 4 | import "../src/stories/story-bootstrap"; 5 | import "../src/styles/index.css"; 6 | /* 7 | addParameters({ 8 | viewport: { 9 | viewports: INITIAL_VIEWPORTS, 10 | defaultViewport: DEFAULT_VIEWPORT, 11 | }, 12 | }); 13 | 14 | const req = require.context("../src/stories", true, /.stories.tsx$/); 15 | function loadStories() { 16 | req.keys().forEach(req); 17 | } 18 | configure(loadStories, module); 19 | */ 20 | export const parameters = { 21 | actions: { argTypesRegex: "^on[A-Z].*" }, 22 | controls: { 23 | matchers: { 24 | color: /(background|color)$/i, 25 | date: /Date$/, 26 | }, 27 | }, 28 | } -------------------------------------------------------------------------------- /.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "allowSyntheticDefaultImports": true, 5 | "module": "es2015", 6 | "target": "es5", 7 | "lib": ["es6", "dom"], 8 | "sourceMap": true, 9 | "allowJs": false, 10 | "jsx": "react", 11 | "moduleResolution": "node", 12 | "rootDir": "../src", 13 | "outDir": "dist", 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": true, 18 | "declaration": true 19 | }, 20 | "include": ["../src/**/*"], 21 | "exclude": [ 22 | "node_modules", 23 | "build", 24 | "scripts" 25 | ] 26 | } -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = ({ config }) => { 5 | config.devtool = "source-map"; 6 | config.plugins.push(new webpack.DefinePlugin({ 7 | __DEV__: process.env.BUILD_MODE !== 'production', 8 | __VERSION__: JSON.stringify(process.env.APPVEYOR_BUILD_VERSION || ""), 9 | __COMMITHASH__: JSON.stringify(process.env.APPVEYOR_REPO_COMMIT || ""), 10 | __BRANCH__: JSON.stringify(process.env.APPVEYOR_REPO_BRANCH || "master") 11 | })); 12 | config.module.rules.push({ //sourcemap 13 | test: /\.js$/, 14 | loader: "source-map-loader", 15 | include: /@blueprintjs/, 16 | enforce: "pre" 17 | }); 18 | config.module.rules.push({ 19 | test: /\.(ts|tsx)$/, 20 | use: [ 21 | { 22 | loader: require.resolve("ts-loader"), 23 | options: { 24 | configFile: path.resolve(__dirname, "..", "tsconfig.json") 25 | } 26 | }/*, 27 | { 28 | loader: require.resolve("react-docgen-typescript-loader") 29 | }*/ 30 | ] 31 | }); 32 | config.resolve.extensions.push(".ts", ".tsx") 33 | return config 34 | } -------------------------------------------------------------------------------- /.yarnclean: -------------------------------------------------------------------------------- 1 | @types/**/node_modules -------------------------------------------------------------------------------- /DONOTUPGRADE.md: -------------------------------------------------------------------------------- 1 | # Packages which cannot be upgraded until further notice 2 | 3 | ## react, react-dom 4 | 5 | Current Version: `17.0.2` 6 | Latest Version: `18.x.x` 7 | 8 | Reasons for not upgrading: It's not so much the package itself may break our library, but rather the ecosystem of supplemental libraries surrounding it that may still require react 17 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jackie Ng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd docker/demo 3 | docker build -t jumpinjackie/mapguide-react-layout-demo . 4 | # run without starting tomcat 5 | docker run -p 80:8008 -t jumpinjackie/mapguide-react-layout-demo --no-tomcat 6 | -------------------------------------------------------------------------------- /docker/demo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM brucepc/mapguide 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | 5 | # --------- 6 | # MULTIVERSE 7 | # --------- 8 | RUN apt-get update 9 | RUN apt-get install -y --no-install-recommends software-properties-common wget unzip curl 10 | RUN apt-add-repository multiverse 11 | RUN apt-get update 12 | 13 | # --------- 14 | # MS CORE FONTS 15 | # --------- 16 | # from http://askubuntu.com/a/25614 17 | RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | debconf-set-selections 18 | RUN apt-get install -y --no-install-recommends fontconfig ttf-mscorefonts-installer 19 | # ADD localfonts.conf /etc/fonts/local.conf 20 | # RUN fc-cache -f -v 21 | 22 | RUN wget https://github.com/jumpinjackie/mapguide-react-layout/releases/download/v0.13.1/viewer.zip -O /tmp/viewer.zip 23 | RUN wget http://download.osgeo.org/mapguide/releases/3.0.0/extras/Sheboygan.mgp -O /usr/local/mapguideopensource-3.1.2/server/Packages/Sheboygan.mgp 24 | RUN unzip /tmp/viewer.zip -d /usr/local/mapguideopensource-3.1.2/webserverextensions/www 25 | ADD index.php /usr/local/mapguideopensource-3.1.2/webserverextensions/www -------------------------------------------------------------------------------- /docker/demo/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | demo: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | ports: 8 | - "8008:8008" 9 | command: ["--no-tomcat"] -------------------------------------------------------------------------------- /docs_dev/content/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/.nojekyll -------------------------------------------------------------------------------- /docs_dev/content/API_NPM.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | `mapguide-react-layout` is a modern map viewer for [MapGuide Open Source](http://mapguide.osgeo.org) and [Autodesk Infrastructure Map Server](http://www.autodesk.com/products/infrastructure-map-server/overview) 4 | 5 | This API documentation covers the `mapguide-react-layout` npm module (version **0.14.10**) 6 | 7 | For an example of how this npm module is used, check out the [mapguide-react-layout-example](https://github.com/jumpinjackie/mapguide-react-layout-example) 8 | 9 | # Browser Support 10 | 11 | `mapguide-react-layout` is supported on all modern browsers 12 | * Chrome 13 | * Firefox 14 | * Safari 15 | * Microsoft Edge 16 | 17 | # Usage 18 | 19 | `mapguide-react-layout` is available as: 20 | 21 | * A pre-built bundle (`viewer.js`) 22 | * A set of transpiled CommonJS modules on [npm](https://www.npmjs.com/package/mapguide-react-layout) 23 | 24 | # TypeScript 25 | 26 | `mapguide-react-layout` is written in [TypeScript](https://www.typescriptlang.org/) and `.d.ts` type definitions are included with the npm package. 27 | 28 | Although you can consume this package with vanilla JavaScript, the API documentation assumes you are also using TypeScript 29 | 30 | # Installation 31 | 32 | With npm: `npm install --save mapguide-react-layout` 33 | 34 | With yarn: `yarn add mapguide-react-layout` 35 | 36 | # Importing Modules 37 | 38 | All APIs provided/documented by this library are importable from the main `mapguide-react-layout` 39 | 40 | > Previous releases allowed you to "piecemeal" import what you want by importing from `mapguide-react-layout/lib/path/to/module`. This style of import is no longer supported and you should just import whatever you require from `mapguide-react-layout` itself -------------------------------------------------------------------------------- /docs_dev/content/DEMO.md: -------------------------------------------------------------------------------- 1 | Demo 2 | ==== 3 | 4 | With [play-with-docker](http://play-with-docker.com), you can easily spin up a demo MapGuide Server instance with this viewer and sample data pre-loaded to see this viewer in action. 5 | 6 | 1. Go to http://play-with-docker.com 7 | 2. Pass the reCAPTCHA check. Once you're in, you will have 4 hours to play around before the site terminates your session and all docker environments you have set up 8 | 3. Click `+ ADD NEW INSTANCE` to create a new shell 9 | 10 | ![](https://github.com/jumpinjackie/mapguide-react-layout/raw/master/docs/pwd-add-instance.png) 11 | 12 | 4. In the new shell, run the following commands 13 | * `git clone https://github.com/jumpinjackie/mapguide-react-layout` 14 | * `cd mapguide-react-layout` 15 | * `./demo.sh` 16 | 5. After a few minutes (while docker downloads the base image and builds the demo image on top of it), the demo container will run and you should see a port number appear beside the IP address 17 | 18 | ![](https://github.com/jumpinjackie/mapguide-react-layout/raw/master/docs/pwd-port.png) 19 | 20 | 6. Click it to open a new browser tab where you should see the default success page from the wwwroot of MapGuide's bundled Apache server 21 | 22 | ![](https://github.com/jumpinjackie/mapguide-react-layout/raw/master/docs/pwd-success.png) 23 | 24 | 7. Now append `/mapguide/index.php` to that URL to enter the demo landing page. 25 | 26 | ![](https://github.com/jumpinjackie/mapguide-react-layout/raw/master/docs/pwd-landing-page.png) 27 | 28 | 8. Click on any template link to load the Sheboygan map in that selected template 29 | 30 | Many thanks to @brucepc for his [docker image](https://github.com/brucepc/mapguide), from which our demo image is built on top of. -------------------------------------------------------------------------------- /docs_dev/content/LAYER_STRUCTURE.md: -------------------------------------------------------------------------------- 1 | # Viewer Layer Structure 2 | 3 | The `mapguide-react-layout` viewer employs the following layer structure (from top to bottom in draw order) 4 | 5 | > TODO: A better visual diagram here would help 6 | 7 | * (hidden from listing): Vector layer for measurement results 8 | * (hidden from listing): Hover highlight layer for client-side vector layers 9 | * (hidden from listing): Scratch vector layer for selected WMS features 10 | * External Layer Set 11 | * 0 or more external layers 12 | * Core Map Layer Set 13 | * MapGuide -or- Subject Layer 14 | * If MapGuide 15 | * Active Selected Feature (if sub-selection is requested from Selection Panel) 16 | * Selection Overlay (if features have been selected) 17 | * Primary Map Overlay (Legend component toggles the layer/group visibility of this overlay) 18 | * 0 or more tiled layer groups (if defined in Map Definition) 19 | * External Base Layer 20 | * 1 of any of the following: 21 | * None 22 | * Your custom XYZ tileset 23 | * OpenStreetMap 24 | * Stamen 25 | * Bing Maps 26 | 27 | ## External Layer Set 28 | 29 | This section represents layers that are managed by the External Layer Manager component. Layers within 30 | this section have mutable draw order and can be re-ordered by the External Layer Manager UI or through 31 | the layer manager API provided by the viewer. 32 | 33 | Layers in this section will always display on top of layers in the `Core Map Layer Set`. There is no means for layers in the `Core Map Layer Set` to display on top of this section. 34 | 35 | ## Core Map Layer Set 36 | 37 | This group represents the main focus your viewer application. Layers within this section have fixed draw order and cannot be re-ordered by any UI or viewer APIs. -------------------------------------------------------------------------------- /docs_dev/content/RELEASE_PROCESS.md: -------------------------------------------------------------------------------- 1 | Release Process 2 | =============== 3 | 4 | 1. Ensure the following files are using the new release version number: 5 | * `appveyor.yml` 6 | * `package.json` 7 | * `docs_dev/content/API_NPM.md` (In the **Introduction** section). You may skip this part if doing a point (bugfix) release. 8 | 2. Write up new features/fixes in `docs_dev/content/RELEASE_NOTES.md` 9 | 3. Generate API docs and copy output to a new versioned subdirectory in the `gh-pages` branch 10 | 3.1. Rename `latest` directory to the previous version before doing this 11 | 4. Regenerate static site content and overwrite existing site html in the `gh-pages` branch 12 | 4.1. Ensure the following static site content reside in the versioned subdirectory as well: 13 | * `api_browser.html` 14 | 5. Build the npm package `yarn run build:npm` and publish a `-pre` package 15 | 6. Verify the [sample project](https://github.com/jumpinjackie/mapguide-react-layout-example) works with the published package 16 | 7. Once the sample project is verified as working, start the actual release process: 17 | 18 | 7.1. `git tag ` 19 | 20 | 7.2. `git push origin --tags` 21 | 22 | 7.3. This will trigger appveyor build and automate the creation of a GitHub release for `` 23 | 24 | 8. Switch to this new tag to publish the real npm package 25 | 26 | 8.1. `yarn run build:barrels` 27 | 28 | 8.2. `yarn run build:npm` 29 | 30 | 8.3. `cd package` 31 | 32 | 8.4. `npm publish` 33 | 34 | 9. Go to the new GitHub release and publish it with the new release notes. 35 | 36 | 37 | Release Process - Pre-release packages 38 | ====================================== 39 | 40 | Pre-release packages should be published under the `next` tag 41 | 42 | 1. Set version in `package.json` to include a pre-release suffix (eg. `-alpha.123`) 43 | 2. Run `yarn run build:barrels` 44 | 3. Run `yarn run build:npm` 45 | 4. `cd package` 46 | 5. Run `npm publish --tag next` -------------------------------------------------------------------------------- /docs_dev/content/_coverpage.md: -------------------------------------------------------------------------------- 1 | # mapguide-react-layout 2 | 3 | > A modern map viewer for MapGuide Open Source 4 | 5 | [GitHub](https://github.com/jumpinjackie/mapguide-react-layout) 6 | [Get Started](GETTING_STARTED.md) 7 | [Release Notes](RELEASE_NOTES.md) 8 | [API Docs](APIDOC.md) 9 | [Download](https://github.com/jumpinjackie/mapguide-react-layout/releases/latest) 10 | [Storybook](https://jumpinjackie.github.io/mapguide-react-layout/master/storybook-static/index.html) 11 | 12 | ![color](#3f3f3f) -------------------------------------------------------------------------------- /docs_dev/content/_sidebar.md: -------------------------------------------------------------------------------- 1 | - [Getting Started](GETTING_STARTED.md) 2 | - [About](README.md) 3 | - [Release Notes](RELEASE_NOTES.md) 4 | - Guide 5 | - [Templates](TEMPLATES.md) 6 | - [Developer Guide](DEVELOPING.md) 7 | - [Application Definiton Reference](APPDEF.md) 8 | - [Components](COMPONENTS.md) 9 | - [Known Issues](KNOWN_ISSUES.md) 10 | - [API](APIDOC.md) 11 | - [Browser API Overview](API_BROWSER.md) 12 | - [Viewer API Compatibility with AJAX/Fusion viewer](API_COMPAT.md) 13 | - [npm package](API_NPM.md) -------------------------------------------------------------------------------- /docs_dev/content/ajax-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/ajax-viewer.png -------------------------------------------------------------------------------- /docs_dev/content/aqua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/aqua.png -------------------------------------------------------------------------------- /docs_dev/content/generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/generic.png -------------------------------------------------------------------------------- /docs_dev/content/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mapguide-react-layout 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 |
18 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs_dev/content/limegold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/limegold.png -------------------------------------------------------------------------------- /docs_dev/content/maroon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/maroon.png -------------------------------------------------------------------------------- /docs_dev/content/mrl_base_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/mrl_base_url.png -------------------------------------------------------------------------------- /docs_dev/content/mrl_template_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/mrl_template_preview.png -------------------------------------------------------------------------------- /docs_dev/content/pwd-add-instance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/pwd-add-instance.png -------------------------------------------------------------------------------- /docs_dev/content/pwd-landing-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/pwd-landing-page.png -------------------------------------------------------------------------------- /docs_dev/content/pwd-port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/pwd-port.png -------------------------------------------------------------------------------- /docs_dev/content/pwd-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/pwd-success.png -------------------------------------------------------------------------------- /docs_dev/content/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/sidebar.png -------------------------------------------------------------------------------- /docs_dev/content/slate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/slate.png -------------------------------------------------------------------------------- /docs_dev/content/turquoise-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jumpinjackie/mapguide-react-layout/1ba697be2e140fbf8e180d88743e94eb26ee05ef/docs_dev/content/turquoise-yellow.png -------------------------------------------------------------------------------- /mocks/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; -------------------------------------------------------------------------------- /mocks/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; -------------------------------------------------------------------------------- /src/actions/app.ts: -------------------------------------------------------------------------------- 1 | import { ActionType } from "../constants/actions"; 2 | import { ISetAppSettingAction } from "./defs"; 3 | 4 | /** 5 | * Sets an app setting to the given value 6 | * 7 | * @param key 8 | * @param value 9 | * @since 0.14.8 10 | */ 11 | export function setAppSetting(key: string, value: string): ISetAppSettingAction { 12 | return { 13 | type: ActionType.SET_APP_SETTING, 14 | payload: { 15 | key, 16 | value 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/actions/modal.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IModalComponentDisplayOptions, 3 | IModalDisplayOptions 4 | } from "../api/common"; 5 | import { ActionType } from '../constants/actions'; 6 | import { ICloseModalAction, IShowModalUrlAction, IShowComponentInModalAction, IUpdateModalDimensionsAction, ModalChangeArgs } from './defs'; 7 | 8 | /** 9 | * Displays the specified component in a modal dialog 10 | * 11 | * @param {*} options Modal dialog display options 12 | * @returns {IShowComponentInModalAction} 13 | */ 14 | export function showModalComponent(options: IModalComponentDisplayOptions): IShowComponentInModalAction { 15 | return { 16 | type: ActionType.MODAL_SHOW_COMPONENT, 17 | payload: { 18 | ...options 19 | } 20 | }; 21 | } 22 | 23 | /** 24 | * Displays the specified URL in a modal dialog 25 | * 26 | * @param {*} options Modal dialog display options 27 | * @returns {IShowModalUrlAction} 28 | */ 29 | export function showModalUrl(options: IModalDisplayOptions): IShowModalUrlAction { 30 | return { 31 | type: ActionType.MODAL_SHOW_URL, 32 | payload: { 33 | ...options 34 | } 35 | }; 36 | } 37 | 38 | /** 39 | * Hides an open modal dialog 40 | * 41 | * @param name The name of the modal to hide 42 | * @returns {ICloseModalAction} 43 | */ 44 | export function hideModal(name: string): ICloseModalAction { 45 | return { 46 | type: ActionType.MODAL_CLOSE, 47 | payload: name 48 | }; 49 | } 50 | 51 | /** 52 | * Update settings of the given modal 53 | * 54 | * @param name The name of the modal to update 55 | * @param args 56 | * @since 0.14.8 57 | */ 58 | export function updateModal(name: string, args: ModalChangeArgs): IUpdateModalDimensionsAction { 59 | return { 60 | type: ActionType.MODAL_UPDATE, 61 | payload: { 62 | name, 63 | args: args 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/actions/taskpane.ts: -------------------------------------------------------------------------------- 1 | import { MgError } from "../api/error"; 2 | import { 3 | ReduxThunkedAction 4 | } from "../api/common"; 5 | import { ActionType } from '../constants/actions'; 6 | import { ITaskPaneBackAction, ITaskPaneForwardAction, ITaskPanePushUrlAction } from './defs'; 7 | 8 | /** 9 | * Go back to the initial task URL 10 | * 11 | * @returns {ReduxThunkedAction} 12 | */ 13 | export function goHome(): ReduxThunkedAction { 14 | return (dispatch, getState) => { 15 | const initUrl = getState().taskpane.initialUrl; 16 | if (initUrl != null) { 17 | dispatch({ 18 | type: ActionType.TASK_PANE_HOME 19 | }); 20 | } else { 21 | throw new MgError("Case not handled yet: Home action when no initial task URL set"); 22 | } 23 | }; 24 | } 25 | 26 | /** 27 | * Go back one entry in the task pane navigation history 28 | * 29 | * @returns 30 | */ 31 | export function goBack(): ITaskPaneBackAction { 32 | return { 33 | type: ActionType.TASK_PANE_BACK 34 | }; 35 | } 36 | 37 | /** 38 | * Go forward one entry in the task pane navigation history 39 | * 40 | * @returns 41 | */ 42 | export function goForward(): ITaskPaneForwardAction { 43 | return { 44 | type: ActionType.TASK_PANE_FORWARD 45 | }; 46 | } 47 | 48 | /** 49 | * Pushes the given URL to the task pane navigation history stack 50 | * 51 | * @param {string} url 52 | * @param {boolean} [silent] 53 | * @returns 54 | */ 55 | export function pushUrl(url: string, silent?: boolean): ITaskPanePushUrlAction { 56 | return { 57 | type: ActionType.TASK_PANE_PUSH_URL, 58 | payload: { 59 | url: url, 60 | silent: silent 61 | } 62 | }; 63 | } -------------------------------------------------------------------------------- /src/actions/template.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * template.ts 3 | * 4 | * Actions to support fusion templates 5 | */ 6 | import { ActionType } from '../constants/actions'; 7 | import { IElementState, ITemplateSetElementStateAction, ITemplateSetTaskPaneVisibilityAction, ITemplateSetSelectionPanelVisibility, ITemplateSetLegendVisibility, ITemplateSetCustomDataAction } from './defs'; 8 | 9 | export function setElementStates(states: IElementState): ITemplateSetElementStateAction { 10 | return { 11 | type: ActionType.FUSION_SET_ELEMENT_STATE, 12 | payload: states 13 | }; 14 | } 15 | 16 | export function setTaskPaneVisibility(visible: boolean): ITemplateSetTaskPaneVisibilityAction { 17 | return { 18 | type: ActionType.FUSION_SET_TASK_PANE_VISIBILITY, 19 | payload: visible 20 | }; 21 | } 22 | 23 | export function setSelectionPanelVisibility(visible: boolean): ITemplateSetSelectionPanelVisibility { 24 | return { 25 | type: ActionType.FUSION_SET_SELECTION_PANEL_VISIBILITY, 26 | payload: visible 27 | }; 28 | } 29 | 30 | /* 31 | export function setOverviewMapVisibility(visible: boolean): ReduxAction { 32 | return { 33 | type: ActionType.FUSION_SET_OVERVIEW_MAP_VISIBILITY, 34 | payload: visible 35 | }; 36 | } 37 | */ 38 | 39 | export function setLegendVisibility(visible: boolean): ITemplateSetLegendVisibility { 40 | return { 41 | type: ActionType.FUSION_SET_LEGEND_VISIBILITY, 42 | payload: visible 43 | }; 44 | } 45 | 46 | /** 47 | * 48 | * @param name 49 | * @param value 50 | * @returns 51 | * 52 | * @since 0.14.8 53 | */ 54 | export function setTemplateCustomData(name: string, value: any): ITemplateSetCustomDataAction { 55 | return { 56 | type: ActionType.FUSION_SET_TEMPLATE_CUSTOM_DATA, 57 | payload: { 58 | name, 59 | value 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/api/bootstrap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Sets up key dependencies needed by the viewer. This only needs to be called on the entry point of your custom viewer bundle 3 | */ 4 | export function bootstrap() { 5 | 6 | } -------------------------------------------------------------------------------- /src/api/builders/factory.ts: -------------------------------------------------------------------------------- 1 | import { RequestBuilder } from '../request-builder'; 2 | import { MgError } from '../error'; 3 | import { ClientKind } from '../common'; 4 | import { MapAgentRequestBuilder } from './mapagent'; 5 | 6 | /** 7 | * A factory method for creating request builders 8 | * 9 | * @since 0.13 10 | */ 11 | export type RequestBuilderFactory = (agentUri: string, locale?: string) => RequestBuilder; 12 | 13 | const _builders: { [kind: string]: RequestBuilderFactory } = {}; 14 | 15 | /** 16 | * Registers a factory for creating request builders for the given kind. This only needs to be called in the entry point of your custom viewer bundle. 17 | * 18 | * @param kind 19 | * @param factory 20 | * @since 0.13 21 | */ 22 | export function registerRequestBuilder(kind: ClientKind, factory: RequestBuilderFactory): void { 23 | _builders[kind] = factory; 24 | } 25 | 26 | /** 27 | * Creates the request builder for the given kind 28 | * 29 | * @param agentUri 30 | * @param kind 31 | * @since 0.13 32 | */ 33 | export function createRequestBuilder(agentUri: string, kind: ClientKind): RequestBuilder { 34 | if (_builders[kind]) { 35 | return _builders[kind](agentUri); 36 | } 37 | throw new MgError(`Unknown or unsupported client kind: ${kind}`); 38 | } -------------------------------------------------------------------------------- /src/api/contracts/common.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * common.ts 3 | * 4 | * Common contracts and type aliases 5 | */ 6 | 7 | import type { Bounds } from "../common"; 8 | 9 | /** 10 | * A MapGuide resource identifier 11 | */ 12 | export type ResourceIdentifier = string; 13 | 14 | /** 15 | * A version string 16 | */ 17 | export type Version = string; 18 | 19 | /** 20 | * A color string 21 | */ 22 | export type Color = string; 23 | 24 | /** 25 | * A string that defines a MIME type 26 | */ 27 | export type MimeType = string; 28 | 29 | /** 30 | * A string that defines a FDO filter 31 | */ 32 | export type FdoFilter = string; 33 | 34 | /** 35 | * A string that defines base64 content 36 | */ 37 | export type Base64Content = string; 38 | 39 | /** 40 | * Base interface for strongly-typed resources 41 | */ 42 | export interface ResourceBase { 43 | 44 | } 45 | 46 | /** 47 | * A site version response 48 | * 49 | * @since 0.14 50 | */ 51 | export interface SiteVersionResponse { 52 | Version: string; 53 | } 54 | 55 | /** 56 | * Defines a client-side selected vector feature 57 | * 58 | * @since 0.14 59 | */ 60 | export interface ClientSelectionFeature { 61 | /** 62 | * The bounds of this feature (for zooming into) 63 | */ 64 | bounds?: Bounds; 65 | /** 66 | * The attributes of this feature 67 | */ 68 | properties: any; 69 | } 70 | 71 | /** 72 | * Defines a layer of selected client-side vector features 73 | * 74 | * @since 0.14 75 | */ 76 | export interface ClientSelectionLayer { 77 | /** 78 | * The name of the layer 79 | */ 80 | name: string; 81 | /** 82 | * The selected features in this layer 83 | */ 84 | features: ClientSelectionFeature[]; 85 | } 86 | 87 | /** 88 | * A client-side vector feature selection set 89 | * 90 | * @since 0.14 91 | */ 92 | export interface ClientSelectionSet { 93 | /** 94 | * The layers in this selection set 95 | */ 96 | layers: ClientSelectionLayer[]; 97 | } -------------------------------------------------------------------------------- /src/api/contracts/map-definition.ts: -------------------------------------------------------------------------------- 1 | import { ResourceBase } from "./common"; 2 | 3 | export interface MapDefinitionLayerCommon { 4 | Name: string; 5 | Visible: boolean; 6 | ShowInLegend: boolean; 7 | ExpandInLegend: boolean; 8 | LegendLabel: string; 9 | Group: string; 10 | } 11 | 12 | export interface MapDefinitionLayerGroup extends MapDefinitionLayerCommon { 13 | 14 | } 15 | 16 | export interface MapDefinitionLayer extends MapDefinitionLayerCommon { 17 | ResourceId: string; 18 | Selectable: boolean; 19 | } 20 | 21 | export interface TileSetSource { 22 | ResourceId: string; 23 | } 24 | 25 | export interface MapDefinition extends ResourceBase { 26 | CoordinateSystem: string; 27 | Extents: { 28 | MinX: number; 29 | MinY: number; 30 | MaxX: number; 31 | MaxY: number; 32 | }, 33 | BackgroundColor: string; 34 | MapLayer: MapDefinitionLayer[]; 35 | MapLayerGroup: MapDefinitionLayerGroup[]; 36 | TileSetSource?: TileSetSource; 37 | } -------------------------------------------------------------------------------- /src/api/contracts/query.ts: -------------------------------------------------------------------------------- 1 | export interface FeatureSetClass { 2 | "@id": string; 3 | ID: string[]; 4 | } 5 | 6 | export interface FeatureSetLayer { 7 | "@id": string; 8 | /** 9 | * @since 0.14 Added null as possible type 10 | */ 11 | "@name": string | null; 12 | Class: FeatureSetClass; 13 | } 14 | export interface FeatureSet { 15 | Layer: FeatureSetLayer[]; 16 | } 17 | 18 | export interface SelectionImage { 19 | MimeType: string; 20 | Content: string; 21 | } 22 | 23 | export interface FeatureProperty { 24 | /** 25 | * The display name of the feature property. Use the layer metadata 26 | * to get the real property name 27 | * 28 | * @type {string} 29 | */ 30 | Name: string; 31 | /** 32 | * The value of the feature property 33 | * 34 | * @type {string | null} 35 | * 36 | * @since 0.14 added null as possible type 37 | */ 38 | Value: string | null 39 | } 40 | 41 | export interface SelectedFeature { 42 | SelectionKey?: string; 43 | Bounds?: string; 44 | Property: FeatureProperty[]; 45 | } 46 | 47 | export interface LayerPropertyMetadata { 48 | DisplayName: string; 49 | Name: string; 50 | Type: number; 51 | } 52 | 53 | export interface LayerMetadata { 54 | Property: LayerPropertyMetadata[]; 55 | } 56 | 57 | export interface SelectedLayer { 58 | "@id": string; 59 | "@name": string; 60 | Feature: SelectedFeature[]; 61 | LayerMetadata?: LayerMetadata | undefined; 62 | } 63 | 64 | export interface SelectedFeatureSet { 65 | SelectedLayer: SelectedLayer[]; 66 | } 67 | 68 | export interface QueryMapFeaturesResponse { 69 | FeatureSet?: FeatureSet | undefined; 70 | Hyperlink?: string | undefined; 71 | InlineSelectionImage?: SelectionImage | undefined; 72 | SelectedFeatures?: SelectedFeatureSet | undefined; 73 | Tooltip?: string | undefined; 74 | } -------------------------------------------------------------------------------- /src/api/contracts/tile-set-definition.ts: -------------------------------------------------------------------------------- 1 | import { ResourceBase } from "./common"; 2 | 3 | /** 4 | * @since 0.14 5 | */ 6 | export interface BaseMapLayer { 7 | Name: string; 8 | ResourceId: string; 9 | Selectable: boolean; 10 | ShowInLegend: boolean; 11 | LegendLabel: string; 12 | ExpandInLegend: boolean; 13 | } 14 | 15 | /** 16 | * @since 0.14 17 | */ 18 | export interface BaseMapLayerGroup { 19 | Name: string; 20 | Visible: boolean; 21 | ShowInLegend: boolean; 22 | ExpandInLegend: boolean; 23 | LegendLabel: string; 24 | BaseMapLayer: BaseMapLayer[]; 25 | } 26 | 27 | /** 28 | * @since 0.14 29 | */ 30 | export interface TileStoreParameters { 31 | TileProvider: string; 32 | Parameter: { Name: string, Value: string }[]; 33 | } 34 | 35 | /** 36 | * @since 0.14 37 | */ 38 | export interface TileSetDefinition extends ResourceBase { 39 | Extents: { 40 | MinX: number; 41 | MinY: number; 42 | MaxX: number; 43 | MaxY: number; 44 | } 45 | TileStoreParameters: TileStoreParameters; 46 | BaseMapLayerGroup: BaseMapLayerGroup[]; 47 | } -------------------------------------------------------------------------------- /src/api/error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The base of any viewer-related error 3 | * 4 | * @class MgError 5 | * @extends {Error} 6 | */ 7 | export class MgError extends Error { 8 | constructor(public message: string) { 9 | super(message); 10 | this.name = "MgError"; 11 | this.message = message; 12 | this.stack = (new Error()).stack; 13 | } 14 | } 15 | 16 | /** 17 | * Indicates if this error is a session expired error 18 | * 19 | * @param {MgError} err 20 | * @returns {boolean} 21 | */ 22 | export function isSessionExpiredError(err: MgError): boolean { 23 | return err.message.indexOf("MgSessionExpiredException") >= 0; 24 | } -------------------------------------------------------------------------------- /src/api/layer-manager/driver-registry.ts: -------------------------------------------------------------------------------- 1 | import { IFormatDriver } from "./format-driver"; 2 | 3 | const _drivers: IFormatDriver[] = []; 4 | 5 | export function getFormatDrivers() { return _drivers; } 6 | 7 | export function addFormatDriver(driver: IFormatDriver) { 8 | _drivers.push(driver); 9 | } -------------------------------------------------------------------------------- /src/api/layer-set-contracts.ts: -------------------------------------------------------------------------------- 1 | import { RefreshMode, IExternalBaseLayer, LayerTransparencySet, Bounds, Size, GenericEvent, Dictionary } from './common'; 2 | import View from 'ol/View'; 3 | import Source from 'ol/source/Source'; 4 | import LayerBase from "ol/layer/Base"; 5 | import { LoadFunction as TileLoadFunction } from 'ol/Tile'; 6 | import { LoadFunction as ImageLoadFunction } from 'ol/Image'; 7 | import type { ProjectionLike } from 'ol/proj'; 8 | 9 | export interface ILayerSetOL { 10 | view: View; 11 | extent: Bounds; 12 | dpi: number; 13 | projection: ProjectionLike; 14 | scaleToResolution(scale: number): number; 15 | resolutionToScale(resolution: number): number; 16 | refreshMap(mode: RefreshMode): void; 17 | getMetersPerUnit(): number; 18 | getLayers(): LayerBase[]; 19 | getSourcesForProgressTracking(): Source[]; 20 | updateExternalBaseLayers(externalBaseLayers: IExternalBaseLayer[]): void; 21 | updateTransparency(trans: LayerTransparencySet): void; 22 | // ====== This is MapGuide-specific ======== // 23 | /** 24 | * 25 | * @param mapExtent 26 | * @param size @deprecated This parameter is no longer used and will be removed in a later release 27 | * @param uri 28 | * 29 | * @since 0.15 Deprecated size parameter 30 | */ 31 | showActiveSelectedFeature(mapExtent: Bounds, size: Size, uri: string): void; 32 | update(showGroups: string[] | undefined, showLayers: string[] | undefined, hideGroups: string[] | undefined, hideLayers: string[] | undefined): void; 33 | updateSelectionColor(color: string): void; 34 | } 35 | 36 | export interface IImageLayerEvents { 37 | addImageLoading(): void; 38 | addImageLoaded(): void; 39 | onImageError(e: GenericEvent): void; 40 | getLocale(): string | undefined; 41 | getBaseTileLoaders(): Dictionary; 42 | getTileLoaders(): Dictionary; 43 | getImageLoaders(): Dictionary; 44 | } 45 | -------------------------------------------------------------------------------- /src/api/lazy.ts: -------------------------------------------------------------------------------- 1 | export class Lazy { 2 | private _value: T | undefined; 3 | constructor(private value: () => T) { } 4 | public getValue() { 5 | if (!this._value) { 6 | this._value = this.value(); 7 | } 8 | return this._value; 9 | } 10 | } 11 | 12 | export class AsyncLazy { 13 | private _value: Promise | undefined; 14 | constructor(private value: () => Promise) { } 15 | public getValueAsync(): Promise { 16 | if (!this._value) { 17 | this._value = this.value(); 18 | } 19 | return this._value; 20 | } 21 | } -------------------------------------------------------------------------------- /src/api/mapguide-components.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { LegendContainer } from "../containers/legend"; 3 | import { SelectedFeatureCountContainer } from "../containers/selected-feature-count"; 4 | import { SelectionPanelContainer } from "../containers/selection-panel"; 5 | import { PoweredByMapGuide } from "../components/pbmg"; 6 | import { SessionExpired } from "../components/session-expired"; 7 | import { ViewerOptions } from "../containers/viewer-options"; 8 | import { QuickPlotContainer } from "../containers/quick-plot"; 9 | import { registerComponentFactory, DefaultComponentNames } from "../api/registry/component"; 10 | 11 | export function registerMapGuideComponents() { 12 | registerComponentFactory(DefaultComponentNames.Legend, (props) => ); 13 | registerComponentFactory(DefaultComponentNames.SelectionPanel, (props) => ); 14 | registerComponentFactory(DefaultComponentNames.SelectedFeatureCount, (props) => ); 15 | registerComponentFactory(DefaultComponentNames.PoweredByMapGuide, (props) => ); 16 | registerComponentFactory(DefaultComponentNames.SessionExpired, (props) => ); 17 | registerComponentFactory(DefaultComponentNames.ViewerOptions, (props) => ); 18 | registerComponentFactory(DefaultComponentNames.QuickPlot, (props) => ); 19 | } -------------------------------------------------------------------------------- /src/api/ol-mapguide-source-factory.ts: -------------------------------------------------------------------------------- 1 | import { IMapGuideImageSource } from "./common"; 2 | //import { MapGuideImageSource } from "./ol-mapguide-source"; 3 | import olMapGuideSource from "ol/source/ImageMapGuide"; 4 | import { OLMapGuideImageSourceOptions } from './ol-types'; 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export function isMapGuideImageSource(arg: any): arg is IMapGuideImageSource { 10 | return typeof(arg.updateParams) != 'undefined'; 11 | } 12 | 13 | /** 14 | * @hidden 15 | */ 16 | export function createMapGuideSource(options: OLMapGuideImageSourceOptions): olMapGuideSource { 17 | //const ctor: any = MapGuideImageSource; 18 | //const source = new ctor(options); 19 | const source = new olMapGuideSource(options); 20 | return source; 21 | } -------------------------------------------------------------------------------- /src/api/ol-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * ol-types.ts 3 | * 4 | * This module is a type "band-aid" module to reduce friction if/when we move 5 | * from @types/ol to typings officially bundled with a future release of OpenLayers 6 | * (whenever that time comes) 7 | */ 8 | 9 | import type { Options as MGImageSourceOptions } from "ol/source/ImageMapGuide"; 10 | import type Feature from "ol/Feature"; 11 | import type Geometry from "ol/geom/Geometry"; 12 | import type VectorLayer from "ol/layer/Vector"; 13 | import type VectorSource from "ol/source/Vector"; 14 | import type ImageSource from "ol/source/Image"; 15 | import type ImageLayer from "ol/layer/Image"; 16 | import type TileLayer from "ol/layer/Tile"; 17 | import type TileSource from "ol/source/Tile"; 18 | import type { Options as VectorLayerOptions } from "ol/layer/BaseVector"; 19 | import Source from "ol/source/Source"; 20 | import Layer from "ol/layer/Layer"; 21 | 22 | export type OLLayer = Layer; 23 | export type OLFeature = Feature; 24 | export { Type as OLGeometryType } from "ol/geom/Geometry"; 25 | export type OLMapGuideImageSourceOptions = MGImageSourceOptions; 26 | export type OLVectorSource = VectorSource; 27 | export type OLVectorLayer = VectorLayer; 28 | export type OLImageLayer = ImageLayer; 29 | export type OLTileLayer = TileLayer; 30 | export type OLVectorLayerOptions = VectorLayerOptions; -------------------------------------------------------------------------------- /src/api/registry/layout.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A react layout template component factory function signature 3 | */ 4 | export type LayoutFactory = (() => JSX.Element); 5 | 6 | /** 7 | * Capabilities of a viewer layout template 8 | * 9 | * @since 0.14 10 | */ 11 | export type LayoutCapabilities = { 12 | /** 13 | * Indicates if this viewer layout template has a task pane 14 | */ 15 | readonly hasTaskPane: boolean; 16 | }; 17 | 18 | const layouts: { [key: string]: LayoutFactory } = {}; 19 | const layoutCaps: { [key: string]: LayoutCapabilities } = {}; 20 | 21 | /** 22 | * Gets the capabilities of the given layout template 23 | * 24 | * @param name 25 | * @returns 26 | * @since 0.14 27 | */ 28 | export function getLayoutCapabilities(name: string): LayoutCapabilities | undefined { 29 | return layoutCaps[name]; 30 | } 31 | 32 | /** 33 | * Register the given react layout template component factory function for the given 34 | * template name 35 | * 36 | * @param {string} name 37 | * @param {LayoutFactory} factory 38 | * @param caps The capabilities of this template 39 | * @since 0.14 40 | */ 41 | export function registerLayout(name: string, factory: LayoutFactory, caps: LayoutCapabilities) { 42 | layouts[name] = factory; 43 | layoutCaps[name] = caps; 44 | } 45 | 46 | /** 47 | * Gets the registerd react layout template component factory function for the given 48 | * template name 49 | * 50 | * @param {string} name 51 | * @returns {LayoutFactory} 52 | */ 53 | export function getLayout(name: string): LayoutFactory { 54 | return layouts[name]; 55 | } -------------------------------------------------------------------------------- /src/api/runtime.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * runtime.ts 3 | * 4 | * This represents a runtime environment where components can interact with the map viewer 5 | * in the traditional way. This module is mainly for servicing the AJAX viewer API emulation. 6 | * 7 | * Where possible, use actions instead of this module 8 | */ 9 | import { debug } from '../utils/logger'; 10 | 11 | let _fusionRoot: string | undefined; 12 | 13 | /** 14 | * Sets the Fusion base URL 15 | * 16 | * 17 | * @param {string} root 18 | */ 19 | export function setFusionRoot(root: string): void { 20 | _fusionRoot = root; 21 | debug(`Fusion root set to: ${root}. Access to Fusion backend services and widget content will be relative to this value`); 22 | } 23 | 24 | /** 25 | * Gets the Fusion base URL 26 | * 27 | * 28 | * @returns {string} 29 | */ 30 | export function getFusionRoot(): string { 31 | return _fusionRoot || "../fusion"; 32 | } -------------------------------------------------------------------------------- /src/api/selection-count.ts: -------------------------------------------------------------------------------- 1 | import { ClientSelectionSet } from "./contracts/common"; 2 | import { SelectedFeatureSet, FeatureSet } from "./contracts/query"; 3 | 4 | function isSelectedFeatureSet(ss: SelectedFeatureSet | FeatureSet): ss is SelectedFeatureSet { 5 | return (ss as any).SelectedLayer; 6 | } 7 | 8 | export function countSelection(mgSelection?: SelectedFeatureSet | FeatureSet, clientSelection?: ClientSelectionSet) { 9 | const summary = { total: 0, layerCount: 0 }; 10 | if (mgSelection) { 11 | if (isSelectedFeatureSet(mgSelection)) { 12 | summary.layerCount = mgSelection.SelectedLayer.length; 13 | for (const lyr of mgSelection.SelectedLayer) { 14 | summary.total += lyr.Feature?.length ?? 0; 15 | } 16 | } else { 17 | summary.layerCount = mgSelection.Layer.length; 18 | for (const lyr of mgSelection.Layer) { 19 | summary.total += lyr.Class?.ID?.length ?? 0; 20 | } 21 | } 22 | } 23 | if (clientSelection) { 24 | summary.layerCount = clientSelection.layers.length; 25 | for (const lyr of clientSelection.layers) { 26 | summary.total += lyr.features?.length ?? 0; 27 | } 28 | } 29 | if (summary.total == 0 && summary.layerCount == 0) { 30 | return undefined; 31 | } 32 | return summary; 33 | } -------------------------------------------------------------------------------- /src/api/session-store.ts: -------------------------------------------------------------------------------- 1 | import { strStartsWith } from '../utils/string'; 2 | import { QueryMapFeaturesResponse } from './contracts/query'; 3 | import { AsyncLazy } from './lazy'; 4 | 5 | /** 6 | * session-store.ts 7 | * 8 | * A thin-wrapper layer over local storage for storing any data related to viewer sessions 9 | */ 10 | 11 | /** 12 | * @since 0.12 13 | */ 14 | export async function clearSessionStore(): Promise { 15 | try { 16 | for (const key in window.localStorage) { 17 | if (strStartsWith(key, "selection_")) { 18 | window.localStorage.removeItem(key); 19 | } 20 | } 21 | } catch (e) { 22 | 23 | } 24 | } 25 | 26 | function encodeKey(sessionId: string, mapName: string) { 27 | return `selection_${sessionId}_${mapName}`; 28 | } 29 | 30 | export async function persistSelectionSetToLocalStorage(sessionId: string, mapName: string, resp: QueryMapFeaturesResponse): Promise { 31 | const key = encodeKey(sessionId, mapName); 32 | const value = JSON.stringify(resp); 33 | try { 34 | window.localStorage.setItem(key, value); 35 | } catch (e) { 36 | 37 | } 38 | } 39 | 40 | export async function retrieveSelectionSetFromLocalStorage(sessionId: AsyncLazy, mapName: string): Promise { 41 | const key = encodeKey(await sessionId.getValueAsync(), mapName); 42 | const content = window.localStorage.getItem(key); 43 | if (content) { 44 | return JSON.parse(content); 45 | } 46 | } -------------------------------------------------------------------------------- /src/components/about.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | /** 4 | * The About component displays information about this viewer 5 | * @param props 6 | */ 7 | export const About: React.StatelessComponent = (props) => { 8 | return
9 |

mapguide-react-layout

10 |
11 |

Hash: {__COMMITHASH__}

12 |
13 |

GitHub

14 |

Issues

15 |

Uses icons from the Fugue icon set by Yusuke Kamiyamane

16 |
; 17 | }; -------------------------------------------------------------------------------- /src/components/color-picker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { tr } from "../api/i18n"; 3 | import { NBSP } from '../constants'; 4 | import { DEFAULT_COLOR } from '../api/ol-style-contracts'; 5 | import { HexColorPicker } from "react-colorful"; 6 | import { useElementContext } from "./elements/element-context"; 7 | 8 | /** 9 | * Color picker props 10 | * 11 | * @since 0.13 12 | */ 13 | export interface IColorPickerProps { 14 | value?: string; 15 | locale: string; 16 | onChange: (value: string) => void; 17 | disabled?: boolean; 18 | } 19 | 20 | /** 21 | * A basic color picker component 22 | * 23 | * @since 0.13 24 | */ 25 | export const ColorPicker = (props: IColorPickerProps) => { 26 | const { Collapsible, Button, Card } = useElementContext(); 27 | const [isPickerOpen, setIsPickerOpen] = React.useState(false); 28 | const onPickerToggle = () => { 29 | setIsPickerOpen(!isPickerOpen); 30 | }; 31 | return
32 | 33 | 34 | 35 | props.onChange(c)} /> 36 | 37 | 38 | 39 |
40 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/button.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@blueprintjs/core"; 2 | import { ButtonProps } from "../../element-context"; 3 | import { iconName, variantToIntent } from "./utils"; 4 | import React from "react"; 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export const BpButton: React.FC = (props) => { 10 | return 13 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/callout.tsx: -------------------------------------------------------------------------------- 1 | import { Callout } from '@blueprintjs/core'; 2 | import { CalloutProps } from '../../element-context'; 3 | import React from 'react'; 4 | import { iconName, variantToIntent } from './utils'; 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export const BpCallout: React.FC = (props) => { 10 | return 14 | {props.children} 15 | ; 16 | }; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/card.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from "@blueprintjs/core"; 2 | import { CardProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpCard: React.FC = (props) => { 9 | return 10 | {props.children} 11 | ; 12 | }; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox } from "@blueprintjs/core"; 2 | import { CheckboxProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpCheckbox: React.FC = (props) => { 9 | return ; 10 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import { Collapse } from "@blueprintjs/core"; 2 | import React from "react"; 3 | import { CollapsibleProps } from "../../element-context"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpCollapsible: React.FC = (props) => { 9 | return {props.children} 10 | }; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/drawer.tsx: -------------------------------------------------------------------------------- 1 | import { Drawer, DrawerSize } from "@blueprintjs/core" 2 | import { DrawerProps } from "../../element-context" 3 | import React from "react" 4 | import { iconName } from "./utils" 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export const BpDrawer: React.FC = ({ icon, onClose, title, position, isOpen, children }) => { 10 | return 18 | {children} 19 | 20 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/editable-text.tsx: -------------------------------------------------------------------------------- 1 | import { EditableText } from "@blueprintjs/core"; 2 | import { EditableTextProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | export const BpEditableText: React.FC = ({ value, onChange }) => { 6 | return ; 7 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/file-input.tsx: -------------------------------------------------------------------------------- 1 | import { FileInput } from "@blueprintjs/core"; 2 | import { FileInputProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpFileInput: React.FC = ({ fill, text, buttonText, onInputChange }) => { 9 | return ; 14 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/form-group.tsx: -------------------------------------------------------------------------------- 1 | import { FormGroup } from "@blueprintjs/core"; 2 | import { FormGroupProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpFormGroup: React.FC = ({ label, inline, children }) => { 9 | return 10 | {children} 11 | 12 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/icon.tsx: -------------------------------------------------------------------------------- 1 | import { Icon } from '@blueprintjs/core'; 2 | import { IconProps } from '../../element-context'; 3 | import React from 'react'; 4 | import { iconName } from './utils'; 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export const BpIcon: React.FC = (props) => { 10 | return ; 11 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/input-group.tsx: -------------------------------------------------------------------------------- 1 | import { InputGroup } from "@blueprintjs/core"; 2 | import { InputGroupProps } from "../../element-context"; 3 | import { iconName } from "./utils"; 4 | import React from "react"; 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export const BpInputGroup: React.FC = (props) => { 10 | return ; 20 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/non-ideal-state.tsx: -------------------------------------------------------------------------------- 1 | import { NonIdealState } from "@blueprintjs/core"; 2 | import { NonIdealStateProps } from "../../element-context"; 3 | import React from "react"; 4 | import { iconName } from "./utils"; 5 | 6 | /** 7 | * @hidden 8 | */ 9 | export const BpNonIdealState: React.FC = ({ icon, title, description, action }) => { 10 | return 11 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/numeric-input.tsx: -------------------------------------------------------------------------------- 1 | import { NumericInput } from '@blueprintjs/core'; 2 | import { NumericInputProps } from '../../element-context'; 3 | import React from 'react'; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpNumericInput: React.FC = (props) => { 9 | return props.onChange?.(e)}/>; 10 | }; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/popover.tsx: -------------------------------------------------------------------------------- 1 | import { Popover } from "@blueprintjs/core"; 2 | import { PopoverProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpPopover: React.FC = ({ usePortal, position, minimal, children }) => { 9 | return 10 | {children} 11 | 12 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/provider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { IElementContext } from "../../element-context"; 3 | import { BpButton } from "./button"; 4 | import { BpCheckbox } from "./checkbox"; 5 | import { BpCard } from "./card"; 6 | import { BpIcon } from "./icon"; 7 | import { BpSlider } from "./slider"; 8 | import { BpCallout } from "./callout"; 9 | import { BpNumericInput } from "./numeric-input"; 10 | import { BpCollapsible } from "./collapsible"; 11 | import { BpRadio } from "./radio"; 12 | import { BpInputGroup } from "./input-group"; 13 | import { BpNonIdealState } from "./non-ideal-state"; 14 | import { BpSpinner } from "./spinner"; 15 | import { BpSwitch } from "./switch"; 16 | import { BpSelect } from "./select"; 17 | import { BpFileInput } from "./file-input"; 18 | import { BpFormGroup } from "./form-group"; 19 | import { BpEditableText } from "./editable-text"; 20 | import { BpMenuComponent } from "./menu"; 21 | 22 | import "@blueprintjs/core/lib/css/blueprint.css"; 23 | import { BpTabSet } from "./tab-set"; 24 | import { BpPopover } from "./popover"; 25 | import { BpDrawer } from "./drawer"; 26 | 27 | const provider: IElementContext = { 28 | Button: BpButton, 29 | Radio: BpRadio, 30 | Slider: BpSlider, 31 | Collapsible: BpCollapsible, 32 | Callout: BpCallout, 33 | Checkbox: BpCheckbox, 34 | Icon: BpIcon, 35 | Card: BpCard, 36 | NumericInput: BpNumericInput, 37 | InputGroup: BpInputGroup, 38 | NonIdealState: BpNonIdealState, 39 | Spinner: BpSpinner, 40 | Switch: BpSwitch, 41 | Select: BpSelect, 42 | FileInput: BpFileInput, 43 | FormGroup: BpFormGroup, 44 | EditableText: BpEditableText, 45 | MenuComponent: BpMenuComponent, 46 | TabSet: BpTabSet, 47 | Drawer: BpDrawer, 48 | Popover: BpPopover 49 | }; 50 | 51 | export default provider; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/radio.tsx: -------------------------------------------------------------------------------- 1 | import { Radio } from '@blueprintjs/core'; 2 | import { RadioProps } from '../../element-context'; 3 | import React from 'react'; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpRadio: React.FC = (props) => { 9 | return ; 16 | }; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/select.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SelectProps } from "../../element-context"; 3 | import { strIsNullOrEmpty } from "../../../../utils/string"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpSelect: React.FC = ({ id, name, value, onChange, items, fill, placeholder, keyFunc, extraClassNames, style }) => { 9 | const classes = `bp3-select ${!!fill ? 'bp3-fill' : ''} ${extraClassNames}`.trim(); 10 | return
11 | 22 |
; 23 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/slider.tsx: -------------------------------------------------------------------------------- 1 | import { Slider } from '@blueprintjs/core'; 2 | import { SliderProps } from '../../element-context'; 3 | import React from 'react'; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpSlider: React.FC = (props) => { 9 | return ; 19 | }; -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/spinner.tsx: -------------------------------------------------------------------------------- 1 | import { Spinner, SpinnerSize as BpSpinSize } from "@blueprintjs/core"; 2 | import { SpinnerProps, SpinnerSize } from "../../element-context"; 3 | import React from "react"; 4 | import { variantToIntent } from "./utils"; 5 | 6 | function presetToSize(preset: SpinnerSize | undefined) { 7 | switch (preset) { 8 | case "small": 9 | return BpSpinSize.SMALL; 10 | case "standard": 11 | return BpSpinSize.STANDARD; 12 | case "large": 13 | return BpSpinSize.LARGE; 14 | } 15 | return undefined; 16 | } 17 | 18 | /** 19 | * @hidden 20 | */ 21 | export const BpSpinner: React.FC = (props) => { 22 | return ; 23 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/switch.tsx: -------------------------------------------------------------------------------- 1 | import { Switch } from "@blueprintjs/core"; 2 | import { SwitchProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | /** 6 | * @hidden 7 | */ 8 | export const BpSwitch: React.FC = (props) => { 9 | return 15 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/tab-set.tsx: -------------------------------------------------------------------------------- 1 | import { Tab, Tabs } from "@blueprintjs/core"; 2 | import { TabSetProps } from "../../element-context"; 3 | import React from "react"; 4 | 5 | export const BpTabSet: React.FC = (props) => { 6 | const { id, className, onTabChanged, tabs, activeTabId } = props; 7 | return onTabChanged?.(tabId)} selectedTabId={activeTabId}> 8 | {tabs.map(tab => )} 9 | 10 | } -------------------------------------------------------------------------------- /src/components/elements/providers/blueprint/utils.ts: -------------------------------------------------------------------------------- 1 | import { IconName, Intent } from "@blueprintjs/core"; 2 | import { ElementVariant } from "../../element-context"; 3 | 4 | /** 5 | * @hidden 6 | */ 7 | export function iconName(name: string | undefined): IconName | undefined { 8 | return name as IconName; 9 | } 10 | 11 | /** 12 | * @hidden 13 | */ 14 | export function variantToIntent(variant: ElementVariant | undefined): Intent | undefined { 15 | switch (variant) { 16 | case "primary": 17 | return Intent.PRIMARY; 18 | case "warning": 19 | return Intent.WARNING; 20 | case "danger": 21 | return Intent.DANGER; 22 | case "success": 23 | return Intent.SUCCESS; 24 | default: 25 | return undefined; 26 | } 27 | } -------------------------------------------------------------------------------- /src/components/form-frame-shim.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type FormFrameShimProps = any; 4 | 5 | /** 6 | * The FormFrameShim component provides a compatibility shim for the AJAX viewer form frame 7 | * 8 | * @class FormFrameShim 9 | * @extends {React.Component} 10 | */ 11 | export class FormFrameShim extends React.Component { 12 | private _form: HTMLFormElement; 13 | constructor(props: FormFrameShimProps) { 14 | super(props); 15 | this.state = { 16 | target: "", 17 | action: "", 18 | params: [] 19 | }; 20 | } 21 | private onFormMounted = (form: HTMLFormElement) => { 22 | this._form = form; 23 | } 24 | submit(url: string, params: string[], target: string): void { 25 | //TODO: Can't convert this to functional component with hooks, until this type 26 | //of pattern is possible 27 | this.setState({ 28 | action: url, 29 | params: params, 30 | target: target 31 | }, () => { 32 | //The form will have the updated content at this point 33 | this._form.submit(); 34 | }) 35 | } 36 | render(): JSX.Element { 37 | const { target, action, params } = this.state; 38 | return
39 | {(() => { 40 | const fields = [] as JSX.Element[]; 41 | for (let i = 0; i < params.length; i+=2) { 42 | fields.push(); 43 | } 44 | return fields; 45 | })()} 46 |
; 47 | } 48 | } -------------------------------------------------------------------------------- /src/components/icon-names.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets the list of supported icon names 3 | * 4 | * @returns The list of supported icon names 5 | * @since 0.15 6 | */ 7 | export function getIconNames() { 8 | return [ 9 | "chevron-up", 10 | "error", 11 | "menu-open", 12 | "small-cross", 13 | "arrows-horizontal", 14 | "info-sign", 15 | "warning-sign", 16 | "upload", 17 | "geosearch", 18 | "arrow-right", 19 | "issue", 20 | "edit", 21 | "cog", 22 | "caret-up", 23 | "caret-down", 24 | "zoom-to-fit", 25 | "trash", 26 | "layers", 27 | "layer", 28 | "new-layer", 29 | "play", 30 | "stop", 31 | "cross", 32 | "print", 33 | "th", 34 | "application", 35 | "plus", 36 | "minus", 37 | "hand", 38 | "select", 39 | "comment", 40 | "map", 41 | "properties", 42 | "menu-open", 43 | "menu-closed" 44 | ] 45 | } -------------------------------------------------------------------------------- /src/components/layer-manager/color-brewer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import colorbrewer from "colorbrewer"; 3 | 4 | export interface IColorBrewerRamp { 5 | displayName: string; 6 | category: string; 7 | scheme: string; 8 | ramp: string[] | undefined; 9 | } 10 | 11 | export function getColorBrewerRamps(): IColorBrewerRamp[] { 12 | const ramps = [] as IColorBrewerRamp[]; 13 | for (const cat in colorbrewer.schemeGroups) { 14 | for (const scheme of colorbrewer.schemeGroups[cat]) { 15 | const ramp = getMaxRamp(scheme); 16 | ramps.push({ displayName: `${cat} - ${scheme}`, category: cat, scheme: scheme, ramp: ramp }); 17 | } 18 | } 19 | return ramps; 20 | } 21 | 22 | export function getMaxRamp(scheme: any) { 23 | let theScheme; 24 | let len = 0; 25 | for (const s in scheme) { 26 | const arr = scheme[s]; 27 | if (Array.isArray(arr)) { 28 | if (arr.length > len) { 29 | theScheme = arr; 30 | len = arr.length; 31 | } 32 | } 33 | } 34 | return theScheme; 35 | } 36 | 37 | export const ColorBrewerSwatch: React.FC<{ theme: string }> = props => { 38 | const ramp = getMaxRamp(colorbrewer[props.theme]); 39 | if (ramp) { 40 | return 41 | 42 | {ramp.map((r, i) => )} 43 | 44 | 45 | 46 | {ramp.map((r, i) => )} 47 | 48 | 49 |
 
50 | }; 51 | return <>; 52 | }; -------------------------------------------------------------------------------- /src/components/map-load-indicator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { MapLoadIndicatorPositioning } from '../api/common'; 3 | 4 | export interface IMapLoadIndicatorProps { 5 | loading: number; 6 | loaded: number; 7 | color: string; 8 | position: MapLoadIndicatorPositioning; 9 | } 10 | 11 | export const MapLoadIndicator = (props: IMapLoadIndicatorProps) => { 12 | const { loaded, loading, color, position } = props; 13 | let visibility: "visible" | "hidden" = "visible"; 14 | const pc = Math.min(100, (loaded / loading * 100)); 15 | let width = pc.toFixed(1) + "%"; 16 | if (loaded === loading || pc >= 100) { 17 | visibility = "hidden"; 18 | width = "0"; 19 | } 20 | const style: React.CSSProperties = { 21 | position: "absolute", 22 | zIndex: 10, 23 | visibility: visibility, 24 | left: 0, 25 | height: 5, 26 | width: width, 27 | background: color, 28 | transition: "width 250ms" 29 | }; 30 | if (position == "top") { 31 | style.top = 0; 32 | } else { 33 | style.bottom = 0; 34 | } 35 | return
; 36 | } -------------------------------------------------------------------------------- /src/components/map-menu.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { IMapMenuEntry } from "../api/common"; 3 | 4 | /** 5 | * MapMenu component props 6 | * 7 | * @interface IMapMenuProps 8 | */ 9 | export interface IMapMenuProps { 10 | locale: string | undefined; 11 | selectedMap: string; 12 | maps: IMapMenuEntry[]; 13 | onActiveMapChanged?: (name: string) => void; 14 | } 15 | 16 | /** 17 | * The MapMenu component provides the ability to switch between active maps 18 | * @param props 19 | */ 20 | export const MapMenu = (props: IMapMenuProps) => { 21 | const [selected, setSelected] = React.useState(undefined); 22 | const onActiveMapChanged = (e: any) => { 23 | const value = e.currentTarget.value; 24 | setSelected(value); 25 | props.onActiveMapChanged?.(value); 26 | }; 27 | return
28 | {props.maps.map(layer => { 29 | return
30 | 35 |
; 36 | })} 37 |
; 38 | } -------------------------------------------------------------------------------- /src/components/mapguide-debug-context.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | /** 4 | * @since 0.13 5 | */ 6 | export enum MapGuideMockMode { 7 | /** 8 | * Render a placeholder image showing the key mapagent parameters 9 | */ 10 | RenderPlaceholder, 11 | /** 12 | * Do not render anything 13 | */ 14 | DoNotRender 15 | } 16 | 17 | /** 18 | * The map debug context, used to check if request mocking should be enabled or not 19 | * @since 0.13 20 | */ 21 | export interface IMapDebugContext { 22 | mock?: MapGuideMockMode; 23 | } 24 | 25 | export const MapDebugContext = React.createContext({}); -------------------------------------------------------------------------------- /src/components/mouse-coordinates.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { fmt } from "../api/i18n"; 3 | import { strTrim } from '../utils/string'; 4 | import DOMPurify from "dompurify"; 5 | 6 | /** 7 | * MouseCoordinates component props 8 | * 9 | * @interface IMouseCoordinatesProps 10 | * @extends {React.Props} 11 | */ 12 | export interface IMouseCoordinatesProps extends React.Props { 13 | format?: string; 14 | coords?: [number, number]; 15 | style?: React.CSSProperties; 16 | decimals?: number; 17 | units?: string; 18 | } 19 | 20 | function formatCoordinates(props: IMouseCoordinatesProps) { 21 | const { coords, decimals, format, units } = props; 22 | if (coords == null) { 23 | return null; //TODO: Use value indicated by EmptyText extension property 24 | } 25 | const [x, y] = coords; 26 | const sfmt = format || "X: {x}, Y: {y} {units}"; 27 | const str = fmt(sfmt, { 28 | x: `${decimals != null ? x.toFixed(decimals) : x}`, 29 | y: `${decimals != null ? y.toFixed(decimals) : y}`, 30 | units: units || "" 31 | }); 32 | return ; 33 | } 34 | 35 | /** 36 | * Displays tracked mouse coordinates 37 | * @param props 38 | */ 39 | export const MouseCoordinates = (props: IMouseCoordinatesProps) => { 40 | return
{formatCoordinates(props)}
; 41 | }; -------------------------------------------------------------------------------- /src/components/pbmg.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ImageIcon } from "./icon"; 3 | 4 | /** 5 | * "Powered by MapGuide" logo 6 | * @param props 7 | */ 8 | export const PoweredByMapGuide = (props: any) => { 9 | return
10 | 11 |
; 12 | }; -------------------------------------------------------------------------------- /src/components/selected-feature-count.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { tr, fmt } from "../api/i18n"; 3 | 4 | /** 5 | * SelectedFeatureCount component props 6 | * 7 | * @interface ISelectedFeatureCountProps 8 | */ 9 | export interface ISelectedFeatureCountProps { 10 | summary?: { 11 | total: number; 12 | layerCount: number; 13 | }, 14 | locale?: string; 15 | format?: string; 16 | style?: React.CSSProperties; 17 | } 18 | 19 | /** 20 | * Displays the number of selected features on the map 21 | */ 22 | export const SelectedFeatureCount = (props: ISelectedFeatureCountProps) => { 23 | const format = props.format || tr("FMT_SELECTION_COUNT", props.locale); 24 | let label: string | undefined; 25 | if (props.summary) { 26 | label = fmt(format, props.summary); 27 | } 28 | return
{label}
; 29 | }; -------------------------------------------------------------------------------- /src/components/session-expired.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { tr } from "../api/i18n"; 3 | import { GenericEvent } from "../api/common"; 4 | 5 | function reload(e: GenericEvent) { 6 | e.preventDefault(); 7 | //TODO: This is obviously the nuclear solution. 8 | // 9 | //The more graceful solution is to re-create the runtime map and interleave the response 10 | //into the current redux store and preserve the existing view and layer/group state and 11 | //we should be able to continue where we left off 12 | window.location.reload(); 13 | return false; 14 | } 15 | 16 | export interface ISessionExpiredProps { 17 | locale: string; 18 | } 19 | 20 | /** 21 | * Displays the "session expired" error message with possible recovery actions 22 | * @param props 23 | */ 24 | export const SessionExpired = (props: ISessionExpiredProps) => { 25 | return
26 |

{tr("SESSION_EXPIRED_DETAILED", props.locale)}

27 |

{tr("SESSION_EXPIRED_AVAILABLE_ACTIONS", props.locale)}

28 | 31 |
; 32 | }; -------------------------------------------------------------------------------- /src/components/session-keep-alive.tsx: -------------------------------------------------------------------------------- 1 | import { Client } from '../api/client'; 2 | 3 | /** 4 | * @hidden 5 | */ 6 | export class SessionKeepAlive { 7 | private getSession: () => string; 8 | private client: Client; 9 | private interval: number; 10 | private timeoutID: any; 11 | constructor(getSession: () => string, client: Client, onSessionExpired: () => void, private check: boolean) { 12 | this.getSession = getSession; 13 | this.client = client; 14 | if (this.check) { 15 | this.client.getServerSessionTimeout(this.getSession()).then(tm => { 16 | this.interval = tm / 5 * 1000; //Ping server 5 times each period. Timeout is returned in seconds. 17 | this.timeoutID = setTimeout(this.tick.bind(this), this.interval); 18 | }); 19 | } 20 | } 21 | public dispose() { 22 | clearTimeout(this.timeoutID); 23 | } 24 | private tick(): void { 25 | this.client.getServerSessionTimeout(this.getSession()).then(tm => { 26 | this.timeoutID = setTimeout(this.tick.bind(this), this.interval); 27 | }); 28 | } 29 | public lastTry(): Promise { 30 | if (this.check) { 31 | return this.client.getServerSessionTimeout(this.getSession()); 32 | } else { 33 | return Promise.resolve(-1); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Non-breaking space 3 | */ 4 | export const NBSP = String.fromCharCode(160); 5 | 6 | export const DEG = String.fromCharCode(176); 7 | 8 | export const EMPTY_OBJECT = {}; 9 | 10 | export const MDF_INFINITY = 1000000000000.0; 11 | 12 | export const WEBLAYOUT_TOOLBAR = "Toolbar"; 13 | export const WEBLAYOUT_TASKMENU = "TaskMenu"; 14 | export const WEBLAYOUT_CONTEXTMENU = "MapContextMenu"; 15 | 16 | export const FUSION_TASKPANE_NAME = "TaskPane"; 17 | export const FUSION_MAP_NAME = "Map"; 18 | export const FUSION_REDLINE_NAME = "Redline"; 19 | 20 | export const LAYER_ID_BASE = "LAYER_ID_BASE"; 21 | export const LAYER_ID_MG_BASE = "LAYER_ID_MG_BASE"; 22 | /** 23 | * @since 0.14.8 24 | */ 25 | export const LAYER_ID_MG_DYNAMIC_OVERLAY = "LAYER_ID_MG_DYNAMIC_OVERLAY"; 26 | export const LAYER_ID_MG_SEL_OVERLAY = "LAYER_ID_MG_SEL_OVERLAY"; 27 | 28 | export const BLANK_GIF_DATA_URI = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; -------------------------------------------------------------------------------- /src/containers/base-layer-switcher.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { BaseLayerSwitcher } from "../components/base-layer-switcher"; 3 | import { useActiveMapName, useViewerLocale, useActiveMapExternalBaseLayers } from './hooks'; 4 | import { setBaseLayer } from '../actions/map'; 5 | import { useReduxDispatch } from "../components/map-providers/context"; 6 | 7 | export interface IBaseLayerSwitcherContainerProps { 8 | 9 | } 10 | 11 | export const BaseLayerSwitcherContainer = () => { 12 | const mapName = useActiveMapName(); 13 | const locale = useViewerLocale(); 14 | const externalBaseLayers = useActiveMapExternalBaseLayers(); 15 | const dispatch = useReduxDispatch(); 16 | const setBaseLayerAction = (mapName: string, layerName: string) => dispatch(setBaseLayer(mapName, layerName)); 17 | const onBaseLayerChanged = (layerName: string) => { 18 | if (mapName) { 19 | setBaseLayerAction?.(mapName, layerName); 20 | } 21 | } 22 | if (locale && externalBaseLayers) { 23 | return ; 24 | } else { 25 | return