funkwhale/front/src/components/ui/Layout.vue

105 lines
2.9 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue';
import { type ColorProps, type DefaultProps, type PastelProps, type RaisedProps, type VariantProps, color } from '~/composables/color'
import { type WidthProps, width } from '~/composables/width'
const props = defineProps<{
columnWidth?: string,
noRule?: true,
noWrap?: true,
} & { [P in "stack" | "grid" | "flex" | "columns" | "row" | "page"]?: true | string }
& { [C in "nav" | "aside" | "header" | "footer" | "main" | "label" | "form" | "h1" | "h2" | "h3" | "h4" | "h5"]?: true }
& { [G in 'no-gap' | 'gap-4' | 'gap-8' | 'gap-12' | 'gap-16' | 'gap-64' | 'gap-auto' ]?: true }
& (PastelProps | ColorProps | DefaultProps)
& RaisedProps
& VariantProps
& WidthProps>()
const columnWidth = props.columnWidth ?? '46px'
const maybeGap = Object.entries(props).find(
([key, value]) => value===true && key.startsWith('gap'))
const gapWidth = maybeGap ? `${maybeGap[0].replace('gap', '')}px` : '32px'
const attributes = computed(() => ({
...color(props)(width(props)()),
layout:
props.grid === true ? 'grid' :
typeof props.grid === 'string' ? 'grid-custom' :
props.flex === true ? 'flex' :
props.columns=== true ? 'columns' :
'stack'
}))
</script>
<template>
<component
:is="props.nav ? 'nav' : props.aside ? 'aside' : props.header ? 'header' : props.footer ? 'footer' : props.main ? 'main' : props.label ? 'label' : props.form ? 'form' : props.h1 ? 'h1' : props.h2 ? 'h2' : props.h3 ? 'h3' : props.h4 ? 'h4' : props.h5 ? 'h5' : 'div'"
:class="[
$style.layout,
('noGap' in props && props.noGap === true) || $style.gap,
noWrap || $style.wrap,
]" v-bind="attributes">
<slot />
</component>
</template>
<style module>
.layout {
transition: all .15s;
/* Override --gap with your preferred value */
gap: var(--gap, v-bind(gapWidth));
&:not(.gap) {
gap: 0;
}
/* Growth */
&:has(:global(>.grow)) {
>:not(:global(.grow)) {
flex-grow: 0;
}
}
/* Layout strategy */
&[layout=columns] {
column-count: auto;
column-width: v-bind(columnWidth);
display: block;
column-rule: 1px solid v-bind("noRule ? 'transparent' : 'var(--border-color)'");
}
&[layout=grid] {
display: grid;
grid-template-columns:
repeat(auto-fit, v-bind(columnWidth));
grid-auto-flow: row dense;
/* If the grid has a fixed size smaller than its container, center it */
place-content: center;
align-items: baseline;
}
&[layout=grid-custom] {
display: grid;
grid: v-bind("props.grid");
grid-auto-flow: row dense;
/* If the grid has a fixed size smaller than its container, center it */
place-content: center;
}
&[layout=stack] {
display: flex;
flex-direction: column;
}
&[layout=flex] {
display: flex;
flex-direction: row;
flex-wrap: v-bind('props.noWrap ? "nowrap" : "wrap"');
}
}
</style>