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