@acorex/cdk 21.0.2-next.29 → 21.0.2-next.30
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/fesm2022/acorex-cdk-accordion.mjs +24 -24
- package/fesm2022/acorex-cdk-accordion.mjs.map +1 -1
- package/fesm2022/acorex-cdk-carousel.mjs +3 -3
- package/fesm2022/acorex-cdk-carousel.mjs.map +1 -1
- package/fesm2022/acorex-cdk-clipboard.mjs +8 -8
- package/fesm2022/acorex-cdk-clipboard.mjs.map +1 -1
- package/fesm2022/acorex-cdk-common.mjs +161 -106
- package/fesm2022/acorex-cdk-common.mjs.map +1 -1
- package/fesm2022/acorex-cdk-dom.mjs +4 -4
- package/fesm2022/acorex-cdk-dom.mjs.map +1 -1
- package/fesm2022/acorex-cdk-double-click.mjs +4 -4
- package/fesm2022/acorex-cdk-double-click.mjs.map +1 -1
- package/fesm2022/acorex-cdk-drag-drop.mjs +22 -22
- package/fesm2022/acorex-cdk-drag-drop.mjs.map +1 -1
- package/fesm2022/acorex-cdk-drawer.mjs +13 -13
- package/fesm2022/acorex-cdk-drawer.mjs.map +1 -1
- package/fesm2022/acorex-cdk-focus-trap.mjs +3 -3
- package/fesm2022/acorex-cdk-focus-trap.mjs.map +1 -1
- package/fesm2022/acorex-cdk-full-screen.mjs +4 -4
- package/fesm2022/acorex-cdk-full-screen.mjs.map +1 -1
- package/fesm2022/acorex-cdk-input-mask.mjs +11 -5
- package/fesm2022/acorex-cdk-input-mask.mjs.map +1 -1
- package/fesm2022/acorex-cdk-list-navigation.mjs +13 -13
- package/fesm2022/acorex-cdk-list-navigation.mjs.map +1 -1
- package/fesm2022/acorex-cdk-outline.mjs +68 -57
- package/fesm2022/acorex-cdk-outline.mjs.map +1 -1
- package/fesm2022/acorex-cdk-overlay.mjs +16 -3
- package/fesm2022/acorex-cdk-overlay.mjs.map +1 -1
- package/fesm2022/acorex-cdk-pan-view.mjs +4 -4
- package/fesm2022/acorex-cdk-pan-view.mjs.map +1 -1
- package/fesm2022/acorex-cdk-qrcode.mjs.map +1 -1
- package/fesm2022/acorex-cdk-resizable.mjs +5 -6
- package/fesm2022/acorex-cdk-resizable.mjs.map +1 -1
- package/fesm2022/acorex-cdk-selection.mjs +13 -13
- package/fesm2022/acorex-cdk-selection.mjs.map +1 -1
- package/fesm2022/acorex-cdk-sliding-item.mjs +3 -3
- package/fesm2022/acorex-cdk-sliding-item.mjs.map +1 -1
- package/fesm2022/acorex-cdk-sticky.mjs +3 -3
- package/fesm2022/acorex-cdk-sticky.mjs.map +1 -1
- package/fesm2022/acorex-cdk-uploader.mjs +171 -281
- package/fesm2022/acorex-cdk-uploader.mjs.map +1 -1
- package/fesm2022/acorex-cdk-virtual-scroll.mjs +11 -11
- package/fesm2022/acorex-cdk-virtual-scroll.mjs.map +1 -1
- package/fesm2022/acorex-cdk-wysiwyg.mjs.map +1 -1
- package/fesm2022/acorex-cdk-z-index.mjs +3 -3
- package/fesm2022/acorex-cdk-z-index.mjs.map +1 -1
- package/fesm2022/acorex-cdk.mjs.map +1 -1
- package/package.json +28 -28
- package/{common/index.d.ts → types/acorex-cdk-common.d.ts} +9 -7
- package/{focus-trap/index.d.ts → types/acorex-cdk-focus-trap.d.ts} +1 -1
- package/{input-mask/index.d.ts → types/acorex-cdk-input-mask.d.ts} +1 -0
- package/{outline/index.d.ts → types/acorex-cdk-outline.d.ts} +1 -0
- package/{overlay/index.d.ts → types/acorex-cdk-overlay.d.ts} +1 -0
- package/{uploader/index.d.ts → types/acorex-cdk-uploader.d.ts} +110 -120
- /package/{accordion/index.d.ts → types/acorex-cdk-accordion.d.ts} +0 -0
- /package/{carousel/index.d.ts → types/acorex-cdk-carousel.d.ts} +0 -0
- /package/{clipboard/index.d.ts → types/acorex-cdk-clipboard.d.ts} +0 -0
- /package/{dom/index.d.ts → types/acorex-cdk-dom.d.ts} +0 -0
- /package/{double-click/index.d.ts → types/acorex-cdk-double-click.d.ts} +0 -0
- /package/{drag-drop/index.d.ts → types/acorex-cdk-drag-drop.d.ts} +0 -0
- /package/{drawer/index.d.ts → types/acorex-cdk-drawer.d.ts} +0 -0
- /package/{full-screen/index.d.ts → types/acorex-cdk-full-screen.d.ts} +0 -0
- /package/{list-navigation/index.d.ts → types/acorex-cdk-list-navigation.d.ts} +0 -0
- /package/{pan-view/index.d.ts → types/acorex-cdk-pan-view.d.ts} +0 -0
- /package/{qrcode/index.d.ts → types/acorex-cdk-qrcode.d.ts} +0 -0
- /package/{resizable/index.d.ts → types/acorex-cdk-resizable.d.ts} +0 -0
- /package/{selection/index.d.ts → types/acorex-cdk-selection.d.ts} +0 -0
- /package/{sliding-item/index.d.ts → types/acorex-cdk-sliding-item.d.ts} +0 -0
- /package/{sticky/index.d.ts → types/acorex-cdk-sticky.d.ts} +0 -0
- /package/{virtual-scroll/index.d.ts → types/acorex-cdk-virtual-scroll.d.ts} +0 -0
- /package/{wysiwyg/index.d.ts → types/acorex-cdk-wysiwyg.d.ts} +0 -0
- /package/{z-index/index.d.ts → types/acorex-cdk-z-index.d.ts} +0 -0
- /package/{index.d.ts → types/acorex-cdk.d.ts} +0 -0
|
@@ -7,27 +7,6 @@ import { AXFileService } from '@acorex/core/file';
|
|
|
7
7
|
import { sumBy } from 'lodash-es';
|
|
8
8
|
import { Subject, BehaviorSubject, map } from 'rxjs';
|
|
9
9
|
|
|
10
|
-
/** DOM marker so browse handles can find a zone across content projection. */
|
|
11
|
-
const AX_UPLOADER_ZONE_HOST = Symbol('AX_UPLOADER_ZONE_HOST');
|
|
12
|
-
function bindUploaderZoneHost(element, zone) {
|
|
13
|
-
element[AX_UPLOADER_ZONE_HOST] = zone;
|
|
14
|
-
}
|
|
15
|
-
function unbindUploaderZoneHost(element) {
|
|
16
|
-
delete element[AX_UPLOADER_ZONE_HOST];
|
|
17
|
-
}
|
|
18
|
-
/** Walk ancestors to resolve the zone (works with projected browse handles). */
|
|
19
|
-
function findUploaderZoneFromDom(start) {
|
|
20
|
-
let el = start;
|
|
21
|
-
while (el) {
|
|
22
|
-
const zone = el[AX_UPLOADER_ZONE_HOST];
|
|
23
|
-
if (zone) {
|
|
24
|
-
return zone;
|
|
25
|
-
}
|
|
26
|
-
el = el.parentElement;
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
10
|
class AXUploadRequest {
|
|
32
11
|
get name() {
|
|
33
12
|
return this.file.name;
|
|
@@ -127,169 +106,76 @@ class AXUploadRequest {
|
|
|
127
106
|
}
|
|
128
107
|
|
|
129
108
|
/**
|
|
130
|
-
*
|
|
131
|
-
* Storage is implemented by subscribing to upload/resolve/delete events.
|
|
109
|
+
* Service for managing file uploads with drag-and-drop support, progress tracking, and dialog management.
|
|
132
110
|
* @category Services
|
|
133
111
|
*/
|
|
134
112
|
class AXUploaderService {
|
|
135
113
|
constructor() {
|
|
114
|
+
/**
|
|
115
|
+
* Translation service for localized text.
|
|
116
|
+
* @ignore
|
|
117
|
+
*/
|
|
118
|
+
this.translateService = inject(AXTranslationService);
|
|
119
|
+
/**
|
|
120
|
+
* File service for file operations.
|
|
121
|
+
* @ignore
|
|
122
|
+
*/
|
|
136
123
|
this.fileService = inject(AXFileService);
|
|
124
|
+
/**
|
|
125
|
+
* Behavior subject for managing upload requests.
|
|
126
|
+
* @ignore
|
|
127
|
+
*/
|
|
137
128
|
this._files$ = new BehaviorSubject([]);
|
|
129
|
+
/**
|
|
130
|
+
* Gets the files behavior subject for observing upload requests.
|
|
131
|
+
*/
|
|
138
132
|
this.files = this._files$.asObservable();
|
|
133
|
+
/**
|
|
134
|
+
* Subject for file upload start events.
|
|
135
|
+
*/
|
|
139
136
|
this.onFileUploadStart = new Subject();
|
|
137
|
+
/**
|
|
138
|
+
* Subject for file upload complete events.
|
|
139
|
+
*/
|
|
140
140
|
this.onFileUploadComplete = new Subject();
|
|
141
|
+
/**
|
|
142
|
+
* Subject for all files upload complete events.
|
|
143
|
+
*/
|
|
141
144
|
this.onFilesUploadComplete = new Subject();
|
|
145
|
+
/**
|
|
146
|
+
* Subject for file upload canceled events.
|
|
147
|
+
*/
|
|
142
148
|
this.onFileUploadCanceled = new Subject();
|
|
143
|
-
/**
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.
|
|
147
|
-
/**
|
|
148
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Signal indicating if any upload has determined progress.
|
|
151
|
+
*/
|
|
152
|
+
this.isAnyDetermined = computed(() => this._files$.value.some((file) => file.isDetermined()), ...(ngDevMode ? [{ debugName: "isAnyDetermined" }] : []));
|
|
153
|
+
/**
|
|
154
|
+
* Observable for total estimated upload time.
|
|
155
|
+
*/
|
|
149
156
|
this.totalEstimateTime = this._files$.pipe(map((files) => sumBy(files, (file) => (file.status() === 'inprogress' ? file.estimateTime() : 0))));
|
|
150
157
|
}
|
|
151
|
-
isAnyDetermined() {
|
|
152
|
-
return this._files$.value.some((file) => file.isDetermined());
|
|
153
|
-
}
|
|
154
|
-
validateFiles(files, fileType) {
|
|
155
|
-
return this.fileService.validateMany(files, fileType);
|
|
156
|
-
}
|
|
157
|
-
async getAcceptAttribute(fileType) {
|
|
158
|
-
return this.fileService.getAcceptAttribute(fileType);
|
|
159
|
-
}
|
|
160
|
-
upload(options) {
|
|
161
|
-
return new Promise((resolve, reject) => {
|
|
162
|
-
if (!this.hasUploadHandler()) {
|
|
163
|
-
reject(new Error('No upload handler subscribed to AXUploaderService.onUpload. Provide a service that handles upload events.'));
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
this.onUpload.next({
|
|
167
|
-
component: this,
|
|
168
|
-
options,
|
|
169
|
-
resolve,
|
|
170
|
-
reject,
|
|
171
|
-
isUserInteraction: false,
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
resolveUrl(reference, signal) {
|
|
176
|
-
return new Promise((resolve, reject) => {
|
|
177
|
-
if (!this.hasResolveUrlHandler()) {
|
|
178
|
-
reject(new Error('No handler subscribed to AXUploaderService.onResolveUrl. Provide a service that handles resolve events.'));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
this.onResolveUrl.next({
|
|
182
|
-
component: this,
|
|
183
|
-
reference,
|
|
184
|
-
signal,
|
|
185
|
-
resolve,
|
|
186
|
-
reject,
|
|
187
|
-
isUserInteraction: false,
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
async resolvePlaybackUrl(reference, signal) {
|
|
192
|
-
const direct = reference.url?.trim();
|
|
193
|
-
if (direct && !reference.mediaId) {
|
|
194
|
-
return direct;
|
|
195
|
-
}
|
|
196
|
-
if (direct && reference.mediaId) {
|
|
197
|
-
try {
|
|
198
|
-
return await this.resolveUrl(reference, signal);
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
return direct;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (reference.mediaId) {
|
|
205
|
-
return this.resolveUrl(reference, signal);
|
|
206
|
-
}
|
|
207
|
-
if (direct) {
|
|
208
|
-
return direct;
|
|
209
|
-
}
|
|
210
|
-
throw new Error('Upload reference has no url or mediaId');
|
|
211
|
-
}
|
|
212
|
-
deleteMedia(reference) {
|
|
213
|
-
return new Promise((resolve, reject) => {
|
|
214
|
-
if (!this.hasDeleteMediaHandler()) {
|
|
215
|
-
reject(new Error('No handler subscribed to AXUploaderService.onDeleteMedia. Provide a service that handles delete events.'));
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
this.onDeleteMedia.next({
|
|
219
|
-
component: this,
|
|
220
|
-
reference,
|
|
221
|
-
resolve,
|
|
222
|
-
reject,
|
|
223
|
-
isUserInteraction: false,
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
hasUploadHandler() {
|
|
228
|
-
return this.onUpload.observers.length > 0;
|
|
229
|
-
}
|
|
230
|
-
hasResolveUrlHandler() {
|
|
231
|
-
return this.onResolveUrl.observers.length > 0;
|
|
232
|
-
}
|
|
233
|
-
hasDeleteMediaHandler() {
|
|
234
|
-
return this.onDeleteMedia.observers.length > 0;
|
|
235
|
-
}
|
|
236
|
-
async add(files, options) {
|
|
237
|
-
const list = Array.from(files).map((f) => new AXUploadRequest(f));
|
|
238
|
-
await this.applyFileTypeValidation(list, options?.fileType);
|
|
239
|
-
const newFiles = [...this._files$.value, ...list];
|
|
240
|
-
this._files$.next(newFiles);
|
|
241
|
-
void this.startUpload();
|
|
242
|
-
return list;
|
|
243
|
-
}
|
|
244
158
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
159
|
+
* Converts a File object to an AXUploadRequest.
|
|
160
|
+
* @private
|
|
247
161
|
*/
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (options.fileType) {
|
|
251
|
-
const accept = options.accept ?? (await this.fileService.getAcceptAttribute(options.fileType));
|
|
252
|
-
return this.fileService.chooseValidated({
|
|
253
|
-
fileType: options.fileType,
|
|
254
|
-
multiple: options.multiple ?? false,
|
|
255
|
-
accept,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
const accepted = await this.fileService.choose({
|
|
259
|
-
accept: options.accept,
|
|
260
|
-
multiple: options.multiple ?? false,
|
|
261
|
-
});
|
|
262
|
-
return { accepted, rejected: [] };
|
|
263
|
-
}
|
|
264
|
-
catch (error) {
|
|
265
|
-
console.error('File choose failed:', error);
|
|
266
|
-
return { accepted: [], rejected: [] };
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
async browse(options = { multiple: false }) {
|
|
270
|
-
const { accepted } = await this.chooseFiles(options);
|
|
271
|
-
if (!accepted.length) {
|
|
272
|
-
return [];
|
|
273
|
-
}
|
|
274
|
-
return this.add(accepted, { fileType: options.fileType });
|
|
275
|
-
}
|
|
276
|
-
async cancelAll() {
|
|
277
|
-
await Promise.all(this._files$.value.filter((c) => c.status() !== 'completed').map((c) => c.cancel()));
|
|
278
|
-
}
|
|
279
|
-
clearAll() {
|
|
280
|
-
const remainingFiles = this._files$.value.filter((c) => c.status() === 'inprogress');
|
|
281
|
-
this._files$.next(remainingFiles);
|
|
282
|
-
}
|
|
283
|
-
remove(item) {
|
|
284
|
-
const updatedFiles = this._files$.value.filter((c) => c !== item);
|
|
285
|
-
this._files$.next(updatedFiles);
|
|
162
|
+
convertFileToRequest(file) {
|
|
163
|
+
return new AXUploadRequest(file);
|
|
286
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Starts uploading files that are in 'new' status.
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
287
169
|
async startUpload() {
|
|
288
170
|
const newFiles = this._files$.value.filter((c) => c.status() === 'new');
|
|
289
171
|
for (const file of newFiles) {
|
|
290
172
|
await this.bindEvents(file);
|
|
291
173
|
}
|
|
292
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Binds event handlers to an upload request.
|
|
177
|
+
* @private
|
|
178
|
+
*/
|
|
293
179
|
async bindEvents(c) {
|
|
294
180
|
c.onStart.subscribe(() => {
|
|
295
181
|
this.onFileUploadStart.next({
|
|
@@ -322,22 +208,62 @@ class AXUploaderService {
|
|
|
322
208
|
});
|
|
323
209
|
});
|
|
324
210
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Opens the file browser dialog and returns selected files as upload requests.
|
|
213
|
+
* @param options - Configuration options for file selection
|
|
214
|
+
* @returns Promise that resolves to an array of upload requests
|
|
215
|
+
*/
|
|
216
|
+
async browse(options = { multiple: false }) {
|
|
217
|
+
try {
|
|
218
|
+
const files = await this.fileService.choose({ multiple: options?.multiple || false, accept: options.accept });
|
|
219
|
+
if (files.length) {
|
|
220
|
+
return this.add(files);
|
|
333
221
|
}
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
// It's good practice to log the error.
|
|
226
|
+
console.error('File browse failed:', error);
|
|
227
|
+
return [];
|
|
334
228
|
}
|
|
335
|
-
return requests;
|
|
336
229
|
}
|
|
337
|
-
|
|
338
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Adds files to the upload queue and starts the upload process.
|
|
232
|
+
* @param files - Files to add to the upload queue
|
|
233
|
+
* @returns Promise that resolves to an array of upload requests
|
|
234
|
+
*/
|
|
235
|
+
async add(files) {
|
|
236
|
+
const list = Array.from(files).map((f) => this.convertFileToRequest(f));
|
|
237
|
+
const newFiles = [...this._files$.value, ...list];
|
|
238
|
+
this._files$.next(newFiles);
|
|
239
|
+
this.startUpload();
|
|
240
|
+
return list;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Cancels all pending and in-progress uploads.
|
|
244
|
+
*/
|
|
245
|
+
async cancelAll() {
|
|
246
|
+
await Promise.all(this._files$.value.filter((c) => c.status() !== 'completed').map((c) => c.cancel()));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Clears all completed uploads from the queue.
|
|
250
|
+
*/
|
|
251
|
+
clearAll() {
|
|
252
|
+
const remainingFiles = this._files$.value.filter((c) => c.status() === 'inprogress');
|
|
253
|
+
this._files$.next(remainingFiles);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Removes a specific upload request from the queue.
|
|
257
|
+
* @param item - The upload request to remove
|
|
258
|
+
*/
|
|
259
|
+
remove(item) {
|
|
260
|
+
const updatedFiles = this._files$.value.filter((c) => c !== item);
|
|
261
|
+
this._files$.next(updatedFiles);
|
|
262
|
+
}
|
|
263
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
264
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderService, providedIn: 'root' }); }
|
|
339
265
|
}
|
|
340
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
266
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderService, decorators: [{
|
|
341
267
|
type: Injectable,
|
|
342
268
|
args: [{ providedIn: 'root' }]
|
|
343
269
|
}] });
|
|
@@ -372,11 +298,6 @@ class AXUploaderZoneDirective {
|
|
|
372
298
|
* @defaultValue null
|
|
373
299
|
*/
|
|
374
300
|
this.accept = input(null, ...(ngDevMode ? [{ debugName: "accept" }] : []));
|
|
375
|
-
/**
|
|
376
|
-
* Logical file type name from {@link AXFileService} (e.g. `conversation-image`).
|
|
377
|
-
* Required for browse handles and validated selection.
|
|
378
|
-
*/
|
|
379
|
-
this.fileType = input(null, ...(ngDevMode ? [{ debugName: "fileType" }] : []));
|
|
380
301
|
/**
|
|
381
302
|
* Custom template for the drag overlay. If provided, this will be used instead of the default overlay.
|
|
382
303
|
*/
|
|
@@ -484,7 +405,6 @@ class AXUploaderZoneDirective {
|
|
|
484
405
|
this.animationEndHandler = null;
|
|
485
406
|
this.element = this.elementRef.nativeElement;
|
|
486
407
|
this.element.style.position = 'relative';
|
|
487
|
-
bindUploaderZoneHost(this.element, this);
|
|
488
408
|
//
|
|
489
409
|
this.uploadService.onFileUploadComplete.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
|
|
490
410
|
this.onFileUploadComplete.emit(c);
|
|
@@ -510,7 +430,6 @@ class AXUploaderZoneDirective {
|
|
|
510
430
|
* Cleans up event listeners when the directive is destroyed.
|
|
511
431
|
*/
|
|
512
432
|
ngOnDestroy() {
|
|
513
|
-
unbindUploaderZoneHost(this.element);
|
|
514
433
|
this.element.removeEventListener('click', this.browser.bind(this));
|
|
515
434
|
this.element.removeEventListener('dragenter', this.handleDragEnter.bind(this));
|
|
516
435
|
this.element.removeEventListener('drop', this.handleOnDrop.bind(this));
|
|
@@ -548,25 +467,16 @@ class AXUploaderZoneDirective {
|
|
|
548
467
|
// Reset drag depth on drop
|
|
549
468
|
this.dragDepth.set(0);
|
|
550
469
|
if (event.dataTransfer?.files && event.dataTransfer.files.length > 0) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const requests = await this.uploadService.add(files, { fileType: catalog ?? undefined });
|
|
562
|
-
if (requests.length > 0) {
|
|
563
|
-
this.onChanged.emit({
|
|
564
|
-
component: this,
|
|
565
|
-
requests,
|
|
566
|
-
isUserInteraction: true,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
}
|
|
470
|
+
const files = Array.from(event.dataTransfer.files);
|
|
471
|
+
// Emit simple file change event (like HTML input)
|
|
472
|
+
this.fileChange.emit({ event, files });
|
|
473
|
+
// Also emit the old onChanged event for backward compatibility
|
|
474
|
+
const requests = await this.uploadService.add(event.dataTransfer.files);
|
|
475
|
+
this.onChanged.emit({
|
|
476
|
+
component: this,
|
|
477
|
+
requests,
|
|
478
|
+
isUserInteraction: true,
|
|
479
|
+
});
|
|
570
480
|
}
|
|
571
481
|
this.removeZone();
|
|
572
482
|
this.cdr.detectChanges();
|
|
@@ -730,60 +640,32 @@ class AXUploaderZoneDirective {
|
|
|
730
640
|
* @returns Promise that resolves when files are processed
|
|
731
641
|
*/
|
|
732
642
|
async browser() {
|
|
733
|
-
|
|
734
|
-
let selected = [];
|
|
735
|
-
let rejected;
|
|
736
|
-
if (catalog) {
|
|
737
|
-
const accept = this.accept() ?? (await this.uploadService.getAcceptAttribute(catalog));
|
|
738
|
-
const result = await this.uploadService.chooseFiles({
|
|
739
|
-
fileType: catalog,
|
|
740
|
-
multiple: this.multiple(),
|
|
741
|
-
accept,
|
|
742
|
-
});
|
|
743
|
-
selected = result.accepted;
|
|
744
|
-
rejected = result.rejected;
|
|
745
|
-
}
|
|
746
|
-
else if (!this.disableBrowse()) {
|
|
747
|
-
const requests = await this.uploadService.browse({
|
|
748
|
-
accept: this.accept() ?? undefined,
|
|
749
|
-
multiple: this.multiple(),
|
|
750
|
-
});
|
|
751
|
-
selected = requests.map((r) => r.file);
|
|
752
|
-
}
|
|
753
|
-
else {
|
|
754
|
-
console.warn('AXUploaderZone.browser: fileType is required when disableBrowse is enabled (use axUploaderBrowseHandle).');
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
if (selected.length === 0 && !(rejected?.length ?? 0)) {
|
|
643
|
+
if (this.disableBrowse())
|
|
758
644
|
return;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const requests = catalog
|
|
773
|
-
? await this.uploadService.add(selected, { fileType: catalog })
|
|
774
|
-
: await this.uploadService.add(selected);
|
|
775
|
-
if (requests.length > 0) {
|
|
645
|
+
const files = await this.uploadService.browse({ accept: this.accept() ?? undefined, multiple: this.multiple() });
|
|
646
|
+
if (files.length > 0) {
|
|
647
|
+
// Create a synthetic event for consistency
|
|
648
|
+
const syntheticEvent = new Event('change');
|
|
649
|
+
const target = Object.create(EventTarget.prototype);
|
|
650
|
+
target.files = files.map((r) => r.file);
|
|
651
|
+
Object.defineProperty(syntheticEvent, 'target', { value: target, writable: false });
|
|
652
|
+
// Emit simple file change event (like HTML input)
|
|
653
|
+
this.fileChange.emit({
|
|
654
|
+
event: syntheticEvent,
|
|
655
|
+
files: files.map((r) => r.file),
|
|
656
|
+
});
|
|
657
|
+
// Also emit the old onChanged event for backward compatibility
|
|
776
658
|
this.onChanged.emit({
|
|
777
659
|
component: this,
|
|
778
|
-
requests,
|
|
660
|
+
requests: files,
|
|
779
661
|
isUserInteraction: true,
|
|
780
662
|
});
|
|
781
663
|
}
|
|
782
664
|
}
|
|
783
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
784
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "
|
|
665
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderZoneDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
666
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.3", type: AXUploaderZoneDirective, isStandalone: true, selector: "[axUploaderZone]", inputs: { multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, overlayTemplate: { classPropertyName: "overlayTemplate", publicName: "overlayTemplate", isSignal: true, isRequired: false, transformFunction: null }, disableBrowse: { classPropertyName: "disableBrowse", publicName: "disableBrowse", isSignal: true, isRequired: false, transformFunction: null }, disableDragDrop: { classPropertyName: "disableDragDrop", publicName: "disableDragDrop", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileChange: "fileChange", onChanged: "onChanged", dragEnter: "dragEnter", dragLeave: "dragLeave", dragOver: "dragOver", onFileUploadComplete: "onFileUploadComplete", onFilesUploadComplete: "onFilesUploadComplete" }, host: { classAttribute: "ax-drop-zone" }, providers: [AXUnsubscriber], ngImport: i0 }); }
|
|
785
667
|
}
|
|
786
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
668
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderZoneDirective, decorators: [{
|
|
787
669
|
type: Directive,
|
|
788
670
|
args: [{
|
|
789
671
|
selector: '[axUploaderZone]',
|
|
@@ -792,58 +674,66 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
|
|
|
792
674
|
class: 'ax-drop-zone',
|
|
793
675
|
},
|
|
794
676
|
}]
|
|
795
|
-
}], ctorParameters: () => [] });
|
|
677
|
+
}], ctorParameters: () => [], propDecorators: { multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], overlayTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "overlayTemplate", required: false }] }], disableBrowse: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableBrowse", required: false }] }], disableDragDrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableDragDrop", required: false }] }], fileChange: [{ type: i0.Output, args: ["fileChange"] }], onChanged: [{ type: i0.Output, args: ["onChanged"] }], dragEnter: [{ type: i0.Output, args: ["dragEnter"] }], dragLeave: [{ type: i0.Output, args: ["dragLeave"] }], dragOver: [{ type: i0.Output, args: ["dragOver"] }], onFileUploadComplete: [{ type: i0.Output, args: ["onFileUploadComplete"] }], onFilesUploadComplete: [{ type: i0.Output, args: ["onFilesUploadComplete"] }] } });
|
|
796
678
|
|
|
797
679
|
/**
|
|
798
|
-
*
|
|
799
|
-
*
|
|
680
|
+
* A directive that provides browse functionality for file uploads.
|
|
681
|
+
* When applied to an element, clicking it will trigger the file browser dialog.
|
|
800
682
|
* @category Directives
|
|
801
683
|
*/
|
|
802
684
|
class AXUploaderBrowseDirective {
|
|
803
685
|
constructor() {
|
|
804
|
-
|
|
686
|
+
/**
|
|
687
|
+
* The uploader zone directive instance.
|
|
688
|
+
* @ignore
|
|
689
|
+
*/
|
|
690
|
+
this.uploaderZone = inject(AXUploaderZoneDirective);
|
|
691
|
+
/**
|
|
692
|
+
* The element reference for the directive host.
|
|
693
|
+
* @ignore
|
|
694
|
+
*/
|
|
695
|
+
this.elementRef = inject((ElementRef));
|
|
696
|
+
/**
|
|
697
|
+
* Platform ID for browser detection.
|
|
698
|
+
* @ignore
|
|
699
|
+
*/
|
|
805
700
|
this.platformID = inject(PLATFORM_ID);
|
|
806
|
-
/** When browse + zone share the same host element. */
|
|
807
|
-
this.zoneFromInjector = inject(AXUploaderZoneDirective, { optional: true, self: true });
|
|
808
|
-
this.onClick = () => {
|
|
809
|
-
void this.handleClick();
|
|
810
|
-
};
|
|
811
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Initializes the directive by adding click event listener and data attribute.
|
|
704
|
+
*/
|
|
812
705
|
ngOnInit() {
|
|
813
|
-
if (
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
706
|
+
if (isPlatformBrowser(this.platformID) && this.elementRef?.nativeElement) {
|
|
707
|
+
const element = this.elementRef.nativeElement;
|
|
708
|
+
element.addEventListener('click', this.handleClick.bind(this));
|
|
709
|
+
// Use setAttribute for SSR compatibility
|
|
710
|
+
if (element.dataset) {
|
|
711
|
+
element.dataset['axUploaderBrowseHandle'] = 'true';
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
element.setAttribute('data-ax-uploader-browse-handle', 'true');
|
|
715
|
+
}
|
|
823
716
|
}
|
|
824
717
|
}
|
|
718
|
+
/**
|
|
719
|
+
* Cleans up the directive by removing event listeners.
|
|
720
|
+
*/
|
|
825
721
|
ngOnDestroy() {
|
|
826
|
-
if (isPlatformBrowser(this.platformID)) {
|
|
827
|
-
this.elementRef.nativeElement.removeEventListener('click', this.
|
|
722
|
+
if (isPlatformBrowser(this.platformID) && this.elementRef.nativeElement) {
|
|
723
|
+
this.elementRef.nativeElement.removeEventListener('click', this.handleClick.bind(this));
|
|
828
724
|
}
|
|
829
725
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
726
|
+
/**
|
|
727
|
+
* Handles the click event to trigger file browser.
|
|
728
|
+
* @private
|
|
729
|
+
*/
|
|
835
730
|
async handleClick() {
|
|
836
|
-
|
|
837
|
-
if (!zone) {
|
|
838
|
-
console.warn('[axUploaderBrowseHandle] No axUploaderZone found. Place axUploaderBrowseHandle inside an axUploaderZone.');
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
await zone.browser();
|
|
731
|
+
await this.uploaderZone.browser();
|
|
842
732
|
}
|
|
843
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
844
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
733
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderBrowseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
734
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: AXUploaderBrowseDirective, isStandalone: true, selector: "[axUploaderBrowseHandle]", ngImport: i0 }); }
|
|
845
735
|
}
|
|
846
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AXUploaderBrowseDirective, decorators: [{
|
|
847
737
|
type: Directive,
|
|
848
738
|
args: [{ selector: '[axUploaderBrowseHandle]' }]
|
|
849
739
|
}] });
|