优化tooltip在有滚动条时的定位

master
yutent 2023-11-22 16:40:05 +08:00
parent e691868a55
commit 2f2d1ebbe9
2 changed files with 114 additions and 198 deletions

View File

@ -1,225 +1,135 @@
/** /**
* {气泡确认框} * {}
* @author chensbox<chensbox@foxmail.com> * @author yutent<yutent.io@gmail.com>
* @date 2023/04/28 16:14:10 * @date 2023/03/21 16:14:10
*/ */
import { css, html, Component, styleMap } from 'wkit' import { css, html, Component, bind, styleMap, offset } from 'wkit'
import '../form/button.js'
class Popconfirm extends Component { const DEFAULT_TIPS = '请确认你的操作!'
class PopConfirm extends Component {
static props = { static props = {
title: { title: 'str!'
type: String,
default: '',
attribute: false
},
confirmButtonText: {
type: String,
default: '确定',
attribute: false
},
cancelButtonText: {
type: String,
default: '取消',
attribute: false
},
confirmButtonType: {
type: String,
default: 'primary',
attribute: false
},
cancelButtonType: {
type: String,
default: '',
attribute: false
},
icon: {
type: String,
default: 'info',
attribute: false
},
iconColor: {
type: String,
default: '#ff9900',
attribute: false
},
hideIcon: {
type: Boolean,
default: false
},
show: {
type: Boolean,
default: false,
attribute: false
}
} }
static styles = [ static styles = [
css` css`
:host { :host {
position: relative; display: inline-flex;
display: inline-block;
} }
.popover {
z-index: 10; .container {
position: relative;
}
.tooltip {
display: none;
position: fixed; position: fixed;
padding: 12px; z-index: 9;
min-width: 150px; justify-content: center;
border-radius: 4px; align-items: center;
border: 1px solid #ebeef5; max-width: 360px;
color: #606266; min-width: 32px;
line-height: 1.4; padding: 6px 8px;
text-align: left; border-radius: 3px;
font-size: 14px; font-size: var(--wc-tooltip-font, 14px);
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); background: var(--wc-tooltip-background, #fff);
color: var(--wc-tooltip-color, var(--color-dark-1));
box-shadow: 0 0 3px var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.2));
word-break: break-all; word-break: break-all;
background: #fff; -webkit-user-select: none;
transition: opacity 0.15s linear; user-select: none;
.btns {
display: flex; &::after {
justify-content: flex-end; position: absolute;
.cancel { display: block;
margin-right: 15px; width: 8px;
cursor: pointer; height: 8px;
color: var(--color-teal-2); border-radius: 2px;
background: var(--wc-tooltip-background, #fff);
content: '';
transform: rotate(45deg);
box-shadow: -1px -1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1));
}
&[placement='left-top'] {
&::after {
right: 16px;
bottom: -4px;
box-shadow: 1px 1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1));
}
}
&[placement='left-bottom'] {
&::after {
right: 16px;
top: -4px;
}
}
&[placement='right-top'] {
&::after {
left: 16px;
bottom: -4px;
box-shadow: 1px 1px 0 var(--wc-tooltip-shadow, rgba(0, 0, 0, 0.1));
}
}
&[placement='right-bottom'] {
&::after {
left: 16px;
top: -4px;
} }
} }
} }
.content {
padding: 8px 0;
display: flex;
align-items: center;
font-size: 14px;
color: #606266;
line-height: 1.4;
text-align: justify;
word-break: break-all;
white-space: nowrap;
wc-icon {
margin-right: 5px;
--wc-icon-size: 14px;
}
}
.arrow {
position: absolute;
z-index: 12;
left: 50%;
top: 100%;
transform: translateX(-50%);
border: 6px solid transparent;
border-bottom-color: #fff;
&[top] {
border-bottom-color: transparent;
top: 0;
border-top-color: #fff;
transform: translateY(-100%);
}
}
.show {
visibility: visible;
opacity: 1;
}
.hide {
visibility: hidden;
opacity: 0;
}
` `
] ]
#click(ev) {
let { left, top } = offset(this)
let placement = 'left'
let styles = { display: 'block' }
if (left < 360 || (left > 360 && window.innerWidth - left > 360)) {
placement = 'right'
styles.left = left + 'px'
} else {
let right = window.innerWidth - left - this.clientWidth
styles.right = right + 'px'
}
if (top < 96) {
top += 8 + this.clientHeight
placement += '-bottom'
styles.top = top + 'px'
} else {
let bottom = window.innerHeight - top + 8
placement += '-top'
styles.bottom = bottom + 'px'
}
//
this.$refs.tips.setAttribute('placement', placement)
this.$refs.tips.style.cssText = styleMap(styles)
this.$refs.tips.$animate()
}
mounted() { mounted() {
this.$slot = this.$refs.slot.assignedElements().shift() // bind(this.$refs.wrap, 'mouseleave', ev => {
this.$popover = this.$refs.popover // this.$refs.tips.$animate(true)
this.$arrow = this.$refs.arrow // })
}
showPopover() {
const slotRect = this.$slot.getBoundingClientRect()
const popoverRect = this.$popover.getBoundingClientRect()
const halfSlotWidth = slotRect.width / 2
const halfPopoverWidth = popoverRect.width / 2
const left = slotRect.left + halfSlotWidth - halfPopoverWidth
if (left < 0) {
this.left = slotRect.left
} else if (left + popoverRect.width > window.innerWidth) {
this.left = slotRect.right - popoverRect.width
} else {
this.left = left
}
if (slotRect.bottom + 10 + popoverRect.height > window.innerHeight) {
this.top = slotRect.top - 10 - popoverRect.height
this.$arrow.setAttribute('top', '')
} else {
this.top = slotRect.bottom + 10
}
this.show = true
}
hide({ target }) {
this.show = false
if (target.hasAttribute('solid')) {
this.$emit('confirm')
} else {
this.$emit('cancel')
}
} }
render() { render() {
let styles = styleMap({
left: `${this.left}px`,
top: `${this.top}px`
})
return html` return html`
<div class="popconfirm"> <main class="container">
<slot ref="slot" @click=${this.showPopover}></slot> <div class="wrapper" ref="wrap" @click=${this.#click}>
<slot></slot>
<div class=${`arrow ${this.show ? 'show' : 'hide'}`} ref="arrow"></div>
<div
class=${`popover ${this.show ? 'show' : 'hide'}`}
style=${styles}
ref="popover"
>
<p class="content">
${this.hideIcon
? ''
: html`<wc-icon
name=${this.icon}
style=${`color:${this.iconColor}`}
></wc-icon>`}
${this.title}
</p>
<div class="btns">
${!this.cancelButtonType
? html`<span class="cancel" @click=${this.hide}
>${this.cancelButtonText}</span
>`
: html`<wc-button
type=${this[cancelButtonType]}
class="cancel"
size="s"
@click=${this.hide}
>${this[cancelButtonText]}</wc-button
>`}
<wc-button
type=${this.confirmButtonType}
size="s"
solid
@click=${this.hide}
>${this.confirmButtonText}</wc-button
>
</div>
</div> </div>
</div> <div class="tooltip" ref="tips" #animation=${{}}>
<slot name="title">${this.title || DEFAULT_TIPS}</slot
><i class="trigon"></i>
</div>
</main>
` `
} }
} }
Popconfirm.reg('popconfirm') PopConfirm.reg('popconfirm')

View File

@ -86,6 +86,11 @@ class Tooltip extends Component {
let { left, top } = offset(this) let { left, top } = offset(this)
let placement = 'left' let placement = 'left'
let styles = { display: 'block' } let styles = { display: 'block' }
let st = document.documentElement.scrollTop
if (this.title.trim() === '') {
return
}
if (left < 360 || (left > 360 && window.innerWidth - left > 360)) { if (left < 360 || (left > 360 && window.innerWidth - left > 360)) {
placement = 'right' placement = 'right'
@ -99,8 +104,9 @@ class Tooltip extends Component {
top += 8 + this.clientHeight top += 8 + this.clientHeight
placement += '-bottom' placement += '-bottom'
styles.top = top + 'px' styles.top = top + 'px'
// console.log('<><><>', st)
} else { } else {
let bottom = window.innerHeight - top + 8 let bottom = window.innerHeight - top + 8 + st
placement += '-top' placement += '-top'
styles.bottom = bottom + 'px' styles.bottom = bottom + 'px'
} }