mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-12-03 04:21:34 +08:00
refactor(placeable): calc position logic
This commit is contained in:
parent
48233c995e
commit
1a17ac4645
@ -1,7 +1,129 @@
|
||||
<template>
|
||||
<div style="height: 3000px;">
|
||||
<div style="margin-left: 400px;">
|
||||
<!-- <n-popover
|
||||
<n-config-provider style="height: 6000px; width: 6000px;" theme="light">
|
||||
<div class="popover-grid" style="margin-top: 1500px;">
|
||||
<n-popover placement="top-start" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 1 / 2 / 2 / 3;">
|
||||
Top Start
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="top" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 1 / 3 / 2 / 4;">
|
||||
Top
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="top-end" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 1 / 4/ 2 / 5;">
|
||||
Top End
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="left-start" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 2 / 1 / 3 / 2;">
|
||||
Left Start
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="left" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 3 / 1 / 4 / 2;">
|
||||
Left
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="left-end" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 4 / 1 / 5 / 2;">
|
||||
Left End
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="right-start" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 2 / 5 / 3 / 6;">
|
||||
Right Start
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="right" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 3 / 5 / 4 / 6;">
|
||||
Right
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="right-end" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 4 / 5 / 5 / 6;">
|
||||
Right End
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="bottom-start" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 5 / 2 / 6 / 3;">
|
||||
Bottom Start
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="bottom" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 5 / 3 / 6 / 4;">
|
||||
Bottom
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
<n-popover placement="bottom-end" trigger="click">
|
||||
<template v-slot:activator>
|
||||
<n-button size="small" style="grid-area: 5 / 4 / 6 / 5;">
|
||||
Bottom End
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="large-text">
|
||||
Oops!
|
||||
</div>
|
||||
</n-popover>
|
||||
</div>
|
||||
<!-- <div style="margin-left: 400px;">
|
||||
<n-popover
|
||||
v-model="v"
|
||||
trigger="manual"
|
||||
>
|
||||
@ -18,7 +140,7 @@
|
||||
>Activator span</span>
|
||||
</template>
|
||||
<span>Out Out Out</span>
|
||||
</n-popover> -->
|
||||
</n-popover>
|
||||
<n-popover
|
||||
arrow
|
||||
trigger="click"
|
||||
@ -29,10 +151,10 @@
|
||||
</template>
|
||||
<span>LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLong</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
<div style="height: 100px;" />
|
||||
</div> -->
|
||||
<!-- <div style="height: 100px;" />
|
||||
<div style="margin-left: 400px;">
|
||||
<!-- <n-popover
|
||||
<n-popover
|
||||
v-model="v"
|
||||
trigger="manual"
|
||||
>
|
||||
@ -49,7 +171,7 @@
|
||||
>Activator span</span>
|
||||
</template>
|
||||
<span>Out Out Out</span>
|
||||
</n-popover> -->
|
||||
</n-popover>
|
||||
<n-popover
|
||||
arrow
|
||||
trigger="click"
|
||||
@ -60,8 +182,8 @@
|
||||
</template>
|
||||
<span>LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLong</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -78,3 +200,17 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.popover-grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
grid-gap: 64px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.large-text {
|
||||
font-size: 240px;
|
||||
}
|
||||
</style>
|
||||
|
@ -126,11 +126,11 @@
|
||||
|
||||
```css
|
||||
.popover-grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
grid-gap: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto auto auto;
|
||||
grid-gap: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.large-text {
|
||||
|
@ -22,7 +22,7 @@
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo-card__view {
|
||||
.demo-card__view, .naive-ui-doc {
|
||||
/**STYLE_SLOT*/
|
||||
}
|
||||
</style>
|
||||
|
@ -77,7 +77,7 @@ function genVueComponent (parts, noRunning = false) {
|
||||
const contentReg = /<!--CONTENT_SLOT-->/
|
||||
const codeReg = /<!--CODE_SLOT-->/
|
||||
const scriptReg = /\/\*\*\sSCRIPT_SLOT\s\*\//
|
||||
const styleReg = /\/\*\*STYLE_SLOT\*\//
|
||||
const styleReg = /\/\*\*STYLE_SLOT\*\//g
|
||||
const demoReg = /<!--DEMO_SLOT-->/
|
||||
let src = demoBlock
|
||||
// console.log(src)
|
||||
|
@ -1,96 +1,144 @@
|
||||
export default function calcPlacementTransform (placement, activatorRect, contentRect) {
|
||||
let contentLeft = null
|
||||
let contentTop = null
|
||||
let contentRight = null
|
||||
let contentBottom = null
|
||||
let suggesetedTransfromOrigin
|
||||
if (placement === 'top-start') {
|
||||
contentTop = activatorRect.top - contentRect.height
|
||||
contentLeft = activatorRect.left
|
||||
suggesetedTransfromOrigin = 'bottom left'
|
||||
} else if (placement === 'top') {
|
||||
contentTop = activatorRect.top - contentRect.height
|
||||
contentLeft = activatorRect.left + activatorRect.width / 2 - contentRect.width / 2
|
||||
suggesetedTransfromOrigin = 'bottom'
|
||||
} else if (placement === 'top-end') {
|
||||
contentTop = activatorRect.top - contentRect.height
|
||||
contentLeft = activatorRect.left + activatorRect.width - contentRect.width
|
||||
suggesetedTransfromOrigin = 'bottom right'
|
||||
} else if (placement === 'left-start') {
|
||||
contentTop = activatorRect.top
|
||||
contentLeft = activatorRect.left - contentRect.width
|
||||
suggesetedTransfromOrigin = 'top right'
|
||||
} else if (placement === 'left') {
|
||||
contentTop = activatorRect.top + activatorRect.height / 2 - contentRect.height / 2
|
||||
contentLeft = activatorRect.left - contentRect.width
|
||||
suggesetedTransfromOrigin = 'center right'
|
||||
} else if (placement === 'left-end') {
|
||||
contentTop = activatorRect.top + activatorRect.height - contentRect.height
|
||||
contentLeft = activatorRect.left - contentRect.width
|
||||
suggesetedTransfromOrigin = 'bottom right'
|
||||
} else if (placement === 'right-start') {
|
||||
const toWindowBottom = window.innerHeight - activatorRect.top - contentRect.height
|
||||
const toWindowRight = window.innerWidth - activatorRect.right - contentRect.width
|
||||
if (toWindowBottom < 0) {
|
||||
contentBottom = window.innerHeight - activatorRect.bottom
|
||||
suggesetedTransfromOrigin = 'bottom'
|
||||
} else {
|
||||
contentTop = activatorRect.top
|
||||
suggesetedTransfromOrigin = 'top'
|
||||
}
|
||||
if (toWindowRight < 0) {
|
||||
contentRight = window.innerWidth - activatorRect.left
|
||||
suggesetedTransfromOrigin += ' right'
|
||||
} else {
|
||||
contentLeft = activatorRect.left + activatorRect.width
|
||||
suggesetedTransfromOrigin += ' left'
|
||||
}
|
||||
} else if (placement === 'right') {
|
||||
contentTop = activatorRect.top + activatorRect.height / 2 - contentRect.height / 2
|
||||
contentLeft = activatorRect.left + activatorRect.width
|
||||
suggesetedTransfromOrigin = 'center left'
|
||||
} else if (placement === 'right-end') {
|
||||
contentTop = activatorRect.top + activatorRect.height - contentRect.height
|
||||
contentLeft = activatorRect.left + activatorRect.width
|
||||
suggesetedTransfromOrigin = 'bottom left'
|
||||
} else if (placement === 'bottom-start') {
|
||||
const toWindowBottom = window.innerHeight - activatorRect.bottom
|
||||
const toWindowRight = window.innerWidth - activatorRect.left - contentRect.width
|
||||
if (contentRect.height > toWindowBottom && activatorRect.top > toWindowBottom) {
|
||||
contentBottom = toWindowBottom + activatorRect.height
|
||||
// contentTop = null
|
||||
suggesetedTransfromOrigin = 'bottom'
|
||||
} else {
|
||||
contentTop = activatorRect.top + activatorRect.height
|
||||
// contentBottom = null
|
||||
suggesetedTransfromOrigin = 'top'
|
||||
}
|
||||
if (toWindowRight < 0) {
|
||||
contentLeft = activatorRect.right - contentRect.width
|
||||
suggesetedTransfromOrigin += ' right'
|
||||
} else {
|
||||
contentLeft = activatorRect.left
|
||||
suggesetedTransfromOrigin += ' left'
|
||||
}
|
||||
} else if (placement === 'bottom-end') {
|
||||
contentTop = activatorRect.top + activatorRect.height
|
||||
contentLeft = activatorRect.left + activatorRect.width - contentRect.width
|
||||
suggesetedTransfromOrigin = 'top right'
|
||||
} else {
|
||||
contentTop = activatorRect.top + activatorRect.height
|
||||
contentLeft = activatorRect.left + activatorRect.width / 2 - contentRect.width / 2
|
||||
suggesetedTransfromOrigin = 'top center'
|
||||
}
|
||||
/**
|
||||
* We could also change the position using transform.
|
||||
* Such as return `transform: translateX(${contentLeft}px) translateY(${contentTop}px);`
|
||||
* However, I found that the dom delay is very serious.
|
||||
* So I decide to use left and top for now.
|
||||
*/
|
||||
return [{
|
||||
left: contentLeft && `${contentLeft}px`,
|
||||
top: contentTop && `${contentTop}px`,
|
||||
right: contentRight && `${contentRight}px`,
|
||||
bottom: contentBottom && `${contentBottom}px`
|
||||
}, suggesetedTransfromOrigin]
|
||||
const oppositeDirection = {
|
||||
top: 'bottom',
|
||||
bottom: 'top',
|
||||
left: 'right',
|
||||
right: 'left'
|
||||
}
|
||||
|
||||
const adjacentDirections = {
|
||||
top: ['left', 'right'],
|
||||
right: ['top', 'bottom'],
|
||||
bottom: ['left', 'right'],
|
||||
left: ['top', 'bottom']
|
||||
}
|
||||
|
||||
const lengthToCompare = {
|
||||
top: 'height',
|
||||
bottom: 'height',
|
||||
left: 'width',
|
||||
right: 'width'
|
||||
}
|
||||
|
||||
function getAdjustedPlacementOfTrackingElement (placement = 'bottom-start', trackedRect, trackingRect) {
|
||||
const [direction, position] = placement.split('-')
|
||||
if (trackedRect[direction] >= trackingRect[lengthToCompare[direction]]) {
|
||||
return placement
|
||||
} else if (trackedRect[oppositeDirection[direction]] >= trackingRect[lengthToCompare[direction]]) {
|
||||
if (position) return `${oppositeDirection[direction]}-${position}`
|
||||
else return oppositeDirection[direction]
|
||||
} else {
|
||||
const [direction1, direction2] = adjacentDirections[direction]
|
||||
let adjacentDirectionWithMoreSpace = direction1
|
||||
if (trackedRect[direction1] < trackedRect[direction2]) {
|
||||
adjacentDirectionWithMoreSpace = direction2
|
||||
}
|
||||
if (trackedRect[adjacentDirectionWithMoreSpace] < trackingRect[lengthToCompare[adjacentDirections]]) {
|
||||
/**
|
||||
* If no direction has required space, simply not flip tracking element to any side.
|
||||
*/
|
||||
return placement
|
||||
}
|
||||
return adjacentDirectionWithMoreSpace
|
||||
}
|
||||
}
|
||||
|
||||
const placementToTransformOrigin = {
|
||||
'bottom-start': 'top left',
|
||||
'bottom': 'top center',
|
||||
'bottom-end': 'top right',
|
||||
'top-start': 'bottom left',
|
||||
'top': 'bottom',
|
||||
'top-end': 'bottom right',
|
||||
'right-start': 'top left',
|
||||
'right': 'center left',
|
||||
'right-end': 'bottom left',
|
||||
'left-start': 'top right',
|
||||
'left': 'center right',
|
||||
'left-end': 'bottom right'
|
||||
}
|
||||
|
||||
function getTransformOriginByPlacement (placement) {
|
||||
return placementToTransformOrigin[placement] || null
|
||||
}
|
||||
|
||||
function getPosition (placement, trackedRect, trackingRect) {
|
||||
const position = {
|
||||
left: null,
|
||||
right: null,
|
||||
top: null,
|
||||
bottom: null
|
||||
}
|
||||
switch (placement) {
|
||||
case 'bottom-start':
|
||||
position.top = trackedRect.top + trackedRect.height
|
||||
position.left = trackedRect.left
|
||||
break
|
||||
case 'bottom':
|
||||
position.top = trackedRect.top + trackedRect.height
|
||||
position.left = trackedRect.left + trackedRect.width / 2 - trackingRect.width / 2
|
||||
break
|
||||
case 'bottom-end':
|
||||
position.top = trackedRect.top + trackedRect.height
|
||||
position.left = trackedRect.left + trackedRect.width - trackingRect.width
|
||||
break
|
||||
case 'top-start':
|
||||
position.top = trackedRect.top - trackingRect.height
|
||||
position.left = trackedRect.left
|
||||
break
|
||||
case 'top':
|
||||
position.top = trackedRect.top - trackingRect.height
|
||||
position.left = trackedRect.left + trackedRect.width / 2 - trackingRect.width / 2
|
||||
break
|
||||
case 'top-end':
|
||||
position.top = trackedRect.top - trackingRect.height
|
||||
position.left = trackedRect.left + trackedRect.width - trackingRect.width
|
||||
break
|
||||
case 'left-start':
|
||||
position.top = trackedRect.top
|
||||
position.left = trackedRect.left - trackingRect.width
|
||||
break
|
||||
case 'left':
|
||||
position.top = trackedRect.top + trackedRect.height / 2 - trackingRect.height / 2
|
||||
position.left = trackedRect.left - trackingRect.width
|
||||
break
|
||||
case 'left-end':
|
||||
position.top = trackedRect.top + trackedRect.height - trackingRect.height
|
||||
position.left = trackedRect.left - trackingRect.width
|
||||
break
|
||||
case 'right-start':
|
||||
position.top = trackedRect.top
|
||||
position.left = trackedRect.left + trackedRect.width
|
||||
break
|
||||
case 'right':
|
||||
position.top = trackedRect.top + trackedRect.height / 2 - trackingRect.height / 2
|
||||
position.left = trackedRect.left + trackedRect.width
|
||||
break
|
||||
case 'right-end':
|
||||
position.top = trackedRect.top + trackedRect.height - trackingRect.height
|
||||
position.left = trackedRect.left + trackedRect.width
|
||||
break
|
||||
}
|
||||
if (position.left !== null) position.left = position.left + 'px'
|
||||
if (position.right !== null) position.right = position.right + 'px'
|
||||
if (position.top !== null) position.top = position.top + 'px'
|
||||
if (position.bottom !== null) position.bottom = position.bottom + 'px'
|
||||
return position
|
||||
}
|
||||
|
||||
function calcPlacementTransform (placement, activatorRect, contentRect) {
|
||||
const trackedRect = {
|
||||
left: parseInt(activatorRect.left),
|
||||
top: parseInt(activatorRect.top),
|
||||
bottom: parseInt(window.innerHeight - activatorRect.bottom),
|
||||
right: parseInt(window.innerWidth - activatorRect.right),
|
||||
width: parseInt(activatorRect.width),
|
||||
height: parseInt(activatorRect.height)
|
||||
}
|
||||
const trackingRect = contentRect
|
||||
const adjustedPlacement = getAdjustedPlacementOfTrackingElement(placement, trackedRect, trackingRect)
|
||||
const suggesetedTransfromOrigin = getTransformOriginByPlacement(adjustedPlacement)
|
||||
const position = getPosition(adjustedPlacement, trackedRect, trackingRect)
|
||||
return [position, suggesetedTransfromOrigin]
|
||||
}
|
||||
|
||||
export default calcPlacementTransform
|
||||
|
Loading…
Reference in New Issue
Block a user