0
# Specialized
1
2
Advanced and specialized hooks for unique UI patterns including color picker integration, file dialogs, selection management, and radial interactions.
3
4
## Capabilities
5
6
### useEyeDropper
7
8
EyeDropper API integration for color picking from the screen.
9
10
```typescript { .api }
11
/**
12
* EyeDropper API integration for color picking
13
* @returns Object with support status and open function
14
*/
15
function useEyeDropper(): UseEyeDropperReturnValue;
16
17
interface EyeDropperOpenOptions {
18
signal?: AbortSignal;
19
}
20
21
interface EyeDropperOpenReturnType {
22
sRGBHex: string;
23
}
24
25
interface UseEyeDropperReturnValue {
26
supported: boolean;
27
open: (options?: EyeDropperOpenOptions) => Promise<EyeDropperOpenReturnType>;
28
}
29
```
30
31
**Usage Examples:**
32
33
```typescript
34
import { useEyeDropper } from "@mantine/hooks";
35
36
function ColorPicker() {
37
const [color, setColor] = useState('#000000');
38
const eyeDropper = useEyeDropper();
39
40
const pickColor = async () => {
41
if (!eyeDropper.supported) {
42
alert('EyeDropper API not supported');
43
return;
44
}
45
46
try {
47
const result = await eyeDropper.open();
48
setColor(result.sRGBHex);
49
} catch (error) {
50
console.log('Color picking cancelled');
51
}
52
};
53
54
return (
55
<div>
56
<div style={{ width: 50, height: 50, background: color }} />
57
<button onClick={pickColor} disabled={!eyeDropper.supported}>
58
Pick Color from Screen
59
</button>
60
</div>
61
);
62
}
63
```
64
65
### useFileDialog
66
67
File input dialog with configurable options and callback handling.
68
69
```typescript { .api }
70
/**
71
* File input dialog with options
72
* @param options - Configuration for file selection
73
* @returns Object with open and reset functions
74
*/
75
function useFileDialog(options: UseFileDialogOptions): UseFileDialogReturnValue;
76
77
interface UseFileDialogOptions {
78
multiple?: boolean;
79
accept?: string;
80
capture?: boolean | 'user' | 'environment';
81
onFiles: (files: FileList | null) => void;
82
}
83
84
interface UseFileDialogReturnValue {
85
open: () => void;
86
reset: () => void;
87
}
88
```
89
90
**Usage Examples:**
91
92
```typescript
93
import { useFileDialog } from "@mantine/hooks";
94
95
function ImageUploader() {
96
const [images, setImages] = useState<File[]>([]);
97
98
const fileDialog = useFileDialog({
99
multiple: true,
100
accept: 'image/*',
101
onFiles: (files) => {
102
if (files) {
103
setImages(Array.from(files));
104
}
105
}
106
});
107
108
return (
109
<div>
110
<button onClick={fileDialog.open}>
111
Select Images
112
</button>
113
<button onClick={fileDialog.reset}>
114
Reset
115
</button>
116
<div>
117
{images.map((file, index) => (
118
<div key={index}>{file.name}</div>
119
))}
120
</div>
121
</div>
122
);
123
}
124
125
// Camera capture
126
function CameraCapture() {
127
const cameraDialog = useFileDialog({
128
accept: 'image/*',
129
capture: 'environment', // Use back camera
130
onFiles: (files) => {
131
if (files && files[0]) {
132
handlePhotoCapture(files[0]);
133
}
134
}
135
});
136
137
return <button onClick={cameraDialog.open}>Take Photo</button>;
138
}
139
```
140
141
### useSelection
142
143
Multi-select state management with comprehensive selection handlers.
144
145
```typescript { .api }
146
/**
147
* Multi-select state management
148
* @param items - Array of items to manage selection for
149
* @param input - Configuration for selection behavior
150
* @returns Object with selected items, handlers, and utilities
151
*/
152
function useSelection<T>(
153
items: T[],
154
input?: UseSelectionInput<T>
155
): UseSelectionReturnValue<T>;
156
157
interface UseSelectionInput<T> {
158
multiple?: boolean;
159
value?: T | T[];
160
onSelectionChange?: (value: T | T[]) => void;
161
}
162
163
interface UseSelectionHandlers<T> {
164
select: (item: T) => void;
165
deselect: (item: T) => void;
166
toggle: (item: T) => void;
167
selectAll: () => void;
168
deselectAll: () => void;
169
setSelection: (items: T[]) => void;
170
}
171
172
interface UseSelectionReturnValue<T> {
173
selected: T[];
174
handlers: UseSelectionHandlers<T>;
175
isSelected: (item: T) => boolean;
176
}
177
```
178
179
**Usage Examples:**
180
181
```typescript
182
import { useSelection } from "@mantine/hooks";
183
184
function TodoList() {
185
const todos = [
186
{ id: 1, text: 'Learn React' },
187
{ id: 2, text: 'Build app' },
188
{ id: 3, text: 'Deploy' }
189
];
190
191
const selection = useSelection(todos, {
192
multiple: true,
193
onSelectionChange: (selected) => {
194
console.log('Selected todos:', selected);
195
}
196
});
197
198
const deleteSelected = () => {
199
// Delete selected todos
200
const selectedIds = selection.selected.map(todo => todo.id);
201
deleteTodos(selectedIds);
202
selection.deselectAll();
203
};
204
205
return (
206
<div>
207
<div>
208
<button onClick={selection.handlers.selectAll}>
209
Select All
210
</button>
211
<button onClick={selection.handlers.deselectAll}>
212
Deselect All
213
</button>
214
<button onClick={deleteSelected} disabled={selection.selected.length === 0}>
215
Delete Selected ({selection.selected.length})
216
</button>
217
</div>
218
219
{todos.map(todo => (
220
<div key={todo.id}>
221
<input
222
type="checkbox"
223
checked={selection.isSelected(todo)}
224
onChange={() => selection.handlers.toggle(todo)}
225
/>
226
{todo.text}
227
</div>
228
))}
229
</div>
230
);
231
}
232
```
233
234
### useRadialMove
235
236
Circular/radial mouse interactions for knobs, dials, and circular controls.
237
238
```typescript { .api }
239
/**
240
* Circular/radial mouse interactions
241
* @param options - Configuration for radial movement
242
* @returns Object with ref callback
243
*/
244
function useRadialMove<T extends HTMLElement = any>(
245
options?: UseRadialMoveOptions
246
): UseRadialMoveReturnValue<T>;
247
248
/**
249
* Normalize radial value to 0-1 range
250
* @param value - Value to normalize
251
* @returns Normalized value
252
*/
253
function normalizeRadialValue(value: number): number;
254
255
interface UseRadialMoveOptions {
256
onValueChange?: (value: number) => void;
257
onScrubStart?: () => void;
258
onScrubEnd?: () => void;
259
step?: number;
260
max?: number;
261
min?: number;
262
}
263
264
interface UseRadialMoveReturnValue<T extends HTMLElement = any> {
265
ref: React.RefCallback<T | null>;
266
}
267
```
268
269
**Usage Examples:**
270
271
```typescript
272
import { useRadialMove, normalizeRadialValue } from "@mantine/hooks";
273
274
function VolumeKnob() {
275
const [volume, setVolume] = useState(0.5);
276
277
const { ref } = useRadialMove({
278
onValueChange: (value) => {
279
const normalized = normalizeRadialValue(value);
280
setVolume(normalized);
281
},
282
min: 0,
283
max: 1,
284
step: 0.01
285
});
286
287
return (
288
<div
289
ref={ref}
290
style={{
291
width: 100,
292
height: 100,
293
borderRadius: '50%',
294
border: '2px solid #ccc',
295
position: 'relative',
296
cursor: 'pointer'
297
}}
298
>
299
<div
300
style={{
301
position: 'absolute',
302
top: '50%',
303
left: '50%',
304
width: 4,
305
height: 40,
306
background: '#000',
307
transformOrigin: 'center bottom',
308
transform: `translate(-50%, -100%) rotate(${volume * 270 - 135}deg)`
309
}}
310
/>
311
<div>Volume: {Math.round(volume * 100)}%</div>
312
</div>
313
);
314
}
315
```
316
317
### useMouse
318
319
Track mouse position relative to an element.
320
321
```typescript { .api }
322
/**
323
* Track mouse position relative to element
324
* @returns Object with ref and mouse coordinates
325
*/
326
function useMouse<T extends HTMLElement = any>(): {
327
ref: React.RefCallback<T | null>;
328
x: number;
329
y: number;
330
};
331
```
332
333
### useTextSelection
334
335
Track current text selection in the document.
336
337
```typescript { .api }
338
/**
339
* Track current text selection
340
* @returns Current Selection object or null
341
*/
342
function useTextSelection(): Selection | null;
343
```
344
345
### useFocusReturn
346
347
Return focus to previously focused element after modal/dialog closes.
348
349
```typescript { .api }
350
/**
351
* Return focus to previous element
352
* @param options - Configuration for focus return
353
* @returns Object with returnFocus function
354
*/
355
function useFocusReturn(options?: UseFocusReturnOptions): UseFocusReturnReturnValue;
356
357
interface UseFocusReturnOptions {
358
opened: boolean;
359
shouldReturnFocus?: boolean;
360
}
361
362
interface UseFocusReturnReturnValue {
363
returnFocus: () => void;
364
}
365
```
366
367
### useLogger
368
369
Development logging utility for debugging hook state changes.
370
371
```typescript { .api }
372
/**
373
* Development logging utility
374
* @param name - Name for the logger
375
* @param props - Object to log when it changes
376
*/
377
function useLogger(name: string, props: Record<string, any>): void;
378
```
379
380
**Usage Examples:**
381
382
```typescript
383
import { useLogger } from "@mantine/hooks";
384
385
function DebugComponent({ userId, settings }: Props) {
386
const [count, setCount] = useState(0);
387
388
// Log prop and state changes in development
389
useLogger('DebugComponent', { userId, settings, count });
390
391
return (
392
<div>
393
<button onClick={() => setCount(c => c + 1)}>
394
Count: {count}
395
</button>
396
</div>
397
);
398
}
399
```
400
401
## Specialized Patterns
402
403
### Custom File Upload
404
405
```typescript
406
import { useFileDialog, useClipboard } from "@mantine/hooks";
407
408
function AdvancedFileUploader() {
409
const [files, setFiles] = useState<File[]>([]);
410
const [uploadProgress, setUploadProgress] = useState<Record<string, number>>({});
411
const clipboard = useClipboard();
412
413
const imageDialog = useFileDialog({
414
multiple: true,
415
accept: 'image/*',
416
onFiles: (fileList) => {
417
if (fileList) {
418
const newFiles = Array.from(fileList);
419
setFiles(prev => [...prev, ...newFiles]);
420
421
// Start upload for each file
422
newFiles.forEach(uploadFile);
423
}
424
}
425
});
426
427
const uploadFile = async (file: File) => {
428
const formData = new FormData();
429
formData.append('file', file);
430
431
try {
432
const response = await fetch('/upload', {
433
method: 'POST',
434
body: formData
435
});
436
437
if (response.ok) {
438
const result = await response.json();
439
clipboard.copy(result.url);
440
}
441
} catch (error) {
442
console.error('Upload failed:', error);
443
}
444
};
445
446
return (
447
<div>
448
<button onClick={imageDialog.open}>
449
Select Images
450
</button>
451
<div>
452
{files.map((file, index) => (
453
<div key={index}>
454
{file.name}
455
{uploadProgress[file.name] && (
456
<div>Progress: {uploadProgress[file.name]}%</div>
457
)}
458
</div>
459
))}
460
</div>
461
</div>
462
);
463
}
464
```