@dataloop-ai/components 0.13.19 → 0.14.3

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.
Files changed (88) hide show
  1. package/.nvmrc +1 -0
  2. package/README.md +3 -1
  3. package/package.json +19 -17
  4. package/src/assets/globals.scss +38 -3
  5. package/src/assets/grid.scss +9 -0
  6. package/src/components/DlAccordion/DlAccordion.vue +4 -4
  7. package/src/components/DlAlert.vue +6 -6
  8. package/src/components/DlChart/BrushThumb.vue +83 -0
  9. package/src/components/DlChart/DlBarChart.vue +545 -0
  10. package/src/components/DlChart/DlBrush.vue +475 -0
  11. package/src/components/DlChart/DlChartLabels.vue +279 -0
  12. package/src/components/DlChart/DlChartLegend.vue +141 -0
  13. package/src/components/DlChart/DlColumnChart.vue +555 -0
  14. package/src/components/DlChart/DlLineChart.vue +644 -0
  15. package/src/components/DlChart/DlScrollBar.vue +147 -0
  16. package/src/components/DlChart/Doughnut/DlDoughnutChart.vue +448 -0
  17. package/src/components/DlChart/Doughnut/DlDoughnutChartLegend.vue +281 -0
  18. package/src/components/DlChart/Doughnut/DlDoughnutChartWidget.vue +64 -0
  19. package/src/components/DlChart/Doughnut/types/TDoughnutChartAnimation.ts +4 -0
  20. package/src/components/DlChart/Doughnut/types/TDoughnutChartData.ts +16 -0
  21. package/src/components/DlChart/Doughnut/types/TDoughnutChartOptions.ts +11 -0
  22. package/src/components/DlChart/Doughnut/types/TDoughnutChartProps.ts +8 -0
  23. package/src/components/DlChart/Doughnut/types/TDoughnutWithOriginalColor.ts +13 -0
  24. package/src/components/DlChart/chart.ts +122 -0
  25. package/src/components/DlChart/index.ts +36 -0
  26. package/src/components/DlChart/props.ts +348 -0
  27. package/src/components/DlChart/typedCharts.ts +88 -0
  28. package/src/components/DlChart/types.ts +63 -0
  29. package/src/components/DlChart/utils.ts +153 -0
  30. package/src/components/DlChip/DlChip.vue +40 -13
  31. package/src/components/DlCounters.vue +31 -7
  32. package/src/components/DlDatePicker/DlCalendar.vue +22 -10
  33. package/src/components/DlDatePicker/DlDatePicker.vue +8 -5
  34. package/src/components/DlDateTimeRange/types.ts +2 -2
  35. package/src/components/DlDialogBox/DlDialogBox.vue +0 -8
  36. package/src/components/DlDialogBox/DlDialogBoxHeader.vue +6 -22
  37. package/src/components/DlDropdownButton.vue +16 -4
  38. package/src/components/DlPagination/RowsSelector.vue +15 -1
  39. package/src/components/DlRange/DlRange.vue +3 -2
  40. package/src/components/DlSearch.vue +1 -1
  41. package/src/components/DlSelect/DlSelect.vue +1 -19
  42. package/src/components/DlSlider/sliderStyles.scss +4 -0
  43. package/src/components/DlSlider/useSlider.ts +39 -12
  44. package/src/components/DlSpinner.vue +259 -0
  45. package/src/components/DlTable/DlTable.vue +4 -1
  46. package/src/components/DlTable/hooks/tablePagination.ts +8 -2
  47. package/src/components/DlTextArea.vue +9 -0
  48. package/src/components/DlTextInput.vue +6 -42
  49. package/src/components/DlThemeProvider.vue +22 -13
  50. package/src/components/DlToastMessage/api/useToast.ts +23 -0
  51. package/src/components/DlToastMessage/components/ToastComponent.vue +279 -0
  52. package/src/components/DlToastMessage/index.ts +5 -0
  53. package/src/components/DlToastMessage/types.ts +4 -0
  54. package/src/components/DlToastMessage/utils/config.ts +17 -0
  55. package/src/components/DlToastMessage/utils/render.ts +54 -0
  56. package/src/components/DlTypography.vue +1 -1
  57. package/src/components/DlWidget/DlGrid.vue +33 -0
  58. package/src/components/DlWidget/DlGridRow.vue +32 -0
  59. package/src/components/DlWidget/DlWidget.vue +202 -11
  60. package/src/components/DlWidget/index.ts +2 -2
  61. package/src/components/DlWidget/utils.ts +26 -19
  62. package/src/components/index.ts +8 -3
  63. package/src/demo/BarChartDemo.vue +77 -0
  64. package/src/demo/ColumnChartDemo.vue +230 -0
  65. package/src/demo/DlButtonDemo.vue +1 -1
  66. package/src/demo/DlChartDoughnutDemo.vue +229 -0
  67. package/src/demo/DlChipDemo.vue +42 -18
  68. package/src/demo/DlDialogBoxDemo.vue +4 -4
  69. package/src/demo/DlLineChartDemo.vue +375 -0
  70. package/src/demo/DlSpinnerDemo.vue +20 -0
  71. package/src/demo/DlTableDemo.vue +13 -94
  72. package/src/demo/DlToastMessageDemo.vue +143 -0
  73. package/src/demo/DlWidgetDemo.vue +105 -143
  74. package/src/demo/index.ts +20 -2
  75. package/src/hooks/use-theme.ts +19 -0
  76. package/src/utils/abbreviate-to-string.ts +14 -0
  77. package/src/utils/colors.ts +47 -0
  78. package/src/utils/getRootStyles.ts +1 -1
  79. package/src/utils/index.ts +1 -0
  80. package/src/utils/swapNodes.ts +30 -0
  81. package/src/utils/update-key.ts +90 -0
  82. package/tsconfig.json +0 -1
  83. package/vite.config.ts +17 -0
  84. package/Dockerfile +0 -12
  85. package/jest.config.js +0 -16
  86. package/jest.setup.js +0 -59
  87. package/src/components/DlWidget/DlWidgetGrid.vue +0 -360
  88. package/storybook.js +0 -8
