fixed some bugs
This commit is contained in:
parent
943915a242
commit
69bd604279
1 changed files with 343 additions and 348 deletions
81
dist/js/app.js
vendored
81
dist/js/app.js
vendored
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue