refactor(ui): additional size props for button, input and link

This commit is contained in:
upsiflu 2025-01-06 23:26:55 +01:00
parent 952d81bfaa
commit 72649a48e3
7 changed files with 110 additions and 59 deletions

View File

@ -38,11 +38,6 @@ const fontWeight = props.thinFont ? 400 : 900
const button = ref() const button = ref()
const attributes = computed(() =>
color(props, ['interactive'])(
width(props, [isIconOnly.value ? 'minContent' : 'buttonWidth'])(
align(props, { alignSelf:'start', alignText:'center' })()
)))
const click = async (...args: any[]) => { const click = async (...args: any[]) => {
internalLoader.value = true internalLoader.value = true
@ -60,17 +55,18 @@ onMounted(() => {
<template> <template>
<button ref="button" <button ref="button"
v-bind="attributes" v-bind="color(props, ['interactive'])(
width(props, isIconOnly ? ['square'] : ['normalHeight', 'buttonWidth'])(
align(props, { alignSelf:'start', alignText:'center' })(
)))"
class="funkwhale button" class="funkwhale button"
:aria-pressed="props.ariaPressed" :aria-pressed="props.ariaPressed"
:class="{ :class="{
'is-active': isActive,
'is-loading': isLoading, 'is-loading': isLoading,
'is-icon-only': isIconOnly, 'is-icon-only': isIconOnly,
'has-icon': !!icon, 'has-icon': !!icon,
'is-round': round, 'is-round': round,
'is-shadow': shadow, 'is-shadow': shadow,
'is-low-height': lowHeight
}" }"
@click="click" @click="click"
> >
@ -97,23 +93,20 @@ onMounted(() => {
display: inline-flex; display: inline-flex;
white-space: nowrap; white-space: nowrap;
justify-content: space-between; justify-content: space-between;
align-items: center;
padding: calc(var(--padding) - var(--shift-by)) var(--padding) calc(var(--padding) + var(--shift-by)) var(--padding); padding: calc(var(--padding) / 2 - var(--shift-by)) var(--padding) calc(var(--padding) / 2 + var(--shift-by)) var(--padding);
&.is-icon-only { &.is-icon-only {
padding: var(--padding); padding: var(--padding);
} }
.is-low-height {
--padding: 12px;
}
// Font // Font
font-family: $font-main; font-family: $font-main;
font-weight: v-bind(fontWeight); font-weight: v-bind(fontWeight);
font-size: 14px; font-size: 14px;
line-height: 1em; line-height: 14px;
// Decoration // Decoration
@ -144,6 +137,13 @@ onMounted(() => {
} }
} }
// Content
> span {
position: relative;
top: calc(0px - var(--shift-by));
}
// Icon // Icon
i.bi { i.bi {
@ -160,7 +160,7 @@ onMounted(() => {
} }
&.is-icon-only i.bi { &.is-icon-only i.bi {
margin: -2px; margin: -6px;
&.large { &.large {
margin: -8px; margin: -8px;
} }

View File

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import type { RouterLinkProps } from 'vue-router';
import { type ColorProps, type DefaultProps, type PastelProps, type RaisedProps, type VariantProps, color } from '~/composables/color' import { type ColorProps, type DefaultProps, type PastelProps, type RaisedProps, type VariantProps, color } from '~/composables/color'
import { type WidthProps, width } from '~/composables/width' import { type WidthProps, width } from '~/composables/width'
@ -78,16 +77,17 @@ const attributes = computed(() => ({
grid-template-columns: grid-template-columns:
repeat(auto-fit, v-bind(columnWidth)); repeat(auto-fit, v-bind(columnWidth));
grid-auto-flow: row dense; grid-auto-flow: row dense;
place-content: center;
/* If the grid has a fixed size smaller than its container, center it */ /* If the grid has a fixed size smaller than its container, center it */
place-content: center;
align-items: baseline;
} }
&[layout=grid-custom] { &[layout=grid-custom] {
display: grid; display: grid;
grid: v-bind("props.grid"); grid: v-bind("props.grid");
grid-auto-flow: row dense; grid-auto-flow: row dense;
place-content: center;
/* If the grid has a fixed size smaller than its container, center it */ /* If the grid has a fixed size smaller than its container, center it */
place-content: center;
} }
&[layout=stack] { &[layout=stack] {

View File

@ -40,7 +40,7 @@ onMounted(() => {
<template> <template>
<component :is="isExternalLink ? 'a' : 'RouterLink'" <component :is="isExternalLink ? 'a' : 'RouterLink'"
v-bind="color(props, ['interactive'])( v-bind="color(props, ['interactive'])(
width(props)( width(props, isNoColors(props) ? [] : ['normalHeight', 'auto'])(
align(props)( align(props)(
)))" )))"
ref="button" ref="button"
@ -75,14 +75,12 @@ onMounted(() => {
position: relative; position: relative;
display: inline-flex; display: inline-flex;
white-space: nowrap; white-space: nowrap;
align-items: center;
padding: calc(var(--padding) - var(--shift-by)) var(--padding) calc(var(--padding) + var(--shift-by)) var(--padding); padding: calc(var(--padding) / 2 - var(--shift-by)) var(--padding) calc(var(--padding) / 2 + var(--shift-by)) var(--padding);
&.is-icon-only { &.is-icon-only {
padding: var(--padding); padding: var(--padding);
} }
.is-low-height {
--padding: 12px;
}
&.no-spacing{ &.no-spacing{
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -100,7 +98,8 @@ onMounted(() => {
// Content // Content
> span { > span {
line-height: initial; position: relative;
top: calc(0px - var(--shift-by));
} }
// Decoration // Decoration

View File

@ -11,9 +11,13 @@ export type WidthProps =
| { auto?: true } | { auto?: true }
| { full?: true } | { full?: true }
| { width?: string } | { width?: string }
| { square?: true }
| { squareSmall?: true }
| { lowHeight?: true }
| { normalHeight?: true }
export type Key = KeysOfUnion<WidthProps> export type Key = KeysOfUnion<WidthProps>
const styles = { const widths = {
minContent: 'width: min-content;', minContent: 'width: min-content;',
iconWidth: 'width: 40px;', iconWidth: 'width: 40px;',
tiny: "width: 124px; grid-column: span 2;", tiny: "width: 124px; grid-column: span 2;",
@ -23,6 +27,17 @@ const styles = {
auto: "width: auto;", auto: "width: auto;",
full: "width: auto; grid-column: 1 / -1; place-self: stretch; flex-grow: 1;", full: "width: auto; grid-column: 1 / -1; place-self: stretch; flex-grow: 1;",
width: (w: string) => `width: ${w}; flex-grow:0;`, width: (w: string) => `width: ${w}; flex-grow:0;`,
} as const
const sizes = {
squareSmall: 'height: 40px; width: 40px; padding: 4px; grid-column: span 1; justify-content: center;',
square: 'height: 48px; width: 48px; grid-column: span 1; justify-content: center;',
lowHeight: 'height: 40px;',
normalHeight: 'height: 48px;',
} as const
const styles = {
...widths, ...sizes
} as const satisfies Record<Key, string | ((w: string) => string)>; } as const satisfies Record<Key, string | ((w: string) => string)>;
const getStyle = (props: Partial<WidthProps>) => (key: Key): string => const getStyle = (props: Partial<WidthProps>) => (key: Key): string =>
@ -39,7 +54,8 @@ const getStyle = (props: Partial<WidthProps>) => (key: Key): string =>
// All keys are exclusive // All keys are exclusive
const conflicts: Set<Key>[] = [ const conflicts: Set<Key>[] = [
new Set(Object.keys(styles) as Key[]) new Set(Object.keys(widths) as Key[]),
new Set(Object.keys(sizes) as Key[]),
] ]
const merge = (rules: string[]) => (attributes: HTMLAttributes = {}) => const merge = (rules: string[]) => (attributes: HTMLAttributes = {}) =>

View File

@ -36,7 +36,7 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
<template> <template>
<Layout aside default raised solid gap-12 :class="[$style.sidebar, $style['sticky-content']]"> <Layout aside default raised solid gap-12 :class="[$style.sidebar, $style['sticky-content']]">
<Layout header flex no-gap style="justify-content:space-between; padding-right:8px;"> <Layout header flex no-gap style="justify-content:space-between; align-items:center; padding-right:8px;">
<Link <Link
:to="{name: logoUrl}" :to="{name: logoUrl}"
:class="$style['logo']" :class="$style['logo']"
@ -52,17 +52,16 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
v-if="store.state.auth.availablePermissions['settings'] || store.state.auth.availablePermissions['moderation']" v-if="store.state.auth.availablePermissions['settings'] || store.state.auth.availablePermissions['moderation']"
to="/manage/settings" to="/manage/settings"
round round
square-small
icon="bi-wrench" icon="bi-wrench"
ghost ghost
> />
</Link>
<Button <Button
v-if="store.state.auth.authenticated" v-if="store.state.auth.authenticated"
align-self="center"
round round
square-small
icon="bi-upload" icon="bi-upload"
icon-width
:class="[$style.button]" :class="[$style.button]"
ghost ghost
@click="store.commit('ui/toggleModal', 'upload')" @click="store.commit('ui/toggleModal', 'upload')"
@ -87,8 +86,8 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
<Button <Button
round round
ghost ghost
square-small
icon="bi-list large" icon="bi-list large"
align-self="center"
class="hide-on-desktop" class="hide-on-desktop"
:class="$style.menu" :class="$style.menu"
@click="isCollapsed=!isCollapsed"/> @click="isCollapsed=!isCollapsed"/>
@ -212,29 +211,6 @@ const logoUrl = computed(() => store.state.auth.authenticated ? 'library.index'
margin: 16px; margin: 16px;
} }
header {
& > nav {
a,
button.button {
height: 40px;
padding: 10px;
}
.menu {
/* TODO: Fix important */
color: red;
padding: 8px !important;
height: 40px;
width: 40px !important;
i {
font-size: 24px !important;
margin: -1px !important;
}
}
}
}
&.sticky-content { &.sticky-content {
max-height: 100dvh; max-height: 100dvh;
overflow: auto; overflow: auto;

View File

@ -45,12 +45,9 @@ const labels = computed(() => ({
<template> <template>
<Popover raised v-model:open="isOpen"> <Popover raised v-model:open="isOpen">
<Button <Button
min-content
@click="isOpen = !isOpen" @click="isOpen = !isOpen"
round round
default square-small
align-self="center"
raised
ghost ghost
:ariaPressed="isOpen ? true : undefined" :ariaPressed="isOpen ? true : undefined"
> >

View File

@ -1,6 +1,9 @@
<script setup> <script setup>
import Card from "~/components/ui/Card.vue" import Card from "~/components/ui/Card.vue"
import Button from "~/components/ui/Button.vue"
import Link from "~/components/ui/Link.vue"
import Layout from "~/components/ui/Layout.vue" import Layout from "~/components/ui/Layout.vue"
import Spacer from "~/components/ui/layout/Spacer.vue";
</script> </script>
# Using widths # Using widths
@ -30,6 +33,66 @@ import Layout from "~/components/ui/Layout.vue"
<Card full title='full' /> <Card full title='full' />
</Layout> </Layout>
## Small height and square aspect ratio
<Layout grid class="preview">
<div style="grid-column: 1 / 7; grid-row: span 2">
```vue-html
<Button outline icon="bi-star"/>
<Button outline icon="bi-star large"/>
<Button outline square-small icon="bi-star" />
<Button outline square-small icon="bi-star large" />
<Button primary square >b</Button>
<Button primary >c</Button>
<Button primary square-small >a</Button>
<Button primary low-height >e</Button>
```
</div>
<Button outline icon="bi-star"/>
<Button outline icon="bi-star large"/>
<Spacer />
<Button outline square-small icon="bi-star" />
<Button outline square-small icon="bi-star large" />
<Spacer />
<Button primary square >b</Button>
<Button primary >c</Button>
<Spacer />
<Button primary square-small >a</Button>
<Button primary low-height >e</Button>
</Layout>
<Layout grid class="preview">
<div style="grid-column: -1 / -6; grid-row: span 4">
```vue-html
<Link icon="bi-star" to="."/>
<Link square-small icon="bi-star" to="."/>
<Link square-small to=".">g</Link>
<Link square to=".">h</Link>
<Link to=".">i</Link>
<Link square-small to=".">j</Link>
<Link low-height to=".">k</Link>
<Link square low-height to=".">l</Link>
```
</div>
<Link icon="bi-star" to="."/>
<Link square-small icon="bi-star" to="."/>
<Link square-small to=".">g</Link>
<Link square to=".">h</Link>
<Link to=".">i</Link>
<Link square-small to=".">j</Link>
<Link low-height to=".">k</Link>
<Link square low-height to=".">l</Link>
</Layout>
## Widths in the grid ## Widths in the grid
::: details Default widths ::: details Default widths