Files
speckle-server/packages/frontend-2/lib/viewer/composables/setup/selection.ts
T
Kristaps Fabians Geikins f074be89aa fix(fe2): error msg templating + Filters undefined error fix (#2411)
* fix(fe2): error msg templating + Filters undefined error fix

* fixx

* fixx
2024-06-20 13:11:05 +03:00

124 lines
4.3 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { MeasurementType } from '@speckle/viewer'
import type { SpeckleObject } from '~/lib/viewer/helpers/sceneExplorer'
import { useMixpanel } from '~~/lib/core/composables/mp'
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
import { useCameraUtilities, useSelectionUtilities } from '~~/lib/viewer/composables/ui'
import { useSelectionEvents } from '~~/lib/viewer/composables/viewer'
function useCollectSelection() {
const {
ui: { selection }
} = useInjectedViewerState()
const selectionCallback: Parameters<
typeof useSelectionEvents
>[0]['singleClickCallback'] = (_event, { firstVisibleSelectionHit }) => {
if (!firstVisibleSelectionHit) return (selection.value = null) // reset selection location
selection.value = firstVisibleSelectionHit.point
}
useSelectionEvents({
singleClickCallback: selectionCallback,
doubleClickCallback: selectionCallback
})
}
function useSelectOrZoomOnSelection() {
const state = useInjectedViewerState()
const { clearSelection, addToSelection } = useSelectionUtilities()
const { zoom } = useCameraUtilities()
const mp = useMixpanel()
const logger = useLogger()
const trackAndClearSelection = () => {
clearSelection()
mp.track('Viewer Action', {
type: 'action',
name: 'selection',
action: 'clear',
source: 'viewer'
})
}
useSelectionEvents(
{
singleClickCallback: (args, { firstVisibleSelectionHit }) => {
if (!args) return trackAndClearSelection()
if (args.hits.length === 0) return trackAndClearSelection()
if (!args.multiple) clearSelection() // note we're not tracking selectino clearing here
if (!firstVisibleSelectionHit) return clearSelection()
addToSelection(firstVisibleSelectionHit.node.model.raw as SpeckleObject)
// Expands default viewer selection behaviour with a special case in diff mode.
// In diff mode, if we select via a mouse click an object, and that object is
// "modified", we want to select its pair as well.
if (
state.ui.diff.enabled.value &&
state.ui.diff.result.value &&
firstVisibleSelectionHit.node.model.raw.applicationId
) {
const modifiedObjectPairs = state.ui.diff.result.value.modified
const obj = firstVisibleSelectionHit.node.model.raw as SpeckleObject
const pairedItems = modifiedObjectPairs.find((item) => {
if (
(item[0].model.raw as SpeckleObject).id === obj.id ||
(item[1].model.raw as SpeckleObject).id === obj.id
) {
return true
}
})
if (!pairedItems) return
const pair =
(pairedItems[0].model.raw as SpeckleObject).id === obj.id
? (pairedItems[1].model.raw as SpeckleObject)
: (pairedItems[0].model.raw as SpeckleObject)
if (!pair) return
addToSelection(pair)
}
mp.track('Viewer Action', {
type: 'action',
name: 'selection',
action: 'select',
multiple: args.multiple
})
},
doubleClickCallback: (args, { firstVisibleSelectionHit }) => {
const isMeasureMode = state.ui.measurement.enabled.value
const measurementType = state.ui.measurement.options.value.type
if (isMeasureMode && measurementType === MeasurementType.PERPENDICULAR) {
return
}
if (!args) return zoom()
if (!args.hits) return zoom()
if (args.hits.length === 0) return zoom()
const firstVisHit = firstVisibleSelectionHit
if (!firstVisHit) return clearSelection()
if (state.ui.filters.selectedObjects.value.length !== 0) {
const ids = state.ui.filters.selectedObjects.value.map((o) => o.id as string)
zoom(ids)
} // else somethingn is weird.
else {
logger.warn(
"Got a double click event but there's no selected object in the state - this should be impossible :)"
)
}
mp.track('Viewer Action', {
type: 'action',
name: 'zoom',
source: 'object-double-click'
})
}
},
{ state }
)
}
export function useViewerSelectionEventHandler() {
useCollectSelection()
useSelectOrZoomOnSelection()
}