infotable 1.4.1 → 1.5.0
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.
- checksums.yaml +4 -4
- data/README.md +35 -1
- data/app/assets/javascripts/infotable/controllers/drawer_controller.js +59 -364
- data/app/assets/javascripts/infotable/controllers/table_controller.js +71 -98
- data/app/assets/javascripts/infotable/vendor/sortable.esm.js +3363 -0
- data/app/controllers/infotable/tables_controller.rb +15 -4
- data/app/helpers/infotable/tables_helper.rb +0 -23
- data/app/views/infotable/tables/_column_visibility.html.erb +4 -4
- data/app/views/infotable/tables/_end_indicator.html.erb +8 -0
- data/app/views/infotable/tables/_next_page_frame.html.erb +1 -9
- data/app/views/infotable/tables/_table_rows.html.erb +9 -2
- data/app/views/infotable/tables/show.html.erb +2 -2
- data/config/importmap.rb +2 -2
- data/lib/generators/infotable/install/templates/initializer.rb +5 -3
- data/lib/infotable/base.rb +61 -4
- data/lib/infotable/configuration.rb +1 -1
- data/lib/infotable/engine.rb +1 -8
- data/lib/infotable/filter_processor.rb +10 -1
- data/lib/infotable/formatters/boolean_formatter.rb +4 -4
- data/lib/infotable/table_resolver.rb +9 -0
- data/lib/infotable/version.rb +1 -1
- metadata +3 -1
|
@@ -48,6 +48,8 @@ export default class extends Controller {
|
|
|
48
48
|
|
|
49
49
|
// Listen for column visibility changes to update export link
|
|
50
50
|
this.boundUpdateExportLink = this.updateExportLink.bind(this)
|
|
51
|
+
this.boundApplyColumns = this.applyColumnsFromDrawer.bind(this)
|
|
52
|
+
document.addEventListener("infotable:apply-columns", this.boundApplyColumns)
|
|
51
53
|
document.addEventListener("infotable:columns-changed", this.boundUpdateExportLink)
|
|
52
54
|
document.addEventListener("infotable:filters-changed", this.boundUpdateExportLink)
|
|
53
55
|
}
|
|
@@ -55,6 +57,7 @@ export default class extends Controller {
|
|
|
55
57
|
disconnect() {
|
|
56
58
|
this.element.removeEventListener("turbo:frame-load", this.boundApplyOrderAndVisibility)
|
|
57
59
|
document.removeEventListener("infotable:columns-changed", this.boundUpdateExportLink)
|
|
60
|
+
document.removeEventListener("infotable:apply-columns", this.boundApplyColumns)
|
|
58
61
|
document.removeEventListener("infotable:filters-changed", this.boundUpdateExportLink)
|
|
59
62
|
window.removeEventListener("resize", this.boundFitHeight)
|
|
60
63
|
cancelAnimationFrame(this.fitFrame)
|
|
@@ -88,22 +91,16 @@ export default class extends Controller {
|
|
|
88
91
|
this.applyColumnPinning()
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
// Handle loading states
|
|
92
|
-
beforeFetchRequest(event) {
|
|
93
|
-
this.showLoading()
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
afterFetchResponse(event) {
|
|
97
|
-
this.hideLoading()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
94
|
// Handle turbo frame load
|
|
101
95
|
handleFrameLoad(event) {
|
|
102
96
|
this.fitHeight()
|
|
103
97
|
this.updateFilterCount(event.target)
|
|
104
98
|
// rows matching the current filters, when the server knows it (not with countless pagination)
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
// (a nested frame, e.g. the infinite-scroll "next page" one, carries no count: keep the last one)
|
|
100
|
+
if (event.target.id === `${this.nameValue}_content`) {
|
|
101
|
+
const total = parseInt(event.target.querySelector("[data-total-count]")?.dataset.totalCount, 10)
|
|
102
|
+
this.totalCount = Number.isFinite(total) ? total : null
|
|
103
|
+
}
|
|
107
104
|
this.syncSelectionScope(event.target)
|
|
108
105
|
if (this.urlStateValue && event.target.id === `${this.nameValue}_content`) this.syncUrl(event.target)
|
|
109
106
|
}
|
|
@@ -179,7 +176,7 @@ export default class extends Controller {
|
|
|
179
176
|
}
|
|
180
177
|
|
|
181
178
|
// The first change on a fresh page replaces the entry, later ones add entries for Back
|
|
182
|
-
const state = { turbo: { restorationIdentifier: crypto.randomUUID() } }
|
|
179
|
+
const state = { turbo: { restorationIdentifier: (crypto.randomUUID?.() ?? `infotable-${Date.now()}-${Math.random().toString(36).slice(2)}`) } }
|
|
183
180
|
if (this.urlStateStarted) {
|
|
184
181
|
window.history.pushState(state, "", next)
|
|
185
182
|
} else {
|
|
@@ -240,7 +237,17 @@ export default class extends Controller {
|
|
|
240
237
|
// Fires for rows added later (next page, infinite scroll) so they pick up the selection
|
|
241
238
|
rowCheckboxTargetConnected(checkbox) {
|
|
242
239
|
checkbox.checked = this.selected.has(checkbox.value)
|
|
243
|
-
this.
|
|
240
|
+
this.scheduleRenderSelection()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// A page connects up to 500 checkboxes at once: render the selection once for all of them
|
|
244
|
+
scheduleRenderSelection() {
|
|
245
|
+
if (this.renderScheduled) return
|
|
246
|
+
this.renderScheduled = true
|
|
247
|
+
queueMicrotask(() => {
|
|
248
|
+
this.renderScheduled = false
|
|
249
|
+
this.renderSelection()
|
|
250
|
+
})
|
|
244
251
|
}
|
|
245
252
|
|
|
246
253
|
toggleRow(event) {
|
|
@@ -379,15 +386,6 @@ export default class extends Controller {
|
|
|
379
386
|
form.remove()
|
|
380
387
|
}
|
|
381
388
|
|
|
382
|
-
showLoading() {
|
|
383
|
-
// Add loading indicator (to be enhanced in later phases)
|
|
384
|
-
this.element.style.opacity = "0.6"
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
hideLoading() {
|
|
388
|
-
this.element.style.opacity = "1"
|
|
389
|
-
}
|
|
390
|
-
|
|
391
389
|
// Shift+click on a sortable header adds/toggles the column in the multi-sort list
|
|
392
390
|
// (sort=name:asc,age:desc). A plain click keeps the link's single-column behaviour.
|
|
393
391
|
sortClick(event) {
|
|
@@ -491,8 +489,18 @@ export default class extends Controller {
|
|
|
491
489
|
}
|
|
492
490
|
}
|
|
493
491
|
|
|
494
|
-
//
|
|
495
|
-
|
|
492
|
+
// The columns drawer changed order, visibility or pinning (it stored them in localStorage)
|
|
493
|
+
applyColumnsFromDrawer(event) {
|
|
494
|
+
const { tableName, order } = event.detail
|
|
495
|
+
if (tableName !== this.nameValue) return
|
|
496
|
+
|
|
497
|
+
this.applyColumnOrder(order)
|
|
498
|
+
this.applyColumnVisibility()
|
|
499
|
+
this.applyColumnPinning()
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Apply column order: the given one, else the one in localStorage
|
|
503
|
+
applyColumnOrder(explicitOrder = null) {
|
|
496
504
|
const tableName = this.nameValue
|
|
497
505
|
const orderKey = `infotable_column_order_${tableName}`
|
|
498
506
|
|
|
@@ -520,11 +528,11 @@ export default class extends Controller {
|
|
|
520
528
|
const defaultSchema = headers.map(h => h.dataset.columnName)
|
|
521
529
|
|
|
522
530
|
// Load stored order from localStorage
|
|
523
|
-
const storedOrder = this.loadColumnOrder(tableName)
|
|
531
|
+
const storedOrder = explicitOrder || this.loadColumnOrder(tableName)
|
|
524
532
|
if (!storedOrder) return
|
|
525
533
|
|
|
526
534
|
// Validate stored order against default schema
|
|
527
|
-
if (!this.isSchemaValid(storedOrder, defaultSchema)) {
|
|
535
|
+
if (!explicitOrder && !this.isSchemaValid(storedOrder, defaultSchema)) {
|
|
528
536
|
console.warn(`Schema mismatch for table ${tableName}, clearing stored order`)
|
|
529
537
|
this.clearColumnOrder(tableName)
|
|
530
538
|
return
|
|
@@ -628,7 +636,6 @@ export default class extends Controller {
|
|
|
628
636
|
}
|
|
629
637
|
|
|
630
638
|
const headers = table.querySelectorAll("thead th[data-column-name]")
|
|
631
|
-
const rows = table.querySelectorAll("tbody tr")
|
|
632
639
|
|
|
633
640
|
if (headers.length === 0) {
|
|
634
641
|
console.warn(`No column headers found for table: ${tableName}`)
|
|
@@ -642,31 +649,18 @@ export default class extends Controller {
|
|
|
642
649
|
visibility = JSON.parse(stored)
|
|
643
650
|
}
|
|
644
651
|
|
|
652
|
+
const shown = new Map()
|
|
645
653
|
headers.forEach((header) => {
|
|
646
654
|
const columnName = header.dataset.columnName
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
let shouldShow
|
|
651
|
-
if (visibility && visibility.hasOwnProperty(columnName)) {
|
|
652
|
-
// Use saved preference
|
|
653
|
-
shouldShow = visibility[columnName]
|
|
654
|
-
} else {
|
|
655
|
-
// Use default visibility
|
|
656
|
-
shouldShow = defaultVisible
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
// Apply visibility
|
|
655
|
+
const saved = visibility && Object.prototype.hasOwnProperty.call(visibility, columnName)
|
|
656
|
+
const shouldShow = saved ? visibility[columnName] : header.dataset.defaultVisible === "true"
|
|
657
|
+
shown.set(columnName, shouldShow)
|
|
660
658
|
header.style.display = shouldShow ? "" : "none"
|
|
659
|
+
})
|
|
661
660
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const cell = Array.from(cells).find(c => c.dataset.columnName === columnName)
|
|
666
|
-
if (cell) {
|
|
667
|
-
cell.style.display = shouldShow ? "" : "none"
|
|
668
|
-
}
|
|
669
|
-
})
|
|
661
|
+
// One pass over the cells: hide/show each by its column
|
|
662
|
+
table.querySelectorAll("tbody td[data-column-name]").forEach(cell => {
|
|
663
|
+
if (shown.has(cell.dataset.columnName)) cell.style.display = shown.get(cell.dataset.columnName) ? "" : "none"
|
|
670
664
|
})
|
|
671
665
|
} catch (e) {
|
|
672
666
|
console.warn("Failed to apply column visibility", e)
|
|
@@ -781,34 +775,31 @@ export default class extends Controller {
|
|
|
781
775
|
// The data-turbo="false" ensures Turbo doesn't intercept this
|
|
782
776
|
}
|
|
783
777
|
|
|
784
|
-
//
|
|
778
|
+
// Row click (`row_link` and/or `row_click`). Fires a cancelable `infotable:row-click` on the row;
|
|
779
|
+
// when nothing calls preventDefault() the click falls back to following the row's link.
|
|
785
780
|
handleRowClick(event) {
|
|
786
781
|
const row = event.currentTarget
|
|
787
|
-
const url = row.dataset.rowLink
|
|
788
782
|
|
|
789
|
-
|
|
783
|
+
// Clicks on controls inside the row belong to those controls
|
|
784
|
+
if (event.target.closest("a, button, summary, details, label, input, select, textarea")) return
|
|
790
785
|
|
|
791
|
-
|
|
792
|
-
const
|
|
793
|
-
|
|
786
|
+
const url = row.dataset.rowLink || null
|
|
787
|
+
const data = { ...row.dataset }
|
|
788
|
+
delete data.rowLink
|
|
789
|
+
delete data.action
|
|
794
790
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// Ignore clicks on elements inside buttons or links
|
|
802
|
-
if (target.closest("a") || target.closest("button")) {
|
|
803
|
-
return
|
|
804
|
-
}
|
|
791
|
+
const click = new CustomEvent("infotable:row-click", {
|
|
792
|
+
bubbles: true,
|
|
793
|
+
cancelable: true,
|
|
794
|
+
detail: { id: row.id || null, url, data, originalEvent: event }
|
|
795
|
+
})
|
|
796
|
+
if (!row.dispatchEvent(click) || !url) return
|
|
805
797
|
|
|
806
|
-
//
|
|
807
|
-
// Check if meta key (Cmd on Mac, Ctrl on Windows) is pressed for new tab
|
|
798
|
+
// Cmd on Mac, Ctrl elsewhere: new tab
|
|
808
799
|
if (event.metaKey || event.ctrlKey) {
|
|
809
800
|
window.open(url, "_blank")
|
|
810
801
|
} else {
|
|
811
|
-
window.location.href = url
|
|
802
|
+
window.Turbo ? window.Turbo.visit(url) : (window.location.href = url)
|
|
812
803
|
}
|
|
813
804
|
}
|
|
814
805
|
|
|
@@ -966,6 +957,12 @@ export default class extends Controller {
|
|
|
966
957
|
}
|
|
967
958
|
|
|
968
959
|
// Apply column pinning (sticky positioning) from localStorage
|
|
960
|
+
// Small pushpin before a pinned column's label, tilted toward the side it is pinned to
|
|
961
|
+
addPinIcon(header, side) {
|
|
962
|
+
const tilt = side === "left" ? "-rotate-45" : "rotate-45"
|
|
963
|
+
header.insertAdjacentHTML("afterbegin", `<svg data-pin-icon aria-hidden="true" class="inline-block w-3 h-3 mr-1 -mt-0.5 text-indigo-500 dark:text-indigo-400 ${tilt}" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>`)
|
|
964
|
+
}
|
|
965
|
+
|
|
969
966
|
applyColumnPinning() {
|
|
970
967
|
const tableName = this.nameValue
|
|
971
968
|
const pinnedKey = `infotable_column_pinned_${tableName}`
|
|
@@ -1011,6 +1008,7 @@ export default class extends Controller {
|
|
|
1011
1008
|
// First pass: reset all columns
|
|
1012
1009
|
headers.forEach(header => {
|
|
1013
1010
|
header.style.position = ''
|
|
1011
|
+
header.style.top = ''
|
|
1014
1012
|
header.style.left = ''
|
|
1015
1013
|
header.style.right = ''
|
|
1016
1014
|
header.style.zIndex = ''
|
|
@@ -1024,6 +1022,7 @@ export default class extends Controller {
|
|
|
1024
1022
|
header.style.removeProperty('opacity')
|
|
1025
1023
|
header.style.backgroundClip = ''
|
|
1026
1024
|
header.style.boxShadow = ''
|
|
1025
|
+
header.querySelectorAll('[data-pin-icon]').forEach(icon => icon.remove())
|
|
1027
1026
|
header.classList.remove('infotable-pinned-left', 'infotable-pinned-right', '!bg-gray-50', 'dark:!bg-gray-800', "after:content-['']", "after:absolute", "after:top-0", "after:right-0", "after:bottom-0", "after:w-5", "after:pointer-events-none", "after:bg-gradient-to-r", "after:from-transparent", "after:to-black/[0.04]", "after:z-[1]", "before:content-['']", "before:absolute", "before:top-0", "before:left-0", "before:bottom-0", "before:w-5", "before:pointer-events-none", "before:bg-gradient-to-l", "before:from-transparent", "before:to-black/[0.04]", "before:z-[1]")
|
|
1028
1027
|
})
|
|
1029
1028
|
|
|
@@ -1044,7 +1043,7 @@ export default class extends Controller {
|
|
|
1044
1043
|
cell.style.removeProperty('opacity')
|
|
1045
1044
|
cell.style.backgroundClip = ''
|
|
1046
1045
|
cell.style.boxShadow = ''
|
|
1047
|
-
cell.classList.remove('infotable-pinned-left', 'infotable-pinned-right', '!bg-white', 'dark:!bg-gray-900', "after:content-['']", "after:absolute", "after:top-0", "after:right-0", "after:bottom-0", "after:w-5", "after:pointer-events-none", "after:bg-gradient-to-r", "after:from-transparent", "after:to-black/[0.04]", "after:z-[1]", "before:content-['']", "before:absolute", "before:top-0", "before:left-0", "before:bottom-0", "before:w-5", "before:pointer-events-none", "before:bg-gradient-to-l", "before:from-transparent", "before:to-black/[0.04]", "before:z-[1]")
|
|
1046
|
+
cell.classList.remove('infotable-pinned-left', 'infotable-pinned-right', '!bg-white', 'dark:!bg-gray-900', 'group-hover:!bg-gray-50', 'dark:group-hover:!bg-gray-800', "after:content-['']", "after:absolute", "after:top-0", "after:right-0", "after:bottom-0", "after:w-5", "after:pointer-events-none", "after:bg-gradient-to-r", "after:from-transparent", "after:to-black/[0.04]", "after:z-[1]", "before:content-['']", "before:absolute", "before:top-0", "before:left-0", "before:bottom-0", "before:w-5", "before:pointer-events-none", "before:bg-gradient-to-l", "before:from-transparent", "before:to-black/[0.04]", "before:z-[1]")
|
|
1048
1047
|
})
|
|
1049
1048
|
})
|
|
1050
1049
|
|
|
@@ -1078,13 +1077,6 @@ export default class extends Controller {
|
|
|
1078
1077
|
top: 0px !important;
|
|
1079
1078
|
left: ${leftOffset}px !important;
|
|
1080
1079
|
z-index: 30 !important;
|
|
1081
|
-
background: #f9fafb !important;
|
|
1082
|
-
background-color: #f9fafb !important;
|
|
1083
|
-
background-image: none !important;
|
|
1084
|
-
background-position: initial !important;
|
|
1085
|
-
background-size: initial !important;
|
|
1086
|
-
background-repeat: initial !important;
|
|
1087
|
-
background-attachment: initial !important;
|
|
1088
1080
|
opacity: 1 !important;
|
|
1089
1081
|
background-clip: padding-box !important;
|
|
1090
1082
|
box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.1) !important;
|
|
@@ -1094,6 +1086,7 @@ export default class extends Controller {
|
|
|
1094
1086
|
// Set all styles at once via setAttribute (most aggressive method)
|
|
1095
1087
|
header.setAttribute('style', newStyles)
|
|
1096
1088
|
header.classList.add('infotable-pinned-left', '!bg-gray-50', 'dark:!bg-gray-800')
|
|
1089
|
+
this.addPinIcon(header, 'left')
|
|
1097
1090
|
|
|
1098
1091
|
// Mark last left-pinned column
|
|
1099
1092
|
if (columnName === lastLeftPinned) {
|
|
@@ -1112,13 +1105,6 @@ export default class extends Controller {
|
|
|
1112
1105
|
position: sticky !important;
|
|
1113
1106
|
left: ${leftOffset}px !important;
|
|
1114
1107
|
z-index: 15 !important;
|
|
1115
|
-
background: #ffffff !important;
|
|
1116
|
-
background-color: #ffffff !important;
|
|
1117
|
-
background-image: none !important;
|
|
1118
|
-
background-position: initial !important;
|
|
1119
|
-
background-size: initial !important;
|
|
1120
|
-
background-repeat: initial !important;
|
|
1121
|
-
background-attachment: initial !important;
|
|
1122
1108
|
opacity: 1 !important;
|
|
1123
1109
|
background-clip: padding-box !important;
|
|
1124
1110
|
box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.1) !important;
|
|
@@ -1126,7 +1112,7 @@ export default class extends Controller {
|
|
|
1126
1112
|
`.replace(/\s+/g, ' ').trim()
|
|
1127
1113
|
|
|
1128
1114
|
cell.setAttribute('style', newStyles)
|
|
1129
|
-
cell.classList.add('infotable-pinned-left', '!bg-white', 'dark:!bg-gray-900')
|
|
1115
|
+
cell.classList.add('infotable-pinned-left', '!bg-white', 'dark:!bg-gray-900', 'group-hover:!bg-gray-50', 'dark:group-hover:!bg-gray-800')
|
|
1130
1116
|
|
|
1131
1117
|
if (columnName === lastLeftPinned) {
|
|
1132
1118
|
cell.classList.add("after:content-['']", "after:absolute", "after:top-0", "after:right-0", "after:bottom-0", "after:w-5", "after:pointer-events-none", "after:bg-gradient-to-r", "after:from-transparent", "after:to-black/[0.04]", "after:z-[1]")
|
|
@@ -1145,13 +1131,6 @@ export default class extends Controller {
|
|
|
1145
1131
|
top: 0px !important;
|
|
1146
1132
|
right: ${rightOffset}px !important;
|
|
1147
1133
|
z-index: 30 !important;
|
|
1148
|
-
background: #f9fafb !important;
|
|
1149
|
-
background-color: #f9fafb !important;
|
|
1150
|
-
background-image: none !important;
|
|
1151
|
-
background-position: initial !important;
|
|
1152
|
-
background-size: initial !important;
|
|
1153
|
-
background-repeat: initial !important;
|
|
1154
|
-
background-attachment: initial !important;
|
|
1155
1134
|
opacity: 1 !important;
|
|
1156
1135
|
background-clip: padding-box !important;
|
|
1157
1136
|
box-shadow: -2px 0 4px -2px rgba(0, 0, 0, 0.1) !important;
|
|
@@ -1160,6 +1139,7 @@ export default class extends Controller {
|
|
|
1160
1139
|
|
|
1161
1140
|
header.setAttribute('style', newStyles)
|
|
1162
1141
|
header.classList.add('infotable-pinned-right', '!bg-gray-50', 'dark:!bg-gray-800')
|
|
1142
|
+
this.addPinIcon(header, 'right')
|
|
1163
1143
|
|
|
1164
1144
|
// Mark first right-pinned column
|
|
1165
1145
|
if (columnName === firstRightPinned) {
|
|
@@ -1178,13 +1158,6 @@ export default class extends Controller {
|
|
|
1178
1158
|
position: sticky !important;
|
|
1179
1159
|
right: ${rightOffset}px !important;
|
|
1180
1160
|
z-index: 15 !important;
|
|
1181
|
-
background: #ffffff !important;
|
|
1182
|
-
background-color: #ffffff !important;
|
|
1183
|
-
background-image: none !important;
|
|
1184
|
-
background-position: initial !important;
|
|
1185
|
-
background-size: initial !important;
|
|
1186
|
-
background-repeat: initial !important;
|
|
1187
|
-
background-attachment: initial !important;
|
|
1188
1161
|
opacity: 1 !important;
|
|
1189
1162
|
background-clip: padding-box !important;
|
|
1190
1163
|
box-shadow: -2px 0 4px -2px rgba(0, 0, 0, 0.1) !important;
|
|
@@ -1192,7 +1165,7 @@ export default class extends Controller {
|
|
|
1192
1165
|
`.replace(/\s+/g, ' ').trim()
|
|
1193
1166
|
|
|
1194
1167
|
cell.setAttribute('style', newStyles)
|
|
1195
|
-
cell.classList.add('infotable-pinned-right', '!bg-white', 'dark:!bg-gray-900')
|
|
1168
|
+
cell.classList.add('infotable-pinned-right', '!bg-white', 'dark:!bg-gray-900', 'group-hover:!bg-gray-50', 'dark:group-hover:!bg-gray-800')
|
|
1196
1169
|
|
|
1197
1170
|
if (columnName === firstRightPinned) {
|
|
1198
1171
|
cell.classList.add("before:content-['']", "before:absolute", "before:top-0", "before:left-0", "before:bottom-0", "before:w-5", "before:pointer-events-none", "before:bg-gradient-to-l", "before:from-transparent", "before:to-black/[0.04]", "before:z-[1]")
|