Initial commit
0
resources/views/vendor/flatpickr/.gitkeep
vendored
Normal file
11
resources/views/vendor/flatpickr/components/script.blade.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<script src="{{ $url ?? config('flatpickr.js_url') ?? asset('vendor/flatpickr/js/flatpickr.js') }}"></script>
|
||||
@if(config('flatpickr.locale'))
|
||||
<script src="{{ asset('vendor/flatpickr/l10n/' . config('flatpickr.locale') . '.js') }}"></script>
|
||||
@endif
|
||||
{{-- <script>
|
||||
{!! file_get_contents(base_path('vendor/asdh/laravel-flatpickr/resources/to-be-minified/laravel-flatpickr.js')) !!}
|
||||
</script> --}}
|
||||
{{-- check /resources/to-be-minified/ folder for not minified scripts --}}
|
||||
<script>
|
||||
window.LaravelFlatpickr={__supportedEventNames:["onChange","onOpen","onClose","onMonthChange","onYearChange","onReady","onValueUpdate","onDayCreate"],initializeFlatpickr:function(e){flatpickr(document.getElementById(e.getAttribute("data-selector-id")),this.__config(e))},__config:function(e){let t=JSON.parse(e.getAttribute("data-config")),a="1"===e.getAttribute("data-disable-weekend");a&&t.disable.push(this.__disableWeekends());let i=e.getAttribute("data-locale"),n=Number(e.getAttribute("data-first-day-of-week")),r=flatpickr.l10ns[i]||{};return{...t,locale:{...r,firstDayOfWeek:n},...this.__events(e)}},__disableWeekends:function(){return function(e){return 0===e.getDay()||6===e.getDay()}},__events:function(el){let elEvents;return el.getAttributeNames().filter(e=>e.startsWith("on")).reduce((obj,elEventName)=>{let eventName=this.__supportedEventNames.find(e=>e.toLowerCase()===elEventName);return eventName&&(obj[eventName]=function(...params){eval(el.getAttribute(eventName))(...params)}),obj},{})}},document.addEventListener("DOMContentLoaded",function(e){document.querySelectorAll(".flatpickr-input").forEach(e=>window.LaravelFlatpickr.initializeFlatpickr(e))});var observer=new MutationObserver(e=>{e.forEach(e=>{e.removedNodes.length>0&&window.LaravelFlatpickr.initializeFlatpickr(e.previousSibling)})});document.querySelectorAll(".flatpickr-container").forEach(e=>{observer.observe(e,{childList:!0})});
|
||||
</script>
|
||||
8
resources/views/vendor/flatpickr/components/style.blade.php
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<link rel="stylesheet" href="{{ $url ?? config('flatpickr.css_url') ?? asset('vendor/flatpickr/css/flatpickr.css') }}">
|
||||
<link rel="stylesheet" href="{{ asset('css/flatpickr-custom.css') }}">
|
||||
@if(config('flatpickr.use_style'))
|
||||
{{-- check /resources/to-be-minified/ folder for not minified styles --}}
|
||||
<style>
|
||||
.flatpickr-container{display:flex}.flatpickr-container .form-control,.flatpickr-input{flex-grow:1;appearance:none;background-color:#fff;border:1px solid #6b7280;padding:.5rem .75rem;line-height:1.5rem}.flatpickr-container .form-control[readonly],.flatpickr-input[readonly]{cursor:pointer}.flatpickr-container .form-control[disabled],.flatpickr-input[disabled]{cursor:not-allowed;background:#ededed}.flatpickr-clearable{cursor:pointer;display:flex;align-items:center;background-color:#fff;border:1px solid #6b7280;border-left-width:0;padding:.5rem 1rem}.flatpickr-clearable>svg{width:1rem}
|
||||
</style>
|
||||
@endif
|
||||
1
resources/views/vendor/flatpickr/flatpickr/clear.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M9.207 8.5l6.646 6.646-.707.707L8.5 9.207l-6.646 6.646-.707-.707L7.793 8.5 1.146 1.854l.707-.707L8.5 7.793l6.646-6.646.707.707L9.207 8.5z' fill='#000'></path></svg>
|
||||
|
After Width: | Height: | Size: 283 B |
24
resources/views/vendor/flatpickr/flatpickr/index.blade.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<div id="{{ $containerId() }}" class="flatpickr-container">
|
||||
<input
|
||||
type="text"
|
||||
id="{{ $id }}"
|
||||
{{ $attributes->class(['flatpickr-input']) }}
|
||||
data-selector-id="{{ $selectorId() }}"
|
||||
data-config='@json($config())'
|
||||
data-disable-weekend="{{ $disableWeekend?1:0 }}"
|
||||
data-first-day-of-week="{{ $firstDayOfWeek }}"
|
||||
data-locale="{{ config('flatpickr.locale', 'default') }}"
|
||||
data-input
|
||||
/>
|
||||
|
||||
@if($clearable)
|
||||
<a id="{{ $id }}-clearable" title="clear" class="flatpickr-clearable" data-clear>
|
||||
@if(isset($clearer))
|
||||
{{ $clearer }}
|
||||
@else
|
||||
{!! $defaultClearer() !!}
|
||||
@endif
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
102
resources/views/vendor/livewire/bootstrap.blade.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
@php
|
||||
if (! isset($scrollTo)) {
|
||||
$scrollTo = 'body';
|
||||
}
|
||||
|
||||
$scrollIntoViewJsSnippet = ($scrollTo !== false)
|
||||
? <<<JS
|
||||
(\$el.closest('{$scrollTo}') || document.querySelector('{$scrollTo}')).scrollIntoView()
|
||||
JS
|
||||
: '';
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
@if ($paginator->hasPages())
|
||||
<nav class="d-flex justify-items-center justify-content-between">
|
||||
<div class="d-flex justify-content-between flex-fill d-sm-none">
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.previous')</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<button type="button" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled">@lang('pagination.previous')</button>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<button type="button" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled">@lang('pagination.next')</button>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link" aria-hidden="true">@lang('pagination.next')</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="d-none flex-sm-fill d-sm-flex align-items-sm-center justify-content-sm-between">
|
||||
<div>
|
||||
<p class="small text-muted">
|
||||
{!! __('Showing') !!}
|
||||
<span class="fw-semibold">{{ $paginator->firstItem() }}</span>
|
||||
{!! __('to') !!}
|
||||
<span class="fw-semibold">{{ $paginator->lastItem() }}</span>
|
||||
{!! __('of') !!}
|
||||
<span class="fw-semibold">{{ $paginator->total() }}</span>
|
||||
{!! __('results') !!}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||
<span class="page-link" aria-hidden="true">‹</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<button type="button" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" aria-label="@lang('pagination.previous')">‹</button>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="page-item active" wire:key="paginator-{{ $paginator->getPageName() }}-page-{{ $page }}" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||
@else
|
||||
<li class="page-item" wire:key="paginator-{{ $paginator->getPageName() }}-page-{{ $page }}"><button type="button" class="page-link" wire:click="gotoPage({{ $page }}, '{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}">{{ $page }}</button></li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<button type="button" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" aria-label="@lang('pagination.next')">›</button>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||
<span class="page-link" aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
53
resources/views/vendor/livewire/simple-bootstrap.blade.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
@php
|
||||
if (! isset($scrollTo)) {
|
||||
$scrollTo = 'body';
|
||||
}
|
||||
|
||||
$scrollIntoViewJsSnippet = ($scrollTo !== false)
|
||||
? <<<JS
|
||||
(\$el.closest('{$scrollTo}') || document.querySelector('{$scrollTo}')).scrollIntoView()
|
||||
JS
|
||||
: '';
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.previous')</span>
|
||||
</li>
|
||||
@else
|
||||
@if(method_exists($paginator,'getCursorName'))
|
||||
<li class="page-item">
|
||||
<button dusk="previousPage" type="button" class="page-link" wire:key="cursor-{{ $paginator->getCursorName() }}-{{ $paginator->previousCursor()->encode() }}" wire:click="setPage('{{$paginator->previousCursor()->encode()}}','{{ $paginator->getCursorName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled">@lang('pagination.previous')</button>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<button type="button" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled">@lang('pagination.previous')</button>
|
||||
</li>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
@if(method_exists($paginator,'getCursorName'))
|
||||
<li class="page-item">
|
||||
<button dusk="nextPage" type="button" class="page-link" wire:key="cursor-{{ $paginator->getCursorName() }}-{{ $paginator->nextCursor()->encode() }}" wire:click="setPage('{{$paginator->nextCursor()->encode()}}','{{ $paginator->getCursorName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled">@lang('pagination.next')</button>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<button type="button" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled">@lang('pagination.next')</button>
|
||||
</li>
|
||||
@endif
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.next')</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
56
resources/views/vendor/livewire/simple-tailwind.blade.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
@php
|
||||
if (! isset($scrollTo)) {
|
||||
$scrollTo = 'body';
|
||||
}
|
||||
|
||||
$scrollIntoViewJsSnippet = ($scrollTo !== false)
|
||||
? <<<JS
|
||||
(\$el.closest('{$scrollTo}') || document.querySelector('{$scrollTo}')).scrollIntoView()
|
||||
JS
|
||||
: '';
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
@if ($paginator->hasPages())
|
||||
<nav role="navigation" aria-label="Pagination Navigation" class="flex justify-between">
|
||||
<span>
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
|
||||
{!! __('pagination.previous') !!}
|
||||
</span>
|
||||
@else
|
||||
@if(method_exists($paginator,'getCursorName'))
|
||||
<button type="button" dusk="previousPage" wire:key="cursor-{{ $paginator->getCursorName() }}-{{ $paginator->previousCursor()->encode() }}" wire:click="setPage('{{$paginator->previousCursor()->encode()}}','{{ $paginator->getCursorName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-blue-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.previous') !!}
|
||||
</button>
|
||||
@else
|
||||
<button
|
||||
type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-blue-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.previous') !!}
|
||||
</button>
|
||||
@endif
|
||||
@endif
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
@if(method_exists($paginator,'getCursorName'))
|
||||
<button type="button" dusk="nextPage" wire:key="cursor-{{ $paginator->getCursorName() }}-{{ $paginator->nextCursor()->encode() }}" wire:click="setPage('{{$paginator->nextCursor()->encode()}}','{{ $paginator->getCursorName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-blue-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.next') !!}
|
||||
</button>
|
||||
@else
|
||||
<button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-blue-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.next') !!}
|
||||
</button>
|
||||
@endif
|
||||
@else
|
||||
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
|
||||
{!! __('pagination.next') !!}
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
126
resources/views/vendor/livewire/tailwind.blade.php
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
@php
|
||||
if (! isset($scrollTo)) {
|
||||
$scrollTo = 'body';
|
||||
}
|
||||
|
||||
$scrollIntoViewJsSnippet = ($scrollTo !== false)
|
||||
? <<<JS
|
||||
(\$el.closest('{$scrollTo}') || document.querySelector('{$scrollTo}')).scrollIntoView()
|
||||
JS
|
||||
: '';
|
||||
@endphp
|
||||
|
||||
<div>
|
||||
@if ($paginator->hasPages())
|
||||
<nav role="navigation" aria-label="Pagination Navigation" class="flex items-center justify-between">
|
||||
<div class="flex justify-between flex-1 sm:hidden">
|
||||
<span>
|
||||
@if ($paginator->onFirstPage())
|
||||
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.previous') !!}
|
||||
</span>
|
||||
@else
|
||||
<button type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.before" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-blue-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.previous') !!}
|
||||
</button>
|
||||
@endif
|
||||
</span>
|
||||
|
||||
<span>
|
||||
@if ($paginator->hasMorePages())
|
||||
<button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" wire:loading.attr="disabled" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.before" class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-blue-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||
{!! __('pagination.next') !!}
|
||||
</button>
|
||||
@else
|
||||
<span class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
|
||||
{!! __('pagination.next') !!}
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-700 leading-5 dark:text-gray-400">
|
||||
<span>{!! __('Showing') !!}</span>
|
||||
<span class="font-medium">{{ $paginator->firstItem() }}</span>
|
||||
<span>{!! __('to') !!}</span>
|
||||
<span class="font-medium">{{ $paginator->lastItem() }}</span>
|
||||
<span>{!! __('of') !!}</span>
|
||||
<span class="font-medium">{{ $paginator->total() }}</span>
|
||||
<span>{!! __('results') !!}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="relative z-0 inline-flex rtl:flex-row-reverse rounded-md shadow-sm">
|
||||
<span>
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<span aria-disabled="true" aria-label="{{ __('pagination.previous') }}">
|
||||
<span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5 dark:bg-gray-800 dark:border-gray-600" aria-hidden="true">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
@else
|
||||
<button type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.after" class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:ring ring-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:active:bg-gray-700 dark:focus:border-blue-800" aria-label="{{ __('pagination.previous') }}">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
@endif
|
||||
</span>
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<span aria-disabled="true">
|
||||
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300">{{ $element }}</span>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
<span wire:key="paginator-{{ $paginator->getPageName() }}-page{{ $page }}">
|
||||
@if ($page == $paginator->currentPage())
|
||||
<span aria-current="page">
|
||||
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 dark:bg-gray-800 dark:border-gray-600">{{ $page }}</span>
|
||||
</span>
|
||||
@else
|
||||
<button type="button" wire:click="gotoPage({{ $page }}, '{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:border-blue-300 focus:ring ring-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 dark:hover:text-gray-300 dark:active:bg-gray-700 dark:focus:border-blue-800" aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
|
||||
{{ $page }}
|
||||
</button>
|
||||
@endif
|
||||
</span>
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
<span>
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" x-on:click="{{ $scrollIntoViewJsSnippet }}" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.after" class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:ring ring-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:active:bg-gray-700 dark:focus:border-blue-800" aria-label="{{ __('pagination.next') }}">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
@else
|
||||
<span aria-disabled="true" aria-label="{{ __('pagination.next') }}">
|
||||
<span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5 dark:bg-gray-800 dark:border-gray-600" aria-hidden="true">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
||||
19
resources/views/vendor/mail/html/button.blade.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="margin: 40px auto; text-align: center;">
|
||||
<tr>
|
||||
<td align="center" style="text-align: center;">
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" style="text-align: center;">
|
||||
<tr>
|
||||
<td align="center" style="text-align: center;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="display: inline-table; margin: 0 auto;">
|
||||
<tr>
|
||||
<td style="border-radius: 6px; background-color: {{ $color === 'success' ? '#48bb78' : ($color === 'error' ? '#e53e3e' : theme_color('brand')) }}; text-align: center;">
|
||||
<a href="{{ $url }}" class="button button-{{ $color ?? 'primary' }}" target="_blank" rel="noopener" style="display: inline-block; padding: 12px 24px; font-family: {{ theme_font('font_family_body') }}; font-size: 14px; font-weight: bold; color: #ffffff; text-decoration: none; border-radius: 6px; text-align: center;">{{ $slot }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
11
resources/views/vendor/mail/html/footer.blade.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<tr>
|
||||
<td>
|
||||
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="content-cell" align="center" style="font-size: 12px; color: {{ theme_color('text.secondary') }}; text-align: center;">
|
||||
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
7
resources/views/vendor/mail/html/header.blade.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<tr>
|
||||
<td class="header">
|
||||
<a href="{{ $url }}" style="display: inline-block; text-decoration: none;">
|
||||
<img src="{{ asset('storage/' . theme_logo('email_logo')) }}" alt="{{ config('app.name') }}" style="max-width: 90px; height: auto; display: block; margin: 0 auto; filter: invert(1);" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
56
resources/views/vendor/mail/html/layout.blade.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="color-scheme" content="light">
|
||||
<meta name="supported-color-schemes" content="light">
|
||||
<style>
|
||||
@media only screen and (max-width: 600px) {
|
||||
.inner-body {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
{{ $header ?? '' }}
|
||||
|
||||
<!-- Email Body -->
|
||||
<tr>
|
||||
<td class="body" width="100%" cellpadding="0" cellspacing="0">
|
||||
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<!-- Body content -->
|
||||
<tr>
|
||||
<td class="content-cell">
|
||||
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||
|
||||
{{ $subcopy ?? '' }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{{ $footer ?? '' }}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
30
resources/views/vendor/mail/html/message.blade.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
@component('mail::layout')
|
||||
{{-- Header --}}
|
||||
@slot('header')
|
||||
@component('mail::header', ['url' => config('app.url')])
|
||||
<img src="{{ asset('storage/' . theme_logo('email_logo', 'app-images/app_mail_logo.png')) }}" class="logo" alt="{{ config('app.name') }} logo">
|
||||
@lang('Your time is currency')
|
||||
@endcomponent
|
||||
@endslot
|
||||
|
||||
{{-- Body --}}
|
||||
{{ $slot }}
|
||||
|
||||
{{-- Subcopy --}}
|
||||
@isset($subcopy)
|
||||
@slot('subcopy')
|
||||
@component('mail::subcopy')
|
||||
{{ $subcopy }}
|
||||
@endcomponent
|
||||
@endslot
|
||||
@endisset
|
||||
|
||||
{{-- Footer --}}
|
||||
@slot('footer')
|
||||
@component('mail::footer')
|
||||
<a href="{{ route('profile.settings.no_locale') }}#message_settings">
|
||||
@lang('Update your message settings')
|
||||
</a>
|
||||
@endcomponent
|
||||
@endslot
|
||||
@endcomponent
|
||||
14
resources/views/vendor/mail/html/panel.blade.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="border-left: {{ theme_color('brand') }} solid 4px; margin: 21px 0;">
|
||||
<tr>
|
||||
<td class="panel-content" style="background-color: #f3f4f6; color: {{ theme_color('text.primary') }}; padding: 16px;">
|
||||
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||
<tr>
|
||||
<td class="panel-item" style="padding: 0;">
|
||||
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
7
resources/views/vendor/mail/html/subcopy.blade.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="border-top: 1px solid #e8e5ef; margin-top: 25px; padding-top: 25px;">
|
||||
<tr>
|
||||
<td style="font-size: 14px; color: {{ theme_color('text.secondary') }};">
|
||||
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
3
resources/views/vendor/mail/html/table.blade.php
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="table" style="margin: 30px 0; font-family: {{ theme_font('font_family_body') }};">
|
||||
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||
</div>
|
||||
289
resources/views/vendor/mail/html/themes/default.css
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
/* Base */
|
||||
|
||||
body,
|
||||
body *:not(html):not(style):not(br):not(tr):not(code) {
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-text-size-adjust: none;
|
||||
background-color: #fefefe;
|
||||
color: #374151;
|
||||
height: 100%;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
blockquote {
|
||||
line-height: 1.4;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
|
||||
h1 {
|
||||
color: #111827;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 1.5em;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p.sub {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
|
||||
.wrapper {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
background-color: #f3f4f6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
.header {
|
||||
padding: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header a {
|
||||
color: #111827;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
|
||||
.logo {
|
||||
height: 56px;
|
||||
max-height: 56px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
/* Body */
|
||||
|
||||
.body {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
background-color: #f3f4f6;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
border-top: 1px solid #f3f4f6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inner-body {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 570px;
|
||||
background-color: #ffffff;
|
||||
border-color: #e8e5ef;
|
||||
border-radius: 8px;
|
||||
border-width: 1px;
|
||||
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.1); margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 570px;
|
||||
}
|
||||
|
||||
/* Subcopy */
|
||||
|
||||
.subcopy {
|
||||
border-top: 1px solid #e8e5ef;
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.subcopy p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
.footer {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 570px;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #6b7280;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
|
||||
.table table {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
margin: 30px auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table th {
|
||||
border-bottom: 1px solid #edeff2;
|
||||
margin: 0;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.table td {
|
||||
color: #74787e;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.content-cell {
|
||||
max-width: 100vw;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.action {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
-webkit-text-size-adjust: none;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-blue,
|
||||
.button-primary {
|
||||
background-color: #111827;
|
||||
border-bottom: 8px solid #111827;
|
||||
border-left: 18px solid #111827;
|
||||
border-right: 18px solid #111827;
|
||||
border-top: 8px solid #111827;
|
||||
}
|
||||
|
||||
.button-green,
|
||||
.button-success {
|
||||
background-color: #48bb78;
|
||||
border-bottom: 8px solid #48bb78;
|
||||
border-left: 18px solid #48bb78;
|
||||
border-right: 18px solid #48bb78;
|
||||
border-top: 8px solid #48bb78;
|
||||
}
|
||||
|
||||
.button-red,
|
||||
.button-error {
|
||||
background-color: #e53e3e;
|
||||
border-bottom: 8px solid #e53e3e;
|
||||
border-left: 18px solid #e53e3e;
|
||||
border-right: 18px solid #e53e3e;
|
||||
border-top: 8px solid #e53e3e;
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
|
||||
.panel {
|
||||
border-left: #111827 solid 4px;
|
||||
margin: 21px 0;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
background-color: #f3f4f6;
|
||||
color: #111827;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.panel-content p {
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.panel-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.panel-item p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
.break-all {
|
||||
word-break: break-all;
|
||||
}
|
||||
298
resources/views/vendor/mail/html/themes/timebank.css
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
/* Base */
|
||||
|
||||
body,
|
||||
body *:not(html):not(style):not(br):not(tr):not(code) {
|
||||
font-family: {{ theme_font('font_family_body') }}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-text-size-adjust: none;
|
||||
background-color: #fefefe;
|
||||
color: {{ theme_color('text.primary') }};
|
||||
height: 100%;
|
||||
line-height: {{ theme_font('line_height_base') }};
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
blockquote {
|
||||
line-height: {{ theme_font('line_height_base') }};
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
a {
|
||||
color: {{ theme_color('brand') }};
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
|
||||
h1 {
|
||||
color: {{ theme_color('text.primary') }};
|
||||
font-family: {{ theme_font('font_family_heading') }};
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
text-transform: {{ theme_font('heading_transform') }};
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: {{ theme_color('text.primary') }};
|
||||
font-family: {{ theme_font('font_family_heading') }};
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
text-transform: {{ theme_font('heading_transform') }};
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: {{ theme_color('text.primary') }};
|
||||
font-family: {{ theme_font('font_family_heading') }};
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
text-transform: {{ theme_font('heading_transform') }};
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 1.5em;
|
||||
margin-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p.sub {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
|
||||
.wrapper {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
background-color: #f3f4f6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
|
||||
.header {
|
||||
padding: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header a {
|
||||
color: {{ theme_color('brand') }};
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
|
||||
.logo {
|
||||
height: 56px;
|
||||
max-height: 56px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
/* Body */
|
||||
|
||||
.body {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
background-color: #f3f4f6;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
border-top: 1px solid #f3f4f6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inner-body {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 570px;
|
||||
background-color: #ffffff;
|
||||
border-color: #e8e5ef;
|
||||
border-radius: 8px;
|
||||
border-width: 1px;
|
||||
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.1);
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 570px;
|
||||
}
|
||||
|
||||
/* Subcopy */
|
||||
|
||||
.subcopy {
|
||||
border-top: 1px solid #e8e5ef;
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.subcopy p {
|
||||
font-size: 14px;
|
||||
color: {{ theme_color('text.secondary') }};
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
.footer {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 570px;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 570px;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
color: {{ theme_color('text.secondary') }};
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: {{ theme_color('text.secondary') }};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
|
||||
.table table {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
margin: 30px auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table th {
|
||||
border-bottom: 1px solid #edeff2;
|
||||
margin: 0;
|
||||
padding-bottom: 8px;
|
||||
font-weight: bold;
|
||||
color: {{ theme_color('text.primary') }};
|
||||
}
|
||||
|
||||
.table td {
|
||||
color: {{ theme_color('text.secondary') }};
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.content-cell {
|
||||
max-width: 100vw;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.action {
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
-premailer-width: 100%;
|
||||
margin: 30px auto;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
-webkit-text-size-adjust: none;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-blue,
|
||||
.button-primary {
|
||||
background-color: {{ theme_color('brand') }};
|
||||
border-bottom: 8px solid {{ theme_color('brand') }};
|
||||
border-left: 18px solid {{ theme_color('brand') }};
|
||||
border-right: 18px solid {{ theme_color('brand') }};
|
||||
border-top: 8px solid {{ theme_color('brand') }};
|
||||
}
|
||||
|
||||
.button-green,
|
||||
.button-success {
|
||||
background-color: #48bb78;
|
||||
border-bottom: 8px solid #48bb78;
|
||||
border-left: 18px solid #48bb78;
|
||||
border-right: 18px solid #48bb78;
|
||||
border-top: 8px solid #48bb78;
|
||||
}
|
||||
|
||||
.button-red,
|
||||
.button-error {
|
||||
background-color: #e53e3e;
|
||||
border-bottom: 8px solid #e53e3e;
|
||||
border-left: 18px solid #e53e3e;
|
||||
border-right: 18px solid #e53e3e;
|
||||
border-top: 8px solid #e53e3e;
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
|
||||
.panel {
|
||||
border-left: {{ theme_color('brand') }} solid 4px;
|
||||
margin: 21px 0;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
background-color: #f3f4f6;
|
||||
color: {{ theme_color('text.primary') }};
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.panel-content p {
|
||||
color: {{ theme_color('text.primary') }};
|
||||
}
|
||||
|
||||
.panel-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.panel-item p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
.break-all {
|
||||
word-break: break-all;
|
||||
}
|
||||
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{{ $slot }}: {{ $url }}
|
||||
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{{ $slot }}
|
||||
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[{{ $slot }}]({{ $url }})
|
||||
9
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{!! strip_tags($header) !!}
|
||||
|
||||
{!! strip_tags($slot) !!}
|
||||
@isset($subcopy)
|
||||
|
||||
{!! strip_tags($subcopy) !!}
|
||||
@endisset
|
||||
|
||||
{!! strip_tags($footer) !!}
|
||||
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
@component('mail::layout')
|
||||
{{-- Header --}}
|
||||
@slot('header')
|
||||
@component('mail::header', ['url' => config('app.url')])
|
||||
{{ config('app.name') }}
|
||||
@endcomponent
|
||||
@endslot
|
||||
|
||||
{{-- Body --}}
|
||||
{{ $slot }}
|
||||
|
||||
{{-- Subcopy --}}
|
||||
@isset($subcopy)
|
||||
@slot('subcopy')
|
||||
@component('mail::subcopy')
|
||||
{{ $subcopy }}
|
||||
@endcomponent
|
||||
@endslot
|
||||
@endisset
|
||||
|
||||
{{-- Footer --}}
|
||||
@slot('footer')
|
||||
@component('mail::footer')
|
||||
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
|
||||
@endcomponent
|
||||
@endslot
|
||||
@endcomponent
|
||||
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{{ $slot }}
|
||||
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{{ $slot }}
|
||||
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{{ $slot }}
|
||||
3
resources/views/vendor/social-share/icons/bluesky.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 655 B |
1
resources/views/vendor/social-share/icons/email.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"></path></svg>
|
||||
|
After Width: | Height: | Size: 571 B |
1
resources/views/vendor/social-share/icons/facebook.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M279.14 288l14.22-92.66h-88.91v-60.13c0-25.35 12.42-50.06 52.24-50.06h40.42V6.26S260.43 0 225.36 0c-73.22 0-121.08 44.38-121.08 124.72v70.62H22.89V288h81.39v224h100.17V288z"></path></svg>
|
||||
|
After Width: | Height: | Size: 279 B |
3
resources/views/vendor/social-share/icons/instagram.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
resources/views/vendor/social-share/icons/linkedin.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"></path></svg>
|
||||
|
After Width: | Height: | Size: 407 B |
3
resources/views/vendor/social-share/icons/mastodon.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M23.193 7.879c0-5.206-3.411-6.732-3.411-6.732C18.062.357 15.108.025 12.041 0h-.076c-3.068.025-6.02.357-7.74 1.147 0 0-3.411 1.526-3.411 6.732 0 1.192-.023 2.618.015 4.129.124 5.092.934 10.109 5.641 11.355 2.17.574 4.034.695 5.535.612 2.722-.15 4.25-.972 4.25-.972l-.09-1.975s-1.945.613-4.129.539c-2.165-.074-4.449-.233-4.799-2.891a5.499 5.499 0 0 1-.048-.745s2.125.52 4.817.643c1.646.075 3.19-.097 4.758-.283 3.007-.359 5.625-2.212 5.954-3.905.517-2.665.475-6.507.475-6.507zm-4.024 6.709h-2.497V8.469c0-1.29-.543-1.943-1.628-1.943-1.2 0-1.801.776-1.801 2.312v3.349h-2.483v-3.349c0-1.536-.601-2.312-1.802-2.312-1.085 0-1.628.653-1.628 1.943v6.119H4.832V8.284c0-1.289.328-2.313.987-3.07.68-.758 1.569-1.146 2.674-1.146 1.278 0 2.246.491 2.886 1.474L12 6.585l.622-1.043c.64-.983 1.608-1.474 2.886-1.474 1.104 0 1.994.388 2.674 1.146.658.757.986 1.781.986 3.07v6.304z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 968 B |
1
resources/views/vendor/social-share/icons/pinterest.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M204 6.5C101.4 6.5 0 74.9 0 185.6 0 256 39.6 296 63.6 296c9.9 0 15.6-27.6 15.6-35.4 0-9.3-23.7-29.1-23.7-67.8 0-80.4 61.2-137.4 140.4-137.4 68.1 0 118.5 38.7 118.5 109.8 0 53.1-21.3 152.7-90.3 152.7-24.9 0-46.2-18-46.2-43.8 0-37.8 26.4-74.4 26.4-113.4 0-66.2-93.9-54.2-93.9 25.8 0 16.8 2.1 35.4 9.6 50.7-13.8 59.4-42 147.9-42 209.1 0 18.9 2.7 37.5 4.5 56.4 3.4 3.8 1.7 3.4 6.9 1.5 50.4-69 48.6-82.5 71.4-172.8 12.3 23.4 44.1 36 69.3 36 106.2 0 153.9-103.5 153.9-196.8C384 71.3 298.2 6.5 204 6.5z"></path></svg>
|
||||
|
After Width: | Height: | Size: 602 B |
1
resources/views/vendor/social-share/icons/reddit.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M440.3 203.5c-15 0-28.2 6.2-37.9 15.9-35.7-24.7-83.8-40.6-137.1-42.3L293 52.3l88.2 19.8c0 21.6 17.6 39.2 39.2 39.2 22 0 39.7-18.1 39.7-39.7s-17.6-39.7-39.7-39.7c-15.4 0-28.7 9.3-35.3 22l-97.4-21.6c-4.9-1.3-9.7 2.2-11 7.1L246.3 177c-52.9 2.2-100.5 18.1-136.3 42.8-9.7-10.1-23.4-16.3-38.4-16.3-55.6 0-73.8 74.6-22.9 100.1-1.8 7.9-2.6 16.3-2.6 24.7 0 83.8 94.4 151.7 210.3 151.7 116.4 0 210.8-67.9 210.8-151.7 0-8.4-.9-17.2-3.1-25.1 49.9-25.6 31.5-99.7-23.8-99.7zM129.4 308.9c0-22 17.6-39.7 39.7-39.7 21.6 0 39.2 17.6 39.2 39.7 0 21.6-17.6 39.2-39.2 39.2-22 .1-39.7-17.6-39.7-39.2zm214.3 93.5c-36.4 36.4-139.1 36.4-175.5 0-4-3.5-4-9.7 0-13.7 3.5-3.5 9.7-3.5 13.2 0 27.8 28.5 120 29 149 0 3.5-3.5 9.7-3.5 13.2 0 4.1 4 4.1 10.2.1 13.7zm-.8-54.2c-21.6 0-39.2-17.6-39.2-39.2 0-22 17.6-39.7 39.2-39.7 22 0 39.7 17.6 39.7 39.7-.1 21.5-17.7 39.2-39.7 39.2z"></path></svg>
|
||||
|
After Width: | Height: | Size: 953 B |
3
resources/views/vendor/social-share/icons/signal.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12.001.002C5.374.002.002 5.374.002 12.001c0 2.15.568 4.165 1.56 5.903l-1.558 4.664 4.848-1.535c1.671.904 3.588 1.421 5.634 1.421 6.627 0 12-5.373 12-11.999C22.486 5.372 17.114.002 12.001.002zm6.168 15.835c-.274.773-.968 1.415-1.739 1.653-.52.161-1.198.292-3.486-.749-2.942-1.338-4.827-4.346-4.973-4.544-.143-.198-1.176-1.565-1.176-2.986 0-1.421.745-2.121 1.008-2.408.264-.287.576-.359.768-.359.192 0 .384.002.552.01.177.009.415-.067.648.495.238.574.81 1.977.88 2.121.07.144.117.312.024.504-.093.198-.14.321-.277.486-.137.165-.288.368-.411.494-.137.143-.28.298-.12.584.16.287.71 1.173 1.525 1.899 1.048.932 1.932 1.222 2.206 1.359.274.137.434.114.594-.07.16-.184.687-.802.871-1.077.184-.275.368-.229.622-.137.254.092 1.612.76 1.887.899.275.139.458.208.525.321.067.113.067.649-.207 1.422z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 892 B |
1
resources/views/vendor/social-share/icons/telegram.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"></path></svg>
|
||||
|
After Width: | Height: | Size: 347 B |
1
resources/views/vendor/social-share/icons/twitter.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>
|
||||
|
After Width: | Height: | Size: 898 B |
1
resources/views/vendor/social-share/icons/whatsapp.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M380.9 97.1C339 55.1 283.2 32 223.9 32c-122.4 0-222 99.6-222 222 0 39.1 10.2 77.3 29.6 111L0 480l117.7-30.9c32.4 17.7 68.9 27 106.1 27h.1c122.3 0 224.1-99.6 224.1-222 0-59.3-25.2-115-67.1-157zm-157 341.6c-33.2 0-65.7-8.9-94-25.7l-6.7-4-69.8 18.3L72 359.2l-4.4-7c-18.5-29.4-28.2-63.3-28.2-98.2 0-101.7 82.8-184.5 184.6-184.5 49.3 0 95.6 19.2 130.4 54.1 34.8 34.9 56.2 81.2 56.1 130.5 0 101.8-84.9 184.6-186.6 184.6zm101.2-138.2c-5.5-2.8-32.8-16.2-37.9-18-5.1-1.9-8.8-2.8-12.5 2.8-3.7 5.6-14.3 18-17.6 21.8-3.2 3.7-6.5 4.2-12 1.4-32.6-16.3-54-29.1-75.5-66-5.7-9.8 5.7-9.1 16.3-30.3 1.8-3.7.9-6.9-.5-9.7-1.4-2.8-12.5-30.1-17.1-41.2-4.5-10.8-9.1-9.3-12.5-9.5-3.2-.2-6.9-.2-10.6-.2-3.7 0-9.7 1.4-14.8 6.9-5.1 5.6-19.4 19-19.4 46.3 0 27.3 19.9 53.7 22.6 57.4 2.8 3.7 39.1 59.7 94.8 83.8 35.2 15.2 49 16.5 66.6 13.9 10.7-1.6 32.8-13.4 37.4-26.4 4.6-13 4.6-24.1 3.2-26.4-1.3-2.5-5-3.9-10.5-6.6z"></path></svg>
|
||||
|
After Width: | Height: | Size: 993 B |
1
resources/views/vendor/social-share/icons/x.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8l164.9-188.5L26.8 48h145.6l100.5 132.9L389.2 48zm-24.8 373.8h39.1L151.1 88h-42l255.3 333.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 256 B |
56
resources/views/vendor/social-share/social-share.blade.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<div x-data="{ canShare: !!navigator.share }" class="flex flex-wrap gap-2 {{ $class }}">
|
||||
@foreach ($services as $service)
|
||||
<a x-show="!canShare"
|
||||
x-cloak
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2"
|
||||
href="{{ $service->url() }}"
|
||||
target="_blank"
|
||||
rel="noopener nofollow"
|
||||
title="{{ $service->label }}">
|
||||
@php
|
||||
$iconPath = storage_path('app/public/app-images/' . $service->name . '.svg');
|
||||
@endphp
|
||||
@if(file_exists($iconPath))
|
||||
<img src="{{ asset('storage/app-images/' . $service->name . '.svg') }}" alt="{{ $service->label }}" class="h-6 w-6" />
|
||||
@else
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
{!! $service->icon() !!}
|
||||
</span>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
|
||||
<button
|
||||
x-show="canShare"
|
||||
x-cloak
|
||||
@click="navigator.share({ url: window.location.href })"
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2"
|
||||
title="{{ __('Share') }}"
|
||||
type="button">
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
x-data="{ copied: false }"
|
||||
@click="navigator.clipboard.writeText(window.location.href); copied = true; setTimeout(() => copied = false, 2000)"
|
||||
class="inline-flex items-center justify-center rounded-md border border-theme-border bg-theme-surface p-2 transition-all duration-200 hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-theme-surface focus:ring-offset-2"
|
||||
:title="copied ? '{{ __('Copied!') }}' : '{{ __('Copy link') }}'"
|
||||
type="button">
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center">
|
||||
<template x-if="!copied">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244" />
|
||||
</svg>
|
||||
</template>
|
||||
<template x-if="copied">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-5 w-5 text-green-500">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||
</svg>
|
||||
</template>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
BIN
resources/views/vendor/wirechat/.DS_Store
vendored
Normal file
4
resources/views/vendor/wirechat/components/actions/close-modal.blade.php
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
<div onclick="Livewire.dispatch('closeWireChatModal')">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
11
resources/views/vendor/wirechat/components/actions/new-chat.blade.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
@props([
|
||||
'widget' => false
|
||||
])
|
||||
|
||||
|
||||
<x-wirechat::actions.open-modal
|
||||
component="wirechat.new.chat"
|
||||
:widget="$widget"
|
||||
>
|
||||
{{$slot}}
|
||||
</x-wirechat::actions.open-modal>
|
||||
11
resources/views/vendor/wirechat/components/actions/new-group.blade.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
@props([
|
||||
'widget' => false
|
||||
])
|
||||
|
||||
|
||||
<x-wirechat::actions.open-modal
|
||||
component="wirechat.new.group"
|
||||
:widget="$widget"
|
||||
>
|
||||
{{$slot}}
|
||||
</x-wirechat::actions.open-modal>
|
||||
16
resources/views/vendor/wirechat/components/actions/open-chat-drawer.blade.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
@props([
|
||||
'component',
|
||||
'conversation' => null,
|
||||
'widget' => false
|
||||
])
|
||||
|
||||
<div {{ $attributes }} onclick="Livewire.dispatch('openChatDrawer', {
|
||||
component: '{{ $component }}',
|
||||
arguments: {
|
||||
conversation: `{{$conversation ?? null }}`,
|
||||
widget: @js($widget)
|
||||
}
|
||||
})">
|
||||
|
||||
{{ $slot }}
|
||||
</div>
|
||||
16
resources/views/vendor/wirechat/components/actions/open-modal.blade.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
@props([
|
||||
'component',
|
||||
'conversation' => null,
|
||||
'widget' => false
|
||||
])
|
||||
|
||||
<div onclick="Livewire.dispatch('openWireChatModal', {
|
||||
component: '{{ $component }}',
|
||||
arguments: {
|
||||
conversation:`{{$conversation ?? null }}`,
|
||||
widget: @js($widget)
|
||||
}
|
||||
})">
|
||||
|
||||
{{ $slot }}
|
||||
</div>
|
||||
14
resources/views/vendor/wirechat/components/actions/show-chat-info.blade.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
@props([
|
||||
'conversation' => null, //Should be conversation ID (Int)
|
||||
'widget' => false
|
||||
])
|
||||
|
||||
|
||||
<x-wirechat::actions.open-chat-drawer
|
||||
component="wirechat.chat.info"
|
||||
dusk="show_chat_info"
|
||||
conversation="{{$conversation}}"
|
||||
:widget="$widget"
|
||||
>
|
||||
{{$slot}}
|
||||
</x-wirechat::actions.open-chat-drawer>
|
||||
14
resources/views/vendor/wirechat/components/actions/show-group-info.blade.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
@props([
|
||||
'conversation' => null, //Should be conversation ID (Int)
|
||||
'widget' => false
|
||||
])
|
||||
|
||||
|
||||
<x-wirechat::actions.open-chat-drawer
|
||||
component="wirechat.chat.group.info"
|
||||
dusk="show_group_info"
|
||||
conversation="{{$conversation}}"
|
||||
:widget="$widget"
|
||||
>
|
||||
{{$slot}}
|
||||
</x-wirechat::actions.open-chat-drawer>
|
||||
67
resources/views/vendor/wirechat/components/avatar.blade.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
@props(['src' => null, 'story' => null, 'group' => false, 'disappearing' => false])
|
||||
<div
|
||||
{{ $attributes->merge([
|
||||
'class' => "shrink-0 inline-flex items-center justify-center relative transition outline outline-1 outline-offset-1 outline-gray-500
|
||||
overflow-visible rounded-full border border-[var(--wc-light-secondary)] text-gray-500 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)]
|
||||
dark:border-[var(--wc-dark-secondary)] text-base ",
|
||||
])->class(
|
||||
$story ? 'bg-linear-to-r p-[2px] ring-2 ring-white from-purple-400 via-pink-500 to-red-500 rounded-full' : ' ',
|
||||
) }}>
|
||||
|
||||
|
||||
@if ($src)
|
||||
<img loading="lazy" @class([
|
||||
'shrink-0 w-full h-full object-cover object-center rounded-full',
|
||||
]) src="{{ $src }}" alt="" />
|
||||
@endif
|
||||
|
||||
@if (!$src && $group==true)
|
||||
{{-- <svg class="shrink-0 scale-90 w-full h-full rounded-full text-gray-300 bg-gray-100 dark:bg-gray-600" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5" />
|
||||
</svg> --}}
|
||||
|
||||
{{-- <svg class="shrink-0 scale-95 w-full h-full rounded-full text-gray-300 bg-gray-100 dark:bg-gray-600" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path d="M8.5 4.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0ZM10.9 12.006c.11.542-.348.994-.9.994H2c-.553 0-1.01-.452-.902-.994a5.002 5.002 0 0 1 9.803 0ZM14.002 12h-1.59a2.556 2.556 0 0 0-.04-.29 6.476 6.476 0 0 0-1.167-2.603 3.002 3.002 0 0 1 3.633 1.911c.18.522-.283.982-.836.982ZM12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"></path>
|
||||
</svg> --}}
|
||||
|
||||
<svg class="shrink-0 p-px w-full h-full rounded-full text-gray-400 dark:text-gray-300 bg-gray-100 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)]"
|
||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path
|
||||
d="M7 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM14.5 9a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM1.615 16.428a1.224 1.224 0 0 1-.569-1.175 6.002 6.002 0 0 1 11.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 0 1 7 18a9.953 9.953 0 0 1-5.385-1.572ZM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 0 0-1.588-3.755 4.502 4.502 0 0 1 5.874 2.636.818.818 0 0 1-.36.98A7.465 7.465 0 0 1 14.5 16Z">
|
||||
</path>
|
||||
</svg>
|
||||
@elseif(!$src)
|
||||
<svg class="shrink-0 w-full h-full rounded-full" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
|
||||
{{-- <svg class="shrink-0 w-full h-full mt-auto rounded-full" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd" />
|
||||
</svg> --}}
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
@if ($disappearing)
|
||||
<span dusk="disappearing_messages_icon"
|
||||
class="absolute z-50 -bottom-1 bg-white -right-2 dark:bg-gray-800 rounded-full p-px">
|
||||
<svg class="w-5 h-5" viewBox="0 0 36 36" height="36" width="36" preserveAspectRatio="xMidYMid meet"
|
||||
fill="none">
|
||||
<title>disappearing</title>
|
||||
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M18 31.5C18.0909 31.5 18.1817 31.4991 18.2722 31.4973C19.1005 31.4809 19.7586 30.7961 19.7422 29.9679C19.7258 29.1396 19.041 28.4815 18.2128 28.4979C18.142 28.4993 18.0711 28.5 18 28.5V31.5ZM18 7.5C18.0711 7.5 18.142 7.5007 18.2128 7.50211C19.041 7.51853 19.7258 6.86039 19.7422 6.03213C19.7586 5.20387 19.1005 4.51912 18.2722 4.5027C18.1817 4.5009 18.0909 4.5 18 4.5V7.5ZM24.5153 6.17374C23.7901 5.77341 22.8776 6.03683 22.4772 6.76211C22.0769 7.48739 22.3403 8.39988 23.0656 8.8002C23.1891 8.86838 23.3111 8.93898 23.4316 9.01195C24.1401 9.44118 25.0625 9.21475 25.4917 8.5062C25.921 7.79765 25.6945 6.87529 24.986 6.44605C24.8311 6.35223 24.6742 6.26144 24.5153 6.17374ZM29.554 11.014C29.1247 10.3055 28.2024 10.079 27.4938 10.5083C26.7852 10.9375 26.5588 11.8599 26.9881 12.5684C27.061 12.6889 27.1316 12.8109 27.1998 12.9344C27.6001 13.6597 28.5126 13.9231 29.2379 13.5228C29.9632 13.1224 30.2266 12.2099 29.8263 11.4847C29.7386 11.3258 29.6478 11.1689 29.554 11.014ZM31.4973 17.7278C31.4809 16.8995 30.7961 16.2414 29.9679 16.2578C29.1396 16.2742 28.4815 16.959 28.4979 17.7872C28.4993 17.858 28.5 17.9289 28.5 18C28.5 18.0711 28.4993 18.142 28.4979 18.2128C28.4815 19.041 29.1396 19.7258 29.9679 19.7422C30.7961 19.7586 31.4809 19.1005 31.4973 18.2722C31.4991 18.1817 31.5 18.0909 31.5 18C31.5 17.9091 31.4991 17.8183 31.4973 17.7278ZM29.8263 24.5153C30.2266 23.7901 29.9632 22.8776 29.2379 22.4772C28.5126 22.0769 27.6001 22.3403 27.1998 23.0656C27.1316 23.1891 27.061 23.3111 26.9881 23.4316C26.5588 24.1401 26.7852 25.0625 27.4938 25.4917C28.2024 25.921 29.1247 25.6945 29.554 24.986C29.6478 24.8311 29.7386 24.6742 29.8263 24.5153ZM24.986 29.554C25.6945 29.1247 25.921 28.2024 25.4917 27.4938C25.0625 26.7852 24.1401 26.5588 23.4316 26.9881C23.3111 27.061 23.1891 27.1316 23.0656 27.1998C22.3403 27.6001 22.0769 28.5126 22.4772 29.2379C22.8776 29.9632 23.7901 30.2266 24.5153 29.8263C24.6742 29.7386 24.8311 29.6478 24.986 29.554Z">
|
||||
</path>
|
||||
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M18.0001 4.5C18 4.5 17.9999 4.5 17.9998 4.5C10.5439 4.5 4.49976 10.5442 4.49976 18C4.49976 25.4558 10.5439 31.5 17.9998 31.5C17.9999 31.5 18 31.5 18.0001 31.5V28.5C18 28.5 17.9999 28.5 17.9998 28.5C12.2008 28.5 7.49976 23.799 7.49976 18C7.49976 12.201 12.2008 7.5 17.9998 7.5C17.9999 7.5 18 7.5 18.0001 7.5V4.5Z">
|
||||
</path>
|
||||
<path fill="currentColor"
|
||||
d="M23.3247 12.0107C23.669 11.7525 24.1507 11.7867 24.455 12.091V12.091C24.7593 12.3953 24.7935 12.877 24.5353 13.2213L19.9714 19.3066C19.2589 20.2566 17.8701 20.3553 17.0304 19.5156V19.5156C16.1907 18.6759 16.2894 17.2871 17.2394 16.5746L23.3247 12.0107Z">
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
4
resources/views/vendor/wirechat/components/divider.blade.php
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<div
|
||||
{{$attributes->merge([ 'class'=>"w-full h-2 shadow-xs bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] "])}} >
|
||||
|
||||
</div>
|
||||
1
resources/views/vendor/wirechat/components/dropdown-button.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-[var(--wc-light-primary)] dark:hover:bg-[var(--wc-dark-primary)] focus:outline-hidden focus:bg-[var(--wc-light-primary)] dark:focus:bg-[var(--wc-dark-primary)] transition duration-150 ease-in-out']) }}>{{ $slot }}</div>
|
||||
1
resources/views/vendor/wirechat/components/dropdown-link.blade.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<a {{ $attributes->merge(['class' => 'block w-full cursor-pointer px-4 py-3 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] hover:bg-[var(--wc-light-primary)] dark:hover:bg-[var(--wc-dark-primary)] focus:outline-hidden focus:bg-[var(--wc-light-primary)] dark:focus:bg-[var(--wc-dark-primary)] transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
|
||||
46
resources/views/vendor/wirechat/components/dropdown.blade.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
@props(['align' => 'right', 'width' => '48', 'contentClasses' => ''])
|
||||
|
||||
@php
|
||||
switch ($align) {
|
||||
case 'left':
|
||||
$alignmentClasses = 'ltr:origin-top-left rtl:origin-top-right start-0';
|
||||
break;
|
||||
case 'top':
|
||||
$alignmentClasses = 'origin-top';
|
||||
break;
|
||||
case 'right':
|
||||
default:
|
||||
$alignmentClasses = 'ltr:origin-top-right rtl:origin-top-left end-0';
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($width) {
|
||||
case '48':
|
||||
$width = 'w-48';
|
||||
break;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div x-ref="button" class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
|
||||
<div @click="open = ! open">
|
||||
{{ $trigger }}
|
||||
</div>
|
||||
|
||||
<div x-show="open"
|
||||
x-anchor="$refs.button"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
|
||||
{{-- class="absolute z-50 mt-2 shadow-lg {{ $alignmentClasses }}" --}}
|
||||
{{$attributes->merge(['class'=>"rounded-lg absolute z-50 mt-2 shadow-lg w-48 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] rounded-md border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] shadow-sm overflow-hidden"])}}
|
||||
style="display: none;"
|
||||
@click="open = false">
|
||||
<div>
|
||||
{{ $content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
16
resources/views/vendor/wirechat/components/loading-spin.blade.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
{{-- Define previous message outside the loop --}}
|
||||
<div class="w-full flex items-center ">
|
||||
<span {{$attributes->merge([ 'class'=>"mx-auto w-5 h-5 "])}} >
|
||||
<svg aria-hidden="true" class="w-full h-full text-gray-200 animate-spin dark:text-gray-600 "
|
||||
viewBox="0 0 100 101" fill="var(--wc-brand-primary)" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor" />
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
133
resources/views/vendor/wirechat/components/notification.blade.php
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
|
||||
@php
|
||||
$activeGuard = session('active_guard', 'web');
|
||||
$authUser = Auth::guard($activeGuard)->user();
|
||||
@endphp
|
||||
|
||||
@if($authUser && WireChat::notificationsEnabled())
|
||||
|
||||
<div dusk="notification_manager"
|
||||
x-data="{
|
||||
showNotification(e) {
|
||||
const message = e.message;
|
||||
const redirect_url = e.redirect_url;
|
||||
|
||||
if (Notification.permission !== 'granted') {
|
||||
return;
|
||||
}
|
||||
|
||||
let title = message.sendable?.display_name || 'User';
|
||||
let body = message.body;
|
||||
let icon = message.sendable?.cover_url;
|
||||
|
||||
if (message.conversation.type == 'group') {
|
||||
title = message.conversation?.group?.name;
|
||||
body = message.sendable?.display_name + ': ' + message.body;
|
||||
icon = message.conversation?.group?.cover_url;
|
||||
}
|
||||
|
||||
const options = {
|
||||
body: body,
|
||||
icon: icon,
|
||||
vibrate: [200, 100, 200],
|
||||
tag: 'wirechat-notification-' + message.conversation_id,
|
||||
renotify: true,
|
||||
data: {
|
||||
url: redirect_url,
|
||||
type: 'SHOW_NOTIFICATION',
|
||||
tag: 'wirechat-notification-' + message.conversation_id
|
||||
}
|
||||
};
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then((registration )=> {
|
||||
// Service worker is fully ready
|
||||
registration.active.postMessage({
|
||||
type: 'SHOW_NOTIFICATION',
|
||||
title: title,
|
||||
options: options
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
// Fallback to regular notifications
|
||||
this.newNotification(title, options);
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
this.newNotification(title, options);
|
||||
}
|
||||
},
|
||||
|
||||
newNotification(title,options){
|
||||
|
||||
|
||||
const notification= new Notification(title, options);
|
||||
|
||||
notification.onclick = (event) => {
|
||||
event.preventDefault();
|
||||
const convId = message.conversation_id || 'default';
|
||||
const windowName = 'wirechat-conversation';
|
||||
const url = event.currentTarget.data.url;
|
||||
const openedWindow = window.open(url, windowName);
|
||||
if (openedWindow) {
|
||||
openedWindow.focus();
|
||||
}
|
||||
//Close current notification
|
||||
event.currentTarget.close();
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
registerServiceWorker() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register(`{{asset(config('wirechat.notifcations.main_sw_script','sw.js'))}}`)
|
||||
.then(reg => {})
|
||||
.catch(err => {});
|
||||
}
|
||||
}
|
||||
}"
|
||||
x-init="
|
||||
registerServiceWorker();
|
||||
|
||||
userId = @js($authUser->id);
|
||||
encodedType = @js(\Namu\WireChat\Helpers\MorphClassResolver::encode($authUser->getMorphClass()));
|
||||
|
||||
{{-- We listen to notify participant event --}}
|
||||
Echo.private(`participant.${encodedType}.${userId}`)
|
||||
.listen('.Namu\\WireChat\\Events\\NotifyParticipant', (e) => {
|
||||
|
||||
{{--Ignore if user is currently open in the chat --}}
|
||||
if (e.redirect_url !== window.location.href) {
|
||||
if (!('Notification' in window)) {
|
||||
} else if (Notification.permission === 'granted') {
|
||||
showNotification(e);
|
||||
} else if (Notification.permission !== 'denied') {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
showNotification(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('chat-opened', (event) => {
|
||||
const conversation = event.detail.conversation;
|
||||
const tag = 'wirechat-notification-' + conversation;
|
||||
|
||||
if (navigator.serviceWorker.controller) {
|
||||
navigator.serviceWorker.controller.postMessage({
|
||||
type: 'CLOSE_NOTIFICATION',
|
||||
tag: tag
|
||||
});
|
||||
}
|
||||
});
|
||||
">
|
||||
</div>
|
||||
|
||||
|
||||
@endif
|
||||
67
resources/views/vendor/wirechat/components/placeholders/chat.blade.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
|
||||
<div class="h-[calc(100vh)] flex flex-col bg-white dark:bg-gray-900 h-full">
|
||||
<header class="w-full bg-gray-50 dark:bg-gray-900 animate-pulse h-16 sticky gap-5 inset-x-0 items-center flex p-5 top-0 z-10 border-gray-100 dark:border-gray-700 border-b">
|
||||
|
||||
<div class="rounded-full bg-gray-100 dark:bg-slate-800/40 h-9 w-9 animate-pulse ">
|
||||
|
||||
</div>
|
||||
<div class="bg-gray-100 dark:bg-slate-800/40 h-4 w-72 animate-pulse rounded-xl">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="rounded-full bg-gray-100 dark:bg-slate-800/40 h-9 w-2 ml-auto animate-pulse ">
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class=" bg-white dark:bg-gray-900 flex flex-col animate-pulse grow h-10/12 h-full">
|
||||
|
||||
<div class="bg-gray-100 dark:bg-slate-800/40 h-9 w-48 mt-12 mx-auto animate-pulse rounded-xl">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="m-auto">
|
||||
|
||||
{{-- Snipper --}}
|
||||
<span
|
||||
style="
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border: 10px solid;
|
||||
border-color: rgba(230, 228, 228, 0.063) rgba(237, 235, 235, 0.094) rgba(255, 255, 255, 0.104) rgba(255, 255, 255, 0.23);
|
||||
box-sizing: border-box;
|
||||
"
|
||||
class=" animate-spin ">
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Loading spinner... -->
|
||||
<foooter class=" sticky bottom-0 w-full h-16 flex items-center gap-3 p-4 dark:bg-gray-900 bg-gray-50 h-10 animate-pulse border-t dark:border-gray-700">
|
||||
<span class="rounded-full bg-gray-100 dark:bg-slate-800/40 h-9 w-9 animate-pulse ">
|
||||
|
||||
</span>
|
||||
|
||||
<span class="rounded-full bg-gray-100 dark:bg-slate-800/40 h-9 w-7 animate-pulse ">
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
<div class="bg-gray-100 dark:bg-slate-800/40 h-8 w-11/12 animate-pulse rounded-xl">
|
||||
|
||||
</div>
|
||||
|
||||
<span class="rounded-full bg-gray-100 dark:bg-slate-800/40 h-9 w-9 animate-pulse ">
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</foooter>
|
||||
</div>
|
||||
76
resources/views/vendor/wirechat/components/popover.blade.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
@props([
|
||||
'position'=>'bottom',
|
||||
'popoverOffset'=>'20'
|
||||
|
||||
]
|
||||
)
|
||||
<div x-data="{
|
||||
popoverOpen: false,
|
||||
popoverArrow: false,
|
||||
popoverPosition: 'top',
|
||||
popoverHeight: 0,
|
||||
popoverOffset: 40,
|
||||
popoverHeightCalculate() {
|
||||
this.$refs.popover.classList.add('invisible');
|
||||
this.popoverOpen=true;
|
||||
let that=this;
|
||||
$nextTick(function(){
|
||||
that.popoverHeight = that.$refs.popover.offsetHeight;
|
||||
that.popoverOpen=false;
|
||||
that.$refs.popover.classList.remove('invisible');
|
||||
that.$refs.popoverInner.setAttribute('x-transition', '');
|
||||
that.popoverPositionCalculate();
|
||||
});
|
||||
},
|
||||
popoverPositionCalculate(){
|
||||
if(window.innerHeight < (this.$refs.popoverButton.getBoundingClientRect().top + this.$refs.popoverButton.offsetHeight + this.popoverOffset + this.popoverHeight)){
|
||||
this.popoverPosition = 'top';
|
||||
} else {
|
||||
this.popoverPosition = 'bottom';
|
||||
}
|
||||
}
|
||||
}"
|
||||
x-init="
|
||||
that = this;
|
||||
window.addEventListener('resize', function(){
|
||||
popoverPositionCalculate();
|
||||
});
|
||||
$watch('popoverOpen', function(value){
|
||||
if(value){
|
||||
popoverPositionCalculate();
|
||||
let el = document.getElementById('width');
|
||||
if(el){
|
||||
el.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
"
|
||||
class="relative overflow-visible">
|
||||
|
||||
<button {{$trigger->attributes->class(["flex items-center cursor-pointer hover:scale-105 transition-transform justify-center disabled:cursor-progress"] )}} type="button" x-ref="popoverButton" @click="popoverOpen=!popoverOpen">
|
||||
{{$trigger}}
|
||||
</button>
|
||||
|
||||
<div x-ref="popover"
|
||||
x-anchor.offset.17="$refs.popoverButton"
|
||||
x-show="popoverOpen"
|
||||
|
||||
x-init="setTimeout(function(){ popoverHeightCalculate(); }, 100);"
|
||||
@click.away="popoverOpen=false;"
|
||||
@keydown.escape.window="popoverOpen=false"
|
||||
class=" min-w-[13rem] max-w-fit "
|
||||
x-cloak
|
||||
@click="popoverOpen=false" >
|
||||
<div
|
||||
|
||||
|
||||
x-ref="popoverInner" x-show="popoverOpen" class="w-full p-2 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-secondary)] border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-primary)] rounded-lg shadow-sm ">
|
||||
<div x-show="popoverArrow && popoverPosition == 'bottom'" class="absolute top-0 inline-block w-5 mt-px overflow-hidden -translate-x-2 -translate-y-2.5 left-1/2"><div class="w-2.5 h-2.5 origin-bottom-left transform rotate-45 bg-white border-t border-l rounded-xs"></div></div>
|
||||
<div x-show="popoverArrow && popoverPosition == 'top'" class="absolute bottom-0 inline-block w-5 mb-px overflow-hidden -translate-x-2 translate-y-2.5 left-1/2"><div class="w-2.5 h-2.5 origin-top-left transform -rotate-45 bg-white border-b border-l rounded-xs"></div></div>
|
||||
<div class="grid gap-4">
|
||||
{{$slot}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
83
resources/views/vendor/wirechat/components/toast.blade.php
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
|
||||
<div x-data="{
|
||||
bannerVisible: false,
|
||||
type:'default',
|
||||
message:' ',
|
||||
bannerVisibleAfter: 300,
|
||||
counter: 3000,
|
||||
timer: null,
|
||||
closeToast:function(){
|
||||
|
||||
this.bannerVisible = false;
|
||||
clearInterval(this.timer);
|
||||
|
||||
}
|
||||
}"
|
||||
|
||||
@wirechat-toast.window="
|
||||
message = $event.detail.message;
|
||||
type = $event.detail.type;
|
||||
bannerVisible = true;
|
||||
counter = 3000;
|
||||
if (timer) clearInterval(timer);
|
||||
timer = setInterval(() => {
|
||||
if (counter <= 0) {
|
||||
bannerVisible = false;
|
||||
clearInterval(timer);
|
||||
} else {
|
||||
counter -= 100;
|
||||
}
|
||||
}, 100);
|
||||
"
|
||||
@mouseenter="clearInterval(timer)"
|
||||
@mouseleave="
|
||||
timer = setInterval(() => {
|
||||
if (counter <= 0) {
|
||||
bannerVisible = false;
|
||||
clearInterval(timer);
|
||||
} else {
|
||||
counter -= 100;
|
||||
}
|
||||
}, 100);
|
||||
"
|
||||
x-show="bannerVisible"
|
||||
x-transition:enter="transition ease-out duration-500"
|
||||
x-transition:enter-start="-translate-y-10"
|
||||
x-transition:enter-end="translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="translate-y-0"
|
||||
x-transition:leave-end="-translate-y-10"
|
||||
|
||||
|
||||
class="fixed sm:top-2 top-0 z-50 inset-x-0 sm:max-w-md mx-auto sm:ml-auto sm:mx-0 w-full h-auto py-2.5 duration-300 ease-out bg-white shadow-md sm:border rounded-md " x-cloak>
|
||||
<div class="flex items-center justify-between w-full h-full px-3 mx-auto max-w-7xl ">
|
||||
<div class="flex items-center gap-3 w-full h-full ">
|
||||
|
||||
<span x-show="type=='warning'" x-cloak>
|
||||
<svg class="w-4 h-4 sm:w-5 sm:h-5 text-yellow-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" >
|
||||
<path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<span x-show="type=='danger'" x-cloak>
|
||||
<svg class="w-4 h-4 sm:w-5 sm:h-5 text-rose-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 0 1 .67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 1 1-.671-1.34l.041-.022ZM12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
|
||||
<span x-show="type=='success'" x-cloak>
|
||||
<svg class="w-4 h-4 sm:w-5 sm:h-5 text-green-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" >
|
||||
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<p class="text-xs text-black " x-text="message"></p>
|
||||
</div>
|
||||
|
||||
<button @click="closeToast()" class="flex items-center shrink-0 translate-x-1 ease-out duration-150 justify-center w-6 h-6 p-1.5 text-black rounded-full hover:bg-neutral-100">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-full h-full"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
73
resources/views/vendor/wirechat/components/video.blade.php
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
@props([
|
||||
'source'=>null,
|
||||
'controls'=>true,
|
||||
'cover'=>true,
|
||||
'height'=>"auto",
|
||||
'showToggleSound'=>true,
|
||||
|
||||
|
||||
|
||||
])
|
||||
|
||||
|
||||
<div x-data="{playing:false,muted:false}"
|
||||
class="relative "
|
||||
@click.outside="$refs.player.pause()"
|
||||
x-intersect:leave="$refs.player.pause()">
|
||||
|
||||
|
||||
<video x-ref="player" src="{{$source}}" @play="playing=true" @pause="playing=false"
|
||||
class=" w-auto dark:bg-gray-600 border rounded-xl border-gray-50 dark:border-gray-700 rounded-xl {{$cover==true?'object-cover':''}} {{$height}}">
|
||||
your browser does not support html5 video
|
||||
</video>
|
||||
|
||||
@if ($controls==true)
|
||||
|
||||
|
||||
{{-- play --}}
|
||||
|
||||
<div x-cloak x-show="!playing" @click="$refs.player.play()" class="absolute z-10 inset-0 flex items-center justify-center w-full h-full cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-play-fill w-16 h-16 text-white" viewBox="0 0 16 16">
|
||||
<path d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{{-- pause button --}}
|
||||
<div x-show="playing" @click="$refs.player.pause()" class="absolute z-10 inset-0 flex items-center justify-center w-full h-full cursor-pointer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pause-fill w-16 h-16 text-white invisible" viewBox="0 0 16 16">
|
||||
<path d="M5.5 3.5A1.5 1.5 0 0 1 7 5v6a1.5 1.5 0 0 1-3 0V5a1.5 1.5 0 0 1 1.5-1.5zm5 0A1.5 1.5 0 0 1 12 5v6a1.5 1.5 0 0 1-3 0V5a1.5 1.5 0 0 1 1.5-1.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{{-- mute --}}
|
||||
|
||||
@if ($showToggleSound==true)
|
||||
<div class="absolute z-100 bottom-2 right-2 m-4 bg-gray-900 text-white rounded-lg p-1 cursor-pointer">
|
||||
|
||||
{{-- mute --}}
|
||||
|
||||
<svg x-cloak x-show="!muted" @click="$refs.player.muted=true;muted=true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-volume-mute-fill w-h w-4" viewBox="0 0 16 16">
|
||||
<path d="M6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06zm7.137 2.096a.5.5 0 0 1 0 .708L12.207 8l1.647 1.646a.5.5 0 0 1-.708.708L11.5 8.707l-1.646 1.647a.5.5 0 0 1-.708-.708L10.793 8 9.146 6.354a.5.5 0 1 1 .708-.708L11.5 7.293l1.646-1.647a.5.5 0 0 1 .708 0z"/>
|
||||
</svg>
|
||||
|
||||
|
||||
<svg x-show="muted" @click="$refs.player.muted=false;muted=false" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-volume-off-fill w-4 h-4" viewBox="0 0 16 16">
|
||||
<path d="M10.717 3.55A.5.5 0 0 1 11 4v8a.5.5 0 0 1-.812.39L7.825 10.5H5.5A.5.5 0 0 1 5 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06z"/>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
{{-- unmute --}}
|
||||
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
||||
108
resources/views/vendor/wirechat/layouts/app.blade.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="@themeId">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ $title ?? config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<!--THEME:--ADD TO TOP OT PREVENT FLICKERING -->
|
||||
<script>
|
||||
|
||||
/* Function to apply or remove the dark theme */
|
||||
function updateTheme(isDark) {
|
||||
if (isDark) {
|
||||
// document.documentElement.classList.add('dark'); // Add dark class to the root element
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the initial theme preference */
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
updateTheme(darkModeMediaQuery.matches);
|
||||
|
||||
/* listen to changed in (prefers-color-scheme: dark) */
|
||||
darkModeMediaQuery.addEventListener('change', (event) => {
|
||||
updateTheme(event.matches);
|
||||
});
|
||||
|
||||
/* Add This to update theme when page is wire navigated */
|
||||
document.addEventListener('livewire:navigated', () => {
|
||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
updateTheme(darkModeMediaQuery.matches); // Re-apply the theme based on system preference
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Fonts -->
|
||||
{{-- <link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" /> --}}
|
||||
|
||||
<!-- Dynamic Theme CSS Custom Properties -->
|
||||
<style>
|
||||
:root {
|
||||
{!! theme_css_vars() !!}
|
||||
}
|
||||
</style>
|
||||
|
||||
@vite(['resources/css/app.css', 'resources/css/fonts.css', 'resources/js/app.js'])
|
||||
@livewireStyles
|
||||
@wirechatStyles
|
||||
@wireUiScripts
|
||||
|
||||
<style>
|
||||
/* Theme-aware typography */
|
||||
body {
|
||||
font-family: var(--font-family-body, 'Poppins', sans-serif) !important;
|
||||
}
|
||||
|
||||
/* Theme-aware heading styles */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-family-heading, 'Oswald', sans-serif) !important;
|
||||
text-transform: var(--heading-transform, uppercase) !important;
|
||||
}
|
||||
|
||||
/* Apply theme-specific CSS custom properties */
|
||||
:root {
|
||||
@themeCssVars
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="font-sans antialiased flex flex-col min-h-screen">
|
||||
|
||||
<x-jetstream.banner />
|
||||
<x-jetstream.toaster />
|
||||
|
||||
<div class="flex-grow bg-gray-100">
|
||||
@livewire('navigation-menu')
|
||||
<x-notifications position="bottom-end" />
|
||||
|
||||
|
||||
<header class="bg-theme-brand shadow lg:mt-16">
|
||||
<!-- System Anounnucement -->
|
||||
@livewire('system-announcement', ['type' => 'SiteContents\SystemAnnouncement' ?? null, 'limit' => 1])
|
||||
<!-- Header --->
|
||||
<div class="max-w-7xl mx-auto pt-1 pb-2 px-4 sm:px-6 lg:px-8">
|
||||
<div class="mt-2 text-xl font-semibold leading-tight text-gray-100">
|
||||
{{ __('Chat messenger') }}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)]">
|
||||
<!-- Page Content -->
|
||||
<main class="h-[calc(100vh_-_3rem)] lg:h-[calc(100vh_-_7rem)] max-w-7xl mx-auto overflow-hidden bg-white shadow-xl">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@auth
|
||||
@livewire('account-info-modal')
|
||||
@endauth
|
||||
@livewireScripts
|
||||
@wirechatAssets
|
||||
</body>
|
||||
|
||||
</html>
|
||||
325
resources/views/vendor/wirechat/livewire/chat/chat.blade.php
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
{{-- Import helper function to use in chatbox --}}
|
||||
@use('Namu\WireChat\Helpers\Helper')
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
|
||||
@php
|
||||
$primaryColor = WireChat::getColor();
|
||||
@endphp
|
||||
|
||||
|
||||
|
||||
@assets
|
||||
<style>
|
||||
|
||||
emoji-picker {
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Emoji picker configuration */
|
||||
emoji-picker {
|
||||
--background: none !important;
|
||||
--border-radius: 12px;
|
||||
--input-border-color: rgb(229 229 229);
|
||||
--input-padding: 0.45rem;
|
||||
--outline-color: none;
|
||||
--outline-size: 1px;
|
||||
--num-columns: 8;
|
||||
/* Mobile-first default */
|
||||
--emoji-padding: 0.7rem;
|
||||
--emoji-size: 1.5rem;
|
||||
/* Smaller size for mobile */
|
||||
--border-color: none;
|
||||
--indicator-color: #9ca3af;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
emoji-picker {
|
||||
--num-columns: 10;
|
||||
/* Increase columns for larger screens */
|
||||
--emoji-size: 1.8rem;
|
||||
/* Larger size for desktop */
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 900px) {
|
||||
emoji-picker {
|
||||
--num-columns: 16;
|
||||
/* Increase columns for larger screens */
|
||||
--emoji-size: 1.9rem;
|
||||
/* Larger size for desktop */
|
||||
}
|
||||
}
|
||||
/* Dark mode using prefers-color-scheme */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
emoji-picker {
|
||||
--background: none !important;
|
||||
--input-border-color: var(--wc-dark-border);
|
||||
--outline-color: none;
|
||||
--outline-size: 1px;
|
||||
--border-color: none;
|
||||
--input-font-color: white;
|
||||
--indicator-color: var(--wc-dark-accent);
|
||||
--button-hover-background: var(--wc-dark-accent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Ensure dark mode takes precedence */
|
||||
.dark emoji-picker {
|
||||
--background: none !important;
|
||||
--input-border-color: var(--wc-dark-border);
|
||||
--outline-color: none;
|
||||
--outline-size: 1px;
|
||||
--border-color: none;
|
||||
--input-font-color: white;
|
||||
--indicator-color: var(--wc-dark-accent);
|
||||
--button-hover-background: var(--wc-dark-accent);
|
||||
}
|
||||
</style>
|
||||
|
||||
@endassets
|
||||
|
||||
<div x-data="{
|
||||
initializing: true,
|
||||
conversationId:@js($conversation->id),
|
||||
conversationElement: document.getElementById('conversation'),
|
||||
loadEmojiPicker() {
|
||||
if (!document.head.querySelector('script[src*=\'/js/vendor/emoji-picker-element/index.js\']')) {
|
||||
// Clear old IndexedDB database that may have cached CDN data
|
||||
if (window.indexedDB) {
|
||||
indexedDB.deleteDatabase('emoji-picker-element');
|
||||
}
|
||||
|
||||
let script = document.createElement('script');
|
||||
script.type = 'module';
|
||||
script.async = true; // Load asynchronously
|
||||
// Add cache-busting parameter to force reload of updated files
|
||||
script.src = '/js/vendor/emoji-picker-element/index.js?v=' + Date.now();
|
||||
|
||||
// After the script loads, configure all emoji pickers
|
||||
script.onload = () => {
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('emoji-picker').forEach(picker => {
|
||||
picker.dataSource = '/js/vendor/emoji-picker-element-data/en/emojibase/data.json';
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
},
|
||||
get isWidget() {
|
||||
|
||||
return $wire.widget == true;
|
||||
}
|
||||
}"
|
||||
|
||||
x-init="setTimeout(() => {
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
initializing = false;
|
||||
$wire.dispatch('focus-input-field');
|
||||
loadEmojiPicker();
|
||||
{{-- if (isWidget) { --}}
|
||||
//NotifyListeners about chat opened
|
||||
$wire.dispatch('chat-opened',{conversation:conversationId});
|
||||
{{-- } --}}
|
||||
});
|
||||
}, 120);"
|
||||
class="w-full transition bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] overflow-hidden h-full relative" style="contain:content">
|
||||
|
||||
<div class=" flex flex-col grow h-full min-h-0 relative ">
|
||||
{{-- ---------- --}}
|
||||
{{-- --Header-- --}}
|
||||
{{-- ---------- --}}
|
||||
@include('wirechat::livewire.chat.partials.header', [ 'conversation' => $conversation, 'receiver' => $receiver])
|
||||
{{-- ---------- --}}
|
||||
{{-- -Body----- --}}
|
||||
{{-- ---------- --}}
|
||||
<div class="flex-1 min-h-0">
|
||||
@include('wirechat::livewire.chat.partials.body', [ 'conversation' => $conversation, 'authParticipant' => $authParticipant, 'loadedMessages' => $loadedMessages, 'isPrivate' => $conversation->isPrivate(), 'isGroup' => $conversation->isGroup(), 'receiver' => $receiver])
|
||||
</div>
|
||||
{{-- ---------- --}}
|
||||
{{-- -Footer--- --}}
|
||||
{{-- ---------- --}}
|
||||
<livewire:wire-chat.typing-indicator :conversation-id="$conversation->id" />
|
||||
|
||||
@include('wirechat::livewire.chat.partials.footer', [ 'conversation' => $conversation, 'authParticipant' => $authParticipant, 'media' => $media, 'files' => $files, 'replyMessage' => $replyMessage])
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<livewire:wirechat.chat.drawer />
|
||||
|
||||
|
||||
{{-- Add this script directly to your WireChat chat template --}}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
function setupWireChatTyping() {
|
||||
const typingIndicators = document.querySelectorAll('.wirechat-typing-indicator');
|
||||
|
||||
if (typingIndicators.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
typingIndicators.forEach((indicator, index) => {
|
||||
const wireId = indicator.getAttribute('wire:id');
|
||||
|
||||
if (!wireId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find WireChat specific inputs
|
||||
const messageInputs = document.querySelectorAll(
|
||||
'input[wire\\:model*="message"], ' +
|
||||
'textarea[wire\\:model*="message"], ' +
|
||||
'input[placeholder*="message"], ' +
|
||||
'textarea[placeholder*="message"], ' +
|
||||
'input[placeholder*="Type"], ' +
|
||||
'textarea[placeholder*="Type"]'
|
||||
);
|
||||
|
||||
|
||||
if (messageInputs.length === 0) {
|
||||
messageInputs = document.querySelectorAll('input[type="text"], textarea');
|
||||
}
|
||||
|
||||
let typingTimer;
|
||||
let isTyping = false;
|
||||
|
||||
function startTyping() {
|
||||
|
||||
if (!isTyping) {
|
||||
isTyping = true;
|
||||
|
||||
try {
|
||||
const component = window.Livewire.find(wireId);
|
||||
if (component && typeof component.call === 'function') {
|
||||
|
||||
component.call('startTyping');
|
||||
} else {
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
// Reset timer
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(() => {
|
||||
stopTyping();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function stopTyping() {
|
||||
if (isTyping) {
|
||||
isTyping = false;
|
||||
clearTimeout(typingTimer);
|
||||
|
||||
try {
|
||||
const component = window.Livewire.find(wireId);
|
||||
if (component && typeof component.call === 'function') {
|
||||
component.call('stopTyping');
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners with improved logic
|
||||
messageInputs.forEach((input, inputIndex) => {
|
||||
const inputDesc = input.placeholder || input.getAttribute('wire:model') || `input-${inputIndex}`;
|
||||
|
||||
// Input event - most reliable for typing detection
|
||||
input.addEventListener('input', function(e) {
|
||||
startTyping();
|
||||
});
|
||||
|
||||
// Keydown event - for immediate response
|
||||
input.addEventListener('keydown', function(e) {
|
||||
// Only log actual typing keys to reduce noise
|
||||
if (e.key.length === 1 || e.key === 'Backspace' || e.key === 'Delete') {
|
||||
startTyping();
|
||||
} else if (e.key === 'Enter' && !e.shiftKey) {
|
||||
stopTyping();
|
||||
}
|
||||
});
|
||||
|
||||
// Focus event - just for logging
|
||||
input.addEventListener('focus', function() {
|
||||
});
|
||||
|
||||
// Blur event - only stop if not typing recently
|
||||
input.addEventListener('blur', function() {
|
||||
// Add a small delay before stopping to prevent immediate stop
|
||||
setTimeout(() => {
|
||||
if (isTyping) {
|
||||
stopTyping();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Setup with delay to ensure Livewire is ready
|
||||
setTimeout(setupWireChatTyping, 500);
|
||||
|
||||
// Re-setup on navigation
|
||||
document.addEventListener('livewire:navigated', function() {
|
||||
setTimeout(setupWireChatTyping, 500);
|
||||
});
|
||||
});
|
||||
|
||||
// Global test functions
|
||||
window.testWireChatTyping = {
|
||||
start: function() {
|
||||
const indicators = document.querySelectorAll('.wirechat-typing-indicator');
|
||||
indicators.forEach(indicator => {
|
||||
const wireId = indicator.getAttribute('wire:id');
|
||||
if (wireId) {
|
||||
try {
|
||||
const component = window.Livewire.find(wireId);
|
||||
component.call('startTyping');
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
const indicators = document.querySelectorAll('.wirechat-typing-indicator');
|
||||
indicators.forEach(indicator => {
|
||||
const wireId = indicator.getAttribute('wire:id');
|
||||
if (wireId) {
|
||||
try {
|
||||
const component = window.Livewire.find(wireId);
|
||||
component.call('stopTyping');
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
checkRedis: function() {
|
||||
// This would need to be done server-side, but we can check component state
|
||||
const indicators = document.querySelectorAll('.wirechat-typing-indicator');
|
||||
indicators.forEach(indicator => {
|
||||
const wireId = indicator.getAttribute('wire:id');
|
||||
if (wireId) {
|
||||
try {
|
||||
const component = window.Livewire.find(wireId);
|
||||
component.call('debug');
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
</div>
|
||||
201
resources/views/vendor/wirechat/livewire/chat/drawer.blade.php
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
<div>
|
||||
|
||||
@script
|
||||
<script>
|
||||
window.ChatDrawer = () => {
|
||||
return {
|
||||
show: false,
|
||||
showActiveComponent: true,
|
||||
activeDrawerComponent: false,
|
||||
componentHistory: [],
|
||||
listeners: [],
|
||||
//current component attributes
|
||||
closeOnEscape: false,
|
||||
closeOnEscapeIsForceful: false,
|
||||
dispatchCloseEvent: false,
|
||||
destroyOnClose: false,
|
||||
closeModalOnClickAway:false,
|
||||
|
||||
closeChatDrawerOnEscape(trigger) {
|
||||
|
||||
///Only proceed if the trigger is for ChatDrawer
|
||||
if (trigger.modalType !== 'ChatDrawer') {
|
||||
return;
|
||||
}
|
||||
|
||||
//check if canCloseOnEsp
|
||||
if (this.closeOnEscape === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Fire closingModalOnEscape:event to parent
|
||||
if (!this.closingModal('closingModalOnEscape')) {
|
||||
return;
|
||||
}
|
||||
|
||||
//check if should also close all children modal when this current on is closed
|
||||
const force = this.closeOnEscapeIsForceful === true;
|
||||
this.closeDrawer(force);
|
||||
},
|
||||
closingModal(eventName) {
|
||||
const componentName = this.$wire.get('drawerComponents')[this.activeDrawerComponent].name;
|
||||
|
||||
var params = {
|
||||
id: this.activeDrawerComponent,
|
||||
closing: true,
|
||||
};
|
||||
|
||||
Livewire.dispatchTo(componentName, eventName, params);
|
||||
|
||||
return params.closing;
|
||||
},
|
||||
|
||||
closeDrawer(force = false, skipPreviousModals = 0, destroySkipped = false) {
|
||||
if (this.show === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if should dispatch events
|
||||
if (this.dispatchCloseEvent === true) {
|
||||
const componentName = this.$wire.get('drawerComponents')[this.activeDrawerComponent].name;
|
||||
Livewire.dispatch('chatDrawerClosed', {
|
||||
name: componentName
|
||||
});
|
||||
}
|
||||
|
||||
//Check if should completley destroy component on close
|
||||
//Meaning state won't be retained if component is opened again
|
||||
if (this.destroyOnClose === true) {
|
||||
Livewire.dispatch('destroyChatDrawer', {
|
||||
id: this.activeDrawerComponent
|
||||
});
|
||||
}
|
||||
|
||||
const id = this.componentHistory.pop();
|
||||
if (id && !force) {
|
||||
if (id) {
|
||||
this.setActiveDrawerComponent(id, true);
|
||||
} else {
|
||||
this.setShowPropertyTo(false);
|
||||
}
|
||||
} else {
|
||||
this.setShowPropertyTo(false);
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
setActiveDrawerComponent(id, skip = false) {
|
||||
this.setShowPropertyTo(true);
|
||||
|
||||
if (this.activeDrawerComponent === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.activeDrawerComponent !== false && skip === false) {
|
||||
this.componentHistory.push(this.activeDrawerComponent);
|
||||
}
|
||||
|
||||
let focusableTimeout = 50;
|
||||
|
||||
if (this.activeDrawerComponent === false) {
|
||||
this.activeDrawerComponent = id
|
||||
this.showActiveComponent = true;
|
||||
} else {
|
||||
|
||||
this.showActiveComponent = false;
|
||||
focusableTimeout = 400;
|
||||
|
||||
setTimeout(() => {
|
||||
this.activeDrawerComponent = id;
|
||||
this.showActiveComponent = true;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
||||
// Fetch modal attributes and set Alpine properties
|
||||
const attributes = this.$wire.get('drawerComponents')[id]?.modalAttributes || {};
|
||||
this.closeOnEscape = attributes.closeOnEscape ?? false;
|
||||
this.closeOnEscapeIsForceful = attributes.closeOnEscapeIsForceful ?? false;
|
||||
this.dispatchCloseEvent = attributes.dispatchCloseEvent ?? false;
|
||||
this.destroyOnClose = attributes.destroyOnClose ?? true;
|
||||
this.closeModalOnClickAway = attributes.closeModalOnClickAway ?? false;
|
||||
|
||||
|
||||
this.$nextTick(() => {
|
||||
let focusable = this.$refs[id]?.querySelector('[autofocus]');
|
||||
if (focusable) {
|
||||
setTimeout(() => {
|
||||
focusable.focus();
|
||||
}, focusableTimeout);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
|
||||
setShowPropertyTo(show) {
|
||||
this.show = show;
|
||||
if (show) {
|
||||
document.body.classList.add('overflow-y-hidden');
|
||||
} else {
|
||||
document.body.classList.remove('overflow-y-hidden');
|
||||
|
||||
setTimeout(() => {
|
||||
this.activeDrawerComponent = false;
|
||||
this.$wire.resetState();
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
init() {
|
||||
|
||||
/*! Changed the event to closeChatDrawer in order to not interfere with the main modal */
|
||||
this.listeners.push(Livewire.on('closeChatDrawer', (data) => { this.closeDrawer(data?.force ?? false, data?.skipPreviousModals ?? 0, data ?.destroySkipped ?? false); }));
|
||||
|
||||
/*! Changed listener name to activeChatDrawerComponentChanged to not interfer with main modal*/
|
||||
this.listeners.push(Livewire.on('activeChatDrawerComponentChanged', ({id}) => {
|
||||
this.setActiveDrawerComponent(id);
|
||||
}));
|
||||
},
|
||||
destroy() {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
@endscript
|
||||
<div
|
||||
data-modal-type="ChatDrawer"
|
||||
id="chat-drawer"
|
||||
x-data="ChatDrawer()" x-on:close.stop="setShowPropertyTo(false)"
|
||||
x-on:keydown.escape.stop="closeChatDrawerOnEscape({ modalType: 'ChatDrawer', event: $event }); "
|
||||
x-show="show"
|
||||
class="fixed bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] dark:text-white opacity-100 inset-0 z-50 h-full overflow-y-auto" style="display: none;"
|
||||
aria-modal="true"
|
||||
tabindex="0"
|
||||
|
||||
>
|
||||
<div class="justify-center text-center relative">
|
||||
<div x-show="show && showActiveComponent" x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 -translate-x-full" x-transition:enter-end="opacity-100 translate-x-0"
|
||||
x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100 translate-x-0"
|
||||
x-transition:leave-end="opacity-0 -translate-x-full"
|
||||
class="w-auto transition-all " id="chatmodal-container"
|
||||
x-trap.noscroll="show && showActiveComponent" aria-modal="true">
|
||||
@forelse($drawerComponents as $id => $component)
|
||||
<div x-show.immediate="activeDrawerComponent == '{{ $id }}'" x-ref="{{ $id }}"
|
||||
wire:key="{{ $id }}">
|
||||
@livewire($component['name'], $component['arguments'], key($id))
|
||||
</div>
|
||||
@empty
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
156
resources/views/vendor/wirechat/livewire/chat/group/add-members.blade.php
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
<div class="bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] dark:text-white border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] overflow-visible">
|
||||
|
||||
<header class=" sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] z-10 p-12 pb-2">
|
||||
<div class="flex items-center pb-2">
|
||||
|
||||
<x-wirechat::actions.close-modal>
|
||||
<button
|
||||
class="p-2 ml-0 text-gray-600 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] dark:hover:text-white rounded-full hover:text-gray-800">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class=" w-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
|
||||
</svg>
|
||||
</button>
|
||||
</x-wirechat::actions.close-modal>
|
||||
|
||||
<h3 class="text-sm mx-auto font-semibold " ><span>{{__('wirechat::chat.group.add_members.heading.label')}} </span> {{$newTotalCount}} / {{$maxGroupMembers}}</h3>
|
||||
|
||||
<x-jetstream.button
|
||||
wire:click="save"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target='save'
|
||||
@disabled(count($selectedMembers)==0)
|
||||
type="button"
|
||||
class="text-xs py-1.5 px-3">
|
||||
{{__('wirechat::chat.group.add_members.actions.save.label')}}
|
||||
</x-jetstream.button>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Member limit error --}}
|
||||
<div
|
||||
x-data="{ showError:false }"
|
||||
x-on:show-member-limit-error.window="
|
||||
showError=true;
|
||||
setTimeout(()=>{ showError=false; },1500);
|
||||
"
|
||||
class="text-red-500 text-sm mx-auto ">
|
||||
<span x-transition x-show="showError">
|
||||
{{__('wirechat::chat.group.add_members.messages.members_limit_error',['count'=>$maxGroupMembers])}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<section class="flex flex-wrap items-center px-0 border-b border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)]">
|
||||
<input type="search" id="users-search-field" wire:model.live.debounce='search' autocomplete="off"
|
||||
placeholder="{{ __('wirechat::chat.group.add_members.inputs.search.placeholder') }}"
|
||||
class=" w-full border-0 w-auto dark:bg-none dark:bg-transparent outline-hidden focus:outline-hidden bg-none rounded-lg focus:ring-0 hover:ring-0">
|
||||
</section>
|
||||
|
||||
|
||||
<section class=" overflow-x-hidden my-2 ">
|
||||
<ul style="-ms-overflow-style: none;scrollbar-width: none;
|
||||
"
|
||||
class="flex w-full overflow-x-auto gap-3">
|
||||
|
||||
@if ($selectedMembers)
|
||||
|
||||
@foreach ($selectedMembers as $key => $member)
|
||||
<li class="flex items-center text-nowrap min-w-fit px-2 py-1 text-sm font-medium text-gray-800 bg-[var(--wc-light-secondary)] rounded-sm dark:bg-[var(--wc-dark-secondary)] dark:text-gray-300"
|
||||
wire:key="selected-member-{{ $member->id }}">
|
||||
{{ $member->display_name }}
|
||||
<button type="button"
|
||||
wire:click="toggleMember('{{ $member->id }}',{{ json_encode(get_class($member)) }})"
|
||||
class="flex items-center p-1 ms-2 text-sm text-gray-400 bg-transparent rounded-xs hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-gray-300"
|
||||
aria-label="Remove">
|
||||
<svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
<span class="sr-only">Remove badge</span>
|
||||
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="relative w-full px-12">
|
||||
{{-- <h5 class="text font-semibold text-gray-800 dark:text-gray-100">Recent Chats</h5> --}}
|
||||
<section class="my-4">
|
||||
@if ($users)
|
||||
|
||||
<ul class="overflow-auto flex flex-col">
|
||||
|
||||
@foreach ($users as $key => $user)
|
||||
@php
|
||||
$isAlreadyAParticipant= $user->belongsToConversation($conversation);
|
||||
@endphp
|
||||
<li wire:key="users-{{$key}}" class="flex cursor-pointer group gap-2 items-center p-2">
|
||||
|
||||
<label
|
||||
{{-- The wire:click attribute is only rendered if $isAlreadyAParticipant is false. --}}
|
||||
@if (!$isAlreadyAParticipant)
|
||||
wire:click="toggleMember('{{ $user->id }}', {{ json_encode(get_class($user)) }})"
|
||||
@endif
|
||||
|
||||
class="flex cursor-pointer gap-2 items-center w-full">
|
||||
<x-wirechat::avatar src="{{$user->cover_url}}" class="w-10 h-10" />
|
||||
|
||||
<div @class(['opacity-70' => $isAlreadyAParticipant, 'flex-1 min-w-0']) >
|
||||
<p
|
||||
@class(['transition-all truncate', 'group-hover:underline ' => !$isAlreadyAParticipant])>
|
||||
{{ $user->display_name }}</p>
|
||||
|
||||
<span
|
||||
@class(['text-gray-600 dark:text-gray-400 text-sm'])>
|
||||
@if ($isAlreadyAParticipant)
|
||||
{{__('wirechat::chat.group.add_members.messages.member_already_exists')}}
|
||||
@else
|
||||
@php
|
||||
$location = $user->getLocationFirst();
|
||||
@endphp
|
||||
@if ($location && isset($location['name_short']))
|
||||
{{ $location['name_short'] }}
|
||||
@endif
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto">
|
||||
@if ($selectedMembers->contains(fn($member) => $member->id == $user->id && get_class($member) == get_class($user)) || $isAlreadyAParticipant)
|
||||
<div class="w-6 h-6 bg-theme-brand rounded flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-4 h-4 text-theme-background">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
</div>
|
||||
@else
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-plus-square-dotted w-6 h-6 text-gray-400"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M2.5 0q-.25 0-.487.048l.194.98A1.5 1.5 0 0 1 2.5 1h.458V0zm2.292 0h-.917v1h.917zm1.833 0h-.917v1h.917zm1.833 0h-.916v1h.916zm1.834 0h-.917v1h.917zm1.833 0h-.917v1h.917zM13.5 0h-.458v1h.458q.151 0 .293.029l.194-.981A2.5 2.5 0 0 0 13.5 0m2.079 1.11a2.5 2.5 0 0 0-.69-.689l-.556.831q.248.167.415.415l.83-.556zM1.11.421a2.5 2.5 0 0 0-.689.69l.831.556c.11-.164.251-.305.415-.415zM16 2.5q0-.25-.048-.487l-.98.194q.027.141.028.293v.458h1zM.048 2.013A2.5 2.5 0 0 0 0 2.5v.458h1V2.5q0-.151.029-.293zM0 3.875v.917h1v-.917zm16 .917v-.917h-1v.917zM0 5.708v.917h1v-.917zm16 .917v-.917h-1v.917zM0 7.542v.916h1v-.916zm15 .916h1v-.916h-1zM0 9.375v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .916v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .917v.458q0 .25.048.487l.98-.194A1.5 1.5 0 0 1 1 13.5v-.458zm16 .458v-.458h-1v.458q0 .151-.029.293l.981.194Q16 13.75 16 13.5M.421 14.89c.183.272.417.506.69.689l.556-.831a1.5 1.5 0 0 1-.415-.415zm14.469.689c.272-.183.506-.417.689-.69l-.831-.556c-.11.164-.251.305-.415.415l.556.83zm-12.877.373Q2.25 16 2.5 16h.458v-1H2.5q-.151 0-.293-.029zM13.5 16q.25 0 .487-.048l-.194-.98A1.5 1.5 0 0 1 13.5 15h-.458v1zm-9.625 0h.917v-1h-.917zm1.833 0h.917v-1h-.917zm1.834-1v1h.916v-1zm1.833 1h.917v-1h-.917zm1.833 0h.917v-1h-.917zM8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z" />
|
||||
</svg>
|
||||
@endif
|
||||
</div>
|
||||
</label>
|
||||
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
@endif
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
304
resources/views/vendor/wirechat/livewire/chat/group/info.blade.php
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
<div id="group-info-modal" class="bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] min-h-screen">
|
||||
|
||||
|
||||
@php
|
||||
$authIsAdminInGroup = $participant?->isAdmin();
|
||||
$authIsOwner = $participant?->isOwner();
|
||||
$isGroup = $conversation?->isGroup();
|
||||
$group = $conversation?->group;
|
||||
@endphp
|
||||
|
||||
<section class="cursor-pointer flex gap-4 z-10 items-center p-5 sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] ">
|
||||
<button wire:click="$dispatch('closeChatDrawer')" class="focus:outline-hidden cursor-pointer"> <svg class="w-7 h-7"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg> </button>
|
||||
<h3>{{__('wirechat::chat.group.info.heading.label')}}</h3>
|
||||
</section>
|
||||
{{-- Details --}}
|
||||
<header>
|
||||
|
||||
{{-- Edit group form --}}
|
||||
@if ($authIsAdminInGroup || $group?->allowsMembersToEditGroupInfo())
|
||||
<div @dusk="edit_group_information_section" class="flex flex-col items-center gap-5 py-5 px-4 ">
|
||||
|
||||
{{-- Avatar --}}
|
||||
<section class="mx-auto items-center justify-center grid">
|
||||
<div @dusk="edit_avatar_label" class="relative h-32 w-32 overflow-clip mx-auto rounded-full">
|
||||
|
||||
<label wire:target="photo" wire:loading.class="cursor-not-allowed" for="photo"
|
||||
class=" cursor-pointer w-full h-full">
|
||||
<x-wirechat::avatar wire:loading.class="cursor-not-allowed" group="{{ $isGroup }}"
|
||||
:src="$cover_url" class="w-full h-full absolute inset-0" />
|
||||
</label>
|
||||
<input accept=".jpg,.jpeg,.png,.webp" wire:loading.attr="disabled" id="photo"
|
||||
wire:model="photo" dusk="add_photo_field" type="file" hidden>
|
||||
|
||||
|
||||
@if (empty($cover_url))
|
||||
{{-- penceil --}}
|
||||
<label wire:target="photo" wire:loading.class="cursor-not-allowed"
|
||||
wire:loading.class.remove="cursor-pointer" for="photo"
|
||||
class=" cursor-pointer bottom-0 inset-x-0 bg-gray-500/40 hover:bg-gray-500/80 dark:bg-white/40 dark:hover:bg-gray-700 transition-colors text-gray-600 flex items-center justify-center absolute ">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="size-6 w-5 h-5">
|
||||
<path
|
||||
d="M21.731 2.269a2.625 2.625 0 0 0-3.712 0l-1.157 1.157 3.712 3.712 1.157-1.157a2.625 2.625 0 0 0 0-3.712ZM19.513 8.199l-3.712-3.712-12.15 12.15a5.25 5.25 0 0 0-1.32 2.214l-.8 2.685a.75.75 0 0 0 .933.933l2.685-.8a5.25 5.25 0 0 0 2.214-1.32L19.513 8.2Z" />
|
||||
</svg>
|
||||
|
||||
</label>
|
||||
@else
|
||||
<button type="button" wire:target="photo" wire:loading.attr="disabled"
|
||||
class="disabled:cursor-not-allowed bottom-0 inset-x-0 bg-gray-500/40 hover:bg-gray-500/80 m-0 p-0 border-0 dark:bg-white/40 dark:hover:bg-gray-700 transition-colors text-red-800 flex items-center justify-center absolute "
|
||||
wire:confirm="Are you sure you want to delete photo ?" wire:click="deletePhoto">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@error('photo')
|
||||
<span class="text-red-500">{{ $message }}</span>
|
||||
@enderror
|
||||
</section>
|
||||
|
||||
|
||||
{{-- Form --}}
|
||||
<div class="space-y-3 grid overflow-x-hidden">
|
||||
|
||||
{{-- Form to update Group name --}}
|
||||
<form @dusk="edit_group_name_form" wire:submit="updateGroupName" x-data="{ editing: false }"
|
||||
class=" justify-center flex items-center w-full gap-5 px-5 items-center">
|
||||
@csrf
|
||||
|
||||
{{-- Left side input --}}
|
||||
<div class=" max-w-[90%] grid h-auto">
|
||||
<div x-show="!editing">
|
||||
<h4 dusk="form_group_name_when_not_editing" class="font-medium break-all whitespace-pre-line text-2xl ">{{ $groupName }} </h4>
|
||||
</div>
|
||||
|
||||
<input x-cloak maxlength="110" x-show="editing" id='groupName' type="text"
|
||||
wire:model='groupName'
|
||||
class="resize-none text-2xl font-medium border-0 px-0 py-0 py-0 border-b border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] bg-inherit dark:text-white outline-hidden w-full focus:outline-hidden focus:ring-0 hover:ring-0">
|
||||
|
||||
|
||||
@error('groupName')
|
||||
<p class="text-red-500 inline">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Right Side --}}
|
||||
<span class=" items-center">
|
||||
|
||||
<button type="button" @click="editing=true" x-show="!editing">
|
||||
{{-- pencil/edit --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="size-6 w-5 h-5">
|
||||
<path
|
||||
d="M21.731 2.269a2.625 2.625 0 0 0-3.712 0l-1.157 1.157 3.712 3.712 1.157-1.157a2.625 2.625 0 0 0 0-3.712ZM19.513 8.199l-3.712-3.712-12.15 12.15a5.25 5.25 0 0 0-1.32 2.214l-.8 2.685a.75.75 0 0 0 .933.933l2.685-.8a5.25 5.25 0 0 0 2.214-1.32L19.513 8.2Z" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
|
||||
<button x-cloak @click="editing=false" x-show="editing">
|
||||
{{-- check/submit --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-check-lg w-5 h-5" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</span>
|
||||
|
||||
</form>
|
||||
|
||||
{{-- Members count --}}
|
||||
<p class="mx-auto"> {{ __('wirechat::chat.group.info.labels.members') }} {{ $totalParticipants }} </p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{-- About --}}
|
||||
<section class=" px-8 py-5 ">
|
||||
<div @dusk="edit_description_section" x-data="{ editing: false }" @click.outside="editing=false"
|
||||
class="grid grid-cols-12 items-center">
|
||||
|
||||
{{-- Left side input --}}
|
||||
<span class="col-span-11">
|
||||
<div x-show="!editing">
|
||||
@if (empty($description))
|
||||
<p class="text-sm" style="color: var(--wirechat-primary-color)">{{ __('wirechat::chat.group.info.labels.add_description') }} </p>
|
||||
@else
|
||||
<p class="font-medium break-all whitespace-pre-line ">{{ $description }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<textarea x-cloak maxlength="501" x-show="editing" id='description' type="text" wire:model.blur='description'
|
||||
class="resize-none font-medium w-full border-0 px-0 py-0 py-0 border-b border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] bg-inherit dark:text-white outline-hidden w-full focus:outline-hidden focus:ring-0 hover:ring-0">
|
||||
</textarea>
|
||||
|
||||
@error('description')
|
||||
<p class="text-red-500">{{ $message }}</p>
|
||||
@enderror
|
||||
</span>
|
||||
|
||||
{{-- Right Side --}}
|
||||
<span class="col-span-1 flex items-center justify-end">
|
||||
|
||||
<button @click="editing=true" x-show="!editing">
|
||||
{{-- pencil/edit --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="size-6 w-5 h-5">
|
||||
<path
|
||||
d="M21.731 2.269a2.625 2.625 0 0 0-3.712 0l-1.157 1.157 3.712 3.712 1.157-1.157a2.625 2.625 0 0 0 0-3.712ZM19.513 8.199l-3.712-3.712-12.15 12.15a5.25 5.25 0 0 0-1.32 2.214l-.8 2.685a.75.75 0 0 0 .933.933l2.685-.8a5.25 5.25 0 0 0 2.214-1.32L19.513 8.2Z" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
|
||||
<button x-cloak @click="editing=false" x-show="editing">
|
||||
{{-- check --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-check-lg w-5 h-5" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
@else
|
||||
{{-- Plain group information --}}
|
||||
<div @dusk="non_editable_group_information_section" class="flex flex-col items-center gap-5 py-5 px-4 ">
|
||||
<x-wirechat::avatar :src="$cover_url" class=" h-32 w-32 mx-auto" />
|
||||
<h4 dusk="group_name" class="font-medium break-all whitespace-pre-line text-2xl ">{{ $groupName }} </h4>
|
||||
<p class="mx-auto">{{ __('wirechat::chat.group.info.labels.members') }} {{ $totalParticipants }} </p>
|
||||
<p class="font-medium break-all whitespace-pre-line ">{{ $description }} </p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
<x-wirechat::divider />
|
||||
|
||||
{{-- Disappearing Messages Settings --}}
|
||||
@if(timebank_config('wirechat.disappearing_messages.enabled', true))
|
||||
<section class="px-8 py-5">
|
||||
@livewire('wire-chat.disappearing-messages-settings', ['conversationId' => $conversation->id], key('disappearing-'.$conversation->id))
|
||||
</section>
|
||||
<x-wirechat::divider />
|
||||
@endif
|
||||
|
||||
{{-- Members section --}}
|
||||
<section class="my-4 text-left space-y-3">
|
||||
|
||||
{{-- Actiion button to trigger opening members modal --}}
|
||||
<x-wirechat::actions.open-modal component="wirechat.chat.group.members"
|
||||
conversation="{{ $conversation?->id }}" widget="{{ $this->isWidget() }}">
|
||||
{{-- Members count --}}
|
||||
<button class="cursor-pointer flex w-full justify-between items-center px-8 focus:outline-hidden ">
|
||||
<span class="text-gray-600 dark:text-gray-300">{{ __('wirechat::chat.group.info.labels.members') }} {{ $totalParticipants }}</span>
|
||||
{{-- Search icon --}}
|
||||
<span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</x-wirechat::actions.open-modal>
|
||||
|
||||
{{-- Add Members --}}
|
||||
@if ($authIsAdminInGroup || $group?->allowsMembersToAddOthers())
|
||||
<x-wirechat::actions.open-modal component="wirechat.chat.group.add-members"
|
||||
conversation="{{ $conversation?->id }}" widget="{{ $this->isWidget() }}">
|
||||
<button @dusk="open_add_members_modal_button"
|
||||
class="cursor-pointer w-full py-5 px-8 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] focus:outline-hidden transition flex gap-3 items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="size-6 w-5 h-5">
|
||||
<path
|
||||
d="M5.25 6.375a4.125 4.125 0 1 1 8.25 0 4.125 4.125 0 0 1-8.25 0ZM2.25 19.125a7.125 7.125 0 0 1 14.25 0v.003l-.001.119a.75.75 0 0 1-.363.63 13.067 13.067 0 0 1-6.761 1.873c-2.472 0-4.786-.684-6.76-1.873a.75.75 0 0 1-.364-.63l-.001-.122ZM18.75 7.5a.75.75 0 0 0-1.5 0v2.25H15a.75.75 0 0 0 0 1.5h2.25v2.25a.75.75 0 0 0 1.5 0v-2.25H21a.75.75 0 0 0 0-1.5h-2.25V7.5Z" />
|
||||
</svg>
|
||||
|
||||
<span>{{ __('wirechat::chat.group.info.actions.add_members.label') }}</span>
|
||||
</button>
|
||||
</x-wirechat::actions.open-modal>
|
||||
@endif
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<x-wirechat::divider />
|
||||
|
||||
{{-- Footer section --}}
|
||||
<footer class="flex flex-col justify-start w-full">
|
||||
|
||||
@if ($authIsOwner)
|
||||
|
||||
{{-- Delete group --}}
|
||||
<button wire:confirm="{{ __('wirechat::chat.group.info.actions.delete_group.confirmation_message') }}" wire:click="deleteGroup"
|
||||
class="cursor-pointer w-full py-5 px-8 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] transition text-start space-y-2 gap-3 text-red-500">
|
||||
<div class="flex gap-3 items-center ">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
<span>{{ __('wirechat::chat.group.info.actions.delete_group.label') }}</span>
|
||||
</div>
|
||||
|
||||
<p class="dark:text-white/60 text-sm text-gray-600/80">@lang('wirechat::chat.group.info.actions.delete_group.helper_text')</p>
|
||||
</button>
|
||||
{{-- Permissions --}}
|
||||
<div>
|
||||
|
||||
<x-wirechat::actions.open-chat-drawer component='wirechat.chat.group.permissions'
|
||||
conversation="{{ $conversation?->id }}">
|
||||
<button
|
||||
class="cursor-pointer w-full py-5 px-8 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] transition text-start space-y-2 gap-3 dark:text-white/90">
|
||||
<div class="flex gap-3 items-center ">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="size-6 w-5 h-5 dark:text-gray-400">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M4.5 12a7.5 7.5 0 0 0 15 0m-15 0a7.5 7.5 0 1 1 15 0m-15 0H3m16.5 0H21m-1.5 0H12m-8.457 3.077 1.41-.513m14.095-5.13 1.41-.513M5.106 17.785l1.15-.964m11.49-9.642 1.149-.964M7.501 19.795l.75-1.3m7.5-12.99.75-1.3m-6.063 16.658.26-1.477m2.605-14.772.26-1.477m0 17.726-.26-1.477M10.698 4.614l-.26-1.477M16.5 19.794l-.75-1.299M7.5 4.205 12 12m6.894 5.785-1.149-.964M6.256 7.178l-1.15-.964m15.352 8.864-1.41-.513M4.954 9.435l-1.41-.514M12.002 12l-3.75 6.495" />
|
||||
</svg>
|
||||
|
||||
<span>@lang('wirechat::chat.group.info.actions.group_permissions.label')</span>
|
||||
</div>
|
||||
</button>
|
||||
</x-wirechat::actions.open-chat-drawer>
|
||||
</div>
|
||||
@else
|
||||
{{-- Exit Group --}}
|
||||
<button wire:confirm="{{ __('wirechat::chat.group.info.actions.exit_group.confirmation_message') }}" wire:click="exitConversation"
|
||||
class="cursor-pointer w-full py-5 px-8 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] transition flex gap-3 items-center text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-box-arrow-right w-5 h-5" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0z" />
|
||||
<path fill-rule="evenodd"
|
||||
d="M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708z" />
|
||||
</svg>
|
||||
<span>@lang('wirechat::chat.group.info.actions.exit_group.label')</span>
|
||||
</button>
|
||||
@endif
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
162
resources/views/vendor/wirechat/livewire/chat/group/members.blade.php
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
@php
|
||||
$authIsAdminInGroup= $participant?->isAdmin();
|
||||
$authIsOwner= $participant?->isOwner();
|
||||
$isGroup= $conversation?->isGroup();
|
||||
|
||||
@endphp
|
||||
|
||||
|
||||
<div x-ref="members"
|
||||
class="h-[calc(100vh_-_6rem)] sm:h-[450px] bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] dark:text-white border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] overflow-y-auto overflow-x-hidden ">
|
||||
|
||||
<header class=" sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] z-10 p-2">
|
||||
<div class="flex items-center justify-center pb-2">
|
||||
|
||||
<x-wirechat::actions.close-modal>
|
||||
<button dusk="close_modal_button"
|
||||
class="p-2 ml-0 text-gray-600 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] dark:hover:text-white rounded-full hover:text-gray-800 ">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class=" w-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
</x-wirechat::actions.close-modal>
|
||||
|
||||
<h3 class=" mx-auto font-semibold ">{{__('wirechat::chat.group.members.heading.label')}} </h3>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Member limit error --}}
|
||||
<section class="flex flex-wrap items-center px-0 border-b dark:border-[var(--wc-dark-secondary)]">
|
||||
<input type="search" id="users-search-field" wire:model.live.debounce='search' autocomplete="off"
|
||||
placeholder="{{__('wirechat::chat.group.members.inputs.search.placeholder')}}"
|
||||
class=" w-full border-0 w-auto dark:bg-[var(--wc-dark-primary)] outline-hidden focus:outline-hidden bg-[var(--wc-dark-parimary)] rounded-lg focus:ring-0 hover:ring-0">
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
<div class="relative w-full p-2 ">
|
||||
{{-- <h5 class="text font-semibold text-gray-800 dark:text-gray-100">Recent Chats</h5> --}}
|
||||
<section class="my-4 grid">
|
||||
@if (count($participants)!=0)
|
||||
|
||||
<ul class="overflow-auto flex flex-col">
|
||||
|
||||
@foreach ($participants as $key => $participant)
|
||||
@php
|
||||
$loopParticipantIsAuth =
|
||||
$participant->participantable_id == auth()->id() &&
|
||||
$participant->participantable_type == auth()->user()->getMorphClass();
|
||||
@endphp
|
||||
<li x-data="{ open: false }" x-ref="button" @click="open = ! open" x-init="$watch('open', value => {
|
||||
$refs.members.style.overflow = value ? 'hidden' : '';
|
||||
})"
|
||||
aria-modal="true"
|
||||
tabindex="0"
|
||||
x-on:keydown.escape.stop="open=false"
|
||||
@click.away ="open=false;" wire:key="users-{{ $key }}"
|
||||
:class="!open || 'bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)]'"
|
||||
class="flex cursor-pointer group gap-2 items-center overflow-x-hidden p-2 py-3">
|
||||
|
||||
<label class="flex cursor-pointer gap-2 items-center w-full">
|
||||
<x-wirechat::avatar src="{{ $participant->participantable->cover_url }}"
|
||||
class="w-10 h-10" />
|
||||
|
||||
<div class="grid grid-cols-12 w-full ">
|
||||
<h6 @class(['transition-all truncate group-hover:underline col-span-10' ])>
|
||||
{{ $loopParticipantIsAuth ? 'You' : $participant->participantable->display_name }}</h6>
|
||||
@if ($participant->isOwner()|| $participant->isAdmin())
|
||||
<span style="background-color: var(--wirechat-primary-color);" class=" flex items-center col-span-2 text-white text-xs font-medium ml-auto px-2.5 py-px rounded-sm ">
|
||||
{{$participant->isOwner()? __('wirechat::chat.group.members.labels.owner'): __('wirechat::chat.group.members.labels.admin')}}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
<div x-show="open" x-anchor.bottom-end="$refs.button"
|
||||
class="ml-auto bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] border-[var(--wc-light-primary) dark:border-[var(--wc-dark-primary)] py-4 shadow-sm border rounded-md grid space-y-2 w-52">
|
||||
{{-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 text-gray-600 dark:text-gray-300 w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
|
||||
</svg> --}}
|
||||
|
||||
<x-wirechat::dropdown-button wire:click="sendMessage('{{ $participant->id }}')"
|
||||
class="truncate ">
|
||||
@if ($loopParticipantIsAuth)
|
||||
|
||||
{{__('wirechat::chat.group.members.actions.send_message_to_yourself.label')}}
|
||||
@else
|
||||
|
||||
{{__('wirechat::chat.group.members.actions.send_message_to_member.label',['member'=>$participant->participantable?->display_name ])}}
|
||||
@endif
|
||||
</x-wirechat::dropdown-button>
|
||||
|
||||
@if ($authIsAdminInGroup || $authIsOwner)
|
||||
{{-- Only show admin actions to owner of group and if is not the current loop --}}
|
||||
{{--AND We only want to show admin actions if participant is not owner --}}
|
||||
@if ($authIsOwner && !$loopParticipantIsAuth)
|
||||
@if ($participant->isAdmin())
|
||||
<x-wirechat::dropdown-button
|
||||
wire:click="dismissAdmin('{{ $participant->id }}')"
|
||||
wire:confirm="{{__('wirechat::chat.group.members.actions.dismiss_admin.confirmation_message',['member'=>$participant->participantable?->display_name])}}"
|
||||
class=" ">
|
||||
{{__('wirechat::chat.group.members.actions.dismiss_admin.label')}}
|
||||
</x-wirechat::dropdown-button>
|
||||
@else
|
||||
<x-wirechat::dropdown-button
|
||||
wire:click="makeAdmin('{{ $participant->id }}')"
|
||||
wire:confirm="{{__('wirechat::chat.group.members.actions.make_admin.confirmation_message',['member'=>$participant->participantable?->display_name])}}"
|
||||
class=" ">
|
||||
{{__('wirechat::chat.group.members.actions.make_admin.label')}}
|
||||
</x-wirechat::dropdown-button>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
{{--AND We only want to show remove actions if participant is not owner of conversation because we don't want to remove owner--}}
|
||||
@if (!$participant->isOwner() && !$loopParticipantIsAuth && !$participant->isAdmin())
|
||||
<x-wirechat::dropdown-button
|
||||
wire:click="removeFromGroup('{{ $participant->id }}')"
|
||||
wire:confirm="{{__('wirechat::chat.group.members.actions.remove_from_group.confirmation_message',['member'=>$participant->participantable?->display_name])}}"
|
||||
class="text-red-500 ">
|
||||
{{__('wirechat::chat.group.members.actions.remove_from_group.label')}}
|
||||
</x-wirechat::dropdown-button>
|
||||
@endif
|
||||
|
||||
@else
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</label>
|
||||
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
{{-- Load more button --}}
|
||||
@if ($canLoadMore)
|
||||
<section class="w-full justify-center flex my-3">
|
||||
<button dusk="loadMoreButton" @click="$wire.loadMore()"
|
||||
class=" text-sm dark:text-white hover:text-gray-700 transition-colors dark:hover:text-gray-500 dark:gray-200">
|
||||
{{__('wirechat::chat.group.members.actions.load_more.label')}}
|
||||
</button>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
@else
|
||||
|
||||
<span class="m-auto">{{__('wirechat::chat.group.members.labels.no_members_found')}}</span>
|
||||
@endif
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
148
resources/views/vendor/wirechat/livewire/chat/group/permissions.blade.php
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
<div class="bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] overfo opaticy-100 min-h-screen w-full">
|
||||
<section class="flex gap-4 z-10 items-center p-5 sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] ">
|
||||
<button wire:click="$dispatch('closeChatDrawer')" class="focus:outline-hidden"> <svg class="w-7 h-7"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg> </button>
|
||||
<h3>{{__('wirechat::chat.group.permisssions.heading.label')}} </h3>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="">
|
||||
<section >
|
||||
|
||||
<h5 class="w-full text-start py-4 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] px-4">
|
||||
|
||||
{{__('wirechat::chat.group.permisssions.labels.members_can')}}:
|
||||
</h5>
|
||||
|
||||
<ul class="space-y-2">
|
||||
{{-- Edit Group Settings --}}
|
||||
<li class="w-full flex p-5">
|
||||
<span class="w-12">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125" />
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
|
||||
<span class="w-full text-start">
|
||||
<h5 class="font-medium">{{__('wirechat::chat.group.permisssions.actions.edit_group_information.label')}}</h5>
|
||||
<p>@lang('wirechat::chat.group.permisssions.actions.edit_group_information.helper_text')</p>
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<span class="w-12">
|
||||
|
||||
<label class="inline-flex items-center cursor-pointer">
|
||||
<input wire:model.live.debounce="allow_members_to_edit_group_info" type="checkbox" class="sr-only peer">
|
||||
<div class="relative w-11 h-6 peer-focus:outline-hidden rounded-full peer
|
||||
bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)]
|
||||
shadow-2xs
|
||||
peer-checked:border-[var(--wc-brand-primary)]
|
||||
peer-checked:after:translate-x-full peer-checked:rtl:after:-translate-x-full
|
||||
peer-checked:dark:border-[var(--wc-dark-primary)] peer-checked:border-[var(--wc-light-primary)]
|
||||
after:content-[''] after:absolute after:top-[2px]
|
||||
after:start-[2px] after:bg-white dark:after:bg-gray-100 after:shadow
|
||||
peer-checked:after:bg-[var(--wc-brand-primary)] peer-checked:dark:after:bg-[var(--wc-brand-primary)]
|
||||
peer-checked:after:border-[var(--wc-brand-primary)] peer-checked:dark:after:border-[var(--wc-brand-primary)]
|
||||
dark:after:border-[var(--wc-dark-primary)] after:border-[var(--wc-light-primary)] after:border
|
||||
after:rounded-full after:h-5 after:w-5 after:transition-all ease-in-out">
|
||||
</div>
|
||||
</label>
|
||||
|
||||
</span>
|
||||
|
||||
</li>
|
||||
|
||||
{{-- Send Messages --}}
|
||||
<li class="w-full flex items-center p-5">
|
||||
<span class="w-12">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.6" stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.625 9.75a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 0 1 .778-.332 48.294 48.294 0 0 0 5.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<span class="w-full text-start">
|
||||
<h5 class="font-medium">@lang('wirechat::chat.group.permisssions.actions.send_messages.label')</h5>
|
||||
</span>
|
||||
|
||||
|
||||
<span class="w-12">
|
||||
|
||||
<label class="inline-flex items-center cursor-pointer">
|
||||
<input wire:model.live.debounce="allow_members_to_send_messages" type="checkbox" class="sr-only peer">
|
||||
<div class="relative w-11 h-6 peer-focus:outline-hidden rounded-full peer
|
||||
bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)]
|
||||
shadow-2xs
|
||||
peer-checked:border-[var(--wc-brand-primary)]
|
||||
peer-checked:after:translate-x-full peer-checked:rtl:after:-translate-x-full
|
||||
peer-checked:dark:border-[var(--wc-dark-primary)] peer-checked:border-[var(--wc-light-primary)]
|
||||
after:content-[''] after:absolute after:top-[2px]
|
||||
after:start-[2px] after:bg-white dark:after:bg-gray-100 after:shadow
|
||||
peer-checked:after:bg-[var(--wc-brand-primary)] peer-checked:dark:after:bg-[var(--wc-brand-primary)]
|
||||
peer-checked:after:border-[var(--wc-brand-primary)] peer-checked:dark:after:border-[var(--wc-brand-primary)]
|
||||
dark:after:border-[var(--wc-dark-primary)] after:border-[var(--wc-light-primary)] after:border
|
||||
after:rounded-full after:h-5 after:w-5 after:transition-all ease-in-out">
|
||||
</div>
|
||||
</label>
|
||||
|
||||
</span>
|
||||
|
||||
</li>
|
||||
|
||||
{{-- Add other members --}}
|
||||
<li class="w-full flex items-center p-5">
|
||||
<span class="w-12">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6 w-6 h-6 text-gray-500 dark:text-white/90">
|
||||
<path d="M5.25 6.375a4.125 4.125 0 1 1 8.25 0 4.125 4.125 0 0 1-8.25 0ZM2.25 19.125a7.125 7.125 0 0 1 14.25 0v.003l-.001.119a.75.75 0 0 1-.363.63 13.067 13.067 0 0 1-6.761 1.873c-2.472 0-4.786-.684-6.76-1.873a.75.75 0 0 1-.364-.63l-.001-.122ZM18.75 7.5a.75.75 0 0 0-1.5 0v2.25H15a.75.75 0 0 0 0 1.5h2.25v2.25a.75.75 0 0 0 1.5 0v-2.25H21a.75.75 0 0 0 0-1.5h-2.25V7.5Z" />
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
|
||||
<span class="w-full text-start">
|
||||
<h5 class="font-medium">@lang('wirechat::chat.group.permisssions.actions.add_other_members.label')</h5>
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
<span class="w-12">
|
||||
|
||||
<label class="inline-flex items-center cursor-pointer">
|
||||
<input wire:model.live.debounce="allow_members_to_add_others" type="checkbox" class="sr-only peer">
|
||||
<div class="relative w-11 h-6 peer-focus:outline-hidden rounded-full peer
|
||||
bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)]
|
||||
shadow-2xs
|
||||
peer-checked:border-[var(--wc-brand-primary)]
|
||||
peer-checked:after:translate-x-full peer-checked:rtl:after:-translate-x-full
|
||||
peer-checked:dark:border-[var(--wc-dark-primary)] peer-checked:border-[var(--wc-light-primary)]
|
||||
after:content-[''] after:absolute after:top-[2px]
|
||||
after:start-[2px] after:bg-white dark:after:bg-gray-100 after:shadow
|
||||
peer-checked:after:bg-[var(--wc-brand-primary)] peer-checked:dark:after:bg-[var(--wc-brand-primary)]
|
||||
peer-checked:after:border-[var(--wc-brand-primary)] peer-checked:dark:after:border-[var(--wc-brand-primary)]
|
||||
dark:after:border-[var(--wc-dark-primary)] after:border-[var(--wc-light-primary)] after:border
|
||||
after:rounded-full after:h-5 after:w-5 after:transition-all ease-in-out">
|
||||
</div>
|
||||
|
||||
|
||||
</label>
|
||||
|
||||
</span>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
64
resources/views/vendor/wirechat/livewire/chat/info.blade.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<div id="info-modal" class="bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] min-h-screen">
|
||||
|
||||
|
||||
<section class="flex gap-4 z-10 items-center p-5 sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] ">
|
||||
<button wire:click="$dispatch('closeChatDrawer')" class="focus:outline-hidden cursor-pointer"> <svg class="w-7 h-7"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.8"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg> </button>
|
||||
<h3>{{ __('wirechat::chat.info.heading.label') }}</h3>
|
||||
</section>
|
||||
{{-- Details --}}
|
||||
|
||||
<header>
|
||||
|
||||
<div class="flex flex-col items-center gap-5 ">
|
||||
|
||||
<div class="mx-auto items-center justify-center grid">
|
||||
|
||||
<a href="{{ $receiver?->profile_url }}">
|
||||
<x-wirechat::avatar :src="$cover_url" class=" h-32 w-32 mx-auto" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class=" grid ">
|
||||
|
||||
<a class="px-8 py-5 " @dusk="receiver_name" href="{{ $receiver?->profile_url }}">
|
||||
<h5 class="text-2xl">{{ $receiver?->display_name }}</h5>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
<x-wirechat::divider />
|
||||
|
||||
{{-- Disappearing Messages Settings --}}
|
||||
@if(timebank_config('wirechat.disappearing_messages.enabled', true))
|
||||
<section class="px-8 py-5">
|
||||
@livewire('wire-chat.disappearing-messages-settings', ['conversationId' => $conversation->id], key('disappearing-'.$conversation->id))
|
||||
</section>
|
||||
<x-wirechat::divider />
|
||||
@endif
|
||||
|
||||
{{-- Footer section --}}
|
||||
<section class="flex flex-col justify-start w-full">
|
||||
|
||||
{{-- Only show if is not group --}}
|
||||
<button wire:confirm="{{ __('wirechat::chat.info.actions.delete_chat.confirmation_message') }}" wire:click="deleteChat"
|
||||
class=" w-full cursor-pointer py-5 px-8 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] transition flex gap-3 items-center text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
|
||||
<span>{{ __('wirechat::chat.info.actions.delete_chat.label') }}</span>
|
||||
</button>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
364
resources/views/vendor/wirechat/livewire/chat/partials/body.blade.php
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
|
||||
<main x-data="{
|
||||
height: 0,
|
||||
previousHeight: 0,
|
||||
updateScrollPosition: function() {
|
||||
// Calculate the difference in height
|
||||
|
||||
newHeight = $el.scrollHeight;
|
||||
|
||||
{{-- console.log('old height' + height);
|
||||
console.log('new height' + document.getElementById('conversation').scrollHeight); --}}
|
||||
heightDifference = newHeight - height;
|
||||
|
||||
{{-- console.log('conversationElement.scrollTop ' + conversationElement.scrollTop);
|
||||
console.log('heightDifference' + heightDifference); --}}
|
||||
|
||||
$el.scrollTop += heightDifference;
|
||||
// Update the previous height to the new height
|
||||
height = newHeight;
|
||||
|
||||
}
|
||||
|
||||
}"
|
||||
x-init="
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
|
||||
this.height = $el.scrollHeight;
|
||||
$el.scrollTop = this.height;
|
||||
});
|
||||
|
||||
}, 300); //! Add delay so height can be update at right time
|
||||
|
||||
|
||||
"
|
||||
@scroll ="
|
||||
scrollTop= $el.scrollTop;
|
||||
if((scrollTop<=0) && $wire.canLoadMore){
|
||||
|
||||
$wire.loadMore();
|
||||
|
||||
}
|
||||
"
|
||||
@update-height.window="
|
||||
requestAnimationFrame(() => {
|
||||
updateScrollPosition();
|
||||
});
|
||||
"
|
||||
|
||||
@scroll-bottom.window="
|
||||
requestAnimationFrame(() => {
|
||||
{{-- overflow-y: hidden; is used to hide the vertical scrollbar initially. --}}
|
||||
$el.style.overflowY='hidden';
|
||||
|
||||
|
||||
|
||||
{{-- scroll the element down --}}
|
||||
$el.scrollTop = $el.scrollHeight;
|
||||
|
||||
{{-- After updating the chat height, overflowY is set back to 'auto',
|
||||
which allows the browser to determine whether to display the scrollbar
|
||||
based on the content height. --}}
|
||||
$el.style.overflowY='auto';
|
||||
});
|
||||
"
|
||||
|
||||
|
||||
x-cloak
|
||||
class='flex flex-col h-full relative gap-2 gap-y-4 p-4 md:p-5 lg:p-8 grow overscroll-contain overflow-x-hidden w-full my-auto'
|
||||
style="contain: content" >
|
||||
|
||||
|
||||
|
||||
<div x-cloak wire:loading.delay.class.remove="invisible" wire:target="loadMore" class="invisible transition-all duration-300 ">
|
||||
<x-wirechat::loading-spin />
|
||||
</div>
|
||||
|
||||
{{-- Define previous message outside the loop --}}
|
||||
@php
|
||||
$previousMessage = null;
|
||||
@endphp
|
||||
|
||||
<!--Message-->
|
||||
@if ($loadedMessages)
|
||||
{{-- @dd($loadedMessages) --}}
|
||||
@foreach ($loadedMessages as $date => $messageGroup)
|
||||
|
||||
{{-- Date --}}
|
||||
<div class="sticky top-0 uppercase p-2 shadow-xs px-2.5 z-50 rounded-xl border dark:border-[var(--wc-dark-primary)] border-[var(--wc-light-primary)] text-sm flex text-center justify-center bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] dark:text-white w-28 mx-auto ">
|
||||
{{ $date }}
|
||||
</div>
|
||||
|
||||
@foreach ($messageGroup as $key => $message)
|
||||
{{-- @dd($message) --}}
|
||||
@php
|
||||
$belongsToAuth = $message->belongsToAuth();
|
||||
$parent = $message->parent ?? null;
|
||||
$attachment = $message->attachment ?? null;
|
||||
$isEmoji = $message->isEmoji();
|
||||
|
||||
|
||||
// keep track of previous message
|
||||
// The ($key -1 ) will get the previous message from loaded
|
||||
// messages since $key is directly linked to $message
|
||||
if ($key > 0) {
|
||||
$previousMessage = $messageGroup->get($key - 1);
|
||||
}
|
||||
|
||||
// Get the next message
|
||||
$nextMessage = $key < $messageGroup->count() - 1 ? $messageGroup->get($key + 1) : null;
|
||||
@endphp
|
||||
|
||||
|
||||
<div class="flex gap-2" wire:key="message-{{ $key }}" >
|
||||
|
||||
{{-- Message user Avatar --}}
|
||||
{{-- Hide avatar if message belongs to auth --}}
|
||||
@if (!$belongsToAuth && !$isPrivate)
|
||||
<div @class([
|
||||
'shrink-0 mb-auto -mb-2',
|
||||
// Hide avatar if the next message is from the same user
|
||||
'invisible' =>
|
||||
$previousMessage &&
|
||||
$message?->sendable?->is($previousMessage?->sendable),
|
||||
])>
|
||||
<x-wirechat::avatar src="{{ $message->sendable?->cover_url ?? null }}" class="h-8 w-8" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- we use w-[95%] to leave space for the image --}}
|
||||
<div class="w-[95%] mx-auto">
|
||||
<div @class([
|
||||
'max-w-[85%] md:max-w-[78%] flex flex-col gap-y-2 ',
|
||||
'ml-auto' => $belongsToAuth])>
|
||||
|
||||
|
||||
|
||||
{{-- Show parent/reply message --}}
|
||||
@if ($parent != null)
|
||||
<div @class([
|
||||
'max-w-fit flex flex-col gap-y-2',
|
||||
'ml-auto' => $belongsToAuth,
|
||||
// 'ml-9 sm:ml-10' => !$belongsToAuth,
|
||||
])>
|
||||
|
||||
|
||||
@php
|
||||
$sender = $message?->ownedBy($this->auth)
|
||||
? __('wirechat::chat.labels.you')
|
||||
: ($message->sendable?->display_name ?? __('wirechat::chat.labels.user'));
|
||||
|
||||
$receiver = $parent?->ownedBy($this->auth)
|
||||
? __('wirechat::chat.labels.you')
|
||||
: ($parent->sendable?->display_name ?? __('wirechat::chat.labels.user'));
|
||||
@endphp
|
||||
|
||||
<h6 class="text-xs text-gray-500 dark:text-gray-300 px-2">
|
||||
@if ($parent?->ownedBy($this->auth) && $message?->ownedBy($this->auth))
|
||||
{{ __('wirechat::chat.labels.you_replied_to_yourself') }}
|
||||
@elseif ($parent?->ownedBy($this->auth))
|
||||
{{ __('wirechat::chat.labels.participant_replied_to_you', ['sender' => $sender]) }}
|
||||
@elseif ($message?->ownedBy($parent->sendable))
|
||||
{{ __('wirechat::chat.labels.participant_replied_to_themself', ['sender' => $sender]) }}
|
||||
@else
|
||||
{{ __('wirechat::chat.labels.participant_replied_other_participant', ['sender' => $sender, 'receiver' => $receiver]) }}
|
||||
@endif
|
||||
</h6>
|
||||
|
||||
|
||||
|
||||
<div @class([
|
||||
'px-1 border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-accent)] overflow-hidden ',
|
||||
' border-r-4 ml-auto' => $belongsToAuth,
|
||||
' border-l-4 mr-auto ' => !$belongsToAuth,
|
||||
])>
|
||||
<p
|
||||
class=" bg-[var(--wc-light-secondary)] dark:text-white dark:bg-[var(--wc-dark-secondary)] text-black line-clamp-1 text-sm rounded-full max-w-fit px-3 py-1 ">
|
||||
{{ $parent?->body != '' ? $parent?->body : ($parent->hasAttachment() ? __('wirechat::chat.labels.attachment') : '') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
{{-- Body section --}}
|
||||
<div @class([
|
||||
'flex gap-1 md:gap-4 group transition-transform ',
|
||||
'justify-end' => $belongsToAuth,
|
||||
])>
|
||||
|
||||
{{-- Message Actions --}}
|
||||
@if (($isGroup && $conversation->group?->allowsMembersToSendMessages()) || $authParticipant->isAdmin())
|
||||
<div dusk="message_actions" @class([ 'my-auto flex w-auto items-center gap-2', 'order-1' => $belongsToAuth, 'order-3' => !$belongsToAuth, ])>
|
||||
{{-- reply button --}}
|
||||
<button wire:click="setReply('{{ encrypt($message->id) }}')"
|
||||
class=" invisible group-hover:visible hover:scale-110 transition-transform">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-reply-fill w-4 h-4 dark:text-white"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M5.921 11.9 1.353 8.62a.72.72 0 0 1 0-1.238L5.921 4.1A.716.716 0 0 1 7 4.719V6c1.5 0 6 0 7 8-2.5-4.5-7-4-7-4v1.281c0 .56-.606.898-1.079.62z" />
|
||||
</svg>
|
||||
</button>
|
||||
{{-- Dropdown actions button --}}
|
||||
<x-wirechat::dropdown class="w-40" align="{{ $belongsToAuth ? 'right' : 'left' }}"
|
||||
width="48">
|
||||
<x-slot name="trigger">
|
||||
{{-- Dots --}}
|
||||
<button class="invisible group-hover:visible hover:scale-110 transition-transform">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-three-dots h-3 w-3 text-gray-700 dark:text-white"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3m5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3" />
|
||||
</svg>
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
|
||||
{{-- Keep message (if disappearing messages enabled) --}}
|
||||
@if (timebank_config('wirechat.disappearing_messages.enabled', true) &&
|
||||
timebank_config('wirechat.disappearing_messages.allow_users_to_keep', true))
|
||||
<button dusk="keep_message_button" wire:click="keepMessage('{{ encrypt($message->id) }}')" class="w-full text-start">
|
||||
<x-wirechat::dropdown-link>
|
||||
@if ($message->kept_at)
|
||||
<span class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18"/>
|
||||
</svg>
|
||||
{{ __('Unkeep message') }}
|
||||
</span>
|
||||
@else
|
||||
<span class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
|
||||
</svg>
|
||||
{{ __('Keep message') }}
|
||||
</span>
|
||||
@endif
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
@endif
|
||||
|
||||
@if ($message->ownedBy($this->auth)|| ($authParticipant->isAdmin() && $isGroup))
|
||||
<button dusk="delete_message_for_everyone" wire:click="deleteForEveryone('{{ encrypt($message->id) }}')"
|
||||
wire:confirm="{{ __('wirechat::chat.actions.delete_for_everyone.confirmation_message') }}" class="w-full text-start">
|
||||
<x-wirechat::dropdown-link>
|
||||
@lang('wirechat::chat.actions.delete_for_everyone.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
@endif
|
||||
|
||||
|
||||
{{-- Dont show delete for me if is group --}}
|
||||
@if (!$isGroup)
|
||||
<button dusk="delete_message_for_me" wire:click="deleteForMe('{{ encrypt($message->id) }}')"
|
||||
wire:confirm="{{ __('wirechat::chat.actions.delete_for_me.confirmation_message') }}" class="w-full text-start">
|
||||
<x-wirechat::dropdown-link>
|
||||
@lang('wirechat::chat.actions.delete_for_me.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
@endif
|
||||
|
||||
|
||||
<button dusk="reply_to_message_button" wire:click="setReply('{{ encrypt($message->id) }}')"class="w-full text-start">
|
||||
<x-wirechat::dropdown-link>
|
||||
@lang('wirechat::chat.actions.reply.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
|
||||
|
||||
</x-slot>
|
||||
</x-wirechat::dropdown>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Kept Message Indicator (Bookmark) --}}
|
||||
@if (timebank_config('wirechat.disappearing_messages.enabled', true) &&
|
||||
timebank_config('wirechat.disappearing_messages.allow_users_to_keep', true) &&
|
||||
$message->kept_at)
|
||||
<div class="flex items-start pt-0.5 order-2">
|
||||
<svg class="w-4 h-4 text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" title="{{ __('Message kept - will not auto-delete') }}">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Message body --}}
|
||||
<div @class([
|
||||
'flex flex-col gap-2 max-w-[95%] relative',
|
||||
'order-3' => $belongsToAuth,
|
||||
'order-1' => !$belongsToAuth,
|
||||
])>
|
||||
{{-- Show sender name is message does not belong to auth and conversation is group --}}
|
||||
|
||||
|
||||
{{-- -------------------- --}}
|
||||
{{-- Attachment section --}}
|
||||
{{-- -------------------- --}}
|
||||
@if ($attachment)
|
||||
@if (!$belongsToAuth && $isGroup)
|
||||
<div style="color: var(--wc-brand-primary);" @class([
|
||||
'shrink-0 font-medium text-sm sm:text-base',
|
||||
// Hide avatar if the next message is from the same user
|
||||
'hidden' => $message?->sendable?->is($previousMessage?->sendable),
|
||||
])>
|
||||
{{ $message->sendable?->display_name }}
|
||||
</div>
|
||||
@endif
|
||||
{{-- Attachemnt is Application/ --}}
|
||||
@if (str()->startsWith($attachment->mime_type, 'application/'))
|
||||
@include('wirechat::livewire.chat.partials.file', [ 'attachment' => $attachment ])
|
||||
@endif
|
||||
|
||||
{{-- Attachemnt is Video/ --}}
|
||||
@if (str()->startsWith($attachment->mime_type, 'video/'))
|
||||
<x-wirechat::video height="max-h-[400px]" :cover="false" source="{{ $attachment?->url }}" />
|
||||
@endif
|
||||
|
||||
{{-- Attachemnt is image/ --}}
|
||||
@if (str()->startsWith($attachment->mime_type, 'image/'))
|
||||
@include('wirechat::livewire.chat.partials.image', [ 'previousMessage' => $previousMessage, 'message' => $message, 'nextMessage' => $nextMessage, 'belongsToAuth' => $belongsToAuth, 'attachment' => $attachment ])
|
||||
@endif
|
||||
@endif
|
||||
|
||||
{{-- if message is emoji then don't show the styled messagebody layout --}}
|
||||
@if ($isEmoji)
|
||||
<p class="text-5xl dark:text-white ">
|
||||
{{ $message->body }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
{{-- -------------------- --}}
|
||||
{{-- Message body section --}}
|
||||
{{-- If message is not emoji then show the message body styles --}}
|
||||
{{-- -------------------- --}}
|
||||
|
||||
@if ($message->body && !$isEmoji)
|
||||
@include('wirechat::livewire.chat.partials.message', [ 'previousMessage' => $previousMessage, 'message' => $message, 'nextMessage' => $nextMessage, 'belongsToAuth' => $belongsToAuth, 'isGroup' => $isGroup, 'attachment' => $attachment])
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endforeach
|
||||
@endforeach
|
||||
|
||||
|
||||
@endif
|
||||
|
||||
</main>
|
||||
32
resources/views/vendor/wirechat/livewire/chat/partials/file.blade.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
<div class="flex items-center group overflow-hidden border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] rounded-xl">
|
||||
<span class=" p-2">
|
||||
{{-- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-pdf-fill w-11 h-11 text-gray-600" viewBox="0 0 16 16">
|
||||
<path d="M5.523 10.424q.21-.124.459-.238a8 8 0 0 1-.45.606c-.28.337-.498.516-.635.572l-.035.012a.3.3 0 0 1-.026-.044c-.056-.11-.054-.216.04-.36.106-.165.319-.354.647-.548m2.455-1.647q-.178.037-.356.078a21 21 0 0 0 .5-1.05 12 12 0 0 0 .51.858q-.326.048-.654.114m2.525.939a4 4 0 0 1-.435-.41q.344.007.612.054c.317.057.466.147.518.209a.1.1 0 0 1 .026.064.44.44 0 0 1-.06.2.3.3 0 0 1-.094.124.1.1 0 0 1-.069.015c-.09-.003-.258-.066-.498-.256M8.278 4.97c-.04.244-.108.524-.2.829a5 5 0 0 1-.089-.346c-.076-.353-.087-.63-.046-.822.038-.177.11-.248.196-.283a.5.5 0 0 1 .145-.04c.013.03.028.092.032.198q.008.183-.038.465z"/>
|
||||
<path fill-rule="evenodd" d="M4 0h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2m.165 11.668c.09.18.23.343.438.419.207.075.412.04.58-.03.318-.13.635-.436.926-.786.333-.401.683-.927 1.021-1.51a11.6 11.6 0 0 1 1.997-.406c.3.383.61.713.91.95.28.22.603.403.934.417a.86.86 0 0 0 .51-.138c.155-.101.27-.247.354-.416.09-.181.145-.37.138-.563a.84.84 0 0 0-.2-.518c-.226-.27-.596-.4-.96-.465a5.8 5.8 0 0 0-1.335-.05 11 11 0 0 1-.98-1.686c.25-.66.437-1.284.52-1.794.036-.218.055-.426.048-.614a1.24 1.24 0 0 0-.127-.538.7.7 0 0 0-.477-.365c-.202-.043-.41 0-.601.077-.377.15-.576.47-.651.823-.073.34-.04.736.046 1.136.088.406.238.848.43 1.295a20 20 0 0 1-1.062 2.227 7.7 7.7 0 0 0-1.482.645c-.37.22-.699.48-.897.787-.21.326-.275.714-.08 1.103"/>
|
||||
</svg> --}}
|
||||
{{-- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-text-fill w-9 h-10 text-gray-500" viewBox="0 0 16 16">
|
||||
<path d="M9.293 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.707A1 1 0 0 0 13.707 4L10 .293A1 1 0 0 0 9.293 0M9.5 3.5v-2l3 3h-2a1 1 0 0 1-1-1M4.5 9a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1zM4 10.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5m.5 2.5a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1z"/>
|
||||
</svg> --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 text-gray-500">
|
||||
<path d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625Z" />
|
||||
<path d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
</span>
|
||||
<p class="mt-auto p-2 text-gray-600 dark:text-gray-100 text-sm">
|
||||
{{$attachment->original_name}}
|
||||
</p>
|
||||
|
||||
|
||||
<button class="px-3 bg-gray-50 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] transition-colors ease-in-out dark:hover:text-blue-500 hover:text-blue-500 dark:text-white p-1 mt-auto h-full">
|
||||
<a download="{{$attachment->original_name}}" href="{{$attachment?->url}}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download w-5 h-5 " viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5"/>
|
||||
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
</button>
|
||||
</div>
|
||||
618
resources/views/vendor/wirechat/livewire/chat/partials/footer.blade.php
vendored
Normal file
@@ -0,0 +1,618 @@
|
||||
@use('Namu\WireChat\Helpers\Helper')
|
||||
|
||||
<footer class="shrink-0 h-auto relative sticky bottom-0 mt-auto">
|
||||
|
||||
{{-- Check if group allows :sending messages --}}
|
||||
@if ($conversation->isGroup() && !$conversation->group?->allowsMembersToSendMessages() && !$authParticipant->isAdmin())
|
||||
<div
|
||||
class="dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] w-full text-center text-gray-600 dark:text-gray-200 justify-center text-sm flex py-4 ">
|
||||
Only admins can send messages
|
||||
</div>
|
||||
@else
|
||||
<div id="chat-footer" x-data="{ 'openEmojiPicker': false }"
|
||||
class=" px-3 md:px-1 border-t shadow-sm bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] z-50 border-[var(--wc-light-primary)] dark:border-[var(--wc-dark-primary)] flex flex-col gap-3 items-center w-full mx-auto">
|
||||
|
||||
{{-- Emoji section , we put it seperate to avoid interfering as overlay for form when opened --}}
|
||||
<section wire:ignore x-cloak x-show="openEmojiPicker" x-transition:enter="transition ease-out duration-180 transform"
|
||||
x-transition:enter-start=" translate-y-full" x-transition:enter-end=" translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-180 transform" x-transition:leave-start=" translate-y-0"
|
||||
x-transition:leave-end="translate-y-full"
|
||||
class="w-full flex hidden sm:flex py-2 sm:px-4 py-1.5 border-b border-[var(--wc-light-primary)] dark:border-[var(--wc-dark-primary)] h-96 min-w-full">
|
||||
|
||||
<emoji-picker dusk="emoji-picker" style="width: 100%"
|
||||
class=" flex w-full h-full rounded-xl"
|
||||
x-init="$el.dataSource = '/js/vendor/emoji-picker-element-data/en/emojibase/data.json'"></emoji-picker>
|
||||
</section>
|
||||
{{-- form and detail section --}}
|
||||
<section
|
||||
class=" py-2 sm:px-4 py-1.5 z-50 dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] flex flex-col gap-3 items-center w-full mx-auto">
|
||||
|
||||
{{-- Media preview section --}}
|
||||
<section x-show="$wire.media.length>0 ||$wire.files.length>0" x-cloak
|
||||
class=" flex flex-col w-full gap-3" wire:loading.class="animate-pulse" wire:target="sendMessage">
|
||||
|
||||
|
||||
|
||||
@if (count($media) > 0)
|
||||
<div x-data="attachments('media')">
|
||||
{{-- todo: Implement error handling fromserver during file uploads --}}
|
||||
{{--
|
||||
@error('media')
|
||||
<span class="flex text-sm text-red-500 pb-2 bg-gray-100 p-2 w-full justify-between">
|
||||
{{$message}}
|
||||
<button @click="$wire.resetAttachmentErrors()">X</button>
|
||||
</span>
|
||||
@enderror --}}
|
||||
{{-- todo:Show progress when uploading files --}}
|
||||
{{-- <div x-show="isUploading" class="w-full">
|
||||
<progress class="w-full h-1 rounded-lg" max="100" x-bind:value="progress"></progress>
|
||||
</div> --}}
|
||||
<section
|
||||
class=" flex overflow-x-scroll ms-overflow-style-none items-center w-full col-span-12 py-2 gap-5 "
|
||||
style=" scrollbar-width: none; -ms-overflow-style: none;">
|
||||
|
||||
|
||||
{{-- Loop through media for preview --}}
|
||||
@foreach ($media as $key => $mediaItem)
|
||||
@if (str()->startsWith($mediaItem->getMimeType(), 'image/'))
|
||||
<div class="relative h-24 sm:h-36 aspect-4/3 ">
|
||||
{{-- Delete image --}}
|
||||
<button wire:loading.attr="disabled"
|
||||
class="disabled:cursor-progress absolute -top-2 -right-2 z-10 dark:text-gray-50"
|
||||
@click="removeUpload('{{ $mediaItem->getFilename() }}')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
|
||||
<path
|
||||
d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708" />
|
||||
</svg>
|
||||
</button>
|
||||
<img class="h-full w-full rounded-lg object-scale-down"
|
||||
src="{{ $mediaItem->temporaryUrl() }}" alt="mediaItem">
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Attachemnt is Video/ --}}
|
||||
@if (str()->startsWith($mediaItem->getMimeType(), 'video/'))
|
||||
<div class="relative h-24 sm:h-36 ">
|
||||
<button wire:loading.attr="disabled"
|
||||
class="disabled:cursor-progress absolute -top-2 -right-2 z-10 dark:text-gray-50"
|
||||
@click="removeUpload('{{ $mediaItem->getFilename() }}')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
|
||||
<path
|
||||
d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708" />
|
||||
</svg>
|
||||
</button>
|
||||
<x-wirechat::video height="h-24 sm:h-36 " :cover="false"
|
||||
:showToggleSound="false" :source="$mediaItem->temporaryUrl()" />
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
|
||||
<label wire:loading.class="cursor-progress"
|
||||
class="shrink-0 cursor-pointer relative w-16 h-14 rounded-lg bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-primary)] hover:bg-[var(--wc-light-primary)] dark:hover:bg-[var(--wc-dark-primary)] border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] flex text-center justify-center ">
|
||||
<input wire:loading.attr="disabled"
|
||||
@change="handleFileSelect(event,{{ count($media) }})" type="file" multiple
|
||||
accept="{{ Helper::formattedMediaMimesForAcceptAttribute() }}" class="sr-only">
|
||||
<span class="m-auto ">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="w-7 h-7 text-gray-600 dark:text-gray-100">
|
||||
<path fill-rule="evenodd"
|
||||
d="M1.5 6a2.25 2.25 0 0 1 2.25-2.25h16.5A2.25 2.25 0 0 1 22.5 6v12a2.25 2.25 0 0 1-2.25 2.25H3.75A2.25 2.25 0 0 1 1.5 18V6ZM3 16.06V18c0 .414.336.75.75.75h16.5A.75.75 0 0 0 21 18v-1.94l-2.69-2.689a1.5 1.5 0 0 0-2.12 0l-.88.879.97.97a.75.75 0 1 1-1.06 1.06l-5.16-5.159a1.5 1.5 0 0 0-2.12 0L3 16.061Zm10.125-7.81a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
</label>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
{{-- ----------------------- --}}
|
||||
{{-- Files preview section --}}
|
||||
@if (count($files) > 0)
|
||||
<section x-data="attachments('files')"
|
||||
class="flex overflow-x-scroll ms-overflow-style-none items-center w-full col-span-12 py-2 gap-5 "
|
||||
style=" scrollbar-width: none; -ms-overflow-style: none;">
|
||||
|
||||
{{-- Loop through files for preview --}}
|
||||
@foreach ($files as $key => $file)
|
||||
<div class="relative shrink-0">
|
||||
{{-- Delete file button --}}
|
||||
<button wire:loading.attr="disabled"
|
||||
class="disabled:cursor-progress absolute -top-2 -right-2 z-10"
|
||||
@click="removeUpload('{{ $file->getFilename() }}')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-x-circle dark:text-white dark:hover:text-red-500 hover:text-red-500 transition-colors"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16" />
|
||||
<path
|
||||
d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{{-- File details --}}
|
||||
<div
|
||||
class="flex items-center group overflow-hidden bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] hover:border-[var(--wc-light-primary)] dark:hover:border-[var(--wc-dark-primary)] border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] rounded-xl">
|
||||
<span class=" p-2">
|
||||
{{-- document svg:HI --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="currentColor" class="w-8 h-8 text-gray-500 dark:text-gray-100">
|
||||
<path
|
||||
d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625Z" />
|
||||
<path
|
||||
d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<p class="mt-auto p-2 text-gray-600 dark:text-gray-100 text-sm">
|
||||
{{ $file->getClientOriginalName() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
{{-- Add more files --}}
|
||||
{{-- TODO @if "( count($media)< $MAXFILES )" to hide upload button when maz files exceeded --}}
|
||||
<label wire:loading.class="cursor-progress"
|
||||
class="cursor-pointer shrink-0 relative w-16 h-14 rounded-lg bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] hover:border-[var(--wc-light-primary)] dark:hover:border-[var(--wc-dark-primary)] border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] transition-colors flex text-center justify-center ">
|
||||
<input wire:loading.attr="disabled"
|
||||
@change="handleFileSelect(event,{{ count($files) }})" type="file" multiple
|
||||
accept="{{ Helper::formattedFileMimesForAcceptAttribute() }}" class="sr-only"
|
||||
hidden>
|
||||
<span class=" m-auto">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="w-6 h-6 dark:text-gray-50">
|
||||
<path fill-rule="evenodd"
|
||||
d="M12 3.75a.75.75 0 0 1 .75.75v6.75h6.75a.75.75 0 0 1 0 1.5h-6.75v6.75a.75.75 0 0 1-1.5 0v-6.75H4.5a.75.75 0 0 1 0-1.5h6.75V4.5a.75.75 0 0 1 .75-.75Z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
|
||||
|
||||
</span>
|
||||
</label>
|
||||
|
||||
</section>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
|
||||
{{-- Replying to --}}
|
||||
@if ($replyMessage != null)
|
||||
<section class="p-px py-1 w-full col-span-12">
|
||||
<div class="flex justify-between items-center dark:text-white">
|
||||
<h6 class="text-sm">
|
||||
{{ $replyMessage?->ownedBy($this->auth) ? __('wirechat::chat.labels.replying_to_yourself'): __('wirechat::chat.labels.replying_to',['participant'=>$replyMessage->sendable?->name]) }}
|
||||
</h6>
|
||||
<button wire:loading.attr="disabled" wire:click="removeReply()"
|
||||
class="disabled:cursor-progress">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="2" stroke="currentColor" class="w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- Message being replied to --}}
|
||||
<p class="truncate text-sm text-gray-500 dark:text-gray-200 max-w-md">
|
||||
{{ $replyMessage->body != '' ? $replyMessage->body : ($replyMessage->hasAttachment() ? 'Attachment' : '') }}
|
||||
</p>
|
||||
|
||||
</section>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
<form x-data="{
|
||||
'body': $wire.entangle('body'),
|
||||
insertNewLine: function(textarea) {
|
||||
{{-- Get the current cursor position --}}
|
||||
var startPos = textarea.selectionStart;
|
||||
var endPos = textarea.selectionEnd;
|
||||
|
||||
{{-- Insert a line break character at the cursor position --}}
|
||||
var text = textarea.value;
|
||||
var newText = text.substring(0, startPos) + '\n' + text.substring(endPos, text.length);
|
||||
|
||||
{{-- Update the textarea value and cursor position --}}
|
||||
textarea.value = newText;
|
||||
textarea.selectionStart = startPos + 1; // Set cursor position after the inserted newline
|
||||
textarea.selectionEnd = startPos + 1;
|
||||
|
||||
{{-- update height of element smoothly --}}
|
||||
textarea.style.height = 'auto';
|
||||
textarea.style.height = textarea.scrollHeight + 'px';
|
||||
|
||||
}
|
||||
}" x-init="{{-- Emoji picture click event listener --}}
|
||||
document.querySelector('emoji-picker')
|
||||
.addEventListener('emoji-click', event => {
|
||||
// Get the emoji unicode from the event
|
||||
const emoji = event.detail['unicode'];
|
||||
|
||||
// Get the current value and cursor position
|
||||
const inputField = $refs.body;
|
||||
const inputFieldValue = inputField._x_model.get() ?? '';
|
||||
|
||||
const startPos = inputField.selectionStart;
|
||||
const endPos = inputField.selectionEnd;
|
||||
|
||||
// Insert the emoji at the current cursor position
|
||||
const newValue = inputFieldValue.substring(0, startPos) + emoji + inputFieldValue.substring(endPos);
|
||||
|
||||
// Update the value and move cursor after the emoji
|
||||
inputField._x_model.set(newValue);
|
||||
|
||||
|
||||
inputField.setSelectionRange(startPos + emoji.length, startPos + emoji.length);
|
||||
});"
|
||||
@submit.prevent="((body && body?.trim().length > 0) || ($wire.media && $wire.media.length > 0)|| ($wire.files && $wire.files.length > 0)) ? ($wire.sendMessage(), openEmojiPicker = false) : null"
|
||||
method="POST" autocapitalize="off" @class(['flex items-center col-span-12 w-full gap-2 gap-5'])>
|
||||
@csrf
|
||||
|
||||
<input type="hidden" autocomplete="false" style="display: none">
|
||||
|
||||
|
||||
{{-- Emoji Triggger icon --}}
|
||||
<div class="w-10 hidden sm:flex max-w-fit items-center">
|
||||
<button wire:loading.attr="disabled" type="button" dusk="emoji-trigger-button"
|
||||
@click="openEmojiPicker = ! openEmojiPicker" x-ref="emojibutton"
|
||||
class="cursor-pointer hover:scale-105 transition-transform disabled:cursor-progress rounded-full p-px dark:border-gray-700">
|
||||
<svg x-bind:style="openEmojiPicker && { color: 'var(--wc-brand-primary)' }"
|
||||
viewBox="0 0 24 24" height="24" width="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
class="w-7 h-7 text-gray-600 dark:text-gray-300 srtoke-[1.3] dark:stroke-[1.2]"
|
||||
version="1.1" x="0px" y="0px" enable-background="new 0 0 24 24">
|
||||
<title>smiley</title>
|
||||
<path fill="currentColor"
|
||||
d="M9.153,11.603c0.795,0,1.439-0.879,1.439-1.962S9.948,7.679,9.153,7.679 S7.714,8.558,7.714,9.641S8.358,11.603,9.153,11.603z M5.949,12.965c-0.026-0.307-0.131,5.218,6.063,5.551 c6.066-0.25,6.066-5.551,6.066-5.551C12,14.381,5.949,12.965,5.949,12.965z M17.312,14.073c0,0-0.669,1.959-5.051,1.959 c-3.505,0-5.388-1.164-5.607-1.959C6.654,14.073,12.566,15.128,17.312,14.073z M11.804,1.011c-6.195,0-10.826,5.022-10.826,11.217 s4.826,10.761,11.021,10.761S23.02,18.423,23.02,12.228C23.021,6.033,17.999,1.011,11.804,1.011z M12,21.354 c-5.273,0-9.381-3.886-9.381-9.159s3.942-9.548,9.215-9.548s9.548,4.275,9.548,9.548C21.381,17.467,17.273,21.354,12,21.354z M15.108,11.603c0.795,0,1.439-0.879,1.439-1.962s-0.644-1.962-1.439-1.962s-1.439,0.879-1.439,1.962S14.313,11.603,15.108,11.603z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- Show upload pop if media or file are empty --}}
|
||||
{{-- Also only show upload popup if allowed in configuration --}}
|
||||
@if (count($this->media) == 0 &&
|
||||
count($this->files) == 0 &&
|
||||
(config('wirechat.allow_file_attachments', true) || config('wirechat.allow_media_attachments', true)))
|
||||
<x-wirechat::popover position="top" popoverOffset="70">
|
||||
|
||||
<x-slot name="trigger" wire:loading.attr="disabled">
|
||||
<span dusk="upload-trigger-button">
|
||||
|
||||
{{-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-7 h-7 dark:text-white/90">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg> --}}
|
||||
{{-- <svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="16" height="16" fill="currentColor"
|
||||
class="bi bi-plus-lg w-6 h-6 text-gray-600 dark:text-white/90" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2" />
|
||||
</svg> --}}
|
||||
|
||||
{{-- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.3" stroke="currentColor" class="size-6 w-7 h-7 text-gray-600 dark:text-white/90">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
|
||||
</svg> --}}
|
||||
<svg class="size-6 w-7 h-7 text-gray-600 dark:text-white/60"
|
||||
xmlns="http://www.w3.org/2000/svg" width="36" height="36"
|
||||
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"
|
||||
stroke-linecap="round" stroke-linejoin="round" class="ai ai-Attach">
|
||||
<path
|
||||
d="M6 7.91V16a6 6 0 0 0 6 6v0a6 6 0 0 0 6-6V6a4 4 0 0 0-4-4v0a4 4 0 0 0-4 4v9.182a2 2 0 0 0 2 2v0a2 2 0 0 0 2-2V8" />
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
|
||||
</x-slot>
|
||||
|
||||
{{-- content --}}
|
||||
<div class="grid gap-2 w-full ">
|
||||
|
||||
{{-- Upload Files --}}
|
||||
@if (config('wirechat.allow_file_attachments', true))
|
||||
<label wire:loading.class="cursor-progress" x-data="attachments('files')"
|
||||
class="cursor-pointer">
|
||||
<input wire:loading.attr="disabled" wire:target="sendMessage"
|
||||
dusk="file-upload-input"
|
||||
@change="handleFileSelect(event, {{ count($files) }})" type="file"
|
||||
multiple accept="{{ Helper::formattedFileMimesForAcceptAttribute() }}"
|
||||
class="sr-only" style="display: none">
|
||||
|
||||
<div
|
||||
class="w-full flex items-center gap-3 px-1.5 py-2 rounded-md hover:bg-[var(--wc-light-primary)] dark:hover:bg-[var(--wc-dark-primary)] cursor-pointer">
|
||||
|
||||
<span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" style="color: var(--wc-brand-primary);"
|
||||
class="bi bi-folder-fill w-6 h-6" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M9.828 3h3.982a2 2 0 0 1 1.992 2.181l-.637 7A2 2 0 0 1 13.174 14H2.825a2 2 0 0 1-1.991-1.819l-.637-7a2 2 0 0 1 .342-1.31L.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3m-8.322.12q.322-.119.684-.12h5.396l-.707-.707A1 1 0 0 0 6.172 2H2.5a1 1 0 0 0-1 .981z" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<span class=" dark:text-white">
|
||||
@lang('wirechat::chat.actions.upload_file.label')
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@endif
|
||||
|
||||
|
||||
{{-- Upload Media --}}
|
||||
@if (config('wirechat.allow_media_attachments', true))
|
||||
<label wire:loading.class="cursor-progress" x-data="attachments('media')"
|
||||
class="cursor-pointer">
|
||||
|
||||
{{-- Trigger image upload --}}
|
||||
<input dusk="media-upload-input" wire:loading.attr="disabled"
|
||||
wire:target="sendMessage"
|
||||
@change="handleFileSelect(event, {{ count($media) }})" type="file"
|
||||
multiple accept="{{ Helper::formattedMediaMimesForAcceptAttribute() }}"
|
||||
class="sr-only" style="display: none">
|
||||
|
||||
<div
|
||||
class="w-full flex items-center gap-3 px-1.5 py-2 rounded-md hover:bg-[var(--wc-light-primary)] dark:hover:bg-[var(--wc-dark-primary)] cursor-pointer">
|
||||
|
||||
<span class="">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||
fill="currentColor" class="w-6 h-6"
|
||||
style="color: var(--wc-brand-primary);">
|
||||
<path fill-rule="evenodd"
|
||||
d="M1.5 6a2.25 2.25 0 0 1 2.25-2.25h16.5A2.25 2.25 0 0 1 22.5 6v12a2.25 2.25 0 0 1-2.25 2.25H3.75A2.25 2.25 0 0 1 1.5 18V6ZM3 16.06V18c0 .414.336.75.75.75h16.5A.75.75 0 0 0 21 18v-1.94l-2.69-2.689a1.5 1.5 0 0 0-2.12 0l-.88.879.97.97a.75.75 0 1 1-1.06 1.06l-5.16-5.159a1.5 1.5 0 0 0-2.12 0L3 16.061Zm10.125-7.81a1.125 1.125 0 1 1 2.25 0 1.125 1.125 0 0 1-2.25 0Z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<span class=" dark:text-white">
|
||||
@lang('wirechat::chat.actions.upload_media.label')
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
||||
</x-wirechat::popover>
|
||||
@endif
|
||||
|
||||
{{-- --------------- --}}
|
||||
{{-- TextArea Input --}}
|
||||
{{-- --------------- --}}
|
||||
|
||||
<div @class(['flex gap-2 sm:px-2 w-full'])>
|
||||
<textarea @focus-input-field.window="$el.focus()" autocomplete="off" x-model='body' x-ref="body"
|
||||
wire:loading.delay.longest.attr="disabled" wire:target="sendMessage" id="chat-input-field" autofocus
|
||||
type="text" name="message" placeholder="{{ __('wirechat::chat.inputs.message.placeholder') }}" maxlength="1700" rows="1"
|
||||
@input="$el.style.height = 'auto'; $el.style.height = $el.scrollHeight + 'px';"
|
||||
@keydown.shift.enter.prevent="insertNewLine($el)" {{-- @keydown.enter.prevent prevents the
|
||||
default behavior of Enter key press only if Shift is not held down. --}} @keydown.enter.prevent=""
|
||||
@keyup.enter.prevent="$event.shiftKey ? null : (((body && body?.trim().length > 0) || ($wire.media && $wire.media.length > 0)) ? ($wire.sendMessage(), openEmojiPicker = false) : null)"
|
||||
class="w-full disabled:cursor-progress resize-none h-auto max-h-20 sm:max-h-72 flex grow border-0 outline-0 focus:border-0 focus:ring-0 hover:ring-0 rounded-lg dark:text-white bg-none dark:bg-inherit focus:outline-hidden "
|
||||
x-init="document.querySelector('emoji-picker')
|
||||
.addEventListener('emoji-click', event => {
|
||||
const emoji = event.detail['unicode'];
|
||||
const inputField = $refs.body;
|
||||
|
||||
// Get the current cursor position (start and end)
|
||||
const startPos = inputField.selectionStart;
|
||||
const endPos = inputField.selectionEnd;
|
||||
|
||||
// Get current value of the input field
|
||||
const currentValue = inputField.value;
|
||||
|
||||
// Insert the emoji at the cursor position, preserving line breaks and spaces
|
||||
const newValue = currentValue.substring(0, startPos) + emoji + currentValue.substring(endPos);
|
||||
|
||||
// Update Alpine.js model (x-model='body') with the new value
|
||||
inputField._x_model.set(newValue);
|
||||
|
||||
// Set the cursor position after the inserted emoji
|
||||
inputField.setSelectionRange(startPos + emoji.length, startPos + emoji.length);
|
||||
|
||||
// Ensure the textarea resizes correctly after adding the emoji
|
||||
inputField.style.height = 'auto';
|
||||
inputField.style.height = inputField.scrollHeight + 'px';
|
||||
});"></textarea>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{-- --------------- --}}
|
||||
{{-- input Actions --}}
|
||||
{{-- --------------- --}}
|
||||
|
||||
<div x-cloak @class(['w-[5%] justify-end min-w-max items-center gap-2 '])>
|
||||
|
||||
{{-- Submit button --}}
|
||||
<button
|
||||
x-show="((body?.trim()?.length>0) || $wire.media.length > 0 || $wire.files.length > 0 )"
|
||||
wire:loading.attr="disabled" wire:target="sendMessage" type="submit"
|
||||
id="sendMessageButton" class="cursor-pointer hover:text-[var(--wc-brand-primary)] transition-color ml-auto disabled:cursor-progress cursor-pointer font-bold">
|
||||
|
||||
<svg class="w-7 h-7 dark:text-gray-200" xmlns="http://www.w3.org/2000/svg"
|
||||
width="36" height="36" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
|
||||
stroke-linejoin="round" class="ai ai-Send">
|
||||
<path
|
||||
d="M9.912 12H4L2.023 4.135A.662.662 0 0 1 2 3.995c-.022-.721.772-1.221 1.46-.891L22 12 3.46 20.896c-.68.327-1.464-.159-1.46-.867a.66.66 0 0 1 .033-.186L3.5 15" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
{{-- send Like button --}}
|
||||
{{-- <button
|
||||
x-show="!((body?.trim()?.length>0) || $wire.media.length > 0 || $wire.files.length > 0 )"
|
||||
wire:loading.attr="disabled" wire:target="sendMessage" wire:click='sendLike()'
|
||||
type="button" class="hover:scale-105 transition-transform cursor-pointer group disabled:cursor-progress">
|
||||
|
||||
<!-- outlined heart -->
|
||||
<span class=" group-hover:hidden transition">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
class="w-7 h-7 text-gray-600 dark:text-white/90 stroke-[1.4] dark:stroke-[1.4]">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />
|
||||
</svg>
|
||||
</span>
|
||||
<!-- filled heart -->
|
||||
<span class="hidden group-hover:block transition " x-bounce>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="size-6 w-7 h-7 text-red-500">
|
||||
<path
|
||||
d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
</button> --}}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
@script
|
||||
<script>
|
||||
Alpine.data('attachments', (type = "media") => ({
|
||||
// State variables
|
||||
isDropping: false, // Tracks if a file is being dragged over the drop area
|
||||
type: type, // Type of file being uploaded (e.g., "media" or "file")
|
||||
isUploading: false, // Indicates if files are currently uploading
|
||||
MAXFILES: @json(config('wirechat.attachments.max_uploads', 5)), // Maximum number of files allowed
|
||||
maxSize: @json(config('wirechat.attachments.media_max_upload_size', 12288)) * 1024, // Max size per file (in bytes)
|
||||
allowedFileTypes: type === 'media' ? @json(config('wirechat.attachments.media_mimes')) :
|
||||
@json(config('wirechat.attachments.file_mimes')), // Allowed MIME types based on type
|
||||
progress: 0, // Progress of the current upload (0-100)
|
||||
wireModel: type, // The Livewire model to bind to
|
||||
|
||||
// Handle file selection from the input field
|
||||
handleFileSelect(event, count) {
|
||||
if (event.target.files.length) {
|
||||
const files = event.target.files;
|
||||
|
||||
// Validate selected files and upload if valid
|
||||
this.validateFiles(files, count)
|
||||
.then((validFiles) => {
|
||||
if (validFiles.length > 0) {
|
||||
this.uploadFiles(validFiles);
|
||||
} else {
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Upload files using Livewire's upload
|
||||
uploadFiles(files) {
|
||||
this.isUploading = true;
|
||||
this.progress = 0;
|
||||
|
||||
// Initialize per-file progress tracking
|
||||
const fileProgress = Array.from(files).map(() => 0);
|
||||
files.forEach((file, index) => {
|
||||
$wire.upload(
|
||||
`${this.wireModel}`, // Livewire model
|
||||
file, // Single file
|
||||
() => {
|
||||
fileProgress[index] = 100; // Mark this file as complete
|
||||
// this.isUploading = false;
|
||||
this.progress = Math.round((fileProgress.reduce((a, b) => a + b, 0)) / files.length);
|
||||
},
|
||||
(error) => {
|
||||
// this.isUploading = false;
|
||||
fileProgress[index] = -1; // Mark as failed
|
||||
$dispatch('wirechat-toast', { type: 'error', message: `Validation error: ${error}` });
|
||||
},
|
||||
(event) => {
|
||||
fileProgress[index] = event.detail.progress; // Update per-file progress
|
||||
this.progress = Math.round((fileProgress.reduce((a, b) => a + b, 0)) / files.length); // Overall progress
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
// Upload files using Livewire's uploadMultiple method
|
||||
|
||||
// Remove an uploaded file from Livewire
|
||||
removeUpload(filename) {
|
||||
$wire.removeUpload(this.wireModel, filename);
|
||||
},
|
||||
|
||||
// Validate selected files against constraints
|
||||
validateFiles(files, count) {
|
||||
const totalFiles = count + files.length; // Total file count including existing uploads
|
||||
|
||||
// Check if total file count exceeds the maximum allowed
|
||||
if (totalFiles > this.MAXFILES) {
|
||||
files = Array.from(files).slice(0, this.MAXFILES -
|
||||
count); // Limit files to the allowed number
|
||||
$dispatch('wirechat-toast', {
|
||||
type: 'warning',
|
||||
message: @js(__('wirechat::validation.max.array', ['attribute' => __('wirechat::chat.inputs.media.label'),'max'=>config('wirechat.attachments.max_uploads', 5)]))
|
||||
});
|
||||
}
|
||||
|
||||
// Filter invalid files based on size and type
|
||||
const invalidFiles = Array.from(files).filter((file) => {
|
||||
const fileType = file.type.split('/')[1].toLowerCase(); // Extract file extension
|
||||
return file.size > this.maxSize || !this.allowedFileTypes.includes(
|
||||
fileType); // Check size and type
|
||||
});
|
||||
|
||||
// Filter valid files
|
||||
const validFiles = Array.from(files).filter((file) => {
|
||||
const fileType = file.type.split('/')[1].toLowerCase();
|
||||
return file.size <= this.maxSize && this.allowedFileTypes.includes(fileType);
|
||||
});
|
||||
|
||||
// Handle invalid files by showing appropriate error messages
|
||||
if (invalidFiles.length > 0) {
|
||||
invalidFiles.forEach((file) => {
|
||||
if (file.size > this.maxSize) {
|
||||
$dispatch('wirechat-toast', {
|
||||
type: 'warning',
|
||||
message: @js(__('wirechat::validation.max.file', ['attribute' => __('wirechat::chat.inputs.media.label'),'max'=>config('wirechat.attachments.media_max_upload_size', 12288)]))
|
||||
// message: `File size exceeds the maximum limit (${this.maxSize / 1024 / 1024}MB): ${file.name}`
|
||||
});
|
||||
} else {
|
||||
const extension = file.name.split('.').pop().toLowerCase();
|
||||
$dispatch('wirechat-toast', {
|
||||
type: 'warning',
|
||||
message: @js(__('wirechat::validation.mimes', [ 'attribute' => __('wirechat::chat.inputs.media.label'), 'values' => implode(', ', config('wirechat.attachments.media_mimes')) ]))
|
||||
// message: `One or more Files not uploaded: .${extension} (type not allowed)`
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(validFiles); // Return valid files for further processing
|
||||
}
|
||||
}));
|
||||
</script>
|
||||
@endscript
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
</footer>
|
||||
179
resources/views/vendor/wirechat/livewire/chat/partials/header.blade.php
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
|
||||
@php
|
||||
$group = $conversation->group;
|
||||
@endphp
|
||||
|
||||
<header
|
||||
class="w-full sticky inset-x-0 flex pb-[5px] pt-[7px] top-0 z-10 dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] border-[var(--wc-light-primary)] dark:border-[var(--wc-dark-secondary)] border-b">
|
||||
|
||||
<div class=" flex w-full items-center px-3 py-3 lg:px-4 gap-2 md:gap-5 ">
|
||||
|
||||
{{-- Return --}}
|
||||
<a @if ($this->isWidget()) @click="$dispatch('close-chat',{conversation: {{json_encode($conversation->id)}} })"
|
||||
dusk="return_to_home_button_dispatch"
|
||||
@else
|
||||
href="{{ route(WireChat::indexRouteName(), $conversation->id) }}"
|
||||
dusk="return_to_home_button_link" @endif
|
||||
@class([
|
||||
'shrink-0 cursor-pointer dark:text-white',
|
||||
'lg:hidden' => !$this->isWidget(),
|
||||
]) id="chatReturn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.6"
|
||||
stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
{{-- Receiver wirechat::Avatar --}}
|
||||
<section class="grid grid-cols-12 w-full">
|
||||
<div class="shrink-0 col-span-11 w-full overflow-h-hidden relative">
|
||||
|
||||
{{-- Group --}}
|
||||
@if ($conversation->isGroup())
|
||||
<x-wirechat::actions.show-group-info conversation="{{ $conversation->id }}"
|
||||
widget="{{ $this->isWidget() }}">
|
||||
<div class="flex items-center gap-2 cursor-pointer w-full">
|
||||
<x-wirechat::avatar :group="true" :src="$group?->cover_url ?? null "
|
||||
class="h-8 w-8 lg:w-10 lg:h-10 " />
|
||||
<h6 class="font-bold text-base text-gray-800 dark:text-white truncate">
|
||||
{{ $group?->name }}
|
||||
</h6>
|
||||
@if(timebank_config('wirechat.disappearing_messages.enabled', true))
|
||||
@php
|
||||
$durationInDays = timebank_config('wirechat.disappearing_messages.duration', 30);
|
||||
@endphp
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap mx-2">
|
||||
{{ __('Messages deleted after :days days', ['days' => $durationInDays]) }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</x-wirechat::actions.show-group-info>
|
||||
@else
|
||||
{{-- Not Group --}}
|
||||
<x-wirechat::actions.show-chat-info conversation="{{ $conversation->id }}"
|
||||
widget="{{ $this->isWidget() }}">
|
||||
<div class="flex items-center gap-2 cursor-pointer w-full">
|
||||
<x-wirechat::avatar :group="false" :src="$receiver?->cover_url ?? null"
|
||||
class="h-8 w-8 lg:w-10 lg:h-10 " />
|
||||
<div class="flex flex-col min-w-0">
|
||||
<h6 class="font-bold text-base text-gray-800 dark:text-white truncate">
|
||||
{{ $receiver?->display_name }} @if ($conversation->isSelfConversation())
|
||||
({{ __('wirechat::chat.labels.you') }})
|
||||
@endif
|
||||
</h6>
|
||||
<livewire:profile-status-badge
|
||||
:profileId="$receiver?->id"
|
||||
:guard="$receiver instanceof \App\Models\Organization ? 'organization' : ($receiver instanceof \App\Models\Bank ? 'bank' : ($receiver instanceof \App\Models\Admin ? 'admin' : 'web'))"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@if(timebank_config('wirechat.disappearing_messages.enabled', true))
|
||||
@php
|
||||
$durationInDays = timebank_config('wirechat.disappearing_messages.duration', 30);
|
||||
@endphp
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 whitespace-normal mt-1 mx-auto">
|
||||
{{ __('messages.wirechat.messages_deleted_after', ['days' => $durationInDays]) }}
|
||||
{{ __('Click on message actions (three dots) to keep.')}}
|
||||
</div>
|
||||
@endif
|
||||
</x-wirechat::actions.show-chat-info>
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Header Actions --}}
|
||||
<div class="flex gap-2 items-center ml-auto col-span-1">
|
||||
<x-wirechat::dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="cursor-pointer inline-flex px-0 text-gray-700 dark:text-gray-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.9" stroke="currentColor" class="size-6 w-7 h-7">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
</x-slot>
|
||||
<x-slot name="content">
|
||||
|
||||
|
||||
@if ($conversation->isGroup())
|
||||
{{-- Open group info button --}}
|
||||
<x-wirechat::actions.show-group-info conversation="{{ $conversation->id }}"
|
||||
widget="{{ $this->isWidget() }}">
|
||||
<button class="w-full text-start">
|
||||
<x-wirechat::dropdown-link>
|
||||
{{ __('wirechat::chat.actions.open_group_info.label') }}
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
</x-wirechat::actions.show-group-info>
|
||||
@else
|
||||
{{-- Open chat info button --}}
|
||||
<x-wirechat::actions.show-chat-info conversation="{{ $conversation->id }}"
|
||||
widget="{{ $this->isWidget() }}">
|
||||
<button class="w-full text-start">
|
||||
<x-wirechat::dropdown-link>
|
||||
{{ __('wirechat::chat.actions.open_chat_info.label') }}
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
</x-wirechat::actions.show-chat-info>
|
||||
@endif
|
||||
|
||||
|
||||
@if ($this->isWidget())
|
||||
<x-wirechat::dropdown-link @click="$dispatch('close-chat',{conversation: {{json_encode($conversation->id)}} })">
|
||||
@lang('wirechat::chat.actions.close_chat.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
@else
|
||||
<x-wirechat::dropdown-link href="{{ route(WireChat::indexRouteName()) }}" class="shrink-0">
|
||||
@lang('wirechat::chat.actions.close_chat.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
@endif
|
||||
|
||||
|
||||
{{-- Only show delete and clear if conversation is NOT group --}}
|
||||
@if (!$conversation->isGroup())
|
||||
<button class="w-full" wire:click="clearConversation"
|
||||
wire:confirm="{{ __('wirechat::chat.actions.clear_chat.confirmation_message') }}">
|
||||
|
||||
<x-wirechat::dropdown-link>
|
||||
@lang('wirechat::chat.actions.clear_chat.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
</button>
|
||||
|
||||
<button wire:click="deleteConversation"
|
||||
wire:confirm="{{ __('wirechat::chat.actions.delete_chat.confirmation_message') }}"
|
||||
class="w-full text-start">
|
||||
|
||||
<x-wirechat::dropdown-link class="text-red-500 dark:text-red-500">
|
||||
@lang('wirechat::chat.actions.delete_chat.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
|
||||
</button>
|
||||
@endif
|
||||
|
||||
|
||||
@if ($conversation->isGroup() && !$this->auth->isOwnerOf($conversation))
|
||||
<button wire:click="exitConversation"
|
||||
wire:confirm="{{ __('wirechat::chat.actions.exit_group.confirmation_message') }}"
|
||||
class="w-full text-start ">
|
||||
|
||||
<x-wirechat::dropdown-link class="text-red-500 dark:text-gray-500">
|
||||
@lang('wirechat::chat.actions.exit_group.label')
|
||||
</x-wirechat::dropdown-link>
|
||||
|
||||
</button>
|
||||
@endif
|
||||
|
||||
</x-slot>
|
||||
</x-wirechat::dropdown>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</header>
|
||||
43
resources/views/vendor/wirechat/livewire/chat/partials/image.blade.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
|
||||
@php
|
||||
|
||||
$isSameAsNext = ($message?->sendable_id === $nextMessage?->sendable_id) && ($message?->sendable_type === $nextMessage?->sendable_type);
|
||||
$isNotSameAsNext = !$isSameAsNext;
|
||||
$isSameAsPrevious = ($message?->sendable_id === $previousMessage?->sendable_id) && ($message?->sendable_type === $previousMessage?->sendable_type);
|
||||
$isNotSameAsPrevious = !$isSameAsPrevious;
|
||||
@endphp
|
||||
|
||||
|
||||
|
||||
<img @class([
|
||||
|
||||
'max-w-max h-[200px] min-h-[210px] bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] object-scale-down grow-0 shrink overflow-hidden rounded-3xl',
|
||||
|
||||
'rounded-br-md rounded-tr-2xl' => ($isSameAsNext && $isNotSameAsPrevious && $belongsToAuth),
|
||||
|
||||
// Middle message on RIGHT
|
||||
'rounded-r-md' => ($isSameAsPrevious && $belongsToAuth),
|
||||
|
||||
// Standalone message RIGHT
|
||||
'rounded-br-xl rounded-r-xl' => ($isNotSameAsPrevious && $isNotSameAsNext && $belongsToAuth),
|
||||
|
||||
// Last Message on RIGHT
|
||||
'rounded-br-2xl' => ($isNotSameAsNext && $belongsToAuth),
|
||||
|
||||
// LEFT
|
||||
// First message on LEFT
|
||||
'rounded-bl-md rounded-tl-2xl' => ($isSameAsNext && $isNotSameAsPrevious && !$belongsToAuth),
|
||||
|
||||
// Middle message on LEFT
|
||||
'rounded-l-md' => ($isSameAsPrevious && !$belongsToAuth),
|
||||
|
||||
// Standalone message LEFT
|
||||
'rounded-bl-xl rounded-l-xl' => ($isNotSameAsPrevious && $isNotSameAsNext && !$belongsToAuth),
|
||||
|
||||
// Last message on LEFT
|
||||
'rounded-bl-2xl' => ($isNotSameAsNext && !$belongsToAuth),
|
||||
])
|
||||
|
||||
loading="lazy" src="{{$attachment?->url}}" alt="{{ __('wirechat::chat.labels.attachment') }}">
|
||||
114
resources/views/vendor/wirechat/livewire/chat/partials/message.blade.php
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
|
||||
|
||||
@php
|
||||
$isSameAsNext = ($message?->sendable_id === $nextMessage?->sendable_id) && ($message?->sendable_type === $nextMessage?->sendable_type);
|
||||
$isNotSameAsNext = !$isSameAsNext;
|
||||
$isSameAsPrevious = ($message?->sendable_id === $previousMessage?->sendable_id) && ($message?->sendable_type === $previousMessage?->sendable_type);
|
||||
$isNotSameAsPrevious = !$isSameAsPrevious;
|
||||
@endphp
|
||||
|
||||
<div
|
||||
|
||||
|
||||
{{-- We use style here to make it easy for dynamic and safe injection --}}
|
||||
@style([
|
||||
'background-color:var(--wc-brand-primary)' => $belongsToAuth==true
|
||||
])
|
||||
|
||||
@class([
|
||||
'flex flex-wrap max-w-fit text-[15px] border border-gray-200/40 dark:border-none rounded-xl p-2.5 flex flex-col text-black bg-[#f6f6f8fb]',
|
||||
'text-white' => $belongsToAuth, // Background color for messages sent by the authenticated user
|
||||
'bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] dark:text-white' => !$belongsToAuth,
|
||||
|
||||
// Message styles based on position and ownership
|
||||
|
||||
// RIGHT
|
||||
// First message on RIGHT
|
||||
'rounded-br-md rounded-tr-2xl' => ($isSameAsNext && $isNotSameAsPrevious && $belongsToAuth),
|
||||
|
||||
// Middle message on RIGHT
|
||||
'rounded-r-md' => ($isSameAsPrevious && $belongsToAuth),
|
||||
|
||||
// Standalone message RIGHT
|
||||
'rounded-br-xl rounded-r-xl' => ($isNotSameAsPrevious && $isNotSameAsNext && $belongsToAuth),
|
||||
|
||||
// Last Message on RIGHT
|
||||
'rounded-br-2xl' => ($isNotSameAsNext && $belongsToAuth),
|
||||
|
||||
// LEFT
|
||||
// First message on LEFT
|
||||
'rounded-bl-md rounded-tl-2xl' => ($isSameAsNext && $isNotSameAsPrevious && !$belongsToAuth),
|
||||
|
||||
// Middle message on LEFT
|
||||
'rounded-l-md' => ($isSameAsPrevious && !$belongsToAuth),
|
||||
|
||||
// Standalone message LEFT
|
||||
'rounded-bl-xl rounded-l-xl' => ($isNotSameAsPrevious && $isNotSameAsNext && !$belongsToAuth),
|
||||
|
||||
// Last message on LEFT
|
||||
'rounded-bl-2xl' => ($isNotSameAsNext && !$belongsToAuth),
|
||||
])
|
||||
>
|
||||
@if (!$belongsToAuth && $isGroup)
|
||||
<div
|
||||
@class([
|
||||
'shrink-0 font-medium text-black',
|
||||
// Hide avatar if the next message is from the same user
|
||||
'hidden' => $isSameAsPrevious
|
||||
])>
|
||||
{{ $message?->sendable?->display_name }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
// Check if the body is a valid internal URL so a href link can be rendered
|
||||
$appUrl = rtrim(config('app.url'), '/');
|
||||
$body = trim($message?->body ?? '');
|
||||
$isInternalUrl = false;
|
||||
if (filter_var($body, FILTER_VALIDATE_URL)) {
|
||||
$isInternalUrl = str_starts_with($body, $appUrl);
|
||||
}
|
||||
|
||||
// Check if body is a transaction statement URL
|
||||
$transactionShowPattern = '#^' . preg_quote($appUrl, '#') . '/[a-z]{2}/statement/\d+$#';
|
||||
$isTransactionLink = preg_match($transactionShowPattern, $body);
|
||||
@endphp
|
||||
@if ($isTransactionLink)
|
||||
<pre class="whitespace-pre-line tracking-normal text-sm border pt-3 border-white rounded-lg md:text-base dark:text-white lg:tracking-normal"
|
||||
style="font-family: inherit;">
|
||||
<a href="{{ $body }}" class="underline">
|
||||
<div class="flex flex-col items-center gap-0 w-fit max-h-fit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-8">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0 1 3.75 9.375v-4.5ZM3.75 14.625c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5a1.125 1.125 0 0 1-1.125-1.125v-4.5ZM13.5 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0 1 13.5 9.375v-4.5Z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 6.75h.75v.75h-.75v-.75ZM6.75 16.5h.75v.75h-.75v-.75ZM16.5 6.75h.75v.75h-.75v-.75ZM13.5 13.5h.75v.75h-.75v-.75ZM13.5 19.5h.75v.75h-.75v-.75ZM19.5 13.5h.75v.75h-.75v-.75ZM19.5 19.5h.75v.75h-.75v-.75ZM16.5 16.5h.75v.75h-.75v-.75Z" />
|
||||
</svg>
|
||||
<span class="text-center mx-6">
|
||||
{{ __('View transaction') }}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</pre>
|
||||
@else
|
||||
<pre class="whitespace-pre-line tracking-normal text-sm md:text-base dark:text-white lg:tracking-normal"
|
||||
style="font-family: inherit;">
|
||||
@if ($isInternalUrl)
|
||||
<a href="{{ $body }}" class="underline">
|
||||
{{ $body }}
|
||||
</a>
|
||||
@else
|
||||
{{ $body }}
|
||||
@endif
|
||||
</pre>
|
||||
@endif
|
||||
|
||||
{{-- Display the created time based on different conditions --}}
|
||||
<span
|
||||
@class(['text-[11px] ml-auto ', 'text-gray-700 dark:text-gray-300' => !$belongsToAuth,'text-gray-100' => $belongsToAuth])>
|
||||
@php
|
||||
// If the message was created today, show only the time (e.g., 1:00 AM)
|
||||
echo $message?->created_at->format('H:i');
|
||||
@endphp
|
||||
</span>
|
||||
|
||||
</div>
|
||||
57
resources/views/vendor/wirechat/livewire/chats/chats.blade.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
<div x-data="{ selectedConversationId: '{{ request()->conversation ?? $selectedConversationId }}' }"
|
||||
x-on:open-chat.window="selectedConversationId= $event.detail.conversation; $wire.selectedConversationId= $event.detail.conversation;"
|
||||
x-init=" setTimeout(() => {
|
||||
conversationElement = document.getElementById('conversation-' + selectedConversationId);
|
||||
|
||||
// Scroll to the conversation element
|
||||
if (conversationElement) {
|
||||
conversationElement.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}, 200);"
|
||||
class="flex flex-col bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] transition-all h-full overflow-hidden w-full sm:p-3">
|
||||
|
||||
@php
|
||||
/* Show header if any of these conditions are true */
|
||||
$showHeader = $showNewChatModalButton || $allowChatsSearch || $showHomeRouteButton || !empty($title);
|
||||
@endphp
|
||||
|
||||
{{-- include header --}}
|
||||
@includeWhen($showHeader, 'wirechat::livewire.chats.partials.header')
|
||||
|
||||
<main x-data
|
||||
@scroll.self.debounce="
|
||||
{{-- Detect when scrolled to the bottom --}}
|
||||
// Calculate scroll values
|
||||
scrollTop = $el.scrollTop;
|
||||
scrollHeight = $el.scrollHeight;
|
||||
clientHeight = $el.clientHeight;
|
||||
|
||||
// Check if the user is at the bottom of the scrollable element
|
||||
if ((scrollTop + clientHeight) >= (scrollHeight - 1) && $wire.canLoadMore) {
|
||||
// Trigger load more if we're at the bottom
|
||||
await $nextTick();
|
||||
$wire.loadMore();
|
||||
}
|
||||
"
|
||||
class=" overflow-y-auto py-2 grow h-full relative " style="contain:content">
|
||||
|
||||
{{-- loading indicator --}}
|
||||
|
||||
@if (count($conversations) > 0)
|
||||
{{-- include list item --}}
|
||||
@include('wirechat::livewire.chats.partials.list')
|
||||
|
||||
|
||||
{{-- include load more if true --}}
|
||||
@includeWhen($canLoadMore, 'wirechat::livewire.chats.partials.load-more-button')
|
||||
@else
|
||||
<div class="w-full flex items-center h-full justify-center">
|
||||
<h6 class=" font-bold text-gray-700 dark:text-white">{{ __('wirechat::chats.labels.no_conversations_yet') }}</h6>
|
||||
</div>
|
||||
@endif
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
71
resources/views/vendor/wirechat/livewire/chats/partials/header.blade.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
|
||||
<header class="px-3 z-10 sticky top-0 w-full py-2 " dusk="header">
|
||||
|
||||
|
||||
{{-- Title/name and Icon --}}
|
||||
<section class=" justify-between flex items-center pb-2">
|
||||
|
||||
@if (isset($title))
|
||||
<div class="flex items-center gap-2 truncate " wire:ignore>
|
||||
<h2 class=" text-2xl font-bold dark:text-white" dusk="title">{{$title}}</h2>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
<div class="flex gap-x-3 items-center ">
|
||||
|
||||
@if ($showNewChatModalButton)
|
||||
|
||||
<x-wirechat::actions.new-chat widget="{{$this->isWidget()}}">
|
||||
<button id="open-new-chat-modal-button" class=" flex items-center gap-2 focus:outline-hidden">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-300">{{ __('wirechat::new.chat.labels.heading') }}</span>
|
||||
<svg class="w-8 h-8 -mb-1 text-gray-900 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 dark:text-gray-300"
|
||||
xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||
<g fill="none" stroke="currentColor">
|
||||
<path
|
||||
d="M12.875 5C9.225 5 7.4 5 6.242 6.103a4 4 0 0 0-.139.139C5 7.4 5 9.225 5 12.875V17c0 .943 0 1.414.293 1.707S6.057 19 7 19h4.125c3.65 0 5.475 0 6.633-1.103a4 4 0 0 0 .139-.139C19 16.6 19 14.775 19 11.125" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 10h6m-6 4h3m7-6V2m-3 3h6" />
|
||||
</g>
|
||||
</svg>
|
||||
{{-- <svg class="w-7 h-7 -mb-1 stroke-[0.1] stroke-none text-gray-500 hover:text-gray-900 dark:hover:text-gray-200 dark:text-gray-300" stroke="currentColor" viewBox="0 0 24 24" height="24" width="24" preserveAspectRatio="xMidYMid meet" fill="none"> <path d="M9.53277 12.9911H11.5086V14.9671C11.5086 15.3999 11.7634 15.8175 12.1762 15.9488C12.8608 16.1661 13.4909 15.6613 13.4909 15.009V12.9911H15.4672C15.9005 12.9911 16.3181 12.7358 16.449 12.3226C16.6659 11.6381 16.1606 11.0089 15.5086 11.0089H13.4909V9.03332C13.4909 8.60007 13.2361 8.18252 12.8233 8.05119C12.1391 7.83391 11.5086 8.33872 11.5086 8.991V11.0089H9.49088C8.83941 11.0089 8.33411 11.6381 8.55097 12.3226C8.68144 12.7358 9.09947 12.9911 9.53277 12.9911Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M0.944298 5.52617L2.99998 8.84848V17.3333C2.99998 18.8061 4.19389 20 5.66665 20H19.3333C20.8061 20 22 18.8061 22 17.3333V6.66667C22 5.19391 20.8061 4 19.3333 4H1.79468C1.01126 4 0.532088 4.85997 0.944298 5.52617ZM4.99998 8.27977V17.3333C4.99998 17.7015 5.29845 18 5.66665 18H19.3333C19.7015 18 20 17.7015 20 17.3333V6.66667C20 6.29848 19.7015 6 19.3333 6H3.58937L4.99998 8.27977Z" fill="currentColor" stroke="currentColor"></path></svg> --}}
|
||||
{{-- <svg class="w-7 h-7 -mb-1 text-gray-500 hover:text-gray-900 dark:hover:text-gray-200 dark:text-gray-300" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" class="ai ai-ChatAdd"><path d="M12 8v3m0 0v3m0-3h3m-3 0H9"/><path d="M14 19c3.771 0 5.657 0 6.828-1.172C22 16.657 22 14.771 22 11c0-3.771 0-5.657-1.172-6.828C19.657 3 17.771 3 14 3h-4C6.229 3 4.343 3 3.172 4.172 2 5.343 2 7.229 2 11c0 3.771 0 5.657 1.172 6.828.653.654 1.528.943 2.828 1.07"/><path d="M14 19c-1.236 0-2.598.5-3.841 1.145-1.998 1.037-2.997 1.556-3.489 1.225-.492-.33-.399-1.355-.212-3.404L6.5 17.5"/></svg> --}}
|
||||
</button>
|
||||
</x-wirechat::actions.new-chat>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
{{-- Search input --}}
|
||||
@if ($allowChatsSearch)
|
||||
<section class="mt-4">
|
||||
<div class="px-2 rounded-lg dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] grid grid-cols-12 items-center">
|
||||
|
||||
<label for="chats-search-field" class="col-span-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="size-5 w-5 h-5 dark:text-gray-300">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
||||
</svg>
|
||||
</label>
|
||||
|
||||
<input id="chats-search-field"
|
||||
name="chats_search"
|
||||
maxlength="100"
|
||||
type="search"
|
||||
wire:model.live.debounce='search'
|
||||
placeholder="{{ __('wirechat::chats.inputs.search.placeholder') }}"
|
||||
autocomplete="off"
|
||||
@click.stop
|
||||
class="col-span-11 border-0 bg-inherit dark:text-white outline-hidden w-full focus:outline-hidden focus:ring-0 hover:ring-0">
|
||||
</div>
|
||||
|
||||
</section>
|
||||
@endif
|
||||
|
||||
</header>
|
||||
107
resources/views/vendor/wirechat/livewire/chats/partials/list.blade.php
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
|
||||
<ul wire:loading.delay.long.remove wire:target="search" class="p-2 grid w-full spacey-y-2">
|
||||
@foreach ($conversations as $key=> $conversation)
|
||||
@php
|
||||
//$receiver =$conversation->getReceiver();
|
||||
$group = $conversation->isGroup() ? $conversation->group : null;
|
||||
$receiver = $conversation->isGroup() ? null : ($conversation->isPrivate() ? $conversation->peer_participant?->participantable : $this->auth);
|
||||
//$receiver = $conversation->isGroup() ? null : ($conversation->isPrivate() ? $conversation->peerParticipant()?->participantable : $this->auth);
|
||||
$lastMessage = $conversation->lastMessage;
|
||||
//mark isReadByAuth true if user has chat opened
|
||||
$isReadByAuth = $conversation?->readBy($conversation->auth_participant??$this->auth) || $selectedConversationId == $conversation->id;
|
||||
$belongsToAuth = $lastMessage?->belongsToAuth();
|
||||
|
||||
@endphp
|
||||
|
||||
<li x-data="{
|
||||
conversationID: @js($conversation->id),
|
||||
showUnreadStatus: @js(!$isReadByAuth),
|
||||
handleChatOpened(event) {
|
||||
// Hide unread dot
|
||||
if (event.detail.conversation== this.conversationID) {
|
||||
this.showUnreadStatus= false;
|
||||
}
|
||||
//update this so that the the selected conversation highlighter can be updated
|
||||
$wire.selectedConversationId= event.detail.conversation;
|
||||
},
|
||||
handleChatClosed(event) {
|
||||
// Clear the globally selected conversation.
|
||||
$wire.selectedConversationId = null;
|
||||
selectedConversationId = null;
|
||||
},
|
||||
handleOpenChat(event) {
|
||||
// Clear the globally selected conversation.
|
||||
if (this.showUnreadStatus== event.detail.conversation== this.conversationID) {
|
||||
this.showUnreadStatus= false;
|
||||
}
|
||||
}
|
||||
}"
|
||||
|
||||
id="conversation-{{ $conversation->id }}"
|
||||
wire:key="conversation-em-{{ $conversation->id }}-{{ $conversation->updated_at->timestamp }}"
|
||||
x-on:chat-opened.window="handleChatOpened($event)"
|
||||
x-on:chat-closed.window="handleChatClosed($event)">
|
||||
<a @if ($widget) tabindex="0"
|
||||
role="button"
|
||||
dusk="openChatWidgetButton"
|
||||
@click="$dispatch('open-chat',{conversation:@js($conversation->id)})"
|
||||
@keydown.enter="$dispatch('open-chat',{conversation:@js($conversation->id)})"
|
||||
@else
|
||||
wire:navigate href="{{ route(WireChat::viewRouteName(), $conversation->id) }}" @endif
|
||||
@style(['border-color:var(--wc-brand-primary)' => $selectedConversationId == $conversation?->id])
|
||||
class="py-3 flex gap-4 dark:hover:bg-[var(--wc-dark-secondary)] hover:bg-[var(--wc-light-secondary)] rounded-xs transition-colors duration-150 relative w-full cursor-pointer px-2"
|
||||
:class="$wire.selectedConversationId == conversationID &&
|
||||
'rounded-md dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] border-[var(--wc-brand-primary)]'">
|
||||
|
||||
<div class="shrink-0">
|
||||
<x-wirechat::avatar group="{{ $conversation->isGroup() }}"
|
||||
:src="$group ? $group?->cover_url : $receiver?->cover_url ?? null" class="w-12 h-12" />
|
||||
</div>
|
||||
|
||||
<aside class="grid grid-cols-12 w-full">
|
||||
<div
|
||||
class="col-span-10 pb-2 relative overflow-hidden truncate leading-5 w-full flex-nowrap p-1">
|
||||
|
||||
{{-- name --}}
|
||||
<div class="flex gap-1 mb-1 w-full items-center">
|
||||
<h6 class="truncate font-medium text-gray-900 dark:text-white">
|
||||
{{ $group ? $group?->name : $receiver?->display_name }}
|
||||
</h6>
|
||||
|
||||
@if ($conversation->isSelfConversation())
|
||||
<span class="font-medium dark:text-white">({{__('wirechat::chats.labels.you') }})</span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Message body --}}
|
||||
@if ($lastMessage != null)
|
||||
@include('wirechat::livewire.chats.partials.message-body')
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Read status --}}
|
||||
{{-- Only show if AUTH is NOT onwer of message --}}
|
||||
@if ($lastMessage != null && !$lastMessage?->ownedBy($this->auth) && !$isReadByAuth)
|
||||
<div x-show="showUnreadStatus" dusk="unreadMessagesDot" class=" col-span-2 flex flex-col text-center my-auto">
|
||||
{{-- Dots icon --}}
|
||||
<span dusk="unreadDotItem" class="sr-only">unread dot</span>
|
||||
{{-- 1. Remove the @style directive and change the class --}}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="#b91c1c" class="bi bi-dot w-10 h-10 text-gray-700" viewBox="0 0 16 16">
|
||||
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</aside>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
</ul>
|
||||
11
resources/views/vendor/wirechat/livewire/chats/partials/load-more-button.blade.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<section wire:loading.remove wire:target="search" class="w-full justify-center flex my-3 ">
|
||||
<button wire:loading.remove wire:target="loadMore" wire:loading.attr="disabled"
|
||||
dusk="loadMoreButton" @click="$wire.loadMore()"
|
||||
class=" text-sm dark:text-white disabled:hover:cursor-not-allowed hover:text-gray-700 transition-colors dark:hover:text-gray-500 dark:gray-200">
|
||||
@lang('wirechat::chats.labels.load_more')
|
||||
</button>
|
||||
|
||||
<div wire:loading wire:target="loadMore">
|
||||
<x-wirechat::loading-spin />
|
||||
</div>
|
||||
</section>
|
||||
5
resources/views/vendor/wirechat/livewire/chats/partials/loading-indicator.blade.php
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
<div x-cloak wire:loading.delay.class.remove="hidden"
|
||||
wire:target="search"class="hidden transition-all duration-300 ">
|
||||
<x-wirechat::loading-spin />
|
||||
</div>
|
||||
35
resources/views/vendor/wirechat/livewire/chats/partials/message-body.blade.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<div class="flex gap-x-2 items-center">
|
||||
|
||||
{{-- Only show if AUTH is onwer of message --}}
|
||||
@if ($lastMessage?->ownedBy($this->auth))
|
||||
<span class="font-bold text-xs dark:text-white/90 dark:font-normal">
|
||||
@lang('wirechat::chats.labels.you'):
|
||||
</span>
|
||||
@elseif(!$lastMessage?->ownedBy($this->auth) && $group !== null)
|
||||
<span class="font-bold text-xs dark:text-white/80 dark:font-normal">
|
||||
{{ $lastMessage->sendable?->display_name }}:
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<p @class([
|
||||
'truncate text-sm dark:text-white gap-2 items-center',
|
||||
'font-semibold text-black' =>
|
||||
!$isReadByAuth && !$lastMessage?->ownedBy($this->auth),
|
||||
'font-normal text-gray-600' =>
|
||||
$isReadByAuth && !$lastMessage?->ownedBy($this->auth),
|
||||
'font-normal text-gray-600' =>
|
||||
$isReadByAuth && $lastMessage?->ownedBy($this->auth),
|
||||
])>
|
||||
{{ $lastMessage->body != '' ? $lastMessage->body : ($lastMessage->isAttachment() ? '📎 '.__('wirechat::chats.labels.attachment') : '') }}
|
||||
</p>
|
||||
|
||||
<span class="font-medium px-1 text-xs shrink-0 text-gray-800 dark:text-gray-50">
|
||||
@if ($lastMessage->created_at->diffInMinutes(now()) < 1)
|
||||
@lang('wirechat::chats.labels.now')
|
||||
@else
|
||||
{{ $lastMessage->created_at->shortAbsoluteDiffForHumans() }}
|
||||
@endif
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
206
resources/views/vendor/wirechat/livewire/modals/modal.blade.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<div>
|
||||
|
||||
<script>
|
||||
window.WireChatModal = () => {
|
||||
return {
|
||||
show: false,
|
||||
showActiveModalComponent: true,
|
||||
activeModalComponent: false,
|
||||
componentHistory: [],
|
||||
listeners: [],
|
||||
closeOnEscape: false,
|
||||
closeOnEscapeIsForceful: false,
|
||||
dispatchCloseEvent: false,
|
||||
destroyOnClose: false,
|
||||
closeOnClickAway:false,
|
||||
|
||||
closeModalOnEscape(trigger) {
|
||||
|
||||
///Only proceed if the trigger is for ChatModal
|
||||
if (trigger.modalType != 'WireChatModal'){ return;}
|
||||
//check if canCloseOnEsp
|
||||
if (this.closeOnEscape === false) { return; }
|
||||
if (!this.closingModal('closingModalOnEscape')) { return; }
|
||||
|
||||
//check if should also close all children modal when this current on is closed
|
||||
const force = this.closeOnEscapeIsForceful === true;
|
||||
this.closeModal(force);
|
||||
|
||||
},
|
||||
closeModalOnClickAway(trigger) {
|
||||
if (this.closeOnClickAway === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.closingModal('closingModalOnClickAway')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.closeModal(true);
|
||||
},
|
||||
closingModal(eventName) {
|
||||
const componentName = this.$wire.get('components')[this.activeModalComponent].name;
|
||||
|
||||
var params = {
|
||||
id: this.activeModalComponent,
|
||||
closing: true,
|
||||
};
|
||||
|
||||
Livewire.dispatchTo(componentName, eventName, params);
|
||||
|
||||
return params.closing;
|
||||
},
|
||||
closeModal(force = false, skipPreviousModals = 0, destroySkipped = false) {
|
||||
if (this.show === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dispatchCloseEvent === true) {
|
||||
const componentName = this.$wire.get('components')[this.activeModalComponent].name;
|
||||
Livewire.dispatch('wireChatModalClosed', {
|
||||
name: componentName
|
||||
});
|
||||
}
|
||||
|
||||
//Check if should completley destroy component on close
|
||||
//Meaning state won't be retained if component is opened again
|
||||
if (this.destroyOnClose === true) {
|
||||
Livewire.dispatch('destroyWireChatModal', {
|
||||
id: this.activeModalComponent
|
||||
});
|
||||
}
|
||||
|
||||
const id = this.componentHistory.pop();
|
||||
|
||||
if (id && !force) {
|
||||
if (id) {
|
||||
this.setActiveModalComponent(id, true);
|
||||
} else {
|
||||
this.setShowPropertyTo(false);
|
||||
}
|
||||
} else {
|
||||
this.setShowPropertyTo(false);
|
||||
}
|
||||
},
|
||||
setActiveModalComponent(id, skip = false) {
|
||||
|
||||
this.setShowPropertyTo(true);
|
||||
|
||||
if (this.activeModalComponent === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.activeModalComponent !== false && skip === false) {
|
||||
this.componentHistory.push(this.activeModalComponent);
|
||||
}
|
||||
|
||||
let focusableTimeout = 50;
|
||||
|
||||
if (this.activeModalComponent === false) {
|
||||
this.activeModalComponent = id
|
||||
this.showActiveModalComponent = true;
|
||||
} else {
|
||||
this.showActiveModalComponent = false;
|
||||
|
||||
focusableTimeout = 400;
|
||||
|
||||
setTimeout(() => {
|
||||
this.activeModalComponent = id;
|
||||
this.showActiveModalComponent = true;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
const attributes = this.$wire.get('components')[id]?.modalAttributes || {};
|
||||
this.closeOnEscape = attributes.closeOnEscape ?? false;
|
||||
this.closeOnEscapeIsForceful = attributes.closeOnEscapeIsForceful ?? false;
|
||||
this.dispatchCloseEvent = attributes.dispatchCloseEvent ?? false;
|
||||
this.destroyOnClose = attributes.destroyOnClose ?? false;
|
||||
this.closeOnClickAway = attributes.closeOnClickAway ?? false;
|
||||
|
||||
this.$nextTick(() => {
|
||||
let focusable = this.$refs[id]?.querySelector('[autofocus]');
|
||||
if (focusable) {
|
||||
setTimeout(() => {
|
||||
focusable.focus();
|
||||
}, focusableTimeout);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setShowPropertyTo(show) {
|
||||
this.show = show;
|
||||
|
||||
if (show) {
|
||||
document.body.classList.add('overflow-y-hidden');
|
||||
} else {
|
||||
document.body.classList.remove('overflow-y-hidden');
|
||||
|
||||
setTimeout(() => {
|
||||
this.activeModalComponent = false;
|
||||
this.$wire.resetState();
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
init() {
|
||||
|
||||
this.listeners.push(
|
||||
Livewire.on('closeWireChatModal', (data) => {
|
||||
this.closeModal(data?.force ?? false, data?.skipPreviousModals ?? 0, data
|
||||
?.destroySkipped ?? false);
|
||||
})
|
||||
);
|
||||
|
||||
this.listeners.push(
|
||||
Livewire.on('activeWireChatModalComponentChanged', ({
|
||||
id
|
||||
}) => {
|
||||
this.setActiveModalComponent(id);
|
||||
})
|
||||
);
|
||||
},
|
||||
destroy() {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<div x-data="WireChatModal()" x-on:close.stop="setShowPropertyTo(false)"
|
||||
x-on:keydown.escape.stop="closeModalOnEscape({modalType: 'WireChatModal', event: $event })"
|
||||
tabindex="0"
|
||||
x-show="show" class="fixed inset-0 z-50 overflow-y-auto" style="display: none;">
|
||||
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-10 text-center sm:block sm:p-0">
|
||||
<div x-show="show" x-on:click="closeModalOnClickAway()" x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0" class="fixed inset-0 transition-all transform">
|
||||
<div class="absolute inset-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] opacity-75"></div>
|
||||
</div>
|
||||
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen " aria-hidden="true">​</span>
|
||||
|
||||
<div x-show="show && showActiveModalComponent"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
|
||||
class="inline-block align-middle rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 w-full sm:max-w-lg"
|
||||
id="chat-dialog-container" x-trap.noscroll="show && showActiveModalComponent" aria-modal="true">
|
||||
@forelse($components as $id => $component)
|
||||
<div x-show.immediate="activeModalComponent == '{{ $id }}'" x-ref="{{ $id }}"
|
||||
wire:key="{{ $id }}">
|
||||
@livewire($component['name'], $component['arguments'], key($id))
|
||||
</div>
|
||||
@empty
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
95
resources/views/vendor/wirechat/livewire/new/chat.blade.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
@use('Namu\WireChat\Facades\WireChat')
|
||||
<div id="new-chat-modal ">
|
||||
|
||||
<div
|
||||
class="relative w-full border mx-auto border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] overflow-visible bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] dark:text-white p-12 sm:max-w-lg sm:rounded-lg">
|
||||
|
||||
<header class=" sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] z-10 py-2">
|
||||
<div class="flex justify-between items-center justify-between pb-2">
|
||||
|
||||
<h3 class="text-lg font-semibold">{{__('wirechat::new.chat.labels.heading') }}</h3>
|
||||
|
||||
<x-wirechat::actions.close-modal>
|
||||
<button
|
||||
dusk="close_modal_button"
|
||||
class="p-2 text-gray-600 hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] dark:hover:text-white rounded-full hover:text-gray-800 ">
|
||||
<svg class="w-5 h-5 cursor-pointer" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke-width="1.2" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</x-wirechat::actions.close-modal>
|
||||
|
||||
</div>
|
||||
|
||||
<section class="flex flex-wrap items-center px-0 border-b border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)]">
|
||||
<input dusk="search_users_field" autofocus type="search" id="users-search-field"
|
||||
wire:model.live.debounce='search' autocomplete="off" placeholder="{{__('wirechat::new.chat.inputs.search.placeholder')}}"
|
||||
class=" w-full border-0 w-auto px-0 dark:bg-[var(--wc-dark-primary)] outline-hidden focus:outline-hidden bg-[var(--wc-light-primary)] rounded-lg focus:ring-0 hover:ring-0">
|
||||
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<div class="relative w-full">
|
||||
|
||||
{{-- New Group button --}}
|
||||
@if (WireChat::showNewGroupModalButton() && auth()->user()->canCreateGroups())
|
||||
|
||||
{{-- Buton to trigger opening of new grop modal --}}
|
||||
<x-wirechat::actions.new-group widget="{{$this->isWidget()}}">
|
||||
<button @dusk="open_new_group_modal_button" class="flex items-center gap-3 my-4 rounded-lg p-2 w-full border transition-colors border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] hover:border-[var(--wc-light-secondary)] dark:hover:border-[var(--wc-dark-secondary)]" >
|
||||
<span style=" color: var(--wc-brand-primary); " class="p-1 bg-gray-100 rounded-full ">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class=" w-5 h-5">
|
||||
<path fill-rule="evenodd" d="M8.25 6.75a3.75 3.75 0 1 1 7.5 0 3.75 3.75 0 0 1-7.5 0ZM15.75 9.75a3 3 0 1 1 6 0 3 3 0 0 1-6 0ZM2.25 9.75a3 3 0 1 1 6 0 3 3 0 0 1-6 0ZM6.31 15.117A6.745 6.745 0 0 1 12 12a6.745 6.745 0 0 1 6.709 7.498.75.75 0 0 1-.372.568A12.696 12.696 0 0 1 12 21.75c-2.305 0-4.47-.612-6.337-1.684a.75.75 0 0 1-.372-.568 6.787 6.787 0 0 1 1.019-4.38Z" clip-rule="evenodd" />
|
||||
<path d="M5.082 14.254a8.287 8.287 0 0 0-1.308 5.135 9.687 9.687 0 0 1-1.764-.44l-.115-.04a.563.563 0 0 1-.373-.487l-.01-.121a3.75 3.75 0 0 1 3.57-4.047ZM20.226 19.389a8.287 8.287 0 0 0-1.308-5.135 3.75 3.75 0 0 1 3.57 4.047l-.01.121a.563.563 0 0 1-.373.486l-.115.04c-.567.2-1.156.349-1.764.441Z" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<p class="dark:text-white">@lang('wirechat::new.chat.actions.new_group.label')</p>
|
||||
</button>
|
||||
</x-wirechat::actions.new-group>
|
||||
@endif
|
||||
{{-- <h5 class="text font-semibold text-gray-800 dark:text-gray-100">Recent Chats</h5> --}}
|
||||
<section class="my-4 grid">
|
||||
@if (count($users)!=0)
|
||||
|
||||
<ul class="overflow-auto flex flex-col">
|
||||
|
||||
@foreach ($users as $key => $user)
|
||||
<li wire:key="user-{{ $key }}"
|
||||
wire:click="createConversation('{{ $user->id }}',{{ json_encode(get_class($user)) }})"
|
||||
class="flex cursor-pointer group gap-2 items-center p-2">
|
||||
|
||||
<x-wirechat::avatar :src="$user->cover_url" class="w-10 h-10" />
|
||||
|
||||
<div class="flex flex-col">
|
||||
<p class="group-hover:underline transition-all">
|
||||
{{ $user->display_name }}</p>
|
||||
@php
|
||||
$location = $user->getLocationFirst();
|
||||
@endphp
|
||||
@if ($location && isset($location['name_short']))
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $location['name_short'] }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
|
||||
</ul>
|
||||
@else
|
||||
@if (!empty($search))
|
||||
|
||||
<span class="m-auto">@lang('wirechat::new.chat.messages.empty_search_result')</span>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
253
resources/views/vendor/wirechat/livewire/new/group.blade.php
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
|
||||
<div x-data dusk="new_group_modal">
|
||||
|
||||
<div
|
||||
class="relative w-full border items-center justify-center border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] overflow-visible bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] dark:text-white sm:max-w-lg sm:rounded-lg">
|
||||
|
||||
{{-- Group Details --}}
|
||||
<section x-show="$wire.showAddMembers==false"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 -translate-x-full" x-transition:enter-end="opacity-100 translate-x-0"
|
||||
x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100 translate-x-0"
|
||||
x-transition:leave-end="opacity-0 -translate-x-full"
|
||||
>
|
||||
|
||||
<form wire:submit="validateDetails()" class="flex flex-col p-12">
|
||||
|
||||
<header>
|
||||
|
||||
<div class="flex gap-10 w-full">
|
||||
|
||||
@if ($photo)
|
||||
<div class="relative w-28 h-28 overflow-clip rounded-full">
|
||||
<x-wirechat::avatar :src="$photo->temporaryUrl()" class="w-28 h-28" />
|
||||
<button
|
||||
type="button"
|
||||
class="bottom-0 inset-x-0 bg-white/40 text-red-800 flex items-center justify-center absolute "
|
||||
wire:click="deletePhoto">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" class="size-6 w-5 h-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@else
|
||||
<label class="cursor-pointer">
|
||||
<x-wirechat::avatar wire:loading.class="animate-pulse" wire:target="photo" :group="true" class="w-28 h-28" />
|
||||
<input wire:model="photo" dusk="add_photo_field" type="file" hidden accept=".jpg,.jpeg,.png,.webp">
|
||||
</label>
|
||||
@endif
|
||||
|
||||
<div class=" my-auto">
|
||||
|
||||
<label for="name">@lang('wirechat::new.group.inputs.name.label')</label>
|
||||
|
||||
<input id='name' type="text" wire:model='name' autofocus placeholder="{{__('wirechat::new.group.inputs.name.placeholder') }}"
|
||||
class=" w-full border-0 px-0 bg-inherit dark:text-white outline-hidden w-full focus:outline-hidden focus:ring-0 hover:ring-0">
|
||||
|
||||
<span class="text-red-500 text-sm ">
|
||||
@error('name')
|
||||
{{ $message }}
|
||||
@enderror
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<span class="text-red-500 text-sm ">
|
||||
@error('photo')
|
||||
{{ $message }}
|
||||
@enderror
|
||||
</span>
|
||||
|
||||
</header>
|
||||
|
||||
<main class="my-5">
|
||||
<div class=" my-auto flex flex-col gap-y-2">
|
||||
|
||||
<label class="my-2" for="description">@lang('wirechat::new.group.inputs.description.label')</label>
|
||||
|
||||
<textarea id='description' type="text" wire:model='description' placeholder="{{__('wirechat::new.group.inputs.description.placeholder')}}" rows="4"
|
||||
class=" w-full resize-none rounded-lg border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] bg-inherit dark:text-white outline-hidden w-full focus:outline-hidden focus:ring-0 hover:ring-0">
|
||||
</textarea>
|
||||
|
||||
|
||||
<span class="text-red-500 text-sm ">
|
||||
@error('description')
|
||||
{{ $message }}
|
||||
@enderror
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="flex gap-4 justify-end mt-auto">
|
||||
<x-wirechat::actions.close-modal>
|
||||
<x-jetstream.secondary-button type="button" dusk="cancel_create_new_group_button">
|
||||
@lang('wirechat::new.group.actions.cancel.label')
|
||||
</x-jetstream.secondary-button>
|
||||
</x-wirechat::actions.close-modal>
|
||||
|
||||
<x-jetstream.button type="submit" x-bind:disabled="!$wire.name || !$wire.name.trim().length" dusk="next_button">
|
||||
@lang('wirechat::new.group.actions.next.label')
|
||||
</x-jetstream.button>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
|
||||
{{-- Add members --}}
|
||||
<section dusk="add_members_section" x-cloak x-show="$wire.showAddMembers==true"
|
||||
x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0 translate-x-full"
|
||||
x-transition:enter-end="opacity-100 translate-x-0" x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-x-0" x-transition:leave-end="opacity-0 translate-x-full"
|
||||
class="p-12 relative h-full overflow-x-hidden ">
|
||||
|
||||
<header class=" sticky top-0 bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] z-10 py-2">
|
||||
<div class="flex items-center pb-2">
|
||||
|
||||
<button @click="$wire.showAddMembers=false"
|
||||
class="p-2 ml-0 text-gray-600 dark:hover:bg-[var(--wc-dark-secondary)] dark:hover:text-white rounded-full hover:text-gray-800 hover:bg-[var(--wc-light-secondary)]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class=" w-5 w-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
|
||||
</svg>
|
||||
|
||||
</button>
|
||||
|
||||
<h3 class="text-sm mx-auto font-semibold "><span>@lang('wirechat::new.group.labels.add_members')</span> {{count($selectedMembers)}} / {{$maxGroupMembers}}</h3>
|
||||
|
||||
<x-jetstream.button
|
||||
wire:click="create"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target='create'
|
||||
type="button"
|
||||
class="text-xs py-1.5 px-3">
|
||||
@lang('wirechat::new.group.actions.create.label')
|
||||
</x-jetstream.button>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Member limit error --}}
|
||||
<div
|
||||
x-data="{ showError:false }"
|
||||
x-on:show-member-limit-error.window="
|
||||
showError=true;
|
||||
setTimeout(()=>{ showError=false; },1500);
|
||||
"
|
||||
class="text-red-500 text-sm mx-auto ">
|
||||
<span x-transition x-show="showError">
|
||||
@lang('wirechat::new.group.messages.members_limit_error',['count'=>$maxGroupMembers])
|
||||
</span>
|
||||
</div>
|
||||
{{-- Search input --}}
|
||||
<section class="flex flex-wrap items-center px-0 border-b border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)]">
|
||||
<input type="search" id="users-search-field" wire:model.live.debounce='search' autocomplete="off"
|
||||
placeholder="{{__('wirechat::new.group.inputs.search.placeholder')}}"
|
||||
class=" w-full border-0 w-auto dark:bg-[var(--wc-dark-primary)] outline-hidden focus:outline-hidden bg-[var(--wc-light-primary)] bg-none rounded-lg focus:ring-0 hover:ring-0">
|
||||
</section>
|
||||
|
||||
|
||||
<section class=" overflow-x-hidden my-2 ">
|
||||
<ul
|
||||
style="
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
"
|
||||
class="flex w-full overflow-x-auto gap-3">
|
||||
|
||||
@if ($selectedMembers)
|
||||
|
||||
@foreach ($selectedMembers as $key => $member)
|
||||
<li class="flex items-center text-nowrap min-w-fit px-2 py-1 text-sm font-medium text-gray-800 bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] rounded-sm dark:text-gray-300"
|
||||
wire:key="selected-member-{{ $member->id }}">
|
||||
{{ $member->display_name }}
|
||||
<button type="button"
|
||||
wire:click="toggleMember('{{ $member->id }}',{{ json_encode(get_class($member)) }})"
|
||||
class="flex items-center p-1 ms-2 text-sm text-gray-400 bg-transparent rounded-xs hover:bg-[var(--wc-light-secondary)] dark:hover:bg-[var(--wc-dark-secondary)] hover:text-gray-900 dark:hover:text-gray-300"
|
||||
aria-label="Remove">
|
||||
<svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
||||
</svg>
|
||||
<span class="sr-only">Remove badge</span>
|
||||
|
||||
</button>
|
||||
</li>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</header>
|
||||
|
||||
|
||||
{{-- Search --}}
|
||||
<div class="relative w-full">
|
||||
{{-- <h5 class="text font-semibold text-gray-800 dark:text-gray-100">Recent Chats</h5> --}}
|
||||
<section class="my-4 grid">
|
||||
@if (count($users)!=0)
|
||||
<ul class="overflow-auto flex flex-col">
|
||||
@foreach ($users as $key => $user)
|
||||
<li class="flex cursor-pointer group gap-2 items-center p-2">
|
||||
|
||||
<label
|
||||
wire:click="toggleMember('{{ $user->id }}',{{ json_encode(get_class($user)) }})"
|
||||
class="flex cursor-pointer gap-2 items-center w-full">
|
||||
<x-wirechat::avatar src="{{ $user->cover_url }}" class="w-10 h-10" />
|
||||
|
||||
<div class="flex flex-col flex-1 min-w-0">
|
||||
<p class="group-hover:underline transition-all truncate">
|
||||
{{ $user->display_name }}</p>
|
||||
@php
|
||||
$location = $user->getLocationFirst();
|
||||
@endphp
|
||||
@if ($location && isset($location['name_short']))
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $location['name_short'] }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="ml-auto">
|
||||
@if ($selectedMembers->contains(fn($member) => $member->id == $user->id && get_class($member) == get_class($user)))
|
||||
<div class="w-6 h-6 bg-theme-brand rounded flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-4 h-4 text-theme-background">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
</div>
|
||||
@else
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
|
||||
fill="currentColor" class="bi bi-plus-square-dotted w-6 h-6 text-gray-400"
|
||||
viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M2.5 0q-.25 0-.487.048l.194.98A1.5 1.5 0 0 1 2.5 1h.458V0zm2.292 0h-.917v1h.917zm1.833 0h-.917v1h.917zm1.833 0h-.916v1h.916zm1.834 0h-.917v1h.917zm1.833 0h-.917v1h.917zM13.5 0h-.458v1h.458q.151 0 .293.029l.194-.981A2.5 2.5 0 0 0 13.5 0m2.079 1.11a2.5 2.5 0 0 0-.69-.689l-.556.831q.248.167.415.415l.83-.556zM1.11.421a2.5 2.5 0 0 0-.689.69l.831.556c.11-.164.251-.305.415-.415zM16 2.5q0-.25-.048-.487l-.98.194q.027.141.028.293v.458h1zM.048 2.013A2.5 2.5 0 0 0 0 2.5v.458h1V2.5q0-.151.029-.293zM0 3.875v.917h1v-.917zm16 .917v-.917h-1v.917zM0 5.708v.917h1v-.917zm16 .917v-.917h-1v.917zM0 7.542v.916h1v-.916zm15 .916h1v-.916h-1zM0 9.375v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .916v.917h1v-.917zm16 .917v-.917h-1v.917zm-16 .917v.458q0 .25.048.487l.98-.194A1.5 1.5 0 0 1 1 13.5v-.458zm16 .458v-.458h-1v.458q0 .151-.029.293l.981.194Q16 13.75 16 13.5M.421 14.89c.183.272.417.506.69.689l.556-.831a1.5 1.5 0 0 1-.415-.415zm14.469.689c.272-.183.506-.417.689-.69l-.831-.556c-.11.164-.251.305-.415.415l.556.83zm-12.877.373Q2.25 16 2.5 16h.458v-1H2.5q-.151 0-.293-.029zM13.5 16q.25 0 .487-.048l-.194-.98A1.5 1.5 0 0 1 13.5 15h-.458v1zm-9.625 0h.917v-1h-.917zm1.833 0h.917v-1h-.917zm1.834-1v1h.916v-1zm1.833 1h.917v-1h-.917zm1.833 0h.917v-1h-.917zM8.5 4.5a.5.5 0 0 0-1 0v3h-3a.5.5 0 0 0 0 1h3v3a.5.5 0 0 0 1 0v-3h3a.5.5 0 0 0 0-1h-3z" />
|
||||
</svg>
|
||||
@endif
|
||||
</div>
|
||||
</label>
|
||||
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
@if (!empty($search))
|
||||
<span class="m-auto">@lang('wirechat::new.group.messages.empty_search_result')</span>
|
||||
@endif
|
||||
@endif
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
12
resources/views/vendor/wirechat/livewire/pages/chat.blade.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="w-full flex min-h-full h-full rounded-lg" >
|
||||
<div class=" hidden md:grid bg-inherit border-r border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] dark:bg-inherit relative w-full h-full md:w-[360px] lg:w-[400px] xl:w-[500px] shrink-0 overflow-y-auto ">
|
||||
<livewire:wirechat.chats
|
||||
:showHomeRouteButton="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<main class=" grid w-full grow h-full min-h-min relative overflow-y-auto" style="contain:content">
|
||||
<livewire:wirechat.chat conversation="{{$this->conversation->id}}"/>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
13
resources/views/vendor/wirechat/livewire/pages/chats.blade.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<div class="w-full h-full min-h-full flex rounded-lg" >
|
||||
<div class="relative w-full h-full border-r border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] md:w-[360px] lg:w-[400px] xl:w-[500px] shrink-0 overflow-y-auto ">
|
||||
<livewire:wirechat.chats/>
|
||||
</div>
|
||||
<main class="hidden md:grid h-full min-h-full w-full bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] h-full relative overflow-y-auto" style="contain:content">
|
||||
|
||||
<div class="m-auto text-center justify-center flex gap-3 flex-col items-center col-span-12">
|
||||
|
||||
<h4 class="font-medium p-2 px-3 rounded-full font-semibold bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] dark:text-white dark:font-normal">@lang('wirechat::pages.chat.messages.welcome')</h4>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
233
resources/views/vendor/wirechat/livewire/widgets/wire-chat.blade.php
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
<div class="h-full ">
|
||||
|
||||
@assets
|
||||
<script>
|
||||
window.ChatWidget = () => {
|
||||
return {
|
||||
show: false,
|
||||
showActiveComponent: true,
|
||||
activeWidgetComponent: false,
|
||||
componentHistory: [],
|
||||
listeners: [],
|
||||
//current component attributes
|
||||
closeOnEscape: false,
|
||||
closeOnEscapeIsForceful: false,
|
||||
dispatchCloseEvent: false,
|
||||
destroyOnClose: false,
|
||||
closeModalOnClickAway:false,
|
||||
closeChatWidgetOnEscape(trigger) {
|
||||
|
||||
///Only proceed if the trigger is for ChatDrawer
|
||||
if (trigger.modalType !== 'ChatWidget') {
|
||||
return;
|
||||
}
|
||||
|
||||
//check if canCloseOnEsp
|
||||
if (this.closeOnEscape === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Fire closingModalOnEscape:event to parent
|
||||
if (!this.closingModal('closingModalOnEscape')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//check if should also close all children modal when this current on is closed
|
||||
const force = this.closeOnEscapeIsForceful === true;
|
||||
this.closeWidget(force);
|
||||
|
||||
},
|
||||
closingModal(eventName) {
|
||||
const componentName = this.$wire.get('widgetComponents')[this.activeWidgetComponent].name;
|
||||
|
||||
var params = {
|
||||
id: this.activeWidgetComponent,
|
||||
closing: true,
|
||||
};
|
||||
|
||||
Livewire.dispatchTo(componentName, eventName, params);
|
||||
|
||||
return params.closing;
|
||||
},
|
||||
|
||||
closeWidget(force = false, skipPreviousModals = 0, destroySkipped = false) {
|
||||
|
||||
if (this.show === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if should completley destroy component on close
|
||||
//Meaning state won't be retained if component is opened again
|
||||
if (this.destroyOnClose === true) {
|
||||
|
||||
Livewire.dispatch('destroyChatWidget', {
|
||||
id: this.activeWidgetComponent
|
||||
});
|
||||
}
|
||||
|
||||
const id = this.componentHistory.pop();
|
||||
if (id && !force) {
|
||||
if (id) {
|
||||
this.setActiveWidgetComponent(id, true);
|
||||
} else {
|
||||
this.setShowPropertyTo(false);
|
||||
}
|
||||
} else {
|
||||
this.setShowPropertyTo(false);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
setActiveWidgetComponent(id, skip = false) {
|
||||
|
||||
this.setShowPropertyTo(true);
|
||||
// this.closeWidget(true);
|
||||
|
||||
|
||||
if (this.activeWidgetComponent === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.activeWidgetComponent !== false && skip === false) {
|
||||
this.componentHistory.push(this.activeWidgetComponent);
|
||||
}
|
||||
|
||||
let focusableTimeout = 50;
|
||||
|
||||
if (this.activeWidgetComponent === false) {
|
||||
this.activeWidgetComponent = id
|
||||
this.showActiveComponent = true;
|
||||
} else {
|
||||
|
||||
this.showActiveComponent = false;
|
||||
focusableTimeout = 400;
|
||||
|
||||
setTimeout(() => {
|
||||
this.activeWidgetComponent = id;
|
||||
this.showActiveComponent = true;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
|
||||
// Fetch modal attributes and set Alpine properties
|
||||
const attributes = this.$wire.get('widgetComponents')[id]?.modalAttributes || {};
|
||||
this.closeOnEscape = attributes.closeOnEscape ?? false;
|
||||
this.closeOnEscapeIsForceful = attributes.closeOnEscapeIsForceful ?? false;
|
||||
this.dispatchCloseEvent = attributes.dispatchCloseEvent ?? false;
|
||||
this.destroyOnClose = attributes.destroyOnClose ?? false;
|
||||
this.closeModalOnClickAway = attributes.closeModalOnClickAway ?? false;
|
||||
|
||||
this.$nextTick(() => {
|
||||
let focusable = this.$refs[id]?.querySelector('[autofocus]');
|
||||
if (focusable) {
|
||||
setTimeout(() => {
|
||||
focusable.focus();
|
||||
}, focusableTimeout);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setShowPropertyTo(show) {
|
||||
this.show = show;
|
||||
if (show) {
|
||||
document.body.classList.add('overflow-y-hidden');
|
||||
} else {
|
||||
document.body.classList.remove('overflow-y-hidden');
|
||||
|
||||
setTimeout(() => {
|
||||
this.activeWidgetComponent = false;
|
||||
this.$wire.resetState();
|
||||
|
||||
//Notify listeners that chat is
|
||||
|
||||
}, 300);
|
||||
|
||||
const conversation = this.$wire.selectedConversationId;
|
||||
Livewire.dispatch('chat-closed', {
|
||||
conversation:conversation
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
init() {
|
||||
|
||||
/*! Changed the event to closeChatWidget in order to not interfere with the main modal */
|
||||
this.listeners.push(Livewire.on('closeChatWidget', (data) => { this.closeWidget(data?.force ?? false, data?.skipPreviousModals ?? 0, data ?.destroySkipped ?? false); }));
|
||||
|
||||
/*! Changed listener name to activeChatWidgetComponentChanged to not interfer with main modal*/
|
||||
this.listeners.push(Livewire.on('activeChatWidgetComponentChanged', ({id}) => {
|
||||
this.setActiveWidgetComponent(id);
|
||||
}));
|
||||
},
|
||||
destroy() {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener();
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
@endassets
|
||||
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
selectedConversationId:null,
|
||||
get chatIsOpen(){
|
||||
|
||||
return $wire.selectedConversationId !==null;
|
||||
|
||||
}
|
||||
}"
|
||||
class ='w-full h-full bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] border border-[var(--wc-light-secondary)] dark:border-[var(--wc-dark-secondary)] flex overflow-hidden rounded-lg'>
|
||||
<div :class="chatIsOpen && 'hidden md:grid'" class="relative w-full h-full sm:border-r border-[var(--wc-light-border)] dark:border-[var(--wc-dark-border)] md:w-[360px] lg:w-[400px] xl:w-[450px] shrink-0 overflow-y-auto ">
|
||||
<livewire:wirechat.chats :widget="true" />
|
||||
</div>
|
||||
<main
|
||||
x-data="ChatWidget()"
|
||||
x-on:open-chat.window="$wire.selectedConversationId= $event.detail.conversation;"
|
||||
x-on:close-chat.stop.window="setShowPropertyTo(false)"
|
||||
x-on:keydown.escape.stop.window="closeChatWidgetOnEscape({ modalType: 'ChatWidget', event: $event });"
|
||||
aria-modal="true"
|
||||
tabindex="0"
|
||||
class="w-full h-full min-h-full grid relative grow focus:outline-hidden focus:border-none"
|
||||
:class="!chatIsOpen && 'hidden md:grid'"
|
||||
style="contain:content;">
|
||||
<div
|
||||
x-cloak
|
||||
x-show="show && showActiveComponent" x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 -translate-x-full" x-transition:enter-end="opacity-100 translate-x-0"
|
||||
x-transition:leave="ease-in duration-100 " x-transition:leave-start="opacity-100 translate-x-0"
|
||||
x-transition:leave-end="opacity-0 -translate-x-full"
|
||||
class="fixed inset-0" id="chatwidget-container"
|
||||
aria-modal="true">
|
||||
@forelse($widgetComponents as $id => $component)
|
||||
<div x-show.immediate="activeWidgetComponent == '{{ $id }}'" x-ref="{{ $id }}"
|
||||
wire:key="key-{{$id }}" class="h-full">
|
||||
@livewire($component['name'], ['conversation'=> $component['conversation'] ,'widget'=>true], key($id))
|
||||
</div>
|
||||
@empty
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div x-show="!show && !chatIsOpen " class="m-auto justify-center flex gap-3 flex-col items-center ">
|
||||
|
||||
<h4 class="font-medium p-2 px-3 rounded-full font-semibold bg-[var(--wc-light-secondary)] dark:bg-[var(--wc-dark-secondary)] dark:text-white dark:font-normal">@lang('wirechat::widgets.wirechat.messages.welcome')</h4>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
236
resources/views/vendor/wireui-select/base.blade.php
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('text-field')"
|
||||
x-ref="container"
|
||||
:config="$config"
|
||||
x-data="wireui_select"
|
||||
:attributes="$wrapper->class([
|
||||
'cursor-pointer' => !$disabled && !$readonly,
|
||||
])"
|
||||
:x-props="WireUi::toJs([
|
||||
'asyncData' => $asyncData,
|
||||
'optionValue' => $optionValue,
|
||||
'optionLabel' => $optionLabel,
|
||||
'optionDescription' => $optionDescription,
|
||||
'hasSlot' => $slot->isNotEmpty(),
|
||||
'multiselect' => $multiselect,
|
||||
'searchable' => $searchable,
|
||||
'clearable' => $clearable,
|
||||
'readonly' => $readonly || $disabled,
|
||||
'placeholder' => $placeholder,
|
||||
'template' => $template,
|
||||
'wireModel' => WireUi::wireModel(isset($__livewire) ? $this : null, $attributes),
|
||||
'alpineModel' => WireUi::alpineModel($attributes),
|
||||
])"
|
||||
x-bind:class="{
|
||||
'ring-1 ring-theme-accent': positionable.isOpen(),
|
||||
}"
|
||||
x-on:click="openIfClosed"
|
||||
x-on:keydown.enter.stop.prevent="openIfClosed"
|
||||
x-on:keydown.space.stop.prevent="openIfClosed"
|
||||
x-on:keydown.arrow-down.prevent="positionable.open()"
|
||||
tabindex="0"
|
||||
>
|
||||
<div class="hidden" hidden>
|
||||
<div hidden x-ref="json">{{ WireUi::toJs($optionsToArray()) }}</div>
|
||||
<div hidden x-ref="slot">{{ $slot }}</div>
|
||||
|
||||
<x-wireui-wrapper::hidden
|
||||
:id="$id"
|
||||
:name="$name"
|
||||
x-ref="input"
|
||||
:value="$value"
|
||||
x-bind:value="getSelectedValue"
|
||||
/>
|
||||
|
||||
@if (app()->runningUnitTests())
|
||||
<div dusk="select.{{ $name }}">
|
||||
{!! json_encode($optionsToArray()) !!}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@include('wireui-wrapper::components.slots', [
|
||||
'except' => ['append', 'label', 'beforeOptions', 'afterOptions'],
|
||||
])
|
||||
|
||||
@if ($label)
|
||||
<x-slot:label class="cursor-pointer select-none" x-on:click="toggle">
|
||||
{{ $label }}
|
||||
</x-slot:label>
|
||||
@endif
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="cursor-pointer flex items-center w-full truncate border-0 outline-0"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="text-sm text-gray-400 dark:text-gray-500 truncate select-none invalidated:text-negative-400 invalidated:dark:text-negative-400"
|
||||
x-show="isEmpty()"
|
||||
x-text="getPlaceholder"
|
||||
></span>
|
||||
|
||||
<span
|
||||
class="text-sm truncate text-secondary-600 dark:text-secondary-400 invalidated:text-negative-600 dark:invalidated:text-negative-400"
|
||||
x-show="!config.multiselect && isNotEmpty()"
|
||||
x-html="getSelectedDisplayText()"
|
||||
></span>
|
||||
|
||||
<div
|
||||
@class([
|
||||
'w-full flex items-center overflow-hidden',
|
||||
'cursor-pointer' => !$readonly && !$disabled
|
||||
])
|
||||
x-show="config.multiselect && isNotEmpty()"
|
||||
>
|
||||
<div class="flex items-center w-full gap-2 overflow-x-auto hide-scrollbar">
|
||||
@unless ($withoutItemsCount)
|
||||
<span
|
||||
class="inline-flex text-sm select-none text-secondary-700 dark:text-secondary-400"
|
||||
x-show="selectedOptions.length"
|
||||
x-text="selectedOptions.length"
|
||||
></span>
|
||||
@endunless
|
||||
|
||||
<div wire:ignore class="flex items-center gap-1 flex-nowrap">
|
||||
<template x-for="(option, index) in selectedOptions" :key="`selected.${index}.${option.value}.${option.label}`">
|
||||
<span class="
|
||||
inline-flex items-center py-0.5 pl-2 pr-0.5 rounded-full text-xs font-medium
|
||||
border border-secondary-200 shadow-sm bg-secondary-100 text-secondary-700
|
||||
dark:bg-secondary-700 dark:text-secondary-400 dark:border-none
|
||||
">
|
||||
<span style="max-width: 5rem" class="truncate select-none" x-text="option.label"></span>
|
||||
|
||||
<button
|
||||
class="cursor-pointer flex items-center justify-center w-4 h-4 shrink-0 text-secondary-400 hover:text-secondary-500"
|
||||
x-on:click.stop="unSelect(option)"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
x-show="config.clearable && !(config.readonly || config.disabled)"
|
||||
>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="w-3 h-3"
|
||||
name="x-mark"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<x-slot name="append" class="flex items-center pr-2.5 gap-x-1">
|
||||
@if ($clearable && !$readonly && !$disabled)
|
||||
<button
|
||||
class="cursor-pointer"
|
||||
x-show="isNotEmpty()"
|
||||
x-on:click.stop="clear"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
x-cloak
|
||||
>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
@class([
|
||||
'w-4 h-4 text-secondary-400 hover:text-negative-400',
|
||||
'invalidated:text-negative-400 dark:invalidated:text-negative-600',
|
||||
])
|
||||
name="x-mark"
|
||||
/>
|
||||
</button>
|
||||
@endif
|
||||
|
||||
<button class="cursor-pointer" tabindex="-1" type="button">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
@class([
|
||||
'w-5 h-5 text-secondary-400',
|
||||
'invalidated:text-negative-400 dark:invalidated:text-negative-600',
|
||||
])
|
||||
:name="$rightIcon"
|
||||
/>
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot:after>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('popover')"
|
||||
:margin="(bool) $label"
|
||||
class="w-full overflow-hidden select-none max-h-80"
|
||||
x-ref="optionsContainer"
|
||||
tabindex="-1"
|
||||
x-on:keydown.tab.prevent="$event.shiftKey || focusable.next()?.focus()"
|
||||
x-on:keydown.shift.tab.prevent="focusable.previous()?.focus()"
|
||||
x-on:keydown.arrow-up.prevent="focusable.previous()?.focus()"
|
||||
x-on:keydown.arrow-down.prevent="focusable.next()?.focus()"
|
||||
>
|
||||
<div
|
||||
class="px-2 my-2"
|
||||
wire:key="search.options.{{ $name }}"
|
||||
x-show="asyncData.api || (config.searchable && options.length >= @js($minItemsForSearch))"
|
||||
>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('input')"
|
||||
class="bg-slate-100 dark:bg-transparent"
|
||||
x-ref="search"
|
||||
x-model.debounce.500ms="search"
|
||||
shadowless
|
||||
right-icon="magnifying-glass"
|
||||
:placeholder="trans('wireui::messages.search_here')"
|
||||
type="search"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="overflow-y-auto select-none max-h-60 snap-y snap-proximity overscroll-contain soft-scrollbar"
|
||||
tabindex="-1"
|
||||
name="wireui.select.options.{{ $name }}"
|
||||
>
|
||||
<div
|
||||
class="w-full h-0.5 rounded-full relative overflow-hidden"
|
||||
:class="{ 'bg-gray-200 dark:bg-gray-700': asyncData.fetching }"
|
||||
>
|
||||
<div
|
||||
class="bg-primary-500 h-0.5 rounded-full absolute animate-linear-progress"
|
||||
style="width: 30%"
|
||||
x-show="asyncData.fetching"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@isset ($beforeOptions)
|
||||
<div {{ $beforeOptions->attributes }}>
|
||||
{{ $beforeOptions }}
|
||||
</div>
|
||||
@endisset
|
||||
|
||||
<ul x-ref="listing" wire:ignore>
|
||||
<template x-for="(option, index) in displayOptions" :key="`${index}.${option.value}`">
|
||||
<li tabindex="-1" :index="index">
|
||||
<div class="px-2 py-0.5">
|
||||
<div class="w-full h-8 rounded-sm animate-pulse bg-slate-200 dark:bg-slate-600"></div>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
|
||||
@unless ($hideEmptyMessage)
|
||||
<div
|
||||
class="px-3 py-12 text-center cursor-pointer sm:py-2 sm:px-3 sm:text-left text-secondary-500"
|
||||
x-show="displayOptions.length === 0"
|
||||
x-on:click="search ? resetSearch() : positionable.close()"
|
||||
>
|
||||
{{ $emptyMessage ?? trans('wireui::messages.empty_options') }}
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
@isset ($afterOptions)
|
||||
<div {{ $afterOptions->attributes }}>
|
||||
{{ $afterOptions }}
|
||||
</div>
|
||||
@endisset
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
</x-slot:after>
|
||||
</x-dynamic-component>
|
||||
27
resources/views/vendor/wireui/components/avatar.blade.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<div {{ $attributes->class($avatarClasses) }}>
|
||||
@if ($label)
|
||||
<span class="font-medium text-white dark:text-gray-200">
|
||||
{!! $label !!}
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@if ($src)
|
||||
<img @class([
|
||||
'shrink-0 object-cover object-center',
|
||||
'rounded-sm' => $squared,
|
||||
'rounded-full' => !$squared,
|
||||
$size,
|
||||
])
|
||||
src="{{ $src }}"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if (!$src && !$label)
|
||||
<svg
|
||||
class="shrink-0 text-gray-300 bg-gray-100 dark:bg-gray-600"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
@endif
|
||||
</div>
|
||||
23
resources/views/vendor/wireui/components/badge.blade.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<span {{ $attributes }}>
|
||||
@if ($icon)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
:name="$icon"
|
||||
class="{{ $iconSize }} shrink-0"
|
||||
/>
|
||||
@elseif (isset($prepend))
|
||||
<div {{ $prepend->attributes }}>{!! $prepend !!}</div>
|
||||
@endif
|
||||
|
||||
{!! $label ?? $slot !!}
|
||||
|
||||
@if ($rightIcon)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
:name="$rightIcon"
|
||||
class="{{ $iconSize }} shrink-0"
|
||||
/>
|
||||
@elseif (isset($append))
|
||||
<div {{ $append->attributes }}>{!! $append !!}</div>
|
||||
@endif
|
||||
</span>
|
||||
48
resources/views/vendor/wireui/components/button.blade.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
@php
|
||||
$tag = $href ? 'a' : 'button';
|
||||
|
||||
$defaultAttributes = [
|
||||
'wire:loading.attr' => 'disabled',
|
||||
'wire:loading.class' => '!cursor-wait',
|
||||
'wire:target' => ($spinner && strlen($spinner) > 1) ? $spinner : null,
|
||||
];
|
||||
|
||||
$href === null
|
||||
? $defaultAttributes['type'] = 'button'
|
||||
: $defaultAttributes['href'] = $href;
|
||||
@endphp
|
||||
|
||||
<{{ $tag }} {{ $attributes->merge($defaultAttributes) }}>
|
||||
@if ($icon)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
:name="$icon"
|
||||
class="{{ $iconSize }} shrink-0"
|
||||
/>
|
||||
@endif
|
||||
|
||||
{!! $label ?? $slot !!}
|
||||
|
||||
@if ($rightIcon)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
:name="$rightIcon"
|
||||
class="{{ $iconSize }} shrink-0"
|
||||
:wire:loading.remove="(bool) $spinner"
|
||||
/>
|
||||
@endif
|
||||
|
||||
@if ($spinner)
|
||||
<svg class="animate-spin {{ $iconSize }} shrink-0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
@if (preg_replace('/[^a-zA-Z]+/', '', $spinner))
|
||||
wire:target="{{ $spinner }}"
|
||||
@endif
|
||||
wire:loading.delay{{ $loadingDelay ? ".{$loadingDelay}":'' }}>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
@endif
|
||||
</{{ $tag }}>
|
||||
23
resources/views/vendor/wireui/components/card.blade.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<div class="{{ $cardClasses }}">
|
||||
@if ($header)
|
||||
{{ $header }}
|
||||
@elseif ($title || $action)
|
||||
<div class="{{ $headerClasses }}">
|
||||
<h3 class="font-medium whitespace-normal text-md text-secondary-700 dark:text-secondary-400">{{ $title }}</h3>
|
||||
|
||||
@if ($action)
|
||||
{{ $action }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div {{ $attributes->merge(['class' => "{$padding} text-secondary-700 rounded-b-xl grow dark:text-secondary-400"]) }}>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@if ($footer)
|
||||
<div class="{{ $footerClasses }}">
|
||||
{{ $footer }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
50
resources/views/vendor/wireui/components/checkbox.blade.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<div>
|
||||
<label for="{{ $id }}" class="flex items-center {{ $errors->has($name) ? 'text-negative-600':'' }}">
|
||||
<div class="relative flex items-start">
|
||||
@if ($leftLabel)
|
||||
<div class="mr-2 text-sm text-right">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('label')"
|
||||
class=""
|
||||
:for="$id"
|
||||
:label="$leftLabel"
|
||||
:has-error="$errors->has($name)"
|
||||
/>
|
||||
@if($description)
|
||||
<div class="text-gray-500">{!! $description !!}</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex items-center h-5">
|
||||
<input {{ $attributes->class([
|
||||
$getClasses($errors->has($name)),
|
||||
])->merge([
|
||||
'type' => 'checkbox',
|
||||
]) }} />
|
||||
</div>
|
||||
|
||||
@if ($label)
|
||||
<div class="ml-4 text-sm">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('label')"
|
||||
class=""
|
||||
:for="$id"
|
||||
:label="$label"
|
||||
:has-error="$errors->has($name)"
|
||||
/>
|
||||
@if($description)
|
||||
<div id="{{ $id }} . comments-description" class="text-gray-500">{!! $description !!}</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@if ($name)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('error')"
|
||||
:name="$name"
|
||||
/>
|
||||
@endif
|
||||
</div>
|
||||
11
resources/views/vendor/wireui/components/circle-badge.blade.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<span {{ $attributes->merge() }}>
|
||||
@if ($icon)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
:name="$icon"
|
||||
class="{{ $iconSize }} shrink-0"
|
||||
/>
|
||||
@else
|
||||
{!! $label ?? $slot !!}
|
||||
@endif
|
||||
</span>
|
||||
45
resources/views/vendor/wireui/components/circle-button.blade.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
@php
|
||||
$tag = $href ? 'a' : 'button';
|
||||
|
||||
$defaultAttributes = [
|
||||
'wire:loading.attr' => 'disabled',
|
||||
'wire:loading.class' => '!cursor-wait',
|
||||
];
|
||||
|
||||
$href === null
|
||||
? $defaultAttributes['type'] = 'button'
|
||||
: $defaultAttributes['href'] = $href;
|
||||
@endphp
|
||||
|
||||
<{{ $tag }} {{ $attributes->merge($defaultAttributes) }}>
|
||||
<div @if($spinner)
|
||||
@if (preg_replace('/[^a-zA-Z]+/', '', $spinner))
|
||||
wire:target="{{ $spinner }}"
|
||||
@endif
|
||||
wire:loading.remove
|
||||
@endif>
|
||||
@if ($icon)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
:name="$icon"
|
||||
class="{{ $iconSize }} shrink-0"
|
||||
/>
|
||||
@else
|
||||
{!! $label ?? $slot !!}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($spinner)
|
||||
<svg class="animate-spin {{ $iconSize }} shrink-0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
@if (preg_replace('/[^a-zA-Z]+/', '', $spinner))
|
||||
wire:target="{{ $spinner }}"
|
||||
@endif
|
||||
wire:loading.delay{{ $loadingDelay ? ".{$loadingDelay}":'' }}>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
@endif
|
||||
</{{ $tag }}>
|
||||
79
resources/views/vendor/wireui/components/color-picker.blade.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<div x-data="wireui_color_picker({
|
||||
colorNameAsValue: @boolean($colorNameAsValue),
|
||||
|
||||
@if ($attributes->wire('model')->value())
|
||||
wireModifiers: @toJs($attributes->wireModifiers()),
|
||||
wireModel: @entangle($attributes->wire('model')).live,
|
||||
@endif
|
||||
|
||||
@if ($colors)
|
||||
colors: @toJs($getColors())
|
||||
@endif
|
||||
})" {{ $attributes->only(['class', 'wire:key'])->class('relative') }}>
|
||||
<x-dynamic-component
|
||||
{{ $attributes->except(['class', 'wire:key'])->whereDoesntStartWith('wire:model.live') }}
|
||||
:component="WireUi::component('input')"
|
||||
x-model="{{ $colorNameAsValue ? 'selected.name' : 'selected.value' }}"
|
||||
x-bind:class="{ 'pl-8': selected.value }"
|
||||
x-on:input="setColor($event.target.value)"
|
||||
x-ref="input"
|
||||
:label="$label"
|
||||
:prefix="null"
|
||||
:icon="null">
|
||||
<x-slot name="prefix">
|
||||
<template x-if="selected.value">
|
||||
<div
|
||||
class="w-4 h-4 rounded shadow border"
|
||||
:style="{ 'background-color': selected.value }"
|
||||
></div>
|
||||
</template>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="append">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center p-0.5">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="h-full rounded-r-md"
|
||||
primary
|
||||
flat
|
||||
squared
|
||||
x-on:click="toggle"
|
||||
trigger
|
||||
:disabled="$disabled">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="
|
||||
w-4 h-4 group-focus:text-primary-700 text-gray-400 dark:text-gray-600
|
||||
dark:group-hover:text-gray-500 dark:group-focus:text-primary-500
|
||||
"
|
||||
:name="$rightIcon"
|
||||
/>
|
||||
</x-dynamic-component>
|
||||
</div>
|
||||
</x-slot>
|
||||
</x-dynamic-component>
|
||||
|
||||
<x-wireui::parts.popover
|
||||
:margin="(bool) $label"
|
||||
class="
|
||||
max-h-56 py-3 px-2 sm:py-2 sm:px-1 sm:w-72 sm:rounded-xl
|
||||
overflow-y-auto soft-scrollbar border border-secondary-200
|
||||
">
|
||||
<div class="flex flex-wrap items-center justify-center gap-1 sm:gap-0.5 max-w-[18rem] mx-auto">
|
||||
<span class="sr-only">dropdown-open</span>
|
||||
|
||||
<template x-for="(color, index) in colors" :key="index">
|
||||
<button class="
|
||||
w-6 h-6 rounded shadow-lg border hover:scale-125 transition-all ease-in-out duration-100 cursor-pointer
|
||||
hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-600 sdark:focus:ring-gray-400
|
||||
dark:border-0 dark:hover:ring-2 dark:hover:ring-gray-400
|
||||
"
|
||||
:style="{ 'background-color': color.value }"
|
||||
x-on:click="select(color)"
|
||||
:title="color.name"
|
||||
type="button">
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</x-wireui::parts.popover>
|
||||
</div>
|
||||
222
resources/views/vendor/wireui/components/datetime-picker.blade copy.php
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
<div
|
||||
x-data="wireui_datetime_picker({
|
||||
model: @entangleable($attributes->wire('model')),
|
||||
})"
|
||||
x-props="{
|
||||
config: {
|
||||
interval: @toJs($interval),
|
||||
is12H: @boolean($timeFormat == '12'),
|
||||
readonly: @boolean($readonly),
|
||||
disabled: @boolean($disabled),
|
||||
min: @toJs($min ? $min->format('Y-m-d\TH:i') : null),
|
||||
max: @toJs($max ? $max->format('Y-m-d\TH:i') : null),
|
||||
minTime: @toJs($minTime),
|
||||
maxTime: @toJs($maxTime),
|
||||
},
|
||||
withoutTimezone: @boolean($withoutTimezone),
|
||||
timezone: @toJs($timezone),
|
||||
userTimezone: @toJs($userTimezone ?? ''),
|
||||
parseFormat: @toJs($parseFormat ?? ''),
|
||||
displayFormat: @toJs($displayFormat ?? ''),
|
||||
weekDays: @lang('wireui::messages.datePicker.days'),
|
||||
monthNames: @lang('wireui::messages.datePicker.months'),
|
||||
withoutTime: @boolean($withoutTime),
|
||||
}"
|
||||
{{ $attributes
|
||||
->only('wire:key')
|
||||
->class('relative')
|
||||
->merge(['wire:key' => "datepicker::{$name}"]) }}
|
||||
>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('input')"
|
||||
{{ $attributes->whereDoesntStartWith(['wire:model.live', 'x-model', 'wire:key', 'readonly']) }}
|
||||
:borderless="$borderless"
|
||||
:shadowless="$shadowless"
|
||||
:label="$label"
|
||||
:hint="$hint"
|
||||
:corner-hint="$cornerHint"
|
||||
:icon="$icon"
|
||||
:prefix="$prefix"
|
||||
:prepend="$prepend"
|
||||
readonly
|
||||
x-on:click="toggle"
|
||||
x-bind:value="model ? getDisplayValue() : null">
|
||||
@if (!$readonly && !$disabled)
|
||||
<x-slot name="append">
|
||||
<div class="absolute inset-y-0 right-3 z-5 flex items-center justify-center">
|
||||
<div class="flex items-center gap-x-2 my-auto
|
||||
{{ $errors->has($name) ? 'text-negative-400 dark:text-negative-600' : 'text-secondary-400' }}">
|
||||
|
||||
@if ($clearable)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="cursor-pointer w-4 h-4 hover:text-negative-500 transition-colors ease-in-out duration-150"
|
||||
x-cloak
|
||||
name="x"
|
||||
x-show="model"
|
||||
x-on:click="clearDate()"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="cursor-pointer w-5 h-5"
|
||||
:name="$rightIcon"
|
||||
x-on:click="toggle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
|
||||
<x-wireui::parts.popover :margin="(bool) $label" class="max-h-96 overflow-y-auto p-3 sm:w-72">
|
||||
<div x-show="tab === 'date'" class="space-y-5">
|
||||
@unless ($withoutTips)
|
||||
<div class="grid grid-cols-3 gap-x-2 text-center text-secondary-600">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="bg-secondary-100 border-none dark:bg-secondary-800"
|
||||
x-on:click="selectYesterday"
|
||||
:label="__('wireui::messages.datePicker.yesterday')"
|
||||
/>
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="bg-secondary-100 border-none dark:bg-secondary-800"
|
||||
x-on:click="selectToday"
|
||||
:label="__('wireui::messages.datePicker.today')"
|
||||
/>
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="bg-secondary-100 border-none dark:bg-secondary-800"
|
||||
x-on:click="selectTomorrow"
|
||||
:label="__('wireui::messages.datePicker.tomorrow')"
|
||||
/>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="rounded-lg shrink-0"
|
||||
x-show="!monthsPicker"
|
||||
x-on:click="previousMonth"
|
||||
icon="chevron-left"
|
||||
flat
|
||||
/>
|
||||
|
||||
<div class="w-full flex items-center justify-center gap-x-2 text-secondary-600 dark:text-secondary-500">
|
||||
<button class="focus:outline-none focus:underline"
|
||||
x-text="monthNames[month]"
|
||||
x-on:click="monthsPicker = !monthsPicker"
|
||||
type="button">
|
||||
</button>
|
||||
<input class="w-14 appearance-none p-0 ring-0 border-none focus:ring-0 focus:outline-none dark:bg-secondary-800"
|
||||
x-model="year"
|
||||
x-on:input.debounce.500ms="fillPickerDates"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="rounded-lg shrink-0"
|
||||
x-show="!monthsPicker"
|
||||
x-on:click="nextMonth"
|
||||
icon="chevron-right"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 bg-white dark:bg-secondary-800 grid grid-cols-3 gap-3"
|
||||
x-show="monthsPicker"
|
||||
x-transition>
|
||||
<template x-for="(monthName, index) in monthNames" :key="`month.${monthName}`">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="text-secondary-400 dark:border-0 dark:hover:bg-secondary-700 uppercase"
|
||||
x-on:click="selectMonth(index)"
|
||||
x-text="monthName"
|
||||
xs
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 gap-2">
|
||||
<template x-for="day in weekDays" :key="`week-day.${day}`">
|
||||
<span class="text-secondary-400 text-3xs text-center uppercase pointer-events-none"
|
||||
x-text="day">
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template
|
||||
x-for="date in dates"
|
||||
:key="`date.${date.day}.${date.month}`"
|
||||
>
|
||||
<div class="flex justify-center picker-days">
|
||||
<button class="text-sm w-7 h-6 focus:outline-none rounded-md focus:ring-2 focus:ring-ofsset-2 focus:ring-primary-600
|
||||
hover:bg-primary-100 dark:hover:bg-secondary-700 dark:focus:ring-secondary-400
|
||||
disabled:cursor-not-allowed"
|
||||
:class="{
|
||||
'text-secondary-600 dark:text-secondary-400': !date.isDisabled && !date.isSelected && date.month === month,
|
||||
'text-secondary-400 dark:text-secondary-600': date.isDisabled || date.month !== month,
|
||||
'text-primary-600 border border-primary-600 dark:border-gray-400': date.isToday && !date.isSelected,
|
||||
'disabled:text-primary-400 disabled:border-primary-400': date.isToday && !date.isSelected,
|
||||
'!text-white bg-primary-600 font-semibold border border-primary-600': date.isSelected,
|
||||
'disabled:bg-primary-400 disabled:border-primary-400': date.isSelected,
|
||||
'hover:bg-primary-600 dark:bg-secondary-700 dark:border-secondary-400': date.isSelected,
|
||||
}"
|
||||
:disabled="date.isDisabled"
|
||||
x-on:click="selectDate(date)"
|
||||
x-text="date.day"
|
||||
type="button">
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="tab === 'time'" x-transition>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('input')"
|
||||
id="search.{{ $attributes->wire('model')->value() }}"
|
||||
:label="__('wireui::messages.selectTime')"
|
||||
x-model="searchTime"
|
||||
x-bind:placeholder="getSearchPlaceholder"
|
||||
x-ref="searchTime"
|
||||
x-on:input.debounce.150ms="onSearchTime($event.target.value)"
|
||||
/>
|
||||
|
||||
<div x-ref="timesContainer"
|
||||
class="mt-1 w-full max-h-52 pb-1 pt-2 overflow-y-auto flex flex-col picker-times">
|
||||
<template x-for="time in filteredTimes" :key="time.value">
|
||||
<button class="group rounded-md focus:outline-none focus:bg-primary-100 dark:focus:bg-secondary-700
|
||||
relative py-2 pl-2 pr-9 text-left transition-colors ease-in-out duration-100 cursor-pointer select-none
|
||||
hover:text-white hover:bg-primary-600 dark:hover:bg-secondary-700 dark:text-secondary-400"
|
||||
:class="{
|
||||
'text-primary-600': modelTime === time.value,
|
||||
'text-secondary-700': modelTime !== time.value,
|
||||
}"
|
||||
:name="`times.${time.value}`"
|
||||
type="button"
|
||||
x-on:click="selectTime(time)">
|
||||
<span x-text="time.label"></span>
|
||||
<span class="text-primary-600 dark:text-secondary-400 group-hover:text-white
|
||||
absolute inset-y-0 right-0 flex items-center pr-4"
|
||||
x-show="modelTime === time.value">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
name="check"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</x-wireui::parts.popover>
|
||||
</div>
|
||||
223
resources/views/vendor/wireui/components/datetime-picker.blade.php
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
<div
|
||||
x-data="wireui_datetime_picker({
|
||||
model: @entangleable($attributes->wire('model')),
|
||||
})"
|
||||
x-props="{
|
||||
config: {
|
||||
interval: @toJs($interval),
|
||||
is12H: @boolean($timeFormat == '12'),
|
||||
readonly: @boolean($readonly),
|
||||
disabled: @boolean($disabled),
|
||||
min: @toJs($min ? $min->format('Y-m-d\TH:i') : null),
|
||||
max: @toJs($max ? $max->format('Y-m-d\TH:i') : null),
|
||||
minTime: @toJs($minTime),
|
||||
maxTime: @toJs($maxTime),
|
||||
},
|
||||
withoutTimezone: @boolean($withoutTimezone),
|
||||
timezone: @toJs($timezone),
|
||||
userTimezone: @toJs($userTimezone ?? ''),
|
||||
parseFormat: @toJs($parseFormat ?? ''),
|
||||
displayFormat: @toJs($displayFormat ?? ''),
|
||||
weekDays: @lang('wireui::messages.datePicker.days'),
|
||||
monthNames: @lang('wireui::messages.datePicker.months'),
|
||||
withoutTime: @boolean($withoutTime),
|
||||
withoutTips: @boolean($withoutTips),
|
||||
}"
|
||||
{{ $attributes
|
||||
->only('wire:key')
|
||||
->class('relative')
|
||||
->merge(['wire:key' => "datepicker::{$name}"]) }}
|
||||
>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('input')"
|
||||
{{ $attributes->whereDoesntStartWith(['wire:model.live', 'x-model', 'wire:key', 'readonly']) }}
|
||||
:borderless="$borderless"
|
||||
:shadowless="$shadowless"
|
||||
:label="$label"
|
||||
:hint="$hint"
|
||||
:corner-hint="$cornerHint"
|
||||
:icon="$icon"
|
||||
:prefix="$prefix"
|
||||
:prepend="$prepend"
|
||||
readonly
|
||||
x-on:click="toggle"
|
||||
x-bind:value="model ? getDisplayValue() : null">
|
||||
@if (!$readonly && !$disabled)
|
||||
<x-slot name="append">
|
||||
<div class="absolute inset-y-0 right-3 z-5 flex items-center justify-center">
|
||||
<div class="flex items-center gap-x-2 my-auto
|
||||
{{ $errors->has($name) ? 'text-negative-400 dark:text-negative-600' : 'text-secondary-400' }}">
|
||||
|
||||
@if ($clearable)
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="cursor-pointer w-4 h-4 hover:text-negative-500 transition-colors ease-in-out duration-150"
|
||||
x-cloak
|
||||
name="x"
|
||||
x-show="model"
|
||||
x-on:click="clearDate()"
|
||||
/>
|
||||
@endif
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="cursor-pointer w-5 h-5"
|
||||
:name="$rightIcon"
|
||||
x-on:click="toggle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot>
|
||||
@endif
|
||||
</x-dynamic-component>
|
||||
|
||||
<x-wireui::parts.popover :margin="(bool) $label" class="max-h-72 overflow-y-auto p-2 sm:w-72">
|
||||
<div x-show="tab === 'date'" class="space-y-5">
|
||||
@unless ($withoutTips)
|
||||
<div class="grid grid-cols-3 gap-x-2 text-center text-secondary-600">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="bg-secondary-100 border-none dark:bg-secondary-800"
|
||||
x-on:click="selectYesterday"
|
||||
:label="__('wireui::messages.datePicker.yesterday')"
|
||||
/>
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="bg-secondary-100 border-none dark:bg-secondary-800"
|
||||
x-on:click="selectToday"
|
||||
:label="__('wireui::messages.datePicker.today')"
|
||||
/>
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="bg-secondary-100 border-none dark:bg-secondary-800"
|
||||
x-on:click="selectTomorrow"
|
||||
:label="__('wireui::messages.datePicker.tomorrow')"
|
||||
/>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="rounded-lg shrink-0"
|
||||
x-show="!monthsPicker"
|
||||
x-on:click="previousMonth"
|
||||
icon="chevron-left"
|
||||
flat
|
||||
/>
|
||||
|
||||
<div class="w-full flex items-center justify-center gap-x-2 text-secondary-600 dark:text-secondary-500">
|
||||
<button class="focus:outline-none focus:underline"
|
||||
x-text="monthNames[month]"
|
||||
x-on:click="monthsPicker = !monthsPicker"
|
||||
type="button">
|
||||
</button>
|
||||
<input class="w-14 appearance-none p-0 ring-0 border-none focus:ring-0 focus:outline-none dark:bg-secondary-800"
|
||||
x-model="year"
|
||||
x-on:input.debounce.500ms="fillPickerDates"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="rounded-lg shrink-0"
|
||||
x-show="!monthsPicker"
|
||||
x-on:click="nextMonth"
|
||||
icon="chevron-right"
|
||||
flat
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 bg-white dark:bg-secondary-800 grid grid-cols-3 gap-3"
|
||||
x-show="monthsPicker"
|
||||
x-transition>
|
||||
<template x-for="(monthName, index) in monthNames" :key="`month.${monthName}`">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('button')"
|
||||
class="text-secondary-400 dark:border-0 dark:hover:bg-secondary-700 uppercase"
|
||||
x-on:click="selectMonth(index)"
|
||||
x-text="monthName"
|
||||
xs
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 gap-3">
|
||||
<template x-for="day in weekDays" :key="`week-day.${day}`">
|
||||
<span class="text-secondary-400 text-3xs text-center uppercase pointer-events-none"
|
||||
x-text="day">
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template
|
||||
x-for="date in dates"
|
||||
:key="`date.${date.day}.${date.month}`"
|
||||
>
|
||||
<div class="flex justify-center picker-days">
|
||||
<button class="text-sm w-7 h-6 focus:outline-none rounded-md focus:ring-2 focus:ring-ofsset-2 focus:ring-primary-600
|
||||
hover:bg-primary-100 dark:hover:bg-secondary-700 dark:focus:ring-secondary-400
|
||||
disabled:cursor-not-allowed"
|
||||
:class="{
|
||||
'text-secondary-600 dark:text-secondary-400': !date.isDisabled && !date.isSelected && date.month === month,
|
||||
'text-secondary-400 dark:text-secondary-600': date.isDisabled || date.month !== month,
|
||||
'text-primary-600 border border-primary-600 dark:border-gray-400': date.isToday && !date.isSelected,
|
||||
'disabled:text-primary-400 disabled:border-primary-400': date.isToday && !date.isSelected,
|
||||
'!text-white bg-primary-600 font-semibold border border-primary-600': date.isSelected,
|
||||
'disabled:bg-primary-400 disabled:border-primary-400': date.isSelected,
|
||||
'hover:bg-primary-600 dark:bg-secondary-700 dark:border-secondary-400': date.isSelected,
|
||||
}"
|
||||
:disabled="date.isDisabled"
|
||||
x-on:click="selectDate(date)"
|
||||
x-text="date.day"
|
||||
type="button">
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="tab === 'time'" x-transition>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('input')"
|
||||
id="search.{{ $attributes->wire('model')->value() }}"
|
||||
:label="__('wireui::messages.selectTime')"
|
||||
x-model="searchTime"
|
||||
x-bind:placeholder="getSearchPlaceholder"
|
||||
x-ref="searchTime"
|
||||
x-on:input.debounce.150ms="onSearchTime($event.target.value)"
|
||||
/>
|
||||
|
||||
<div x-ref="timesContainer"
|
||||
class="mt-1 w-full max-h-52 pb-1 pt-2 overflow-y-auto flex flex-col picker-times">
|
||||
<template x-for="time in filteredTimes" :key="time.value">
|
||||
<button class="group rounded-md focus:outline-none focus:bg-primary-100 dark:focus:bg-secondary-700
|
||||
relative py-2 pl-2 pr-9 text-left transition-colors ease-in-out duration-100 cursor-pointer select-none
|
||||
hover:text-white hover:bg-primary-600 dark:hover:bg-secondary-700 dark:text-secondary-400"
|
||||
:class="{
|
||||
'text-primary-600': modelTime === time.value,
|
||||
'text-secondary-700': modelTime !== time.value,
|
||||
}"
|
||||
:name="`times.${time.value}`"
|
||||
type="button"
|
||||
x-on:click="selectTime(time)">
|
||||
<span x-text="time.label"></span>
|
||||
<span class="text-primary-600 dark:text-secondary-400 group-hover:text-white
|
||||
absolute inset-y-0 right-0 flex items-center pr-4"
|
||||
x-show="modelTime === time.value">
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
name="check"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</x-wireui::parts.popover>
|
||||
</div>
|
||||
95
resources/views/vendor/wireui/components/dialog.blade.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<div class="fixed inset-0 flex items-end overflow-y-auto sm:pt-16 justify-center {{ $align }} {{ $zIndex }}"
|
||||
x-data="wireui_dialog({ id: '{{ $dialog }}' })"
|
||||
x-show="show"
|
||||
x-on:wireui:{{ $dialog }}.window="showDialog($event.detail)"
|
||||
x-on:wireui:confirm-{{ $dialog }}.window="confirmDialog($event.detail)"
|
||||
x-on:keydown.escape.window="handleEscape"
|
||||
style="display: none"
|
||||
x-cloak>
|
||||
<div class="fixed inset-0 bg-secondary-400 bg-opacity-60 transform transition-opacity
|
||||
{{ $dialog }}-backdrop @if ($blur) {{ $blur }} @endif dark:bg-secondary-700 dark:bg-opacity-60"
|
||||
x-show="show"
|
||||
x-on:click="dismiss"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0">
|
||||
</div>
|
||||
|
||||
<div class="w-full transition-all p-4 sm:max-w-lg"
|
||||
x-show="show"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-on:mouseenter="pauseTimeout"
|
||||
x-on:mouseleave="resumeTimeout">
|
||||
<div class="relative shadow-md bg-white dark:bg-secondary-800 rounded-xl space-y-4 p-4"
|
||||
:class="{
|
||||
'sm:p-5 sm:pt-7': style === 'center',
|
||||
'sm:p-0 sm:pt-1': style === 'inline',
|
||||
}">
|
||||
<div class="bg-secondary-300 dark:bg-secondary-600 rounded-full transition-all duration-150 ease-linear absolute top-0 left-0"
|
||||
style="height: 2px; width: 100%;"
|
||||
x-ref="progressbar"
|
||||
x-show="dialog && dialog.progressbar && dialog.timeout">
|
||||
</div>
|
||||
|
||||
<div x-show="dialog && dialog.closeButton" class="absolute right-2 -top-2">
|
||||
<button class="{{ $dialog }}-button-close focus:outline-none p-1 focus:ring-2 focus:ring-secondary-200 rounded-full text-secondary-300"
|
||||
x-on:click="close"
|
||||
type="button">
|
||||
<span class="sr-only">close</span>
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="w-5 h-5"
|
||||
name="x"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4" :class="{ 'sm:space-x-4 sm:flex sm:items-center sm:space-y-0 sm:px-5 sm:py-2': style === 'inline' }">
|
||||
<div class="mx-auto flex items-center self-start justify-center shrink-0"
|
||||
:class="{ 'sm:items-start sm:mx-0': style === 'inline' }"
|
||||
x-show="dialog && dialog.icon">
|
||||
<div x-ref="iconContainer"></div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 w-full" :class="{ 'sm:mt-5': style === 'center' }">
|
||||
<h3 class="text-lg leading-6 font-medium text-secondary-900 dark:text-secondary-400 text-center"
|
||||
:class="{ 'sm:text-left': style === 'inline' }"
|
||||
@unless($title) x-ref="title" @endunless>
|
||||
{!! $title !!}
|
||||
</h3>
|
||||
|
||||
<p class="mt-2 text-sm text-secondary-500 text-center"
|
||||
:class="{ 'sm:text-left': style === 'inline' }"
|
||||
@unless($description) x-ref="description" @endunless>
|
||||
{!! $description !!}
|
||||
</p>
|
||||
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-y-2 sm:gap-x-3 rounded-b-xl"
|
||||
:class="{
|
||||
'sm:grid-cols-2 sm:gap-y-0': style === 'center',
|
||||
'sm:p-4 sm:bg-secondary-100 sm:dark:bg-secondary-800 sm:grid-cols-none sm:flex sm:justify-end': style === 'inline',
|
||||
}"
|
||||
x-show="dialog && (dialog.accept || dialog.reject)">
|
||||
<div x-show="dialog && dialog.accept" class="sm:order-last" x-ref="accept"></div>
|
||||
<div x-show="dialog && dialog.reject" x-ref="reject"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center"
|
||||
x-show="dialog && dialog.close && !dialog.accept && !dialog.accept"
|
||||
x-ref="close">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
38
resources/views/vendor/wireui/components/dropdown.blade.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<div class="relative inline-block text-left"
|
||||
x-data="wireui_dropdown"
|
||||
x-on:click.outside="close"
|
||||
x-on:keydown.escape.window="close"
|
||||
{{ $attributes->only('wire:key') }}>
|
||||
<div class="cursor-pointer focus:outline-none" x-on:click="toggle">
|
||||
@if (isset($trigger))
|
||||
{{ $trigger }}
|
||||
@else
|
||||
<x-dynamic-component
|
||||
:component="WireUi::component('icon')"
|
||||
class="w-4 h-4 text-secondary-500 hover:text-secondary-700
|
||||
dark:hover:text-secondary-600 transition duration-150 ease-in-out"
|
||||
name="dots-vertical"
|
||||
/>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div x-show="status"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
{{ $attributes->except('wire:key')->class([
|
||||
$getAlign(),
|
||||
$width,
|
||||
'z-30 absolute mt-2 whitespace-nowrap'
|
||||
]) }}
|
||||
style="display: none;"
|
||||
@unless($persistent) x-on:click="close" @endunless>
|
||||
<div class="relative {{ $height }} soft-scrollbar overflow-auto border border-secondary-200
|
||||
rounded-lg shadow-lg p-1 bg-white dark:bg-secondary-800 dark:border-secondary-600">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
7
resources/views/vendor/wireui/components/dropdown/header.blade.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<div class="@if($separator) border-t border-secondary-200 dark:border-secondary-600 @endif">
|
||||
<h6 {{ $attributes->merge(['class' => $classes]) }}>
|
||||
{!! $label !!}
|
||||
</h6>
|
||||
|
||||
{!! $slot !!}
|
||||
</div>
|
||||