feat(Loader): create loader component with spinner and optional message

This commit is contained in:
Ilia Mashkov
2026-02-06 12:03:06 +03:00
parent 4b440496ba
commit 3ed63562b7
2 changed files with 110 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
<script module>
import { defineMeta } from '@storybook/addon-svelte-csf';
import Loader from './Loader.svelte';
const { Story } = defineMeta({
title: 'Shared/Loader',
tags: ['autodocs'],
parameters: {
docs: {
description: {
component: 'Spinner with optional message',
},
story: { inline: false }, // Render stories in iframe for state isolation
},
},
argTypes: {
message: {
control: 'text',
description: 'Optional message to display',
defaultValue: 'analyzing_data',
},
size: {
control: 'number',
description: 'Size of the spinner',
defaultValue: 20,
},
},
});
</script>
<Story name="Default">
<Loader />
</Story>

View File

@@ -0,0 +1,77 @@
<!--
Component: Loader
Displays a loading spinner with an optional message.
-->
<script lang="ts">
import { fade } from 'svelte/transition';
interface Props {
/**
* Icon size (in pixels)
* @default 20
*/
size?: number;
/**
* Additional classes for container
*/
class?: string;
/**
* Message text
* @default analyzing_data
*/
message?: string;
}
let { size = 20, class: className = '', message = 'analyzing_data' }: Props = $props();
</script>
<div
class="absolute inset-x-0 inset-y-0 flex items-center justify-center gap-4 {className}"
in:fade={{ duration: 300 }}
out:fade={{ duration: 300 }}
>
<div style:width="{size}px" style:height="{size}px">
<svg class="stroke-gray-900 stroke-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(12, 12)">
<!-- Four corner brackets rotating -->
<g>
<path
d="M -8 -8 L -4 -8 M -8 -8 L -8 -4"
stroke="currentColor"
stroke-width="1"
stroke-linecap="round"
/>
<path
d="M 8 -8 L 4 -8 M 8 -8 L 8 -4"
stroke="currentColor"
stroke-width="1"
stroke-linecap="round"
/>
<path
d="M -8 8 L -4 8 M -8 8 L -8 4"
stroke="currentColor"
stroke-width="1"
stroke-linecap="round"
/>
<path d="M 8 8 L 4 8 M 8 8 L 8 4" stroke="currentColor" stroke-width="1" stroke-linecap="round" />
<animateTransform
attributeName="transform"
type="rotate"
from="0"
to="360"
dur="3s"
repeatCount="indefinite"
/>
</g>
</g>
</svg>
</div>
<!-- Divider -->
<div class="w-px h-3 bg-gray-400/50"></div>
<!-- Message -->
<span class="font-mono text-[10px] uppercase tracking-[0.2em] text-gray-600 font-medium">
{message}
</span>
</div>