@orderly.network/ui-scaffold 2.5.1 → 2.5.2

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/README.md ADDED
@@ -0,0 +1,575 @@
1
+ # Scaffold Component Integration Guide
2
+
3
+ ## Overview
4
+
5
+ Scaffold is a powerful layout component that provides a complete application scaffolding structure, including top navigation bar, sidebar, main content area, bottom navigation, and footer. This component supports responsive layouts for both desktop and mobile devices and offers rich customization options.
6
+
7
+ ## Installation and Import
8
+
9
+ ```typescript
10
+ import { Scaffold, type ScaffoldProps } from "@orderly.network/ui-scaffold";
11
+ ```
12
+
13
+ ## Basic Usage
14
+
15
+ ```typescript
16
+ import React from 'react';
17
+ import { Scaffold } from "@orderly.network/ui-scaffold";
18
+
19
+ const App = () => {
20
+ return (
21
+ <Scaffold>
22
+ <div>Your main content here</div>
23
+ </Scaffold>
24
+ );
25
+ };
26
+ ```
27
+
28
+ ## ScaffoldProps Properties Detailed
29
+
30
+ ### 1. leftSidebar (Optional)
31
+
32
+ - **Type**: `React.ReactNode`
33
+ - **Description**: Custom left sidebar component. If provided, the layout will use this component instead of the default sidebar component
34
+ - **Use Case**: When you need to completely customize the sidebar style and functionality
35
+
36
+ ```typescript
37
+ <Scaffold
38
+ leftSidebar={<CustomSidebar />}
39
+ >
40
+ {children}
41
+ </Scaffold>
42
+ ```
43
+
44
+ ### 2. leftSideProps (Optional)
45
+
46
+ - **Type**: `SideBarProps`
47
+ - **Description**: Configuration properties for the default sidebar component
48
+ - **Main Properties**:
49
+ - `title`: Sidebar title
50
+ - `items`: Array of menu items
51
+ - `open`: Whether the sidebar is expanded
52
+ - `onOpenChange`: Callback for expansion state changes
53
+ - `onItemSelect`: Callback for menu item selection
54
+ - `current`: Currently selected menu item
55
+ - `maxWidth`: Maximum width when expanded (default: 185px)
56
+ - `minWidth`: Minimum width when collapsed (default: 98px)
57
+
58
+ ```typescript
59
+ const sidebarMenus = [
60
+ { name: "Dashboard", href: "/dashboard", icon: <DashboardIcon /> },
61
+ { name: "Trading", href: "/trading", icon: <TradingIcon /> },
62
+ { name: "Portfolio", href: "/portfolio", icon: <PortfolioIcon /> }
63
+ ];
64
+
65
+ <Scaffold
66
+ leftSideProps={{
67
+ title: "Navigation",
68
+ items: sidebarMenus,
69
+ current: "/dashboard",
70
+ onItemSelect: (item) => console.log('Selected:', item),
71
+ maxWidth: 200,
72
+ minWidth: 80
73
+ }}
74
+ >
75
+ {children}
76
+ </Scaffold>
77
+ ```
78
+
79
+ ### 3. topBar (Optional)
80
+
81
+ - **Type**: `React.ReactNode`
82
+ - **Description**: Custom top navigation bar component
83
+ - **Use Case**: When you need to completely customize the top navigation bar
84
+
85
+ ```typescript
86
+ <Scaffold
87
+ topBar={<CustomTopBar />}
88
+ >
89
+ {children}
90
+ </Scaffold>
91
+ ```
92
+
93
+ ### 4. mainNavProps (Optional)
94
+
95
+ - **Type**: `MainNavWidgetProps`
96
+ - **Description**: Configuration properties for the default main navigation component
97
+ - **Main Properties**:
98
+ - `leading`: Content on the left side of the navigation bar
99
+ - `trailing`: Content on the right side of the navigation bar
100
+ - `logo`: Brand logo configuration
101
+ - `mainMenus`: Array of main menu items
102
+ - `campaigns`: Campaign menu item
103
+ - `campaignPosition`: Campaign menu position
104
+ - `initialMenu`: Initial menu path
105
+ - `onItemClick`: Menu item click callback
106
+ - `leftNav`: Left navigation configuration for mobile drawer (only works on mobile)
107
+ - `customLeftNav`: Custom left navigation component to replace the default leftNav
108
+ - `customRender`: Function to customize the main navigation layout
109
+ - **Type**: `(components: MainNavComponents) => ReactNode`
110
+ - **Components**:
111
+ - `title`: Logo or title component (desktop & mobile)
112
+ - `languageSwitcher`: Language selection component (desktop & mobile)
113
+ - `subAccount`: Sub-account component (desktop & mobile)
114
+ - `linkDevice`: Device linking component (desktop & mobile)
115
+ - `chainMenu`: Chain selection menu (desktop & mobile)
116
+ - `walletConnect`: Wallet connection component (desktop & mobile)
117
+ - `mainNav`: Main navigation menu (desktop only)
118
+ - `accountSummary`: Account summary component (desktop only)
119
+ - `leftNav`: Left navigation component (mobile only)
120
+ - `scanQRCode`: QR code scanner component (mobile only)
121
+
122
+ ```typescript
123
+ <Scaffold
124
+ mainNavProps={{
125
+ logo: {
126
+ src: "/logo.png",
127
+ alt: "Company Logo"
128
+ },
129
+ mainMenus: [
130
+ { name: "Trade", href: "/trade" },
131
+ { name: "Portfolio", href: "/portfolio" }
132
+ ],
133
+ leading: <BrandSection />,
134
+ trailing: <UserActions />,
135
+ onItemClick: ({ href, name }) => {
136
+ console.log(`Navigating to ${href}`);
137
+ },
138
+ leftNav: {
139
+ menus: [
140
+ { name: "Dashboard", href: "/dashboard", icon: <DashboardIcon /> },
141
+ { name: "Trading", href: "/trading", icon: <TradingIcon /> },
142
+ { name: "Portfolio", href: "/portfolio", icon: <PortfolioIcon /> }
143
+ ],
144
+ leading: <CustomLeftNavHeader />,
145
+ twitterUrl: "https://twitter.com/yourhandle",
146
+ telegramUrl: "https://t.me/yourgroup",
147
+ discordUrl: "https://discord.gg/yourserver",
148
+ duneUrl: "https://dune.com/youranalytics",
149
+ feedbackUrl: "https://feedback.yoursite.com"
150
+ },
151
+ // Custom layout for main navigation
152
+ customRender: (components) => {
153
+ // Mobile layout
154
+ if (isMobile) {
155
+ return (
156
+ <Flex width="100%" justify="between">
157
+ <Flex gapX={2}>
158
+ {components.leftNav}
159
+ {components.title}
160
+ </Flex>
161
+ <Flex gapX={2}>
162
+ {components.languageSwitcher}
163
+ {components.scanQRCode}
164
+ {components.linkDevice}
165
+ {components.chainMenu}
166
+ {components.walletConnect}
167
+ </Flex>
168
+ </Flex>
169
+ );
170
+ }
171
+
172
+ // Desktop layout
173
+ return (
174
+ <Flex width="100%" justify="between">
175
+ <Flex gapX={2}>
176
+ {components.title}
177
+ {components.mainNav}
178
+ </Flex>
179
+ <Flex gapX={2}>
180
+ {components.accountSummary}
181
+ {components.linkDevice}
182
+ {components.languageSwitcher}
183
+ {components.subAccount}
184
+ {components.chainMenu}
185
+ {components.walletConnect}
186
+ </Flex>
187
+ </Flex>
188
+ );
189
+ }
190
+ }}
191
+ >
192
+ {children}
193
+ </Scaffold>
194
+ ```
195
+
196
+ ### 5. leftNav Configuration (Optional)
197
+
198
+ `leftNav` is a configuration property within `mainNavProps` that controls the mobile slide-out navigation drawer. It only appears on mobile devices and provides a comprehensive menu system.
199
+
200
+ #### LeftNavProps Properties
201
+
202
+ - **Type**: `LeftNavProps`
203
+ - **Description**: Configuration for the mobile left navigation drawer
204
+ - **Main Properties**:
205
+ - `menus`: Array of navigation menu items for the drawer
206
+ - `leading`: Custom content at the top of the drawer (below logo)
207
+ - `twitterUrl`: Twitter/X social media link
208
+ - `telegramUrl`: Telegram community link
209
+ - `discordUrl`: Discord community link
210
+ - `duneUrl`: Dune Analytics link
211
+ - `feedbackUrl`: Feedback form link
212
+ - `customLeftNav`: Custom component to replace the default leftNav trigger
213
+
214
+ #### LeftNavItem Properties
215
+
216
+ Each menu item in the `menus` array supports:
217
+
218
+ - `name`: Display name of the menu item
219
+ - `href`: Navigation URL
220
+ - `icon`: Icon component to display (optional)
221
+ - `trailing`: Additional content on the right side (optional)
222
+ - `customRender`: Custom render function for complete control over item appearance
223
+
224
+ #### Usage Example
225
+
226
+ ```typescript
227
+ <Scaffold
228
+ mainNavProps={{
229
+ leftNav: {
230
+ menus: [
231
+ {
232
+ name: "Dashboard",
233
+ href: "/dashboard",
234
+ icon: <DashboardIcon />
235
+ },
236
+ {
237
+ name: "Trading",
238
+ href: "/trading",
239
+ icon: <TradingIcon />,
240
+ trailing: <NotificationBadge />
241
+ },
242
+ {
243
+ name: "Portfolio",
244
+ href: "/portfolio",
245
+ icon: <PortfolioIcon />,
246
+ customRender: ({ name, href, isActive }) => (
247
+ <div className={`custom-menu-item ${isActive ? 'active' : ''}`}>
248
+ <PortfolioIcon />
249
+ <span>{name}</span>
250
+ <SpecialBadge />
251
+ </div>
252
+ )
253
+ }
254
+ ],
255
+ leading: (
256
+ <div className="px-3 py-2">
257
+ <Text className="text-sm text-gray-500">Quick Actions</Text>
258
+ </div>
259
+ ),
260
+ twitterUrl: "https://twitter.com/yourhandle",
261
+ telegramUrl: "https://t.me/yourgroup",
262
+ discordUrl: "https://discord.gg/yourserver",
263
+ duneUrl: "https://dune.com/youranalytics",
264
+ feedbackUrl: "https://feedback.yoursite.com"
265
+ }
266
+ }}
267
+ >
268
+ {children}
269
+ </Scaffold>
270
+ ```
271
+
272
+ #### Mobile Navigation Behavior
273
+
274
+ - **Trigger**: The leftNav appears as a hamburger menu icon on mobile devices
275
+ - **Drawer**: Slides out from the left side when triggered
276
+ - **Content Structure**:
277
+ 1. Logo at the top
278
+ 2. Leading content (if provided)
279
+ 3. Sub-account selector (if user is logged in with trading enabled)
280
+ 4. Menu items list (scrollable if needed)
281
+ 5. Social media links at the bottom
282
+ 6. Feedback link at the very bottom
283
+ - **Auto-close**: The drawer automatically closes when a menu item is selected
284
+
285
+ #### Advanced Customization
286
+
287
+ You can provide a completely custom left navigation trigger:
288
+
289
+ ```typescript
290
+ <Scaffold
291
+ mainNavProps={{
292
+ customLeftNav: <CustomHamburgerButton />,
293
+ leftNav: {
294
+ // ... leftNav configuration
295
+ }
296
+ }}
297
+ >
298
+ {children}
299
+ </Scaffold>
300
+ ```
301
+
302
+ ### 6. bottomNavProps (Optional)
303
+
304
+ - **Type**: `BottomNavProps`
305
+ - **Description**: Mobile bottom navigation configuration (only displayed on mobile devices)
306
+ - **Main Properties**:
307
+ - `mainMenus`: Array of bottom menu items
308
+
309
+ ```typescript
310
+ const bottomMenus = [
311
+ {
312
+ name: "Home",
313
+ href: "/home",
314
+ activeIcon: <HomeActiveIcon />,
315
+ inactiveIcon: <HomeInactiveIcon />
316
+ },
317
+ {
318
+ name: "Trade",
319
+ href: "/trade",
320
+ activeIcon: <TradeActiveIcon />,
321
+ inactiveIcon: <TradeInactiveIcon />
322
+ }
323
+ ];
324
+
325
+ <Scaffold
326
+ bottomNavProps={{
327
+ mainMenus: bottomMenus,
328
+ }}
329
+ >
330
+ {children}
331
+ </Scaffold>
332
+ ```
333
+
334
+ ### 7. footer (Optional)
335
+
336
+ - **Type**: `React.ReactNode`
337
+ - **Description**: Custom footer component
338
+
339
+ ```typescript
340
+ <Scaffold
341
+ footer={<CustomFooter />}
342
+ >
343
+ {children}
344
+ </Scaffold>
345
+ ```
346
+
347
+ ### 8. footerProps (Optional)
348
+
349
+ - **Type**: `FooterProps`
350
+ - **Description**: Configuration properties for the default footer component
351
+ - **Main Properties**:
352
+ - `telegramUrl`: Telegram link
353
+ - `twitterUrl`: Twitter link
354
+ - `discordUrl`: Discord link
355
+ - `trailing`: Content on the right side of the footer
356
+
357
+ ```typescript
358
+ <Scaffold
359
+ footerProps={{
360
+ telegramUrl: "https://t.me/yourgroup",
361
+ twitterUrl: "https://twitter.com/yourhandle",
362
+ discordUrl: "https://discord.gg/yourserver",
363
+ trailing: <AdditionalFooterContent />
364
+ }}
365
+ >
366
+ {children}
367
+ </Scaffold>
368
+ ```
369
+
370
+ ### 9. routerAdapter (Optional)
371
+
372
+ - **Type**: `RouterAdapter`
373
+ - **Description**: Router adapter for integrating with different routing libraries
374
+ - **Main Properties**:
375
+ - `onRouteChange`: Route change handler function
376
+ - `currentPath`: Current path
377
+
378
+ ```typescript
379
+ // React Router integration example
380
+ const routerAdapter = {
381
+ onRouteChange: (option) => {
382
+ if (option.target === '_blank') {
383
+ window.open(option.href, '_blank');
384
+ } else {
385
+ navigate(option.href);
386
+ }
387
+ },
388
+ currentPath: location.pathname
389
+ };
390
+
391
+ <Scaffold
392
+ routerAdapter={routerAdapter}
393
+ >
394
+ {children}
395
+ </Scaffold>
396
+ ```
397
+
398
+ ### 10. classNames (Optional)
399
+
400
+ - **Type**: Style class name configuration object
401
+ - **Description**: Used to customize styles for various layout areas
402
+ - **Configurable Areas**:
403
+ - `root`: Root container (topNavbar + container + footer)
404
+ - `container`: Main container
405
+ - `content`: Content area
406
+ - `body`: Body area (leftSidebar + content)
407
+ - `leftSidebar`: Left sidebar
408
+ - `topNavbar`: Top navigation bar
409
+ - `footer`: Footer
410
+
411
+ ```typescript
412
+ <Scaffold
413
+ classNames={{
414
+ root: "custom-root-class",
415
+ container: "custom-container-class",
416
+ content: "custom-content-class",
417
+ body: "custom-body-class",
418
+ leftSidebar: "custom-sidebar-class",
419
+ topNavbar: "custom-navbar-class",
420
+ footer: "custom-footer-class"
421
+ }}
422
+ >
423
+ {children}
424
+ </Scaffold>
425
+ ```
426
+
427
+ ## Complete Usage Example
428
+
429
+ ```typescript
430
+ import React from 'react';
431
+ import { Scaffold } from "@orderly.network/ui-scaffold";
432
+ import { useNavigate, useLocation } from 'react-router-dom';
433
+
434
+ const App = () => {
435
+ const navigate = useNavigate();
436
+ const location = useLocation();
437
+
438
+ const sidebarMenus = [
439
+ {
440
+ name: "Dashboard",
441
+ href: "/dashboard",
442
+ icon: <DashboardIcon />
443
+ },
444
+ {
445
+ name: "Trading",
446
+ href: "/trading",
447
+ icon: <TradingIcon />
448
+ },
449
+ {
450
+ name: "Portfolio",
451
+ href: "/portfolio",
452
+ icon: <PortfolioIcon />
453
+ }
454
+ ];
455
+
456
+ const bottomMenus = [
457
+ {
458
+ name: "Home",
459
+ href: "/home",
460
+ activeIcon: <HomeActiveIcon />,
461
+ inactiveIcon: <HomeInactiveIcon />
462
+ },
463
+ {
464
+ name: "Trade",
465
+ href: "/trade",
466
+ activeIcon: <TradeActiveIcon />,
467
+ inactiveIcon: <TradeInactiveIcon />
468
+ }
469
+ ];
470
+
471
+ const routerAdapter = {
472
+ onRouteChange: (option) => {
473
+ if (option.target === '_blank') {
474
+ window.open(option.href, '_blank');
475
+ } else {
476
+ navigate(option.href);
477
+ }
478
+ },
479
+ currentPath: location.pathname
480
+ };
481
+
482
+ return (
483
+ <Scaffold
484
+ leftSideProps={{
485
+ title: "Navigation",
486
+ items: sidebarMenus,
487
+ current: location.pathname,
488
+ onItemSelect: (item) => {
489
+ if (item.href) {
490
+ navigate(item.href);
491
+ }
492
+ }
493
+ }}
494
+ mainNavProps={{
495
+ logo: {
496
+ src: "/logo.png",
497
+ alt: "Company Logo"
498
+ },
499
+ mainMenus: [
500
+ { name: "Trade", href: "/trade" },
501
+ { name: "Portfolio", href: "/portfolio" }
502
+ ],
503
+ leading: <BrandSection />,
504
+ trailing: <UserActions />,
505
+ onItemClick: ({ href }) => navigate(href),
506
+ leftNav: {
507
+ menus: sidebarMenus, // Reuse the same menu items
508
+ twitterUrl: "https://twitter.com/yourhandle",
509
+ telegramUrl: "https://t.me/yourgroup",
510
+ discordUrl: "https://discord.gg/yourserver",
511
+ feedbackUrl: "https://feedback.yoursite.com"
512
+ }
513
+ }}
514
+ bottomNavProps={{
515
+ mainMenus: bottomMenus,
516
+ }}
517
+ footerProps={{
518
+ telegramUrl: "https://t.me/yourgroup",
519
+ twitterUrl: "https://twitter.com/yourhandle",
520
+ discordUrl: "https://discord.gg/yourserver"
521
+ }}
522
+ routerAdapter={routerAdapter}
523
+ classNames={{
524
+ content: "p-4",
525
+ leftSidebar: "border-r border-gray-200"
526
+ }}
527
+ >
528
+ <div className="min-h-screen">
529
+ {/* Your main application content */}
530
+ <h1>Welcome to your application</h1>
531
+ <p>This is the main content area.</p>
532
+ </div>
533
+ </Scaffold>
534
+ );
535
+ };
536
+
537
+ export default App;
538
+ ```
539
+
540
+ ## Responsive Behavior
541
+
542
+ The Scaffold component automatically detects device type and provides appropriate layouts:
543
+
544
+ - **Desktop**: Displays complete sidebar, top navigation bar, and footer
545
+ - **Mobile**: Hides sidebar, displays bottom navigation bar and left navigation drawer (leftNav), optimized for touch interaction
546
+
547
+ ### Mobile Navigation Features
548
+
549
+ - **Left Navigation Drawer**: Accessed via hamburger menu icon, provides slide-out menu with complete navigation options
550
+ - **Bottom Navigation**: Fixed bottom bar for quick access to main sections
551
+ - **Responsive Top Bar**: Adapts to show essential controls and navigation elements
552
+
553
+ ## Best Practices
554
+
555
+ 1. **Router Integration**: Always provide `routerAdapter` to ensure navigation functionality works properly
556
+ 2. **Responsive Design**: Provide `bottomNavProps` and `leftNav` configuration for optimal mobile experience
557
+ 3. **Style Customization**: Use the `classNames` property for style customization instead of directly modifying component styles
558
+ 4. **Menu State Management**: Ensure the `current` property is synchronized with actual route state
559
+ 5. **Performance Optimization**: For large menu lists, consider using React.memo to optimize rendering performance
560
+ 6. **Mobile Navigation**: Configure `leftNav` with social media links and feedback URL to provide comprehensive mobile navigation experience
561
+
562
+ ## Important Notes
563
+
564
+ - The Scaffold component must be wrapped in `ScaffoldProvider` to work properly (automatically handled internally)
565
+ - Sidebar expand/collapse state is automatically saved to localStorage
566
+ - Mobile and desktop use different layout components to ensure good user experience on different devices
567
+ - The `leftNav` drawer is only available on mobile devices and provides a comprehensive navigation menu
568
+ - All navigation callbacks should properly handle route navigation logic
569
+
570
+ ## Related Components
571
+
572
+ - `ScaffoldProvider`: Provides Scaffold context
573
+ - `useScaffoldContext`: Hook to get Scaffold state
574
+ - `MobileScaffold`: Mobile layout component
575
+ - `DesktopScaffold`: Desktop layout component
package/dist/index.d.mts CHANGED
@@ -25,6 +25,11 @@ type LeftNavItem = {
25
25
  href: string;
26
26
  icon?: ReactNode;
27
27
  trailing?: ReactNode;
28
+ customRender?: (option: {
29
+ name: string;
30
+ href: string;
31
+ isActive?: boolean;
32
+ }) => React.ReactNode;
28
33
  };
