feat: Enhance UI components and add command palette

- Updated dropdown component styles and transitions for improved UX.
- Refined checkbox, input, radio, select, and textarea components with consistent error messaging and styling.
- Introduced a new card component for better layout options.
- Added a stat card component for displaying metrics with trends.
- Implemented a command palette for global search and navigation, accessible via Cmd/Ctrl+K.
- Enhanced modal and navigation components with new styles and transitions.
- Improved layout styles for better responsiveness and visual appeal.
This commit is contained in:
2026-04-22 10:40:33 -04:00
parent a910d41e6e
commit 8baf7d6c7d
19 changed files with 859 additions and 82 deletions

View File

@@ -16,37 +16,86 @@ document.addEventListener('livewire:init', () => {
Alpine.store('notifications', {
notifications: [],
init() {
Livewire.on('notify', (e) => {
this.addNotification(e);
});
},
addNotification(notification) {
notification = notification[0];
notification.show = false;
notification.id = Date.now() + Math.floor(Math.random() * 1000);
notification.type = notification.type || 'success';
notification.actions = Array.isArray(notification.actions) ? notification.actions : [];
notification.position = notification.position || 'bottom-right';
notification.timeout = notification.timeout || 5000;
notification.paused = false;
notification._remaining = notification.timeout;
notification._startedAt = Date.now();
this.notifications.push(notification);
requestAnimationFrame(() => {
this.notifications = this.notifications.map(n => {
if (n.id === notification.id) {
n.show = true;
}
if (n.id === notification.id) n.show = true;
return n;
});
});
setTimeout(() => {
this.removeNotification(notification.id);
}, notification.timeout || 5000);
this._scheduleDismiss(notification);
},
_scheduleDismiss(n) {
if (n._timer) clearTimeout(n._timer);
n._startedAt = Date.now();
n._timer = setTimeout(() => {
if (!n.paused) this.removeNotification(n.id);
}, n._remaining);
},
pauseNotification(id) {
const n = this.notifications.find(x => x.id === id);
if (!n || n.paused) return;
n.paused = true;
n._remaining -= (Date.now() - n._startedAt);
if (n._timer) { clearTimeout(n._timer); n._timer = null; }
},
resumeNotification(id) {
const n = this.notifications.find(x => x.id === id);
if (!n || !n.paused) return;
n.paused = false;
this._scheduleDismiss(n);
},
invokeAction(id, actionIdx) {
const n = this.notifications.find(x => x.id === id);
if (!n) return;
const action = n.actions?.[actionIdx];
if (!action) return;
if (action.event) {
const detail = { notificationId: id, payload: action.payload || null };
window.dispatchEvent(new CustomEvent(action.event, { detail }));
if (window.Livewire && typeof window.Livewire.dispatch === 'function') {
try { window.Livewire.dispatch(action.event, [detail]); } catch (_) {}
}
}
if (action.href) {
if (window.Livewire && typeof window.Livewire.navigate === 'function') {
window.Livewire.navigate(action.href);
} else {
window.location.href = action.href;
}
}
if (action.dismiss !== false) this.removeNotification(id);
},
removeNotification(id) {
this.notifications = this.notifications.filter(n => {
if (n.id === id) {
n.show = false;
if (n._timer) clearTimeout(n._timer);
}
return n.show;
});