182 lines
5.6 KiB
PHP
182 lines
5.6 KiB
PHP
<?php
|
|
|
|
$fieldName = $fieldName ?? '';
|
|
$fieldLabel = $fieldLabel ?? '';
|
|
$fieldId = $fieldId ?? $fieldName;
|
|
$fieldValue = $fieldValue ?? '';
|
|
$required = $required ?? true;
|
|
$validationErrors = $validationErrors ?? [];
|
|
$placeholder = $placeholder ?? "Enter " . strtolower($fieldLabel);
|
|
$hasError = isset($validationErrors[$fieldName]);
|
|
?>
|
|
|
|
<div class="name-field-component">
|
|
<label class="form-label" for="<?= esc($fieldId) ?>">
|
|
<?= esc($fieldLabel) ?>
|
|
<?php if ($required): ?><span class="text-danger">*</span><?php endif; ?>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
name="<?= esc($fieldName) ?>"
|
|
id="<?= esc($fieldId) ?>"
|
|
value="<?= esc($fieldValue) ?>"
|
|
class="form-control name-input <?= $hasError ? 'is-invalid' : '' ?>"
|
|
placeholder="<?= esc($placeholder) ?>"
|
|
data-field-name="<?= esc($fieldName) ?>"
|
|
data-label="<?= esc($fieldLabel) ?>"
|
|
<?php if ($required): ?>required<?php endif; ?>
|
|
oninput="validateNameField(this)"
|
|
onblur="validateNameField(this)"
|
|
>
|
|
<div class="invalid-feedback" id="<?= esc($fieldId) ?>_error">
|
|
<?= $hasError ? esc($validationErrors[$fieldName]) : '' ?>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.name-field-component {
|
|
position: relative;
|
|
}
|
|
|
|
.name-input.is-invalid {
|
|
border-color: #dc3545;
|
|
}
|
|
|
|
.invalid-feedback {
|
|
display: none;
|
|
font-size: 0.875em;
|
|
color: #dc3545;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.invalid-feedback.show {
|
|
display: block;
|
|
}
|
|
|
|
.name-field-component .text-muted {
|
|
font-size: 0.75rem;
|
|
margin-top: 0.25rem;
|
|
display: block;
|
|
}
|
|
|
|
.name-input.is-invalid ~ .text-muted {
|
|
display: none;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
function validateNameField(input) {
|
|
const fieldName = input.dataset.fieldName;
|
|
const fieldLabel = input.dataset.label;
|
|
let value = input.value.trim();
|
|
const errorElement = document.getElementById(input.id + '_error');
|
|
const hintElement = document.getElementById(input.id + '_hint');
|
|
|
|
// Clear previous validation state
|
|
input.classList.remove('is-invalid');
|
|
errorElement.classList.remove('show');
|
|
errorElement.textContent = '';
|
|
|
|
if (value === '') {
|
|
// Field is empty, check if required
|
|
if (input.hasAttribute('required')) {
|
|
input.classList.add('is-invalid');
|
|
errorElement.textContent = fieldLabel + ' is required';
|
|
errorElement.classList.add('show');
|
|
hintElement.style.display = 'none';
|
|
} else {
|
|
hintElement.style.display = 'block';
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check for valid characters (letters only, including spaces for names)
|
|
const nameRegex = /^[a-zA-Z\s'-]+$/;
|
|
if (!nameRegex.test(value)) {
|
|
input.classList.add('is-invalid');
|
|
errorElement.textContent = fieldLabel + ' can only contain letters, spaces, hyphens, and apostrophes';
|
|
errorElement.classList.add('show');
|
|
hintElement.style.display = 'none';
|
|
return false;
|
|
}
|
|
|
|
// Check minimum length
|
|
if (value.length < 2) {
|
|
input.classList.add('is-invalid');
|
|
errorElement.textContent = fieldLabel + ' must be at least 2 characters long';
|
|
errorElement.classList.add('show');
|
|
hintElement.style.display = 'none';
|
|
return false;
|
|
}
|
|
|
|
// Check maximum length
|
|
if (value.length > 50) {
|
|
input.classList.add('is-invalid');
|
|
errorElement.textContent = fieldLabel + ' cannot exceed 50 characters';
|
|
errorElement.classList.add('show');
|
|
hintElement.style.display = 'none';
|
|
return false;
|
|
}
|
|
|
|
// Auto-capitalize first letter of each word
|
|
const words = value.toLowerCase().split(/\s+/);
|
|
const capitalizedWords = words.map(word => {
|
|
if (word.length === 0) return '';
|
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
});
|
|
const capitalizedValue = capitalizedWords.join(' ');
|
|
|
|
// Update input value if different
|
|
if (input.value !== capitalizedValue) {
|
|
input.value = capitalizedValue;
|
|
}
|
|
|
|
// Hide hint and show success
|
|
hintElement.style.display = 'none';
|
|
input.classList.remove('is-invalid');
|
|
input.classList.add('is-valid');
|
|
|
|
// Remove is-valid class after a short delay
|
|
setTimeout(() => {
|
|
input.classList.remove('is-valid');
|
|
}, 2000);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Prevent non-letter characters on keypress
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const nameInputs = document.querySelectorAll('.name-input');
|
|
|
|
nameInputs.forEach(input => {
|
|
input.addEventListener('keypress', function(e) {
|
|
const char = String.fromCharCode(e.which || e.keyCode);
|
|
const isAllowed = /^[a-zA-Z\s'-]$/.test(char);
|
|
|
|
if (!isAllowed) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
});
|
|
|
|
// Prevent paste of invalid characters
|
|
input.addEventListener('paste', function(e) {
|
|
e.preventDefault();
|
|
const pastedData = (e.clipboardData || window.clipboardData).getData('text');
|
|
const cleanedData = pastedData.replace(/[^a-zA-Z\s'-]/g, '');
|
|
|
|
// Insert cleaned data at cursor position
|
|
const start = this.selectionStart;
|
|
const end = this.selectionEnd;
|
|
const currentValue = this.value;
|
|
const newValue = currentValue.substring(0, start) + cleanedData + currentValue.substring(end);
|
|
|
|
this.value = newValue;
|
|
this.selectionStart = this.selectionEnd = start + cleanedData.length;
|
|
|
|
validateNameField(this);
|
|
});
|
|
});
|
|
});
|
|
</script>
|