@scaleflex/asset-picker 0.1.0

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.
Files changed (180) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/LICENSE +50 -0
  3. package/README.md +702 -0
  4. package/dist/asset-picker-BA876lMW.js +11444 -0
  5. package/dist/asset-picker-BbEOZw7O.cjs +5865 -0
  6. package/dist/asset-picker.d.ts +77 -0
  7. package/dist/asset-picker.d.ts.map +1 -0
  8. package/dist/components/filters/ap-filter-approval.d.ts +43 -0
  9. package/dist/components/filters/ap-filter-approval.d.ts.map +1 -0
  10. package/dist/components/filters/ap-filter-color.d.ts +31 -0
  11. package/dist/components/filters/ap-filter-color.d.ts.map +1 -0
  12. package/dist/components/filters/ap-filter-date.d.ts +32 -0
  13. package/dist/components/filters/ap-filter-date.d.ts.map +1 -0
  14. package/dist/components/filters/ap-filter-image.d.ts +21 -0
  15. package/dist/components/filters/ap-filter-image.d.ts.map +1 -0
  16. package/dist/components/filters/ap-filter-labels.d.ts +26 -0
  17. package/dist/components/filters/ap-filter-labels.d.ts.map +1 -0
  18. package/dist/components/filters/ap-filter-metadata.d.ts +93 -0
  19. package/dist/components/filters/ap-filter-metadata.d.ts.map +1 -0
  20. package/dist/components/filters/ap-filter-popover.d.ts +13 -0
  21. package/dist/components/filters/ap-filter-popover.d.ts.map +1 -0
  22. package/dist/components/filters/ap-filter-product-ref.d.ts +38 -0
  23. package/dist/components/filters/ap-filter-product-ref.d.ts.map +1 -0
  24. package/dist/components/filters/ap-filter-size.d.ts +24 -0
  25. package/dist/components/filters/ap-filter-size.d.ts.map +1 -0
  26. package/dist/components/filters/ap-filter-tags.d.ts +31 -0
  27. package/dist/components/filters/ap-filter-tags.d.ts.map +1 -0
  28. package/dist/components/filters/ap-filter-type.d.ts +13 -0
  29. package/dist/components/filters/ap-filter-type.d.ts.map +1 -0
  30. package/dist/components/filters/ap-filters-bar.d.ts +40 -0
  31. package/dist/components/filters/ap-filters-bar.d.ts.map +1 -0
  32. package/dist/components/filters/filters.constants.d.ts +189 -0
  33. package/dist/components/filters/filters.constants.d.ts.map +1 -0
  34. package/dist/components/filters/shared/filter-styles.d.ts +10 -0
  35. package/dist/components/filters/shared/filter-styles.d.ts.map +1 -0
  36. package/dist/components/header/ap-header.d.ts +30 -0
  37. package/dist/components/header/ap-header.d.ts.map +1 -0
  38. package/dist/components/header/ap-regional-settings.d.ts +19 -0
  39. package/dist/components/header/ap-regional-settings.d.ts.map +1 -0
  40. package/dist/components/modal/ap-modal.d.ts +17 -0
  41. package/dist/components/modal/ap-modal.d.ts.map +1 -0
  42. package/dist/components/navigation/ap-breadcrumb.d.ts +14 -0
  43. package/dist/components/navigation/ap-breadcrumb.d.ts.map +1 -0
  44. package/dist/components/preview/ap-preview-panel.d.ts +80 -0
  45. package/dist/components/preview/ap-preview-panel.d.ts.map +1 -0
  46. package/dist/components/selection/ap-marquee-overlay.d.ts +14 -0
  47. package/dist/components/selection/ap-marquee-overlay.d.ts.map +1 -0
  48. package/dist/components/selection/ap-selection-bar.d.ts +21 -0
  49. package/dist/components/selection/ap-selection-bar.d.ts.map +1 -0
  50. package/dist/components/shared/ap-badge.d.ts +12 -0
  51. package/dist/components/shared/ap-badge.d.ts.map +1 -0
  52. package/dist/components/shared/ap-checkbox.d.ts +17 -0
  53. package/dist/components/shared/ap-checkbox.d.ts.map +1 -0
  54. package/dist/components/shared/ap-dropdown.d.ts +37 -0
  55. package/dist/components/shared/ap-dropdown.d.ts.map +1 -0
  56. package/dist/components/shared/ap-icon.d.ts +13 -0
  57. package/dist/components/shared/ap-icon.d.ts.map +1 -0
  58. package/dist/components/shared/ap-popover.d.ts +15 -0
  59. package/dist/components/shared/ap-popover.d.ts.map +1 -0
  60. package/dist/components/shared/ap-radio-group.d.ts +30 -0
  61. package/dist/components/shared/ap-radio-group.d.ts.map +1 -0
  62. package/dist/components/shared/ap-spinner.d.ts +12 -0
  63. package/dist/components/shared/ap-spinner.d.ts.map +1 -0
  64. package/dist/components/shared/ap-tooltip.d.ts +12 -0
  65. package/dist/components/shared/ap-tooltip.d.ts.map +1 -0
  66. package/dist/components/toolbar/ap-content-toolbar.d.ts +64 -0
  67. package/dist/components/toolbar/ap-content-toolbar.d.ts.map +1 -0
  68. package/dist/components/toolbar/sort.constants.d.ts +9 -0
  69. package/dist/components/toolbar/sort.constants.d.ts.map +1 -0
  70. package/dist/components/views/ap-asset-card.d.ts +20 -0
  71. package/dist/components/views/ap-asset-card.d.ts.map +1 -0
  72. package/dist/components/views/ap-asset-row.d.ts +18 -0
  73. package/dist/components/views/ap-asset-row.d.ts.map +1 -0
  74. package/dist/components/views/ap-folder-card.d.ts +27 -0
  75. package/dist/components/views/ap-folder-card.d.ts.map +1 -0
  76. package/dist/components/views/ap-folder-row.d.ts +14 -0
  77. package/dist/components/views/ap-folder-row.d.ts.map +1 -0
  78. package/dist/components/views/ap-grid-view.d.ts +25 -0
  79. package/dist/components/views/ap-grid-view.d.ts.map +1 -0
  80. package/dist/components/views/ap-list-view.d.ts +22 -0
  81. package/dist/components/views/ap-list-view.d.ts.map +1 -0
  82. package/dist/components/views/ap-skeleton.d.ts +12 -0
  83. package/dist/components/views/ap-skeleton.d.ts.map +1 -0
  84. package/dist/controllers/infinite-scroll.controller.d.ts +13 -0
  85. package/dist/controllers/infinite-scroll.controller.d.ts.map +1 -0
  86. package/dist/controllers/keyboard.controller.d.ts +10 -0
  87. package/dist/controllers/keyboard.controller.d.ts.map +1 -0
  88. package/dist/controllers/marquee.controller.d.ts +43 -0
  89. package/dist/controllers/marquee.controller.d.ts.map +1 -0
  90. package/dist/controllers/selection.controller.d.ts +19 -0
  91. package/dist/controllers/selection.controller.d.ts.map +1 -0
  92. package/dist/controllers/store.controller.d.ts +14 -0
  93. package/dist/controllers/store.controller.d.ts.map +1 -0
  94. package/dist/define.cjs +1 -0
  95. package/dist/define.d.ts +2 -0
  96. package/dist/define.d.ts.map +1 -0
  97. package/dist/define.js +2 -0
  98. package/dist/hls.light-C3NKRmfw.cjs +27 -0
  99. package/dist/hls.light-DxDlt6yF.js +14441 -0
  100. package/dist/index.cjs +1 -0
  101. package/dist/index.d.ts +8 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +4 -0
  104. package/dist/react.cjs +1 -0
  105. package/dist/react.d.ts +17 -0
  106. package/dist/react.d.ts.map +1 -0
  107. package/dist/react.js +46 -0
  108. package/dist/services/api-client.d.ts +12 -0
  109. package/dist/services/api-client.d.ts.map +1 -0
  110. package/dist/services/auth.service.d.ts +3 -0
  111. package/dist/services/auth.service.d.ts.map +1 -0
  112. package/dist/services/files.service.d.ts +21 -0
  113. package/dist/services/files.service.d.ts.map +1 -0
  114. package/dist/services/filters.service.d.ts +13 -0
  115. package/dist/services/filters.service.d.ts.map +1 -0
  116. package/dist/services/folders.service.d.ts +26 -0
  117. package/dist/services/folders.service.d.ts.map +1 -0
  118. package/dist/services/labels.service.d.ts +4 -0
  119. package/dist/services/labels.service.d.ts.map +1 -0
  120. package/dist/services/settings.service.d.ts +4 -0
  121. package/dist/services/settings.service.d.ts.map +1 -0
  122. package/dist/services/tags.service.d.ts +4 -0
  123. package/dist/services/tags.service.d.ts.map +1 -0
  124. package/dist/store/index.d.ts +6 -0
  125. package/dist/store/index.d.ts.map +1 -0
  126. package/dist/store/store.d.ts +14 -0
  127. package/dist/store/store.d.ts.map +1 -0
  128. package/dist/store/store.types.d.ts +49 -0
  129. package/dist/store/store.types.d.ts.map +1 -0
  130. package/dist/styles/shared-styles.d.ts +4 -0
  131. package/dist/styles/shared-styles.d.ts.map +1 -0
  132. package/dist/types/api.types.d.ts +82 -0
  133. package/dist/types/api.types.d.ts.map +1 -0
  134. package/dist/types/asset.types.d.ts +77 -0
  135. package/dist/types/asset.types.d.ts.map +1 -0
  136. package/dist/types/config.types.d.ts +51 -0
  137. package/dist/types/config.types.d.ts.map +1 -0
  138. package/dist/types/events.types.d.ts +23 -0
  139. package/dist/types/events.types.d.ts.map +1 -0
  140. package/dist/types/filter.types.d.ts +190 -0
  141. package/dist/types/filter.types.d.ts.map +1 -0
  142. package/dist/types/folder.types.d.ts +27 -0
  143. package/dist/types/folder.types.d.ts.map +1 -0
  144. package/dist/types/index.d.ts +8 -0
  145. package/dist/types/index.d.ts.map +1 -0
  146. package/dist/types/label.types.d.ts +9 -0
  147. package/dist/types/label.types.d.ts.map +1 -0
  148. package/dist/types/tag.types.d.ts +13 -0
  149. package/dist/types/tag.types.d.ts.map +1 -0
  150. package/dist/utils/brand-color.d.ts +12 -0
  151. package/dist/utils/brand-color.d.ts.map +1 -0
  152. package/dist/utils/css-helpers.d.ts +2 -0
  153. package/dist/utils/css-helpers.d.ts.map +1 -0
  154. package/dist/utils/debounce.d.ts +4 -0
  155. package/dist/utils/debounce.d.ts.map +1 -0
  156. package/dist/utils/file-type.d.ts +25 -0
  157. package/dist/utils/file-type.d.ts.map +1 -0
  158. package/dist/utils/filter-date.d.ts +9 -0
  159. package/dist/utils/filter-date.d.ts.map +1 -0
  160. package/dist/utils/filter-normalize.d.ts +7 -0
  161. package/dist/utils/filter-normalize.d.ts.map +1 -0
  162. package/dist/utils/filter-pin-storage.d.ts +8 -0
  163. package/dist/utils/filter-pin-storage.d.ts.map +1 -0
  164. package/dist/utils/filter-serialize.d.ts +3 -0
  165. package/dist/utils/filter-serialize.d.ts.map +1 -0
  166. package/dist/utils/filter-url.d.ts +8 -0
  167. package/dist/utils/filter-url.d.ts.map +1 -0
  168. package/dist/utils/filter-validate.d.ts +3 -0
  169. package/dist/utils/filter-validate.d.ts.map +1 -0
  170. package/dist/utils/format.d.ts +5 -0
  171. package/dist/utils/format.d.ts.map +1 -0
  172. package/dist/utils/preference-storage.d.ts +6 -0
  173. package/dist/utils/preference-storage.d.ts.map +1 -0
  174. package/dist/utils/sort-storage.d.ts +7 -0
  175. package/dist/utils/sort-storage.d.ts.map +1 -0
  176. package/dist/utils/thumbnail.d.ts +27 -0
  177. package/dist/utils/thumbnail.d.ts.map +1 -0
  178. package/dist/utils/video.d.ts +7 -0
  179. package/dist/utils/video.d.ts.map +1 -0
  180. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,702 @@
