From 2c0d9c92a73f6feef027854414588df6ed1b74ac Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Fri, 29 Dec 2023 23:36:06 +0000 Subject: [PATCH 001/338] Update ChangeLog - Showing as UNRELEASED rather than 3.1.8 (#1621) * Fix CHANGELOG.md missing release --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02f417dde..a1d313638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ All notable changes to `laravel-livewire-tables` will be documented in this file -## UNRELEASED +## [v3.1.9] - 2023-12-29 +### Bug Fixes +- Fix CHANGELOG.md missing release + +## [v3.1.8] - 2023-12-29 ### New Features - Add capability to show/hide Pagination Details (Rows x of y) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1618 - Add perPage to queryString by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1618 From 05a3d41d082260457b4436932e82f77eec455c74 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sat, 2 Mar 2024 00:24:26 +0000 Subject: [PATCH 002/338] Fix for Collapsing Columns - Multiple Tables, Single Page (#1678) (#1679) --- CHANGELOG.md | 4 ++++ resources/views/components/table/collapsed-columns.blade.php | 2 +- .../views/components/table/td/collapsed-columns.blade.php | 4 ++-- src/LaravelLivewireTablesServiceProvider.php | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 883695ef2..e3e60a676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.2.4] - 2024-03-01 +### Bug Fixes +- Collapsing Columns fix when multiple tables are displayed on a page by @lrljoe + ## [v3.2.3] - 2024-03-01 ### New Features - Add capability to customise colors/styling on the Pagination Per-Page Dropdown by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1677 diff --git a/resources/views/components/table/collapsed-columns.blade.php b/resources/views/components/table/collapsed-columns.blade.php index 1e4aea9a5..49569281a 100644 --- a/resources/views/components/table/collapsed-columns.blade.php +++ b/resources/views/components/table/collapsed-columns.blade.php @@ -24,7 +24,7 @@ @@ -36,7 +36,7 @@ @if (! $hidden) diff --git a/src/Views/Columns/WireLinkColumn.php b/src/Views/Columns/WireLinkColumn.php new file mode 100644 index 000000000..343f28ef9 --- /dev/null +++ b/src/Views/Columns/WireLinkColumn.php @@ -0,0 +1,45 @@ +label(fn () => null); + } + + public function getContents(Model $row): null|string|\Illuminate\Support\HtmlString|DataTableConfigurationException|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + { + if (! $this->hasTitleCallback()) { + throw new DataTableConfigurationException('You must specify a title callback for a WireLink column.'); + } + + if (! $this->hasActionCallback()) { + throw new DataTableConfigurationException('You must specify an action callback for a WireLink column.'); + } + + return view($this->getView()) + ->withColumn($this) + ->withTitle(app()->call($this->getTitleCallback(), ['row' => $row])) + ->withPath(app()->call($this->getActionCallback(), ['row' => $row])) + ->withAttributes($this->hasAttributesCallback() ? app()->call($this->getAttributesCallback(), ['row' => $row]) : []); + } +} diff --git a/src/Views/Traits/Configuration/WireLinkColumnConfiguration.php b/src/Views/Traits/Configuration/WireLinkColumnConfiguration.php new file mode 100644 index 000000000..238b36fbf --- /dev/null +++ b/src/Views/Traits/Configuration/WireLinkColumnConfiguration.php @@ -0,0 +1,5 @@ +actionCallback = $callback; + + return $this; + } + + public function getActionCallback(): ?callable + { + return $this->actionCallback; + } + + public function hasActionCallback(): bool + { + return $this->actionCallback !== null; + } +} diff --git a/src/Views/Traits/Core/HasConfirmation.php b/src/Views/Traits/Core/HasConfirmation.php new file mode 100644 index 000000000..9a8597947 --- /dev/null +++ b/src/Views/Traits/Core/HasConfirmation.php @@ -0,0 +1,28 @@ +confirmMessage = $confirmMessage; + + return $this; + } + + public function hasConfirmMessage(): bool + { + return isset($this->confirmMessage); + } + + public function getConfirmMessage(): string + { + return $this->confirmMessage; + } +} diff --git a/src/Views/Traits/Helpers/WireLinkColumnHelpers.php b/src/Views/Traits/Helpers/WireLinkColumnHelpers.php new file mode 100644 index 000000000..f24c7e460 --- /dev/null +++ b/src/Views/Traits/Helpers/WireLinkColumnHelpers.php @@ -0,0 +1,5 @@ +assertSame('Name', $column->getTitle()); + } + + public function test_can_not_infer_field_name_from_title_if_no_from(): void + { + $column = WireLinkColumn::make('My Title'); + + $this->assertNull($column->getField()); + } + + public function test_can_not_render_field_if_no_title_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + + WireLinkColumn::make('Name')->getContents(Pet::find(1)); + } + + public function test_can_not_render_field_if_no_action_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + + WireLinkColumn::make('Name')->title(fn ($row) => 'Edit')->getContents(Pet::find(1)); + } + + public function test_can_render_field_if_title_and_action_callback(): void + { + $column = WireLinkColumn::make('Name')->title(fn ($row) => 'Edit')->action(fn ($row) => 'delete("'.$row->id.'")')->getContents(Pet::find(1)); + + $this->assertNotEmpty($column); + } + + public function test_can_render_field_if_confirm_set(): void + { + $column = WireLinkColumn::make('Name')->title(fn ($row) => 'Edit')->action(fn ($row) => 'delete("'.$row->id.'")')->confirmMessage('Test')->getContents(Pet::find(1)); + + $this->assertNotEmpty($column); + } + + public function test_can_add_confirm_message(): void + { + $column = WireLinkColumn::make('Name', 'name'); + + $this->assertFalse($column->hasConfirmMessage()); + + $column->confirmMessage('Test'); + + $this->assertTrue($column->hasConfirmMessage()); + + $this->assertSame('Test', $column->getConfirmMessage()); + } +} From 2593615745cf91e9f21b71dc3fd8c0a4f3730259 Mon Sep 17 00:00:00 2001 From: lrljoe Date: Thu, 11 Jul 2024 00:44:48 +0000 Subject: [PATCH 031/338] Fix styling --- src/Traits/WithBulkActions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Traits/WithBulkActions.php b/src/Traits/WithBulkActions.php index 5411f230a..37a5cd111 100644 --- a/src/Traits/WithBulkActions.php +++ b/src/Traits/WithBulkActions.php @@ -40,7 +40,7 @@ trait WithBulkActions public bool $clearSelectedOnFilter = true; - public function bulkActions(): array + public function bulkActions(): array { return property_exists($this, 'bulkActions') ? $this->bulkActions : []; } From 3a260dc55eb779dadd7b25b5b057c6b91093d47c Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Thu, 11 Jul 2024 01:48:46 +0100 Subject: [PATCH 032/338] Development to Master (3.3.0) (#1764) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adjust for HTML Columns * Update ChangeLog and SP * fix: Apply cursor pointer only on clickable columns when using Bootst… (#1742) * Ensure HTML Columns Return HTML Correctly (#1737) * Adjust for HTML Columns * fix: Apply cursor pointer only on clickable columns when using Bootstrap --------- Co-authored-by: Joe <104938042+lrljoe@users.noreply.github.com> * Fix styling * Fix hide bulk actions when empty not reflecting in frontend (#1747) * Fix issue with Hide Bulk Actions When Empty not reflecting in frontend * Fix styling * Add development branch into tests --------- Co-authored-by: lrljoe * Change Return Type for attributes() to static (#1749) * Switch to using Composer\InstalledVersions for AboutCommand to reduce necessity to update ServiceProvider with each update (#1748) * Two improvements to improve typehinting, migrate to larastan/larastan, cleanup of test (#1750) * Add ArrayColumn (BETA) (#1751) * Add ArrayColumn * Fix styling --------- Co-authored-by: lrljoe * Always hide bulk actions option (#1752) * Add option to "Always Hide Bulk Actions" * Fix styling * Fix test function name clash --------- Co-authored-by: lrljoe * Optionally disable count for simple pagination (#1755) * Add option for setShouldRetrieveTotalItemCountStatus * Fix styling --------- Co-authored-by: lrljoe * Update ChangeLog For 3.2.8 Release (#1754) * Update ChangeLog for 3.2.8 * Add release date * Fix phpstan unescaped | * Fix missing typehints (#1757) * Add additional typehints * Fix styling * Add filterCollection typehint * Fix styling * trUrlCallback fixes * Use Collection rather than collect() helper * Fix styling * Add ignore for "Unable to resolve the template type" for Illuminate Collection, add typehint for empty * Add ignore for $model has no defined type (allows for non Eloquent Model to be used longer term) * Adjust concurrency * Adjust Test * Adjust Again * Adjust PHPStan * Add Max Parallel * Use v4 of checkout/cache * Run one at a time * Add Clear Cache Workflow * Fix * Migrate to v4 and adjust workflows * Adjust workflow run rules * Adjust Run-Tests to separate L10 and L11 jobs * Adjust run-tests * Adjust Test * Add Laravel matrix * Adjust Concurrency * Adjust * Adjust Pull Jobs to Match Push jobs --------- Co-authored-by: lrljoe * Add CountColumn, simpler adding of WithCounts, With (#1761) * Initial Commit * Adjust CountColumn * Add ExtraWiths * Add AggregateColumn * Add SumColumn * Update Docs - Add Column Types Section * Add exceptions for empty data source, add standard tests * Ensure pcov runs on push to master/development/develop * Update to use codecov v4 --------- Co-authored-by: lrljoe * Add Option to Retain Selected when Searching/Filtering (#1762) * Initial Commit for Retaining Selected * Update Test for Search/Filter --------- Co-authored-by: lrljoe * Add WireLink Column (#1763) * Add WireLinkColumn * Add Tests for WireLinkColumn --------- Co-authored-by: lrljoe * Fix styling --------- Co-authored-by: Matt Pickering Co-authored-by: lrljoe --- .github/workflows/run-tests-pcov-pull.yml | 9 +- .gitignore | 4 +- CHANGELOG.md | 4 + coverage.xml | 2894 ----------------- docs/bulk-actions/_index.md | 2 +- docs/bulk-actions/available-methods.md | 72 + docs/column-types/_index.md | 4 + docs/column-types/array_column.md | 22 + docs/column-types/avg_column.md | 14 + docs/column-types/boolean_columns.md | 81 + docs/column-types/button_group_column.md | 34 + docs/column-types/color_columns.md | 41 + docs/column-types/component_column.md | 28 + docs/column-types/count_column.md | 14 + docs/column-types/date_columns.md | 28 + docs/column-types/image_columns.md | 26 + docs/column-types/link_columns.md | 24 + .../column-types/livewire_component_column.md | 5 + docs/column-types/sum_column.md | 14 + docs/column-types/wire_link_column.md | 37 + docs/columns/other-column-types.md | 241 +- docs/examples/_index.md | 2 +- docs/filter-types/_index.md | 2 +- docs/filters/_index.md | 2 +- docs/footer/_index.md | 2 +- docs/misc/_index.md | 2 +- docs/pagination/_index.md | 2 +- docs/reordering/_index.md | 2 +- docs/rows/_index.md | 2 +- docs/search/_index.md | 2 +- docs/secondary-header/_index.md | 2 +- docs/sorting/_index.md | 2 +- .../includes/columns/wire-link.blade.php | 9 + src/Traits/ComponentUtilities.php | 8 + .../BulkActionsConfiguration.php | 42 + .../Configuration/ComponentConfiguration.php | 56 + src/Traits/Helpers/BulkActionsHelpers.php | 10 + src/Traits/Helpers/ColumnHelpers.php | 28 + src/Traits/Helpers/ComponentHelpers.php | 40 + src/Traits/WithBulkActions.php | 4 + src/Traits/WithData.php | 26 +- src/Traits/WithFilters.php | 8 +- src/Traits/WithSearch.php | 8 +- src/Views/Columns/AggregateColumn.php | 23 + src/Views/Columns/AvgColumn.php | 18 + src/Views/Columns/CountColumn.php | 18 + src/Views/Columns/SumColumn.php | 18 + src/Views/Columns/WireLinkColumn.php | 45 + .../AggregateColumnConfiguration.php | 59 + .../ArrayColumnConfiguration.php | 10 + .../WireLinkColumnConfiguration.php | 5 + src/Views/Traits/Core/HasActionCallback.php | 28 + src/Views/Traits/Core/HasConfirmation.php | 28 + .../Traits/Helpers/AggregateColumnHelpers.php | 46 + .../Traits/Helpers/ArrayColumnHelpers.php | 4 - .../Traits/Helpers/WireLinkColumnHelpers.php | 5 + src/Views/Traits/IsAggregateColumn.php | 14 + tests/Attributes/AggregateColumnProvider.php | 13 + tests/Http/Livewire/SpeciesTable.php | 38 + tests/TestCase.php | 22 +- .../ComponentConfigurationTest.php | 102 + .../Traits/Helpers/BulkActionsHelpersTest.php | 70 + tests/Views/Columns/ArrayColumnTest.php | 87 + tests/Views/Columns/AvgColumnTest.php | 111 + tests/Views/Columns/CountColumnTest.php | 50 + tests/Views/Columns/SumColumnTest.php | 111 + tests/Views/Columns/WireLinkColumnTest.php | 66 + 67 files changed, 1691 insertions(+), 3159 deletions(-) delete mode 100644 coverage.xml create mode 100644 docs/column-types/_index.md create mode 100644 docs/column-types/array_column.md create mode 100644 docs/column-types/avg_column.md create mode 100644 docs/column-types/boolean_columns.md create mode 100644 docs/column-types/button_group_column.md create mode 100644 docs/column-types/color_columns.md create mode 100644 docs/column-types/component_column.md create mode 100644 docs/column-types/count_column.md create mode 100644 docs/column-types/date_columns.md create mode 100644 docs/column-types/image_columns.md create mode 100644 docs/column-types/link_columns.md create mode 100644 docs/column-types/livewire_component_column.md create mode 100644 docs/column-types/sum_column.md create mode 100644 docs/column-types/wire_link_column.md create mode 100644 resources/views/includes/columns/wire-link.blade.php create mode 100644 src/Views/Columns/AggregateColumn.php create mode 100644 src/Views/Columns/AvgColumn.php create mode 100644 src/Views/Columns/CountColumn.php create mode 100644 src/Views/Columns/SumColumn.php create mode 100644 src/Views/Columns/WireLinkColumn.php create mode 100644 src/Views/Traits/Configuration/AggregateColumnConfiguration.php create mode 100644 src/Views/Traits/Configuration/WireLinkColumnConfiguration.php create mode 100644 src/Views/Traits/Core/HasActionCallback.php create mode 100644 src/Views/Traits/Core/HasConfirmation.php create mode 100644 src/Views/Traits/Helpers/AggregateColumnHelpers.php create mode 100644 src/Views/Traits/Helpers/WireLinkColumnHelpers.php create mode 100644 src/Views/Traits/IsAggregateColumn.php create mode 100644 tests/Attributes/AggregateColumnProvider.php create mode 100644 tests/Http/Livewire/SpeciesTable.php create mode 100644 tests/Views/Columns/ArrayColumnTest.php create mode 100644 tests/Views/Columns/AvgColumnTest.php create mode 100644 tests/Views/Columns/CountColumnTest.php create mode 100644 tests/Views/Columns/SumColumnTest.php create mode 100644 tests/Views/Columns/WireLinkColumnTest.php diff --git a/.github/workflows/run-tests-pcov-pull.yml b/.github/workflows/run-tests-pcov-pull.yml index 3a24a91ba..ef73e4b42 100644 --- a/.github/workflows/run-tests-pcov-pull.yml +++ b/.github/workflows/run-tests-pcov-pull.yml @@ -1,6 +1,11 @@ name: run-tests-pcov-pull on: + push: + branches: + - 'develop' + - 'development' + - 'master' pull_request: branches: - 'develop' @@ -18,7 +23,7 @@ jobs: laravel: [10] stability: [prefer-dist] - name: PCOV-PULL - ${{ matrix.os }} - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} + name: PCOV - ${{ matrix.os }} - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} env: extensionKey: phpextensions-${{ matrix.os }}-P${{ matrix.php }}-withpcov extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pcov,pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo @@ -86,7 +91,7 @@ jobs: run: php ./vendor/bin/paratest --cache-directory=".phpunit.cache/code-coverage" --strict-coverage --coverage-clover ./coverage.xml --processes=4 - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.gitignore b/.gitignore index a36981422..d3e4b8ca4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ phpunit.xml.dist.dev .history/* .env phpunit.xml.bak -phpstan.txt \ No newline at end of file +phpstan.txt +coverage.xml +./tmp/** \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6993a50e3..940323134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.3.0] - 2024-07-12 +### New Features +- Add new columns (ArrayColumn, AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1761 + ## [v3.2.8] - 2024-07-03 ### Bug Fixes - Fix hide bulk actions when empty not reflecting in frontend by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1747 diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index 7428459d2..000000000 --- a/coverage.xml +++ /dev/null @@ -1,2894 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/bulk-actions/_index.md b/docs/bulk-actions/_index.md index f76ab3244..48cb71a17 100644 --- a/docs/bulk-actions/_index.md +++ b/docs/bulk-actions/_index.md @@ -1,4 +1,4 @@ --- title: Bulk Actions -weight: 9 +weight: 10 --- diff --git a/docs/bulk-actions/available-methods.md b/docs/bulk-actions/available-methods.md index ad98e1999..512394c23 100644 --- a/docs/bulk-actions/available-methods.md +++ b/docs/bulk-actions/available-methods.md @@ -274,3 +274,75 @@ public function configure(): void $this->setShouldAlwaysHideBulkActionsDropdownOptionDisabled(); } ``` + + +## setClearSelectedOnSearch + +By default, any selected items for Bulk Actions are cleared upon searching. You may configure this behaviour here. + +```php +public function configure(): void +{ + $this->setClearSelectedOnSearch(true); +} +``` + + +## setClearSelectedOnSearchEnabled + +By default, any selected items for Bulk Actions are cleared upon searching. This enables this behaviour. + +```php +public function configure(): void +{ + $this->setClearSelectedOnSearchEnabled(); +} +``` + + +## setClearSelectedOnSearchDisabled + +By default, any selected items for Bulk Actions are cleared upon searching. This disables this behaviour, ensuring that selected items are retained after searching. + +```php +public function configure(): void +{ + $this->setClearSelectedOnSearchDisabled(); +} +``` + + +## setClearSelectedOnFilter + +By default, any selected items for Bulk Actions are cleared upon filtering. You may configure this behaviour here. + +```php +public function configure(): void +{ + $this->setClearSelectedOnFilter(true); +} +``` + + +## setClearSelectedOnFilterEnabled + +By default, any selected items for Bulk Actions are cleared upon filtering. This enables this behaviour. + +```php +public function configure(): void +{ + $this->setClearSelectedOnFilterEnabled(); +} +``` + + +## setClearSelectedOnFilterDisabled + +By default, any selected items for Bulk Actions are cleared upon filtering. This disables this behaviour, ensuring that selected items are retained after filtering. + +```php +public function configure(): void +{ + $this->setClearSelectedOnFilterDisabled(); +} +``` diff --git a/docs/column-types/_index.md b/docs/column-types/_index.md new file mode 100644 index 000000000..821c869a2 --- /dev/null +++ b/docs/column-types/_index.md @@ -0,0 +1,4 @@ +--- +title: Column Types +weight: 5 +--- diff --git a/docs/column-types/array_column.md b/docs/column-types/array_column.md new file mode 100644 index 000000000..1571da2b9 --- /dev/null +++ b/docs/column-types/array_column.md @@ -0,0 +1,22 @@ +--- +title: Array Columns (beta) +weight: 1 +--- + +Array columns provide an easy way to work with and display an array of data from a field. + +``` +ArrayColumn::make('notes', 'name') + ->data(fn($value, $row) => ($row->notes)) + ->outputFormat(fn($index, $value) => "".$value->name."") + ->separator('
') + ->sortable(), +``` + +### Empty Value +You may define the default/empty value using the "emptyValue" method + +``` +ArrayColumn::make('notes', 'name') + ->emptyValue('Unknown'), +``` \ No newline at end of file diff --git a/docs/column-types/avg_column.md b/docs/column-types/avg_column.md new file mode 100644 index 000000000..faa653ca2 --- /dev/null +++ b/docs/column-types/avg_column.md @@ -0,0 +1,14 @@ +--- +title: Avg Columns (beta) +weight: 2 +--- + +Avg columns provide an easy way to display the "Average" of a field on a relation. + +``` + AvgColumn::make('Average Related User Age') + ->setDataSource('users','age') + ->sortable(), +``` + +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file diff --git a/docs/column-types/boolean_columns.md b/docs/column-types/boolean_columns.md new file mode 100644 index 000000000..32a759918 --- /dev/null +++ b/docs/column-types/boolean_columns.md @@ -0,0 +1,81 @@ +--- +title: Boolean Columns +weight: 3 +--- + +Boolean columns are good if you have a column type that is a true/false, or 0/1 value. + +For example: + +```php +BooleanColumn::make('Active') +``` + +Would yield: + +![Boolean Column](https://imgur.com/LAk6gHY.png) + +### Using your own view + +If you don't want to use the default view and icons you can set your own: + +```php +BooleanColumn::make('Active') + ->setView('my.active.view') +``` + +You will have access to `$component`, `$status`, and `$successValue`. + +To help you better understand, this is the Tailwind implementation of BooleanColumn: + +```html +@if ($status) + + + +@else + + + +@endif +``` + +### Setting the truthy value + +If you want the false value to be the green option, you can set: + +```php +BooleanColumn::make('Active') + ->setSuccessValue(false); // Makes false the 'successful' option +``` + +That would swap the colors of the icons in the image above. + +### Setting the status value + +By default, the `$status` is set to: + +```php +(bool)$value === true +``` + +You can override this functionality: + +```php +BooleanColumn::make('Active') + // Note: Parameter `$row` available as of v2.4 + ->setCallback(function(string $value, $row) { + // Figure out what makes $value true + }), +``` + +### Different types of boolean display + +By default, the BooleanColumn displays icons. + +If you would like the BooleanColumn to display a plain Yes/No, you can set: + +```php +BooleanColumn::make('Active') + ->yesNo() +``` diff --git a/docs/column-types/button_group_column.md b/docs/column-types/button_group_column.md new file mode 100644 index 000000000..5a3390049 --- /dev/null +++ b/docs/column-types/button_group_column.md @@ -0,0 +1,34 @@ +--- +title: Button Group Columns +weight: 4 +--- + +Button group columns let you provide an array of LinkColumns to display in a single cell. + +```php +ButtonGroupColumn::make('Actions') + ->attributes(function($row) { + return [ + 'class' => 'space-x-2', + ]; + }) + ->buttons([ + LinkColumn::make('View') // make() has no effect in this case but needs to be set anyway + ->title(fn($row) => 'View ' . $row->name) + ->location(fn($row) => route('user.show', $row)) + ->attributes(function($row) { + return [ + 'class' => 'underline text-blue-500 hover:no-underline', + ]; + }), + LinkColumn::make('Edit') + ->title(fn($row) => 'Edit ' . $row->name) + ->location(fn($row) => route('user.edit', $row)) + ->attributes(function($row) { + return [ + 'target' => '_blank', + 'class' => 'underline text-blue-500 hover:no-underline', + ]; + }), + ]), +``` diff --git a/docs/column-types/color_columns.md b/docs/column-types/color_columns.md new file mode 100644 index 000000000..e4920144d --- /dev/null +++ b/docs/column-types/color_columns.md @@ -0,0 +1,41 @@ +--- +title: Color Columns +weight: 5 +--- + +Color columns provide an easy way to a Color in a Column + +You may pass either pass a CSS-compliant colour as a field +```php +ColorColumn::make('Favourite Colour', 'favourite_color'), +``` + +Or you may use a Callback +```php +ColorColumn::make('Favourite Colour') + ->color( + function ($row) { + if ($row->success_rate < 40) + { + return '#ff0000'; + } + else if ($row->success_rate > 90) + { + return '#008000'; + } + else return '#ffa500'; + + } + ), +``` + +You may also specify attributes to use on the div displaying the color, to adjust the size or appearance, this receives the full row. By default, this will replace the standard classes, to retain them, set "default" to true. To then over-ride, you should prefix your classes with "!" to signify importance. +```php + ColorColumn::make('Favourite Colour') + ->attributes(function ($row) { + return [ + 'class' => '!rounded-lg self-center', + 'default' => true, + ]; + }), +``` diff --git a/docs/column-types/component_column.md b/docs/column-types/component_column.md new file mode 100644 index 000000000..0db9c9483 --- /dev/null +++ b/docs/column-types/component_column.md @@ -0,0 +1,28 @@ +--- +title: Component Columns +weight: 6 +--- + +Component columns let you specify a component name and attributes and provides the column value to the slot. + +```php +// Before +Column::make("Email", "email") + ->format(function ($value) { + return view('components.alert') + ->with('attributes', new ComponentAttributeBag([ + 'type' => Str::endsWith($value, 'example.org') ? 'success' : 'danger', + 'dismissible' => true, + ])) + ->with('slot', $value); + }), + +// After +ComponentColumn::make('E-mail', 'email') + ->component('email') + ->attributes(fn ($value, $row, Column $column) => [ + 'type' => Str::endsWith($value, 'example.org') ? 'success' : 'danger', + 'dismissible' => true, + ]), +``` + diff --git a/docs/column-types/count_column.md b/docs/column-types/count_column.md new file mode 100644 index 000000000..d33cf5df9 --- /dev/null +++ b/docs/column-types/count_column.md @@ -0,0 +1,14 @@ +--- +title: Count Columns (beta) +weight: 7 +--- + +Count columns provide an easy way to display the "Count" of a relation. + +``` + CountColumn::make('Related Users') + ->setDataSource('users') + ->sortable(), +``` + +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file diff --git a/docs/column-types/date_columns.md b/docs/column-types/date_columns.md new file mode 100644 index 000000000..abc8bbb42 --- /dev/null +++ b/docs/column-types/date_columns.md @@ -0,0 +1,28 @@ +--- +title: Date Columns +weight: 8 +--- + +Date columns provide an easy way to display dates in a given format, without having to use repetitive format() methods or partial views. + +You may pass either a DateTime object, in which you can define an "outputFormat" +```php +DateColumn::make('Updated At', 'updated_at') + ->outputFormat('Y-m-d H:i:s), +``` + +Or you may pass a string, in which case you can define an "inputFormat" in addition to the outputFormat: +```php +DateColumn::make('Last Charged', 'last_charged_at') + ->inputFormat('Y-m-d H:i:s') + ->outputFormat('Y-m-d'), +``` + +You may also set an "emptyValue" to use when there is no value from the database: +```php +DateColumn::make('Last Charged', 'last_charged_at') + ->inputFormat('Y-m-d H:i:s') + ->outputFormat('Y-m-d') + ->emptyValue('Not Found'), +``` + diff --git a/docs/column-types/image_columns.md b/docs/column-types/image_columns.md new file mode 100644 index 000000000..f816280af --- /dev/null +++ b/docs/column-types/image_columns.md @@ -0,0 +1,26 @@ +--- +title: Image Columns +weight: 9 +--- + +Image columns provide a way to display images in your table without having to use `format()` or partial views: + +```php +ImageColumn::make('Avatar') + ->location( + fn($row) => storage_path('app/public/avatars/' . $row->id . '.jpg') + ), +``` + +You may also pass an array of attributes to apply to the image tag: + +```php +ImageColumn::make('Avatar') + ->location( + fn($row) => storage_path('app/public/avatars/' . $row->id . '.jpg') + ) + ->attributes(fn($row) => [ + 'class' => 'rounded-full', + 'alt' => $row->name . ' Avatar', + ]), +``` diff --git a/docs/column-types/link_columns.md b/docs/column-types/link_columns.md new file mode 100644 index 000000000..cba4bc887 --- /dev/null +++ b/docs/column-types/link_columns.md @@ -0,0 +1,24 @@ +--- +title: Link Columns +weight: 10 +--- + +Link columns provide a way to display HTML links in your table without having to use `format()` or partial views: + +```php +LinkColumn::make('Action') + ->title(fn($row) => 'Edit') + ->location(fn($row) => route('admin.users.edit', $row)), +``` + +You may also pass an array of attributes to apply to the `a` tag: + +```php +LinkColumn::make('Action') + ->title(fn($row) => 'Edit') + ->location(fn($row) => route('admin.users.edit', $row)) + ->attributes(fn($row) => [ + 'class' => 'rounded-full', + 'alt' => $row->name . ' Avatar', + ]), +``` diff --git a/docs/column-types/livewire_component_column.md b/docs/column-types/livewire_component_column.md new file mode 100644 index 000000000..b75172881 --- /dev/null +++ b/docs/column-types/livewire_component_column.md @@ -0,0 +1,5 @@ +--- +title: Livewire Component (beta) +weight: 11 +--- + diff --git a/docs/column-types/sum_column.md b/docs/column-types/sum_column.md new file mode 100644 index 000000000..7f99f7cab --- /dev/null +++ b/docs/column-types/sum_column.md @@ -0,0 +1,14 @@ +--- +title: Sum Columns (beta) +weight: 12 +--- + +Sum columns provide an easy way to display the "Sum" of a field on a relation. + +``` + SumColumn::make('Total Age of Related Users') + ->setDataSource('users','age') + ->sortable(), +``` + +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file diff --git a/docs/column-types/wire_link_column.md b/docs/column-types/wire_link_column.md new file mode 100644 index 000000000..3577ad95b --- /dev/null +++ b/docs/column-types/wire_link_column.md @@ -0,0 +1,37 @@ +--- +title: Wire Link Column (beta) +weight: 13 +--- + +WireLink columns provide a way to display Wired Links in your table without having to use `format()` or partial views, with or without a Confirmation Message + +WireLinkColumn requires title, and an "action", which must be a valid LiveWire method in the current class, or a global method + +Without a Confirmation Message +```php + WireLinkColumn::make("Delete Item") + ->title(fn($row) => 'Delete Item') + ->action(fn($row) => 'delete("'.$row->id.'")'), +``` + +You may also pass a string to "confirmMessage", which will utilise LiveWire 3's "wire:confirm" approach to display a confirmation modal. + +```php + WireLinkColumn::make("Delete Item") + ->title(fn($row) => 'Delete Item') + ->confirmMessage('Are you sure you want to delete this item?') + ->action(fn($row) => 'delete("'.$row->id.'")') + ->attributes(fn($row) => [ + 'class' => 'btn btn-danger', + ]), +``` + +And you may also pass an array of attributes, which will be applied to the "button" element used within the Column +```php + WireLinkColumn::make("Delete Item") + ->title(fn($row) => 'Delete Item') + ->action(fn($row) => 'delete("'.$row->id.'")') + ->attributes(fn($row) => [ + 'class' => 'btn btn-danger', + ]), +``` diff --git a/docs/columns/other-column-types.md b/docs/columns/other-column-types.md index dc80da449..2e515eedf 100644 --- a/docs/columns/other-column-types.md +++ b/docs/columns/other-column-types.md @@ -3,247 +3,14 @@ title: Other Column Types weight: 4 --- -## Boolean Columns -Boolean columns are good if you have a column type that is a true/false, or 0/1 value. -For example: -```php -BooleanColumn::make('Active') -``` -Would yield: +## Aggregate Columns -![Boolean Column](https://imgur.com/LAk6gHY.png) +### AvgColumn -### Using your own view +### CountColumn -If you don't want to use the default view and icons you can set your own: - -```php -BooleanColumn::make('Active') - ->setView('my.active.view') -``` - -You will have access to `$component`, `$status`, and `$successValue`. - -To help you better understand, this is the Tailwind implementation of BooleanColumn: - -```html -@if ($status) - - - -@else - - - -@endif -``` - -### Setting the truthy value - -If you want the false value to be the green option, you can set: - -```php -BooleanColumn::make('Active') - ->setSuccessValue(false); // Makes false the 'successful' option -``` - -That would swap the colors of the icons in the image above. - -### Setting the status value - -By default, the `$status` is set to: - -```php -(bool)$value === true -``` - -You can override this functionality: - -```php -BooleanColumn::make('Active') - // Note: Parameter `$row` available as of v2.4 - ->setCallback(function(string $value, $row) { - // Figure out what makes $value true - }), -``` - -### Different types of boolean display - -By default, the BooleanColumn displays icons. - -If you would like the BooleanColumn to display a plain Yes/No, you can set: - -```php -BooleanColumn::make('Active') - ->yesNo() -``` -## Color Columns - -Color columns provide an easy way to a Color in a Column - -You may pass either pass a CSS-compliant colour as a field -```php -ColorColumn::make('Favourite Colour', 'favourite_color'), -``` - -Or you may use a Callback -```php -ColorColumn::make('Favourite Colour') - ->color( - function ($row) { - if ($row->success_rate < 40) - { - return '#ff0000'; - } - else if ($row->success_rate > 90) - { - return '#008000'; - } - else return '#ffa500'; - - } - ), -``` - -You may also specify attributes to use on the div displaying the color, to adjust the size or appearance, this receives the full row. By default, this will replace the standard classes, to retain them, set "default" to true. To then over-ride, you should prefix your classes with "!" to signify importance. -```php - ColorColumn::make('Favourite Colour') - ->attributes(function ($row) { - return [ - 'class' => '!rounded-lg self-center', - 'default' => true, - ]; - }), -``` - -## Date Columns - -Date columns provide an easy way to display dates in a given format, without having to use repetitive format() methods or partial views. - -You may pass either a DateTime object, in which you can define an "outputFormat" -```php -DateColumn::make('Updated At', 'updated_at') - ->outputFormat('Y-m-d H:i:s), -``` - -Or you may pass a string, in which case you can define an "inputFormat" in addition to the outputFormat: -```php -DateColumn::make('Last Charged', 'last_charged_at') - ->inputFormat('Y-m-d H:i:s') - ->outputFormat('Y-m-d'), -``` - -You may also set an "emptyValue" to use when there is no value from the database: -```php -DateColumn::make('Last Charged', 'last_charged_at') - ->inputFormat('Y-m-d H:i:s') - ->outputFormat('Y-m-d') - ->emptyValue('Not Found'), -``` - -## Image Columns - -Image columns provide a way to display images in your table without having to use `format()` or partial views: - -```php -ImageColumn::make('Avatar') - ->location( - fn($row) => storage_path('app/public/avatars/' . $row->id . '.jpg') - ), -``` - -You may also pass an array of attributes to apply to the image tag: - -```php -ImageColumn::make('Avatar') - ->location( - fn($row) => storage_path('app/public/avatars/' . $row->id . '.jpg') - ) - ->attributes(fn($row) => [ - 'class' => 'rounded-full', - 'alt' => $row->name . ' Avatar', - ]), -``` - -## Link Columns - -Link columns provide a way to display HTML links in your table without having to use `format()` or partial views: - -```php -LinkColumn::make('Action') - ->title(fn($row) => 'Edit') - ->location(fn($row) => route('admin.users.edit', $row)), -``` - -You may also pass an array of attributes to apply to the `a` tag: - -```php -LinkColumn::make('Action') - ->title(fn($row) => 'Edit') - ->location(fn($row) => route('admin.users.edit', $row)) - ->attributes(fn($row) => [ - 'class' => 'rounded-full', - 'alt' => $row->name . ' Avatar', - ]), -``` - -## Button Group Columns - -Button group columns let you provide an array of LinkColumns to display in a single cell. - -```php -ButtonGroupColumn::make('Actions') - ->attributes(function($row) { - return [ - 'class' => 'space-x-2', - ]; - }) - ->buttons([ - LinkColumn::make('View') // make() has no effect in this case but needs to be set anyway - ->title(fn($row) => 'View ' . $row->name) - ->location(fn($row) => route('user.show', $row)) - ->attributes(function($row) { - return [ - 'class' => 'underline text-blue-500 hover:no-underline', - ]; - }), - LinkColumn::make('Edit') - ->title(fn($row) => 'Edit ' . $row->name) - ->location(fn($row) => route('user.edit', $row)) - ->attributes(function($row) { - return [ - 'target' => '_blank', - 'class' => 'underline text-blue-500 hover:no-underline', - ]; - }), - ]), -``` - -## Component Columns - -Component columns let you specify a component name and attributes and provides the column value to the slot. - -```php -// Before -Column::make("Email", "email") - ->format(function ($value) { - return view('components.alert') - ->with('attributes', new ComponentAttributeBag([ - 'type' => Str::endsWith($value, 'example.org') ? 'success' : 'danger', - 'dismissible' => true, - ])) - ->with('slot', $value); - }), - -// After -ComponentColumn::make('E-mail', 'email') - ->component('email') - ->attributes(fn ($value, $row, Column $column) => [ - 'type' => Str::endsWith($value, 'example.org') ? 'success' : 'danger', - 'dismissible' => true, - ]), -``` +### SumColumn \ No newline at end of file diff --git a/docs/examples/_index.md b/docs/examples/_index.md index a831d1a26..03b1d982b 100644 --- a/docs/examples/_index.md +++ b/docs/examples/_index.md @@ -1,4 +1,4 @@ --- title: Examples -weight: 14 +weight: 16 --- diff --git a/docs/filter-types/_index.md b/docs/filter-types/_index.md index 6c6d8ccf4..3631183ba 100644 --- a/docs/filter-types/_index.md +++ b/docs/filter-types/_index.md @@ -1,4 +1,4 @@ --- title: Filter Types -weight: 11 +weight: 12 --- diff --git a/docs/filters/_index.md b/docs/filters/_index.md index 43af373bd..e0ac299f1 100644 --- a/docs/filters/_index.md +++ b/docs/filters/_index.md @@ -1,4 +1,4 @@ --- title: Filters -weight: 10 +weight: 11 --- diff --git a/docs/footer/_index.md b/docs/footer/_index.md index e16f7efb5..4edcb2460 100644 --- a/docs/footer/_index.md +++ b/docs/footer/_index.md @@ -1,4 +1,4 @@ --- title: Footer -weight: 13 +weight: 15 --- diff --git a/docs/misc/_index.md b/docs/misc/_index.md index 9f806b32e..193fabdf4 100644 --- a/docs/misc/_index.md +++ b/docs/misc/_index.md @@ -1,4 +1,4 @@ --- title: Misc. -weight: 15 +weight: 17 --- diff --git a/docs/pagination/_index.md b/docs/pagination/_index.md index 723cb0c11..50491640d 100644 --- a/docs/pagination/_index.md +++ b/docs/pagination/_index.md @@ -1,4 +1,4 @@ --- title: Pagination -weight: 7 +weight: 8 --- diff --git a/docs/reordering/_index.md b/docs/reordering/_index.md index bc21a450b..e66081c28 100644 --- a/docs/reordering/_index.md +++ b/docs/reordering/_index.md @@ -1,4 +1,4 @@ --- title: Reordering -weight: 11 +weight: 13 --- diff --git a/docs/rows/_index.md b/docs/rows/_index.md index f731c19d2..7d5ebba99 100644 --- a/docs/rows/_index.md +++ b/docs/rows/_index.md @@ -1,4 +1,4 @@ --- title: Rows -weight: 5 +weight: 6 --- diff --git a/docs/search/_index.md b/docs/search/_index.md index edb174fde..4aac1542b 100644 --- a/docs/search/_index.md +++ b/docs/search/_index.md @@ -1,4 +1,4 @@ --- title: Search -weight: 8 +weight: 9 --- diff --git a/docs/secondary-header/_index.md b/docs/secondary-header/_index.md index 5e2b9aba5..f334c9e68 100644 --- a/docs/secondary-header/_index.md +++ b/docs/secondary-header/_index.md @@ -1,4 +1,4 @@ --- title: Secondary Header -weight: 12 +weight: 14 --- diff --git a/docs/sorting/_index.md b/docs/sorting/_index.md index a99eabf1d..422739fb6 100644 --- a/docs/sorting/_index.md +++ b/docs/sorting/_index.md @@ -1,4 +1,4 @@ --- title: Sorting -weight: 6 +weight: 7 --- diff --git a/resources/views/includes/columns/wire-link.blade.php b/resources/views/includes/columns/wire-link.blade.php new file mode 100644 index 000000000..191ba5f1d --- /dev/null +++ b/resources/views/includes/columns/wire-link.blade.php @@ -0,0 +1,9 @@ + diff --git a/src/Traits/ComponentUtilities.php b/src/Traits/ComponentUtilities.php index 1e421e0b6..8ec4bd8b6 100644 --- a/src/Traits/ComponentUtilities.php +++ b/src/Traits/ComponentUtilities.php @@ -37,6 +37,14 @@ trait ComponentUtilities protected array $additionalSelects = []; + protected array $extraWiths = []; + + protected array $extraWithCounts = []; + + protected array $extraWithSums = []; + + protected array $extraWithAvgs = []; + /** * Set any configuration options */ diff --git a/src/Traits/Configuration/BulkActionsConfiguration.php b/src/Traits/Configuration/BulkActionsConfiguration.php index d0d5e17f3..6af318f0e 100644 --- a/src/Traits/Configuration/BulkActionsConfiguration.php +++ b/src/Traits/Configuration/BulkActionsConfiguration.php @@ -171,4 +171,46 @@ public function setShouldAlwaysHideBulkActionsDropdownOptionDisabled(): self return $this; } + + public function setClearSelectedOnSearch(bool $status): self + { + $this->clearSelectedOnSearch = $status; + + return $this; + } + + public function setClearSelectedOnSearchEnabled(): self + { + $this->setClearSelectedOnSearch(true); + + return $this; + } + + public function setClearSelectedOnSearchDisabled(): self + { + $this->setClearSelectedOnSearch(false); + + return $this; + } + + public function setClearSelectedOnFilter(bool $status): self + { + $this->clearSelectedOnFilter = $status; + + return $this; + } + + public function setClearSelectedOnFilterEnabled(): self + { + $this->setClearSelectedOnFilter(true); + + return $this; + } + + public function setClearSelectedOnFilterDisabled(): self + { + $this->setClearSelectedOnFilter(false); + + return $this; + } } diff --git a/src/Traits/Configuration/ComponentConfiguration.php b/src/Traits/Configuration/ComponentConfiguration.php index 90611185a..044f5655e 100644 --- a/src/Traits/Configuration/ComponentConfiguration.php +++ b/src/Traits/Configuration/ComponentConfiguration.php @@ -96,4 +96,60 @@ public function setDataTableFingerprint(string $dataTableFingerprint): self return $this; } + + public function setExtraWiths(array $extraWiths): self + { + $this->extraWiths = $extraWiths; + + return $this; + } + + public function addExtraWith(string $extraWith): self + { + $this->extraWiths[] = $extraWith; + + return $this; + } + + public function addExtraWiths(array $extraWiths): self + { + $this->extraWiths = [...$this->extraWiths, ...$extraWiths]; + + return $this; + } + + public function setExtraWithCounts(array $extraWithCounts): self + { + $this->extraWithCounts = $extraWithCounts; + + return $this; + } + + public function addExtraWithCount(string $extraWithCount): self + { + $this->extraWithCounts[] = $extraWithCount; + + return $this; + } + + public function addExtraWithCounts(array $extraWithCounts): self + { + $this->extraWithCounts = [...$this->extraWithCounts, ...$extraWithCounts]; + + return $this; + } + + public function addExtraWithSum(string $relationship, string $column): self + { + $this->extraWithSums[] = ['table' => $relationship, 'field' => $column]; + + return $this; + } + + public function addExtraWithAvg(string $relationship, string $column): self + { + $this->extraWithAvgs[] = ['table' => $relationship, 'field' => $column]; + + return $this; + } } diff --git a/src/Traits/Helpers/BulkActionsHelpers.php b/src/Traits/Helpers/BulkActionsHelpers.php index 70ea427a5..77a263635 100644 --- a/src/Traits/Helpers/BulkActionsHelpers.php +++ b/src/Traits/Helpers/BulkActionsHelpers.php @@ -220,4 +220,14 @@ public function shouldAlwaysHideBulkActionsDropdownOption(): bool { return $this->alwaysHideBulkActionsDropdownOption ?? false; } + + public function getClearSelectedOnSearch(): bool + { + return $this->clearSelectedOnSearch ?? true; + } + + public function getClearSelectedOnFilter(): bool + { + return $this->clearSelectedOnFilter ?? true; + } } diff --git a/src/Traits/Helpers/ColumnHelpers.php b/src/Traits/Helpers/ColumnHelpers.php index 009a6f4bd..920d63fe6 100644 --- a/src/Traits/Helpers/ColumnHelpers.php +++ b/src/Traits/Helpers/ColumnHelpers.php @@ -4,6 +4,7 @@ use Illuminate\Support\Collection; use Rappasoft\LaravelLivewireTables\Views\Column; +use Rappasoft\LaravelLivewireTables\Views\Columns\AggregateColumn; trait ColumnHelpers { @@ -18,6 +19,15 @@ public function setColumns(): void ->filter(fn ($column) => $column instanceof Column) ->map(function (Column $column) { $column->setComponent($this); + if ($column instanceof AggregateColumn) { + if ($column->getAggregateMethod() == 'count' && $column->hasDataSource()) { + $this->addExtraWithCount($column->getDataSource()); + } elseif ($column->getAggregateMethod() == 'sum' && $column->hasDataSource() && $column->hasForeignColumn()) { + $this->addExtraWithSum($column->getDataSource(), $column->getForeignColumn()); + } elseif ($column->getAggregateMethod() == 'avg' && $column->hasDataSource() && $column->hasForeignColumn()) { + $this->addExtraWithAvg($column->getDataSource(), $column->getForeignColumn()); + } + } if ($column->hasField()) { if ($column->isBaseColumn()) { @@ -198,6 +208,15 @@ public function getPrependedColumns(): Collection ->filter(fn ($column) => $column instanceof Column) ->map(function (Column $column) { $column->setComponent($this); + if ($column instanceof AggregateColumn) { + if ($column->getAggregateMethod() == 'count' && $column->hasDataSource()) { + $this->addExtraWithCount($column->getDataSource()); + } elseif ($column->getAggregateMethod() == 'sum' && $column->hasDataSource() && $column->hasForeignColumn()) { + $this->addExtraWithSum($column->getDataSource(), $column->getForeignColumn()); + } elseif ($column->getAggregateMethod() == 'avg' && $column->hasDataSource() && $column->hasForeignColumn()) { + $this->addExtraWithAvg($column->getDataSource(), $column->getForeignColumn()); + } + } if ($column->hasField()) { if ($column->isBaseColumn()) { @@ -217,6 +236,15 @@ public function getAppendedColumns(): Collection ->filter(fn ($column) => $column instanceof Column) ->map(function (Column $column) { $column->setComponent($this); + if ($column instanceof AggregateColumn) { + if ($column->getAggregateMethod() == 'count' && $column->hasDataSource()) { + $this->addExtraWithCount($column->getDataSource()); + } elseif ($column->getAggregateMethod() == 'sum' && $column->hasDataSource() && $column->hasForeignColumn()) { + $this->addExtraWithSum($column->getDataSource(), $column->getForeignColumn()); + } elseif ($column->getAggregateMethod() == 'avg' && $column->hasDataSource() && $column->hasForeignColumn()) { + $this->addExtraWithAvg($column->getDataSource(), $column->getForeignColumn()); + } + } if ($column->hasField()) { if ($column->isBaseColumn()) { diff --git a/src/Traits/Helpers/ComponentHelpers.php b/src/Traits/Helpers/ComponentHelpers.php index c600200b5..96eae35f8 100644 --- a/src/Traits/Helpers/ComponentHelpers.php +++ b/src/Traits/Helpers/ComponentHelpers.php @@ -149,4 +149,44 @@ public function getAdditionalSelects(): array { return $this->additionalSelects; } + + public function hasExtraWiths(): bool + { + return ! empty($this->extraWiths); + } + + public function getExtraWiths(): array + { + return $this->extraWiths; + } + + public function hasExtraWithCounts(): bool + { + return ! empty($this->extraWithCounts); + } + + public function getExtraWithCounts(): array + { + return $this->extraWithCounts; + } + + public function hasExtraWithSums(): bool + { + return ! empty($this->extraWithSums); + } + + public function getExtraWithSums(): array + { + return $this->extraWithSums; + } + + public function hasExtraWithAvgs(): bool + { + return ! empty($this->extraWithAvgs); + } + + public function getExtraWithAvgs(): array + { + return $this->extraWithAvgs; + } } diff --git a/src/Traits/WithBulkActions.php b/src/Traits/WithBulkActions.php index 08f69197b..37a5cd111 100644 --- a/src/Traits/WithBulkActions.php +++ b/src/Traits/WithBulkActions.php @@ -36,6 +36,10 @@ trait WithBulkActions protected bool $alwaysHideBulkActionsDropdownOption = false; + public bool $clearSelectedOnSearch = true; + + public bool $clearSelectedOnFilter = true; + public function bulkActions(): array { return property_exists($this, 'bulkActions') ? $this->bulkActions : []; diff --git a/src/Traits/WithData.php b/src/Traits/WithData.php index 5e1ca3298..23d1055bb 100644 --- a/src/Traits/WithData.php +++ b/src/Traits/WithData.php @@ -56,6 +56,29 @@ protected function baseQuery(): Builder $this->setBuilder($this->applyFilters()); + $builder = $this->getBuilder(); + + if ($this->hasExtraWiths()) { + $builder->with($this->getExtraWiths()); + } + + if ($this->hasExtraWithSums()) { + foreach ($this->getExtraWithSums() as $extraSum) { + $builder->withSum($extraSum['table'], $extraSum['field']); + } + } + if ($this->hasExtraWithAvgs()) { + foreach ($this->getExtraWithAvgs() as $extraAvg) { + $builder->withAvg($extraAvg['table'], $extraAvg['field']); + } + } + + if ($this->hasExtraWithCounts()) { + $builder->withCount($this->getExtraWithCounts()); + } + + $this->setBuilder($builder); + return $this->getBuilder(); } @@ -249,7 +272,8 @@ protected function getTableAlias(?string $currentTableAlias, string $relationPar public function builder(): Builder { if ($this->hasModel()) { - return $this->getModel()::query()->with($this->getRelationships()); + return $this->getModel()::query() + ->with($this->getRelationships()); } // If model does not exist diff --git a/src/Traits/WithFilters.php b/src/Traits/WithFilters.php index 878c660ce..981d0e855 100644 --- a/src/Traits/WithFilters.php +++ b/src/Traits/WithFilters.php @@ -74,9 +74,11 @@ public function updatedFilterComponents(string|array|null $value, string $filter { $this->resetComputedPage(); - // Clear bulk actions on filter - $this->clearSelected(); - $this->setSelectAllDisabled(); + // Clear bulk actions on filter - if enabled + if ($this->getClearSelectedOnFilter()) { + $this->clearSelected(); + $this->setSelectAllDisabled(); + } // Clear filters on empty value $filter = $this->getFilterByKey($filterName); diff --git a/src/Traits/WithSearch.php b/src/Traits/WithSearch.php index 0ef402573..5d90a991f 100644 --- a/src/Traits/WithSearch.php +++ b/src/Traits/WithSearch.php @@ -70,9 +70,11 @@ public function updatedSearch(string|array|null $value): void { $this->resetComputedPage(); - // Clear bulk actions on search - $this->clearSelected(); - $this->setSelectAllDisabled(); + // Clear bulk actions on search - if enabled + if ($this->getClearSelectedOnSearch()) { + $this->clearSelected(); + $this->setSelectAllDisabled(); + } if (is_null($value) || $value === '') { $this->clearSearch(); diff --git a/src/Views/Columns/AggregateColumn.php b/src/Views/Columns/AggregateColumn.php new file mode 100644 index 000000000..a284b08ff --- /dev/null +++ b/src/Views/Columns/AggregateColumn.php @@ -0,0 +1,23 @@ +label(fn () => null); + } +} diff --git a/src/Views/Columns/AvgColumn.php b/src/Views/Columns/AvgColumn.php new file mode 100644 index 000000000..3af197873 --- /dev/null +++ b/src/Views/Columns/AvgColumn.php @@ -0,0 +1,18 @@ +label(fn () => null); + } +} diff --git a/src/Views/Columns/CountColumn.php b/src/Views/Columns/CountColumn.php new file mode 100644 index 000000000..4a1ffd812 --- /dev/null +++ b/src/Views/Columns/CountColumn.php @@ -0,0 +1,18 @@ +label(fn () => null); + } +} diff --git a/src/Views/Columns/SumColumn.php b/src/Views/Columns/SumColumn.php new file mode 100644 index 000000000..a01822d7b --- /dev/null +++ b/src/Views/Columns/SumColumn.php @@ -0,0 +1,18 @@ +label(fn () => null); + } +} diff --git a/src/Views/Columns/WireLinkColumn.php b/src/Views/Columns/WireLinkColumn.php new file mode 100644 index 000000000..343f28ef9 --- /dev/null +++ b/src/Views/Columns/WireLinkColumn.php @@ -0,0 +1,45 @@ +label(fn () => null); + } + + public function getContents(Model $row): null|string|\Illuminate\Support\HtmlString|DataTableConfigurationException|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + { + if (! $this->hasTitleCallback()) { + throw new DataTableConfigurationException('You must specify a title callback for a WireLink column.'); + } + + if (! $this->hasActionCallback()) { + throw new DataTableConfigurationException('You must specify an action callback for a WireLink column.'); + } + + return view($this->getView()) + ->withColumn($this) + ->withTitle(app()->call($this->getTitleCallback(), ['row' => $row])) + ->withPath(app()->call($this->getActionCallback(), ['row' => $row])) + ->withAttributes($this->hasAttributesCallback() ? app()->call($this->getAttributesCallback(), ['row' => $row]) : []); + } +} diff --git a/src/Views/Traits/Configuration/AggregateColumnConfiguration.php b/src/Views/Traits/Configuration/AggregateColumnConfiguration.php new file mode 100644 index 000000000..2f181ed2f --- /dev/null +++ b/src/Views/Traits/Configuration/AggregateColumnConfiguration.php @@ -0,0 +1,59 @@ +dataSource = $dataSource; + + if (isset($foreignColumn)) { + $this->setForeignColumn($foreignColumn); + } + + $this->setDefaultLabel(); + + return $this; + } + + public function setAggregateMethod(string $aggregateMethod): self + { + $this->aggregateMethod = $aggregateMethod; + $this->setDefaultLabel(); + + return $this; + } + + public function setForeignColumn(string $foreignColumn): self + { + $this->foreignColumn = $foreignColumn; + $this->setDefaultLabel(); + + return $this; + } + + public function setDefaultLabel(): void + { + $this->label(function ($row, Column $column) { + if ($this->hasForeignColumn()) { + return $row->{$this->getDataSource().'_'.$this->getAggregateMethod().'_'.$this->getForeignColumn()}; + } + + return $row->{$this->getDataSource().'_'.$this->getAggregateMethod()}; + }); + + } + + public function sortable(?callable $callback = null): self + { + $this->sortable = true; + + $this->sortCallback = ($callback === null) ? ($this->hasForeignColumn() ? fn (Builder $query, string $direction) => $query->orderBy($this->getDataSource().'_'.$this->getAggregateMethod().'_'.$this->getForeignColumn(), $direction) : fn (Builder $query, string $direction) => $query->orderBy($this->dataSource.'_count', $direction)) : $callback; + + return $this; + } +} diff --git a/src/Views/Traits/Configuration/ArrayColumnConfiguration.php b/src/Views/Traits/Configuration/ArrayColumnConfiguration.php index 72188fd79..c0f7657b7 100644 --- a/src/Views/Traits/Configuration/ArrayColumnConfiguration.php +++ b/src/Views/Traits/Configuration/ArrayColumnConfiguration.php @@ -26,4 +26,14 @@ public function outputFormat(callable $callable): self return $this; } + + /** + * Define the Empty Value to use for the Column + */ + public function emptyValue(string $emptyValue): self + { + $this->emptyValue = $emptyValue; + + return $this; + } } diff --git a/src/Views/Traits/Configuration/WireLinkColumnConfiguration.php b/src/Views/Traits/Configuration/WireLinkColumnConfiguration.php new file mode 100644 index 000000000..238b36fbf --- /dev/null +++ b/src/Views/Traits/Configuration/WireLinkColumnConfiguration.php @@ -0,0 +1,5 @@ +actionCallback = $callback; + + return $this; + } + + public function getActionCallback(): ?callable + { + return $this->actionCallback; + } + + public function hasActionCallback(): bool + { + return $this->actionCallback !== null; + } +} diff --git a/src/Views/Traits/Core/HasConfirmation.php b/src/Views/Traits/Core/HasConfirmation.php new file mode 100644 index 000000000..9a8597947 --- /dev/null +++ b/src/Views/Traits/Core/HasConfirmation.php @@ -0,0 +1,28 @@ +confirmMessage = $confirmMessage; + + return $this; + } + + public function hasConfirmMessage(): bool + { + return isset($this->confirmMessage); + } + + public function getConfirmMessage(): string + { + return $this->confirmMessage; + } +} diff --git a/src/Views/Traits/Helpers/AggregateColumnHelpers.php b/src/Views/Traits/Helpers/AggregateColumnHelpers.php new file mode 100644 index 000000000..1748b8091 --- /dev/null +++ b/src/Views/Traits/Helpers/AggregateColumnHelpers.php @@ -0,0 +1,46 @@ +dataSource; + } + + public function hasDataSource(): bool + { + return isset($this->dataSource); + } + + public function getAggregateMethod(): string + { + return $this->aggregateMethod; + } + + public function hasForeignColumn(): bool + { + return isset($this->foreignColumn); + } + + public function getForeignColumn(): string + { + return $this->foreignColumn; + } + + public function getContents(Model $row): null|string|\BackedEnum|HtmlString|DataTableConfigurationException|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + { + if (! isset($this->dataSource)) { + throw new DataTableConfigurationException('You must specify a data source'); + } else { + return parent::getContents($row); + } + } +} diff --git a/src/Views/Traits/Helpers/ArrayColumnHelpers.php b/src/Views/Traits/Helpers/ArrayColumnHelpers.php index 7c32f7c70..8289038e4 100644 --- a/src/Views/Traits/Helpers/ArrayColumnHelpers.php +++ b/src/Views/Traits/Helpers/ArrayColumnHelpers.php @@ -48,10 +48,6 @@ public function getContents(Model $row): null|string|\BackedEnum|HtmlString|Data $outputValues = []; $value = $this->getValue($row); - if (! $this->hasSeparator()) { - throw new DataTableConfigurationException('You must set a valid separator on an ArrayColumn'); - } - if (! $this->hasDataCallback()) { throw new DataTableConfigurationException('You must set a data() method on an ArrayColumn'); } diff --git a/src/Views/Traits/Helpers/WireLinkColumnHelpers.php b/src/Views/Traits/Helpers/WireLinkColumnHelpers.php new file mode 100644 index 000000000..f24c7e460 --- /dev/null +++ b/src/Views/Traits/Helpers/WireLinkColumnHelpers.php @@ -0,0 +1,5 @@ +setPrimaryKey('id'); + } + + public function columns(): array + { + return [ + Column::make('ID', 'id') + ->sortable() + ->setSortingPillTitle('Key') + ->setSortingPillDirections('0-9', '9-0'), + Column::make('Name') + ->sortable() + ->searchable(), + AvgColumn::make('Average Age') + ->setDataSource('pets', 'age'), + CountColumn::make('Number of Pets') + ->setDataSource('pets'), + SumColumn::make('Total Age') + ->setDataSource('pets', 'age'), + + ]; + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index fdcbd44a6..ab22227bd 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,7 +9,7 @@ use Livewire\LivewireServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use Rappasoft\LaravelLivewireTables\LaravelLivewireTablesServiceProvider; -use Rappasoft\LaravelLivewireTables\Tests\Http\Livewire\{PetsTable,PetsTableUnpaginated}; +use Rappasoft\LaravelLivewireTables\Tests\Http\Livewire\{PetsTable,PetsTableUnpaginated,SpeciesTable}; use Rappasoft\LaravelLivewireTables\Tests\Models\Breed; use Rappasoft\LaravelLivewireTables\Tests\Models\Pet; use Rappasoft\LaravelLivewireTables\Tests\Models\Species; @@ -19,6 +19,8 @@ class TestCase extends Orchestra { public PetsTable $basicTable; + public SpeciesTable $speciesTable; + public PetsTableUnpaginated $unpaginatedTable; /** @@ -76,7 +78,7 @@ protected function setUp(): void } $this->setupBasicTable(); $this->setupUnpaginatedTable(); - + $this->setupSpeciesTable(); } protected function setupBasicTable() @@ -95,6 +97,22 @@ protected function setupBasicTable() $this->basicTable->render(); } + protected function setupSpeciesTable() + { + $view = view('livewire-tables::datatable'); + $this->speciesTable = new SpeciesTable(); + $this->speciesTable->boot(); + $this->speciesTable->bootedComponentUtilities(); + $this->speciesTable->bootedWithData(); + $this->speciesTable->bootedWithColumns(); + $this->speciesTable->bootedWithColumnSelect(); + $this->speciesTable->bootedWithSecondaryHeader(); + $this->speciesTable->booted(); + $this->speciesTable->renderingWithData($view, []); + $this->speciesTable->renderingWithPagination($view, []); + $this->speciesTable->render(); + } + protected function setupUnpaginatedTable() { diff --git a/tests/Traits/Configuration/ComponentConfigurationTest.php b/tests/Traits/Configuration/ComponentConfigurationTest.php index 001cea2a0..3bed87443 100644 --- a/tests/Traits/Configuration/ComponentConfigurationTest.php +++ b/tests/Traits/Configuration/ComponentConfigurationTest.php @@ -300,4 +300,106 @@ public function test_can_set_hide_configurable_areas_when_reordering_status(): v $this->basicTable->setHideConfigurableAreasWhenReorderingStatus(true); } + + public function test_no_extra_withs_by_default(): void + { + $this->assertFalse($this->basicTable->hasExtraWiths()); + $this->assertEmpty($this->basicTable->getExtraWiths()); + } + + public function test_can_add_extra_with(): void + { + $this->assertFalse($this->basicTable->hasExtraWiths()); + $this->assertEmpty($this->basicTable->getExtraWiths()); + $this->basicTable->addExtraWith('user'); + $this->assertTrue($this->basicTable->hasExtraWiths()); + $this->assertSame(['user'], $this->basicTable->getExtraWiths()); + } + + public function test_can_add_extra_withs(): void + { + $this->assertFalse($this->basicTable->hasExtraWiths()); + $this->assertEmpty($this->basicTable->getExtraWiths()); + $this->basicTable->addExtraWiths(['user', 'pets']); + $this->assertTrue($this->basicTable->hasExtraWiths()); + $this->assertSame(['user', 'pets'], $this->basicTable->getExtraWiths()); + } + + public function test_can_set_extra_withs(): void + { + $this->assertFalse($this->basicTable->hasExtraWiths()); + $this->assertEmpty($this->basicTable->getExtraWiths()); + $this->basicTable->addExtraWith('test'); + $this->assertSame(['test'], $this->basicTable->getExtraWiths()); + $this->assertTrue($this->basicTable->hasExtraWiths()); + $this->basicTable->setExtraWiths(['user', 'pets']); + $this->assertTrue($this->basicTable->hasExtraWiths()); + $this->assertSame(['user', 'pets'], $this->basicTable->getExtraWiths()); + } + + public function test_no_extra_with_counts_by_default(): void + { + $this->assertFalse($this->basicTable->hasExtraWithCounts()); + $this->assertEmpty($this->basicTable->getExtraWithCounts()); + } + + public function test_can_add_extra_with_count(): void + { + $this->assertFalse($this->basicTable->hasExtraWithCounts()); + $this->assertEmpty($this->basicTable->getExtraWithCounts()); + $this->basicTable->addExtraWithCount('users'); + $this->assertTrue($this->basicTable->hasExtraWithCounts()); + $this->assertSame(['users'], $this->basicTable->getExtraWithCounts()); + } + + public function test_can_add_extra_with_counts(): void + { + $this->assertFalse($this->basicTable->hasExtraWithCounts()); + $this->assertEmpty($this->basicTable->getExtraWithCounts()); + $this->basicTable->addExtraWithCounts(['user', 'pets']); + $this->assertTrue($this->basicTable->hasExtraWithCounts()); + $this->assertSame(['user', 'pets'], $this->basicTable->getExtraWithCounts()); + } + + public function test_can_set_extra_with_counts(): void + { + $this->assertFalse($this->basicTable->hasExtraWithCounts()); + $this->assertEmpty($this->basicTable->getExtraWithCounts()); + $this->basicTable->addExtraWithCount('test'); + $this->assertSame(['test'], $this->basicTable->getExtraWithCounts()); + $this->assertTrue($this->basicTable->hasExtraWithCounts()); + $this->basicTable->setExtraWithCounts(['user', 'pets']); + $this->assertTrue($this->basicTable->hasExtraWithCounts()); + $this->assertSame(['user', 'pets'], $this->basicTable->getExtraWithCounts()); + } + + public function test_no_extra_with_sums_by_default(): void + { + $this->assertFalse($this->basicTable->hasExtraWithSums()); + $this->assertEmpty($this->basicTable->getExtraWithSums()); + } + + public function test_can_add_extra_with_sum(): void + { + $this->assertFalse($this->basicTable->hasExtraWithSums()); + $this->assertEmpty($this->basicTable->getExtraWithSums()); + $this->basicTable->addExtraWithSum('users', 'age'); + $this->assertTrue($this->basicTable->hasExtraWithSums()); + $this->assertSame([['table' => 'users', 'field' => 'age']], $this->basicTable->getExtraWithSums()); + } + + public function test_no_extra_with_avgs_by_default(): void + { + $this->assertFalse($this->basicTable->hasExtraWiths()); + $this->assertEmpty($this->basicTable->getExtraWiths()); + } + + public function test_can_add_extra_with_avg(): void + { + $this->assertFalse($this->basicTable->hasExtraWithAvgs()); + $this->assertEmpty($this->basicTable->getExtraWithAvgs()); + $this->basicTable->addExtraWithAvg('user', 'age'); + $this->assertTrue($this->basicTable->hasExtraWithAvgs()); + $this->assertSame([['table' => 'user', 'field' => 'age']], $this->basicTable->getExtraWithAvgs()); + } } diff --git a/tests/Traits/Helpers/BulkActionsHelpersTest.php b/tests/Traits/Helpers/BulkActionsHelpersTest.php index bf4cc9e6e..5506fcb4b 100644 --- a/tests/Traits/Helpers/BulkActionsHelpersTest.php +++ b/tests/Traits/Helpers/BulkActionsHelpersTest.php @@ -207,4 +207,74 @@ public function test_bulk_actions_th_checkbox_attributes_returns_default_true_if { $this->assertSame(['default' => true], $this->basicTable->getBulkActionsThCheckboxAttributes()); } + + public function test_select_clears_by_default(): void + { + $this->basicTable->setSelected([1, 2, 3, 4, 5]); + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + + $this->basicTable->setSearch('Anthony'); + $this->basicTable->updatedSearch('Anthony'); + + $this->assertSame([], $this->basicTable->getSelected()); + } + + public function test_select_does_not_clear_when_disabled(): void + { + $this->basicTable->setClearSelectedOnSearchDisabled(); + + $this->basicTable->setSelected([1, 2, 3, 4, 5]); + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + + $this->basicTable->setSearch('Anthony'); + $this->basicTable->updatedSearch('Anthony'); + + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + } + + public function test_select_does_clear_when_enabled(): void + { + $this->basicTable->setClearSelectedOnSearchEnabled(); + + $this->basicTable->setSelected([1, 2, 3, 4, 5]); + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + + $this->basicTable->setSearch('Anthony'); + $this->basicTable->updatedSearch('Anthony'); + + $this->assertSame([], $this->basicTable->getSelected()); + + } + + public function test_select_clears_by_default_when_filtering(): void + { + $this->basicTable->setSelected([1, 2, 3, 4, 5]); + + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + + $this->basicTable->setFilter('breed_id_filter', '2'); + $this->basicTable->updatedFilterComponents('2', 'breed_id_filter'); + + $this->assertSame([], $this->basicTable->getSelected()); + } + + public function test_select_does_clear_when_filtering_when_enabled(): void + { + $this->basicTable->setSelected([1, 2, 3, 4, 5]); + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + $this->basicTable->setClearSelectedOnFilterEnabled(); + $this->basicTable->setFilter('breed_id_filter', '2'); + $this->basicTable->updatedFilterComponents('2', 'breed_id_filter'); + $this->assertSame([], $this->basicTable->getSelected()); + } + + public function test_select_does_not_clear_when_filtering_when_disabled(): void + { + $this->basicTable->setSelected([1, 2, 3, 4, 5]); + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + $this->basicTable->setClearSelectedOnFilterDisabled(); + $this->basicTable->setFilter('breed_id_filter', '2'); + $this->basicTable->updatedFilterComponents('2', 'breed_id_filter'); + $this->assertSame([1, 2, 3, 4, 5], $this->basicTable->getSelected()); + } } diff --git a/tests/Views/Columns/ArrayColumnTest.php b/tests/Views/Columns/ArrayColumnTest.php new file mode 100644 index 000000000..0eff2808b --- /dev/null +++ b/tests/Views/Columns/ArrayColumnTest.php @@ -0,0 +1,87 @@ +assertSame('Array Col', $column->getTitle()); + } + + public function test_can_set_the_separator(): void + { + $column = ArrayColumn::make('Array Col'); + + $this->assertSame('
', $column->getSeparator()); + $column->separator('

'); + $this->assertTrue($column->hasSeparator()); + + $this->assertSame('

', $column->getSeparator()); + } + + public function test_can_set_the_output_format(): void + { + $column = ArrayColumn::make('Array Col'); + + $this->assertNull($column->getOutputFormatCallback()); + $this->assertFalse($column->hasOutputFormatCallback()); + $column->outputFormat(fn ($index, $value) => "".$value->name.''); + $this->assertTrue($column->hasOutputFormatCallback()); + } + + public function test_requires_the_data_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + $column = ArrayColumn::make('Average Age') + ->separator('

') + ->sortable(); + $contents = $column->getContents(Pet::find(1)); + $this->assertNull($contents); + } + + public function test_can_get_the_output_format_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + $column = ArrayColumn::make('Average Age') + ->separator('

') + ->data(fn ($value, $row) => ($row->pets)) + ->sortable(); + $this->assertNotNull($column->getDataCallback()); + + $contents = $column->getContents(Pet::find(1)); + $this->assertNull($contents); + } + + public function test_requires_the_output_format_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + $column = ArrayColumn::make('Average Age') + ->separator('

') + ->data(fn ($value, $row) => ($row->pets)) + ->sortable(); + + $contents = $column->getContents(Pet::find(1)); + $this->assertNull($contents); + } + + public function test_can_get_empty_value(): void + { + $column = ArrayColumn::make('Average Age') + ->separator('

') + ->data(fn ($value, $row) => ($row->pets)) + ->sortable(); + + $this->assertSame('', $column->getEmptyValue()); + $column->emptyValue('Unknown'); + $this->assertSame('Unknown', $column->getEmptyValue()); + + } +} diff --git a/tests/Views/Columns/AvgColumnTest.php b/tests/Views/Columns/AvgColumnTest.php new file mode 100644 index 000000000..f3657c710 --- /dev/null +++ b/tests/Views/Columns/AvgColumnTest.php @@ -0,0 +1,111 @@ +assertSame('Average Age', $column->getTitle()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_setup_column_correctly(string $relation_name, string $foreign_field): void + { + $column = AvgColumn::make('Average Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + + $this->assertNotEmpty($column); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_not_skip_set_data_source(string $relation_name, string $foreign_field): void + { + $this->expectException(DataTableConfigurationException::class); + + $column = AvgColumn::make('Average Age') + ->sortable(); + $contents = $column->getContents(Pet::find(1)); + $this->assertNull($contents); + + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_data_source(string $relation_name, string $foreign_field): void + { + $column = AvgColumn::make('Average Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasDataSource()); + $this->assertSame($relation_name, $column->getDataSource()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_foreign_column(string $relation_name, string $foreign_field): void + { + $column = AvgColumn::make('Average Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame($foreign_field, $column->getForeignColumn()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_set_foreign_column(string $relation_name, string $foreign_field): void + { + $column = AvgColumn::make('Average Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame($foreign_field, $column->getForeignColumn()); + $column->setForeignColumn('test'); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame('test', $column->getForeignColumn()); + + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_data_source_fields(string $relation_name, string $foreign_field): void + { + $column = AvgColumn::make('Average Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasDataSource()); + $this->assertSame($relation_name, $column->getDataSource()); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame($foreign_field, $column->getForeignColumn()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_aggregate_method(string $relation_name, string $foreign_field): void + { + $column = AvgColumn::make('Average Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertSame('avg', $column->getAggregateMethod()); + $column->setAggregateMethod('test_avg'); + $this->assertSame('test_avg', $column->getAggregateMethod()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_renders_correctly(string $relation_name, string $foreign_field): void + { + $rows = $this->speciesTable->getRows(); + $column = AvgColumn::make('Average Age') + ->setDataSource('pets', 'age'); + $contents = $column->getContents($rows->first()); + $this->assertSame('15', $contents); + $contents = $column->getContents($rows[2]); + $this->assertSame('6', $contents); + } +} diff --git a/tests/Views/Columns/CountColumnTest.php b/tests/Views/Columns/CountColumnTest.php new file mode 100644 index 000000000..6c3d1e772 --- /dev/null +++ b/tests/Views/Columns/CountColumnTest.php @@ -0,0 +1,50 @@ +assertSame('Total Users', $column->getTitle()); + } + + public function test_can_setup_column_correctly(): void + { + $column = CountColumn::make('Total Users') + ->setDataSource('users') + ->sortable(); + + $this->assertNotEmpty($column); + } + + public function test_can_not_skip_set_data_source(): void + { + $this->expectException(DataTableConfigurationException::class); + + $column = CountColumn::make('Average Age') + ->sortable(); + $contents = $column->getContents(Pet::find(1)); + $this->assertNull($contents); + + } + + public function test_renders_correctly(): void + { + $rows = $this->speciesTable->getRows(); + $row1 = $rows->first(); + $column = CountColumn::make('Pets') + ->setDataSource('pets'); + $contents = $column->getContents($rows->first()); + $this->assertSame('2', $contents); + $contents = $column->getContents($rows->last()); + $this->assertSame('0', $contents); + } +} diff --git a/tests/Views/Columns/SumColumnTest.php b/tests/Views/Columns/SumColumnTest.php new file mode 100644 index 000000000..9d5cbff51 --- /dev/null +++ b/tests/Views/Columns/SumColumnTest.php @@ -0,0 +1,111 @@ +assertSame('Sum User Age', $column->getTitle()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_setup_column_correctly(string $relation_name, string $foreign_field): void + { + $column = SumColumn::make('Sum User Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + + $this->assertNotEmpty($column); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_not_skip_set_data_source(string $relation_name, string $foreign_field): void + { + $this->expectException(DataTableConfigurationException::class); + + $column = SumColumn::make('Sum User Age') + ->sortable(); + $contents = $column->getContents(Pet::find(1)); + $this->assertNull($contents); + + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_set_foreign_column(string $relation_name, string $foreign_field): void + { + $column = SumColumn::make('Sum User Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame($foreign_field, $column->getForeignColumn()); + $column->setForeignColumn('test'); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame('test', $column->getForeignColumn()); + + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_data_source(string $relation_name, string $foreign_field): void + { + $column = SumColumn::make('Sum User Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasDataSource()); + $this->assertSame($relation_name, $column->getDataSource()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_foreign_column(string $relation_name, string $foreign_field): void + { + $column = SumColumn::make('Sum User Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame($foreign_field, $column->getForeignColumn()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_data_source_fields(string $relation_name, string $foreign_field): void + { + $column = SumColumn::make('Sum User Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertTrue($column->hasDataSource()); + $this->assertSame($relation_name, $column->getDataSource()); + $this->assertTrue($column->hasForeignColumn()); + $this->assertSame($foreign_field, $column->getForeignColumn()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_can_get_aggregate_method(string $relation_name, string $foreign_field): void + { + $column = SumColumn::make('Sum User Age') + ->setDataSource($relation_name, $foreign_field) + ->sortable(); + $this->assertSame('sum', $column->getAggregateMethod()); + $column->setAggregateMethod('test_sum'); + $this->assertSame('test_sum', $column->getAggregateMethod()); + } + + #[DataProviderExternal(AggregateColumnProvider::class, 'relationshipProvider')] + public function test_renders_correctly(string $relation_name, string $foreign_field): void + { + $rows = $this->speciesTable->getRows(); + $column = SumColumn::make('Total Age') + ->setDataSource('pets', 'age'); + $contents = $column->getContents($rows->first()); + $this->assertSame('30', $contents); + $contents = $column->getContents($rows[2]); + $this->assertSame('12', $contents); + } +} diff --git a/tests/Views/Columns/WireLinkColumnTest.php b/tests/Views/Columns/WireLinkColumnTest.php new file mode 100644 index 000000000..ebe15d32b --- /dev/null +++ b/tests/Views/Columns/WireLinkColumnTest.php @@ -0,0 +1,66 @@ +assertSame('Name', $column->getTitle()); + } + + public function test_can_not_infer_field_name_from_title_if_no_from(): void + { + $column = WireLinkColumn::make('My Title'); + + $this->assertNull($column->getField()); + } + + public function test_can_not_render_field_if_no_title_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + + WireLinkColumn::make('Name')->getContents(Pet::find(1)); + } + + public function test_can_not_render_field_if_no_action_callback(): void + { + $this->expectException(DataTableConfigurationException::class); + + WireLinkColumn::make('Name')->title(fn ($row) => 'Edit')->getContents(Pet::find(1)); + } + + public function test_can_render_field_if_title_and_action_callback(): void + { + $column = WireLinkColumn::make('Name')->title(fn ($row) => 'Edit')->action(fn ($row) => 'delete("'.$row->id.'")')->getContents(Pet::find(1)); + + $this->assertNotEmpty($column); + } + + public function test_can_render_field_if_confirm_set(): void + { + $column = WireLinkColumn::make('Name')->title(fn ($row) => 'Edit')->action(fn ($row) => 'delete("'.$row->id.'")')->confirmMessage('Test')->getContents(Pet::find(1)); + + $this->assertNotEmpty($column); + } + + public function test_can_add_confirm_message(): void + { + $column = WireLinkColumn::make('Name', 'name'); + + $this->assertFalse($column->hasConfirmMessage()); + + $column->confirmMessage('Test'); + + $this->assertTrue($column->hasConfirmMessage()); + + $this->assertSame('Test', $column->getConfirmMessage()); + } +} From 16314aca53306cd815f4c89fd8440465dd2e25ca Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Thu, 11 Jul 2024 01:50:46 +0100 Subject: [PATCH 033/338] Update ChangeLog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 940323134..f2f5b502f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ All notable changes to `laravel-livewire-tables` will be documented in this file -## [v3.3.0] - 2024-07-12 +## [v3.3.0] - 2024-07-11 ### New Features - Add new columns (ArrayColumn, AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1761 +- Add new column WireLinkColumn by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1763 +- Add Option to Retain Selected when Searching/Filtering by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1762 ## [v3.2.8] - 2024-07-03 ### Bug Fixes From f642ce0536e6d0664c8671146377ee005a366b36 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Thu, 11 Jul 2024 01:53:07 +0100 Subject: [PATCH 034/338] 3.3.0 (#1765) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adjust for HTML Columns * Update ChangeLog and SP * fix: Apply cursor pointer only on clickable columns when using Bootst… (#1742) * Ensure HTML Columns Return HTML Correctly (#1737) * Adjust for HTML Columns * fix: Apply cursor pointer only on clickable columns when using Bootstrap --------- Co-authored-by: Joe <104938042+lrljoe@users.noreply.github.com> * Fix styling * Fix hide bulk actions when empty not reflecting in frontend (#1747) * Fix issue with Hide Bulk Actions When Empty not reflecting in frontend * Fix styling * Add development branch into tests --------- Co-authored-by: lrljoe * Change Return Type for attributes() to static (#1749) * Switch to using Composer\InstalledVersions for AboutCommand to reduce necessity to update ServiceProvider with each update (#1748) * Two improvements to improve typehinting, migrate to larastan/larastan, cleanup of test (#1750) * Add ArrayColumn (BETA) (#1751) * Add ArrayColumn * Fix styling --------- Co-authored-by: lrljoe * Always hide bulk actions option (#1752) * Add option to "Always Hide Bulk Actions" * Fix styling * Fix test function name clash --------- Co-authored-by: lrljoe * Optionally disable count for simple pagination (#1755) * Add option for setShouldRetrieveTotalItemCountStatus * Fix styling --------- Co-authored-by: lrljoe * Update ChangeLog For 3.2.8 Release (#1754) * Update ChangeLog for 3.2.8 * Add release date * Fix phpstan unescaped | * Fix missing typehints (#1757) * Add additional typehints * Fix styling * Add filterCollection typehint * Fix styling * trUrlCallback fixes * Use Collection rather than collect() helper * Fix styling * Add ignore for "Unable to resolve the template type" for Illuminate Collection, add typehint for empty * Add ignore for $model has no defined type (allows for non Eloquent Model to be used longer term) * Adjust concurrency * Adjust Test * Adjust Again * Adjust PHPStan * Add Max Parallel * Use v4 of checkout/cache * Run one at a time * Add Clear Cache Workflow * Fix * Migrate to v4 and adjust workflows * Adjust workflow run rules * Adjust Run-Tests to separate L10 and L11 jobs * Adjust run-tests * Adjust Test * Add Laravel matrix * Adjust Concurrency * Adjust * Adjust Pull Jobs to Match Push jobs --------- Co-authored-by: lrljoe * Add CountColumn, simpler adding of WithCounts, With (#1761) * Initial Commit * Adjust CountColumn * Add ExtraWiths * Add AggregateColumn * Add SumColumn * Update Docs - Add Column Types Section * Add exceptions for empty data source, add standard tests * Ensure pcov runs on push to master/development/develop * Update to use codecov v4 --------- Co-authored-by: lrljoe * Add Option to Retain Selected when Searching/Filtering (#1762) * Initial Commit for Retaining Selected * Update Test for Search/Filter --------- Co-authored-by: lrljoe * Add WireLink Column (#1763) * Add WireLinkColumn * Add Tests for WireLinkColumn --------- Co-authored-by: lrljoe * Fix styling * Update ChangeLog --------- Co-authored-by: Matt Pickering Co-authored-by: lrljoe --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 940323134..f2f5b502f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ All notable changes to `laravel-livewire-tables` will be documented in this file -## [v3.3.0] - 2024-07-12 +## [v3.3.0] - 2024-07-11 ### New Features - Add new columns (ArrayColumn, AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1761 +- Add new column WireLinkColumn by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1763 +- Add Option to Retain Selected when Searching/Filtering by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1762 ## [v3.2.8] - 2024-07-03 ### Bug Fixes From 11fbd707eb0806e8862c6d4ea2a944d709edf03a Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Thu, 11 Jul 2024 03:21:40 +0100 Subject: [PATCH 035/338] Adjust column documentation (#1767) * Update OtherColumnTypes docs * Add Links for Additional Docs --- docs/column-types/array_column.md | 20 ++++++- docs/column-types/avg_column.md | 20 ++++++- docs/column-types/boolean_columns.md | 18 ++++++- docs/column-types/button_group_column.md | 9 +++- docs/column-types/color_columns.md | 18 ++++++- docs/column-types/component_column.md | 17 +++++- docs/column-types/count_column.md | 20 ++++++- docs/column-types/date_columns.md | 17 +++++- docs/column-types/image_columns.md | 18 ++++++- docs/column-types/link_columns.md | 18 ++++++- .../column-types/livewire_component_column.md | 29 +++++++++- docs/column-types/standard_column.md | 31 +++++++++++ docs/column-types/sum_column.md | 20 ++++++- docs/column-types/wire_link_column.md | 18 ++++++- docs/columns/available-methods.md | 2 +- docs/columns/footer.md | 2 +- docs/columns/other-column-types.md | 53 +++++++++++++++---- docs/columns/secondary-header.md | 2 +- 18 files changed, 301 insertions(+), 31 deletions(-) create mode 100644 docs/column-types/standard_column.md diff --git a/docs/column-types/array_column.md b/docs/column-types/array_column.md index 1571da2b9..7802e78aa 100644 --- a/docs/column-types/array_column.md +++ b/docs/column-types/array_column.md @@ -1,6 +1,6 @@ --- title: Array Columns (beta) -weight: 1 +weight: 2 --- Array columns provide an easy way to work with and display an array of data from a field. @@ -19,4 +19,20 @@ You may define the default/empty value using the "emptyValue" method ``` ArrayColumn::make('notes', 'name') ->emptyValue('Unknown'), -``` \ No newline at end of file +``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/avg_column.md b/docs/column-types/avg_column.md index faa653ca2..d5e4b32fb 100644 --- a/docs/column-types/avg_column.md +++ b/docs/column-types/avg_column.md @@ -1,6 +1,6 @@ --- title: Avg Columns (beta) -weight: 2 +weight: 3 --- Avg columns provide an easy way to display the "Average" of a field on a relation. @@ -11,4 +11,20 @@ Avg columns provide an easy way to display the "Average" of a field on a relatio ->sortable(), ``` -The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/boolean_columns.md b/docs/column-types/boolean_columns.md index 32a759918..8732db839 100644 --- a/docs/column-types/boolean_columns.md +++ b/docs/column-types/boolean_columns.md @@ -1,6 +1,6 @@ --- title: Boolean Columns -weight: 3 +weight: 4 --- Boolean columns are good if you have a column type that is a true/false, or 0/1 value. @@ -79,3 +79,19 @@ If you would like the BooleanColumn to display a plain Yes/No, you can set: BooleanColumn::make('Active') ->yesNo() ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/button_group_column.md b/docs/column-types/button_group_column.md index 5a3390049..da8ff7399 100644 --- a/docs/column-types/button_group_column.md +++ b/docs/column-types/button_group_column.md @@ -1,6 +1,6 @@ --- title: Button Group Columns -weight: 4 +weight: 5 --- Button group columns let you provide an array of LinkColumns to display in a single cell. @@ -32,3 +32,10 @@ ButtonGroupColumn::make('Actions') }), ]), ``` + + +Please also see the following for other available methods: +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/available-methods](Available Methods) +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/column-selection](Column Selection) +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/secondary-header](Secondary Header) +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/footer](Footer) diff --git a/docs/column-types/color_columns.md b/docs/column-types/color_columns.md index e4920144d..eeae20b2f 100644 --- a/docs/column-types/color_columns.md +++ b/docs/column-types/color_columns.md @@ -1,6 +1,6 @@ --- title: Color Columns -weight: 5 +weight: 6 --- Color columns provide an easy way to a Color in a Column @@ -39,3 +39,19 @@ You may also specify attributes to use on the div displaying the color, to adjus ]; }), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/component_column.md b/docs/column-types/component_column.md index 0db9c9483..79611121f 100644 --- a/docs/column-types/component_column.md +++ b/docs/column-types/component_column.md @@ -1,6 +1,6 @@ --- title: Component Columns -weight: 6 +weight: 7 --- Component columns let you specify a component name and attributes and provides the column value to the slot. @@ -26,3 +26,18 @@ ComponentColumn::make('E-mail', 'email') ]), ``` +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/count_column.md b/docs/column-types/count_column.md index d33cf5df9..4b1fe6c3a 100644 --- a/docs/column-types/count_column.md +++ b/docs/column-types/count_column.md @@ -1,6 +1,6 @@ --- title: Count Columns (beta) -weight: 7 +weight: 8 --- Count columns provide an easy way to display the "Count" of a relation. @@ -11,4 +11,20 @@ Count columns provide an easy way to display the "Count" of a relation. ->sortable(), ``` -The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/date_columns.md b/docs/column-types/date_columns.md index abc8bbb42..3e2e6ca0a 100644 --- a/docs/column-types/date_columns.md +++ b/docs/column-types/date_columns.md @@ -1,6 +1,6 @@ --- title: Date Columns -weight: 8 +weight: 9 --- Date columns provide an easy way to display dates in a given format, without having to use repetitive format() methods or partial views. @@ -26,3 +26,18 @@ DateColumn::make('Last Charged', 'last_charged_at') ->emptyValue('Not Found'), ``` +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/image_columns.md b/docs/column-types/image_columns.md index f816280af..5e6868792 100644 --- a/docs/column-types/image_columns.md +++ b/docs/column-types/image_columns.md @@ -1,6 +1,6 @@ --- title: Image Columns -weight: 9 +weight: 10 --- Image columns provide a way to display images in your table without having to use `format()` or partial views: @@ -24,3 +24,19 @@ ImageColumn::make('Avatar') 'alt' => $row->name . ' Avatar', ]), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/link_columns.md b/docs/column-types/link_columns.md index cba4bc887..e3f3120a4 100644 --- a/docs/column-types/link_columns.md +++ b/docs/column-types/link_columns.md @@ -1,6 +1,6 @@ --- title: Link Columns -weight: 10 +weight: 11 --- Link columns provide a way to display HTML links in your table without having to use `format()` or partial views: @@ -22,3 +22,19 @@ LinkColumn::make('Action') 'alt' => $row->name . ' Avatar', ]), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/livewire_component_column.md b/docs/column-types/livewire_component_column.md index b75172881..b12ada72e 100644 --- a/docs/column-types/livewire_component_column.md +++ b/docs/column-types/livewire_component_column.md @@ -1,5 +1,32 @@ --- title: Livewire Component (beta) -weight: 11 +weight: 12 --- +Livewire Component Columns allow for the use of a Livewire Component as a Column. + +This is **not recommended** as due to the nature of Livewire, it becomes inefficient at scale. + +## component +``` +LivewireComponentColumn::make('Action') + ->title(fn($row) => 'Edit') + ->component('PathToLivewireComponent'), + +``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/standard_column.md b/docs/column-types/standard_column.md new file mode 100644 index 000000000..6f7844982 --- /dev/null +++ b/docs/column-types/standard_column.md @@ -0,0 +1,31 @@ +--- +title: Standard Column +weight: 1 +--- + +A standard Column has a multitude of different options, making it simple for you to display your data, as documented below: + + diff --git a/docs/column-types/sum_column.md b/docs/column-types/sum_column.md index 7f99f7cab..c643253a7 100644 --- a/docs/column-types/sum_column.md +++ b/docs/column-types/sum_column.md @@ -1,6 +1,6 @@ --- title: Sum Columns (beta) -weight: 12 +weight: 13 --- Sum columns provide an easy way to display the "Sum" of a field on a relation. @@ -11,4 +11,20 @@ Sum columns provide an easy way to display the "Sum" of a field on a relation. ->sortable(), ``` -The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/wire_link_column.md b/docs/column-types/wire_link_column.md index 3577ad95b..eaee09537 100644 --- a/docs/column-types/wire_link_column.md +++ b/docs/column-types/wire_link_column.md @@ -1,6 +1,6 @@ --- title: Wire Link Column (beta) -weight: 13 +weight: 14 --- WireLink columns provide a way to display Wired Links in your table without having to use `format()` or partial views, with or without a Confirmation Message @@ -35,3 +35,19 @@ And you may also pass an array of attributes, which will be applied to the "butt 'class' => 'btn btn-danger', ]), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/columns/available-methods.md b/docs/columns/available-methods.md index ab41c7095..26f34b926 100644 --- a/docs/columns/available-methods.md +++ b/docs/columns/available-methods.md @@ -25,7 +25,7 @@ Column::make(__('Address')) ### [Multi-column sorting](../sorting/available-methods#setsinglesortingstatus) -In v2, multi-column sorting is **disabled by default**. To enable it you can set the `setSingleSortingDisabled()` method on the component. +Multi-column sorting is **disabled by default**. To enable it you can set the `setSingleSortingDisabled()` method on the component. ```php public function configure(): void diff --git a/docs/columns/footer.md b/docs/columns/footer.md index caf9a45d4..c4b3877e7 100644 --- a/docs/columns/footer.md +++ b/docs/columns/footer.md @@ -21,7 +21,7 @@ See also [footer component configuration](../footer/available-methods). ## Using a filter as a footer -As of version 2.7, you can use a filter as a footer. +You can use a filter as a footer. ```php // Example filter diff --git a/docs/columns/other-column-types.md b/docs/columns/other-column-types.md index 2e515eedf..c3a8bd154 100644 --- a/docs/columns/other-column-types.md +++ b/docs/columns/other-column-types.md @@ -3,14 +3,45 @@ title: Other Column Types weight: 4 --- - - - - -## Aggregate Columns - -### AvgColumn - -### CountColumn - -### SumColumn \ No newline at end of file + \ No newline at end of file diff --git a/docs/columns/secondary-header.md b/docs/columns/secondary-header.md index 31790b3e8..258db8d30 100644 --- a/docs/columns/secondary-header.md +++ b/docs/columns/secondary-header.md @@ -21,7 +21,7 @@ See also [secondary header component configuration](../secondary-header/availabl ## Using a filter as a secondary header -As of version 2.7, you can use a filter as a header. +You can use a filter as a header. ```php // Example filter From d6141865bb2db46e1ec4b39399c6d8378c288686 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Thu, 11 Jul 2024 03:34:40 +0100 Subject: [PATCH 036/338] 3.3.0 - Docs Update (#1768) * Adjust column documentation (#1767) --------- Co-authored-by: lrljoe --- docs/column-types/array_column.md | 20 ++++++- docs/column-types/avg_column.md | 20 ++++++- docs/column-types/boolean_columns.md | 18 ++++++- docs/column-types/button_group_column.md | 9 +++- docs/column-types/color_columns.md | 18 ++++++- docs/column-types/component_column.md | 17 +++++- docs/column-types/count_column.md | 20 ++++++- docs/column-types/date_columns.md | 17 +++++- docs/column-types/image_columns.md | 18 ++++++- docs/column-types/link_columns.md | 18 ++++++- .../column-types/livewire_component_column.md | 29 +++++++++- docs/column-types/standard_column.md | 31 +++++++++++ docs/column-types/sum_column.md | 20 ++++++- docs/column-types/wire_link_column.md | 18 ++++++- docs/columns/available-methods.md | 2 +- docs/columns/footer.md | 2 +- docs/columns/other-column-types.md | 53 +++++++++++++++---- docs/columns/secondary-header.md | 2 +- 18 files changed, 301 insertions(+), 31 deletions(-) create mode 100644 docs/column-types/standard_column.md diff --git a/docs/column-types/array_column.md b/docs/column-types/array_column.md index 1571da2b9..7802e78aa 100644 --- a/docs/column-types/array_column.md +++ b/docs/column-types/array_column.md @@ -1,6 +1,6 @@ --- title: Array Columns (beta) -weight: 1 +weight: 2 --- Array columns provide an easy way to work with and display an array of data from a field. @@ -19,4 +19,20 @@ You may define the default/empty value using the "emptyValue" method ``` ArrayColumn::make('notes', 'name') ->emptyValue('Unknown'), -``` \ No newline at end of file +``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/avg_column.md b/docs/column-types/avg_column.md index faa653ca2..d5e4b32fb 100644 --- a/docs/column-types/avg_column.md +++ b/docs/column-types/avg_column.md @@ -1,6 +1,6 @@ --- title: Avg Columns (beta) -weight: 2 +weight: 3 --- Avg columns provide an easy way to display the "Average" of a field on a relation. @@ -11,4 +11,20 @@ Avg columns provide an easy way to display the "Average" of a field on a relatio ->sortable(), ``` -The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/boolean_columns.md b/docs/column-types/boolean_columns.md index 32a759918..8732db839 100644 --- a/docs/column-types/boolean_columns.md +++ b/docs/column-types/boolean_columns.md @@ -1,6 +1,6 @@ --- title: Boolean Columns -weight: 3 +weight: 4 --- Boolean columns are good if you have a column type that is a true/false, or 0/1 value. @@ -79,3 +79,19 @@ If you would like the BooleanColumn to display a plain Yes/No, you can set: BooleanColumn::make('Active') ->yesNo() ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/button_group_column.md b/docs/column-types/button_group_column.md index 5a3390049..da8ff7399 100644 --- a/docs/column-types/button_group_column.md +++ b/docs/column-types/button_group_column.md @@ -1,6 +1,6 @@ --- title: Button Group Columns -weight: 4 +weight: 5 --- Button group columns let you provide an array of LinkColumns to display in a single cell. @@ -32,3 +32,10 @@ ButtonGroupColumn::make('Actions') }), ]), ``` + + +Please also see the following for other available methods: +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/available-methods](Available Methods) +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/column-selection](Column Selection) +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/secondary-header](Secondary Header) +- [https://rappasoft.com/docs/laravel-livewire-tables/v3/columns/footer](Footer) diff --git a/docs/column-types/color_columns.md b/docs/column-types/color_columns.md index e4920144d..eeae20b2f 100644 --- a/docs/column-types/color_columns.md +++ b/docs/column-types/color_columns.md @@ -1,6 +1,6 @@ --- title: Color Columns -weight: 5 +weight: 6 --- Color columns provide an easy way to a Color in a Column @@ -39,3 +39,19 @@ You may also specify attributes to use on the div displaying the color, to adjus ]; }), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/component_column.md b/docs/column-types/component_column.md index 0db9c9483..79611121f 100644 --- a/docs/column-types/component_column.md +++ b/docs/column-types/component_column.md @@ -1,6 +1,6 @@ --- title: Component Columns -weight: 6 +weight: 7 --- Component columns let you specify a component name and attributes and provides the column value to the slot. @@ -26,3 +26,18 @@ ComponentColumn::make('E-mail', 'email') ]), ``` +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/count_column.md b/docs/column-types/count_column.md index d33cf5df9..4b1fe6c3a 100644 --- a/docs/column-types/count_column.md +++ b/docs/column-types/count_column.md @@ -1,6 +1,6 @@ --- title: Count Columns (beta) -weight: 7 +weight: 8 --- Count columns provide an easy way to display the "Count" of a relation. @@ -11,4 +11,20 @@ Count columns provide an easy way to display the "Count" of a relation. ->sortable(), ``` -The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/date_columns.md b/docs/column-types/date_columns.md index abc8bbb42..3e2e6ca0a 100644 --- a/docs/column-types/date_columns.md +++ b/docs/column-types/date_columns.md @@ -1,6 +1,6 @@ --- title: Date Columns -weight: 8 +weight: 9 --- Date columns provide an easy way to display dates in a given format, without having to use repetitive format() methods or partial views. @@ -26,3 +26,18 @@ DateColumn::make('Last Charged', 'last_charged_at') ->emptyValue('Not Found'), ``` +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/image_columns.md b/docs/column-types/image_columns.md index f816280af..5e6868792 100644 --- a/docs/column-types/image_columns.md +++ b/docs/column-types/image_columns.md @@ -1,6 +1,6 @@ --- title: Image Columns -weight: 9 +weight: 10 --- Image columns provide a way to display images in your table without having to use `format()` or partial views: @@ -24,3 +24,19 @@ ImageColumn::make('Avatar') 'alt' => $row->name . ' Avatar', ]), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/link_columns.md b/docs/column-types/link_columns.md index cba4bc887..e3f3120a4 100644 --- a/docs/column-types/link_columns.md +++ b/docs/column-types/link_columns.md @@ -1,6 +1,6 @@ --- title: Link Columns -weight: 10 +weight: 11 --- Link columns provide a way to display HTML links in your table without having to use `format()` or partial views: @@ -22,3 +22,19 @@ LinkColumn::make('Action') 'alt' => $row->name . ' Avatar', ]), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/livewire_component_column.md b/docs/column-types/livewire_component_column.md index b75172881..b12ada72e 100644 --- a/docs/column-types/livewire_component_column.md +++ b/docs/column-types/livewire_component_column.md @@ -1,5 +1,32 @@ --- title: Livewire Component (beta) -weight: 11 +weight: 12 --- +Livewire Component Columns allow for the use of a Livewire Component as a Column. + +This is **not recommended** as due to the nature of Livewire, it becomes inefficient at scale. + +## component +``` +LivewireComponentColumn::make('Action') + ->title(fn($row) => 'Edit') + ->component('PathToLivewireComponent'), + +``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/standard_column.md b/docs/column-types/standard_column.md new file mode 100644 index 000000000..6f7844982 --- /dev/null +++ b/docs/column-types/standard_column.md @@ -0,0 +1,31 @@ +--- +title: Standard Column +weight: 1 +--- + +A standard Column has a multitude of different options, making it simple for you to display your data, as documented below: + + diff --git a/docs/column-types/sum_column.md b/docs/column-types/sum_column.md index 7f99f7cab..c643253a7 100644 --- a/docs/column-types/sum_column.md +++ b/docs/column-types/sum_column.md @@ -1,6 +1,6 @@ --- title: Sum Columns (beta) -weight: 12 +weight: 13 --- Sum columns provide an easy way to display the "Sum" of a field on a relation. @@ -11,4 +11,20 @@ Sum columns provide an easy way to display the "Sum" of a field on a relation. ->sortable(), ``` -The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. \ No newline at end of file +The "sortable()" callback can accept a callback, or you can use the default behaviour, which calculates the correct field to sort on. + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/wire_link_column.md b/docs/column-types/wire_link_column.md index 3577ad95b..eaee09537 100644 --- a/docs/column-types/wire_link_column.md +++ b/docs/column-types/wire_link_column.md @@ -1,6 +1,6 @@ --- title: Wire Link Column (beta) -weight: 13 +weight: 14 --- WireLink columns provide a way to display Wired Links in your table without having to use `format()` or partial views, with or without a Confirmation Message @@ -35,3 +35,19 @@ And you may also pass an array of attributes, which will be applied to the "butt 'class' => 'btn btn-danger', ]), ``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/columns/available-methods.md b/docs/columns/available-methods.md index ab41c7095..26f34b926 100644 --- a/docs/columns/available-methods.md +++ b/docs/columns/available-methods.md @@ -25,7 +25,7 @@ Column::make(__('Address')) ### [Multi-column sorting](../sorting/available-methods#setsinglesortingstatus) -In v2, multi-column sorting is **disabled by default**. To enable it you can set the `setSingleSortingDisabled()` method on the component. +Multi-column sorting is **disabled by default**. To enable it you can set the `setSingleSortingDisabled()` method on the component. ```php public function configure(): void diff --git a/docs/columns/footer.md b/docs/columns/footer.md index caf9a45d4..c4b3877e7 100644 --- a/docs/columns/footer.md +++ b/docs/columns/footer.md @@ -21,7 +21,7 @@ See also [footer component configuration](../footer/available-methods). ## Using a filter as a footer -As of version 2.7, you can use a filter as a footer. +You can use a filter as a footer. ```php // Example filter diff --git a/docs/columns/other-column-types.md b/docs/columns/other-column-types.md index 2e515eedf..c3a8bd154 100644 --- a/docs/columns/other-column-types.md +++ b/docs/columns/other-column-types.md @@ -3,14 +3,45 @@ title: Other Column Types weight: 4 --- - - - - -## Aggregate Columns - -### AvgColumn - -### CountColumn - -### SumColumn \ No newline at end of file + \ No newline at end of file diff --git a/docs/columns/secondary-header.md b/docs/columns/secondary-header.md index 31790b3e8..258db8d30 100644 --- a/docs/columns/secondary-header.md +++ b/docs/columns/secondary-header.md @@ -21,7 +21,7 @@ See also [secondary header component configuration](../secondary-header/availabl ## Using a filter as a secondary header -As of version 2.7, you can use a filter as a header. +You can use a filter as a header. ```php // Example filter From a3658013bc1489958d57ffdc4d2e48d5037dfc87 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sat, 13 Jul 2024 00:55:47 +0100 Subject: [PATCH 037/338] Add bulk actions button/dropdown customisations (#1771) * Add Customisations for BulkActions * Add default-colors and default-styling options * Add "Customisations" doc for Bulk Actions * Fix broken test --------- Co-authored-by: lrljoe --- docs/bulk-actions/available-methods.md | 57 +--------- docs/bulk-actions/customisations.md | 106 ++++++++++++++++++ .../toolbar/items/bulk-actions.blade.php | 56 ++++++--- .../BulkActionsConfiguration.php | 21 ++++ src/Traits/Helpers/BulkActionsHelpers.php | 19 ++++ src/Traits/WithBulkActions.php | 6 + .../BulkActionsConfigurationTest.php | 49 ++++++++ 7 files changed, 245 insertions(+), 69 deletions(-) create mode 100644 docs/bulk-actions/customisations.md diff --git a/docs/bulk-actions/available-methods.md b/docs/bulk-actions/available-methods.md index 512394c23..112b83189 100644 --- a/docs/bulk-actions/available-methods.md +++ b/docs/bulk-actions/available-methods.md @@ -184,61 +184,6 @@ public function configure(): void } ``` -## setBulkActionsThAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the table header - -```php -public function configure(): void -{ - $this->setBulkActionsThAttributes([ - 'class' => 'bg-red-500', - 'default' => false - ]); -} -``` - -## setBulkActionsThCheckboxAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the Select All/None checkbox in the Table Header - -```php -public function configure(): void -{ - $this->setBulkActionsThCheckboxAttributes([ - 'class' => 'bg-blue-500', - 'default' => false - ]); -} -``` - -## setBulkActionsTdAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the td containing the Bulk Actions Checkbox for the row - -```php -public function configure(): void -{ - $this->setBulkActionsTdAttributes([ - 'class' => 'bg-green-500', - 'default' => true - ]); -} -``` - -## setBulkActionsTdCheckboxAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Checkbox for the row - -```php -public function configure(): void -{ - $this->setBulkActionsTdCheckboxAttributes([ - 'class' => 'bg-green-500', - 'default' => true - ]); -} -``` ## setShouldAlwaysHideBulkActionsDropdownOption @@ -345,4 +290,4 @@ public function configure(): void { $this->setClearSelectedOnFilterDisabled(); } -``` +``` \ No newline at end of file diff --git a/docs/bulk-actions/customisations.md b/docs/bulk-actions/customisations.md new file mode 100644 index 000000000..dfe64e2e8 --- /dev/null +++ b/docs/bulk-actions/customisations.md @@ -0,0 +1,106 @@ +--- +title: Customising Style +weight: 5 +--- + +## setBulkActionsThAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the table header + +```php +public function configure(): void +{ + $this->setBulkActionsThAttributes([ + 'class' => 'bg-red-500', + 'default' => false + ]); +} +``` + +## setBulkActionsThCheckboxAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Select All/None checkbox in the Table Header + +```php +public function configure(): void +{ + $this->setBulkActionsThCheckboxAttributes([ + 'class' => 'bg-blue-500', + 'default' => false + ]); +} +``` + +## setBulkActionsTdAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the td containing the Bulk Actions Checkbox for the row + +```php +public function configure(): void +{ + $this->setBulkActionsTdAttributes([ + 'class' => 'bg-green-500', + 'default' => true + ]); +} +``` + +## setBulkActionsTdCheckboxAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Checkbox for the row + +```php +public function configure(): void +{ + $this->setBulkActionsTdCheckboxAttributes([ + 'class' => 'bg-green-500', + 'default' => true + ]); +} +``` + +## setBulkActionsButtonAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Button in the Toolbar + +```php +public function configure(): void +{ + $this->setBulkActionsButtonAttributes([ + 'class' => 'bg-green-500', + 'default-colors' => true, + 'default-styling' => true, + ]); +} +``` + +## setBulkActionsMenuAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Menu + +```php +public function configure(): void +{ + $this->setBulkActionsMenuAttributes([ + 'class' => 'bg-green-500', + 'default-colors' => true, + 'default-styling' => true, + ]); +} +``` + + +## setBulkActionsMenuItemAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into Items on the Bulk Actions Menu + +```php +public function configure(): void +{ + $this->setBulkActionsMenuItemAttributes([ + 'class' => 'bg-green-500', + 'default-colors' => true, + 'default-styling' => true, + ]); +} +``` diff --git a/resources/views/components/tools/toolbar/items/bulk-actions.blade.php b/resources/views/components/tools/toolbar/items/bulk-actions.blade.php index 811a27b99..6ac5bb3c3 100644 --- a/resources/views/components/tools/toolbar/items/bulk-actions.blade.php +++ b/resources/views/components/tools/toolbar/items/bulk-actions.blade.php @@ -13,10 +13,16 @@ ]) > @@ -68,10 +90,14 @@ class="block w-full px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 @else
$component->isBootstrap4(), - 'dropdown-menu dropdown-menu-end w-100' => $component->isBootstrap5(), - ]) + {{ + $attributes->merge($this->getBulkActionsMenuAttributes) + ->class([ + 'dropdown-menu dropdown-menu-right w-100' => $component->isBootstrap4() && $this->getBulkActionsMenuAttributes['default-styling'] ?? true, + 'dropdown-menu dropdown-menu-end w-100' => $component->isBootstrap5() && $this->getBulkActionsMenuAttributes['default-styling'] ?? true, + ]) + ->except('default') + }} aria-labelledby="{{ $tableName }}-bulkActionsDropdown" > @foreach ($component->getBulkActions() as $action => $title) @@ -82,9 +108,13 @@ class="block w-full px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 @endif wire:click="{{ $action }}" wire:key="{{ $tableName }}-bulk-action-{{ $action }}" - @class([ - 'dropdown-item' => $component->isBootstrap(), - ]) + {{ + $attributes->merge($this->getBulkActionsMenuItemAttributes) + ->class([ + 'dropdown-item' => $component->isBootstrap() && $this->getBulkActionsMenuItemAttributes['default-styling'] ?? true, + ]) + ->except('default') + }} > {{ $title }} diff --git a/src/Traits/Configuration/BulkActionsConfiguration.php b/src/Traits/Configuration/BulkActionsConfiguration.php index 6af318f0e..f46cb712e 100644 --- a/src/Traits/Configuration/BulkActionsConfiguration.php +++ b/src/Traits/Configuration/BulkActionsConfiguration.php @@ -213,4 +213,25 @@ public function setClearSelectedOnFilterDisabled(): self return $this; } + + public function setBulkActionsButtonAttributes(array $bulkActionsButtonAttributes): self + { + $this->bulkActionsButtonAttributes = [...$this->bulkActionsButtonAttributes, ...$bulkActionsButtonAttributes]; + + return $this; + } + + public function setBulkActionsMenuAttributes(array $bulkActionsMenuAttributes): self + { + $this->bulkActionsMenuAttributes = [...$this->bulkActionsMenuAttributes, ...$bulkActionsMenuAttributes]; + + return $this; + } + + public function setBulkActionsMenuItemAttributes(array $bulkActionsMenuItemAttributes): self + { + $this->bulkActionsMenuItemAttributes = [...$this->bulkActionsMenuItemAttributes, ...$bulkActionsMenuItemAttributes]; + + return $this; + } } diff --git a/src/Traits/Helpers/BulkActionsHelpers.php b/src/Traits/Helpers/BulkActionsHelpers.php index 77a263635..542e31bb4 100644 --- a/src/Traits/Helpers/BulkActionsHelpers.php +++ b/src/Traits/Helpers/BulkActionsHelpers.php @@ -230,4 +230,23 @@ public function getClearSelectedOnFilter(): bool { return $this->clearSelectedOnFilter ?? true; } + + #[Computed] + public function getBulkActionsButtonAttributes(): array + { + return array_merge(['default-colors' => true, 'default-styling' => true], $this->bulkActionsButtonAttributes); + + } + + #[Computed] + public function getBulkActionsMenuAttributes(): array + { + return array_merge(['default-colors' => true, 'default-styling' => true], $this->bulkActionsMenuAttributes); + } + + #[Computed] + public function getBulkActionsMenuItemAttributes(): array + { + return array_merge(['default-colors' => true, 'default-styling' => true], $this->bulkActionsMenuItemAttributes); + } } diff --git a/src/Traits/WithBulkActions.php b/src/Traits/WithBulkActions.php index 37a5cd111..13a516974 100644 --- a/src/Traits/WithBulkActions.php +++ b/src/Traits/WithBulkActions.php @@ -40,6 +40,12 @@ trait WithBulkActions public bool $clearSelectedOnFilter = true; + protected array $bulkActionsButtonAttributes = ['default-colors' => true, 'default-styling' => true]; + + protected array $bulkActionsMenuAttributes = ['default-colors' => true, 'default-styling' => true]; + + protected array $bulkActionsMenuItemAttributes = ['default-colors' => true, 'default-styling' => true]; + public function bulkActions(): array { return property_exists($this, 'bulkActions') ? $this->bulkActions : []; diff --git a/tests/Traits/Configuration/BulkActionsConfigurationTest.php b/tests/Traits/Configuration/BulkActionsConfigurationTest.php index b2f24a499..7cf864bf8 100644 --- a/tests/Traits/Configuration/BulkActionsConfigurationTest.php +++ b/tests/Traits/Configuration/BulkActionsConfigurationTest.php @@ -193,4 +193,53 @@ public function test_can_set_bulk_actions_th_checkbox_attributes(): void $this->assertSame(['default' => false, 'class' => 'bg-green-500'], $this->basicTable->getBulkActionsThCheckboxAttributes()); } + + public function test_can_set_bulk_actions_button_attributes(): void + { + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + $this->basicTable->setBulkActionsButtonAttributes(['default-colors' => false, 'class' => 'bg-green-500']); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-green-500'], $this->basicTable->getBulkActionsButtonAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + $this->basicTable->setBulkActionsButtonAttributes(['class' => 'bg-green-500', 'default-colors' => false]); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-green-500'], $this->basicTable->getBulkActionsButtonAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + } + + public function test_can_set_bulk_actions_menu_attributes(): void + { + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + + $this->basicTable->setBulkActionsMenuAttributes(['class' => 'bg-blue-500']); + + $this->assertSame(['default-colors' => true, 'default-styling' => true, 'class' => 'bg-blue-500'], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + + $this->basicTable->setBulkActionsMenuAttributes(['class' => 'bg-blue-500', 'default-colors' => false]); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-blue-500'], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + } + + public function test_can_set_bulk_actions_menu_item_attributes(): void + { + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuItemAttributes()); + + $this->basicTable->setBulkActionsMenuItemAttributes(['class' => 'bg-red-500']); + + $this->assertSame(['default-colors' => true, 'default-styling' => true, 'class' => 'bg-red-500'], $this->basicTable->getBulkActionsMenuItemAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + $this->basicTable->setBulkActionsMenuItemAttributes(['class' => 'bg-amber-500', 'default-colors' => false]); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-amber-500'], $this->basicTable->getBulkActionsMenuItemAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + } } From 73d761bc31a3694ebebb43340186aa41723675be Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sat, 13 Jul 2024 04:53:13 +0100 Subject: [PATCH 038/338] Add Separator customisation for Array Filters for FilterPills (#1772) * Add Filter Pills Separation Methods * Add FilterVisuals test for Separator --------- Co-authored-by: lrljoe --- docs/filter-types/filters-date.md | 2 - docs/filter-types/filters-daterange.md | 2 - docs/filter-types/filters-datetime.md | 2 - .../filters-livewire-component.md | 2 - .../filters-multiselect-dropdown.md | 26 +++- docs/filter-types/filters-multiselect.md | 25 +++- docs/filter-types/filters-number-range.md | 2 - docs/filter-types/filters-number.md | 2 - docs/filter-types/filters-select.md | 2 - docs/filter-types/filters-text.md | 2 - docs/filter-types/introduction.md | 33 +++++ .../components/tools/filter-pills.blade.php | 12 +- src/Views/Filters/DateFilter.php | 2 +- src/Views/Filters/DateRangeFilter.php | 2 +- src/Views/Filters/DateTimeFilter.php | 2 +- .../Filters/MultiSelectDropdownFilter.php | 4 +- src/Views/Filters/MultiSelectFilter.php | 4 +- src/Views/Filters/NumberRangeFilter.php | 2 +- src/Views/Filters/SelectFilter.php | 2 +- src/Views/Traits/Filters/IsArrayFilter.php | 14 ++ src/Views/Traits/Helpers/FilterHelpers.php | 2 +- .../Http/Livewire/PetsTableCustomFilters.php | 138 ++++++++++++++++++ tests/Traits/Visuals/FilterVisualsTest.php | 57 ++++++++ .../Filters/MultiSelectDropdownFilterTest.php | 13 +- tests/Views/Filters/MultiSelectFilterTest.php | 7 + 25 files changed, 327 insertions(+), 34 deletions(-) create mode 100644 tests/Http/Livewire/PetsTableCustomFilters.php diff --git a/docs/filter-types/filters-date.md b/docs/filter-types/filters-date.md index 488cda84f..d29a900df 100644 --- a/docs/filter-types/filters-date.md +++ b/docs/filter-types/filters-date.md @@ -3,8 +3,6 @@ title: Date Filters weight: 2 --- -## Date Filters - Date filters are HTML date elements. ```php diff --git a/docs/filter-types/filters-daterange.md b/docs/filter-types/filters-daterange.md index 37d0ea552..742719db1 100644 --- a/docs/filter-types/filters-daterange.md +++ b/docs/filter-types/filters-daterange.md @@ -3,8 +3,6 @@ title: DateRange Filters weight: 3 --- -## DateRange Filters - DateRange filters are Flatpickr based components, and simply filtering by a date range. If you would like to more smoothly filter your query by a start and end date, you can use the DateRangeFilter: ```php diff --git a/docs/filter-types/filters-datetime.md b/docs/filter-types/filters-datetime.md index 288d90b4b..64c14eca4 100644 --- a/docs/filter-types/filters-datetime.md +++ b/docs/filter-types/filters-datetime.md @@ -3,8 +3,6 @@ title: DateTime Filters weight: 4 --- -## DateTime Filters - DateTime filters are HTML datetime-local elements and act the same as date filters. ```php diff --git a/docs/filter-types/filters-livewire-component.md b/docs/filter-types/filters-livewire-component.md index d11d337f9..5ffb6dac6 100644 --- a/docs/filter-types/filters-livewire-component.md +++ b/docs/filter-types/filters-livewire-component.md @@ -3,8 +3,6 @@ title: Livewire Custom Filter (Beta) weight: 11 --- -## Livewire Custom Filter - **IN BETA** This feature is currently in beta, and use in production is not recommended. diff --git a/docs/filter-types/filters-multiselect-dropdown.md b/docs/filter-types/filters-multiselect-dropdown.md index d81c14570..2f3ed9c01 100644 --- a/docs/filter-types/filters-multiselect-dropdown.md +++ b/docs/filter-types/filters-multiselect-dropdown.md @@ -3,9 +3,6 @@ title: Multi-Select Dropdown Filters weight: 5 --- - -## Multi-select dropdown Filters - Multi-select dropdown filters are a simple dropdown list. The user can select multiple options from the list. There is also an 'All' option that will select all values ```php @@ -27,3 +24,26 @@ public function filters(): array ]; } ``` + +## Filter Pills Separator + +As this filter returns one or more values, you have the option to utilise a custom separator for the values displayed in the Filter Pills section at the top of the table. The default is ", ", but you may use any HTML string to separate the selected values + +```php +public function filters(): array +{ + return [ + MultiSelectFilter::make('Tags') + ->options( + Tag::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn($tag) => $tag->name) + ->toArray() + ) + ->setPillsSeparator('
'), + ]; +} + +``` \ No newline at end of file diff --git a/docs/filter-types/filters-multiselect.md b/docs/filter-types/filters-multiselect.md index f6c6bfa37..f91a65912 100644 --- a/docs/filter-types/filters-multiselect.md +++ b/docs/filter-types/filters-multiselect.md @@ -3,8 +3,6 @@ title: Multi-Select Filters weight: 6 --- -## Multi-select Filters - Multi-select filters are a list of checkboxes. The user can select multiple options from the list. There is also an 'All' option that will select all values. ```php @@ -25,3 +23,26 @@ public function filters(): array ]; } ``` + +## Filter Pills Separator + +As this filter returns one or more values, you have the option to utilise a custom separator for the values displayed in the Filter Pills section at the top of the table. The default is ", ", but you may use any HTML string to separate the selected values + +```php +public function filters(): array +{ + return [ + MultiSelectFilter::make('Tags') + ->options( + Tag::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn($tag) => $tag->name) + ->toArray() + ) + ->setPillsSeparator('
'), + ]; +} + +``` \ No newline at end of file diff --git a/docs/filter-types/filters-number-range.md b/docs/filter-types/filters-number-range.md index 0969e5c33..bb0c7a04a 100644 --- a/docs/filter-types/filters-number-range.md +++ b/docs/filter-types/filters-number-range.md @@ -3,8 +3,6 @@ title: NumberRange Filters weight: 7 --- -## NumberRange Filters - NumberRange filters allow for a minimum and maximum value to be input on a single slider. ```php diff --git a/docs/filter-types/filters-number.md b/docs/filter-types/filters-number.md index 3b629dd0a..e1423ae9c 100644 --- a/docs/filter-types/filters-number.md +++ b/docs/filter-types/filters-number.md @@ -3,8 +3,6 @@ title: Number Filters weight: 8 --- -## Number Filters - Number filters are just HTML number inputs. ```php diff --git a/docs/filter-types/filters-select.md b/docs/filter-types/filters-select.md index d28cfba94..568522f1b 100644 --- a/docs/filter-types/filters-select.md +++ b/docs/filter-types/filters-select.md @@ -3,8 +3,6 @@ title: Select Filters weight: 9 --- -## Select Filters - Select filters are a simple dropdown list. The user selects one choice from the list. ```php diff --git a/docs/filter-types/filters-text.md b/docs/filter-types/filters-text.md index 649384915..22255dfd4 100644 --- a/docs/filter-types/filters-text.md +++ b/docs/filter-types/filters-text.md @@ -3,8 +3,6 @@ title: Text Filters weight: 10 --- -## Text Filters - Text filters are just HTML text fields. ```php diff --git a/docs/filter-types/introduction.md b/docs/filter-types/introduction.md index d509cdc39..c553de5f9 100644 --- a/docs/filter-types/introduction.md +++ b/docs/filter-types/introduction.md @@ -4,3 +4,36 @@ weight: 1 --- There are several Filter types available for use, offering a range of capabilities to filter your data. + + \ No newline at end of file diff --git a/resources/views/components/tools/filter-pills.blade.php b/resources/views/components/tools/filter-pills.blade.php index afb9568a7..5079909d7 100644 --- a/resources/views/components/tools/filter-pills.blade.php +++ b/resources/views/components/tools/filter-pills.blade.php @@ -30,7 +30,17 @@ 'badge rounded-pill bg-info d-inline-flex align-items-center' => $component->isBootstrap5(), ]) > - {{ $filter->getFilterPillTitle() }}: {{ $filter->getFilterPillValue($value) }} + {{ $filter->getFilterPillTitle() }}: + @php( $filterPillValue = $filter->getFilterPillValue($value)) + @php( $separator = method_exists($filter, 'getPillsSeparator') ? $filter->getPillsSeparator() : ', ') + + @if(is_array($filterPillValue) && !empty($filterPillValue)) + @foreach($filterPillValue as $filterPillArrayValue) + {{ $filterPillArrayValue }}{!! $separator !!} + @endforeach + @else + {{ $filterPillValue }} + @endif @if ($component->isTailwind())
diff --git a/resources/views/components/tools/filters/number-range.blade.php b/resources/views/components/tools/filters/number-range.blade.php index 8fe85bf06..b04ae3c5a 100644 --- a/resources/views/components/tools/filters/number-range.blade.php +++ b/resources/views/components/tools/filters/number-range.blade.php @@ -2,7 +2,8 @@ $filterKey = $filter->getKey(); $currentMin = $minRange = $filter->getConfig('minRange'); $currentMax = $maxRange = $filter->getConfig('maxRange'); - $suffix = $filter->getConfig('suffix'); + $suffix = $filter->hasConfig('suffix') ? '--suffix:"'. $filter->getConfig('suffix') .'";' : ''; + $prefix = $filter->hasConfig('prefix') ? '--prefix:"'.$filter->getConfig('prefix').'";' : ''; @endphp
@@ -19,7 +20,7 @@ 'range-slider flat' => $isTailwind, 'range-slider flat w-100' => $isBootstrap, ]) - style=' --min:{{ $minRange }}; --max:{{ $maxRange }}; --suffix:"{{ $suffix }}";' + style=' --min:{{ $minRange }}; --max:{{ $maxRange }}; {{ $suffix . $prefix }}' >
- \ No newline at end of file + From 6486d66a77e02319b1f34afbd9aa21639d44c047 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 16 Jul 2024 02:30:09 +0100 Subject: [PATCH 042/338] Update ChangeLog --- CHANGELOG.md | 9 +++++++++ docs/column-types/standard_column.md | 1 + 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f5b502f..f5def0fe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.3.1] - 2024-07-16 +### Bug Fixes +- Fix NumberRangeFilter initial state when loaded by querystring by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1774 + +### New Features +- Set a Prefix for NumberRangeFilter by @RenoLooijmans in https://github.com/rappasoft/laravel-livewire-tables/pull/1773 +- Add Separator customisation for Array Filters for FilterPills by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1772 +- Add bulk actions button/dropdown customisations by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1771 + ## [v3.3.0] - 2024-07-11 ### New Features - Add new columns (ArrayColumn, AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1761 diff --git a/docs/column-types/standard_column.md b/docs/column-types/standard_column.md index 6f7844982..2c3b767af 100644 --- a/docs/column-types/standard_column.md +++ b/docs/column-types/standard_column.md @@ -29,3 +29,4 @@ A standard Column has a multitude of different options, making it simple for you + From 10b0bc5144547ac509d88023bfb888f1fea111fe Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 16 Jul 2024 02:31:18 +0100 Subject: [PATCH 043/338] Add "prefix" to docs --- docs/filter-types/filters-number-range.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/filter-types/filters-number-range.md b/docs/filter-types/filters-number-range.md index bb0c7a04a..938900ef9 100644 --- a/docs/filter-types/filters-number-range.md +++ b/docs/filter-types/filters-number-range.md @@ -20,6 +20,7 @@ public function filters(): array 'minRange' => 0, 'maxRange' => 100, 'suffix' => '%', + 'prefix' => '$', ]) ->filter(function (Builder $builder, array $values) { $builder->where('users.success_rate', '>=', intval($values['min'])) @@ -32,4 +33,6 @@ public function filters(): array The default values should be set in the options() method. -You may also specify a minimum and maximum range in the config() options, and should you wish to use real values instead of a percentage, you can change the "suffix" to a metric of your choosing. +You may also specify a minimum and maximum range in the config() options, and should you wish to use real values instead of a percentage. +You can change the "suffix" to a metric of your choosing. +You can change the "prefix" to an item of your choosing (e.g $/£/€) From 7a8ada62e331d2ffbb1e38e8009516d7ecdc8496 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 16 Jul 2024 02:53:23 +0100 Subject: [PATCH 044/338] Fix aggregate column snake case (#1777) * Add SnakeCaseToLabel --------- Co-authored-by: lrljoe --- .../Traits/Configuration/AggregateColumnConfiguration.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Views/Traits/Configuration/AggregateColumnConfiguration.php b/src/Views/Traits/Configuration/AggregateColumnConfiguration.php index 2f181ed2f..9e12611dd 100644 --- a/src/Views/Traits/Configuration/AggregateColumnConfiguration.php +++ b/src/Views/Traits/Configuration/AggregateColumnConfiguration.php @@ -3,6 +3,7 @@ namespace Rappasoft\LaravelLivewireTables\Views\Traits\Configuration; use Illuminate\Database\Eloquent\{Builder, Model}; +use Illuminate\Support\Str; use Rappasoft\LaravelLivewireTables\Views\Column; trait AggregateColumnConfiguration @@ -40,10 +41,10 @@ public function setDefaultLabel(): void { $this->label(function ($row, Column $column) { if ($this->hasForeignColumn()) { - return $row->{$this->getDataSource().'_'.$this->getAggregateMethod().'_'.$this->getForeignColumn()}; + return $row->{Str::snake($this->getDataSource()).'_'.$this->getAggregateMethod().'_'.$this->getForeignColumn()}; } - return $row->{$this->getDataSource().'_'.$this->getAggregateMethod()}; + return $row->{Str::snake($this->getDataSource()).'_'.$this->getAggregateMethod()}; }); } From bcf732a960fa0f7b988ce2cd24ea3ef56b02e598 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:19:12 +0100 Subject: [PATCH 045/338] Add bug fix for SnakeCase --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5def0fe4..09a0c4d88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to `laravel-livewire-tables` will be documented in this file ## [v3.3.1] - 2024-07-16 ### Bug Fixes - Fix NumberRangeFilter initial state when loaded by querystring by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1774 +- Fix SnakeCase issue with snake-cased relations for AggregateColumn types (AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1777 ### New Features - Set a Prefix for NumberRangeFilter by @RenoLooijmans in https://github.com/rappasoft/laravel-livewire-tables/pull/1773 From 3f96b3620946c0c38f39d9a2b4a8130043ea4c5b Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 16 Jul 2024 03:29:50 +0100 Subject: [PATCH 046/338] Update changelog - missed item in 3.3.0 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09a0c4d88..165e4b733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to `laravel-livewire-tables` will be documented in this file - Add new columns (ArrayColumn, AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1761 - Add new column WireLinkColumn by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1763 - Add Option to Retain Selected when Searching/Filtering by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1762 +- Add Option to set locale and default date to DateRangeFilter by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1766 ## [v3.2.8] - 2024-07-03 ### Bug Fixes From 6fb0016f26cc87b30dd2b7508c1ff2b783ea0786 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 16 Jul 2024 04:09:44 +0100 Subject: [PATCH 047/338] 3.3.1 Release (#1776) ### Bug Fixes - Fix NumberRangeFilter initial state when loaded by querystring by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1774 - Fix SnakeCase issue with snake-cased relations for AggregateColumn types (AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1777 ### New Features - Set a Prefix for NumberRangeFilter by @RenoLooijmans in https://github.com/rappasoft/laravel-livewire-tables/pull/1773 - Add Separator customisation for Array Filters for FilterPills by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1772 - Add bulk actions button/dropdown customisations by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1771 Co-authored-by: lrljoe --- CHANGELOG.md | 11 ++ config/livewire-tables.php | 4 +- docs/bulk-actions/available-methods.md | 57 +------- docs/bulk-actions/customisations.md | 106 ++++++++++++++ docs/column-types/standard_column.md | 2 +- docs/filter-types/filters-date.md | 2 - docs/filter-types/filters-daterange.md | 87 ++++++++--- docs/filter-types/filters-datetime.md | 2 - .../filters-livewire-component.md | 2 - .../filters-multiselect-dropdown.md | 26 +++- docs/filter-types/filters-multiselect.md | 25 +++- docs/filter-types/filters-number-range.md | 7 +- docs/filter-types/filters-number.md | 2 - docs/filter-types/filters-select.md | 2 - docs/filter-types/filters-text.md | 2 - docs/filter-types/introduction.md | 33 +++++ resources/css/laravel-livewire-tables.css | 2 - resources/css/laravel-livewire-tables.min.css | 2 +- resources/js/laravel-livewire-tables.js | 26 ++-- resources/js/laravel-livewire-tables.min.js | 2 +- resources/views/components/table/th.blade.php | 6 +- .../components/tools/filter-pills.blade.php | 12 +- .../tools/filters/number-range.blade.php | 56 +++---- .../toolbar/items/bulk-actions.blade.php | 56 +++++-- .../BulkActionsConfiguration.php | 21 +++ src/Traits/Helpers/BulkActionsHelpers.php | 19 +++ src/Traits/WithBulkActions.php | 6 + src/Views/Filters/DateFilter.php | 2 +- src/Views/Filters/DateRangeFilter.php | 2 +- src/Views/Filters/DateTimeFilter.php | 2 +- .../Filters/MultiSelectDropdownFilter.php | 4 +- src/Views/Filters/MultiSelectFilter.php | 4 +- src/Views/Filters/NumberRangeFilter.php | 2 +- src/Views/Filters/SelectFilter.php | 2 +- .../AggregateColumnConfiguration.php | 5 +- src/Views/Traits/Filters/IsArrayFilter.php | 14 ++ src/Views/Traits/Helpers/FilterHelpers.php | 2 +- .../Http/Livewire/PetsTableCustomFilters.php | 138 ++++++++++++++++++ .../BulkActionsConfigurationTest.php | 49 +++++++ tests/Traits/Visuals/FilterVisualsTest.php | 57 ++++++++ tests/Views/Filters/DateRangeFilterTest.php | 32 ++++ .../Filters/MultiSelectDropdownFilterTest.php | 13 +- tests/Views/Filters/MultiSelectFilterTest.php | 7 + 43 files changed, 745 insertions(+), 168 deletions(-) create mode 100644 docs/bulk-actions/customisations.md create mode 100644 tests/Http/Livewire/PetsTableCustomFilters.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f5b502f..165e4b733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,22 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.3.1] - 2024-07-16 +### Bug Fixes +- Fix NumberRangeFilter initial state when loaded by querystring by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1774 +- Fix SnakeCase issue with snake-cased relations for AggregateColumn types (AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1777 + +### New Features +- Set a Prefix for NumberRangeFilter by @RenoLooijmans in https://github.com/rappasoft/laravel-livewire-tables/pull/1773 +- Add Separator customisation for Array Filters for FilterPills by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1772 +- Add bulk actions button/dropdown customisations by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1771 + ## [v3.3.0] - 2024-07-11 ### New Features - Add new columns (ArrayColumn, AvgColumn, CountColumn, SumColumn) by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1761 - Add new column WireLinkColumn by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1763 - Add Option to Retain Selected when Searching/Filtering by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1762 +- Add Option to set locale and default date to DateRangeFilter by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1766 ## [v3.2.8] - 2024-07-03 ### Bug Fixes diff --git a/config/livewire-tables.php b/config/livewire-tables.php index 1e0b0b04c..4cc3dccab 100644 --- a/config/livewire-tables.php +++ b/config/livewire-tables.php @@ -72,6 +72,7 @@ 'dateFormat' => 'Y-m-d', // Date format that will be received by the filter 'earliestDate' => null, // The earliest acceptable date 'latestDate' => null, // The latest acceptable date + 'locale' => 'en', // The default locale ], ], @@ -87,6 +88,7 @@ 'minRange' => 0, // The minimum possible value 'maxRange' => 100, // The maximum possible value 'suffix' => '', // A suffix to append to the values when displayed + 'prefix' => '', // A prefix to prepend to the values when displayed ], ], /** @@ -105,7 +107,7 @@ ], /** - * Configuration options for MultiSelectFilter + * Configuration options for MultiSelectDropdownFilter */ 'multiSelectDropdownFilter' => [ 'defaultOptions' => [], diff --git a/docs/bulk-actions/available-methods.md b/docs/bulk-actions/available-methods.md index 512394c23..112b83189 100644 --- a/docs/bulk-actions/available-methods.md +++ b/docs/bulk-actions/available-methods.md @@ -184,61 +184,6 @@ public function configure(): void } ``` -## setBulkActionsThAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the table header - -```php -public function configure(): void -{ - $this->setBulkActionsThAttributes([ - 'class' => 'bg-red-500', - 'default' => false - ]); -} -``` - -## setBulkActionsThCheckboxAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the Select All/None checkbox in the Table Header - -```php -public function configure(): void -{ - $this->setBulkActionsThCheckboxAttributes([ - 'class' => 'bg-blue-500', - 'default' => false - ]); -} -``` - -## setBulkActionsTdAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the td containing the Bulk Actions Checkbox for the row - -```php -public function configure(): void -{ - $this->setBulkActionsTdAttributes([ - 'class' => 'bg-green-500', - 'default' => true - ]); -} -``` - -## setBulkActionsTdCheckboxAttributes - -You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Checkbox for the row - -```php -public function configure(): void -{ - $this->setBulkActionsTdCheckboxAttributes([ - 'class' => 'bg-green-500', - 'default' => true - ]); -} -``` ## setShouldAlwaysHideBulkActionsDropdownOption @@ -345,4 +290,4 @@ public function configure(): void { $this->setClearSelectedOnFilterDisabled(); } -``` +``` \ No newline at end of file diff --git a/docs/bulk-actions/customisations.md b/docs/bulk-actions/customisations.md new file mode 100644 index 000000000..dfe64e2e8 --- /dev/null +++ b/docs/bulk-actions/customisations.md @@ -0,0 +1,106 @@ +--- +title: Customising Style +weight: 5 +--- + +## setBulkActionsThAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the table header + +```php +public function configure(): void +{ + $this->setBulkActionsThAttributes([ + 'class' => 'bg-red-500', + 'default' => false + ]); +} +``` + +## setBulkActionsThCheckboxAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Select All/None checkbox in the Table Header + +```php +public function configure(): void +{ + $this->setBulkActionsThCheckboxAttributes([ + 'class' => 'bg-blue-500', + 'default' => false + ]); +} +``` + +## setBulkActionsTdAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the td containing the Bulk Actions Checkbox for the row + +```php +public function configure(): void +{ + $this->setBulkActionsTdAttributes([ + 'class' => 'bg-green-500', + 'default' => true + ]); +} +``` + +## setBulkActionsTdCheckboxAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Checkbox for the row + +```php +public function configure(): void +{ + $this->setBulkActionsTdCheckboxAttributes([ + 'class' => 'bg-green-500', + 'default' => true + ]); +} +``` + +## setBulkActionsButtonAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Button in the Toolbar + +```php +public function configure(): void +{ + $this->setBulkActionsButtonAttributes([ + 'class' => 'bg-green-500', + 'default-colors' => true, + 'default-styling' => true, + ]); +} +``` + +## setBulkActionsMenuAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the Bulk Actions Menu + +```php +public function configure(): void +{ + $this->setBulkActionsMenuAttributes([ + 'class' => 'bg-green-500', + 'default-colors' => true, + 'default-styling' => true, + ]); +} +``` + + +## setBulkActionsMenuItemAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into Items on the Bulk Actions Menu + +```php +public function configure(): void +{ + $this->setBulkActionsMenuItemAttributes([ + 'class' => 'bg-green-500', + 'default-colors' => true, + 'default-styling' => true, + ]); +} +``` diff --git a/docs/column-types/standard_column.md b/docs/column-types/standard_column.md index 6f7844982..7d5b4ede3 100644 --- a/docs/column-types/standard_column.md +++ b/docs/column-types/standard_column.md @@ -28,4 +28,4 @@ A standard Column has a multitude of different options, making it simple for you Anonymous Columns - + \ No newline at end of file diff --git a/docs/filter-types/filters-date.md b/docs/filter-types/filters-date.md index 488cda84f..d29a900df 100644 --- a/docs/filter-types/filters-date.md +++ b/docs/filter-types/filters-date.md @@ -3,8 +3,6 @@ title: Date Filters weight: 2 --- -## Date Filters - Date filters are HTML date elements. ```php diff --git a/docs/filter-types/filters-daterange.md b/docs/filter-types/filters-daterange.md index 37d0ea552..e496b31ed 100644 --- a/docs/filter-types/filters-daterange.md +++ b/docs/filter-types/filters-daterange.md @@ -3,8 +3,6 @@ title: DateRange Filters weight: 3 --- -## DateRange Filters - DateRange filters are Flatpickr based components, and simply filtering by a date range. If you would like to more smoothly filter your query by a start and end date, you can use the DateRangeFilter: ```php @@ -33,6 +31,7 @@ public function filters(): array 'earliestDate' => '2020-01-01', // The earliest acceptable date 'latestDate' => '2023-08-01', // The latest acceptable date 'placeholder' => 'Enter Date Range', // A placeholder value + 'locale' => 'en', ]) ->setFilterPillValues([0 => 'minDate', 1 => 'maxDate']) // The values that will be displayed for the Min/Max Date Values ->filter(function (Builder $builder, array $dateRange) { // Expects an array. @@ -44,31 +43,85 @@ public function filters(): array } ``` +## Configuration +By default, this filter will inject the Flatpickr JS Library and CSS. However, you can customise this behaviour using the configuration file. + +### Option 1 - The default behaviour: +``` + 'inject_third_party_assets_enabled' => true, +``` +### Option 2 - Bundled +If you choose to bundle the Tables JS/CSS (recommended) by adding the following to your build process: -## Configuration -By default, this filter will use a CDN to include the Flatpickr JS Library and CSS. However, you can customise this behaviour using the configuration file. +``` +'vendor/rappasoft/laravel-livewire-tables/resources/js/laravel-livewire-tables-thirdparty.min.js'; +``` + +or in your app.js + +``` +import '../../vendor/rappasoft/livewire-tables/resources/js/laravel-livewire-tables-thirdparty.min.js'; +``` -### Option 1 - The default CDN behaviour: +Then you should disable injection to avoid conflicts: + +``` + 'inject_third_party_assets_enabled' => false, +``` + +### Option 3 - CDN +You must ensure that Flatpickr is present PRIOR to the tables loading. For example, to add Flatpickr with the Spanish locale, ensure that the following is present in your Layout head section. Noting the "async" presence to ensure that the script is present before a page renders. + +It is typically recommended not to utilise the CDN approach, as changes to core code may impact behaviour, and you may need to implement changes to your CSP if present. + +If using the CDN approach, ensure the following config matches: ``` - 'published_third_party_assets' => false, - 'remote_third_party_assets' => true, + 'inject_third_party_assets_enabled' => false, ``` -### Option 2 - Publish included version -You may publish the included version of Flatpickr. To do so, run: +Then include the following in your layout: ``` -php artisan vendor:publish --tag=livewire-tables-public +// Flatpickr Core Script + + +// Flatpickr Core CSS + ``` -This will publish the tested version of Flatpickr to your public directory. You should then set + +### Option 4 - Locally Installed +If you have a locally installed version of Flatpickr already, you can set injection to false, and your local version will be used instead. ``` - 'published_third_party_assets' => true, - 'remote_third_party_assets' => false, + 'inject_third_party_assets_enabled' => false, ``` -### Option 3 - Locally Installed -If you have a locally installed version of Flatpickr already, you can set both options to false, and your local version will be used instead. +## Localisation (BETA) +The default installation includes only the English (en) locale. + +### Bundling +Should you wish to localise, you must include the Flatpickr locale files in your build pipeline. This applies to only the specific locales that you require in your app.js (requires adding the flatpickr library to your package.json by executing "npm i flatpickr --save") ``` - 'published_third_party_assets' => false, - 'remote_third_party_assets' => false, +import { Arabic } from "../imports/flatpickr/l10n/ar.js"; +import { Catalan } from "../imports/flatpickr/l10n/cat.js"; +import { Danish } from "../imports/flatpickr/l10n/da.js"; +import { German } from "../imports/flatpickr/l10n/de.js"; +import { Spanish } from "../imports/flatpickr/l10n/es.js"; +import { French } from "../imports/flatpickr/l10n/fr.js"; +import { Indonesian } from "../imports/flatpickr/l10n/id.js"; +import { Italian } from "../imports/flatpickr/l10n/it.js"; +import { Malaysian } from "../imports/flatpickr/l10n/ms.js"; +import { Dutch } from "../imports/flatpickr/l10n/nl.js"; +import { Portuguese } from "../imports/flatpickr/l10n/pt.js"; +import { Russian } from "../imports/flatpickr/l10n/ru.js" +import { Thai } from "../imports/flatpickr/l10n/th.js" +import { Turkish } from "../imports/flatpickr/l10n/tr.js" +import { Ukrainian } from "../imports/flatpickr/l10n/uk.js" +``` + +### CDN +You can also add locales using the Flatpickr CDN, ensuring that these are loaded before the page renders. + +For example to add German (de), ensure that the following is in the "head" section of your layout, ideally before your app.js ``` + +``` \ No newline at end of file diff --git a/docs/filter-types/filters-datetime.md b/docs/filter-types/filters-datetime.md index 288d90b4b..64c14eca4 100644 --- a/docs/filter-types/filters-datetime.md +++ b/docs/filter-types/filters-datetime.md @@ -3,8 +3,6 @@ title: DateTime Filters weight: 4 --- -## DateTime Filters - DateTime filters are HTML datetime-local elements and act the same as date filters. ```php diff --git a/docs/filter-types/filters-livewire-component.md b/docs/filter-types/filters-livewire-component.md index d11d337f9..5ffb6dac6 100644 --- a/docs/filter-types/filters-livewire-component.md +++ b/docs/filter-types/filters-livewire-component.md @@ -3,8 +3,6 @@ title: Livewire Custom Filter (Beta) weight: 11 --- -## Livewire Custom Filter - **IN BETA** This feature is currently in beta, and use in production is not recommended. diff --git a/docs/filter-types/filters-multiselect-dropdown.md b/docs/filter-types/filters-multiselect-dropdown.md index d81c14570..2f3ed9c01 100644 --- a/docs/filter-types/filters-multiselect-dropdown.md +++ b/docs/filter-types/filters-multiselect-dropdown.md @@ -3,9 +3,6 @@ title: Multi-Select Dropdown Filters weight: 5 --- - -## Multi-select dropdown Filters - Multi-select dropdown filters are a simple dropdown list. The user can select multiple options from the list. There is also an 'All' option that will select all values ```php @@ -27,3 +24,26 @@ public function filters(): array ]; } ``` + +## Filter Pills Separator + +As this filter returns one or more values, you have the option to utilise a custom separator for the values displayed in the Filter Pills section at the top of the table. The default is ", ", but you may use any HTML string to separate the selected values + +```php +public function filters(): array +{ + return [ + MultiSelectFilter::make('Tags') + ->options( + Tag::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn($tag) => $tag->name) + ->toArray() + ) + ->setPillsSeparator('
'), + ]; +} + +``` \ No newline at end of file diff --git a/docs/filter-types/filters-multiselect.md b/docs/filter-types/filters-multiselect.md index f6c6bfa37..f91a65912 100644 --- a/docs/filter-types/filters-multiselect.md +++ b/docs/filter-types/filters-multiselect.md @@ -3,8 +3,6 @@ title: Multi-Select Filters weight: 6 --- -## Multi-select Filters - Multi-select filters are a list of checkboxes. The user can select multiple options from the list. There is also an 'All' option that will select all values. ```php @@ -25,3 +23,26 @@ public function filters(): array ]; } ``` + +## Filter Pills Separator + +As this filter returns one or more values, you have the option to utilise a custom separator for the values displayed in the Filter Pills section at the top of the table. The default is ", ", but you may use any HTML string to separate the selected values + +```php +public function filters(): array +{ + return [ + MultiSelectFilter::make('Tags') + ->options( + Tag::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn($tag) => $tag->name) + ->toArray() + ) + ->setPillsSeparator('
'), + ]; +} + +``` \ No newline at end of file diff --git a/docs/filter-types/filters-number-range.md b/docs/filter-types/filters-number-range.md index 0969e5c33..938900ef9 100644 --- a/docs/filter-types/filters-number-range.md +++ b/docs/filter-types/filters-number-range.md @@ -3,8 +3,6 @@ title: NumberRange Filters weight: 7 --- -## NumberRange Filters - NumberRange filters allow for a minimum and maximum value to be input on a single slider. ```php @@ -22,6 +20,7 @@ public function filters(): array 'minRange' => 0, 'maxRange' => 100, 'suffix' => '%', + 'prefix' => '$', ]) ->filter(function (Builder $builder, array $values) { $builder->where('users.success_rate', '>=', intval($values['min'])) @@ -34,4 +33,6 @@ public function filters(): array The default values should be set in the options() method. -You may also specify a minimum and maximum range in the config() options, and should you wish to use real values instead of a percentage, you can change the "suffix" to a metric of your choosing. +You may also specify a minimum and maximum range in the config() options, and should you wish to use real values instead of a percentage. +You can change the "suffix" to a metric of your choosing. +You can change the "prefix" to an item of your choosing (e.g $/£/€) diff --git a/docs/filter-types/filters-number.md b/docs/filter-types/filters-number.md index 3b629dd0a..e1423ae9c 100644 --- a/docs/filter-types/filters-number.md +++ b/docs/filter-types/filters-number.md @@ -3,8 +3,6 @@ title: Number Filters weight: 8 --- -## Number Filters - Number filters are just HTML number inputs. ```php diff --git a/docs/filter-types/filters-select.md b/docs/filter-types/filters-select.md index d28cfba94..568522f1b 100644 --- a/docs/filter-types/filters-select.md +++ b/docs/filter-types/filters-select.md @@ -3,8 +3,6 @@ title: Select Filters weight: 9 --- -## Select Filters - Select filters are a simple dropdown list. The user selects one choice from the list. ```php diff --git a/docs/filter-types/filters-text.md b/docs/filter-types/filters-text.md index 649384915..22255dfd4 100644 --- a/docs/filter-types/filters-text.md +++ b/docs/filter-types/filters-text.md @@ -3,8 +3,6 @@ title: Text Filters weight: 10 --- -## Text Filters - Text filters are just HTML text fields. ```php diff --git a/docs/filter-types/introduction.md b/docs/filter-types/introduction.md index d509cdc39..c553de5f9 100644 --- a/docs/filter-types/introduction.md +++ b/docs/filter-types/introduction.md @@ -4,3 +4,36 @@ weight: 1 --- There are several Filter types available for use, offering a range of capabilities to filter your data. + + \ No newline at end of file diff --git a/resources/css/laravel-livewire-tables.css b/resources/css/laravel-livewire-tables.css index 77c564573..2e8ffef5f 100644 --- a/resources/css/laravel-livewire-tables.css +++ b/resources/css/laravel-livewire-tables.css @@ -137,13 +137,11 @@ .range-slider::before { --before: 1; - --at-edge: var(--thumb-close-to-min); counter-reset: x var(--min); left: var(--offset); } .range-slider::after { - --at-edge: var(--thumb-close-to-max); counter-reset: x var(--max); right: var(--offset); } diff --git a/resources/css/laravel-livewire-tables.min.css b/resources/css/laravel-livewire-tables.min.css index bf77392cc..750fbd378 100644 --- a/resources/css/laravel-livewire-tables.min.css +++ b/resources/css/laravel-livewire-tables.min.css @@ -4,4 +4,4 @@ var(--value, 0), var(--max));--value-b:var(--value, 0);--text-value-a:var(--text-value, "");--completed-a:calc((var(--value-a) - var(--min)) / (var(--max) - var(--min)) * 100);--completed-b:calc((var(--value-b) - var(--min)) / (var(--max) - var(--min)) * 100);--ca:Min(var(--completed-a), var(--completed-b));--cb:Max(var(--completed-a), var(--completed-b));--thumbs-too-close:Clamp(-1, 1000 * (Min(1, Max(var(--cb) - var(--ca) - 5, -1)) + 0.001), 1);--thumb-close-to-min:Min(1, Max(var(--ca) - 2, - 0));--thumb-close-to-max:Min(1, Max(98 - var(--cb), 0));display:inline-block;height:max(var(--track-height),var(--thumb-size));background:linear-gradient(to right,var(--ticks-color,silver) var(--ticks-thickness),transparent 1px) repeat-x;background-size:var(--tickIntervalPerc) var(--ticks-height);background-position-x:calc(var(--thumb-size)/ 2 - var(--ticks-thickness)/ 2);background-position-y:var(--flip-y,bottom);padding-bottom:var(--flip-y,var(--ticks-gap));padding-top:calc(var(--flip-y) * var(--ticks-gap));position:relative;z-index:1}.range-slider::after,.range-slider::before{--offset:calc(var(--thumb-size) / 2);content:counter(x);display:var(--show-min-max,block);font:var(--min-max-font, 12px Arial);position:absolute;bottom:var(--flip-y,-2.5ch);top:calc(-2.5ch * var(--flip-y));opacity:clamp(0, var(--at-edge), var(--min-max-opacity));transform:translateX(calc(var(--min-max-x-offset) * var(--before,-1) * -1)) scale(var(--at-edge));pointer-events:none}.dark .range-slider::after,.dark .range-slider::before,.dark .range-slider>input+output::after,.dark .range-slider>input:first-of-type+output::after{color:#fff}.range-slider::before{--before:1;--at-edge:var(--thumb-close-to-min);counter-reset:x var(--min);left:var(--offset)}.range-slider::after{--at-edge:var(--thumb-close-to-max);counter-reset:x var(--max);right:var(--offset)}.range-slider__progress::after,.range-slider__progress::before{content:"";top:0;right:0;bottom:0;border-radius:inherit;left:0}.range-slider__values{position:relative;top:50%;line-height:0;text-align:justify;width:100%;pointer-events:none;margin:0 auto;z-index:5}.range-slider__values::after{content:"";width:100%;display:inline-block;height:0;background:red}.range-slider__progress{--start-end:calc(var(--thumb-size) / 2);--clip-end:calc(100% - (var(--cb)) * 1%);--clip-start:calc(var(--ca) * 1%);--clip:inset(-20px var(--clip-end) -20px var(--clip-start));position:absolute;left:var(--start-end);right:var(--start-end);top:calc(var(--ticks-gap) * var(--flip-y,0) + var(--thumb-size)/ 2 - var(--track-height)/ 2);height:calc(var(--track-height));background:var(--progress-background,#eee);pointer-events:none;z-index:-1;border-radius:var(--progress-radius)}.range-slider__progress::before{position:absolute;-webkit-clip-path:var(--clip);clip-path:var(--clip);background:var(--fill-color,#0366d6);box-shadow:var(--progress-flll-shadow);z-index:1}.range-slider__progress::after{position:absolute;box-shadow:var(--progress-shadow);pointer-events:none}.range-slider>input{-webkit-appearance:none;width:100%;height:var(--thumb-size);margin:0;position:absolute;left:0;top:calc(50% - Max(var(--track-height),var(--thumb-size))/ 2 + calc(var(--ticks-gap)/ 2 * var(--flip-y,-1)));cursor:-webkit-grab;cursor:grab;outline:0;background:0 0}.range-slider>input:not(:only-of-type){pointer-events:none}.range-slider>input::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-webkit-transition:.1s;transition:.1s}.range-slider>input::-moz-range-thumb{-moz-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-moz-transition:.1s;transition:.1s}.range-slider>input::-ms-thumb{appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-ms-transition:.1s;transition:.1s}.range-slider>input:hover{--thumb-shadow:var(--thumb-shadow-hover)}.range-slider>input:hover+output{--value-background:var(--value-background-hover, #0366d6);--y-offset:-5px;color:var(--value-active-color,#fff);box-shadow:0 0 0 3px var(--value-background)}.range-slider>input:active{--thumb-shadow:var(--thumb-shadow-active);cursor:-webkit-grabbing;cursor:grabbing;z-index:2}.range-slider>input:active+output{transition:none}.range-slider>input:first-of-type{--is-left-most:Clamp(0, (var(--value-a) - var(--value-b)) * 99999, 1)}.range-slider>input:first-of-type+output{--value:var(--value-a);--x-offset:calc(var(--completed-a) * -1%)}.range-slider>input:first-of-type+output:not(:only-of-type){--flip:calc(var(--thumbs-too-close) * -1)}.range-slider>input:first-of-type+output::after{content:var(--prefix, "") var(--text-value-a) var(--suffix, "")}.range-slider>input:nth-of-type(2){--is-left-most:Clamp(0, (var(--value-b) - var(--value-a)) * 99999, 1)}.range-slider>input:nth-of-type(2)+output{--value:var(--value-b)}.range-slider>input:only-of-type~.range-slider__progress{--clip-start:0}.range-slider>input+output{--flip:-1;--x-offset:calc(var(--completed-b) * -1%);--pos:calc(((var(--value) - var(--min)) / (var(--max) - var(--min))) * 100%);pointer-events:none;position:absolute;z-index:5;background:var(--value-background);border-radius:10px;padding:2px 4px;left:var(--pos);transform:translate(var(--x-offset),calc(150% * var(--flip) - (var(--y-offset,0px) + var(--value-offset-y)) * var(--flip)));transition:.12s ease-out,left}.range-slider>input+output::after{content:var(--prefix, "") var(--text-value-b) var(--suffix, "");font:var(--value-font)}body>.range-slider,label[dir=rtl] .range-slider{width:clamp(300px,50vw,800px);min-width:200px}.superhide{display:none}.lds-hourglass{display:inline-block;position:relative;width:80px;height:80px}.lds-hourglass:after{content:" ";display:block;border-radius:50%;width:0;height:0;margin:8px;box-sizing:border-box;border:32px solid #000;border-color:#000 transparent #fff;animation:1.2s infinite lds-hourglass}.dark .lds-hourglass:after{border:32px solid #fff;border-color:#fff transparent #000}@keyframes lds-hourglass{0%{transform:rotate(0);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}50%{transform:rotate(900deg);animation-timing-function:cubic-bezier(0.215,0.61,0.355,1)}100%{transform:rotate(1800deg)}} \ No newline at end of file + 0));--thumb-close-to-max:Min(1, Max(98 - var(--cb), 0));display:inline-block;height:max(var(--track-height),var(--thumb-size));background:linear-gradient(to right,var(--ticks-color,silver) var(--ticks-thickness),transparent 1px) repeat-x;background-size:var(--tickIntervalPerc) var(--ticks-height);background-position-x:calc(var(--thumb-size)/ 2 - var(--ticks-thickness)/ 2);background-position-y:var(--flip-y,bottom);padding-bottom:var(--flip-y,var(--ticks-gap));padding-top:calc(var(--flip-y) * var(--ticks-gap));position:relative;z-index:1}.range-slider::after,.range-slider::before{--offset:calc(var(--thumb-size) / 2);content:counter(x);display:var(--show-min-max,block);font:var(--min-max-font, 12px Arial);position:absolute;bottom:var(--flip-y,-2.5ch);top:calc(-2.5ch * var(--flip-y));opacity:clamp(0, var(--at-edge), var(--min-max-opacity));transform:translateX(calc(var(--min-max-x-offset) * var(--before,-1) * -1)) scale(var(--at-edge));pointer-events:none}.dark .range-slider::after,.dark .range-slider::before,.dark .range-slider>input+output::after,.dark .range-slider>input:first-of-type+output::after{color:#fff}.range-slider::before{--before:1;counter-reset:x var(--min);left:var(--offset)}.range-slider::after{counter-reset:x var(--max);right:var(--offset)}.range-slider__progress::after,.range-slider__progress::before{content:"";top:0;right:0;bottom:0;border-radius:inherit;left:0}.range-slider__values{position:relative;top:50%;line-height:0;text-align:justify;width:100%;pointer-events:none;margin:0 auto;z-index:5}.range-slider__values::after{content:"";width:100%;display:inline-block;height:0;background:red}.range-slider__progress{--start-end:calc(var(--thumb-size) / 2);--clip-end:calc(100% - (var(--cb)) * 1%);--clip-start:calc(var(--ca) * 1%);--clip:inset(-20px var(--clip-end) -20px var(--clip-start));position:absolute;left:var(--start-end);right:var(--start-end);top:calc(var(--ticks-gap) * var(--flip-y,0) + var(--thumb-size)/ 2 - var(--track-height)/ 2);height:calc(var(--track-height));background:var(--progress-background,#eee);pointer-events:none;z-index:-1;border-radius:var(--progress-radius)}.range-slider__progress::before{position:absolute;-webkit-clip-path:var(--clip);clip-path:var(--clip);background:var(--fill-color,#0366d6);box-shadow:var(--progress-flll-shadow);z-index:1}.range-slider__progress::after{position:absolute;box-shadow:var(--progress-shadow);pointer-events:none}.range-slider>input{-webkit-appearance:none;width:100%;height:var(--thumb-size);margin:0;position:absolute;left:0;top:calc(50% - Max(var(--track-height),var(--thumb-size))/ 2 + calc(var(--ticks-gap)/ 2 * var(--flip-y,-1)));cursor:-webkit-grab;cursor:grab;outline:0;background:0 0}.range-slider>input:not(:only-of-type){pointer-events:none}.range-slider>input::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-webkit-transition:.1s;transition:.1s}.range-slider>input::-moz-range-thumb{-moz-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-moz-transition:.1s;transition:.1s}.range-slider>input::-ms-thumb{appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-ms-transition:.1s;transition:.1s}.range-slider>input:hover{--thumb-shadow:var(--thumb-shadow-hover)}.range-slider>input:hover+output{--value-background:var(--value-background-hover, #0366d6);--y-offset:-5px;color:var(--value-active-color,#fff);box-shadow:0 0 0 3px var(--value-background)}.range-slider>input:active{--thumb-shadow:var(--thumb-shadow-active);cursor:-webkit-grabbing;cursor:grabbing;z-index:2}.range-slider>input:active+output{transition:none}.range-slider>input:first-of-type{--is-left-most:Clamp(0, (var(--value-a) - var(--value-b)) * 99999, 1)}.range-slider>input:first-of-type+output{--value:var(--value-a);--x-offset:calc(var(--completed-a) * -1%)}.range-slider>input:first-of-type+output:not(:only-of-type){--flip:calc(var(--thumbs-too-close) * -1)}.range-slider>input:first-of-type+output::after{content:var(--prefix, "") var(--text-value-a) var(--suffix, "")}.range-slider>input:nth-of-type(2){--is-left-most:Clamp(0, (var(--value-b) - var(--value-a)) * 99999, 1)}.range-slider>input:nth-of-type(2)+output{--value:var(--value-b)}.range-slider>input:only-of-type~.range-slider__progress{--clip-start:0}.range-slider>input+output{--flip:-1;--x-offset:calc(var(--completed-b) * -1%);--pos:calc(((var(--value) - var(--min)) / (var(--max) - var(--min))) * 100%);pointer-events:none;position:absolute;z-index:5;background:var(--value-background);border-radius:10px;padding:2px 4px;left:var(--pos);transform:translate(var(--x-offset),calc(150% * var(--flip) - (var(--y-offset,0px) + var(--value-offset-y)) * var(--flip)));transition:.12s ease-out,left}.range-slider>input+output::after{content:var(--prefix, "") var(--text-value-b) var(--suffix, "");font:var(--value-font)}body>.range-slider,label[dir=rtl] .range-slider{width:clamp(300px,50vw,800px);min-width:200px}.superhide{display:none}.lds-hourglass{display:inline-block;position:relative;width:80px;height:80px}.lds-hourglass:after{content:" ";display:block;border-radius:50%;width:0;height:0;margin:8px;box-sizing:border-box;border:32px solid #000;border-color:#fff transparent;animation:1.2s infinite lds-hourglass}.dark .lds-hourglass:after{border:32px solid #fff}@keyframes lds-hourglass{0%{transform:rotate(0);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}50%{transform:rotate(900deg);animation-timing-function:cubic-bezier(0.215,0.61,0.355,1)}100%{transform:rotate(1800deg)}} \ No newline at end of file diff --git a/resources/js/laravel-livewire-tables.js b/resources/js/laravel-livewire-tables.js index 0d068dac1..c9afc8331 100644 --- a/resources/js/laravel-livewire-tables.js +++ b/resources/js/laravel-livewire-tables.js @@ -60,14 +60,20 @@ document.addEventListener('alpine:init', () => { defaultMin: filterConfig['minRange'], defaultMax: filterConfig['maxRange'], restrictUpdates: false, - updateStyles() { + initialiseStyles() + { let numRangeFilterContainer = document.getElementById(parentElementPath); - let currentFilterMin = document.getElementById(childElementRoot + "-min"); - let currentFilterMax = document.getElementById(childElementRoot + "-max"); - numRangeFilterContainer.style.setProperty('--value-a', currentFilterMin.value); - numRangeFilterContainer.style.setProperty('--text-value-a', JSON.stringify(currentFilterMin.value)); - numRangeFilterContainer.style.setProperty('--value-b', currentFilterMax.value); - numRangeFilterContainer.style.setProperty('--text-value-b', JSON.stringify(currentFilterMax.value)); + numRangeFilterContainer.style.setProperty('--value-a', this.wireValues['min'] ?? this.filterMin); + numRangeFilterContainer.style.setProperty('--text-value-a', JSON.stringify(this.wireValues['min'] ?? this.filterMin)); + numRangeFilterContainer.style.setProperty('--value-b', this.wireValues['max'] ?? this.filterMax); + numRangeFilterContainer.style.setProperty('--text-value-b', JSON.stringify(this.wireValues['max'] ?? this.filterMax)); + }, + updateStyles(filterMin, filterMax) { + let numRangeFilterContainer = document.getElementById(parentElementPath); + numRangeFilterContainer.style.setProperty('--value-a', filterMin); + numRangeFilterContainer.style.setProperty('--text-value-a', JSON.stringify(filterMin)); + numRangeFilterContainer.style.setProperty('--value-b', filterMax); + numRangeFilterContainer.style.setProperty('--text-value-b', JSON.stringify(filterMax)); }, setupWire() { if (this.wireValues !== undefined) { @@ -77,7 +83,7 @@ document.addEventListener('alpine:init', () => { this.filterMin = this.originalMin = this.defaultMin; this.filterMax = this.originalMax = this.defaultMax; } - this.updateStyles(); + this.updateStyles(this.filterMin, this.filterMax); }, allowUpdates() { this.updateWire(); @@ -95,7 +101,7 @@ document.addEventListener('alpine:init', () => { this.originalMin = tmpFilterMin; this.originalMax = tmpFilterMax; } - this.updateStyles(); + this.updateStyles(this.filterMin,this.filterMax); }, updateWireable() { if (this.hasUpdate) { @@ -103,9 +109,9 @@ document.addEventListener('alpine:init', () => { this.wireValues = { 'min': this.filterMin, 'max': this.filterMax }; wire.set('filterComponents.' + filterKey, this.wireValues); } - }, init() { + this.initialiseStyles(); this.setupWire(); this.$watch('allFilters', value => this.setupWire()); }, diff --git a/resources/js/laravel-livewire-tables.min.js b/resources/js/laravel-livewire-tables.min.js index 3b5991c4a..740ebca80 100644 --- a/resources/js/laravel-livewire-tables.min.js +++ b/resources/js/laravel-livewire-tables.min.js @@ -1 +1 @@ -document.addEventListener("alpine:init",()=>{Alpine.data("tableWrapper",(e,t)=>({childElementOpen:!1,filtersOpen:e.entangle("filterSlideDownDefaultVisible"),paginationCurrentCount:e.entangle("paginationCurrentCount"),paginationTotalItemCount:e.entangle("paginationTotalItemCount"),paginationCurrentItems:e.entangle("paginationCurrentItems"),selectedItems:e.entangle("selected"),hideBulkActionsWhenEmpty:e.entangle("hideBulkActionsWhenEmpty"),toggleSelectAll(){t&&(this.paginationTotalItemCount===this.selectedItems.length?this.clearSelected():this.setAllSelected())},setAllSelected(){t&&e.setAllSelected()},clearSelected(){t&&e.clearSelected()},selectAllOnPage(){if(!t)return;let e=this.selectedItems,i=this.paginationCurrentItems.values();for(let l of i)e.push(l.toString());this.selectedItems=[...new Set(e)]}})),Alpine.data("numberRangeFilter",(e,t,i,l,a)=>({allFilters:e.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!1),defaultMin:l.minRange,defaultMax:l.maxRange,restrictUpdates:!1,updateStyles(){let e=document.getElementById(i),t=document.getElementById(a+"-min"),l=document.getElementById(a+"-max");e.style.setProperty("--value-a",t.value),e.style.setProperty("--text-value-a",JSON.stringify(t.value)),e.style.setProperty("--value-b",l.value),e.style.setProperty("--text-value-b",JSON.stringify(l.value))},setupWire(){void 0!==this.wireValues?(this.filterMin=this.originalMin=void 0!==this.wireValues.min?this.wireValues.min:this.defaultMin,this.filterMax=this.originalMax=void 0!==this.wireValues.max?this.wireValues.max:this.defaultMax):(this.filterMin=this.originalMin=this.defaultMin,this.filterMax=this.originalMax=this.defaultMax),this.updateStyles()},allowUpdates(){this.updateWire()},updateWire(){let e=parseInt(this.filterMin),t=parseInt(this.filterMax);(e!=this.originalMin||t!=this.originalMax)&&(tthis.setupWire())}})),Alpine.data("flatpickrFilter",(e,t,i,l,a)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(l,{mode:"range",clickOpens:!0,allowInvalidPreload:!0,defaultDate:[],ariaDateFormat:i.ariaDateFormat,allowInput:i.allowInput,altFormat:i.altFormat,altInput:i.altInput,dateFormat:i.dateFormat,locale:"en",minDate:i.earliestDate,maxDate:i.latestDate,onOpen:function(){window.childElementOpen=!0},onChange:function(i,l,a){if(i.length>1){var s=l.split(" ")[0],r=l.split(" ")[2],n={};window.childElementOpen=!1,window.filterPopoverOpen=!1,n={minDate:s,maxDate:r},e.set("filterComponents."+t,n)}}}),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())}})),Alpine.data("reorderFunction",(e,t,i)=>({dragging:!1,reorderEnabled:!1,sourceID:"",targetID:"",evenRowClasses:"",oddRowClasses:"",currentlyHighlightedElement:"",evenRowClassArray:{},oddRowClassArray:{},evenNotInOdd:{},oddNotInEven:{},orderedRows:[],defaultReorderColumn:e.get("defaultReorderColumn"),reorderStatus:e.get("reorderStatus"),currentlyReorderingStatus:e.entangle("currentlyReorderingStatus"),hideReorderColumnUnlessReorderingStatus:e.entangle("hideReorderColumnUnlessReorderingStatus"),reorderDisplayColumn:e.entangle("reorderDisplayColumn"),dragStart(e){this.sourceID=e.target.id,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",e.target.id),e.target.classList.add("laravel-livewire-tables-dragging")},dragOverEvent(e){"object"==typeof this.currentlyHighlightedElement&&this.currentlyHighlightedElement.classList.remove("laravel-livewire-tables-highlight-bottom","laravel-livewire-tables-highlight-top");let t=e.target.closest("tr");this.currentlyHighlightedElement=t,e.offsetYi.getBoundingClientRect().height/2?l.insertBefore(a,i.nextSibling):l.insertBefore(a,i),r!l.includes(e)),this.oddNotInEven=l.filter(e=>!i.includes(e)),i=[],l=[])}},init(){this.$watch("currentlyReorderingStatus",e=>this.setupEvenOddClasses())}}))}); \ No newline at end of file +document.addEventListener("alpine:init",()=>{Alpine.data("tableWrapper",(e,t)=>({childElementOpen:!1,filtersOpen:e.entangle("filterSlideDownDefaultVisible"),paginationCurrentCount:e.entangle("paginationCurrentCount"),paginationTotalItemCount:e.entangle("paginationTotalItemCount"),paginationCurrentItems:e.entangle("paginationCurrentItems"),selectedItems:e.entangle("selected"),hideBulkActionsWhenEmpty:e.entangle("hideBulkActionsWhenEmpty"),toggleSelectAll(){t&&(this.paginationTotalItemCount===this.selectedItems.length?this.clearSelected():this.setAllSelected())},setAllSelected(){t&&e.setAllSelected()},clearSelected(){t&&e.clearSelected()},selectAllOnPage(){if(!t)return;let e=this.selectedItems,i=this.paginationCurrentItems.values();for(let l of i)e.push(l.toString());this.selectedItems=[...new Set(e)]}})),Alpine.data("numberRangeFilter",(e,t,i,l,s)=>({allFilters:e.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!1),defaultMin:l.minRange,defaultMax:l.maxRange,restrictUpdates:!1,initialiseStyles(){let e=document.getElementById(i);e.style.setProperty("--value-a",this.wireValues.min??this.filterMin),e.style.setProperty("--text-value-a",JSON.stringify(this.wireValues.min??this.filterMin)),e.style.setProperty("--value-b",this.wireValues.max??this.filterMax),e.style.setProperty("--text-value-b",JSON.stringify(this.wireValues.max??this.filterMax))},updateStyles(e,t){let l=document.getElementById(i);l.style.setProperty("--value-a",e),l.style.setProperty("--text-value-a",JSON.stringify(e)),l.style.setProperty("--value-b",t),l.style.setProperty("--text-value-b",JSON.stringify(t))},setupWire(){void 0!==this.wireValues?(this.filterMin=this.originalMin=void 0!==this.wireValues.min?this.wireValues.min:this.defaultMin,this.filterMax=this.originalMax=void 0!==this.wireValues.max?this.wireValues.max:this.defaultMax):(this.filterMin=this.originalMin=this.defaultMin,this.filterMax=this.originalMax=this.defaultMax),this.updateStyles(this.filterMin,this.filterMax)},allowUpdates(){this.updateWire()},updateWire(){let e=parseInt(this.filterMin),t=parseInt(this.filterMax);(e!=this.originalMin||t!=this.originalMax)&&(tthis.setupWire())}})),Alpine.data("flatpickrFilter",(e,t,i,l,s)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(l,{mode:"range",clickOpens:!0,allowInvalidPreload:!0,defaultDate:[],ariaDateFormat:i.ariaDateFormat,allowInput:i.allowInput,altFormat:i.altFormat,altInput:i.altInput,dateFormat:i.dateFormat,locale:"en",minDate:i.earliestDate,maxDate:i.latestDate,onOpen:function(){window.childElementOpen=!0},onChange:function(i,l,s){if(i.length>1){var a=l.split(" ")[0],r=l.split(" ")[2],n={};window.childElementOpen=!1,window.filterPopoverOpen=!1,n={minDate:a,maxDate:r},e.set("filterComponents."+t,n)}}}),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())}})),Alpine.data("reorderFunction",(e,t,i)=>({dragging:!1,reorderEnabled:!1,sourceID:"",targetID:"",evenRowClasses:"",oddRowClasses:"",currentlyHighlightedElement:"",evenRowClassArray:{},oddRowClassArray:{},evenNotInOdd:{},oddNotInEven:{},orderedRows:[],defaultReorderColumn:e.get("defaultReorderColumn"),reorderStatus:e.get("reorderStatus"),currentlyReorderingStatus:e.entangle("currentlyReorderingStatus"),hideReorderColumnUnlessReorderingStatus:e.entangle("hideReorderColumnUnlessReorderingStatus"),reorderDisplayColumn:e.entangle("reorderDisplayColumn"),dragStart(e){this.sourceID=e.target.id,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",e.target.id),e.target.classList.add("laravel-livewire-tables-dragging")},dragOverEvent(e){"object"==typeof this.currentlyHighlightedElement&&this.currentlyHighlightedElement.classList.remove("laravel-livewire-tables-highlight-bottom","laravel-livewire-tables-highlight-top");let t=e.target.closest("tr");this.currentlyHighlightedElement=t,e.offsetYi.getBoundingClientRect().height/2?l.insertBefore(s,i.nextSibling):l.insertBefore(s,i),r!l.includes(e)),this.oddNotInEven=l.filter(e=>!i.includes(e)),i=[],l=[])}},init(){this.$watch("currentlyReorderingStatus",e=>this.setupEvenOddClasses())}}))}); \ No newline at end of file diff --git a/resources/views/components/table/th.blade.php b/resources/views/components/table/th.blade.php index ee6333ee6..184f71cd0 100644 --- a/resources/views/components/table/th.blade.php +++ b/resources/views/components/table/th.blade.php @@ -70,11 +70,11 @@ class="d-flex align-items-center" @if ($direction === 'asc') - + @elseif ($direction === 'desc') - + @else - + @endif diff --git a/resources/views/components/tools/filter-pills.blade.php b/resources/views/components/tools/filter-pills.blade.php index afb9568a7..5079909d7 100644 --- a/resources/views/components/tools/filter-pills.blade.php +++ b/resources/views/components/tools/filter-pills.blade.php @@ -30,7 +30,17 @@ 'badge rounded-pill bg-info d-inline-flex align-items-center' => $component->isBootstrap5(), ]) > - {{ $filter->getFilterPillTitle() }}: {{ $filter->getFilterPillValue($value) }} + {{ $filter->getFilterPillTitle() }}: + @php( $filterPillValue = $filter->getFilterPillValue($value)) + @php( $separator = method_exists($filter, 'getPillsSeparator') ? $filter->getPillsSeparator() : ', ') + + @if(is_array($filterPillValue) && !empty($filterPillValue)) + @foreach($filterPillValue as $filterPillArrayValue) + {{ $filterPillArrayValue }}{!! $separator !!} + @endforeach + @else + {{ $filterPillValue }} + @endif @if ($component->isTailwind()) @@ -68,10 +90,14 @@ class="block w-full px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 @else
$component->isBootstrap4(), - 'dropdown-menu dropdown-menu-end w-100' => $component->isBootstrap5(), - ]) + {{ + $attributes->merge($this->getBulkActionsMenuAttributes) + ->class([ + 'dropdown-menu dropdown-menu-right w-100' => $component->isBootstrap4() && $this->getBulkActionsMenuAttributes['default-styling'] ?? true, + 'dropdown-menu dropdown-menu-end w-100' => $component->isBootstrap5() && $this->getBulkActionsMenuAttributes['default-styling'] ?? true, + ]) + ->except('default') + }} aria-labelledby="{{ $tableName }}-bulkActionsDropdown" > @foreach ($component->getBulkActions() as $action => $title) @@ -82,9 +108,13 @@ class="block w-full px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 @endif wire:click="{{ $action }}" wire:key="{{ $tableName }}-bulk-action-{{ $action }}" - @class([ - 'dropdown-item' => $component->isBootstrap(), - ]) + {{ + $attributes->merge($this->getBulkActionsMenuItemAttributes) + ->class([ + 'dropdown-item' => $component->isBootstrap() && $this->getBulkActionsMenuItemAttributes['default-styling'] ?? true, + ]) + ->except('default') + }} > {{ $title }} diff --git a/src/Traits/Configuration/BulkActionsConfiguration.php b/src/Traits/Configuration/BulkActionsConfiguration.php index 6af318f0e..f46cb712e 100644 --- a/src/Traits/Configuration/BulkActionsConfiguration.php +++ b/src/Traits/Configuration/BulkActionsConfiguration.php @@ -213,4 +213,25 @@ public function setClearSelectedOnFilterDisabled(): self return $this; } + + public function setBulkActionsButtonAttributes(array $bulkActionsButtonAttributes): self + { + $this->bulkActionsButtonAttributes = [...$this->bulkActionsButtonAttributes, ...$bulkActionsButtonAttributes]; + + return $this; + } + + public function setBulkActionsMenuAttributes(array $bulkActionsMenuAttributes): self + { + $this->bulkActionsMenuAttributes = [...$this->bulkActionsMenuAttributes, ...$bulkActionsMenuAttributes]; + + return $this; + } + + public function setBulkActionsMenuItemAttributes(array $bulkActionsMenuItemAttributes): self + { + $this->bulkActionsMenuItemAttributes = [...$this->bulkActionsMenuItemAttributes, ...$bulkActionsMenuItemAttributes]; + + return $this; + } } diff --git a/src/Traits/Helpers/BulkActionsHelpers.php b/src/Traits/Helpers/BulkActionsHelpers.php index 77a263635..542e31bb4 100644 --- a/src/Traits/Helpers/BulkActionsHelpers.php +++ b/src/Traits/Helpers/BulkActionsHelpers.php @@ -230,4 +230,23 @@ public function getClearSelectedOnFilter(): bool { return $this->clearSelectedOnFilter ?? true; } + + #[Computed] + public function getBulkActionsButtonAttributes(): array + { + return array_merge(['default-colors' => true, 'default-styling' => true], $this->bulkActionsButtonAttributes); + + } + + #[Computed] + public function getBulkActionsMenuAttributes(): array + { + return array_merge(['default-colors' => true, 'default-styling' => true], $this->bulkActionsMenuAttributes); + } + + #[Computed] + public function getBulkActionsMenuItemAttributes(): array + { + return array_merge(['default-colors' => true, 'default-styling' => true], $this->bulkActionsMenuItemAttributes); + } } diff --git a/src/Traits/WithBulkActions.php b/src/Traits/WithBulkActions.php index 37a5cd111..13a516974 100644 --- a/src/Traits/WithBulkActions.php +++ b/src/Traits/WithBulkActions.php @@ -40,6 +40,12 @@ trait WithBulkActions public bool $clearSelectedOnFilter = true; + protected array $bulkActionsButtonAttributes = ['default-colors' => true, 'default-styling' => true]; + + protected array $bulkActionsMenuAttributes = ['default-colors' => true, 'default-styling' => true]; + + protected array $bulkActionsMenuItemAttributes = ['default-colors' => true, 'default-styling' => true]; + public function bulkActions(): array { return property_exists($this, 'bulkActions') ? $this->bulkActions : []; diff --git a/src/Views/Filters/DateFilter.php b/src/Views/Filters/DateFilter.php index 99d6a7ba0..6679a86aa 100644 --- a/src/Views/Filters/DateFilter.php +++ b/src/Views/Filters/DateFilter.php @@ -28,7 +28,7 @@ public function validate(string $value): string|bool return $value; } - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { if ($this->validate($value)) { return DateTime::createFromFormat('Y-m-d', $value)->format($this->getConfig('pillFormat')); diff --git a/src/Views/Filters/DateRangeFilter.php b/src/Views/Filters/DateRangeFilter.php index e08c4bfe5..9e95265b1 100644 --- a/src/Views/Filters/DateRangeFilter.php +++ b/src/Views/Filters/DateRangeFilter.php @@ -108,7 +108,7 @@ public function getDefaultValue(): array return []; } - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { $validatedValue = $this->validate($value); diff --git a/src/Views/Filters/DateTimeFilter.php b/src/Views/Filters/DateTimeFilter.php index 1d266549e..f46b77f20 100644 --- a/src/Views/Filters/DateTimeFilter.php +++ b/src/Views/Filters/DateTimeFilter.php @@ -28,7 +28,7 @@ public function validate(string $value): string|bool return $value; } - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { if ($this->validate($value)) { return DateTime::createFromFormat('Y-m-d\TH:i', $value)->format($this->getConfig('pillFormat')); diff --git a/src/Views/Filters/MultiSelectDropdownFilter.php b/src/Views/Filters/MultiSelectDropdownFilter.php index 3022ddba5..027767735 100644 --- a/src/Views/Filters/MultiSelectDropdownFilter.php +++ b/src/Views/Filters/MultiSelectDropdownFilter.php @@ -37,7 +37,7 @@ public function validate(int|string|array $value): array|int|string|bool return (is_string($value) || is_numeric($value)) ? $value : false; } - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { $values = []; @@ -52,7 +52,7 @@ public function getFilterPillValue($value): ?string } } - return implode(', ', $values); + return $values; } public function isEmpty(mixed $value): bool diff --git a/src/Views/Filters/MultiSelectFilter.php b/src/Views/Filters/MultiSelectFilter.php index f43b37302..614f615c7 100644 --- a/src/Views/Filters/MultiSelectFilter.php +++ b/src/Views/Filters/MultiSelectFilter.php @@ -34,7 +34,7 @@ public function validate(int|string|array $value): array|int|string|bool return $value; } - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { $values = []; @@ -46,6 +46,6 @@ public function getFilterPillValue($value): ?string } } - return implode(', ', $values); + return $values; } } diff --git a/src/Views/Filters/NumberRangeFilter.php b/src/Views/Filters/NumberRangeFilter.php index a95be42c3..ed7ab5911 100644 --- a/src/Views/Filters/NumberRangeFilter.php +++ b/src/Views/Filters/NumberRangeFilter.php @@ -86,7 +86,7 @@ public function getDefaultValue(): array|string return []; } - public function getFilterPillValue($values): ?string + public function getFilterPillValue($values): string|array|null { if ($this->validate($values)) { return __('Min:').$values['min'].', '.__('Max:').$values['max']; diff --git a/src/Views/Filters/SelectFilter.php b/src/Views/Filters/SelectFilter.php index 0f247ada4..9940d7150 100644 --- a/src/Views/Filters/SelectFilter.php +++ b/src/Views/Filters/SelectFilter.php @@ -41,7 +41,7 @@ public function validate(string $value): array|string|bool return $value; } - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { return $this->getCustomFilterPillValue($value) diff --git a/src/Views/Traits/Configuration/AggregateColumnConfiguration.php b/src/Views/Traits/Configuration/AggregateColumnConfiguration.php index 2f181ed2f..9e12611dd 100644 --- a/src/Views/Traits/Configuration/AggregateColumnConfiguration.php +++ b/src/Views/Traits/Configuration/AggregateColumnConfiguration.php @@ -3,6 +3,7 @@ namespace Rappasoft\LaravelLivewireTables\Views\Traits\Configuration; use Illuminate\Database\Eloquent\{Builder, Model}; +use Illuminate\Support\Str; use Rappasoft\LaravelLivewireTables\Views\Column; trait AggregateColumnConfiguration @@ -40,10 +41,10 @@ public function setDefaultLabel(): void { $this->label(function ($row, Column $column) { if ($this->hasForeignColumn()) { - return $row->{$this->getDataSource().'_'.$this->getAggregateMethod().'_'.$this->getForeignColumn()}; + return $row->{Str::snake($this->getDataSource()).'_'.$this->getAggregateMethod().'_'.$this->getForeignColumn()}; } - return $row->{$this->getDataSource().'_'.$this->getAggregateMethod()}; + return $row->{Str::snake($this->getDataSource()).'_'.$this->getAggregateMethod()}; }); } diff --git a/src/Views/Traits/Filters/IsArrayFilter.php b/src/Views/Traits/Filters/IsArrayFilter.php index 5812fe35c..5238d71d3 100644 --- a/src/Views/Traits/Filters/IsArrayFilter.php +++ b/src/Views/Traits/Filters/IsArrayFilter.php @@ -8,6 +8,8 @@ trait IsArrayFilter { + public string $pillsSeparator = ', '; + /** * Get the filter default options. */ @@ -32,4 +34,16 @@ public function isEmpty(mixed $value): bool return empty($value); } + + public function getPillsSeparator(): string + { + return $this->pillsSeparator ?? ', '; + } + + public function setPillsSeparator(string $pillsSeparator): self + { + $this->pillsSeparator = $pillsSeparator; + + return $this; + } } diff --git a/src/Views/Traits/Helpers/FilterHelpers.php b/src/Views/Traits/Helpers/FilterHelpers.php index 5e4f1ae7f..672353184 100644 --- a/src/Views/Traits/Helpers/FilterHelpers.php +++ b/src/Views/Traits/Helpers/FilterHelpers.php @@ -70,7 +70,7 @@ public function getFilterPillTitle(): string /** * @param mixed $value */ - public function getFilterPillValue($value): ?string + public function getFilterPillValue($value): string|array|null { return $value; } diff --git a/tests/Http/Livewire/PetsTableCustomFilters.php b/tests/Http/Livewire/PetsTableCustomFilters.php new file mode 100644 index 000000000..ef717c99d --- /dev/null +++ b/tests/Http/Livewire/PetsTableCustomFilters.php @@ -0,0 +1,138 @@ +setPerPageAccepted([1, 3, 5, 10, 15, 25, 50])->setPerPage(3); + $this->setPaginationMethod($type); + $this->setDisplayPaginationDetailsEnabled(); + + } + + public function disableDetailedPagination(string $type = 'standard') + { + $this->setPerPageAccepted([1, 3, 5, 10, 15, 25, 50])->setPerPage(3); + $this->setPaginationMethod($type); + $this->setDisplayPaginationDetailsDisabled(); + } + + public function setPaginationTest(string $type) + { + $this->paginationTest = $type; + } + + public function configure(): void + { + $this->setPrimaryKey('id'); + } + + public function columns(): array + { + return [ + Column::make('ID', 'id') + ->sortable() + ->setSortingPillTitle('Key') + ->setSortingPillDirections('0-9', '9-0'), + Column::make('Sort') + ->sortable() + ->excludeFromColumnSelect(), + Column::make('Name') + ->sortable() + ->secondaryHeader($this->getFilterByKey('pet_name_filter')) + ->footerFilter('pet_name_filter') + ->searchable(), + + Column::make('Age'), + + Column::make('Breed', 'breed.name') + ->secondaryHeaderFilter('breed') + ->footer($this->getFilterByKey('breed')) + ->sortable( + fn (Builder $query, string $direction) => $query->orderBy('pets.id', $direction) + ) + ->searchable( + fn (Builder $query, $searchTerm) => $query->orWhere('breed.name', $searchTerm) + ), + + Column::make('Other') + ->label(function ($row, Column $column) { + return 'Other'; + }) + ->footer(function ($rows) { + return 'Count: '.$rows->count(); + }), + + LinkColumn::make('Link') + ->title(fn ($row) => 'Edit') + ->location(fn ($row) => 'http://www.google.com') + ->attributes(fn ($row) => [ + 'class' => 'rounded-full', + 'alt' => $row->name.' Avatar', + ]), + ImageColumn::make('RowImg') + ->location(fn ($row) => 'test'.$row->id) + ->attributes(fn ($row) => [ + 'class' => 'rounded-full', + 'alt' => $row->name.' Avatar', + ]), + Column::make('Last Visit', 'last_visit') + ->sortable() + ->deselected(), + ]; + } + + public function filters(): array + { + return [ + MultiSelectFilter::make('Breed') + ->options( + Breed::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn ($breed) => $breed->name) + ->toArray() + ) + ->filter(function (Builder $builder, array $values) { + return $builder->whereIn('breed_id', $values); + }), + MultiSelectDropdownFilter::make('Species') + ->options( + Species::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn ($species) => $species->name) + ->toArray() + ) + ->filter(function (Builder $builder, array $values) { + return $builder->whereIn('species_id', $values); + }) + ->setPillsSeparator('
'), + + ]; + } +} diff --git a/tests/Traits/Configuration/BulkActionsConfigurationTest.php b/tests/Traits/Configuration/BulkActionsConfigurationTest.php index b2f24a499..7cf864bf8 100644 --- a/tests/Traits/Configuration/BulkActionsConfigurationTest.php +++ b/tests/Traits/Configuration/BulkActionsConfigurationTest.php @@ -193,4 +193,53 @@ public function test_can_set_bulk_actions_th_checkbox_attributes(): void $this->assertSame(['default' => false, 'class' => 'bg-green-500'], $this->basicTable->getBulkActionsThCheckboxAttributes()); } + + public function test_can_set_bulk_actions_button_attributes(): void + { + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + $this->basicTable->setBulkActionsButtonAttributes(['default-colors' => false, 'class' => 'bg-green-500']); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-green-500'], $this->basicTable->getBulkActionsButtonAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + $this->basicTable->setBulkActionsButtonAttributes(['class' => 'bg-green-500', 'default-colors' => false]); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-green-500'], $this->basicTable->getBulkActionsButtonAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + } + + public function test_can_set_bulk_actions_menu_attributes(): void + { + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + + $this->basicTable->setBulkActionsMenuAttributes(['class' => 'bg-blue-500']); + + $this->assertSame(['default-colors' => true, 'default-styling' => true, 'class' => 'bg-blue-500'], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + + $this->basicTable->setBulkActionsMenuAttributes(['class' => 'bg-blue-500', 'default-colors' => false]); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-blue-500'], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsButtonAttributes()); + } + + public function test_can_set_bulk_actions_menu_item_attributes(): void + { + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuItemAttributes()); + + $this->basicTable->setBulkActionsMenuItemAttributes(['class' => 'bg-red-500']); + + $this->assertSame(['default-colors' => true, 'default-styling' => true, 'class' => 'bg-red-500'], $this->basicTable->getBulkActionsMenuItemAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + $this->basicTable->setBulkActionsMenuItemAttributes(['class' => 'bg-amber-500', 'default-colors' => false]); + + $this->assertSame(['default-colors' => false, 'default-styling' => true, 'class' => 'bg-amber-500'], $this->basicTable->getBulkActionsMenuItemAttributes()); + $this->assertSame(['default-colors' => true, 'default-styling' => true], $this->basicTable->getBulkActionsMenuAttributes()); + + } } diff --git a/tests/Traits/Visuals/FilterVisualsTest.php b/tests/Traits/Visuals/FilterVisualsTest.php index d73c317ad..25a877c8b 100644 --- a/tests/Traits/Visuals/FilterVisualsTest.php +++ b/tests/Traits/Visuals/FilterVisualsTest.php @@ -87,6 +87,63 @@ public function test_filters_with_invalid_key_dont_error(): void ->assertDontSee('Applied Filters'); } + public function test_filters_pills_separator_is_customisable(): void + { + Livewire::test(new class extends PetsTable + { + public function configure(): void + { + $this->setPrimaryKey('id'); + } + + public function filters(): array + { + return [ + \Rappasoft\LaravelLivewireTables\Views\Filters\MultiSelectFilter::make('Breed') + ->options( + \Rappasoft\LaravelLivewireTables\Tests\Models\Breed::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn ($breed) => $breed->name) + ->toArray() + ) + ->filter(function (\Illuminate\Database\Eloquent\Builder $builder, array $values) { + return $builder->whereIn('pets.breed_id', $values); + }), + \Rappasoft\LaravelLivewireTables\Views\Filters\MultiSelectDropdownFilter::make('Species') + ->options( + \Rappasoft\LaravelLivewireTables\Tests\Models\Species::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn ($species) => $species->name) + ->toArray() + ) + ->filter(function (\Illuminate\Database\Eloquent\Builder $builder, array $values) { + return $builder->whereIn('pets.species_id', $values); + }) + ->setPillsSeparator('
'), + + ]; + } + }) + ->set('filterComponents.species', [1, 2]) + ->assertSeeHtmlInOrder([ + 'wire:key="table-filter-pill-species"', + 'Cat', + '
', + 'Dog', + '
', + ]) + ->set('filterComponents.breed', [1, 2]) + ->assertSeeHtmlInOrder([ + 'wire:key="table-filter-pill-breed"', + 'American Shorthair,', + 'Maine Coon', + ]); + } + /*public function test_filter_events_apply_correctly(): void { Livewire::test(PetsTable::class) diff --git a/tests/Views/Filters/DateRangeFilterTest.php b/tests/Views/Filters/DateRangeFilterTest.php index 312579f82..bef64b240 100644 --- a/tests/Views/Filters/DateRangeFilterTest.php +++ b/tests/Views/Filters/DateRangeFilterTest.php @@ -32,6 +32,7 @@ public function test_can_get_filter_configs(): void 'dateFormat' => 'Y-m-d', 'earliestDate' => null, 'latestDate' => null, + 'locale' => 'en', ]; $this->assertSame($defaultConfig, $filter->getConfigs()); @@ -52,6 +53,7 @@ public function test_get_a_single_filter_config(): void 'dateFormat' => 'Y-m-d', 'earliestDate' => null, 'latestDate' => null, + 'locale' => 'en', ], $filter->getConfigs()); $filter->config(['foo' => 'bar']); @@ -64,10 +66,40 @@ public function test_get_a_single_filter_config(): void 'dateFormat' => 'Y-m-d', 'earliestDate' => null, 'latestDate' => null, + 'locale' => 'en', 'foo' => 'bar'], $filter->getConfigs()); } + public function test_can_change_locale(): void + { + $filter = DateRangeFilter::make('Active'); + + $this->assertSame([ + 'allowInput' => true, + 'altFormat' => 'F j, Y', + 'ariaDateFormat' => 'F j, Y', + 'dateFormat' => 'Y-m-d', + 'earliestDate' => null, + 'latestDate' => null, + 'locale' => 'en', + ], $filter->getConfigs()); + + $filter->config(['locale' => 'fr']); + + $this->assertSame([ + 'allowInput' => true, + 'altFormat' => 'F j, Y', + 'ariaDateFormat' => 'F j, Y', + 'dateFormat' => 'Y-m-d', + 'earliestDate' => null, + 'latestDate' => null, + 'locale' => 'fr', + ], + $filter->getConfigs() + ); + } + public function test_can_get_filter_options(): void { $filter = DateRangeFilter::make('Active'); diff --git a/tests/Views/Filters/MultiSelectDropdownFilterTest.php b/tests/Views/Filters/MultiSelectDropdownFilterTest.php index b0a31441c..8f06e0209 100644 --- a/tests/Views/Filters/MultiSelectDropdownFilterTest.php +++ b/tests/Views/Filters/MultiSelectDropdownFilterTest.php @@ -90,8 +90,8 @@ public function test_can_get_filter_pill_title(): void public function test_can_get_filter_pill_value(array $optionsArray): void { $filter = MultiSelectDropdownFilter::make('Active')->options($optionsArray); - $this->assertSame($optionsArray[1], $filter->getFilterPillValue(['1'])); - $this->assertSame($optionsArray[1].', '.$optionsArray[2], $filter->getFilterPillValue(['1', '2'])); + $this->assertSame($optionsArray[1], $filter->getFilterPillValue(['1'])[0]); + $this->assertSame([$optionsArray[1], $optionsArray[2]], $filter->getFilterPillValue(['1', '2'])); } public function test_can_check_if_filter_has_configs(): void @@ -259,4 +259,13 @@ public function test_can_set_select_filter_wireable_live(array $optionsArray): v $this->assertSame('wire:model.live.debounce.500ms=filterComponents.active', $filter->getWireMethod('filterComponents.'.$filter->getKey())); } + + #[Depends('test_array_setup')] + public function test_can_set_separator(array $optionsArray): void + { + $filter = MultiSelectDropdownFilter::make('Active')->options($optionsArray); + $this->assertSame(', ', $filter->getPillsSeparator()); + $filter->setPillsSeparator('
'); + $this->assertSame('
', $filter->getPillsSeparator()); + } } diff --git a/tests/Views/Filters/MultiSelectFilterTest.php b/tests/Views/Filters/MultiSelectFilterTest.php index cf3ebdeab..9d52f88bd 100644 --- a/tests/Views/Filters/MultiSelectFilterTest.php +++ b/tests/Views/Filters/MultiSelectFilterTest.php @@ -224,6 +224,13 @@ public function test_can_set_select_filter_wireable_live(): void $this->assertSame('live.debounce.500ms', $filter->getWireableMethod()); $this->assertSame('wire:model.live.debounce.500ms=filterComponents.active', $filter->getWireMethod('filterComponents.'.$filter->getKey())); + } + public function test_can_set_separator(): void + { + $filter = MultiSelectFilter::make('Active'); + $this->assertSame(', ', $filter->getPillsSeparator()); + $filter->setPillsSeparator('
'); + $this->assertSame('
', $filter->getPillsSeparator()); } } From 98117b65c288b907ca5418fbaacf12c51cc0ef04 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Fri, 19 Jul 2024 20:49:36 +0100 Subject: [PATCH 048/338] Fix DateTimeFilter not wired correctly. Thanks to @RenoLooijmans for the fix --- resources/views/components/tools/filters/datetime.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/components/tools/filters/datetime.blade.php b/resources/views/components/tools/filters/datetime.blade.php index 5abd43c94..901cc8b11 100644 --- a/resources/views/components/tools/filters/datetime.blade.php +++ b/resources/views/components/tools/filters/datetime.blade.php @@ -5,7 +5,7 @@ "rounded-md shadow-sm" => $isTailwind, "mb-3 mb-md-0 input-group" => $isBootstrap, ])> - getWireMethod("filterComponents.".$filter->getKey()) }} wire:key="{{ $filter->generateWireKey($tableName, 'datetime') }}" id="{{ $tableName }}-filter-{{ $filter->getKey() }}@if($filter->hasCustomPosition())-{{ $filter->getCustomPosition() }}@endif" type="datetime-local" From e64fa40edccbd25dab2c9b54c85dab9f0971dc27 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Fri, 19 Jul 2024 21:41:59 +0100 Subject: [PATCH 049/338] Set Custom Classes for Collapsing Column - Collapse/Expand buttons (#1785) * Initial Commit * Update Blade * Add Tests for Collapsing Column Button Attributes * Add DocBlock --------- Co-authored-by: lrljoe --- docs/datatable/available-methods.md | 280 +------------ docs/datatable/styling.md | 385 ++++++++++++++++++ .../table/td/collapsed-columns.blade.php | 42 +- .../CollapsingColumnConfiguration.php | 24 ++ .../Helpers/CollapsingColumnHelpers.php | 24 ++ src/Traits/WithCollapsingColumns.php | 4 + .../ComponentConfigurationTest.php | 39 ++ 7 files changed, 516 insertions(+), 282 deletions(-) create mode 100644 docs/datatable/styling.md diff --git a/docs/datatable/available-methods.md b/docs/datatable/available-methods.md index f774367f4..b897d6708 100644 --- a/docs/datatable/available-methods.md +++ b/docs/datatable/available-methods.md @@ -20,283 +20,7 @@ public function configure(): void ## Attributes -### setComponentWrapperAttributes - -Set a list of attributes to override on the main wrapper of the component - -```php -public function configure(): void -{ - $this->setComponentWrapperAttributes([ - 'id' => 'my-id', - 'class' => 'this that', - ]); -} -``` - -### setTableWrapperAttributes - -Set a list of attributes to override on the div that wraps the table - -```php -public function configure(): void -{ - $this->setTableWrapperAttributes([ - 'id' => 'my-id', - 'class' => 'this that', - ]); -} -``` - -By default, this replaces the default classes on the table wrapper, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setTableWrapperAttributes([ - 'default' => true, - 'class' => 'added these classes', - ]); -} -``` - -### setTableAttributes - -Set a list of attributes to override on the table element - -```php -public function configure(): void -{ - $this->setTableAttributes([ - 'id' => 'my-id', - 'class' => 'this that', - ]); -} -``` - -By default, this replaces the default classes on the table, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setTableAttributes([ - 'default' => true, - 'class' => 'added these classes', - ]); -} -``` - -### setTheadAttributes - -Set a list of attributes to override on the thead element - -```php -public function configure(): void -{ - $this->setTheadAttributes([ - 'id' => 'my-id', - 'class' => 'this that', - ]); -} -``` - -By default, this replaces the default classes on the thead, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setTheadAttributes([ - 'default' => true, - 'class' => 'added these classes', - ]); -} -``` - -### setTbodyAttributes - -Set a list of attributes to override on the tbody element - -```php -public function configure(): void -{ - $this->setTbodyAttributes([ - 'id' => 'my-id', - 'class' => 'this that', - ]); -} -``` - -By default, this replaces the default classes on the tbody, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setTbodyAttributes([ - 'default' => true, - 'class' => 'added these classes', - ]); -} -``` - -### setThAttributes - -Set a list of attributes to override on the th elements - -```php -public function configure(): void -{ - // Takes a callback that gives you the current column. - $this->setThAttributes(function(Column $column) { - if ($column->isField('name')) { - return [ - 'class' => 'bg-green-500', - ]; - } - - return []; - }); -} -``` - -By default, this replaces the default classes on the th, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setThAttributes(function(Column $column) { - if ($column->isField('name')) { - return [ - 'default' => true, - 'class' => 'bg-green-500', - ]; - } - - return ['default' => true]; - }); -} -``` - -### setThSortButtonAttributes - -Set a list of attributes to override on the th sort button elements - -```php -public function configure(): void -{ - // Takes a callback that gives you the current column. - $this->setThSortButtonAttributes(function(Column $column) { - if ($column->isField('name')) { - return [ - 'class' => 'bg-green-500', - ]; - } - - return []; - }); -} -``` - -### setTrAttributes - -Set a list of attributes to override on the tr elements - -```php -public function configure(): void -{ - // Takes a callback that gives you the current row and its index - $this->setTrAttributes(function($row, $index) { - if ($index % 2 === 0) { - return [ - 'class' => 'bg-gray-200', - ]; - } - - return []; - }); -} -``` - -By default, this replaces the default classes on the tr, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setTrAttributes(function($row, $index) { - if ($index % 2 === 0) { - return [ - 'default' => true, - 'class' => 'bg-gray-200', - ]; - } - - return ['default' => true]; - }); -} -``` - -### setTdAttributes - -Set a list of attributes to override on the td elements - -```php -public function configure(): void -{ - // Takes a callback that gives you the current column, row, column index, and row index - $this->setTdAttributes(function(Column $column, $row, $columnIndex, $rowIndex) { - if ($column->isField('total') && $row->total < 1000) { - return [ - 'class' => 'bg-red-500 text-white', - ]; - } - - return []; - }); -} -``` - -By default, this replaces the default classes on the td, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - // Takes a callback that gives you the current column, row, column index, and row index - $this->setTdAttributes(function(Column $column, $row, $columnIndex, $rowIndex) { - if ($column->isField('total') && $row->total < 1000) { - return [ - 'default' => true, - 'class' => 'bg-red-500 text-white', - ]; - } - - return ['default' => true]; - }); -} - -``` -### setSearchFieldAttributes - -Set a list of attributes to override on the search field - -```php -public function configure(): void -{ - $this->setSearchFieldAttributes([ - 'class' => 'this that', - ]); -} -``` - -By default, this replaces the default classes on the search field, if you would like to keep them, set the default flag to true. - -```php -public function configure(): void -{ - $this->setSearchFieldAttributes([ - 'default' => true, - 'class' => 'added these classes', - ]); -} -``` +Documentation for Data Table Styling/Attributes is now: Here ## Offline @@ -461,4 +185,4 @@ public function configure(): void { $this->setEmptyMessage('No results found'); } -``` +``` \ No newline at end of file diff --git a/docs/datatable/styling.md b/docs/datatable/styling.md new file mode 100644 index 000000000..49726978f --- /dev/null +++ b/docs/datatable/styling.md @@ -0,0 +1,385 @@ +--- +title: Styling +weight: 5 +--- + +The package offers significant opportunities to customise the look & feel of the core table, as well as other elements (which are documented in the relevant sections). + +## Attributes + +### setComponentWrapperAttributes + +Set a list of attributes to override on the main wrapper of the component + +```php +public function configure(): void +{ + $this->setComponentWrapperAttributes([ + 'id' => 'my-id', + 'class' => 'this that', + ]); +} +``` + +### setTableWrapperAttributes + +Set a list of attributes to override on the div that wraps the table + +```php +public function configure(): void +{ + $this->setTableWrapperAttributes([ + 'id' => 'my-id', + 'class' => 'this that', + ]); +} +``` + +By default, this replaces the default classes on the table wrapper, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setTableWrapperAttributes([ + 'default' => true, + 'class' => 'added these classes', + ]); +} +``` + +### setTableAttributes + +Set a list of attributes to override on the table element + +```php +public function configure(): void +{ + $this->setTableAttributes([ + 'id' => 'my-id', + 'class' => 'this that', + ]); +} +``` + +By default, this replaces the default classes on the table, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setTableAttributes([ + 'default' => true, + 'class' => 'added these classes', + ]); +} +``` + +### setTheadAttributes + +Set a list of attributes to override on the thead element + +```php +public function configure(): void +{ + $this->setTheadAttributes([ + 'id' => 'my-id', + 'class' => 'this that', + ]); +} +``` + +By default, this replaces the default classes on the thead, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setTheadAttributes([ + 'default' => true, + 'class' => 'added these classes', + ]); +} +``` + +### setTbodyAttributes + +Set a list of attributes to override on the tbody element + +```php +public function configure(): void +{ + $this->setTbodyAttributes([ + 'id' => 'my-id', + 'class' => 'this that', + ]); +} +``` + +By default, this replaces the default classes on the tbody, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setTbodyAttributes([ + 'default' => true, + 'class' => 'added these classes', + ]); +} +``` + +### setThAttributes + +Set a list of attributes to override on the th elements + +```php +public function configure(): void +{ + // Takes a callback that gives you the current column. + $this->setThAttributes(function(Column $column) { + if ($column->isField('name')) { + return [ + 'class' => 'bg-green-500', + ]; + } + + return []; + }); +} +``` + +By default, this replaces the default classes on the th, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setThAttributes(function(Column $column) { + if ($column->isField('name')) { + return [ + 'default' => true, + 'class' => 'bg-green-500', + ]; + } + + return ['default' => true]; + }); +} +``` + +### setThSortButtonAttributes + +Set a list of attributes to override on the th sort button elements + +```php +public function configure(): void +{ + // Takes a callback that gives you the current column. + $this->setThSortButtonAttributes(function(Column $column) { + if ($column->isField('name')) { + return [ + 'class' => 'bg-green-500', + ]; + } + + return []; + }); +} +``` + +### setTrAttributes + +Set a list of attributes to override on the tr elements + +```php +public function configure(): void +{ + // Takes a callback that gives you the current row and its index + $this->setTrAttributes(function($row, $index) { + if ($index % 2 === 0) { + return [ + 'class' => 'bg-gray-200', + ]; + } + + return []; + }); +} +``` + +By default, this replaces the default classes on the tr, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setTrAttributes(function($row, $index) { + if ($index % 2 === 0) { + return [ + 'default' => true, + 'class' => 'bg-gray-200', + ]; + } + + return ['default' => true]; + }); +} +``` + +### setTdAttributes + +Set a list of attributes to override on the td elements + +```php +public function configure(): void +{ + // Takes a callback that gives you the current column, row, column index, and row index + $this->setTdAttributes(function(Column $column, $row, $columnIndex, $rowIndex) { + if ($column->isField('total') && $row->total < 1000) { + return [ + 'class' => 'bg-red-500 text-white', + ]; + } + + return []; + }); +} +``` + +By default, this replaces the default classes on the td, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + // Takes a callback that gives you the current column, row, column index, and row index + $this->setTdAttributes(function(Column $column, $row, $columnIndex, $rowIndex) { + if ($column->isField('total') && $row->total < 1000) { + return [ + 'default' => true, + 'class' => 'bg-red-500 text-white', + ]; + } + + return ['default' => true]; + }); +} + +``` +### setSearchFieldAttributes + +Set a list of attributes to override on the search field + +```php +public function configure(): void +{ + $this->setSearchFieldAttributes([ + 'class' => 'this that', + ]); +} +``` + +By default, this replaces the default classes on the search field, if you would like to keep them, set the default flag to true. + +```php +public function configure(): void +{ + $this->setSearchFieldAttributes([ + 'default' => true, + 'class' => 'added these classes', + ]); +} +``` + +### setCollapsingColumnButtonCollapseAttributes + +This customises the look & feel of the button displayed to Collapse an expanded Collapsed Column + +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonCollapseAttributes([ + 'class' => 'text-blue-500', + ]); +} +``` + +By default, this replaces the default classes on the button, if you would like to keep: +Default Colors: set default-colors flag to true. +Default Styling: set default-styling flag to true. + +#### Keeping the Default Styling +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonCollapseAttributes([ + 'default-styling' => true, + 'class' => 'text-blue-500', + ]); +} +``` + +#### Keeping the Default Colors +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonCollapseAttributes([ + 'default-colors' => true, + 'class' => 'h-12 w-12', + ]); +} +``` + +#### Replacing All +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonCollapseAttributes([ + 'class' => 'h-12 w-12 text-blue-500', + ]); +} +``` + + +### setCollapsingColumnButtonExpandAttributes + +This customises the look & feel of the button displayed to Expand a collapsed Collapsed Column + +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonExpandAttributes([ + 'class' => 'text-red-500', + ]); +} +``` + +By default, this replaces the default classes on the button, if you would like to keep: +Default Colors: set default-colors flag to true. +Default Styling: set default-styling flag to true. + +#### Keeping the Default Styling +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonExpandAttributes([ + 'default-styling' => true, + 'class' => 'text-red-500', + ]); +} +``` + +#### Keeping the Default Colors +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonExpandAttributes([ + 'default-colors' => true, + 'class' => 'h-12 w-12', + ]); +} +``` + +#### Replacing All +```php +public function configure(): void +{ + $this->setCollapsingColumnButtonExpandAttributes([ + 'class' => 'h-12 w-12 text-red-500', + ]); +} +``` diff --git a/resources/views/components/table/td/collapsed-columns.blade.php b/resources/views/components/table/td/collapsed-columns.blade.php index 29248b1cd..e591825dc 100644 --- a/resources/views/components/table/td/collapsed-columns.blade.php +++ b/resources/views/components/table/td/collapsed-columns.blade.php @@ -18,8 +18,26 @@ x-cloak x-show="!currentlyReorderingStatus" x-on:click.prevent="$dispatch('toggle-row-content', {'tableName': '{{ $tableName }}', 'row': {{ $rowIndex }}}); open = !open" > - - + merge($this->getCollapsingColumnButtonExpandAttributes) + ->class([ + 'h-6 w-6' => $this->getCollapsingColumnButtonExpandAttributes['default-styling'] ?? true, + 'text-green-600' => $this->getCollapsingColumnButtonExpandAttributes['default-colors'] ?? true, + ]) + ->except('default') + }} + /> + merge($this->getCollapsingColumnButtonCollapseAttributes) + ->class([ + 'h-6 w-6' => $this->getCollapsingColumnButtonCollapseAttributes['default-styling'] ?? true, + 'text-yellow-600' => $this->getCollapsingColumnButtonCollapseAttributes['default-colors'] ?? true, + ]) + ->except('default') + }} + /> @endif @@ -40,8 +58,24 @@ class="p-0" style="background:none;border:none;" > - - + merge($this->getCollapsingColumnButtonExpandAttributes) + ->class([ + 'text-success' => $this->getCollapsingColumnButtonExpandAttributes['default-colors'] ?? true, + ]) + ->except('default') + }} + /> + merge($this->getCollapsingColumnButtonExpandAttributes) + ->class([ + 'text-warning' => $this->getCollapsingColumnButtonExpandAttributes['default-colors'] ?? true, + ]) + ->except('default') + }} + /> @endif diff --git a/src/Traits/Configuration/CollapsingColumnConfiguration.php b/src/Traits/Configuration/CollapsingColumnConfiguration.php index 88e883e7d..d39e648a5 100644 --- a/src/Traits/Configuration/CollapsingColumnConfiguration.php +++ b/src/Traits/Configuration/CollapsingColumnConfiguration.php @@ -24,4 +24,28 @@ public function setCollapsingColumnsDisabled(): self return $this; } + + /** + * Used to set attributes for the Collapsed Column Collapse Button + * + * @param array $collapsingColumnButtonCollapseAttributes + */ + public function setCollapsingColumnButtonCollapseAttributes(array $collapsingColumnButtonCollapseAttributes): self + { + $this->collapsingColumnButtonCollapseAttributes = [...['default-colors' => false, 'default-styling' => false], ...$collapsingColumnButtonCollapseAttributes]; + + return $this; + } + + /** + * Used to set attributes for the Collapsed Column Expand Button + * + * @param array $collapsingColumnButtonExpandAttributes + */ + public function setCollapsingColumnButtonExpandAttributes(array $collapsingColumnButtonExpandAttributes): self + { + $this->collapsingColumnButtonExpandAttributes = [...['default-colors' => false, 'default-styling' => false], ...$collapsingColumnButtonExpandAttributes]; + + return $this; + } } diff --git a/src/Traits/Helpers/CollapsingColumnHelpers.php b/src/Traits/Helpers/CollapsingColumnHelpers.php index f04b705eb..a2e9bfb5f 100644 --- a/src/Traits/Helpers/CollapsingColumnHelpers.php +++ b/src/Traits/Helpers/CollapsingColumnHelpers.php @@ -2,6 +2,8 @@ namespace Rappasoft\LaravelLivewireTables\Traits\Helpers; +use Livewire\Attributes\Computed; + trait CollapsingColumnHelpers { public function getCollapsingColumnsStatus(): bool @@ -23,4 +25,26 @@ public function collapsingColumnsAreDisabled(): bool { return $this->getCollapsingColumnsStatus() === false; } + + /** + * Retrieves attributes for the Collapsed Column Collapse Button + * + * @return array + */ + #[Computed] + public function getCollapsingColumnButtonCollapseAttributes(): array + { + return [...['default-styling' => true, 'default-colors' => true], ...$this->collapsingColumnButtonCollapseAttributes]; + } + + /** + * Retrieves attributes for the Collapsed Column Expand Button + * + * @return array + */ + #[Computed] + public function getCollapsingColumnButtonExpandAttributes(): array + { + return [...['default-styling' => true, 'default-colors' => true], ...$this->collapsingColumnButtonExpandAttributes]; + } } diff --git a/src/Traits/WithCollapsingColumns.php b/src/Traits/WithCollapsingColumns.php index 259442ed3..60e454970 100644 --- a/src/Traits/WithCollapsingColumns.php +++ b/src/Traits/WithCollapsingColumns.php @@ -11,4 +11,8 @@ trait WithCollapsingColumns use CollapsingColumnHelpers; protected bool $collapsingColumnsStatus = true; + + protected array $collapsingColumnButtonCollapseAttributes = ['default-styling' => true, 'default-colors' => true]; + + protected array $collapsingColumnButtonExpandAttributes = ['default-styling' => true, 'default-colors' => true]; } diff --git a/tests/Traits/Configuration/ComponentConfigurationTest.php b/tests/Traits/Configuration/ComponentConfigurationTest.php index 3bed87443..3892083a5 100644 --- a/tests/Traits/Configuration/ComponentConfigurationTest.php +++ b/tests/Traits/Configuration/ComponentConfigurationTest.php @@ -402,4 +402,43 @@ public function test_can_add_extra_with_avg(): void $this->assertTrue($this->basicTable->hasExtraWithAvgs()); $this->assertSame([['table' => 'user', 'field' => 'age']], $this->basicTable->getExtraWithAvgs()); } + + public function test_can_set_collapsing_column_button_collapse_attributes(): void + { + $this->assertSame(['default-styling' => true, 'default-colors' => true], $this->basicTable->getCollapsingColumnButtonCollapseAttributes()); + + $this->basicTable->setCollapsingColumnButtonCollapseAttributes(['class' => 'text-blue-500']); + $this->assertSame(['default-styling' => false, 'default-colors' => false, 'class' => 'text-blue-500'], $this->basicTable->getCollapsingColumnButtonCollapseAttributes()); + + $this->basicTable->setCollapsingColumnButtonCollapseAttributes(['class' => 'text-blue-500', 'default-styling' => true]); + $this->assertSame(['default-styling' => true, 'default-colors' => false, 'class' => 'text-blue-500'], $this->basicTable->getCollapsingColumnButtonCollapseAttributes()); + + $this->basicTable->setCollapsingColumnButtonCollapseAttributes(['class' => 'text-red-500', 'default-colors' => true]); + $this->assertSame(['default-styling' => false, 'default-colors' => true, 'class' => 'text-red-500'], $this->basicTable->getCollapsingColumnButtonCollapseAttributes()); + + $this->basicTable->setCollapsingColumnButtonCollapseAttributes(['default-styling' => true, 'class' => 'text-green-500', 'default-colors' => true]); + $this->assertSame(['default-styling' => true, 'default-colors' => true, 'class' => 'text-green-500'], $this->basicTable->getCollapsingColumnButtonCollapseAttributes()); + + $this->assertSame(['default-styling' => true, 'default-colors' => true], $this->basicTable->getCollapsingColumnButtonExpandAttributes()); + } + + public function test_can_set_collapsing_column_button_expand_attributes(): void + { + $this->assertSame(['default-styling' => true, 'default-colors' => true], $this->basicTable->getCollapsingColumnButtonExpandAttributes()); + + $this->basicTable->setCollapsingColumnButtonExpandAttributes(['class' => 'text-blue-500']); + $this->assertSame(['default-styling' => false, 'default-colors' => false, 'class' => 'text-blue-500'], $this->basicTable->getCollapsingColumnButtonExpandAttributes()); + + $this->basicTable->setCollapsingColumnButtonExpandAttributes(['class' => 'text-blue-500', 'default-styling' => true]); + $this->assertSame(['default-styling' => true, 'default-colors' => false, 'class' => 'text-blue-500'], $this->basicTable->getCollapsingColumnButtonExpandAttributes()); + + $this->basicTable->setCollapsingColumnButtonExpandAttributes(['class' => 'text-red-500', 'default-colors' => true]); + $this->assertSame(['default-styling' => false, 'default-colors' => true, 'class' => 'text-red-500'], $this->basicTable->getCollapsingColumnButtonExpandAttributes()); + + $this->basicTable->setCollapsingColumnButtonExpandAttributes(['default-styling' => true, 'class' => 'text-green-500', 'default-colors' => true]); + $this->assertSame(['default-styling' => true, 'default-colors' => true, 'class' => 'text-green-500'], $this->basicTable->getCollapsingColumnButtonExpandAttributes()); + + $this->assertSame(['default-styling' => true, 'default-colors' => true], $this->basicTable->getCollapsingColumnButtonCollapseAttributes()); + + } } From e959209a9e365353b28d9a27f451c47cb6139b53 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Fri, 19 Jul 2024 22:12:18 +0100 Subject: [PATCH 050/338] Add ViewComponentColumn (#1779) * AddViewComponentColumn * Add ViewComponentColumn Docs * Add ViewComponentColumnTests --------- Co-authored-by: lrljoe --- docs/column-types/view_component_column.md | 32 +++++++++++ docs/column-types/wire_link_column.md | 2 +- src/Views/Columns/ViewComponentColumn.php | 51 +++++++++++++++++ .../ViewComponentColumnConfiguration.php | 16 ++++++ .../Helpers/ViewComponentColumnHelpers.php | 22 ++++++++ tests/Http/TestComponent.php | 27 +++++++++ tests/TestCase.php | 4 ++ .../Views/Columns/ViewComponentColumnTest.php | 55 +++++++++++++++++++ 8 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 docs/column-types/view_component_column.md create mode 100644 src/Views/Columns/ViewComponentColumn.php create mode 100644 src/Views/Traits/Configuration/ViewComponentColumnConfiguration.php create mode 100644 src/Views/Traits/Helpers/ViewComponentColumnHelpers.php create mode 100644 tests/Http/TestComponent.php create mode 100644 tests/Views/Columns/ViewComponentColumnTest.php diff --git a/docs/column-types/view_component_column.md b/docs/column-types/view_component_column.md new file mode 100644 index 000000000..1df2ea7a3 --- /dev/null +++ b/docs/column-types/view_component_column.md @@ -0,0 +1,32 @@ +--- +title: View Component Columns +weight: 14 +--- + +View Component columns let you specify a component name and attributes and provide attributes to the View Component. This will render the View Component in it's entirety. + +```php + +ViewComponentColumn::make('E-mail', 'email') + ->component('email') + ->attributes(fn ($value, $row, Column $column) => [ + 'id' => $row->id, + 'email' => $value, + ]), +``` + +Please also see the following for other available methods: + \ No newline at end of file diff --git a/docs/column-types/wire_link_column.md b/docs/column-types/wire_link_column.md index eaee09537..9abf15aac 100644 --- a/docs/column-types/wire_link_column.md +++ b/docs/column-types/wire_link_column.md @@ -1,6 +1,6 @@ --- title: Wire Link Column (beta) -weight: 14 +weight: 15 --- WireLink columns provide a way to display Wired Links in your table without having to use `format()` or partial views, with or without a Confirmation Message diff --git a/src/Views/Columns/ViewComponentColumn.php b/src/Views/Columns/ViewComponentColumn.php new file mode 100644 index 000000000..91b3ec5ff --- /dev/null +++ b/src/Views/Columns/ViewComponentColumn.php @@ -0,0 +1,51 @@ +html(); + + } + + public function getContents(Model $row): null|string|HtmlString|DataTableConfigurationException|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + { + if ($this->isLabel()) { + throw new DataTableConfigurationException('You can not use a label column with a component column'); + } + + if ($this->hasComponentView() === false) { + throw new DataTableConfigurationException('You must specify a component view for a component column'); + } + + $value = $this->getValue($row); + $attributes = ['value' => $value]; + + if ($this->hasAttributesCallback()) { + $attributes = call_user_func($this->getAttributesCallback(), $value, $row, $this); + + if (! is_array($attributes)) { + throw new DataTableConfigurationException('The return type of callback must be an array'); + } + } + + return \Illuminate\Support\Facades\Blade::render( + 'getComponentView().' '.new ComponentAttributeBag($attributes).' />'); + } +} diff --git a/src/Views/Traits/Configuration/ViewComponentColumnConfiguration.php b/src/Views/Traits/Configuration/ViewComponentColumnConfiguration.php new file mode 100644 index 000000000..7e0300854 --- /dev/null +++ b/src/Views/Traits/Configuration/ViewComponentColumnConfiguration.php @@ -0,0 +1,16 @@ +componentView = $component; + + return $this; + } +} diff --git a/src/Views/Traits/Helpers/ViewComponentColumnHelpers.php b/src/Views/Traits/Helpers/ViewComponentColumnHelpers.php new file mode 100644 index 000000000..8de648e84 --- /dev/null +++ b/src/Views/Traits/Helpers/ViewComponentColumnHelpers.php @@ -0,0 +1,22 @@ +componentView; + } + + /** + * Determines whether a Component View has been set + */ + public function hasComponentView(): bool + { + return isset($this->componentView); + } +} diff --git a/tests/Http/TestComponent.php b/tests/Http/TestComponent.php new file mode 100644 index 000000000..7fa31a7ba --- /dev/null +++ b/tests/Http/TestComponent.php @@ -0,0 +1,27 @@ +testItem = $age * 110; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return \Illuminate\Support\Facades\Blade::render( + '
'.($this->testItem ?? 'Unknown').'
'); + + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index ab22227bd..2a9a2479a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,11 +5,13 @@ use BladeUI\Heroicons\BladeHeroiconsServiceProvider; use BladeUI\Icons\BladeIconsServiceProvider; use Illuminate\Encryption\Encrypter; +use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\DB; use Livewire\LivewireServiceProvider; use Orchestra\Testbench\TestCase as Orchestra; use Rappasoft\LaravelLivewireTables\LaravelLivewireTablesServiceProvider; use Rappasoft\LaravelLivewireTables\Tests\Http\Livewire\{PetsTable,PetsTableUnpaginated,SpeciesTable}; +use Rappasoft\LaravelLivewireTables\Tests\Http\TestComponent; use Rappasoft\LaravelLivewireTables\Tests\Models\Breed; use Rappasoft\LaravelLivewireTables\Tests\Models\Pet; use Rappasoft\LaravelLivewireTables\Tests\Models\Species; @@ -30,6 +32,8 @@ protected function setUp(): void { parent::setUp(); + Blade::component('test-component', TestComponent::class); + if (! Breed::where('id', 1)->get()) { include_once __DIR__.'/../database/migrations/create_test_tables.php.stub'; (new \CreateTestTables())->down(); diff --git a/tests/Views/Columns/ViewComponentColumnTest.php b/tests/Views/Columns/ViewComponentColumnTest.php new file mode 100644 index 000000000..ae40b89d5 --- /dev/null +++ b/tests/Views/Columns/ViewComponentColumnTest.php @@ -0,0 +1,55 @@ +assertSame('Total Users', $column->getTitle()); + } + + public function test_can_have_component_view(): void + { + $column = ViewComponentColumn::make('Age 2', 'age') + ->component('test-component') + ->attributes(fn ($value, $row, Column $column) => [ + 'age' => $row->age, + ]); + $this->assertTrue($column->hasComponentView()); + } + + public function test_can_not_omit_component(): void + { + $this->expectException(DataTableConfigurationException::class); + + $column = ViewComponentColumn::make('Age 2', 'age') + ->attributes(fn ($value, $row, Column $column) => [ + 'age' => $row->age, + ]); + $contents = $column->getContents(Pet::find(1)); + $this->assertSame('
2420
', $contents); + + } + + public function test_can_render_component(): void + { + + $column = ViewComponentColumn::make('Age 2', 'age') + ->component('test-component') + ->attributes(fn ($value, $row, Column $column) => [ + 'age' => $row->age, + ]); + $contents = $column->getContents(Pet::find(1)); + $this->assertSame('
2420
', $contents); + + } +} From 79fe6b01ec0917c634837e1437176877657b4a72 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sat, 20 Jul 2024 01:18:30 +0100 Subject: [PATCH 051/338] Add new ViewComponentColumn (#1786) * AddViewComponentColumn * Add ViewComponentColumn Docs * Add ViewComponentColumnTests * Fix styling --------- Co-authored-by: lrljoe From d63c63f6c790c0dd98db9bf2862e8bf7893e7fb7 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sat, 20 Jul 2024 01:21:52 +0100 Subject: [PATCH 052/338] Update ChangeLog for Release --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 165e4b733..e10de2f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.3.2] - 2024-07-20 +### New Features +- Add a new ViewComponentColumn that runs the construction for a View Component by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1786 +- Set Custom Classes for Collapsing Column - Collapse/Expand buttons by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1785 + +### Bug Fixes +- Fix DateTimeFilter by @RenoLooijmans in https://github.com/rappasoft/laravel-livewire-tables/pull/1784 +- Fix AggregateColumn SnakeCase issue by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1777 + ## [v3.3.1] - 2024-07-16 ### Bug Fixes - Fix NumberRangeFilter initial state when loaded by querystring by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1774 From b5d299d438dc205073f0d1feac404c32faa046a4 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 23 Jul 2024 21:39:53 +0100 Subject: [PATCH 053/338] v3.3.3 (#1792) * Add more FlatpickrJS options --- docs/filter-types/filters-daterange.md | 24 +++++++++++++++++ resources/js/laravel-livewire-tables.js | 29 ++++++++++++++------- resources/js/laravel-livewire-tables.min.js | 2 +- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/docs/filter-types/filters-daterange.md b/docs/filter-types/filters-daterange.md index e496b31ed..db2eb323b 100644 --- a/docs/filter-types/filters-daterange.md +++ b/docs/filter-types/filters-daterange.md @@ -43,6 +43,30 @@ public function filters(): array } ``` +A full list of options is below, please see the Flatpickr documentation for reference as to purpose: +| Config Option | Type | Default | Description | +| ------------- | ------------- | ------------- | ------------- | +| allowInput | Boolean | false | Allows the user to enter a date directly into the input field. By default, direct entry is disabled. | +| altFormat | String | "F j, Y" | Exactly the same as date format, but for the altInput field | +| altInput | Boolean | false | Show the user a readable date (as per altFormat), but return something totally different to the server. | +| ariaDateFormat | String | "F j, Y" | Defines how the date will be formatted in the aria-label for calendar days, using the same tokens as dateFormat. If you change this, you should choose a value that will make sense if a screen reader reads it out loud. | +| dateFormat | String | 'Y-m-d' | A string of characters which are used to define how the date will be displayed in the input box | +| defaultDate | String | null | Sets the initial selected date | +| defaultHour | Number | 12 | Initial value of the hour element. | +| defaultMinute | Number | 0 | Initial value of the minute element. | +| earliestDate | String/Date | null | The minimum date that a user can start picking from (inclusive) | +| enableTime | Boolean | false | Enables time picker | +| enableSeconds | Boolean | false | Enables seconds in the time picker. | +| hourIncrement | Integer | 1 | Adjusts the step for the hour input (incl. scrolling) | +| latestDate | String/Date | null | The maximum date that a user can pick to (inclusive) | +| locale | String | en | The locale used (see below) | +| minuteIncrement | Integer | 5 | Adjusts the step for the minute input (incl. scrolling) | +| placeholder | String | null | Set a placeholder value for the input field | +| shorthandCurrentMonth | Boolean | false | Show the month using the shorthand version (ie, Sep instead of September). | +| time_24hr | Boolean | false | Displays time picker in 24 hour mode without AM/PM selection when enabled. | +| weekNumbers | Boolean | false | Enables display of week numbers in calendar. | + + ## Configuration By default, this filter will inject the Flatpickr JS Library and CSS. However, you can customise this behaviour using the configuration file. diff --git a/resources/js/laravel-livewire-tables.js b/resources/js/laravel-livewire-tables.js index c9afc8331..0c02f6c47 100644 --- a/resources/js/laravel-livewire-tables.js +++ b/resources/js/laravel-livewire-tables.js @@ -121,17 +121,26 @@ document.addEventListener('alpine:init', () => { wireValues: wire.entangle('filterComponents.' + filterKey), flatpickrInstance: flatpickr(refLocation, { mode: 'range', - clickOpens: true, + altFormat: filterConfig['altFormat'] ?? "F j, Y", + altInput: filterConfig['altInput'] ?? false, + allowInput: filterConfig['allowInput'] ?? false, allowInvalidPreload: true, - defaultDate: [], - ariaDateFormat: filterConfig['ariaDateFormat'], - allowInput: filterConfig['allowInput'], - altFormat: filterConfig['altFormat'], - altInput: filterConfig['altInput'], - dateFormat: filterConfig['dateFormat'], - locale: 'en', - minDate: filterConfig['earliestDate'], - maxDate: filterConfig['latestDate'], + ariaDateFormat: filterConfig['ariaDateFormat'] ?? "F j, Y", + clickOpens: true, + dateFormat: filterConfig['dateFormat'] ?? "Y-m-d", + defaultDate: filterConfig['defaultDate'] ?? null, + defaultHour: filterConfig['defaultHour'] ?? 12, + defaultMinute: filterConfig['defaultMinute'] ?? 0, + enableTime: filterConfig['enableTime'] ?? false, + enableSeconds: filterConfig['enableSeconds'] ?? false, + hourIncrement: filterConfig['hourIncrement'] ?? 1, + locale: filterConfig['locale'] ?? 'en', + minDate: filterConfig['earliestDate'] ?? null, + maxDate: filterConfig['latestDate'] ?? null, + minuteIncrement: filterConfig['minuteIncrement'] ?? 5, + shorthandCurrentMonth: filterConfig['shorthandCurrentMonth'] ?? false, + time_24hr: filterConfig['time_24hr'] ?? false, + weekNumbers: filterConfig['weekNumbers'] ?? false, onOpen: function () { window.childElementOpen = true; }, diff --git a/resources/js/laravel-livewire-tables.min.js b/resources/js/laravel-livewire-tables.min.js index 740ebca80..46255d270 100644 --- a/resources/js/laravel-livewire-tables.min.js +++ b/resources/js/laravel-livewire-tables.min.js @@ -1 +1 @@ -document.addEventListener("alpine:init",()=>{Alpine.data("tableWrapper",(e,t)=>({childElementOpen:!1,filtersOpen:e.entangle("filterSlideDownDefaultVisible"),paginationCurrentCount:e.entangle("paginationCurrentCount"),paginationTotalItemCount:e.entangle("paginationTotalItemCount"),paginationCurrentItems:e.entangle("paginationCurrentItems"),selectedItems:e.entangle("selected"),hideBulkActionsWhenEmpty:e.entangle("hideBulkActionsWhenEmpty"),toggleSelectAll(){t&&(this.paginationTotalItemCount===this.selectedItems.length?this.clearSelected():this.setAllSelected())},setAllSelected(){t&&e.setAllSelected()},clearSelected(){t&&e.clearSelected()},selectAllOnPage(){if(!t)return;let e=this.selectedItems,i=this.paginationCurrentItems.values();for(let l of i)e.push(l.toString());this.selectedItems=[...new Set(e)]}})),Alpine.data("numberRangeFilter",(e,t,i,l,s)=>({allFilters:e.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!1),defaultMin:l.minRange,defaultMax:l.maxRange,restrictUpdates:!1,initialiseStyles(){let e=document.getElementById(i);e.style.setProperty("--value-a",this.wireValues.min??this.filterMin),e.style.setProperty("--text-value-a",JSON.stringify(this.wireValues.min??this.filterMin)),e.style.setProperty("--value-b",this.wireValues.max??this.filterMax),e.style.setProperty("--text-value-b",JSON.stringify(this.wireValues.max??this.filterMax))},updateStyles(e,t){let l=document.getElementById(i);l.style.setProperty("--value-a",e),l.style.setProperty("--text-value-a",JSON.stringify(e)),l.style.setProperty("--value-b",t),l.style.setProperty("--text-value-b",JSON.stringify(t))},setupWire(){void 0!==this.wireValues?(this.filterMin=this.originalMin=void 0!==this.wireValues.min?this.wireValues.min:this.defaultMin,this.filterMax=this.originalMax=void 0!==this.wireValues.max?this.wireValues.max:this.defaultMax):(this.filterMin=this.originalMin=this.defaultMin,this.filterMax=this.originalMax=this.defaultMax),this.updateStyles(this.filterMin,this.filterMax)},allowUpdates(){this.updateWire()},updateWire(){let e=parseInt(this.filterMin),t=parseInt(this.filterMax);(e!=this.originalMin||t!=this.originalMax)&&(tthis.setupWire())}})),Alpine.data("flatpickrFilter",(e,t,i,l,s)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(l,{mode:"range",clickOpens:!0,allowInvalidPreload:!0,defaultDate:[],ariaDateFormat:i.ariaDateFormat,allowInput:i.allowInput,altFormat:i.altFormat,altInput:i.altInput,dateFormat:i.dateFormat,locale:"en",minDate:i.earliestDate,maxDate:i.latestDate,onOpen:function(){window.childElementOpen=!0},onChange:function(i,l,s){if(i.length>1){var a=l.split(" ")[0],r=l.split(" ")[2],n={};window.childElementOpen=!1,window.filterPopoverOpen=!1,n={minDate:a,maxDate:r},e.set("filterComponents."+t,n)}}}),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())}})),Alpine.data("reorderFunction",(e,t,i)=>({dragging:!1,reorderEnabled:!1,sourceID:"",targetID:"",evenRowClasses:"",oddRowClasses:"",currentlyHighlightedElement:"",evenRowClassArray:{},oddRowClassArray:{},evenNotInOdd:{},oddNotInEven:{},orderedRows:[],defaultReorderColumn:e.get("defaultReorderColumn"),reorderStatus:e.get("reorderStatus"),currentlyReorderingStatus:e.entangle("currentlyReorderingStatus"),hideReorderColumnUnlessReorderingStatus:e.entangle("hideReorderColumnUnlessReorderingStatus"),reorderDisplayColumn:e.entangle("reorderDisplayColumn"),dragStart(e){this.sourceID=e.target.id,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",e.target.id),e.target.classList.add("laravel-livewire-tables-dragging")},dragOverEvent(e){"object"==typeof this.currentlyHighlightedElement&&this.currentlyHighlightedElement.classList.remove("laravel-livewire-tables-highlight-bottom","laravel-livewire-tables-highlight-top");let t=e.target.closest("tr");this.currentlyHighlightedElement=t,e.offsetYi.getBoundingClientRect().height/2?l.insertBefore(s,i.nextSibling):l.insertBefore(s,i),r!l.includes(e)),this.oddNotInEven=l.filter(e=>!i.includes(e)),i=[],l=[])}},init(){this.$watch("currentlyReorderingStatus",e=>this.setupEvenOddClasses())}}))}); \ No newline at end of file +document.addEventListener("alpine:init",()=>{Alpine.data("tableWrapper",(e,t)=>({childElementOpen:!1,filtersOpen:e.entangle("filterSlideDownDefaultVisible"),paginationCurrentCount:e.entangle("paginationCurrentCount"),paginationTotalItemCount:e.entangle("paginationTotalItemCount"),paginationCurrentItems:e.entangle("paginationCurrentItems"),selectedItems:e.entangle("selected"),hideBulkActionsWhenEmpty:e.entangle("hideBulkActionsWhenEmpty"),toggleSelectAll(){t&&(this.paginationTotalItemCount===this.selectedItems.length?this.clearSelected():this.setAllSelected())},setAllSelected(){t&&e.setAllSelected()},clearSelected(){t&&e.clearSelected()},selectAllOnPage(){if(!t)return;let e=this.selectedItems,i=this.paginationCurrentItems.values();for(let l of i)e.push(l.toString());this.selectedItems=[...new Set(e)]}})),Alpine.data("numberRangeFilter",(e,t,i,l,s)=>({allFilters:e.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!1),defaultMin:l.minRange,defaultMax:l.maxRange,restrictUpdates:!1,initialiseStyles(){let e=document.getElementById(i);e.style.setProperty("--value-a",this.wireValues.min??this.filterMin),e.style.setProperty("--text-value-a",JSON.stringify(this.wireValues.min??this.filterMin)),e.style.setProperty("--value-b",this.wireValues.max??this.filterMax),e.style.setProperty("--text-value-b",JSON.stringify(this.wireValues.max??this.filterMax))},updateStyles(e,t){let l=document.getElementById(i);l.style.setProperty("--value-a",e),l.style.setProperty("--text-value-a",JSON.stringify(e)),l.style.setProperty("--value-b",t),l.style.setProperty("--text-value-b",JSON.stringify(t))},setupWire(){void 0!==this.wireValues?(this.filterMin=this.originalMin=void 0!==this.wireValues.min?this.wireValues.min:this.defaultMin,this.filterMax=this.originalMax=void 0!==this.wireValues.max?this.wireValues.max:this.defaultMax):(this.filterMin=this.originalMin=this.defaultMin,this.filterMax=this.originalMax=this.defaultMax),this.updateStyles(this.filterMin,this.filterMax)},allowUpdates(){this.updateWire()},updateWire(){let e=parseInt(this.filterMin),t=parseInt(this.filterMax);(e!=this.originalMin||t!=this.originalMax)&&(tthis.setupWire())}})),Alpine.data("flatpickrFilter",(e,t,i,l,s)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(l,{mode:"range",altFormat:i.altFormat??"F j, Y",altInput:i.altInput??!1,allowInput:i.allowInput??!1,allowInvalidPreload:!0,ariaDateFormat:i.ariaDateFormat??"F j, Y",clickOpens:!0,dateFormat:i.dateFormat??"Y-m-d",defaultDate:i.defaultDate??null,defaultHour:i.defaultHour??12,defaultMinute:i.defaultMinute??0,enableTime:i.enableTime??!1,enableSeconds:i.enableSeconds??!1,hourIncrement:i.hourIncrement??1,locale:i.locale??"en",minDate:i.earliestDate??null,maxDate:i.latestDate??null,minuteIncrement:i.minuteIncrement??5,shorthandCurrentMonth:i.shorthandCurrentMonth??!1,time_24hr:i.time_24hr??!1,weekNumbers:i.weekNumbers??!1,onOpen:function(){window.childElementOpen=!0},onChange:function(i,l,s){if(i.length>1){var a=l.split(" ")[0],r=l.split(" ")[2],n={};window.childElementOpen=!1,window.filterPopoverOpen=!1,n={minDate:a,maxDate:r},e.set("filterComponents."+t,n)}}}),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())}})),Alpine.data("reorderFunction",(e,t,i)=>({dragging:!1,reorderEnabled:!1,sourceID:"",targetID:"",evenRowClasses:"",oddRowClasses:"",currentlyHighlightedElement:"",evenRowClassArray:{},oddRowClassArray:{},evenNotInOdd:{},oddNotInEven:{},orderedRows:[],defaultReorderColumn:e.get("defaultReorderColumn"),reorderStatus:e.get("reorderStatus"),currentlyReorderingStatus:e.entangle("currentlyReorderingStatus"),hideReorderColumnUnlessReorderingStatus:e.entangle("hideReorderColumnUnlessReorderingStatus"),reorderDisplayColumn:e.entangle("reorderDisplayColumn"),dragStart(e){this.sourceID=e.target.id,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",e.target.id),e.target.classList.add("laravel-livewire-tables-dragging")},dragOverEvent(e){"object"==typeof this.currentlyHighlightedElement&&this.currentlyHighlightedElement.classList.remove("laravel-livewire-tables-highlight-bottom","laravel-livewire-tables-highlight-top");let t=e.target.closest("tr");this.currentlyHighlightedElement=t,e.offsetYi.getBoundingClientRect().height/2?l.insertBefore(s,i.nextSibling):l.insertBefore(s,i),r!l.includes(e)),this.oddNotInEven=l.filter(e=>!i.includes(e)),i=[],l=[])}},init(){this.$watch("currentlyReorderingStatus",e=>this.setupEvenOddClasses())}}))}); \ No newline at end of file From 5a3d4714ebd60167beb7919507823a8e14d32c0a Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 23 Jul 2024 21:47:56 +0100 Subject: [PATCH 054/338] FlatpickrJs Options Update (#1794) * Fix javascript updates * Add more FlatpickrJS options * Update docs for the flatpickr options * Update config Option Documentation * Update ChangeLog * Update Changelog Date --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e10de2f4b..f4ab866dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.3.3] - 2024-07-23 +### New Features +- Add additional DateRangeFilter options by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1793 + ## [v3.3.2] - 2024-07-20 ### New Features - Add a new ViewComponentColumn that runs the construction for a View Component by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1786 From 8bcca7c459df90fed6398bbfe7b7361ffc04428b Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sat, 27 Jul 2024 19:21:10 +0100 Subject: [PATCH 055/338] v3.3.4 - Development to Master (#1799) * Add SetFilterDefaultValue to DateRange Filter (#1796) --------- Co-authored-by: lrljoe * Add Localised Pills for DateRangeFilter --------- Co-authored-by: lrljoe * Tidying Filters - Migrating Carbon usage into Trait, Adding Lifecycle Hooks (#1798) * Tweaks to handle dates centrally * Add localisation for DateFilter, DateTimeFilter, migrate DateRangeFilter to use Core Trait * Add Filter Lifecycle Hooks * Add Search Lifecycle Hook * Update DateFilter, DateTimeFilter validate returns * Update DateRangeFilterTest * Add Pills Locale Tests to DateTests * Remove superfluous method --------- Co-authored-by: lrljoe * Update ChangeLog * Update docs for setPillsLocale (#1800) --------- Co-authored-by: lrljoe --- CHANGELOG.md | 8 + docs/filter-types/filters-date.md | 20 +- docs/filter-types/filters-daterange.md | 46 +++ docs/filter-types/filters-datetime.md | 20 +- docs/misc/lifecycle-hooks.md | 18 ++ src/Commands/MakeCommand.php | 4 +- src/Traits/Helpers/FilterHelpers.php | 6 + src/Traits/WithFilters.php | 10 + src/Traits/WithSearch.php | 3 + src/Views/Filters/DateFilter.php | 15 +- src/Views/Filters/DateRangeFilter.php | 86 +++-- src/Views/Filters/DateTimeFilter.php | 16 +- src/Views/Traits/Filters/HandlesDates.php | 59 ++++ src/Views/Traits/Filters/HasPillsLocale.php | 25 ++ tests/DataTableComponentTest.php | 6 +- tests/TestCase.php | 10 +- tests/Traits/WithMountTest.php | 6 +- tests/Views/Filters/DateFilterTest.php | 19 ++ tests/Views/Filters/DateRangeFilterTest.php | 334 +++++++++++--------- tests/Views/Filters/DateTimeFilterTest.php | 18 ++ 20 files changed, 525 insertions(+), 204 deletions(-) create mode 100644 src/Views/Traits/Filters/HandlesDates.php create mode 100644 src/Views/Traits/Filters/HasPillsLocale.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f4ab866dc..0375276e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.3.4] - 2024-07-27 +### New Features +- Added capability to setFilterDefaultValue for a DateRangeFilter by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1796 +- Add localised pill values for DateFilter, DateTimeFilter, DateRangeFilter by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1797 + +### Tweaks +- Migrating Carbon usage into Trait, Adding Filter/Search Lifecycle Hooks by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1798 + ## [v3.3.3] - 2024-07-23 ### New Features - Add additional DateRangeFilter options by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1793 diff --git a/docs/filter-types/filters-date.md b/docs/filter-types/filters-date.md index d29a900df..7880c6087 100644 --- a/docs/filter-types/filters-date.md +++ b/docs/filter-types/filters-date.md @@ -33,6 +33,7 @@ public function filters(): array } ``` +## setFilterDefaultValue Date filters also support the setFilterDefaultValue() method, which must be a valid date in the "Y-m-d" format. This will apply as a default until removed. ```php public function filters(): array @@ -47,5 +48,22 @@ public function filters(): array ]; } ``` - + +## setPillsLocale +Date Filters also support the setPillsLocale method, which allows you to set a locale for use in generating the Filter Pills values +```php +public function filters(): array +{ + return [ + DateFilter::make('Verified From') + ->setPillsLocale('fr ') // Use French localisation for the Filter Pills values + ->config([ + 'min' => '2020-01-01', // Earliest Acceptable Date + 'max' => '2021-12-31', // Latest Acceptable Date + 'pillFormat' => 'd M Y', // Format for use in Filter Pills + 'placeholder' => 'Enter Date', // A placeholder value + ]) + ]; +} +``` diff --git a/docs/filter-types/filters-daterange.md b/docs/filter-types/filters-daterange.md index db2eb323b..5e340b157 100644 --- a/docs/filter-types/filters-daterange.md +++ b/docs/filter-types/filters-daterange.md @@ -66,6 +66,52 @@ A full list of options is below, please see the Flatpickr documentation for refe | time_24hr | Boolean | false | Displays time picker in 24 hour mode without AM/PM selection when enabled. | | weekNumbers | Boolean | false | Enables display of week numbers in calendar. | +## setFilterDefaultValue + +You may use this to set a default value for the filter that will be applied on first load (but may be cleared by the user). This should be an array: + +``` + DateRangeFilter::make('EMail Verified Range') + ->setFilterDefaultValue(['minDate' => '2024-05-05', 'maxDate' => '2024-06-06']) +``` +or +``` + DateRangeFilter::make('EMail Verified Range') + ->setFilterDefaultValue(['min' => '2024-05-05', 'max' => '2024-06-06']) +``` +or +``` + DateRangeFilter::make('EMail Verified Range') + ->setFilterDefaultValue(['2024-05-05', '2024-06-06']) +``` + +## setPillsLocale +DateRange Filters also support the setPillsLocale method, which allows you to set a locale for use in generating the Filter Pills values +```php +public function filters(): array +{ + return [ + DateRangeFilter::make('EMail Verified Range') + ->setPillsLocale('fr ') // Use French localisation for the Filter Pills values + ->config([ + 'allowInput' => true, // Allow manual input of dates + 'altFormat' => 'F j, Y', // Date format that will be displayed once selected + 'ariaDateFormat' => 'F j, Y', // An aria-friendly date format + 'dateFormat' => 'Y-m-d', // Date format that will be received by the filter + 'earliestDate' => '2020-01-01', // The earliest acceptable date + 'latestDate' => '2023-08-01', // The latest acceptable date + 'placeholder' => 'Enter Date Range', // A placeholder value + 'locale' => 'en', + ]) + ->setFilterPillValues([0 => 'minDate', 1 => 'maxDate']) // The values that will be displayed for the Min/Max Date Values + ->filter(function (Builder $builder, array $dateRange) { // Expects an array. + $builder + ->whereDate('users.email_verified_at', '>=', $dateRange['minDate']) // minDate is the start date selected + ->whereDate('users.email_verified_at', '<=', $dateRange['maxDate']); // maxDate is the end date selected + }), + ]; +} +``` ## Configuration By default, this filter will inject the Flatpickr JS Library and CSS. However, you can customise this behaviour using the configuration file. diff --git a/docs/filter-types/filters-datetime.md b/docs/filter-types/filters-datetime.md index 64c14eca4..eafc19bca 100644 --- a/docs/filter-types/filters-datetime.md +++ b/docs/filter-types/filters-datetime.md @@ -33,6 +33,7 @@ public function filters(): array } ``` +## setFilterDefaultValue DateTime filters also support the setFilterDefaultValue() method, which must be a valid datetime in the "Y-m-dTH:i" format. This will apply as a default until removed. ```php public function filters(): array @@ -47,6 +48,23 @@ public function filters(): array ->setFilterDefaultValue('2023-07-07T06:27') ]; } +``` + +## setPillsLocale +DateTime Filters also support the setPillsLocale method, which allows you to set a locale for use in generating the Filter Pills values +```php +public function filters(): array +{ + return [ + DateTimeFilter::make('Verified From') + ->setPillsLocale('fr ') // Use French localisation for the Filter Pills values + ->config([ + 'min' => '2020-01-01', // Earliest Acceptable Date + 'max' => '2021-12-31', // Latest Acceptable Date + 'pillFormat' => 'd M Y - H:i', // Format for use in Filter Pills + 'placeholder' => 'Enter Date', // A placeholder value + ]) + ]; +} ``` - diff --git a/docs/misc/lifecycle-hooks.md b/docs/misc/lifecycle-hooks.md index 05f490ae8..1e05569a9 100644 --- a/docs/misc/lifecycle-hooks.md +++ b/docs/misc/lifecycle-hooks.md @@ -22,6 +22,24 @@ This is called immediately after the Columns are set up ## rowsRetrieved This is called immediately after the query is executed, and is passed the result from the executed query. +## searchUpdated +This is called whenever the search is updated, and is passed the value that has been searched for + +## filterApplying +This is called whenever a Filter is applying + +## filterReset +This is called whenever a Filter is reset + +## filterSet +This is called whenever a Filter is set + +## filterUpdated +This is called whenever a Filter is updated/used + +## filterRemoved +This is called whenever a Filter is removed from the table + ## Use in Traits To use these in a trait, allowing you to easily set defaults across multiple tables, you should ensure that you append the Lifecycle Hook with your trait name, e.g. diff --git a/src/Commands/MakeCommand.php b/src/Commands/MakeCommand.php index 8338a1216..97120c800 100644 --- a/src/Commands/MakeCommand.php +++ b/src/Commands/MakeCommand.php @@ -63,7 +63,7 @@ public function handle(): void $this->argument('name') ); - $livewireMakeCommand = new LivewireMakeCommand(); + $livewireMakeCommand = new LivewireMakeCommand; if ($livewireMakeCommand->isReservedClassName($name = $this->parser->className())) { $this->line("Class is reserved: {$name}"); @@ -184,7 +184,7 @@ private function getClassesList(string $file): array */ private function generateColumns(string $modelName): string { - $model = new $modelName(); + $model = new $modelName; if ($model instanceof Model === false) { throw new \Exception('Invalid model given.'); diff --git a/src/Traits/Helpers/FilterHelpers.php b/src/Traits/Helpers/FilterHelpers.php index d4c043531..32f3aa470 100644 --- a/src/Traits/Helpers/FilterHelpers.php +++ b/src/Traits/Helpers/FilterHelpers.php @@ -135,6 +135,10 @@ public function getFilterByKey(string $key) public function setFilter(string $filterKey, mixed $value): void { $this->appliedFilters[$filterKey] = $this->filterComponents[$filterKey] = $value; + + $this->callHook('filterSet', ['filter' => $filterKey, 'value' => $value]); + $this->callTraitHook('filterSet', ['filter' => $filterKey, 'value' => $value]); + } public function selectAllFilterOptions(string $filterKey): void @@ -238,6 +242,8 @@ public function resetFilter($filter): void if (! $filter instanceof Filter) { $filter = $this->getFilterByKey($filter); } + $this->callHook('filterReset', ['filter' => $filter->getKey()]); + $this->callTraitHook('filterReset', ['filter' => $filter->getKey()]); $this->setFilter($filter->getKey(), $filter->getDefaultValue()); } diff --git a/src/Traits/WithFilters.php b/src/Traits/WithFilters.php index 981d0e855..b5519cca8 100644 --- a/src/Traits/WithFilters.php +++ b/src/Traits/WithFilters.php @@ -61,6 +61,9 @@ public function applyFilters(): Builder continue; } + $this->callHook('filterApplying', ['filter' => $filter->getKey(), 'value' => $value]); + $this->callTraitHook('filterApplying', ['filter' => $filter->getKey(), 'value' => $value]); + ($filter->getFilterCallback())($this->getBuilder(), $value); } } @@ -84,7 +87,14 @@ public function updatedFilterComponents(string|array|null $value, string $filter $filter = $this->getFilterByKey($filterName); if ($filter && $filter->isEmpty($value)) { + $this->callHook('filterRemoved', ['filter' => $filter->getKey()]); + $this->callTraitHook('filterRemoved', ['filter' => $filter->getKey()]); + $this->resetFilter($filterName); + } elseif ($filter) { + $this->callHook('filterUpdated', ['filter' => $filter->getKey(), 'value' => $value]); + $this->callTraitHook('filterUpdated', ['filter' => $filter->getKey(), 'value' => $value]); + } } } diff --git a/src/Traits/WithSearch.php b/src/Traits/WithSearch.php index 5d90a991f..4535a9cdc 100644 --- a/src/Traits/WithSearch.php +++ b/src/Traits/WithSearch.php @@ -50,6 +50,9 @@ public function applySearch(): Builder if ($this->searchIsEnabled() && $this->hasSearch()) { $searchableColumns = $this->getSearchableColumns(); + $this->callHook('searchUpdated', ['value' => $this->getSearch()]); + $this->callTraitHook('searchUpdated', ['value' => $this->getSearch()]); + if ($searchableColumns->count()) { $this->setBuilder($this->getBuilder()->where(function ($query) use ($searchableColumns) { foreach ($searchableColumns as $index => $column) { diff --git a/src/Views/Filters/DateFilter.php b/src/Views/Filters/DateFilter.php index 6679a86aa..f6fe92391 100644 --- a/src/Views/Filters/DateFilter.php +++ b/src/Views/Filters/DateFilter.php @@ -2,14 +2,14 @@ namespace Rappasoft\LaravelLivewireTables\Views\Filters; -use DateTime; use Rappasoft\LaravelLivewireTables\Views\Filter; use Rappasoft\LaravelLivewireTables\Views\Traits\Core\HasWireables; -use Rappasoft\LaravelLivewireTables\Views\Traits\Filters\{HasConfig, IsStringFilter}; +use Rappasoft\LaravelLivewireTables\Views\Traits\Filters\{HandlesDates, HasConfig, IsStringFilter}; class DateFilter extends Filter { - use HasConfig, + use HandlesDates, + HasConfig, IsStringFilter; use HasWireables; @@ -21,17 +21,16 @@ class DateFilter extends Filter public function validate(string $value): string|bool { - if (DateTime::createFromFormat('Y-m-d', $value) === false) { - return false; - } + $this->setInputDateFormat('Y-m-d')->setOutputDateFormat($this->getConfig('pillFormat') ?? 'Y-m-d'); + $carbonDate = $this->createCarbonDate($value); - return $value; + return ($carbonDate === false) ? false : $carbonDate->format('Y-m-d'); } public function getFilterPillValue($value): string|array|null { if ($this->validate($value)) { - return DateTime::createFromFormat('Y-m-d', $value)->format($this->getConfig('pillFormat')); + return $this->outputTranslatedDate($this->createCarbonDate($value)); } return null; diff --git a/src/Views/Filters/DateRangeFilter.php b/src/Views/Filters/DateRangeFilter.php index 9e95265b1..dac3192da 100644 --- a/src/Views/Filters/DateRangeFilter.php +++ b/src/Views/Filters/DateRangeFilter.php @@ -2,13 +2,16 @@ namespace Rappasoft\LaravelLivewireTables\Views\Filters; +use Carbon\Carbon; +use Illuminate\Support\Facades\Validator; use Rappasoft\LaravelLivewireTables\Views\Filter; use Rappasoft\LaravelLivewireTables\Views\Traits\Core\HasWireables; -use Rappasoft\LaravelLivewireTables\Views\Traits\Filters\{HasConfig,HasOptions}; +use Rappasoft\LaravelLivewireTables\Views\Traits\Filters\{HandlesDates, HasConfig,HasOptions}; class DateRangeFilter extends Filter { - use HasOptions, + use HandlesDates, + HasOptions, HasConfig; use HasWireables; @@ -29,6 +32,8 @@ public function validate(array|string $values): array|bool { $this->getOptions(); $this->getConfigs(); + $this->setInputDateFormat($this->getConfig('dateFormat'))->setOutputDateFormat($this->getConfig('ariaDateFormat')); + $dateFormat = $this->getConfigs()['dateFormat']; $returnedValues = ['minDate' => '', 'maxDate' => '']; if (is_array($values)) { @@ -55,19 +60,17 @@ public function validate(array|string $values): array|bool return false; } - $dateFormat = $this->getConfigs()['dateFormat']; - - $validator = \Illuminate\Support\Facades\Validator::make($returnedValues, [ + $validator = Validator::make($returnedValues, [ 'minDate' => 'required|date_format:'.$dateFormat, 'maxDate' => 'required|date_format:'.$dateFormat, ]); if ($validator->fails()) { return false; } - $startDate = \Carbon\Carbon::createFromFormat($dateFormat, $returnedValues['minDate']); - $endDate = \Carbon\Carbon::createFromFormat($dateFormat, $returnedValues['maxDate']); + $startDate = $this->createCarbonDate($returnedValues['minDate']); + $endDate = $this->createCarbonDate($returnedValues['maxDate']); - if (! ($startDate instanceof \Carbon\Carbon) || ! ($endDate instanceof \Carbon\Carbon)) { + if (! ($startDate instanceof Carbon) || ! ($endDate instanceof Carbon)) { return false; } if ($startDate->gt($endDate)) { @@ -79,20 +82,20 @@ public function validate(array|string $values): array|bool if ($earliestDateString != '' && ! is_null($earliestDateString) && $latestDateString != '' && ! is_null($latestDateString)) { $dateLimits = ['earliest' => $earliestDateString, 'latest' => $latestDateString]; - $earlyLateValidator = \Illuminate\Support\Facades\Validator::make($dateLimits, [ + $earlyLateValidator = Validator::make($dateLimits, [ 'earliest' => 'date_format:'.$dateFormat, 'latest' => 'date_format:'.$dateFormat, ]); if (! $earlyLateValidator->fails()) { - $earliestDate = \Carbon\Carbon::createFromFormat($dateFormat, $earliestDateString); - $latestDate = \Carbon\Carbon::createFromFormat($dateFormat, $latestDateString); + $earliestDate = $this->createCarbonDate($earliestDateString); + $latestDate = $this->createCarbonDate($latestDateString); - if ($earliestDate instanceof \Carbon\Carbon) { + if ($earliestDate instanceof Carbon) { if ($startDate->lt($earliestDate)) { return false; } } - if ($latestDate instanceof \Carbon\Carbon) { + if ($latestDate instanceof Carbon) { if ($endDate->gt($latestDate)) { return false; } @@ -108,22 +111,61 @@ public function getDefaultValue(): array return []; } + public function getFilterDefaultValue(): array + { + return $this->filterDefaultValue ?? []; + } + + public function hasFilterDefaultValue(): bool + { + return ! is_null($this->filterDefaultValue); + } + + public function setFilterDefaultValue($value): self + { + if (is_array($value)) { + $minDate = ''; + $maxDate = ''; + + if (array_key_exists('minDate', $value)) { + $minDate = $value['minDate']; + } elseif (array_key_exists('min', $value)) { + $minDate = $value['min']; + } elseif (array_key_exists(0, $value)) { + $minDate = $value[0]; + } + + if (array_key_exists('maxDate', $value)) { + $maxDate = $value['maxDate']; + } elseif (array_key_exists('max', $value)) { + $maxDate = $value['max']; + } elseif (array_key_exists(1, $value)) { + $maxDate = $value[1]; + } + $this->filterDefaultValue = ['minDate' => $minDate, 'maxDate' => $maxDate]; + } else { + $this->filterDefaultValue = ['minDate' => $value, 'maxDate' => $value]; + } + + return $this; + } + public function getFilterPillValue($value): string|array|null { $validatedValue = $this->validate($value); if (is_array($validatedValue)) { - $dateFormat = $this->getConfig('dateFormat'); - $ariaDateFormat = $this->getConfig('ariaDateFormat'); - - $minDateCarbon = \Carbon\Carbon::createFromFormat($dateFormat, $validatedValue['minDate']); - $maxDateCarbon = \Carbon\Carbon::createFromFormat($dateFormat, $validatedValue['maxDate']); + if ($this->hasConfig('locale')) { + $this->setPillsLocale($this->getConfig('locale')); + } - if (($minDateCarbon instanceof \Carbon\Carbon) && $maxDateCarbon instanceof \Carbon\Carbon) { - $minDate = $minDateCarbon->format($ariaDateFormat); - $maxDate = $maxDateCarbon->format($ariaDateFormat); + $minDate = $this->createCarbonDate($validatedValue['minDate']); + $maxDate = $this->createCarbonDate($validatedValue['maxDate']); - return $minDate.' '.__('to').' '.$maxDate; + if (($minDate instanceof Carbon) && $maxDate instanceof Carbon) { + return $this->outputTranslatedDate($minDate) + .' '.__('to').' '. + $this->outputTranslatedDate($maxDate); } } diff --git a/src/Views/Filters/DateTimeFilter.php b/src/Views/Filters/DateTimeFilter.php index f46b77f20..1bb5663c6 100644 --- a/src/Views/Filters/DateTimeFilter.php +++ b/src/Views/Filters/DateTimeFilter.php @@ -2,14 +2,14 @@ namespace Rappasoft\LaravelLivewireTables\Views\Filters; -use DateTime; use Rappasoft\LaravelLivewireTables\Views\Filter; use Rappasoft\LaravelLivewireTables\Views\Traits\Core\HasWireables; -use Rappasoft\LaravelLivewireTables\Views\Traits\Filters\{HasConfig, IsStringFilter}; +use Rappasoft\LaravelLivewireTables\Views\Traits\Filters\{HandlesDates, HasConfig, IsStringFilter}; class DateTimeFilter extends Filter { - use HasConfig, + use HandlesDates, + HasConfig, IsStringFilter; use HasWireables; @@ -21,17 +21,17 @@ class DateTimeFilter extends Filter public function validate(string $value): string|bool { - if (DateTime::createFromFormat('Y-m-d\TH:i', $value) === false) { - return false; - } + $this->setInputDateFormat('Y-m-d\TH:i')->setOutputDateFormat($this->getConfig('pillFormat')); + + $carbonDate = $this->createCarbonDate($value); - return $value; + return ($carbonDate === false) ? false : $carbonDate->format('Y-m-d\TH:i'); } public function getFilterPillValue($value): string|array|null { if ($this->validate($value)) { - return DateTime::createFromFormat('Y-m-d\TH:i', $value)->format($this->getConfig('pillFormat')); + return $this->outputTranslatedDate($this->createCarbonDate($value)); } return null; diff --git a/src/Views/Traits/Filters/HandlesDates.php b/src/Views/Traits/Filters/HandlesDates.php new file mode 100644 index 000000000..4b827f468 --- /dev/null +++ b/src/Views/Traits/Filters/HandlesDates.php @@ -0,0 +1,59 @@ +carbonInstance = new Carbon; + $this->carbonInstance->setLocale($this->getPillsLocale()); + + } + + protected function createCarbonDate(string $value): Carbon|bool + { + $this->createCarbon(); + $fromFormat = false; + try { + $fromFormat = $this->carbonInstance->createFromFormat($this->inputDateFormat, $value); + } catch (\Exception $e) { + return false; + } + + return $fromFormat; + } + + protected function setInputDateFormat(string $inputDateFormat): self + { + $this->inputDateFormat = $inputDateFormat; + + return $this; + } + + protected function setOutputDateFormat(string $outputDateFormat): self + { + $this->outputDateFormat = $outputDateFormat; + + return $this; + } + + protected function outputTranslatedDate(?Carbon $carbon): string + { + if ($carbon instanceof Carbon) { + return $carbon->translatedFormat($this->outputDateFormat); + } + + return ''; + } +} diff --git a/src/Views/Traits/Filters/HasPillsLocale.php b/src/Views/Traits/Filters/HasPillsLocale.php new file mode 100644 index 000000000..a3ad474d1 --- /dev/null +++ b/src/Views/Traits/Filters/HasPillsLocale.php @@ -0,0 +1,25 @@ +pillsLocale = $pillsLocale; + + return $this; + } + + public function hasPillsLocale(): bool + { + return isset($this->pillsLocale); + } + + public function getPillsLocale(): string + { + return isset($this->pillsLocale) ? $this->pillsLocale : ($this->getConfig('locale') ?? config('app.locale', 'en')); + } +} diff --git a/tests/DataTableComponentTest.php b/tests/DataTableComponentTest.php index 5cd0115ff..992b91639 100644 --- a/tests/DataTableComponentTest.php +++ b/tests/DataTableComponentTest.php @@ -55,7 +55,7 @@ public function test_default_fingerprint_will_always_be_the_same_for_same_datata public function test_default_datatable_fingerprints_will_be_different_for_each_table(): void { - $mockTable = new class() extends PetsTable {}; + $mockTable = new class extends PetsTable {}; $this->assertNotSame($this->basicTable->getDataTableFingerprint(), $mockTable->getDataTableFingerprint()); } @@ -64,7 +64,7 @@ public function test_default_fingerprint_will_be_url_friendy(): void { $mocks = []; for ($i = 0; $i < 9; $i++) { - $mocks[$i] = new class() extends PetsTable {}; + $mocks[$i] = new class extends PetsTable {}; $this->assertFalse(filter_var('http://'.$mocks[$i]->getDataTableFingerprint().'.dev', FILTER_VALIDATE_URL) === false); } // control @@ -74,7 +74,7 @@ public function test_default_fingerprint_will_be_url_friendy(): void public function test_minimum_one_column_expected(): void { $this->expectException(\Rappasoft\LaravelLivewireTables\Exceptions\NoColumnsException::class); - $table = new NoColumnsTable(); + $table = new NoColumnsTable; $table->boot(); $table->bootedComponentUtilities(); $table->bootedWithData(); diff --git a/tests/TestCase.php b/tests/TestCase.php index 2a9a2479a..7a6b22366 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -36,8 +36,8 @@ protected function setUp(): void if (! Breed::where('id', 1)->get()) { include_once __DIR__.'/../database/migrations/create_test_tables.php.stub'; - (new \CreateTestTables())->down(); - (new \CreateTestTables())->up(); + (new \CreateTestTables)->down(); + (new \CreateTestTables)->up(); Species::insert([ ['id' => 1, 'name' => 'Cat'], @@ -88,7 +88,7 @@ protected function setUp(): void protected function setupBasicTable() { $view = view('livewire-tables::datatable'); - $this->basicTable = new PetsTable(); + $this->basicTable = new PetsTable; $this->basicTable->boot(); $this->basicTable->bootedComponentUtilities(); $this->basicTable->bootedWithData(); @@ -104,7 +104,7 @@ protected function setupBasicTable() protected function setupSpeciesTable() { $view = view('livewire-tables::datatable'); - $this->speciesTable = new SpeciesTable(); + $this->speciesTable = new SpeciesTable; $this->speciesTable->boot(); $this->speciesTable->bootedComponentUtilities(); $this->speciesTable->bootedWithData(); @@ -121,7 +121,7 @@ protected function setupUnpaginatedTable() { $view = view('livewire-tables::datatable'); - $this->unpaginatedTable = new PetsTableUnpaginated(); + $this->unpaginatedTable = new PetsTableUnpaginated; $this->unpaginatedTable->boot(); $this->unpaginatedTable->bootedComponentUtilities(); $this->unpaginatedTable->bootedWithData(); diff --git a/tests/Traits/WithMountTest.php b/tests/Traits/WithMountTest.php index b2a06e988..9452fb87d 100644 --- a/tests/Traits/WithMountTest.php +++ b/tests/Traits/WithMountTest.php @@ -11,7 +11,7 @@ public function test_mounttable_gets_correct_first_item(): void { $view = view('livewire-tables::datatable'); - $table = new PetsTableMount(); + $table = new PetsTableMount; $table->boot(); $table->mount(102); $table->bootedComponentUtilities(); @@ -29,7 +29,7 @@ public function test_mounttable_gets_correct_first_item(): void $this->assertNotSame(strtoupper($rows->first()->name), 'CHICO'); $this->assertNotSame(strtoupper($rows->first()->name), 'CARTMAN'); - $table2 = new PetsTableMount(); + $table2 = new PetsTableMount; $table2->boot(); $table2->mount(202); $table2->bootedComponentUtilities(); @@ -46,7 +46,7 @@ public function test_mounttable_gets_correct_first_item(): void $this->assertNotSame(strtoupper($rows2->first()->name), 'CARTMAN'); $this->assertNotSame(strtoupper($rows2->first()->name), 'MAY'); - $table3 = new PetsTableMount(); + $table3 = new PetsTableMount; $table3->boot(); $table3->mount(); $table3->bootedComponentUtilities(); diff --git a/tests/Views/Filters/DateFilterTest.php b/tests/Views/Filters/DateFilterTest.php index dec55d412..3fa33d32c 100644 --- a/tests/Views/Filters/DateFilterTest.php +++ b/tests/Views/Filters/DateFilterTest.php @@ -175,4 +175,23 @@ public function test_can_set_text_filter_wireable_live(): void $this->assertSame('wire:model.live.debounce.500ms=filterComponents.active', self::$filterInstance->getWireMethod('filterComponents.'.self::$filterInstance->getKey())); } + + public function test_check_if_has_locale(): void + { + $this->assertFalse(self::$filterInstance->hasPillsLocale()); + self::$filterInstance->setPillsLocale('fr'); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + } + + public function test_check_if_can_get_locale(): void + { + $this->assertFalse(self::$filterInstance->hasPillsLocale()); + $this->assertSame('en', self::$filterInstance->getPillsLocale()); + self::$filterInstance->setPillsLocale('fr'); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + $this->assertSame('fr', self::$filterInstance->getPillsLocale()); + self::$filterInstance->setPillsLocale('de'); + $this->assertSame('de', self::$filterInstance->getPillsLocale()); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + } } diff --git a/tests/Views/Filters/DateRangeFilterTest.php b/tests/Views/Filters/DateRangeFilterTest.php index bef64b240..ed4131746 100644 --- a/tests/Views/Filters/DateRangeFilterTest.php +++ b/tests/Views/Filters/DateRangeFilterTest.php @@ -7,24 +7,24 @@ final class DateRangeFilterTest extends FilterTestCase { + protected function setUp(): void + { + parent::setUp(); + self::$filterInstance = DateRangeFilter::make('Active'); + } + public function test_can_get_filter_name(): void { - $filter = DateRangeFilter::make('Active'); - // Matches - $this->assertSame('Active', $filter->getName()); + $this->assertSame('Active', self::$filterInstance->getName()); } public function test_can_get_filter_key(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertSame('active', $filter->getKey()); + $this->assertSame('active', self::$filterInstance->getKey()); } public function test_can_get_filter_configs(): void { - $filter = DateRangeFilter::make('Active'); - $defaultConfig = [ 'allowInput' => true, 'altFormat' => 'F j, Y', @@ -35,17 +35,15 @@ public function test_can_get_filter_configs(): void 'locale' => 'en', ]; - $this->assertSame($defaultConfig, $filter->getConfigs()); + $this->assertSame($defaultConfig, self::$filterInstance->getConfigs()); - $filter->config(['foo' => 'bar']); + self::$filterInstance->config(['foo' => 'bar']); - $this->assertSame(array_merge($defaultConfig, ['foo' => 'bar']), $filter->getConfigs()); + $this->assertSame(array_merge($defaultConfig, ['foo' => 'bar']), self::$filterInstance->getConfigs()); } public function test_get_a_single_filter_config(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame([ 'allowInput' => true, 'altFormat' => 'F j, Y', @@ -54,11 +52,11 @@ public function test_get_a_single_filter_config(): void 'earliestDate' => null, 'latestDate' => null, 'locale' => 'en', - ], $filter->getConfigs()); + ], self::$filterInstance->getConfigs()); - $filter->config(['foo' => 'bar']); + self::$filterInstance->config(['foo' => 'bar']); - $this->assertSame('bar', $filter->getConfig('foo')); + $this->assertSame('bar', self::$filterInstance->getConfig('foo')); $this->assertSame([ 'allowInput' => true, 'altFormat' => 'F j, Y', @@ -67,14 +65,15 @@ public function test_get_a_single_filter_config(): void 'earliestDate' => null, 'latestDate' => null, 'locale' => 'en', - 'foo' => 'bar'], $filter->getConfigs()); + 'foo' => 'bar', + ], + self::$filterInstance->getConfigs() + ); } public function test_can_change_locale(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame([ 'allowInput' => true, 'altFormat' => 'F j, Y', @@ -83,9 +82,11 @@ public function test_can_change_locale(): void 'earliestDate' => null, 'latestDate' => null, 'locale' => 'en', - ], $filter->getConfigs()); + ], + self::$filterInstance->getConfigs() + ); - $filter->config(['locale' => 'fr']); + self::$filterInstance->config(['locale' => 'fr']); $this->assertSame([ 'allowInput' => true, @@ -96,161 +97,154 @@ public function test_can_change_locale(): void 'latestDate' => null, 'locale' => 'fr', ], - $filter->getConfigs() + self::$filterInstance->getConfigs() ); } public function test_can_get_filter_options(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame([], $filter->getOptions()); + $this->assertSame([], self::$filterInstance->getOptions()); - $filter->options(['foo' => 'bar']); + self::$filterInstance->options(['foo' => 'bar']); + $this->assertSame(['foo' => 'bar'], self::$filterInstance->getOptions()); } public function test_can_get_if_empty(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertTrue($filter->isEmpty('')); - $this->assertFalse($filter->isEmpty(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'])); - $this->assertTrue($filter->isEmpty(['minDate' => '2020-01-01', 'maxDate' => null])); - $this->assertTrue($filter->isEmpty(['minDate' => null, 'maxDate' => '2020-02-02'])); - - $this->assertTrue($filter->isEmpty(['minDate' => '2020-01-01'])); - $this->assertFalse($filter->isEmpty([0 => '2020-01-01', 1 => '2020-02-02'])); - $this->assertFalse($filter->isEmpty(['2020-01-01', '2020-02-02'])); - $this->assertTrue($filter->isEmpty('test')); + $this->assertTrue(self::$filterInstance->isEmpty('')); + $this->assertFalse(self::$filterInstance->isEmpty(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->isEmpty([0 => '2020-01-01', 1 => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->isEmpty(['2020-01-01', '2020-02-02'])); + $this->assertTrue(self::$filterInstance->isEmpty(['minDate' => '2020-01-01', 'maxDate' => null])); + $this->assertTrue(self::$filterInstance->isEmpty(['minDate' => null, 'maxDate' => '2020-02-02'])); + $this->assertTrue(self::$filterInstance->isEmpty(['minDate' => '2020-01-01'])); + $this->assertTrue(self::$filterInstance->isEmpty('test')); } public function test_can_check_validation_accepts_valid_values_array(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'], $filter->validate(['2020-01-01', '2020-02-02'])); + $this->assertSame( + ['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'], + self::$filterInstance->validate(['2020-01-01', '2020-02-02']) + ); } public function test_can_check_validation_accepts_valid_values_string(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'], $filter->validate('2020-01-01 to 2020-02-02')); - $this->assertFalse($filter->validate('2020-01-01 to ')); - $this->assertFalse($filter->validate(' to 2020-01-01')); + $this->assertSame( + ['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'], + self::$filterInstance->validate('2020-01-01 to 2020-02-02') + ); + $this->assertFalse(self::$filterInstance->validate('2020-01-01 to ')); + $this->assertFalse(self::$filterInstance->validate(' to 2020-01-01')); } public function test_can_check_validation_rejects_invalid_values(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'], $filter->validate(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-02-30'])); - $this->assertFalse($filter->validate(['minDate' => '2020-02-30', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => 'test', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-13-22'])); - $this->assertFalse($filter->validate(['minDate' => '2020-13-21', 'maxDate' => '2020-12-22'])); - $this->assertFalse($filter->validate(['minDate' => '12020-13-21', 'maxDate' => '2020-12-22'])); - $this->assertFalse($filter->validate(['minDate' => '2020-02-22', 'maxDate' => '2020-02-21'])); + $this->assertSame(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'], self::$filterInstance->validate(['minDate' => '2020-01-01', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-02-30'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-02-30', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => 'test', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-13-22'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-13-21', 'maxDate' => '2020-12-22'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '12020-13-21', 'maxDate' => '2020-12-22'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-02-22', 'maxDate' => '2020-02-21'])); } public function test_can_check_validation_rejects_invalid_earliest_latest_values(): void { - $filter = DateRangeFilter::make('Active')->options(['earliestDate' => '20214-0111-01']); - $this->assertFalse($filter->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-02-30'])); + self::$filterInstance->options(['earliestDate' => '20214-0111-01']); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-02-30'])); } public function test_can_check_validation_rejects_invalid_latest_latest_values(): void { - $filter = DateRangeFilter::make('Active')->config(['latestDate' => '2191-111-11']); - $this->assertFalse($filter->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-02-30'])); + self::$filterInstance->config(['latestDate' => '2191-111-11']); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-02-21', 'maxDate' => '2020-02-30'])); } public function test_can_check_validation_rejects_values_before_earliest_or_after_latest_with_dateformat(): void { - $filter = DateRangeFilter::make('Active')->config(['dateFormat' => 'Y-m-d', 'earliestDate' => '2020-01-01', 'latestDate' => '2020-10-10']); - $this->assertSame(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'], $filter->validate(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-04-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2019-01-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-01-05', 'maxDate' => '2021-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2021-01-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2019-01-05', 'maxDate' => '2019-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); + self::$filterInstance->config(['dateFormat' => 'Y-m-d', 'earliestDate' => '2020-01-01', 'latestDate' => '2020-10-10']); + $this->assertSame(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'], self::$filterInstance->validate(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-04-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2019-01-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-01-05', 'maxDate' => '2021-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2021-01-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2019-01-05', 'maxDate' => '2019-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); } public function test_can_check_validation_rejects_values_before_earliest_or_after_latest_default_dateformat(): void { - $filter = DateRangeFilter::make('Active')->config(['earliestDate' => '2020-01-01', 'latestDate' => '2020-10-10']); - $this->assertSame(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'], $filter->validate(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-04-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2019-01-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-01-05', 'maxDate' => '2021-02-02'])); - - $this->assertFalse($filter->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2021-01-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2019-01-05', 'maxDate' => '2019-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); + self::$filterInstance->config(['earliestDate' => '2020-01-01', 'latestDate' => '2020-10-10']); + $this->assertSame(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'], self::$filterInstance->validate(['minDate' => '2020-01-02', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-04-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2019-01-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-01-05', 'maxDate' => '2021-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2021-01-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2019-01-05', 'maxDate' => '2019-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2021-01-05', 'maxDate' => '2021-02-02'])); } public function test_can_check_validation_rejects_values_2_dateformat(): void { - $filter = DateRangeFilter::make('Active')->config(['earliestDate' => '2020-01-01', 'latestDate' => '2020-10-10']); - $this->assertSame(['minDate' => '2020-01-02', 'maxDate' => '2020-03-02'], $filter->validate(['minDate' => '2020-01-02', 'maxDate' => '2020-03-02'])); - $this->assertFalse($filter->validate(['minDate' => '2020-01-05', 'maxDate' => '2020-02-30'])); + self::$filterInstance->config(['earliestDate' => '2020-01-01', 'latestDate' => '2020-10-10']); + $this->assertSame(['minDate' => '2020-01-02', 'maxDate' => '2020-03-02'], self::$filterInstance->validate(['minDate' => '2020-01-02', 'maxDate' => '2020-03-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-01-05', 'maxDate' => '2020-02-30'])); } public function test_can_check_date_format_can_be_changed(): void { - $filter = DateRangeFilter::make('Active')->config(['dateFormat' => 'd-m-Y', 'earliestDate' => '01-01-2020', 'latestDate' => '12-10-2020']); - $this->assertSame(['minDate' => '02-01-2020', 'maxDate' => '02-03-2020'], $filter->validate(['minDate' => '02-01-2020', 'maxDate' => '02-03-2020'])); - $this->assertFalse($filter->validate(['minDate' => '2020-04-05', 'maxDate' => '2020-02-02'])); - $this->assertFalse($filter->validate(['minDate' => '10-12-2020', 'maxDate' => '12-12-2020'])); + self::$filterInstance->config(['dateFormat' => 'd-m-Y', 'earliestDate' => '01-01-2020', 'latestDate' => '12-10-2020']); + $this->assertSame(['minDate' => '02-01-2020', 'maxDate' => '02-03-2020'], self::$filterInstance->validate(['minDate' => '02-01-2020', 'maxDate' => '02-03-2020'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '2020-04-05', 'maxDate' => '2020-02-02'])); + $this->assertFalse(self::$filterInstance->validate(['minDate' => '10-12-2020', 'maxDate' => '12-12-2020'])); } public function test_filter_pill_values_can_be_set_for_daterange(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertEquals('February 2, 2020 to February 5, 2020', $filter->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2020-02-05'])); - - $this->assertEquals('February 2, 2010 to February 5, 2020', $filter->getFilterPillValue(['minDate' => '2010-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('February 2, 2020 to February 5, 2020', self::$filterInstance->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('February 2, 2010 to February 5, 2020', self::$filterInstance->getFilterPillValue(['minDate' => '2010-02-02', 'maxDate' => '2020-02-05'])); } public function test_filter_pill_values_cannot_be_set_for_invalid_dates(): void { - $filter = DateRangeFilter::make('Active')->options(['dateFormat' => 'd-m-Y', 'earliestDate' => '01-01-2020', 'latestDate' => '1d-10-2020']); + self::$filterInstance->options(['dateFormat' => 'd-m-Y', 'earliestDate' => '01-01-2020', 'latestDate' => '1d-10-2020']); - $this->assertEquals('', $filter->getFilterPillValue(['minDate' => '20q0-02-02', 'maxDate' => '2020-02-05'])); - $this->assertEquals('', $filter->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2020-13-05'])); - $this->assertEquals('February 2, 2010 to February 5, 2020', $filter->getFilterPillValue(['minDate' => '2010-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('', self::$filterInstance->getFilterPillValue(['minDate' => '20q0-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('', self::$filterInstance->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2020-13-05'])); + $this->assertEquals('February 2, 2010 to February 5, 2020', self::$filterInstance->getFilterPillValue(['minDate' => '2010-02-02', 'maxDate' => '2020-02-05'])); } public function test_filter_pill_values_can_be_set_for_daterange_limits(): void { - $filter = DateRangeFilter::make('Active')->options(['ariaDateFormat' => 'F j, Y', 'earliestDate' => '2020-01-01', 'latestDate' => '2022-01-01']); + self::$filterInstance->options(['ariaDateFormat' => 'F j, Y', 'earliestDate' => '2020-01-01', 'latestDate' => '2022-01-01']); - $this->assertEquals('February 2, 2020 to February 5, 2020', $filter->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2020-02-05'])); - $this->assertEquals('February 2, 2010 to February 5, 2020', $filter->getFilterPillValue(['minDate' => '2010-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('February 2, 2020 to February 5, 2020', self::$filterInstance->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('February 2, 2010 to February 5, 2020', self::$filterInstance->getFilterPillValue(['minDate' => '2010-02-02', 'maxDate' => '2020-02-05'])); } public function test_filter_pill_values_can_be_set_for_daterange_customformat(): void { - $filter = DateRangeFilter::make('Active')->config(['ariaDateFormat' => 'Y', 'latestDate' => '2022-01-01']); + self::$filterInstance->config(['ariaDateFormat' => 'Y', 'latestDate' => '2022-01-01']); - $this->assertEquals('2020 to 2021', $filter->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2021-02-05'])); - $this->assertEquals('', $filter->getFilterPillValue(['minDate' => '20220-02-02', 'maxDate' => '2020-02-05'])); + $this->assertEquals('2020 to 2021', self::$filterInstance->getFilterPillValue(['minDate' => '2020-02-02', 'maxDate' => '2021-02-05'])); + $this->assertEquals('', self::$filterInstance->getFilterPillValue(['minDate' => '20220-02-02', 'maxDate' => '2020-02-05'])); } public function test_can_get_filter_keys(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertSame(['minDate' => '', 'maxDate' => ''], $filter->getKeys()); + $this->assertSame(['minDate' => '', 'maxDate' => ''], self::$filterInstance->getKeys()); } public function test_can_get_filter_default_value(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertSame([], $filter->getDefaultValue()); + $this->assertSame([], self::$filterInstance->getDefaultValue()); } public function test_can_get_filter_callback(): void @@ -271,14 +265,11 @@ public function test_can_get_filter_callback(): void public function test_can_get_filter_pill_title(): void { - $filter = DateRangeFilter::make('Active'); + $this->assertSame('Active', self::$filterInstance->getFilterPillTitle()); - $this->assertSame('Active', $filter->getFilterPillTitle()); - - $filter = DateRangeFilter::make('Active') - ->setFilterPillTitle('User Status'); + self::$filterInstance->setFilterPillTitle('User Status'); - $this->assertSame('User Status', $filter->getFilterPillTitle()); + $this->assertSame('User Status', self::$filterInstance->getFilterPillTitle()); } /* @@ -313,89 +304,130 @@ public function test_can_get_nested_filter_pill_value(): void public function test_can_check_if_filter_has_configs(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertTrue($filter->hasConfigs()); + $this->assertTrue(self::$filterInstance->hasConfigs()); - $filter->config(['foo' => 'bar']); + self::$filterInstance->config(['foo' => 'bar']); - $this->assertTrue($filter->hasConfigs()); + $this->assertTrue(self::$filterInstance->hasConfigs()); } public function test_can_check_filter_config_by_name(): void { - $filter = DateRangeFilter::make('Active') - ->config(['foo' => 'bar']); + self::$filterInstance->config(['foo' => 'bar']); - $this->assertTrue($filter->hasConfig('foo')); - $this->assertFalse($filter->hasConfig('bar')); + $this->assertTrue(self::$filterInstance->hasConfig('foo')); + $this->assertFalse(self::$filterInstance->hasConfig('bar')); } public function test_can_check_if_filter_is_hidden_from_menus(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertFalse($filter->isHiddenFromMenus()); - $this->assertTrue($filter->isVisibleInMenus()); + $this->assertFalse(self::$filterInstance->isHiddenFromMenus()); + $this->assertTrue(self::$filterInstance->isVisibleInMenus()); - $filter->hiddenFromMenus(); + self::$filterInstance->hiddenFromMenus(); - $this->assertTrue($filter->isHiddenFromMenus()); - $this->assertFalse($filter->isVisibleInMenus()); + $this->assertTrue(self::$filterInstance->isHiddenFromMenus()); + $this->assertFalse(self::$filterInstance->isVisibleInMenus()); } public function test_can_check_if_filter_is_hidden_from_pills(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertFalse($filter->isHiddenFromPills()); - $this->assertTrue($filter->isVisibleInPills()); + $this->assertFalse(self::$filterInstance->isHiddenFromPills()); + $this->assertTrue(self::$filterInstance->isVisibleInPills()); - $filter->hiddenFromPills(); + self::$filterInstance->hiddenFromPills(); - $this->assertTrue($filter->isHiddenFromPills()); - $this->assertFalse($filter->isVisibleInPills()); + $this->assertTrue(self::$filterInstance->isHiddenFromPills()); + $this->assertFalse(self::$filterInstance->isVisibleInPills()); } public function test_can_check_if_filter_is_hidden_from_count(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertFalse($filter->isHiddenFromFilterCount()); - $this->assertTrue($filter->isVisibleInFilterCount()); + $this->assertFalse(self::$filterInstance->isHiddenFromFilterCount()); + $this->assertTrue(self::$filterInstance->isVisibleInFilterCount()); - $filter->hiddenFromFilterCount(); + self::$filterInstance->hiddenFromFilterCount(); - $this->assertTrue($filter->isHiddenFromFilterCount()); - $this->assertFalse($filter->isVisibleInFilterCount()); + $this->assertTrue(self::$filterInstance->isHiddenFromFilterCount()); + $this->assertFalse(self::$filterInstance->isVisibleInFilterCount()); } public function test_can_check_if_filter_is_reset_by_clear_button(): void { - $filter = DateRangeFilter::make('Active'); - - $this->assertTrue($filter->isResetByClearButton()); + $this->assertTrue(self::$filterInstance->isResetByClearButton()); - $filter->notResetByClearButton(); + self::$filterInstance->notResetByClearButton(); - $this->assertFalse($filter->isResetByClearButton()); + $this->assertFalse(self::$filterInstance->isResetByClearButton()); } public function test_can_get_datestring(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame('', $filter->getDateString('')); - $this->assertSame('2020-01-01 to 2020-02-02', $filter->getDateString(['2020-02-02', '2020-01-01'])); - $this->assertSame('2021-03-03 to 2021-04-04', $filter->getDateString(['minDate' => '2021-03-03', 'maxDate' => '2021-04-04'])); - $this->assertSame('2022-05-05 to 2022-06-06', $filter->getDateString('2022-05-05,to,2022-06-06')); + $this->assertSame('', self::$filterInstance->getDateString('')); + $this->assertSame('2020-01-01 to 2020-02-02', self::$filterInstance->getDateString(['2020-02-02', '2020-01-01'])); + $this->assertSame('2021-03-03 to 2021-04-04', self::$filterInstance->getDateString(['minDate' => '2021-03-03', 'maxDate' => '2021-04-04'])); + $this->assertSame('2022-05-05 to 2022-06-06', self::$filterInstance->getDateString('2022-05-05,to,2022-06-06')); } public function test_can_set_custom_filter_view(): void { - $filter = DateRangeFilter::make('Active'); - $this->assertSame('livewire-tables::components.tools.filters.date-range', $filter->getViewPath()); - $filter->setCustomView('test-custom-filter-view'); - $this->assertSame('test-custom-filter-view', $filter->getViewPath()); + $this->assertSame('livewire-tables::components.tools.filters.date-range', self::$filterInstance->getViewPath()); + self::$filterInstance->setCustomView('test-custom-filter-view'); + $this->assertSame('test-custom-filter-view', self::$filterInstance->getViewPath()); + } + + public function test_can_set_default_value_by_string(): void + { + $this->assertFalse(self::$filterInstance->hasFilterDefaultValue()); + self::$filterInstance->setFilterDefaultValue('2024-04-04'); + $this->assertTrue(self::$filterInstance->hasFilterDefaultValue()); + $this->assertSame(['minDate' => '2024-04-04', 'maxDate' => '2024-04-04'], self::$filterInstance->getFilterDefaultValue()); + + } + + public function test_can_set_default_value_by_named_array(): void + { + $this->assertFalse(self::$filterInstance->hasFilterDefaultValue()); + self::$filterInstance->setFilterDefaultValue(['minDate' => '2024-05-04', 'maxDate' => '2024-06-04']); + $this->assertTrue(self::$filterInstance->hasFilterDefaultValue()); + $this->assertSame(['minDate' => '2024-05-04', 'maxDate' => '2024-06-04'], self::$filterInstance->getFilterDefaultValue()); + + } + + public function test_can_set_default_value_by_short_named_array(): void + { + $this->assertFalse(self::$filterInstance->hasFilterDefaultValue()); + self::$filterInstance->setFilterDefaultValue(['min' => '2024-05-04', 'max' => '2024-06-04']); + $this->assertTrue(self::$filterInstance->hasFilterDefaultValue()); + $this->assertSame(['minDate' => '2024-05-04', 'maxDate' => '2024-06-04'], self::$filterInstance->getFilterDefaultValue()); + + } + + public function test_can_set_default_value_by_numbered_array(): void + { + $this->assertFalse(self::$filterInstance->hasFilterDefaultValue()); + self::$filterInstance->setFilterDefaultValue(['2024-06-04', '2024-07-04']); + $this->assertTrue(self::$filterInstance->hasFilterDefaultValue()); + $this->assertSame(['minDate' => '2024-06-04', 'maxDate' => '2024-07-04'], self::$filterInstance->getFilterDefaultValue()); + } + + public function test_check_if_has_locale(): void + { + $this->assertFalse(self::$filterInstance->hasPillsLocale()); + self::$filterInstance->setPillsLocale('fr'); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + } + + public function test_check_if_can_get_locale(): void + { + $this->assertFalse(self::$filterInstance->hasPillsLocale()); + $this->assertSame('en', self::$filterInstance->getPillsLocale()); + self::$filterInstance->setPillsLocale('fr'); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + $this->assertSame('fr', self::$filterInstance->getPillsLocale()); + self::$filterInstance->setPillsLocale('de'); + $this->assertSame('de', self::$filterInstance->getPillsLocale()); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); } } diff --git a/tests/Views/Filters/DateTimeFilterTest.php b/tests/Views/Filters/DateTimeFilterTest.php index e0597efc1..5a21e02b8 100644 --- a/tests/Views/Filters/DateTimeFilterTest.php +++ b/tests/Views/Filters/DateTimeFilterTest.php @@ -164,6 +164,24 @@ public function test_can_set_text_filter_wireable_live(): void $this->assertSame('live.debounce.500ms', self::$filterInstance->getWireableMethod()); $this->assertSame('wire:model.live.debounce.500ms=filterComponents.active', self::$filterInstance->getWireMethod('filterComponents.'.self::$filterInstance->getKey())); + } + public function test_check_if_has_locale(): void + { + $this->assertFalse(self::$filterInstance->hasPillsLocale()); + self::$filterInstance->setPillsLocale('fr'); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + } + + public function test_check_if_can_get_locale(): void + { + $this->assertFalse(self::$filterInstance->hasPillsLocale()); + $this->assertSame('en', self::$filterInstance->getPillsLocale()); + self::$filterInstance->setPillsLocale('fr'); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); + $this->assertSame('fr', self::$filterInstance->getPillsLocale()); + self::$filterInstance->setPillsLocale('de'); + $this->assertSame('de', self::$filterInstance->getPillsLocale()); + $this->assertTrue(self::$filterInstance->hasPillsLocale()); } } From ea22d63410acd2cb1254ec23e3213914695a4d21 Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Sun, 4 Aug 2024 05:03:35 +0100 Subject: [PATCH 056/338] V3.4.0 Release (#1815) ## [v3.4.0] - 2024-08-04 ### New Features - Add Helpers for TextFilters by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1812 - Change method names for TextFilters handler by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1814 - Capability to set Reorder Column TH Attributes by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1811 - Bulk Actions - Customise Select All Behaviours by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1810 ### Bug Fixes - Fix loading spinner for dark tailwind theme by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1809 ### Tweaks - Blade Minimisation & ColumnSelect Cleanup by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1806 - Adjust DateColumn with Missing Tests and Coping with DateTime Casts by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1813 ### Docs - Add reference to Bulk Actions TH styling in main styling by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1808 - Update docs for setPillsLocale by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1800 - Add note on label method for setAdditionalSelects by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1816 --- CHANGELOG.md | 19 ++ database/database.sqlite | Bin 40960 -> 0 bytes .../migrations/create_test_tables.php.stub | 9 + database/sqlite.database | Bin 40960 -> 45056 bytes docs/bulk-actions/available-methods.md | 24 +- docs/columns/available-methods.md | 21 ++ docs/datatable/styling.md | 5 +- docs/filter-types/filters-text.md | 132 +++++++- docs/reordering/available-methods.md | 16 + resources/css/bootstrap-custom.css | 31 ++ resources/css/bootstrap-custom.min.css | 1 + resources/css/laravel-livewire-tables.css | 30 +- resources/css/laravel-livewire-tables.min.css | 8 +- resources/js/laravel-livewire-tables.js | 47 ++- resources/js/laravel-livewire-tables.min.js | 2 +- .../table/collapsed-columns.blade.php | 20 +- resources/views/components/table/td.blade.php | 2 +- .../table/td/collapsed-columns.blade.php | 11 +- resources/views/components/table/th.blade.php | 9 +- .../components/table/th/reorder.blade.php | 11 +- .../table/tr/bulk-actions.blade.php | 12 +- .../table/tr/secondary-header.blade.php | 10 +- .../components/tools/filter-pills.blade.php | 2 +- .../components/tools/sorting-pills.blade.php | 4 +- .../toolbar/items/reorder-buttons.blade.php | 28 +- .../toolbar/items/search-field.blade.php | 2 +- resources/views/components/wrapper.blade.php | 4 + resources/views/datatable.blade.php | 51 +-- .../views/includes/columns/boolean.blade.php | 8 +- resources/views/includes/offline.blade.php | 2 +- .../BulkActionsConfiguration.php | 21 ++ .../Configuration/ReorderingConfiguration.php | 10 + src/Traits/Helpers/BulkActionsHelpers.php | 26 +- .../Helpers/CollapsingColumnHelpers.php | 6 + src/Traits/Helpers/ColumnSelectHelpers.php | 91 ++++- src/Traits/Helpers/FilterHelpers.php | 7 + src/Traits/Helpers/ReorderingHelpers.php | 14 + src/Traits/Helpers/SortingHelpers.php | 7 + src/Traits/Helpers/TableAttributeHelpers.php | 37 +-- src/Traits/WithBulkActions.php | 2 + src/Traits/WithColumnSelect.php | 74 +---- src/Traits/WithCustomisations.php | 14 +- src/Traits/WithReordering.php | 2 + src/Views/Columns/DateColumn.php | 30 +- src/Views/Filters/DateRangeFilter.php | 101 +++--- src/Views/Filters/TextFilter.php | 3 +- .../Traits/Filters/HandlesApplyingFilter.php | 28 ++ src/Views/Traits/Filters/HandlesFieldName.php | 27 ++ .../Traits/Filters/HandlesWildcardStrings.php | 76 +++++ .../Traits/Helpers/DateColumnHelpers.php | 13 + tests/Http/Livewire/BreedsTable.php | 71 ++++ tests/Http/Livewire/PetsTableWithOwner.php | 171 ++++++++++ tests/Models/Owner.php | 35 ++ tests/Models/Pet.php | 10 + tests/TestCase.php | 61 +++- .../BulkActionsConfigurationTest.php | 48 +++ .../ColumnSelectConfigurationTest.php | 12 + .../PaginationConfigurationTest.php | 6 + .../ReorderingConfigurationTest.php | 18 + .../Helpers/ColumnSelectHelpersTest.php | 40 +++ tests/Traits/Visuals/FilterVisualsTest.php | 1 + .../Visuals/Filters/TextFilterVisualsTest.php | 311 ++++++++++++++++++ tests/Views/Columns/AvgColumnTest.php | 6 + tests/Views/Columns/CountColumnTest.php | 6 + tests/Views/Columns/DateColumnTest.php | 40 ++- tests/Views/Columns/SumColumnTest.php | 6 + .../Views/Columns/ViewComponentColumnTest.php | 8 + tests/Views/Filters/DateRangeFilterTest.php | 3 + tests/Views/Filters/TextFilterTest.php | 20 ++ 69 files changed, 1691 insertions(+), 292 deletions(-) delete mode 100644 database/database.sqlite create mode 100644 resources/css/bootstrap-custom.css create mode 100644 resources/css/bootstrap-custom.min.css create mode 100644 src/Views/Traits/Filters/HandlesApplyingFilter.php create mode 100644 src/Views/Traits/Filters/HandlesFieldName.php create mode 100644 src/Views/Traits/Filters/HandlesWildcardStrings.php create mode 100644 tests/Http/Livewire/BreedsTable.php create mode 100644 tests/Http/Livewire/PetsTableWithOwner.php create mode 100644 tests/Models/Owner.php create mode 100644 tests/Traits/Visuals/Filters/TextFilterVisualsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0375276e7..df1740043 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [v3.4.0] - 2024-08-04 +### New Features +- Add Helpers for TextFilters by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1812 +- Change method names for TextFilters handler by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1814 +- Capability to set Reorder Column TH Attributes by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1811 +- Bulk Actions - Customise Select All Behaviours by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1810 + +### Bug Fixes +- Fix loading spinner for dark tailwind theme by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1809 + +### Tweaks +- Blade Minimisation & ColumnSelect Cleanup by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1806 +- Adjust DateColumn with Missing Tests and Coping with DateTime Casts by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1813 + +### Docs +- Add reference to Bulk Actions TH styling in main styling by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1808 +- Update docs for setPillsLocale by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1800 +- Add note on label method for setAdditionalSelects by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1816 + ## [v3.3.4] - 2024-07-27 ### New Features - Added capability to setFilterDefaultValue for a DateRangeFilter by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1796 diff --git a/database/database.sqlite b/database/database.sqlite deleted file mode 100644 index 6ab3c4998ca5a6a52b5fff19f99084a346c5b6e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 zcmeI*&2QUe90%~{v7M&w3(r{BMI}P@GO8|Gi_)}RJ0T?6uF*Daqij2XI7D9ZG&M`@ z&UU+18bU2_;>4X@IE_C5PH-L?#~sFD6PFEf0D**%5GQ_*^HRGnn>3KN!Pio1`+2oL ze*7eM9;X-HI^%@WTJ-#7JG92xKE^qF!?GA-f?i$pYE33B7_AkW^VsrUi-NscVZFVA znVmP7*c62q%AGfQzUk@j{=9pnE4^1XghK!V5P$##An?Bx*sK}pOn*QBcr3Ip)#Zu| z1NBPHou8k%IB#8?nLRUaZFS(CoC;ycT>ziwTY>z2J5dX8K1<+5}` z%k}8BTCa~-xxn+ott_nwt11`m)p}?R7ltX3YcI>3wPyR3CEJhZwrgAAl&)?EVR_97 z94fMEhfe=pe0>ilcDq-mlO#&w$`;nKNk5! z$3c}JGm0Z`ZNwfa5CDg&-|UTALwvL2%jslGh(|q9JJ|D7Ye?)k3G)l7j?B@1PG>hA4|SYlB5JiG zDX}d@Lm+A%q}|wax~f@FJm_4JA7zkV(5T5BLc zxvD32a9i3VYNJOeXZ|`*XR=wo@n)mQ#(PF{bF@Setv7&w@#M<)(uWH}+-KrX@tgRO z7BE2o0uX=z1Rwwb2tWV=5P$##An<$%qz#?*&pLj!Q`cGMq~`~+L(^H`39r_{X+CF% z>iRz=zGC8cy86E-Zqp(r2tWV=5P$##AOHafKmY;|fB*!Z34#47JiWM+yvxK-;(PI(_)^>ypNUVzb@3hr zVuAn!AOHafKmY;|fB*y_009U<;Bf*y8>bDHVVRE!Ep|jhEOVRWSr$pHo51BkWXZzu@?G}rTbYqmxI9!OPgF2<^6ZA8D*|YX~ zQCp<&QmIg&di$sf#dt@HNEihX=~>e%GOcyTOC!~OFrxY4!cTE^aZp`oAZBeTqAXsv!UY2tWV= z5P$##AOHafKmY;|c$~lieUxv%IiT;S9|=s}643VP!)D?}K;!!VuKNA|hvM$zszE#i zAOHafKmY;|fB*y_009U<00PgDK(9W?+k*piwbmXDp!Mj3X8ZepHU8fa519Ci?g0Eu z3z#4P0SG_<0uX=z1Rwwb2tWV=5O^*HI_Td8aIWbodWL|TT8f?(Fu7*XQvs&=Hw(j8 AQUCw| diff --git a/database/migrations/create_test_tables.php.stub b/database/migrations/create_test_tables.php.stub index ef7720eae..4afbd0e13 100644 --- a/database/migrations/create_test_tables.php.stub +++ b/database/migrations/create_test_tables.php.stub @@ -25,6 +25,12 @@ class CreateTestTables extends Migration $table->foreign('species_id')->references('id')->on('species'); }); + Schema::create('owners', function (Blueprint $table) { + $table->id(); + $table->string('name'); + $table->date('date_of_birth')->nullable(); + }); + Schema::create('pets', function (Blueprint $table) { $table->id(); $table->integer('sort')->default(0); @@ -34,10 +40,13 @@ class CreateTestTables extends Migration $table->string('favorite_color')->nullable(); $table->integer('species_id')->unsigned()->nullable(); $table->integer('breed_id')->unsigned()->nullable(); + $table->integer('owner_id')->unsigned()->nullable(); $table->foreign('species_id')->references('id')->on('species'); $table->foreign('breed_id')->references('id')->on('breeds'); + $table->foreign('owner_id')->references('id')->on('owners'); }); + Schema::create('veterinaries', function (Blueprint $table) { $table->id(); $table->string('name')->index(); diff --git a/database/sqlite.database b/database/sqlite.database index e3c6f68a2e4bc9e1dd1658d3f59c39973ec882ff..51e3c6632db04ef6c45a17f38916d86e009bb5ad 100644 GIT binary patch delta 732 zcmX|;K}^$77{}l1YscDdybV~!GGe+SaU>Ml0>Tc40I~y-2xf^lX1Bdb1FYtCAUg?| zcr==4Jb5BU6D}r@cvMfu%$Ru9gW+a87%!eY7++iBOWNf7f4}ehfA4$y?}Ys`;Za_~ z008x|Hdnx4{N{3!M~XZE@F6yPtI1D^A+@XO$}6QL51gh`PvYoMSD2y{p~@zeKTu}l zstFZVRj)w;LkQC-`0Xnn(>>|=5)rh9i=gt8yWo%)#8^eAy}x3o>t zDMpE^+t5cfN}7a{R-O1-B>Q?%4>F?%SnSE>a`2!N`i9N?=r1kaSemaaX_fhfYfI;K z>|yWq{)WRx@tW0|OWPZ|7KEqky)V+K7(NF0H~x;l;?MXEKIW@=iYt9FGz$4;s=K63 zsM~UAM@&g*1pbd*;-k2TG6CgFDndD-MhR*7psNZf5BVzUoDmg>axfh3^zQ^p1#cwc z6M%nm!}s`v+a2LseWD05g=J@xSf$x%!?Fy^?n8mL;?Pa2RI&_n(y%Y51rhWYD&BU8 znX`s%r(qy1kQSG0!<;hAnc(Fmyawl1!Y(c<%GhdHl9-f}T98^&48p9FO?Z}1{>q~@`8JQ_WFy{m7R@Fl&dK7O z8jM_%9XNk6a!j`7GMM~;OJ;IAmnI|oAwB3|5$jI5K-aLWNj zUUM5tFfcF%fNW~1#cGo#$d=7+ zOn``);e@gQ5V*OiFaniG3o3FlN;@YOmEKnA$Efh+<>p)iLy$R*AhnaTO)KqbnoKuusFX}B2Bd8T0J fodW6u0WoH<$qbB)(!Pn6Moeiy2Z2mnln?*__>+K* diff --git a/docs/bulk-actions/available-methods.md b/docs/bulk-actions/available-methods.md index 112b83189..ad17cda4f 100644 --- a/docs/bulk-actions/available-methods.md +++ b/docs/bulk-actions/available-methods.md @@ -290,4 +290,26 @@ public function configure(): void { $this->setClearSelectedOnFilterDisabled(); } -``` \ No newline at end of file +``` + +## setDelaySelectAllEnabled + +By default, using the "Select All", immediately makes a call to the backend to populate the "selected" array with the primary key of all resultant rows (based on Filter/Search). This can be slow with large result sets, but gives a good user experience with smaller results, as it allows them to "Select All" and then deselect some rows. + +```php +public function configure(): void +{ + $this->setDelaySelectAllEnabled(); +} +``` + +This prevents the default behaviour from firing, which improves performance when working with very large sets of data. With this feature enabled, the backend update will not fire, however an indication that all result rows have been selected will be passed to the backend, and the frontend will behave as if all rows are selected. + +When running your Bulk Action, having used "Select All", you may then access the array of "all rows" based on your most recent search/filter results: +``` +$rows = $this->getSelectedRows(); +``` + +## setDelaySelectAllDisabled + +This is the default behaviour, see setDelaySelectEnabled for details on what enabling this does. \ No newline at end of file diff --git a/docs/columns/available-methods.md b/docs/columns/available-methods.md index 26f34b926..5c6eaa9b6 100644 --- a/docs/columns/available-methods.md +++ b/docs/columns/available-methods.md @@ -3,6 +3,10 @@ title: Available Methods weight: 3 --- +## Styling + +To change the CSS classes or other attributes assigned to a Column, use can use [setTdAttributes](../datatable/styling), which allows customising attributes based on column type, name or value. + ## Sorting See also [component sorting configuration](../sorting/available-methods). @@ -156,6 +160,23 @@ Column::make('My one off column') ), ``` +Note that any field not used elsewhere in the table, that is required (for example creating an attribute based on two unused fields, these must be added to the query with setAdditionalSelects() in the configure() method (See Here)[https://rappasoft.com/docs/laravel-livewire-tables/v3/datatable/available-methods#content-builder]) +```php + public function configure(): void + { + $this->setAdditionalSelects(['users.forename as forename', 'users.surname as surname']); + } +``` + +You can then use the fields: +```php +Column::make('My one off column') + ->label( + fn($row, Column $column) => $row->forename.' '.$row->surname + ) + ->html(), +``` + ## Collapsing The component has the ability to collapse certain columns at different screen sizes. It will add a plus icon as the left most column that will open up a view below the row with the information of the collapsed columns: diff --git a/docs/datatable/styling.md b/docs/datatable/styling.md index 49726978f..612cf7a47 100644 --- a/docs/datatable/styling.md +++ b/docs/datatable/styling.md @@ -127,7 +127,10 @@ public function configure(): void ### setThAttributes -Set a list of attributes to override on the th elements +Set a list of attributes to override on the th elements. + +Note: If you are using Bulk Actions, then the th for Bulk Actions is [styled separately](../bulk-actions/customisations). +Note: If you are using Reorder, then the th for Reorder is [styled separately](../reordering/available-methods). ```php public function configure(): void diff --git a/docs/filter-types/filters-text.md b/docs/filter-types/filters-text.md index 22255dfd4..81a3de187 100644 --- a/docs/filter-types/filters-text.md +++ b/docs/filter-types/filters-text.md @@ -3,7 +3,7 @@ title: Text Filters weight: 10 --- -Text filters are just HTML text fields. +Text filters are just simple text filters, allowing you to pass a string value into a builder query. ```php public function filters(): array @@ -20,3 +20,133 @@ public function filters(): array ]; } ``` + +### Extra Helpers + +There are a number of helpers to simplify your code, should you not wish to rewrite the filter function repeatedly for a Text Filter. You can only use one of the below methods per-filter. + +#### Contains + +This executes the filter and returns results where the field contains the filter value + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->contains('users.name'), + ]; +} +``` + +#### notContains + +This executes the filter and returns results where the field does not contain filter value + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->notContains('users.name'), + ]; +} +``` + +#### startsWith + +This executes the filter and returns results where the field starts with the filter value + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->startsWith('users.name'), + ]; +} +``` + +#### notStartsWith + +This executes the filter and returns results where the field does not start with the filter value + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->notStartsWith('users.name'), + ]; +} +``` + +#### endsWith + +This executes the filter and returns results where the field ends with the filter value + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->endsWith('users.name'), + ]; +} +``` + +#### notEndsWith + +This executes the filter and returns results where the field does not end with the filter value + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->notEndsWith('users.name'), + ]; +} +``` + +#### setFieldName +An optional method for setting the field to use when filtering, if used, you may omit the field from the above methods, for example: + +```php +public function filters(): array +{ + return [ + TextFilter::make('Name') + ->config([ + 'placeholder' => 'Search Name', + 'maxlength' => '25', + ]) + ->setFieldName('users.name') + ->contains(), + ]; +} +``` diff --git a/docs/reordering/available-methods.md b/docs/reordering/available-methods.md index 101848599..6534c92ec 100644 --- a/docs/reordering/available-methods.md +++ b/docs/reordering/available-methods.md @@ -142,3 +142,19 @@ public function configure(): void $this->setDefaultReorderSort('order', 'desc'); } ``` + + +## setReorderThAttributes + +You may pass an array to this method, which allows you to pass Custom Attributes into the table header for the Reorder Column + +```php +public function configure(): void +{ + + $this->setReorderThAttributes([ + 'class' => 'bg-red-500', + 'default' => false + ]); +} +``` \ No newline at end of file diff --git a/resources/css/bootstrap-custom.css b/resources/css/bootstrap-custom.css new file mode 100644 index 000000000..edd9e2ee6 --- /dev/null +++ b/resources/css/bootstrap-custom.css @@ -0,0 +1,31 @@ +.laravel-livewire-tables-cursor { + cursor:pointer; +} + +.laravel-livewire-tables-btn-tiny { + width:0.5em; + height:0.5em; +} + +.laravel-livewire-tables-btn-smaller { + width:1em; + height:1em; +} + +.laravel-livewire-tables-btn-small +{ + width:1.2em; + height:1.2em; +} + +.laravel-livewire-tables-btn-md +{ + width:1.3em; + height:1.3em; +} + +.laravel-livewire-tables-btn-lg +{ + width:1.4em; + height:1.4em; +} diff --git a/resources/css/bootstrap-custom.min.css b/resources/css/bootstrap-custom.min.css new file mode 100644 index 000000000..40bb25da2 --- /dev/null +++ b/resources/css/bootstrap-custom.min.css @@ -0,0 +1 @@ +.laravel-livewire-tables-cursor{cursor:pointer}.laravel-livewire-tables-btn-tiny{width:.5em;height:.5em}.laravel-livewire-tables-btn-smaller{width:1em;height:1em}.laravel-livewire-tables-btn-small{width:1.2em;height:1.2em}.laravel-livewire-tables-btn-md{width:1.3em;height:1.3em}.laravel-livewire-tables-btn-lg{width:1.4em;height:1.4em} \ No newline at end of file diff --git a/resources/css/laravel-livewire-tables.css b/resources/css/laravel-livewire-tables.css index 2e8ffef5f..a9c1e186d 100644 --- a/resources/css/laravel-livewire-tables.css +++ b/resources/css/laravel-livewire-tables.css @@ -5,9 +5,37 @@ border-color: var(--rappasoft-table-highlight-color, rgb(255 255 255)) !important; } +.laravel-livewire-tables-cursor { + cursor:pointer; +} +.laravel-livewire-tables-btn-tiny { + width:0.5em; + height:0.5em; +} +.laravel-livewire-tables-btn-smaller { + width:1em; + height:1em; +} +.laravel-livewire-tables-btn-small +{ + width:1.2em; + height:1.2em; +} + +.laravel-livewire-tables-btn-md +{ + width:1.3em; + height:1.3em; +} + +.laravel-livewire-tables-btn-lg +{ + width:1.4em; + height:1.4em; +} .laravel-livewire-tables-highlight-top { border-style: solid !important; @@ -381,7 +409,7 @@ label[dir=rtl] .range-slider { } .dark .lds-hourglass:after { border: 32px solid #FFF; - + border-color: #000 transparent #000 transparent; } diff --git a/resources/css/laravel-livewire-tables.min.css b/resources/css/laravel-livewire-tables.min.css index 750fbd378..778bf1e21 100644 --- a/resources/css/laravel-livewire-tables.min.css +++ b/resources/css/laravel-livewire-tables.min.css @@ -1,7 +1 @@ -.laravel-livewire-tables-highlight{border-style:solid!important;border-top-width:2px!important;border-bottom-width:2px!important;border-color:var(--rappasoft-table-highlight-color,rgb(255 255 255))!important}.laravel-livewire-tables-highlight-top{border-style:solid!important;border-top-width:2px!important;border-bottom-width:0!important;border-color:var(--rappasoft-table-top-highlight-color,var(--rappasoft-table-highlight-color,rgb(255 255 255)))!important}.laravel-livewire-tables-highlight-bottom{border-style:solid!important;border-top-width:0!important;border-bottom-width:2px!important;border-color:var(--rappasoft-table-bottom-highlight-color,var(--rappasoft-table-highlight-color,rgb(255 255 255)))!important}.laravel-livewire-tables-reorderingMinimised{width:0}.laravel-livewire-table-dragging{opacity:.5!important}.range-slider.grad{--progress-shadow:2px 2px 4px rgba(0, 0, 0, 0.2) inset;--progress-flll-shadow:var(--progress-shadow);--fill-color:linear-gradient(to right, LightCyan, var(--primary-color, #0366d6));--thumb-shadow:0 0 4px rgba(0, 0, 0, 0.3),-3px 9px 9px rgba(255, 241, 241, 0.33) inset,-1px 3px 2px rgba(255, 255, 255, 0.33) inset,0 0 0 99px var(--primary-color, #0366d6) inset}.range-slider.grad input:hover{--thumb-transform:scale(1.2)}.range-slider.grad input:active{--thumb-shadow:inherit;--thumb-transform:scale(1)}.range-slider.flat{--thumb-size:25px;--track-height:calc(var(--thumb-size) / 3);--progress-shadow:none;--progress-flll-shadow:none;--thumb-shadow:0 0 0 7px var(--primary-color, #0366d6) inset,0 0 0 99px white inset;--thumb-shadow-hover:0 0 0 9px var(--primary-color, #0366d6) inset,0 0 0 99px white inset;--thumb-shadow-active:0 0 0 13px var(--primary-color, #0366d6) inset}.range-slider{--value-offset-y:var(--ticks-gap);--value-background:transparent;--value-font:700 12px/1 Arial;--progress-radius:20px;--track-height:calc(var(--thumb-size) / 2);--min-max-opacity:1;--min-max-x-offset:10%;--thumb-size:22px;--thumb-shadow:0 0 3px rgba(0, 0, 0, 0.4),0 0 1px rgba(0, 0, 0, 0.5) inset,0 0 0 99px var(--thumb-color, white) inset;--thumb-shadow-active:0 0 0 calc(var(--thumb-size) / 4) inset var(--thumb-color, white),0 0 0 99px var(--primary-color, #0366d6) inset,0 0 3px rgba(0, 0, 0, 0.4);--thumb-shadow-hover:var(--thumb-shadow);--ticks-thickness:1px;--ticks-height:5px;--ticks-gap:var(--ticks-height, - 0);--step:1;--ticks-count:Calc(var(--max) - var(--min))/var(--step);--maxTicksAllowed:30;--too-many-ticks:Min(1, Max(var(--ticks-count) - var(--maxTicksAllowed), 0));--x-step:Max(var(--step), - var(--too-many-ticks) * (var(--max) - var(--min)));--tickInterval:100/((var(--max) - var(--min)) / var(--step)) * var(--tickEvery, 1);--tickIntervalPerc:calc((100% - var(--thumb-size)) / ((var(--max) - var(--min)) / var(--x-step)) * var(--tickEvery, 1));--value-a:Clamp(var(--min), - var(--value, 0), - var(--max));--value-b:var(--value, 0);--text-value-a:var(--text-value, "");--completed-a:calc((var(--value-a) - var(--min)) / (var(--max) - var(--min)) * 100);--completed-b:calc((var(--value-b) - var(--min)) / (var(--max) - var(--min)) * 100);--ca:Min(var(--completed-a), - var(--completed-b));--cb:Max(var(--completed-a), var(--completed-b));--thumbs-too-close:Clamp(-1, 1000 * (Min(1, Max(var(--cb) - var(--ca) - 5, -1)) + 0.001), 1);--thumb-close-to-min:Min(1, Max(var(--ca) - 2, - 0));--thumb-close-to-max:Min(1, Max(98 - var(--cb), 0));display:inline-block;height:max(var(--track-height),var(--thumb-size));background:linear-gradient(to right,var(--ticks-color,silver) var(--ticks-thickness),transparent 1px) repeat-x;background-size:var(--tickIntervalPerc) var(--ticks-height);background-position-x:calc(var(--thumb-size)/ 2 - var(--ticks-thickness)/ 2);background-position-y:var(--flip-y,bottom);padding-bottom:var(--flip-y,var(--ticks-gap));padding-top:calc(var(--flip-y) * var(--ticks-gap));position:relative;z-index:1}.range-slider::after,.range-slider::before{--offset:calc(var(--thumb-size) / 2);content:counter(x);display:var(--show-min-max,block);font:var(--min-max-font, 12px Arial);position:absolute;bottom:var(--flip-y,-2.5ch);top:calc(-2.5ch * var(--flip-y));opacity:clamp(0, var(--at-edge), var(--min-max-opacity));transform:translateX(calc(var(--min-max-x-offset) * var(--before,-1) * -1)) scale(var(--at-edge));pointer-events:none}.dark .range-slider::after,.dark .range-slider::before,.dark .range-slider>input+output::after,.dark .range-slider>input:first-of-type+output::after{color:#fff}.range-slider::before{--before:1;counter-reset:x var(--min);left:var(--offset)}.range-slider::after{counter-reset:x var(--max);right:var(--offset)}.range-slider__progress::after,.range-slider__progress::before{content:"";top:0;right:0;bottom:0;border-radius:inherit;left:0}.range-slider__values{position:relative;top:50%;line-height:0;text-align:justify;width:100%;pointer-events:none;margin:0 auto;z-index:5}.range-slider__values::after{content:"";width:100%;display:inline-block;height:0;background:red}.range-slider__progress{--start-end:calc(var(--thumb-size) / 2);--clip-end:calc(100% - (var(--cb)) * 1%);--clip-start:calc(var(--ca) * 1%);--clip:inset(-20px var(--clip-end) -20px var(--clip-start));position:absolute;left:var(--start-end);right:var(--start-end);top:calc(var(--ticks-gap) * var(--flip-y,0) + var(--thumb-size)/ 2 - var(--track-height)/ 2);height:calc(var(--track-height));background:var(--progress-background,#eee);pointer-events:none;z-index:-1;border-radius:var(--progress-radius)}.range-slider__progress::before{position:absolute;-webkit-clip-path:var(--clip);clip-path:var(--clip);background:var(--fill-color,#0366d6);box-shadow:var(--progress-flll-shadow);z-index:1}.range-slider__progress::after{position:absolute;box-shadow:var(--progress-shadow);pointer-events:none}.range-slider>input{-webkit-appearance:none;width:100%;height:var(--thumb-size);margin:0;position:absolute;left:0;top:calc(50% - Max(var(--track-height),var(--thumb-size))/ 2 + calc(var(--ticks-gap)/ 2 * var(--flip-y,-1)));cursor:-webkit-grab;cursor:grab;outline:0;background:0 0}.range-slider>input:not(:only-of-type){pointer-events:none}.range-slider>input::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-webkit-transition:.1s;transition:.1s}.range-slider>input::-moz-range-thumb{-moz-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-moz-transition:.1s;transition:.1s}.range-slider>input::-ms-thumb{appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-ms-transition:.1s;transition:.1s}.range-slider>input:hover{--thumb-shadow:var(--thumb-shadow-hover)}.range-slider>input:hover+output{--value-background:var(--value-background-hover, #0366d6);--y-offset:-5px;color:var(--value-active-color,#fff);box-shadow:0 0 0 3px var(--value-background)}.range-slider>input:active{--thumb-shadow:var(--thumb-shadow-active);cursor:-webkit-grabbing;cursor:grabbing;z-index:2}.range-slider>input:active+output{transition:none}.range-slider>input:first-of-type{--is-left-most:Clamp(0, (var(--value-a) - var(--value-b)) * 99999, 1)}.range-slider>input:first-of-type+output{--value:var(--value-a);--x-offset:calc(var(--completed-a) * -1%)}.range-slider>input:first-of-type+output:not(:only-of-type){--flip:calc(var(--thumbs-too-close) * -1)}.range-slider>input:first-of-type+output::after{content:var(--prefix, "") var(--text-value-a) var(--suffix, "")}.range-slider>input:nth-of-type(2){--is-left-most:Clamp(0, (var(--value-b) - var(--value-a)) * 99999, 1)}.range-slider>input:nth-of-type(2)+output{--value:var(--value-b)}.range-slider>input:only-of-type~.range-slider__progress{--clip-start:0}.range-slider>input+output{--flip:-1;--x-offset:calc(var(--completed-b) * -1%);--pos:calc(((var(--value) - var(--min)) / (var(--max) - var(--min))) * 100%);pointer-events:none;position:absolute;z-index:5;background:var(--value-background);border-radius:10px;padding:2px 4px;left:var(--pos);transform:translate(var(--x-offset),calc(150% * var(--flip) - (var(--y-offset,0px) + var(--value-offset-y)) * var(--flip)));transition:.12s ease-out,left}.range-slider>input+output::after{content:var(--prefix, "") var(--text-value-b) var(--suffix, "");font:var(--value-font)}body>.range-slider,label[dir=rtl] .range-slider{width:clamp(300px,50vw,800px);min-width:200px}.superhide{display:none}.lds-hourglass{display:inline-block;position:relative;width:80px;height:80px}.lds-hourglass:after{content:" ";display:block;border-radius:50%;width:0;height:0;margin:8px;box-sizing:border-box;border:32px solid #000;border-color:#fff transparent;animation:1.2s infinite lds-hourglass}.dark .lds-hourglass:after{border:32px solid #fff}@keyframes lds-hourglass{0%{transform:rotate(0);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}50%{transform:rotate(900deg);animation-timing-function:cubic-bezier(0.215,0.61,0.355,1)}100%{transform:rotate(1800deg)}} \ No newline at end of file +.laravel-livewire-tables-highlight{border-style:solid!important;border-top-width:2px!important;border-bottom-width:2px!important;border-color:var(--rappasoft-table-highlight-color,rgb(255 255 255))!important}.laravel-livewire-tables-cursor{cursor:pointer}.laravel-livewire-tables-btn-tiny{width:.5em;height:.5em}.laravel-livewire-tables-btn-smaller{width:1em;height:1em}.laravel-livewire-tables-btn-small{width:1.2em;height:1.2em}.laravel-livewire-tables-btn-md{width:1.3em;height:1.3em}.laravel-livewire-tables-btn-lg{width:1.4em;height:1.4em}.laravel-livewire-tables-highlight-top{border-style:solid!important;border-top-width:2px!important;border-bottom-width:0!important;border-color:var(--rappasoft-table-top-highlight-color,var(--rappasoft-table-highlight-color,rgb(255 255 255)))!important}.laravel-livewire-tables-highlight-bottom{border-style:solid!important;border-top-width:0!important;border-bottom-width:2px!important;border-color:var(--rappasoft-table-bottom-highlight-color,var(--rappasoft-table-highlight-color,rgb(255 255 255)))!important}.laravel-livewire-tables-reorderingMinimised{width:0}.laravel-livewire-table-dragging{opacity:.5!important}.range-slider.grad{--progress-shadow:2px 2px 4px rgba(0, 0, 0, 0.2) inset;--progress-flll-shadow:var(--progress-shadow);--fill-color:linear-gradient(to right, LightCyan, var(--primary-color, #0366d6));--thumb-shadow:0 0 4px rgba(0, 0, 0, 0.3),-3px 9px 9px rgba(255, 241, 241, 0.33) inset,-1px 3px 2px rgba(255, 255, 255, 0.33) inset,0 0 0 99px var(--primary-color, #0366d6) inset}.range-slider.grad input:hover{--thumb-transform:scale(1.2)}.range-slider.grad input:active{--thumb-shadow:inherit;--thumb-transform:scale(1)}.range-slider.flat{--thumb-size:25px;--track-height:calc(var(--thumb-size) / 3);--progress-shadow:none;--progress-flll-shadow:none;--thumb-shadow:0 0 0 7px var(--primary-color, #0366d6) inset,0 0 0 99px white inset;--thumb-shadow-hover:0 0 0 9px var(--primary-color, #0366d6) inset,0 0 0 99px white inset;--thumb-shadow-active:0 0 0 13px var(--primary-color, #0366d6) inset}.range-slider{--value-offset-y:var(--ticks-gap);--value-background:transparent;--value-font:700 12px/1 Arial;--progress-radius:20px;--track-height:calc(var(--thumb-size) / 2);--min-max-opacity:1;--min-max-x-offset:10%;--thumb-size:22px;--thumb-shadow:0 0 3px rgba(0, 0, 0, 0.4),0 0 1px rgba(0, 0, 0, 0.5) inset,0 0 0 99px var(--thumb-color, white) inset;--thumb-shadow-active:0 0 0 calc(var(--thumb-size) / 4) inset var(--thumb-color, white),0 0 0 99px var(--primary-color, #0366d6) inset,0 0 3px rgba(0, 0, 0, 0.4);--thumb-shadow-hover:var(--thumb-shadow);--ticks-thickness:1px;--ticks-height:5px;--ticks-gap:var(--ticks-height, 0);--step:1;--ticks-count:Calc(var(--max) - var(--min))/var(--step);--maxTicksAllowed:30;--too-many-ticks:Min(1, Max(var(--ticks-count) - var(--maxTicksAllowed), 0));--x-step:Max(var(--step), var(--too-many-ticks) * (var(--max) - var(--min)));--tickInterval:100/((var(--max) - var(--min)) / var(--step)) * var(--tickEvery, 1);--tickIntervalPerc:calc((100% - var(--thumb-size)) / ((var(--max) - var(--min)) / var(--x-step)) * var(--tickEvery, 1));--value-a:Clamp(var(--min), var(--value, 0), var(--max));--value-b:var(--value, 0);--text-value-a:var(--text-value, "");--completed-a:calc((var(--value-a) - var(--min)) / (var(--max) - var(--min)) * 100);--completed-b:calc((var(--value-b) - var(--min)) / (var(--max) - var(--min)) * 100);--ca:Min(var(--completed-a), var(--completed-b));--cb:Max(var(--completed-a), var(--completed-b));--thumbs-too-close:Clamp(-1, 1000 * (Min(1, Max(var(--cb) - var(--ca) - 5, -1)) + 0.001), 1);--thumb-close-to-min:Min(1, Max(var(--ca) - 2, 0));--thumb-close-to-max:Min(1, Max(98 - var(--cb), 0));display:inline-block;height:max(var(--track-height),var(--thumb-size));background:linear-gradient(to right,var(--ticks-color,silver) var(--ticks-thickness),transparent 1px) repeat-x;background-size:var(--tickIntervalPerc) var(--ticks-height);background-position-x:calc(var(--thumb-size)/ 2 - var(--ticks-thickness)/ 2);background-position-y:var(--flip-y,bottom);padding-bottom:var(--flip-y,var(--ticks-gap));padding-top:calc(var(--flip-y) * var(--ticks-gap));position:relative;z-index:1}.range-slider::after,.range-slider::before{--offset:calc(var(--thumb-size) / 2);content:counter(x);display:var(--show-min-max,block);font:var(--min-max-font, 12px Arial);position:absolute;bottom:var(--flip-y,-2.5ch);top:calc(-2.5ch * var(--flip-y));opacity:clamp(0, var(--at-edge), var(--min-max-opacity));transform:translateX(calc(var(--min-max-x-offset) * var(--before,-1) * -1)) scale(var(--at-edge));pointer-events:none}.dark .range-slider::after,.dark .range-slider::before,.dark .range-slider>input+output::after,.dark .range-slider>input:first-of-type+output::after{color:#fff}.range-slider::before{--before:1;counter-reset:x var(--min);left:var(--offset)}.range-slider::after{counter-reset:x var(--max);right:var(--offset)}.range-slider__progress::after,.range-slider__progress::before{content:"";top:0;right:0;bottom:0;border-radius:inherit;left:0}.range-slider__values{position:relative;top:50%;line-height:0;text-align:justify;width:100%;pointer-events:none;margin:0 auto;z-index:5}.range-slider__values::after{content:"";width:100%;display:inline-block;height:0;background:red}.range-slider__progress{--start-end:calc(var(--thumb-size) / 2);--clip-end:calc(100% - (var(--cb)) * 1%);--clip-start:calc(var(--ca) * 1%);--clip:inset(-20px var(--clip-end) -20px var(--clip-start));position:absolute;left:var(--start-end);right:var(--start-end);top:calc(var(--ticks-gap) * var(--flip-y,0) + var(--thumb-size)/ 2 - var(--track-height)/ 2);height:calc(var(--track-height));background:var(--progress-background,#eee);pointer-events:none;z-index:-1;border-radius:var(--progress-radius)}.range-slider__progress::before{position:absolute;-webkit-clip-path:var(--clip);clip-path:var(--clip);background:var(--fill-color,#0366d6);box-shadow:var(--progress-flll-shadow);z-index:1}.range-slider__progress::after{position:absolute;box-shadow:var(--progress-shadow);pointer-events:none}.range-slider>input{-webkit-appearance:none;width:100%;height:var(--thumb-size);margin:0;position:absolute;left:0;top:calc(50% - Max(var(--track-height),var(--thumb-size))/ 2 + calc(var(--ticks-gap)/ 2 * var(--flip-y,-1)));cursor:-webkit-grab;cursor:grab;outline:0;background:0 0}.range-slider>input:not(:only-of-type){pointer-events:none}.range-slider>input::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-webkit-transition:.1s;transition:.1s}.range-slider>input::-moz-range-thumb{-moz-appearance:none;appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-moz-transition:.1s;transition:.1s}.range-slider>input::-ms-thumb{appearance:none;height:var(--thumb-size);width:var(--thumb-size);transform:var(--thumb-transform);border-radius:var(--thumb-radius,50%);background:var(--thumb-color,#fff);box-shadow:var(--thumb-shadow);border:none;pointer-events:auto;-ms-transition:.1s;transition:.1s}.range-slider>input:hover{--thumb-shadow:var(--thumb-shadow-hover)}.range-slider>input:hover+output{--value-background:var(--value-background-hover, #0366d6);--y-offset:-5px;color:var(--value-active-color,#fff);box-shadow:0 0 0 3px var(--value-background)}.range-slider>input:active{--thumb-shadow:var(--thumb-shadow-active);cursor:-webkit-grabbing;cursor:grabbing;z-index:2}.range-slider>input:active+output{transition:none}.range-slider>input:first-of-type{--is-left-most:Clamp(0, (var(--value-a) - var(--value-b)) * 99999, 1)}.range-slider>input:first-of-type+output{--value:var(--value-a);--x-offset:calc(var(--completed-a) * -1%)}.range-slider>input:first-of-type+output:not(:only-of-type){--flip:calc(var(--thumbs-too-close) * -1)}.range-slider>input:first-of-type+output::after{content:var(--prefix, "") var(--text-value-a) var(--suffix, "")}.range-slider>input:nth-of-type(2){--is-left-most:Clamp(0, (var(--value-b) - var(--value-a)) * 99999, 1)}.range-slider>input:nth-of-type(2)+output{--value:var(--value-b)}.range-slider>input:only-of-type~.range-slider__progress{--clip-start:0}.range-slider>input+output{--flip:-1;--x-offset:calc(var(--completed-b) * -1%);--pos:calc(((var(--value) - var(--min)) / (var(--max) - var(--min))) * 100%);pointer-events:none;position:absolute;z-index:5;background:var(--value-background);border-radius:10px;padding:2px 4px;left:var(--pos);transform:translate(var(--x-offset),calc(150% * var(--flip) - (var(--y-offset,0px) + var(--value-offset-y)) * var(--flip)));transition:.12s ease-out,left}.range-slider>input+output::after{content:var(--prefix, "") var(--text-value-b) var(--suffix, "");font:var(--value-font)}body>.range-slider,label[dir=rtl] .range-slider{width:clamp(300px,50vw,800px);min-width:200px}.superhide{display:none}.lds-hourglass{display:inline-block;position:relative;width:80px;height:80px}.lds-hourglass:after{content:" ";display:block;border-radius:50%;width:0;height:0;margin:8px;box-sizing:border-box;border:32px solid #000;border-color:#fff transparent;animation:1.2s infinite lds-hourglass}.dark .lds-hourglass:after{border:32px solid #fff;border-color:#000 transparent}@keyframes lds-hourglass{0%{transform:rotate(0);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}50%{transform:rotate(900deg);animation-timing-function:cubic-bezier(0.215,0.61,0.355,1)}100%{transform:rotate(1800deg)}} \ No newline at end of file diff --git a/resources/js/laravel-livewire-tables.js b/resources/js/laravel-livewire-tables.js index 0c02f6c47..d698ff82c 100644 --- a/resources/js/laravel-livewire-tables.js +++ b/resources/js/laravel-livewire-tables.js @@ -8,6 +8,8 @@ document.addEventListener('alpine:init', () => { paginationTotalItemCount: wire.entangle('paginationTotalItemCount'), paginationCurrentItems: wire.entangle('paginationCurrentItems'), selectedItems: wire.entangle('selected'), + selectAllStatus: wire.entangle('selectAll'), + delaySelectAll: wire.entangle('delaySelectAll'), hideBulkActionsWhenEmpty: wire.entangle('hideBulkActionsWhenEmpty'), toggleSelectAll() { if (!showBulkActionsAlpine) { @@ -16,22 +18,44 @@ document.addEventListener('alpine:init', () => { if (this.paginationTotalItemCount === this.selectedItems.length) { this.clearSelected(); + this.selectAllStatus = false; } else { - this.setAllSelected(); + if (this.delaySelectAll) + { + this.setAllItemsSelected(); + } + else + { + this.setAllSelected(); + } + } + }, + setAllItemsSelected() { + if (!showBulkActionsAlpine) { + return; } + this.selectAllStatus = true; + this.selectAllOnPage(); }, setAllSelected() { if (!showBulkActionsAlpine) { return; } - - wire.setAllSelected(); + if (this.delaySelectAll) + { + this.selectAllStatus = true; + this.selectAllOnPage(); + } + else + { + wire.setAllSelected(); + } }, clearSelected() { if (!showBulkActionsAlpine) { return; } - + this.selectAllStatus = false; wire.clearSelected(); }, selectAllOnPage() { @@ -196,6 +220,9 @@ document.addEventListener('alpine:init', () => { hideReorderColumnUnlessReorderingStatus: wire.entangle('hideReorderColumnUnlessReorderingStatus'), reorderDisplayColumn: wire.entangle('reorderDisplayColumn'), dragStart(event) { + this.$nextTick(() => { this.setupEvenOddClasses() }); + + this.sourceID = event.target.id; event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', event.target.id); @@ -266,11 +293,13 @@ document.addEventListener('alpine:init', () => { } }, reorderToggle() { + this.$nextTick(() => { this.setupEvenOddClasses() }); if (this.currentlyReorderingStatus) { wire.disableReordering(); } else { + this.setupEvenOddClasses(); if (this.hideReorderColumnUnlessReorderingStatus) { this.reorderDisplayColumn = true; } @@ -294,8 +323,8 @@ document.addEventListener('alpine:init', () => { wire.storeReorder(orderedRows); }, setupEvenOddClasses() { - if (this.currentlyReorderingStatus === true) { - + if (this.evenNotInOdd.length === undefined || this.evenNotInOdd.length == 0 || this.oddNotInEven.length === undefined || this.oddNotInEven.length == 0) + { let tbody = document.getElementById(tableID).getElementsByTagName('tbody')[0]; let evenRowClassArray = []; let oddRowClassArray = []; @@ -305,15 +334,15 @@ document.addEventListener('alpine:init', () => { oddRowClassArray = Array.from(tbody.rows[1].classList); this.evenNotInOdd = evenRowClassArray.filter(element => !oddRowClassArray.includes(element)); this.oddNotInEven = oddRowClassArray.filter(element => !evenRowClassArray.includes(element)); + console.log("EvenNotInOdd:"+this.evenNotInOdd); + console.log("oddNotInEven:"+this.oddNotInEven); + evenRowClassArray = [] oddRowClassArray = [] } } }, init() { - this.$watch('currentlyReorderingStatus', value => this.setupEvenOddClasses()); - } })); - }); \ No newline at end of file diff --git a/resources/js/laravel-livewire-tables.min.js b/resources/js/laravel-livewire-tables.min.js index 46255d270..82e3e3a1a 100644 --- a/resources/js/laravel-livewire-tables.min.js +++ b/resources/js/laravel-livewire-tables.min.js @@ -1 +1 @@ -document.addEventListener("alpine:init",()=>{Alpine.data("tableWrapper",(e,t)=>({childElementOpen:!1,filtersOpen:e.entangle("filterSlideDownDefaultVisible"),paginationCurrentCount:e.entangle("paginationCurrentCount"),paginationTotalItemCount:e.entangle("paginationTotalItemCount"),paginationCurrentItems:e.entangle("paginationCurrentItems"),selectedItems:e.entangle("selected"),hideBulkActionsWhenEmpty:e.entangle("hideBulkActionsWhenEmpty"),toggleSelectAll(){t&&(this.paginationTotalItemCount===this.selectedItems.length?this.clearSelected():this.setAllSelected())},setAllSelected(){t&&e.setAllSelected()},clearSelected(){t&&e.clearSelected()},selectAllOnPage(){if(!t)return;let e=this.selectedItems,i=this.paginationCurrentItems.values();for(let l of i)e.push(l.toString());this.selectedItems=[...new Set(e)]}})),Alpine.data("numberRangeFilter",(e,t,i,l,s)=>({allFilters:e.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!1),defaultMin:l.minRange,defaultMax:l.maxRange,restrictUpdates:!1,initialiseStyles(){let e=document.getElementById(i);e.style.setProperty("--value-a",this.wireValues.min??this.filterMin),e.style.setProperty("--text-value-a",JSON.stringify(this.wireValues.min??this.filterMin)),e.style.setProperty("--value-b",this.wireValues.max??this.filterMax),e.style.setProperty("--text-value-b",JSON.stringify(this.wireValues.max??this.filterMax))},updateStyles(e,t){let l=document.getElementById(i);l.style.setProperty("--value-a",e),l.style.setProperty("--text-value-a",JSON.stringify(e)),l.style.setProperty("--value-b",t),l.style.setProperty("--text-value-b",JSON.stringify(t))},setupWire(){void 0!==this.wireValues?(this.filterMin=this.originalMin=void 0!==this.wireValues.min?this.wireValues.min:this.defaultMin,this.filterMax=this.originalMax=void 0!==this.wireValues.max?this.wireValues.max:this.defaultMax):(this.filterMin=this.originalMin=this.defaultMin,this.filterMax=this.originalMax=this.defaultMax),this.updateStyles(this.filterMin,this.filterMax)},allowUpdates(){this.updateWire()},updateWire(){let e=parseInt(this.filterMin),t=parseInt(this.filterMax);(e!=this.originalMin||t!=this.originalMax)&&(tthis.setupWire())}})),Alpine.data("flatpickrFilter",(e,t,i,l,s)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(l,{mode:"range",altFormat:i.altFormat??"F j, Y",altInput:i.altInput??!1,allowInput:i.allowInput??!1,allowInvalidPreload:!0,ariaDateFormat:i.ariaDateFormat??"F j, Y",clickOpens:!0,dateFormat:i.dateFormat??"Y-m-d",defaultDate:i.defaultDate??null,defaultHour:i.defaultHour??12,defaultMinute:i.defaultMinute??0,enableTime:i.enableTime??!1,enableSeconds:i.enableSeconds??!1,hourIncrement:i.hourIncrement??1,locale:i.locale??"en",minDate:i.earliestDate??null,maxDate:i.latestDate??null,minuteIncrement:i.minuteIncrement??5,shorthandCurrentMonth:i.shorthandCurrentMonth??!1,time_24hr:i.time_24hr??!1,weekNumbers:i.weekNumbers??!1,onOpen:function(){window.childElementOpen=!0},onChange:function(i,l,s){if(i.length>1){var a=l.split(" ")[0],r=l.split(" ")[2],n={};window.childElementOpen=!1,window.filterPopoverOpen=!1,n={minDate:a,maxDate:r},e.set("filterComponents."+t,n)}}}),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())}})),Alpine.data("reorderFunction",(e,t,i)=>({dragging:!1,reorderEnabled:!1,sourceID:"",targetID:"",evenRowClasses:"",oddRowClasses:"",currentlyHighlightedElement:"",evenRowClassArray:{},oddRowClassArray:{},evenNotInOdd:{},oddNotInEven:{},orderedRows:[],defaultReorderColumn:e.get("defaultReorderColumn"),reorderStatus:e.get("reorderStatus"),currentlyReorderingStatus:e.entangle("currentlyReorderingStatus"),hideReorderColumnUnlessReorderingStatus:e.entangle("hideReorderColumnUnlessReorderingStatus"),reorderDisplayColumn:e.entangle("reorderDisplayColumn"),dragStart(e){this.sourceID=e.target.id,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",e.target.id),e.target.classList.add("laravel-livewire-tables-dragging")},dragOverEvent(e){"object"==typeof this.currentlyHighlightedElement&&this.currentlyHighlightedElement.classList.remove("laravel-livewire-tables-highlight-bottom","laravel-livewire-tables-highlight-top");let t=e.target.closest("tr");this.currentlyHighlightedElement=t,e.offsetYi.getBoundingClientRect().height/2?l.insertBefore(s,i.nextSibling):l.insertBefore(s,i),r!l.includes(e)),this.oddNotInEven=l.filter(e=>!i.includes(e)),i=[],l=[])}},init(){this.$watch("currentlyReorderingStatus",e=>this.setupEvenOddClasses())}}))}); \ No newline at end of file +document.addEventListener("alpine:init",()=>{Alpine.data("tableWrapper",(e,t)=>({childElementOpen:!1,filtersOpen:e.entangle("filterSlideDownDefaultVisible"),paginationCurrentCount:e.entangle("paginationCurrentCount"),paginationTotalItemCount:e.entangle("paginationTotalItemCount"),paginationCurrentItems:e.entangle("paginationCurrentItems"),selectedItems:e.entangle("selected"),selectAllStatus:e.entangle("selectAll"),delaySelectAll:e.entangle("delaySelectAll"),hideBulkActionsWhenEmpty:e.entangle("hideBulkActionsWhenEmpty"),toggleSelectAll(){t&&(this.paginationTotalItemCount===this.selectedItems.length?(this.clearSelected(),this.selectAllStatus=!1):this.delaySelectAll?this.setAllItemsSelected():this.setAllSelected())},setAllItemsSelected(){t&&(this.selectAllStatus=!0,this.selectAllOnPage())},setAllSelected(){t&&(this.delaySelectAll?(this.selectAllStatus=!0,this.selectAllOnPage()):e.setAllSelected())},clearSelected(){t&&(this.selectAllStatus=!1,e.clearSelected())},selectAllOnPage(){if(!t)return;let e=this.selectedItems,i=this.paginationCurrentItems.values();for(let l of i)e.push(l.toString());this.selectedItems=[...new Set(e)]}})),Alpine.data("numberRangeFilter",(e,t,i,l,s)=>({allFilters:e.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!1),defaultMin:l.minRange,defaultMax:l.maxRange,restrictUpdates:!1,initialiseStyles(){let e=document.getElementById(i);e.style.setProperty("--value-a",this.wireValues.min??this.filterMin),e.style.setProperty("--text-value-a",JSON.stringify(this.wireValues.min??this.filterMin)),e.style.setProperty("--value-b",this.wireValues.max??this.filterMax),e.style.setProperty("--text-value-b",JSON.stringify(this.wireValues.max??this.filterMax))},updateStyles(e,t){let l=document.getElementById(i);l.style.setProperty("--value-a",e),l.style.setProperty("--text-value-a",JSON.stringify(e)),l.style.setProperty("--value-b",t),l.style.setProperty("--text-value-b",JSON.stringify(t))},setupWire(){void 0!==this.wireValues?(this.filterMin=this.originalMin=void 0!==this.wireValues.min?this.wireValues.min:this.defaultMin,this.filterMax=this.originalMax=void 0!==this.wireValues.max?this.wireValues.max:this.defaultMax):(this.filterMin=this.originalMin=this.defaultMin,this.filterMax=this.originalMax=this.defaultMax),this.updateStyles(this.filterMin,this.filterMax)},allowUpdates(){this.updateWire()},updateWire(){let e=parseInt(this.filterMin),t=parseInt(this.filterMax);(e!=this.originalMin||t!=this.originalMax)&&(tthis.setupWire())}})),Alpine.data("flatpickrFilter",(e,t,i,l,s)=>({wireValues:e.entangle("filterComponents."+t),flatpickrInstance:flatpickr(l,{mode:"range",altFormat:i.altFormat??"F j, Y",altInput:i.altInput??!1,allowInput:i.allowInput??!1,allowInvalidPreload:!0,ariaDateFormat:i.ariaDateFormat??"F j, Y",clickOpens:!0,dateFormat:i.dateFormat??"Y-m-d",defaultDate:i.defaultDate??null,defaultHour:i.defaultHour??12,defaultMinute:i.defaultMinute??0,enableTime:i.enableTime??!1,enableSeconds:i.enableSeconds??!1,hourIncrement:i.hourIncrement??1,locale:i.locale??"en",minDate:i.earliestDate??null,maxDate:i.latestDate??null,minuteIncrement:i.minuteIncrement??5,shorthandCurrentMonth:i.shorthandCurrentMonth??!1,time_24hr:i.time_24hr??!1,weekNumbers:i.weekNumbers??!1,onOpen:function(){window.childElementOpen=!0},onChange:function(i,l,s){if(i.length>1){var a=l.split(" ")[0],r=l.split(" ")[2],n={};window.childElementOpen=!1,window.filterPopoverOpen=!1,n={minDate:a,maxDate:r},e.set("filterComponents."+t,n)}}}),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())}})),Alpine.data("reorderFunction",(e,t,i)=>({dragging:!1,reorderEnabled:!1,sourceID:"",targetID:"",evenRowClasses:"",oddRowClasses:"",currentlyHighlightedElement:"",evenRowClassArray:{},oddRowClassArray:{},evenNotInOdd:{},oddNotInEven:{},orderedRows:[],defaultReorderColumn:e.get("defaultReorderColumn"),reorderStatus:e.get("reorderStatus"),currentlyReorderingStatus:e.entangle("currentlyReorderingStatus"),hideReorderColumnUnlessReorderingStatus:e.entangle("hideReorderColumnUnlessReorderingStatus"),reorderDisplayColumn:e.entangle("reorderDisplayColumn"),dragStart(e){this.$nextTick(()=>{this.setupEvenOddClasses()}),this.sourceID=e.target.id,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",e.target.id),e.target.classList.add("laravel-livewire-tables-dragging")},dragOverEvent(e){"object"==typeof this.currentlyHighlightedElement&&this.currentlyHighlightedElement.classList.remove("laravel-livewire-tables-highlight-bottom","laravel-livewire-tables-highlight-top");let t=e.target.closest("tr");this.currentlyHighlightedElement=t,e.offsetYi.getBoundingClientRect().height/2?l.insertBefore(s,i.nextSibling):l.insertBefore(s,i),r{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 l=document.getElementById(t),s=[];for(let a=1,r;r=l.rows[a];a++)s.push({[i]: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],i=[],l=[];void 0!==e.rows[0]&&void 0!==e.rows[1]&&(i=Array.from(e.rows[0].classList),l=Array.from(e.rows[1].classList),this.evenNotInOdd=i.filter(e=>!l.includes(e)),this.oddNotInEven=l.filter(e=>!i.includes(e)),console.log("EvenNotInOdd:"+this.evenNotInOdd),console.log("oddNotInEven:"+this.oddNotInEven),i=[],l=[])}},init(){}}))}); \ No newline at end of file diff --git a/resources/views/components/table/collapsed-columns.blade.php b/resources/views/components/table/collapsed-columns.blade.php index 49569281a..b83ea8070 100644 --- a/resources/views/components/table/collapsed-columns.blade.php +++ b/resources/views/components/table/collapsed-columns.blade.php @@ -1,6 +1,10 @@ @aware(['component', 'tableName']) @props(['row', 'rowIndex']) +@php + $customAttributes = $component->getTrAttributes($row, $rowIndex); +@endphp + @if ($component->collapsingColumnsAreEnabled() && $component->hasCollapsedColumns()) @php $colspan = $component->getColspanCount(); @@ -28,16 +32,20 @@ wire:key="{{ $tableName }}-row-{{ $row->{$this->getPrimaryKey()} }}-collapsed-contents" wire:loading.class.delay="opacity-50 dark:bg-gray-900 dark:opacity-60" + {{ + $attributes->merge($customAttributes) + ->class(['hidden bg-white dark:bg-gray-700 dark:text-white rappasoft-striped-row' => ($component->isTailwind() && ($customAttributes['default'] ?? true) && $rowIndex % 2 === 0)]) + ->class(['hidden bg-gray-50 dark:bg-gray-800 dark:text-white rappasoft-striped-row' => ($component->isTailwind() && ($customAttributes['default'] ?? true) && $rowIndex % 2 !== 0)]) + ->class(['d-none bg-light rappasoft-striped-row' => ($component->isBootstrap() && $rowIndex % 2 === 0 && ($customAttributes['default'] ?? true))]) + ->class(['d-none bg-white rappasoft-striped-row' => ($component->isBootstrap() && $rowIndex % 2 !== 0 && ($customAttributes['default'] ?? true))]) + ->except(['default']) + }} - @class([ - 'hidden bg-white dark:bg-gray-700 dark:text-white' => $component->isTailwind(), - 'd-none' => $component->isBootstrap() - ]) > $component->isTailwind(), - 'pt-3 p-2' => $component->isBootstrap(), + 'text-left pt-4 pb-2 px-4' => $component->isTailwind(), + 'text-start pt-3 p-2' => $component->isBootstrap(), ]) colspan="{{ $colspan }}" > diff --git a/resources/views/components/table/td.blade.php b/resources/views/components/table/td.blade.php index 0d6973242..2374e504f 100644 --- a/resources/views/components/table/td.blade.php +++ b/resources/views/components/table/td.blade.php @@ -21,7 +21,7 @@ ->class(['d-none' => $component->isBootstrap() && $column && $column->shouldCollapseAlways()]) ->class(['d-none d-md-table-cell' => $component->isBootstrap() && $column && $column->shouldCollapseOnMobile()]) ->class(['d-none d-lg-table-cell' => $component->isBootstrap() && $column && $column->shouldCollapseOnTablet()]) - ->style(['cursor:pointer' => $component->isBootstrap() && $column && $column->isClickable()]) + ->class(['laravel-livewire-tables-cursor' => $component->isBootstrap() && $column && $column->isClickable()]) ->except('default') }} > diff --git a/resources/views/components/table/td/collapsed-columns.blade.php b/resources/views/components/table/td/collapsed-columns.blade.php index e591825dc..79fc2b990 100644 --- a/resources/views/components/table/td/collapsed-columns.blade.php +++ b/resources/views/components/table/td/collapsed-columns.blade.php @@ -55,23 +55,22 @@