@dataloop-ai/components 0.18.54 → 0.18.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/basic/DlGrid/DlGrid.vue +139 -94
- package/src/components/basic/DlGrid/types.ts +7 -2
- package/src/components/basic/DlGrid/utils.ts +69 -0
- package/src/components/basic/DlWidget/DlWidget.vue +6 -2
- package/src/components/basic/utils.ts +27 -0
- package/src/demos/DlGridDemo.vue +303 -22
- package/src/demos/DlWidgetDemo.vue +86 -271
- package/src/components/basic/DlWidget/utils.ts +0 -119
package/package.json
CHANGED
|
@@ -9,14 +9,21 @@
|
|
|
9
9
|
</template>
|
|
10
10
|
|
|
11
11
|
<script lang="ts">
|
|
12
|
-
import {
|
|
12
|
+
import { Dictionary } from 'lodash'
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
computed,
|
|
15
|
+
defineComponent,
|
|
16
|
+
getCurrentInstance,
|
|
17
|
+
nextTick,
|
|
18
|
+
onMounted,
|
|
19
|
+
PropType,
|
|
20
|
+
ref,
|
|
21
|
+
toRefs,
|
|
22
|
+
watch
|
|
23
|
+
} from 'vue-demi'
|
|
24
|
+
import { getGridTemplate, swapElementsInMatrix } from './utils'
|
|
25
|
+
import { isCustomEvent, getElementAbove } from '../utils'
|
|
26
|
+
import { DlGridMode } from './types'
|
|
20
27
|
|
|
21
28
|
export default defineComponent({
|
|
22
29
|
model: {
|
|
@@ -25,7 +32,7 @@ export default defineComponent({
|
|
|
25
32
|
},
|
|
26
33
|
props: {
|
|
27
34
|
modelValue: {
|
|
28
|
-
type: Array as PropType<number[][]>,
|
|
35
|
+
type: Array as PropType<(string | number)[][]>,
|
|
29
36
|
default: null
|
|
30
37
|
},
|
|
31
38
|
rowGap: {
|
|
@@ -39,111 +46,147 @@ export default defineComponent({
|
|
|
39
46
|
maxElementsPerRow: {
|
|
40
47
|
type: Number,
|
|
41
48
|
default: 3
|
|
49
|
+
},
|
|
50
|
+
mode: {
|
|
51
|
+
type: String as PropType<DlGridMode>,
|
|
52
|
+
default: DlGridMode.LAYOUT
|
|
42
53
|
}
|
|
43
54
|
},
|
|
44
55
|
emits: ['update:model-value', 'layout-changed'],
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
setup(props, { emit }) {
|
|
57
|
+
const vm = getCurrentInstance()
|
|
58
|
+
const grid = ref<HTMLElement | null>(null)
|
|
59
|
+
const { modelValue, mode, rowGap, columnGap, maxElementsPerRow } =
|
|
60
|
+
toRefs(props)
|
|
61
|
+
|
|
62
|
+
const isLayoutMode = computed(() => mode.value == DlGridMode.LAYOUT)
|
|
63
|
+
const isGridMode = computed(() => mode.value == DlGridMode.GRID)
|
|
64
|
+
const isFlexMode = computed(() => mode.value == DlGridMode.FLEX)
|
|
65
|
+
|
|
66
|
+
const gridClass = computed(() =>
|
|
67
|
+
modelValue.value || !isFlexMode.value
|
|
54
68
|
? 'dl-grid-wrapper__grid'
|
|
55
69
|
: 'dl-grid-wrapper__flex'
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
const gridStyles = computed(() => {
|
|
73
|
+
const gridStyles: Dictionary<string | number> = {
|
|
74
|
+
'--row-gap': rowGap.value,
|
|
75
|
+
'--column-gap': columnGap.value
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!isGridMode.value) {
|
|
79
|
+
gridStyles['--element-per-row'] = maxElementsPerRow.value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return gridStyles
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const layoutChanged = () => {
|
|
86
|
+
emit('layout-changed', modelValue.value)
|
|
56
87
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
immediate: true
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
mounted() {
|
|
74
|
-
this.applyIndexesForChildren()
|
|
75
|
-
},
|
|
76
|
-
methods: {
|
|
77
|
-
applyGridElementStyles() {
|
|
78
|
-
if (!this.modelValue) return
|
|
79
|
-
const gridElements = Array.from(
|
|
80
|
-
(this.$refs.grid as HTMLElement).children
|
|
88
|
+
|
|
89
|
+
const changePosition = (e: CustomEvent) => {
|
|
90
|
+
if (!modelValue.value) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
const className = grid.value.children[0].classList[0]
|
|
94
|
+
const sourceEl = getElementAbove(e.detail.source, className)
|
|
95
|
+
const targetEl = getElementAbove(e.detail.target, className)
|
|
96
|
+
|
|
97
|
+
const newLayout: (string | number)[][] = swapElementsInMatrix(
|
|
98
|
+
modelValue.value,
|
|
99
|
+
sourceEl,
|
|
100
|
+
targetEl
|
|
81
101
|
)
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
// Update modelValue is required to trigger visualization of the changes
|
|
103
|
+
emit('update:model-value', newLayout)
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
// Force update is required to trigger the re-render of the grid
|
|
106
|
+
vm?.proxy?.$forceUpdate()
|
|
107
|
+
|
|
108
|
+
if (e.detail.endDragging) {
|
|
109
|
+
layoutChanged()
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const applyGridElementStyles = () => {
|
|
114
|
+
const childrenElements = Array.from(grid.value.children)
|
|
115
|
+
const layoutOrder = modelValue.value?.flat() ?? []
|
|
116
|
+
|
|
117
|
+
// The check is needed to avoid errors and incorrect behavior
|
|
118
|
+
if (
|
|
119
|
+
!modelValue.value ||
|
|
120
|
+
childrenElements.length > layoutOrder.flat().length ||
|
|
121
|
+
isFlexMode.value
|
|
122
|
+
) {
|
|
123
|
+
for (const element of childrenElements) {
|
|
124
|
+
const htmlElement = element as HTMLElement
|
|
125
|
+
htmlElement.style.flexGrow = '1'
|
|
126
|
+
}
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let gridTemplate: string[]
|
|
131
|
+
if (isGridMode.value) {
|
|
132
|
+
gridTemplate = getGridTemplate(modelValue.value)
|
|
133
|
+
}
|
|
134
|
+
for (const element of childrenElements) {
|
|
89
135
|
const htmlElement = element as HTMLElement
|
|
136
|
+
const orderIndex: number = layoutOrder
|
|
137
|
+
.flat()
|
|
138
|
+
.findIndex((w) => w === htmlElement.dataset.id)
|
|
139
|
+
if (isGridMode.value) {
|
|
140
|
+
htmlElement.style.gridColumn = gridTemplate[orderIndex]
|
|
141
|
+
}
|
|
90
142
|
htmlElement.style.order = `${orderIndex}`
|
|
91
|
-
htmlElement.style.gridColumn = gridTemplate[orderIndex - 1]
|
|
92
143
|
htmlElement.addEventListener('position-changing', (e) => {
|
|
93
144
|
if (!isCustomEvent(e)) return
|
|
94
|
-
|
|
145
|
+
changePosition(e)
|
|
95
146
|
})
|
|
96
|
-
htmlElement.addEventListener(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
})
|
|
101
|
-
},
|
|
102
|
-
changePosition(e: CustomEvent) {
|
|
103
|
-
if (!this.modelValue) return
|
|
104
|
-
const side = e.detail.side
|
|
105
|
-
const className = (this.$refs.grid as HTMLElement).children[0]
|
|
106
|
-
.classList[0]
|
|
107
|
-
const sourceIndex =
|
|
108
|
-
parseInt(
|
|
109
|
-
getElementAbove(e.detail.source, className).dataset.index
|
|
110
|
-
) + 1
|
|
111
|
-
const targetIndex =
|
|
112
|
-
parseInt(
|
|
113
|
-
getElementAbove(e.detail.target, className).dataset.index
|
|
114
|
-
) + 1
|
|
115
|
-
const sourceMatrixIndex = findIndexInMatrix(
|
|
116
|
-
this.modelValue,
|
|
117
|
-
sourceIndex
|
|
118
|
-
)
|
|
119
|
-
const targetMatrixIndex = findIndexInMatrix(
|
|
120
|
-
this.modelValue,
|
|
121
|
-
targetIndex
|
|
122
|
-
)
|
|
147
|
+
htmlElement.addEventListener('position-changed', layoutChanged)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
123
150
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
targetMatrixIndex,
|
|
128
|
-
side,
|
|
129
|
-
this.maxElementsPerRow
|
|
130
|
-
)
|
|
131
|
-
// Update modelValue is required to trigger visualization of the changes
|
|
132
|
-
this.$emit('update:model-value', newLayout)
|
|
133
|
-
if (e.detail.endDragging) {
|
|
134
|
-
this.layoutChanged()
|
|
151
|
+
const applyIndexesForChildren = () => {
|
|
152
|
+
if (!modelValue.value) {
|
|
153
|
+
return
|
|
135
154
|
}
|
|
136
|
-
|
|
137
|
-
layoutChanged() {
|
|
138
|
-
this.$emit('layout-changed', this.modelValue)
|
|
139
|
-
},
|
|
140
|
-
applyIndexesForChildren() {
|
|
141
|
-
Array.from((this.$refs.grid as HTMLElement).children).forEach(
|
|
155
|
+
Array.from(grid.value.children).forEach(
|
|
142
156
|
(element: Element, index: number) => {
|
|
143
|
-
|
|
157
|
+
const el = element as HTMLElement
|
|
158
|
+
el.dataset.id = `${modelValue.value.flat()[index]}`
|
|
144
159
|
}
|
|
145
160
|
)
|
|
146
161
|
}
|
|
162
|
+
|
|
163
|
+
watch(
|
|
164
|
+
modelValue,
|
|
165
|
+
(val, old) => {
|
|
166
|
+
nextTick(() => {
|
|
167
|
+
if (val) {
|
|
168
|
+
if (val.flat().length !== old?.flat().length) {
|
|
169
|
+
applyIndexesForChildren()
|
|
170
|
+
}
|
|
171
|
+
applyGridElementStyles()
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
},
|
|
175
|
+
{ immediate: true }
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
onMounted(() => {
|
|
179
|
+
applyGridElementStyles()
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
isLayoutMode,
|
|
184
|
+
isGridMode,
|
|
185
|
+
isFlexMode,
|
|
186
|
+
gridClass,
|
|
187
|
+
gridStyles,
|
|
188
|
+
grid
|
|
189
|
+
}
|
|
147
190
|
}
|
|
148
191
|
})
|
|
149
192
|
</script>
|
|
@@ -154,9 +197,11 @@ export default defineComponent({
|
|
|
154
197
|
display: grid;
|
|
155
198
|
row-gap: var(--row-gap);
|
|
156
199
|
column-gap: var(--column-gap);
|
|
200
|
+
grid-template-columns: repeat(var(--element-per-row), 1fr);
|
|
157
201
|
}
|
|
158
202
|
&__flex {
|
|
159
203
|
display: flex;
|
|
204
|
+
gap: var(--row-gap);
|
|
160
205
|
flex-wrap: wrap;
|
|
161
206
|
}
|
|
162
207
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { cloneDeep } from 'lodash'
|
|
2
|
+
|
|
3
|
+
function leastCommonMultiple(arr: number[]) {
|
|
4
|
+
if (!arr || !arr.length) {
|
|
5
|
+
return
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const gcd = (a: number, b: number): number => (a ? gcd(b % a, a) : b)
|
|
9
|
+
const lcm = (a: number, b: number): number => (a * b) / gcd(a, b)
|
|
10
|
+
return arr.reduce(lcm)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function buildNewLayoutOrder(
|
|
14
|
+
layout: (string | number)[],
|
|
15
|
+
matrix: (string | number)[][]
|
|
16
|
+
) {
|
|
17
|
+
const template: (string | number)[][] = []
|
|
18
|
+
let index = 0
|
|
19
|
+
|
|
20
|
+
for (const row of matrix) {
|
|
21
|
+
const templateRow: (string | number)[] = []
|
|
22
|
+
for (const cell of row) {
|
|
23
|
+
templateRow.push(layout[index])
|
|
24
|
+
index++
|
|
25
|
+
}
|
|
26
|
+
template.push(templateRow)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return template
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getGridTemplate(layout: (string | number)[][]) {
|
|
33
|
+
if (!layout) {
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const flatLayout = layout.map((el) => el.length)
|
|
38
|
+
const template = []
|
|
39
|
+
const lcm = leastCommonMultiple(flatLayout)
|
|
40
|
+
for (let i = 0; i < flatLayout.length; i++) {
|
|
41
|
+
const columns = flatLayout[i]
|
|
42
|
+
let columnTrack = 1
|
|
43
|
+
for (let j = 0; j < columns; j++) {
|
|
44
|
+
let gridSpan = lcm / columns
|
|
45
|
+
template.push(`${columnTrack} / ${gridSpan + columnTrack}`)
|
|
46
|
+
columnTrack += gridSpan
|
|
47
|
+
gridSpan += gridSpan
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return template
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function swapElementsInMatrix(
|
|
54
|
+
oldLayout: (string | number)[][],
|
|
55
|
+
sourceEl: HTMLElement,
|
|
56
|
+
targetEl: HTMLElement
|
|
57
|
+
) {
|
|
58
|
+
if (!sourceEl || !targetEl) {
|
|
59
|
+
return oldLayout
|
|
60
|
+
}
|
|
61
|
+
const newLayout = cloneDeep(oldLayout).flat(1)
|
|
62
|
+
|
|
63
|
+
const sourceIndex = newLayout.indexOf(sourceEl.dataset.id)
|
|
64
|
+
const targetIndex = newLayout.indexOf(targetEl.dataset.id)
|
|
65
|
+
newLayout.splice(sourceIndex, 1)
|
|
66
|
+
newLayout.splice(targetIndex, 0, sourceEl.dataset.id)
|
|
67
|
+
|
|
68
|
+
return buildNewLayoutOrder(newLayout, oldLayout)
|
|
69
|
+
}
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
import { v4 } from 'uuid'
|
|
67
67
|
import { computed, defineComponent, ref, toRef, PropType } from 'vue-demi'
|
|
68
68
|
import { DlIcon } from '../../essential'
|
|
69
|
-
import { getElementAbove, addMouseEnter, removeMouseEnter } from '
|
|
69
|
+
import { getElementAbove, addMouseEnter, removeMouseEnter } from '../utils'
|
|
70
70
|
import { DlEmptyStateProps } from '../DlEmptyState/types'
|
|
71
71
|
import DlEmptyState from '../DlEmptyState/DlEmptyState.vue'
|
|
72
72
|
|
|
@@ -77,7 +77,10 @@ export default defineComponent({
|
|
|
77
77
|
DlEmptyState
|
|
78
78
|
},
|
|
79
79
|
props: {
|
|
80
|
-
isEmpty:
|
|
80
|
+
isEmpty: {
|
|
81
|
+
type: Boolean,
|
|
82
|
+
default: false
|
|
83
|
+
},
|
|
81
84
|
emptyStateProps: {
|
|
82
85
|
type: Object as PropType<DlEmptyStateProps>,
|
|
83
86
|
default: null
|
|
@@ -126,6 +129,7 @@ export default defineComponent({
|
|
|
126
129
|
if (draggedWidget.value && clone.value) {
|
|
127
130
|
clone.value.appendChild(draggedWidget.value.cloneNode(true))
|
|
128
131
|
clone.value.style.visibility = 'visible'
|
|
132
|
+
clone.value.style.position = 'fixed'
|
|
129
133
|
clone.value.style.width = `${draggedWidget.value.offsetWidth}px`
|
|
130
134
|
clone.value.style.height = `${draggedWidget.value.offsetHeight}px`
|
|
131
135
|
clone.value.style.backgroundColor = `var(--dl-color-bg)`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function getElementAbove(el: HTMLElement, className: string) {
|
|
2
|
+
//@ts-ignore
|
|
3
|
+
for (; el && el !== document; el = el.parentNode) {
|
|
4
|
+
if (el.classList.contains(className)) {
|
|
5
|
+
return el
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function addMouseEnter(className: string, method: EventListenerObject) {
|
|
11
|
+
Array.from(document.getElementsByClassName(className)).forEach((widget) => {
|
|
12
|
+
widget.addEventListener('mouseenter', method)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function removeMouseEnter(
|
|
17
|
+
className: string,
|
|
18
|
+
method: EventListenerObject
|
|
19
|
+
) {
|
|
20
|
+
Array.from(document.getElementsByClassName(className)).forEach((widget) => {
|
|
21
|
+
widget.removeEventListener('mouseenter', method)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isCustomEvent(event: Event): event is CustomEvent {
|
|
26
|
+
return 'detail' in event
|
|
27
|
+
}
|
package/src/demos/DlGridDemo.vue
CHANGED
|
@@ -1,40 +1,321 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
<div class="widgets-demo-wrapper">
|
|
3
|
+
<div>
|
|
4
|
+
<h3>Layout mode</h3>
|
|
5
|
+
<dl-button
|
|
6
|
+
class="select-layout__button"
|
|
7
|
+
style="width: 200px; margin-bottom: 20px"
|
|
8
|
+
@click="addWidget(layout)"
|
|
9
9
|
>
|
|
10
|
-
|
|
10
|
+
Add widget
|
|
11
|
+
</dl-button>
|
|
12
|
+
<dl-grid
|
|
13
|
+
:key="cmWidgets.length"
|
|
14
|
+
v-model="cmLayout"
|
|
15
|
+
:max-elements-per-row="cmMaxWidgetsPerRow"
|
|
16
|
+
:mode="layout"
|
|
17
|
+
>
|
|
18
|
+
<dl-widget
|
|
19
|
+
v-for="widget in cmWidgets"
|
|
20
|
+
:key="widget.id"
|
|
21
|
+
>
|
|
22
|
+
<template #header>
|
|
23
|
+
{{ widget.header }}
|
|
24
|
+
</template>
|
|
25
|
+
<template #content>
|
|
26
|
+
<dl-bar-chart
|
|
27
|
+
:data="data"
|
|
28
|
+
:items-in-view="6"
|
|
29
|
+
/>
|
|
30
|
+
</template>
|
|
31
|
+
<template #menu>
|
|
32
|
+
<div class="menu-icons">
|
|
33
|
+
<dl-icon
|
|
34
|
+
size="m"
|
|
35
|
+
icon="icon-dl-edit"
|
|
36
|
+
/>
|
|
37
|
+
<dl-icon
|
|
38
|
+
size="m"
|
|
39
|
+
icon="icon-dl-delete"
|
|
40
|
+
@click="deleteWidget(layout, widget.id)"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
<template #description>
|
|
45
|
+
{{ widget.description }}
|
|
46
|
+
</template>
|
|
47
|
+
</dl-widget>
|
|
48
|
+
</dl-grid>
|
|
49
|
+
</div>
|
|
50
|
+
<div>
|
|
51
|
+
<h3>Grid mode</h3>
|
|
52
|
+
<dl-button
|
|
53
|
+
class="select-layout__button"
|
|
54
|
+
style="width: 200px; margin-bottom: 20px"
|
|
55
|
+
@click="addWidget(grid)"
|
|
56
|
+
>
|
|
57
|
+
Add widget
|
|
58
|
+
</dl-button>
|
|
59
|
+
<dl-grid
|
|
60
|
+
:key="dmWidgets.length"
|
|
61
|
+
v-model="dmLayout"
|
|
62
|
+
:max-elements-per-row="dmMaxWidgetsPerRow"
|
|
63
|
+
:mode="grid"
|
|
64
|
+
>
|
|
65
|
+
<dl-widget
|
|
66
|
+
v-for="widget in dmWidgets"
|
|
67
|
+
:key="widget.id"
|
|
68
|
+
>
|
|
69
|
+
<template #header>
|
|
70
|
+
{{ widget.header }}
|
|
71
|
+
</template>
|
|
72
|
+
<template #content>
|
|
73
|
+
<dl-bar-chart
|
|
74
|
+
:data="data"
|
|
75
|
+
:items-in-view="6"
|
|
76
|
+
/>
|
|
77
|
+
</template>
|
|
78
|
+
<template #menu>
|
|
79
|
+
<div class="menu-icons">
|
|
80
|
+
<dl-icon
|
|
81
|
+
size="m"
|
|
82
|
+
icon="icon-dl-edit"
|
|
83
|
+
/>
|
|
84
|
+
<dl-icon
|
|
85
|
+
size="m"
|
|
86
|
+
icon="icon-dl-delete"
|
|
87
|
+
@click="deleteWidget(grid, widget.id)"
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
</template>
|
|
91
|
+
<template #description>
|
|
92
|
+
{{ widget.description }}
|
|
93
|
+
</template>
|
|
94
|
+
</dl-widget>
|
|
95
|
+
</dl-grid>
|
|
96
|
+
</div>
|
|
97
|
+
<div>
|
|
98
|
+
<h3>Flex mode</h3>
|
|
99
|
+
<dl-button
|
|
100
|
+
class="select-layout__button"
|
|
101
|
+
style="width: 200px; margin-bottom: 20px"
|
|
102
|
+
@click="addWidget(flex)"
|
|
103
|
+
>
|
|
104
|
+
Add widget
|
|
105
|
+
</dl-button>
|
|
106
|
+
<dl-grid
|
|
107
|
+
:key="fmWidgets.length"
|
|
108
|
+
:mode="flex"
|
|
109
|
+
>
|
|
110
|
+
<dl-widget
|
|
111
|
+
v-for="widget in fmWidgets"
|
|
112
|
+
:key="widget.id"
|
|
113
|
+
:draggable="false"
|
|
114
|
+
style="max-width: 15vw"
|
|
115
|
+
>
|
|
116
|
+
<template #header>
|
|
117
|
+
{{ widget.header }}
|
|
118
|
+
</template>
|
|
119
|
+
<template #content>
|
|
120
|
+
<dl-bar-chart
|
|
121
|
+
:data="data"
|
|
122
|
+
:items-in-view="6"
|
|
123
|
+
/>
|
|
124
|
+
</template>
|
|
125
|
+
<template #menu>
|
|
126
|
+
<div class="menu-icons">
|
|
127
|
+
<dl-icon
|
|
128
|
+
size="m"
|
|
129
|
+
icon="icon-dl-edit"
|
|
130
|
+
/>
|
|
131
|
+
<dl-icon
|
|
132
|
+
size="m"
|
|
133
|
+
icon="icon-dl-delete"
|
|
134
|
+
@click="deleteWidget(flex, widget.id)"
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
</template>
|
|
138
|
+
<template #description>
|
|
139
|
+
{{ widget.description }}
|
|
140
|
+
</template>
|
|
141
|
+
</dl-widget>
|
|
142
|
+
</dl-grid>
|
|
143
|
+
</div>
|
|
11
144
|
</div>
|
|
12
145
|
</template>
|
|
13
146
|
|
|
14
147
|
<script lang="ts">
|
|
15
|
-
import { defineComponent } from 'vue-demi'
|
|
16
|
-
import { DlGrid } from '../components
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
148
|
+
import { defineComponent, Ref, ref } from 'vue-demi'
|
|
149
|
+
import { DlWidget, DlGrid, DlBarChart, DlIcon, DlButton } from '../components'
|
|
150
|
+
import { DlGridMode } from '../types'
|
|
151
|
+
|
|
152
|
+
const data = {
|
|
153
|
+
labels: Array.from({ length: 20 }, (_, i) => `${i}`),
|
|
154
|
+
datasets: [
|
|
155
|
+
{
|
|
156
|
+
label: 'Data One',
|
|
157
|
+
backgroundColor: '--dl-color-secondary',
|
|
158
|
+
borderRadius: 4,
|
|
159
|
+
data: Array.from({ length: 20 }, (_, i) => i + 1)
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
label: 'Data Two',
|
|
163
|
+
backgroundColor: '--dl-color-warning',
|
|
164
|
+
borderRadius: 4,
|
|
165
|
+
data: Array.from({ length: 20 }, (_, i) => i + 1)
|
|
166
|
+
}
|
|
167
|
+
]
|
|
24
168
|
}
|
|
169
|
+
|
|
25
170
|
export default defineComponent({
|
|
26
171
|
components: {
|
|
27
|
-
DlGrid
|
|
172
|
+
DlGrid,
|
|
173
|
+
DlWidget,
|
|
174
|
+
DlBarChart,
|
|
175
|
+
DlIcon,
|
|
176
|
+
DlButton
|
|
28
177
|
},
|
|
29
178
|
setup() {
|
|
30
|
-
|
|
179
|
+
const cmLayout: Ref<string[][]> = ref([
|
|
180
|
+
['a', 'b', 'c'],
|
|
181
|
+
['d', 'e']
|
|
182
|
+
])
|
|
183
|
+
const dmLayout: Ref<string[][]> = ref([
|
|
184
|
+
['a', 'b', 'c'],
|
|
185
|
+
['d', 'e']
|
|
186
|
+
])
|
|
187
|
+
const fmLayout: Ref<string[][]> = ref([
|
|
188
|
+
['a', 'b', 'c'],
|
|
189
|
+
['d', 'e', 'f']
|
|
190
|
+
])
|
|
191
|
+
|
|
192
|
+
const cmMaxWidgetsPerRow = ref(3)
|
|
193
|
+
const dmMaxWidgetsPerRow = ref(3)
|
|
194
|
+
const fmMaxWidgetsPerRow = ref(3)
|
|
195
|
+
|
|
196
|
+
const cmWidgets = ref([])
|
|
197
|
+
const dmWidgets = ref([])
|
|
198
|
+
const fmWidgets = ref([])
|
|
199
|
+
|
|
200
|
+
const createRandomWidgets = (
|
|
201
|
+
layout: Ref<string[][]>,
|
|
202
|
+
widgets: Ref<any[]>,
|
|
203
|
+
numOfWidgets: number
|
|
204
|
+
) => {
|
|
205
|
+
for (let i = 0; i < numOfWidgets; i++) {
|
|
206
|
+
widgets.value.push({
|
|
207
|
+
id: layout.value.flat()[i],
|
|
208
|
+
header: `Widget ${i}`,
|
|
209
|
+
description: `I'm description`
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
createRandomWidgets(cmLayout, cmWidgets, cmLayout.value.flat().length)
|
|
214
|
+
createRandomWidgets(dmLayout, dmWidgets, dmLayout.value.flat().length)
|
|
215
|
+
createRandomWidgets(fmLayout, fmWidgets, fmLayout.value.flat().length)
|
|
216
|
+
|
|
217
|
+
const deleteWidget = (mode: string, id: string) => {
|
|
218
|
+
const { widgets, layout, widgetsPerRow } = modeScope(mode)
|
|
219
|
+
widgets.value = widgets.value.filter((widget) => widget.id !== id)
|
|
220
|
+
layout.value = buildNewLayout(widgets.value, widgetsPerRow)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const addWidget = (mode: string) => {
|
|
224
|
+
const { widgets, layout, widgetsPerRow } = modeScope(mode)
|
|
225
|
+
widgets.value.push({
|
|
226
|
+
id: (widgets.value.length + 1).toString(),
|
|
227
|
+
header: `Widget ${widgets.value.length}`,
|
|
228
|
+
description: `I'm description`
|
|
229
|
+
})
|
|
230
|
+
layout.value = buildNewLayout(widgets.value, widgetsPerRow)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const modeScope = (mode: string) => {
|
|
234
|
+
let widgets
|
|
235
|
+
let layout
|
|
236
|
+
let widgetsPerRow
|
|
237
|
+
switch (mode) {
|
|
238
|
+
case DlGridMode.LAYOUT:
|
|
239
|
+
widgets = cmWidgets
|
|
240
|
+
layout = cmLayout
|
|
241
|
+
widgetsPerRow = cmMaxWidgetsPerRow.value
|
|
242
|
+
break
|
|
243
|
+
case DlGridMode.GRID:
|
|
244
|
+
widgets = dmWidgets
|
|
245
|
+
layout = dmLayout
|
|
246
|
+
widgetsPerRow = dmMaxWidgetsPerRow.value
|
|
247
|
+
break
|
|
248
|
+
case DlGridMode.FLEX:
|
|
249
|
+
widgets = fmWidgets
|
|
250
|
+
layout = fmLayout
|
|
251
|
+
widgetsPerRow = fmMaxWidgetsPerRow.value
|
|
252
|
+
break
|
|
253
|
+
}
|
|
254
|
+
return { widgets, layout, widgetsPerRow }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const buildNewLayout = (widgets: any[], widgetsPerRow: number) => {
|
|
258
|
+
const template: string[][] = []
|
|
259
|
+
let index = 0
|
|
260
|
+
|
|
261
|
+
while (index < widgets.length) {
|
|
262
|
+
const row = widgets.slice(index, index + widgetsPerRow)
|
|
263
|
+
template.push(row.map((w) => w.id))
|
|
264
|
+
index += widgetsPerRow
|
|
265
|
+
}
|
|
266
|
+
return template
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
data,
|
|
271
|
+
deleteWidget,
|
|
272
|
+
addWidget,
|
|
273
|
+
cmLayout,
|
|
274
|
+
cmWidgets,
|
|
275
|
+
cmMaxWidgetsPerRow,
|
|
276
|
+
fmLayout,
|
|
277
|
+
fmWidgets,
|
|
278
|
+
fmMaxWidgetsPerRow,
|
|
279
|
+
dmLayout,
|
|
280
|
+
dmWidgets,
|
|
281
|
+
dmMaxWidgetsPerRow,
|
|
282
|
+
layout: DlGridMode.LAYOUT,
|
|
283
|
+
flex: DlGridMode.FLEX,
|
|
284
|
+
grid: DlGridMode.GRID
|
|
285
|
+
}
|
|
31
286
|
}
|
|
32
287
|
})
|
|
33
288
|
</script>
|
|
34
289
|
|
|
35
290
|
<style lang="scss" scoped>
|
|
36
|
-
.
|
|
37
|
-
|
|
38
|
-
|
|
291
|
+
.widgets-demo-wrapper {
|
|
292
|
+
display: flex;
|
|
293
|
+
flex-direction: column;
|
|
294
|
+
align-items: center;
|
|
295
|
+
justify-content: center;
|
|
296
|
+
gap: 20px;
|
|
297
|
+
|
|
298
|
+
& > * {
|
|
299
|
+
display: flex;
|
|
300
|
+
flex-direction: column;
|
|
301
|
+
gap: 10px;
|
|
302
|
+
border: 1px solid var(--dl-color-separator);
|
|
303
|
+
padding: 10px;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
.widgets-per-row {
|
|
307
|
+
&__input {
|
|
308
|
+
padding: 5px;
|
|
309
|
+
border-radius: 5px;
|
|
310
|
+
width: 50px;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
.menu-icons {
|
|
314
|
+
display: flex;
|
|
315
|
+
align-items: center;
|
|
316
|
+
& > * {
|
|
317
|
+
cursor: pointer;
|
|
318
|
+
margin: 0px 5px;
|
|
319
|
+
}
|
|
39
320
|
}
|
|
40
321
|
</style>
|
|
@@ -1,316 +1,131 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@mousedown="saveLayout"
|
|
20
|
-
>
|
|
21
|
-
Save
|
|
22
|
-
</button>
|
|
23
|
-
<span class="select-layout__info">{{ hasBeenSaved }}</span>
|
|
24
|
-
</div>
|
|
25
|
-
<div class="widgets-per-row">
|
|
26
|
-
<span class="widgets-per-row__label"> Widgets per row: </span>
|
|
27
|
-
<input
|
|
28
|
-
v-model="widgetsPerRow"
|
|
29
|
-
class="widgets-per-row__input"
|
|
30
|
-
type="number"
|
|
31
|
-
>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
<dl-grid
|
|
35
|
-
v-model="layout"
|
|
36
|
-
:max-elements-per-row="widgetsPerRow"
|
|
37
|
-
>
|
|
38
|
-
<dl-widget>
|
|
39
|
-
<template #header>
|
|
40
|
-
<span>Widget 1</span>
|
|
41
|
-
<span style="font-size: 12px; color: var(--dl-color-medium)">Subtitle</span>
|
|
42
|
-
</template>
|
|
43
|
-
<template #content>
|
|
44
|
-
<dl-bar-chart
|
|
45
|
-
:data="data"
|
|
46
|
-
:items-in-view="8"
|
|
3
|
+
<dl-widget style="width: 50vw">
|
|
4
|
+
<template #header>
|
|
5
|
+
<span>Widget with menu icons</span>
|
|
6
|
+
<span style="font-size: 12px; color: var(--dl-color-medium)">Subtitle</span>
|
|
7
|
+
</template>
|
|
8
|
+
<template #content>
|
|
9
|
+
<dl-bar-chart
|
|
10
|
+
:data="data"
|
|
11
|
+
:items-in-view="8"
|
|
12
|
+
/>
|
|
13
|
+
</template>
|
|
14
|
+
<template #menu>
|
|
15
|
+
<div class="menu-icons">
|
|
16
|
+
<dl-icon
|
|
17
|
+
size="m"
|
|
18
|
+
icon="icon-dl-settings"
|
|
47
19
|
/>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<dl-icon
|
|
52
|
-
size="m"
|
|
53
|
-
icon="icon-dl-settings"
|
|
54
|
-
/>
|
|
55
|
-
<dl-icon
|
|
56
|
-
size="m"
|
|
57
|
-
icon="icon-dl-download"
|
|
58
|
-
/>
|
|
59
|
-
</div>
|
|
60
|
-
</template>
|
|
61
|
-
<template #description>
|
|
62
|
-
<span>Lorem ipsum dolor sit amet consectetur adipisicing
|
|
63
|
-
elit. Libero eligendi dolore, similique possimus
|
|
64
|
-
veritatis in vitae quia praesentium fuga quibusdam
|
|
65
|
-
autem. Doloremque tenetur repudiandae a cupiditate modi
|
|
66
|
-
dicta eveniet veritatis?</span>
|
|
67
|
-
</template>
|
|
68
|
-
</dl-widget>
|
|
69
|
-
|
|
70
|
-
<dl-widget>
|
|
71
|
-
<template #header>
|
|
72
|
-
<span>Widget 2</span>
|
|
73
|
-
</template>
|
|
74
|
-
<template #content>
|
|
75
|
-
<dl-bar-chart
|
|
76
|
-
:data="data"
|
|
77
|
-
:items-in-view="6"
|
|
20
|
+
<dl-icon
|
|
21
|
+
size="m"
|
|
22
|
+
icon="icon-dl-download"
|
|
78
23
|
/>
|
|
79
|
-
</
|
|
80
|
-
</
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
</
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
</dl-widget>
|
|
27
|
+
|
|
28
|
+
<dl-widget style="width: 50vw">
|
|
29
|
+
<template #header>
|
|
30
|
+
<span>Widget with description</span>
|
|
31
|
+
</template>
|
|
32
|
+
<template #content>
|
|
33
|
+
<dl-bar-chart
|
|
34
|
+
:data="data"
|
|
35
|
+
:items-in-view="6"
|
|
36
|
+
/>
|
|
37
|
+
</template>
|
|
38
|
+
<template #description>
|
|
39
|
+
<span>Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
|
40
|
+
Libero eligendi dolore, similique possimus veritatis in
|
|
41
|
+
vitae quia praesentium fuga quibusdam autem. Doloremque
|
|
42
|
+
tenetur repudiandae a cupiditate modi dicta eveniet
|
|
43
|
+
veritatis?</span>
|
|
44
|
+
</template>
|
|
45
|
+
</dl-widget>
|
|
46
|
+
|
|
47
|
+
<dl-widget
|
|
48
|
+
is-empty
|
|
49
|
+
:empty-state-props="{
|
|
50
|
+
style: 'min-height: 350px;',
|
|
51
|
+
title: 'Lorem ipsum',
|
|
52
|
+
subtitle:
|
|
53
|
+
'Lorem ipsum dolor sit amet consectetur. Senectus condimentum dolor sit',
|
|
54
|
+
info: 'To learn more about this analytics, read our documentation.',
|
|
55
|
+
responsive: false
|
|
56
|
+
}"
|
|
57
|
+
style="width: 50vw"
|
|
58
|
+
>
|
|
59
|
+
<template #header>
|
|
60
|
+
<span>Empty state widget</span>
|
|
61
|
+
</template>
|
|
62
|
+
<template #links="">
|
|
63
|
+
<div style="display: flex; gap: 5px; padding: 0 20px">
|
|
64
|
+
<dl-button
|
|
65
|
+
padding="0px"
|
|
66
|
+
icon="icon-dl-sdk-documentation"
|
|
67
|
+
flat
|
|
68
|
+
uppercase
|
|
69
|
+
label="SDK"
|
|
90
70
|
/>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<span style="font-size: 12px; color: gray">Subtitle</span>
|
|
98
|
-
</template>
|
|
99
|
-
<template #content>
|
|
100
|
-
<dl-bar-chart
|
|
101
|
-
:data="data"
|
|
102
|
-
:items-in-view="8"
|
|
71
|
+
<div class="break" />
|
|
72
|
+
<dl-button
|
|
73
|
+
padding="0px"
|
|
74
|
+
icon="icon-dl-file"
|
|
75
|
+
flat
|
|
76
|
+
label="Documentation"
|
|
103
77
|
/>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
dicta eveniet veritatis?</span>
|
|
111
|
-
</template>
|
|
112
|
-
</dl-widget>
|
|
113
|
-
|
|
114
|
-
<dl-widget>
|
|
115
|
-
<template #header>
|
|
116
|
-
<span>Widget 5</span>
|
|
117
|
-
</template>
|
|
118
|
-
<template #content>
|
|
119
|
-
<dl-bar-chart
|
|
120
|
-
:data="data"
|
|
121
|
-
:items-in-view="6"
|
|
78
|
+
<div class="break" />
|
|
79
|
+
<dl-button
|
|
80
|
+
padding="0px"
|
|
81
|
+
icon="icon-dl-youtube"
|
|
82
|
+
flat
|
|
83
|
+
label="Video"
|
|
122
84
|
/>
|
|
123
|
-
</
|
|
124
|
-
</
|
|
125
|
-
|
|
126
|
-
<dl-widget
|
|
127
|
-
is-empty
|
|
128
|
-
:empty-state-props="{
|
|
129
|
-
responsive: true,
|
|
130
|
-
style: 'min-height: 350px;',
|
|
131
|
-
bgSize: '130px',
|
|
132
|
-
bgImage: `url(https://raw.githubusercontent.com/dataloop-ai/icons/main/assets/usage.svg)`,
|
|
133
|
-
title: 'Lorem ipsum',
|
|
134
|
-
subtitle:
|
|
135
|
-
'Lorem ipsum dolor sit amet consectetur. Senectus condimentum dolor sit',
|
|
136
|
-
info: 'To learn more about this analytics, read our documentation.'
|
|
137
|
-
}"
|
|
138
|
-
>
|
|
139
|
-
<template #header>
|
|
140
|
-
<span>Widget 5</span>
|
|
141
|
-
</template>
|
|
142
|
-
<template #links="">
|
|
143
|
-
<div style="display: flex; gap: 5px; padding: 0 20px">
|
|
144
|
-
<dl-button
|
|
145
|
-
padding="0px"
|
|
146
|
-
icon="icon-dl-sdk-documentation"
|
|
147
|
-
flat
|
|
148
|
-
uppercase
|
|
149
|
-
label="SDK"
|
|
150
|
-
/>
|
|
151
|
-
<div class="break" />
|
|
152
|
-
<dl-button
|
|
153
|
-
padding="0px"
|
|
154
|
-
icon="icon-dl-file"
|
|
155
|
-
flat
|
|
156
|
-
label="Documentation"
|
|
157
|
-
/>
|
|
158
|
-
<div class="break" />
|
|
159
|
-
<dl-button
|
|
160
|
-
padding="0px"
|
|
161
|
-
icon="icon-dl-youtube"
|
|
162
|
-
flat
|
|
163
|
-
label="Video"
|
|
164
|
-
/>
|
|
165
|
-
</div>
|
|
166
|
-
</template>
|
|
167
|
-
</dl-widget>
|
|
168
|
-
</dl-grid>
|
|
85
|
+
</div>
|
|
86
|
+
</template>
|
|
87
|
+
</dl-widget>
|
|
169
88
|
</div>
|
|
170
89
|
</template>
|
|
171
90
|
|
|
172
91
|
<script lang="ts">
|
|
173
|
-
import { defineComponent
|
|
174
|
-
import {
|
|
175
|
-
DlWidget,
|
|
176
|
-
DlGrid,
|
|
177
|
-
DlBarChart,
|
|
178
|
-
DlGridLayout,
|
|
179
|
-
DlIcon,
|
|
180
|
-
DlButton
|
|
181
|
-
} from '../components'
|
|
182
|
-
|
|
183
|
-
const labelsFn = () => {
|
|
184
|
-
const a = []
|
|
185
|
-
for (let i = 0; i < 20; i++) {
|
|
186
|
-
a.push(`${i}`)
|
|
187
|
-
}
|
|
188
|
-
return a
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const dataFn = () => {
|
|
192
|
-
const a = []
|
|
193
|
-
for (let i = 1; i <= 20; i++) {
|
|
194
|
-
a.push(i)
|
|
195
|
-
}
|
|
196
|
-
return a
|
|
197
|
-
}
|
|
92
|
+
import { defineComponent } from 'vue-demi'
|
|
93
|
+
import { DlWidget, DlBarChart, DlIcon, DlButton } from '../components'
|
|
198
94
|
|
|
199
95
|
const data = {
|
|
200
|
-
labels:
|
|
96
|
+
labels: Array.from({ length: 20 }, (_, i) => `${i}`),
|
|
201
97
|
datasets: [
|
|
202
98
|
{
|
|
203
99
|
label: 'Data One',
|
|
204
100
|
backgroundColor: '--dl-color-secondary',
|
|
205
101
|
borderRadius: 4,
|
|
206
|
-
data:
|
|
102
|
+
data: Array.from({ length: 20 }, (_, i) => i + 1)
|
|
207
103
|
},
|
|
208
104
|
{
|
|
209
105
|
label: 'Data Two',
|
|
210
106
|
backgroundColor: '--dl-color-warning',
|
|
211
107
|
borderRadius: 4,
|
|
212
|
-
data:
|
|
108
|
+
data: Array.from({ length: 20 }, (_, i) => i + 1)
|
|
213
109
|
}
|
|
214
110
|
]
|
|
215
111
|
}
|
|
216
112
|
|
|
217
|
-
const matrix: number[][] = []
|
|
218
|
-
const labels: string[] = []
|
|
219
|
-
const size: number = 10
|
|
220
|
-
|
|
221
|
-
for (let i = 0; i < size; i++) {
|
|
222
|
-
const row = []
|
|
223
|
-
for (let j = 0; j < size; j++) {
|
|
224
|
-
row.push(Math.floor(Math.random() * 10))
|
|
225
|
-
}
|
|
226
|
-
matrix.push(row)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const items = ['Van', 'Truck', 'Motorcycle', 'Car', 'Bus']
|
|
230
|
-
|
|
231
|
-
for (let i = 0; i < size; i++) {
|
|
232
|
-
labels.push(items[Math.floor(Math.random() * items.length)])
|
|
233
|
-
}
|
|
234
|
-
|
|
235
113
|
export default defineComponent({
|
|
236
114
|
components: {
|
|
237
|
-
DlGrid,
|
|
238
115
|
DlWidget,
|
|
239
116
|
DlBarChart,
|
|
240
117
|
DlIcon,
|
|
241
118
|
DlButton
|
|
242
119
|
},
|
|
243
120
|
setup() {
|
|
244
|
-
const layout = ref([
|
|
245
|
-
[1, 5, 2],
|
|
246
|
-
[3, 4, 6]
|
|
247
|
-
])
|
|
248
|
-
|
|
249
|
-
const layouts = ref<DlGridLayout[]>([
|
|
250
|
-
{
|
|
251
|
-
name: 'Layout 1',
|
|
252
|
-
value: layout.value
|
|
253
|
-
}
|
|
254
|
-
])
|
|
255
|
-
|
|
256
|
-
const widgetsPerRow = ref(3)
|
|
257
|
-
const hasBeenSaved = ref('')
|
|
258
|
-
|
|
259
|
-
const saveLayout = () => {
|
|
260
|
-
const newLayout = {
|
|
261
|
-
name: `Layout ${layouts.value.length + 1}`,
|
|
262
|
-
value: layout.value
|
|
263
|
-
}
|
|
264
|
-
layouts.value.push(newLayout)
|
|
265
|
-
hasBeenSaved.value = `${newLayout.name} has been saved.`
|
|
266
|
-
setTimeout(() => {
|
|
267
|
-
hasBeenSaved.value = ''
|
|
268
|
-
}, 2000)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const selectLayout = (e: InputEvent) => {
|
|
272
|
-
const index = parseInt((e.target as HTMLInputElement).value)
|
|
273
|
-
layout.value = layouts.value[index].value
|
|
274
|
-
}
|
|
275
|
-
|
|
276
121
|
return {
|
|
277
|
-
data
|
|
278
|
-
layout,
|
|
279
|
-
layouts,
|
|
280
|
-
widgetsPerRow,
|
|
281
|
-
hasBeenSaved,
|
|
282
|
-
saveLayout,
|
|
283
|
-
selectLayout
|
|
122
|
+
data
|
|
284
123
|
}
|
|
285
124
|
}
|
|
286
125
|
})
|
|
287
126
|
</script>
|
|
288
127
|
|
|
289
128
|
<style lang="scss" scoped>
|
|
290
|
-
.options {
|
|
291
|
-
width: 100%;
|
|
292
|
-
display: flex;
|
|
293
|
-
justify-content: space-between;
|
|
294
|
-
}
|
|
295
|
-
.widgets-per-row {
|
|
296
|
-
&__input {
|
|
297
|
-
padding: 5px;
|
|
298
|
-
border-radius: 5px;
|
|
299
|
-
width: 50px;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
.select-layout {
|
|
303
|
-
&__input,
|
|
304
|
-
&__button {
|
|
305
|
-
padding: 5px;
|
|
306
|
-
border-radius: 5px;
|
|
307
|
-
margin: 0px 2px;
|
|
308
|
-
}
|
|
309
|
-
&__info {
|
|
310
|
-
font-size: 0.8em;
|
|
311
|
-
margin-left: 10px;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
129
|
.menu-icons {
|
|
315
130
|
display: flex;
|
|
316
131
|
align-items: center;
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { cloneDeep } from 'lodash'
|
|
2
|
-
import { DlGridSideType } from '../DlGrid/types'
|
|
3
|
-
|
|
4
|
-
export function leastCommonMultiple(arr: number[]) {
|
|
5
|
-
if (!arr) return
|
|
6
|
-
const gcd = (a: number, b: number): number => (a ? gcd(b % a, a) : b)
|
|
7
|
-
const lcm = (a: number, b: number): number => (a * b) / gcd(a, b)
|
|
8
|
-
return arr.reduce(lcm)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function getGridTemplate(layout: number[][]) {
|
|
12
|
-
if (!layout) return
|
|
13
|
-
const flatLayout = layout.map((el) => el.length)
|
|
14
|
-
const template = []
|
|
15
|
-
const lcm = leastCommonMultiple(flatLayout)
|
|
16
|
-
for (let i = 0; i < flatLayout.length; i++) {
|
|
17
|
-
const columns = flatLayout[i]
|
|
18
|
-
let columnTrack = 1
|
|
19
|
-
for (let j = 0; j < columns; j++) {
|
|
20
|
-
let gridSpan = lcm / columns
|
|
21
|
-
template.push(`${columnTrack} / ${gridSpan + columnTrack}`)
|
|
22
|
-
columnTrack += gridSpan
|
|
23
|
-
gridSpan += gridSpan
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return template
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function getElementAbove(el: HTMLElement, className: string) {
|
|
30
|
-
//@ts-ignore
|
|
31
|
-
for (; el && el !== document; el = el.parentNode) {
|
|
32
|
-
if (el.classList.contains(className)) {
|
|
33
|
-
return el
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function addMouseEnter(className: string, method: EventListenerObject) {
|
|
39
|
-
Array.from(document.getElementsByClassName(className)).forEach((widget) => {
|
|
40
|
-
widget.addEventListener('mouseenter', method)
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function removeMouseEnter(
|
|
45
|
-
className: string,
|
|
46
|
-
method: EventListenerObject
|
|
47
|
-
) {
|
|
48
|
-
Array.from(document.getElementsByClassName(className)).forEach((widget) => {
|
|
49
|
-
widget.removeEventListener('mouseenter', method)
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function findIndexInMatrix(matrix: number[][], nr: number) {
|
|
54
|
-
for (let i = 0; i < matrix.length; i++) {
|
|
55
|
-
for (let j = 0; j < matrix[i].length; j++) {
|
|
56
|
-
if (matrix[i][j] === nr) return { row: i, column: j }
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function swapElemensInMatrix(
|
|
62
|
-
layout: number[][],
|
|
63
|
-
sourceIndex: any,
|
|
64
|
-
targetIndex: any,
|
|
65
|
-
side: DlGridSideType,
|
|
66
|
-
maxElements: number
|
|
67
|
-
) {
|
|
68
|
-
const newLayout = cloneDeep(layout)
|
|
69
|
-
|
|
70
|
-
const removedElement = newLayout[sourceIndex.row].splice(
|
|
71
|
-
sourceIndex.column,
|
|
72
|
-
1
|
|
73
|
-
)
|
|
74
|
-
newLayout[targetIndex.row].splice(
|
|
75
|
-
side === 'right' ? targetIndex.column + 1 : targetIndex.column,
|
|
76
|
-
0,
|
|
77
|
-
removedElement[0]
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
return isTooLarge(newLayout, maxElements)
|
|
81
|
-
? moveElementsToNextIndex(newLayout, maxElements)
|
|
82
|
-
: newLayout
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function moveElementsToNextIndex(
|
|
86
|
-
template: number[][],
|
|
87
|
-
maxElements: number
|
|
88
|
-
): number[][] {
|
|
89
|
-
const clonedTemplate: number[][] = cloneDeep(template)
|
|
90
|
-
let overflow: number[] = []
|
|
91
|
-
|
|
92
|
-
for (let i = 0; i < clonedTemplate.length; i++) {
|
|
93
|
-
let currentRow = clonedTemplate[i]
|
|
94
|
-
if (overflow.length > 0) {
|
|
95
|
-
currentRow = overflow.concat(currentRow)
|
|
96
|
-
overflow = []
|
|
97
|
-
}
|
|
98
|
-
if (currentRow.length > maxElements) {
|
|
99
|
-
overflow = currentRow.slice(maxElements)
|
|
100
|
-
currentRow = currentRow.slice(0, maxElements)
|
|
101
|
-
}
|
|
102
|
-
clonedTemplate[i] = currentRow
|
|
103
|
-
if (i + 1 < clonedTemplate.length && overflow.length > 0) {
|
|
104
|
-
clonedTemplate[i + 1] = overflow.concat(clonedTemplate[i + 1])
|
|
105
|
-
overflow = []
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return clonedTemplate
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function isTooLarge(layout: number[][], max: number) {
|
|
112
|
-
const lengths = layout.map((row) => row.length)
|
|
113
|
-
const highest = Math.max(...lengths)
|
|
114
|
-
return highest > max
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function isCustomEvent(event: Event): event is CustomEvent {
|
|
118
|
-
return 'detail' in event
|
|
119
|
-
}
|