@scripso-homepad/ui 0.4.35 → 0.4.37

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": "@scripso-homepad/ui",
3
- "version": "0.4.35",
3
+ "version": "0.4.37",
4
4
  "type": "module",
5
5
  "description": "Cross-platform UI components for Homepad (React Web + React Native)",
6
6
  "license": "MIT",
@@ -124,7 +124,11 @@ export function Button({
124
124
  icon != null && typeof icon !== "boolean" ? icon : undefined;
125
125
  const hasIcon = showIcon || icon === true || customIcon != null;
126
126
  const resolvedContainerClassName = [
127
- Platform.OS === "web" ? "max-md:px-4!" : "",
127
+ Platform.OS === "web"
128
+ ? hasIcon
129
+ ? "max-md:pl-4! pr-2!"
130
+ : "max-md:px-4!"
131
+ : "",
128
132
  className,
129
133
  ]
130
134
  .filter(Boolean)
@@ -175,12 +175,15 @@ export function PhoneInput({
175
175
  const styles = StyleSheet.create({
176
176
  wrapper: {
177
177
  width: "100%",
178
+ minWidth: 0,
178
179
  gap: spacing.sm,
179
180
  },
180
181
  row: {
181
182
  flexDirection: "row",
182
183
  alignItems: "center",
183
184
  gap: INPUT_ICON_GAP,
185
+ minWidth: 0,
186
+ width: "100%",
184
187
  },
185
188
  countryOutline: {
186
189
  flexShrink: 0,
@@ -190,9 +193,11 @@ const styles = StyleSheet.create({
190
193
  },
191
194
  phoneOutline: {
192
195
  flex: 1,
196
+ minWidth: 0,
193
197
  },
194
198
  phoneField: {
195
199
  flex: 1,
200
+ minWidth: 0,
196
201
  },
197
202
  error: {
198
203
  fontSize: 12,
@@ -148,7 +148,7 @@ export function Drawer({
148
148
  type="button"
149
149
  aria-label={closeAriaLabel}
150
150
  className={cn(
151
- 'absolute inset-0 bg-storm-gray-850/40 backdrop-blur-[12px] transition-opacity duration-300 ease-in-out',
151
+ 'absolute inset-0 z-0 cursor-pointer bg-storm-gray-850/40 backdrop-blur-[12px] transition-opacity duration-300 ease-in-out',
152
152
  isAnimatedIn ? 'opacity-100' : 'opacity-0',
153
153
  )}
154
154
  onClick={onClose}
@@ -160,7 +160,7 @@ export function Drawer({
160
160
  aria-labelledby="homepad-drawer-title"
161
161
  {...(subtitle ? { 'aria-describedby': 'homepad-drawer-subtitle' } : {})}
162
162
  className={cn(
163
- 'relative z-10 flex h-[calc(100dvh-16px)] min-h-0 flex-col overflow-hidden rounded-[16px] bg-white shadow-2xl transition-transform duration-300 ease-in-out max-md:h-dvh max-md:rounded-none',
163
+ 'relative z-10 flex h-[calc(100dvh-16px)] min-h-0 cursor-auto flex-col overflow-hidden rounded-[16px] bg-white shadow-2xl transition-transform duration-300 ease-in-out max-md:h-dvh max-md:rounded-none',
164
164
  // Keep GPU transform only while animating — settled translate-x-0 blurs text.
165
165
  !isTransformSettled && 'will-change-transform',
166
166
  isAnimatedIn
@@ -42,6 +42,7 @@ export type PaginationProps = {
42
42
  */
43
43
  variant?: PaginationVariant;
44
44
  className?: string;
45
+ /** Hide the entire bar when there is only one page. Page buttons are always hidden in that case. */
45
46
  hideOnSinglePage?: boolean;
46
47
  };
47
48
 
@@ -64,15 +65,16 @@ export function Pagination({
64
65
  const visiblePages = getVisiblePageNumbers(safePage, totalPages, maxPageButtons);
65
66
  const canGoPrevious = safePage > 1;
66
67
  const canGoNext = safePage < totalPages;
68
+ const showPageControls = totalPages > 1;
67
69
 
68
- if (hideOnSinglePage && totalPages <= 1) {
70
+ if (hideOnSinglePage && !showPageControls) {
69
71
  return null;
70
72
  }
71
73
 
72
74
  return (
73
75
  <div
74
76
  className={cn(
75
- 'flex flex-col gap-4 px-4 pb-4 pt-4 font-[family-name:var(--font-sans)] sm:flex-row sm:items-center sm:justify-between',
77
+ 'flex flex-col items-center gap-4 px-4 pb-4 pt-4 text-center font-[family-name:var(--font-sans)] sm:flex-row sm:items-center sm:justify-between sm:text-left',
76
78
  variant === 'standalone' && cn('rounded-xl border bg-storm-gray-0', 'border-storm-gray-50'),
77
79
  dataTableClassNames.pagination,
78
80
  className,
@@ -82,66 +84,68 @@ export function Pagination({
82
84
  {mergedLabels.showing({ start, end, total })}
83
85
  </p>
84
86
 
85
- <nav
86
- aria-label="Pagination"
87
- className="flex items-center gap-2.5"
88
- >
89
- <button
90
- type="button"
91
- onClick={() => onPageChange(safePage - 1)}
92
- disabled={!canGoPrevious}
93
- aria-label={mergedLabels.previousPage}
94
- className={cn(
95
- paginationControlClassName,
96
- canGoPrevious ? paginationHoverClassName : 'cursor-not-allowed opacity-40',
97
- )}
87
+ {showPageControls ? (
88
+ <nav
89
+ aria-label="Pagination"
90
+ className="flex items-center gap-2.5"
98
91
  >
99
- <ChevronLeft
100
- className="size-3.5 text-storm-gray-500"
101
- strokeWidth={1.75}
102
- aria-hidden
103
- />
104
- </button>
92
+ <button
93
+ type="button"
94
+ onClick={() => onPageChange(safePage - 1)}
95
+ disabled={!canGoPrevious}
96
+ aria-label={mergedLabels.previousPage}
97
+ className={cn(
98
+ paginationControlClassName,
99
+ canGoPrevious ? paginationHoverClassName : 'cursor-not-allowed opacity-40',
100
+ )}
101
+ >
102
+ <ChevronLeft
103
+ className="size-3.5 text-storm-gray-500"
104
+ strokeWidth={1.75}
105
+ aria-hidden
106
+ />
107
+ </button>
105
108
 
106
- {visiblePages.map((pageNumber) => {
107
- const isActive = pageNumber === safePage;
109
+ {visiblePages.map((pageNumber) => {
110
+ const isActive = pageNumber === safePage;
108
111
 
109
- return (
110
- <button
111
- key={pageNumber}
112
- type="button"
113
- onClick={() => onPageChange(pageNumber)}
114
- aria-label={mergedLabels.page(pageNumber)}
115
- aria-current={isActive ? 'page' : undefined}
116
- className={cn(
117
- paginationControlClassName,
118
- isActive
119
- ? 'border-storm-gray-50 font-bold text-storm-gray-900'
120
- : paginationHoverClassName,
121
- )}
122
- >
123
- {pageNumber}
124
- </button>
125
- );
126
- })}
112
+ return (
113
+ <button
114
+ key={pageNumber}
115
+ type="button"
116
+ onClick={() => onPageChange(pageNumber)}
117
+ aria-label={mergedLabels.page(pageNumber)}
118
+ aria-current={isActive ? 'page' : undefined}
119
+ className={cn(
120
+ paginationControlClassName,
121
+ isActive
122
+ ? 'border-storm-gray-50 font-bold text-storm-gray-900'
123
+ : paginationHoverClassName,
124
+ )}
125
+ >
126
+ {pageNumber}
127
+ </button>
128
+ );
129
+ })}
127
130
 
128
- <button
129
- type="button"
130
- onClick={() => onPageChange(safePage + 1)}
131
- disabled={!canGoNext}
132
- aria-label={mergedLabels.nextPage}
133
- className={cn(
134
- paginationControlClassName,
135
- canGoNext ? paginationHoverClassName : 'cursor-not-allowed opacity-40',
136
- )}
137
- >
138
- <ChevronRight
139
- className="size-3.5 text-storm-gray-500"
140
- strokeWidth={1.75}
141
- aria-hidden
142
- />
143
- </button>
144
- </nav>
131
+ <button
132
+ type="button"
133
+ onClick={() => onPageChange(safePage + 1)}
134
+ disabled={!canGoNext}
135
+ aria-label={mergedLabels.nextPage}
136
+ className={cn(
137
+ paginationControlClassName,
138
+ canGoNext ? paginationHoverClassName : 'cursor-not-allowed opacity-40',
139
+ )}
140
+ >
141
+ <ChevronRight
142
+ className="size-3.5 text-storm-gray-500"
143
+ strokeWidth={1.75}
144
+ aria-hidden
145
+ />
146
+ </button>
147
+ </nav>
148
+ ) : null}
145
149
  </div>
146
150
  );
147
151
  }
@@ -86,7 +86,7 @@ export function DataTable({
86
86
 
87
87
  return (
88
88
  <DataTableClassNamesContext.Provider value={mergedClassNames}>
89
- <div className={cn('flex flex-col', fillHeight && 'min-h-0 flex-1', mergedClassNames.root)}>
89
+ <div className={cn('flex min-w-0 flex-col', fillHeight && 'min-h-0 flex-1', mergedClassNames.root)}>
90
90
  <div
91
91
  className={cn(
92
92
  tableShellClassName,
@@ -109,6 +109,7 @@ export function DataTable({
109
109
  {hasFooter ? (
110
110
  <div
111
111
  className={cn(
112
+ 'shrink-0',
112
113
  tableFooterShellClassName,
113
114
  tableBorderClassName,
114
115
  mergedClassNames.footer,
@@ -23,7 +23,7 @@ export function TableScrollArea({
23
23
  return (
24
24
  <div
25
25
  className={cn(
26
- 'table-scroll-area w-full overflow-auto',
26
+ 'table-scroll-area w-full min-w-0 max-w-full overflow-auto',
27
27
  fillHeight && 'min-h-0 flex-1',
28
28
  className,
29
29
  )}
@@ -0,0 +1,32 @@
1
+ type SidebarCollapseIconProps = {
2
+ className?: string;
3
+ size?: number | string;
4
+ strokeWidth?: number | string;
5
+ };
6
+
7
+ /** Shown when the sidebar is open — click collapses it. */
8
+ export function SidebarCollapseIcon({
9
+ className,
10
+ size = 20,
11
+ strokeWidth = 2,
12
+ }: SidebarCollapseIconProps) {
13
+ return (
14
+ <svg
15
+ width={size}
16
+ height={size}
17
+ viewBox="0 0 20 20"
18
+ fill="none"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ aria-hidden
21
+ className={className}
22
+ >
23
+ <path
24
+ d="M12.5 2.5V17.5M8.33333 12.5L5.83333 10L8.33333 7.5M4.16667 2.5H15.8333C16.7538 2.5 17.5 3.24619 17.5 4.16667V15.8333C17.5 16.7538 16.7538 17.5 15.8333 17.5H4.16667C3.24619 17.5 2.5 16.7538 2.5 15.8333V4.16667C2.5 3.24619 3.24619 2.5 4.16667 2.5Z"
25
+ stroke="currentColor"
26
+ strokeWidth={strokeWidth}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ />
30
+ </svg>
31
+ );
32
+ }
@@ -0,0 +1,32 @@
1
+ type SidebarExpandIconProps = {
2
+ className?: string;
3
+ size?: number | string;
4
+ strokeWidth?: number | string;
5
+ };
6
+
7
+ /** Shown when the sidebar is closed — click expands it. */
8
+ export function SidebarExpandIcon({
9
+ className,
10
+ size = 20,
11
+ strokeWidth = 2,
12
+ }: SidebarExpandIconProps) {
13
+ return (
14
+ <svg
15
+ width={size}
16
+ height={size}
17
+ viewBox="0 0 20 20"
18
+ fill="none"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ aria-hidden
21
+ className={className}
22
+ >
23
+ <path
24
+ d="M12.5 2.5V17.5M6.66667 7.5L9.16667 10L6.66667 12.5M4.16667 2.5H15.8333C16.7538 2.5 17.5 3.24619 17.5 4.16667V15.8333C17.5 16.7538 16.7538 17.5 15.8333 17.5H4.16667C3.24619 17.5 2.5 16.7538 2.5 15.8333V4.16667C2.5 3.24619 3.24619 2.5 4.16667 2.5Z"
25
+ stroke="currentColor"
26
+ strokeWidth={strokeWidth}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ />
30
+ </svg>
31
+ );
32
+ }
package/src/web/index.ts CHANGED
@@ -23,6 +23,8 @@ export type { SidebarUserCardProps } from './layout/SidebarUserCard';
23
23
 
24
24
  export { BellIcon } from './icons/BellIcon';
25
25
  export { BuildingIcon } from './icons/BuildingIcon';
26
+ export { SidebarCollapseIcon } from './icons/SidebarCollapseIcon';
27
+ export { SidebarExpandIcon } from './icons/SidebarExpandIcon';
26
28
  export { TableMoreIcon } from './icons/TableMoreIcon';
27
29
  export { TableSortIcon } from './icons/TableSortIcon';
28
30
  export { TableMenuCheckIcon } from './icons/TableMenuCheckIcon';
@@ -43,6 +43,7 @@ function DashboardLayoutDemo() {
43
43
  user: storyUser,
44
44
  mobileOpen: mobileMenuOpen,
45
45
  onMobileClose: () => setMobileMenuOpen(false),
46
+ onMobileOpen: () => setMobileMenuOpen(true),
46
47
  onLogout: fn(),
47
48
  onNavItemClick: () => setMobileMenuOpen(false),
48
49
  }}
@@ -63,7 +64,7 @@ function DashboardLayoutDemo() {
63
64
  buildingOptions: storyBuildingOptions,
64
65
  selectedBuildingId,
65
66
  onBuildingChange: setSelectedBuildingId,
66
- className: 'hidden md:flex',
67
+ className: 'hidden lg:flex',
67
68
  searchValue: searchQuery,
68
69
  onSearchChange: setSearchQuery,
69
70
  onNotificationsClick: fn(),
@@ -27,14 +27,14 @@ export function DashboardLayout({
27
27
  mainClassName,
28
28
  }: DashboardLayoutProps) {
29
29
  return (
30
- <div className={cn('flex h-screen overflow-hidden flex-row bg-storm-gray-0', className)}>
31
- <div className="h-screen shrink-0">
30
+ <div className={cn('flex h-screen min-w-0 overflow-hidden flex-row bg-storm-gray-0', className)}>
31
+ <div className="max-md:contents md:h-screen md:shrink-0">
32
32
  <Sidebar {...sidebar} />
33
33
  </div>
34
34
  <div className="flex h-screen min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
35
35
  <SidebarMobileHeader {...mobileHeader} />
36
36
  <AppHeader {...header} />
37
- <main className={mainClassName ?? 'min-h-0 flex-1 overflow-y-auto p-4 md:p-6'}>{children}</main>
37
+ <main className={mainClassName ?? 'min-h-0 min-w-0 flex-1 overflow-x-hidden overflow-y-auto p-4 xl:p-6'}>{children}</main>
38
38
  </div>
39
39
  </div>
40
40
  );
@@ -67,6 +67,7 @@ function MobileDrawerStory(args: SidebarProps) {
67
67
  {...args}
68
68
  mobileOpen={mobileOpen}
69
69
  onMobileClose={() => setMobileOpen(false)}
70
+ onMobileOpen={() => setMobileOpen(true)}
70
71
  />
71
72
  </div>
72
73
  </WebLayoutCanvas>
@@ -1,6 +1,7 @@
1
- import { PanelLeftClose, PanelLeftOpen, X } from 'lucide-react';
2
1
  import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
3
2
 
3
+ import { SidebarCollapseIcon } from '../icons/SidebarCollapseIcon';
4
+ import { SidebarExpandIcon } from '../icons/SidebarExpandIcon';
4
5
  import { useMediaQuery } from '../hooks/useMediaQuery';
5
6
  import { cn } from '../utils/cn';
6
7
  import { isNavItemActive } from './nav-active';
@@ -24,6 +25,7 @@ export type SidebarProps = {
24
25
  className?: string;
25
26
  mobileOpen: boolean;
26
27
  onMobileClose?: () => void;
28
+ onMobileOpen?: () => void;
27
29
  /**
28
30
  * Host-owned navigation. Call `navigate(item.to)` from the app router here.
29
31
  * The sidebar never imports react-router, so this must perform the route change.
@@ -66,6 +68,7 @@ export function Sidebar({
66
68
  className,
67
69
  mobileOpen,
68
70
  onMobileClose,
71
+ onMobileOpen,
69
72
  onNavItemClick,
70
73
  collapseIcon,
71
74
  expandIcon,
@@ -74,13 +77,15 @@ export function Sidebar({
74
77
  collapsed,
75
78
  onCollapsedChange,
76
79
  }: SidebarProps) {
77
- const isDesktop = useMediaQuery('(min-width: 768px)');
80
+ const isDesktop = useMediaQuery('(min-width: 1024px)');
81
+ const isRailLayout = useMediaQuery('(min-width: 768px)');
78
82
  const [internalCollapsed, setInternalCollapsed] = useState(() =>
79
83
  readInitialCollapsedState(sidebarStorageKey, initialCollapsed),
80
84
  );
81
85
 
82
86
  const isCollapsed = collapsed ?? internalCollapsed;
83
- const isExpanded = isDesktop ? !isCollapsed : true;
87
+ const isExpanded = isDesktop ? !isCollapsed : mobileOpen;
88
+ const showCollapsedRail = isRailLayout && !isDesktop;
84
89
 
85
90
  const setCollapsed = useCallback(
86
91
  (next: boolean) => {
@@ -105,8 +110,14 @@ export function Sidebar({
105
110
  return;
106
111
  }
107
112
 
108
- onMobileClose?.();
109
- }, [isDesktop, onMobileClose, toggleCollapsed]);
113
+ if (mobileOpen) {
114
+ onMobileClose?.();
115
+
116
+ return;
117
+ }
118
+
119
+ onMobileOpen?.();
120
+ }, [isDesktop, mobileOpen, onMobileClose, onMobileOpen, toggleCollapsed]);
110
121
 
111
122
  const handleNavItemNavigate = useCallback(
112
123
  (item: AdminNavItem) => {
@@ -156,21 +167,28 @@ export function Sidebar({
156
167
  aria-label={labels.closeMenu}
157
168
  onClick={onMobileClose}
158
169
  className={cn(
159
- 'fixed inset-0 z-40 bg-navy-800/60 backdrop-blur-[1px] transition-opacity duration-300 ease-in-out md:hidden',
170
+ 'fixed inset-0 z-40 bg-navy-800/60 backdrop-blur-[1px] transition-opacity duration-300 ease-in-out lg:hidden',
160
171
  mobileOpen ? 'opacity-100' : 'pointer-events-none opacity-0',
161
172
  )}
162
173
  />
163
174
 
175
+ {showCollapsedRail && mobileOpen ? (
176
+ <div className="h-screen w-[76px] shrink-0" aria-hidden />
177
+ ) : null}
178
+
164
179
  <aside
165
180
  className={cn(
166
181
  'fixed inset-y-0 left-0 z-50 flex h-screen w-[min(300px,85vw)] flex-col gap-8 overflow-hidden bg-navy p-4 shadow-xl',
167
182
  'transition-[transform,width,padding,gap] duration-300 ease-in-out will-change-[transform,width]',
168
- mobileOpen ? 'translate-x-0' : '-translate-x-full',
169
- 'md:relative md:z-auto md:w-[300px] md:translate-x-0 md:shadow-none',
170
- isExpanded ? 'md:w-[300px]' : 'md:w-[76px] md:items-center md:gap-2 md:px-3 md:py-4',
183
+ mobileOpen ? 'translate-x-0' : '-translate-x-full pointer-events-none',
184
+ 'md:relative md:z-auto md:w-[76px] md:translate-x-0 md:pointer-events-auto md:shadow-none md:items-center md:gap-2 md:px-3 md:py-4',
185
+ mobileOpen &&
186
+ 'md:fixed md:z-50 md:w-[min(300px,85vw)] md:items-stretch md:gap-8 md:p-4 md:shadow-xl',
187
+ 'lg:relative lg:z-auto lg:translate-x-0 lg:pointer-events-auto lg:shadow-none',
188
+ isExpanded ? 'lg:w-[300px] lg:items-stretch lg:gap-8 lg:p-4' : 'lg:w-[76px] lg:items-center lg:gap-2 lg:px-3 lg:py-4',
171
189
  className,
172
190
  )}
173
- aria-hidden={!isDesktop && !mobileOpen}
191
+ aria-hidden={!isDesktop && !mobileOpen && !showCollapsedRail}
174
192
  >
175
193
  <div
176
194
  className={cn(
@@ -199,22 +217,16 @@ export function Sidebar({
199
217
  onClick={handleHeaderAction}
200
218
  className="shrink-0 cursor-pointer text-white transition-[opacity,transform] duration-200 hover:opacity-80 active:scale-95"
201
219
  aria-label={
202
- isDesktop
203
- ? isExpanded
220
+ isExpanded
221
+ ? isDesktop
204
222
  ? labels.collapse
205
- : labels.expand
206
- : labels.closeMenu
223
+ : labels.closeMenu
224
+ : labels.expand
207
225
  }
208
226
  >
209
- {isDesktop ? (
210
- isExpanded ? (
211
- collapseIcon ?? <PanelLeftClose size={20} strokeWidth={1.75} />
212
- ) : (
213
- expandIcon ?? <PanelLeftOpen size={20} strokeWidth={1.75} />
214
- )
215
- ) : (
216
- closeIcon ?? <X size={20} strokeWidth={1.75} />
217
- )}
227
+ {isExpanded
228
+ ? (collapseIcon ?? closeIcon ?? <SidebarCollapseIcon />)
229
+ : (expandIcon ?? <SidebarExpandIcon />)}
218
230
  </button>
219
231
  </div>
220
232
 
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { fn } from '@storybook/test';
3
3
 
4
+ import { BellIcon } from '../icons/BellIcon';
4
5
  import { WebLayoutCanvas } from './story-helpers';
5
6
  import { SidebarMobileHeader } from './SidebarMobileHeader';
6
7
  import { storyBranding, storyLabels, storyLogoIconDarkSrc } from './story-fixtures';
@@ -45,3 +46,22 @@ export const MenuOpen: Story = {
45
46
  </WebLayoutCanvas>
46
47
  ),
47
48
  };
49
+
50
+ export const WithEndAction: Story = {
51
+ args: {
52
+ end: (
53
+ <button
54
+ type="button"
55
+ className="relative flex size-11 shrink-0 items-center justify-center rounded-xl border border-storm-gray-50 text-navy"
56
+ aria-label="Notifications"
57
+ >
58
+ <BellIcon className="text-navy" />
59
+ </button>
60
+ ),
61
+ },
62
+ render: (args) => (
63
+ <WebLayoutCanvas>
64
+ <SidebarMobileHeader {...args} />
65
+ </WebLayoutCanvas>
66
+ ),
67
+ };
@@ -1,5 +1,6 @@
1
- import { Menu } from 'lucide-react';
1
+ import type { ReactNode } from 'react';
2
2
 
3
+ import { SidebarExpandIcon } from '../icons/SidebarExpandIcon';
3
4
  import { cn } from '../utils/cn';
4
5
  import type { AdminSidebarBranding } from './types';
5
6
 
@@ -9,6 +10,8 @@ export type SidebarMobileHeaderProps = {
9
10
  onOpenMenu: () => void;
10
11
  menuOpen?: boolean;
11
12
  className?: string;
13
+ /** Rendered on the right side of the mobile header (e.g. notifications). */
14
+ end?: ReactNode;
12
15
  };
13
16
 
14
17
  export function SidebarMobileHeader({
@@ -17,6 +20,7 @@ export function SidebarMobileHeader({
17
20
  onOpenMenu,
18
21
  menuOpen = false,
19
22
  className,
23
+ end,
20
24
  }: SidebarMobileHeaderProps) {
21
25
  return (
22
26
  <header
@@ -32,9 +36,9 @@ export function SidebarMobileHeader({
32
36
  aria-label={openMenuLabel}
33
37
  aria-expanded={menuOpen}
34
38
  >
35
- <Menu size={22} strokeWidth={1.75} />
39
+ <SidebarExpandIcon size={22} />
36
40
  </button>
37
- <div className="flex min-w-0 items-center gap-2">
41
+ <div className="flex min-w-0 flex-1 items-center gap-2">
38
42
  <img src={branding.logoIconSrc} alt="" className="h-[30px] w-[42px] shrink-0" />
39
43
  <div className="min-w-0 leading-none">
40
44
  <p className="truncate text-base font-bold tracking-tight text-navy">{branding.logoTitle}</p>
@@ -43,6 +47,7 @@ export function SidebarMobileHeader({
43
47
  </p>
44
48
  </div>
45
49
  </div>
50
+ {end ? <div className="shrink-0">{end}</div> : null}
46
51
  </header>
47
52
  );
48
53
  }