summaryrefslogtreecommitdiffstats
path: root/assets/js/form-components.js
diff options
context:
space:
mode:
Diffstat (limited to 'assets/js/form-components.js')
-rw-r--r--assets/js/form-components.js127
1 files changed, 127 insertions, 0 deletions
diff --git a/assets/js/form-components.js b/assets/js/form-components.js
new file mode 100644
index 0000000..ffa4260
--- /dev/null
+++ b/assets/js/form-components.js
@@ -0,0 +1,127 @@
+// Form component utilities and Alpine.js data
+
+export function formComponentsData() {
+ return {
+ // Modal states
+ showAlertModal: false,
+ showConfirmModal: false,
+ showContentModal: false,
+
+ // Toast notification state
+ toasts: [],
+
+ // Handle confirm modal action
+ handleConfirm() {
+ this.showConfirmModal = false;
+ this.showToast('success', 'Action confirmed!');
+ },
+
+ // Show toast notification
+ showToast(type = 'success', message = null) {
+ const messages = {
+ success: 'Operation completed successfully!',
+ error: 'An error occurred. Please try again.',
+ info: 'Here is some information.',
+ warning: 'Please be careful with this action.'
+ };
+
+ const toastMessage = message || messages[type] || messages.success;
+ const toastId = Date.now();
+
+ // Add toast to list
+ this.toasts.push({
+ id: toastId,
+ type: type,
+ message: toastMessage
+ });
+
+ // Auto-remove after 5 seconds
+ setTimeout(() => {
+ this.toasts = this.toasts.filter(t => t.id !== toastId);
+ }, 5000);
+ },
+
+ // Remove toast manually
+ removeToast(id) {
+ this.toasts = this.toasts.filter(t => t.id !== id);
+ },
+
+ // Form validation utilities
+ validateEmail(email) {
+ const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return regex.test(email);
+ },
+
+ validatePassword(password) {
+ return password.length >= 8;
+ },
+
+ // Auto-expand textarea
+ autoExpandTextarea(event) {
+ const textarea = event.target;
+ textarea.style.height = 'auto';
+ textarea.style.height = (textarea.scrollHeight) + 'px';
+ }
+ };
+}
+
+// Toast container component for Alpine.js
+export function renderToastContainer(Alpine) {
+ if (!Alpine) return;
+
+ // This can be used in templates via Alpine
+ window.formUtils = {
+ formatCharCount(current, max) {
+ if (max) {
+ return `${current}/${max}`;
+ }
+ return current;
+ },
+
+ isCharCountWarning(current, max) {
+ if (!max) return false;
+ return current > (max * 0.8);
+ },
+
+ isCharCountError(current, max) {
+ if (!max) return false;
+ return current >= max;
+ }
+ };
+}
+
+// Focus Trap for Modals - Week 5
+function createFocusTrap(modalElement) {
+ const focusableElements = modalElement.querySelectorAll(
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
+ );
+
+ if (focusableElements.length === 0) return;
+
+ const firstElement = focusableElements[0];
+ const lastElement = focusableElements[focusableElements.length - 1];
+
+ modalElement.addEventListener('keydown', (e) => {
+ if (e.key !== 'Tab') return;
+
+ if (e.shiftKey) {
+ // Shift + Tab
+ if (document.activeElement === firstElement) {
+ e.preventDefault();
+ lastElement.focus();
+ }
+ } else {
+ // Tab
+ if (document.activeElement === lastElement) {
+ e.preventDefault();
+ firstElement.focus();
+ }
+ }
+ });
+
+ // Set initial focus
+ firstElement.focus();
+}
+
+// Export for use in Alpine.js
+window.createFocusTrap = createFocusTrap;