694 lines
22 KiB
Vue
694 lines
22 KiB
Vue
<template>
|
|
<div class="flex flex-col space-y-2">
|
|
<div class="px-2 space-y-1">
|
|
<FormButton to="/" size="sm" :icon-left="ArrowLeftIcon" class="my-1">
|
|
Home
|
|
</FormButton>
|
|
<hr />
|
|
</div>
|
|
|
|
<!-- Step 1: Mapping Mode Selection -->
|
|
<div class="px-2">
|
|
<p class="h5">Assign by</p>
|
|
<div class="space-y-2 my-2">
|
|
<FormSelectBase
|
|
:model-value="selectedMappingMode"
|
|
name="mappingMode"
|
|
label="Assign by"
|
|
class="w-full"
|
|
fixed-height
|
|
size="sm"
|
|
:items="mappingModeOptions"
|
|
:allow-unset="false"
|
|
mount-menu-on-body
|
|
@update:model-value="(value) => handleModeChange(value as string)"
|
|
>
|
|
<template #something-selected="{ value }">
|
|
<span class="text-primary text-xs">{{ value }}</span>
|
|
</template>
|
|
<template #option="{ item }">
|
|
<span class="text-xs">{{ item }}</span>
|
|
</template>
|
|
</FormSelectBase>
|
|
|
|
<!-- Mode-specific content -->
|
|
<div v-if="selectedMappingMode === 'Selection'">
|
|
<MapperSelectionMapper
|
|
:has-selection="(selectionInfo?.selectedObjectIds?.length || 0) > 0"
|
|
:selection-summary="selectionInfo?.summary || ''"
|
|
/>
|
|
</div>
|
|
|
|
<MapperLayerMapper
|
|
v-if="selectedMappingMode === 'Layer'"
|
|
v-model:selected-layers="selectedLayers"
|
|
:layer-options="layerOptions"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Category Selection -->
|
|
<div v-if="hasTargetsSelected" class="px-2">
|
|
<p class="h5">Target Category</p>
|
|
<div class="space-y-2 my-2">
|
|
<div class="flex space-x-2 items-center">
|
|
<div class="flex-1">
|
|
<FormSelectBase
|
|
key="label"
|
|
v-model="revitMapperStore.selectedCategory"
|
|
name="categoryMapping"
|
|
:placeholder="dropdownPlaceholder"
|
|
label="Target Category"
|
|
fixed-height
|
|
size="sm"
|
|
search
|
|
:search-placeholder="''"
|
|
:filter-predicate="searchFilterPredicate"
|
|
:items="categoryOptions"
|
|
:allow-unset="false"
|
|
mount-menu-on-body
|
|
>
|
|
<template #something-selected>
|
|
<span class="text-primary text-xs">
|
|
{{ displayLabel }}
|
|
</span>
|
|
</template>
|
|
<template #option="{ item }">
|
|
<span class="text-xs">{{ item.label }}</span>
|
|
</template>
|
|
</FormSelectBase>
|
|
</div>
|
|
|
|
<!-- Apply button -->
|
|
<FormButton
|
|
color="primary"
|
|
size="sm"
|
|
:disabled="!revitMapperStore.selectedCategory?.value"
|
|
@click="assignToCategory()"
|
|
>
|
|
Apply
|
|
</FormButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
<!-- Step 3: Mappings Summary Tables -->
|
|
<div
|
|
v-if="currentMappings.length > 0 || currentLayerMappings.length > 0"
|
|
class="px-2"
|
|
>
|
|
<p class="h5">
|
|
{{ `Assigned Categories (${currentMappings.length > 0 ? 'Object' : 'Layer'})` }}
|
|
</p>
|
|
|
|
<!-- Object Mappings Section -->
|
|
<div v-if="currentMappings.length > 0" class="my-2">
|
|
<div class="space-y-1">
|
|
<MapperMappedElementItem
|
|
v-for="mapping in currentMappings"
|
|
:key="mapping.categoryValue"
|
|
:category-label="mapping.categoryLabel"
|
|
:count-text="`${mapping.objectCount} object${
|
|
mapping.objectCount !== 1 ? 's' : ''
|
|
}`"
|
|
@select="selectMappedObjects(mapping)"
|
|
@clear="clearMapping(mapping)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Layer Mappings Section -->
|
|
<div v-if="currentLayerMappings.length > 0" class="my-2">
|
|
<div class="space-y-1">
|
|
<MapperMappedElementItem
|
|
v-for="layerMapping in currentLayerMappings"
|
|
:key="layerMapping.categoryValue"
|
|
:category-label="layerMapping.categoryLabel"
|
|
:count-text="`${layerMapping.layerCount} layer${
|
|
layerMapping.layerCount !== 1 ? 's' : ''
|
|
}`"
|
|
:tooltip-text="`Layers: ${layerMapping.layerNames.join(', ')}`"
|
|
@select="selectMappedLayers(layerMapping)"
|
|
@clear="clearLayerMapping(layerMapping)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Clear All and Select All buttons -->
|
|
<div class="flex justify-end space-x-2">
|
|
<!-- Selection mode buttons -->
|
|
<div
|
|
v-if="selectedMappingMode === 'Selection' && currentMappings.length > 0"
|
|
class="flex space-x-2"
|
|
>
|
|
<FormButton size="sm" color="outline" @click="selectAllMappedObjects()">
|
|
Select All
|
|
</FormButton>
|
|
<FormButton size="sm" color="danger" @click="clearAllMappings()">
|
|
Clear All Objects
|
|
</FormButton>
|
|
</div>
|
|
|
|
<!-- Layer mode buttons -->
|
|
<div
|
|
v-else-if="selectedMappingMode === 'Layer' && currentLayerMappings.length > 0"
|
|
class="flex space-x-2"
|
|
>
|
|
<FormButton size="sm" color="outline" @click="selectAllMappedLayers()">
|
|
Select All
|
|
</FormButton>
|
|
<FormButton size="sm" color="danger" @click="clearAllLayerMappings()">
|
|
Clear All Layers
|
|
</FormButton>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mode Confirmation Dialog -->
|
|
<CommonDialog
|
|
v-model:open="showModeConfirmDialog"
|
|
title="Switch Category Assignment Mode"
|
|
fullscreen="none"
|
|
>
|
|
<div class="text-sm text-foreground">
|
|
{{ conflictMessage }}
|
|
</div>
|
|
|
|
<div class="mt-4 flex justify-end space-x-2">
|
|
<FormButton size="sm" color="outline" @click="cancelModeChange()">
|
|
Cancel
|
|
</FormButton>
|
|
<FormButton size="sm" color="danger" @click="confirmModeChange()">
|
|
Clear & Switch
|
|
</FormButton>
|
|
</div>
|
|
</CommonDialog>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// === IMPORTS ===
|
|
import { storeToRefs } from 'pinia'
|
|
import { ArrowLeftIcon } from '@heroicons/vue/20/solid'
|
|
import { useSelectionStore } from '~/store/selection'
|
|
import { useRevitMapper } from '~/store/revitMapper'
|
|
import type {
|
|
Category,
|
|
CategoryMapping,
|
|
LayerCategoryMapping
|
|
} from '~/lib/bindings/definitions/IRevitMapperBinding'
|
|
|
|
// Import categories
|
|
import { getAvailableCategories, getCategoryLabel } from '~/lib/mapper/revit-categories'
|
|
import { useMixpanel } from '~/lib/core/composables/mixpanel'
|
|
|
|
// === STORES ===
|
|
const selectionStore = useSelectionStore()
|
|
const revitMapperStore = useRevitMapper()
|
|
const { selectionInfo } = storeToRefs(selectionStore)
|
|
const { trackEvent } = useMixpanel()
|
|
|
|
// === STATE ===
|
|
const selectedMappingMode = ref<string | undefined>(undefined)
|
|
const mappingModeOptions = ['Selection', 'Layer']
|
|
const categoryOptions = ref<Category[]>([])
|
|
const mappings = ref<CategoryMapping[]>([])
|
|
|
|
// Layer-specific state
|
|
const selectedLayers = ref<LayerOption[]>([])
|
|
const layerOptions = ref<LayerOption[]>([])
|
|
const layerMappings = ref<LayerCategoryMapping[]>([])
|
|
|
|
// Mode switching state
|
|
const showModeConfirmDialog = ref(false)
|
|
const pendingMode = ref<string>('')
|
|
const conflictMessage = ref('')
|
|
|
|
// === TYPES ===
|
|
interface LayerOption {
|
|
id: string
|
|
name: string
|
|
}
|
|
|
|
// === MAPPING CATEGORY STATE MGMT ===
|
|
const app = useNuxtApp()
|
|
const { $revitMapperBinding, $baseBinding } = app
|
|
// const categoryState = useRevitCategoryState(categoryOptions, $revitMapperBinding)
|
|
|
|
// === COMPUTED ===
|
|
const hasTargetsSelected = computed(() => {
|
|
if (selectedMappingMode.value === 'Selection') {
|
|
return (selectionInfo.value?.selectedObjectIds?.length || 0) > 0
|
|
} else if (selectedMappingMode.value === 'Layer') {
|
|
return selectedLayers.value.length > 0
|
|
}
|
|
return false
|
|
})
|
|
|
|
// Show appropriate mappings based on current mode
|
|
const currentMappings = computed(() => {
|
|
return selectedMappingMode.value === 'Selection' ? mappings.value : []
|
|
})
|
|
|
|
const currentLayerMappings = computed(() => {
|
|
return selectedMappingMode.value === 'Layer' ? layerMappings.value : []
|
|
})
|
|
|
|
const dropdownPlaceholder = computed(() => {
|
|
if (revitMapperStore.categoryStatus) {
|
|
return revitMapperStore.categoryStatus?.isMultiple
|
|
? 'Multiple categories'
|
|
: 'Select a category'
|
|
}
|
|
return undefined
|
|
})
|
|
|
|
const displayLabel = computed(() => {
|
|
const multiple = revitMapperStore.categoryStatus?.isMultiple
|
|
return multiple
|
|
? 'Multiple categories'
|
|
: revitMapperStore.selectedCategory?.label || ''
|
|
})
|
|
|
|
// === METHODS ===
|
|
|
|
// Search predicate for category dropdown
|
|
const searchFilterPredicate = (item: Category, query: string) => {
|
|
return item.label.toLowerCase().includes(query.toLowerCase())
|
|
}
|
|
|
|
// Handle mode changes with conflict checking
|
|
const handleModeChange = (newMode: string) => {
|
|
// If switching to same mode, do nothing
|
|
if (newMode === selectedMappingMode.value) return
|
|
|
|
// Check for conflicts - ONLY show dialog if there are existing mappings
|
|
if (newMode === 'Layer' && mappings.value.length > 0) {
|
|
// Switching to Layer mode with existing object mappings
|
|
pendingMode.value = newMode
|
|
conflictMessage.value = `Switching to Layer assignment mode will clear all current object category assignments. Continue?`
|
|
showModeConfirmDialog.value = true
|
|
} else if (newMode === 'Selection' && layerMappings.value.length > 0) {
|
|
// Switching to Selection mode with existing layer mappings
|
|
pendingMode.value = newMode
|
|
conflictMessage.value = `Switching to Selection assignment mode will clear all current layer category assignments. Continue?`
|
|
showModeConfirmDialog.value = true
|
|
} else {
|
|
// No conflicts, switch directly (no existing mappings or switching to same mode)
|
|
selectedMappingMode.value = newMode
|
|
}
|
|
}
|
|
|
|
// Cancel mode change
|
|
const cancelModeChange = () => {
|
|
showModeConfirmDialog.value = false
|
|
pendingMode.value = ''
|
|
conflictMessage.value = ''
|
|
}
|
|
|
|
// Confirm mode change and clear conflicting mappings
|
|
const confirmModeChange = async () => {
|
|
try {
|
|
if (pendingMode.value === 'Layer') {
|
|
// Clear all object mappings before switching to Layer mode
|
|
await $revitMapperBinding?.clearAllObjectsCategoryAssignments()
|
|
} else if (pendingMode.value === 'Selection') {
|
|
// Clear all layer mappings before switching to Selection mode
|
|
await $revitMapperBinding?.clearAllLayerCategoryAssignments()
|
|
}
|
|
|
|
// Track the manual mode switch
|
|
trackEvent('DUI3 Action', {
|
|
name: 'Mapper Mode Changed',
|
|
mode: selectedMappingMode.value
|
|
})
|
|
|
|
// Switch mode
|
|
selectedMappingMode.value = pendingMode.value
|
|
await refreshMappings()
|
|
|
|
// Close dialog
|
|
showModeConfirmDialog.value = false
|
|
pendingMode.value = ''
|
|
conflictMessage.value = ''
|
|
} catch (error) {
|
|
console.error('Failed to clear category assignments during mode switch:', error)
|
|
}
|
|
}
|
|
|
|
// Assign selected objects/layers to the chosen category
|
|
const assignToCategory = async () => {
|
|
if (!revitMapperStore.selectedCategory?.value || !hasTargetsSelected.value) return
|
|
|
|
try {
|
|
let assignedCount = 0
|
|
const { selectedCategory } = storeToRefs(revitMapperStore)
|
|
const categoryValue = selectedCategory?.value?.value
|
|
|
|
if (selectedMappingMode.value === 'Selection' && categoryValue) {
|
|
const objectIds = selectionInfo.value?.selectedObjectIds || []
|
|
await $revitMapperBinding?.assignObjectsToCategory(objectIds, categoryValue)
|
|
assignedCount = objectIds.length
|
|
|
|
// Track the assignment
|
|
trackEvent('DUI3 Action', {
|
|
name: 'Mapper Assign Category',
|
|
category: categoryValue,
|
|
count: assignedCount,
|
|
mappingType: 'object'
|
|
})
|
|
} else if (selectedMappingMode.value === 'Layer' && categoryValue) {
|
|
const layerIds = selectedLayers.value.map((layer) => layer.id)
|
|
await $revitMapperBinding?.assignLayerToCategory(layerIds, categoryValue)
|
|
assignedCount = selectedLayers.value.length
|
|
|
|
// Track the assignment
|
|
trackEvent('DUI3 Action', {
|
|
name: 'Mapper Assign Category',
|
|
category: categoryValue,
|
|
count: assignedCount,
|
|
mappingType: 'layer'
|
|
})
|
|
|
|
selectedLayers.value = []
|
|
}
|
|
|
|
selectedCategory.value = undefined
|
|
await refreshMappings()
|
|
} catch (error) {
|
|
console.error('Failed to assign to category:', error)
|
|
}
|
|
}
|
|
|
|
// Clear a specific object mapping
|
|
const clearMapping = async (mapping: CategoryMapping) => {
|
|
try {
|
|
await $revitMapperBinding?.clearObjectsCategoryAssignment(mapping.objectIds)
|
|
await refreshMappings()
|
|
} catch (error) {
|
|
console.error('Failed to clear category assignment:', error)
|
|
}
|
|
}
|
|
|
|
// Clear all object mappings
|
|
const clearAllMappings = async () => {
|
|
try {
|
|
await $revitMapperBinding?.clearAllObjectsCategoryAssignments()
|
|
await refreshMappings()
|
|
} catch (error) {
|
|
console.error('Failed to clear all category assignments:', error)
|
|
}
|
|
}
|
|
|
|
// Clear a specific layer mapping
|
|
const clearLayerMapping = async (layerMapping: LayerCategoryMapping) => {
|
|
try {
|
|
await $revitMapperBinding?.clearLayerCategoryAssignment(layerMapping.layerIds)
|
|
await refreshMappings()
|
|
} catch (error) {
|
|
console.error('Failed to clear layer assignment:', error)
|
|
}
|
|
}
|
|
|
|
// Clear all layer mappings
|
|
const clearAllLayerMappings = async () => {
|
|
try {
|
|
await $revitMapperBinding?.clearAllLayerCategoryAssignments()
|
|
await refreshMappings()
|
|
} catch (error) {
|
|
console.error('Failed to clear all layer assignments:', error)
|
|
}
|
|
}
|
|
|
|
// Select mapped objects in Rhino
|
|
const selectMappedObjects = async (mapping: CategoryMapping) => {
|
|
try {
|
|
await $baseBinding?.highlightObjects(mapping.objectIds)
|
|
} catch (error) {
|
|
console.error('Failed to highlight objects:', error)
|
|
}
|
|
}
|
|
|
|
// Select mapped layers (highlight objects AND restore UI state)
|
|
const selectMappedLayers = async (layerMapping: LayerCategoryMapping) => {
|
|
try {
|
|
// 1. Highlight objects in Rhino
|
|
const effectiveObjectIds =
|
|
(await $revitMapperBinding?.getEffectiveObjectsForLayerMapping(
|
|
layerMapping.layerIds,
|
|
layerMapping.categoryValue
|
|
)) || []
|
|
|
|
if (effectiveObjectIds.length > 0) {
|
|
await $baseBinding?.highlightObjects(effectiveObjectIds)
|
|
}
|
|
|
|
// 2. Restore UI state - populate layer selection
|
|
const layersToSelect = layerOptions.value.filter((layer) =>
|
|
layerMapping.layerIds.includes(layer.id)
|
|
)
|
|
selectedLayers.value = layersToSelect
|
|
|
|
// 3. Pre-select category in dropdown
|
|
const categoryToSelect = categoryOptions.value.find(
|
|
(cat) => cat.value === layerMapping.categoryValue
|
|
)
|
|
|
|
const { selectedCategory, currentCategories } = storeToRefs(revitMapperStore)
|
|
|
|
selectedCategory.value = categoryToSelect
|
|
|
|
// 4. Update reactive state
|
|
currentCategories.value = [layerMapping.categoryValue]
|
|
} catch (error) {
|
|
console.error('Failed to highlight effective objects:', error)
|
|
}
|
|
}
|
|
|
|
// Select all mapped objects (Selection mode)
|
|
const selectAllMappedObjects = async () => {
|
|
try {
|
|
const allObjectIds = currentMappings.value.flatMap((mapping) => mapping.objectIds)
|
|
if (allObjectIds.length > 0) {
|
|
await $baseBinding?.highlightObjects(allObjectIds)
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to select all objects with categories assigned:', error)
|
|
}
|
|
}
|
|
|
|
// Select all objects affected by layer mappings (Layer mode)
|
|
const selectAllMappedLayers = async () => {
|
|
try {
|
|
const allEffectiveObjectIds: string[] = []
|
|
|
|
// Get effective objects for each layer mapping
|
|
for (const layerMapping of currentLayerMappings.value) {
|
|
const effectiveObjectIds =
|
|
(await $revitMapperBinding?.getEffectiveObjectsForLayerMapping(
|
|
layerMapping.layerIds,
|
|
layerMapping.categoryValue
|
|
)) || []
|
|
allEffectiveObjectIds.push(...effectiveObjectIds)
|
|
}
|
|
|
|
// Remove duplicates and highlight
|
|
const uniqueObjectIds = [...new Set(allEffectiveObjectIds)]
|
|
if (uniqueObjectIds.length > 0) {
|
|
await $baseBinding?.highlightObjects(uniqueObjectIds)
|
|
}
|
|
} catch (error) {
|
|
console.error(
|
|
'Failed to select all objects with categories assigned by layer:',
|
|
error
|
|
)
|
|
}
|
|
}
|
|
|
|
// Load available categories, layers, and current mappings
|
|
const loadData = async () => {
|
|
try {
|
|
const [categories, rawMappings, rawLayerMappings, layers] = await Promise.all([
|
|
getAvailableCategories() || [],
|
|
$revitMapperBinding?.getCurrentObjectsMappings() || [],
|
|
$revitMapperBinding?.getCurrentLayerMappings() || [],
|
|
loadAvailableLayers()
|
|
])
|
|
|
|
categoryOptions.value = categories
|
|
|
|
// Transform mappings to include human-readable labels
|
|
mappings.value = rawMappings.map((mapping) => ({
|
|
...mapping,
|
|
categoryLabel: getCategoryLabel(mapping.categoryValue)
|
|
}))
|
|
|
|
layerMappings.value = rawLayerMappings.map((mapping) => ({
|
|
...mapping,
|
|
categoryLabel: getCategoryLabel(mapping.categoryValue)
|
|
}))
|
|
|
|
layerOptions.value = layers
|
|
|
|
// IMPORTANT: Determine initial mapping mode based on existing mappings
|
|
// This preserves the user's last used mode and prevents mixed state scenarios
|
|
if (!selectedMappingMode.value) {
|
|
if (rawLayerMappings.length > 0 && rawMappings.length === 0) {
|
|
// Only layer mappings exist - user was in Layer mode
|
|
selectedMappingMode.value = 'Layer'
|
|
} else if (rawMappings.length > 0 && rawLayerMappings.length === 0) {
|
|
// Only object mappings exist - user was in Selection mode
|
|
selectedMappingMode.value = 'Selection'
|
|
} else if (rawLayerMappings.length > 0 && rawMappings.length > 0) {
|
|
// Mixed state detected - this shouldn't happen, but default to Selection
|
|
// and let the conflict handling take care of it
|
|
selectedMappingMode.value = 'Selection'
|
|
console.warn(
|
|
'Mixed assignment state detected - both object and layer assignments exist'
|
|
)
|
|
} else {
|
|
// No existing mappings - default to Selection mode
|
|
selectedMappingMode.value = 'Selection'
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load categorizer data:', error)
|
|
// Fallback to Selection mode if loading fails
|
|
if (!selectedMappingMode.value) {
|
|
selectedMappingMode.value = 'Selection'
|
|
}
|
|
}
|
|
}
|
|
|
|
// Refresh both object and layer mappings
|
|
const refreshMappings = async () => {
|
|
try {
|
|
if (!$revitMapperBinding) {
|
|
console.warn('No Revit category assignment binding available')
|
|
return
|
|
}
|
|
|
|
const [rawMappings, rawLayerMappings] = await Promise.all([
|
|
$revitMapperBinding.getCurrentObjectsMappings(),
|
|
$revitMapperBinding.getCurrentLayerMappings()
|
|
])
|
|
|
|
// Transform to resolve labels
|
|
mappings.value = rawMappings.map((mapping) => ({
|
|
...mapping,
|
|
categoryLabel: getCategoryLabel(mapping.categoryValue)
|
|
}))
|
|
|
|
layerMappings.value = rawLayerMappings.map((mapping) => ({
|
|
...mapping,
|
|
categoryLabel: getCategoryLabel(mapping.categoryValue)
|
|
}))
|
|
} catch (error) {
|
|
console.error('Failed to refresh category assignments:', error)
|
|
}
|
|
}
|
|
|
|
// Load available layers from Rhino document
|
|
const loadAvailableLayers = async (): Promise<LayerOption[]> => {
|
|
try {
|
|
// Call the backend method to get available layers
|
|
const layers = (await $revitMapperBinding?.getAvailableLayers()) || []
|
|
return layers
|
|
} catch (error) {
|
|
console.error('Failed to load layers:', error)
|
|
return []
|
|
}
|
|
}
|
|
|
|
// === WATCHER ===
|
|
// Main watcher
|
|
watch(
|
|
() => ({
|
|
mode: selectedMappingMode.value,
|
|
objectIds: selectionInfo.value?.selectedObjectIds || [],
|
|
layerIds: selectedLayers.value.map((l) => l.id)
|
|
}),
|
|
async ({ mode, objectIds, layerIds }) => {
|
|
if (mode === 'Selection') {
|
|
await revitMapperStore.updateFromTargets(objectIds, false)
|
|
} else if (mode === 'Layer') {
|
|
// In Layer mode, we need to watch both manual layer selection AND object selection
|
|
// This keeps dropdowns clear when objects are deselected (like Selection mode)
|
|
// while still supporting manual layer selection
|
|
if (layerIds.length > 0) {
|
|
// User has manually selected layers in UI - use layer mode logic
|
|
await revitMapperStore.updateFromTargets(layerIds, true)
|
|
} else {
|
|
// No manual layer selection - use object mode logic (like Selection mode)
|
|
// This handles the case where selectMappedLayers populated the UI but objects were deselected
|
|
await revitMapperStore.updateFromTargets(objectIds, false)
|
|
}
|
|
}
|
|
},
|
|
{ immediate: true, deep: true }
|
|
)
|
|
|
|
// This handles clearing selectedLayers when objects are deselected in Layer mode
|
|
watch(
|
|
() => selectionInfo.value?.selectedObjectIds?.length || 0,
|
|
async (newCount, oldCount) => {
|
|
// Only act in Layer mode when selection count goes to 0 and we have selected layers
|
|
if (
|
|
selectedMappingMode.value === 'Layer' &&
|
|
newCount === 0 &&
|
|
oldCount > 0 &&
|
|
selectedLayers.value.length > 0
|
|
) {
|
|
// nextTick to avoid interfering with the main watcher? not nice :(
|
|
await nextTick()
|
|
selectedLayers.value = []
|
|
}
|
|
}
|
|
)
|
|
|
|
// === LIFECYCLE ===
|
|
onMounted(async () => {
|
|
await selectionStore.refreshSelectionFromHostApp()
|
|
|
|
await loadData()
|
|
|
|
// Listen for mappings changes
|
|
$revitMapperBinding?.on('mappingsChanged', (newMappings: CategoryMapping[]) => {
|
|
mappings.value = newMappings.map((mapping) => ({
|
|
...mapping,
|
|
categoryLabel: getCategoryLabel(mapping.categoryValue)
|
|
}))
|
|
refreshLayerMappings()
|
|
})
|
|
|
|
// Listen for layer list changes
|
|
$revitMapperBinding?.on('layersChanged', (newLayers: LayerOption[]) => {
|
|
layerOptions.value = newLayers
|
|
selectedLayers.value = []
|
|
})
|
|
|
|
// Track mapper opened with the initial mode (after loadData determines it)
|
|
trackEvent('DUI3 Action', {
|
|
name: 'Mapper Opened',
|
|
mode: selectedMappingMode.value
|
|
})
|
|
})
|
|
|
|
// Refresh just layer mappings
|
|
const refreshLayerMappings = async () => {
|
|
try {
|
|
const rawLayerMappings =
|
|
(await $revitMapperBinding?.getCurrentLayerMappings()) || []
|
|
|
|
layerMappings.value = rawLayerMappings.map((mapping) => ({
|
|
...mapping,
|
|
categoryLabel: getCategoryLabel(mapping.categoryValue)
|
|
}))
|
|
} catch (error) {
|
|
console.error('Failed to refresh layer category assignments:', error)
|
|
}
|
|
}
|
|
</script>
|