mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
MDL-78096 tiny_media: Implement drag-drop to upload an image
This commit is contained in:
parent
a420f895d6
commit
4d3c8e895e
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,3 +1,3 @@
|
||||
define("tiny_media/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_image_urlentrysubmit",imageBrowser:".openimagebrowser",addUrl:".tiny_image_addurl"},elements:{form:"form.tiny_image_form",alignSettings:".tiny_image_button",alt:".tiny_image_altentry",altWarning:".tiny_image_altwarning",height:".tiny_image_heightentry",width:".tiny_image_widthentry",url:".tiny_image_urlentry",urlWarning:".tiny_image_urlwarning",size:".tiny_image_size",presentation:".tiny_image_presentation",constrain:".tiny_image_constrain",customStyle:".tiny_image_customstyle",preview:".tiny_image_preview",previewBox:".tiny_image_preview_box",loaderIcon:".tiny_image_loader",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_media_submit",mediaBrowser:".openmediabrowser"},elements:{form:"form.tiny_media_form",source:".tiny_media_source",track:".tiny_media_track",mediaSource:".tiny_media_media_source",linkSource:".tiny_media_link_source",linkSize:".tiny_media_link_size",posterSource:".tiny_media_poster_source",posterSize:".tiny_media_poster_size",displayOptions:".tiny_media_display_options",name:".tiny_media_name_entry",title:".tiny_media_title_entry",url:".tiny_media_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",trackKind:".tiny_media_track_kind_entry",trackLabel:".tiny_media_track_label_entry",trackLang:".tiny_media_track_lang_entry",trackDefault:".tiny_media_track_default",mediaControl:".tiny_media_controls",mediaAutoplay:".tiny_media_autoplay",mediaMute:".tiny_media_mute",mediaLoop:".tiny_media_loop",advancedSettings:".tiny_media_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}}},_exports.default}));
|
||||
define("tiny_media/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_image_urlentrysubmit",imageBrowser:".openimagebrowser",addUrl:".tiny_image_addurl"},elements:{form:"form.tiny_image_form",alignSettings:".tiny_image_button",alt:".tiny_image_altentry",altWarning:".tiny_image_altwarning",height:".tiny_image_heightentry",width:".tiny_image_widthentry",url:".tiny_image_urlentry",urlWarning:".tiny_image_urlwarning",size:".tiny_image_size",presentation:".tiny_image_presentation",constrain:".tiny_image_constrain",customStyle:".tiny_image_customstyle",preview:".tiny_image_preview",previewBox:".tiny_image_preview_box",loaderIcon:".tiny_image_loader",loaderIconContainer:".tiny_image_loader_container",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_image_dropzone_container",fileInput:"#tiny_image_fileinput"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_media_submit",mediaBrowser:".openmediabrowser"},elements:{form:"form.tiny_media_form",source:".tiny_media_source",track:".tiny_media_track",mediaSource:".tiny_media_media_source",linkSource:".tiny_media_link_source",linkSize:".tiny_media_link_size",posterSource:".tiny_media_poster_source",posterSize:".tiny_media_poster_size",displayOptions:".tiny_media_display_options",name:".tiny_media_name_entry",title:".tiny_media_title_entry",url:".tiny_media_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",trackKind:".tiny_media_track_kind_entry",trackLabel:".tiny_media_track_label_entry",trackLang:".tiny_media_track_lang_entry",trackDefault:".tiny_media_track_default",mediaControl:".tiny_media_controls",mediaAutoplay:".tiny_media_autoplay",mediaMute:".tiny_media_mute",mediaLoop:".tiny_media_loop",advancedSettings:".tiny_media_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=selectors.min.js.map
|
File diff suppressed because one or more lines are too long
@ -22,6 +22,8 @@
|
||||
*/
|
||||
|
||||
import Selectors from './selectors';
|
||||
import Dropzone from 'core/dropzone';
|
||||
import uploadFile from 'editor_tiny/uploader';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
import {getStrings} from 'core/str';
|
||||
import {component} from "./common";
|
||||
@ -38,6 +40,7 @@ prefetchStrings('tiny_media', [
|
||||
'imageurlrequired',
|
||||
'uploading',
|
||||
'loading',
|
||||
'addfilesdrop',
|
||||
]);
|
||||
|
||||
export class ImageInsert {
|
||||
@ -65,12 +68,25 @@ export class ImageInsert {
|
||||
'imageurlrequired',
|
||||
'uploading',
|
||||
'loading',
|
||||
'addfilesdrop',
|
||||
];
|
||||
const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));
|
||||
|
||||
// Convert array to object.
|
||||
this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));
|
||||
this.currentModal.setTitle(this.langStrings.insertimage);
|
||||
if (this.canShowDropZone) {
|
||||
const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);
|
||||
const dropZone = new Dropzone(
|
||||
dropZoneEle,
|
||||
'image/*',
|
||||
files => {
|
||||
this.handleUploadedFile(files);
|
||||
}
|
||||
);
|
||||
dropZone.setLabel(this.langStrings.addfilesdrop);
|
||||
dropZone.init();
|
||||
}
|
||||
await this.registerEventListeners();
|
||||
};
|
||||
|
||||
@ -164,6 +180,43 @@ export class ImageInsert {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the content of the loader icon.
|
||||
*
|
||||
* @param {HTMLElement} root - The root element containing the loader icon.
|
||||
* @param {object} langStrings - An object containing language strings.
|
||||
* @param {number|null} progress - The progress percentage (optional).
|
||||
* @returns {void}
|
||||
*/
|
||||
updateLoaderIcon = (root, langStrings, progress = null) => {
|
||||
const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');
|
||||
loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the uploaded file, initiates the upload process, and updates the UI during the upload.
|
||||
*
|
||||
* @param {FileList} files - The list of files to upload (usually from a file input field).
|
||||
* @returns {Promise<void>} A promise that resolves when the file is uploaded and processed.
|
||||
*/
|
||||
handleUploadedFile = async(files) => {
|
||||
try {
|
||||
this.startImageLoading();
|
||||
const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {
|
||||
this.updateLoaderIcon(this.root, this.langStrings, progress);
|
||||
});
|
||||
// Set the loader icon content to "loading" after the file upload completes.
|
||||
this.updateLoaderIcon(this.root, this.langStrings);
|
||||
this.filePickerCallback({url: fileURL});
|
||||
} catch (error) {
|
||||
// Handle the error.
|
||||
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
||||
urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;
|
||||
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
||||
this.stopImageLoading();
|
||||
}
|
||||
};
|
||||
|
||||
registerEventListeners() {
|
||||
this.root.addEventListener('click', async(e) => {
|
||||
const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl);
|
||||
@ -185,5 +238,12 @@ export class ImageInsert {
|
||||
this.toggleUrlButton();
|
||||
}
|
||||
});
|
||||
|
||||
const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', () => {
|
||||
this.handleUploadedFile(fileInput.files);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -44,8 +44,11 @@ export default {
|
||||
preview: '.tiny_image_preview',
|
||||
previewBox: '.tiny_image_preview_box',
|
||||
loaderIcon: '.tiny_image_loader',
|
||||
loaderIconContainer: '.tiny_image_loader_container',
|
||||
insertImage: '.tiny_image_insert_image',
|
||||
modalFooter: '.modal-footer',
|
||||
dropzoneContainer: '.tiny_image_dropzone_container',
|
||||
fileInput: '#tiny_image_fileinput',
|
||||
},
|
||||
styles: {
|
||||
responsive: 'img-fluid',
|
||||
|
@ -25,6 +25,7 @@
|
||||
$string['addcaptionstrack'] = 'Add caption track';
|
||||
$string['addchapterstrack'] = 'Add chapter track';
|
||||
$string['adddescriptionstrack'] = 'Add description track';
|
||||
$string['addfilesdrop'] = 'Drag and drop an image to upload, or click to select';
|
||||
$string['addmetadatatrack'] = 'Add metadata track';
|
||||
$string['addsource_help'] = 'You are recommended to provide an alternative media source, as desktop and mobile browsers support different file formats.';
|
||||
$string['addsource'] = 'Add alternative source';
|
||||
@ -69,6 +70,7 @@ $string['label'] = 'Label';
|
||||
$string['languagesavailable'] = 'Languages available';
|
||||
$string['languagesinstalled'] = 'Languages installed';
|
||||
$string['link'] = 'Link';
|
||||
$string['loading'] = 'Preparing the image';
|
||||
$string['loop'] = 'Loop';
|
||||
$string['managefiles'] = 'Manage files';
|
||||
$string['mediabuttontitle'] = 'Multimedia';
|
||||
@ -84,6 +86,7 @@ $string['presentation'] = 'This image is decorative only';
|
||||
$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.';
|
||||
$string['privacy:metadata'] = 'The media plugin for TinyMCE does not store any personal data.';
|
||||
$string['remove'] = 'Remove';
|
||||
$string['repositorynotpermitted'] = 'Paste an image link in the field below.';
|
||||
$string['saveimage'] = 'Save image';
|
||||
$string['size'] = 'Width x height (in pixels)';
|
||||
$string['srclang'] = 'Language';
|
||||
@ -96,6 +99,7 @@ $string['unusedfilesdesc'] = 'The following embedded files are not used in the t
|
||||
$string['unusedfilesheader'] = 'Unused files';
|
||||
$string['unusedfilesremovalnotice'] = 'Any unused files will be automatically deleted when saving changes.';
|
||||
$string['updatemedia'] = 'Update media';
|
||||
$string['uploading'] = 'Uploading';
|
||||
$string['video'] = 'Video';
|
||||
$string['videoheight'] = 'Video height';
|
||||
$string['videosourcelabel'] = 'Video source URL';
|
||||
|
@ -34,8 +34,12 @@ iframe.mm_iframe {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.tiny_image_form .tiny_image_dropzone {
|
||||
padding: 1.5rem 1rem;
|
||||
.tiny_image_form .tiny_image_dropzone_container {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.tiny_image_form .tiny_image_dropzone_container .dropzone-label {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.tiny_image_form .tiny_image_loader_container {
|
||||
|
@ -27,25 +27,18 @@
|
||||
|
||||
}}
|
||||
<div class="tiny_image_insert_image">
|
||||
<div class="tiny_image_dropzone mb-1 d-flex align-items-center flex-column justify-content-center border rounded">
|
||||
<div class="tiny_image_dropzone d-flex flex-column justify-content-center">
|
||||
{{#showdropzone}}
|
||||
<div class="tiny_image_dropzone_iconlabel text-center" role="button">
|
||||
<!-- Dropzone icon -->
|
||||
<i class="tiny_image_dropzone_icon fa-6x pb-2 text-secondary fa fa-cloud-upload"></i>
|
||||
<!-- Dropzone string -->
|
||||
<div class="tiny_image_dropzone_label lead text-center">
|
||||
<p class="mb-0">{{#str}} draganddrop, tiny_media {{/str}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tiny_image_dropzone_container"></div>
|
||||
<!-- File input -->
|
||||
<input type="file" id="tiny_image_fileinput" accept="image/*" class="d-none">
|
||||
{{/showdropzone}}
|
||||
{{^showfilepicker}}
|
||||
<div class="tiny_image_dropzone_iconlabel text-center">
|
||||
<div class="text-center">
|
||||
<!-- Dropzone icon -->
|
||||
<i class="tiny_image_dropzone_icon fa-6x pb-2 text-secondary fa fa-cloud"></i>
|
||||
<i class="fa-6x pb-2 text-secondary fa fa-cloud"></i>
|
||||
<!-- Dropzone string -->
|
||||
<div class="tiny_image_dropzone_label lead text-center">
|
||||
<div class="lead text-center">
|
||||
<p class="mb-0">{{#str}} repositorynotpermitted, tiny_media {{/str}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user