This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
wcui
Archived
1
0
Fork 0

更新layer

old
宇天 2019-08-26 19:23:04 +08:00
parent 353355dbf1
commit 13665be881
1 changed files with 66 additions and 529 deletions

View File

@ -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()
}
})
}