Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navbar Icons Improvements #1246

Merged
merged 9 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ declare module '@vue/runtime-core' {
ListItem: typeof import('./src/components/atomic/ListItem.vue')['default']
ManualPipelinePopup: typeof import('./src/components/layout/popups/ManualPipelinePopup.vue')['default']
Navbar: typeof import('./src/components/layout/header/Navbar.vue')['default']
NavbarIcon: typeof import('./src/components/layout/header/NavbarIcon.vue')['default']
NumberField: typeof import('./src/components/form/NumberField.vue')['default']
OrgSecretsTab: typeof import('./src/components/org/settings/OrgSecretsTab.vue')['default']
Panel: typeof import('./src/components/layout/Panel.vue')['default']
Expand Down
5 changes: 3 additions & 2 deletions web/src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"password": "Password",
"url": "URL",
"back": "Back",
"color_scheme_light": "Switch between dark and light mode (currently light mode)",
"color_scheme_dark": "Switch between dark and light mode (currently dark mode)",
"color_scheme_light": "Switch to dark mode",
"color_scheme_dark": "Switch to light mode",
"unknown_error": "An unknown error occurred",
"not_found": {
"not_found": "Whoa 404, either we broke something or you had a typing mishap :-/",
Expand Down Expand Up @@ -292,6 +292,7 @@
}
},
"user": {
"settings": "User Settings",
"oauth_error": "Error while authenticating against OAuth provider",
"internal_error": "Some internal error occurred",
"access_denied": "You are not allowed to login",
Expand Down
8 changes: 1 addition & 7 deletions web/src/components/atomic/IconButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@
@click="doClick"
>
<Icon :name="icon" />
<div
class="absolute left-0 top-0 right-0 bottom-0 flex items-center justify-center"
:class="{
'opacity-100': isLoading,
'opacity-0': !isLoading,
}"
>
<div v-if="isLoading" class="absolute left-0 top-0 right-0 bottom-0 flex items-center justify-center">
<Icon name="loading" class="animate-spin" />
</div>
</button>
Expand Down
36 changes: 21 additions & 15 deletions web/src/components/layout/header/ActivePipelines.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
<template>
<button
class="flex rounded-full w-8 h-8 bg-opacity-30 hover:bg-opacity-50 bg-white items-center justify-center cursor-pointer text-white select-none"
:class="{
spinner: activePipelines.length !== 0,
}"
type="button"
@click="toggle"
>
<div class="spinner-ring ring1" />
<div class="spinner-ring ring2" />
<div class="spinner-ring ring3" />
<div class="spinner-ring ring4" />
{{ activePipelines.length || 0 }}
</button>
<NavbarIcon :title="$t('repo.pipeline.tasks')" class="!p-1.5 relative" @click="toggle">
<div v-if="activePipelines.length > 0" class="spinner">
<div class="spinner-ring ring1" />
<div class="spinner-ring ring2" />
<div class="spinner-ring ring3" />
<div class="spinner-ring ring4" />
</div>
<div
class="flex items-center justify-center h-full w-full font-bold bg-white bg-opacity-15 dark:bg-black dark:bg-opacity-10 rounded-full"
>
{{ activePipelines.length || 0 }}
</div>
</NavbarIcon>
</template>

<script lang="ts">
import { defineComponent, onMounted } from 'vue';

import usePipelineFeed from '~/compositions/usePipelineFeed';

import NavbarIcon from './NavbarIcon.vue';

