Fix types

This commit is contained in:
andrewwallacespeckle
2025-09-12 13:17:57 +01:00
parent 4f938cc4ab
commit eb2fe4f789
6 changed files with 80 additions and 55 deletions
@@ -87,9 +87,11 @@
<script setup lang="ts">
import { useInjectedViewerInterfaceState } from '~~/lib/viewer/composables/setup'
import type { PropertySelectOption } from '~/lib/viewer/helpers/filters/types'
import type {
PropertySelectOption,
ExtendedPropertyInfo
} from '~/lib/viewer/helpers/filters/types'
import { FilterType } from '~/lib/viewer/helpers/filters/types'
import type { PropertyInfo } from '@speckle/viewer'
import { useMixpanel } from '~~/lib/core/composables/mp'
import { X, Plus } from 'lucide-vue-next'
import { FormButton } from '@speckle/ui-components'
@@ -120,7 +122,8 @@ const filtersContainerRef = ref<HTMLElement>()
const shouldScrollToNewFilter = ref(false)
const showLargePropertyWarning = ref(false)
const pendingProperty = ref<Nullable<{ property: PropertyInfo; count: number }>>(null)
const pendingProperty =
ref<Nullable<{ property: ExtendedPropertyInfo; count: number }>>(null)
const propertySelectOptions = computed((): PropertySelectOption[] => {
if (!showPropertySelection.value) {
@@ -234,7 +237,10 @@ const selectProperty = async (propertyKey: string) => {
processPropertySelection(property, propertyKey)
}
const processPropertySelection = (property: PropertyInfo, propertyKey: string) => {
const processPropertySelection = (
property: ExtendedPropertyInfo,
propertyKey: string
) => {
if (swappingFilterId.value) {
updateFilterProperty(swappingFilterId.value, property)
mp.track('Viewer Action', {
@@ -76,8 +76,10 @@ import { useFilterUtilities } from '~/lib/viewer/composables/filtering/filtering
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
import type { KeyValuePair } from '~/components/viewer/selection/types'
import { isNumericPropertyInfo } from '~/lib/viewer/helpers/sceneExplorer'
import type { PropertyInfo } from '@speckle/viewer'
import { BooleanFilterCondition } from '~/lib/viewer/helpers/filters/types'
import {
BooleanFilterCondition,
type ExtendedPropertyInfo
} from '~/lib/viewer/helpers/filters/types'
import { isBooleanProperty } from '~/lib/viewer/helpers/filters/utils'
const props = defineProps<{
@@ -103,13 +105,13 @@ const {
} = useInjectedViewerState()
const availableFilters = computed(
() => getPropertyOptionsFromDataStore() as PropertyInfo[]
() => getPropertyOptionsFromDataStore() as ExtendedPropertyInfo[]
)
const showActionsMenu = ref(false)
const showLargePropertyWarning = ref(false)
const pendingFilter = ref<PropertyInfo | null>(null)
const pendingFilter = ref<ExtendedPropertyInfo | null>(null)
const pendingFilterCount = ref(0)
const isUrlString = (v: unknown) => typeof v === 'string' && VALID_HTTP_URL.test(v)
@@ -152,7 +154,7 @@ const handleAddToFilters = (kvp: KeyValuePair) => {
}
}
const addFilterWithValue = (filter: PropertyInfo, kvp: KeyValuePair) => {
const addFilterWithValue = (filter: ExtendedPropertyInfo, kvp: KeyValuePair) => {
const filterId = addActiveFilter(filter)
if (isNumericPropertyInfo(filter)) {
@@ -1,8 +1,4 @@
import type {
PropertyInfo,
NumericPropertyInfo,
StringPropertyInfo
} from '@speckle/viewer'
import type { NumericPropertyInfo, StringPropertyInfo } from '@speckle/viewer'
import { difference, uniq, partition } from 'lodash-es'
import { whenever } from '@vueuse/core'
import {
@@ -28,7 +24,8 @@ import {
BooleanFilterCondition,
SortMode,
type DataSlice,
type QueryCriteria
type QueryCriteria,
type ExtendedPropertyInfo
} from '~/lib/viewer/helpers/filters/types'
import { getConditionLabel } from '~/lib/viewer/helpers/filters/constants'
import { useFilteringDataStore } from '~/lib/viewer/composables/filtering/dataStore'
@@ -128,11 +125,16 @@ export function useFilterUtilities(
/**
* Gets available values for the current property filter (used for UI display)
*/
const getAvailableFilterValues = (filter: PropertyInfo, limit?: number): string[] => {
const getAvailableFilterValues = (
filter: ExtendedPropertyInfo,
limit?: number
): string[] => {
// Type guard to check if filter has valueGroups property
const hasValueGroups = (
f: PropertyInfo
): f is PropertyInfo & { valueGroups: Array<{ value: string | number }> } => {
f: ExtendedPropertyInfo
): f is ExtendedPropertyInfo & {
valueGroups: Array<{ value: string | number }>
} => {
return (
'valueGroups' in f && Array.isArray((f as Record<string, unknown>).valueGroups)
)
@@ -187,7 +189,7 @@ export function useFilterUtilities(
*/
const computeFullPropertyData = (
propertyKey: string
): PropertyInfo | BooleanPropertyInfo | null => {
): ExtendedPropertyInfo | null => {
const valueToObjectIds = new Map<string, string[]>()
for (const dataSource of dataStore.dataSources.value) {
@@ -273,7 +275,7 @@ export function useFilterUtilities(
}
const isBooleanPropertyInfo = (
prop: PropertyInfo | BooleanPropertyInfo
prop: ExtendedPropertyInfo
): prop is BooleanPropertyInfo => {
return prop.type === 'boolean'
}
@@ -345,7 +347,7 @@ export function useFilterUtilities(
}
}
const addActiveFilter = (filter: PropertyInfo): string => {
const addActiveFilter = (filter: ExtendedPropertyInfo): string => {
const existingIndex = filters.propertyFilters.value.findIndex(
(f) => f.filter?.key === filter.key
)
@@ -368,7 +370,7 @@ export function useFilterUtilities(
*/
const updateFilterProperty = (
filterId: string,
newProperty: PropertyInfo
newProperty: ExtendedPropertyInfo
): boolean => {
const filterIndex = filters.propertyFilters.value.findIndex(
(f) => f.id === filterId
@@ -708,7 +710,7 @@ export function useFilterUtilities(
resetFilters() // Clear existing filters first
const availableProperties = getPropertyOptionsFromDataStore() as PropertyInfo[]
const availableProperties = getPropertyOptionsFromDataStore()
// If data store is ready, restore immediately
if (availableProperties.length > 0) {
@@ -730,7 +732,7 @@ export function useFilterUtilities(
id: string
condition: 'AND' | 'OR'
}>,
availableProperties: PropertyInfo[]
availableProperties: ExtendedPropertyInfo[]
) => {
for (const serializedFilter of serializedFilters) {
if (serializedFilter.key) {
@@ -756,9 +758,9 @@ export function useFilterUtilities(
* Filters the available filters to only include relevant ones for the filter UI
*/
const getRelevantFilters = (
allFilters: PropertyInfo[] | null | undefined
): PropertyInfo[] => {
return (allFilters || []).filter((f: PropertyInfo) => {
allFilters: ExtendedPropertyInfo[] | null | undefined
): ExtendedPropertyInfo[] => {
return (allFilters || []).filter((f: ExtendedPropertyInfo) => {
if (shouldExcludeFromFiltering(f.key)) {
return false
}
@@ -770,10 +772,10 @@ export function useFilterUtilities(
* Gets property options from the data store (optimized to use propertyMap)
*/
const getPropertyOptionsFromDataStore = (): (
| PropertyInfo
| ExtendedPropertyInfo
| BooleanPropertyInfo
)[] => {
const allProperties = new Map<string, PropertyInfo | BooleanPropertyInfo>()
const allProperties = new Map<string, ExtendedPropertyInfo | BooleanPropertyInfo>()
for (const dataSource of dataStore.dataSources.value) {
const propertyKeys = Object.keys(dataSource.propertyMap)
@@ -825,7 +827,7 @@ export function useFilterUtilities(
* Gets filtered and sorted values for a string filter with search and sorting options
*/
const getFilteredFilterValues = (
filter: PropertyInfo,
filter: ExtendedPropertyInfo,
options?: {
searchQuery?: string
sortMode?: SortMode
@@ -866,7 +868,8 @@ export function useFilterUtilities(
whenever(shouldRestoreFilters, () => {
if (pendingFiltersToRestore.value) {
const availableProperties = getPropertyOptionsFromDataStore() as PropertyInfo[]
const availableProperties =
getPropertyOptionsFromDataStore() as ExtendedPropertyInfo[]
applyFiltersFromSerialized(pendingFiltersToRestore.value, availableProperties)
pendingFiltersToRestore.value = null
}
@@ -77,10 +77,16 @@ export type StringFilterData = BaseFilterData & {
isDefaultAllSelected?: boolean
}
export type BooleanPropertyInfo = Omit<PropertyInfo, 'type'> & {
type: 'boolean'
valueGroups: { value: boolean; ids: string[] }[]
}
export type ExtendedPropertyInfo =
| PropertyInfo
| {
key: string
objectCount: number
type: 'boolean'
valueGroups: { value: boolean; ids: string[] }[]
}
export type BooleanPropertyInfo = Extract<ExtendedPropertyInfo, { type: 'boolean' }>
export type BooleanFilterData = BaseFilterData & {
type: FilterType.Boolean
@@ -128,7 +134,7 @@ export type PropertySelectionListItem = {
}
export type CreateFilterParams = {
filter: PropertyInfo
filter: ExtendedPropertyInfo
id: string
availableValues?: string[]
}
@@ -1,6 +1,9 @@
import type { PropertyInfo } from '@speckle/viewer'
import { isStringPropertyInfo } from '~/lib/viewer/helpers/sceneExplorer'
import { ExistenceFilterCondition } from './types'
import {
ExistenceFilterCondition,
type BooleanPropertyInfo,
type ExtendedPropertyInfo
} from '~/lib/viewer/helpers/filters/types'
export const revitPropertyRegex = /^parameters\./
export const revitPropertyRegexDui3000InstanceProps = /^properties\.Instance/
@@ -59,7 +62,7 @@ export const shouldExcludeFromFiltering = (key: string): boolean => {
*/
export const getPropertyName = (
key: string,
availableFilters?: PropertyInfo[] | null
availableFilters?: ExtendedPropertyInfo[] | null
): string => {
if (!key) return 'Loading'
@@ -68,7 +71,7 @@ export const getPropertyName = (
if (isRevitProperty(key) && key.endsWith('.value')) {
const correspondingProperty = (availableFilters || []).find(
(f: PropertyInfo) => f.key === key.replace('.value', '.name')
(f: ExtendedPropertyInfo) => f.key === key.replace('.value', '.name')
)
if (correspondingProperty && isStringPropertyInfo(correspondingProperty)) {
return correspondingProperty.valueGroups[0]?.value || key.split('.').pop() || key
@@ -83,8 +86,8 @@ export const getPropertyName = (
*/
export const findFilterByDisplayName = (
displayKey: string,
availableFilters: PropertyInfo[] | null | undefined
): PropertyInfo | undefined => {
availableFilters: ExtendedPropertyInfo[] | null | undefined
): ExtendedPropertyInfo | undefined => {
if (!availableFilters) return undefined
// First, try to find an exact display name match
@@ -107,7 +110,7 @@ export const findFilterByDisplayName = (
*/
export const isKvpFilterable = (
kvp: { key: string; backendPath?: string },
availableFilters: PropertyInfo[] | null | undefined
availableFilters: ExtendedPropertyInfo[] | null | undefined
): boolean => {
// Use backendPath for legacy compatibility, but prefer the direct key
const propertyKey = kvp.backendPath || kvp.key
@@ -132,7 +135,7 @@ export const isKvpFilterable = (
*/
export const getFilterDisabledReason = (
kvp: { key: string; backendPath?: string },
availableFilters: PropertyInfo[] | null | undefined
availableFilters: ExtendedPropertyInfo[] | null | undefined
): string => {
const availableKeys = availableFilters?.map((f) => f.key) || []
@@ -187,20 +190,22 @@ export const getFilterDisabledReason = (
*/
export const findFilterByKvp = (
kvp: { key: string; backendPath?: string },
availableFilters: PropertyInfo[] | null | undefined
): PropertyInfo | undefined => {
availableFilters: ExtendedPropertyInfo[] | null | undefined
): ExtendedPropertyInfo | undefined => {
if (!availableFilters) return undefined
if (kvp.backendPath) {
const exactMatch = availableFilters.find(
(f: PropertyInfo) => f.key === kvp.backendPath
(f: ExtendedPropertyInfo) => f.key === kvp.backendPath
)
if (exactMatch) {
return exactMatch
}
}
const directMatch = availableFilters.find((f: PropertyInfo) => f.key === kvp.key)
const directMatch = availableFilters.find(
(f: ExtendedPropertyInfo) => f.key === kvp.key
)
if (directMatch) {
return directMatch
}
@@ -208,7 +213,7 @@ export const findFilterByKvp = (
// If we have a backendPath but no exact match, try partial matching
if (kvp.backendPath) {
const pathParts = kvp.backendPath.split('.')
const partialMatches = availableFilters.filter((f: PropertyInfo) => {
const partialMatches = availableFilters.filter((f: ExtendedPropertyInfo) => {
const filterParts = f.key.split('.')
if (pathParts.length === 1) {
@@ -241,14 +246,17 @@ export const findFilterByKvp = (
return findFilterByDisplayName(displayKey, availableFilters)
}
export const isBooleanProperty = (filter: PropertyInfo): boolean => {
export const isBooleanProperty = (filter: ExtendedPropertyInfo): boolean => {
return 'type' in filter && (filter as { type: string }).type === 'boolean'
}
/**
* Get count for a specific filter value
*/
export function getFilterValueCount(filter: PropertyInfo, value: string): number {
export function getFilterValueCount(
filter: ExtendedPropertyInfo,
value: string
): number {
if (!('valueGroups' in filter) || !Array.isArray(filter.valueGroups)) {
return 0
}
@@ -268,7 +276,7 @@ export function getFilterValueCount(filter: PropertyInfo, value: string): number
* Get count for existence filters (objects that have/don't have a property set)
*/
export function getExistenceFilterCount(
filter: PropertyInfo,
filter: ExtendedPropertyInfo | BooleanPropertyInfo,
condition: ExistenceFilterCondition,
totalObjectCount?: number
): number {
@@ -1,18 +1,18 @@
import type { MaybeNullOrUndefined } from '@speckle/shared'
import type {
NumericPropertyInfo,
PropertyInfo,
SpeckleObject,
SpeckleReference,
StringPropertyInfo
} from '@speckle/viewer'
import type { Raw } from 'vue'
import type { ExtendedPropertyInfo } from '~/lib/viewer/helpers/filters/types'
export const isStringPropertyInfo = (
info: MaybeNullOrUndefined<PropertyInfo>
info: MaybeNullOrUndefined<ExtendedPropertyInfo>
): info is StringPropertyInfo => info?.type === 'string'
export const isNumericPropertyInfo = (
info: MaybeNullOrUndefined<PropertyInfo>
info: MaybeNullOrUndefined<ExtendedPropertyInfo>
): info is NumericPropertyInfo => info?.type === 'number'
export type ExplorerNode = {