diff --git a/.gitattributes b/.gitattributes index b15bffdc8..8a9c5c832 100644 --- a/.gitattributes +++ b/.gitattributes @@ -32,4 +32,7 @@ /resources/css/laravel-livewire-tables.css export-ignore /resources/css/numberRange.css export-ignore /resources/css/numericSlider.css export-ignore -/resources/css/flatpickr.css export-ignore \ No newline at end of file +/resources/css/flatpickr.css export-ignore +/resources/js/partials/filter-date-range.js export-ignore +/resources/js/partials/filter-number-range.js export-ignore +/resources/js/partials/reorder.js export-ignore diff --git a/CHANGELOG.md b/CHANGELOG.md index d5ebe7647..dbd00df7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `laravel-livewire-tables` will be documented in this file +## UNRELEASED +- Add capability to set a custom script path for the scripts/styles by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1557 +- Added missing tailwind background colour class for when hovering over the clear button in dark mode by @slakbal in https://github.com/rappasoft/laravel-livewire-tables/pull/1553 +- Add capability to hide Column Label by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1512 +- Revert previous splitting of JS Files + ## [v3.1.3] - 2023-11-03 - Add additional Lifecycle Hook by @lrljoe in https://github.com/rappasoft/laravel-livewire-tables/pull/1534 - SettingColumns/ColumnsSet diff --git a/config/livewire-tables.php b/config/livewire-tables.php index 3dbdf1757..30908f118 100644 --- a/config/livewire-tables.php +++ b/config/livewire-tables.php @@ -30,6 +30,11 @@ */ 'enable_blade_directives ' => false, + /** + * Customise Script & Styles Paths + */ + 'script_base_path' => '/rappasoft/laravel-livewire-tables', + /** * Filter Default Configuration Options * diff --git a/docs/columns/anonymous_columns.md b/docs/columns/anonymous_columns.md new file mode 100644 index 000000000..21bde25dd --- /dev/null +++ b/docs/columns/anonymous_columns.md @@ -0,0 +1,132 @@ +--- +title: Anonymous Columns +weight: 9 +--- + +## Introduction + +Sometimes you may need an "anonymous column", or a column that isn't bound to any column in your database. A common +example of this would be an "actions" column at the end of every row with actions of "view", "edit", and/or "delete". +Though, it doesn't have to be an action column, it could be anything. + +By using an anonymous column, you take full control by using your own view component. So if you find the LinkColumn, +ImageColumn, or any of the other columns too restrictive for your needs, then an anonymous column may be what you need. + +To make an anonymous column, you create an anonymous function that returns a view into the `label()` method, which will +remove the requirement for a database column. Thus, making it "anonymous". You can also pass variables to the view by +chaining the `with()` method onto the `view()` method that gets returned by the anonymous function into the `label()`. +So you can either pass specific values, or the whole row itself. Lastly, chain the `html()` method to the column so it +can render your view component as html. + +## Example Action Column + +Here is an example of an action column using FontAwesome icons for the "view", "edit", and "delete" actions. + +In your `DataTableComponent`: + +```php +setPrimaryKey('id'); + $this->setDefaultSort('id', 'asc'); + } + + public function columns(): array + { + return [ + Column::make('id', 'id') + ->sortable() + ->searchable(), + Column::make('Name', 'name') + ->sortable() + ->searchable(), + Column::make('Email', 'email') + ->sortable() + ->searchable(), + Column::make('Registered', 'created_at') + ->sortable(), + Column::make('Updated', 'updated_at') + ->sortable(), + Column::make('Last Login', 'last_login_at') + ->sortable(), + + Column::make('Action') + ->label( + fn ($row, Column $column) => view('components.livewire.datatables.action-column')->with( + [ + 'viewLink' => route('users.view', $row), + 'editLink' => route('users.edit', $row), + 'deleteLink' => route('users.delete', $row), + ] + ) + )->html(), + ]; + } +} +``` + +NOTE: You don't have to pass individual properties like `viewLink` and so on. You could simply +pass the whole record to your view and handle it however you need within the view file. Example: + +```php +Column::make('Action') + ->label( + fn ($row, Column $column) => view('components.livewire.datatables.action-column')->with([ + 'user' => $row, + ]) + )->html(), +``` + +Now in your component's view file you can do something like this: + +```php +
+ @isset ( $viewLink ) + + @endif + + @isset ( $editLink ) + + @endif + + @isset ( $deleteLink ) +
+ @method('DELETE') + @csrf + +
+ @endif +
+``` + +Or, if you passed the whole record, you could use: + +```php + +``` + +## Screenshot + +The final result can look something like this: + +users-table-action-column diff --git a/docs/columns/available-methods.md b/docs/columns/available-methods.md index 23797087d..d7158a04f 100644 --- a/docs/columns/available-methods.md +++ b/docs/columns/available-methods.md @@ -255,3 +255,20 @@ If you are using non-latin characters as the Column Title, you should set a lati Column::make('地址', 'address.address') ->setCustomSlug('Address') ``` + +### Hiding Column Label + +Labels are visible by default, but should you wish to hide the label from the table header, without impacting on wider table behaviour, you may implement the following method: +```php +Column::make('Name') + ->setColumnLabelStatusDisabled() +``` + +### Displaying Column Label + +Labels are visible by default, but should you wish to override a previous "hideColumnLabel()", you may implement the below method: + +```php +Column::make('Name') + ->setColumnLabelStatusEnabled() +``` diff --git a/docs/start/including-assets.md b/docs/start/including-assets.md index 7682c223d..179f5ed55 100644 --- a/docs/start/including-assets.md +++ b/docs/start/including-assets.md @@ -24,6 +24,18 @@ This is enabled by default, but to re-enable, enable the following options in th * Enable or Disable automatic injection of third-party assets */ 'inject_third_party_assets_enabled' => true, + +``` + +#### Changing Script Path + +You can change the path used by customising the script_base_path option in the configuration file: + +```php + /** + * Customise Script & Styles Paths + */ + 'script_base_path' => '/rappasoft/laravel-livewire-tables', ``` ### Bundler Including @@ -61,4 +73,5 @@ Update the following options in the livewire-tables configuration file, to disab */ 'enable_blade_directives ' => false, -``` \ No newline at end of file +``` + diff --git a/resources/js/laravel-livewire-tables.js b/resources/js/laravel-livewire-tables.js index 22e84fe57..532a615ea 100644 --- a/resources/js/laravel-livewire-tables.js +++ b/resources/js/laravel-livewire-tables.js @@ -47,7 +47,121 @@ document.addEventListener('alpine:init', () => { this.selectedItems = [...new Set(tempSelectedItems)]; } })); - + Alpine.data('numberRangeFilter', (wire, filterKey, parentElementPath, filterConfig, childElementRoot) => ({ + allFilters: wire.entangle('filterComponents', false), + originalMin: 0, + originalMax: 100, + filterMin: 0, + filterMax: 100, + currentMin: 0, + currentMax: 100, + hasUpdate: false, + wireValues: wire.entangle('filterComponents.' + filterKey, false), + defaultMin: filterConfig['minRange'], + defaultMax: filterConfig['maxRange'], + restrictUpdates: false, + updateStyles() { + 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)); + }, + setupWire() { + if (this.wireValues !== undefined) { + this.filterMin = this.originalMin = (this.wireValues['min'] !== undefined) ? this.wireValues['min'] : this.defaultMin; + this.filterMax = this.originalMax = (this.wireValues['max'] !== undefined) ? this.wireValues['max'] : this.defaultMax; + } else { + this.filterMin = this.originalMin = this.defaultMin; + this.filterMax = this.originalMax = this.defaultMax; + } + this.updateStyles(); + }, + allowUpdates() { + this.updateWire(); + }, + updateWire() { + let tmpFilterMin = parseInt(this.filterMin); + let tmpFilterMax = parseInt(this.filterMax); + + if (tmpFilterMin != this.originalMin || tmpFilterMax != this.originalMax) { + if (tmpFilterMax < tmpFilterMin) { + this.filterMin = tmpFilterMax; + this.filterMax = tmpFilterMin; + } + this.hasUpdate = true; + this.originalMin = tmpFilterMin; + this.originalMax = tmpFilterMax; + } + this.updateStyles(); + }, + updateWireable() { + if (this.hasUpdate) { + this.hasUpdate = false; + this.wireValues = { 'min': this.filterMin, 'max': this.filterMax }; + wire.set('filterComponents.' + filterKey, this.wireValues); + } + + }, + init() { + this.setupWire(); + this.$watch('allFilters', value => this.setupWire()); + }, + })); + + Alpine.data('flatpickrFilter', (wire, filterKey, filterConfig, refLocation, locale) => ({ + wireValues: wire.entangle('filterComponents.' + filterKey), + flatpickrInstance: flatpickr(refLocation, { + mode: 'range', + clickOpens: true, + 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'], + onOpen: function () { + window.childElementOpen = true; + }, + onChange: function (selectedDates, dateStr, instance) { + if (selectedDates.length > 1) { + var startDate = dateStr.split(' ')[0]; + var endDate = dateStr.split(' ')[2]; + var wireDateArray = {}; + window.childElementOpen = false; + window.filterPopoverOpen = false; + wireDateArray = { 'minDate': startDate, 'maxDate': endDate }; + wire.set('filterComponents.' + filterKey, wireDateArray); + } + } + }), + setupWire() { + if (this.wireValues !== undefined) { + if (this.wireValues.minDate !== undefined && this.wireValues.maxDate !== undefined) { + let initialDateArray = [this.wireValues.minDate, this.wireValues.maxDate]; + this.flatpickrInstance.setDate(initialDateArray); + } + else { + this.flatpickrInstance.setDate([]); + } + } + else { + this.flatpickrInstance.setDate([]); + } + }, + init() { + this.setupWire(); + this.$watch('wireValues', value => this.setupWire()); + } + + + })); Alpine.data('reorderFunction', (wire, tableID, primaryKeyName) => ({ dragging: false, reorderEnabled: false, @@ -78,7 +192,7 @@ document.addEventListener('alpine:init', () => { } let target = event.target.closest('tr'); this.currentlyHighlightedElement = target; - + if (event.offsetY < (target.getBoundingClientRect().height / 2)) { target.classList.add('laravel-livewire-tables-highlight-top'); target.classList.remove('laravel-livewire-tables-highlight-bottom'); @@ -95,7 +209,7 @@ document.addEventListener('alpine:init', () => { if (typeof this.currentlyHighlightedElement == 'object') { this.currentlyHighlightedElement.classList.remove('laravel-livewire-tables-highlight-bottom', 'laravel-livewire-tables-highlight-top'); } - + let target = event.target.closest('tr'); let parent = event.target.closest('tr').parentNode; let element = document.getElementById(this.sourceID).closest('tr'); @@ -113,7 +227,7 @@ document.addEventListener('alpine:init', () => { if (newPosition < originalPosition) { loopStart = newPosition; } - + /* let evenList = parentNode.querySelectorAll("table[tableType='rappasoft-laravel-livewire-tables']>tbody>tr:nth-child(even of tr.rappasoft-striped-row) ").forEach(function (elem) { elem.classList.remove(...this.oddNotInEven); @@ -139,14 +253,14 @@ document.addEventListener('alpine:init', () => { reorderToggle() { if (this.currentlyReorderingStatus) { wire.disableReordering(); - + } else { if (this.hideReorderColumnUnlessReorderingStatus) { this.reorderDisplayColumn = true; } wire.enableReordering(); - + } }, cancelReorder() { @@ -154,23 +268,23 @@ document.addEventListener('alpine:init', () => { this.reorderDisplayColumn = false; } wire.disableReordering(); - + }, updateOrderedItems() { let table = document.getElementById(tableID); let orderedRows = []; for (let i = 1, row; row = table.rows[i]; i++) { - orderedRows.push({ [primaryKeyName]: row.getAttribute('rowpk'), [this.defaultReorderColumn]: i }); + orderedRows.push({ [primaryKeyName]: row.getAttribute('rowpk'), [this.defaultReorderColumn]: i }); } wire.storeReorder(orderedRows); }, setupEvenOddClasses() { if (this.currentlyReorderingStatus === true) { - + let tbody = document.getElementById(tableID).getElementsByTagName('tbody')[0]; let evenRowClassArray = []; let oddRowClassArray = []; - + if (tbody.rows[0] !== undefined && tbody.rows[1] !== undefined) { evenRowClassArray = Array.from(tbody.rows[0].classList); oddRowClassArray = Array.from(tbody.rows[1].classList); @@ -183,124 +297,8 @@ document.addEventListener('alpine:init', () => { }, init() { this.$watch('currentlyReorderingStatus', value => this.setupEvenOddClasses()); - + } })); - - Alpine.data('numberRangeFilter', (wire, filterKey, parentElementPath, filterConfig, childElementRoot) => ({ - allFilters: wire.entangle('filterComponents', true), - originalMin: 0, - originalMax: 100, - filterMin: 0, - filterMax: 100, - currentMin: 0, - currentMax: 100, - hasUpdate: false, - wireValues: wire.entangle('filterComponents.' + filterKey, true), - defaultMin: filterConfig['minRange'], - defaultMax: filterConfig['maxRange'], - restrictUpdates: false, - updateStyles() { - 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)); - }, - setupWire() { - if (this.wireValues !== undefined) { - this.filterMin = this.originalMin = (this.wireValues['min'] !== undefined) ? this.wireValues['min'] : this.defaultMin; - this.filterMax = this.originalMax = (this.wireValues['max'] !== undefined) ? this.wireValues['max'] : this.defaultMax; - } else { - this.filterMin = this.originalMin = this.defaultMin; - this.filterMax = this.originalMax = this.defaultMax; - } - this.updateStyles(); - }, - allowUpdates() { - this.updateWire(); - }, - updateWire() { - let tmpFilterMin = parseInt(this.filterMin); - let tmpFilterMax = parseInt(this.filterMax); - - if (tmpFilterMin != this.originalMin || tmpFilterMax != this.originalMax) { - if (tmpFilterMax < tmpFilterMin) { - this.filterMin = tmpFilterMax; - this.filterMax = tmpFilterMin; - } - this.hasUpdate = true; - this.originalMin = tmpFilterMin; - this.originalMax = tmpFilterMax; - } - this.updateStyles(); - }, - updateWireable() { - if (this.hasUpdate) { - this.hasUpdate = false; - this.wireValues = { 'min': this.filterMin, 'max': this.filterMax }; - } - - }, - init() { - this.setupWire(); - this.$watch('allFilters', value => this.setupWire()); - }, - })); - - Alpine.data('flatpickrFilter', (wire, filterKey, filterConfig, refLocation, locale) => ({ - wireValues: wire.entangle('filterComponents.' + filterKey, true), - flatpickrInstance: flatpickr(refLocation, { - mode: 'range', - clickOpens: true, - 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'], - onOpen: function () { - window.childElementOpen = true; - }, - onChange: function (selectedDates, dateStr, instance) { - if (selectedDates.length > 1) { - var startDate = dateStr.split(' ')[0]; - var endDate = dateStr.split(' ')[2]; - var wireDateArray = {}; - window.childElementOpen = false; - window.filterPopoverOpen = false; - wireDateArray = { 'minDate': startDate, 'maxDate': endDate }; - wire.set('filterComponents.' + filterKey, wireDateArray); - } - } - }), - setupWire() { - if (this.wireValues !== undefined) { - if (this.wireValues.minDate !== undefined && this.wireValues.maxDate !== undefined) { - let initialDateArray = [this.wireValues.minDate, this.wireValues.maxDate]; - this.flatpickrInstance.setDate(initialDateArray); - } - else { - this.flatpickrInstance.setDate([]); - } - } - else { - this.flatpickrInstance.setDate([]); - } - }, - init() { - this.setupWire(); - this.$watch('wireValues', value => this.setupWire()); - } - - - })); - - -}); + +}); \ 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 efc7239c1..6d78ed178 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"),alwaysShowBulkActions:!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("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())}})),Alpine.data("numberRangeFilter",(e,t,i,l,a)=>({allFilters:e.entangle("filterComponents",!0),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:e.entangle("filterComponents."+t,!0),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,!0),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())}}))}); \ 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"),alwaysShowBulkActions:!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 diff --git a/resources/js/partials/filter-date-range.js b/resources/js/partials/filter-date-range.js new file mode 100644 index 000000000..4755b5d17 --- /dev/null +++ b/resources/js/partials/filter-date-range.js @@ -0,0 +1,57 @@ +/*jshint esversion: 6 */ + +function fpf() { + Alpine.data('flatpickrFilter', (wire, filterKey, filterConfig, refLocation, locale) => ({ + wireValues: wire.entangle('filterComponents.' + filterKey), + flatpickrInstance: flatpickr(refLocation, { + mode: 'range', + clickOpens: true, + 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'], + onOpen: function () { + window.childElementOpen = true; + }, + onChange: function (selectedDates, dateStr, instance) { + if (selectedDates.length > 1) { + var startDate = dateStr.split(' ')[0]; + var endDate = dateStr.split(' ')[2]; + var wireDateArray = {}; + window.childElementOpen = false; + window.filterPopoverOpen = false; + wireDateArray = { 'minDate': startDate, 'maxDate': endDate }; + wire.set('filterComponents.' + filterKey, wireDateArray); + } + } + }), + setupWire() { + if (this.wireValues !== undefined) { + if (this.wireValues.minDate !== undefined && this.wireValues.maxDate !== undefined) { + let initialDateArray = [this.wireValues.minDate, this.wireValues.maxDate]; + this.flatpickrInstance.setDate(initialDateArray); + } + else { + this.flatpickrInstance.setDate([]); + } + } + else { + this.flatpickrInstance.setDate([]); + } + }, + init() { + this.setupWire(); + this.$watch('wireValues', value => this.setupWire()); + } + + +})); +} + +export default fpf; \ No newline at end of file diff --git a/resources/js/partials/filter-date-range.min.js b/resources/js/partials/filter-date-range.min.js new file mode 100644 index 000000000..5178ea8c6 --- /dev/null +++ b/resources/js/partials/filter-date-range.min.js @@ -0,0 +1 @@ +function fpf(){Alpine.data("flatpickrFilter",(t,e,a,i,l)=>({wireValues:t.entangle("filterComponents."+e),flatpickrInstance:flatpickr(i,{mode:"range",clickOpens:!0,allowInvalidPreload:!0,defaultDate:[],ariaDateFormat:a.ariaDateFormat,allowInput:a.allowInput,altFormat:a.altFormat,altInput:a.altInput,dateFormat:a.dateFormat,locale:"en",minDate:a.earliestDate,maxDate:a.latestDate,onOpen:function(){window.childElementOpen=!0},onChange:function(a,i,l){if(a.length>1){var n=i.split(" ")[0],s=i.split(" ")[2],r={};window.childElementOpen=!1,window.filterPopoverOpen=!1,r={minDate:n,maxDate:s},t.set("filterComponents."+e,r)}}}),setupWire(){if(void 0!==this.wireValues){if(void 0!==this.wireValues.minDate&&void 0!==this.wireValues.maxDate){let t=[this.wireValues.minDate,this.wireValues.maxDate];this.flatpickrInstance.setDate(t)}else this.flatpickrInstance.setDate([])}else this.flatpickrInstance.setDate([])},init(){this.setupWire(),this.$watch("wireValues",t=>this.setupWire())}}))}export default fpf; \ No newline at end of file diff --git a/resources/js/partials/filter-number-range.js b/resources/js/partials/filter-number-range.js new file mode 100644 index 000000000..8aff53043 --- /dev/null +++ b/resources/js/partials/filter-number-range.js @@ -0,0 +1,68 @@ +/*jshint esversion: 6 */ +export function nrf() { + Alpine.data('numberRangeFilter', (wire, filterKey, parentElementPath, filterConfig, childElementRoot) => ({ + allFilters: wire.entangle('filterComponents', false), + originalMin: 0, + originalMax: 100, + filterMin: 0, + filterMax: 100, + currentMin: 0, + currentMax: 100, + hasUpdate: false, + wireValues: wire.entangle('filterComponents.' + filterKey, false), + defaultMin: filterConfig['minRange'], + defaultMax: filterConfig['maxRange'], + restrictUpdates: false, + updateStyles() { + 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)); + }, + setupWire() { + if (this.wireValues !== undefined) { + this.filterMin = this.originalMin = (this.wireValues['min'] !== undefined) ? this.wireValues['min'] : this.defaultMin; + this.filterMax = this.originalMax = (this.wireValues['max'] !== undefined) ? this.wireValues['max'] : this.defaultMax; + } else { + this.filterMin = this.originalMin = this.defaultMin; + this.filterMax = this.originalMax = this.defaultMax; + } + this.updateStyles(); + }, + allowUpdates() { + this.updateWire(); + }, + updateWire() { + let tmpFilterMin = parseInt(this.filterMin); + let tmpFilterMax = parseInt(this.filterMax); + + if (tmpFilterMin != this.originalMin || tmpFilterMax != this.originalMax) { + if (tmpFilterMax < tmpFilterMin) { + this.filterMin = tmpFilterMax; + this.filterMax = tmpFilterMin; + } + this.hasUpdate = true; + this.originalMin = tmpFilterMin; + this.originalMax = tmpFilterMax; + } + this.updateStyles(); + }, + updateWireable() { + if (this.hasUpdate) { + this.hasUpdate = false; + this.wireValues = { 'min': this.filterMin, 'max': this.filterMax }; + wire.set('filterComponents.' + filterKey, this.wireValues); + } + + }, + init() { + this.setupWire(); + this.$watch('allFilters', value => this.setupWire()); + }, +})); +} + +export default nrf; diff --git a/resources/js/partials/filter-number-range.min.js b/resources/js/partials/filter-number-range.min.js new file mode 100644 index 000000000..40f63cc71 --- /dev/null +++ b/resources/js/partials/filter-number-range.min.js @@ -0,0 +1 @@ +export function nrf(){Alpine.data("numberRangeFilter",(t,i,e,a,s)=>({allFilters:t.entangle("filterComponents",!1),originalMin:0,originalMax:100,filterMin:0,filterMax:100,currentMin:0,currentMax:100,hasUpdate:!1,wireValues:t.entangle("filterComponents."+i,!1),defaultMin:a.minRange,defaultMax:a.maxRange,restrictUpdates:!1,updateStyles(){let t=document.getElementById(e),i=document.getElementById(s+"-min"),a=document.getElementById(s+"-max");t.style.setProperty("--value-a",i.value),t.style.setProperty("--text-value-a",JSON.stringify(i.value)),t.style.setProperty("--value-b",a.value),t.style.setProperty("--text-value-b",JSON.stringify(a.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 t=parseInt(this.filterMin),i=parseInt(this.filterMax);(t!=this.originalMin||i!=this.originalMax)&&(ithis.setupWire())}}))}export default nrf; \ No newline at end of file diff --git a/resources/js/partials/reorder.js b/resources/js/partials/reorder.js new file mode 100644 index 000000000..b6ede2ec0 --- /dev/null +++ b/resources/js/partials/reorder.js @@ -0,0 +1,143 @@ +/*jshint esversion: 6 */ + +function tableReorder() { + Alpine.data('reorderFunction', (wire, tableID, primaryKeyName) => ({ + dragging: false, + reorderEnabled: false, + sourceID: '', + targetID: '', + evenRowClasses: '', + oddRowClasses: '', + currentlyHighlightedElement: '', + evenRowClassArray: {}, + oddRowClassArray: {}, + evenNotInOdd: {}, + oddNotInEven: {}, + orderedRows: [], + defaultReorderColumn: wire.get('defaultReorderColumn'), + reorderStatus: wire.get('reorderStatus'), + currentlyReorderingStatus: wire.entangle('currentlyReorderingStatus'), + hideReorderColumnUnlessReorderingStatus: wire.entangle('hideReorderColumnUnlessReorderingStatus'), + reorderDisplayColumn: wire.entangle('reorderDisplayColumn'), + dragStart(event) { + this.sourceID = event.target.id; + event.dataTransfer.effectAllowed = 'move'; + event.dataTransfer.setData('text/plain', event.target.id); + event.target.classList.add("laravel-livewire-tables-dragging"); + }, + dragOverEvent(event) { + if (typeof this.currentlyHighlightedElement == 'object') { + this.currentlyHighlightedElement.classList.remove('laravel-livewire-tables-highlight-bottom', 'laravel-livewire-tables-highlight-top'); + } + let target = event.target.closest('tr'); + this.currentlyHighlightedElement = target; + + if (event.offsetY < (target.getBoundingClientRect().height / 2)) { + target.classList.add('laravel-livewire-tables-highlight-top'); + target.classList.remove('laravel-livewire-tables-highlight-bottom'); + } + else { + target.classList.remove('laravel-livewire-tables-highlight-top'); + target.classList.add('laravel-livewire-tables-highlight-bottom'); + } + }, + dragLeaveEvent(event) { + event.target.closest('tr').classList.remove('laravel-livewire-tables-highlight-bottom', 'laravel-livewire-tables-highlight-top'); + }, + dropEvent(event) { + if (typeof this.currentlyHighlightedElement == 'object') { + this.currentlyHighlightedElement.classList.remove('laravel-livewire-tables-highlight-bottom', 'laravel-livewire-tables-highlight-top'); + } + + let target = event.target.closest('tr'); + let parent = event.target.closest('tr').parentNode; + let element = document.getElementById(this.sourceID).closest('tr'); + element.classList.remove("laravel-livewire-table-dragging"); + let originalPosition = element.rowIndex; + let newPosition = target.rowIndex; + let table = document.getElementById(tableID); + let loopStart = originalPosition; + if (event.offsetY > (target.getBoundingClientRect().height / 2)) { + parent.insertBefore(element, target.nextSibling); + } + else { + parent.insertBefore(element, target); + } + if (newPosition < originalPosition) { + loopStart = newPosition; + } + + /* + let evenList = parentNode.querySelectorAll("table[tableType='rappasoft-laravel-livewire-tables']>tbody>tr:nth-child(even of tr.rappasoft-striped-row) ").forEach(function (elem) { + elem.classList.remove(...this.oddNotInEven); + row.classList.add(...this.evenNotInOdd); + }); + */ + let nextLoop = 'even'; + for (let i = 1, row; row = table.rows[i]; i++) { + if (!row.classList.contains('hidden') && !row.classList.contains('md:hidden') ) { + if (nextLoop === 'even') { + row.classList.remove(...this.oddNotInEven); + row.classList.add(...this.evenNotInOdd); + nextLoop = 'odd'; + } + else { + row.classList.remove(...this.evenNotInOdd); + row.classList.add(...this.oddNotInEven); + nextLoop = 'even'; + } + } + } + }, + reorderToggle() { + if (this.currentlyReorderingStatus) { + wire.disableReordering(); + + } + else { + if (this.hideReorderColumnUnlessReorderingStatus) { + this.reorderDisplayColumn = true; + } + wire.enableReordering(); + + } + }, + cancelReorder() { + if (this.hideReorderColumnUnlessReorderingStatus) { + this.reorderDisplayColumn = false; + } + wire.disableReordering(); + + }, + updateOrderedItems() { + let table = document.getElementById(tableID); + let orderedRows = []; + for (let i = 1, row; row = table.rows[i]; i++) { + orderedRows.push({ [primaryKeyName]: row.getAttribute('rowpk'), [this.defaultReorderColumn]: i }); + } + wire.storeReorder(orderedRows); + }, + setupEvenOddClasses() { + if (this.currentlyReorderingStatus === true) { + + let tbody = document.getElementById(tableID).getElementsByTagName('tbody')[0]; + let evenRowClassArray = []; + let oddRowClassArray = []; + + if (tbody.rows[0] !== undefined && tbody.rows[1] !== undefined) { + evenRowClassArray = Array.from(tbody.rows[0].classList); + oddRowClassArray = Array.from(tbody.rows[1].classList); + this.evenNotInOdd = evenRowClassArray.filter(element => !oddRowClassArray.includes(element)); + this.oddNotInEven = oddRowClassArray.filter(element => !evenRowClassArray.includes(element)); + evenRowClassArray = [] + oddRowClassArray = [] + } + } + }, + init() { + this.$watch('currentlyReorderingStatus', value => this.setupEvenOddClasses()); + + } + })); +} + export default tableReorder; \ No newline at end of file diff --git a/resources/js/partials/reorder.min.js b/resources/js/partials/reorder.min.js new file mode 100644 index 000000000..c0da29df6 --- /dev/null +++ b/resources/js/partials/reorder.min.js @@ -0,0 +1 @@ +function tableReorder(){Alpine.data("reorderFunction",(e,t,r)=>({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.offsetYr.getBoundingClientRect().height/2?l.insertBefore(s,r.nextSibling):l.insertBefore(s,r),o!l.includes(e)),this.oddNotInEven=l.filter(e=>!r.includes(e)),r=[],l=[])}},init(){this.$watch("currentlyReorderingStatus",e=>this.setupEvenOddClasses())}}))}export default tableReorder; \ No newline at end of file diff --git a/resources/views/components/table.blade.php b/resources/views/components/table.blade.php index f4cfb035d..2e3d20ae4 100644 --- a/resources/views/components/table.blade.php +++ b/resources/views/components/table.blade.php @@ -24,7 +24,7 @@ > merge($customAttributes['thead']) - ->class(['bg-gray-50' => $customAttributes['thead']['default'] ?? true]) + ->class(['bg-gray-50 dark:bg-gray-800' => $customAttributes['thead']['default'] ?? true]) ->except('default') }} > diff --git a/resources/views/components/table/th.blade.php b/resources/views/components/table/th.blade.php index a7ebcd836..ee6333ee6 100644 --- a/resources/views/components/table/th.blade.php +++ b/resources/views/components/table/th.blade.php @@ -18,32 +18,34 @@ ->except('default') }} > - @unless ($component->sortingIsEnabled() && ($column->isSortable() || $column->getSortCallback())) - {{ $column->getTitle() }} - @else - - @endunless + + @if ($direction === 'asc') + + + @elseif ($direction === 'desc') + + + @else + + @endif + + + @endunless + @endif @elseif ($component->isBootstrap()) except('default') }} > - @unless ($component->sortingIsEnabled() && ($column->isSortable() || $column->getSortCallback())) - {{ $column->getTitle() }} - @else -
- {{ $column->getTitle() }} + @if($column->getColumnLabelStatus()) + @unless ($component->sortingIsEnabled() && ($column->isSortable() || $column->getSortCallback())) + {{ $column->getTitle() }} + @else +
+ {{ $column->getTitle() }} - - @if ($direction === 'asc') - - @elseif ($direction === 'desc') - - @else - - @endif - -
- @endunless + + @if ($direction === 'asc') + + @elseif ($direction === 'desc') + + @else + + @endif + +
+ @endunless + @endif @endif diff --git a/resources/views/components/tools/toolbar/items/column-select.blade.php b/resources/views/components/tools/toolbar/items/column-select.blade.php index 9c1f1af60..040677d51 100644 --- a/resources/views/components/tools/toolbar/items/column-select.blade.php +++ b/resources/views/components/tools/toolbar/items/column-select.blade.php @@ -33,7 +33,7 @@ class="inline-flex justify-center px-4 py-2 w-full text-sm font-medium text-gray x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" - class="absolute right-0 z-50 mt-2 w-full bg-white rounded-md divide-y divide-gray-100 ring-1 ring-black ring-opacity-5 shadow-lg origin-top-right md:w-48 focus:outline-none" + class="absolute right-0 z-50 mt-2 w-full rounded-md divide-y divide-gray-100 ring-1 ring-black ring-opacity-5 shadow-lg origin-top-right md:w-48 focus:outline-none" >