mirror of
https://github.com/picocss/pico.git
synced 2025-04-25 10:46:14 -04:00
Added form validation
This commit is contained in:
parent
359e51ee06
commit
7602474cfd
241 changed files with 17845 additions and 118 deletions
|
@ -32,6 +32,7 @@
|
|||
<aside>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="#validation">Validation</a></li>
|
||||
<li><a href="#group">Group</a></li>
|
||||
<li><a href="#rows">Row</a></li>
|
||||
<li><a href="#row-offsets">Row Offset</a></li>
|
||||
|
@ -48,6 +49,58 @@
|
|||
<!-- /Header -->
|
||||
</div>
|
||||
<main class="col-12 col-md-9 col-lg-10">
|
||||
<!-- Validation -->
|
||||
<form action="javascript:void(0);">
|
||||
<article id="group">
|
||||
<header><h2>Validation</h2></header>
|
||||
<p>
|
||||
Form :user-valid and :user-invalid
|
||||
</p>
|
||||
<input placeholder="name" type="text" name="test" minlength="4" maxlength="30" pattern="[a-z]{4,30}" required>
|
||||
<small data-valid="this is good!" data-invalid="Min of 4 characters, max of 39, only a-z no caps."></small>
|
||||
|
||||
<input placeholder="Url" type="url" name="test" required>
|
||||
<small data-valid="this is good!" data-invalid="Must be a url."></small>
|
||||
|
||||
<input placeholder="Email" type="email" name="test" required>
|
||||
<small data-valid="this is good!" data-invalid="Must be a valid email."></small>
|
||||
|
||||
<select name="favorite-cuisine" required>
|
||||
<option selected disabled value="">Select your favorite cuisine...</option>
|
||||
<option>Italian</option>
|
||||
<option>Japanese</option>
|
||||
<option>Indian</option>
|
||||
<option>Thai</option>
|
||||
<option>French</option>
|
||||
</select>
|
||||
<small data-valid="this is good!" data-invalid="Error.."></small>
|
||||
|
||||
<input type="date" name="date" required>
|
||||
<small data-valid="this is good!" data-invalid="Error.."></small>
|
||||
|
||||
<input type="datetime-local" name="datetime-local" required>
|
||||
<small data-valid="this is good!" data-invalid="Error.."></small>
|
||||
|
||||
<input type="month" name="month" min="2018-03" max="2024-12" placeholder="2024-12" step="1" pattern="[0-9]{4}-[0-9]{2}" required>
|
||||
<small data-valid="this is good!" data-invalid="Format: YYYY-MM"></small>
|
||||
|
||||
<input type="time" name="time" required>
|
||||
<small data-valid="this is good!" data-invalid="Error.."></small>
|
||||
|
||||
<header>Check JS for FileValidator class</header>
|
||||
<input type="file" data-max-size="1048576" accept=".jpg,.svg,.png,.gif,.webp" id="checkFile" multiple />
|
||||
<small data-invalid="Only .jpg,.svg,.png,.gif,.webp image types allowed" data-valid="Good!"></small>
|
||||
|
||||
<textarea minlength="5" placeholder="its always valid?" required></textarea>
|
||||
<small data-valid="this is good!" data-invalid="Min of 5 characters!"></small>
|
||||
<br>
|
||||
<label><input type="checkbox" name="english" required />English </label>
|
||||
<footer>
|
||||
<input type="submit" value="submit" class="btn btn-primary">
|
||||
</footer>
|
||||
</article>
|
||||
</form>
|
||||
<!-- /Validation -->
|
||||
<!-- Group -->
|
||||
<article id="group">
|
||||
<header><h2>Group</h2></header>
|
||||
|
@ -317,9 +370,13 @@
|
|||
<script src="js/modal.js"></script>
|
||||
<!-- alert notifications -->
|
||||
<script src="js/notifications.js"></script>
|
||||
<script src="js/FileValidator.js"></script>
|
||||
<script src="js/accordion.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const checkFile = document.getElementById("checkFile");
|
||||
new FileValidator(checkFile);
|
||||
|
||||
const alignButtons = document.querySelectorAll(".change-row-align");
|
||||
const alignRow = document.getElementById("row-align-example");
|
||||
alignButtons.forEach((button) => {
|
||||
|
|
158
demo/js/FileValidator.js
Normal file
158
demo/js/FileValidator.js
Normal file
|
@ -0,0 +1,158 @@
|
|||
class FileValidator {
|
||||
/**
|
||||
* Initializes the FileValidator with a file input element.
|
||||
* @param {HTMLInputElement} fileInput - File input element to validate.
|
||||
* @param {boolean} displayList - Whether to display the file list (default: true).
|
||||
* @param {HTMLElement} customListContainer - Optional custom container for the file list.
|
||||
*/
|
||||
constructor(fileInput, displayList = true, customListContainer = null) {
|
||||
if (!(fileInput instanceof HTMLInputElement) || fileInput.type !== "file") {
|
||||
throw new Error("FileValidator requires an input element of type 'file'.");
|
||||
}
|
||||
|
||||
this.fileInput = fileInput;
|
||||
this.displayList = displayList;
|
||||
this.customListContainer = customListContainer;
|
||||
|
||||
this.fileInput.addEventListener("change", () => this.validateFiles());
|
||||
|
||||
// Create a container for the file list
|
||||
if (this.displayList) {
|
||||
this.fileListContainer = document.createElement("ul");
|
||||
this.fileListContainer.className = "file-list";
|
||||
if (this.customListContainer) {
|
||||
this.customListContainer.append(this.fileListContainer);
|
||||
} else {
|
||||
this.fileInput.insertAdjacentElement("afterend", this.fileListContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates files based on the accept and size attributes of the input element.
|
||||
* Displays a file list if files are valid.
|
||||
* @returns {boolean} - True if all files are valid, otherwise false.
|
||||
*/
|
||||
validateFiles() {
|
||||
const accept = this.fileInput.getAttribute("accept") || "";
|
||||
const maxSize = parseInt(this.fileInput.getAttribute("data-max-size"), 10) || Infinity;
|
||||
const files = Array.from(this.fileInput.files);
|
||||
|
||||
// Clear any existing list
|
||||
if (this.displayList) {
|
||||
this.fileListContainer.innerHTML = "";
|
||||
}
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log("No files selected.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (accept && !this.isFileTypeValid(file, accept)) {
|
||||
console.error(`Invalid file type: ${file.name}`);
|
||||
this.fileInput.setCustomValidity(`Invalid file type: ${file.name}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size > maxSize) {
|
||||
console.error(`File too large: ${file.name} (${file.size} bytes)`);
|
||||
this.fileInput.setCustomValidity(`File too large: ${file.name}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add file to the list if valid
|
||||
if (this.displayList) {
|
||||
this.addFileToList(file);
|
||||
}
|
||||
}
|
||||
|
||||
this.fileInput.setCustomValidity("");
|
||||
console.log("All files are valid.");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the displayed list, including an image preview if the file is an image.
|
||||
* @param {File} file - The file to add to the list.
|
||||
*/
|
||||
addFileToList(file) {
|
||||
const listItem = document.createElement("li");
|
||||
listItem.textContent = `${file.name} (${(file.size / 1024).toFixed(2)} KB)`;
|
||||
|
||||
// Add an image preview if the file is an image
|
||||
if (file.type.startsWith("image/")) {
|
||||
const imagePreview = document.createElement("img");
|
||||
imagePreview.src = URL.createObjectURL(file);
|
||||
imagePreview.style.maxWidth = "50px";
|
||||
imagePreview.style.maxHeight = "50px";
|
||||
imagePreview.style.marginRight = "10px";
|
||||
imagePreview.onload = () => URL.revokeObjectURL(imagePreview.src); // Release memory
|
||||
listItem.prepend(imagePreview);
|
||||
}
|
||||
|
||||
// Add a remove button
|
||||
const removeButton = document.createElement("button");
|
||||
removeButton.textContent = "🗙";
|
||||
removeButton.className = "outline btn-file-rm";
|
||||
removeButton.addEventListener("click", () => this.removeFile(file, listItem));
|
||||
|
||||
listItem.append(removeButton);
|
||||
|
||||
if (this.customListContainer) {
|
||||
this.customListContainer.append(listItem);
|
||||
} else {
|
||||
this.fileListContainer.append(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the list and updates the input file element.
|
||||
* @param {File} file - The file to remove.
|
||||
* @param {HTMLElement} listItem - The list item element to remove.
|
||||
*/
|
||||
removeFile(file, listItem) {
|
||||
const filesArray = Array.from(this.fileInput.files);
|
||||
const updatedFiles = filesArray.filter(f => f !== file);
|
||||
|
||||
// Update the input element with the new FileList
|
||||
const dataTransfer = new DataTransfer();
|
||||
updatedFiles.forEach(f => dataTransfer.items.add(f));
|
||||
this.fileInput.files = dataTransfer.files;
|
||||
|
||||
// Remove the list item from the display
|
||||
listItem.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file's type matches any of the allowed types in the accept attribute.
|
||||
* @param {File} file - File object to check.
|
||||
* @param {string} accept - Accept attribute value (e.g., ".jpg, .png, image/*").
|
||||
* @returns {boolean} - True if the file type is valid, otherwise false.
|
||||
*/
|
||||
isFileTypeValid(file, accept) {
|
||||
const acceptedTypes = accept.split(",").map(type => type.trim());
|
||||
return acceptedTypes.some(type => {
|
||||
if (type.includes("/") && file.type === type) return true;
|
||||
if (type.startsWith(".") && file.name.endsWith(type)) return true;
|
||||
if (type.endsWith("/*")) return file.type.startsWith(type.split("/")[0] + "/");
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Usage example:
|
||||
// <input type="file" id="fileInput" accept=".jpg, .png, .gif, .svg" data-max-size="1048576" multiple>
|
||||
// const fileInput = document.getElementById("fileInput");
|
||||
// new FileValidator(fileInput, false); // Hide display list
|
||||
//
|
||||
// Alternatively, with custom list container:
|
||||
// const customListContainer = document.getElementById("customListContainer");
|
||||
// new FileValidator(fileInput, true, customListContainer);
|
||||
|
||||
// Usage example2:
|
||||
// <input type="file" id="checkfile" accept=".jpg, .png, .gif, .svg" data-max-size="1048576" multiple>
|
||||
// 1048576 = 1MB
|
||||
// const checkfile = document.getElementById("checkfile");
|
||||
// new FileValidator(checkfile);
|
Loading…
Add table
Add a link
Reference in a new issue