Skip to content

Commit cfdd43a

Browse files
authored
fix: system file dialog on safari (#730)
Co-authored-by: nd0ut <[email protected]>
1 parent 90531f2 commit cfdd43a

File tree

1 file changed

+39
-13
lines changed

1 file changed

+39
-13
lines changed

abstract/UploaderPublicApi.js

+39-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @ts-check
22
import { ActivityBlock } from './ActivityBlock.js';
33

4-
import { Data } from '@symbiotejs/symbiote';
4+
import { applyStyles, Data } from '@symbiotejs/symbiote';
55
import { EventType } from '../blocks/UploadCtxProvider/EventEmitter.js';
66
import { UploadSource } from '../blocks/utils/UploadSource.js';
77
import { serializeCsv } from '../blocks/utils/comma-separated.js';
@@ -138,7 +138,9 @@ export class UploaderPublicApi {
138138

139139
/** @param {{ captureCamera?: boolean }} options */
140140
openSystemDialog = (options = {}) => {
141-
let accept = serializeCsv(mergeFileTypes([this.cfg.accept ?? '', ...(this.cfg.imgOnly ? IMAGE_ACCEPT_LIST : [])]));
141+
const accept = serializeCsv(
142+
mergeFileTypes([this.cfg.accept ?? '', ...(this.cfg.imgOnly ? IMAGE_ACCEPT_LIST : [])]),
143+
);
142144

143145
if (this.cfg.accept && !!this.cfg.imgOnly) {
144146
console.warn(
@@ -147,7 +149,16 @@ export class UploaderPublicApi {
147149
'The value of `accept` will be concatenated with the internal image mime types list.',
148150
);
149151
}
152+
153+
const INPUT_ATTR_NAME = 'uploadcare-file-input';
150154
const fileInput = document.createElement('input');
155+
fileInput.setAttribute(INPUT_ATTR_NAME, '');
156+
applyStyles(fileInput, {
157+
opacity: 0,
158+
height: 0,
159+
width: 0,
160+
visibility: 'hidden',
161+
});
151162
fileInput.type = 'file';
152163
fileInput.multiple = this.cfg.multiple;
153164
if (options.captureCamera) {
@@ -156,18 +167,33 @@ export class UploaderPublicApi {
156167
} else {
157168
fileInput.accept = accept;
158169
}
170+
fileInput.addEventListener(
171+
'change',
172+
() => {
173+
if (!fileInput.files) {
174+
return;
175+
}
176+
[...fileInput.files].forEach((file) =>
177+
this.addFileFromObject(file, { source: options.captureCamera ? UploadSource.CAMERA : UploadSource.LOCAL }),
178+
);
179+
// To call uploadTrigger UploadList should draw file items first:
180+
this._ctx.$['*currentActivity'] = ActivityBlock.activities.UPLOAD_LIST;
181+
this._ctx.setOrAddState('*modalActive', true);
182+
fileInput.remove();
183+
},
184+
{
185+
once: true,
186+
},
187+
);
188+
189+
document.querySelectorAll(`[${INPUT_ATTR_NAME}]`).forEach((el) => el.remove());
190+
191+
/**
192+
* Some browsers (e.g. Safari) require the file input to be in the DOM to work properly. Without it the file input
193+
* will open system dialog but won't trigger the change event sometimes.
194+
*/
195+
document.body.appendChild(fileInput);
159196
fileInput.dispatchEvent(new MouseEvent('click'));
160-
fileInput.onchange = () => {
161-
// @ts-ignore TODO: fix this
162-
[...fileInput['files']].forEach((file) =>
163-
this.addFileFromObject(file, { source: options.captureCamera ? UploadSource.CAMERA : UploadSource.LOCAL }),
164-
);
165-
// To call uploadTrigger UploadList should draw file items first:
166-
this._ctx.$['*currentActivity'] = ActivityBlock.activities.UPLOAD_LIST;
167-
this._ctx.setOrAddState('*modalActive', true);
168-
// @ts-ignore TODO: fix this
169-
fileInput['value'] = '';
170-
};
171197
};
172198

173199
/**

0 commit comments

Comments
 (0)