feat(Loader): create loader component with spinner and optional message
This commit is contained in:
33
src/shared/ui/Loader/Loader.stories.svelte
Normal file
33
src/shared/ui/Loader/Loader.stories.svelte
Normal 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>
|
||||
77
src/shared/ui/Loader/Loader.svelte
Normal file
77
src/shared/ui/Loader/Loader.svelte
Normal 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>
|
||||
Reference in New Issue
Block a user