Skip to main content

HERE Maps Adress Search

UPDATED: | POSTED: | by Stefan Schüttenkopf


Requirements

✓ HERE Maps API Key
This sample gives you the possibility to use HERE Maps address search in your Browser Application respectively within your Dashboard application

Result of the implementation of the code snippet should contain the following points and should look like the following screenshot:

✓ Input field for the adress
✓ Different font sizes for name / adress
✓ Setting the number of maximum results
✓ Visualize the result as a pin
HERE Maps Adress search results
Visualization of the result in a 3D map

Custom Script

Browser JS

import { Bounds } from '@luciad/ria/shape/Bounds';
import { CoordinateReference } from '@luciad/ria/reference/CoordinateReference';
import * as ReferenceProvider from '@luciad/ria/reference/ReferenceProvider';
import * as TransformationFactory from '@luciad/ria/transformation/TransformationFactory';
import { Feature } from '@luciad/ria/model/feature/Feature';
import { FeatureModel } from '@luciad/ria/model/feature/FeatureModel';
import { MemoryStore } from '@luciad/ria/model/store/MemoryStore';
import * as ShapeFactory from '@luciad/ria/shape/ShapeFactory';
import { BasicFeaturePainter } from '@luciad/ria/view/feature/BasicFeaturePainter';
import { FeatureLayer } from '@luciad/ria/view/feature/FeatureLayer';
import { OcclusionMode } from '@luciad/ria/view/style/OcclusionMode';


const wgs84Ref = ReferenceProvider.getReference('EPSG:4326');
const mapToWgs84Transformation = TransformationFactory.createTransformation(window.map.reference, wgs84Ref);

const map = window.map;
const MAX_RESULTS = 10;

function showPointOfInterestLayer(name: string, label: string, visible: boolean, coords: number[], ref: CoordinateReference, fitBounds: any) {
    const oldLayer = map.layerTree.findLayerById(name);

    oldLayer && map.layerTree.removeChild(oldLayer);

    const location = ShapeFactory.createPoint(ref, [coords[1], coords[0]]);
    location.z = 0;

    let wgs84Bounds: Bounds;

    //If HERE sends boundingbox, use it. Else create 1km GeoBuffer around point as bounding box to fit to.
    if(fitBounds && fitBounds.west) {
        wgs84Bounds = ShapeFactory.createBounds(wgs84Ref, [fitBounds.west, fitBounds.east - fitBounds.west, fitBounds.south, fitBounds.north - fitBounds.south]);
    } else {
        const geoBuffer = ShapeFactory.createGeoBuffer(wgs84Ref, ShapeFactory.createPolyline(wgs84Ref, [location]), 1000);
        wgs84Bounds = geoBuffer.bounds;
    }

    map.mapNavigator.fit({
        bounds: wgs84Bounds,
        animate: false
    });

    const pHeight = ShapeFactory.createPoint(ref, [location.x, location.y, location.z + 50]);
    const verticalLine = ShapeFactory.createPolyline(ref, [location, pHeight]);
    const imagePoint = ShapeFactory.createPoint(ref, [location.x, location.y, location.z + 50]);

    const feature = new Feature(location, {}, 1);
    const store = new MemoryStore({
        data: [feature]
    });

    const featureModel = new FeatureModel(store, { reference: ref });

    const icon3dStylePainter = new BasicFeaturePainter();

    icon3dStylePainter.paintBody = function (geoCanvas) {
        geoCanvas.drawShape(verticalLine, {
            stroke: {
                color: '#de2d26',
                width: 3
            },
            occlusionMode: OcclusionMode.VISIBLE_ONLY,
            draped: false
        });

        geoCanvas.drawIcon(imagePoint, {
            draped: false,
            image: createPinIcon(30, '#de2d26', '1', 'white'),
            anchorY: '30px',
            occlusionMode: OcclusionMode.ALWAYS_VISIBLE
        });
    };

    const featureLayer = new FeatureLayer(featureModel, { id: name, label: label, painter: icon3dStylePainter, visible: visible });

    map.layerTree.addChild(featureLayer);
}

class SearchPanel {
    private domElement: HTMLDivElement;
    private inputElement: HTMLInputElement;
    private inputCleanButton: HTMLSpanElement;
    private loaderElement: HTMLDivElement;
    private resultsCountElement: HTMLSpanElement;
    private resultsElement: HTMLSpanElement;
    private requestTime: number;

