更新layer
parent
353355dbf1
commit
13665be881
|
@ -42,6 +42,9 @@
|
|||
transform: scale(1.02);
|
||||
transition: transform 0.1s linear;
|
||||
}
|
||||
&.blur {
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
z-index: 65536;
|
||||
|
@ -178,8 +181,7 @@ const LANGUAGES = {
|
|||
LANGUAGES['zh-CN'] = LANGUAGES.zh
|
||||
const lang =
|
||||
LANGUAGES[window.__ENV_LANG__ || navigator.language] || LANGUAGES.en
|
||||
let layerDom = {}
|
||||
let layerObj = {}
|
||||
|
||||
let unique = null // 储存当前打开的1/2/3类型的弹窗
|
||||
let lid = 0
|
||||
let defconf = {
|
||||
|
@ -195,46 +197,14 @@ let defconf = {
|
|||
content: '', // 弹窗的内容
|
||||
fixed: false, // 是否固定不可拖拽
|
||||
shift: {}, // 弹窗出来的初始位置,用于出场动画
|
||||
offset: {}, // 弹窗出来后的坐标, 为数组,可有4个值,依次是 上右下左
|
||||
btns: [lang.YES_BTN, lang.NO_BTN] // 弹窗的2个按钮的文字
|
||||
offset: {} // 弹窗出来后的坐标, 为数组,可有4个值,依次是 上右下左
|
||||
}
|
||||
const $doc = Anot(document)
|
||||
|
||||
const uuid = function() {
|
||||
return 'layer-' + lid++
|
||||
}
|
||||
const close = function(id) {
|
||||
if (typeof id !== 'string' && typeof id !== 'number') {
|
||||
return Anot.error(lang.ERROR)
|
||||
}
|
||||
if (/^layerwrap\-/.test(id) || layerObj['layerwrap-' + id]) {
|
||||
try {
|
||||
id = (layerObj['layerwrap-' + id] ? 'layerwrap-' : '') + id
|
||||
//未显示过,忽略
|
||||
if (!layerObj[id].show) {
|
||||
return
|
||||
}
|
||||
layerObj[id].parentElem.replaceChild(layerObj[id].wrap, layerDom[id][0])
|
||||
layerObj[id].wrap.style.display = 'none'
|
||||
layerObj[id].show = false
|
||||
} catch (err) {}
|
||||
} else {
|
||||
unique = null
|
||||
if (layerDom[id]) {
|
||||
layerDom[id][0].classList.add('shift')
|
||||
layerDom[id][1].classList.add('shift')
|
||||
layerDom[id][0].style.opacity = ''
|
||||
layerDom[id][1].style.opacity = 0
|
||||
setTimeout(function() {
|
||||
try {
|
||||
layerDom[id][0].parentNode.removeChild(layerDom[id][0])
|
||||
delete layerDom[id]
|
||||
delete Anot.vmodels[id]
|
||||
} catch (err) {}
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
// 要保证弹层唯一的类型
|
||||
const UNIQUE_TYPES = ['alert', 'confirm', 'prompt']
|
||||
|
||||
const fixOffset = function(offset) {
|
||||
for (let i in offset) {
|
||||
|
@ -259,457 +229,51 @@ const fixOffset = function(offset) {
|
|||
7: 'msg',
|
||||
} */
|
||||
|
||||
class __layer__ {
|
||||
get dot() {
|
||||
//loading的子元素数量
|
||||
return {
|
||||
1: 1,
|
||||
2: 5,
|
||||
3: 5,
|
||||
4: 9
|
||||
}
|
||||
function createLayer(opt) {
|
||||
var layDom = document.createElement('wc-layer')
|
||||
layDom.type = opt.type
|
||||
if (opt.mask) {
|
||||
layDom.setAttribute('mask', '')
|
||||
}
|
||||
|
||||
constructor(opt) {
|
||||
if (opt) {
|
||||
let { yes, no, success } = opt
|
||||
delete opt.yes
|
||||
delete opt.no
|
||||
delete opt.success
|
||||
|
||||
this.__init__({
|
||||
state: { ...opt },
|
||||
props: { yes, no, success }
|
||||
})
|
||||
.append()
|
||||
.show()
|
||||
}
|
||||
this.timeout = null
|
||||
if (opt.title) {
|
||||
layDom.title = opt.title
|
||||
}
|
||||
|
||||
// 真正的初始化弹层配置
|
||||
__init__(opt) {
|
||||
let _id = opt.$id || uuid()
|
||||
this.init = {
|
||||
$id: _id,
|
||||
state: {
|
||||
...defconf,
|
||||
...opt.state
|
||||
},
|
||||
props: opt.props,
|
||||
skip: [
|
||||
'area',
|
||||
'shift',
|
||||
'offset',
|
||||
'mask',
|
||||
'maskClose',
|
||||
'container',
|
||||
'follow'
|
||||
],
|
||||
methods: {
|
||||
shake() {
|
||||
this.$refs.layer.classList.add('scale')
|
||||
setTimeout(() => {
|
||||
this.$refs.layer.classList.remove('scale')
|
||||
}, 100)
|
||||
},
|
||||
onMaskClick: function(ev) {
|
||||
if (ev.target === ev.currentTarget) {
|
||||
if (this.type < 4 && !this.maskClose) {
|
||||
this.shake()
|
||||
} else {
|
||||
this.maskClose && this.close()
|
||||
}
|
||||
}
|
||||
},
|
||||
handleConfirm: function() {
|
||||
if (this.type === 3) {
|
||||
if (!this.prompt) {
|
||||
return this.shake()
|
||||
}
|
||||
}
|
||||
if (typeof this.props.yes === 'function') {
|
||||
let cb = [this.$id]
|
||||
if (this.type === 3) {
|
||||
cb.unshift(this.prompt)
|
||||
}
|
||||
this.props.yes.apply(this, cb)
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
handleCancel: function() {
|
||||
if (typeof this.props.no === 'function') {
|
||||
this.props.no.call(this, this.$id)
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
close: function() {
|
||||
close(this.$id)
|
||||
}
|
||||
// cancelBubble: function(ev) {
|
||||
// ev.cancelBubble = true
|
||||
// }
|
||||
},
|
||||
mounted: function() {
|
||||
if (typeof this.props.success === 'function') {
|
||||
this.props.success.call(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
layDom.innerHTML = opt.content
|
||||
document.body.appendChild(layDom)
|
||||
|
||||
// iframe类型补一个自适应高度的方法
|
||||
if (this.init.state.type === 4) {
|
||||
this.init.methods.autoSize = function() {
|
||||
let { layer, frame } = this.$refs
|
||||
frame.onload = function() {
|
||||
try {
|
||||
let $body = frame.contentWindow.document.body
|
||||
let { clientWidth, clientHeight } = $body
|
||||
|
||||
layer.style.cssText += `width: ${clientWidth}px;height: ${clientHeight}px;`
|
||||
frame.style.cssText += `height: ${clientHeight}px;`
|
||||
} catch (err) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
// 创建弹层容器及骨架
|
||||
create() {
|
||||
let { state, $id } = this.init
|
||||
let outerBox = document.createElement('div')
|
||||
let layBox = document.createElement('div')
|
||||
|
||||
outerBox.setAttribute('anot', $id)
|
||||
outerBox.setAttribute(':on-click', 'onMaskClick')
|
||||
|
||||
outerBox.classList.add('do-layer')
|
||||
if (state.mask) {
|
||||
outerBox.classList.add('mask')
|
||||
if (state.container && state.container !== document.body) {
|
||||
outerBox.classList.add('inner')
|
||||
}
|
||||
}
|
||||
if (state.maskColor) {
|
||||
outerBox.style.background = state.maskColor
|
||||
}
|
||||
|
||||
layBox.classList.add('layer-box')
|
||||
layBox.classList.add('skin-normal')
|
||||
|
||||
if (state.extraClass) {
|
||||
layBox.classList.add(state.extraClass)
|
||||
delete state.extraClass
|
||||
}
|
||||
if (state.shift) {
|
||||
fixOffset(state.shift)
|
||||
for (let k in state.shift) {
|
||||
let val = state.shift[k]
|
||||
val += isFinite(val) ? 'px' : ''
|
||||
layBox.style.cssText += `${k}: ${val};`
|
||||
}
|
||||
}
|
||||
|
||||
if (state.toast) {
|
||||
layBox.classList.add('type-toast')
|
||||
} else {
|
||||
layBox.classList.add('type-' + state.type)
|
||||
}
|
||||
|
||||
layBox.setAttribute('ref', 'layer')
|
||||
// layBox.setAttribute(':on-click', 'cancelBubble')
|
||||
|
||||
// 暂时隐藏,避免修正定位时,能看到闪一下
|
||||
layBox.style.cssText += 'border-radius:' + state.radius + 'px'
|
||||
|
||||
// 没有菜单栏, 且未禁止拖拽,则加上可拖拽属性
|
||||
if (!state.menubar && !state.fixed) {
|
||||
layBox.setAttribute(':drag', '')
|
||||
layBox.setAttribute('data-limit', 'window')
|
||||
}
|
||||
|
||||
// size of layer-content
|
||||
var boxcss = ''
|
||||
if (state.area[0] !== 'auto') {
|
||||
boxcss += 'width: ' + state.area[0] + ';'
|
||||
}
|
||||
if (state.area[1] !== 'auto') {
|
||||
boxcss += 'height: ' + state.area[1] + ';'
|
||||
}
|
||||
let arrow = ''
|
||||
if (state.type === 5) {
|
||||
arrow += `<i class="arrow"></i>`
|
||||
}
|
||||
|
||||
layBox.innerHTML = `
|
||||
${this.mkMenubar()}
|
||||
<div
|
||||
class="layer-content do-fn-cl"
|
||||
style="${boxcss}"
|
||||
${!state.wrap && state.type !== 6 ? ':html="content"' : ''}>
|
||||
|
||||
${state.type === 6 ? this.mkLoading(state.load) : ''}
|
||||
</div>
|
||||
${this.mkCtrl()}
|
||||
${arrow}
|
||||
`
|
||||
delete state.wrap
|
||||
outerBox.appendChild(layBox)
|
||||
return [outerBox, layBox]
|
||||
}
|
||||
|
||||
// 创建loading元素
|
||||
mkLoading(style) {
|
||||
return `
|
||||
<div class="loading style-${style}">
|
||||
<span class="dot-box">
|
||||
${(style === 1 ? '<i class="do-icon-loading"></i>' : '<i></i>').repeat(
|
||||
this.dot[style]
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
// 创建窗口导航条
|
||||
mkMenubar() {
|
||||
let { menubar, fixed } = this.init.state
|
||||
let html = ''
|
||||
if (menubar) {
|
||||
html = `
|
||||
<div class="layer-title do-fn-noselect"
|
||||
:text="title"
|
||||
${!fixed ? ':drag="layer-box" data-limit="window"' : ''}>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
return html
|
||||
}
|
||||
|
||||
// 创建窗口按钮
|
||||
mkCtrl() {
|
||||
let { type } = this.init.state
|
||||
if (type > 3) {
|
||||
return ''
|
||||
} else {
|
||||
let html = ''
|
||||
let btns = `
|
||||
<a class="action-yes"
|
||||
:on-click="handleConfirm"
|
||||
tabindex="-1"
|
||||
:text="btns[0]"
|
||||
></a>
|
||||
`
|
||||
if (type > 1) {
|
||||
btns =
|
||||
`
|
||||
<a class="action-no"
|
||||
:on-click="handleCancel"
|
||||
:text="btns[1]"
|
||||
></a>
|
||||
` + btns
|
||||
}
|
||||
html = `
|
||||
<div class="layer-ctrl do-fn-noselect">
|
||||
${btns}
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
append() {
|
||||
let { state, $id } = this.init
|
||||
let container = state.container
|
||||
|
||||
if (state.type < 4) {
|
||||
// 如果有已经打开的弹窗,则关闭
|
||||
if (unique) {
|
||||
close(unique)
|
||||
}
|
||||
unique = $id
|
||||
}
|
||||
|
||||
// 返回一个数组,第1个元素是容器,第2个是骨架
|
||||
layerDom[$id] = this.create()
|
||||
|
||||
delete state.toast
|
||||
this.toast = true
|
||||
if (!container) {
|
||||
container = document.body
|
||||
}
|
||||
|
||||
container.appendChild(layerDom[$id][0])
|
||||
this.vm = Anot(this.init)
|
||||
return this
|
||||
}
|
||||
|
||||
show() {
|
||||
let vm = this.vm
|
||||
let { state, $id } = this.init
|
||||
let container = state.container
|
||||
|
||||
setTimeout(function() {
|
||||
let style = { background: state.background }
|
||||
|
||||
let $dom1 = Anot(layerDom[$id][1])
|
||||
|
||||
// tips类型, 弹层的定位要在指定的容器上
|
||||
if (state.type === 5) {
|
||||
let css = getComputedStyle(layerDom[$id][1])
|
||||
// 只有tips类型可以定义 `color`
|
||||
style.color = state.color
|
||||
style.opacity = 1
|
||||
let $container = Anot(container)
|
||||
let $arrow = $container[0].querySelector('.arrow')
|
||||
let cw = $container.innerWidth()
|
||||
let ch = $container.innerHeight()
|
||||
let ol = $container.offset().left - $doc.scrollLeft()
|
||||
let ot = $container.offset().top - $doc.scrollTop()
|
||||
|
||||
let layw = parseInt(css.width)
|
||||
let layh = parseInt(css.height)
|
||||
let arrowOffset = ['top']
|
||||
|
||||
Anot(layerDom[$id][1]).css(style)
|
||||
|
||||
$container.bind('mouseenter', ev => {
|
||||
let tmpStyle = { visibility: 'visible' }
|
||||
ol = $container.offset().left - $doc.scrollLeft()
|
||||
ot = $container.offset().top - $doc.scrollTop()
|
||||
|
||||
if (ot + 18 < layh) {
|
||||
arrowOffset[0] = 'bottom'
|
||||
$arrow.style.borderBottomColor = state.background
|
||||
tmpStyle.top = ot + ch + 8
|
||||
} else {
|
||||
$arrow.style.borderTopColor = state.background
|
||||
tmpStyle.top = ot - layh - 8
|
||||
}
|
||||
|
||||
if (ol + cw * 0.7 + layw > window.innerWidth) {
|
||||
tmpStyle.left = ol + cw * 0.3 - layw
|
||||
arrowOffset[1] = 'left'
|
||||
} else {
|
||||
tmpStyle.left = ol + cw * 0.7
|
||||
}
|
||||
|
||||
$arrow.classList.add('offset-' + arrowOffset.join('-'))
|
||||
$dom1.css(tmpStyle)
|
||||
})
|
||||
$container.bind('mouseleave', () => {
|
||||
setTimeout(() => {
|
||||
$arrow.classList.remove('offset-' + arrowOffset.join('-'))
|
||||
arrowOffset = ['top']
|
||||
$arrow.style.borderBottomColor = ''
|
||||
$arrow.style.borderTopColor = ''
|
||||
layerDom[$id][1].style.visibility = 'hidden'
|
||||
}, 100)
|
||||
})
|
||||
} else {
|
||||
let offsetStyle = { opacity: 1 }
|
||||
if (state.offset) {
|
||||
fixOffset(state.offset)
|
||||
|
||||
Object.assign(offsetStyle, state.offset)
|
||||
}
|
||||
$dom1.css(style)
|
||||
setTimeout(() => {
|
||||
document.body.style.overflow = 'hidden'
|
||||
layerDom[$id][1].classList.add('shift')
|
||||
setTimeout(_ => {
|
||||
$dom1.css(offsetStyle)
|
||||
if (vm && vm.$refs.input) {
|
||||
vm.$refs.input.focus()
|
||||
}
|
||||
setTimeout(_ => {
|
||||
try {
|
||||
layerDom[$id][1].classList.remove('shift')
|
||||
} catch (err) {}
|
||||
}, 500)
|
||||
}, 50)
|
||||
}, 50)
|
||||
}
|
||||
}, 4)
|
||||
|
||||
// loading类型,回调需要自动触发
|
||||
if (state.type > 3) {
|
||||
//大于0自动触发超时关闭
|
||||
if (state.timeout > 0) {
|
||||
clearTimeout(this.timeout)
|
||||
this.timeout = setTimeout(() => {
|
||||
clearTimeout(this.timeout)
|
||||
close($id)
|
||||
|
||||
// 为loading类型时,自动关闭同时触发回调
|
||||
if (state.type === 6) {
|
||||
this.vm.props.yes.call(this.vm, $id)
|
||||
}
|
||||
}, state.timeout)
|
||||
} else if (state.type === 6) {
|
||||
// loading类型, 非自动关闭时, 主动触发回调
|
||||
this.vm.props.yes.call(this.vm, $id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return layDom
|
||||
}
|
||||
|
||||
const _layer = {
|
||||
alert(content, title, cb) {
|
||||
let opt = { content, fixed: true }
|
||||
|
||||
if (typeof title === 'function') {
|
||||
opt.yes = title
|
||||
} else {
|
||||
if (title) {
|
||||
opt.title = title + ''
|
||||
}
|
||||
if (cb && typeof cb === 'function') {
|
||||
opt.yes = cb
|
||||
}
|
||||
}
|
||||
var l = document.createElement('wc-layer')
|
||||
l.title = 'hello'
|
||||
l.setAttribute('type', 'alert')
|
||||
l.setAttribute('mask', '')
|
||||
l.innerHTML = '<p>ffdfdffd</p>'
|
||||
document.body.appendChild(l)
|
||||
|
||||
return _layer.open(opt)
|
||||
alert(content, title) {
|
||||
return createLayer({
|
||||
type: 'alert',
|
||||
title: title || 'Hello world!',
|
||||
content:
|
||||
content || '<p>blablablablsjkdjbskj fghjsdgfjs djfhjsdgf jhs jhj</p>',
|
||||
fixed: true,
|
||||
mask: true
|
||||
})
|
||||
},
|
||||
confirm(content, title, yescb, nocb) {
|
||||
let opt = { content, fixed: true, type: 2 }
|
||||
|
||||
if (typeof title === 'function') {
|
||||
opt.yes = title
|
||||
if (typeof yescb === 'function') {
|
||||
opt.no = yescb
|
||||
}
|
||||
} else {
|
||||
if (title) {
|
||||
opt.title = title + ''
|
||||
}
|
||||
if (yescb && typeof yescb === 'function') {
|
||||
opt.yes = yescb
|
||||
}
|
||||
if (nocb && typeof nocb === 'function') {
|
||||
opt.no = nocb
|
||||
}
|
||||
}
|
||||
return _layer.open(opt)
|
||||
confirm(content, title) {
|
||||
return createLayer({
|
||||
type: 'confirm',
|
||||
title: title || 'Hello world!',
|
||||
content: content || 'blablablablsjkdjbskj fghjsdgfjs djfhjsdgf jhs jhj',
|
||||
fixed: true,
|
||||
mask: true
|
||||
})
|
||||
},
|
||||
frame(url, extra = {}) {
|
||||
let opt = {
|
||||
return createLayer({
|
||||
type: 'frame',
|
||||
content: `<iframe ref="frame" class="frame-box" src="${url}"></iframe>`,
|
||||
menubar: false,
|
||||
maskClose: true,
|
||||
type: 4,
|
||||
...extra
|
||||
}
|
||||
return _layer.open(opt)
|
||||
fixed: true,
|
||||
mask: true,
|
||||
maskClose: true
|
||||
})
|
||||
},
|
||||
toast(txt, type = 'info', timeout = 2500) {
|
||||
if (typeof type === 'number') {
|
||||
|
@ -746,53 +310,6 @@ const _layer = {
|
|||
|
||||
return _layer.open(opt)
|
||||
},
|
||||
load(style, container, cb) {
|
||||
style = style >>> 0
|
||||
style = style < 1 ? 1 : style > 4 ? 4 : style
|
||||
|
||||
if (typeof container === 'function') {
|
||||
cb = container
|
||||
container = null
|
||||
} else {
|
||||
if (!(container instanceof HTMLElement)) {
|
||||
container = null
|
||||
}
|
||||
if (typeof cb !== 'function') {
|
||||
cb = Anot.noop
|
||||
}
|
||||
}
|
||||
return _layer.open({
|
||||
container,
|
||||
type: 6,
|
||||
load: style,
|
||||
yes: cb,
|
||||
menubar: false,
|
||||
background: 'none',
|
||||
fixed: true
|
||||
})
|
||||
},
|
||||
tips(content, container, opt = {}) {
|
||||
if (!(container instanceof HTMLElement)) {
|
||||
return Anot.error(lang.NEED_CONTAINER)
|
||||
}
|
||||
|
||||
if (!opt.background) {
|
||||
opt.background = 'rgba(0,0,0,.5)'
|
||||
}
|
||||
if (!opt.color) {
|
||||
opt.color = '#fff'
|
||||
}
|
||||
Object.assign(opt, {
|
||||
container,
|
||||
content,
|
||||
type: 5,
|
||||
fixed: true,
|
||||
mask: false,
|
||||
menubar: false,
|
||||
timeout: 0
|
||||
})
|
||||
return _layer.open(opt)
|
||||
},
|
||||
prompt(title, yescb) {
|
||||
if (typeof yescb !== 'function') {
|
||||
return console.error(
|
||||
|
@ -1008,6 +525,8 @@ export default class Layer {
|
|||
}
|
||||
|
||||
set type(val) {
|
||||
this.props.type = val
|
||||
|
||||
switch (val) {
|
||||
case 'alert':
|
||||
this.__CTRL__.innerHTML = renderBtns(['确定'])
|
||||
|
@ -1018,12 +537,17 @@ export default class Layer {
|
|||
break
|
||||
case 'toast':
|
||||
break
|
||||
case 'frame':
|
||||
break
|
||||
default:
|
||||
val = 'common'
|
||||
if (this.opt.btns) {
|
||||
this.__CTRL__.innerHTML = renderBtns(this.opt.btns)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.setAttribute('type', val)
|
||||
}
|
||||
|
||||
get fixed() {
|
||||
|
@ -1033,24 +557,37 @@ export default class Layer {
|
|||
set fixed(val) {
|
||||
this.props.fixed = !!val
|
||||
|
||||
// 这3类弹层不允许拖拽
|
||||
if (UNIQUE_TYPES.includes(this.props.type)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.props.fixed) {
|
||||
this._dragIns = new Drag(this.root.children[1]).by(this.__TITLE__, {
|
||||
overflow: false
|
||||
})
|
||||
} else {
|
||||
if (this._dragIns) {
|
||||
this._dragIns.destroy()
|
||||
this._dragIns = null
|
||||
}
|
||||
} else {
|
||||
this._dragIns = new Drag(this.root.children[1]).by(this.__TITLE__, {
|
||||
overflow: false
|
||||
})
|
||||
this.removeAttribute('fixed')
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this._dragIns) {
|
||||
this._dragIns.destroy()
|
||||
}
|
||||
this.parentNode.removeChild(this)
|
||||
}
|
||||
|
||||
mounted() {
|
||||
bind(this.__CTRL__, 'click', ev => {
|
||||
if (ev.target.tagName === 'BUTTON') {
|
||||
var idx = +ev.target.dataset.idx
|
||||
log(idx)
|
||||
this.parentNode.removeChild(this)
|
||||
this.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Reference in New Issue