From 808e288848406b4084842629fac7db01792019b1 Mon Sep 17 00:00:00 2001 From: Dogukan Karatas Date: Mon, 7 Jul 2025 14:18:18 +0200 Subject: [PATCH 1/3] adds option for zoom on select --- .../src/components/ViewerControls.vue | 19 +++++++++++ src/powerbi-visual/src/plugins/viewer.ts | 16 +++++---- src/powerbi-visual/src/store/visualStore.ts | 34 ++++++++++++++++--- src/powerbi-visual/src/visual.ts | 10 ++++++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/powerbi-visual/src/components/ViewerControls.vue b/src/powerbi-visual/src/components/ViewerControls.vue index aef4f7a..39be8c0 100644 --- a/src/powerbi-visual/src/components/ViewerControls.vue +++ b/src/powerbi-visual/src/components/ViewerControls.vue @@ -18,6 +18,19 @@ + + + + + @@ -65,6 +78,7 @@ import Perspective from '../components/global/icon/Perspective.vue' import PerspectiveMore from '../components/global/icon/PerspectiveMore.vue' import Ghost from '../components/global/icon/Ghost.vue' +import HandZoom from '../components/global/icon/HandZoom.vue' const visualStore = useVisualStore() @@ -111,6 +125,11 @@ const toggleGhostHidden = () => { visualStore.writeIsGhostToFile() } +const toggleZoomOnFilter = () => { + visualStore.setIsZoomOnFilterActive(!visualStore.isZoomOnFilterActive) + visualStore.writeZoomOnFilterToFile() +} + const viewModesOpen = computed({ get: () => activeControl.value === 'viewModes', set: (value) => { diff --git a/src/powerbi-visual/src/plugins/viewer.ts b/src/powerbi-visual/src/plugins/viewer.ts index e88f962..1253ab7 100644 --- a/src/powerbi-visual/src/plugins/viewer.ts +++ b/src/powerbi-visual/src/plugins/viewer.ts @@ -37,8 +37,8 @@ export interface Hit { export interface IViewerEvents { ping: (message: string) => void setSelection: (objectIds: string[]) => void - resetFilter: (objectIds: string[], ghost: boolean) => void - filterSelection: (objectIds: string[], ghost: boolean) => void + resetFilter: (objectIds: string[], ghost: boolean, zoom: boolean) => void + filterSelection: (objectIds: string[], ghost: boolean, zoom: boolean) => void setViewMode: (viewMode: ViewMode) => void colorObjectsByGroup: ( colorById: { @@ -149,20 +149,24 @@ export class ViewerHandler { } } - public filterSelection = (objectIds: string[], ghost: boolean) => { + public filterSelection = (objectIds: string[], ghost: boolean, zoom: boolean = true) => { console.log('🔗 Handling filterSelection inside ViewerHandler') if (objectIds) { this.unIsolateObjects() this.filteringState = this.filtering.isolateObjects(objectIds, 'powerbi', true, ghost) - this.zoomObjects(objectIds, true) + if (zoom) { + this.zoomObjects(objectIds, true) + } } } - public resetFilter = (objectIds: string[], ghost: boolean) => { + public resetFilter = (objectIds: string[], ghost: boolean, zoom: boolean = true) => { console.log('🔗 Handling filterSelection inside ViewerHandler') if (objectIds) { this.isolateObjects(objectIds, ghost) - this.zoomObjects(objectIds, true) + if (zoom) { + this.zoomObjects(objectIds, true) + } } } diff --git a/src/powerbi-visual/src/store/visualStore.ts b/src/powerbi-visual/src/store/visualStore.ts index 1ed1672..c8793da 100644 --- a/src/powerbi-visual/src/store/visualStore.ts +++ b/src/powerbi-visual/src/store/visualStore.ts @@ -36,6 +36,7 @@ export const useVisualStore = defineStore('visualStore', () => { const isOrthoProjection = ref(false) const isGhostActive = ref(true) const isNavbarHidden = ref(false) + const isZoomOnFilterActive = ref(true) const commonError = ref(undefined) @@ -165,13 +166,13 @@ export const useVisualStore = defineStore('visualStore', () => { if (dataInput.value.selectedIds.length > 0) { isFilterActive.value = true - viewerEmit.value('filterSelection', dataInput.value.selectedIds, isGhostActive.value) + viewerEmit.value('filterSelection', dataInput.value.selectedIds, isGhostActive.value, isZoomOnFilterActive.value) } else { isFilterActive.value = false latestColorBy.value = dataInput.value.colorByIds // Only apply filtering if object IDs are available, otherwise show all objects normally if (fieldInputState.value.objectIds && dataInput.value.objectIds && dataInput.value.objectIds.length > 0) { - viewerEmit.value('resetFilter', dataInput.value.objectIds, isGhostActive.value) + viewerEmit.value('resetFilter', dataInput.value.objectIds, isGhostActive.value, isZoomOnFilterActive.value) } else { // No object IDs provided - show all objects without any filtering viewerEmit.value('unIsolateObjects') @@ -247,6 +248,22 @@ export const useVisualStore = defineStore('visualStore', () => { }) } + const writeZoomOnFilterToFile = () => { + // NOTE: need skipping the update function, it resets the viewer state unneccessarily. + postFileSaveSkipNeeded.value = true + host.value.persistProperties({ + merge: [ + { + objectName: 'camera', + properties: { + zoomOnFilter: isZoomOnFilterActive.value + }, + selector: null + } + ] + }) + } + const writeViewModeToFile = (viewMode: ViewMode) => { // NOTE: need skipping the update function, it resets the viewer state unneccessarily. postFileSaveSkipNeeded.value = true @@ -353,6 +370,10 @@ export const useVisualStore = defineStore('visualStore', () => { isGhostActive.value = val } + const setIsZoomOnFilterActive = (val: boolean) => { + isZoomOnFilterActive.value = val + } + const setPostFileSaveSkipNeeded = (newValue: boolean) => (postFileSaveSkipNeeded.value = newValue) const setPostClickSkipNeeded = (newValue: boolean) => (postClickSkipNeeded.value = newValue) @@ -366,7 +387,7 @@ export const useVisualStore = defineStore('visualStore', () => { const resetFilters = () => { // Only apply filtering if object IDs are available, otherwise show all objects normally if (fieldInputState.value.objectIds && dataInput.value && dataInput.value.objectIds && dataInput.value.objectIds.length > 0) { - viewerEmit.value('resetFilter', dataInput.value.objectIds, isGhostActive.value) + viewerEmit.value('resetFilter', dataInput.value.objectIds, isGhostActive.value, isZoomOnFilterActive.value) } else { // No object IDs provided - show all objects without any filtering viewerEmit.value('unIsolateObjects') @@ -395,13 +416,13 @@ export const useVisualStore = defineStore('visualStore', () => { // Restore selection filters if they exist if (dataInput.value.selectedIds.length > 0) { isFilterActive.value = true - viewerEmit.value('filterSelection', dataInput.value.selectedIds, isGhostActive.value) + viewerEmit.value('filterSelection', dataInput.value.selectedIds, isGhostActive.value, isZoomOnFilterActive.value) } else { isFilterActive.value = false latestColorBy.value = dataInput.value.colorByIds // Only apply filtering if object IDs are available, otherwise show all objects normally if (fieldInputState.value.objectIds && dataInput.value.objectIds && dataInput.value.objectIds.length > 0) { - viewerEmit.value('resetFilter', dataInput.value.objectIds, isGhostActive.value) + viewerEmit.value('resetFilter', dataInput.value.objectIds, isGhostActive.value, isZoomOnFilterActive.value) } else { // No object IDs provided - show all objects without any filtering viewerEmit.value('unIsolateObjects') @@ -443,6 +464,7 @@ export const useVisualStore = defineStore('visualStore', () => { isOrthoProjection, isGhostActive, isNavbarHidden, + isZoomOnFilterActive, latestAvailableVersion, isConnectorUpToDate, commonError, @@ -450,6 +472,7 @@ export const useVisualStore = defineStore('visualStore', () => { setLatestAvailableVersion, setIsOrthoProjection, setIsGhost, + setIsZoomOnFilterActive, setFormattingSettings, setBrandingHidden, setNavbarHidden, @@ -466,6 +489,7 @@ export const useVisualStore = defineStore('visualStore', () => { writeObjectsToFile, writeCameraViewToFile, writeIsGhostToFile, + writeZoomOnFilterToFile, writeIsOrthoToFile, writeViewModeToFile, writeCameraPositionToFile, diff --git a/src/powerbi-visual/src/visual.ts b/src/powerbi-visual/src/visual.ts index 2f6847f..c828d18 100644 --- a/src/powerbi-visual/src/visual.ts +++ b/src/powerbi-visual/src/visual.ts @@ -196,6 +196,16 @@ export class Visual implements IVisual { ) } + if (camera && 'zoomOnFilter' in camera) { + console.log( + `Zoom on filter?: ${options.dataViews[0].metadata.objects.camera?.zoomOnFilter as boolean}` + ) + + visualStore.setIsZoomOnFilterActive( + options.dataViews[0].metadata.objects.camera?.zoomOnFilter as boolean + ) + } + // get receive info from file for mixpanel try { const receiveInfoFromFile = JSON.parse( From d155a4b1652cff81da0a4aa6ec0fc88733f9b33c Mon Sep 17 00:00:00 2001 From: Dogukan Karatas Date: Tue, 8 Jul 2025 13:37:17 +0200 Subject: [PATCH 2/3] registered to the capabilities --- src/powerbi-visual/capabilities.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/powerbi-visual/capabilities.json b/src/powerbi-visual/capabilities.json index 715ae5a..db181e7 100644 --- a/src/powerbi-visual/capabilities.json +++ b/src/powerbi-visual/capabilities.json @@ -105,6 +105,9 @@ }, "isGhost": { "type": { "bool": true } + }, + "zoomOnFilter": { + "type": { "bool": true } } } }, From 2a8925c8efcf20b63effa37a7fa65ffc409eddd6 Mon Sep 17 00:00:00 2001 From: Dogukan Karatas Date: Tue, 8 Jul 2025 15:08:47 +0200 Subject: [PATCH 3/3] icon updated --- .../src/components/ViewerControls.vue | 28 +++++++++---------- .../src/components/global/icon/ZoomToFit.vue | 24 ++++++++++++++++ 2 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 src/powerbi-visual/src/components/global/icon/ZoomToFit.vue diff --git a/src/powerbi-visual/src/components/ViewerControls.vue b/src/powerbi-visual/src/components/ViewerControls.vue index 39be8c0..7802e1a 100644 --- a/src/powerbi-visual/src/components/ViewerControls.vue +++ b/src/powerbi-visual/src/components/ViewerControls.vue @@ -5,6 +5,19 @@ + + + + + - - - - - @@ -78,7 +78,7 @@ import Perspective from '../components/global/icon/Perspective.vue' import PerspectiveMore from '../components/global/icon/PerspectiveMore.vue' import Ghost from '../components/global/icon/Ghost.vue' -import HandZoom from '../components/global/icon/HandZoom.vue' +import ZoomToFit from '../components/global/icon/ZoomToFit.vue' const visualStore = useVisualStore() diff --git a/src/powerbi-visual/src/components/global/icon/ZoomToFit.vue b/src/powerbi-visual/src/components/global/icon/ZoomToFit.vue new file mode 100644 index 0000000..4311db7 --- /dev/null +++ b/src/powerbi-visual/src/components/global/icon/ZoomToFit.vue @@ -0,0 +1,24 @@ + \ No newline at end of file