fix: 修改 拖拽高度的问题,以及 chart-line nodata的判断

This commit is contained in:
zhangyu
2021-12-21 14:25:13 +08:00
parent d895033e8b
commit 409f911771
17 changed files with 2418 additions and 18 deletions

View File

@@ -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: [

View File

@@ -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",

View File

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

View File

@@ -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 => {

View File

@@ -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>

View 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)
}

View 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>

View 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>

View File

@@ -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>

View 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

View File

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

View 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 }

View 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]
})
}

View 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)
}
})
}

View File

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

View File

@@ -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

View File

@@ -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">