This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nezha-nezha-fronted/nezha-fronted/src/components/chart/gridItemUtils.js
2021-12-14 13:41:23 +08:00

267 lines
8.7 KiB
JavaScript

export function setTopLeft (top, left, width, height) {
return {
top: top + 'px',
left: left + 'px',
width: width + 'px',
height: height + 'px',
position: 'absolute'
}
}
export function setTopRight (top, right, width, height) {
return {
top: top + 'px',
right: right + 'px',
width: width + 'px',
height: height + 'px',
position: 'absolute'
}
}
export function setTransformRtl (top, right, width, height) {
// Replace unitless items with px
const translate = 'translate3d(' + right * -1 + 'px,' + top + 'px, 0)'
return {
transform: translate,
WebkitTransform: translate,
MozTransform: translate,
msTransform: translate,
OTransform: translate,
width: width + 'px',
height: height + 'px',
position: 'absolute'
}
}
export function setTransform (top, left, width, height) {
// Replace unitless items with px
const translate = 'translate3d(' + left + 'px,' + top + 'px, 0)'
return {
transform: translate,
WebkitTransform: translate,
MozTransform: translate,
msTransform: translate,
OTransform: translate,
width: width + 'px',
height: height + 'px',
position: 'absolute'
}
}
export function bottom (layout) {
let max = 0; let bottomY
for (let i = 0, len = layout.length; i < len; i++) {
bottomY = layout[i].y + layout[i].h
if (bottomY > max) max = bottomY
}
return max
}
export function compact (layout, verticalCompact) {
// Statics go in the compareWith array right away so items flow around them.
const compareWith = getStatics(layout)
// We go through the items by row and column.
const sorted = sortLayoutItemsByRowCol(layout)
// Holding for new items.
const out = Array(layout.length)
for (let i = 0, len = sorted.length; i < len; i++) {
let l = sorted[i]
// Don't move static elements
if (!l.static) {
l = compactItem(compareWith, l, verticalCompact)
// Add to comparison array. We only collide with items before this one.
// Statics are already in this array.
compareWith.push(l)
}
// Add to output array to make sure they still come out in the right order.
out[layout.indexOf(l)] = l
// Clear moved flag, if it exists.
l.moved = false
}
return out
}
export function getLayoutItem (layout, id) {
for (let i = 0, len = layout.length; i < len; i++) {
if (layout[i].i === id) return layout[i]
}
}
export function moveElement (layout, l, x, y, isUserAction, preventCollision) {
if (l.static) return layout
// Short-circuit if nothing to do.
// if (l.y === y && l.x === x) return layout;
const oldX = l.x
const oldY = l.y
const movingUp = y && l.y > y
// This is quite a bit faster than extending the object
if (typeof x === 'number') l.x = x
if (typeof y === 'number') l.y = y
l.moved = true
// If this collides with anything, move it.
// When doing this comparison, we have to sort the items we compare with
// to ensure, in the case of multiple collisions, that we're getting the
// nearest collision.
let sorted = sortLayoutItemsByRowCol(layout)
if (movingUp) sorted = sorted.reverse()
const collisions = getAllCollisions(sorted, l)
if (preventCollision && collisions.length) {
l.x = oldX
l.y = oldY
l.moved = false
return layout
}
// Move each item that collides away from this element.
for (let i = 0, len = collisions.length; i < len; i++) {
const collision = collisions[i]
// console.log('resolving collision between', l.i, 'at', l.y, 'and', collision.i, 'at', collision.y);
// Short circuit so we can't infinite loop
if (collision.moved) continue
// This makes it feel a bit more precise by waiting to swap for just a bit when moving up.
if (l.y > collision.y && l.y - collision.y > collision.h / 4) continue
// Don't move static items - we have to move *this* element away
if (collision.static) {
layout = moveElementAwayFromCollision(layout, collision, l, isUserAction)
} else {
layout = moveElementAwayFromCollision(layout, l, collision, isUserAction)
}
}
return layout
}
export function validateLayout (layout, contextName) {
contextName = contextName || 'Layout'
const subProps = ['x', 'y', 'w', 'h']
if (!Array.isArray(layout)) throw new Error(contextName + ' must be an array!')
for (let i = 0, len = layout.length; i < len; i++) {
const item = layout[i]
for (let j = 0; j < subProps.length; j++) {
if (typeof item[subProps[j]] !== 'number') {
throw new Error('VueGridLayout: ' + contextName + '[' + i + '].' + subProps[j] + ' must be a number!')
}
}
if (item.i && typeof item.i !== 'string') {
// number is also ok, so comment the error
// TODO confirm if commenting the line below doesn't cause unexpected problems
// throw new Error('VueGridLayout: ' + contextName + '[' + i + '].i must be a string!');
}
if (item.static !== undefined && typeof item.static !== 'boolean') {
throw new Error('VueGridLayout: ' + contextName + '[' + i + '].static must be a boolean!')
}
}
}
export function cloneLayout (layout) {
const newLayout = Array(layout.length)
for (let i = 0, len = layout.length; i < len; i++) {
newLayout[i] = cloneLayoutItem(layout[i])
}
return newLayout
}
export function getAllCollisions (layout, layoutItem) {
return layout.filter((l) => collides(l, layoutItem))
}
export function cloneLayoutItem (layoutItem) {
/* return {
w: layoutItem.w, h: layoutItem.h, x: layoutItem.x, y: layoutItem.y, i: layoutItem.i,
minW: layoutItem.minW, maxW: layoutItem.maxW, minH: layoutItem.minH, maxH: layoutItem.maxH,
moved: Boolean(layoutItem.moved), static: Boolean(layoutItem.static),
// These can be null
isDraggable: layoutItem.isDraggable, isResizable: layoutItem.isResizable
}; */
return JSON.parse(JSON.stringify(layoutItem))
}
export function collides (l1, l2) {
if (l1 === l2) return false // same element
if (l1.x + l1.w <= l2.x) return false // l1 is left of l2
if (l1.x >= l2.x + l2.w) return false // l1 is right of l2
if (l1.y + l1.h <= l2.y) return false // l1 is above l2
if (l1.y >= l2.y + l2.h) return false // l1 is below l2
return true // boxes overlap
}
export function compactItem (compareWith, l, verticalCompact) {
if (verticalCompact) {
// Move the element up as far as it can go without colliding.
while (l.y > 0 && !getFirstCollision(compareWith, l)) {
l.y--
}
}
// Move it down, and keep moving it down if it's colliding.
let collides
while ((collides = getFirstCollision(compareWith, l))) {
l.y = collides.y + collides.h
}
return l
}
export function getFirstCollision (layout, layoutItem) {
for (let i = 0, len = layout.length; i < len; i++) {
if (collides(layout[i], layoutItem)) return layout[i]
}
}
export function getStatics (layout) {
// return [];
return layout.filter((l) => l.static)
}
export function moveElementAwayFromCollision (layout, collidesWith, itemToMove, isUserAction) {
const preventCollision = false // we're already colliding
// If there is enough space above the collision to put this element, move it there.
// We only do this on the main collision as this can get funky in cascades and cause
// unwanted swapping behavior.
if (isUserAction) {
// Make a mock item so we don't modify the item here, only modify in moveElement.
const fakeItem = {
x: itemToMove.x,
y: itemToMove.y,
w: itemToMove.w,
h: itemToMove.h,
i: '-1'
}
fakeItem.y = Math.max(collidesWith.y - itemToMove.h, 0)
if (!getFirstCollision(layout, fakeItem)) {
return moveElement(layout, itemToMove, undefined, fakeItem.y, preventCollision)
}
}
// Previously this was optimized to move below the collision directly, but this can cause problems
// with cascading moves, as an item may actually leapflog a collision and cause a reversal in order.
return moveElement(layout, itemToMove, undefined, itemToMove.y + 1, preventCollision)
}
export function sortLayoutItemsByRowCol (layout) {
return [].concat(layout).sort(function (a, b) {
if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
return 1
}
return -1
})
}
export function correctBounds (layout, bounds) {
const collidesWith = getStatics(layout)
for (let i = 0, len = layout.length; i < len; i++) {
const l = layout[i]
// Overflows right
if (l.x + l.w > bounds.cols) l.x = bounds.cols - l.w
// Overflows left
if (l.x < 0) {
l.x = 0
l.w = bounds.cols
}
if (!l.static) collidesWith.push(l)
else {
// If this is static and collides with other statics, we must move it down.
// We have to do something nicer than just letting them overlap.
while (getFirstCollision(collidesWith, l)) {
l.y++
}
}
}
return layout
}