@qwickapps/react-framework 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/README.md +1681 -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
@@ -14,12 +14,22 @@
14
14
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
15
15
  */
16
16
 
17
- import React from 'react';
17
+ import React, { ReactElement } from 'react';
18
18
  import { Button as MuiButton, ButtonProps as MuiButtonProps, CircularProgress, Paper, Typography } from '@mui/material';
19
19
  import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
20
20
  import { QWICKAPP_COMPONENT, useBaseProps } from '../../hooks';
21
21
  import { useDataBinding } from '../../hooks';
22
22
  import ButtonModel from '../../schemas/ButtonSchema';
23
+ import { ModelView } from '../base/ModelView';
24
+
25
+ // Action serialization pattern for button clicks
26
+ export interface ButtonAction {
27
+ type: 'navigate' | 'submit' | 'custom' | 'external';
28
+ url?: string;
29
+ target?: '_blank' | '_self' | '_parent' | '_top';
30
+ form?: string;
31
+ customHandler?: string;
32
+ }
23
33
 
24
34
  type ButtonViewProps = ModelProps<ButtonModel> & {
25
35
  /** Icon to display (before text) */
@@ -30,6 +40,8 @@ type ButtonViewProps = ModelProps<ButtonModel> & {
30
40
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
31
41
  /** Button content when using children */
32
42
  children?: React.ReactNode;
43
+ /** Serializable action descriptor for buttons */
44
+ action?: ButtonAction;
33
45
  };
34
46
 
35
47
  export interface ButtonProps extends ButtonViewProps, WithDataBinding {}
@@ -45,6 +57,7 @@ const ButtonView = React.forwardRef<HTMLButtonElement, ButtonViewProps>((props,
45
57
  href,
46
58
  target = '_self',
47
59
  onClick,
60
+ action,
48
61
  disabled = false,
49
62
  loading = false,
50
63
  fullWidth = false,
@@ -90,7 +103,64 @@ const ButtonView = React.forwardRef<HTMLButtonElement, ButtonViewProps>((props,
90
103
  }
91
104
  };
92
105
 
93
- // Determine if this should be a link
106
+ // Handle action serialization pattern for onClick
107
+ const handleActionClick = React.useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
108
+ if (action) {
109
+ event.preventDefault();
110
+
111
+ switch (action.type) {
112
+ case 'navigate':
113
+ if (action.url) {
114
+ if (action.target === '_blank') {
115
+ window.open(action.url, '_blank', 'noopener,noreferrer');
116
+ } else {
117
+ // Try to use React Router navigation if available
118
+ try {
119
+ // eslint-disable-next-line react-hooks/rules-of-hooks
120
+ const navigate = require('react-router-dom').useNavigate();
121
+ navigate(action.url);
122
+ } catch {
123
+ // Fallback to regular navigation
124
+ window.location.href = action.url;
125
+ }
126
+ }
127
+ }
128
+ break;
129
+
130
+ case 'submit':
131
+ if (action.form) {
132
+ const form = document.getElementById(action.form);
133
+ if (form instanceof HTMLFormElement) {
134
+ form.requestSubmit();
135
+ }
136
+ }
137
+ break;
138
+
139
+ case 'external':
140
+ if (action.url) {
141
+ window.open(action.url, action.target || '_blank', 'noopener,noreferrer');
142
+ }
143
+ break;
144
+
145
+ case 'custom':
146
+ if (action.customHandler && typeof window !== 'undefined') {
147
+ // Look for custom handler function on window
148
+ const handler = (window as any)[action.customHandler];
149
+ if (typeof handler === 'function') {
150
+ handler(event);
151
+ }
152
+ }
153
+ break;
154
+ }
155
+ }
156
+
157
+ // Call original onClick if provided
158
+ if (onClick) {
159
+ onClick(event);
160
+ }
161
+ }, [action, onClick]);
162
+
163
+ // Determine if this should be a link (prioritize href over action)
94
164
  const isLink = Boolean(href && !disabled && !loading);
95
165
 
96
166
  // Base button props
@@ -141,10 +211,10 @@ const ButtonView = React.forwardRef<HTMLButtonElement, ButtonViewProps>((props,
141
211
  );
142
212
  }
143
213
 
144
- // Button-specific props
214
+ // Button-specific props with action handling
145
215
  const buttonProps = {
146
216
  ...baseProps,
147
- onClick: disabled || loading ? undefined : onClick,
217
+ onClick: disabled || loading ? undefined : (action ? handleActionClick : onClick),
148
218
  };
149
219
 
150
220
  return (
@@ -156,21 +226,122 @@ const ButtonView = React.forwardRef<HTMLButtonElement, ButtonViewProps>((props,
156
226
 
157
227
  ButtonView.displayName = 'ButtonView';
158
228
 
159
- /**
160
- * Button component with data binding support
161
- * Supports both traditional props and dataSource-driven rendering
162
- */
163
- function Button(props: ButtonProps) {
164
- const { dataSource, bindingOptions, ...restProps } = props;
229
+ // Main component with data binding support and serialization capability
230
+ export class Button extends ModelView<ButtonProps, ButtonModel> {
231
+ // Component self-declaration for serialization
232
+ static readonly tagName = 'Button';
233
+ static readonly version = '1.0.0';
234
+
235
+ // Deserialization: JSON data → React element
236
+ static fromJson(jsonData: any): ReactElement {
237
+ return <Button {...jsonData} />;
238
+ }
239
+
240
+ // Component-specific serialization properties
241
+ protected getComponentSpecificProps(): any {
242
+ return {
243
+ label: this.props.label,
244
+ variant: this.props.variant,
245
+ buttonSize: this.props.buttonSize,
246
+ href: this.props.href,
247
+ target: this.props.target,
248
+ disabled: this.props.disabled,
249
+ loading: this.props.loading,
250
+ fullWidth: this.props.fullWidth,
251
+ // Include action pattern for click handlers
252
+ action: this.props.action
253
+ };
254
+ }
165
255
 
166
- // If no dataSource, use traditional props
167
- if (!dataSource) {
256
+ // Button component renders traditional props view
257
+ protected renderView(): React.ReactElement {
258
+ const { dataSource, bindingOptions, ...restProps } = this.props;
168
259
  return <ButtonView {...restProps} />;
169
260
  }
170
261
 
262
+ // Button component renders data-bound view
263
+ protected renderWithDataBinding(): React.ReactElement {
264
+ return <ButtonWithDataBinding {...this.props} />;
265
+ }
266
+
267
+ // Register HTML patterns that Button component can handle
268
+ static registerPatternHandlers(registry: any): void {
269
+ // Register button elements
270
+ if (!registry.hasPattern('button')) {
271
+ registry.registerPattern('button', Button.transformButton);
272
+ }
273
+
274
+ // Register input type="button" elements
275
+ if (!registry.hasPattern('input[type="button"]')) {
276
+ registry.registerPattern('input[type="button"]', Button.transformInputButton);
277
+ }
278
+
279
+ // Register input type="submit" elements
280
+ if (!registry.hasPattern('input[type="submit"]')) {
281
+ registry.registerPattern('input[type="submit"]', Button.transformSubmitButton);
282
+ }
283
+ }
284
+
285
+ // Transform button elements to Button component
286
+ private static transformButton(element: Element): any {
287
+ const variant = element.getAttribute('data-variant') ||
288
+ (element.className.includes('btn-primary') ? 'primary' :
289
+ element.className.includes('btn-outlined') ? 'outlined' : 'secondary');
290
+ const disabled = element.hasAttribute('disabled');
291
+ const href = element.getAttribute('data-href');
292
+ const target = element.getAttribute('data-target');
293
+
294
+ return {
295
+ tagName: 'Button',
296
+ props: {
297
+ label: element.textContent || 'Button',
298
+ variant,
299
+ disabled,
300
+ href: href || undefined,
301
+ target: target || undefined
302
+ }
303
+ };
304
+ }
305
+
306
+ // Transform input type="button" elements to Button component
307
+ private static transformInputButton(element: Element): any {
308
+ const disabled = element.hasAttribute('disabled');
309
+ const value = element.getAttribute('value') || 'Button';
310
+
311
+ return {
312
+ tagName: 'Button',
313
+ props: {
314
+ label: value,
315
+ variant: 'secondary',
316
+ disabled
317
+ }
318
+ };
319
+ }
320
+
321
+ // Transform input type="submit" elements to Button component
322
+ private static transformSubmitButton(element: Element): any {
323
+ const disabled = element.hasAttribute('disabled');
324
+ const value = element.getAttribute('value') || 'Submit';
325
+
326
+ return {
327
+ tagName: 'Button',
328
+ props: {
329
+ label: value,
330
+ variant: 'primary',
331
+ disabled,
332
+ type: 'submit'
333
+ }
334
+ };
335
+ }
336
+ }
337
+
338
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
339
+ function ButtonWithDataBinding(props: ButtonProps) {
340
+ const { dataSource, bindingOptions, ...restProps } = props;
341
+
171
342
  // Use data binding
172
343
  const bindingResult = useDataBinding<ButtonModel>(
173
- dataSource,
344
+ dataSource!,
174
345
  restProps as Partial<ButtonModel>,
175
346
  ButtonModel.getSchema(),
176
347
  { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
@@ -183,7 +354,6 @@ function Button(props: ButtonProps) {
183
354
  const { dataSource: _source, $loading, $error, $dataSource, $cached, cached, ...buttonProps } = bindingResult;
184
355
  const error = bindingResult.$error;
185
356
 
186
-
187
357
  // Show loading state while fetching data
188
358
  if (bindingLoading) {
189
359
  return (
@@ -229,5 +399,4 @@ function Button(props: ButtonProps) {
229
399
  // Mark as QwickApp component
230
400
  (Button as any)[QWICKAPP_COMPONENT] = true;
231
401
 
232
- export { Button };
233
402
  export default Button;
@@ -1,17 +1,18 @@
1
1
  /**
2
- * FormBlock - Reusable form layout component
2
+ * FormBlock - Reusable form layout component with serialization support
3
3
  *
4
- * Provides consistent layout for forms with:
5
- * - Optional header section (logo, title, subtitle)
6
- * - Form content area
7
- * - Status message area (info, success, warning, error)
8
- * - Optional footer section
4
+ * Features:
5
+ * - Complete form layout with header, content, and footer
6
+ * - Status message handling (info, success, warning, error)
7
+ * - Cover image and logo integration
9
8
  * - Data binding support for dynamic content
10
- * - Grid integration with useBaseProps
9
+ * - Full serialization support via ModelView
10
+ * - Consistent Material-UI styling
11
11
  *
12
12
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
13
13
  */
14
14
 
15
+ import React, { ReactElement } from 'react';
15
16
  import {
16
17
  Alert,
17
18
  Box,
@@ -23,12 +24,12 @@ import {
23
24
  useTheme,
24
25
  } from '@mui/material';
25
26
  import type { ModelProps, WithDataBinding } from '@qwickapps/schema';
26
- import React from 'react';
27
27
  import { useQwickApp } from '../../contexts/QwickAppContext';
28
28
  import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../../hooks';
29
29
  import FormBlockModel from '../../schemas/FormBlockSchema';
30
30
  import CoverImageHeader from '../blocks/CoverImageHeader';
31
31
  import Logo from '../Logo';
32
+ import { ModelView } from '../base/ModelView';
32
33
 
33
34
  type FormBlockViewProps = ModelProps<FormBlockModel> & {
34
35
  /**
@@ -223,21 +224,73 @@ function FormBlockView({
223
224
  );
224
225
  }
225
226
 
226
- /**
227
- * FormBlock component with data binding support
228
- * Supports both traditional props and dataSource-driven rendering
229
- */
230
- function FormBlock(props: FormBlockProps) {
231
- const { dataSource, bindingOptions, ...restProps } = props;
227
+ // Main component with data binding support and serialization capability
228
+ export class FormBlock extends ModelView<FormBlockProps, FormBlockModel> {
229
+ // Component self-declaration for serialization
230
+ static readonly tagName = 'FormBlock';
231
+ static readonly version = '1.0.0';
232
+
233
+ // Deserialization: JSON data → React element
234
+ static fromJson(jsonData: any): ReactElement {
235
+ return <FormBlock {...jsonData} />;
236
+ }
237
+
238
+ // FormBlock components have nested ReactNode children (form content)
239
+ protected hasNestedComponents(children: React.ReactNode): boolean {
240
+ // FormBlock may contain other serializable components as children
241
+ return true;
242
+ }
243
+
244
+ // Custom children serialization for FormBlock
245
+ protected serializeChildren(children: React.ReactNode): any {
246
+ // For FormBlock, children represent the form content which could be other components
247
+ // We need to handle this properly by checking if children contain serializable components
248
+ if (React.isValidElement(children) && (children.type as any).prototype?.toJson) {
249
+ // If it's a serializable component, serialize it
250
+ return (children.type as any).fromJson ? {
251
+ component: (children.type as any).tagName,
252
+ props: children.props
253
+ } : children;
254
+ }
255
+
256
+ // For non-serializable children (regular JSX), extract text or return as-is
257
+ return super.serializeChildren(children);
258
+ }
259
+
260
+ // Component-specific serialization properties
261
+ protected getComponentSpecificProps(): any {
262
+ return {
263
+ title: this.props.title,
264
+ description: this.props.description,
265
+ status: this.props.status,
266
+ message: this.props.message,
267
+ maxWidth: this.props.maxWidth,
268
+ background: this.props.background,
269
+ backgroundImage: this.props.backgroundImage,
270
+ // header, coverImage, footer are ReactNode (handled by base serialization if needed)
271
+ // children is handled by serializeChildren override
272
+ };
273
+ }
232
274
 
233
- // If no dataSource, use traditional props
234
- if (!dataSource) {
275
+ // FormBlock component renders traditional props view
276
+ protected renderView(): React.ReactElement {
277
+ const { dataSource, bindingOptions, ...restProps } = this.props;
235
278
  return <FormBlockView {...restProps} />;
236
279
  }
237
280
 
281
+ // FormBlock component renders data-bound view
282
+ protected renderWithDataBinding(): React.ReactElement {
283
+ return <FormBlockWithDataBinding {...this.props} />;
284
+ }
285
+ }
286
+
287
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
288
+ function FormBlockWithDataBinding(props: FormBlockProps) {
289
+ const { dataSource, bindingOptions, ...restProps } = props;
290
+
238
291
  // Use data binding
239
292
  const { dataSource: _source, loading, error, cached, ...formBlockProps } = useDataBinding<FormBlockModel>(
240
- dataSource,
293
+ dataSource!,
241
294
  restProps as Partial<FormBlockModel>,
242
295
  FormBlockModel.getSchema(),
243
296
  { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
@@ -2,6 +2,7 @@
2
2
  * All component props and exports available in QwickApps React Framework.
3
3
  *
4
4
  * Includes:
5
+ * - Base classes and abstractions
5
6
  * - Buttons
6
7
  * - Form inputs
7
8
  * - Layout components
@@ -9,6 +10,7 @@
9
10
  * - Pages and scaffolding
10
11
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
11
12
  */
13
+ export * from './base';
12
14
  export * from './blocks';
13
15
  export * from './buttons';
14
16
  export * from './forms';
@@ -30,6 +32,9 @@ export type { LogoProps } from './Logo';
30
32
  export { default as QwickAppsLogo } from './QwickAppsLogo';
31
33
  export type { QwickAppsLogoProps } from './QwickAppsLogo';
32
34
 
35
+ export { default as QwickIcon } from './QwickIcon';
36
+ export type { QwickIconProps } from './QwickIcon';
37
+
33
38
  export { default as SafeSpan } from './SafeSpan';
34
39
  export type { SafeSpanProps } from './SafeSpan';
35
40
 
@@ -1,16 +1,17 @@
1
1
  /**
2
- * ChoiceInputField - Dynamic option inputs management component
2
+ * ChoiceInputField - Dynamic option inputs management component with serialization support
3
3
  *
4
- * Provides consistent interface for:
5
- * - Dynamic list of option inputs
6
- * - Adding new options
7
- * - Rich text editing for each option
8
- * - Data binding support
4
+ * Features:
5
+ * - Complete dynamic option input management
6
+ * - Rich text editing for each option (via HtmlInputField)
7
+ * - Data binding support for dynamic content
8
+ * - Full serialization support via ModelView
9
+ * - Consistent Material-UI styling
9
10
  *
10
11
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
11
12
  */
12
13
 
13
- import React from 'react';
14
+ import React, { ReactElement } from 'react';
14
15
  import {
15
16
  Alert,
16
17
  Box,
@@ -20,10 +21,10 @@ import {
20
21
  } from '@mui/material';
21
22
  import { Add as AddIcon } from '@mui/icons-material';
22
23
  import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
23
- import { QWICKAPP_COMPONENT, useBaseProps } from '../../hooks';
24
- import { useDataBinding } from '../../hooks';
24
+ import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../../hooks';
25
25
  import HtmlInputField from './HtmlInputField';
26
26
  import ChoiceInputFieldModel from '../../schemas/ChoiceInputFieldSchema';
27
+ import { ModelView } from '../base/ModelView';
27
28
 
28
29
  type ChoiceInputFieldViewProps = ModelProps<ChoiceInputFieldModel> & {
29
30
  /** Handler for option value changes */
@@ -120,21 +121,50 @@ function ChoiceInputFieldView({
120
121
  );
121
122
  }
122
123
 
123
- /**
124
- * ChoiceInputField component with data binding support
125
- * Supports both traditional props and dataSource-driven rendering
126
- */
127
- function ChoiceInputField(props: ChoiceInputFieldProps) {
128
- const { dataSource, bindingOptions, ...restProps } = props;
124
+ // Main component with data binding support and serialization capability
125
+ export class ChoiceInputField extends ModelView<ChoiceInputFieldProps, ChoiceInputFieldModel> {
126
+ // Component self-declaration for serialization
127
+ static readonly tagName = 'ChoiceInputField';
128
+ static readonly version = '1.0.0';
129
+
130
+ // Deserialization: JSON data → React element
131
+ static fromJson(jsonData: any): ReactElement {
132
+ return <ChoiceInputField {...jsonData} />;
133
+ }
134
+
135
+ // Component-specific serialization properties
136
+ protected getComponentSpecificProps(): any {
137
+ return {
138
+ label: this.props.label,
139
+ options: this.props.options,
140
+ disabled: this.props.disabled,
141
+ placeholder: this.props.placeholder,
142
+ optionLabelPrefix: this.props.optionLabelPrefix,
143
+ rows: this.props.rows,
144
+ addButtonText: this.props.addButtonText,
145
+ // Event handlers don't serialize
146
+ };
147
+ }
129
148
 
130
- // If no dataSource, use traditional props
131
- if (!dataSource) {
149
+ // ChoiceInputField component renders traditional props view
150
+ protected renderView(): React.ReactElement {
151
+ const { dataSource, bindingOptions, ...restProps } = this.props;
132
152
  return <ChoiceInputFieldView {...restProps} />;
133
153
  }
134
154
 
155
+ // ChoiceInputField component renders data-bound view
156
+ protected renderWithDataBinding(): React.ReactElement {
157
+ return <ChoiceInputFieldWithDataBinding {...this.props} />;
158
+ }
159
+ }
160
+
161
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
162
+ function ChoiceInputFieldWithDataBinding(props: ChoiceInputFieldProps) {
163
+ const { dataSource, bindingOptions, ...restProps } = props;
164
+
135
165
  // Use data binding
136
166
  const { dataSource: _source, loading, error, cached, ...choiceInputFieldProps } = useDataBinding<ChoiceInputFieldModel>(
137
- dataSource,
167
+ dataSource!,
138
168
  restProps as Partial<ChoiceInputFieldModel>,
139
169
  ChoiceInputFieldModel.getSchema(),
140
170
  { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
@@ -1,17 +1,18 @@
1
1
  /**
2
- * HtmlInputField - Custom HTML text field with formatting controls
2
+ * HtmlInputField - Custom HTML text field with serialization support
3
3
  *
4
- * Provides HTML editing capabilities with:
5
- * - Formatting toolbar (bold, italic, underline, code)
6
- * - HTML preview mode
7
- * - Help system with supported tags
4
+ * Features:
5
+ * - Complete HTML editing with formatting toolbar
6
+ * - HTML preview mode and help system
8
7
  * - HTML sanitization for security
9
- * - Data binding support
8
+ * - Data binding support for dynamic content
9
+ * - Full serialization support via ModelView
10
+ * - Consistent Material-UI styling
10
11
  *
11
12
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
12
13
  */
13
14
 
14
- import { useState } from 'react';
15
+ import React, { ReactElement, useState } from 'react';
15
16
  import {
16
17
  Box,
17
18
  TextField,
@@ -36,9 +37,9 @@ import {
36
37
  import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
37
38
  import SafeSpan from '../SafeSpan';
38
39
  import sanitizeHtml from 'sanitize-html';
39
- import { QWICKAPP_COMPONENT, useBaseProps } from '../../hooks';
40
- import { useDataBinding } from '../../hooks';
40
+ import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../../hooks';
41
41
  import HtmlInputFieldModel from '../../schemas/HtmlInputFieldSchema';
42
+ import { ModelView } from '../base/ModelView';
42
43
 
43
44
  type HtmlInputFieldViewProps = ModelProps<HtmlInputFieldModel> & {
44
45
  /** Change handler */
@@ -258,21 +259,50 @@ function HtmlInputFieldView({
258
259
  );
259
260
  }
260
261
 
261
- /**
262
- * HtmlInputField component with data binding support
263
- * Supports both traditional props and dataSource-driven rendering
264
- */
265
- function HtmlInputField(props: HtmlInputFieldProps) {
266
- const { dataSource, bindingOptions, ...restProps } = props;
262
+ // Main component with data binding support and serialization capability
263
+ export class HtmlInputField extends ModelView<HtmlInputFieldProps, HtmlInputFieldModel> {
264
+ // Component self-declaration for serialization
265
+ static readonly tagName = 'HtmlInputField';
266
+ static readonly version = '1.0.0';
267
+
268
+ // Deserialization: JSON data → React element
269
+ static fromJson(jsonData: any): ReactElement {
270
+ return <HtmlInputField {...jsonData} />;
271
+ }
272
+
273
+ // Component-specific serialization properties
274
+ protected getComponentSpecificProps(): any {
275
+ return {
276
+ label: this.props.label,
277
+ value: this.props.value,
278
+ required: this.props.required,
279
+ placeholder: this.props.placeholder,
280
+ multiline: this.props.multiline,
281
+ rows: this.props.rows,
282
+ disabled: this.props.disabled,
283
+ // Event handlers don't serialize
284
+ };
285
+ }
267
286
 
268
- // If no dataSource, use traditional props
269
- if (!dataSource) {
287
+ // HtmlInputField component renders traditional props view
288
+ protected renderView(): React.ReactElement {
289
+ const { dataSource, bindingOptions, ...restProps } = this.props;
270
290
  return <HtmlInputFieldView {...restProps} />;
271
291
  }
272
292
 
293
+ // HtmlInputField component renders data-bound view
294
+ protected renderWithDataBinding(): React.ReactElement {
295
+ return <HtmlInputFieldWithDataBinding {...this.props} />;
296
+ }
297
+ }
298
+
299
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
300
+ function HtmlInputFieldWithDataBinding(props: HtmlInputFieldProps) {
301
+ const { dataSource, bindingOptions, ...restProps } = props;
302
+
273
303
  // Use data binding
274
304
  const { dataSource: _source, loading, error, cached, ...htmlInputFieldProps } = useDataBinding<HtmlInputFieldModel>(
275
- dataSource,
305
+ dataSource!,
276
306
  restProps as Partial<HtmlInputFieldModel>,
277
307
  HtmlInputFieldModel.getSchema(),
278
308
  { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }