1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2025-05-13 21:26:26 +02:00
2023-08-14 17:41:39 +03:00

172 lines
4.2 KiB
JavaScript

/**
* @fileoverview
* In this example, we replace the standard offer list
* with an alternative implementation that shows offers
* as markers on a map
*/
const {
SearchBox,
SearchBoxComposer,
DummyMapApi,
dummyCoffeeApi,
util
} = ourCoffeeSdk;
/**
* A custom offer list component that
* renders data on the map instead of a static
* list. This class implements the `IOfferListComponent`
* interface from scratch.
*/
class CustomOfferList {
constructor(context, container, offerList) {
this.context = context;
this.container = container;
this.events = new util.EventEmitter();
this.offerList = null;
this.map = null;
/**
* We listen to the map events (marker selection)
* and translate it as an offer selection event.
* This is the requirement from the `IOfferListComponent`
* interface
*/
this.onMarkerClick = (markerId) => {
this.events.emit("offerSelect", {
offerId: markerId
});
};
/**
* We are free to implement the business logic in
* any that suits our needs
*/
this.setOfferList = ({ offerList: newOfferList }) => {
if (this.map) {
this.map.destroy();
this.map = null;
}
this.offerList = newOfferList;
if (newOfferList) {
// We're displaying data on a map (a dummy one),
// using the additional data we pass through the
// customized composer (see below)
this.map = new DummyMapApi(this.container, [
[16.355, 48.2],
[16.375, 48.214]
]);
for (const offer of newOfferList) {
this.map.addMarker(
offer.offerId,
offer.location,
this.onMarkerClick
);
}
}
};
this.setOfferList({ offerList });
this.contextListeners = [
context.events.on(
"offerPreviewListChange",
this.setOfferList
),
// We listen to the
// 'offerFullViewToggle' event on
// the parent composer context
// to select or deselect the corresponding
// marker.
//
// Note the important pattern:
// when the marker is clicked, we DO NOT
// mark it as selected, but only emit an
// event. This is because the offer list
// does not own the logic of selecting
// offers.
// It is the composer's responsibility
// to decide, whether this event should
// result in opening a panel or not
context.events.on(
"offerFullViewToggle",
({ offer }) => {
this.map.selectSingleMarker(
offer && offer.offerId
);
}
)
];
}
/**
* As required in the `IOfferListComponent` interface
*/
destroy() {
if (this.map) {
this.map.destroy();
}
for (const listener of this.contextListeners) {
listener.off();
}
}
}
/**
* We need to subclass a standard `SearchBoxComposer`
* to achieve to important goals:
* * Use the custom offer list we created instead
* of the standard component
* * Enrich the preview data with the geographical
* coordinates of the coffee shop
*/
class CustomComposer extends SearchBoxComposer {
buildOfferListComponent(
context,
container,
offerList,
contextOptions
) {
return new CustomOfferList(
context,
container,
this.generateOfferPreviews(offerList, contextOptions),
this.generateOfferListComponentOptions(contextOptions)
);
}
generateOfferPreviews(offerList) {
const result = super.generateOfferPreviews(offerList);
return result === null
? result
: result.map((preview, index) => ({
...preview,
location: offerList[index].place.location
}));
}
}
/**
* We're subclassing `SearchBox` to use our
* enhanced composer
*/
class CustomSearchBox extends SearchBox {
buildComposer(context, container, options) {
return new CustomComposer(context, container, options);
}
createOrder(offer) {
alert(`Isn't actually implemented (yet)`);
return super.createOrder(offer);
}
}
const searchBox = new CustomSearchBox(
document.getElementById("search-box"),
dummyCoffeeApi,
{
offerPanel: {
transparent: true
}
}
);
searchBox.search("Lungo");