import WithRender from "./search-box.vue"; import Vue from "vue"; import { Component, ProvideReactive, Ref } from "vue-property-decorator"; import "./search-box.scss"; import context from "../context"; import $ from "jquery"; import { BTab } from "bootstrap-vue"; import Icon from "../ui/icon/icon"; import SearchFormTab from "../search-form/search-form-tab"; import MarkerInfoTab from "../marker-info/marker-info-tab"; import LineInfoTab from "../line-info/line-info-tab"; import hammer from "hammerjs"; import { InjectMapComponents, InjectMapContext, SEARCH_BOX_CONTEXT_INJECT_KEY } from "../../utils/decorators"; import { MapComponents, MapContext } from "../leaflet-map/leaflet-map"; import RouteFormTab from "../route-form/route-form-tab"; export type SearchBoxContext = Vue; @WithRender @Component({ components: { Icon, LineInfoTab, MarkerInfoTab, RouteFormTab, SearchFormTab } }) export default class SearchBox extends Vue { @InjectMapComponents() mapComponents!: MapComponents; @InjectMapContext() mapContext!: MapContext; @ProvideReactive(SEARCH_BOX_CONTEXT_INJECT_KEY) searchBoxContext = new Vue(); @Ref() tabsComponent!: any; @Ref() searchBox!: HTMLElement; @Ref() resizeHandle!: HTMLElement; cardHeader!: HTMLElement; tab = 0; panStartTop: number | null = null; restoreTop: number | null = null; resizeStartHeight: number | null = null; resizeStartWidth: number | null = null; hasFocus = false; isResizing = false; get isNarrow(): boolean { return context.isNarrow; } mounted(): void { this.mapContext.$on("fm-search-box-show-tab", this.handleShowTab); this.cardHeader = this.searchBox.querySelector(".card-header")!; const pan = new hammer.Manager(this.cardHeader); pan.add(new hammer.Pan({ direction: hammer.DIRECTION_VERTICAL })); pan.on("panstart", this.handlePanStart); pan.on("pan", this.handlePanMove); pan.on("panend", this.handlePanEnd); const resize = new hammer.Manager(this.resizeHandle); resize.add(new hammer.Pan({ direction: hammer.DIRECTION_ALL })); resize.add(new hammer.Tap()); resize.on("panstart", this.handleResizeStart); resize.on("pan", this.handleResizeMove); resize.on("panend", this.handleResizeEnd); resize.on("tap", this.handleResizeClick); } beforeDestroy(): void { this.mapContext.$off("fm-search-box-show-tab", this.handleShowTab); this.cardHeader = undefined as any; } handlePanStart(event: any): void { this.restoreTop = null; this.panStartTop = parseInt($(this.searchBox).css("flex-basis")); } handlePanMove(event: any): void { if (this.isNarrow && this.panStartTop != null && event.srcEvent.type != "pointercancel") $(this.searchBox).stop().css("flexBasis", `${this.getSanitizedTop(this.panStartTop - event.deltaY)}px`); } handlePanEnd(): void { this.mapComponents.map.invalidateSize({ animate: true }); } getTopFromBottom(bottom: number): number { return (this.searchBox.offsetParent as HTMLElement).offsetHeight - bottom; } getSanitizedTop(top: number): number { const maxTop = (this.searchBox.offsetParent as HTMLElement).offsetHeight - 45; return Math.max(0, Math.min(maxTop, top)); } handleActivateTab(): void { this.restoreTop = null; } handleChanged(newTabs: BTab[], oldTabs: BTab[]): void { if (this.restoreTop != null && newTabs.length < oldTabs.length) { $(this.searchBox).animate({ top: this.restoreTop }); this.restoreTop = null; } } handleShowTab(id: string, expand = true): void { const idx = this.tabsComponent.tabs.findIndex((tab: any) => tab.id == id); if (idx != -1) this.tab = idx; if (this.isNarrow && expand) { setTimeout(() => { const maxTop = this.getSanitizedTop(this.getTopFromBottom(300)); const currentTop = this.searchBox.offsetTop; if (currentTop > maxTop) { this.restoreTop = currentTop; $(this.searchBox).animate({ top: maxTop }); } }, 0); } } handleResizeStart(event: any): void { this.isResizing = true; this.resizeStartWidth = this.searchBox.offsetWidth; this.resizeStartHeight = this.searchBox.offsetHeight; this.$root.$emit('bv::hide::tooltip'); this.searchBoxContext.$emit("resizestart"); } handleResizeMove(event: any): void { this.searchBox.style.width = `${this.resizeStartWidth + event.deltaX}px`; this.searchBox.style.height = `${this.resizeStartHeight + event.deltaY}px`; this.searchBoxContext.$emit("resize"); } handleResizeEnd(event: any): void { this.isResizing = false; this.searchBoxContext.$emit("resizeend"); } handleResizeClick(): void { this.searchBox.style.width = ""; this.searchBox.style.height = ""; this.$root.$emit('bv::hide::tooltip'); this.searchBoxContext.$emit("resizereset"); } }