@scaleflex/asset-picker 0.2.11 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,6 +44,8 @@
44
44
  - [Public Methods](#public-methods)
45
45
  - [Events](#events)
46
46
  - [React API](#react-api)
47
+ - [Provider + Hook](#provider--hook-recommended)
48
+ - [Asset Utilities](#asset-utilities)
47
49
  - [Theming](#theming)
48
50
  - [Brand Color](#brand-color)
49
51
  - [CSS Custom Properties](#css-custom-properties)
@@ -112,8 +114,8 @@ pnpm add @scaleflex/asset-picker
112
114
 
113
115
  | Export path | Description |
114
116
  |---|---|
115
- | `@scaleflex/asset-picker` | `AssetPicker` class + all TypeScript types |
116
- | `@scaleflex/asset-picker/react` | React wrapper component |
117
+ | `@scaleflex/asset-picker` | `AssetPicker` class + all TypeScript types + asset utility functions |
118
+ | `@scaleflex/asset-picker/react` | React wrapper component + `AssetPickerProvider` + `useAssetPicker` hook |
117
119
  | `@scaleflex/asset-picker/define` | Side-effect import — registers `<sfx-asset-picker>` custom element |
118
120
 
119
121
  Both ESM (`import`) and CJS (`require`) builds are provided.
@@ -248,7 +250,13 @@ Use when your application already has a SASS key — e.g. inside the Scaleflex H
248
250
  | `rememberLastTab` | `boolean` | `false` | Persist the last active tab (assets/folders) and restore on next open |
249
251
  | `defaultFilters` | `FiltersInput` | `undefined` | Filters pre-applied on open. User can modify/remove |
250
252
  | `forcedFilters` | `FiltersInput` | `undefined` | Filters always active. Shown as locked chips the user cannot remove |
251
- | `onSelect` | `(assets: Asset[]) => void` | `undefined` | Callback when assets are selected |
253
+ | `displayMode` | `'modal' \| 'inline'` | `'modal'` | `'modal'` renders as a dialog overlay, `'inline'` renders in page flow |
254
+ | `gridSize` | `'normal' \| 'large'` | `'normal'` | Grid card density: `'normal'` (4 cols at ~1200px) or `'large'` (3 cols) |
255
+ | `stickyFilters` | `boolean` | `false` | Make the toolbar and filters bar sticky while scrolling content |
256
+ | `folderSelection` | `boolean` | `true` | Allow selecting folders via checkboxes |
257
+ | `folderSelectionMode` | `'folder' \| 'assets'` | `'folder'` | `'folder'` returns Folder objects; `'assets'` fetches folder contents and returns only Assets |
258
+ | `uploader` | `UploaderIntegrationConfig` | `undefined` | Enable integrated uploader. Adds an "Upload" button and drop zone. Requires `@scaleflex/uploader` |
259
+ | `onSelect` | `(assets: Asset[], folders?: Folder[]) => void` | `undefined` | Callback when assets are selected |
252
260
  | `onCancel` | `() => void` | `undefined` | Callback when the picker is cancelled |
253
261
 
254
262
  #### Sort fields
@@ -327,7 +335,7 @@ All events bubble and cross shadow DOM boundaries (`composed: true`).
327
335
  | Event | Detail | Description |
328
336
  |---|---|---|
329
337
  | `ap-select` | `{ assets: Asset[] }` | Fired when the user confirms their selection |
330
- | `ap-cancel` | `{ reason: 'backdrop' \| 'escape' \| 'button' \| 'close-button' }` | Fired when the picker is closed without selecting |
338
+ | `ap-cancel` | `{ reason: 'backdrop' \| 'escape' \| 'button' }` | Fired when the picker is closed without selecting |
331
339
  | `ap-open` | `{ timestamp: number }` | Fired when the picker opens successfully |
332
340
  | `ap-error` | `{ error: Error, context: string }` | Fired on initialisation or runtime errors |
333
341
 
@@ -362,7 +370,8 @@ import { AssetPicker, type AssetPickerRef, type AssetPickerProps } from '@scalef
362
370
  |---|---|---|
363
371
  | `config` | `AssetPickerConfig` | Configuration object (see [Config Options](#config-options)) |
364
372
  | `open` | `boolean` | Controlled open state |
365
- | `onSelect` | `(assets: Asset[]) => void` | Selection callback |
373
+ | `onSelect` | `(assets: Asset[], folders?: Folder[]) => void` | Selection callback (assets + optional folders) |
374
+ | `onSelectWithFolders` | `(result: { assets: Asset[]; folders: Folder[] }) => void` | Alternative callback that always includes folders |
366
375
  | `onCancel` | `() => void` | Cancel callback |
367
376
  | `className` | `string` | CSS class for the wrapper |
368
377
  | `style` | `CSSProperties` | Inline styles for the wrapper |
@@ -399,6 +408,163 @@ const ref = useRef<AssetPickerRef>(null);
399
408
  <AssetPicker ref={ref} config={config} onSelect={handleSelect} />
400
409
  ```
401
410
 
411
+ ### Provider + Hook (recommended)
412
+
413
+ For apps that open the picker from many places, use `AssetPickerProvider` + `useAssetPicker()` to avoid managing open/close state yourself. One picker instance is mounted at the root and shared across the tree.
414
+
415
+ #### Setup
416
+
417
+ ```tsx
418
+ import { AssetPickerProvider } from '@scaleflex/asset-picker/react';
419
+
420
+ function App() {
421
+ return (
422
+ <AssetPickerProvider
423
+ config={{
424
+ auth: {
425
+ mode: 'sassKey',
426
+ sassKey: 'YOUR_SASS_KEY',
427
+ projectToken: 'YOUR_TOKEN',
428
+ },
429
+ }}
430
+ >
431
+ <Dashboard />
432
+ </AssetPickerProvider>
433
+ );
434
+ }
435
+ ```
436
+
437
+ #### Promise mode
438
+
439
+ ```tsx
440
+ import { useAssetPicker } from '@scaleflex/asset-picker/react';
441
+
442
+ function ImageSelector() {
443
+ const picker = useAssetPicker();
444
+
445
+ const handleClick = async () => {
446
+ try {
447
+ const assets = await picker.open({ multiSelect: true });
448
+ console.log('Selected:', assets);
449
+ } catch {
450
+ console.log('User cancelled');
451
+ }
452
+ };
453
+
454
+ return <button onClick={handleClick}>Choose images</button>;
455
+ }
456
+ ```
457
+
458
+ The promise resolves with the selected `Asset[]` on confirm, and rejects with `'cancelled'` when the user closes without selecting. Note: promise mode returns only assets — use callback mode with `onSelect(assets, folders)` if you need folder data.
459
+
460
+ #### Callback mode
461
+
462
+ ```tsx
463
+ function VideoSelector() {
464
+ const picker = useAssetPicker();
465
+
466
+ return (
467
+ <button
468
+ onClick={() =>
469
+ picker.open({
470
+ forcedFilters: { type: { values: ['video'] } },
471
+ onSelect: (assets) => console.log(assets),
472
+ onCancel: () => console.log('Cancelled'),
473
+ })
474
+ }
475
+ >
476
+ Choose video
477
+ </button>
478
+ );
479
+ }
480
+ ```
481
+
482
+ #### Config overrides
483
+
484
+ Any `AssetPickerConfig` property passed to `open()` is merged with (and overrides) the base config from the provider:
485
+
486
+ ```tsx
487
+ // Base config has multiSelect: false
488
+ // This call overrides it to true and adds a forced filter
489
+ const assets = await picker.open({
490
+ multiSelect: true,
491
+ forcedFilters: { type: { values: ['image'] } },
492
+ });
493
+ ```
494
+
495
+ #### Hook return type
496
+
497
+ ```ts
498
+ interface UseAssetPickerReturn {
499
+ open(overrides?: OpenOptions): Promise<Asset[]>;
500
+ close(): void;
501
+ isOpen: boolean;
502
+ }
503
+ ```
504
+
505
+ ---
506
+
507
+ ## Asset Utilities
508
+
509
+ Pure helper functions for working with `Asset` objects. Exported from the main entry point — no React required.
510
+
511
+ ```ts
512
+ import {
513
+ getAltText,
514
+ getCdnUrl,
515
+ getAssetWidth,
516
+ getAssetHeight,
517
+ getAssetDimensions,
518
+ isTranscoded,
519
+ getTranscodedUrl,
520
+ getBestVideoUrl,
521
+ isVideo,
522
+ isImage,
523
+ isAudio,
524
+ } from '@scaleflex/asset-picker';
525
+ ```
526
+
527
+ ### Type checks
528
+
529
+ | Function | Returns | Description |
530
+ |---|---|---|
531
+ | `isImage(asset)` | `boolean` | `true` if `asset.type` starts with `"image"` |
532
+ | `isVideo(asset)` | `boolean` | `true` if `asset.type` starts with `"video"` |
533
+ | `isAudio(asset)` | `boolean` | `true` if `asset.type` starts with `"audio"` |
534
+
535
+ ### URLs
536
+
537
+ | Function | Returns | Description |
538
+ |---|---|---|
539
+ | `getCdnUrl(asset)` | `string` | CDN URL, falling back to public URL, then `""` |
540
+ | `getBestVideoUrl(asset)` | `string` | Transcoded HLS URL > CDN URL > public URL |
541
+ | `getTranscodedUrl(asset)` | `string \| null` | HLS manifest URL, or `null` if not transcoded |
542
+
543
+ ### Alt text
544
+
545
+ ```ts
546
+ const alt = getAltText(asset); // uses first available language
547
+ const alt = getAltText(asset, 'fr'); // prefers French title
548
+ ```
549
+
550
+ Resolution priority: `meta.alt` > `meta.title` (string or localized `Record<string, string>`) > filename without extension.
551
+
552
+ ### Dimensions
553
+
554
+ | Function | Returns | Description |
555
+ |---|---|---|
556
+ | `getAssetWidth(asset)` | `number` | Width in px (`0` if unknown). Works for images and videos. |
557
+ | `getAssetHeight(asset)` | `number` | Height in px (`0` if unknown). Works for images and videos. |
558
+ | `getAssetDimensions(asset)` | `{ width, height }` | Both dimensions as an object. |
559
+
560
+ ### Video transcoding
561
+
562
+ | Function | Returns | Description |
563
+ |---|---|---|
564
+ | `isTranscoded(asset)` | `boolean` | Whether the asset has a transcoded HLS version |
565
+ | `getTranscodedUrl(asset)` | `string \| null` | The HLS manifest URL, or `null` |
566
+ | `getBestVideoUrl(asset)` | `string` | Best playback URL (transcoded > CDN > public) |
567
+
402
568
  ---
403
569
 
404
570
  ## Theming
@@ -635,24 +801,36 @@ interface Asset {
635
801
  };
636
802
  created_at: string; // ISO timestamp
637
803
  modified_at: string; // ISO timestamp
638
- tags: Record<string, any>;
804
+ tags: Record<string, Array<{ label: string; sid: string }>>
805
+ | Record<string, { label: string; sid: string }>
806
+ | string[];
639
807
  labels: string[];
640
808
  meta: {
641
- title?: string;
809
+ title?: string | Record<string, string>; // plain or localized by language code
642
810
  description?: string;
643
811
  alt?: string;
644
812
  [key: string]: unknown;
645
813
  };
646
814
  info: {
815
+ img_type?: string; // Image format (e.g. "jpeg", "png")
647
816
  img_w?: number; // Image width (px)
648
817
  img_h?: number; // Image height (px)
649
- duration?: number; // Video/audio duration (seconds)
818
+ duration?: number; // Audio duration (seconds)
819
+ video_duration?: number; // Video duration (seconds)
650
820
  video_w?: number; // Video width (px)
651
821
  video_h?: number; // Video height (px)
652
822
  thumbnail?: string; // Thumbnail URL
653
- main_colors_hex?: string[]; // Dominant colours
654
- dominant_color_hex?: string; // Most dominant colour
655
- [key: string]: unknown;
823
+ preview?: string; // Preview URL
824
+ video_thumbnail?: string; // Video poster image URL
825
+ video_gif?: string; // Animated GIF preview URL
826
+ image_thumbnail?: string; // Image thumbnail URL
827
+ main_colors?: string[]; // Dominant colours (names)
828
+ main_colors_hex?: string[]; // Dominant colours (hex)
829
+ dominant_color?: string; // Most dominant colour (name)
830
+ dominant_color_hex?: string; // Most dominant colour (hex)
831
+ color_space?: string; // Colour space (e.g. "sRGB")
832
+ metadata?: Record<string, unknown>; // Embedded metadata (EXIF, IPTC, etc.)
833
+ playlists?: Array<{ playlists: string[]; resolution?: string }>; // HLS transcoded playlists
656
834
  };
657
835
  folder?: {
658
836
  uuid: string;
@@ -673,8 +851,10 @@ interface Folder {
673
851
  uuid: string;
674
852
  name: string;
675
853
  path: string;
854
+ owner?: string;
676
855
  created_at: string;
677
856
  modified_at?: string;
857
+ updated_at?: string;
678
858
  count?: {
679
859
  files_recursive?: number;
680
860
  files_direct?: number;
@@ -682,6 +862,10 @@ interface Folder {
682
862
  size?: {
683
863
  total_recursive_bytes?: number;
684
864
  };
865
+ visibility?: {
866
+ in_cdn?: { actual: string; set: string };
867
+ in_dam?: { actual: string; set: string };
868
+ };
685
869
  }
686
870
  ```
687
871