fix: 修改 拖拽高度的问题,以及 chart-line nodata的判断
This commit is contained in:
@@ -37,7 +37,14 @@ module.exports = {
|
||||
{
|
||||
test: /\.(js)$/,
|
||||
loader: 'babel-loader',
|
||||
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client'), resolve('node_modules/element-ui/packages/scrollbar')],
|
||||
include: [
|
||||
resolve('src'),
|
||||
resolve('test'),
|
||||
resolve('node_modules/webpack-dev-server/client'),
|
||||
resolve('node_modules/element-ui/packages/scrollbar'),
|
||||
resolve('node_modules/@interactjs'),
|
||||
resolve('node_modules/vue-grid-layout')
|
||||
],
|
||||
exclude: '/node_modules/',
|
||||
options: {
|
||||
presets: [
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"vue-color": "^2.8.1",
|
||||
"vue-countupjs": "^1.0.0",
|
||||
"vue-draggable-resizable": "^2.3.0",
|
||||
"vue-grid-layout": "2.3.7",
|
||||
"vue-grid-layout": "^2.3.12",
|
||||
"vue-i18n": "^8.15.1",
|
||||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-resource": "^1.5.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div :style="chartInfo.param.showHeader ? '' : 'padding-top: 15px;'" class="nz-chart" :class="chartInfo.param.showHeader ? '' : 'no-header'" >
|
||||
<loading :loading="loading"></loading>
|
||||
<chart-no-data v-if="isNoData || isError"></chart-no-data>
|
||||
<chart-no-data v-if="isNoData || isError || chartChildrenData"></chart-no-data>
|
||||
<template v-else>
|
||||
<chart-time-series
|
||||
v-if="isTimeSeries(chartInfo.type)"
|
||||
@@ -10,6 +10,7 @@
|
||||
:chart-info="chartInfo"
|
||||
:chart-option="chartOption"
|
||||
:is-fullscreen="isFullscreen"
|
||||
@chartIsNoData="chartIsNoData"
|
||||
></chart-time-series>
|
||||
<chart-pie
|
||||
:ref="'chart' + chartInfo.id"
|
||||
@@ -205,6 +206,11 @@ export default {
|
||||
from: String,
|
||||
isError: Boolean
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
chartChildrenData: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isNoData () {
|
||||
return lodash.isEmpty(this.chartData) && ['text', 'url'].indexOf(this.chartInfo.type) === -1
|
||||
@@ -215,7 +221,7 @@ export default {
|
||||
} else {
|
||||
return getOption(this.chartInfo.type)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isTimeSeries,
|
||||
@@ -235,6 +241,9 @@ export default {
|
||||
isMap,
|
||||
isTable,
|
||||
isGauge,
|
||||
chartIsNoData (flag) {
|
||||
this.chartChildrenData = flag
|
||||
},
|
||||
resize () {
|
||||
this.$refs['chart' + this.chartInfo.id].resize()
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ export default {
|
||||
gauge.min = getMetricTypeValue(data.values, 'min')
|
||||
gauge.label = data.metric
|
||||
gauge.legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
|
||||
gauge.name = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
|
||||
gauge.showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(gauge.value, null, -1, 2)
|
||||
// gauge.value = gauge.showValue
|
||||
gauge.mapping = this.selectMapping(gauge.value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
|
||||
@@ -155,6 +156,7 @@ export default {
|
||||
this.gaugeData.forEach((item, index) => {
|
||||
const myChart = echarts.init(document.getElementById('chart-gauge-' + this.chartInfo.id + '-' + index))
|
||||
const option = lodash.cloneDeep(this.chartOption)
|
||||
option.tooltip = {}
|
||||
option.series[0].data.push(item)
|
||||
option.series[0].max = item.max
|
||||
option.series[0].detail = {
|
||||
@@ -179,6 +181,8 @@ export default {
|
||||
color: lodash.get(item, 'mapping.color.bac', randomcolor())
|
||||
}
|
||||
}
|
||||
option.tooltip.formatter = this.formatterFunc
|
||||
// option.tooltip.position = this.formatterFunc
|
||||
option.series[0].min = item.max == item.min ? 0 : item.min
|
||||
myChart.setOption(option)
|
||||
this.chartInstances.push(myChart)
|
||||
@@ -186,7 +190,6 @@ export default {
|
||||
},
|
||||
gaugeChartResize () {
|
||||
this.chartInstances.forEach(item => {
|
||||
console.log(123123123, item)
|
||||
if (item && item.resize) {
|
||||
item.resize()
|
||||
}
|
||||
@@ -198,12 +201,27 @@ export default {
|
||||
this.gaugeChartResize()
|
||||
})
|
||||
})
|
||||
},
|
||||
formatterFunc: function (params, ticket, callback) {
|
||||
const self = this
|
||||
return `<div>
|
||||
<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 14px;">
|
||||
<div style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;margin-bottom: 5px">${params.data.name}</div>
|
||||
<div style="font-size:12px;display:flex;justify-content: space-between;">
|
||||
<div>value</div>
|
||||
<div style="display: ${params.data.mapping && params.data.mapping.display ? 'none' : 'inline-block'}">${params.data.showValue}</div>
|
||||
<div style="display: ${params.data.mapping && params.data.mapping.display ? 'inline-block' : 'none'}">${self.handleDisplay(params.data.mapping.display, { ...params.data.labels, value: params.data.showValue })}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.chartOption.color || (this.chartOption.color = initColor(20))
|
||||
this.colorList = this.chartOption.color
|
||||
this.initChart()
|
||||
console.log(this.isFullscreen , 'isFullscreen')
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.chartInstances.forEach(item => {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<span class="text">
|
||||
{{text}}
|
||||
<button>xxx</button>
|
||||
<span class="vue-draggable-handle"></span>
|
||||
</span>
|
||||
</template>
|
||||
<style>
|
||||
.vue-draggable-handle {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
|
||||
background-position: bottom right;
|
||||
padding: 0 8px 8px 0;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: content-box;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
name: 'CustomDragElement',
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
default: 'x'
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
console.log('### ' + this.text + ' ready!')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
46
nezha-fronted/src/components/chart/chart/grid/DOM.js
Normal file
46
nezha-fronted/src/components/chart/chart/grid/DOM.js
Normal file
@@ -0,0 +1,46 @@
|
||||
let currentDir = 'auto'
|
||||
// let currentDir = "auto";
|
||||
|
||||
function hasDocument () {
|
||||
return (typeof document !== 'undefined')
|
||||
}
|
||||
|
||||
function hasWindow () {
|
||||
return (typeof window !== 'undefined')
|
||||
}
|
||||
|
||||
export function getDocumentDir () {
|
||||
if (!hasDocument()) {
|
||||
return currentDir
|
||||
}
|
||||
const direction = (typeof document.dir !== 'undefined')
|
||||
? document.dir
|
||||
: document.getElementsByTagName('html')[0].getAttribute('dir')
|
||||
return direction
|
||||
}
|
||||
|
||||
export function setDocumentDir (dir = 'auto') {
|
||||
// export function setDocumentDir(dir){
|
||||
if (!hasDocument) {
|
||||
currentDir = dir
|
||||
return
|
||||
}
|
||||
|
||||
const html = document.getElementsByTagName('html')[0]
|
||||
html.setAttribute('dir', dir)
|
||||
}
|
||||
|
||||
export function addWindowEventListener (event, callback) {
|
||||
if (!hasWindow) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
window.addEventListener(event, callback)
|
||||
}
|
||||
|
||||
export function removeWindowEventListener (event, callback) {
|
||||
if (!hasWindow) {
|
||||
return
|
||||
}
|
||||
window.removeEventListener(event, callback)
|
||||
}
|
||||
864
nezha-fronted/src/components/chart/chart/grid/GridItem.vue
Normal file
864
nezha-fronted/src/components/chart/chart/grid/GridItem.vue
Normal file
@@ -0,0 +1,864 @@
|
||||
<template>
|
||||
<div ref="item"
|
||||
class="vue-grid-item"
|
||||
:class="classObj"
|
||||
:style="style"
|
||||
>
|
||||
<slot></slot>
|
||||
<span v-if="resizableAndNotStatic" ref="handle" :class="resizableHandleClass"></span>
|
||||
<!--<span v-if="draggable" ref="dragHandle" class="vue-draggable-handle"></span>-->
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.vue-grid-item {
|
||||
transition: all 200ms ease;
|
||||
transition-property: left, top, right;
|
||||
/* add right for rtl */
|
||||
}
|
||||
|
||||
.vue-grid-item.no-touch {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.vue-grid-item.cssTransforms {
|
||||
transition-property: transform;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.vue-grid-item.cssTransforms.render-rtl {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.vue-grid-item.resizing {
|
||||
opacity: 0.6;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.vue-grid-item.vue-draggable-dragging {
|
||||
transition:none;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.vue-grid-item.vue-grid-placeholder {
|
||||
background: red;
|
||||
opacity: 0.2;
|
||||
transition-duration: 100ms;
|
||||
z-index: 2;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.vue-grid-item > .vue-resizable-handle {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=');
|
||||
background-position: bottom right;
|
||||
padding: 0 3px 3px 0;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: content-box;
|
||||
box-sizing: border-box;
|
||||
cursor: se-resize;
|
||||
}
|
||||
|
||||
.vue-grid-item > .vue-rtl-resizable-handle {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAuMDAwMDAwMDAwMDAwMDAyIiBoZWlnaHQ9IjEwLjAwMDAwMDAwMDAwMDAwMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDwhLS0gQ3JlYXRlZCB3aXRoIE1ldGhvZCBEcmF3IC0gaHR0cDovL2dpdGh1Yi5jb20vZHVvcGl4ZWwvTWV0aG9kLURyYXcvIC0tPgogPGc+CiAgPHRpdGxlPmJhY2tncm91bmQ8L3RpdGxlPgogIDxyZWN0IGZpbGw9Im5vbmUiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgaGVpZ2h0PSIxMiIgd2lkdGg9IjEyIiB5PSItMSIgeD0iLTEiLz4KICA8ZyBkaXNwbGF5PSJub25lIiBvdmVyZmxvdz0idmlzaWJsZSIgeT0iMCIgeD0iMCIgaGVpZ2h0PSIxMDAlIiB3aWR0aD0iMTAwJSIgaWQ9ImNhbnZhc0dyaWQiPgogICA8cmVjdCBmaWxsPSJ1cmwoI2dyaWRwYXR0ZXJuKSIgc3Ryb2tlLXdpZHRoPSIwIiB5PSIwIiB4PSIwIiBoZWlnaHQ9IjEwMCUiIHdpZHRoPSIxMDAlIi8+CiAgPC9nPgogPC9nPgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxsaW5lIGNhbnZhcz0iI2ZmZmZmZiIgY2FudmFzLW9wYWNpdHk9IjEiIHN0cm9rZS1saW5lY2FwPSJ1bmRlZmluZWQiIHN0cm9rZS1saW5lam9pbj0idW5kZWZpbmVkIiBpZD0ic3ZnXzEiIHkyPSItNzAuMTc4NDA3IiB4Mj0iMTI0LjQ2NDE3NSIgeTE9Ii0zOC4zOTI3MzciIHgxPSIxNDQuODIxMjg5IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSJub25lIi8+CiAgPGxpbmUgc3Ryb2tlPSIjNjY2NjY2IiBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z181IiB5Mj0iOS4xMDY5NTciIHgyPSIwLjk0NzI0NyIgeTE9Ii0wLjAxODEyOCIgeDE9IjAuOTQ3MjQ3IiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz4KICA8bGluZSBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z183IiB5Mj0iOSIgeDI9IjEwLjA3MzUyOSIgeTE9IjkiIHgxPSItMC42NTU2NCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM2NjY2NjYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+);
|
||||
background-position: bottom left;
|
||||
padding-left: 3px;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: content-box;
|
||||
cursor: sw-resize;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.vue-grid-item.disable-userselect {
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { setTopLeft, setTopRight, setTransformRtl, setTransform } from './utils'
|
||||
import { getControlPosition, createCoreData } from './draggableUtils'
|
||||
import { getColsFromBreakpoint } from './responsiveUtils'
|
||||
import { getDocumentDir } from './DOM'
|
||||
// var eventBus = require('./eventBus');
|
||||
|
||||
import '@interactjs/auto-start'
|
||||
import '@interactjs/actions/drag'
|
||||
import '@interactjs/actions/resize'
|
||||
import '@interactjs/modifiers'
|
||||
import '@interactjs/dev-tools'
|
||||
import interact from '@interactjs/interact'
|
||||
|
||||
export default {
|
||||
name: 'GridItem',
|
||||
props: {
|
||||
/* cols: {
|
||||
type: Number,
|
||||
required: true
|
||||
}, */
|
||||
/* containerWidth: {
|
||||
type: Number,
|
||||
required: true
|
||||
|
||||
},
|
||||
rowHeight: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
margin: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
maxRows: {
|
||||
type: Number,
|
||||
required: true
|
||||
}, */
|
||||
isDraggable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
isResizable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
/* useCssTransforms: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
*/
|
||||
static: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
minH: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
},
|
||||
minW: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
},
|
||||
maxH: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: Infinity
|
||||
},
|
||||
maxW: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: Infinity
|
||||
},
|
||||
x: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
y: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
w: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
h: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
i: {
|
||||
required: true
|
||||
},
|
||||
dragIgnoreFrom: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'a, button'
|
||||
},
|
||||
dragAllowFrom: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
resizeIgnoreFrom: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'a, button'
|
||||
}
|
||||
},
|
||||
inject: ['eventBus', 'layout'],
|
||||
data: function () {
|
||||
return {
|
||||
cols: 1,
|
||||
containerWidth: 100,
|
||||
rowHeight: 30,
|
||||
margin: [10, 10],
|
||||
maxRows: Infinity,
|
||||
draggable: null,
|
||||
resizable: null,
|
||||
useCssTransforms: true,
|
||||
useStyleCursor: true,
|
||||
|
||||
isDragging: false,
|
||||
dragging: null,
|
||||
isResizing: false,
|
||||
resizing: null,
|
||||
lastX: NaN,
|
||||
lastY: NaN,
|
||||
lastW: NaN,
|
||||
lastH: NaN,
|
||||
style: {},
|
||||
rtl: false,
|
||||
|
||||
dragEventSet: false,
|
||||
resizeEventSet: false,
|
||||
|
||||
previousW: null,
|
||||
previousH: null,
|
||||
previousX: null,
|
||||
previousY: null,
|
||||
innerX: this.x,
|
||||
innerY: this.y,
|
||||
innerW: this.w,
|
||||
innerH: this.h
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const self = this
|
||||
|
||||
// Accessible refernces of functions for removing in beforeDestroy
|
||||
self.updateWidthHandler = function (width) {
|
||||
self.updateWidth(width)
|
||||
}
|
||||
|
||||
self.compactHandler = function (layout) {
|
||||
self.compact(layout)
|
||||
}
|
||||
|
||||
self.setDraggableHandler = function (isDraggable) {
|
||||
if (self.isDraggable === null) {
|
||||
self.draggable = isDraggable
|
||||
}
|
||||
}
|
||||
|
||||
self.setResizableHandler = function (isResizable) {
|
||||
if (self.isResizable === null) {
|
||||
self.resizable = isResizable
|
||||
}
|
||||
}
|
||||
|
||||
self.setRowHeightHandler = function (rowHeight) {
|
||||
self.rowHeight = rowHeight
|
||||
}
|
||||
|
||||
self.setMaxRowsHandler = function (maxRows) {
|
||||
self.maxRows = maxRows
|
||||
}
|
||||
|
||||
self.directionchangeHandler = () => {
|
||||
this.rtl = getDocumentDir() === 'rtl'
|
||||
this.compact()
|
||||
}
|
||||
|
||||
self.setColNum = (colNum) => {
|
||||
self.cols = parseInt(colNum)
|
||||
}
|
||||
|
||||
this.eventBus.$on('updateWidth', self.updateWidthHandler)
|
||||
this.eventBus.$on('compact', self.compactHandler)
|
||||
this.eventBus.$on('setDraggable', self.setDraggableHandler)
|
||||
this.eventBus.$on('setResizable', self.setResizableHandler)
|
||||
this.eventBus.$on('setRowHeight', self.setRowHeightHandler)
|
||||
this.eventBus.$on('setMaxRows', self.setMaxRowsHandler)
|
||||
this.eventBus.$on('directionchange', self.directionchangeHandler)
|
||||
this.eventBus.$on('setColNum', self.setColNum)
|
||||
|
||||
this.rtl = getDocumentDir() === 'rtl'
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
const self = this
|
||||
// Remove listeners
|
||||
this.eventBus.$off('updateWidth', self.updateWidthHandler)
|
||||
this.eventBus.$off('compact', self.compactHandler)
|
||||
this.eventBus.$off('setDraggable', self.setDraggableHandler)
|
||||
this.eventBus.$off('setResizable', self.setResizableHandler)
|
||||
this.eventBus.$off('setRowHeight', self.setRowHeightHandler)
|
||||
this.eventBus.$off('setMaxRows', self.setMaxRowsHandler)
|
||||
this.eventBus.$off('directionchange', self.directionchangeHandler)
|
||||
this.eventBus.$off('setColNum', self.setColNum)
|
||||
if (this.interactObj) {
|
||||
this.interactObj.unset() // destroy interact intance
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
if (this.layout.responsive && this.layout.lastBreakpoint) {
|
||||
this.cols = getColsFromBreakpoint(this.layout.lastBreakpoint, this.layout.cols)
|
||||
} else {
|
||||
this.cols = this.layout.colNum
|
||||
}
|
||||
this.rowHeight = this.layout.rowHeight
|
||||
this.containerWidth = this.layout.width !== null ? this.layout.width : 100
|
||||
this.margin = this.layout.margin !== undefined ? this.layout.margin : [10, 10]
|
||||
this.maxRows = this.layout.maxRows
|
||||
|
||||
if (this.isDraggable === null) {
|
||||
this.draggable = this.layout.isDraggable
|
||||
} else {
|
||||
this.draggable = this.isDraggable
|
||||
}
|
||||
if (this.isResizable === null) {
|
||||
this.resizable = this.layout.isResizable
|
||||
} else {
|
||||
this.resizable = this.isResizable
|
||||
}
|
||||
this.useCssTransforms = this.layout.useCssTransforms
|
||||
this.useStyleCursor = this.layout.useStyleCursor
|
||||
this.createStyle()
|
||||
},
|
||||
watch: {
|
||||
isDraggable: function () {
|
||||
this.draggable = this.isDraggable
|
||||
},
|
||||
static: function () {
|
||||
this.tryMakeDraggable()
|
||||
this.tryMakeResizable()
|
||||
},
|
||||
draggable: function () {
|
||||
this.tryMakeDraggable()
|
||||
},
|
||||
isResizable: function () {
|
||||
this.resizable = this.isResizable
|
||||
},
|
||||
resizable: function () {
|
||||
this.tryMakeResizable()
|
||||
},
|
||||
rowHeight: function () {
|
||||
this.createStyle()
|
||||
this.emitContainerResized()
|
||||
},
|
||||
cols: function () {
|
||||
this.tryMakeResizable()
|
||||
this.createStyle()
|
||||
this.emitContainerResized()
|
||||
},
|
||||
containerWidth: function () {
|
||||
this.tryMakeResizable()
|
||||
this.createStyle()
|
||||
this.emitContainerResized()
|
||||
},
|
||||
x: function (newVal) {
|
||||
this.innerX = newVal
|
||||
this.createStyle()
|
||||
},
|
||||
y: function (newVal) {
|
||||
this.innerY = newVal
|
||||
this.createStyle()
|
||||
},
|
||||
h: function (newVal) {
|
||||
this.innerH = newVal
|
||||
this.createStyle()
|
||||
// this.emitContainerResized();
|
||||
},
|
||||
w: function (newVal) {
|
||||
this.innerW = newVal
|
||||
this.createStyle()
|
||||
// this.emitContainerResized();
|
||||
},
|
||||
renderRtl: function () {
|
||||
// console.log("### renderRtl");
|
||||
this.tryMakeResizable()
|
||||
this.createStyle()
|
||||
},
|
||||
minH: function () {
|
||||
this.tryMakeResizable()
|
||||
},
|
||||
maxH: function () {
|
||||
this.tryMakeResizable()
|
||||
},
|
||||
minW: function () {
|
||||
this.tryMakeResizable()
|
||||
},
|
||||
maxW: function () {
|
||||
this.tryMakeResizable()
|
||||
},
|
||||
'$parent.margin': function (margin) {
|
||||
if (!margin || (margin[0] == this.margin[0] && margin[1] == this.margin[1])) {
|
||||
return
|
||||
}
|
||||
this.margin = margin.map(m => Number(m))
|
||||
this.createStyle()
|
||||
this.emitContainerResized()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classObj () {
|
||||
return {
|
||||
'vue-resizable': this.resizableAndNotStatic,
|
||||
static: this.static,
|
||||
resizing: this.isResizing,
|
||||
'vue-draggable-dragging': this.isDragging,
|
||||
cssTransforms: this.useCssTransforms,
|
||||
'render-rtl': this.renderRtl,
|
||||
'disable-userselect': this.isDragging,
|
||||
'no-touch': this.isAndroid && this.draggableOrResizableAndNotStatic
|
||||
}
|
||||
},
|
||||
resizableAndNotStatic () {
|
||||
return this.resizable && !this.static
|
||||
},
|
||||
draggableOrResizableAndNotStatic () {
|
||||
return (this.draggable || this.resizable) && !this.static
|
||||
},
|
||||
isAndroid () {
|
||||
return navigator.userAgent.toLowerCase().indexOf('android') !== -1
|
||||
},
|
||||
renderRtl () {
|
||||
return (this.layout.isMirrored) ? !this.rtl : this.rtl
|
||||
},
|
||||
resizableHandleClass () {
|
||||
if (this.renderRtl) {
|
||||
return 'vue-resizable-handle vue-rtl-resizable-handle'
|
||||
} else {
|
||||
return 'vue-resizable-handle'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createStyle: function () {
|
||||
if (this.x + this.w > this.cols) {
|
||||
this.innerX = 0
|
||||
this.innerW = (this.w > this.cols) ? this.cols : this.w
|
||||
} else {
|
||||
this.innerX = this.x
|
||||
this.innerW = this.w
|
||||
}
|
||||
const pos = this.calcPosition(this.innerX, this.innerY, this.innerW, this.innerH)
|
||||
|
||||
if (this.isDragging) {
|
||||
pos.top = this.dragging.top
|
||||
// Add rtl support
|
||||
if (this.renderRtl) {
|
||||
pos.right = this.dragging.left
|
||||
} else {
|
||||
pos.left = this.dragging.left
|
||||
}
|
||||
}
|
||||
if (this.isResizing) {
|
||||
pos.width = this.resizing.width
|
||||
pos.height = this.resizing.height
|
||||
}
|
||||
|
||||
let style
|
||||
// CSS Transforms support (default)
|
||||
if (this.useCssTransforms) {
|
||||
// Add rtl support
|
||||
if (this.renderRtl) {
|
||||
style = setTransformRtl(pos.top, pos.right, pos.width, pos.height)
|
||||
} else {
|
||||
style = setTransform(pos.top, pos.left, pos.width, pos.height)
|
||||
}
|
||||
} else { // top,left (slow)
|
||||
// Add rtl support
|
||||
if (this.renderRtl) {
|
||||
style = setTopRight(pos.top, pos.right, pos.width, pos.height)
|
||||
} else {
|
||||
style = setTopLeft(pos.top, pos.left, pos.width, pos.height)
|
||||
}
|
||||
}
|
||||
this.style = style
|
||||
},
|
||||
emitContainerResized () {
|
||||
// this.style has width and height with trailing 'px'. The
|
||||
// resized event is without them
|
||||
const styleProps = {}
|
||||
for (const prop of ['width', 'height']) {
|
||||
const val = this.style[prop]
|
||||
const matches = val.match(/^(\d+)px$/)
|
||||
if (!matches) { return }
|
||||
styleProps[prop] = matches[1]
|
||||
}
|
||||
this.$emit('container-resized', this.i, this.h, this.w, styleProps.height, styleProps.width)
|
||||
},
|
||||
handleResize: function (event) {
|
||||
if (this.static) return
|
||||
const position = getControlPosition(event)
|
||||
// Get the current drag point from the event. This is used as the offset.
|
||||
if (position == null) return // not possible but satisfies flow
|
||||
const { x, y } = position
|
||||
|
||||
const newSize = { width: 0, height: 0 }
|
||||
let pos
|
||||
switch (event.type) {
|
||||
case 'resizestart': {
|
||||
this.previousW = this.innerW
|
||||
this.previousH = this.innerH
|
||||
pos = this.calcPosition(this.innerX, this.innerY, this.innerW, this.innerH)
|
||||
newSize.width = pos.width
|
||||
newSize.height = pos.height
|
||||
this.resizing = newSize
|
||||
this.isResizing = true
|
||||
break
|
||||
}
|
||||
case 'resizemove': {
|
||||
// console.log("### resize => " + event.type + ", lastW=" + this.lastW + ", lastH=" + this.lastH);
|
||||
const coreEvent = createCoreData(this.lastW, this.lastH, x, y)
|
||||
if (this.renderRtl) {
|
||||
newSize.width = this.resizing.width - coreEvent.deltaX
|
||||
} else {
|
||||
newSize.width = this.resizing.width + coreEvent.deltaX
|
||||
}
|
||||
newSize.height = this.resizing.height + coreEvent.deltaY
|
||||
|
||||
/// console.log("### resize => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
|
||||
this.resizing = newSize
|
||||
break
|
||||
}
|
||||
case 'resizeend': {
|
||||
// console.log("### resize end => x=" +this.innerX + " y=" + this.innerY + " w=" + this.innerW + " h=" + this.innerH);
|
||||
pos = this.calcPosition(this.innerX, this.innerY, this.innerW, this.innerH)
|
||||
newSize.width = pos.width
|
||||
newSize.height = pos.height
|
||||
// console.log("### resize end => " + JSON.stringify(newSize));
|
||||
this.resizing = null
|
||||
this.isResizing = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get new WH
|
||||
pos = this.calcWH(newSize.height, newSize.width)
|
||||
if (pos.w < this.minW) {
|
||||
pos.w = this.minW
|
||||
}
|
||||
if (pos.w > this.maxW) {
|
||||
pos.w = this.maxW
|
||||
}
|
||||
if (pos.h < this.minH) {
|
||||
pos.h = this.minH
|
||||
}
|
||||
if (pos.h > this.maxH) {
|
||||
pos.h = this.maxH
|
||||
}
|
||||
|
||||
if (pos.h < 1) {
|
||||
pos.h = 1
|
||||
}
|
||||
if (pos.w < 1) {
|
||||
pos.w = 1
|
||||
}
|
||||
|
||||
this.lastW = x
|
||||
this.lastH = y
|
||||
|
||||
if (this.innerW !== pos.w || this.innerH !== pos.h) {
|
||||
this.$emit('resize', this.i, pos.h, pos.w, newSize.height, newSize.width)
|
||||
}
|
||||
if (event.type === 'resizeend' && (this.previousW !== this.innerW || this.previousH !== this.innerH)) {
|
||||
this.$emit('resized', this.i, pos.h, pos.w, newSize.height, newSize.width)
|
||||
}
|
||||
this.eventBus.$emit('resizeEvent', event.type, this.i, this.innerX, this.innerY, pos.h, pos.w)
|
||||
},
|
||||
handleDrag (event) {
|
||||
if (this.static) return
|
||||
if (this.isResizing) return
|
||||
|
||||
const position = getControlPosition(event)
|
||||
|
||||
// Get the current drag point from the event. This is used as the offset.
|
||||
if (position === null) return // not possible but satisfies flow
|
||||
const { x, y } = position
|
||||
|
||||
// let shouldUpdate = false;
|
||||
const newPosition = { top: 0, left: 0 }
|
||||
switch (event.type) {
|
||||
case 'dragstart': {
|
||||
this.previousX = this.innerX
|
||||
this.previousY = this.innerY
|
||||
|
||||
const parentRect = event.target.offsetParent.getBoundingClientRect()
|
||||
const clientRect = event.target.getBoundingClientRect()
|
||||
if (this.renderRtl) {
|
||||
newPosition.left = (clientRect.right - parentRect.right) * -1
|
||||
} else {
|
||||
newPosition.left = clientRect.left - parentRect.left
|
||||
}
|
||||
newPosition.top = clientRect.top - parentRect.top
|
||||
this.dragging = newPosition
|
||||
this.isDragging = true
|
||||
break
|
||||
}
|
||||
case 'dragend': {
|
||||
if (!this.isDragging) return
|
||||
const parentRect = event.target.offsetParent.getBoundingClientRect()
|
||||
const clientRect = event.target.getBoundingClientRect()
|
||||
// Add rtl support
|
||||
if (this.renderRtl) {
|
||||
newPosition.left = (clientRect.right - parentRect.right) * -1
|
||||
} else {
|
||||
newPosition.left = clientRect.left - parentRect.left
|
||||
}
|
||||
newPosition.top = clientRect.top - parentRect.top
|
||||
// console.log("### drag end => " + JSON.stringify(newPosition));
|
||||
// console.log("### DROP: " + JSON.stringify(newPosition));
|
||||
this.dragging = null
|
||||
this.isDragging = false
|
||||
// shouldUpdate = true;
|
||||
break
|
||||
}
|
||||
case 'dragmove': {
|
||||
const coreEvent = createCoreData(this.lastX, this.lastY, x, y)
|
||||
// Add rtl support
|
||||
if (this.renderRtl) {
|
||||
newPosition.left = this.dragging.left - coreEvent.deltaX
|
||||
} else {
|
||||
newPosition.left = this.dragging.left + coreEvent.deltaX
|
||||
}
|
||||
newPosition.top = this.dragging.top + coreEvent.deltaY
|
||||
// console.log("### drag => " + event.type + ", x=" + x + ", y=" + y);
|
||||
// console.log("### drag => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
|
||||
// console.log("### drag end => " + JSON.stringify(newPosition));
|
||||
this.dragging = newPosition
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get new XY
|
||||
let pos
|
||||
if (this.renderRtl) {
|
||||
pos = this.calcXY(newPosition.top, newPosition.left)
|
||||
} else {
|
||||
pos = this.calcXY(newPosition.top, newPosition.left)
|
||||
}
|
||||
|
||||
this.lastX = x
|
||||
this.lastY = y
|
||||
|
||||
if (this.innerX !== pos.x || this.innerY !== pos.y) {
|
||||
this.$emit('move', this.i, pos.x, pos.y)
|
||||
}
|
||||
if (event.type === 'dragend' && (this.previousX !== this.innerX || this.previousY !== this.innerY)) {
|
||||
this.$emit('moved', this.i, pos.x, pos.y)
|
||||
}
|
||||
this.eventBus.$emit('dragEvent', event.type, this.i, pos.x, pos.y, this.innerH, this.innerW)
|
||||
},
|
||||
calcPosition: function (x, y, w, h) {
|
||||
const colWidth = this.calcColWidth()
|
||||
// add rtl support
|
||||
let out
|
||||
if (this.renderRtl) {
|
||||
out = {
|
||||
right: Math.round(colWidth * x + (x + 1) * this.margin[0]),
|
||||
top: Math.round(this.rowHeight * y + (y + 1) * this.margin[1]),
|
||||
// 0 * Infinity === NaN, which causes problems with resize constriants;
|
||||
// Fix this if it occurs.
|
||||
// Note we do it here rather than later because Math.round(Infinity) causes deopt
|
||||
width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
|
||||
height: h === Infinity ? h : Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])
|
||||
}
|
||||
} else {
|
||||
out = {
|
||||
left: Math.round(colWidth * x + (x + 1) * this.margin[0]),
|
||||
top: Math.round(this.rowHeight * y + (y + 1) * this.margin[1]),
|
||||
// 0 * Infinity === NaN, which causes problems with resize constriants;
|
||||
// Fix this if it occurs.
|
||||
// Note we do it here rather than later because Math.round(Infinity) causes deopt
|
||||
width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
|
||||
height: h === Infinity ? h : Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
},
|
||||
/**
|
||||
* Translate x and y coordinates from pixels to grid units.
|
||||
* @param {Number} top Top position (relative to parent) in pixels.
|
||||
* @param {Number} left Left position (relative to parent) in pixels.
|
||||
* @return {Object} x and y in grid units.
|
||||
*/
|
||||
// TODO check if this function needs change in order to support rtl.
|
||||
calcXY (top, left) {
|
||||
const colWidth = this.calcColWidth()
|
||||
|
||||
// left = colWidth * x + margin * (x + 1)
|
||||
// l = cx + m(x+1)
|
||||
// l = cx + mx + m
|
||||
// l - m = cx + mx
|
||||
// l - m = x(c + m)
|
||||
// (l - m) / (c + m) = x
|
||||
// x = (left - margin) / (coldWidth + margin)
|
||||
let x = Math.round((left - this.margin[0]) / (colWidth + this.margin[0]))
|
||||
let y = Math.round((top - this.margin[1]) / (this.rowHeight + this.margin[1]))
|
||||
|
||||
// Capping
|
||||
x = Math.max(Math.min(x, this.cols - this.innerW), 0)
|
||||
y = Math.max(Math.min(y, this.maxRows - this.innerH), 0)
|
||||
|
||||
return { x, y }
|
||||
},
|
||||
// Helper for generating column width
|
||||
calcColWidth () {
|
||||
const colWidth = (this.containerWidth - (this.margin[0] * (this.cols + 1))) / this.cols
|
||||
// console.log("### COLS=" + this.cols + " COL WIDTH=" + colWidth + " MARGIN " + this.margin[0]);
|
||||
return colWidth
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a height and width in pixel values, calculate grid units.
|
||||
* @param {Number} height Height in pixels.
|
||||
* @param {Number} width Width in pixels.
|
||||
* @return {Object} w, h as grid units.
|
||||
*/
|
||||
calcWH (height, width) {
|
||||
const colWidth = this.calcColWidth()
|
||||
|
||||
// width = colWidth * w - (margin * (w - 1))
|
||||
// ...
|
||||
// w = (width + margin) / (colWidth + margin)
|
||||
let w = Math.round((width + this.margin[0]) / (colWidth + this.margin[0]))
|
||||
let h = Math.round((height + this.margin[1]) / (this.rowHeight + this.margin[1]))
|
||||
|
||||
// Capping
|
||||
w = Math.max(Math.min(w, this.cols - this.innerX), 0)
|
||||
h = Math.max(Math.min(h, this.maxRows - this.innerY), 0)
|
||||
return { w, h }
|
||||
},
|
||||
updateWidth: function (width, colNum) {
|
||||
this.containerWidth = width
|
||||
if (colNum !== undefined && colNum !== null) {
|
||||
this.cols = colNum
|
||||
}
|
||||
},
|
||||
compact: function () {
|
||||
this.createStyle()
|
||||
},
|
||||
tryMakeDraggable: function () {
|
||||
const self = this
|
||||
if (this.interactObj === null || this.interactObj === undefined) {
|
||||
this.interactObj = interact(this.$refs.item)
|
||||
if (!this.useStyleCursor) {
|
||||
this.interactObj.styleCursor(false)
|
||||
}
|
||||
}
|
||||
if (this.draggable && !this.static) {
|
||||
const opts = {
|
||||
ignoreFrom: this.dragIgnoreFrom,
|
||||
allowFrom: this.dragAllowFrom
|
||||
}
|
||||
this.interactObj.draggable(opts)
|
||||
/* this.interactObj.draggable({allowFrom: '.vue-draggable-handle'}); */
|
||||
if (!this.dragEventSet) {
|
||||
this.dragEventSet = true
|
||||
this.interactObj.on('dragstart dragmove dragend', function (event) {
|
||||
self.handleDrag(event)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.interactObj.draggable({
|
||||
enabled: false
|
||||
})
|
||||
}
|
||||
},
|
||||
tryMakeResizable: function () {
|
||||
const self = this
|
||||
if (this.interactObj === null || this.interactObj === undefined) {
|
||||
this.interactObj = interact(this.$refs.item)
|
||||
if (!this.useStyleCursor) {
|
||||
this.interactObj.styleCursor(false)
|
||||
}
|
||||
}
|
||||
if (this.resizable && !this.static) {
|
||||
const maximum = this.calcPosition(0, 0, this.maxW, this.maxH)
|
||||
const minimum = this.calcPosition(0, 0, this.minW, this.minH)
|
||||
|
||||
// console.log("### MAX " + JSON.stringify(maximum));
|
||||
// console.log("### MIN " + JSON.stringify(minimum));
|
||||
|
||||
const opts = {
|
||||
preserveAspectRatio: true,
|
||||
// allowFrom: "." + this.resizableHandleClass.trim().replace(" ", "."),
|
||||
edges: {
|
||||
left: false,
|
||||
right: '.' + this.resizableHandleClass.trim().replace(' ', '.'),
|
||||
bottom: '.' + this.resizableHandleClass.trim().replace(' ', '.'),
|
||||
top: false
|
||||
},
|
||||
ignoreFrom: this.resizeIgnoreFrom,
|
||||
restrictSize: {
|
||||
min: {
|
||||
height: minimum.height,
|
||||
width: minimum.width
|
||||
},
|
||||
max: {
|
||||
height: maximum.height,
|
||||
width: maximum.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.interactObj.resizable(opts)
|
||||
if (!this.resizeEventSet) {
|
||||
this.resizeEventSet = true
|
||||
this.interactObj
|
||||
.on('resizestart resizemove resizeend', function (event) {
|
||||
self.handleResize(event)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.interactObj.resizable({
|
||||
enabled: false
|
||||
})
|
||||
}
|
||||
},
|
||||
autoSize: function () {
|
||||
// ok here we want to calculate if a resize is needed
|
||||
this.previousW = this.innerW
|
||||
this.previousH = this.innerH
|
||||
|
||||
const newSize = this.$slots.default[0].elm.getBoundingClientRect()
|
||||
const pos = this.calcWH(newSize.height, newSize.width)
|
||||
if (pos.w < this.minW) {
|
||||
pos.w = this.minW
|
||||
}
|
||||
if (pos.w > this.maxW) {
|
||||
pos.w = this.maxW
|
||||
}
|
||||
if (pos.h < this.minH) {
|
||||
pos.h = this.minH
|
||||
}
|
||||
if (pos.h > this.maxH) {
|
||||
pos.h = this.maxH
|
||||
}
|
||||
|
||||
if (pos.h < 1) {
|
||||
pos.h = 1
|
||||
}
|
||||
if (pos.w < 1) {
|
||||
pos.w = 1
|
||||
}
|
||||
|
||||
// this.lastW = x; // basically, this is copied from resizehandler, but shouldn't be needed
|
||||
// this.lastH = y;
|
||||
|
||||
if (this.innerW !== pos.w || this.innerH !== pos.h) {
|
||||
this.$emit('resize', this.i, pos.h, pos.w, newSize.height, newSize.width)
|
||||
}
|
||||
if (this.previousW !== pos.w || this.previousH !== pos.h) {
|
||||
this.$emit('resized', this.i, pos.h, pos.w, newSize.height, newSize.width)
|
||||
this.eventBus.$emit('resizeEvent', 'resizeend', this.i, this.innerX, this.innerY, pos.h, pos.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
462
nezha-fronted/src/components/chart/chart/grid/GridLayout.vue
Normal file
462
nezha-fronted/src/components/chart/chart/grid/GridLayout.vue
Normal file
@@ -0,0 +1,462 @@
|
||||
<template>
|
||||
<div ref="item" class="vue-grid-layout" :style="mergedStyle">
|
||||
<slot></slot>
|
||||
<grid-item class="vue-grid-placeholder"
|
||||
v-show="isDragging"
|
||||
:x="placeholder.x"
|
||||
:y="placeholder.y"
|
||||
:w="placeholder.w"
|
||||
:h="placeholder.h"
|
||||
:i="placeholder.i"></grid-item>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.vue-grid-layout {
|
||||
position: relative;
|
||||
transition: height 200ms ease;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
|
||||
import { bottom, compact, getLayoutItem, moveElement, validateLayout, cloneLayout, getAllCollisions } from './utils'
|
||||
import { getBreakpointFromWidth, getColsFromBreakpoint, findOrGenerateResponsiveLayout } from './responsiveUtils'
|
||||
// var eventBus = require('./eventBus');
|
||||
|
||||
import GridItem from './GridItem.vue'
|
||||
import { addWindowEventListener, removeWindowEventListener } from './DOM'
|
||||
const elementResizeDetectorMaker = require('element-resize-detector')
|
||||
|
||||
export default {
|
||||
name: 'GridLayout',
|
||||
provide () {
|
||||
return {
|
||||
eventBus: null,
|
||||
layout: this
|
||||
}
|
||||
},
|
||||
components: {
|
||||
GridItem
|
||||
},
|
||||
props: {
|
||||
// If true, the container height swells and contracts to fit contents
|
||||
autoSize: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
colNum: {
|
||||
type: Number,
|
||||
default: 12
|
||||
},
|
||||
rowHeight: {
|
||||
type: Number,
|
||||
default: 150
|
||||
},
|
||||
maxRows: {
|
||||
type: Number,
|
||||
default: Infinity
|
||||
},
|
||||
margin: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [10, 10]
|
||||
}
|
||||
},
|
||||
isDraggable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isResizable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isMirrored: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useCssTransforms: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
verticalCompact: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
layout: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
responsive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
responsiveLayouts: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
breakpoints: {
|
||||
type: Object,
|
||||
default: function () { return { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } }
|
||||
},
|
||||
cols: {
|
||||
type: Object,
|
||||
default: function () { return { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 } }
|
||||
},
|
||||
preventCollision: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useStyleCursor: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
width: null,
|
||||
mergedStyle: {},
|
||||
lastLayoutLength: 0,
|
||||
isDragging: false,
|
||||
placeholder: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 0,
|
||||
h: 0,
|
||||
i: -1
|
||||
},
|
||||
layouts: {}, // array to store all layouts from different breakpoints
|
||||
lastBreakpoint: null, // store last active breakpoint
|
||||
originalLayout: null // store original Layout
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const self = this
|
||||
|
||||
// Accessible refernces of functions for removing in beforeDestroy
|
||||
self.resizeEventHandler = function (eventType, i, x, y, h, w) {
|
||||
self.resizeEvent(eventType, i, x, y, h, w)
|
||||
}
|
||||
|
||||
self.dragEventHandler = function (eventType, i, x, y, h, w) {
|
||||
self.dragEvent(eventType, i, x, y, h, w)
|
||||
}
|
||||
|
||||
self._provided.eventBus = new Vue()
|
||||
self.eventBus = self._provided.eventBus
|
||||
self.eventBus.$on('resizeEvent', self.resizeEventHandler)
|
||||
self.eventBus.$on('dragEvent', self.dragEventHandler)
|
||||
self.$emit('layout-created', self.layout)
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
// Remove listeners
|
||||
this.eventBus.$off('resizeEvent', this.resizeEventHandler)
|
||||
this.eventBus.$off('dragEvent', this.dragEventHandler)
|
||||
this.eventBus.$destroy()
|
||||
removeWindowEventListener('resize', this.onWindowResize)
|
||||
this.erd.uninstall(this.$refs.item)
|
||||
},
|
||||
beforeMount: function () {
|
||||
this.$emit('layout-before-mount', this.layout)
|
||||
},
|
||||
mounted: function () {
|
||||
this.$emit('layout-mounted', this.layout)
|
||||
this.$nextTick(function () {
|
||||
validateLayout(this.layout)
|
||||
|
||||
this.originalLayout = this.layout
|
||||
const self = this
|
||||
this.$nextTick(function () {
|
||||
self.onWindowResize()
|
||||
|
||||
self.initResponsiveFeatures()
|
||||
|
||||
// self.width = self.$el.offsetWidth;
|
||||
addWindowEventListener('resize', self.onWindowResize)
|
||||
|
||||
compact(self.layout, self.verticalCompact)
|
||||
|
||||
self.$emit('layout-updated', self.layout)
|
||||
|
||||
self.updateHeight()
|
||||
self.$nextTick(function () {
|
||||
this.erd = elementResizeDetectorMaker({
|
||||
strategy: 'scroll', // <- For ultra performance.
|
||||
// See https://github.com/wnr/element-resize-detector/issues/110 about callOnAdd.
|
||||
callOnAdd: false
|
||||
})
|
||||
this.erd.listenTo(self.$refs.item, function () {
|
||||
self.onWindowResize()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
width: function (newval, oldval) {
|
||||
const self = this
|
||||
this.$nextTick(function () {
|
||||
// this.$broadcast("updateWidth", this.width);
|
||||
this.eventBus.$emit('updateWidth', this.width)
|
||||
if (oldval === null) {
|
||||
/*
|
||||
If oldval == null is when the width has never been
|
||||
set before. That only occurs when mouting is
|
||||
finished, and onWindowResize has been called and
|
||||
this.width has been changed the first time after it
|
||||
got set to null in the constructor. It is now time
|
||||
to issue layout-ready events as the GridItems have
|
||||
their sizes configured properly.
|
||||
|
||||
The reason for emitting the layout-ready events on
|
||||
the next tick is to allow for the newly-emitted
|
||||
updateWidth event (above) to have reached the
|
||||
children GridItem-s and had their effect, so we're
|
||||
sure that they have the final size before we emit
|
||||
layout-ready (for this GridLayout) and
|
||||
item-layout-ready (for the GridItem-s).
|
||||
|
||||
This way any client event handlers can reliably
|
||||
invistigate stable sizes of GridItem-s.
|
||||
*/
|
||||
this.$nextTick(() => {
|
||||
this.$emit('layout-ready', self.layout)
|
||||
})
|
||||
}
|
||||
this.updateHeight()
|
||||
})
|
||||
},
|
||||
layout: function () {
|
||||
this.layoutUpdate()
|
||||
},
|
||||
colNum: function (val) {
|
||||
this.eventBus.$emit('setColNum', val)
|
||||
},
|
||||
rowHeight: function () {
|
||||
this.eventBus.$emit('setRowHeight', this.rowHeight)
|
||||
},
|
||||
isDraggable: function () {
|
||||
this.eventBus.$emit('setDraggable', this.isDraggable)
|
||||
},
|
||||
isResizable: function () {
|
||||
this.eventBus.$emit('setResizable', this.isResizable)
|
||||
},
|
||||
responsive () {
|
||||
if (!this.responsive) {
|
||||
this.$emit('update:layout', this.originalLayout)
|
||||
this.eventBus.$emit('setColNum', this.colNum)
|
||||
}
|
||||
this.onWindowResize()
|
||||
},
|
||||
maxRows: function () {
|
||||
this.eventBus.$emit('setMaxRows', this.maxRows)
|
||||
},
|
||||
margin () {
|
||||
this.updateHeight()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
layoutUpdate () {
|
||||
if (this.layout !== undefined && this.originalLayout !== null) {
|
||||
if (this.layout.length !== this.originalLayout.length) {
|
||||
// console.log("### LAYOUT UPDATE!", this.layout.length, this.originalLayout.length);
|
||||
|
||||
const diff = this.findDifference(this.layout, this.originalLayout)
|
||||
if (diff.length > 0) {
|
||||
// console.log(diff);
|
||||
if (this.layout.length > this.originalLayout.length) {
|
||||
this.originalLayout = this.originalLayout.concat(diff)
|
||||
} else {
|
||||
this.originalLayout = this.originalLayout.filter(obj => {
|
||||
return !diff.some(obj2 => {
|
||||
return obj.i === obj2.i
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.lastLayoutLength = this.layout.length
|
||||
this.initResponsiveFeatures()
|
||||
}
|
||||
|
||||
compact(this.layout, this.verticalCompact)
|
||||
this.eventBus.$emit('updateWidth', this.width)
|
||||
this.updateHeight()
|
||||
|
||||
this.$emit('layout-updated', this.layout)
|
||||
}
|
||||
},
|
||||
updateHeight: function () {
|
||||
this.mergedStyle = {
|
||||
height: this.containerHeight()
|
||||
}
|
||||
},
|
||||
onWindowResize: function () {
|
||||
if (this.$refs !== null && this.$refs.item !== null && this.$refs.item !== undefined) {
|
||||
this.width = this.$refs.item.offsetWidth
|
||||
}
|
||||
this.eventBus.$emit('resizeEvent')
|
||||
},
|
||||
containerHeight: function () {
|
||||
if (!this.autoSize) return
|
||||
// console.log("bottom: " + bottom(this.layout))
|
||||
// console.log("rowHeight + margins: " + (this.rowHeight + this.margin[1]) + this.margin[1])
|
||||
const containerHeight = bottom(this.layout) * (this.rowHeight + this.margin[1]) + this.margin[1] + 'px'
|
||||
return containerHeight
|
||||
},
|
||||
dragEvent: function (eventName, id, x, y, h, w) {
|
||||
// console.log(eventName + " id=" + id + ", x=" + x + ", y=" + y);
|
||||
let l = getLayoutItem(this.layout, id)
|
||||
// GetLayoutItem sometimes returns null object
|
||||
if (l === undefined || l === null) {
|
||||
l = { x: 0, y: 0 }
|
||||
}
|
||||
|
||||
if (eventName === 'dragmove' || eventName === 'dragstart') {
|
||||
this.placeholder.i = id
|
||||
this.placeholder.x = l.x
|
||||
this.placeholder.y = l.y
|
||||
this.placeholder.w = w
|
||||
this.placeholder.h = h
|
||||
this.$nextTick(function () {
|
||||
this.isDragging = true
|
||||
})
|
||||
// this.$broadcast("updateWidth", this.width);
|
||||
this.eventBus.$emit('updateWidth', this.width)
|
||||
} else {
|
||||
this.$nextTick(function () {
|
||||
this.isDragging = false
|
||||
})
|
||||
}
|
||||
|
||||
// Move the element to the dragged location.
|
||||
this.layout = moveElement(this.layout, l, x, y, true, this.preventCollision)
|
||||
compact(this.layout, this.verticalCompact)
|
||||
// needed because vue can't detect changes on array element properties
|
||||
this.eventBus.$emit('compact')
|
||||
this.updateHeight()
|
||||
if (eventName === 'dragend') this.$emit('layout-updated', this.layout)
|
||||
},
|
||||
resizeEvent: function (eventName, id, x, y, h, w) {
|
||||
let l = getLayoutItem(this.layout, id)
|
||||
// GetLayoutItem sometimes return null object
|
||||
if (l === undefined || l === null) {
|
||||
l = { h: 0, w: 0 }
|
||||
}
|
||||
|
||||
let hasCollisions
|
||||
if (this.preventCollision) {
|
||||
const collisions = getAllCollisions(this.layout, { ...l, w, h }).filter(
|
||||
layoutItem => layoutItem.i !== l.i
|
||||
)
|
||||
hasCollisions = collisions.length > 0
|
||||
|
||||
// If we're colliding, we need adjust the placeholder.
|
||||
if (hasCollisions) {
|
||||
// adjust w && h to maximum allowed space
|
||||
let leastX = Infinity
|
||||
let leastY = Infinity
|
||||
collisions.forEach(layoutItem => {
|
||||
if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x)
|
||||
if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y)
|
||||
})
|
||||
|
||||
if (Number.isFinite(leastX)) l.w = leastX - l.x
|
||||
if (Number.isFinite(leastY)) l.h = leastY - l.y
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasCollisions) {
|
||||
// Set new width and height.
|
||||
l.w = w
|
||||
l.h = h
|
||||
}
|
||||
|
||||
if (eventName === 'resizestart' || eventName === 'resizemove') {
|
||||
this.placeholder.i = id
|
||||
this.placeholder.x = x
|
||||
this.placeholder.y = y
|
||||
this.placeholder.w = l.w
|
||||
this.placeholder.h = l.h
|
||||
this.$nextTick(function () {
|
||||
this.isDragging = true
|
||||
})
|
||||
// this.$broadcast("updateWidth", this.width);
|
||||
this.eventBus.$emit('updateWidth', this.width)
|
||||
} else {
|
||||
this.$nextTick(function () {
|
||||
this.isDragging = false
|
||||
})
|
||||
}
|
||||
|
||||
if (this.responsive) this.responsiveGridLayout()
|
||||
|
||||
compact(this.layout, this.verticalCompact)
|
||||
this.eventBus.$emit('compact')
|
||||
this.updateHeight()
|
||||
|
||||
if (eventName === 'resizeend') this.$emit('layout-updated', this.layout)
|
||||
},
|
||||
|
||||
// finds or generates new layouts for set breakpoints
|
||||
responsiveGridLayout () {
|
||||
const newBreakpoint = getBreakpointFromWidth(this.breakpoints, this.width)
|
||||
const newCols = getColsFromBreakpoint(newBreakpoint, this.cols)
|
||||
|
||||
// save actual layout in layouts
|
||||
if (this.lastBreakpoint != null && !this.layouts[this.lastBreakpoint]) { this.layouts[this.lastBreakpoint] = cloneLayout(this.layout) }
|
||||
|
||||
// Find or generate a new layout.
|
||||
const layout = findOrGenerateResponsiveLayout(
|
||||
this.originalLayout,
|
||||
this.layouts,
|
||||
this.breakpoints,
|
||||
newBreakpoint,
|
||||
this.lastBreakpoint,
|
||||
newCols,
|
||||
this.verticalCompact
|
||||
)
|
||||
|
||||
// Store the new layout.
|
||||
this.layouts[newBreakpoint] = layout
|
||||
|
||||
if (this.lastBreakpoint !== newBreakpoint) {
|
||||
this.$emit('breakpoint-changed', newBreakpoint, layout)
|
||||
}
|
||||
|
||||
// new prop sync
|
||||
this.$emit('update:layout', layout)
|
||||
|
||||
this.lastBreakpoint = newBreakpoint
|
||||
this.eventBus.$emit('setColNum', getColsFromBreakpoint(newBreakpoint, this.cols))
|
||||
},
|
||||
|
||||
// clear all responsive layouts
|
||||
initResponsiveFeatures () {
|
||||
// clear layouts
|
||||
this.layouts = Object.assign({}, this.responsiveLayouts)
|
||||
},
|
||||
|
||||
// find difference in layouts
|
||||
findDifference (layout, originalLayout) {
|
||||
// Find values that are in result1 but not in result2
|
||||
const uniqueResultOne = layout.filter(function (obj) {
|
||||
return !originalLayout.some(function (obj2) {
|
||||
return obj.i === obj2.i
|
||||
})
|
||||
})
|
||||
|
||||
// Find values that are in result2 but not in result1
|
||||
const uniqueResultTwo = originalLayout.filter(function (obj) {
|
||||
return !layout.some(function (obj2) {
|
||||
return obj.i === obj2.i
|
||||
})
|
||||
})
|
||||
|
||||
// Combine the two arrays of unique entries#
|
||||
return uniqueResultOne.concat(uniqueResultTwo)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<span class="text">
|
||||
{{text}}
|
||||
</span>
|
||||
</template>
|
||||
<style>
|
||||
</style>
|
||||
<script>
|
||||
export default {
|
||||
name: "TestElement",
|
||||
props: {
|
||||
text : {
|
||||
type: String,
|
||||
default: "x",
|
||||
},
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
console.log("### " + this.text + " ready!");
|
||||
},
|
||||
}
|
||||
</script>
|
||||
147
nezha-fronted/src/components/chart/chart/grid/aspectRatio.js
Normal file
147
nezha-fronted/src/components/chart/chart/grid/aspectRatio.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @module modifiers/aspectRatio
|
||||
*
|
||||
* @description
|
||||
* This module forces elements to be resized with a specified dx/dy ratio.
|
||||
*
|
||||
* ```js
|
||||
* interact(target).resizable({
|
||||
* modifiers: [
|
||||
* interact.modifiers.snapSize({
|
||||
* targets: [ interact.snappers.grid({ x: 20, y: 20 }) ],
|
||||
* }),
|
||||
* interact.aspectRatio({ ratio: 'preserve' }),
|
||||
* ],
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
import extend from "../utils/extend.js";
|
||||
import { addEdges } from "../utils/rect.js";
|
||||
import Modification from "./Modification.js";
|
||||
import { makeModifier } from "./base.js";
|
||||
const aspectRatio = {
|
||||
start(arg) {
|
||||
const {
|
||||
state,
|
||||
rect,
|
||||
edges: originalEdges,
|
||||
pageCoords: coords
|
||||
} = arg;
|
||||
let {
|
||||
ratio
|
||||
} = state.options;
|
||||
const {
|
||||
equalDelta,
|
||||
modifiers
|
||||
} = state.options;
|
||||
|
||||
if (ratio === 'preserve') {
|
||||
ratio = rect.width / rect.height;
|
||||
}
|
||||
|
||||
state.startCoords = extend({}, coords);
|
||||
state.startRect = extend({}, rect);
|
||||
state.ratio = ratio;
|
||||
state.equalDelta = equalDelta;
|
||||
const linkedEdges = state.linkedEdges = {
|
||||
top: originalEdges.top || originalEdges.left && !originalEdges.bottom,
|
||||
left: originalEdges.left || originalEdges.top && !originalEdges.right,
|
||||
bottom: originalEdges.bottom || originalEdges.right && !originalEdges.top,
|
||||
right: originalEdges.right || originalEdges.bottom && !originalEdges.left
|
||||
};
|
||||
state.xIsPrimaryAxis = !!(originalEdges.left || originalEdges.right);
|
||||
|
||||
if (state.equalDelta) {
|
||||
state.edgeSign = (linkedEdges.left ? 1 : -1) * (linkedEdges.top ? 1 : -1);
|
||||
} else {
|
||||
const negativeSecondaryEdge = state.xIsPrimaryAxis ? linkedEdges.top : linkedEdges.left;
|
||||
state.edgeSign = negativeSecondaryEdge ? -1 : 1;
|
||||
}
|
||||
|
||||
extend(arg.edges, linkedEdges);
|
||||
if (!modifiers || !modifiers.length) return;
|
||||
const subModification = new Modification(arg.interaction);
|
||||
subModification.copyFrom(arg.interaction.modification);
|
||||
subModification.prepareStates(modifiers);
|
||||
state.subModification = subModification;
|
||||
subModification.startAll({ ...arg
|
||||
});
|
||||
},
|
||||
|
||||
set(arg) {
|
||||
const {
|
||||
state,
|
||||
rect,
|
||||
coords
|
||||
} = arg;
|
||||
const initialCoords = extend({}, coords);
|
||||
const aspectMethod = state.equalDelta ? setEqualDelta : setRatio;
|
||||
aspectMethod(state, state.xIsPrimaryAxis, coords, rect);
|
||||
|
||||
if (!state.subModification) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const correctedRect = extend({}, rect);
|
||||
addEdges(state.linkedEdges, correctedRect, {
|
||||
x: coords.x - initialCoords.x,
|
||||
y: coords.y - initialCoords.y
|
||||
});
|
||||
const result = state.subModification.setAll({ ...arg,
|
||||
rect: correctedRect,
|
||||
edges: state.linkedEdges,
|
||||
pageCoords: coords,
|
||||
prevCoords: coords,
|
||||
prevRect: correctedRect
|
||||
});
|
||||
const {
|
||||
delta
|
||||
} = result;
|
||||
|
||||
if (result.changed) {
|
||||
const xIsCriticalAxis = Math.abs(delta.x) > Math.abs(delta.y); // do aspect modification again with critical edge axis as primary
|
||||
|
||||
aspectMethod(state, xIsCriticalAxis, result.coords, result.rect);
|
||||
extend(coords, result.coords);
|
||||
}
|
||||
|
||||
return result.eventProps;
|
||||
},
|
||||
|
||||
defaults: {
|
||||
ratio: 'preserve',
|
||||
equalDelta: false,
|
||||
modifiers: [],
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
|
||||
function setEqualDelta({
|
||||
startCoords,
|
||||
edgeSign
|
||||
}, xIsPrimaryAxis, coords) {
|
||||
if (xIsPrimaryAxis) {
|
||||
coords.y = startCoords.y + (coords.x - startCoords.x) * edgeSign;
|
||||
} else {
|
||||
coords.x = startCoords.x + (coords.y - startCoords.y) * edgeSign;
|
||||
}
|
||||
}
|
||||
|
||||
function setRatio({
|
||||
startRect,
|
||||
startCoords,
|
||||
ratio,
|
||||
edgeSign
|
||||
}, xIsPrimaryAxis, coords, rect) {
|
||||
if (xIsPrimaryAxis) {
|
||||
const newHeight = rect.width / ratio;
|
||||
coords.y = startCoords.y + (newHeight - startRect.height) * edgeSign;
|
||||
} else {
|
||||
const newWidth = rect.height * ratio;
|
||||
coords.x = startCoords.x + (newWidth - startRect.width) * edgeSign;
|
||||
}
|
||||
}
|
||||
|
||||
export default makeModifier(aspectRatio, 'aspectRatio');
|
||||
export { aspectRatio };
|
||||
//# sourceMappingURL=aspectRatio.js.map
|
||||
@@ -0,0 +1,50 @@
|
||||
// Get {x, y} positions from event.
|
||||
export function getControlPosition (e) {
|
||||
return offsetXYFromParentOf(e)
|
||||
}
|
||||
|
||||
// Get from offsetParent
|
||||
export function offsetXYFromParentOf (evt) {
|
||||
const offsetParent = evt.target.offsetParent || document.body
|
||||
const offsetParentRect = evt.offsetParent === document.body ? { left: 0, top: 0 } : offsetParent.getBoundingClientRect()
|
||||
|
||||
const x = evt.clientX + offsetParent.scrollLeft - offsetParentRect.left
|
||||
const y = evt.clientY + offsetParent.scrollTop - offsetParentRect.top
|
||||
|
||||
/* const x = Math.round(evt.clientX + offsetParent.scrollLeft - offsetParentRect.left);
|
||||
const y = Math.round(evt.clientY + offsetParent.scrollTop - offsetParentRect.top); */
|
||||
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
// Create an data object exposed by <DraggableCore>'s events
|
||||
export function createCoreData (lastX, lastY, x, y) {
|
||||
// State changes are often (but not always!) async. We want the latest value.
|
||||
const isStart = !isNum(lastX)
|
||||
|
||||
if (isStart) {
|
||||
// If this is our first move, use the x and y as last coords.
|
||||
return {
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
lastX: x,
|
||||
lastY: y,
|
||||
x: x,
|
||||
y: y
|
||||
}
|
||||
} else {
|
||||
// Otherwise calculate proper values.
|
||||
return {
|
||||
deltaX: x - lastX,
|
||||
deltaY: y - lastY,
|
||||
lastX: lastX,
|
||||
lastY: lastY,
|
||||
x: x,
|
||||
y: y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isNum (num) {
|
||||
return typeof num === 'number' && !isNaN(num)
|
||||
}
|
||||
34
nezha-fronted/src/components/chart/chart/grid/index.js
Normal file
34
nezha-fronted/src/components/chart/chart/grid/index.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import GridItem from './GridItem.vue'
|
||||
import GridLayout from './GridLayout.vue'
|
||||
// import ResponsiveGridLayout from './ResponsiveGridLayout.vue';
|
||||
|
||||
const VueGridLayout = {
|
||||
// ResponsiveGridLayout,
|
||||
GridLayout,
|
||||
GridItem
|
||||
}
|
||||
|
||||
export function install (Vue) {
|
||||
if (install.installed) return
|
||||
install.installed = true
|
||||
Object.keys(VueGridLayout).forEach(name => {
|
||||
Vue.component(name, VueGridLayout[name])
|
||||
})
|
||||
}
|
||||
|
||||
const plugin = {
|
||||
install
|
||||
}
|
||||
|
||||
let GlobalVue = null
|
||||
if (typeof window !== 'undefined') {
|
||||
GlobalVue = window.Vue
|
||||
} else if (typeof global !== 'undefined') {
|
||||
GlobalVue = global.Vue
|
||||
}
|
||||
if (GlobalVue) {
|
||||
GlobalVue.use(plugin)
|
||||
}
|
||||
|
||||
export default VueGridLayout
|
||||
export { GridLayout, GridItem }
|
||||
107
nezha-fronted/src/components/chart/chart/grid/responsiveUtils.js
Normal file
107
nezha-fronted/src/components/chart/chart/grid/responsiveUtils.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// @flow
|
||||
|
||||
import { cloneLayout, compact, correctBounds, Layout } from './utils'
|
||||
|
||||
// export const ResponsiveLayout = {lg: Layout, md?: Layout, sm?: Layout, xs?: Layout, xxs?: Layout};
|
||||
// type Breakpoint = string;
|
||||
// type Breakpoints = {lg?: number, md?: number, sm?: number, xs?: number, xxs?: number};
|
||||
|
||||
/**
|
||||
* Given a width, find the highest breakpoint that matches is valid for it (width > breakpoint).
|
||||
*
|
||||
* @param {Object} breakpoints Breakpoints object (e.g. {lg: 1200, md: 960, ...})
|
||||
* @param {Number} width Screen width.
|
||||
* @return {String} Highest breakpoint that is less than width.
|
||||
*/
|
||||
export function getBreakpointFromWidth (breakpoints, width) {
|
||||
const sorted = sortBreakpoints(breakpoints)
|
||||
let matching = sorted[0]
|
||||
for (let i = 1, len = sorted.length; i < len; i++) {
|
||||
const breakpointName = sorted[i]
|
||||
if (width > breakpoints[breakpointName]) matching = breakpointName
|
||||
}
|
||||
return matching
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a breakpoint, get the # of cols set for it.
|
||||
* @param {String} breakpoint Breakpoint name.
|
||||
* @param {Object} cols Map of breakpoints to cols.
|
||||
* @return {Number} Number of cols.
|
||||
*/
|
||||
export function getColsFromBreakpoint (breakpoint, cols) {
|
||||
if (!cols[breakpoint]) {
|
||||
throw new Error('ResponsiveGridLayout: `cols` entry for breakpoint ' + breakpoint + ' is missing!')
|
||||
}
|
||||
return cols[breakpoint]
|
||||
}
|
||||
|
||||
/**
|
||||
* Given existing layouts and a new breakpoint, find or generate a new layout.
|
||||
*
|
||||
* This finds the layout above the new one and generates from it, if it exists.
|
||||
*
|
||||
* @param {Array} orgLayout Original layout.
|
||||
* @param {Object} layouts Existing layouts.
|
||||
* @param {Array} breakpoints All breakpoints.
|
||||
* @param {String} breakpoint New breakpoint.
|
||||
* @param {String} breakpoint Last breakpoint (for fallback).
|
||||
* @param {Number} cols Column count at new breakpoint.
|
||||
* @param {Boolean} verticalCompact Whether or not to compact the layout
|
||||
* vertically.
|
||||
* @return {Array} New layout.
|
||||
*/
|
||||
export function findOrGenerateResponsiveLayout (orgLayout, layouts, breakpoints,
|
||||
breakpoint, lastBreakpoint,
|
||||
cols, verticalCompact) {
|
||||
// If it already exists, just return it.
|
||||
if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint])
|
||||
// Find or generate the next layout
|
||||
let layout = orgLayout
|
||||
|
||||
const breakpointsSorted = sortBreakpoints(breakpoints)
|
||||
const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint))
|
||||
for (let i = 0, len = breakpointsAbove.length; i < len; i++) {
|
||||
const b = breakpointsAbove[i]
|
||||
if (layouts[b]) {
|
||||
layout = layouts[b]
|
||||
break
|
||||
}
|
||||
}
|
||||
layout = cloneLayout(layout || []) // clone layout so we don't modify existing items
|
||||
return compact(correctBounds(layout, { cols }), verticalCompact)
|
||||
}
|
||||
|
||||
export function generateResponsiveLayout (layout, breakpoints,
|
||||
breakpoint, lastBreakpoint,
|
||||
cols, verticalCompact) {
|
||||
// If it already exists, just return it.
|
||||
/* if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint]);
|
||||
// Find or generate the next layout
|
||||
let layout = layouts[lastBreakpoint]; */
|
||||
/* const breakpointsSorted = sortBreakpoints(breakpoints);
|
||||
const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint));
|
||||
for (let i = 0, len = breakpointsAbove.length; i < len; i++) {
|
||||
const b = breakpointsAbove[i];
|
||||
if (layouts[b]) {
|
||||
layout = layouts[b];
|
||||
break;
|
||||
}
|
||||
} */
|
||||
layout = cloneLayout(layout || []) // clone layout so we don't modify existing items
|
||||
return compact(correctBounds(layout, { cols: cols }), verticalCompact)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given breakpoints, return an array of breakpoints sorted by width. This is usually
|
||||
* e.g. ['xxs', 'xs', 'sm', ...]
|
||||
*
|
||||
* @param {Object} breakpoints Key/value pair of breakpoint names to widths.
|
||||
* @return {Array} Sorted breakpoints.
|
||||
*/
|
||||
export function sortBreakpoints (breakpoints) {
|
||||
const keys = Object.keys(breakpoints)
|
||||
return keys.sort(function (a, b) {
|
||||
return breakpoints[a] - breakpoints[b]
|
||||
})
|
||||
}
|
||||
583
nezha-fronted/src/components/chart/chart/grid/utils.js
Normal file
583
nezha-fronted/src/components/chart/chart/grid/utils.js
Normal file
@@ -0,0 +1,583 @@
|
||||
// @flow
|
||||
// export const LayoutItemRequired = {w: 4, h: 4, x: 0, y: 0, i: 1};
|
||||
// export const LayoutItem = LayoutItemRequired &
|
||||
// {minW?: number, minH?: number, maxW?: number, maxH?: number,
|
||||
// moved?: boolean, static?: boolean,
|
||||
// isDraggable?: ?boolean, isResizable?: ?boolean};
|
||||
// export const Layout = Array<LayoutItem>
|
||||
// export type Position = {left: number, top: number, width: number, height: number};
|
||||
/*
|
||||
export type DragCallbackData = {
|
||||
node: HTMLElement,
|
||||
x: number, y: number,
|
||||
deltaX: number, deltaY: number,
|
||||
lastX: number, lastY: number
|
||||
};
|
||||
*/
|
||||
// export type DragEvent = {e: Event} & DragCallbackData;
|
||||
export const Size = { width: 4, height: 4 }
|
||||
// export type ResizeEvent = {e: Event, node: HTMLElement, size: Size};
|
||||
|
||||
// const isProduction = process.env.NODE_ENV === 'production';
|
||||
/**
|
||||
* Return the bottom coordinate of the layout.
|
||||
*
|
||||
* @param {Array} layout Layout array.
|
||||
* @return {Number} Bottom coordinate.
|
||||
*/
|
||||
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 cloneLayout (layout) {
|
||||
const newLayout = Array(layout.length)
|
||||
for (let i = 0, len = layout.length; i < len; i++) {
|
||||
newLayout[i] = cloneLayoutItem(layout[i])
|
||||
}
|
||||
return newLayout
|
||||
}
|
||||
|
||||
// Fast path to cloning, since this is monomorphic
|
||||
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))
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two layoutitems, check if they collide.
|
||||
*
|
||||
* @return {Boolean} True if colliding.
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a layout, compact it. This involves going down each y coordinate and removing gaps
|
||||
* between items.
|
||||
*
|
||||
* @param {Array} layout Layout.
|
||||
* @param {Boolean} verticalCompact Whether or not to compact the layout
|
||||
* vertically.
|
||||
* @return {Array} Compacted Layout.
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact an item in the layout.
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a layout, make sure all elements fit within its bounds.
|
||||
*
|
||||
* @param {Array} layout Layout array.
|
||||
* @param {Number} bounds Number of columns.
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a layout item by ID. Used so we can override later on if necessary.
|
||||
*
|
||||
* @param {Array} layout Layout array.
|
||||
* @param {String} id ID
|
||||
* @return {LayoutItem} Item at ID.
|
||||
*/
|
||||
export function getLayoutItem (layout, id) {
|
||||
for (let i = 0, len = layout.length; i < len; i++) {
|
||||
if (layout[i].i === id) return layout[i]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first item this layout collides with.
|
||||
* It doesn't appear to matter which order we approach this from, although
|
||||
* perhaps that is the wrong thing to do.
|
||||
*
|
||||
* @param {Object} layoutItem Layout item.
|
||||
* @return {Object|undefined} A colliding layout item, or undefined.
|
||||
*/
|
||||
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 getAllCollisions (layout, layoutItem) {
|
||||
return layout.filter((l) => collides(l, layoutItem))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all static elements.
|
||||
* @param {Array} layout Array of layout objects.
|
||||
* @return {Array} Array of static layout items..
|
||||
*/
|
||||
export function getStatics (layout) {
|
||||
// return [];
|
||||
return layout.filter((l) => l.static)
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an element. Responsible for doing cascading movements of other elements.
|
||||
*
|
||||
* @param {Array} layout Full layout to modify.
|
||||
* @param {LayoutItem} l element to move.
|
||||
* @param {Number} [x] X position in grid units.
|
||||
* @param {Number} [y] Y position in grid units.
|
||||
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is
|
||||
* being dragged/resized by th euser.
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where the magic needs to happen - given a collision, move an element away from the collision.
|
||||
* We attempt to move it up if there's room, otherwise it goes below.
|
||||
*
|
||||
* @param {Array} layout Full layout to modify.
|
||||
* @param {LayoutItem} collidesWith Layout item we're colliding with.
|
||||
* @param {LayoutItem} itemToMove Layout item we're moving.
|
||||
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is being dragged/resized
|
||||
* by the user.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to convert a number to a percentage string.
|
||||
*
|
||||
* @param {Number} num Any number
|
||||
* @return {String} That number as a percentage.
|
||||
*/
|
||||
export function perc (num) {
|
||||
return num * 100 + '%'
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Just like the setTransform method, but instead it will return a negative value of right.
|
||||
*
|
||||
* @param top
|
||||
* @param right
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns {{transform: string, WebkitTransform: string, MozTransform: string, msTransform: string, OTransform: string, width: string, height: string, position: string}}
|
||||
*/
|
||||
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 setTopLeft (top, left, width, height) {
|
||||
return {
|
||||
top: top + 'px',
|
||||
left: left + 'px',
|
||||
width: width + 'px',
|
||||
height: height + 'px',
|
||||
position: 'absolute'
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Just like the setTopLeft method, but instead, it will return a right property instead of left.
|
||||
*
|
||||
* @param top
|
||||
* @param right
|
||||
* @param width
|
||||
* @param height
|
||||
* @returns {{top: string, right: string, width: string, height: string, position: string}}
|
||||
*/
|
||||
export function setTopRight (top, right, width, height) {
|
||||
return {
|
||||
top: top + 'px',
|
||||
right: right + 'px',
|
||||
width: width + 'px',
|
||||
height: height + 'px',
|
||||
position: 'absolute'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get layout items sorted from top left to right and down.
|
||||
*
|
||||
* @return {Array} Array of layout objects.
|
||||
* @return {Array} Layout, sorted static items first.
|
||||
*/
|
||||
export function sortLayoutItemsByRowCol (layout) {
|
||||
return [].concat(layout).sort(function (a, b) {
|
||||
if (a.y === b.y && a.x === b.x) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a layout using the initialLayout and children as a template.
|
||||
* Missing entries will be added, extraneous ones will be truncated.
|
||||
*
|
||||
* @param {Array} initialLayout Layout passed in through props.
|
||||
* @param {String} breakpoint Current responsive breakpoint.
|
||||
* @param {Boolean} verticalCompact Whether or not to compact the layout vertically.
|
||||
* @return {Array} Working layout.
|
||||
*/
|
||||
/*
|
||||
export function synchronizeLayoutWithChildren(initialLayout: Layout, children: Array<React.Element>|React.Element,
|
||||
cols: number, verticalCompact: boolean): Layout {
|
||||
// ensure 'children' is always an array
|
||||
if (!Array.isArray(children)) {
|
||||
children = [children];
|
||||
}
|
||||
initialLayout = initialLayout || [];
|
||||
|
||||
// Generate one layout item per child.
|
||||
let layout: Layout = [];
|
||||
for (let i = 0, len = children.length; i < len; i++) {
|
||||
let newItem;
|
||||
const child = children[i];
|
||||
|
||||
// Don't overwrite if it already exists.
|
||||
const exists = getLayoutItem(initialLayout, child.key || "1" /!* FIXME satisfies Flow *!/);
|
||||
if (exists) {
|
||||
newItem = exists;
|
||||
} else {
|
||||
const g = child.props._grid;
|
||||
|
||||
// Hey, this item has a _grid property, use it.
|
||||
if (g) {
|
||||
if (!isProduction) {
|
||||
validateLayout([g], 'ReactGridLayout.children');
|
||||
}
|
||||
// Validated; add it to the layout. Bottom 'y' possible is the bottom of the layout.
|
||||
// This allows you to do nice stuff like specify {y: Infinity}
|
||||
if (verticalCompact) {
|
||||
newItem = cloneLayoutItem({...g, y: Math.min(bottom(layout), g.y), i: child.key});
|
||||
} else {
|
||||
newItem = cloneLayoutItem({...g, y: g.y, i: child.key});
|
||||
}
|
||||
}
|
||||
// Nothing provided: ensure this is added to the bottom
|
||||
else {
|
||||
newItem = cloneLayoutItem({w: 1, h: 1, x: 0, y: bottom(layout), i: child.key || "1"});
|
||||
}
|
||||
}
|
||||
layout[i] = newItem;
|
||||
}
|
||||
|
||||
// Correct the layout.
|
||||
layout = correctBounds(layout, {cols: cols});
|
||||
layout = compact(layout, verticalCompact);
|
||||
|
||||
return layout;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validate a layout. Throws errors.
|
||||
*
|
||||
* @param {Array} layout Array of layout items.
|
||||
* @param {String} [contextName] Context name for errors.
|
||||
* @throw {Error} Validation error.
|
||||
*/
|
||||
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!')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flow can't really figure this out, so we just use Object
|
||||
export function autoBindHandlers (el, fns) {
|
||||
fns.forEach((key) => el[key] = el[key].bind(el))
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JS object to CSS string. Similar to React's output of CSS.
|
||||
* @param obj
|
||||
* @returns {string}
|
||||
*/
|
||||
export function createMarkup (obj) {
|
||||
const keys = Object.keys(obj)
|
||||
if (!keys.length) return ''
|
||||
let i; const len = keys.length
|
||||
let result = ''
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
const key = keys[i]
|
||||
const val = obj[key]
|
||||
result += hyphenate(key) + ':' + addPx(key, val) + ';'
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/* The following list is defined in React's core */
|
||||
export var IS_UNITLESS = {
|
||||
animationIterationCount: true,
|
||||
boxFlex: true,
|
||||
boxFlexGroup: true,
|
||||
boxOrdinalGroup: true,
|
||||
columnCount: true,
|
||||
flex: true,
|
||||
flexGrow: true,
|
||||
flexPositive: true,
|
||||
flexShrink: true,
|
||||
flexNegative: true,
|
||||
flexOrder: true,
|
||||
gridRow: true,
|
||||
gridColumn: true,
|
||||
fontWeight: true,
|
||||
lineClamp: true,
|
||||
lineHeight: true,
|
||||
opacity: true,
|
||||
order: true,
|
||||
orphans: true,
|
||||
tabSize: true,
|
||||
widows: true,
|
||||
zIndex: true,
|
||||
zoom: true,
|
||||
|
||||
// SVG-related properties
|
||||
fillOpacity: true,
|
||||
stopOpacity: true,
|
||||
strokeDashoffset: true,
|
||||
strokeOpacity: true,
|
||||
strokeWidth: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Will add px to the end of style values which are Numbers.
|
||||
* @param name
|
||||
* @param value
|
||||
* @returns {*}
|
||||
*/
|
||||
export function addPx (name, value) {
|
||||
if (typeof value === 'number' && !IS_UNITLESS[name]) {
|
||||
return value + 'px'
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hyphenate a camelCase string.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
export var hyphenateRE = /([a-z\d])([A-Z])/g
|
||||
|
||||
export function hyphenate (str) {
|
||||
return str.replace(hyphenateRE, '$1-$2').toLowerCase()
|
||||
}
|
||||
|
||||
export function findItemInArray (array, property, value) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (array[i][property] == value) { return true }
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function findAndRemove (array, property, value) {
|
||||
array.forEach(function (result, index) {
|
||||
if (result[property] === value) {
|
||||
// Remove from array
|
||||
array.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -5,14 +5,13 @@
|
||||
v-if="gridLayoutShow"
|
||||
:class="firstInit ? 'no-animation' : ''"
|
||||
:col-num="12"
|
||||
:maxRows="12"
|
||||
:is-draggable="!panelLock"
|
||||
:is-resizable="!panelLock"
|
||||
:layout.sync="copyDataList"
|
||||
:margin="[10, 10]"
|
||||
:row-height="stepWidth"
|
||||
:use-css-transforms="true"
|
||||
:vertical-compact="true"
|
||||
:use-css-transforms="true"
|
||||
:style="{
|
||||
'margin-top': layoutMargintop,
|
||||
'padding-bottom': isGroup ? '0' : (200 + 'px')
|
||||
@@ -27,6 +26,7 @@
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:min-h="headerH"
|
||||
:max-h="12"
|
||||
:static="item.static"
|
||||
:class="{
|
||||
'group-hide-header':item.type === 'group' && item.param.collapse,
|
||||
@@ -84,7 +84,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueGridLayout from 'vue-grid-layout'
|
||||
import GridLayout from './chart/grid/GridLayout'
|
||||
import GridItem from './chart/grid/GridItem'
|
||||
import { fromRoute } from '@/components/common/js/constants'
|
||||
import { getGroupHeight, getLayoutPosition, isGroup } from './chart/tools'
|
||||
import panelChart from '@/components/chart/panelChart'
|
||||
@@ -105,8 +106,8 @@ export default {
|
||||
dataList: Array // 看板中所有图表信息
|
||||
},
|
||||
components: {
|
||||
GridLayout: VueGridLayout.GridLayout,
|
||||
GridItem: VueGridLayout.GridItem,
|
||||
GridLayout: GridLayout,
|
||||
GridItem: GridItem,
|
||||
panelChart
|
||||
},
|
||||
computed: {
|
||||
@@ -267,10 +268,10 @@ export default {
|
||||
charts: charts
|
||||
}
|
||||
// console.log(this.copyDataList)
|
||||
this.$put('/visual/panel/chart/weights', params).then(() => {
|
||||
const position = getLayoutPosition(this.copyDataList)
|
||||
this.$store.commit('setChartLastPosition', position)
|
||||
})
|
||||
// this.$put('/visual/panel/chart/weights', params).then(() => {
|
||||
// const position = getLayoutPosition(this.copyDataList)
|
||||
// this.$store.commit('setChartLastPosition', position)
|
||||
// })
|
||||
}, 300)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,7 +12,8 @@ export default {
|
||||
active: '#53a3cb',
|
||||
inactive: '#7e7e7e'
|
||||
},
|
||||
chartId: ''
|
||||
chartId: '',
|
||||
isNoData: true
|
||||
}
|
||||
},
|
||||
props: {
|
||||
@@ -36,8 +37,10 @@ export default {
|
||||
handleTimeSeries (chartInfo, seriesTemplate, originalDatas) {
|
||||
const series = []
|
||||
let colorIndex = 0
|
||||
console.log(this.chartData)
|
||||
originalDatas.forEach((originalData, expressionIndex) => {
|
||||
originalData.forEach((data, dataIndex) => {
|
||||
this.isNoData = false
|
||||
const s = lodash.cloneDeep(seriesTemplate)
|
||||
if (s) {
|
||||
s.data = data.values
|
||||
@@ -45,6 +48,7 @@ export default {
|
||||
if (chartInfo.param.stack) { // 堆叠
|
||||
s.stack = 'Total'
|
||||
}
|
||||
console.log(chartInfo.param.enable && chartInfo.param.enable.thresholds && !lodash.isEmpty(chartInfo.param.thresholds) && chartInfo.param.thresholds.length)
|
||||
if (chartInfo.param.enable && chartInfo.param.enable.thresholds && !lodash.isEmpty(chartInfo.param.thresholds) && chartInfo.param.thresholds.length) { // 阈值
|
||||
s.markLine = {
|
||||
symbol: 'circle',
|
||||
@@ -52,7 +56,7 @@ export default {
|
||||
}
|
||||
s.markLine.data = chartInfo.param.thresholds.map(threshold => {
|
||||
return {
|
||||
yAxis: threshold.value,
|
||||
yAxis: threshold.value || 0,
|
||||
lineStyle: {
|
||||
color: threshold.color,
|
||||
width: 2,
|
||||
@@ -66,6 +70,8 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(this.isNoData)
|
||||
this.$emit('chartIsNoData', this.isNoData)
|
||||
return series
|
||||
},
|
||||
// 单个legend
|
||||
|
||||
@@ -508,9 +508,9 @@
|
||||
></el-switch>
|
||||
</div>
|
||||
<transition name="el-zoom-in-top">
|
||||
<el-row v-if="chartConfig.param.enable.valueMapping">
|
||||
<el-row v-if="chartConfig.param.enable.thresholds">
|
||||
<div
|
||||
v-for="(item,index) in chartConfig.param.valueMapping"
|
||||
v-for="(item,index) in chartConfig.param.thresholds"
|
||||
:key="index"
|
||||
>
|
||||
<div class="chart-title chart-title-config">
|
||||
|
||||
Reference in New Issue
Block a user