@opencage/geosearch-core
Version:
Core client for OpenCage GeoSearch
243 lines (218 loc) • 7.86 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.opencage = {}));
})(this, (function (exports) { 'use strict';
const isString = (value) => {
const type = typeof value;
return type === 'string';
};
const checkResponseStatus = (res) => {
if (res.ok) {
return res;
}
const err = new Error(
`The HTTP status of the reponse: ${res.status} (${res.statusText})`
);
err.status = res.status;
err.statusText = res.statusText;
throw err;
};
const debouncePromise = (fn, time) => {
let timerId;
return (...args) => {
if (timerId) {
clearTimeout(timerId);
}
return new Promise((resolve) => {
timerId = setTimeout(() => resolve(fn(...args)), time);
});
};
};
const uniqByKeepFirst = (a, key) => {
const seen = new Set();
return a.filter((item) => {
const k = key(item);
return seen.has(k) ? false : seen.add(k);
});
};
const htmlFooter = `
<div style="display:flex;flex-direction:column;align-items:flex-end;">
<div
style="display:flex;align-items:center;justify-content:center;font-family: sans-serif;font-size:0.7em;color:#009966;">
<span><a href="https://opencagedata.com/geosearch" target="_blank" rel="noreferrer"><img
src="https://assets.opencagedata.com/opencage-20x21.png" height="21" width="20" border="0" alt="OpenCage"
style="display:inline;" /></a></span>
<span> Made by <a href="https://opencagedata.com/geosearch" target="_blank" rel="noreferrer"
style="text-decoration:none;color:#009966;">OpenCage</a>.</span>
<span> © <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noreferrer"
style="text-decoration:none;color:#009966;">OpenStreetMap</a>,</span>
<span> <a href="https://opencagedata.com/credits" target="_blank" rel="noreferrer"
style="text-decoration:none;color:#009966;">others</a>.</span>
</div>
</div>
`;
const SOURCE_ID = 'opencage';
const AWAIT_LABEL = '. . .';
const AWAIT_USER_INPUT = { results: [{ formatted: AWAIT_LABEL }] };
const fn = () => {};
const handleResult = (
{ results: returnedResults },
{ onActive = fn, onSelect = fn, noResults = 'No results.' } = {}
) => {
// filter, to dedupe , results on the attribute `formatted`
const results = uniqByKeepFirst(returnedResults, (it) => it.formatted);
return [
{
sourceId: SOURCE_ID,
//
// ---- getItems
// type: (params: { query: string, state: AutocompleteState, ...setters }) => Item[] | Promise<Item[]>
getItems() {
return results;
},
//
// ---- getItemUrl
// type: (params: { item: Item, state: AutocompleteState }) => string | undefined
// getItemUrl() {
// }
//
// ---- getItemInputValue
// type: (params: { item, state: AutocompleteState }) => string` | defaults to `({ state }) => state.query
getItemInputValue({ item }) {
return item.formatted;
},
//
// ---- onSelect
// type: (params: { state: AutocompleteState, ...setters, event: Event, item: TItem, itemInputValue: string, itemUrl: string, source: AutocompleteSource }) => void` | defaults to `({ setIsOpen }) => setIsOpen(false)
onSelect,
//
// ---- onActive
// type: (params: { state: AutocompleteState, ...setters, event: Event, item: TItem, itemInputValue: string, itemUrl: string, source: AutocompleteSource }) => void
onActive,
// ----
templates: {
item({ item }) {
return `${item.formatted}`;
},
noResults() {
return noResults;
},
footer({ createElement }) {
// type: (params: { state: AutocompleteState<TItem>, source: AutocompleteSource<TItem>, items: TItem[], createElement: Pragma, Fragment: PragmaFrag }) => VNode | string
return createElement('div', {
dangerouslySetInnerHTML: {
__html: htmlFooter,
},
});
},
},
// ...
},
];
};
const buildURL = (url, options) => {
let result = url;
if (!options) return result;
// if (options.key) {
// result = `${result}&key=${options.key}`;
// }
if (options.limit) {
result = `${result}&limit=${options.limit}`;
}
if (options.countrycode) {
result = `${result}&countrycode=${options.countrycode}`;
}
if (options.language) {
result = `${result}&language=${options.language}`;
}
if (options.bounds) {
result = `${result}&bounds=${options.bounds}`;
}
return result;
};
const OpenCageGeoSearchPlugin = (
options = { debounce: 300, noResults: 'No results.' },
events = {}
) => {
const fn = () => {};
let selectedItem = null;
const onSelect = (params) => {
selectedItem = params.item;
if (events.onSelect) events.onSelect(params);
};
const onActive = events.onActive || fn;
const onSubmit = events.onSubmit || fn;
const debouncedFetch = debouncePromise(fetch, options.debounce);
return {
async getSources({ query }) {
// if (!window.fetch) {
// console.warn('Please Contact the developer of this website!');
// }
if (query === '') {
selectedItem = null;
return [];
}
if (!isString(query)) {
console.debug('Not a string');
selectedItem = null;
return [];
}
if (query.length < 3) {
console.debug('Query is too short');
selectedItem = null;
return handleResult(AWAIT_USER_INPUT, {
noResults: options.noResults,
onActive,
onSelect,
});
}
if (selectedItem)
return handleResult(
{ results: [selectedItem] },
{ noResults: options.noResults, onActive, onSelect }
);
const url = buildURL(
`https://api.opencagedata.com/geosearch?q=${query}`,
options
);
const headers = {
'OpenCage-Geosearch-Key': options.key,
};
return debouncedFetch(url, { headers, mode: 'cors' })
.then(checkResponseStatus)
.then((response) => {
if (response.ok) return response.json();
console.log(response.statusText);
throw response.statusText;
})
.then((apiResponse) =>
handleResult(apiResponse, {
noResults: options.noResults,
onActive,
onSelect,
})
)
.catch((err) => {
console.error(`[Opencage GeoSearch error]: ${err.message}`);
console.error(`[error] status: ${err.status}`);
console.error(`[error] statusText: ${err.statusText}`);
return [];
});
},
// ---- onSubmit
// type: (params: { state: AutocompleteState, event: Event, ...setters }) => void
onSubmit,
// ---- onReset
// type: (params: { state: AutocompleteState, event: Event, ...setters: AutocompleteSetters }) => void
onReset: () => {
selectedItem = null;
},
};
};
var index = { OpenCageGeoSearchPlugin };
exports.OpenCageGeoSearchPlugin = OpenCageGeoSearchPlugin;
exports.default = index;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=opencage-geosearch-core.js.map