UNPKG

@boligmappa/web-component-search

Version:

Web component for interacting with the Boligmappa APIs

744 lines 29.8 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { html, css, unsafeCSS } from "lit"; import { property, eventOptions, state } from "lit/decorators.js"; import "./results-table/results-table"; import "./results-table/tables/loading-table"; import SearchType from "./helpers/enums/search-type"; import loupeIcon from "./assets/icons/loupe.svg"; // const loupeIcon = require("../assets/icons/loupe.svg") as string; import removeIcon from "./assets/icons/remove-button.svg"; import { getAccessToken } from "./requests/tokenRequests"; import { getAddresses, getBuildings, getProjects, getProperties, getStreets, } from "./requests/searchRequests"; import { darkGreen } from "./helpers/css-modules/colors"; import TableType from "./helpers/enums/table-type"; import { getLanguageObject } from "./helpers/languages/language-loader"; import { isObjectEmpty } from "./helpers/functions/isObjectEmpty"; import { MobxLitElement } from "@adobe/lit-mobx"; export class BoligmappaSearch extends MobxLitElement { constructor() { super(); this.isLoading = false; this.isLoadingMoreData = false; this.searchText = "test"; this.tableData = { tableType: TableType.None }; this.development = false; // @property() developmentStorage = false; this.textField = ""; this.config = {}; this.clickedItems = {}; this.previousSearchFailed = false; this.apiUrl = "https://proff-api.boligmappa.no/v1"; this.addressPageSize = 5000; this.propertyCursor = ""; this.backCounter = 1; this.initalSearchTextset = false; this.latestSearchText = ""; this.debounce = (callback, waitFor) => { let timeout; return (...args) => { let result; timeout && clearTimeout(timeout); timeout = setTimeout(() => { result = callback(...args); }, waitFor); return result; }; }; this.debounceFunction = this.debounce(async () => { const lastClickedTableType = this.getLastClickedTableType(); this.isLoading = true || this.searchText; // using this.searchText to aviod loading animation when text is empty switch (lastClickedTableType) { case TableType.Overview: if (this.tableData.searchType === SearchType.Streets) { await this.searchStreets(1); } if (this.tableData.searchType === SearchType.Projects) { await this.searchProjects(1); } break; case TableType.Streets: await this.searchAdresses(1); break; case TableType.Addresses: await this.searchBuildings(1); break; case TableType.Buildings: await this.searchProperties(); break; default: await this.initalSearch(); } this.isLoading = false; }, 200).bind(this); } firstUpdated() { // if (!this.development) { // this.developmentStorage = false; // } if (this.development) { this.apiUrl = "https://staging-proff-api.boligmappa.no/v1"; } } updated(changedProperties) { var _a, _b; if (changedProperties.has("config")) { if (!isObjectEmpty(this.config)) { this.configureSettingsFromConfiurationInput(); if (!this.config.integrationPartnerSetsAccessToken) { this.getAndSetAccessToken(); } } } if (Boolean(this.textField) && !this.initalSearchTextset) { console.log(`text: ${this.textField}`); const searchField = (_b = (_a = document .querySelector("boligmappa-search")) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.getElementById("searchText"); if (searchField) { searchField.value = this.textField; this.searchText = this.textField; this.initalSearchTextset = true; this.debounceFunction(); } } } configureSettingsFromConfiurationInput() { this.setupDefaultPageSizesIfNoneIsProvided(); this.setupLanguageObject(); } setupLanguageObject() { if (!this.config.language) { this.config.language = "NOR"; } this.languageObject = getLanguageObject(this.config.language); } setupDefaultPageSizesIfNoneIsProvided() { const defaultPageSizes = { streets: 100, projects: 10, buildings: 10, }; this.config.pageSizes = Object.assign(Object.assign({}, defaultPageSizes), this.config.pageSizes); } async handleOnSearchEvent(e) { const input = e.target; this.searchText = input.value; this.searchIfSearchTextIsNotEmpty(); } searchIfSearchTextIsNotEmpty() { if (this.searchText === "") { this.clickedItems = {}; this.tableData = this.generateNewTableDataObject({ tableType: TableType.None, }); } else { this.checkIfSelectedAddressIsStillInSearchText(); this.debounceFunction(); } } checkIfSelectedAddressIsStillInSearchText() { var _a; if (this.tableData.selectedStreet === undefined) { return; } const streetName = (_a = this.tableData.selectedStreet) === null || _a === void 0 ? void 0 : _a.streetName.toUpperCase(); const searchTextWords = this.searchText.split(" "); if (!searchTextWords.some((word) => streetName.startsWith(word.toUpperCase()))) { this.clickedItems = {}; } } removeText() { var _a, _b; this.clickedItems = {}; const searchField = (_b = (_a = document .querySelector("boligmappa-search")) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.getElementById("searchText"); searchField.value = ""; this.tableData = this.generateNewTableDataObject({ tableType: TableType.None, }); } handleSearchError(error, func, paginator) { if (error.name === "InvalidTokenError" && !this.previousSearchFailed) { this.previousSearchFailed = true; this.waitingFunction = [func, paginator]; if (this.config.integrationPartnerSetsAccessToken) { this.dispatchTokenExpiredEvent(); } else { this.getNewTokenThenCallInterruptedSearch(); } } else { console.log(error); } } getLastClickedTableType() { if (this.clickedItems.clickedBuilding) { return TableType.Buildings; } if (this.clickedItems.clickedAddress) { return TableType.Addresses; } if (this.clickedItems.clickedStreet) { return TableType.Streets; } if (this.clickedItems.clickedOverview) { return TableType.Overview; } return null; } async initalSearch() { if (!this.config.access_token) { return; } this.latestSearchText = this.searchText; const usedSearchText = this.searchText; const [streets, projects] = await Promise.all([ getStreets(this.searchText, this.config.pageSizes.streets, 1, this.config.access_token, this.apiUrl).catch((error) => this.handleSearchError(error, this.initalSearch, undefined)), getProjects(1, this.config.pageSizes.projects, this.searchText, this.apiUrl, this.config.access_token).catch((error) => this.handleSearchError(error, this.initalSearch, undefined)), ]); if (usedSearchText !== this.latestSearchText) { return; } if (!streets || !projects) { console.log("Initial search failed"); return; } this.previousSearchFailed = false; let tableType = TableType.Overview; let searchType; if (streets.length && !projects.length) { tableType = TableType.Streets; searchType = SearchType.Streets; } if (!streets.length && projects.length) { tableType = TableType.Projects; searchType = SearchType.Projects; } this.tableData = this.generateNewTableDataObject({ tableType: tableType, searchType: searchType, projectListData: projects, streetListData: streets, }); } async searchProjects(page) { var _a; if (!this.config.access_token) { return; } this.latestSearchText = this.searchText; const usedSearchText = this.searchText; const projects = await getProjects(page, this.config.pageSizes.projects, this.searchText, this.apiUrl, this.config.access_token).catch((error) => this.handleSearchError(error, this.searchProjects, page)); if (usedSearchText !== this.latestSearchText) { return; } if (projects) { this.previousSearchFailed = false; if (page === 1) { this.tableData = this.generateNewTableDataObject({ tableType: TableType.Projects, searchType: SearchType.Projects, projectListData: projects, }); } else { const concatedProjectListData = (_a = this.tableData.projectListData) === null || _a === void 0 ? void 0 : _a.concat(projects); this.tableData = this.generateNewTableDataObject({ tableType: TableType.Projects, projectListData: concatedProjectListData, }); } } } async searchStreets(page) { var _a; if (!this.config.access_token) { return; } this.latestSearchText = this.searchText; const usedSearchText = this.searchText; const streets = await getStreets(this.searchText, this.config.pageSizes.streets, page, this.config.access_token, this.apiUrl).catch((error) => this.handleSearchError(error, this.searchStreets, page)); if (usedSearchText !== this.latestSearchText) { return; } if (streets) { this.previousSearchFailed = false; const tableType = this.clickedItems.clickedOverview && this.tableData.searchType === SearchType.Streets ? TableType.Streets : TableType.Overview; if (page === 1) { this.tableData = this.generateNewTableDataObject({ tableType: streets.length === 1 ? undefined : tableType, streetListData: streets, }); if (streets.length == 1) { this.tableData = this.generateNewTableDataObject({ selectedStreet: streets[0], }); this.searchAdresses(1); this.backCounter++; } } else { const concatedStreetListData = (_a = this.tableData.streetListData) === null || _a === void 0 ? void 0 : _a.concat(streets); this.tableData = this.generateNewTableDataObject({ tableType: TableType.Streets, streetListData: concatedStreetListData, }); } } } async searchAdresses(page) { var _a; if (!this.config.access_token || !this.tableData.selectedStreet) { return; } this.latestSearchText = this.searchText; const usedSearchText = this.searchText; const addresses = await getAddresses(this.addressPageSize, page, this.config.access_token, this.apiUrl, this.tableData.selectedStreet.id).catch((error) => this.handleSearchError(error, this.searchAdresses, page)); if (usedSearchText !== this.latestSearchText) { return; } if (addresses) { this.previousSearchFailed = false; if (page === 1) { this.tableData = this.generateNewTableDataObject({ tableType: TableType.Addresses, addressListData: addresses, }); } else { const concatedAddressListData = (_a = this.tableData.addressListData) === null || _a === void 0 ? void 0 : _a.concat(addresses); this.tableData = this.generateNewTableDataObject({ addressListData: concatedAddressListData, }); } } } async searchBuildings(page) { var _a, _b; if (!this.config.access_token || !this.tableData.selectedAddress) { return; } this.latestSearchText = this.searchText; const usedSearchText = this.searchText; const buildings = await getBuildings(this.config.pageSizes.buildings, page, this.config.access_token, this.apiUrl, (_a = this.tableData.selectedAddress) === null || _a === void 0 ? void 0 : _a.id).catch((error) => this.handleSearchError(error, this.searchBuildings, page)); if (usedSearchText !== this.latestSearchText) { return; } if (buildings) { this.previousSearchFailed = false; if (page === 1) { this.tableData = this.generateNewTableDataObject({ tableType: buildings.length === 1 ? undefined : TableType.Buildings, selectedBuilding: buildings.length === 1 ? buildings[0] : undefined, buildingListData: buildings, }); if (buildings.length == 1) { await this.searchProperties(); this.backCounter++; } } else { const concatedBuildingListData = (_b = this.tableData.buildingListData) === null || _b === void 0 ? void 0 : _b.concat(buildings); this.tableData = this.generateNewTableDataObject({ buildingListData: concatedBuildingListData, }); } } } async searchProperties(cursor) { var _a, _b; if (!this.config.access_token || !this.tableData.selectedAddress) { return; } this.latestSearchText = this.searchText; const usedSearchText = this.searchText; let cursorParameterString = ""; if (cursor) { cursorParameterString = `&cursor=${cursor}`; } const data = await getProperties(cursorParameterString, this.config.access_token, this.apiUrl, (_a = this.tableData.selectedAddress) === null || _a === void 0 ? void 0 : _a.id).catch((error) => this.handleSearchError(error, this.searchBuildings, cursor)); if (usedSearchText !== this.latestSearchText) { return; } if (data) { this.previousSearchFailed = false; this.propertyCursor = data.meta.cursor.next; if (data.meta.cursor.prev === "") { this.tableData = this.generateNewTableDataObject({ tableType: TableType.Properties, propertyListData: data.response, }); } else { const concatedPropertyListData = (_b = this.tableData.propertyListData) === null || _b === void 0 ? void 0 : _b.concat(data.response); this.tableData = this.generateNewTableDataObject({ tableType: TableType.Properties, propertyListData: concatedPropertyListData, }); } } } _overviewListener(e) { this.backCounter = 1; switch (e.detail.searchType) { case SearchType.Streets: this.tableData = this.generateNewTableDataObject({ tableType: TableType.Streets, searchType: SearchType.Streets, }); break; case SearchType.Projects: this.tableData = this.generateNewTableDataObject({ tableType: TableType.Projects, searchType: SearchType.Projects, }); this.clickedItems.clickedOverview = true; } } _streetListener(e) { this.backCounter = 1; this.tableData = this.generateNewTableDataObject({ selectedStreet: e.detail.street, }); this.isLoading = true; this.searchAdresses(1).finally(() => (this.isLoading = false)); this.clickedItems.clickedStreet = true; } _streetLoadListener() { var _a; if (!this.tableData.streetListData) { this.isLoadingMoreData = false; return; } const streetPagesLoaded = (_a = this.tableData.streetListData) === null || _a === void 0 ? void 0 : _a.length; if (!(streetPagesLoaded % this.config.pageSizes.streets === 0)) { this.isLoadingMoreData = false; return; } const page = streetPagesLoaded / this.config.pageSizes.streets; this.isLoadingMoreData = true; this.searchStreets(page + 1).finally(() => (this.isLoadingMoreData = false)); } _addressListener(e) { this.backCounter = 1; const address = e.detail.address; this.tableData = this.generateNewTableDataObject({ tableType: TableType.Addresses, selectedAddress: address, }); this.isLoading = true; this.searchBuildings(1).finally(() => (this.isLoading = false)); this.clickedItems.clickedAddress = true; } _addressLoadListener() { var _a; if (!this.tableData.addressListData) { return; } const addressPagesLoaded = (_a = this.tableData.addressListData) === null || _a === void 0 ? void 0 : _a.length; if (!(addressPagesLoaded % this.addressPageSize === 0)) { return; } const page = addressPagesLoaded / this.addressPageSize; this.searchAdresses(page + 1); } _buildingListener(e) { this.backCounter = 1; const building = e.detail.building; this.tableData = this.generateNewTableDataObject({ tableType: TableType.Buildings, selectedBuilding: building, }); if (this.tableData.selectedAddress) { this.isLoading = true; this.searchProperties().finally(() => (this.isLoading = false)); this.clickedItems.clickedBuilding = true; } } _buildingsLoadListener() { var _a; if (!this.tableData.buildingListData) { return; } const buildingPagesLoaded = (_a = this.tableData.buildingListData) === null || _a === void 0 ? void 0 : _a.length; if (!(buildingPagesLoaded % this.config.pageSizes.buildings === 0)) { return; } const page = buildingPagesLoaded / this.config.pageSizes.buildings; this.searchBuildings(page + 1); } _propertyListener() { this.backCounter = 1; this.tableData = this.generateNewTableDataObject({ tableType: TableType.None, }); this.clickedItems = {}; this.removeText(); } _propertiesLoadListener() { if (this.propertyCursor === "") { return; } this.isLoadingMoreData = true; this.searchProperties(this.propertyCursor).finally(() => (this.isLoadingMoreData = false)); } _backListener() { if (this.tableData.tableType) { switch (this.tableData.tableType) { case TableType.Properties: this.clickedItems.clickedBuilding = false; break; case TableType.Buildings: this.clickedItems.clickedAddress = false; break; case TableType.Addresses: this.clickedItems.clickedStreet = false; break; case TableType.Streets: this.clickedItems.clickedOverview = false; break; } this.tableData = this.generateNewTableDataObject({ tableType: this.tableData.tableType - this.backCounter, }); this.backCounter = 1; } } dispatchTokenExpiredEvent() { const options = { bubbles: true, composed: true, }; this.dispatchEvent(new CustomEvent("token-expired", options)); } generateNewTableDataObject({ searchType = undefined, tableType = undefined, selectedStreet = undefined, selectedAddress = undefined, selectedBuilding = undefined, streetListData = undefined, addressListData = undefined, buildingListData = undefined, propertyListData = undefined, projectListData = undefined, }) { const newData = Object.assign({}, this.tableData); if (typeof tableType === "number") { newData.tableType = tableType; } if (typeof searchType === "number") { newData.searchType = searchType; } if (selectedStreet) { newData.selectedStreet = selectedStreet; } if (selectedAddress) { newData.selectedAddress = selectedAddress; } if (selectedBuilding) { newData.selectedBuilding = selectedBuilding; } if (streetListData) { newData.streetListData = streetListData; } if (addressListData) { newData.addressListData = addressListData; } if (buildingListData) { newData.buildingListData = buildingListData; } if (propertyListData) { newData.propertyListData = propertyListData; } if (projectListData) { newData.projectListData = projectListData; } return newData; } getNewTokenThenCallInterruptedSearch() { this.getAndSetAccessToken(); this.callWaitingSearch(); } async getAndSetAccessToken() { if (!this.config.retrieveTokenEndpoint) { throw new Error("IntegrationPartnerSetsAccessToken flag is not set, but retrieveTokenEndpoint is not provided. Component has no way to get access tokens."); } const access_token = await getAccessToken(this.config.idObject, this.config.retrieveTokenEndpoint); this.config.access_token = access_token; this.requestUpdate(); } callWaitingSearch() { if (this.waitingFunction) { const func = this.waitingFunction[0]; const parameters = this.waitingFunction[1]; const bindedFunc = func.bind(this); if (parameters) { bindedFunc(parameters); } else { bindedFunc(); } this.waitingFunction = undefined; } } resultsOrLoading() { var _a; if (this.isLoading) return html ` <loading-table></loading-table> `; return html ` <results-table style=${"display: block"} .tableData=${this.tableData} .streetPageSize=${(_a = this.config.pageSizes) === null || _a === void 0 ? void 0 : _a.streets} .searchText=${this.searchText} .isLoadingMoreData=${this.isLoadingMoreData} .languageObject=${this.languageObject} ></results-table> `; } render() { var _a; if (this.config.access_token) { return html ` <div class="search-container"> <div class="search-box"> <img src=${loupeIcon} class="icons" /> <input @input=${this.handleOnSearchEvent} type="text" aria-label="Search" aria-describedby="search-box" id="searchText" placeholder=${this.config.placeholderText ? this.config.placeholderText : (_a = this.languageObject) === null || _a === void 0 ? void 0 : _a.search.placeholder} /> <img src=${removeIcon} class="icons remove-icon" @click=${() => this.removeText()} /> </div> <div class="results-container" @overview-clicked=${this._overviewListener} @street-clicked=${this._streetListener} @address-clicked=${this._addressListener} @building-clicked=${this._buildingListener} @back-clicked=${this._backListener} @property-confirmed=${this._propertyListener} @load-more-streets=${this._streetLoadListener} @load-more-addresses=${this._addressLoadListener} @load-more-properties=${this._propertiesLoadListener} @load-more-buildings=${this._buildingsLoadListener} > ${this.resultsOrLoading()} </div> </div> `; } else { return html ``; } } } BoligmappaSearch.styles = css ` * { font-family: var(--search-font-family); color: var(--search-text-color); } .search-loader { display: none; float: right; margin-top: 16px; margin-right: 18px; } .main { border: 0.1em solid grey; border-radius: 1em; padding: 4em; height: 30em; } .search-container { display: flex; position: relative; align-items: center; flex-direction: column; background-color: var(--search-container-background-color); width: var(--search-container-width, 490px); height: var(--search-container-height); min-width: var(--search-container-min-width); max-width: var(--search-container-max-width); margin: var(--search-container-margin, auto); } .search-box { display: flex; align-items: center; box-sizing: border-box; justify-content: space-between; margin: var(--search-bar-margin, auto); width: var(--search-bar-width, 100%); height: var(--search-bar-height, 3rem); padding: var(--search-bar-padding, 0.2rem 1rem); border: var(--search-bar-border, 1px solid ${unsafeCSS(darkGreen)}); border-radius: var(--search-bar-border-radius, 1.5rem); background-color: var(--search-bar-background-color, white); margin-bottom: var(--search-bar-margin-bottom); box-shadow: var(--search-bar-box-shadow, 0 0 5px #ccc); } .results-container { position: absolute; top: 100%; width: var(--search-results-container-width, 100%); z-index: var(--search-results-z-index, 100); margin-top: var(--search-results-margin-top, 0.3em); } .icons { height: 40%; filter: invert(40%) sepia(10%) saturate(8%) hue-rotate(328deg) brightness(95%) contrast(92%); } .remove-icon { opacity: 0.4; } .remove-icon:hover { opacity: 1; cursor: pointer; } textarea, input { border: none; outline: none; width: 100%; height: 100%; padding: 0 0.5rem; } `; __decorate([ state() ], BoligmappaSearch.prototype, "isLoading", void 0); __decorate([ state() ], BoligmappaSearch.prototype, "isLoadingMoreData", void 0); __decorate([ state() ], BoligmappaSearch.prototype, "searchText", void 0); __decorate([ state() ], BoligmappaSearch.prototype, "tableData", void 0); __decorate([ property() ], BoligmappaSearch.prototype, "development", void 0); __decorate([ property() ], BoligmappaSearch.prototype, "textField", void 0); __decorate([ property() ], BoligmappaSearch.prototype, "config", void 0); __decorate([ state() ], BoligmappaSearch.prototype, "clickedItems", void 0); __decorate([ eventOptions({ passive: true }) ], BoligmappaSearch.prototype, "handleOnSearchEvent", null); customElements.define("boligmappa-search", BoligmappaSearch); //# sourceMappingURL=BoligmappaSearch.js.map