or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

base-plugins.mdimage.mdindex.mdmedia-embeds.mdreact-components.mdupload-system.md

upload-system.mddocs/

0

# Upload System

1

2

Advanced placeholder-based upload system with file validation, progress tracking, error handling, and drag & drop support. Provides a comprehensive solution for handling file uploads in rich text editing contexts.

3

4

## Capabilities

5

6

### Base Placeholder Plugin

7

8

Core plugin providing placeholder functionality for file uploads with validation and progress tracking.

9

10

```typescript { .api }

11

/**

12

* Base plugin with insert methods for all media types

13

* Provides placeholder elements that can be replaced with actual media

14

*/

15

const BasePlaceholderPlugin: TSlatePlugin<PlaceholderConfig>;

16

17

interface PlaceholderConfig extends MediaPluginOptions {

18

rules?: PlaceholderRule[];

19

}

20

21

interface PlaceholderRule {

22

/** Media type this rule applies to */

23

mediaType: string;

24

}

25

```

26

27

**Usage Example:**

28

29

```typescript

30

import { BasePlaceholderPlugin } from "@udecode/plate-media";

31

import { createSlateEditor } from "@udecode/plate";

32

33

const editor = createSlateEditor({

34

plugins: [

35

BasePlaceholderPlugin.configure({

36

options: {

37

rules: [

38

{ mediaType: 'image' },

39

{ mediaType: 'video' },

40

{ mediaType: 'audio' },

41

{ mediaType: 'file' }

42

]

43

}

44

})

45

]

46

});

47

```

48

49

### Placeholder Insertion

50

51

Functions for inserting placeholder elements of different media types.

52

53

#### Generic Placeholder Insertion

54

55

```typescript { .api }

56

/**

57

* Inserts a placeholder element for the specified media type

58

* @param editor - Slate editor instance

59

* @param mediaType - Type of media placeholder to insert

60

* @param options - Optional insertion configuration

61

*/

62

function insertPlaceholder(

63

editor: SlateEditor,

64

mediaType: string,

65

options?: InsertNodesOptions

66

): void;

67

```

68

69

#### Typed Placeholder Functions

70

71

```typescript { .api }

72

/**

73

* Insert image placeholder element

74

* @param editor - Slate editor instance

75

* @param options - Optional insertion configuration

76

*/

77

function insertImagePlaceholder(

78

editor: SlateEditor,

79

options?: InsertNodesOptions

80

): void;

81

82

/**

83

* Insert video placeholder element

84

* @param editor - Slate editor instance

85

* @param options - Optional insertion configuration

86

*/

87

function insertVideoPlaceholder(

88

editor: SlateEditor,

89

options?: InsertNodesOptions

90

): void;

91

92

/**

93

* Insert audio placeholder element

94

* @param editor - Slate editor instance

95

* @param options - Optional insertion configuration

96

*/

97

function insertAudioPlaceholder(

98

editor: SlateEditor,

99

options?: InsertNodesOptions

100

): void;

101

102

/**

103

* Insert file placeholder element

104

* @param editor - Slate editor instance

105

* @param options - Optional insertion configuration

106

*/

107

function insertFilePlaceholder(

108

editor: SlateEditor,

109

options?: InsertNodesOptions

110

): void;

111

```

112

113

**Usage Examples:**

114

115

```typescript

116

import {

117

insertImagePlaceholder,

118

insertVideoPlaceholder,

119

insertPlaceholder

120

} from "@udecode/plate-media";

121

122

// Insert specific placeholder types

123

insertImagePlaceholder(editor);

124

insertVideoPlaceholder(editor);

125

126

// Insert generic placeholder

127

insertPlaceholder(editor, 'audio');

128

insertPlaceholder(editor, 'file', { at: [0, 0] });

129

```

130

131

### Media Node Updates

132

133

Function for updating placeholder elements with actual media data after upload.

134

135

```typescript { .api }

136

/**

137

* Updates a placeholder or media element with new properties

138

* Used to replace placeholders with actual media after upload completion

139

* @param editor - Slate editor instance

140

* @param props - Media properties to set

141

* @param options - Optional node update configuration

142

*/

143

function setMediaNode(

144

editor: SlateEditor,

145

props: MediaNodeProps,

146

options?: SetNodesOptions

147

): void;

148

149

interface MediaNodeProps {

150

/** Media element type */

151

type: string;

152

/** Media URL */

153

url: string;

154

/** Optional media ID */

155

id?: string;

156

/** Initial height for sizing */

157

initialHeight?: number;

158

/** Initial width for sizing */

159

initialWidth?: number;

160

/** Whether this is an upload operation */

161

isUpload?: boolean;

162

/** File name for downloads */

163

name?: string;

164

/** Placeholder ID to replace */

165

placeholderId?: string;

166

/** Current width for responsive sizing */

167

width?: number;

168

}

169

```

170

171

**Usage Example:**

172

173

```typescript

174

import { setMediaNode, insertImagePlaceholder } from "@udecode/plate-media";

175

176

// Insert placeholder first

177

insertImagePlaceholder(editor);

178

179

// Later, after upload completes

180

const uploadResult = await uploadFile(file);

181

182

setMediaNode(editor, {

183

type: 'image',

184

url: uploadResult.url,

185

name: uploadResult.fileName,

186

initialWidth: uploadResult.width,

187

initialHeight: uploadResult.height,

188

isUpload: true

189

});

190

```

191

192

## React Upload System

193

194

Enhanced React integration with comprehensive file handling, validation, and UI components.

195

196

### Placeholder Plugin

197

198

React-enhanced plugin with file drop, paste handling, and upload management.

199

200

```typescript { .api }

201

/**

202

* Enhanced React plugin with file drop/paste handling and upload management

203

* Provides comprehensive file validation and progress tracking

204

*/

205

const PlaceholderPlugin: TPlatePlugin<PlaceholderConfig>;

206

207

interface PlaceholderConfig extends MediaPluginOptions {

208

/** Disable empty placeholder insertion */

209

disableEmptyPlaceholder?: boolean;

210

/** Disable drag & drop functionality */

211

disableFileDrop?: boolean;

212

/** File type configuration */

213

uploadConfig?: UploadConfig;

214

/** Maximum files allowed globally */

215

maxFileCount?: number;

216

/** Allow multiple file selection */

217

multiple?: boolean;

218

/** Current upload error */

219

error?: UploadError | null;

220

}

221

```

222

223

### File Type Configuration

224

225

Comprehensive file type support with validation and limits.

226

227

#### Upload Configuration

228

229

```typescript { .api }

230

/**

231

* Configuration mapping for different file types

232

*/

233

type UploadConfig = Partial<Record<AllowedFileType, MediaItemConfig>>;

234

235

interface MediaItemConfig {

236

/** Target media type for this file type */

237

mediaType: MediaKeys;

238

/** Maximum files allowed for this type */

239

maxFileCount?: number;

240

/** Maximum file size (e.g., "4MB", "100KB") */

241

maxFileSize?: FileSize;

242

/** Minimum files required */

243

minFileCount?: number;

244

}

245

246

type AllowedFileType = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'blob';

247

type FileSize = `${number}${'B' | 'KB' | 'MB' | 'GB'}`;

248

type MediaKeys = 'image' | 'video' | 'audio' | 'file';

249

```

250

251

#### File Type Constants

252

253

```typescript { .api }

254

/**

255

* Array of all supported file type categories

256

*/

257

const ALLOWED_FILE_TYPES: AllowedFileType[] = [

258

'image',

259

'video',

260

'audio',

261

'pdf',

262

'text',

263

'blob'

264

];

265

```

266

267

**Usage Example:**

268

269

```typescript

270

import { PlaceholderPlugin, type UploadConfig } from "@udecode/plate-media/react";

271

272

const uploadConfig: UploadConfig = {

273

image: {

274

mediaType: 'image',

275

maxFileCount: 10,

276

maxFileSize: '5MB'

277

},

278

video: {

279

mediaType: 'video',

280

maxFileCount: 3,

281

maxFileSize: '50MB'

282

},

283

audio: {

284

mediaType: 'audio',

285

maxFileCount: 5,

286

maxFileSize: '10MB'

287

},

288

pdf: {

289

mediaType: 'file',

290

maxFileCount: 2,

291

maxFileSize: '25MB'

292

}

293

};

294

295

const editor = createPlateEditor({

296

plugins: [

297

PlaceholderPlugin.configure({

298

options: {

299

uploadConfig,

300

maxFileCount: 20,

301

multiple: true,

302

disableFileDrop: false

303

}

304

})

305

]

306

});

307

```

308

309

### Error Handling

310

311

Comprehensive error system for upload validation and user feedback.

312

313