export default defineComponent({
name: 'ActivePipelines',

components: { NavbarIcon },

setup() {
const pipelineFeed = usePipelineFeed();

Expand All @@ -36,10 +39,13 @@ export default defineComponent({
</script>

<style scoped>
.spinner {
@apply absolute top-0 bottom-0 left-0 right-0;
}
.spinner .spinner-ring {
animation: spinner 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #fff transparent transparent transparent;
@apply w-8 h-8 border-2 rounded-full m-4 absolute;
@apply border-3 rounded-full absolute top-1.5 bottom-1.5 left-1.5 right-1.5;
}
.spinner .ring1 {
animation-delay: -0.45s;
Expand Down
53 changes: 31 additions & 22 deletions web/src/components/layout/header/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,45 @@
<span class="text-xs">{{ version }}</span>
</router-link>
<!-- Repo Link -->
<router-link v-if="user" :to="{ name: 'repos' }" class="navbar-link">
<router-link v-if="user" :to="{ name: 'repos' }" class="navbar-link navbar-clickable">
<span class="flex md:hidden">{{ $t('repos') }}</span>
<span class="hidden md:flex">{{ $t('repositories') }}</span>
</router-link>
<!-- Docs Link -->
<a :href="docsUrl" target="_blank" class="navbar-link hidden md:flex">{{ $t('docs') }}</a>
<a :href="docsUrl" target="_blank" class="navbar-link navbar-clickable hidden md:flex">{{ $t('docs') }}</a>
</div>
<!-- Right Icons Box -->
<div class="flex ml-auto items-center space-x-3 text-white dark:text-gray-400">
<div class="flex ml-auto -m-1.5 items-center space-x-2 text-white dark:text-gray-400">
<!-- Dark Mode Toggle -->
<IconButton
:icon="darkMode ? 'dark' : 'light'"
class="!text-white !dark:text-gray-500 navbar-icon"
:title="darkMode ? $t('color_scheme_dark') : $t('color_scheme_light')"
<NavbarIcon
:title="$t(darkMode ? 'color_scheme_dark' : 'color_scheme_light')"
class="navbar-icon navbar-clickable"
@click="darkMode = !darkMode"
/>
>
<i-ic-baseline-dark-mode v-if="darkMode" />
<i-ic-round-light-mode v-else />
</NavbarIcon>
<!-- Admin Settings -->
<IconButton
<NavbarIcon
v-if="user?.admin"
icon="settings"
class="!text-white !dark:text-gray-500 navbar-icon"
class="navbar-icon navbar-clickable"
:title="$t('admin.settings.settings')"
:to="{ name: 'admin-settings' }"
/>
<!-- Active Builds Indicator -->
<ActivePipelines v-if="user" />
>
<i-clarity-settings-solid />
</NavbarIcon>

<!-- Active Pipelines Indicator -->
<ActivePipelines v-if="user" class="navbar-icon navbar-clickable" />
<!-- User Avatar -->
<router-link v-if="user" :to="{ name: 'user' }" class="rounded-full overflow-hidden">
<img v-if="user && user.avatar_url" class="navbar-icon" :src="`${user.avatar_url}`" />
</router-link>
<NavbarIcon
v-if="user"
:to="{ name: 'user' }"
:title="$t('user.settings')"
class="navbar-icon navbar-clickable !p-1.5"
>
<img v-if="user && user.avatar_url" class="rounded-full" :src="`${user.avatar_url}`" />
</NavbarIcon>
<!-- Login Button -->
<Button v-else :text="$t('login')" @click="doLogin" />
</div>
Expand All @@ -50,17 +59,17 @@ import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';

import Button from '~/components/atomic/Button.vue';
import IconButton from '~/components/atomic/IconButton.vue';
import useAuthentication from '~/compositions/useAuthentication';
import useConfig from '~/compositions/useConfig';
import { useDarkMode } from '~/compositions/useDarkMode';

import ActivePipelines from './ActivePipelines.vue';
import NavbarIcon from './NavbarIcon.vue';

export default defineComponent({
name: 'Navbar',

components: { Button, ActivePipelines, IconButton },
components: { Button, ActivePipelines, NavbarIcon },

setup() {
const config = useConfig();
Expand All @@ -82,10 +91,10 @@ export default defineComponent({

<style scoped>
.navbar-link {
@apply hover:bg-black hover:bg-opacity-10 transition-colors duration-100 px-3 py-2 -my-1 rounded-md;
@apply px-3 py-2 -my-1 rounded-md;
}

.navbar-icon {
@apply w-8 h-8;
.navbar-clickable {
@apply hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-5 transition-colors duration-100;
}
</style>
55 changes: 55 additions & 0 deletions web/src/components/layout/header/NavbarIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<button type="button" :title="title" :aria-label="title" class="navbar-icon" @click="doClick">
<slot />
</button>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { RouteLocationRaw, useRouter } from 'vue-router';

export default defineComponent({
name: 'NavbarIcon',

props: {
to: {
type: [String, Object, null] as PropType<RouteLocationRaw | null>,
default: null,
},

title: {
type: String,
required: true,
},
},

setup(props) {
const router = useRouter();

async function doClick() {
if (!props.to) {
return;
}

if (typeof props.to === 'string' && props.to.startsWith('http')) {
window.location.href = props.to;
return;
}

await router.push(props.to);
}

return { doClick };
},
});
</script>

<style scoped>
.navbar-icon {
@apply w-11 h-11 rounded-full p-2.5;
}

.navbar-icon :deep(svg) {
@apply w-full h-full;
}
</style>