    constructor(private hereSearchService, private showPin: any, private hideLayer: any) {
        const rootEle = document.createElement('div');
        rootEle.id = 'search-panel';
        rootEle.className = 'hide empty';
        map.domNode.appendChild(rootEle);

        const searchInputPanelEle = document.createElement('div');
        searchInputPanelEle.id = 'search-input-panel';
        rootEle.appendChild(searchInputPanelEle);

        const searchEle = document.createElement('input');
        searchEle.id = 'search';
        searchEle.type = 'search';
        searchEle.autocomplete = 'off';
        searchEle.placeholder = 'Address, Place, POI, ..';
        searchInputPanelEle.appendChild(searchEle);

        const searchClearEle = document.createElement('span');
        searchClearEle.id = 'search-clear';
        searchClearEle.innerHTML = 'x';
        searchInputPanelEle.appendChild(searchClearEle);

        const searchResultsDescPanelEle = document.createElement('div');
        searchResultsDescPanelEle.id = 'search-results-desc-panel';
        rootEle.appendChild(searchResultsDescPanelEle);

        const searchResultsDescEle = document.createElement('div');
        searchResultsDescEle.id = 'search-results-desc';
        searchResultsDescPanelEle.appendChild(searchResultsDescEle);

        const searchResultsCountEle = document.createElement('span');
        searchResultsCountEle.id = 'search-results-count';
        searchResultsCountEle.textContent = '0';
        searchResultsCountEle.style.display = 'none';
        searchResultsDescEle.appendChild(searchResultsCountEle);

        const searchResultsLoaderEle = document.createElement('div');
        searchResultsLoaderEle.id = 'search-results-loader';
        searchResultsLoaderEle.className = 'preloader-dots dot hide';
        searchResultsDescPanelEle.appendChild(searchResultsLoaderEle);

        for (let i = 0; i < 5; i++) {
            const dotEle = document.createElement('div');
            dotEle.className = 'dot';
            searchResultsLoaderEle.appendChild(dotEle);
        }

        const searchResultsEle = document.createElement('div');
        searchResultsEle.className = 'search-results';
        rootEle.appendChild(searchResultsEle);

        this.domElement = rootEle;
        this.inputElement = searchEle;
        this.inputCleanButton = searchClearEle;
        this.loaderElement = searchResultsLoaderEle;
        this.resultsCountElement = searchResultsCountEle;
        this.resultsElement = searchResultsEle;

        this.inputElement.addEventListener('input', () => this.onInput());
        this.inputCleanButton.addEventListener('click', () => this.onInputClean());

        this.domElement.classList.remove('hide');
    }

    private async onInput() {
        if (this.inputElement.value === '') {
            this.domElement.classList.add('empty');
            this.resultsCountElement.innerText = '0';
            this.loaderElement.classList.add('hide');
            this.resultsElement.innerHTML = '';
            this.inputCleanButton.click();

            return;
        }

        this.domElement.classList.remove('empty');
        this.loaderElement.classList.remove('hide');

        this.requestTime = Date.now();
        const thatRequestTime = this.requestTime;
        const autosuggestItems = await this.performHereQuery(this.inputElement.value);
        if (this.requestTime !== thatRequestTime) {
            return;
        }

        this.loaderElement.classList.add('hide');
        this.renderResults(autosuggestItems);
    }

    private renderResults(results: any) {
        this.resultsCountElement.innerText = results.length + "";

        const fragment = document.createDocumentFragment();

        for (const item of results) {
            fragment.appendChild(this.createRowElement(item));
        }

        this.resultsElement.innerHTML = '';
        this.resultsElement.appendChild(fragment);
    }

    private createRowElement(item: any) {
        const resultElement = document.createElement('div');
        const resultElementHeader = document.createElement('div');
        const resultElementSubtitle = document.createElement('div');
        resultElementSubtitle.classList.add('search-result-subtitle');
        resultElementHeader.classList.add('search-result-header');
        resultElement.classList.add('search-result-row');
        //resultElement.innerText = item.title;

        resultElementHeader.innerText = item.title;
        if(item.address?.label) {
            resultElementSubtitle.innerText = item.address.label;
        }

        resultElement.title = `${item.title} \n${item.address?.label ? item.address.label : ''}`;
        //resultElement.title = "AXSAXAXAAX" + item.title + "
" + item.address?.label ? item.address.label : '';

        resultElement.appendChild(resultElementHeader);
        resultElement.appendChild(resultElementSubtitle);

        resultElement.addEventListener('click', () => {
            const oldSelectedRow = this.domElement.querySelector('.search-result-row.selected');
            oldSelectedRow && oldSelectedRow.classList.remove('selected');
            resultElement.classList.add('selected');

            this.showPin([item.position.lat, item.position.lng], item.mapView, wgs84Ref); 
        });

        return resultElement;
    }

