@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
@@ -22,28 +22,27 @@
22
22
 
23
23
  import { Box } from '@mui/material';
24
24
  import { marked } from 'marked';
25
- import React from 'react';
26
- import { QWICKAPP_COMPONENT, useBaseProps, WithBaseProps } from '../hooks';
25
+ import React, { ReactElement } from 'react';
26
+ import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
27
+ import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../hooks';
28
+ import MarkdownModel from '../schemas/MarkdownSchema';
29
+ import { ModelView } from './base/ModelView';
27
30
  import Html from './Html';
28
31
  import SafeSpan from './SafeSpan';
29
32
  import { TransformConfig, defaultMarkdownRules } from '../utils/htmlTransform';
30
33
 
31
- export interface MarkdownProps extends WithBaseProps {
32
- /** Markdown content as string */
33
- children: string;
34
+ type MarkdownViewProps = ModelProps<MarkdownModel> & {
34
35
  /** Custom transformation configuration for HTML conversion */
35
36
  htmlTransformConfig?: TransformConfig;
36
- /** Whether to sanitize HTML output (default: true) */
37
- sanitize?: boolean;
38
37
  /** Custom sanitization options */
39
38
  sanitizeOptions?: any;
40
- /** Fallback content when Markdown is empty */
41
- placeholder?: string;
42
39
  /** Container element type */
43
40
  component?: React.ElementType;
44
41
  /** Marked options for Markdown parsing */
45
42
  markedOptions?: marked.MarkedOptions;
46
- }
43
+ };
44
+
45
+ export interface MarkdownProps extends MarkdownViewProps, WithDataBinding {}
47
46
 
