All files / src/components/Skeleton/variants Grid.vue

97.36% Statements 37/38
96.15% Branches 25/26
100% Functions 14/14
100% Lines 29/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110                                29x   29x     118x 118x     29x 29x   29x   29x   29x     29x   29x 29x         29x 29x 29x     29x   29x 29x 2x   27x     29x 29x           29x 2x               27x               28x 167x 168x         173x                                          
<script setup lang="ts">
import { computed } from "vue";
import Skeleton from "../Skeleton.vue";
import type { SkeletonBaseProps } from "../types";
 
type GridOptions = {
  rows?: number;
  columns?: number;
  minItemWidth?: number | string;
  itemCount?: number;
  gap?: number | string;
  itemHeight?: number | string;
  itemRadius?: number | string;
  padding?: number | string;
};
 
const props = defineProps<SkeletonBaseProps>();
 
const options = computed(() => (props.options ?? {}) as GridOptions);
 
function toUnit(value?: number | string) {
  Iif (value == null) return undefined;
  return typeof value === "number" ? `${value}px` : value;
}
 
const rows = computed(() => Math.max(1, options.value.rows ?? 2));
const columns = computed(() => Math.max(1, options.value.columns ?? 3));
 
const gap = computed(() => toUnit(options.value.gap ?? "16px"));
 
const itemHeight = computed(() => toUnit(options.value.itemHeight ?? "140px"));
 
const itemRadius = computed(() => toUnit(options.value.itemRadius ?? "12px"));
 
 
const padding = computed(() => toUnit(options.value.padding ?? "0"));
 
const containerWidth = computed(() =>
  typeof props.width === "number"
    ? `${props.width}px`
    : props.width ?? "100%"
);
 
const forwardedProps = computed(() => {
  const { width, options, ...rest } = props;
  return rest;
});
 
const isAutoFit = computed(() => options.value.minItemWidth != null);
 
const totalItems = computed(() => {
  if (options.value.itemCount != null) {
    return Math.max(1, options.value.itemCount);
  }
  return rows.value * columns.value;
});
 
const gridStyle = computed(() => {
  const base = {
    width: containerWidth.value,
    gap: gap.value,
    padding: padding.value,
  };
 
  if (isAutoFit.value) {
    return {
      ...base,
      gridTemplateColumns: `repeat(auto-fit, minmax(${toUnit(
        options.value.minItemWidth!
      )}, 1fr))`,
    };
  }
 
  return {
    ...base,
    gridTemplateColumns: `repeat(${columns.value}, minmax(0, 1fr))`,
  };
});
</script>
 
<template>
  <div class="vslk-grid" :style="gridStyle">
    <div
      v-for="item in totalItems"
      :key="item"
      class="vslk-grid__item"
    >
      <Skeleton
        v-bind="forwardedProps"
        variant="block"
        width="100%"
        :height="itemHeight"
        :radius="itemRadius"
      />
    </div>
  </div>
</template>
 
<style scoped>
.vslk-grid {
  display: grid;
  box-sizing: border-box;
  min-width: 0;
}
 
.vslk-grid__item {
  min-width: 0;
  box-sizing: border-box;
}
</style>