@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.
@@ -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 MlToolbar from './MlToolbar.vue'
2
- export { MlToolbar }
3
- export type { MlButtonData } from './MlToolbar.vue'
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>
@@ -0,0 +1,6 @@
1
+ // svg.d.ts
2
+ declare module '*.svg' {
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ const content: any
5
+ export default content
6
+ }
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}