Skip to content

Instantly share code, notes, and snippets.

@CarlBoneri
Created July 12, 2016 23:26
Show Gist options
  • Save CarlBoneri/b111b262a43d1505b7861fe1f5835d34 to your computer and use it in GitHub Desktop.
Save CarlBoneri/b111b262a43d1505b7861fe1f5835d34 to your computer and use it in GitHub Desktop.
Leaflet Material Controls
<html>
<head>
<meta charset=utf-8 />
<title>A simple map</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.2.1/mapbox.css' rel='stylesheet' />
<link rel="stylesheet" href="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.indigo-pink.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link href='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v0.0.4/leaflet.fullscreen.css' rel='stylesheet'>
<style>
#map {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
content: '';
}
.leaflet-bar-mdl.leaflet-control {
margin: 8px 16px 8px 16px;
}
.leaflet-bar-mdl.leaflet-control button {
margin-bottom: 16px;
display: block;
}
@media (min-width: 840px) {
.leaflet-bar-mdl.leaflet-control {
margin: 24px 24px 0 24px;
}
}
</style>
</head>
<body>
<div id='map'></div>
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.2.1/mapbox.js'></script>
<script src="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.min.js"></script>
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v0.0.4/Leaflet.fullscreen.min.js'></script>
<!-- <script src="/Users/Chris/Documents/Projects/aeroster/dist/js/leaflet-material-min.js"></script> -->
<script>
L.Control.Attribution.prototype.options.position = "bottomleft";
L.mapbox.accessToken = 'pk.eyJ1IjoiY3RpcHBldHQiLCJhIjoiS3lpTnN4MCJ9.YG_uH8r7IgwgcSWEPYROMA';
var map = L.mapbox.map('map', null ,{zoomControl: false, attributionControl: true})
.setView([40, -74.50], 9);
var layers = {
Streets: L.mapbox.tileLayer('mapbox.streets'),
Outdoors: L.mapbox.tileLayer('mapbox.outdoors'),
Satellite: L.mapbox.tileLayer('mapbox.satellite')
};
layers.Streets.addTo(map);
</script>
</body>
</html>
'use strict';
L.Control.include({
defaultMaterialOptions: {
fab: true,
miniFab: true,
rippleEffect: true,
toolTips: false,
color: 'lime-A200'
},
_setDefaultOptions: function () {
var materialOptions = {};
for (var attrname in this.defaultMaterialOptions) {
materialOptions[attrname] = this.defaultMaterialOptions[attrname];
};
if (typeof this.options.materialOptions !== 'undefined') {
for (var attrname in this.options.materialOptions) {
materialOptions[attrname] = this.options.materialOptions[attrname];
}
};
return materialOptions;
},
_addToolTip: function (el, idTag, toolTipText) {
var parentNode = el.parentNode,
toolTipNode = L.DomUtil.create('div', 'mdl-tooltip', parentNode);
toolTipNode.setAttribute('for', idTag);
toolTipNode.innerHTML = toolTipText;
return toolTipNode;
},
_createMaterialButton: function (idTag, iconText, title, container, optionalClass) {
var materialClass = 'mdl-button mdl-js-button mdl-button--icon',
materialOptions = this._setDefaultOptions(),
button = L.DomUtil.create('button', materialClass, container);
this._materialIcon = L.DomUtil.create('i', 'material-icons', button);
this._materialIcon.innerHTML = iconText;
button.title = title;
button.id = idTag;
if (materialOptions.fab) {
L.DomUtil.removeClass(button, 'mdl-button--icon');
L.DomUtil.addClass(button, 'mdl-button--fab');
if (materialOptions.miniFab) {
L.DomUtil.addClass(button, 'mdl-button--mini-fab');
}
}
if (materialOptions.rippleEffect) {
L.DomUtil.addClass(button, 'mdl-js-ripple-effect');
}
if (materialOptions.color) {
var colorClass;
if (typeof materialOptions.color === "boolean") {
colorClass = 'mdl-button--colored';
} else {
switch (materialOptions.color) {
case "colored":
colorClass = 'mdl-button--colored';
break;
case "primary":
colorClass = 'mdl-button--primary';
break;
case "accent":
colorClass = "mdl-button--accent";
break;
default:
colorClass = 'mdl-color--' + materialOptions.color;
}
}
L.DomUtil.addClass(button, colorClass);
}
if (optionalClass) {
L.DomUtil.addClass(button, optionalClass);
}
if (materialOptions.toolTips) {
this._materialToolTip = this._addToolTip(button, idTag, title);
button.removeAttribute('title');
}
this._materialButton = button;
return this._materialButton;
}
});
L.Control.MaterialFullscreen = L.Control.Fullscreen.extend({
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-control-fullscreen-mdl leaflet-bar-mdl'),
options = this.options,
button = this._createMaterialButton('leaflet-fullscreen-mdl', 'fullscreen', options.title['false'], container);
this._map = map;
this._map.on('fullscreenchange', this._toggleTitle, this);
this._toggleTitle();
L.DomEvent.on(button, 'click', this._click, this);
return container;
},
_toggleTitle: function () {
var fullScreenIcon = {
'false': 'fullscreen',
'true': 'fullscreen_exit'
};
this._materialButton.title = this.options.title[this._map.isFullscreen()];
this._materialIcon.innerHTML = fullScreenIcon[this._map.isFullscreen()];
if (this._materialToolTip) {
this._materialToolTip.innerHTML = this.options.title[this._map.isFullscreen()];
this._materialButton.removeAttribute('title');
}
}
});
L.Control.MaterialZoom = L.Control.Zoom.extend({
onAdd: function (map) {
var container = L.DomUtil.create('div','leaflet-control-zoom-mdl leaflet-bar-mdl'),
options = this.options;
options.zoomInText = options.zoomInText === "+" ? 'add' : options.zoomInText;
options.zoomOutText = options.zoomOutText === "-" ?'remove' : options.zoomInText;
this._zoomInButton = this._createMaterialButton('leaflet-zoom-in-mdl', options.zoomInText, options.zoomInTitle, container);
this._zoomOutButton = this._createMaterialButton('leaflet-zoom-out-mdl', options.zoomOutText, options.zoomOutTitle, container);
this._addZoomFunction(this._zoomInButton, this._zoomIn);
this._addZoomFunction(this._zoomOutButton, this._zoomOut);
this._updateDisabled();
map.on('zoomend zoomlevelschange', this._updateDisabled, this);
return container;
},
_addZoomFunction: function (button, fn) {
L.DomEvent
.on(button, 'mousedown dblclick', L.DomEvent.stopPropagation)
.on(button, 'click', L.DomEvent.stop)
.on(button, 'click', fn, this)
.on(button, 'click', this._refocusOnMap, this);
return button;
},
_updateDisabled: function () {
var map = this._map;
this._zoomInButton.removeAttribute('disabled');
this._zoomOutButton.removeAttribute('disabled');
if (this._disabled || map._zoom === map.getMinZoom()) {
this._zoomOutButton.setAttribute('disabled', true);
}
if (this._disabled || map._zoom === map.getMaxZoom()) {
this._zoomInButton.setAttribute('disabled', true);
}
}
});
L.Control.MaterialLayers = L.Control.Layers.extend({
options: {
collapsed: true,
position: 'topright',
autoZIndex: true,
hideSingleBase: false
},
_initLayout: function () {
var className = 'leaflet-control-layers-mdl',
container = this._container = L.DomUtil.create('div', className + ' leaflet-bar-mdl'),
buttonId = 'leaflet-control-layers-toggle-mdl';
// adjust timeout time for material menu
MaterialMenu.prototype.Constant_ = {
// Total duration of the menu animation.
TRANSITION_DURATION_SECONDS: 0.3,
// The fraction of the total duration we want to use for menu item animations.
TRANSITION_DURATION_FRACTION: 0.8,
// How long the menu stays open after choosing an option (so the user can see
// the ripple).
CLOSE_TIMEOUT: 600
};
// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
container.setAttribute('aria-haspopup', true);
if (!L.Browser.touch) {
L.DomEvent
.disableClickPropagation(container)
.disableScrollPropagation(container);
} else {
L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
}
// create material menu
var menuClass = 'mdl-menu mdl-js-menu mdl-js-menu';
var form = this._form = L.DomUtil.create('ul', menuClass);
form.setAttribute('for', buttonId)
if (this.options.materialOptions.rippleEffect) {
L.DomUtil.addClass(form, 'mdl-js-ripple-effect');
}
switch (this.options.position) {
case 'topleft':
L.DomUtil.addClass(form, 'mdl-menu--bottom-left');
break;
case 'topright':
L.DomUtil.addClass(form, 'mdl-menu--bottom-right');
break;
case 'bottomright':
L.DomUtil.addClass(form, 'mdl-menu--top-right');
break;
case 'bottomleft':
L.DomUtil.addClass(form, 'mdl-menu--top-left');
break;
}
var link = this._layersLink = this._createMaterialButton(buttonId, 'layers', 'Layers', container);
this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
this._separator = L.DomUtil.create('div', className + '-separator', form);
this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
var separatorItem = L.DomUtil.create('li', 'mdl-menu__divider', this._separator);
separatorItem.innerHTML = '<hr />';
container.appendChild(form);
},
_addItem: function (obj) {
var listClass = 'mdl-menu__item',
listItem = L.DomUtil.create('li', listClass),
label = document.createElement('label'),
checked = this._map.hasLayer(obj.layer),
spanLabelClass,
inputId,
input;
if (this.options.materialOptions.rippleEffect) {
L.DomUtil.addClass(label, 'mdl-js-ripple-effect');
}
if (obj.overlay) {
L.DomUtil.addClass(label, 'mdl-checkbox mdl-js-checkbox');
inputId = 'mdl-layer-checkbox-' + L.stamp(obj.layer);
label.setAttribute('for', inputId);
input = L.DomUtil.create('input', 'mdl-checkbox__input');
input.type = 'checkbox';
input.defaultChecked = checked;
input.id = inputId;
spanLabelClass = 'mdl-checkbox__label';
} else {
L.DomUtil.addClass(label, 'mdl-radio mdl-js-radio');
inputId = 'mdl-layer-option-' + L.stamp(obj.layer);;
label.setAttribute('for', inputId);
listItem.innerHTML = this._createRadioElement('leaflet-base-layers', checked);
input = listItem.firstChild;
input.id = inputId;
spanLabelClass = 'mdl-radio__label';
}
input.layerId = L.stamp(obj.layer);
L.DomEvent.on(input, 'click', this._onInputClick, this);
var name = L.DomUtil.create('span', spanLabelClass);
name.innerHTML = obj.name;
label.appendChild(input);
label.appendChild(name);
listItem.appendChild(label);
var container = obj.overlay ? this._overlaysList : this._baseLayersList;
container.appendChild(listItem);
return listItem;
},
// IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
_createRadioElement: function (name, checked) {
var radioHtml = '<input type="radio" class="mdl-radio__button" name="' +
name + '"' + (checked ? ' checked="checked"' : '') + '/>';
return radioHtml;
},
});
L.Control.MaterialGeocoderControl = L.mapbox.GeocoderControl.extend({
onAdd: function(map) {
var container = L.DomUtil.create('div', 'leaflet-control-geocoder-mdl leaflet-bar-mdl leaflet-control'),
form = L.DomUtil.create('form', 'geocoder-form', container),
textField = L.DomUtil.create('div', 'mdl-textfield mdl-js-textfield mdl-shadow--2dp', form),
input = L.DomUtil.create('input', 'mdl-textfield__input', textField),
label = L.DomUtil.create('label', 'mdl-textfield__label', textField),
icon = L.DomUtil.create('i', 'material-icons', label),
clearSearchButton = L.DomUtil.create('button', 'mdl-button mdl-js-button mdl-button--icon clear-search', textField),
clearSearchIcon = L.DomUtil.create('i', 'material-icons', clearSearchButton),
progressBar = L.DomUtil.create('div', 'mdl-progress mdl-js-progress mdl-progress__indeterminate', textField),
results = L.DomUtil.create('div', 'leaflet-control-geocoder-results-mdl mdl-shadow--2dp', container);
icon.innerHTML = 'search';
clearSearchIcon.innerHTML = 'close';
label.setAttribute('for', 'mapbox-geocoder-mdl')
input.setAttribute('id', 'mapbox-geocoder-mdl')
input.type = 'text';
input.setAttribute('placeholder', 'Search...');
// L.DomUtil.addClass(results, 'show-results');
// this._map.on('click', this._closeResults, this);
clearSearchButton.type = "button";
L.DomEvent.addListener(clearSearchButton, 'click', this._clearSearch, this);
L.DomEvent.addListener(form, 'submit', this._geocode, this);
L.DomEvent.addListener(input, 'keyup', this._autocomplete, this);
L.DomEvent.disableClickPropagation(container);
this._textField = textField;
this._map = map;
this._results = results;
this._input = input;
this._form = form;
return container;
},
_clearSearch: function() {
this._input.value = null;
L.DomUtil.removeClass(this._textField, 'is-dirty');
this._closeResults();
},
_closeResults: function() {
L.DomUtil.removeClass(this._results, 'show-results');
// have results fade out before removing result elements
if (this._results.childNodes.length > 0) {
setTimeout(function(results) {
while (results.firstChild) {
results.removeChild(results.firstChild);
};
}, 500, this._results);
}
},
_displayResults: function(features) {
L.DomUtil.addClass(this._results, 'show-results');
if (features.length === 0) {
var message = L.DomUtil.create('span', 'no-results-message', this._results);
message.innerHTML = 'No results found.';
} else {
L.mapbox.GeocoderControl.prototype._displayResults.apply(this, [features]);
}
},
});
// =================================
var materialOptions = {
fab: true,
miniFab: true,
rippleEffect: true,
toolTips: false,
color: 'primary'
}
// Material zoom control:
var materialZoomControl = new L.Control.MaterialZoom({position: 'topright' }).addTo(map);
// Material fullscreen control:
var materialFullscreen = new L.Control.MaterialFullscreen({position: 'topright' }).addTo(map);
// Material layer control:
var materialLayerControl = new L.Control.MaterialLayers(layers, null, {position: 'bottomright', materialOptions: materialOptions}).addTo(map);
// Search location (geocode) control
var materialGeocodeControl = new L.Control.MaterialGeocoderControl('mapbox.places', {position: 'topleft', autoComplete: true}).addTo(map);
.mdl-menu--bottom-right {
margin-top: -40px !important;
margin-left: -50px !important;
margin-right: 50px !important;
}
.mdl-menu--bottom-left {
margin-top: -40px !important;
margin-left: 50px !important;
margin-right: -50px !important;
}
.mdl-menu--top-left {
margin-top: 40px !important;
margin-bottom: -40px !important;
margin-left: 50px !important;
margin-right: -50px !important;
}
.mdl-menu--top-right {
margin-top: 40px !important;
margin-bottom: -40px !important;
margin-left: -50px !important;
margin-right: 50px !important;
}
/* MATERIAL GEOCODER CONTROL */
form.geocoder-form div.mdl-textfield, .leaflet-control-geocoder-results-mdl {
background-color: white;
padding: 10px;
padding-left: 12px;
overflow: hidden;
width: 300px;
}
.leaflet-control-geocoder-results-mdl {
padding: 5px 0 5px 0;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s linear, visibility 0s linear 0.2s;
}
.leaflet-control-geocoder-results-mdl.show-results {
visibility: visible;
opacity: 1;
transition-delay:0s;
}
.leaflet-control-geocoder-results-mdl > * {
display: block;
padding: 5px 10px 5px 10px;
}
.leaflet-control-geocoder-results-mdl a:hover {
background-color: #fafafa;
}
form.geocoder-form .mdl-textfield .mdl-textfield__label {
top: 12px;
padding-left: 12px;
}
form.geocoder-form .mdl-textfield .mdl-textfield__label:after {
bottom: 0;
}
form.geocoder-form .mdl-textfield.is-dirty .mdl-textfield__label {
visibility: visible;
}
form.geocoder-form .mdl-textfield .mdl-textfield__input {
border-bottom: none;
padding-left: 35px;
padding-right: 35px;
}
form.geocoder-form .mdl-button.clear-search {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
margin-right: 5px;
visibility: hidden;
}
form.geocoder-form .is-dirty .mdl-button.clear-search {
visibility: visible;
}
form.geocoder-form .mdl-progress {
height: 2px;
position: absolute;
bottom: 0;
left: 0;
visibility: hidden;
}
.searching form.geocoder-form .mdl-progress {
visibility: visible;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment