@lateralus-ai/shipping-ui 2.0.0-dev.20 → 2.0.0-dev.3

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.
Files changed (41) hide show
  1. package/dist/components/Entry.d.ts +7 -27
  2. package/dist/components/Tabs.d.ts +5 -5
  3. package/dist/components/index.d.ts +2 -4
  4. package/dist/index.cjs +25 -25
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.esm.js +4628 -4857
  7. package/dist/patterns/Search/ResultRow.d.ts +5 -11
  8. package/dist/patterns/Sidebar/sidebar-styles.d.ts +1 -2
  9. package/dist/primitives/Badge.d.ts +4 -7
  10. package/dist/primitives/index.d.ts +1 -1
  11. package/dist/tailwind-theme.d.ts +4 -8
  12. package/dist/{theme-entry-tLBc6zGT.mjs → theme-entry-CxDa1D0_.mjs} +8 -12
  13. package/dist/theme-entry-D2X3Ptjf.js +1 -0
  14. package/dist/theme.cjs +1 -1
  15. package/dist/theme.esm.js +1 -1
  16. package/package.json +2 -2
  17. package/src/components/Entry.tsx +45 -119
  18. package/src/components/Tabs.tsx +139 -146
  19. package/src/components/index.ts +42 -56
  20. package/src/index.ts +74 -85
  21. package/src/patterns/Search/ResultRow.tsx +58 -44
  22. package/src/patterns/Search/SearchModal.tsx +292 -310
  23. package/src/patterns/Search/index.ts +31 -31
  24. package/src/patterns/Sidebar/CollapsibleNavGroup.tsx +1 -1
  25. package/src/patterns/Sidebar/SidebarAction.tsx +17 -26
  26. package/src/patterns/Sidebar/SidebarEntry.tsx +39 -54
  27. package/src/patterns/Sidebar/SidebarLink.tsx +20 -30
  28. package/src/patterns/Sidebar/sidebar-styles.ts +1 -2
  29. package/src/patterns/Skeleton/Skeleton.tsx +56 -56
  30. package/src/primitives/Badge.tsx +10 -20
  31. package/src/primitives/Button.tsx +11 -16
  32. package/src/primitives/index.ts +1 -1
  33. package/src/stories/canvases/ContentCanvas.tsx +114 -353
  34. package/src/stories/canvases/SearchCanvas.tsx +150 -150
  35. package/src/tailwind-theme.ts +182 -186
  36. package/src/utils/cn.ts +1 -28
  37. package/dist/components/PageHeader.d.ts +0 -41
  38. package/dist/components/ScrollableList.d.ts +0 -22
  39. package/dist/theme-entry-Dwr2mV7_.js +0 -1
  40. package/src/components/PageHeader.tsx +0 -132
  41. package/src/components/ScrollableList.tsx +0 -61
@@ -1,310 +1,292 @@
1
- import {
2
- type ChangeEvent,
3
- type InputHTMLAttributes,
4
- type ReactNode,
5
- } from "react";
6
- import {
7
- Modal,
8
- ModalBody,
9
- ModalContent,
10
- ModalDescription,
11
- ModalTitle,
12
- type ModalContentProps,
13
- type ModalProps,
14
- } from "../../components/Modal";
15
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../components/Tabs";
16
- import { Badge } from "../../primitives/Badge";
17
- import { cn } from "../../utils/cn";
18
- import { ResultRow, type ResultRowProps } from "./ResultRow";
19
- import { SectionHeader } from "./SectionHeader";
20
- import { Skeleton } from "../Skeleton";
21
-
22
- export type SearchModalState = "idle" | "loading" | "results";
23
-
24
- export type SearchFilterTab = {
25
- value: string;
26
- label: string;
27
- count?: number;
28
- };
29
-
30
- export type SearchModalProps = ModalProps & {
31
- /** Panel state when using the built-in layout helpers. */
32
- state?: SearchModalState;
33
- className?: string;
34
- contentClassName?: string;
35
- showOverlay?: boolean;
36
- overlayClassName?: string;
37
- children?: ReactNode;
38
- };
39
-
40
- const DEFAULT_FILTERS: SearchFilterTab[] = [
41
- { value: "all", label: "All", count: 0 },
42
- { value: "reports", label: "Reports", count: 0 },
43
- { value: "chats", label: "Chats", count: 0 },
44
- { value: "issues", label: "Issues", count: 0 },
45
- ];
46
-
47
- /**
48
- * Search dialog composed on Modal.
49
- * Use compound parts (Input / Body / Filters / Results) for app wiring,
50
- * or pass `state` + demo children for Storybook.
51
- */
52
- export const SearchModal = ({
53
- state,
54
- className,
55
- contentClassName,
56
- showOverlay = true,
57
- overlayClassName,
58
- children,
59
- ...modalProps
60
- }: SearchModalProps) => (
61
- <Modal {...modalProps}>
62
- <ModalContent
63
- showOverlay={showOverlay}
64
- overlayClassName={overlayClassName}
65
- className={cn(
66
- "flex max-h-[min(80vh,640px)] w-[min(100vw-2rem,700px)] max-w-[768px] flex-col p-0",
67
- contentClassName,
68
- )}
69
- aria-label="Search"
70
- data-state={state}
71
- >
72
- <ModalTitle className="sr-only">Search</ModalTitle>
73
- <ModalDescription className="sr-only">
74
- Search reports, chats, or issues
75
- </ModalDescription>
76
- <div className={cn("flex min-h-0 flex-1 flex-col", className)}>
77
- {children}
78
- </div>
79
- </ModalContent>
80
- </Modal>
81
- );
82
-
83
- export type SearchModalInputProps = Omit<
84
- InputHTMLAttributes<HTMLInputElement>,
85
- "type"
86
- > & {
87
- className?: string;
88
- wrapperClassName?: string;
89
- };
90
-
91
- export const SearchModalInput = ({
92
- className,
93
- wrapperClassName,
94
- placeholder = "Search reports, chats, or issues…",
95
- ...props
96
- }: SearchModalInputProps) => (
97
- <div
98
- className={cn(
99
- "shrink-0 p-3",
100
- wrapperClassName,
101
- )}
102
- >
103
- <div className="flex min-h-10 items-center px-3">
104
- <input
105
- type="search"
106
- placeholder={placeholder}
107
- className={cn(
108
- "w-full bg-transparent text-caption-1 text-display-on-light-primary",
109
- "placeholder:text-display-on-light-tertiary focus:outline-none",
110
- className,
111
- )}
112
- aria-label="Search query"
113
- {...props}
114
- />
115
- </div>
116
- </div>
117
- );
118
-
119
- export const SearchModalDivider = ({ className }: { className?: string }) => (
120
- <div
121
- className={cn("h-px w-full shrink-0 bg-divider-primary", className)}
122
- aria-hidden
123
- />
124
- );
125
-
126
- export type SearchModalBodyProps = {
127
- children: ReactNode;
128
- className?: string;
129
- };
130
-
131
- export const SearchModalBody = ({
132
- children,
133
- className,
134
- }: SearchModalBodyProps) => (
135
- <ModalBody
136
- className={cn(
137
- "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden p-2",
138
- className,
139
- )}
140
- >
141
- {children}
142
- </ModalBody>
143
- );
144
-
145
- export type SearchModalFiltersProps = {
146
- tabs?: SearchFilterTab[];
147
- value: string;
148
- onValueChange: (value: string) => void;
149
- children?: ReactNode;
150
- className?: string;
151
- listClassName?: string;
152
- };
153
-
154
- /**
155
- * Pill-style filter tabs for the results state.
156
- * Children render below the tab list (typically a single filtered results list).
157
- * For per-tab panels, nest `SearchModalFilterPanel` (TabsContent) as children.
158
- */
159
- export const SearchModalFilters = ({
160
- tabs = DEFAULT_FILTERS,
161
- value,
162
- onValueChange,
163
- children,
164
- className,
165
- listClassName,
166
- }: SearchModalFiltersProps) => (
167
- <Tabs
168
- type="pills"
169
- value={value}
170
- onValueChange={onValueChange}
171
- className={cn("min-h-0 flex-1 gap-0", className)}
172
- >
173
- <TabsList
174
- className={cn(
175
- "w-full shrink-0 bg-background-primary p-2",
176
- listClassName,
177
- )}
178
- >
179
- {tabs.map((tab) => (
180
- <TabsTrigger key={tab.value} value={tab.value}>
181
- {tab.label}
182
- {typeof tab.count === "number" && (
183
- <Badge color="blue">
184
- {tab.count > 99 ? "99+" : tab.count}
185
- </Badge>
186
- )}
187
- </TabsTrigger>
188
- ))}
189
- </TabsList>
190
- <div className="min-h-0 flex-1 overflow-y-auto">{children}</div>
191
- </Tabs>
192
- );
193
-
194
- export type SearchModalFilterPanelProps = {
195
- value: string;
196
- children: ReactNode;
197
- className?: string;
198
- };
199
-
200
- export const SearchModalFilterPanel = ({
201
- value,
202
- children,
203
- className,
204
- }: SearchModalFilterPanelProps) => (
205
- <TabsContent value={value} className={className}>
206
- {children}
207
- </TabsContent>
208
- );
209
-
210
- export type SearchModalResultsProps = {
211
- items?: ResultRowProps[];
212
- children?: ReactNode;
213
- className?: string;
214
- };
215
-
216
- export const SearchModalResults = ({
217
- items,
218
- children,
219
- className,
220
- }: SearchModalResultsProps) => (
221
- <div className={cn("flex flex-col", className)}>
222
- {items?.map((item, index) => (
223
- <ResultRow key={index} {...item} />
224
- ))}
225
- {children}
226
- </div>
227
- );
228
-
229
- export type SearchModalIdleProps = {
230
- label?: string;
231
- items?: ResultRowProps[];
232
- children?: ReactNode;
233
- className?: string;
234
- };
235
-
236
- export const SearchModalIdle = ({
237
- label = "Recently viewed",
238
- items,
239
- children,
240
- className,
241
- }: SearchModalIdleProps) => (
242
- <div className={cn("flex min-h-0 flex-1 flex-col p-1", className)}>
243
- <SectionHeader
244
- label={label}
245
- className="shrink-0 px-3 py-1 text-caption-2 font-normal normal-case tracking-[0.01em] text-display-on-light-secondary"
246
- />
247
- <div className="min-h-0 flex-1 overflow-y-auto">
248
- <SearchModalResults items={items}>{children}</SearchModalResults>
249
- </div>
250
- </div>
251
- );
252
-
253
- export type SearchModalLoadingProps = {
254
- className?: string;
255
- };
256
-
257
- export const SearchModalLoading = ({ className }: SearchModalLoadingProps) => (
258
- <Skeleton variant="search" className={cn("rounded-none", className)} />
259
- );
260
-
261
- /** Convenience controlled panel for Storybook / simple embeds. */
262
- export type SearchModalPanelProps = {
263
- state?: SearchModalState;
264
- query?: string;
265
- onQueryChange?: (query: string) => void;
266
- filter?: string;
267
- onFilterChange?: (filter: string) => void;
268
- filters?: SearchFilterTab[];
269
- recentItems?: ResultRowProps[];
270
- resultItems?: ResultRowProps[];
271
- className?: string;
272
- };
273
-
274
- export const SearchModalPanel = ({
275
- state = "idle",
276
- query = "",
277
- onQueryChange,
278
- filter = "all",
279
- onFilterChange,
280
- filters = DEFAULT_FILTERS,
281
- recentItems,
282
- resultItems,
283
- className,
284
- }: SearchModalPanelProps) => {
285
- const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
286
- onQueryChange?.(event.target.value);
287
- };
288
-
289
- return (
290
- <div className={cn("flex min-h-0 flex-1 flex-col", className)} data-state={state}>
291
- <SearchModalInput value={query} onChange={handleChange} />
292
- <SearchModalDivider />
293
- <SearchModalBody className={state === "loading" ? "p-0" : undefined}>
294
- {state === "idle" && <SearchModalIdle items={recentItems} />}
295
- {state === "loading" && <SearchModalLoading />}
296
- {state === "results" && (
297
- <SearchModalFilters
298
- tabs={filters}
299
- value={filter}
300
- onValueChange={(next) => onFilterChange?.(next)}
301
- >
302
- <SearchModalResults items={resultItems} />
303
- </SearchModalFilters>
304
- )}
305
- </SearchModalBody>
306
- </div>
307
- );
308
- };
309
-
310
- export type { ModalContentProps };
1
+ import {
2
+ type ChangeEvent,
3
+ type InputHTMLAttributes,
4
+ type ReactNode,
5
+ } from "react";
6
+ import {
7
+ Modal,
8
+ ModalBody,
9
+ ModalContent,
10
+ ModalDescription,
11
+ ModalTitle,
12
+ type ModalContentProps,
13
+ type ModalProps,
14
+ } from "../../components/Modal";
15
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../components/Tabs";
16
+ import { cn } from "../../utils/cn";
17
+ import { ResultRow, type ResultRowProps } from "./ResultRow";
18
+ import { SectionHeader } from "./SectionHeader";
19
+ import { Skeleton } from "../Skeleton";
20
+
21
+ export type SearchModalState = "idle" | "loading" | "results";
22
+
23
+ export type SearchFilterTab = {
24
+ value: string;
25
+ label: string;
26
+ count?: number;
27
+ };
28
+
29
+ export type SearchModalProps = ModalProps & {
30
+ /** Panel state when using the built-in layout helpers. */
31
+ state?: SearchModalState;
32
+ className?: string;
33
+ contentClassName?: string;
34
+ showOverlay?: boolean;
35
+ overlayClassName?: string;
36
+ children?: ReactNode;
37
+ };
38
+
39
+ const DEFAULT_FILTERS: SearchFilterTab[] = [
40
+ { value: "all", label: "All", count: 0 },
41
+ { value: "reports", label: "Reports", count: 0 },
42
+ { value: "chats", label: "Chats", count: 0 },
43
+ { value: "issues", label: "Issues", count: 0 },
44
+ ];
45
+
46
+ /**
47
+ * Search dialog composed on Modal.
48
+ * Use compound parts (Input / Body / Filters / Results) for app wiring,
49
+ * or pass `state` + demo children for Storybook.
50
+ */
51
+ export const SearchModal = ({
52
+ state,
53
+ className,
54
+ contentClassName,
55
+ showOverlay = true,
56
+ overlayClassName,
57
+ children,
58
+ ...modalProps
59
+ }: SearchModalProps) => (
60
+ <Modal {...modalProps}>
61
+ <ModalContent
62
+ showOverlay={showOverlay}
63
+ overlayClassName={overlayClassName}
64
+ className={cn(
65
+ "flex max-h-[min(80vh,640px)] w-[min(100vw-2rem,700px)] max-w-[768px] flex-col p-0",
66
+ contentClassName,
67
+ )}
68
+ aria-label="Search"
69
+ data-state={state}
70
+ >
71
+ <ModalTitle className="sr-only">Search</ModalTitle>
72
+ <ModalDescription className="sr-only">
73
+ Search reports, chats, or issues
74
+ </ModalDescription>
75
+ <div className={cn("flex min-h-0 flex-1 flex-col", className)}>
76
+ {children}
77
+ </div>
78
+ </ModalContent>
79
+ </Modal>
80
+ );
81
+
82
+ export type SearchModalInputProps = Omit<
83
+ InputHTMLAttributes<HTMLInputElement>,
84
+ "type"
85
+ > & {
86
+ className?: string;
87
+ wrapperClassName?: string;
88
+ };
89
+
90
+ export const SearchModalInput = ({
91
+ className,
92
+ wrapperClassName,
93
+ placeholder = "Search reports, chats, or issues…",
94
+ ...props
95
+ }: SearchModalInputProps) => (
96
+ <div
97
+ className={cn(
98
+ "shrink-0 p-3",
99
+ wrapperClassName,
100
+ )}
101
+ >
102
+ <div className="flex min-h-10 items-center px-3">
103
+ <input
104
+ type="search"
105
+ placeholder={placeholder}
106
+ className={cn(
107
+ "w-full bg-transparent text-caption-1 text-display-on-light-primary",
108
+ "placeholder:text-display-on-light-tertiary focus:outline-none",
109
+ className,
110
+ )}
111
+ aria-label="Search query"
112
+ {...props}
113
+ />
114
+ </div>
115
+ </div>
116
+ );
117
+
118
+ export const SearchModalDivider = ({ className }: { className?: string }) => (
119
+ <div
120
+ className={cn("h-px w-full shrink-0 bg-divider-primary", className)}
121
+ aria-hidden
122
+ />
123
+ );
124
+
125
+ export type SearchModalBodyProps = {
126
+ children: ReactNode;
127
+ className?: string;
128
+ };
129
+
130
+ export const SearchModalBody = ({
131
+ children,
132
+ className,
133
+ }: SearchModalBodyProps) => (
134
+ <ModalBody className={cn("min-w-0 flex-1 p-2", className)}>
135
+ {children}
136
+ </ModalBody>
137
+ );
138
+
139
+ export type SearchModalFiltersProps = {
140
+ tabs?: SearchFilterTab[];
141
+ value: string;
142
+ onValueChange: (value: string) => void;
143
+ children?: ReactNode;
144
+ className?: string;
145
+ listClassName?: string;
146
+ };
147
+
148
+ /**
149
+ * Pill-style filter tabs for the results state.
150
+ * Children render below the tab list (typically a single filtered results list).
151
+ * For per-tab panels, nest `SearchModalFilterPanel` (TabsContent) as children.
152
+ */
153
+ export const SearchModalFilters = ({
154
+ tabs = DEFAULT_FILTERS,
155
+ value,
156
+ onValueChange,
157
+ children,
158
+ className,
159
+ listClassName,
160
+ }: SearchModalFiltersProps) => (
161
+ <Tabs
162
+ type="pills"
163
+ value={value}
164
+ onValueChange={onValueChange}
165
+ className={cn("gap-0", className)}
166
+ >
167
+ <TabsList className={cn("w-full p-2", listClassName)}>
168
+ {tabs.map((tab) => (
169
+ <TabsTrigger key={tab.value} value={tab.value} count={tab.count}>
170
+ {tab.label}
171
+ </TabsTrigger>
172
+ ))}
173
+ </TabsList>
174
+ {children}
175
+ </Tabs>
176
+ );
177
+
178
+ export type SearchModalFilterPanelProps = {
179
+ value: string;
180
+ children: ReactNode;
181
+ className?: string;
182
+ };
183
+
184
+ export const SearchModalFilterPanel = ({
185
+ value,
186
+ children,
187
+ className,
188
+ }: SearchModalFilterPanelProps) => (
189
+ <TabsContent value={value} className={className}>
190
+ {children}
191
+ </TabsContent>
192
+ );
193
+
194
+ export type SearchModalResultsProps = {
195
+ items?: ResultRowProps[];
196
+ children?: ReactNode;
197
+ className?: string;
198
+ };
199
+
200
+ export const SearchModalResults = ({
201
+ items,
202
+ children,
203
+ className,
204
+ }: SearchModalResultsProps) => (
205
+ <div className={cn("flex flex-col", className)}>
206
+ {items?.map((item, index) => (
207
+ <ResultRow key={index} {...item} />
208
+ ))}
209
+ {children}
210
+ </div>
211
+ );
212
+
213
+ export type SearchModalIdleProps = {
214
+ label?: string;
215
+ items?: ResultRowProps[];
216
+ children?: ReactNode;
217
+ className?: string;
218
+ };
219
+
220
+ export const SearchModalIdle = ({
221
+ label = "Recently viewed",
222
+ items,
223
+ children,
224
+ className,
225
+ }: SearchModalIdleProps) => (
226
+ <div className={cn("flex flex-col p-1", className)}>
227
+ <SectionHeader
228
+ label={label}
229
+ className="px-3 py-1 text-caption-2 font-normal normal-case tracking-[0.01em] text-display-on-light-secondary"
230
+ />
231
+ <SearchModalResults items={items}>{children}</SearchModalResults>
232
+ </div>
233
+ );
234
+
235
+ export type SearchModalLoadingProps = {
236
+ className?: string;
237
+ };
238
+
239
+ export const SearchModalLoading = ({ className }: SearchModalLoadingProps) => (
240
+ <Skeleton variant="search" className={cn("rounded-none", className)} />
241
+ );
242
+
243
+ /** Convenience controlled panel for Storybook / simple embeds. */
244
+ export type SearchModalPanelProps = {
245
+ state?: SearchModalState;
246
+ query?: string;
247
+ onQueryChange?: (query: string) => void;
248
+ filter?: string;
249
+ onFilterChange?: (filter: string) => void;
250
+ filters?: SearchFilterTab[];
251
+ recentItems?: ResultRowProps[];
252
+ resultItems?: ResultRowProps[];
253
+ className?: string;
254
+ };
255
+
256
+ export const SearchModalPanel = ({
257
+ state = "idle",
258
+ query = "",
259
+ onQueryChange,
260
+ filter = "all",
261
+ onFilterChange,
262
+ filters = DEFAULT_FILTERS,
263
+ recentItems,
264
+ resultItems,
265
+ className,
266
+ }: SearchModalPanelProps) => {
267
+ const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
268
+ onQueryChange?.(event.target.value);
269
+ };
270
+
271
+ return (
272
+ <div className={cn("flex min-h-0 flex-1 flex-col", className)} data-state={state}>
273
+ <SearchModalInput value={query} onChange={handleChange} />
274
+ <SearchModalDivider />
275
+ <SearchModalBody className={state === "loading" ? "p-0" : undefined}>
276
+ {state === "idle" && <SearchModalIdle items={recentItems} />}
277
+ {state === "loading" && <SearchModalLoading />}
278
+ {state === "results" && (
279
+ <SearchModalFilters
280
+ tabs={filters}
281
+ value={filter}
282
+ onValueChange={(next) => onFilterChange?.(next)}
283
+ >
284
+ <SearchModalResults items={resultItems} />
285
+ </SearchModalFilters>
286
+ )}
287
+ </SearchModalBody>
288
+ </div>
289
+ );
290
+ };
291
+
292
+ export type { ModalContentProps };
@@ -1,31 +1,31 @@
1
- export { Pill, type PillProps, type PillSize, type PillState } from "./Pill";
2
- export { PillInfo, type PillInfoProps, type PillInfoType } from "./PillInfo";
3
- export {
4
- ResultRow,
5
- type ResultRowProps,
6
- type ResultRowState,
7
- type ResultRowVariant,
8
- } from "./ResultRow";
9
- export {
10
- SearchModal,
11
- SearchModalBody,
12
- SearchModalDivider,
13
- SearchModalFilterPanel,
14
- SearchModalFilters,
15
- SearchModalIdle,
16
- SearchModalInput,
17
- SearchModalLoading,
18
- SearchModalPanel,
19
- SearchModalResults,
20
- type SearchFilterTab,
21
- type SearchModalBodyProps,
22
- type SearchModalFiltersProps,
23
- type SearchModalIdleProps,
24
- type SearchModalInputProps,
25
- type SearchModalLoadingProps,
26
- type SearchModalPanelProps,
27
- type SearchModalProps,
28
- type SearchModalResultsProps,
29
- type SearchModalState,
30
- } from "./SearchModal";
31
- export { SectionHeader, type SectionHeaderProps } from "./SectionHeader";
1
+ export { Pill, type PillProps, type PillSize, type PillState } from "./Pill";
2
+ export { PillInfo, type PillInfoProps, type PillInfoType } from "./PillInfo";
3
+ export {
4
+ ResultRow,
5
+ type ResultRowProps,
6
+ type ResultRowState,
7
+ type ResultRowVariant,
8
+ } from "./ResultRow";
9
+ export {
10
+ SearchModal,
11
+ SearchModalBody,
12
+ SearchModalDivider,
13
+ SearchModalFilterPanel,
14
+ SearchModalFilters,
15
+ SearchModalIdle,
16
+ SearchModalInput,
17
+ SearchModalLoading,
18
+ SearchModalPanel,
19
+ SearchModalResults,
20
+ type SearchFilterTab,
21
+ type SearchModalBodyProps,
22
+ type SearchModalFiltersProps,
23
+ type SearchModalIdleProps,
24
+ type SearchModalInputProps,
25
+ type SearchModalLoadingProps,
26
+ type SearchModalPanelProps,
27
+ type SearchModalProps,
28
+ type SearchModalResultsProps,
29
+ type SearchModalState,
30
+ } from "./SearchModal";
31
+ export { SectionHeader, type SectionHeaderProps } from "./SectionHeader";