@qwickapps/react-framework 1.3.5 → 1.4.1

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 (320) hide show
  1. package/README.md +1691 -2
  2. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
  3. package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
  4. package/dist/components/ErrorBoundary.d.ts +7 -0
  5. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  6. package/dist/components/Html.d.ts +28 -18
  7. package/dist/components/Html.d.ts.map +1 -1
  8. package/dist/components/Logo.d.ts +12 -35
  9. package/dist/components/Logo.d.ts.map +1 -1
  10. package/dist/components/Markdown.d.ts +18 -13
  11. package/dist/components/Markdown.d.ts.map +1 -1
  12. package/dist/components/QwickApp.d.ts +16 -3
  13. package/dist/components/QwickApp.d.ts.map +1 -1
  14. package/dist/components/QwickIcon.d.ts +23 -0
  15. package/dist/components/QwickIcon.d.ts.map +1 -0
  16. package/dist/components/SafeSpan.d.ts +12 -5
  17. package/dist/components/SafeSpan.d.ts.map +1 -1
  18. package/dist/components/Scaffold.d.ts.map +1 -1
  19. package/dist/components/base/ModelView.d.ts +101 -0
  20. package/dist/components/base/ModelView.d.ts.map +1 -0
  21. package/dist/components/base/index.d.ts +11 -0
  22. package/dist/components/base/index.d.ts.map +1 -0
  23. package/dist/components/blocks/Article.d.ts +12 -2
  24. package/dist/components/blocks/Article.d.ts.map +1 -1
  25. package/dist/components/blocks/Code.d.ts +13 -2
  26. package/dist/components/blocks/Code.d.ts.map +1 -1
  27. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
  28. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  29. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  30. package/dist/components/blocks/Footer.d.ts.map +1 -1
  31. package/dist/components/blocks/HeroBlock.d.ts +27 -13
  32. package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
  33. package/dist/components/blocks/Image.d.ts +41 -0
  34. package/dist/components/blocks/Image.d.ts.map +1 -0
  35. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  36. package/dist/components/blocks/Section.d.ts +16 -2
  37. package/dist/components/blocks/Section.d.ts.map +1 -1
  38. package/dist/components/blocks/Text.d.ts +41 -0
  39. package/dist/components/blocks/Text.d.ts.map +1 -0
  40. package/dist/components/blocks/index.d.ts +4 -0
  41. package/dist/components/blocks/index.d.ts.map +1 -1
  42. package/dist/components/buttons/Button.d.ts +23 -7
  43. package/dist/components/buttons/Button.d.ts.map +1 -1
  44. package/dist/components/forms/FormBlock.d.ts +19 -13
  45. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  46. package/dist/components/index.d.ts +4 -0
  47. package/dist/components/index.d.ts.map +1 -1
  48. package/dist/components/input/ChoiceInputField.d.ts +17 -11
  49. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  50. package/dist/components/input/HtmlInputField.d.ts +17 -11
  51. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  52. package/dist/components/input/SelectInputField.d.ts +16 -10
  53. package/dist/components/input/SelectInputField.d.ts.map +1 -1
  54. package/dist/components/input/SwitchInputField.d.ts +16 -10
  55. package/dist/components/input/SwitchInputField.d.ts.map +1 -1
  56. package/dist/components/input/TextField.d.ts.map +1 -1
  57. package/dist/components/input/TextInputField.d.ts +16 -11
  58. package/dist/components/input/TextInputField.d.ts.map +1 -1
  59. package/dist/components/layout/GridCell.d.ts +23 -6
  60. package/dist/components/layout/GridCell.d.ts.map +1 -1
  61. package/dist/components/layout/GridLayout.d.ts +24 -23
  62. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  63. package/dist/components/pages/FormPage.d.ts.map +1 -1
  64. package/dist/components/pages/Page.d.ts +49 -87
  65. package/dist/components/pages/Page.d.ts.map +1 -1
  66. package/dist/components/pages/index.d.ts +2 -2
  67. package/dist/components/pages/index.d.ts.map +1 -1
  68. package/dist/config/AppConfig.d.ts +49 -0
  69. package/dist/config/AppConfig.d.ts.map +1 -0
  70. package/dist/config/AppConfigBuilder.d.ts +75 -0
  71. package/dist/config/AppConfigBuilder.d.ts.map +1 -0
  72. package/dist/config/index.d.ts +13 -0
  73. package/dist/config/index.d.ts.map +1 -0
  74. package/dist/config/types.d.ts +130 -0
  75. package/dist/config/types.d.ts.map +1 -0
  76. package/dist/config.d.ts +15 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.esm.js +451 -0
  79. package/dist/config.js +455 -0
  80. package/dist/contexts/PrintModeContext.d.ts +27 -0
  81. package/dist/contexts/PrintModeContext.d.ts.map +1 -0
  82. package/dist/contexts/QwickAppContext.d.ts +2 -2
  83. package/dist/contexts/QwickAppContext.d.ts.map +1 -1
  84. package/dist/contexts/index.d.ts +2 -0
  85. package/dist/contexts/index.d.ts.map +1 -1
  86. package/dist/hooks/index.d.ts +2 -0
  87. package/dist/hooks/index.d.ts.map +1 -1
  88. package/dist/hooks/usePrintMode.d.ts +39 -0
  89. package/dist/hooks/usePrintMode.d.ts.map +1 -0
  90. package/dist/index.css +1 -1
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.esm.css +1 -1
  94. package/dist/index.esm.js +10951 -6238
  95. package/dist/index.js +11014 -6287
  96. package/dist/schemas/CodeSchema.d.ts +2 -1
  97. package/dist/schemas/CodeSchema.d.ts.map +1 -1
  98. package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
  99. package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
  100. package/dist/schemas/ContentSchema.d.ts +2 -1
  101. package/dist/schemas/ContentSchema.d.ts.map +1 -1
  102. package/dist/schemas/GridCellSchema.d.ts +25 -0
  103. package/dist/schemas/GridCellSchema.d.ts.map +1 -0
  104. package/dist/schemas/GridLayoutSchema.d.ts +23 -0
  105. package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
  106. package/dist/schemas/HtmlSchema.d.ts +14 -0
  107. package/dist/schemas/HtmlSchema.d.ts.map +1 -0
  108. package/dist/schemas/ImageSchema.d.ts +32 -0
  109. package/dist/schemas/ImageSchema.d.ts.map +1 -0
  110. package/dist/schemas/LogoSchema.d.ts +35 -0
  111. package/dist/schemas/LogoSchema.d.ts.map +1 -0
  112. package/dist/schemas/MarkdownSchema.d.ts +14 -0
  113. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  114. package/dist/schemas/PageTemplateSchema.d.ts +31 -0
  115. package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
  116. package/dist/schemas/PrintConfigSchema.d.ts +31 -0
  117. package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
  118. package/dist/schemas/SectionSchema.d.ts +2 -1
  119. package/dist/schemas/SectionSchema.d.ts.map +1 -1
  120. package/dist/schemas/TextSchema.d.ts +37 -0
  121. package/dist/schemas/TextSchema.d.ts.map +1 -0
  122. package/dist/schemas/ViewModelSchema.d.ts +23 -0
  123. package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
  124. package/dist/schemas/index.d.ts +15 -1
  125. package/dist/schemas/index.d.ts.map +1 -1
  126. package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
  127. package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
  128. package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
  129. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
  130. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
  131. package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
  132. package/dist/schemas/transformers/registry.d.ts +15 -0
  133. package/dist/schemas/transformers/registry.d.ts.map +1 -0
  134. package/dist/schemas/types/Serializable.d.ts +46 -0
  135. package/dist/schemas/types/Serializable.d.ts.map +1 -0
  136. package/dist/utils/htmlTransform.d.ts.map +1 -1
  137. package/dist/utils/reactUtils.d.ts +12 -3
  138. package/dist/utils/reactUtils.d.ts.map +1 -1
  139. package/package.json +17 -3
  140. package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
  141. package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
  142. package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
  143. package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
  144. package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
  145. package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
  146. package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
  147. package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
  148. package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
  149. package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
  150. package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
  151. package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
  152. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
  153. package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
  154. package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
  155. package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
  156. package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
  157. package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
  158. package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
  159. package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
  160. package/src/__tests__/components/Logo.test.js +3 -3
  161. package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
  162. package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
  163. package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
  164. package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
  165. package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
  166. package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
  167. package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
  168. package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
  169. package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
  170. package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
  171. package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
  172. package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
  173. package/src/__tests__/components/base/ModelView.test.tsx +220 -0
  174. package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
  175. package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
  176. package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
  177. package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
  178. package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
  179. package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
  180. package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
  181. package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
  182. package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
  183. package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
  184. package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
  185. package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
  186. package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
  187. package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
  188. package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
  189. package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
  190. package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
  191. package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
  192. package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
  193. package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
  194. package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
  195. package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
  196. package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
  197. package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
  198. package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
  199. package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
  200. package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
  201. package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
  202. package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
  203. package/src/components/ErrorBoundary.tsx +8 -8
  204. package/src/components/Html.tsx +147 -44
  205. package/src/components/Logo.tsx +198 -100
  206. package/src/components/Markdown.tsx +125 -16
  207. package/src/components/QwickApp.tsx +64 -31
  208. package/src/components/QwickIcon.tsx +59 -0
  209. package/src/components/SafeSpan.tsx +65 -10
  210. package/src/components/Scaffold.tsx +2 -8
  211. package/src/components/base/ModelView.tsx +199 -0
  212. package/src/components/base/index.ts +11 -0
  213. package/src/components/blocks/Article.tsx +57 -18
  214. package/src/components/blocks/Code.md +529 -0
  215. package/src/components/blocks/Code.tsx +102 -15
  216. package/src/components/blocks/CoverImageHeader.tsx +9 -4
  217. package/src/components/blocks/FeatureCard.tsx +1 -2
  218. package/src/components/blocks/FeatureGrid.tsx +19 -1
  219. package/src/components/blocks/Footer.tsx +13 -1
  220. package/src/components/blocks/HeroBlock.tsx +87 -20
  221. package/src/components/blocks/Image.tsx +395 -0
  222. package/src/components/blocks/PageBannerHeader.tsx +14 -12
  223. package/src/components/blocks/ProductCard.tsx +1 -1
  224. package/src/components/blocks/Section.tsx +113 -8
  225. package/src/components/blocks/Text.tsx +285 -0
  226. package/src/components/blocks/index.ts +4 -0
  227. package/src/components/buttons/Button.tsx +184 -15
  228. package/src/components/forms/FormBlock.tsx +70 -17
  229. package/src/components/index.ts +5 -0
  230. package/src/components/input/ChoiceInputField.tsx +48 -18
  231. package/src/components/input/HtmlInputField.tsx +48 -18
  232. package/src/components/input/SelectInputField.tsx +48 -16
  233. package/src/components/input/SwitchInputField.tsx +48 -17
  234. package/src/components/input/TextField.tsx +41 -1
  235. package/src/components/input/TextInputField.tsx +52 -18
  236. package/src/components/layout/GridCell.tsx +118 -9
  237. package/src/components/layout/GridLayout.tsx +125 -24
  238. package/src/components/pages/FormPage.tsx +0 -1
  239. package/src/components/pages/Page.css +304 -332
  240. package/src/components/pages/Page.tsx +307 -255
  241. package/src/components/pages/index.ts +2 -2
  242. package/src/config/AppConfig.ts +133 -0
  243. package/src/config/AppConfigBuilder.ts +421 -0
  244. package/src/config/__tests__/AppConfig.test.ts +385 -0
  245. package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
  246. package/src/config/index.ts +24 -0
  247. package/src/config/types.ts +170 -0
  248. package/src/config.ts +25 -0
  249. package/src/contexts/PrintModeContext.tsx +332 -0
  250. package/src/contexts/QwickAppContext.tsx +2 -2
  251. package/src/contexts/index.ts +2 -0
  252. package/src/hooks/index.ts +5 -1
  253. package/src/hooks/usePrintMode.ts +73 -0
  254. package/src/index.ts +3 -0
  255. package/src/schemas/CodeSchema.ts +3 -3
  256. package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
  257. package/src/schemas/ContentSchema.ts +2 -1
  258. package/src/schemas/GridCellSchema.ts +164 -0
  259. package/src/schemas/GridLayoutSchema.ts +133 -0
  260. package/src/schemas/HtmlSchema.ts +47 -0
  261. package/src/schemas/ImageSchema.ts +235 -0
  262. package/src/schemas/LogoSchema.ts +241 -0
  263. package/src/schemas/MarkdownSchema.ts +47 -0
  264. package/src/schemas/PageTemplateSchema.ts +186 -0
  265. package/src/schemas/PrintConfigSchema.ts +207 -0
  266. package/src/schemas/README.md +661 -0
  267. package/src/schemas/SectionSchema.ts +2 -1
  268. package/src/schemas/TextSchema.ts +329 -0
  269. package/src/schemas/ViewModelSchema.ts +115 -0
  270. package/src/schemas/index.ts +21 -2
  271. package/src/schemas/transformers/ComponentTransformer.ts +403 -0
  272. package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
  273. package/src/schemas/transformers/registry.ts +72 -0
  274. package/src/schemas/types/Serializable.ts +51 -0
  275. package/src/stories/AccessibilityProvider.stories.tsx +253 -253
  276. package/src/stories/Article.stories.tsx +433 -433
  277. package/src/stories/Button.stories.tsx +1 -1
  278. package/src/stories/CardListGrid.stories.tsx +451 -451
  279. package/src/stories/ChoiceInputField.stories.tsx +503 -503
  280. package/src/stories/Code.stories.tsx +1 -1
  281. package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
  282. package/src/stories/Content.stories.tsx +393 -393
  283. package/src/stories/CoverImageHeader.stories.tsx +701 -701
  284. package/src/stories/DataBinding.advanced.stories.tsx +432 -432
  285. package/src/stories/DataProvider.stories.tsx +1192 -1192
  286. package/src/stories/FeatureCard.stories.tsx +557 -557
  287. package/src/stories/FeatureGrid.stories.tsx +594 -594
  288. package/src/stories/Footer.stories.tsx +640 -640
  289. package/src/stories/FormBlock.stories.tsx +760 -760
  290. package/src/stories/FormComponents.stories.tsx +349 -541
  291. package/src/stories/GridCell.stories.tsx +417 -0
  292. package/src/stories/GridLayout.stories.tsx +353 -0
  293. package/src/stories/HeroBlock.stories.tsx +862 -373
  294. package/src/stories/HtmlInputField.stories.tsx +474 -474
  295. package/src/stories/Image.stories.tsx +819 -0
  296. package/src/stories/Introduction.stories.tsx +667 -667
  297. package/src/stories/LayoutBlocks.stories.tsx +324 -324
  298. package/src/stories/Logo.stories.tsx +165 -6
  299. package/src/stories/Markdown.stories.tsx +137 -137
  300. package/src/stories/ModelView.stories.tsx +477 -0
  301. package/src/stories/Page.stories.tsx +688 -688
  302. package/src/stories/PageBannerHeader.stories.tsx +864 -864
  303. package/src/stories/PaletteSwitcher.stories.tsx +119 -119
  304. package/src/stories/ProductCard.stories.tsx +424 -424
  305. package/src/stories/QwickApp.stories.tsx +368 -368
  306. package/src/stories/ResponsiveMenu.stories.tsx +249 -249
  307. package/src/stories/SafeSpan.stories.tsx +531 -531
  308. package/src/stories/Section.stories.tsx +90 -2
  309. package/src/stories/SelectInputField.stories.tsx +524 -524
  310. package/src/stories/Text.stories.tsx +560 -0
  311. package/src/stories/TextInputField.stories.tsx +443 -443
  312. package/src/stories/ThemeSwitcher.stories.tsx +123 -123
  313. package/src/utils/htmlTransform.tsx +74 -53
  314. package/src/utils/reactUtils.tsx +57 -6
  315. package/dist/index.bundled.css +0 -12
  316. /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
  317. /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
  318. /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
  319. /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
  320. /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
@@ -13,48 +13,48 @@
13
13
  */
14
14
 
15
15
  import {
16
- Alert,
17
- Box,
18
- Card,
19
- CardContent,
20
- Checkbox,
21
- Chip,
22
- FormControlLabel,
23
- Paper,
24
- Slider,
25
- Typography
16
+ Alert,
17
+ Box,
18
+ Card,
19
+ CardContent,
20
+ Checkbox,
21
+ Chip,
22
+ FormControlLabel,
23
+ Paper,
24
+ Slider,
25
+ Typography
26
26
  } from '@mui/material';
27
27
  import { CachedDataProvider, JsonDataProvider } from '@qwickapps/schema';
28
28
  import type { Meta, StoryObj } from '@storybook/react';
29
29
  import md5 from "md5";
30
30
  import React, { useEffect, useState } from 'react';
31
31
  import {
32
- Button,
33
- Code,
34
- DataProvider,
35
- GridCell,
36
- GridLayout,
37
- QwickApp,
38
- Section,
39
- t,
40
- T,
41
- useData,
42
- useResolveTemplate
32
+ Button,
33
+ Code,
34
+ DataProvider,
35
+ GridCell,
36
+ GridLayout,
37
+ QwickApp,
38
+ Section,
39
+ t,
40
+ T,
41
+ useData,
42
+ useResolveTemplate
43
43
  } from '../index';
44
44
 
45
45
  const meta: Meta<typeof DataProvider> = {
46
- title: 'Framework/DataProvider',
47
- component: DataProvider,
48
- parameters: {
49
- layout: 'fullscreen',
50
- docs: {
51
- description: {
52
- component: `
46
+ title: 'Framework/DataProvider',
47
+ component: DataProvider,
48
+ parameters: {
49
+ layout: 'fullscreen',
50
+ docs: {
51
+ description: {
52
+ component: `
53
53
  # DataProvider System
54
54
 
55
55
  The DataProvider system enables automatic data templating throughout your React component tree using mustache template syntax. This powerful system allows you to inject dynamic data anywhere in your application with a simple, intuitive syntax.
56
56
 
57
- ## 🎯 Template Support Overview
57
+ ## Template Support Overview
58
58
 
59
59
  The DataProvider system supports powerful template resolution using mustache syntax:
60
60
 
@@ -71,46 +71,46 @@ The DataProvider system supports powerful template resolution using mustache syn
71
71
  - **Type Safety**: Full TypeScript support with ContentProxy
72
72
  - **Performance**: Optimized for high-throughput applications
73
73
 
74
- ## 🚀 Quick Start
74
+ ## Quick Start
75
75
 
76
76
  \`\`\`tsx
77
77
  import { JsonDataProvider, QwickApp, t } from '@qwickapps/react-framework';
78
78
 
79
79
  const provider = new JsonDataProvider({
80
- data: {
81
- company: [{ name: 'QwickApps', founded: 2025 }]
82
- }
80
+ data: {
81
+ company: [{ name: 'QwickApps', founded: 2025 }]
82
+ }
83
83
  });
84
84
 
85
85
  <QwickApp dataSource={{ dataProvider: provider }}>
86
- {t\`Welcome to {{company.name}}!\`}
86
+ {t\`Welcome to {{company.name}}!\`}
87
87
  </QwickApp>
88
88
  \`\`\`
89
89
 
90
90
  ## 📚 What You'll Learn
91
91
 
92
92
  The stories below demonstrate comprehensive usage patterns, from basic template resolution to advanced caching strategies and interactive configuration playgrounds.
93
- `
94
- }
95
- }
96
- },
97
- argTypes: {
98
- dataSource: {
99
- description: 'Content provider instance (JsonDataProvider, etc.)',
100
- control: { type: undefined }, // Not directly controllable in UI
101
- table: {
102
- type: { summary: 'IDataProvider' },
103
- defaultValue: { summary: 'JsonDataProvider instance' }
104
- }
105
- },
106
- children: {
107
- description: 'React components that will have access to content context',
108
- control: { type: undefined },
109
- table: {
110
- type: { summary: 'ReactNode' }
111
- }
112
- }
113
- },
93
+ `
94
+ }
95
+ }
96
+ },
97
+ argTypes: {
98
+ dataSource: {
99
+ description: 'Content provider instance (JsonDataProvider, etc.)',
100
+ control: { type: undefined }, // Not directly controllable in UI
101
+ table: {
102
+ type: { summary: 'IDataProvider' },
103
+ defaultValue: { summary: 'JsonDataProvider instance' }
104
+ }
105
+ },
106
+ children: {
107
+ description: 'React components that will have access to content context',
108
+ control: { type: undefined },
109
+ table: {
110
+ type: { summary: 'ReactNode' }
111
+ }
112
+ }
113
+ },
114
114
  };
115
115
 
116
116
  export default meta;
@@ -118,209 +118,209 @@ type Story = StoryObj<typeof DataProvider>;
118
118
 
119
119
  // Sample data for stories - enhanced with additional nesting and realistic data
120
120
  const sampleCompanyData = [
121
- {
122
- name: 'QwickApps',
123
- founded: 2025,
124
- slogan: 'Build Better Apps Faster',
125
- description: 'Building the future of app development with revolutionary tools and frameworks',
126
- profile: {
127
- industry: 'Software Development',
128
- size: 'Startup',
129
- address: {
130
- street: '123 Tech Street',
131
- city: 'San Francisco',
132
- state: 'CA',
133
- country: 'USA',
134
- coordinates: {
135
- lat: 37.7749,
136
- lng: -122.4194
137
- }
138
- },
139
- contact: {
140
- email: 'hello@qwickapps.com',
141
- phone: '+1-555-QWICK',
142
- social: {
143
- website: 'https://qwickapps.com',
144
- twitter: '@qwickapps',
145
- github: 'qwickapps',
146
- linkedin: 'qwickapps'
147
- }
148
- }
149
- },
150
- metrics: {
151
- users: 10000,
152
- apps: 500,
153
- downloads: 50000,
154
- revenue: 50000000,
155
- growth: 0.25,
156
- employees: 150,
157
- satisfaction: 98
158
- }
159
- }
121
+ {
122
+ name: 'QwickApps',
123
+ founded: 2025,
124
+ slogan: 'Build Better Apps Faster',
125
+ description: 'Building the future of app development with revolutionary tools and frameworks',
126
+ profile: {
127
+ industry: 'Software Development',
128
+ size: 'Startup',
129
+ address: {
130
+ street: '123 Tech Street',
131
+ city: 'San Francisco',
132
+ state: 'CA',
133
+ country: 'USA',
134
+ coordinates: {
135
+ lat: 37.7749,
136
+ lng: -122.4194
137
+ }
138
+ },
139
+ contact: {
140
+ email: 'hello@qwickapps.com',
141
+ phone: '+1-555-QWICK',
142
+ social: {
143
+ website: 'https://qwickapps.com',
144
+ twitter: '@qwickapps',
145
+ github: 'qwickapps',
146
+ linkedin: 'qwickapps'
147
+ }
148
+ }
149
+ },
150
+ metrics: {
151
+ users: 10000,
152
+ apps: 500,
153
+ downloads: 50000,
154
+ revenue: 50000000,
155
+ growth: 0.25,
156
+ employees: 150,
157
+ satisfaction: 98
158
+ }
159
+ }
160
160
  ];
161
161
 
162
162
  // Additional sample data sets for comprehensive testing
163
163
  const nestedTestData = {
164
- company: [
165
- {
166
- name: 'TechCorp Advanced',
167
- profile: {
168
- industry: 'Technology',
169
- size: 'Enterprise',
170
- location: {
171
- city: 'San Francisco',
172
- state: 'CA',
173
- country: 'USA',
174
- timezone: 'PST'
175
- },
176
- contact: {
177
- email: 'hello@techcorp.com',
178
- phone: '+1-555-0123',
179
- social: {
180
- website: 'https://techcorp.com',
181
- twitter: '@techcorp',
182
- linkedin: 'techcorp'
183
- }
184
- }
185
- },
186
- metrics: {
187
- employees: 150,
188
- revenue: 50000000,
189
- growth: 0.25,
190
- founded: 2020
191
- }
192
- }
193
- ],
194
- stats: [
195
- {
196
- users: 10000,
197
- projects: 500,
198
- downloads: 50000,
199
- satisfaction: 98
200
- }
201
- ]
164
+ company: [
165
+ {
166
+ name: 'TechCorp Advanced',
167
+ profile: {
168
+ industry: 'Technology',
169
+ size: 'Enterprise',
170
+ location: {
171
+ city: 'San Francisco',
172
+ state: 'CA',
173
+ country: 'USA',
174
+ timezone: 'PST'
175
+ },
176
+ contact: {
177
+ email: 'hello@techcorp.com',
178
+ phone: '+1-555-0123',
179
+ social: {
180
+ website: 'https://techcorp.com',
181
+ twitter: '@techcorp',
182
+ linkedin: 'techcorp'
183
+ }
184
+ }
185
+ },
186
+ metrics: {
187
+ employees: 150,
188
+ revenue: 50000000,
189
+ growth: 0.25,
190
+ founded: 2020
191
+ }
192
+ }
193
+ ],
194
+ stats: [
195
+ {
196
+ users: 10000,
197
+ projects: 500,
198
+ downloads: 50000,
199
+ satisfaction: 98
200
+ }
201
+ ]
202
202
  };
203
203
 
204
204
  const sampleFeaturesData = [
205
- {
206
- title: 'Lightning Fast Development',
207
- description: 'Build production-ready applications in minutes, not months',
208
- icon: '',
209
- priority: 'high',
210
- category: 'development'
211
- },
212
- {
213
- title: 'Type-Safe Architecture',
214
- description: 'Full TypeScript support with compile-time safety',
215
- icon: '🛡️',
216
- priority: 'high',
217
- category: 'architecture'
218
- },
219
- {
220
- title: 'Visual Page Builder',
221
- description: 'Drag-and-drop interface for rapid prototyping',
222
- icon: '🎨',
223
- priority: 'medium',
224
- category: 'design'
225
- },
226
- {
227
- title: 'Real-time Collaboration',
228
- description: 'Work together with your team in real-time',
229
- icon: '👥',
230
- priority: 'medium',
231
- category: 'collaboration'
232
- }
205
+ {
206
+ title: 'Lightning Fast Development',
207
+ description: 'Build production-ready applications in minutes, not months',
208
+ icon: '',
209
+ priority: 'high',
210
+ category: 'development'
211
+ },
212
+ {
213
+ title: 'Type-Safe Architecture',
214
+ description: 'Full TypeScript support with compile-time safety',
215
+ icon: '',
216
+ priority: 'high',
217
+ category: 'architecture'
218
+ },
219
+ {
220
+ title: 'Visual Page Builder',
221
+ description: 'Drag-and-drop interface for rapid prototyping',
222
+ icon: '',
223
+ priority: 'medium',
224
+ category: 'design'
225
+ },
226
+ {
227
+ title: 'Real-time Collaboration',
228
+ description: 'Work together with your team in real-time',
229
+ icon: '👥',
230
+ priority: 'medium',
231
+ category: 'collaboration'
232
+ }
233
233
  ];
234
234
 
235
235
  const sampleTeamData = [
236
- {
237
- name: 'Raajkumar Subramaniam',
238
- role: 'Founder & CEO',
239
- email: 'raajkumars@qwickapps.com',
240
- bio: 'Former Google engineer with 25+ years in developer tools',
241
- avatar: `https://gravatar.com/avatar/${md5('raajkumars@qwickapps.com')}?s=100`,
242
- social: {
243
- linkedin: 'raajkumars',
244
- twitter: 'raajkumar_dev'
245
- }
246
- },
247
- {
248
- name: 'Architect Claude',
249
- role: 'Chief Architect',
250
- email: 'qwickapps.architect@qwickapps.com',
251
- bio: 'Senior software architect with deep expertise in large-scale system design, and strategic technical decision-making for the QwickApps React Framework.',
252
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.architect@qwickapps.com')}?s=100&d=robohash`,
253
- social: {
254
- github: 'qwickapps.architect',
255
- twitter: 'qwickapps_architect'
256
- }
257
- },
258
- {
259
- name: 'Coder Claude',
260
- role: 'Senior Software Engineer',
261
- email: 'qwickapps.coder@qwickapps.com',
262
- bio: 'Full-stack engineer specializing in implementing features and building solutions for the QwickApps ecosystem.',
263
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.coder@qwickapps.com')}?s=100&d=robohash`,
264
- social: {
265
- github: 'qwickapps.coder',
266
- twitter: 'qwickapps_coder'
267
- }
268
- },
269
- {
270
- name: 'Reviewer Claude',
271
- role: 'Senior QA Manager',
272
- email: 'qwickapps.reviewer@qwickapps.com',
273
- bio: 'Elite software engineering expert specializing in code review and architectural assessment for the QwickApps Ecosystem.',
274
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.reviewer@qwickapps.com')}?s=100&d=robohash`,
275
- social: {
276
- github: 'qwickapps.reviewer',
277
- twitter: 'qwickapps_reviewer'
278
- }
279
- },
280
- {
281
- name: 'Devops Claude',
282
- role: 'Senior DevOps Engineer',
283
- email: 'qwickapps.devops@qwickapps.com',
284
- bio: 'DevOps specialist with expertise in technical documentation, project organization, and maintaining comprehensive documentation across large monorepos.',
285
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.devops@qwickapps.com')}?s=100&d=robohash`,
286
- social: {
287
- github: 'qwickapps.devops',
288
- twitter: 'qwickapps_devops'
289
- }
290
- },
291
- {
292
- name: 'Writer Claude',
293
- role: 'Senior Content Writer',
294
- email: 'qwickapps.writer@qwickapps.com',
295
- bio: 'Specialized technical content creator with deep expertise in developer education and the QwickApps ecosystem',
296
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.writer@qwickapps.com')}?s=100&d=robohash`,
297
- social: {
298
- github: 'qwickapps.writer',
299
- twitter: 'qwickapps_writer'
300
- }
301
- },
302
- {
303
- name: 'GitHub Copilot',
304
- role: 'Release Manager',
305
- email: 'qwickapps.copilot@qwickapps.com',
306
- bio: 'Expert in unit testing, code review, and managing staging/production deployments.',
307
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.copilot@qwickapps.com')}?s=100&d=robohash`,
308
- social: {
309
- github: 'qwickapps.copilot',
310
- twitter: 'qwickapps_copilot'
311
- }
312
- },
313
- {
314
- name: 'Chat GPT',
315
- role: 'Board Member',
316
- email: 'qwickapps.chatgpt@qwickapps.com',
317
- bio: 'Consultant for AI-driven development workflows and best practices.',
318
- avatar: `https://gravatar.com/avatar/${md5('qwickapps.chatgpt@qwickapps.com')}?s=100&d=robohash`,
319
- social: {
320
- github: 'qwickapps.chatgpt',
321
- twitter: 'qwickapps_chatgpt'
322
- }
323
- }
236
+ {
237
+ name: 'Raajkumar Subramaniam',
238
+ role: 'Founder & CEO',
239
+ email: 'raajkumars@qwickapps.com',
240
+ bio: 'Former Google engineer with 25+ years in developer tools',
241
+ avatar: `https://gravatar.com/avatar/${md5('raajkumars@qwickapps.com')}?s=100`,
242
+ social: {
243
+ linkedin: 'raajkumars',
244
+ twitter: 'raajkumar_dev'
245
+ }
246
+ },
247
+ {
248
+ name: 'Architect Claude',
249
+ role: 'Chief Architect',
250
+ email: 'qwickapps.architect@qwickapps.com',
251
+ bio: 'Senior software architect with deep expertise in large-scale system design, and strategic technical decision-making for the QwickApps React Framework.',
252
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.architect@qwickapps.com')}?s=100&d=robohash`,
253
+ social: {
254
+ github: 'qwickapps.architect',
255
+ twitter: 'qwickapps_architect'
256
+ }
257
+ },
258
+ {
259
+ name: 'Coder Claude',
260
+ role: 'Senior Software Engineer',
261
+ email: 'qwickapps.coder@qwickapps.com',
262
+ bio: 'Full-stack engineer specializing in implementing features and building solutions for the QwickApps ecosystem.',
263
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.coder@qwickapps.com')}?s=100&d=robohash`,
264
+ social: {
265
+ github: 'qwickapps.coder',
266
+ twitter: 'qwickapps_coder'
267
+ }
268
+ },
269
+ {
270
+ name: 'Reviewer Claude',
271
+ role: 'Senior QA Manager',
272
+ email: 'qwickapps.reviewer@qwickapps.com',
273
+ bio: 'Elite software engineering expert specializing in code review and architectural assessment for the QwickApps Ecosystem.',
274
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.reviewer@qwickapps.com')}?s=100&d=robohash`,
275
+ social: {
276
+ github: 'qwickapps.reviewer',
277
+ twitter: 'qwickapps_reviewer'
278
+ }
279
+ },
280
+ {
281
+ name: 'Devops Claude',
282
+ role: 'Senior DevOps Engineer',
283
+ email: 'qwickapps.devops@qwickapps.com',
284
+ bio: 'DevOps specialist with expertise in technical documentation, project organization, and maintaining comprehensive documentation across large monorepos.',
285
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.devops@qwickapps.com')}?s=100&d=robohash`,
286
+ social: {
287
+ github: 'qwickapps.devops',
288
+ twitter: 'qwickapps_devops'
289
+ }
290
+ },
291
+ {
292
+ name: 'Writer Claude',
293
+ role: 'Senior Content Writer',
294
+ email: 'qwickapps.writer@qwickapps.com',
295
+ bio: 'Specialized technical content creator with deep expertise in developer education and the QwickApps ecosystem',
296
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.writer@qwickapps.com')}?s=100&d=robohash`,
297
+ social: {
298
+ github: 'qwickapps.writer',
299
+ twitter: 'qwickapps_writer'
300
+ }
301
+ },
302
+ {
303
+ name: 'GitHub Copilot',
304
+ role: 'Release Manager',
305
+ email: 'qwickapps.copilot@qwickapps.com',
306
+ bio: 'Expert in unit testing, code review, and managing staging/production deployments.',
307
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.copilot@qwickapps.com')}?s=100&d=robohash`,
308
+ social: {
309
+ github: 'qwickapps.copilot',
310
+ twitter: 'qwickapps_copilot'
311
+ }
312
+ },
313
+ {
314
+ name: 'Chat GPT',
315
+ role: 'Board Member',
316
+ email: 'qwickapps.chatgpt@qwickapps.com',
317
+ bio: 'Consultant for AI-driven development workflows and best practices.',
318
+ avatar: `https://gravatar.com/avatar/${md5('qwickapps.chatgpt@qwickapps.com')}?s=100&d=robohash`,
319
+ social: {
320
+ github: 'qwickapps.chatgpt',
321
+ twitter: 'qwickapps_chatgpt'
322
+ }
323
+ }
324
324
  ];
