# Spinner

A loading indicator component that displays an animated spinner to indicate processing, loading, or waiting states. This component provides visual feedback to users during asynchronous operations and helps maintain engagement during loading periods.

## Aliases

- Spinner
- LoadingSpinner
- Loader

## Props Breakdown

**Extends:** Standalone interface (no HTML element inheritance)

| Prop | Type | Default | Required | Description |
|------|------|---------|----------|-------------|
| `className` | `string` | `undefined` | No | Additional class for styling |

*Note: This component has a standalone interface and does not inherit from any HTML element attributes. It only accepts the props explicitly defined above.*

## Examples

### Basic Spinner

```tsx
import { Spinner } from '@delightui/components';

function BasicSpinnerExample() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center', padding: '20px' }}>
      <Spinner />
    </div>
  );
}
```

### Loading State in Button

```tsx
import { Button, Spinner } from '@delightui/components';

function LoadingButtonExample() {
  const [isLoading, setIsLoading] = useState(false);

  const handleClick = async () => {
    setIsLoading(true);
    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 2000));
    setIsLoading(false);
  };

  return (
    <Button
      onClick={handleClick}
      disabled={isLoading}
      leadingIcon={isLoading ? <Spinner /> : undefined}
    >
      {isLoading ? 'Loading...' : 'Submit'}
    </Button>
  );
}
```

### Form Loading State

```tsx
import { Form, FormField, Input, Button, Spinner, Text } from '@delightui/components';

function FormLoadingExample() {
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleSubmit = async (data: any) => {
    setIsSubmitting(true);
    try {
      // Simulate form submission
      await new Promise(resolve => setTimeout(resolve, 3000));
      console.log('Form submitted:', data);
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <Form onSubmit={handleSubmit}>
      <FormField name="name" label="Name" required>
        <Input placeholder="Enter your name" disabled={isSubmitting} />
      </FormField>

      <FormField name="email" label="Email" required>
        <Input 
          inputType="Email" 
          placeholder="Enter your email" 
          disabled={isSubmitting} 
        />
      </FormField>

      {isSubmitting ? (
        <div style={{ 
          display: 'flex', 
          alignItems: 'center', 
          gap: '12px', 
          justifyContent: 'center',
          padding: '20px'
        }}>
          <Spinner />
          <Text>Submitting your information...</Text>
        </div>
      ) : (
        <Button type="submit">
          Submit Form
        </Button>
      )}
    </Form>
  );
}
```

### Data Loading State

```tsx
import { Spinner, Text, Button } from '@delightui/components';

function DataLoadingExample() {
  const [data, setData] = useState<string[] | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const loadData = async () => {
    setLoading(true);
    setError(null);
    try {
      // Simulate data fetching
      await new Promise(resolve => setTimeout(resolve, 2000));
      setData(['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5']);
    } catch (err) {
      setError('Failed to load data');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
      <div style={{ marginBottom: '16px' }}>
        <Button onClick={loadData} disabled={loading}>
          Load Data
        </Button>
      </div>

      {loading && (
        <div style={{ 
          display: 'flex', 
          flexDirection: 'column',
          alignItems: 'center', 
          gap: '12px',
          padding: '40px'
        }}>
          <Spinner />
          <Text color="secondary">Loading data...</Text>
        </div>
      )}

      {error && (
        <Text color="error">{error}</Text>
      )}

      {data && !loading && (
        <div>
          <Text weight="bold" style={{ marginBottom: '8px' }}>
            Loaded Data:
          </Text>
          <ul>
            {data.map((item, index) => (
              <li key={index}>
                <Text>{item}</Text>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}
```

### Page Loading Overlay

```tsx
import { Spinner, Text } from '@delightui/components';

function PageLoadingOverlayExample() {
  const [isPageLoading, setIsPageLoading] = useState(false);

  const simulatePageLoad = () => {
    setIsPageLoading(true);
    setTimeout(() => setIsPageLoading(false), 3000);
  };

  return (
    <div style={{ position: 'relative', minHeight: '300px' }}>
      <div>
        <Text weight="bold" style={{ marginBottom: '16px' }}>
          Page Content
        </Text>
        <Text style={{ marginBottom: '16px' }}>
          This is some page content that would be covered by the loading overlay.
        </Text>
        <Button onClick={simulatePageLoad}>
          Simulate Page Loading
        </Button>
      </div>

      {isPageLoading && (
        <div style={{
          position: 'absolute',
          top: 0,
          left: 0,
          right: 0,
          bottom: 0,
          backgroundColor: 'rgba(255, 255, 255, 0.8)',
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          gap: '16px',
          zIndex: 1000
        }}>
          <Spinner />
          <Text weight="medium">Loading page...</Text>
        </div>
      )}
    </div>
  );
}
```

