Merge pull request #378 from alisaweb3/v3-single

UI refactor: Default Layout, Sidebar,Chain Profile
This commit is contained in:
ping 2023-05-12 08:22:54 +08:00 committed by GitHub
commit 69ef2b1da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 313 additions and 164 deletions

View File

@ -27,7 +27,7 @@ const addFavor = (e: Event) => {
<template>
<RouterLink
:to="`/${name}`"
class="bg-base-100 hover:bg-base-content rounded shadow flex items-center px-3 py-3 cursor-pointer"
class="bg-base-100 hover:bg-gray-100 dark:hover:bg-[#373f59] rounded shadow flex items-center px-3 py-3 cursor-pointer"
>
<div class="w-8 h-8 rounded-full overflow-hidden">
<img :src="conf.logo" />

View File

@ -11,62 +11,90 @@ chainStore.$subscribe((m, s) => {
</script>
<template>
<VListItem class="m-0 p-0">
<template #prepend>
<VBadge
dot
location="bottom right"
offset-x="3"
offset-y="3"
bordered
color="success"
class="mr-2"
<div class="flex items-center">
<div class="dropdown">
<div tabindex="0" class="p-1 relative mr-3 cursor-pointer">
<img v-lazy="chainStore.logo" class="w-9 h-9 rounded-full" />
<div
class="w-2 h-2 rounded-full bg-yes absolute right-0 bottom-0"
></div>
</div>
<div
class="dropdown-content menu shadow bg-base-100 rounded-box max-h-[300px] overflow-auto"
>
<VAvatar class="cursor-pointer" color="primary" variant="tonal">
<VImg :src="chainStore.logo" />
<div>
<!-- rest -->
<div
class="px-4 py-2 text-sm text-gray-400"
v-if="chainStore.current?.endpoints?.rest"
>
Rest Endpoint
</div>
<div tabindex="0">
<div
v-for="(item, index) in chainStore.current?.endpoints?.rest"
@click="chainStore.setRestEndpoint(item)"
class="px-4 py-2 hover:bg-gray-100 dark:bg-[#384059] cursor-pointer"
:key="index"
>
<div class="flex flex-col items-start">
<div class="flex items-center justify-between w-full">
<div class="text-gray-500 dark:text-gray-200 capitalize">
{{ item.provider }}
</div>
<!-- SECTION Menu -->
<VMenu activator="parent" location="bottom start" offset="14px">
<VList>
<!-- 👉 Rest -->
<VListSubheader
v-if="chainStore.current?.endpoints?.rest"
title="Rest Endpoint"
/>
<VListItem
v-for="i in chainStore.current?.endpoints?.rest"
link
@click="chainStore.setRestEndpoint(i)"
>
<VListItemTitle
>{{ i.provider }}
<VIcon
v-if="i.address === chainStore.endpoint?.address"
icon="mdi-check"
color="success"
/></VListItemTitle>
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
</VListItem>
<span
v-if="item.address === chainStore.endpoint?.address"
class="bg-yes inline-block h-2 w-2 rounded-full"
/>
</div>
<div class="text-gray-400 text-xs whitespace-nowrap">
{{ item.address }}
</div>
</div>
</div>
</div>
<VListSubheader
v-if="chainStore.current?.endpoints?.grpc"
title="gRPC Endpoint"
/>
<VListItem v-for="i in chainStore.current?.endpoints?.grpc" link>
<VListItemTitle>{{ i.provider }}</VListItemTitle>
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
</VListItem>
</VList>
</VMenu>
<!-- !SECTION -->
</VAvatar>
</VBadge>
</template>
<VListItemTitle>{{
baseStore.latest.block?.header?.chain_id || chainStore.chainName || ''
}}</VListItemTitle>
<VListItemSubtitle>
{{ chainStore.connErr || chainStore.endpoint.address }}</VListItemSubtitle
>
</VListItem>
<!-- grpc -->
<div
class="px-4 py-2 text-sm text-gray-400"
v-if="chainStore.current?.endpoints?.grpc"
>
gRPC Endpoint
</div>
<ul tabindex="0">
<li
v-for="(item, index) in chainStore.current?.endpoints?.grpc"
:key="index"
>
<div class="flex flex-col items-start">
<div class="flex items-center justify-between w-full">
<p class="text-gray-500 dark:text-gray-200 capitalize">
{{ item?.provider }}
</p>
</div>
<div class="text-gray-400 text-xs whitespace-nowrap">
{{ item?.address }}
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="flex-1 w-0">
<div
class="capitalize whitespace-nowrap text-base font-semibold text-gray-600 dark:text-gray-200"
>
{{
baseStore.latest.block?.header?.chain_id || chainStore.chainName || ''
}}
</div>
<div
class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap hidden lg:block"
>
{{ chainStore.connErr || chainStore.endpoint.address }}
</div>
</div>
</div>
</template>

View File

@ -1,5 +1,6 @@
<script lang="ts" setup>
import { Icon } from '@iconify/vue';
import { ref } from 'vue';
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig';
// Components
@ -11,7 +12,6 @@ import UserProfile from '@/layouts/components/ChainProfile.vue';
import { useDashboard } from '@/stores/useDashboard';
// @layouts plugin
import { VerticalNavLayout } from '@layouts';
import NavBarI18n from './NavBarI18n.vue';
import NavSearchBar from './NavSearchBar.vue';
import NavBarNotifications from './NavBarNotifications.vue';
@ -20,23 +20,7 @@ import TheCustomizer from '@/plugins/vuetify/@core/components/TheCustomizer.vue'
import Breadcrumbs from './Breadcrumbs.vue';
import { useBlockchain } from '@/stores';
const {
appRouteTransition,
isLessThanOverlayNavBreakpoint,
isVerticalNavCollapsed,
} = useThemeConfig();
const { width: windowWidth } = useWindowSize();
// Provide animation name for vertical nav collapse icon.
const verticalNavHeaderActionAnimationName = ref<
null | 'rotate-180' | 'rotate-back-180'
>(null);
watch(isVerticalNavCollapsed, (val) => {
verticalNavHeaderActionAnimationName.value = val
? 'rotate-180'
: 'rotate-back-180';
});
const { appRouteTransition } = useThemeConfig();
const dashboard = useDashboard();
dashboard.initial();
@ -47,23 +31,148 @@ blockchain.$subscribe((m, s) => {
blockchain.initial();
}
});
const sidebarShow = ref(false);
</script>
<template>
<VerticalNavLayout :nav-items="blockchain.computedChainMenu">
<!-- 👉 navbar -->
<template #navbar="{ toggleVerticalOverlayNavActive }">
<div class="flex items-center py-3">
<div class="">
<!-- sidebar -->
<div
class="w-64 fixed z-50 left-0 top-0 bottom-0 overflow-auto bg-base-100 border-r border-gray-100 dark:border-gray-700"
:class="{ block: sidebarShow, 'hidden xl:block': !sidebarShow }"
>
<div class="flex items-center pl-4 py-4 mb-1">
<img class="w-10 h-10" src="../../assets/logo.svg" />
<h1 class="flex-1 ml-3 text-2xl font-semibold dark:text-white">Ping.pub</h1>
<div class="pr-4 cursor-pointer xl:hidden" @click="sidebarShow = false">
<Icon icon="mdi-close" class="text-3xl" />
</div>
</div>
<div v-for="(item, index) of blockchain.computedChainMenu" :key="index">
<div
v-if="item?.title && item?.children?.length"
class="collapse"
:class="{ 'collapse-arrow': item?.children?.length > 0 }"
>
<input type="checkbox" />
<div
class="collapse-title px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]"
>
<Icon
v-if="item?.icon?.icon"
:icon="item?.icon?.icon"
class="text-xl mr-2"
:class="{
'text-yellow-500': item?.title === 'Favorite',
'text-blue-500': item?.title !== 'Favorite',
}"
/>
<img
v-if="item?.icon?.image"
:src="item?.icon?.image"
class="w-6 h-6 rounded-full mr-3"
/>
<div
class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200"
>
{{ item?.title }}
</div>
<div
v-if="item?.badgeContent"
class="mr-6 badge badge-sm badge-primary"
>
{{ item?.badgeContent }}
</div>
</div>
<div class="collapse-content">
<div class="menu bg-base-100 w-full">
<RouterLink
v-for="(el, key) of item?.children"
@click="sidebarShow = false"
:key="key"
class="hover:bg-gray-100 dark:hover:bg-[#373f59] rounded cursor-pointer px-3 py-2 flex items-center"
:to="el?.to"
:class="{
'bg-primary':
$route.path === el?.to?.path && item?.title !== 'Favorite',
}"
>
<img
v-if="el?.icon?.image"
:src="el?.icon?.image"
class="w-6 h-6 rounded-full mr-3"
/>
<div
class="text-base text-gray-500 dark:text-gray-300"
:class="{
'text-white':
$route.path === el?.to?.path &&
item?.title !== 'Favorite',
}"
>
{{ $t(el?.title) }}
</div>
</RouterLink>
</div>
</div>
</div>
<RouterLink
:to="item?.to"
v-if="item?.title && !item?.children?.length"
@click="sidebarShow = false"
class="collapse-title px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]"
>
<Icon
v-if="item?.icon?.icon"
:icon="item?.icon?.icon"
class="text-xl mr-2"
:class="{
'text-yellow-500': item?.title === 'Favorite',
'text-blue-500': item?.title !== 'Favorite',
}"
/>
<img
v-if="item?.icon?.image"
:src="item?.icon?.image"
class="w-6 h-6 rounded-full mr-3"
/>
<div
class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200"
>
{{ item?.title }}
</div>
<div
v-if="item?.badgeContent"
class="mr-6 badge badge-sm badge-primary"
>
{{ item?.badgeContent }}
</div>
</RouterLink>
<div
v-if="item?.heading"
class="px-4 text-sm pt-4 text-gray-400 pb-2 uppercase"
>
{{ item?.heading }}
</div>
</div>
</div>
<div class="xl:ml-64 px-5">
<!-- header -->
<div
class="flex items-center py-3 bg-base-100 mb-4 rounded px-4 sticky top-0 z-10 mt-4 shadow"
>
<div
class="text-2xl pr-3 cursor-pointer xl:hidden"
@click="toggleVerticalOverlayNavActive(true)"
@click="sidebarShow = true"
>
<Icon icon="mdi-menu" />
</div>
<UserProfile />
<div class="flex-1"></div>
<div class="flex-1 w-0"></div>
<!-- <NavSearchBar />-->
<NavBarNotifications class="hidden md:inline-block" />
@ -71,56 +180,15 @@ blockchain.$subscribe((m, s) => {
<NavbarThemeSwitcher class="hidden md:inline-block" />
<NavBarWallet class="md:inline-block" />
</div>
</template>
<!-- 👉 Pages -->
<RouterView v-slot="{ Component }">
<Transition :name="appRouteTransition" mode="out-in">
<Component :is="Component" />
</Transition>
</RouterView>
<!-- 👉 Pages -->
<RouterView v-slot="{ Component }">
<Transition :name="appRouteTransition" mode="out-in">
<Component :is="Component" />
</Transition>
</RouterView>
<!-- 👉 Footer -->
<template #footer>
<!-- <Footer /> -->
<newFooter />
</template>
<!-- 👉 Customizer -->
<!-- <TheCustomizer /> -->
</VerticalNavLayout>
</div>
</div>
</template>
<style lang="scss">
@keyframes rotate-180 {
from {
transform: rotate(0deg);
}
to {
transform: rotate(180deg);
}
}
@keyframes rotate-back-180 {
from {
transform: rotate(180deg);
}
to {
transform: rotate(0deg);
}
}
.layout-vertical-nav {
.nav-header {
.header-action {
animation-duration: 0s;
animation-duration: 0.35s;
animation-fill-mode: forwards;
animation-name: v-bind(verticalNavHeaderActionAnimationName);
transform: rotate(0deg);
}
}
}
</style>

View File

@ -3,7 +3,12 @@ import MdEditor from 'md-editor-v3';
import PriceMarketChart from '@/components/charts/PriceMarketChart.vue';
import { Icon } from '@iconify/vue';
import { useBlockchain, useFormatter, useTxDialog, useWalletStore } from '@/stores';
import {
useBlockchain,
useFormatter,
useTxDialog,
useWalletStore,
} from '@/stores';
import { onMounted, ref } from 'vue';
import { useIndexModule } from './indexStore';
import { computed } from '@vue/reactivity';
@ -13,9 +18,9 @@ import ProposalListItem from '@/components/ProposalListItem.vue';
const blockchain = useBlockchain();
const store = useIndexModule();
const walletStore = useWalletStore()
const format = useFormatter()
const dialog = useTxDialog()
const walletStore = useWalletStore();
const format = useFormatter();
const dialog = useTxDialog();
const coinInfo = computed(() => {
return store.coinInfo;
@ -23,7 +28,7 @@ const coinInfo = computed(() => {
onMounted(() => {
store.loadDashboard();
walletStore.loadMyAsset()
walletStore.loadMyAsset();
});
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
@ -34,7 +39,7 @@ blockchain.$subscribe((m, s) => {
['chainName', 'endpoint'].includes(m.events.key)
) {
store.loadDashboard();
walletStore.loadMyAsset()
walletStore.loadMyAsset();
}
});
function shortName(name: string, id: string) {
@ -69,19 +74,19 @@ const comLinks = [
// wallet box
const change = computed(() => {
const token = walletStore.balanceOfStakingToken
return token? format.priceChanges(token.denom) : 0
})
const color= computed(() => {
switch(true) {
case change.value > 0:
return "text-green-600"
const token = walletStore.balanceOfStakingToken;
return token ? format.priceChanges(token.denom) : 0;
});
const color = computed(() => {
switch (true) {
case change.value > 0:
return 'text-green-600';
case change.value === 0:
return "text-grey-500"
return 'text-grey-500';
case change.value < 0:
return "text-red-600"
return 'text-red-600';
}
})
});
</script>
<template>
@ -236,18 +241,59 @@ const color= computed(() => {
</div>
</div>
<div class="bg-transparent rounded mt-4 border-2 border-primary">
<div class="bg-base-100 rounded mt-4 shadow">
<div class="px-4 pt-4 pb-2 text-lg font-semibold text-secondary">
{{ walletStore.currentAddress || "Not Connected" }}
<span v-if="walletStore.currentAddress" class="float-right font-light text-sm">More</span>
{{ walletStore.currentAddress || 'Not Connected' }}
<span
v-if="walletStore.currentAddress"
class="float-right font-light text-sm"
>More</span
>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 auto-cols-auto gap-4 px-4 pb-8 py-4">
<div class="bg-base-100">{{ format.formatToken(walletStore.balanceOfStakingToken) }}
<br><span :class="color">{{ format.showChanges(change) }}<small>%</small></span>{{ format.tokenValue(walletStore.balanceOfStakingToken) }}</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.stakingAmount) }}
<br> {{ format.tokenValue(walletStore.stakingAmount) }}</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.rewardAmount) }}</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.unbondingAmount) }}</div>
<div
class="grid grid-cols-1 md:grid-cols-4 auto-cols-auto gap-4 px-4 pb-6"
>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
<div class="text-sm mb-1">Balance</div>
<div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.balanceOfStakingToken) }}
</div>
<div class="text-sm">
<span :class="color"
>{{ format.showChanges(change) }}<small>%</small>
</span>
<span class="ml-1">{{
format.tokenValue(walletStore.balanceOfStakingToken)
}}</span>
</div>
</div>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
<div class="text-sm mb-1">Staking</div>
<div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.stakingAmount) }}
</div>
<div class="text-sm">
{{ format.tokenValue(walletStore.stakingAmount) }}
</div>
</div>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
<div class="text-sm mb-1">Reward</div>
<div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.rewardAmount) }}
</div>
<div class="text-sm">
{{ format.tokenValue(walletStore.rewardAmount) }}
</div>
</div>
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
<div class="text-sm mb-1">Unbonding</div>
<div class="text-lg font-semibold text-main">
{{ format.formatToken(walletStore.unbondingAmount) }}
</div>
<div class="text-sm">
{{ format.tokenValue(walletStore.unbondingAmount) }}
</div>
</div>
</div>
<div>
@ -256,12 +302,8 @@ const color= computed(() => {
</div>
</div>
<div>
</div>
<div></div>
</div>
</div>
</template>

View File

@ -45,7 +45,7 @@ function changeLang(lang: string){
>
<li v-for="lang in props.languages" :key="lang.i18nLang">
<a
class="hover:bg-base-content"
class="hover:bg-gray-100 dark:hover:bg-[#373f59]"
:class="{ 'text-primary': currentLang === lang.i18nLang }"
@click="changeLang(lang.i18nLang)"
>{{ lang.label }}</a

View File

@ -15,3 +15,17 @@
--bg-active: #242b40;
}
}
.collapse-title,
:where(.collapse > input[type='checkbox']) {
padding: 0;
padding-right: 0;
min-height: unset;
}
.collapse-open :where(.collapse-content),
.collapse:focus:not(.collapse-close) :where(.collapse-content),
.collapse:not(.collapse-close)
:where(input[type='checkbox']:checked ~ .collapse-content) {
padding-bottom: 0;
}

View File

@ -8,7 +8,6 @@ module.exports = {
yes: '#3fb68b',
no: '#ff5353',
info: '#00b2ff',
primary: '#666cff',
main: 'var(--text-main)',
secondary: 'var(--text-secondary)',
active: 'var(--bg-active)',
@ -22,7 +21,6 @@ module.exports = {
light: {
...require('daisyui/src/colors/themes')['[data-theme=light]'],
primary: '#666cff',
'base-content': '#e9eaeb',
},
},
{
@ -30,7 +28,6 @@ module.exports = {
...require('daisyui/src/colors/themes')['[data-theme=dark]'],
primary: '#666cff',
'base-100': '#2a334c',
'base-content': '#373f57',
},
},
],