@qwickapps/react-framework 1.5.6 → 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 (110) hide show
  1. package/dist/components/QwickApp.d.ts.map +1 -1
  2. package/dist/contexts/NavigationContext.d.ts.map +1 -1
  3. package/dist/hooks/useBaseProps.d.ts +12 -1161
  4. package/dist/hooks/useBaseProps.d.ts.map +1 -1
  5. package/dist/index.esm.js +387 -232
  6. package/dist/index.js +385 -230
  7. package/dist/palettes/manifest.json +19 -19
  8. package/dist/palettes/palette-autumn.1.4.9.css +172 -0
  9. package/dist/palettes/palette-autumn.1.4.9.min.css +1 -0
  10. package/dist/palettes/palette-autumn.1.5.0.css +172 -0
  11. package/dist/palettes/palette-autumn.1.5.0.min.css +1 -0
  12. package/dist/palettes/palette-autumn.1.5.1.css +172 -0
  13. package/dist/palettes/palette-autumn.1.5.1.min.css +1 -0
  14. package/dist/palettes/palette-autumn.1.5.2.css +172 -0
  15. package/dist/palettes/palette-autumn.1.5.2.min.css +1 -0
  16. package/dist/palettes/palette-autumn.1.5.3.css +172 -0
  17. package/dist/palettes/palette-autumn.1.5.3.min.css +1 -0
  18. package/dist/palettes/palette-autumn.1.5.4.css +172 -0
  19. package/dist/palettes/palette-autumn.1.5.4.min.css +1 -0
  20. package/dist/palettes/palette-autumn.1.5.5.css +172 -0
  21. package/dist/palettes/palette-autumn.1.5.5.min.css +1 -0
  22. package/dist/palettes/palette-autumn.1.5.7.css +172 -0
  23. package/dist/palettes/palette-autumn.1.5.7.min.css +1 -0
  24. package/dist/palettes/palette-cosmic.1.4.9.css +172 -0
  25. package/dist/palettes/palette-cosmic.1.4.9.min.css +1 -0
  26. package/dist/palettes/palette-cosmic.1.5.0.css +172 -0
  27. package/dist/palettes/palette-cosmic.1.5.0.min.css +1 -0
  28. package/dist/palettes/palette-cosmic.1.5.1.css +172 -0
  29. package/dist/palettes/palette-cosmic.1.5.1.min.css +1 -0
  30. package/dist/palettes/palette-cosmic.1.5.2.css +172 -0
  31. package/dist/palettes/palette-cosmic.1.5.2.min.css +1 -0
  32. package/dist/palettes/palette-cosmic.1.5.3.css +172 -0
  33. package/dist/palettes/palette-cosmic.1.5.3.min.css +1 -0
  34. package/dist/palettes/palette-cosmic.1.5.4.css +172 -0
  35. package/dist/palettes/palette-cosmic.1.5.4.min.css +1 -0
  36. package/dist/palettes/palette-cosmic.1.5.5.css +172 -0
  37. package/dist/palettes/palette-cosmic.1.5.5.min.css +1 -0
  38. package/dist/palettes/palette-cosmic.1.5.7.css +172 -0
  39. package/dist/palettes/palette-cosmic.1.5.7.min.css +1 -0
  40. package/dist/palettes/palette-default.1.4.9.css +178 -0
  41. package/dist/palettes/palette-default.1.4.9.min.css +1 -0
  42. package/dist/palettes/palette-default.1.5.0.css +178 -0
  43. package/dist/palettes/palette-default.1.5.0.min.css +1 -0
  44. package/dist/palettes/palette-default.1.5.1.css +178 -0
  45. package/dist/palettes/palette-default.1.5.1.min.css +1 -0
  46. package/dist/palettes/palette-default.1.5.2.css +178 -0
  47. package/dist/palettes/palette-default.1.5.2.min.css +1 -0
  48. package/dist/palettes/palette-default.1.5.3.css +178 -0
  49. package/dist/palettes/palette-default.1.5.3.min.css +1 -0
  50. package/dist/palettes/palette-default.1.5.4.css +178 -0
  51. package/dist/palettes/palette-default.1.5.4.min.css +1 -0
  52. package/dist/palettes/palette-default.1.5.5.css +178 -0
  53. package/dist/palettes/palette-default.1.5.5.min.css +1 -0
  54. package/dist/palettes/palette-default.1.5.7.css +178 -0
  55. package/dist/palettes/palette-default.1.5.7.min.css +1 -0
  56. package/dist/palettes/palette-ocean.1.4.9.css +172 -0
  57. package/dist/palettes/palette-ocean.1.4.9.min.css +1 -0
  58. package/dist/palettes/palette-ocean.1.5.0.css +172 -0
  59. package/dist/palettes/palette-ocean.1.5.0.min.css +1 -0
  60. package/dist/palettes/palette-ocean.1.5.1.css +172 -0
  61. package/dist/palettes/palette-ocean.1.5.1.min.css +1 -0
  62. package/dist/palettes/palette-ocean.1.5.2.css +172 -0
  63. package/dist/palettes/palette-ocean.1.5.2.min.css +1 -0
  64. package/dist/palettes/palette-ocean.1.5.3.css +172 -0
  65. package/dist/palettes/palette-ocean.1.5.3.min.css +1 -0
  66. package/dist/palettes/palette-ocean.1.5.4.css +172 -0
  67. package/dist/palettes/palette-ocean.1.5.4.min.css +1 -0
  68. package/dist/palettes/palette-ocean.1.5.5.css +172 -0
  69. package/dist/palettes/palette-ocean.1.5.5.min.css +1 -0
  70. package/dist/palettes/palette-ocean.1.5.7.css +172 -0
  71. package/dist/palettes/palette-ocean.1.5.7.min.css +1 -0
  72. package/dist/palettes/palette-spring.1.4.9.css +160 -0
  73. package/dist/palettes/palette-spring.1.4.9.min.css +1 -0
  74. package/dist/palettes/palette-spring.1.5.0.css +160 -0
  75. package/dist/palettes/palette-spring.1.5.0.min.css +1 -0
  76. package/dist/palettes/palette-spring.1.5.1.css +160 -0
  77. package/dist/palettes/palette-spring.1.5.1.min.css +1 -0
  78. package/dist/palettes/palette-spring.1.5.2.css +160 -0
  79. package/dist/palettes/palette-spring.1.5.2.min.css +1 -0
  80. package/dist/palettes/palette-spring.1.5.3.css +166 -0
  81. package/dist/palettes/palette-spring.1.5.3.min.css +1 -0
  82. package/dist/palettes/palette-spring.1.5.4.css +166 -0
  83. package/dist/palettes/palette-spring.1.5.4.min.css +1 -0
  84. package/dist/palettes/palette-spring.1.5.5.css +166 -0
  85. package/dist/palettes/palette-spring.1.5.5.min.css +1 -0
  86. package/dist/palettes/palette-spring.1.5.7.css +166 -0
  87. package/dist/palettes/palette-spring.1.5.7.min.css +1 -0
  88. package/dist/palettes/palette-winter.1.4.9.css +172 -0
  89. package/dist/palettes/palette-winter.1.4.9.min.css +1 -0
  90. package/dist/palettes/palette-winter.1.5.0.css +172 -0
  91. package/dist/palettes/palette-winter.1.5.0.min.css +1 -0
  92. package/dist/palettes/palette-winter.1.5.1.css +172 -0
  93. package/dist/palettes/palette-winter.1.5.1.min.css +1 -0
  94. package/dist/palettes/palette-winter.1.5.2.css +172 -0
  95. package/dist/palettes/palette-winter.1.5.2.min.css +1 -0
  96. package/dist/palettes/palette-winter.1.5.3.css +172 -0
  97. package/dist/palettes/palette-winter.1.5.3.min.css +1 -0
  98. package/dist/palettes/palette-winter.1.5.4.css +172 -0
  99. package/dist/palettes/palette-winter.1.5.4.min.css +1 -0
  100. package/dist/palettes/palette-winter.1.5.5.css +172 -0
  101. package/dist/palettes/palette-winter.1.5.5.min.css +1 -0
  102. package/dist/palettes/palette-winter.1.5.7.css +172 -0
  103. package/dist/palettes/palette-winter.1.5.7.min.css +1 -0
  104. package/dist/utils/iconMap.d.ts +21 -8
  105. package/dist/utils/iconMap.d.ts.map +1 -1
  106. package/package.json +1 -1
  107. package/src/__tests__/utils/iconMap.test.tsx +197 -0
  108. package/src/components/QwickApp.tsx +8 -1
  109. package/src/contexts/NavigationContext.tsx +21 -15
  110. package/src/utils/iconMap.tsx +209 -153
@@ -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;AAuE1B;;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,CAqG/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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwickapps/react-framework",
3
- "version": "1.5.6",
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,7 +34,7 @@
34
34
  *
35
35
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
36
36
  */
37
- import React, { cloneElement, useState } from 'react';
37
+ import React, { cloneElement, useState, useEffect } from 'react';
38
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';
@@ -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,
@@ -15,7 +15,7 @@ import { createContext, useContext, type ReactNode } from 'react';
15
15
  import {
16
16
  useNavigate,
17
17
  useLocation,
18
- UNSAFE_NavigationContext as RouterContext,
18
+ useInRouterContext,
19
19
  } from 'react-router-dom';
20
20
 
21
21
  /**
@@ -58,17 +58,23 @@ function ReactRouterNavigationProvider({ children }: { children: ReactNode }) {
58
58
  }
59
59
  };
60
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
+
61
76
  return (
62
- <NavigationContext.Provider
63
- value={{
64
- navigate,
65
- location: {
66
- pathname: reactRouterLocation.pathname,
67
- search: reactRouterLocation.search,
68
- hash: reactRouterLocation.hash,
69
- },
70
- }}
71
- >
77
+ <NavigationContext.Provider value={{ navigate, location }}>
72
78
  {children}
73
79
  </NavigationContext.Provider>
74
80
  );
@@ -115,11 +121,11 @@ function FallbackNavigationProvider({ children }: { children: ReactNode }) {
115
121
  * This is included automatically by QwickApp - you don't need to add it manually.
116
122
  */
117
123
  export function NavigationProvider({ children }: { children: ReactNode }) {
118
- // Check if we're inside a React Router by checking its internal context
119
- // useContext doesn't throw - it returns null if the context doesn't exist
120
- const routerContext = useContext(RouterContext);
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();
121
127
 
122
- if (routerContext) {
128
+ if (isInRouter) {
123
129
  // We're inside a Router, use React Router's navigation
124
130
  return (
125
131
  <ReactRouterNavigationProvider>{children}</ReactRouterNavigationProvider>