Omacro Buy Now Widget BS4
Exploring Omacro Buy Now WIdget upgrade with Bootstrap 4
by Jesse Johnson
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://bootswatch.com/4/cosmo/bootstrap.min.css">
<div class="omacro">
<div class="modal" tabindex="-1" role="dialog" data-bind="with: product">
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<span data-bind="text: name"></span>
<span data-bind="text: key" class="small"></span>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="online-tab" data-toggle="tab" href="#online" role="tab" aria-controls="online" aria-selected="true">Online Retailers</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Local Retailers</a>
</li>
<li class="nav-item">
<a class="nav-link" id="kocontext-tab" data-toggle="tab" href="#kocontext" role="tab" aria-controls="kocontext" aria-selected="false">Knockout Context</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane show active" id="online" role="tabpanel" aria-labelledby="online-tab">
<div class="modal">
<div data-bind="with: markets">
<div data-bind="with: online"...
CSS
body {
padding: 1rem;
}
.omacro {
min-width: 480px;
}
.modal {
position: relative;
top: auto;
right: auto;
bottom: auto;
left: auto;
z-index: 1;
display: block;
}
.modal-dialog {
left: auto;
margin-right: auto;
margin-left: auto;
}
TypeScript
interface IRetailer {
cid: string,
name: string,
logo: string,
locations: IRetailLocation[]
}
interface IRetailLocation {
lid: string,
inStock?: boolean,
price?: number
attributes?: {
[name: string]: string
}
}
interface IOnlineRetailer extends IRetailer {
locations: IOnlineLocation[]
}
interface IOnlineLocation extends IRetailLocation {
link: string;
}
var BUYNOW = {
PRODUCTS: {
SINGLEPRD: {
KEY: "SINGLEPRD",
NAME: "Sample Single Product",
ISSTYLED: false,
RETAILERS: {
ONLINE: "https://next.json-generator.com/api/json/get/VyPblEhmG?indent=4&delay=1500"
}
},
STYLEDPRD: {
KEY: "STYLEDPRD",
NAME: "Sample Styled Product",
ISSTYLED: true,
RETAILERS: {
ONLINE: "https://next.json-generator.com/api/json/get/41YDiQ058?indent=4&delay=1500"
}
}
}
}
class OnlineMarketViewModel {
url: string;
retailers: KnockoutObservableArray<IOnlineRetailer>;
getRetailers() {
$.getJSON(this.url, this.retailers);
}
constructor(data) {
this.url = data.RETAILERS.ONLINE;
this.retailers = ko.observableArray([]);
this.getRetailers();
}
}
class MarketplaceViewModel {
online: OnlineMarketViewModel;
constructor(data) {
this.online = new OnlineMarketViewModel(data);
}
}
class ProductViewModel {
key: KnockoutObservable<string>;
name: KnockoutObservable<string>;
markets: MarketplaceViewModel;
constructor(data) {
this.key = ko.observable(data.KEY || null);
this.name = ko.observable(data.NAME || null);
this.markets = new MarketplaceViewModel(data);
}
}
class WidgetViewModel {
product: KnockoutObservable<ProductViewModel>;
show(key: string) {
let data = BUYNOW.PRODUCTS[key];
let product = new ProductViewModel(data);
this.product(product);
}
constructor() {
this.product = new ko.observable(null);
}
}
let widget = new WidgetViewModel();
ko.applyBindings(widget);
widget.show(BUYNOW.PRODUCTS.STYLEDPRD.KEY);