diff --git a/components/model-info.tsx b/components/model-info.tsx index cc0aab5..3b33258 100644 --- a/components/model-info.tsx +++ b/components/model-info.tsx @@ -203,6 +203,7 @@ interface PropertyRowProps { icon?: React.ReactNode; copyValue?: string; t?: (key: string, options?: any) => string; + propertyPath?: string; } const PropertyRow: React.FC = ({ @@ -211,11 +212,17 @@ const PropertyRow: React.FC = ({ icon, copyValue, t, + propertyPath, }) => { - const { ifcApi } = useIFCContext(); + const { ifcApi, selectAllByProperty } = useIFCContext(); const handleCopy = () => { if (copyValue !== undefined) navigator.clipboard.writeText(copyValue); }; + const handleSelectAll = () => { + if (propertyPath) { + selectAllByProperty(propertyPath, propValue); + } + }; return (
@@ -233,6 +240,23 @@ const PropertyRow: React.FC = ({ } > {renderPropertyValue(propValue, propKey, ifcApi, t)} + {propertyPath && ( + + + + + + + {t?.('selectAllWithValue') || 'Select all with this value'} + + + + )} {copyValue !== undefined && (
-
+
{ifcType || 'Unknown'} + {ifcType && ( + + + + + + + {t('selectAllWithValue') || 'Select all with this value'} + + + + )}
@@ -654,6 +699,7 @@ export function ModelInfo() { propValue={rawAttributes.Name.value || rawAttributes.Name} icon={} t={t} + propertyPath="attributes.Name" /> )} {rawAttributes.Description && ( @@ -664,6 +710,7 @@ export function ModelInfo() { } icon={} t={t} + propertyPath="attributes.Description" /> )} {rawAttributes.ObjectType && ( @@ -674,6 +721,7 @@ export function ModelInfo() { } icon={} t={t} + propertyPath="attributes.ObjectType" /> )} } copyValue={rawAttributes.GlobalId.value || rawAttributes.GlobalId} t={t} + propertyPath="attributes.GlobalId" /> )} {Object.entries(displayableAttributes).map(([key, value]) => ( @@ -704,6 +753,7 @@ export function ModelInfo() { propValue={value} icon={getPropertyIcon(key)} t={t} + propertyPath={`attributes.${key}`} /> ))} @@ -786,6 +836,7 @@ export function ModelInfo() { propValue={propValue} icon={getPropertyIcon(propName)} t={t} + propertyPath={`propertySets.${psetName}.${propName}`} /> ), ) @@ -821,6 +872,7 @@ export function ModelInfo() { propValue={propValue} icon={getPropertyIcon(propName)} t={t} + propertyPath={`typeSets.${psetName}.${propName}`} /> ))} diff --git a/context/ifc-context.tsx b/context/ifc-context.tsx index 38fb8ee..976179e 100644 --- a/context/ifc-context.tsx +++ b/context/ifc-context.tsx @@ -143,6 +143,7 @@ interface IFCContextType { modelID: number, expressID: number, ) => Promise; + selectAllByProperty: (path: string, value: any) => Promise; toggleShowAllClassificationColors: () => void; // Added new toggle function // Classification and Rule methods (can remain global or be refactored later if needed per model) @@ -1716,6 +1717,51 @@ export function IFCContextProvider({ children }: { children: ReactNode }) { [ifcApiInternal], ); + const selectAllByProperty = useCallback( + async (path: string, value: any) => { + if (!ifcApiInternal) return; + const matches: SelectedElementInfo[] = []; + for (const model of loadedModels) { + if (model.modelID == null) continue; + const elements = IFCElementExtractor.getAllElements(ifcApiInternal, model.modelID); + const propsMap = await PropertyCache.getBatchProperties( + ifcApiInternal, + model.modelID, + elements.map((e) => e.expressID), + ); + for (const el of elements) { + let elementVal: any; + if (path === 'ifcType') { + elementVal = el.type; + } else { + const props = propsMap.get(el.expressID); + if (!props) continue; + const parts = path.split('.'); + if (parts[0] === 'attributes') { + elementVal = props.attributes?.[parts[1]]; + } else if (parts[0] === 'propertySets') { + elementVal = props.propertySets?.[parts[1]]?.[parts[2]]; + } else if (parts[0] === 'typeSets') { + elementVal = props.typeSets?.[parts[1]]?.[parts[2]]; + } else if (parts[0] === 'materialSets') { + elementVal = props.materialSets?.[parts[1]]?.[parts[2]]; + } + } + if ( + elementVal !== undefined && + JSON.stringify(elementVal) === JSON.stringify(value) + ) { + matches.push({ modelID: model.modelID, expressID: el.expressID }); + } + } + } + if (matches.length) { + selectElements(matches); + } + }, + [ifcApiInternal, loadedModels, selectElements], + ); + const showElements = useCallback((elements: SelectedElementInfo[]) => { setUserHiddenElements((prev) => prev.filter( @@ -2304,6 +2350,7 @@ export function IFCContextProvider({ children }: { children: ReactNode }) { setAvailableProperties, setIfcApi, getElementPropertiesCached, + selectAllByProperty, toggleShowAllClassificationColors, toggleIsolateUnclassified, baseCoordinationMatrix,