@madgex/design-system 13.5.1 → 13.6.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.
- package/dist/assets/icons.json +1 -1
- package/dist/css/index.css +1 -1
- package/dist/js/components/mds-image-cropper-standalone.js +1 -1
- package/dist/js/{image-cropper-BlqRRHAU.js → image-cropper-BUN9gxO4.js} +1 -1
- package/dist/js/index-fractal.js +1 -1
- package/dist/js/index.js +1 -1
- package/package.json +1 -1
- package/src/components/_macro-index.njk +1 -0
- package/src/components/image-cropper/README.md +39 -6
- package/src/components/image-cropper/_macro.njk +3 -0
- package/src/components/image-cropper/_template.njk +24 -0
- package/src/components/image-cropper/image-cropper.js +2 -1
- package/src/components/image-cropper/image-cropper.scss +7 -0
- package/src/components/inputs/file-upload/README.md +15 -7
- package/src/components/inputs/file-upload/_template.njk +52 -37
- package/src/components/inputs/file-upload/file-upload.config.js +56 -2
- package/src/components/inputs/file-upload/file-upload.js +166 -56
- package/src/components/inputs/file-upload/file-upload.njk +5 -2
- package/src/components/inputs/file-upload/file-upload.scss +98 -106
- package/src/components/inputs/text-editor/_template.njk +1 -1
- package/src/components/inputs/text-editor/text-editor.config.js +1 -1
- package/src/components/modal/modal.js +2 -2
- package/src/js/index.js +4 -2
|
@@ -7,11 +7,18 @@ module.exports = {
|
|
|
7
7
|
name: 'file',
|
|
8
8
|
id: 'file',
|
|
9
9
|
fileTypes: '.doc,.docx,.pdf,.rtf',
|
|
10
|
+
cropper: {
|
|
11
|
+
i18n: {
|
|
12
|
+
tBtnReset: 'Cancel',
|
|
13
|
+
tBtnCommit: 'Crop and save',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
10
16
|
},
|
|
11
17
|
variants: [
|
|
12
18
|
{
|
|
13
19
|
name: 'Selected file version',
|
|
14
20
|
context: {
|
|
21
|
+
labelText: 'Selected file version',
|
|
15
22
|
value: 'myCV.doc',
|
|
16
23
|
name: 'selected-file',
|
|
17
24
|
id: 'selected-file',
|
|
@@ -24,13 +31,60 @@ module.exports = {
|
|
|
24
31
|
name: 'Errored file input',
|
|
25
32
|
context: {
|
|
26
33
|
labelText: 'Error with uploaded file',
|
|
27
|
-
name: 'selected-file',
|
|
28
|
-
id: 'selected-file',
|
|
34
|
+
name: 'selected-file-with-error',
|
|
35
|
+
id: 'selected-file-with-error',
|
|
29
36
|
validationError: 'There was an error',
|
|
30
37
|
i18n: {
|
|
31
38
|
fallbackSelectedFileText: 'Use my saved CV or upload a different one',
|
|
32
39
|
},
|
|
33
40
|
},
|
|
34
41
|
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Image upload 320px x 160px',
|
|
44
|
+
context: {
|
|
45
|
+
labelText: 'Image upload 320px x 160px',
|
|
46
|
+
name: 'image-upload',
|
|
47
|
+
id: 'image-upload',
|
|
48
|
+
helpText: 'Your file must be a .jpg or.png. No larger than 244KB',
|
|
49
|
+
fileTypes: '.jpg,.png',
|
|
50
|
+
i18n: {
|
|
51
|
+
fallbackSelectedFileText: 'Add an image',
|
|
52
|
+
},
|
|
53
|
+
cropper: {
|
|
54
|
+
outputWidth: 320,
|
|
55
|
+
outputHeight: 160,
|
|
56
|
+
restrictPosition: false,
|
|
57
|
+
minZoom: '0.5',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'Image upload - No auto open image cropper modal',
|
|
63
|
+
context: {
|
|
64
|
+
labelText: 'Image upload - No auto open image cropper modal',
|
|
65
|
+
name: 'image-upload-no-auto-open',
|
|
66
|
+
id: 'image-upload-no-auto-open',
|
|
67
|
+
helpText: 'Your file must be a .jpg or.png. No larger than 244KB',
|
|
68
|
+
fileTypes: '.jpg,.png',
|
|
69
|
+
i18n: {
|
|
70
|
+
fallbackSelectedFileText: 'Add an image',
|
|
71
|
+
},
|
|
72
|
+
noAutoOpenImageCropper: true,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'Image upload without cropper',
|
|
77
|
+
context: {
|
|
78
|
+
labelText: 'Image upload without cropper',
|
|
79
|
+
name: 'image-upload-no-cropper',
|
|
80
|
+
id: 'image-upload-no-cropper',
|
|
81
|
+
helpText: 'Your file must be a .jpg or.png. No larger than 244KB',
|
|
82
|
+
fileTypes: '.jpg,.png',
|
|
83
|
+
i18n: {
|
|
84
|
+
fallbackSelectedFileText: 'Add an image',
|
|
85
|
+
},
|
|
86
|
+
noImageCropper: true,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
35
89
|
],
|
|
36
90
|
};
|
|
@@ -1,63 +1,173 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* MdsFileUpload
|
|
3
|
+
*
|
|
4
|
+
* Requires HTML rendered by MdsFileUpload Nunjucks macro.
|
|
5
|
+
*
|
|
6
|
+
*Image Cropper and Modal are optionally rendered by Nunjucks, this Web Component will gracefully continue if they do not exist.
|
|
7
|
+
*/
|
|
8
|
+
export class MdsFileUpload extends HTMLElement {
|
|
9
|
+
static selectedFileClass = 'mds-file-upload--selected-file';
|
|
10
|
+
static selectedFileIsImageClass = 'mds-file-upload--selected-file-is-image';
|
|
11
|
+
static dragOverClass = 'mds-file-upload--dragover';
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
connectedCallback() {
|
|
17
|
+
this.classList.add('mds-form-element--file-supported'); // useless?
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
this.#elInput.addEventListener('change', this.#onChangeInput);
|
|
20
|
+
this.#elRemoveButton.addEventListener('click', this.#onClickRemoveButton);
|
|
21
|
+
this.#elInput.addEventListener('dragover', this.#onDragOver);
|
|
22
|
+
this.#elInput.addEventListener('dragenter', this.#onDragOver);
|
|
23
|
+
this.#elInput.addEventListener('dragleave', this.#onDragEnd);
|
|
24
|
+
this.#elInput.addEventListener('dragend', this.#onDragEnd);
|
|
25
|
+
this.#elInput.addEventListener('drop', this.#onDragEnd);
|
|
26
|
+
this.#imageCropper?.addEventListener('commit', this.#onCommitImageCropper);
|
|
27
|
+
this.#imageCropper?.addEventListener('reset', this.#onResetImageCropper);
|
|
18
28
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
this.#syncFileInputPopulated(true);
|
|
30
|
+
}
|
|
31
|
+
disconnectedCallback() {
|
|
32
|
+
this.#elInput?.removeEventListener('change', this.#onChangeInput);
|
|
33
|
+
this.#elRemoveButton?.removeEventListener('click', this.#onClickRemoveButton);
|
|
34
|
+
this.#elInput?.removeEventListener('dragover', this.#onDragOver);
|
|
35
|
+
this.#elInput?.removeEventListener('dragenter', this.#onDragOver);
|
|
36
|
+
this.#elInput?.removeEventListener('dragleave', this.#onDragEnd);
|
|
37
|
+
this.#elInput?.removeEventListener('dragend', this.#onDragEnd);
|
|
38
|
+
this.#elInput?.removeEventListener('drop', this.#onDragEnd);
|
|
39
|
+
this.#imageCropper?.removeEventListener('commit', this.#onCommitImageCropper);
|
|
40
|
+
this.#imageCropper?.removeEventListener('reset', this.#onResetImageCropper);
|
|
41
|
+
}
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
get rootNode() {
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
get #document() {
|
|
47
|
+
return this.getRootNode({ composed: true });
|
|
48
|
+
}
|
|
49
|
+
/** @type {File?} the file populated (we only handle single file) */
|
|
50
|
+
get #file() {
|
|
51
|
+
return this.#elInput?.files?.[0];
|
|
52
|
+
}
|
|
53
|
+
get #isFileImage() {
|
|
54
|
+
return this.#file?.type?.startsWith('image/');
|
|
55
|
+
}
|
|
56
|
+
/* Attributes */
|
|
57
|
+
get #noAutoOpenImageCropper() {
|
|
58
|
+
const attr = this.rootNode.getAttribute('no-auto-open-image-cropper');
|
|
59
|
+
return attr !== null && attr !== 'false';
|
|
60
|
+
}
|
|
61
|
+
/* Elements */
|
|
62
|
+
get #elFileNameContainer() {
|
|
63
|
+
return this.rootNode.querySelector('.mds-file-upload__file-name');
|
|
64
|
+
}
|
|
65
|
+
get #elInput() {
|
|
66
|
+
return this.rootNode.querySelector('input[type=file]');
|
|
67
|
+
}
|
|
68
|
+
get #elRemoveButton() {
|
|
69
|
+
return this.rootNode.querySelector('.mds-file-upload__remove-button');
|
|
70
|
+
}
|
|
71
|
+
get #elPreviewImage() {
|
|
72
|
+
return this.rootNode.querySelector('.mds-file-upload__preview-image');
|
|
73
|
+
}
|
|
74
|
+
get #imageCropperModalTriggerButton() {
|
|
75
|
+
return this.rootNode.querySelector('.mds-file-upload__open-modal');
|
|
76
|
+
}
|
|
77
|
+
get #imageCropperModal() {
|
|
78
|
+
return this.rootNode.querySelector('.mds-modal');
|
|
79
|
+
}
|
|
80
|
+
get #imageCropper() {
|
|
81
|
+
return this.rootNode.querySelector('mds-image-cropper');
|
|
82
|
+
}
|
|
33
83
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
e.stopPropagation();
|
|
45
|
-
uploader.classList.add(dragOverClass);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
['dragleave', 'dragend', 'drop'].forEach((event) => {
|
|
50
|
-
input.addEventListener(event, (e) => {
|
|
51
|
-
e.stopPropagation();
|
|
52
|
-
uploader.classList.remove(dragOverClass);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
});
|
|
84
|
+
/* Event handlers */
|
|
85
|
+
#onChangeInput = (e) => {
|
|
86
|
+
const { dontOpenModal = false } = e?.detail || {};
|
|
87
|
+
this.#syncFileInputPopulated();
|
|
88
|
+
if (this.#file) {
|
|
89
|
+
this.#elRemoveButton.focus();
|
|
90
|
+
}
|
|
91
|
+
if (!dontOpenModal && this.#isFileImage && !this.#noAutoOpenImageCropper) {
|
|
92
|
+
// automatically open image cropper modal if file is image
|
|
93
|
+
this.#openModal();
|
|
56
94
|
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
95
|
+
};
|
|
96
|
+
#onClickRemoveButton = (e) => {
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
this.removeFile();
|
|
99
|
+
this.#elInput.focus();
|
|
100
|
+
};
|
|
101
|
+
#onDragOver = (e) => {
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
this.classList.add(MdsFileUpload.dragOverClass);
|
|
104
|
+
};
|
|
105
|
+
#onDragEnd = (e) => {
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
this.classList.remove(MdsFileUpload.dragOverClass);
|
|
108
|
+
};
|
|
109
|
+
/** on receiving a new Blob from image cropper, update file input with new data */
|
|
110
|
+
#onCommitImageCropper = (e) => {
|
|
111
|
+
/** @type {Blob?} */
|
|
112
|
+
const blob = e.detail;
|
|
113
|
+
if (blob) {
|
|
114
|
+
// load blob back into fileinput using DataTransfer
|
|
115
|
+
const croppedFile = new File([blob], this.#file?.name || 'cropped-image', { type: blob.type });
|
|
116
|
+
const dataTransfer = new DataTransfer();
|
|
117
|
+
dataTransfer.items.add(croppedFile);
|
|
118
|
+
this.#elInput.files = dataTransfer.files;
|
|
119
|
+
// trigger 'change' as if a user selected a file, which in turn will run `#onChangeInput`
|
|
120
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
121
|
+
this.#elInput.dispatchEvent(new CustomEvent('change', { detail: { dontOpenModal: true }, bubbles: true }));
|
|
122
|
+
// close modal
|
|
123
|
+
this.#closeModal();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
#onResetImageCropper = () => {
|
|
127
|
+
// close modal - TODO: convert modal js into web component so we can call `close` method instead of knowing this classname?
|
|
128
|
+
this.#closeModal();
|
|
129
|
+
};
|
|
62
130
|
|
|
63
|
-
|
|
131
|
+
/* Methods */
|
|
132
|
+
/**
|
|
133
|
+
* sync filename, image preview and selected state based on `this.#elInput` files
|
|
134
|
+
* @param {boolean} [dontResetState=false] used on connectedCallback, if there is no real file, we still want to display a populated state!
|
|
135
|
+
* */
|
|
136
|
+
#syncFileInputPopulated(dontResetState = false) {
|
|
137
|
+
if (this.#file) {
|
|
138
|
+
// has file
|
|
139
|
+
this.#elFileNameContainer.textContent = this.#file.name;
|
|
140
|
+
this.classList.add(MdsFileUpload.selectedFileClass);
|
|
141
|
+
// if file is image, load and show preview, populate cropper src
|
|
142
|
+
if (this.#isFileImage) {
|
|
143
|
+
this.classList.add(MdsFileUpload.selectedFileIsImageClass);
|
|
144
|
+
const reader = new FileReader();
|
|
145
|
+
reader.onload = (e) => {
|
|
146
|
+
this.#elPreviewImage.src = e.target.result;
|
|
147
|
+
this.#imageCropper?.setAttribute('src', e.target.result);
|
|
148
|
+
};
|
|
149
|
+
reader.readAsDataURL(this.#file);
|
|
150
|
+
} else {
|
|
151
|
+
this.classList.remove(MdsFileUpload.selectedFileIsImageClass);
|
|
152
|
+
}
|
|
153
|
+
} else if (!dontResetState) {
|
|
154
|
+
// no file, empty state
|
|
155
|
+
this.#elFileNameContainer.textContent = '';
|
|
156
|
+
this.classList.remove(MdsFileUpload.selectedFileClass);
|
|
157
|
+
this.classList.remove(MdsFileUpload.selectedFileIsImageClass);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
#openModal() {
|
|
161
|
+
// TODO: When `modal code` is Web Component, use that instead of triggering click events on DOM elements...
|
|
162
|
+
this.#imageCropperModalTriggerButton?.dispatchEvent(new Event('click'));
|
|
163
|
+
}
|
|
164
|
+
#closeModal() {
|
|
165
|
+
// TODO: When `modal code` is Web Component, use that instead of triggering click events on DOM elements...
|
|
166
|
+
this.#imageCropperModal?.querySelector('.js-mds-modal-close').dispatchEvent(new Event('click'));
|
|
167
|
+
}
|
|
168
|
+
/** remove file from input */
|
|
169
|
+
removeFile() {
|
|
170
|
+
this.#elInput.value = '';
|
|
171
|
+
this.#elInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{% from "./inputs/file-upload/_macro.njk" import MdsFileUpload %}
|
|
2
|
-
|
|
3
2
|
<div class="mds-grid-row">
|
|
4
3
|
<div class="mds-grid-col-12 mds-grid-col-lg-9">
|
|
5
4
|
<div class="mds-form-field">
|
|
@@ -18,7 +17,11 @@
|
|
|
18
17
|
classes: classes,
|
|
19
18
|
tooltipMessage: tooltipMessage,
|
|
20
19
|
fileTypes: fileTypes,
|
|
21
|
-
i18n: i18n
|
|
20
|
+
i18n: i18n,
|
|
21
|
+
siteContainerId:siteContainerId,
|
|
22
|
+
noImageCropper: noImageCropper,
|
|
23
|
+
noAutoOpenImageCropper: noAutoOpenImageCropper,
|
|
24
|
+
cropper: cropper
|
|
22
25
|
}) }}
|
|
23
26
|
</div>
|
|
24
27
|
</div>
|
|
@@ -1,137 +1,129 @@
|
|
|
1
|
-
|
|
1
|
+
/* container for fancy file upload, hidden without `.js` */
|
|
2
|
+
.mds-file-upload__wrapper {
|
|
2
3
|
display: none;
|
|
3
4
|
}
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
padding: $constant-size-baseline;
|
|
10
|
-
|
|
11
|
-
@include mq($from: $constant-size-breakpoint-md) {
|
|
12
|
-
flex-direction: row;
|
|
6
|
+
/* all styling is only relevant if JS is enabled */
|
|
7
|
+
.js {
|
|
8
|
+
.mds-file-upload__selected-file-fallback {
|
|
9
|
+
display: none;
|
|
13
10
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
.js .mds-form-element--file-supported .mds-file-upload__input {
|
|
17
|
-
position: relative;
|
|
18
|
-
border: 1px dashed $constant-color-neutral-lighter;
|
|
19
|
-
width: 100%;
|
|
20
|
-
padding: ($constant-size-baseline * 4) ($constant-size-baseline * 2);
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
justify-content: center;
|
|
24
|
-
|
|
25
|
-
& .mds-form-label {
|
|
11
|
+
.mds-file-upload {
|
|
26
12
|
position: relative;
|
|
27
|
-
@include z-index;
|
|
28
13
|
display: block;
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
background-color: $constant-color-neutral-lightest;
|
|
15
|
+
max-width: 100%;
|
|
16
|
+
width: 100%;
|
|
17
|
+
padding: $constant-size-baseline;
|
|
18
|
+
.mds-form-label {
|
|
19
|
+
position: relative;
|
|
20
|
+
@include z-index;
|
|
21
|
+
display: block;
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
margin-bottom: 0;
|
|
24
|
+
}
|
|
25
|
+
.mds-form-label__label {
|
|
26
|
+
@extend .mds-button;
|
|
27
|
+
@extend .mds-button--small;
|
|
28
|
+
font-weight: normal;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
.mds-file-upload__wrapper {
|
|
32
|
+
display: block;
|
|
33
|
+
padding: ($constant-size-baseline * 3);
|
|
34
|
+
border: 1px dashed $constant-color-neutral-lighter;
|
|
35
|
+
}
|
|
36
|
+
.mds-file-upload--dragover {
|
|
37
|
+
.mds-file-upload__wrapper {
|
|
38
|
+
background-color: #fff;
|
|
39
|
+
}
|
|
40
|
+
.mds-form-label {
|
|
41
|
+
pointer-events: none; // avoid label blocking drag action
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
.mds-file-upload__state {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
}
|
|
48
|
+
.mds-file-upload__state--no-file {
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
row-gap: $constant-size-baseline * 5;
|
|
51
|
+
& .mds-icon {
|
|
52
|
+
fill: var(--mds-color-button-bg-base, #000);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
.mds-file-upload__state--selected {
|
|
56
|
+
display: none;
|
|
57
|
+
}
|
|
58
|
+
.mds-file-upload__preview .mds-file-upload__preview-image {
|
|
59
|
+
display: none;
|
|
60
|
+
max-width: 100%;
|
|
61
|
+
max-height: 100%;
|
|
62
|
+
}
|
|
63
|
+
.mds-file-upload:not(.mds-file-upload--selected-file-is-image) .mds-file-upload__open-modal {
|
|
64
|
+
display: none;
|
|
65
|
+
}
|
|
66
|
+
.mds-file-upload--selected-file-is-image .mds-file-upload__preview-icon {
|
|
67
|
+
display: none;
|
|
68
|
+
}
|
|
69
|
+
.mds-file-upload--selected-file-is-image .mds-file-upload__preview-image {
|
|
70
|
+
display: block;
|
|
71
|
+
width: 70px;
|
|
72
|
+
height: 70px;
|
|
73
|
+
object-fit: contain;
|
|
31
74
|
}
|
|
32
75
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
font-weight: normal;
|
|
76
|
+
.mds-file-upload__file-name-container {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
37
79
|
}
|
|
38
80
|
|
|
39
|
-
|
|
81
|
+
.mds-file-upload__input {
|
|
40
82
|
position: absolute;
|
|
41
83
|
width: 100%;
|
|
42
84
|
height: 100%;
|
|
43
85
|
top: 0;
|
|
44
86
|
opacity: 0;
|
|
45
87
|
}
|
|
46
|
-
|
|
47
|
-
& .mds-file-upload__input-controls {
|
|
48
|
-
display: flex;
|
|
49
|
-
flex-direction: column;
|
|
50
|
-
align-items: center;
|
|
51
|
-
justify-content: center;
|
|
52
|
-
width: 100%;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
& .mds-form-control:focus ~ .mds-file-upload__input-controls {
|
|
88
|
+
.mds-file-upload__input:focus ~ .mds-file-upload__wrapper {
|
|
56
89
|
// Apply some focus styling to the 'button' when the input has focus and can be keyboard-activated
|
|
57
|
-
|
|
90
|
+
.mds-form-label__label {
|
|
58
91
|
border: 1px solid $focus-color;
|
|
59
92
|
outline-color: $focus-color;
|
|
60
93
|
box-shadow: 0 0 4px 2px $focus-color;
|
|
61
94
|
}
|
|
62
95
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.js .mds-form-element--file-supported .mds-file-upload__prompt {
|
|
66
|
-
display: flex;
|
|
67
|
-
align-items: center;
|
|
68
|
-
justify-content: center;
|
|
69
|
-
margin-bottom: $constant-size-baseline * 5;
|
|
70
|
-
|
|
71
|
-
& .mds-icon {
|
|
72
|
-
fill: var(--mds-color-button-bg-base, #000);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.mds-file-upload__selected-state {
|
|
77
|
-
display: none;
|
|
78
|
-
}
|
|
79
|
-
.mds-file-upload__file-name-container {
|
|
80
|
-
display: flex;
|
|
81
|
-
align-items: center;
|
|
82
|
-
justify-content: center;
|
|
83
|
-
padding: $constant-size-baseline * 2 $constant-size-baseline * 3;
|
|
84
|
-
}
|
|
85
|
-
.mds-file-upload__remove-button {
|
|
86
|
-
@include z-index;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.js .mds-form-element--file-supported.mds-form-element--selected-file {
|
|
90
|
-
@include mq($from: $constant-size-breakpoint-md) {
|
|
91
|
-
width: 50%;
|
|
92
|
-
}
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
/* Selected state */
|
|
98
|
+
.mds-file-upload--selected-file {
|
|
95
99
|
border: $regular-border;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
@include mq($from: $constant-size-breakpoint-md) {
|
|
101
|
+
width: 50%;
|
|
102
|
+
min-width: fit-content;
|
|
103
|
+
}
|
|
104
|
+
.mds-file-upload__wrapper {
|
|
105
|
+
border: none;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* When a file is selected, we reduce the input size to 0 (.mds-form-control)
|
|
101
109
|
* so the user can't click or drag and drop in the grey area anymore.
|
|
102
110
|
* Will mostly be useful when used with the cloud services buttons
|
|
103
111
|
* as the user will need to clear the input to see the buttons again.
|
|
104
112
|
* We also remove the dotted border to indicate the change
|
|
105
113
|
*/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
flex-wrap: wrap;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.js .mds-form-element--file-supported.mds-form-element--dragover {
|
|
125
|
-
& .mds-file-upload__input {
|
|
126
|
-
background-color: #fff;
|
|
114
|
+
.mds-file-upload__input {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
.mds-file-upload__state--no-file {
|
|
118
|
+
display: none;
|
|
119
|
+
}
|
|
120
|
+
.mds-file-upload__state--selected {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-wrap: wrap;
|
|
123
|
+
gap: $constant-size-baseline * 4; // spacing between preview image/icon and filename, also works when flex wrapping occurs on small screens
|
|
124
|
+
.mds-button {
|
|
125
|
+
padding-left: 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
/* If the drag and drop is supported, we hide the "selected file" fallback sentence.
|
|
131
|
-
* The text will show by default when there is no javascript
|
|
132
|
-
* or for browsers like IE that don't support drag and drop,
|
|
133
|
-
* as they will both get the default file input.
|
|
134
|
-
*/
|
|
135
|
-
.js .mds-form-element--file-supported .mds-file-upload__selected-file-fallback {
|
|
136
|
-
display: none;
|
|
137
|
-
}
|
|
@@ -61,10 +61,10 @@ const modals = {
|
|
|
61
61
|
focusedElementBeforeModal = document.activeElement;
|
|
62
62
|
modal.classList.add(modalActiveClass);
|
|
63
63
|
firstTabStop.focus();
|
|
64
|
-
siteContainer
|
|
64
|
+
siteContainer?.setAttribute('aria-hidden', 'true');
|
|
65
65
|
},
|
|
66
66
|
close: (modal, previousActiveElement, siteContainer) => {
|
|
67
|
-
siteContainer
|
|
67
|
+
siteContainer?.removeAttribute('aria-hidden');
|
|
68
68
|
modal.classList.remove(modalActiveClass);
|
|
69
69
|
previousActiveElement.focus();
|
|
70
70
|
},
|
package/src/js/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import subnavigation from '../components/subnavigation/subnavigation';
|
|
|
5
5
|
import checkboxList from '../components/inputs/checkbox-list/checkbox-list';
|
|
6
6
|
import popovers from '../components/popover/popover';
|
|
7
7
|
import modals from '../components/modal/modal';
|
|
8
|
-
import
|
|
8
|
+
import { MdsFileUpload } from '../components/inputs/file-upload/file-upload';
|
|
9
9
|
import characterCount from '../components/inputs/textarea/character-count';
|
|
10
10
|
import button from '../components/button/button';
|
|
11
11
|
import prose from '../helpers/prose/prose';
|
|
@@ -31,6 +31,9 @@ if (!window.customElements.get('mds-conditional-section')) {
|
|
|
31
31
|
if (!window.customElements.get('mds-image-cropper')) {
|
|
32
32
|
window.customElements.define('mds-image-cropper', MdsImageCropper);
|
|
33
33
|
}
|
|
34
|
+
if (!window.customElements.get('mds-file-upload')) {
|
|
35
|
+
window.customElements.define('mds-file-upload', MdsFileUpload);
|
|
36
|
+
}
|
|
34
37
|
if (!window.customElements.get('mds-category-picker')) {
|
|
35
38
|
window.customElements.define('mds-category-picker', MdsCategoryPicker);
|
|
36
39
|
}
|
|
@@ -41,7 +44,6 @@ const initAll = () => {
|
|
|
41
44
|
subnavigation.init();
|
|
42
45
|
checkboxList.init();
|
|
43
46
|
modals.init();
|
|
44
|
-
fileUpload.init();
|
|
45
47
|
characterCount.init();
|
|
46
48
|
popovers.init();
|
|
47
49
|
button.init();
|