kopia lustrzana https://github.com/FacilMap/facilmap
149 wiersze
4.6 KiB
TypeScript
149 wiersze
4.6 KiB
TypeScript
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");
|
|
}
|
|
|
|
} |