@dile/crud 1.14.6 → 1.14.8
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/components/image-uploader/src/DileImageUploader.js +12 -2
- package/components/image-uploader/src/DileImageUploaderForm.js +101 -3
- package/lib/i18n/en.js +14 -0
- package/lib/i18n/es.js +14 -0
- package/lib/image/InvalidImageFileError.js +7 -0
- package/lib/image/buildValidationMessages.js +16 -0
- package/lib/image/buildValidationMessages.test.js +41 -0
- package/lib/image/createThumbnail.js +13 -0
- package/lib/image/formatFileSize.js +9 -0
- package/lib/image/formatFileSize.test.js +23 -0
- package/lib/image/readImageFile.js +23 -0
- package/lib/image/validateImageFile.js +18 -0
- package/lib/image/validateImageFile.test.js +54 -0
- package/package.json +4 -3
|
@@ -20,9 +20,14 @@ export class DileImageUploader extends DileI18nMixin(LitElement) {
|
|
|
20
20
|
saveLabel: { type: String },
|
|
21
21
|
allowedExtensions: { type: Array },
|
|
22
22
|
responseAdapter: { type: Object },
|
|
23
|
+
maxFileSize: { type: Number },
|
|
24
|
+
minWidth: { type: Number },
|
|
25
|
+
maxWidth: { type: Number },
|
|
26
|
+
minHeight: { type: Number },
|
|
27
|
+
maxHeight: { type: Number },
|
|
23
28
|
};
|
|
24
29
|
}
|
|
25
|
-
|
|
30
|
+
|
|
26
31
|
constructor() {
|
|
27
32
|
super();
|
|
28
33
|
this.responseAdapter = new ResponseApiAdapter();
|
|
@@ -41,11 +46,16 @@ export class DileImageUploader extends DileI18nMixin(LitElement) {
|
|
|
41
46
|
@save-success=${this.imageSaveSuccess}
|
|
42
47
|
.responseAdapter=${this.responseAdapter}
|
|
43
48
|
>
|
|
44
|
-
<dile-image-uploader-form
|
|
49
|
+
<dile-image-uploader-form
|
|
45
50
|
id="form"
|
|
46
51
|
label="${this.selectLabelComputed(this.selectImageLabel, this.translations)}"
|
|
47
52
|
.allowedExtensions=${this.allowedExtensions}
|
|
48
53
|
.translations=${this.translations}
|
|
54
|
+
.maxFileSize=${this.maxFileSize}
|
|
55
|
+
.minWidth=${this.minWidth}
|
|
56
|
+
.maxWidth=${this.maxWidth}
|
|
57
|
+
.minHeight=${this.minHeight}
|
|
58
|
+
.maxHeight=${this.maxHeight}
|
|
49
59
|
></dile-image-uploader-form>
|
|
50
60
|
</dile-ajax-form>
|
|
51
61
|
`;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { LitElement, html, css } from 'lit';
|
|
2
2
|
import { DileForm } from '@dile/ui/mixins/form/index.js'
|
|
3
3
|
import '@dile/ui/components/drop-file/drop-file.js';
|
|
4
|
+
import { readImageFile } from '../../../lib/image/readImageFile.js';
|
|
5
|
+
import { validateImageFile } from '../../../lib/image/validateImageFile.js';
|
|
6
|
+
import { createThumbnail } from '../../../lib/image/createThumbnail.js';
|
|
7
|
+
import { formatFileSize } from '../../../lib/image/formatFileSize.js';
|
|
8
|
+
import { buildValidationMessages } from '../../../lib/image/buildValidationMessages.js';
|
|
4
9
|
|
|
5
10
|
export class DileImageUploaderForm extends DileForm(LitElement) {
|
|
6
11
|
static styles = [
|
|
@@ -11,6 +16,19 @@ export class DileImageUploaderForm extends DileForm(LitElement) {
|
|
|
11
16
|
dile-drop-file {
|
|
12
17
|
margin-bottom: 0;
|
|
13
18
|
}
|
|
19
|
+
.image-summary {
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: 0.75rem;
|
|
23
|
+
margin: 0.5rem 0;
|
|
24
|
+
font-size: var(--dile-file-preview-font-size, 0.8rem);
|
|
25
|
+
}
|
|
26
|
+
.image-summary img {
|
|
27
|
+
max-width: 60px;
|
|
28
|
+
max-height: 60px;
|
|
29
|
+
border-radius: 4px;
|
|
30
|
+
display: block;
|
|
31
|
+
}
|
|
14
32
|
`
|
|
15
33
|
];
|
|
16
34
|
|
|
@@ -19,21 +37,101 @@ export class DileImageUploaderForm extends DileForm(LitElement) {
|
|
|
19
37
|
label: { type: String },
|
|
20
38
|
allowedExtensions: { type: Array },
|
|
21
39
|
translations: { type: Object },
|
|
40
|
+
maxFileSize: { type: Number },
|
|
41
|
+
minWidth: { type: Number },
|
|
42
|
+
maxWidth: { type: Number },
|
|
43
|
+
minHeight: { type: Number },
|
|
44
|
+
maxHeight: { type: Number },
|
|
45
|
+
imageInfo: { state: true },
|
|
22
46
|
};
|
|
23
47
|
}
|
|
24
48
|
|
|
49
|
+
constructor() {
|
|
50
|
+
super();
|
|
51
|
+
this.imageInfo = null;
|
|
52
|
+
this._inspectToken = 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
render() {
|
|
26
56
|
return html`
|
|
27
|
-
<dile-drop-file
|
|
57
|
+
<dile-drop-file
|
|
28
58
|
name="image"
|
|
29
|
-
label="${this.label}"
|
|
30
|
-
id="dropfile"
|
|
59
|
+
label="${this.label}"
|
|
60
|
+
id="dropfile"
|
|
31
61
|
.allowedExtensions=${this.allowedExtensions}
|
|
32
62
|
dropLabel="${this.translations.file_drop}"
|
|
33
63
|
buttonLabel="${this.translations.select_file}"
|
|
34
64
|
selectedFileLabel="${this.translations.selected_file}"
|
|
35
65
|
extensionErrorMessage="${this.translations.extension_allowed}"
|
|
66
|
+
@element-changed=${this.handleFileChange}
|
|
36
67
|
></dile-drop-file>
|
|
68
|
+
${this.imageSummaryTemplate}
|
|
37
69
|
`;
|
|
38
70
|
}
|
|
71
|
+
|
|
72
|
+
get imageSummaryTemplate() {
|
|
73
|
+
if (!this.imageInfo) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
return html`
|
|
77
|
+
<div class="image-summary">
|
|
78
|
+
<img src="${this.imageInfo.thumbnail}" alt="">
|
|
79
|
+
<span>${this.translations.image_dimensions_summary(this.imageInfo.width, this.imageInfo.height, formatFileSize(this.imageInfo.size))}</span>
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get dropFileEl() {
|
|
85
|
+
return this.renderRoot.querySelector('#dropfile');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async handleFileChange(e) {
|
|
89
|
+
const file = e.detail.value;
|
|
90
|
+
const token = ++this._inspectToken;
|
|
91
|
+
this.imageInfo = null;
|
|
92
|
+
|
|
93
|
+
if (!file) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let info;
|
|
98
|
+
try {
|
|
99
|
+
info = await readImageFile(file);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (token !== this._inspectToken) return;
|
|
102
|
+
const message = error.code === 'EMPTY_FILE'
|
|
103
|
+
? this.translations.image_empty_file
|
|
104
|
+
: this.translations.image_invalid_file;
|
|
105
|
+
this.rejectFile(message);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (token !== this._inspectToken) return;
|
|
110
|
+
|
|
111
|
+
const { valid, errors } = validateImageFile(info, {
|
|
112
|
+
maxFileSize: this.maxFileSize,
|
|
113
|
+
minWidth: this.minWidth,
|
|
114
|
+
maxWidth: this.maxWidth,
|
|
115
|
+
minHeight: this.minHeight,
|
|
116
|
+
maxHeight: this.maxHeight,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!valid) {
|
|
120
|
+
this.rejectFile(buildValidationMessages(errors, this.translations).join('. '));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.imageInfo = {
|
|
125
|
+
width: info.width,
|
|
126
|
+
height: info.height,
|
|
127
|
+
size: info.size,
|
|
128
|
+
thumbnail: createThumbnail(info.bitmap),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
rejectFile(message) {
|
|
133
|
+
this.dropFileEl.errored = true;
|
|
134
|
+
this.dropFileEl.message = message;
|
|
135
|
+
this.dropFileEl.clear();
|
|
136
|
+
}
|
|
39
137
|
}
|
package/lib/i18n/en.js
CHANGED
|
@@ -52,6 +52,20 @@ export const translations = {
|
|
|
52
52
|
select_file: "Select file",
|
|
53
53
|
selected_file: "Selected file",
|
|
54
54
|
extension_allowed: "Only this file extensions are allowed: ",
|
|
55
|
+
image_empty_file: "The selected file is empty",
|
|
56
|
+
image_invalid_file: "The selected file is not a valid image or it's corrupted",
|
|
57
|
+
image_file_too_large: (maxSizeLabel) => `The image exceeds the maximum allowed size (${maxSizeLabel})`,
|
|
58
|
+
image_width_error: (minWidth, maxWidth) => minWidth != null && maxWidth != null
|
|
59
|
+
? `Image width must be between ${minWidth} and ${maxWidth}px`
|
|
60
|
+
: minWidth != null
|
|
61
|
+
? `Image width must be at least ${minWidth}px`
|
|
62
|
+
: `Image width must be at most ${maxWidth}px`,
|
|
63
|
+
image_height_error: (minHeight, maxHeight) => minHeight != null && maxHeight != null
|
|
64
|
+
? `Image height must be between ${minHeight} and ${maxHeight}px`
|
|
65
|
+
: minHeight != null
|
|
66
|
+
? `Image height must be at least ${minHeight}px`
|
|
67
|
+
: `Image height must be at most ${maxHeight}px`,
|
|
68
|
+
image_dimensions_summary: (width, height, sizeLabel) => `${width} × ${height} px · ${sizeLabel}`,
|
|
55
69
|
confirm_submit_title: "Confirm submission",
|
|
56
70
|
confirm_submit_text: "Are you sure you want to submit this form?",
|
|
57
71
|
add_relation_label: "Add",
|
package/lib/i18n/es.js
CHANGED
|
@@ -52,6 +52,20 @@ export const translations = {
|
|
|
52
52
|
select_file: "Seleccionar archivo",
|
|
53
53
|
selected_file: "Archivo seleccionado",
|
|
54
54
|
extension_allowed: "Solo se permiten estas extensiones: ",
|
|
55
|
+
image_empty_file: "El archivo seleccionado está vacío",
|
|
56
|
+
image_invalid_file: "El archivo seleccionado no es una imagen válida o está corrupto",
|
|
57
|
+
image_file_too_large: (maxSizeLabel) => `La imagen supera el tamaño máximo permitido (${maxSizeLabel})`,
|
|
58
|
+
image_width_error: (minWidth, maxWidth) => minWidth != null && maxWidth != null
|
|
59
|
+
? `El ancho de la imagen debe estar entre ${minWidth} y ${maxWidth}px`
|
|
60
|
+
: minWidth != null
|
|
61
|
+
? `El ancho de la imagen debe ser de al menos ${minWidth}px`
|
|
62
|
+
: `El ancho de la imagen debe ser como máximo de ${maxWidth}px`,
|
|
63
|
+
image_height_error: (minHeight, maxHeight) => minHeight != null && maxHeight != null
|
|
64
|
+
? `El alto de la imagen debe estar entre ${minHeight} y ${maxHeight}px`
|
|
65
|
+
: minHeight != null
|
|
66
|
+
? `El alto de la imagen debe ser de al menos ${minHeight}px`
|
|
67
|
+
: `El alto de la imagen debe ser como máximo de ${maxHeight}px`,
|
|
68
|
+
image_dimensions_summary: (width, height, sizeLabel) => `${width} × ${height} px · ${sizeLabel}`,
|
|
55
69
|
confirm_submit_title: "Confirmar envío",
|
|
56
70
|
confirm_submit_text: "¿Estás seguro de que deseas enviar este formulario?",
|
|
57
71
|
add_relation_label: "Añadir",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { formatFileSize } from './formatFileSize.js';
|
|
2
|
+
|
|
3
|
+
export function buildValidationMessages(errors, translations) {
|
|
4
|
+
return errors.map(error => {
|
|
5
|
+
switch (error.code) {
|
|
6
|
+
case 'FILE_TOO_LARGE':
|
|
7
|
+
return translations.image_file_too_large(formatFileSize(error.maxFileSize));
|
|
8
|
+
case 'WIDTH_OUT_OF_RANGE':
|
|
9
|
+
return translations.image_width_error(error.minWidth, error.maxWidth);
|
|
10
|
+
case 'HEIGHT_OUT_OF_RANGE':
|
|
11
|
+
return translations.image_height_error(error.minHeight, error.maxHeight);
|
|
12
|
+
default:
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { buildValidationMessages } from './buildValidationMessages.js';
|
|
4
|
+
|
|
5
|
+
const translations = {
|
|
6
|
+
image_file_too_large: (maxSizeLabel) => `Too large, max ${maxSizeLabel}`,
|
|
7
|
+
image_width_error: (min, max) => `Width must be between ${min} and ${max}`,
|
|
8
|
+
image_height_error: (min, max) => `Height must be between ${min} and ${max}`,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
test('builds message for FILE_TOO_LARGE using formatted size', () => {
|
|
12
|
+
const messages = buildValidationMessages([{ code: 'FILE_TOO_LARGE', maxFileSize: 2048 }], translations);
|
|
13
|
+
assert.deepEqual(messages, ['Too large, max 2.0 KB']);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('builds message for WIDTH_OUT_OF_RANGE', () => {
|
|
17
|
+
const messages = buildValidationMessages(
|
|
18
|
+
[{ code: 'WIDTH_OUT_OF_RANGE', minWidth: 200, maxWidth: 1000 }],
|
|
19
|
+
translations
|
|
20
|
+
);
|
|
21
|
+
assert.deepEqual(messages, ['Width must be between 200 and 1000']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('builds message for HEIGHT_OUT_OF_RANGE', () => {
|
|
25
|
+
const messages = buildValidationMessages(
|
|
26
|
+
[{ code: 'HEIGHT_OUT_OF_RANGE', minHeight: 200, maxHeight: 1000 }],
|
|
27
|
+
translations
|
|
28
|
+
);
|
|
29
|
+
assert.deepEqual(messages, ['Height must be between 200 and 1000']);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('builds messages for multiple errors in order', () => {
|
|
33
|
+
const messages = buildValidationMessages(
|
|
34
|
+
[
|
|
35
|
+
{ code: 'FILE_TOO_LARGE', maxFileSize: 1024 },
|
|
36
|
+
{ code: 'WIDTH_OUT_OF_RANGE', minWidth: 100, maxWidth: 200 },
|
|
37
|
+
],
|
|
38
|
+
translations
|
|
39
|
+
);
|
|
40
|
+
assert.equal(messages.length, 2);
|
|
41
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function createThumbnail(bitmap, maxSize = 120) {
|
|
2
|
+
const scale = Math.min(1, maxSize / Math.max(bitmap.width, bitmap.height));
|
|
3
|
+
const width = Math.round(bitmap.width * scale);
|
|
4
|
+
const height = Math.round(bitmap.height * scale);
|
|
5
|
+
|
|
6
|
+
const canvas = document.createElement('canvas');
|
|
7
|
+
canvas.width = width;
|
|
8
|
+
canvas.height = height;
|
|
9
|
+
const context = canvas.getContext('2d');
|
|
10
|
+
context.drawImage(bitmap, 0, 0, width, height);
|
|
11
|
+
|
|
12
|
+
return canvas.toDataURL('image/png');
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function formatFileSize(bytes) {
|
|
2
|
+
if (bytes === 0) {
|
|
3
|
+
return '0 B';
|
|
4
|
+
}
|
|
5
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
6
|
+
const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
|
|
7
|
+
const value = bytes / Math.pow(1024, exponent);
|
|
8
|
+
return `${exponent === 0 ? value : value.toFixed(1)} ${units[exponent]}`;
|
|
9
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { formatFileSize } from './formatFileSize.js';
|
|
4
|
+
|
|
5
|
+
test('formats 0 bytes', () => {
|
|
6
|
+
assert.equal(formatFileSize(0), '0 B');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('formats bytes below 1 KB', () => {
|
|
10
|
+
assert.equal(formatFileSize(512), '512 B');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('formats kilobytes', () => {
|
|
14
|
+
assert.equal(formatFileSize(2048), '2.0 KB');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('formats megabytes', () => {
|
|
18
|
+
assert.equal(formatFileSize(2_411_724), '2.3 MB');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('formats gigabytes', () => {
|
|
22
|
+
assert.equal(formatFileSize(1_610_612_736), '1.5 GB');
|
|
23
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { InvalidImageFileError } from './InvalidImageFileError.js';
|
|
2
|
+
|
|
3
|
+
export async function readImageFile(file) {
|
|
4
|
+
if (!file || file.size === 0) {
|
|
5
|
+
throw new InvalidImageFileError('EMPTY_FILE', 'The file is empty');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let bitmap;
|
|
9
|
+
try {
|
|
10
|
+
bitmap = await createImageBitmap(file);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
throw new InvalidImageFileError('DECODE_ERROR', 'The file could not be read as a valid image');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
name: file.name,
|
|
17
|
+
size: file.size,
|
|
18
|
+
type: file.type,
|
|
19
|
+
width: bitmap.width,
|
|
20
|
+
height: bitmap.height,
|
|
21
|
+
bitmap,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function validateImageFile(info, constraints = {}) {
|
|
2
|
+
const { maxFileSize, minWidth, maxWidth, minHeight, maxHeight } = constraints;
|
|
3
|
+
const errors = [];
|
|
4
|
+
|
|
5
|
+
if (maxFileSize != null && info.size > maxFileSize) {
|
|
6
|
+
errors.push({ code: 'FILE_TOO_LARGE', maxFileSize, actual: info.size });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if ((minWidth != null && info.width < minWidth) || (maxWidth != null && info.width > maxWidth)) {
|
|
10
|
+
errors.push({ code: 'WIDTH_OUT_OF_RANGE', minWidth, maxWidth, actual: info.width });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if ((minHeight != null && info.height < minHeight) || (maxHeight != null && info.height > maxHeight)) {
|
|
14
|
+
errors.push({ code: 'HEIGHT_OUT_OF_RANGE', minHeight, maxHeight, actual: info.height });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { valid: errors.length === 0, errors };
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { validateImageFile } from './validateImageFile.js';
|
|
4
|
+
|
|
5
|
+
const baseInfo = { size: 1000, width: 800, height: 600 };
|
|
6
|
+
|
|
7
|
+
test('valid when no constraints are set', () => {
|
|
8
|
+
const result = validateImageFile(baseInfo, {});
|
|
9
|
+
assert.equal(result.valid, true);
|
|
10
|
+
assert.deepEqual(result.errors, []);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('rejects files over maxFileSize', () => {
|
|
14
|
+
const result = validateImageFile(baseInfo, { maxFileSize: 500 });
|
|
15
|
+
assert.equal(result.valid, false);
|
|
16
|
+
assert.equal(result.errors[0].code, 'FILE_TOO_LARGE');
|
|
17
|
+
assert.equal(result.errors[0].maxFileSize, 500);
|
|
18
|
+
assert.equal(result.errors[0].actual, 1000);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('rejects width below minWidth', () => {
|
|
22
|
+
const result = validateImageFile(baseInfo, { minWidth: 1000 });
|
|
23
|
+
assert.equal(result.valid, false);
|
|
24
|
+
assert.equal(result.errors[0].code, 'WIDTH_OUT_OF_RANGE');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('rejects width above maxWidth', () => {
|
|
28
|
+
const result = validateImageFile(baseInfo, { maxWidth: 400 });
|
|
29
|
+
assert.equal(result.valid, false);
|
|
30
|
+
assert.equal(result.errors[0].code, 'WIDTH_OUT_OF_RANGE');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('rejects height out of range', () => {
|
|
34
|
+
const result = validateImageFile(baseInfo, { minHeight: 700, maxHeight: 900 });
|
|
35
|
+
assert.equal(result.valid, false);
|
|
36
|
+
assert.equal(result.errors[0].code, 'HEIGHT_OUT_OF_RANGE');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('accumulates multiple errors', () => {
|
|
40
|
+
const result = validateImageFile(baseInfo, { maxFileSize: 10, maxWidth: 10, maxHeight: 10 });
|
|
41
|
+
assert.equal(result.valid, false);
|
|
42
|
+
assert.equal(result.errors.length, 3);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('passes when dimensions and size are within range', () => {
|
|
46
|
+
const result = validateImageFile(baseInfo, {
|
|
47
|
+
maxFileSize: 2000,
|
|
48
|
+
minWidth: 200,
|
|
49
|
+
maxWidth: 1000,
|
|
50
|
+
minHeight: 200,
|
|
51
|
+
maxHeight: 1000,
|
|
52
|
+
});
|
|
53
|
+
assert.equal(result.valid, true);
|
|
54
|
+
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/crud",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.8",
|
|
4
4
|
"description": "Components to create a generic crud system based on Web Components and Lit",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
9
|
},
|
|
@@ -24,12 +25,12 @@
|
|
|
24
25
|
},
|
|
25
26
|
"homepage": "https://dile-components.com/",
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"@dile/ui": "^2.
|
|
28
|
+
"@dile/ui": "^2.27.0",
|
|
28
29
|
"axios": "^1.16.0",
|
|
29
30
|
"lit": "^2.7.0 || ^3.0.0"
|
|
30
31
|
},
|
|
31
32
|
"publishConfig": {
|
|
32
33
|
"access": "public"
|
|
33
34
|
},
|
|
34
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "5c04395f81c01fd289869b1b99d427d482c516a5"
|
|
35
36
|
}
|