1
+ <p align="center">
2
+ <a href="https://www.scaleflex.com">
3
+ <img src="https://scaleflex.cloudimg.io/v7/plugins/js-cloudimage-360-view/logo_scaleflex_on_white_bg.jpg?vh=91b12d&w=700" alt="Scaleflex" width="350">
4
+ </a>
5
+ </p>
6
+
7
+ <h1 align="center">@scaleflex/asset-picker</h1>
8
+
9
+ <p align="center">
10
+ Framework-agnostic Asset Picker Web Component for <a href="https://www.scaleflex.com/page/digital-asset-management">Scaleflex VXP DAM</a>.<br>
11
+ Browse, search, filter, and select assets from your DAM — in any frontend stack.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@scaleflex/asset-picker"><img src="https://img.shields.io/npm/v/@scaleflex/asset-picker.svg?style=flat-square" alt="npm version"></a>
16
+ <a href="https://www.npmjs.com/package/@scaleflex/asset-picker"><img src="https://img.shields.io/npm/dm/@scaleflex/asset-picker.svg?style=flat-square" alt="npm downloads"></a>
17
+ <img src="https://img.shields.io/badge/license-proprietary-red?style=flat-square" alt="license">
18
+ </p>
19
+
20
+ ---
21
+
22
+ ## Table of Contents
23
+
24
+ - [Overview](#overview)
25
+ - [Features](#features)
26
+ - [Requirements](#requirements)
27
+ - [Installation](#installation)
28
+ - [Quick Start](#quick-start)
29
+ - [Vanilla JS / Web Component](#vanilla-js--web-component)
30
+ - [React](#react)
31
+ - [Configuration](#configuration)
32
+ - [Authentication](#authentication)
33
+ - [Config Options](#config-options)
34
+ - [Default & Forced Filters](#default--forced-filters)
35
+ - [Public Methods](#public-methods)
36
+ - [Events](#events)
37
+ - [React API](#react-api)
38
+ - [Theming](#theming)
39
+ - [Brand Color](#brand-color)
40
+ - [CSS Custom Properties](#css-custom-properties)
41
+ - [Filters Reference](#filters-reference)
42
+ - [Filter Keys](#filter-keys)
43
+ - [Filter Data Structures](#filter-data-structures)
44
+ - [Types Reference](#types-reference)
45
+ - [Asset](#asset)
46
+ - [Folder](#folder)
47
+ - [Browser Support](#browser-support)
48
+ - [License](#license)
49
+
50
+ ---
51
+
52
+ ## Overview
53
+
54
+ `@scaleflex/asset-picker` is a drop-in modal component that connects to a [Scaleflex VXP](https://www.scaleflex.com) DAM project and lets users browse, search, filter, preview, and select digital assets. It ships as a standard [Web Component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) (Custom Element) built with [Lit 3](https://lit.dev/), so it works everywhere — vanilla JS, React, Vue, Angular, Svelte, or any other framework.
55
+
56
+ The npm package contains **only pre-built, minified production files** (`dist/`). Source code is maintained in a private Scaleflex GitLab repository.
57
+
58
+ ## Features
59
+
60
+ - **Framework-agnostic** — standard `<asset-picker>` custom element, works in any stack
61
+ - **First-class React wrapper** — `forwardRef` component with controlled `open` prop and imperative ref
62
+ - **Two auth modes** — session tokens or security templates
63
+ - **Grid & list views** — switchable with persistent user preference
64
+ - **Full-text search** — real-time search across your DAM
65
+ - **14+ filter types** — type, date, size, tags, labels, color, approval status, metadata, and more
66
+ - **Filter pinning** — users can pin favourite filters; persisted to `localStorage`
67
+ - **Default & forced filters** — pre-apply filters on open or lock filters the user cannot remove
68
+ - **Infinite scroll** — automatic pagination as the user scrolls
69
+ - **Folder navigation** — browse the full DAM folder tree with breadcrumb
70
+ - **Asset preview panel** — side panel with metadata, thumbnails, and video/audio playback
71
+ - **Multi-select & select all** — single or bulk selection with configurable max
72
+ - **Keyboard navigation** — arrow keys, Enter, Escape, Shift+click range select
73
+ - **Marquee selection** — click-and-drag to select multiple assets
74
+ - **Approval workflow** — filter by approval status, approver, requester, due date
75
+ - **Regional variants** — metadata variant groups with per-variant filtering
76
+ - **Customisable theming** — brand color config + 20 CSS custom properties
77
+ - **Tiny footprint** — ~70 KB gzipped (main chunk)
78
+
79
+ ## Requirements
80
+
81
+ - A [Scaleflex](https://www.scaleflex.com) VXP DAM account with a project token
82
+ - Either **session credentials** or a **security template key** for authentication
83
+ - Modern browser with Custom Elements v1 support (see [Browser Support](#browser-support))
84
+
85
+ ## Installation
86
+
87
+ ```bash
88
+ npm install @scaleflex/asset-picker
89
+ ```
90
+
91
+ ```bash
92
+ yarn add @scaleflex/asset-picker
93
+ ```
94
+
95
+ ```bash
96
+ pnpm add @scaleflex/asset-picker
97
+ ```
98
+
99
+ `lit` is bundled with the package. For React usage, you also need `react` and `react-dom` (v18+) as peer dependencies.
100
+
101
+ ### Package exports
102
+
103
+ | Export path | Description |
104
+ |---|---|
105
+ | `@scaleflex/asset-picker` | `AssetPicker` class + all TypeScript types |
106
+ | `@scaleflex/asset-picker/react` | React wrapper component |
107
+ | `@scaleflex/asset-picker/define` | Side-effect import — registers `<asset-picker>` custom element |
108
+
109
+ Both ESM (`import`) and CJS (`require`) builds are provided.
110
+
111
+ ---
112
+
113
+ ## Quick Start
114
+
115
+ ### Vanilla JS / Web Component
116
+
117
+ ```html
118
+ <asset-picker></asset-picker>
119
+
120
+ <script type="module">
121
+ // 1. Register the custom element (once)
122
+ import '@scaleflex/asset-picker/define';
123
+
124
+ // 2. Grab the element
125
+ const picker = document.querySelector('asset-picker');
126
+
127
+ // 3. Configure
128
+ picker.config = {
129
+ auth: {
130
+ mode: 'securityTemplate',
131
+ securityTemplateKey: 'YOUR_KEY',
132
+ projectToken: 'YOUR_TOKEN',
133
+ },
134
+ };
135
+
136
+ // 4. Listen for events
137
+ picker.addEventListener('ap-select', (e) => {
138
+ console.log('Selected assets:', e.detail.assets);
139
+ });
140
+
141
+ picker.addEventListener('ap-cancel', (e) => {
142
+ console.log('Cancelled via:', e.detail.reason);
143
+ });
144
+
145
+ // 5. Open
146
+ picker.open();
147
+ </script>
148
+ ```
149
+
150
+ ### React
151
+
152
+ ```tsx
153
+ import { useRef } from 'react';
154
+ import { AssetPicker, type AssetPickerRef } from '@scaleflex/asset-picker/react';
155
+
156
+ function App() {
157
+ const pickerRef = useRef<AssetPickerRef>(null);
158
+
159
+ return (
160
+ <>
161
+ <button onClick={() => pickerRef.current?.open()}>
162
+ Pick assets
163
+ </button>
164
+
165
+ <AssetPicker
166
+ ref={pickerRef}
167
+ config={{
168
+ auth: {
169
+ mode: 'securityTemplate',
170
+ securityTemplateKey: 'YOUR_KEY',
171
+ projectToken: 'YOUR_TOKEN',
172
+ },
173
+ }}
174
+ onSelect={(assets) => console.log(assets)}
175
+ onCancel={() => console.log('Cancelled')}
176
+ />
177
+ </>
178
+ );
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Configuration
185
+
186
+ ### Authentication
187
+
188
+ The picker supports two authentication modes:
189
+
190
+ #### Security template (external / public apps)
191
+
192
+ Use for client-side integrations where you don't want to expose session tokens. The picker automatically exchanges the key for a SASS key on init.
193
+
194
+ ```ts
195
+ {
196
+ auth: {
197
+ mode: 'securityTemplate',
198
+ securityTemplateKey: string, // Exchanged for a SASS key via API
199
+ projectToken: string,
200
+ }
201
+ }
202
+ ```
203
+
204
+ #### Session (internal / Scaleflex apps)
205
+
206
+ Use when your backend already manages Scaleflex sessions.
207
+
208
+ ```ts
209
+ {
210
+ auth: {
211
+ mode: 'session',
212
+ sessionToken: string, // X-Session-Token
213
+ companyToken: string, // X-Company-Token
214
+ projectToken: string, // X-Project-Token
215
+ }
216
+ }
217
+ ```
218
+
219
+ ### Config Options
220
+
221
+ | Property | Type | Default | Description |
222
+ |---|---|---|---|
223
+ | `auth` | `AuthConfig` | **required** | Authentication credentials (see above) |
224
+ | `apiBase` | `string` | auto | Override the API base URL |
225
+ | `locale` | `string` | `undefined` | Locale for translations |
226
+ | `multiSelect` | `boolean` | `true` | Enable multi-asset selection |
227
+ | `maxSelections` | `number` | `undefined` | Maximum number of selectable assets |
228
+ | `defaultViewMode` | `'grid' \| 'list'` | `'grid'` | Initial view mode |
229
+ | `defaultSortBy` | `SortBy` | `'created_at'` | Initial sort field (see table below) |
230
+ | `defaultSortDirection` | `'asc' \| 'desc'` | `'desc'` | Initial sort direction |
231
+ | `hiddenTabs` | `TabKey[]` | `[]` | Tabs to hide: `'assets'`, `'folders'` |
232
+ | `enabledFilters` | `FilterKey[]` | all | Restrict which filters appear in the toolbar |
233
+ | `allowedFileTypes` | `string[]` | `undefined` | Restrict selectable file types (e.g. `['image', 'video']`) |
234
+ | `rootFolderUuid` | `string` | `undefined` | Start browsing from a specific folder UUID |
235
+ | `showMetadata` | `boolean` | `true` | Show metadata sections in the preview panel |
236
+ | `brandColor` | `string` | from API | Brand accent colour as hex (e.g. `'#3b82f6'`). Overrides the API-fetched value |
237
+ | `defaultFilters` | `Filters` | `undefined` | Filters pre-applied on open. User can modify/remove |
238
+ | `forcedFilters` | `Filters` | `undefined` | Filters always active. Shown as locked chips the user cannot remove |
239
+ | `onSelect` | `(assets: Asset[]) => void` | `undefined` | Callback when assets are selected |
240
+ | `onCancel` | `() => void` | `undefined` | Callback when the picker is cancelled |
241
+
242
+ #### Sort fields
243
+
244
+ | Value | Available in |
245
+ |---|---|
246
+ | `'name'` | Assets, Folders |
247
+ | `'created_at'` | Assets, Folders |
248
+ | `'modified_at'` | Assets, Folders |
249
+ | `'size'` | Assets |
250
+ | `'type'` | Assets |
251
+ | `'relevance'` | Search results only |
252
+ | `'title'` | Assets |
253
+ | `'color'` | Assets |
254
+ | `'uploaded'` | Assets |
255
+ | `'updated_at'` | Assets |
256
+ | `'files_count_recursive'` | Folders only |
257
+ | `'files_size_recursive'` | Folders only |
258
+
259
+ ### Default & Forced Filters
260
+
261
+ You can pre-configure filters that are applied when the picker opens, and/or lock filters that the user cannot remove.
262
+
263
+ ```ts
264
+ picker.config = {
265
+ auth: { /* ... */ },
266
+
267
+ // Pre-applied on open — user can modify or remove
268
+ defaultFilters: {
269
+ type: { type: 'string', values: ['image'] },
270
+ },
271
+
272
+ // Always active — locked chip with lock icon, cannot be removed
273
+ forcedFilters: {
274
+ tags: { type: 'string', values: ['approved'] },
275
+ },
276
+ };
277
+ ```
278
+
279
+ **Behaviour:**
280
+
281
+ - **`defaultFilters`** are seeded into the applied filters state when `open()` is called. The user sees them as normal filter chips and can modify or remove them freely.
282
+ - **`forcedFilters`** are merged into every API request but are **not** stored in the mutable applied state. They render as locked chips (with a lock icon instead of an X button). The user cannot remove them, and "Clear filters" does not affect them. Forced filter keys are also hidden from the "Add filter" dropdown.
283
+ - If the same key appears in both `defaultFilters` and `forcedFilters`, the forced filter takes precedence — the default filter for that key is skipped.
284
+
285
+ ---
286
+
287
+ ## Public Methods
288
+
289
+ | Method | Returns | Description |
290
+ |---|---|---|
291
+ | `open()` | `Promise<void>` | Opens the picker modal. Initialises the API client and loads initial data if not already done. Fires `ap-open` on success. |
292
+ | `close()` | `void` | Closes the picker modal and clears the selection state. |
293
+
294
+ ```js
295
+ // Open the picker
296
+ await picker.open();
297
+
298
+ // Close programmatically
299
+ picker.close();
300
+ ```
301
+
302
+ ---
303
+
304
+ ## Events
305
+
306
+ All events bubble and cross shadow DOM boundaries (`composed: true`).
307
+
308
+ | Event | Detail | Description |
309
+ |---|---|---|
310
+ | `ap-select` | `{ assets: Asset[] }` | Fired when the user confirms their selection |
311
+ | `ap-cancel` | `{ reason: 'backdrop' \| 'escape' \| 'button' \| 'close-button' }` | Fired when the picker is closed without selecting |
312
+ | `ap-open` | `{ timestamp: number }` | Fired when the picker opens successfully |
313
+ | `ap-error` | `{ error: Error, context: string }` | Fired on initialisation or runtime errors |
314
+
315
+ ```js
316
+ picker.addEventListener('ap-select', (e) => {
317
+ const { assets } = e.detail;
318
+ assets.forEach((asset) => {
319
+ console.log(asset.name, asset.url?.cdn);
320
+ });
321
+ });
322
+
323
+ picker.addEventListener('ap-cancel', (e) => {
324
+ console.log('Cancelled via:', e.detail.reason);
325
+ });
326
+
327
+ picker.addEventListener('ap-error', (e) => {
328
+ console.error(`[${e.detail.context}]`, e.detail.error);
329
+ });
330
+ ```
331
+
332
+ ---
333
+
334
+ ## React API
335
+
336
+ ```tsx
337
+ import { AssetPicker, type AssetPickerRef, type AssetPickerProps } from '@scaleflex/asset-picker/react';
338
+ ```
339
+
340
+ ### Props
341
+
342
+ | Prop | Type | Description |
343
+ |---|---|---|
344
+ | `config` | `AssetPickerConfig` | Configuration object (see [Config Options](#config-options)) |
345
+ | `open` | `boolean` | Controlled open state |
346
+ | `onSelect` | `(assets: Asset[]) => void` | Selection callback |
347
+ | `onCancel` | `() => void` | Cancel callback |
348
+ | `className` | `string` | CSS class for the wrapper |
349
+ | `style` | `CSSProperties` | Inline styles for the wrapper |
350
+
351
+ ### Ref methods
352
+
353
+ | Method | Description |
354
+ |---|---|
355
+ | `open()` | Open the picker imperatively |
356
+ | `close()` | Close the picker imperatively |
357
+
358
+ ### Controlled mode
359
+
360
+ ```tsx
361
+ const [isOpen, setIsOpen] = useState(false);
362
+
363
+ <AssetPicker
364
+ config={config}
365
+ open={isOpen}
366
+ onSelect={(assets) => {
367
+ console.log(assets);
368
+ setIsOpen(false);
369
+ }}
370
+ onCancel={() => setIsOpen(false)}
371
+ />
372
+ ```
373
+
374
+ ### Imperative mode
375
+
376
+ ```tsx
377
+ const ref = useRef<AssetPickerRef>(null);
378
+
379
+ <button onClick={() => ref.current?.open()}>Open</button>
380
+ <AssetPicker ref={ref} config={config} onSelect={handleSelect} />
381
+ ```
382
+
383
+ ---
384
+
385
+ ## Theming
386
+
387
+ ### Brand Color
388
+
389
+ The simplest way to theme the picker is via the `brandColor` config option. It accepts a hex colour string and applies it as the primary accent across all UI elements.
390
+
391
+ ```ts
392
+ picker.config = {
393
+ auth: { /* ... */ },
394
+ brandColor: '#6366f1', // Indigo
395
+ };
396
+ ```
397
+
398
+ If not set, the picker uses the brand colour configured in your Scaleflex project settings.
399
+
400
+ ### CSS Custom Properties
401
+
402
+ For fine-grained control, override these CSS custom properties on the `<asset-picker>` element or any ancestor. All variables use the `--ap-` prefix.
403
+
404
+ #### Colours
405
+
406
+ | Property | Default | Description |
407
+ |---|---|---|
408
+ | `--ap-primary` | `oklch(0.65 0.19 258)` | Primary accent colour |
409
+ | `--ap-primary-foreground` | `#fff` | Text on primary backgrounds |
410
+ | `--ap-primary-10` | primary @ 10% | Subtle primary tint |
411
+ | `--ap-primary-20` | primary @ 20% | Light primary background |
412
+ | `--ap-background` | `#ffffff` | Main background |
413
+ | `--ap-foreground` | `#09090b` | Main text colour |
414
+ | `--ap-card` | `#ffffff` | Card/panel background |
415
+ | `--ap-border` | `#e4e4e7` | Border colour |
416
+ | `--ap-muted` | `#f4f4f5` | Muted/disabled background |
417
+ | `--ap-muted-foreground` | `#71717a` | Muted/disabled text |
418
+ | `--ap-ring` | same as primary | Focus ring colour |
419
+ | `--ap-selection-bg` | primary @ 8% | Selected item background |
420
+
421
+ #### Modal
422
+
423
+ | Property | Default | Description |
424
+ |---|---|---|
425
+ | `--ap-modal-backdrop` | `rgba(0,0,0,0.5)` | Backdrop overlay colour |
426
+ | `--ap-modal-radius` | `12px` | Modal corner radius |
427
+ | `--ap-modal-shadow` | large shadow | Modal box shadow |
428
+ | `--ap-modal-max-width` | `1200px` | Maximum modal width |
429
+ | `--ap-modal-max-height` | `85vh` | Maximum modal height |
430
+
431
+ #### Typography & Radius
432
+
433
+ | Property | Default | Description |
434
+ |---|---|---|
435
+ | `--ap-font-family` | `system-ui, -apple-system, sans-serif` | Font stack |
436
+ | `--ap-radius` | `8px` | Default border radius |
437
+ | `--ap-radius-sm` | `6px` | Small border radius |
438
+ | `--ap-radius-lg` | `12px` | Large border radius |
439
+
440
+ ```css
441
+ asset-picker {
442
+ --ap-primary: #6366f1;
443
+ --ap-primary-foreground: #fff;
444
+ --ap-radius: 12px;
445
+ --ap-modal-max-width: 1400px;
446
+ }
447
+ ```
448
+
449
+ ---
450
+
451
+ ## Filters Reference
452
+
453
+ ### Filter Keys
454
+
455
+ These are the keys used in `enabledFilters`, `defaultFilters`, and `forcedFilters`.
456
+
457
+ | Key | Constant | Description |
458
+ |---|---|---|
459
+ | `'type'` | `FILTER_KEYS.TYPE` | File format (image, video, audio, document, archive, font) |
460
+ | `'mimetype'` | `FILTER_KEYS.MIME_TYPE` | MIME type |
461
+ | `'date'` | `FILTER_KEYS.DATE` | Upload/modification date |
462
+ | `'size'` | `FILTER_KEYS.SIZE` | File size range |
463
+ | `'tags'` | `FILTER_KEYS.TAGS` | Asset tags |
464
+ | `'labels'` | `FILTER_KEYS.LABELS` | Asset labels |
465
+ | `'color'` | `FILTER_KEYS.COLOR` | Dominant colour search |
466
+ | `'image'` | `FILTER_KEYS.IMAGE` | Image-specific (resolution, orientation, faces) |
467
+ | `'approval'` | `FILTER_KEYS.APPROVAL` | Approval workflow status |
468
+ | `'metadata'` | `FILTER_KEYS.METADATA` | Custom metadata fields |
469
+ | `'product_ref'` | `FILTER_KEYS.PRODUCT_REF` | Product reference |
470
+ | `'asset_expiration'` | `FILTER_KEYS.LICENSE_EXPIRY` | License/asset expiry date |
471
+ | `'folders'` | `FILTER_KEYS.FOLDERS` | Folder location |
472
+ | `'resolution'` | `FILTER_KEYS.RESOLUTION` | Image resolution |
473
+ | `'orientation'` | `FILTER_KEYS.ORIENTATION` | Image orientation |
474
+ | `'faces'` | `FILTER_KEYS.FACES` | Detected faces count |
475
+ | `'products'` | `FILTER_KEYS.PRODUCTS` | Products |
476
+
477
+ ### Filter Data Structures
478
+
479
+ Filters used in `defaultFilters` and `forcedFilters` use these shapes:
480
+
481
+ #### String filter
482
+
483
+ ```ts
484
+ {
485
+ type: 'string',
486
+ values: string[], // Filter values
487
+ operator?: string, // Filter operator (default: ':')
488
+ logic?: 'OR' | 'AND', // Combine logic (default: 'OR')
489
+ }
490
+ ```
491
+
492
+ **Examples:**
493
+
494
+ ```ts
495
+ // Only images
496
+ { type: 'string', values: ['image'] }
497
+
498
+ // Only JPEG and PNG
499
+ { type: 'string', values: ['image/jpeg', 'image/png'] }
500
+
501
+ // Tagged with "hero" or "banner"
502
+ { type: 'string', values: ['hero', 'banner'] }
503
+
504
+ // Size range: 1 MB to 50 MB (values in bytes as "min..max")
505
+ { type: 'string', values: ['1000000..50000000'] }
506
+ ```
507
+
508
+ #### Date filter
509
+
510
+ ```ts
511
+ {
512
+ type: 'date',
513
+ field: 'created' | 'updated',
514
+ kind: 'preset' | 'after' | 'before' | 'between' | 'specific' | null,
515
+ preset: string | null, // e.g. 'today', 'last_week', 'last_month'
516
+ from: string | null, // ISO date string
517
+ to: string | null, // ISO date string
518
+ }
519
+ ```
520
+
521
+ **Examples:**
522
+
523
+ ```ts
524
+ // Uploaded in the last month
525
+ {
526
+ type: 'date',
527
+ field: 'created',
528
+ kind: 'preset',
529
+ preset: 'last_month',
530
+ from: null,
531
+ to: null,
532
+ }
533
+
534
+ // Modified after a specific date
535
+ {
536
+ type: 'date',
537
+ field: 'updated',
538
+ kind: 'after',
539
+ preset: null,
540
+ from: '2025-01-01T00:00:00.000Z',
541
+ to: null,
542
+ }
543
+ ```
544
+
545
+ **Available date presets:** `'today'`, `'last_week'`, `'within_week'`, `'last_month'`, `'within_month'`, `'last_year'`, `'within_year'`, `'empty'`, `'non-empty'`
546
+
547
+ #### Filter operators
548
+
549
+ | Operator | Symbol | Description |
550
+ |---|---|---|
551
+ | `IS` | `:` | Exact match (default) |
552
+ | `EQUAL` | `=` | Equality |
553
+ | `NOT_EQUAL` | `!=` | Inequality |
554
+ | `RANGE` | `..` | Range match |
555
+ | `IS_NOT` | `:-` | Negated match |
556
+ | `IS_EXACT` | `:=` | Strict exact match |
557
+ | `CONTAINS` | `~` | Contains substring |
558
+ | `CONTAINS_IN_TEXT` | `~~~` | Full text search |
559
+ | `STARTS_WITH` | `~^` | Starts with |
560
+ | `GREATER_THAN` | `>` | Greater than |
561
+ | `LESS_THAN` | `<` | Less than |
562
+ | `GREATER_THAN_OR_EQUAL` | `>=` | Greater than or equal |
563
+ | `LESS_THAN_OR_EQUAL` | `<=` | Less than or equal |
564
+ | `SIMILAR_TO` | `~~` | Similarity match |
565
+
566
+ ---
567
+
568
+ ## Types Reference
569
+
570
+ All types are exported from the main entry point:
571
+
572
+ ```ts
573
+ import type {
574
+ AssetPickerConfig,
575
+ AuthConfig,
576
+ SessionAuth,
577
+ SecurityTemplateAuth,
578
+ Asset,
579
+ Folder,
580
+ FilterKey,
581
+ AnyFilterKey,
582
+ AnyFilter,
583
+ StringFilter,
584
+ DateFilter,
585
+ Filters,
586
+ FiltersState,
587
+ ViewMode,
588
+ SortBy,
589
+ SortDirection,
590
+ TabKey,
591
+ AssetPickerSelectDetail,
592
+ AssetPickerCancelDetail,
593
+ } from '@scaleflex/asset-picker';
594
+ ```
595
+
596
+ ### Asset
597
+
598
+ The `Asset` object returned in `ap-select` events:
599
+
600
+ ```ts
601
+ interface Asset {
602
+ uuid: string;
603
+ name: string;
604
+ extension: string;
605
+ type: string; // 'image', 'video', 'audio', 'document', ...
606
+ mime?: string; // MIME type
607
+ size: {
608
+ bytes: number;
609
+ pretty: string;
610
+ };
611
+ url?: {
612
+ public: string; // Public URL
613
+ cdn: string; // CDN-optimised URL
614
+ path: string; // Relative path
615
+ permalink?: string; // Permanent link
616
+ };
617
+ created_at: string; // ISO timestamp
618
+ modified_at: string; // ISO timestamp
619
+ tags: Record<string, any>;
620
+ labels: string[];
621
+ meta: {
622
+ title?: string;
623
+ description?: string;
624
+ alt?: string;
625
+ [key: string]: unknown;
626
+ };
627
+ info: {
628
+ img_w?: number; // Image width (px)
629
+ img_h?: number; // Image height (px)
630
+ duration?: number; // Video/audio duration (seconds)
631
+ video_w?: number; // Video width (px)
632
+ video_h?: number; // Video height (px)
633
+ thumbnail?: string; // Thumbnail URL
634
+ main_colors_hex?: string[]; // Dominant colours
635
+ dominant_color_hex?: string; // Most dominant colour
636
+ [key: string]: unknown;
637
+ };
638
+ folder?: {
639
+ uuid: string;
640
+ name: string;
641
+ };
642
+ owner?: {
643
+ uuid: string;
644
+ name: string;
645
+ email: string;
646
+ };
647
+ }
648
+ ```
649
+
650
+ ### Folder
651
+
652
+ ```ts
653
+ interface Folder {
654
+ uuid: string;
655
+ name: string;
656
+ path: string;
657
+ created_at: string;
658
+ modified_at?: string;
659
+ count?: {
660
+ files_recursive?: number;
661
+ files_direct?: number;
662
+ };
663
+ size?: {
664
+ total_recursive_bytes?: number;
665
+ };
666
+ }
667
+ ```
668
+
669
+ ---
670
+
671
+ ## Browser Support
672
+
673
+ | Browser | Minimum version |
674
+ |---|---|
675
+ | Chrome | 67+ |
676
+ | Firefox | 63+ |
677
+ | Safari | 13.1+ |
678
+ | Edge (Chromium) | 79+ |
679
+
680
+ Requires native support for Custom Elements v1, Shadow DOM, and ES2020+. Internet Explorer is **not** supported.
681
+
682
+ ---
683
+
684
+ ## License
685
+
686
+ **PROPRIETARY** &mdash; All Rights Reserved.
687
+
688
+ Copyright &copy; 2025 [Scaleflex SAS](https://www.scaleflex.com).
689
+
690
+ This software and associated documentation are the exclusive property of Scaleflex SAS. No part of this software may be copied, modified, distributed, sublicensed, sold, or otherwise made available to any third party without prior written permission from Scaleflex SAS.
691
+
692
+ This package is distributed via npm solely for the convenience of licensed customers. Installing or using this package does not grant any licence to use the software. Use is permitted only under a separate written licence agreement with Scaleflex SAS.
693
+
694
+ Unauthorised use, reproduction, or distribution of this software may result in civil and criminal penalties and will be prosecuted to the maximum extent permitted by law.
695
+
696
+ For licensing enquiries, contact [sales@scaleflex.com](mailto:sales@scaleflex.com).
697
+
698
+ ---
699
+
700
+ <p align="center">
701
+ Made with care by <a href="https://www.scaleflex.com">Scaleflex</a>
702
+ </p>