@qwickapps/react-framework 1.3.0

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 (441) hide show
  1. package/LICENSE +44 -0
  2. package/README.md +794 -0
  3. package/dist/components/AccessibilityChecker.d.ts +12 -0
  4. package/dist/components/AccessibilityChecker.d.ts.map +1 -0
  5. package/dist/components/Html.d.ts +48 -0
  6. package/dist/components/Html.d.ts.map +1 -0
  7. package/dist/components/Logo.d.ts +79 -0
  8. package/dist/components/Logo.d.ts.map +1 -0
  9. package/dist/components/Markdown.d.ts +47 -0
  10. package/dist/components/Markdown.d.ts.map +1 -0
  11. package/dist/components/QwickApp.d.ts +56 -0
  12. package/dist/components/QwickApp.d.ts.map +1 -0
  13. package/dist/components/QwickAppsLogo.d.ts +25 -0
  14. package/dist/components/QwickAppsLogo.d.ts.map +1 -0
  15. package/dist/components/ResponsiveMenu.d.ts +38 -0
  16. package/dist/components/ResponsiveMenu.d.ts.map +1 -0
  17. package/dist/components/SafeSpan.d.ts +23 -0
  18. package/dist/components/SafeSpan.d.ts.map +1 -0
  19. package/dist/components/Scaffold.d.ts +57 -0
  20. package/dist/components/Scaffold.d.ts.map +1 -0
  21. package/dist/components/blocks/Article.d.ts +23 -0
  22. package/dist/components/blocks/Article.d.ts.map +1 -0
  23. package/dist/components/blocks/CardListGrid.d.ts +23 -0
  24. package/dist/components/blocks/CardListGrid.d.ts.map +1 -0
  25. package/dist/components/blocks/Code.d.ts +21 -0
  26. package/dist/components/blocks/Code.d.ts.map +1 -0
  27. package/dist/components/blocks/Content.d.ts +24 -0
  28. package/dist/components/blocks/Content.d.ts.map +1 -0
  29. package/dist/components/blocks/CoverImageHeader.d.ts +44 -0
  30. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -0
  31. package/dist/components/blocks/FeatureCard.d.ts +66 -0
  32. package/dist/components/blocks/FeatureCard.d.ts.map +1 -0
  33. package/dist/components/blocks/FeatureGrid.d.ts +48 -0
  34. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -0
  35. package/dist/components/blocks/Footer.d.ts +56 -0
  36. package/dist/components/blocks/Footer.d.ts.map +1 -0
  37. package/dist/components/blocks/HeroBlock.d.ts +33 -0
  38. package/dist/components/blocks/HeroBlock.d.ts.map +1 -0
  39. package/dist/components/blocks/PageBannerHeader.d.ts +30 -0
  40. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -0
  41. package/dist/components/blocks/ProductCard.d.ts +57 -0
  42. package/dist/components/blocks/ProductCard.d.ts.map +1 -0
  43. package/dist/components/blocks/Section.d.ts +40 -0
  44. package/dist/components/blocks/Section.d.ts.map +1 -0
  45. package/dist/components/blocks/index.d.ts +37 -0
  46. package/dist/components/blocks/index.d.ts.map +1 -0
  47. package/dist/components/buttons/Button.d.ts +38 -0
  48. package/dist/components/buttons/Button.d.ts.map +1 -0
  49. package/dist/components/buttons/PaletteSwitcher.d.ts +24 -0
  50. package/dist/components/buttons/PaletteSwitcher.d.ts.map +1 -0
  51. package/dist/components/buttons/ThemeSwitcher.d.ts +24 -0
  52. package/dist/components/buttons/ThemeSwitcher.d.ts.map +1 -0
  53. package/dist/components/buttons/index.d.ts +11 -0
  54. package/dist/components/buttons/index.d.ts.map +1 -0
  55. package/dist/components/forms/FormBlock.d.ts +45 -0
  56. package/dist/components/forms/FormBlock.d.ts.map +1 -0
  57. package/dist/components/forms/index.d.ts +8 -0
  58. package/dist/components/forms/index.d.ts.map +1 -0
  59. package/dist/components/index.d.ts +32 -0
  60. package/dist/components/index.d.ts.map +1 -0
  61. package/dist/components/input/ChoiceInputField.d.ts +30 -0
  62. package/dist/components/input/ChoiceInputField.d.ts.map +1 -0
  63. package/dist/components/input/HtmlInputField.d.ts +29 -0
  64. package/dist/components/input/HtmlInputField.d.ts.map +1 -0
  65. package/dist/components/input/SelectInputField.d.ts +29 -0
  66. package/dist/components/input/SelectInputField.d.ts.map +1 -0
  67. package/dist/components/input/TextField.d.ts +18 -0
  68. package/dist/components/input/TextField.d.ts.map +1 -0
  69. package/dist/components/input/TextInputField.d.ts +32 -0
  70. package/dist/components/input/TextInputField.d.ts.map +1 -0
  71. package/dist/components/input/index.d.ts +17 -0
  72. package/dist/components/input/index.d.ts.map +1 -0
  73. package/dist/components/layout/GridCell.d.ts +16 -0
  74. package/dist/components/layout/GridCell.d.ts.map +1 -0
  75. package/dist/components/layout/GridCellWrapper.d.ts +46 -0
  76. package/dist/components/layout/GridCellWrapper.d.ts.map +1 -0
  77. package/dist/components/layout/GridLayout.d.ts +38 -0
  78. package/dist/components/layout/GridLayout.d.ts.map +1 -0
  79. package/dist/components/layout/index.d.ts +12 -0
  80. package/dist/components/layout/index.d.ts.map +1 -0
  81. package/dist/components/menu/Menu.d.ts +1 -0
  82. package/dist/components/menu/Menu.d.ts.map +1 -0
  83. package/dist/components/menu/MenuItem.d.ts +31 -0
  84. package/dist/components/menu/MenuItem.d.ts.map +1 -0
  85. package/dist/components/menu/index.d.ts +7 -0
  86. package/dist/components/menu/index.d.ts.map +1 -0
  87. package/dist/components/pages/FormPage.d.ts +66 -0
  88. package/dist/components/pages/FormPage.d.ts.map +1 -0
  89. package/dist/components/pages/Page.d.ts +124 -0
  90. package/dist/components/pages/Page.d.ts.map +1 -0
  91. package/dist/components/pages/index.d.ts +11 -0
  92. package/dist/components/pages/index.d.ts.map +1 -0
  93. package/dist/contexts/DataContext.d.ts +139 -0
  94. package/dist/contexts/DataContext.d.ts.map +1 -0
  95. package/dist/contexts/DimensionsContext.d.ts +42 -0
  96. package/dist/contexts/DimensionsContext.d.ts.map +1 -0
  97. package/dist/contexts/PaletteContext.d.ts +53 -0
  98. package/dist/contexts/PaletteContext.d.ts.map +1 -0
  99. package/dist/contexts/QwickAppContext.d.ts +71 -0
  100. package/dist/contexts/QwickAppContext.d.ts.map +1 -0
  101. package/dist/contexts/ThemeContext.d.ts +65 -0
  102. package/dist/contexts/ThemeContext.d.ts.map +1 -0
  103. package/dist/contexts/index.d.ts +9 -0
  104. package/dist/contexts/index.d.ts.map +1 -0
  105. package/dist/hooks/index.d.ts +10 -0
  106. package/dist/hooks/index.d.ts.map +1 -0
  107. package/dist/hooks/useBaseProps.d.ts +101 -0
  108. package/dist/hooks/useBaseProps.d.ts.map +1 -0
  109. package/dist/hooks/useDataBinding.d.ts +22 -0
  110. package/dist/hooks/useDataBinding.d.ts.map +1 -0
  111. package/dist/index.css +1 -0
  112. package/dist/index.d.ts +8 -0
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.esm.css +1 -0
  115. package/dist/index.esm.js +24143 -0
  116. package/dist/index.js +24245 -0
  117. package/dist/palettes/PaletteAutumn.d.ts +10 -0
  118. package/dist/palettes/PaletteAutumn.d.ts.map +1 -0
  119. package/dist/palettes/PaletteCosmic.d.ts +10 -0
  120. package/dist/palettes/PaletteCosmic.d.ts.map +1 -0
  121. package/dist/palettes/PaletteDefault.d.ts +10 -0
  122. package/dist/palettes/PaletteDefault.d.ts.map +1 -0
  123. package/dist/palettes/PaletteOcean.d.ts +10 -0
  124. package/dist/palettes/PaletteOcean.d.ts.map +1 -0
  125. package/dist/palettes/PaletteSpring.d.ts +10 -0
  126. package/dist/palettes/PaletteSpring.d.ts.map +1 -0
  127. package/dist/palettes/PaletteWinter.d.ts +10 -0
  128. package/dist/palettes/PaletteWinter.d.ts.map +1 -0
  129. package/dist/palettes/index.d.ts +13 -0
  130. package/dist/palettes/index.d.ts.map +1 -0
  131. package/dist/schemas/ActionSchema.d.ts +21 -0
  132. package/dist/schemas/ActionSchema.d.ts.map +1 -0
  133. package/dist/schemas/ArticleSchema.d.ts +13 -0
  134. package/dist/schemas/ArticleSchema.d.ts.map +1 -0
  135. package/dist/schemas/Builders.d.ts +7 -0
  136. package/dist/schemas/Builders.d.ts.map +1 -0
  137. package/dist/schemas/ButtonSchema.d.ts +19 -0
  138. package/dist/schemas/ButtonSchema.d.ts.map +1 -0
  139. package/dist/schemas/CardListGridSchema.d.ts +17 -0
  140. package/dist/schemas/CardListGridSchema.d.ts.map +1 -0
  141. package/dist/schemas/ChoiceInputFieldSchema.d.ts +18 -0
  142. package/dist/schemas/ChoiceInputFieldSchema.d.ts.map +1 -0
  143. package/dist/schemas/CodeSchema.d.ts +18 -0
  144. package/dist/schemas/CodeSchema.d.ts.map +1 -0
  145. package/dist/schemas/ContentSchema.d.ts +20 -0
  146. package/dist/schemas/ContentSchema.d.ts.map +1 -0
  147. package/dist/schemas/CoverImageHeaderSchema.d.ts +28 -0
  148. package/dist/schemas/CoverImageHeaderSchema.d.ts.map +1 -0
  149. package/dist/schemas/FeatureCardSchema.d.ts +28 -0
  150. package/dist/schemas/FeatureCardSchema.d.ts.map +1 -0
  151. package/dist/schemas/FeatureGridSchema.d.ts +17 -0
  152. package/dist/schemas/FeatureGridSchema.d.ts.map +1 -0
  153. package/dist/schemas/FeatureItemSchema.d.ts +16 -0
  154. package/dist/schemas/FeatureItemSchema.d.ts.map +1 -0
  155. package/dist/schemas/FooterItemSchema.d.ts +15 -0
  156. package/dist/schemas/FooterItemSchema.d.ts.map +1 -0
  157. package/dist/schemas/FooterSchema.d.ts +20 -0
  158. package/dist/schemas/FooterSchema.d.ts.map +1 -0
  159. package/dist/schemas/FooterSectionSchema.d.ts +15 -0
  160. package/dist/schemas/FooterSectionSchema.d.ts.map +1 -0
  161. package/dist/schemas/FormBlockSchema.d.ts +19 -0
  162. package/dist/schemas/FormBlockSchema.d.ts.map +1 -0
  163. package/dist/schemas/HeaderActionSchema.d.ts +17 -0
  164. package/dist/schemas/HeaderActionSchema.d.ts.map +1 -0
  165. package/dist/schemas/HeroBlockSchema.d.ts +22 -0
  166. package/dist/schemas/HeroBlockSchema.d.ts.map +1 -0
  167. package/dist/schemas/HtmlInputFieldSchema.d.ts +18 -0
  168. package/dist/schemas/HtmlInputFieldSchema.d.ts.map +1 -0
  169. package/dist/schemas/MetadataItemSchema.d.ts +13 -0
  170. package/dist/schemas/MetadataItemSchema.d.ts.map +1 -0
  171. package/dist/schemas/PageBannerHeaderSchema.d.ts +28 -0
  172. package/dist/schemas/PageBannerHeaderSchema.d.ts.map +1 -0
  173. package/dist/schemas/PaletteSwitcherSchema.d.ts +16 -0
  174. package/dist/schemas/PaletteSwitcherSchema.d.ts.map +1 -0
  175. package/dist/schemas/ProductCardSchema.d.ts +39 -0
  176. package/dist/schemas/ProductCardSchema.d.ts.map +1 -0
  177. package/dist/schemas/SafeSpanSchema.d.ts +13 -0
  178. package/dist/schemas/SafeSpanSchema.d.ts.map +1 -0
  179. package/dist/schemas/SectionSchema.d.ts +17 -0
  180. package/dist/schemas/SectionSchema.d.ts.map +1 -0
  181. package/dist/schemas/SelectInputFieldSchema.d.ts +27 -0
  182. package/dist/schemas/SelectInputFieldSchema.d.ts.map +1 -0
  183. package/dist/schemas/TextInputFieldSchema.d.ts +22 -0
  184. package/dist/schemas/TextInputFieldSchema.d.ts.map +1 -0
  185. package/dist/schemas/ThemeSwitcherSchema.d.ts +19 -0
  186. package/dist/schemas/ThemeSwitcherSchema.d.ts.map +1 -0
  187. package/dist/schemas/index.d.ts +33 -0
  188. package/dist/schemas/index.d.ts.map +1 -0
  189. package/dist/schemas/types.d.ts +7 -0
  190. package/dist/schemas/types.d.ts.map +1 -0
  191. package/dist/templates/TemplateResolver.d.ts +52 -0
  192. package/dist/templates/TemplateResolver.d.ts.map +1 -0
  193. package/dist/templates/index.d.ts +7 -0
  194. package/dist/templates/index.d.ts.map +1 -0
  195. package/dist/tests/ConsoleWarningTest.d.ts +5 -0
  196. package/dist/tests/ConsoleWarningTest.d.ts.map +1 -0
  197. package/dist/tests/StorageKeyTest.d.ts +6 -0
  198. package/dist/tests/StorageKeyTest.d.ts.map +1 -0
  199. package/dist/tests/ThemeStorageKeyTest.d.ts +6 -0
  200. package/dist/tests/ThemeStorageKeyTest.d.ts.map +1 -0
  201. package/dist/types/CacheProvider.d.ts +18 -0
  202. package/dist/types/CacheProvider.d.ts.map +1 -0
  203. package/dist/types/ContentProxy.d.ts +47 -0
  204. package/dist/types/ContentProxy.d.ts.map +1 -0
  205. package/dist/types/DataBinding.d.ts +7 -0
  206. package/dist/types/DataBinding.d.ts.map +1 -0
  207. package/dist/types/DataProvider.d.ts +7 -0
  208. package/dist/types/DataProvider.d.ts.map +1 -0
  209. package/dist/types/DataTypes.d.ts +185 -0
  210. package/dist/types/DataTypes.d.ts.map +1 -0
  211. package/dist/types/TemplateProvider.d.ts +10 -0
  212. package/dist/types/TemplateProvider.d.ts.map +1 -0
  213. package/dist/types/TemplateResolver.d.ts +23 -0
  214. package/dist/types/TemplateResolver.d.ts.map +1 -0
  215. package/dist/types/index.d.ts +81 -0
  216. package/dist/types/index.d.ts.map +1 -0
  217. package/dist/utils/breakpoints.d.ts +35 -0
  218. package/dist/utils/breakpoints.d.ts.map +1 -0
  219. package/dist/utils/customPaletteManager.d.ts +8 -0
  220. package/dist/utils/customPaletteManager.d.ts.map +1 -0
  221. package/dist/utils/dimensions.d.ts +34 -0
  222. package/dist/utils/dimensions.d.ts.map +1 -0
  223. package/dist/utils/htmlTransform.d.ts +44 -0
  224. package/dist/utils/htmlTransform.d.ts.map +1 -0
  225. package/dist/utils/index.d.ts +15 -0
  226. package/dist/utils/index.d.ts.map +1 -0
  227. package/dist/utils/logger.d.ts +14 -0
  228. package/dist/utils/logger.d.ts.map +1 -0
  229. package/dist/utils/paletteUtils.d.ts +38 -0
  230. package/dist/utils/paletteUtils.d.ts.map +1 -0
  231. package/dist/utils/persistenceUtils.d.ts +31 -0
  232. package/dist/utils/persistenceUtils.d.ts.map +1 -0
  233. package/dist/utils/reactUtils.d.ts +24 -0
  234. package/dist/utils/reactUtils.d.ts.map +1 -0
  235. package/dist/utils/spacing.d.ts +34 -0
  236. package/dist/utils/spacing.d.ts.map +1 -0
  237. package/dist/utils/themePerformanceMonitor.d.ts +32 -0
  238. package/dist/utils/themePerformanceMonitor.d.ts.map +1 -0
  239. package/dist/utils/themeUtils.d.ts +27 -0
  240. package/dist/utils/themeUtils.d.ts.map +1 -0
  241. package/package.json +141 -0
  242. package/src/__tests__/components/Logo.test.js +172 -0
  243. package/src/__tests__/contexts/DataContext.test.js +505 -0
  244. package/src/__tests__/contexts/PaletteContext.test.js +115 -0
  245. package/src/__tests__/contexts/ThemeContext.test.js +123 -0
  246. package/src/__tests__/utils/paletteUtils.test.js +142 -0
  247. package/src/__tests__/utils/themeUtils.test.js +142 -0
  248. package/src/components/AccessibilityChecker.tsx +264 -0
  249. package/src/components/Html.tsx +191 -0
  250. package/src/components/Logo.css +217 -0
  251. package/src/components/Logo.tsx +370 -0
  252. package/src/components/Markdown.tsx +191 -0
  253. package/src/components/QwickApp.css +257 -0
  254. package/src/components/QwickApp.tsx +157 -0
  255. package/src/components/QwickAppsLogo.tsx +77 -0
  256. package/src/components/ResponsiveMenu.css +416 -0
  257. package/src/components/ResponsiveMenu.tsx +310 -0
  258. package/src/components/SafeSpan.tsx +128 -0
  259. package/src/components/Scaffold.css +541 -0
  260. package/src/components/Scaffold.tsx +463 -0
  261. package/src/components/__tests__/Article.test.tsx +419 -0
  262. package/src/components/__tests__/Button.test.tsx +702 -0
  263. package/src/components/__tests__/CardListGrid.test.tsx +478 -0
  264. package/src/components/__tests__/ChoiceInputField.test.tsx +864 -0
  265. package/src/components/__tests__/Code.test.tsx +595 -0
  266. package/src/components/__tests__/Content.integration.test.tsx +193 -0
  267. package/src/components/__tests__/Content.test.tsx +504 -0
  268. package/src/components/__tests__/CoverImageHeader.test.tsx +456 -0
  269. package/src/components/__tests__/FeatureCard.integration.test.tsx +384 -0
  270. package/src/components/__tests__/FeatureGrid.integration.test.tsx +364 -0
  271. package/src/components/__tests__/FeatureGrid.test.tsx +494 -0
  272. package/src/components/__tests__/Footer.test.tsx +544 -0
  273. package/src/components/__tests__/FormBlock.test.tsx +857 -0
  274. package/src/components/__tests__/HeroBlock.integration.test.tsx +272 -0
  275. package/src/components/__tests__/HeroBlock.test.tsx +463 -0
  276. package/src/components/__tests__/Html.test.tsx +174 -0
  277. package/src/components/__tests__/HtmlInputField.test.tsx +856 -0
  278. package/src/components/__tests__/Markdown.test.tsx +233 -0
  279. package/src/components/__tests__/PageBannerHeader.test.tsx +614 -0
  280. package/src/components/__tests__/PaletteSwitcher.test.tsx +864 -0
  281. package/src/components/__tests__/ProductCard.test.tsx +377 -0
  282. package/src/components/__tests__/SafeSpan.integration.test.tsx +123 -0
  283. package/src/components/__tests__/SafeSpan.simple.test.tsx +65 -0
  284. package/src/components/__tests__/SafeSpan.test.tsx +388 -0
  285. package/src/components/__tests__/Section.integration.test.tsx +288 -0
  286. package/src/components/__tests__/Section.test.tsx +494 -0
  287. package/src/components/__tests__/SelectInputField.test.tsx +886 -0
  288. package/src/components/__tests__/TextInputField.test.tsx +749 -0
  289. package/src/components/__tests__/ThemeSwitcher.test.tsx +777 -0
  290. package/src/components/blocks/Article.tsx +194 -0
  291. package/src/components/blocks/CardListGrid.tsx +132 -0
  292. package/src/components/blocks/Code.tsx +313 -0
  293. package/src/components/blocks/Content.tsx +265 -0
  294. package/src/components/blocks/CoverImageHeader.css +17 -0
  295. package/src/components/blocks/CoverImageHeader.tsx +435 -0
  296. package/src/components/blocks/FeatureCard.tsx +321 -0
  297. package/src/components/blocks/FeatureGrid.tsx +147 -0
  298. package/src/components/blocks/Footer.tsx +343 -0
  299. package/src/components/blocks/HeroBlock.tsx +280 -0
  300. package/src/components/blocks/PageBannerHeader.tsx +471 -0
  301. package/src/components/blocks/ProductCard.tsx +472 -0
  302. package/src/components/blocks/Section.tsx +209 -0
  303. package/src/components/blocks/index.ts +37 -0
  304. package/src/components/buttons/Button.tsx +233 -0
  305. package/src/components/buttons/PaletteSwitcher.tsx +268 -0
  306. package/src/components/buttons/ThemeSwitcher.tsx +283 -0
  307. package/src/components/buttons/index.ts +11 -0
  308. package/src/components/forms/FormBlock.tsx +291 -0
  309. package/src/components/forms/index.ts +7 -0
  310. package/src/components/index.ts +37 -0
  311. package/src/components/input/ChoiceInputField.tsx +188 -0
  312. package/src/components/input/HtmlInputField.tsx +326 -0
  313. package/src/components/input/SelectInputField.tsx +197 -0
  314. package/src/components/input/TextField.tsx +47 -0
  315. package/src/components/input/TextInputField.tsx +144 -0
  316. package/src/components/input/index.ts +17 -0
  317. package/src/components/layout/GridCell.tsx +46 -0
  318. package/src/components/layout/GridCellWrapper.tsx +87 -0
  319. package/src/components/layout/GridLayout.tsx +169 -0
  320. package/src/components/layout/index.ts +13 -0
  321. package/src/components/menu/Menu.tsx +0 -0
  322. package/src/components/menu/MenuItem.tsx +32 -0
  323. package/src/components/menu/index.ts +6 -0
  324. package/src/components/pages/FormPage.tsx +108 -0
  325. package/src/components/pages/Page.css +460 -0
  326. package/src/components/pages/Page.tsx +345 -0
  327. package/src/components/pages/index.ts +11 -0
  328. package/src/contexts/DataContext.tsx +355 -0
  329. package/src/contexts/DimensionsContext.tsx +154 -0
  330. package/src/contexts/PaletteContext.tsx +217 -0
  331. package/src/contexts/QwickAppContext.tsx +95 -0
  332. package/src/contexts/ThemeContext.tsx +376 -0
  333. package/src/contexts/index.ts +9 -0
  334. package/src/hooks/__tests__/useDataBinding.test.tsx.disabled +229 -0
  335. package/src/hooks/index.ts +11 -0
  336. package/src/hooks/useBaseProps.ts +267 -0
  337. package/src/hooks/useDataBinding.ts +77 -0
  338. package/src/index.ts +23 -0
  339. package/src/palettes/PaletteAutumn.css +172 -0
  340. package/src/palettes/PaletteAutumn.ts +16 -0
  341. package/src/palettes/PaletteCosmic.css +172 -0
  342. package/src/palettes/PaletteCosmic.ts +16 -0
  343. package/src/palettes/PaletteDefault.css +178 -0
  344. package/src/palettes/PaletteDefault.ts +17 -0
  345. package/src/palettes/PaletteOcean.css +172 -0
  346. package/src/palettes/PaletteOcean.ts +16 -0
  347. package/src/palettes/PaletteSpring.css +160 -0
  348. package/src/palettes/PaletteSpring.ts +16 -0
  349. package/src/palettes/PaletteWinter.css +172 -0
  350. package/src/palettes/PaletteWinter.ts +16 -0
  351. package/src/palettes/index.css +12 -0
  352. package/src/palettes/index.ts +29 -0
  353. package/src/schemas/ActionSchema.ts +140 -0
  354. package/src/schemas/ArticleSchema.ts +35 -0
  355. package/src/schemas/ButtonSchema.ts +99 -0
  356. package/src/schemas/CardListGridSchema.ts +102 -0
  357. package/src/schemas/ChoiceInputFieldSchema.ts +89 -0
  358. package/src/schemas/CodeSchema.ts +88 -0
  359. package/src/schemas/ContentSchema.ts +128 -0
  360. package/src/schemas/CoverImageHeaderSchema.ts +208 -0
  361. package/src/schemas/FeatureCardSchema.ts +161 -0
  362. package/src/schemas/FeatureGridSchema.ts +87 -0
  363. package/src/schemas/FeatureItemSchema.ts +68 -0
  364. package/src/schemas/FooterItemSchema.ts +57 -0
  365. package/src/schemas/FooterSchema.ts +116 -0
  366. package/src/schemas/FooterSectionSchema.ts +50 -0
  367. package/src/schemas/FormBlockSchema.ts +102 -0
  368. package/src/schemas/HeaderActionSchema.ts +83 -0
  369. package/src/schemas/HeroBlockSchema.ts +149 -0
  370. package/src/schemas/HtmlInputFieldSchema.ts +88 -0
  371. package/src/schemas/MetadataItemSchema.ts +35 -0
  372. package/src/schemas/PageBannerHeaderSchema.ts +206 -0
  373. package/src/schemas/PaletteSwitcherSchema.ts +66 -0
  374. package/src/schemas/ProductCardSchema.ts +264 -0
  375. package/src/schemas/SafeSpanSchema.ts +36 -0
  376. package/src/schemas/SectionSchema.ts +106 -0
  377. package/src/schemas/SelectInputFieldSchema.ts +137 -0
  378. package/src/schemas/TextInputFieldSchema.ts +129 -0
  379. package/src/schemas/ThemeSwitcherSchema.ts +97 -0
  380. package/src/schemas/__tests__/builders.test.ts +313 -0
  381. package/src/schemas/index.ts +34 -0
  382. package/src/setupTests.js +60 -0
  383. package/src/stories/Article.stories.tsx +549 -0
  384. package/src/stories/Button.stories.tsx +498 -0
  385. package/src/stories/CardListGrid.stories.tsx +539 -0
  386. package/src/stories/ChoiceInputField.stories.tsx +591 -0
  387. package/src/stories/Code.stories.tsx +711 -0
  388. package/src/stories/Content.stories.tsx +463 -0
  389. package/src/stories/CoverImageHeader.stories.tsx +794 -0
  390. package/src/stories/DataBinding.advanced.stories.tsx +548 -0
  391. package/src/stories/DataBinding.stories.tsx +452 -0
  392. package/src/stories/DataProvider.stories.tsx +1361 -0
  393. package/src/stories/FeatureCard.stories.tsx +642 -0
  394. package/src/stories/FeatureGrid.stories.tsx +669 -0
  395. package/src/stories/Footer.stories.tsx +724 -0
  396. package/src/stories/FormBlock.stories.tsx +834 -0
  397. package/src/stories/HeroBlock.stories.tsx +442 -0
  398. package/src/stories/Html.stories.tsx +264 -0
  399. package/src/stories/HtmlInputField.stories.tsx +558 -0
  400. package/src/stories/Introduction.stories.tsx +721 -0
  401. package/src/stories/LayoutBlocks.stories.tsx +382 -0
  402. package/src/stories/LayoutSystem.stories.tsx +253 -0
  403. package/src/stories/Logo.stories.tsx +400 -0
  404. package/src/stories/Markdown.stories.tsx +349 -0
  405. package/src/stories/Page.stories.tsx +762 -0
  406. package/src/stories/PageBannerHeader.stories.tsx +949 -0
  407. package/src/stories/PaletteSwitcher.stories.tsx +156 -0
  408. package/src/stories/ProductCard.stories.tsx +504 -0
  409. package/src/stories/QwickApp.stories.tsx +461 -0
  410. package/src/stories/ResponsiveMenu.stories.tsx +299 -0
  411. package/src/stories/SafeSpan.stories.tsx +612 -0
  412. package/src/stories/Section.stories.tsx +613 -0
  413. package/src/stories/SelectInputField.stories.tsx +605 -0
  414. package/src/stories/TextInputField.stories.tsx +526 -0
  415. package/src/stories/ThemeSwitcher.stories.tsx +170 -0
  416. package/src/stories/form/FormComponents.stories.tsx +588 -0
  417. package/src/templates/TemplateResolver.ts +156 -0
  418. package/src/templates/index.ts +6 -0
  419. package/src/tests/ConsoleWarningTest.tsx +30 -0
  420. package/src/tests/StorageKeyTest.tsx +110 -0
  421. package/src/tests/ThemeStorageKeyTest.tsx +114 -0
  422. package/src/types/CacheProvider.ts +14 -0
  423. package/src/types/ContentProxy.ts +99 -0
  424. package/src/types/DataTypes.ts +196 -0
  425. package/src/types/TemplateProvider.ts +9 -0
  426. package/src/types/TemplateResolver.ts +26 -0
  427. package/src/types/index.ts +99 -0
  428. package/src/utils/__tests__/createDataDrivenComponent.test.tsx.disabled +193 -0
  429. package/src/utils/__tests__/htmlTransform.test.tsx +255 -0
  430. package/src/utils/breakpoints.ts +87 -0
  431. package/src/utils/customPaletteManager.js +214 -0
  432. package/src/utils/dimensions.ts +147 -0
  433. package/src/utils/htmlTransform.tsx +323 -0
  434. package/src/utils/index.ts +16 -0
  435. package/src/utils/logger.ts +28 -0
  436. package/src/utils/paletteUtils.ts +78 -0
  437. package/src/utils/persistenceUtils.ts +107 -0
  438. package/src/utils/reactUtils.tsx +37 -0
  439. package/src/utils/spacing.ts +155 -0
  440. package/src/utils/themePerformanceMonitor.js +113 -0
  441. package/src/utils/themeUtils.ts +67 -0
