@acorex/core 21.0.2-next.4 → 21.0.2-next.41
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-core-components.mjs +3 -3
- package/fesm2022/acorex-core-config.mjs +3 -3
- package/fesm2022/acorex-core-date-time.mjs +105 -91
- package/fesm2022/acorex-core-date-time.mjs.map +1 -1
- package/fesm2022/acorex-core-events.mjs +3 -3
- package/fesm2022/acorex-core-file.mjs +667 -92
- package/fesm2022/acorex-core-file.mjs.map +1 -1
- package/fesm2022/acorex-core-format.mjs +19 -19
- package/fesm2022/acorex-core-full-screen.mjs +4 -4
- package/fesm2022/acorex-core-full-screen.mjs.map +1 -1
- package/fesm2022/acorex-core-icon.mjs +3 -3
- package/fesm2022/acorex-core-image.mjs +3 -3
- package/fesm2022/acorex-core-locale.mjs +30 -13
- package/fesm2022/acorex-core-locale.mjs.map +1 -1
- package/fesm2022/acorex-core-network.mjs +4 -4
- package/fesm2022/acorex-core-network.mjs.map +1 -1
- package/fesm2022/acorex-core-pipes.mjs +3 -3
- package/fesm2022/acorex-core-platform.mjs +4 -4
- package/fesm2022/acorex-core-platform.mjs.map +1 -1
- package/fesm2022/acorex-core-storage.mjs +9 -9
- package/fesm2022/acorex-core-translation.mjs +68 -24
- package/fesm2022/acorex-core-translation.mjs.map +1 -1
- package/fesm2022/acorex-core-utils.mjs +3 -78
- package/fesm2022/acorex-core-utils.mjs.map +1 -1
- package/fesm2022/acorex-core-validation.mjs +40 -40
- package/fesm2022/acorex-core-z-index.mjs +3 -3
- package/package.json +3 -2
- package/types/acorex-core-date-time.d.ts +24 -20
- package/types/acorex-core-file.d.ts +268 -40
- package/types/acorex-core-locale.d.ts +5 -0
- package/types/acorex-core-translation.d.ts +8 -1
- package/types/acorex-core-utils.d.ts +0 -22
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable,
|
|
2
|
+
import { Injectable, InjectionToken, inject, NgModule, DOCUMENT, isDevMode } from '@angular/core';
|
|
3
|
+
import * as i2 from '@acorex/core/validation';
|
|
4
|
+
import { AXValidationService, AXValidationRegistryService, AXValidationModule } from '@acorex/core/validation';
|
|
3
5
|
import * as i1 from '@acorex/core/format';
|
|
4
6
|
import { AXFormatModule } from '@acorex/core/format';
|
|
5
7
|
|
|
@@ -29,24 +31,585 @@ class AXFileSizeFormatter {
|
|
|
29
31
|
const i = Math.floor(Math.log(size) / Math.log(k));
|
|
30
32
|
return parseFloat((size / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
31
33
|
}
|
|
32
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
33
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
34
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileSizeFormatter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
35
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileSizeFormatter }); }
|
|
34
36
|
}
|
|
35
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
37
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileSizeFormatter, decorators: [{
|
|
36
38
|
type: Injectable
|
|
37
39
|
}] });
|
|
38
40
|
|
|
41
|
+
/** Human-readable file size (binary units, e.g. `41.5 MB`). */
|
|
42
|
+
function formatFileSizeBytes(bytes) {
|
|
43
|
+
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
44
|
+
return '0 B';
|
|
45
|
+
}
|
|
46
|
+
if (bytes === 0) {
|
|
47
|
+
return '0 B';
|
|
48
|
+
}
|
|
49
|
+
const k = 1024;
|
|
50
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
51
|
+
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), units.length - 1);
|
|
52
|
+
const value = bytes / Math.pow(k, i);
|
|
53
|
+
if (i === 0) {
|
|
54
|
+
return `${bytes} B`;
|
|
55
|
+
}
|
|
56
|
+
const digits = value >= 100 ? 0 : value >= 10 ? 1 : 2;
|
|
57
|
+
return `${value.toFixed(digits)} ${units[i]}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Resolves clipboard/plain text from a copy result. */
|
|
61
|
+
function resolveFileCopyText(result) {
|
|
62
|
+
if (result == null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
if (typeof result === 'string') {
|
|
66
|
+
const trimmed = result.trim();
|
|
67
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
68
|
+
}
|
|
69
|
+
const trimmed = result.text?.trim();
|
|
70
|
+
return trimmed && trimmed.length > 0 ? trimmed : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Contributes {@link AXFileType} entries (multi provider). */
|
|
74
|
+
class AXFileTypeInfoProvider {
|
|
75
|
+
}
|
|
76
|
+
const AX_FILE_TYPE_INFO_PROVIDER = new InjectionToken('AX_FILE_TYPE_INFO_PROVIDER');
|
|
77
|
+
function provideFileTypeInfoProvider(provider) {
|
|
78
|
+
return {
|
|
79
|
+
provide: AX_FILE_TYPE_INFO_PROVIDER,
|
|
80
|
+
useClass: provider,
|
|
81
|
+
multi: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Merges type-level validations with extension overrides (extension wins per field). */
|
|
86
|
+
function mergeFileValidations(base, override) {
|
|
87
|
+
if (!override) {
|
|
88
|
+
return { ...base };
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
mimeTypes: override.mimeTypes ?? base.mimeTypes,
|
|
92
|
+
minSize: override.minSize ?? base.minSize,
|
|
93
|
+
maxSize: override.maxSize ?? base.maxSize,
|
|
94
|
+
rules: override.rules ?? base.rules,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Resolved validations for a type + optional extension. */
|
|
98
|
+
function resolveFileValidations(type, extension) {
|
|
99
|
+
return mergeFileValidations(type.validations ?? {}, extension?.validations);
|
|
100
|
+
}
|
|
101
|
+
/** True when no mime/size/rules are defined on the type. */
|
|
102
|
+
function isEmptyFileValidations(validations) {
|
|
103
|
+
if (!validations) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return ((validations.mimeTypes?.length ?? 0) === 0 &&
|
|
107
|
+
validations.minSize == null &&
|
|
108
|
+
validations.maxSize == null &&
|
|
109
|
+
(validations.rules?.length ?? 0) === 0);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Extension-only mode: empty type `validations` and a non-empty `extensions` list.
|
|
113
|
+
* Files must match a listed extension; rules come from that extension entry.
|
|
114
|
+
*/
|
|
115
|
+
function isExtensionsOnlyType(type) {
|
|
116
|
+
return isEmptyFileValidations(type.validations) && (type.extensions?.length ?? 0) > 0;
|
|
117
|
+
}
|
|
118
|
+
/** File extension from name (lowercase, no dot). */
|
|
119
|
+
function getFileExtension(fileName) {
|
|
120
|
+
const parts = fileName.trim().split('.');
|
|
121
|
+
return parts.length > 1 ? (parts.pop()?.toLowerCase() ?? '') : '';
|
|
122
|
+
}
|
|
123
|
+
/** Matching extension entry for a file within a type, if any. */
|
|
124
|
+
function findFileTypeExtension(file, type) {
|
|
125
|
+
const ext = getFileExtension(file.name);
|
|
126
|
+
if (!ext || !type.extensions?.length) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
return type.extensions.find((e) => e.name.toLowerCase() === ext);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Maps {@link AXFileValidations} to executable validation entries. */
|
|
133
|
+
function fileValidationsToRules(validations) {
|
|
134
|
+
const rules = [];
|
|
135
|
+
if (validations.mimeTypes?.length) {
|
|
136
|
+
rules.push({
|
|
137
|
+
rule: 'fileMimeType',
|
|
138
|
+
options: { allowed: validations.mimeTypes },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (validations.minSize != null) {
|
|
142
|
+
rules.push({
|
|
143
|
+
rule: 'fileMinSize',
|
|
144
|
+
options: { minSize: validations.minSize },
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (validations.maxSize != null) {
|
|
148
|
+
rules.push({
|
|
149
|
+
rule: 'fileMaxSize',
|
|
150
|
+
options: { maxSize: validations.maxSize },
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (validations.rules?.length) {
|
|
154
|
+
rules.push(...validations.rules);
|
|
155
|
+
}
|
|
156
|
+
return rules;
|
|
157
|
+
}
|
|
158
|
+
/** Failed validation results; empty array means the file is valid. */
|
|
159
|
+
async function validateFileWithValidations(validation, file, validations) {
|
|
160
|
+
const entries = fileValidationsToRules(validations);
|
|
161
|
+
const errors = [];
|
|
162
|
+
for (const { rule, options } of entries) {
|
|
163
|
+
const result = await validation.validate(rule, file, options);
|
|
164
|
+
if (!result.result) {
|
|
165
|
+
errors.push(result);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return errors;
|
|
169
|
+
}
|
|
170
|
+
/** Validates a file against a type; extension rules override when the file name matches. */
|
|
171
|
+
async function validateFileAgainstType(validation, file, type, extension) {
|
|
172
|
+
const matchedExtension = extension ?? findFileTypeExtension(file, type);
|
|
173
|
+
if (isExtensionsOnlyType(type) && !matchedExtension) {
|
|
174
|
+
const ext = getFileExtension(file.name);
|
|
175
|
+
return [
|
|
176
|
+
{
|
|
177
|
+
rule: 'fileExtension',
|
|
178
|
+
result: false,
|
|
179
|
+
message: ext ? `Extension ".${ext}" is not allowed.` : 'File must have an allowed extension.',
|
|
180
|
+
value: ext,
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
const resolved = resolveFileValidations(type, matchedExtension);
|
|
185
|
+
return validateFileWithValidations(validation, file, resolved);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
class AXFileTypeRegistryService {
|
|
189
|
+
constructor() {
|
|
190
|
+
this.validation = inject(AXValidationService);
|
|
191
|
+
this.providers = inject(AX_FILE_TYPE_INFO_PROVIDER, { optional: true }) ?? [];
|
|
192
|
+
this.registeredTypes = [];
|
|
193
|
+
this.cache = null;
|
|
194
|
+
}
|
|
195
|
+
/** Registers or replaces catalog entries (e.g. from feature bootstrap). */
|
|
196
|
+
registerTypes(types) {
|
|
197
|
+
for (const type of types) {
|
|
198
|
+
const index = this.registeredTypes.findIndex((t) => t.name === type.name);
|
|
199
|
+
if (index >= 0) {
|
|
200
|
+
this.registeredTypes[index] = type;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
this.registeredTypes.push(type);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
this.invalidateCache();
|
|
207
|
+
}
|
|
208
|
+
invalidateCache() {
|
|
209
|
+
this.cache = null;
|
|
210
|
+
}
|
|
211
|
+
async resolveTypes() {
|
|
212
|
+
if (this.cache) {
|
|
213
|
+
return this.cache;
|
|
214
|
+
}
|
|
215
|
+
const byName = new Map();
|
|
216
|
+
if (this.providers.length) {
|
|
217
|
+
const batches = await Promise.all(this.providers.map((p) => p.items()));
|
|
218
|
+
for (const batch of batches) {
|
|
219
|
+
for (const type of batch) {
|
|
220
|
+
if (!byName.has(type.name)) {
|
|
221
|
+
byName.set(type.name, type);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const type of this.registeredTypes) {
|
|
227
|
+
byName.set(type.name, type);
|
|
228
|
+
}
|
|
229
|
+
this.cache = [...byName.values()];
|
|
230
|
+
return this.cache;
|
|
231
|
+
}
|
|
232
|
+
async getTypes() {
|
|
233
|
+
return this.resolveTypes();
|
|
234
|
+
}
|
|
235
|
+
async get(name) {
|
|
236
|
+
const types = await this.resolveTypes();
|
|
237
|
+
return types.find((t) => t.name === name);
|
|
238
|
+
}
|
|
239
|
+
async accept(typeNames) {
|
|
240
|
+
const names = typeNames ? (Array.isArray(typeNames) ? typeNames : [typeNames]) : undefined;
|
|
241
|
+
const types = await this.resolveTypes();
|
|
242
|
+
const selected = names?.length ? types.filter((t) => names.includes(t.name)) : types;
|
|
243
|
+
if (names?.length && selected.length === 0) {
|
|
244
|
+
return '';
|
|
245
|
+
}
|
|
246
|
+
const parts = new Set();
|
|
247
|
+
for (const type of selected) {
|
|
248
|
+
type.validations?.mimeTypes?.forEach((m) => parts.add(m));
|
|
249
|
+
type.extensions?.forEach((ext) => {
|
|
250
|
+
ext.validations?.mimeTypes?.forEach((m) => parts.add(m));
|
|
251
|
+
parts.add(`.${ext.name.replace(/^\./, '')}`);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return [...parts].join(',');
|
|
255
|
+
}
|
|
256
|
+
async matchType(file) {
|
|
257
|
+
const types = await this.resolveTypes();
|
|
258
|
+
const mime = file.type.trim().toLowerCase();
|
|
259
|
+
return types.find((type) => {
|
|
260
|
+
const patterns = [
|
|
261
|
+
...(type.validations?.mimeTypes ?? []),
|
|
262
|
+
...(type.extensions?.flatMap((e) => e.validations?.mimeTypes ?? []) ?? []),
|
|
263
|
+
];
|
|
264
|
+
if (!patterns.length) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
return patterns.some((pattern) => this.matchesMime(mime, pattern.trim().toLowerCase()));
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async validate(file, typeName) {
|
|
271
|
+
const type = await this.get(typeName);
|
|
272
|
+
if (!type) {
|
|
273
|
+
return [
|
|
274
|
+
{
|
|
275
|
+
rule: 'fileType',
|
|
276
|
+
result: false,
|
|
277
|
+
message: `Unknown file type: ${typeName}`,
|
|
278
|
+
value: typeName,
|
|
279
|
+
},
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
const extension = findFileTypeExtension(file, type);
|
|
283
|
+
return validateFileAgainstType(this.validation, file, type, extension);
|
|
284
|
+
}
|
|
285
|
+
async validateAgainstTypes(file, typeNames) {
|
|
286
|
+
const names = Array.isArray(typeNames) ? typeNames : [typeNames];
|
|
287
|
+
let lastErrors = [];
|
|
288
|
+
for (const name of names) {
|
|
289
|
+
const errors = await this.validate(file, name);
|
|
290
|
+
if (errors.length === 0) {
|
|
291
|
+
return [];
|
|
292
|
+
}
|
|
293
|
+
lastErrors = errors;
|
|
294
|
+
}
|
|
295
|
+
return lastErrors;
|
|
296
|
+
}
|
|
297
|
+
async validateMany(files, typeNames) {
|
|
298
|
+
const accepted = [];
|
|
299
|
+
const rejected = [];
|
|
300
|
+
for (const file of files) {
|
|
301
|
+
const errors = await this.validateAgainstTypes(file, typeNames);
|
|
302
|
+
if (errors.length === 0) {
|
|
303
|
+
accepted.push(file);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
rejected.push({ file, errors });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return { accepted, rejected };
|
|
310
|
+
}
|
|
311
|
+
matchesMime(mime, pattern) {
|
|
312
|
+
if (!pattern || pattern === '*/*' || pattern === '*') {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
if (pattern.endsWith('/*')) {
|
|
316
|
+
const category = pattern.slice(0, -2);
|
|
317
|
+
return mime.startsWith(`${category}/`) || (mime === '' && category === 'application');
|
|
318
|
+
}
|
|
319
|
+
return mime === pattern;
|
|
320
|
+
}
|
|
321
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileTypeRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
322
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileTypeRegistryService, providedIn: 'root' }); }
|
|
323
|
+
}
|
|
324
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileTypeRegistryService, decorators: [{
|
|
325
|
+
type: Injectable,
|
|
326
|
+
args: [{ providedIn: 'root' }]
|
|
327
|
+
}] });
|
|
328
|
+
|
|
329
|
+
const MB$4 = 1024 * 1024;
|
|
330
|
+
const AX_AUDIO_FILE_TYPE = {
|
|
331
|
+
name: 'audio',
|
|
332
|
+
title: 'Audio',
|
|
333
|
+
icon: 'fa-light fa-music ax-text-amber-500',
|
|
334
|
+
validations: {
|
|
335
|
+
mimeTypes: ['audio/*'],
|
|
336
|
+
minSize: 1,
|
|
337
|
+
maxSize: 50 * MB$4,
|
|
338
|
+
},
|
|
339
|
+
extensions: [
|
|
340
|
+
{ name: 'mp3', title: 'MP3' },
|
|
341
|
+
{ name: 'wav', title: 'WAV', validations: { maxSize: 30 * MB$4 } },
|
|
342
|
+
{ name: 'ogg', title: 'OGG' },
|
|
343
|
+
{ name: 'm4a', title: 'M4A' },
|
|
344
|
+
],
|
|
345
|
+
};
|
|
346
|
+
class AXAudioFileTypeProvider extends AXFileTypeInfoProvider {
|
|
347
|
+
items() {
|
|
348
|
+
return Promise.resolve([AX_AUDIO_FILE_TYPE]);
|
|
349
|
+
}
|
|
350
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXAudioFileTypeProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
351
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXAudioFileTypeProvider }); }
|
|
352
|
+
}
|
|
353
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXAudioFileTypeProvider, decorators: [{
|
|
354
|
+
type: Injectable
|
|
355
|
+
}] });
|
|
356
|
+
|
|
357
|
+
const MB$3 = 1024 * 1024;
|
|
358
|
+
/** Extension-only: no type `validations` — only listed extensions are allowed. */
|
|
359
|
+
const AX_DOCUMENT_FILE_TYPE = {
|
|
360
|
+
name: 'document',
|
|
361
|
+
title: 'Document',
|
|
362
|
+
icon: 'fa-light fa-file-lines ax-text-red-500',
|
|
363
|
+
extensions: [{ name: 'pdf', title: 'PDF', validations: { mimeTypes: ['application/pdf'], maxSize: 5 * MB$3 } }],
|
|
364
|
+
};
|
|
365
|
+
class AXDocumentFileTypeProvider extends AXFileTypeInfoProvider {
|
|
366
|
+
items() {
|
|
367
|
+
return Promise.resolve([AX_DOCUMENT_FILE_TYPE]);
|
|
368
|
+
}
|
|
369
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXDocumentFileTypeProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
370
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXDocumentFileTypeProvider }); }
|
|
371
|
+
}
|
|
372
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXDocumentFileTypeProvider, decorators: [{
|
|
373
|
+
type: Injectable
|
|
374
|
+
}] });
|
|
375
|
+
|
|
376
|
+
const MB$2 = 1024 * 1024;
|
|
377
|
+
const AX_FILE_FILE_TYPE = {
|
|
378
|
+
name: 'file',
|
|
379
|
+
title: 'File',
|
|
380
|
+
icon: 'fa-light fa-file ax-text-neutral-500',
|
|
381
|
+
validations: {
|
|
382
|
+
mimeTypes: ['application/*', 'text/*'],
|
|
383
|
+
minSize: 1,
|
|
384
|
+
maxSize: 100 * MB$2,
|
|
385
|
+
},
|
|
386
|
+
extensions: [
|
|
387
|
+
{ name: 'pdf', title: 'PDF', validations: { mimeTypes: ['application/pdf'], maxSize: 25 * MB$2 } },
|
|
388
|
+
{ name: 'doc', title: 'Word' },
|
|
389
|
+
{ name: 'docx', title: 'Word' },
|
|
390
|
+
{ name: 'txt', title: 'Text', validations: { mimeTypes: ['text/plain'], maxSize: 5 * MB$2 } },
|
|
391
|
+
{ name: 'zip', title: 'ZIP', validations: { maxSize: 50 * MB$2 } },
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
class AXFileFileTypeProvider extends AXFileTypeInfoProvider {
|
|
395
|
+
items() {
|
|
396
|
+
return Promise.resolve([AX_FILE_FILE_TYPE]);
|
|
397
|
+
}
|
|
398
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileFileTypeProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
399
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileFileTypeProvider }); }
|
|
400
|
+
}
|
|
401
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileFileTypeProvider, decorators: [{
|
|
402
|
+
type: Injectable
|
|
403
|
+
}] });
|
|
404
|
+
|
|
405
|
+
const MB$1 = 1024 * 1024;
|
|
406
|
+
const AX_IMAGE_FILE_TYPE = {
|
|
407
|
+
name: 'image',
|
|
408
|
+
title: 'Image',
|
|
409
|
+
icon: 'fa-light fa-image ax-text-purple-500',
|
|
410
|
+
validations: {
|
|
411
|
+
mimeTypes: ['image/*'],
|
|
412
|
+
minSize: 1,
|
|
413
|
+
maxSize: 100 * MB$1,
|
|
414
|
+
},
|
|
415
|
+
extensions: [
|
|
416
|
+
{ name: 'jpg', title: 'JPEG' },
|
|
417
|
+
{ name: 'jpeg', title: 'JPEG', validations: { maxSize: 5 * MB$1 } },
|
|
418
|
+
{ name: 'png', title: 'PNG' },
|
|
419
|
+
{ name: 'gif', title: 'GIF' },
|
|
420
|
+
{ name: 'webp', title: 'WebP' },
|
|
421
|
+
{ name: 'svg', title: 'SVG', validations: { maxSize: 2 * MB$1 } },
|
|
422
|
+
],
|
|
423
|
+
};
|
|
424
|
+
class AXImageFileTypeProvider extends AXFileTypeInfoProvider {
|
|
425
|
+
items() {
|
|
426
|
+
return Promise.resolve([AX_IMAGE_FILE_TYPE]);
|
|
427
|
+
}
|
|
428
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXImageFileTypeProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
429
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXImageFileTypeProvider }); }
|
|
430
|
+
}
|
|
431
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXImageFileTypeProvider, decorators: [{
|
|
432
|
+
type: Injectable
|
|
433
|
+
}] });
|
|
434
|
+
|
|
435
|
+
const MB = 1024 * 1024;
|
|
436
|
+
const AX_VIDEO_FILE_TYPE = {
|
|
437
|
+
name: 'video',
|
|
438
|
+
title: 'Video',
|
|
439
|
+
icon: 'fa-light fa-video ax-text-blue-500',
|
|
440
|
+
validations: {
|
|
441
|
+
mimeTypes: ['video/*'],
|
|
442
|
+
minSize: 1,
|
|
443
|
+
maxSize: 500 * MB,
|
|
444
|
+
},
|
|
445
|
+
extensions: [
|
|
446
|
+
{ name: 'mp4', title: 'MP4' },
|
|
447
|
+
{ name: 'webm', title: 'WebM', validations: { maxSize: 200 * MB } },
|
|
448
|
+
{ name: 'ogg', title: 'OGG' },
|
|
449
|
+
],
|
|
450
|
+
};
|
|
451
|
+
class AXVideoFileTypeProvider extends AXFileTypeInfoProvider {
|
|
452
|
+
items() {
|
|
453
|
+
return Promise.resolve([AX_VIDEO_FILE_TYPE]);
|
|
454
|
+
}
|
|
455
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXVideoFileTypeProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
456
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXVideoFileTypeProvider }); }
|
|
457
|
+
}
|
|
458
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXVideoFileTypeProvider, decorators: [{
|
|
459
|
+
type: Injectable
|
|
460
|
+
}] });
|
|
461
|
+
|
|
462
|
+
/** Built-in file type provider classes (one logical type per provider). */
|
|
463
|
+
const AX_BUILTIN_FILE_TYPE_PROVIDERS = [
|
|
464
|
+
AXImageFileTypeProvider,
|
|
465
|
+
AXVideoFileTypeProvider,
|
|
466
|
+
AXAudioFileTypeProvider,
|
|
467
|
+
AXFileFileTypeProvider,
|
|
468
|
+
AXDocumentFileTypeProvider,
|
|
469
|
+
];
|
|
470
|
+
/** Registers all built-in file type providers (image, video, audio, file, document). */
|
|
471
|
+
function provideBuiltinFileTypeProviders() {
|
|
472
|
+
return AX_BUILTIN_FILE_TYPE_PROVIDERS.map((provider) => provideFileTypeInfoProvider(provider));
|
|
473
|
+
}
|
|
474
|
+
/** @deprecated Use {@link provideBuiltinFileTypeProviders}. */
|
|
475
|
+
const provideDefaultFileTypeProviders = provideBuiltinFileTypeProviders;
|
|
476
|
+
|
|
477
|
+
class AXFileMimeTypeValidationRule {
|
|
478
|
+
get name() {
|
|
479
|
+
return 'fileMimeType';
|
|
480
|
+
}
|
|
481
|
+
async validate(value, options) {
|
|
482
|
+
const mime = (value instanceof File || value instanceof Blob ? value.type : '').trim().toLowerCase();
|
|
483
|
+
const allowed = options.allowed ?? [];
|
|
484
|
+
const valid = allowed.length === 0 ||
|
|
485
|
+
allowed.some((pattern) => this.matchesMime(mime, pattern.trim().toLowerCase()));
|
|
486
|
+
return {
|
|
487
|
+
rule: this.name,
|
|
488
|
+
result: valid,
|
|
489
|
+
message: valid ? '' : (options.message ?? 'File type is not allowed.'),
|
|
490
|
+
value: mime,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
matchesMime(mime, pattern) {
|
|
494
|
+
if (!pattern || pattern === '*/*' || pattern === '*') {
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
if (pattern.endsWith('/*')) {
|
|
498
|
+
const category = pattern.slice(0, -2);
|
|
499
|
+
return mime.startsWith(`${category}/`) || (mime === '' && category === 'application');
|
|
500
|
+
}
|
|
501
|
+
return mime === pattern;
|
|
502
|
+
}
|
|
503
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMimeTypeValidationRule, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
504
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMimeTypeValidationRule }); }
|
|
505
|
+
}
|
|
506
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMimeTypeValidationRule, decorators: [{
|
|
507
|
+
type: Injectable
|
|
508
|
+
}] });
|
|
509
|
+
|
|
510
|
+
class AXFileMinSizeValidationRule {
|
|
511
|
+
get name() {
|
|
512
|
+
return 'fileMinSize';
|
|
513
|
+
}
|
|
514
|
+
async validate(value, options) {
|
|
515
|
+
const size = value instanceof File || value instanceof Blob ? value.size : 0;
|
|
516
|
+
const valid = size >= options.minSize;
|
|
517
|
+
const limitLabel = formatFileSizeBytes(options.minSize);
|
|
518
|
+
return {
|
|
519
|
+
rule: this.name,
|
|
520
|
+
result: valid,
|
|
521
|
+
message: valid
|
|
522
|
+
? ''
|
|
523
|
+
: (options.message ?? `File is too small. Minimum size is ${limitLabel}.`),
|
|
524
|
+
value: size,
|
|
525
|
+
minSize: options.minSize,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMinSizeValidationRule, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
529
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMinSizeValidationRule }); }
|
|
530
|
+
}
|
|
531
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMinSizeValidationRule, decorators: [{
|
|
532
|
+
type: Injectable
|
|
533
|
+
}] });
|
|
534
|
+
|
|
535
|
+
class AXFileMaxSizeValidationRule {
|
|
536
|
+
get name() {
|
|
537
|
+
return 'fileMaxSize';
|
|
538
|
+
}
|
|
539
|
+
async validate(value, options) {
|
|
540
|
+
const size = value instanceof File || value instanceof Blob ? value.size : 0;
|
|
541
|
+
const valid = size <= options.maxSize;
|
|
542
|
+
const limitLabel = formatFileSizeBytes(options.maxSize);
|
|
543
|
+
return {
|
|
544
|
+
rule: this.name,
|
|
545
|
+
result: valid,
|
|
546
|
+
message: valid
|
|
547
|
+
? ''
|
|
548
|
+
: (options.message ??
|
|
549
|
+
`File is too large. Maximum size is ${limitLabel}.`),
|
|
550
|
+
value: size,
|
|
551
|
+
maxSize: options.maxSize,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMaxSizeValidationRule, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
555
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMaxSizeValidationRule }); }
|
|
556
|
+
}
|
|
557
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileMaxSizeValidationRule, decorators: [{
|
|
558
|
+
type: Injectable
|
|
559
|
+
}] });
|
|
560
|
+
|
|
561
|
+
/** Validation rule classes to register with {@link AXValidationModule}. */
|
|
562
|
+
const AX_FILE_VALIDATION_RULES = [
|
|
563
|
+
AXFileMimeTypeValidationRule,
|
|
564
|
+
AXFileMinSizeValidationRule,
|
|
565
|
+
AXFileMaxSizeValidationRule,
|
|
566
|
+
];
|
|
567
|
+
/** Registers file validation rules with {@link AXValidationRegistryService}. */
|
|
568
|
+
function provideFileValidationRules() {
|
|
569
|
+
return {
|
|
570
|
+
provide: 'AXValidationModuleFactory',
|
|
571
|
+
useFactory: (registry) => () => {
|
|
572
|
+
registry.register(...AX_FILE_VALIDATION_RULES);
|
|
573
|
+
},
|
|
574
|
+
deps: [AXValidationRegistryService],
|
|
575
|
+
multi: true,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Registers validation rules and optional built-in file type providers.
|
|
580
|
+
* {@link AXFileTypeRegistryService} and {@link AXFileService} are `providedIn: 'root'`.
|
|
581
|
+
*/
|
|
582
|
+
function provideFileTypeSystem() {
|
|
583
|
+
return [provideFileValidationRules(), ...provideBuiltinFileTypeProviders()];
|
|
584
|
+
}
|
|
585
|
+
|
|
39
586
|
class AXFileModule {
|
|
40
|
-
static
|
|
41
|
-
|
|
42
|
-
|
|
587
|
+
static forRoot(config) {
|
|
588
|
+
const includeDefaults = config?.includeDefaultFileTypes !== false;
|
|
589
|
+
return {
|
|
590
|
+
ngModule: AXFileModule,
|
|
591
|
+
providers: [
|
|
592
|
+
provideFileValidationRules(),
|
|
593
|
+
...(includeDefaults ? provideDefaultFileTypeProviders() : []),
|
|
594
|
+
...(config?.fileTypeProviders ?? []),
|
|
595
|
+
],
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
static forChild(config) {
|
|
599
|
+
return AXFileModule.forRoot(config);
|
|
600
|
+
}
|
|
601
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
602
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXFileModule, imports: [i1.AXFormatModule, i2.AXValidationModule] }); }
|
|
603
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileModule, providers: [AXFileSizeFormatter], imports: [AXFormatModule.forChild({ formatters: [AXFileSizeFormatter] }),
|
|
604
|
+
AXValidationModule.forChild({ rules: [...AX_FILE_VALIDATION_RULES] })] }); }
|
|
43
605
|
}
|
|
44
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
606
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileModule, decorators: [{
|
|
45
607
|
type: NgModule,
|
|
46
608
|
args: [{
|
|
47
|
-
imports: [
|
|
48
|
-
|
|
49
|
-
|
|
609
|
+
imports: [
|
|
610
|
+
AXFormatModule.forChild({ formatters: [AXFileSizeFormatter] }),
|
|
611
|
+
AXValidationModule.forChild({ rules: [...AX_FILE_VALIDATION_RULES] }),
|
|
612
|
+
],
|
|
50
613
|
providers: [AXFileSizeFormatter],
|
|
51
614
|
}]
|
|
52
615
|
}] });
|
|
@@ -54,112 +617,124 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
54
617
|
class AXFileService {
|
|
55
618
|
constructor() {
|
|
56
619
|
this.document = inject(DOCUMENT);
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return Promise.reject('input is not blob');
|
|
77
|
-
}
|
|
78
|
-
};
|
|
620
|
+
this.fileTypes = inject(AXFileTypeRegistryService);
|
|
621
|
+
}
|
|
622
|
+
async getFileTypes() {
|
|
623
|
+
return this.fileTypes.getTypes();
|
|
624
|
+
}
|
|
625
|
+
async getFileType(name) {
|
|
626
|
+
return this.fileTypes.get(name);
|
|
627
|
+
}
|
|
628
|
+
async getAcceptAttribute(fileType) {
|
|
629
|
+
return this.fileTypes.accept(fileType);
|
|
630
|
+
}
|
|
631
|
+
async matchFileType(file) {
|
|
632
|
+
return this.fileTypes.matchType(file);
|
|
633
|
+
}
|
|
634
|
+
validate(file, fileType) {
|
|
635
|
+
return this.fileTypes.validateAgainstTypes(file, fileType);
|
|
636
|
+
}
|
|
637
|
+
validateMany(files, fileType) {
|
|
638
|
+
return this.fileTypes.validateMany(files, fileType);
|
|
79
639
|
}
|
|
80
640
|
/**
|
|
81
|
-
* Opens a file selection dialog
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* @param options.accept - File types to accept (e.g., 'image/*', '.pdf')
|
|
85
|
-
* @param options.multiple - Whether to allow multiple file selection
|
|
86
|
-
* @returns Promise<File[]> - Promise that resolves with the selected files
|
|
641
|
+
* Opens a file selection dialog.
|
|
642
|
+
* With `fileType`, returns files that pass catalog validation (`accept` defaults from the catalog).
|
|
643
|
+
* Without `fileType`, returns all selected files (legacy `accept` / `multiple` only).
|
|
87
644
|
*/
|
|
88
|
-
|
|
645
|
+
async chooseValidated(options) {
|
|
646
|
+
const resolved = await this.resolveChooseOptions(options);
|
|
647
|
+
const files = await this.openFileDialog(resolved);
|
|
648
|
+
if (files.length === 0) {
|
|
649
|
+
return { accepted: [], rejected: [] };
|
|
650
|
+
}
|
|
651
|
+
if (options.fileType) {
|
|
652
|
+
return this.validateMany(files, options.fileType);
|
|
653
|
+
}
|
|
654
|
+
return { accepted: files, rejected: [] };
|
|
655
|
+
}
|
|
656
|
+
async choose(options) {
|
|
657
|
+
const { accepted } = await this.chooseValidated(options);
|
|
658
|
+
return accepted;
|
|
659
|
+
}
|
|
660
|
+
blobToBase64(blob) {
|
|
661
|
+
if (!(blob instanceof Blob)) {
|
|
662
|
+
return Promise.reject(new Error('input is not blob'));
|
|
663
|
+
}
|
|
89
664
|
return new Promise((resolve, reject) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
input.type = 'file';
|
|
95
|
-
input.accept = options.accept || '';
|
|
96
|
-
input.multiple = options.multiple || false;
|
|
97
|
-
//
|
|
98
|
-
const onError = (e) => {
|
|
99
|
-
reject(e);
|
|
100
|
-
this.document.body.removeChild(input);
|
|
101
|
-
input.remove();
|
|
102
|
-
input.removeEventListener('change', onChange);
|
|
103
|
-
input.removeEventListener('error', onError);
|
|
104
|
-
};
|
|
105
|
-
//
|
|
106
|
-
const onChange = () => {
|
|
107
|
-
resolve(Array.from(input.files || []));
|
|
108
|
-
this.document.body.removeChild(input);
|
|
109
|
-
input.remove();
|
|
110
|
-
input.removeEventListener('change', onChange);
|
|
111
|
-
input.removeEventListener('error', onError);
|
|
112
|
-
};
|
|
113
|
-
//
|
|
114
|
-
input.addEventListener('change', onChange);
|
|
115
|
-
input.addEventListener('error', onError);
|
|
116
|
-
input.click();
|
|
665
|
+
const reader = new FileReader();
|
|
666
|
+
reader.onerror = () => reject(reader.error);
|
|
667
|
+
reader.onload = () => resolve(reader.result);
|
|
668
|
+
reader.readAsDataURL(blob);
|
|
117
669
|
});
|
|
118
670
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Gets the size of a file, blob, or base64 string.
|
|
121
|
-
*
|
|
122
|
-
* @param file - The file, blob, or base64 string to get size for
|
|
123
|
-
* @returns number | false - The size in bytes or false if not a base64 string
|
|
124
|
-
*/
|
|
125
671
|
getSize(file) {
|
|
126
672
|
if (this.isBase64(file)) {
|
|
127
673
|
return this.getBase64Size(file);
|
|
128
674
|
}
|
|
129
675
|
return false;
|
|
130
676
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Checks if a string is a valid base64 string.
|
|
133
|
-
*
|
|
134
|
-
* @param base64 - The string to check
|
|
135
|
-
* @returns boolean - True if the string is a valid base64 string
|
|
136
|
-
*/
|
|
137
677
|
isBase64(base64) {
|
|
138
678
|
const regx = /[^:]\w+\/[\w-+\d.]+(?=;|,)/;
|
|
139
679
|
return regx.test(base64);
|
|
140
680
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Gets the size of a base64 string in bytes.
|
|
143
|
-
*
|
|
144
|
-
* @param base64 - The base64 string to get size for
|
|
145
|
-
* @returns number - The size in bytes
|
|
146
|
-
*/
|
|
147
681
|
getBase64Size(base64) {
|
|
148
682
|
return atob(base64.substring(base64.indexOf(',') + 1)).length;
|
|
149
683
|
}
|
|
150
|
-
|
|
151
|
-
|
|
684
|
+
async resolveChooseOptions(options) {
|
|
685
|
+
const multiple = options.multiple ?? false;
|
|
686
|
+
if (options.fileType) {
|
|
687
|
+
const accept = options.accept ?? (await this.getAcceptAttribute(options.fileType));
|
|
688
|
+
if (isDevMode() && !accept) {
|
|
689
|
+
const name = Array.isArray(options.fileType) ? options.fileType.join(', ') : options.fileType;
|
|
690
|
+
console.warn(`[AXFileService] No accept filter for file type "${name}". ` +
|
|
691
|
+
'Register a file type provider (e.g. provideConversationFileCatalog).');
|
|
692
|
+
}
|
|
693
|
+
return { accept, multiple };
|
|
694
|
+
}
|
|
695
|
+
return { accept: options.accept ?? '', multiple };
|
|
696
|
+
}
|
|
697
|
+
openFileDialog(options) {
|
|
698
|
+
return new Promise((resolve, reject) => {
|
|
699
|
+
const input = this.document.createElement('input');
|
|
700
|
+
input.style.display = 'none';
|
|
701
|
+
this.document.body.appendChild(input);
|
|
702
|
+
input.type = 'file';
|
|
703
|
+
input.accept = options.accept;
|
|
704
|
+
input.multiple = options.multiple;
|
|
705
|
+
const cleanup = () => {
|
|
706
|
+
input.removeEventListener('change', onChange);
|
|
707
|
+
input.removeEventListener('error', onError);
|
|
708
|
+
if (input.parentNode) {
|
|
709
|
+
this.document.body.removeChild(input);
|
|
710
|
+
}
|
|
711
|
+
input.remove();
|
|
712
|
+
};
|
|
713
|
+
const onError = (e) => {
|
|
714
|
+
cleanup();
|
|
715
|
+
reject(e);
|
|
716
|
+
};
|
|
717
|
+
const onChange = () => {
|
|
718
|
+
const files = Array.from(input.files ?? []);
|
|
719
|
+
cleanup();
|
|
720
|
+
resolve(files);
|
|
721
|
+
};
|
|
722
|
+
input.addEventListener('change', onChange);
|
|
723
|
+
input.addEventListener('error', onError);
|
|
724
|
+
input.click();
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
728
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileService, providedIn: 'root' }); }
|
|
152
729
|
}
|
|
153
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
730
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXFileService, decorators: [{
|
|
154
731
|
type: Injectable,
|
|
155
|
-
args: [{
|
|
156
|
-
providedIn: 'root',
|
|
157
|
-
}]
|
|
732
|
+
args: [{ providedIn: 'root' }]
|
|
158
733
|
}] });
|
|
159
734
|
|
|
160
735
|
/**
|
|
161
736
|
* Generated bundle index. Do not edit.
|
|
162
737
|
*/
|
|
163
738
|
|
|
164
|
-
export { AXFileModule, AXFileService, AXFileSizeFormatter };
|
|
739
|
+
export { AXAudioFileTypeProvider, AXDocumentFileTypeProvider, AXFileFileTypeProvider, AXFileMaxSizeValidationRule, AXFileMimeTypeValidationRule, AXFileMinSizeValidationRule, AXFileModule, AXFileService, AXFileSizeFormatter, AXFileTypeInfoProvider, AXFileTypeRegistryService, AXImageFileTypeProvider, AXVideoFileTypeProvider, AX_AUDIO_FILE_TYPE, AX_BUILTIN_FILE_TYPE_PROVIDERS, AX_DOCUMENT_FILE_TYPE, AX_FILE_FILE_TYPE, AX_FILE_TYPE_INFO_PROVIDER, AX_FILE_VALIDATION_RULES, AX_IMAGE_FILE_TYPE, AX_VIDEO_FILE_TYPE, fileValidationsToRules, findFileTypeExtension, formatFileSizeBytes, getFileExtension, isEmptyFileValidations, isExtensionsOnlyType, mergeFileValidations, provideBuiltinFileTypeProviders, provideDefaultFileTypeProviders, provideFileTypeInfoProvider, provideFileTypeSystem, provideFileValidationRules, resolveFileCopyText, resolveFileValidations, validateFileAgainstType, validateFileWithValidations };
|
|
165
740
|
//# sourceMappingURL=acorex-core-file.mjs.map
|