Enhance ObjectSelect Component with Improved Value Handling and Synchronization

- Introduced utility functions to standardize value conversion and retrieval, improving consistency in value handling.
- Enhanced synchronization of external values with internal state, ensuring accurate representation of selected items.
- Improved selection change handling to prevent unnecessary updates when no valid selection is made.
- Refactored tree selection logic for better clarity and maintainability, ensuring robust handling of both single and multiple selections.
This commit is contained in:
Tom Butcher 2026-09-06 19:24:17 +01:00
parent a1697fe0fd
commit 8bb8d417f7

View File

@ -25,6 +25,14 @@ const areValuesEqual = (v1, v2) => {
return String(id1) === String(id2)
}
const toSelectValue = (id) => {
if (id == null || id === '') return null
return String(id).toLowerCase()
}
const getValueId = (item) =>
item && typeof item === 'object' ? item._id : item
const getFirstSelectableLeaf = (nodes) => {
if (!Array.isArray(nodes)) return null
for (const node of nodes) {
@ -69,6 +77,23 @@ const isValueInTree = (nodes, id) => {
return findTreeNodeByValue(nodes, id) != null
}
const getSelectValueFromExternal = (externalValue, multiple, nodes) => {
if (externalValue == null || !Array.isArray(nodes) || nodes.length === 0) {
return undefined
}
if (multiple) {
const values = Array.isArray(externalValue) ? externalValue : []
const ids = values
.map((item) => toSelectValue(getValueId(item)))
.filter((id) => id != null && isValueInTree(nodes, id))
return ids
}
const node = findTreeNodeByValue(nodes, getValueId(externalValue))
return node?.value
}
const isExternalValueMissing = (
externalValue,
multiple,
@ -528,9 +553,27 @@ const ObjectSelect = ({
setObjectList(objects)
setTreeData(treeNodes)
treeDataRef.current = treeNodes
const syncedValue = getSelectValueFromExternal(
valueRef.current,
multiple,
treeNodes
)
if (multiple) {
if (Array.isArray(syncedValue) && syncedValue.length > 0) {
setTreeSelectValue(syncedValue)
setValueNotFound(false)
clearedMissingValueRef.current = false
}
} else if (syncedValue != null) {
setTreeSelectValue(syncedValue)
setValueNotFound(false)
clearedMissingValueRef.current = false
}
return { treeNodes, objects }
},
[buildTreeData]
[buildTreeData, multiple]
)
const buildFilterFromNode = useCallback(
@ -633,6 +676,17 @@ const ObjectSelect = ({
const onTreeSelectChange = useCallback(
(value) => {
const isEmptySelection = multiple
? !Array.isArray(value) || value.length === 0
: value == null || value === ''
if (
isEmptySelection &&
treeDataRef.current.length === 0 &&
valueRef.current != null
) {
return
}
setValueNotFound(false)
clearedMissingValueRef.current = false
// Mark this as an internal change
@ -827,7 +881,7 @@ const ObjectSelect = ({
setExpandedKeys([...new Set(pathKeys)])
setTreeSelectValue(
value
.map((item) => (item && typeof item === 'object' ? item._id : item))
.map((item) => toSelectValue(getValueId(item)))
.filter((id) => id != null)
)
setInitialized(true)
@ -880,7 +934,7 @@ const ObjectSelect = ({
setExpandedKeys(pathKeys)
// Fetch with the new filter
handleFetchObjectsProperties(valueFilter)
setTreeSelectValue(valueRef.current._id)
setTreeSelectValue(toSelectValue(valueRef.current._id))
setInitialized(true)
return
}
@ -905,7 +959,13 @@ const ObjectSelect = ({
setInitialized(true)
}
}
handleValue()
const timeoutId = setTimeout(() => {
handleValue()
}, 10)
return () => {
clearTimeout(timeoutId)
}
}, [
value,
filter,
@ -940,6 +1000,7 @@ const ObjectSelect = ({
setTreeVersion((v) => v + 1)
setExpandedKeys([])
setInitialized(false)
valueRef.current = null
onTreeSelectChange(null)
setTreeSelectValue(null)
setInitialLoading(true)
@ -963,7 +1024,12 @@ const ObjectSelect = ({
if (changeSource == 'external') {
setObjectPropertiesTree({})
setTreeData([])
treeDataRef.current = []
setInitialized(false)
setInitialLoading(true)
valueRef.current = null
clearedMissingValueRef.current = false
setValueNotFound(false)
prevValuesRef.current = { type, masterFilter }
}