@yuuvis/client-framework 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import { MatIcon, MatIconModule } from '@angular/material/icon';
5
5
  import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
6
6
  import * as i4 from '@angular/material/tooltip';
7
7
  import { MatTooltip, MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipModule } from '@angular/material/tooltip';
8
- import { ObjectConfigService, DmsService, DmsObject, BaseObjectTypeField, TranslatePipe, SystemService, ContentStreamField, Utils, Sort } from '@yuuvis/client-core';
8
+ import { ObjectConfigService, DmsService, EventService, DmsObject, YuvEventType, BaseObjectTypeField, TranslatePipe, SystemService, ContentStreamField, Utils, Sort } from '@yuuvis/client-core';
9
9
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
10
10
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
11
11
  import * as i1$2 from '@angular/forms';
@@ -181,6 +181,7 @@ class TileListComponent {
181
181
  #cdr;
182
182
  #actionService;
183
183
  #dmsService;
184
+ #eventService;
184
185
  #busy;
185
186
  #preselect;
186
187
  #rawResultItems;
@@ -193,6 +194,7 @@ class TileListComponent {
193
194
  this.#cdr = inject(ChangeDetectorRef);
194
195
  this.#actionService = inject(ActionsService);
195
196
  this.#dmsService = inject(DmsService);
197
+ this.#eventService = inject(EventService);
196
198
  //#endregion
197
199
  //#region Angular stuff
198
200
  /**
@@ -573,6 +575,20 @@ class TileListComponent {
573
575
  effect(this.#preselectEffect);
574
576
  effect(this.#closeMenuEffect);
575
577
  effect(this.#outsideClickCloseEffect);
578
+ // Auto-remove drop-in tiles whose backing DmsObject was just deleted upstream.
579
+ // Without this, the framework reconstructs drop-ins in onQueryResult by slicing
580
+ // the leading dropInSize() items from the `items` signal, so a deleted tile
581
+ // would survive every subsequent refresh as a stale leader. Listening here
582
+ // keeps the behavior transparent to consumers — any caller that deletes via
583
+ // DmsService gets correct rendering with no additional wiring.
584
+ this.#eventService
585
+ .on(YuvEventType.DMS_OBJECT_DELETED)
586
+ .pipe(takeUntilDestroyed())
587
+ .subscribe((evt) => {
588
+ const id = evt.data?.id;
589
+ if (id)
590
+ this.removeDropItems([id]);
591
+ });
576
592
  }
577
593
  //#region Lifecycle hooks
578
594
  ngOnInit() {
@@ -837,6 +853,64 @@ class TileListComponent {
837
853
  }
838
854
  this.list().dropItems(items);
839
855
  }
856
+ /**
857
+ * Surgically removes tiles matching the given IDs from the rendered list.
858
+ *
859
+ * Use this when an upstream deletion (e.g. `DmsService.deleteDmsObject`) removes
860
+ * an object that is currently rendered. Without this call, drop-in tiles would
861
+ * remain visible after `refresh()` because `onQueryResult` reconstructs them by
862
+ * slicing the leading `dropInSize()` items from the `items` signal — the deleted
863
+ * tile would survive as a stale leader.
864
+ *
865
+ * The method is also invoked automatically by an internal `DMS_OBJECT_DELETED`
866
+ * subscription, so consumers that delete via `DmsService` get correct behavior
867
+ * without any extra wiring. Calling it directly is only needed for deletions
868
+ * that do not flow through the standard event service.
869
+ *
870
+ * Internally:
871
+ * 1. Locates positions of matching tiles in the `items` signal.
872
+ * 2. Removes them from `items` (the parent-visible view model).
873
+ * 3. Filters `_selection` to drop indices on removed tiles, then shifts the
874
+ * remaining indices back by the count of removed positions that preceded
875
+ * each surviving entry so selection stays attached to the same logical items.
876
+ * 4. Clears `_lastSelection` if it pointed to a removed tile, else shifts it.
877
+ * 5. Delegates to `QueryListComponent.removeDropItems` to filter the inner
878
+ * drop-in set so `resultItems` recomputes with the correct leading slice.
879
+ *
880
+ * @param ids Object IDs to remove.
881
+ */
882
+ removeDropItems(ids) {
883
+ if (ids.length === 0)
884
+ return;
885
+ const removeSet = new Set(ids);
886
+ const currentItems = this.items();
887
+ const removedPositions = [];
888
+ currentItems.forEach((item, idx) => {
889
+ if (removeSet.has(item.id))
890
+ removedPositions.push(idx);
891
+ });
892
+ if (removedPositions.length === 0)
893
+ return;
894
+ const removedSet = new Set(removedPositions);
895
+ this.items.set(currentItems.filter((_, idx) => !removedSet.has(idx)));
896
+ if (this._selection.length > 0) {
897
+ this._selection = this._selection
898
+ .filter((idx) => !removedSet.has(idx))
899
+ .map((idx) => idx - removedPositions.filter((r) => r < idx).length);
900
+ }
901
+ if (this._lastSelection !== undefined) {
902
+ if (removedSet.has(this._lastSelection)) {
903
+ this._lastSelection = this._selection.length ? this._selection[this._selection.length - 1] : undefined;
904
+ }
905
+ else {
906
+ this._lastSelection -= removedPositions.filter((r) => r < this._lastSelection).length;
907
+ }
908
+ }
909
+ const selectedTiles = this._selectionToTileData(this._selection);
910
+ this.selection.set(selectedTiles.map((tile) => tile.id));
911
+ this.selectedTile.set(selectedTiles);
912
+ this.list().removeDropItems(ids);
913
+ }
840
914
  /**
841
915
  * Toggles an `ObjectFlavor` on or off and re-maps the current result set.
842
916
  *