@qwickapps/react-framework 1.5.7 → 1.5.8

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 (211) hide show
  1. package/dist/components/AccessibilityChecker.d.ts.map +1 -1
  2. package/dist/components/Html.d.ts +1 -1
  3. package/dist/components/Html.d.ts.map +1 -1
  4. package/dist/components/Logo.d.ts.map +1 -1
  5. package/dist/components/Markdown.d.ts +2 -2
  6. package/dist/components/Markdown.d.ts.map +1 -1
  7. package/dist/components/SafeSpan.d.ts +1 -1
  8. package/dist/components/SafeSpan.d.ts.map +1 -1
  9. package/dist/components/base/ModelView.d.ts +1 -1
  10. package/dist/components/base/ModelView.d.ts.map +1 -1
  11. package/dist/components/blocks/Article.d.ts +1 -1
  12. package/dist/components/blocks/Article.d.ts.map +1 -1
  13. package/dist/components/blocks/CardListGrid.d.ts.map +1 -1
  14. package/dist/components/blocks/Code.d.ts.map +1 -1
  15. package/dist/components/blocks/Content.d.ts.map +1 -1
  16. package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
  17. package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
  18. package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
  19. package/dist/components/blocks/Footer.d.ts.map +1 -1
  20. package/dist/components/blocks/Image.d.ts.map +1 -1
  21. package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
  22. package/dist/components/blocks/ProductCard.d.ts.map +1 -1
  23. package/dist/components/blocks/Section.d.ts.map +1 -1
  24. package/dist/components/blocks/Text.d.ts +8 -1
  25. package/dist/components/blocks/Text.d.ts.map +1 -1
  26. package/dist/components/buttons/Button.d.ts.map +1 -1
  27. package/dist/components/buttons/PaletteSwitcher.d.ts.map +1 -1
  28. package/dist/components/buttons/ThemeSwitcher.d.ts.map +1 -1
  29. package/dist/components/forms/FormBlock.d.ts +1 -1
  30. package/dist/components/forms/FormBlock.d.ts.map +1 -1
  31. package/dist/components/forms/SchemaFormRenderer.d.ts +28 -0
  32. package/dist/components/forms/SchemaFormRenderer.d.ts.map +1 -0
  33. package/dist/components/forms/index.d.ts +2 -0
  34. package/dist/components/forms/index.d.ts.map +1 -1
  35. package/dist/components/index.d.ts +1 -0
  36. package/dist/components/index.d.ts.map +1 -1
  37. package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
  38. package/dist/components/input/HtmlInputField.d.ts.map +1 -1
  39. package/dist/components/layout/CollapsibleLayout/CollapsibleLayout.d.ts.map +1 -1
  40. package/dist/components/layout/GridLayout.d.ts +5 -0
  41. package/dist/components/layout/GridLayout.d.ts.map +1 -1
  42. package/dist/components/plugins/DataTable.d.ts +57 -0
  43. package/dist/components/plugins/DataTable.d.ts.map +1 -0
  44. package/dist/components/plugins/StatCard.d.ts +44 -0
  45. package/dist/components/plugins/StatCard.d.ts.map +1 -0
  46. package/dist/components/plugins/index.d.ts +13 -0
  47. package/dist/components/plugins/index.d.ts.map +1 -0
  48. package/dist/components/shared/createSerializableView.d.ts.map +1 -1
  49. package/dist/hooks/useBaseProps.d.ts +1161 -12
  50. package/dist/hooks/useBaseProps.d.ts.map +1 -1
  51. package/dist/index.esm.js +5468 -5216
  52. package/dist/index.js +5572 -5317
  53. package/dist/palettes/manifest.json +19 -19
  54. package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -1
  55. package/dist/utils/iconMap.d.ts.map +1 -1
  56. package/package.json +1 -2
  57. package/src/components/AccessibilityChecker.tsx +10 -7
  58. package/src/components/ErrorBoundary.tsx +3 -3
  59. package/src/components/Html.tsx +17 -12
  60. package/src/components/Logo.tsx +1 -8
  61. package/src/components/Markdown.tsx +10 -10
  62. package/src/components/ResponsiveMenu.tsx +1 -1
  63. package/src/components/SafeSpan.tsx +9 -9
  64. package/src/components/Scaffold.tsx +4 -4
  65. package/src/components/base/ModelView.tsx +2 -2
  66. package/src/components/blocks/Article.tsx +7 -7
  67. package/src/components/blocks/CardListGrid.tsx +1 -3
  68. package/src/components/blocks/Code.tsx +10 -8
  69. package/src/components/blocks/Content.tsx +2 -4
  70. package/src/components/blocks/CoverImageHeader.tsx +3 -4
  71. package/src/components/blocks/FeatureCard.tsx +2 -4
  72. package/src/components/blocks/FeatureGrid.tsx +2 -4
  73. package/src/components/blocks/Footer.tsx +2 -4
  74. package/src/components/blocks/Image.tsx +8 -5
  75. package/src/components/blocks/PageBannerHeader.tsx +3 -4
  76. package/src/components/blocks/ProductCard.tsx +8 -5
  77. package/src/components/blocks/Section.tsx +6 -4
  78. package/src/components/blocks/Text.tsx +15 -7
  79. package/src/components/buttons/Button.tsx +8 -6
  80. package/src/components/buttons/PaletteSwitcher.tsx +6 -8
  81. package/src/components/buttons/ThemeSwitcher.tsx +8 -9
  82. package/src/components/forms/Captcha.tsx +1 -1
  83. package/src/components/forms/FormBlock.tsx +3 -5
  84. package/src/components/forms/FormCheckbox.tsx +1 -1
  85. package/src/components/forms/FormField.tsx +1 -1
  86. package/src/components/forms/FormSelect.tsx +1 -1
  87. package/src/components/forms/SchemaFormRenderer.tsx +268 -0
  88. package/src/components/forms/__tests__/SchemaFormRenderer.test.tsx +212 -0
  89. package/src/components/forms/index.ts +3 -0
  90. package/src/components/index.ts +1 -0
  91. package/src/components/input/ChoiceInputField.tsx +2 -1
  92. package/src/components/input/HtmlInputField.tsx +14 -9
  93. package/src/components/input/TextField.tsx +1 -1
  94. package/src/components/layout/CollapsibleLayout/CollapsibleLayout.tsx +6 -8
  95. package/src/components/layout/GridLayout.tsx +4 -0
  96. package/src/components/plugins/DataTable.tsx +259 -0
  97. package/src/components/plugins/StatCard.tsx +122 -0
  98. package/src/components/plugins/__tests__/DataTable.test.tsx +158 -0
  99. package/src/components/plugins/index.ts +14 -0
  100. package/src/components/shared/createSerializableView.tsx +8 -6
  101. package/src/hooks/useBaseProps.ts +1 -1
  102. package/src/schemas/transformers/ReactNodeTransformer.ts +13 -10
  103. package/src/utils/iconMap.tsx +143 -83
  104. package/dist/palettes/palette-autumn.1.4.9.css +0 -172
  105. package/dist/palettes/palette-autumn.1.4.9.min.css +0 -1
  106. package/dist/palettes/palette-autumn.1.5.0.css +0 -172
  107. package/dist/palettes/palette-autumn.1.5.0.min.css +0 -1
  108. package/dist/palettes/palette-autumn.1.5.1.css +0 -172
  109. package/dist/palettes/palette-autumn.1.5.1.min.css +0 -1
  110. package/dist/palettes/palette-autumn.1.5.2.css +0 -172
  111. package/dist/palettes/palette-autumn.1.5.2.min.css +0 -1
  112. package/dist/palettes/palette-autumn.1.5.4.css +0 -172
  113. package/dist/palettes/palette-autumn.1.5.4.min.css +0 -1
  114. package/dist/palettes/palette-autumn.1.5.5.css +0 -172
  115. package/dist/palettes/palette-autumn.1.5.5.min.css +0 -1
  116. package/dist/palettes/palette-autumn.1.5.6.css +0 -172
  117. package/dist/palettes/palette-autumn.1.5.6.min.css +0 -1
  118. package/dist/palettes/palette-autumn.1.5.7.css +0 -172
  119. package/dist/palettes/palette-autumn.1.5.7.min.css +0 -1
  120. package/dist/palettes/palette-cosmic.1.4.9.css +0 -172
  121. package/dist/palettes/palette-cosmic.1.4.9.min.css +0 -1
  122. package/dist/palettes/palette-cosmic.1.5.0.css +0 -172
  123. package/dist/palettes/palette-cosmic.1.5.0.min.css +0 -1
  124. package/dist/palettes/palette-cosmic.1.5.1.css +0 -172
  125. package/dist/palettes/palette-cosmic.1.5.1.min.css +0 -1
  126. package/dist/palettes/palette-cosmic.1.5.2.css +0 -172
  127. package/dist/palettes/palette-cosmic.1.5.2.min.css +0 -1
  128. package/dist/palettes/palette-cosmic.1.5.4.css +0 -172
  129. package/dist/palettes/palette-cosmic.1.5.4.min.css +0 -1
  130. package/dist/palettes/palette-cosmic.1.5.5.css +0 -172
  131. package/dist/palettes/palette-cosmic.1.5.5.min.css +0 -1
  132. package/dist/palettes/palette-cosmic.1.5.6.css +0 -172
  133. package/dist/palettes/palette-cosmic.1.5.6.min.css +0 -1
  134. package/dist/palettes/palette-cosmic.1.5.7.css +0 -172
  135. package/dist/palettes/palette-cosmic.1.5.7.min.css +0 -1
  136. package/dist/palettes/palette-default.1.4.9.css +0 -178
  137. package/dist/palettes/palette-default.1.4.9.min.css +0 -1
  138. package/dist/palettes/palette-default.1.5.0.css +0 -178
  139. package/dist/palettes/palette-default.1.5.0.min.css +0 -1
  140. package/dist/palettes/palette-default.1.5.1.css +0 -178
  141. package/dist/palettes/palette-default.1.5.1.min.css +0 -1
  142. package/dist/palettes/palette-default.1.5.2.css +0 -178
  143. package/dist/palettes/palette-default.1.5.2.min.css +0 -1
  144. package/dist/palettes/palette-default.1.5.4.css +0 -178
  145. package/dist/palettes/palette-default.1.5.4.min.css +0 -1
  146. package/dist/palettes/palette-default.1.5.5.css +0 -178
  147. package/dist/palettes/palette-default.1.5.5.min.css +0 -1
  148. package/dist/palettes/palette-default.1.5.6.css +0 -178
  149. package/dist/palettes/palette-default.1.5.6.min.css +0 -1
  150. package/dist/palettes/palette-default.1.5.7.css +0 -178
  151. package/dist/palettes/palette-default.1.5.7.min.css +0 -1
  152. package/dist/palettes/palette-ocean.1.4.9.css +0 -172
  153. package/dist/palettes/palette-ocean.1.4.9.min.css +0 -1
  154. package/dist/palettes/palette-ocean.1.5.0.css +0 -172
  155. package/dist/palettes/palette-ocean.1.5.0.min.css +0 -1
  156. package/dist/palettes/palette-ocean.1.5.1.css +0 -172
  157. package/dist/palettes/palette-ocean.1.5.1.min.css +0 -1
  158. package/dist/palettes/palette-ocean.1.5.2.css +0 -172
  159. package/dist/palettes/palette-ocean.1.5.2.min.css +0 -1
  160. package/dist/palettes/palette-ocean.1.5.4.css +0 -172
  161. package/dist/palettes/palette-ocean.1.5.4.min.css +0 -1
  162. package/dist/palettes/palette-ocean.1.5.5.css +0 -172
  163. package/dist/palettes/palette-ocean.1.5.5.min.css +0 -1
  164. package/dist/palettes/palette-ocean.1.5.6.css +0 -172
  165. package/dist/palettes/palette-ocean.1.5.6.min.css +0 -1
  166. package/dist/palettes/palette-ocean.1.5.7.css +0 -172
  167. package/dist/palettes/palette-ocean.1.5.7.min.css +0 -1
  168. package/dist/palettes/palette-spring.1.4.9.css +0 -160
  169. package/dist/palettes/palette-spring.1.4.9.min.css +0 -1
  170. package/dist/palettes/palette-spring.1.5.0.css +0 -160
  171. package/dist/palettes/palette-spring.1.5.0.min.css +0 -1
  172. package/dist/palettes/palette-spring.1.5.1.css +0 -160
  173. package/dist/palettes/palette-spring.1.5.1.min.css +0 -1
  174. package/dist/palettes/palette-spring.1.5.2.css +0 -160
  175. package/dist/palettes/palette-spring.1.5.2.min.css +0 -1
  176. package/dist/palettes/palette-spring.1.5.4.css +0 -166
  177. package/dist/palettes/palette-spring.1.5.4.min.css +0 -1
  178. package/dist/palettes/palette-spring.1.5.5.css +0 -166
  179. package/dist/palettes/palette-spring.1.5.5.min.css +0 -1
  180. package/dist/palettes/palette-spring.1.5.6.css +0 -166
  181. package/dist/palettes/palette-spring.1.5.6.min.css +0 -1
  182. package/dist/palettes/palette-spring.1.5.7.css +0 -166
  183. package/dist/palettes/palette-spring.1.5.7.min.css +0 -1
  184. package/dist/palettes/palette-winter.1.4.9.css +0 -172
  185. package/dist/palettes/palette-winter.1.4.9.min.css +0 -1
  186. package/dist/palettes/palette-winter.1.5.0.css +0 -172
  187. package/dist/palettes/palette-winter.1.5.0.min.css +0 -1
  188. package/dist/palettes/palette-winter.1.5.1.css +0 -172
  189. package/dist/palettes/palette-winter.1.5.1.min.css +0 -1
  190. package/dist/palettes/palette-winter.1.5.2.css +0 -172
  191. package/dist/palettes/palette-winter.1.5.2.min.css +0 -1
  192. package/dist/palettes/palette-winter.1.5.4.css +0 -172
  193. package/dist/palettes/palette-winter.1.5.4.min.css +0 -1
  194. package/dist/palettes/palette-winter.1.5.5.css +0 -172
  195. package/dist/palettes/palette-winter.1.5.5.min.css +0 -1
  196. package/dist/palettes/palette-winter.1.5.6.css +0 -172
  197. package/dist/palettes/palette-winter.1.5.6.min.css +0 -1
  198. package/dist/palettes/palette-winter.1.5.7.css +0 -172
  199. package/dist/palettes/palette-winter.1.5.7.min.css +0 -1
  200. /package/dist/palettes/{palette-autumn.1.5.3.css → palette-autumn.1.5.8.css} +0 -0
  201. /package/dist/palettes/{palette-autumn.1.5.3.min.css → palette-autumn.1.5.8.min.css} +0 -0
  202. /package/dist/palettes/{palette-cosmic.1.5.3.css → palette-cosmic.1.5.8.css} +0 -0
  203. /package/dist/palettes/{palette-cosmic.1.5.3.min.css → palette-cosmic.1.5.8.min.css} +0 -0
  204. /package/dist/palettes/{palette-default.1.5.3.css → palette-default.1.5.8.css} +0 -0
  205. /package/dist/palettes/{palette-default.1.5.3.min.css → palette-default.1.5.8.min.css} +0 -0
  206. /package/dist/palettes/{palette-ocean.1.5.3.css → palette-ocean.1.5.8.css} +0 -0
  207. /package/dist/palettes/{palette-ocean.1.5.3.min.css → palette-ocean.1.5.8.min.css} +0 -0
  208. /package/dist/palettes/{palette-spring.1.5.3.css → palette-spring.1.5.8.css} +0 -0
  209. /package/dist/palettes/{palette-spring.1.5.3.min.css → palette-spring.1.5.8.min.css} +0 -0
  210. /package/dist/palettes/{palette-winter.1.5.3.css → palette-winter.1.5.8.css} +0 -0
  211. /package/dist/palettes/{palette-winter.1.5.3.min.css → palette-winter.1.5.8.min.css} +0 -0
