@mlightcad/ui-components 0.1.2 → 0.1.4
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/dist/ui-components.es.js +529 -424
- package/dist/ui-components.umd.js +2 -2
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/package.json +1 -1
- package/src/components/MlToggleButton.vue +4 -6
- package/src/components/MlToolBar.vue +194 -57
- package/src/components/MlToolPalette.vue +89 -62
- package/src/composables/useResize.ts +3 -3
- package/src/index.ts +0 -1
- package/src/components/types.ts +0 -3
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
<!-- Main tool palette container -->
|
|
2
3
|
<div
|
|
3
4
|
ref="toolPaletteElement"
|
|
4
5
|
:style="[resizedStyle]"
|
|
5
6
|
class="ml-tool-palette-dialog"
|
|
6
7
|
v-if="visible"
|
|
8
|
+
:class="{ 'ml-tool-palette-disabled': disabled }"
|
|
7
9
|
>
|
|
10
|
+
<!-- Layout container for title bar and content -->
|
|
8
11
|
<div class="ml-tool-palette-dialog-layout" :class="orientation">
|
|
9
|
-
|
|
12
|
+
<!-- Title bar with close button, collapse toggle, and title -->
|
|
13
|
+
<div
|
|
14
|
+
ref="titleBarElement"
|
|
15
|
+
class="ml-tool-palette-title-bar"
|
|
16
|
+
:style="titleBarBorderStyle"
|
|
17
|
+
>
|
|
18
|
+
<!-- Close button (disabled when component is disabled) -->
|
|
10
19
|
<el-icon
|
|
11
20
|
:size="18"
|
|
12
21
|
class="ml-tool-palette-dialog-icon"
|
|
13
|
-
@click="handleClose"
|
|
22
|
+
@click="!disabled && handleClose()"
|
|
14
23
|
>
|
|
15
24
|
<svg
|
|
16
25
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -24,21 +33,29 @@
|
|
|
24
33
|
/>
|
|
25
34
|
</svg>
|
|
26
35
|
</el-icon>
|
|
36
|
+
|
|
37
|
+
<!-- Collapse toggle (disabled when component is disabled) -->
|
|
27
38
|
<ml-collapse
|
|
28
39
|
class="ml-tool-palette-dialog-icon"
|
|
29
40
|
v-model="collapsed"
|
|
30
41
|
:reverse="reversed"
|
|
42
|
+
:disabled="disabled"
|
|
31
43
|
/>
|
|
44
|
+
|
|
45
|
+
<!-- Displayed title of the tool palette -->
|
|
32
46
|
<span class="ml-tool-palette-title">{{ displayedTitle }}</span>
|
|
33
47
|
</div>
|
|
48
|
+
|
|
49
|
+
<!-- Main content area -->
|
|
34
50
|
<div class="ml-tool-palette-content">
|
|
51
|
+
<!-- Tabs if provided -->
|
|
35
52
|
<el-tabs
|
|
36
53
|
v-if="hasTabs"
|
|
37
54
|
v-model="activeTab"
|
|
38
55
|
type="border-card"
|
|
39
56
|
class="ml-tool-palette-tabs"
|
|
40
|
-
@tab-remove="handleTabClose"
|
|
41
|
-
@tab-change="handleTabChange"
|
|
57
|
+
@tab-remove="!disabled && handleTabClose"
|
|
58
|
+
@tab-change="!disabled && handleTabChange"
|
|
42
59
|
>
|
|
43
60
|
<el-tab-pane
|
|
44
61
|
v-for="tab in props.tabs"
|
|
@@ -52,6 +69,8 @@
|
|
|
52
69
|
<slot :name="`tab-${tab.name}`" />
|
|
53
70
|
</el-tab-pane>
|
|
54
71
|
</el-tabs>
|
|
72
|
+
|
|
73
|
+
<!-- Default slot content if no tabs -->
|
|
55
74
|
<div v-else class="ml-tool-palette-default-content">
|
|
56
75
|
<slot></slot>
|
|
57
76
|
</div>
|
|
@@ -114,8 +133,11 @@ interface Props {
|
|
|
114
133
|
* The minimum distance from the bottom side of the tool palette to the bottom side of the window
|
|
115
134
|
*/
|
|
116
135
|
bottomOffset?: number
|
|
136
|
+
/** Disable all interactions for this component */
|
|
137
|
+
disabled?: boolean
|
|
117
138
|
}
|
|
118
139
|
|
|
140
|
+
/** Events emitted by the tool palette */
|
|
119
141
|
interface Events {
|
|
120
142
|
/**
|
|
121
143
|
* Trigger this event when the tool palette closed.
|
|
@@ -141,7 +163,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
141
163
|
leftOffset: 0,
|
|
142
164
|
rightOffset: 0,
|
|
143
165
|
topOffset: 0,
|
|
144
|
-
bottomOffset: 0
|
|
166
|
+
bottomOffset: 0,
|
|
167
|
+
disabled: false
|
|
145
168
|
})
|
|
146
169
|
// Flag to control whether the tool palette is visible
|
|
147
170
|
const visible = defineModel({ default: true })
|
|
@@ -156,17 +179,26 @@ const hasTabs = computed(() => {
|
|
|
156
179
|
|
|
157
180
|
// Initialize active tab to first tab if tabs are provided and no active tab is set
|
|
158
181
|
onMounted(() => {
|
|
159
|
-
if (
|
|
182
|
+
if (
|
|
183
|
+
hasTabs.value &&
|
|
184
|
+
!activeTab.value &&
|
|
185
|
+
props.tabs &&
|
|
186
|
+
props.tabs.length > 0
|
|
187
|
+
) {
|
|
160
188
|
activeTab.value = props.tabs[0].name
|
|
161
189
|
}
|
|
162
190
|
})
|
|
163
191
|
|
|
164
192
|
// Watch for tabs prop changes and initialize active tab if needed
|
|
165
|
-
watch(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
193
|
+
watch(
|
|
194
|
+
() => props.tabs,
|
|
195
|
+
(newTabs: MlToolPaletteTab[] | undefined) => {
|
|
196
|
+
if (newTabs && newTabs.length > 0 && !activeTab.value) {
|
|
197
|
+
activeTab.value = newTabs[0].name
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
{ immediate: true }
|
|
201
|
+
)
|
|
170
202
|
|
|
171
203
|
// This varible is used in CSS
|
|
172
204
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -178,55 +210,49 @@ const titleBarElement = ref<HTMLElement | null>(null)
|
|
|
178
210
|
// Reference to tool palette HTML element
|
|
179
211
|
const toolPaletteElement = ref<HTMLElement | null>(null)
|
|
180
212
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
213
|
+
// Drag options
|
|
214
|
+
const dragOptions = computed<DragOptions>(() => ({
|
|
215
|
+
offset: ref({
|
|
216
|
+
left: props.leftOffset,
|
|
217
|
+
right: props.rightOffset,
|
|
218
|
+
top: props.topOffset,
|
|
219
|
+
bottom: props.bottomOffset
|
|
220
|
+
})
|
|
221
|
+
}))
|
|
222
|
+
|
|
223
|
+
// Compute bounding rect and orientation for layout
|
|
191
224
|
const {
|
|
192
225
|
rect: toolPaletteRect,
|
|
193
226
|
orientation,
|
|
194
227
|
reversed
|
|
195
228
|
} = useBoundingRect(toolPaletteElement, titleBarElement, collapsed, dragOptions)
|
|
196
229
|
|
|
197
|
-
// Resized style
|
|
198
|
-
const resizedStyle = computed(() => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
borderLeft: '1px solid var(--el-border-color)'
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
borderRight: '1px solid var(--el-border-color)'
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
// Compute the title to display in the title bar
|
|
230
|
+
// Resized style for container
|
|
231
|
+
const resizedStyle = computed(() => ({
|
|
232
|
+
left: `${toolPaletteRect.value.left}px`,
|
|
233
|
+
top: `${toolPaletteRect.value.top}px`,
|
|
234
|
+
width: `${toolPaletteRect.value.width}px`,
|
|
235
|
+
height: `${toolPaletteRect.value.height}px`
|
|
236
|
+
}))
|
|
237
|
+
|
|
238
|
+
// Border style for title bar based on reversed orientation
|
|
239
|
+
const titleBarBorderStyle = computed(() =>
|
|
240
|
+
reversed.value
|
|
241
|
+
? { borderLeft: '1px solid var(--el-border-color)', borderRight: null }
|
|
242
|
+
: { borderLeft: null, borderRight: '1px solid var(--el-border-color)' }
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
// Compute the displayed title (from active tab or component title)
|
|
218
246
|
const displayedTitle = computed(() => {
|
|
219
247
|
// If tabs are provided and there's an active tab, use the active tab's title
|
|
220
248
|
if (hasTabs.value && activeTab.value && props.tabs) {
|
|
221
249
|
const activeTabData = props.tabs.find(tab => tab.name === activeTab.value)
|
|
222
|
-
if (activeTabData
|
|
223
|
-
return activeTabData.title
|
|
224
|
-
}
|
|
250
|
+
if (activeTabData?.title) return activeTabData.title
|
|
225
251
|
}
|
|
226
|
-
// Otherwise, use the component's title prop
|
|
227
252
|
return props.title
|
|
228
253
|
})
|
|
229
254
|
|
|
255
|
+
// Close tool palette
|
|
230
256
|
const handleClose = () => {
|
|
231
257
|
visible.value = false
|
|
232
258
|
const element = toolPaletteElement.value
|
|
@@ -236,28 +262,25 @@ const handleClose = () => {
|
|
|
236
262
|
})
|
|
237
263
|
}
|
|
238
264
|
|
|
239
|
-
//
|
|
265
|
+
// Tab change handler
|
|
240
266
|
const handleTabChange = (tabName: string) => {
|
|
241
267
|
emit('tab-change', tabName)
|
|
242
268
|
}
|
|
243
269
|
|
|
244
|
-
//
|
|
270
|
+
// Tab close handler
|
|
245
271
|
const handleTabClose = (tabName: string) => {
|
|
246
272
|
emit('tab-close', tabName)
|
|
247
|
-
|
|
248
|
-
//
|
|
273
|
+
|
|
274
|
+
// Switch to another tab if active tab is closed
|
|
249
275
|
if (activeTab.value === tabName && props.tabs) {
|
|
250
276
|
const currentIndex = props.tabs.findIndex(t => t.name === tabName)
|
|
251
277
|
if (currentIndex >= 0) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (nextTab)
|
|
255
|
-
activeTab.value = nextTab.name
|
|
256
|
-
}
|
|
278
|
+
const nextTab =
|
|
279
|
+
props.tabs[currentIndex + 1] || props.tabs[currentIndex - 1]
|
|
280
|
+
if (nextTab) activeTab.value = nextTab.name
|
|
257
281
|
}
|
|
258
282
|
}
|
|
259
283
|
}
|
|
260
|
-
|
|
261
284
|
</script>
|
|
262
285
|
|
|
263
286
|
<style scoped>
|
|
@@ -270,9 +293,15 @@ const handleTabClose = (tabName: string) => {
|
|
|
270
293
|
border: 1px solid var(--el-border-color);
|
|
271
294
|
}
|
|
272
295
|
|
|
296
|
+
/* Disabled overlay to prevent interactions */
|
|
297
|
+
.ml-tool-palette-disabled {
|
|
298
|
+
pointer-events: none;
|
|
299
|
+
opacity: 0.6;
|
|
300
|
+
}
|
|
301
|
+
|
|
273
302
|
.ml-tool-palette-dialog-icon {
|
|
274
303
|
border-bottom: 1px solid var(--el-border-color);
|
|
275
|
-
cursor: default;
|
|
304
|
+
cursor: default;
|
|
276
305
|
}
|
|
277
306
|
|
|
278
307
|
.ml-tool-palette-dialog-icon:hover {
|
|
@@ -289,8 +318,8 @@ const handleTabClose = (tabName: string) => {
|
|
|
289
318
|
display: flex;
|
|
290
319
|
justify-content: left;
|
|
291
320
|
align-items: center;
|
|
292
|
-
cursor: move;
|
|
293
|
-
writing-mode: vertical-rl;
|
|
321
|
+
cursor: move;
|
|
322
|
+
writing-mode: vertical-rl;
|
|
294
323
|
text-align: center;
|
|
295
324
|
background-color: var(--el-fill-color);
|
|
296
325
|
}
|
|
@@ -361,16 +390,14 @@ const handleTabClose = (tabName: string) => {
|
|
|
361
390
|
.ml-tool-palette-dialog-layout.left .ml-tool-palette-title-bar {
|
|
362
391
|
order: 1;
|
|
363
392
|
}
|
|
364
|
-
|
|
365
393
|
.ml-tool-palette-dialog-layout.left .ml-tool-palette-content {
|
|
366
394
|
order: 2;
|
|
367
395
|
}
|
|
368
396
|
|
|
369
|
-
/*
|
|
397
|
+
/* Right orientation */
|
|
370
398
|
.ml-tool-palette-dialog-layout.right .ml-tool-palette-title-bar {
|
|
371
399
|
order: 2;
|
|
372
400
|
}
|
|
373
|
-
|
|
374
401
|
.ml-tool-palette-dialog-layout.right .ml-tool-palette-content {
|
|
375
402
|
order: 1;
|
|
376
403
|
}
|
|
@@ -53,9 +53,9 @@ export function useResize(
|
|
|
53
53
|
const offsetY = event.clientY - rect.top
|
|
54
54
|
|
|
55
55
|
// Check if the mouse is near the borders or the corner
|
|
56
|
-
const nearLeft = offsetX <= resizeThreshold
|
|
57
|
-
const nearRight =
|
|
58
|
-
const nearBottom =
|
|
56
|
+
const nearLeft = Math.abs(offsetX) <= resizeThreshold
|
|
57
|
+
const nearRight = Math.abs(rect.width - offsetX) <= resizeThreshold
|
|
58
|
+
const nearBottom = Math.abs(rect.height - offsetY) <= resizeThreshold
|
|
59
59
|
|
|
60
60
|
// Set the resize cursor based on the position
|
|
61
61
|
if (nearLeft && nearBottom && reverse.value) {
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,6 @@ export type { MlDropdownMenuItem } from './components/MlDropdown.vue'
|
|
|
18
18
|
export type { MlToggleButtonData } from './components/MlToggleButton.vue'
|
|
19
19
|
export type { MlButtonData } from './components/MlToolBar.vue'
|
|
20
20
|
export type { MlToolPaletteTab } from './components/MlToolPalette.vue'
|
|
21
|
-
export * from './components/types'
|
|
22
21
|
|
|
23
22
|
// Optionally, export them as a plugin for Vue
|
|
24
23
|
export default {
|
package/src/components/types.ts
DELETED