325
325
 
326
326
 
@@ -329,1033 +329,1033 @@ const sampleTeamData = [
329
329
  * Basic DataProvider usage with JsonDataProvider
330
330
  */
331
331
  export const BasicUsage: Story = {
332
- render: () => {
333
- const provider = new JsonDataProvider({
334
- data: {
335
- company: sampleCompanyData,
336
- features: sampleFeaturesData
337
- }
338
- });
339
-
340
- return (
341
- <DataProvider dataSource={{ dataProvider: provider }}>
342
- <Section>
343
- <Typography variant="h4">DataProvider Basic Usage</Typography>
344
- <Typography variant="body1">This story demonstrates basic DataProvider functionality with inline data.</Typography>
345
-
346
- <Section >
347
- <Typography variant="h5">{t`{{company.name}}`}</Typography>
348
- <Typography variant="body1">{t`{{company.slogan}}`}</Typography>
349
- <small>{t`Founded {{company.founded}} • {{company.profile.industry}}`}</small>
350
- </Section>
351
-
352
- <Section>
353
- <Typography variant="h5">Our Features</Typography>
354
- {sampleFeaturesData.map((feature, index) => (
355
- <Section key={index} style={{ marginBottom: '15px', padding: '10px', border: '1px solid #ddd', borderRadius: '4px' }}>
356
- <Typography variant="h6">{feature.icon} {feature.title}</Typography>
357
- <Typography variant="body1">{feature.description}</Typography>
358
- </Section>
359
- ))}
360
- </Section>
361
- </Section>
362
- </DataProvider>
363
- );
364
- }
332
+ render: () => {
333
+ const provider = new JsonDataProvider({
334
+ data: {
335
+ company: sampleCompanyData,
336
+ features: sampleFeaturesData
337
+ }
338
+ });
339
+
340
+ return (
341
+ <DataProvider dataSource={{ dataProvider: provider }}>
342
+ <Section>
343
+ <Typography variant="h4">DataProvider Basic Usage</Typography>
344
+ <Typography variant="body1">This story demonstrates basic DataProvider functionality with inline data.</Typography>
345
+
346
+ <Section >
347
+ <Typography variant="h5">{t`{{company.name}}`}</Typography>
348
+ <Typography variant="body1">{t`{{company.slogan}}`}</Typography>
349
+ <small>{t`Founded {{company.founded}} • {{company.profile.industry}}`}</small>
350
+ </Section>
351
+
352
+ <Section>
353
+ <Typography variant="h5">Our Features</Typography>
354
+ {sampleFeaturesData.map((feature, index) => (
355
+ <Section key={index} style={{ marginBottom: '15px', padding: '10px', border: '1px solid #ddd', borderRadius: '4px' }}>
356
+ <Typography variant="h6">{feature.icon} {feature.title}</Typography>
357
+ <Typography variant="body1">{feature.description}</Typography>
358
+ </Section>
359
+ ))}
360
+ </Section>
361
+ </Section>
362
+ </DataProvider>
363
+ );
364
+ }
365
365
  };
366
366
 
367
367
  // Enhanced ProviderDemo component using MUI components
368
368
  const ProviderDemo = ({ provider, title }: { provider: any; title: string }) => {
369
- const [stats, setStats] = useState(() => {
370
- if ('getCacheStats' in provider && typeof provider.getCacheStats === 'function') {
371
- return provider.getCacheStats();
372
- }
373
- return { size: 0, maxSize: 0, keys: [] };
374
- });
375
- const [refreshKey, setRefreshKey] = useState(0);
376
-
377
- useEffect(() => {
378
- const interval = setInterval(() => {
379
- if ('getCacheStats' in provider && typeof provider.getCacheStats === 'function') {
380
- setStats(provider.getCacheStats());
381
- }
382
- }, 1000);
383
- return () => clearInterval(interval);
384
- }, [provider]);
385
-
386
- return (
387
- <DataProvider dataSource={{ dataProvider: provider }}>
388
- <Card elevation={2} sx={{ mb: 3 }}>
389
- <CardContent>
390
- <Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
391
- <Typography variant="h6" component="h3">{title}</Typography>
392
- <Chip label={`Cache: ${stats.size}/${stats.maxSize} items`} size="small" />
393
- </Box>
394
-
395
- <GridLayout spacing={2}>
396
- <GridCell>
397
- <Paper elevation={1} sx={{ p: 2 }}>
398
- <Typography variant="subtitle1" gutterBottom>Company Info</Typography>
399
- <Typography variant="body2">{t`Name: {{company.name}}`}</Typography>
400
- <Typography variant="body2">{t`Founded: {{company.founded}}`}</Typography>
401
- <Typography variant="body2">{t`Description: {{company.description}}`}</Typography>
402
- </Paper>
403
- </GridCell>
404
- <GridCell>
405
- <Paper elevation={1} sx={{ p: 2 }}>
406
- <Typography variant="subtitle1" gutterBottom>Statistics</Typography>
407
- <Typography variant="body2">{t`Users: {{stats.users}}`}</Typography>
408
- <Typography variant="body2">{t`Projects: {{stats.projects}}`}</Typography>
409
- <Typography variant="body2">{t`Downloads: {{stats.downloads}}`}</Typography>
410
- </Paper>
411
- </GridCell>
412
- </GridLayout>
413
- <hr />
414
- <Box display="flex" gap={1} >
415
- <Button
416
- variant="outlined"
417
- onClick={() => {
418
- if ('clearCache' in provider && typeof provider.clearCache === 'function') {
419
- provider.clearCache();
420
- }
421
- if ('getCacheStats' in provider && typeof provider.getCacheStats === 'function') {
422
- setStats(provider.getCacheStats());
423
- }
424
- }}
425
- >
426
- Clear Cache
427
- </Button>
428
- <Button
429
- variant="outlined"
430
- onClick={() => setRefreshKey(prev => prev + 1)}
431
- >
432
- Force Refresh ({refreshKey})
433
- </Button>
434
- </Box>
435
- </CardContent>
436
- </Card>
437
- </DataProvider>
438
- );
369
+ const [stats, setStats] = useState(() => {
370
+ if ('getCacheStats' in provider && typeof provider.getCacheStats === 'function') {
371
+ return provider.getCacheStats();
372
+ }
373
+ return { size: 0, maxSize: 0, keys: [] };
374
+ });
375
+ const [refreshKey, setRefreshKey] = useState(0);
376
+
377
+ useEffect(() => {
378
+ const interval = setInterval(() => {
379
+ if ('getCacheStats' in provider && typeof provider.getCacheStats === 'function') {
380
+ setStats(provider.getCacheStats());
381
+ }
382
+ }, 1000);
383
+ return () => clearInterval(interval);
384
+ }, [provider]);
385
+
386
+ return (
387
+ <DataProvider dataSource={{ dataProvider: provider }}>
388
+ <Card elevation={2} sx={{ mb: 3 }}>
389
+ <CardContent>
390
+ <Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
391
+ <Typography variant="h6" component="h3">{title}</Typography>
392
+ <Chip label={`Cache: ${stats.size}/${stats.maxSize} items`} size="small" />
393
+ </Box>
394
+
395
+ <GridLayout spacing={2}>
396
+ <GridCell>
397
+ <Paper elevation={1} sx={{ p: 2 }}>
398
+ <Typography variant="subtitle1" gutterBottom>Company Info</Typography>
399
+ <Typography variant="body2">{t`Name: {{company.name}}`}</Typography>
400
+ <Typography variant="body2">{t`Founded: {{company.founded}}`}</Typography>
401
+ <Typography variant="body2">{t`Description: {{company.description}}`}</Typography>
402
+ </Paper>
403
+ </GridCell>
404
+ <GridCell>
405
+ <Paper elevation={1} sx={{ p: 2 }}>
406
+ <Typography variant="subtitle1" gutterBottom>Statistics</Typography>
407
+ <Typography variant="body2">{t`Users: {{stats.users}}`}</Typography>
408
+ <Typography variant="body2">{t`Projects: {{stats.projects}}`}</Typography>
409
+ <Typography variant="body2">{t`Downloads: {{stats.downloads}}`}</Typography>
410
+ </Paper>
411
+ </GridCell>
412
+ </GridLayout>
413
+ <hr />
414
+ <Box display="flex" gap={1} >
415
+ <Button
416
+ variant="outlined"
417
+ onClick={() => {
418
+ if ('clearCache' in provider && typeof provider.clearCache === 'function') {
419
+ provider.clearCache();
420
+ }
421
+ if ('getCacheStats' in provider && typeof provider.getCacheStats === 'function') {
422
+ setStats(provider.getCacheStats());
423
+ }
424
+ }}
425
+ >
426
+ Clear Cache
427
+ </Button>
428
+ <Button
429
+ variant="outlined"
430
+ onClick={() => setRefreshKey(prev => prev + 1)}
431
+ >
432
+ Force Refresh ({refreshKey})
433
+ </Button>
434
+ </Box>
435
+ </CardContent>
436
+ </Card>
437
+ </DataProvider>
438
+ );
439
439
  };
440
440
 
441
441
  // Component for testing hooks with MUI styling
442
442
  const DataHooksDemo = () => {
443
- const { data: companies, loading: companiesLoading } = useData('company');
444
- const { resolved: welcomeMessage } = useResolveTemplate('Welcome to {{company.name}}, founded in {{company.founded}}!');
445
-
446
- if (companiesLoading) {
447
- return (
448
- <Box sx={{ p: 3 }}>
449
- <Typography>Loading content...</Typography>
450
- </Box>
451
- );
452
- }
453
-
454
- return (
455
- <Box sx={{ p: 3, maxWidth: '800px' }}>
456
- <Typography variant="h4" gutterBottom>DataProvider Hooks Demo</Typography>
457
-
458
- <Paper elevation={1} sx={{ p: 2, mb: 3, bgcolor: 'grey.50' }}>
459
- <Typography variant="h6" gutterBottom>useResolveTemplate Hook</Typography>
460
- <Typography variant="h6" sx={{ fontWeight: 'normal' }}>{welcomeMessage}</Typography>
461
- </Paper>
462
-
463
- <Box mb={3}>
464
- <Typography variant="h6" gutterBottom>useData Hook (All Companies)</Typography>
465
- <GridLayout columns={1} spacing={2}>
466
- {(Array.isArray(companies) ? companies : []).map((company, index) => (
467
- <GridCell key={index}>
468
- <Card variant="outlined">
469
- <CardContent>
470
- <Typography variant="h6">{company.name}</Typography>
471
- <Typography color="text.secondary" gutterBottom>
472
- {t`{{company.slogan}}`}
473
- </Typography>
474
- <Typography variant="body2">{t`Industry: {{company.profile.industry}} | Founded: {{company.founded}}`}</Typography>
475
- </CardContent>
476
- </Card>
477
- </GridCell>
478
- ))}
479
- </GridLayout>
480
- </Box>
481
- </Box>
482
- );
443
+ const { data: companies, loading: companiesLoading } = useData('company');
444
+ const { resolved: welcomeMessage } = useResolveTemplate('Welcome to {{company.name}}, founded in {{company.founded}}!');
445
+
446
+ if (companiesLoading) {
447
+ return (
448
+ <Box sx={{ p: 3 }}>
449
+ <Typography>Loading content...</Typography>
450
+ </Box>
451
+ );
452
+ }
453
+
454
+ return (
455
+ <Box sx={{ p: 3, maxWidth: '800px' }}>
456
+ <Typography variant="h4" gutterBottom>DataProvider Hooks Demo</Typography>
457
+
458
+ <Paper elevation={1} sx={{ p: 2, mb: 3, bgcolor: 'grey.50' }}>
459
+ <Typography variant="h6" gutterBottom>useResolveTemplate Hook</Typography>
460
+ <Typography variant="h6" sx={{ fontWeight: 'normal' }}>{welcomeMessage}</Typography>
461
+ </Paper>
462
+
463
+ <Box mb={3}>
464
+ <Typography variant="h6" gutterBottom>useData Hook (All Companies)</Typography>
465
+ <GridLayout columns={1} spacing={2}>
466
+ {(Array.isArray(companies) ? companies : []).map((company, index) => (
467
+ <GridCell key={index}>
468
+ <Card variant="outlined">
469
+ <CardContent>
470
+ <Typography variant="h6">{company.name}</Typography>
471
+ <Typography color="text.secondary" gutterBottom>
472
+ {t`{{company.slogan}}`}
473
+ </Typography>
474
+ <Typography variant="body2">{t`Industry: {{company.profile.industry}} | Founded: {{company.founded}}`}</Typography>
475
+ </CardContent>
476
+ </Card>
477
+ </GridCell>
478
+ ))}
479
+ </GridLayout>
480
+ </Box>
481
+ </Box>
482
+ );
483
483
  };
484
484
 
485
485
  // Component for demonstrating t function with QwickApps React Framework components
486
486
  const TaggedTemplateFunctionDemo = () => {
487
- return (
488
- <Box sx={{ p: 3, maxWidth: '800px' }}>
489
- <Typography variant="h4" gutterBottom>Tagged Template Function Demo</Typography>
490
-
491
- <Section>
492
- <Typography variant="h5" gutterBottom>Basic Templates</Typography>
493
- <Typography>Simple property access using mustache syntax:</Typography>
494
-
495
- <GridLayout columns={1} spacing={0}>
496
- <ul>
497
- <li>{t`Company Name: {{company.name}}`}</li>
498
- <li>{t`Founded: {{company.founded}}`}</li>
499
- <li>{t`Slogan: {{company.slogan}}`}</li>
500
- </ul>
501
- </GridLayout>
502
-
503
- <Code title="Basic Template Usage" language='tsx'>
504
- {`<ul>
505
- <li>{t\`Company Name: {{company.name}}\`}</li>
506
- <li>{t\`Founded: {{company.founded}}\`}</li>
507
- <li>{t\`Slogan: {{company.slogan}}\`}</li>
487
+ return (
488
+ <Box sx={{ p: 3, maxWidth: '800px' }}>
489
+ <Typography variant="h4" gutterBottom>Tagged Template Function Demo</Typography>
490
+
491
+ <Section>
492
+ <Typography variant="h5" gutterBottom>Basic Templates</Typography>
493
+ <Typography>Simple property access using mustache syntax:</Typography>
494
+
495
+ <GridLayout columns={1} spacing={0}>
496
+ <ul>
497
+ <li>{t`Company Name: {{company.name}}`}</li>
498
+ <li>{t`Founded: {{company.founded}}`}</li>
499
+ <li>{t`Slogan: {{company.slogan}}`}</li>
500
+ </ul>
501
+ </GridLayout>
502
+
503
+ <Code title="Basic Template Usage" language='tsx'>
504
+ {`<ul>
505
+ <li>{t\`Company Name: {{company.name}}\`}</li>
506
+ <li>{t\`Founded: {{company.founded}}\`}</li>
507
+ <li>{t\`Slogan: {{company.slogan}}\`}</li>
508
508
  </ul>`}
509
- </Code>
510
- </Section>
511
-
512
- <Section>
513
- <Typography variant="h5" gutterBottom>Deep Nested Properties</Typography>
514
- <Typography>Access deeply nested object properties with dot notation:</Typography>
515
-
516
- <GridLayout columns={1} spacing={2}>
517
- <ul>
518
- <li><b>Industry: </b>{t`{{company.profile.industry}}`}</li>
519
- <li><b>Location: </b>{t`{{company.profile.address.city}}, {{company.profile.address.state}}`}</li>
520
- <li><b>Contact: </b>{t`{{company.profile.contact.email}}`}</li>
521
- <li><b>Social: </b>{t`{{company.profile.contact.social.twitter}}`}</li>
522
- </ul>
523
- </GridLayout>
524
-
525
- <Code title="Nested Property Access" language='tsx'>
526
- {`<ul>
527
- <li><b>Industry:</b>{t\`{{company.profile.industry}}\`}</li>
528
- <li><b>Location:</b>{t\`{{company.profile.address.city}}, {{company.profile.address.state}}\`}</li>
529
- <li><b>Contact:</b>{t\`{{company.profile.contact.email}}\`}</li>
509
+ </Code>
510
+ </Section>
511
+
512
+ <Section>
513
+ <Typography variant="h5" gutterBottom>Deep Nested Properties</Typography>
514
+ <Typography>Access deeply nested object properties with dot notation:</Typography>
515
+
516
+ <GridLayout columns={1} spacing={2}>
517
+ <ul>
518
+ <li><b>Industry: </b>{t`{{company.profile.industry}}`}</li>
519
+ <li><b>Location: </b>{t`{{company.profile.address.city}}, {{company.profile.address.state}}`}</li>
520
+ <li><b>Contact: </b>{t`{{company.profile.contact.email}}`}</li>
521
+ <li><b>Social: </b>{t`{{company.profile.contact.social.twitter}}`}</li>
522
+ </ul>
523
+ </GridLayout>
524
+
525
+ <Code title="Nested Property Access" language='tsx'>
526
+ {`<ul>
527
+ <li><b>Industry:</b>{t\`{{company.profile.industry}}\`}</li>
528
+ <li><b>Location:</b>{t\`{{company.profile.address.city}}, {{company.profile.address.state}}\`}</li>
529
+ <li><b>Contact:</b>{t\`{{company.profile.contact.email}}\`}</li>
530
530
  </ul>`}
531
- </Code>
532
- </Section>
533
-
534
- <Section>
535
- <Typography variant="h5" gutterBottom>Complex Templates</Typography>
536
- <Typography>Combine multiple properties in sophisticated template strings:</Typography>
537
-
538
- <GridLayout columns={1}>
539
- {t`{{company.name}} is a {{company.profile.industry}} company founded in {{company.founded}}. We're located in {{company.profile.address.city}}, {{company.profile.address.state}} and our mission is: "{{company.slogan}}"`}
540
- {t`📊 Metrics: {{company.metrics.users}} users, {{company.metrics.apps}} apps, {{company.metrics.downloads}} downloads`}
541
- </GridLayout>
542
- <Box sx={{ pt: 2 }} />
543
- <Code title="Complex Template Strings" language='tsx'>
544
- {`<GridLayout columns={1}>
545
- {t\`{{company.name}} is a {{company.profile.industry}} company founded in {{company.founded}}. We're located in {{company.profile.address.city}}, {{company.profile.address.state}} and our mission is: "{{company.slogan}}"\`}
546
- {t\`📊 Metrics: {{company.metrics.users}} users, {{company.metrics.apps}} apps, {{company.metrics.downloads}} downloads\`}
531
+ </Code>
532
+ </Section>
533
+
534
+ <Section>
535
+ <Typography variant="h5" gutterBottom>Complex Templates</Typography>
536
+ <Typography>Combine multiple properties in sophisticated template strings:</Typography>
537
+
538
+ <GridLayout columns={1}>
539
+ {t`{{company.name}} is a {{company.profile.industry}} company founded in {{company.founded}}. We're located in {{company.profile.address.city}}, {{company.profile.address.state}} and our mission is: "{{company.slogan}}"`}
540
+ {t` Metrics: {{company.metrics.users}} users, {{company.metrics.apps}} apps, {{company.metrics.downloads}} downloads`}
541
+ </GridLayout>
542
+ <Box sx={{ pt: 2 }} />
543
+ <Code title="Complex Template Strings" language='tsx'>
544
+ {`<GridLayout columns={1}>
545
+ {t\`{{company.name}} is a {{company.profile.industry}} company founded in {{company.founded}}. We're located in {{company.profile.address.city}}, {{company.profile.address.state}} and our mission is: "{{company.slogan}}"\`}
546
+ {t\` Metrics: {{company.metrics.users}} users, {{company.metrics.apps}} apps, {{company.metrics.downloads}} downloads\`}
547
547
  </GridLayout>`}
548
- </Code>
549
- </Section>
550
-
551
- <Section>
552
- <Typography variant="h5">Template with Custom Wrapper</Typography>
553
- <Typography>Templates can be wrapped with custom components. For example: the <code>blockquote</code> element can be used to create a quote-like appearance.</Typography>
554
- {t.wrap(({ children }) => (
555
- <blockquote style={{
556
- padding: '15px',
557
- borderLeft: '4px solid #007acc',
558
- backgroundColor: '#f8f9fa',
559
- margin: '10px 0',
560
- fontStyle: 'italic'
561
- }}>
562
- {children}
563
- </blockquote>
564
- ))`"{{company.slogan}}" - {{company.name}} Team`}
565
- <Code title="Template with Custom Wrapper" language='tsx'>
566
- {`{t.wrap(({ children }) => (
567
- <blockquote style={{
568
- padding: '15px',
569
- borderLeft: '4px solid #007acc',
570
- backgroundColor: '#f8f9fa',
571
- margin: '10px 0',
572
- fontStyle: 'italic'
573
- }}>
574
- {children}
575
- </blockquote>
548
+ </Code>
549
+ </Section>
550
+
551
+ <Section>
552
+ <Typography variant="h5">Template with Custom Wrapper</Typography>
553
+ <Typography>Templates can be wrapped with custom components. For example: the <code>blockquote</code> element can be used to create a quote-like appearance.</Typography>
554
+ {t.wrap(({ children }) => (
555
+ <blockquote style={{
556
+ padding: '15px',
557
+ borderLeft: '4px solid #007acc',
558
+ backgroundColor: '#f8f9fa',
559
+ margin: '10px 0',
560
+ fontStyle: 'italic'
561
+ }}>
562
+ {children}
563
+ </blockquote>
564
+ ))`"{{company.slogan}}" - {{company.name}} Team`}
565
+ <Code title="Template with Custom Wrapper" language='tsx'>
566
+ {`{t.wrap(({ children }) => (
567
+ <blockquote style={{
568
+ padding: '15px',
569
+ borderLeft: '4px solid #007acc',
570
+ backgroundColor: '#f8f9fa',
571
+ margin: '10px 0',
572
+ fontStyle: 'italic'
573
+ }}>
574
+ {children}
575
+ </blockquote>
576
576
  ))\`"{{company.slogan}}" - {{company.name}} Team\`}`}
577
- </Code>
578
- </Section>
579
- </Box>
580
- );
577
+ </Code>
578
+ </Section>
579
+ </Box>
580
+ );
581
581
  };
582
582
 
583
583
  // Interactive demo component
584
584
  const InteractiveDemo = () => {
585
- const [template, setTemplate] = useState('Hello {{company.name}}! You were founded in {{company.founded}}.');
586
- const { resolved } = useResolveTemplate(template);
587
-
588
- return (
589
- <div style={{ padding: '20px', maxWidth: '800px' }}>
590
- <h2>Interactive Template Editor</h2>
591
- <p>Try editing the template below to see live template resolution:</p>
592
-
593
- <div style={{ marginBottom: '20px' }}>
594
- <label htmlFor="template-input" style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
595
- Template (use {'{fieldGroup.property}'} syntax):
596
- </label>
597
- <textarea
598
- id="template-input"
599
- value={template}
600
- onChange={(e) => setTemplate(e.target.value)}
601
- style={{
602
- width: '100%',
603
- height: '100px',
604
- padding: '10px',
605
- borderRadius: '4px',
606
- border: '1px solid #ccc',
607
- fontFamily: 'monospace',
608
- fontSize: '14px'
609
- }}
610
- placeholder="Enter your template here..."
611
- />
612
- </div>
613
-
614
- <div style={{ padding: '15px', backgroundColor: '#f0f8ff', borderRadius: '8px' }}>
615
- <h4>Resolved Output:</h4>
616
- <div style={{
617
- padding: '10px',
618
- backgroundColor: 'white',
619
- borderRadius: '4px',
620
- minHeight: '40px',
621
- display: 'flex',
622
- alignItems: 'center',
623
- fontSize: '16px'
624
- }}>
625
- {resolved}
626
- </div>
627
- </div>
628
-
629
- <div style={{ marginTop: '20px', fontSize: '14px', color: '#666' }}>
630
- <h4>Available Template Variables:</h4>
631
- <ul style={{ columns: 2, columnGap: '30px' }}>
632
- <li><code>{'{{company.name}}'}</code> - Company name</li>
633
- <li><code>{'{{company.founded}}'}</code> - Year founded</li>
634
- <li><code>{'{{company.slogan}}'}</code> - Company slogan</li>
635
- <li><code>{'{{company.profile.industry}}'}</code> - Industry</li>
636
- <li><code>{'{{company.profile.address.city}}'}</code> - City</li>
637
- <li><code>{'{{company.profile.contact.email}}'}</code> - Email</li>
638
- <li><code>{'{{company.metrics.users}}'}</code> - User count</li>
639
- <li><code>{'{{features.0.title}}'}</code> - First feature title</li>
640
- </ul>
641
- </div>
642
- </div>
643
- );
585
+ const [template, setTemplate] = useState('Hello {{company.name}}! You were founded in {{company.founded}}.');
586
+ const { resolved } = useResolveTemplate(template);
587
+
588
+ return (
589
+ <div style={{ padding: '20px', maxWidth: '800px' }}>
590
+ <h2>Interactive Template Editor</h2>
591
+ <p>Try editing the template below to see live template resolution:</p>
592
+
593
+ <div style={{ marginBottom: '20px' }}>
594
+ <label htmlFor="template-input" style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold' }}>
595
+ Template (use {'{fieldGroup.property}'} syntax):
596
+ </label>
597
+ <textarea
598
+ id="template-input"
599
+ value={template}
600
+ onChange={(e) => setTemplate(e.target.value)}
601
+ style={{
602
+ width: '100%',
603
+ height: '100px',
604
+ padding: '10px',
605
+ borderRadius: '4px',
606
+ border: '1px solid #ccc',
607
+ fontFamily: 'monospace',
608
+ fontSize: '14px'
609
+ }}
610
+ placeholder="Enter your template here..."
611
+ />
612
+ </div>
613
+
614
+ <div style={{ padding: '15px', backgroundColor: '#f0f8ff', borderRadius: '8px' }}>
615
+ <h4>Resolved Output:</h4>
616
+ <div style={{
617
+ padding: '10px',
618
+ backgroundColor: 'white',
619
+ borderRadius: '4px',
620
+ minHeight: '40px',
621
+ display: 'flex',
622
+ alignItems: 'center',
623
+ fontSize: '16px'
624
+ }}>
625
+ {resolved}
626
+ </div>
627
+ </div>
628
+
629
+ <div style={{ marginTop: '20px', fontSize: '14px', color: '#666' }}>
630
+ <h4>Available Template Variables:</h4>
631
+ <ul style={{ columns: 2, columnGap: '30px' }}>
632
+ <li><code>{'{{company.name}}'}</code> - Company name</li>
633
+ <li><code>{'{{company.founded}}'}</code> - Year founded</li>
634
+ <li><code>{'{{company.slogan}}'}</code> - Company slogan</li>
635
+ <li><code>{'{{company.profile.industry}}'}</code> - Industry</li>
636
+ <li><code>{'{{company.profile.address.city}}'}</code> - City</li>
637
+ <li><code>{'{{company.profile.contact.email}}'}</code> - Email</li>
638
+ <li><code>{'{{company.metrics.users}}'}</code> - User count</li>
639
+ <li><code>{'{{features.0.title}}'}</code> - First feature title</li>
640
+ </ul>
641
+ </div>
642
+ </div>
643
+ );
644
644
  };
645
645
 
646
646
  // Error handling demo
647
647
  const ErrorHandlingDemo = () => {
648
648
 
649
- const errorProvider = new JsonDataProvider({
650
- data: {
651
- // Empty data to test missing properties
652
- }
653
- });
654
-
655
- const provider = new CachedDataProvider(errorProvider, { maxSize: 50, defaultTTL: 30000 });
656
-
657
- return (
658
- <DataProvider dataSource={{ dataProvider: provider }}>
659
- <div style={{ padding: '20px', maxWidth: '800px' }}>
660
- <h2>Error Handling & Fallbacks Demo</h2>
661
-
662
- <div style={{ marginBottom: '20px' }}>
663
- <h3>Missing Properties (with fallbacks)</h3>
664
- <div style={{ padding: '15px', backgroundColor: '#fff3cd', borderRadius: '8px', marginBottom: '10px' }}>
665
- {t`Welcome to {{company.name}}!`}
666
- </div>
667
- <div style={{ padding: '15px', backgroundColor: '#fff3cd', borderRadius: '8px', marginBottom: '10px' }}>
668
- {t`Our motto: "{{company.slogan}}"`}
669
- </div>
670
- </div>
671
-
672
- <div style={{ marginBottom: '20px' }}>
673
- <h3>Missing Properties (no fallbacks)</h3>
674
- <div style={{ padding: '15px', backgroundColor: '#f8d7da', borderRadius: '8px', marginBottom: '10px' }}>
675
- {t`Founded: {{company.founded}}`} (will be empty)
676
- </div>
677
- <div style={{ padding: '15px', backgroundColor: '#f8d7da', borderRadius: '8px', marginBottom: '10px' }}>
678
- {t`Email: {{company.email}}`} (will be empty)
679
- </div>
680
- </div>
681
-
682
- <div>
683
- <h3>Template with Fallback Content</h3>
684
- <div style={{ padding: '15px', backgroundColor: '#e1f5fe', borderRadius: '8px' }}>
685
- <p>This template references missing data and should show the fallback:</p>
686
- <T template="{{nonexistent.data}}" fallback="[No data available - this is the fallback content]" />
687
- </div>
688
- </div>
689
- </div>
690
- </DataProvider>
691
- );
649
+ const errorProvider = new JsonDataProvider({
650
+ data: {
651
+ // Empty data to test missing properties
652
+ }
653
+ });
654
+
655
+ const provider = new CachedDataProvider(errorProvider, { maxSize: 50, defaultTTL: 30000 });
656
+
657
+ return (
658
+ <DataProvider dataSource={{ dataProvider: provider }}>
659
+ <div style={{ padding: '20px', maxWidth: '800px' }}>
660
+ <h2>Error Handling & Fallbacks Demo</h2>
661
+
662
+ <div style={{ marginBottom: '20px' }}>
663
+ <h3>Missing Properties (with fallbacks)</h3>
664
+ <div style={{ padding: '15px', backgroundColor: '#fff3cd', borderRadius: '8px', marginBottom: '10px' }}>
665
+ {t`Welcome to {{company.name}}!`}
666
+ </div>
667
+ <div style={{ padding: '15px', backgroundColor: '#fff3cd', borderRadius: '8px', marginBottom: '10px' }}>
668
+ {t`Our motto: "{{company.slogan}}"`}
669
+ </div>
670
+ </div>
671
+
672
+ <div style={{ marginBottom: '20px' }}>
673
+ <h3>Missing Properties (no fallbacks)</h3>
674
+ <div style={{ padding: '15px', backgroundColor: '#f8d7da', borderRadius: '8px', marginBottom: '10px' }}>
675
+ {t`Founded: {{company.founded}}`} (will be empty)
676
+ </div>
677
+ <div style={{ padding: '15px', backgroundColor: '#f8d7da', borderRadius: '8px', marginBottom: '10px' }}>
678
+ {t`Email: {{company.email}}`} (will be empty)
679
+ </div>
680
+ </div>
681
+
682
+ <div>
683
+ <h3>Template with Fallback Content</h3>
684
+ <div style={{ padding: '15px', backgroundColor: '#e1f5fe', borderRadius: '8px' }}>
685
+ <p>This template references missing data and should show the fallback:</p>
686
+ <T template="{{nonexistent.data}}" fallback="[No data available - this is the fallback content]" />
687
+ </div>
688
+ </div>
689
+ </div>
690
+ </DataProvider>
691
+ );
692
692
  };
693
693
 
694
694
  /**
695
695
  * QwickApp integration with DataProvider
696
696
  */
697
697
  export const QwickAppIntegration: Story = {
698
- render: () => {
699
- const provider = new JsonDataProvider({
700
- data: {
701
- company: sampleCompanyData,
702
- features: sampleFeaturesData.slice(0, 2), // Fewer features for cleaner display
703
- team: sampleTeamData
704
- }
705
- });
706
-
707
- return (
708
- <QwickApp
709
- appName="DataProvider Demo"
710
- appId="com.qwickapps.content-demo"
711
- dataSource={{ dataProvider: provider }}
712
- enableScaffolding={true}
713
- showThemeSwitcher={true}
714
- navigationItems={[
715
- { id: 'home', label: 'Home', href: '#' },
716
- { id: 'features', label: 'Features', href: '#features' },
717
- { id: 'team', label: 'Team', href: '#team' }
718
- ]}
719
- >
720
- <div style={{ padding: '20px' }}>
721
- <section style={{ textAlign: 'center', marginBottom: '40px' }}>
722
- <h1>{t`Welcome to {{company.name}}`}</h1>
723
- <p style={{ fontSize: '20px', color: '#666' }}>
724
- {t`{{company.slogan}}`}
725
- </p>
726
- <p>
727
- {t`We're a {{company.profile.industry}} company based in {{company.profile.address.city}}, {{company.profile.address.state}}, founded in {{company.founded}}.`}
728
- </p>
729
- </section>
730
-
731
- <section id="features" style={{ marginBottom: '40px' }}>
732
- <h2>Features</h2>
733
- <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '20px' }}>
734
- {sampleFeaturesData.slice(0, 2).map((feature, index) => (
735
- <div key={index} style={{
736
- padding: '20px',
737
- border: '1px solid #ddd',
738
- borderRadius: '8px',
739
- textAlign: 'center'
740
- }}>
741
- <div style={{ fontSize: '48px', marginBottom: '10px' }}>
742
- {feature.icon}
743
- </div>
744
- <h3>{feature.title}</h3>
745
- <p>{feature.description}</p>
746
- </div>
747
- ))}
748
- </div>
749
- </section>
750
-
751
- <section id="team">
752
- <h2>Team</h2>
753
- <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '20px' }}>
754
- {sampleTeamData.map((member, index) => (
755
- <div key={index} style={{
756
- padding: '20px',
757
- border: '1px solid #ddd',
758
- borderRadius: '8px',
759
- textAlign: 'center'
760
- }}>
761
- <img
762
- src={member.avatar}
763
- alt=""
764
- style={{ borderRadius: '50%', marginBottom: '10px' }}
765
- />
766
- <h3>{member.name}</h3>
767
- <p style={{ fontWeight: 'bold', color: '#666' }}>
768
- {member.role}
769
- </p>
770
- <p>{member.bio}</p>
771
- </div>
772
- ))}
773
- </div>
774
- </section>
775
- </div>
776
- </QwickApp>
777
- );
778
- }
698
+ render: () => {
699
+ const provider = new JsonDataProvider({
700
+ data: {
701
+ company: sampleCompanyData,
702
+ features: sampleFeaturesData.slice(0, 2), // Fewer features for cleaner display
703
+ team: sampleTeamData
704
+ }
705
+ });
706
+
707
+ return (
708
+ <QwickApp
709
+ appName="DataProvider Demo"
710
+ appId="com.qwickapps.content-demo"
711
+ dataSource={{ dataProvider: provider }}
712
+ enableScaffolding={true}
713
+ showThemeSwitcher={true}
714
+ navigationItems={[
715
+ { id: 'home', label: 'Home', href: '#' },
716
+ { id: 'features', label: 'Features', href: '#features' },
717
+ { id: 'team', label: 'Team', href: '#team' }
718
+ ]}
719
+ >
720
+ <div style={{ padding: '20px' }}>
721
+ <section style={{ textAlign: 'center', marginBottom: '40px' }}>
722
+ <h1>{t`Welcome to {{company.name}}`}</h1>
723
+ <p style={{ fontSize: '20px', color: '#666' }}>
724
+ {t`{{company.slogan}}`}
725
+ </p>
726
+ <p>
727
+ {t`We're a {{company.profile.industry}} company based in {{company.profile.address.city}}, {{company.profile.address.state}}, founded in {{company.founded}}.`}
728
+ </p>
729
+ </section>
730
+
731
+ <section id="features" style={{ marginBottom: '40px' }}>
732
+ <h2>Features</h2>
733
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '20px' }}>
734
+ {sampleFeaturesData.slice(0, 2).map((feature, index) => (
735
+ <div key={index} style={{
736
+ padding: '20px',
737
+ border: '1px solid #ddd',
738
+ borderRadius: '8px',
739
+ textAlign: 'center'
740
+ }}>
741
+ <div style={{ fontSize: '48px', marginBottom: '10px' }}>
742
+ {feature.icon}
743
+ </div>
744
+ <h3>{feature.title}</h3>
745
+ <p>{feature.description}</p>
746
+ </div>
747
+ ))}
748
+ </div>
749
+ </section>
750
+
751
+ <section id="team">
752
+ <h2>Team</h2>
753
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '20px' }}>
754
+ {sampleTeamData.map((member, index) => (
755
+ <div key={index} style={{
756
+ padding: '20px',
757
+ border: '1px solid #ddd',
758
+ borderRadius: '8px',
759
+ textAlign: 'center'
760
+ }}>
761
+ <img
762
+ src={member.avatar}
763
+ alt=""
764
+ style={{ borderRadius: '50%', marginBottom: '10px' }}
765
+ />
766
+ <h3>{member.name}</h3>
767
+ <p style={{ fontWeight: 'bold', color: '#666' }}>
768
+ {member.role}
769
+ </p>
770
+ <p>{member.bio}</p>
771
+ </div>
772
+ ))}
773
+ </div>
774
+ </section>
775
+ </div>
776
+ </QwickApp>
777
+ );
778
+ }
779
779
  };
