@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.
Files changed (125) hide show
  1. package/dist/components/QwickApp.d.ts.map +1 -1
  2. package/dist/components/ResponsiveMenu.d.ts.map +1 -1
  3. package/dist/components/Scaffold.d.ts.map +1 -1
  4. package/dist/components/index.d.ts +1 -0
  5. package/dist/components/index.d.ts.map +1 -1
  6. package/dist/contexts/NavigationContext.d.ts +64 -0
  7. package/dist/contexts/NavigationContext.d.ts.map +1 -0
  8. package/dist/contexts/index.d.ts +2 -0
  9. package/dist/contexts/index.d.ts.map +1 -1
  10. package/dist/hooks/useBaseProps.d.ts +12 -1161
  11. package/dist/hooks/useBaseProps.d.ts.map +1 -1
  12. package/dist/index.esm.js +514 -272
  13. package/dist/index.js +514 -272
  14. package/dist/palettes/manifest.json +19 -19
  15. package/dist/palettes/palette-autumn.1.4.9.css +172 -0
  16. package/dist/palettes/palette-autumn.1.4.9.min.css +1 -0
  17. package/dist/palettes/palette-autumn.1.5.0.css +172 -0
  18. package/dist/palettes/palette-autumn.1.5.0.min.css +1 -0
  19. package/dist/palettes/palette-autumn.1.5.1.css +172 -0
  20. package/dist/palettes/palette-autumn.1.5.1.min.css +1 -0
  21. package/dist/palettes/palette-autumn.1.5.2.css +172 -0
  22. package/dist/palettes/palette-autumn.1.5.2.min.css +1 -0
  23. package/dist/palettes/palette-autumn.1.5.3.css +172 -0
  24. package/dist/palettes/palette-autumn.1.5.3.min.css +1 -0
  25. package/dist/palettes/palette-autumn.1.5.4.css +172 -0
  26. package/dist/palettes/palette-autumn.1.5.4.min.css +1 -0
  27. package/dist/palettes/palette-autumn.1.5.6.css +172 -0
  28. package/dist/palettes/palette-autumn.1.5.6.min.css +1 -0
  29. package/dist/palettes/palette-autumn.1.5.7.css +172 -0
  30. package/dist/palettes/palette-autumn.1.5.7.min.css +1 -0
  31. package/dist/palettes/palette-cosmic.1.4.9.css +172 -0
  32. package/dist/palettes/palette-cosmic.1.4.9.min.css +1 -0
  33. package/dist/palettes/palette-cosmic.1.5.0.css +172 -0
  34. package/dist/palettes/palette-cosmic.1.5.0.min.css +1 -0
  35. package/dist/palettes/palette-cosmic.1.5.1.css +172 -0
  36. package/dist/palettes/palette-cosmic.1.5.1.min.css +1 -0
  37. package/dist/palettes/palette-cosmic.1.5.2.css +172 -0
  38. package/dist/palettes/palette-cosmic.1.5.2.min.css +1 -0
  39. package/dist/palettes/palette-cosmic.1.5.3.css +172 -0
  40. package/dist/palettes/palette-cosmic.1.5.3.min.css +1 -0
  41. package/dist/palettes/palette-cosmic.1.5.4.css +172 -0
  42. package/dist/palettes/palette-cosmic.1.5.4.min.css +1 -0
  43. package/dist/palettes/palette-cosmic.1.5.6.css +172 -0
  44. package/dist/palettes/palette-cosmic.1.5.6.min.css +1 -0
  45. package/dist/palettes/palette-cosmic.1.5.7.css +172 -0
  46. package/dist/palettes/palette-cosmic.1.5.7.min.css +1 -0
  47. package/dist/palettes/palette-default.1.4.9.css +178 -0
  48. package/dist/palettes/palette-default.1.4.9.min.css +1 -0
  49. package/dist/palettes/palette-default.1.5.0.css +178 -0
  50. package/dist/palettes/palette-default.1.5.0.min.css +1 -0
  51. package/dist/palettes/palette-default.1.5.1.css +178 -0
  52. package/dist/palettes/palette-default.1.5.1.min.css +1 -0
  53. package/dist/palettes/palette-default.1.5.2.css +178 -0
  54. package/dist/palettes/palette-default.1.5.2.min.css +1 -0
  55. package/dist/palettes/palette-default.1.5.3.css +178 -0
  56. package/dist/palettes/palette-default.1.5.3.min.css +1 -0
  57. package/dist/palettes/palette-default.1.5.4.css +178 -0
  58. package/dist/palettes/palette-default.1.5.4.min.css +1 -0
  59. package/dist/palettes/palette-default.1.5.6.css +178 -0
  60. package/dist/palettes/palette-default.1.5.6.min.css +1 -0
  61. package/dist/palettes/palette-default.1.5.7.css +178 -0
  62. package/dist/palettes/palette-default.1.5.7.min.css +1 -0
  63. package/dist/palettes/palette-ocean.1.4.9.css +172 -0
  64. package/dist/palettes/palette-ocean.1.4.9.min.css +1 -0
  65. package/dist/palettes/palette-ocean.1.5.0.css +172 -0
  66. package/dist/palettes/palette-ocean.1.5.0.min.css +1 -0
  67. package/dist/palettes/palette-ocean.1.5.1.css +172 -0
  68. package/dist/palettes/palette-ocean.1.5.1.min.css +1 -0
  69. package/dist/palettes/palette-ocean.1.5.2.css +172 -0
  70. package/dist/palettes/palette-ocean.1.5.2.min.css +1 -0
  71. package/dist/palettes/palette-ocean.1.5.3.css +172 -0
  72. package/dist/palettes/palette-ocean.1.5.3.min.css +1 -0
  73. package/dist/palettes/palette-ocean.1.5.4.css +172 -0
  74. package/dist/palettes/palette-ocean.1.5.4.min.css +1 -0
  75. package/dist/palettes/palette-ocean.1.5.6.css +172 -0
  76. package/dist/palettes/palette-ocean.1.5.6.min.css +1 -0
  77. package/dist/palettes/palette-ocean.1.5.7.css +172 -0
  78. package/dist/palettes/palette-ocean.1.5.7.min.css +1 -0
  79. package/dist/palettes/palette-spring.1.4.9.css +160 -0
  80. package/dist/palettes/palette-spring.1.4.9.min.css +1 -0
  81. package/dist/palettes/palette-spring.1.5.0.css +160 -0
  82. package/dist/palettes/palette-spring.1.5.0.min.css +1 -0
  83. package/dist/palettes/palette-spring.1.5.1.css +160 -0
  84. package/dist/palettes/palette-spring.1.5.1.min.css +1 -0
  85. package/dist/palettes/palette-spring.1.5.2.css +160 -0
  86. package/dist/palettes/palette-spring.1.5.2.min.css +1 -0
  87. package/dist/palettes/palette-spring.1.5.3.css +166 -0
  88. package/dist/palettes/palette-spring.1.5.3.min.css +1 -0
  89. package/dist/palettes/palette-spring.1.5.4.css +166 -0
  90. package/dist/palettes/palette-spring.1.5.4.min.css +1 -0
  91. package/dist/palettes/palette-spring.1.5.6.css +166 -0
  92. package/dist/palettes/palette-spring.1.5.6.min.css +1 -0
  93. package/dist/palettes/palette-spring.1.5.7.css +166 -0
  94. package/dist/palettes/palette-spring.1.5.7.min.css +1 -0
  95. package/dist/palettes/palette-winter.1.4.9.css +172 -0
  96. package/dist/palettes/palette-winter.1.4.9.min.css +1 -0
  97. package/dist/palettes/palette-winter.1.5.0.css +172 -0
  98. package/dist/palettes/palette-winter.1.5.0.min.css +1 -0
  99. package/dist/palettes/palette-winter.1.5.1.css +172 -0
  100. package/dist/palettes/palette-winter.1.5.1.min.css +1 -0
  101. package/dist/palettes/palette-winter.1.5.2.css +172 -0
  102. package/dist/palettes/palette-winter.1.5.2.min.css +1 -0
  103. package/dist/palettes/palette-winter.1.5.3.css +172 -0
  104. package/dist/palettes/palette-winter.1.5.3.min.css +1 -0
  105. package/dist/palettes/palette-winter.1.5.4.css +172 -0
  106. package/dist/palettes/palette-winter.1.5.4.min.css +1 -0
  107. package/dist/palettes/palette-winter.1.5.6.css +172 -0
  108. package/dist/palettes/palette-winter.1.5.6.min.css +1 -0
  109. package/dist/palettes/palette-winter.1.5.7.css +172 -0
  110. package/dist/palettes/palette-winter.1.5.7.min.css +1 -0
  111. package/dist/utils/iconMap.d.ts +21 -8
  112. package/dist/utils/iconMap.d.ts.map +1 -1
  113. package/dist/utils/reactUtils.d.ts +0 -23
  114. package/dist/utils/reactUtils.d.ts.map +1 -1
  115. package/package.json +1 -1
  116. package/src/__tests__/utils/iconMap.test.tsx +197 -0
  117. package/src/components/QwickApp.tsx +24 -15
  118. package/src/components/ResponsiveMenu.tsx +3 -4
  119. package/src/components/Scaffold.tsx +3 -4
  120. package/src/components/index.ts +1 -0
  121. package/src/components/pages/Page.tsx +2 -2
  122. package/src/contexts/NavigationContext.tsx +168 -0
  123. package/src/contexts/index.ts +2 -0
  124. package/src/utils/iconMap.tsx +209 -151
  125. package/src/utils/reactUtils.tsx +2 -47
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Winter Color Palette
3
+ *
4
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
5
+ *
6
+ * Cool blues, icy whites, and frosty grays - inspired by winter landscapes
7
+ */
8
+
9
+ /* ===== WINTER PALETTE - LIGHT THEME ===== */
10
+ html[data-palette="winter"]:not([data-theme="dark"]),
11
+ html[data-palette="winter"][data-theme="light"] {
12
+ /* Primary palette - Ice blue */
13
+ --palette-primary-main: #0077be;
14
+ --palette-primary-light: #5ba3d0;
15
+ --palette-primary-dark: #005082;
16
+ --palette-on-primary: #ffffff;
17
+
18
+ /* Secondary palette - Steel blue */
19
+ --palette-secondary-main: #4682b4;
20
+ --palette-secondary-light: #7ba7cc;
21
+ --palette-secondary-dark: #2e5984;
22
+ --palette-on-secondary: #ffffff;
23
+
24
+ /* Surface palette - Snow white */
25
+ --palette-surface-main: #fafbfc;
26
+ --palette-surface-variant: #e2e8f0; /* Improved contrast with cooler tone */
27
+ --palette-surface-elevated: #ffffff;
28
+ --palette-on-surface: #0f172a;
29
+
30
+ /* Background palette - Frosty */
31
+ --palette-background-main: #f8fafc;
32
+ --palette-background-dark: #f1f5f9;
33
+ --palette-background-overlay: rgba(248, 250, 252, 0.95);
34
+ --palette-on-background: #475569;
35
+
36
+ /* Header background with transparency */
37
+ --palette-header-bg-start: rgba(248, 250, 252, 0.98);
38
+ --palette-header-bg-end: rgba(248, 250, 252, 0.95);
39
+ --palette-header-collapsed-bg-start: rgba(248, 250, 252, 0.99);
40
+ --palette-header-collapsed-bg-end: rgba(248, 250, 252, 0.96);
41
+
42
+ /* Text palette - Winter tones */
43
+ --palette-text-primary: #0f172a;
44
+ --palette-text-secondary: #475569;
45
+ --palette-text-disabled: rgba(15, 23, 42, 0.38);
46
+ --palette-text-inverted: #ffffff;
47
+
48
+ /* Border palette - Icy */
49
+ --palette-border-main: #cbd5e1;
50
+ --palette-border-light: rgba(15, 23, 42, 0.12);
51
+ --palette-border-lighter: rgba(15, 23, 42, 0.05);
52
+ --palette-border-medium: #94a3b8;
53
+
54
+ /* Success palette - Pine green */
55
+ --palette-success-main: #059669;
56
+ --palette-success-light: #d1fae5;
57
+ --palette-success-dark: #064e3b;
58
+ --palette-success-border: #a7f3d0;
59
+
60
+ /* Error palette - Winter berry */
61
+ --palette-error-main: #dc2626;
62
+ --palette-error-light: #fee2e2;
63
+ --palette-error-dark: #7f1d1d;
64
+ --palette-error-border: #fecaca;
65
+
66
+ /* Warning palette - Amber frost */
67
+ --palette-warning-main: #d97706;
68
+ --palette-warning-light: #fef3c7;
69
+ --palette-warning-dark: #92400e;
70
+ --palette-warning-border: #fde68a;
71
+
72
+ /* Info palette - Arctic blue */
73
+ --palette-info-main: #0284c7;
74
+ --palette-info-light: #e0f2fe;
75
+ --palette-info-dark: #0c4a6e;
76
+ --palette-on-info: #ffffff;
77
+ --palette-info-border: #7dd3fc;
78
+
79
+ /* Accent palette - Aurora colors */
80
+ --palette-accent-main: #ec4899;
81
+ --palette-accent-light: #fce7f3;
82
+ --palette-accent-dark: #be185d;
83
+ --palette-on-accent: #ffffff;
84
+
85
+ /* Control palette - Charcoal */
86
+ --palette-control-main: #1e293b;
87
+ --palette-control-light: #334155;
88
+ --palette-control-text: #e2e8f0;
89
+ --palette-control-border: #475569;
90
+ }
91
+
92
+ /* ===== WINTER PALETTE - DARK THEME ===== */
93
+ html[data-palette="winter"][data-theme="dark"] {
94
+ /* Primary palette - Arctic glow */
95
+ --palette-primary-main: #7dd3fc;
96
+ --palette-primary-light: #bae6fd;
97
+ --palette-primary-dark: #0369a1;
98
+ --palette-on-primary: #020617;
99
+
100
+ /* Secondary palette - Moonlight */
101
+ --palette-secondary-main: #94a3b8;
102
+ --palette-secondary-light: #cbd5e1;
103
+ --palette-secondary-dark: #64748b;
104
+ --palette-on-secondary: #020617;
105
+
106
+ /* Surface palette - Dark frost */
107
+ --palette-surface-main: #172033;
108
+ --palette-surface-variant: #1e293b;
109
+ --palette-surface-elevated: #334155;
110
+ --palette-on-surface: #f8fafc;
111
+
112
+ /* Background palette - Winter night */
113
+ --palette-background-main: #04080f;
114
+ --palette-background-dark: #0a1018;
115
+ --palette-background-overlay: rgba(15, 23, 42, 0.95);
116
+ --palette-on-background: #cbd5e1;
117
+
118
+ /* Header background with transparency */
119
+ --palette-header-bg-start: rgba(4, 8, 15, 0.98);
120
+ --palette-header-bg-end: rgba(4, 8, 15, 0.95);
121
+ --palette-header-collapsed-bg-start: rgba(4, 8, 15, 0.99);
122
+ --palette-header-collapsed-bg-end: rgba(4, 8, 15, 0.96);
123
+
124
+ /* Text palette - Snow and ice */
125
+ --palette-text-primary: #f8fafc;
126
+ --palette-text-secondary: #cbd5e1;
127
+ --palette-text-disabled: rgba(248, 250, 252, 0.38);
128
+ --palette-text-inverted: #020617;
129
+
130
+ /* Border palette - Dark ice */
131
+ --palette-border-main: #475569;
132
+ --palette-border-light: rgba(248, 250, 252, 0.12);
133
+ --palette-border-lighter: rgba(248, 250, 252, 0.05);
134
+ --palette-border-medium: #334155;
135
+
136
+ /* Success palette - Aurora green */
137
+ --palette-success-main: #34d399;
138
+ --palette-success-light: #064e3b;
139
+ --palette-success-dark: #10b981;
140
+ --palette-success-border: #065f46;
141
+
142
+ /* Error palette - Northern lights red */
143
+ --palette-error-main: #f87171;
144
+ --palette-error-light: #7f1d1d;
145
+ --palette-error-dark: #ef4444;
146
+ --palette-error-border: #991b1b;
147
+
148
+ /* Warning palette - Aurora orange */
149
+ --palette-warning-main: #fb923c;
150
+ --palette-warning-light: #9a3412;
151
+ --palette-warning-dark: #f97316;
152
+ --palette-warning-border: #c2410c;
153
+
154
+ /* Info palette - Polar blue */
155
+ --palette-info-main: #38bdf8;
156
+ --palette-info-light: #0c4a6e;
157
+ --palette-info-dark: #0ea5e9;
158
+ --palette-on-info: #020617;
159
+ --palette-info-border: #0284c7;
160
+
161
+ /* Accent palette - Northern lights */
162
+ --palette-accent-main: #f472b6;
163
+ --palette-accent-light: #fce7f3;
164
+ --palette-accent-dark: #db2777;
165
+ --palette-on-accent: #f8fafc;
166
+
167
+ /* Control palette - Midnight */
168
+ --palette-control-main: #1e293b;
169
+ --palette-control-light: #334155;
170
+ --palette-control-text: #e2e8f0;
171
+ --palette-control-border: #475569;
172
+ }
@@ -0,0 +1 @@
1
+ html[data-palette="winter"]:not([data-theme="dark"]),html[data-palette="winter"][data-theme="light"]{--palette-primary-main:#0077be;--palette-primary-light:#5ba3d0;--palette-primary-dark:#005082;--palette-on-primary:#ffffff;--palette-secondary-main:#4682b4;--palette-secondary-light:#7ba7cc;--palette-secondary-dark:#2e5984;--palette-on-secondary:#ffffff;--palette-surface-main:#fafbfc;--palette-surface-variant:#e2e8f0;--palette-surface-elevated:#ffffff;--palette-on-surface:#0f172a;--palette-background-main:#f8fafc;--palette-background-dark:#f1f5f9;--palette-background-overlay:rgba(248,250,252,0.95);--palette-on-background:#475569;--palette-header-bg-start:rgba(248,250,252,0.98);--palette-header-bg-end:rgba(248,250,252,0.95);--palette-header-collapsed-bg-start:rgba(248,250,252,0.99);--palette-header-collapsed-bg-end:rgba(248,250,252,0.96);--palette-text-primary:#0f172a;--palette-text-secondary:#475569;--palette-text-disabled:rgba(15,23,42,0.38);--palette-text-inverted:#ffffff;--palette-border-main:#cbd5e1;--palette-border-light:rgba(15,23,42,0.12);--palette-border-lighter:rgba(15,23,42,0.05);--palette-border-medium:#94a3b8;--palette-success-main:#059669;--palette-success-light:#d1fae5;--palette-success-dark:#064e3b;--palette-success-border:#a7f3d0;--palette-error-main:#dc2626;--palette-error-light:#fee2e2;--palette-error-dark:#7f1d1d;--palette-error-border:#fecaca;--palette-warning-main:#d97706;--palette-warning-light:#fef3c7;--palette-warning-dark:#92400e;--palette-warning-border:#fde68a;--palette-info-main:#0284c7;--palette-info-light:#e0f2fe;--palette-info-dark:#0c4a6e;--palette-on-info:#ffffff;--palette-info-border:#7dd3fc;--palette-accent-main:#ec4899;--palette-accent-light:#fce7f3;--palette-accent-dark:#be185d;--palette-on-accent:#ffffff;--palette-control-main:#1e293b;--palette-control-light:#334155;--palette-control-text:#e2e8f0;--palette-control-border:#475569}html[data-palette="winter"][data-theme="dark"]{--palette-primary-main:#7dd3fc;--palette-primary-light:#bae6fd;--palette-primary-dark:#0369a1;--palette-on-primary:#020617;--palette-secondary-main:#94a3b8;--palette-secondary-light:#cbd5e1;--palette-secondary-dark:#64748b;--palette-on-secondary:#020617;--palette-surface-main:#172033;--palette-surface-variant:#1e293b;--palette-surface-elevated:#334155;--palette-on-surface:#f8fafc;--palette-background-main:#04080f;--palette-background-dark:#0a1018;--palette-background-overlay:rgba(15,23,42,0.95);--palette-on-background:#cbd5e1;--palette-header-bg-start:rgba(4,8,15,0.98);--palette-header-bg-end:rgba(4,8,15,0.95);--palette-header-collapsed-bg-start:rgba(4,8,15,0.99);--palette-header-collapsed-bg-end:rgba(4,8,15,0.96);--palette-text-primary:#f8fafc;--palette-text-secondary:#cbd5e1;--palette-text-disabled:rgba(248,250,252,0.38);--palette-text-inverted:#020617;--palette-border-main:#475569;--palette-border-light:rgba(248,250,252,0.12);--palette-border-lighter:rgba(248,250,252,0.05);--palette-border-medium:#334155;--palette-success-main:#34d399;--palette-success-light:#064e3b;--palette-success-dark:#10b981;--palette-success-border:#065f46;--palette-error-main:#f87171;--palette-error-light:#7f1d1d;--palette-error-dark:#ef4444;--palette-error-border:#991b1b;--palette-warning-main:#fb923c;--palette-warning-light:#9a3412;--palette-warning-dark:#f97316;--palette-warning-border:#c2410c;--palette-info-main:#38bdf8;--palette-info-light:#0c4a6e;--palette-info-dark:#0ea5e9;--palette-on-info:#020617;--palette-info-border:#0284c7;--palette-accent-main:#f472b6;--palette-accent-light:#fce7f3;--palette-accent-dark:#db2777;--palette-on-accent:#f8fafc;--palette-control-main:#1e293b;--palette-control-light:#334155;--palette-control-text:#e2e8f0;--palette-control-border:#475569}
@@ -4,6 +4,11 @@
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
  import React from 'react';
@@ -15,21 +20,28 @@ export interface IconMapping {
15
20
  component: React.ComponentType;
16
21
  }
17
22
  /**
18
- * Centralized icon registry mapping icon names to their representations
19
- * Supports both Material-UI components and emoji for different contexts
23
+ * Centralized icon registry mapping icon names to their representations.
24
+ * Sorted alphabetically by category, then by key within each category.
25
+ *
26
+ * For icons not in this map, getIconComponent() will return a HelpOutline fallback
27
+ * and log a warning. Use registerIcon() to add app-specific icons at runtime.
20
28
  */
21
29
  export declare const iconMap: Record<string, IconMapping>;
22
30
  /**
23
31
  * Get emoji representation of an icon
24
- * @param iconName - Icon name (case-insensitive)
32
+ * @param iconName - Icon name (case-insensitive, supports snake_case)
25
33
  * @param fallback - Fallback emoji if icon not found (default: 🔗)
26
34
  * @returns Emoji string
27
35
  */
28
36
  export declare function getIconEmoji(iconName: string | undefined, fallback?: string): string;
29
37
  /**
30
- * Get Material-UI component representation of an icon
31
- * @param iconName - Icon name (case-insensitive)
32
- * @returns React element or null if not found
38
+ * Get Material-UI component representation of an icon.
39
+ *
40
+ * Uses the static iconMap for known icons. For unmapped icons,
41
+ * returns a HelpOutline fallback and logs a warning.
42
+ *
43
+ * @param iconName - Icon name (case-insensitive, supports snake_case)
44
+ * @returns React element (mapped icon or HelpOutline fallback), or null if no name provided
33
45
  */
34
46
  export declare function getIconComponent(iconName: string | undefined): React.ReactElement | null;
35
47
  /**
@@ -38,11 +50,12 @@ export declare function getIconComponent(iconName: string | undefined): React.Re
38
50
  */
39
51
  export declare function registerIcon(name: string, mapping: IconMapping): void;
40
52
  /**
41
- * Check if an icon is registered
53
+ * Check if an icon is registered in the static map
54
+ * If false, getIconComponent will return HelpOutline fallback
42
55
  */
43
56
  export declare function hasIcon(iconName: string): boolean;
44
57
  /**
45
- * Get all registered icon names
58
+ * Get all registered icon names from the static map
46
59
  */
47
60
  export declare function getRegisteredIcons(): string[];
48
61
  //# sourceMappingURL=iconMap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"iconMap.d.ts","sourceRoot":"","sources":["../../src/utils/iconMap.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAsE1B;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC;CAChC;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAoG/C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,GAAE,MAAa,GAAG,MAAM,CAI1F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAWxF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAErE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C"}
1
+ {"version":3,"file":"iconMap.d.ts","sourceRoot":"","sources":["../../src/utils/iconMap.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAsF1B;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC;CAChC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CA0H/C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,GAAE,MAAa,GAAG,MAAM,CAK1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAiBxF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAErE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C"}
@@ -5,28 +5,6 @@
5
5
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
6
6
  */
7
7
  import { ReactNode } from 'react';
8
- /** Location type for routing */
9
- type LocationType = {
10
- pathname: string;
11
- search: string;
12
- hash: string;
13
- };
14
- /**
15
- * Get the current location from browser or undefined in SSR
16
- * Works without relying on routing libraries
17
- * @returns {object | undefined} - The location object if in browser, otherwise undefined
18
- */
19
- export declare const useSafeLocation: () => LocationType | undefined;
20
- /**
21
- * Navigate function type
22
- */
23
- export type NavigateFunction = (to: string | number, options?: unknown) => void;
24
- /**
25
- * Get a navigation function with fallback to window.location
26
- * Works without relying on routing libraries
27
- * @returns {NavigateFunction} - The navigate function
28
- */
29
- export declare const useSafeNavigate: () => NavigateFunction;
30
8
  /**
31
9
  * Extract text content from ReactNode for code processing
32
10
  * Handles natural React usage like <Code>const x = 1;</Code>
@@ -35,5 +13,4 @@ export declare const useSafeNavigate: () => NavigateFunction;
35
13
  * @returns String representation of the node's text content
36
14
  */
37
15
  export declare function extractTextFromReactNode(node: ReactNode): string;
38
- export {};
39
16
  //# sourceMappingURL=reactUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reactUtils.d.ts","sourceRoot":"","sources":["../../src/utils/reactUtils.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,gCAAgC;AAChC,KAAK,YAAY,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAAO,YAAY,GAAG,SAUjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAAO,gBAalC,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CA2BhE"}
1
+ {"version":3,"file":"reactUtils.d.ts","sourceRoot":"","sources":["../../src/utils/reactUtils.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CA2BhE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwickapps/react-framework",
3
- "version": "1.5.5",
3
+ "version": "1.5.7",
4
4
  "type": "module",
5
5
  "description": "Complete React framework with responsive navigation, flexible layouts, theming system, and reusable components for building modern applications.",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Icon Map Tests
3
+ *
4
+ * Tests for the icon mapping utility functions.
5
+ */
6
+
7
+ import {
8
+ getIconComponent,
9
+ getIconEmoji,
10
+ hasIcon,
11
+ registerIcon,
12
+ getRegisteredIcons,
13
+ iconMap,
14
+ } from '../../utils/iconMap';
15
+ import { Home, Star } from '@mui/icons-material';
16
+
17
+ describe('iconMap', () => {
18
+ describe('getIconComponent', () => {
19
+ it('returns null for undefined input', () => {
20
+ expect(getIconComponent(undefined)).toBeNull();
21
+ });
22
+
23
+ it('returns null for empty string', () => {
24
+ expect(getIconComponent('')).toBeNull();
25
+ });
26
+
27
+ it('returns correct component for mapped icon', () => {
28
+ const result = getIconComponent('home');
29
+ expect(result).not.toBeNull();
30
+ expect(result?.type).toBe(Home);
31
+ });
32
+
33
+ it('is case-insensitive', () => {
34
+ const lower = getIconComponent('home');
35
+ const upper = getIconComponent('HOME');
36
+ const mixed = getIconComponent('Home');
37
+
38
+ expect(lower?.type).toBe(upper?.type);
39
+ expect(lower?.type).toBe(mixed?.type);
40
+ });
41
+
42
+ it('supports snake_case icon names', () => {
43
+ const result = getIconComponent('manage_accounts');
44
+ expect(result).not.toBeNull();
45
+ });
46
+
47
+ it('returns HelpOutline fallback for unmapped icons', () => {
48
+ // Suppress console.warn for this test
49
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
50
+
51
+ const result = getIconComponent('nonexistent_icon_xyz');
52
+ expect(result).not.toBeNull();
53
+
54
+ warnSpy.mockRestore();
55
+ });
56
+
57
+ it('logs warning for unmapped icons in non-production', () => {
58
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
59
+
60
+ getIconComponent('nonexistent_icon_xyz');
61
+
62
+ expect(warnSpy).toHaveBeenCalledWith(
63
+ expect.stringContaining('nonexistent_icon_xyz')
64
+ );
65
+
66
+ warnSpy.mockRestore();
67
+ });
68
+ });
69
+
70
+ describe('getIconEmoji', () => {
71
+ it('returns fallback for undefined input', () => {
72
+ expect(getIconEmoji(undefined)).toBe('🔗');
73
+ });
74
+
75
+ it('returns custom fallback when provided', () => {
76
+ expect(getIconEmoji(undefined, '❓')).toBe('❓');
77
+ });
78
+
79
+ it('returns correct emoji for mapped icon', () => {
80
+ expect(getIconEmoji('home')).toBe('🏠');
81
+ expect(getIconEmoji('favorite')).toBe('❤️');
82
+ expect(getIconEmoji('star')).toBe('⭐');
83
+ });
84
+
85
+ it('is case-insensitive', () => {
86
+ expect(getIconEmoji('HOME')).toBe(getIconEmoji('home'));
87
+ });
88
+
89
+ it('returns fallback for unmapped icons', () => {
90
+ expect(getIconEmoji('nonexistent_icon')).toBe('🔗');
91
+ });
92
+ });
93
+
94
+ describe('hasIcon', () => {
95
+ it('returns true for mapped icons', () => {
96
+ expect(hasIcon('home')).toBe(true);
97
+ expect(hasIcon('settings')).toBe(true);
98
+ expect(hasIcon('people')).toBe(true);
99
+ });
100
+
101
+ it('returns false for unmapped icons', () => {
102
+ expect(hasIcon('nonexistent_icon')).toBe(false);
103
+ });
104
+
105
+ it('is case-insensitive', () => {
106
+ expect(hasIcon('HOME')).toBe(true);
107
+ expect(hasIcon('Home')).toBe(true);
108
+ });
109
+ });
110
+
111
+ describe('registerIcon', () => {
112
+ // Cleanup any test icons after each test to prevent pollution
113
+ afterEach(() => {
114
+ delete iconMap['custom_test_icon'];
115
+ delete iconMap['upper_case_icon'];
116
+ });
117
+
118
+ it('registers new icons at runtime', () => {
119
+ const customIconName = 'custom_test_icon';
120
+
121
+ // Should not exist initially
122
+ expect(hasIcon(customIconName)).toBe(false);
123
+
124
+ // Register it
125
+ registerIcon(customIconName, {
126
+ emoji: '🎯',
127
+ component: Star,
128
+ });
129
+
130
+ // Should exist now
131
+ expect(hasIcon(customIconName)).toBe(true);
132
+ expect(getIconEmoji(customIconName)).toBe('🎯');
133
+ });
134
+
135
+ it('is case-insensitive for registration', () => {
136
+ registerIcon('UPPER_CASE_ICON', {
137
+ emoji: '🔤',
138
+ component: Star,
139
+ });
140
+
141
+ expect(hasIcon('upper_case_icon')).toBe(true);
142
+ expect(hasIcon('UPPER_CASE_ICON')).toBe(true);
143
+ });
144
+ });
145
+
146
+ describe('getRegisteredIcons', () => {
147
+ it('returns an array of icon names', () => {
148
+ const icons = getRegisteredIcons();
149
+
150
+ expect(Array.isArray(icons)).toBe(true);
151
+ expect(icons.length).toBeGreaterThan(0);
152
+ });
153
+
154
+ it('includes common icons', () => {
155
+ const icons = getRegisteredIcons();
156
+
157
+ expect(icons).toContain('home');
158
+ expect(icons).toContain('settings');
159
+ expect(icons).toContain('dashboard');
160
+ expect(icons).toContain('people');
161
+ expect(icons).toContain('manage_accounts');
162
+ });
163
+ });
164
+
165
+ describe('iconMap coverage', () => {
166
+ it('has emoji and component for all entries', () => {
167
+ for (const [name, mapping] of Object.entries(iconMap)) {
168
+ expect(mapping.emoji).toBeDefined();
169
+ expect(typeof mapping.emoji).toBe('string');
170
+ expect(mapping.emoji.length).toBeGreaterThan(0);
171
+
172
+ expect(mapping.component).toBeDefined();
173
+ // Components can be functions or objects (React.memo, forwardRef, etc.)
174
+ expect(['function', 'object']).toContain(typeof mapping.component);
175
+ }
176
+ });
177
+
178
+ it('includes essential icons for navigation', () => {
179
+ const essentialIcons = [
180
+ 'home', 'dashboard', 'settings', 'menu',
181
+ 'people', 'person', 'help', 'info',
182
+ ];
183
+
184
+ for (const icon of essentialIcons) {
185
+ expect(hasIcon(icon)).toBe(true);
186
+ }
187
+ });
188
+
189
+ it('includes authentication icons', () => {
190
+ const authIcons = ['lock', 'security', 'key', 'login', 'logout'];
191
+
192
+ for (const icon of authIcons) {
193
+ expect(hasIcon(icon)).toBe(true);
194
+ }
195
+ });
196
+ });
197
+ });
@@ -34,8 +34,8 @@
34
34
  *
35
35
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
36
36
  */
37
- import React, { cloneElement, useState } from 'react';
38
- import { DataProvider, ThemeProvider, PrintModeProvider, type ThemeMode } from '../contexts';
37
+ import React, { cloneElement, useState, useEffect } from 'react';
38
+ import { DataProvider, ThemeProvider, PrintModeProvider, NavigationProvider, type ThemeMode } from '../contexts';
39
39
  import { QwickAppContext, type QwickAppContextValue, type QwickAppProps } from '../contexts/QwickAppContext';
40
40
  import { type TemplateResolverConfig } from '../types';
41
41
  import { AppConfig } from '../config';
@@ -126,6 +126,13 @@ export const QwickApp: React.FC<QwickAppComponentProps> = ({
126
126
  setAppConfig(prev => ({ ...prev, ...updates } as typeof prev));
127
127
  };
128
128
 
129
+ // Sync logo prop changes with internal state (for dynamic logo updates)
130
+ useEffect(() => {
131
+ if (resolvedConfig.logo !== appConfig.logo) {
132
+ setAppConfig(prev => ({ ...prev, logo: resolvedConfig.logo }));
133
+ }
134
+ }, [resolvedConfig.logo]);
135
+
129
136
  const contextValue: QwickAppContextValue = {
130
137
  appName: resolvedConfig.appName!, // Safe to use ! since we validated above
131
138
  appId: resolvedConfig.appId,
@@ -160,19 +167,21 @@ export const QwickApp: React.FC<QwickAppComponentProps> = ({
160
167
  const appContent = (
161
168
  <ErrorBoundary>
162
169
  <AccessibilityProvider>
163
- <div className={`qwick-app ${className || ''}`} style={style}>
164
- <ThemeProvider
165
- appId={resolvedConfig.appId}
166
- defaultTheme={resolvedConfig.defaultTheme}
167
- defaultPalette={resolvedConfig.defaultPalette}
168
- >
169
- <QwickAppContext.Provider value={contextValue}>
170
- <PrintModeProvider>
171
- {wrappedContent}
172
- </PrintModeProvider>
173
- </QwickAppContext.Provider>
174
- </ThemeProvider>
175
- </div>
170
+ <NavigationProvider>
171
+ <div className={`qwick-app ${className || ''}`} style={style}>
172
+ <ThemeProvider
173
+ appId={resolvedConfig.appId}
174
+ defaultTheme={resolvedConfig.defaultTheme}
175
+ defaultPalette={resolvedConfig.defaultPalette}
176
+ >
177
+ <QwickAppContext.Provider value={contextValue}>
178
+ <PrintModeProvider>
179
+ {wrappedContent}
180
+ </PrintModeProvider>
181
+ </QwickAppContext.Provider>
182
+ </ThemeProvider>
183
+ </div>
184
+ </NavigationProvider>
176
185
  </AccessibilityProvider>
177
186
  </ErrorBoundary>
178
187
  );
@@ -22,7 +22,7 @@ import { useQwickApp } from '../contexts/QwickAppContext';
22
22
  import Logo from './Logo';
23
23
  import { getIconComponent } from './buttons/Button';
24
24
  import './ResponsiveMenu.css';
25
- import { useSafeLocation, useSafeNavigate } from '../utils/reactUtils';
25
+ import { useNavigation } from '../contexts/NavigationContext';
26
26
  import { loggers } from '../utils/logger';
27
27
  import type { MenuItem } from './menu/MenuItem';
28
28
 
@@ -63,9 +63,8 @@ const ResponsiveMenu: React.FC<ResponsiveMenuProps> = (props) => {
63
63
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
64
64
  const { appName, logo: contextLogo } = useQwickApp();
65
65
 
66
- // React Router hooks (if available)
67
- const navigate = useSafeNavigate();
68
- const location = useSafeLocation();
66
+ // Navigation (uses React Router if available, falls back to window.location)
67
+ const { navigate, location } = useNavigation();
69
68
  const currentPath = location?.pathname;
70
69
 
71
70
  // Debug logging
@@ -24,7 +24,7 @@ import PaletteSwitcher from './buttons/PaletteSwitcher';
24
24
  import { RadioButtonUnchecked as DefaultIcon } from '@mui/icons-material';
25
25
  import { getIconComponent } from './buttons/Button';
26
26
  import './Scaffold.css';
27
- import { useSafeLocation, useSafeNavigate } from '../utils/reactUtils';
27
+ import { useNavigation } from '../contexts/NavigationContext';
28
28
  import { loggers } from '../utils/logger';
29
29
  import type { MenuItem } from './menu/MenuItem';
30
30
 
@@ -92,9 +92,8 @@ const Scaffold: React.FC<ScaffoldProps> = ({
92
92
  const [isRailExpanded, setIsRailExpanded] = useState(false);
93
93
  const [isMounted, setIsMounted] = useState(false);
94
94
 
95
- // React Router hooks (if available)
96
- const location = useSafeLocation();
97
- const navigate = useSafeNavigate();
95
+ // Navigation (uses React Router if available, falls back to window.location)
96
+ const { navigate, location } = useNavigation();
98
97
  const currentPath = isMounted ? location?.pathname : undefined;
99
98
 
100
99
  // Debug logging for navigation
@@ -25,6 +25,7 @@ export * from './layout';
25
25
  export * from './pages';
26
26
  export { default as Scaffold } from './Scaffold';
27
27
  export type { ScaffoldProps, AppBarProps } from './Scaffold';
28
+ export type { MenuItem } from './menu/MenuItem';
28
29
  export { default as ResponsiveMenu } from './ResponsiveMenu';
29
30
  export type { ResponsiveMenuProps } from './ResponsiveMenu';
30
31
  export * from './QwickApp';
@@ -38,7 +38,7 @@ import { usePrintMode } from '../../hooks/usePrintMode';
38
38
  import { PageTemplateSchema } from '../../schemas/PageTemplateSchema';
39
39
  import { PrintConfigSchema } from '../../schemas/PrintConfigSchema';
40
40
  import { toCssLength } from '../../utils/cssUtils';
41
- import { useSafeLocation } from '../../utils/reactUtils';
41
+ import { useNavigation } from '../../contexts/NavigationContext';
42
42
  import { SafeSpan } from '../SafeSpan';
43
43
  import './Page.css';
44
44
  import { Box } from '@mui/material';
@@ -275,7 +275,7 @@ export const PageView: React.FC<PageViewProps> = ({
275
275
  }, [isPrintMode, printConfig]);
276
276
 
277
277
  // Route (fallback to current location)
278
- const location = useSafeLocation();
278
+ const { location } = useNavigation();
279
279
  const actualRoute = resolved.route || location?.pathname || '';
280
280
 
281
281
  // Context value