import { defaultValue, Evented, Log } from '@mapgis/webclient-common'
import * as L from '@mapgis/leaflet'
/**
* 二维场景信息弹窗(leaflet引擎)
* 参考示例:
* <a href='#MapView'>[初始化二维场景视图]</a>
* [ES6引入方式]:<br/>
* import { Popup } from '@mapgis/webclient-leaflet-plugin' <br/>
* 自定义样式说明:<br/>
* zondy-popup__content 弹窗容器样式<br/>
* zondy-popup__tip 弹窗对话框箭头样式<br/>
* zondy-popup__header 弹窗头部样式<br/>
* zondy-popup__content 弹窗主体样式<br/>
* zondy-popup__footer 弹窗底部样式<br/>
* @class Popup
* @moduleEX ViewModule
* @param {Object} options 构造参数
* @param {MapView} [options.view] 弹窗地图视图对象
* @param {String} [options.id] 弹窗ID
* @param {Point} [options.location] 弹窗定位点
* @param {String} [options.title] 弹窗标题
* @param {String|HTMLElement} [options.content] 弹框内容
* @param {String} [options.alignment] 弹框方位,值为"auto"或"bottom-left"或"top-center"或"top-right"或"bottom-left"或"bottom-center"或"bottom-right"。
* @param {Array} [options.defaultButtons] 弹框内容,可选数组项有'toggle','close','zoom'。toggle:收缩主题内容按钮;close:关闭按钮;zoom:缩放按钮。
*
* @example <caption><h7 id='Popup'>打开视图弹窗</h7></caption>
* // ES5引入方式
* const { Map, MapView } = Zondy
* // ES6引入方式
* import { Map, MapView } from "@mapgis/webclient-leaflet-plugin"
* // 初始化图层管理容器
* const map = new Map();
* // 初始化地图视图对象
* const mapView = new MapView({
* // 二维场景视图的容器(html的div标签)ID
* viewId: "二维场景视图的容器的id",
* // 图层管理容器
* map: map
* });
* const popupObj = {
* title: "标题",
* content:"弹出窗口主题内容,详细内容",
* location: new Point({coordinates:[123,34]}),
* alignment: "bottom-center"
*}
* mapView.popup.open(popupObj)
* mapView.popup.close(popupObj)
* <style>
* .zondy-popup__container{
* background-color: #40a9ff;
* margin: 0
* }
* .zondy-popup__tip{
* // display: none;
* background-color: #ffffff;
* }
* .zondy-popup__header{
* background-color: #40a9ff;
* }
* .zondy-popup__content{
* background-color: #ffffff;
* }
* .zondy-popup__footer{
* // display: none;
* background-color: #40a9ff;
* }
* </style>
*/
class Popup extends Evented {
constructor(options) {
super(options)
/**
* 弹框的地图视野
* @member {MapView}
*/
this.view = defaultValue(options.view, null)
// /**
// * 弹框唯一id
// * @member {String}
// */
// this.id = this._id
/**
* 弹框位置
* @member {String}
*/
this.location = defaultValue(options.location, '')
/**
* 弹框标题
* @member {String}
*/
this.title = defaultValue(options.title, '')
/**
* 弹框内容
* @member {String}
*/
this.content = defaultValue(options.content, '')
// /**
// * 弹框按钮事件
// * @member {String}
// */
// this.action = undefined
/**
* 弹框方位,值为"auto"|"bottom-left"|"top-center"|"top-right"|"bottom-left"|"bottom-center"|"bottom-right"
* @member {String}
*/
this.alignment = defaultValue(options.alignment, 'bottom-center')
/**
* 默认按钮 ['toggle','close','zoom']
* @member {Array}
*/
this.defaultButtons = defaultValue(options.defaultButtons, [
'toggle',
'close',
'zoom'
])
this._popup = undefined
// 初始化弹窗
this._initPopup()
// 设置popup自定义class样式
this._initDefaultClass()
}
/**
* 初始化弹窗
* @private
* */
_initPopup() {
this._customPopup()
// 关闭地图点击关闭弹窗事件
this.view._innerView.closePopupOnClick = false
}
/**
* 自定义弹窗样式,修改leaflet默认样式
* @private
* */
_customPopup() {
L.CustomPopup = L.Popup.extend({
_initLayout() {
const prefix = 'leaflet-popup'
const container = L.DomUtil.create(
'div',
`${prefix} ${this.options.className || ''} leaflet-zoom-animated`
)
this._container = container
container.setAttribute('style', 'margin: 0;')
// if (this.options.closeButton) {
// const closeButton = L.DomUtil.create(
// 'a',
// `${prefix}-close-button`,
// container
// )
// this._closeButton = closeButton
// closeButton.href = '#close'
// closeButton.innerHTML = '×'
// closeButton.setAttribute('style', 'top:12px;right:8px')
// // L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this)
// }
const wrapper = L.DomUtil.create(
'div',
`zondy-popup__container ${prefix}-content-wrapper`,
container
)
this._wrapper = wrapper
this._contentNode = L.DomUtil.create(
'div',
`${prefix}-content`,
wrapper
)
this._contentNode.setAttribute('style', 'margin:0')
L.DomEvent.disableClickPropagation(wrapper)
.disableScrollPropagation(this._contentNode)
.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation)
// this._tipContainer = L.DomUtil.create(
// 'div',
// `${prefix}-tip-container`,
// container
// )
// this._tip = L.DomUtil.create('div', `zondy-popup__tip ${prefix}-tip`, this._tipContainer)
this._tipContainer = L.DomUtil.create('div', '', container)
this._tipContainer.setAttribute(
'style',
'margin: auto;width: 40px;height: 20px;pointer-events: none;overflow: hidden;pointer-events: none;'
)
this._tip = L.DomUtil.create(
'div',
`zondy-popup__tip ${prefix}-tip`,
this._tipContainer
)
}
})
}
/**
* 初始化弹窗默认面板
* @private
* */
_contentUI(title, content) {
const self = this
let defaultUI = undefined
const string2Dom = function (arg) {
const objE = document.createElement('div')
objE.innerHTML = arg
return objE.childNodes
}
defaultUI = document.createElement('div')
defaultUI.setAttribute('id', `${this.id}-popup-default`)
// 构建头部DOM
const headerDom = document.createElement('div')
headerDom.setAttribute('class', 'zondy-popup__header')
headerDom.setAttribute('style', 'display: flex;')
// 收缩按钮
const toggleDom = string2Dom(`<div class="zondy-popup__toggle">
<svg aria-hidden="true" class="svg" fill="currentColor" height="16px" width="16px" viewBox="0 0 24 24" width="100%" xmlns="http://www.w3.org/2000/svg"><path d="M5 13.793l7-7 7 7v1.414l-7-7-7 7z"></path></svg>
</div>`)[0]
// 关闭按钮
const closeDom = string2Dom(`<div class="zondy-popup__close">
<svg aria-hidden="true" class="svg" fill="currentColor" height="16px" width="16px" viewBox="0 0 16 16" width="100%" xmlns="http://www.w3.org/2000/svg"><path d="M3.98 11.303L7.281 8 3.98 4.697l.707-.707L7.99 7.293l.01-.01.01.01 3.304-3.303.707.707L8.718 8l3.303 3.303-.707.707L8.01 8.707l-.01.01-.01-.01-3.304 3.303z"></path></svg>
</div>`)[0]
if (title) {
const titleDom = string2Dom(
`<div class="zondy-popup__title" style="flex: 1;"></div>`
)[0]
headerDom.appendChild(titleDom)
if (typeof title === 'object') {
if (title instanceof Node) {
titleDom.appendChild(title)
} else if (title instanceof NodeList) {
title.forEach((dom) => {
titleDom.appendChild(dom)
})
}
} else {
titleDom.innerHTML = title
}
if (this.defaultButtons.indexOf('toggle') > -1) {
headerDom.appendChild(toggleDom)
// 收缩按钮事件
let showContentFlag = true
headerDom.onclick = function () {
showContentFlag = !showContentFlag
if (showContentFlag) {
document
.getElementById(`${self.id}-popup-default`)
.querySelector('.zondy-popup__content').style.display = 'block'
} else {
document
.getElementById(`${self.id}-popup-default`)
.querySelector('.zondy-popup__content').style.display = 'none'
}
}
}
if (this.defaultButtons.indexOf('close') > -1) {
headerDom.appendChild(closeDom)
// 关闭按钮事件
closeDom.onclick = () => {
this.close()
}
}
}
// 构建中间内容DOM
const contentDom = document.createElement('div')
contentDom.setAttribute('class', 'zondy-popup__content')
if (content) {
if (typeof content === 'object') {
if (content instanceof Node) {
contentDom.appendChild(content)
} else if (content instanceof NodeList) {
// content.forEach((dom)=>{
// contentDom.appendChild(dom)
// })
for (let i = 0; i < content.length; i++) {
contentDom.appendChild(content[i])
i--
}
}
} else {
contentDom.innerHTML = content
}
}
// 构建底部页脚DOM
const footerDom = document.createElement('div')
footerDom.setAttribute('class', 'zondy-popup__footer')
// 缩放按钮
const zoomDom =
string2Dom(`<div class="zondy-popup__zoom" style="display: inline-block;width: 36px;height: 36px;line-height: 36px;text-align: center;">
<svg class="svg" fill="currentColor" height="16px" width="16px" viewBox="0 0 16 16" width="100%" xmlns="http://www.w3.org/2000/svg"><path d="M9 7H7v2H6V7H4V6h2V4h1v2h2zm6.805 7.861l-.943.942a.665.665 0 0 1-.943 0l-3.067-3.067a.667.667 0 0 1 0-.943l.129-.13-1.108-1.107A5.279 5.279 0 1 1 11.8 6.5a5.251 5.251 0 0 1-1.237 3.366l1.108 1.108.124-.124a.668.668 0 0 1 .943 0l3.067 3.068a.666.666 0 0 1 0 .943zM10.8 6.5a4.3 4.3 0 1 0-4.3 4.3 4.304 4.304 0 0 0 4.3-4.3zm4.062 7.89l-2.595-2.598-.473.473 2.597 2.595z"></path></svg>
</div>`)[0]
if (this.defaultButtons.indexOf('zoom') > -1) {
footerDom.appendChild(zoomDom)
// 缩放按钮事件
zoomDom.onclick = function () {
self.view.flyTo({ zoom: self.view._zoom + 1, center: self.location })
}
}
// footer有子节点,增加分割线样式
if (footerDom.childNodes.length > 0) {
footerDom.setAttribute(
'class',
'zondy-popup__footer zondy-popup__footer-division'
)
}
defaultUI.appendChild(headerDom)
defaultUI.appendChild(contentDom)
defaultUI.appendChild(footerDom)
// // tip三角
// const tipDom = document.createElement('div')
// tipDom.setAttribute('style', 'width: 40px;height: 20px;position: absolute;left: 50%;margin-top: -1px;margin-left: -20px;overflow: hidden;pointer-events: none;bottom: -3px;')
// tipDom.innerHTML = `<div class='zondy-popup__tip' style='width: 17px;height: 17px;padding: 1px;margin: -10px auto 0;pointer-events: auto;transform: rotate(45deg);'>
// </div>`
// defaultUI.appendChild(tipDom)
return defaultUI
}
/**
* 设置popup自定义class样式
* @private
*/
_initDefaultClass() {
const styleDomString = `
.zondy-popup__container{background-color: #fff;padding:0;border-radius: 10px;}
.zondy-popup__tip{
background-color: #fff;
}
.zondy-popup__header{
padding: 8px;
font-size: 18px;
font-weight: 600;
line-height: 20px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.zondy-popup__content{
padding: 8px;
}
.zondy-popup__footer{
}
.zondy-popup__footer-division{
border-top: 1px solid;
}
.zondy-popup__toggle,.zondy-popup__close{
display: inline-block;width: 30px;text-align: center;
}
`
let styleExist = false
const styles = document.head.getElementsByTagName('style')
if (styles) {
for (let i = 0; i < styles.length; i++) {
if (styles[i].innerHTML.indexOf('styleDomString') > -1) {
styleExist = true
break
}
}
}
if (!styleExist) {
const styleDom = document.createElement('style')
styleDom.innerHTML = styleDomString
document.querySelector('head').append(styleDom)
}
}
/**
* 打开popup弹窗
* @param {Object} options 弹窗属性对象
* */
open(options) {
if (this._popup) {
this.close()
this._popup = undefined
this.openPopup(options)
} else {
this.openPopup(options)
}
}
/**
* 打开popup弹窗
* @param {Object} options 弹窗属性对象
* */
openPopup(options) {
const self = this
this.view = options.view ? options.view : this.view
this.location = options.location ? options.location : this.location
this.title = options.title ? options.title : this.title
this.content = options.content ? options.content : this.content
this.alignment = options.alignment ? options.alignment : this.alignment
if (!this.view._innerView) {
Log.info('没有视图引擎!')
return
}
const latlng = L.latLng(
this.location.coordinates[1],
this.location.coordinates[0]
)
const content = this._contentUI(this.title, this.content)
this._popup = new L.CustomPopup({
className: 'layerPopup',
// autoClose: false,
maxWidth: 1900,
minWidth: 50,
closeOnClick: false
})
.setLatLng(latlng)
.setContent(content)
.openOn(this.view._innerView)
// this._popup = L.popup().setLatLng(latlng).setContent(content).openOn(this.view)
const offset = this._getOffset()
this._popup = new L.CustomPopup({
className: 'layerPopup',
// autoClose: false,
maxWidth: 1900,
minWidth: 50,
closeOnClick: false,
offset
})
.setLatLng(latlng)
.setContent(content)
.openOn(this.view._innerView)
}
/**
* 获取偏移对象
* @private
*/
_getOffset() {
let offset = null
if (this._popup && this._popup._container) {
if (this.alignment) {
const rect = this._popup._container.getBoundingClientRect()
const offsetXY = this._getOffsetByAlignment(
this.alignment,
rect.width,
rect.height
)
offset = new L.Point(offsetXY.x, offsetXY.y)
}
}
return offset
}
/**
* 将锚点位置转换成x、y方向偏移量
* @private
*/
_getOffsetByAlignment(alignment, width, height) {
let offsetX = 0
let offsetY = 0
switch (alignment) {
case 'top-left':
offsetX = width / 2
offsetY = height
break
case 'top-center':
offsetY = height
break
case 'top-right':
offsetX = -width / 2
offsetY = height
break
case 'bottom-left':
offsetX = width / 2
break
case 'auto':
case 'bottom-center':
break
case 'bottom-right':
offsetX = -width / 2
break
default:
break
}
return { x: offsetX, y: offsetY }
}
/**
* 关闭popup弹窗
* */
close() {
if (this.view._innerView) {
this.view._innerView.closePopup(this._popup)
}
}
destroy() {}
}
export default Popup