@@ -0,0 +1,345 @@
1
+ /**
2
+ * Page Component - Main content wrapper for application pages
3
+ *
4
+ * The Page component is what developers primarily focus on when building applications.
5
+ * It provides:
6
+ * - Automatic page title management
7
+ * - Route-based configuration
8
+ * - Contextual menu items that appear in navigation
9
+ * - Proper content spacing and layout
10
+ * - SEO and accessibility features
11
+ *
12
+ * Page components automatically integrate with the AppScaffold to provide
13
+ * contextual actions and menu items specific to the current page.
14
+ *
15
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
16
+ */
17
+
18
+ import React, { useContext, createContext, useCallback } from 'react';
19
+ import './Page.css';
20
+ import { useSafeLocation } from '../../utils/reactUtils';
21
+
22
+ export interface PageContextValue {
23
+ /** Current page route/path */
24
+ route?: string;
25
+ }
26
+
27
+ // Create context for page-level state
28
+ const PageContext = createContext<PageContextValue>({});
29
+
30
+ export const usePageContext = (): PageContextValue => {
31
+ const context = useContext(PageContext);
32
+ if (!context) {
33
+ throw new Error('usePageContext must be used within a PageProvider or Page component');
34
+ }
35
+ return context;
36
+ };
37
+
38
+ export type MessageType = 'loading' | 'error' | 'warning' | 'success' | 'info';
39
+
40
+ export type LoadingType = 'spinner' | 'bar-top' | 'bar-bottom' | 'dots' | 'custom';
41
+
42
+ export interface MessageState {
43
+ /** Type of message to display */
44
+ type: MessageType;
45
+ /** Message content (string or custom JSX) */
46
+ content?: string | React.ReactNode;
47
+ /** Custom component to render instead of default */
48
+ customComponent?: React.ReactNode;
49
+ }
50
+
51
+ export interface LoadingState {
52
+ /** Type of loading indicator */
53
+ type?: LoadingType;
54
+ /** Loading message */
55
+ message?: string;
56
+ /** Custom loading component */
57
+ customComponent?: React.ReactNode;
58
+ }
59
+
60
+ export interface PageProps {
61
+ /** Page content */
62
+ children: React.ReactNode;
63
+ /** Current route/path for this page */
64
+ route?: string;
65
+ /** Optional header component */
66
+ header?: React.ReactNode;
67
+ /** Optional footer component */
68
+ footer?: React.ReactNode;
69
+ /** Additional CSS class */
70
+ className?: string;
71
+ /** Page layout variant */
72
+ variant?: 'default' | 'centered' | 'narrow' | 'wide' | 'fullwidth';
73
+ /** Padding around page content */
74
+ padding?: 'none' | 'small' | 'medium' | 'large';
75
+ /** Background variant */
76
+ background?: 'default' | 'surface' | 'alternate';
77
+ /** Maximum width for content */
78
+ maxWidth?: 'small' | 'medium' | 'large' | 'extra-large' | 'none';
79
+ /** Loading state configuration */
80
+ loading?: boolean | LoadingState;
81
+ /** Message state for displaying info/warning/error/success messages */
82
+ message?: MessageState;
83
+ }
84
+
85
+ const Page: React.FC<PageProps> = ({
86
+ children,
87
+ route,
88
+ header,
89
+ footer,
90
+ className = '',
91
+ variant = 'default',
92
+ padding = 'medium',
93
+ background = 'default',
94
+ maxWidth = 'large',
95
+ loading = false,
96
+ message,
97
+ }) => {
98
+ // Auto-detect current route if available
99
+ const location = useSafeLocation();
100
+ const actualRoute = route || location?.pathname || '';
101
+
102
+ // SCROLL HANDLING COMMENTED OUT FOR STABILITY
103
+ // Handle scroll for title shrinking
104
+ // useEffect(() => {
105
+ // if (!shrinkTitleOnScroll || !pageContentRef.current) return;
106
+ //
107
+ // const handleScroll = () => {
108
+ // if (pageContentRef.current) {
109
+ // const scrollTop = pageContentRef.current.scrollTop;
110
+ // const shouldCollapse = scrollTop > scrollThreshold;
111
+ // setIsTitleCollapsed(shouldCollapse);
112
+ // }
113
+ // };
114
+ //
115
+ // const pageElement = pageContentRef.current;
116
+ // if (pageElement) {
117
+ // pageElement.addEventListener('scroll', handleScroll);
118
+ // return () => pageElement.removeEventListener('scroll', handleScroll);
119
+ // }
120
+ // }, [shrinkTitleOnScroll, scrollThreshold]);
121
+
122
+ // Memoized context value
123
+ const contextValue = React.useMemo<PageContextValue>(() => ({
124
+ route: actualRoute,
125
+ }), [actualRoute]);
126
+
127
+ // Generate class names
128
+ const pageClasses = [
129
+ 'page',
130
+ `page-variant-${variant}`,
131
+ `page-padding-${padding}`,
132
+ `page-background-${background}`,
133
+ `page-max-width-${maxWidth}`,
134
+ className,
135
+ ].filter(Boolean).join(' ');
136
+
137
+ // Render loading indicator
138
+ const renderLoading = (loadingState: boolean | LoadingState) => {
139
+ if (loadingState === false) return null;
140
+
141
+ // Handle simple boolean loading
142
+ if (loadingState === true) {
143
+ return (
144
+ <div className="page-loading">
145
+ <div className="loading-spinner" aria-label="Loading content">
146
+ <div className="spinner"></div>
147
+ </div>
148
+ </div>
149
+ );
150
+ }
151
+
152
+ // Handle custom loading state
153
+ const { type = 'spinner', message, customComponent } = loadingState;
154
+
155
+ if (customComponent) {
156
+ return <div className="page-loading">{customComponent}</div>;
157
+ }
158
+
159
+ switch (type) {
160
+ case 'bar-top':
161
+ return (
162
+ <div className="page-loading-bar page-loading-bar-top">
163
+ <div className="loading-bar-progress"></div>
164
+ {message && <div className="loading-message">{message}</div>}
165
+ </div>
166
+ );
167
+ case 'bar-bottom':
168
+ return (
169
+ <div className="page-loading-bar page-loading-bar-bottom">
170
+ <div className="loading-bar-progress"></div>
171
+ {message && <div className="loading-message">{message}</div>}
172
+ </div>
173
+ );
174
+ case 'dots':
175
+ return (
176
+ <div className="page-loading page-loading-dots">
177
+ <div className="loading-dots" aria-label="Loading content">
178
+ <span></span><span></span><span></span>
179
+ </div>
180
+ {message && <div className="loading-message">{message}</div>}
181
+ </div>
182
+ );
183
+ case 'spinner':
184
+ default:
185
+ return (
186
+ <div className="page-loading">
187
+ <div className="loading-spinner" aria-label="Loading content">
188
+ <div className="spinner"></div>
189
+ </div>
190
+ {message && <div className="loading-message">{message}</div>}
191
+ </div>
192
+ );
193
+ }
194
+ };
195
+
196
+ // Render message (error/warning/success/info)
197
+ const renderMessage = (messageState: MessageState) => {
198
+ const { type, content, customComponent } = messageState;
199
+
200
+ if (customComponent) {
201
+ return <div className={`page-message page-message-${type}`}>{customComponent}</div>;
202
+ }
203
+
204
+ const icons = {
205
+ error: '⚠️',
206
+ warning: '⚠️',
207
+ success: '✅',
208
+ info: 'ℹ️',
209
+ loading: '⏳'
210
+ };
211
+
212
+ return (
213
+ <div className={`page-message page-message-${type}`}>
214
+ <div className="message-icon" aria-hidden="true">{icons[type]}</div>
215
+ <div className="message-content">
216
+ {typeof content === 'string' ? <p>{content}</p> : content}
217
+ </div>
218
+ </div>
219
+ );
220
+ };
221
+
222
+ // Handle loading state
223
+ if (loading) {
224
+ return (
225
+ <PageContext.Provider value={contextValue}>
226
+ <div className={pageClasses}>
227
+ {renderLoading(loading)}
228
+ </div>
229
+ </PageContext.Provider>
230
+ );
231
+ }
232
+
233
+ return (
234
+ <PageContext.Provider value={contextValue}>
235
+ <div className={pageClasses}>
236
+ {/* Optional Header */}
237
+ {header && (
238
+ <div className="page-header">
239
+ {header}
240
+ </div>
241
+ )}
242
+
243
+ {/* Message state (error/warning/success/info) */}
244
+ {message && renderMessage(message)}
245
+
246
+ {/* Page Content */}
247
+ <div className="page-content">
248
+ {children}
249
+ </div>
250
+
251
+ {/* Optional Footer */}
252
+ {footer && (
253
+ <div className="page-footer">
254
+ {footer}
255
+ </div>
256
+ )}
257
+ </div>
258
+ </PageContext.Provider>
259
+ );
260
+ };
261
+
262
+ // Utility hook for updating page configuration from within page components
263
+ export const usePage = () => {
264
+ const context = usePageContext();
265
+
266
+ return {
267
+ ...context,
268
+ // Helper method for setting page title
269
+ setTitle: useCallback((newTitle: string) => {
270
+ document.title = newTitle;
271
+ }, []),
272
+ };
273
+ };
274
+
275
+ /**
276
+ * Base Page Class Component - Extendable page component for inheritance pattern
277
+ *
278
+ * Usage:
279
+ * class MyPage extends BasePage {
280
+ * getPageProps() {
281
+ * return {
282
+ * route: '/my-page',
283
+ * variant: 'narrow',
284
+ * padding: 'large',
285
+ * // ... other page props
286
+ * };
287
+ * }
288
+ *
289
+ * renderContent() {
290
+ * return (
291
+ * <div>My page content</div>
292
+ * );
293
+ * }
294
+ * }
295
+ */
296
+ export abstract class BasePage<P = {}, S = {}> extends React.Component<P, S> {
297
+ /**
298
+ * Override this method to provide page configuration
299
+ * This will be passed to the Page component wrapper
300
+ */
301
+ abstract getPageProps(): Partial<PageProps>;
302
+
303
+ /**
304
+ * Override this method to provide the page content
305
+ * This replaces the render() method in your page components
306
+ */
307
+ abstract renderContent(): React.ReactNode;
308
+
309
+ /**
310
+ * Override this method to handle page mounting logic
311
+ * Called after the page is mounted and context is available
312
+ */
313
+ onPageMount?(): void;
314
+
315
+ /**
316
+ * Override this method to handle page unmounting logic
317
+ */
318
+ onPageUnmount?(): void;
319
+
320
+ /**
321
+ * Internal render method - wraps content in Page component
322
+ */
323
+ render() {
324
+ const pageProps = this.getPageProps();
325
+
326
+ return (
327
+ <Page {...pageProps}>
328
+ {this.renderContent()}
329
+ </Page>
330
+ );
331
+ }
332
+
333
+ /**
334
+ * Component lifecycle methods
335
+ */
336
+ componentDidMount() {
337
+ this.onPageMount?.();
338
+ }
339
+
340
+ componentWillUnmount() {
341
+ this.onPageUnmount?.();
342
+ }
343
+ }
344
+
345
+ export default Page;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * All page components available in QwickApps React Framework.
3
+ *
4
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
5
+ */
6
+ export { BasePage } from './Page';
7
+ export { default as FormPage } from './FormPage';
8
+ export { default as Page } from './Page';
9
+
10
+ export type { FormPageProps } from './FormPage';
11
+ export type { PageProps } from './Page';
@@ -0,0 +1,355 @@
1
+ /**
2
+ * DataContext - React Context for Data Provider
3
+ *
4
+ * Provides data templating capabilities throughout the React component tree
5
+ * via React Context API. Supports mustache template resolution and caching.
6
+ * Enables JSX components to use mustache syntax to reference named data elements.
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+
11
+ import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
12
+ import { ITemplateResolver, TemplateResolverConfig } from '../types';
13
+ import { TemplateResolver } from '../templates';
14
+ import { Model } from '@qwickapps/schema';
15
+
16
+ /**
17
+ * Data Context interface
18
+ */
19
+ interface DataContextValue {
20
+ provider: ITemplateResolver | null;
21
+ get<T extends Model>(slug: string): Promise<T | undefined>;
22
+ select<T extends Model>(schema: string, options?: any): Promise<T[]>;
23
+ resolveTemplate(template: string): Promise<string>;
24
+ }
25
+
26
+ /**
27
+ * React Context for data provider
28
+ */
29
+ const DataContext = createContext<DataContextValue | null>(null);
30
+
31
+ /**
32
+ * Props for DataProvider component
33
+ */
34
+ export interface DataProviderProps {
35
+ /** The data source configuration (follows new SRP architecture) */
36
+ dataSource: TemplateResolverConfig;
37
+ /** Child components that will have access to data templating */
38
+ children: ReactNode;
39
+ }
40
+
41
+ /**
42
+ * DataProvider component that wraps children with data templating capabilities
43
+ *
44
+ * Enables JSX components to use mustache syntax for data interpolation:
45
+ * - {t`Welcome {{company.name}}!`}
46
+ * - {useData('users')} for data fetching
47
+ * - Template resolution with caching support
48
+ *
49
+ * Usage (new SRP architecture):
50
+ * ```tsx
51
+ * <DataProvider dataSource={{
52
+ * dataProvider: new JsonDataProvider({ data: { ... } }),
53
+ * cache: true,
54
+ * templateResolver: new MustacheTemplateResolver()
55
+ * }}>
56
+ * <MyComponent /> // Can use useData() hooks and t`` templates
57
+ * </DataProvider>
58
+ * ```
59
+ */
60
+ export function DataProvider({ dataSource, children }: DataProviderProps) {
61
+ // Create TemplateResolver internally
62
+ const dataProvider = new TemplateResolver(dataSource);
63
+
64
+ const contextValue: DataContextValue = {
65
+ provider: dataProvider,
66
+ get: async <T extends Model>(slug: string): Promise<T | undefined> => {
67
+ const result = await dataProvider.get(slug);
68
+ return result && result.data ? (result.data as T) : undefined;
69
+ },
70
+ select: async <T extends Model>(schema: string, options?: any): Promise<T[]> => {
71
+ const result = await dataProvider.select(schema, options);
72
+ return result && Array.isArray(result.data) ? (result.data as T[]) : [];
73
+ },
74
+ resolveTemplate: (template: string) => dataProvider.resolveTemplate(template)
75
+ };
76
+
77
+ return (
78
+ <DataContext.Provider value={contextValue}>
79
+ {children}
80
+ </DataContext.Provider>
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Hook to access the data context
86
+ *
87
+ * @throws Error if used outside of DataProvider
88
+ */
89
+ export function useDataContext(): DataContextValue {
90
+ const context = useContext(DataContext);
91
+ if (!context) {
92
+ throw new Error('useDataContext must be used within a DataProvider. Did you wrap your component with <DataProvider>?');
93
+ }
94
+ return context;
95
+ }
96
+
97
+ /**
98
+ * Hook to get data by unique slug (safe version that doesn't throw)
99
+ *
100
+ * @param slug - The unique slug to fetch data for
101
+ * @returns Data with loading state information
102
+ */
103
+ export function useDataSafe<T extends Model>(slug: string): {
104
+ data: T | undefined;
105
+ loading: boolean;
106
+ error: Error | null;
107
+ } {
108
+ const context = useContext(DataContext);
109
+ if (!context) {
110
+ return { data: undefined, loading: false, error: null };
111
+ }
112
+
113
+ const [data, setData] = useState<T | undefined>(undefined);
114
+ const [loading, setLoading] = useState(true);
115
+ const [error, setError] = useState<Error | null>(null);
116
+
117
+ const fetchData = async () => {
118
+ if (!slug) {
119
+ setLoading(false);
120
+ return;
121
+ }
122
+
123
+ try {
124
+ setLoading(true);
125
+ setError(null);
126
+ const result = await context.get<T>(slug);
127
+ setData(result);
128
+ setLoading(false);
129
+ } catch (err) {
130
+ setError(err instanceof Error ? err : new Error(String(err)));
131
+ setLoading(false);
132
+ }
133
+ };
134
+
135
+ useEffect(() => {
136
+ fetchData();
137
+ }, [slug]);
138
+
139
+ return { data, loading, error };
140
+ }
141
+
142
+ /**
143
+ * Hook to get data by field group ID
144
+ *
145
+ * @param fieldGroupId - The field group to fetch
146
+ * @returns Data and loading state
147
+ */
148
+ export function useData<T extends Model>(fieldGroupId: string): {
149
+ data: T | undefined;
150
+ loading: boolean;
151
+ error: Error | null;
152
+ refetch: () => void;
153
+ } {
154
+ const { get } = useDataContext();
155
+ const [data, setData] = useState<T | undefined>(undefined);
156
+ const [loading, setLoading] = useState(true);
157
+ const [error, setError] = useState<Error | null>(null);
158
+
159
+ const fetchData = async () => {
160
+ if (!fieldGroupId) {
161
+ setLoading(false);
162
+ return;
163
+ }
164
+
165
+ try {
166
+ setLoading(true);
167
+ setError(null);
168
+ const result = await get<T>(fieldGroupId);
169
+ setData(result ? result : undefined);
170
+ setLoading(false);
171
+ } catch (err) {
172
+ setError(err instanceof Error ? err : new Error(String(err)));
173
+ setLoading(false);
174
+ }
175
+ };
176
+
177
+ useEffect(() => {
178
+ fetchData();
179
+ }, [fieldGroupId, get]);
180
+
181
+ return {
182
+ data,
183
+ loading,
184
+ error,
185
+ refetch: fetchData
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Hook to resolve a template string with mustache syntax
191
+ *
192
+ * @param template - Template string with {{fieldGroup.property}} syntax
193
+ * @returns Resolved template string and loading state
194
+ */
195
+ export function useResolveTemplate(template: string) {
196
+ const { resolveTemplate } = useDataContext();
197
+ const [resolved, setResolved] = useState(template);
198
+ const [loading, setLoading] = useState(false);
199
+ const [error, setError] = useState<Error | null>(null);
200
+
201
+ useEffect(() => {
202
+ let isMounted = true;
203
+
204
+ async function resolve() {
205
+ if (!template || !template.includes('{{')) {
206
+ setResolved(template);
207
+ return;
208
+ }
209
+
210
+ try {
211
+ setLoading(true);
212
+ setError(null);
213
+ const result = await resolveTemplate(template);
214
+
215
+ if (isMounted) {
216
+ setResolved(result);
217
+ setLoading(false);
218
+ }
219
+ } catch (err) {
220
+ if (isMounted) {
221
+ setError(err instanceof Error ? err : new Error(String(err)));
222
+ setResolved(template); // Fallback to original template
223
+ setLoading(false);
224
+ }
225
+ }
226
+ }
227
+
228
+ resolve();
229
+
230
+ return () => {
231
+ isMounted = false;
232
+ };
233
+ }, [template, resolveTemplate]);
234
+
235
+ return { resolved, loading, error };
236
+ }
237
+
238
+ /**
239
+ * Hook to access data provider directly for advanced use cases
240
+ */
241
+ export function useDataProvider(): ITemplateResolver {
242
+ const { provider } = useDataContext();
243
+ if (!provider) {
244
+ throw new Error('Data provider not available');
245
+ }
246
+ return provider;
247
+ }
248
+
249
+ /**
250
+ * Template component for inline template resolution
251
+ *
252
+ * This is a React-friendly component for template resolution
253
+ * that works well with conditional rendering and fallbacks.
254
+ *
255
+ * Usage:
256
+ * ```tsx
257
+ * <T template="Welcome to {{company.name}}!" fallback="Loading..." />
258
+ * <T template="Hello {{user.name}}" fallback="Guest" />
259
+ * ```
260
+ */
261
+ export const T: React.FC<{ template: string; fallback?: ReactNode; wrapper?: React.ComponentType<{ children: ReactNode }> }> = ({
262
+ template,
263
+ fallback,
264
+ wrapper: Wrapper
265
+ }) => {
266
+ try {
267
+ const context = useContext(DataContext);
268
+ if (!context) {
269
+ throw new Error('T component must be used within a DataProvider. Did you wrap your component with <DataProvider>?');
270
+ }
271
+
272
+ const { resolved, loading, error } = useResolveTemplate(template);
273
+
274
+ if (loading) {
275
+ return fallback ? <>{fallback}</> : null;
276
+ } else if (error) {
277
+ if (process.env.NODE_ENV === 'development') {
278
+ return <>[Template Error: {error.message}]</>;
279
+ } else {
280
+ return fallback ? <>{fallback}</> : null;
281
+ }
282
+ } else {
283
+ const content = <>{resolved}</>;
284
+ return Wrapper ? <Wrapper>{content}</Wrapper> : content;
285
+ }
286
+ } catch (error) {
287
+ throw error;
288
+ }
289
+ };
290
+
291
+ /**
292
+ * Hook to resolve template with tagged template literal syntax
293
+ *
294
+ * This returns the resolved value directly so it can be used with || operator
295
+ */
296
+ export function useTemplate(template: string): string | null {
297
+ const { resolved, loading, error } = useResolveTemplate(template);
298
+
299
+ if (loading) {
300
+ return null;
301
+ } else if (error) {
302
+ if (process.env.NODE_ENV === 'development') {
303
+ return `[Template Error: ${error.message}]`;
304
+ } else {
305
+ return null;
306
+ }
307
+ } else {
308
+ return resolved;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Tagged template function for inline data templating
314
+ *
315
+ * Enables JSX components to use mustache syntax for data interpolation:
316
+ *
317
+ * Usage:
318
+ * ```tsx
319
+ * {t`Welcome to {{company.name}}!`}
320
+ * {t`Hello {{user.name}}` || 'Loading...'}
321
+ * {t.wrap(CustomComponent)`{{company.slogan}}`}
322
+ * ```
323
+ */
324
+ export function t(strings: TemplateStringsArray, ...values: any[]): ReactNode {
325
+ // First do regular template literal interpolation
326
+ const template = String.raw(strings, ...values);
327
+
328
+ // Return a component that uses the hook
329
+ const TemplateResolver = () => {
330
+ const resolved = useTemplate(template);
331
+ return resolved;
332
+ };
333
+
334
+ return <TemplateResolver />;
335
+ }
336
+
337
+ /**
338
+ * Tagged template function with custom wrapper component
339
+ *
340
+ * Usage:
341
+ * ```tsx
342
+ * {t.wrap(QuoteComponent)`{{company.slogan}}`}
343
+ * ```
344
+ */
345
+ t.wrap = (WrapperComponent: React.ComponentType<{ children: ReactNode }>) =>
346
+ (strings: TemplateStringsArray, ...values: any[]): ReactNode => {
347
+ const template = String.raw(strings, ...values);
348
+
349
+ const WrappedTemplate = () => {
350
+ const resolved = useTemplate(template);
351
+ return resolved ? <WrapperComponent>{resolved}</WrapperComponent> : null;
352
+ };
353
+
354
+ return <WrappedTemplate />;
355
+ };