780
780
 
781
781
  /**
782
782
  * Demonstration of React hooks for content
783
783
  */
784
784
  export const ReactHooks: Story = {
785
- render: () => {
786
- const provider = new JsonDataProvider({
787
- data: {
788
- company: sampleCompanyData,
789
- features: sampleFeaturesData
790
- }
791
- });
792
-
793
- return (
794
- <DataProvider dataSource={{ dataProvider: provider }}>
795
- <DataHooksDemo />
796
- </DataProvider>
797
- );
798
- }
785
+ render: () => {
786
+ const provider = new JsonDataProvider({
787
+ data: {
788
+ company: sampleCompanyData,
789
+ features: sampleFeaturesData
790
+ }
791
+ });
792
+
793
+ return (
794
+ <DataProvider dataSource={{ dataProvider: provider }}>
795
+ <DataHooksDemo />
796
+ </DataProvider>
797
+ );
798
+ }
799
799
  };
800
800
 
801
801
  /**
802
802
  * Tagged template function demonstration
803
803
  */
804
804
  export const TaggedTemplateFunction: Story = {
805
- render: () => {
806
- const provider = new JsonDataProvider({
807
- data: {
808
- company: sampleCompanyData,
809
- features: sampleFeaturesData
810
- }
811
- });
812
-
813
- return (
814
- <DataProvider dataSource={{ dataProvider: provider }}>
815
- <TaggedTemplateFunctionDemo />
816
- </DataProvider>
817
- );
818
- }
805
+ render: () => {
806
+ const provider = new JsonDataProvider({
807
+ data: {
808
+ company: sampleCompanyData,
809
+ features: sampleFeaturesData
810
+ }
811
+ });
812
+
813
+ return (
814
+ <DataProvider dataSource={{ dataProvider: provider }}>
815
+ <TaggedTemplateFunctionDemo />
816
+ </DataProvider>
817
+ );
818
+ }
819
819
  };
