fixed some bugs

This commit is contained in:
fynks 2026-04-19 14:26:12 +05:00
parent 943915a242
commit 69bd604279

81
dist/js/app.js vendored
View file

@ -52,7 +52,7 @@ class ComponentLifecycle {
onDestroy(fn) { onDestroy(fn) {
if (this.#isDestroyed) { if (this.#isDestroyed) {
console.warn('Cannot register cleanup on destroyed component'); console.warn('Cannot register cleanup on destroyed component');
return () => {}; return () => { };
} }
this.#cleanupFns.push(fn); this.#cleanupFns.push(fn);
@ -143,7 +143,7 @@ class EventBus {
emit(event, data) { emit(event, data) {
const listeners = this.#events.get(event); const listeners = this.#events.get(event);
if (listeners) { if (listeners) {
listeners.forEach(callback => { [...listeners].forEach(callback => {
try { try {
callback(data); callback(data);
} catch (error) { } catch (error) {
@ -161,8 +161,8 @@ class EventBus {
*/ */
once(event, callback) { once(event, callback) {
const wrappedCallback = (data) => { const wrappedCallback = (data) => {
callback(data);
this.off(event, wrappedCallback); this.off(event, wrappedCallback);
callback(data);
}; };
return this.on(event, wrappedCallback); return this.on(event, wrappedCallback);
} }
@ -368,7 +368,7 @@ const memoize = (fn, options = {}) => {
const maxSize = options.maxSize || 100; const maxSize = options.maxSize || 100;
const keyGenerator = options.keyGenerator || JSON.stringify; const keyGenerator = options.keyGenerator || JSON.stringify;
const memoized = function(...args) { const memoized = function (...args) {
const key = keyGenerator(args); const key = keyGenerator(args);
if (cache.has(key)) { if (cache.has(key)) {
@ -401,16 +401,12 @@ const Utils = (() => {
const debounceCache = new WeakMap(); const debounceCache = new WeakMap();
const debounce = (func, wait = CONFIG.PERFORMANCE.DEBOUNCE_DELAY) => { const debounce = (func, wait = CONFIG.PERFORMANCE.DEBOUNCE_DELAY) => {
if (debounceCache.has(func)) return debounceCache.get(func);
let timeoutId; let timeoutId;
const debounced = function executeDebouncedFunction(...args) { const debounced = function (...args) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), wait); timeoutId = setTimeout(() => func.apply(this, args), wait);
}; };
debounced.cancel = () => clearTimeout(timeoutId); debounced.cancel = () => clearTimeout(timeoutId);
debounceCache.set(func, debounced);
return debounced; return debounced;
}; };
@ -831,7 +827,7 @@ class FocusManager {
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])' 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled])'
); );
if (focusable.length === 0) return () => {}; if (focusable.length === 0) return () => { };
const first = focusable[0]; const first = focusable[0];
const last = focusable[focusable.length - 1]; const last = focusable[focusable.length - 1];
@ -901,7 +897,7 @@ class KeyboardNavigation {
static setupTableNavigation(table) { static setupTableNavigation(table) {
let currentRow = -1; let currentRow = -1;
const tbody = table.querySelector('tbody'); const tbody = table.querySelector('tbody');
if (!tbody) return () => {}; if (!tbody) return () => { };
const getRows = () => Array.from(tbody.querySelectorAll('tr:not(.empty-state-row)')); const getRows = () => Array.from(tbody.querySelectorAll('tr:not(.empty-state-row)'));
@ -963,7 +959,7 @@ class ResponsiveTable {
*/ */
static makeResponsive(table) { static makeResponsive(table) {
const wrapper = table.closest('.table-wrapper'); const wrapper = table.closest('.table-wrapper');
if (!wrapper) return () => {}; if (!wrapper) return () => { };
const checkResponsive = () => { const checkResponsive = () => {
const isMobile = window.innerWidth < 768; const isMobile = window.innerWidth < 768;
@ -1302,8 +1298,9 @@ class TableManager {
}); });
// Create header // Create header
const thead = Utils.createElement('thead'); const thead = Utils.createElement('thead');
thead.innerHTML = this.#generateHeaderHTML(columns); thead.appendChild(this.#generateHeaderRow(columns));
table.appendChild(thead); table.appendChild(thead);
// Create body // Create body
@ -1328,31 +1325,35 @@ class TableManager {
this.#renderTableBody(); this.#renderTableBody();
} }
#generateHeaderHTML(columns) { #generateHeaderRow(columns) {
const generateHeaderCell = (label, dataColumn) => ` const row = Utils.createElement('tr');
<th class="sortable"
data-column="${dataColumn}"
tabindex="0"
role="columnheader"
aria-sort="none">
<span>${label}</span>
<svg class="sort-icon" width="12" height="12" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path d="M12 5v14M5 12l7-7 7 7"/>
</svg>
</th>
`;
const columnHeaders = columns const makeHeaderCell = (label, dataColumn) => {
.map(col => generateHeaderCell(col, col)) const th = Utils.createElement('th', {
.join(''); className: 'sortable',
tabindex: '0',
role: 'columnheader',
'aria-sort': 'none',
dataset: { column: dataColumn }
});
const span = Utils.createElement('span', {}, [label]); // text node — safe
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '12'); svg.setAttribute('height', '12');
svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('stroke-width', '2');
svg.setAttribute('aria-hidden', 'true');
svg.classList.add('sort-icon');
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M12 5v14M5 12l7-7 7 7');
svg.appendChild(path);
th.appendChild(span);
th.appendChild(svg);
return th;
};
return ` row.appendChild(makeHeaderCell('Service Name', 'service'));
<tr> columns.forEach(col => row.appendChild(makeHeaderCell(col, col)));
${generateHeaderCell('Service Name', 'service')} return row;
${columnHeaders}
</tr>
`;
} }
#renderTableBody() { #renderTableBody() {
@ -1478,20 +1479,14 @@ class TableManager {
} }
#loadAllEntries() { #loadAllEntries() {
// Update state to show all entries
this.#state.update({ isFullyLoaded: true }); this.#state.update({ isFullyLoaded: true });
// Show loading indicator on button
const btn = this.#elements.loadAllBtn?.querySelector('.load-all-btn'); const btn = this.#elements.loadAllBtn?.querySelector('.load-all-btn');
if (btn) { if (btn) {
btn.disabled = true; btn.disabled = true;
btn.innerHTML = ` btn.innerHTML = `<div class="loading-spinner" style="width:20px;height:20px;margin-right:8px;"></div>Loading...`;
<div class="loading-spinner" style="width: 20px; height: 20px; margin-right: 8px;"></div>
Loading...
`;
} }
// Re-render table body with all entries
setTimeout(() => { setTimeout(() => {
this.#removeLoadAllButton(); this.#removeLoadAllButton();
this.#renderTableBody(); this.#renderTableBody();