Skip to content

Commit

Permalink
FEAT: update API, add new GoogleMapField
Browse files Browse the repository at this point in the history
  • Loading branch information
Iliain committed Oct 21, 2024
1 parent e8df177 commit 5bd8ffd
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 4 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ You can call data from the Google settings on the frontend via `$GoogleConfig`,
<% end_with %>
```

### GTM Scripts

You can render the GTM scripts in your template with the following:

* `$HeadScripts.RAW`
* `$BodyStartScripts.RAW`
* `$BodyEndScripts.RAW`


## Places

Setting up a Place in the CMS, with an example of the Review data
Expand All @@ -50,5 +59,7 @@ With a selected Place, you can render the badge and feed in your template with `

## TODO

* Fix the issue of the CMS needing to be reloaded for the map to appear when going back and viewing another map
* Add more APIs
* Update CSS to properly render as-is on the frontend (like a widget)
* Allow enabling/disabling of specific Admin sections
2 changes: 2 additions & 0 deletions client/css/badge.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
box-shadow: 0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
width: 100px !important;
height: 100px !important;
object-fit: cover;
object-position: center;
}

.info-place a {
Expand Down
33 changes: 33 additions & 0 deletions client/css/map-field.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#map-field {
height:500px;
}

.controls {
background-color: #fff;
border-radius: 2px;
border: 1px solid transparent;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
box-sizing: border-box;
font-family: Roboto !important;
font-size: 15px !important;
font-weight: 300;
height: 29px;
margin-left: 17px;
margin-top: 10px;
outline: none;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 400px;
}

.controls:focus {
border-color: #4d90fe;
}

.title {
font-weight: bold;
}

#map #infowindow-content {
display: inline;
}
6 changes: 6 additions & 0 deletions client/javascript/default-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
function initMap() {
const map = new google.maps.Map(document.getElementById("map-field"), {
center: { lat: -33.8688, lng: 151.2195 },
zoom: 13,
});
}
74 changes: 74 additions & 0 deletions client/javascript/places-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
function initMap() {
const position = { lat: -33.8688, lng: 151.2195 };
const existingPlace = document.getElementById("place-id");

const map = new google.maps.Map(document.getElementById("map-field"), {
center: position,
zoom: 13,
mapId: "DEMO_MAP_ID"
});

const input = document.getElementById("pac-input");
const autocomplete = new google.maps.places.Autocomplete(input);

autocomplete.bindTo("bounds", map);

map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

const infowindow = new google.maps.InfoWindow({content: null, ariaLabel: 'Test'});
const infowindowContent = document.getElementById("infowindow-content");

infowindow.setContent(infowindowContent);

const marker = new google.maps.marker.AdvancedMarkerElement({
map: map,
position: null,
});

if (existingPlace.value) {
const geocoder = new google.maps.Geocoder();
geocoder
.geocode({ placeId: existingPlace.value })
.then(({ results }) => {
const existingCoords = results[0].geometry.location;
marker.position = existingCoords;
map.setCenter(existingCoords);
});
}

autocomplete.addListener("place_changed", function() {
marker.position = null;
infowindow.close();

const place = autocomplete.getPlace();

if (!place.geometry || !place.geometry.location) {
return;
}

if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17);
}

marker.position = place.geometry.location;

infowindowContent.children.namedItem("place-id").textContent = place.place_id;
// set href
infowindowContent.children.namedItem("place-id").href = `javascript:setPlaceValue('${place.place_id}')`;
infowindowContent.children.namedItem("place-address").textContent = place.formatted_address;
infowindow.open(map, marker);

let heading = document.createElement('div');
heading.textContent = place.name;
heading.style.fontWeight = 'bold';

infowindow.setHeaderContent(heading);
});
}

function setPlaceValue(placeId) {
document.getElementById("Form_ItemEditForm_PlaceID").value = placeId;
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
},
"extra": {
"expose": [
"client/css"
"client/css",
"client/javscript"
]
},
"minimum-stability": "dev"
Expand Down
Binary file modified docs/images/place-fields.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 133 additions & 0 deletions src/Fields/GoogleMapField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

namespace Iliain\GoogleConfig\Fields;

use SilverStripe\Core\Environment;
use SilverStripe\View\Requirements;
use SilverStripe\Forms\DatalessField;