    private async performHereQuery(searchInput: string) {

        let lon, lat;

        try {
            const viewCenterPoint = window.map.viewToMapTransformation.transform(ShapeFactory.createPoint(null, [window.map.viewSize[0]/2, window.map.viewSize[1]/2]));
            
            const wgs84Point = mapToWgs84Transformation.transform(viewCenterPoint);
            lon = wgs84Point.x;
            lat = wgs84Point.y;
        } catch(e) {
            lon = 0;
            lat = 0;
        }

        const response = await this.hereSearchService.autosuggest({
                // Search query
                q: searchInput,
                // Center of the search context
                at: `${lat},${lon}`
            }, (result) => {
        }, console.error);

        return response.items;
    }

    private onInputClean() {
        this.inputElement.value = '';

        this.hideLayer();
        this.onInput();
    }
}

function createPinIcon(size, color, text, textColor) {
    const canvas = document.createElement('canvas');
    canvas.width = size;
    canvas.height = size * 2;
    const ctx = canvas.getContext('2d');

    // circle
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.fill();

    // text
    ctx.fillStyle = textColor;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(text, size / 2, size / 2);

    return canvas;
}

function loadExternalLibraries(urls: string[]): Promise {
    return Promise.all(urls.map((u) => loadScript(u)));
}

function loadScript(url: string): Promise {
    const head = document.getElementsByTagName('head')[0];
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.charset = 'utf-8';
    script.src = url;

    const pr = new Promise((resolve, reject) => {
        script.onload = resolve;
    });

    head.appendChild(script);
    return pr;
}


function main() {

    /*HERE Setup*/
    const herePlatform = new H.service.Platform({
    'apikey': 'API_KEY_GOES_HERE'
    });
    const hereSearchService = herePlatform.getSearchService();
    /************/

    new SearchPanel(hereSearchService,
        (coords: number[], fitBounds: any, ref: CoordinateReference) => {
            showPointOfInterestLayer('poi', 'POI', true, coords, ref, fitBounds);
        },
        () => {
            removeLayerById('poi');
        }
    );
}

ready(() => {
    loadExternalLibraries([
        'https://js.api.here.com/v3/3.1/mapsjs-core.js'
    ]).then(() => {
        loadExternalLibraries([
            'https://js.api.here.com/v3/3.1/mapsjs-service.js'
        ]).then(() => {
            main();
        });
    });
});

Dashboard JS

import { Bounds } from '@luciad/ria/shape/Bounds';
import { CoordinateReference } from '@luciad/ria/reference/CoordinateReference';
import * as ReferenceProvider from '@luciad/ria/reference/ReferenceProvider';
import * as TransformationFactory from '@luciad/ria/transformation/TransformationFactory';
import { Feature } from '@luciad/ria/model/feature/Feature';
import { FeatureModel } from '@luciad/ria/model/feature/FeatureModel';
import { MemoryStore } from '@luciad/ria/model/store/MemoryStore';
import * as ShapeFactory from '@luciad/ria/shape/ShapeFactory';
import { BasicFeaturePainter } from '@luciad/ria/view/feature/BasicFeaturePainter';
import { FeatureLayer } from '@luciad/ria/view/feature/FeatureLayer';
import { OcclusionMode } from '@luciad/ria/view/style/OcclusionMode';


const map = this.getStageModel().mapViews[0].getMap().map;
const poiLayerId = "poi";
const poiLayerLabel ="Poi";

const wgs84Ref = ReferenceProvider.getReference('EPSG:4326');
const mapToWgs84Transformation = TransformationFactory.createTransformation(map.reference, wgs84Ref);

const MAX_RESULTS = 10;