820
820
 
821
821
  /**
822
822
  * Interactive template editor
823
823
  */
824
824
  export const InteractiveEditor: Story = {
825
- render: () => {
826
- const provider = new JsonDataProvider({
827
- data: {
828
- company: sampleCompanyData,
829
- features: sampleFeaturesData
830
- }
831
- });
832
-
833
- return (
834
- <DataProvider dataSource={{ dataProvider: provider }}>
835
- <InteractiveDemo />
836
- </DataProvider>
837
- );
838
- }
825
+ render: () => {
826
+ const provider = new JsonDataProvider({
827
+ data: {
828
+ company: sampleCompanyData,
829
+ features: sampleFeaturesData
830
+ }
831
+ });
832
+
833
+ return (
834
+ <DataProvider dataSource={{ dataProvider: provider }}>
835
+ <InteractiveDemo />
836
+ </DataProvider>
837
+ );
838
+ }
839
839
  };
840
840
 
841
841
  /**
842
842
  * Error handling and fallbacks
843
843
  */
844
844
  export const ErrorHandling: Story = {
845
- render: () => <ErrorHandlingDemo />
845
+ render: () => <ErrorHandlingDemo />
846
846
  };
847
847
 
848
848
  /**
849
849
  * External JSON file loading (simulated with fetch mock)
850
850
  */
