@olwiba/ui 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olwiba/ui",
3
- "version": "0.0.33",
3
+ "version": "0.0.35",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -87,6 +87,10 @@ export interface AppShellProps {
87
87
  user?: AppShellUser;
88
88
  /** Text shown in the top header bar */
89
89
  pageTitle?: string;
90
+ /** Slot rendered at the start of the top header bar, after the sidebar trigger. */
91
+ headerStart?: ReactNode;
92
+ /** Slot rendered at the end of the top header bar. */
93
+ headerEnd?: ReactNode;
90
94
  /** Override link rendering for SPA navigation. Defaults to a native <a>. */
91
95
  renderLink?: AppShellRenderLink;
92
96
  children?: ReactNode;
@@ -203,18 +207,26 @@ function ShellSidebar({
203
207
  side: 'left' | 'right';
204
208
  sidebarPosition: 'viewport' | 'contained';
205
209
  }) {
210
+ const fallbackLogo = typeof brand.name === 'string' ? brand.name.slice(0, 1).toUpperCase() : null;
211
+
206
212
  return (
207
213
  <Sidebar side={side} collapsible={collapsible} sidebarPosition={sidebarPosition}>
208
214
  <SidebarHeader>
209
215
  <SidebarMenu>
210
216
  <SidebarMenuItem>
211
- <SidebarMenuButton asChild className="data-[slot=sidebar-menu-button]:!p-1.5">
217
+ <SidebarMenuButton asChild tooltip={typeof brand.name === 'string' ? brand.name : undefined} className="data-[slot=sidebar-menu-button]:!p-1.5">
212
218
  {renderLink({
213
219
  href: brand.href ?? '#',
214
220
  children: (
215
221
  <>
216
- {brand.logo}
217
- <span className="text-base font-semibold">{brand.name}</span>
222
+ {brand.logo ?? (
223
+ <span className="flex size-4 shrink-0 items-center justify-center text-sm font-semibold">
224
+ {fallbackLogo}
225
+ </span>
226
+ )}
227
+ <span className="min-w-0 truncate text-base font-semibold group-data-[collapsible=icon]:hidden">
228
+ {brand.name}
229
+ </span>
218
230
  </>
219
231
  ),
220
232
  })}
@@ -287,13 +299,31 @@ function ShellSidebar({
287
299
  );
288
300
  }
289
301
 
290
- function ShellHeader({ pageTitle }: { pageTitle: string }) {
302
+ function ShellHeader({
303
+ pageTitle,
304
+ headerStart,
305
+ headerEnd,
306
+ }: {
307
+ pageTitle: string;
308
+ headerStart?: ReactNode;
309
+ headerEnd?: ReactNode;
310
+ }) {
291
311
  return (
292
312
  <header className="flex h-12 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
293
313
  <div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
294
314
  <SidebarTrigger className="-ml-1" />
315
+ {headerStart && (
316
+ <div className="flex items-center gap-1">
317
+ {headerStart}
318
+ </div>
319
+ )}
295
320
  <Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
296
321
  <h1 className="text-base font-medium">{pageTitle}</h1>
322
+ {headerEnd && (
323
+ <div className="ml-auto flex items-center gap-1">
324
+ {headerEnd}
325
+ </div>
326
+ )}
297
327
  </div>
298
328
  </header>
299
329
  );
@@ -323,6 +353,8 @@ export function AppShell({
323
353
  action,
324
354
  user = defaultUser,
325
355
  pageTitle = 'Dashboard',
356
+ headerStart,
357
+ headerEnd,
326
358
  renderLink = defaultRenderLink,
327
359
  collapsible = 'icon',
328
360
  sidebarPosition = 'viewport',
@@ -347,7 +379,7 @@ export function AppShell({
347
379
  sidebarPosition={sidebarPosition}
348
380
  />
349
381
  <SidebarInset className={isContained ? undefined : 'overflow-y-auto'}>
350
- <ShellHeader pageTitle={pageTitle} />
382
+ <ShellHeader pageTitle={pageTitle} headerStart={headerStart} headerEnd={headerEnd} />
351
383
  {children}
352
384
  </SidebarInset>
353
385
  </SidebarProvider>
@@ -22,7 +22,12 @@ export function ThemeSwitchMinimal() {
22
22
 
23
23
  return (
24
24
  <Button variant="ghost" size="icon" onClick={toggle} className="size-8" aria-label="Toggle theme">
25
- {theme === 'dark' ? <Sun className="size-4" /> : <Moon className="size-4" />}
25
+ <span
26
+ key={theme}
27
+ className="inline-flex size-4 items-center justify-center animate-in fade-in zoom-in-75 spin-in-90 duration-200 motion-reduce:animate-none"
28
+ >
29
+ {theme === 'dark' ? <Sun className="size-4" /> : <Moon className="size-4" />}
30
+ </span>
26
31
  </Button>
27
32
  );
28
33
  }