@fleetbase/ember-ui
Version:
Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.
234 lines (205 loc) • 7.87 kB
JavaScript
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { isBlank } from '@ember/utils';
import { isArray } from '@ember/array';
import { later } from '@ember/runloop';
import { debug } from '@ember/debug';
import { task } from 'ember-concurrency';
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
const DEFAULT_LATITUDE = 1.3521;
const DEFAULT_LONGITUDE = 103.8198;
export default class CoordinatesInputComponent extends Component {
fetch;
currentUser;
zoom;
zoomControl;
leafletMap;
latitude;
longitude;
mapLat;
mapLng;
lookupQuery;
isLoading = false;
isReady = false;
isInitialMoveEnded = false;
tileSourceUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
mapTheme = 'light';
disabled = false;
/**
* Constructor for CoordinatesInputComponent. Sets initial map coordinates and values.
* @memberof CoordinatesInputComponent
*/
constructor(owner, { onInit, value, darkMode = false, zoom = 9, zoomControl = false, disabled = false }) {
super(...arguments);
this.setInitialMapCoordinates();
this.setInitialValueFromPoint(value);
this.changeTileSource(darkMode ? 'dark' : 'light');
this.zoom = zoom;
this.zoomControl = zoomControl;
this.disabled = disabled;
if (typeof onInit === 'function') {
onInit(this);
}
}
changeTileSource(sourceUrl = null) {
if (sourceUrl === 'dark') {
this.mapTheme = 'dark';
this.tileSourceUrl = 'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png';
} else if (sourceUrl === 'dark_all') {
this.mapTheme = 'dark_all';
this.tileSourceUrl = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png';
} else if (sourceUrl === 'light') {
this.mapTheme = 'light';
this.tileSourceUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
} else if (typeof sourceUrl === 'string' && sourceUrl.startsWith('https://')) {
this.mapTheme = 'custom';
this.tileSourceUrl = sourceUrl;
} else {
this.mapTheme = 'light';
this.tileSourceUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
}
}
/**
* Checks if the provided object is a geographical point.
* @param {Object} point - Object to check.
* @returns {boolean} True if the object is a geographical point, false otherwise.
* @memberof CoordinatesInputComponent
*/
isPoint(point) {
return typeof point === 'object' && !isBlank(point.type) && point.type === 'Point' && isArray(point.coordinates);
}
/**
* Sets the initial value of the map's coordinates from a geographical point.
* @param {Object} point - Geographical point to set the initial value from.
* @memberof CoordinatesInputComponent
*/
setInitialValueFromPoint(point) {
if (this.isPoint(point)) {
const [longitude, latitude] = point.coordinates;
if (longitude === 0 && latitude === 0) {
return;
}
this.updateCoordinates(latitude, longitude, { fireCallback: false });
}
}
/**
* Sets the initial map coordinates based on the current user's location.
* @memberof CoordinatesInputComponent
*/
setInitialMapCoordinates() {
const whois = this.currentUser.getOption('whois', {});
this.mapLat = getWithDefault(whois, 'latitude', DEFAULT_LATITUDE);
this.mapLng = getWithDefault(whois, 'longitude', DEFAULT_LONGITUDE);
}
/**
* Updates the coordinates of the map.
* @param {number|Object} lat - Latitude or object with coordinates.
* @param {number} [lng] - Longitude.
* @param {Object} [options={}] - Additional options.
* @memberof CoordinatesInputComponent
*/
updateCoordinates(lat, lng, options = {}) {
if (this.isPoint(lat)) {
const [longitude, latitude] = lat.coordinates;
return this.updateCoordinates(latitude, longitude);
}
const { onChange } = this.args;
const fireCallback = getWithDefault(options, 'fireCallback', true);
const updateMap = getWithDefault(options, 'updateMap', true);
this.latitude = lat;
this.longitude = lng;
if (updateMap === true) {
this.mapLat = lat;
this.mapLng = lng;
}
if (fireCallback === true && typeof onChange === 'function') {
onChange({ latitude: lat, longitude: lng });
}
}
/**
* Leaflet event triggered when the map has loaded. Sets the leafletMap property.
* @param {Object} event - The event object containing the map target.
* @memberof CoordinatesInputComponent
*/
onMapLoaded({ target }) {
this.leafletMap = target;
later(
this,
() => {
this.isReady = true;
},
300
);
}
/**
* Ember action to zoom in on the map.
* @memberof CoordinatesInputComponent
*/
onZoomIn() {
if (this.leafletMap) {
this.leafletMap.zoomIn();
}
}
/**
* Ember action to zoom out on the map.
* @memberof CoordinatesInputComponent
*/
onZoomOut() {
if (this.leafletMap) {
this.leafletMap.zoomOut();
}
}
/**
* Ember action to handle closing the map or the component. Resets the map coordinates to the current latitude and longitude.
* @memberof CoordinatesInputComponent
*/
onClose() {
this.mapLat = this.latitude;
this.mapLng = this.longitude;
}
/**
* Ember action to set coordinates based on the map's current position.
* @param {Object} event - The event object containing map details.
* @memberof CoordinatesInputComponent
*/
setCoordinatesFromMap(event) {
const { onUpdatedFromMap } = this.args;
const { target } = event;
const center = target.getCenter();
const geographicalCenter = typeof center.wrap === 'function' ? center.wrap() : center;
const { lat, lng } = geographicalCenter;
this.updateCoordinates(lat, lng, { updateMap: false });
if (typeof onUpdatedFromMap === 'function') {
onUpdatedFromMap({ latitude: lat, longitude: lng });
}
}
/**
* Task which performs a reverse geolocation lookup. Updates the coordinates based on the lookup query result.
*
* @return {AsyncGenerator<Promise>}
* @memberof CoordinatesInputComponent
*/
*reverseLookup() {
if (isBlank(this.lookupQuery)) {
return;
}
try {
const place = yield this.fetch.get('geocoder/query', { query: this.lookupQuery, single: true });
if (place) {
const [longitude, latitude] = place.location.coordinates;
this.updateCoordinates(latitude, longitude);
if (typeof this.args.onGeocode === 'function') {
this.args.onGeocode(place);
}
}
return place;
} catch (error) {
debug('Coordinates input reverse lookup query failed:', error);
if (typeof this.args.onGeocodeError === 'function') {
this.args.onGeocodeError(error);
}
}
}
}