@mlightcad/ui-components 0.0.6 → 0.0.8
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/README.md +52 -0
- package/dist/ui-components.es.js +459 -38
- package/dist/ui-components.umd.js +2 -1
- package/lib/MlDropdown.vue.d.ts +59 -0
- package/lib/MlDropdown.vue.js +106 -0
- package/lib/MlLanguage.vue.d.ts +29 -0
- package/lib/MlLanguage.vue.js +67 -0
- package/lib/MlToolPalette.vue.d.ts +2 -0
- package/lib/MlToolPalette.vue.js +149 -0
- package/lib/MlToolbar.vue.d.ts +1 -1
- package/lib/index.d.ts +8 -3
- package/lib/index.js +10 -2
- package/package.json +1 -1
- package/src/components/MlCollapse.vue +55 -0
- package/src/components/MlDropdown.vue +94 -0
- package/src/components/MlLanguage.vue +41 -0
- package/src/components/MlToolPalette.vue +199 -0
- package/src/{MlToolbar.vue → components/MlToolbar.vue} +13 -9
- package/src/composable/types.ts +16 -0
- package/src/composable/useBoundingRect.ts +87 -0
- package/src/composable/useDrag.ts +157 -0
- package/src/composable/useDragEx.ts +50 -0
- package/src/composable/useInitialRect.ts +51 -0
- package/src/composable/useRect.ts +23 -0
- package/src/composable/useResize.ts +184 -0
- package/src/composable/useTransition.ts +67 -0
- package/src/composable/useWindowSize.ts +25 -0
- package/src/index.ts +12 -3
- package/src/svg/arrow-left.svg +11 -0
- package/src/svg/arrow-right.svg +11 -0
- package/src/svg/svg.d.ts +6 -0
- package/dist/style.css +0 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { Rect } from './types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resize the specified element when moving mouse to its
|
|
7
|
+
* - right border, bottom border, and right-bottom corner if argument `reverse` is false
|
|
8
|
+
* - or left border, bottom border, and left-bottom corner if argument `reverse` is true
|
|
9
|
+
* @param targetRef Input element to resize
|
|
10
|
+
* @param reverse Input flag to decide where to resize the element
|
|
11
|
+
* @param minSize Input minimum size to resize.
|
|
12
|
+
* @returns Return the following data.
|
|
13
|
+
* - width: new width of the element after resized
|
|
14
|
+
* - height: new height of the element after resized
|
|
15
|
+
* - isResizing: flag to indicate whether the element is in resizing state
|
|
16
|
+
*/
|
|
17
|
+
export function useResize(
|
|
18
|
+
targetRef: Ref<HTMLElement | null>,
|
|
19
|
+
reverse: Ref<boolean> = ref(false),
|
|
20
|
+
minSize: { width: number; height: number } = { width: 20, height: 40 }
|
|
21
|
+
) {
|
|
22
|
+
const resizedBoundingRect = ref<Rect>({
|
|
23
|
+
width: null,
|
|
24
|
+
height: null,
|
|
25
|
+
left: null,
|
|
26
|
+
top: null
|
|
27
|
+
})
|
|
28
|
+
const isResizing = ref(false)
|
|
29
|
+
let initialLeft = 0
|
|
30
|
+
let initialWidth = 0
|
|
31
|
+
let initialHeight = 0
|
|
32
|
+
let startX = 0
|
|
33
|
+
let startY = 0
|
|
34
|
+
const resizeThreshold = 5 // Defines the area where resize can be triggered
|
|
35
|
+
const resizeDirection = ref<'left' | 'right' | 'bottom' | 'right-bottom-corner' | 'left-bottom-corner' | null>(
|
|
36
|
+
null
|
|
37
|
+
) // Track the resize direction
|
|
38
|
+
|
|
39
|
+
const onMouseMove = (event: MouseEvent) => {
|
|
40
|
+
if (!targetRef.value) return
|
|
41
|
+
|
|
42
|
+
if (!isResizing.value) {
|
|
43
|
+
const rect = targetRef.value.getBoundingClientRect()
|
|
44
|
+
const offsetX = event.clientX - rect.left
|
|
45
|
+
const offsetY = event.clientY - rect.top
|
|
46
|
+
|
|
47
|
+
// Check if the mouse is near the borders or the corner
|
|
48
|
+
const nearLeft = offsetX <= resizeThreshold
|
|
49
|
+
const nearRight = offsetX >= rect.width - resizeThreshold
|
|
50
|
+
const nearBottom = offsetY >= rect.height - resizeThreshold
|
|
51
|
+
|
|
52
|
+
// Set the resize cursor based on the position
|
|
53
|
+
if (nearLeft && nearBottom && reverse.value) {
|
|
54
|
+
targetRef.value.style.cursor = 'nesw-resize' // Change to bottom-right resize cursor
|
|
55
|
+
resizeDirection.value = 'left-bottom-corner'
|
|
56
|
+
} else if (nearRight && nearBottom && !reverse.value) {
|
|
57
|
+
targetRef.value.style.cursor = 'nwse-resize' // Change to bottom-left resize cursor
|
|
58
|
+
resizeDirection.value = 'right-bottom-corner'
|
|
59
|
+
} else if (nearLeft && reverse.value) {
|
|
60
|
+
targetRef.value.style.cursor = 'ew-resize' // Change to left-side resize cursor
|
|
61
|
+
resizeDirection.value = 'left'
|
|
62
|
+
} else if (nearRight && !reverse.value) {
|
|
63
|
+
targetRef.value.style.cursor = 'ew-resize' // Change to right-side resize cursor
|
|
64
|
+
resizeDirection.value = 'right'
|
|
65
|
+
} else if (nearBottom) {
|
|
66
|
+
targetRef.value.style.cursor = 'ns-resize' // Change to bottom-side resize cursor
|
|
67
|
+
resizeDirection.value = 'bottom'
|
|
68
|
+
} else {
|
|
69
|
+
targetRef.value.style.cursor = '' // Reset cursor if not near the edges
|
|
70
|
+
resizeDirection.value = null
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// While resizing, update dimensions based on the direction
|
|
74
|
+
const deltaX = event.clientX - startX
|
|
75
|
+
const deltaY = event.clientY - startY
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
resizeDirection.value === 'left' ||
|
|
79
|
+
resizeDirection.value === 'left-bottom-corner'
|
|
80
|
+
) {
|
|
81
|
+
resizedBoundingRect.value.width = Math.max(
|
|
82
|
+
minSize.width,
|
|
83
|
+
initialWidth - deltaX
|
|
84
|
+
)
|
|
85
|
+
resizedBoundingRect.value.left = initialLeft + deltaX
|
|
86
|
+
targetRef.value.style.left = resizedBoundingRect.value.left + 'px'
|
|
87
|
+
targetRef.value.style.width = resizedBoundingRect.value.width + 'px'
|
|
88
|
+
}
|
|
89
|
+
if (
|
|
90
|
+
resizeDirection.value === 'right' ||
|
|
91
|
+
resizeDirection.value === 'right-bottom-corner'
|
|
92
|
+
) {
|
|
93
|
+
resizedBoundingRect.value.width = Math.max(
|
|
94
|
+
minSize.width,
|
|
95
|
+
initialWidth + deltaX
|
|
96
|
+
)
|
|
97
|
+
targetRef.value.style.width = resizedBoundingRect.value.width + 'px'
|
|
98
|
+
}
|
|
99
|
+
if (
|
|
100
|
+
resizeDirection.value === 'bottom' ||
|
|
101
|
+
resizeDirection.value === 'left-bottom-corner' ||
|
|
102
|
+
resizeDirection.value === 'right-bottom-corner'
|
|
103
|
+
) {
|
|
104
|
+
resizedBoundingRect.value.height = Math.max(
|
|
105
|
+
minSize.height,
|
|
106
|
+
initialHeight + deltaY
|
|
107
|
+
)
|
|
108
|
+
targetRef.value.style.height = resizedBoundingRect.value.height + 'px'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const onMouseDown = (event: MouseEvent) => {
|
|
114
|
+
if (!targetRef.value || !resizeDirection.value) return
|
|
115
|
+
|
|
116
|
+
const rect = targetRef.value.getBoundingClientRect()
|
|
117
|
+
startX = event.clientX
|
|
118
|
+
startY = event.clientY
|
|
119
|
+
|
|
120
|
+
initialWidth = rect.width
|
|
121
|
+
initialHeight = rect.height
|
|
122
|
+
initialLeft = rect.left
|
|
123
|
+
|
|
124
|
+
// Set width and height before resizing starts
|
|
125
|
+
resizedBoundingRect.value.width = initialWidth
|
|
126
|
+
resizedBoundingRect.value.height = initialHeight
|
|
127
|
+
resizedBoundingRect.value.left = rect.left
|
|
128
|
+
resizedBoundingRect.value.top = rect.top
|
|
129
|
+
|
|
130
|
+
isResizing.value = true
|
|
131
|
+
|
|
132
|
+
document.addEventListener('mousemove', onMouseMove)
|
|
133
|
+
document.addEventListener('mouseup', onMouseUp)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const onMouseUp = () => {
|
|
137
|
+
isResizing.value = false
|
|
138
|
+
resizeDirection.value = null
|
|
139
|
+
|
|
140
|
+
// Reset the cursor to default after resizing
|
|
141
|
+
if (targetRef.value) {
|
|
142
|
+
targetRef.value.style.cursor = ''
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
document.removeEventListener('mousemove', onMouseMove)
|
|
146
|
+
document.removeEventListener('mouseup', onMouseUp)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const cleanupListeners = () => {
|
|
150
|
+
if (targetRef.value) {
|
|
151
|
+
targetRef.value.removeEventListener('mousedown', onMouseDown)
|
|
152
|
+
targetRef.value.removeEventListener('mousemove', onMouseMove)
|
|
153
|
+
}
|
|
154
|
+
document.removeEventListener('mouseup', onMouseUp)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const setupListeners = () => {
|
|
158
|
+
if (targetRef.value) {
|
|
159
|
+
targetRef.value.addEventListener('mousedown', onMouseDown)
|
|
160
|
+
targetRef.value.addEventListener('mousemove', onMouseMove)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
onMounted(() => {
|
|
165
|
+
if (targetRef.value) {
|
|
166
|
+
setupListeners()
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
onBeforeUnmount(() => {
|
|
171
|
+
cleanupListeners()
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Watch for changes in the targetRef, to handle cases where v-if makes the element appear/disappear
|
|
175
|
+
watch(targetRef, newVal => {
|
|
176
|
+
if (newVal) {
|
|
177
|
+
setupListeners()
|
|
178
|
+
} else {
|
|
179
|
+
cleanupListeners()
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
return { rect: resizedBoundingRect, isResizing }
|
|
184
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, Ref, watch } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add and clean transition style for the specified element
|
|
5
|
+
* @param targetRef Input element to add and clean transition style
|
|
6
|
+
* @param reversed Input flag whether to reverse cllapse icon
|
|
7
|
+
* @param collapsed Input flag to indicate whether the element is collapsed
|
|
8
|
+
*/
|
|
9
|
+
export function useTransition(
|
|
10
|
+
targetRef: Ref<HTMLElement | null>,
|
|
11
|
+
reversed: Ref<boolean>,
|
|
12
|
+
collapsed: Ref<boolean>,
|
|
13
|
+
) {
|
|
14
|
+
// Clean transition logic
|
|
15
|
+
function cleanTransition() {
|
|
16
|
+
if (targetRef.value) {
|
|
17
|
+
// Here you can clean up or reset the style after transition
|
|
18
|
+
targetRef.value.style.transition = '' // Reset any inline transitions
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const cleanupListeners = () => {
|
|
23
|
+
if (targetRef.value) {
|
|
24
|
+
targetRef.value.removeEventListener('transitionend', cleanTransition)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const setupListeners = () => {
|
|
29
|
+
if (targetRef.value) {
|
|
30
|
+
targetRef.value.addEventListener('transitionend', cleanTransition)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Attach and remove the transitionend event listener
|
|
35
|
+
onMounted(() => {
|
|
36
|
+
if (targetRef.value) {
|
|
37
|
+
targetRef.value.addEventListener('transitionend', cleanTransition)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
onBeforeUnmount(() => {
|
|
42
|
+
if (targetRef.value) {
|
|
43
|
+
targetRef.value.removeEventListener('transitionend', cleanTransition)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Watch for changes in the targetRef, to handle cases where v-if makes the element appear/disappear
|
|
48
|
+
watch(targetRef, newVal => {
|
|
49
|
+
if (newVal) {
|
|
50
|
+
setupListeners()
|
|
51
|
+
} else {
|
|
52
|
+
cleanupListeners()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Add transition style
|
|
57
|
+
watch(collapsed, () => {
|
|
58
|
+
if (targetRef.value) {
|
|
59
|
+
const element = targetRef.value as HTMLElement
|
|
60
|
+
if (reversed.value) {
|
|
61
|
+
element.style.transition = 'width 0.3s ease-out, left 0.3s ease-out'
|
|
62
|
+
} else {
|
|
63
|
+
element.style.transition = 'width 0.3s ease'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { onMounted, onUnmounted, ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get window size
|
|
5
|
+
* @returns Return window size
|
|
6
|
+
*/
|
|
7
|
+
export function useWindowSize() {
|
|
8
|
+
const windowWidth = ref(window.innerWidth)
|
|
9
|
+
const windowHeight = ref(window.innerHeight)
|
|
10
|
+
|
|
11
|
+
const updateWindowSize = () => {
|
|
12
|
+
windowWidth.value = window.innerWidth
|
|
13
|
+
windowHeight.value = window.innerHeight
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
onMounted(() => {
|
|
17
|
+
window.addEventListener('resize', updateWindowSize)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
onUnmounted(() => {
|
|
21
|
+
window.removeEventListener('resize', updateWindowSize)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
return { windowWidth, windowHeight }
|
|
25
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import MlCollapse from './components/MlCollapse.vue'
|
|
2
|
+
import MlDropdown from './components/MlDropdown.vue'
|
|
3
|
+
import MlLanguage from './components/MlLanguage.vue'
|
|
4
|
+
import MlToolbar from './components/MlToolbar.vue'
|
|
5
|
+
import MlToolPalette from './components/MlToolPalette.vue'
|
|
6
|
+
export { MlCollapse, MlDropdown, MlLanguage, MlToolbar, MlToolPalette }
|
|
7
|
+
export type { MlDropdownMenuItem } from './components/MlDropdown.vue'
|
|
8
|
+
export type { MlButtonData } from './components/MlToolbar.vue'
|
|
4
9
|
|
|
5
10
|
// Optionally, export them as a plugin for Vue
|
|
6
11
|
export default {
|
|
7
12
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
13
|
install(app: any) {
|
|
14
|
+
app.component('MlCollapse', MlDropdown)
|
|
15
|
+
app.component('MlDropdown', MlDropdown)
|
|
16
|
+
app.component('MlLanguage', MlLanguage)
|
|
9
17
|
app.component('MlToolbar', MlToolbar)
|
|
18
|
+
app.component('MlToolPalette', MlToolPalette)
|
|
10
19
|
}
|
|
11
20
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
3
|
+
width="1em"
|
|
4
|
+
height="1em"
|
|
5
|
+
viewBox="0 0 1024 1024"
|
|
6
|
+
>
|
|
7
|
+
<path
|
|
8
|
+
fill="currentColor"
|
|
9
|
+
d="M609.408 149.376L277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0a30.59 30.59 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.59 30.59 0 0 0 0-42.688a29.12 29.12 0 0 0-41.728 0"
|
|
10
|
+
/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
3
|
+
width="1em"
|
|
4
|
+
height="1em"
|
|
5
|
+
viewBox="0 0 1024 1024"
|
|
6
|
+
>
|
|
7
|
+
<path
|
|
8
|
+
fill="currentColor"
|
|
9
|
+
d="M340.864 149.312a30.59 30.59 0 0 0 0 42.752L652.736 512L340.864 831.872a30.59 30.59 0 0 0 0 42.752a29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z"
|
|
10
|
+
/>
|
|
11
|
+
</svg>
|
package/src/svg/svg.d.ts
ADDED
package/dist/style.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.ml-vertical-toolbar-button-group[data-v-0fe7ba99]{display:flex;flex-direction:column}.ml-horizontal-toolbar-button-group[data-v-0fe7ba99]{display:flex;flex-direction:row}.ml-toolbar-button[data-v-0fe7ba99]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:5px}.ml-toolbar-button-text[data-v-0fe7ba99]{margin-left:0;margin-top:5px}
|