function showPointOfInterestLayer(name: string, label: string, visible: boolean, coords: number[], ref: CoordinateReference, fitBounds: any) {
    const oldLayer = map.layerTree.findLayerById(name);

    oldLayer && map.layerTree.removeChild(oldLayer);

    const location = ShapeFactory.createPoint(ref, [coords[1], coords[0]]);
    location.z = 0;

    let wgs84Bounds: Bounds;

    //If HERE sends boundingbox, use it. Else create 1km GeoBuffer around point as bounding box to fit to.
    if(fitBounds && fitBounds.west) {
        wgs84Bounds = ShapeFactory.createBounds(wgs84Ref, [fitBounds.west, fitBounds.east - fitBounds.west, fitBounds.south, fitBounds.north - fitBounds.south]);
    } else {
        const geoBuffer = ShapeFactory.createGeoBuffer(wgs84Ref, ShapeFactory.createPolyline(wgs84Ref, [location]), 1000);
        wgs84Bounds = geoBuffer.bounds;
    }

    map.mapNavigator.fit({
        bounds: wgs84Bounds,
        animate: false
    });

    const pHeight = ShapeFactory.createPoint(ref, [location.x, location.y, location.z + 50]);
    const verticalLine = ShapeFactory.createPolyline(ref, [location, pHeight]);
    const imagePoint = ShapeFactory.createPoint(ref, [location.x, location.y, location.z + 50]);

    const feature = new Feature(location, {}, 1);
    const store = new MemoryStore({
        data: [feature]
    });

    const featureModel = new FeatureModel(store, { reference: ref });

    const icon3dStylePainter = new BasicFeaturePainter();

    icon3dStylePainter.paintBody = function (geoCanvas) {
        geoCanvas.drawShape(verticalLine, {
            stroke: {
                color: '#de2d26',
                width: 3
            },
            occlusionMode: OcclusionMode.VISIBLE_ONLY,
            draped: false
        });

        geoCanvas.drawIcon(imagePoint, {
            draped: false,
            image: createPinIcon(30, '#de2d26', '1', 'white'),
            anchorY: '30px',
            occlusionMode: OcclusionMode.ALWAYS_VISIBLE
        });
    };

    const featureLayer = new FeatureLayer(featureModel, { id: name, label: label, painter: icon3dStylePainter, visible: visible });

    map.layerTree.addChild(featureLayer);
}

class SearchPanel {
    private domElement: HTMLDivElement;
    private inputElement: HTMLInputElement;
    private inputCleanButton: HTMLSpanElement;
    private loaderElement: HTMLDivElement;
    private resultsCountElement: HTMLSpanElement;
    private resultsElement: HTMLSpanElement;
    private requestTime: number;

    constructor(private hereSearchService, private showPin: any, private hideLayer: any) {
        const rootEle = document.createElement('div');
        rootEle.id = 'search-panel';
        rootEle.className = 'hide empty';
        map.domNode.appendChild(rootEle);

        const searchInputPanelEle = document.createElement('div');
        searchInputPanelEle.id = 'search-input-panel';
        rootEle.appendChild(searchInputPanelEle);

        const searchEle = document.createElement('input');
        searchEle.id = 'search';
        searchEle.type = 'search';
        searchEle.autocomplete = 'off';
        searchEle.placeholder = 'Address, Place, POI, ..';
        searchInputPanelEle.appendChild(searchEle);

        const searchClearEle = document.createElement('span');
        searchClearEle.id = 'search-clear';
        searchClearEle.innerHTML = 'x';
        searchInputPanelEle.appendChild(searchClearEle);

        const searchResultsDescPanelEle = document.createElement('div');
        searchResultsDescPanelEle.id = 'search-results-desc-panel';
        rootEle.appendChild(searchResultsDescPanelEle);

        const searchResultsDescEle = document.createElement('div');
        searchResultsDescEle.id = 'search-results-desc';
        searchResultsDescPanelEle.appendChild(searchResultsDescEle);

        const searchResultsCountEle = document.createElement('span');
        searchResultsCountEle.id = 'search-results-count';
        searchResultsCountEle.textContent = '0';
        searchResultsCountEle.style.display = 'none';
        searchResultsDescEle.appendChild(searchResultsCountEle);

        const searchResultsLoaderEle = document.createElement('div');
        searchResultsLoaderEle.id = 'search-results-loader';
        searchResultsLoaderEle.className = 'preloader-dots dot hide';
        searchResultsDescPanelEle.appendChild(searchResultsLoaderEle);

        for (let i = 0; i < 5; i++) {
            const dotEle = document.createElement('div');
            dotEle.className = 'dot';
            searchResultsLoaderEle.appendChild(dotEle);
        }

        const searchResultsEle = document.createElement('div');
        searchResultsEle.className = 'search-results';
        rootEle.appendChild(searchResultsEle);

        this.domElement = rootEle;
        this.inputElement = searchEle;
        this.inputCleanButton = searchClearEle;
        this.loaderElement = searchResultsLoaderEle;
        this.resultsCountElement = searchResultsCountEle;
        this.resultsElement = searchResultsEle;

        this.inputElement.addEventListener('input', () => this.onInput());
        this.inputCleanButton.addEventListener('click', () => this.onInputClean());

        this.domElement.classList.remove('hide');

        console.log("init done", this);
    }

