SideMenu control

Control used to render a side menu. Define an init function to setup the data source, search textbox, menu height, and isSelected function. The height of the menu will be reset (getHeight method) on window scroll and resize. From the search textbox you can filter and navigate through the menu, select an item by hitting enter.

SideMenu with Search Box

SideMenu with a textbox for search. Besides filtering the menu you can navigate the menu and select an item using the keyboard while the searchbox is focused.
Filtering is done by Name, and additionally using the Keywords property.
ParentId can be set for menu items to achieve a tree structure.
Set Collapsed property if you need to have the node initially collapsed.
Shared/Demos/SideMenuSearch.cshtml
<div style="width:15rem;margin-top: 1em;">
@(Html.Awe().TextBox(new TextBoxOpt
{
Name = "txtMyMenuSearch",
Placeholder = "search...",
FieldAttr = new { @class = "msearch" },
ClearBtn = true
}))

<nav role="navigation" class="scrlh">
@(Html.Awe().SideMenu(new SideMenuOpt
{
Name = "MyMenu",
InitFunc = string.Format("mySideMenuInit('{0}')", Url.Action("GetMenuNodes", "Data"))
}))
</nav>
</div>
<script>
function mySideMenuInit(url) {
return (opt) => {
// search textbox
opt.searchValId = 'txtMyMenuSearch';

opt.getData = () => cstorg.get(url);

opt.isSelected = function (item) {
return item.Action == document.action && item.Controller == document.controller;
};

//opt.getHeight = () => 300 // window header footer etc. height|offset can be used

// alternative to getHeight
opt.grid.css('height', 'max(calc(100vh - 500px), 300px)');
};
}
</script>
Awesome/DataController.cs
public IActionResult GetMenuNodes()
{
var menuNodes =
MySiteMap.GetAll().Select(
o =>
new
{
o.Id,
o.Name,
o.Keywords,
ParentId = o.Parent?.Id ?? 0,
Url = o.Action != null ? Url.Action(o.Action, o.Controller) + o.Anchor : null,
o.Action,
o.Controller,
o.Collapsed,
o.NoMenu,
o.Anchor
});

return Json(menuNodes, new JsonSerializerOptions());
}

SideMenu with scroll sync

Header number 0
Header number 1
Header number 2
Header number 3
Header number 4
Header number 5
Header number 6
Header number 7
Header number 8
Header number 9
SideMenu with scroll sync. When we scroll the content of the page (or specified container), the menu will sync with the content, it will scroll to the menu item that corresponds to the content currently visible in the scroll container, and it will emphasize that item.
SideMenu/Index.cshtml

<div id="syncScrollCont" style="position:relative; height: 320px; overflow: auto;border: 1px solid gray;display:flex; gap: 2rem; border-radius: 5px; padding: .5rem;">
<div style="position:sticky; top: 0;">
@(Html.Awe().TextBox(new TextBoxOpt
{
Name = "txtSyncMenuSearch",
Placeholder = "search...",
FieldAttr = new { @class = "msearch" },
ClearBtn = true
}))

<nav role="navigation" class="scrlh">
@(Html.Awe().SideMenu(new SideMenuOpt
{
Name = "SyncMenu",
InitFunc = "syncMenuInit"
}))
</nav>
</div>
<div>
@foreach(var i in Enumerable.Range(0, 10)){
<div style="height: 200px;">
<h5 id="sync@(i)"> <span> Header number @i</span></h5>
</div>
}
</div>
</div>
<script>
function syncMenuInit(opt){
const data = [];
const syncElms = $('#syncDemo').find('h5');

// add data based on the h5 elements
syncElms.each((i, elm) => {
const url = $(elm).attr('id');
data.push({ Id: i, Name: $(elm).find('span').html(), Url: `#${url}` });
});

// search textbox
opt.searchValId ='txtSyncMenuSearch';

opt.getData = () => data;

opt.getHeight = () => 250;

// viewport scroll nodes
opt.getScrollNodes = () => syncElms;

// match/find the menu data model based on the scroll synced html node
opt.syncMatch = ({model, node}) => model.Url == '#' +$(node).attr('id');

opt.scrollCont = $('#syncScrollCont'); // by default it is window
}
</script>



Comments