@@ -17,7 +17,8 @@
17
17
 
18
18
  import React, { useState, useCallback } from 'react';
19
19
  import { Box, Skeleton, Typography, useTheme } from '@mui/material';
20
- import { BrokenImage as BrokenImageIcon } from '@mui/icons-material';
20
+ import BrokenImage from "@mui/icons-material/BrokenImage";
21
+ const BrokenImageIcon = BrokenImage;
21
22
  import { ImageFit, ImageLoading, ImagePosition } from '../../schemas/ImageSchema';
22
23
  import { createSerializableView, SerializableComponent } from '../shared/createSerializableView';
23
24
  import { ViewProps } from '../shared/viewProps';
@@ -231,14 +232,16 @@ interface PatternRegistry {
231
232
 
232
233
  // Register HTML patterns that Image component can handle
233
234
  (Image as unknown as { registerPatternHandlers: (registry: PatternRegistry) => void }).registerPatternHandlers = (registry: PatternRegistry): void => {
235
+ const typedRegistry = registry as { hasPattern?: (pattern: string) => boolean; registerPattern?: (pattern: string, handler: (element: Element) => Record<string, unknown>) => void };
236
+
234
237
  // Register img elements
235
- if (!registry.hasPattern('img')) {
236
- registry.registerPattern('img', transformImage);
238
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('img')) {
239
+ typedRegistry.registerPattern?.('img', transformImage);
237
240
  }
238
241
 
239
242
  // Register figure elements with img
240
- if (!registry.hasPattern('figure img')) {
241
- registry.registerPattern('figure img', transformFigureImage);
243
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('figure img')) {
244
+ typedRegistry.registerPattern?.('figure img', transformFigureImage);
242
245
  }
243
246
  };
244
247
 
@@ -12,7 +12,8 @@
12
12
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
13
13
  */
14
14
 
15
- import { MoreVert as MoreIcon } from '@mui/icons-material';
15
+ import MoreVert from "@mui/icons-material/MoreVert";
16
+ const MoreIcon = MoreVert;
16
17
  import {
17
18
  Avatar,
18
19
  Box,
@@ -387,9 +388,7 @@ function PageBannerHeader(props: PageBannerHeaderProps) {
387
388
  // Always call hooks unconditionally
388
389
  const bindingResult = useDataBinding<PageBannerHeaderModel>(
389
390
  dataSource || '',
390
- restProps as Partial<PageBannerHeaderModel>,
391
- PageBannerHeaderModel.getSchema(),
392
- { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
391
+ restProps as Partial<PageBannerHeaderModel>
393
392
  );
394
393
 
395
394
  // If no dataSource, use traditional props
@@ -10,7 +10,12 @@
10
10
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
11
11
  */
12
12
 
13
- import { Schedule as ComingSoonIcon, Launch as LaunchIcon, Visibility as PreviewIcon } from '@mui/icons-material';
13
+ import Schedule from '@mui/icons-material/Schedule';
14
+ import Launch from '@mui/icons-material/Launch';
15
+ import Visibility from '@mui/icons-material/Visibility';
16
+ const ComingSoonIcon = Schedule;
17
+ const LaunchIcon = Launch;
18
+ const PreviewIcon = Visibility;
14
19
  import {
15
20
  Box,
16
21
  Chip,
@@ -406,14 +411,12 @@ function ProductCard(props: ProductCardProps) {
406
411
  const { dataSource, bindingOptions, ...restProps } = props;
407
412
 
408
413
  // Mark as QwickApp component
409
- (ProductCard as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
414
+ Object.assign(ProductCard, { [QWICKAPP_COMPONENT]: true });
410
415
 
411
416
  // Always call hooks unconditionally
412
417
  const bindingResult = useDataBinding<ProductCardModel>(
413
418
  dataSource || '',
414
- restProps as Partial<ProductCardModel>,
415
- ProductCardModel.getSchema(),
416
- { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
419
+ restProps as Partial<ProductCardModel>
417
420
  );
418
421
 
419
422
  // If no dataSource, use traditional props
@@ -168,14 +168,16 @@ interface PatternRegistry {
168
168
 
169
169
  // Register HTML patterns that Section component can handle
170
170
  (Section as unknown as { registerPatternHandlers: (registry: PatternRegistry) => void }).registerPatternHandlers = (registry: PatternRegistry): void => {
171
+ const typedRegistry = registry as { hasPattern?: (pattern: string) => boolean; registerPattern?: (pattern: string, handler: (element: Element) => Record<string, unknown>) => void };
172
+
171
173
  // Register section element pattern
172
- if (!registry.hasPattern('section')) {
173
- registry.registerPattern('section', (Section as unknown as { transformSection: (element: Element) => unknown }).transformSection);
174
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('section')) {
175
+ typedRegistry.registerPattern?.('section', (Section as unknown as { transformSection: (element: Element) => unknown }).transformSection);
174
176
  }
175
177
 
176
178
  // Register section with specific classes
177
- if (!registry.hasPattern('section.blog-section')) {
178
- registry.registerPattern('section.blog-section', (Section as unknown as { transformBlogSection: (element: Element) => unknown }).transformBlogSection);
179
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('section.blog-section')) {
180
+ typedRegistry.registerPattern?.('section.blog-section', (Section as unknown as { transformBlogSection: (element: Element) => unknown }).transformBlogSection);
179
181
  }
180
182
  };
181
183
 
@@ -25,8 +25,14 @@ import type { SchemaProps } from '@qwickapps/schema/src/types/ModelProps';
25
25
  /**
26
26
  * Props interface for Text component
27
27
  * Uses SchemaProps<typeof TextSchema> for clean typing
28
+ * Explicitly includes sx and style for type resolution
28
29
  */
29
- export type TextProps = ViewProps & SchemaProps<typeof TextSchema>;
30
+ export type TextProps = ViewProps & SchemaProps<typeof TextSchema> & {
31
+ /** MUI sx prop for advanced styling (explicit override for type resolution) */
32
+ sx?: import('@mui/material/styles').SxProps<import('@mui/material/styles').Theme>;
33
+ /** Inline CSS styles (explicit override for type resolution) */
34
+ style?: React.CSSProperties;
35
+ };
30
36
 
31
37
  /**
32
38
  * TextView - Pure view component that renders the typography
@@ -130,24 +136,26 @@ interface TextComponentWithPatterns {
130
136
 
131
137
  // Register HTML patterns that Text component can handle
132
138
  (Text as unknown as TextComponentWithPatterns).registerPatternHandlers = (registry: PatternRegistry): void => {
139
+ const typedRegistry = registry as { hasPattern?: (pattern: string) => boolean; registerPattern?: (pattern: string, handler: (element: Element) => Record<string, unknown>) => void };
133
140
  const textComponent = Text as unknown as TextComponentWithPatterns;
134
141
 
135
142
  // Register paragraph elements
136
- if (!registry.hasPattern('p')) {
137
- registry.registerPattern('p', textComponent.transformParagraph);
143
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('p')) {
144
+ typedRegistry.registerPattern?.('p', textComponent.transformParagraph);
138
145
  }
139
146
 
140
147
  // Register heading elements
141
148
  const headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
142
149
  headings.forEach(heading => {
143
- if (!registry.hasPattern(heading)) {
144
- registry.registerPattern(heading, (element: Element) => textComponent.transformHeading(element, heading));
150
+
151
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern(heading)) {
152
+ typedRegistry.registerPattern?.(heading, (element: Element) => textComponent.transformHeading(element, heading));
145
153
  }
146
154
  });
147
155
 
148
156
  // Register span elements
149
- if (!registry.hasPattern('span')) {
150
- registry.registerPattern('span', textComponent.transformSpan);
157
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('span')) {
158
+ typedRegistry.registerPattern?.('span', textComponent.transformSpan);
151
159
  }
152
160
  };
153
161
 
@@ -311,19 +311,21 @@ export const Button: SerializableComponent<ButtonProps> = createSerializableView
311
311
 
312
312
  // Register HTML patterns that Button component can handle
313
313
  (Button as Record<string, unknown>).registerPatternHandlers = (registry: Record<string, (...args: unknown[]) => unknown>): void => {
314
+ const typedRegistry = registry as { hasPattern?: (pattern: string) => boolean; registerPattern?: (pattern: string, handler: (element: Element) => Record<string, unknown>) => void };
315
+
314
316
  // Register button elements
315
- if (!registry.hasPattern('button')) {
316
- registry.registerPattern('button', transformButton);
317
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('button')) {
318
+ typedRegistry.registerPattern?.('button', transformButton);
317
319
  }
318
320
 
319
321
  // Register input type="button" elements
320
- if (!registry.hasPattern('input[type="button"]')) {
321
- registry.registerPattern('input[type="button"]', transformInputButton);
322
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('input[type="button"]')) {
323
+ typedRegistry.registerPattern?.('input[type="button"]', transformInputButton);
322
324
  }
323
325
 
324
326
  // Register input type="submit" elements
325
- if (!registry.hasPattern('input[type="submit"]')) {
326
- registry.registerPattern('input[type="submit"]', transformSubmitButton);
327
+ if (typedRegistry.hasPattern && !typedRegistry.hasPattern('input[type="submit"]')) {
328
+ typedRegistry.registerPattern?.('input[type="submit"]', transformSubmitButton);
327
329
  }
328
330
  };
329
331
 
@@ -11,10 +11,10 @@
11
11
  * Copyright (c) 2025 QwickApps.com. All rights reserved.
12
12
  */
13
13
 
14
- import {
15
- Palette as PaletteIcon,
16
- Circle as CircleIcon,
17
- } from '@mui/icons-material';
14
+ import Palette from '@mui/icons-material/Palette';
15
+ import Circle from '@mui/icons-material/Circle';
16
+ const PaletteIcon = Palette;
17
+ const CircleIcon = Circle;
18
18
  import {
19
19
  IconButton,
20
20
  Menu,
@@ -210,9 +210,7 @@ function PaletteSwitcher(props: PaletteSwitcherProps) {
210
210
  // Always call hooks unconditionally
211
211
  const bindingResult = useDataBinding<PaletteSwitcherModel>(
212
212
  dataSource || '',
213
- restProps as Partial<PaletteSwitcherModel>,
214
- PaletteSwitcherModel.getSchema(),
215
- { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
213
+ restProps as Partial<PaletteSwitcherModel>
216
214
  );
217
215
 
218
216
  // If no dataSource, use traditional props
@@ -266,6 +264,6 @@ function PaletteSwitcher(props: PaletteSwitcherProps) {
266
264
  }
267
265
 
268
266
  // Mark as QwickApp component
269
- (PaletteSwitcher as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
267
+ Object.assign(PaletteSwitcher, { [QWICKAPP_COMPONENT]: true });
270
268
 
271
269
  export default PaletteSwitcher;
@@ -21,11 +21,12 @@ import {
21
21
  Tooltip,
22
22
  Typography
23
23
  } from '@mui/material';
24
- import {
25
- DarkMode as DarkModeIcon,
26
- LightMode as LightModeIcon,
27
- SettingsSystemDaydream as SystemIcon
28
- } from '@mui/icons-material';
24
+ import DarkMode from '@mui/icons-material/DarkMode';
25
+ import LightMode from '@mui/icons-material/LightMode';
26
+ import SettingsSystemDaydream from '@mui/icons-material/SettingsSystemDaydream';
27
+ const DarkModeIcon = DarkMode;
28
+ const LightModeIcon = LightMode;
29
+ const SystemIcon = SettingsSystemDaydream;
29
30
  import type { WithDataBinding, SchemaProps } from '@qwickapps/schema';
30
31
  import { useState } from 'react';
31
32
  import { useTheme } from '../../contexts/ThemeContext';
@@ -235,9 +236,7 @@ function ThemeSwitcher(props: ThemeSwitcherProps) {
235
236
  // Always call hooks unconditionally
236
237
  const bindingResult = useDataBinding<ThemeSwitcherModel>(
237
238
  dataSource || '',
238
- restProps as Partial<ThemeSwitcherModel>,
239
- ThemeSwitcherModel.getSchema(),
240
- { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
239
+ restProps as Partial<ThemeSwitcherModel>
241
240
  );
242
241
 
243
242
  // If no dataSource, use traditional props
@@ -282,6 +281,6 @@ function ThemeSwitcher(props: ThemeSwitcherProps) {
282
281
  }
283
282
 
284
283
  // Mark as QwickApp component
285
- (ThemeSwitcher as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
284
+ Object.assign(ThemeSwitcher, { [QWICKAPP_COMPONENT]: true });
286
285
 
287
286
  export default ThemeSwitcher;
@@ -286,6 +286,6 @@ export const Captcha = React.forwardRef<HTMLDivElement, CaptchaProps>((props, re
286
286
  Captcha.displayName = 'Captcha';
287
287
 
288
288
  // Mark as QwickApp component
289
- (Captcha as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
289
+ Object.assign(Captcha, { [QWICKAPP_COMPONENT]: true });
290
290
 
291
291
  export default Captcha;
@@ -225,7 +225,7 @@ function FormBlockView({
225
225
  }
226
226
 
227
227
  // Main component with data binding support and serialization capability
228
- export class FormBlock extends ModelView<FormBlockProps, FormBlockModel> {
228
+ export class FormBlock extends ModelView<FormBlockProps> {
229
229
  // Component self-declaration for serialization
230
230
  static readonly tagName = 'FormBlock';
231
231
  static readonly version = '1.0.0';
@@ -298,9 +298,7 @@ function FormBlockWithDataBinding(props: FormBlockProps) {
298
298
  // Use data binding
299
299
  const { loading, error, ...formBlockProps } = useDataBinding<FormBlockModel>(
300
300
  dataSource!,
301
- restProps as Partial<FormBlockModel>,
302
- FormBlockModel.getSchema(),
303
- { cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
301
+ restProps as Partial<FormBlockModel>
304
302
  );
305
303
 
306
304
  // Show loading state
@@ -346,6 +344,6 @@ function FormBlockWithDataBinding(props: FormBlockProps) {
346
344
  }
347
345
 
348
346
  // Mark as QwickApp component
349
- (FormBlock as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
347
+ Object.assign(FormBlock, { [QWICKAPP_COMPONENT]: true });
350
348
 
351
349
  export default FormBlock;
@@ -111,6 +111,6 @@ export const FormCheckbox = React.forwardRef<HTMLDivElement, FormCheckboxProps>(
111
111
  FormCheckbox.displayName = 'FormCheckbox';
112
112
 
113
113
  // Mark as QwickApp component
114
- (FormCheckbox as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
114
+ Object.assign(FormCheckbox, { [QWICKAPP_COMPONENT]: true });
115
115
 
116
116
  export default FormCheckbox;
@@ -175,6 +175,6 @@ export const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>((props
175
175
  FormField.displayName = 'FormField';
176
176
 
177
177
  // Mark as QwickApp component
178
- (FormField as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
178
+ Object.assign(FormField, { [QWICKAPP_COMPONENT]: true });
179
179
 
180
180
  export default FormField;
@@ -135,6 +135,6 @@ export const FormSelect = React.forwardRef<HTMLDivElement, FormSelectProps>((pro
135
135
  FormSelect.displayName = 'FormSelect';
136
136
 
137
137
  // Mark as QwickApp component
138
- (FormSelect as Record<string, unknown>)[QWICKAPP_COMPONENT] = true;
138
+ Object.assign(FormSelect, { [QWICKAPP_COMPONENT]: true });
139
139
 
140
140
  export default FormSelect;
@@ -0,0 +1,268 @@
1
+ /**
2
+ * SchemaFormRenderer - Dynamic form generation from @qwickapps/schema models
3
+ *
4
+ * Reads @Editor metadata from Model classes and generates Material-UI form fields.
5
+ * Maps field_type to appropriate input components with validation.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import React, { useState, useCallback } from 'react';
11
+ import {
12
+ TextField,
13
+ FormControlLabel,
14
+ Switch,
15
+ Box,
16
+ Typography,
17
+ FormHelperText,
18
+ Alert,
19
+ } from '@mui/material';
20
+ import { Model, FieldType } from '@qwickapps/schema';
21
+ import type { FieldDefinition } from '@qwickapps/schema';
22
+
23
+ export interface SchemaFormRendererProps<T extends Model> {
24
+ /** Model class to generate form from */
25
+ modelClass: new () => T;
26
+
27
+ /** Current form data */
28
+ value: Partial<T>;
29
+
30
+ /** Called when any field changes */
31
+ onChange: (data: Partial<T>) => void;
32
+
33
+ /** Show validation errors */
34
+ showValidation?: boolean;
35
+
36
+ /** Validation errors from Model.validate() */
37
+ validationErrors?: string[];
38
+
39
+ /** Read-only mode */
40
+ readOnly?: boolean;
41
+ }
42
+
43
+ /**
44
+ * Render a single form field based on its editor configuration
45
+ */
46
+ function renderField<T extends Model>(
47
+ field: FieldDefinition,
48
+ value: unknown,
49
+ onChange: (name: string, value: unknown) => void,
50
+ readOnly: boolean
51
+ ): React.ReactNode {
52
+ const { name, required, editor } = field;
53
+
54
+ if (!editor) {
55
+ return null;
56
+ }
57
+
58
+ const { field_type, label, description, placeholder, validation } = editor;
59
+ const fieldValue = value ?? '';
60
+
61
+ const commonProps = {
62
+ fullWidth: true,
63
+ margin: 'normal' as const,
64
+ required,
65
+ disabled: readOnly,
66
+ label,
67
+ helperText: description,
68
+ };
69
+
70
+ switch (field_type) {
71
+ case FieldType.TEXT:
72
+ case FieldType.EMAIL:
73
+ return (
74
+ <TextField
75
+ {...commonProps}
76
+ type={field_type === FieldType.EMAIL ? 'email' : 'text'}
77
+ value={fieldValue}
78
+ onChange={(e) => onChange(name, e.target.value)}
79
+ placeholder={placeholder}
80
+ inputProps={{
81
+ minLength: validation?.min,
82
+ maxLength: validation?.max,
83
+ }}
84
+ />
85
+ );
86
+
87
+ case FieldType.TEXTAREA:
88
+ return (
89
+ <TextField
90
+ {...commonProps}
91
+ multiline
92
+ rows={4}
93
+ value={fieldValue}
94
+ onChange={(e) => onChange(name, e.target.value)}
95
+ placeholder={placeholder}
96
+ inputProps={{
97
+ minLength: validation?.min,
98
+ maxLength: validation?.max,
99
+ }}
100
+ />
101
+ );
102
+
103
+ case FieldType.NUMBER:
104
+ return (
105
+ <TextField
106
+ {...commonProps}
107
+ type="number"
108
+ value={fieldValue}
109
+ onChange={(e) => {
110
+ const val = e.target.value;
111
+ onChange(name, val === '' ? undefined : parseFloat(val));
112
+ }}
113
+ placeholder={placeholder}
114
+ inputProps={{
115
+ min: validation?.min,
116
+ max: validation?.max,
117
+ }}
118
+ />
119
+ );
120
+
121
+ case FieldType.BOOLEAN:
122
+ return (
123
+ <FormControlLabel
124
+ control={
125
+ <Switch
126
+ checked={!!fieldValue}
127
+ onChange={(e) => onChange(name, e.target.checked)}
128
+ disabled={readOnly}
129
+ />
130
+ }
131
+ label={
132
+ <Box>
133
+ <Typography variant="body2" fontWeight={required ? 600 : 400}>
134
+ {label}
135
+ </Typography>
136
+ {description && (
137
+ <Typography variant="caption" color="text.secondary">
138
+ {description}
139
+ </Typography>
140
+ )}
141
+ </Box>
142
+ }
143
+ />
144
+ );
145
+
146
+ case FieldType.DATE_TIME:
147
+ return (
148
+ <TextField
149
+ {...commonProps}
150
+ type="datetime-local"
151
+ value={fieldValue}
152
+ onChange={(e) => onChange(name, e.target.value)}
153
+ InputLabelProps={{ shrink: true }}
154
+ />
155
+ );
156
+
157
+ case FieldType.COLOR:
158
+ return (
159
+ <Box>
160
+ <TextField
161
+ {...commonProps}
162
+ type="color"
163
+ value={fieldValue || '#000000'}
164
+ onChange={(e) => onChange(name, e.target.value)}
165
+ />
166
+ </Box>
167
+ );
168
+
169
+ case FieldType.IMAGE:
170
+ return (
171
+ <Box>
172
+ <Typography variant="body2" fontWeight={required ? 600 : 400}>
173
+ {label}
174
+ </Typography>
175
+ <TextField
176
+ fullWidth
177
+ margin="normal"
178
+ type="url"
179
+ value={fieldValue}
180
+ onChange={(e) => onChange(name, e.target.value)}
181
+ placeholder={placeholder || 'https://example.com/image.jpg'}
182
+ helperText={description}
183
+ disabled={readOnly}
184
+ />
185
+ </Box>
186
+ );
187
+
188
+ case FieldType.FORM:
189
+ // Nested form - would need recursive rendering
190
+ return (
191
+ <Alert severity="info" sx={{ my: 2 }}>
192
+ Nested form for {name} (not yet implemented)
193
+ </Alert>
194
+ );
195
+
196
+ case FieldType.MODEL_REPEATER:
197
+ // Array of nested forms
198
+ return (
199
+ <Alert severity="info" sx={{ my: 2 }}>
200
+ Array field {name} (not yet implemented)
201
+ </Alert>
202
+ );
203
+
204
+ default:
205
+ return (
206
+ <TextField
207
+ {...commonProps}
208
+ value={fieldValue}
209
+ onChange={(e) => onChange(name, e.target.value)}
210
+ placeholder={placeholder}
211
+ />
212
+ );
213
+ }
214
+ }
215
+
216
+ /**
217
+ * SchemaFormRenderer Component
218
+ */
219
+ export function SchemaFormRenderer<T extends Model>({
220
+ modelClass,
221
+ value,
222
+ onChange,
223
+ showValidation = false,
224
+ validationErrors = [],
225
+ readOnly = false,
226
+ }: SchemaFormRendererProps<T>) {
227
+ const schema = modelClass.getSchema();
228
+
229
+ const handleFieldChange = useCallback(
230
+ (fieldName: string, fieldValue: any) => {
231
+ onChange({
232
+ ...value,
233
+ [fieldName]: fieldValue,
234
+ });
235
+ },
236
+ [value, onChange]
237
+ );
238
+
239
+ return (
240
+ <Box>
241
+ {showValidation && validationErrors.length > 0 && (
242
+ <Alert severity="error" sx={{ mb: 2 }}>
243
+ <Typography variant="body2" fontWeight={600} gutterBottom>
244
+ Please fix the following errors:
245
+ </Typography>
246
+ <ul style={{ margin: 0, paddingLeft: 20 }}>
247
+ {validationErrors.map((error, idx) => (
248
+ <li key={idx}>
249
+ <Typography variant="body2">{error}</Typography>
250
+ </li>
251
+ ))}
252
+ </ul>
253
+ </Alert>
254
+ )}
255
+
256
+ {schema.fields.map((field) => (
257
+ <Box key={field.name}>
258
+ {renderField<T>(
259
+ field,
260
+ (value as any)[field.name],
261
+ handleFieldChange,
262
+ readOnly
263
+ )}
264
+ </Box>
265
+ ))}
266
+ </Box>
267
+ );
268
+ }