    private async onInput() {
        if (this.inputElement.value === '') {
            this.domElement.classList.add('empty');
            this.resultsCountElement.innerText = '0';
            this.loaderElement.classList.add('hide');
            this.resultsElement.innerHTML = '';
            this.inputCleanButton.click();

            return;
        }

        this.domElement.classList.remove('empty');
        this.loaderElement.classList.remove('hide');

        this.requestTime = Date.now();
        const thatRequestTime = this.requestTime;
        const autosuggestItems = await this.performHereQuery(this.inputElement.value);
        if (this.requestTime !== thatRequestTime) {
            return;
        }

        this.loaderElement.classList.add('hide');
        this.renderResults(autosuggestItems);
    }

    private renderResults(results: any) {
        this.resultsCountElement.innerText = results.length + "";

        const fragment = document.createDocumentFragment();

        for (const item of results) {
            fragment.appendChild(this.createRowElement(item));
        }

        this.resultsElement.innerHTML = '';
        this.resultsElement.appendChild(fragment);
    }

    private createRowElement(item: any) {
        const resultElement = document.createElement('div');
        const resultElementHeader = document.createElement('div');
        const resultElementSubtitle = document.createElement('div');
        resultElementSubtitle.classList.add('search-result-subtitle');
        resultElementHeader.classList.add('search-result-header');
        resultElement.classList.add('search-result-row');
        //resultElement.innerText = item.title;

        resultElementHeader.innerText = item.title;
        if(item.address?.label) {
            resultElementSubtitle.innerText = item.address.label;
        }

        resultElement.title = `${item.title} \n${item.address?.label ? item.address.label : ''}`;
        //resultElement.title = "AXSAXAXAAX" + item.title + "
" + item.address?.label ? item.address.label : '';

        resultElement.appendChild(resultElementHeader);
        resultElement.appendChild(resultElementSubtitle);

        resultElement.addEventListener('click', () => {
            const oldSelectedRow = this.domElement.querySelector('.search-result-row.selected');
            oldSelectedRow && oldSelectedRow.classList.remove('selected');
            resultElement.classList.add('selected');

            this.showPin([item.position.lat, item.position.lng], item.mapView, wgs84Ref); 
        });

        return resultElement;
    }

    private async performHereQuery(searchInput: string) {

        let lon, lat;

        try {
            const viewCenterPoint = window.map.viewToMapTransformation.transform(ShapeFactory.createPoint(null, [window.map.viewSize[0]/2, window.map.viewSize[1]/2]));
            
            const wgs84Point = mapToWgs84Transformation.transform(viewCenterPoint);
            lon = wgs84Point.x;
            lat = wgs84Point.y;
        } catch(e) {
            lon = 0;
            lat = 0;
        }

        const response = await this.hereSearchService.autosuggest({
                // Search query
                q: searchInput,
                // Center of the search context
                at: `${lat},${lon}`
            }, (result) => {
        }, console.error);

        return response.items;
    }

    private onInputClean() {
        this.inputElement.value = '';

        this.hideLayer();
        this.onInput();
    }
}

function createPinIcon(size, color, text, textColor) {
    const canvas = document.createElement('canvas');
    canvas.width = size;
    canvas.height = size * 2;
    const ctx = canvas.getContext('2d');

    // circle
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.fill();

    // text
    ctx.fillStyle = textColor;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(text, size / 2, size / 2);

    return canvas;
}

function loadExternalLibraries(urls: string[]): Promise {
    return Promise.all(urls.map((u) => loadScript(u)));
}

function loadScript(url: string): Promise {
    const head = document.getElementsByTagName('head')[0];
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.charset = 'utf-8';
    script.src = url;

    const pr = new Promise((resolve, reject) => {
        script.onload = resolve;
    });

    head.appendChild(script);
    return pr;
}


