From 586a172755e56f733a40c80f96ddc798eeb27a9d Mon Sep 17 00:00:00 2001 From: Pratyush Tiwary Date: Sun, 22 Oct 2023 11:02:51 +0530 Subject: [PATCH 1/4] Fixed import issue and added options for module in HomePage --- src/assets/main.css | 8 +++ src/hooks/useStorage.ts | 2 +- src/stores/routes.ts | 47 ++++++++----- src/types/menuItem.ts | 5 ++ src/views/HomeView.vue | 142 +++++++++++++++++++++++++++++++++++----- tsconfig.json | 5 ++ 6 files changed, 176 insertions(+), 33 deletions(-) create mode 100644 src/types/menuItem.ts diff --git a/src/assets/main.css b/src/assets/main.css index a400b92..95a2c01 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -225,4 +225,12 @@ pre code.hljs { .mdc-button--outlined:not(:disabled) { border-color: var(--color-border) !important; +} + +.mdc-menu-surface { + background-color: var(--color-background) !important; +} + +.material-icons { + color: var(--color-text) !important; } \ No newline at end of file diff --git a/src/hooks/useStorage.ts b/src/hooks/useStorage.ts index bc1a131..6abdcd3 100644 --- a/src/hooks/useStorage.ts +++ b/src/hooks/useStorage.ts @@ -1,5 +1,5 @@ const storageHandler = { - load(key: string, autoParse = true) { + load(key: string, autoParse = true): any { try { let content = localStorage.getItem(key) if (content === 'undefined') { diff --git a/src/stores/routes.ts b/src/stores/routes.ts index 7cb57fb..1b46f60 100644 --- a/src/stores/routes.ts +++ b/src/stores/routes.ts @@ -1,31 +1,48 @@ +import useStorage from '@/hooks/useStorage' import routes from '@/routes' +import { type Routes } from '@/types/route' import { defineStore } from 'pinia' +const storage = useStorage() + export const useRoutes = defineStore('routes', { state: () => ({ routes }), getters: { - getRoute: (state) => (slug: string) => { - return state.routes[slug] + isDeleted: + () => + (slug: string): boolean => { + const deleted_routes: string[] = storage.load('deletedModules') || [] + return deleted_routes.includes(slug) + }, + getRoute(state) { + return (slug: string) => (this.isDeleted(slug) ? null : state.routes[slug]) }, - getRoutes: (state) => () => { - return state.routes + getRoutes(state) { + return (): Routes => + Object.keys(state.routes) + .filter((e: string) => !this.isDeleted(e)) + .reduce((a, b) => ({ ...a, [b]: routes[b] }), {}) }, - search: (state) => (term: string) => { - const query = new RegExp(term, 'gi') + search() { + const self = this + return (term: string) => { + const query = new RegExp(term, 'gi') + const routes = self.getRoutes() - if (term.replace(/\s/g, '') === '') { - return state.routes - } + if (term.replace(/\s/g, '') === '') { + return routes + } - return Object.keys(state.routes) - .filter((e) => { - const route = state.routes[e] + return Object.keys(routes) + .filter((e: string) => { + const route = routes[e] - return route.name.match(query) - }) - .reduce((a, b) => ({ ...a, [b]: state.routes[b] }), {}) + return route.name.match(query) + }) + .reduce((a, b) => ({ ...a, [b]: routes[b] }), {}) + } } } }) diff --git a/src/types/menuItem.ts b/src/types/menuItem.ts new file mode 100644 index 0000000..bfadbff --- /dev/null +++ b/src/types/menuItem.ts @@ -0,0 +1,5 @@ +export interface MenuItem { + index: number + text: string + value: string +} diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 5614d9b..2b116a8 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -5,10 +5,10 @@ import useThrottle from "@/hooks/useThrottle"; import { useRoutes } from "@/stores/routes"; import { useSnackbar } from '@/stores/snackbar'; import { type NestedComponentRef } from '@/types/componentRef'; +import { type MenuItem } from '@/types/menuItem'; import { onMounted, onUnmounted, ref } from "vue"; import { RouterLink } from "vue-router"; const snackbarStore = useSnackbar(); - const routesState = useRoutes(); const allRoutes = routesState.getRoutes(); const routes = ref(allRoutes); @@ -16,23 +16,31 @@ const isSearchState = ref(false); const searchTerm = ref(""); const searchInput = ref(); const storage = useStorage(); -const starredModules = ref(new Set(storage.load("starredModules") || [])); -const expandedSections = ref([true, true]); +const deletedModules = ref>(new Set(storage.load("deletedModules") || [])); +const starredModules = ref>(new Set(storage.load("starredModules") || [])); +const expandedSections = ref([true, true, false]); +const showOptions = ref(false); +const optionsMenuPosition = ref<{ top: number, left: number }>({ + top: 0, + left: 0 +}); +const selectedModuleId = ref(null); function handleChange(newVal: string) { searchTerm.value = newVal; + expandedSections.value[1] = true; routes.value = routesState.search(newVal) isSearchState.value = true; } function reset() { - routes.value = allRoutes; + routes.value = routesState.getRoutes(); isSearchState.value = false; } const starModule = (e: Event, moduleId: any) => { - e.preventDefault(); + if (e) e.preventDefault(); try { starredModules.value = new Set(starredModules.value); if (!starredModules.value.has(moduleId)) { @@ -51,6 +59,54 @@ const starModule = (e: Event, moduleId: any) => { } } +const deleteModule = (e: Event, moduleId: any) => { + if (e) e.preventDefault(); + try { + deletedModules.value = new Set(deletedModules.value); + if (!deletedModules.value.has(moduleId)) { + deletedModules.value.add(moduleId); + } else { + deletedModules.value.delete(moduleId); + } + storage.save('deletedModules', Array.from(deletedModules.value)); + routes.value = routesState.getRoutes(); + } catch (e) { + if (e instanceof Error) { + snackbarStore.show(`Failed to delete module! Error: ${e.message}`, 'error') + } + } +} + +function openOptions(e: Event, id: string) { + e.preventDefault(); + showOptions.value = true; + const { top, left } = (e.target as Element).getBoundingClientRect(); + optionsMenuPosition.value = { + top: window.scrollY + top, + left: window.scrollX + left + }; + selectedModuleId.value = id; +} + +function performAction(menuItem: MenuItem) { + const moduleId = selectedModuleId.value; + switch (menuItem.text) { + case 'Star Module': + case 'Unstar Module': + starModule(null, moduleId) + break; + case 'Remove': + case 'Restore': + deleteModule(null, moduleId) + break; + } +} + +function closeOptions() { + selectedModuleId.value = null; + showOptions.value = false; +} + function handleKeyPress(e: KeyboardEvent) { const key = e.key || e.code || e.which || e.keyCode; if ((key === 'p' || key === 'P' || key === 'KeyP' || key == 80) && e.ctrlKey) { @@ -85,19 +141,20 @@ onUnmounted(() => { @reset="reset" ref="searchInput" :value="searchTerm" />
-
+
No Starred Module Found!
- + - + {{ routes[route].icon }} @@ -109,15 +166,15 @@ onUnmounted(() => {
- + {{ routes[route].icon }} @@ -127,6 +184,49 @@ onUnmounted(() => {
+ + +
+
+ No Deleted Module Found! +
+ + + + + {{ routesState.routes[route].icon }} + +

{{ routesState.routes[route].name }}

+
+
+
+
+
+ + + + + {{ starredModules.has(selectedModuleId) ? 'favorite' : 'favorite_border' }} + + {{ starredModules.has(selectedModuleId) ? 'Unstar Module' : 'Star Module' + }} + + + + {{ deletedModules.has(selectedModuleId) ? 'restore_from_trash' : 'delete' }} + + {{ deletedModules.has(selectedModuleId) ? 'Restore' : 'Remove' }} + + + +
No Module Found!
@@ -162,6 +262,10 @@ main { margin: 5px; } +.modules .deleted { + cursor: no-drop !important; +} + .modules .module .content { width: 100%; height: 250px; @@ -208,10 +312,14 @@ main { font-size: 24px; } -.star { +.options { position: absolute; top: 10px; - right: 10px; + right: 5px; +} + +.menu-anchor { + position: absolute; } .collapse { diff --git a/tsconfig.json b/tsconfig.json index ca23412..7be2243 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,11 @@ { "include": ["env.d.ts", "global.d.ts", "src/**/*", "src/**/*.vue"], "files": [], + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + }, "references": [ { "path": "./tsconfig.node.json" From a2398ddbdc427f1430d728ca5869ea9ff0f4093b Mon Sep 17 00:00:00 2001 From: Pratyush Tiwary Date: Sun, 22 Oct 2023 11:32:19 +0530 Subject: [PATCH 2/4] Fixed failing build --- src/stores/routes.ts | 5 +++-- src/views/HomeView.vue | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/stores/routes.ts b/src/stores/routes.ts index 1b46f60..83d2863 100644 --- a/src/stores/routes.ts +++ b/src/stores/routes.ts @@ -1,6 +1,6 @@ import useStorage from '@/hooks/useStorage' import routes from '@/routes' -import { type Routes } from '@/types/route' +import { type Route, type Routes } from '@/types/route' import { defineStore } from 'pinia' const storage = useStorage() @@ -17,7 +17,8 @@ export const useRoutes = defineStore('routes', { return deleted_routes.includes(slug) }, getRoute(state) { - return (slug: string) => (this.isDeleted(slug) ? null : state.routes[slug]) + return (slug: string): Route | undefined => + this.isDeleted(slug) ? undefined : state.routes[slug] }, getRoutes(state) { return (): Routes => diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 2b116a8..e06f620 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -39,7 +39,7 @@ function reset() { } -const starModule = (e: Event, moduleId: any) => { +const starModule = (e: Event | null, moduleId: any) => { if (e) e.preventDefault(); try { starredModules.value = new Set(starredModules.value); @@ -59,7 +59,7 @@ const starModule = (e: Event, moduleId: any) => { } } -const deleteModule = (e: Event, moduleId: any) => { +const deleteModule = (e: Event | null, moduleId: any) => { if (e) e.preventDefault(); try { deletedModules.value = new Set(deletedModules.value); @@ -211,18 +211,18 @@ onUnmounted(() => { left: optionsMenuPosition.left + 'px' }"> - + - {{ starredModules.has(selectedModuleId) ? 'favorite' : 'favorite_border' }} + {{ starredModules.has(selectedModuleId || '') ? 'favorite' : 'favorite_border' }} - {{ starredModules.has(selectedModuleId) ? 'Unstar Module' : 'Star Module' + {{ starredModules.has(selectedModuleId || '') ? 'Unstar Module' : 'Star Module' }} - {{ deletedModules.has(selectedModuleId) ? 'restore_from_trash' : 'delete' }} + {{ deletedModules.has(selectedModuleId || '') ? 'restore_from_trash' : 'delete' }} - {{ deletedModules.has(selectedModuleId) ? 'Restore' : 'Remove' }} + {{ deletedModules.has(selectedModuleId || '') ? 'Restore' : 'Remove' }} From aa95a4bbb1205b287c91313fc71fcbb1b30c2c82 Mon Sep 17 00:00:00 2001 From: Pratyush Tiwary Date: Mon, 23 Oct 2023 21:46:36 +0530 Subject: [PATCH 3/4] Optimized for small screens --- src/hooks/useMediaQuery.ts | 17 ++++++++++++++++ src/views/HomeView.vue | 41 ++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 src/hooks/useMediaQuery.ts diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts new file mode 100644 index 0000000..455d8ab --- /dev/null +++ b/src/hooks/useMediaQuery.ts @@ -0,0 +1,17 @@ +import { ref } from 'vue' + +/** + * Returns boolean ref, whose value is true if the passed media query + * matches else returns false + * @param mediaQuery string + */ +export default function useMediaQuery(mediaQuery: string) { + const matcher = window.matchMedia(mediaQuery) + const matchMedia = ref(matcher.matches) + + matcher.onchange = (e) => { + matchMedia.value = e.matches + } + + return matchMedia +} diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index e06f620..d1cdbf0 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -1,5 +1,6 @@