Redesign page search input area

+ redefine global search input colors
+ redesign settings page
+ move settings text to a strings module
+ add title and description to now playing settings
This commit is contained in:
geoffrey45
2022-09-27 21:28:42 +03:00
committed by Mungai Njoroge
parent 460695fd87
commit 62b9aa7a3e
23 changed files with 181 additions and 143 deletions
-2
View File
@@ -6,7 +6,6 @@
<section <section
id="app-grid" id="app-grid"
:class="{ :class="{
showAltNP: settings.use_sidebar && settings.use_alt_np,
noSidebar: !settings.use_sidebar || !xl, noSidebar: !settings.use_sidebar || !xl,
extendWidth: settings.extend_width && settings.extend_width_enabled, extendWidth: settings.extend_width && settings.extend_width_enabled,
addBorderRight: xxl && !settings.extend_width, addBorderRight: xxl && !settings.extend_width,
@@ -51,7 +50,6 @@ import Notification from "@/components/Notification.vue";
import NavBar from "@/components/nav/NavBar.vue"; import NavBar from "@/components/nav/NavBar.vue";
import LeftSidebar from "./components/LeftSidebar/index.vue"; import LeftSidebar from "./components/LeftSidebar/index.vue";
import RightSideBar from "@/components/RightSideBar/Main.vue"; import RightSideBar from "@/components/RightSideBar/Main.vue";
import SearchInput from "@/components/RightSideBar/SearchInput.vue";
import NowPlayingRight from "@/components/RightSideBar/NowPlayingRight.vue"; import NowPlayingRight from "@/components/RightSideBar/NowPlayingRight.vue";
const queue = useQStore(); const queue = useQStore();
+13 -5
View File
@@ -9,6 +9,11 @@
gap: 0 1.5rem; gap: 0 1.5rem;
height: 100%; height: 100%;
border: solid 1px $gray3;
border-top: none;
border-bottom: none;
margin: 0 auto;
max-width: 1720px;
} }
#acontent { #acontent {
@@ -21,6 +26,7 @@
.r-sidebar { .r-sidebar {
grid-area: r-sidebar; grid-area: r-sidebar;
border-left: solid 1px $gray3;
} }
.topnav { .topnav {
@@ -33,8 +39,9 @@
grid-area: l-sidebar; grid-area: l-sidebar;
display: grid; display: grid;
grid-template-rows: 1fr max-content; grid-template-rows: 1fr max-content;
border-top: none !important; // border-top: none !important;
border-bottom: none !important; // border-bottom: none !important;
border-right: solid 1px $gray3;
} }
.b-bar { .b-bar {
@@ -44,9 +51,10 @@
// ====== MODIFIERS ======= // ====== MODIFIERS =======
#app-grid.extendWidth { #app-grid.extendWidth {
padding-right: 0;
border-right: none;
max-width: 100%; max-width: 100%;
padding-right: 0;
border-left: none;
border-right: none;
} }
#app-grid.noSidebar { #app-grid.noSidebar {
@@ -58,7 +66,7 @@
#acontent { #acontent {
margin-right: 0 !important; margin-right: 0 !important;
padding-right: 1rem !important; padding-right: $medium !important;
} }
.topnav { .topnav {
+4 -3
View File
@@ -33,7 +33,7 @@
} }
.circular { .circular {
border-radius: 50%; border-radius: 10rem;
} }
.flex { .flex {
@@ -82,12 +82,13 @@ button {
} }
&:hover { &:hover {
background-image: linear-gradient(70deg, $darkestblue, $darkblue); background-image: linear-gradient($darkestblue, $darkblue);
} }
} }
.btn-active { .btn-active {
background-image: linear-gradient(70deg, $darkestblue, $darkblue); background-image: linear-gradient($darkestblue, $darkblue);
} }
.btn-more { .btn-more {
+1 -2
View File
@@ -36,8 +36,7 @@ body {
#app { #app {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0 auto;
max-width: 1720px;
} }
a { a {
+1 -1
View File
@@ -25,7 +25,7 @@ $gray2: rgb(99, 99, 102);
$gray3: rgb(72, 72, 74); $gray3: rgb(72, 72, 74);
$gray4: rgb(58, 58, 60); $gray4: rgb(58, 58, 60);
$gray5: rgb(44, 44, 46); $gray5: rgb(44, 44, 46);
$body: $gray; $body: rgba(0, 0, 0, 0.95);
$red: #ff453a; $red: #ff453a;
$blue: #0a84ff; $blue: #0a84ff;
+16 -16
View File
@@ -14,23 +14,23 @@
} }
} }
#app-grid.isSmall, // #app-grid.isSmall,
#app-grid.disableSidebar { // #app-grid.disableSidebar {
#acontent { // #acontent {
margin-right: -$small; // margin-right: -$small;
padding-right: $medium; // padding-right: $medium;
.search-view { // .search-view {
margin-right: -0.8rem; // margin-right: -0.8rem;
} // }
} // }
} // }
#app-grid.isSmall #page-search { // #app-grid.isSmall #page-search {
margin-right: $smaller; // margin-right: $smaller;
} // }
#app-grid.disableSidebar #page-search { // #app-grid.disableSidebar #page-search {
margin-right: $smaller; // margin-right: $smaller;
} // }
} }
+3 -3
View File
@@ -1,17 +1,17 @@
<template> <template>
<div class="l-sidebar noscroll border"> <div class="l-sidebar noscroll">
<div class="withlogo"> <div class="withlogo">
<Logo /> <Logo />
<Navigation /> <Navigation />
</div> </div>
<nowPlaying v-if="settings.show_default_np" /> <NPImg v-if="settings.use_np_img" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Navigation from "@/components/LeftSidebar/Navigation.vue"; import Navigation from "@/components/LeftSidebar/Navigation.vue";
import nowPlaying from "@/components/LeftSidebar/nowPlaying.vue"; import NPImg from "@/components/LeftSidebar/nowPlayingImg.vue";
import Logo from "@/components/Logo.vue"; import Logo from "@/components/Logo.vue";
import useSettingsStore from "@/stores/settings"; import useSettingsStore from "@/stores/settings";
+2 -8
View File
@@ -1,10 +1,5 @@
<template> <template>
<div <div class="r-sidebar">
class="r-sidebar border"
:style="{
marginBottom: !settings.show_alt_np ? '-1rem' : '',
}"
>
<SearchInput /> <SearchInput />
<div class="r-content noscroll"> <div class="r-content noscroll">
<div class="r-dash" v-if="tabs.current === tabs.tabs.home"> <div class="r-dash" v-if="tabs.current === tabs.tabs.home">
@@ -25,11 +20,9 @@ import Search from "./Search/Main.vue";
import Queue from "./Queue.vue"; import Queue from "./Queue.vue";
import DashBoard from "./Home/Main.vue"; import DashBoard from "./Home/Main.vue";
import useTabStore from "../../stores/tabs"; import useTabStore from "../../stores/tabs";
import useSettingsStore from "@/stores/settings";
import SearchInput from "./SearchInput.vue"; import SearchInput from "./SearchInput.vue";
const tabs = useTabStore(); const tabs = useTabStore();
const settings = useSettingsStore();
</script> </script>
<style lang="scss"> <style lang="scss">
@@ -41,6 +34,7 @@ const settings = useSettingsStore();
padding-bottom: 1rem; padding-bottom: 1rem;
border-top: none; border-top: none;
border-bottom: none; border-bottom: none;
margin-bottom: -1rem;
.gsearch-input { .gsearch-input {
height: 2.5rem; height: 2.5rem;
+32 -21
View File
@@ -1,24 +1,20 @@
<template> <template>
<div class="gsearch-input"> <div class="gsearch-input">
<div id="ginner" tabindex="0" class="bg-primary"> <div id="ginner" tabindex="0" ref="inputRef">
<button <button
:title=" :title="
tabs.current === tabs.tabs.search ? 'back to queue' : 'go to search' tabs.current === tabs.tabs.search ? 'back to queue' : 'go to search'
" "
@click.prevent="handleButton"
:class="{ no_bg: on_nav }"
> >
<SearchSvg <SearchSvg v-if="on_nav || tabs.current === tabs.tabs.queue" />
v-if="on_nav || tabs.current === tabs.tabs.queue" <BackSvg v-else-if="tabs.current === tabs.tabs.search" />
@click.prevent="!on_nav && tabs.switchToSearch()"
/>
<BackSvg
v-else-if="tabs.current === tabs.tabs.search"
@click.prevent="tabs.switchToQueue"
/>
</button> </button>
<input <input
id="globalsearch" id="globalsearch"
v-model.trim="search.query" v-model.trim="search.query"
placeholder="Search your library" placeholder="Start typing to search"
type="text" type="text"
autocomplete="off" autocomplete="off"
@blur.prevent="removeFocusedClass" @blur.prevent="removeFocusedClass"
@@ -29,30 +25,39 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from "vue"; import { onMounted, ref } from "vue";
import BackSvg from "@/assets/icons/arrow.svg"; import BackSvg from "@/assets/icons/arrow.svg";
import SearchSvg from "@/assets/icons/search.svg"; import SearchSvg from "@/assets/icons/search.svg";
import useSearchStore from "@/stores/search"; import useSearchStore from "@/stores/search";
import useTabStore from "@/stores/tabs"; import useTabStore from "@/stores/tabs";
defineProps<{ const props = defineProps<{
on_nav?: boolean; on_nav?: boolean;
}>(); }>();
const search = useSearchStore();
const tabs = useTabStore(); const tabs = useTabStore();
let classList: DOMTokenList | undefined; const search = useSearchStore();
onMounted(() => {
classList = document.getElementById("ginner")?.classList;
});
// HANDLE FOCUS
const inputRef = ref<HTMLElement>();
function addFocusedClass() { function addFocusedClass() {
classList?.add("search-focused"); inputRef.value?.classList.add("search-focused");
} }
function removeFocusedClass() { function removeFocusedClass() {
classList?.remove("search-focused"); inputRef.value?.classList.remove("search-focused");
}
// @end
function handleButton() {
if (props.on_nav) return;
if (tabs.current === tabs.tabs.search) {
tabs.switchToQueue();
} else {
tabs.switchToSearch();
}
} }
</script> </script>
@@ -68,12 +73,14 @@ function removeFocusedClass() {
align-items: center; align-items: center;
gap: $small; gap: $small;
border-radius: 3rem; border-radius: 3rem;
outline: solid 1px $gray3;
button { button {
background: transparent; background: transparent;
width: 3rem; width: 3rem;
padding: 0; padding: 0;
border-radius: 3rem; border-radius: 3rem;
height: 100%;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
@@ -82,6 +89,10 @@ function removeFocusedClass() {
} }
} }
button.no_bg {
pointer-events: none;
}
input { input {
width: 100%; width: 100%;
border: none; border: none;
@@ -94,6 +105,6 @@ function removeFocusedClass() {
} }
} }
.search-focused { .search-focused {
outline: solid $accent; outline: solid $darkblue !important;
} }
</style> </style>
@@ -13,7 +13,7 @@ defineProps<{
<style lang="scss"> <style lang="scss">
.switch { .switch {
height: 1.5rem; height: 1.5rem;
background-color: $gray; background-color: $gray3;
width: 2.5rem; width: 2.5rem;
padding: $smaller; padding: $smaller;
position: relative; position: relative;
+9 -4
View File
@@ -1,10 +1,10 @@
<template> <template>
<div class="settingsgroup"> <div class="settingsgroup">
<div v-if="group.name || group.desc"> <div v-if="group.title || group.desc" class="info">
<h4 v-if="group.name">{{ group.name }}</h4> <h4 v-if="group.title">{{ group.title }}</h4>
<div class="desc" v-if="group.desc">{{ group.desc }}</div> <div class="desc" v-if="group.desc">{{ group.desc }}</div>
</div> </div>
<div class="setting rounded border pad-lg"> <div class="setting rounded pad-lg">
<div <div
v-for="(setting, index) in group.settings" v-for="(setting, index) in group.settings"
:key="index" :key="index"
@@ -44,16 +44,21 @@ defineProps<{
margin-top: 0; margin-top: 0;
} }
.info {
margin-left: $smaller;
}
h4 { h4 {
margin: $small auto; margin: $small auto;
} }
.desc { .desc {
opacity: 0.5; opacity: 0.5;
font-size: 0.9rem; font-size: 0.8rem;
} }
.setting { .setting {
background-color: $gray;
display: grid; display: grid;
gap: 1rem; gap: 1rem;
+10 -11
View File
@@ -25,15 +25,15 @@
</div> </div>
</div> </div>
</div> </div>
<div> <SearchInput :page="Routes.folder" />
<Input :page="Routes.folder" /> <!-- <div>
</div> </div> -->
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Input from "@/components/shared/Input.vue"; import SearchInput from "@/components/shared/Input.vue";
import { Routes } from "@/composables/enums"; import { Routes } from "@/composables/enums";
import { subPath } from "@/interfaces"; import { subPath } from "@/interfaces";
import { focusElem } from "@/utils"; import { focusElem } from "@/utils";
@@ -55,7 +55,6 @@ onUpdated(() => {
.folder { .folder {
display: grid; display: grid;
grid-template-columns: 1fr max-content; grid-template-columns: 1fr max-content;
gap: $small;
.fname-wrapper { .fname-wrapper {
width: 100%; width: 100%;
@@ -122,13 +121,13 @@ onUpdated(() => {
} }
} }
.inthisfolder > .text { // .inthisfolder > .text {
color: #fff; // color: #fff;
font-weight: bold; // font-weight: bold;
background-color: $gray; // background-color: $gray;
transition: all 0.5s; // transition: all 0.5s;
} // }
} }
} }
} }
+24 -24
View File
@@ -1,13 +1,16 @@
<template> <template>
<div class="header-input-wrapper rounded-sm" :class="{ showInput: clicked }"> <div class="header-input-wrapper rounded-sm" :class="{ showInput: clicked }">
<div class="search-svg" @click="handleFocus"> <button
<SearchSvg /> class="search-btn circular"
</div> :class="{ 'btn-active': clicked }"
@click="handleFocus"
>
<SearchSvg /> Search
</button>
<input <input
type="search" class="header-input pad-sm"
class="header-input rounded-sm pad-sm"
:class="{ showInput: clicked }" :class="{ showInput: clicked }"
placeholder="Search here" placeholder="Type to search"
v-model.trim="query" v-model.trim="query"
id="page-search" id="page-search"
ref="inputRef" ref="inputRef"
@@ -26,7 +29,7 @@ import useFolderStore from "@/stores/pages/folder";
import { Routes } from "@/composables/enums"; import { Routes } from "@/composables/enums";
import SearchSvg from "@/assets/icons/search.svg"; import SearchSvg from "@/assets/icons/search.svg";
const clicked = ref(true); const clicked = ref(false);
const [playlist, album, folder] = [ const [playlist, album, folder] = [
usePStore(), usePStore(),
useAlbumStore(), useAlbumStore(),
@@ -82,18 +85,19 @@ if (source) {
<style lang="scss"> <style lang="scss">
.header-input-wrapper { .header-input-wrapper {
&.showInput {
width: 21.5rem;
}
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;
width: 1.5rem; width: 7rem;
gap: $small;
transition: all 0.25s; transition: all 0.25s;
&.showInput {
width: 15rem;
}
} }
.header-input { .header-input {
background-color: $gray3; background-color: transparent;
outline: none; outline: none;
border: none; border: none;
color: inherit; color: inherit;
@@ -102,9 +106,12 @@ if (source) {
transition: all 0.25s $overshoot; transition: all 0.25s $overshoot;
opacity: 0; opacity: 0;
transform: translateY(-1rem); transform: translateY(-1rem);
border-radius: 3rem;
padding-left: 1rem;
outline: solid 1px $gray1;
&:focus { &:focus {
outline: solid; outline: solid $darkblue;
} }
&.showInput { &.showInput {
@@ -114,16 +121,9 @@ if (source) {
} }
} }
.search-svg { .search-btn {
margin-top: $smaller;
cursor: pointer; cursor: pointer;
width: 2.25rem; padding: 0 $small;
height: 2rem; padding-right: 1rem;
z-index: 100;
svg {
display: block;
margin: 0 auto;
}
} }
</style> </style>
+1 -1
View File
@@ -20,7 +20,7 @@ export interface Setting {
} }
export interface SettingGroup { export interface SettingGroup {
name?: string; title?: string;
desc?: string; desc?: string;
settings: Setting[]; settings: Setting[];
} }
+2 -2
View File
@@ -3,7 +3,7 @@
<div class="header-list-layout"> <div class="header-list-layout">
<div <div
v-bind="containerProps" v-bind="containerProps"
style="height: 100%;" style="height: 100%"
:style="{ paddingTop: !no_header ? headerHeight - 64 + 24 + 'px' : 0 }" :style="{ paddingTop: !no_header ? headerHeight - 64 + 24 + 'px' : 0 }"
@scroll="handleScroll" @scroll="handleScroll"
> >
@@ -118,7 +118,7 @@ function handleScroll(e: Event) {
<style lang="scss"> <style lang="scss">
.header-list-layout { .header-list-layout {
margin-right: calc(0rem - ($medium)); margin-right: -$medium;
height: 100%; height: 100%;
.scrollable { .scrollable {
+2 -1
View File
@@ -1,10 +1,11 @@
import { Setting, SettingType } from "@/interfaces/settings"; import { Setting, SettingType } from "@/interfaces/settings";
import useSettingsStore from "@/stores/settings"; import useSettingsStore from "@/stores/settings";
import { appWidthStrings } from "./../strings";
const settings = useSettingsStore; const settings = useSettingsStore;
const extend_to_full_width: Setting = { const extend_to_full_width: Setting = {
title: "Extend app to full screen width", title: appWidthStrings.settings.extend,
type: SettingType.binary, type: SettingType.binary,
source: () => settings().extend_width, source: () => settings().extend_width,
action: () => settings().toggleExtendWidth(), action: () => settings().toggleExtendWidth(),
+10 -2
View File
@@ -1,13 +1,21 @@
import { SettingCategory } from "@/interfaces/settings"; import { SettingCategory } from "@/interfaces/settings";
import * as strings from "../strings";
import extendWidth from "./extend-width";
import nowPlaying from "./now-playing"; import nowPlaying from "./now-playing";
import sidebarSettings from "./sidebar"; import sidebarSettings from "./sidebar";
import extendWidth from "./extend-width";
const npStrings = strings.nowPlayingStrings;
export default { export default {
title: "General", title: "General",
groups: [ groups: [
{ {
settings: [...sidebarSettings, ...nowPlaying, ...extendWidth], settings: [...sidebarSettings, ...extendWidth],
},
{
title: npStrings.title,
desc: npStrings.desc,
settings: [...nowPlaying],
}, },
], ],
} as SettingCategory; } as SettingCategory;
+6 -6
View File
@@ -1,14 +1,14 @@
import { Setting, SettingType } from "@/interfaces/settings"; import { Setting, SettingType } from "@/interfaces/settings";
import useSettingsStore from "@/stores/settings"; import useSettingsStore from "@/stores/settings";
import { nowPlayingStrings as data } from "../strings";
const settings = useSettingsStore; const settings = useSettingsStore;
const use_alt_np: Setting = { const disable_np_img: Setting = {
title: "Use alternate now playing card", title: data.settings.album_art,
type: SettingType.binary, type: SettingType.binary,
source: () => settings().use_alt_np, source: () => settings().use_np_img,
inactive: () => settings().disable_show_alt_np, action: () => settings().toggleUseNPImg(),
action: () => settings().toggleUseRightNP(),
}; };
export default [use_alt_np]; export default [disable_np_img];
+2 -1
View File
@@ -1,10 +1,11 @@
import { sidebarStrings } from "./../strings";
import { Setting, SettingType } from "@/interfaces/settings"; import { Setting, SettingType } from "@/interfaces/settings";
import useSettingsStore from "@/stores/settings"; import useSettingsStore from "@/stores/settings";
const settings = useSettingsStore; const settings = useSettingsStore;
const use_sidebar: Setting = { const use_sidebar: Setting = {
title: "Use right sidebar", title: sidebarStrings.settings.use_sidebar,
type: SettingType.binary, type: SettingType.binary,
source: () => settings().use_sidebar, source: () => settings().use_sidebar,
action: () => settings().toggleDisableSidebar(), action: () => settings().toggleDisableSidebar(),
+30
View File
@@ -0,0 +1,30 @@
/**
* Settings data strings
*/
interface S {
title?: string;
desc?: string;
settings: {
[key: string]: string;
};
}
export const nowPlayingStrings = {
title: "Now playing",
desc: "Settings related to the current song",
settings: {
album_art: "Show album art on the left sidebar",
},
} as S;
export const appWidthStrings = {
settings: {
extend: "Extend app to full screen width",
},
} as S;
export const sidebarStrings = <S>{
settings: {
use_sidebar: "Show right sidebar",
},
};
+4 -17
View File
@@ -1,16 +1,15 @@
import { xxl, xl } from "@/composables/useBreakpoints"; import { xxl } from "@/composables/useBreakpoints";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
export default defineStore("settings", { export default defineStore("settings", {
state: () => ({ state: () => ({
use_alt_np: false, use_np_img: false,
use_sidebar: true, use_sidebar: true,
extend_width: false, extend_width: false,
}), }),
actions: { actions: {
toggleUseRightNP() { toggleUseNPImg() {
if (!this.use_sidebar) return; this.use_np_img = !this.use_np_img;
this.use_alt_np = !this.use_alt_np;
}, },
toggleDisableSidebar() { toggleDisableSidebar() {
this.use_sidebar = !this.use_sidebar; this.use_sidebar = !this.use_sidebar;
@@ -20,18 +19,6 @@ export default defineStore("settings", {
}, },
}, },
getters: { getters: {
show_alt_np(): boolean {
return xl.value && this.use_sidebar && this.use_alt_np;
},
show_default_np(): boolean {
return !this.show_alt_np;
},
disable_show_alt_np(): boolean {
return !xl.value || !this.use_sidebar;
},
hide_queue_page(): boolean {
return this.use_sidebar;
},
extend_width_enabled(): boolean { extend_width_enabled(): boolean {
return xxl.value return xxl.value
}, },
+2 -6
View File
@@ -1,6 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"allowSyntheticDefaultImports": true,
"useDefineForClassFields": true, "useDefineForClassFields": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Node",
@@ -17,11 +18,6 @@
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
}, },
"include": [ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "src/**/*.svg"],
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.vue",
"src/**/*.svg"
],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
} }