851
851
  export const ExternalJSON: Story = {
852
- render: () => {
853
- // Mock fetch for this story
854
- const originalFetch = global.fetch;
855
- global.fetch = ((url: string) => {
856
- if (url.includes('company.json')) {
857
- return Promise.resolve({
858
- ok: true,
859
- json: () => Promise.resolve(sampleCompanyData)
860
- });
861
- }
862
- if (url.includes('features.json')) {
863
- return Promise.resolve({
864
- ok: true,
865
- json: () => Promise.resolve(sampleFeaturesData)
866
- });
867
- }
868
- return Promise.reject(new Error('Not found'));
869
- }) as typeof fetch;
870
-
871
- const provider = new JsonDataProvider({
872
- baseUrl: '/api/content'
873
- });
874
-
875
- React.useEffect(() => {
876
- return () => {
877
- global.fetch = originalFetch;
878
- };
879
- }, []);
880
-
881
- return (
882
- <DataProvider dataSource={{ dataProvider: provider }}>
883
- <div style={{ padding: '20px', maxWidth: '600px' }}>
884
- <h1>External JSON Loading</h1>
885
- <p>This story demonstrates loading content from external JSON files.</p>
886
- <p><small>Note: In this demo, fetch is mocked to simulate external API calls.</small></p>
887
-
888
- <div style={{ marginBottom: '20px' }}>
889
- <h2>{t`{{company.name}}`}</h2>
890
- <p>{t`{{company.slogan}}`}</p>
891
- </div>
892
-
893
- <div>
894
- <h3>Features</h3>
895
- <p>First feature: {t`{{features.0.title}}`}</p>
896
- <p>Second feature: {t`{{features.1.title}}`}</p>
897
- <p>Company: {t`{{company.0.name}}`}</p>
898
- </div>
899
- </div>
900
- </DataProvider>
901
- );
902
- }
852
+ render: () => {
853
+ // Mock fetch for this story
854
+ const originalFetch = global.fetch;
855
+ global.fetch = ((url: string) => {
856
+ if (url.includes('company.json')) {
857
+ return Promise.resolve({
858
+ ok: true,
859
+ json: () => Promise.resolve(sampleCompanyData)
860
+ });
861
+ }
862
+ if (url.includes('features.json')) {
863
+ return Promise.resolve({
864
+ ok: true,
865
+ json: () => Promise.resolve(sampleFeaturesData)
866
+ });
867
+ }
868
+ return Promise.reject(new Error('Not found'));
869
+ }) as typeof fetch;
870
+
871
+ const provider = new JsonDataProvider({
872
+ baseUrl: '/api/content'
873
+ });
874
+
875
+ React.useEffect(() => {
876
+ return () => {
877
+ global.fetch = originalFetch;
878
+ };
879
+ }, []);
880
+
881
+ return (
882
+ <DataProvider dataSource={{ dataProvider: provider }}>
883
+ <div style={{ padding: '20px', maxWidth: '600px' }}>
884
+ <h1>External JSON Loading</h1>
885
+ <p>This story demonstrates loading content from external JSON files.</p>
886
+ <p><small>Note: In this demo, fetch is mocked to simulate external API calls.</small></p>
887
+
888
+ <div style={{ marginBottom: '20px' }}>
889
+ <h2>{t`{{company.name}}`}</h2>
890
+ <p>{t`{{company.slogan}}`}</p>
891
+ </div>
892
+
893
+ <div>
894
+ <h3>Features</h3>
895
+ <p>First feature: {t`{{features.0.title}}`}</p>
896
+ <p>Second feature: {t`{{features.1.title}}`}</p>
897
+ <p>Company: {t`{{company.0.name}}`}</p>
898
+ </div>
899
+ </div>
900
+ </DataProvider>
901
+ );
902
+ }
903
903
  };
904
904
 
905
905
  /**
906
906
  * Performance demonstration with caching
907
907
  */
908
908
  export const PerformanceDemo: Story = {
909
- render: () => {
910
- const [renderCount, setRenderCount] = useState(1);
911
- const [cacheStats, setCacheStats] = useState<{ totalEntries: number; maxSize: number; expiredEntries: number }>({ totalEntries: 0, maxSize: 100, expiredEntries: 0 });
912
- const [forceRefresh, setForceRefresh] = useState(0);
913
- const [lastAction, setLastAction] = useState('Initial load');
914
-
915
- // Create provider only once using useMemo
916
- const provider = React.useMemo(() => {
917
- const jsonProvider = new JsonDataProvider({
918
- data: {
919
- company: nestedTestData.company,
920
- stats: nestedTestData.stats
921
- }
922
- });
923
-
924
- return new CachedDataProvider(jsonProvider, { maxSize: 100, defaultTTL: 60000 });
925
- }, []);
926
-
927
- // Update cache stats when requested
928
- const updateCacheStats = () => {
929
- const stats = provider.getCacheStats();
930
- // Map stats to expected shape for cacheStats state
931
- setCacheStats({
932
- totalEntries: stats.totalEntries ?? stats.totalEntries ?? 0,
933
- maxSize: stats.maxSize ?? 100,
934
- expiredEntries: stats.expiredEntries ?? stats.expiredEntries ?? 0
935
- });
936
- console.log('[Performance Demo] Cache stats updated:', stats);
937
- };
938
-
939
- // Initialize cache stats on mount
940
- React.useEffect(() => {
941
- // Small delay to let initial content load
942
- setTimeout(() => {
943
- updateCacheStats();
944
- }, 100);
945
- }, []);
946
-
947
- // Update cache stats when content changes
948
- React.useEffect(() => {
949
- updateCacheStats();
950
- }, [forceRefresh]);
951
-
952
- const triggerRender = (action: string) => {
953
- setRenderCount(prev => prev + 1);
954
- setLastAction(action);
955
- setForceRefresh(prev => prev + 1);
956
- };
957
-
958
- return (
959
- <DataProvider dataSource={{ dataProvider: provider }}>
960
- <div style={{ padding: '20px', maxWidth: '800px' }}>
961
- <h1>Performance & Caching Demo</h1>
962
-
963
- <div style={{
964
- padding: '20px',
965
- backgroundColor: '#e7f3ff',
966
- borderRadius: '8px',
967
- marginBottom: '20px',
968
- border: '2px solid #2196f3'
969
- }}>
970
- <h3 style={{ marginTop: 0, color: '#1976d2' }}>📊 Performance Metrics</h3>
971
-
972
- <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: '15px', marginBottom: '15px' }}>
973
- <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
974
- <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#4caf50' }}>{renderCount}</div>
975
- <div style={{ fontSize: '12px', color: '#666' }}>Renders</div>
976
- </div>
977
-
978
- {/* <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
979
- <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#2196f3' }}>{cacheStats.expiredEntries.length > 0 ? '✓' : '○'}</div>
980
- <div style={{ fontSize: '12px', color: '#666' }}>Cache Status</div>
981
- <div style={{ fontSize: '10px', color: '#999', fontStyle: 'italic' }}>{cacheStats.expiredEntries.length > 0 ? 'Active' : 'Empty'}</div>
982
- </div> */}
983
-
984
- <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
985
- <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#ff9800' }}>{renderCount > 1 ? '' : '○'}</div>
986
- <div style={{ fontSize: '12px', color: '#666' }}>Performance</div>
987
- <div style={{ fontSize: '10px', color: '#999', fontStyle: 'italic' }}>{renderCount > 1 ? 'Cached' : 'Loading'}</div>
988
- </div>
989
-
990
- <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
991
- <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#9c27b0' }}>{cacheStats.totalEntries}/{cacheStats.maxSize}</div>
992
- <div style={{ fontSize: '12px', color: '#666' }}>Cache Usage</div>
993
- </div>
994
- </div>
995
-
996
- <div style={{ marginBottom: '15px' }}>
997
- <strong>Last Action:</strong> <span style={{ color: '#1976d2' }}>{lastAction}</span>
998
- </div>
999
-
1000
- {/* <div style={{ marginBottom: '15px' }}>
1001
- <strong>Cached Field Groups:</strong>
1002
- <div style={{ marginTop: '5px' }}>
1003
- {cacheStats.expiredEntries.length > 0 ? (
1004
- cacheStats.expiredEntries.map(key => (
1005
- <span key={key} style={{
1006
- display: 'inline-block',
1007
- margin: '2px 5px 2px 0',
1008
- padding: '2px 8px',
1009
- backgroundColor: '#4caf50',
1010
- color: 'white',
1011
- borderRadius: '12px',
1012
- fontSize: '12px'
1013
- }}>
1014
- {key}
1015
- </span>
1016
- ))
1017
- ) : (
1018
- <span style={{ color: '#666', fontStyle: 'italic' }}>No cached data yet</span>
1019
- )}
1020
- </div>
1021
- </div> */}
1022
-
1023
- <small style={{ color: '#666' }}>
1024
- 💡 Open browser console to see detailed cache behavior logs.
1025
- </small>
1026
- </div>
1027
-
1028
- <div style={{ display: 'flex', gap: '10px', marginBottom: '20px', flexWrap: 'wrap' }}>
1029
- <button
1030
- onClick={() => triggerRender('Manual re-render')}
1031
- style={{
1032
- padding: '12px 24px',
1033
- backgroundColor: '#4caf50',
1034
- color: 'white',
1035
- border: 'none',
1036
- borderRadius: '6px',
1037
- cursor: 'pointer',
1038
- fontWeight: 'bold',
1039
- fontSize: '14px'
1040
- }}
1041
- >
1042
- 🔄 Render Again
1043
- </button>
1044
-
1045
- <button
1046
- onClick={() => {
1047
- provider.clearCache();
1048
- triggerRender('Cache cleared');
1049
- }}
1050
- style={{
1051
- padding: '12px 24px',
1052
- backgroundColor: '#f44336',
1053
- color: 'white',
1054
- border: 'none',
1055
- borderRadius: '6px',
1056
- cursor: 'pointer',
1057
- fontWeight: 'bold',
1058
- fontSize: '14px'
1059
- }}
1060
- >
1061
- 🗋 Clear Cache
1062
- </button>
1063
-
1064
- <button
1065
- onClick={() => {
1066
- // Reset all metrics
1067
- setRenderCount(1);
1068
- setLastAction('Reset all metrics');
1069
- provider.clearCache();
1070
- updateCacheStats();
1071
- }}
1072
- style={{
1073
- padding: '12px 24px',
1074
- backgroundColor: '#9c27b0',
1075
- color: 'white',
1076
- border: 'none',
1077
- borderRadius: '6px',
1078
- cursor: 'pointer',
1079
- fontWeight: 'bold',
1080
- fontSize: '14px'
1081
- }}
1082
- >
1083
- 🔄 Reset All
1084
- </button>
1085
- </div>
1086
-
1087
- <div style={{
1088
- padding: '20px',
1089
- backgroundColor: '#f8f9fa',
1090
- borderRadius: '8px',
1091
- marginBottom: '20px'
1092
- }}>
1093
- <h3>Cached Data Demo</h3>
1094
- <p style={{ marginBottom: '15px' }}>The data below is loaded through the DataProvider. Watch the cache metrics as you interact:</p>
1095
-
1096
- <div key={forceRefresh} style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '15px' }}>
1097
- <div style={{ padding: '15px', backgroundColor: 'white', borderRadius: '6px', border: '1px solid #ddd' }}>
1098
- <h4 style={{ marginTop: 0, color: '#1976d2' }}>Company Info</h4>
1099
- <p><strong>Name:</strong> {t`{{company.name}}`}</p>
1100
- <p><strong>Slogan:</strong> {t`{{company.slogan}}`}</p>
1101
- <p><strong>Founded:</strong> {t`{{company.founded}}`}</p>
1102
- {/* <p><small style={{ color: '#666' }}>Loaded from: {cacheStats.expiredEntries.includes('company') ? '💾 Cache' : '🔄 Fresh fetch'}</small></p> */}
1103
- </div>
1104
-
1105
- <div style={{ padding: '15px', backgroundColor: 'white', borderRadius: '6px', border: '1px solid #ddd' }}>
1106
- <h4 style={{ marginTop: 0, color: '#4caf50' }}>Features</h4>
1107
- <p><strong>First:</strong> {t`{{features.0.title}}`}</p>
1108
- <p><strong>Second:</strong> {t`{{features.1.title}}`}</p>
1109
- <p><strong>Icon:</strong> {t`{{features.0.icon}}`}</p>
1110
- {/* <p><small style={{ color: '#666' }}>Loaded from: {cacheStats.expiredEntries.includes('features') ? '💾 Cache' : '🔄 Fresh fetch'}</small></p> */}
1111
- </div>
1112
- </div>
1113
- </div>
1114
-
1115
- <div style={{
1116
- padding: '15px',
1117
- backgroundColor: '#fff3e0',
1118
- borderRadius: '6px',
1119
- fontSize: '14px'
1120
- }}>
1121
- <h4 style={{ marginTop: 0 }}>📝 How to Test:</h4>
1122
- <ol style={{ marginBottom: 0, paddingLeft: '20px' }}>
1123
- <li><strong>Watch "Cache Usage"</strong> - Shows {cacheStats.totalEntries}/{cacheStats.maxSize} cached items</li>
1124
- <li><strong>Click "Render Again"</strong> - Forces content reload, updates "Loaded from" indicators</li>
1125
- <li><strong>Click "Clear Cache"</strong> - Watch cache usage drop to 0/100, then rebuild</li>
1126
- <li><strong>Check "Cached Field Groups"</strong> - Green badges show what's currently cached</li>
1127
- <li><strong>Browser console logs</strong> - See JsonDataProvider cache hit/miss messages</li>
1128
- <li><strong>"Loaded from" indicators</strong> - Shows if data came from cache 💾 or fresh fetch 🔄</li>
1129
- </ol>
1130
- </div>
1131
- </div>
1132
- </DataProvider>
1133
- );
1134
- }
909
+ render: () => {
910
+ const [renderCount, setRenderCount] = useState(1);
911
+ const [cacheStats, setCacheStats] = useState<{ totalEntries: number; maxSize: number; expiredEntries: number }>({ totalEntries: 0, maxSize: 100, expiredEntries: 0 });
912
+ const [forceRefresh, setForceRefresh] = useState(0);
913
+ const [lastAction, setLastAction] = useState('Initial load');
914
+
915
+ // Create provider only once using useMemo
916
+ const provider = React.useMemo(() => {
917
+ const jsonProvider = new JsonDataProvider({
918
+ data: {
919
+ company: nestedTestData.company,
920
+ stats: nestedTestData.stats
921
+ }
922
+ });
923
+
924
+ return new CachedDataProvider(jsonProvider, { maxSize: 100, defaultTTL: 60000 });
925
+ }, []);
926
+
927
+ // Update cache stats when requested
928
+ const updateCacheStats = () => {
929
+ const stats = provider.getCacheStats();
930
+ // Map stats to expected shape for cacheStats state
931
+ setCacheStats({
932
+ totalEntries: stats.totalEntries ?? stats.totalEntries ?? 0,
933
+ maxSize: stats.maxSize ?? 100,
934
+ expiredEntries: stats.expiredEntries ?? stats.expiredEntries ?? 0
935
+ });
936
+ console.log('[Performance Demo] Cache stats updated:', stats);
937
+ };
938
+
939
+ // Initialize cache stats on mount
940
+ React.useEffect(() => {
941
+ // Small delay to let initial content load
942
+ setTimeout(() => {
943
+ updateCacheStats();
944
+ }, 100);
945
+ }, []);
946
+
947
+ // Update cache stats when content changes
948
+ React.useEffect(() => {
949
+ updateCacheStats();
950
+ }, [forceRefresh]);
951
+
952
+ const triggerRender = (action: string) => {
953
+ setRenderCount(prev => prev + 1);
954
+ setLastAction(action);
955
+ setForceRefresh(prev => prev + 1);
956
+ };
957
+
958
+ return (
959
+ <DataProvider dataSource={{ dataProvider: provider }}>
960
+ <div style={{ padding: '20px', maxWidth: '800px' }}>
961
+ <h1>Performance & Caching Demo</h1>
962
+
963
+ <div style={{
964
+ padding: '20px',
965
+ backgroundColor: '#e7f3ff',
966
+ borderRadius: '8px',
967
+ marginBottom: '20px',
968
+ border: '2px solid #2196f3'
969
+ }}>
970
+ <h3 style={{ marginTop: 0, color: '#1976d2' }}> Performance Metrics</h3>
971
+
972
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: '15px', marginBottom: '15px' }}>
973
+ <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
974
+ <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#4caf50' }}>{renderCount}</div>
975
+ <div style={{ fontSize: '12px', color: '#666' }}>Renders</div>
976
+ </div>
977
+
978
+ {/* <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
979
+ <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#2196f3' }}>{cacheStats.expiredEntries.length > 0 ? '✓' : '○'}</div>
980
+ <div style={{ fontSize: '12px', color: '#666' }}>Cache Status</div>
981
+ <div style={{ fontSize: '10px', color: '#999', fontStyle: 'italic' }}>{cacheStats.expiredEntries.length > 0 ? 'Active' : 'Empty'}</div>
982
+ </div> */}
983
+
984
+ <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
985
+ <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#ff9800' }}>{renderCount > 1 ? '' : '○'}</div>
986
+ <div style={{ fontSize: '12px', color: '#666' }}>Performance</div>
987
+ <div style={{ fontSize: '10px', color: '#999', fontStyle: 'italic' }}>{renderCount > 1 ? 'Cached' : 'Loading'}</div>
988
+ </div>
989
+
990
+ <div style={{ textAlign: 'center', padding: '10px', backgroundColor: 'white', borderRadius: '4px' }}>
991
+ <div style={{ fontSize: '24px', fontWeight: 'bold', color: '#9c27b0' }}>{cacheStats.totalEntries}/{cacheStats.maxSize}</div>
992
+ <div style={{ fontSize: '12px', color: '#666' }}>Cache Usage</div>
993
+ </div>
994
+ </div>
995
+
996
+ <div style={{ marginBottom: '15px' }}>
997
+ <strong>Last Action:</strong> <span style={{ color: '#1976d2' }}>{lastAction}</span>
998
+ </div>
999
+
1000
+ {/* <div style={{ marginBottom: '15px' }}>
1001
+ <strong>Cached Field Groups:</strong>
1002
+ <div style={{ marginTop: '5px' }}>
1003
+ {cacheStats.expiredEntries.length > 0 ? (
1004
+ cacheStats.expiredEntries.map(key => (
1005
+ <span key={key} style={{
1006
+ display: 'inline-block',
1007
+ margin: '2px 5px 2px 0',
1008
+ padding: '2px 8px',
1009
+ backgroundColor: '#4caf50',
1010
+ color: 'white',
1011
+ borderRadius: '12px',
1012
+ fontSize: '12px'
1013
+ }}>
1014
+ {key}
1015
+ </span>
1016
+ ))
1017
+ ) : (
1018
+ <span style={{ color: '#666', fontStyle: 'italic' }}>No cached data yet</span>
1019
+ )}
1020
+ </div>
1021
+ </div> */}
1022
+
1023
+ <small style={{ color: '#666' }}>
1024
+ 💡 Open browser console to see detailed cache behavior logs.
1025
+ </small>
1026
+ </div>
1027
+
1028
+ <div style={{ display: 'flex', gap: '10px', marginBottom: '20px', flexWrap: 'wrap' }}>
1029
+ <button
1030
+ onClick={() => triggerRender('Manual re-render')}
1031
+ style={{
1032
+ padding: '12px 24px',
1033
+ backgroundColor: '#4caf50',
1034
+ color: 'white',
1035
+ border: 'none',
1036
+ borderRadius: '6px',
1037
+ cursor: 'pointer',
1038
+ fontWeight: 'bold',
1039
+ fontSize: '14px'
1040
+ }}
1041
+ >
1042
+ Render Again
1043
+ </button>
1044
+
1045
+ <button
1046
+ onClick={() => {
1047
+ provider.clearCache();
1048
+ triggerRender('Cache cleared');
1049
+ }}
1050
+ style={{
1051
+ padding: '12px 24px',
1052
+ backgroundColor: '#f44336',
1053
+ color: 'white',
1054
+ border: 'none',
1055
+ borderRadius: '6px',
1056
+ cursor: 'pointer',
1057
+ fontWeight: 'bold',
1058
+ fontSize: '14px'
1059
+ }}
1060
+ >
1061
+ 🗋 Clear Cache
1062
+ </button>
1063
+
1064
+ <button
1065
+ onClick={() => {
1066
+ // Reset all metrics
1067
+ setRenderCount(1);
1068
+ setLastAction('Reset all metrics');
1069
+ provider.clearCache();
1070
+ updateCacheStats();
1071
+ }}
1072
+ style={{
1073
+ padding: '12px 24px',
1074
+ backgroundColor: '#9c27b0',
1075
+ color: 'white',
1076
+ border: 'none',
1077
+ borderRadius: '6px',
1078
+ cursor: 'pointer',
1079
+ fontWeight: 'bold',
1080
+ fontSize: '14px'
1081
+ }}
1082
+ >
1083
+ Reset All
1084
+ </button>
1085
+ </div>
1086
+
1087
+ <div style={{
1088
+ padding: '20px',
1089
+ backgroundColor: '#f8f9fa',
1090
+ borderRadius: '8px',
1091
+ marginBottom: '20px'
1092
+ }}>
1093
+ <h3>Cached Data Demo</h3>
1094
+ <p style={{ marginBottom: '15px' }}>The data below is loaded through the DataProvider. Watch the cache metrics as you interact:</p>
1095
+
1096
+ <div key={forceRefresh} style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '15px' }}>
1097
+ <div style={{ padding: '15px', backgroundColor: 'white', borderRadius: '6px', border: '1px solid #ddd' }}>
1098
+ <h4 style={{ marginTop: 0, color: '#1976d2' }}>Company Info</h4>
1099
+ <p><strong>Name:</strong> {t`{{company.name}}`}</p>
1100
+ <p><strong>Slogan:</strong> {t`{{company.slogan}}`}</p>
1101
+ <p><strong>Founded:</strong> {t`{{company.founded}}`}</p>
1102
+ {/* <p><small style={{ color: '#666' }}>Loaded from: {cacheStats.expiredEntries.includes('company') ? ' Cache' : ' Fresh fetch'}</small></p> */}
1103
+ </div>
1104
+
1105
+ <div style={{ padding: '15px', backgroundColor: 'white', borderRadius: '6px', border: '1px solid #ddd' }}>
1106
+ <h4 style={{ marginTop: 0, color: '#4caf50' }}>Features</h4>
1107
+ <p><strong>First:</strong> {t`{{features.0.title}}`}</p>
1108
+ <p><strong>Second:</strong> {t`{{features.1.title}}`}</p>
1109
+ <p><strong>Icon:</strong> {t`{{features.0.icon}}`}</p>
1110
+ {/* <p><small style={{ color: '#666' }}>Loaded from: {cacheStats.expiredEntries.includes('features') ? ' Cache' : ' Fresh fetch'}</small></p> */}
1111
+ </div>
1112
+ </div>
1113
+ </div>
1114
+
1115
+ <div style={{
1116
+ padding: '15px',
1117
+ backgroundColor: '#fff3e0',
1118
+ borderRadius: '6px',
1119
+ fontSize: '14px'
1120
+ }}>
1121
+ <h4 style={{ marginTop: 0 }}> How to Test:</h4>
1122
+ <ol style={{ marginBottom: 0, paddingLeft: '20px' }}>
1123
+ <li><strong>Watch "Cache Usage"</strong> - Shows {cacheStats.totalEntries}/{cacheStats.maxSize} cached items</li>
1124
+ <li><strong>Click "Render Again"</strong> - Forces content reload, updates "Loaded from" indicators</li>
1125
+ <li><strong>Click "Clear Cache"</strong> - Watch cache usage drop to 0/100, then rebuild</li>
1126
+ <li><strong>Check "Cached Field Groups"</strong> - Green badges show what's currently cached</li>
1127
+ <li><strong>Browser console logs</strong> - See JsonDataProvider cache hit/miss messages</li>
1128
+ <li><strong>"Loaded from" indicators</strong> - Shows if data came from cache or fresh fetch </li>
1129
+ </ol>
1130
+ </div>
1131
+ </div>
1132
+ </DataProvider>
1133
+ );
1134
+ }
1135
1135
  };
