Skip to content

Commit

Permalink
Feat/skeleton (#2918)
Browse files Browse the repository at this point in the history
* feat(skeleton): init skeleton component

* chore: minor refactor

* chore: refactoring

* remove old odcs

* docs(skeleton): init skeleton docs

* feat(skeleton): rename textWidth to lastLineWidth

* feat(skeleton): changed demos

* feat(skeleton): css variables

* feat(skeleton): added new badge in docs

* feat(skeleton): accessability improvement

* fix(skeleton): wave animation on text

* docs(skeleton): loading example
  • Loading branch information
m0ksem authored Feb 20, 2023
1 parent 82cbd44 commit 7ca7772
Show file tree
Hide file tree
Showing 27 changed files with 681 additions and 8 deletions.
1 change: 1 addition & 0 deletions .stylelintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'color-hex-length': 'long',
'color-hex-case': 'lower',
'at-rule-no-unknown': null,
'function-calc-no-unspaced-operator': null,
'indentation': [ 2, { baseIndentLevel: 1 } ],
"selector-pseudo-class-no-unknown": [true, {
ignorePseudoClasses: ["deep"]
Expand Down
8 changes: 7 additions & 1 deletion packages/docs/components/ComponentPlayground.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const optionValues = computed(() => {
return values;
});
const filteredOptions = computed(() => props.options.filter((option) => !option.hidden?.(optionValues.value)));
const codeEl = ref<HTMLElement>();
const copyButtonIcon = ref('content_copy')
Expand All @@ -53,7 +55,7 @@ const copyCode = async () => {
>
<va-card-content>
<div
v-for="option in options"
v-for="option in filteredOptions"
:key="option.key"
class="mb-2"
>
Expand All @@ -62,13 +64,15 @@ const copyCode = async () => {
v-model="option.value"
class="w-full"
:label="option.key"
:rules="option.rules"
/>
<va-select
v-if="option.type === 'select'"
v-model="option.value"
class="w-full"
:options="option.options"
:label="option.key"
:rules="option.rules"
clearable
prevent-overflow
/>
Expand All @@ -78,6 +82,7 @@ const copyCode = async () => {
class="w-full"
:options="option.options"
:label="option.key"
:rules="option.rules"
clearable
multiple
prevent-overflow
Expand All @@ -88,6 +93,7 @@ const copyCode = async () => {
:label="option.key"
:true-value="true"
:false-value="false"
:rules="option.rules"
/>
</div>
</va-card-content>
Expand Down
17 changes: 11 additions & 6 deletions packages/docs/composables/useComponentPlayground.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
export type PlaygroundOption = {
import { ValidationRule } from "vuestic-ui/src/composables"

export type PlaygroundOption<T extends string = string> = {
key: string,
type: 'select' | 'input' | 'color' | 'checkbox' | 'multiselect' | 'none',
options?: string[],
/** In case you want value to be clear, but provide default value */
defaultValue?: any,
value?: string | boolean | string[]
value?: string | boolean | string[],
hidden?: (options: Record<T, any>) => boolean,
rules?: ValidationRule[]
}

export const useComponentPlayground = (options: Record<string, Omit<PlaygroundOption, 'key'>>) => {
const optionsRef = reactive(Object.keys(options).map((key) => reactive({ ...options[key], key })))
export const useComponentPlayground = <Options extends string>(options: Record<Options, Omit<PlaygroundOption<Options>, 'key'>>) => {
const optionsRef = reactive(Object.keys(options).map((key) => reactive({ ...options[key as Options], key })))

const slots = computed(() => {
return optionsRef
Expand All @@ -17,7 +22,7 @@ export const useComponentPlayground = (options: Record<string, Omit<PlaygroundOp
.map(({ key, value }) => {
return {
name: key.replace('slot:', ''),
value: value
value: String(value)
}
})
})
Expand Down Expand Up @@ -105,7 +110,7 @@ ${slots}
}

return {
options: optionsRef,
options: optionsRef as unknown as (PlaygroundOption & { value: any })[],
attrs,
slots,
renderAttrs,
Expand Down
57 changes: 56 additions & 1 deletion packages/docs/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,18 @@
"updateChecked": "The array of checked tree nodes.",
"updateExpanded": "The array of expanded tree nodes."
}
},
"VaSkeleton": {
"props": {
"width": "Skeleton width.",
"height": "Skeleton height.",
"animation": "Skeleton animation. Can be `pulse`, `wave` or `none`.",
"variant": "Skeleton variant. Can be `squared`, `rounded`, `circle` or `text`.",
"lines": "Lines count if `variant` is `text`",
"lineGap": "Gap between lines in `px`, `rem`, `em` if `variant` is `text`",
"lastLineWidth": "Width of last line in `px`, `rem`, `em`, `%` if `variant` is `text`",
"tag": "Skeleton tag."
}
}
},
"menu": {
Expand Down Expand Up @@ -1633,7 +1645,8 @@
"team": "Meet The Team",
"i18n": "I18n",
"webComponents": "Web Components",
"colorsClasses": "Colors Classes"
"colorsClasses": "Colors Classes",
"skeleton": "Skeleton"
},
"all": {
"examples": "Examples",
Expand Down Expand Up @@ -5029,5 +5042,47 @@
"api": {
"title": "Helper classes configuration API"
}
},
"skeleton": {
"title": "Skeleton",
"description": "Skeleton is a component that is used to show the user that the content is loading. It is used to improve the user experience and to show the user that the content is loading.",
"examples": {
"default": {
"title": "Default",
"text": "The default skeleton is a `squared` reactable with a width of 100% and a height of 5rem."
},
"text": {
"title": "Text",
"text": "The skeleton with `text` variant looks like a text line. Count of lines can be configured with `lines` prop. The last line can be shorter than others with `lastLineWidth` prop."
},
"headline": {
"title": "Headline",
"text": "You can apply typography or any other classes to the skeleton. For example, `va-h1` class will make the skeleton look like a headline."
},
"circle": {
"title": "Circle",
"text": "The skeleton with `circle` variant looks like a circle. The size of the circle can be configured with `height` and `width` prop. Usualy used as avatar placeholder."
},
"rounded": {
"title": "Rounded",
"text": "The skeleton with `rounded` variant looks like a rounded rectangle. The size of the rectangle can be configured with `height` and `width` prop. Can be used as button or input placeholder."
},
"square": {
"title": "Square",
"text": "The skeleton with `square` variant looks like a square. The size of the square can be configured with `height` and `width` prop. Can be used as card or image placeholder."
},
"group": {
"title": "Group",
"text": "Skeletons can be grouped in `VaSekeletonGroup` component to sync their animations. For example, you can use it to create a card, list or table placeholder."
},
"groupWave": {
"title": "Wave animation",
"text": "You can use `wave` animation for `VaSkeletonGroup` to make it look like a wave. VaSkeletonGroup will sync animations."
},
"loading": {
"title": "Loading",
"text": "Down bellow is an example how you can use skeleton in your application. We use `isLoading` ref to indicate if content is loading. Don't forget to add `aria-busy` attribute to the content to indicate that it is loading to sreen reader."
}
}
}
}
7 changes: 7 additions & 0 deletions packages/docs/page-config/navigationRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ export const navigationRoutes: NavigationRoute[] = [
badge: 'new',
},
},
{
name: "skeleton",
displayName: "menu.skeleton",
meta: {
badge: "new",
}
},
{
name: "card",
displayName: "menu.card",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default defineManualApi({});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<ComponentPlayground
v-slot="{ bind, slots }"
:options="options"
:code="renderComponent('va-skeleton')"
:slots="slots"
>
<VaCard class="w-full">
<VaCardContent>
<VaSkeleton v-bind="bind">
<template v-for="slot in slots" #[slot.name]>
{{ slot.value }}
</template>
</VaSkeleton>
</VaCardContent>
</VaCard>
</ComponentPlayground>
</template>

<script setup lang="ts">
import { useComponentPlayground } from "@/composables/useComponentPlayground";
const { options, renderComponent, slots } = useComponentPlayground({
variant: {
type: "select",
value: "squared",
options: ["squared", "circle", "rounded", "text"],
},
height: {
type: "input",
value: "",
rules: [
(v: string) =>
/(\d(\%|rem|em|px))$/.test(v) || "Height must be a css size",
],
},
width: {
type: "input",
value: "",
rules: [
(v: string) => /(\d(\%|rem|em|px))$/.test(v) || "Must must be css size",
],
},
color: {
type: "input",
value: "",
},
animation: {
type: "select",
value: "pulse",
options: ["pulse", "wave", "none"],
},
gradient: {
type: "checkbox",
value: false,
},
// Text
lines: {
type: "input",
value: "",
hidden: (props) => props.variant !== "text",
rules: [(v: string) => /\d$/.test(v) || "Must must be a number"],
},
lastLineWidth: {
type: "input",
value: "",
hidden: (props) => props.variant !== "text",
rules: [
(v: string) => /(\d(\%|rem|em|px))$/.test(v) || "Must must be css size",
],
},
lineGap: {
type: "input",
value: "",
hidden: (props) => props.variant !== "text",
rules: [(v: string) => /\d$/.test(v) || "Must must be a number"],
},
});
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<VaSkeleton variant="circle" height="4rem" />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<VaSkeleton />
</template>
18 changes: 18 additions & 0 deletions packages/docs/page-config/ui-elements/skeleton/examples/Group.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<VaSkeletonGroup>
<VaCard>
<VaSkeleton variant="squared" height="120px" />
<va-card-content style="display: flex; align-items: center;">
<VaSkeleton variant="circle" width="1rem" height="48px" />
<VaSkeleton variant="text" class="ml-2" :lines="2" />
</va-card-content>
<va-card-content>
<VaSkeleton variant="text" :lines="4" text-width="50%" />
</va-card-content>
<va-card-actions style="display: flex; justify-content: end;">
<VaSkeleton class="mr-2" variant="rounded" inline width="64px" height="32px" />
<VaSkeleton variant="rounded" inline width="64px" height="32px" />
</va-card-actions>
</VaCard>
</VaSkeletonGroup>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<VaSkeletonGroup animation="wave">
<VaCard>
<va-card-content style="display: flex; align-items: center;">
<VaSkeleton variant="circle" width="1rem" height="48px" />
<VaSkeleton variant="text" class="ml-2" :lines="2" />
</va-card-content>
<va-card-actions style="display: flex; justify-content: end;">
<VaSkeleton class="mr-2" variant="rounded" inline width="64px" height="32px" />
<VaSkeleton variant="rounded" inline width="64px" height="32px" />
</va-card-actions>
</VaCard>
</VaSkeletonGroup>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<VaSkeleton tag="h1" variant="text" class="va-h1" />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<div :aria-busy="isLoading">
<VaSkeletonGroup v-if="isLoading" animation="wave" :delay="0">
<VaCard>
<va-card-content style="display: flex; align-items: center;">
<VaSkeleton variant="circle" width="1rem" height="48px" />
<VaSkeleton variant="text" class="ml-2 va-text" :lines="2" />
</va-card-content>
<va-card-actions style="display: flex; justify-content: end;">
<VaSkeleton class="mr-2" variant="rounded" inline width="82px" height="32px" />
<VaSkeleton variant="rounded" inline width="64px" height="32px" />
</va-card-actions>
</VaCard>
</VaSkeletonGroup>

<VaCard v-else>
<va-card-content style="display: flex; align-items: center;">
<VaAvatar src="https://i.pinimg.com/280x280_RS/08/e9/ba/08e9ba9cbd24db8de0c250570af460a4.jpg" />
<p class="ml-2 mb-0 va-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
tincidunt, nisl eget aliquam tincidunt, nisl nisl aliquam
tortor, eget aliquam nisl nisl sit amet lorem.
</p>
</va-card-content>

<va-card-actions style="display: flex; justify-content: end;">
<va-button color="primary">Message</va-button>
<va-button color="secondary">Follow</va-button>
</va-card-actions>
</VaCard>

<va-divider class="my-8" />

<va-button @click="load">Load</va-button>
</div>
</template>

<script setup>
import { ref } from 'vue'
const isLoading = ref(true)
const load = () => {
isLoading.value = true
setTimeout(() => {
isLoading.value = false
}, 2000)
}
onMounted(load)
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<VaSkeleton variant="rounded" inline width="64px" height="32px" />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<VaSkeleton variant="squared" height="10rem" />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<VaSkeleton variant="text" :lines="2" />
</template>
Loading

0 comments on commit 7ca7772

Please sign in to comment.