1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2025-05-13 21:26:26 +02:00

197 lines
5.3 KiB
JavaScript
Raw Permalink Normal View History

2023-08-14 17:41:39 +03:00
/**
* @fileoverview
* In this example, we change the composition logic:
* there is no offer full view (panel) component, only
* a offer list with additional actions
*/
2023-08-04 10:49:50 +03:00
const {
SearchBox,
SearchBoxComposer,
OfferListComponent,
dummyCoffeeApi,
util
} = ourCoffeeSdk;
2023-08-14 17:41:39 +03:00
/**
* A customized version of the standard `OfferListComponent`.
* As we're okay with its logic, we reuse it with two modifications:
* * List items could be expanded (and then collapsed back)
* * List items contain the 'Place an order' button
*/
2023-08-04 10:49:50 +03:00
class CustomOfferList extends OfferListComponent {
constructor(context, container, offerList, options) {
2023-08-01 01:25:30 +03:00
super(context, container, offerList, options);
2023-08-14 17:41:39 +03:00
/**
* This is a custom DOM event listener to make
* other than selecting an offer actions on the item
* click event. This is the shortcut we took (see
* the explanations in the `OfferPanelComponent.ts`
* file).
*/
2023-08-04 10:49:50 +03:00
this.onClickListener = (e) => {
const { target, value: action } = util.findDataField(
e.target,
"action"
);
if (target === null || action === null) {
return;
}
const { target: container, value: offerId } =
util.findDataField(target, "offerId");
if (container === null || offerId === null) {
return;
}
switch (action) {
case "expand":
this.expand(container);
break;
case "collapse":
this.collapse(container);
break;
case "createOrder":
2023-08-14 17:41:39 +03:00
this.events.emit("createOrder", { offerId });
2023-08-04 10:49:50 +03:00
break;
2023-07-30 23:04:39 +03:00
}
};
2023-08-01 01:25:30 +03:00
}
2023-07-30 23:04:39 +03:00
2023-08-04 10:49:50 +03:00
expand(item) {
item.classList.add("expanded");
2023-08-01 01:25:30 +03:00
}
2023-08-04 10:49:50 +03:00
collapse(item) {
item.classList.remove("expanded");
2023-07-30 23:04:39 +03:00
}
2023-08-14 17:41:39 +03:00
/**
* This is a redefined function that returns
* the offer preview markup in the list
*/
2023-08-04 10:49:50 +03:00
generateOfferHtml(offer) {
return util.html`<li
class="custom-offer"
data-offer-id="${util.attrValue(offer.offerId)}"
>
<button data-action="expand" class="preview"><aside class="expand-action">&gt;</aside><strong>${
offer.title
}</strong> · ${offer.price.formattedValue}</button>
<div class="fullview">
<button data-action="collapse" class="collapse-action"></button>
<div><strong>${offer.title}</strong> · ${
offer.price.formattedValue
}</div>
<div>${offer.subtitle}</div>
<div>${offer.bottomLine}</div>
<button data-action="createOrder" class="action-button">Place an Order</button>
</div>
</li>`.toString();
2023-07-30 23:04:39 +03:00
}
}
2023-07-30 15:25:21 +03:00
2023-08-14 17:41:39 +03:00
/**
* This is a custom implementation of the
* `ISearchBoxComposer` interface from scratch.
* As there is no offer panel in this particular
* UI, we don't need all the associated logic,
* so we replace the standard implementation
* with this new one. However, we re-use the
* implementation of the offer list subcomponent
*/
class CustomComposer {
constructor(searchBox, container) {
this.events = new util.EventEmitter();
this.offerList = null;
this.container = container;
// This is our enhanced offer list
this.offerList = new CustomOfferList(
this,
2023-07-30 23:04:39 +03:00
container,
2023-08-14 17:41:39 +03:00
this.offerList
2023-07-30 23:04:39 +03:00
);
2023-08-14 17:41:39 +03:00
this.eventDisposers = [
searchBox.events.on(
"offerListChange",
({ offerList }) => this.onOfferListChange(offerList)
),
// What we need is to listen to an additional event
// the custom offer list emits, and convert it into
// the order creation request
this.offerList.events.on(
"createOrder",
({ offerId }) => {
const offer = this.findOfferById(offerId);
if (offer) {
this.events.emit("createOrder", {
offer
});
}
}
)
];
}
/**
* This is the `ISearchBoxComposer` interface
* method we must implement
*/
findOfferById(refOfferId) {
return this.offerList
? this.offerList.find(
({ offerId }) => offerId == refOfferId
)
: null;
}
/**
* This is the `ISearchBoxComposer` interface
* method we must implement
*/
destroy() {
for (const disposer of this.eventDisposers) {
disposer.off();
}
this.offerList.destroy();
}
onOfferListChange(offerList) {
this.offerList = offerList;
this.events.emit("offerPreviewListChange", {
// This is our custom offer preview generator
// function. As we don't plan to customize
// it further, we don't bother with exposing
// overridable methods, etc.
offerList:
offerList !== null
? offerList.map((offer) => ({
offerId: offer.offerId,
title: offer.place.title,
subtitle: offer.description,
bottomLine:
SearchBoxComposer.generateOfferBottomLine(
offer
),
price: offer.price
}))
: null
});
2023-07-30 23:04:39 +03:00
}
2023-07-30 15:25:21 +03:00
}
2023-08-14 17:41:39 +03:00
/**
* We're subclassing `SearchBox` to use our
* custom composer
*/
2023-08-04 10:49:50 +03:00
class CustomSearchBox extends SearchBox {
2023-07-30 15:25:21 +03:00
buildComposer(context, container, options) {
2023-08-04 10:49:50 +03:00
return new CustomComposer(context, container, options);
2023-07-30 15:25:21 +03:00
}
createOrder(offer) {
alert(`Isn't actually implemented (yet)`);
return super.createOrder(offer);
}
}
const searchBox = new CustomSearchBox(
document.getElementById("search-box"),
2023-08-04 10:49:50 +03:00
dummyCoffeeApi
2023-07-30 15:25:21 +03:00
);
searchBox.search("Lungo");