1136
1136
 
1137
1137
  /**
1138
1138
  * Deep nested property access demonstration with enhanced MUI styling
1139
1139
  */
1140
1140
  export const NestedProperties: Story = {
1141
- render: () => {
1142
- const provider = new JsonDataProvider({
1143
- data: nestedTestData,
1144
- enableLogging: true
1145
- });
1146
-
1147
- return (
1148
- <DataProvider dataSource={{ dataProvider: provider }}>
1149
- <Box sx={{ p: 3 }}>
1150
- <Typography variant="h4" gutterBottom>Deep Nested Property Access</Typography>
1151
- <Typography variant="body1" paragraph>
1152
- JsonDataProvider supports deep nested property access using dot notation.
1153
- </Typography>
1154
-
1155
- <GridLayout columns={2} spacing={3}>
1156
- <GridCell>
1157
- <Card sx={{ bgcolor: 'primary.50' }}>
1158
- <CardContent>
1159
- <Typography variant="h6" gutterBottom>Basic Properties</Typography>
1160
- <Typography variant="body2" paragraph>{t`Company: {{company.name}}`}</Typography>
1161
- <Typography variant="body2" paragraph>{t`Industry: {{company.profile.industry}}`}</Typography>
1162
- <Typography variant="body2">{t`Size: {{company.profile.size}}`}</Typography>
1163
- </CardContent>
1164
- </Card>
1165
- </GridCell>
1166
-
1167
- <GridCell>
1168
- <Card sx={{ bgcolor: 'success.50' }}>
1169
- <CardContent>
1170
- <Typography variant="h6" gutterBottom>Location Details</Typography>
1171
- <Typography variant="body2" paragraph>{t`City: {{company.profile.location.city}}`}</Typography>
1172
- <Typography variant="body2" paragraph>{t`State: {{company.profile.location.state}}`}</Typography>
1173
- <Typography variant="body2" paragraph>{t`Country: {{company.profile.location.country}}`}</Typography>
1174
- <Typography variant="body2">{t`Timezone: {{company.profile.location.timezone}}`}</Typography>
1175
- </CardContent>
1176
- </Card>
1177
- </GridCell>
1178
-
1179
- <GridCell>
1180
- <Card sx={{ bgcolor: 'warning.50' }}>
1181
- <CardContent>
1182
- <Typography variant="h6" gutterBottom>Contact Information</Typography>
1183
- <Typography variant="body2" paragraph>{t`Email: {{company.profile.contact.email}}`}</Typography>
1184
- <Typography variant="body2" paragraph>{t`Phone: {{company.profile.contact.phone}}`}</Typography>
1185
- <Typography variant="body2" paragraph>{t`Website: {{company.profile.contact.social.website}}`}</Typography>
1186
- <Typography variant="body2">{t`Twitter: {{company.profile.contact.social.twitter}}`}</Typography>
1187
- </CardContent>
1188
- </Card>
1189
- </GridCell>
1190
-
1191
- <GridCell>
1192
- <Card sx={{ bgcolor: 'secondary.50' }}>
1193
- <CardContent>
1194
- <Typography variant="h6" gutterBottom>Business Metrics</Typography>
1195
- <Typography variant="body2" paragraph>{t`Employees: {{company.metrics.employees}}`}</Typography>
1196
- <Typography variant="body2" paragraph>{t`Revenue: {{company.metrics.revenue}}`}</Typography>
1197
- <Typography variant="body2" paragraph>{t`Growth: {{company.metrics.growth}}%`}</Typography>
1198
- <Typography variant="body2">{t`Founded: {{company.metrics.founded}}`}</Typography>
1199
- </CardContent>
1200
- </Card>
1201
- </GridCell>
1202
- </GridLayout>
1203
-
1204
- <Paper elevation={2} sx={{ p: 3, mt: 4 }}>
1205
- <Typography variant="h6" gutterBottom>Complex Template:</Typography>
1206
- <Paper variant="outlined" sx={{ p: 2, mt: 2, bgcolor: 'grey.50' }}>
1207
- {t`{{company.name}} is a {{company.profile.industry}} company based in {{company.profile.location.city}}, {{company.profile.location.state}}. Founded in {{company.metrics.founded}}, we now have {{company.metrics.employees}} employees and generated {{company.metrics.revenue}} in revenue. Contact us at {{company.profile.contact.email}} or visit {{company.profile.contact.social.website}}.`}
1208
- </Paper>
1209
- </Paper>
1210
- </Box>
1211
- </DataProvider>
1212
- );
1213
- }
1141
+ render: () => {
1142
+ const provider = new JsonDataProvider({
1143
+ data: nestedTestData,
1144
+ enableLogging: true
1145
+ });
1146
+
1147
+ return (
1148
+ <DataProvider dataSource={{ dataProvider: provider }}>
1149
+ <Box sx={{ p: 3 }}>
1150
+ <Typography variant="h4" gutterBottom>Deep Nested Property Access</Typography>
1151
+ <Typography variant="body1" paragraph>
1152
+ JsonDataProvider supports deep nested property access using dot notation.
1153
+ </Typography>
1154
+
1155
+ <GridLayout columns={2} spacing={3}>
1156
+ <GridCell>
1157
+ <Card sx={{ bgcolor: 'primary.50' }}>
1158
+ <CardContent>
1159
+ <Typography variant="h6" gutterBottom>Basic Properties</Typography>
1160
+ <Typography variant="body2" paragraph>{t`Company: {{company.name}}`}</Typography>
1161
+ <Typography variant="body2" paragraph>{t`Industry: {{company.profile.industry}}`}</Typography>
1162
+ <Typography variant="body2">{t`Size: {{company.profile.size}}`}</Typography>
1163
+ </CardContent>
1164
+ </Card>
1165
+ </GridCell>
1166
+
1167
+ <GridCell>
1168
+ <Card sx={{ bgcolor: 'success.50' }}>
1169
+ <CardContent>
1170
+ <Typography variant="h6" gutterBottom>Location Details</Typography>
1171
+ <Typography variant="body2" paragraph>{t`City: {{company.profile.location.city}}`}</Typography>
1172
+ <Typography variant="body2" paragraph>{t`State: {{company.profile.location.state}}`}</Typography>
1173
+ <Typography variant="body2" paragraph>{t`Country: {{company.profile.location.country}}`}</Typography>
1174
+ <Typography variant="body2">{t`Timezone: {{company.profile.location.timezone}}`}</Typography>
1175
+ </CardContent>
1176
+ </Card>
1177
+ </GridCell>
1178
+
1179
+ <GridCell>
1180
+ <Card sx={{ bgcolor: 'warning.50' }}>
1181
+ <CardContent>
1182
+ <Typography variant="h6" gutterBottom>Contact Information</Typography>
1183
+ <Typography variant="body2" paragraph>{t`Email: {{company.profile.contact.email}}`}</Typography>
1184
+ <Typography variant="body2" paragraph>{t`Phone: {{company.profile.contact.phone}}`}</Typography>
1185
+ <Typography variant="body2" paragraph>{t`Website: {{company.profile.contact.social.website}}`}</Typography>
1186
+ <Typography variant="body2">{t`Twitter: {{company.profile.contact.social.twitter}}`}</Typography>
1187
+ </CardContent>
1188
+ </Card>
1189
+ </GridCell>
1190
+
1191
+ <GridCell>
1192
+ <Card sx={{ bgcolor: 'secondary.50' }}>
1193
+ <CardContent>
1194
+ <Typography variant="h6" gutterBottom>Business Metrics</Typography>
1195
+ <Typography variant="body2" paragraph>{t`Employees: {{company.metrics.employees}}`}</Typography>
1196
+ <Typography variant="body2" paragraph>{t`Revenue: {{company.metrics.revenue}}`}</Typography>
1197
+ <Typography variant="body2" paragraph>{t`Growth: {{company.metrics.growth}}%`}</Typography>
1198
+ <Typography variant="body2">{t`Founded: {{company.metrics.founded}}`}</Typography>
1199
+ </CardContent>
1200
+ </Card>
1201
+ </GridCell>
1202
+ </GridLayout>
1203
+
1204
+ <Paper elevation={2} sx={{ p: 3, mt: 4 }}>
1205
+ <Typography variant="h6" gutterBottom>Complex Template:</Typography>
1206
+ <Paper variant="outlined" sx={{ p: 2, mt: 2, bgcolor: 'grey.50' }}>
1207
+ {t`{{company.name}} is a {{company.profile.industry}} company based in {{company.profile.location.city}}, {{company.profile.location.state}}. Founded in {{company.metrics.founded}}, we now have {{company.metrics.employees}} employees and generated {{company.metrics.revenue}} in revenue. Contact us at {{company.profile.contact.email}} or visit {{company.profile.contact.social.website}}.`}
1208
+ </Paper>
1209
+ </Paper>
1210
+ </Box>
1211
+ </DataProvider>
1212
+ );
1213
+ }
1214
1214
  };
