Skip to main content

UI Components

Overview

Kumpulan reusable UI components untuk aplikasi frontend. Components ini dirancang untuk modular, customizable, dan mudah diintegrasikan ke dalam berbagai project.

Komponen

Field Dropzone

Advanced file upload component dengan drag-and-drop functionality.

Features:

  • Drag and drop file upload
  • Image compression otomatis
  • Multi-format preview (images, PDF, video)
  • Dual view modes (thumbnail grid & table row)
  • File validation (size, type)
  • Auto-cleanup untuk deleted files
  • Integration dengan file management system

Teknologi: React, React Dropzone, File API

import { FieldDropzoneNew } from '@/components/Field/FieldDropzoneNew'

<FieldDropzoneNew
label="Upload Files"
value={files}
onFilesChange={setFiles}
maxSize={5242880} // 5MB
accept={{
'image/*': ['.png', '.jpg', '.jpeg'],
'application/pdf': ['.pdf']
}}
/>

File Viewer

Component untuk menampilkan berbagai tipe file dengan preview.

Features:

  • Support multiple file types (images, PDF, videos, documents)
  • Lightbox untuk images
  • PDF viewer dengan zoom controls
  • Video player dengan controls
  • Download functionality
  • Responsive design

Teknologi: React, PDF.js, Custom viewers

import { FileViewer } from '@/components/FileViewer'

<FileViewer
fileUrl="/path/to/file.pdf"
fileType="application/pdf"
fileName="document.pdf"
/>

Modal dialog untuk viewing files dengan full-screen support.

Features:

  • Full-screen file preview
  • Navigation untuk multiple files
  • Keyboard shortcuts (ESC to close, arrows to navigate)
  • Download button
  • Close button dan overlay click to close
  • Responsive modal layout

Teknologi: React, Modal component, File Viewer integration

import { ModalFileViewer } from '@/components/ModalFileViewer'

<ModalFileViewer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
files={fileList}
currentIndex={selectedIndex}
onNavigate={setSelectedIndex}
/>

Async Select Components

Advanced async dropdown components untuk data dari API dengan lazy loading dan pagination.

AsyncSingleSelect

Standalone single-select dropdown dengan async loading (tanpa form library).

Features:

  • Async lazy loading dari API
  • Infinite scroll pagination
  • Debounce search (500ms)
  • Clearable option
  • Loading states
  • Controlled component

Use Case: Simple forms, custom form handling, non-RHF projects

import { AsyncSingleSelect } from '@/components/AsyncSingleSelect'

<AsyncSingleSelect
placeholder="Pilih User"
selected={selectedUser}
onChange={setSelectedUser}
loadOptions={loadUsers}
clearable={true}
/>

AsyncSingleSelectRHF

Single-select dropdown dengan React Hook Form integration.

Features:

  • React Hook Form integration
  • Form validation built-in
  • Pagination dengan caching
  • IntersectionObserver untuk smooth scroll
  • TypeScript generics support
  • Value transformation (mapFormToOption/mapOptionToForm)

Use Case: Complex forms dengan validation, RHF projects

import { FormAsyncSingleSelect } from '@/components/AsyncSingleSelectRHF'

<FormAsyncSingleSelect
name="userId"
control={control}
loadOptions={loadUsers}
rules={{ required: "User wajib dipilih" }}
/>

AsyncMultiSelect

Standalone multi-select dropdown dengan chips display (tanpa form library).

Features:

  • Multiple selection dengan chips/badges
  • Async lazy loading
  • Infinite scroll pagination
  • Debounce search (500ms)
  • Removable chips
  • Throttled scroll (300ms)

Use Case: Simple multi-selection, custom forms

import { AsyncMultiSelect } from '@/components/AsyncMultiSelect'

<AsyncMultiSelect
placeholder="Pilih Tags"
selected={selectedTags}
onChange={setSelectedTags}
loadOptions={loadTags}
pageSize={10}
/>

AsyncMultiSelectRHF

Multi-select dropdown dengan React Hook Form integration dan advanced features.

Features:

  • React Hook Form integration
  • Non-removable items control (fixed items)
  • Custom chip styling
  • Form validation
  • Caching mechanism
  • IntersectionObserver scroll

Use Case: Complex multi-selection forms, task assignees, tagging systems

import { FormAsyncMultiSelect } from '@/components/AsyncMultiSelectRHF'

<FormAsyncMultiSelect
name="assignees"
control={control}
loadOptions={loadUsers}
isValueRemovable={(user) => !user.isCurrentUser}
rules={{
validate: (v) => v.length >= 1 || "Min 1 assignee required"
}}
/>

Comparison: Async Select Variants

FeatureAsyncSingleSelectAsyncSingleSelectRHFAsyncMultiSelectAsyncMultiSelectRHF
Form Integration❌ Manual✅ React Hook Form❌ Manual✅ React Hook Form
Validation❌ Manual✅ Built-in❌ Manual✅ Built-in
Selection TypeSingleSingleMultipleMultiple
Chips DisplayN/AN/A✅ Yes✅ Yes
Caching❌ No✅ Yes❌ No✅ Yes
IntersectionObserver❌ No✅ Yes❌ No✅ Yes
Non-Removable ItemsN/AN/A❌ No✅ Yes
Custom Chip StyleN/AN/A❌ Fixed✅ Customizable
TypeScript Generics✅ Good✅ Excellent✅ Good✅ Excellent
Use CaseSimple formsComplex formsSimple multi-selectComplex multi-select

Common Patterns

File Upload Flow

import { useState } from 'react'
import { FieldDropzoneNew } from '@/components/Field/FieldDropzoneNew'
import { ModalFileViewer } from '@/components/ModalFileViewer'

function FileManagement() {
const [files, setFiles] = useState([])
const [viewerOpen, setViewerOpen] = useState(false)
const [selectedFile, setSelectedFile] = useState(0)

const handleFileClick = (index) => {
setSelectedFile(index)
setViewerOpen(true)
}

return (
<>
{/* Upload Section */}
<FieldDropzoneNew
label="Upload Documents"
value={files}
onFilesChange={setFiles}
onFileClick={handleFileClick}
/>

{/* Viewer Modal */}
<ModalFileViewer
isOpen={viewerOpen}
onClose={() => setViewerOpen(false)}
files={files}
currentIndex={selectedFile}
onNavigate={setSelectedFile}
/>
</>
)
}
import { FieldDropzoneNew } from '@/components/Field/FieldDropzoneNew'

function ImageGallery() {
const [images, setImages] = useState([])

return (
<FieldDropzoneNew
label="Photo Gallery"
value={images}
onFilesChange={setImages}
accept={{
'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.webp']
}}
maxSize={10485760} // 10MB
view="thumbnail" // Grid view untuk gallery
compressImage={true} // Enable compression
maxWidth={1920}
maxHeight={1080}
quality={0.8}
/>
)
}

Document Management

import { FieldDropzoneNew } from '@/components/Field/FieldDropzoneNew'

function DocumentManager() {
const [documents, setDocuments] = useState([])

return (
<FieldDropzoneNew
label="Documents"
value={documents}
onFilesChange={setDocuments}
accept={{
'application/pdf': ['.pdf'],
'application/msword': ['.doc'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
'application/vnd.ms-excel': ['.xls'],
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx']
}}
maxSize={52428800} // 50MB
view="row" // Table view untuk documents
multiple={true}
/>
)
}

Styling & Customization

Theme Integration

Components mendukung customization melalui CSS variables:

/* Custom theme colors */
:root {
--dropzone-border: #e2e8f0;
--dropzone-bg: #f8fafc;
--dropzone-hover: #eff6ff;
--dropzone-text: #64748b;
--primary-color: #3b82f6;
}

Custom Styling

<FieldDropzoneNew
className="custom-dropzone"
style={{
borderRadius: '12px',
padding: '24px'
}}
// ... other props
/>

Best Practices

1. File Size Limits

Selalu set file size limits yang reasonable:

// Images: 5-10MB
<FieldDropzoneNew maxSize={10485760} />

// Documents: 50MB
<FieldDropzoneNew maxSize={52428800} />

// Videos: 100MB
<FieldDropzoneNew maxSize={104857600} />

2. Image Compression

Enable compression untuk images:

<FieldDropzoneNew
compressImage={true}
maxWidth={1920}
maxHeight={1080}
quality={0.8} // 80% quality
/>

3. File Type Validation

Specify accepted file types:

<FieldDropzoneNew
accept={{
'image/*': ['.png', '.jpg', '.jpeg'],
'application/pdf': ['.pdf']
}}
/>

4. Error Handling

Handle upload errors properly:

<FieldDropzoneNew
onError={(error) => {
console.error('Upload error:', error)
toast.error('Failed to upload file')
}}
/>

5. Loading States

Show loading indicators:

const [uploading, setUploading] = useState(false)

<FieldDropzoneNew
disabled={uploading}
onUploadStart={() => setUploading(true)}
onUploadEnd={() => setUploading(false)}
/>

Accessibility

Components dibangun dengan accessibility in mind:

  • Keyboard navigation support
  • ARIA labels dan roles
  • Screen reader friendly
  • Focus management
  • Color contrast compliance
<FieldDropzoneNew
aria-label="Upload files"
aria-describedby="upload-instructions"
/>

Performance Tips

1. Lazy Loading

import { lazy, Suspense } from 'react'

const FieldDropzoneNew = lazy(() =>
import('@/components/Field/FieldDropzoneNew')
)

function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<FieldDropzoneNew />
</Suspense>
)
}

2. Memoization

import { useMemo } from 'react'

const memoizedFiles = useMemo(() => {
return files.map(file => ({
...file,
preview: URL.createObjectURL(file)
}))
}, [files])

3. Cleanup

useEffect(() => {
return () => {
// Revoke object URLs to free memory
files.forEach(file => {
if (file.preview) {
URL.revokeObjectURL(file.preview)
}
})
}
}, [files])

Troubleshooting

File Upload Fails

Problem: Files tidak ter-upload

Solutions:

  • Check file size limits
  • Verify file types are accepted
  • Ensure auth token valid
  • Check network connectivity

Preview Not Showing

Problem: File preview tidak muncul

Solutions:

  • Verify file type support
  • Check file URL accessibility
  • Ensure CORS headers configured
  • Check browser console for errors

Memory Leaks

Problem: Memory usage meningkat

Solutions:

  • Revoke object URLs after use
  • Implement proper cleanup in useEffect
  • Limit preview generation
  • Use pagination untuk large file lists
  • Hooks - Custom hooks untuk file handling
  • Utils - Utility functions (ImageCompressor)
  • Code Generation - API client generation

Technology Stack: React, TypeScript, React Dropzone, File API
Use Case: File upload, File management, Document handling
Last Updated: November 2025