Hide apps grid until icons finish loading

Prevent a visual flash by hiding the apps grid until all app icons have loaded. Adds revealWhenLoaded($container) which removes the myapps-grid-loading class when images finish (or error). TabApps now caches the .myapps-container lookup and calls revealWhenLoaded after initial render, after search results, and after loading installed apps. CSS changes: add .myapps-grid-loading (opacity:0 + transition) and a small padding-right tweak for the search input.
This commit is contained in:
jelveh
2026-03-26 20:29:35 -07:00
parent 017308a741
commit e1e2cad31d
2 changed files with 45 additions and 3 deletions
+38 -3
View File
@@ -29,7 +29,7 @@ function buildAppsGrid (apps) {
return h;
}
let h = '<div class="myapps-grid">';
let h = '<div class="myapps-grid myapps-grid-loading">';
for ( const app of apps ) {
const title = (app.title || app.name || '').trim();
const iconUrl = app.iconUrl || window.icons['app.svg'];
@@ -45,6 +45,36 @@ function buildAppsGrid (apps) {
return h;
}
function revealWhenLoaded ($container) {
const $grid = $container.find('.myapps-grid-loading');
if ( $grid.length === 0 ) return;
const imgs = $grid.find('img').toArray();
if ( imgs.length === 0 ) {
$grid.removeClass('myapps-grid-loading');
return;
}
let loaded = 0;
const total = imgs.length;
function onDone () {
loaded++;
if ( loaded >= total ) {
$grid.removeClass('myapps-grid-loading');
}
}
for ( const img of imgs ) {
if ( img.complete ) {
onDone();
} else {
img.addEventListener('load', onDone, { once: true });
img.addEventListener('error', onDone, { once: true });
}
}
}
const TabApps = {
id: 'apps',
label: 'Apps',
@@ -80,8 +110,11 @@ const TabApps = {
if ( ! self._apps ) return;
const $container = $el_window.find('.myapps-container');
if ( ! query ) {
$el_window.find('.myapps-container').html(buildAppsGrid(self._apps));
$container.html(buildAppsGrid(self._apps));
revealWhenLoaded($container);
return;
}
@@ -91,11 +124,12 @@ const TabApps = {
return title.includes(query) || name.includes(query);
});
$el_window.find('.myapps-container').html(
$container.html(
filtered.length > 0
? buildAppsGrid(filtered)
: '<div class="myapps-empty"><p>No apps match your search</p></div>',
);
revealWhenLoaded($container);
}
$el_window.on('input', '.myapps-search', function () {
@@ -174,6 +208,7 @@ const TabApps = {
this._apps = merged;
$container.html(buildAppsGrid(merged));
revealWhenLoaded($container);
} catch (e) {
console.error('Failed to load installed apps:', e);
$container.html('<div class="myapps-empty"><p>Failed to load apps</p></div>');
+7
View File
@@ -478,6 +478,7 @@ body {
color: var(--dashboard-text-primary);
outline: none;
transition: border-color 0.15s;
padding-right: 35px;
}
.myapps-search:focus {
@@ -525,6 +526,12 @@ body {
grid-template-columns: repeat(auto-fill, 80px);
gap: 24px 20px;
justify-content: start;
opacity: 1;
transition: opacity 0.2s ease;
}
.myapps-grid-loading {
opacity: 0;
}
.myapps-tile {