feat(ui): implement link styles; use growing Spacers in Sidebar
This commit is contained in:
parent
302781d1fe
commit
1ca7f41ac2
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,12 +118,12 @@
|
||||||
background-color: var(--pressed-background-color, var(--active-background-color));
|
background-color: var(--pressed-background-color, var(--active-background-color));
|
||||||
border-color: var(--pressed-background-color, var(--active-background-color));
|
border-color: var(--pressed-background-color, var(--active-background-color));
|
||||||
}
|
}
|
||||||
&:hover{
|
&:hover {
|
||||||
color:var(--hover-color);
|
color:var(--hover-color);
|
||||||
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);
|
||||||
|
@ -142,13 +142,13 @@
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|
||||||
&.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-010);
|
--border-color:var(--fw-blue-400);
|
||||||
|
&> .primary {
|
||||||
|
--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 {
|
||||||
|
|
|
@ -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,16 +66,16 @@ const uploads = useUploadsStore()
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<div :class="$style.search">
|
<Layout no-gap stack :class="$style['button-list']">
|
||||||
<Input
|
<div :class="$style.search">
|
||||||
v-model="searchQuery"
|
<Input
|
||||||
type="search"
|
v-model="searchQuery"
|
||||||
icon="bi-search"
|
type="search"
|
||||||
:placeholder="t('components.audio.SearchBar.placeholder.search')"
|
icon="bi-search"
|
||||||
/>
|
:placeholder="t('components.audio.SearchBar.placeholder.search')"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
<nav :class="$style['button-list']">
|
<nav style="display:contents;">
|
||||||
<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%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue