@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
@@ -0,0 +1,199 @@
1
+ /**
2
+ * ModelView - Abstract Base Class for Serializable Components
3
+ *
4
+ * Provides common serialization patterns and data binding support for
5
+ * all QwickApps components. This eliminates code duplication and ensures
6
+ * consistent behavior across all serializable components.
7
+ *
8
+ * All components extending ModelView must implement:
9
+ * - getComponentSpecificProps(): Component-specific serialization properties
10
+ * - renderView(): Render method for traditional props
11
+ * - renderWithDataBinding(): Render method for data-bound components
12
+ * - static tagName and version properties
13
+ *
14
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
15
+ */
16
+
17
+ import React, { ReactElement, ReactNode } from 'react';
18
+ import type { WithDataBinding } from '@qwickapps/schema';
19
+ import { Serializable } from '../../schemas/types/Serializable';
20
+ import { extractTextFromReactNode } from '../../utils/reactUtils';
21
+
22
+ /**
23
+ * Abstract base class for all serializable QwickApps components
24
+ * Handles common serialization patterns, data binding, and rendering logic
25
+ */
26
+ export abstract class ModelView<TProps = any, TModel = any>
27
+ extends React.Component<TProps & WithDataBinding>
28
+ implements Serializable {
29
+
30
+ // Static properties - must be overridden by subclasses
31
+ // Note: TypeScript doesn't support abstract static members, so we provide defaults
32
+ static readonly tagName: string = '';
33
+ static readonly version: string = '';
34
+
35
+ /**
36
+ * Common fromJson implementation
37
+ * Subclasses can override this if they need custom deserialization logic
38
+ */
39
+ static fromJson(jsonData: any): ReactElement {
40
+ // This will be overridden by subclasses but provides the base pattern
41
+ const ComponentClass = this as any;
42
+ return React.createElement(ComponentClass, jsonData);
43
+ }
44
+
45
+ /**
46
+ * Common toJson implementation with hooks for customization
47
+ * Combines base serializable props with component-specific props
48
+ */
49
+ toJson(): any {
50
+ const baseProps = this.getBaseSerializableProps();
51
+ const componentProps = this.getComponentSpecificProps();
52
+
53
+ return {
54
+ ...baseProps,
55
+ ...componentProps
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Common base props that all components serialize
61
+ * Handles ReactNode children, data binding, and common styling props
62
+ */
63
+ protected getBaseSerializableProps(): any {
64
+ const props = this.props as any;
65
+ return {
66
+ // Handle ReactNode children conversion
67
+ children: props.children ? this.serializeChildren(props.children) : undefined,
68
+ // Preserve data binding
69
+ dataSource: props.dataSource,
70
+ bindingOptions: props.bindingOptions,
71
+ // Common styling props that many components have
72
+ className: props.className,
73
+ id: props.id
74
+ };
75
+ }
76
+
77
+ /**
78
+ * Hook for subclasses to add component-specific serialization
79
+ * Must be implemented by each component to include its specific props
80
+ */
81
+ protected abstract getComponentSpecificProps(): any;
82
+
83
+ /**
84
+ * Common children serialization logic
85
+ * Handles different children types based on component capabilities
86
+ */
87
+ protected serializeChildren(children: ReactNode): any {
88
+ if (typeof children === 'string') {
89
+ return children;
90
+ }
91
+
92
+ // For complex components (Section), we'll let the specific component handle serialization
93
+ // to avoid circular dependencies. The Section component can override this method.
94
+ if (this.hasNestedComponents(children)) {
95
+ // This should be overridden by components that support nested components
96
+ throw new Error('Components with nested components must override serializeChildren method');
97
+ }
98
+
99
+ // For simple components (Code, Button), extract text
100
+ return extractTextFromReactNode(children);
101
+ }
102
+
103
+ /**
104
+ * Helper to determine if children contain other serializable components
105
+ * Override in subclasses that support nested components (like Section)
106
+ */
107
+ protected hasNestedComponents(children: ReactNode): boolean {
108
+ // Default: false for simple components
109
+ return false;
110
+ }
111
+
112
+ /**
113
+ * Common render pattern
114
+ * Determines whether to use data binding or traditional props rendering
115
+ */
116
+ render() {
117
+ const props = this.props as any;
118
+
119
+ if (props.dataSource) {
120
+ return this.renderWithDataBinding();
121
+ }
122
+
123
+ return this.renderView();
124
+ }
125
+
126
+ /**
127
+ * Hook for subclasses to implement traditional props rendering
128
+ * This is where the component renders using props directly
129
+ */
130
+ protected abstract renderView(): React.ReactElement;
131
+
132
+ /**
133
+ * Hook for subclasses to implement data binding rendering
134
+ * This should render the data-bound version of the component
135
+ */
136
+ protected abstract renderWithDataBinding(): React.ReactElement;
137
+
138
+ /**
139
+ * Register HTML pattern handlers for this component
140
+ * Override this method to register patterns that this component can handle
141
+ * @param registry - ComponentTransformer to register patterns with
142
+ */
143
+ static registerPatternHandlers(registry: any): void {
144
+ // Default: no patterns to register
145
+ // Subclasses should override this to register their HTML patterns
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Type helper for ModelView component constructors
151
+ * Ensures proper typing for registration and factory functions
152
+ */
153
+ export interface ModelViewConstructor<TProps = any, TModel = any> {
154
+ new (props: TProps & WithDataBinding): ModelView<TProps, TModel>;
155
+ readonly tagName: string;
156
+ readonly version: string;
157
+ fromJson(jsonData: any): ReactElement;
158
+ }
159
+
160
+ /**
161
+ * Helper function to create a ModelView component class
162
+ * Provides better TypeScript inference for component registration
163
+ */
164
+ export function createModelViewClass<TProps, TModel>(
165
+ config: {
166
+ tagName: string;
167
+ version: string;
168
+ getComponentSpecificProps: (props: TProps) => any;
169
+ hasNestedComponents?: (children: ReactNode) => boolean;
170
+ renderView: (props: TProps) => React.ReactElement;
171
+ renderWithDataBinding: (props: TProps & WithDataBinding) => React.ReactElement;
172
+ }
173
+ ): ModelViewConstructor<TProps, TModel> {
174
+
175
+ class DynamicModelView extends ModelView<TProps, TModel> {
176
+ static readonly tagName = config.tagName;
177
+ static readonly version = config.version;
178
+
179
+ protected getComponentSpecificProps(): any {
180
+ return config.getComponentSpecificProps(this.props as TProps);
181
+ }
182
+
183
+ protected hasNestedComponents(children: ReactNode): boolean {
184
+ return config.hasNestedComponents ? config.hasNestedComponents(children) : false;
185
+ }
186
+
187
+ protected renderView(): React.ReactElement {
188
+ return config.renderView(this.props as TProps);
189
+ }
190
+
191
+ protected renderWithDataBinding(): React.ReactElement {
192
+ return config.renderWithDataBinding(this.props as TProps & WithDataBinding);
193
+ }
194
+ }
195
+
196
+ return DynamicModelView as ModelViewConstructor<TProps, TModel>;
197
+ }
198
+
199
+ export default ModelView;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Base Components and Abstractions
3
+ *
4
+ * Provides common base classes and utilities for creating
5
+ * serializable QwickApps components.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ export { default as ModelView, createModelViewClass } from './ModelView';
11
+ export type { ModelViewConstructor } from './ModelView';
@@ -15,11 +15,11 @@
15
15
 
16
16
  import { Box, Typography } from '@mui/material';
17
17
  import { WithDataBinding, ModelProps } from '@qwickapps/schema';
18
- import React from 'react';
18
+ import React, { ReactElement } from 'react';
19
19
  import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../../hooks';
20
20
  import ArticleModel from '../../schemas/ArticleSchema';
21
+ import { ModelView } from '../base/ModelView';
21
22
  import Html from '../Html';
22
- import { defaultArticleRules, TransformConfig } from '../../utils/htmlTransform';
23
23
 
24
24
  type ArticleViewProps = ModelProps<ArticleModel>;
25
25
 
@@ -65,22 +65,15 @@ function ArticleView({
65
65
  );
66
66
  }
67
67
 
68
- // Configure transformation for article content
69
- const transformConfig: TransformConfig = {
70
- rules: defaultArticleRules,
71
- sanitize: true
72
- };
73
-
74
68
  return (
75
69
  <Html
76
70
  component="article"
77
- transformConfig={transformConfig}
78
71
  stripHeaders={skipHeader}
79
72
  placeholder="No content available"
80
73
  {...htmlProps}
81
- {...styleProps}
82
74
  {...otherProps}
83
- sx={{
75
+ {...styleProps}
76
+ {...{
84
77
  // Ensure proper width constraints for article content
85
78
  maxWidth: '800px',
86
79
  mx: 'auto',
@@ -126,18 +119,65 @@ function ArticleView({
126
119
  );
127
120
  }
128
121
 
129
- // Main component with data binding support
130
- function Article(props: ArticleProps) {
131
- const { dataSource, bindingOptions, ...restProps } = props;
122
+ // Main component with data binding support and serialization capability
123
+ export class Article extends ModelView<ArticleProps, ArticleModel> {
124
+ // Component self-declaration for serialization
125
+ static readonly tagName = 'Article';
126
+ static readonly version = '1.0.0';
127
+
128
+ // Deserialization: JSON data → React element
129
+ static fromJson(jsonData: any): ReactElement {
130
+ return <Article {...jsonData} />;
131
+ }
132
+
133
+ // Component-specific serialization properties
134
+ protected getComponentSpecificProps(): any {
135
+ return {
136
+ html: this.props.html,
137
+ skipHeader: this.props.skipHeader
138
+ };
139
+ }
132
140
 
133
- // If no dataSource, use traditional props
134
- if (!dataSource) {
141
+ // Article component renders traditional props view
142
+ protected renderView(): React.ReactElement {
143
+ const { dataSource, bindingOptions, ...restProps } = this.props;
135
144
  return <ArticleView {...restProps} />;
136
145
  }
137
146
 
147
+ // Article component renders data-bound view
148
+ protected renderWithDataBinding(): React.ReactElement {
149
+ return <ArticleWithDataBinding {...this.props} />;
150
+ }
151
+
152
+ // Register HTML patterns that Article component can handle
153
+ static registerPatternHandlers(registry: any): void {
154
+ // Register article elements
155
+ if (!registry.hasPattern('article')) {
156
+ registry.registerPattern('article', Article.transformArticle);
157
+ }
158
+ }
159
+
160
+ // Transform article elements to Article component
161
+ private static transformArticle(element: Element): any {
162
+ const skipHeader = element.getAttribute('data-skip-header') === 'true';
163
+
164
+ return {
165
+ tagName: 'Article',
166
+ props: {
167
+ html: element.innerHTML,
168
+ skipHeader
169
+ }
170
+ };
171
+ }
172
+ }
173
+
174
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
175
+ function ArticleWithDataBinding(props: ArticleProps) {
176
+ const { dataSource, bindingOptions, ...restProps } = props;
177
+
138
178
  // Use data binding
139
179
  const { dataSource: _source, loading, error, cached, ...articleProps } = useDataBinding<ArticleModel>(
140
- dataSource,
180
+ dataSource!,
141
181
  restProps as Partial<ArticleModel>,
142
182
  ArticleModel.getSchema(),
143
183
  { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
@@ -191,4 +231,3 @@ function Article(props: ArticleProps) {
191
231
  }
192
232
 
193
233
  export default Article;
194
- export { Article };