├── .vscode
└── extensions.json
├── assets
└── demo.gif
├── .gitattributes
├── .prettierignore
├── tsconfig.node.json
├── src
├── vite-env.d.ts
├── scss
│ ├── vue-awesome-sidebar.scss
│ ├── menu-scrollbar.scss
│ ├── types
│ │ ├── _shared.scss
│ │ ├── simple.scss
│ │ └── fully.scss
│ ├── menu-item.scss
│ ├── _base.scss
│ └── themes
│ │ ├── dark-theme.scss
│ │ └── white-theme.scss
├── VueAwesomeSidebar.ts
├── hooks
│ ├── useAwsemoeItem.ts
│ ├── useClickOutSide.ts
│ ├── useAwsomeRouter.ts
│ └── useAwseomeSideBar.ts
├── components
│ ├── Menuline.vue
│ ├── MenuItemIcon.vue
│ ├── HeaderItem.vue
│ ├── Menu.vue
│ └── MenuItem.vue
├── main.ts
└── App.vue
├── .gitignore
├── .prettierrc.json
├── index.html
├── vite.config.ts
├── tsconfig.json
├── LICENCE
├── types
└── index.d.ts
├── package.json
├── README.md
└── pnpm-lock.yaml
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amirkian007/vue-awesome-sidebar/HEAD/assets/demo.gif
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=TypeScript
2 | *.vue linguist-language=TypeScript
3 | * -text
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bin
3 | dist
4 | build
5 | pnpm-lock.yaml
6 | .vscode
7 | README.md
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | //"extends": "@vue/tsconfig/tsconfig.node.json",
3 | "include": ["vite.config.*"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "types": ["node"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import type { DefineComponent } from 'vue'
5 | const component: DefineComponent<{}, {}, any>
6 | export default component
7 | }
8 |
--------------------------------------------------------------------------------
/src/scss/vue-awesome-sidebar.scss:
--------------------------------------------------------------------------------
1 | @import './menu-scrollbar';
2 | @import './base';
3 | //@use './types/shared';
4 | @import './themes/white-theme.scss';
5 | @import './themes/dark-theme.scss';
6 | @import '../scss/menu-item.scss';
7 |
--------------------------------------------------------------------------------
/src/VueAwesomeSidebar.ts:
--------------------------------------------------------------------------------
1 | // This is where the package installs.
2 | import type { App } from 'vue'
3 | import VueAwesomeSideBar from './components/Menu.vue'
4 |
5 | export default {
6 | install: (app: App) => {
7 | app.component('VueAwesomeSideBar', VueAwesomeSideBar)
8 | }
9 | }
10 |
11 | export { VueAwesomeSideBar }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 | *.tgz
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/src/hooks/useAwsemoeItem.ts:
--------------------------------------------------------------------------------
1 | import { onBeforeUnmount, ref, onMounted, watch } from 'vue'
2 |
3 | export function useAwsemoeItem(target: any, callBack: any, isCollapsed: any) {
4 | const currentRoute = ref(
5 | window.location.pathname + window.location.search + window.location.hash
6 | )
7 |
8 | const itemShow = ref(false)
9 | const itemHover = ref(false)
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/Menuline.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
21 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "tabWidth": 2,
4 | "singleQuote": true,
5 | "printWidth": 80,
6 | "trailingComma": "none",
7 | "overrides": [
8 | {
9 | "files": ["*.json5"],
10 | "options": {
11 | "singleQuote": false,
12 | "quoteProps": "preserve"
13 | }
14 | },
15 | {
16 | "files": ["*.yml"],
17 | "options": {
18 | "singleQuote": false
19 | }
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/MenuItemIcon.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
23 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue awesome sidebar
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/HeaderItem.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
14 |
22 |
23 |
24 |
29 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import { terser } from 'rollup-plugin-terser'
4 | import typescript2 from 'rollup-plugin-typescript2'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [vue()],
9 | server: {},
10 | build: {
11 | lib: {
12 | entry: './src/VueAwesomeSidebar.ts',
13 | formats: ['es', 'cjs'],
14 | name: 'VueAwesomeSidebar',
15 | fileName: 'vue-awesome-sidebar'
16 | },
17 | sourcemap:true,
18 | rollupOptions: {
19 | external: ['vue'],
20 | output: {
21 | globals: {
22 | vue: 'Vue'
23 | },
24 | assetFileNames: (assetInfo) => {
25 | if (assetInfo.name == 'style.css') return 'vue-awesome-sidebar.css'
26 | return assetInfo.name
27 | }
28 | }
29 | }
30 | }
31 | })
32 |
--------------------------------------------------------------------------------
/src/scss/menu-scrollbar.scss:
--------------------------------------------------------------------------------
1 | $scroll-bar-width: 13px !default;
2 |
3 | .menu-wraper,
4 | .topContainer {
5 | &::-webkit-scrollbar {
6 | // transition: 2s;
7 | width: $scroll-bar-width;
8 | }
9 | &.compeleteCoolapseMenu::-webkit-scrollbar {
10 | width: $scroll-bar-width;
11 | }
12 | &.miniCoolapseMenu::-webkit-scrollbar {
13 | width: 0px;
14 | }
15 | &.miniCoolapseMenu {
16 | -ms-overflow-style: none; /* IE 11 */
17 | scrollbar-width: none; /* Firefox 64 */
18 | }
19 | &::-webkit-scrollbar-thumb {
20 | background-clip: content-box;
21 | border: 4px solid transparent;
22 | border-radius: 7px;
23 | box-shadow: inset 0 0 0 10px;
24 | }
25 | &::-webkit-scrollbar-button {
26 | width: 0;
27 | height: 0;
28 | display: none;
29 | }
30 | &::-webkit-scrollbar-corner {
31 | background-color: transparent;
32 | }
33 | .topContainer::-webkit-scrollbar {
34 | width: $scroll-bar-width;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/scss/types/_shared.scss:
--------------------------------------------------------------------------------
1 | $menu-item-simpleType-icon-height: 35px !default;
2 | $menu-item-fullyType-height: 44px !default;
3 | $label-font-size: 16px !default;
4 | $label-icon-size: 20px !default;
5 |
6 | .menu-iconS {
7 | font-size: $label-icon-size;
8 | width: $label-icon-size; //
9 | overflow: hidden;
10 | }
11 | .labelMiniS {
12 | font-size: $label-font-size; //d
13 | .rtl & {
14 | border-top-left-radius: 4px;
15 | border-bottom-left-radius: 4px;
16 | }
17 | .ltr & {
18 | border-top-right-radius: 4px;
19 | border-bottom-right-radius: 4px;
20 | }
21 | }
22 |
23 | .hoverClassS {
24 | transition: all 0.3s;
25 | }
26 | .topContainerS {
27 | .rtl & {
28 | border-top-left-radius: 7px;
29 | border-bottom-left-radius: 7px;
30 | }
31 | .ltr & {
32 | border-top-right-radius: 7px;
33 | border-bottom-right-radius: 7px;
34 | }
35 | }
36 | .labelS {
37 | width: 100%;
38 | .left {
39 | span {
40 | padding-left: 6px;
41 | padding-right: 6px;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // {
2 |
3 | // "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4 | // "compilerOptions": {
5 | // "strictNullChecks": false,
6 | // "baseUrl": ".",
7 | // "paths": {
8 | // "@/*": ["./src/*"]
9 | // },
10 | // "allowJs":true
11 |
12 | // },
13 | // "references": [{ "path": "./tsconfig.node.json" }]
14 | // }
15 | {
16 | "compilerOptions": {
17 | "target": "esnext",
18 | "useDefineForClassFields": true,
19 | "module": "esnext",
20 | "moduleResolution": "node",
21 | "strict": true,
22 | "jsx": "preserve",
23 | "sourceMap": true,
24 | "resolveJsonModule": true,
25 | "esModuleInterop": true,
26 | "lib": ["esnext", "dom"],
27 | "skipLibCheck": true,
28 | "allowJs": true,
29 | "declaration": true,
30 | "isolatedModules": true,
31 | "paths": {
32 | "@": ["./src"],
33 | "@/*": ["./src/*"]
34 | }
35 | },
36 | "paths": {
37 | "@": ["./src"],
38 | "@/*": ["./src/*"]
39 | },
40 | "include": ["src/**/*"],
41 | "references": [{ "path": "./tsconfig.node.json" }]
42 | }
43 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 amirkian007
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import 'material-icons/iconfont/material-icons.css'
4 | import { createRouter, createWebHistory } from 'vue-router'
5 |
6 | import MenuItemIcon from './components/MenuItemIcon.vue'
7 | const Home = { template: 'Home
' }
8 | const About = { template: 'About
' }
9 | const test = { template: 'test
' }
10 | const test2 = { template: 'test2
' }
11 | const test3 = { template: 'test3
' }
12 | const test4 = { template: 'test4
' }
13 | // 2. Define some routes
14 | // Each route should map to a component.
15 | // We'll talk about nested routes later.
16 | const routes = [
17 | { path: '/', component: Home },
18 | { path: '/a', component: About },
19 | { path: '/a/b', component: test },
20 | { path: '/a/b/c', component: test2 },
21 | { path: '/a/b/c/d', component: test3 },
22 | { path: '/k', component: test4 }
23 | ]
24 |
25 | // 3. Create the router instance and pass the `routes` option
26 | // You can pass in additional options here, but let's
27 | // keep it simple for now.
28 | const router = createRouter({
29 | // 4. Provide the history implementation to use. We are using the hash history for simplicity here.
30 | strict: true,
31 | history: createWebHistory(''),
32 | routes // short for `routes: routes`
33 | })
34 |
35 | console.log(App)
36 | console.log(App.setup)
37 | createApp(App).use(router).mount('#app')
38 |
--------------------------------------------------------------------------------
/src/hooks/useClickOutSide.ts:
--------------------------------------------------------------------------------
1 | import { onBeforeUnmount, onMounted, watch, ref } from 'vue'
2 |
3 | export function useClickOutSide(target: any, callBack: any, isCollapsed: any) {
4 | if (!target) return
5 |
6 | const listner = (event: any) => {
7 | if (isCollapsed.value) {
8 | removeSideBarListner()
9 | return
10 | }
11 | if (
12 | event.target == target.value ||
13 | event.composedPath().includes(target.value)
14 | ) {
15 | return
16 | }
17 | callBack()
18 | }
19 | const removeSideBarListner = () => {
20 | window.removeEventListener('click', listner)
21 | }
22 | const addSideBarListner = () => {
23 | removeSideBarListner()
24 | setTimeout(() => {
25 | //push this to the end of call stack
26 | window.addEventListener('click', listner)
27 | }, 0)
28 | }
29 |
30 | onBeforeUnmount(removeSideBarListner)
31 |
32 | return { removeSideBarListner, addSideBarListner }
33 | }
34 | export function useAutoCollapse(target: number, callBack: any) {
35 | if (!target) return
36 | callBack(target > innerWidth)
37 | let initialWidth = window.innerWidth
38 | const listner = () => {
39 | if (initialWidth != window.innerWidth) {
40 | callBack(target > innerWidth)
41 | initialWidth = window.innerWidth
42 | }
43 | }
44 | window.addEventListener('resize', listner)
45 | onBeforeUnmount(() => {
46 | window.removeEventListener('resize', listner)
47 | })
48 | }
49 |
--------------------------------------------------------------------------------
/src/scss/types/simple.scss:
--------------------------------------------------------------------------------
1 | @import './shared';
2 |
3 | .vsss-header,
4 | .vas-simple {
5 | width: 95%;
6 | }
7 | .vas-simple {
8 | .noIconWidth {
9 | width: calc(97% - 20px) !important;
10 | }
11 | .noIconwidthMiniMenu {
12 | width: calc(95% - 20px) !important;
13 | }
14 | .label,
15 | .labelMini,
16 | .labelminiSub {
17 | height: $menu-item-simpleType-icon-height;
18 | }
19 | .label {
20 | padding: 6px 8px;
21 | }
22 | }
23 |
24 | .vas-simple {
25 | margin-top: 1.5px;
26 | margin-bottom: 1.5px;
27 | border-radius: 4px;
28 | .menu-icon {
29 | @extend .menu-iconS;
30 | }
31 | .labelMini {
32 | border-radius: 4px;
33 | @extend .labelMiniS;
34 | > .left {
35 | margin-left: 7px;
36 | margin-right: 7px;
37 | align-items: center;
38 | }
39 | }
40 | .label {
41 | font-size: $label-font-size;
42 | > .left {
43 | margin-left: 7px;
44 | margin-right: 7px;
45 | align-items: center;
46 | }
47 | }
48 | .icons {
49 | margin-left: 7px;
50 | margin-right: 7px;
51 | }
52 | .hoverClass {
53 | @extend .hoverClassS;
54 | }
55 | .topContainer {
56 | @extend .topContainerS;
57 | }
58 |
59 | &.miniCollapseIconWidth {
60 | width: 70%;
61 | }
62 | &.MenuItemWidthOnMiniCollapse {
63 | width: 95%;
64 | }
65 | .label {
66 | height: $menu-item-simpleType-icon-height;
67 | border-radius: 4px;
68 | @extend .labelS;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { App } from 'vue'
2 |
3 | export interface MenuItemIcon {
4 | text?: string
5 |
6 | class?: string
7 |
8 | element?: string
9 |
10 | attributes?: object
11 | }
12 |
13 | export interface MenuItem {
14 | href: string | object
15 |
16 | name: string
17 |
18 | icon?: MenuItemIcon
19 |
20 | children?: Array