@navette-js/tables 0.0.0-stage → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,791 @@
1
+ <%# Table — port partiel de filament-tables::index. Portés : recherche, tri, sélection de page, lignes cliquables, action de ligne « lien », pagination.
2
+ Pas encore portés : actions groupées, filtres, gestionnaire de colonnes, recherche par colonne, groupes, résumés, réordonnancement, état vide. %>
3
+ <% const selectPageBinding = `
4
+ const recordsOnPage = getRecordsOnPage()
5
+
6
+ if (recordsOnPage.length && areRecordsSelected(recordsOnPage)) {
7
+ $el.checked = true
8
+ $el.indeterminate = false
9
+
10
+ return 'checked'
11
+ }
12
+
13
+ $el.checked = false
14
+ $el.indeterminate =
15
+ recordsOnPage.length && areRecordsPartiallySelected(recordsOnPage)
16
+
17
+ return null
18
+ ` %>
19
+ <div x-data="filamentTable({
20
+ areGroupsCollapsedByDefault: <%- grouping.collapsedByDefault ? 'true' : 'false' %>,
21
+ canTrackDeselectedRecords: true,
22
+ currentSelectionLivewireProperty: null,
23
+ maxSelectableRecords: null,
24
+ reorderMessage: <%- js(t('filament-tables::table.reordering.moved')) %>,
25
+ selectsCurrentPageOnly: false,
26
+ })" class="fi-ta">
27
+ <input type="hidden" value="<%- allCount %>" x-ref="allSelectableRecordsCount" />
28
+
29
+ <% if (reorder) { %><span wire:ignore x-ref="tableReorderStatus" role="status" aria-live="polite" aria-atomic="true" class="fi-sr-only"></span><% } %>
30
+
31
+ <div class="fi-ta-ctn<%- contentLayout ? ' fi-ta-ctn-with-content-layout' : '' %> fi-ta-ctn-with-footer fi-ta-ctn-with-header">
32
+ <div class="fi-ta-main">
33
+ <%# La barre d'outils n'est visible en permanence que si elle a un contenu (recherche, filtres, colonnes) ; sinon seulement pendant une sélection. %>
34
+ <% const filtersAbove = filtersForm !== null && filtersLayout === 'above-content-collapsible'; %>
35
+ <% const toolbarVisible = reorder !== null || groups.length > 0 || isSearchable || filtersForm !== null || hasColumnManager; %>
36
+ <div x-show="<%- heading !== null || headerActions.length || toolbarVisible ? 'true' : 'false' %> || false || (getSelectedRecordsCount() && <%- bulkActions.length ? 1 : 0 %>)" wire:key="<%= id %>.table.header.visible" class="fi-ta-header-ctn">
37
+ <%- hook('tables::header.before') %>
38
+ <% if (heading !== null || headerActions.length) { %>
39
+ <div class="<%- cls(['fi-ta-header', { 'fi-ta-header-adaptive-actions-position': headerActions.length }]) %>">
40
+ <% if (heading !== null) { %>
41
+ <div>
42
+ <h2 class="fi-ta-header-heading">
43
+ <%= heading %>
44
+ </h2>
45
+ </div>
46
+ <% } %>
47
+ <% if (headerActions.length) { %>
48
+ <div class="fi-ta-actions fi-align-start fi-wrapped">
49
+ <%- headerActions.join('') %>
50
+ </div>
51
+ <% } %>
52
+ </div>
53
+ <% } %>
54
+ <%- hook('tables::header.after') %>
55
+ <% if (filtersAbove) { %>
56
+ <%# Filtres au-dessus du contenu, repliables (FiltersLayout::AboveContentCollapsible) %>
57
+ <div x-bind:class="{ 'fi-open': areFiltersOpen }" class="fi-ta-filters-above-content-ctn">
58
+ <div class="fi-ta-filters" x-cloak="x-cloak" x-show="areFiltersOpen">
59
+ <div class="fi-ta-filters-header">
60
+ <h2 class="fi-ta-filters-heading">
61
+ <%= t('filament-tables::table.filters.heading') %>
62
+ </h2>
63
+
64
+ <div>
65
+ <button type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="<%- cls(['fi-ac-link-action', 'fi-link', 'fi-size-md', ...dangerLinkClasses]) %>" data-table-filter-action="reset" wire:click="resetTableFiltersForm">
66
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'resetTableFiltersForm' })) %>
67
+ <span class="fi-link-label">
68
+ <%= t('filament-tables::table.filters.actions.reset.label') %>
69
+ </span>
70
+ </button>
71
+ </div>
72
+ </div>
73
+
74
+ <%- filtersForm %>
75
+
76
+ <div class="fi-ta-filters-actions-ctn">
77
+ <button type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="<%- cls(['fi-ac-btn-action', 'fi-btn', 'fi-size-md', ...primaryButtonClasses]) %>" data-table-filter-action="apply" wire:click="applyTableFilters">
78
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'applyTableFilters' })) %>
79
+ <%= t('filament-tables::table.filters.actions.apply.label') %>
80
+ </button>
81
+ </div>
82
+ </div>
83
+
84
+ <span x-on:click="areFiltersOpen = ! areFiltersOpen" class="fi-ta-filters-trigger-action-ctn">
85
+ <button title="<%= t('filament-tables::table.actions.filter.label') %>" aria-label="<%= t('filament-tables::table.actions.filter.label') %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="fi-force-enabled fi-ac-icon-btn-action fi-icon-btn fi-size-md">
86
+ <%- icon('m-funnel') %>
87
+ <div class="fi-icon-btn-badge-ctn">
88
+ <span class="<%- cls([...badgeClasses, 'fi-badge', 'fi-size-xs']) %>">
89
+ <%- activeFiltersCount %>
90
+ </span>
91
+ </div>
92
+ </button>
93
+ </span>
94
+ </div>
95
+ <% } %>
96
+ <%- hook('tables::toolbar.before') %>
97
+ <div<% if (!toolbarVisible) { %> x-cloak<% } %> x-show="<%- toolbarVisible ? 'true' : 'false' %> || false || (getSelectedRecordsCount() && <%- bulkActions.length ? 1 : 0 %>)" wire:key="<%= id %>.table.header-toolbar.<%- toolbarVisible ? 'visible' : 'selection' %>" class="fi-ta-header-toolbar">
98
+ <%- hook('tables::toolbar.start') %>
99
+ <div class="fi-ta-actions fi-align-start fi-wrapped">
100
+ <%- hook('tables::toolbar.reorder-trigger.before') %>
101
+ <% if (reorder) { %>
102
+ <% const reorderLabel = t(reorder.active ? 'filament-tables::table.actions.disable_reordering.label' : 'filament-tables::table.actions.enable_reordering.label') %>
103
+ <button title="<%= reorderLabel %>" aria-label="<%= reorderLabel %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" wire:click="toggleTableReordering" class="fi-force-enabled fi-ac-icon-btn-action fi-icon-btn fi-size-md">
104
+ <%- icon(reorder.active ? 'm-check' : 'm-arrows-up-down', { attributes: attrs({ 'wire:loading.remove.delay.default': '1', 'wire:target': 'toggleTableReordering' }) }) %>
105
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'toggleTableReordering' })) %>
106
+ </button>
107
+ <% } %>
108
+ <%- hook('tables::toolbar.reorder-trigger.after') %>
109
+ <% if (bulkActions.length) { %>
110
+ <div x-data="filamentDropdown" x-cloak="x-cloak" x-show="getSelectedRecordsCount()" class="fi-dropdown">
111
+ <div x-on:mousedown="if ($event.button === 0) toggle($event)" class="fi-dropdown-trigger">
112
+ <button title="<%= t('filament-tables::table.actions.open_bulk_actions.label') %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="fi-ac-btn-group fi-icon-btn fi-size-md">
113
+ <%- icon('m-ellipsis-vertical') %>
114
+ </button>
115
+ <button type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="fi-ac-btn-group fi-btn fi-size-md fi-labeled-from-sm">
116
+ <%- icon('m-ellipsis-vertical') %>
117
+ <%= t('filament-tables::table.actions.open_bulk_actions.label') %>
118
+ </button>
119
+ </div>
120
+
121
+ <div x-cloak x-float.placement.bottom-start.flip.offset="{ offset: 8 }" x-ref="panel" x-transition:enter-start="fi-opacity-0" x-transition:leave-end="fi-opacity-0" role="menu" class="fi-dropdown-panel">
122
+ <div class="fi-dropdown-list" role="group">
123
+ <%- bulkActions.join('') %>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ <% } %>
128
+ <%- hook('tables::toolbar.grouping-selector.before') %>
129
+ <% if (groups.length) { %>
130
+ <% const groupLabel = t('filament-tables::table.grouping.fields.group.label'); const directionLabel = t('filament-tables::table.grouping.fields.direction.label'); %>
131
+ <div x-data="{
132
+ grouping: $wire.$entangle('tableGrouping', true),
133
+ group: null,
134
+ direction: null,
135
+ }" x-init="
136
+ if (grouping) {
137
+ ;[group, direction] = grouping.split(':')
138
+ direction ??= 'asc'
139
+ }
140
+
141
+ $watch('grouping', function () {
142
+ if (! grouping) {
143
+ group = null
144
+ direction = null
145
+
146
+ return
147
+ }
148
+
149
+ ;[group, direction] = grouping.split(':')
150
+ direction ??= 'asc'
151
+ })
152
+
153
+ $watch('direction', function () {
154
+ grouping = group ? `${group}:${direction}` : null
155
+ })
156
+
157
+ $watch('group', function (newGroup, oldGroup) {
158
+ if (! newGroup) {
159
+ direction = null
160
+ grouping = group ? `${group}:${direction}` : null
161
+
162
+ return
163
+ }
164
+
165
+ if (oldGroup) {
166
+ grouping = group ? `${group}:${direction}` : null
167
+
168
+ return
169
+ }
170
+
171
+ direction ??= 'asc'
172
+ grouping = group ? `${group}:${direction}` : null
173
+ })
174
+ " class="fi-ta-grouping-settings">
175
+ <div x-data="filamentDropdown({ isMenu: false })" class="<%- cls(['fi-dropdown', { 'sm:fi-hidden': !grouping.inDropdownOnDesktop }]) %>" wire:key="<%= id %>.table.grouping">
176
+ <div x-on:mousedown="if ($event.button === 0) toggle($event)" class="fi-dropdown-trigger">
177
+ <button title="<%= t('filament-tables::table.actions.group.label') %>" aria-label="<%= t('filament-tables::table.actions.group.label') %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="fi-force-enabled fi-ac-icon-btn-action fi-icon-btn fi-size-md">
178
+ <%- icon('m-rectangle-stack') %>
179
+ </button>
180
+ </div>
181
+ <div x-cloak x-float.placement.bottom-start.flip.shift.offset="{ offset: 8, }" x-ref="panel" x-transition:enter-start="fi-opacity-0" x-transition:leave-end="fi-opacity-0" wire:ignore.self wire:key="<%= id %>.table.grouping.panel" class="fi-dropdown-panel fi-width-xs" style="">
182
+ <div class="fi-ta-grouping-settings-fields">
183
+ <label>
184
+ <span><%= groupLabel %></span>
185
+ <div class="fi-input-wrp"><div class="fi-input-wrp-content-ctn"><select class="fi-select-input" x-model="group" x-on:change="resetCollapsedGroups()"><option value="">-</option><% for (const option of groups) { %><option value="<%= option.id %>"><%= option.label %></option><% } %></select></div></div>
186
+ </label>
187
+ <% if (!grouping.directionHidden) { %>
188
+ <label x-cloak x-show="group">
189
+ <span><%= directionLabel %></span>
190
+ <div class="fi-input-wrp"><div class="fi-input-wrp-content-ctn"><select class="fi-select-input" x-model="direction"><option value="asc"><%= t('filament-tables::table.grouping.fields.direction.options.asc') %></option><option value="desc"><%= t('filament-tables::table.grouping.fields.direction.options.desc') %></option></select></div></div>
191
+ </label>
192
+ <% } %>
193
+ </div>
194
+ </div>
195
+ </div>
196
+ <% if (!grouping.inDropdownOnDesktop) { %>
197
+ <div class="fi-ta-grouping-settings-fields">
198
+ <label>
199
+ <div class="fi-input-wrp"><div x-on:click="if (! $event.target.closest('.fi-input-wrp-actions')) $dispatch('focus-input')" class="fi-input-wrp-prefix fi-input-wrp-prefix-has-content fi-input-wrp-prefix-has-label"><span class="fi-input-wrp-label"><%= groupLabel %></span></div><div class="fi-input-wrp-content-ctn"><select class="fi-select-input" x-model="group" x-on:change="resetCollapsedGroups()"><option value="">-</option><% for (const option of groups) { %><option value="<%= option.id %>"><%= option.label %></option><% } %></select></div></div>
200
+ </label>
201
+ <% if (!grouping.directionHidden) { %>
202
+ <label x-cloak x-show="group">
203
+ <span class="fi-sr-only"><%= directionLabel %></span>
204
+ <div class="fi-input-wrp"><div class="fi-input-wrp-content-ctn"><select class="fi-select-input" x-model="direction"><option value="asc"><%= t('filament-tables::table.grouping.fields.direction.options.asc') %></option><option value="desc"><%= t('filament-tables::table.grouping.fields.direction.options.desc') %></option></select></div></div>
205
+ </label>
206
+ <% } %>
207
+ </div>
208
+ <% } %>
209
+ </div>
210
+ <% } %>
211
+ <%- hook('tables::toolbar.grouping-selector.after') %>
212
+ </div>
213
+
214
+ <% if (toolbarVisible) { %><div>
215
+ <%- hook('tables::toolbar.search.before') %>
216
+ <% if (isSearchable) { %><div x-id="['input']" class="fi-ta-search-field">
217
+ <label x-bind:for="$id('input')" class="fi-sr-only">
218
+ <%= t('filament-tables::table.fields.search.label') %>
219
+ </label>
220
+
221
+ <div class="fi-input-wrp">
222
+ <div x-on:click="if (! $event.target.closest('.fi-input-wrp-actions')) $dispatch('focus-input')" class="fi-input-wrp-prefix fi-input-wrp-prefix-has-content fi-inline">
223
+ <%- icon('m-magnifying-glass', { attributes: attrs({ 'wire:loading.remove.delay.default': '1', 'wire:target': 'tableSearch' }) }) %>
224
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'tableSearch' })) %>
225
+ </div>
226
+
227
+ <div class="fi-input-wrp-content-ctn">
228
+ <input class="fi-input fi-input-has-inline-prefix" autocomplete="off" maxlength="1000" placeholder="<%= t('filament-tables::table.fields.search.placeholder') %>" type="search" wire:key="<%= id %>.table.tableSearch.field.input" wire:model.live.debounce.500ms="tableSearch" x-bind:id="$id('input')" x-on:keyup="if ($event.key === 'Enter') { $wire.$refresh() }" />
229
+ </div>
230
+ </div>
231
+ </div><% } %>
232
+ <%- hook('tables::toolbar.search.after') %>
233
+
234
+ <% if (filtersForm !== null && !filtersAbove) { %>
235
+ <div x-data="filamentDropdown({ isMenu: false })" class="fi-dropdown fi-ta-filters-dropdown" wire:key="<%= id %>.table.filters">
236
+ <div x-on:mousedown="if ($event.button === 0) toggle($event)" class="fi-dropdown-trigger">
237
+ <button title="<%= t('filament-tables::table.actions.filter.label') %>" aria-label="<%= t('filament-tables::table.actions.filter.label') %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="fi-force-enabled fi-ac-icon-btn-action fi-icon-btn fi-size-md">
238
+ <%- icon('m-funnel') %>
239
+ <div class="fi-icon-btn-badge-ctn">
240
+ <span class="<%- cls([...badgeClasses, 'fi-badge', 'fi-size-xs']) %>">
241
+ <%- activeFiltersCount %>
242
+ </span>
243
+ </div>
244
+ </button>
245
+ </div>
246
+
247
+ <div x-cloak x-float.placement.bottom-end.shift.offset="{ offset: 8, }" x-ref="panel" x-transition:enter-start="fi-opacity-0" x-transition:leave-end="fi-opacity-0" wire:ignore.self wire:key="<%= id %>.table.filters.panel" class="fi-dropdown-panel fi-width-xs" style="">
248
+ <div class="fi-ta-filters" x-data="filamentTableFilters" x-on:click="handleActionClick($event)" tabindex="-1">
249
+ <div class="fi-ta-filters-header">
250
+ <h2 class="fi-ta-filters-heading">
251
+ <%= t('filament-tables::table.filters.heading') %>
252
+ </h2>
253
+
254
+ <div>
255
+ <button type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="<%- cls(['fi-ac-link-action', 'fi-link', 'fi-size-md', ...dangerLinkClasses]) %>" data-table-filter-action="reset" wire:click="resetTableFiltersForm">
256
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'resetTableFiltersForm' })) %>
257
+ <span class="fi-link-label">
258
+ <%= t('filament-tables::table.filters.actions.reset.label') %>
259
+ </span>
260
+ </button>
261
+ </div>
262
+ </div>
263
+
264
+ <%- filtersForm %>
265
+
266
+ <div class="fi-ta-filters-actions-ctn">
267
+ <button type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" class="<%- cls(['fi-ac-btn-action', 'fi-btn', 'fi-size-md', ...primaryButtonClasses]) %>" data-table-filter-action="apply" wire:click="applyTableFilters">
268
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'applyTableFilters' })) %>
269
+ <%= t('filament-tables::table.filters.actions.apply.label') %>
270
+ </button>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ <% } %>
276
+
277
+ <%# Comme Filament : ces deux emplacements n'existent que s'il y a un déclencheur de filtres ou un gestionnaire de colonnes. %>
278
+ <% if ((filtersForm !== null && !filtersAbove) || hasColumnManager) { %><%- hook('tables::toolbar.toggle-column-trigger.before') %><% } %>
279
+ <% if (hasColumnManager) { %>
280
+ <%- view('filament-tables::column-manager', { id, dangerLinkClasses, primaryButtonClasses }) %>
281
+ <% } %>
282
+ <% if ((filtersForm !== null && !filtersAbove) || hasColumnManager) { %><%- hook('tables::toolbar.toggle-column-trigger.after') %><% } %>
283
+ </div><% } %>
284
+ <%- hook('tables::toolbar.end') %>
285
+ </div>
286
+ <%- hook('tables::toolbar.after') %>
287
+
288
+ </div>
289
+
290
+ <% if (reorder?.active) { %>
291
+ <div x-cloak role="status" aria-live="polite" wire:key="<%= id %>.table.reorder.indicator" class="fi-ta-reorder-indicator">
292
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'reorderTable,moveTableRecord' })) %>
293
+ <%= t('filament-tables::table.reorder_indicator') %>
294
+ </div>
295
+ <% } %>
296
+ <% if (bulkActions.length) { %>
297
+ <div x-cloak role="status" aria-live="polite" aria-atomic="true" x-bind:hidden="! getSelectedRecordsCount()" x-show="getSelectedRecordsCount()" wire:key="<%= id %>.table.selection.indicator" class="fi-ta-selection-indicator">
298
+ <div>
299
+ <%- loadingIndicator(attrs({ 'x-show': 'isLoading' })) %>
300
+
301
+ <span x-text="
302
+ window.pluralize(<%= js(t('filament-tables::table.selection_indicator.selected_count')) %>, getSelectedRecordsCount(), {
303
+ count: new Intl.NumberFormat(<%= js('en') %>).format(getSelectedRecordsCount()),
304
+ })
305
+ "></span>
306
+ </div>
307
+
308
+ <div>
309
+ <%- hook('tables::selection.indicator.actions.before') %>
310
+ <div class="fi-ta-selection-indicator-actions-ctn">
311
+ <button class="<%- cls([...linkClasses, 'fi-link', 'fi-size-md']) %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" x-on:click="selectAllRecords" x-show="canSelectAllRecords()" wire:key="<%= id %>table.selection.indicator.actions.select-all.<%- allCount %>.<%- page.page %>">
312
+ <span class="fi-link-label">
313
+ <%= t('filament-tables::table.selection_indicator.actions.select_all.label', { count: number(allCount) }) %>
314
+ </span>
315
+ </button>
316
+ <button class="<%- cls([...dangerLinkClasses, 'fi-link', 'fi-size-md']) %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" x-on:click="deselectAllRecords">
317
+ <span class="fi-link-label">
318
+ <%= t('filament-tables::table.selection_indicator.actions.deselect_all.label') %>
319
+ </span>
320
+ </button>
321
+ </div>
322
+ <%- hook('tables::selection.indicator.actions.after') %>
323
+ </div>
324
+ </div>
325
+ <% } %>
326
+
327
+ <%# Comme Filament : un hook `tables::filter.indicators` qui rend quelque chose remplace les indicateurs par défaut. %>
328
+ <% const filterIndicatorsView = filterIndicators.length ? hook('tables::filter.indicators', { filterIndicators }) : ''; %>
329
+ <% if (filterIndicatorsView.trim() !== '') { %><%- filterIndicatorsView %><% } else if (filterIndicators.length) { %>
330
+ <%# Indicateurs des filtres actifs — port de filament-tables::components.filters.indicators %>
331
+ <div class="fi-ta-filter-indicators">
332
+ <div>
333
+ <span class="fi-ta-filter-indicators-label">
334
+ <%= t('filament-tables::table.filters.indicator') %>
335
+ </span>
336
+
337
+ <div class="fi-ta-filter-indicators-badges-ctn" role="list">
338
+ <% for (const indicator of filterIndicators) { %>
339
+ <% const removal = 'removeTableFilter(' + js(indicator.filter) + ', ' + js(indicator.field) + ')'; %>
340
+ <span class="<%- cls([...indicatorBadgeClasses, 'fi-badge', 'fi-size-md']) %>" role="listitem">
341
+ <span class="fi-badge-label-ctn">
342
+ <span class="fi-badge-label">
343
+ <%= indicator.label %>
344
+ </span>
345
+ </span>
346
+
347
+ <button data-prevent-disabled-clicks type="button" class="fi-badge-delete-btn" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" wire:target="removeTableFilter" wire:click="<%= removal %>">
348
+ <span aria-hidden="true" wire:loading.remove.delay.default="" wire:target="<%= removal %>" class="fi-badge-delete-btn-icon"></span>
349
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': removal }), 'xs') %>
350
+ <span class="fi-sr-only">
351
+ <%= t('filament-tables::table.filters.actions.remove.label') %>
352
+ </span>
353
+ </button>
354
+ </span>
355
+ <% } %>
356
+ </div>
357
+ </div>
358
+
359
+ <button x-tooltip="{
360
+ content: <%= js(t('filament-tables::table.filters.actions.remove_all.tooltip')) %>,
361
+ theme: $store.theme,
362
+ allowHTML: false,
363
+ }" aria-label="<%= t('filament-tables::table.filters.actions.remove_all.label') %>" type="button" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" wire:target="removeTableFilters,removeTableFilter" class="fi-ac-icon-btn-action fi-icon-btn fi-size-sm" wire:click="removeTableFilters">
364
+ <%- icon('m-x-mark', { attributes: attrs({ 'wire:loading.remove.delay.default': '1', 'wire:target': 'removeTableFilters' }) }) %>
365
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'removeTableFilters' })) %>
366
+ </button>
367
+ </div>
368
+ <% } %>
369
+
370
+ <%- hook('tables::content.before', { table, records: page.records }) %>
371
+ <div wire:loading.attr="aria-busy" wire:target="<%- targets %>" class="fi-ta-content-ctn fi-fixed-positioning-context">
372
+ <div role="status" aria-live="polite" aria-atomic="true" class="fi-sr-only">
373
+ <%= tc('filament-tables::table.result_count', resultsCount) %>
374
+ </div>
375
+
376
+ <div wire:loading.delay.default.block wire:target="<%- targets %>" role="status" aria-live="polite" data-testid="table-loading-state" class="fi-sr-only">
377
+ <%= t('filament-tables::table.loading') %>
378
+ </div>
379
+
380
+ <% if (contentLayout) { %>
381
+ <%# Mise en page des colonnes (Split / Stack) : une carte par enregistrement, tri par sélecteur %>
382
+ <% if (rows.length) { %>
383
+ <% if (selectable || columns.some((c) => c.sortable)) { %>
384
+ <div class="fi-ta-content-header">
385
+ <% if (selectable) { %>
386
+ <input aria-label="<%= t('filament-tables::table.fields.bulk_select_page.label') %>" type="checkbox" x-bind:checked="<%= selectPageBinding %>" x-on:click="toggleSelectRecordsOnPage" wire:key="<%= id %>.table.bulk-select-page.checkbox.<%- randomKey() %>" wire:loading.attr="disabled" wire:target="<%- targets %>" class="fi-ta-page-checkbox fi-checkbox-input" />
387
+ <% } %>
388
+ <% if (columns.some((c) => c.sortable)) { %>
389
+ <div x-data="{
390
+ sort: $wire.$entangle('tableSort', true),
391
+ column: null,
392
+ direction: null,
393
+ }" x-init="
394
+ if (sort) {
395
+ ;[column, direction] = sort.split(':')
396
+ direction ??= 'asc'
397
+ }
398
+
399
+ $watch('sort', function () {
400
+ if (! sort) {
401
+ return
402
+ }
403
+
404
+ ;[column, direction] = sort.split(':')
405
+ direction ??= 'asc'
406
+ })
407
+
408
+ $watch('direction', function () {
409
+ sort = column ? `${column}:${direction}` : null
410
+ })
411
+
412
+ $watch('column', function (newColumn, oldColumn) {
413
+ if (! newColumn) {
414
+ direction = null
415
+ sort = column ? `${column}:${direction}` : null
416
+
417
+ return
418
+ }
419
+
420
+ if (oldColumn) {
421
+ sort = column ? `${column}:${direction}` : null
422
+
423
+ return
424
+ }
425
+
426
+ direction = 'asc'
427
+ sort = column ? `${column}:${direction}` : null
428
+ })
429
+ " class="fi-ta-sorting-settings">
430
+ <label>
431
+ <div class="fi-input-wrp">
432
+ <div x-on:click="if (! $event.target.closest('.fi-input-wrp-actions')) $dispatch('focus-input')" class="fi-input-wrp-prefix fi-input-wrp-prefix-has-content fi-input-wrp-prefix-has-label">
433
+ <span class="fi-input-wrp-label">
434
+ <%= t('filament-tables::table.sorting.fields.column.label') %>
435
+ </span>
436
+ </div>
437
+
438
+ <div class="fi-input-wrp-content-ctn">
439
+ <select class="fi-select-input" x-model="column">
440
+ <option value="">-</option>
441
+ <% for (const column of columns.filter((c) => c.sortable)) { %>
442
+ <option value="<%= column.name %>"><%= column.label %></option>
443
+ <% } %>
444
+ </select>
445
+ </div>
446
+ </div>
447
+ </label>
448
+
449
+ <label x-cloak x-show="column">
450
+ <span class="fi-sr-only">
451
+ <%= t('filament-tables::table.sorting.fields.direction.label') %>
452
+ </span>
453
+
454
+ <div class="fi-input-wrp">
455
+ <div class="fi-input-wrp-content-ctn">
456
+ <select class="fi-select-input" x-model="direction">
457
+ <option value="asc"><%= t('filament-tables::table.sorting.fields.direction.options.asc') %></option>
458
+ <option value="desc"><%= t('filament-tables::table.sorting.fields.direction.options.desc') %></option>
459
+ </select>
460
+ </div>
461
+ </div>
462
+ </label>
463
+ </div>
464
+ <% } %>
465
+ </div>
466
+ <% } %>
467
+ <div aria-label="<%= heading ?? table.getPluralModelLabel() %>" role="list" class="fi-ta-content">
468
+ <% for (const row of rows) { %>
469
+ <div role="listitem" wire:key="<%= id %>.table.records.<%= row.key %>" class="<%- cls(['fi-ta-record', { 'fi-clickable': row.url || row.mountExpression, 'fi-ta-record-with-content-prefix': selectable }, row.classes]) %>" x-bind:class="{
470
+ 'fi-selected': <%- selectable ? 'true' : 'false' %> && isRecordSelected(<%= js(row.key) %>),
471
+ }">
472
+ <% if (selectable) { %>
473
+ <input aria-label="<%= t('filament-tables::table.fields.bulk_select_record.label', { key: row.key }) %>" type="checkbox" value="<%= row.key %>" x-on:click="toggleSelectedRecord(<%= js(row.key) %>)" x-bind:checked="isRecordSelected(<%= js(row.key) %>) ? 'checked' : null" data-group="<%= row.groupKey ?? '' %>" wire:loading.attr="disabled" wire:target="<%- targets %>" class="fi-ta-record-checkbox fi-checkbox-input" />
474
+ <% } %>
475
+ <div class="fi-ta-record-content-ctn">
476
+ <div>
477
+ <% if (row.url) { %>
478
+ <a href="<%= row.url %>" x-on:click="if (! ($event.altKey || $event.ctrlKey || $event.metaKey || $event.shiftKey)) { $event.preventDefault(); Alpine.navigate($el.getAttribute('href')) }" class="fi-ta-record-content"><%- row.contentHtml %></a>
479
+ <% } else if (row.mountExpression) { %>
480
+ <button type="button" wire:click="<%= row.mountExpression %>" wire:loading.attr="disabled" wire:target="<%= row.mountExpression %>" class="fi-ta-record-content"><%- row.contentHtml %></button>
481
+ <% } else { %>
482
+ <div class="fi-ta-record-content"><%- row.contentHtml %></div>
483
+ <% } %>
484
+ </div>
485
+ <% if (hasRowActions) { %>
486
+ <div class="fi-ta-actions fi-wrapped sm:fi-not-wrapped fi-align-start md:fi-align-end">
487
+ <%- row.actionsHtml ?? '' %>
488
+ </div>
489
+ <% } %>
490
+ </div>
491
+ </div>
492
+ <% } %>
493
+ </div>
494
+ <% } %>
495
+ <% } else { %>
496
+ <table aria-label="<%= heading ?? table.getPluralModelLabel() %>" class="<%- cls(['fi-ta-table', { 'fi-ta-table-stacked-on-mobile': table.isStackedOnMobile() }]) %>">
497
+ <thead>
498
+ <% if (table.isStackedOnMobile()) { %>
499
+ <tr class="fi-ta-table-stacked-header-row">
500
+ <td colspan="100%" class="fi-ta-table-stacked-header-cell">
501
+ <%# Tri empilé : seulement s'il existe des colonnes triables (comme Filament) %>
502
+ <% if (columns.some((c) => c.sortable)) { %>
503
+ <div x-data="{
504
+ sort: $wire.$entangle('tableSort', true),
505
+ column: null,
506
+ direction: null,
507
+ }" x-init="
508
+ if (sort) {
509
+ ;[column, direction] = sort.split(':')
510
+ direction ??= 'asc'
511
+ }
512
+
513
+ $watch('sort', function () {
514
+ if (! sort) {
515
+ return
516
+ }
517
+
518
+ ;[column, direction] = sort.split(':')
519
+ direction ??= 'asc'
520
+ })
521
+
522
+ $watch('direction', function () {
523
+ sort = column ? `${column}:${direction}` : null
524
+ })
525
+
526
+ $watch('column', function (newColumn, oldColumn) {
527
+ if (! newColumn) {
528
+ direction = null
529
+ sort = column ? `${column}:${direction}` : null
530
+
531
+ return
532
+ }
533
+
534
+ if (oldColumn) {
535
+ sort = column ? `${column}:${direction}` : null
536
+
537
+ return
538
+ }
539
+
540
+ direction = 'asc'
541
+ sort = column ? `${column}:${direction}` : null
542
+ })
543
+ " class="fi-ta-table-stacked-sorting">
544
+ <label>
545
+ <div class="fi-input-wrp">
546
+ <div x-on:click="if (! $event.target.closest('.fi-input-wrp-actions')) $dispatch('focus-input')" class="fi-input-wrp-prefix fi-input-wrp-prefix-has-content fi-input-wrp-prefix-has-label">
547
+ <span class="fi-input-wrp-label">
548
+ <%= t('filament-tables::table.sorting.fields.column.label') %>
549
+ </span>
550
+ </div>
551
+
552
+ <div class="fi-input-wrp-content-ctn">
553
+ <select class="fi-select-input" x-model="column">
554
+ <option value="">-</option>
555
+ <% for (const column of columns.filter((c) => c.sortable)) { %>
556
+ <option value="<%= column.name %>"><%= column.label %></option>
557
+ <% } %>
558
+ </select>
559
+ </div>
560
+ </div>
561
+ </label>
562
+
563
+ <label x-cloak x-show="column">
564
+ <span class="fi-sr-only">
565
+ <%= t('filament-tables::table.sorting.fields.direction.label') %>
566
+ </span>
567
+
568
+ <div class="fi-input-wrp">
569
+ <div class="fi-input-wrp-content-ctn">
570
+ <select class="fi-select-input" x-model="direction">
571
+ <option value="asc"><%= t('filament-tables::table.sorting.fields.direction.options.asc') %></option>
572
+ <option value="desc"><%= t('filament-tables::table.sorting.fields.direction.options.desc') %></option>
573
+ </select>
574
+ </div>
575
+ </div>
576
+ </label>
577
+ </div>
578
+ <% } %>
579
+
580
+ <input aria-label="<%= t('filament-tables::table.fields.bulk_select_page.label') %>" type="checkbox" x-bind:checked="<%= selectPageBinding %>" x-on:click="toggleSelectRecordsOnPage" wire:key="<%= id %>.table.bulk-select-page.checkbox.stacked.<%- randomKey() %>" wire:loading.attr="disabled" wire:target="<%- targets %>" class="fi-ta-page-checkbox fi-checkbox-input" />
581
+ </td>
582
+ </tr>
583
+ <% } %>
584
+
585
+ <% if (headerGroups) { %>
586
+ <tr class="fi-ta-table-head-groups-row">
587
+ <% if (rows.length) { %><th></th><% } %>
588
+ <% for (const group of headerGroups) { %>
589
+ <% if (group.label === null) { %><th></th><% } else { %><th colspan="<%- group.span %>" scope="colgroup" class="fi-ta-header-group-cell"><%= group.label %></th><% } %>
590
+ <% } %>
591
+ <% if (rows.length) { %><th></th><% } %>
592
+ </tr>
593
+ <% } %>
594
+
595
+ <tr>
596
+ <% if (rows.length && reorder?.active) { %><th></th><% } %>
597
+ <% if (rows.length && !reorder?.active && selectable) { %>
598
+ <th scope="col" class="fi-ta-cell fi-ta-selection-cell">
599
+ <input aria-label="<%= t('filament-tables::table.fields.bulk_select_page.label') %>" type="checkbox" x-bind:checked="<%= selectPageBinding %>" x-on:click="toggleSelectRecordsOnPage" wire:key="<%= id %>.table.bulk-select-page.checkbox.<%- randomKey() %>" wire:loading.attr="disabled" wire:target="<%- targets %>" class="fi-ta-page-checkbox fi-checkbox-input" />
600
+ </th>
601
+ <% } %>
602
+
603
+ <%# Comme Filament : un hook `tables::header.cell` qui rend quelque chose remplace la cellule d'en-tête de la colonne. %>
604
+ <% const hasHeaderCellHook = hasHook('tables::header.cell'); %>
605
+ <% for (const [columnIndex, column] of columns.entries()) { %>
606
+ <% const headerCellView = hasHeaderCellHook ? hook('tables::header.cell', { column: visibleColumns[columnIndex], isReordering }) : ''; %>
607
+ <% if (headerCellView.trim() !== '') { %><%- headerCellView %><% continue; } %>
608
+ <th<% if (column.sortable) { %> aria-sort="<%- column.sort %>"<% } %> scope="col" class="<%- cls(['fi-ta-header-cell', 'fi-ta-header-cell-' + column.cssName, { 'fi-grouped': column.grouped }, column.alignment && column.alignment !== 'start' ? 'fi-align-' + column.alignment : null]) %>">
609
+ <% if (column.sortable) { %>
610
+ <button type="button" data-prevent-disabled-clicks wire:click="sortTable(<%= js(column.name) %>)" wire:loading.attr="aria-disabled" wire:loading.class="fi-disabled" wire:target="<%- targets %>" class="fi-ta-header-cell-sort-btn">
611
+ <%= column.label %>
612
+ <%- icon(column.sort === 'ascending' ? 'm-chevron-up' : 'm-chevron-down', { attributes: attrs({ 'wire:loading.remove.delay.default': '1', 'wire:target': 'sortTable(' + js(column.name) + ')' }) }) %>
613
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'sortTable(' + js(column.name) + ')' })) %>
614
+ </button>
615
+ <% } else { %>
616
+ <%= column.label %>
617
+ <% } %>
618
+ </th>
619
+ <% } %>
620
+
621
+ <% if (rows.length && !reorder?.active && hasRowActions) { %><th aria-label="<%= tc('filament-tables::table.columns.actions.label', 2) %>" scope="col" class="fi-ta-actions-header-cell fi-ta-empty-header-cell"></th><% } %>
622
+ </tr>
623
+ </thead>
624
+
625
+ <% if (columns.some((c) => c.individuallySearchable)) { %>
626
+ <tbody>
627
+ <tr class="fi-ta-row fi-ta-row-not-reorderable fi-ta-individual-search-row">
628
+ <% if (rows.length) { %><td></td><% } %>
629
+ <% for (const column of columns) { %>
630
+ <td class="<%- cls(['fi-ta-cell', column.individuallySearchable ? 'fi-ta-individual-search-cell fi-ta-individual-search-cell-' + column.cssName : null]) %>">
631
+ <% if (column.individuallySearchable) { %>
632
+ <div x-id="['input']" class="fi-ta-search-field">
633
+ <label x-bind:for="$id('input')" class="fi-ta-cell-label">
634
+ <%= column.label %>
635
+ </label>
636
+
637
+ <div class="fi-input-wrp">
638
+ <div x-on:click="if (! $event.target.closest('.fi-input-wrp-actions')) $dispatch('focus-input')" class="fi-input-wrp-prefix fi-input-wrp-prefix-has-content fi-inline">
639
+ <%- icon('m-magnifying-glass', { attributes: attrs({ 'wire:loading.remove.delay.default': '1', 'wire:target': 'tableColumnSearches.' + column.name }) }) %>
640
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': 'tableColumnSearches.' + column.name })) %>
641
+ </div>
642
+
643
+ <div class="fi-input-wrp-content-ctn">
644
+ <input class="fi-input fi-input-has-inline-prefix" autocomplete="off" maxlength="1000" placeholder="<%= t('filament-tables::table.fields.search.placeholder') %>" type="search" wire:key="<%= id %>.table.tableColumnSearches.<%= column.name %>.field.input" wire:model.live.debounce.500ms="tableColumnSearches.<%= column.name %>" x-bind:id="$id('input')" x-on:keyup="if ($event.key === 'Enter') { $wire.$refresh() }" />
645
+ </div>
646
+ </div>
647
+ </div>
648
+ <% } %>
649
+ </td>
650
+ <% } %>
651
+ <% if (rows.length) { %><td></td><% } %>
652
+ </tr>
653
+ </tbody>
654
+ <% } %>
655
+
656
+ <% if (rows.length) { %>
657
+ <tbody<% if (reorder) { %> x-ref="tableReorderRecords" x-on:start.stop="startTableReorder($event)" x-on:end.stop="reorderTableRecords($event)" x-sortable data-sortable-animation-duration="300"<% } %> class="fi-ta-records">
658
+ <% for (const row of rows) { %>
659
+ <%- row.groupSummaryHtml %>
660
+ <% if (grouping.groupsOnly) continue; %>
661
+ <% if (row.groupHeader) { const group = row.groupHeader; const groupTag = heading ? 'h4' : 'h3'; %>
662
+ <tr<% if (group.ariaHidden) { %> aria-hidden="true"<% } %> class="<%- cls(['fi-ta-row fi-ta-group-header-row', { 'fi-ta-group-header-row-with-selection': selectable }]) %>">
663
+ <% if (selectable) { %>
664
+ <td class="fi-ta-cell fi-ta-group-selection-cell">
665
+ <input aria-label="<%= t('filament-tables::table.fields.bulk_select_group.label', { title: group.title ?? '' }) %>" type="checkbox" data-group-selectable-record-keys="<%= JSON.stringify(group.keys) %>" x-on:click="toggleSelectRecords(JSON.parse($el.dataset.groupSelectableRecordKeys))" x-bind:checked="
666
+ const recordsInGroup = JSON.parse($el.dataset.groupSelectableRecordKeys)
667
+
668
+ if (recordsInGroup.length && areRecordsSelected(recordsInGroup)) {
669
+ $el.checked = true
670
+ $el.indeterminate = false
671
+
672
+ return 'checked'
673
+ }
674
+
675
+ $el.checked = false
676
+ $el.indeterminate =
677
+ recordsInGroup.length && areRecordsPartiallySelected(recordsInGroup)
678
+
679
+ return null
680
+ " wire:key="<%= id %>.table.bulk_select_group.checkbox.<%= page.page %>" wire:loading.attr="disabled" wire:target="<%= targets %>" class="fi-ta-group-checkbox fi-checkbox-input" />
681
+ </td>
682
+ <% } %>
683
+ <td colspan="<%= visibleColumns.length + (hasRowActions ? 1 : 0) %>" class="fi-ta-group-header-cell">
684
+ <div<% if (group.collapsible) { %> x-on:click="toggleCollapseGroup(<%= js(group.title) %>)" x-bind:class="isGroupCollapsed(<%= js(group.title) %>) ? 'fi-collapsed' : null"<% } %> class="<%- cls(['fi-ta-group-header', { 'fi-collapsible': group.collapsible }]) %>">
685
+ <div>
686
+ <<%- groupTag %> class="fi-ta-group-heading"><% if (group.label) { %><%= group.label %>: <% } %><%= group.title ?? '' %></<%- groupTag %>>
687
+ <% if (group.description) { %><p class="fi-ta-group-description"><%= group.description %></p><% } %>
688
+ </div>
689
+ <% if (group.collapsible) { %>
690
+ <button aria-label="<%= (group.label ? group.label + ': ' : '') + (group.title ?? '') %>" x-bind:aria-expanded="! isGroupCollapsed(<%= js(group.title) %>)" type="button" class="fi-icon-btn fi-size-sm"><%- icon('m-chevron-up', { size: 'sm' }) %></button>
691
+ <% } %>
692
+ </div>
693
+ </td>
694
+ </tr>
695
+ <% } %>
696
+ <tr wire:key="<%= id %>.table.records.<%= row.key %>" x-bind:class="{
697
+ <% if (row.collapsedExpression) { %> 'fi-collapsed': <%= row.collapsedExpression %>,
698
+ <% } %> 'fi-selected': <%- selectable ? 'true' : 'false' %> && isRecordSelected(<%= js(row.key) %>),
699
+ }"<% if (reorder?.active) { %> x-sortable-item="<%= row.key %>" x-sortable-handle<% } %> class="<%- cls(['fi-ta-row', { 'fi-clickable': row.url || row.mountExpression || row.wasClickable }, row.classes]) %>">
700
+ <% if (reorder?.active) { %>
701
+ <td class="fi-ta-cell">
702
+ <button aria-hidden="true" tabindex="-1" type="button" class="fi-ta-reorder-handle fi-icon-btn"><%- icon('m-bars-2') %></button>
703
+ <span id="<%= row.reorder.id %>" data-table-reorder-label="<%= row.reorder.label %>" class="fi-sr-only"><%= t('filament-tables::table.reordering.position', { label: row.reorder.label, position: row.reorder.position, count: rows.length }) %></span>
704
+ <div class="fi-sr-only">
705
+ <% for (const direction of ['up', 'down']) { %>
706
+ <button type="button" aria-describedby="<%= row.reorder.id %>"<% if ((direction === 'up' && row.reorder.position === 1) || (direction === 'down' && row.reorder.position === rows.length)) { %> disabled<% } %> tabindex="-1" data-table-reorder-direction="<%- direction %>" x-bind:aria-disabled="tableReorderOperation !== null || $el.disabled" x-on:click.stop="moveTableRecord($event, <%= js(row.key) %>, '<%- direction %>')"><%= t('filament-tables::table.actions.move_record_' + direction + '.label', { label: row.reorder.label }) %></button>
707
+ <% } %>
708
+ </div>
709
+ </td>
710
+ <% } else if (selectable) { %>
711
+ <td class="fi-ta-cell fi-ta-selection-cell">
712
+ <input aria-label="<%= t('filament-tables::table.fields.bulk_select_record.label', { key: row.key }) %>" type="checkbox" value="<%= row.key %>" x-on:click="toggleSelectedRecord(<%= js(row.key) %>)" x-bind:checked="isRecordSelected(<%= js(row.key) %>) ? 'checked' : null" data-group="<%= row.groupKey ?? '' %>" wire:loading.attr="disabled" wire:target="<%- targets %>" class="fi-ta-record-checkbox fi-checkbox-input" />
713
+ </td>
714
+ <% } %><%# fin cellule de sélection %>
715
+
716
+ <% for (const cell of row.cells) { %>
717
+ <td wire:key="<%= id %>.table.record.<%= row.key %>.column.<%= cell.name %>" class="<%- cls(['fi-ta-cell', 'fi-ta-cell-' + cell.cssName, cell.alignment && cell.alignment !== 'start' ? 'fi-align-' + cell.alignment : null]) %>">
718
+ <% if (cell.url) { %>
719
+ <% if (table.isStackedOnMobile()) { %><div class="fi-ta-cell-label"><%= cell.label %></div><div class="fi-ta-cell-content"><% } %>
720
+ <a href="<%= cell.url %>" x-on:click="if (! ($event.altKey || $event.ctrlKey || $event.metaKey || $event.shiftKey)) { $event.preventDefault(); Alpine.navigate($el.getAttribute('href')) }" class="fi-ta-col fi-ta-col-has-column-url">
721
+ <%- cell.html %>
722
+ </a>
723
+ <% if (table.isStackedOnMobile()) { %></div><% } %>
724
+ <% } else if (row.mountExpression && !cell.plain) { %>
725
+ <% if (table.isStackedOnMobile()) { %><div class="fi-ta-cell-label"><%= cell.label %></div><div class="fi-ta-cell-content"><% } %>
726
+ <button type="button" wire:click.prevent.stop="<%= row.mountExpression %>" wire:loading.attr="disabled" wire:target="<%= row.mountExpression %>" class="fi-ta-col">
727
+ <%- cell.html %>
728
+ </button>
729
+ <% if (table.isStackedOnMobile()) { %></div><% } %>
730
+ <% } else { %>
731
+ <%# L'enveloppe de contenu n'existe qu'en mode empilé (comme Filament) %>
732
+ <% if (table.isStackedOnMobile()) { %><div class="fi-ta-cell-label"><%= cell.label %></div><div class="fi-ta-cell-content"><% } %>
733
+ <% if (row.url && !cell.plain) { %>
734
+ <a href="<%= row.url %>" x-on:click="if (! ($event.altKey || $event.ctrlKey || $event.metaKey || $event.shiftKey)) { $event.preventDefault(); Alpine.navigate($el.getAttribute('href')) }" class="fi-ta-col">
735
+ <%- cell.html %>
736
+ </a>
737
+ <% } else { %>
738
+ <div class="fi-ta-col"><%- cell.html %></div>
739
+ <% } %>
740
+ <% if (table.isStackedOnMobile()) { %></div><% } %>
741
+ <% } %>
742
+ </td>
743
+ <% } %>
744
+
745
+ <% if (!reorder?.active && hasRowActions) { %>
746
+ <td class="fi-ta-cell">
747
+ <div class="fi-ta-actions ">
748
+ <%- row.actionsHtml ?? '' %>
749
+ <% for (const action of row.actions) { %>
750
+ <a href="<%= action.url %>" wire:navigate wire:key="<%= id %>.actions.<%= action.key %>" class="<%- cls(['fi-ac-link-action', 'fi-link', 'fi-size-sm', ...linkClasses]) %>">
751
+ <%- icon(action.icon, { size: 'sm' }) %>
752
+ <span class="fi-link-label">
753
+ <%= action.label %>
754
+ </span>
755
+ </a>
756
+ <% } %>
757
+ </div>
758
+ </td>
759
+ <% } %><%# fin cellule d'actions %>
760
+ </tr>
761
+ <% } %>
762
+ <%# Résumés (lignes déjà rendues, échappées) : celui du dernier groupe, puis ceux de la table. %>
763
+ <%- trailingGroupSummary %>
764
+ <%- summaryHtml %>
765
+ </tbody>
766
+ <% } %>
767
+ </table>
768
+ <% } %><%# fin tableau / cartes %>
769
+ </div>
770
+
771
+ <%- hook('tables::content.after', { table, records: page.records }) %>
772
+ <% if (rows.length) { %>
773
+ <%- pagination %>
774
+ <% } else { %>
775
+ <div class="fi-ta-empty-state" role="status">
776
+ <div class="fi-ta-empty-state-content">
777
+ <div class="fi-ta-empty-state-icon-bg">
778
+ <%- icon('o-x-mark', { size: 'lg', attributes: attrs({ 'wire:loading.remove.delay.default': '', 'wire:target': targets }) }) %>
779
+ <%- loadingIndicator(attrs({ 'wire:loading.delay.default': '', 'wire:target': targets }), 'lg') %>
780
+ </div>
781
+
782
+ <h2 class="fi-ta-empty-state-heading">
783
+ <%= t('filament-tables::table.empty.heading', { model: table.getPluralModelLabel() }) %>
784
+ </h2>
785
+ </div>
786
+ </div>
787
+ <% } %>
788
+ </div>
789
+ </div>
790
+ <%- afterContainer %>
791
+ </div>