29
34
 
30
35
  type MainNavItem = {
@@ -56,6 +61,21 @@ type MainNavItem = {
56
61
  * if true, the item will be shown as a home page in mobile
57
62
  */
58
63
  isHomePageInMobile?: boolean;
64
+ customRender?: (option: {
65
+ name: string;
66
+ href: string;
67
+ isActive?: boolean;
68
+ }) => react__default.ReactNode;
69
+ tooltipConfig?: {
70
+ /**
71
+ * if true, the tooltip will be shown on first visit
72
+ */
73
+ showOnFirstVisit?: boolean;
74
+ /**
75
+ * the text to show in the tooltip
76
+ */
77
+ text?: string;
78
+ };
59
79
  };
60
80
 
61
81
  type MainNavClassNames = {
package/dist/index.d.ts CHANGED
@@ -25,6 +25,11 @@ type LeftNavItem = {
25
25
  href: string;
26
26
  icon?: ReactNode;
27
27
  trailing?: ReactNode;
28
+ customRender?: (option: {
29
+ name: string;
30
+ href: string;
31
+ isActive?: boolean;
32
+ }) => React.ReactNode;
28
33
  };
29
34
 
30
35
  type MainNavItem = {
@@ -56,6 +61,21 @@ type MainNavItem = {
56
61
  * if true, the item will be shown as a home page in mobile
57
62
  */
58
63
  isHomePageInMobile?: boolean;
64
+ customRender?: (option: {
65
+ name: string;
66
+ href: string;
67
+ isActive?: boolean;
68
+ }) => react__default.ReactNode;
69
+ tooltipConfig?: {
70
+ /**
71
+ * if true, the tooltip will be shown on first visit
72
+ */
73
+ showOnFirstVisit?: boolean;
74
+ /**
75
+ * the text to show in the tooltip
76
+ */
77
+ text?: string;
78
+ };
59
79
  };
60
80
 
61
81
  type MainNavClassNames = {