@@ -0,0 +1,279 @@
1
+ <template>
2
+ <transition
3
+ :enter-active-class="transition.enter"
4
+ :leave-active-class="transition.leave"
5
+ >
6
+ <div
7
+ v-show="isActive"
8
+ ref="root"
9
+ class="v-toast__item"
10
+ :class="[
11
+ `v-toast__item--${type}`,
12
+ `v-toast__item--${position}`,
13
+ classItem
14
+ ]"
15
+ >
16
+ <dl-alert
17
+ :type="type"
18
+ :closable="closable"
19
+ @update:model-value="(val) => closeToast(val)"
20
+ >
21
+ <span
22
+ class="v-toast__text"
23
+ data-test="message-text"
24
+ />
25
+ </dl-alert>
26
+ </div>
27
+ </transition>
28
+ </template>
29
+
30
+ <script lang="ts">
31
+ import {
32
+ defineComponent,
33
+ ref,
34
+ onBeforeMount,
35
+ computed,
36
+ onMounted
37
+ } from 'vue-demi'
38
+ import DlAlert from '../../DlAlert.vue'
39
+ import { Positions, Types } from '../utils/config'
40
+ import { removeElement } from '../utils/render'
41
+ import { Animation } from '../types'
42
+
43
+ export default defineComponent({
44
+ name: 'ToastComponent',
45
+ components: { DlAlert },
46
+ props: {
47
+ message: {
48
+ type: String,
49
+ required: true
50
+ },
51
+ type: {
52
+ type: String,
53
+ default: 'success',
54
+ validator(value: string): boolean {
55
+ return Object.values(Types as unknown).includes(value)
56
+ }
57
+ },
58
+ classItem: {
59
+ type: String,
60
+ default: ''
61
+ },
62
+ duration: {
63
+ type: Number,
64
+ default: 10
65
+ },
66
+ position: {
67
+ type: String,
68
+ default: Positions.bottom,
69
+ validator(value: string): boolean {
70
+ return Object.values(Positions as unknown).includes(value)
71
+ }
72
+ },
73
+ closable: {
74
+ type: Boolean,
75
+ default: true
76
+ }
77
+ },
78
+ setup(props) {
79
+ const { position, duration, message } = props
80
+ const root = ref(null)
81
+ let parentTop: HTMLElement = null
82
+ let parentBottom: HTMLElement = null
83
+ const toastParentPosition = ref(null)
84
+ const isActive = ref(false)
85
+ function closeToastMessage(): void {
86
+ isActive.value = false
87
+ setTimeout(() => removeElement(root.value), 200)
88
+ }
89
+ onBeforeMount(() => {
90
+ setupContainer()
91
+ })
92
+ function setupContainer(): void {
93
+ parentTop = document.querySelector('.v-toast.v-toast--top')
94
+ parentBottom = document.querySelector('.v-toast.v-toast--bottom')
95
+ if (parentTop && parentBottom) return
96
+ if (!parentTop) {
97
+ parentTop = document.createElement('div')
98
+ parentTop.className = 'v-toast v-toast--top'
99
+ }
100
+ if (!parentBottom) {
101
+ parentBottom = document.createElement('div')
102
+ parentBottom.className = 'v-toast v-toast--bottom'
103
+ }
104
+ const container = document.body
105
+ container.appendChild(parentTop)
106
+ container.appendChild(parentBottom)
107
+ }
108
+
109
+ const correctParent = computed(() => {
110
+ switch (position) {
111
+ case Positions.top:
112
+ case Positions.top_right:
113
+ case Positions.top_left:
114
+ toastParentPosition.value = 'top'
115
+ return parentTop
116
+ case Positions.bottom:
117
+ case Positions.bottom_right:
118
+ case Positions.bottom_left:
119
+ toastParentPosition.value = 'bottom'
120
+ return parentBottom
121
+ }
122
+ })
123
+
124
+ const transition = computed((): Animation => {
125
+ switch (position) {
126
+ case Positions.top:
127
+ case Positions.top_right:
128
+ case Positions.top_left:
129
+ return {
130
+ enter: 'v-toast--fade-in-down',
131
+ leave: 'v-toast--fade-out'
132
+ }
133
+ case Positions.bottom:
134
+ case Positions.bottom_right:
135
+ case Positions.bottom_left:
136
+ return {
137
+ enter: 'v-toast--fade-in-up',
138
+ leave: 'v-toast--fade-out'
139
+ }
140
+ }
141
+ })
142
+
143
+ onMounted(() => {
144
+ showNotice()
145
+ })
146
+
147
+ function showNotice(): void {
148
+ const parent = correctParent.value
149
+ const container = root.value.closest('.v-toast--pending')
150
+ root.value.querySelector('.v-toast__text').innerHTML = message
151
+ parent.insertAdjacentElement('afterbegin', root.value)
152
+ container?.remove()
153
+ isActive.value = true
154
+ if (duration) {
155
+ setTimeout(() => {
156
+ closeToastMessage()
157
+ }, duration * 1000)
158
+ }
159
+ }
160
+ function closeToast(val: boolean) {
161
+ if (!val) removeElement(root.value)
162
+ }
163
+
164
+ return {
165
+ root,
166
+ transition,
167
+ isActive,
168
+ closeToast,
169
+ correctParent
170
+ }
171
+ }
172
+ })
173
+ </script>
174
+
175
+ <style lang="scss">
176
+ .v-toast {
177
+ position: fixed;
178
+ display: flex;
179
+ top: 0;
180
+ bottom: 0;
181
+ left: 0;
182
+ right: 0;
183
+ overflow: hidden;
184
+ z-index: 1052;
185
+ pointer-events: none;
186
+ &__text {
187
+ min-width: 300px;
188
+ max-width: 800px;
189
+ }
190
+ &__item {
191
+ display: inline-flex;
192
+ align-items: center;
193
+ pointer-events: auto;
194
+ margin-bottom: 5px;
195
+ margin-top: 5px;
196
+ cursor: pointer;
197
+ animation-duration: 150ms;
198
+ &.v-toast__item--top,
199
+ &.v-toast__item--bottom {
200
+ align-self: center;
201
+ }
202
+
203
+ &.v-toast__item--top-right,
204
+ &.v-toast__item--bottom-right {
205
+ align-self: flex-end;
206
+ }
207
+
208
+ &.v-toast__item--top-left,
209
+ &.v-toast__item--bottom-left {
210
+ align-self: flex-start;
211
+ }
212
+ }
213
+
214
+ &.v-toast--top {
215
+ flex-direction: column;
216
+ }
217
+
218
+ &.v-toast--bottom {
219
+ flex-direction: column-reverse;
220
+ }
221
+ }
222
+
223
+ @keyframes fadeOut {
224
+ from {
225
+ opacity: 1;
226
+ }
227
+ to {
228
+ opacity: 0;
229
+ }
230
+ }
231
+
232
+ .v-toast--fade-out {
233
+ animation-name: fadeOut;
234
+ }
235
+
236
+ @keyframes fadeInDown {
237
+ from {
238
+ opacity: 0;
239
+ transform: translate3d(0, -100%, 0);
240
+ }
241
+ to {
242
+ opacity: 1;
243
+ transform: none;
244
+ }
245
+ }
246
+
247
+ .v-toast--fade-in-down {
248
+ animation-name: fadeInDown;
249
+ }
250
+
251
+ @keyframes fadeInUp {
252
+ from {
253
+ opacity: 0;
254
+ transform: translate3d(0, 100%, 0);
255
+ }
256
+ to {
257
+ opacity: 1;
258
+ transform: none;
259
+ }
260
+ }
261
+
262
+ .v-toast--fade-in-up {
263
+ animation-name: fadeInUp;
264
+ }
265
+
266
+ /**
267
+ * Vue Transitions
268
+ */
269
+
270
+ .fade-enter-active,
271
+ .fade-leave-active {
272
+ transition: opacity 150ms ease-out;
273
+ }
274
+
275
+ .fade-enter,
276
+ .fade-leave-to {
277
+ opacity: 0;
278
+ }
279
+ </style>
@@ -0,0 +1,5 @@
1
+ import { useToast } from './api/useToast'
2
+
3
+ const DlToast = useToast()
4
+
5
+ export { DlToast }
@@ -0,0 +1,4 @@
1
+ export interface Animation {
2
+ enter: string
3
+ leave: string
4
+ }
@@ -0,0 +1,17 @@
1
+ const Positions = Object.freeze({
2
+ top_right: 'top-right',
3
+ top: 'top',
4
+ top_left: 'top-left',
5
+ bottom_right: 'bottom-right',
6
+ bottom: 'bottom',
7
+ bottom_left: 'bottom-left'
8
+ })
9
+
10
+ const Types = Object.freeze({
11
+ success: 'success',
12
+ warning: 'warning',
13
+ error: 'error',
14
+ info: 'info'
15
+ })
16
+
17
+ export { Positions, Types }
@@ -0,0 +1,54 @@
1
+ import * as VueDemi from 'vue-demi'
2
+ import ToastComponent from '../components/ToastComponent.vue'
3
+
4
+ let createComponent: Function
5
+
6
+ if (VueDemi.isVue3) {
7
+ createComponent = function (
8
+ component: object,
9
+ props: object,
10
+ parentContainer: Element,
11
+ slots = {}
12
+ ) {
13
+ const vNode = VueDemi.h(component, props, slots)
14
+ const container = document.createElement('div')
15
+ container.classList.add('v-toast--pending')
16
+ parentContainer.appendChild(container)
17
+ VueDemi.render(vNode, container)
18
+
19
+ return vNode.component
20
+ }
21
+ } else {
22
+ createComponent = function (
23
+ component: object,
24
+ props: object,
25
+ parentContainer: Element,
26
+ slots = {}
27
+ ) {
28
+ const container = document.createElement('div')
29
+ container.classList.add('v-toast--pending')
30
+ parentContainer.appendChild(container)
31
+ renderVue2Component(ToastComponent, props, '.v-toast--pending')
32
+ }
33
+ }
34
+
35
+ export function removeElement(el: Element) {
36
+ if (typeof el.remove !== 'undefined') {
37
+ el.remove()
38
+ } else {
39
+ el.parentNode?.removeChild(el)
40
+ }
41
+ }
42
+
43
+ export { createComponent }
44
+
45
+ function renderVue2Component(
46
+ component: Object,
47
+ props: Object,
48
+ container: string
49
+ ) {
50
+ new VueDemi.Vue2({
51
+ render: (h: (arg0: Object, arg1: { props: Object }) => any) =>
52
+ h(component, { props })
53
+ }).$mount(container)
54
+ }
@@ -15,7 +15,7 @@ import { getColor } from '../utils'
15
15
 
16
16
  type Variant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'
17
17
 
18
- const sizes = ['h1', 'h2', 'h3', 'h4', 'body']
18
+ const sizes = ['h1', 'h2', 'h3', 'h4', 'body', 'small']
19
19
 
20
20
  export default defineComponent({
21
21
  name: 'DlTypography',
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <div
3
+ :style="rowGap"
4
+ class="dl-grid"
5
+ >
6
+ <slot />
7
+ </div>
8
+ </template>
9
+
10
+ <script lang="ts">
11
+ import { defineComponent } from 'vue-demi'
12
+
13
+ export default defineComponent({
14
+ props: {
15
+ gap: {
16
+ type: String,
17
+ default: '30px'
18
+ }
19
+ },
20
+ computed: {
21
+ rowGap(): Record<string, string> {
22
+ return { '--row-gap': this.gap }
23
+ }
24
+ }
25
+ })
26
+ </script>
27
+
28
+ <style lang="scss" scoped>
29
+ .dl-grid {
30
+ display: flex;
31
+ flex-direction: column;
32
+ }
33
+ </style>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <div
3
+ :style="columnGap"
4
+ class="dl-grid-row"
5
+ >
6
+ <slot />
7
+ </div>
8
+ </template>
9
+
10
+ <script lang="ts">
11
+ import { defineComponent } from 'vue-demi'
12
+
13
+ export default defineComponent({
14
+ props: {
15
+ gap: {
16
+ type: String,
17
+ default: '30px'
18
+ }
19
+ },
20
+ computed: {
21
+ columnGap(): Record<string, string> {
22
+ return { '--column-gap': this.gap }
23
+ }
24
+ }
25
+ })
26
+ </script>
27
+
28
+ <style scoped>
29
+ .dl-grid-row {
30
+ display: flex;
31
+ }
32
+ </style>
@@ -1,29 +1,186 @@
1
1
  <template>
2
2
  <div
3
- :id="uuid"
4
- class="dl-widget"
3
+ ref="wrapper"
4
+ class="widget-wrapper"
5
5
  >
6
- <div class="dl-widget__header">
7
- <slot name="header" />
8
- </div>
6
+ <div
7
+ :id="uuid"
8
+ ref="widget"
9
+ :class="widgetStyles"
10
+ >
11
+ <div
12
+ class="dl-widget__header"
13
+ @mouseenter="visibleDragIcon = true"
14
+ @mouseleave="visibleDragIcon = false"
15
+ >
16
+ <div class="dl-widget__header--titles">
17
+ <slot name="header" />
18
+ </div>
19
+ <dl-icon
20
+ :style="`visibility: ${
21
+ visibleDragIcon ? 'visible' : 'hidden'
22
+ }`"
23
+ class="dl-widget__header--drag-icon"
24
+ icon="icon-dl-drag"
25
+ color="dl-color-medium"
26
+ size="15px"
27
+ @mousedown="startDragging"
28
+ />
29
+ <slot name="menu" />
30
+ </div>
9
31
 
10
- <div class="dl-widget__content">
11
- <slot name="content" />
12
- </div>
32
+ <div class="dl-widget__content">
33
+ <slot name="content" />
34
+ </div>
13
35
 
14
- <div class="dl-widget__description">
15
- <slot name="description" />
36
+ <div class="dl-widget__description">
37
+ <slot name="description" />
38
+ </div>
16
39
  </div>
40
+ <div
41
+ ref="clone"
42
+ class="drag-clone"
43
+ />
17
44
  </div>
18
45
  </template>
19
46
  <script lang="ts">
20
47
  import { v4 } from 'uuid'
21
48
  import { defineComponent } from 'vue-demi'
49
+ import DlIcon from '../DlIcon.vue'
50
+ import {
51
+ getElementAbove,
52
+ addMouseEnter,
53
+ removeMouseEnter,
54
+ setFlexBasis,
55
+ insertAfter
56
+ } from './utils'
57
+ import { swapNodes } from '../../utils/swapNodes'
58
+
22
59
  export default defineComponent({
23
60
  name: 'DlWidget',
61
+ components: {
62
+ DlIcon
63
+ },
24
64
  data() {
25
65
  return {
26
- uuid: `dl-widget-${v4()}`
66
+ uuid: `dl-widget-${v4()}`,
67
+ draggedWidget: null,
68
+ hoveredWidget: null,
69
+ isLeftSide: false,
70
+ flexBasis: null,
71
+ isDragging: false,
72
+ timer: null,
73
+ visibleDragIcon: false
74
+ }
75
+ },
76
+ computed: {
77
+ widgetStyles() {
78
+ return `${this.isDragging ? 'dl-widget__drag' : 'dl-widget'}`
79
+ }
80
+ },
81
+ mounted() {
82
+ setFlexBasis()
83
+ },
84
+ methods: {
85
+ startDragging(e: MouseEvent) {
86
+ this.isDragging = true
87
+ this.draggedWidget = getElementAbove(
88
+ e.target as HTMLElement,
89
+ 'dl-widget'
90
+ )
91
+ if (this.draggedWidget) {
92
+ (this.$refs.clone as HTMLElement).appendChild(
93
+ this.draggedWidget.cloneNode(true)
94
+ )
95
+ }
96
+
97
+ const sourceCanvas = this.draggedWidget?.querySelector('canvas')
98
+ if (sourceCanvas) {
99
+ const targetCanvasCtx = (this.$refs.clone as HTMLElement)
100
+ .querySelector('canvas')
101
+ .getContext('2d')
102
+
103
+ targetCanvasCtx.drawImage(sourceCanvas, 0, 0)
104
+ }
105
+
106
+ this.moveClone(e as MouseEvent)
107
+
108
+ addMouseEnter('dl-widget', this.handleMouseEnter as any)
109
+
110
+ window.addEventListener('mousemove', this.moveClone)
111
+ window.addEventListener('mouseup', this.stopDragging)
112
+ },
113
+ moveClone(e: MouseEvent) {
114
+ if (!this.isDragging) return
115
+ const clone = this.$refs.clone as HTMLElement
116
+ clone.style.left = `${e.pageX - clone.offsetWidth / 2 - 5}px`
117
+ clone.style.top = `${e.pageY + 10}px`
118
+ },
119
+ stopDragging(e: MouseEvent) {
120
+ this.isDragging = false
121
+ ;(this.$refs.clone as HTMLElement).innerHTML = ''
122
+ const target = getElementAbove(e.target as HTMLElement, 'dl-widget')
123
+ if (target && this.draggedWidget) {
124
+ swapNodes({
125
+ source: this.draggedWidget,
126
+ target
127
+ })
128
+ }
129
+ window.removeEventListener('mousemove', this.moveClone)
130
+ window.removeEventListener('mouseup', this.stopDragging)
131
+ setTimeout(() => {
132
+ removeMouseEnter('dl-widget', this.handleMouseEnter as any)
133
+ }, 1)
134
+ },
135
+ handleMouseEnter(e: MouseEvent) {
136
+ this.hoveredWidget = e.target as HTMLElement
137
+ const mouseOffsetInside = e.clientX - this.hoveredWidget.offsetLeft
138
+ this.isLeftSide =
139
+ mouseOffsetInside < this.hoveredWidget.offsetWidth / 2
140
+ this.hoveredWidget.addEventListener(
141
+ 'mousemove',
142
+ this.handleMouseInsideWidget
143
+ )
144
+ this.hoveredWidget.addEventListener('mouseleave', () => {
145
+ clearTimeout(this.timer)
146
+ this.hoveredWidget.removeEventListener(
147
+ 'mousemove',
148
+ this.handleMouseInsideWidget
149
+ )
150
+ })
151
+ this.timer = setTimeout(this.insertWidget, 1000)
152
+ },
153
+ insertWidget() {
154
+ const targetWidget = getElementAbove(
155
+ this.hoveredWidget,
156
+ 'widget-wrapper'
157
+ )
158
+ const targetRow = getElementAbove(this.hoveredWidget, 'dl-grid-row')
159
+ if (this.isLeftSide) {
160
+ targetRow.insertBefore(
161
+ this.$refs.wrapper as HTMLElement,
162
+ targetWidget
163
+ )
164
+ } else {
165
+ insertAfter(
166
+ this.$refs.wrapper as unknown as HTMLElement,
167
+ targetWidget
168
+ )
169
+ }
170
+ this.hoveredWidget.removeEventListener(
171
+ 'mousemove',
172
+ this.handleMouseInsideWidget
173
+ )
174
+ setFlexBasis()
175
+ },
176
+ handleMouseInsideWidget(e: MouseEvent) {
177
+ const mouseOffsetInside = e.clientX - this.hoveredWidget.offsetLeft
178
+ const isLeftSide =
179
+ mouseOffsetInside < this.hoveredWidget.offsetWidth / 2
180
+ if (this.isLeftSide !== isLeftSide) {
181
+ clearTimeout(this.timer)
182
+ this.handleMouseEnter(e)
183
+ }
27
184
  }
28
185
  }
29
186
  })
@@ -31,12 +188,30 @@ export default defineComponent({
31
188
 
32
189
  <style lang="scss" scoped>
33
190
  .dl-widget {
191
+ border: 1px solid var(--dl-color-separator);
192
+ user-select: none;
34
193
  height: 100%;
35
194
  display: flex;
36
195
  flex-direction: column;
37
196
  &__header {
197
+ display: flex;
38
198
  padding: 10px;
39
199
  border-bottom: 1px solid var(--dl-color-separator);
200
+
201
+ &--drag-icon {
202
+ flex-grow: 1;
203
+ cursor: pointer;
204
+
205
+ &::v-deep .dl-icon {
206
+ transform: rotate(90deg) !important;
207
+ }
208
+ }
209
+
210
+ &--titles {
211
+ width: 50%;
212
+ display: flex;
213
+ flex-direction: column;
214
+ }
40
215
  }
41
216
  &__content {
42
217
  padding: 5px;
@@ -46,5 +221,21 @@ export default defineComponent({
46
221
  padding: 5px;
47
222
  font-size: 10px;
48
223
  }
224
+
225
+ &__drag {
226
+ visibility: hidden;
227
+ }
228
+ }
229
+
230
+ .drag-clone {
231
+ position: absolute;
232
+ background-color: var(--dl-color-text-buttons);
233
+ z-index: var(--dl-z-index-menu);
234
+ }
235
+
236
+ .widget-wrapper {
237
+ flex-basis: var(--widget-flex-basis);
238
+ margin: var(--row-gap) var(--column-gap);
239
+ transition: 0.1s;
49
240
  }
50
241
  </style>
@@ -1,4 +1,4 @@
1
1
  import DlWidget from './DlWidget.vue'
2
- import DlWidgetGrid from './DlWidgetGrid.vue'
2
+ import DlGrid from './DlGrid.vue'
3
3
 
4
- export { DlWidget, DlWidgetGrid }
4
+ export { DlWidget, DlGrid }