```typescript { .api }

314

/**

315

* Upload error codes enumeration

316

*/

317

enum UploadErrorCode {

318

INVALID_FILE_TYPE = 400,

319

TOO_MANY_FILES = 402,

320

INVALID_FILE_SIZE = 403,

321

TOO_LESS_FILES = 405,

322

TOO_LARGE = 413,

323

}

324

325

/**

326

* Discriminated union of upload error types with detailed data structures

327

*/

328

type UploadError =

329

| {

330

code: UploadErrorCode.INVALID_FILE_TYPE;

331

data: {

332

allowedTypes: string[];

333

files: File[];

334

};

335

}

336

| {

337

code: UploadErrorCode.TOO_MANY_FILES;

338

data: {

339

files: File[];

340

fileType: AllowedFileType | null;

341

maxFileCount: number;

342

};

343

}

344

| {

345

code: UploadErrorCode.INVALID_FILE_SIZE;

346

data: {

347

files: File[];

348

};

349

}

350

| {

351

code: UploadErrorCode.TOO_LESS_FILES;

352

data: {

353

files: File[];

354

fileType: AllowedFileType;

355

minFileCount: number;

356

};

357

}

358

| {

359

code: UploadErrorCode.TOO_LARGE;

360

data: {

361

files: File[];

362

fileType: AllowedFileType;

363

maxFileSize: string;

364

};

365

};

366

367

/**

368

* Creates typed upload errors with detailed error data

369

* @param code - Error code from UploadErrorCode enum

370

* @param data - Error-specific data structure

371

* @returns Formatted upload error

372

*/

373

function createUploadError<T extends UploadErrorCode>(

374

code: T,

375

data: Extract<UploadError, { code: T }>['data']

376

): UploadError;

377

378

/**

379

* Type guard to check if an unknown value is an UploadError

380

* @param error - Value to check

381

* @returns Boolean indicating if value is UploadError

382

*/

383

function isUploadError(error: unknown): error is UploadError;

384

```

385

386

**Usage Example:**

387

388

```typescript

389

import {

390

createUploadError,

391

isUploadError,

392

UploadErrorCode,

393

type UploadError

394

} from "@udecode/plate-media/react";

395

396

const handleUploadError = (error: UploadError) => {

397

switch (error.code) {

398

case UploadErrorCode.INVALID_FILE_TYPE:

399

console.error(`Invalid file types. Allowed: ${error.data.allowedTypes.join(', ')}`);

400

console.error(`Invalid files:`, error.data.files.map(f => f.name));

401

break;

402

case UploadErrorCode.TOO_MANY_FILES:

403

console.error(`Too many files. Maximum allowed: ${error.data.maxFileCount}`);

404

console.error(`File type: ${error.data.fileType}`);

405

break;

406

case UploadErrorCode.INVALID_FILE_SIZE:

407

console.error(`Invalid file size for files:`, error.data.files.map(f => f.name));

408

break;

409

case UploadErrorCode.TOO_LESS_FILES:

410

console.error(`Not enough ${error.data.fileType} files. Minimum required: ${error.data.minFileCount}`);

411

console.error(`Current files:`, error.data.files.map(f => f.name));

412

break;

413

case UploadErrorCode.TOO_LARGE:

414

console.error(`Files too large for ${error.data.fileType}. Max size: ${error.data.maxFileSize}`);

415

console.error(`Large files:`, error.data.files.map(f => f.name));

416

break;

417

}

418

};

419

```

420

421

### File Validation

422

423

Comprehensive file validation system with type detection and size checking.

424

425

#### Validation Functions

426

427

```typescript { .api }

428

/**

429

* Validates file selection against upload configuration

430

* @param files - FileList to validate

431

* @param config - Upload configuration

432

* @returns Validation result with errors if any

433

*/

434

function validateFiles(

435

files: FileList,

436

config: UploadConfig

437

): { isValid: boolean; error?: UploadError };

438

439

/**

440

* Validates individual file against configuration

441

* @param file - File to validate

442

* @param config - Media item configuration

443

* @returns Validation result

444

*/

445

function validateFileItem(

446

file: File,

447

config: MediaItemConfig

448

): { isValid: boolean; error?: UploadError };

449

450

/**

451

* Determines file type category from File object

452

* @param file - File to categorize

453

* @returns File type category or undefined if not supported

454

*/

455

function matchFileType(file: File): AllowedFileType | undefined;

456

457

/**

458

* Groups files by their detected media type

459

* @param files - Array of files to group

460

* @returns Object mapping media types to file arrays

461

*/

462

function groupFilesByType(files: File[]): Record<string, File[]>;

463

```

464

465

#### Utility Functions

466

467

```typescript { .api }

468

/**

469

* Converts file size string to bytes

470

* @param size - Size string like "4MB", "100KB"

471

* @returns Size in bytes

472

*/

473

function fileSizeToBytes(size: FileSize): number;

474

475

/**

476

* Determines appropriate media type for a file

477

* @param file - File to analyze

478

* @returns Corresponding media type

479

*/

480

function getMediaType(file: File): MediaKeys;

481

```

482

483

**Usage Examples:**

484

485