48
47
  /**
49
48
  * Configure marked with security and GitHub Flavored Markdown support
@@ -65,10 +64,8 @@ const getMarkedOptions = (customOptions?: marked.MarkedOptions): marked.MarkedOp
65
64
  ...customOptions
66
65
  });
67
66
 
68
- /**
69
- * Markdown component - converts Markdown to React components via Html
70
- */
71
- export function Markdown({
67
+ // View component - handles the actual rendering
68
+ function MarkdownView({
72
69
  children = '',
73
70
  htmlTransformConfig,
74
71
  sanitize = true,
@@ -77,11 +74,11 @@ export function Markdown({
77
74
  component = 'div',
78
75
  markedOptions,
79
76
  ...restProps
80
- }: MarkdownProps) {
77
+ }: MarkdownViewProps) {
81
78
  const { styleProps, htmlProps, restProps: otherProps } = useBaseProps(restProps);
82
79
 
83
80
  // Mark as QwickApp component
84
- (Markdown as any)[QWICKAPP_COMPONENT] = true;
81
+ (MarkdownView as any)[QWICKAPP_COMPONENT] = true;
85
82
 
86
83
  // Return placeholder if no Markdown content
87
84
  if (!children || !children.trim()) {
@@ -188,4 +185,116 @@ export function Markdown({
188
185
  }
189
186
  }
190
187
 
188
+ // Main component with data binding support and serialization capability
189
+ export class Markdown extends ModelView<MarkdownProps, MarkdownModel> {
190
+ // Component self-declaration for serialization
191
+ static readonly tagName = 'Markdown';
192
+ static readonly version = '1.0.0';
193
+
194
+ // Deserialization: JSON data → React element
195
+ static fromJson(jsonData: any): ReactElement {
196
+ return <Markdown {...jsonData} />;
197
+ }
198
+
199
+ // Component-specific serialization properties
200
+ protected getComponentSpecificProps(): any {
201
+ return {
202
+ children: this.props.children,
203
+ sanitize: this.props.sanitize,
204
+ placeholder: this.props.placeholder
205
+ };
206
+ }
207
+
208
+ // Markdown component renders traditional props view
209
+ protected renderView(): React.ReactElement {
210
+ const { dataSource, bindingOptions, ...restProps } = this.props;
211
+ return <MarkdownView {...restProps} />;
212
+ }
213
+
214
+ // Markdown component renders data-bound view
215
+ protected renderWithDataBinding(): React.ReactElement {
216
+ return <MarkdownWithDataBinding {...this.props} />;
217
+ }
218
+
219
+ // Register HTML patterns that Markdown component can handle
220
+ static registerPatternHandlers(registry: any): void {
221
+ // Register div elements with specific classes for Markdown transformation
222
+ if (!registry.hasPattern('div.markdown-content')) {
223
+ registry.registerPattern('div.markdown-content', Markdown.transformMarkdownDiv);
224
+ }
225
+
226
+ // Register elements with data-markdown attribute
227
+ if (!registry.hasPattern('[data-markdown]')) {
228
+ registry.registerPattern('[data-markdown]', Markdown.transformDataMarkdown);
229
+ }
230
+ }
231
+
232
+ // Transform div with markdown-content class to Markdown component
233
+ private static transformMarkdownDiv(element: Element): any {
234
+ const sanitize = element.getAttribute('data-sanitize') !== 'false';
235
+ const placeholder = element.getAttribute('data-placeholder');
236
+
237
+ return {
238
+ tagName: 'Markdown',
239
+ props: {
240
+ children: element.textContent || '',
241
+ sanitize,
242
+ placeholder: placeholder || undefined
243
+ }
244
+ };
245
+ }
246
+
247
+ // Transform elements with data-markdown attribute to Markdown component
248
+ private static transformDataMarkdown(element: Element): any {
249
+ const markdownContent = element.getAttribute('data-markdown') || element.textContent;
250
+ const sanitize = element.getAttribute('data-sanitize') !== 'false';
251
+ const placeholder = element.getAttribute('data-placeholder');
252
+
253
+ return {
254
+ tagName: 'Markdown',
255
+ props: {
256
+ children: markdownContent,
257
+ sanitize,
258
+ placeholder: placeholder || undefined
259
+ }
260
+ };
261
+ }
262
+ }
263
+
264
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
265
+ function MarkdownWithDataBinding(props: MarkdownProps) {
266
+ const { dataSource, bindingOptions, ...restProps } = props;
267
+
268
+ // Use data binding
269
+ const { dataSource: _source, loading, error, cached, ...markdownProps } = useDataBinding<MarkdownModel>(
270
+ dataSource!,
271
+ restProps as Partial<MarkdownModel>,
272
+ MarkdownModel.getSchema(),
273
+ { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
274
+ );
275
+
276
+ // Show loading state
277
+ if (loading) {
278
+ return (
279
+ <Box sx={{ p: 2, textAlign: 'center', opacity: 0.6 }}>
280
+ Loading Markdown content...
281
+ </Box>
282
+ );
283
+ }
284
+
285
+ if (error) {
286
+ console.error('Error loading Markdown content:', error);
287
+ if (process.env.NODE_ENV !== 'production') {
288
+ return (
289
+ <Box sx={{ p: 2, border: '1px solid orange', borderRadius: 1, backgroundColor: 'rgba(255, 165, 0, 0.1)' }}>
290
+ <strong>Error Loading Markdown:</strong> {error.message}
291
+ </Box>
292
+ );
293
+ }
294
+ return null;
295
+ }
296
+
297
+ return <MarkdownView {...markdownProps} />;
298
+ }
299
+
191
300
  export default Markdown;
@@ -4,12 +4,17 @@
4
4
  * This component eliminates the need to manually set up provider hierarchy.
5
5
  * Provides theme system, app context, optional scaffolding, and routing.
6
6
  *
7
- * Example usage:
7
+ * Example usage with config:
8
8
  * ```tsx
9
9
  * import { BrowserRouter } from 'react-router-dom';
10
- * import { QwickApp, AuthProvider, JsonDataProvider } from '@qwickapps/react-framework';
10
+ * import { QwickApp, AuthProvider, JsonDataProvider, AppConfigBuilder } from '@qwickapps/react-framework';
11
11
  *
12
12
  * function App() {
13
+ * const config = AppConfigBuilder.create()
14
+ * .withName("My App")
15
+ * .withId("my.app")
16
+ * .build();
17
+ *
13
18
  * const dataSource = {
14
19
  * dataProvider: new JsonDataProvider({ data: { company: [...] } }),
15
20
  * cacheProvider: true, // Use default MemoryCacheProvider
@@ -17,7 +22,7 @@
17
22
  * };
18
23
  *
19
24
  * return (
20
- * <QwickApp appName="My App" appId="my.app" dataSource={dataSource}>
25
+ * <QwickApp config={config} dataSource={dataSource}>
21
26
  * <AuthProvider router={<BrowserRouter />} user={user}>
22
27
  * <Route path="/" component={HomePage} />
23
28
  * <Route path="/admin" component={AdminPage} requiresRole="admin" />
@@ -30,9 +35,10 @@
30
35
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
31
36
  */
32
37
  import React, { cloneElement, useState } from 'react';
33
- import { DataProvider, ThemeProvider, type ThemeMode } from '../contexts';
38
+ import { DataProvider, ThemeProvider, PrintModeProvider, type ThemeMode } from '../contexts';
34
39
  import { QwickAppContext, type QwickAppContextValue, type QwickAppProps } from '../contexts/QwickAppContext';
35
40
  import { type TemplateResolverConfig } from '../types';
41
+ import { AppConfig } from '../config';
36
42
  import './QwickApp.css';
37
43
  import Scaffold from './Scaffold';
38
44
  import { ErrorBoundary } from './ErrorBoundary';
@@ -56,6 +62,13 @@ interface QwickAppComponentProps extends QwickAppProps {
56
62
  router?: React.ReactElement;
57
63
  /** Data source configuration for content management and template resolution */
58
64
  dataSource?: TemplateResolverConfig;
65
+ /**
66
+ * AppConfig instance - when provided, overrides individual props
67
+ * @example
68
+ * const config = AppConfigBuilder.create().withName("My App").build();
69
+ * <QwickApp config={config}>...</QwickApp>
70
+ */
71
+ config?: AppConfig;
59
72
  // Auth-related props moved to AuthProvider for better separation of concerns
60
73
  }
61
74
 
@@ -63,41 +76,59 @@ export const QwickApp: React.FC<QwickAppComponentProps> = ({
63
76
  children,
64
77
  className,
65
78
  style,
66
- defaultTheme,
67
- defaultPalette,
68
- appName,
69
- logo,
70
- appId = true,
71
- enableScaffolding = false,
72
- navigationItems = [],
79
+ defaultTheme: defaultThemeProp,
80
+ defaultPalette: defaultPaletteProp,
81
+ appName: appNameProp,
82
+ logo: logoProp,
83
+ appId: appIdProp,
84
+ enableScaffolding: enableScaffoldingProp,
85
+ navigationItems: navigationItemsProp = [],
73
86
  appBar,
74
- showAppBar = true,
75
- appBarHeight = 64,
76
- showThemeSwitcher = false,
77
- showPaletteSwitcher = false,
87
+ showAppBar: showAppBarProp = true,
88
+ appBarHeight: appBarHeightProp = 64,
89
+ showThemeSwitcher: showThemeSwitcherProp,
90
+ showPaletteSwitcher: showPaletteSwitcherProp,
78
91
  onLogoClick,
79
92
  router,
80
93
  dataSource,
94
+ config,
81
95
  }) => {
96
+ // Resolve configuration: explicit props override config defaults
97
+ const resolvedConfig = {
98
+ appName: appNameProp ?? config?.app.name,
99
+ appId: appIdProp ?? config?.app.id ?? true,
100
+ logo: logoProp ?? config?.app.logo,
101
+ enableScaffolding: enableScaffoldingProp ?? config?.ui.enableScaffolding ?? false,
102
+ showThemeSwitcher: showThemeSwitcherProp ?? config?.ui.showThemeSwitcher ?? false,
103
+ showPaletteSwitcher: showPaletteSwitcherProp ?? config?.ui.showPaletteSwitcher ?? false,
104
+ defaultTheme: defaultThemeProp ?? config?.ui.defaultTheme,
105
+ defaultPalette: defaultPaletteProp ?? config?.ui.defaultPalette,
106
+ };
107
+
108
+ // Ensure we have required configuration
109
+ if (!resolvedConfig.appName) {
110
+ throw new Error('QwickApp requires either appName prop or config with app.name');
111
+ }
112
+
82
113
  // State for app configuration that can be updated via useQwickApp
83
114
  const [appConfig, setAppConfig] = useState({
84
- logo,
85
- enableScaffolding,
86
- navigationItems,
115
+ logo: resolvedConfig.logo,
116
+ enableScaffolding: resolvedConfig.enableScaffolding,
117
+ navigationItems: navigationItemsProp,
87
118
  appBar,
88
- showAppBar,
89
- appBarHeight,
90
- showThemeSwitcher,
91
- showPaletteSwitcher,
119
+ showAppBar: showAppBarProp,
120
+ appBarHeight: appBarHeightProp,
121
+ showThemeSwitcher: resolvedConfig.showThemeSwitcher,
122
+ showPaletteSwitcher: resolvedConfig.showPaletteSwitcher,
92
123
  });
93
124
 
94
- const updateConfig = (updates: Partial<typeof appConfig>) => {
95
- setAppConfig(prev => ({ ...prev, ...updates }));
125
+ const updateConfig = (updates: Partial<Pick<QwickAppProps, 'logo' | 'enableScaffolding' | 'navigationItems' | 'appBar' | 'showAppBar' | 'appBarHeight' | 'showThemeSwitcher' | 'showPaletteSwitcher'>>) => {
126
+ setAppConfig(prev => ({ ...prev, ...updates } as typeof prev));
96
127
  };
97
128
 
98
129
  const contextValue: QwickAppContextValue = {
99
- appName,
100
- appId,
130
+ appName: resolvedConfig.appName!, // Safe to use ! since we validated above
131
+ appId: resolvedConfig.appId,
101
132
  ...appConfig,
102
133
  onLogoClick,
103
134
  updateConfig,
@@ -106,7 +137,7 @@ export const QwickApp: React.FC<QwickAppComponentProps> = ({
106
137
 
107
138
  const content = appConfig.enableScaffolding ? (
108
139
  <Scaffold
109
- appName={appName}
140
+ appName={resolvedConfig.appName!} // Safe to use ! since we validated above
110
141
  navigationItems={appConfig.navigationItems}
111
142
  appBar={appConfig.appBar}
112
143
  showAppBar={appConfig.showAppBar}
@@ -131,12 +162,14 @@ export const QwickApp: React.FC<QwickAppComponentProps> = ({
131
162
  <AccessibilityProvider>
132
163
  <div className={`qwick-app ${className || ''}`} style={style}>
133
164
  <ThemeProvider
134
- appId={appId}
135
- defaultTheme={defaultTheme}
136
- defaultPalette={defaultPalette}
165
+ appId={resolvedConfig.appId}
166
+ defaultTheme={resolvedConfig.defaultTheme}
167
+ defaultPalette={resolvedConfig.defaultPalette}
137
168
  >
138
169
  <QwickAppContext.Provider value={contextValue}>
139
- {wrappedContent}
170
+ <PrintModeProvider>
171
+ {wrappedContent}
172
+ </PrintModeProvider>
140
173
  </QwickAppContext.Provider>
141
174
  </ThemeProvider>
142
175
  </div>
@@ -0,0 +1,59 @@
1
+ /**
2
+ * QwickIcon - Official QwickApps brand icon
3
+ *
4
+ * A reusable SVG icon component for the QwickApps brand that can be used
5
+ * across all QwickApps applications. Features customizable size and
6
+ * optional background for different use cases.
7
+ *
8
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
9
+ */
10
+
11
+ import React from 'react';
12
+
13
+ export interface QwickIconProps {
14
+ /** Size of the icon in pixels */
15
+ size?: number;
16
+ /** Whether to show the background rectangle */
17
+ showBackground?: boolean;
18
+ /** Additional CSS class names */
19
+ className?: string;
20
+ /** Additional inline styles */
21
+ style?: React.CSSProperties;
22
+ }
23
+
24
+ const QwickIcon: React.FC<QwickIconProps> = ({
25
+ size = 48,
26
+ showBackground = true,
27
+ className = '',
28
+ style = {}
29
+ }) => (
30
+ <svg
31
+ width={size}
32
+ height={size}
33
+ viewBox="0 0 225 225"
34
+ className={`qwick-icon ${className}`.trim()}
35
+ style={style}
36
+ role="img"
37
+ aria-label="QwickApps Icon"
38
+ >
39
+ {showBackground && <rect width="225" height="225" rx="48" fill="var(--theme-surface)" />}
40
+ <g transform="scale(0.9) translate(-5,-5)">
41
+ <g transform="translate(-128,-60)">
42
+ <path
43
+ className="logo-first-part"
44
+ fill="#FE6D0F"
45
+ d="M0 0 C1.3480925 -0.00788544 1.3480925 -0.00788544 2.72341919 -0.01593018 C5.67419172 -0.02905871 8.62441792 -0.02041442 11.57519531 -0.01025391 C13.64251813 -0.0122774 15.70984025 -0.01519113 17.77716064 -0.01895142 C22.10443943 -0.02336572 26.43153199 -0.01697508 30.75878906 -0.00292969 C36.2773468 0.01411856 41.79546534 0.00434284 47.31400681 -0.01364708 C51.5831018 -0.02454577 55.85209467 -0.02101006 60.12119293 -0.0132637 C62.15435082 -0.01127624 64.18751828 -0.01366927 66.22066498 -0.02070236 C76.73689794 -0.04908929 86.57929633 -0.04924466 96.65332031 3.38818359 C97.54098083 3.67795074 98.42864136 3.9677179 99.34320068 4.26626587 C114.15399949 9.31896076 126.4675764 21.76744512 133.65332031 35.38818359 C137.44514211 43.9738112 139.81630642 51.43992278 139.7668457 60.84057617 C139.76699173 61.5728241 139.76713776 62.30507202 139.76728821 63.05950928 C139.76659444 65.46727515 139.75882723 67.87496257 139.75097656 70.28271484 C139.74911048 71.95708677 139.74768747 73.63145924 139.74668884 75.30583191 C139.74287867 79.7034558 139.73306121 84.10104644 139.72198486 88.49865723 C139.71173847 92.9898229 139.70718431 97.48099397 139.70214844 101.97216797 C139.69143757 110.77752174 139.6743792 119.58284852 139.65332031 128.38818359 C138.53702723 127.43704375 137.42154262 126.48495501 136.30639648 125.5324707 C135.68512375 125.002379 135.06385101 124.47228729 134.42375183 123.9261322 C132.20679827 122.00029626 130.09344729 119.97434276 127.96582031 117.95068359 C121.3544337 111.96606452 113.93765643 107.63396094 105.65332031 104.38818359 C105.64397461 103.07736816 105.63462891 101.76655273 105.625 100.41601562 C105.58487807 95.51415049 105.51905854 90.61289839 105.44580078 85.71142578 C105.41771934 83.59724858 105.39644155 81.4829689 105.38232422 79.36865234 C105.36071633 76.31473808 105.31397548 73.26203954 105.26269531 70.20849609 C105.26085236 68.80943527 105.26085236 68.80943527 105.25897217 67.3821106 C105.05613797 58.12249123 102.35361677 49.9326592 95.65332031 43.38818359 C93.37687152 41.83978825 91.12212389 40.64502905 88.65332031 39.38818359 C88.0702562 39.02808197 87.48719208 38.66798035 86.88645935 38.29696655 C83.71010797 37.00434036 80.90937194 37.11787995 77.4855957 37.09765625 C76.41412041 37.08758041 76.41412041 37.08758041 75.32099915 37.07730103 C72.96425383 37.05833326 70.60779341 37.05439768 68.25097656 37.05224609 C66.60957218 37.04581795 64.96816895 37.03908958 63.32676697 37.03207397 C59.88782087 37.02010475 56.44897522 37.01639285 53.01000977 37.01757812 C48.60705215 37.01778144 44.20473906 36.99046492 39.80193424 36.95603371 C36.41239981 36.93377976 33.02302096 36.92974009 29.63342094 36.93079758 C28.01032729 36.9284399 26.38722945 36.91960691 24.76420784 36.90413284 C22.49249412 36.88449971 20.22217644 36.89029871 17.95043945 36.90234375 C16.65900528 36.89949371 15.36757111 36.89664368 14.03700256 36.89370728 C10.0894619 37.47058336 8.4533178 38.60386481 5.65332031 41.38818359 C4.36488826 43.96504769 4.49328598 45.87037118 4.44799805 48.75585938 C4.42805283 49.87687042 4.4081076 50.99788147 4.38755798 52.15286255 C4.36248924 53.97477615 4.36248924 53.97477615 4.33691406 55.83349609 C4.31613297 57.07364471 4.29535187 58.31379333 4.27394104 59.59152222 C4.21954326 62.88930182 4.16931936 66.18711079 4.12109375 69.48498535 C4.07086797 72.84991002 4.01524033 76.21474503 3.95996094 79.57958984 C3.85188552 86.18238589 3.75285167 92.78523103 3.65332031 99.38818359 C-7.89667969 99.38818359 -19.44667969 99.38818359 -31.34667969 99.38818359 C-31.54722373 80.32312956 -31.54722373 80.32312956 -31.59082031 72.14990234 C-31.6206242 66.58894779 -31.65574998 61.02840778 -31.72387695 55.46777344 C-31.77850048 50.98085383 -31.80816368 46.4943792 -31.8210659 42.00715256 C-31.83025168 40.30161924 -31.84818881 38.59610823 -31.87532997 36.89076614 C-32.04228479 25.95844515 -31.41983501 17.49965078 -24.17089844 8.76708984 C-23.46578125 8.16767578 -22.76066406 7.56826172 -22.03417969 6.95068359 C-21.33679688 6.33322266 -20.63941406 5.71576172 -19.92089844 5.07958984 C-13.46479327 0.83756627 -7.52553047 -0.0549708 0 0 Z"
46
+ transform="translate(185.3466796875,71.61181640625)"
47
+ />
48
+ <path
49
+ className="logo-second-part"
50
+ fill="#03BBCA"
51
+ d="M0 0 C11.55 0 23.1 0 35 0 C35.04125 3.25875 35.0825 6.5175 35.125 9.875 C35.47125788 19.41754586 36.68077925 25.86440869 43.74900818 32.68862915 C50.89908532 38.23277376 58.35470598 37.77209826 66.97265625 37.5859375 C68.50677317 37.57472707 70.04091178 37.56619562 71.57505798 37.56021118 C75.58524366 37.53745715 79.59409585 37.47865487 83.60375977 37.4119873 C87.70635953 37.3502711 91.80917779 37.32313407 95.91210938 37.29296875 C103.94201372 37.2288942 111.97084033 37.12668322 120 37 C117.59324168 34.57863654 115.18359847 32.1601637 112.7734375 29.7421875 C111.75527954 28.71774536 111.75527954 28.71774536 110.71655273 27.67260742 C106.8829741 23.82970702 102.99745284 20.13165615 98.8515625 16.625 C95.05012274 13.28871531 91.43825451 9.70885787 88 6 C88 5.67 88 5.34 88 5 C110.75508023 4.37644173 110.75508023 4.37644173 121 8 C122.051875 8.35191406 123.10375 8.70382813 124.1875 9.06640625 C147.22737862 17.29881571 164.80240124 33.21278198 175.41943359 55.15429688 C179.68328916 64.37125249 183.15018908 73.71312925 183.09765625 83.95703125 C183.09443359 85.06884766 183.09121094 86.18066406 183.08789062 87.32617188 C183.07951172 88.47666016 183.07113281 89.62714844 183.0625 90.8125 C183.05798828 91.98232422 183.05347656 93.15214844 183.04882812 94.35742188 C183.0370693 97.23833358 183.02063426 100.11914044 183 103 C180.55229893 100.57810948 178.13689836 98.15012289 175.81640625 95.60546875 C164.49520711 83.46019619 148.63443632 75.12990963 131.85790539 74.44966316 C128.34721591 74.38623286 124.84006559 74.37663155 121.32896042 74.40841484 C118.45052331 74.43424814 115.57348925 74.42890466 112.69497681 74.41651917 C106.58815705 74.39596309 100.48178961 74.41534647 94.375 74.4375 C87.28101821 74.45873236 80.18749777 74.46298673 73.09352493 74.43699837 C70.28119532 74.43367673 67.46978403 74.45471435 64.65756226 74.47740173 C46.51926055 74.50804771 31.73862671 70.07462761 18.28125 57.27734375 C17.5284375 56.52582031 16.775625 55.77429688 16 55 C15.02546875 54.04287109 15.02546875 54.04287109 14.03125 53.06640625 C3.06105089 41.49477078 -0.55499727 26.82435381 -0.1875 11.3125 C-0.125625 7.579375 -0.06375 3.84625 0 0 Z"
52
+ transform="translate(154,171)"
53
+ />
54
+ </g>
55
+ </g>
56
+ </svg>
57
+ );
58
+
59
+ export default QwickIcon;
@@ -10,10 +10,12 @@
10
10
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
11
11
  */
12
12
 
13
+ import React, { ReactElement } from 'react';
13
14
  import { WithDataBinding, ModelProps } from '@qwickapps/schema';
14
15
  import sanitizeHtml from 'sanitize-html';
15
- import { useBaseProps, useDataBinding, WithBaseProps } from '../hooks';
16
+ import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../hooks';
16
17
  import SafeSpanModel from '../schemas/SafeSpanSchema';
18
+ import { ModelView } from './base/ModelView';
17
19
 
18
20
  type SafeSpanViewProps = ModelProps<SafeSpanModel>;
19
21
  export interface SafeSpanProps extends SafeSpanViewProps, WithDataBinding {}
@@ -23,6 +25,9 @@ function SafeSpanView(props: SafeSpanViewProps) {
23
25
  const { html, placeholder, ...restProps } = props;
24
26
  const { styleProps, htmlProps, restProps: otherProps } = useBaseProps(restProps);
25
27
 
28
+ // Mark as QwickApp component
29
+ (SafeSpanView as any)[QWICKAPP_COMPONENT] = true;
30
+
26
31
  // Enhanced HTML sanitization with strict security configuration
27
32
  const sanitizeOptions = {
28
33
  allowedTags: [
@@ -89,21 +94,70 @@ function SafeSpanView(props: SafeSpanViewProps) {
89
94
  );
90
95
  }
91
96
 
92
- /**
93
- * SafeSpan component with data binding support
94
- * Supports both traditional props and dataSource-driven rendering
95
- */
96
- function SafeSpan(props: SafeSpanProps) {
97
- const { dataSource, bindingOptions, ...restProps } = props;
97
+ // Main component with data binding support and serialization capability
98
+ export class SafeSpan extends ModelView<SafeSpanProps, SafeSpanModel> {
99
+ // Component self-declaration for serialization
100
+ static readonly tagName = 'SafeSpan';
101
+ static readonly version = '1.0.0';
102
+
103
+ // Deserialization: JSON data → React element
104
+ static fromJson(jsonData: any): ReactElement {
105
+ return <SafeSpan {...jsonData} />;
106
+ }
107
+
108
+ // Component-specific serialization properties
109
+ protected getComponentSpecificProps(): any {
110
+ return {
111
+ html: this.props.html,
112
+ placeholder: this.props.placeholder
113
+ };
114
+ }
98
115
 
99
- // If no dataSource, use traditional props
100
- if (!dataSource) {
116
+ // SafeSpan component renders traditional props view
117
+ protected renderView(): React.ReactElement {
118
+ const { dataSource, bindingOptions, ...restProps } = this.props;
101
119
  return <SafeSpanView {...restProps} />;
102
120
  }
103
121
 
122
+ // SafeSpan component renders data-bound view
123
+ protected renderWithDataBinding(): React.ReactElement {
124
+ return <SafeSpanWithDataBinding {...this.props} />;
125
+ }
126
+
127
+ // Register HTML patterns that SafeSpan component can handle
128
+ static registerPatternHandlers(registry: any): void {
129
+ // Register span elements with specific classes or attributes
130
+ if (!registry.hasPattern('span.safe-content')) {
131
+ registry.registerPattern('span.safe-content', SafeSpan.transformSafeSpan);
132
+ }
133
+
134
+ // Register span elements with data-safe attribute
135
+ if (!registry.hasPattern('span[data-safe]')) {
136
+ registry.registerPattern('span[data-safe]', SafeSpan.transformSafeSpan);
137
+ }
138
+ }
139
+
140
+ // Transform span elements to SafeSpan component
141
+ private static transformSafeSpan(element: Element): any {
142
+ const placeholder = element.getAttribute('data-placeholder');
143
+
144
+ return {
145
+ tagName: 'SafeSpan',
146
+ props: {
147
+ html: element.innerHTML,
148
+ placeholder: placeholder || undefined
149
+ }
150
+ };
151
+ }
152
+ }
153
+
154
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
155
+ function SafeSpanWithDataBinding(props: SafeSpanProps) {
156
+ const { dataSource, bindingOptions, ...restProps } = props;
157
+
104
158
  // Use data binding
105
159
  const { dataSource: _source, loading, error, cached, ...safeSpanProps } = useDataBinding<SafeSpanModel>(
106
- dataSource,
160
+ dataSource!,
107
161
  restProps as Partial<SafeSpanModel>,
108
162
  SafeSpanModel.getSchema(),
109
163
  { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
@@ -122,6 +176,7 @@ function SafeSpan(props: SafeSpanProps) {
122
176
  return null;
123
177
  }
124
178
 
179
+ console.log('Resolved props for SafeSpan:', safeSpanProps);
125
180
  return <SafeSpanView {...safeSpanProps} />;
126
181
  }
127
182
 
@@ -89,8 +89,6 @@ const Scaffold: React.FC<ScaffoldProps> = ({
89
89
  const [screenSize, setScreenSize] = useState<ScreenSize>('desktop');
90
90
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
91
91
  const [isRailExpanded, setIsRailExpanded] = useState(false);
92
- // Use navigation items directly since Page no longer provides menu items
93
- const combinedMenuItems = navigationItems;
94
92
 
95
93
  // React Router hooks (if available)
96
94
  const location = useSafeLocation();
@@ -132,8 +130,8 @@ const Scaffold: React.FC<ScaffoldProps> = ({
132
130
  // Page-specific actions and menu items are handled by header components
133
131
 
134
132
  // Sort items by priority
135
- const sortedItems = [...combinedMenuItems].sort((a, b) => (a.priority || 999) - (b.priority || 999));
136
-
133
+ const sortedItems = [...navigationItems].sort((a, b) => (a.priority || 999) - (b.priority || 999));
134
+
137
135
  // Material UI Guidelines: Split items based on screen size
138
136
  const getNavigationSplit = () => {
139
137
  switch (screenSize) {
@@ -217,10 +215,6 @@ const Scaffold: React.FC<ScaffoldProps> = ({
217
215
  logger.error('Menu item onClick error:', error);
218
216
  }
219
217
  } else if (item.route) {
220
- if (navigate === undefined) {
221
- logger.error('No navigation function available! Cannot navigate to:', item.route);
222
- return;
223
- }
224
218
  // Robust navigation with comprehensive error handling
225
219
  try {
226
220
  logger.debug(`Navigating from ${currentPath} to ${item.route}`);