or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-apis.mddevice.mddom-events.mdindex.mdnavigation.mdnetwork.mdobservers.mdspecialized.mdstate-management.mdstorage.mdtiming.mdutilities.md

specialized.mddocs/

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

```