### Card Loading State

```tsx
import { Spinner, Text, Button } from '@delightui/components';

function CardLoadingExample() {
  const [cards, setCards] = useState([
    { id: 1, loading: false, content: 'Card 1 content loaded' },
    { id: 2, loading: false, content: 'Card 2 content loaded' },
    { id: 3, loading: false, content: 'Card 3 content loaded' }
  ]);

  const loadCard = async (cardId: number) => {
    setCards(prev => prev.map(card => 
      card.id === cardId ? { ...card, loading: true } : card
    ));

    // Simulate loading
    await new Promise(resolve => setTimeout(resolve, 2000));

    setCards(prev => prev.map(card => 
      card.id === cardId ? { 
        ...card, 
        loading: false, 
        content: `Updated content for Card ${cardId} - ${new Date().toLocaleTimeString()}`
      } : card
    ));
  };

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '16px' }}>
      {cards.map(card => (
        <div 
          key={card.id}
          style={{ 
            border: '1px solid #ccc', 
            borderRadius: '8px', 
            padding: '16px',
            minHeight: '150px',
            display: 'flex',
            flexDirection: 'column'
          }}
        >
          <Text weight="bold" style={{ marginBottom: '12px' }}>
            Card {card.id}
          </Text>

          {card.loading ? (
            <div style={{ 
              flex: 1,
              display: 'flex', 
              flexDirection: 'column',
              alignItems: 'center', 
              justifyContent: 'center',
              gap: '8px'
            }}>
              <Spinner />
              <Text size="small" color="secondary">Loading...</Text>
            </div>
          ) : (
            <>
              <Text style={{ flex: 1, marginBottom: '12px' }}>
                {card.content}
              </Text>
              <Button 
                size="Small" 
                type="Outlined"
                onClick={() => loadCard(card.id)}
              >
                Refresh
              </Button>
            </>
          )}
        </div>
      ))}
    </div>
  );
}
```

### List Loading with Placeholder

```tsx
import { Spinner, Text, Button } from '@delightui/components';

function ListLoadingExample() {
  const [items, setItems] = useState<string[]>([]);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);

  const loadItems = async () => {
    setLoading(true);
    // Simulate loading more items
    await new Promise(resolve => setTimeout(resolve, 1500));
    
    const newItems = Array.from({ length: 5 }, (_, i) => 
      `Item ${items.length + i + 1}`
    );
    
    setItems(prev => [...prev, ...newItems]);
    setHasMore(items.length + newItems.length < 20);
    setLoading(false);
  };

  return (
    <div style={{ 
      border: '1px solid #ccc', 
      borderRadius: '8px', 
      padding: '16px',
      maxWidth: '300px'
    }}>
      <Text weight="bold" style={{ marginBottom: '16px' }}>
        Item List
      </Text>

      <div style={{ marginBottom: '16px' }}>
        {items.map((item, index) => (
          <div 
            key={index}
            style={{ 
              padding: '8px 0', 
              borderBottom: index < items.length - 1 ? '1px solid #eee' : 'none'
            }}
          >
            <Text>{item}</Text>
          </div>
        ))}
      </div>

      {loading && (
        <div style={{ 
          display: 'flex', 
          alignItems: 'center', 
          justifyContent: 'center',
          gap: '8px',
          padding: '16px'
        }}>
          <Spinner />
          <Text size="small" color="secondary">Loading more items...</Text>
        </div>
      )}

      {!loading && hasMore && (
        <Button 
          onClick={loadItems}
          size="Small"
          style={{ width: '100%' }}
        >
          Load More
        </Button>
      )}

      {!hasMore && items.length > 0 && (
        <Text size="small" color="secondary" style={{ textAlign: 'center' }}>
          All items loaded
        </Text>
      )}
    </div>
  );
}
```

### Search with Loading

