Last active
May 22, 2025 14:04
-
-
Save Qubadi/5793d9bd3fbc996fb35cee7e50031f33 to your computer and use it in GitHub Desktop.
Jetformbuilder customize the Choose File and drag-and-drop of images
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
UPDATED: 04.11.2024 | |
Copy the following HTML code and create a HTML snippet using your snippet plugins. | |
Paste the code into the plugin and save it. | |
Don't forget to add MIME types (image formats), for example, PNG and JPEG, in the media field where it shows 'Allow MIME types. | |
______________________________________________________ | |
<style> | |
/* Custom styles for the file upload field */ | |
.jet-form-builder__field-wrap.jet-form-builder-file-upload { | |
background: #ecf6ff !important; | |
border: 3px dashed #c7d4e1 !important; | |
padding: 20px !important; | |
text-align: center; | |
position: relative; | |
margin-bottom: 20px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
border-radius: 6px !important; | |
transition: border-color 0.3s ease, background-color 0.3s ease; | |
} | |
/* Styles for the image upload */ | |
.jet-form-builder-file-upload__file img { | |
display: block; | |
width: 100%; | |
height: 100% !important; | |
padding: 0; | |
margin: 0; | |
-o-object-fit: cover; | |
object-fit: cover; | |
-o-object-position: center center; | |
object-position: center center; | |
border-radius: 6px !important; | |
box-shadow: 0 0 30px -8px rgba(0, 0, 0, 0.24) !important; | |
} | |
/* Styles for image content */ | |
.jet-form-builder-file-upload__content { | |
min-height: auto !important; | |
} | |
/* Styles for the custom "Choose File" and "File Uploaded" buttons */ | |
.addfile { | |
padding: 10px 20px; | |
background-color: #0037fd !important; | |
color: #ffffff !important; | |
border: none !important; | |
font-size: 16px; | |
font-weight: 600; | |
cursor: pointer; | |
border-radius: 6px; | |
margin-top: 10px; /* Space between button and label */ | |
} | |
.addfile:hover { | |
background-color: #000000 !important; | |
color: #ffffff !important; | |
} | |
/* Styles for the label that shows file upload status */ | |
.labeladdfile { | |
padding: 6px 12px; | |
background-color: transparent; | |
color: #000000 !important; | |
font-size: 12px; | |
font-weight: 400; | |
display: block; | |
margin-top: 10px; | |
border-radius: 6px; | |
} | |
/* Class added when files are uploaded */ | |
.files-uploaded { | |
background-color: #09b872 !important; | |
padding: 6px 12px; | |
color: #ffffff !important; | |
border-radius: 6px; | |
} | |
/* Hide the default file input visually */ | |
.jet-form-builder-file-upload__input { | |
display: none; | |
} | |
/* Drag and drop hover effect */ | |
.jet-form-builder__field-wrap.jet-form-builder-file-upload.dragover { | |
border-color: #0037fd !important; | |
background: rgba(0, 55, 253, 0.1) !important; | |
} | |
/* New styles for drag and drop text and icon */ | |
.drag-drop-text { | |
font-size: 30px; | |
font-weight: 700; | |
color: #333; | |
margin-bottom: 10px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
transition: opacity 0.3s ease, height 0.3s ease; | |
height: auto; | |
} | |
.drag-drop-text.hidden { | |
opacity: 0; | |
height: 0; | |
overflow: hidden; | |
} | |
.drag-drop-text .icon { | |
font-size: 24px; | |
margin-bottom: 10px; | |
} | |
.allowed-mime-types { | |
font-size: 14px; | |
color: #666; | |
margin-bottom: 10px; | |
transition: opacity 0.3s ease, height 0.3s ease; | |
height: auto; | |
} | |
.allowed-mime-types.hidden { | |
opacity: 0; | |
height: 0; | |
overflow: hidden; | |
} | |
.allowed-mime-types .formats { | |
font-weight: bold; | |
} | |
.or-text { | |
font-size: 14px; | |
color: #666; | |
margin-bottom: 10px; | |
transition: opacity 0.3s ease, height 0.3s ease; | |
height: auto; | |
} | |
.or-text.hidden { | |
opacity: 0; | |
height: 0; | |
overflow: hidden; | |
} | |
/* Responsive adjustments */ | |
@media (max-width: 767px) { | |
.addfile, .labeladdfile { | |
padding: 8px 16px; | |
font-size: 12px; | |
} | |
} | |
@media (max-width: 1024px) { | |
.addfile, .labeladdfile { | |
padding: 8px 16px; | |
font-size: 12px; | |
} | |
} | |
</style> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
const { addAction } = window.JetPlugins.hooks; | |
addAction('jet.fb.input.makeReactive', 'set-upload-labels', function(input) { | |
if (!input?.nodes?.length || !input.nodes[0].classList.contains('jet-form-builder-file-upload__input')) { | |
return; | |
} | |
const $ = jQuery; | |
const upload = $(input.nodes[0]); | |
const fields = $(input.nodes[0].closest('.jet-form-builder-file-upload__fields')); | |
// Extract allowed MIME types from the input element and format them | |
let mimeTypes = upload.attr('accept')?.split(',').map(type => type.split('/')[1].trim()) || []; | |
if (!fields.find('.addfile').length) { | |
fields.prepend(` | |
<div class="drag-drop-text"> | |
<i class="fas fa-file-upload icon"></i> | |
Drag and drop your files here | |
</div> | |
<div class="allowed-mime-types"> | |
Files supported: <span class="formats">${mimeTypes.join(', ')}</span> | |
</div> | |
<div class="or-text">OR</div> | |
`); | |
fields.append(`<input type="button" class="addfile" value="Choose File"/>`); | |
fields.append(`<div class="labeladdfile"></div>`); | |
} | |
const labelAdd = fields.find('.labeladdfile'); | |
const buttonAdd = fields.find('.addfile'); | |
const dragDropText = fields.find('.drag-drop-text'); | |
const allowedMimeText = fields.find('.allowed-mime-types'); | |
const orText = fields.find('.or-text'); | |
upload.css('display', 'none'); | |
buttonAdd.click(function() { | |
upload.trigger('click'); | |
}); | |
function updateFileLabel() { | |
const fileCount = upload[0].files.length; | |
if (fileCount > 0) { | |
buttonAdd.val('File Uploaded'); | |
labelAdd.html(`Uploaded files (${fileCount})`); | |
labelAdd.addClass('files-uploaded'); | |
buttonAdd.css({ opacity: 1, transform: 'scale(1)' }); | |
labelAdd.css({ opacity: 1, transform: 'scale(1)' }); | |
} else { | |
resetFileLabel(); | |
} | |
} | |
function resetFileLabel() { | |
// Smoothly hide the button and label | |
buttonAdd.css({ opacity: 0, transform: 'scale(0.9)' }); | |
labelAdd.css({ opacity: 0, transform: 'scale(0.9)' }); | |
// After the transition, reset them | |
setTimeout(() => { | |
buttonAdd.val('Choose File'); | |
labelAdd.html('Choose File'); | |
labelAdd.removeClass('files-uploaded'); | |
buttonAdd.css({ opacity: 1, transform: 'scale(1)' }); | |
labelAdd.css({ opacity: 1, transform: 'scale(1)' }); | |
$('.jet-form-builder-file-upload__file').remove(); // Remove any previews | |
}, 300); | |
} | |
const fieldWrap = fields.closest('.jet-form-builder__field-wrap.jet-form-builder-file-upload'); | |
fieldWrap.on('dragover', function(event) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
fieldWrap.addClass('dragover'); | |
dragDropText.addClass('hidden'); | |
allowedMimeText.addClass('hidden'); | |
orText.addClass('hidden'); | |
}); | |
fieldWrap.on('dragleave', function(event) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
fieldWrap.removeClass('dragover'); | |
if (input.value.get().length === 0) { | |
dragDropText.removeClass('hidden'); | |
allowedMimeText.removeClass('hidden'); | |
orText.removeClass('hidden'); | |
} | |
}); | |
fieldWrap.on('drop', function(event) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
fieldWrap.removeClass('dragover'); | |
let files = event.originalEvent.dataTransfer.files; | |
{ | |
const dt = new DataTransfer(); | |
// Keep previously selected | |
for (let i = 0; i < upload[0].files.length; i++) { | |
dt.items.add(upload[0].files[i]); | |
} | |
// Add newly dropped | |
for (let i = 0; i < files.length; i++) { | |
dt.items.add(files[i]); | |
} | |
// Remove duplicates | |
for (let i = 0; i < dt.files.length; i++) { | |
for (let j = i + 1; j < dt.files.length; j++) { | |
if ( | |
dt.files[i].name === dt.files[j].name && | |
dt.files[i].lastModified === dt.files[j].lastModified && | |
dt.files[i].size === dt.files[j].size && | |
dt.files[i].type === dt.files[j].type | |
) { | |
dt.items.remove(j); | |
j--; | |
} | |
} | |
} | |
files = dt.files; | |
} | |
// Assign files, trigger change, rely on JetFormBuilder’s validation | |
upload[0].files = files; | |
const eventChange = new Event('change', { bubbles: true }); | |
upload[0].dispatchEvent(eventChange); | |
// Give JetFormBuilder time to detect error (if any) | |
setTimeout(() => { | |
const errorMessage = $('.jet-form-builder-row.field-has-error .error-message') | |
.text().trim(); | |
if (errorMessage) { | |
alert(errorMessage); | |
window.location.reload(); | |
resetFileInput(); | |
} | |
}, 300); // Increased to 300ms to ensure validation is done | |
}); | |
// We rely on JetFormBuilder’s own validation | |
function validateFiles(files) { | |
return true; | |
} | |
function showErrorMessage() { | |
const errorMessage = $('.jet-form-builder-row.field-has-error .error-message') | |
.text().trim(); | |
if (errorMessage) { | |
alert(errorMessage); | |
resetFileInput(); | |
setTimeout(() => { | |
window.location.reload(); | |
}, 700); | |
} | |
} | |
/* | |
* Merge chosen files, remove duplicates, | |
* then let JetFormBuilder do its validation | |
*/ | |
upload.on('change', function() { | |
let chosenFiles = upload[0].files; | |
{ | |
const dt = new DataTransfer(); | |
for (let i = 0; i < upload[0].files.length; i++) { | |
dt.items.add(upload[0].files[i]); | |
} | |
// Remove duplicates (same logic as drag-and-drop) | |
for (let i = 0; i < dt.files.length; i++) { | |
for (let j = i + 1; j < dt.files.length; j++) { | |
if ( | |
dt.files[i].name === dt.files[j].name && | |
dt.files[i].lastModified === dt.files[j].lastModified && | |
dt.files[i].size === dt.files[j].size && | |
dt.files[i].type === dt.files[j].type | |
) { | |
dt.items.remove(j); | |
j--; | |
} | |
} | |
} | |
chosenFiles = dt.files; | |
upload[0].files = chosenFiles; | |
} | |
// Wait for JetFormBuilder to validate before checking for errors | |
setTimeout(() => { | |
const errorMessage = $('.jet-form-builder-row.field-has-error .error-message') | |
.text().trim(); | |
if (errorMessage) { | |
alert(errorMessage); | |
window.location.reload(); | |
resetFileInput(); | |
} else { | |
// If no error, update the file label | |
updateFileLabel(); | |
} | |
}, 300); | |
}); | |
// Keep the real-time UI changes | |
input.value.watch(function() { | |
const fileCount = this.current.length; | |
const fileText = fileCount > 0 ? `Uploaded files (${fileCount})` : ''; | |
buttonAdd.val(fileCount > 0 ? 'File Uploaded' : 'Choose File'); | |
labelAdd.html(fileText); | |
labelAdd.toggleClass('files-uploaded', fileCount > 0); | |
if (fileCount > 0) { | |
dragDropText.addClass('hidden'); | |
allowedMimeText.addClass('hidden'); | |
orText.addClass('hidden'); | |
} else { | |
dragDropText.removeClass('hidden'); | |
allowedMimeText.removeClass('hidden'); | |
orText.removeClass('hidden'); | |
} | |
}); | |
$(document).on('click', '.jet-form-builder-file-upload__file-remove', function() { | |
setTimeout(() => { | |
if ($('.jet-form-builder-file-upload__file').length === 0) { | |
resetFileLabel(); | |
} else { | |
buttonAdd.val('File Uploaded'); | |
labelAdd.html(`Uploaded files (${$('.jet-form-builder-file-upload__file').length})`); | |
labelAdd.addClass('files-uploaded'); | |
buttonAdd.css({ opacity: 1, transform: 'scale(1)' }); | |
labelAdd.css({ opacity: 1, transform: 'scale(1)' }); | |
} | |
}, 100); | |
}); | |
const successMessageObserver = new MutationObserver((mutationsList) => { | |
for (let mutation of mutationsList) { | |
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { | |
const successMessage = $(mutation.addedNodes).find('.jet-form-builder-message--success'); | |
if (successMessage.length > 0) { | |
resetFileLabel(); | |
} | |
} | |
} | |
}); | |
const formContainer = $('.jet-form-builder')[0]; | |
if (formContainer) { | |
successMessageObserver.observe(formContainer, { childList: true, subtree: true }); | |
} | |
function resetFileInput() { | |
upload.val(''); | |
resetFileLabel(); | |
input.value.set([]); | |
} | |
}); | |
}); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Qubadi Thank you for sharing the code! Please add or implement a new functionality that enables image rearrangement via drag-and-drop, including on mobile devices. Currently, rearranging images is not possible on mobile, so this enhancement would be highly beneficial