Created
March 27, 2025 10:19
-
-
Save Mykola-Veryha/b1c6c09a26f4290967a8f0d1172e4c08 to your computer and use it in GitHub Desktop.
Filament FilePond Validation Plugin
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
| const FilePondPluginFilenameLengthValidation = ({ addFilter, utils }) => { | |
| const { Type } = utils; | |
| function validateFile(file, maxLength, errorMessage) { | |
| return file.name?.length > maxLength | |
| ? { status: { main: errorMessage.replace('{maxLength}', maxLength), sub: '' }} | |
| : true; | |
| } | |
| addFilter('ALLOW_FILE', (file, { query }) => { | |
| return validateFile(file, query('GET_MAX_FILENAME_LENGTH'), query('GET_MAX_FILENAME_LENGTH_EXCEEDED')); | |
| }); | |
| addFilter('LOAD_FILE', (file, { query }) => { | |
| // The query transforms the name using `GET_${fromCamels(key, '_').toUpperCase()}`, | |
| // which corresponds to the maxFilenameLength value. | |
| const result = validateFile(file, query('GET_MAX_FILENAME_LENGTH'), query('GET_MAX_FILENAME_LENGTH_EXCEEDED')); | |
| // If validation fails, reject the file. Otherwise, continue with the file | |
| return result !== true ? Promise.reject(result) : Promise.resolve(file); | |
| }); | |
| return { | |
| options: { | |
| maxFilenameLength: [null, Type.INT], | |
| maxFilenameLengthExceeded: ['', Type.STRING], | |
| }, | |
| }; | |
| }; | |
| const FilePondPluginFilenameRegexValidation = ({ addFilter, utils }) => { | |
| const { Type } = utils; | |
| function validateFile(file, filenameRegex, errorMessage) { | |
| // Extract filename without extension | |
| const lastDotIndex = file.name.lastIndexOf('.'); | |
| const filenameWithoutExt = lastDotIndex !== -1 ? file.name.substring(0, lastDotIndex) : file.name; | |
| return !(new RegExp(filenameRegex)).test(filenameWithoutExt) | |
| ? { status: { main: errorMessage, sub: '' } } | |
| : true; | |
| } | |
| addFilter('ALLOW_FILE', (file, { query }) => { | |
| return validateFile(file, query('GET_FILENAME_REGEX'), query('GET_FILENAME_REGEX_FAILS')); | |
| }); | |
| addFilter('LOAD_FILE', (file, { query }) => { | |
| const result = validateFile(file, query('GET_FILENAME_REGEX'), query('GET_FILENAME_REGEX_FAILS')); | |
| // If validation fails, reject the file. Otherwise, continue with the file | |
| return result !== true ? Promise.reject(result) : Promise.resolve(file); | |
| }); | |
| return { | |
| options: { | |
| filenameRegex: ['', Type.STRING], | |
| filenameRegexFails: ['', Type.STRING], | |
| }, | |
| }; | |
| }; | |
| document.addEventListener('FilePond:loaded', (event) => { | |
| const { registerPlugin, setOptions } = event.detail; | |
| registerPlugin(FilePondPluginFilenameLengthValidation); | |
| registerPlugin(FilePondPluginFilenameRegexValidation); | |
| setOptions({ | |
| labelMaxFileSizeExceeded: 'The file is too large. Maximum file size is 100MB', | |
| labelFileTypeNotAllowed: 'File of invalid type', | |
| maxFilenameLengthExceeded: 'The file name exceeds the maximum character limit. Please rename your file and ensure it is no longer than {maxLength} characters.', | |
| filenameRegexFails: 'The filename contains invalid characters. Please use only letters, numbers, underscores.', | |
| }); | |
| }); | |
| document.addEventListener('FilePond:init', (event) => { | |
| const { pond } = event.detail; | |
| const fileUploadElement = event.target?.closest('.fi-fo-file-upload'); | |
| const maxFilenameLength = fileUploadElement?.dataset?.maxFilenameLength; | |
| if (maxFilenameLength) { | |
| pond.setOptions({ maxFilenameLength }); | |
| } else { | |
| console.warn('Warning: "data-max-filename-length" attribute is not set. Please check your field configuration.'); | |
| } | |
| const filenameRegex = fileUploadElement?.dataset?.filenameRegex; | |
| if (filenameRegex) { | |
| pond.setOptions({ filenameRegex }); | |
| } else { | |
| console.warn('Warning: "data-filename-regex" attribute is not set. Please check your field configuration.'); | |
| } | |
| function addInProgressClass() { | |
| const fileStatusMain = document.querySelector('.filepond--file-status-main'); | |
| if (fileStatusMain) { | |
| const isUploading = fileStatusMain.textContent.toLocaleLowerCase().includes('uploading'); | |
| const isUploadComplete = fileStatusMain.textContent.toLocaleLowerCase().includes('upload complete'); | |
| fileStatusMain.classList.toggle('in-progress', isUploading); | |
| fileStatusMain.classList.toggle('complete', isUploadComplete); | |
| fileStatusMain.classList.toggle('empty', !fileStatusMain.innerHTML.trim()); | |
| } | |
| } | |
| function resizeFilepondRoot() { | |
| const fileStatus = document.querySelector('.filepond--file-status'); | |
| const filepondRootWrapper = document.querySelector('.filepond--root:has(.filepond--item)')?.parentElement; | |
| if (fileStatus && filepondRootWrapper) { | |
| filepondRootWrapper.style.height = 'auto'; // Reset height to auto to calculate new height | |
| const newHeight = filepondRootWrapper.offsetHeight + fileStatus.offsetHeight; | |
| filepondRootWrapper.style.setProperty('height', newHeight + 'px'); | |
| fileStatus.querySelector('.filepond--file-status-main') | |
| .style | |
| .setProperty('bottom', `-${fileStatus.offsetHeight - 20}px`); | |
| } else { | |
| // Reset the height when there's no file status message | |
| const wrapper = document.querySelector('.filepond--root')?.parentElement; | |
| if (wrapper) { | |
| wrapper.style.height = 'auto'; | |
| } | |
| } | |
| } | |
| const observer = new MutationObserver(() => { | |
| addInProgressClass(); | |
| resizeFilepondRoot(); | |
| }); | |
| const filepondRoot = document.querySelector('.filepond--root'); | |
| if (filepondRoot) { | |
| observer.observe(filepondRoot, { childList: true, subtree: true }); | |
| } | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment