JSFiddle - React, Tailwind, and code Playground
by Barry Carter
HTML
<script src="https://github.com/knockout/knockout/releases/download/v3.4.0/knockout-3.4.0.js"></script>
<div id="page">
<!-- ko if: processing -->
<div class="processing" data-bind="style: { height: subHeightCss }">
<p><span data-bind="text: processingText"></span><span class="spinner"></span></p>
</div>
<!-- /ko -->
</div>
<div id="subheader" class="subheader" data-bind="animateHeight: window.pageModel.subHeightCss">
<div class="control-group-row">
<ul class="control-group">
<li>
<button>
Add
</button>
</li>
<li>
<button>
Delete
</button>
</li>
</ul>
<ul class="control-group control-group-right">
<li>
<button data-bind="click: advanced_click">
Advanced
</button>
</li>
<li>
<input value="search..."/>
</li>
<li>
<button data-bind="click: save_click">
Save
</button>
</li>
</ul>
</div>
<div class="control-group-row">
<ul class="control-group">
<li>
<button>
Second
</button>
</li>
<li>
<button>
Row
</button>
</li>
</ul>
</div>
</div>
<h2>Entirely flex subheader</h2>
<pre>
With avanced multirow.
Can add as many rows as you need and call the page viewmodel from any other view model
the overlay for saving is scaled appropriately
</pre>
CSS
.subheader {
background-color: white;
border-bottom: 1px solid black;
height: 60px;
overflow: hidden;
}
.control-group-row {
display: flex;
}
.control-group {
display:flex;
list-style:none;
}
.control-group li {
margin: 0 5px 0 5px;
}
.control-group-right {
margin-left: auto;
}
.processing {
position: absolute;
left: 0px;
top: 0px;
height: 67px;
opacity: 0.7;
background-color: black;
width: 100%;
line-height: 60px;
margin: 3px;
}
.processing p {
opacity: 1;
font-size: 1.5em;
font-weight: 2;
color: white;
width: 320px;
margin: auto;
vertical-align: middle;
}
.processing .spinner {
background-image: url("https://www.usana.com/images/loading-spinner-black.gif");
background-size: 30px 30px;
background-repeat: no-repeat;
width: 30px;
height: 30px;
display: inline-block;
vertical-align: middle;
margin-left: 20px;
}
JavaScript
ko.bindingHandlers.animateHeight = {
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).stop(true, false);
var value = valueAccessor();
var valu = ko.utils.unwrapObservable(value);
$(element).animate({ height: valu } , 500);
}
};
// standard function every page has and will need.
// example deals withsaving banner and also expanded advanced options
var PageViewModel = function(parm) {
var pself = this;
this.processing = ko.observable(false);
this.processingText = ko.observable("Please Wait...");
this.defaultHeight = 60;
this.submenuHeight = ko.observable(pself.defaultHeight);
this.submenuExpanded = ko.observable(false);
this.startAction = function(displayText) {
pself.processingText(displayText)
pself.processing(true);
}
this.endAction = function() {
pself.processing(false);
}
this.toggleSubHeader = function(height) {
if (!pself.submenuExpanded()) {
pself.defaultHeight = pself.submenuHeight();
pself.submenuHeight(height);
pself.submenuExpanded(true);
}
else {
pself.submenuHeight(pself.defaultHeight);
pself.submenuExpanded(false);
}
}
this.subHeightCss = ko.computed(function() {
var h = pself.submenuHeight();
return (h) + "px";
});
}
var NormalViewModel = function(parm) {
var self = this;
this.save_click = function(data,event) {
pageModel.startAction("Saving...");
setTimeout(function() {
pageModel.endAction();
}, 3000)
}
this.advanced_click = function(data, event) {
pageModel.toggleSubHeader(120);
}
}
window.pageModel = new PageViewModel()
ko.applyBindings(pageModel, document.getElementById("page"));
ko.applyBindings(new NormalViewModel(), document.getElementById("subheader"));