8 changed files with 495 additions and 37 deletions
@ -0,0 +1,235 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import L, { TB } from 'leaflet'; |
||||
|
|
||||
|
class SidebarControl extends L.Control<TB.SidebarControlOptions> { |
||||
|
|
||||
|
private readonly sidebar: JQuery<HTMLElement>; |
||||
|
|
||||
|
private current = $(); |
||||
|
private currentButton = $(); |
||||
|
|
||||
|
private map: L.Map; |
||||
|
|
||||
|
constructor(options: TB.SidebarControlOptions) { |
||||
|
super(options); |
||||
|
this.sidebar = $('<div class="tb-map-sidebar"></div>'); |
||||
|
this.options.container.append(this.sidebar); |
||||
|
const position = options?.position || 'topleft'; |
||||
|
if (['topleft', 'bottomleft'].includes(position)) { |
||||
|
this.options.container.addClass('tb-sidebar-left'); |
||||
|
} else { |
||||
|
this.options.container.addClass('tb-sidebar-right'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addPane(pane: JQuery<HTMLElement>): this { |
||||
|
pane.hide().appendTo(this.sidebar); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
togglePane(pane: JQuery<HTMLElement>, button: JQuery<HTMLElement>) { |
||||
|
const paneWidth = this.options?.paneWidth || 220; |
||||
|
const position = this.options?.position || 'topleft'; |
||||
|
|
||||
|
this.current.hide().trigger('hide'); |
||||
|
this.currentButton.removeClass('active'); |
||||
|
|
||||
|
if (this.current === pane) { |
||||
|
if (['topleft', 'bottomleft'].includes(position)) { |
||||
|
this.map.panBy([-paneWidth, 0], { animate: false }); |
||||
|
} |
||||
|
this.sidebar.hide(); |
||||
|
this.current = this.currentButton = $(); |
||||
|
} else { |
||||
|
this.sidebar.show(); |
||||
|
this.current = pane; |
||||
|
this.currentButton = button || $(); |
||||
|
if (['topleft', 'bottomleft'].includes(position)) { |
||||
|
this.map.panBy([paneWidth, 0], { animate: false }); |
||||
|
} |
||||
|
} |
||||
|
this.map.invalidateSize({ pan: false, animate: false }); |
||||
|
this.current.show().trigger('show'); |
||||
|
this.currentButton.addClass('active'); |
||||
|
} |
||||
|
|
||||
|
addTo(map: L.Map): this { |
||||
|
this.map = map; |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class SidebarPaneControl<O extends TB.SidebarPaneControlOptions> extends L.Control<O> { |
||||
|
|
||||
|
private button: JQuery<HTMLElement>; |
||||
|
private $ui: JQuery<HTMLElement>; |
||||
|
|
||||
|
constructor(options: O) { |
||||
|
super(options); |
||||
|
} |
||||
|
|
||||
|
onAdd(map: L.Map): HTMLElement { |
||||
|
const $container = $("<div>") |
||||
|
.attr('class', 'leaflet-bar'); |
||||
|
|
||||
|
this.button = $("<a>") |
||||
|
.attr('class', 'tb-control-button') |
||||
|
.attr('href', '#') |
||||
|
.html('<div class="' + this.options.uiClass + '"></div>') |
||||
|
.on('click', (e) => { |
||||
|
this.toggle(e); |
||||
|
}); |
||||
|
if (this.options.buttonTitle) { |
||||
|
this.button.attr('title', this.options.buttonTitle); |
||||
|
} |
||||
|
this.button.appendTo($container); |
||||
|
|
||||
|
this.$ui = $('<div>') |
||||
|
.attr('class', this.options.uiClass); |
||||
|
|
||||
|
$('<div class="tb-layers-title-container">') |
||||
|
.appendTo(this.$ui) |
||||
|
.append($('<div class="tb-layers-title">') |
||||
|
.text(this.options.paneTitle)) |
||||
|
.append($('<div>') |
||||
|
.append($('<button type="button" class="tb-button-close mdc-icon-button mat-mdc-icon-button">' + |
||||
|
'<span class="mat-mdc-button-persistent-ripple mdc-icon-button__ripple"></span>' + |
||||
|
'<span class="material-icons">close</span>' + |
||||
|
'</button>') |
||||
|
.attr('aria-label', 'Close') |
||||
|
.bind('click', (e) => { |
||||
|
this.toggle(e); |
||||
|
}))); |
||||
|
|
||||
|
this.options.sidebar.addPane(this.$ui); |
||||
|
|
||||
|
this.onAddPane(map, this.button, this.$ui, (e) => { |
||||
|
this.toggle(e); |
||||
|
}); |
||||
|
|
||||
|
return $container[0]; |
||||
|
} |
||||
|
|
||||
|
public onAddPane(map: L.Map, button: JQuery<HTMLElement>, $ui: JQuery<HTMLElement>, toggle: (e: JQuery.MouseEventBase) => void) {} |
||||
|
|
||||
|
private toggle(e: JQuery.MouseEventBase) { |
||||
|
e.stopPropagation(); |
||||
|
e.preventDefault(); |
||||
|
if (!this.button.hasClass("disabled")) { |
||||
|
this.options.sidebar.togglePane(this.$ui, this.button); |
||||
|
} |
||||
|
//$(".leaflet-control .control-button").tooltip("hide");
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class LayersControl extends SidebarPaneControl<TB.LayersControlOptions> { |
||||
|
constructor(options: TB.LayersControlOptions) { |
||||
|
super(options); |
||||
|
} |
||||
|
|
||||
|
public onAddPane(map: L.Map, button: JQuery<HTMLElement>, $ui: JQuery<HTMLElement>, toggle: (e: JQuery.MouseEventBase) => void) { |
||||
|
const layers = this.options.layers; |
||||
|
const baseSection = $("<div>") |
||||
|
.attr('class', 'tb-layers-container') |
||||
|
.appendTo($ui); |
||||
|
|
||||
|
layers.forEach((layerData, i) => { |
||||
|
const id = 'map-ui-layer-' + i; |
||||
|
const buttonContainer = $('<div class="tb-layer-card">') |
||||
|
.appendTo(baseSection); |
||||
|
const mapContainer = $('<div class="tb-layer-map">') |
||||
|
.appendTo(buttonContainer); |
||||
|
const input = $('<input type="radio" class="tb-layer-button" name="layer">') |
||||
|
.prop('id', id) |
||||
|
.prop('checked', map.hasLayer(layerData.layer)) |
||||
|
.appendTo(buttonContainer); |
||||
|
|
||||
|
const item = $('<label class="tb-layer-label">') |
||||
|
.prop('for', id) |
||||
|
.append($('<span>').append(layerData.title)) |
||||
|
.appendTo(buttonContainer); |
||||
|
|
||||
|
map.whenReady(() => { |
||||
|
|
||||
|
const miniMap = L.map(mapContainer[0], { attributionControl: false, zoomControl: false, keyboard: false }) |
||||
|
.addLayer(layerData.mini); |
||||
|
|
||||
|
miniMap.dragging.disable(); |
||||
|
miniMap.touchZoom.disable(); |
||||
|
miniMap.doubleClickZoom.disable(); |
||||
|
miniMap.scrollWheelZoom.disable(); |
||||
|
|
||||
|
const moved = () => { |
||||
|
miniMap.setView(map.getCenter(), Math.max(map.getZoom() - 2, 0)); |
||||
|
}; |
||||
|
|
||||
|
const shown = () => { |
||||
|
miniMap.invalidateSize(); |
||||
|
miniMap.setView(map.getCenter(), Math.max(map.getZoom() - 2, 0), { animate: false }); |
||||
|
map.on('moveend', moved); |
||||
|
}; |
||||
|
|
||||
|
$ui.on('show', shown); |
||||
|
$ui.on('hide', () => { |
||||
|
map.off('moveend', moved); |
||||
|
}) |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
input.on('click', () => { |
||||
|
layers.forEach((other) => { |
||||
|
if (other.layer === layerData.layer) { |
||||
|
map.addLayer(other.layer); |
||||
|
} else { |
||||
|
map.removeLayer(other.layer); |
||||
|
} |
||||
|
}); |
||||
|
map.fire('baselayerchange', { layer: layerData.layer }); |
||||
|
}); |
||||
|
|
||||
|
item.on('dblclick', (e) => { |
||||
|
toggle(e); |
||||
|
}); |
||||
|
|
||||
|
map.on('layeradd layerremove', function () { |
||||
|
input.prop('checked', map.hasLayer(layerData.layer)); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const sidebar = (options: TB.SidebarControlOptions): SidebarControl => { |
||||
|
return new SidebarControl(options); |
||||
|
} |
||||
|
|
||||
|
const sidebarPane = <O extends TB.SidebarPaneControlOptions>(options: O): SidebarPaneControl<O> => { |
||||
|
return new SidebarPaneControl(options); |
||||
|
} |
||||
|
|
||||
|
const layers = (options: TB.LayersControlOptions): LayersControl => { |
||||
|
return new LayersControl(options); |
||||
|
} |
||||
|
|
||||
|
L.TB = L.TB || { |
||||
|
SidebarControl, |
||||
|
SidebarPaneControl, |
||||
|
LayersControl, |
||||
|
sidebar, |
||||
|
sidebarPane, |
||||
|
layers |
||||
|
} |
||||
Loading…
Reference in new issue