```typescript

486

import {

487

validateFiles,

488

matchFileType,

489

fileSizeToBytes,

490

groupFilesByType

491

} from "@udecode/plate-media/react";

492

493

// Validate file selection

494

const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {

495

if (event.target.files) {

496

const validation = validateFiles(event.target.files, uploadConfig);

497

498

if (!validation.isValid && validation.error) {

499

setError(validation.error);

500

return;

501

}

502

503

// Process valid files

504

processFiles(event.target.files);

505

}

506

};

507

508

// Check file types

509

const checkFileType = (file: File) => {

510

const fileType = matchFileType(file);

511

console.log(`File type: ${fileType}`); // "image", "video", etc.

512

};

513

514

// Convert file sizes

515

const maxSizeBytes = fileSizeToBytes("5MB"); // 5242880

516

517

// Group files by type

518

const files = Array.from(fileList);

519

const grouped = groupFilesByType(files);

520

// { image: [file1, file2], video: [file3], audio: [file4] }

521

```

522

523

### State Management

524

525

Upload progress and state tracking with React integration.

526

527

#### Placeholder Store

528

529

```typescript { .api }

530

/**

531

* Atom store for upload state management

532

*/

533

interface PlaceholderStore {

534

/** Upload operation in progress */

535

isUploading: boolean;

536

/** Progress by placeholder ID */

537

progresses: Record<string, number>;

538

/** Size constraints for uploads */

539

size: { width: number; height: number } | null;

540

/** Files currently being processed */

541

updatedFiles: File[];

542

}

543

544

/**

545

* Store access hooks

546

*/

547

function usePlaceholderStore(): PlaceholderStore;

548

function usePlaceholderValue<T>(selector: (store: PlaceholderStore) => T): T;

549

function usePlaceholderSet(): (updates: Partial<PlaceholderStore>) => void;

550

```

551

552

#### API Extensions

553

554

```typescript { .api }

555

/**

556

* Extended API for file tracking during uploads

557

*/

558

interface PlaceholderApi {

559

/** Add file to upload tracking */

560

addUploadingFile(id: string, file: File): void;

561

/** Retrieve uploading file by ID */

562

getUploadingFile(id: string): File | undefined;

563

/** Remove file from tracking */

564

removeUploadingFile(id: string): void;

565

}

566

567

/**

568

* Extended transforms for media insertion

569

*/

570

interface PlaceholderTransforms {

571

/** Insert media from file list */

572

insertMedia(files: FileList, options?: InsertNodesOptions): void;

573

}

574

```

575

576

### React Hooks

577

578

Specialized hooks for placeholder and upload functionality.

579

580

```typescript { .api }

581

/**

582

* Provides placeholder element state and handlers

583

* @returns Placeholder element props and interaction handlers

584

*/

585

function usePlaceholderElement(): {

586

element: TPlaceholderElement;

587

isUploading: boolean;

588

progress: number;

589

error: UploadError | null;

590

};

591

592

/**

593

* Manages placeholder popover interactions

594

* @returns Popover state and control functions

595

*/

596

function usePlaceholderPopover(): {

597

isOpen: boolean;

598

open: () => void;

599

close: () => void;

600

toggle: () => void;

601

};

602

```

603

604

**Usage Example:**

605

606

```typescript

607

import {

608

usePlaceholderElement,

609

usePlaceholderPopover

610

} from "@udecode/plate-media/react";

611

612

const PlaceholderComponent = () => {

613

const { element, isUploading, progress, error } = usePlaceholderElement();

614

const popover = usePlaceholderPopover();

615

616

return (

617

<div>

618

{isUploading && (

619

<div>

620

<div>Uploading... {Math.round(progress * 100)}%</div>

621

<progress value={progress} max={1} />

622

</div>

623

)}

624

625

{error && (

626

<div style={{ color: 'red' }}>

627

Error: {error.message}

628

</div>

629

)}

630

631

<button onClick={popover.toggle}>

632

{popover.isOpen ? 'Close' : 'Open'} Upload Options

633

</button>

634

635

{popover.isOpen && (

636

<div>

637

{/* Upload options UI */}

638

</div>

639

)}

640

</div>

641

);

642

};

643

```

644

645

## MIME Type Support

646

647

Comprehensive MIME type definitions organized by category for accurate file type detection.

648

649

### MIME Type Categories

650

651

The upload system supports extensive MIME type mappings:

652

653

- **Image**: JPG, PNG, GIF, SVG, WebP, HEIC, AVIF, and many RAW formats

654

- **Video**: MP4, WebM, AVI, MOV, MKV, and other common video formats

655

- **Audio**: MP3, WAV, FLAC, AAC, OGG, and professional audio formats

656

- **PDF**: Application/PDF documents

657

- **Text**: Plain text, markdown, CSV, and code files

658

- **Blob**: Generic file support for other formats

659

660

File type detection uses both MIME type and file extension for maximum compatibility across different browsers and systems.