Skip to content

Commit

Permalink
Merge pull request #31 from fleetbase/dev-v0.2.5
Browse files Browse the repository at this point in the history
v0.2.5
  • Loading branch information
roncodes authored Nov 16, 2023
2 parents c4c9c18 + cea9fd7 commit 8faf4cc
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 59 deletions.
28 changes: 21 additions & 7 deletions addon/components/coordinates-input.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,38 @@
<Input class="w-full form-input" @type="text" placeholder="Longitude" aria-label="Longitude" @value={{this.longitude}} />
</div>
<div class="my-1">
<BasicDropdown ...attributes @verticalPosition="top" @horizontalPosition="left" @renderInPlace={{true}} as |dd|>
<BasicDropdown ...attributes @verticalPosition="top" @horizontalPosition="left" @onClose={{this.onClose}} @renderInPlace={{or @renderInPlace true}} as |dd|>
<dd.Trigger class={{@triggerClass}}>
<span class="text-sky-500 hover:text-sky-600">Select from map</span>
</dd.Trigger>
<dd.Content class="bg-transparent min-w-500px">
<div class="rounded shadow-md w-60 h-60 relative my-3">
<LeafletMap class="w-60 h-60 rounded shadow-sm" @lat={{this.mapLat}} @lng={{this.mapLng}} @zoom={{12}} @onMoveend={{this.setCoordinatesFromMap}} as |layers|>
<layers.tile @url="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png" />
<div class="coordinates-input-map-container rounded shadow-md w-60 h-60 relative my-3 {{@mapContainerClass}}">
<LeafletMap class={{@leafletMapClass}} @lat={{this.mapLat}} @lng={{this.mapLng}} @zoomControl={{this.zoomControl}} @zoom={{this.zoom}} @onLoad={{this.onMapLoaded}} @onMoveend={{this.setCoordinatesFromMap}} @doubleClickZoom={{false}} as |layers|>
<layers.tile @url={{this.tileSourceUrl}} />
</LeafletMap>
<div class="absolute inset-0 m-auto z-9999 w-10 h-10">
<div class="coordinates-input-zoom-controls {{@zoomControlClass}}">
<div class="coordinates-input-zoom-controls-container {{@zoomControlContainerClass}}">
<div id="map-toolbar-zoom-in-button-wrapper">
<button type="button" id="map-toolbar-zoom-in-button" class="toolbar-button" {{on "click" this.onZoomIn}}>
<FaIcon @icon="plus" @prefix="fas" class="text-gray-100" />
</button>
</div>
<div id="map-toolbar-zoom-out-button-wrapper">
<button type="button" class="toolbar-button" {{on "click" this.onZoomOut}}>
<FaIcon @icon="minus" id="map-toolbar-zoom-out-button" @prefix="fas" class="text-gray-100" />
</button>
</div>
</div>
</div>
<div class="absolute inset-0 m-auto z-9999 w-10 h-10 pointer-events-none">
<img src="/images/map-marker.png" alt="Draggable map marker" class="w-10" />
</div>
</div>
<div class="flex flex-col">
<div class="flex flex-row items-center pb-4">
<Input @type="text" class="form-input mr-2" @value={{this.lookupQuery}} aria-label="Address Search" disabled={{this.isLoading}} placeholder="Enter address" />
<Button @wrapperClass="mr-2" @icon="search-location" @type="primary" @size="md" @text="Lookup" @onClick={{this.reverseLookup}} @isLoading={{this.isLoading}} />
<Button @type="default" @iconPrefix="fas" @icon="times" @size="md" @text="Done" @onClick={{dd.actions.close}} />
<Button @wrapperClass="mr-2" @icon="search-location" @type="primary" @size="sm" @text="Lookup" @onClick={{this.reverseLookup}} @isLoading={{this.isLoading}} />
<Button @type="default" @iconPrefix="fas" @icon="times" @size="sm" @text="Done" @onClick={{dd.actions.close}} />
</div>
</div>
</dd.Content>
Expand Down
208 changes: 195 additions & 13 deletions addon/components/coordinates-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,183 @@ 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 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 {
/**
* Service for fetching data.
* @type {Service}
* @memberof CoordinatesInputComponent
*/
@service fetch;

/**
* Service for accessing current user information.
* @type {Service}
* @memberof CoordinatesInputComponent
*/
@service currentUser;

/**
* Current zoom level of the map.
* @type {number}
* @memberof CoordinatesInputComponent
*/
@tracked zoom;

/**
* Controls whether zoom controls are shown.
* @type {boolean}
* @memberof CoordinatesInputComponent
*/
@tracked zoomControl;

/**
* Reference to the Leaflet map instance.
* @type {Object}
* @memberof CoordinatesInputComponent
*/
@tracked leafletMap;

/**
* Current latitude of the map center.
* @type {number}
* @memberof CoordinatesInputComponent
*/
@tracked latitude;

/**
* Current longitude of the map center.
* @type {number}
* @memberof CoordinatesInputComponent
*/
@tracked longitude;

/**
* Latitude for map positioning.
* @type {number}
* @memberof CoordinatesInputComponent
*/
@tracked mapLat;

/**
* Longitude for map positioning.
* @type {number}
* @memberof CoordinatesInputComponent
*/
@tracked mapLng;

/**
* Query used for location lookup.
* @type {string}
* @memberof CoordinatesInputComponent
*/
@tracked lookupQuery;

/**
* Indicates if the component is loading data.
* @type {boolean}
* @memberof CoordinatesInputComponent
*/
@tracked isLoading = false;

/**
* Indicates if the map is ready.
* @type {boolean}
* @memberof CoordinatesInputComponent
*/
@tracked isReady = false;

/**
* Flag to track if the initial map movement has ended.
* @type {boolean}
* @memberof CoordinatesInputComponent
*/
@tracked isInitialMoveEnded = false;

/**
* The URL for the map's tile source.
* @type {string}
* @memberof CoordinatesInputComponent
*/
@tracked tileSourceUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';

/**
* Constructor for CoordinatesInputComponent. Sets initial map coordinates and values.
* @memberof CoordinatesInputComponent
*/
constructor() {
super(...arguments);

this.setInitialMapCoordinates();
this.setInitialValueFromPoint(this.args.value);
this.zoom = getWithDefault(this.args, 'zoom', 9);
this.zoomControl = getWithDefault(this.args, 'zoomControl', false);

if (typeof this.args.onInit === 'function') {
this.args.onInit(this);
}
}

/**
* 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);
}

@action setInitialValueFromPoint(point) {
/**
* 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 });
}
}

@action setInitialMapCoordinates() {
/**
* Sets the initial map coordinates based on the current user's location.
* @memberof CoordinatesInputComponent
*/
setInitialMapCoordinates() {
const whois = this.currentUser.getOption('whois');

this.mapLat = whois.latitude ?? DEFAULT_LATITUDE;
this.mapLng = whois.longitude ?? DEFAULT_LONGITUDE;
this.mapLat = getWithDefault(whois, 'latitude', DEFAULT_LATITUDE);
this.mapLng = getWithDefault(whois, 'longitude', DEFAULT_LONGITUDE);
}

@action updateCoordinates(lat, lng, options = {}) {
/**
* 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 fireCallback = options.fireCallback ?? true;
const updateMap = options.updateMap ?? true;
const { onChange } = this.args;
const fireCallback = getWithDefault(options, 'fireCallback', true);
const updateMap = getWithDefault(options, 'updateMap', true);

this.latitude = lat;
this.longitude = lng;
Expand All @@ -66,12 +190,64 @@ export default class CoordinatesInputComponent extends Component {
this.mapLng = lng;
}

if (fireCallback === true && typeof this.args.onChange === 'function') {
this.args.onChange({ latitude: lat, longitude: 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
*/
@action onMapLoaded({ target }) {
this.leafletMap = target;

later(
this,
() => {
this.isReady = true;
},
300
);
}

/**
* Ember action to zoom in on the map.
* @memberof CoordinatesInputComponent
*/
@action onZoomIn() {
if (this.leafletMap) {
this.leafletMap.zoomIn();
}
}

@action setCoordinatesFromMap({ target }) {
/**
* Ember action to zoom out on the map.
* @memberof CoordinatesInputComponent
*/
@action 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
*/
@action 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
*/
@action setCoordinatesFromMap(event) {
const { target } = event;
const { onUpdatedFromMap } = this.args;
const { lat, lng } = target.getCenter();

Expand All @@ -82,8 +258,12 @@ export default class CoordinatesInputComponent extends Component {
}
}

@action async reverseLookup() {
const { onGeocode } = this.args;
/**
* Ember action for performing a reverse geolocation lookup. Updates the coordinates based on the lookup query result.
* @memberof CoordinatesInputComponent
*/
@action reverseLookup() {
const { onGeocode, onGeocodeError } = this.args;
const query = this.lookupQuery;

if (isBlank(query)) {
Expand All @@ -108,7 +288,9 @@ export default class CoordinatesInputComponent extends Component {
}
})
.catch((error) => {
console.log(error);
if (typeof onGeocodeError === 'function') {
onGeocodeError(error);
}
})
.finally(() => {
this.isLoading = false;
Expand Down
2 changes: 1 addition & 1 deletion addon/components/country-select.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="fleetbase-power-select {{@wrapperClass}}">
<PowerSelect @renderInPlace={{true}} @searchEnabled={{true}} @selected={{this.selected}} @searchField="name" @options={{this.countries}} @onChange={{this.selectCountry}} @triggerClass="form-select form-input {{@triggerClass}}" @dropdownClass={{or @dropdownClass "ember-model-select__dropdown"}} @placeholder={{or @placeholder "Select country..."}} @disabled={{this.isLoading}} ...attributes as |country|>
<PowerSelect @renderInPlace={{true}} @searchEnabled={{true}} @selected={{this.selected}} @searchField="name" @options={{this.countries}} @onChange={{this.selectCountry}} @triggerClass="form-select form-input {{@triggerClass}}" @dropdownClass={{or @dropdownClass "ember-model-select__dropdown"}} @placeholder={{or @placeholder "Select country..."}} @disabled={{this.isLoading}} @allowClear={{@allowClear}} ...attributes as |country|>
<span class="mr-1">{{country.emoji}}</span>
<span>{{country.name}}</span>
</PowerSelect>
Expand Down
Loading

0 comments on commit 8faf4cc

Please sign in to comment.