{this.setupEvenOddClasses()}),this.currentlyReorderingStatus?e.disableReordering():(this.setupEvenOddClasses(),this.hideReorderColumnUnlessReorderingStatus&&(this.reorderDisplayColumn=!0),e.enableReordering())},cancelReorder(){this.hideReorderColumnUnlessReorderingStatus&&(this.reorderDisplayColumn=!1),e.disableReordering()},updateOrderedItems(){let i=document.getElementById(t),s=[];for(let a=1,r;r=i.rows[a];a++)s.push({[l]:r.getAttribute("rowpk"),[this.defaultReorderColumn]:a});e.storeReorder(s)},setupEvenOddClasses(){if(void 0===this.evenNotInOdd.length||0==this.evenNotInOdd.length||void 0===this.oddNotInEven.length||0==this.oddNotInEven.length){let e=document.getElementById(t).getElementsByTagName("tbody")[0],l=[],i=[];void 0!==e.rows[0]&&void 0!==e.rows[1]&&(l=Array.from(e.rows[0].classList),i=Array.from(e.rows[1].classList),this.evenNotInOdd=l.filter(e=>!i.includes(e)),this.oddNotInEven=i.filter(e=>!l.includes(e)),l=[],i=[])}},init(){}}))});
\ No newline at end of file
diff --git a/resources/js/partials/filter-date-range.js b/resources/js/partials/filter-date-range.js
index f28f42d93..aa630347e 100644
--- a/resources/js/partials/filter-date-range.js
+++ b/resources/js/partials/filter-date-range.js
@@ -8,7 +8,7 @@ function fpf() {
altFormat: filterConfig['altFormat'] ?? "F j, Y",
altInput: filterConfig['altInput'] ?? false,
allowInput: filterConfig['allowInput'] ?? false,
- allowInvalidPreload: true,
+ allowInvalidPreload: filterConfig['allowInvalidPreload'] ?? true,
ariaDateFormat: filterConfig['ariaDateFormat'] ?? "F j, Y",
clickOpens: true,
dateFormat: filterConfig['dateFormat'] ?? "Y-m-d",
@@ -30,15 +30,16 @@ function fpf() {
},
onChange: function (selectedDates, dateStr, instance) {
if (selectedDates.length > 1) {
- var startDate = dateStr.split(' ')[0];
- var endDate = dateStr.split(' ')[2];
+ var dates = dateStr.split(' ');
+
var wireDateArray = {};
window.childElementOpen = false;
window.filterPopoverOpen = false;
- wireDateArray = { 'minDate': startDate, 'maxDate': endDate };
+ wireDateArray = { 'minDate': dates[0], 'maxDate': (typeof dates[2] === "undefined") ? dates[0] : dates[2] };
wire.set('filterComponents.' + filterKey, wireDateArray);
}
- }
+
+ },
}),
setupWire() {
if (this.wireValues !== undefined) {
diff --git a/resources/js/partials/filter-date-range.min.js b/resources/js/partials/filter-date-range.min.js
index 7a7e5b158..e789c0b05 100644
--- a/resources/js/partials/filter-date-range.min.js
+++ b/resources/js/partials/filter-date-range.min.js
@@ -1 +1 @@
-function a(){Alpine.data('flatpickrFilter',(A,b,c,d,e)=>({wireValues:A.entangle(`filterComponents.${b}`),flatpickrInstance:flatpickr(d,{mode:'range',altFormat:c['altFormat']??'F j, Y',altInput:c['altInput']??!1,allowInput:c['allowInput']??!1,allowInvalidPreload:!0,ariaDateFormat:c['ariaDateFormat']??'F j, Y',clickOpens:!0,dateFormat:c['dateFormat']??'Y-m-d',defaultDate:c['defaultDate']??null,defaultHour:c['defaultHour']??12,defaultMinute:c['defaultMinute']??0,enableTime:c['enableTime']??!1,enableSeconds:c['enableSeconds']??!1,hourIncrement:c['hourIncrement']??1,locale:c['locale']??'en',minDate:c['earliestDate']??null,maxDate:c['latestDate']??null,minuteIncrement:c['minuteIncrement']??5,shorthandCurrentMonth:c['shorthandCurrentMonth']??!1,time_24hr:c['time_24hr']??!1,weekNumbers:c['weekNumbers']??!1,onOpen:function(){window.childElementOpen=!0},onChange:function(_,B,C){if(_.length>1){var D=B.split(' ')[0],E=B.split(' ')[2],f={};window.childElementOpen=window.filterPopoverOpen=!1;f={'minDate':D,'maxDate':E};A.set(`filterComponents.${b}`,f)}}}),setupWire(){if(this.wireValues!==void 0)if(this.wireValues.minDate!==void 0&&this.wireValues.maxDate!==void 0){this.flatpickrInstance.setDate([this.wireValues.minDate,this.wireValues.maxDate])}else this.flatpickrInstance.setDate([]);else this.flatpickrInstance.setDate([])},init(){this.setupWire();this.$watch('wireValues',value=>this.setupWire())}}))}export default a;
+function fpf(){Alpine.data("flatpickrFilter",(e,t,a,n,l)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(n,{mode:"range",altFormat:a.altFormat??"F j, Y",altInput:a.altInput??!1,allowInput:a.allowInput??!1,allowInvalidPreload:a.allowInvalidPreload??!0,ariaDateFormat:a.ariaDateFormat??"F j, Y",clickOpens:!0,dateFormat:a.dateFormat??"Y-m-d",defaultDate:a.defaultDate??null,defaultHour:a.defaultHour??12,defaultMinute:a.defaultMinute??0,enableTime:a.enableTime??!1,enableSeconds:a.enableSeconds??!1,hourIncrement:a.hourIncrement??1,locale:a.locale??"en",minDate:a.earliestDate??null,maxDate:a.latestDate??null,minuteIncrement:a.minuteIncrement??5,shorthandCurrentMonth:a.shorthandCurrentMonth??!1,time_24hr:a.time_24hr??!1,weekNumbers:a.weekNumbers??!1,onOpen:function(){window.childElementOpen=!0},onChange:function(a,n,l){if(a.length>1){var i=n.split(" "),r={};window.childElementOpen=!1,window.filterPopoverOpen=!1,r={minDate:i[0],maxDate:void 0===i[2]?i[0]:i[2]},e.set("filterComponents."+t,r)}}}),setupWire(){if(void 0!==this.wireValues){if(void 0!==this.wireValues.minDate&&void 0!==this.wireValues.maxDate){let e=[this.wireValues.minDate,this.wireValues.maxDate];this.flatpickrInstance.setDate(e)}else this.flatpickrInstance.setDate([])}else this.flatpickrInstance.setDate([])},init(){this.setupWire(),this.$watch("wireValues",e=>this.setupWire())}}))}export default fpf;
\ No newline at end of file
diff --git a/resources/views/components/includes/actions.blade.php b/resources/views/components/includes/actions.blade.php
new file mode 100644
index 000000000..422381ed1
--- /dev/null
+++ b/resources/views/components/includes/actions.blade.php
@@ -0,0 +1,12 @@
+merge($this->getActionWrapperAttributes())
+ ->class(['flex flex-cols justify-center' => $this->isTailwind && $this->getActionWrapperAttributes()['default-styling'] ?? true])
+ ->class(['' => $this->isTailwind && $this->getActionWrapperAttributes()['default-colors'] ?? true])
+ ->class(['d-flex flex-cols justify-center' => $this->isBootstrap && $this->getActionWrapperAttributes()['default-styling'] ?? true])
+ ->class(['' => $this->isBootstrap && $this->getActionWrapperAttributes()['default-colors'] ?? true])
+ ->except(['default-styling','default-colors'])
+ }} >
+ @foreach($this->getActions as $action)
+ {{ $action->render() }}
+ @endforeach
+
\ No newline at end of file
diff --git a/resources/views/components/table/tr/bulk-actions.blade.php b/resources/views/components/table/tr/bulk-actions.blade.php
index 7c7815b91..73201debf 100644
--- a/resources/views/components/table/tr/bulk-actions.blade.php
+++ b/resources/views/components/table/tr/bulk-actions.blade.php
@@ -97,7 +97,7 @@ class="btn btn-primary btn-sm"
- <
+
@lang('You have selected')
diff --git a/resources/views/components/tools/filters/date-range.blade.php b/resources/views/components/tools/filters/date-range.blade.php
index bdca66903..4d233b1e4 100644
--- a/resources/views/components/tools/filters/date-range.blade.php
+++ b/resources/views/components/tools/filters/date-range.blade.php
@@ -14,6 +14,7 @@
type="text"
x-ref="dateRangeInput"
x-on:click="init"
+ x-on:change="changedValue($refs.dateRangeInput.value)"
value="{{ $filter->getDateString(isset($this->appliedFilters[$filterKey]) ? $this->appliedFilters[$filterKey] : '') }}"
wire:key="{{ $filter->generateWireKey($tableName, 'dateRange') }}"
id="{{ $tableName }}-filter-dateRange-{{ $filterKey }}"
diff --git a/resources/views/datatable.blade.php b/resources/views/datatable.blade.php
index 88eadc23a..02cb79d0f 100644
--- a/resources/views/datatable.blade.php
+++ b/resources/views/datatable.blade.php
@@ -8,6 +8,11 @@
+ @if(method_exists($this,'hasActions') && $this->hasActions())
+
+ @endif
+
+
@if ($this->hasConfigurableAreaFor('before-tools'))
@include($this->getConfigurableAreaFor('before-tools'), $this->getParametersForConfigurableArea('before-tools'))
@endif
diff --git a/resources/views/includes/actions/button.blade.php b/resources/views/includes/actions/button.blade.php
new file mode 100644
index 000000000..878be5b0b
--- /dev/null
+++ b/resources/views/includes/actions/button.blade.php
@@ -0,0 +1,33 @@
+merge()
+ ->class(['justify-center text-center items-center inline-flex space-x-2 rounded-md border shadow-sm px-4 py-2 text-sm font-medium focus:ring focus:ring-opacity-50' => $isTailwind && $attributes['default-styling'] ?? true])
+ ->class(['focus:border-indigo-300 focus:ring-indigo-200' => $isTailwind && $attributes['default-colors'] ?? true])
+ ->class(['btn btn-sm btn-success' => $isBootstrap && $attributes['default-styling'] ?? true])
+ ->class(['' => $isBootstrap && $attributes['default-colors'] ?? true])
+ ->except(['default-styling', 'default-colors'])
+ }}
+ @if($action->hasWireAction())
+ {{ $action->getWireAction() }}="{{ $action->getWireActionParams() }}"
+ @endif
+ @if($action->getWireNavigateEnabled())
+ wire:navigate
+ @endif
+ >
+
+ @if($action->hasIcon() && $action->getIconRight())
+ {{ $action->getLabel() }}
+ getIconAttributes()
+ ->class(["ms-1 ". $action->getIcon() => $isBootstrap])
+ ->class(["ml-1 ". $action->getIcon() => $isTailwind])
+ ->except('default-styling')
+ }}
+ >
+ @elseif($action->hasIcon() && !$action->getIconRight())
+ getIconAttributes()
+ ->class(["ms-1 ". $action->getIcon() => $isBootstrap])
+ ->class(["ml-1 ". $action->getIcon() => $isTailwind])
+ ->except('default-styling')
+ }}
+ >
+ {{ $action->getLabel() }}
+ @endif
+
\ No newline at end of file
diff --git a/src/Traits/ComponentUtilities.php b/src/Traits/ComponentUtilities.php
index d065b25ed..815cd216e 100644
--- a/src/Traits/ComponentUtilities.php
+++ b/src/Traits/ComponentUtilities.php
@@ -45,7 +45,7 @@ trait ComponentUtilities
protected array $extraWithAvgs = [];
- protected bool $useComputedProperties = true;
+ protected bool $useComputedProperties = false;
/**
* Set any configuration options
diff --git a/src/Traits/HasAllTraits.php b/src/Traits/HasAllTraits.php
index d4287dc04..7f048a4a8 100644
--- a/src/Traits/HasAllTraits.php
+++ b/src/Traits/HasAllTraits.php
@@ -8,6 +8,7 @@ trait HasAllTraits
use WithTableHooks;
use WithLoadingPlaceholder;
use ComponentUtilities,
+ WithActions,
WithData,
WithColumns,
WithSorting,
diff --git a/src/Traits/WithActions.php b/src/Traits/WithActions.php
new file mode 100644
index 000000000..31f563a98
--- /dev/null
+++ b/src/Traits/WithActions.php
@@ -0,0 +1,48 @@
+ true, 'default-colors' => true];
+
+ protected function actions(): array
+ {
+ return [];
+ }
+
+ public function setActionWrapperAttributes(array $actionWrapperAttributes): self
+ {
+ $this->actionWrapperAttributes = [...['default-styling' => true, 'default-colors' => true], ...$actionWrapperAttributes];
+
+ return $this;
+ }
+
+ #[Computed]
+ public function getActionWrapperAttributes(): array
+ {
+ return [...['default-styling' => true, 'default-colors' => true], ...$this->actionWrapperAttributes];
+ }
+
+ #[Computed]
+ public function hasActions(): bool
+ {
+ return (new Collection($this->actions()))
+ ->filter(fn ($action) => $action instanceof Action)->count() > 0;
+ }
+
+ #[Computed]
+ public function getActions(): Collection
+ {
+ return (new Collection($this->actions()))
+ ->filter(fn ($action) => $action instanceof Action)
+ ->each(function (Action $action, int $key) {
+ $action->setTheme($this->getTheme());
+ });
+
+ }
+}
diff --git a/src/Traits/WithBulkActions.php b/src/Traits/WithBulkActions.php
index f7121cf2d..c36b87bb4 100644
--- a/src/Traits/WithBulkActions.php
+++ b/src/Traits/WithBulkActions.php
@@ -12,14 +12,17 @@ trait WithBulkActions
public bool $bulkActionsStatus = true;
+ // Entangled in JS
public bool $selectAll = false;
public array $bulkActions = [];
public array $bulkActionConfirms = [];
+ // Entangled in JS
public array $selected = [];
+ // Entangled in JS
public bool $hideBulkActionsWhenEmpty = false;
public ?string $bulkActionConfirmDefaultMessage;
@@ -46,6 +49,7 @@ trait WithBulkActions
protected array $bulkActionsMenuItemAttributes = ['default-colors' => true, 'default-styling' => true];
+ // Entangled in JS
public bool $delaySelectAll = false;
public function bulkActions(): array
diff --git a/src/Traits/WithFilters.php b/src/Traits/WithFilters.php
index 1499fa1d0..49c91da8c 100644
--- a/src/Traits/WithFilters.php
+++ b/src/Traits/WithFilters.php
@@ -5,6 +5,7 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Livewire\Attributes\Locked;
+use Rappasoft\LaravelLivewireTables\Events\FilterApplied;
use Rappasoft\LaravelLivewireTables\Traits\Configuration\FilterConfiguration;
use Rappasoft\LaravelLivewireTables\Traits\Helpers\FilterHelpers;
@@ -22,7 +23,7 @@ trait WithFilters
#[Locked]
public bool $filterPillsStatus = true;
- #[Locked]
+ // Entangled in JS
public bool $filterSlideDownDefaultVisible = false;
#[Locked]
@@ -31,8 +32,10 @@ trait WithFilters
#[Locked]
public int $filterCount;
+ // Set in JS
public array $filterComponents = [];
+ // Set in Frontend
public array $appliedFilters = [];
public array $filterGenericData = [];
@@ -101,6 +104,10 @@ public function updatedFilterComponents(string|array|null $value, string $filter
} elseif ($filter) {
$this->callHook('filterUpdated', ['filter' => $filter->getKey(), 'value' => $value]);
$this->callTraitHook('filterUpdated', ['filter' => $filter->getKey(), 'value' => $value]);
+ if ($this->getEventStatusFilterApplied() && $filter->getKey() != null && $value != null) {
+ event(new FilterApplied($this->getTableName(), $filter->getKey(), $value));
+ }
+ $this->dispatch('filter-was-set', tableName: $this->getTableName(), filterKey: $filter->getKey(), value: $value);
}
}
diff --git a/src/Traits/WithPagination.php b/src/Traits/WithPagination.php
index 28be97ee5..571c2b76c 100644
--- a/src/Traits/WithPagination.php
+++ b/src/Traits/WithPagination.php
@@ -32,10 +32,13 @@ trait WithPagination
#[Locked]
public bool $perPageVisibilityStatus = true;
+ // Entangled in JS
public array $paginationCurrentItems = [];
+ // Entangled in JS
public int $paginationCurrentCount = 0;
+ // Entangled in JS
public ?int $paginationTotalItemCount = null;
public array $numberOfPaginatorsRendered = [];
diff --git a/src/Traits/WithReordering.php b/src/Traits/WithReordering.php
index f9e833e4b..2251fe2c4 100644
--- a/src/Traits/WithReordering.php
+++ b/src/Traits/WithReordering.php
@@ -11,14 +11,19 @@ trait WithReordering
use ReorderingConfiguration,
ReorderingHelpers;
+ // Entangled in JS
public bool $reorderStatus = false;
+ // Entangled in JS
public bool $currentlyReorderingStatus = false;
+ // Entangled in JS
public bool $hideReorderColumnUnlessReorderingStatus = false;
+ // Entangled in JS
public bool $reorderDisplayColumn = false;
+ // Retrieved in JS
public string $defaultReorderColumn = 'sort';
public array $orderedItems = [];
diff --git a/src/Traits/WithSorting.php b/src/Traits/WithSorting.php
index 3c0127a31..78c8e9a73 100644
--- a/src/Traits/WithSorting.php
+++ b/src/Traits/WithSorting.php
@@ -31,7 +31,7 @@ trait WithSorting
public string $defaultSortingLabelDesc = 'Z-A';
- protected function queryStringWithSorting(): array
+ public function queryStringWithSorting(): array
{
if ($this->queryStringIsEnabled() && $this->sortingIsEnabled()) {
return [
diff --git a/src/Views/Action.php b/src/Views/Action.php
new file mode 100644
index 000000000..3c6570328
--- /dev/null
+++ b/src/Views/Action.php
@@ -0,0 +1,44 @@
+label = trim(__($label));
+ }
+
+ public static function make(?string $label = null): self
+ {
+ return new static($label);
+ }
+
+ public function render(): null|string|\Illuminate\Support\HtmlString|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
+ {
+ $view = view($this->getView())
+ ->withAction($this)
+ ->withIsBootstrap($this->isBootstrap())
+ ->withIsTailwind($this->isTailwind())
+ ->withAttributes($this->getActionAttributes());
+
+ return $view;
+ }
+}
diff --git a/src/Views/Actions/Action.php b/src/Views/Actions/Action.php
new file mode 100644
index 000000000..e30ce0114
--- /dev/null
+++ b/src/Views/Actions/Action.php
@@ -0,0 +1,13 @@
+ true, 'default-colors' => true];
+
+ public function setActionAttributes(array $actionAttributes): self
+ {
+ $this->actionAttributes = [...['default-styling' => true, 'default-colors' => true], ...$actionAttributes];
+
+ return $this;
+ }
+
+ public function getActionAttributes(): ComponentAttributeBag
+ {
+ $actionAttributes = [...['default-styling' => true, 'default-colors' => true], ...$this->actionAttributes];
+
+ if (! $this->hasWireAction() && method_exists($this, 'getRoute')) {
+ $actionAttributes['href'] = $this->getRoute();
+ } else {
+ $actionAttributes['href'] = '#';
+ }
+
+ return new ComponentAttributeBag($actionAttributes);
+ }
+}
diff --git a/src/Views/Traits/Actions/HasRoute.php b/src/Views/Traits/Actions/HasRoute.php
new file mode 100644
index 000000000..5b2696469
--- /dev/null
+++ b/src/Views/Traits/Actions/HasRoute.php
@@ -0,0 +1,29 @@
+route = $route;
+ $this->attributes['href'] = $route;
+
+ return $this;
+ }
+
+ public function setRoute(string $route): self
+ {
+ $this->route = $route;
+ $this->attributes['href'] = $route;
+
+ return $this;
+ }
+
+ public function getRoute(): string
+ {
+ return $this->route;
+ }
+}
diff --git a/src/Views/Traits/Core/HasAttributes.php b/src/Views/Traits/Core/HasAttributes.php
index 2978d5822..76220d4bc 100644
--- a/src/Views/Traits/Core/HasAttributes.php
+++ b/src/Views/Traits/Core/HasAttributes.php
@@ -5,7 +5,7 @@
use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\View\ComponentAttributeBag;
-use Rappasoft\LaravelLivewireTables\Views\{Column,Filter};
+use Rappasoft\LaravelLivewireTables\Views\{Action, Column,Filter};
trait HasAttributes
{
diff --git a/src/Views/Traits/Core/HasIcon.php b/src/Views/Traits/Core/HasIcon.php
new file mode 100644
index 000000000..3056e45c5
--- /dev/null
+++ b/src/Views/Traits/Core/HasIcon.php
@@ -0,0 +1,63 @@
+ true];
+
+ public bool $iconRight = true;
+
+ public function setIcon(string $icon): self
+ {
+ $this->icon = $icon;
+
+ return $this;
+ }
+
+ public function hasIcon(): bool
+ {
+ return isset($this->icon);
+ }
+
+ public function getIcon(): string
+ {
+ return $this->icon;
+ }
+
+ public function setIconAttributes(array $iconAttributes): self
+ {
+ $this->iconAttributes = [...['default-styling' => true], ...$iconAttributes];
+
+ return $this;
+ }
+
+ public function getIconAttributes(): ComponentAttributeBag
+ {
+ return new ComponentAttributeBag([...['default-styling' => true], ...$this->iconAttributes]);
+ }
+
+ public function getIconRight(): bool
+ {
+ return $this->iconRight ?? true;
+ }
+
+ public function setIconLeft(): self
+ {
+ $this->iconRight = false;
+
+ return $this;
+ }
+
+ public function setIconRight(): self
+ {
+ $this->iconRight = true;
+
+ return $this;
+ }
+}
diff --git a/src/Views/Traits/Core/HasLabel.php b/src/Views/Traits/Core/HasLabel.php
new file mode 100644
index 000000000..cfb2f13c5
--- /dev/null
+++ b/src/Views/Traits/Core/HasLabel.php
@@ -0,0 +1,13 @@
+label;
+ }
+}
diff --git a/src/Views/Traits/Core/HasTheme.php b/src/Views/Traits/Core/HasTheme.php
new file mode 100644
index 000000000..65ccfb885
--- /dev/null
+++ b/src/Views/Traits/Core/HasTheme.php
@@ -0,0 +1,35 @@
+theme = $theme;
+
+ return $this;
+ }
+
+ public function isTailwind(): bool
+ {
+ return $this->theme != 'bootstrap-4' && $this->theme != 'bootstrap-5';
+ }
+
+ public function isBootstrap(): bool
+ {
+ return $this->theme == 'bootstrap-4' || $this->theme == 'bootstrap-5';
+ }
+
+ public function isBootstrap4(): bool
+ {
+ return $this->theme == 'bootstrap-4';
+ }
+
+ public function isBootstrap5(): bool
+ {
+ return $this->theme == 'bootstrap-5';
+ }
+}
diff --git a/src/Views/Traits/Core/HasWireActions.php b/src/Views/Traits/Core/HasWireActions.php
new file mode 100644
index 000000000..04d96a050
--- /dev/null
+++ b/src/Views/Traits/Core/HasWireActions.php
@@ -0,0 +1,67 @@
+shouldWireNavigate = true;
+
+ return $this;
+ }
+
+ public function getWireNavigateEnabled(): bool
+ {
+ return $this->shouldWireNavigate;
+ }
+
+ public function hasWireAction(): bool
+ {
+ return isset($this->wireAction);
+ }
+
+ public function getWireAction(): string
+ {
+ return $this->wireAction;
+ }
+
+ public function setWireAction(string $wireAction): self
+ {
+ $this->wireAction = $wireAction;
+
+ return $this;
+ }
+
+ public function hasWireActionParams(): bool
+ {
+ return isset($this->wireActionParams);
+ }
+
+ public function getWireActionParams(): string
+ {
+ return $this->wireActionParams;
+ }
+
+ public function setWireActionParams(string $wireActionParams): self
+ {
+ $this->wireActionParams = $wireActionParams;
+
+ return $this;
+ }
+
+ public function setWireActionDispatchParams(string $wireActionParams): self
+ {
+ $this->setWireActionParams('$dispatch('.$wireActionParams.')');
+
+ return $this;
+ }
+}
diff --git a/tests/Traits/Visuals/FilterVisualsTest.php b/tests/Traits/Visuals/FilterVisualsTest.php
index 221ee7836..5b34da945 100644
--- a/tests/Traits/Visuals/FilterVisualsTest.php
+++ b/tests/Traits/Visuals/FilterVisualsTest.php
@@ -49,6 +49,20 @@ public function test_filter_pills_show_when_enabled(): void
->assertSee('Applied Filters');
}
+ public function test_event_dispatched_when_filterComponents_set(): void
+ {
+ Livewire::test(PetsTable::class)
+ ->set('filterComponents.breed', [1])
+ ->assertDispatched('filter-was-set');
+ }
+
+ public function test_event_dispatched_when_setFilter_dispatched(): void
+ {
+ Livewire::test(PetsTable::class)
+ ->dispatch('setFilter', filterKey: 'breed', value: [1])
+ ->assertDispatched('filter-was-set');
+ }
+
public function test_filter_pills_show_when_visible(): void
{
Livewire::test(PetsTable::class)
diff --git a/tests/Views/Actions/ActionTest.php b/tests/Views/Actions/ActionTest.php
new file mode 100644
index 000000000..f33d30bcb
--- /dev/null
+++ b/tests/Views/Actions/ActionTest.php
@@ -0,0 +1,346 @@
+setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->wireNavigate()
+ ->route('dashboard2');
+
+ $this->assertSame('Update Summaries', $action->getLabel());
+ }
+
+ public function test_can_get_action_button_icon(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes([
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'default-styling' => true,
+ 'default-colors' => true,
+ ])
+ ->wireNavigate()
+ ->route('dashboard2');
+ $this->assertFalse($action->hasIcon());
+ $action->setIcon('fas fa-minus');
+ $this->assertTrue($action->hasIcon());
+ $this->assertSame('fas fa-minus', $action->getIcon());
+ $this->assertSame('fas fa-minus', $action->icon);
+
+ }
+
+ public function test_can_get_action_button_icon_attributes(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes([
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'default-styling' => true,
+ 'default-colors' => true,
+ ])
+ ->wireNavigate()
+ ->route('dashboard2');
+ $this->assertFalse($action->hasIcon());
+
+ $action->setIconAttributes(['class' => 'font-sm text-sm']);
+ $bag = new \Illuminate\View\ComponentAttributeBag(['default-styling' => true, 'class' => 'font-sm text-sm']);
+
+ $this->assertSame($bag->getAttributes(), $action->getIconAttributes()->getAttributes());
+ $this->assertSame(['default-styling' => true, 'class' => 'font-sm text-sm'], $action->iconAttributes);
+
+ }
+
+ public function test_can_get_action_button_route(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->wireNavigate();
+ $this->assertSame('#', $action->getRoute());
+ $this->assertSame('#', $action->route);
+ $action->route('dashboard2');
+ $this->assertSame('dashboard2', $action->route);
+
+ $this->assertSame('dashboard2', $action->getRoute());
+ $action->setRoute('dashboard4');
+ $this->assertSame('dashboard4', $action->getRoute());
+ }
+
+ public function test_can_set_action_button_to_wire_navigate(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->route('dashboard2');
+ $this->assertFalse($action->getWireNavigateEnabled());
+ $action->wireNavigate();
+ $this->assertTrue($action->getWireNavigateEnabled());
+ }
+
+ public function test_can_get_action_button_action_attributes(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->wireNavigate()
+ ->route('dashboard2');
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => true,
+ 'href' => 'dashboard2',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+
+ $action->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true]);
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => true,
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'href' => 'dashboard2',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+
+ $action->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true]);
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => true,
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'href' => 'dashboard2',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+
+ $action->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => false]);
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => false,
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'href' => 'dashboard2',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+
+ $action->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-colors' => false]);
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => false,
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'href' => 'dashboard2',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+
+ }
+
+ public function test_can_check_has_wire_action(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm']);
+
+ $this->assertFalse($action->hasWireAction());
+
+ $action->setWireAction('wire:click')
+ ->setWireActionParams("\$dispatch('openModal', { component: 'test-modal', arguments: JSON.parse('{\u0022modelID\u0022:\u0022\u0022}') })");
+
+ $this->assertTrue($action->hasWireAction());
+ }
+
+ public function test_can_get_wire_action(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->setWireAction('wire:click')
+ ->setWireActionParams("\$dispatch('openModal', { component: 'test-modal', arguments: JSON.parse('{\u0022modelID\u0022:\u0022\u0022}') })");
+
+ $this->assertTrue($action->hasWireAction());
+
+ $this->assertSame('wire:click', $action->getWireAction());
+
+ }
+
+ public function test_can_get_wire_action_dispatch(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->setWireAction('wire:click')
+ ->setWireActionDispatchParams("'openModal', { component: 'test-modal', arguments: JSON.parse('{\u0022modelID\u0022:\u0022\u0022}') }");
+ $this->assertTrue($action->hasWireAction());
+
+ $this->assertSame('wire:click', $action->getWireAction());
+ $this->assertSame("\$dispatch('openModal', { component: 'test-modal', arguments: JSON.parse('{\u0022modelID\u0022:\u0022\u0022}') })", $action->getWireActionParams());
+
+ }
+
+ public function test_can_get_wire_action_params(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->setWireAction('wire:click')
+ ->setWireActionParams('testactionparams');
+
+ $this->assertTrue($action->hasWireActionParams());
+ $this->assertTrue($action->hasWireAction());
+
+ $this->assertSame('testactionparams', $action->getWireActionParams());
+ $this->assertSame('wire:click', $action->getWireAction());
+
+ }
+
+ public function test_can_set_action_wrapper_attributes(): void
+ {
+ $petsTable = (new class extends PetsTable
+ {
+ use \Rappasoft\LaravelLivewireTables\Traits\WithActions;
+
+ public function configure(): void
+ {
+ $this->setPrimaryKey('id');
+ }
+
+ public function bulkActions(): array
+ {
+ return ['exportBulk' => 'exportBulk'];
+ }
+
+ public function exportBulk($items)
+ {
+ return $items;
+ }
+ });
+ $this->assertSame(['default-styling' => true, 'default-colors' => true], $petsTable->getActionWrapperAttributes());
+ $petsTable->setActionWrapperAttributes(['default-styling' => false, 'class' => 'bg-blue-500']);
+ $this->assertSame([
+ 'default-styling' => false,
+ 'default-colors' => true,
+ 'class' => 'bg-blue-500',
+ ], $petsTable->getActionWrapperAttributes());
+
+ $petsTable->setActionWrapperAttributes(['default-colors' => false, 'class' => 'bg-red-500']);
+ $this->assertSame([
+ 'default-styling' => true,
+ 'default-colors' => false,
+ 'class' => 'bg-red-500',
+ ], $petsTable->getActionWrapperAttributes());
+
+ }
+
+ public function test_can_check_that_route_is_appended_to_attributes(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->route('dashboard22');
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => true,
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'href' => 'dashboard22',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+ }
+
+ public function test_can_check_that_route_is_not_appended_to_attributes_with_wireaction(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->route('dashboard22')
+ ->setWireAction('wire:click')
+ ->setWireActionParams('testactionparams');
+ $this->assertSame((new ComponentAttributeBag([
+ 'default-styling' => true,
+ 'default-colors' => true,
+ 'class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'href' => '#',
+ ]))->getAttributes(), $action->getActionAttributes()->getAttributes());
+ }
+
+ public function test_can_check_has_actions(): void
+ {
+ $petsTable = (new class extends PetsTable
+ {
+ use \Rappasoft\LaravelLivewireTables\Traits\WithActions;
+
+ public function actions(): array
+ {
+ return [
+ \Rappasoft\LaravelLivewireTables\Views\Actions\Action::make('Test Edit 1')
+ ->setRoute('dashboard24'),
+ ];
+ }
+
+ public function configure(): void
+ {
+ $this->setPrimaryKey('id');
+ }
+
+ public function bulkActions(): array
+ {
+ return ['exportBulk' => 'exportBulk'];
+ }
+
+ public function exportBulk($items)
+ {
+ return $items;
+ }
+ });
+ $this->assertTrue($petsTable->hasActions());
+
+ $this->assertSame(1, $petsTable->getActions()->count());
+
+ }
+
+ public function test_can_set_icon_to_right_default(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->setWireAction('wire:click')
+ ->setWireActionParams('testactionparams');
+ $this->assertTrue($action->getIconRight());
+ }
+
+ public function test_can_set_icon_to_left(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->setIconLeft()
+ ->setWireAction('wire:click')
+ ->setWireActionParams('testactionparams');
+ $this->assertFalse($action->getIconRight());
+ }
+
+ public function test_can_set_icon_to_right(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800', 'default-styling' => true, 'default-colors' => true])
+ ->setIcon('fas fa-minus')
+ ->setIconAttributes(['class' => 'font-sm text-sm'])
+ ->setWireAction('wire:click')
+ ->setWireActionParams('testactionparams')
+ ->setIconLeft()
+ ->setIconRight();
+ $this->assertTrue($action->getIconRight());
+ }
+
+ public function test_action_renders_correctly(): void
+ {
+ $action = Action::make('Update Summaries')
+ ->setActionAttributes(['class' => 'dark:bg-green-500 dark:text-white dark:border-green-600 dark:hover:border-green-900 dark:hover:bg-green-800',
+ 'default-styling' => true,
+ 'default-colors' => true]
+ )
+ ->route('dashboard22');
+
+ $this->assertStringContainsString('render());
+ }
+}