From 707b6b8e1ff9546db74cbf2f90b612773cb2a61a Mon Sep 17 00:00:00 2001 From: Joe <104938042+lrljoe@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:38:02 +0100 Subject: [PATCH] v3-Develop to v3-Master - Beta 004 (#1425) * Beta Fixes - Publishing Views/Localizations. Beta Improvements - Search Options (#1324) * Update setSearchDebounce, add setSearchThrottle/setSearchBlur * Remove Lazy Test * Remove Lazy Tests - Update Docs * Update Views Publish Path * Add Translations Publish Option * Update ChangeLog * Add Reusable Columns * Add Tests for Prepend/Append Cols --------- Co-authored-by: lrljoe * Bulk Actions Fix - v3 (#1326) * Update setSearchDebounce, add setSearchThrottle/setSearchBlur * Remove Lazy Test * Remove Lazy Tests - Update Docs * Update Views Publish Path * Add Translations Publish Option * Update ChangeLog * Add Reusable Columns * Add Tests for Prepend/Append Cols * Column Select Fixes * Fix test * Add docs for setSearchPlaceholder * Move Filter Specific Docs ino Filter-Types * Fix styling * Remove extraneous space * Adjusting Selectable Test * Fix For BulkActions Dropdown * Fixes for ColumnSelect * Update ChangeLog * Test Fix for Selectable Columns * Remove Faulty Test --------- Co-authored-by: lrljoe * Fix Column Select - Select All state (#1333) * Fix Column Select - All * Update ChangeLog * Add NonArray setAdditionalSelects Test (#1337) * Test Additions (#1339) * Add NonArray setAdditionalSelects Test * Update for FrontendAssets Test * Fix styling --------- Co-authored-by: lrljoe * Add Third Party Tests (#1340) * Add NonArray setAdditionalSelects Test * Update for FrontendAssets Test * Add ThirdPartyScripts/Styles to Test * Fix styling --------- Co-authored-by: lrljoe * Third Party Asset Tests (#1341) * Add NonArray setAdditionalSelects Test * Update for FrontendAssets Test * Add ThirdPartyScripts/Styles to Test * Add ThirdParty Tests * Fix AutoInjection Issue * Fix 3rd party test * Set default back to remote 3rd party assets * Add CodeCov YML * Update CodeCov - 80% minimum * Add Filter Custom Pills Blade Test * Fix styling * Add formatted column test * Column - Get Formatted Contents Test * Fix styling --------- Co-authored-by: lrljoe * Lazy Loading Fix (#1355) * Add fix for lazy loading * Fix styling --------- Co-authored-by: lrljoe * Spacing fixes * v3 - Missing Tests - Pagination Related Tests (#1363) * Add missing pagination helper tests --------- Co-authored-by: lrljoe * v3 - Missing Tests - Broken Tables (Missing Primary Key/Builder) (#1362) * Add missing component visuals tests & failed tables * Update Exception Expectation * Temporary test swapping to check Exception throwing --------- Co-authored-by: lrljoe * v3 - Add setSearchFieldAttribute Options (#1361) * Add setSearchFieldAttribute Options --------- Co-authored-by: lrljoe * v3 - Column Select Fixes & Tweaks (#1360) * ColumnSelect Fixes - Phase 1 * Update "Select All" to use counts * Missing closing bracket * All Are Selected -> Move to method in Helpers * ColSelectFixes * Ensure event fires * Update ColumnsSelectedTest * Fix superfluous empty array return * Remove superfluous default return * Adjust Tests - Include Coverage For ExcludeDeselectedCols * Add Tests For AllColsSelected * Fix styling * Fixes for Deselected Cols * Fix styling * Updates to Changelog * Removing ColumnSelected Event Test - Temporary * Add default true to replace removed test * Add improved test --------- Co-authored-by: lrljoe * Fix ServiceProvider Fault (#1366) * V3 Frontend Asset Tweaks (#1371) * Check for "original" property * Adjusting order of request validations * Updates to Asset Injection Methods * Fix assertViewIs is not working (#67) Contribs By: Jackson Tong * Bundler Import Options (#1377) * Add All Option * Update Import Paths * Add minified CSS Versions * V3 Restore setTrAttributes (#1378) * Add setTrAttributes back into use * Fix styling --------- Co-authored-by: lrljoe * Update Reorder Documentation (#1381) * Update Reorder Documentation * Update ChangeLog - Merge Unreleased * Update Docs - Advanced Example, Add Including Assets (#1383) * Fix DateRangeFilter Blade Items (#1394) * Add setFilterLabelAttributes Capability (#1397) * Add setFilterLabelAttributes Capability * Update filter-label to use customLabelAttributes to avoid any potential conflicts * Adjust documentation for setCustomFilterLabel --------- Co-authored-by: lrljoe * v3 - Apply Reorder Sorting When Reordering (#1402) * Fix for reorder sort not applying * Fix styling --------- Co-authored-by: lrljoe * v3 - Fix Unrequired Update Call (#1407) * Fixes for Additional Update * Increase cache time to 1 day from 1 hour * Fix tests for cache time * Update mapping to use min.css --------- Co-authored-by: lrljoe * v3 - Adding CollapseAlways Option for Columns (#1403) * Add CollapseAlways & Fix ReorderColumn * Remove superfluous md:hidden from the icon * Tweaks for Column Collapsing For BS4/BS5 * Fix Collapsed on Bootstrap * Tweak Bootstrap Theme --------- Co-authored-by: lrljoe * Minor tweaks to blades for reorder cols (#1411) * V3 - Fix Reorder For Bootstrap (#1412) * Minor tweaks to blades for reorder cols * Bootstrap Reorder Toggle * Add DiscordBot (#1413) --------- Co-authored-by: Abdul Majeed Shehzad * Docs livewire namespace fix (#1420) * Fixed Livewire namespace in docs * Updated livewire url in docs --------- Co-authored-by: lrljoe * V3 - Fix Localisation (#1424) * Fix Localisations * Fix styling * Load Original and Publish Localisation Paths * Fix styling * v3 - Add Loading Placeholder (#1421) * Initial Loading Placeholder Code, Use Hourglass Spinner * Add wrappers, Class Merging, Colouring * Renaming Traits - Adding LoadingPlaceholder Tests, Add Placeholder Test Table * Update Docs for Placeholder --------- Co-authored-by: lrljoe --------- Co-authored-by: lrljoe Co-authored-by: Anthony Rappa --- CHANGELOG.md | 8 +- docs/datatable/loaders.md | 80 ++++++++++ docs/examples/advanced-example.md | 4 +- docs/examples/basic-example.md | 4 +- docs/misc/custom-markup.md | 4 +- docs/misc/debugging.md | 2 +- docs/misc/loading-placeholder.md | 78 +++++++++ docs/misc/multiple-tables.md | 2 +- docs/misc/saving-state.md | 2 +- docs/start/commands.md | 4 +- docs/start/rendering.md | 6 +- docs/start/requirements.md | 2 +- docs/usage/creating-components.md | 2 +- resources/css/laravel-livewire-tables.css | 38 +++++ resources/css/laravel-livewire-tables.min.css | 2 +- .../components/includes/loading.blade.php | 35 ++++ resources/views/components/table/tr.blade.php | 12 +- resources/views/datatable.blade.php | 4 + src/DataTableComponent.php | 2 + src/LaravelLivewireTablesServiceProvider.php | 15 +- .../LoadingPlaceholderConfiguration.php | 62 ++++++++ .../Helpers/LoadingPlaceholderHelpers.php | 47 ++++++ src/Traits/WithLoadingPlaceholder.php | 24 +++ .../Livewire/PetsTableLoadingPlaceholder.php | 150 ++++++++++++++++++ .../LoadingPlaceholderConfigurationTest.php | 99 ++++++++++++ .../Helpers/LoadingPlaceholderHelpersTest.php | 21 +++ .../Visuals/LoadingPlaceholderVisualsTest.php | 52 ++++++ 27 files changed, 736 insertions(+), 25 deletions(-) create mode 100644 docs/datatable/loaders.md create mode 100644 docs/misc/loading-placeholder.md create mode 100644 resources/views/components/includes/loading.blade.php create mode 100644 src/Traits/Configuration/LoadingPlaceholderConfiguration.php create mode 100644 src/Traits/Helpers/LoadingPlaceholderHelpers.php create mode 100644 src/Traits/WithLoadingPlaceholder.php create mode 100644 tests/Http/Livewire/PetsTableLoadingPlaceholder.php create mode 100644 tests/Traits/Configuration/LoadingPlaceholderConfigurationTest.php create mode 100644 tests/Traits/Helpers/LoadingPlaceholderHelpersTest.php create mode 100644 tests/Traits/Visuals/LoadingPlaceholderVisualsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 56875d62e..ddd8715db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## [3.0.0-beta.4] - 2023-10-17 +- Introduction of Loading Placeholder +- Docs livewire namespace fix [Here](https://github.com/rappasoft/laravel-livewire-tables/pull/1420) +- Add CollapseAlways capability for Columns +- Fix localisation bug + ## [3.0.0-beta.3] - 2023-10-13 - Fix for Livewire ^3.0.6 where the table loading causes an additional lifecycle - Add unminified files to .gitattributes export-ignore @@ -1000,4 +1006,4 @@ Ground Up Rebuild [0.1.4]: https://github.com/rappasoft/laravel-livewire-tables/compare/v0.1.3...v0.1.4 [0.1.3]: https://github.com/rappasoft/laravel-livewire-tables/compare/v0.1.2...v0.1.3 [0.1.2]: https://github.com/rappasoft/laravel-livewire-tables/compare/v0.1.1...v0.1.2 -[0.1.1]: https://github.com/rappasoft/laravel-livewire-tables/compare/v0.1.0...v0.1.1 \ No newline at end of file +[0.1.1]: https://github.com/rappasoft/laravel-livewire-tables/compare/v0.1.0...v0.1.1 diff --git a/docs/datatable/loaders.md b/docs/datatable/loaders.md new file mode 100644 index 000000000..a18c7b77e --- /dev/null +++ b/docs/datatable/loaders.md @@ -0,0 +1,80 @@ +--- +title: Loaders +weight: 4 +--- + +With the introduction of Livewire 3, there are several new methods available for use: + +## Loading Placeholder +```php +public function configure(): void +{ + $this->setLoadingPlaceholderBlade(''); +} +``` + +### setLoadingPlaceholderStatus +```php +public function configure(): void +{ + $this->setLoadingPlaceholderStatus(true); +} +``` + +### setLoadingPlaceholderEnabled +```php +public function configure(): void +{ + $this->setLoadingPlaceholderEnabled(); +} +``` + + +### setLoadingPlaceholderDisabled +```php +public function configure(): void +{ + $this->setLoadingPlaceholderDisabled(); +} +``` + +### setLoadingPlaceholderContent +```php +public function configure(): void +{ + $this->setLoadingPlaceholderContent(''); +} +``` + +### setLoadingPlaceHolderAttributes +```php +public function configure(): void +{ + $this->setLoadingPlaceHolderAttributes([]); +} +``` + +### setLoadingPlaceHolderIconAttributes +```php +public function configure(): void +{ + $this->setLoadingPlacehosetLoadingPlaceHolderIconAttributeslderBlade([]); +} +``` + +### setLoadingPlaceHolderWrapperAttributes +```php +public function configure(): void +{ + $this->setLoadingPlaceHolderWrapperAttributes([]); +} +``` + + +### setLoadingPlaceholderBlade +```php +public function configure(): void +{ + $this->setLoadingPlaceholderBlade(''); +} +``` diff --git a/docs/examples/advanced-example.md b/docs/examples/advanced-example.md index b9c498801..33166dc46 100644 --- a/docs/examples/advanced-example.md +++ b/docs/examples/advanced-example.md @@ -6,7 +6,7 @@ weight: 2 ```php @endif -``` \ No newline at end of file +``` diff --git a/docs/examples/basic-example.md b/docs/examples/basic-example.md index 60c2e07ca..d100933bd 100644 --- a/docs/examples/basic-example.md +++ b/docs/examples/basic-example.md @@ -6,7 +6,7 @@ weight: 1 ```php setLoadingPlaceholderStatus(true); + } +``` + +### setLoadingPlaceholderEnabled + +Use this method to enable the loading placeholder: + +```php + public function configure(): void + { + $this->setLoadingPlaceholderEnabled(); + } +``` + +### setLoadingPlaceholderDisabled + +Use this method to disable the loading placeholder: + +```php + public function configure(): void + { + $this->setLoadingPlaceholderDisabled(); + } +``` + +### setLoadingPlaceholderContent + +You may use this method to set custom text for the placeholder: + +```php + public function configure(): void + { + $this->setLoadingPlaceholderContent('Text To Display'); + } +``` +### setLoadingPlaceHolderWrapperAttributes + +This method allows you to customise the attributes for the <tr> element used as a Placeholder when the table is loading. Similar to other setAttribute methods, this accepts a range of attributes, and a boolean "default", which will enable/disable the default attributes. + +```php + public function configure(): void + { + $this->setLoadingPlaceHolderWrapperAttributes([ + 'class' => 'text-bold', + 'default' => false, + ]); + } + +``` + +### setLoadingPlaceHolderIconAttributes + +This method allows you to customise the attributes for the <div> element that is used solely for the PlaceholderIcon. Similar to other setAttribute methods, this accepts a range of attributes, and a boolean "default", which will enable/disable the default attributes. + +```php + public function configure(): void + { + $this->setLoadingPlaceHolderIconAttributes([ + 'class' => 'lds-hourglass', + 'default' => false, + ]); + } + +``` diff --git a/docs/misc/multiple-tables.md b/docs/misc/multiple-tables.md index 026d8ca1b..2a6ac0dd4 100644 --- a/docs/misc/multiple-tables.md +++ b/docs/misc/multiple-tables.md @@ -1,6 +1,6 @@ --- title: Multiple Tables Same Page -weight: 2 +weight: 3 --- This feature works for mutiple tables on the same page that are **different** components. diff --git a/docs/misc/saving-state.md b/docs/misc/saving-state.md index 3ce5148ee..8d85acd26 100644 --- a/docs/misc/saving-state.md +++ b/docs/misc/saving-state.md @@ -1,6 +1,6 @@ --- title: Saving Table State -weight: 5 +weight: 6 --- There may be occasions that you'd like to save the table state, for example if you have a complex set of filters, search parameters, or simply to remember which page you were on! diff --git a/docs/start/commands.md b/docs/start/commands.md index 26e62788d..da02db15b 100644 --- a/docs/start/commands.md +++ b/docs/start/commands.md @@ -7,7 +7,7 @@ weight: 6 To generate a new datatable component you can use the `make:datatable` command: -Create a new datatable component called `UserTable` in `App\Http\Livewire` that uses the `App\Models\User` model. +Create a new datatable component called `UserTable` in `App\Livewire` that uses the `App\Models\User` model. ```bash php artisan make:datatable UserTable User @@ -17,7 +17,7 @@ php artisan make:datatable UserTable User You may pass a Custom Path to your model, should it not be contained within the "App" or "App\Models" namespaces: -Create a new datatable component called `TestTable` in `App\Http\Livewire` that uses the `App\Domains\Test\Models\Example` model. +Create a new datatable component called `TestTable` in `App\Livewire` that uses the `App\Domains\Test\Models\Example` model. ```bash php artisan make:datatable TestTable example app/Domains/Test/Models/ diff --git a/docs/start/rendering.md b/docs/start/rendering.md index 5694923fd..e21320aca 100644 --- a/docs/start/rendering.md +++ b/docs/start/rendering.md @@ -5,9 +5,9 @@ weight: 5 ## Rendering Components -You render components the same way you [render](https://laravel-livewire.com/docs/2.x/rendering-components) any Livewire component. +You render components the same way you [render](https://livewire.laravel.com/docs/components#rendering-components) any Livewire component. -Your component at `App\Http\Livewire\UsersTable.php` +Your component at `App\Livewire\UsersTable.php` ```html @@ -24,7 +24,7 @@ By default, all components will use the theme in the config file. But if for som ## Using sub-folders -If your component does not live in `App\Http\Livewire`, you can specify a different sub-folder. For example if your component lives in `App\Http\Livewire\Backend\Users` you would use the following: +If your component does not live in `App\Livewire`, you can specify a different sub-folder. For example if your component lives in `App\Livewire\Backend\Users` you would use the following: ```html diff --git a/docs/start/requirements.md b/docs/start/requirements.md index 04a236079..1f1a76054 100644 --- a/docs/start/requirements.md +++ b/docs/start/requirements.md @@ -7,4 +7,4 @@ The following are required to use this package regardless of what theme you choo - PHP 8.1+ - [Laravel 10.x](https://laravel.com) -- [Laravel Livewire 3.x](https://laravel-livewire.com) +- [Laravel Livewire 3.x](https://livewire.laravel.com) diff --git a/docs/usage/creating-components.md b/docs/usage/creating-components.md index d58ed79e3..b5173e2ca 100644 --- a/docs/usage/creating-components.md +++ b/docs/usage/creating-components.md @@ -10,7 +10,7 @@ This is what a bare bones component looks like before your customization: ```php 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} \ 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;--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 diff --git a/resources/views/components/includes/loading.blade.php b/resources/views/components/includes/loading.blade.php new file mode 100644 index 000000000..7c71772ed --- /dev/null +++ b/resources/views/components/includes/loading.blade.php @@ -0,0 +1,35 @@ +@aware(['isTailwind', 'isBootstrap', 'tableName', 'component']) +@props(['colCount' => 1]) + +@php +$customAttributes['loader-wrapper'] = $component->getLoadingPlaceHolderWrapperAttributes(); +$customAttributes['loader-icon'] = $component->getLoadingPlaceHolderIconAttributes(); +@endphp +@if($this->hasLoadingPlaceholderBlade()) + @include($this->getLoadingPlaceHolderBlade(), ['colCount' => $colCount]) +@else + + merge($customAttributes['loader-wrapper']) + ->class(['w-full text-center h-screen place-items-center align-middle' => $isTailwind && ($customAttributes['loader-wrapper']['default'] ?? true)]) + ->class(['w-100 text-center h-100 align-items-center' => $isBootstrap && ($customAttributes['loader-wrapper']['default'] ?? true)]); + }} + wire:loading.class.remove="hidden d-none" + > + +
+
merge($customAttributes['loader-icon']) + ->class(['lds-hourglass' => $isTailwind && ($customAttributes['loader-icon']['default'] ?? true)]) + ->class(['lds-hourglass' => $isBootstrap && ($customAttributes['loader-icon']['default'] ?? true)]) + ->except('default'); + }} + >
+
{{ $component->getLoadingPlaceholderContent() }}
+
+ + + +@endif diff --git a/resources/views/components/table/tr.blade.php b/resources/views/components/table/tr.blade.php index 92d1326ea..fba5f1601 100644 --- a/resources/views/components/table/tr.blade.php +++ b/resources/views/components/table/tr.blade.php @@ -2,19 +2,23 @@ @props(['row', 'rowIndex']) @php - $customAttributes = $this->getTrAttributes($row, $rowIndex); + $customAttributes = $component->getTrAttributes($row, $rowIndex); @endphp hasDisplayLoadingPlaceholder()) + wire:loading.remove + @else wire:loading.class.delay="opacity-50 dark:bg-gray-900 dark:opacity-60" - id="{{ $tableName }}-row-{{ $row->{$this->getPrimaryKey()} }}" + @endif + id="{{ $tableName }}-row-{{ $row->{$component->getPrimaryKey()} }}" :draggable="currentlyReorderingStatus" - wire:key="{{ $tableName }}-tablerow-tr-{{ $row->{$this->getPrimaryKey()} }}" + wire:key="{{ $tableName }}-tablerow-tr-{{ $row->{$component->getPrimaryKey()} }}" loopType="{{ ($rowIndex % 2 === 0) ? 'even' : 'odd' }}" {{ $attributes->merge($customAttributes) diff --git a/resources/views/datatable.blade.php b/resources/views/datatable.blade.php index 1d6307f94..36f2de741 100644 --- a/resources/views/datatable.blade.php +++ b/resources/views/datatable.blade.php @@ -30,6 +30,10 @@ @if($this->secondaryHeaderIsEnabled() && $this->hasColumnsWithSecondaryHeader()) @endif + @if($this->hasDisplayLoadingPlaceholder()) + + @endif + diff --git a/src/DataTableComponent.php b/src/DataTableComponent.php index 7733b00c0..ebe33c7a2 100644 --- a/src/DataTableComponent.php +++ b/src/DataTableComponent.php @@ -14,6 +14,7 @@ use Rappasoft\LaravelLivewireTables\Traits\WithEvents; use Rappasoft\LaravelLivewireTables\Traits\WithFilters; use Rappasoft\LaravelLivewireTables\Traits\WithFooter; +use Rappasoft\LaravelLivewireTables\Traits\WithLoadingPlaceholder; use Rappasoft\LaravelLivewireTables\Traits\WithPagination; use Rappasoft\LaravelLivewireTables\Traits\WithRefresh; use Rappasoft\LaravelLivewireTables\Traits\WithReordering; @@ -32,6 +33,7 @@ abstract class DataTableComponent extends Component WithEvents, WithFilters, WithFooter, + WithLoadingPlaceholder, WithPagination, WithRefresh, WithReordering, diff --git a/src/LaravelLivewireTablesServiceProvider.php b/src/LaravelLivewireTablesServiceProvider.php index 5c8ee96d5..cd8e3eb5f 100644 --- a/src/LaravelLivewireTablesServiceProvider.php +++ b/src/LaravelLivewireTablesServiceProvider.php @@ -20,7 +20,15 @@ public function boot(): void __DIR__.'/../config/livewire-tables.php', 'livewire-tables' ); - $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'livewire-tables'); + // Load Default Translations + $this->loadJsonTranslationsFrom( + __DIR__.'/../resources/lang' + ); + + // Override if Published + $this->loadJsonTranslationsFrom( + $this->app->langPath('vendor/rappasoft/livewire-tables') + ); $this->loadViewsFrom(__DIR__.'/../resources/views', 'livewire-tables'); @@ -35,8 +43,9 @@ public function boot(): void public function consoleCommands() { if ($this->app->runningInConsole()) { + $this->publishes([ - __DIR__.'/../resources/lang' => $this->app->langPath('livewire-tables'), + __DIR__.'/../resources/lang' => $this->app->langPath('vendor/rappasoft/livewire-tables'), ], 'livewire-tables-translations'); $this->publishes([ @@ -44,7 +53,7 @@ public function consoleCommands() ], 'livewire-tables-config'); $this->publishes([ - __DIR__.'/../resources/views' => resource_path('views/vendor/livewire-tables'), + __DIR__.'/../resources/views' => resource_path('views/vendor/rappasoft/livewire-tables'), ], 'livewire-tables-views'); $this->publishes([ diff --git a/src/Traits/Configuration/LoadingPlaceholderConfiguration.php b/src/Traits/Configuration/LoadingPlaceholderConfiguration.php new file mode 100644 index 000000000..a88bcccda --- /dev/null +++ b/src/Traits/Configuration/LoadingPlaceholderConfiguration.php @@ -0,0 +1,62 @@ +displayLoadingPlaceholder = $status; + + return $this; + } + + public function setLoadingPlaceholderEnabled(): self + { + $this->setLoadingPlaceholderStatus(true); + + return $this; + } + + public function setLoadingPlaceholderDisabled(): self + { + $this->setLoadingPlaceholderStatus(false); + + return $this; + } + + public function setLoadingPlaceholderContent(string $content): self + { + $this->loadingPlaceholderContent = $content; + + return $this; + } + + public function setLoadingPlaceHolderAttributes(array $attributes): self + { + $this->loadingPlaceHolderAttributes = $attributes; + + return $this; + } + + public function setLoadingPlaceHolderIconAttributes(array $attributes): self + { + $this->loadingPlaceHolderIconAttributes = $attributes; + + return $this; + } + + public function setLoadingPlaceHolderWrapperAttributes(array $attributes): self + { + $this->loadingPlaceHolderWrapperAttributes = $attributes; + + return $this; + } + + public function setLoadingPlaceholderBlade(string $customBlade): self + { + $this->loadingPlaceholderBlade = $customBlade; + + return $this; + } +} diff --git a/src/Traits/Helpers/LoadingPlaceholderHelpers.php b/src/Traits/Helpers/LoadingPlaceholderHelpers.php new file mode 100644 index 000000000..4a5a9e7e7 --- /dev/null +++ b/src/Traits/Helpers/LoadingPlaceholderHelpers.php @@ -0,0 +1,47 @@ +getDisplayLoadingPlaceholder(); + } + + public function getDisplayLoadingPlaceholder(): bool + { + return $this->displayLoadingPlaceholder; + } + + public function getLoadingPlaceholderContent(): string + { + return $this->loadingPlaceholderContent ?? __('livewire-tables:loading'); + } + + public function getLoadingPlaceholderAttributes(): array + { + return count($this->loadingPlaceHolderAttributes) ? $this->loadingPlaceHolderAttributes : ['default' => true]; + + } + + public function getLoadingPlaceHolderIconAttributes(): array + { + return count($this->loadingPlaceHolderIconAttributes) ? $this->loadingPlaceHolderIconAttributes : ['default' => true]; + } + + public function getLoadingPlaceHolderWrapperAttributes(): array + { + return count($this->loadingPlaceHolderWrapperAttributes) ? $this->loadingPlaceHolderWrapperAttributes : ['default' => true]; + } + + public function hasLoadingPlaceholderBlade(): bool + { + return ! is_null($this->getLoadingPlaceHolderBlade()); + } + + public function getLoadingPlaceHolderBlade(): ?string + { + return $this->loadingPlaceholderBlade; + } +} diff --git a/src/Traits/WithLoadingPlaceholder.php b/src/Traits/WithLoadingPlaceholder.php new file mode 100644 index 000000000..47c3a3072 --- /dev/null +++ b/src/Traits/WithLoadingPlaceholder.php @@ -0,0 +1,24 @@ +setPrimaryKey('id') + ->setLoadingPlaceholderEnabled() + ->setLoadingPlaceholderContent('TestLoadingPlaceholderContentTestTest'); + } + + 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); + }), + NumberFilter::make('Breed ID', 'breed_id_filter') + ->filter(function (Builder $builder, string $value) { + return $builder->where('breed_id', '=', $value); + }), + + TextFilter::make('Pet Name', 'pet_name_filter') + ->filter(function (Builder $builder, string $value) { + return $builder->where('pets.name', '=', $value); + }), + + DateFilter::make('Last Visit After Date', 'last_visit_date_filter') + ->filter(function (Builder $builder, string $value) { + return $builder->whereDate('pets.last_visit', '=>', $value); + }), + + DateTimeFilter::make('Last Visit Before DateTime', 'last_visit_datetime_filter') + ->filter(function (Builder $builder, string $value) { + return $builder->whereDate('pets.last_visit', '<=', $value); + }), + + SelectFilter::make('Breed SelectFilter', 'breed_select_filter') + ->options( + Breed::query() + ->orderBy('name') + ->get() + ->keyBy('id') + ->map(fn ($breed) => $breed->name) + ->toArray() + ) + ->filter(function (Builder $builder, string $value) { + return $builder->where('breed_id', $value); + }) + ->setCustomFilterLabel('livewire-tables::tests.testFilterLabel') + ->setFilterPillBlade('livewire-tables::tests.testFilterPills'), + ]; + } +} diff --git a/tests/Traits/Configuration/LoadingPlaceholderConfigurationTest.php b/tests/Traits/Configuration/LoadingPlaceholderConfigurationTest.php new file mode 100644 index 000000000..e5b033f99 --- /dev/null +++ b/tests/Traits/Configuration/LoadingPlaceholderConfigurationTest.php @@ -0,0 +1,99 @@ +assertFalse($this->basicTable->hasDisplayLoadingPlaceholder()); + + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertTrue($this->basicTable->hasDisplayLoadingPlaceholder()); + + } + + /** @test */ + public function can_set_loading_placeholder_status_disabled(): void + { + $this->assertFalse($this->basicTable->hasDisplayLoadingPlaceholder()); + + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertTrue($this->basicTable->hasDisplayLoadingPlaceholder()); + + $this->basicTable->setLoadingPlaceholderDisabled(); + + $this->assertFalse($this->basicTable->hasDisplayLoadingPlaceholder()); + + } + + /** @test */ + public function can_set_loading_placeholder_content(): void + { + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertTrue($this->basicTable->hasDisplayLoadingPlaceholder()); + + $this->assertSame('Loading', $this->basicTable->getLoadingPlaceholderContent()); + + $this->basicTable->setLoadingPlaceholderContent('LoadingConfigurationTest - LoadingLoadingLoading'); + + $this->assertSame('LoadingConfigurationTest - LoadingLoadingLoading', $this->basicTable->getLoadingPlaceholderContent()); + + } + + /** @test */ + public function can_set_loading_placeholder_attributes(): void + { + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertSame(['default' => true], $this->basicTable->getLoadingPlaceHolderAttributes()); + + $this->basicTable->setLoadingPlaceHolderAttributes(['class' => 'test12345']); + + $this->assertSame(['class' => 'test12345'], $this->basicTable->getLoadingPlaceHolderAttributes()); + + } + + /** @test */ + public function can_set_loading_placeholder_icon_attributes(): void + { + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertSame(['default' => true], $this->basicTable->getLoadingPlaceHolderIconAttributes()); + + $this->basicTable->setLoadingPlaceHolderIconAttributes(['class' => 'test123']); + + $this->assertSame(['class' => 'test123'], $this->basicTable->getLoadingPlaceHolderIconAttributes()); + + } + + /** @test */ + public function can_set_loading_placeholder_wrapper_attributes(): void + { + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertSame(['default' => true], $this->basicTable->getLoadingPlaceHolderWrapperAttributes()); + + $this->basicTable->setLoadingPlaceHolderWrapperAttributes(['class' => 'test1234567-wrapper']); + + $this->assertSame(['class' => 'test1234567-wrapper'], $this->basicTable->getLoadingPlaceHolderWrapperAttributes()); + } + + /** @test */ + public function can_set_loading_placeholder_custom_blade(): void + { + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertNull($this->basicTable->getLoadingPlaceHolderBlade()); + + $this->basicTable->setLoadingPlaceholderBlade('test-blade'); + + $this->assertSame('test-blade', $this->basicTable->getLoadingPlaceHolderBlade()); + } +} diff --git a/tests/Traits/Helpers/LoadingPlaceholderHelpersTest.php b/tests/Traits/Helpers/LoadingPlaceholderHelpersTest.php new file mode 100644 index 000000000..5d3b92c28 --- /dev/null +++ b/tests/Traits/Helpers/LoadingPlaceholderHelpersTest.php @@ -0,0 +1,21 @@ +assertFalse($this->basicTable->hasDisplayLoadingPlaceholder()); + + $this->basicTable->setLoadingPlaceholderEnabled(); + + $this->assertTrue($this->basicTable->getDisplayLoadingPlaceholder()); + + $this->assertTrue($this->basicTable->hasDisplayLoadingPlaceholder()); + + } +} diff --git a/tests/Traits/Visuals/LoadingPlaceholderVisualsTest.php b/tests/Traits/Visuals/LoadingPlaceholderVisualsTest.php new file mode 100644 index 000000000..04cf3e788 --- /dev/null +++ b/tests/Traits/Visuals/LoadingPlaceholderVisualsTest.php @@ -0,0 +1,52 @@ +call('setPerPageAccepted', [1, 5, 10]) + ->assertSeeHtml('tr wire:key="table-loader" class="hidden d-none"') + ->call('setPerPage', 5); + } + + /** @test */ + public function can_see_placeholder_custom_text(): void + { + Livewire::test(PetsTableLoadingPlaceholder::class) + ->call('setPerPageAccepted', [1, 5, 10]) + ->assertSeeHtmlInOrder([ + 'TestLoadingPlaceholderContentTestTest', + ]) + ->call('setPerPage', 5); + } + + /** @test */ + public function can_see_correct_placeholder_text_visually(): void + { + Livewire::test(PetsTableLoadingPlaceholder::class) + ->call('setPerPageAccepted', [1, 5, 10]) + ->assertSee('TestLoadingPlaceholderContentTestTest') + ->call('setPerPage', 5); + } + + /** @test */ + public function cannot_see_incorrect_placeholder_text_visually(): void + { + Livewire::test(PetsTableLoadingPlaceholder::class) + ->call('setPerPageAccepted', [1, 5, 10]) + ->assertDontSee('TestLoadingPlaceholderContentTestTest22') + ->call('setPerPage', 5); + } +}