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
7 changes: 6 additions & 1 deletion src/app/expo-hall-map/expo-hall-map.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<!-- Map -->
<div class="map-container">
<pinch-zoom class="expo-hall-map">
<pinch-zoom #pinchZoom class="expo-hall-map" [autoZoomOut]="false" [doubleTap]="true" [wheel]="true" [draggableImage]="false">
<div class="map-inner">

<img
Expand Down Expand Up @@ -54,6 +54,11 @@
<!-- Tap popup -->
<div class="booth-popup" *ngIf="selectedBooth" (click)="selectedBooth = null">
<div class="booth-popup-inner" (click)="$event.stopPropagation()">
<img
*ngIf="selectedBooth.logoUrl"
class="booth-popup-logo"
[src]="selectedBooth.logoUrl"
[alt]="selectedBooth.name + ' logo'" />
<a *ngIf="selectedBooth.level"
class="booth-popup-content"
[routerLink]="'/app/tabs/sponsors/sponsor-detail/' + getSponsorSlug(selectedBooth.name)">
Expand Down
28 changes: 25 additions & 3 deletions src/app/expo-hall-map/expo-hall-map.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,19 @@
}

.booth-img {
max-width: 85%;
max-height: 85%;
// Anchored to the upper portion of the booth (top 8%, ~60% height)
// because the floor-plan PNG prints the booth number in the lower
// third of the box. Centering vertically caused wide logos like
// Tetrix and TimeCopilot to bleed over the printed number; pinning
// to the top keeps the lower band clean.
position: absolute;
top: 8%;
left: 50%;
width: 60%;
height: 60%;
max-width: 60%;
max-height: 60%;
transform: translateX(-50%);
object-fit: contain;
pointer-events: none;
}
Expand Down Expand Up @@ -136,10 +147,21 @@
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
background: var(--ion-background-color, #fff);
border: 2px solid #DD04D2;
border-radius: 12px;
padding: 14px 16px;
box-shadow: 0 -2px 20px rgba(0, 0, 0, 0.15);
// Pink-tinted shadow so the popup also lifts visually off white booth tiles
box-shadow: 0 -4px 24px rgba(221, 4, 210, 0.25);
}

.booth-popup-logo {
width: 48px;
height: 48px;
object-fit: contain;
flex-shrink: 0;
border-radius: 6px;
}

.booth-popup-content {
Expand Down
43 changes: 41 additions & 2 deletions src/app/expo-hall-map/expo-hall-map.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { IonSearchbar, LoadingController } from '@ionic/angular';

import { ConferenceData } from '../providers/conference-data';
Expand All @@ -22,15 +22,23 @@ export interface BoothData {
templateUrl: './expo-hall-map.component.html',
styleUrls: ['./expo-hall-map.component.scss'],
})
export class ExpoHallMapComponent implements OnInit {
export class ExpoHallMapComponent implements OnInit, AfterViewInit {
@ViewChild('searchBar') searchBar!: IonSearchbar;
@ViewChild('pinchZoom') pinchZoomCmp?: { pinchZoom?: { maxScale: number } };

showSearchbar = false;
searchQuery = '';
searchResults: BoothData[] = [];
selectedBooth: BoothData | null = null;
highlightedBoothId: string | null = null;

// Static logo fallbacks for booths that don't come through the sponsor API
// (PSF, community booths, attendee lounge, etc.). Keyed by booth id.
private readonly STATIC_BOOTH_LOGOS: { [id: string]: string } = {
'407': 'assets/img/python-logo.png',
'606': 'assets/img/pycon-us-2026-logo.svg',
};

// Booth coordinates in the original 8000×5655 floor plan image.
// Names are the seed labels — they get overwritten with the live sponsor
// name (and gain logoUrl/level/description) once the API responds.
Expand Down Expand Up @@ -108,7 +116,38 @@ export class ExpoHallMapComponent implements OnInit {
this.loadSponsors();
}

ngAfterViewInit() {
// @ciag/ngx-pinch-zoom hardcodes defaultMaxScale=3 and only auto-derives
// a higher cap when the first descendant is an <img> AND limitZoom is
// the string "original image size". Our content is wrapped in a div so
// the auto-detect path is never taken; passing a numeric limitZoom is
// silently ignored. Reach into the underlying IvyPinch instance after
// it's constructed and bump maxScale directly. Polled because the
// instance is created during ngOnInit on the child component.
const start = Date.now();
const tick = () => {
const inner = this.pinchZoomCmp?.pinchZoom;
if (inner) {
inner.maxScale = 25;
return;
}
if (Date.now() - start < 2000) {
setTimeout(tick, 50);
}
};
tick();
}

loadSponsors(showLoader = false) {
// Seed static fallbacks first so the API merge can override them when a
// booth does come through as a sponsor; otherwise PSF/community booths
// remain blank.
for (const booth of this.booths) {
if (!booth.logoUrl && this.STATIC_BOOTH_LOGOS[booth.id]) {
booth.logoUrl = this.STATIC_BOOTH_LOGOS[booth.id];
}
}

const apply = (sponsors: any) => {
for (const list of Object.values(sponsors || {})) {
for (const sponsor of list as any[]) {
Expand Down
Loading
Loading