function main() {

    /*HERE Setup*/
    const herePlatform = new H.service.Platform({
    'apikey': 'APIKEY_GOES_HERE'
    });
    const hereSearchService = herePlatform.getSearchService();
    /************/

    new SearchPanel(hereSearchService,
        (coords: number[], fitBounds: any, ref: CoordinateReference) => {
            showPointOfInterestLayer(poiLayerId, poiLayerLabel, true, coords, ref, fitBounds);
        },
        () => {
            const oldLayer = map.layerTree.findLayerById(poiLayerId);
            
            oldLayer && map.layerTree.removeChild(oldLayer);
        }
    );
}


loadExternalLibraries([
    'https://js.api.here.com/v3/3.1/mapsjs-core.js'
]).then(() => {
    loadExternalLibraries([
        'https://js.api.here.com/v3/3.1/mapsjs-service.js'
    ]).then(() => {
        main();
    });
});

Custom Style

Browser CSS

clears the ‘X’ from Internet Explorer */
input[type=search]::-ms-clear { display: none; width : 0; height: 0; }
input[type=search]::-ms-reveal { display: none; width : 0; height: 0; }
/* clears the ‘X’ from Chrome */
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration { display: none; }

div#search-panel {
    position: absolute;
    top: 20px;
    left: 80px;
    background-color: #222;
    opacity: 0.85;
    color: #Fff;
    width: 320px;
    padding: 5px;
    font-size: 14px;
}

input#search {
    width: 100%;
    height: 32px;
    left: 100px;
    background-color: #222;
    opacity: 0.85;
    color: white;
    border: 0;
    border-bottom: 1px solid white;
    outline: none;
}

div#search-results {
    left: 100px;
    background-color: #222;
    opacity: 0.85;
    color: #Fff;
    width: 100%;
    max-height: 600px;
    overflow-y: auto;
}

div#search-results > div {
    padding: 5px;
}

.search-result-subtitle {
    opacity: 0.5;
    font-size: 0.7em;
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-result-header {
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-result-row {
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.search-result-row:hover {
    background-color: #2b3540;
}

.search-result-row.selected {
    background-color: #fff!important;
    color: black;
}

#search-results-desc {
    float: left;
    margin-right: 10px;
}

#search-results-desc-panel {
    overflow: hidden;
    margin: 10px 0;
}

#search-clear {
    position: absolute;
    right: 2px;
    bottom: 8px;
    cursor: pointer;
}

.empty #search-clear {
    display: none;
}

.empty #search-results-desc-panel {
    display: none;
}

#search-input-panel {
    position: relative;
}

.activateButton {
    font-size: 1.5rem;
    padding: 8px;
    width: fit-content;
    cursor: pointer;
    margin-left: 8px;
}

.activateButton.active {
    color: rgb(0, 150, 184);
}

Dashboard CSS

clears the ‘X’ from Internet Explorer */
input[type=search]::-ms-clear { display: none; width : 0; height: 0; }
input[type=search]::-ms-reveal { display: none; width : 0; height: 0; }
/* clears the ‘X’ from Chrome */
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration { display: none; }

div#search-panel {
    position: absolute;
    top: 50px; /* 20px without header */
    left: 80px;
    background-color: #222;
    opacity: 0.85;
    color: #Fff;
    width: 320px;
    padding: 5px;
    font-size: 14px;
}

input#search {
    width: 100%;
    height: 32px;
    left: 100px;
    background-color: #222;
    opacity: 0.85;
    color: white;
    border: 0;
    border-bottom: 1px solid white;
    outline: none;
}

div#search-results {
    left: 100px;
    background-color: #222;
    opacity: 0.85;
    color: #Fff;
    width: 100%;
    max-height: 600px;
    overflow-y: auto;
}

div#search-results > div {
    padding: 5px;
}

.search-result-subtitle {
    opacity: 0.5;
    font-size: 0.7em;
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-result-header {
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-result-row {
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.search-result-row:hover {
    background-color: #2b3540;
}

.search-result-row.selected {
    background-color: #fff!important;
    color: black;
}

#search-results-desc {
    float: left;
    margin-right: 10px;
}

#search-results-desc-panel {
    overflow: hidden;
    margin: 10px 0;
}

#search-clear {
    position: absolute;
    right: 2px;
    bottom: 8px;
    cursor: pointer;
}

.empty #search-clear {
    display: none;
}

.empty #search-results-desc-panel {
    display: none;
}

#search-input-panel {
    position: relative;
}

.activateButton {
    font-size: 1.5rem;
    padding: 8px;
    width: fit-content;
    cursor: pointer;
    margin-left: 8px;
}

.activateButton.active {
    color: rgb(0, 150, 184);
}