The app features an interactive map of your local area and a list of nearby points of interest (stores, restaurants, parks, etc).
Technologies: Vue.js, Google Maps API, HTML5, CSS3
When you select a place from the list or click its icon on the map, you can save it to your favorites and add a review with a star rating. The review will be displayed on the map and on the list. If you saved the location as a favorite it will be displayed in the My Favorites tab.
When a user saves a favorite place or adds a review, their favorites are saved locally using the browser's LocalStorage so that the data can persist when the user returns to the website.
When the app initializes it prompts the user's browser for their location using the Geolocation API via
navigator.geolocation. This location is provided to the Maps Service to update the bounds of the visible map. The user can also use the autocomplete search input, which is added as a map control, to search for a specific place or general area using the Places Autocomplete Service.
When the bounds of the map have changed, the system will request a new list of nearby places using the Places Service. These places are displayed in the results list on the Nearby tab. Additionally, the user can click a place icon on the map to select a location.
The project is separated into multiple single-file Vue.js components which provide their own internal templates, logic, and styling. The component
.vue files are compiled using Webpack.
The project also utilizes a centralized state management pattern which places app state data management into the
AppState.js file, so that the common state data can be accessed by separate components. This pattern made the component interaction easier to maintain overall, but could be replaced with a more standardized implementation like Vuex (see Future Updates section).
Local Favorites uses a Flexbox based design that allows the main section (the map and the results list sidebar) to fill the page vertically and horizontally on larger screens. The sidebar uses a maximum width of 400px and the map can fill the rest of the available screen width. The contents of the sidebar scroll instead of flowing beyond the screen height.
On smaller screens, the app uses a vertical design. The map has its maximum height to set to 250px and it fills the screen horizontally. The results list sidebar is then moved to below the map.
Some components make use of the Vue.js animation templates to provide motion to certain controls, as seen in the example screen capture. For example, when a place is selected the button controls slide into the screen. Also, when the active tab is changed the tab's content slides in and the other tab slides out.
The app uses some base styles from the Bulma CSS framework, but only imports the style modules required. This reduces the overall project CSS file size. Additionally SVG icons are used to add extra flair to buttons, links, tabs, etc.
The website aims to be accessible by keyboard, as well as mouse. The results in the sidebar list have been added to the
tabindex so that they can be focused when hitting the
Tab key. The results can also be navigated using the
Down keys. Once focused, a result can be selected by pressing the
Space keys. The user can then use the
Tab key to reach the Save and Add Note control buttons.
The app also uses the
outline CSS property to add a highlighted look to controls when they are focused. The highlighted focus look improves visibility when using the keyboard to navigate, but doesn't look out of place when using the app with a mouse.
One issue that arose while reviewing accessibility is that the map doesn't seem to be able to release keyboard focus in Firefox, even if you keep pressing the
Tab key. To avoid this issue, the sidebar was placed ahead of the map in the HTML code. The Flexbox CSS was updated to reverse the flow of the design so that the sidebar will still appear after the map. Now in Firefox, the user can tab through the results list and its controls before hitting the map element and losing focus.
Progressive Web App
The app was bootstrapped using the Vue CLI tool which provides the configuration necessary for the Progressive Web App standard. By following the PWA standard, the website can be saved to a user's device home screen and act as a native app.
Due to the use of Flexbox and the Vue.js framework, browser support is limited to modern browsers, as well as Internet Explorer 10 and 11.
Support for Internet Explorer 10 and 11 required the use additional polyfills included in the Webpack configuration. Polyfills are scripts that provide support for newer ECMAScript features in browsers that aren't compatible.
When the map bounds are changed, the app requests a new list of results from the Google Maps API. When a result is selected, that result is centered on the map and an InfoWindow is displayed on the map. This may cause a positioning issue where the map automatically readjusts the map bounds to fit the InfoWindow, which could result in a new list being requested.
This can result in a jumpy experience where the list is refreshed multiple times in a short amount of time. One possible fix would be to check that the bounds haven't changed past a certain distance before requesting a new list.
When the app is initialized, it requests the browser's geolocation, which requires a prompt asking for the user's permission. This experience could be improved by providing the map with a default location and adding a control to the map that requests the browser's geolocation on demand.
If the system detects the user is offline or otherwise unable to connect to Google Maps, it should hide or minimize the features that require API connection and instead show the My Favorites tab, which can be used offline.
The centralized app state management pattern provided by the
AppState.jsfile could be replaced with a more robust implementation like Vuex.
A library like Vuex could improve the way the central app state is managed in terms of efficiency and structure, but would require the introduction of more boilerplate code and concepts specific to the library.