diff --git a/.eslintignore b/.eslintignore index 51f7f14ab..371793efb 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,5 @@ -/plugin/build \ No newline at end of file +plugin +example +lib +__tests__ +ios/RCTMGL/index.d.ts \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 859bbf885..a2c067fa3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,6 @@ module.exports = { root: true, - parser: 'babel-eslint', + parser: '@babel/eslint-parser', plugins: ['react', 'react-native', 'fp', 'import'], env: { jest: true, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 058d6e1f5..6ae4f822f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,7 +20,7 @@ Added `your feature` that allows ... - [ ] I have tested this on a device/simulator for each compatible OS - [ ] I updated the documentation with running `yarn generate` in the root folder - [ ] I mentioned this change in `CHANGELOG.md` -- [ ] I updated the typings files (`index.d.ts`) +- [ ] I updated the typings files (`definitions.d.ts`) - [ ] I added/ updated a sample (`/example`) ## Screenshot OR Video diff --git a/.gitignore b/.gitignore index 74642f657..0e1097636 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,10 @@ package-lock.json yarn.lock /example/yarn.lock +# Compiled +lib +*.tgz + # Expo .expo diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26d623d24..5a85b11fd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,30 +1,27 @@ # Contributing -## Setup for creating pull requests +## Creating pull requests + - Fork this project - In your fork, create a branch, for example: `fix/camera-update` - Add your changes - Push and open a PR with your branch -## Testing my changes -The metro bundler under `/example` is set up to use the libraries files under root. -Which means, when you change something within `javascript/components/UserLocation.js` -it will be reflected in any scene in example that uses that component. +## Testing changes + +The metro bundler under `/example` is set up to use the libraries files under root. Which means, when you change something within `javascript/components/UserLocation.js`, it will be reflected in any scene in `/example` that uses that component. + +The library is a combination of Typescript and Javascript, which gets compiled into `/lib` for consumption by the `/example` project. This should work fine, but if you have any difficulty with the example trying to read the files in `/javascript` (outside of `/lib`), as a last resort you can run `npm pack` in the root, and then in `/example`, run `npm install ../rnmapbox-maps-.tgz`. ## Best practices for PR's + - If you add a feature, make sure you add it to the documentation -- If you add an objective-c or java method, make sure you update the declaration file: `index.d.ts`. +- If you add an objective-c or java method, make sure you update the declaration file: `definitions.d.ts`. - Make sure to use small concise commits - Use meaningful commit messages - Make sure to update/ add new tests for your changes - If you add a new feature make sure to add a scene in `/example` for others to see/ test it ## Documentation -Documentation is generated from code blocks and comments. -It will be auto-generated when you commit changes. -If any changes are generated from your edits, the changed files will need to be added using `git add` before attempting the commit again. -To manually generate the changes, run `npm run generate`. - -Notice, that changing the documentation in the individual .md within `/docs` will not suffice. -The correct way is the above described - + +Documentation is generated from code blocks and comments. It will be auto-generated when you commit changes. If any changes are generated from your edits, the changed files will need to be added using `git add` before attempting the commit again. To manually generate the changes, run `npm run generate`. \ No newline at end of file diff --git a/__tests__/components/Camera.test.js b/__tests__/components/Camera.test.js index 720302bd8..00bb408aa 100644 --- a/__tests__/components/Camera.test.js +++ b/__tests__/components/Camera.test.js @@ -3,1145 +3,70 @@ import { render } from '@testing-library/react-native'; import Camera from '../../javascript/components/Camera'; -const cameraWithoutFollowDefault = { - ...Camera.defaultProps, - animationDuration: 2000, - animationMode: 'easeTo', - centerCoordinate: [-111.8678, 40.2866], - zoomLevel: 16, - followUserLocation: false, - followUserMode: 'normal', - isUserInteraction: false, -}; +const coordinate1 = [-111.8678, 40.2866]; +const coordinate2 = [-110.8678, 37.2866]; -const cameraWithoutFollowChanged = { - ...Camera.defaultProps, - animationDuration: 1000, - animationMode: 'easeTo', - centerCoordinate: [-110.8678, 37.2866], - zoomLevel: 13, - followUserLocation: false, - followUserMode: 'normal', - isUserInteraction: false, +const bounds1 = { + ne: [-74.12641, 40.797968], + sw: [-74.143727, 40.772177], }; -const cameraWithFollowCourse = { - ...Camera.defaultProps, - animationDuration: 2000, - animationMode: 'easeTo', - defaultSettings: { - centerCoordinate: [-111.8678, 40.2866], - zoomLevel: 16, - }, - followUserLocation: true, - followUserMode: 'course', - isUserInteraction: false, +const paddingZero = { + paddingTop: 0, + paddingRight: 0, + paddingBottom: 0, + paddingLeft: 0, }; -const cameraWithBounds = { - ...Camera.defaultProps, - animationDuration: 2000, - animationMode: 'easeTo', - bounds: { - ne: [-74.12641, 40.797968], - sw: [-74.143727, 40.772177], - }, - isUserInteraction: false, - maxZoomLevel: 19, +const toFeature = (position) => { + return { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: position, + }, + properties: {}, + }; }; +const toFeatureCollection = (bounds) => { + return { + type: 'FeatureCollection', + features: [toFeature(bounds.ne), toFeature(bounds.sw)], + }; +}; describe('Camera', () => { - describe('render', () => { - test('renders correctly', () => { - const { getByTestId } = render(); - - expect(getByTestId('Camera')).toBeDefined(); - }); - - test('has proper default props', () => { - const { getByTestId } = render(); - - expect(getByTestId('Camera').props).toStrictEqual({ - children: undefined, - testID: 'Camera', - followUserLocation: undefined, - followUserMode: undefined, - followPitch: undefined, - followHeading: undefined, - followZoomLevel: undefined, - stop: { - mode: 'Ease', - pitch: undefined, - heading: undefined, - duration: 2000, - zoom: undefined, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - }, - maxZoomLevel: undefined, - minZoomLevel: undefined, - maxBounds: null, - defaultStop: null, - onUserTrackingModeChange: undefined, - }); + test('defaults are set', () => { + const result = render(); + const { props } = result.queryByTestId('Camera'); + expect(props.stop).toStrictEqual({ + ...paddingZero, }); }); - - describe('class', () => { - test('correct "UserTrackingModes" statics', () => { - expect(Camera.UserTrackingModes).toStrictEqual({ - Follow: 'normal', - FollowWithCourse: 'course', - FollowWithHeading: 'compass', - }); + test('set location by center', () => { + const result = render( + , + ); + const { props } = result.queryByTestId('Camera'); + props.stop.centerCoordinate = JSON.parse(props.stop.centerCoordinate); + expect(props.stop).toStrictEqual({ + centerCoordinate: toFeature(coordinate1), + zoom: 14, + ...paddingZero, }); }); - - describe('methods', () => { - describe('#_handleCameraChange', () => { - let camera; - - beforeEach(() => { - camera = new Camera(); - - // set up fake ref - camera.refs = { - camera: { - setNativeProps: jest.fn(), - }, - }; - - // set up fake props - // we only do this, because we want to test the class methods! - camera.props = {}; - - jest.spyOn(camera, '_setCamera'); - jest.spyOn(camera, '_hasCameraChanged'); - jest.spyOn(camera, '_hasBoundsChanged'); - }); - - test('does not call "#_setCamera" or "#setNativeProps" when "nextCamera" has no changes to "currentCamera"', () => { - camera._handleCameraChange( - cameraWithoutFollowDefault, - cameraWithoutFollowDefault, - ); - - expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._setCamera).not.toHaveBeenCalled(); - expect(camera.refs.camera.setNativeProps).not.toHaveBeenCalled(); - }); - - test('sets "followUserLocation" to false when it was removed on "nextCamera"', () => { - camera._handleCameraChange( - cameraWithFollowCourse, - cameraWithoutFollowDefault, - ); - - expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._setCamera).not.toHaveBeenCalled(); - - expect(camera.refs.camera.setNativeProps).toHaveBeenCalledTimes(1); - - expect(camera.refs.camera.setNativeProps).toHaveBeenCalledWith({ - followUserLocation: false, - }); - }); - - test('sets "followUserLocation" to true when it was added on "nextCamera"', () => { - camera._handleCameraChange( - cameraWithoutFollowDefault, - cameraWithFollowCourse, - ); - - expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._setCamera).not.toHaveBeenCalled(); - - expect(camera.refs.camera.setNativeProps).toHaveBeenCalledTimes(2); - - expect(camera.refs.camera.setNativeProps).toHaveBeenNthCalledWith(1, { - followUserLocation: true, - }); - expect(camera.refs.camera.setNativeProps).toHaveBeenNthCalledWith(2, { - followHeading: undefined, - followPitch: undefined, - followUserMode: 'course', - followZoomLevel: undefined, - }); - }); - - test('calls "#_setCamera" when "nextCamera" "hasChanged" without bounds', () => { - camera._handleCameraChange( - cameraWithoutFollowDefault, - cameraWithoutFollowChanged, - ); - - expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._hasBoundsChanged).toHaveBeenCalled(); - expect(camera._setCamera).toHaveBeenCalledWith({ - animationDuration: 1000, - animationMode: 'easeTo', - centerCoordinate: [-110.8678, 37.2866], - heading: undefined, - pitch: undefined, - zoomLevel: 13, - }); - }); - - test('calls "#_hasBoundsChanged" & "#_setCamera" when "nextCamera" "hasChanged" with bounds', () => { - camera._handleCameraChange( - cameraWithoutFollowDefault, - cameraWithBounds, - ); - - expect(camera._hasCameraChanged).toHaveBeenCalled(); - expect(camera._hasBoundsChanged).toHaveBeenCalledTimes(2); - expect(camera._setCamera).toHaveBeenCalledWith({ - animationDuration: 2000, - animationMode: 'easeTo', - bounds: { ne: [-74.12641, 40.797968], sw: [-74.143727, 40.772177] }, - heading: undefined, - pitch: undefined, - zoomLevel: undefined, - }); - }); - }); - - describe('#_hasCameraChanged', () => { - let camera; - - beforeEach(() => { - camera = new Camera(); - - // set up fake ref - camera.refs = { - camera: { - setNativeProps: jest.fn(), - }, - }; - - // set up fake props - // we only do this, because we want to test the class methods! - camera.props = {}; - - jest.spyOn(camera, '_hasCenterCoordinateChanged'); - jest.spyOn(camera, '_hasBoundsChanged'); - }); - - test('returns true if "hasDefaultPropsChanged"', () => { - const testCases = [ - [{ heading: 120 }, { heading: 121 }], - [ - { - centerCoordinate: [-111.8678, 40.2866], - }, - { - centerCoordinate: [-111.8678, 38.2866], - }, - ], - [ - { - bounds: { - ne: [-74.12641, 40.797968], - sw: [-74.143727, 40.772177], - }, - }, - { - bounds: { - ne: [-64.12641, 40.797968], - sw: [-74.143727, 40.772177], - }, - }, - ], - [ - { - pitch: 45, - }, - { - pitch: 55, - }, - ], - [ - { - zoomLevel: 10, - }, - { - zoomLevel: 15, - }, - ], - [ - // using the usecase in /example - { - triggerKey: 1582486618640, // Date.now() - }, - { - triggerKey: 1582486626818, // Date.now() - }, - ], - ]; - - testCases.forEach((c) => { - expect(camera._hasCameraChanged(c[0], c[1])).toBe(true); - }); - }); - - test('returns true if "hasFollowPropsChanged"', () => { - const testCases = [ - [{ followUserLocation: false }, { followUserLocation: true }], - [{ followUserMode: 'normal' }, { followUserMode: 'course' }], - [{ followZoomLevel: 10 }, { followZoomLevel: 13 }], - [{ followHeading: 100 }, { followHeading: 110 }], - [{ followPitch: 40 }, { followPitch: 49 }], - ]; - - testCases.forEach((c) => { - expect(camera._hasCameraChanged(c[0], c[1])).toBe(true); - }); - }); - - test('returns true if "hasAnimationPropsChanged"', () => { - const testCases = [ - [{ animationDuration: 3000 }, { animationDuration: 1000 }], - [{ animationMode: 'flyTo' }, { animationMode: 'easeTo' }], - ]; - - testCases.forEach((c) => { - expect(camera._hasCameraChanged(c[0], c[1])).toBe(true); - }); - }); - }); - - describe('#_hasCenterCoordinateChanged', () => { - const camera = new Camera(); - - test('returns false when centerCoordinates are missing', () => { - expect(camera._hasCenterCoordinateChanged({}, {})).toBe(false); - }); - - test('returns false when centerCoordinates have not changed', () => { - expect( - camera._hasCenterCoordinateChanged( - [-111.8678, 40.2866], - [-111.8678, 40.2866], - ), - ).toBe(false); - }); - - test('returns true when centerCoordinates have changed', () => { - expect( - camera._hasCenterCoordinateChanged([-111.8678, 40.2866], undefined), - ).toBe(true); - - expect( - camera._hasCenterCoordinateChanged(undefined, [-111.8678, 40.2866]), - ).toBe(true); - - // isLngDiff - expect( - camera._hasCenterCoordinateChanged( - [-111.2678, 40.2866], - [-111.8678, 40.2866], - ), - ).toBe(true); - - // isLatDiff - expect( - camera._hasCenterCoordinateChanged( - [-111.2678, 40.2866], - [-111.8678, 33.2866], - ), - ).toBe(true); - }); - }); - - describe('#_hasBoundsChanged', () => { - const camera = new Camera(); - const bounds = { - ne: [-74.12641, 40.797968], - sw: [-74.143727, 40.772177], - paddingTop: 5, - paddingLeft: 5, - paddingRight: 5, - paddingBottom: 5, - }; - - test('returns false when bounds are missing', () => { - expect(camera._hasBoundsChanged(undefined, undefined)).toBe(false); - }); - - test('returns false when bounds have not changed', () => { - expect(camera._hasBoundsChanged(bounds, bounds)).toBe(false); - }); - - test('returns true when bound props have changed', () => { - // ne[0] - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - ne: [-34.12641, 40.797968], - }), - ).toBe(true); - - // ne[1] - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - ne: [-74.12641, 30.797968], - }), - ).toBe(true); - - // sw[0] - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - sw: [-74.143723, 40.772177], - }), - ).toBe(true); - - // sw[1] - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - sw: [-74.143727, 40.772137], - }), - ).toBe(true); - - // paddingTop - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - paddingTop: 3, - }), - ).toBe(true); - - // paddingLeft - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - paddingLeft: 3, - }), - ).toBe(true); - - // paddingRight - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - paddingRight: 3, - }), - ).toBe(true); - - // paddingBottom - expect( - camera._hasBoundsChanged(bounds, { - ...bounds, - paddingBottom: 3, - }), - ).toBe(true); - }); - - describe('does work with maxBounds', () => { - const currentMaxBounds = { - ne: [-74.12641, 40.797968], - sw: [-74.143727, 40.772177], - }; - - const nextMaxBounds = { - ne: [-83.12641, 42.797968], - sw: [-64.143727, 35.772177], - }; - - test('returns true if changed', () => { - expect( - camera._hasBoundsChanged(currentMaxBounds, nextMaxBounds), - ).toBe(true); - }); - - test('returns false if unchanged', () => { - expect( - camera._hasBoundsChanged(currentMaxBounds, currentMaxBounds), - ).toBe(false); - }); - - test('returns false if both undefined', () => { - expect(camera._hasBoundsChanged(undefined, undefined)).toBe(false); - }); - - test('does work with currentBounds being undefined', () => { - expect(camera._hasBoundsChanged(undefined, nextMaxBounds)).toBe(true); - }); - - test('does work with nextBounds being undefined', () => { - expect(camera._hasBoundsChanged(currentMaxBounds, undefined)).toBe( - true, - ); - }); - }); - }); - - describe('#fitBounds', () => { - const camera = new Camera(); - const ne = [-63.12641, 39.797968]; - const sw = [-74.143727, 40.772177]; - - beforeEach(() => { - camera.setCamera = jest.fn(); - }); - - test('works without provided "padding" and/ or "animationDuration"', () => { - // FIXME: animationDuration and padding of null lead to malformed setCamera config - - const expectedCallResults = [ - { - animationDuration: null, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - sw: [-74.143727, 40.772177], - }, - padding: { - paddingBottom: null, - paddingLeft: null, - paddingRight: null, - paddingTop: null, - }, - }, - { - animationDuration: 0, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - sw: [-74.143727, 40.772177], - }, - padding: { - paddingBottom: null, - paddingLeft: null, - paddingRight: null, - paddingTop: null, - }, - }, - { - animationDuration: 0, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - sw: [-74.143727, 40.772177], - }, - padding: { - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - }, - }, - ]; - - camera.fitBounds(ne, sw, null, null); - camera.fitBounds(ne, sw, null); - camera.fitBounds(ne, sw); - - camera.setCamera.mock.calls.forEach((call, i) => { - expect(call[0]).toStrictEqual(expectedCallResults[i]); - }); - }); - - // TODO: Refactor #fitBounds to throw when ne or sw aren't provided - // This is a public method and people will call it with all sorts of data - test.skip('throws when "ne" or "sw" are missing', () => {}); - - test('works with "padding" being a single number', () => { - const expectedCallResult = { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - sw: [-74.143727, 40.772177], - }, - padding: { - paddingBottom: 3, - paddingLeft: 3, - paddingRight: 3, - paddingTop: 3, - }, - }; - - camera.fitBounds(ne, sw, 3, 500); - expect(camera.setCamera).toHaveBeenCalledWith(expectedCallResult); - }); - - test('works with "padding" being an array of two numbers', () => { - const expectedCallResult = { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - sw: [-74.143727, 40.772177], - }, - padding: { - paddingBottom: 3, - paddingLeft: 5, - paddingRight: 5, - paddingTop: 3, - }, - }; - - camera.fitBounds(ne, sw, [3, 5], 500); - expect(camera.setCamera).toHaveBeenCalledWith(expectedCallResult); - }); - - test('works with "padding" being an array of four numbers', () => { - const expectedCallResult = { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - sw: [-74.143727, 40.772177], - }, - padding: { - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - }, - }; - - camera.fitBounds(ne, sw, [3, 5, 8, 10], 500); - expect(camera.setCamera).toHaveBeenCalledWith(expectedCallResult); - }); - }); - - describe('#flyTo', () => { - const camera = new Camera(); - - beforeEach(() => { - camera.setCamera = jest.fn(); - }); - - test.skip('throws when no coordinates are provided', () => { - // TODO: Refactor #flyTo to throw when coordinates aren't provided - // This is a public method and people will call it with all sorts of data - }); - - test('sets default "animationDuration" when called without it', () => { - camera.flyTo([-111.8678, 40.2866]); - expect(camera.setCamera).toHaveBeenCalledWith({ - animationDuration: 2000, - animationMode: 'flyTo', - centerCoordinate: [-111.8678, 40.2866], - }); - }); - - test('calls "setCamera" with correct config', () => { - camera.flyTo([-111.8678, 40.2866], 5000); - expect(camera.setCamera).toHaveBeenCalledWith({ - animationDuration: 5000, - animationMode: 'flyTo', - centerCoordinate: [-111.8678, 40.2866], - }); - }); - }); - - describe('#moveTo', () => { - const camera = new Camera(); - - beforeEach(() => { - // FIXME: Why is moveTo calling #_setCamera instead of #setCamera? - // let's be consistent here - have all methods use one of both - camera._setCamera = jest.fn(); - }); - - test.skip('throws when no coordinates are provided', () => { - // TODO: Refactor #moveTo to throw when coordinates aren't provided - // This is a public method and people will call it with all sorts of data - }); - - test('sets default "animationDuration" when called without it', () => { - camera.moveTo([-111.8678, 40.2866]); - expect(camera._setCamera).toHaveBeenCalledWith({ - animationDuration: 0, - centerCoordinate: [-111.8678, 40.2866], - }); - }); - - test('calls "_setCamera" with correct config', () => { - camera.moveTo([-111.8678, 40.2866], 5000); - expect(camera._setCamera).toHaveBeenCalledWith({ - animationDuration: 5000, - centerCoordinate: [-111.8678, 40.2866], - }); - }); - }); - - describe('#zoomTo', () => { - const camera = new Camera(); - - beforeEach(() => { - camera._setCamera = jest.fn(); - }); - - test.skip('throws when no zoomLevel is provided', () => { - // TODO: Refactor #moveTo to throw when coordinates aren't provided - // This is a public method and people will call it with all sorts of data - }); - - test('sets default "animationDuration" when called without it', () => { - camera.zoomTo(10); - expect(camera._setCamera).toHaveBeenCalledWith({ - animationDuration: 2000, - zoomLevel: 10, - animationMode: 'flyTo', - }); - }); - - test('calls "_setCamera" with correct config', () => { - camera.zoomTo(10, 3000); - expect(camera._setCamera).toHaveBeenCalledWith({ - animationDuration: 3000, - zoomLevel: 10, - animationMode: 'flyTo', - }); - }); - }); - - describe('#setCamera', () => { - const camera = new Camera(); - - beforeEach(() => { - camera._setCamera = jest.fn(); - }); - - test('sets default empty "config" when called without one', () => { - camera.setCamera(); - expect(camera._setCamera).toHaveBeenCalledWith({}); - }); - - test('calls "_setCamera" with passed config', () => { - const config = { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-74.143727, 40.772177], - }, - }; - - camera.setCamera(config); - expect(camera._setCamera).toHaveBeenCalledWith(config); - }); - }); - - describe('#_setCamera', () => { - const camera = new Camera(); - - beforeEach(() => { - jest.spyOn(Camera.prototype, '_createStopConfig'); - - // set up fake ref - camera.refs = { - camera: { - setNativeProps: jest.fn(), - }, - }; - - // set up fake props - // we only do this, because we want to test the class methods! - camera.props = {}; - - jest.clearAllMocks(); - }); - - test('calls "_createStopConfig" and passes stopConfig to "setNativeProps"', () => { - const config = { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-74.143727, 40.772177], - }, - heading: 100, - pitch: 45, - zoomLevel: 11, - }; - - camera._setCamera(config); - - expect(camera._createStopConfig).toHaveBeenCalledWith({ - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-74.143727, 40.772177], - }, - heading: 100, - pitch: 45, - zoomLevel: 11, - }); - - expect(camera._createStopConfig).toHaveBeenCalledTimes(1); - - expect(camera.refs.camera.setNativeProps).toHaveBeenCalledWith({ - stop: { - bounds: - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - duration: 500, - heading: 100, - mode: 'Ease', - pitch: 45, - zoom: 11, - }, - }); - }); - - test('creates multiple stops when provided', () => { - const config = { - stops: [ - { - animationDuration: 50, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 2, - paddingLeft: 2, - paddingRight: 2, - paddingTop: 2, - sw: [-74.143727, 40.772177], - }, - heading: 20, - pitch: 25, - zoomLevel: 16, - }, - { - animationDuration: 3000, - animationMode: 'flyTo', - bounds: { - ne: [-63.12641, 59.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-71.143727, 40.772177], - }, - heading: 40, - pitch: 45, - zoomLevel: 8, - }, - { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-74.143727, 40.772177], - }, - heading: 100, - pitch: 45, - zoomLevel: 11, - }, - ], - }; - - camera._setCamera(config); - - expect(camera._createStopConfig).toHaveBeenCalledTimes(3); - - expect(camera._createStopConfig).toHaveBeenCalledWith({ - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-74.143727, 40.772177], - }, - heading: 100, - pitch: 45, - zoomLevel: 11, - }); - - expect(camera.refs.camera.setNativeProps).toHaveBeenCalledWith({ - stop: { - stops: [ - { - bounds: - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - paddingBottom: 2, - paddingLeft: 2, - paddingRight: 2, - paddingTop: 2, - duration: 50, - heading: 20, - mode: 'Ease', - pitch: 25, - zoom: 16, - }, - { - bounds: - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,59.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-71.143727,40.772177]}}]}', - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - duration: 3000, - heading: 40, - mode: 'Flight', - pitch: 45, - zoom: 8, - }, - { - bounds: - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - duration: 500, - heading: 100, - mode: 'Ease', - pitch: 45, - zoom: 11, - }, - ], - }, - }); - }); - }); - - describe('#_createDefaultCamera', () => { - const camera = new Camera(); - - beforeEach(() => {}); - - test('returns null without "defaultSettings"', () => { - camera.props = {}; - expect(camera._createDefaultCamera()).toBe(null); - }); - - test('returns "defaultCamera" with "defaultSettings" and sets property', () => { - camera.props = { - defaultSettings: { - centerCoordinate: [-111.8678, 40.2866], - zoomLevel: 16, - animationMode: 'moveTo', - }, - }; - - const defaultCamera = { - centerCoordinate: - '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-111.8678,40.2866]}}', - duration: 0, - heading: undefined, - mode: 'Move', - pitch: undefined, - zoom: 16, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - }; - - expect(camera.defaultCamera).toStrictEqual(undefined); - expect(camera._createDefaultCamera()).toStrictEqual(defaultCamera); - expect(camera.defaultCamera).toStrictEqual(defaultCamera); - }); - }); - - describe('#_createStopConfig', () => { - const camera = new Camera(); - const configWithoutBounds = { - animationDuration: 2000, - animationMode: 'easeTo', - pitch: 45, - heading: 110, - zoomLevel: 9, - }; - - const configWithBounds = { - animationDuration: 500, - animationMode: 'easeTo', - bounds: { - ne: [-63.12641, 39.797968], - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - sw: [-74.143727, 40.772177], - }, - heading: 100, - pitch: 45, - zoomLevel: 11, - }; - - beforeEach(() => { - jest.spyOn(Camera.prototype, '_getNativeCameraMode'); - - jest.clearAllMocks(); - }); - - test('returns null with "followUserLocation" prop and "!ignoreFollowUserLocation"', () => { - camera.props = { - followUserLocation: true, - }; - expect(camera._createStopConfig()).toBe(null); - }); - - test('returns correct "stopConfig" without bounds', () => { - camera.props = { - followUserLocation: true, - }; - - expect( - camera._createStopConfig(configWithoutBounds, true), - ).toStrictEqual({ - duration: 2000, - heading: 110, - mode: 'Ease', - pitch: 45, - zoom: 9, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - }); - - // with centerCoordinate - expect( - camera._createStopConfig( - { ...configWithoutBounds, centerCoordinate: [-111.8678, 40.2866] }, - true, - ), - ).toStrictEqual({ - centerCoordinate: - '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-111.8678,40.2866]}}', - duration: 2000, - heading: 110, - mode: 'Ease', - pitch: 45, - zoom: 9, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - paddingTop: 0, - }); - }); - - test('returns correct "stopConfig" with bounds', () => { - camera.props = { - followUserLocation: true, - }; - - expect(camera._createStopConfig(configWithBounds, true)).toStrictEqual({ - bounds: - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - duration: 500, - heading: 100, - mode: 'Ease', - pitch: 45, - zoom: 11, - }); - - // with centerCoordinate - expect( - camera._createStopConfig( - { ...configWithBounds, centerCoordinate: [-111.8678, 40.2866] }, - true, - ), - ).toStrictEqual({ - bounds: - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-63.12641,39.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - paddingBottom: 8, - paddingLeft: 10, - paddingRight: 5, - paddingTop: 3, - centerCoordinate: - '{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-111.8678,40.2866]}}', - duration: 500, - heading: 100, - mode: 'Ease', - pitch: 45, - zoom: 11, - }); - }); - }); - - describe('#_getNativeCameraMode', () => { - const camera = new Camera(); - - test('returns "Flight" for "flyTo"', () => { - expect( - camera._getNativeCameraMode({ animationMode: 'flyTo' }), - ).toStrictEqual('Flight'); - }); - - test('returns "None" for "moveTo"', () => { - expect( - camera._getNativeCameraMode({ animationMode: 'moveTo' }), - ).toStrictEqual('Move'); - }); - - test('returns "Ease" as default', () => { - expect(camera._getNativeCameraMode({})).toStrictEqual('Ease'); - }); - }); - - describe('#_getMaxBounds', () => { - const camera = new Camera(); - - test('returns null if no "maxBounds"', () => { - camera.props = {}; - expect(camera._getMaxBounds()).toStrictEqual(null); - - camera.props = { - maxBounds: { - ne: [-74.12641, 40.797968], - }, - }; - expect(camera._getMaxBounds()).toStrictEqual(null); - - camera.props = { - maxBounds: { - sw: [-74.143727, 40.772177], - }, - }; - expect(camera._getMaxBounds()).toStrictEqual(null); - }); - - test('returns maxBounds when "maxBounds" property is set', () => { - camera.props = { - maxBounds: { - ne: [-74.12641, 40.797968], - sw: [-74.143727, 40.772177], - }, - }; - - expect(camera._getMaxBounds()).toStrictEqual( - '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.12641,40.797968]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-74.143727,40.772177]}}]}', - ); - }); + test('set location by bounds', () => { + const result = render(); + const { props } = result.queryByTestId('Camera'); + props.stop.bounds = JSON.parse(props.stop.bounds); + expect(props.stop).toStrictEqual({ + bounds: toFeatureCollection(bounds1), + ...paddingZero, }); }); + test('animation mode', () => { + const result = render(); + const { props } = result.queryByTestId('Camera'); + expect(props.stop.mode).toEqual('Move'); + }); }); diff --git a/__tests__/interface.test.js b/__tests__/interface.test.js index 66e87946b..c3966557e 100644 --- a/__tests__/interface.test.js +++ b/__tests__/interface.test.js @@ -1,4 +1,4 @@ -import MapboxGL from '../javascript'; +import MapboxGL from '..'; describe('Public Interface', () => { it('should contain all expected components and utils', () => { @@ -20,6 +20,9 @@ describe('Public Interface', () => { 'snapshotManager', 'locationManager', + // utils + 'geoUtils', + // layers 'FillLayer', 'FillExtrusionLayer', diff --git a/__tests__/modules/offline/offlineManager.test.js b/__tests__/modules/offline/offlineManager.test.js index 2a2e14add..d27520583 100644 --- a/__tests__/modules/offline/offlineManager.test.js +++ b/__tests__/modules/offline/offlineManager.test.js @@ -1,6 +1,6 @@ import { NativeModules, Platform } from 'react-native'; -import MapboxGL from '../../../javascript'; +import MapboxGL from '../../..'; import { OfflineModuleEventEmitter } from '../../../javascript/modules/offline/offlineManager'; describe('offlineManager', () => { diff --git a/__tests__/modules/snapshot/snapshotManager.test.js b/__tests__/modules/snapshot/snapshotManager.test.js index 320015d05..68c41f464 100644 --- a/__tests__/modules/snapshot/snapshotManager.test.js +++ b/__tests__/modules/snapshot/snapshotManager.test.js @@ -1,4 +1,4 @@ -import MapboxGL from '../../../javascript'; +import MapboxGL from '../../..'; describe('snapshotManager', () => { it('should resolve uri', async () => { diff --git a/docs/Annotation.md b/docs/Annotation.md index 1ec01e548..26141152f 100644 --- a/docs/Annotation.md +++ b/docs/Annotation.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/BackgroundLayer.md b/docs/BackgroundLayer.md index 016214525..fda192eec 100644 --- a/docs/BackgroundLayer.md +++ b/docs/BackgroundLayer.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/Callout.md b/docs/Callout.md index 7eb28e42a..d025d595a 100644 --- a/docs/Callout.md +++ b/docs/Callout.md @@ -1,4 +1,4 @@ - + ## ### Callout that displays information about a selected annotation near the annotation. diff --git a/docs/Camera.md b/docs/Camera.md index c52100eb8..8e45af81c 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -1,161 +1,122 @@ - + ## -### +### Controls the perspective from which the user sees the map.

To use imperative methods, pass in a ref object:

const camera = useRef(null);

useEffect(() => {
camera.current?.setCamera({
centerCoordinate: [lon, lat],
});
}, []);

return (
\
);
### props | Prop | Type | Default | Required | Description | | ---- | :--: | :-----: | :------: | :----------: | -| allowUpdates | `bool` | `true` | `false` | If false, the camera will not send any props to the native module. Intended to be used to prevent unnecessary tile fetching and improve performance when the map is not visible. Defaults to true. | -| animationDuration | `number` | `2000` | `false` | The duration a camera update takes (in ms) | -| animationMode | `enum` | `'easeTo'` | `false` | The animation style when the camara updates. One of:
`flyTo`: A complex flight animation, affecting both position and zoom.
`easeTo`: A standard damped curve.
`linearTo`: An even linear transition.
`none`: An instantaneous change (v10 only).
`moveTo`: An instantaneous change (The `bounds.padding*` properties are deprecated; use root `padding` property instead. | -|     ne | `array` | `none` | `true` | North east coordinate of bound | -|     sw | `array` | `none` | `true` | South west coordinate of bound | -|     paddingLeft | `number` | `none` | `false` | Left padding in points (deprecated; use root `padding` property instead) | -|     paddingRight | `number` | `none` | `false` | Right padding in points (deprecated; use root `padding` property instead) | -|     paddingTop | `number` | `none` | `false` | Top padding in points (deprecated; use root `padding` property instead) | -|     paddingBottom | `number` | `none` | `false` | Bottom padding in points (deprecated; use root `padding` property instead) | -|   onUserTrackingModeChange | `func` | `none` | `false` | Callback that is triggered on user tracking mode changes | -|   zoomLevel | `number` | `none` | `false` | Zoom level of the map | -| centerCoordinate | `array` | `none` | `false` | Center coordinate on map [lng, lat] | -| padding | `shape` | `none` | `false` | Padding around edges of map in points | -|   paddingLeft | `number` | `none` | `false` | Left padding in points | -|   paddingRight | `number` | `none` | `false` | Right padding in points | -|   paddingTop | `number` | `none` | `false` | Top padding in points | -|   paddingBottom | `number` | `none` | `false` | Bottom padding in points | -| heading | `number` | `none` | `false` | Heading on map | -| pitch | `number` | `none` | `false` | Pitch on map | -| bounds | `shape` | `none` | `false` | Represents a rectangle in geographical coordinates marking the visible area of the map.
The `bounds.padding*` properties are deprecated; use root `padding` property instead. | -|   ne | `array` | `none` | `true` | North east coordinate of bound | -|   sw | `array` | `none` | `true` | South west coordinate of bound | -|   paddingLeft | `number` | `none` | `false` | Left padding in points (deprecated; use root `padding` property instead) | -|   paddingRight | `number` | `none` | `false` | Right padding in points (deprecated; use root `padding` property instead) | -|   paddingTop | `number` | `none` | `false` | Top padding in points (deprecated; use root `padding` property instead) | -|   paddingBottom | `number` | `none` | `false` | Bottom padding in points (deprecated; use root `padding` property instead) | -| onUserTrackingModeChange | `func` | `none` | `false` | Callback that is triggered on user tracking mode changes | -| zoomLevel | `number` | `none` | `false` | Zoom level of the map | -| minZoomLevel | `number` | `none` | `false` | The minimum zoom level of the map | -| maxZoomLevel | `number` | `none` | `false` | The maximum zoom level of the map | -| maxBounds | `shape` | `none` | `false` | Restrict map panning so that the center is within these bounds | -|   ne | `array` | `none` | `true` | northEastCoordinates - North east coordinate of bound | -|   sw | `array` | `none` | `true` | southWestCoordinates - South west coordinate of bound | -| followUserLocation | `bool` | `none` | `false` | Should the map orientation follow the user's. | -| followUserMode | `enum` | `none` | `false` | The mode used to track the user location on the map. One of; "normal", "compass", "course". Each mode string is also available as a member on the `MapboxGL.UserTrackingModes` object. `Follow` (normal), `FollowWithHeading` (compass), `FollowWithCourse` (course). NOTE: `followUserLocation` must be set to `true` for any of the modes to take effect. [Example](../example/src/examples/SetUserTrackingModes.js) | -| followZoomLevel | `number` | `none` | `false` | The zoomLevel on map while followUserLocation is set to `true` | -| followPitch | `number` | `none` | `false` | The pitch on map while followUserLocation is set to `true` | -| followHeading | `number` | `none` | `false` | The heading on map while followUserLocation is set to `true` | -| triggerKey | `any` | `none` | `false` | Manually update the camera - helpful for when props did not update, however you still want the camera to move | +| type | `literal` | `none` | `false` | Allows static check of the data type. For internal use only. | +| centerCoordinate | `Position` | `none` | `false` | The location on which the map should center. | +| bounds | `CameraBoundsWithPadding` | `none` | `false` | The corners of a box around which the map should bound. Contains padding props for backwards
compatibility; the root `padding` prop should be used instead. | +| heading | `number` | `none` | `false` | The heading (orientation) of the map. | +| pitch | `number` | `none` | `false` | The pitch of the map. | +| zoomLevel | `number` | `none` | `false` | The zoom level of the map. | +| padding | `CameraPadding` | `none` | `false` | The viewport padding in points. | +| animationDuration | `number` | `none` | `false` | The duration the map takes to animate to a new configuration. | +| animationMode | `CameraAnimationMode` | `none` | `false` | The easing or path the camera uses to animate to a new configuration. | +| followUserMode | `UserTrackingMode` | `none` | `false` | The mode used to track the user location on the map. | +| followUserLocation | `boolean` | `none` | `false` | Whether the map orientation follows the user location. | +| followZoomLevel | `number` | `none` | `false` | The zoom level used when following the user location. | +| followPitch | `number` | `none` | `false` | The pitch used when following the user location. | +| followHeading | `number` | `none` | `false` | The heading used when following the user location. | +| minZoomLevel | `number` | `none` | `false` | The lowest allowed zoom level. | +| maxZoomLevel | `number` | `none` | `false` | The highest allowed zoom level. | +| maxBounds | `{ne:Position;sw:Position;}` | `none` | `false` | The corners of a box defining the limits of where the camera can pan or zoom. | +| defaultSettings | `CameraStop` | `none` | `false` | The configuration that the camera falls back on, if no other values are specified. | +| allowUpdates | `boolean` | `none` | `false` | Whether the camera should send any configuration to the native module. Prevents unnecessary tile
fetching and improves performance when the map is not visible. Defaults to `true`. | +| triggerKey | `string \| number` | `none` | `false` | Any arbitrary primitive value that, when changed, causes the camera to retry moving to its target
configuration. (Not yet implemented.) | +| onUserTrackingModeChange | `UserTrackingModeChangeCallback` | `none` | `false` | Executes when user tracking mode changes. | ### methods -#### fitBounds(northEastCoordinates, southWestCoordinates[, padding][, animationDuration]) +#### setCamera(config) -Map camera transitions to fit provided bounds +Sets any camera properties, with default fallbacks if unspecified. ##### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `northEastCoordinates` | `Array` | `Yes` | North east coordinate of bound | -| `southWestCoordinates` | `Array` | `Yes` | South west coordinate of bound | -| `padding` | `Number` | `No` | Camera padding for bound | -| `animationDuration` | `Number` | `No` | Duration of camera animation | +| `config` | `CameraStop \| CameraStops` | `Yes` | null | ```javascript -this.camera.fitBounds([lng, lat], [lng, lat]) -this.camera.fitBounds([lng, lat], [lng, lat], 20, 1000) // padding for all sides -this.camera.fitBounds([lng, lat], [lng, lat], [verticalPadding, horizontalPadding], 1000) -this.camera.fitBounds([lng, lat], [lng, lat], [top, right, bottom, left], 1000) +camera.current?.setCamera({ + centerCoordinate: [lon, lat], +}); ``` -#### flyTo(coordinates[, animationDuration]) +#### fitBounds(ne, sw, paddingConfig, _animationDuration) -Map camera will fly to new coordinate +Set the camera position to enclose the provided bounds, with optional
padding and duration. ##### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `coordinates` | `Array` | `Yes` | Coordinates that map camera will jump too | -| `animationDuration` | `Number` | `No` | Duration of camera animation | +| `ne` | `Position` | `Yes` | Northeast coordinate of bounding box | +| `sw` | `Position` | `Yes` | Southwest coordinate of bounding box | +| `paddingConfig` | `number \| Array` | `Yes` | The viewport padding, specified as a number (all sides equal), a 2-item array ([vertical, horizontal]), or a 4-item array ([top, right, bottom, left]) | +| `_animationDuration` | `n/a` | `Yes` | undefined | ```javascript -this.camera.flyTo([lng, lat]) -this.camera.flyTo([lng, lat], 12000) +camera.fitBounds([lon, lat], [lon, lat]); +camera.fitBounds([lon, lat], [lon, lat], [20, 0], 1000); ``` -#### moveTo(coordinates[, animationDuration]) +#### flyTo(_centerCoordinate, _animationDuration) -Map camera will move to new coordinate at the same zoom level +Sets the camera to center around the provided coordinate using a realistic 'travel'
animation, with optional duration. ##### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `coordinates` | `Array` | `Yes` | Coordinates that map camera will move too | -| `animationDuration` | `Number` | `No` | Duration of camera animation | +| `_centerCoordinate` | `n/a` | `Yes` | undefined | +| `_animationDuration` | `n/a` | `Yes` | undefined | ```javascript -this.camera.moveTo([lng, lat], 200) // eases camera to new location based on duration -this.camera.moveTo([lng, lat]) // snaps camera to new location without any easing +camera.flyTo([lon, lat]); +camera.flyTo([lon, lat], 12000); ``` -#### zoomTo(zoomLevel[, animationDuration]) +#### moveTo(_centerCoordinate, _animationDuration) -Map camera will zoom to specified level +Sets the camera to center around the provided coordinate, with optional duration. ##### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `zoomLevel` | `Number` | `Yes` | Zoom level that the map camera will animate too | -| `animationDuration` | `Number` | `No` | Duration of camera animation | +| `_centerCoordinate` | `n/a` | `Yes` | undefined | +| `_animationDuration` | `n/a` | `Yes` | undefined | ```javascript -this.camera.zoomTo(16) -this.camera.zoomTo(16, 100) +camera.moveTo([lon, lat], 200); +camera.moveTo([lon, lat]); ``` -#### setCamera(config) +#### zoomTo(_zoomLevel, _animationDuration) -Map camera will perform updates based on provided config. Advanced use only! +Zooms the camera to the provided level, with optional duration. ##### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `config` | `Object` | `Yes` | Camera configuration | +| `_zoomLevel` | `n/a` | `Yes` | undefined | +| `_animationDuration` | `n/a` | `Yes` | undefined | ```javascript -this.camera.setCamera({ - centerCoordinate: [lng, lat], - zoomLevel: 16, - animationDuration: 2000, -}) - -this.camera.setCamera({ - stops: [ - { pitch: 45, animationDuration: 200 }, - { heading: 180, animationDuration: 300 }, - ] -}) +camera.zoomTo(16); +camera.zoomTo(16, 100); ``` diff --git a/docs/CircleLayer.md b/docs/CircleLayer.md index 2ee395835..3e1df7539 100644 --- a/docs/CircleLayer.md +++ b/docs/CircleLayer.md @@ -1,4 +1,4 @@ - + ## ### CircleLayer is a style layer that renders one or more filled circles on the map. diff --git a/docs/FillExtrusionLayer.md b/docs/FillExtrusionLayer.md index aaddbf5ff..60a228280 100644 --- a/docs/FillExtrusionLayer.md +++ b/docs/FillExtrusionLayer.md @@ -1,4 +1,4 @@ - + ## ### FillExtrusionLayer is a style layer that renders one or more 3D extruded polygons on the map. diff --git a/docs/FillLayer.md b/docs/FillLayer.md index b0873407c..1aea7d1f6 100644 --- a/docs/FillLayer.md +++ b/docs/FillLayer.md @@ -1,4 +1,4 @@ - + ## ### FillLayer is a style layer that renders one or more filled (and optionally stroked) polygons on the map. diff --git a/docs/HeadingIndicator.md b/docs/HeadingIndicator.md index e51321497..ca56fc9ca 100644 --- a/docs/HeadingIndicator.md +++ b/docs/HeadingIndicator.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/HeatmapLayer.md b/docs/HeatmapLayer.md index e19e9d3a0..579dd3bc8 100644 --- a/docs/HeatmapLayer.md +++ b/docs/HeatmapLayer.md @@ -1,4 +1,4 @@ - + ## ### HeatmapLayer is a style layer that renders one or more filled circles on the map. diff --git a/docs/ImageSource.md b/docs/ImageSource.md index 04bc95912..62c98f5eb 100644 --- a/docs/ImageSource.md +++ b/docs/ImageSource.md @@ -1,4 +1,4 @@ - + ## ### ImageSource is a content source that is used for a georeferenced raster image to be shown on the map.
The georeferenced image scales and rotates as the user zooms and rotates the map diff --git a/docs/Images.md b/docs/Images.md index 7bf0a4b24..bbd1eb09a 100644 --- a/docs/Images.md +++ b/docs/Images.md @@ -1,4 +1,4 @@ - + ## ### Images defines the images used in Symbol etc layers diff --git a/docs/Light.md b/docs/Light.md index ecafcae8d..47f61033c 100644 --- a/docs/Light.md +++ b/docs/Light.md @@ -1,4 +1,4 @@ - + ## ### Light represents the light source for extruded geometries diff --git a/docs/LineLayer.md b/docs/LineLayer.md index 0ca1d8a32..4ea130cbf 100644 --- a/docs/LineLayer.md +++ b/docs/LineLayer.md @@ -1,4 +1,4 @@ - + ## ### LineLayer is a style layer that renders one or more stroked polylines on the map. diff --git a/docs/MapView.md b/docs/MapView.md index a8909cb74..dd5f3d336 100644 --- a/docs/MapView.md +++ b/docs/MapView.md @@ -1,6 +1,6 @@ - + ## -### MapView backed by Mapbox Native GL +### MapView backed by Mapbox Native GL. ### props | Prop | Type | Default | Required | Description | diff --git a/docs/MarkerView.md b/docs/MarkerView.md index a20bcb603..666e2c7d0 100644 --- a/docs/MarkerView.md +++ b/docs/MarkerView.md @@ -1,4 +1,4 @@ - + ## ### MarkerView allows you to place a interactive react native marker to the map.

If you have static view consider using PointAnnotation or SymbolLayer they'll offer much better performance
.
This is based on [MakerView plugin](https://docs.mapbox.com/android/plugins/overview/markerview/) on Android
and PointAnnotation on iOS. diff --git a/docs/NativeUserLocation.md b/docs/NativeUserLocation.md index c97db255e..19556f971 100644 --- a/docs/NativeUserLocation.md +++ b/docs/NativeUserLocation.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/OfflineManager.md b/docs/OfflineManager.md index 539f7dcb3..ac3786624 100644 --- a/docs/OfflineManager.md +++ b/docs/OfflineManager.md @@ -1,4 +1,4 @@ - + ## ### OfflineManager implements a singleton (shared object) that manages offline packs.
All of this class’s instance methods are asynchronous, reflecting the fact that offline resources are stored in a database.
The shared object maintains a canonical collection of offline packs. diff --git a/docs/PointAnnotation.md b/docs/PointAnnotation.md index 713b36924..8ac5ff12f 100644 --- a/docs/PointAnnotation.md +++ b/docs/PointAnnotation.md @@ -1,6 +1,6 @@ - + ## -### PointAnnotation represents a one-dimensional shape located at a single geographical coordinate.

Consider using ShapeSource and SymbolLayer instead, if you have many points and you have static images,
they'll offer much better performance.

If you need interactive views please use MarkerView,
as with PointAnnotation on Android child views are rendered onto a bitmap for better performance. +### PointAnnotation represents a one-dimensional shape located at a single geographical coordinate.

Consider using ShapeSource and SymbolLayer instead, if you have many points and you have static images,
they'll offer much better performance.

If you need interactive views, please use MarkerView, as with PointAnnotation on Android, child views
are rendered onto a bitmap for better performance. ### props | Prop | Type | Default | Required | Description | diff --git a/docs/RasterDemSource.md b/docs/RasterDemSource.md index 43fe5fb18..e47cdaa3c 100644 --- a/docs/RasterDemSource.md +++ b/docs/RasterDemSource.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/RasterLayer.md b/docs/RasterLayer.md index c0df673f0..b3ab5d5b3 100644 --- a/docs/RasterLayer.md +++ b/docs/RasterLayer.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/RasterSource.md b/docs/RasterSource.md index ba8073ebc..309458d2a 100644 --- a/docs/RasterSource.md +++ b/docs/RasterSource.md @@ -1,4 +1,4 @@ - + ## ### RasterSource is a map content source that supplies raster image tiles to be shown on the map.
The location of and metadata about the tiles are defined either by an option dictionary
or by an external file that conforms to the TileJSON specification. diff --git a/docs/ShapeSource.md b/docs/ShapeSource.md index b0789126c..1bd02fbec 100644 --- a/docs/ShapeSource.md +++ b/docs/ShapeSource.md @@ -1,6 +1,6 @@ - + ## -### ShapeSource is a map content source that supplies vector shapes to be shown on the map.
The shape may be a url or a GeoJSON object +### ShapeSource is a map content source that supplies vector shapes to be shown on the map.
The shape may be a url or a GeoJSON object. ### props | Prop | Type | Default | Required | Description | diff --git a/docs/SkyLayer.md b/docs/SkyLayer.md index fd4e289a1..4b656b8d1 100644 --- a/docs/SkyLayer.md +++ b/docs/SkyLayer.md @@ -1,4 +1,4 @@ - + ## ### SkyLayer is a spherical dome around the map that is always rendered behind all other layers diff --git a/docs/Style.md b/docs/Style.md index 2ca9452fe..f11ce096d 100644 --- a/docs/Style.md +++ b/docs/Style.md @@ -1,4 +1,4 @@ - + ## ### Style is a component that automatically adds sources / layers to the map using Mapbox GL Style Spec.
Only [`sources`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources) & [`layers`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/) are supported.
Other fields such as `sprites`, `glyphs` etc. will be ignored. Not all layer / source attributes from the style spec are supported, in general the supported attributes will mentioned under https://github.com/rnmapbox/maps/tree/main/docs. diff --git a/docs/SymbolLayer.md b/docs/SymbolLayer.md index 3d346fbc9..fcbb8ef56 100644 --- a/docs/SymbolLayer.md +++ b/docs/SymbolLayer.md @@ -1,4 +1,4 @@ - + ## ### SymbolLayer is a style layer that renders icon and text labels at points or along lines on the map. diff --git a/docs/Terrain.md b/docs/Terrain.md index 47eabc320..9a5771045 100644 --- a/docs/Terrain.md +++ b/docs/Terrain.md @@ -1,4 +1,4 @@ - + ## ### Terrain renders a terran diff --git a/docs/UserLocation.md b/docs/UserLocation.md index 84e837ae1..3ce940d8f 100644 --- a/docs/UserLocation.md +++ b/docs/UserLocation.md @@ -1,4 +1,4 @@ - + ## ### diff --git a/docs/VectorSource.md b/docs/VectorSource.md index 83e528a2d..a47f6ce04 100644 --- a/docs/VectorSource.md +++ b/docs/VectorSource.md @@ -1,6 +1,6 @@ - + ## -### VectorSource is a map content source that supplies tiled vector data in Mapbox Vector Tile format to be shown on the map.
The location of and metadata about the tiles are defined either by an option dictionary or by an external file that conforms to the TileJSON specification. +### VectorSource is a map content source that supplies tiled vector data in Mapbox Vector Tile format to be shown
on the map. The location of and metadata about the tiles are defined either by an option dictionary or by an
external file that conforms to the TileJSON specification. ### props | Prop | Type | Default | Required | Description | diff --git a/docs/docs.json b/docs/docs.json index 614ab847e..451876232 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -209,610 +209,282 @@ "name": "Callout" }, "Camera": { - "description": "", + "description": "Controls the perspective from which the user sees the map.\n\nTo use imperative methods, pass in a ref object:\n\n
const camera = useRef(null);\n\nuseEffect(() => {\n  camera.current?.setCamera({\n    centerCoordinate: [lon, lat],\n  });\n}, []);\n\nreturn (\n  \\\n);
", "displayName": "Camera", "methods": [ { - "name": "fitBounds", - "docblock": "Map camera transitions to fit provided bounds\n\n@example\nthis.camera.fitBounds([lng, lat], [lng, lat])\nthis.camera.fitBounds([lng, lat], [lng, lat], 20, 1000) // padding for all sides\nthis.camera.fitBounds([lng, lat], [lng, lat], [verticalPadding, horizontalPadding], 1000)\nthis.camera.fitBounds([lng, lat], [lng, lat], [top, right, bottom, left], 1000)\n\n@param {Array} northEastCoordinates - North east coordinate of bound\n@param {Array} southWestCoordinates - South west coordinate of bound\n@param {Number=} padding - Camera padding for bound\n@param {Number=} animationDuration - Duration of camera animation\n@return {void}", + "name": "setCamera", + "docblock": "Sets any camera properties, with default fallbacks if unspecified.\n\n@example\ncamera.current?.setCamera({\n centerCoordinate: [lon, lat],\n});\n\n@param {CameraStop | CameraStops} config", "modifiers": [], "params": [ { - "name": "northEastCoordinates", - "description": "North east coordinate of bound", + "name": "config", + "description": null, "type": { - "name": "Array", - "elements": [ - { - "name": "Number" - } - ] + "name": "CameraStop \\| CameraStops" }, "optional": false - }, + } + ], + "returns": null, + "description": "Sets any camera properties, with default fallbacks if unspecified.", + "examples": [ + "\ncamera.current?.setCamera({\n centerCoordinate: [lon, lat],\n});\n\n" + ] + }, + { + "name": "fitBounds", + "docblock": "Set the camera position to enclose the provided bounds, with optional\npadding and duration.\n\n@example\ncamera.fitBounds([lon, lat], [lon, lat]);\ncamera.fitBounds([lon, lat], [lon, lat], [20, 0], 1000);\n\n@param {Position} ne Northeast coordinate of bounding box\n@param {Position} sw Southwest coordinate of bounding box\n@param {number | number[]} paddingConfig The viewport padding, specified as a number (all sides equal), a 2-item array ([vertical, horizontal]), or a 4-item array ([top, right, bottom, left])\n@param {number} animationDuration The transition duration", + "modifiers": [], + "params": [ { - "name": "southWestCoordinates", - "description": "South west coordinate of bound", + "name": "ne", + "description": "Northeast coordinate of bounding box", "type": { - "name": "Array", - "elements": [ - { - "name": "Number" - } - ] + "name": "Position" }, "optional": false }, { - "name": "padding", - "description": "Camera padding for bound", + "name": "sw", + "description": "Southwest coordinate of bounding box", "type": { - "name": "Number" + "name": "Position" }, - "optional": true + "optional": false }, { - "name": "animationDuration", - "description": "Duration of camera animation", + "name": "paddingConfig", + "description": "The viewport padding, specified as a number (all sides equal), a 2-item array ([vertical, horizontal]), or a 4-item array ([top, right, bottom, left])", "type": { - "name": "Number" + "name": "number \\| Array" }, - "optional": true + "optional": false + }, + { + "name": "_animationDuration", + "type": {} } ], - "returns": { - "description": null, - "type": { - "name": "void" - } - }, - "description": "Map camera transitions to fit provided bounds", + "returns": null, + "description": "Set the camera position to enclose the provided bounds, with optional\npadding and duration.", "examples": [ - "\nthis.camera.fitBounds([lng, lat], [lng, lat])\nthis.camera.fitBounds([lng, lat], [lng, lat], 20, 1000) // padding for all sides\nthis.camera.fitBounds([lng, lat], [lng, lat], [verticalPadding, horizontalPadding], 1000)\nthis.camera.fitBounds([lng, lat], [lng, lat], [top, right, bottom, left], 1000)\n\n" + "\ncamera.fitBounds([lon, lat], [lon, lat]);\ncamera.fitBounds([lon, lat], [lon, lat], [20, 0], 1000);\n\n" ] }, { "name": "flyTo", - "docblock": "Map camera will fly to new coordinate\n\n@example\nthis.camera.flyTo([lng, lat])\nthis.camera.flyTo([lng, lat], 12000)\n\n @param {Array} coordinates - Coordinates that map camera will jump too\n @param {Number=} animationDuration - Duration of camera animation\n @return {void}", + "docblock": "Sets the camera to center around the provided coordinate using a realistic 'travel'\nanimation, with optional duration.\n\n@example\ncamera.flyTo([lon, lat]);\ncamera.flyTo([lon, lat], 12000);\n\n @param {Position} centerCoordinate The coordinate to center in the view\n @param {number} animationDuration The transition duration", "modifiers": [], "params": [ { - "name": "coordinates", - "description": "Coordinates that map camera will jump too", - "type": { - "name": "Array", - "elements": [ - { - "name": "Number" - } - ] - }, - "optional": false + "name": "_centerCoordinate", + "optional": false, + "type": {} }, { - "name": "animationDuration", - "description": "Duration of camera animation", - "type": { - "name": "Number" - }, - "optional": true + "name": "_animationDuration", + "type": {} } ], - "returns": { - "description": null, - "type": { - "name": "void" - } - }, - "description": "Map camera will fly to new coordinate", + "returns": null, + "description": "Sets the camera to center around the provided coordinate using a realistic 'travel'\nanimation, with optional duration.", "examples": [ - "\nthis.camera.flyTo([lng, lat])\nthis.camera.flyTo([lng, lat], 12000)\n\n " + "\ncamera.flyTo([lon, lat]);\ncamera.flyTo([lon, lat], 12000);\n\n " ] }, { "name": "moveTo", - "docblock": "Map camera will move to new coordinate at the same zoom level\n\n@example\nthis.camera.moveTo([lng, lat], 200) // eases camera to new location based on duration\nthis.camera.moveTo([lng, lat]) // snaps camera to new location without any easing\n\n @param {Array} coordinates - Coordinates that map camera will move too\n @param {Number=} animationDuration - Duration of camera animation\n @return {void}", + "docblock": "Sets the camera to center around the provided coordinate, with optional duration.\n\n@example\ncamera.moveTo([lon, lat], 200);\ncamera.moveTo([lon, lat]);\n\n @param {Position} centerCoordinate The coordinate to center in the view\n @param {number} animationDuration The transition duration", "modifiers": [], "params": [ { - "name": "coordinates", - "description": "Coordinates that map camera will move too", - "type": { - "name": "Array", - "elements": [ - { - "name": "Number" - } - ] - }, - "optional": false + "name": "_centerCoordinate", + "optional": false, + "type": {} }, { - "name": "animationDuration", - "description": "Duration of camera animation", - "type": { - "name": "Number" - }, - "optional": true + "name": "_animationDuration", + "type": {} } ], - "returns": { - "description": null, - "type": { - "name": "void" - } - }, - "description": "Map camera will move to new coordinate at the same zoom level", + "returns": null, + "description": "Sets the camera to center around the provided coordinate, with optional duration.", "examples": [ - "\nthis.camera.moveTo([lng, lat], 200) // eases camera to new location based on duration\nthis.camera.moveTo([lng, lat]) // snaps camera to new location without any easing\n\n " + "\ncamera.moveTo([lon, lat], 200);\ncamera.moveTo([lon, lat]);\n\n " ] }, { "name": "zoomTo", - "docblock": "Map camera will zoom to specified level\n\n@example\nthis.camera.zoomTo(16)\nthis.camera.zoomTo(16, 100)\n\n@param {Number} zoomLevel - Zoom level that the map camera will animate too\n@param {Number=} animationDuration - Duration of camera animation\n@return {void}", + "docblock": "Zooms the camera to the provided level, with optional duration.\n\n@example\ncamera.zoomTo(16);\ncamera.zoomTo(16, 100);\n\n@param {number} zoomLevel The target zoom\n@param {number} animationDuration The transition duration", "modifiers": [], "params": [ { - "name": "zoomLevel", - "description": "Zoom level that the map camera will animate too", - "type": { - "name": "Number" - }, - "optional": false + "name": "_zoomLevel", + "optional": false, + "type": {} }, { - "name": "animationDuration", - "description": "Duration of camera animation", - "type": { - "name": "Number" - }, - "optional": true - } - ], - "returns": { - "description": null, - "type": { - "name": "void" - } - }, - "description": "Map camera will zoom to specified level", - "examples": [ - "\nthis.camera.zoomTo(16)\nthis.camera.zoomTo(16, 100)\n\n" - ] - }, - { - "name": "setCamera", - "docblock": "Map camera will perform updates based on provided config. Advanced use only!\n\n@example\nthis.camera.setCamera({\n centerCoordinate: [lng, lat],\n zoomLevel: 16,\n animationDuration: 2000,\n})\n\nthis.camera.setCamera({\n stops: [\n { pitch: 45, animationDuration: 200 },\n { heading: 180, animationDuration: 300 },\n ]\n})\n\n @param {Object} config - Camera configuration", - "modifiers": [], - "params": [ - { - "name": "config", - "description": "Camera configuration", - "type": { - "name": "Object" - }, - "optional": false + "name": "_animationDuration", + "type": {} } ], "returns": null, - "description": "Map camera will perform updates based on provided config. Advanced use only!", + "description": "Zooms the camera to the provided level, with optional duration.", "examples": [ - "\nthis.camera.setCamera({\n centerCoordinate: [lng, lat],\n zoomLevel: 16,\n animationDuration: 2000,\n})\n\nthis.camera.setCamera({\n stops: [\n { pitch: 45, animationDuration: 200 },\n { heading: 180, animationDuration: 300 },\n ]\n})\n\n " + "\ncamera.zoomTo(16);\ncamera.zoomTo(16, 100);\n\n" ] } ], "props": [ { - "name": "allowUpdates", + "name": "type", "required": false, - "type": "bool", - "default": "true", - "description": "If false, the camera will not send any props to the native module. Intended to be used to prevent unnecessary tile fetching and improve performance when the map is not visible. Defaults to true." + "type": "literal", + "default": "none", + "description": "Allows static check of the data type. For internal use only." }, { - "name": "animationDuration", + "name": "centerCoordinate", "required": false, - "type": "number", - "default": "2000", - "description": "The duration a camera update takes (in ms)" + "type": "Position", + "default": "none", + "description": "The location on which the map should center." }, { - "name": "animationMode", + "name": "bounds", "required": false, - "type": "enum", - "default": "'easeTo'", - "description": "The animation style when the camara updates. One of:\n`flyTo`: A complex flight animation, affecting both position and zoom.\n`easeTo`: A standard damped curve.\n`linearTo`: An even linear transition.\n`none`: An instantaneous change (v10 only).\n`moveTo`: An instantaneous change ( + ## ### The snapshotManager generates static raster images of the map.
Each snapshot image depicts a portion of a map defined by an SnapshotOptions object you provide.
The snapshotter generates the snapshot asynchronous. diff --git a/example/README.md b/example/README.md index 30412f0f6..f91f8ad6f 100644 --- a/example/README.md +++ b/example/README.md @@ -51,8 +51,8 @@ Not a Mapbox user yet? [Sign up for an account here](https://www.mapbox.com/sign cd example ``` * Create a file called `accesstoken` in the root of the example project and just paste in your [Mapbox access token](https://www.mapbox.com/studio/account/tokens/). (The `accesstoken` file is processed in postinstall, so you need to run `yarn install` after adding/changing accesstoken.) - * Install our dependencies using `yarn install`. +* Tell the TypeScript compiler to recompile when files are changed with `yarn dev`.
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 927ef1dab..dd5792b2e 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -291,7 +291,7 @@ PODS: - React-jsinspector (0.68.1) - React-logger (0.68.1): - glog - - react-native-safe-area-context (3.2.0): + - react-native-safe-area-context (3.4.1): - React-Core - React-perflogger (0.68.1) - React-RCTActionSheet (0.68.1): @@ -358,24 +358,24 @@ PODS: - React-jsi (= 0.68.1) - React-logger (= 0.68.1) - React-perflogger (= 0.68.1) - - RNGestureHandler (1.9.0): + - RNGestureHandler (1.10.3): - React-Core - - rnmapbox-maps (10.0.0-beta.3): + - rnmapbox-maps (10.0.0-beta.15): - MapboxMaps (~> 10.5.0) - React - React-Core - - rnmapbox-maps/DynamicLibrary (= 10.0.0-beta.3) + - rnmapbox-maps/DynamicLibrary (= 10.0.0-beta.15) - Turf - - rnmapbox-maps/DynamicLibrary (10.0.0-beta.3): + - rnmapbox-maps/DynamicLibrary (10.0.0-beta.15): - MapboxMaps (~> 10.5.0) - React - React-Core - Turf - - RNScreens (3.8.0): + - RNScreens (3.13.1): - React-Core - React-RCTImage - - RNSVG (12.1.0): - - React + - RNSVG (12.3.0): + - React-Core - RNVectorIcons (9.0.0): - React-Core - SocketRocket (0.6.0) @@ -582,7 +582,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 4a4bae5671b064a2248a690cf75957669489d08c React-jsinspector: 218a2503198ff28a085f8e16622a8d8f507c8019 React-logger: f79dd3cc0f9b44f5611c6c7862badd891a862cf8 - react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79 + react-native-safe-area-context: 9e40fb181dac02619414ba1294d6c2a807056ab9 React-perflogger: 30ab8d6db10e175626069e742eead3ebe8f24fd5 React-RCTActionSheet: 4b45da334a175b24dabe75f856b98fed3dfd6201 React-RCTAnimation: d6237386cb04500889877845b3e9e9291146bc2e @@ -595,10 +595,10 @@ SPEC CHECKSUMS: React-RCTVibration: 9e344c840176b0af9c84d5019eb4fed8b3c105a1 React-runtimeexecutor: 7285b499d0339104b2813a1f58ad1ada4adbd6c0 ReactCommon: bf2888a826ceedf54b99ad1b6182d1bc4a8a3984 - RNGestureHandler: 9b7e605a741412e20e13c512738a31bd1611759b - rnmapbox-maps: 75c78ebf20cbe15a72418cf7b616d45bcf245c2d - RNScreens: 6e1ea5787989f92b0671049b808aef64fa1ef98c - RNSVG: ce9d996113475209013317e48b05c21ee988d42e + RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211 + rnmapbox-maps: f88612089df70340b68fd7136b28f9c7efc7d7ea + RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19 + RNSVG: 302bfc9905bd8122f08966dc2ce2d07b7b52b9f8 RNVectorIcons: 4143ba35feebab8fdbe6bc43d1e776b393d47ac8 SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608 Turf: 60b93cfdc62758526bc7bf1ef191a829aeb9694d @@ -607,4 +607,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 4b8dadcc679d57c909588d625f8a25f9c79719e3 -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/example/package.json b/example/package.json index b1f81bf73..e70ded7ed 100644 --- a/example/package.json +++ b/example/package.json @@ -2,7 +2,9 @@ "name": "RNMapboxGLExample", "version": "1.0.0", "private": true, + "main": "./index.js", "scripts": { + "dev": "npx tsc --watch", "android": "react-native run-android", "ios": "react-native run-ios --simulator=\"iPhone SE (2nd generation)\"", "pod:install": "cd ios && pod install", @@ -43,6 +45,8 @@ "devDependencies": { "@babel/core": "^7.12.9", "@babel/runtime": "^7.12.5", + "@types/react": "^18.0.9", + "@types/react-native": "^0.67.7", "babel-plugin-module-resolver": "^4.1.0", "detox": "^19.1.0 <19.5.2 || >19.5.2", "glob-to-regexp": "^0.4.0", @@ -50,7 +54,7 @@ "jest-circus": "^27.1.1", "jetifier": "^2.0.0", "metro-react-native-babel-preset": "^0.64.0", - "typescript": "^4.4.3" + "typescript": "^4.6.4" }, "detox": { "testRunner": "jest", @@ -77,4 +81,4 @@ } } } -} +} \ No newline at end of file diff --git a/example/src/examples/Annotations/CustomCallout.tsx b/example/src/examples/Annotations/CustomCallout.tsx index 3eb08a703..99a83dfe6 100644 --- a/example/src/examples/Annotations/CustomCallout.tsx +++ b/example/src/examples/Annotations/CustomCallout.tsx @@ -1,8 +1,9 @@ import React, { FC, useState } from 'react'; import MapboxGL, { SymbolLayerStyle } from '@rnmapbox/maps'; -import { Feature } from '@turf/helpers/dist/js'; import { View, Text, ViewStyle, StyleProp, TextStyle } from 'react-native'; +import { Feature, FeatureCollection, Point } from 'geojson'; +// @ts-ignore import exampleIcon from '../../assets/pin.png'; import sheet from '../../styles/sheet'; import Page from '../common/Page'; @@ -12,7 +13,7 @@ const defaultCamera = { zoomLevel: 17.4, }; -const featureCollection = { +const featureCollection: FeatureCollection = { type: 'FeatureCollection', features: [ { @@ -49,7 +50,7 @@ type CustomCalloutProps = { const CustomCallout: FC = (props) => { const [selectedFeature, setSelectedFeature] = - useState>(); + useState>(); const onPinPress = (e: any): void => { if (selectedFeature) { diff --git a/example/src/examples/Map/ShowMap.tsx b/example/src/examples/Map/ShowMap.tsx index 37635edc4..01f0c4c9d 100755 --- a/example/src/examples/Map/ShowMap.tsx +++ b/example/src/examples/Map/ShowMap.tsx @@ -1,22 +1,25 @@ import React, { FC, useState, useEffect } from 'react'; import { Alert } from 'react-native'; -import MapboxGL from '@rnmapbox/maps'; +import MapboxGL, { StyleURL, StyleURLKey } from '@rnmapbox/maps'; import sheet from '../../styles/sheet'; import { onSortOptions } from '../../utils'; import TabBarPage from '../common/TabBarPage'; const ShowMap: FC = (props) => { - const _mapOptions = Object.keys(MapboxGL.StyleURL) - .map((key) => { + const keys = Object.keys(StyleURL) as StyleURLKey[]; + const _mapOptions = keys + .map((key: StyleURLKey) => { return { label: key, - data: (MapboxGL.StyleURL as any)[key], // bad any, because enums + data: StyleURL[key], }; }) .sort(onSortOptions); - const [styleURL, setStyleURL] = useState({ styleURL: _mapOptions[0].data }); + const [styleURL, setStyleURL] = useState( + _mapOptions[0].data, + ); useEffect(() => { MapboxGL.locationManager.start(); @@ -26,8 +29,11 @@ const ShowMap: FC = (props) => { }; }, []); - const onMapChange = (index: number, newStyleURL: MapboxGL.StyleURL): void => { - setStyleURL({ styleURL: newStyleURL }); + const onMapChange = ( + index: number, + newStyleURL: typeof StyleURL[StyleURLKey], + ): void => { + setStyleURL(newStyleURL); }; const onUserMarkerPress = (): void => { @@ -41,7 +47,7 @@ const ShowMap: FC = (props) => { options={_mapOptions} onOptionPress={onMapChange} > - + diff --git a/example/src/examples/V10/CameraAnimation.js b/example/src/examples/V10/CameraAnimation.js deleted file mode 100644 index c5d988977..000000000 --- a/example/src/examples/V10/CameraAnimation.js +++ /dev/null @@ -1,248 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import { Button, SafeAreaView, View } from 'react-native'; -import { - MapView, - Camera, - ShapeSource, - CircleLayer, - Logger, -} from '@rnmapbox/maps'; -import bbox from '@turf/bbox'; -import { Text, Divider } from 'react-native-elements'; - -import Page from '../common/Page'; -import colors from '../../styles/colors'; - -Logger.setLogLevel('verbose'); - -const styles = { - map: { - flex: 1, - }, - circle: { - circleRadius: 6, - circleColor: colors.primary.blue, - }, - sheet: { - paddingTop: 10, - paddingHorizontal: 10, - }, - content: { - padding: 10, - }, - buttonRow: { - flex: 0, - flexDirection: 'row', - justifyContent: 'space-around', - }, - divider: { - marginVertical: 10, - }, - fadedText: { - color: 'gray', - }, -}; - -const zeroPadding = { - paddingTop: 0, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, -}; -const evenPadding = { - paddingTop: 40, - paddingBottom: 40, - paddingLeft: 40, - paddingRight: 40, -}; -const minZoomLevel = 8; -const maxZoomLevel = 16; - -const randPadding = () => { - const randNum = () => { - const items = [0, 150, 300]; - return items[Math.floor(Math.random() * items.length)]; - }; - - return { - paddingTop: randNum(), - paddingBottom: randNum(), - paddingLeft: randNum(), - paddingRight: randNum(), - }; -}; - -const toPosition = (coordinate) => { - return [coordinate.longitude, coordinate.latitude]; -}; - -const CameraAnimation = (props) => { - const initialCoordinate = { - latitude: 40.759211, - longitude: -73.984638, - }; - - const [animationMode, setAnimationMode] = useState('moveTo'); - const [coordinates, setCoordinates] = useState([initialCoordinate]); - const [padding, setPadding] = useState(zeroPadding); - - const paddingDisplay = useMemo(() => { - return `L ${padding.paddingLeft} | R ${padding.paddingRight} | T ${padding.paddingTop} | B ${padding.paddingBottom}`; - }, [padding]); - - const move = (_animationMode, shouldCreateMultiple) => { - setAnimationMode(_animationMode); - - if (shouldCreateMultiple) { - const _centerCoordinate = { - latitude: initialCoordinate.latitude + Math.random() * 0.2, - longitude: initialCoordinate.longitude + Math.random() * 0.2, - }; - const _coordinates = Array(10) - .fill(0) - .map((_) => { - return { - latitude: _centerCoordinate.latitude + Math.random() * 0.2, - longitude: _centerCoordinate.longitude + Math.random() * 0.2, - }; - }); - setCoordinates(_coordinates); - } else { - setCoordinates([ - { - latitude: initialCoordinate.latitude + Math.random() * 0.2, - longitude: initialCoordinate.longitude + Math.random() * 0.2, - }, - ]); - } - }; - - const features = useMemo(() => { - return coordinates.map((p) => { - return { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: toPosition(p), - }, - }; - }); - }, [coordinates]); - - const centerOrBounds = useMemo(() => { - if (coordinates.length === 1) { - return { - centerCoordinate: toPosition(coordinates[0]), - }; - } else { - const positions = coordinates.map(toPosition); - const lineString = { - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: positions, - }, - }; - const _bbox = bbox(lineString); - return { - bounds: { - ne: [_bbox[0], _bbox[1]], - sw: [_bbox[2], _bbox[3]], - }, - }; - } - }, [coordinates]); - - const locationDisplay = useMemo(() => { - if (coordinates.length > 1) { - const ne = centerOrBounds.bounds?.ne.map((n) => n.toFixed(3)); - const sw = centerOrBounds.bounds?.sw.map((n) => n.toFixed(3)); - return `ne ${ne} | sw ${sw}`; - } else if (coordinates.length === 1) { - const lon = coordinates[0].longitude.toFixed(4); - const lat = coordinates[0].latitude.toFixed(4); - return `lon ${lon} | lat ${lat}`; - } - }, [coordinates, centerOrBounds]); - - return ( - - - - - {features.map((f) => { - const id = JSON.stringify(f.geometry.coordinates); - return ( - - - - ); - })} - - - - - - centerCoordinate - -