@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataloop-ai/components",
3
- "version": "0.18.54",
3
+ "version": "0.18.55",
4
4
  "exports": {
5
5
  ".": "./index.ts",
6
6
  "./models": "./models.ts",
@@ -9,14 +9,21 @@
9
9
  </template>
10
10
 
11
11
  <script lang="ts">
12
- import { defineComponent, PropType } from 'vue-demi'
12
+ import { Dictionary } from 'lodash'
13
13
  import {
14
- getGridTemplate,
15
- getElementAbove,
16
- findIndexInMatrix,
17
- swapElemensInMatrix,
18
- isCustomEvent
19
- } from '../DlWidget/utils'
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
- computed: {
46
- gridStyles(): object {
47
- return {
48
- '--row-gap': this.rowGap,
49
- '--column-gap': this.columnGap
50
- }
51
- },
52
- gridClass(): string {
53
- return this.modelValue
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
- watch: {
59
- modelValue: {
60
- handler(val, oldVal) {
61
- this.$nextTick(() => {
62
- if (val) {
63
- if (val.length !== oldVal?.length) {
64
- this.applyIndexesForChildren()
65
- }
66
- this.applyGridElementStyles()
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
- const gridTemplate = getGridTemplate(this.modelValue)
83
- if (gridElements.length > gridTemplate.length) return
102
+ // Update modelValue is required to trigger visualization of the changes
103
+ emit('update:model-value', newLayout)
84
104
 
85
- const order = this.modelValue.flat()
86
- gridElements.forEach((element: Element, index: number) => {
87
- const orderIndex =
88
- order.findIndex((nr: number) => nr === index + 1) + 1
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
- this.changePosition(e)
145
+ changePosition(e)
95
146
  })
96
- htmlElement.addEventListener(
97
- 'position-changed',
98
- this.layoutChanged.bind(this)
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
- const newLayout = swapElemensInMatrix(
125
- this.modelValue,
126
- sourceMatrixIndex,
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
- (element as HTMLElement).dataset.index = `${index}`
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
  }
@@ -1,5 +1,10 @@
1
1
  export interface DlGridLayout {
2
2
  name: string
3
- value: number[][]
3
+ value: (number | string)[][]
4
+ }
5
+
6
+ export enum DlGridMode {
7
+ GRID = 'grid',
8
+ FLEX = 'flex',
9
+ LAYOUT = 'layout'
4
10
  }
5
- export type DlGridSideType = 'left' | 'right'
@@ -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 './utils'
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: Boolean,
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
+ }
@@ -1,40 +1,321 @@
1
1
  <template>
2
- <div class="container">
3
- <dl-grid>
4
- <img
5
- v-for="(a, index) in 10"
6
- :key="index"
7
- class="grid-image"
8
- :src="getRandomImg()"
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
- </dl-grid>
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
- const getRandomImg = () => {
19
- const random = Math.floor(Math.random() * 3) + 1
20
- const link = `https://picsum.photos/${random * 100}/${
21
- random * 100
22
- }?random=${random}`
23
- return link
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
- return { getRandomImg }
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
- .grid-image {
37
- border: 2px solid var(--dl-color-darker);
38
- margin: 5px;
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
- <div class="options">
4
- <div class="select-layout">
5
- <select
6
- class="select-layout__input"
7
- @change="selectLayout"
8
- >
9
- <option
10
- v-for="(layoutItem, layoutIndex) in layouts"
11
- :key="layoutIndex"
12
- :value="layoutIndex"
13
- >
14
- {{ layoutItem.name }}
15
- </option>
16
- </select>
17
- <button
18
- class="select-layout__button"
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
- </template>
49
- <template #menu>
50
- <div class="menu-icons">
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
- </template>
80
- </dl-widget>
81
-
82
- <dl-widget>
83
- <template #header>
84
- <span>Widget 3</span>
85
- </template>
86
- <template #content>
87
- <dl-bar-chart
88
- :data="data"
89
- :items-in-view="6"
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
- </template>
92
- </dl-widget>
93
-
94
- <dl-widget>
95
- <template #header>
96
- <span>Widget 4</span>
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
- </template>
105
- <template #description>
106
- <span>Lorem ipsum dolor sit amet consectetur adipisicing
107
- elit. Libero eligendi dolore, similique possimus
108
- veritatis in vitae quia praesentium fuga quibusdam
109
- autem. Doloremque tenetur repudiandae a cupiditate modi
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
- </template>
124
- </dl-widget>
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, ref } from 'vue-demi'
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: labelsFn(),
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: dataFn()
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: dataFn()
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
- }