11 Commits

12 changed files with 143 additions and 8 deletions
+1
View File
@@ -0,0 +1 @@
.bumpversion.toml
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+1 -1
View File
@@ -1 +1 @@
@source inline("loading-bars loading-infinity loading-circle loading-spinner loading-ring loading-ball"); #loading-overlay{display: none; position: fixed; top:0; left:0; width: 100%; height: 100%; min-height: 100vh; background: rgba(45,45,65,.6); z-index: 100;}#loading-overlay div{margin:auto} @source inline("loading-bars loading-infinity loading-circle loading-spinner loading-ring loading-ball");#loading-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;min-height:100vh;background:#2d2d4199;z-index:100}#loading-overlay div{margin:auto}
+1 -1
View File
@@ -1 +1 @@
document.addEventListener("alpine:init",()=>{Alpine.directive("confirm-modal",(c,a,{evaluate:l})=>{c.addEventListener("click",m=>{m.preventDefault();const o=document.getElementById("global-confirm-modal"),e=c.dataset;console.log("Dataset:",e),document.getElementById("confirm-title").innerHTML=e.title||"Confirm Action",document.getElementById("confirm-message").innerHTML=e.message||"Are you sure you want to proceed?",document.getElementById("confirm-cancel").innerHTML=e.cancelLabel||"Cancel",document.getElementById("confirm-confirm").innerHTML=e.confirmLabel||"Confirm",document.getElementById("confirm-confirm").addEventListener("click",()=>{var d;if(e.url)window.location.href=e.url;else if(e.action){const t=document.createElement("form");t.method="POST",t.action=e.action;const i=(d=document.querySelector('meta[name="csrf-token"]'))==null?void 0:d.content;if(i){const n=document.createElement("input");n.type="hidden",n.name="_token",n.value=i,t.appendChild(n)}if(e.method&&e.method.toUpperCase()!=="POST"){const n=document.createElement("input");n.type="hidden",n.name="_method",n.value=e.method,t.appendChild(n)}document.body.appendChild(t),t.submit()}else console.error("No URL or action specified for confirmation."),o.close()}),document.getElementById("confirm-cancel").addEventListener("click",()=>{o.close()}),o.showModal()})})}); document.addEventListener("alpine:init",()=>{Alpine.directive("confirm-modal",(t,c,{evaluate:a})=>{t.addEventListener("click",m=>{m.preventDefault();const d=document.getElementById("global-confirm-modal"),e=t.dataset;console.log("Dataset:",e),document.getElementById("confirm-title").innerHTML=e.title||"Confirm Action",document.getElementById("confirm-message").innerHTML=e.message||"Are you sure you want to proceed?",document.getElementById("confirm-cancel").innerHTML=e.cancelLabel||"Cancel",document.getElementById("confirm-confirm").innerHTML=e.confirmLabel||"Confirm",document.getElementById("confirm-confirm").addEventListener("click",()=>{var i;if(e.url)window.location.href=e.url;else if(e.action){const o=document.createElement("form");o.method="POST",o.action=e.action;const r=(i=document.querySelector('meta[name="csrf-token"]'))==null?void 0:i.content;if(r){const n=document.createElement("input");n.type="hidden",n.name="_token",n.value=r,o.appendChild(n)}if(e.method&&e.method.toUpperCase()!=="POST"){const n=document.createElement("input");n.type="hidden",n.name="_method",n.value=e.method,o.appendChild(n)}document.body.appendChild(o),o.submit()}else console.error("No URL or action specified for confirmation."),d.close()}),document.getElementById("confirm-cancel").addEventListener("click",()=>{d.close()}),d.showModal()})})});(function(){const t=()=>{document.querySelectorAll(".dropdown.dropdown-open").forEach(c=>c.classList.remove("dropdown-open")),document.querySelectorAll("details[open]").forEach(c=>c.removeAttribute("open")),document.activeElement&&document.activeElement.blur&&document.activeElement.blur()};window.addEventListener("livewire:navigated",t),window.addEventListener("livewire:navigating",t),window.addEventListener("popstate",t),window.addEventListener("pageshow",t)})();
@@ -0,0 +1,19 @@
(function () {
const closeDropdowns = () => {
document.querySelectorAll('.dropdown.dropdown-open')
.forEach(el => el.classList.remove('dropdown-open'))
document.querySelectorAll('details[open]')
.forEach(d => d.removeAttribute('open'))
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur()
}
};
window.addEventListener('livewire:navigated', closeDropdowns)
window.addEventListener('livewire:navigating', closeDropdowns)
window.addEventListener('popstate', closeDropdowns)
window.addEventListener('pageshow', closeDropdowns)
})()
+1
View File
@@ -1 +1,2 @@
import './__modals' import './__modals'
import './__close-dropdowns-on-navigate'
@@ -0,0 +1,42 @@
<div
x-data="{ isOpen: false }"
class="fixed inset-x-0 bottom-0 z-50 pointer-events-none"
@keydown.escape="isOpen = false"
>
<button
x-show="!isOpen"
x-transition
@click="isOpen = true"
:aria-expanded="isOpen"
aria-controls="help-footer-panel"
class="pointer-events-auto mx-auto mb-[max(0.5rem,env(safe-area-inset-bottom))] block rounded-full px-3 py-1 text-sm font-medium shadow ring-1 ring-black/10 bg-primary text-primary-content flex flex-row gap-1"
>
<x-lucide-help-circle class="w-5 h-5" />Aide
</button>
<div
id="help-footer-panel"
x-show="isOpen"
x-cloak
x-transition.opacity
class="pointer-events-auto mx-auto max-w-4xl w-[min(92vw,64rem)] mb-[max(0.5rem,env(safe-area-inset-bottom))]"
role="region"
aria-label="Page help"
>
<div class="rounded-2xl shadow-lg ring-1 ring-black/10 overflow-hidden bg-base-100 text-base-content">
<div class="flex items-center gap-2 px-3 py-2 border-b border-black/5">
<h2 class="text-sm font-semibold">Aide & Tips</h2>
<button
class="ml-auto px-2 py-1 text-sm rounded hover:bg-base-200"
@click="isOpen = false"
>
<x-lucide-x class="w-5 h-5" />
</button>
</div>
<div class="max-h-96 overflow-y-auto p-4 text-sm leading-relaxed">
{{ $slot }}
</div>
</div>
</div>
</div>
@@ -3,7 +3,10 @@
]) ])
<li class="z-10"> <li class="z-10">
<details> <details
x-data
x-ref="dropdown"
@click.outside="$refs.dropdown.open = false">
<summary>{{ $title }}</summary> <summary>{{ $title }}</summary>
<ul class="p-2 bg-primary"> <ul class="p-2 bg-primary">
{{ $slot }} {{ $slot }}
@@ -1,6 +1,20 @@
@props([ @props([
'label' => '', 'label' => '',
'link' => null, 'link' => null,
'useNavigate' => true,
'hover' => false,
]) ])
<li><a class="whitespace-nowrap" @if($link) href="{{ $link }}" @endif>{{ $label }}</a></li> <li>
<a
class="whitespace-nowrap"
@if($link) href="{{ $link }}" @endif
@if($useNavigate)
@if($hover)
wire:navigate.hover
@else
wire:navigate
@endif
@endif
>{{ $label }}</a>
</li>
@@ -15,7 +15,7 @@
<x-heroicon-m-bars-4 class="h-5 w-5"/> <x-heroicon-m-bars-4 class="h-5 w-5"/>
</div> </div>
<ul <ul
class="menu menu-sm dropdown-content bg-primary rounded-box z-1 mt-3 w-52 p-2 shadow w-fit"> class="menu menu-sm dropdown-content bg-primary rounded-box z-99 mt-3 w-52 p-2 shadow w-fit">
{{ $menu }} {{ $menu }}
</ul> </ul>
</div> </div>
@@ -0,0 +1,48 @@
@props([
'eventName' => 'showToast',
'position' => 'toast-top toast-center',
])
<div
x-data="{ toast: null }"
x-init="
window.addEventListener('{{ $eventName }}', event => {
toast = {
title: event.detail.title,
message: event.detail.message,
type: event.detail.type,
};
if (event.detail.scrollToTop) {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
const delay = event.detail.timeout ?? 4000;
if (delay > 0) {
setTimeout(() => toast = null, delay);
}
})
"
class="toast {{ $position }} z-50"
>
<template x-if="toast">
<template x-if="toast">
<div
x-transition
class="alert shadow-lg"
:class="{
'alert-error': toast.type === 'error',
'alert-success': toast.type === 'success',
'alert-warning': toast.type === 'warning',
'alert-info': toast.type === 'info',
}"
>
<div>
<span class="font-bold" x-html="toast.title"></span>
<span class="block text-sm" x-html="toast.message"></span>
</div>
</div>
</template>
</template>
</div>
@@ -1,8 +1,9 @@
@props([ @props([
'type' => 'bars' 'type' => 'bars',
'except' => null
]) ])
<div id="loading-overlay" wire:loading.flex> <div id="loading-overlay" wire:loading.flex wire:target.except="{{ $except }}">
<div> <div>
<span class="loading loading-{{ $type }} text-primary loading-xl"></span> <span class="loading loading-{{ $type }} text-primary loading-xl"></span>
</div> </div>