feat(ui): implement link styles; use growing Spacers in Sidebar

This commit is contained in:
upsiflu 2024-12-17 01:37:37 +01:00
parent 302781d1fe
commit 1ca7f41ac2
5 changed files with 151 additions and 57 deletions

View File

@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { type RouterLinkProps, RouterLink } from 'vue-router' import { type RouterLinkProps, RouterLink } from 'vue-router'
import { type ColorProps, propsToColor } from '~/composables/colors' import { type ColorProps, type VariantProps, propsToColor } from '~/composables/colors';
const { to, icon, inline, ...otherProps } = defineProps<RouterLinkProps const { to, icon, inline, round, ...otherProps } = defineProps<RouterLinkProps
& ColorProps
& { & {
icon?: string; icon?: string;
round?: boolean
inline?: true inline?: true
}>() } & ColorProps & VariantProps>()
const isExternalLink = computed(() => { const isExternalLink = computed(() => {
return typeof to === 'string' && to.startsWith('http') return typeof to === 'string' && to.startsWith('http')
@ -17,8 +17,13 @@ const isExternalLink = computed(() => {
<template> <template>
<a v-if="isExternalLink" <a v-if="isExternalLink"
:v-bind="propsToColor(otherProps)" v-bind="propsToColor(otherProps)"
:class="[$style.link, $style.external, inline && $style.inline]" :class="[
'interactive',
$style.link,
$style.external,
round && $style['is-round']
]"
:href="to?.toString()" :href="to?.toString()"
target="_blank" target="_blank"
> >
@ -27,8 +32,12 @@ const isExternalLink = computed(() => {
</a> </a>
<RouterLink v-else <RouterLink v-else
:to="to" :to="to"
:v-bind="propsToColor(otherProps)" v-bind="propsToColor(otherProps)"
:class="[$style.link, inline && $style.inline]" :class="[
'interactive',
$style.link,
round && $style['is-round']
]"
> >
<i v-if="icon" :class="['bi', icon]" /> <i v-if="icon" :class="['bi', icon]" />
<slot /> <slot />
@ -38,13 +47,8 @@ const isExternalLink = computed(() => {
<style module lang="scss"> <style module lang="scss">
.active { outline: 3px solid red; } .active { outline: 3px solid red; }
.external { outline: 3px dotted blue; } .external { outline: 3px dotted blue; }
.inline { display:inline-flex; } .link {
.button { display: inline-flex;
background-color: var(--fw-bg-color);
color: var(--fw-text-color);
border: 1px solid var(--fw-bg-color);
display: flex;
align-items: center; align-items: center;
white-space: nowrap; white-space: nowrap;
@ -72,5 +76,9 @@ const isExternalLink = computed(() => {
background-color:transparent; background-color:transparent;
border-color:transparent; border-color:transparent;
} }
&.is-round {
border-radius: 100vh;
}
} }
</style> </style>

View File

@ -22,13 +22,13 @@
} }
&:not(.is-raw) { &:not(.is-raw) {
color: var(--fw-link-color); color: var(--link-color);
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
&:hover, &:hover,
&.is-hovered { &.is-hovered {
color: var(--fw-link-hover-color); color: var(--link-hover-color);
} }
} }
} }

View File

@ -123,7 +123,7 @@
background-color:var(--hover-background-color); background-color:var(--hover-background-color);
border-color: var(--hover-background-color); border-color: var(--hover-background-color);
} }
&:active{ &:is(:active, .active) {
color:var(--active-color); color:var(--active-color);
background-color:var(--active-background-color); background-color:var(--active-background-color);
border-color: var(--active-background-color); border-color: var(--active-background-color);
@ -143,12 +143,12 @@
&.interactive{ &.interactive{
&:hover { &:hover {
border: 1px solid var(--hover-background-color); border-color: var(--hover-background-color);
} }
&:active{ &:is(:active, .active) {
border: 1px solid var(--active-background-color); border-color: var(--active-background-color);
&.router-link-exact-active { &.router-link-exact-active {
border: 1px solid var(--exact-active-background-color); border-color: var(--exact-active-background-color);
} }
} }
&[disabled] { &[disabled] {
@ -164,12 +164,12 @@
&.interactive{ &.interactive{
&:hover{ &:hover{
border: 1px solid var(--hover-background-color); border-color: var(--hover-background-color);
} }
&:active{ &:is(:active, .active) {
border: 1px solid var(--active-background-color); border-color: var(--active-background-color);
&.router-link-exact-active { &.router-link-exact-active {
background: 1px solid var(--exact-active-background-color); background-color: var(--exact-active-background-color);
} }
} }
} }
@ -201,6 +201,9 @@
--background-color:var(--fw-beige-300); --background-color:var(--fw-beige-300);
--border-color:var(--fw-beige-400); --border-color:var(--fw-beige-400);
} }
--link-color:var(--fw-blue-400);
--link-hover-color:var(--fw-blue-500);
} }
.secondary, button { .secondary, button {
@ -229,12 +232,18 @@
--hover-background-color:var(--fw-gray-400); --hover-background-color:var(--fw-gray-400);
--active-background-color:var(--fw-gray-500); --active-background-color:var(--fw-gray-500);
} }
--link-color:var(--fw-blue-600);
--link-hover-color:var(--fw-blue-700);
} }
.primary { .primary {
--color: var(--fw-blue-010); --color: var(--fw-blue-010);
--background-color:var(--fw-blue-400); --background-color:var(--fw-blue-400);
--border-color:var(--fw-blue-400);
&> .primary {
--border-color:var(--fw-blue-010); --border-color:var(--fw-blue-010);
}
--hover-color: var(--fw-blue-010); --hover-color: var(--fw-blue-010);
--hover-background-color:var(--fw-blue-500); --hover-background-color:var(--fw-blue-500);
@ -254,6 +263,11 @@
--hover-background-color:var(--fw-blue-600); --hover-background-color:var(--fw-blue-600);
--active-background-color:var(--fw-blue-700); --active-background-color:var(--fw-blue-700);
} }
&:not(:is(.ghost, .outline)) {
--link-color:var(--fw-blue-010);
--link-hover-color:white;
}
} }
.destructive { .destructive {
@ -272,6 +286,11 @@
--disabled-color:var(--fw-gray-500); --disabled-color:var(--fw-gray-500);
--disabled-background-color:var(--fw-gray-100); --disabled-background-color:var(--fw-gray-100);
--disabled-border-color:var(--fw-gray-100); --disabled-border-color:var(--fw-gray-100);
&:not(:is(.ghost, .outline)) {
--link-color:var(--fw-blue-010);
--link-hover-color:white;
}
} }
.blue { .blue {
@ -285,6 +304,9 @@
--disabled-border-color: var(--fw-gray-400); --disabled-border-color: var(--fw-gray-400);
--disabled-background-color: transparent; --disabled-background-color: transparent;
} }
--link-color:var(--fw-blue-010);
--link-hover-color:white;
} }
.red { .red {
@ -298,6 +320,9 @@
--disabled-border-color: var(--fw-gray-400); --disabled-border-color: var(--fw-gray-400);
--disabled-background-color: transparent; --disabled-background-color: transparent;
} }
--link-color:var(--fw-blue-010);
--link-hover-color:white;
} }
.purple { .purple {
@ -311,6 +336,9 @@
--disabled-border-color: var(--fw-gray-400); --disabled-border-color: var(--fw-gray-400);
--disabled-background-color: transparent; --disabled-background-color: transparent;
} }
--link-color:var(--fw-blue-010);
--link-hover-color:white;
} }
.green { .green {
@ -324,6 +352,9 @@
--disabled-border-color: var(--fw-gray-400); --disabled-border-color: var(--fw-gray-400);
--disabled-background-color: transparent; --disabled-background-color: transparent;
} }
--link-color:var(--fw-blue-010);
--link-hover-color:white;
} }
.yellow { .yellow {
@ -337,6 +368,9 @@
--disabled-border-color: var(--fw-gray-400); --disabled-border-color: var(--fw-gray-400);
--disabled-background-color: transparent; --disabled-background-color: transparent;
} }
--link-color:var(--fw-blue-010);
--link-hover-color:white;
} }
} }
@ -365,6 +399,9 @@
--background-color:var(--fw-gray-900); --background-color:var(--fw-gray-900);
--border-color:var(--fw-gray-600); --border-color:var(--fw-gray-600);
} }
--link-color:var(--fw-gray-300);
--link-hover-color:var(--fw-gray-400)
} }
.secondary, button { .secondary, button {
@ -396,6 +433,9 @@
--active-color:var(--fw-gray-400); --active-color:var(--fw-gray-400);
--active-background-color:var(--fw-gray-900); --active-background-color:var(--fw-gray-900);
} }
--link-color:var(--fw-gray-500);
--link-hover-color:var(--fw-gray-600)
} }
.primary { .primary {

View File

@ -10,6 +10,8 @@ import Link from '~/components/ui/Link.vue'
import ActorAvatar from '~/components/common/ActorAvatar.vue' import ActorAvatar from '~/components/common/ActorAvatar.vue'
import UserMenu from './UserMenu.vue' import UserMenu from './UserMenu.vue'
import Button from '~/components/ui/Button.vue' import Button from '~/components/ui/Button.vue'
import Layout from '~/components/ui/Layout.vue'
import Spacer from '~/components/ui/layout/Spacer.vue'
const searchQuery = ref('') const searchQuery = ref('')
@ -25,7 +27,7 @@ const uploads = useUploadsStore()
</script> </script>
<template> <template>
<aside :class="[$style.sidebar, $style['sticky-content']]"> <aside :class="[$style.sidebar, $style['sticky-content']]" v-bind="color('default solid raised')">
<header :class="$style['header-wrapper']"> <header :class="$style['header-wrapper']">
<Link to="/" :class="$style['logo']"> <Link to="/" :class="$style['logo']">
<img <img
@ -64,6 +66,7 @@ const uploads = useUploadsStore()
</nav> </nav>
</header> </header>
<Layout no-gap stack :class="$style['button-list']">
<div :class="$style.search"> <div :class="$style.search">
<Input <Input
v-model="searchQuery" v-model="searchQuery"
@ -72,8 +75,7 @@ const uploads = useUploadsStore()
:placeholder="t('components.audio.SearchBar.placeholder.search')" :placeholder="t('components.audio.SearchBar.placeholder.search')"
/> />
</div> </div>
<nav style="display:contents;">
<nav :class="$style['button-list']">
<Link to="/library" <Link to="/library"
ghost ghost
secondary secondary
@ -112,7 +114,7 @@ const uploads = useUploadsStore()
> >
{{ t('components.Sidebar.link.radios') }} {{ t('components.Sidebar.link.radios') }}
</Link> </Link>
<Link to="/library/podcasts"> <Link to="/library/podcasts"
ghost ghost
secondary secondary
icon="bi-mic" icon="bi-mic"
@ -127,8 +129,10 @@ const uploads = useUploadsStore()
{{ t('components.Sidebar.link.favorites') }} {{ t('components.Sidebar.link.favorites') }}
</Link> </Link>
</nav> </nav>
<Spacer grow />
<h3>{{ t('components.Sidebar.link.channels') }}</h3> <h3>{{ t('components.Sidebar.link.channels') }}</h3>
<nav :class="$style['button-list']"> <Spacer grow />
<nav>
<Link inline to="/about"> <Link inline to="/about">
{{ t('components.Sidebar.link.about') }} {{ t('components.Sidebar.link.about') }}
</Link> </Link>
@ -139,6 +143,7 @@ const uploads = useUploadsStore()
Legal Legal
</Link> </Link>
</nav> </nav>
</Layout>
</aside> </aside>
</template> </template>
@ -153,7 +158,6 @@ const uploads = useUploadsStore()
height: 100%; height: 100%;
display:flex; display:flex;
flex-direction:column; flex-direction:column;
background-color: var(--fw-gray-900);
&.sticky-content { &.sticky-content {
position: sticky; position: sticky;
@ -260,24 +264,11 @@ const uploads = useUploadsStore()
> h3 { > h3 {
margin: 0; margin: 0;
padding: 0 32px 8px; padding: 0 32px 8px;
color: var(--fw-gray-700);
@include light-theme {
color: var(--fw-gray-700);
}
@include dark-theme {
color: var(--fw-blue-100);
}
font-size: 14px; font-size: 14px;
line-height: 1.2; line-height: 1.2;
} }
nav.button-list { .button-list {
padding: 0 16px 32px; padding: 0 16px 32px;
button {
margin: 2px 0;
justify-content: start;
width: 100%;
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import Link from '~/components/ui/Link.vue' import Link from '~/components/ui/Link.vue'
import Button from '~/components/ui/Button.vue'
</script> </script>
# Link # Link
@ -21,3 +22,57 @@ This component only adds some styles to a `<RouterLink>`.
</Link> </Link>
Instead of a route, you can set the prop `to` to any web address starting with `http`. Instead of a route, you can set the prop `to` to any web address starting with `http`.
## Colors and Variants
###### Solid:
<Link primary solid to="/">
Home
</Link>
<Link secondary solid to="/">
Home
</Link>
<Link destructive solid to="/">
Home
</Link>
###### Outline:
<Link primary outline to="/">
Home
</Link>
<Link secondary outline to="/">
Home
</Link>
<Link destructive outline to="/">
Home
</Link>
###### Ghost:
<Link primary ghost to="/">
Home
</Link>
<Link secondary ghost to="/">
Home
</Link>
<Link destructive ghost to="/">
Home
</Link>
<Button>
I'm a button
</Button>
## Shapes
<Link primary solid round to="/">
Home
</Link>