Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/loose-worms-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@itwin/changed-elements-react": patch
---

Changed elements gets hidden when toggling filters
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ pnpm-debug.log*
.env.*

packages/test-app-frontend/dist/
.DS_Store
*.DS_Store
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,12 @@ export function NamedVersionSelectorWidget(props: Readonly<NamedVersionSelectorW
throw new Error("V2 Client is not initialized in given context.");
}

const iTwinId = iModel.iTwinId as string;
const iModelId = iModel.iModelId as string;
if (!iModel.iTwinId || !iModel.iModelId || !iModel.changeset.id) {
throw new Error("Empty IModel Connection")
}

const iTwinId = iModel.iTwinId;
const iModelId = iModel.iModelId;
const currentChangesetId = iModel.changeset.id;

const { isLoading, currentNamedVersion, entries, updateJobStatus } = useNamedVersionsList({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export interface ModelElementChanges {
typeOfChange: number;
}

export interface EntryPartition {
visible: ChangedElementEntry[];
hidden: ChangedElementEntry[];
}

export function isModelElementChanges(input: unknown): input is ModelElementChanges {
return !!input
&& typeof input === "object"
Expand Down Expand Up @@ -1077,19 +1082,30 @@ export class ChangesTreeDataProvider implements ITreeDataProvider {
return entries;
}

/** Get entries with model ids based on a filter function. */
public getEntriesWithModelIds(
/** Get entries with model ids based on a filter function.
* @param includeFilter Function to filter entries that should be visible
* @param hideFilter Function to filter entries that should be hidden
*/
public GetEntriesByModelIdsAndFilters(
modelIds: Set<string>,
filterFunc: (entry: ChangedElementEntry) => boolean,
): ChangedElementEntry[] {
const entries = [];
for (const pair of this._elements) {
if (pair[1].modelId !== undefined && modelIds.has(pair[1].modelId) && filterFunc(pair[1])) {
entries.push(pair[1]);
includeFilter: (entry: ChangedElementEntry) => boolean,
hideFilter: (entry: ChangedElementEntry) => boolean,
): EntryPartition {
const visible: ChangedElementEntry[] = [];
const hidden: ChangedElementEntry[] = [];

for (const [, entry] of this._elements) {
if (!entry.modelId || !modelIds.has(entry.modelId)) continue;

if (hideFilter(entry)){
hidden.push(entry);
}
else if (includeFilter(entry)) {
visible.push(entry);
}
}

return entries;
return { visible, hidden };
}

public getModelAllChildElementEntries(modelId: string): ChangedElementEntry[] {
Expand Down
48 changes: 35 additions & 13 deletions packages/changed-elements-react/src/api/VersionCompareTiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ export class Provider
private _treeRef2d: Reference | undefined;
public readonly iModel: IModelConnection;
public secondaryIModelOverrides: FeatureSymbology.Overrides;
public changedElems: ChangedElementEntry[];
public visibleChangedElems: ChangedElementEntry[];
public hiddenChangedElems: ChangedElementEntry[];
public readonly viewport: Viewport;
private readonly _removals: Array<() => void> = [];
private _options: VersionDisplayOptions | undefined;
Expand All @@ -135,9 +136,11 @@ export class Provider
options?: VersionDisplayOptions,
targetIModelModels?: Set<string>,
targetIModelCategories?: Set<string>,
hiddenElems?: ChangedElementEntry[],
) {
this.iModel = iModel;
this.changedElems = elems;
this.visibleChangedElems = elems;
this.hiddenChangedElems = hiddenElems || [];
this.viewport = vp;
this._options = options;

Expand Down Expand Up @@ -224,9 +227,11 @@ export class Provider
/**
* Set changed element entries to visualize
* @param elems Changed elements
* @param hiddenElems Optional hidden changed elements to override display
*/
public setChangedElems(elems: ChangedElementEntry[]) {
this.changedElems = elems;
public setChangedElems(elems: ChangedElementEntry[], hiddenElems: ChangedElementEntry[] | undefined) {
this.visibleChangedElems = elems;
this.hiddenChangedElems = hiddenElems || [];
this.viewport.invalidateScene();
this.viewport.setFeatureOverrideProviderChanged();
refreshEvent.raiseEvent();
Expand Down Expand Up @@ -347,8 +352,8 @@ export class Provider
this._wantHideUnchanged() ? hiddenAppearance : unchangedAppearance,
);

const insertedElems = this.changedElems.filter((entry: ChangedElement) => entry.opcode === DbOpcode.Insert);
const updatedElems = this.changedElems.filter((entry: ChangedElement) => entry.opcode === DbOpcode.Update);
const insertedElems = this.visibleChangedElems.filter((entry: ChangedElement) => entry.opcode === DbOpcode.Insert);
const updatedElems = this.visibleChangedElems.filter((entry: ChangedElement) => entry.opcode === DbOpcode.Update);

const inserted = FeatureAppearance.fromJSON({
rgb: VersionCompareVisualizationManager.colorInsertedRgb(),
Expand All @@ -360,6 +365,7 @@ export class Provider
? true
: undefined,
});

for (const elem of insertedElems) {
// Check if user is emphasizing some elements, and if so, override said elements
if (this._internalAlwaysDrawn.size === 0 || this._internalAlwaysDrawn.has(elem.id)) {
Expand Down Expand Up @@ -391,7 +397,22 @@ export class Provider
for (const elem of updatedElems) {
// Check if user is emphasizing some elements, and if so, only override said elements
if (this._internalAlwaysDrawn.size === 0 || this._internalAlwaysDrawn.has(elem.id)) {
overrides.override({ elementId: elem.id, appearance: elem.indirect ? updatedIndirectly : updated });
overrides.override({
elementId: elem.id,
appearance: elem.indirect
? updatedIndirectly
: updated,
});
}
}

for (const elem of this.hiddenChangedElems){
// If the user has hidden elements, we have to override them with the hidden appearance
if (this._internalAlwaysDrawn.size === 0 || this._internalAlwaysDrawn.has(elem.id)) {
overrides.override({
elementId: elem.id,
appearance: hiddenAppearance,
});
}
}
}
Expand Down Expand Up @@ -454,7 +475,7 @@ export class Provider
// Handle removed elements that are in secondary iModel
if (!this._wantHideRemoved()) {
const deletedElemIds = new Set(
this.changedElems
this.visibleChangedElems
.filter(
(entry: ChangedElement) =>
entry.opcode === DbOpcode.Delete &&
Expand All @@ -474,7 +495,7 @@ export class Provider
// Handle modified elements that are in secondary iModel
if (this._options?.wantModified && !this._wantHideModified()) {
const modifiedElemIds = new Set(
this.changedElems
this.visibleChangedElems
.filter(
(entry: ChangedElement) =>
entry.opcode === DbOpcode.Update && !neverDrawn.has(entry.id),
Expand Down Expand Up @@ -745,7 +766,7 @@ export class Provider

public toJSON(): ProviderProps {
return {
changedElems: this.changedElems,
changedElems: this.visibleChangedElems,
options: this._options,
internalAlwaysDrawn: this._internalAlwaysDrawn,
internalNeverDrawn: this._internalNeverDrawn,
Expand All @@ -762,7 +783,7 @@ export class Provider
}

public fromJSON(props: ProviderProps) {
this.changedElems = props.changedElems;
this.visibleChangedElems = props.changedElems;
this._internalAlwaysDrawn = props.internalAlwaysDrawn;
this._internalNeverDrawn = props.internalNeverDrawn;
this._exclusive = props.exclusive;
Expand Down Expand Up @@ -926,11 +947,12 @@ export function isVersionComparisonDisplayUsingContextTools(vp: Viewport): boole

export function updateVersionCompareDisplayEntries(
vp: Viewport,
entries: ChangedElementEntry[],
visibleEntries: ChangedElementEntry[],
hiddenEntries: ChangedElementEntry[] | undefined,
): boolean {
const existing = vp.findFeatureOverrideProviderOfType(Provider);
if (undefined !== existing && existing instanceof Provider) {
existing.setChangedElems(entries);
existing.setChangedElems(visibleEntries, hiddenEntries);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,17 @@ export class VersionCompareVisualizationManager {

/**
* Used to emphasize and focus on a list of elements instead of all changed elements in comparison
* @param elements Elements to focus during visualization
* @param visible Elements to focus during visualization
* @param hiddenElements Optional list of elements that should not be shown in the visualization
*/
public setFocusedElements = async (elements: ChangedElementEntry[] | undefined) => {
this._focusedElements = elements;
public setFocusedElements = async (visible: ChangedElementEntry[] | undefined, hiddenElements?: ChangedElementEntry[]) => {
this._focusedElements = visible;
updateVersionCompareDisplayEntries(
this._viewport,
this._focusedElements !== undefined
? this._focusedElements
: this._changedElements,
hiddenElements,
);
};

Expand Down Expand Up @@ -288,6 +290,7 @@ export class VersionCompareVisualizationManager {
public async toggleUnchangedVisibility(hide?: boolean): Promise<boolean> {
this.displayOptions.hideUnchanged =
hide !== undefined ? hide : !this.displayOptions.hideUnchanged;

this.displayOptions.changedModels = this._changedModels;
this.displayOptions.emphasized = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,26 +810,48 @@ export class ChangedElementsListComponent extends Component<ChangedElementsListP
return this.props.dataProvider.getEntriesFromIds(ids);
};

/** Returns true it state's filterOptions matches the entry. */
private _filterEntryWithOptions = (entry: ChangedElementEntry): boolean => {
return this._filterEntryWithGivenOptions(entry, this.state.filterOptions);
};
/** return true for entries we want to hide */
private _hideFilter = (entry: ChangedElementEntry, opts: FilterOptions): boolean => {
if (entry.opcode === DbOpcode.Update) {
// hide when the filter options do not want modified elements
if (!opts.wantModified) {
return true;
}
// hide when type of change filtering or advanced filtering is matched
if (!this._modifiedEntryMatchesFilters(entry, opts)){
return true;
}
}

/** Returns true if filterOptions matches the entry. */
private _filterEntryWithGivenOptions = (entry: ChangedElementEntry, opts: FilterOptions): boolean => {
return this._wantShowEntry(entry, opts);
};
// hide when the filter options do not want added elements
if (entry.opcode === DbOpcode.Insert && !opts.wantAdded) {
return true;
}
return false;
}

/** return true for entries we want to include */
private _includeFilter = (entry: ChangedElementEntry, opts: FilterOptions): boolean => {
if (entry.opcode === DbOpcode.Delete){
return opts.wantDeleted;
}
return true;
}

/** Obtains the children nodes of the models, creates their entries, and visualizes them. */
private _visualizeModelNodes = async (nodes: TreeNodeItem[], options?: FilterOptions): Promise<void> => {
// Handle model nodes: Get the children entries they already have into an array
const filter = options
? (entry: ChangedElementEntry) => this._filterEntryWithGivenOptions(entry, options)
: this._filterEntryWithOptions;
const modelIds = new Set(nodes.map((value) => value.id));
const entries = this.props.dataProvider.getEntriesWithModelIds(modelIds, filter);
const opts = options ?? this.state.filterOptions;

const includeFilter = (e: ChangedElementEntry) => this._includeFilter(e, opts);
const hideFilter = (e: ChangedElementEntry) => this._hideFilter(e, opts);

const { visible, hidden } =
this.props.dataProvider.GetEntriesByModelIdsAndFilters(modelIds, includeFilter, hideFilter);

const visualizationManager = this.props.manager.visualization?.getSingleViewVisualizationManager();
await visualizationManager?.setFocusedElements(entries);
await visualizationManager?.setFocusedElements(visible, hidden);
};

/**
Expand All @@ -856,15 +878,15 @@ export class ChangedElementsListComponent extends Component<ChangedElementsListP
): Promise<void> => {
// Get entries to visualize containing the relevant children entries as well
const entries = targetNode ? this._getEntriesToVisualize(targetNode) : directChildNodes.map(nodeToEntry);
const opts = options ?? this.state.filterOptions;

// Filter function for matching to the given filter options
const filterFunc = options
? (entry: ChangedElementEntry) => this._filterEntryWithGivenOptions(entry, options)
: this._filterEntryWithOptions;
const includeFilter = (e: ChangedElementEntry) => this._includeFilter(e, opts) && !this._hideFilter(e, opts);
const hideFilter = (e: ChangedElementEntry) => this._hideFilter(e, opts);

// Visualize the filtered elements and focus
const visualizationManager = this.props.manager.visualization?.getSingleViewVisualizationManager();
await visualizationManager?.setFocusedElements(entries.filter(filterFunc));
await visualizationManager?.setFocusedElements(entries.filter(includeFilter), entries.filter(hideFilter));
};

/** Sets viewport visualization based on the given nodes and target/parent node. */
Expand Down
Loading