feat: draw the rail's add app button in the page skeleton

This commit is contained in:
Linus Rath
2026-11-01 18:48:01 +01:00
parent 8d790b29fd
commit 441371554b
5 changed files with 32 additions and 2 deletions
+1
View File
@@ -44,6 +44,7 @@
@custom-variant boot-no-calendar (&:where([data-boot-rail]:not([data-boot-rail~="calendar"]) *));
@custom-variant boot-no-contacts (&:where([data-boot-rail]:not([data-boot-rail~="contacts"]) *));
@custom-variant boot-no-files (&:where([data-boot-rail]:not([data-boot-rail~="files"]) *));
@custom-variant boot-rail-add-app (&:where([data-boot-rail~="add-app"] *));
@custom-variant boot-rail-admin (&:where([data-boot-rail~="admin"] *));
@custom-variant boot-rail-a11y (&:where([data-boot-rail~="a11y"] *));
@custom-variant boot-rail-quota (&:where([data-boot-rail~="quota"] *));
+4
View File
@@ -16,6 +16,7 @@ import {
Keyboard,
Mail,
Menu,
Plus,
Settings,
Shield,
Users,
@@ -66,6 +67,9 @@ export function RailSkeleton({ active, className }: { active?: RailApp; classNam
<Icon className={cn("w-[18px] h-[18px] flex-shrink-0", active === id && "text-primary")} />
</span>
))}
<span className={cn(item(false), "hidden boot-rail-add-app:flex")}>
<Plus className="w-[18px] h-[18px] flex-shrink-0" />
</span>
</div>
<div className="mt-auto flex flex-col items-center gap-2 pb-3 px-1 text-muted-foreground">
<span className={cn(footerItem, "hidden boot-rail-admin:flex")}>
@@ -0,0 +1,24 @@
"use client";
import { useTranslations } from "next-intl";
import type { RailLabels } from "./shell";
/**
* The few strings a page skeleton shows: the status message and the phone
* bottom bar's labels. For the gates of client pages, which have the message
* catalog; the skeleton components themselves stay free of next-intl.
*/
export function useSkeletonLabels(): { label: string; labels: RailLabels } {
const t = useTranslations();
return {
label: t("common.loading"),
labels: {
mail: t("sidebar.mail"),
calendar: t("sidebar.calendar"),
contacts: t("sidebar.contacts"),
files: t("sidebar.files"),
settings: t("sidebar.settings"),
admin: t("sidebar.admin"),
},
};
}
+2 -1
View File
@@ -357,13 +357,14 @@ export function NavigationRail({
if (!client) return;
const items: RailHint[] = [];
for (const item of navItems) if (item.id !== 'mail' && !item.hidden) items.push(item.id as RailHint);
if (sidebarAppsEnabled && onManageApps) items.push('add-app');
if (isStalwartAdmin) items.push('admin');
if (showAccessibilityButton) items.push('a11y');
if (quotaShown) items.push('quota');
writeRailHints(items);
// navItems is rebuilt every render; its inputs are listed instead.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [client, supportsCalendar, calendarEnabled, supportsContacts, contactsEnabled, supportsFiles, filesEnabled, isStalwartAdmin, showAccessibilityButton, quotaShown]);
}, [client, supportsCalendar, calendarEnabled, supportsContacts, contactsEnabled, supportsFiles, filesEnabled, sidebarAppsEnabled, !!onManageApps, isStalwartAdmin, showAccessibilityButton, quotaShown]);
const getIsActive = (href: string, itemId: string) => {
if (activeAppId) return false;
+1 -1
View File
@@ -6,7 +6,7 @@ import { BOOT_HINTS_KEY } from './keys';
* admin rights, quota), which nothing else keeps across a reload. The boot
* script turns it into `data-boot-rail` (lib/boot-skeleton/boot-script.ts).
*/
export type RailHint = 'calendar' | 'contacts' | 'files' | 'admin' | 'a11y' | 'quota';
export type RailHint = 'calendar' | 'contacts' | 'files' | 'add-app' | 'admin' | 'a11y' | 'quota';
let lastWritten: string | null = null;