diff --git a/packages/tables/dist/index.js b/packages/tables/dist/index.js index 563010312b..0a8c9b5f6e 100644 --- a/packages/tables/dist/index.js +++ b/packages/tables/dist/index.js @@ -1 +1 @@ -(()=>{var g=({canTrackDeselectedRecords:u,currentSelectionLivewireProperty:d,maxSelectableRecords:l,selectsCurrentPageOnly:r,$wire:s})=>({checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:new Set,deselectedRecords:new Set,isTrackingDeselectedRecords:!1,shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,entangledSelectedRecords:d?s.$entangle(d):null,init(){this.livewireId=this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value,s.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),s.$on("scrollToTopOfTable",()=>this.$root.scrollIntoView({block:"start",inline:"nearest"})),d&&(l!==1?this.selectedRecords=new Set(this.entangledSelectedRecords):this.selectedRecords=new Set(this.entangledSelectedRecords?[this.entangledSelectedRecords]:[])),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction(...e){s.set("isTrackingDeselectedTableRecords",this.isTrackingDeselectedRecords,!1),s.set("selectedTableRecords",[...this.selectedRecords],!1),s.set("deselectedTableRecords",[...this.deselectedRecords],!1),s.mountAction(...e)},toggleSelectRecordsOnPage(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecords(e){this.areRecordsSelected(e)?this.deselectRecords(e):this.selectRecords(e)},getSelectedRecordsCount(){return this.isTrackingDeselectedRecords?(this.$refs.allSelectableRecordsCount?.value??this.deselectedRecords.size)-this.deselectedRecords.size:this.selectedRecords.size},getRecordsOnPage(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords(e){l===1&&(this.deselectAllRecords(),e=e.slice(0,1));for(let t of e)if(!this.isRecordSelected(t)){if(this.isTrackingDeselectedRecords){this.deselectedRecords.delete(t);continue}this.selectedRecords.add(t)}this.updatedSelectedRecords()},deselectRecords(e){for(let t of e){if(this.isTrackingDeselectedRecords){this.deselectedRecords.add(t);continue}this.selectedRecords.delete(t)}this.updatedSelectedRecords()},updatedSelectedRecords(){if(l!==1){this.entangledSelectedRecords=[...this.selectedRecords];return}this.entangledSelectedRecords=[...this.selectedRecords][0]??null},toggleSelectedRecord(e){if(this.isRecordSelected(e)){this.deselectRecords([e]);return}this.selectRecords([e])},async selectAllRecords(){if(!u||r){this.isLoading=!0,this.selectedRecords=new Set(await s.getAllSelectableTableRecordKeys()),this.updatedSelectedRecords(),this.isLoading=!1;return}this.isTrackingDeselectedRecords=!0,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},canSelectAllRecords(){if(r){let i=this.getRecordsOnPage();return!this.areRecordsSelected(i)&&this.areRecordsToggleable(i)}let e=parseInt(this.$refs.allSelectableRecordsCount?.value);if(!e)return!1;let t=this.getSelectedRecordsCount();return e===t?!1:l===null||e<=l},deselectAllRecords(){this.isTrackingDeselectedRecords=!1,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},isRecordSelected(e){return this.isTrackingDeselectedRecords?!this.deselectedRecords.has(e):this.selectedRecords.has(e)},areRecordsSelected(e){return e.every(t=>this.isRecordSelected(t))},areRecordsToggleable(e){if(l===null||l===1)return!0;let t=e.filter(i=>this.isRecordSelected(i));return t.length===e.length?!0:this.getSelectedRecordsCount()+(e.length-t.length)<=l},toggleCollapseGroup(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups(){this.collapsedGroups=[]},watchForCheckboxClicks(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let i=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!i.includes(this.lastChecked)){this.lastChecked=t;return}let o=i.indexOf(this.lastChecked),n=i.indexOf(t),a=[o,n].sort((c,R)=>c-R),h=[];for(let c=a[0];c<=a[1];c++)h.push(i[c].value);if(t.checked){if(!this.areRecordsToggleable(h)){t.checked=!1,this.deselectRecords([t.value]);return}this.selectRecords(h)}else this.deselectRecords(h)}this.lastChecked=t}});function f({columns:u,isLive:d}){return{error:void 0,isLoading:!1,columns:u,isLive:d,init(){if(!this.columns||this.columns.length===0){this.columns=[];return}},get groupedColumns(){let l={};return this.columns.filter(r=>r.type==="group").forEach(r=>{l[r.name]=this.calculateGroupedColumns(r)}),l},calculateGroupedColumns(l){if((l?.columns?.filter(i=>!i.isHidden)??[]).length===0)return{hidden:!0,checked:!1,disabled:!1,indeterminate:!1};let s=l.columns.filter(i=>!i.isHidden&&i.isToggleable!==!1);if(s.length===0)return{checked:!0,disabled:!0,indeterminate:!1};let e=s.filter(i=>i.isToggled).length,t=l.columns.filter(i=>!i.isHidden&&i.isToggleable===!1);return e===0&&t.length>0?{checked:!0,disabled:!1,indeterminate:!0}:e===0?{checked:!1,disabled:!1,indeterminate:!1}:e===s.length?{checked:!0,disabled:!1,indeterminate:!1}:{checked:!0,disabled:!1,indeterminate:!0}},getColumn(l,r=null){return r?this.columns.find(e=>e.type==="group"&&e.name===r)?.columns?.find(e=>e.name===l):this.columns.find(s=>s.name===l)},toggleGroup(l){let r=this.columns.find(o=>o.type==="group"&&o.name===l);if(!r?.columns)return;let s=this.calculateGroupedColumns(r);if(s.disabled)return;let t=r.columns.filter(o=>o.isToggleable!==!1).some(o=>o.isToggled),i=s.indeterminate?!0:!t;r.columns.filter(o=>o.isToggleable!==!1).forEach(o=>{o.isToggled=i}),this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager()},toggleColumn(l,r=null){let s=this.getColumn(l,r);!s||s.isToggleable===!1||(s.isToggled=!s.isToggled,this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager())},reorderColumns(l){let r=l.map(s=>s.split("::"));this.reorderTopLevel(r),this.isLive&&this.applyTableColumnManager()},reorderGroupColumns(l,r){let s=this.columns.find(i=>i.type==="group"&&i.name===r);if(!s)return;let e=l.map(i=>i.split("::")),t=[];e.forEach(([i,o])=>{let n=s.columns.find(a=>a.name===o);n&&t.push(n)}),s.columns=t,this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager()},reorderTopLevel(l){let r=this.columns,s=[];l.forEach(([e,t])=>{let i=r.find(o=>e==="group"?o.type==="group"&&o.name===t:e==="column"?o.type!=="group"&&o.name===t:!1);i&&s.push(i)}),this.columns=s},async applyTableColumnManager(){this.isLoading=!0;try{await this.$wire.call("applyTableColumnManager",this.columns),this.error=void 0}catch(l){this.error="Failed to update column visibility",console.error("Table toggle columns error:",l)}finally{this.isLoading=!1}}}}document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentTable",g),window.Alpine.data("filamentTableColumnManager",f)});})(); +(()=>{var R=({areGroupsCollapsedByDefault:o,canTrackDeselectedRecords:h,currentSelectionLivewireProperty:r,maxSelectableRecords:s,selectsCurrentPageOnly:l,$wire:d})=>({checkboxClickController:null,groupVisibility:[],isLoading:!1,selectedRecords:new Set,deselectedRecords:new Set,isTrackingDeselectedRecords:!1,shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,entangledSelectedRecords:r?d.$entangle(r):null,init(){this.livewireId=this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value,d.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),d.$on("scrollToTopOfTable",()=>this.$root.scrollIntoView({block:"start",inline:"nearest"})),r&&(s!==1?this.selectedRecords=new Set(this.entangledSelectedRecords):this.selectedRecords=new Set(this.entangledSelectedRecords?[this.entangledSelectedRecords]:[])),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction(...e){d.set("isTrackingDeselectedTableRecords",this.isTrackingDeselectedRecords,!1),d.set("selectedTableRecords",[...this.selectedRecords],!1),d.set("deselectedTableRecords",[...this.deselectedRecords],!1),d.mountAction(...e)},toggleSelectRecordsOnPage(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecords(e){this.areRecordsSelected(e)?this.deselectRecords(e):this.selectRecords(e)},getSelectedRecordsCount(){return this.isTrackingDeselectedRecords?(this.$refs.allSelectableRecordsCount?.value??this.deselectedRecords.size)-this.deselectedRecords.size:this.selectedRecords.size},getRecordsOnPage(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords(e){s===1&&(this.deselectAllRecords(),e=e.slice(0,1));for(let t of e)if(!this.isRecordSelected(t)){if(this.isTrackingDeselectedRecords){this.deselectedRecords.delete(t);continue}this.selectedRecords.add(t)}this.updatedSelectedRecords()},deselectRecords(e){for(let t of e){if(this.isTrackingDeselectedRecords){this.deselectedRecords.add(t);continue}this.selectedRecords.delete(t)}this.updatedSelectedRecords()},updatedSelectedRecords(){if(s!==1){this.entangledSelectedRecords=[...this.selectedRecords];return}this.entangledSelectedRecords=[...this.selectedRecords][0]??null},toggleSelectedRecord(e){if(this.isRecordSelected(e)){this.deselectRecords([e]);return}this.selectRecords([e])},async selectAllRecords(){if(!h||l){this.isLoading=!0,this.selectedRecords=new Set(await d.getAllSelectableTableRecordKeys()),this.updatedSelectedRecords(),this.isLoading=!1;return}this.isTrackingDeselectedRecords=!0,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},canSelectAllRecords(){if(l){let i=this.getRecordsOnPage();return!this.areRecordsSelected(i)&&this.areRecordsToggleable(i)}let e=parseInt(this.$refs.allSelectableRecordsCount?.value);if(!e)return!1;let t=this.getSelectedRecordsCount();return e===t?!1:s===null||e<=s},deselectAllRecords(){this.isTrackingDeselectedRecords=!1,this.selectedRecords=new Set,this.deselectedRecords=new Set,this.updatedSelectedRecords()},isRecordSelected(e){return this.isTrackingDeselectedRecords?!this.deselectedRecords.has(e):this.selectedRecords.has(e)},areRecordsSelected(e){return e.every(t=>this.isRecordSelected(t))},areRecordsToggleable(e){if(s===null||s===1)return!0;let t=e.filter(i=>this.isRecordSelected(i));return t.length===e.length?!0:this.getSelectedRecordsCount()+(e.length-t.length)<=s},toggleCollapseGroup(e){this.isGroupCollapsed(e)?o?this.groupVisibility.push(e):this.groupVisibility.splice(this.groupVisibility.indexOf(e),1):o?this.groupVisibility.splice(this.groupVisibility.indexOf(e),1):this.groupVisibility.push(e)},isGroupCollapsed(e){return o?!this.groupVisibility.includes(e):this.groupVisibility.includes(e)},resetCollapsedGroups(){this.groupVisibility=[]},watchForCheckboxClicks(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let i=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!i.includes(this.lastChecked)){this.lastChecked=t;return}let n=i.indexOf(this.lastChecked),u=i.indexOf(t),g=[n,u].sort((c,m)=>c-m),a=[];for(let c=g[0];c<=g[1];c++)a.push(i[c].value);if(t.checked){if(!this.areRecordsToggleable(a)){t.checked=!1,this.deselectRecords([t.value]);return}this.selectRecords(a)}else this.deselectRecords(a)}this.lastChecked=t}});function f({columns:o,isLive:h}){return{error:void 0,isLoading:!1,columns:o,isLive:h,init(){if(!this.columns||this.columns.length===0){this.columns=[];return}},get groupedColumns(){let r={};return this.columns.filter(s=>s.type==="group").forEach(s=>{r[s.name]=this.calculateGroupedColumns(s)}),r},calculateGroupedColumns(r){if((r?.columns?.filter(t=>!t.isHidden)??[]).length===0)return{hidden:!0,checked:!1,disabled:!1,indeterminate:!1};let l=r.columns.filter(t=>!t.isHidden&&t.isToggleable!==!1);if(l.length===0)return{checked:!0,disabled:!0,indeterminate:!1};let d=l.filter(t=>t.isToggled).length,e=r.columns.filter(t=>!t.isHidden&&t.isToggleable===!1);return d===0&&e.length>0?{checked:!0,disabled:!1,indeterminate:!0}:d===0?{checked:!1,disabled:!1,indeterminate:!1}:d===l.length?{checked:!0,disabled:!1,indeterminate:!1}:{checked:!0,disabled:!1,indeterminate:!0}},getColumn(r,s=null){return s?this.columns.find(d=>d.type==="group"&&d.name===s)?.columns?.find(d=>d.name===r):this.columns.find(l=>l.name===r)},toggleGroup(r){let s=this.columns.find(i=>i.type==="group"&&i.name===r);if(!s?.columns)return;let l=this.calculateGroupedColumns(s);if(l.disabled)return;let e=s.columns.filter(i=>i.isToggleable!==!1).some(i=>i.isToggled),t=l.indeterminate?!0:!e;s.columns.filter(i=>i.isToggleable!==!1).forEach(i=>{i.isToggled=t}),this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager()},toggleColumn(r,s=null){let l=this.getColumn(r,s);!l||l.isToggleable===!1||(l.isToggled=!l.isToggled,this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager())},reorderColumns(r){let s=r.map(l=>l.split("::"));this.reorderTopLevel(s),this.isLive&&this.applyTableColumnManager()},reorderGroupColumns(r,s){let l=this.columns.find(t=>t.type==="group"&&t.name===s);if(!l)return;let d=r.map(t=>t.split("::")),e=[];d.forEach(([t,i])=>{let n=l.columns.find(u=>u.name===i);n&&e.push(n)}),l.columns=e,this.columns=[...this.columns],this.isLive&&this.applyTableColumnManager()},reorderTopLevel(r){let s=this.columns,l=[];r.forEach(([d,e])=>{let t=s.find(i=>d==="group"?i.type==="group"&&i.name===e:d==="column"?i.type!=="group"&&i.name===e:!1);t&&l.push(t)}),this.columns=l},async applyTableColumnManager(){this.isLoading=!0;try{await this.$wire.call("applyTableColumnManager",this.columns),this.error=void 0}catch(r){this.error="Failed to update column visibility",console.error("Table toggle columns error:",r)}finally{this.isLoading=!1}}}}document.addEventListener("alpine:init",()=>{window.Alpine.data("filamentTable",R),window.Alpine.data("filamentTableColumnManager",f)});})(); diff --git a/packages/tables/docs/07-grouping.md b/packages/tables/docs/07-grouping.md index a2e224a240..e2a46a0fd9 100644 --- a/packages/tables/docs/07-grouping.md +++ b/packages/tables/docs/07-grouping.md @@ -202,6 +202,27 @@ public function table(Table $table): Table } ``` +### Collapsing groups by default + +By default, groups with the `collapsible()` method are expanded when the table loads. + +If you want all groups to be collapsed by default when the table loads, use `$table->collapsedGroupsByDefault()`: + +```php +use Filament\Tables\Grouping\Group; +use Filament\Tables\Table; + +public function table(Table $table): Table +{ + return $table + ->groups([ + Group::make('author.name') + ->collapsible(), + ]) + ->collapsedGroupsByDefault(); +} +``` + ## Summarising groups You can use [summaries](summaries) with groups to display a summary of the records inside a group. This works automatically if you choose to add a summariser to a column in a grouped table. diff --git a/packages/tables/resources/js/components/table.js b/packages/tables/resources/js/components/table.js index b6e278d30a..6228c5e9c0 100644 --- a/packages/tables/resources/js/components/table.js +++ b/packages/tables/resources/js/components/table.js @@ -1,4 +1,5 @@ export default ({ + areGroupsCollapsedByDefault, canTrackDeselectedRecords, currentSelectionLivewireProperty, maxSelectableRecords, @@ -7,7 +8,7 @@ export default ({ }) => ({ checkboxClickController: null, - collapsedGroups: [], + groupVisibility: [], isLoading: false, @@ -266,20 +267,36 @@ export default ({ toggleCollapseGroup(group) { if (this.isGroupCollapsed(group)) { - this.collapsedGroups.splice(this.collapsedGroups.indexOf(group), 1) - - return + if (areGroupsCollapsedByDefault) { + this.groupVisibility.push(group) + } else { + this.groupVisibility.splice( + this.groupVisibility.indexOf(group), + 1, + ) + } + } else { + if (areGroupsCollapsedByDefault) { + this.groupVisibility.splice( + this.groupVisibility.indexOf(group), + 1, + ) + } else { + this.groupVisibility.push(group) + } } - - this.collapsedGroups.push(group) }, isGroupCollapsed(group) { - return this.collapsedGroups.includes(group) + if (areGroupsCollapsedByDefault) { + return !this.groupVisibility.includes(group) + } + + return this.groupVisibility.includes(group) }, resetCollapsedGroups() { - this.collapsedGroups = [] + this.groupVisibility = [] }, watchForCheckboxClicks() { diff --git a/packages/tables/resources/views/index.blade.php b/packages/tables/resources/views/index.blade.php index a0af9d037e..a37870253e 100644 --- a/packages/tables/resources/views/index.blade.php +++ b/packages/tables/resources/views/index.blade.php @@ -83,6 +83,7 @@ $isReordering = $isReordering(); $areGroupingSettingsVisible = (! $isReordering) && count($groups) && (! $areGroupingSettingsHidden()); $isGroupingDirectionSettingHidden = $isGroupingDirectionSettingHidden(); + $areGroupsCollapsedByDefault = $areGroupsCollapsedByDefault(); $areGroupingSettingsInDropdownOnDesktop = $areGroupingSettingsInDropdownOnDesktop(); $isColumnSearchVisible = $isSearchableByColumn(); $isGlobalSearchVisible = $isSearchable(); @@ -136,6 +137,7 @@ wire:init="loadTable" @endif x-data="filamentTable({ + areGroupsCollapsedByDefault: @js($areGroupsCollapsedByDefault), canTrackDeselectedRecords: @js($canTrackDeselectedRecords()), currentSelectionLivewireProperty: @js($getCurrentSelectionLivewireProperty()), maxSelectableRecords: @js($maxSelectableRecords), diff --git a/packages/tables/src/Table/Concerns/CanGroupRecords.php b/packages/tables/src/Table/Concerns/CanGroupRecords.php index b5206b630c..246f98c2f4 100644 --- a/packages/tables/src/Table/Concerns/CanGroupRecords.php +++ b/packages/tables/src/Table/Concerns/CanGroupRecords.php @@ -32,6 +32,8 @@ trait CanGroupRecords protected bool | Closure $isGroupingDirectionSettingHidden = false; + protected bool | Closure $areGroupsCollapsedByDefault = false; + protected ?Closure $modifyGroupRecordsTriggerActionUsing = null; public function groupRecordsTriggerAction(?Closure $callback): static @@ -72,6 +74,13 @@ trait CanGroupRecords return $this; } + public function collapsedGroupsByDefault(bool | Closure $condition = true): static + { + $this->areGroupsCollapsedByDefault = $condition; + + return $this; + } + public function defaultGroup(string | Group | Closure | null $group): static { $this->defaultGroup = $group; @@ -146,6 +155,11 @@ trait CanGroupRecords return (bool) $this->evaluate($this->isGroupingDirectionSettingHidden); } + public function areGroupsCollapsedByDefault(): bool + { + return (bool) $this->evaluate($this->areGroupsCollapsedByDefault); + } + public function getDefaultGroup(): ?Group { $defaultGroup = $this->evaluate($this->defaultGroup);