134 lines
3.0 KiB
Vue
134 lines
3.0 KiB
Vue
<template>
|
|
<div
|
|
class="cw-description-grid"
|
|
:class="{
|
|
bordered,
|
|
'mobile-single': singleColumnOnMobile
|
|
}"
|
|
:style="gridStyle"
|
|
>
|
|
<template v-for="item in items" :key="item.key">
|
|
<div class="cell label" :class="item.labelClass">
|
|
<slot :name="`label-${item.key}`" :item="item">
|
|
<span v-text="item.label" />
|
|
</slot>
|
|
</div>
|
|
<div class="cell value" :class="item.valueClass">
|
|
<slot :name="`value-${item.key}`" :item="item">
|
|
<span v-text="formatValue(item.value)" />
|
|
</slot>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed } from "vue";
|
|
import type { CSSProperties, PropType } from "vue";
|
|
import type { DescriptionGridAlign, DescriptionGridItem } from "./type";
|
|
|
|
defineOptions({
|
|
name: "DescriptionGrid"
|
|
});
|
|
|
|
const props = defineProps({
|
|
items: {
|
|
type: Array as PropType<DescriptionGridItem[]>,
|
|
default: () => []
|
|
},
|
|
columns: {
|
|
type: Number,
|
|
default: 2
|
|
},
|
|
emptyText: {
|
|
type: String,
|
|
default: "-"
|
|
},
|
|
bordered: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
singleColumnOnMobile: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
labelAlign: {
|
|
type: String as PropType<DescriptionGridAlign>,
|
|
default: "left"
|
|
},
|
|
valueAlign: {
|
|
type: String as PropType<DescriptionGridAlign>,
|
|
default: "left"
|
|
}
|
|
});
|
|
|
|
const normalizedColumns = computed(() => {
|
|
if (!props.columns || props.columns < 1) {
|
|
return 1;
|
|
}
|
|
return Math.floor(props.columns);
|
|
});
|
|
|
|
const gridStyle = computed<CSSProperties>(() => {
|
|
return {
|
|
"--cw-description-columns": String(normalizedColumns.value),
|
|
"--cw-description-grid-template": Array
|
|
.from({ length: normalizedColumns.value }, () => "max-content minmax(0, 1fr)")
|
|
.join(" "),
|
|
"--cw-description-label-align": props.labelAlign,
|
|
"--cw-description-value-align": props.valueAlign
|
|
} as CSSProperties;
|
|
});
|
|
|
|
function formatValue(value: DescriptionGridItem["value"]): string {
|
|
if (value === null || value === undefined || value === "") {
|
|
return props.emptyText;
|
|
}
|
|
return String(value);
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.cw-description-grid {
|
|
display: grid;
|
|
max-width: 100%;
|
|
grid-template-columns: var(--cw-description-grid-template);
|
|
|
|
.cell {
|
|
min-width: 0;
|
|
padding: 12px 16px;
|
|
}
|
|
|
|
.label {
|
|
color: var(--t-color-text-placeholder, var(--td-text-color-placeholder));
|
|
text-align: var(--cw-description-label-align);
|
|
white-space: nowrap;
|
|
background: var(--t-color-bg-container-secondary, var(--td-bg-color-secondarycontainer));
|
|
}
|
|
|
|
.value {
|
|
text-align: var(--cw-description-value-align);
|
|
word-break: break-all;
|
|
background: var(--t-color-bg-container, var(--td-bg-color-container));
|
|
}
|
|
|
|
&.bordered {
|
|
border-top: 1px solid var(--t-color-border-1, var(--td-component-border));
|
|
border-left: 1px solid var(--t-color-border-1, var(--td-component-border));
|
|
|
|
.cell {
|
|
border-right: 1px solid var(--t-color-border-1, var(--td-component-border));
|
|
border-bottom: 1px solid var(--t-color-border-1, var(--td-component-border));
|
|
}
|
|
}
|
|
|
|
@media (max-width: 48rem) {
|
|
&.mobile-single {
|
|
grid-template-columns: max-content minmax(0, max-content);
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|
|
|