Search through local collection of objects
vanilla.html
react.html
vue.html
common.css
common.ts
variables.css
variables.ts
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
import type {MMapSearchControlProps} from '@mappable-world/mappable-default-ui-theme';
import type {SuggestResponseItem} from '@mappable-world/mappable-types';
import {COMMON_LOCATION_PARAMS, type ExtendedFeature, searchFromList} from './common';
import {LOCAL_LIST, LOCATION} from '../variables';
interface InfoMessageProps {
text: string;
}
window.map = null;
main();
async function main() {
// Waiting for all api elements to be loaded
await mappable.ready;
const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControl} = mappable;
const {MMapSearchControl, MMapDefaultMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme');
// Initialize the map
map = new MMap(
// Pass the link to the HTMLElement of the container
document.getElementById('app'),
// Pass the map initialization parameters
{location: LOCATION, showScaleInCopyrights: true},
// Add a map scheme layer
[new MMapDefaultSchemeLayer({}), new MMapDefaultFeaturesLayer({})]
);
class InfoMessageClass extends mappable.MMapComplexEntity<InfoMessageProps> {
private _element!: HTMLDivElement;
private _detachDom!: () => void;
// Method for create a DOM control element
_createElement(props: InfoMessageProps) {
// Create a root element
const infoWindow = document.createElement('div');
infoWindow.classList.add('info-window');
infoWindow.innerText = props.text;
return infoWindow;
}
// Method for attaching the control to the map
_onAttach() {
this._element = this._createElement(this._props);
this._detachDom = mappable.useDomContext(this, this._element, this._element);
}
// Method for detaching control from the map
_onDetach() {
this._detachDom();
this._detachDom = undefined;
this._element = undefined;
}
}
const search: MMapSearchControlProps['search'] = ({params}) => {
return searchFromList(params.uri, LOCAL_LIST);
};
const suggest: MMapSearchControlProps['suggest'] = ({text}) => {
return searchFromList(text, LOCAL_LIST).map(
(object: ExtendedFeature) =>
({
uri: object.properties.name,
title: {
text: object.properties.name
},
subtitle: {
text: object.properties.id
}
} as SuggestResponseItem)
);
};
const searchResult: MMapSearchControlProps['searchResult'] = (searchResult) => {
map.update({location: {center: searchResult[0].geometry.coordinates, ...COMMON_LOCATION_PARAMS}});
};
const searchControl = new MMapSearchControl({
placeholder: 'Search from your objects',
search,
searchResult,
suggest
});
map.addChild(
new MMapControls(
{
position: 'top right'
},
[searchControl]
)
);
const control = new MMapControl({transparent: true}).addChild(
new InfoMessageClass({text: 'This is what users see'})
);
map.addChild(new MMapControls({position: 'top left'}, [control]));
const listElement = document.getElementById('list');
LOCAL_LIST.forEach((object) => {
map.addChild(
new MMapDefaultMarker({
iconName: object.properties.iconName,
coordinates: object.geometry.coordinates,
title: object.properties.name,
size: 'normal'
})
);
const listItem = document.createElement('li');
listItem.classList.add('list_item');
const listItemIcon = document.createElement('div');
listItemIcon.classList.add('list_item__icon');
listItemIcon.innerText = object.properties.id;
const listItemText = document.createElement('div');
listItemText.classList.add('list_item__text');
listItemText.innerText = object.properties.name;
listItem.appendChild(listItemIcon);
listItem.appendChild(listItemText);
listElement.appendChild(listItem);
});
}
</script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div class="container">
<div class="list">
<div class="list__title">Your local list of objects</div>
<ul class="list__items" id="list"></ul>
</div>
<div id="app" class="map"></div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@17/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
import type {MMapSearchControlProps} from '@mappable-world/mappable-default-ui-theme';
import type {SuggestResponseItem} from '@mappable-world/mappable-types';
import {LOCATION, LOCAL_LIST} from '../variables';
import {COMMON_LOCATION_PARAMS, type ExtendedFeature, searchFromList} from './common';
window.map = null;
main();
async function main() {
const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]);
const reactify = mappableReact.reactify.bindTo(React, ReactDOM);
const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControl} = reactify.module(mappable);
const {MMapSearchControl, MMapDefaultMarker} = await reactify.module(
await mappable.import('@mappable-world/mappable-default-ui-theme')
);
const {useCallback} = React;
function App() {
const search: MMapSearchControlProps['search'] = useCallback(({params}) => {
return searchFromList(params.uri, LOCAL_LIST);
}, []);
const suggest: MMapSearchControlProps['suggest'] = useCallback(({text}) => {
return searchFromList(text, LOCAL_LIST).map(
(object: ExtendedFeature) =>
({
uri: object.properties.name,
title: {
text: object.properties.name
},
subtitle: {
text: object.properties.id
}
} as SuggestResponseItem)
);
}, []);
const searchResult: MMapSearchControlProps['searchResult'] = useCallback((searchResult) => {
map.update({location: {center: searchResult[0].geometry.coordinates, ...COMMON_LOCATION_PARAMS}});
}, []);
return (
<div className="container">
<div className="list">
<span className="list__title">Your local list of objects</span>
<ul className="list__items">
{LOCAL_LIST.map((object) => (
<li className="list_item" key={object.properties.id}>
<div className="list_item__icon">{object.properties.id}</div>
<div className="list_item__text">{object.properties.name}</div>
</li>
))}
</ul>
</div>
<div className="map">
<MMap location={LOCATION} showScaleInCopyrights={true} ref={(x) => (map = x)}>
<MMapDefaultSchemeLayer />
<MMapDefaultFeaturesLayer />
<MMapControls position="top left">
<MMapControl transparent>
<div className="info-window">This is what users see</div>
</MMapControl>
</MMapControls>
<MMapControls position="top right">
<MMapSearchControl
placeholder="Search from your objects"
suggest={suggest}
search={search}
searchResult={searchResult}
/>
</MMapControls>
{LOCAL_LIST.map((marker) => (
<MMapDefaultMarker
key={marker.properties.id}
coordinates={marker.geometry.coordinates}
iconName={marker.properties.iconName}
title={marker.properties.name}
size="normal"
/>
))}
</MMap>
</div>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('app')
);
}
</script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel"></script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
.info-window {
padding: 8px 12px 8px 40px;
border-radius: 12px;
background-color: #313133;
background-image: url('./info-icon.svg');
background-position: 10px 8px;
background-repeat: no-repeat;
color: #f2f5fa;
font-size: 14px;
line-height: 20px;
min-width: max-content;
}
.container {
width: 100%;
height: 100%;
display: flex;
gap: 10px;
}
.map {
border-radius: 8px;
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: hidden;
}
.list {
width: 260px;
flex-shrink: 0;
border-radius: 8px;
border: 1px solid rgba(92, 94, 102, 0.14);
padding: 12px;
box-sizing: border-box;
}
.list__title {
line-height: 20px;
font-weight: 500;
font-size: 14px;
color: #000;
}
.list__items {
list-style: none;
margin-top: 24px;
padding: 0;
}
.list_item {
display: flex;
flex-direction: row;
gap: 8px;
margin-top: 12px;
}
.list_item__icon {
width: 16px;
height: 16px;
border-radius: 4px;
text-align: center;
background-color: var(--icon-background-color);
color: #ffffff;
font-weight: 700;
font-size: 11px;
line-height: 16px;
}
.list_item__text {
font-size: 14px;
}
import type {Feature} from '@mappable-world/mappable-types';
import type {MMapDefaultMarkerProps} from '@mappable-world/mappable-default-ui-theme';
import type {MMapLocationRequest} from '@mappable-world/mappable-types/imperative/MMap';
export type ExtendedFeature = {
properties: Feature['properties'] & {
id: string;
iconName: MMapDefaultMarkerProps['iconName'];
};
geometry: Feature['geometry'];
};
mappable.ready.then(() => {
mappable.import.registerCdn(
'https://cdn.jsdelivr.net/npm/{package}',
'@mappable-world/mappable-default-ui-theme@0.0'
);
});
export const COMMON_LOCATION_PARAMS: MMapLocationRequest = {easing: 'ease-in-out', duration: 2000, zoom: 16};
export function searchFromList(text: string, objects: ExtendedFeature[]): Feature[] {
const trimmedText = text.trim().toLowerCase();
return objects.filter((object) => {
const trimmedObjectName = object.properties.name.trim().toLowerCase();
const trimmedObjectDescription = object.properties.id.trim().toLowerCase();
return trimmedObjectDescription.includes(trimmedText) || trimmedObjectName.includes(trimmedText);
});
}
:root {
--icon-background-color: rgba(49, 49, 51, 1);
}
import type {MMapCenterZoomLocation} from '@mappable-world/mappable-types';
import type {ExtendedFeature} from './common';
export const LOCATION: MMapCenterZoomLocation = {
center: [55.2811, 25.2239],
zoom: 13.5
};
export const LOCAL_LIST: ExtendedFeature[] = [
{
properties: {
name: 'School',
id: '1',
iconName: 'college',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2856, 25.2116]
}
},
{
properties: {
name: 'Hospital',
id: '2',
iconName: 'hospital',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2785, 25.2362]
}
},
{
properties: {
name: 'Shopping mall',
id: '3',
iconName: 'malls',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2931, 25.2351]
}
},
{
properties: {
name: 'Business center',
id: '4',
iconName: 'office',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2846, 25.2489]
}
},
{
properties: {
name: 'Sport center',
id: '5',
iconName: 'sportcenter',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2689, 25.1928]
}
},
{
properties: {
name: 'University',
id: '6',
iconName: 'college',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2575, 25.2182]
}
},
{
properties: {
name: 'Spa and beauty salon',
id: '7',
iconName: 'spa',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.318, 25.2096]
}
},
{
properties: {
name: 'Boat',
id: '8',
iconName: 'boat_station',
description: null
},
geometry: {
type: 'Point',
coordinates: [55.2356, 25.2115]
}
}
];