Skip to content

Commit

Permalink
feat added all Loader variants
Browse files Browse the repository at this point in the history
  • Loading branch information
mosarabi committed Jun 19, 2024
1 parent 1aece86 commit ceb2441
Show file tree
Hide file tree
Showing 16 changed files with 323 additions and 247 deletions.
12 changes: 6 additions & 6 deletions packages/alto/src/_dev/App.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
<script setup lang="ts">
import 'uno.css';
import '../assets/main.css';
import { Spinner } from '~/components';
import { Loader } from '~/components';
</script>

<template>
<div class="container">
<div class="row">
<Spinner />
</div>
<Loader type="spinner" />
<Loader type="brand" />
<Loader type="bar" />
</div>
</template>

<style scoped>
.container {
display: flex;
flex-direction: column;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
gap: 40px;
gap: 80px;
}
</style>
4 changes: 2 additions & 2 deletions packages/alto/src/components/Dropdown/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { elementChildrenNavigate } from './utils';
import type { DropdownGroupValue } from './types';
import type { DropdownProps, DropdownValue } from '~/types';
import { setPosition } from '~/helpers';
import { Spinner } from '~/components';
import { Loader } from '~/components';
const props = withDefaults(defineProps<DropdownProps>(), {
multiSelectLabel: 'Selected items',
Expand Down Expand Up @@ -342,7 +342,7 @@ onClickOutside(dropdown, () => show.value = false);
{{ noDataText }}
</div>
<div v-if="isLoading" class="loading">
<Spinner :size="15" />
<Loader :size="15" />
</div>
</div>
</Transition>
Expand Down
75 changes: 75 additions & 0 deletions packages/alto/src/components/Loader/Loader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script setup lang="ts">
import { computed, defineAsyncComponent } from 'vue';
import type { LoaderProps } from '~/types';
const props = withDefaults(
defineProps<LoaderProps>(),
{
color: 'var(--brand-500)',
size: 40,
labelColor: 'var(--brand-500)',
labelFontSize: 'md',
type: 'spinner',
},
);
const loaderType = computed (() => {
if (['brand', 'bar', 'spinner'].includes(props.type)) {
return props.type;
}
return 'spinner';
});
const loader = computed (() => {
switch (props.type) {
case 'brand':
return defineAsyncComponent(() => import('./internal/BrandLoading.vue'));
case 'bar':
return defineAsyncComponent(() => import('./internal/BarLoading.vue'));
default:
return defineAsyncComponent(() => import('./internal/Spinner.vue'));
}
});
</script>

<template>
<div class="loader-block">
<component :is="loader" :type="loaderType" :color="color" :size="size" />

<p v-if="label" class="label" :class="[labelFontSize]">
{{ label }}
</p>
</div>
</template>

<style scoped>
.loader-block {
display: flex;
flex-direction: column;
align-items: center;
}
.loader-block .label {
color: v-bind("labelColor");
font: var(--text-md-regular);
}
.loader-block .label.xs {
font: var(--text-xs-regular);
}
.loader-block .label.sm {
font: var(--text-sm-regular);
}
.loader-block .label.lg {
font: var(--text-lg-regular);
}
.loader-block .label.xl {
font: var(--text-xl-regular);
}
</style>
Binary file not shown.
35 changes: 35 additions & 0 deletions packages/alto/src/components/Loader/internal/BarLoading.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup lang="ts">
import type { BarLoadingProps } from '~/types';
defineProps<BarLoadingProps>();
</script>

<template>
<div class="bar-spinner" />
</template>

<style scoped>
.bar-spinner {
position: relative;
width: 160px;
height: 4px;
overflow: hidden;
border-radius: 2px;
background-color: var(--gray-100);
}
.bar-spinner::before {
content: "";
display: block;
width: 100%;
height: 100%;
animation: progress 1200ms ease-in-out backwards alternate infinite;
border-radius: 2px;
background-color: v-bind(color);
}
@keyframes progress {
0% { transform: translateX(-93%); }
100% { transform: translateX(93%); }
}
</style>
104 changes: 104 additions & 0 deletions packages/alto/src/components/Loader/internal/BrandLoading.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { BrandLoadingProps } from '~/types';
const props = withDefaults(defineProps<BrandLoadingProps>(), {
size: 67,
});
const percent = 59 / 67;
const height = computed(() => `${props.size * percent}px`);
const width = computed(() => `${props.size}px`);
</script>

<template>
<div class="brand-loader-block">
<div class="brand-loader" :class="type">
<div class="shadow-brand">
<svg :width="width" :height="height" fill="var(--gray-100)" viewBox="0 0 67 59" xmlns="http://www.w3.org/2000/svg">
<path d="M20.4435 0H0L12.7798 29.0835H33.2128L20.4435 0Z" />
<path d="M45.9925 0L33.2128 29.0939L20.433 58.167H40.8765L53.6562 29.0939L66.436 0H45.9925Z" />
</svg>
</div>
<div class="animated-brand">
<svg :width="width" :height="height" viewBox="0 0 67 59" :fill="color" :stroke="color" xmlns="http://www.w3.org/2000/svg">
<path d="M32.4472 28.5835H13.1062L0.765851 0.5H20.1169L32.4472 28.5835Z" />
<path d="M33.6705 29.2951L33.6705 29.295L46.319 0.5H65.6703L53.1985 28.8927L53.1984 28.8928L40.5501 57.667H21.1989L33.6705 29.2951Z" />
</svg>
</div>
</div>
</div>
</template>

<style scoped>
.brand-loader-block {
width: v-bind(width);
height: v-bind(height);
}
.brand-loader {
display: flex;
position: absolute;
justify-content: center;
overflow: hidden;
}
.brand-loader .shadow-brand,
.brand-loader .animated-brand {
width: v-bind(width);
height: v-bind(height);
}
.brand-loader .shadow-brand {
position: relative;
z-index: 1;
bottom: 0;
}
.brand-loader .animated-brand {
position: absolute;
z-index: 2;
bottom: 0;
overflow: hidden;
}
.brand-loader.fill-brand .animated-brand {
animation: fill 600ms ease-in-out backwards alternate infinite;
}
.brand-loader.draw-brand .shadow-brand svg path {
fill: transparent;
}
.brand-loader.draw-brand .animated-brand svg path {
animation: draw 1500ms linear forwards infinite;
stroke-dasharray: 163.58;
fill: transparent;
}
.brand-loader .animated-brand svg {
position: absolute;
bottom: 0;
}
@keyframes fill {
0% { height: 0; }
100% { height: 100%; }
}
@keyframes draw {
0% { stroke-dashoffset: 163.58; }
40%,
50% {
stroke-dashoffset: 0;
fill: transparent;
}
85%,
100% {
stroke-dashoffset: 0;
fill: v-bind(color);
}
}
</style>
71 changes: 71 additions & 0 deletions packages/alto/src/components/Loader/internal/Spinner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { SpinnerProps } from '~/types';
const props = defineProps<SpinnerProps>();
const spinnerSize = computed(() => `${props.size}px`);
const borderWidth = computed(() => `${props.size * 0.1}px`);
</script>

<template>
<div class="spinner-block" :class="type">
<div class="spinner" />
<div class="shadow" />
</div>
</template>

<style scoped>
.spinner-block {
position: relative;
width: v-bind(spinnerSize);
height: v-bind(spinnerSize);
}
.spinner-block > div {
box-sizing: border-box;
width: 100%;
height: 100%;
border: v-bind(borderWidth) solid var(--gray-100);
border-radius: 50%;
}
.spinner-block .spinner {
position: absolute;
z-index: 2;
animation: s4 1s infinite linear;
border-right-color: v-bind(color);
}
.spinner-block.shadow-spinner .spinner {
border: 0;
background-image: linear-gradient(177deg, var(--f8a75c4c-color) 15%, var(--gray-100) 93%);
mask-image: url("../assets/spinner-shadow.webp");
mask-size: v-bind(spinnerSize);
mask-repeat: no-repeat;
}
.spinner-block.shadow-spinner .spinner::before {
content: "";
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 100%;
height: 100%;
border: v-bind(borderWidth) solid transparent;
border-radius: 50%;
border-right-color: v-bind(color);
}
.spinner-block .shadow {
position: relative;
z-index: 1;
}
@keyframes s4 {
100% {
transform: rotate(1turn);
}
}
</style>
23 changes: 23 additions & 0 deletions packages/alto/src/components/Loader/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export type SpinnerType = 'brand' | 'bar' | 'spinner';

interface SharedProps {
size: number
type?: SpinnerType
color?: string
}
export interface BarLoadingProps {
color?: string
}

export interface BrandLoadingProps extends SharedProps {}

export interface SpinnerProps extends SharedProps {}

export interface LoaderProps {
label?: string
size?: number
color?: string
labelColor?: string
labelFontSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
type?: SpinnerType
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ResourceItem from './Internal/Resource.vue';
import { isEmptyArray } from './utils';
import type { PickerProps, Resource } from '~/types';
import Overlay from '~/components/Overlay/Overlay.vue';
import { Button, Input, Spinner } from '~/components';
import { Button, Input, Loader } from '~/components';
const props = withDefaults(defineProps<PickerProps>(), {
visible: false,
Expand Down Expand Up @@ -120,7 +120,7 @@ onUnmounted(() => {
<Input v-model.trim="term" :placeholder="searchPlaceholder" @input.stop="handleSearch" @keyup.enter.stop="handleSearch" />
</div>
<div v-if="isLoading" class="loading">
<Spinner label="" />
<Loader />
</div>
<ul v-else-if="!isEmptyArray(resources)" class="list">
<li v-for="resource in resources" :key="resource.id" class="resource">
Expand Down
Loading

0 comments on commit ceb2441

Please sign in to comment.