---
title: "Компоненти"
description: "Документація компонентів для платформи Ring"
locale: "uk"
---
# Перевизначення компонентів

Безпечна заміна та розширення UI компонентів платформи Ring без порушення основної логіки та архітектури.

## 🏗️ Архітектура компонентів

Платформа Ring використовує модульну архітектуру компонентів з чітким розділенням між:

- **Базові компоненти** (`components/ui/`) - Radix UI примітиви
- **Функціональні компоненти** (`features/*/components/`) - бізнес логіка
- **Макет компоненти** (`features/layout/components/`) - навігація та структура
- **Загальні компоненти** (`components/common/`) - переважно використовувані елементи

## 🎨 Підходи до перевизначення

### Патерн обгортки компонентів

// components/custom/MyEntityCard.tsx

{`'use client'

import { EntityCard } from '@/features/entities/components/entity-card'
import { Badge } from '@/components/ui/badge'

interface MyEntityCardProps {
  entity: Entity
  showCustomBadge?: boolean
}

export function MyEntityCard({ entity, showCustomBadge = false, ...props }: MyEntityCardProps) {
  return (
    
      

      {showCustomBadge && (
        
          Преміум
        
      )}
    
  )
}`}

### Розширення через композицію

// components/custom/EnhancedOpportunityForm.tsx

{`'use client'

import { OpportunityForm } from '@/features/opportunities/components/opportunity-form'
import { Progress } from '@/components/ui/progress'

export function EnhancedOpportunityForm({ ...props }) {
  const [currentStep, setCurrentStep] = useState(1)
  const totalSteps = 4

  return (
    
      
        Створити можливість
        
        
          Крок {currentStep} з {totalSteps}
        
      

      
         {
            // Ваша користувацька валідація
            return validateCustomFields(data)
          }}
        />
      
    
  )
}`}

### Перевизначення стилів через CSS модулі

// components/custom/CustomButton.module.css

{`.customButton {
  background: linear-gradient(135deg, var(--brand-primary), var(--brand-secondary));
  border-radius: var(--border-radius-brand, 8px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.customButton:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}

// components/custom/CustomButton.tsx
import styles from './CustomButton.module.css'
import { Button } from '@/components/ui/button'

export function CustomButton({ className, ...props }) {
  return (
    
  )
}`}

## 🔧 Перевизначення ключових компонентів

### Навігація та макет

// components/custom/CustomNavigation.tsx

{`'use client'

import { Navigation } from '@/features/layout/components/navigation'
import { useTheme } from 'next-themes'
import { Crown } from 'lucide-react'

export function CustomNavigation() {
  const { theme } = useTheme()

  return (
    
          
          Моя Платформа
        
      }
      additionalActions={
        
          ✨
        
      }
    />
  )
}`}

### Форми та введення даних

// components/custom/CustomInput.tsx

{`'use client'

import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { useState } from 'react'
import { Eye, EyeOff } from 'lucide-react'

interface CustomInputProps {
  label: string
  type?: 'text' | 'password' | 'email'
  showStrengthIndicator?: boolean
  // ... інші пропси
}

export function CustomInput({
  label,
  type = 'text',
  showStrengthIndicator = false,
  ...props
}) {
  const [showPassword, setShowPassword] = useState(false)
  const [passwordStrength, setPasswordStrength] = useState(0)

  const inputType = type === 'password' && showPassword ? 'text' : type

  return (
    
      {label}

      
         {
            props.onChange?.(e)
            if (showStrengthIndicator && type === 'password') {
              setPasswordStrength(calculateStrength(e.target.value))
            }
          }}
        />

        {type === 'password' && (
           setShowPassword(!showPassword)}
            className="absolute right-3 top-1/2 -translate-y-1/2"
          >
            {showPassword ?  : }
          
        )}
      

      {showStrengthIndicator && type === 'password' && (
        
          
            {[1, 2, 3, 4].map((level) => (
              
            ))}
          
          
            Міцність пароля: {getStrengthLabel(passwordStrength)}
          
        
      )}
    
  )
}`}

## 🎯 Стратегії перевизначення

### Для різних рівнів кастомізації

#### Рівень 1: Мінімальні зміни
// Просто додавання CSS класів

{``}

#### Рівень 2: Обгортки з додатковою функціональністю

{`function MyEntityCard({ entity, ...props }) {
return (
     {
        trackCustomEvent('entity_clicked', entity.id)
        props.onClick?.()
      }}
    />
  )
}`}

#### Рівень 3: Повне перевизначення

{`function MyEntityCard({ entity, ...props }) {
// Повністю власна реалізація з використанням тих самих хуків
  const { user } = useSession()
  const { trackInteraction } = useAnalytics()

  return (
    
      {/* Ваша власна розмітка */}
    
  )
}`}

## 🧪 Тестування перевизначених компонентів

// __tests__/components/custom/MyEntityCard.test.tsx

{`import { render, screen, fireEvent } from '@testing-library/react'
import { MyEntityCard } from '@/components/custom/MyEntityCard'

describe('MyEntityCard', () => {
  it('відображає сутність з кастомним бейджем', () => {
    const entity = { id: '1', name: 'Тестова компанія' }

    render()

    expect(screen.getByText('Тестова компанія')).toBeInTheDocument()
    expect(screen.getByText('Преміум')).toBeInTheDocument()
  })

  it('викликає оригінальні обробники подій', () => {
    const handleClick = jest.fn()
    const entity = { id: '1', name: 'Тестова компанія' }

    render()

    fireEvent.click(screen.getByText('Тестова компанія'))

    expect(handleClick).toHaveBeenCalled()
  })
})`}

## 🚀 Рекомендації

### Найкращі практики

1. **Зберігайте API сумісність** - Ваші компоненти повинні приймати ті ж пропси
2. **Використовуйте композицію** - Обгортки замість повного переписування
3. **Тестуйте ретельно** - Переконайтеся, що функціональність не порушена
4. **Документуйте зміни** - Опишіть, що саме було змінено та чому

### Поширені помилки уникати

- ❌ Не змінюйте внутрішню логіку існуючих компонентів
- ❌ Не порушуйте контракти пропсів та подій
- ❌ Не видаляйте важливі функції доступності
- ❌ Не ігноруйте теми та responsive дизайн

### Коли використовувати перевизначення

- ✅ Для брендингу та візуального стилю
- ✅ Для додавання специфічної бізнес логіки
- ✅ Для інтеграції з зовнішніми сервісами
- ✅ Для покращення UX для конкретних випадків використання

---

**🔧 Гнучка архітектура компонентів**

Платформа Ring розроблена з урахуванням можливості кастомізації, дозволяючи адаптувати інтерфейс під ваші потреби без порушення основної функціональності.

*Потрібна допомога з перевизначенням компонентів? Зверніться до [документації API](https://ring-platform.org/docs/api/components) або приєднайтеся до нашого [Discord спільноти](https://discord.gg/ring-platform).*"
