A full-featured <a href="https://kit.svelte.dev/">SvelteKit</a> menu component.
npm install @yarknode/svelte-menuA full-featured SvelteKit menu component.
Demo site: https://menu-demo-lyart.vercel.app/
Try the demo first, then read on please.
Read on to decide whether this componet is compatible to your projects.
Warning: this menu componet is capable to SvelteKit project, because it use SvelteKit-specific modules "$app/navigation".
I only use "goto" function of "$app/navigation", once I find a way to navigate among Svelte routes the same way as SvelteKit does, I may remove the dependency to "SvelteKit", then, this component will be available to all Vite-based projects. Meanwhile, I really suggest you give SvelteKit a try if you have not, it gives me a charming experience.
This component uses bootstrap as well.
```
git clone https://github.com/cnshsliu/menu.demo
Or, added to your exiting project following next steps
`bash`
npm i @yarknode/svelte-menu
Normally, we place menu in "src/routes/+layout.svelte"
First, define menu structure:
`typescript`
Then, place <Menu> component in "+layout.svelte"
`xml`
bind:this={theMenu}
{menuDef}
{menuStyle} // see below
avatar={{ img: 'https://.../avatar.png' }}
logo={{ img: 'https://.../yn.png' }}
on:changeWorklistStatus={onChangeWorklistStatus}
on:sizeChanged={onSizeChanged}
on:changeStyle={onChangeStyle}
/>
Look, there are three custom event handlers.
The first is "on:changeWorklistStatus", the event name "changeWorklistStatus" should be the same as what we defined in menuitem previously.
The event handler "onChangeWorklistStatus" is defined in "+layout.svelte",
`javascript
const onChangeWorklistStatus = async (event: CustomEvent) => {
const payload = event.detail;
if (payload === undefined) return;
$demoData = payload;
goto(/work);`
};
Of course, this is an example callback function, you should use your own as required.
However, normally you need to keep the second "on:sizeChanged", because svelte-menu can change it's own size on mouse enter/leave, and we should catch this event to change the position of main display area.
Here is the exmapple in demo, you may change it accordinly to fit your need.
`javascript`
const onSizeChanged = async (event: CustomEvent) => {
const payload = event.detail;
if (payload === undefined) return;
if (menuStyle === 'mobile') return '';
switch (payload.to) {
case 'float-logo':
case 'float-small':
mainAreaClass = 'main-area-width-small';
break;
case 'float-big':
mainAreaClass = 'main-area-width-big';
break;
}
};
The third on is "on:styleChanged", If you are going to let your users to toggle among menu styles on-demand just within the menu, keep this event handler. and have menuitems defined like below:
`json`
{
"id": "____demostye",
"alias": "Menu Style",
"icon": "flower",
"sub": [
{
"id": "__ds_browser",
"alias": "Browser",
"callback": "changeStyle",
"payload": { "style": "browser" }
},
{
"id": "__ds_browser",
"alias": "PC",
"callback": "changeStyle",
"payload": { "style": "pc" }
},
{
"id": "__ds_browser",
"alias": "Mobile",
"callback": "changeStyle",
"payload": { "style": "mobile" }
},
{
"id": "__ds_browser",
"alias": "Windows",
"callback": "changeStyle",
"payload": { "style": "windows" }
}
]
}
The handler:
`javascriptStyle changed to ${menuStyle}
const onChangeStyle = async (event: CustomEvent) => {
const payload = event.detail;
if (payload === undefined) return;
menuStyle = payload.style;
notify.message = ;`
const toast = new Toast(document.getElementById('liveToast') as Element);
toast.show();
};
Of course, you may decide to provie one menu style to your user, just pass it to Menu component like this then:
``
menuStyle={"browser"}
/>
We support four menu styles: browser, pc, mobile and windows.
- browser
- will be placed at left-top corner
- menu items are displayed vertically
- automatical collapse/expand on mouse enter/leave
- can pinned to expanded mode
- pc
- will be placed on top, just like the menus of desktop applications on Windows/Mac PC.
- first level menu items are displayed horiontally.
- sub items are displayed as dropdown
- mobile
- will be placed at the bottom-right corner
- touch icon to show/hide menus
- windows
- will be placed at the bottom-left corner, make it looks like Windows "Start" menu
- touch icon to show/hide menus
In the demo, we also have mobile detection codes in onMount, if the demo is running on mobile devices, it will use "mobile" style as the default style, explore source code here
are given to <Menu> as "logo" and "avatar"
``
avatar={{ img: 'https://.../avatar.png' }}
logo={{ img: 'https://.../yn.png' }}
If you already give the demo a try, you should already know where avatar and logo are used.
`
let theMenu;
Binding component to a variable makes it easier to handle menu dynamically later, just read following sections, if you will use a simple static menu which will not be changed, then no need to play around menu binding.
Change menuitems on fly
Change menuitems defintion, passed it to $menuDataForSet and set $menuRefreshFlag to true.
`
let newMenus = [...];
$menuDataForSet = newMenus;
$menuRefreshFlag = true;
`Control the visiblilty of a specific menu item:
Method 1: set "visible" value directly
`
menuDef[index].visible = false;
theMenu.tickMenu();
`Method 2: control visibility with a function
first, define a function in "+layout.svelte" where the <Menu> componet is placed in. This function return true/false to control whether a menu item should be visible or not. Like this example:
`
const checkValue = (what: string, expect: any) => {
let ret = false;
switch (what) {
case 'inSession':
ret = $menuInSession === expect;
break;
}
return ret;
};
`then, have "check_visible" specidifed like below for menu items which visbility should be controlled dynamically. like:
` {
id: '____signin',
class: 'toplevel',
alias: 'Signin',
icon: 'door-open',
href: '/login',
check_visible: { fn: checkValue, what: 'inSession', expect: false }
},
{
id: '____signout',
class: 'toplevel',
alias: 'Signout',
icon: 'door-closed-fill',
href: '/logout',
check_visible: { fn: checkValue, what: 'inSession', expect: true }
},
`The "fn: checkValue" is the function name we defined previously, the "what" and "expect" will be passed to it.
Finally, give "fn: checkValue" a chance to run by calling tickMenu().
In the demo, we make it run when the value of $menuInSession is changed.
`
$: $menuInSession + theMenu?.tickMenu();
`Once the Menu beging ticked, svelte-menu will re-run "check_visible", thus, call "fn: checkValue" function, then show or hide correspoding menu items depend on the return value of "fn: checkValue".
In the demo, $menuInSession stores a true value on user being logged in, a false value on logging out, thus, when user is logged in, "Signin" is hdden while "Signout" is shown, and vice versa
use own component for user avatar
svelte-menu has a slot named as "me", so if you'd like to use your own component to display user avatar, maybe a dropdown to provide more info for your user. just place it like below:
`
``The above codes will replace user avatar with "MyComp"