```tsx
import { Input, Spinner, Text } from '@delightui/components';

function SearchLoadingExample() {
  const [searchTerm, setSearchTerm] = useState('');
  const [loading, setLoading] = useState(false);
  const [results, setResults] = useState<string[]>([]);

  const mockData = [
    'Apple', 'Banana', 'Cherry', 'Date', 'Elderberry',
    'Fig', 'Grape', 'Honeydew', 'Kiwi', 'Lemon'
  ];

  useEffect(() => {
    if (searchTerm.trim() === '') {
      setResults([]);
      return;
    }

    setLoading(true);
    const timeoutId = setTimeout(() => {
      const filteredResults = mockData.filter(item =>
        item.toLowerCase().includes(searchTerm.toLowerCase())
      );
      setResults(filteredResults);
      setLoading(false);
    }, 800);

    return () => clearTimeout(timeoutId);
  }, [searchTerm]);

  return (
    <div style={{ maxWidth: '300px' }}>
      <Input
        value={searchTerm}
        onValueChange={setSearchTerm}
        placeholder="Search items..."
        leadingIcon={loading ? <Spinner /> : <Icon name="SearchFilled" />}
      />

      <div style={{ marginTop: '16px', minHeight: '100px' }}>
        {loading ? (
          <div style={{ 
            display: 'flex', 
            alignItems: 'center', 
            justifyContent: 'center',
            padding: '20px'
          }}>
            <Text color="secondary">Searching...</Text>
          </div>
        ) : results.length > 0 ? (
          <div>
            {results.map((result, index) => (
              <div 
                key={index}
                style={{ 
                  padding: '8px', 
                  borderBottom: '1px solid #eee'
                }}
              >
                <Text>{result}</Text>
              </div>
            ))}
          </div>
        ) : searchTerm.trim() !== '' ? (
          <div style={{ padding: '20px', textAlign: 'center' }}>
            <Text color="secondary">No results found</Text>
          </div>
        ) : null}
      </div>
    </div>
  );
}
```

### Custom Styled Spinner

```tsx
import { Spinner } from '@delightui/components';

function CustomStyledSpinnerExample() {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '24px', alignItems: 'center' }}>
      <div>
        <Text style={{ marginBottom: '8px' }}>Default Spinner</Text>
        <Spinner />
      </div>

      <div>
        <Text style={{ marginBottom: '8px' }}>Large Custom Spinner</Text>
        <Spinner 
          className="large-spinner"
          style={{ 
            width: '48px', 
            height: '48px',
            borderWidth: '4px'
          }}
        />
      </div>

      <div>
        <Text style={{ marginBottom: '8px' }}>Colored Spinner</Text>
        <Spinner 
          className="colored-spinner"
          style={{ 
            borderTopColor: '#007bff',
            borderRightColor: '#007bff'
          }}
        />
      </div>

      <div>
        <Text style={{ marginBottom: '8px' }}>Small Spinner</Text>
        <Spinner 
          className="small-spinner"
          style={{ 
            width: '16px', 
            height: '16px',
            borderWidth: '2px'
          }}
        />
      </div>
    </div>
  );
}
```

### Multi-step Loading

```tsx
import { Spinner, Text, Button } from '@delightui/components';

function MultiStepLoadingExample() {
  const [currentStep, setCurrentStep] = useState(0);
  const [loading, setLoading] = useState(false);

  const steps = [
    'Initializing...',
    'Processing data...',
    'Validating results...',
    'Finalizing...',
    'Complete!'
  ];

  const startProcess = async () => {
    setLoading(true);
    setCurrentStep(0);

    for (let i = 0; i < steps.length - 1; i++) {
      setCurrentStep(i);
      await new Promise(resolve => setTimeout(resolve, 1000));
    }

    setCurrentStep(steps.length - 1);
    setLoading(false);
  };

  return (
    <div style={{ 
      padding: '24px', 
      border: '1px solid #ccc', 
      borderRadius: '8px',
      textAlign: 'center',
      maxWidth: '300px'
    }}>
      <Text weight="bold" style={{ marginBottom: '24px' }}>
        Multi-step Process
      </Text>

      {!loading && currentStep < steps.length - 1 && (
        <Button onClick={startProcess}>
          Start Process
        </Button>
      )}

      {(loading || currentStep === steps.length - 1) && (
        <div style={{ 
          display: 'flex', 
          flexDirection: 'column',
          alignItems: 'center',
          gap: '16px'
        }}>
          {loading && <Spinner />}
          <Text 
            color={currentStep === steps.length - 1 ? 'success' : 'default'}
            weight="medium"
          >
            {steps[currentStep]}
          </Text>
          
          {currentStep === steps.length - 1 && (
            <Button 
              size="Small" 
              type="Outlined"
              onClick={() => setCurrentStep(0)}
            >
              Reset
            </Button>
          )}
        </div>
      )}
    </div>
  );
}
```