everyone!
How to make a sidebar navigator using veux + typescript?
I'm stuck and I don't know how to move on.. help me, dear, patient developers
using vue3 (Composition API setup) + Typescript + Tailwind CSS
Can u help me?
package.json
{
"name": "kalys.io",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"@heroicons/vue": "^1.0.6",
"@tailwindcss/postcss7-compat": "^2.2.17",
"autoprefixer": "^9",
"chart.js": "^3.7.1",
"core-js": "^3.6.5",
"heroicons": "^1.0.6",
"postcss": "^7",
"query-string": "^7.1.1",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
"vue": "^3.0.0",
"vue-chartjs": "^4.1.0",
"vue-google-maps-typescript": "^0.1.4",
"vue-router": "^4.0.0-0",
"vueperslides": "^3.3.2",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"typescript": "~4.1.5",
"vue-cli-plugin-tailwind": "^2.2.18"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
inside store/index.ts
import {
createStore,
Store as VuexStore,
CommitOptions,
createLogger,
DispatchOptions
} from 'vuex'
import { State, state } from './modules/state'
import { Mutations, mutations } from './modules/mutations'
import { Actions, actions } from './modules/actions'
import { Getters, getters } from './modules/getters'
export const store = createStore<State>({
plugins: process.env.NODE_ENV === 'development' ? [createLogger()] : [],
state,
mutations,
actions,
getters,
modules: {
}
})
export function useStore() {
return store as Store
}
export type Store = Omit<
VuexStore<State>,
'getters' | 'commit' | 'dispatch'
> & {
commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>(
key: K,
payload: P,
options?: CommitOptions
): ReturnType<Mutations[K]>
} & {
dispatch<K extends keyof Actions>(
key: K,
payload?: Parameters<Actions[K]>[1],
options?: DispatchOptions
): ReturnType<Actions[K]>
} & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
}
}
inside store/modules/state.ts
export type State = {
theme: string;
showSidebar: boolean;
};
export const state: State = reactive({
theme: 'light',
showSidebar: true
});
inside store/modules/mutation-types.ts
export enum MutationTypes {
SET_THEME = 'SET_THEME',
SET_SHOWSIDEBAR = 'SET_SHOWSIDEBAR'
}
inside store/modules/mutation.ts
import { MutationTree } from "vuex";
import { MutationTypes } from "./mutation-types";
import { State } from './state';
export type Mutations<S = State> = {
[MutationTypes.SET_THEME](state: S, payload: string | boolean): void;
[MutationTypes.SET_SHOWSIDEBAR](state: S, payload: boolean): void;
}
export const mutations: MutationTree<State> & Mutations = {
[MutationTypes.SET_THEME](state: State, payload: string) {
state.theme = payload,
localStorage.theme = payload
},
[MutationTypes.SET_SHOWSIDEBAR](state: State, payload: boolean) {
state.showSidebar = payload
},
}
inside store/modules/action-types.ts
export enum ActionTypes {
INIT_THEME = 'INIT_THEME',
toggle_theme = 'TOGGLE_THEME',
toggle_sidebar = 'TOGGLE_SIDEBAR'
}
inside store/modules/action.ts
import { MutationTypes } from './mutation-types';
import { ActionContext, ActionTree } from "vuex";
import { ActionTypes } from "./action-types";
import { Mutations } from "./mutations";
import { State } from "./state";
type ActionAugments = Omit<ActionContext<State, State>, 'commit'> & {
commit<K extends keyof Mutations>(
key: K,
payload: Parameters<Mutations[K]>[1]
): ReturnType<Mutations[K]>
}
export type Actions = {
[ActionTypes.INIT_THEME](context: ActionAugments): void;
}
export const actions: ActionTree<State, State> & Actions = {
[ActionTypes.INIT_THEME]({ commit }) {
const cachedTheme: boolean = localStorage.theme ? localStorage.theme : false;
const userPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if(cachedTheme) {
commit(MutationTypes.SET_THEME, cachedTheme)
} else if(userPrefersDark) {
commit(MutationTypes.SET_THEME, 'dark')
} else {
commit(MutationTypes.SET_THEME, 'light')
}
},
[ActionTypes.toggle_theme]({ commit }) {
switch (localStorage.theme) {
case 'light':
commit('SET_THEME', 'dark')
break;
default:
commit('SET_THEME', 'light')
break;
}
},
[ActionTypes.toggle_sidebar]({ commit }, state) {
let sidebar = MutationTypes.SET_SHOWSIDEBAR ? false : true;
commit(MutationTypes.SET_SHOWSIDEBAR, sidebar)
}
}
inside store/modules/getters.ts
import { GetterTree } from "vuex";
import { State } from './state';
export type Getters = {
GET_THEME(state: State): string
GET_SHOWSIDEBAR(state: State): boolean
}
export const getters: GetterTree<State, State> & Getters = {
GET_THEME(state) {
return state.theme;
},
GET_SHOWSIDEBAR(state) {
return state.showSidebar;
},
}
Components/TheHeaderUpNav.vue At the bottom I connect the sidebar and sidebar switch button (, )
<template>
<div
class="top-0 fixed lg:relative z-50 flex lg:justify-center justify-end items-center w-full py-1 lg:px-1 bg-gray-900 text-gray-300"
>
<div
class="flex justify-between w-full text-xs md:text-sm lg:justify-around lg:gap-4 xl:gap-16"
>
<ul class="hidden lg:flex justify-around font-semibold py-2 text-xs xl:text-sm">
<li class="xl:mx-3" v-for="(navup, index) in navups" :key="index">
<router-link
:to="navup.to"
class="text-gray-300 px-3 py-2 rounded-sm hover:border-b-2 hover:border-white"
>
{{ navup.title }}
</router-link>
</li>
</ul>
<div class="flex items-center mx-5 sm:mr-20 lg:hidden">
<TheMobileButtonHumburger />
<div class="mx-3 flex xs:hidden">LOGO</div>
</div>
<div class="hidden items-center 2xl:flex">Пн-Пт. 9:00 - 17:30</div>
<div class="flex mx-5 lg:mx-0">
<div class="lg:hidden flex items-center">
<MultiLanguage />
</div>
<div class="flex items-center right-0 py-1">
<TheToggleTheme />
</div>
</div>
</div>
</div>
<TheMobileSidebar />
</template>
<script lang="ts" setup>
import TheMobileButtonHumburger from "./TheMobileButtonHumburger.vue";
import TheMobileSidebar from "./TheMobileSidebar.vue";
<script/>
The component itself looks like this. Components/TheMobileSidebar.vue
<template>
<div
class="flex flex-col lg:hidden w-[300px] h-[100%] px-5 py-20 xs:py-10 text-gray-300 bg-black z-30 fixed bg-opacity-50 backdrop-filter backdrop-blur-lg"
:class="{ '-left-full': !isShowSidebar }"
>
<ul class="w-full h-full" v-for="(navup, id) in navups" :key="id">
<li>{{ navup.title }}</li>
</ul>
</div>
</template>
<script lang="ts" setup>
import { useStore } from "@/store";
import { ref } from "vue";
const store = useStore();
const navups = store.getters.GET_NAVUPS;
let isShowSidebar = ref(store.getters.GET_SHOWSIDEBAR);
//const theme = computed(() => store.getters.GET_THEME);
// watch(isShowSidebar, (newSidebar, oldSidebar) => {
// const toggleSidebar = store.getters.GET_SHOWSIDEBAR;
// newSidebar === false
// ? commit(MutationTypes.SET_SHOWSIDEBAR, true)
// : commit(MutationTypes.SET_SHOWSIDEBAR, false);
// });
// console.log(isShowSidebar);
</script>
The button itself looks like this Components/TheMobileButtonHumburger.vue
<template>
<button class="cursor-pointer border-2 border-white rounded-md" @click="toggleSidebar">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h7"
/>
</svg>
</button>
</template>
<script lang="ts">
import { ActionTypes } from "@/store/modules/action-types";
import { defineComponent } from "vue";
import { useStore } from "vuex";
export default defineComponent({
name: "toggleSidebar",
setup() {
const store = useStore();
const toggleSidebar = () => {
store.dispatch(ActionTypes.toggle_sidebar);
console.log(store.getters.GET_SHOWSIDEBAR);
};
return { toggleSidebar };
},
});
</script>