├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── docker-compose.yml ├── docs ├── api │ ├── circle.md │ ├── here-map.md │ ├── marker.md │ ├── routeline.md │ └── use-platform.md └── introduction │ └── installation.md ├── jest.setup.js ├── package.json ├── rollup.config.js ├── src ├── Circle.tsx ├── HEREMap.tsx ├── Marker.tsx ├── RouteLine.tsx ├── __tests__ │ └── HEREMap.test.tsx ├── hooks │ ├── use-link.ts │ ├── use-platform.ts │ └── use-script.ts ├── index.tsx └── utils │ ├── __tests__ │ ├── get-dom-marker-icon.test.ts │ └── get-marker-icon.test.ts │ ├── get-dom-marker-icon.ts │ ├── get-marker-icon.ts │ ├── map-context.ts │ ├── map-events.ts │ └── set-drag-event.ts ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.test.json ├── website ├── README.md ├── core │ └── Footer.js ├── i18n │ └── en.json ├── package.json ├── pages │ └── en │ │ ├── help.js │ │ ├── index.js │ │ └── users.js ├── sidebars.json ├── siteConfig.js ├── static │ ├── css │ │ └── custom.css │ └── img │ │ ├── favicon.ico │ │ ├── logo-white.png │ │ ├── logo.png │ │ ├── undraw_online.svg │ │ ├── undraw_operating_system.svg │ │ ├── undraw_react.svg │ │ └── undraw_tweetstorm.svg └── yarn.lock └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/example/ 3 | **/dist/ 4 | **/public/ 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint", "react-hooks"], 4 | "extends": [ 5 | "eslint:recommended", 6 | "plugin:react/recommended", 7 | "plugin:@typescript-eslint/recommended", 8 | ], 9 | "settings": { 10 | "react": { 11 | "version": "16.8" 12 | } 13 | }, 14 | "env": { 15 | "browser": true, 16 | "jest": true, 17 | "es6": true, 18 | "node": true 19 | }, 20 | "rules": { 21 | "react-hooks/rules-of-hooks": "error", 22 | "react-hooks/exhaustive-deps": "warn", 23 | "@typescript-eslint/indent": 0, 24 | "@typescript-eslint/explicit-function-return-type": 0, 25 | "@typescript-eslint/no-explicit-any": 0, 26 | "@typescript-eslint/no-var-requires": 0, 27 | "@typescript-eslint/no-parameter-properties": 0, 28 | "@typescript-eslint/explicit-member-accessibility": 0, 29 | "@typescript-eslint/no-object-literal-type-assertion": 0, 30 | "@typescript-eslint/no-non-null-assertion": 0, 31 | "@typescript-eslint/no-use-before-define": 0, 32 | "react/prop-types": 0 33 | }, 34 | "globals": { 35 | "H": "readonly" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | example/node_modules 25 | 26 | # test 27 | coverage 28 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /dist 3 | /node_modules 4 | /example 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "jsxBracketSameLine": false, 8 | "proseWrap": "always", 9 | "trailingComma": "all" 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | addons: 3 | apt: 4 | update: true 5 | 6 | before_install: 7 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- -version 1.16.0 8 | - export PATH=$HOME/.yarn/bin:$PATH 9 | 10 | node_js: 11 | - 'lts/*' 12 | 13 | cache: 14 | directories: 15 | - node_modules 16 | - ~/.yarn 17 | 18 | branches: 19 | only: 20 | - master 21 | script: 22 | - git config --global user.name "${GH_NAME}" 23 | - git config --global user.email "${GH_EMAIL}" 24 | - echo "machine github.com login ${GH_NAME} password ${GH_TOKEN}" > ~/.netrc 25 | - cd website && yarn install && GIT_USER="${GH_NAME}" yarn run 26 | publish-gh-pages 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.1.0 (January 18, 2020) 2 | 3 | - [#20](https://github.com/ordazgustavo/here-maps-react/pull/20) feat: add map 4 | container id. ([@omhoumz](https://github.com/omhoumz)) 5 | 6 | # 1.0.0-beta.2 (August 11, 2019) 7 | 8 | A complete rebuild of this library to use React Hooks internally (and in the 9 | near future expose some hooks), add features, fix bugs, and the initial version 10 | of the documentation. 11 | 12 | # 0.1.3 (July 07, 2019) 13 | 14 | Fixes issues with context API when map instance was not already defined. 15 | 16 | - [#8](https://github.com/ordazgustavo/here-maps-react/pull/10) fix: 17 | conditionally show children. 18 | ([@ordazsgustavo](https://github.com/ordazsgustavo)) 19 | 20 | # 0.1.1 (February 19, 2019) 21 | 22 | Add drag events feature support for Marker component. 23 | 24 | - [#3](https://github.com/ordazgustavo/here-maps-react/pull/3) Feat/marker drag 25 | event. ([@ordazsgustavo](https://github.com/ordazsgustavo)) 26 | 27 | # 0.1.0 (January 30, 2019) 28 | 29 | This release is considered a breaking change due to changes in the way the map 30 | internal container sets it's size. Previously it was set to `100vh` so it would 31 | allways take the full window height, now it is set to `100%` so it adapts to the 32 | container size set by the user. 33 | 34 | - [#1](https://github.com/ordazgustavo/here-maps-react/pull/1) Change vh to 35 | percent. ([@ordazsgustavo](https://github.com/ordazsgustavo)) 36 | - [#2](https://github.com/ordazgustavo/here-maps-react/pull/2) Remove Lodash 37 | dependency. ([@ordazsgustavo](https://github.com/ordazsgustavo)) 38 | 39 | # 0.0.1 (January 23, 2019) 40 | 41 | Project release. 42 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.11.4 2 | 3 | WORKDIR /app/website 4 | 5 | EXPOSE 3000 35729 6 | COPY ./docs /app/docs 7 | COPY ./website /app/website 8 | RUN yarn install 9 | 10 | CMD ["yarn", "start"] 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 Gustavo Ordaz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # here-maps-react [](https://www.npmjs.com/package/here-maps-react)   2 | 3 | > React components library for HERE Maps 4 | 5 | Easily integrate HERE Maps into your app with a set of React components. 6 | 7 | Full TypeScript support! 8 | 9 | Tree shakeable! 10 | 11 | ## Installation 12 | 13 | ```bash 14 | yarn add here-maps-react 15 | ``` 16 | 17 | Or 18 | 19 | ```bash 20 | npm install --save here-maps-react 21 | ``` 22 | 23 | The version **1.0.0** of this library uses React Hooks so you'll need to be on 24 | **16.8.0** or later. 25 | 26 | ## Docs 27 | 28 | The Here Maps React docs are pubished at 29 | [https://ordazgustavo.github.io/here-maps-react](https://ordazgustavo.github.io/here-maps-react) 30 | 31 | This library is based on the original 32 | [react-here-maps](https://github.com/Josh-ES/react-here-maps), updated to 33 | conform to React StrictMode, as well as, adding new components. 34 | 35 | ## License 36 | 37 | MIT © [ordazgustavo](https://github.com/ordazgustavo) 38 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | docusaurus: 5 | build: . 6 | ports: 7 | - 3000:3000 8 | - 35729:35729 9 | volumes: 10 | - ./docs:/app/docs 11 | - ./website/blog:/app/website/blog 12 | - ./website/core:/app/website/core 13 | - ./website/i18n:/app/website/i18n 14 | - ./website/pages:/app/website/pages 15 | - ./website/static:/app/website/static 16 | - ./website/sidebars.json:/app/website/sidebars.json 17 | - ./website/siteConfig.js:/app/website/siteConfig.js 18 | working_dir: /app/website 19 | -------------------------------------------------------------------------------- /docs/api/circle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: circle 3 | title: 4 | --- 5 | 6 | `` is the component used for displaying circle shapes on the map. 7 | 8 | ## Usage 9 | 10 | ```jsx 11 | import React from 'react': 12 | import { HEREMap, Marker, Circle } from 'here-maps-react'; 13 | 14 | export function Map() { 15 | const [shape, setShape] = React.useState([]); 16 | 17 | React.useEffect(() => { 18 | ... 19 | setShape([...]); 20 | }); 21 | 22 | return ( 23 | 29 | 30 | 31 | 32 | ); 33 | } 34 | ``` 35 | 36 | ### Props 37 | 38 | **Available** 39 | 40 | - `lat: H.geo.Latitude;` 41 | - `lng: H.geo.Longitude;` 42 | - `radius: number;` 43 | - `strokeColor?: string;` 44 | - `lineWidth?: number;` 45 | - `fillColor?: string;` 46 | 47 | **To be implemented** 48 | 49 | - `style?: H.map.SpatialStyle | H.map.SpatialStyle.Options;` 50 | - `visibility?: boolean;` 51 | - `precision?: number;` 52 | - `zIndex?: number;` 53 | - `min?: number;` 54 | - `max?: number;` 55 | - `provider?: H.map.provider.Provider;` 56 | - `data?: any;` 57 | 58 | ### Reference 59 | 60 | You can find more info about this types 61 | [here](https://developer.here.com/documentation/maps/topics_api/h-intro.html). 62 | -------------------------------------------------------------------------------- /docs/api/here-map.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: here-map 3 | title: 4 | --- 5 | 6 | `` is the main component included. It is the one that encapsulates 7 | the logic. 8 | 9 | It can be imported as a default import: 10 | 11 | ```jsx 12 | import HEREMap from 'here-maps-react'; 13 | ``` 14 | 15 | or as a named import 16 | 17 | ```jsx 18 | import { HEREMap } from 'here-maps-react'; 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```jsx 24 | export function Map() { 25 | return ( 26 | 32 | ); 33 | } 34 | ``` 35 | 36 | ### Props 37 | 38 | **Available** 39 | 40 | - `appId: string;` 41 | - `appCode: string;` 42 | - `animateCenter?: boolean;` 43 | - `animateZoom?: boolean;` 44 | - `center?: H.geo.IPoint;` 45 | - `children: React.ReactNode` 46 | - `hidpi?: boolean;` 47 | - `interactive?: boolean;` 48 | - `secure?: boolean;` 49 | - `setLayer?: { layer: keyof H.service.MapType; mapType: keyof H.service.DefaultLayers; };` 50 | - `zoom?: number;` 51 | - `onPointerDown?: (e: H.util.Event) => void;` 52 | - `onPointerUp?: (e: H.util.Event) => void;` 53 | - `onPointerMove?: (e: H.util.Event) => void;` 54 | - `onPointerEnter?: (e: H.util.Event) => void;` 55 | - `onPointerLeave?: (e: H.util.Event) => void;` 56 | - `onPointerCancel?: (e: H.util.Event) => void;` 57 | - `onDragStart?: (e: H.util.Event) => void;` 58 | - `onDrag?: (e: H.util.Event) => void;` 59 | - `onDragEnd?: (e: H.util.Event) => void;` 60 | - `onTap?: (e: H.util.Event) => void;` 61 | - `onDoubleTap?: (e: H.util.Event) => void;` 62 | - `onLongPress?: (e: H.util.Event) => void;` 63 | 64 | **To be implemented** 65 | 66 | - `bounds?: H.geo.Rect;` 67 | - `layers?: H.map.layer.Layer[];` 68 | - `engineType?: EngineType;` 69 | - `pixelRatio?: number;` 70 | - `imprint?: H.map.Imprint.Options;` 71 | - `renderBaseBackground?: BackgroundRange;` 72 | - `autoColor?: boolean;` 73 | - `margin?: number;` 74 | - `padding?: H.map.ViewPort.Padding;` 75 | - `fixedCenter?: boolean;` 76 | - `noWrap?: boolean;` 77 | 78 | ### Reference 79 | 80 | You can find more info about this types 81 | [here](https://developer.here.com/documentation/maps/topics_api/h-intro.html). 82 | -------------------------------------------------------------------------------- /docs/api/marker.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: marker 3 | title: 4 | --- 5 | 6 | `` is the component used for displaying a marker on the map, the only 7 | required props are `lat` and `lng` to set its position. 8 | 9 | ## Usage 10 | 11 | ```jsx 12 | import { HEREMap, Marker } from 'here-maps-react'; 13 | 14 | export function Map() { 15 | return ( 16 | 22 | {...}} 27 | /> 28 | 29 | ); 30 | } 31 | ``` 32 | 33 | ### Props 34 | 35 | **Available** 36 | 37 | - `lat: H.geo.Latitude;` 38 | - `lng: H.geo.Longitude;` 39 | - `bitmap?: string;` 40 | - `children: React.ReactNode` 41 | - `draggable?: boolean;` 42 | - `onPointerDown?: (e: H.util.Event) => void;` 43 | - `onPointerUp?: (e: H.util.Event) => void;` 44 | - `onPointerMove?: (e: H.util.Event) => void;` 45 | - `onPointerEnter?: (e: H.util.Event) => void;` 46 | - `onPointerLeave?: (e: H.util.Event) => void;` 47 | - `onPointerCancel?: (e: H.util.Event) => void;` 48 | - `onDragStart?: (e: H.util.Event) => void;` 49 | - `onDrag?: (e: H.util.Event) => void;` 50 | - `onDragEnd?: (e: H.util.Event) => void;` 51 | - `onTap?: (e: H.util.Event) => void;` 52 | - `onDoubleTap?: (e: H.util.Event) => void;` 53 | - `onLongPress?: (e: H.util.Event) => void;` 54 | 55 | **To be implemented** 56 | 57 | - `alt?: H.geo.Altitude;` 58 | - `ctx?: H.geo.AltitudeContext;` 59 | - `min?: number;` 60 | - `max?: number;` 61 | - `visibility?: boolean;` 62 | - `zIndex?: number;` 63 | - `provider?: H.map.provider.Provider;` 64 | - `icon?: H.map.Icon;` 65 | - `data?: any;` 66 | 67 | ### Reference 68 | 69 | You can find more info about this types 70 | [here](https://developer.here.com/documentation/maps/topics_api/h-intro.html). 71 | -------------------------------------------------------------------------------- /docs/api/routeline.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: routeline 3 | title: 4 | --- 5 | 6 | `` is the component used for displaying a `Polyline` representing a 7 | route between two or more points on the map given a `shape` of latitudes and 8 | longitudes. 9 | 10 | ## Usage 11 | 12 | ```jsx 13 | import React from 'react': 14 | import { HEREMap, Marker, RouteLine } from 'here-maps-react'; 15 | 16 | export function Map() { 17 | const [shape, setShape] = React.useState([]); 18 | 19 | React.useEffect(() => { 20 | ... 21 | setShape([...]); 22 | }); 23 | 24 | return ( 25 | 31 | 32 | 33 | 34 | 35 | ); 36 | } 37 | ``` 38 | 39 | ### Props 40 | 41 | **Available** 42 | 43 | - `strokeColor?: string;` 44 | - `lineWidth?: number;` 45 | - `shape: string[];` 46 | 47 | **To be implemented** 48 | 49 | - `style?: (H.map.SpatialStyle | H.map.SpatialStyle.Options);` 50 | - `arrows?: (H.map.ArrowStyle | H.map.ArrowStyle.Options);` 51 | - `visibility?: boolean;` 52 | - `zIndex?: number;` 53 | - `min?: number;` 54 | - `max?: number;` 55 | - `provider?: H.map.provider.Provider;` 56 | - `data?: any;` 57 | 58 | ### Reference 59 | 60 | You can find more info about this types 61 | [here](https://developer.here.com/documentation/maps/topics_api/h-intro.html). 62 | -------------------------------------------------------------------------------- /docs/api/use-platform.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: use-platform 3 | title: usePlatform() 4 | --- 5 | 6 | `usePlatform()` is a hook that you can use to initialize the HERE Maps service 7 | Platform by yourselve. 8 | 9 | ## Usage 10 | 11 | ```jsx 12 | import React from 'react': 13 | import { usePlatform } from 'here-maps-react'; 14 | 15 | export function Map() { 16 | const platform = usePlatform({ 17 | app_code: appCode, 18 | app_id: appId, 19 | useHTTPS: true, 20 | }); 21 | 22 | ... 23 | } 24 | ``` 25 | 26 | It receives all of the options that you can pass to the `H.service.Platform` 27 | constructor as a first argument and an optional `scriptsLoaded?: boolean` 28 | (defaults to `true`) which is mostly for internal use, and returns a 29 | `H.service.Platform`. 30 | 31 | ### Reference 32 | 33 | You can find more info about this types 34 | [here](https://developer.here.com/documentation/maps/topics_api/h-intro.html). 35 | -------------------------------------------------------------------------------- /docs/introduction/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Installation 4 | sidebar_label: Installation 5 | --- 6 | 7 | [here-maps-react](https://github.com/ordazgustavo/here-maps-react) is an 8 | "unnoficial" library for adding HERE Maps to a React application in a composable 9 | fashion. 10 | 11 | The **1.0.0** version of this library requires React 16.8.0 or later. 12 | 13 | ```bash 14 | npm install react-here-maps 15 | ``` 16 | 17 | or 18 | 19 | ```bash 20 | yarn add react-here-maps 21 | ``` 22 | 23 | There's no need to add the required 24 | [script tags](https://developer.here.com/documentation/maps/topics/quick-start.html) 25 | because it is all managed by `here-maps-react`. 26 | 27 | The only required step is to 28 | [generate](https://developer.here.com/documentation/maps/common/get-credentials-api-key.html) 29 | an `api_code` and `api_id`. 30 | 31 | Currently this library uses the 3.0 version of the map api until the types are 32 | updated for the 3.1 version. 33 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | require('@testing-library/jest-dom/extend-expect'); 2 | 3 | class Platform { 4 | createDefaultLayers() { 5 | return { 6 | normal: { 7 | map: '', 8 | }, 9 | }; 10 | } 11 | } 12 | 13 | class Map { 14 | addObject() {} 15 | removeObject() {} 16 | } 17 | 18 | class Circle { 19 | setRadius() {} 20 | setCenter() {} 21 | } 22 | class DomIcon {} 23 | class Icon {} 24 | class Marker { 25 | setPosition() {} 26 | } 27 | class DomMarker extends Marker {} 28 | class Polyline {} 29 | 30 | class LineString { 31 | pushLatLngAlt() {} 32 | } 33 | 34 | global.H = { 35 | service: { 36 | Platform, 37 | }, 38 | Map, 39 | map: { 40 | Circle, 41 | DomIcon, 42 | DomMarker, 43 | Icon, 44 | Marker, 45 | Polyline, 46 | }, 47 | geo: { 48 | LineString, 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "here-maps-react", 3 | "version": "1.1.0", 4 | "description": "React components library for HERE Maps", 5 | "author": "Gustavo Ordaz ", 6 | "license": "MIT", 7 | "repository": "ordazgustavo/here-maps-react", 8 | "main": "dist/index.js", 9 | "module": "dist/index.es.js", 10 | "engines": { 11 | "node": ">=8", 12 | "npm": ">=5" 13 | }, 14 | "keywords": [ 15 | "here", 16 | "heremaps", 17 | "here-maps-react", 18 | "maps", 19 | "hooks" 20 | ], 21 | "scripts": { 22 | "test": "jest", 23 | "test:watch": "jest --watch", 24 | "test:cov": "jest --coverage", 25 | "build": "rollup -c", 26 | "start": "rollup -c -w", 27 | "prepare": "npm run build", 28 | "format": "prettier --write \"src/**/*.+(js|md|css|json|ts|tsx)\"" 29 | }, 30 | "peerDependencies": { 31 | "react": ">=16.8.0", 32 | "react-dom": ">=16.8.0" 33 | }, 34 | "dependencies": { 35 | "lodash.debounce": "^4.0.8" 36 | }, 37 | "devDependencies": { 38 | "@testing-library/jest-dom": "^4.2.3", 39 | "@testing-library/react": "^9.3.2", 40 | "@types/heremaps": "^3.0.15", 41 | "@types/jest": "^24.0.22", 42 | "@types/lodash.debounce": "^4.0.6", 43 | "@types/react": "^16.9.11", 44 | "@types/react-dom": "^16.9.4", 45 | "@typescript-eslint/eslint-plugin": "^2.7.0", 46 | "@typescript-eslint/parser": "^2.7.0", 47 | "eslint": "6.6.0", 48 | "eslint-plugin-react": "^7.16.0", 49 | "eslint-plugin-react-hooks": "^2.2.0", 50 | "gh-pages": "^2.1.1", 51 | "husky": "^3.0.9", 52 | "jest": "^24.9.0", 53 | "jest-environment-jsdom": "^24.9.0", 54 | "lint-staged": "^9.4.2", 55 | "prettier": "^1.19.1", 56 | "react": "^16.11.0", 57 | "react-dom": "^16.11.0", 58 | "rollup": "^1.26.5", 59 | "rollup-plugin-commonjs": "^10.1.0", 60 | "rollup-plugin-node-resolve": "^5.2.0", 61 | "rollup-plugin-peer-deps-external": "^2.2.0", 62 | "rollup-plugin-typescript2": "^0.25.2", 63 | "rollup-plugin-url": "^3.0.0", 64 | "ts-jest": "^24.1.0", 65 | "typescript": "^3.7.2" 66 | }, 67 | "jest": { 68 | "clearMocks": true, 69 | "browser": true, 70 | "moduleFileExtensions": [ 71 | "ts", 72 | "tsx", 73 | "js", 74 | "jsx" 75 | ], 76 | "modulePaths": [ 77 | "/src/" 78 | ], 79 | "collectCoverageFrom": [ 80 | "src/**/*.{ts,tsx}", 81 | "!**/node_modules/**", 82 | "!**/lib/**", 83 | "!**/dist/**", 84 | "!**/__tests__/**" 85 | ], 86 | "testMatch": [ 87 | "/src/**/__tests__/**/?(*.)(spec|test).ts(x|)" 88 | ], 89 | "transform": { 90 | "^.+\\.(ts|tsx)$": "ts-jest" 91 | }, 92 | "globals": { 93 | "ts-jest": { 94 | "tsConfig": "tsconfig.test.json", 95 | "diagnostics": { 96 | "pathRegex": "/.(spec|test).ts$/" 97 | } 98 | } 99 | }, 100 | "setupFilesAfterEnv": [ 101 | "/jest.setup.js" 102 | ] 103 | }, 104 | "files": [ 105 | "dist" 106 | ], 107 | "husky": { 108 | "hooks": { 109 | "pre-commit": "lint-staged" 110 | } 111 | }, 112 | "lint-staged": { 113 | "src/**/*.{js,md,css,json,ts,tsx}": [ 114 | "prettier --write", 115 | "git add" 116 | ] 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import external from 'rollup-plugin-peer-deps-external'; 4 | import resolve from 'rollup-plugin-node-resolve'; 5 | import url from 'rollup-plugin-url'; 6 | 7 | import pkg from './package.json'; 8 | 9 | export default { 10 | input: 'src/index.tsx', 11 | output: [ 12 | { 13 | file: pkg.main, 14 | format: 'cjs', 15 | exports: 'named', 16 | sourcemap: true, 17 | }, 18 | { 19 | file: pkg.module, 20 | format: 'es', 21 | exports: 'named', 22 | sourcemap: true, 23 | }, 24 | ], 25 | external: ['stream'], 26 | plugins: [ 27 | external(), 28 | url(), 29 | resolve({ preferBuiltins: true }), 30 | typescript({ 31 | rollupCommonJSResolveHack: true, 32 | clean: true, 33 | }), 34 | commonjs(), 35 | ], 36 | }; 37 | -------------------------------------------------------------------------------- /src/Circle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import MapContext from './utils/map-context'; 4 | 5 | export interface CircleProps extends H.map.Circle.Options, H.geo.IPoint { 6 | strokeColor?: string; 7 | lineWidth?: number; 8 | fillColor?: string; 9 | radius: number; 10 | } 11 | 12 | export const Circle: React.FC = ({ 13 | lat, 14 | lng, 15 | strokeColor, 16 | lineWidth, 17 | fillColor, 18 | radius, 19 | }) => { 20 | const mapContext = React.useContext(MapContext); 21 | const [circle, setCircle] = React.useState(); 22 | 23 | React.useEffect(() => { 24 | const { map } = mapContext; 25 | 26 | if (map && !circle) { 27 | const newCircle = new H.map.Circle( 28 | { 29 | lat, 30 | lng, 31 | }, 32 | radius, 33 | { 34 | style: { 35 | fillColor, 36 | lineWidth, 37 | strokeColor, 38 | }, 39 | }, 40 | ); 41 | map.addObject(newCircle); 42 | 43 | setCircle(newCircle); 44 | } 45 | return () => { 46 | if (map && circle) { 47 | map.removeObject(circle); 48 | } 49 | }; 50 | }, [circle, fillColor, lat, lineWidth, lng, mapContext, radius, strokeColor]); 51 | 52 | React.useEffect(() => { 53 | if (circle && lat && lng) { 54 | circle.setCenter({ 55 | lat, 56 | lng, 57 | }); 58 | } 59 | }, [circle, lat, lng]); 60 | 61 | React.useEffect(() => { 62 | if (circle && radius) { 63 | circle.setRadius(radius); 64 | } 65 | }, [circle, radius]); 66 | 67 | return null; 68 | }; 69 | 70 | export default Circle; 71 | -------------------------------------------------------------------------------- /src/HEREMap.tsx: -------------------------------------------------------------------------------- 1 | /* eslint @typescript-eslint/camelcase: 0 */ 2 | import React from 'react'; 3 | import debounce from 'lodash.debounce'; 4 | 5 | import MapContext from './utils/map-context'; 6 | import { HEvents, events, Events } from './utils/map-events'; 7 | import { usePlatform } from './hooks/use-platform'; 8 | import { useScript } from './hooks/use-script'; 9 | import { useLink } from './hooks/use-link'; 10 | 11 | export interface HEREMapProps extends H.Map.Options, HEvents { 12 | appId: string; 13 | appCode: string; 14 | mapContainerId?: string; 15 | animateCenter?: boolean; 16 | animateZoom?: boolean; 17 | hidpi?: boolean; 18 | interactive?: boolean; 19 | secure?: boolean; 20 | setLayer?: { 21 | layer: keyof H.service.MapType; 22 | mapType: keyof H.service.DefaultLayers; 23 | }; 24 | } 25 | 26 | export const HEREMap: React.FC = ({ 27 | animateCenter, 28 | animateZoom, 29 | appId, 30 | appCode, 31 | mapContainerId = 'map-container', 32 | center, 33 | hidpi, 34 | interactive, 35 | secure, 36 | zoom, 37 | setLayer, 38 | children, 39 | ...rest 40 | }) => { 41 | const [map, setMap] = React.useState(undefined); 42 | const [behavior, setBehavior] = React.useState< 43 | H.mapevents.Behavior | undefined 44 | >(undefined); 45 | const [ui, setUi] = React.useState(undefined); 46 | const debouncedResizeMap = debounce(resizeMap, 200); 47 | const [,] = useLink( 48 | 'https://js.api.here.com/v3/3.0/mapsjs-ui.css?dp-version=1526040296', 49 | 'map-styles', 50 | ); 51 | const [coreLoaded] = useScript( 52 | 'https://js.api.here.com/v3/3.0/mapsjs-core.js', 53 | 'core', 54 | ); 55 | const [serviceLoaded] = useScript( 56 | 'https://js.api.here.com/v3/3.0/mapsjs-service.js', 57 | 'service', 58 | ); 59 | const [uiLoaded] = useScript( 60 | 'https://js.api.here.com/v3/3.0/mapsjs-ui.js', 61 | 'ui', 62 | ); 63 | const [mapeventsLoaded] = useScript( 64 | 'https://js.api.here.com/v3/3.0/mapsjs-mapevents.js', 65 | 'mapevents', 66 | ); 67 | const platform = usePlatform( 68 | { 69 | app_code: appCode, 70 | app_id: appId, 71 | useHTTPS: secure === true, 72 | }, 73 | coreLoaded && serviceLoaded && uiLoaded && mapeventsLoaded, 74 | ); 75 | 76 | React.useEffect(() => { 77 | if (platform) { 78 | const defaultLayers = platform.createDefaultLayers({ 79 | ppi: hidpi ? 320 : 72, 80 | }); 81 | 82 | const mapElement = document.querySelector(`#${mapContainerId}`); 83 | 84 | let customLayer: H.map.layer.Layer | undefined; 85 | if (setLayer && setLayer.mapType && setLayer.layer) { 86 | const { mapType, layer } = setLayer; 87 | if (mapType === 'incidents' || mapType === 'venues') { 88 | customLayer = defaultLayers[mapType]; 89 | } else { 90 | customLayer = defaultLayers[mapType][layer]; 91 | } 92 | } 93 | 94 | if (mapElement && !map) { 95 | const newMap = new H.Map( 96 | mapElement, 97 | customLayer || defaultLayers.normal.map, 98 | { 99 | center, 100 | zoom, 101 | pixelRatio: hidpi ? 2 : 1, 102 | }, 103 | ); 104 | setMap(newMap); 105 | if (interactive) { 106 | const newBehavior = new H.mapevents.Behavior( 107 | new H.mapevents.MapEvents(newMap), 108 | ); 109 | 110 | const newUi = H.ui.UI.createDefault(newMap, defaultLayers); 111 | setBehavior(newBehavior); 112 | setUi(newUi); 113 | } 114 | } 115 | 116 | if (typeof window !== 'undefined') { 117 | window.addEventListener('resize', debouncedResizeMap); 118 | } 119 | } 120 | 121 | return () => { 122 | if (typeof window !== 'undefined') { 123 | window.removeEventListener('resize', debouncedResizeMap); 124 | } 125 | }; 126 | }, [ 127 | center, 128 | debouncedResizeMap, 129 | hidpi, 130 | interactive, 131 | map, 132 | mapContainerId, 133 | platform, 134 | setLayer, 135 | zoom, 136 | ]); 137 | 138 | React.useEffect(() => { 139 | if (map) { 140 | Object.entries(events).forEach(([event, hereEvent]) => { 141 | const e = rest[event as keyof Events]; 142 | if (typeof e === 'function') { 143 | map.addEventListener(hereEvent, e); 144 | } 145 | }); 146 | } 147 | return () => { 148 | if (map) { 149 | Object.entries(events).forEach(([event, hereEvent]) => { 150 | const e = rest[event as keyof Events]; 151 | if (typeof e === 'function') { 152 | map.removeEventListener(hereEvent, e); 153 | } 154 | }); 155 | } 156 | }; 157 | }, [map, rest]); 158 | 159 | React.useEffect(() => { 160 | if (map && center) { 161 | map.setCenter(center, animateCenter === true); 162 | } 163 | }, [animateCenter, center, map]); 164 | 165 | React.useEffect(() => { 166 | if (map && zoom) { 167 | map.setZoom(zoom, animateZoom === true); 168 | } 169 | }, [animateZoom, map, zoom]); 170 | 171 | function resizeMap() { 172 | if (map) { 173 | map.getViewPort().resize(); 174 | } 175 | } 176 | 177 | return ( 178 | 179 | 184 | {map ? children : null} 185 | 186 | 187 | ); 188 | }; 189 | 190 | export default HEREMap; 191 | -------------------------------------------------------------------------------- /src/Marker.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOMServer from 'react-dom/server'; 3 | 4 | import getDomMarkerIcon from './utils/get-dom-marker-icon'; 5 | import getMarkerIcon from './utils/get-marker-icon'; 6 | import MapContext from './utils/map-context'; 7 | import { setMarkerDragEvent } from './utils/set-drag-event'; 8 | import { HEvents, events, Events } from './utils/map-events'; 9 | 10 | export interface MarkerProps 11 | extends H.map.Marker.Options, 12 | H.geo.IPoint, 13 | HEvents { 14 | bitmap?: string; 15 | draggable?: boolean; 16 | } 17 | 18 | export const Marker: React.FC = ({ 19 | children, 20 | bitmap, 21 | lat, 22 | lng, 23 | draggable, 24 | ...rest 25 | }) => { 26 | const mapContext = React.useContext(MapContext); 27 | const [marker, setMarker] = React.useState< 28 | H.map.Marker | H.map.DomMarker | undefined 29 | >(undefined); 30 | 31 | React.useEffect(() => { 32 | const { map, behavior } = mapContext; 33 | 34 | if (map && !marker) { 35 | let newMarker: H.map.DomMarker | H.map.Marker; 36 | if (React.Children.count(children) > 0) { 37 | const html = ReactDOMServer.renderToStaticMarkup( 38 | {children}, 39 | ); 40 | const icon = getDomMarkerIcon(html); 41 | newMarker = new H.map.DomMarker({ lat, lng }, { icon }); 42 | } else if (bitmap) { 43 | const icon = getMarkerIcon(bitmap); 44 | 45 | newMarker = new H.map.Marker({ lat, lng }, { icon }); 46 | } else { 47 | newMarker = new H.map.Marker({ lat, lng }); 48 | } 49 | 50 | if (draggable && behavior) { 51 | newMarker.draggable = draggable; 52 | setMarkerDragEvent(map, behavior); 53 | } 54 | 55 | map.addObject(newMarker); 56 | setMarker(newMarker); 57 | } 58 | return () => { 59 | if (map && marker) { 60 | map.removeObject(marker); 61 | } 62 | }; 63 | }, [bitmap, children, draggable, lat, lng, mapContext, marker]); 64 | 65 | React.useEffect(() => { 66 | if (marker) { 67 | Object.entries(events).forEach(([event, hereEvent]) => { 68 | const e = rest[event as keyof Events]; 69 | if (typeof e === 'function') { 70 | marker.addEventListener(hereEvent, e); 71 | } 72 | }); 73 | } 74 | return () => { 75 | if (marker) { 76 | Object.entries(events).forEach(([event, hereEvent]) => { 77 | const e = rest[event as keyof Events]; 78 | if (typeof e === 'function') { 79 | marker.removeEventListener(hereEvent, e); 80 | } 81 | }); 82 | } 83 | }; 84 | }, [marker, rest]); 85 | 86 | React.useEffect(() => { 87 | if (marker && lat && lng) { 88 | marker.setPosition({ 89 | lat, 90 | lng, 91 | }); 92 | } 93 | }, [lat, lng, marker]); 94 | 95 | return null; 96 | }; 97 | 98 | export default Marker; 99 | -------------------------------------------------------------------------------- /src/RouteLine.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import MapContext from './utils/map-context'; 4 | 5 | type Shape = string[]; 6 | 7 | export interface RouteLineProps extends H.map.Polyline.Options { 8 | strokeColor?: string; 9 | lineWidth?: number; 10 | shape: Shape; 11 | } 12 | 13 | export const RouteLine: React.FC = ({ 14 | shape, 15 | strokeColor, 16 | lineWidth, 17 | }) => { 18 | const mapContext = React.useContext(MapContext); 19 | const [routeLine, setRouteLine] = React.useState( 20 | undefined, 21 | ); 22 | 23 | React.useEffect(() => { 24 | const { map } = mapContext; 25 | if (map) { 26 | const linestring = new H.geo.LineString(); 27 | shape.forEach(point => { 28 | const [lat, lng] = point.split(','); 29 | linestring.pushLatLngAlt(Number(lat), Number(lng), 1); 30 | }); 31 | 32 | const newRouteLine = new H.map.Polyline(linestring, { 33 | style: { strokeColor, lineWidth }, 34 | }); 35 | 36 | if (routeLine) { 37 | map.removeObject(routeLine); 38 | } 39 | map.addObject(newRouteLine); 40 | 41 | setRouteLine(routeLine); 42 | } 43 | return () => { 44 | if (map && routeLine) { 45 | map.removeObject(routeLine); 46 | } 47 | }; 48 | }, [lineWidth, mapContext, routeLine, shape, strokeColor]); 49 | 50 | return null; 51 | }; 52 | 53 | export default RouteLine; 54 | -------------------------------------------------------------------------------- /src/__tests__/HEREMap.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import HEREMap from '../HEREMap'; 5 | import Marker from '../Marker'; 6 | import RouteLine from '../RouteLine'; 7 | import Circle from '../Circle'; 8 | 9 | const appId = ''; 10 | const appCode = ''; 11 | 12 | describe('HEREMap', () => { 13 | it('renders with required props', () => { 14 | const { getByTestId } = render(); 15 | expect(getByTestId('map-container')).toBeTruthy(); 16 | }); 17 | 18 | it('renders with Marker', () => { 19 | const { getByTestId } = render( 20 | 21 | 22 | , 23 | ); 24 | expect(getByTestId('map-container')).toBeTruthy(); 25 | }); 26 | 27 | it('renders with RouteLine', () => { 28 | const { getByTestId } = render( 29 | 30 | 31 | , 32 | ); 33 | expect(getByTestId('map-container')).toBeTruthy(); 34 | }); 35 | 36 | it('renders with Circle', () => { 37 | const { getByTestId } = render( 38 | 39 | 40 | , 41 | ); 42 | expect(getByTestId('map-container')).toBeTruthy(); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/hooks/use-link.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const loadedLinks: Map = new Map(); 4 | 5 | export function useLink(url: string, name: string) { 6 | const [state, setState] = React.useState({ 7 | loaded: false, 8 | error: false, 9 | }); 10 | 11 | React.useEffect(() => { 12 | if (loadedLinks.get(name)) { 13 | setState({ 14 | loaded: true, 15 | error: false, 16 | }); 17 | } else { 18 | loadedLinks.set(name, url); 19 | 20 | const link = document.createElement('link'); 21 | const body = document.getElementsByTagName('body')[0]; 22 | 23 | link.href = url; 24 | link.rel = 'stylesheet'; 25 | link.type = 'text/css'; 26 | 27 | const onLinkLoad = () => { 28 | setState({ 29 | loaded: true, 30 | error: false, 31 | }); 32 | }; 33 | 34 | const onLinkError = () => { 35 | const exist = loadedLinks.get(name); 36 | if (exist) { 37 | loadedLinks.delete(name); 38 | } 39 | link.remove(); 40 | 41 | setState({ 42 | loaded: true, 43 | error: true, 44 | }); 45 | }; 46 | 47 | link.addEventListener('load', onLinkLoad); 48 | link.addEventListener('error', onLinkError); 49 | 50 | body.appendChild(link); 51 | 52 | return () => { 53 | link.removeEventListener('load', onLinkLoad); 54 | link.removeEventListener('error', onLinkError); 55 | }; 56 | } 57 | }, [name, url]); 58 | 59 | return [state.loaded, state.error]; 60 | } 61 | -------------------------------------------------------------------------------- /src/hooks/use-platform.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export function usePlatform( 4 | platformOptions: H.service.Platform.Options, 5 | scriptsLoaded = true, 6 | ) { 7 | const [platform, setPlatform] = React.useState< 8 | H.service.Platform | undefined 9 | >(undefined); 10 | 11 | React.useEffect(() => { 12 | if (!platform && scriptsLoaded) { 13 | setPlatform(new H.service.Platform(platformOptions)); 14 | } 15 | }, [platform, platformOptions, scriptsLoaded]); 16 | 17 | return platform; 18 | } 19 | -------------------------------------------------------------------------------- /src/hooks/use-script.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const cachedScripts: Map = new Map(); 4 | 5 | /** 6 | * 7 | * @param src - script URL 8 | * @param name - name for cache pourposes 9 | */ 10 | export function useScript(src: string, name: string) { 11 | // Keeping track of script loaded and error state 12 | const [state, setState] = React.useState({ 13 | loaded: false, 14 | error: false, 15 | }); 16 | 17 | React.useEffect(() => { 18 | // If cachedScripts array already includes src that means another instance ... 19 | // ... of this hook already loaded this script, so no need to load again. 20 | if (cachedScripts.get(name)) { 21 | setState({ 22 | loaded: true, 23 | error: false, 24 | }); 25 | } else { 26 | cachedScripts.set(name, src); 27 | 28 | // Create script 29 | let script = document.createElement('script'); 30 | script.src = src; 31 | script.async = false; 32 | 33 | // Script event listener callbacks for load and error 34 | const onScriptLoad = () => { 35 | setState({ 36 | loaded: true, 37 | error: false, 38 | }); 39 | }; 40 | 41 | const onScriptError = () => { 42 | // Remove from cachedScripts we can try loading again 43 | const exist = cachedScripts.get(name); 44 | if (exist) { 45 | cachedScripts.delete(name); 46 | } 47 | script.remove(); 48 | 49 | setState({ 50 | loaded: true, 51 | error: true, 52 | }); 53 | }; 54 | 55 | script.addEventListener('load', onScriptLoad); 56 | script.addEventListener('error', onScriptError); 57 | 58 | // Add script to document body 59 | document.body.appendChild(script); 60 | 61 | // Remove event listeners on cleanup 62 | return () => { 63 | script.removeEventListener('load', onScriptLoad); 64 | script.removeEventListener('error', onScriptError); 65 | }; 66 | } 67 | }, [name, src]); // Only re-run effect if script src and name changes 68 | 69 | return [state.loaded, state.error]; 70 | } 71 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import HEREMap from './HEREMap'; 2 | import Circle from './Circle'; 3 | import Marker from './Marker'; 4 | import RouteLine from './RouteLine'; 5 | import { usePlatform } from './hooks/use-platform'; 6 | 7 | export { Circle, HEREMap, Marker, RouteLine, usePlatform }; 8 | 9 | export default HEREMap; 10 | -------------------------------------------------------------------------------- /src/utils/__tests__/get-dom-marker-icon.test.ts: -------------------------------------------------------------------------------- 1 | import getDomMarkerIcon from 'utils/get-dom-marker-icon'; 2 | 3 | describe('get-dom-marker-icon', () => { 4 | it('returns the same object', () => { 5 | const icon = getDomMarkerIcon('test'); 6 | expect(icon).toEqual(getDomMarkerIcon('marker')); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/utils/__tests__/get-marker-icon.test.ts: -------------------------------------------------------------------------------- 1 | import getMarkerIcon from 'utils/get-marker-icon'; 2 | 3 | describe('get-dom-marker-icon', () => { 4 | it('returns the same object', () => { 5 | const icon = getMarkerIcon('test'); 6 | expect(icon).toEqual(getMarkerIcon('marker')); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /src/utils/get-dom-marker-icon.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Map for HTML strings against H.map.DomIcon instances 3 | */ 4 | export const DomIcons: Map = new Map(); 5 | 6 | /** 7 | * Returns the DOM Icon for the input HTML string, ensuring that no more 8 | * than one DOM Icon is created for each HTML string 9 | * @param html {string} - A string containing the markup to be used as a Dom Icon. 10 | */ 11 | export default function getDomMarkerIcon( 12 | html: string, 13 | ): H.map.DomIcon | undefined { 14 | if (!DomIcons.has(html)) { 15 | const icon = new H.map.DomIcon(html); 16 | DomIcons.set(html, icon); 17 | } 18 | 19 | return DomIcons.get(html); 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/get-marker-icon.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Map for image URL strings against H.map.Icon instances 3 | */ 4 | export const Icons: Map = new Map(); 5 | 6 | /** 7 | * Returns the Icon for the input bitmap URL string, ensuring that no more 8 | * than one Icon is created for each bitmap 9 | * @param bitmap {string} - The location of the bitmap to be used as an icon 10 | */ 11 | export default function getMarkerIcon(bitmap: string): H.map.Icon | undefined { 12 | if (!Icons.has(bitmap)) { 13 | const icon = new H.map.Icon(bitmap); 14 | Icons.set(bitmap, icon); 15 | } 16 | 17 | return Icons.get(bitmap); 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/map-context.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export interface HEREMapContext { 4 | map?: H.Map; 5 | behavior?: H.mapevents.Behavior; 6 | ui?: H.ui.UI; 7 | } 8 | 9 | const MapContext = React.createContext({} as HEREMapContext); 10 | 11 | export default MapContext; 12 | -------------------------------------------------------------------------------- /src/utils/map-events.ts: -------------------------------------------------------------------------------- 1 | export interface HEvents { 2 | onPointerDown?: HEvent; 3 | onPointerUp?: HEvent; 4 | onPointerMove?: HEvent; 5 | onPointerEnter?: HEvent; 6 | onPointerLeave?: HEvent; 7 | onPointerCancel?: HEvent; 8 | onDragStart?: HEvent; 9 | onDrag?: HEvent; 10 | onDragEnd?: HEvent; 11 | onTap?: HEvent; 12 | onDoubleTap?: HEvent; 13 | onLongPress?: HEvent; 14 | } 15 | 16 | export type HEvent = (e: H.util.Event) => void; 17 | 18 | export interface Events { 19 | onPointerDown: string; 20 | onPointerUp: string; 21 | onPointerMove: string; 22 | onPointerEnter: string; 23 | onPointerLeave: string; 24 | onPointerCancel: string; 25 | onDragStart: string; 26 | onDrag: string; 27 | onDragEnd: string; 28 | onTap: string; 29 | onDoubleTap: string; 30 | onLongPress: string; 31 | } 32 | 33 | export const events: Events = { 34 | onPointerDown: 'pointerdown', 35 | onPointerUp: 'pointerup', 36 | onPointerMove: 'pointermove', 37 | onPointerEnter: 'pointerenter', 38 | onPointerLeave: 'pointerleave', 39 | onPointerCancel: 'pointercancel', 40 | onDragStart: 'dragstart', 41 | onDrag: 'drag', 42 | onDragEnd: 'dragend', 43 | onTap: 'tap', 44 | onDoubleTap: 'dbltap', 45 | onLongPress: 'onlongpress', 46 | }; 47 | -------------------------------------------------------------------------------- /src/utils/set-drag-event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A helper function that disables map behavior on drag event in order to allow 3 | * the marker to be moved. 4 | * @param map 5 | * @param behavior 6 | */ 7 | export function setMarkerDragEvent(map: H.Map, behavior: H.mapevents.Behavior) { 8 | map.addEventListener( 9 | 'dragstart', 10 | (e: H.util.Event) => { 11 | if ( 12 | e.target instanceof H.map.Marker || 13 | e.target instanceof H.map.DomMarker 14 | ) { 15 | behavior.disable(); 16 | } 17 | }, 18 | false, 19 | ); 20 | 21 | map.addEventListener( 22 | 'dragend', 23 | (e: H.util.Event) => { 24 | if ( 25 | e.target instanceof H.map.Marker || 26 | e.target instanceof H.map.DomMarker 27 | ) { 28 | behavior.enable(); 29 | } 30 | }, 31 | false, 32 | ); 33 | 34 | map.addEventListener( 35 | 'drag', 36 | (e: any) => { 37 | const target = e.target; 38 | const pointer = e.currentPointer; 39 | if ( 40 | target instanceof H.map.Marker || 41 | e.target instanceof H.map.DomMarker 42 | ) { 43 | target.setPosition( 44 | map.screenToGeo(pointer.viewportX, pointer.viewportY), 45 | ); 46 | } 47 | }, 48 | false, 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "esnext", 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "lib": ["esnext", "dom"], 8 | "strict": true, 9 | "experimentalDecorators": true, 10 | "noUnusedLocals": true, 11 | "jsx": "react", 12 | "forceConsistentCasingInFileNames": true, 13 | "declaration": true, 14 | "declarationMap": true, 15 | "sourceMap": true, 16 | // "allowJs": false, 17 | // "allowSyntheticDefaultImports": true, 18 | // "noImplicitReturns": true, 19 | // "noImplicitThis": true, 20 | // "noImplicitAny": true, 21 | // "strictNullChecks": true, 22 | // "suppressImplicitAnyIndexErrors": true, 23 | // "noUnusedParameters": true 24 | } 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "baseUrl": "./src", 6 | "allowSyntheticDefaultImports": true, 7 | }, 8 | "include": ["src"], 9 | "exclude": ["node_modules", "build", "dist", "example", "rollup.config.js"] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "allowJs": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | This website was created with [Docusaurus](https://docusaurus.io/). 2 | 3 | # What's In This Document 4 | 5 | * [Get Started in 5 Minutes](#get-started-in-5-minutes) 6 | * [Directory Structure](#directory-structure) 7 | * [Editing Content](#editing-content) 8 | * [Adding Content](#adding-content) 9 | * [Full Documentation](#full-documentation) 10 | 11 | # Get Started in 5 Minutes 12 | 13 | 1. Make sure all the dependencies for the website are installed: 14 | 15 | ```sh 16 | # Install dependencies 17 | $ yarn 18 | ``` 19 | 2. Run your dev server: 20 | 21 | ```sh 22 | # Start the site 23 | $ yarn start 24 | ``` 25 | 26 | ## Directory Structure 27 | 28 | Your project file structure should look something like this 29 | 30 | ``` 31 | my-docusaurus/ 32 | docs/ 33 | doc-1.md 34 | doc-2.md 35 | doc-3.md 36 | website/ 37 | blog/ 38 | 2016-3-11-oldest-post.md 39 | 2017-10-24-newest-post.md 40 | core/ 41 | node_modules/ 42 | pages/ 43 | static/ 44 | css/ 45 | img/ 46 | package.json 47 | sidebar.json 48 | siteConfig.js 49 | ``` 50 | 51 | # Editing Content 52 | 53 | ## Editing an existing docs page 54 | 55 | Edit docs by navigating to `docs/` and editing the corresponding document: 56 | 57 | `docs/doc-to-be-edited.md` 58 | 59 | ```markdown 60 | --- 61 | id: page-needs-edit 62 | title: This Doc Needs To Be Edited 63 | --- 64 | 65 | Edit me... 66 | ``` 67 | 68 | For more information about docs, click [here](https://docusaurus.io/docs/en/navigation) 69 | 70 | ## Editing an existing blog post 71 | 72 | Edit blog posts by navigating to `website/blog` and editing the corresponding post: 73 | 74 | `website/blog/post-to-be-edited.md` 75 | ```markdown 76 | --- 77 | id: post-needs-edit 78 | title: This Blog Post Needs To Be Edited 79 | --- 80 | 81 | Edit me... 82 | ``` 83 | 84 | For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) 85 | 86 | # Adding Content 87 | 88 | ## Adding a new docs page to an existing sidebar 89 | 90 | 1. Create the doc as a new markdown file in `/docs`, example `docs/newly-created-doc.md`: 91 | 92 | ```md 93 | --- 94 | id: newly-created-doc 95 | title: This Doc Needs To Be Edited 96 | --- 97 | 98 | My new content here.. 99 | ``` 100 | 101 | 1. Refer to that doc's ID in an existing sidebar in `website/sidebar.json`: 102 | 103 | ```javascript 104 | // Add newly-created-doc to the Getting Started category of docs 105 | { 106 | "docs": { 107 | "Getting Started": [ 108 | "quick-start", 109 | "newly-created-doc" // new doc here 110 | ], 111 | ... 112 | }, 113 | ... 114 | } 115 | ``` 116 | 117 | For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation) 118 | 119 | ## Adding a new blog post 120 | 121 | 1. Make sure there is a header link to your blog in `website/siteConfig.js`: 122 | 123 | `website/siteConfig.js` 124 | ```javascript 125 | headerLinks: [ 126 | ... 127 | { blog: true, label: 'Blog' }, 128 | ... 129 | ] 130 | ``` 131 | 132 | 2. Create the blog post with the format `YYYY-MM-DD-My-Blog-Post-Title.md` in `website/blog`: 133 | 134 | `website/blog/2018-05-21-New-Blog-Post.md` 135 | 136 | ```markdown 137 | --- 138 | author: Frank Li 139 | authorURL: https://twitter.com/foobarbaz 140 | authorFBID: 503283835 141 | title: New Blog Post 142 | --- 143 | 144 | Lorem Ipsum... 145 | ``` 146 | 147 | For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) 148 | 149 | ## Adding items to your site's top navigation bar 150 | 151 | 1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`: 152 | 153 | `website/siteConfig.js` 154 | ```javascript 155 | { 156 | headerLinks: [ 157 | ... 158 | /* you can add docs */ 159 | { doc: 'my-examples', label: 'Examples' }, 160 | /* you can add custom pages */ 161 | { page: 'help', label: 'Help' }, 162 | /* you can add external links */ 163 | { href: 'https://github.com/facebook/Docusaurus', label: 'GitHub' }, 164 | ... 165 | ], 166 | ... 167 | } 168 | ``` 169 | 170 | For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation) 171 | 172 | ## Adding custom pages 173 | 174 | 1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`: 175 | 1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element: 176 | 177 | `website/siteConfig.js` 178 | ```javascript 179 | { 180 | headerLinks: [ 181 | ... 182 | { page: 'my-new-custom-page', label: 'My New Custom Page' }, 183 | ... 184 | ], 185 | ... 186 | } 187 | ``` 188 | 189 | For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages). 190 | 191 | # Full Documentation 192 | 193 | Full documentation can be found on the [website](https://docusaurus.io/). 194 | -------------------------------------------------------------------------------- /website/core/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | class Footer extends React.Component { 11 | docUrl(doc, language) { 12 | const baseUrl = this.props.config.baseUrl; 13 | const docsUrl = this.props.config.docsUrl; 14 | const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`; 15 | const langPart = `${language ? `${language}/` : ''}`; 16 | return `${baseUrl}${docsPart}${langPart}${doc}`; 17 | } 18 | 19 | pageUrl(doc, language) { 20 | const baseUrl = this.props.config.baseUrl; 21 | return baseUrl + (language ? `${language}/` : '') + doc; 22 | } 23 | 24 | render() { 25 | return ( 26 | 95 | ); 96 | } 97 | } 98 | 99 | module.exports = Footer; 100 | -------------------------------------------------------------------------------- /website/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "This file is auto-generated by write-translations.js", 3 | "localized-strings": { 4 | "next": "Next", 5 | "previous": "Previous", 6 | "tagline": "React components library for HERE Maps", 7 | "docs": { 8 | "api/circle": { 9 | "title": "" 10 | }, 11 | "api/here-map": { 12 | "title": "" 13 | }, 14 | "api/marker": { 15 | "title": "" 16 | }, 17 | "api/routeline": { 18 | "title": "" 19 | }, 20 | "api/use-platform": { 21 | "title": "usePlatform()" 22 | }, 23 | "introduction/installation": { 24 | "title": "Installation", 25 | "sidebar_label": "Installation" 26 | } 27 | }, 28 | "links": { 29 | "Docs": "Docs", 30 | "API": "API", 31 | "Help": "Help", 32 | "GitHub": "GitHub" 33 | }, 34 | "categories": { 35 | "Introduction": "Introduction", 36 | "API Reference": "API Reference" 37 | } 38 | }, 39 | "pages-strings": { 40 | "Help Translate|recruit community translators for your project": "Help Translate", 41 | "Edit this Doc|recruitment message asking to edit the doc source": "Edit", 42 | "Translate this Doc|recruitment message asking to translate the docs": "Translate" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "examples": "docusaurus-examples", 4 | "start": "docusaurus-start", 5 | "build": "docusaurus-build", 6 | "publish-gh-pages": "docusaurus-publish", 7 | "write-translations": "docusaurus-write-translations", 8 | "version": "docusaurus-version", 9 | "rename-version": "docusaurus-rename-version" 10 | }, 11 | "devDependencies": { 12 | "docusaurus": "^1.12.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /website/pages/en/help.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | const CompLibrary = require('../../core/CompLibrary.js'); 11 | 12 | const Container = CompLibrary.Container; 13 | const GridBlock = CompLibrary.GridBlock; 14 | 15 | function Help(props) { 16 | const {config: siteConfig, language = ''} = props; 17 | const {baseUrl, docsUrl} = siteConfig; 18 | const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`; 19 | const langPart = `${language ? `${language}/` : ''}`; 20 | const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`; 21 | 22 | const supportLinks = [ 23 | { 24 | content: `Learn more using the [documentation on this site.](${docUrl( 25 | 'introduction/installation.html', 26 | )})`, 27 | title: 'Browse Docs', 28 | }, 29 | { 30 | content: 'Ask questions about the documentation and project', 31 | title: 'Join the community', 32 | }, 33 | { 34 | content: "Find out what's new with this project. Follow [@ordazsgustavo](https://twitter.com/ordazsgustavo) on Twitter", 35 | title: 'Stay up to date', 36 | }, 37 | ]; 38 | 39 | return ( 40 | 41 | 42 | 43 | 44 | Need help? 45 | 46 | This project is maintained by a dedicated group of people. 47 | 48 | 49 | 50 | 51 | ); 52 | } 53 | 54 | module.exports = Help; 55 | -------------------------------------------------------------------------------- /website/pages/en/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | const CompLibrary = require('../../core/CompLibrary.js'); 11 | 12 | const Container = CompLibrary.Container; 13 | const GridBlock = CompLibrary.GridBlock; 14 | 15 | class HomeSplash extends React.Component { 16 | render() { 17 | const { siteConfig, language = '' } = this.props; 18 | const { baseUrl, docsUrl } = siteConfig; 19 | const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`; 20 | const langPart = `${language ? `${language}/` : ''}`; 21 | const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`; 22 | 23 | const SplashContainer = props => ( 24 | 25 | 26 | {props.children} 27 | 28 | 29 | ); 30 | 31 | const Logo = props => ( 32 | 33 | 34 | 35 | ); 36 | 37 | const ProjectTitle = () => ( 38 | 39 | {siteConfig.title} 40 | {siteConfig.tagline} 41 | 42 | ); 43 | 44 | const PromoSection = props => ( 45 | 46 | 47 | {props.children} 48 | 49 | 50 | ); 51 | 52 | const Button = props => ( 53 | 54 | 55 | {props.children} 56 | 57 | 58 | ); 59 | 60 | return ( 61 | 62 | 63 | 64 | 65 | 66 | 67 | Try It Out 68 | 69 | Docs 70 | 71 | 72 | 73 | ); 74 | } 75 | } 76 | 77 | class Index extends React.Component { 78 | render() { 79 | const { config: siteConfig, language = '' } = this.props; 80 | const { baseUrl } = siteConfig; 81 | 82 | const Block = props => ( 83 | 88 | 93 | 94 | ); 95 | 96 | const Features = () => ( 97 | 98 | {[ 99 | { 100 | content: 101 | 'Easily display a map just importing the HEREMap Component', 102 | image: `${baseUrl}img/undraw_react.svg`, 103 | imageAlign: 'top', 104 | title: 'Composable', 105 | }, 106 | { 107 | content: 'Just add your appCode and appId from HERE', 108 | image: `${baseUrl}img/undraw_operating_system.svg`, 109 | imageAlign: 'top', 110 | title: 'Ready to use', 111 | }, 112 | ]} 113 | 114 | ); 115 | 116 | return ( 117 | 118 | 119 | 120 | 121 | 122 | 123 | ); 124 | } 125 | } 126 | 127 | module.exports = Index; 128 | -------------------------------------------------------------------------------- /website/pages/en/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | const CompLibrary = require('../../core/CompLibrary.js'); 11 | 12 | const Container = CompLibrary.Container; 13 | 14 | class Users extends React.Component { 15 | render() { 16 | const {config: siteConfig} = this.props; 17 | if ((siteConfig.users || []).length === 0) { 18 | return null; 19 | } 20 | 21 | const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`; 22 | const showcase = siteConfig.users.map(user => ( 23 | 24 | 25 | 26 | )); 27 | 28 | return ( 29 | 30 | 31 | 32 | 33 | Who is Using This? 34 | This project is used by many folks 35 | 36 | {showcase} 37 | Are you using this project? 38 | 39 | Add your company 40 | 41 | 42 | 43 | 44 | ); 45 | } 46 | } 47 | 48 | module.exports = Users; 49 | -------------------------------------------------------------------------------- /website/sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": { 3 | "Introduction": ["introduction/installation"], 4 | "API Reference": [ 5 | "api/here-map", 6 | "api/marker", 7 | "api/routeline", 8 | "api/circle", 9 | "api/use-platform" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /website/siteConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | // See https://docusaurus.io/docs/site-config for all the possible 9 | // site configuration options. 10 | 11 | // List of projects/orgs using your project for the users page. 12 | // const users = [ 13 | // { 14 | // caption: 'User1', 15 | // You will need to prepend the image path with your baseUrl 16 | // if it is not '/', like: '/test-site/img/image.jpg'. 17 | // image: '/img/undraw_open_source.svg', 18 | // infoLink: 'https://www.facebook.com', 19 | // pinned: true, 20 | // }, 21 | // ]; 22 | 23 | const siteConfig = { 24 | title: 'React Here Maps', // Title for your website. 25 | tagline: 'React components library for HERE Maps', 26 | url: 'https://ordazgustavo.github.io', // Your website URL 27 | baseUrl: '/here-maps-react/', // Base URL for your project */ 28 | // For github.io type URLs, you would set the url and baseUrl like: 29 | // url: 'https://facebook.github.io', 30 | // baseUrl: '/test-site/', 31 | 32 | // Used for publishing and more 33 | projectName: 'here-maps-react', 34 | organizationName: 'ordazgustavo', 35 | // For top-level user or org sites, the organization is still the same. 36 | // e.g., for the https://JoelMarcey.github.io site, it would be set like... 37 | // organizationName: 'JoelMarcey' 38 | 39 | // For no header links in the top nav bar -> headerLinks: [], 40 | headerLinks: [ 41 | { doc: 'introduction/installation', label: 'Docs' }, 42 | { doc: 'api/here-map', label: 'API' }, 43 | { page: 'help', label: 'Help' }, 44 | { 45 | href: 'https://github.com/ordazgustavo/here-maps-react', 46 | label: 'GitHub', 47 | }, 48 | ], 49 | 50 | // If you have users set above, you add it here: 51 | // users, 52 | 53 | /* path to images for header/footer */ 54 | headerIcon: 'img/logo-white.png', 55 | footerIcon: 'img/logo.png', 56 | favicon: 'img/favicon.ico', 57 | 58 | /* Colors for website */ 59 | colors: { 60 | primaryColor: '#65C1C2', 61 | secondaryColor: '#393E47', 62 | }, 63 | 64 | /* Custom fonts for website */ 65 | /* 66 | fonts: { 67 | myFont: [ 68 | "Times New Roman", 69 | "Serif" 70 | ], 71 | myOtherFont: [ 72 | "-apple-system", 73 | "system-ui" 74 | ] 75 | }, 76 | */ 77 | 78 | // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds. 79 | copyright: `Copyright © ${new Date().getFullYear()} ordazgustavo`, 80 | 81 | highlight: { 82 | // Highlight.js theme to use for syntax highlighting in code blocks. 83 | theme: 'default', 84 | }, 85 | 86 | // Add custom scripts here that would be placed in
This project is maintained by a dedicated group of people.
This project is used by many folks
Are you using this project?