class GoogleMapField extends DatalessField
{
/**
* The type of map to be displayed
* @var string
*/
protected $mapType;

/**
* An array of URL parameters to be appended to the Google Maps API URL
* @var array
*/
protected $urlParams;

const TYPE_DEFAULT = 'default';

const TYPE_PLACES = 'places';

/**
* @param string $name
* @param string $mapType
* @param array $urlParams
*/
public function __construct($name, $mapType = null, $urlParams = [])
{
$this->setMapType($mapType);
$this->setUrlParams($urlParams);

parent::__construct($name);
}

/**
* @param string $type
*
* @return $this
*/
public function setMapType($mapType)
{
if ($mapType) {
$this->mapType = $mapType;
} else {
$this->mapType = self::TYPE_DEFAULT;
}

return $this;
}

/**
* @return string
*/
public function getMapType()
{
return $this->mapType;
}

/**
* @param string $type
*
* @return $this
*/
public function setUrlParams($urlParams)
{
$urlParams['loading'] = 'async';
$urlParams['callback'] = 'initMap';
$urlParams['v'] = 'weekly';

if ($this->getMapType() === self::TYPE_PLACES) {
$urlParams['libraries'] = 'places,marker';
}

$this->urlParams = $urlParams;

return $this;
}

/**
* @return array
*/
public function getUrlParams()
{
return $this->urlParams;
}

public function Field($properties = [])
{
Requirements::css('iliain/silverstripe-google-config: client/css/map-field.css');

switch ($this->getMapType()) {
case self::TYPE_PLACES:
Requirements::javascript('iliain/silverstripe-google-config: client/javascript/places-map.js', ['defer' => true]);
break;
default:
Requirements::javascript('iliain/silverstripe-google-config: client/javascript/default-map.js', ['defer' => true]);
break;
}

Requirements::javascript($this->getMapURL(), ['async' => true, 'defer' => true]);

$properties['MapType'] = $this->getMapType();

// Get the place ID from object loading this field
$properties['PlaceID'] = $this->getForm()->getRecord()->PlaceID;

return parent::Field($properties);
}

/**
* Returns the URL for the Google Maps API with relevant parameters
* @return string
*/
public function getMapURL()
{
if (!Environment::getEnv('GOOGLE_MAPS_API_KEY')) {
throw new \Exception('No Google Maps API key set');
}

$url = 'https://maps.googleapis.com/maps/api/js?key=' . Environment::getEnv('GOOGLE_MAPS_API_KEY');

if ($this->getUrlParams()) {
$url .= '&' . http_build_query($this->getUrlParams());
}

return $url;
}
}
6 changes: 3 additions & 3 deletions src/Model/GooglePlace.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Iliain\GoogleConfig\Models;

use Exception;
use Iliain\GoogleConfig\Fields\GoogleMapField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\Forms\TextField;
Expand All @@ -12,7 +13,6 @@
use SilverStripe\Forms\TextareaField;
use SilverStripe\ORM\FieldType\DBText;
use SilverStripe\ORM\FieldType\DBHTMLText;
use Iliain\GoogleConfig\Models\GoogleConfig;
use SilverStripe\Forms\ToggleCompositeField;

class GooglePlace extends DataObject
Expand Down Expand Up @@ -64,9 +64,9 @@ public function getCMSFields()
TextField::create('PlaceTitle', 'Place Title', $this->getPlaceField('name'))->setReadonly(true),
TextField::create('PlaceID', 'Place ID'),
LiteralField::create('Message', '<div class="message notice"><p>Use the map below to find your location, then copy the Place ID into the field above.</p></div>'),
// ToggleCompositeField is causing issues with the elements not rendering before the script executes
ToggleCompositeField::create('PlaceMap', 'Map', [
// Use map listed in the guides. If it stops working, we'll have to figure out how best to get the place ID
LiteralField::create('SelectorMap', '<iframe src="https://geo-devrel-javascript-samples.web.app/samples/places-placeid-finder/app/dist/" allow="fullscreen; " style="width: 100%; height: 400px;" loading="lazy"></iframe>'),
GoogleMapField::create('MapFrame', 'places'),
])->setHeadingLevel(4)->setStartClosed($this->PlaceID ? true : false),
]);

Expand Down
33 changes: 33 additions & 0 deletions templates/iliain/GoogleConfig/Fields/GoogleMapField.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<div id="map-holder">
<% if $MapType == 'places' %>
<div style="display: none">
<input
id="pac-input"
class="controls"
type="text"
placeholder="Enter a location"
/>
</div>
<% end_if %>

<% if $PlaceID %>
<div style="display: none">
<input type="hidden"
id="place-id"
value="{$PlaceID}"
/>
</div>
<% end_if %>

<div id="map-field"><a href="javascript:window.location.href=window.location.href">Click to reload map</a></div>

<% if $MapType == 'places' %>
<div style="display: none;">
<div id="infowindow-content">
<span id="place-address"></span>
<br /><br />
<strong>Place ID:</strong> <a href="" id="place-id"></a>
</div>
</div>
<% end_if %>
</div>

0 comments on commit 5bd8ffd

Please sign in to comment.