105 lines
2.9 KiB
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' | '8' | '12' | '16' | '24' | '32' | '48' | '64' | '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 lang="scss">
|
|
.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>
|