@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
@@ -23,37 +23,37 @@
23
23
  */
24
24
 
25
25
  import { Box } from '@mui/material';
26
- import React from 'react';
27
- import { QWICKAPP_COMPONENT, useBaseProps, WithBaseProps } from '../hooks';
26
+ import React, { ReactElement } from 'react';
27
+ import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
28
+ import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../hooks';
29
+ import { ComponentTransformer } from '../schemas/transformers/ComponentTransformer';
30
+ import HtmlModel from '../schemas/HtmlSchema';
31
+ import { ModelView } from './base/ModelView';
28
32
  import SafeSpan from './SafeSpan';
29
- import {
30
- TransformConfig,
31
- defaultArticleRules,
32
- transformHtmlToReact,
33
- stripHeaderFromContent
34
- } from '../utils/htmlTransform';
35
-
36
- export interface HtmlProps extends WithBaseProps {
37
- /** HTML content as string */
38
- children: string;
39
- /** Custom transformation configuration */
33
+
34
+ // Legacy types for backward compatibility (now unused internally but maintained for API)
35
+ export interface TransformConfig {
36
+ rules?: any[];
37
+ sanitize?: boolean;
38
+ sanitizeOptions?: any;
39
+ fallbackComponent?: (element: Element, key: string) => React.ReactNode;
40
+ }
41
+
42
+ type HtmlViewProps = ModelProps<HtmlModel> & {
43
+ /** Custom transformation configuration (legacy - now handled by ComponentTransformer) */
40
44
  transformConfig?: TransformConfig;
41
- /** Whether to strip header elements (useful for articles) */
42
- stripHeaders?: boolean;
43
- /** Whether to sanitize HTML (default: true) */
45
+ /** Whether to sanitize HTML (legacy - now handled internally) */
44
46
  sanitize?: boolean;
45
- /** Custom sanitization options */
47
+ /** Custom sanitization options (legacy - now handled internally) */
46
48
  sanitizeOptions?: any;
47
- /** Fallback content when HTML is empty */
48
- placeholder?: string;
49
- /** Container element type */
49
+ /** Container element type (React.ElementType for internal use, string in model for serialization) */
50
50
  component?: React.ElementType;
51
- }
51
+ };
52
52
 
