Version 1.0.0 - Ajout du design et début search bar

This commit is contained in:
2025-08-07 15:58:18 +02:00
parent 7b25fcfa5c
commit b5dc2a9e37
15 changed files with 333 additions and 23 deletions

View File

@@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "vite --host --port 8080",
"preview": "vite preview --host --port 8080",
"build": "vite build"
},
"dependencies": {

View File

@@ -24,7 +24,7 @@
--primary: #FFFFFF;
--primary-hover: #292b26;
--secondary: #EAEAEA;
--tertiary: #cacaca;
--tertiary: #d3d3d3;
--text: #111210;
--text-inverse: #FFFFFF;
--text-secondary: #404040;
@@ -60,6 +60,11 @@ html, body {
}
#app {
height: 100%;
display: flex;
}
.no-decoration {
text-decoration: none !important;
}

View File

@@ -103,7 +103,9 @@ function updateServerInfo() {
.container {
display: flex;
flex-direction: column;
gap: 10px;;
justify-content: space-between;
gap: 10px;
height: 100%;
}
.itm {
@@ -146,6 +148,7 @@ function updateServerInfo() {
position: absolute;
background-color: var(--tertiary);
border-radius: 0px 0px 10px 10px;
z-index: 1000;
}
.menu-content {

View File

@@ -1,13 +1,41 @@
<template>
<div class="search">
<input type="text" placeholder="Search..." v-model="searchQuery" />
<Icon color="#FFFFFF" icon="fa-solid fa-magnifying-glass" style="width: 20px;" />
<input ref="searchBar" type="text" placeholder="Insérer votre recherche ici" v-model="searchQuery" />
<IconAction
icon="fa-solid fa-cloud-arrow-up"
color="#FFFFFF"
title="Mes fichiers"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { onBeforeUnmount, onMounted, ref } from 'vue';
import IconAction from '../UI/IconAction.vue';
import { IORequest } from '@/utils/IORequest';
import Events from '@/utils/Events';
const searchBar = ref(null);
const searchQuery = ref('');
onMounted(() => {
searchBar.value.addEventListener('change', find);
});
function find() {
if (searchQuery.value.trim() === '') {
return;
}
Events.emit("SEARCH_STARTED");
IORequest("/SEARCH", (data) => {
Events.emit("SEARCH_RESULT", data);
}, searchQuery.value);
}
</script>
<style scoped>
@@ -15,18 +43,21 @@ const searchQuery = ref('');
display: flex;
align-items: center;
padding: 10px;
background-color: var(--tertiary);
gap: 5px;
background-color: var(--main);
border-radius: 10px;
box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.50);
}
.search input {
border: none;
outline: none;
background: transparent;
background: var(--secondary);
color: var(--text-primary);
border-radius: 10px;;
flex: 1;
padding: 5px 10px;
border-radius: 5px;
padding: 10px 10px;
border-radius: 20px;
}
.search input::placeholder {

View File

@@ -0,0 +1,95 @@
<template>
<Box v-if="deviceType === 'desktop'" padding="closed" class="view">
<IconAction @click="returnDefault()" v-if="!actualComponent.unclosable" icon="fa-solid fa-xmark" class="close"/>
<component :is="actualComponent.component" v-bind="actualComponent.props"/>
</Box>
<div v-else-if="deviceType === 'mobile' && !actualComponent.default" class="view-mobile">
<IconAction @click="returnDefault()" v-if="!actualComponent.unclosable" icon="fa-solid fa-xmark" class="close"/>
<component :is="actualComponent.component" v-bind="actualComponent.props"/>
</div>
</template>
<script setup>
import Box from '../UI/Box.vue';
import Events from '@/utils/Events';
import { computed, ref, watch } from 'vue';
import Loading from '@/components/Widget/View/LoadingView.vue'
import SearchResults from '@/components/Widget/View/SearchResults.vue';
import Default from '../Widget/View/Default.vue';
import IconAction from '../UI/IconAction.vue';
//TODO: Render compatibility with mobile
//TODO: Make Default.vue a real default view
//TODO: Make the search request
//TODO: Make the medias
const deviceType = ref((window.innerWidth < 768 || window.innerHeight < 607) ? 'mobile' : 'desktop');
watch(() => window.innerWidth, () => {
deviceType.value = (window.innerWidth < 768 || window.innerHeight < 607) ? 'mobile' : 'desktop';
});
const defaultComponent = {
component: Default,
unclosable: true,
default: true,
props: {}
};
function returnDefault() {
actualComponent.value = defaultComponent;
}
const actualComponent = ref(defaultComponent);
Events.on("SEARCH_STARTED", () => {
setLoading("Recherche en cours ...")
})
Events.on("SEARCH_RESULT", (data) => {
console.log("Search results received:", data);
actualComponent.value = {
component: SearchResults,
props: {
results: data
}
};
});
function setLoading(messageLoading) {
actualComponent.value = {
component: Loading,
unclosable: true,
props: {
message: messageLoading
}
};
}
</script>
<style scoped>
.close {
position: absolute;
top: 10px;
right: 10px;
color: var(--text-secondary);
cursor: pointer;
}
.view {
position: relative;
}
.view-mobile {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--background-secondary);
z-index: 1000;
border-radius: 0 !important;
}
</style>

View File

@@ -2,7 +2,7 @@
<div class="settings-container">
<Icon style="width: 20px;" class="settings-icon" :icon="icon"/>
<div class="settings-item">
<p class="settings-title">{{ title }}</p>
<p class="settings-title"><Icon style="width: 20px;" class="settings-icon-replace" :icon="icon"/> {{ title }}</p>
<slot></slot>
</div>
</div>
@@ -20,6 +20,16 @@ defineProps({
})
</script>
<style scoped>
@media screen and (max-width: 768px) {
.settings-icon {
display: none;
}
.settings-icon-replace {
display: unset !important;
}
}
.settings-title {
font-size: 1.1em;
@@ -33,6 +43,10 @@ defineProps({
margin-top: 1px;
}
.settings-icon-replace {
display: none;
}
.settings-container {
display: flex;
gap: 7px;

View File

@@ -86,9 +86,10 @@ defineExpose({
<section>
<div ref="box" :class="showMenu ? `showed firstbox` : 'firstbox'" @click="showMenu = !showMenu">
<template v-if="firstSlot">
<component :is="firstSlot" />
<component style="white-space: break-spaces;" :is="firstSlot" />
</template>
<IconAction
class="icon"
:icon="showMenu ? 'fa-solid fa-angle-up' : 'fa-solid fa-angle-down'"
/>
</div>
@@ -114,6 +115,7 @@ defineExpose({
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
}
.container {
@@ -148,6 +150,7 @@ defineExpose({
padding: 5px;
background-color: var(--tertiary);
cursor: pointer;
white-space: break-spaces;
}
.option:hover {
@@ -160,4 +163,9 @@ defineExpose({
padding-bottom: 0; */
border-radius: 5px 5px 0 0 !important;
}
.icon {
display: flex;
align-items: center;
}
</style>

View File

@@ -11,7 +11,6 @@
</div>
</template>
<script setup>
import { defineProps } from 'vue';
import Avatar from './Avatar.vue';
const props = defineProps({
user: {

View File

@@ -1,5 +1,5 @@
<template>
<div class="metrics" v-if="metrics">
<div class="metrics" v-if="metrics && metrics.length > 0">
<div class="metric" v-for="metric in metrics" :key="metric.name">
<div class="metric-header">
<Icon class="metric-icon" :icon="getIcons(metric.name)" />

View File

@@ -32,7 +32,6 @@
<script setup>
import Events from '@/utils/Events';
import { IORequest } from '@/utils/IORequest';
import { defineProps } from 'vue';
import { ref } from 'vue';
import User from '@/components/UI/User.vue';
import Role from '@/components/UI/Role.vue';

View File

@@ -92,6 +92,7 @@ onMounted(() => {
.version p {
margin: 0;
font-size: 12px;
text-align: center;
color: var(--text-secondary);
}

View File

@@ -0,0 +1,10 @@
<template>
<p>Test</p>
</template>
<script setup>
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,41 @@
<template>
<div class="loading">
<div class="loading-content">
<Icon icon="fa-solid fa-spinner" spin-pulse />
<span>{{ message }}</span>
</div>
<span class="second">Un peu de patience, le groupe se prépare !</span>
</div>
</template>
<script setup>
const props = defineProps({
message: {
type: String,
default: 'Chargement en cours...'
}
});
</script>
<style scoped>
.loading {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100%;
font-size: 2rem;
gap: 10px;
color: var(--text-tertiary);
}
.loading-content {
display: flex;
align-items: center;
gap: 10px;
}
.second {
font-size: 0.8rem;
color: var(--text-secondary);
opacity: 0.8;
}
</style>

View File

@@ -0,0 +1,3 @@
<template>
</template>

View File

@@ -1,8 +1,10 @@
<template>
<SocketEnvironment>
<div class="container">
<GuildHeader/>
<Box>
<Search class="search"/>
<div class="left-side">
<GuildHeader class="guildheader"/>
<Box class="playlist">
<Button @click="router.push('/servers')">Choisir un serveur</Button>
<Button @click="router.push('/terms')">Terms</Button>
<Button @click="router.push('/privacy')">Privacy</Button>
@@ -10,8 +12,11 @@
<Button @click="globalStore.toogleTheme()">Changer le thème</Button>
<p>{{ guildId }}</p>
</Box>
<Search/>
<Account/>
<Account class="account"/>
</div>
<div class="queue"></div>
<View class="view"></View>
<div class="player"></div>
</div>
</SocketEnvironment>
</template>
@@ -29,6 +34,7 @@ import Account from '@/components/Layout/Account.vue';
import events from '@/utils/Events.js';
import GuildHeader from '@/components/Layout/GuildHeader.vue';
import Search from '@/components/Layout/Search.vue';
import View from '@/components/Layout/View.vue';
const props = defineProps({
guildId: {
@@ -98,11 +104,105 @@ function checkGuildAvailability() {
</script>
<style scoped>
.container {
padding: 15px;;;
max-width: 400px;
display: flex;
flex-direction: column;
gap: 20px;
@media screen and (max-width: 768px), screen and (max-height: 607px) {
.container {
display: flex;
gap: 15px;
flex-direction: column;
}
.left-side {
display: flex;
flex-direction: column;
gap: 15px;
}
.guildheader {
order: 0;
}
.account {
order: 1;
}
.playlist {
order: 2;
}
}
@media screen and (min-width: 769px) and (max-width: 1280px) and (min-height: 607px) {
.queue {
display: none
}
.container {
display: grid;
grid-template-columns: 0.8fr repeat(2, 1fr);
grid-template-rows: 0.1fr repeat(2, 1fr) 0.5fr;
grid-column-gap: 15px;
grid-row-gap: 15px;
}
.left-side { grid-area: 1 / 1 / 5 / 2; }
.player { grid-area: 4 / 2 / 5 / 4; }
.search { grid-area: 1 / 2 / 2 / 4; }
.view { grid-area: 2 / 2 / 4 / 4; }
.left-side {
display: flex;
flex-direction: column;
gap: 15px;
height: 100%;
}
.playlist {
flex: 1;
}
}
@media screen and (min-width: 1281px) {
.container {
display: grid;
grid-template-columns: 0.7fr repeat(2, 1fr) 0.8fr;
grid-template-rows: 0.1fr repeat(2, 1fr) 0.5fr;
grid-column-gap: 15px;
grid-row-gap: 15px;
}
.left-side { grid-area: 1 / 1 / 5 / 2; }
.player { grid-area: 4 / 2 / 5 / 4; }
.search { grid-area: 1 / 2 / 2 / 4; }
.queue { grid-area: 1 / 4 / 5 / 5; }
.view { grid-area: 2 / 2 / 4 / 4; }
.left-side {
display: flex;
flex-direction: column;
gap: 15px;
height: 100%;
}
.playlist {
flex: 1;
}
}
.container {
padding: 15px;
width: 100%;
}
.queue, .view, .player {
background-color: var(--secondary);
border-radius: 10px;
}
</style>