Punk-Queue
view release on metacpan or search on metacpan
lib/Punk/Plugin/Queue/assets/funky/js/35-table.js view on Meta::CPAN
/**
* Set live connection status
*/
TableInstance.prototype._setLiveConnected = function(connected) {
if (!this._liveBinding) return;
this._liveBinding.connected = connected;
// Update UI indicator
var indicator = this.container.querySelector('.funky-live-indicator');
if (indicator) {
Funky.Dom.one(indicator)
.classRemove('funky-live-connected', 'funky-live-disconnected')
.classAdd(connected ? 'funky-live-connected' : 'funky-live-disconnected');
}
// Emit event
this._emit(connected ? 'liveConnected' : 'liveDisconnected', {});
};
/**
* Create live status indicator
*/
TableInstance.prototype._createLiveIndicator = function() {
var config = this.config.liveBinding;
if (!config || !config.enabled) return;
if (config.showIndicator === false) return;
var D = Funky.Dom;
var indicator = D.create('span')
.classAdd('funky-live-indicator')
.classAdd('funky-live-disconnected')
.attr('title', 'Live updates')
.append(D.create('span').classAdd('funky-live-dot'))
.append(D.create('span').classAdd('funky-live-label').text('Live'));
// Add to header actions
var headerActions = D.one(this.container).one('.funky-table-header-actions');
if (headerActions) {
headerActions.append(indicator);
} else {
// Add before table
D.one(this.container).prepend(indicator);
}
};
// =========================================================================
// Responsive (Phase 17)
// =========================================================================
/**
* Default breakpoints
*/
var DEFAULT_BREAKPOINTS = [
{ name: 'mobile', width: 480 },
{ name: 'mobile-l', width: 640 },
{ name: 'tablet', width: 768 },
{ name: 'tablet-l', width: 1024 },
{ name: 'laptop', width: 1280 },
{ name: 'desktop', width: 10000 }
];
/**
* Determine current breakpoint based on container width
* @param {boolean} force - Force recalculation even if breakpoint hasn't changed
*/
TableInstance.prototype._determineBreakpoint = function(force) {
if (!this.config.responsive) return;
// Use wrapper element for width detection (container may be replaced)
var widthElement = this.wrapper && this.wrapper.el ? this.wrapper.el : this.container;
var containerWidth = widthElement.offsetWidth || window.innerWidth;
// Skip if container has no width (still hidden)
if (containerWidth === 0) return;
if (this.config.debug) {
console.log('[Funky.Table] _determineBreakpoint containerWidth:', containerWidth, 'force:', !!force);
}
// Support responsive as object { enabled: true, breakpoints: [...] } or just breakpoints at config level
var responsiveConfig = typeof this.config.responsive === 'object' ? this.config.responsive : {};
var breakpoints = responsiveConfig.breakpoints || this.config.breakpoints || DEFAULT_BREAKPOINTS;
// Ensure breakpoints is an array
if (!Array.isArray(breakpoints)) {
breakpoints = DEFAULT_BREAKPOINTS;
}
var newBreakpoint = 'desktop';
// Sort breakpoints by width descending
var sorted = breakpoints.slice().sort(function(a, b) {
return b.width - a.width;
});
for (var i = 0; i < sorted.length; i++) {
if (containerWidth <= sorted[i].width) {
newBreakpoint = sorted[i].name;
}
}
if (force || newBreakpoint !== this.currentBreakpoint) {
this.currentBreakpoint = newBreakpoint;
this._updateResponsiveColumns();
if (this.config.debug) {
console.log('[Funky.Table] Breakpoint changed:', newBreakpoint, '(' + containerWidth + 'px)', force ? '(forced)' : '');
}
this._emit('breakpointChange', { breakpoint: newBreakpoint, width: containerWidth });
}
};
/**
* Handle window resize
*/
TableInstance.prototype._handleResize = function() {
var self = this;
// Debounce resize
if (this._resizeTimeout) {
clearTimeout(this._resizeTimeout);
}
this._resizeTimeout = setTimeout(function() {
self._determineBreakpoint();
}, 100);
};
/**
* Update visible columns based on breakpoint
*/
TableInstance.prototype._updateResponsiveColumns = function() {
if (!this.config.responsive) return;
if (this.config.debug) {
console.log('[Funky.Table] _updateResponsiveColumns called, breakpoint:', this.currentBreakpoint);
console.log('[Funky.Table] tbody rows before:', this.tbody && this.tbody.el ? this.tbody.el.querySelectorAll('tr').length : 0);
}
var self = this;
var breakpoint = this.currentBreakpoint;
// Use this.columns (processed) not this.config.columns (original)
var columns = this.columns;
// Get minimum priority for this breakpoint
var maxPriority = this._getMaxPriorityForBreakpoint(breakpoint);
if (this.config.debug) {
lib/Punk/Plugin/Queue/assets/funky/js/35-table.js view on Meta::CPAN
if (col.responsive[breakpoint] === false) {
columnsToHide.push(index);
return;
}
}
// Check priority - columns without explicit responsivePriority always show
// Lower priority numbers = more important = stay visible longer
// Higher priority numbers = less important = hide first
var colPriority = col.responsivePriority;
if (colPriority === undefined || colPriority === null) {
// No priority set - always show
columnsToShow.push(index);
} else if (colPriority > maxPriority) {
// Priority exceeds breakpoint threshold - hide (less important)
columnsToHide.push(index);
} else {
columnsToShow.push(index);
}
});
// Store visibility info (include manually hidden in hidden count for control column)
this.visibleColumns = columnsToShow;
this.hiddenColumns = columnsToHide;
this.manuallyHiddenColumns = manuallyHiddenColumns;
if (this.config.debug) {
console.log('[Funky.Table] Responsive: columnsToShow:', columnsToShow, 'columnsToHide:', columnsToHide, 'manuallyHidden:', manuallyHiddenColumns);
console.log('[Funky.Table] Total columns:', columns.length, 'visible:', columnsToShow.length, 'hidden:', columnsToHide.length, 'manuallyHidden:', manuallyHiddenColumns.length);
}
// Apply visibility (only for responsive columns, not manually hidden ones)
this._applyColumnVisibility(columnsToShow, columnsToHide);
if (this.config.debug) {
console.log('[Funky.Table] After _applyColumnVisibility, tbody rows:', this.tbody && this.tbody.el ? this.tbody.el.querySelectorAll('tr').length : 0);
}
// Show control column if ANY columns are hidden (responsive OR manual)
var totalHidden = columnsToHide.length + manuallyHiddenColumns.length;
this._updateControlColumn(totalHidden > 0);
// Re-render aggregation row to update hidden columns
if (this._aggregations) {
this._renderAggregations();
}
// Collapse all expanded details rows when breakpoint changes
this._collapseAllDetails();
if (this.config.debug) {
console.log('[Funky.Table] After _collapseAllDetails, tbody rows:', this.tbody && this.tbody.el ? this.tbody.el.querySelectorAll('tr').length : 0);
}
};
/**
* Get breakpoint priority number
*/
TableInstance.prototype._getBreakpointPriority = function(breakpoint) {
var priorities = {
'desktop': 1,
'laptop': 2,
'tablet-l': 3,
'tablet': 4,
'mobile-l': 5,
'mobile': 6
};
return priorities[breakpoint] || 1;
};
/**
* Get maximum priority to show at breakpoint
* Priority scale: 1-10 (lower = more important = stays visible longer)
* Columns with no responsivePriority always remain visible
*/
TableInstance.prototype._getMaxPriorityForBreakpoint = function(breakpoint) {
// Threshold = maximum priority value to SHOW at this breakpoint
// Lower priority number = more important = stays visible longer
// Columns with priority > threshold will be hidden
// At desktop (widest), threshold is high - show all columns
// At mobile (narrowest), threshold is low - only priority 1-2 shown
var thresholds = {
'desktop': 10000, // Show all (priority 1-10000)
'laptop': 6, // Show priority 1-6
'tablet-l': 5, // Show priority 1-5
'tablet': 4, // Show priority 1-4
'mobile-l': 3, // Show priority 1-3
'mobile': 2 // Show priority 1-2
};
return thresholds[breakpoint] || 10000;
};
/**
* Apply column visibility to DOM
*/
TableInstance.prototype._applyColumnVisibility = function(show, hide) {
var D = Funky.Dom;
var self = this;
// Update column state
var columns = this.config.columns;
columns.forEach(function(col, index) {
col._responsive_hidden = hide.indexOf(index) !== -1;
});
// Check if any columns are being hidden (responsive mode active)
var hasHiddenColumns = hide.length > 0;
// Headers - update visibility and widths
if (this._headerRow) {
var headers = this._headerRow.querySelectorAll('th');
for (var i = 0; i < headers.length; i++) {
var th = headers[i];
var index = parseInt(th.getAttribute('data-column-index'), 10);
if (!isNaN(index)) {
if (hide.indexOf(index) !== -1) {
th.classList.add('funky-table-hidden');
} else {
th.classList.remove('funky-table-hidden');
// When responsive mode is active, clear fixed widths so columns can expand
// When back to desktop, restore original widths
if (hasHiddenColumns) {
// Store original width if not already stored
if (!th.hasAttribute('data-original-width') && th.style.width) {
th.setAttribute('data-original-width', th.style.width);
}
th.style.width = '';
} else {
// Restore original width if we stored one
var originalWidth = th.getAttribute('data-original-width');
if (originalWidth) {
th.style.width = originalWidth;
}
}
}
}
}
}
// Body cells
if (this.tbody && this.tbody.el) {
var rows = this.tbody.el.querySelectorAll('tr:not(.funky-table-details-row)');
for (var r = 0; r < rows.length; r++) {
var cells = rows[r].querySelectorAll('td');
for (var c = 0; c < cells.length; c++) {
var td = cells[c];
var colIndex = parseInt(td.getAttribute('data-column-index'), 10);
if (!isNaN(colIndex)) {
if (hide.indexOf(colIndex) !== -1) {
td.classList.add('funky-table-hidden');
} else {
td.classList.remove('funky-table-hidden');
}
}
}
}
}
// Footer cells
if (this._tfoot) {
var footerCells = this._tfoot.querySelectorAll('td');
for (var f = 0; f < footerCells.length; f++) {
var ftd = footerCells[f];
var fIndex = parseInt(ftd.getAttribute('data-column-index'), 10);
if (!isNaN(fIndex)) {
if (hide.indexOf(fIndex) !== -1) {
ftd.classList.add('funky-table-hidden');
} else {
ftd.classList.remove('funky-table-hidden');
}
}
}
}
};
/**
* Update control column visibility
*/
TableInstance.prototype._updateControlColumn = function(show) {
var controlHeader = this._headerRow ? this._headerRow.querySelector('th.funky-table-control-header') : null;
var controlCells = this.tbody && this.tbody.el ? this.tbody.el.querySelectorAll('td.funky-table-control-cell') : [];
( run in 0.423 second using v1.01-cache-2.11-cpan-b16cb0d3907 )