53
- /**
54
- * Html component - transforms HTML strings to React components
55
- */
56
- export function Html({
53
+ export interface HtmlProps extends HtmlViewProps, WithDataBinding {}
54
+
55
+ // View component - handles the actual rendering
56
+ function HtmlView({
57
57
  children = '',
58
58
  transformConfig,
59
59
  stripHeaders = false,
@@ -62,11 +62,11 @@ export function Html({
62
62
  placeholder,
63
63
  component = 'div',
64
64
  ...restProps
65
- }: HtmlProps) {
65
+ }: HtmlViewProps) {
66
66
  const { styleProps, htmlProps, restProps: otherProps } = useBaseProps(restProps);
67
67
 
68
68
  // Mark as QwickApp component
69
- (Html as any)[QWICKAPP_COMPONENT] = true;
69
+ (HtmlView as any)[QWICKAPP_COMPONENT] = true;
70
70
 
71
71
  // Return placeholder if no HTML content
72
72
  if (!children || !children.trim()) {
@@ -95,25 +95,16 @@ export function Html({
95
95
  let processedHtml = children;
96
96
 
97
97
  if (stripHeaders) {
98
- processedHtml = stripHeaderFromContent(processedHtml);
98
+ // Simple header stripping - remove h1, h2, etc. tags
99
+ processedHtml = processedHtml.replace(/<h[1-6][^>]*>.*?<\/h[1-6]>/gi, '');
99
100
  }
100
101
 
101
- // Use provided transform config or default article rules
102
- const config: TransformConfig = transformConfig || {
103
- rules: defaultArticleRules,
104
- sanitize,
105
- sanitizeOptions,
106
- fallbackComponent: (element: Element, key: string) => (
107
- <SafeSpan
108
- key={key}
109
- html={element.outerHTML}
110
- placeholder="Invalid HTML content"
111
- />
112
- )
113
- };
114
-
115
- // Transform HTML to React components
116
- const components = transformHtmlToReact(processedHtml, config);
102
+ // Note: transformConfig, sanitize, and sanitizeOptions are legacy props
103
+ // maintained for backward compatibility. The new ComponentTransformer system
104
+ // handles transformation through registered patterns automatically.
105
+
106
+ // Transform HTML to React components using ComponentTransformer
107
+ const components = ComponentTransformer.transformHTML(processedHtml);
117
108
 
118
109
  // Return transformed components
119
110
  return (
@@ -188,4 +179,116 @@ export function Html({
188
179
  }
189
180
  }
190
181
 
182
+ // Main component with data binding support and serialization capability
183
+ export class Html extends ModelView<HtmlProps, HtmlModel> {
184
+ // Component self-declaration for serialization
185
+ static readonly tagName = 'Html';
186
+ static readonly version = '1.0.0';
187
+
188
+ // Deserialization: JSON data → React element
189
+ static fromJson(jsonData: any): ReactElement {
190
+ return <Html {...jsonData} />;
191
+ }
192
+
193
+ // Component-specific serialization properties
194
+ protected getComponentSpecificProps(): any {
195
+ return {
196
+ children: this.props.children,
197
+ stripHeaders: this.props.stripHeaders,
198
+ placeholder: this.props.placeholder
199
+ };
200
+ }
201
+
202
+ // Html component renders traditional props view
203
+ protected renderView(): React.ReactElement {
204
+ const { dataSource, bindingOptions, ...restProps } = this.props;
205
+ return <HtmlView {...restProps} />;
206
+ }
207
+
208
+ // Html component renders data-bound view
209
+ protected renderWithDataBinding(): React.ReactElement {
210
+ return <HtmlWithDataBinding {...this.props} />;
211
+ }
212
+
213
+ // Register HTML patterns that Html component can handle
214
+ static registerPatternHandlers(registry: any): void {
215
+ // Register div elements with specific classes for Html transformation
216
+ if (!registry.hasPattern('div.html-content')) {
217
+ registry.registerPattern('div.html-content', Html.transformHtmlDiv);
218
+ }
219
+
220
+ // Register elements with data-html attribute
221
+ if (!registry.hasPattern('[data-html]')) {
222
+ registry.registerPattern('[data-html]', Html.transformDataHtml);
223
+ }
224
+ }
225
+
226
+ // Transform div with html-content class to Html component
227
+ private static transformHtmlDiv(element: Element): any {
228
+ const stripHeaders = element.getAttribute('data-strip-headers') === 'true';
229
+ const placeholder = element.getAttribute('data-placeholder');
230
+
231
+ return {
232
+ tagName: 'Html',
233
+ props: {
234
+ children: element.innerHTML,
235
+ stripHeaders,
236
+ placeholder: placeholder || undefined
237
+ }
238
+ };
239
+ }
240
+
241
+ // Transform elements with data-html attribute to Html component
242
+ private static transformDataHtml(element: Element): any {
243
+ const htmlContent = element.getAttribute('data-html') || element.innerHTML;
244
+ const stripHeaders = element.getAttribute('data-strip-headers') === 'true';
245
+ const placeholder = element.getAttribute('data-placeholder');
246
+
247
+ return {
248
+ tagName: 'Html',
249
+ props: {
250
+ children: htmlContent,
251
+ stripHeaders,
252
+ placeholder: placeholder || undefined
253
+ }
254
+ };
255
+ }
256
+ }
257
+
258
+ // Helper component to handle data binding with hooks (since we can't use hooks in class components)
259
+ function HtmlWithDataBinding(props: HtmlProps) {
260
+ const { dataSource, bindingOptions, ...restProps } = props;
261
+
262
+ // Use data binding
263
+ const { dataSource: _source, loading, error, cached, ...htmlProps } = useDataBinding<HtmlModel>(
264
+ dataSource!,
265
+ restProps as Partial<HtmlModel>,
266
+ HtmlModel.getSchema(),
267
+ { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
268
+ );
269
+
270
+ // Show loading state
271
+ if (loading) {
272
+ return (
273
+ <Box sx={{ p: 2, textAlign: 'center', opacity: 0.6 }}>
274
+ Loading HTML content...
275
+ </Box>
276
+ );
277
+ }
278
+
279
+ if (error) {
280
+ console.error('Error loading HTML content:', error);
281
+ if (process.env.NODE_ENV !== 'production') {
282
+ return (
283
+ <Box sx={{ p: 2, border: '1px solid red', borderRadius: 1, backgroundColor: 'rgba(255, 0, 0, 0.1)' }}>
284
+ <strong>Error Loading HTML:</strong> {error.message}
285
+ </Box>
286
+ );
287
+ }
288
+ return null;
289
+ }
290
+
291
+ return <HtmlView {...htmlProps} />;
292
+ }
293
+
191
294
  export default Html;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dynamic Logo Component - Generic theme-aware logo
2
+ * Dynamic Logo Component - Generic theme-aware logo with image support
3
3
  *
4
4
  * Features:
5
5
  * - Automatic text width calculation with dynamic SVG sizing
@@ -10,6 +10,7 @@
10
10
  * - Accessibility support with proper ARIA labels
11
11
  * - Customizable styling via CSS classes for each text part
12
12
  * - Text formatting with escape sequences
13
+ * - Image support with flexible positioning
13
14
  *
14
15
  * Text Formatting (TWO PARTS MAXIMUM):
15
16
  * - Use \n for line breaks (second part appears on new line)
@@ -35,78 +36,49 @@
35
36
  */
36
37
 
37
38
  import React, { useRef, useEffect, useState, useCallback } from 'react';
38
- import { WithBaseProps, useBaseProps, QWICKAPP_COMPONENT } from '../hooks/useBaseProps';
39
+ import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
40
+ import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../hooks';
41
+ import LogoModel from '../schemas/LogoSchema';
42
+ import { LogoVariant, LogoSize, LogoBadgeShape, PositionType, BadgeOffset } from '../schemas/LogoSchema';
39
43
  import './Logo.css';
40
44
 
41
- export type LogoVariant = 'default' | 'high-contrast' | 'monochrome' | 'on-primary';
42
- export type LogoSize = 'tiny' | 'small' | 'medium' | 'large' | 'extra-large';
43
- export type LogoBadgeShape = 'circle' | 'star' | 'square' | 'heart';
44
- export type BadgePositionType =
45
- | 'none'
46
- | 'top-left'
47
- | 'top-center'
48
- | 'top-right'
49
- | 'center-left'
50
- | 'center'
51
- | 'center-right'
52
- | 'bottom-left'
53
- | 'bottom-center'
54
- | 'bottom-right';
55
-
56
- export interface BadgeOffset {
57
- /** Horizontal offset from the calculated position (positive = right, negative = left) */
58
- x?: number;
59
- /** Vertical offset from the calculated position (positive = down, negative = up) */
60
- y?: number;
61
- }
62
-
63
- export interface LogoProps extends WithBaseProps, Omit<React.SVGProps<SVGSVGElement>, 'width' | 'height' | 'className' | 'style' | 'role' | 'onClick' | 'onMouseEnter' | 'onMouseLeave' | 'onFocus' | 'onBlur'> {
64
- /** Logo name/text to display. Supports up to TWO parts with \n for line breaks and \s for explicit spaces. */
65
- name?: string;
45
+ type LogoViewProps = ModelProps<LogoModel> & {
66
46
  /** Click handler for the logo */
67
- onClick?: (event: React.MouseEvent<SVGSVGElement>) => void;
47
+ onClick?: (event: React.MouseEvent<SVGSVGElement | HTMLDivElement>) => void;
68
48
  /** Additional inline styles */
69
49
  style?: React.CSSProperties;
70
- /** Visual variant of the logo */
71
- variant?: LogoVariant;
72
- /** Size variant of the logo (controls both text size and visual height) */
73
- size?: LogoSize;
74
50
  /** Additional CSS class names */
75
51
  className?: string;
76
- /** Badge position and visibility. 'none' hides the badge, others show it at the specified position. */
77
- badge?: BadgePositionType;
78
- /** Shape of the badge */
79
- badgeShape?: LogoBadgeShape;
80
- /** Offset from the calculated badge position. Applied after position calculation. Automatically scales with logo size. */
81
- badgeOffset?: BadgeOffset;
82
- /** Font family for the logo text */
83
- fontFamily?: string;
84
- /** Font weight for the logo text */
85
- fontWeight?: string | number;
86
- /** CSS class name for the first part of the logo text. Defaults to 'logo-first-part'. */
87
- firstPartClass?: string;
88
- /** CSS class name for the second part of the logo text. Defaults to 'logo-second-part'. */
89
- secondPartClass?: string;
90
- }
52
+ };
53
+
54
+ interface LogoProps extends LogoViewProps, WithDataBinding {}
91
55
 
92
- const Logo: React.FC<LogoProps> = (props) => {
93
- const { styleProps, htmlProps, restProps } = useBaseProps(props);
94
- const {
95
- name = 'Qwick Apps',
96
- variant = 'default',
97
- size = 'medium',
98
- badge = 'top-right',
99
- badgeShape = 'circle',
100
- badgeOffset,
101
- fontFamily = 'Segoe UI, sans-serif',
102
- fontWeight = 'bold',
103
- firstPartClass = 'logo-first-part',
104
- secondPartClass = 'logo-second-part',
105
- ...svgProps
106
- } = restProps;
56
+ // View component - handles the actual rendering
57
+ function LogoView({
58
+ name = 'Qwick Apps',
59
+ variant = 'default',
60
+ size = 'medium',
61
+ badge = 'top-right',
62
+ badgeShape = 'circle',
63
+ badgeOffsetX,
64
+ badgeOffsetY,
65
+ fontFamily = 'Segoe UI, sans-serif',
66
+ fontWeight = 'bold',
67
+ firstPartClass = 'logo-first-part',
68
+ secondPartClass = 'logo-second-part',
69
+ image,
70
+ imagePosition = 'start',
71
+ onClick,
72
+ style,
73
+ className,
74
+ ...restProps
75
+ }: LogoViewProps) {
76
+ const { styleProps, htmlProps, restProps: otherProps } = useBaseProps(restProps);
107
77
 
108
- // Mark as QwickApp component
109
- (Logo as any)[QWICKAPP_COMPONENT] = true;
78
+ // Convert separate offset values to BadgeOffset object
79
+ const badgeOffset: BadgeOffset | undefined = (badgeOffsetX !== undefined || badgeOffsetY !== undefined)
80
+ ? { x: badgeOffsetX, y: badgeOffsetY }
81
+ : undefined;
110
82
 
111
83
  const textRef = useRef<SVGTextElement>(null);
112
84
  const [calculatedBadgePosition, setCalculatedBadgePosition] = useState({ x: 155, y: 20 });
@@ -209,7 +181,7 @@ const Logo: React.FC<LogoProps> = (props) => {
209
181
  x = textBBox.x + textBBox.width + scaledOffset;
210
182
  y = textBBox.y + scaledOffset;
211
183
  break;
212
- case 'center-left':
184
+ case 'start': // was 'center-left'
213
185
  x = textBBox.x - scaledOffset;
214
186
  y = textBBox.y + textBBox.height / 2;
215
187
  break;
@@ -217,7 +189,7 @@ const Logo: React.FC<LogoProps> = (props) => {
217
189
  x = textBBox.x + textBBox.width / 2;
218
190
  y = textBBox.y + textBBox.height / 2;
219
191
  break;
220
- case 'center-right':
192
+ case 'end': // was 'center-right'
221
193
  x = textBBox.x + textBBox.width + scaledOffset;
222
194
  y = textBBox.y + textBBox.height / 2;
223
195
  break;
@@ -330,41 +302,167 @@ const Logo: React.FC<LogoProps> = (props) => {
330
302
  );
331
303
  }
332
304
  };
305
+
306
+ // Render image if provided
307
+ const renderImage = (): React.ReactNode => {
308
+ if (!image || imagePosition === 'none') return null;
309
+
310
+ // If image is a string, assume it's an image path
311
+ if (typeof image === 'string') {
312
+ const imageSize = fontSize; // Scale image with font size
313
+ return (
314
+ <img
315
+ src={image}
316
+ alt=""
317
+ style={{
318
+ width: imageSize,
319
+ height: imageSize,
320
+ objectFit: 'contain'
321
+ }}
322
+ className="logo-image"
323
+ />
324
+ );
325
+ }
326
+
327
+ // If image is a React node, render it directly
328
+ return <div className="logo-image-container">{image}</div>;
329
+ };
330
+
331
+ // Determine layout based on image position
332
+ const renderWithImage = () => {
333
+ const logoSvg = (
334
+ <svg
335
+ width={svgWidth}
336
+ height={height}
337
+ viewBox={`0 0 ${svgWidth} ${height}`}
338
+ fill="none"
339
+ xmlns="http://www.w3.org/2000/svg"
340
+ className={`logo-svg dynamic-logo ${sizeClass} ${variantClass}`.trim()}
341
+ style={{ height: `${height}px` }}
342
+ role="img"
343
+ aria-label={ariaLabel}
344
+ >
345
+ <text ref={textRef} fontFamily={fontFamily} fontSize={fontSize} fontWeight={fontWeight}>
346
+ <tspan x="15" y={isNewLine ? height * 0.4 : height * 0.7} className={firstPartClass}>{firstPart}</tspan>
347
+ {secondPart && (
348
+ <tspan
349
+ x={isNewLine ? "15" : undefined}
350
+ dy={isNewLine ? fontSize * 1.2 : undefined}
351
+ className={secondPartClass}
352
+ >
353
+ {isNewLine ? secondPart : (hasExplicitSpaces ? ` ${secondPart}` : secondPart)}
354
+ </tspan>
355
+ )}
356
+ </text>
357
+ {renderBadgeShape()}
358
+ </svg>
359
+ );
360
+
361
+ const imageElement = renderImage();
362
+
363
+ if (!imageElement) {
364
+ return logoSvg;
365
+ }
366
+
367
+ // Create container with image positioned based on imagePosition
368
+ let containerStyle: React.CSSProperties = {
369
+ display: 'flex',
370
+ alignItems: 'center',
371
+ gap: '8px',
372
+ };
373
+
374
+ let containerClass = 'logo-container';
375
+
376
+ switch (imagePosition) {
377
+ case 'start':
378
+ containerStyle.flexDirection = 'row';
379
+ containerClass += ' logo-image-start';
380
+ break;
381
+ case 'end':
382
+ containerStyle.flexDirection = 'row-reverse';
383
+ containerClass += ' logo-image-end';
384
+ break;
385
+ case 'top-center':
386
+ containerStyle.flexDirection = 'column';
387
+ containerClass += ' logo-image-top';
388
+ break;
389
+ case 'bottom-center':
390
+ containerStyle.flexDirection = 'column-reverse';
391
+ containerClass += ' logo-image-bottom';
392
+ break;
393
+ default:
394
+ // For other positions, default to start
395
+ containerStyle.flexDirection = 'row';
396
+ containerClass += ' logo-image-start';
397
+ break;
398
+ }
399
+
400
+ return (
401
+ <div
402
+ {...htmlProps}
403
+ {...styleProps}
404
+ className={`${containerClass} ${className || ''}`.trim()}
405
+ style={{
406
+ ...containerStyle,
407
+ cursor: onClick ? 'pointer' : 'default',
408
+ ...style
409
+ }}
410
+ onClick={onClick}
411
+ >
412
+ {imageElement}
413
+ {logoSvg}
414
+ </div>
415
+ );
416
+ };
417
+
418
+ return renderWithImage();
419
+ }
420
+
421
+ // Main Logo component with data binding support
422
+ function Logo(props: LogoProps) {
423
+ const { dataSource, bindingOptions, ...restProps } = props;
333
424
 
334
- return (
335
- <svg
336
- width={svgWidth}
337
- height={height}
338
- viewBox={`0 0 ${svgWidth} ${height}`}
339
- fill="none"
340
- xmlns="http://www.w3.org/2000/svg"
341
- {...htmlProps}
342
- {...styleProps}
343
- className={`logo dynamic-logo ${sizeClass} ${variantClass} ${styleProps.className || ''}`.trim()}
344
- style={{
345
- height: `${height}px`,
346
- cursor: htmlProps.onClick ? 'pointer' : 'default',
347
- ...styleProps.style
348
- }}
349
- role="img"
350
- aria-label={ariaLabel}
351
- {...svgProps}
352
- >
353
- <text ref={textRef} fontFamily={fontFamily} fontSize={fontSize} fontWeight={fontWeight}>
354
- <tspan x="15" y={isNewLine ? height * 0.4 : height * 0.7} className={firstPartClass}>{firstPart}</tspan>
355
- {secondPart && (
356
- <tspan
357
- x={isNewLine ? "15" : undefined}
358
- dy={isNewLine ? fontSize * 1.2 : undefined}
359
- className={secondPartClass}
360
- >
361
- {isNewLine ? secondPart : (hasExplicitSpaces ? ` ${secondPart}` : secondPart)}
362
- </tspan>
363
- )}
364
- </text>
365
- {renderBadgeShape()}
366
- </svg>
425
+ // If no dataSource, use traditional props
426
+ if (!dataSource) {
427
+ return <LogoView {...restProps} />;
428
+ }
429
+
430
+ // Use data binding
431
+ const { dataSource: _source, loading, error, cached, ...logoProps } = useDataBinding<LogoModel>(
432
+ dataSource,
433
+ restProps as Partial<LogoModel>,
434
+ LogoModel.getSchema(),
435
+ { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
367
436
  );
368
- };
437
+
438
+ // Show loading state
439
+ if (loading) {
440
+ return (
441
+ <div style={{ opacity: 0.5, textAlign: 'center', padding: '16px' }}>
442
+ Loading logo...
443
+ </div>
444
+ );
445
+ }
446
+
447
+ // Show error state
448
+ if (error) {
449
+ console.error('Error loading logo:', error);
450
+ return (
451
+ <div style={{ color: 'red', textAlign: 'center', padding: '16px' }}>
452
+ Error loading logo: {error.message}
453
+ </div>
454
+ );
455
+ }
456
+
457
+ console.log('Resolved props for Logo:', logoProps);
458
+ return <LogoView {...logoProps} />;
459
+ }
460
+
461
+ // Mark as QwickApp component
462
+ (Logo as any)[QWICKAPP_COMPONENT] = true;
463
+
464
+ // Export types for external use
465
+ export type { LogoVariant, LogoSize, LogoBadgeShape, PositionType, BadgeOffset };
466
+ export type { LogoProps, LogoViewProps };
369
467
 
370
468
  export default Logo;