Skip to content

Commit 55e7b7a

Browse files
The-x-35Arpit Singhanwarulislam
authored
Solving issue #18 feat: add skeleton component (#26)
* Added Skeleton component * feat: added render order * chore: added gap --------- Co-authored-by: Arpit Singh <[email protected]> Co-authored-by: Anwarul Islam <[email protected]>
1 parent e3bab05 commit 55e7b7a

File tree

4 files changed

+155
-1
lines changed

4 files changed

+155
-1
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,6 @@
118118
"types": "./dist/src/helpers/index.d.ts"
119119
}
120120
},
121-
"types": "./dist/index.d.ts"
121+
"types": "./dist/index.d.ts",
122+
"packageManager": "[email protected]+sha256.dbdf5961c32909fb030595a9daa1dae720162e658609a8f92f2fa99835510ca5"
122123
}

src/components/smart/Skeleton.vue

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<template>
2+
<div
3+
:style="{ backgroundColor: bgColor }"
4+
class="flex flex-col gap-y-4 rounded-md p-4"
5+
>
6+
<div
7+
v-if="showImage"
8+
:style="{
9+
backgroundColor: skeletonColor,
10+
width: imageWidth,
11+
height: imageHeight,
12+
order: orderMap.image,
13+
}"
14+
class="relative rounded-md"
15+
>
16+
<!-- Mountain triangle design for image placeholder -->
17+
<svg
18+
v-if="showImage"
19+
class="absolute inset-0 h-full w-full text-gray-300"
20+
fill="currentColor"
21+
viewBox="0 0 24 24"
22+
xmlns="http://www.w3.org/2000/svg"
23+
>
24+
<path fill-rule="evenodd" d="M2 20h20L12 4 2 20z" clip-rule="evenodd" />
25+
</svg>
26+
</div>
27+
<div v-if="showText" class="space-y-2" :style="{ order: orderMap.text }">
28+
<div
29+
:style="{
30+
backgroundColor: skeletonColor,
31+
width: textWidthFirst,
32+
height: `${textHeight}`,
33+
}"
34+
class="rounded-md"
35+
></div>
36+
<div
37+
v-for="n in textLines"
38+
:key="n"
39+
:style="{
40+
backgroundColor: skeletonColor,
41+
width: textWidth,
42+
height: `${textHeight}`,
43+
}"
44+
class="rounded-md"
45+
></div>
46+
</div>
47+
<div
48+
v-if="showVideo"
49+
:style="{
50+
backgroundColor: skeletonColor,
51+
width: videoWidth,
52+
height: videoHeight,
53+
order: orderMap.video,
54+
}"
55+
class="relative flex items-center justify-center rounded-md"
56+
>
57+
<!-- Play button SVG for video placeholder -->
58+
<svg
59+
v-if="showVideo"
60+
fill="#000000"
61+
height="48px"
62+
width="48px"
63+
viewBox="0 0 60 60"
64+
xmlns="http://www.w3.org/2000/svg"
65+
>
66+
<path
67+
d="M45.563,29.174l-22-15c-0.307-0.208-0.703-0.231-1.031-0.058C22.205,14.289,22,14.629,22,15v30c0,0.371,0.205,0.711,0.533,0.884C22.679,45.962,22.84,46,23,46c0.197,0,0.394-0.059,0.563-0.174l22-15C45.836,30.64,46,30.331,46,30S45.836,29.36,45.563,29.174z M24,43.107V16.893L43.225,30L24,43.107z"
68+
/>
69+
<path
70+
d="M30,0C13.458,0,0,13.458,0,30s13.458,30,30,30s30-13.458,30-30S46.542,0,30,0z M30,58C14.561,58,2,45.439,2,30S14.561,2,30,2s28,12.561,28,28S45.439,58,30,58z"
71+
/>
72+
</svg>
73+
</div>
74+
</div>
75+
</template>
76+
77+
<script setup lang="ts">
78+
import { computed } from "vue"
79+
80+
const props = withDefaults(
81+
defineProps<{
82+
bgColor: string
83+
skeletonColor: string
84+
showImage: boolean
85+
showText: boolean
86+
showVideo: boolean
87+
imageWidth: string
88+
imageHeight: string
89+
textWidthFirst: string
90+
textWidth: string
91+
textHeight: string
92+
textTotalHeight: string
93+
videoWidth: string
94+
videoHeight: string
95+
orders: string
96+
}>(),
97+
{
98+
bgColor: "#f3f4f6", // Default background color
99+
skeletonColor: "#e1e1e1", // Default skeleton color
100+
showImage: false, // Toggle for showing image skeleton
101+
showText: false, // Toggle for showing text skeleton
102+
showVideo: false, // Toggle for showing video skeleton
103+
imageWidth: "100%", // Default image width
104+
imageHeight: "150px", // Default image height
105+
textWidthFirst: "25%", // Default text width of first line
106+
textWidth: "75%", // Default text width
107+
textHeight: "20px", // Default text height
108+
textTotalHeight: "40px", // Default total text height
109+
videoWidth: "100%", // Default video width
110+
videoHeight: "200px", // Default video height
111+
orders: "text image video", // Default order of elements
112+
},
113+
)
114+
115+
const textLines = computed(() => {
116+
const totalHeight = parseFloat(props.textTotalHeight)
117+
const lineHeight = parseFloat(props.textHeight)
118+
return Math.floor((totalHeight - lineHeight) / lineHeight)
119+
})
120+
121+
const orderMap = computed(() => {
122+
const orderArray = props.orders.split(" ")
123+
// TODO: fix this any type
124+
const map: any = {
125+
text: 0,
126+
image: 1,
127+
video: 2,
128+
}
129+
orderArray.forEach((element, index) => {
130+
map[element] = index
131+
})
132+
return map
133+
})
134+
</script>
135+
136+
<style scoped>
137+
div {
138+
@apply animate-pulse;
139+
}
140+
</style>

src/components/smart/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ export { default as HoppSmartPlaceholder } from "./Placeholder.vue"
2525
export { default as HoppSmartTree } from "./Tree.vue"
2626
export { default as HoppSmartTreeBranch } from "./TreeBranch.vue"
2727
export { default as HoppSmartSelectWrapper } from "./SelectWrapper.vue"
28+
export { default as HoppSmartSkeleton } from "./Skeleton.vue"

src/stories/Skeleton.story.vue

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<template>
2+
<Story title="Skeleton">
3+
<Variant title="Single">
4+
<HoppSmartSkeleton />
5+
</Variant>
6+
</Story>
7+
</template>
8+
9+
<script setup lang="ts">
10+
import { HoppSmartSkeleton } from "../components/smart"
11+
</script>
12+

0 commit comments

Comments
 (0)