@qwickapps/react-framework 1.5.5 → 1.5.7
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/dist/components/QwickApp.d.ts.map +1 -1
- package/dist/components/ResponsiveMenu.d.ts.map +1 -1
- package/dist/components/Scaffold.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/contexts/NavigationContext.d.ts +64 -0
- package/dist/contexts/NavigationContext.d.ts.map +1 -0
- package/dist/contexts/index.d.ts +2 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/hooks/useBaseProps.d.ts +12 -1161
- package/dist/hooks/useBaseProps.d.ts.map +1 -1
- package/dist/index.esm.js +514 -272
- package/dist/index.js +514 -272
- package/dist/palettes/manifest.json +19 -19
- package/dist/palettes/palette-autumn.1.4.9.css +172 -0
- package/dist/palettes/palette-autumn.1.4.9.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.0.css +172 -0
- package/dist/palettes/palette-autumn.1.5.0.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.1.css +172 -0
- package/dist/palettes/palette-autumn.1.5.1.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.2.css +172 -0
- package/dist/palettes/palette-autumn.1.5.2.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.3.css +172 -0
- package/dist/palettes/palette-autumn.1.5.3.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.4.css +172 -0
- package/dist/palettes/palette-autumn.1.5.4.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.6.css +172 -0
- package/dist/palettes/palette-autumn.1.5.6.min.css +1 -0
- package/dist/palettes/palette-autumn.1.5.7.css +172 -0
- package/dist/palettes/palette-autumn.1.5.7.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.4.9.css +172 -0
- package/dist/palettes/palette-cosmic.1.4.9.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.0.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.0.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.1.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.1.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.2.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.2.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.3.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.3.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.4.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.4.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.6.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.6.min.css +1 -0
- package/dist/palettes/palette-cosmic.1.5.7.css +172 -0
- package/dist/palettes/palette-cosmic.1.5.7.min.css +1 -0
- package/dist/palettes/palette-default.1.4.9.css +178 -0
- package/dist/palettes/palette-default.1.4.9.min.css +1 -0
- package/dist/palettes/palette-default.1.5.0.css +178 -0
- package/dist/palettes/palette-default.1.5.0.min.css +1 -0
- package/dist/palettes/palette-default.1.5.1.css +178 -0
- package/dist/palettes/palette-default.1.5.1.min.css +1 -0
- package/dist/palettes/palette-default.1.5.2.css +178 -0
- package/dist/palettes/palette-default.1.5.2.min.css +1 -0
- package/dist/palettes/palette-default.1.5.3.css +178 -0
- package/dist/palettes/palette-default.1.5.3.min.css +1 -0
- package/dist/palettes/palette-default.1.5.4.css +178 -0
- package/dist/palettes/palette-default.1.5.4.min.css +1 -0
- package/dist/palettes/palette-default.1.5.6.css +178 -0
- package/dist/palettes/palette-default.1.5.6.min.css +1 -0
- package/dist/palettes/palette-default.1.5.7.css +178 -0
- package/dist/palettes/palette-default.1.5.7.min.css +1 -0
- package/dist/palettes/palette-ocean.1.4.9.css +172 -0
- package/dist/palettes/palette-ocean.1.4.9.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.0.css +172 -0
- package/dist/palettes/palette-ocean.1.5.0.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.1.css +172 -0
- package/dist/palettes/palette-ocean.1.5.1.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.2.css +172 -0
- package/dist/palettes/palette-ocean.1.5.2.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.3.css +172 -0
- package/dist/palettes/palette-ocean.1.5.3.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.4.css +172 -0
- package/dist/palettes/palette-ocean.1.5.4.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.6.css +172 -0
- package/dist/palettes/palette-ocean.1.5.6.min.css +1 -0
- package/dist/palettes/palette-ocean.1.5.7.css +172 -0
- package/dist/palettes/palette-ocean.1.5.7.min.css +1 -0
- package/dist/palettes/palette-spring.1.4.9.css +160 -0
- package/dist/palettes/palette-spring.1.4.9.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.0.css +160 -0
- package/dist/palettes/palette-spring.1.5.0.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.1.css +160 -0
- package/dist/palettes/palette-spring.1.5.1.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.2.css +160 -0
- package/dist/palettes/palette-spring.1.5.2.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.3.css +166 -0
- package/dist/palettes/palette-spring.1.5.3.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.4.css +166 -0
- package/dist/palettes/palette-spring.1.5.4.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.6.css +166 -0
- package/dist/palettes/palette-spring.1.5.6.min.css +1 -0
- package/dist/palettes/palette-spring.1.5.7.css +166 -0
- package/dist/palettes/palette-spring.1.5.7.min.css +1 -0
- package/dist/palettes/palette-winter.1.4.9.css +172 -0
- package/dist/palettes/palette-winter.1.4.9.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.0.css +172 -0
- package/dist/palettes/palette-winter.1.5.0.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.1.css +172 -0
- package/dist/palettes/palette-winter.1.5.1.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.2.css +172 -0
- package/dist/palettes/palette-winter.1.5.2.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.3.css +172 -0
- package/dist/palettes/palette-winter.1.5.3.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.4.css +172 -0
- package/dist/palettes/palette-winter.1.5.4.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.6.css +172 -0
- package/dist/palettes/palette-winter.1.5.6.min.css +1 -0
- package/dist/palettes/palette-winter.1.5.7.css +172 -0
- package/dist/palettes/palette-winter.1.5.7.min.css +1 -0
- package/dist/utils/iconMap.d.ts +21 -8
- package/dist/utils/iconMap.d.ts.map +1 -1
- package/dist/utils/reactUtils.d.ts +0 -23
- package/dist/utils/reactUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/utils/iconMap.test.tsx +197 -0
- package/src/components/QwickApp.tsx +24 -15
- package/src/components/ResponsiveMenu.tsx +3 -4
- package/src/components/Scaffold.tsx +3 -4
- package/src/components/index.ts +1 -0
- package/src/components/pages/Page.tsx +2 -2
- package/src/contexts/NavigationContext.tsx +168 -0
- package/src/contexts/index.ts +2 -0
- package/src/utils/iconMap.tsx +209 -151
- package/src/utils/reactUtils.tsx +2 -47
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Context - Smart navigation that auto-detects React Router
|
|
3
|
+
*
|
|
4
|
+
* Provides navigation functions that work with React Router when available,
|
|
5
|
+
* falling back to window.location for non-router apps or SSR.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* - Wrap your app with NavigationProvider (done automatically by QwickApp)
|
|
9
|
+
* - Use useNavigation() hook to get navigate function and location
|
|
10
|
+
*
|
|
11
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createContext, useContext, type ReactNode } from 'react';
|
|
15
|
+
import {
|
|
16
|
+
useNavigate,
|
|
17
|
+
useLocation,
|
|
18
|
+
useInRouterContext,
|
|
19
|
+
} from 'react-router-dom';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Location type matching React Router's location shape
|
|
23
|
+
*/
|
|
24
|
+
export interface NavigationLocation {
|
|
25
|
+
pathname: string;
|
|
26
|
+
search: string;
|
|
27
|
+
hash: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Navigate function type
|
|
32
|
+
*/
|
|
33
|
+
export type NavigateFunction = (to: string | number) => void;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Navigation context value
|
|
37
|
+
*/
|
|
38
|
+
export interface NavigationContextValue {
|
|
39
|
+
navigate: NavigateFunction;
|
|
40
|
+
location: NavigationLocation | undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const NavigationContext = createContext<NavigationContextValue | null>(null);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Internal provider that uses React Router hooks
|
|
47
|
+
* Only rendered when we're inside a Router context
|
|
48
|
+
*/
|
|
49
|
+
function ReactRouterNavigationProvider({ children }: { children: ReactNode }) {
|
|
50
|
+
const reactRouterNavigate = useNavigate();
|
|
51
|
+
const reactRouterLocation = useLocation();
|
|
52
|
+
|
|
53
|
+
const navigate: NavigateFunction = (to) => {
|
|
54
|
+
if (typeof to === 'string') {
|
|
55
|
+
reactRouterNavigate(to);
|
|
56
|
+
} else if (typeof to === 'number') {
|
|
57
|
+
reactRouterNavigate(to);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Defensive check for location - fall back to window.location if React Router's location is undefined
|
|
62
|
+
const location: NavigationLocation | undefined = reactRouterLocation
|
|
63
|
+
? {
|
|
64
|
+
pathname: reactRouterLocation.pathname,
|
|
65
|
+
search: reactRouterLocation.search,
|
|
66
|
+
hash: reactRouterLocation.hash,
|
|
67
|
+
}
|
|
68
|
+
: typeof window !== 'undefined'
|
|
69
|
+
? {
|
|
70
|
+
pathname: window.location.pathname,
|
|
71
|
+
search: window.location.search,
|
|
72
|
+
hash: window.location.hash,
|
|
73
|
+
}
|
|
74
|
+
: undefined;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<NavigationContext.Provider value={{ navigate, location }}>
|
|
78
|
+
{children}
|
|
79
|
+
</NavigationContext.Provider>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Internal provider that uses window.location fallback
|
|
85
|
+
* Used when not inside a React Router context
|
|
86
|
+
*/
|
|
87
|
+
function FallbackNavigationProvider({ children }: { children: ReactNode }) {
|
|
88
|
+
const navigate: NavigateFunction = (to) => {
|
|
89
|
+
if (typeof window === 'undefined') return;
|
|
90
|
+
|
|
91
|
+
if (typeof to === 'string') {
|
|
92
|
+
window.location.href = to;
|
|
93
|
+
} else if (typeof to === 'number') {
|
|
94
|
+
window.history.go(to);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const location: NavigationLocation | undefined =
|
|
99
|
+
typeof window !== 'undefined'
|
|
100
|
+
? {
|
|
101
|
+
pathname: window.location.pathname,
|
|
102
|
+
search: window.location.search,
|
|
103
|
+
hash: window.location.hash,
|
|
104
|
+
}
|
|
105
|
+
: undefined;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<NavigationContext.Provider value={{ navigate, location }}>
|
|
109
|
+
{children}
|
|
110
|
+
</NavigationContext.Provider>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Smart Navigation Provider
|
|
116
|
+
*
|
|
117
|
+
* Automatically detects if the app is inside a React Router context:
|
|
118
|
+
* - If inside Router: uses React Router's useNavigate/useLocation (respects basename)
|
|
119
|
+
* - If outside Router: falls back to window.location
|
|
120
|
+
*
|
|
121
|
+
* This is included automatically by QwickApp - you don't need to add it manually.
|
|
122
|
+
*/
|
|
123
|
+
export function NavigationProvider({ children }: { children: ReactNode }) {
|
|
124
|
+
// Check if we're inside a React Router using the official hook
|
|
125
|
+
// This is more reliable than checking internal UNSAFE contexts
|
|
126
|
+
const isInRouter = useInRouterContext();
|
|
127
|
+
|
|
128
|
+
if (isInRouter) {
|
|
129
|
+
// We're inside a Router, use React Router's navigation
|
|
130
|
+
return (
|
|
131
|
+
<ReactRouterNavigationProvider>{children}</ReactRouterNavigationProvider>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Not inside a Router, use window.location fallback
|
|
136
|
+
return <FallbackNavigationProvider>{children}</FallbackNavigationProvider>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Hook to access navigation functions
|
|
141
|
+
*
|
|
142
|
+
* @returns Object containing navigate function and current location
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```tsx
|
|
146
|
+
* function MyComponent() {
|
|
147
|
+
* const { navigate, location } = useNavigation();
|
|
148
|
+
*
|
|
149
|
+
* const handleClick = () => {
|
|
150
|
+
* navigate('/dashboard');
|
|
151
|
+
* };
|
|
152
|
+
*
|
|
153
|
+
* return <button onClick={handleClick}>Go to Dashboard</button>;
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function useNavigation(): NavigationContextValue {
|
|
158
|
+
const context = useContext(NavigationContext);
|
|
159
|
+
|
|
160
|
+
if (!context) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
'useNavigation must be used within a NavigationProvider. ' +
|
|
163
|
+
'Make sure your component is wrapped in QwickApp or NavigationProvider.'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return context;
|
|
168
|
+
}
|
package/src/contexts/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ export { DataProvider, t, T, useData, useDataContext, useDataProvider, useResolv
|
|
|
3
3
|
export type { DataProviderProps } from './DataContext';
|
|
4
4
|
export { DimensionsProvider, useDimensions } from './DimensionsContext';
|
|
5
5
|
export type { DimensionsContextValue, DimensionTokens } from './DimensionsContext';
|
|
6
|
+
export { NavigationProvider, useNavigation } from './NavigationContext';
|
|
7
|
+
export type { NavigateFunction, NavigationContextValue, NavigationLocation } from './NavigationContext';
|
|
6
8
|
export { PaletteProvider, usePalette } from './PaletteContext';
|
|
7
9
|
export type { PaletteConfig, PaletteContextValue } from './PaletteContext';
|
|
8
10
|
export { ThemeProvider, useTheme } from './ThemeContext';
|
package/src/utils/iconMap.tsx
CHANGED
|
@@ -1,80 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Icon Mapping Utility
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Provides centralized icon mapping for both Material-UI components and emoji representations.
|
|
5
5
|
* Used across the framework for consistent icon rendering in buttons, navigation, admin UI, etc.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Static map for commonly used icons with emoji support
|
|
9
|
+
* - Fallback to HelpOutline icon for unmapped icons (with console warning)
|
|
10
|
+
* - Runtime icon registration via registerIcon() for app-specific icons
|
|
11
|
+
*
|
|
7
12
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
8
13
|
*/
|
|
9
14
|
|
|
10
15
|
import React from 'react';
|
|
16
|
+
|
|
17
|
+
// Material UI Icons - sorted alphabetically
|
|
11
18
|
import {
|
|
19
|
+
AccountCircle,
|
|
20
|
+
Add,
|
|
21
|
+
Architecture,
|
|
22
|
+
ArrowBack,
|
|
23
|
+
ArrowForward,
|
|
12
24
|
Article,
|
|
13
|
-
|
|
25
|
+
AttachMoney,
|
|
26
|
+
Autorenew,
|
|
27
|
+
Block,
|
|
14
28
|
Book,
|
|
15
|
-
|
|
29
|
+
Business,
|
|
30
|
+
Check,
|
|
31
|
+
CheckCircle,
|
|
32
|
+
Close,
|
|
33
|
+
Cloud,
|
|
16
34
|
CloudDownload,
|
|
17
35
|
CloudUpload,
|
|
36
|
+
Code,
|
|
18
37
|
Computer,
|
|
19
|
-
|
|
38
|
+
Construction,
|
|
20
39
|
Dashboard,
|
|
40
|
+
Delete,
|
|
41
|
+
Download,
|
|
42
|
+
Edit,
|
|
43
|
+
Email,
|
|
44
|
+
Explore,
|
|
45
|
+
Favorite,
|
|
46
|
+
Group,
|
|
47
|
+
Help,
|
|
48
|
+
HelpOutline,
|
|
49
|
+
Home,
|
|
21
50
|
Info,
|
|
51
|
+
InsertPhoto,
|
|
52
|
+
IntegrationInstructions,
|
|
22
53
|
Inventory,
|
|
23
54
|
Inventory2,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
Key,
|
|
56
|
+
Layers,
|
|
57
|
+
LibraryBooks,
|
|
58
|
+
LocalOffer,
|
|
59
|
+
Lock,
|
|
60
|
+
LockOpen,
|
|
61
|
+
Login,
|
|
62
|
+
Logout,
|
|
63
|
+
ManageAccounts,
|
|
64
|
+
Memory,
|
|
32
65
|
Menu,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
66
|
+
Notifications,
|
|
67
|
+
People,
|
|
68
|
+
Person,
|
|
69
|
+
PersonSearch,
|
|
70
|
+
Phone,
|
|
71
|
+
PhotoLibrary,
|
|
72
|
+
PlayArrow,
|
|
73
|
+
Psychology,
|
|
74
|
+
Refresh,
|
|
75
|
+
Rocket,
|
|
76
|
+
RotateRight,
|
|
77
|
+
Route,
|
|
37
78
|
Save,
|
|
79
|
+
Search,
|
|
80
|
+
Security,
|
|
38
81
|
Send,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Group,
|
|
43
|
-
Business,
|
|
82
|
+
Settings,
|
|
83
|
+
Share,
|
|
84
|
+
Shield,
|
|
44
85
|
ShoppingCart,
|
|
45
86
|
Speed,
|
|
87
|
+
Star,
|
|
88
|
+
Storage,
|
|
46
89
|
SupportAgent,
|
|
90
|
+
Sync,
|
|
91
|
+
TrendingUp,
|
|
47
92
|
Tune,
|
|
48
|
-
|
|
49
|
-
Lock,
|
|
50
|
-
LockOpen,
|
|
93
|
+
VerifiedUser,
|
|
51
94
|
Visibility,
|
|
52
95
|
VisibilityOff,
|
|
53
|
-
// New icons for seed content
|
|
54
|
-
Psychology,
|
|
55
|
-
Autorenew,
|
|
56
|
-
Code,
|
|
57
|
-
IntegrationInstructions,
|
|
58
|
-
Construction,
|
|
59
|
-
Work,
|
|
60
|
-
Layers,
|
|
61
|
-
TrendingUp,
|
|
62
|
-
Route,
|
|
63
|
-
Sync,
|
|
64
|
-
Architecture,
|
|
65
|
-
Security,
|
|
66
|
-
VerifiedUser,
|
|
67
|
-
// Additional icons for control panels and admin UIs
|
|
68
96
|
VpnKey,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
ManageAccounts,
|
|
72
|
-
Storage,
|
|
73
|
-
Refresh,
|
|
74
|
-
Block,
|
|
75
|
-
CheckCircle,
|
|
76
|
-
RotateRight,
|
|
77
|
-
Memory,
|
|
97
|
+
Work,
|
|
98
|
+
WorkspacePremium,
|
|
78
99
|
} from '@mui/icons-material';
|
|
79
100
|
|
|
80
101
|
/**
|
|
@@ -86,139 +107,175 @@ export interface IconMapping {
|
|
|
86
107
|
}
|
|
87
108
|
|
|
88
109
|
/**
|
|
89
|
-
* Centralized icon registry mapping icon names to their representations
|
|
90
|
-
*
|
|
110
|
+
* Centralized icon registry mapping icon names to their representations.
|
|
111
|
+
* Sorted alphabetically by category, then by key within each category.
|
|
112
|
+
*
|
|
113
|
+
* For icons not in this map, getIconComponent() will return a HelpOutline fallback
|
|
114
|
+
* and log a warning. Use registerIcon() to add app-specific icons at runtime.
|
|
91
115
|
*/
|
|
92
116
|
export const iconMap: Record<string, IconMapping> = {
|
|
93
|
-
//
|
|
94
|
-
home: { emoji: '🏠', component: Home },
|
|
95
|
-
menu: { emoji: '☰', component: Menu },
|
|
96
|
-
dashboard: { emoji: '📊', component: Dashboard },
|
|
97
|
-
|
|
98
|
-
// Information & Help
|
|
99
|
-
info: { emoji: 'ℹ️', component: Info },
|
|
100
|
-
about: { emoji: 'ℹ️', component: Info },
|
|
101
|
-
help: { emoji: '❓', component: Help },
|
|
102
|
-
book: { emoji: '📖', component: Book },
|
|
103
|
-
|
|
104
|
-
// Communication
|
|
105
|
-
email: { emoji: '📧', component: Email },
|
|
106
|
-
contact: { emoji: '📧', component: Email },
|
|
107
|
-
phone: { emoji: '📱', component: Phone },
|
|
108
|
-
send: { emoji: '📤', component: Send },
|
|
109
|
-
|
|
110
|
-
// Actions
|
|
117
|
+
// === Actions ===
|
|
111
118
|
add: { emoji: '➕', component: Add },
|
|
112
|
-
edit: { emoji: '✏️', component: Edit },
|
|
113
|
-
delete: { emoji: '🗑️', component: Delete },
|
|
114
|
-
save: { emoji: '💾', component: Save },
|
|
115
119
|
check: { emoji: '✓', component: Check },
|
|
120
|
+
check_circle: { emoji: '✅', component: CheckCircle },
|
|
116
121
|
close: { emoji: '✕', component: Close },
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// Content
|
|
122
|
+
delete: { emoji: '🗑️', component: Delete },
|
|
123
|
+
edit: { emoji: '✏️', component: Edit },
|
|
124
|
+
refresh: { emoji: '🔄', component: Refresh },
|
|
125
|
+
rotate_right: { emoji: '🔄', component: RotateRight },
|
|
126
|
+
save: { emoji: '💾', component: Save },
|
|
123
127
|
search: { emoji: '🔍', component: Search },
|
|
128
|
+
send: { emoji: '📤', component: Send },
|
|
124
129
|
share: { emoji: '🔗', component: Share },
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
person: { emoji: '👤', component: Person },
|
|
131
|
-
user: { emoji: '👤', component: Person },
|
|
132
|
-
group: { emoji: '👥', component: Group },
|
|
133
|
-
favorite: { emoji: '❤️', component: Favorite },
|
|
134
|
-
star: { emoji: '⭐', component: Star },
|
|
135
|
-
verified_user: { emoji: '✅', component: VerifiedUser },
|
|
136
|
-
|
|
137
|
-
// Business
|
|
138
|
-
business: { emoji: '🏢', component: Business },
|
|
139
|
-
shoppingcart: { emoji: '🛒', component: ShoppingCart },
|
|
140
|
-
cart: { emoji: '🛒', component: ShoppingCart },
|
|
141
|
-
attachmoney: { emoji: '💰', component: AttachMoney },
|
|
142
|
-
|
|
143
|
-
// Security
|
|
130
|
+
sync: { emoji: '🔄', component: Sync },
|
|
131
|
+
|
|
132
|
+
// === Authentication & Security ===
|
|
133
|
+
block: { emoji: '🚫', component: Block },
|
|
134
|
+
key: { emoji: '🔑', component: Key },
|
|
144
135
|
lock: { emoji: '🔒', component: Lock },
|
|
145
|
-
|
|
136
|
+
lock_open: { emoji: '🔓', component: LockOpen },
|
|
137
|
+
lockopen: { emoji: '🔓', component: LockOpen }, // alias
|
|
138
|
+
login: { emoji: '🔑', component: Login },
|
|
139
|
+
logout: { emoji: '🚪', component: Logout },
|
|
140
|
+
security: { emoji: '🔐', component: Security },
|
|
141
|
+
shield: { emoji: '🛡️', component: Shield },
|
|
142
|
+
verified_user: { emoji: '✅', component: VerifiedUser },
|
|
146
143
|
visibility: { emoji: '👁️', component: Visibility },
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
144
|
+
visibility_off: { emoji: '🙈', component: VisibilityOff },
|
|
145
|
+
visibilityoff: { emoji: '🙈', component: VisibilityOff }, // alias
|
|
146
|
+
vpn_key: { emoji: '🔐', component: VpnKey },
|
|
147
|
+
|
|
148
|
+
// === Business & Commerce ===
|
|
149
|
+
attach_money: { emoji: '💰', component: AttachMoney },
|
|
150
|
+
attachmoney: { emoji: '💰', component: AttachMoney }, // alias
|
|
151
|
+
business: { emoji: '🏢', component: Business },
|
|
152
|
+
cart: { emoji: '🛒', component: ShoppingCart }, // alias
|
|
153
|
+
shopping_cart: { emoji: '🛒', component: ShoppingCart },
|
|
154
|
+
shoppingcart: { emoji: '🛒', component: ShoppingCart }, // alias
|
|
155
|
+
work: { emoji: '💼', component: Work },
|
|
156
|
+
workspace_premium: { emoji: '⭐', component: WorkspacePremium },
|
|
157
|
+
|
|
158
|
+
// === Communication ===
|
|
159
|
+
contact: { emoji: '📧', component: Email }, // alias
|
|
160
|
+
email: { emoji: '📧', component: Email },
|
|
161
|
+
mail: { emoji: '📧', component: Email }, // alias
|
|
162
|
+
notifications: { emoji: '🔔', component: Notifications },
|
|
163
|
+
phone: { emoji: '📱', component: Phone },
|
|
164
164
|
support_agent: { emoji: '🛎️', component: SupportAgent },
|
|
165
|
-
tune: { emoji: '🎛️', component: Tune },
|
|
166
165
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
// === Content & Media ===
|
|
167
|
+
article: { emoji: '📰', component: Article },
|
|
168
|
+
blog: { emoji: '📝', component: Book }, // alias
|
|
169
|
+
book: { emoji: '📖', component: Book },
|
|
170
|
+
gallery: { emoji: '🖼️', component: InsertPhoto }, // alias
|
|
171
|
+
image: { emoji: '🖼️', component: InsertPhoto },
|
|
172
|
+
insert_photo: { emoji: '🖼️', component: InsertPhoto },
|
|
173
|
+
library_books: { emoji: '📚', component: LibraryBooks },
|
|
174
|
+
news: { emoji: '📰', component: Article }, // alias
|
|
175
|
+
photo_library: { emoji: '📸', component: PhotoLibrary },
|
|
176
|
+
play: { emoji: '▶️', component: PlayArrow }, // alias
|
|
177
|
+
play_arrow: { emoji: '▶️', component: PlayArrow },
|
|
178
|
+
portfolio: { emoji: '💼', component: Business }, // alias
|
|
179
|
+
|
|
180
|
+
// === Development & Technology ===
|
|
181
|
+
architecture: { emoji: '🏛️', component: Architecture },
|
|
170
182
|
autorenew: { emoji: '🔄', component: Autorenew },
|
|
171
|
-
|
|
183
|
+
cloud: { emoji: '☁️', component: Cloud },
|
|
184
|
+
cloud_download: { emoji: '☁️⬇️', component: CloudDownload },
|
|
185
|
+
cloud_upload: { emoji: '☁️⬆️', component: CloudUpload },
|
|
186
|
+
clouddownload: { emoji: '☁️⬇️', component: CloudDownload }, // alias
|
|
187
|
+
cloudupload: { emoji: '☁️⬆️', component: CloudUpload }, // alias
|
|
188
|
+
code: { emoji: '💻', component: Code },
|
|
189
|
+
computer: { emoji: '💻', component: Computer },
|
|
172
190
|
construction: { emoji: '🚧', component: Construction },
|
|
173
|
-
|
|
191
|
+
integration_instructions: { emoji: '🔌', component: IntegrationInstructions },
|
|
192
|
+
memory: { emoji: '🧠', component: Memory },
|
|
193
|
+
psychology: { emoji: '🧠', component: Psychology },
|
|
194
|
+
rocket: { emoji: '🚀', component: Rocket },
|
|
195
|
+
storage: { emoji: '💾', component: Storage },
|
|
196
|
+
|
|
197
|
+
// === Navigation & Layout ===
|
|
198
|
+
arrow_back: { emoji: '←', component: ArrowBack },
|
|
199
|
+
arrow_forward: { emoji: '→', component: ArrowForward },
|
|
200
|
+
arrowback: { emoji: '←', component: ArrowBack }, // alias
|
|
201
|
+
arrowforward: { emoji: '→', component: ArrowForward }, // alias
|
|
202
|
+
dashboard: { emoji: '📊', component: Dashboard },
|
|
203
|
+
download: { emoji: '⬇️', component: Download },
|
|
204
|
+
explore: { emoji: '🧭', component: Explore },
|
|
205
|
+
home: { emoji: '🏠', component: Home },
|
|
174
206
|
layers: { emoji: '📚', component: Layers },
|
|
175
|
-
|
|
207
|
+
menu: { emoji: '☰', component: Menu },
|
|
176
208
|
route: { emoji: '🗺️', component: Route },
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
209
|
+
settings: { emoji: '⚙️', component: Settings },
|
|
210
|
+
trending_up: { emoji: '📈', component: TrendingUp },
|
|
211
|
+
tune: { emoji: '🎛️', component: Tune },
|
|
180
212
|
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
213
|
+
// === Products & Inventory ===
|
|
214
|
+
inventory: { emoji: '📦', component: Inventory },
|
|
215
|
+
inventory_2: { emoji: '📦', component: Inventory2 },
|
|
216
|
+
local_offer: { emoji: '🏷️', component: LocalOffer },
|
|
217
|
+
products: { emoji: '🛍️', component: ShoppingCart }, // alias
|
|
218
|
+
services: { emoji: '⚙️', component: Settings }, // alias
|
|
219
|
+
speed: { emoji: '⚡', component: Speed },
|
|
220
|
+
|
|
221
|
+
// === Status & Feedback ===
|
|
222
|
+
about: { emoji: 'ℹ️', component: Info }, // alias
|
|
223
|
+
favorite: { emoji: '❤️', component: Favorite },
|
|
224
|
+
heart: { emoji: '❤️', component: Favorite }, // alias
|
|
225
|
+
help: { emoji: '❓', component: Help },
|
|
226
|
+
info: { emoji: 'ℹ️', component: Info },
|
|
227
|
+
star: { emoji: '⭐', component: Star },
|
|
228
|
+
|
|
229
|
+
// === Users & People ===
|
|
230
|
+
account_circle: { emoji: '👤', component: AccountCircle },
|
|
231
|
+
group: { emoji: '👥', component: Group },
|
|
232
|
+
manage_accounts: { emoji: '👤', component: ManageAccounts },
|
|
233
|
+
people: { emoji: '👥', component: People },
|
|
234
|
+
person: { emoji: '👤', component: Person },
|
|
184
235
|
person_search: { emoji: '🔍', component: PersonSearch },
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
refresh: { emoji: '🔄', component: Refresh },
|
|
188
|
-
block: { emoji: '🚫', component: Block },
|
|
189
|
-
check_circle: { emoji: '✅', component: CheckCircle },
|
|
190
|
-
rotate_right: { emoji: '🔄', component: RotateRight },
|
|
191
|
-
memory: { emoji: '🧠', component: Memory },
|
|
236
|
+
user: { emoji: '👤', component: Person }, // alias
|
|
237
|
+
users: { emoji: '👥', component: People }, // alias
|
|
192
238
|
};
|
|
193
239
|
|
|
194
240
|
/**
|
|
195
241
|
* Get emoji representation of an icon
|
|
196
|
-
* @param iconName - Icon name (case-insensitive)
|
|
242
|
+
* @param iconName - Icon name (case-insensitive, supports snake_case)
|
|
197
243
|
* @param fallback - Fallback emoji if icon not found (default: 🔗)
|
|
198
244
|
* @returns Emoji string
|
|
199
245
|
*/
|
|
200
246
|
export function getIconEmoji(iconName: string | undefined, fallback: string = '🔗'): string {
|
|
201
247
|
if (!iconName) return fallback;
|
|
202
|
-
const
|
|
203
|
-
|
|
248
|
+
const normalized = iconName.toLowerCase();
|
|
249
|
+
const mapping = iconMap[normalized];
|
|
250
|
+
return mapping?.emoji || fallback;
|
|
204
251
|
}
|
|
205
252
|
|
|
206
253
|
/**
|
|
207
|
-
* Get Material-UI component representation of an icon
|
|
208
|
-
*
|
|
209
|
-
*
|
|
254
|
+
* Get Material-UI component representation of an icon.
|
|
255
|
+
*
|
|
256
|
+
* Uses the static iconMap for known icons. For unmapped icons,
|
|
257
|
+
* returns a HelpOutline fallback and logs a warning.
|
|
258
|
+
*
|
|
259
|
+
* @param iconName - Icon name (case-insensitive, supports snake_case)
|
|
260
|
+
* @returns React element (mapped icon or HelpOutline fallback), or null if no name provided
|
|
210
261
|
*/
|
|
211
262
|
export function getIconComponent(iconName: string | undefined): React.ReactElement | null {
|
|
212
263
|
if (!iconName) return null;
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
264
|
+
|
|
265
|
+
const normalized = iconName.toLowerCase();
|
|
266
|
+
const mapping = iconMap[normalized];
|
|
267
|
+
|
|
268
|
+
if (mapping?.component) {
|
|
269
|
+
const IconComponent = mapping.component;
|
|
270
|
+
return <IconComponent />;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Fallback: Return HelpOutline icon and warn about unmapped icon
|
|
274
|
+
// Use registerIcon() to add app-specific icons at runtime
|
|
275
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
276
|
+
console.warn(`[IconMap] Icon "${iconName}" not found. Add it to iconMap or use registerIcon().`);
|
|
218
277
|
}
|
|
219
|
-
|
|
220
|
-
const IconComponent = mapping.component;
|
|
221
|
-
return <IconComponent />;
|
|
278
|
+
return <HelpOutline />;
|
|
222
279
|
}
|
|
223
280
|
|
|
224
281
|
/**
|
|
@@ -230,14 +287,15 @@ export function registerIcon(name: string, mapping: IconMapping): void {
|
|
|
230
287
|
}
|
|
231
288
|
|
|
232
289
|
/**
|
|
233
|
-
* Check if an icon is registered
|
|
290
|
+
* Check if an icon is registered in the static map
|
|
291
|
+
* If false, getIconComponent will return HelpOutline fallback
|
|
234
292
|
*/
|
|
235
293
|
export function hasIcon(iconName: string): boolean {
|
|
236
294
|
return iconName.toLowerCase() in iconMap;
|
|
237
295
|
}
|
|
238
296
|
|
|
239
297
|
/**
|
|
240
|
-
* Get all registered icon names
|
|
298
|
+
* Get all registered icon names from the static map
|
|
241
299
|
*/
|
|
242
300
|
export function getRegisteredIcons(): string[] {
|
|
243
301
|
return Object.keys(iconMap);
|
package/src/utils/reactUtils.tsx
CHANGED
|
@@ -7,55 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import { ReactNode } from 'react';
|
|
9
9
|
|
|
10
|
-
/** Location type for routing */
|
|
11
|
-
type LocationType = { pathname: string, search: string, hash: string };
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Get the current location from browser or undefined in SSR
|
|
15
|
-
* Works without relying on routing libraries
|
|
16
|
-
* @returns {object | undefined} - The location object if in browser, otherwise undefined
|
|
17
|
-
*/
|
|
18
|
-
export const useSafeLocation = (): LocationType | undefined => {
|
|
19
|
-
// Use browser location if available (works in any React app)
|
|
20
|
-
if (typeof window !== 'undefined') {
|
|
21
|
-
return {
|
|
22
|
-
pathname: window.location.pathname,
|
|
23
|
-
search: window.location.search,
|
|
24
|
-
hash: window.location.hash
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
return undefined;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Navigate function type
|
|
32
|
-
*/
|
|
33
|
-
export type NavigateFunction = (to: string | number, options?: unknown) => void;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get a navigation function with fallback to window.location
|
|
37
|
-
* Works without relying on routing libraries
|
|
38
|
-
* @returns {NavigateFunction} - The navigate function
|
|
39
|
-
*/
|
|
40
|
-
export const useSafeNavigate = (): NavigateFunction => {
|
|
41
|
-
// Return function that uses window.location
|
|
42
|
-
return (to: string | number) => {
|
|
43
|
-
if (typeof to === 'string') {
|
|
44
|
-
if (typeof window !== 'undefined') {
|
|
45
|
-
window.location.href = to;
|
|
46
|
-
}
|
|
47
|
-
} else if (typeof to === 'number') {
|
|
48
|
-
if (typeof window !== 'undefined' && window.history) {
|
|
49
|
-
window.history.go(to);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
|
|
55
10
|
/**
|
|
56
11
|
* Extract text content from ReactNode for code processing
|
|
57
12
|
* Handles natural React usage like <Code>const x = 1;</Code>
|
|
58
|
-
*
|
|
13
|
+
*
|
|
59
14
|
* @param node - The ReactNode to extract text from
|
|
60
15
|
* @returns String representation of the node's text content
|
|
61
16
|
*/
|
|
@@ -73,7 +28,7 @@ export function extractTextFromReactNode(node: ReactNode): string {
|
|
|
73
28
|
}
|
|
74
29
|
|
|
75
30
|
if (Array.isArray(node)) {
|
|
76
|
-
return node.map(child => extractTextFromReactNode(child)).join('');
|
|
31
|
+
return node.map((child) => extractTextFromReactNode(child)).join('');
|
|
77
32
|
}
|
|
78
33
|
|
|
79
34
|
// For React elements, try to extract text content
|