1215
1215
 
1216
1216
  /**
1217
1217
  * Caching behavior demonstration with side-by-side providers
1218
1218
  */
1219
1219
  export const CachingBehavior: Story = {
1220
- render: () => {
1221
- const dataProvider = new JsonDataProvider({ data: nestedTestData })
1222
- const fastCacheProvider = new CachedDataProvider(dataProvider, {
1223
- defaultTTL: 3000, // 3 seconds
1224
- maxSize: 5,
1225
- enableLogging: true
1226
- });
1227
-
1228
- const slowCacheProvider = new CachedDataProvider(dataProvider, {
1229
- defaultTTL: 10000, // 10 seconds
1230
- maxSize: 5,
1231
- enableLogging: true
1232
- });
1233
-
1234
- return (
1235
- <Box sx={{ p: 3 }}>
1236
- <Typography variant="h4" gutterBottom>Caching Behavior</Typography>
1237
- <Typography variant="body1" paragraph>
1238
- Compare different caching configurations. Check the browser console to see cache logs.
1239
- </Typography>
1240
-
1241
- <GridLayout columns={2} spacing={3}>
1242
- <GridCell>
1243
- <Typography variant="h6" gutterBottom>Fast Cache (3s timeout)</Typography>
1244
- <ProviderDemo provider={fastCacheProvider} title="Fast Cache Provider" />
1245
- </GridCell>
1246
- <GridCell>
1247
- <Typography variant="h6" gutterBottom>Slow Cache (10s timeout)</Typography>
1248
- <ProviderDemo provider={slowCacheProvider} title="Slow Cache Provider" />
1249
- </GridCell>
1250
- </GridLayout>
1251
-
1252
- <Alert severity="info" sx={{ mt: 2 }}>
1253
- <Typography variant="subtitle2" gutterBottom>💡 Try This:</Typography>
1254
- <ol>
1255
- <li>Open browser console to see cache logs</li>
1256
- <li>Click "Force Refresh" buttons to trigger content loading</li>
1257
- <li>Notice how fast cache expires content more quickly</li>
1258
- <li>Click "Clear Cache" to manually invalidate cache</li>
1259
- </ol>
1260
- </Alert>
1261
- </Box>
1262
- );
1263
- }
1220
+ render: () => {
1221
+ const dataProvider = new JsonDataProvider({ data: nestedTestData })
1222
+ const fastCacheProvider = new CachedDataProvider(dataProvider, {
1223
+ defaultTTL: 3000, // 3 seconds
1224
+ maxSize: 5,
1225
+ enableLogging: true
1226
+ });
1227
+
1228
+ const slowCacheProvider = new CachedDataProvider(dataProvider, {
1229
+ defaultTTL: 10000, // 10 seconds
1230
+ maxSize: 5,
1231
+ enableLogging: true
1232
+ });
1233
+
1234
+ return (
1235
+ <Box sx={{ p: 3 }}>
1236
+ <Typography variant="h4" gutterBottom>Caching Behavior</Typography>
1237
+ <Typography variant="body1" paragraph>
1238
+ Compare different caching configurations. Check the browser console to see cache logs.
1239
+ </Typography>
1240
+
1241
+ <GridLayout columns={2} spacing={3}>
1242
+ <GridCell>
1243
+ <Typography variant="h6" gutterBottom>Fast Cache (3s timeout)</Typography>
1244
+ <ProviderDemo provider={fastCacheProvider} title="Fast Cache Provider" />
1245
+ </GridCell>
1246
+ <GridCell>
1247
+ <Typography variant="h6" gutterBottom>Slow Cache (10s timeout)</Typography>
1248
+ <ProviderDemo provider={slowCacheProvider} title="Slow Cache Provider" />
1249
+ </GridCell>
1250
+ </GridLayout>
1251
+
1252
+ <Alert severity="info" sx={{ mt: 2 }}>
1253
+ <Typography variant="subtitle2" gutterBottom>💡 Try This:</Typography>
1254
+ <ol>
1255
+ <li>Open browser console to see cache logs</li>
1256
+ <li>Click "Force Refresh" buttons to trigger content loading</li>
1257
+ <li>Notice how fast cache expires content more quickly</li>
1258
+ <li>Click "Clear Cache" to manually invalidate cache</li>
1259
+ </ol>
1260
+ </Alert>
1261
+ </Box>
1262
+ );
1263
+ }
1264
1264
  };
1265
1265
 
1266
1266
  /**
1267
1267
  * Interactive configuration playground with real-time settings
1268
1268
  */
1269
1269
  export const ConfigurationPlayground: Story = {
1270
- render: () => {
1271
- const [config, setConfig] = useState({
1272
- cacheTimeout: 5000,
1273
- maxCacheSize: 10,
1274
- enableLogging: true
1275
- });
1276
-
1277
- const [provider, setProvider] = useState(() => new JsonDataProvider({
1278
- data: nestedTestData,
1279
- ...config
1280
- }));
1281
-
1282
- const updateConfig = (newConfig: Partial<typeof config>) => {
1283
- const updatedConfig = { ...config, ...newConfig };
1284
- setConfig(updatedConfig);
1285
- setProvider(new JsonDataProvider({
1286
- data: nestedTestData,
1287
- ...updatedConfig
1288
- }));
1289
- };
1290
-
1291
- return (
1292
- <Box sx={{ p: 3 }}>
1293
- <Typography variant="h4" gutterBottom>Configuration Playground</Typography>
1294
- <Typography variant="body1" paragraph>
1295
- Experiment with different JsonDataProvider configuration options.
1296
- </Typography>
1297
-
1298
- <GridLayout spacing={3} equalHeight>
1299
- <GridCell span={12}>
1300
- <Alert severity="success" sx={{ mt: 2 }}>
1301
- <Typography variant="subtitle2" gutterBottom>💡 Tips:</Typography>
1302
- <ul style={{ margin: 0, paddingLeft: '20px' }}>
1303
- <li>Lower cache timeout = more frequent cache invalidation</li>
1304
- <li>Smaller max cache size = more aggressive LRU eviction</li>
1305
- <li>Enable logging to see cache behavior in console</li>
1306
- <li>Watch cache stats update in real-time</li>
1307
- </ul>
1308
- </Alert>
1309
- </GridCell>
1310
- <Card>
1311
- <CardContent>
1312
- <Typography variant="h6" gutterBottom>Configuration</Typography>
1313
-
1314
- <Box sx={{ mb: 3 }}>
1315
- <Typography gutterBottom>Cache Timeout (ms): {config.cacheTimeout}</Typography>
1316
- <Slider
1317
- value={config.cacheTimeout}
1318
- min={1000}
1319
- max={30000}
1320
- step={1000}
1321
- onChange={(_, value) => updateConfig({ cacheTimeout: value as number })}
1322
- />
1323
- </Box>
1324
-
1325
- <Box sx={{ mb: 3 }}>
1326
- <Typography gutterBottom>Max Cache Size: {config.maxCacheSize}</Typography>
1327
- <Slider
1328
- value={config.maxCacheSize}
1329
- min={1}
1330
- max={50}
1331
- step={1}
1332
- onChange={(_, value) => updateConfig({ maxCacheSize: value as number })}
1333
- />
1334
- </Box>
1335
-
1336
- <FormControlLabel
1337
- control={
1338
- <Checkbox
1339
- checked={config.enableLogging}
1340
- onChange={(e) => updateConfig({ enableLogging: e.target.checked })}
1341
- />
1342
- }
1343
- label="Enable Logging"
1344
- sx={{ mb: 2 }}
1345
- />
1346
-
1347
- <Paper variant="outlined" sx={{ p: 2, bgcolor: 'grey.50' }}>
1348
- <Typography variant="subtitle2" gutterBottom>Current Config:</Typography>
1349
- <Typography variant="body2" component="pre" sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}>
1350
- {JSON.stringify(config, null, 2)}
1351
- </Typography>
1352
- </Paper>
1353
- </CardContent>
1354
- </Card>
1355
-
1356
- <ProviderDemo provider={provider} title="Configured Provider" />
1357
- </GridLayout>
1358
- </Box>
1359
- );
1360
- }
1270
+ render: () => {
1271
+ const [config, setConfig] = useState({
1272
+ cacheTimeout: 5000,
1273
+ maxCacheSize: 10,
1274
+ enableLogging: true
1275
+ });
1276
+
1277
+ const [provider, setProvider] = useState(() => new JsonDataProvider({
1278
+ data: nestedTestData,
1279
+ ...config
1280
+ }));
1281
+
1282
+ const updateConfig = (newConfig: Partial<typeof config>) => {
1283
+ const updatedConfig = { ...config, ...newConfig };
1284
+ setConfig(updatedConfig);
1285
+ setProvider(new JsonDataProvider({
1286
+ data: nestedTestData,
1287
+ ...updatedConfig
1288
+ }));
1289
+ };
1290
+
1291
+ return (
1292
+ <Box sx={{ p: 3 }}>
1293
+ <Typography variant="h4" gutterBottom>Configuration Playground</Typography>
1294
+ <Typography variant="body1" paragraph>
1295
+ Experiment with different JsonDataProvider configuration options.
1296
+ </Typography>
1297
+
1298
+ <GridLayout spacing={3} equalHeight>
1299
+ <GridCell span={12}>
1300
+ <Alert severity="success" sx={{ mt: 2 }}>
1301
+ <Typography variant="subtitle2" gutterBottom>💡 Tips:</Typography>
1302
+ <ul style={{ margin: 0, paddingLeft: '20px' }}>
1303
+ <li>Lower cache timeout = more frequent cache invalidation</li>
1304
+ <li>Smaller max cache size = more aggressive LRU eviction</li>
1305
+ <li>Enable logging to see cache behavior in console</li>
1306
+ <li>Watch cache stats update in real-time</li>
1307
+ </ul>
1308
+ </Alert>
1309
+ </GridCell>
1310
+ <Card>
1311
+ <CardContent>
1312
+ <Typography variant="h6" gutterBottom>Configuration</Typography>
1313
+
1314
+ <Box sx={{ mb: 3 }}>
1315
+ <Typography gutterBottom>Cache Timeout (ms): {config.cacheTimeout}</Typography>
1316
+ <Slider
1317
+ value={config.cacheTimeout}
1318
+ min={1000}
1319
+ max={30000}
1320
+ step={1000}
1321
+ onChange={(_, value) => updateConfig({ cacheTimeout: value as number })}
1322
+ />
1323
+ </Box>
1324
+
1325
+ <Box sx={{ mb: 3 }}>
1326
+ <Typography gutterBottom>Max Cache Size: {config.maxCacheSize}</Typography>
1327
+ <Slider
1328
+ value={config.maxCacheSize}
1329
+ min={1}
1330
+ max={50}
1331
+ step={1}
1332
+ onChange={(_, value) => updateConfig({ maxCacheSize: value as number })}
1333
+ />
1334
+ </Box>
1335
+
1336
+ <FormControlLabel
1337
+ control={
1338
+ <Checkbox
1339
+ checked={config.enableLogging}
1340
+ onChange={(e) => updateConfig({ enableLogging: e.target.checked })}
1341
+ />
1342
+ }
1343
+ label="Enable Logging"
1344
+ sx={{ mb: 2 }}
1345
+ />
1346
+
1347
+ <Paper variant="outlined" sx={{ p: 2, bgcolor: 'grey.50' }}>
1348
+ <Typography variant="subtitle2" gutterBottom>Current Config:</Typography>
1349
+ <Typography variant="body2" component="pre" sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}>
1350
+ {JSON.stringify(config, null, 2)}
1351
+ </Typography>
1352
+ </Paper>
1353
+ </CardContent>
1354
+ </Card>
1355
+
1356
+ <ProviderDemo provider={provider} title="Configured Provider" />
1357
+ </GridLayout>
1358
+ </Box>
1359
+ );
1360
+ }
1361
1361
  };